AccelStepper library

I'm planning to use this library to control two stepper motors (200step/rev) on a self balancing robot, but will this library work well if the looptime of the balancing algorithm exceeds the step period?

The sensor readings and calculations take approx 5ms (with a frequency of 100Hz). If the run() command has to be called of each step doesn't that mean that there will be no steps during that period?

Btw. The stepper is controlled by an 8825 driver, which means that the Arduino sends one pulse for each step.

The run() function will issue a step to a motor when it is time to step, so yes the loop() time needs to be shorter than the shortest step time. Maybe there are ways to speed up your loop? Integer math, change ADC clock... ?

groundfungus:
The run() function will issue a step to a motor when it is time to step, so yes the loop() time needs to be shorter than the shortest step time. Maybe there are ways to speed up your loop? Integer math, change ADC clock... ?

I don't use ADC as the sensors have i2c communications. The PID-library, as well as the filtering and angle calculations use floats.

I guess I'll try to run the stepper motors off the timers on the arduino (e.g fast PWM) and then change the pulse frequency (stepper speed) once during each loop.

Maybe only do a small part of the sensor readings and calculations in each iteration of loop().

Have you considered using a DC motor in place of the stepper motor. The PWM output for a DC motor will continue in the background while you are doing the calculations.

You may be able to use a hardware Timer to control the stepper motor. Set up a low-duty-cycle PWM output and then change the frequency rather than the duty cycle. But I don't know if it would be easy to get the range of frequencies you may need.

...R

Robin2:
Maybe only do a small part of the sensor readings and calculations in each iteration of loop().

Have you considered using a DC motor in place of the stepper motor. The PWM output for a DC motor will continue in the background while you are doing the calculations.

You may be able to use a hardware Timer to control the stepper motor. Set up a low-duty-cycle PWM output and then change the frequency rather than the duty cycle. But I don't know if it would be easy to get the range of frequencies you may need.

...R

I have DC motors on the robot now, (I mounted DC motors while I waited for the steppers to arrive). It works, but the robot starts to wander off in one direction since I'm not able to get the sensor in perfect alignment with the c.o.g (either physically or with software offset). I'm using a PD controller to set the pwm based on the tilt angle, and I've also tried using another PID controller to control the tiltangle based on the posision (using encoders), which gives me problems with escalating oscillations. Not sure if this is the best way to stabilize it though. Perhaps I should regulate the velocity with a pid controller as well?

Btw. I recon I will be able to get frequencies down to 30Hz with prescaler of 8. If I use halfstepping then that will account to 3.6deg per second?

Lars81:
Btw. I recon I will be able to get frequencies down to 30Hz with prescaler of 8. If I use halfstepping then that will account to 3.6deg per second?

I'm not the expert on balancing robots - I have never built one. But won't you need to be able to get down to single steps?

See this link where @zhomeslice demonstrates a balancing robot

...R

Robin2:
I'm not the expert on balancing robots - I have never built one. But won't you need to be able to get down to single steps?

See this link where @zhomeslice demonstrates a balancing robot

...R

You mean to correct for very small errors in tilt?

I thought about it, but I don't think it will matter much. Even with the DC motors I can see that they don't have a perfectly smooth start, as the torque has to overcome friction and the pwm nescessary for rotation isn't perfectly consistent. Also with the DC motors there is a slack due to the gearbox, and it doesn't seem to be a huge problem. With a 30Hz signal I will be able to stop it after a single step also, as I can control the speed in every loop and each loop is less than 1/30 sec.

I wrote a small sketch to test this stepper motor control, but I can't get it to work properly.

I use Timer 1 to create a PWM signal with fixed pulsewidth where the frequency can be set to correspond to the desired stepper velocity (steps pr sec). This frequency is calculated in the function calcSpeed(acceleration, update interval). When I update the frequency inside the function it works, but stutters sometimes and my guess is that this happens because it updates the new frequency too close to the last pulse, and that this can be fixed by updating to the new frequency after the current cycle is over by setting the frequency inside an interrupt routine that is called when the counter overflows. I've tried to the best of my abilities to copy the examples in "Gammon Forum : Electronics : Microprocessors : Interrupts", but I'm not able to make the ISR work.

const int stepPin = 9;
const int directionPin = 8;

volatile float frequency = 50; // starting frequency
const byte prescaler = 8;
unsigned int pulsewidth_us = 50; // us
unsigned int pulsewidth_counts = pulsewidth_us * 2; // counts

float maxFrequency = 2400; // Hz
float minFrequency = 50;

void setup() {
  Serial.begin(115200);
  // put your setup code here, to run once:
  pinMode(directionPin, OUTPUT);
  digitalWrite(directionPin, LOW);
  pinMode(stepPin, OUTPUT);
   //Timer 1: Fast PWM, non-inverted, prescaler = 8.
  bitWrite(TCCR1A, COM1A1, 1);
  bitWrite(TCCR1A, COM1A0, 0);
  bitWrite(TCCR1A, COM1B1, 0);
  bitWrite(TCCR1A, COM1B0, 0);
  bitWrite(TCCR1A, WGM11, 1);
  bitWrite(TCCR1A, WGM10, 0);

  bitWrite(TCCR1B, WGM13, 1);
  bitWrite(TCCR1B, WGM12, 1);
  bitWrite(TCCR1B, CS12, 0);
  bitWrite(TCCR1B, CS11, 1);
  bitWrite(TCCR1B, CS10, 0);
  
  ICR1 = F_CPU / prescaler / frequency; // period in clock cycles for starting frequency
  OCR1A = pulsewidth_counts; // pulse width
}

boolean newFrequency = 0; // flag to know if a new frequency is calculated.
ISR(TIMER1_OVF_vect){
  if (newFrequency){
  ICR1 = F_CPU / prescaler / frequency;
  newFrequency = 0;
  }
}

void loop() {
  calcSpeed(500,50); // 500 Hz/s^2, update interval 50ms
}

void calcSpeed(int a, unsigned dt) {
  // f += a * t;
  static unsigned long PT = 0;
  unsigned long CT = millis(); // Check if time to update frequency
  if (CT - PT > dt) {
    PT += dt;
    float t = (float)dt * 0.001;
    frequency += a * t;
    if (frequency > maxFrequency) frequency = maxFrequency;
    if (frequency < minFrequency) frequency = minFrequency;
    //ICR1 = F_CPU / prescaler / frequency; // set new frequency <- works, but sometimes causes the motor to shake. Find a way to make this happen after a full cycle?
    newFrequency = 1; // set flag so that interrupt routing updates the frequency at next cycle.
    Serial.println(frequency); 
  }
}

Robin2:
Maybe only do a small part of the sensor readings and calculations in each iteration of loop().

I'd phrase that as decouple your stepper loop from the PID loop. Doing stepper from an ISR sounds
a good way (I think that works with the library, not totally certain, but you'll have put some of the
calls in critical sections).

MarkT:
I'd phrase that as decouple your stepper loop from the PID loop. Doing stepper from an ISR sounds
a good way (I think that works with the library, not totally certain, but you'll have put some of the
calls in critical sections).

Do you know if the overflow interrupt (TIMER1_OVF_vect) works when the timer reaches the value in ICR1 (as I've tried in the code above), or does it only work when the timer reaches 65535?

The datasheet will tell you all the details for each of the 16 modes for timer1, I haven't memorized
them, life is too short.