Tutorial · Intermediate · 20 min read
Differential-Drive Odometry from Wheel Encoders
Calculate a differential-drive robot's position from left and right encoder motion, handle straight and curved travel, and measure odometry drift.
Published
A differential-drive robot has two independently driven wheels separated by a track width. When both wheels move the same distance, the robot travels straight. When one travels farther, the chassis follows an arc. Odometry integrates those small motions over time to estimate pose: horizontal position x, vertical position y, and heading θ.
The estimate is local and cumulative. It starts from a chosen pose and cannot correct itself from wheel encoders alone. Small wheel-diameter, track-width, timing, and slip errors accumulate, so the goal is a useful short-term estimate with measured limits—not perfect ground truth.
Define one coordinate convention
Choose a frame before writing equations. A common two-dimensional convention is:
xpoints forward from the starting pose.ypoints left.- positive
θturns counter-clockwise. - left and right wheel distance are positive when the robot drives forward.
Store angles in radians internally. Convert to degrees only for display. Normalize heading if bounded values help debugging, but sine and cosine work with unbounded angles too.
Let dL and dR be the left and right wheel distances since the previous update, and b the effective distance between the wheel contact lines.
Calculate forward and angular motion
The center of the axle moves by the average wheel distance:
dCenter = (dR + dL) / 2
The heading changes according to their difference:
dTheta = (dR - dL) / b
Equal distances make dTheta = 0. Equal and opposite distances make dCenter = 0, so the robot rotates in place around the axle midpoint.
For small updates, applying translation at the midpoint heading is accurate and avoids a straight-line special case:
headingMid = theta + dTheta / 2
x = x + dCenter * cos(headingMid)
y = y + dCenter * sin(headingMid)
theta = theta + dTheta
This midpoint integration is simple enough for an embedded controller and behaves well when the loop runs frequently. An exact arc integration is useful for large steps, but it does not repair wrong wheel measurements.
Implement a repeatable update
Keep raw encoder totals and convert their differences to metres. Do not reset hardware counters on every loop; snapshots make rollover and diagnostics easier.
struct Pose { float x, y, theta; };
void updateOdometry(Pose& pose, float dLeft, float dRight, float trackWidth) {
const float dCenter = 0.5f * (dLeft + dRight);
const float dTheta = (dRight - dLeft) / trackWidth;
const float headingMid = pose.theta + 0.5f * dTheta;
pose.x += dCenter * cosf(headingMid);
pose.y += dCenter * sinf(headingMid);
pose.theta += dTheta;
}
Call this after atomically reading both encoders. The left and right snapshots should represent nearly the same instant; a long gap between them creates apparent steering during rapid motion.
If another subsystem needs velocity, divide the increments by the measured elapsed time. Pose integration itself uses distance, so loop-time jitter does not directly change the travelled distance estimate.
Work through one turn
Suppose the left wheel moves 0.20 m, the right moves 0.30 m, and track width is 0.25 m:
dCenter = 0.25 mdTheta = (0.30 - 0.20) / 0.25 = 0.4 rad- from heading zero, midpoint heading is
0.2 rad dx ≈ 0.25 cos(0.2) = 0.245 mdy ≈ 0.25 sin(0.2) = 0.050 m
The robot ends slightly left of its original forward axis and rotated about 22.9°. Checking a hand calculation like this catches sign and unit errors before the code reaches hardware.
Calibrate the two dominant parameters
Effective wheel diameter controls distance scale. Drive a long measured straight line, compare reported and actual distance, and multiply the diameter scale by actual / reported. Calibrate each side if the robot curves despite equal commands.
Effective track width controls rotation scale. Command several full in-place rotations, measure actual heading change with an external reference, and adjust track width. Multiple turns magnify the error and make measurement easier. Tire scrub means the effective value may differ from ruler-measured wheel spacing.
Keep both parameters configurable. Surface, tire compression, payload, and wear can change them; hardware models need calibration knobs.
Add a gyro without confusing the frames
A gyro often provides a better short-term heading change than the difference between wheel encoders. You can use gyro heading for theta while still using average wheel distance for translation. Apply translation at the average of old and new gyro headings.
That does not make position absolute. Gyros have bias and wheel distance still slips. A global sensor—camera landmarks, lidar localization, or another external reference—is needed to correct accumulated pose.
In ROS terminology, a continuous local estimate belongs naturally in the odom frame. A localization system may update map → odom without forcing the locally smooth odom → base_link transform to jump.
Measure drift instead of hiding it
Run three repeatable tests:
- Drive forward and backward to the start.
- Rotate several turns and return to the original heading.
- Drive a large square and compare the final pose with the starting mark.
Log encoder deltas, computed pose, battery voltage, and surface. Repeat each test in both directions. Consistent scale error suggests calibration; error that changes with direction suggests backlash or asymmetry; error that changes by surface suggests slip.
Diagnose common odometry failures
- The robot turns left but heading decreases: swap the heading sign or wheel order once at the boundary.
- Straight motion changes heading: wheel scales differ, a count is missed, or snapshots are not synchronized.
- Rotation is consistently too large: effective track width is too small.
- A square never closes despite calibration: wheel slip and integration error are accumulating; add external correction.
- Position jumps after counter rollover: calculate count deltas with deliberate fixed-width modular arithmetic.
- The estimate looks perfect in simulation only: the model lacks tire deformation, uneven floors, backlash, and sensor timing.
Try the steering consequences in the Line Follower Simulator and compare local motion estimates with the mapping concepts in SLAM fundamentals.
Further reading