05 Simultaneous Localization & Mapping · #23 of 46

EKF SLAM

How a robot draws the map and finds itself on it at the same time

A dense 3D lidar point cloud of a street scene, walls and parked cars rendered as millions of bright scan points.
A single lidar scan: millions of points, no labels. SLAM has to decide which of these dots is a landmark, and where the robot is standing among them. · Daniel L. Lu, CC BY 4.0

To build a map you need to know where you are. To know where you are you need a map. A robot that has just woken up in an unknown room has neither, and has to bootstrap both at once.

This is the chicken-and-egg problem at the heart of robotics, and for a long time it looked unsolvable. The trick that cracked it is almost embarrassingly simple, and you can watch it happen below.

landmarks 0/0 · loops 0 · robot err 0.00m · map err 0.00m

Yellow = first sighting, green = re-observed. Drive a lap: the estimate (blue trail) drifts from truth, then re-seeing an early landmark snaps the whole map tighter: robot AND landmark ellipses shrink together.

The robot does not know where it is and does not know where the landmarks are. But it knows one precious thing: how far apart it and each landmark are, the instant it looks. Hold onto that: it is the entire secret.

Play with the entanglement

The accent ellipse is the robot's uncertainty. The blue ellipses are landmark uncertainties. The faint halo is sensor range: anything inside it gets observed this tick.

Try this:

  1. Drive blind. Move the robot. Watch its uncertainty ellipse swell as odometry drift accumulates, the same drunkard's walk you met in the last phase.
  2. First sighting. Spot a brand-new landmark. Its ellipse starts huge, then shrinks within a few looks as the robot triangulates it.
  3. The magic moment. Drive a loop and return to a landmark you logged way back at the start. The instant you re-recognise it, the entire map snaps tighter at once: robot and every landmark. That is loop closure, and it is the most satisfying half-second in all of SLAM.
  4. Spot the strings. Watch two nearby landmark ellipses shrink together even when you only looked at one. They are not independent. They never were.

One matrix to bind them

Here is the idea most courses bury under linear algebra. EKF-SLAM does not keep a separate guess for the robot and a separate guess for each landmark. It keeps a single state vector (robot pose stacked on top of every landmark position) and a single covariance matrix that records how every estimate correlates with every other.

That shared matrix is the secret. When you re-observe an old landmark, the correction doesn't just nudge that one landmark. It flows down the off-diagonal correlations into the robot's pose, and from there back out into landmarks the robot hasn't looked at in minutes. The robot and the map are tied together by invisible springs of mathematics. Pull one, and the whole structure heals.

Predict, then correct, now with a map

EKF-SLAM is just the Kalman two-step you already know, run on a state vector that happens to include the world.

Predict. The robot moves. Push the pose through the motion model, and let its uncertainty grow:

x^kk1=f(x^k1,uk)\hat{x}_{k|k-1} = f(\hat{x}_{k-1}, u_k) Pkk1=FkPk1FkT+QkP_{k|k-1} = F_k P_{k-1} F_k^T + Q_k

where Fk=fxF_k = \dfrac{\partial f}{\partial x} is the Jacobian that linearizes the motion: the "E" in EKF, because the real motion is curved and we pretend, locally, that it is flat.

Update. A landmark comes into view. Compute the Kalman gain, then correct everything by how wrong the prediction was:

K=PHT(HPHT+R)1K = P H^T (H P H^T + R)^{-1} x^k=x^kk1+K(zh(x^kk1))\hat{x}_k = \hat{x}_{k|k-1} + K \, (z - h(\hat{x}_{k|k-1}))

Here hh is the measurement model (the range and bearing the robot expects to see) and zh()z - h(\cdot) is the innovation, the surprise. The genius is that KK has a row for every state, so a single observation rewrites the robot pose and all the correlated landmarks in one stroke.

Portrait of roboticist Sebastian Thrun.
Sebastian Thrun · 1967– wrote Probabilistic Robotics, led Stanley to win the 2005 DARPA Grand Challenge, and founded Google's self-driving project. read more →
The math: why the covariance matrix is the whole map

Stack the robot pose and all nn landmarks into one state:

x=[xr,yr,θr,x1,y1,x2,y2,,xn,yn]T\mathbf{x} = [x_r, y_r, \theta_r, x_1, y_1, x_2, y_2, \ldots, x_n, y_n]^T

The robot pose is (xr,yr,θr)(x_r, y_r, \theta_r); each (xi,yi)(x_i, y_i) is a landmark. The estimate's uncertainty lives in one big covariance matrix:

Σ=[ΣrrΣrm1Σrm2Σm1rΣm1m1Σm1m2]\Sigma = \begin{bmatrix} \Sigma_{rr} & \Sigma_{rm_1} & \Sigma_{rm_2} & \cdots \\ \Sigma_{m_1 r} & \Sigma_{m_1 m_1} & \Sigma_{m_1 m_2} & \cdots \\ \vdots & & & \ddots \end{bmatrix}

The diagonal blocks are how unsure you are about each thing on its own. The off-diagonal blocks (Σrm1\Sigma_{rm_1}, Σm1m2\Sigma_{m_1 m_2}) are the springs. They say "if the robot turns out to be a metre east, this landmark must be a metre east too." Loop closure works because those blocks are non-zero: a measurement of one landmark reaches every estimate it is correlated with.

For a range-and-bearing sensor, the measurement Jacobian HH (with Δx,Δy\Delta x, \Delta y the offset from robot to landmark and d=Δx2+Δy2d = \sqrt{\Delta x^2 + \Delta y^2}) is:

H=[ΔxdΔyd0ΔxdΔydΔyd2Δxd21Δyd2Δxd2]H = \begin{bmatrix} \frac{-\Delta x}{d} & \frac{-\Delta y}{d} & 0 & \frac{\Delta x}{d} & \frac{\Delta y}{d} \\ \frac{\Delta y}{d^2} & \frac{-\Delta x}{d^2} & -1 & \frac{-\Delta y}{d^2} & \frac{\Delta x}{d^2} \end{bmatrix}

Notice it has columns for the robot pose and for the observed landmark. That is the channel through which a single look corrects both ends of the spring at once.

The quadratic cost falls straight out of this picture: a matrix that correlates everything with everything has on the order of n2n^2 entries to maintain. Beautiful, and unscalable.

Where it earns its keep

The hard part in practice is rarely the filter. It is data association: deciding which landmark you're looking at. Guess wrong, fuse a measurement into the wrong landmark, and the springs yank the whole map into a confident, catastrophic lie. The standard defence is the Mahalanobis distance, which measures "how surprising is this observation, scaled by how uncertain I already am," and refuses any match beyond a few sigma.

// Mahalanobis distance: accounts for uncertainty
fn mahalanobis_dist(z: Vec2, landmark: Vec2, s: Mat2) -> f32 {
    let innovation = z - landmark;
    (innovation.transpose() * s.inverse() * innovation).sqrt()
}

// Match to nearest landmark within 3-sigma; otherwise it's new
if mahal_dist < 3.0 { /* associate */ } else { /* spawn a landmark */ }

A complete loop subscribes to wheel odometry at high rate for the predict step, to a lidar scan at a lower rate for the update step, extracts landmarks (walls, corners), associates them, and publishes a corrected pose plus a map of landmark positions.

Key takeaways

A robot waking up in a dark room has no map and no idea where it stands. Within a few seconds it has both, not because it solved the chicken-and-egg problem, but because it refused to choose. It guessed at both, watched how they had to move together, and let one truth pull the other into focus.

Drive a loop, return to where you started, and the whole world clicks into place. That click is the sound of a machine realising it has been somewhere before.

full glossary →