Sensor fusion is not a category of algorithm. It is one idea — weight each measurement by how much you trust it — applied recursively over time. Everything from a complementary filter to an unscented Kalman filter is a variation on where those weights come from.
Two numbers, one quantity
Suppose a room has a thermistor reading 21.4 °C and an infrared surface sensor reading 22.8 °C. Both are measuring the same temperature and they disagree by 1.4 degrees. Averaging them gives 22.1, which is defensible only if you believe the two sensors equally. You usually do not: the thermistor has a manufacturer-stated accuracy of a few tenths of a degree, the infrared sensor is sensitive to emissivity and to what is in its field of view, and their errors are not the same size.
The useful reframing is that a sensor does not give you a number, it gives you a distribution. A reading of 21.4 with a standard deviation of 0.3 is a statement that the truth is probably within about 0.6 degrees either side. A reading of 22.8 with a standard deviation of 1.2 is a much vaguer statement. Fusion is the question of what a single distribution consistent with both statements looks like.
Inverse-variance weighting
If two measurements x1 and x2 of the same quantity have independent, zero-mean errors with variances σ1² and σ2², the minimum-variance unbiased combination is the inverse-variance weighted mean. Each measurement contributes in proportion to its precision, which is one over its variance:
x_hat = (x1/s1^2 + x2/s2^2) / (1/s1^2 + 1/s2^2)
var(x_hat) = 1 / (1/s1^2 + 1/s2^2)
thermistor : x1 = 21.4, s1 = 0.3 -> 1/s1^2 = 11.11
infrared : x2 = 22.8, s2 = 1.2 -> 1/s2^2 = 0.694
x_hat = (21.4*11.11 + 22.8*0.694) / (11.11 + 0.694)
= (237.78 + 15.83) / 11.805
= 21.48 C
var = 1 / 11.805 = 0.0847 -> sd = 0.29 C
Two things in that result are worth staring at. The fused estimate is 21.48, barely moved from the thermistor, because the infrared sensor is sixteen times less precise and gets weighted accordingly. And the fused standard deviation, 0.29, is lower than either input — slightly lower than the thermistor’s 0.3. That is the entire reason to fuse: even a bad sensor carries some information, and combining precisions always produces a precision at least as high as the best one you had.
The formula also tells you when fusion is pointless. If one sensor is ten times noisier than another, its weight is a hundredth, and the fused answer is the good sensor with a rounding error attached. Adding a cheap sensor to a good one does not improve much; adding two comparable sensors does.
Adding time: the Kalman recursion
The weighting above assumes both numbers arrived at the same instant. Real telemetry arrives in a stream, and the previous estimate is itself a measurement of sorts — a prediction of what the quantity should be now, given what it was a moment ago. The Kalman filter, published by Rudolf Kálmán in 1960 in the Transactions of the ASME, is exactly inverse-variance weighting applied between that prediction and the new measurement, repeated forever.
In the scalar case the whole algorithm is five lines. x is the estimate, P its variance, Q the process noise (how much the true value can wander between samples), R the measurement noise variance, and z the new reading:
# predict
x_pred = x # constant-value model; a motion model goes here
P_pred = P + Q
# update
K = P_pred / (P_pred + R) # the Kalman gain
x = x_pred + K * (z - x_pred)
P = (1 - K) * P_pred
The gain K is the same quantity as before wearing a different hat. Write it as (1/R) / (1/P_pred + 1/R) and it is the measurement’s share of the total precision. When the prediction is confident and the sensor is noisy, K is near zero and the new reading barely moves the estimate. When the prediction has gone stale — a long gap, or a large Q — K approaches one and the filter simply believes the sensor.
Fusing several sensors is then a matter of running the update step once per sensor, each with its own R, between predictions. Sensors arriving at different rates need no special handling: a 100 Hz accelerometer and a 1 Hz GPS fix are just updates that happen at different times, with the prediction step advancing the state in between. That is why the recursion is the standard answer for mixed-rate telemetry, and it depends on the timestamps being trustworthy — see time-aligning data from sensors with different clocks.
A worked example
Take a tank level that is genuinely constant at 60.0 cm. The sensor has R = 4.0 (standard deviation 2 cm) and we believe the level barely moves, so Q = 0.01. Starting from a vague prior x = 0, P = 100, the first three readings are 62.1, 57.4 and 61.0:
step 1: P_pred = 100.01, K = 100.01/104.01 = 0.9615
x = 0 + 0.9615*(62.1 - 0) = 59.71
P = (1-0.9615)*100.01 = 3.846
step 2: P_pred = 3.856, K = 3.856/7.856 = 0.4908
x = 59.71 + 0.4908*(57.4-59.71) = 58.58
P = (1-0.4908)*3.856 = 1.963
step 3: P_pred = 1.973, K = 1.973/5.973 = 0.3303
x = 58.58 + 0.3303*(61.0-58.58) = 59.38
P = (1-0.3303)*1.973 = 1.322
The gain falls from 0.96 to 0.33 over three samples: the filter starts out knowing nothing and taking the sensor at its word, and settles into treating each new reading as a nudge. P converges to a steady value that depends only on the ratio of Q to R, which is the useful thing to remember — the filter’s long-run behaviour is set by that ratio, not by the two numbers individually.
Q and R are the whole difficulty
The recursion is trivial to implement and easy to get wrong, because both noise terms are things you have to supply.
- R comes from the datasheet or from a still period. Park the sensor in front of something that is not changing, log a few thousand samples, and take the sample variance. That is
R. It is the one parameter you can measure rather than guess. - Q is a modelling choice, not a measurement. It encodes how fast you believe the truth can change. Too small and the filter becomes over-confident and lags real movement for many samples; too large and it tracks noise. If your filter feels sluggish after a genuine step change,
Qis the knob. - Correlated errors break the derivation. Inverse-variance weighting assumes the two error terms are independent. Two sensors on the same board sharing a supply rail and a temperature both drift together, so their errors are correlated, and fusing them reports a confidence the pair does not have. This is the same failure that makes cross-sensor anomaly detection subtle.
- A bias is not noise. A sensor reading 2 degrees high every time has zero variance about its own mean and the filter will happily converge to the wrong answer with high confidence. Bias is a calibration drift problem and is handled by adding a bias term to the state vector, not by tuning
R.
When the linear filter is the wrong tool
The Kalman filter is optimal under a specific set of conditions: linear dynamics, linear measurement model, and Gaussian noise. Depart from those and it is merely a reasonable heuristic.
For a nonlinear measurement — a range-and-bearing fix, an orientation from magnetometer and accelerometer — the standard moves are the extended Kalman filter, which linearises around the current estimate with a Jacobian, and the unscented Kalman filter, which propagates a small set of chosen sample points through the true nonlinearity instead. The EKF is cheaper and degrades badly when the function curves sharply near the operating point; the UKF costs a handful more evaluations and is usually the safer default on an embedded target that can afford it. Where the posterior is genuinely multi-modal — a robot that might be in one of three corridors — no Gaussian filter works and a particle filter is the honest answer.
There is also a much cheaper option that is often enough. A complementary filter combines a signal that is trustworthy at low frequency with one that is trustworthy at high frequency — typically an accelerometer, which is stable over minutes but noisy over milliseconds, and a gyroscope, which is smooth over milliseconds but integrates its own bias into a growing error over minutes. Low-pass one, high-pass the other, add them. It has one tuning constant, runs in a few instructions, and on a device where an on-device model already owns the compute budget, it is frequently the right engineering answer rather than a compromise.
Top comments (0)