I am using a standard infared distance sensor on a small robot and i am trying to even out the readings on it so it is more accurate. The readings are very sporadic and often take huge jumps in the distance. Is there a method of programming or circuit design that might make this easier to use? I'm trying to keep my robot from running into things.
-
Can you please include which sensor(s) you are including and your current circuit/code? Thanks!– Anonymous PenguinCommented May 16, 2015 at 23:40
-
Thanks for the quick reply. The sensor is the Infrared Proximity Sensor - Sharp GP2Y0A21YK I believe.– JaredTS486Commented May 16, 2015 at 23:47
5 Answers
Wikipedia has a page on moving averages: http://en.wikipedia.org/wiki/Moving_average
I'd suggest at first try an exponential moving average:
average = a * sample + (1 - a) * average
Experiment with different values of a (with 0 < a <= 1).
The smaller a is, the smoother the curve, but also the longer (more iterations) it takes for an actual change in the input to be reflected in average.
IR sensors are very erratic in my experience. The best way is to take an average of multiple sensor readings. The number of readings required can be found by experimentation.
Example Sketch (Source)
The example below should be similar, and a good start for your model of IR sensor.
/*
Sharp GP2Y0A21YK0F infrared proximity sensor (#28995)
Collects an average of five readings from the sensor and displays the
resulting value about every half second.
See the SharpGP2Y0A21_Simple demonstration for additional usage notes,
including connection diagram.
This example code is for the Arduino Uno and direct compatible boards, using the
Arduino 1.0 or later IDE software. It has not been tested, nor designed for, other
Arduino boards, including the Arduino Due.
*/
const int irSense = A0; // Connect sensor to analog pin A0
int distance = 0;
void setup() {
Serial.begin(9600); // Use Serial Monitor window
}
void loop() {
Serial.println(irRead(), DEC); // Call irRead function to read sensor
// Print value in Serial Monitor
delay(250); // Wait another 1/4 second for the next read
// (Note: Because of delays built into the
// irRead function the display of values will
// be slower than in the SharpGP2Y0A21_Simple
// sketch
}
// Take multiple readings, and average them out to reduce false readings
int irRead() {
int averaging = 0; // Holds value to average readings
// Get a sampling of 5 readings from sensor
for (int i=0; i<5; i++) {
distance = analogRead(irSense);
averaging = averaging + distance;
delay(55); // Wait 55 ms between each read
// According to datasheet time between each read
// is -38ms +/- 10ms. Waiting 55 ms assures each
// read is from a different sample
}
distance = averaging / 5; // Average out readings
return(distance); // Return value
}
Averaging is a very good technique for improving noisy data, provided you have a “nice” noise. “Nice” meaning that you never get “atypical” data points, i.e. the error of any given data point is of the order of the “typical” error amplitude. If you have for instance additive white Gaussian noise, then it can be shown that averaging is in some way the “best” way to clean your data.
However, if you have big sporadic errors, then averaging is not a good solution. This is because you will have to average a lot of samples in order to smoothen out a single big error... and the more samples you average, the more chances you have to get another huge error that will ruin your average. Of course, averaging will somewhat improve things, and it is better than no smoothing at all, but there are better options.
The solutions to this problem lye in the realm of robust statistics. It may look like hairy math, but there is actually a very simple smoothing filter that is far more robust than the average: it's called the median. The idea is quite simple: you sort your list of samples from smallest to largest, then you pick the one sample that ends right in the middle of the sorted list. Unlike the average, this filter is completely insensitive to samples carrying huge errors, as long as the majority of samples are reasonable.
Here is an implementation of a simple moving-median filter based on the
standard libc’s qsort()
. It assumes samples have int
type, but you
can change it to whatever numeric type you want. You can also change the
filter length: longer lengths provide more robustness against series of
consecutive bad samples, at the cost of more filter delay, more memory,
and more processing time.
/*
* Comparison function for qsort(): return an integer "less than, equal
* to, or greater than zero if the first argument is considered to be
* respectively less than, equal to, or greater than the second."
*/
int compare(const void *pa, const void *pb) {
int a = *(int *)pa;
int b = *(int *)pb;
return a<b ? -1 : a>b ? +1 : 0;
}
#define FILTER_LENGTH 11 // length of the median filter, should be odd
/*
* Input: raw sample.
* Ouput: filtered sample.
*/
int median_filter(int data) {
static int input_buffer[FILTER_LENGTH];
static int input_index;
int sorted_buffer[FILTER_LENGTH];
// Store the current data point.
input_buffer[input_index] = data;
if (++input_index >= FILTER_LENGTH) input_index = 0;
// Sort a copy of the input buffer.
for (int i = 0; i < FILTER_LENGTH; i++)
sorted_buffer[i] = input_buffer[i];
qsort(sorted_buffer, FILTER_LENGTH, sizeof *sorted_buffer, compare);
// Return median.
return sorted_buffer[FILTER_LENGTH/2];
}
As I did not explicitly initialize the filter memory, this will output zeros the few first times it is called. I encourage you to try this filter and compare against an exponential moving average, and only then decide which works best for you.
The Sharp GP... infra red proximity sensors can produce noisy output sometimes. This is partly caused by the fact that they work by repeating a brief, powerful IR pulse.
So - the very first tweak I would suggest when using any of these sensors is to try a cheap 10 micro Farad electrolytic capacitor on the 5V power connections, reasonably close to the sensor itself. This is mentioned on some of their datasheets, but gets forgotten...
My solution keeps reading for cycles (10 times) and calculate average it.
Be careful voltage output of many surface NOT same.