Sensor readings spike now and then: use a median filter

Use the median of the last few readings. An average lets one bad value pull the result.

What the message means

Ultrasonic and infrared sensors sometimes return a wild value. Control code reacts to it and the robot jerks.

Fix it in this order

  1. Keep the last three to five readings.
  2. Sort them and take the middle value.
  3. Use an odd number of samples.
  4. Follow with a light average if you need smoothness.
  5. Reject values outside the possible range of the sensor.
  6. Do not filter so much that the robot reacts late.

Why a median beats an average

Ultrasonic and infrared distance sensors sometimes return a single wild reading, such as zero or a very large value. An average drags the result towards that outlier, so one bad sample still moves the output. A median sorts a few recent samples and takes the middle one, so a single outlier is thrown away completely while real changes still come through.

Example: median of five

int readMedian5(int (*readFn)()) {
  int v[5];
  for (int i = 0; i < 5; i++) { v[i] = readFn(); delay(5); }
  for (int i = 1; i < 5; i++) {          // insertion sort
    int k = v[i], j = i - 1;
    while (j >= 0 && v[j] > k) { v[j + 1] = v[j]; j--; }
    v[j + 1] = k;
  }
  return v[2];                           // the middle value
}

Example sketch: change pins and values for your own wiring. It has not been bench-tested on your hardware.

Choosing the window size

Three samples remove one outlier at a time; five remove two. A bigger window is steadier but reacts later, so for a moving robot keep it small and take the samples from a running buffer instead of waiting with delay().

If you see this, do this

You seeDo this
Single wild valuesMedian filter.
Slow responseToo many samples.

ultrasonic reads zero, vibration noise.

Last reviewed: November 2026. These are the usual causes and the order that finds them fastest. Test on your own board; clones and cables differ.

Why this happens

An average treats every sample equally, so one bad value moves the result by its size divided by the number of samples. A median only cares about order. After sorting, the extreme values sit at the ends and the middle value ignores how far away they are. That is why a single spike, however huge, has no effect on a median as long as most samples are good.

Distance sensors produce this kind of fault in a specific way. An ultrasonic module that misses its echo times out and many sketches then get 0 from pulseIn(). An infrared sensor can jump when its beam hits a shiny edge. These are isolated wild readings, exactly the case a median handles well.

How to confirm the cause

  • Point the sensor at a fixed wall and print 200 raw readings. Count how many are far from the rest. Isolated outliers mean a median will help; a slow drift will not.
  • Look at what the outliers are. A run of zeros usually means timeouts, which you can reject by value. Large random values suggest noise or reflections.
  • Print the raw value and the filtered value side by side. The filtered value should ignore a single spike and follow a real step change within a few samples.
  • Move your hand in front of the sensor and check that the filtered value follows. If it lags badly, your window is too big for the loop speed.

Worked example

Take five readings: 50, 51, 0, 50, 52. The average is (50 + 51 + 0 + 50 + 52) / 5 = 40.6, which is wrong by nearly 10. Sorted, the readings are 0, 50, 50, 51, 52 and the median is 50. The sketch below keeps a window of three without using delay(), so it can run every loop.

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;               // the middle value
}
int buf[3]; int idx = 0; bool primed = false;
int filter(int x) {
  if (!primed) { buf[0] = buf[1] = buf[2] = x; primed = true; }
  buf[idx] = x; idx = (idx + 1) % 3;
  return median3(buf[0], buf[1], buf[2]);
}

A window of three removes one outlier at a time. If two bad readings can arrive back to back, use five.

Common mistakes

  • Starting the buffer with zeros, so the first outputs are wrong. Fill it with the first real reading as shown.
  • Using an even number of samples, which has no single middle value.
  • Sorting inside an interrupt or a long blocking loop and making the robot slow to react.
  • Filtering the value and then taking its difference to estimate speed. Filtering first delays the signal and can distort that estimate.

If this did not fix it

  • The sensor itself is unstable: check its supply and add a capacitor across its power pins.
  • Reflections or angled surfaces: ultrasonic sensors read poorly on soft or steeply angled targets, so test on a flat wall.
  • Readings are steady but wrong: that is calibration or mounting, not a spike problem.