To begin with, lets talk about what are the goals of using a filter. In the simplest terms I can think of, the purpose of a filter is to remove from a mixture some part that is unwanted while allowing the desirable part to pass through unaffected. If you think about a filter for drinking water, you would expect it to remove dirt, rust, and other impurities while allowing only clean water to pass through. When talking about filtering data, we want to remove undesired signals while allowing the desired signal to pass through with as little alteration as possible.
When looking at a signal coming from an accelerometer which is mounted on a quadcopter, there is a large potential for noise in the actual acceleration signal. If the motors or propellers are out of balance, the the shaking from them will show up in the accelerometer data. Even in an perfect environment, the accelerometer will naturally have noise of its own in its signal. So if we are going to use the acceleration data, it needs to be free of errors or else whatever we use the data for will be tainted.
To make things more complicated, a quadrotor needs fast data to keep the aircraft stable. We don't have all day for the processor to work on the data and remove the error. In an 16Mhz Arduino, we have the processing power of a 25 year old desktop (which is amazing in it's own right) so nothing we do can be super complicated. My smartphone is 100x more powerful (or even more if you include the graphics chip).
Lets start looking at data and see what all this really means.
Fig. 1 - Raw Accelerometer Data
In figure 1 above, I have plotted a graph of the raw data from the accelerometer. In this data I held the sensor and made one slow oscillation followed by four quick oscillations. For any one who likes nice pretty lines and curves, this graph is not for you. There are a lot of small wiggles that make the data look very spiky. If we are going to use this data we want to get rid of the spikiness and try to make it smooth while still following the the original data as close as possible.
Any time a signal has many spikes that happen very often, it would be considered a high frequency signal. The noise in the above picture looks like it has a high frequency. In contrast, the part of the signal we want looks like it moves in slow waves. This suggests that the data we want is a low frequency. If we want to keep the low frequency data while throwing away the high frequency noise, then we might want to use a low-pass filter. A low pass filter does exactly what its name implies, it allows the low frequencies to pass while blocking the high frequencies. We can look at the graph below and see how well it works.
Fig 2 - Raw data and a Low Pass Filter |
Above we see in figure 2 what an exponential decay low pass filter looks like when applied to our rough looking raw data. Clearly we have eliminated the high frequency spiky look. Unfortunately, there are two problems with this solution. The first is that the filtered data appears to have been shifted to the right. This indicates that the data is being delayed by the filter which is typical of this kind of filter when it is set to have a strong filtering effect. The second issue is that the data doesn't seem to follow the original data very well. If you look at the four fast oscillation, the red line doesn't seem to reach as high as it should while also not reaching as low as it should. Reducing the strength of this filter would reduce both of these problems, but also allow more noise back into the signal.
After playing with the low pass filter for a while, I concluded that I needed another type of filter as the side effects of the only using a low pass filter were killing the real signal I wanted to preserve. In the spirit of doing things the hard way, I started looking at lists of filters to see if any of them looked like itt could do a better job than the LPF. After looking for a little while I tripped across what is referred to as the Median-Filter. It looked like it had the potential to do exactly what I needed ignore the noise while following the main signal closely. I then did an internet search for "accelerometer data median filter" and found out that this is a very common filter to use for cleaning up accelerometer data. Oops, I could have saved a lot of time with a little research. Below is a graph showing the results.
Fig 3 - Raw data and a Low Pass Filter |
In figure 3 above, you can see that the median filter does a very good job of following the data while ignoring the noise. I have paired it with a very light low pass filter to help smooth some of the edges that a median filter can naturally create. This filter no longer shifts our data to the right by a significant amount so we can conclude that is is not delaying the signal very much. You can also see that even during the fast oscillations the peak of the filtered data is very close to the same height as the unfiltered data. I have created a graph to make it easy to compare this to the previous aggressive low pass filter.
Fig 4 - Both Filters |
Clearly the median filter (red line) is doing a much better job of representing the signal we actually want when compared to the low pass filter (yellow line).
Fig 4 - Final Result |
Here are some random final notes. I wrote the sliding median function myself as I wanted to make sure it ran as fast as possible and I wanted the challenge. There is a library available to do it as well for anyone who doesn't want to go through the work of writing it. I am currently using a 7 sample sliding window for my median filter which seems to be working well with the 800Hz sample rate coming from my accelerometer. Due to the time required to send the data back to the computer while sampling, the above data doesn't represent a true 800Hz sample rate as the serial print function takes a huge amount of time.
That is all for now. Let me know if you have any questions.
Thanks for reading and please sign up on the right if you want to be automatically notified when I post more updates.
~Phillip
<< Previous Quadrotor Post | Next Quadrotor Post >>
I might have tried a moving average filter. You will get more or less the same results as the median filter by adjusting the filter order to best achieve the desired trade-off between noise attenuation, phase delay, and dynamic performance.
ReplyDeleteHere's the MATLAB code to implement a 7 term FIR moving average filter:
% Define Moving Average Filter Terms
n = 7;
b = ones(1,n)./n;
a = 1;
% Filter the Raw Accel Data
smooth_signal = filter(b,a,raw_data);
I can send you some matlab code or C code if you want to try this but can't figure it out.
Thanks for the question.
DeleteA moving average filter is vastly inferior to a median filter in removing the "salt and pepper" noise that my sensor generates.
Here is a list of the standard deviations of each filter for the first 150 data points (i.e. while the sensor is laying on the table before I touch it) Units are LSB.
9.70 : Raw Data
3.84 : 7 term sliding average
2.79 : Exponential decay filter -- alpha = 1/7
1.40 : 7 term median filter
Clearly the sliding average is still letting a large amount of noise through.
In looking at delay, here are the number of samples for each filter type to reach 95% or greater on a step function.
00 : Raw Data
07 : 7 term sliding average
20 : Exponential decay filter -- alpha = 1/7
04 : 7 term median filter
These results indicate that when evaluating a step function, a sliding average takes nearly twice as long to react to a sudden change compared to a median filter of identical window size.
I assumed this would be the case when I did the original testing and so I excluded the sliding window average. In hindsight, including it for the sake of completeness might have been advisable.
~Phillip
Hi there,
ReplyDeleteHow did you measure the delay? I remember that your PID rate is 100 Hz. Your sensor data rate is 100Hz. You have a median filter size of 7. So you filter the 8 samples in 1 pid loop. In term of PID loop rate, there is no delay, isn't it.
Am I missing something?
Thanks for the question.
DeleteThe short answer is that the delay of a median filter is determined by a very simple equation. When testing by using a step function, a median filter has a delay equal to its window size divided by 2 and then rounded up to the next higher integer. So a window size of 7 samples divided by 2 equals 3.5, and that rounded to the next higher integer indicates a 4 sample delay. So assuming a sampling rate of 800Hz on the accelerometer, then this would be equal to a 5ms delay. My control loop may add an additional delay before the data effects the control system, but that is not included as part of the filter delay. In a scenario where the data has random noise, the delay is also random, although a distinct trend will act very mush like a step function.
A faster control loop will reduce the delay from the time the data exits the filter until it effects the control system, but it will never reduce the inherent filter delay.
For reference, my current control loop runs at 267Hz. I sample the accelerometer at 800Hz and store the data in it's internal FIFO buffer. Each control loop I pull all of the data that is in the buffer and filter it (3 samples). My current filter is an 11 term median filter followed by an exponential decay low pass filter with alpha set to 0.25 (bit shift of >> 2).
I hope this helps.
Phillip
is it easy to use this filter .i want a software to do this job because i don't wright matlab. i have an accelerometer 1000 Hz .i need your help .this is my email mans_78@yahoo.com i can send you my data
ReplyDeleteI have uploaded my source code to github: https://github.com/daPhoosa/MedianFilter
DeleteCan u please provide the code of sliding median function.
ReplyDeleteThank you.
email id: nehalsaraf2510@gmail.com
I have uploaded my source code to github: https://github.com/daPhoosa/MedianFilter
Delete