Mobile Robotics & Autonomous Navigation

Robots that move around. Learn locomotion types, differential-drive and other kinematic models, wheel odometry and its drift, sensors (encoders, IMU, lidar, cameras, GNSS), state estimation and sensor fusion, occupancy-grid mapping and SLAM, path planning (A*, Dijkstra, RRT), path following and obstacle avoidance, ROS 2 and the navigation stack, AGVs/AMRs in industry and safety — with Python for odometry and A* planning.

Autonomous mobile robots (AMRs) deliver parts in factories, pick goods in warehouses, clean floors, mow lawns, inspect pipelines and explore planets. To do so they must answer three questions continuously: Where am I? (localisation) What does the world look like? (mapping) How do I get there? (planning and control).

Mobile robotics brings together mechatronics (motors, sensors), estimation (Kalman filters), algorithms (search, optimisation) and software architecture (ROS 2).

An analogy

A mobile robot navigating a building is like you walking through an unfamiliar museum with a map app: you count your steps (odometry), recognise landmarks to correct yourself (localisation), sketch the rooms (mapping) and plan the route to the café (path planning) while avoiding people (obstacle avoidance).

Locomotion and kinematics

Wheeled robots: differential drive (two driven wheels + caster, turns on the spot), Ackermann/car-like (steered wheels, can't turn in place), omnidirectional (mecanum or omni wheels, move sideways). Legged robots handle rough terrain; tracked robots give traction. The kinematic model maps wheel speeds to robot velocity and constrains which motions are possible (non-holonomic constraints).

Sensing and localisation

Robots combine complementary sensors:

Mapping, SLAM and planning

Occupancy grids represent free/occupied space; SLAM (simultaneous localisation and mapping — e.g. GMapping, Cartographer, SLAM Toolbox, ORB-SLAM) builds the map while localising. Global planners (Dijkstra, A*, hybrid A*, RRT*) find a path on the map; local planners (DWA, TEB, MPC) follow it while avoiding dynamic obstacles within velocity/acceleration limits.

Software stacks, fleets and safety

ROS 2 provides the middleware (DDS), tools and the Nav2 navigation stack (behaviour trees, costmaps, planners, controllers). Industrial AMRs add fleet management, traffic control, charging and integration with WMS/MES (VDA 5050 interface). Safety: safety-rated laser scanners with protective fields, speed-dependent zones, emergency stops, ISO 3691-4 (driverless industrial trucks) and risk assessment for mixed human–robot spaces.

Formulas

Worked example: Robot velocity

Given: r = 0.05 m, L = 0.30 m, ω_l = 4, ω_r = 6 rad/s

Find: v, ω, R.

Answer: 0.25 m/s, 0.333 rad/s, R = 0.75 m

Worked example: Odometry drift

Given: Wheel radius error 0.5% on one wheel, 0.3 m track, 20 m straight drive

Find: Heading error.

Answer: ≈ 0.33 rad (19°) — why odometry needs correction

Worked example: Stopping distance

Given: v = 1.5 m/s, reaction 0.2 s, braking 1 m/s²

Find: Distance.

Answer: 1.43 m → size the safety scanner's protective field accordingly

Differential-drive odometry and A* path planning

import math, heapq
# Differential-drive kinematics and odometry
r, Lw = 0.05, 0.30                 # wheel radius, wheel separation (m)
x = y = th = 0.0
dt = 0.01
for n in range(300):               # 3 s: left 4 rad/s, right 6 rad/s
    wl, wr = 4.0, 6.0
    v = r * (wr + wl) / 2
    w = r * (wr - wl) / Lw
    x += v * math.cos(th) * dt; y += v * math.sin(th) * dt; th += w * dt
print(f"v = {v:.2f} m/s, w = {w:.3f} rad/s, turn radius {v/w:.2f} m")
print(f"pose after 3 s: x = {x:.3f} m, y = {y:.3f} m, heading {math.degrees(th):.1f} deg")
# A* on an occupancy grid
grid = ["S...#.....",
        ".##.#.##.#",
        ".#.......#",
        ".#.####.#.",
        "...#.....G"]
R_, C_ = len(grid), len(grid[0])
find = lambda ch: next((r_, c) for r_ in range(R_) for c in range(C_) if grid[r_][c] == ch)
s, g = find("S"), find("G")
h = lambda p: abs(p[0] - g[0]) + abs(p[1] - g[1])
openq = [(h(s), 0, s)]; cost = {s: 0}; expanded = 0
while openq:
    f, gc, p = heapq.heappop(openq)
    if p == g: break
    expanded += 1
    for d in ((1,0),(-1,0),(0,1),(0,-1)):
        q = (p[0]+d[0], p[1]+d[1])
        if 0 <= q[0] < R_ and 0 <= q[1] < C_ and grid[q[0]][q[1]] != "#":
            if gc + 1 < cost.get(q, 1e9):
                cost[q] = gc + 1
                heapq.heappush(openq, (gc + 1 + h(q), gc + 1, q))
print(f"A*: path length {cost[g]} cells, nodes expanded {expanded}")
# v = 0.25 m/s, w = 0.333 rad/s, turn radius 0.75 m
# pose after 3 s: x = 0.632 m, y = 0.344 m, heading 57.3 deg
# A*: path length 13 cells, nodes expanded 20

Different wheel speeds make the robot follow an arc of radius 0.75 m; integrating gives the pose — but wheel slip and encoder errors accumulate, so odometry is fused with IMU, lidar or vision. A* with a Manhattan-distance heuristic finds the shortest grid path while expanding only 20 cells, much fewer than a blind breadth-first search.

Common mistakes

Applications

Key takeaways

Going further