Skip to content

Sensor signal conditioning

Sensors lie by offset, drift, noise and spikes. Fuse a noisy accelerometer and a drifting gyroscope into a steady tilt with a complementary filter.

Before thisRLS and the Kalman filter (26.4)

3 more before it

Difference equations (6.1), Simple smoothing filters (18.2), Random processes (24.2)

Before this26.4 · 3 more
Chapter 32 · Lesson 2 of 2

First, the picture

Two sensors can measure how far a board is tilted, and each fails in its own way. Watch them below against the dashed truth: one’s dots scatter about it, while the other’s smooth line drifts further off.

Noisy but right, smooth but drifting

Synthetic tilt, 30 s at 100 Hz: an accelerometer with 3° noise (seed 3220), a gyroscope with a 0.5 °/s bias and noise (seed 322).

The true tilt: still, up to 30°, down to −20°, back to level.

time
0 s
accelerometer error RMS since 5 s
—
gyroscope error now
—
0.00 / 13.00 s
Describe this picture

A synthetic tilt, 30 s at 100 Hz, measured by an accelerometer with 3° noise (seed 3220) and a gyroscope with a 0.5 °/s bias and noise (seed 322). One panel, tilt from −40° to 45° against time from 0 to 30 s. The true tilt is a dashed line, the accelerometer’s readings are small dots, and the integrated gyroscope is a solid line. The readouts are the time in seconds, the accelerometer’s error RMS since 5 s, over the readings drawn so far, and the gyroscope’s error now, in degrees. There is no control. The 13 s clip opens on the true tilt alone: still, up to 30°, down to −20°, back to level. From 2.5 s both sensors draw. The drawing pauses at 15 s: the accelerometer scatters about the truth, 2.94° RMS, and the gyroscope follows each turn but has begun to drift, 7.6° off. Then it goes on to 30 s, where the accelerometer’s error is 3.00° RMS over 5–30 s, centred on 0, and the gyroscope is 15.2° off.

Noisy but right, smooth but drifting

A sensor never hands you the quantity itself. It hands you the quantity plus its own habits. The good news is that those habits come in a few kinds, and each kind has its own fix.

Four ways a sensor lies

An offset shifts every reading by the same amount. A scale that reads 0.2 kg with nothing on it has an offset. The fix is calibration: measure the offset at a moment when you know the truth, then subtract it from every reading.

A drift is an offset that changes slowly, with temperature or age. One calibration does not cure it, because the offset you measured stops being true. You have to measure it again, or check against a second sensor.

Noise scatters each reading around the truth. Smoothing reduces it, at the price of delay, as in “Choosing a” of Simple smoothing filters (18.2).

An outlier is a single wild reading, from a bad contact or a knock. A median ignores it, as in “A median ignores spikes” of the same lesson.

FaultWhat you seeThe fix
offsetevery reading shifted the samecalibrate: subtract a measured offset
driftthe shift changes slowlycalibrate again, or check against another sensor
noisereadings scatter about the truthsmooth, and accept some delay
outlierssingle readings far offtake a median

All of this is signal conditioning: turning raw readings into numbers you can trust. This page works through one classic case, where two sensors with opposite faults rescue each other.

Two ways to measure a tilt

Picture a board that tilts about one axis, like a phone tipped toward you. I want its tilt θ\theta, the angle from level. On this page angles are in degrees: 30° is 0.52 rad.

An accelerometer measures the push it feels along each of its axes. At rest it feels only gravity, straight down. When the board tilts, gravity’s pull shifts from one of the sensor’s axes toward another, and the shares give the angle. I call that reading θacc\theta_\text{acc}.

It is right on average, because gravity always points the same way. But each reading is noisy. A real one is also thrown off whenever the board speeds up or is shaken, since those pushes add to gravity. On this page its only fault is noise.

A gyroscope measures how fast the board turns, in degrees per second. I call that reading ωgyro\omega_\text{gyro}. It knows nothing about the angle itself, only how quickly it is changing.

To get an angle from it, add up the turns. Each sample the board turns by about ωgyroTs\omega_\text{gyro}T_s, so the running total is the recursion of “Each answer comes from the last one” in Difference equations (6.1):

θgyro[n]=θgyro[n−1]+ωgyro[n] Ts,\theta_\text{gyro}[n]=\theta_\text{gyro}[n-1]+\omega_\text{gyro}[n]\,T_s,

with the delay box starting at 0, since the board starts level. A gyroscope’s readings are smooth, so this angle is smooth too.

Its weakness is a small constant error in the rate, called its bias bb: the offset of a rate sensor. Added up sample after sample, a bias of bb degrees per second puts the angle bb degrees further off every second. A constant offset in the rate turns into a growing drift in the angle.

The two sensors often sit on one chip, called an inertial measurement unit, or IMU.

The test tilt

Every signal on this page is synthetic: a stated tilt, noise from the site’s seeded generator, and a stated gyroscope bias. Nothing is recorded, so every error can be measured against a known truth. I measure every RMS error from 5 s, where the first turn starts, to 30 s.

The true tilt lasts 30 s, sampled at 100 Hz, so Ts=0.01T_s=0.01 s and there are 3000 samples. It is level until 5 s. It turns to 30° between 5 and 7 s, to −20° between 15 and 18 s, and back to 0° between 24 and 26 s.

Each turn follows half a cosine, so it starts and ends gently. Its rate is highest in the middle. The fastest is the 50° turn over 3 s, at 26.2 degrees per second.

The accelerometer reads the true tilt plus white noise of standard deviation 3°, from seed 3220. These 3000 draws measure 3.007°.

The gyroscope reads the true rate, plus a bias of 0.5 degrees per second, plus white noise of 0.3 degrees per second from seed 322. Those draws measure 0.301.

Think of a compass that jitters against a ship’s log of speed and heading. The compass is right on average. The log is smooth, but every small error in it carries forward, so the ship’s reckoned position slowly goes astray.

The picture at the top of the page draws both sensors against this tilt.

Notice the two kinds of error. The accelerometer’s dots stay centred on the truth, scattered by 3° at every moment. The gyroscope’s line is smooth and follows every turn, yet by the end it is 15.2° off.

Where the 15.2° comes from

Most of it is the bias. Half a degree per second for 30 s is 15°. The remaining 0.22° comes from adding up the gyroscope’s noise.

That noise does not average away when you add it up. Each sample adds an independent step of standard deviation 0.3×0.01=0.0030.3\times0.01=0.003°. In “White noise forgets, coloured noise remembers” of Random processes (24.2), variances of independent parts add.

So after nn samples the variance is nn times a step’s, and the spread is 0.003n0.003\sqrt n degrees. At 30 s that is 0.16°, and it keeps growing with the square root of time. This is 24.2’s AR(1) loop with a=1a=1: it never forgets, and its value wanders without limit.

So the gyroscope is good over short times and bad over long ones. The accelerometer is the opposite: no single reading is good, but on average it never strays.

Low-pass one, high-pass the other

That suggests the fix. Take the slow part of the tilt from the accelerometer, which is right on average, and the fast part from the gyroscope, which is smooth. It is like trusting a friend’s directions for the next turn and the map for the way home.

One line of code

Each sample, start from the last estimate θ^[n−1]{\hat\theta[n-1]} and add the gyroscope’s turn. Then pull the result a small fraction of the way toward the accelerometer’s reading:

θ^[n]=a(θ^[n−1]+ωgyro[n] Ts)+(1−a) θacc[n].\begin{aligned} \hat\theta[n]&=a\big(\hat\theta[n-1]+\omega_\text{gyro}[n]\,T_s\big)\\ &\quad+(1-a)\,\theta_\text{acc}[n]. \end{aligned}

The hat marks an estimate, as in Random variables for signals (24.1). I start at the accelerometer’s first reading, θ^[0]=θacc[0]\hat\theta[0]=\theta_\text{acc}[0], since I do not know the truth.

This is the complementary filter. Its two weights, aa and 1−a1-a, add to 1. With a number τ\tau in seconds, I set

a=ττ+Ts.a=\frac{\tau}{\tau+T_s}.

Why τ\tau is a time constant

Drop the gyroscope term and the line is the EMA of 18.2, θ^[n]=a θ^[n−1]+(1−a) θacc[n]\hat\theta[n]=a\,{\hat\theta[n-1]}+(1-a)\,\theta_\text{acc}[n]. In “A 9-point average and its one-multiply twin”, its mean delay is a/(1−a)a/(1-a) samples.

With this aa, that is τ/Ts\tau/T_s samples, which is exactly τ\tau seconds. So τ\tau is the filter’s time constant.

In “Choosing a”, 18.2 set a=e−Ts/τa=e^{-T_s/\tau} instead. When τ\tau is many samples long the two agree. At τ=1\tau=1 s, they give 0.990099 and 0.990050, both 0.9901.

The truth passes untouched

Suppose both sensors were perfect. The gyroscope’s turn would equal the true change, θ[n]−θ[n−1]\theta[n]-{\theta[n-1]}, and the accelerometer would read θ[n]\theta[n].

If the estimate is right at sample n−1n-1, the bracket becomes θ[n]\theta[n]. Then θ^[n]=a θ[n]+(1−a) θ[n]=θ[n]\hat\theta[n]=a\,\theta[n]+(1-a)\,\theta[n]=\theta[n], right again. Starting right, the estimate stays right at every sample, however fast the tilt moves.

So the filter adds no delay and no blur of its own. Only the sensors’ faults get through, and each through its own path.

What each path does

The accelerometer’s noise enters through the EMA, a low-pass. By 18.2’s noise rule, it keeps a share (1−a)/(1+a)\sqrt{(1-a)/(1+a)} of the noise’s RMS. With this aa, that share is Ts/(2τ+Ts)\sqrt{T_s/(2\tau+T_s)}, so a longer τ\tau leaves less noise.

The gyroscope’s bias goes the other way. Hold the board still and ignore the noise. The estimate settles at a steady error ee where one more step changes nothing:

e=a (e+b Ts),e=a1−a b Ts=b τ.\begin{aligned} e&=a\,(e+b\,T_s),\\ e&=\frac{a}{1-a}\,b\,T_s=b\,\tau. \end{aligned}

For this aa the steady error is exactly bτb\tau, the bias times the time constant. With 18.2’s a=e−Ts/τa=e^{-T_s/\tau} it would be a little less, about b(τ−Ts/2)b(\tau-T_s/2).

The error does not appear at once. From a correct start it grows as bτ(1−an)b\tau(1-a^n), getting most of the way there within a few τ\tau.

So the time constant sets the trade. A short τ\tau trusts the accelerometer and keeps its noise. A long one trusts the gyroscope and lets the bias push the estimate off, by up to bτb\tau.

The handover

In frequency, the accelerometer’s path has the EMA’s gain, as 18.2 worked out. It is 1 for a steady tilt and small for fast changes. The gyroscope’s path acts on the added-up angle θgyro\theta_\text{gyro} and passes only its changes: nothing of a steady angle, nearly all of a fast one. A bias makes θgyro\theta_\text{gyro} a ramp that never stops changing, which is why bτb\tau gets through.

The two gains are equal where 1−a=a ∣1−e−jΩ∣1-a=a\,\lvert1-e^{-j\Omega}\rvert. The distance from 1 to e−jΩe^{-j\Omega} on the unit circle is 2sin⁡(Ω/2)2\sin(\Omega/2), so with this aa the condition becomes sin⁡(Ω/2)=Ts/(2τ)\sin(\Omega/2)=T_s/(2\tau). For the small angles here, Ω≈Ts/τ\Omega\approx T_s/\tau, which in hertz is

f≈12πτ.f\approx\frac{1}{2\pi\tau}.

I call this the handover frequency; it is also called the crossover. Slower changes come mostly from the accelerometer, faster ones mostly from the gyroscope.

Low-pass one, high-pass the other

The same sensors, fused with a complementary filter (gyro not calibrated).

τ = 0.05 s: mostly accelerometer; the noise comes through, 0.91° RMS.

τ
0.05 s
a
0.8333
error RMS, 5–30 s
0.91°
handover
3.183 Hz
0.00 / 13.00 s
Describe this picture

The same sensors, fused with a complementary filter, the gyroscope not calibrated. Two stacked panels share the time axis. The first, the tilt, has the axes of the first picture: the fused estimate is a solid line over the true tilt, dashed. The second draws the error, the estimate minus the truth, as a solid line from −10° to 10°. The readouts are τ\tau in seconds, aa with four decimals, the error RMS over 5–30 s in degrees, and the handover frequency 1/(2πτ)1/(2\pi\tau) in hertz. The 13 s clip opens at τ = 0.05 s, aa = 0.8333: mostly accelerometer, so the noise comes through, 0.91° RMS, with the handover at 3.183 Hz. From 3.5 s the time constant grows to 20 s, aa = 0.9995: mostly gyroscope, smooth, but the bias pushes it off, 5.43° RMS, handover 0.008 Hz. From 8 s it moves to 0.5 s, aa = 0.9804: the handover where neither fault dominates, 0.42° RMS, at 0.318 Hz. When the clip ends, a full-width slider, “Time constant τ”, has 15 positions, 1, 2, 3 and 5 in each decade from 0.01 to 30 s, starting at 0.5 s. The arrow keys move one position, and the caption gives the error and the handover. The setting is kept in the link, as fusion.tau.

Watch the error panel as τ\tau changes. At 0.05 s the accelerometer’s noise comes through; at 20 s the bias pushes the estimate off; at 0.5 s neither fault dominates.

Notice the error panel at 0.5 s, the best of the slider’s positions. The error is 0.42° RMS, about a seventh of the accelerometer’s 3.00°, and it does not grow with time like the gyroscope’s.

Checking the bias rule

Set τ=1\tau=1 s and look at the hold at 30°, from 10 to 15 s. The rule predicts a steady error of 0.5×1=0.500.5\times1=0.50°. The run measures 0.45° on average there: the accelerometer’s noise, averaged over those 5 s, comes to −0.06°.

At τ=20\tau=20 s the rule predicts 10.0°, but only after a few time constants, a minute or more. Over the same hold the error averages 4.05° and is still growing.

Calibrate first

The bias is an offset in the rate, so it has the offset’s fix. The first 5 s are still, so the true rate there is 0. Whatever the gyroscope reports then is its bias plus noise.

The average of those 500 readings is b^=0.504\hat b=0.504 degrees per second. Its own noise is about 0.3/500=0.0130.3/\sqrt{500}=0.013, so the true 0.5 is well within reach. Subtract b^\hat b from every gyroscope reading and fuse again.

Now the best of the slider’s positions moves to τ=3\tau=3 s, and the error falls to 0.14° RMS.

What stops a longer τ\tau now is not the bias, which is down to 0.004 degrees per second. It is mostly the start: the filter began at the accelerometer’s first reading, 1.11° off, and a long τ\tau takes long to forget it.

So why fuse at all, once calibrated? Over these 30 s the calibrated gyroscope alone stays within 0.12°. But a leftover of 0.004 degrees per second is 14.6° after an hour, and its added-up noise spreads by about 1.8°.

Real biases also change with temperature, so a calibration does not stay true. The accelerometer keeps the long run honest.

A Kalman filter with a fixed gain

Look again at the filter’s two steps. In “Predict, then correct” of RLS and the Kalman filter (26.4), a Kalman filter predicts with a model, then moves toward the measurement by a gain gg.

Here the prediction is the last estimate plus the gyroscope’s turn, and the measurement is θacc\theta_\text{acc}. The correction gives (1−g)(1-g) times the prediction plus g θaccg\,\theta_\text{acc}. That is the complementary filter, with a=1−ga=1-g.

The Kalman filter computes its gain from the noise sizes. The gyroscope’s noise adds a variance of (0.3×0.01)2(0.3\times0.01)^2 per step to the prediction. The accelerometer’s variance is 323^2.

Started at the accelerometer’s first reading, with that reading’s variance, its gain begins at 0.5 and settles to 0.0010 within the 30 s. Then a=0.9990a=0.9990, and τ=aTs/(1−a)\tau=aT_s/(1-a) is about 10 s. That is the accelerometer’s noise over the gyroscope’s, 3 over 0.3.

Once its gain stops changing, a Kalman filter is a complementary filter. But this one’s model has no bias. On the uncalibrated gyroscope, the slider’s 10 s gives 3.82° RMS. Real attitude filters add the bias to the state, as 26.4 added the velocity.

The maths behind it · summing to the identity

The two paths add to the identity. The accelerometer’s path is Hlp=(1−a)/(1−ae−jΩ)H_\text{lp}=(1-a)/(1-ae^{-j\Omega}), and the gyroscope’s, acting on θgyro\theta_\text{gyro}, is Hhp=a(1−e−jΩ)/(1−ae−jΩ)H_\text{hp}=a(1-e^{-j\Omega})/(1-ae^{-j\Omega}). Their sum is 1 at every frequency, so a tilt both sensors measure correctly passes through undistorted.

The maths behind it · the bias–variance trade

The best τ\tau balances two kinds of error. The accelerometer’s noise left is a random scatter, a variance, and it falls as τ\tau grows. The gyroscope’s bias leaves a systematic error, bτb\tau, that grows with it. Their mean squares add, so this is a bias–variance trade, the central compromise of statistics.

Worked example

1. The coefficient. Take τ=1\tau=1 s and Ts=0.01T_s=0.01 s. Then a=1/1.01=0.9901a=1/1.01=0.9901, and the mean delay is a/(1−a)=100a/(1-a)=100 samples, 1 s.

2. Bias against τ. A gyroscope bias of b=0.5b=0.5 degrees per second leaves a steady error bτ=0.5×1=0.50b\tau=0.5\times1=0.50°. This is exact for a=τ/(τ+Ts)a=\tau/(\tau+T_s), once the start is forgotten and the tilt holds still.

3. The best τ, by hand. For τ\tau much longer than TsT_s, model the error as the accelerometer’s noise left, 3Ts/(2τ)3\sqrt{T_s/(2\tau)}, plus the bias error bτb\tau. Their mean squares add: 9Ts/(2τ)+b2τ29T_s/(2\tau)+b^2\tau^2. Setting its slope to zero gives τ3=9Ts/(4b2)=0.09/1=0.09\tau^3=9T_s/(4b^2)=0.09/1=0.09, so τ=0.448\tau=0.448 s.

The slider’s nearest position is 0.5 s. There the model gives a noise left of 30.01/1=0.303\sqrt{0.01/1}=0.30° and a bias error of 0.25°, together 0.302+0.252=0.39\sqrt{0.30^2+0.25^2}=0.39°. The run measures 0.42°. The difference is the start, the turns, the gyroscope’s noise and this particular noise record.

4. The handover. At τ=0.5\tau=0.5 s, 1/(2π×0.5)=1/π=0.3181/(2\pi\times0.5)=1/\pi=0.318 Hz. The exact equal-gain point, from sin⁡(Ω/2)=Ts/(2τ)\sin(\Omega/2)=T_s/(2\tau), is also 0.318 Hz. At τ=0.05\tau=0.05 s the shortcut gives 3.183 Hz and the exact point 3.188 Hz.

5. Calibration. The gyroscope’s 500 still readings average 0.504 degrees per second. Subtracted, they leave 0.004, which at τ=3\tau=3 s is a steady error of 0.004×3=0.0120.004\times3=0.012°.

Where you’ll meet this

A phone fuses its accelerometer and gyroscope many times a second to know how it is held, for games and maps. Virtual reality headsets fuse the same two sensors to follow your head with little delay.

Drone flight controllers need their attitude, the tilt in every direction, hundreds of times a second to stay level. Many use a complementary filter of this kind, or its three-axis form by Mahony and colleagues. Self-balancing robots and scooters, and the stabilisers that keep a handheld camera level, do the same.

I left out the full three-dimensional problem, where the tilt is a rotation, often stored as a quaternion. I also left out magnetometers, which give a heading the way the accelerometer gives the tilt, and temperature compensation of the bias.

For more, see W. T. Higgins, “A comparison of complementary and Kalman filtering” (IEEE Trans. Aerospace and Electronic Systems, 1975), and R. Mahony, T. Hamel and J.-M. Pflimlin, “Nonlinear complementary filters on the special orthogonal group” (IEEE Trans. Automatic Control, 2008). Next, Radar signal processing (33.1) measures distance and speed from echoes.

Reference card

QuantityFormulaNotes
Gyroscope angleθgyro[n]=θgyro[n−1]+ωgyro[n] Ts\theta_\text{gyro}[n]={\theta_\text{gyro}[n-1]}+\omega_\text{gyro}[n]\,T_sa bias bb drifts by bb per second
Calibrationb^\hat b = mean of ωgyro\omega_\text{gyro} while still; subtract itremoves the bias
Complementary filterθ^[n]=a(θ^[n−1]+ωgyro[n]Ts)+(1−a)θacc[n]\hat\theta[n]=a\big({\hat\theta[n-1]}+\omega_\text{gyro}[n]T_s\big)+(1-a)\theta_\text{acc}[n]a=τ/(τ+Ts)a=\tau/(\tau+T_s)
Mean delaya/(1−a)=τ/Tsa/(1-a)=\tau/T_s samplesexactly τ\tau seconds
Accelerometer noise leftshare (1−a)/(1+a)\sqrt{(1-a)/(1+a)} of its RMSfalls as τ\tau grows
Bias errorbτb\tausteady state; exact for this aa
Handover1/(2πτ)1/(2\pi\tau)slow: accelerometer, fast: gyroscope
Kalman linka=1−ga=1-gonce the gain gg settles

End of lesson 32.2

Where to go next.

Phasorium
LibraryEvery lesson, in order

Parts

About Phasorium
Look