Odometry
How dead-reckoning lets the robot know its absolute X, Y position and heading—and why it drifts over time.
What Is Odometry?
Odometry is the process of estimating a robot's current position by integrating the movement it has measured so far. Think of it like counting your steps: if you know you started at point A and you've taken 50 steps north, you can estimate your current location.
On a VEX robot, the "steps" are measured by wheel encoders (how far each wheel has rotated) and an IMU (which direction the robot is facing). Each time the odometry loop runs (~1 ms), it calculates a tiny change in position (Δx, Δy) and adds it to the accumulated pose.
The Field Coordinate System
LemLib uses inches for distance and degrees for heading.
When you call chassis.setPose(0, 0, 0) in initialize(),
you tell the robot "I am at the origin, facing North."
All subsequent moves are relative to this starting pose.
How Dead-Reckoning Works
At every odometry update, LemLib reads the encoder(s) and IMU and computes:
Using the midpoint heading (heading + Δheading/2) reduces the error
when the robot is curving. This is the standard arc-integration approach.
Sources of Drift
Dead-reckoning is inherently accumulative: every small error adds to the total error. In a 60-second skills run the robot can drift 1–2 inches even with a good tracking wheel. Sources include:
| Source | Effect |
|---|---|
| Wheel slip (acceleration/braking) | Encoder reads more/less than actual distance |
| Tracking wheel bouncing on seams | Random noise in encoder readings |
| IMU drift (vibration, temperature) | Heading slowly rotates away from true north |
| Incorrect wheel diameter | Systematic scaling error (every move is X% off) |
| Incorrect track width | Systematic heading error (every turn is X% off) |
Sensors LemLib Uses
You configure sensors in lemlib::OdomSensors:
src/robot.cpplemlib::OdomSensors sensors( &verticalTracker, // Vertical tracking wheel (most important) nullptr, // 2nd vertical tracker (optional) nullptr, // Horizontal tracking wheel nullptr, // 2nd horizontal tracker &inertial // IMU (primary heading source) );
Tracking Wheel vs. Drive Encoders
This robot uses a dedicated tracking wheel (a small unpowered wheel with a rotation sensor) rather than reading encoder values from the drive motors. Here is why:
- Drive wheels slip when motors accelerate or brake under load
- If the robot is pushed by a defense robot, drive encoders count the push; a tracking wheel does not
- Drive motors can be stalled (e.g., against a wall), giving false zero-movement readings
The tracking wheel is mounted close to the center of rotation with 0" lateral offset in this config, minimizing systematic error from turning.
Measuring Your Wheel Diameter
Getting the wheel diameter right is critical. Even a 0.1" error causes every move to be slightly wrong. The best method:
-
Place the robot at a known starting mark. Reset the encoder to zero.
-
Push the robot exactly 48 inches (use a measuring tape on the floor).
-
Read the raw encoder value (in degrees or ticks).
-
Calculate:
diameter = (2 × 48 × 360) / encoder_degrees
(for a rotation sensor that reports in degrees) -
Update the value in
lemlib::TrackingWheeland re-test.
Calibrating Track Width
Track width affects how accurately the robot turns. If the robot commands a 90° turn but ends up at 92°, your track width is wrong.
-
Mark the robot's starting heading (use a ruler against the wall for precision).
-
Command 10 × 360° turns (3600° total). Note the actual final heading with a protractor or IMU.
-
Scale the track width:
new_track_width = old_track_width × (3600 / actual_degrees)