The Kalman filter is the optimal linear estimator for combining noisy sensor measurements with a predictive model of system behavior. It operates in a predict-update cycle: the prediction step projects the state forward (uncertainty grows), and the update step corrects using sensor measurements (uncertainty shrinks). The Kalman gain automatically balances trust between the model and measurements based on their respective uncertainties.
Learning Objectives
After completing this chapter, you will be able to:
Explain Kalman filter theory and state-space model components
Implement the predict-update cycle for 1D and multi-dimensional systems
Apply Kalman filters to sensor fusion problems such as GPS-IMU tracking
Configure filter parameters (Q, R) and evaluate their effect on tracking performance
Diagnose filter divergence caused by incorrect noise models or sensor failures
For Beginners: Kalman Filters for Sensor Fusion
Imagine you are trying to track the temperature in a room, but your thermometer gives slightly different readings each time – sometimes too high, sometimes too low. A Kalman filter is a smart algorithm that combines what you expect the temperature to be (based on recent trends) with what your sensor actually reads, giving you a better estimate than either source alone. It is the most widely used technique for combining noisy sensor data in IoT, robotics, and navigation systems.
Understanding State Estimation
Core Concept: State estimation infers the true value of a system’s hidden state (position, velocity, temperature) from noisy, incomplete sensor measurements by combining a predictive model of how the state evolves with observations that constrain what the state can be.
Why It Matters: Real sensors never report the true state directly. A GPS chip reports a position estimate with 3-5 meter uncertainty; an accelerometer measures gravity plus linear acceleration plus noise. State estimation algorithms like Kalman filters maintain a running best-guess of the true state plus a measure of confidence (covariance), enabling downstream systems to make informed decisions even when individual measurements are unreliable.
Key Takeaway: Think of state estimation as maintaining a belief about reality rather than trusting any single measurement. The belief gets updated by two mechanisms: prediction (what physics says should happen) and correction (what sensors actually observed). Good estimators balance these based on how much you trust each.
Interactive: Kalman Filter Predict-Update Cycle
Key Concepts
State vector: The set of variables the Kalman filter estimates at each time step — for an IoT position tracker, typically (x, y, velocity_x, velocity_y).
Predict step: The phase where the Kalman filter uses a motion model to project the previous state estimate forward in time, producing a predicted state and inflated uncertainty.
Update step: The phase where the Kalman filter incorporates a new sensor measurement, correcting the predicted state proportionally to how much it trusts the measurement versus the prediction.
Kalman gain (K): The weight computed at each update step controlling how much the measurement corrects the prediction; high K means trust the measurement, low K means trust the model.
Process noise covariance (Q): A matrix representing uncertainty in the motion model — higher Q means the filter adapts faster to unexpected motion but is noisier.
Measurement noise covariance (R): A matrix representing sensor measurement uncertainty; higher R means the filter trusts measurements less and relies more on the motion model.
Extended Kalman Filter (EKF): A non-linear extension of the Kalman filter that linearises the motion or measurement model at each step using Jacobians, required when relationships are not linear.
20.1 Introduction to Kalman Filtering
The Kalman filter is the optimal linear estimator for systems with Gaussian noise. It’s widely used for sensor fusion in navigation, robotics, and tracking.
20.1.1 State Space Model
State equation: \(x_k = F x_{k-1} + B u_k + w_k\) (process model)
Measurement equation: \(z_k = H x_k + v_k\) (sensor model)
Where:
\(x_k\): State vector at time \(k\)
\(F\): State transition matrix
\(B\): Control input matrix
\(u_k\): Control input
\(H\): Measurement matrix
\(w_k\): Process noise ~ \(N(0, Q)\)
\(v_k\): Measurement noise ~ \(N(0, R)\)
20.2 The Predict-Update Cycle
The Kalman filter operates in two alternating phases:
20.2.1 Step 1: Initialize
Set initial estimate and uncertainty based on prior knowledge:
Initial state \(\hat{x}_0\)
Initial covariance \(P_0\)
20.2.2 Step 2: Predict
Project state forward using motion model. Uncertainty grows:
\[\hat{x}_{k|k-1} = F \hat{x}_{k-1|k-1} + B u_k\]
\[P_{k|k-1} = F P_{k-1|k-1} F^T + Q\]
20.2.3 Step 3: Compute Kalman Gain
Balance prediction vs measurement trust (for scalar case with \(H=1\), \(K \in [0,1]\)):
Correct prediction using measurement. Uncertainty shrinks:
\[\hat{x}_{k|k} = \hat{x}_{k|k-1} + K_k (z_k - H \hat{x}_{k|k-1})\]
\[P_{k|k} = (I - K_k H) P_{k|k-1}\]
Key Insight: Why Uncertainty Shrinks
When you multiply two Gaussian distributions (prediction x measurement), the result is always narrower than either input. This is the mathematical magic of sensor fusion - combining imperfect information sources produces a better estimate than any single source alone.
The math: If prediction has variance P and measurement has variance R, the fused variance is: \[\sigma_{fused}^2 = \frac{1}{\frac{1}{P} + \frac{1}{R}} = \frac{PR}{P+R}\]
For example: P = 100 m^2 (10m uncertainty), R = 25 m^2 (5m uncertainty) \[\sigma_{fused}^2 = \frac{100 \times 25}{100 + 25} = \frac{2500}{125} = 20 \text{ m}^2 \approx (4.5\text{m})^2\]
The fused uncertainty (4.5m) is smaller than both inputs (10m and 5m)!
20.3 Worked Example: Temperature Sensor Tracking
Scenario: Track room temperature using a noisy sensor (+/-1.0°C accuracy)
System Parameters:
State: Temperature at time \(k\) (scalar, 1D case)
State transition: \(F = 1\) (temperature doesn’t change much between samples)
Measurement matrix: \(H = 1\) (we measure temperature directly)
Process noise: \(Q = 0.1\) (variance in temperature change per time step)
k=2 through k=5: Continue the predict-update cycle…
Summary Table:
k
Measurement
Kalman Gain
Updated Estimate
Uncertainty
Error
0
-
-
20.000
1.000
0.000
1
21.5
0.524
20.786
0.524
-0.214
2
20.8
0.384
20.791
0.384
-0.709
3
22.3
0.326
21.283
0.326
-0.717
4
21.9
0.299
21.467
0.299
-0.833
5
22.6
0.285
21.790
0.285
-0.710
Key Observations:
Uncertainty Decreases: \(P\) reduces from 1.000 to 0.285 (71.5% reduction)
Kalman Gain Stabilizes: \(K\) converges from 0.524 to 0.285
Filter smooths noise while tracking trend
20.4 Worked Example: GPS + Accelerometer Fusion
This example demonstrates how a Kalman filter combines two complementary sensors for drone position tracking.
Real-World Application: Delivery Drone Navigation
Delivery drones face a fundamental challenge: GPS provides absolute position (no drift) but updates slowly (1 Hz) with moderate noise (several meters). The accelerometer provides high-frequency motion updates (100 Hz) but suffers from integration drift. The Kalman filter optimally fuses these complementary sensors.
Standard deviations: - Prediction: \(\sqrt{26} = 5.1\) m - Measurement: \(\sqrt{16} = 4.0\) m - Fused: \(\sqrt{9.9} = 3.1\) m (better than either!)
Key insight: The Kalman gain \(K = \frac{26}{42} = 0.619\) represents the information ratio: 62% trust in measurement (smaller uncertainty), 38% trust in prediction. This is optimal weighting for minimum variance.
Interactive: Sensor Fusion Calculator
Adjust the prediction uncertainty (P) and measurement noise (R) to see how the Kalman gain and fused uncertainty change. Observe how the fused result is always better than either source alone.
Show code
viewof P_input = Inputs.range([1,100], {value:26,step:1,label:"Prediction uncertainty P (m\u00B2)"})viewof R_input = Inputs.range([1,100], {value:16,step:1,label:"Measurement noise R (m\u00B2)"})viewof x_pred = Inputs.range([90,110], {value:102,step:0.1,label:"Predicted position (m)"})viewof z_meas = Inputs.range([90,120], {value:105,step:0.1,label:"Measurement (m)"})
Show code
{const K = P_input / (P_input + R_input);const innovation = z_meas - x_pred;const x_fused = x_pred + K * innovation;const P_fused = P_input * R_input / (P_input + R_input);const std_pred =Math.sqrt(P_input);const std_meas =Math.sqrt(R_input);const std_fused =Math.sqrt(P_fused);const container = htl.html`<div style="background: var(--bs-light, #f8f9fa); border-left: 4px solid #3498DB; padding: 1em; border-radius: 4px; font-family: system-ui;"> <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 0.5em;"> <div><strong>Kalman Gain K:</strong></div><div>${K.toFixed(3)} (${(K*100).toFixed(1)}% trust in measurement)</div> <div><strong>Innovation (z - x):</strong></div><div>${innovation.toFixed(2)} m</div> <div><strong>Fused position:</strong></div><div>${x_fused.toFixed(2)} m</div> <div><strong>Fused uncertainty:</strong></div><div>${P_fused.toFixed(2)} m\u00B2 (${std_fused.toFixed(2)} m std dev)</div> </div> <hr style="margin: 0.5em 0;"> <div style="font-size: 0.9em;"> <strong>Comparison:</strong> Prediction std: ${std_pred.toFixed(2)} m | Measurement std: ${std_meas.toFixed(2)} m | <strong>Fused std: ${std_fused.toFixed(2)} m</strong>${std_fused < std_pred && std_fused < std_meas ?" -- Fused is better than both!": std_fused < std_pred || std_fused < std_meas ?" -- Fused improves on at least one source.":""} </div> </div>`;return container;}
20.5 Parameter Tuning: Q and R
Tuning Guidelines
Process Noise Q:
Higher Q = less trust in motion model (tracks measurements more)
Lower Q = more trust in model (smoother but slower to respond)
Set based on physical system dynamics
Measurement Noise R:
From sensor datasheets or empirical testing
Measure variance of stationary readings
Higher R = noisier sensor, less trusted
Balance:
If Q large and R small: K approaches 1 (trust measurements)
If Q small and R large: K approaches 0 (trust prediction)
Interactive: Q/R Tuning Explorer
Explore how process noise (Q) and measurement noise (R) affect the steady-state Kalman gain and convergence. The steady-state gain is the value K converges to after many iterations.
20.6 Uncertainty Propagation and Gaussian Intuition
Understanding Uncertainty Propagation
Core Concept: Uncertainty propagation tracks how measurement errors and model imperfections accumulate, spread, and combine as data flows through a fusion system, represented mathematically by the covariance matrix P that grows during prediction and shrinks during measurement updates.
Why It Matters: Knowing the estimate is not enough; you must know how confident you are. A position estimate of “10 meters east” is useless without knowing if the uncertainty is 0.1 meters (safe for drone landing) or 50 meters (dangerous for navigation). The covariance matrix P captures not just individual variable uncertainties but also correlations between them.
Key Takeaway: Always propagate uncertainty alongside state estimates. Use covariance to weight sensor contributions (Kalman gain), detect anomalies (innovation monitoring), and communicate confidence to downstream systems.
For nonlinear systems, the Extended Kalman Filter linearizes around the current estimate using Jacobians:
Nonlinear state model: \(x_k = f(x_{k-1}, u_k) + w_k\)
Linearization: Compute Jacobian \(F = \frac{\partial f}{\partial x}\) at each time step
The EKF is commonly used for GPS-IMU fusion where the measurement model (range to satellites) is nonlinear.
Worked Example: Kalman Filter for Indoor Position Tracking
Scenario: Track forklift position in warehouse using wheel odometry + UWB beacons.
Given:
State vector: [x, y, vx, vy] (4 states)
Wheel odometry: 50 Hz, 2% drift
UWB beacons: 10 Hz, +/-15 cm accuracy
Target: <30 cm position error
Result: EKF achieves 18 cm RMS error (within 30 cm target). Pure odometry after 5 minutes: 3.2 m error. Pure UWB: 22 cm. Fusion provides 6x improvement over odometry.
Key Insight: The Kalman gain automatically weights each source based on current uncertainty - trusting odometry between UWB updates, then snapping back when UWB measurements arrive.
Try It: 1D Kalman Filter for Temperature Tracking
Run this Python code to implement a Kalman filter that tracks temperature from a noisy sensor. Observe how the filter smooths measurements while tracking the true value.
import randomclass KalmanFilter1D:"""Simple 1D Kalman filter for scalar state estimation."""def__init__(self, x0, P0, Q, R, F=1.0, H=1.0):self.x = x0 # State estimateself.P = P0 # Estimate uncertainty (covariance)self.Q = Q # Process noise varianceself.R = R # Measurement noise varianceself.F = F # State transition (1.0 = constant)self.H = H # Measurement model (1.0 = direct)def predict(self):self.x =self.F *self.xself.P =self.F *self.P *self.F +self.Qreturnself.x, self.Pdef update(self, z):# Innovation (measurement residual) y = z -self.H *self.x# Innovation covariance S =self.H *self.P *self.H +self.R# Kalman gain K =self.P *self.H / S# Update state and covarianceself.x =self.x + K * yself.P = (1- K *self.H) *self.Preturnself.x, self.P, K# Simulate a slowly rising temperature with noisy sensorrandom.seed(42)true_temps = [20.0+0.3* t +0.1* (t %5) for t inrange(30)]measurements = [t + random.gauss(0, 1.5) for t in true_temps]# Initialize Kalman filterkf = KalmanFilter1D( x0=20.0, # Initial guess P0=5.0, # High initial uncertainty Q=0.2, # Process noise (temp changes slowly) R=2.25# Measurement noise (sensor variance 1.5^2))print(f"{'Step':>4}{'True':>7}{'Measured':>9}{'Estimate':>9} "f"{'Error':>7}{'K':>6}{'P':>6}")print("-"*55)errors_raw = []errors_filtered = []for i, z inenumerate(measurements): kf.predict() x_est, P, K = kf.update(z) raw_err =abs(z - true_temps[i]) filt_err =abs(x_est - true_temps[i]) errors_raw.append(raw_err) errors_filtered.append(filt_err)if i %3==0: # Print every 3rd stepprint(f"{i:4d}{true_temps[i]:7.2f}{z:9.2f}{x_est:9.2f} "f"{filt_err:7.3f}{K:6.3f}{P:6.3f}")avg_raw =sum(errors_raw) /len(errors_raw)avg_filt =sum(errors_filtered) /len(errors_filtered)print(f"\nAverage absolute error:")print(f" Raw measurements: {avg_raw:.3f} C")print(f" Kalman filtered: {avg_filt:.3f} C")print(f" Improvement: {(1- avg_filt/avg_raw)*100:.1f}%")print(f"\nFinal Kalman gain K = {K:.3f} (steady state)")print(f"Final uncertainty P = {P:.3f} (converged from {5.0})")
What to Observe:
The Kalman gain K starts high (~0.7) and converges to a steady-state value (~0.25) as the filter becomes confident
The uncertainty P drops rapidly in the first few steps, then stabilizes
Filtered estimates are consistently closer to true values than raw measurements
The filter tracks the upward trend while rejecting sensor noise
Try It: GPS + Accelerometer Sensor Fusion
This example fuses a slow but accurate GPS sensor (1 Hz) with a fast but drifty accelerometer (10 Hz) to track 1D position – the core idea behind navigation systems.
import randomimport mathclass SensorFusionKF:"""Kalman filter for position+velocity fusion."""def__init__(self, x, v, P_x, P_v, Q_x, Q_v, R_gps, R_accel):self.x = x # Position estimateself.v = v # Velocity estimateself.P_x = P_x # Position uncertaintyself.P_v = P_v # Velocity uncertaintyself.Q_x = Q_x # Position process noiseself.Q_v = Q_v # Velocity process noiseself.R_gps = R_gps # GPS measurement noiseself.R_accel = R_accel # Accelerometer noisedef predict(self, dt, accel=0):# Kinematic prediction: x = x + v*dt + 0.5*a*dt^2self.x +=self.v * dt +0.5* accel * dt * dtself.v += accel * dt# Uncertainty growsself.P_x +=self.P_v * dt * dt +self.Q_xself.P_v +=self.Q_vdef update_gps(self, z_gps): K =self.P_x / (self.P_x +self.R_gps)self.x += K * (z_gps -self.x)self.P_x *= (1- K)return Kdef update_velocity(self, z_vel): K =self.P_v / (self.P_v +self.R_accel)self.v += K * (z_vel -self.v)self.P_v *= (1- K)return K# Simulate a vehicle: constant velocity 10 m/s for 10 secondsrandom.seed(123)true_vel =10.0# m/s constantdt_accel =0.1# Accelerometer at 10 Hzdt_gps =1.0# GPS at 1 Hztotal_time =10.0kf = SensorFusionKF( x=0, v=9.0, # Initial estimates (slightly wrong velocity) P_x=10.0, P_v=4.0, # Initial uncertainties Q_x=0.01, Q_v=0.1, # Process noise R_gps=9.0, # GPS noise (3m std dev) R_accel=1.0# Velocity from accel integration noise)# Track errors for comparisongps_only_x =0.0accel_only_x =0.0accel_only_v =9.0print("Sensor Fusion: GPS (1 Hz) + Accelerometer (10 Hz)")print(f"{'Time':>5}{'True X':>8}{'GPS Only':>9}{'Accel Only':>11} "f"{'Fused':>8}{'P_x':>6}")print("-"*58)t =0.0step =0while t < total_time: true_x = true_vel * t# Accelerometer update (every 0.1s) accel_noise = random.gauss(0, 0.5) measured_vel = true_vel + accel_noise kf.predict(dt_accel, accel=0) kf.update_velocity(measured_vel)# Accumulate accel-only estimate (no GPS correction) accel_only_v += accel_noise * dt_accel accel_only_x += accel_only_v * dt_accel# GPS update (every 1.0s)if step %10==0and step >0: gps_noise = random.gauss(0, 3.0) gps_measurement = true_x + gps_noise K_gps = kf.update_gps(gps_measurement) gps_only_x = gps_measurement # GPS-only = last readingprint(f"{t:5.1f}{true_x:8.1f}{gps_only_x:9.1f} "f"{accel_only_x:11.1f}{kf.x:8.1f}{kf.P_x:6.2f} "f"<-- GPS update (K={K_gps:.2f})") t += dt_accel step +=1# Final comparisontrue_final = true_vel * total_timeprint(f"\n--- Final Position Errors at t={total_time}s ---")print(f"True position: {true_final:.1f} m")print(f"GPS-only error: {abs(gps_only_x - true_final):.1f} m")print(f"Accel-only error: {abs(accel_only_x - true_final):.1f} m (drift!)")print(f"Fused error: {abs(kf.x - true_final):.1f} m")print(f"Fused uncertainty: {math.sqrt(kf.P_x):.2f} m (1-sigma)")
What to Observe:
GPS-only has 3m noise per reading but no drift
Accelerometer-only drifts further and further from truth over time
The fused estimate is better than either sensor alone
Between GPS updates, uncertainty grows (predict-only); each GPS update shrinks it (see P_x jumps)
The Kalman gain for GPS starts high and decreases as the filter becomes confident
Interactive Quiz: Match Concepts
Interactive Quiz: Sequence the Steps
Common Pitfalls
1. Treating Q and R as constants for all operating conditions
Process noise varies with device motion state (stationary vs vibrating machine), and measurement noise varies with sensor conditions (temperature, battery level). Use adaptive covariance estimation or at least tune Q and R separately for key operating modes.
2. Using the standard Kalman filter for strongly non-linear systems
The standard Kalman filter assumes linear system and measurement models. For non-linear IoT applications (GPS/INS, robot localisation), the linearisation error of an EKF can cause filter divergence. Consider Unscented Kalman Filter (UKF) for better non-linear handling.
3. Initialising the state vector without a good prior
Starting the Kalman filter with a state estimate far from the true initial value causes a long convergence period with poor estimates. Use the first few sensor readings to initialise the state before trusting filter outputs.
4. Not monitoring filter health in production
Kalman filter innovation (difference between measurement and prediction) should be consistent with the innovation covariance. Large, persistent innovations indicate model mismatch or sensor malfunction. Log and monitor innovation statistics.
Label the Diagram
20.9 Summary
The Kalman filter is the foundation of sensor fusion:
Predict-Update Cycle: Project forward with model, correct with measurements
Optimal Weighting: Kalman gain balances prediction vs measurement based on uncertainties
Uncertainty Tracking: Covariance matrix captures confidence in estimates
Fusion Benefit: Combined estimate better than any single sensor
Quiz: Kalman Filter Fusion
For Kids: Meet the Sensor Squad!
The Kalman filter is like a super-smart guesser who gets better and better at predicting the future!
20.9.1 The Sensor Squad Adventure: The Great Guessing Game
Max the Microcontroller invented a new game for the Sensor Squad called “Guess Where the Ball Is!”
A ball was rolling across the playground, and the squad had to track it. But there was a problem – they could only peek at the ball through a foggy window (that is their noisy sensor!).
Round 1 – The Prediction: Max said, “The ball was at the 10-meter mark and rolling at 2 meters per second. So in one second, it should be at… 12 meters!” But Max was not 100% sure because the wind might have pushed the ball. His CONFIDENCE went down a tiny bit.
Round 2 – The Peek: Sammy the Sensor peeked through the foggy window and said, “I think the ball is at 14 meters!” But Sammy’s view was fuzzy too.
Round 3 – The Magic Blend: Here is where the Kalman magic happens! Max thought: “My guess says 12, Sammy’s peek says 14. Sammy’s peek is a LITTLE more reliable than my guess right now, so let me blend them: about 13.2 meters!” Max’s CONFIDENCE went UP because he combined two clues!
Bella the Battery was amazed: “Your confidence went UP even though BOTH clues were fuzzy?”
“That is the magic!” said Max. “Two fuzzy clues combined are BETTER than either one alone. Every time I predict AND peek, I get more and more confident!”
Lila the LED added: “And the Kalman GAIN is like a dial that controls how much you trust your prediction versus your peek!”
After 10 rounds, Max was tracking the ball within half a meter – way better than either his predictions or Sammy’s peeks alone!
20.9.2 Key Words for Kids
Word
What It Means
Predict
Using what you know to guess what will happen next
Update
Fixing your guess when you get new information
Kalman Gain
The dial that controls how much you trust new info vs your guess
Covariance
A number that tells you how confident or uncertain you are
Converge
When your guesses get closer and closer to the real answer
The Kalman filter is the foundation of most sensor fusion systems. Its power lies in the predict-update cycle that automatically balances trust between the motion model and sensor measurements. The key to success is accurate noise modeling: measure R empirically from stationary sensor data, and set Q based on expected system dynamics. When the system is non-linear, use the Extended Kalman Filter (EKF) with Jacobian linearization.
20.10 What’s Next
If you want to…
Read this
Apply the Kalman filter to a complementary IMU fusion example