# Filter for rotary encoder

I'm attempting to use PID for DC motor control. The motor is geared, with a magnet on the motor shaft coupled with a single hall effect sensor to help measure speed. This is all wired up to a capture and compare timer on a STM32.

In my application, the frequency of the measuerment is between 0Hz and 100Hz depending on motor speed. The period between each pulse does vary so wondering what the best kind of SW filter to use ? Currently im averaging over 5 samples, but doesn't seem to be elimnating the error that well.

• You need to be clearer about the error you are seeing and also the period variations. You should also reveal the method you are using to convert the hall effect sensor to a digital value (data sheet links help here). Mar 25, 2021 at 10:12
• Sorry, when I mean error, I'm seeing small variation in the measured intervals between pulses. I'm using the compare and capture module to measure time between adjacent rising edges and converting this to a Hz number. Mar 25, 2021 at 11:35
• Numbers are important. It's a numbers game. Mar 25, 2021 at 11:42
• What is the resolution of your measurements? It could easily be in the range of nanoseconds depending on your actual cpu and configuration. In which case you simply have too much resolution. The PID should filter it out anyways. Mar 25, 2021 at 12:11
• @Kartman, I'm sampling the sensor input every 10ms Mar 25, 2021 at 15:09

1. Moving Average

When calcuating the real time moving average of some samples it's always a good idea dividing the sum by 4, 8, 16, etc using the C shift operator.

That allows you to use integer arithmetic instead of floating point one which is way slower. This is especially true if you make calculation in an interrupt function.

Yet, the average variable used should be of a bigger size than the samples. If samples are "unsigned short" than the average variable should be "unsigned long".

Example:

unsigned short samples[256];
unsigned long average;
int i;

average = 0;
for (i = 0; i < 8; i++)
{
average += (unsigned long) samples[i];
}

average >>= 3;


Type cast is necessary. Don't trust your C compiler.

1. Interrupts and Critical Sections

If your capture and compare peripheral fires an interrupt at every double edge detection and the pulse width is evaluated in the interrupt function, than calculate the moving average within the the interrupt function (foreground) and not in the main() function (background).

• Even better, use properly defined data types (uint16_t etc) - I hate having to guess what a short or a long is going to be. Mar 25, 2021 at 10:48
• Thanks. Is there any guidance on the size of the window. I guess if the window is too big, the response will be poor ? Mar 25, 2021 at 11:38
• The other issue I may be seeing is with a delayed response, the PID is over compensating and i'm seeing big oscilations. Mar 25, 2021 at 11:41
• If the windows is big, than the moving average becomes less dependent on input data fluctuations. Mar 25, 2021 at 11:48
• If you see oscillations the PID reacts to fast. Slow down the action on the motor in this way: use a hardware timer that every 100 ms fires an interrupt. Within this interrupt put the C statements that drives the motor. I did that in the past and worked. Then, if the PID becomes too slow than bring 100 ms to 50 ms. The action on the motor must be timer driven. Mar 25, 2021 at 11:54

When hunting for noise and thinking of using filters to address it, it is best to first collect the data and then see what is appropriate.

Since you've wired the sensor to the timer capture input, then you need to make sure that those edges are REALLY clean. Glitches will throw off the timer unit, as it will see those glitches as an edge that triggers the input capture. Therefore, you will want to get an o-scope on that input and make sure that waveform looks nice and clean. Hardware techniques are best here to clean up the signal.

The next step is to simply collect data and analyze it. Ideally, you would run the motor at a constant speed, collect a sample of sensor data, and analyze it. Is it fairly close together? Mean deviation, min's, max's? A graph in Excel would be very revealing at this point. From this data, you would choose your software-side filter (if you even need one).

At this point, who knows what you will find. If you find that it's very consistent but an outlier appears randomly, then a median filter would be best. If the signals vary a bit and you want a smoothed output, then there are any number of averaging, FIR, IIR and so on filters, including some whose math is very easy for use in embedded systems (as in the math is quick and the RAM requirements are low). The exact choices depend on how much weight you want to put on recent values, as opposed to how much weight is placed on historic values. Also note that typical filters will slow your step response.

Since you are running data into a PID, then you want to make sure that your input data doesn't adversely effect the PID. The amount of noise that goes in, or conversely a long response time, will have impacts.

As a veteran of PID programming, it is very handy to have a data stream system off of the target that somehow gets into a spreadsheet on your computer. Knowing the inputs and the states of P, I and D will be tremendously useful for tuning.