← All lessons
Odometry · pose · dead reckoning · WPILib

Count your steps.

There's no GPS under the field carpet. A robot that wants to know where it is must do what sailors did for centuries: know exactly where it started, then carefully add up every step since. That bookkeeping is odometry — and its arch-enemy is a wheel that lies.

where it really is (truth) where it thinks it is (odometry) gyro / heading wheels & encoders
Part 1 / The oldest trick in navigation

Start known. Add up every move.

Navigators call this idea dead reckoning: no outside references, just your last known position plus everything you've done since. On a robot, the "everything you've done" comes from wheel encoders and a gyro, and the bookkeeping produces a pose — the robot's full answer to "where am I":

pose (x, y, θ)
x, y — the robot's position on the field, in meters, in a fixed field frameθ — the robot's heading ("theta"): which way it faces, measured counter-clockwisePosition alone isn't enough — a robot at the right (x, y) facing the wrong way scores exactly zero points. Pose is position plus heading, always.

And here is the whole promise and the whole problem, in one picture. This robot drives a square with its eyes closed — no camera, no beacons — while odometry counts its steps. The wheels lie a little (they always do), and every lie is added into the running total, forever:

Widget 00 — The blind squaretruth vs bookkeeping
Plot — position error vs distance traveled

The gap between truth and estimate, plotted against total distance driven. Look where the jumps happen: at the corners, where the sharp direction change scrubs the wheels for an instant. Between corners the wheel-calibration errors gnaw slowly. Either way the total only ever ratchets upward — odometry has no mechanism for forgetting a mistake.

Position error
—
Distance driven
—

Watch a lap or two: the violet estimate starts glued to the dashed truth, then peels away a few centimeters per lap. Nothing dramatic went wrong — the wheels are fractionally miscalibrated and every sharp corner scrubs them for a blink. Dead reckoning never forgets a mistake; it only accumulates. Open the plot below and watch where the error jumps.

Part 2 / One wheel first

What an encoder actually knows

Strip it to one wheel. An encoder counts how much the wheel shaft has turned — thousands of ticks per revolution. Multiply by the wheel's circumference and you get distance:

distance d = rotations × π·D
d — how far the wheel has rolled (m)rotations — wheel revolutions counted by the encoder (ticks ÷ ticks-per-revolution, through the gear ratio)π·D — the wheel's circumference: distance covered per revolution, from diameter DSpot the fine print: the encoder measures how much the wheel turned, then this formula assumes the wheel gripped the floor the whole time. Every time it spins without gripping — slip, scrub, getting shoved — the assumption is false and the bookkeeping quietly absorbs a lie.
Widget 01 — The honest rulerencoder vs ground truth
Encoder says
—
Actually moved
—
The lie
—

Experiments: (1) Slip at 0% — the wheel lays down perfectly even tread marks, one per eighth-turn, and encoder and tape measure agree to the millimeter. Encoders are genuinely superb sensors. (2) Slip at 15% — now watch the ground: the tread marks bunch closer together and an orange skid streak appears, because the wheel is turning more than it travels. The encoder counts the turning, so it over-reports — the violet ghost is where the bookkeeping believes the wheel is. (3) Crank it to 30% — this is what a push match against another robot looks like to your odometry: tread marks piling up, robot barely moving, estimate marching confidently away.

Part 3 / Heading is sacred

A small angle is a big lie

Distance errors add up politely — centimeter here, centimeter there. Heading errors are different: a heading error rotates every meter you drive afterwards. Get θ wrong by a few degrees and your position error grows with every step, even if the wheels count perfectly:

the miss miss = d · sin(φ)
miss — how far sideways you end up from where you think you are (m)d — distance driven while the heading was wrong (m)φ — the heading error ("phi"), the angle between where you think you're pointed and where you're actually pointedNumbers to feel: 5° of heading error costs 8.7 cm of position error per meter driven. Drive a 10 m auto path with 5° of error and you arrive nearly a meter off. This is why heading gets its own dedicated sensor.
Widget 02 — The protractor taxheading error × distance
Distance driven
—
The miss
—
Miss per meter
—

Set φ = 5° and watch a 4 m drive: the miss readout lands at ~35 cm — 8.7 cm for each meter, exactly sin(5°). Now try φ = 2°, a heading error you'd barely notice by eye: still 3.5 cm per meter. The lesson robots take from this: never derive heading from the wheels (every scrub corrupts it) — carry a gyro, which measures rotation directly and holds to a fraction of a degree over a match. WPILib's odometry classes won't even let you skip it: the gyro angle is a required argument.

Part 4 / The full loop

Swerve odometry: vote, rotate, accumulate

Now assemble the real thing. Fifty times a second, each swerve module reports its distance rolled and its steering angle. From there it's three moves — and the first one you already own: it's the forward kinematics vote from the kinematics lesson, run on distances instead of speeds:

1 vote (Δxr, Δyr) = forward_kinematics(module Δdistances, angles)
2 rotate Δx = Δxr·cos θ − Δyr·sin θ   Δy = Δxr·sin θ + Δyr·cos θ
3 add x ← x + Δx   y ← y + Δy   θ ← gyro
module Δdistances — how far each wheel rolled this tick, from its encoder(Δxr, Δyr) — the chassis's step this tick, in its own robot frame: the wheels' voted answerθ — the heading, taken from the gyro (Part 3 explained why it's never taken from the wheels)(Δx, Δy) — the same step, rotated into the fixed field frame so steps can be added across headingsThat's all odometry is: a tiny step measured in the robot's frame, rotated into the field's frame, added to the running total. Repeat forever.

Drive it. And then do what match play does — get bumped, get pinned against a wall — and watch what each one does to the bookkeeping:

Widget 03 — Truth vs bookkeepingfull swerve odometry

Drive it with your keyboard — WASD to translate (robot-relative), Q / E to spin — or with the sliders below. Keys work while the widget is on screen.

Plot — position error vs time

Clean driving: a slow patient creep. Each shove: an instant staircase jump that never comes back down — odometry has no mechanism for noticing it was moved. Each reset: the error snaps to zero and the accumulation starts over.

Position error
—
Truth (x, y)
—
Estimate (x, y)
—

Experiments: (1) Drive gently and watch the error readout creep — that's the Widget 00 tax. (2) Press Bump! a couple of times: another robot just shoved you; your wheels never turned, so the estimate stands still while the truth jumps — and the offset is permanent. (3) Drive straight into a wall and keep pushing: now it's reversed — the wheels spin and scrub while the truth stops, so the estimate happily drives through the wall. (If it sails clean off the map it pins to the edge of the view with an arrow, and the error readout keeps counting.) (4) Press Reset odometry — with a trusted position (a camera fix, a known starting spot), the bookkeeping snaps back to truth and starts fresh. Notice what that button needs: information from outside the wheels. (5) Toggle the defense bot on and just try to drive your route: every hit staircases the error while your wheels swear nothing happened. A full match of that treatment is exactly why serious teams bolt on a camera and fuse.

Part 5 / WPILib & what comes next

Drift is the deal you signed

So odometry is a bargain: silky-smooth, 50-times-a-second position — in exchange for error that only ever grows. During autonomous, with no contact, it's superb. In a match, every bump and pin and push moves the truth without moving the books. In WPILib the whole machine is two calls, and note the gyro angle riding along in both:

odometry = new SwerveDriveOdometry(kinematics, gyro.getRotation2d(), positions, startPose);
pose = odometry.update(gyro.getRotation2d(), positions);
SwerveDriveOdometry — the bookkeeping object; it needs your kinematics (the vote), the gyro, and where you startedpositions — an array of SwerveModulePosition: each module's total distance rolled (m) and current angleupdate(...) — steps 1-2-3 from Part 4; call it every loop and it returns the current poseresetPosition(...) — the "Reset odometry" button: must be called whenever you reset encoders or the gyro, or learn your true pose from outsideDocs: Swerve Drive Odometry. WPILib's own accuracy note says it plainly: estimates drift over time, "especially as your robot comes into contact with other robots" — you just watched exactly that in Widget 03.

And the ending you can now see coming: a sensor that's smooth-but-drifting, plus an occasional absolute fix from a camera spotting an AprilTag — two imperfect answers begging to be fused. That is precisely the sensor-fusion story of the next lesson, and WPILib ships it ready-made as SwerveDrivePoseEstimator — a drop-in upgrade to the odometry class that accepts vision measurements and weighs them against the wheel data. Odometry counts the steps; fusion keeps the count honest. (For the mathematics under all of it, the Controls Engineering in FRC textbook is the deep end.)

Part 6 / Recap

Five ideas to walk away with

1. Odometry is dead reckoning with receipts. Known start, plus every measured step since, added up 50 times a second into a pose (x, y, θ).

2. Encoders measure the wheel, not the world. The math assumes grip; every slip, scrub, and shove is a lie absorbed silently into the total.

3. Heading is sacred. Angle errors multiply by every meter you drive — 5° costs 8.7 cm per meter — so heading comes from the gyro, never from the wheels.

4. Swerve odometry is three moves. The wheels vote (forward kinematics), the step rotates into the field frame, the total accumulates. You already knew move one.

5. Drift is structural, and the fix comes from outside. Wheel bookkeeping can only accumulate; only an absolute reference — a camera, a known pose — can correct it. Fusing the two is the whole job of the next lesson.

odometry — tracking pose by accumulating measured wheel motion
pose — position plus heading: (x, y, θ)
dead reckoning — known start + sum of every move since, no outside references
encoder — counts shaft rotation; distance follows only if the wheel gripped
gyro — measures rotation directly; the trusted source for θ
slip / scrub — wheel motion that isn't robot motion; odometry's poison
drift — accumulated estimate error; grows with distance and contact
pose estimator — odometry + absolute fixes, fused Kalman-style
Pocket glossary — the whole page, one line each. Further reading: WPILib swerve odometry and the Controls Engineering in FRC textbook.