Open source · C++ / Arduino
github.com/muhammadzareii/diff-drive-robotThe 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
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_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:
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:
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.
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:
- Sanity check after turns: If the odometry model claims a 90° turn but the gyro reports only 45°, slippage occurred. The gyro reading overrides the odometric heading.
- Slip detection: If commanded forward motion produces nonzero gyroscope yaw rate, a wheel is slipping. The robot stops and retries.
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.