Trouble measuring wheel speed with encoder

I am building a simple two wheeled robot using the following:

*Arduino Due processor
*Micro Metal gearmotor pololu.com/catalog/category/51
*Pololu 42x19mm Wheel and Encoder Set pololu.com/catalog/product/1218
*DRV8835 motor controller pololu.com/catalog/product/2135

My goal is to use the speed calculations obtained at the two wheels to determine direction of the bot. My problem is, using the code I have developed, I can’t get timely and accurate speeds. I have experimented with varying the length of time between speed measurements but have not yet obtained results that correlate with the PWM sent to the motor. Code below. Thanks in advance for your help.

int encoder0PinA = 10;
volatile long encoderAPos=0;
long oldposition = 0;
unsigned long oldtime = 0; 
long vel;
int bENABLE = 12;
int bPHASE = 13;
int modePin = 44;
int speed_forward_right;

void setup()  {
  Serial.begin (9600);  
  //  Encoder setup
  pinMode(encoder0PinA, INPUT);
  digitalWrite(encoder0PinA, HIGH);
  attachInterrupt(10, doEncoder, CHANGE);
  //  Motor setup
  pinMode (bENABLE, OUTPUT);
  pinMode (bPHASE, OUTPUT);
  pinMode (modePin, INPUT);
  digitalWrite (modePin, 1);  
}

void loop()  {  // slowly run motor from zero to max
 for (speed_forward_right = 0; speed_forward_right <= 255; speed_forward_right +=5)  { 
 right_wheel_forward();
 Serial.print  ("Right wheel PWM "); 
 Serial.println(speed_forward_right);               
 delay(1000);   
 Serial.println(); 
 }
}

void doEncoder()  {  // deltaD/deltaT to calculate speed
  ++encoderAPos;                               
  Serial.print   ("Encoder position         "); 
  Serial.println (encoderAPos);                                                                    
  if ((millis() - oldtime) > 2000)  {  vel = (encoderAPos-oldposition) * 10000  / ((millis())-oldtime);       
  Serial.print   ("Wheel speed     "); 
  Serial.println (vel);
  oldposition = encoderAPos;                 
  oldtime = (millis());
}
}

void right_wheel_forward()  {  digitalWrite (bPHASE, 0); analogWrite (bENABLE, speed_forward_right);  }

You should NEVER print from within an interrupt routine. While printing, your program probably missed some ticks. Other things can go wrong as well. An encoder interrupt routine should just count ticks, and possibly read a timer into one or more global variables and return.

In general, interrupt routines should be as short and fast as possible, so that means no delays, printing or lengthy calculations. Leave all that the to main program.

Thanks for the input. I stripped everything out of the ISR and am doing all the calculations in the loop. I still cannot get consistent speed measurements even when holding the wheel sped constant.

int encoder0PinA = 10;
volatile long encoderAPos=0;
long oldposition = 0;
long newposition;
unsigned long oldtime = 0; 
unsigned long newtime;
long vel;
int bENABLE = 12;
int bPHASE = 13;
int modePin = 44;
int speed_forward_right;

void setup()  {
  Serial.begin (9600);  
  Serial.println ("START _____________________________________________");
  //  Encoder setup
  pinMode(encoder0PinA, INPUT);
  digitalWrite(encoder0PinA, HIGH);
  attachInterrupt(10, doEncoder, CHANGE);
  //  Motor setup
  pinMode (bENABLE, OUTPUT);
  pinMode (bPHASE, OUTPUT);
  pinMode (modePin, INPUT);
  digitalWrite (modePin, 1);  
}

void loop()  { 
 speed_forward_right = 250;                                                               // PWM to right wheel, the only one I'm powering now
 right_wheel_forward();                                                                   // right wheel goes forward
 newtime = millis();                                                                      // gets new time
 newposition = encoderAPos;
 if (encoderAPos > (oldposition + 10))  {                                                 // makes sure wheel has moved by stated number of clicks
   vel = (newposition-oldposition) * 10000  / (newtime-oldtime);                          // calculates new wheel speed
   Serial.print   ("Right wheel PWM     "); Serial.println (speed_forward_right); 
   Serial.print   ("vel                 "); Serial.println (vel);       
   oldposition = newposition;                                                             // stores wheel position
   oldtime = newtime;                                                                     // stores time
  } 
 delay(1000);                                                                             // determines sampling rate
 Serial.println(); 
}

void doEncoder()  {  ++encoderAPos;  }                                                    // ISR finds new wheel position

void right_wheel_forward()  {  digitalWrite (bPHASE, 0); analogWrite (bENABLE, speed_forward_right);  }

Hello.

The Arduino Due runs at 3.3V; how confident are you that you properly recalibrated the encoders to work at 3.3V?

If you are confident in your 3.3V calibration, can you be more explicit about the results you are getting, the conditions under which you are getting them, and what you expect to get? Some pictures of your setup would probably be helpful. Also, why are you enabling the Arduino’s internal pull-up on the encoder A output pin?

- Ben

Hello.
I am experiecing something similar. When I try to measure the velocity (a constant velocity) getting the time between two pulses, this delta time varies a lot. So the speed I try to measure with the encoders varies too much (something like 30 rpm). I would like to know it these encoders are noisy or I am doing something wrong.
Another question: do I need to calibrate de encoders using those potentiometers?
Could somebody post how to read the speed the right way?

Thank you very much!!!

You need to provide more specifics than “varies too much”. What is the average measured speed and standard deviation from the average?

In any case, assuming that your encoder is properly calibrated, for several mechanical, electrical and software reasons the time between two successive encoder pulses will not be an accurate measure of the speed. Most people use a first order low pass digital filter or some other means of averaging several measurements. I find a first order “running average” to be useful, e.g.

time_between_ticks = timenow-timethen;  //time between most recent encoder ticks
timethen=timenow; //update encoder tick timestamp
average_time = (7*average_time + time_between_ticks)/8;  //update running average
current_speed = distance_per_tick/average_time; //update average speed

For faster response, use (3average_time+time_between_ticks)/4, for more slowly changing averages, use (15average_time+time_between_ticks)/16.

Finally, to save a little computing time, note that all of these divisions can be implemented with right shifts, for example average_time=(15*average_time+pulsetime)>>4

Also note that the encoders are not particularly noise free when used with PWM control on motors. You can clean this up a little bit with a capacitor in the range of 1.5 nF to 12 nF on each of the encoder output lines, and a little bit more with a capacitor up to 100 nF across the motor power terminals. (Both of them should be ceramic, ideally NP0.)
Here’s a picture of what I’m seeing after adding these clean-ups:

This is enough that a hardware quadrature decoder can read the right thing, but a “naive” reader will probably not get the right reading from this signal, especially if you do no clean-up.

The red is my 12V drive signal with PWM; the yellow is the quadrature signal (for one of the channels) smoothed out with a 12 nF capacitor.