Siddhant Kumar
Project 091 · Robotics

Line-Following Delivery Bot.

A wheeled robot that follows a painted line to carry small loads between fixed points — the simplest reliable form of autonomous indoor delivery.

Intermediate 10–16 hours 28 min read RobotSensorsAutonomy
Jump to source Bill of materials
Line-Following Delivery Bot — reference build illustration
Difficulty
Intermediate
Build time
10–16 hours
Indicative cost
₹1,500 – ₹2,800
Platform
Arduino Uno R3 (ATmega328P)
Category
Robotics
Last updated
28 July 2026
Contents — 26 sections

Project Overview

A wheeled robot that follows a painted line to carry small loads between fixed points — the simplest reliable form of autonomous indoor delivery.

The hardest part of autonomous navigation is knowing where you are and where to go, and the cheapest, most reliable way to sidestep that problem entirely is to paint the path on the floor. A line-following delivery bot follows a marked line — a strip of tape or paint — from one fixed point to another, carrying a small payload. It needs no map, no GPS, no expensive sensors: the line is the map, and the robot's whole job is to stay on it. This is why line following is the foundation of real automated guided vehicles (AGVs) in factories and warehouses, and the ideal first autonomous robot.

The robot senses the line with an array of infrared reflectance sensors pointed at the floor: the line (dark on light, or light on dark) reflects differently from the surrounding floor, so the sensors report where the line sits relative to the robot's centre. A control loop turns that error — how far the robot has drifted off the line — into steering corrections by driving its two wheels at different speeds (differential drive): centred, both wheels go forward; drifting left, speed up the left wheel to turn back; drifting right, speed up the right. Done crudely it wobbles; done well, with proportional (or PID) control, it tracks smoothly even through curves.

On top of the follow-the-line core, a delivery bot adds the delivery logic: it follows the line between stations (marked by junctions, tags or stop markers), stops to load/unload, and handles the practicalities — losing the line, reaching a junction, or an obstacle in the way. It is honest that line following is a constrained form of autonomy (it goes only where the line goes, and a dirty or broken line breaks it) and that real AGVs add junction logic, traffic management and safety. But as a smooth, reliable line follower with delivery stops, it is both a genuinely useful little transport robot and the clearest possible introduction to sensing, differential-drive control, and closed-loop robotics.

A small wheeled line-following robot on a track
A line-following delivery bot turns a painted path into map-free autonomy — the AGV principle in its simplest form. Photograph sourced from Wikimedia Commons — Line following robot.jpg. Reused under the licence stated on that page; please check it before republishing.

What this project does

  • Follows a marked line from point to point carrying a small load
  • Senses the line with an IR reflectance array
  • Steers by differential drive (proportional/PID control)
  • Tracks smoothly through straights and curves
  • Stops at stations to load/unload
  • Handles junctions, lost-line and obstacles
  • Demonstrates the core of factory AGVs — cheaply

Real-World Applications

SettingHow it is used
Indoor material transportCarrying small loads along fixed routes (offices, labs, small workshops).
AGV learning platformThe core principle behind factory/warehouse automated guided vehicles.
Robotics educationSensing, differential drive and closed-loop (PID) control in one build.
Fixed-route service robotsRepeated deliveries between known stations.

Deployment contexts where a build of this kind earns its keep.

Features & Capabilities

  • IR line sensing (position/error extraction)
  • Proportional/PID differential-drive steering
  • Station stops for delivery
  • Junction and lost-line handling
  • Obstacle stop (optional front sensor)
  • Simple, robust, map-free autonomy
  • Honest about the limits of line-constrained navigation

Difficulty, Time & Required Skills

AttributeValue
Difficulty levelIntermediate
Estimated completion time10–16 hours
Indicative build cost₹1,500 – ₹2,800
Primary disciplineRobotics
Reference platformArduino Uno R3 (ATmega328P)

Skills you should have (or will pick up)

  • IR reflectance sensing and line-position extraction
  • Differential-drive kinematics
  • Proportional/PID closed-loop control
  • Junction/station/lost-line state logic
  • Motor driver and chassis integration

Bill of Materials

Every part below is commonly available from Indian and international hobby-electronics suppliers. Prices are indicative 2026 retail figures in Indian rupees and will drift — treat them as a budgeting guide, not a quotation.

ComponentKey specificationQtyApprox. cost
Arduino Uno R3 (ATmega328P)
2 KB of SRAM is the real constraint — keep strings in PROGMEM.
16 MHz AVR, 32 KB flash, 2 KB SRAM, 1 KB EEPROM, 14 digital I/O (6 PWM), 6× 10-bit ADC1₹700
TCRT5000 IR reflectance sensor module
Ambient sunlight saturates it — shroud the sensor on outdoor robots.
1–8 mm optimum sensing gap, 950 nm emitter, analogue + digital out1₹45
BO gear motor 300 rpm + wheel
No two BO motors run at the same speed — closed-loop or trim in software.
3–12 V, 300 rpm at 6 V, 0.8 kg·cm, plastic dual-shaft gearbox1₹150
L298N dual H-bridge motor driver
Bipolar transistors drop ~2 V per side — TB6612FNG is far more efficient.
2 × 2 A continuous, 5–35 V motor supply, 4.9 V logic regulator on board1₹180
18650 Li-ion cell 3400 mAh + holder
Never charge below 0 °C; always use a protected cell or a BMS.
3.7 V nominal, 4.2 V full, 3400 mAh, ~12.6 Wh, 2 C discharge1₹450
HC-SR04 ultrasonic distance sensor
Echo pin outputs 5 V — divide it down before feeding a 3.3 V ESP32.
2–400 cm, ±3 mm, 15° beam, 40 kHz, 10 µs trigger pulse1₹90
IR sensor array
More sensors = finer line-position error
5–8 IR reflectance sensors in a row (line position)1₹300
Chassis + wheels + castor2-wheel differential-drive chassis1₹500
Geared DC motorsTwo matched gear motors2₹600
Payload traySmall load platform1₹150

Estimated total: ₹3,165, excluding tools, shipping and consumables.

Tools and consumables

  • Soldering iron (temperature controlled, 350 °C) with 0.8 mm 60/40 or lead-free solder
  • Digital multimeter — continuity, DC volts and current ranges
  • Wire strippers, flush cutters and a small set of precision screwdrivers
  • Heat-shrink tubing and a heat gun (or a lighter, carefully)
  • A laptop with a USB port and the toolchain listed above

Hardware Specifications

PartSpecificationSupplyInterfaceReference
Arduino Uno R3 (ATmega328P)16 MHz AVR, 32 KB flash, 2 KB SRAM, 1 KB EEPROM, 14 digital I/O (6 PWM), 6× 10-bit ADC5 V logic, 7–12 V barrel inUART, SPI, I²CDatasheet
TCRT5000 IR reflectance sensor module1–8 mm optimum sensing gap, 950 nm emitter, analogue + digital out3.3–5 VAnalogue + digitalDatasheet
BO gear motor 300 rpm + wheel3–12 V, 300 rpm at 6 V, 0.8 kg·cm, plastic dual-shaft gearbox3–12 VPWM + H-bridgeDatasheet
L298N dual H-bridge motor driver2 × 2 A continuous, 5–35 V motor supply, 4.9 V logic regulator on board5–35 VIN1–IN4 + 2 PWMDatasheet
18650 Li-ion cell 3400 mAh + holder3.7 V nominal, 4.2 V full, 3400 mAh, ~12.6 Wh, 2 C discharge3.0–4.2 VHolder / spot-welded tabsDatasheet
HC-SR04 ultrasonic distance sensor2–400 cm, ±3 mm, 15° beam, 40 kHz, 10 µs trigger pulse5 VTrigger/Echo digitalDatasheet

Consolidated electrical and interface specifications for every active part in the build.

Power Budget & Supply Sizing

Add up the typical active current of every part, then size the supply with at least 50 % headroom so transmit bursts and motor inrush never brown out the controller.

LoadSupply railTypical current (mA)Notes
Arduino Uno R3 (ATmega328P)5 V logic, 7–12 V barrel in452 KB of SRAM is the real constraint — keep strings in PROGMEM.
TCRT5000 IR reflectance sensor module3.3–5 V20Ambient sunlight saturates it — shroud the sensor on outdoor robots.
BO gear motor 300 rpm + wheel3–12 V200No two BO motors run at the same speed — closed-loop or trim in software.
L298N dual H-bridge motor driver5–35 V36Bipolar transistors drop ~2 V per side — TB6612FNG is far more efficient.
HC-SR04 ultrasonic distance sensor5 V15Echo pin outputs 5 V — divide it down before feeding a 3.3 V ESP32.

Summed typical draw is 316 mA. With a 1.5× design margin the supply should deliver at least 500 mA continuously at the stated rail voltage.

Software Requirements & Development Environment

Reference toolchain: Arduino IDE 2.3.x (AVR core 1.8.6). Anything newer normally works; anything older may lack the board definitions used here.

  • Install the Arduino IDE 2.3.x (or PlatformIO if you prefer a real editor and dependency locking).
  • Select Tools → Board → Arduino AVR Boards and the matching board. Clone boards with a CH340 USB bridge need the CH340 driver on Windows and macOS.
  • Set the correct port under Tools → Port. On Linux add yourself to the dialout group: sudo usermod -aG dialout $USER and log out and back in.
  • Open the Serial Monitor at 115200 baud — every sketch here logs its state there.
  • Keep File → Preferences → Show verbose output during: compilation switched on while you are debugging build errors.

Required libraries

LibraryWhy it is neededInstall
ESP32Servo / Servo 3.0.x50 Hz PWM generation with correct pulse widths for hobby servos.Library Manager → "ESP32Servo" by Kevin Harrington
PID_v1 1.2.1Proportional-integral-derivative controller with anti-windup.Library Manager → "PID" by Brett Beauregard
Preferences (NVS) bundledWear-levelled key/value storage in ESP32 flash for settings.Bundled with the ESP32 core

Block Diagram

The block diagram shows the functional decomposition of the system — what senses, what decides, what acts, and where the data ends up.

Line-Following Delivery Bot — system block diagramFunctional block diagram of the Line-Following Delivery Bot system. Sense lineIR arraypositionErroroff-centreControlPIDsteerDiff drivewheel speedsActLeft motorPWMRight motorPWMDeliverJunction/stationstopObstaclehaltrightrightnone
Line-Following Delivery Bot — system block diagram

Circuit Diagram & Wiring

Every signal line in the build is shown below, followed by a pin-by-pin connection table you can work through with a multimeter in hand.

Line-Following Delivery Bot — wiring schematicConnection schematic showing which controller pin drives each peripheral. Sensors / InputsControllerActuators / OutputsArduino Uno R3(ATmega328P)5 V logic, 7–12 V barrel inIR arrayA0..A5Line positionFront ultrasonicD7/D8Obstacle stopMotor driver LD2/D4/D5Left wheelMotor driver RD3/D9/D6Right wheelBatteryVINPowerStation LED/buzzerD10Arrived
Line-Following Delivery Bot — wiring schematic
PeripheralPeripheral pinController pinSignal
IR arrayS1..S6A0..A5Line position
Front ultrasonicTRIG/ECHOD7/D8Obstacle stop
Motor driver LIN1/IN2/ENAD2/D4/D5Left wheel
Motor driver RIN3/IN4/ENBD3/D9/D6Right wheel
Battery+VINPower
Station LED/buzzerIND10Arrived

Wire one row at a time and tick it off — most "it does not work" reports trace back to a single swapped pair.

Wiring explanation

  • Mount the IR array low and centred at the front, looking straight down at the floor/line.
  • Wire the two motors through an H-bridge driver; ENA/ENB take PWM for speed control.
  • Add a front ultrasonic sensor for an obstacle stop.
  • Keep the wheels matched; mismatch shows up as drift and is corrected in software/trim.
  • Power motors and logic sensibly (motor supply vs logic supply); common ground.
A brushed DC gear motor
An IR reflectance array reads the line's position, and differential-drive PID steering keeps the robot centred. Photograph sourced from Wikimedia Commons — DC motor.jpg. Reused under the licence stated on that page; please check it before republishing.

System Architecture

Read the stack from the bottom up: physical hardware, the firmware that drives it, the transport that moves data off the device, and the software a human actually looks at.

Line-Following Delivery Bot — architecture stackLayered architecture from hardware to user interface. Hardware layerArduino Uno R3 (ATmega328P) · TCRT5000 IR reflectance sensor module ·HC-SR04 ultrasonic distance sensorDriver layerservo · pid · preferencesApplication logicsampling loop · filtering · thresholds · state machinePresentation layerlocal display · serial console · logged output
Line-Following Delivery Bot — architecture stack

Working Principle

Autonomous navigation is hard because a robot generally does not know where it is; line following makes it easy by encoding the route into the environment. The line on the floor is a pre-computed, always-available path, so the robot never needs a map, localisation, or a plan — it needs only to answer one question, continuously: "where is the line relative to me?", and correct. That reduction of a hard problem (navigate) to a simple one (stay centred on a line) is exactly why line-guided AGVs remain the workhorses of factories and warehouses, and why line following is the canonical first autonomous robot.

Answering the question is a sensing task. An array of infrared reflectance sensors looks down at the floor; the line and the floor reflect IR differently (a dark line absorbs, a light floor reflects, or vice versa), so each sensor reports line or not-line. From the pattern across the array the robot computes the line position — a weighted centre of the sensors seeing the line — and hence the error: how far, and which way, the line has drifted from the robot's centre. More sensors give finer error resolution and smoother control; the error signal is the entire input to the controller.

Correcting is a differential-drive control task. With two independently-driven wheels, the robot turns by driving them at different speeds: equal speeds go straight, faster-left turns right, faster-right turns left. The naive approach — full-left or full-right depending on which side the line is on — produces a violent zig-zag. The good approach is proportional control (the heart of PID): steer in proportion to the error, so a small drift gets a gentle correction and a large drift a strong one, which tracks the line smoothly. Adding a derivative term damps overshoot (anticipating the approaching correction) and an integral term removes steady drift; a well-tuned PID line follower glides through curves that make a bang-bang robot flail. This closed loop — sense error, steer proportionally, repeat fast — is the same feedback principle behind almost all robot control.

A delivery bot wraps navigation logic around the follower. It recognises stations and junctions — marked by a cross-line, a tag, or a distinctive sensor pattern — to know where it is along the line, stops to load and unload, and chooses turns at junctions to reach the right destination. It handles the failure modes honestly: a lost line (all sensors off the line) triggers a search or stop rather than a bolt into the unknown, and an obstacle ahead triggers a stop. The design is candid that this is constrained autonomy — the robot goes only where the line goes, a dirty, worn, or broken line breaks it, and real AGVs layer on junction routing, fleet traffic management, and safety systems. But within those honest limits it is both a genuinely useful point-to-point transport robot and the clearest hands-on lesson in sensing, differential-drive kinematics, and closed-loop control that robotics has to offer.

The maths behind it

Line-position error

plainLine-position error
For an array of N sensors at positions x_i (centre = 0),
with s_i = 1 if sensor i sees the line:

  error = ( Σ x_i·s_i ) / ( Σ s_i )     (weighted line centre)

error = 0 centred; sign/magnitude = direction/amount of drift.
All sensors off the line → LINE LOST → search/stop.

Proportional (PID) steering

plainProportional (PID) steering
steer = Kp·error + Ki·∫error dt + Kd·d(error)/dt

Steer in proportion to drift → smooth tracking.
Kd damps overshoot on curves; Ki removes steady bias.

Differential-drive mixing

plainDifferential-drive mixing
left_pwm  = base_speed − steer
right_pwm = base_speed + steer

Equal → straight; steer>0 → turn one way, <0 → the other.
Slow base_speed on tight curves to avoid overshoot.

Program Flowchart

The firmware is a single cooperative loop. Nothing blocks for long, so networking, sensing and the user interface all stay responsive.

Line-Following Delivery Bot — firmware flowchartControl flow through the main program loop. Read IR array → lineposition/errorObstacle ahead?Stop and waitPID: compute steeringStop and waitPID: compute steeringSet differential wheel speedsAt station/junction?Stop / deliver / turnContinue followingStop / deliver / turnContinue following
Line-Following Delivery Bot — firmware flowchart

Assembly Instructions

Build on a breadboard first and only commit to solder once the whole system has run for an hour without a fault.

  1. Build the differential-drive chassis

    Mount two matched gear motors and wheels with a castor, the H-bridge driver, battery, and the IR array low and centred at the front.

    Verify the robot drives straight at equal PWM; trim any mismatch.

  2. Calibrate line sensing

    Calibrate the IR array to the line/floor contrast and extract a clean line-position error.

  3. Tune the PID and add delivery logic

    Tune Kp/Ki/Kd for smooth tracking, then add junction/station stops and lost-line/obstacle handling.

Step-by-Step Implementation Guide

Work through these in order. Each step ends in something you can observe, so a failure is always localised to the step you just finished.

  1. Extract line error and steer with PID

    Compute the weighted line-position error from the IR array and drive the wheels with base speed ± a PID steering term.

    cppline_follow.ino
    const int N = 6;
    const int SPIN[N] = {A0,A1,A2,A3,A4,A5};
    const int W[N] = {-5,-3,-1, 1, 3, 5};   // sensor positions (centre 0)
    
    float Kp=8, Ki=0.0, Kd=40, integ=0, prevErr=0;
    const int BASE = 140;                   // base PWM
    
    // Weighted line-position error; returns 999 if line lost.
    float lineError(){
      long num=0, den=0;
      for (int i=0;i<N;i++){
        int on = analogRead(SPIN[i]) < 500;  // sees the line?
        if (on){ num += W[i]; den += 1; }
      }
      if (den==0) return 999;                // LINE LOST
      return (float)num/den;
    }
    
    void loop(){
      float e = lineError();
      if (e==999){ recoverLostLine(); return; }
      integ += e;
      float steer = Kp*e + Ki*integ + Kd*(e-prevErr);
      prevErr = e;
      drive(BASE - steer, BASE + steer);     // diff-drive: turn toward line
    }
    return (float)num/den; // weighted line centreThe error is the weighted centre of the sensors seeing the line — a smooth, signed measure of how far and which way the robot has drifted.
    if (den==0) return 999; // LINE LOSTWhen no sensor sees the line the robot recovers deliberately instead of bolting into the unknown — honest handling of a broken/dirty line.
    float steer = Kp*e + Ki*integ + Kd*(e-prevErr);Proportional-plus-derivative steering: correct in proportion to drift and damp overshoot, which is what tracks curves smoothly instead of zig-zagging.
    drive(BASE - steer, BASE + steer); // diff-drive: turn toward lineDifferential mixing turns the single steering term into two wheel speeds — the essence of differential-drive control.
  2. Handle junctions, stations and obstacles

    Detect junction/station markers to stop, deliver or choose a turn, and stop for obstacles — the delivery logic on top of the follower.

Complete Source Code

The listing below is complete and compiles as written — there are no elided sections. Read the annotations under each block before you upload it.

cppdelivery_bot.ino
/* ═══════════════════════════════════════════════════════════════
   Line-Following Delivery Bot

   Follows a marked line by differential-drive PID steering, carrying a
   small load between stations. Detects junctions/stations, stops for
   obstacles, and recovers deliberately if the line is lost.
   Constrained (line-guided) autonomy — the AGV principle, cheaply.
   ══════════════════════════════════════════════════════════════════ */

const int N = 6;
const int SPIN[N] = {A0,A1,A2,A3,A4,A5};
const int W[N] = {-5,-3,-1,1,3,5};
const int BASE = 140, TRIG=7, ECHO=8;
float Kp=8, Kd=40, prevErr=0;

int readArray(int* on){                  // fill on[], return count seeing line
  int c=0;
  for (int i=0;i<N;i++){ on[i]=analogRead(SPIN[i])<500; c+=on[i]; }
  return c;
}
float lineError(int* on){
  long num=0,den=0;
  for (int i=0;i<N;i++) if(on[i]){ num+=W[i]; den++; }
  return den? (float)num/den : 999;
}
bool junction(int count){ return count>=N-1; }   // most sensors on = cross-line
long obstacleCm(){ /* HC-SR04 ping */ return pingCm(TRIG,ECHO); }

void drive(int l,int r){ setMotor(LEFT,l); setMotor(RIGHT,r); }

void setup(){ motorInit(); Serial.begin(115200); }

void loop(){
  if (obstacleCm() < 15){ drive(0,0); return; }    // obstacle: stop & wait

  int on[N]; int count = readArray(on);
  float e = lineError(on);

  if (junction(count)){                            // station/junction
    drive(0,0); handleStation(); return;           // stop / deliver / turn
  }
  if (e==999){ recoverLostLine(); return; }        // line lost: search, don't bolt

  float steer = Kp*e + Kd*(e-prevErr);             // PID steering
  prevErr = e;
  int base = fabs(e)>3 ? BASE-40 : BASE;           // slow on tight drift/curves
  drive(base - steer, base + steer);               // differential drive
}
if (obstacleCm() < 15){ drive(0,0); return; } // obstacle: stop & waitSafety first: an obstacle ahead halts the robot before it collides, checked every loop.
bool junction(int count){ return count>=N-1; } // most sensors on = cross-lineA junction/station is recognised when most sensors see the line at once (a cross-line), telling the robot where it is along the route.
if (e==999){ recoverLostLine(); return; } // line lost: search, don't boltA lost line triggers a deliberate recovery, not a dash into the unknown — honest handling of the follower's main failure mode.
float steer = Kp*e + Kd*(e-prevErr); // PID steeringProportional-derivative steering keeps tracking smooth and damped through curves.
int base = fabs(e)>3 ? BASE-40 : BASE; // slow on tight drift/curvesSlowing the base speed on large errors gives the loop time to react on tight curves, preventing overshoot.

Configuration & Calibration

Configuration steps

  • Configure the IR array pins, line/floor threshold and sensor weights.
  • Configure base speed and PID gains (Kp/Ki/Kd).
  • Configure junction/station detection and delivery actions.
  • Configure obstacle-stop distance and lost-line recovery.

Calibration procedure

An uncalibrated sensor produces confident, precise, wrong numbers. Do this once per physical unit and record the constants.

  1. IR array

    Calibrate the line/floor threshold under the actual lighting so each sensor cleanly reports line vs floor.

  2. PID

    Raise Kp until it tracks but starts to wobble, then add Kd to damp; keep Ki small. Slow the base speed on curves.

  3. Drive trim

    Trim wheel PWM so the robot runs straight at zero error, removing mechanical bias.

Mechanical Assembly & Motion

Mechanical assembly

  • Two-wheel differential-drive chassis with a front IR sensor array and a rear castor for balance.
  • Motors matched and mounted rigidly; wheels aligned so the robot runs straight at equal PWM.
  • IR array mounted low and centred at the front, looking straight down at a fixed height above the floor.
  • A small payload tray over the chassis centre of mass so loads do not upset steering.

Motion logic

Motion is pure differential drive: the two wheels' speed difference sets the turn rate while their average sets forward speed. Steering the line follower is nothing more than adding a PID steering term to one wheel and subtracting it from the other.

On straights the error is near zero and both wheels run at base speed; on curves the sustained error drives a steady speed difference that arcs the robot around the bend; at a sharp junction the base speed is reduced so the loop has time to react.

StateLeft motorRight motorResult
Line centredBase speedBase speedDrive straight
Line drifts leftFasterSlowerSteer left (back to line)
Line drifts rightSlowerFasterSteer right (back to line)
Sharp left junctionReverse/stopForwardPivot left
Line lostStop/searchStop/searchRecover, don't bolt
Obstacle / stationStopStopHalt / deliver

Sensor integration

  • IR reflectance array (5–8 sensors): reports line position/error under the robot.
  • Front ultrasonic (optional): obstacle stop before collision.
  • Junction/station markers: cross-lines or tags detected as distinctive array patterns.

Actuator explanation

Two geared DC motors via an H-bridge driver, PWM-controlled for speed. The controller sets each wheel's PWM from base speed ± the PID steering term.

A buzzer/LED signals arrival at a station; a servo can actuate a simple load release if fitted.

Kinematics

Differential-drive kinematics relate the two wheel speeds to the robot's forward and turning motion, which is all the steering law needs.

plainkinematics
v     = (v_R + v_L) / 2          # forward speed (avg of wheels)
omega = (v_R − v_L) / L          # turn rate (difference / wheel base)

Steering law:
  v_L = base − steer,   v_R = base + steer
  → omega ∝ steer ∝ line error   (turn toward the line)

Testing Procedure & Expected Output

Test from the bottom up. Confirm power, then each sensor in isolation, then the integrated loop — the first failing step tells you exactly where to look.

TestWhat you should see
Place on a straight lineTracks straight without wobble
Introduce a gentle curveFollows smoothly (PID)
Add a sharp turn/junctionDetects junction; turns/stops correctly
Lift off the lineLine-lost recovery — does not bolt
Place an obstacle aheadStops and waits
Load the trayDelivers between stations reliably

Bench-test checklist. If a row fails, stop and fix it before moving on.

Expected output

A robot that follows the line smoothly, stops at stations to deliver, and handles junctions, lost line and obstacles.

plainfollower-state.txt
line_error: -1.0   (slightly left)
steer:      -48
left_pwm:   188   right_pwm: 92
state:      FOLLOWING
next:       station B (junction in 0.4 m)

The robot is a touch left of the line and steering back smoothly, approaching station B — the closed loop at work.

An 18650 lithium-ion cell
Two geared motors through an H-bridge provide the differential drive that steers by wheel-speed difference. Photograph sourced from Wikimedia Commons — 18650 Li-ion battery.jpg. Reused under the licence stated on that page; please check it before republishing.

Troubleshooting: Common Errors & Fixes

Zig-zags / wobbles

Likely cause. Kp too high / no Kd / too fast

Fix. Lower Kp, add Kd, reduce base speed

Drifts off on curves

Likely cause. Too fast / Kp too low

Fix. Slow on curves; raise Kp; add more sensors

Veers at zero error

Likely cause. Wheel mismatch

Fix. Trim wheel PWM; match motors

Loses line often

Likely cause. Threshold/lighting/array height

Fix. Recalibrate threshold; fix array height; shield ambient light

Misses junctions

Likely cause. Detection logic/speed

Fix. Tune junction pattern; slow near junctions

The sketch will not upload — "Failed to connect" or "avrdude: stk500_recv()"

Likely cause. The bootloader is not being reached: wrong port, wrong board, a serial monitor holding the port open, or a USB cable that only carries power.

Fix. Close every serial monitor, confirm Tools → Board and Port, and swap to a known data-capable USB cable. On an ESP32 hold BOOT while the IDE prints "Connecting…", then release. If a peripheral is wired to the UART pins (GPIO 1/3 on ESP32, D0/D1 on Uno) unplug it — it fights the programmer.

The board resets in a loop, or the serial monitor prints "Brownout detector was triggered"

Likely cause. The supply cannot deliver peak current. Wi-Fi transmit bursts, relay coils and servos all pull far more than their average draw.

Fix. Power peripherals from a separate regulated supply with a common ground rather than from the board 5 V pin. Add a 470–1000 µF electrolytic capacitor across the supply near the load, and use a real power adapter rather than a laptop USB port.

Serial monitor shows garbage characters

Likely cause. Baud rate mismatch between Serial.begin() and the monitor, or a floating/shared UART line.

Fix. Set the monitor to 115200 to match the sketch. If it still garbles, the crystal or the USB bridge is being confused by noise — shorten the cable and keep motor wiring away from the USB lead.

Performance Optimisation

  • Extract a smooth weighted line error; more sensors = finer control.
  • Proportional-plus-derivative steering for smooth curve tracking.
  • Slow the base speed on curves/junctions to avoid overshoot.
  • Recover a lost line deliberately; stop for obstacles.
  • Replace every delay() with a millis() comparison — blocking delays are the single most common cause of dropped readings.
  • Sample sensors on a fixed cadence and publish on a slower one; you almost never need to transmit at the sampling rate.
  • Wrap constant strings in F("...") so they stay in flash — an Uno only has 2 KB of SRAM.
  • Use uint8_t / uint16_t where the range allows; on an 8-bit AVR a 32-bit add costs four times as much.
  • Profile before optimising — print micros() deltas around each stage and fix the slowest one first.

Safety Precautions

  • Keep speeds modest indoors; a fast robot that loses the line can collide or fall.
  • Add an obstacle stop so the robot halts rather than pushing into people/objects.
  • Secure the payload so it cannot shift and upset steering or fall off.
  • This is constrained autonomy — do not rely on it where a broken line could cause harm without a safe fallback.
  • Lithium cells vent and burn when abused. Only use protected cells or a proper BMS, never charge below 0 °C, and never leave a charging pack unattended on a wooden desk.
  • Moving parts pinch. Keep fingers, cables and hair out of gear trains and wheels, and always test motion with the drivetrain unloaded and the robot on blocks first.
  • Motors are inductive — always fit a flyback diode across a DC coil, or use a driver that already has one, or the back-EMF spike will destroy your GPIO.
  • Wear eye protection when soldering or cutting, and solder in a ventilated space — rosin flux fumes are a respiratory irritant.
  • Power the circuit through a bench supply with a current limit while you are testing. A 300 mA limit turns a wiring mistake into a beep instead of a dead board.
  • Disconnect power before changing any wiring. Hot-plugging a sensor onto a live bus is the fastest way to lose a controller.

Maintenance

  • Keep the line clean and unbroken; a worn line degrades tracking.
  • Clean the IR array and re-calibrate for lighting/floor changes.
  • Check wheels/motors for wear that introduces drift.
  • Re-tune PID if speed, load or surface changes.
  • Re-check every screw terminal and header after the first week — thermal cycling loosens connections that felt tight on day one.
  • Log pack voltage. When resting voltage after a full charge drops below about 4.0 V, the cell is near end of life — replace it.
  • Listen for gear chatter. A servo that buzzes at rest is either stripped or fighting a mechanical bind — fix it before it burns out.
  • Recalibrate at the interval given in the calibration section, and keep the constants in a text file next to the firmware — not only in flash.
  • Keep a short logbook of firmware versions and what changed. Six months later you will not remember why that constant is 1.083.

Future Improvements & Upgrades

A working v1 is a platform, not a finish line. These are the upgrades that add the most capability for the least rework.

  • Add encoders for odometry and precise station positioning.
  • Add junction routing (choose turns to reach a named destination).
  • Add fleet/traffic management for multiple bots on shared lines.
  • Upgrade to magnetic-tape or camera-based line following for robustness.
  • Design a proper PCB. Once the breadboard version has run for a month, moving to a two-layer board removes the intermittent-contact failures that dominate prototype faults.
  • Add connectivity — an ESP32 and an MQTT publish turn a local gadget into something you can graph, alert on and analyse over months.
  • Add persistent local storage (microSD or the on-chip flash) so a network outage does not create a hole in your data.
  • Move configuration out of the source: a captive-portal setup page or a JSON config file makes the build reusable without a recompile.
  • Add a battery and solar option so the unit survives a power cut and can be sited away from a socket.
  • Write a small test harness that feeds synthetic sensor values through the decision logic, so you can validate thresholds without physically triggering the event.

Frequently Asked Questions

Why follow a line instead of navigating freely?

Because the line encodes the route into the environment, so the robot needs no map, localisation or planning — just "stay on the line". It is the cheapest reliable autonomy and the principle behind factory AGVs.

Why use PID rather than just turning left/right?

Bang-bang steering (full-left or full-right) makes the robot zig-zag violently. Proportional control steers in proportion to the drift — gentle for small errors, strong for large — and derivative damping tracks curves smoothly.

What is differential drive?

Steering by driving two wheels at different speeds: equal for straight, faster on one side to turn. The steering term is added to one wheel and subtracted from the other.

What happens if it loses the line?

It recovers deliberately — searching for the line or stopping — rather than bolting off in a random direction. A dirty or broken line is the follower's main failure mode and must be handled honestly.

Is this how real AGVs work?

The core is the same — many industrial AGVs follow lines or magnetic tape. Real systems add junction routing, fleet traffic management and safety systems on top of the same follow-the-guide principle.

References & Learning Resources

These are the primary sources worth reading in full. Manufacturer datasheets always outrank forum posts when the two disagree.

  1. Automated guided vehicleReference
  2. Line-following robotReference
  3. PID controllerReference
  4. Differential wheeled robotReference
  5. Infrared reflectance sensingReference