The Idea, and Why It Is Harder Than It Looks
A line-following robot drives along a black line taped to a pale floor, correcting itself continuously so that it never wanders off. It is the most common robotics competition event in Indian colleges and schools, and it looks trivially simple until you build one.
What makes it interesting is that the robot cannot see the line ahead. It only knows about the small patch of floor directly beneath its sensors, right now. It has no map, no memory of the track, and no idea whether the next turn is left or right. All the intelligence has to come from reacting quickly and correctly to a tiny amount of information, over and over, several hundred times a second.
That is a genuine engineering pattern and not a toy problem. A cruise control keeping a car at a set speed, a thermostat holding a room temperature, and a drone holding altitude are all doing the same thing: measuring an error, correcting, measuring again. Learn it here on a robot you can watch, and the idea transfers everywhere.
You are reusing the whole chassis from the obstacle-avoiding robot. Only the sensors change — take off the ultrasonic sensor and mount two infrared reflectance sensors pointing at the floor instead.
- This project rewards patience over cleverness. A slow robot that never loses the line beats a fast one that overshoots every corner, and in competitions a robot that finishes at all usually beats several that were faster and left the track.
How Infrared Line Sensors Actually Work
A line sensor module is two components facing the floor: an infrared LED that shines light downwards, and a phototransistor that measures how much of it comes back. A pale surface reflects most of the light and the receiver sees a lot; a black line absorbs most of it and the receiver sees little. The module compares that against a threshold and outputs a plain HIGH or LOW on a single digital pin, which is why you read it with digitalRead().
Which state means which is not universal, and this is the first thing to settle. On very many common modules the output goes LOW over a dark surface, and many also carry a small indicator LED that lights when the line is detected. Do not assume. Wire one sensor, print its value to the Serial Monitor, and slide a strip of black tape underneath while you watch. Thirty seconds of testing tells you the truth for the module you actually own, and saves you from a robot that steers confidently in exactly the wrong direction.
The threshold between light and dark is set by the small potentiometer on the module. Turn it slowly with a screwdriver until the sensor switches reliably as you move it on and off the tape, and check both directions — a common mistake is tuning it until it detects the line and never checking that it also releases when it leaves.
Mounting height matters more than beginners expect. Too high and the reflected light is too weak to distinguish anything; too low and the sensor's field of view is so narrow that it slips off the line between readings. Around half a centimetre to a centimetre above the surface is the usual working range for these modules, and the correct value is best found by testing rather than measuring. Keep both sensors at exactly the same height as each other, because a difference between them behaves in code exactly like a permanent steering bias.
- Infrared LED shines down; phototransistor measures the reflection
- Pale floor reflects strongly, black line absorbs — that difference is the whole signal
- Output is a single digital HIGH or LOW, so use
digitalRead(), notanalogRead() - The on-board potentiometer sets the light/dark threshold; tune it on your actual track
- Roughly half a centimetre to a centimetre above the floor is the usual mounting height
- Both sensors must sit at the same height and the same distance from the line's centre
- Sunlight contains a great deal of infrared, and so do some lamps. A robot tuned indoors in the evening can behave completely differently near a window at midday. Always re-tune the threshold in the light where the robot will actually run, which for a competition means during the practice session, not the night before.
Sensor Placement Decides Everything
With two sensors there are two sensible arrangements, and they lead to opposite code. In the first, both sensors sit outside the line, one on each side, so that on a straight run both see pale floor and neither sees black. When the robot drifts left, the right-hand sensor finds the line and tells you to correct right. In the second arrangement the sensors straddle the line closely enough that both sit on black when centred.
The first arrangement is easier to reason about and is what the code below assumes: both sensors on pale floor means you are centred and should go straight. One sensor finding the line means the robot has drifted, and the robot turns towards the side that found it. Both sensors seeing black at once means something unusual — a junction, a very wide line, or the end marker.
The gap between the sensors should be a little wider than the line. Too narrow and both sensors are on black most of the time, so the robot has no idea which way it is drifting. Too wide and the robot can wander a long way before either sensor notices, which produces the characteristic wide zigzag.
Mount them ahead of the wheels, not behind. A sensor in front gives the robot a slightly earlier warning, and the difference between correcting early and correcting late is exactly the difference between smooth following and violent oscillation.
- Mount both sensors ahead of the drive wheels, facing straight down
- Set their spacing slightly wider than the line so exactly one finds it when you drift
- Keep both at identical height — a height difference acts like a permanent steering bias
- Fix them rigidly; a sensor that vibrates as the robot moves gives readings that flicker
- Test each sensor separately with the Serial Monitor before running the full sketch
- Build your practice track from black insulation tape on a pale floor or a large sheet of paper. Two to three centimetres wide is a good width for two-sensor robots. Start with a gentle oval; add sharp corners only after the robot completes the oval reliably.
The Sketch
The logic is four cases, and the whole robot is those four cases repeated as fast as the board can manage. Read the two sensors, work out which case you are in, set the motors accordingly, repeat.
Notice that the turns here are gentle, not pivots. Rather than stopping one wheel and spinning the other, a correction slows one wheel and keeps the other running. This produces a smooth curve back to the line instead of a jerk, and it is the single biggest improvement most beginner line-followers need. Stopping a wheel dead is the reason so many first attempts wobble violently down a straight track.
The last case is the one people leave out and then regret: what to do when both sensors report pale floor and the robot has lost the line entirely. Stopping is honest and safe. Better is to remember which way the robot was correcting most recently and keep turning that way to search for the line, since a robot usually loses the line by overshooting a corner and the line is therefore on the side it was last steering towards.
// --- line sensors ---
const int leftSensor = 2;
const int rightSensor = 3;
// --- motors (same wiring as the obstacle robot) ---
const int enA = 9; const int in1 = 8; const int in2 = 7; // right
const int enB = 3; const int in3 = 5; const int in4 = 4; // left
// --- tuning: start slow, raise only when it follows reliably ---
const int baseSpeed = 140; // both wheels on a straight
const int slowSpeed = 70; // the inside wheel during a correction
// what the sensor reads over BLACK on your module - verify this yourself
const int ON_LINE = LOW;
int lastTurn = 0; // -1 = was correcting left, +1 = right
void setup() {
pinMode(leftSensor, INPUT);
pinMode(rightSensor, INPUT);
pinMode(enA, OUTPUT); pinMode(in1, OUTPUT); pinMode(in2, OUTPUT);
pinMode(enB, OUTPUT); pinMode(in3, OUTPUT); pinMode(in4, OUTPUT);
Serial.begin(9600);
stopRobot();
delay(2000); // time to place the robot on the line
}
void loop() {
bool leftOnLine = (digitalRead(leftSensor) == ON_LINE);
bool rightOnLine = (digitalRead(rightSensor) == ON_LINE);
if (!leftOnLine && !rightOnLine) {
driveWheels(baseSpeed, baseSpeed); // centred - straight on
}
else if (leftOnLine && !rightOnLine) {
driveWheels(baseSpeed, slowSpeed); // drifted right - curve left
lastTurn = -1;
}
else if (!leftOnLine && rightOnLine) {
driveWheels(slowSpeed, baseSpeed); // drifted left - curve right
lastTurn = 1;
}
else {
// both on black: a junction, a wide line, or the finish marker
driveWheels(baseSpeed, baseSpeed);
}
}
// rightSpeed and leftSpeed are 0-255; both wheels always drive forward
void driveWheels(int rightSpeed, int leftSpeed) {
digitalWrite(in1, HIGH); digitalWrite(in2, LOW);
digitalWrite(in3, HIGH); digitalWrite(in4, LOW);
analogWrite(enA, rightSpeed);
analogWrite(enB, leftSpeed);
}
void stopRobot() {
digitalWrite(in1, LOW); digitalWrite(in2, LOW);
digitalWrite(in3, LOW); digitalWrite(in4, LOW);
analogWrite(enA, 0);
analogWrite(enB, 0);
} ON_LINEis declared once at the top for a reason. If your modules turn out to read HIGH over black, you change that single line and the whole sketch is correct — instead of hunting for every comparison and inverting it by hand.- Set
slowSpeedabove zero rather than stopping the inside wheel. Zero makes the robot pivot sharply, which overshoots and starts the zigzag; a slower-but-still-turning wheel curves the robot back gently.
Tuning: Turning a Wobbling Robot into a Smooth One
Your first run will almost certainly zigzag. That is not a failure, it is the standard starting point, and understanding why is the real lesson of this project.
The robot only corrects after it has already drifted off centre, so every correction is late. If the correction is also too strong, the robot swings past the centre to the other side, gets a correction the other way, and oscillates. Reducing the strength of the correction — raising slowSpeed so the inside wheel slows less — usually calms it immediately. Reducing baseSpeed helps too, because a slower robot drifts less far between corrections.
The opposite problem is a robot that leaves the track on sharp corners. Here the correction is too weak or too late: it cannot turn quickly enough to stay with the line. Lower slowSpeed so the turn is sharper, or slow the whole robot down. There is a real trade-off between the two problems, and finding the balance for a particular track and a particular set of motors is the actual skill being tested.
When two speeds are no longer enough, the next step is proportional control: instead of two digital sensors giving four cases, use an array of analog sensors, calculate how far from centre the robot is as a number, and make the correction proportional to that number — a small drift gets a small correction, a large one gets a large one. That is the P in PID control, and it is what every competitive line-follower uses. Get the two-sensor version working smoothly first; the ideas transfer directly.
- Zigzagging down a straight — the correction is too strong; raise
slowSpeedor lowerbaseSpeed - Leaves the line on sharp corners — the correction is too weak; lower
slowSpeedor slow the robot - Works one way round the track but not the other — the two sensors are not symmetric, in height or spacing
- Stops randomly on a clean track — usually a loose wire or a sagging battery, not the logic
- Follows well indoors, fails near a window — ambient infrared; re-tune the threshold in that light
- Behaves differently as the battery drains — a common competition surprise; test on a battery that is not fully charged
- Change one value at a time and note what happened. It is slower for the first ten minutes and far faster after that, and it is the difference between tuning a robot and randomly poking at it.
- Once the robot follows reliably, try raising
baseSpeedin small steps until it starts to fail, then back off. That upper limit is a genuinely useful thing to know about your own machine.
