Mobile Robot Kinematics and Odometry

Differential, Ackermann and mecanum drives, odometry from encoders, how it drifts, calibration and fusing a gyroscope.

Drive types

DriveHow it steersHolonomic?StrengthsLimits
Differential (two driven wheels + caster)Speed difference between the two wheelsNoSimple, spins on the spot, cheapCaster drag; drifts straight-line if wheels differ
Skid steer / trackedSame as differential, with slipNoRugged, rough terrain, good tractionLarge slip: odometry is poor
Ackermann (car-like)Steered front wheels, driven rearNoStable at speed, outdoorsCannot turn on the spot; needs a turning radius
MecanumFour angled-roller wheels give sideways motionYesMoves in any direction, spins freelySlips on rollers, needs flat floors, odometry drifts
Omni (three or four omni wheels)Free-rolling rollers at right anglesYesFast changes of directionLow traction; rollers wear
SwerveEach wheel steered and driven independentlyYesBest manoeuvrability and tractionComplex and costly

A holonomic base can move in any direction without first turning; a non-holonomic one (differential, Ackermann) cannot move sideways, so path planners must respect that (the plans are curves with a minimum radius, or turn-then-drive).

Differential drive

A differential-drive robot has two driven wheels on one axle, each with its own motor, and a caster for balance. Steering comes from the wheel speed difference. With wheel speeds vl and vr (m/s) and a track width L (distance between the wheels):

v = (vr + vl) ÷ 2    ω = (vr − vl) ÷ L    R = v ÷ ω

vr = v + ω L ÷ 2    vl = v − ω L ÷ 2    (the inverse: wheel speeds from a velocity command)

ICCR = 0.30 mleftrightv = 0.6 m/sTrack width L = 0.2 mLeft wheel v_l = 0.4 m/sRight wheel v_r = 0.8 m/sv = (v_r + v_l) ÷ 2 = 0.60 m/sω = (v_r − v_l) ÷ L = 2.00 rad/sR = v ÷ ω = 0.30 mThe faster wheel is on the outsideof the turn, so the robot turns left.ICC = instantaneous center of rotation
A differential drive turning left. The wheels trace circles about the same center of rotation, with the outer wheel faster in proportion to its radius.

Worked example: speeds and turning

  • Wheel speed from rpm. A 100 mm wheel at 120 rpm turns 2 revolutions per second: v = π × 0.1 × 2 = 0.628 m/s.
  • Turning. With L = 0.2 m, vl = 0.4 and vr = 0.8 m/s: v = 0.60 m/s, ω = (0.8 − 0.4) ÷ 0.2 = 2.00 rad/s (115° per second), and the turning radius R = 0.60 ÷ 2.00 = 0.30 m. The left wheel runs on radius R − L/2 = 0.20 m and the right on 0.40 m: speeds 0.40 and 0.80 m/s ✓.
  • From a command to wheel speeds. /cmd_vel asks for v = 0.5 m/s and ω = 1.0 rad/s: vl = 0.5 − 1.0 × 0.1 = 0.40 m/s and vr = 0.60 m/s; on 100 mm wheels that is 76 and 115 rpm.
  • Spin on the spot. If vr = −vl, then v = 0 and the robot turns about its own center with ω = 2 vr ÷ L: wheels at ±0.30 m/s give 3.0 rad/s (172°/s).
def wheel_speeds(v, w, track):              # command -> wheel speeds (m/s)
    return v - w * track / 2, v + w * track / 2

def body_velocity(vl, vr, track):           # wheel speeds -> (v, w)
    return (vr + vl) / 2, (vr - vl) / track

wheel_speeds(0.5, 1.0, 0.2)                 # (0.40, 0.60)

Ackermann, mecanum and others

Worked example: an Ackermann robot

  • A car-like robot with a wheelbase W = 0.30 m and a track of 0.20 m steers by an angle δ at the front: the turning radius (of the rear axle center) is R = W ÷ tan δ. With a maximum steering angle of 30°, Rmin = 0.30 ÷ tan 30° = 0.52 m: it cannot turn tighter, so a planner must use curves at least that wide (and a 3-point turn to reverse).
  • Inner and outer front wheels turn by different angles so that they roll without slip about one center: inner δi = atan(W ÷ (R − T/2)) = 35.6°, outer δo = atan(W ÷ (R + T/2)) = 25.8°. A steering linkage with Ackermann geometry does this; a single servo steering both wheels equally scrubs the tires.
  • Speed: v is the speed of the rear axle center; ω = v ÷ R = v tan δ ÷ W.

Worked example: a mecanum base

Four mecanum wheels (radius 0.05 m) at ±0.15 m and ±0.12 m from the center can move in any direction. For a body velocity (vx, vy, ω) the wheel angular speeds (front-left, front-right, rear-left, rear-right) are ωi = (vx ∓ vy ∓ (lx + ly) ω) ÷ r. For vx = 0.4 m/s forward, vy = 0.2 m/s sideways and ω = 0.5 rad/s, with lx + ly = 0.27 m: front-left 1.3, front-right 14.7, rear-left 9.3, rear-right 6.7 rad/s. Pure sideways motion (vy only) drives the diagonal pairs in opposite directions; the rollers slip, so odometry is poor and good floors matter.

Wheel encoders

Encoders on the wheel or motor shaft count rotation (see Sensors and Field Devices and Motion Control and Actuators). A quadrature encoder of 360 pulses per revolution gives 4 × 360 = 1 440 counts per turn. On a 100 mm wheel (circumference 314.16 mm) one count is π × 100 ÷ 1 440 = 0.2182 mm.

Worked example: count rates

At 1.0 m/s the counts arrive at 1.0 ÷ 0.000218 m = 4 584 counts/s; a 50 Hz loop sees about 92 counts per cycle, so the speed resolution is one count per cycle = 0.011 m/s: coarse at low speed (a loop-rate-limited estimate is noisy; estimate speed from the time between counts, or use a higher-resolution encoder or a faster loop). If the encoder is on the motor before a 20:1 gearbox, the counts per wheel turn are 20 times larger.

  • Count in hardware (a timer in encoder mode, or an interrupt on a microcontroller) so that no counts are lost at high speed (see Embedded Systems).
  • Use differences of the counter, with the wrap-around handled by unsigned arithmetic.
  • Speed from counts: over a fixed interval (good at high speed) or from the interval between counts (good at low speed); filter lightly (see Filters).

Odometry: dead reckoning

Odometry estimates the robot’s pose by adding up wheel movement. Each cycle, with Δsl and Δsr the distances the wheels rolled:

Δs = (Δsr + Δsl) ÷ 2    Δθ = (Δsr − Δsl) ÷ L    x += Δs cos(θ + Δθ/2)    y += Δs sin(θ + Δθ/2)    θ += Δθ

Worked example: three odometry updates

Track width L = 0.2 m, starting at the origin facing +x. Wheel distances Δsl / Δsr (m) for three cycles:

CycleΔsl / ΔsrΔsΔθ (rad)Position (x, y)Heading
10.10 / 0.100.1000.000(0.100, 0.000)0.0°
20.08 / 0.120.1000.200(0.200, 0.010)11.5°
30.12 / 0.080.100-0.200(0.299, 0.020)0.0°

The first cycle drives straight (equal distances, Δθ = 0); the second turns left (the right wheel travels further), the third turns back. Note the midpoint angle θ + Δθ/2 in the update: it is a much better approximation than using the old heading.

import math
class Odometry:
    def __init__(self, track):
        self.track, self.x, self.y, self.th = track, 0.0, 0.0, 0.0
    def update(self, dsl, dsr):                       # distances rolled since the last update (m)
        ds  = (dsr + dsl) / 2
        dth = (dsr - dsl) / self.track
        self.x  += ds * math.cos(self.th + dth / 2)
        self.y  += ds * math.sin(self.th + dth / 2)
        self.th  = (self.th + dth + math.pi) % (2 * math.pi) - math.pi      # keep within -pi..pi
        return self.x, self.y, self.th

How odometry drifts

Odometry drift on a 2 m square (the same commands, small calibration errors)00.511.522.500.511.522.5x (m)y (m)ideal squareleft wheel diameter 0.5 % largetrack width 1 % largeEnd point error after 8 m of travel:wheel error: 0.27 m, heading 349.4°track error: 0.09 m, heading 356.4°
The same commands to drive a 2 m square, with two small calibration errors: the robot ends far from where it started.

Worked example: a 0.5 % error

  • The robot is commanded to drive a 2 m square (8 m of travel). If the left wheel diameter is 0.5 % larger than the software assumes, the left wheel rolls 0.5 % further on every meter. The heading error grows as Δθ = ε s ÷ L: over one 2 m side, 0.005 × 2.0 ÷ 0.2 = 0.050 rad (2.9°), and the sideways error at the end of the side is about ε s² ÷ (2 L) = 0.05 m.
  • After the whole square, the simulation above ends 0.27 m from the start with that one wheel error, and 0.09 m with a 1 % error in the track width (every turn is 1 % too large or small). With a 0.2 m track the robot is very sensitive, because the heading changes by ε ÷ L per meter.
  • The errors do not average out: a systematic error (wheel size, track width) grows in proportion to the distance; a random one (slip, floor) as its square root; and heading errors matter most, because they turn into position errors in proportion to the distance traveled afterwards.
  • Rule of thumb: without correction, wheel odometry is good to about 1 to 5 % of distance traveled on a floor, and a few degrees per turn.
Error sourceEffectCorrect by
Wheel diameter error (unequal wheels)Curved path on a straight commandCalibrate each wheel diameter
Track width errorTurns too large or smallCalibrate by spinning a known number of turns
Wheel slip and skiddingCounts do not match travelGentle accelerations; a gyroscope; vision or lidar
Floor irregularitiesRandom errorsAbsolute corrections from a map
Encoder resolution and samplingQuantization noiseHigher resolution; update fast
Wheel compression and loadEffective diameter changes with weight and tire pressureCalibrate loaded
Time delaysPose is for a moment earlierTime-stamp every update at the encoder reading

Calibrating the wheels

Worked example: two quick tests

  • Straight-line test: command 2.000 m forward and measure with a tape: the robot actually moved 1.972 m. The odometry is high by 1.014: multiply the wheel diameter by 0.9860 (a 1.40 % error). Repeat in both directions and average.
  • Rotation test: command 10 full turns on the spot and mark the heading; it actually turned only 9.72 turns. The effective track is wider than assumed: Lnew = L × commanded ÷ measured = 0.20 × 10 ÷ 9.72 = 0.2058 m (2.9 % larger).
  • Bidirectional square test (UMBmark): drive a square clockwise and counter-clockwise; the two end-point errors separate the wheel-diameter and track-width errors, so both can be corrected.
  • Do it loaded, on the real floor, at the speed you use, and repeat after changing the tires or the payload.

Fusing a gyroscope and other sensors

Heading is the quantity odometry gets worst, and a gyroscope measures it directly. A MEMS gyroscope has no slip, but a small constant bias that integrates into drift. The two have opposite weaknesses, so combine them.

Worked example: gyro drift and a complementary filter

  • A cheap gyroscope with a bias of 0.1 °/s drifts by 0.1 × 60 = 6° in a minute if integrated alone: useless over a long run, excellent over a few seconds.
  • A complementary filter trusts the gyro in the short term and the wheel (or compass) heading in the long term: θ ← α (θ + ω Δt) + (1 − α) θwheel. With α = 0.98 and Δt = 0.01 s the crossover time constant is α Δt ÷ (1 − α) = 0.5 s: faster changes come from the gyro, slower ones are pulled to the wheel heading.
  • The Kalman filter does this optimally and also estimates the bias (see Localization, Mapping and Navigation); in ROS the robot_localization package fuses wheel odometry, an IMU and other sensors into the odom frame.
  • Calibrate the gyroscope bias at start-up by averaging its output while the robot is still.
  • An absolute reference (a map and lidar scan matching, visual landmarks, GNSS outdoors) removes drift entirely: odometry then fills the gaps between corrections (see Localization, Mapping and Navigation).

Sizing motors and wheels

Worked example: torque and power for a 10 kg robot

  • The robot (10 kg) must accelerate at 0.5 m/s² while climbing a 10° slope, with a rolling resistance coefficient of 0.02. The traction force is F = m (a + g sin θ + g Crr cos θ) = 10 × (0.5 + 1.70 + 0.19) = 24.0 N.
  • With two driven wheels of radius 0.05 m each wheel supplies 12.0 N: torque at the wheel = 12.0 × 0.05 = 0.60 N·m.
  • Through a 20:1 gearbox at 80 % efficiency the motor torque is 0.60 ÷ (20 × 0.8) = 37 mN·m at 3 820 rpm for 1.0 m/s; the mechanical power is F v = 24.0 × 1.0 = 24 W (add the drivetrain loss and a margin of 1.5 to 2 for the peak).
  • Choose the motor for continuous torque at the operating speed and peak torque for acceleration, and the supply voltage so that the back-EMF does not limit the top speed (see Motion Control and Actuators).
  • Larger wheels roll over obstacles and reduce the torque needed for a given speed (torque = F r), but raise the speed per rpm and lose resolution.
  • Do not forget the caster or skid: a poorly chosen caster adds drag and a heading disturbance when reversing.
  • Check the slope and the surface in the specification, not only the flat floor.

Velocity commands and acceleration limits

A velocity command from a planner or a person can jump: send it as given and the motors slam, the wheels slip and the odometry breaks. Put a limit on the acceleration (and on the jerk if the load is delicate) between the command and the motor controllers; nav2_velocity_smoother and the diff_drive_controller parameters do it.

Worked example: an acceleration limit

With a limit of 0.8 m/s², reaching 1.0 m/s takes 1.0 ÷ 0.8 = 1.25 s and covers ½ a t² = 0.63 m. A stop from 1 m/s needs at least the same distance, which sets the clearances the planner must keep (see Robot Software Architecture).

  • Limit by what the wheels can grip: the maximum acceleration is μ g, so a floor with a friction coefficient of 0.5 limits it to about 4.9 m/s² (and a wet floor much less): far above most robots’ motor limit, but not above the grip of a heavy robot with a high center of mass, which tips.
  • Command timeouts: if no new command arrives for a few periods, ramp the speed down to zero.
  • Limit angular acceleration separately, and the speed in turns (the outer wheel runs faster).

Related

FAQ

Why does my robot drive in a curve?

The wheels are not the same size, a motor is slower, or one wheel’s encoder scale is wrong. Calibrate each wheel’s diameter with a straight-line test and use closed-loop speed control on each wheel.

How accurate is wheel odometry?

On a good floor, about 1 to 5 % of the distance, and a few degrees per turn; much worse with slip. Use it for short-term motion and correct it with a gyroscope and a map.

Differential drive or mecanum?

Differential drive for simplicity, spinning in place and good odometry; mecanum when you must move sideways in tight spaces on a flat, smooth floor and can accept poor odometry and slip.

Where should the encoders go?

On the wheel shaft for the best odometry (no gear backlash in the measurement), or on the motor shaft for higher resolution and a faster speed loop; account for the gear ratio and backlash.

References

  • Siegwart, R., Nourbakhsh, I. and Scaramuzza, D., Introduction to Autonomous Mobile Robots (kinematics and odometry).
  • Borenstein, J. and Feng, L., “Measurement and correction of systematic odometry errors in mobile robots”, IEEE Transactions on Robotics and Automation, 1996 (UMBmark).
  • ROS 2 diff_drive_controller, robot_localization and Nav2 velocity-smoother documentation.

Last reviewed: October 2026. All values are computed in the page builder: measure your own robot.