Rover 5 Encoder Feedback Loop - speed to encoder pulse duration conversion

To use the Rover 5 encoders to regulate speed, you have to be able to calculate the expected time between encoder interrupts.  Each Rover 5 motor runs at a max speed of 8500RPM.  At that speed, you will measure 3676 usecs between each encoder state change (high to low, or low to high).  That's the output of a single (A or B) encoder.  A quadrature configuartion produces changes at 2X the frequency.

If you use the PWM output of something like an Arduino, you will write a value between 0 and 255 to the PWM (analogWrite) ouput.  The problem is to covert speed values into expected time durations between encoder state changes.  These durations will range from infinity (stopped) to 3676 microseconds (fullspeed).

The formula requires that the "Math.h" library be included.  "Speed" is a value from 0 to 255 and "pulseWidthMin" is the time between two encoder state changes at maximum motor speed.  The two "pulse" values are long integers.

    pulseWidth = pulseWidthMin * pow( 2, ( 8 - ( log( Speed + 1) / log( 2))));

[ The formula would be simpler as: pulseWidth = pulseWidthMin * pow( 2, ( 8 - ( log2( Speed + 1)))); but Math.h does not include log2 functions. ]

Speed 0 (stopped) will produce a calculated pulse duratioin of almost a seond (941055 usec), but the real duration would be Infinity.  Therefore Speed 0 needs to be handled as special case.

Isn’t the speed dependent on

Isn’t the speed dependent on the actual diameter of your wheels?

I am using these all terrain wheels:

20150831_028.jpg

which make a complete different speed because the distance per wheel turnaround is different than with the tracks that came with the Rover 5. In this case one wheel turn is approx 41cm!

Therefore I had to measure the encoder count per 1 wheel turn. And this is different than the 1000 per 3 turns which are written in the spec. All 4 encoders for the 4 motors deliver different values.

Rover 5 Specs

I use the term ‘speed’ to identify a value between 0 and 255 written to the PWM output,
not the actual velocity of the robot over the ground in a certain direction.

The spec sheet is a little misleading because 1000 per 3 turns of the drive wheel
is the output of an XOR circuit feed by the the two encoder outputs (A & B) coming from each wheel.
Each individual encoder (A or B) changes state only 166.66 times per single drive wheel rotation.

Rover 5 Encoder and Motor Specifications:
-----------------------------------------------------------------------

     Motor speed: 8,500RPM @ 7.2V
     Motor stall current: 2.5A
     Output shaft stall torque: 10Kg/cm
     Gearbox ratio: 86.8:1
         4.166:1 motor -> encoder
         20.833:1 encoder -> wheel (two gears: 4.166:1 & 5:1)
    
    Encoder type: Quadrature, hall effect magnetic
    Encoder resolution: 166.66 state changes per wheel rotation
    Encoder Speed: 272 state changes/sec at 8,500RPM

    Drive Wheel Rotational Speed: 1.632 rev/sec @ 7.2V
    Drive Wheel Circumference = 7.5" roughly (7.4 - 7.8)
    Rover 5 Speed: roughly 12"/sec.

Identical values written to each PWM output [ analogWrite() ] will result in different motor RPM
due to:
    1) minor differences in individual motor construction
    2) variations in supply voltage

Your chassis looks great with those wheels. Each wheel will turn at a different rate
without encoder feedback.
 
Good luck.

Rotational velocity isn’t dependent on wheel size

MEgg,

you’re confusing two different things. The spec says, the XOR’ed encoder signal fires 1000 times per 3 turns of the wheel. That’s true regardless of the size of the wheels. The size of the wheels determines how many cm / in you travel with 3 turns of the wheel of course. 

I’m building a robot on the Rover 5 platform as well, see my description on LMR

You can also check out my Arduino code that controls the motors on Github. The SMotors struct controls the motors and used PID to achieve the desired (rotational) speed on all 4 wheels, so the robot drives straight. 

SMotors::setSpeed sets the desired speed in encoder ticks per second. My motors achieve at most 500 encoder ticks/s. (I measured this). Note that SMotors::setSpeed does not control the  motors yet. SMotors::onInterrupt simply counts the encoder ticks. SMotors::ComputePID does the magic using the Arduino PID library. It compares the desired speed (set by setSpeed) with the measured speed (by onInterrupt), both in encoder ticks per second. The PID library computes the new PWM output for an individual motor based on the difference between desired and achieved speed.

Each of my four motors reacts very differently to the same PWM output. That’s why I (and probably you too) need to use PID to control  motors individually.

I made a couple of tests and measurements to see how my motors performed and how to calibrate the PID algorithms, see this Excel sheet. It contains two sheets showing the desired speed (in Ticks/s), the measured Ticks/s for each motor and the different PWM outputs for each motor that the PID library set. The charts visualize the response of the motors to the desired speed. The two sheets used different parameters for the PID algorithm.

Another Excel sheet in my GitHub repository shows how differently my motors reacted to the same PWM output.

Hope this helps.

Sebastian

Ok both of you mean speed as

Ok both of you mean speed as “encoder speed” and not the real ground speed. I also realized that the 4 encoders are producing different values and not the 1000/3 .

But the consecutive encoder values per time per motor are linear according to my measurements. I measured this for several PWM duty cycle values for 2 motors currently. Also the mean value and the error values seem to be quite easy.

Correct me if I am wrong: If I know the real encoder value per turn then I know the speed per motor also as real ground speed. I measured that e.g. my left back motor needs approx 320 encoder changes to make one turn so after one turn I know that these were 41cm for this motor/wheel and it only depends on the PWM duty cycle how fast these 41cm have been done. The lowest encoder value per turn is 236 and this will be a problem when positioning because accuracy will be 0,173728813559cm at max, but I do not see a problem here.

:wink:

It would be more difficult, if the encoder produces non linear values per time for one PWM duty cycle value say PWM40. I also realized that my motors have zero movement and are humming under PWM25 duty cycle.

Btw. these are the wheels: http://robosavvy.com/store/dagu-pack-of-4-all-terrain-wheels-metallic-silver.html

Yes, exactly. Speed ==

Yes, exactly. Speed == encoder speed. 

I’m surprised you measure a different number of encoder ticks for a precise wheel turn. As far as I know, most encoders count an optical signal. This doesn’t sound like it should report different numbers for different wheels. This would leave me with the conclusion that either your motors are broken or your measurements are off. The difference between 320 and 333 is certainly small, but the difference between 236 and 333 isn’t. I have no idea what’s going on there. Maybe you can check with OddBot here on LMR, he works for Dagu. 

I don’t understand what you mean with “non-linear values per time”. If you power a motor with a PWM value of 40, it will rotate at some speed of x deg/s. x will be slightly different for each motor. Let’s say the second motor will only turn with x0.9 deg/s. 

Even worse, when you double the PWM value to 80, the motors will react differently. Motor 1 most probably won’t turn with 2x deg/s and motor 2 very certainly won’t turn with 2x0.9 deg/s. My motors had a different response curve to the PWM value. I didn’t really check if their response was linear, but I doubt that too, because their response was completely different when turning backwards. 

Make sure you test the motors under load, i.e., driving your robot around and not rotating in thin air. Drive them backwards and forwards and measure. 

PS: As you might have seen, I’ve used the same wheels. They work fine. Check out my 3D printed wheel adaptors! They fit the wheels better. 

Absolutely gosh darn right.

It’s mechanical.  It’s a 20.833:1 ratio between the drive wheel and the encoder wheel.  If the you grab hold of the drive wheel and twist it exactly one full turn, the quadrature encoder circuit will generate 333 state changes.  Not 330 or 340, but exactly 333.  If it doesn’t, then there’s either something wrong with the way you’re counting state changes or your encoder is seriously broken.

Theophil,  I spent a lot of time figuring out how to translate PWM input values (0-255) into durations between state changes.  Now I find out about the PID_v1 library.  I am looking forward to trying that out.

I stumbled across your site once before and I was intrigued by your work with mapping.  I’m starting to do something similar.  At first glance your code is very readable and I will enjoy stealing whatever I can.  Thank you for your hard work.

> Maybe you can check with

> Maybe you can check with OddBot here on LMR, he works for Dagu.

OddBot left this forum which is a huge loss, that’s what I realized - and I am not that long in this forum.

Mechanical?

@mechanical: isn’t there a hall sensor in there according to one of the posters knowing Dagu 5 (who is not present in LMR anymore)?

The measurement of encoder counting is done by a simple program (I just post
the sceleton):

volatile unsigned long encodercountHR = 0;
volatile unsigned long encodercountHL = 0;
[…]
void encodercountHR_intproc()
{
    encodercountHR++;
}

void encodercountHL_intproc()
{
    encodercountHL++;
}

[…]

void setup()
{
[…]
  attachInterrupt(digitalPinToInterrupt(MOTOR2INT), encodercountHR_intproc,CHANGE);
  attachInterrupt(digitalPinToInterrupt(MOTOR1INT), encodercountHL_intproc,CHANGE);
[…]
  time = millis();
  time2= 0;
  time3= 0;
}

void loop()
{
  if (Serial2.available())
  {
     // setting the motor to direction and PWM-duty cycle with certain keystrokes
  }
  time2=millis();
  if ((time2 - time)>=1000)
  {
     time3=time3+(time2 - time);
     Serial2.print(“Time: “);
     Serial2.print(time3);

     Serial2.print(”, encoder-HR:”);
     Serial2.print(encodercountHR);
     Serial2.print(", encoder-HL:");
     Serial2.println(encodercountHL);
     time = millis();
  }
}

Thats how I simply get the time/encoder values for a certain PWM duty cycle for two motors currently.
Since the loop with non serial input available is very short with the 
  if (Serial2.available())
  {
  }
(unless Serial2.available() itself is very slow which I did not measure) measurement should be ok.

There might be a slight delta of time and encoder between
time2=millis();
and
Serial2.print of the encoder values, but if I would miss encoder values,
then there would not be a straight line that one can produce out of the values, but jumps.
The lines of the two motors have a different gradient which means they have a
different increase of encoder values which matches with the different encoder
values per turn.
Other posters which left this forum told me, that the difference to 333 is quite ok and does not mean I have a broken Dagu 5.

Try using a Timer interrupt

I also believe that your Dagu Rover 5 is not “broken.”  Instead there is something wrong with the way you count encoder pulses.

The encoder wheel will turn a little less than 21 times (20.833) for each turn of the drive wheel.  You will count 16 state changes for every turn of the encoder wheel:  16 * 20.833 = 333.328… exactly.

Your code appears to be trying to count the number of state chagnges (encoder pulses) per second, not per drive wheel turn.  That number can be anything from 0 to over 700, depending on your PWM duty cycle and how much voltage your feeding your motors.

If counting puilses per second is what you want to do, try using a Timer interrupt to measure 1 second.  It will provide a much more accurate count.

Good luck.

The main thing for me is,

The main thing for me is, that the 4 motors have different encoder values per turn (not measured with the above sceleton) and that this difference fits to the gradient of the measurement of encoder/time value pairs.

There for I will have different encoder values per 1 turn per motor and not the specified 333.

I count the pulses with the simple

void encodercountHL_intproc()
{
    encodercountHL++;
}

The difference is only when I do print out a time/encoder value pair.

Last try

As I said, the motor to encoder to drive wheel path is mechanical.  It is geared.  There is no slippage, no friction.  Unless it’s broken, it is precise.  One turn of the Rover 5 electric motor gets you 3.84 encoder pulses.  86.8 turns of the motor get you one turn of the drive wheel.  Do the math.  Multiply 3.84 times 86.8.

Unless you use feedback from the encoders, you WILL get a different number of pulses per second per motor. You will never get a different number of pulses per turn of the drive wheel from any Rover 5 encoder.

Maybe different versions of Dagu Rover?

My encoders show per 1 turn:

Motor 1 : 320 encoder "ticks"
Motor 2 : 236 encoder "ticks"
Motor 3 : 320 encoder "ticks"
Motor 4 : 280 encoder "ticks"

Cannot help, but this is what I measure. Maybe there are different versions of encoders on the Dagu 5 rover on the market?

measure drive wheel rotation

How do you measure drive wheel rotations?  Your code appears to be adding up encoder state changes every 1000 milliseconds (or more):

        if ((time2 - time)>=1000)

The easy way to measure one drive wheel rotation is to count 333 encoder state changes; but I am anxious to know your method.

The measurement of ticks per

The measurement of ticks per one turn was not done with the code which only prints out the ticks every second.

The interrupts are counted in

void encodercountHR_intproc()
{
    encodercountHR++;

and the other 3 interrupt routins. There are no delay() in the program. I also let the wheel turn till there were 333 encoder ticks and for the motor which has less encoder ticks per 1 turn this showed more than one turn. Then I made an input via serial to give certain tick limits and came to the approximate values of

Motor 1 : 320
Motor 2 : 236
Motor 3 : 320
Motor 4 : 280

For more accurate values I would need some light-sensitive barrier or similar so this may be 13 up or down which means motor 1 and 3 seem to be ok, but motor 2 is far away from the normal 333 and motor 4 also a bit.