Line-following robot
What you’ll build
A robot that follows a black line on the floor using two IR sensors, built on a 2WD chassis. Then you’ll make it faster and smoother.
Parts
| Part | Qty | Notes |
|---|---|---|
| 2WD robot car chassis with 2 TT gear motors and wheels | 1 | Includes a caster |
| Arduino UNO (or compatible) | 1 | |
| L298N or TB6612FNG motor driver | 1 | |
| 6 × AA battery holder with switch | 1 | About 9 V with alkalines |
| Jumper wires (male–male and male–female) | ~20 | |
| IR line sensor modules (TCRT5000 type) | 2 | |
| Black electrical tape and a light floor or card | — | For the track |
Motor wiring
| Driver pin | Arduino pin | Purpose |
|---|---|---|
| ENA | 5 (PWM) | Left motor speed |
| IN1 / IN2 | 7 / 8 | Left motor direction |
| ENB | 6 (PWM) | Right motor speed |
| IN3 / IN4 | 9 / 10 | Right motor direction |
| 12V (VM) | Battery + (through the switch) | Motor power |
| GND | Battery − and Arduino GND | Shared ground |
| 5V | Arduino VIN is fed from the battery instead | Leave the driver’s 5V output unused, or check your board’s jumper |
Sensor wiring
| Sensor pin | Arduino |
|---|---|
| Left sensor DO | A0 (used as a digital input) |
| Right sensor DO | A1 |
| VCC / GND | 5V / GND |
Mount the sensors under the front, about 3–8 mm above the floor, roughly 2–3 cm apart so the tape (about 2 cm wide) fits between them.
Build steps
- Assemble the chassis, motors, wheels and caster (chassis lesson).
- Mount the Arduino, driver and battery; wire the motors (table above).
- Test motors alone with wheels off the ground (Examples tab, step 1). Fix any motor that spins backwards by swapping its two wires.
- Mount and wire the sensors. Test sensors alone (step 2): note what each reads over white and over black.
- Upload the line follower (step 3), set it on the line and switch on.
- Tune speeds until it completes the track reliably.
If it doesn’t work
| Symptom | Check |
|---|---|
| Spins in circles | One motor wired backwards |
| Drives straight off the line | Sensor logic inverted: swap HIGH/LOW in onLine() |
| Wobbles wildly | Lower the speeds; turn less sharply |
| Loses the line on tight curves | Lower the base speed, or move the sensors further forward |
| Arduino resets | Batteries low, or motors and logic sharing a weak supply |
Step 1: test the motors
const int ENA = 5, IN1 = 7, IN2 = 8, ENB = 6, IN3 = 9, IN4 = 10;
void setMotor(int en, int a, int b, int speed) { // speed -255..255 digitalWrite(a, speed > 0); digitalWrite(b, speed < 0); analogWrite(en, min(abs(speed), 255));}
void drive(int left, int right) { setMotor(ENA, IN1, IN2, left); setMotor(ENB, IN3, IN4, right);}
void setupMotors() { int pins[] = {ENA, IN1, IN2, ENB, IN3, IN4}; for (int p : pins) pinMode(p, OUTPUT);}
void setup() { setupMotors(); }
void loop() { drive(180, 180); delay(1000); // both forward drive(0, 0); delay(500); drive(-180, 180); delay(700); // spin left drive(0, 0); delay(1500);}Step 2: test the sensors
void setup() { Serial.begin(9600); pinMode(A0, INPUT); pinMode(A1, INPUT); }void loop() { Serial.print("left: "); Serial.print(digitalRead(A0)); Serial.print(" right: "); Serial.println(digitalRead(A1)); delay(200);}Step 3: the line follower
const int ENA = 5, IN1 = 7, IN2 = 8, ENB = 6, IN3 = 9, IN4 = 10;
void setMotor(int en, int a, int b, int speed) { // speed -255..255 digitalWrite(a, speed > 0); digitalWrite(b, speed < 0); analogWrite(en, min(abs(speed), 255));}
void drive(int left, int right) { setMotor(ENA, IN1, IN2, left); setMotor(ENB, IN3, IN4, right);}
void setupMotors() { int pins[] = {ENA, IN1, IN2, ENB, IN3, IN4}; for (int p : pins) pinMode(p, OUTPUT);}
const int LEFT_SENSOR = A0, RIGHT_SENSOR = A1;const int SPEED = 160, TURN_FAST = 170, TURN_SLOW = 40;
// Many modules read HIGH over black. If yours is the opposite, change HIGH to LOW.bool onLine(int pin) { return digitalRead(pin) == HIGH; }
void setup() { setupMotors(); pinMode(LEFT_SENSOR, INPUT); pinMode(RIGHT_SENSOR, INPUT); delay(2000); // time to put it down}
void loop() { bool left = onLine(LEFT_SENSOR), right = onLine(RIGHT_SENSOR); if (left && right) drive(0, 0); // junction or end else if (left) drive(TURN_SLOW, TURN_FAST); // line on the left: turn left else if (right) drive(TURN_FAST, TURN_SLOW); // line on the right: turn right else drive(SPEED, SPEED); // straight}Try it in the simulator
The same logic, on the virtual robot. Tune it here before you tune the real one.
Robot commands
motors(left, right): wheel power, −100 to 100lineLeft(),lineRight():trueover the black linedistance(): cm to the nearest thing ahead (max 200)distanceLeft(),distanceRight(): cm to the sideslinePosition(): where the line is, −1 (left) to 1 (right), ornullmillis(): milliseconds since Runprint(value): show a message belowsetup()runs once;loop()runs 50 times a second
Lap recordEasy
Find the highest straight speed that still completes a lap in the simulator.
Stop at the endMedium
On a real track with a black bar across the finish, make the robot stop and blink the built-in LED when both sensors see black.
In the left && right branch: drive(0, 0); while (true) { digitalWrite(LED_BUILTIN, HIGH); delay(200); digitalWrite(LED_BUILTIN, LOW); delay(200); } and set pinMode(LED_BUILTIN, OUTPUT) in setup().
Upgrade to P controlHard
Use the simulator’s linePosition() with a proportional controller (see Feedback and PID) and beat your bang-bang lap time.
On a real robot this needs 3–5 analog sensors (or a sensor array) to measure how far off the line you are.
Quick check
Finished this lesson?