Skip to content

Sensors & Signal Processing

How an exponential moving average filter works

An exponential moving average (EMA) smooths a noisy reading with one line of code: each sample, move a running value a fraction alpha of the way toward the new reading. Recent readings count most, and older ones fade away exponentially. A small alpha is smooth but slow to follow real changes; a large one is quick but lets noise through.

One line

filtered = filtered + α × (reading − filtered)

With α = 0.125, filtered at 400 and a reading of 480, the gap is 80 and an eighth of it is 10, so filtered moves to 410. The next reading nudges it again. A noisy reading jumps around, but each jump only moves the value an eighth of the way, and the jumps in opposite directions largely cancel.

float filtered = 0;

void setup() {
  filtered = analogRead(A0);   // start from a real reading
}

void loop() {
  int reading = analogRead(A0);
  filtered = filtered + 0.125 * (reading - filtered);
}

Live experiment

Alpha is the trade

The filter has settled on a knob at 30 %, and the lamp lights above 480. At 1 s the knob jumps to 70 %, at 2 s it drops to 45 %. Alpha 1 follows at once and flickers on the noise; 0.125 lights within 0.05 s and stays calm; 0.016 takes a third of a second and is still lit at 3 s.

Try it yourself: free lesson

“Smooth it out” is lesson 2 of 6 in Signal Sands, Sensors & Signal Processing World 1. Free with an account. Lessons unlock in order, so you’ll do the one before it first.

Alpha is the trade

  • α = 1 copies every reading: no smoothing at all.
  • α = 0.5 moves halfway each time: quick, but half of every jitter gets through.
  • α = 0.125 is a common choice: calm, and still quick.
  • α = 0.016 is very smooth, and very slow, to rise and to fall.

As a rule of thumb, the filter takes about 1 ÷ α samples to cover most of a sudden change, about 63% of it. Sampled 100 times a second, α = 0.125 responds in about 8 samples, 80 ms.

It's a low-pass filter

An EMA is the digital twin of an RC low-pass filter: slow changes pass, fast wobbles shrink. For small alpha its cutoff frequency is about:

fc ≈ α × sample rate over 2π

So α = 0.02 at 100 samples a second cuts off around 0.32 Hz. Run the same code ten times as fast and the cutoff rises ten times: alpha is per sample, not per second.

Why it's everywhere

It needs one variable, no buffer and one multiply per sample, which makes it ideal for small microcontrollers. Battery gauges, fan speed readouts, audio level meters and the D term of many PID controllers all use it. For a sharper trade between smoothness and delay, chain two in a row; for wild one-off spikes, put a median filter in front.

Free lesson

Now build it yourself

Each lesson comes in three short parts: see the idea, try it on a live board, then build it yourself, with quick checks as you go.

Try it yourself: free lesson

“Smooth it out” is lesson 2 of 6 in Signal Sands, Sensors & Signal Processing World 1. Free with an account. Lessons unlock in order, so you’ll do the one before it first.

Or start from the very first lesson, no account needed

Practice questions

Three quick questions from the lessons. Pick an answer to check it.

  1. Question 1 of 3Change alpha from 0.125 to 0.016. The filter becomes…
    Show the answer

    Answer: Smoother but slower to follow a change. A smaller step each sample means more samples to get anywhere, and more averaging of the noise along the way.

    • Why not “Noisier”? Smaller steps average more readings together, so the noise shrinks rather than grows.
    • Why not “Unchanged”? Alpha sets how far each sample moves the value, so changing it changes both the smoothing and the speed.
    • Why not “Faster”? A smaller alpha moves less of the gap each sample, so it gets anywhere more slowly, not faster.
  2. Question 2 of 3Alpha 1 makes the filter…
    Show the answer

    Answer: Copy every reading: no smoothing at all. filtered + (reading − filtered) × 1 is just the reading. Alpha 0 would never move at all.

    • Why not “Halve each reading”? Halving each reading isn't what the line does. With alpha 1 the value simply becomes the reading.
    • Why not “Never move”? Never moving is alpha 0: each sample would move the value by none of the gap.
    • Why not “Twice as smooth”? Alpha 1 smooths less, not more. Each sample jumps the whole gap, straight to the reading.
  3. Question 3 of 3Why is filtered a float and not an int?
    Show the answer

    Answer: An int would throw away the fraction of each nudge and get stuck short of the reading. With an int, (reading − filtered) × 0.125 rounds to 0 once the gap is under 8, and the value stops moving. A float keeps the fractions.

    • Why not “It doesn't matter”? It matters: an int rounds away each small nudge and the filter stops short of the reading.
    • Why not “analogRead returns a float”? analogRead returns a whole number. The filtered value needs fractions because of the small nudges.
    • Why not “Floats are faster”? Speed isn't the point; on a small board floats are slower. They're used to keep the fractions.
Keep practising: free lesson

“Smooth it out” is lesson 2 of 6 in Signal Sands, Sensors & Signal Processing World 1. Free with an account. Lessons unlock in order, so you’ll do the one before it first.

Questions people ask

What is an exponential moving average filter?

A running value that moves a fixed fraction, alpha, of the way toward each new reading: filtered = filtered + alpha × (reading − filtered). Recent readings count most and older ones fade away exponentially. It's the digital version of an RC low-pass filter.

How do I choose alpha?

Smaller alpha is smoother but slower. Roughly, the filter takes about 1 ÷ alpha samples to follow a change most of the way, and its cutoff is about alpha × sample rate ÷ 2π. Pick the largest alpha that makes the noise small enough.

Why does my filtered value get stuck short of the reading?

It's stored in an int. Once the gap is small, alpha times the gap is a fraction of a count and the int throws it away, so the value stops short. Use a float, or keep the value scaled up by a power of two.

Every topic