Skip to main content
Open In Colab EKF localization needs a map, and correspondence estimation needs a map to match against. A robot placed in an unknown room has neither. Simultaneous localization and mapping (SLAM) estimates the map and the robot’s path together, from the same measurements and actions. Given the actions a1:ta_{1:t} and the measurements z1:tz_{1:t}, full SLAM estimates the whole path and the map, p(s1:t,m∣z1:t,a1:t),p(s_{1:t}, m \mid z_{1:t}, a_{1:t}), while online SLAM estimates only the current pose and the map, p(st,m∣z1:t,a1:t).p(s_t, m \mid z_{1:t}, a_{1:t}). The two estimates are coupled. An error in the pose places every landmark seen from that pose in the wrong spot, and when the robot sees a wrongly placed landmark again, the update shifts the pose estimate. This section builds the oldest online SLAM algorithm, EKF SLAM, in the version for unknown correspondences, where the landmark that produced each measurement is not given: section 10.3 of Thrun, Burgard and Fox (Burgard & Thrun, n.d.). It runs it on a TurtleBot with two different landmark front ends, the LIDAR and the D435i depth camera, feeding the same filter.

The EKF SLAM state

EKF SLAM keeps a Gaussian belief over a state that stacks the robot pose and every landmark found so far: yt=[stm]=(x,y,θ,m1,x,m1,y,…,mN,x,mN,y)⊤,bel(yt)=N(μt,Σt).y_t = \begin{bmatrix} s_t \\ m \end{bmatrix} = (x, y, \theta, m_{1,x}, m_{1,y}, \ldots, m_{N,x}, m_{N,y})^\top, \qquad \mathtt{bel}(y_t) = \mathcal N(\mu_t, \Sigma_t). The state has 3+2N3 + 2N entries and grows as the robot finds landmarks. The covariance Σt\Sigma_t holds the uncertainty of the pose and of each landmark, and the cross terms between every pair. The cross terms are nonzero because the map was built from an uncertain pose. Through them, a correction to one landmark also moves the pose and every other landmark. The prediction step moves only the pose, because landmarks do not move. With the velocity motion model gg from the EKF localization section and its Jacobians GtG_t (state) and VtV_t (wheel speeds), μˉt=[g(at,μt−1,s)μt−1,m],Σˉt=[Gt00I]Σt−1[Gt00I]⊤+[Qt000],Qt=VtMtVt⊤,\bar\mu_t = \begin{bmatrix} g(a_t, \mu_{t-1,s}) \\ \mu_{t-1,m} \end{bmatrix}, \qquad \bar\Sigma_t = \begin{bmatrix} G_t & 0 \\ 0 & I \end{bmatrix} \Sigma_{t-1} \begin{bmatrix} G_t & 0 \\ 0 & I \end{bmatrix}^\top + \begin{bmatrix} Q_t & 0 \\ 0 & 0 \end{bmatrix}, \qquad Q_t = V_t M_t V_t^\top , where MtM_t is the covariance of the wheel-speed noise. The pose block, the pose’s cross terms with the map, and nothing else change. The robot model is copied from the EKF localization section.

Measurements, new landmarks and correspondences

A landmark mkm_k is measured as a range and bearing, z=h(s,mk)z = h(s, m_k), the same model as in localization. The difference is that the landmark position is now part of the state, so the Jacobian has two nonzero blocks: one for the pose and one for landmark kk. For the innovation covariance and the gain the filter builds the full 2×(3+2N)2 \times (3 + 2N) matrix Htk=[∂h∂s0⋯0∂h∂mk0⋯0],Ψk=HtkΣˉtHtk⊤+Rt,H^k_t = \begin{bmatrix} \dfrac{\partial h}{\partial s} & 0 \cdots 0 & \dfrac{\partial h}{\partial m_k} & 0 \cdots 0 \end{bmatrix}, \qquad \Psi_k = H^k_t \bar\Sigma_t H^{k\top}_t + R_t , where RtR_t is the measurement noise. The bearing part of the innovation is wrapped to [−π,π)[-\pi, \pi): a landmark straight behind the robot can be measured at π−ϵ\pi - \epsilon and predicted at −π+ϵ-\pi + \epsilon, and without wrapping, the raw difference of almost 2π2\pi would enter the update as a large wrong innovation.

Unknown correspondences

The algorithm of Thrun, Burgard and Fox (Table 10.2) picks, for each measurement ztiz^i_t, the landmark with the smallest Mahalanobis distance πk=(zti−z^tk)⊤Ψk−1(zti−z^tk),\pi_k = (z^i_t - \hat z^k_t)^\top \Psi_k^{-1} (z^i_t - \hat z^k_t), the quantity Dik2D^2_{ik} of the correspondence estimation section. It also considers one extra hypothesis, that ztiz^i_t comes from a landmark not yet in the map, and gives that hypothesis the fixed score α\alpha. If every existing landmark scores above α\alpha, the measurement starts a new landmark. The code uses α=13.8\alpha = 13.8, the 99.9th percentile of the χ2\chi^2 distribution with 2 degrees of freedom: a measurement of a known landmark scores above it only once in a thousand times.

Adding a landmark

The new landmark’s mean is the measured point placed in the world from the predicted pose, mN+1=[μˉx+rcos⁡(ϕ+μˉθ)μˉy+rsin⁡(ϕ+μˉθ)].m_{N+1} = \begin{bmatrix} \bar\mu_x + r \cos(\phi + \bar\mu_\theta) \\ \bar\mu_y + r \sin(\phi + \bar\mu_\theta) \end{bmatrix}. Its covariance must contain both the measurement noise and the pose uncertainty, and it must be correlated with the pose. Thrun, Burgard and Fox start every landmark with infinite variance (their equation 10.10), which a computer cannot store. The code instead extends Σ\Sigma with the two Jacobians of this placement function, GsG_s with respect to the pose and GzG_z with respect to the measurement, as Corke does in sections 6.3 and 6.4 (Corke, 2023): ΣmN+1=GsΣssGs⊤+GzRtGz⊤,ΣmN+1,⋅=GsΣs,⋅.\Sigma_{m_{N+1}} = G_s \Sigma_{ss} G_s^\top + G_z R_t G_z^\top, \qquad \Sigma_{m_{N+1}, \cdot} = G_s \Sigma_{s, \cdot} . With NN landmarks in the map, the quantities above have these sizes: The block matrix that multiplies Σt−1\Sigma_{t-1} in the prediction is (3+2N)×(3+2N)(3 + 2N) \times (3 + 2N), with GtG_t in the pose block and the identity on the landmarks. Adding a landmark grows the state by 2 and the covariance by two rows and two columns. A measurement that creates a landmark is not used to update the pose: it carries no information about the pose until the landmark is seen again. The function ekf_slam below performs one prediction and then processes the measurements one at a time, updating the belief after each.

The room and the drive

The TurtleBot drives a counterclockwise circle of radius 2.4 m at 0.2 m/s in a room with walls at ±4\pm 4 m. Eight pillars of radius 10 cm stand on a ring of radius 2.8 m, just outside the path. Only pillars within 2.5 m are used. The drive lasts 1.6 laps, 603 steps of 0.2 s, so the robot passes its first landmarks again. The robot follows the circle exactly. The noise is in the wheel encoder readings: each step the reported wheel speeds differ from the true ones by the noise used in the EKF localization section, a standard deviation of 5% of each wheel speed plus 0.1 rad/s. Dead reckoning integrates these reports, and so does the filter’s prediction.
Output from cell 5 The figure shows the room from above, drawn to scale. The robot starts at the green dot on the right, heading in the +y+y direction, and drives counterclockwise. The blue pose, 30° around the circle, is the example used in the next two figures. The dashed circle around it marks the 2.5 m range within which pillars count as landmarks, and the shaded wedge is the D435i’s field of view, about 55° wide.

Pillars from a LIDAR scan

The simulated LIDAR matches the one on the course simulator’s TurtleBot3: 360 beams, one per degree, five scans a second, range noise of 1 cm. A scan is a list of ranges, so the front end has to find the pillars in it. It splits the scan wherever the range jumps by more than 15 cm between neighboring beams. A pillar in front of a wall shows up as a short run of beams with a jump on both sides. A cluster wider than 25 cm is a stretch of wall and is dropped, and so is a cluster farther than 2.5 m. The pillar’s bearing is the mean bearing of its beams, and its range is the shortest range in the cluster plus the pillar radius, because the beams hit the near surface. The same steps apply to a real scan, with three refinements. The jump threshold should grow with range, because neighboring beams land farther apart on distant surfaces. A pole of unknown size is better handled by fitting a circle to the cluster’s points and reporting the range and bearing of its center. Walls and corners, the other common landmarks, are found by fitting lines to the clusters. For ROS 2, laser_segmentation segments laser scans this way. A 3D point cloud is usually treated as an unordered set of points instead. The common pipeline removes the ground plane with RANSAC and groups the remaining points by their Euclidean distance. Open3D provides the two steps as segment_plane and cluster_dbscan, and the Point Cloud Library as SACSegmentation and EuclideanClusterExtraction. A LIDAR feature carries a weaker signature than a camera’s. The cluster’s width and shape separate a pillar from a wall, and the intensity of the returns separates a retroreflector from paint or concrete. The Point Cloud Library also computes shape descriptors that serve as a 3D signature, such as FPFH for the neighborhood of a point and VFH for a whole cluster. These attributes identify the kind of landmark that was hit, but not the individual landmark. Identical pillars still look alike, which is why this section has to estimate the correspondences.
Output from cell 7 On the left, each dot is where one beam of the scan from the example pose stopped. Most beams end on the walls. The colored dots are returns from the three pillars within 2.5 m, and the crosses are the centers the front end computed for them. The right panel is the same scan in the form the front end receives it: one range per degree, measured from the robot’s heading. Each pillar is a short dip with a steep jump on both sides. The three dips below the dashed line become landmark measurements. The dips above it are pillars farther than 2.5 m, which the front end drops.

Pillars from the D435i

The D435i sees only what is in front of it, within its field of view. The front end projects each pillar’s center into the color image with the intrinsics from the course’s D435i configuration (fx=617f_x = 617 px, cx=321c_x = 321 px, 640 pixels wide) and adds one pixel of noise to the column. The depth image gives the distance to the pillar’s near surface. Stereo depth error grows with the square of the distance, so the code uses a standard deviation of 0.01z2+0.0020.01 z^2 + 0.002 m, an assumed model rather than a datasheet value. From the column uu and the depth, the bearing and range in the camera frame are ϕ=arctan⁡cx−ufx,r=z+rpillarcos⁡ϕ.\phi = \arctan\frac{c_x - u}{f_x}, \qquad r = \frac{z + r_{\text{pillar}}}{\cos\phi}. The camera sits at the robot’s origin, looking forward. On a real robot the camera-to-base transform from the D435i section moves the measurement into the base frame first.
Output from cell 9 From the example pose only pillar 1 is inside the field of view and within 2.5 m. In the image it is a vertical band about 80 pixels wide. The front end reports the column of the band’s center and the depth to the pillar’s near surface. Along the whole path the camera never has more than one pillar in view, while the LIDAR sees two or three. The results below show how the number of pillars in view affects the estimate.

Measurement noise of the front ends

The filter needs RtR_t, the covariance of the range and bearing a front end reports. The code below places the robot at 400 random poses and compares each detection with the true pillar.
The LIDAR’s range is 7 mm short on average, because the nearest beam rarely hits the exact front of the pillar, and its errors are 8 mm and 0.2° (one standard deviation). The camera is four times worse in range, 3.2 cm, because of the depth noise, but better in bearing, 0.09°, because a pixel is narrower than a LIDAR beam. RtR_t is set above these figures, to 2 cm and 0.75° for the LIDAR and 5 cm and 0.5° for the camera. The filter is also affected by errors that these statistics do not measure. It evaluates hh and its Jacobian at the estimated pose and landmarks, and the LIDAR’s 7 mm range bias repeats in every scan, while the filter assumes the noise is independent from scan to scan. If RtR_t is set to the raw statistics, the filter gives each measurement too much weight, the gate becomes too tight, and a measurement of a known pillar that falls just outside the gate starts a duplicate landmark.

Running the filter

The code below drives the loop once with each front end. Both runs use the same wheel noise, so dead reckoning is the same in both.
Output from cell 12 Dead reckoning (dotted) spirals inward and ends 1.21 m from the truth. Both filters stay on the circle. Each found exactly the eight pillars and assigned every measurement to the right one. The LIDAR’s landmark ellipses are smaller than the camera’s: the LIDAR keeps each pillar in view for longer and from more directions as the robot goes round, while the camera sees it only on the approach.

Watching a run step by step

The recording below replays both runs, the LIDAR run on the left and the camera run on the right. In each map the green arrow is the true pose and the orange arrow is the filter’s estimate, with its 2σ2\sigma ellipse. The gray line is dead reckoning. Blue dots are the landmarks in the map, each with its own 2σ2\sigma ellipse, and the gold lines are the current step’s measurements. The gray dots in the LIDAR map are the current scan, and the gold wedge in the camera map is the field of view. The plots underneath show the position error and the number of landmarks in the map for both runs.
Press play or drag the time marker. Scroll to zoom a map and double-click to reset it. The button at the top right of each panel opens it full size. Output from cell 15 The error plots show how the two runs differ. With the camera, the error grows steadily to about 0.33 m: a pillar leaves the narrow field of view after a few seconds, so each landmark constrains the pose only briefly, and the drift of the pose carries over into the landmark positions. At 67.8 s the camera sees its first landmark again, initialized at the start when the pose was known exactly. That one update reduces the pose error from 0.289 m to 0.199 m, and the following updates briefly bring it below 1 cm. The error then grows again on the second lap and ends at 0.135 m. This is loop closure. Through the cross terms of Σ\Sigma, the correction also moves every landmark mapped during the lap. The LIDAR sees about three pillars at all times, so its error stays below 10 cm. It closes the loop too, at 57.8 s, and its error drops from 0.085 m to 0.026 m. In both runs the shaded 2σ2\sigma band follows the error: it grows while the robot explores and shrinks at the closure. Output from cell 16 The correlation matrix of the camera run’s final belief shows the structure Thrun, Burgard and Fox describe in section 10.3.2. Rows and columns are the pose followed by the xx and yy coordinates of each landmark. Many entries are close to +1+1 or −1-1: the landmark estimates have become strongly correlated with each other. The map is well known in relative terms, how far each pillar is from the others, while the whole map can still shift together by an amount that no measurement can remove. Every landmark is also correlated with the pose. Output from cell 17 The normalized estimation error squared (μt−st)⊤Σt,ss−1(μt−st)(\mu_t - s_t)^\top \Sigma_{t,ss}^{-1} (\mu_t - s_t) of the pose checks whether the filter’s covariance matches its actual error. For a consistent filter it follows a χ2\chi^2 distribution with 3 degrees of freedom, so 95% of the steps should fall between the dashed lines. The LIDAR run has 97% of its steps inside with a mean of 3.1, and the camera run 99% with a mean of 3.0. The dips of the LIDAR curve below the lower line mark the stretches right after its loop closure, when the error is smaller than the covariance implies. A single run may not be representative, so the code below repeats both runs for ten seeds of the wheel and sensor noise.
In all twenty runs the filter finds exactly eight landmarks and makes no wrong association. The final error stays below 0.27 m, against more than a meter of dead reckoning in the run above.

Limits of EKF SLAM

The covariance has (3+2N)2(3 + 2N)^2 entries, and every update touches all of them, so each step costs O(N2)O(N^2). A few hundred landmarks is the practical limit. Maximum likelihood correspondence has a second weakness: it selects one assignment and never reconsiders it, so a single wrong association stays in the map permanently. Thrun, Burgard and Fox (section 10.3.3) list the usual defenses: a provisional list where new landmarks wait until they have been seen several times, a counter that removes landmarks the robot should see but does not, and landmarks spaced far enough apart that they cannot be confused. The pillars here are 2.1 m apart and are seen within 2.5 m, so the association is easy. In this section the LIDAR and the camera only produce landmarks for the same filter. A camera can also estimate its own motion without any depth, from how image features move, and that is what the visual-inertial odometry and visual SLAM sections build on. Visual SLAM systems also replace the single growing filter with a graph of keyframes optimized in batches, which removes the O(N2)O(N^2) limit.

Lectures

Key references: (Burgard & Thrun, n.d.; Corke, 2023)

References

  • Burgard, W., Thrun, S. (n.d.). Probabilistic Robotics.
  • Corke, P. (2023). Robotics, vision and control: Fundamental algorithms in python.

Found an error on this page? Report an issue.