Kinematics is the study of motion without reference to forces. In robotics, it answers the question: given the speeds of the wheels, how fast and in which direction does the robot move? And the inverse: given a desired robot motion, what wheel speeds are required? This chapter derives these relationships for differential drive, Ackermann, and mecanum platforms, then addresses velocity ramping as a practical implementation concern.
Let:
The robot’s linear velocity $v$ and angular velocity $\omega$ are:
\[v = \frac{v_R + v_L}{2}, \qquad \omega = \frac{v_R - v_L}{L}\]Worked example: with $r = 0.033$ m and $L = 0.15$ m, left wheel at $v_L = 0.20$ m/s and right wheel at $v_R = 0.30$ m/s:
$v = (0.30 + 0.20)/2 = 0.25$ m/s
$\omega = (0.30 - 0.20)/0.15 = 0.67$ rad/s
The instantaneous turning radius is $R = v/\omega = 0.25/0.67 \approx 0.37$ m.
Given a desired $(v, \omega)$, solve for wheel speeds:
\[v_R = v + \frac{\omega L}{2}, \qquad v_L = v - \frac{\omega L}{2}\]Example: to drive at $v = 0.20$ m/s with $\omega = 1.0$ rad/s (turning left):
$v_R = 0.20 + (1.0 \times 0.15)/2 = 0.275$ m/s
$v_L = 0.20 - (1.0 \times 0.15)/2 = 0.125$ m/s
If either wheel speed exceeds the motor’s maximum ($v_{max}$), both speeds should be scaled down proportionally to preserve the curvature:
def clamp_wheel_speeds(v_l, v_r, v_max):
scale = max(abs(v_l), abs(v_r)) / v_max
if scale > 1.0:
v_l /= scale
v_r /= scale
return v_l, v_r
See code/03_diff_kinematics.py
In an Ackermann-steered vehicle with wheelbase $l$ (front-to-rear axle distance) and track width $t$, the steering angle of the inner wheel $\delta_i$ and outer wheel $\delta_o$ for a desired turning radius $R$ (measured to the vehicle centre) are:
\[\tan(\delta_i) = \frac{l}{R - t/2}, \qquad \tan(\delta_o) = \frac{l}{R + t/2}\]Example: $l = 0.20$ m, $t = 0.14$ m, $R = 0.50$ m:
$\delta_i = \arctan(0.20 / (0.50 - 0.07)) = \arctan(0.465) \approx 25.0°$
$\delta_o = \arctan(0.20 / (0.50 + 0.07)) = \arctan(0.351) \approx 19.4°$
The difference (about 5.6°) is the Ackermann correction. A simple linkage can approximate this geometry mechanically. Note that the maximum steering angle is limited by geometry (and by tyres clearing the chassis), which sets the minimum turning radius.
See code/03_ackermann_kinematics.py
A mecanum wheel has rollers mounted at 45° to the wheel rotation axis. The net force a mecanum wheel exerts on the floor has both a forward and a lateral component. By choosing wheel speeds appropriately, the lateral components can add (for sideways motion) or cancel (for straight motion).
For a four-wheel mecanum robot with wheel positions at $(\pm a, \pm b)$ from the robot centre (typically $a = L/2$, $b = $ half wheelbase), and all rollers at 45°, the forward kinematics matrix $\mathbf{F}$ gives body velocities from wheel speeds:
\[\begin{pmatrix} v_x \\ v_y \\ \omega \end{pmatrix} = \frac{r}{4} \begin{pmatrix} 1 & 1 & 1 & 1 \\ -1 & 1 & 1 & -1 \\ -1/(a+b) & 1/(a+b) & -1/(a+b) & 1/(a+b) \end{pmatrix} \begin{pmatrix} \omega_1 \\ \omega_2 \\ \omega_3 \\ \omega_4 \end{pmatrix}\]where wheels are numbered: 1=front-left, 2=front-right, 3=rear-left, 4=rear-right.
The inverse (body → wheels) is the pseudo-inverse $\mathbf{F}^+$, or equivalently:
\[\begin{pmatrix} \omega_1 \\ \omega_2 \\ \omega_3 \\ \omega_4 \end{pmatrix} = \frac{1}{r} \begin{pmatrix} 1 & -1 & -(a+b) \\ 1 & 1 & (a+b) \\ 1 & 1 & -(a+b) \\ 1 & -1 & (a+b) \end{pmatrix} \begin{pmatrix} v_x \\ v_y \\ \omega \end{pmatrix}\]Example — pure sideways motion: $v_x = 0$, $v_y = 0.2$ m/s, $\omega = 0$, $r = 0.048$ m, $a+b = 0.19$ m:
$\omega_1 = (1/0.048) \cdot (-0.2) = -4.17$ rad/s (front-left, reverse)
$\omega_2 = (1/0.048) \cdot (0.2) = +4.17$ rad/s (front-right, forward)
And so on. This confirms the diagonal pattern that makes the robot slide right.
See code/03_mecanum_kinematics.py
The kinematic constraint on a differential-drive robot can be written as:
\[\dot{y}\cos\theta - \dot{x}\sin\theta = 0\]This says the robot’s velocity perpendicular to its heading is zero — it cannot slip sideways instantaneously. This is the non-holonomic constraint. It is a constraint on velocities, not on reachable positions: given enough manoeuvring, a differential-drive robot can reach any $(x, y, \theta)$ in an open space. But it cannot take an arbitrary straight-line path there.
The practical implication for planning: paths generated for a holonomic robot (like a point mass that can move in any direction) may not be directly executable. Either the planner must respect the constraint, or a separate step must smooth the path into a kinematically feasible trajectory.
Applying an instantaneous jump in wheel speed commands causes mechanical shock and may cause wheel slip. It also exceeds the motor’s ability to respond immediately, leading to overshoot if there is a PID loop. A trapezoidal velocity profile limits the rate of change of speed:
For a move of duration $T$ with acceleration limit $a_{max}$ and target speed $v_{target}$, the ramp-up time is $t_{ramp} = v_{target}/a_{max}$.
def trapezoidal_profile(v_target, a_max, dt, current_v):
if current_v < v_target:
return min(current_v + a_max * dt, v_target)
elif current_v > v_target:
return max(current_v - a_max * dt, v_target)
return current_v
The full generator version yields $(v, \omega)$ setpoints at each time step:
Typical acceleration limit: $a_{max} = 0.5$ m/s² for a small robot. At this limit, a 0.3 m/s cruising speed is reached in $0.3/0.5 = 0.6$ seconds, during which the robot travels $0.5 \times 0.3 \times 0.6 = 0.09$ m. The ramp distances must be accounted for when computing total move distances.
With kinematics established, Chapter 4 closes the loop: rather than computing what wheel speeds should produce a desired motion, we measure what is actually happening and use a PID controller to correct the error.
Navigation: