Layer 4 — Applied Intelligence

Autonomous Differential-Drive Robot

Built from scratch at age 15. Dead-reckoning odometry, 8-sensor ring, 10×10 occupancy grid, Dijkstra path planning, boustrophedon coverage. Now published as clean C++.

Arduino MegaATmega2560
Built at 15Original Build Year ~2016
8 sensors5× HC-SR04 + 3× Sharp IR
10×10 gridOccupancy Map
DijkstraPath Planner

Open source · C++ / Arduino

github.com/muhammadzareii/diff-drive-robot

The Story

This was one of the first serious engineering projects — a fully autonomous mobile robot designed, built, and programmed from scratch at age 15. There was no robotics kit, no tutorial, no library handling the hard parts. Every number in the firmware — the centimetres per stepper pulse, the wheel radius asymmetry, the turn correction factor, the IR distance calibration curve — came from physical measurement: commanding motion, measuring the result with a tape measure or protractor, and adjusting.

The original source code still exists: a single monolithic .ino file with Farsi comments, global variables, and goto statements — exactly as written at the time. Rough code, but the thinking inside it was solid. The kinematic model is correct. The sensor fusion is principled. The path planning is real Dijkstra.

This repository is a clean reimplementation of the same algorithms in modular, documented C++ — preserving every empirical constant, every calibration equation, every algorithm — because the original thinking deserves to be readable.

Robot Schematic

Differential-Drive Robot — Top View (Schematic) ▲ FORWARD LEFT Rl = 4.79 cm RIGHT Rr = 4.78 cm Arduino Mega 2560 ATmega2560 @ 16 MHz C++ / Arduino firmware I²C · SPI · UART MPU-9250 IMU 9DOF · I²C · Yaw heading nRF24L01 2.4 GHz SPI SR04-F SR04-FL SR04-FR SR04-L SR04-R IR-L IR-C IR-R d = 23.0 cm (axle width) Calibration Constants CM_PER_PULSE = 0.150325 Rl = 4.79 cm | Rr = 4.78 cm d (axle) = 23.0 cm CW = 82/90 | CCW = 83.75/90 Δr asymmetry: 0.01 cm (measured) Sharp IR (GP2Y0A21YK) dist = 93677 × ADC^(−1.505) Valid: 5–45 cm Fitted from bench measurements 10-bit ADC · 5V reference Non-monotonic below 5 cm HC-SR04 Ultrasonic (×5) 2 cm–4 m Sharp IR (×3) 5–45 cm
Fig. 1. Top-down schematic of the differential-drive robot. Calibration constants were measured empirically — not guessed. The 0.01 cm asymmetry between Rl and Rr was discovered by observing systematic drift under straight-line commands and iterated until eliminated.

Dead-Reckoning Odometry

The kinematic model for a differential-drive robot tracks position (x, y) and heading θ from wheel pulse counts. Stepper motors are used instead of encoders — each commanded pulse moves the wheel by a precisely measured distance.

s = pulses × CM_PER_PULSE = pulses × 0.150325 cm/pulse

Δθ = (s_right − s_left) / d    [for differential turns]

x ← x + s·cos(θ)     y ← y + s·sin(θ)     θ ← θ + Δθ

The wheel radius asymmetry (Rl = 4.79 cm, Rr = 4.78 cm) causes a 0.21% systematic drift per metre of straight travel — invisible over short distances, visible after 3+ metres. This is corrected in firmware by applying asymmetric pulse counts for commanded forward motion.

Turn Calibration

Turning by exactly 90° requires knowing how many stepper pulses correspond to that angle. Measured:

CW correction: 82/90    (command 82 pulses per wheel to achieve 90° CW)
CCW correction: 83.75/90    (83.75 pulses per wheel for 90° CCW)

The asymmetry (CW ≠ CCW) is due to mechanical asymmetry in the drive train — the motor + gearbox has slightly different friction in the two directions. Discovered by commanding 360° rotations and measuring actual rotation with a protractor, then iterating the correction factor.

Sharp IR Calibration

The Sharp GP2Y family produces a voltage inversely related to distance via a power law:

distance_cm = 93677 × ADC^(−1.505)

Fitted using least-squares regression in log space from measurements at 5, 10, 15, 20, 30, 40, 45 cm. Valid for 5–45 cm with the GP2Y0A21YK sensor at 5V, 10-bit ADC. Below 5 cm the output is non-monotonic and unusable; above 45 cm the noise exceeds 5 cm uncertainty.

Path Planning — Dijkstra on 10×10 Grid

The arena (2m × 2m) is discretised into a 10×10 grid of 20×20 cm cells. Cells start as "unknown" and are marked "free" as the robot visits them; cells where an obstacle was detected are marked "blocked". Dijkstra's shortest-path algorithm runs on the 100-node graph (4-connected adjacency, uniform edge weight) to find the shortest free path to any target cell.

The algorithm is implemented in O(N²) with a linear scan priority queue — fast enough for N=100 on ATmega2560 (runs in <1 ms). Memory: ~200 bytes for distance table + ~100 bytes for parent pointers. Both fit easily in the 8 KB SRAM of the ATmega2560.

Coverage — Boustrophedon Pattern

Area coverage uses the boustrophedon ("as the ox ploughs") pattern: alternating left-to-right and right-to-left passes across the grid columns. When a new obstacle is discovered mid-sweep, the map is updated and the remaining coverage path is regenerated around it.

Boustrophedon Coverage (10×10 grid) · · · ✕ obstacle → re-plan R Start 0 1 2 3 9 Upward sweep Downward sweep Obstacle → re-plan
Fig. 2. Boustrophedon coverage: alternating column sweeps, top-to-bottom and bottom-to-top. When an obstacle is encountered (✕), the occupancy map is updated and the remaining path is re-planned around it.

IMU — Heading Correction

The MPU-9250 (9-DOF: accel + gyro + magnetometer via AK8963) is connected over I²C at 400 kHz. The Z-axis gyroscope provides angular velocity; Euler integration at 20 Hz gives heading. The IMU is used for two purposes:

nRF24L01 Wireless

The 2.4 GHz nRF24L01 transceiver (SPI) provides a wireless link for two purposes: real-time position telemetry to a remote monitor (the robot broadcasts its current (x, y, θ) every 500 ms), and emergency stop command reception. If the remote sends the stop byte 0xFF, the robot immediately halts all motion regardless of current navigation state.

Why This Matters at 15

No robotics library was used. No SLAM library, no navigation stack, no pre-written kinematics. Every part — the kinematic model, the IR calibration, the Dijkstra implementation, the sensor ring layout, the turn asymmetry correction — was derived from reading datasheets and papers and verified experimentally. The result was a robot that actually worked: navigated to (x, y) coordinates, covered its arena, avoided obstacles, and communicated wirelessly.

The empirical calibration discipline embedded here — measure it, don't guess it — has carried forward into every subsequent hardware project.

Chista Robotics Team context: This robot was the first serious hardware project built as part of Chista Robotics Team, a competitive robotics team based in Shiraz, Iran, where I have been the hardware and embedded programmer since 2014. Chista has competed at Asia Pacific Robot Contest (2018), Iran Open Robocup (2016, 2017), AUT Cup (2017), FIRA Cup, Shiraz Open (2015), and Autonomous Vehicles Tehran (2025), among others. The team competes seriously but is entirely volunteer-driven — shared engineering passion, no compensation. Certificates of participation and awards for relevant competitions are available on request.