Raw sensor readings are noisy, and a robot that reacts to every wobble steers badly. Three small filters fix most of it: a moving average, an exponential filter and a median. This guide shows what each one does to the same readings, with the arithmetic worked by hand, so you can pick the right one.
Status: the filters are standard textbook methods. The sketches are untested outlines and the numbers below are a hand-worked example, not data from a real sensor.
Two kinds of noise
Jitter is small random variation around the true value. Spikes are occasional wild readings, such as an ultrasonic sensor returning a very short distance for one sample. Averages smooth jitter well but are pulled by spikes. A median ignores spikes but smooths less. Knowing which problem you have decides the filter.
The example readings
Take a distance that is really 100 cm, with one bad reading of 40: 100, 100, 100, 40, 100. Here is what each filter reports at the fifth sample.
| Filter | How it works | Result at the spike | Next sample |
|---|---|---|---|
| Raw | No filtering | 40 | 100 |
| Moving average, window 5 | Mean of the last 5 values | (100+100+100+40+100)/5 = 88 | Stays below 100 until the spike leaves the window |
| Exponential, alpha 0.2 | new = old + 0.2 × (reading − old) | 100 + 0.2 × (40 − 100) = 88 | 88 + 0.2 × (100 − 88) = 90.4 |
| Median of 3 | Middle of the last 3 sorted | Sorted 100, 40, 100 gives 100 | 100 |
The spike drags the average and exponential filters down to 88 and they recover slowly, while the median of three is untouched. If a false 88 cm reading would make your robot swerve, the median is the better defence against spikes. If your signal is merely jittery, an average or exponential filter gives a smoother line.
Moving average
// Untested outline.
const int N = 5;
int buf[N];
int idx = 0;
long sum = 0;
int movingAverage(int x) {
sum -= buf[idx];
buf[idx] = x;
sum += x;
idx = (idx + 1) % N;
return sum / N;
}The buffer starts at zero, so the first few outputs read low. Fill the buffer with the first reading, or ignore the output until N samples have arrived. The cost is lag: a larger window is smoother but responds later, which matters for a fast line follower.
Exponential filter
// Untested outline.
float filtered = 0;
bool first = true;
const float ALPHA = 0.2;
float expFilter(float x) {
if (first) { filtered = x; first = false; }
else filtered += ALPHA * (x - filtered);
return filtered;
}It needs one variable and no buffer, which suits a small board. A small alpha is smoother and slower; a large alpha follows the reading more closely. The complementary filter guide uses the same idea to combine two sensors.
Median of three
// Untested outline.
int median3(int a, int b, int c) {
if (a > b) { int t = a; a = b; b = t; }
if (b > c) { int t = b; b = c; c = t; }
if (a > b) { int t = a; a = b; b = t; }
return b;
}Take three readings and return the middle. For an ultrasonic sensor, see how the HC-SR04 reliable distance guide combines a median with a timeout.
Which one to use
| Problem | Choose |
|---|---|
| Occasional wild readings (ultrasonic, IR) | Median of 3 or 5 |
| Steady jitter (analog sensors, battery voltage) | Exponential filter or moving average |
| Both | Median first, then an exponential filter |
| Fast control loop where lag hurts | Small window or a larger alpha; test on the bench |
Common mistakes
- Filtering so hard that the robot reacts late to a real obstacle.
- Filtering a value that is used as a stop condition with the same filter as one used for display.
- Using integer division and losing the fractional part in an exponential filter.
- Calling
delay()between samples; use millis() timing instead.
A test plan
- Print raw and filtered values together on the Serial Plotter and watch the two lines.
- Change the window or alpha and note the lag against the smoothness.
- Wave your hand in front of the sensor to produce real spikes and see which filter ignores them.
- Choose the weakest filter that gives steady behaviour, then test the robot on the floor.
- Save your plots and your final settings. They are what make this guide your own.
Sources: standard digital filter definitions; the worked numbers above are arithmetic you can check by hand. Last reviewed November 2026. Written, checked and maintained by Jignesh Panchal.