← SHEET 02 · ASSEMBLIES EL-004
PD Line-Follower Robot
| PART NO | EL-004 |
|---|---|
| MATL / SYSTEM | ARDUINO NANO · TCRT5000 ×5 · L298N |
| TOOLS | Arduino IDE · Wokwi · C++ |
A 2-wheel differential-drive robot that tracks a line using a 5-channel IR reflectance array. A weighted-centroid estimate (0-4000-style, QTR-spacing) turns five analog readings into a single line position, a PD law converts position error into a steering correction, and the correction is mixed into slew-limited left/right motor commands so the drive never jumps. A boot-time calibration sweep captures each sensor’s min/max reflectance, and a spin-search recovery kicks in when the line drops out from under all five channels. Firmware and control math are written and exercised in Wokwi; the physical chassis has not been built.
OVERVIEW & MOTIVATION
Line-following is a compact, well-bounded control problem: a noisy 1-D position estimate, a classic PD loop, and a differential-drive mixer that has to saturate and slew gracefully instead of lurching. It’s also the standard first project for closed-loop mobile robotics, and a good proving ground for the sensor-fusion and state-machine habits (calibration, fault/lost-state handling) that carry over to bigger platforms. This build keeps the mechanical side simple — a 5-sensor IR array, one H-bridge, two geared motors — so the firmware can carry the interesting work: reflectance normalization, centroid weighting, PD tuning, and a real recovery behavior instead of just stopping when the line is lost.
COMPONENTS & BOM
| REF | COMPONENT | SPEC | ROLE |
|---|---|---|---|
| U1 | Arduino Nano | ATmega328P, 5 V logic | controller — calibration + PD loop + telemetry |
| S1-S5 | TCRT5000-style IR reflectance module | VCC/GND/DO/AO breakout, analog out used | 5-channel line-position sensing array |
| U2 | L298N dual H-bridge module | 2 A/channel, onboard 5 V reg (optional) | drives both geared DC motors from PWM + direction |
| M1, M2 | Geared DC motor | 6-12 V gearmotor, wheel-mounted | differential-drive actuation, left/right |
| SW1 | Momentary pushbutton | normally-open, wired INPUT_PULLUP | triggers the calibration sweep on boot/hold |
| BT1 | Battery pack | 6-12 V (e.g. 2S Li-ion or 4×AA) | motor power rail, separate from logic 5 V |
| — | Chassis, wheels, caster | 2WD line-follower frame | mechanical platform — not yet built |
WIRING
| MCU PIN | NET | PERIPHERAL PIN |
|---|---|---|
| A0 | S1 (far left) | sensor 1 — AO |
| A1 | S2 | sensor 2 — AO |
| A2 | S3 (center) | sensor 3 — AO |
| A3 | S4 | sensor 4 — AO |
| A4 | S5 (far right) | sensor 5 — AO |
| D2 | CAL_BTN | pushbutton — SIG (INPUT_PULLUP, other leg to GND) |
| D5 | left motor speed | L298N — ENA (PWM) |
| D6 | left motor dir A | L298N — IN1 |
| D7 | left motor dir B | L298N — IN2 |
| D8 | right motor dir A | L298N — IN3 |
| D9 | right motor dir B | L298N — IN4 |
| D10 | right motor speed | L298N — ENB (PWM) |
| D13 | status LED (onboard) | calibration / lost-line indicator |
| 5V | logic rail | sensor array VCC ×5 |
| GND | common ground | sensors, CAL_BTN, L298N logic GND, battery − |
| — | motor power | L298N OUT1/OUT2 → motor L, OUT3/OUT4 → motor R |
| — | motor power | battery + → L298N VMOT, battery − → L298N GND |
CONTROL DESIGN
Weighted-centroid position estimate. Each of the 5 channels is normalized against its own calibrated min/max span into a 0-1000 “on-line” value, then combined into a single 0-4000-style position using fixed QTR-style weights (0, 1000, 2000, 3000, 4000) — 2000 is dead-center:
uint32_t weightedSum = 0, valueSum = 0;
for (uint8_t i = 0; i < NUM_SENSORS; i++) {
int32_t norm = (raw[i] - sensorMin[i]) * 1000L / (sensorMax[i] - sensorMin[i]);
uint16_t onLine = 1000 - constrain(norm, 0, 1000); // invert: dark line -> high weight
weightedSum += (uint32_t)onLine * SENSOR_WEIGHTS[i];
valueSum += onLine;
}
bool lineSeen = valueSum >= LOST_THRESHOLD;
uint16_t position = weightedSum / valueSum; // 0..4000, 2000 = centered
PD steering + slew-limited mixing. Position error (position - 2000) drives a PD term; the
term steers a fixed base speed differentially, saturates to the PWM range, then is slew-limited
so the actual motor command only moves SLEW_STEP counts per 10 ms loop tick — no instant jumps
even on a sharp error step:
float steer = KP * error + KD * (error - lastError) / dt;
int16_t leftTarget = constrain(BASE_SPEED - steer, -MAX_SPEED, MAX_SPEED);
int16_t rightTarget = constrain(BASE_SPEED + steer, -MAX_SPEED, MAX_SPEED);
leftCmd = slewToward(leftCmd, leftTarget, SLEW_STEP);
rightCmd = slewToward(rightCmd, rightTarget, SLEW_STEP);
Lost-line recovery. When valueSum falls below LOST_THRESHOLD (no channel sees the line),
the sign of the last nonzero error is remembered and the robot spin-searches toward that side at
a fixed PWM until the line reappears or LOST_TIMEOUT_MS elapses, at which point it slews back
to a stop instead of spinning indefinitely.
SIMULATION
Runs in Wokwi against sketch.ino + diagram.json
(sources/electronics/line-follower/). Hold the calibration button at boot to run the sweep, then
watch mode / pos / err / L / R on the 115200-baud serial monitor. Wokwi has no IR-reflectance,
DC-motor, or L298N part, so the diagram substitutes honestly:
- TCRT5000 module →
wokwi-photoresistor-sensor— same VCC/GND/DO/AO 4-pin footprint, but it’s a light sensor, not an IR-reflectance sensor. Each channel’s light level is set by hand (a slider in the Wokwi UI) to fake a line passing under the array — there’s no simulated floor to reflect off. - L298N + 2 DC motors → 6 LEDs on ENA/IN1/IN2/IN3/IN4/ENB — Wokwi ships no H-bridge or DC-motor part. The LED bank verifies the logic only: which direction bit is set per motor and the PWM brightness standing in for commanded speed. No current, stall, or back-EMF is modeled.
What the sim actually proves: the calibration state machine, the weighted-centroid and
normalization math, the PD law and slew-limited mixing, and the lost-line spin-search/timeout —
all exercised with synthetic sensor input. What it can’t prove: real line tracking (no
simulated floor/line exists), motor torque/current/stall behavior, sensor height/spacing effects,
or real PWM-to-wheel-speed mapping. Those need the physical chassis. Full substitution table and
step-by-step instructions are in sources/electronics/line-follower/README.md.
STATUS
Design and firmware are complete and exercised in simulation: calibration, centroid math, PD
steering, slew-limited mixing, and lost-line recovery all run and produce sane telemetry against
synthetic sensor input in Wokwi. Not done: the chassis has not been built, no sensor array has
been mounted on a real robot, no motor has been driven by an actual L298N, and no gains have been
tuned against a real step response. KP, KD, BASE_SPEED, and SLEW_STEP are starting points
carried over from the control design, not measured/tuned values — this is a simulation-verified
control design awaiting hardware, not a working robot.
USE CASES & APPLICATIONS
The same weighted-centroid + PD structure underlies factory-floor AGVs and warehouse tow-tractors that follow painted or taped guide lines, and it’s the standard entry-level platform for robotics courses and competitions (line-following, then maze-solving). More broadly, it’s a small, tunable example of the sensor-fusion pattern — combine several noisy single-axis readings into one continuous estimate before closing a loop on it — that scales up to lane-centering and multi-sensor localization on larger mobile platforms.
FILES
sources/electronics/line-follower/sketch.ino— full firmware: calibration sweep, weighted- centroid position, PD steering, slew-limited mixing, lost-line spin-search, serial telemetry. Pinout documented in the header comment.sources/electronics/line-follower/diagram.json— Wokwi circuit diagram (Arduino Nano, 5× photoresistor-sensor stand-ins, pushbutton, 6-LED H-bridge/motor stand-in bank).sources/electronics/line-follower/README.md— Wokwi run steps, full substitution table, and what the simulation can/can’t validate, plus real-build notes (power jumper, grounding, sensor mount height, re-calibration).