# Arduino sensor filtering: average vs median

> Compare moving average, exponential and median filters on the same readings, with hand-worked numbers, to steady noisy Arduino robot sensors.

Canonical: https://roboticsengineering.in/arduino-sensor-filtering-moving-average-median/ | Published: 2026-11-04 | Updated: 2026-11-04 | Author: jaadudigital

**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](https://roboticsengineering.in/complementary-filter-imu-arduino/) 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](https://roboticsengineering.in/hc-sr04-reliable-distance/) 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](https://roboticsengineering.in/arduino-millis-non-blocking-robot-state-machine/) 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.*
