I am working on a mobile robot controlled via a wireless 2.4 GHz link.The receiver is connected to the Arduino Uno which serves onboard as the main controller. The most critical (and main) input channel coming from the receiver produces a very noisy signal, which leads to lots of minor changes in the output of the actuators, even though these are not needed.
enter image description here
Plot of the input of the Arduino in a 30 second interval.
I am looking for libraries that can perform efficient smoothing. Are there any signal smoothing libraries available for the Arduino (Uno)?
-
Don't know if you can find this kind of libraries but I'm afraid these might need to much CPU power for an Arduino. I'd rather go with an electronic solution (low-pass filter) if possible.jfpoilpret– jfpoilpret02/16/2014 15:30:30Commented Feb 16, 2014 at 15:30
-
I am with jfpoilpret on this. I think you need an electronic solution. I would try a stabilization capacitor, (simple low pass filter). I assume you are using an adc channel, so you should put a cap from this channel to ground. Start with values around 100pf, and work up from there.John b– John b03/26/2014 18:42:22Commented Mar 26, 2014 at 18:42
5 Answers 5
Microsmooth is a lighweight signal smoothing library currently being developed by me.
It is still being worked on and the aim is to make it lightweight in terms of memory and fast. The library provides multiple filters for smoothing:
- Simple Moving Average
- Exponential Moving Average
- Cumulative Moving Average
- Savitzky Golay Filter
- Ramer Douglas Pecker Algorithm
- Kalmogorov Zurbenko Filter
To use the library, download and add it to the source directory. Also, add the following line to your source file:
#include "microsmooth.h"
-
Hi, I'm having trouble using your library. You wouldn't use "Import Library..." to import your library right? I tried just copying the source into my .ino folder, but I'm getting errors about missing automicrosmooth.h, Serial not being defined, and missing ';'. Is this library still working? Thankswaspinator– waspinator02/20/2015 23:38:49Commented Feb 20, 2015 at 23:38
-
@waspinator Sorry about that. Fixed the errors. Thanks for the feedback!asheeshr– asheeshr02/21/2015 02:51:35Commented Feb 21, 2015 at 2:51
I think I see a lot of single-sample noise spikes in your noisy signal.
The median filter does better at getting rid of single-sample noise spikes than any linear filter. (It is better than any low pass filter, moving average, weighted moving average, etc. in terms of its response time and its ability to ignore such single-sample noise spike outliers).
There are, in fact, many signal-smoothing libraries for the Arduino, many of which include a median filter.
signal-smoothing libraries at arduino.cc:
- Paul Badger: smooth digital low-pass filter
- Paul Badger: digitalSmooth digital low-pass filter with outlier rejection
- David A. Mellis and Tom Igoe: Smoothing tutorial
- Majenki: Average Library
signal-smoothing libraries at github:
- AsheeshR / Microsmooth
- jeroendoggen: Arduino-signal-filtering-library
- karlward: Arduino data filtering library
- sebnil: FIR-filter-Arduino-Library
- daPhoosa: MedianFilter
- arc12: A Collection of Digital Signal Filters (intended for use with Arduino)
- sebnil: Selfbalancing robot in Arduino. Implemented with PID controllers, FIR filters, complementary filter.
Would something like this work in your robot? (The median-of-3 requires very little CPU power, and hence fast):
/*
median_filter.ino
2014年03月25日: started by David Cary
*/
int median_of_3( int a, int b, int c ){
int the_max = max( max( a, b ), c );
int the_min = min( min( a, b ), c );
// unnecessarily clever code
int the_median = the_max ^ the_min ^ a ^ b ^ c;
return( the_median );
}
int newest = 0;
int recent = 0;
int oldest = 0;
void setup()
{
Serial.begin(9600);
// read first value, initialize with it.
oldest = random(200);
recent = oldest;
newest = recent;
Serial.println("median filter example: ");
}
void loop()
{
// drop oldest value and shift in latest value
oldest = recent;
recent = newest;
newest = random(200);
Serial.print("new value: ");
Serial.print(newest, DEC);
int median = median_of_3( oldest, recent, newest );
Serial.print("smoothed value: ");
Serial.print(median, DEC);
Serial.println("");
delay(5000);
}
Have you tried a low pass filter? I found an example here an another one here.
Both of these libraries have a list of data being read from the analog sensor of your choice which is averaged. Every new sensor value is added to the list, and the last one is thrown out, like this:
List: 3 4 3 3 4 3 5 3 2 3 4 3
new reading added. old one thrown out
/-- /--
List: 5 3 4 3 3 4 3 5 3 2 3 4
list averaged
-
Pretty much what a simple FIR filter does with all tap values set to 1. Fiddling with the taps values can improve the signal further, but requires higher math.jippie– jippie02/16/2014 15:39:54Commented Feb 16, 2014 at 15:39
-
Note: The second link computes the cumulative moving average which is not a practical choice for actuator control, especially one which may involve frequent start and stops. The smoothed signal will always trail the peak value of the actual signal by quite a margin.asheeshr– asheeshr02/16/2014 15:40:03Commented Feb 16, 2014 at 15:40
You could filter this digitally using a low pass filter:
int valueFilt = (1-0.99)*value + 0.99*valueFilt;
Change the 0.99 to change the cut off frequency (closer to 1.0 is lower frequency). The actual expression for that value is exp(-2*pi*f/fs) where f is the cutoff frequency you want and fs is the frequency the data is sampled at.
Another type of "digital filter" is an event filter. It works well on data that has outliers; e.g. 9,9,8,10,9,25,9 . An event filter returns the most frequent value. Statistically this is the mode.
Statistical averages such as Mean, Mode etc.. can be calculated using the Arduino Average Library.
An example taken from the Arduino Library page referred to :
#include <Average.h>
#define CNT 600
int d[CNT];
void setup()
{
Serial.begin(9600);
}
void loop()
{
int i;
for(i=0; i<CNT; i++)
{
d[i] = random(500);
}
Serial.print("Mean: ");
Serial.print(mean(d,CNT),DEC);
Serial.print(" Mode: ");
Serial.print(mode(d,CNT),DEC);
Serial.print(" Max: ");
Serial.print(maximum(d,CNT),DEC);
Serial.print(" Min: ");
Serial.print(minimum(d,CNT),DEC);
Serial.print(" Standard deviation: ");
Serial.print(stddev(d,CNT),4);
Serial.println("");
Serial.println("");
delay(5000);
}
-
1Note that this will be very slow, since it's doing lots of implicit casts to float and back.Connor Wolf– Connor Wolf02/17/2014 04:18:50Commented Feb 17, 2014 at 4:18
Check out the Arduino Smooth library. It uses absolutely no arrays, no looping over past values, it's fast with constant compute time, and the objects take up 8 bytes regardless of the sample window size.
-
2It would be worth mentioning that this library, like some of the others already cited, implements an exponentially-weighted moving average. The fact that it works in constant-time and constant-size is nothing special: this is how this type of filter is always implemented. There are good use cases for an exponentially-weighted moving average, but the spiky noise of the original question is not one of them: this would be better served by a moving median.Edgar Bonet– Edgar Bonet06/18/2023 16:02:29Commented Jun 18, 2023 at 16:02