Blog · 2026-08-15 · Kinematics
Two wheels, two motors, and a surprising amount of geometry. This is how a robot converts "drive forward at 0.4 m/s while turning left at 45°/s" into two numbers its motor controllers understand — and what goes wrong when it can't.
The differential drive is the most common robot base in existence, and for good reason: two powered wheels and a caster, no steering linkage, no articulation, and the ability to spin on the spot. But between your navigation code — which thinks in terms of where the robot should go — and your motor drivers — which think in PWM and RPM — sits a translation layer. That layer is kinematics, and getting it right is the difference between a robot that follows a path and one that describes an approximate spiral in the general direction of your intent.
Inverse kinematics is the everyday one: you have a desired body motion, and you need wheel speeds. Forward kinematics runs the other way: you have measured wheel speeds and you want to know what the body did — which is odometry, covered in Wheel Odometry Explained. Both use the same two relationships, just rearranged.
A differential drive's body motion has exactly two degrees of freedom: forward velocity V (metres per second) and rotation rate ω (radians per second). It has no third — it cannot move sideways, which is why parallel parking a differential robot requires a manoeuvre rather than a translation. That pairing of V and ω is what ROS calls a Twist message, and what most control code calls a velocity command.
v_left = V − ω × (track ÷ 2)
v_right = V + ω × (track ÷ 2)
wheel RPM = v × 60 ÷ (π × wheel diameter)
Read the structure rather than the symbols: forward speed is the average of the two wheels; turn rate is their difference divided by track width. Everything a differential drive can do is a combination of those. Both wheels equal and positive: straight ahead. Equal and opposite: spin in place. One stopped, one moving: an arc pivoting around the stopped wheel. Any other pair: an arc somewhere in between. The Differential Drive Calculator runs these numbers for your geometry and also checks whether the result is physically achievable, which turns out to matter enormously.
There's an elegant way to picture what's happening. At any moment, a differential drive robot is rotating around a single point somewhere on the line extending through both wheel axles — the instantaneous centre of curvature, or ICC. Driving straight puts the ICC infinitely far away. Spinning in place puts it exactly between the wheels. A gentle arc puts it far off to one side; a tight turn brings it close.
turning radius R = V ÷ ω
R > track/2 → both wheels turn the same direction
R = track/2 → inside wheel stopped, pivot on that wheel
R < track/2 → inside wheel reverses
That last case is worth internalising, because it's where a lot of mechanical unpleasantness lives. When the commanded radius is tighter than half the track width, the inside wheel must run backwards while the outside wheel runs forwards. The robot is no longer rolling through the turn — it's scrubbing, dragging rubber sideways across the floor. It works, it's legal, and every spin-in-place manoeuvre does it, but the tyre scrub wrecks odometry accuracy for the duration and adds a load spike your motors have to absorb.
Look at where track width sits in the equations — it multiplies ω. That means an error in track width produces a proportional error in every single turn the robot ever makes, while leaving straight-line motion perfectly accurate. This is the signature failure: a robot that drives straight beautifully and consistently under- or over-rotates.
Wheel diameter plays the complementary role: it scales everything, straight and turning alike, because it's the conversion between wheel rotation and ground distance. If your robot travels the wrong distance and turns wrong by the same proportion, suspect wheel diameter. If only turns are wrong, suspect track width. That diagnostic split saves a lot of guessing.
Here's the subtle failure that catches almost everyone. Suppose your motors top out at 150 RPM at the wheel, and your controller commands a fast forward speed with a sharp turn. The outer wheel needs 180 RPM; the inner needs 90. The outer motor saturates at 150 and the inner faithfully does 90 — and now the difference between the wheels is 60 instead of the commanded 90, so the robot turns at two thirds the intended rate while travelling at an unintended speed. The robot doesn't just go slower; it goes somewhere else entirely.
The fix is to detect saturation and scale both wheel commands by the same factor, preserving their ratio:
peak = max(|v_left|, |v_right|)
if peak > v_max:
scale = v_max ÷ peak
v_left ×= scale ; v_right ×= scale
The robot then traces the same curve more slowly, which is almost always what you wanted. Some controllers instead prioritise ω over V — preserving the turn rate exactly and sacrificing forward speed — which is the better choice for path-following where heading errors compound. Either is better than letting saturation silently distort the motion.
Leave headroom, too. Motor specs quote no-load RPM, and a loaded motor on carpet running up a slight incline will not reach it. Designing to 80% of nominal maximum is a reasonable default; the calculator flags anything above 85%.
Kinematics gets you a target wheel speed, not a PWM value. The naive approach — assume PWM is proportional to speed — fails immediately, because motors have a deadband (below some duty cycle they don't turn at all), because load changes the speed at any given PWM, and because no two motors are identical. Two wheels commanded to the same PWM will not run at the same speed, and your beautifully-computed kinematics will drive the robot in a curve.
The proper answer is a closed velocity loop per wheel: measure actual wheel speed from encoders, compare to the kinematic target, and let a PI or PID controller close the gap — the subject of How to Tune a PID Controller. This is why encoders and differential drive go together so naturally: the kinematics tell each wheel what to do, the encoders confirm it did it, and the same encoder counts feed odometry to tell you where the robot ended up.
The same equations cover more robots than you'd expect. A four-wheel skid-steer uses them directly with both wheels on each side commanded together — but skid-steering drags wheels sideways in every turn by design, so the real turn rate falls short of commanded, often by 20% or more on grippy surfaces. The standard workaround is an empirical "track width" larger than the physical one, tuned until commanded turns match reality. Tracked robots behave similarly, with even more scrub. Mecanum and omni drives add a third degree of freedom and need a matrix rather than two equations, and Ackermann steering (car-like) is a different model entirely, since it cannot spin in place at all.
Radians per second inside the equations, always — the maths assumes it. Convert at the boundary: degrees per second × π ÷ 180. Mixing the two silently scales every turn by 57×, which is at least an unmissable bug.
With ω = 0 the kinematics command identical wheel speeds, so a curve means the wheels aren't actually matching — unequal effective diameters, mismatched motors, or open-loop PWM control. Closed-loop velocity control fixes the motor mismatch; per-side diameter scale factors fix the rest.
Kinematics describes velocities, not how quickly you can change them. Commanding a step change in velocity asks for infinite acceleration; real robots slip, tip or brown out. Ramping commands over time is a separate and worthwhile layer, and it protects the power system described in Voltage Sag and Brownouts.
Two equations, one geometric constant, and a saturation check — that's the whole translation layer. Run yours through the Differential Drive Calculator, and if you're still deciding what kind of base to build, the companion piece compares the options: Choosing a Robot Drivetrain.