Reading and display settings

Appearance

System follows your operating system and keeps following it, even if you change it later. The header's sun, moon and monitor cycle the same three options.

Text size (%) 100%

Default. Scales every text size on the site, equations and tables included.

Reading width 70ch

How much text runs across one line of prose. Narrower is easier to track; wider fits more on screen.

Line spacing 1.6

The leading on body text. Taller leading helps a tired eye stay on the line.

Density

Padding and gaps around controls, cards, and tables — how much breathing room the layout leaves itself.

Motion

System follows your operating system. Reduced removes every transition on this site. Full keeps them on unless your system asks for less.

0

What's new: the map is unknown too

Recap

Before: unknown = robot pose (x, y, θ), landmarks L given. Now: unknown = robot pose and every landmark position. With M unknown landmarks, the state grows from 3 numbers to:

state = (x, y, θ, Lx₁, Ly₁, Lx₂, Ly₂, …, Lxᴸ, Lyᴸ)    3 + 2M unknowns

Every observation still produces the same range and bearing residuals from parts 1–2. The only change is that a residual can now depend on landmark unknowns as well as pose unknowns — so each observation contributes a Jacobian row with a couple of nonzero entries for the pose, a couple more for whichever landmark it's looking at, and zeros everywhere else. Bigger state, same update rule.

💡 The one genuinely new idea: if everything — robot and map alike — is unknown, the whole picture can float. Before touching the optimizer, we need to understand and fix that.
1

Gauge freedom: the whole map can float

A new kind of ambiguity

🎯 Learning goal: range and bearing only ever measure relative geometry — how far and which way, from the robot to a landmark. Slide, or spin, the robot and every landmark together by the exact same amount, and not one measurement changes.

Drag the sliders below. They shift and rotate the entire configuration — robot and both landmarks — together, rigidly. Watch the cost: it stays at zero no matter where you drag it to, because every relative range and bearing is exactly preserved. There is no "correct" absolute position and orientation to converge to — only a correct shape, floating freely in the plane.

Original configuration Shifted / rotated
2

Anchoring: pinning the map down

The fix

🎯 Learning goal: declare at least one landmark's position (or the robot's starting pose) known, by fiat. That single fixed reference removes the sliding-and-spinning freedom entirely.

Same sliders, same scene — but now the diamond landmark on the left is treated as a fixed, known anchor. Its true position is pinned on the canvas. As soon as you shift or rotate the scene, the anchor landmark visibly leaves its pin, and a second cost term — how far the anchor has drifted from where it's supposed to be — climbs away from zero. That term is what turns "any shift is equally good" back into an ordinary bowl with one correct answer.

📌 anchor's true (pinned) position anchor landmark
⚠️ In practice: real SLAM systems anchor the very first pose of the trajectory as the origin of the whole map — everything else, including every landmark, is estimated relative to that first fixed reference.
3

Warm-up: locating one landmark from a known pose

Landmark-only unknowns

🎯 Learning goal: if the robot's own pose is already known, finding an unknown landmark is the easy direction of the same equations. One range+bearing observation pins it down exactly — two independent, noisy observations, combined with least-squares, pin it down better.

The robot visits two known positions (shown as gray triangles) and takes one noisy range+bearing reading of the same unknown landmark from each. A single reading alone would place the landmark exactly at the tip of its own measurement — no fitting required. But with two independent noisy readings disagreeing slightly, there's no single point that satisfies both exactly, so we're back to minimizing a sum of squared residuals — the same machinery as parts 1–2, just with a landmark's (Lx, Ly) as the only unknowns instead of the robot's pose.

Click the map to set a starting guess for the landmark.

▲ known robot viewpoints Gauss-Newton estimate ★ true landmark
4

Joint estimation: pose and map together

Gauss-Newton, now sparse

🎯 Learning goal: solve for the robot's pose and two unknown landmarks in one Gauss-Newton solve — a 7-unknown problem — anchored by two known landmarks so the answer is unique. The Hessian this produces has a very particular, exploitable shape.

Two known anchors (diamonds) plus two unknown landmarks (circles) — the robot observes all four. Step through Gauss-Newton and watch the pose and both unknown landmark estimates converge together. To the right, the block structure of the Hessian JᵀJ for this exact problem: the pose interacts with every landmark it observes (dense blocks along the top row and left column), but two different landmarks never interact with each other directly — only through the shared pose. That empty block is sparsity, and it's the reason real SLAM systems with thousands of landmarks are solvable at all.

Click the map to set the robot's starting position.

JᵀJ block structure (7×7: pose, landmark 1, landmark 2)

5

Levenberg-Marquardt on the joint problem

Same damping trick, bigger state

🎯 Learning goal: nothing about damping cares how large the state is — λI just grows to match. Try a starting guess that's badly wrong about both the robot's pose and the landmarks at once.

Same anchored scenario, but start from a rough, hand-wavy guess for everything — robot pose and both unknown landmarks placed more or less arbitrarily. Plain Gauss-Newton can thrash around before settling; adaptive λ tames the early, unreliable steps and speeds up once the estimate is closer to correct.

Click the map to set the robot's starting position.

6

Playground: build the map and localize at once

Put it all together

Two known anchors, three unknown landmarks, one robot pose — all estimated jointly. Race gradient descent, Gauss-Newton, and Levenberg-Marquardt (full Newton is skipped here on purpose: with landmarks in the mix too, hand-deriving every curvature term gets unwieldy fast — exactly why Gauss-Newton, not Newton, is the real-world default for SLAM-shaped problems).

⚠️ Try dragging the starting guess somewhere wild: with this many unknowns tangled together, the cost surface has more than one valley. A bad enough starting guess can make every method settle into a self-consistent but wrong answer — small residuals, zero gradient, still off from the true pose and map. It's a real failure mode of joint SLAM, not a bug in this demo, and it's exactly why a good initial guess (e.g. from odometry) matters so much in practice.

Click the map to set the shared starting position.

Gradient descent Gauss-Newton Levenberg-Marquardt
MethodItersPos. errMap errStatus
✓

Cheat sheet

Recap

ConceptWhat changed from part 2
UnknownPose (x, y, θ) → pose plus every unknown landmark's (Lx, Ly)
New ambiguityGauge freedom — the whole map can slide and spin with zero cost change
FixAnchor at least one landmark (or the first pose) as a known reference
Jacobian rowsNonzero for the pose and whichever landmark that observation is looking at; zero elsewhere
Hessian shapeNo longer dense — landmark-landmark blocks are zero unless a robot pose observed both; this sparsity is what makes large SLAM problems tractable
Update rulesIdentical in form to parts 1–2, just over a bigger state vector

This is genuinely how SLAM back-ends work, just at a scale of thousands of landmarks and thousands of poses instead of a handful: a giant, extremely sparse least-squares problem, solved with Gauss-Newton or Levenberg-Marquardt, using sparse linear algebra to exploit exactly the block structure shown above. From here, two directions extend this further — chaining many robot poses together over time with odometry between them (a full pose graph, rather than a single anchored moment), and moving from a flat 2D plane to full 3D rotations.

So far every estimate was for a single moment. What happens once the robot starts moving? Continue: pose graphs & loop closure →