Skip to the content
FixTheRobot Explore Robotics / UAV / Module 3 / M3-T5

UAV Systems Engineering · Module 3

M3-T5: Extended Kalman Filter

When the world is nonlinear, linearise at the current estimate: the EKF is the Kalman filter with Jacobians in place of matrices.

  • Lesson 5 of 16
  • Sensor Fusion and State Estimation
  • Established engineering
  • About 6 minutes to read

UAV models are nonlinear: attitude rotations, bearing and range measurements, camera projections. The EKF handles them by linearising at each step. It works well when uncertainty is small, and it fails in ways that are worth knowing before you trust it.

Learning objectives

After this lesson, you should be able to:

  • State the EKF equations and the role of the Jacobians.
  • Derive the Jacobian of a range-bearing measurement model.
  • Verify Jacobians numerically with finite differences.
  • Explain EKF inconsistency and the situations that cause it.

Conceptual explanation

The Kalman filter needs linear models. The EKF replaces the nonlinear motion ff and measurement hh with their first-order Taylor expansions around the current estimate. The matrices F=∂f/∂xF=\partial f/\partial x and H=∂h/∂xH=\partial h/\partial x (the Jacobians) take the place of the constant matrices, and the covariance is propagated through them exactly as before. The state is propagated through the full nonlinear function; only the covariance uses the linearisation.

Linearisation in the EKFA nonlinear function is replaced by its tangent at the current estimate. A Gaussian passed through the tangent stays Gaussian; the approximation is good when the uncertainty is small relative to the curvature. x̂nonlinear h(x)tangent: h(x̂) + H (x − x̂)H = ∂h/∂x evaluated at the current estimate
Linearisation: the EKF treats the function as its tangent at the current estimate.
Animation of how a Gaussian passes through a nonlinear measurement function, the range to a beacon. The left chart shows the curve and its tangent line at the mean; the right chart compares the histogram of the true transformed samples with the Gaussian predicted by the linearisation. As the input uncertainty grows from 0.2 to 3 metres the true distribution becomes skewed and its mean shifts, while the tangent-based Gaussian stays symmetric and increasingly wrong.
EKF: y=h(x)≈h(μ)+H(x−μ)y=h(x)\approx h(\mu )+H(x-\mu ), so the output is Gaussian with variance H2 σ2H^{2}\,\sigma ^{2}. For small σ\sigma the tangent is accurate; for large σ\sigma the curvature of hh skews the true distribution and the EKF becomes biased and overconfident. h(x)=x2+22h(x)=\sqrt{x^{2} + 2^{2}}, mean 1 m; 20 000 samples. (Looping, about 6 seconds; drawn and computed for this course with illustrative values.)

Engineering intuition

If the estimate is uncertain by ±1 m and the function curves gently over that range, the tangent is nearly the function and the EKF is nearly exact. If the uncertainty is large relative to the curvature (a poor initial guess, a bearing measurement to a nearby object, a heading error of tens of degrees) the tangent is a poor stand-in. The covariance then no longer describes the true error, the filter becomes overconfident, and it can diverge. The cure is not bigger numbers but better linearisation points, better initial estimates, or a different filter (error-state, iterated, unscented).

Mathematical formulation

Model: xk=f(xk−1, uk)+wkx_{k}=f(x_{k-1},\,u_{k})+w_{k}, zk=h(xk)+vkz_{k}=h(x_{k})+v_{k}.

Predict
x−=f(x,u),P−=F P F⊤+Q,F=∂f∂x∣(x,u)\mathbf x^{-} = f(\mathbf x, \mathbf u), \qquad \mathbf P^{-} = \mathbf F\,\mathbf P\,\mathbf F^{\top} + \mathbf Q, \qquad \mathbf F = \left.\frac{\partial f}{\partial \mathbf x}\right|_{(\mathbf x,\mathbf u)}
Update: innovation
y=z−h(x−),S=H P−H⊤+R,H=∂h∂x∣x−\mathbf y = \mathbf z - h(\mathbf x^{-}), \qquad \mathbf S = \mathbf H\,\mathbf P^{-}\mathbf H^{\top} + \mathbf R, \qquad \mathbf H = \left.\frac{\partial h}{\partial \mathbf x}\right|_{\mathbf x^{-}}
Update: gain
K=P−H⊤S−1\mathbf K = \mathbf P^{-}\mathbf H^{\top}\mathbf S^{-1}
Update: state and covariance (Joseph form)
x=x−+K y,P=(I−KH) P−(I−KH)⊤+KRK⊤\mathbf x = \mathbf x^{-} + \mathbf K\,\mathbf y, \qquad \mathbf P = (\mathbf I - \mathbf K\mathbf H)\,\mathbf P^{-}(\mathbf I - \mathbf K\mathbf H)^{\top} + \mathbf K\mathbf R\mathbf K^{\top}

Range and bearing

For a station at the origin and a vehicle at (xx, yy), the measurement is h(x)=[r, β]=[x2+y2, arctan⁡2(y, x)]h(x)=[r,\,\beta ]=[\sqrt{x^{2} + y^{2}},\,\arctan 2(y,\,x)]. With r=x2+y2r=\sqrt{x^{2} + y^{2}}:

Range-bearing Jacobian
H=[x/ry/r00−y/r2x/r200]\mathbf H = \begin{bmatrix}x/r & y/r & 0 & 0\\ -y/r^{2} & x/r^{2} & 0 & 0\end{bmatrix}
The state is [x, y, vx, vy].

The bearing innovation must be wrapped to (−π, π](-\pi ,\,\pi ]: yβ=wrap(zβ−β^)y_{\beta }=\mathrm{wrap}(z_{\beta }-\hat{\beta }).

Checking Jacobians

Finite-difference check
Hnum[:,j]≈h(x+ε ej)−h(x−ε ej)2ε\mathbf H_{\text{num}}[:, j] \approx \frac{h(\mathbf x + \varepsilon\,\mathbf e_j) - h(\mathbf x - \varepsilon\,\mathbf e_j)}{2\varepsilon}
Compare with the analytic H to about 10−6 relative error.

Most EKF bugs live in the Jacobians. Check them numerically in a test, always.

Architecture and data flow

Inside PX4, EKF2 is an error-state filter: it propagates the nominal state with the IMU and estimates small errors (lesson M3-T6). The principle, linearise at the estimate and fuse sensors sequentially with their measurement Jacobians, is the EKF’s. When you feed external odometry, you are supplying a measurement zz with a covariance RR that the filter uses exactly as in the update above.

Real-world UAV applications

  • GNSS/INS fusion in autopilots.
  • Bearing-only and range-bearing target tracking.
  • Camera-based measurement updates (reprojection residuals) in MSCKF-style VIO.
  • Landing on a beacon using range and bearing.

Implementation example

This runs as shown: it verifies the analytic Jacobians with finite differences and runs an EKF on range-bearing tracking, reporting the position error and NIS consistency.

import numpy as np
rng = np.random.default_rng(5)

def h(x):  return np.array([np.hypot(x[0], x[1]), np.arctan2(x[1], x[0])])
def H_of(x):
    r = np.hypot(x[0], x[1])
    return np.array([[x[0]/r, x[1]/r, 0, 0], [-x[1]/r**2, x[0]/r**2, 0, 0]])

# 1) Jacobian check by central differences
x0 = np.array([30.0, 20.0, 3.0, -1.0]); eps = 1e-6
Hn = np.column_stack([(h(x0 + eps*e) - h(x0 - eps*e)) / (2*eps) for e in np.eye(4)])
assert np.allclose(Hn, H_of(x0), atol=1e-7)

# 2) EKF on a constant-velocity target
dt, n = 0.2, 300
F = np.eye(4); F[0, 2] = F[1, 3] = dt
q = 0.3; Qb = q * np.array([[dt**3/3, dt**2/2], [dt**2/2, dt]])
Q = np.zeros((4, 4)); Q[np.ix_([0, 2], [0, 2])] = Qb; Q[np.ix_([1, 3], [1, 3])] = Qb
R = np.diag([1.5**2, np.radians(1.0)**2])
Lq = np.linalg.cholesky(Q + 1e-12*np.eye(4))
xt = np.array([60.0, 25.0, -2.0, 1.0]); xh = xt + np.array([5.0, -5.0, 1.0, 1.0]); P = np.diag([25.0, 25.0, 4.0, 4.0])
nis, errs = [], []
for k in range(n):
    xt = F @ xt + Lq @ rng.standard_normal(4)
    z = h(xt) + np.sqrt(np.diag(R)) * rng.standard_normal(2)
    xh = F @ xh; P = F @ P @ F.T + Q
    H = H_of(xh); y = z - h(xh); y[1] = (y[1] + np.pi) % (2*np.pi) - np.pi        # wrap the bearing innovation
    S = H @ P @ H.T + R; K = P @ H.T @ np.linalg.inv(S)
    xh = xh + K @ y; I_KH = np.eye(4) - K @ H; P = I_KH @ P @ I_KH.T + K @ R @ K.T
    if k > 30: nis.append(y @ np.linalg.inv(S) @ y); errs.append(np.hypot(*(xh[:2] - xt[:2])))
print(f"mean position error {np.mean(errs):.2f} m, mean NIS {np.mean(nis):.2f} (expected 2)")
assert np.mean(errs) < 3.0 and 1.2 < np.mean(nis) < 3.2
Jacobian verification and an EKF for range-bearing tracking.

Common engineering mistakes

Wrong or unchecked Jacobians

Verify with finite differences in a unit test.

Forgetting to wrap angle innovations

A 359∘A\,359^{\circ} bearing error instead of −1∘1^{\circ} wrecks the update.

Linearising at a poor initial guess

A far-off start produces an unrepresentative Jacobian and divergence; initialise with a robust method, then switch to the EKF.

Compensating inconsistency by inflating noise blindly

It hides the problem. Diagnose with NIS and NEES.

Using the EKF for large attitude errors with Euler angles

Use an error-state formulation with quaternions.

Failure modes

Failure modes, symptoms and causes
Failure modeHow it shows upLikely cause
Linearisation errorFilter is confidently wrong when uncertainty is largeBad initialisation, nearby bearing measurements, large heading error
Angle wrap bugSudden state jump when the bearing crosses ±π\pi Innovation not wrapped
InconsistencyNEES far above its expected valueLinearisation or unmodelled error; overconfident covariance
SingularityNaN near r=0r=0Division by range in the Jacobian

Debugging approach

  1. Compare analytic and numerical Jacobians at several operating points including extreme ones.
  2. Run the filter in simulation with exactly known noise; fix inconsistency there before real data.
  3. Plot angle innovations and verify they stay within (−π, π](-\pi ,\,\pi ] and look white.
  4. Start the filter from several initial guesses and check it converges from all.

Practical exercises

  1. Derive the Jacobian for a bearing-only measurement and discuss observability from a single station.
  2. Use the EKF Simulator to find the smallest QQ that keeps NIS in its 95% bounds on the manoeuvring target.
  3. Increase the initial position error to 40 m with an initial σ\sigma of 1 m and report what happens.
  4. Replace the EKF update by an iterated update (relinearise 3 times) and compare consistency.

Interview questions

Write an answer in your own words, then reveal the model answer. Nothing is sent anywhere.

Question 1

How does the EKF differ from the Kalman filter?

Model answer

It linearises the nonlinear process and measurement models at the current estimate using Jacobians. The state goes through the nonlinear functions; the covariance goes through their linearisations.

Question 2

When does the EKF fail?

Model answer

When uncertainty is large compared with the nonlinearity (poor initialisation, strong curvature), producing overconfident covariance and divergence; also from wrong Jacobians or unwrapped angles.

Question 3

How do you verify an EKF implementation?

Model answer

Check Jacobians against finite differences, test on simulated data with known noise, and check NIS/NEES against chi-square expectations.

References and further reading

Primary papers, official documentation and standards. Each claim above that is specific to a tool or version points here.

  • TT. DD. Barfoot, State Estimation for Robotics, Cambridge University Press, 2017, chapter 4.
  • YY. Bar-Shalom, XX. RR. Li, TT. Kirubarajan, Estimation with Applications to Tracking and Navigation, Wiley, 2001, chapter 5.
  • PX4: Using the EKF2 estimator

What’s next

Mark this lesson

Saved in this browser and synced to your account when you are signed in.

Practice this lesson

M3-L2: Implement an EKF

Tune and inspect an EKF on a nonlinear tracking problem.

Next lesson

M3-T6: Error-State Kalman Filter

Explain why attitude estimation uses an error-state formulation and describe its propagation and injection steps.