Adafruit_StepperMotor::onestep is too slow
Moderators: adafruit_support_bill, adafruit

Adafruit_StepperMotor::onestep is too slow

by AlexeyB on Thu Oct 31, 2013 12:32 am

I am using Adafruit Motor shield V2 with Arduino Uno. According to my measurements, Adafruit_StepperMotor::onestep method call takes about 3800 microseconds.

Code: Select all | TOGGLE FULL SIZE
    ...
    unsigned long vSec = micros();

    myMotor->onestep(fwd ? FORWARD : BACKWARD, style);

    unsigned long vSec2 = micros();
    Serial.print("delay=");
    Serial.println(vSec2-vSec);
    ...

And from Serial monitor:
Code: Select all | TOGGLE FULL SIZE
delay=3788
delay=3800
delay=3792
delay=3800
delay=3804
delay=3792
delay=3800
delay=3804

It means that, with my 1.8 degree/step stepper motor, the maximum speed I can reach is (1000000/3800)/( 360/1.8 ) = 1.316 RPS = 78.95 RPM.
It is not enough for my task.
(I guess this may also be not enough for anybody who uses AccelStepper library.)
I wonder why this method time is too big. Is there _any_way_ to speed up "onestep" call?

P.S. Please do not offer blocking methods such as Adafruit_StepperMotor::step. My program has a lot of work to do between steps :shock:
P.P.S. It looks like Adafruit_StepperMotor::step has the same problem, it doesn't allow to rotate stepper with speed 120 RPM, for example.
AlexeyB
 
Posts: 6
Joined: Wed Oct 09, 2013 11:36 pm

Re: Adafruit_StepperMotor::onestep is too slow

by AlexeyB on Thu Oct 31, 2013 3:20 am

It looks like Adafruit_StepperMotor::step has the same problem, it doesn't allow to rotate stepper with speed 120 RPM, for example.


What do I mean: the following program DOES NOT actually rotate 1.8 deg/step stepper with 120 RPM.
Code: Select all | TOGGLE FULL SIZE
#include <Wire.h>
#include <Adafruit_MotorShield.h>
#include "utility/Adafruit_PWMServoDriver.h"

Adafruit_MotorShield AFMS = Adafruit_MotorShield();
Adafruit_StepperMotor *myMotor = AFMS.getStepper(200, 2);

void setup() {
  AFMS.begin();
  myMotor->setSpeed(120);  // 120 rpm   
}

void loop() {
  myMotor->step(3000, FORWARD, DOUBLE);
  myMotor->step(3000, BACKWARD, DOUBLE);
}
AlexeyB
 
Posts: 6
Joined: Wed Oct 09, 2013 11:36 pm

Re: Adafruit_StepperMotor::onestep is too slow

by adafruit_support_bill on Thu Oct 31, 2013 6:01 am

The bottleneck is i2c. You can increase the default i2c bus speed: viewtopic.php?f=31&t=42477&p=214932#p214932
User avatar
adafruit_support_bill
 
Posts: 30794
Joined: Sat Feb 07, 2009 10:11 am

Re: Adafruit_StepperMotor::onestep is too slow

by AlexeyB on Thu Oct 31, 2013 2:27 pm

Thank you very much for the quick answer!
It really helps, in my case it even works with TWI_FREQ=500000L.
But it seems I still can't make onestep time less than 1600 microseconds, though I didn't have enough time for comprehensive testing. It allows me to rotate up to 90 rpm, and it's a great improvement, but still not enough for me. I need at least 120 (and 180-240 would be ideal). I can't increase it more than 90.
I'll try testing again this evening and post the results.
AlexeyB
 
Posts: 6
Joined: Wed Oct 09, 2013 11:36 pm

Re: Adafruit_StepperMotor::onestep is too slow

by AlexeyB on Fri Nov 01, 2013 12:47 am

Well, I made some testing and gathered statistics with TWI_FREQ=500000L. Here is the sketch:
Code: Select all | TOGGLE FULL SIZE
#include <limits.h>

#include <Wire.h>
#include <Adafruit_MotorShield.h>
#include "utility/Adafruit_PWMServoDriver.h"

Adafruit_MotorShield AFMS = Adafruit_MotorShield();
Adafruit_StepperMotor *myMotor = AFMS.getStepper(200, 1);

void setup()
{
  AFMS.begin();

  Serial.begin(9600);
  delay(2000);
}

unsigned long prev = 0;
#define STEP_TIME 2500

// statistics
unsigned long t_max = 0, t_min = ULONG_MAX, t_sum = 0;
int cnt = 0;
#define MAX_CNT 3000

void add_statistics(unsigned long t)
{
    if(t_min > t)
        t_min = t;
    if(t_max < t)
        t_max = t;
    t_sum += t;
    if(++cnt == MAX_CNT)
    {
        Serial.print("cnt=");
        Serial.print(cnt);
        Serial.print(" t_min=");
        Serial.print(t_min);
        Serial.print(" t_max=");
        Serial.print(t_max);
        Serial.print(" t_ave=");
        Serial.println(double(t_sum)/cnt);

        t_max = 0;
        t_min = ULONG_MAX;
        t_sum = 0;
        cnt = 0;
    }
}

void loop()
{
    unsigned long ms = micros(), diff = ms - prev;
    if(diff > STEP_TIME)
    {
        myMotor->onestep(BACKWARD, DOUBLE);
        unsigned long ms2 = micros();
        add_statistics(ms2 - ms);
        prev = ms;
    }
}

Results:
Code: Select all | TOGGLE FULL SIZE
cnt=3000 t_min=988 t_max=1012 t_ave=997.81
cnt=3000 t_min=988 t_max=1008 t_ave=999.59
cnt=3000 t_min=992 t_max=1008 t_ave=1001.02
cnt=3000 t_min=996 t_max=1008 t_ave=1001.72
cnt=3000 t_min=996 t_max=1012 t_ave=1002.33
cnt=3000 t_min=996 t_max=1012 t_ave=1002.54
cnt=3000 t_min=996 t_max=1012 t_ave=1002.73
cnt=3000 t_min=996 t_max=1012 t_ave=1003.05
cnt=3000 t_min=996 t_max=1012 t_ave=1002.99
cnt=3000 t_min=992 t_max=1012 t_ave=998.68
cnt=3000 t_min=988 t_max=1008 t_ave=997.55
cnt=3000 t_min=992 t_max=1008 t_ave=1001.78
cnt=3000 t_min=996 t_max=1012 t_ave=1003.12

So the maximum "onestep" duration is about 1012 microseconds, which theoretically should allow to rotate at (1000000/1012)/( 360/1.8 ) = 4.94 RPS = 296 RPM.
It would be more than enough for me. However practically I still can't rotate motor faster than 120 RPM. It is exactly STEP_TIME=2500 microseconds in the code above which is much more than 1012.
Besides, it works only if I use DOUBLE stepping and connect motor in the bipolar mode. If I use the unipolar mode or SINGLE style the motor stalls, I can't even make 120 RPM.

So it seems the cause of the problem is not in "onestep" anymore.

It is a huge progress, and I will use it this way if it is not possible to enhance results. However the motor is unstable on 120 RPM and skips steps.
What could be the cause now? Does anybody have any idea? I have no experience with steppers.

P.S. This is the spec of the motor I use.
http://www.robotshop.com/media/files/pd ... -specs.pdf
Is it possible that it is too big and powerful for the shield?
AlexeyB
 
Posts: 6
Joined: Wed Oct 09, 2013 11:36 pm

Re: Adafruit_StepperMotor::onestep is too slow

by adafruit_support_bill on Fri Nov 01, 2013 7:34 am

The shield, like most hobbiest-level stepper controllers is a fairly basic controller. Getting optimal performance from a stepper requires a more sophisticated kind of stepper driver that is tuned to the drive.

Step speed is limited by step torque and torque is a function of the current applied to the coils. However, the inductive reactance of the coil means that the current can't be applied instantaneously on each step. It takes some time for it to build up. More sophisticated "constant current" drives compensate for this by using much higher voltages (sometimes several times the rated voltage of the drive) at the start of the step, but controlling the current so that it does not exceed the current rating for the motor.

This white-paper from Gecko Drives explains this and some other optimizations that you can find in high-end controllers. http://www.geckodrive.com/gecko/images/ ... 0Guide.pdf

www.pololu.com has some moderately priced stepper controller breakouts that do constant-current.
User avatar
adafruit_support_bill
 
Posts: 30794
Joined: Sat Feb 07, 2009 10:11 am

Re: Adafruit_StepperMotor::onestep is too slow

by AlexeyB on Fri Nov 01, 2013 11:30 am

Thank you for explanations and links!!!
AlexeyB
 
Posts: 6
Joined: Wed Oct 09, 2013 11:36 pm