Line-following robot with Arduino and IR sensors
Build a line-following robot with Arduino and IR sensors
A two-IR-sensor line follower is the "hello world" of mobile robotics: a small two-wheeled chassis that watches a black tape line on a white floor and steers itself to stay on it. Total cost lands around $25–35, build time is 2–3 hours if you have everything to hand, and the final result is genuinely satisfying — the robot reacts to a track you drew with electrical tape five minutes earlier.
This guide uses the most common parts pile (Arduino Uno, L298N driver, two IR modules, 2WD chassis, 18650 pack) and the bang-bang control algorithm. It's not the fastest line follower you can build — for that you want 5–8 sensors and PID — but it's the one almost every guide and every kit on the market actually targets, so it's the right starting point.
What you'll need
| Item | AliExpress | Amazon |
|---|---|---|
| Arduino Uno R3 (clone is fine) | AliExpress Link | Amazon Link |
| 2WD smart car chassis kit (acrylic plate, 2× TT gear motors, 2× wheels, 1× caster, hardware) | AliExpress Link | Amazon Link |
| L298N motor driver module | AliExpress Link | — |
| TCRT5000 IR line-tracking sensor module ×2 | AliExpress Link | Amazon Link |
| 18650 holder (2-cell, with leads) + two protected 18650 cells | AliExpress Link | Amazon Link |
| Female–female Dupont jumper wires (20 cm, 40-pack) | AliExpress Link | Amazon Link |
| Black electrical tape (19 mm wide, matt) | AliExpress Link | Amazon Link |
You'll also want a small Phillips screwdriver, double-sided foam tape or zip ties for mounting the sensors, and a smooth pale-coloured surface to drive on (a sheet of plain white paper, a kitchen tile, or a piece of foamboard).
Notes on the parts
- TT motors are deliberately slow. They run at roughly 200 RPM at 6 V and a 2WD line follower wants slow — fast robots overshoot curves. Don't "upgrade" to N20 high-RPM motors on your first build.
- Get the TCRT5000 module, not bare TCRT5000 sensors. The module has an LM393 comparator, a trim pot, and a clean digital output. Bare sensors mean a breadboard, pull-down resistors, and a bad afternoon.
- Two 18650s in series gives ~7.4 V, which the L298N happily drops to ~5 V for the Arduino through its onboard regulator and feeds 7.4 V straight to the motors. A 9 V "smoke alarm" battery technically works but sags to nothing within minutes — don't.
- Wide matt black tape, light surface. The IR reflectance contrast is everything. 19 mm tape works well with two sensors spaced 25–30 mm apart. Glossy tape on a glossy floor will frustrate you.
How it actually works
Each TCRT5000 module sits a few millimetres above the floor and shines an IR LED downward. A photodiode next to it measures the reflected light. White surfaces reflect strongly (digital output LOW), black tape absorbs almost everything (digital output HIGH). The trim pot on the module sets the threshold.
You mount one sensor a little to the left of the line and one a little to the right. With the robot centred, both sensors see white → drive straight. Drift left, the right sensor finds the tape → turn right to compensate. Drift right, the left sensor finds the tape → turn left. That's the whole algorithm. It's called "bang-bang" because the robot is always either going straight or turning hard; it wiggles down the line rather than gliding.
Step 1 — Assemble the chassis
The 2WD kit has an acrylic base plate, two yellow TT motors that bolt to the underside with the long M3 screws, two wheels that press onto the D-shaped output shafts, and a single ball caster for the third point of support. Mount the motors at the rear of the plate and the caster at the front — that puts the IR sensors out ahead of the wheels, which gives the controller a tiny bit of lookahead and makes the robot stable on curves.
Peel the protective film off the acrylic before you screw anything down. You'll thank yourself later.
Step 2 — Mount the IR sensors
The sensors mount on the front edge of the chassis, pointing straight down, one on each side of where the line will be. Three numbers worth getting right:
- Sensor-to-floor distance: 5–10 mm. Closer than 5 mm and small bumps confuse it; further than 10 mm and the IR reflection gets too weak to discriminate. Most kit chassis put you in this window naturally.
- Spacing between the two sensors: ~25–30 mm centre-to-centre for 19 mm tape. The rule is "narrower than the line is wide so both sensors can sit on the white either side, but wide enough that the line clearly enters one or the other when you drift". Some guides recommend 11 cm centre-to-centre with 5 cm tape — that also works, just at a different scale.
- Sensors well ahead of the wheels — by 30–50 mm if your chassis allows it. This is what gives the robot time to react.
Double-sided foam tape works fine for a first build. Hot glue or M3 standoffs through the kit's mounting holes are tidier.
Step 2 — Assemble the chassis
The mechanical build is straightforward but gets you ahead of wiring headaches. Build in this order: motors first, then battery, then sensors last so nothing blocks access to mounting holes.
Tools beyond the basics
You'll need a Phillips screwdriver (the TT motors use M3 screws), needle-nose pliers for bending servo horns or holding wires while you solder, and optionally a small hobby knife for trimming 3D-printed supports. If you're using hot glue instead of screws, a low-temp iron is safer around plastic chassis parts — high-temp melts ABS in seconds.
Mount the motors
- Position the TT gearmotors at the rear of the chassis so the wheels sit flush with or slightly inside the chassis edges. The axle should be parallel to the chassis edge within 2° — even a small angle makes the robot crab-walk instead of drive straight.
- Secure each motor with two M3 × 6 mm screws through the chassis mounting holes into the motor's threaded inserts. Don't overtighten; plastic threads strip at about 0.5 N·m. Hand-tight plus a quarter turn is enough.
- Attach the wheels. The TT motors come with rubber tires that press-fit onto the axle hubs. Push straight on — don't angle them, or you'll get uneven wear and vibration. If the tires are loose, a drop of cyanoacrylate on the hub solves it permanently.
Install the battery pack
- Slide the 2× 18650 holder into the centre-rear of the chassis. Centre it left-to-right so the weight is balanced between the two drive wheels — an off-centre pack makes one wheel carry more load and the robot drifts toward that side.
- Secure with M3 screws or double-sided foam tape. If your chassis has threaded inserts, use M3 × 8 mm screws. Otherwise, 10 mm squares of 3M VHB foam tape hold fine for a prototype; swap to screws once you're happy with the placement.
- Route the battery wires toward the front of the chassis where the Arduino and motor driver will sit. Leave ~5 cm of slack so you can remove the pack without desoldering later.
Mount the IR sensors
- Position the TCRT5000 modules on the front underside, pointing down at the floor. The sensor-to-floor gap should be 3–5 mm — any more and they miss thin tape; any less and they scrape. Use small spacers (M3 nuts work) between the module PCB and the chassis to dial in the height.
- Space the sensors roughly 40–60 mm apart, centred on the robot's longitudinal axis. Too close together (< 25 mm) and the robot can't tell which side of the line it's on at speed. Too far apart (> 80 mm) and wide curves look like a straight line to both sensors simultaneously.
- Angle them slightly inward (about 10° from perpendicular). This gives each sensor a bit of "look-ahead" — they see the line before the robot reaches it, which makes turns smoother instead of jerky corrections after you've already overshot.
Quality checks before wiring
- Spin both wheels by hand. They should turn freely without rubbing the chassis or each other. If one drags, loosen that motor's screws and reseat it.
- Check sensor height with a coin. Slide a 1 mm coin under each sensor — you should see the signal LED change state. If not, lower the module.
- Give the whole thing a gentle shake. Nothing should rattle loose. Motors and battery pack are the heaviest parts; if they're not secure, vibration will work screws loose within minutes of running.
📷 (photo placeholder — slot chassis-assembled)
Step 3 — Wire it up
Pin assignments (matches the code below):
| From | To | Purpose |
|---|---|---|
Left IR sensor OUT |
Arduino D2 | Left line detection |
Right IR sensor OUT |
Arduino D4 | Right line detection |
IR sensor VCC |
Arduino 5V | Both sensors |
IR sensor GND |
Arduino GND | Both sensors |
L298N IN1, IN2 |
Arduino D6, D7 | Left motor direction |
L298N IN3, IN4 |
Arduino D9, D10 | Right motor direction |
L298N ENA, ENB |
Arduino D5, D8 | Motor enable / speed |
L298N +12V |
Battery + (7.4 V from 2× 18650) | Motor supply |
L298N GND |
Battery − and Arduino GND | Common ground (critical) |
L298N +5V |
Arduino Vin |
Powers Arduino from regulator |
Leave the 5V-EN jumper on the L298N installed when you're running on 7.4 V — that enables the onboard 5 V regulator. Remove it only if you're feeding more than 12 V (you aren't here).
Common ground is the one wire people forget. Without it the IR-sensor logic levels float relative to the L298N and the motors twitch randomly.
Step 4 — The code
Drop this into the Arduino IDE, select Board → Arduino Uno, pick the right COM port, and upload. Disconnect the motor power while uploading; USB alone is fine for flashing.
// Pin map — match the wiring table above
#define ENA 5 // L298N enable A (left motor PWM)
#define IN1 6
#define IN2 7
#define IN3 9
#define IN4 10
#define ENB 8 // L298N enable B (right motor PWM)
#define L_S 2 // Left IR sensor
#define R_S 4 // Right IR sensor
const int SPEED = 180; // 0-255. Start slow; raise once tracking is reliable.
void setup() {
pinMode(L_S, INPUT); pinMode(R_S, INPUT);
pinMode(ENA, OUTPUT); pinMode(ENB, OUTPUT);
pinMode(IN1, OUTPUT); pinMode(IN2, OUTPUT);
pinMode(IN3, OUTPUT); pinMode(IN4, OUTPUT);
analogWrite(ENA, SPEED);
analogWrite(ENB, SPEED);
}
void loop() {
// TCRT5000 module: HIGH on black tape, LOW on white floor.
bool left = digitalRead(L_S);
bool right = digitalRead(R_S);
if (!left && !right) forward(); // both on white -> straight
else if (!left && right) turnRight(); // tape under right -> steer right
else if ( left && !right) turnLeft(); // tape under left -> steer left
else stopMotors();// both on tape (junction) -> stop
}
void forward() { drive(HIGH, LOW, HIGH, LOW); }
void turnLeft() { drive(LOW, LOW, HIGH, LOW); } // left wheel stops
void turnRight() { drive(HIGH, LOW, LOW, LOW); } // right wheel stops
void stopMotors() { drive(LOW, LOW, LOW, LOW); }
void drive(int a, int b, int c, int d) {
digitalWrite(IN1, a); digitalWrite(IN2, b);
digitalWrite(IN3, c); digitalWrite(IN4, d);
}
If your sensor module uses inverted logic (LOW on black — some KY-033 and 5-channel arrays do), flip the ! operators in loop() or swap the comparator's wiring. The trim pot's onboard LED tells you which way: when the LED is off over white tape, your output is the standard polarity.
Step 5 — Calibrate the sensors
Power the robot up on the bench (wheels off the floor):
- Hold one sensor over white floor. The module's signal LED should be off.
- Slide it over the black tape. The LED should turn on.
- If the transition is mushy or backwards, turn the trim pot with a small screwdriver until the LED snaps cleanly on/off as you move between black and white.
- Repeat for the other sensor. Both pots will need slightly different settings — they're cheap parts.
The aim isn't a specific threshold value, it's a clean snap. If you can't get a clean snap, your sensor is too far from the floor or your tape isn't matt enough.
Step 6 — Lay a track and tune
Tape down a loop on white paper or foamboard. Avoid tight curves on your first track — keep radii above 15 cm. Put the robot on the line, switch on the battery, and let go.
What you'll probably see, and what to do:
- Wiggles wildly from side to side → speed too high. Drop
SPEEDto 130–150. - Drives in circles → one motor's wiring is reversed. Swap
IN1/IN2(orIN3/IN4). - Loses the line on curves → sensors too close together, or too far behind the wheels. Move them forward and slightly outward.
- Stops on the line for no reason → both sensors triggering. Either the line is wider than your sensor spacing, or one trim pot is mis-set. Recalibrate.
- Runs fine for a minute then dies → battery sag. 18650s should hold ~7 V under load; if you're seeing 5 V, the cells are tired or unprotected and tripped.
Where to take it next
Once it works, the obvious upgrades are: PWM differential turning (instead of stopping a wheel, run it slow — turns become smoother), a 5-channel IR array with PID control (handles tight curves and crossings), and wheel encoders so you can navigate with dead-reckoning when the line breaks. Each of those is a separate guide.
Bang-bang on two sensors will get you 90% of what's interesting about line following. Don't skip it.