How do I accurately calculate the speed of a rotary encoder at a high sample rate?

I'm aiming to control a motorized joint at a specific speed. To do this, I'm planning on attaching a rotary encoder to do this.

I'll be controlling the motor with a PID controller. With this PID controller, I need to control the joints based on their velocity.

Since:

speed = distance / time


It would make sense to do something like this:

double getCurrentSpeed() {
return (currentAngle - lastAngle) / samplingRate;
}


However, there's an issue; the encoder doesn't provide a high enough resolution to accurately calculate the speed (the sample rate is too high). I want to have updated data every 5-15 ms (somewhere in that range as my current motors seem to be able to respond to a change in that range)

• 14 bit precision (roughly 0.0219726562 degrees per "step" of encoder
• I'd like to be able to calculate as small of speed differences as possible
• As the motors will be going fairly fast (120+ degrees/second at highly variable speeds and directions), so the feedback has to be accurate and not delayed at all

So, a couple of ideas:

• I can find encoders that I can sample at a very high rate. I was thinking about sampling the time between the changes of the encoder's value. However, this seems finicky and likely to be noise-prone
• I could do some sort of rolling average, but that would cause the data values to "lag" because the previous values would "hold back" the output of the calculations somewhat and this would play with my PID loop some
• Noise filter of some sort, although I don't know if that would work given the rapidly changing values of this application

However, none of these seem ideal. Is my only option to get a 16 bit (or higher!) encoder? Or is there another method/combination of methods that I could use to get the data I need?

• You might consider that for greater accuracy at high speeds, measure distance traveled in a given time (for example encoder pulses counted per ms), while for greater accuracy at lower speeds, measure elapsed time for a given distance (for example timer count between encoder pulses, or for moderate speeds timer count between n encoder pulses). For greater dynamic range, do both. – Tut Jun 11 '15 at 13:35
• A possible implementation on the above: 1) Measure the time between every encoder pulse; 2) Count the number of complete encoder pulses in say 10ms = n; 3) From your pulse timer data, sum the times taken between the last n+1 encoder pulses = T ie: the sum of n periods; 4) Your speed is calculated using (n*distance_per_pulse)/T – Tut Jun 11 '15 at 14:46
• Just to clarify: are you using an absolute or incremental encoder (do you read position from it or just ticks)? – mactro Jun 11 '15 at 15:07
• @mactro absolute – Anonymous Penguin Jun 11 '15 at 15:19
• Can you interrupt on change of the lsb of your encoder? – Tut Jun 11 '15 at 15:43

I think you're getting a little confused - you mention a 14b encoder that you have, then ask if you should get a 16b encoder to sample faster. While resolution and the time it takes to query a device can be somewhat related, the electronic interface has a lot more to do with how quickly you can sample than the sensor resolution. For instance, an I2C protocol requires the master to call the sensor by "name" (address), so the time you spend transmitting that information means you can't sample as quickly as you could with SPI, where you use a chip select and start getting data immediately. Both methods transmit data one bit at a time, so both take longer than would a sensor with a parallel interface, where you read each bit simultaneously.

:EDIT: on my phone, pushed enter on accident.

Regarding sampling speed, 120deg/s is 0.6deg/5ms, which means your encoder should increment nearly 30 times (0.02 to 0.6) in that interval. You should always consider filtering the signal, because resolution isn't just some number that's exact. It's better to think of it as wedges. Anywhere in a particular wedge will give you the same reading. This means that, if you were perfectly centered on a bit (wedge), you are likely to get 30 increments per sample, but if you start a little to one side you might get 29 or the other side may give 31.

You can generally watch this behavior on a per sample basis - you may get increments of 29 29 29 30 30 31 29 29... You can watch the measurements change slowly because you are essentially strobing a rotating shaft and if you don't hit a multiple of speed exactly you'll get a phase roll. If you don't filter this signal then your robot will think you're actually going those speeds.

Every filter is going to, uh, filter your signal, so then it's up to you how to handle that alteration to your signal. I would suggest a simple lag filter as they are effective, exceedingly simple to implement, and there are really straightforward design guidelines to help you maintain whatever level of responsiveness you would like.

• Re: 1st paragraph--you're misunderstanding the issue here. The issue is that the change in the value of the rotary encoder is so small at my sampling rate that it causes the acceleration values to jump around. A greater resolution would mean that it would give me a greater difference of value in that small timeframe, giving me more accurate values. – Anonymous Penguin Jun 11 '15 at 14:15
• @Annonomus Penguin - to clarify, are you saying that at one step you have a "speed" or movement of just one encoder tick, then several steps with zero, then another with one tick again? (In other words a very low speed.) And this is why you ask about a higher resolution encoder? – Andy Jun 11 '15 at 14:42
• @AnnonomusPenguin, I'm not misunderstanding. As I stated, you are looking at approximately 30 increments per 5ms sample time at your top speed. You will always get inconsistent samples because you are probably not going to rotate and sample exactly in synch with the bits of the encoder. A higher resolution encoder will get you better velocities, but you will always get the random accelerations because of sampling inadequacies. The lag filter guidelines I linked to will show you how to design a filter that meets arbitrary response criteria. – Chuck Jun 11 '15 at 14:47
• So how about an example. $2^{14}$ gives 0.02197 deg/bit. 120 degrees per second at a 5ms sample period is 0.6 degrees per sample. 0.6 deg divided by the samples of 0.02197 gives a reading of 27.3. Your sensor doesn't do fractions, so you're going to get 27 or 28 (presumably). This corresponds to an increment of 0.5932 or 0.6152 degrees, divided by your sample time of 5ms, gives a speed of 118.7 or 123 deg/s. – Chuck Jun 11 '15 at 15:05
• $2^{16}$ gives 0.00549 deg/bit, with the same 0.6 deg/sample. Here you'll get 109.22, but again, you'll only get 109 or 110. This gives you an increment of 0.5988 or 0.6042, for speeds of 119.8 or 120.85. Again, your samples here are better than the $2^{14}$ encoder is, but you will never get one that is absolutely correct. You will always get sporadic acceleration bumps due to sampling inadequacies, so you need to filter your encoder output. – Chuck Jun 11 '15 at 15:06