A line-following robot is a self-driving machine that detects a black line on a white surface and steers itself along the path. It is, without question, the single best first robotics project you can build. The concept is simple enough for a complete beginner, yet the same project scales all the way up to PID control algorithms used in industrial automation. You will solder, wire, code, and debug — every core skill in robotics, packed into one build.
This guide takes you from zero to a competition-ready line follower. We start with a basic two-sensor design using simple if-else logic, improve it with PWM speed control, and then implement a full PID controller for smooth, fast line tracking. Every line of code is explained. Every wire is mapped. Let us build.
How a Line-Following Robot Works
The principle is straightforward. IR (infrared) sensors mounted on the bottom of the robot point downward at the surface. A white surface reflects IR light back to the sensor; a black line absorbs it. The sensor reports this difference as a digital HIGH/LOW signal or an analog voltage.
The robot has two DC motors — one for the left wheel, one for the right. The Arduino reads the sensors and decides:
- Both sensors on white (line is between them): drive both motors forward.
- Left sensor on black: the robot has drifted right of the line. Turn left by slowing or stopping the left motor.
- Right sensor on black: the robot has drifted left. Turn right by slowing or stopping the right motor.
- Both sensors on black: the robot is at a junction or a wide section of line. Handle according to your strategy.
That is the entire algorithm. Everything else — PWM, PID, sensor arrays — is refinement of this core idea.
Components List
Here is everything you need. All parts are commonly available across India, and you can find them on wavtron.in.
| Component | Quantity | Approx. Price |
|---|---|---|
| Arduino Nano (CH340 USB) | 1 | ~249 |
| L298N Motor Driver Module | 1 | ~139 |
| TCRT5000 IR Line Sensor Module | 2 | ~49 each |
| BO Motor (DC Geared Motor, 150 RPM) | 2 | ~59 each |
| BO Motor Wheel (65mm) | 2 | ~29 each |
| Robot Chassis (acrylic, 2WD) | 1 | ~149 |
| Caster Wheel (ball type) | 1 | ~29 |
| 18650 Battery Holder (2-cell) | 1 | ~35 |
| 18650 Li-ion Batteries (3.7V) | 2 | ~99 each |
| Jumper Wires (M-M, M-F) | 1 set | ~49 |
| Mini Breadboard (optional) | 1 | ~29 |
| Black electrical tape (for track) | 1 roll | ~20 |
Total cost: approximately 1,100 to 1,300 rupees. That is an entire autonomous robot for less than the price of a meal out. Hard to beat.
Understanding IR Line Sensors (TCRT5000)
The TCRT5000 module is the workhorse of line-following robots in India. It contains:
- IR LED (emitter): shines infrared light downward onto the surface.
- Phototransistor (receiver): detects reflected IR light.
- Comparator circuit (on the module): converts the analog signal into a clean digital HIGH or LOW output via an onboard potentiometer threshold.
Digital vs Analog Output
Most TCRT5000 modules have both a D0 (digital) and an A0 (analog) output pin.
- Digital output (D0): gives a clean 0 or 1. Easier to use but less precise. The onboard potentiometer sets the threshold.
- Analog output (A0): gives a value from 0 to 1023 on the Arduino's ADC. White surfaces produce low values (high reflection), black surfaces produce high values (low reflection). This is what you want for PID control.
Mounting Height
Mount the sensor 5 to 12 mm above the surface. Too high and it cannot distinguish black from white. Too low and it may scrape the ground. 8mm is a good starting point.
Calibrating the Threshold
If using digital output, turn the potentiometer with a small screwdriver while holding the sensor over the black line. The onboard LED should toggle right at the boundary between black and white.
If using analog output, read values over black and white, then set your threshold in code as the midpoint. For example, if white reads 150 and black reads 800, set threshold at 475.
L298N Motor Driver: How It Works
The Arduino's pins can supply only about 40mA. A BO motor draws 100-250mA. You need a motor driver to act as a power amplifier between the Arduino and the motors.
The L298N uses an H-bridge circuit — four transistors arranged in an H pattern around the motor. By activating different pairs, current flows through the motor in either direction, giving you forward and reverse control.
L298N Pin Reference
| Pin | Function |
|---|---|
| IN1, IN2 | Direction control for Motor A |
| IN3, IN4 | Direction control for Motor B |
| ENA | Enable/PWM speed control for Motor A |
| ENB | Enable/PWM speed control for Motor B |
| OUT1, OUT2 | Motor A output terminals |
| OUT3, OUT4 | Motor B output terminals |
| +12V | Motor power supply input (7-12V) |
| GND | Common ground |
| +5V | 5V regulated output (when jumper is on) |
Key detail: The ENA and ENB pins have jumpers on them by default. Remove these jumpers and connect ENA/ENB to Arduino PWM pins. This allows you to control motor speed using analogWrite(), not just on/off.
Direction Truth Table
| IN1 | IN2 | Motor A |
|---|---|---|
| HIGH | LOW | Forward |
| LOW | HIGH | Reverse |
| LOW | LOW | Stop (coast) |
| HIGH | HIGH | Brake |
Wiring Diagram
Arduino Nano to L298N Motor Driver
| Arduino Pin | L298N Pin | Purpose |
|---|---|---|
| D5 (PWM) | ENA | Left motor speed |
| D6 (PWM) | ENB | Right motor speed |
| D7 | IN1 | Left motor direction |
| D8 | IN2 | Left motor direction |
| D9 | IN3 | Right motor direction |
| D10 | IN4 | Right motor direction |
| GND | GND | Common ground |
Arduino Nano to IR Sensors
| Arduino Pin | Sensor Pin | Purpose |
|---|---|---|
| A0 | Left sensor A0 | Analog reading (for PID) |
| A1 | Right sensor A0 | Analog reading (for PID) |
| D2 | Left sensor D0 | Digital reading (for basic code) |
| D3 | Right sensor D0 | Digital reading (for basic code) |
| 5V | Both sensor VCC | Power |
| GND | Both sensor GND | Ground |
Power
Connect the two 18650 batteries in series (7.4V) to the L298N's +12V and GND terminals. With the onboard jumper in place, the L298N's +5V output pin can power the Arduino Nano through its 5V pin. This means one battery pack runs everything.
Warning: Never connect the battery to the Arduino's VIN and the L298N's 5V to the Arduino's 5V simultaneously. Pick one power path.
Code: Basic Line Follower (Digital Sensors, If-Else Logic)
This is the simplest possible line follower. Two sensors, four states, if-else decisions. The robot either goes straight, turns left, turns right, or stops.
// Basic Line Follower - Digital Sensors
// Wavtron.in
// Motor pins
#define ENA 5
#define ENB 6
#define IN1 7
#define IN2 8
#define IN3 9
#define IN4 10
// Sensor pins (digital)
#define LEFT_SENSOR 2
#define RIGHT_SENSOR 3
// Motor speed (0-255)
#define MOTOR_SPEED 150
void setup() {
// Motor pins as output
pinMode(ENA, OUTPUT);
pinMode(ENB, OUTPUT);
pinMode(IN1, OUTPUT);
pinMode(IN2, OUTPUT);
pinMode(IN3, OUTPUT);
pinMode(IN4, OUTPUT);
// Sensor pins as input
pinMode(LEFT_SENSOR, INPUT);
pinMode(RIGHT_SENSOR, INPUT);
// Set motor speed
analogWrite(ENA, MOTOR_SPEED);
analogWrite(ENB, MOTOR_SPEED);
Serial.begin(9600);
}
void loop() {
int leftVal = digitalRead(LEFT_SENSOR);
int rightVal = digitalRead(RIGHT_SENSOR);
// Sensor reads HIGH on black, LOW on white
// (Check your module — some are inverted)
if (leftVal == LOW && rightVal == LOW) {
// Both on white: line is between sensors, go forward
moveForward();
}
else if (leftVal == HIGH && rightVal == LOW) {
// Left sensor on black: line is to the left, turn left
turnLeft();
}
else if (leftVal == LOW && rightVal == HIGH) {
// Right sensor on black: line is to the right, turn right
turnRight();
}
else {
// Both on black: stop or go forward (depends on track design)
stopMotors();
}
}
void moveForward() {
digitalWrite(IN1, HIGH);
digitalWrite(IN2, LOW);
digitalWrite(IN3, HIGH);
digitalWrite(IN4, LOW);
}
void turnLeft() {
digitalWrite(IN1, LOW); // Left motor stop
digitalWrite(IN2, LOW);
digitalWrite(IN3, HIGH); // Right motor forward
digitalWrite(IN4, LOW);
}
void turnRight() {
digitalWrite(IN1, HIGH); // Left motor forward
digitalWrite(IN2, LOW);
digitalWrite(IN3, LOW); // Right motor stop
digitalWrite(IN4, LOW);
}
void stopMotors() {
digitalWrite(IN1, LOW);
digitalWrite(IN2, LOW);
digitalWrite(IN3, LOW);
digitalWrite(IN4, LOW);
}
Upload this and place the robot on a black-tape track. It should follow the line. The movement will be jerky — the robot overshoots, corrects, overshoots again. This zigzag behavior is normal for bang-bang control. We fix it next.
Code: Improved with PWM Speed Control
Instead of fully stopping one motor to turn, we slow it down using PWM. This produces smoother curves.
// Improved Line Follower - PWM Speed Control
// Wavtron.in
#define ENA 5
#define ENB 6
#define IN1 7
#define IN2 8
#define IN3 9
#define IN4 10
#define LEFT_SENSOR 2
#define RIGHT_SENSOR 3
#define BASE_SPEED 180 // Normal forward speed
#define TURN_SPEED 60 // Slow wheel speed during turns
#define SHARP_TURN_SPEED 0 // For sharp corners
void setup() {
pinMode(ENA, OUTPUT);
pinMode(ENB, OUTPUT);
pinMode(IN1, OUTPUT);
pinMode(IN2, OUTPUT);
pinMode(IN3, OUTPUT);
pinMode(IN4, OUTPUT);
pinMode(LEFT_SENSOR, INPUT);
pinMode(RIGHT_SENSOR, INPUT);
// Set both motors to forward direction
digitalWrite(IN1, HIGH);
digitalWrite(IN2, LOW);
digitalWrite(IN3, HIGH);
digitalWrite(IN4, LOW);
Serial.begin(9600);
}
void loop() {
int leftVal = digitalRead(LEFT_SENSOR);
int rightVal = digitalRead(RIGHT_SENSOR);
if (leftVal == LOW && rightVal == LOW) {
// On track — full speed ahead
analogWrite(ENA, BASE_SPEED);
analogWrite(ENB, BASE_SPEED);
}
else if (leftVal == HIGH && rightVal == LOW) {
// Drifting right, correct left
analogWrite(ENA, TURN_SPEED); // Slow left motor
analogWrite(ENB, BASE_SPEED); // Right motor full
}
else if (leftVal == LOW && rightVal == HIGH) {
// Drifting left, correct right
analogWrite(ENA, BASE_SPEED); // Left motor full
analogWrite(ENB, TURN_SPEED); // Slow right motor
}
else {
// Both on black — sharp turn or stop
analogWrite(ENA, SHARP_TURN_SPEED);
analogWrite(ENB, SHARP_TURN_SPEED);
}
}
This version is noticeably smoother. The robot no longer stutters at every correction. But with only two sensors giving binary readings, we have no sense of how far off the line we are. Enter PID.
Code: PID Control for Smooth Line Following
PID stands for Proportional-Integral-Derivative. It is a control algorithm that calculates how much correction to apply based on three factors:
- Proportional (P): Correction proportional to the current error. If you are far off the line, turn hard. If slightly off, turn gently.
- Integral (I): Correction based on accumulated past error. If the robot has been slightly off-center for a long time, increase correction. Eliminates steady-state offset.
- Derivative (D): Correction based on the rate of change of error. If the error is growing fast, apply extra correction to prevent overshoot. Acts as a damper.
The formula:
correction = (Kp * error) + (Ki * integral) + (Kd * derivative)
For this to work, we need analog sensor readings so we can calculate a continuous error value, not just left/right.
Error Calculation with Two Analog Sensors
Read both sensors as analog values (0-1023). Compute the error as the difference:
error = leftSensorValue - rightSensorValue
- When centered on the line: both sensors read similar values, error is near 0.
- When drifting left: right sensor sees more black (higher value), error becomes negative.
- When drifting right: left sensor sees more black (higher value), error becomes positive.
Full PID Code
// PID Line Follower - Analog Sensors
// Wavtron.in
#define ENA 5
#define ENB 6
#define IN1 7
#define IN2 8
#define IN3 9
#define IN4 10
#define LEFT_SENSOR A0
#define RIGHT_SENSOR A1
// PID constants — TUNE THESE for your robot
float Kp = 0.4;
float Ki = 0.0;
float Kd = 0.8;
// Speed settings
int baseSpeed = 150;
int maxSpeed = 255;
int minSpeed = 0;
// PID variables
float error = 0;
float previousError = 0;
float integral = 0;
float derivative = 0;
float correction = 0;
void setup() {
pinMode(ENA, OUTPUT);
pinMode(ENB, OUTPUT);
pinMode(IN1, OUTPUT);
pinMode(IN2, OUTPUT);
pinMode(IN3, OUTPUT);
pinMode(IN4, OUTPUT);
// Both motors forward
digitalWrite(IN1, HIGH);
digitalWrite(IN2, LOW);
digitalWrite(IN3, HIGH);
digitalWrite(IN4, LOW);
Serial.begin(9600);
delay(1000); // Give yourself time to place the robot
}
void loop() {
int leftVal = analogRead(LEFT_SENSOR);
int rightVal = analogRead(RIGHT_SENSOR);
// Calculate error
error = leftVal - rightVal;
// PID terms
integral += error;
derivative = error - previousError;
correction = (Kp * error) + (Ki * integral) + (Kd * derivative);
// Prevent integral windup
integral = constrain(integral, -1000, 1000);
// Apply correction to motor speeds
int leftSpeed = baseSpeed + correction;
int rightSpeed = baseSpeed - correction;
// Constrain speeds to valid PWM range
leftSpeed = constrain(leftSpeed, minSpeed, maxSpeed);
rightSpeed = constrain(rightSpeed, minSpeed, maxSpeed);
// Drive motors
analogWrite(ENA, leftSpeed);
analogWrite(ENB, rightSpeed);
// Store error for next iteration
previousError = error;
// Debug output (comment out for competition)
Serial.print("L:");
Serial.print(leftVal);
Serial.print(" R:");
Serial.print(rightVal);
Serial.print(" Err:");
Serial.print(error);
Serial.print(" Cor:");
Serial.println(correction);
delay(10); // Small delay for stability
}
The difference is dramatic. A well-tuned PID line follower glides along the line with almost no visible oscillation.
Tuning PID Constants: Kp, Ki, Kd
PID tuning is part science, part art. Here is a reliable method:
Step 1: Start with Kp Only
Set Ki = 0 and Kd = 0. Start Kp at a low value like 0.1. Increase it gradually until the robot follows the line but oscillates (zigzags) noticeably. This is your starting Kp.
Step 2: Add Kd to Reduce Oscillation
Increase Kd from 0.0 in small increments (try 0.5, 1.0, 2.0). Kd dampens the oscillation. When the robot follows the line smoothly without zigzag, you have found a good Kd. If the robot becomes sluggish and unresponsive, Kd is too high.
Step 3: Add Ki if Needed
Ki is often not necessary for a line follower on a clean track. If the robot consistently drifts to one side (due to motor mismatch), add a tiny Ki value like 0.001. Too much Ki causes the robot to wind up and oscillate wildly.
Quick Reference
| Symptom | Fix |
|---|---|
| Robot oscillates/zigzags quickly | Decrease Kp or increase Kd |
| Robot responds too slowly to curves | Increase Kp |
| Robot overshoots on curves then corrects | Increase Kd |
| Robot drifts to one side consistently | Add small Ki |
| Robot oscillates and gets worse over time | Decrease Ki, add integral windup limit |
Typical starting values for a 2-sensor BO motor robot: Kp = 0.3 to 0.6, Ki = 0.0, Kd = 0.5 to 2.0.
Designing a Good Test Track
Your track matters as much as your code. Use 19mm black electrical tape on a smooth white surface (white chart paper, white laminate table, or white floor tiles).
Track Design Tips
- Start with a simple oval (large radius curves). Get the basics working first.
- Add tighter curves gradually. A well-tuned PID should handle 5cm radius curves.
- Include one right angle turn (90 degrees) to test recovery.
- Add an S-curve (two opposing curves in quick succession) to test derivative response.
- Keep the track width consistent. One strip of tape (19mm) is standard for competitions.
- Ensure lighting is even. Shadows and bright spots confuse IR sensors.
- Tape the paper/track down firmly. Ripples cause inconsistent sensor readings.
Advanced: Multi-Sensor Arrays (3 and 5 Sensors)
Two sensors work fine for simple tracks. For competitions and complex tracks with intersections, you want more sensors.
3-Sensor Array
Add a center sensor. Now you have:
| Left | Center | Right | State |
|---|---|---|---|
| W | B | W | Perfectly centered |
| B | B | W | Slightly left of center |
| W | B | B | Slightly right of center |
| B | W | W | Far left, hard turn right |
| W | W | B | Far right, hard turn left |
| B | B | B | Intersection or T-junction |
| W | W | W | Lost the line |
5-Sensor Array
This is competition standard. Sensors are labeled S1 (far left) through S5 (far right). Calculate a weighted position value:
int sensorValues[5];
int weights[5] = {-2, -1, 0, 1, 2};
float position = 0;
int sum = 0;
for (int i = 0; i < 5; i++) {
sensorValues[i] = analogRead(A0 + i);
// Normalize: 1 if on line, 0 if off
int onLine = (sensorValues[i] > threshold) ? 1 : 0;
position += weights[i] * onLine;
sum += onLine;
}
if (sum > 0) {
position = position / sum; // Weighted average: -2.0 to +2.0
}
// Use 'position' as your PID error input
This gives a continuous error value from -2.0 (far left) to +2.0 (far right), with 0.0 being perfectly centered. Feed this directly into your PID loop.
Handling Intersections
Intersections are where beginner robots fail and competition robots prove themselves.
T-Junction (line branches left and right)
All sensors read black simultaneously. Strategy:
- Always turn left: simple, deterministic. Use the "left-hand rule" for maze solving.
- Always turn right: same idea, opposite direction.
- Follow stored path: for maze-solving competitions, store a sequence of decisions and optimize on subsequent runs.
Crossroads (line goes in four directions)
All sensors read black. Drive forward for a fixed distance (50-100ms at current speed), then check sensors again. If the line continues ahead, keep going. Otherwise, make a turn based on your strategy.
Dead End (line stops)
All sensors read white. The robot has lost the line. Execute a 180-degree turn: run one motor forward and one backward for a calibrated duration.
void uTurn() {
// Spin in place: left motor reverse, right motor forward
digitalWrite(IN1, LOW);
digitalWrite(IN2, HIGH);
digitalWrite(IN3, HIGH);
digitalWrite(IN4, LOW);
analogWrite(ENA, 180);
analogWrite(ENB, 180);
delay(600); // Adjust timing for your robot
// Resume normal line-following
}
Indian Robotics Competitions
Once your line follower works well, consider entering it in competitions. India has a strong robotics competition scene:
- e-Yantra (IIT Bombay): One of India's largest embedded systems and robotics competitions. Free to enter, national level, with mentoring and prizes.
- Robocon (ABU Asia-Pacific): University-level competition with a different theme each year. Teams design and build robots for specific tasks.
- Technoxian: Annual robotics championship with line follower as a regular category. Held in Delhi.
- College techfests: IIT Bombay (Techfest), IIT Delhi (Tryst), IIT Kanpur (Techkriti), BITS Pilani (APOGEE), and many others host line-follower events. These are excellent starting points.
Competition line follower tracks typically use 20mm black tape on white surfaces, with 90-degree turns, acute angles, broken lines (gaps in the line the robot must bridge), and intersection mazes. Start practicing with complex tracks early.
Common Problems and Solutions
| Problem | Cause | Solution |
|---|---|---|
| Robot does not detect the line | Sensor mounted too high, or wrong threshold | Lower sensor to 5-8mm from surface. Recalibrate potentiometer. |
| Robot always turns one direction | One sensor is miscalibrated, or wires are swapped | Read both sensors in Serial Monitor. Swap wires if sensor logic is inverted. |
| Robot oscillates wildly | Kp too high or sensor noise | Reduce Kp. Add a small delay(5) in loop. Ensure even lighting. |
| One motor is faster than the other | Manufacturing variation in BO motors | Multiply the faster motor's speed by a correction factor (e.g., 0.9). |
| Robot works then slows/stops | Battery voltage dropping | Use fully charged 18650 cells. Consider a 3-cell pack for more headroom. |
| Robot flies off sharp curves | Speed too high for turning radius | Reduce baseSpeed. Increase Kd to anticipate curves. |
| Robot stops at intersections | Both-on-black case triggers stop | Change the both-on-black handler to drive forward briefly instead of stopping. |
| Sensors give inconsistent readings | Ambient light interference (sunlight, fluorescent) | Test in consistent lighting. Add a shroud/tube around each sensor. |
Upgrading Your Line Follower
Once the basic line follower works reliably, these are natural next steps:
Bluetooth Control (HC-05 Module)
Add an HC-05 Bluetooth module to start/stop the robot remotely, switch between manual and autonomous modes, or tune PID constants in real time from a phone app without re-uploading code.
Obstacle Avoidance (HC-SR04 Ultrasonic Sensor)
Mount an ultrasonic distance sensor on the front. If an obstacle is detected within 15cm, stop, wait, or navigate around it. This combines line-following with reactive obstacle avoidance — a common competition challenge.
Maze Solving
Use intersection detection and the left-hand rule or Tremaux's algorithm to navigate a maze. Store the path as an array of decisions (Left, Right, Straight, U-turn). After the first run, simplify the path by removing dead ends, and the second run will be optimal.
Speed Optimization
For competition speed runs, increase baseSpeed and rely heavily on PID tuning (particularly Kd) to keep the robot on track at high speeds. Use encoders on the motors for precise speed measurement and closed-loop motor control. Replace BO motors with N20 metal-geared motors for higher speed and better torque consistency.
Sensor Upgrade
Replace individual TCRT5000 modules with a QTR-8A reflectance sensor array (Pololu-style) for 8 sensors in a neat line. This gives superb resolution for PID control and reliable intersection detection.
Wrapping Up
A line-following robot teaches you sensor reading, motor control, power management, control theory, and debugging — the five pillars of robotics. The project costs roughly 1,200 rupees and fits in a shoebox, but the concepts scale directly to warehouse robots, autonomous vehicles, and industrial automation.
Start with the basic if-else code. Get it working. Then move to PWM speed control. Then implement PID and spend time tuning it. Each step teaches a distinct concept, and by the time your robot glides silently along a curved track with zero oscillation, you will understand feedback control systems better than any textbook could teach you.
All the components used in this project — Arduino Nano, L298N motor driver, TCRT5000 IR sensors, BO motors, chassis kits, and more — are available at wavtron.in. Pick up a kit and start building.



