Before writing a single line of control code, we need a clear picture of the mechanical system being controlled. This chapter surveys the three dominant families of wheeled robots, explains the mathematical language used to describe position and orientation in 2-D, and introduces the specific hardware platform used throughout the rest of the book.
A differential-drive robot has two driven wheels mounted on a common axle, with one or more passive caster wheels for support. Speed is controlled independently for each driven wheel. To go straight, both wheels run at the same speed. To turn, one wheel runs faster than the other. To spin in place, wheels run at equal speed in opposite directions.
This is the simplest and most common configuration for small robots. It maps cleanly to two PWM channels, requires no steering servo, and has a straightforward kinematic model (Chapter 3). The trade-off is the non-holonomic constraint: the robot cannot move sideways. Its instantaneous velocity is always aligned with its heading.
A car steers by pivoting its front wheels about a vertical axis. For the car to roll without tyre scrub, the inner and outer front wheels must track circles of different radii — this geometric requirement is called the Ackermann condition. The turning radius $R$ is determined by the steering angle $\delta$ and the wheelbase $l$:
$R = l / \tan(\delta)$
With $l = 0.20$ m and $\delta = 15°$: $R = 0.20 / \tan(15°) \approx 0.75$ m.
Ackermann robots cannot turn in place. Their minimum turning radius limits manoeuvrability in tight spaces. They are appropriate for higher-speed applications (autonomous cars, RC-car-scale platforms) where stability and efficiency matter more than agility.
Mecanum wheels have small rollers mounted at 45° around their circumference. By combining forward/backward rotation of all four wheels in specific ratios, the robot can translate in any direction — including sideways — without rotating. This eliminates the non-holonomic constraint entirely.
The kinematic mixing matrix for a four-mecanum platform maps desired body velocities $(v_x, v_y, \omega)$ to individual wheel speeds. Chapter 3 derives this matrix in full. The cost is mechanical complexity and reduced efficiency: power is lost through the rollers even when driving straight.
A rigid body in the plane has three degrees of freedom: $x$, $y$, and heading $\theta$. The number of controllable degrees of freedom depends on the drive family:
| Drive type | Controllable DOF | Can slide sideways? |
|---|---|---|
| Differential | 2 (v, ω) | No |
| Ackermann | 2 (v, δ) | No |
| Mecanum/omni | 3 (v_x, v_y, ω) | Yes |
Differential and Ackermann robots are non-holonomic: they have fewer controllable DOF than configuration DOF. They can still reach any $(x, y, \theta)$ in an open space, but not via an arbitrary path — manoeuvres like parallel parking require multi-step sequences.
Robotics uses several coordinate frames, and keeping track of which frame a quantity is expressed in is the single most common source of bugs.
The world frame (also called the global frame or map frame) is fixed to the environment. Its origin can be placed anywhere — typically at the robot’s starting position. Axes follow the right-hand convention: $x$ points forward (East), $y$ points left (North), $z$ points up. Angles are measured counter-clockwise from the positive $x$-axis.
The robot frame is fixed to the robot body, with its origin at the centre of the driven axle. The $x$-axis points forward along the robot’s heading, the $y$-axis points left, and $z$ points up.
Each sensor has its own frame. The ultrasound sensor, for example, has its origin at the transducer face, with $x$ pointing in the emission direction. Sensor readings must be transformed into the robot frame (and then possibly the world frame) before being used for planning.
A rotation by angle $\theta$ in the plane is given by:
\[R(\theta) = \begin{pmatrix} \cos\theta & -\sin\theta \\ \sin\theta & \cos\theta \end{pmatrix}\]A pose in 2-D is an element of the Special Euclidean group SE(2), represented as a 3×3 homogeneous matrix:
\[T = \begin{pmatrix} \cos\theta & -\sin\theta & x \\ \sin\theta & \cos\theta & y \\ 0 & 0 & 1 \end{pmatrix}\]To transform a point $\mathbf{p}^S$ expressed in the sensor frame into the robot frame, given the sensor’s pose $T_{RS}$ in the robot frame:
\[\mathbf{p}^R = T_{RS} \cdot \mathbf{p}^S\]The helper functions for these operations are in:
See code/01_coordinate_frames.py
import numpy as np
def rotation_2d(theta: float) -> np.ndarray:
c, s = np.cos(theta), np.sin(theta)
return np.array([[c, -s], [s, c]])
def se2_matrix(x: float, y: float, theta: float) -> np.ndarray:
c, s = np.cos(theta), np.sin(theta)
return np.array([[c, -s, x], [s, c, y], [0, 0, 1]])
def transform_point(T: np.ndarray, px: float, py: float):
pt = T @ np.array([px, py, 1.0])
return pt[0], pt[1]
The robot used throughout this book has the following characteristics:
Chassis and Drive
Sensors
Sensor Head The sensor head carries both the ultrasound module and the camera. It rotates about the robot’s vertical ($z$) axis via a servo, giving a ±90° scan range. This lets the robot look sideways or behind without turning the whole chassis.
Compute and Wiring
The pin assignments and physical constants are collected in a single configuration dataclass:
from dataclasses import dataclass
@dataclass(frozen=True)
class RobotConfig:
wheel_radius_m: float = 0.033
track_width_m: float = 0.150
max_speed_mps: float = 0.50
# Motor GPIO pins (BCM numbering)
motor_left_in1: int = 17
motor_left_in2: int = 18
motor_left_en: int = 12 # hardware PWM
motor_right_in1: int = 22
motor_right_in2: int = 23
motor_right_en: int = 13 # hardware PWM
# Sensor pins
us_trigger: int = 5
us_echo: int = 6
servo_pan: int = 19
Several hardware constraints must be respected in software:
Motor stall current. At zero speed, a DC motor draws its maximum (stall) current. For typical N20 or GA25 gear-motors, stall current is 500 mA – 2 A. If the motor is held stalled for more than a few seconds, the windings overheat. The software should implement a stall-detection timer that kills power if speed feedback drops below threshold while a non-zero duty cycle is applied.
Servo rotation range. The pan servo on the sensor head has a mechanical stop at ±90° from centre (0°). Commanding positions outside this range will strip gears. The software servo driver must clamp commands to $[-90°, +90°]$.
Camera field of view. The camera has a 62° horizontal FOV. Objects closer than approximately 15 cm may be partially outside the frame. Keep this in mind when using vision-based obstacle detection at close range.
A brief note on interfaces for readers who are new to embedded hardware:
With the hardware platform understood, Chapter 2 dives into motor control: how to generate PWM signals in Python, how H-bridges work, and how to calibrate for the real-world non-linearities that make open-loop motor control unreliable.
Navigation: