Encoder::GetRate()

Hi,
Just to clarify, what does Encoder::GetRate() do? I heard that it is a nasty function, but I have some other thoughts…

THANKS!!!

-Masoug

Well, if it didn’t have any bugs, it does what you would expect. It gives the time rate of change of the encoder signal by deriving the frequency of the pulses detected by interrupts triggered by the A an B encoder channels.

http://www.chiefdelphi.com/forums/showthread.php?t=82171 addresses some obvious coding errors and suggests a code patch that corrects the function for an erroneous double output. Joe Hershberger is working the why’s of the problem. This thread includes a long discussion of the problem , various algorithms used in encoder.c and some of the more subtle points that arise under unusual input oscillatory signals. If you have some time, wade through it and you will learn more than you wanted to about quadrature encoders and how to decode them conventionally (unconventional hysteresis decoding is debated but is not used in GetRate()).

If you just apply the proposed patch, it should work fine for you. There might be some additional fine tuning by Joe pending his analysis and my suggestions. Maybe Joe will chime in with some more info.

Edit: I have a little more time to comment today.

Since the encoder is a type of analog to digital converter it suffers from the same quantization errors as regular A/D’s. Small amplitude signal variations within quantization thresholds will go undetected but when sustained over a threshold (ie from an oscillatory vibration) they are detected and amplified due to the round off errors. Joe’s GetRate() algorithm will smooth out these amplified errors by detecting same edge reversals and not allowing rate changes until a new edge is detected.

Also , since the rate is derived from the actual period between pulses , it is insensitive to user iteration cycle time. If a user derives rate by simply taking differences in GetDistance() values and dividing them by the iteration cycle time he will have spikes equal to 1 count/(iter cycle time) on each pulse with a bunch of zeros in between. So usually additional filtering is required if these derived rates are to be used in robot control systems. Therefore, the GetRate() output can be quieter for use with indicators , logic comparators and as a rate term (D) in PID contollers.

The GetRate() does have some tricky features such as SetMinRate() or SetMaxPeriod(). Since the rate is based upon the period between the last two pulses, when the encoder stalls it will hold the last rate in its registers until a timer expires and the rate is forced to zero. This timer is set by SetMaxPeriod(). So if you set this too long, then an abrupt stall after a large rate will give you a distance error = last_rate_before_stall*timer_period if you are by chance integrating the GetRate() output. So I would not recommend integrating GetRate() and expecting to recover a position change that would match the change indicated by GetDistance(). Something like this might occur if you are resolving a body axis velocity computed from GetRate() into fixed inertial axes and integrating North/East components for a navigation function. Here it would be better to use the time derivative of GetDistance().

Thanks for the quick reply, so if there are no errors, it returns the number of pulses over time?

How would I get a speed value from it? IF GetRate() is REALLY not THAT good, then would GetDistance() work?

THANKS!!!

-Masoug

GetRate() automatically scales the pulse rate to get speed. The output = distance_per_pulse/pulse_period, where pulse_period is the time between the last two recorded pulses unless there is a stall condition. In a stall condition, pulse_period is set to “infinity” after the max_period time is exceeded. This drives GetRate() to zero.

GetDistance() is working ok. Only GetRate() has a few bugs. As I mentioned before, a temporary patch has been defined to get this operational on first forge and will surely be corrected in a software update before next season.

I am sorry if I don’t understand. If the goal is to get velocity, wouldn’t it be easier to do this instead:


vel = (currentDistance - previousDistance)/period;

This should work unless there is a problem with GetDistance() too.

If the period is identical to the pulse period, then this is the same as GetRate() (less some filtering).

If the period is set by a periodic loop outside of encoder.c and not the actual pulse period, then vel is an average speed over that sample period. As I mentioned in the first post, if the sample period is much shorter than the actual pulse period, then this “vel” approaches an impulse train with zeros in between. These impulses can cause trouble if there are nonlinearities in the system such as limits or threshold detectors. So with very high sample rates, additional filtering is needed to negate these effects. The simplest way to filter the impulses is to increase the sample period over which you obtain the average rate, but this causes added delays is detecting speed changes.

So the GetRate() output is a nice compromise. It detects when a pulse has changed and then adjusts the rate and holds it in a register for whoever wants to use it. The averaging time varies with the pulse period and it has an artificial max period but it eliminates the impulses present with very high sample rates and minimizes the need for additional filtering.

I might add that encoder rate noise from other sources often dictates that even GetRate() be further filtered.