engineering
Step-By-Step Tutorial on Programming a Line-Following Robot
Table of Contents
Introduction to Line-Following Robots
Building a line-following robot is a classic entry point into robotics and embedded programming. This project teaches you how sensors, actuators, and logic work together in real time. By the end of this guide, you will have a fully functional robot that can autonomously navigate a dark line on a light surface (or the inverse). We will cover hardware selection, assembly, programming from scratch, calibration, and advanced tuning for smoother performance. Whether you are a hobbyist or a student, this tutorial provides the foundation for many complex robotic systems.
How a Line-Following Robot Works
The core concept is simple: the robot uses infrared (IR) reflectance sensors to detect the contrast between a line (typically black) and the background (white). The sensors output a digital or analog signal indicating whether they are over the line. Based on this input, the microcontroller adjusts the speed of two independent motors to steer the robot along the path. A common configuration uses two sensors placed side by side under the front of the chassis. When both sensors are on the line, the robot moves forward. If the left sensor goes off the line, it turns right to correct; if the right sensor goes off, it turns left. This binary decision logic is the foundation, but can be extended to PID control for smoother and faster tracking.
Materials and Tools Needed
Core Components
- Microcontroller board: Arduino Uno (or Nano/Mega). The official Arduino Uno is durable and well-supported. Clone boards work fine but verify USB driver compatibility.
- Infrared reflectance sensors: At least two modules based on the TCRT5000 sensor. These are inexpensive, widely available, and come with a comparator output (digital) or raw analog output. Analog sensors allow more precise calibration.
- Motors: Two DC geared motors with wheels (e.g., 6V, 120 RPM). Avoid very high RPM motors for a beginner because speed makes control harder.
- Motor driver: L298N or L293D. The L298N is common and can drive two motors with separate PWM inputs for speed control.
- Chassis: An acrylic or aluminum robot chassis. Many kits include mounting holes for the Arduino, motor driver, and sensors.
- Power supply: A battery pack for 5–9V. Use 4×AA cells (6V) or a 7.4V LiPo pack with a voltage regulator for the Arduino.
- Breadboard and jumper wires: For prototyping connections. Once tested, you can solder everything onto a perfboard.
Optional but Recommended
- USB-B cable for programming the Arduino.
- Multimeter for measuring voltage and continuity.
- Soldering iron and solder for permanent connections.
- Sharpie marker and white paper to create test tracks.
Step 1: Hardware Assembly
Building the Chassis
Start by attaching the motors to the chassis using the provided brackets or screws. Ensure the wheels spin freely and the motor shafts are properly aligned. Mount the caster wheel (ball bearing or swivel) at the front or rear for balance. The center of gravity should be low to prevent tipping during sharp turns.
Installing the Sensors
Place the IR sensor modules at the front of the chassis, approximately 5–10 mm apart (center-to-center). The distance between sensors affects how early the robot can detect a curve. A typical spacing is 20–30 mm for a line width of 15–20 mm. Use standoffs or double-sided tape to mount them so the sensor face is about 5–10 mm above the ground. Too high reduces sensitivity; too low can cause false readings from surface irregularities.
Wiring the Motor Driver
Connect the L298N motor driver as follows (refer to its datasheet):
- Power: Connect +12V (or battery positive) to the driver’s VCC, and GND to battery negative. Also connect the driver’s +5V output to Arduino’s 5V pin if using that regulated supply. Never power the Arduino through its Vin if using a higher battery voltage without a regulator.
- Motors: Connect Motor A (left) wires to OUT1/OUT2, and Motor B (right) to OUT3/OUT4. Polarity determines forward/backward direction.
- Enable pins: Connect ENA and ENB to Arduino PWM pins (e.g., 9 and 10) for speed control. Some L298N modules have jumpers that bypass PWM; remove them to use PWM.
- Input pins: Connect IN1, IN2 (left motor control) and IN3, IN4 (right motor control) to digital pins (e.g., 4,5,6,7). These define motor direction.
Wire the IR sensors: each module typically has VCC, GND, and OUT (digital) or AD (analog). Connect VCC to Arduino +5V, GND to common ground, and out pins to digital input pins (e.g., 2 and 3) if using digital, or analog pins A0/A1 if using analog. For digital sensors, you may need to calibrate the built-in potentiometer to set the threshold.
Final Checks
Before connecting the battery, use a multimeter to check for shorts between power and ground. Then power up and verify the Arduino LED turns on. Upload an empty sketch to confirm communication. Move the robot over the line manually and watch the sensor LEDs (if present) to see if they change state.
Step 2: Setting Up the Arduino IDE and Basic Code
Download and install the Arduino IDE from the official site. Connect your board via USB and select the correct board and port in Tools menu. We will write a program that reads sensor inputs and commands the motor driver.
Pin Definitions and Initialization
Define constants for all pins used. This makes the code easy to modify later.
const int sensorLeft = 2; // Digital input from left IR sensor
const int sensorRight = 3; // Digital input from right IR sensor
const int enA = 9; // PWM pin for left motor speed
const int enB = 10; // PWM pin for right motor speed
const int in1 = 4; // Left motor direction 1
const int in2 = 5; // Left motor direction 2
const int in3 = 6; // Right motor direction 1
const int in4 = 7; // Right motor direction 2
Inside setup(), set the motor control pins as outputs and the sensor pins as inputs. Also set enable pins high or use PWM in loop(). Write helper functions for motor control:
void moveForward() {
digitalWrite(in1, HIGH);
digitalWrite(in2, LOW);
digitalWrite(in3, HIGH);
digitalWrite(in4, LOW);
}
void turnRight() {
digitalWrite(in1, HIGH);
digitalWrite(in2, LOW);
digitalWrite(in3, LOW);
digitalWrite(in4, HIGH); // reverse right wheel for tighter turn
}
void turnLeft() {
digitalWrite(in1, LOW);
digitalWrite(in2, HIGH); // reverse left wheel
digitalWrite(in3, HIGH);
digitalWrite(in4, LOW);
}
void stopMotors() {
digitalWrite(in1, LOW);
digitalWrite(in2, LOW);
digitalWrite(in3, LOW);
digitalWrite(in4, LOW);
}
Note that actual direction depends on motor wiring; you may need to swap HIGH/LOW pairs if wheels spin opposite.
Step 3: Programming the Line-Following Logic
Digital Sensor Reading (On/Off)
In the loop(), read the digital sensor states. Most TCRT5000 digital modules output HIGH when no line is detected (on white) and LOW when over a black line. If your modules behave opposite, adjust logic accordingly.
void loop() {
int leftSensor = digitalRead(sensorLeft);
int rightSensor = digitalRead(sensorRight);
// Case 1: Both sensors on line (black) -> go straight
if (leftSensor == LOW && rightSensor == LOW) {
moveForward();
analogWrite(enA, 150); // speed value 0-255
analogWrite(enB, 150);
}
// Case 2: Left sensor off line (white) -> turn right
else if (leftSensor == HIGH && rightSensor == LOW) {
turnRight();
analogWrite(enA, 150);
analogWrite(enB, 150);
}
// Case 3: Right sensor off line -> turn left
else if (leftSensor == LOW && rightSensor == HIGH) {
turnLeft();
analogWrite(enA, 150);
analogWrite(enB, 150);
}
// Case 4: Both off line (robot lost) -> stop or search
else {
stopMotors();
delay(100);
// optional: rotate in place to find line
}
delay(10); // small delay to prevent jitter
}
This simple state machine works on gentle curves but may oscillate on sharp turns. Experiment with different speeds: faster for straight lines, slower for curves. You can also use PWM pin values to set aggressive turns (one motor full speed, the other reversed or stopped).
Using Analog Sensors for Smoother Control
If your sensors provide analog output, connect them to analog pins and read analogRead() (0–1023). Set a threshold, e.g., 500 (midpoint). Then compare each reading against the threshold. Analog sensors allow you to proportionally adjust motor speeds, reducing jitter.
int leftValue = analogRead(A0);
int rightValue = analogRead(A1);
int threshold = 500; // calibrate based on your surface
bool leftOnLine = (leftValue > threshold);
bool rightOnLine = (rightValue > threshold);
Then use the same logic as above with leftOnLine and rightOnLine.
Step 4: Calibration and Testing
Creating a Test Track
Draw a bold black line (approximately 15–20 mm wide) on a large sheet of white paper using a permanent marker. Include straight sections, gentle curves, and at least one 90-degree turn. Ensure the background is matte to avoid reflections that confuse sensors.
Adjusting Sensor Thresholds
If using digital sensor modules, turn the onboard potentiometer gently with a small screwdriver until the sensor LED turns on when over the black line and off when over white. For analog sensors, measure the maximum and minimum readings on white and black and set your threshold midway. Better yet, calibrate dynamically in code by reading values on white and black during a calibration routine.
Tuning Motor Speeds
Start with a low PWM value (e.g., 100) to reduce speed. Observe the robot’s behavior:
- If it misses turns, increase the turn aggressiveness (e.g., lower the inside motor speed further or reverse it).
- If it oscillates too much, reduce speed or increase the delay in the loop.
- If it overshoots straight lines, try a lower straight speed.
Record your findings and adjust the code constants. A good starting point is 150 for straight, 200 for outside wheel on turns, 0 for inside wheel (stop) or -50 (reverse if using separate PWM for direction).
Advanced Techniques: PID Control for Smoother Tracking
The simple on-off controller works but often leads to zigzag movement at higher speeds. For smoother and faster line following, implement a PID (Proportional-Integral-Derivative) controller. The error is the difference between the two analog sensor values (or derived from a sensor array). The output adjusts the motor speeds proportionally.
float error = leftValue - rightValue; // or use a weighted sensor array
float Kp = 1.0; // proportional gain (tune empirically)
float Ki = 0.01; // integral gain
float Kd = 0.1; // derivative gain
float integral = 0;
float lastError = 0;
unsigned long lastTime = 0;
void loop() {
unsigned long now = millis();
float dt = (now - lastTime) / 1000.0;
lastTime = now;
float leftValue = analogRead(A0);
float rightValue = analogRead(A1);
float error = leftValue - rightValue; // negative when robot needs to turn left
integral += error * dt;
float derivative = (error - lastError) / dt;
lastError = error;
float correction = Kp * error + Ki * integral + Kd * derivative;
int baseSpeed = 150;
int leftSpeed = constrain(baseSpeed + correction, 0, 255);
int rightSpeed = constrain(baseSpeed - correction, 0, 255);
// Apply speeds and forward direction
digitalWrite(in1, HIGH); digitalWrite(in2, LOW);
digitalWrite(in3, HIGH); digitalWrite(in4, LOW);
analogWrite(enA, leftSpeed);
analogWrite(enB, rightSpeed);
delay(10);
}
Tuning PID gains requires patience. Start with only P (set I and D to zero) and increase until the robot oscillates, then reduce by half. Then add D to dampen oscillations. Finally, add a small I to eliminate steady-state error. There are many online resources for PID tuning for line followers.
Troubleshooting Common Issues
- Robot does not move: Check motor wiring, enable pins, and that power is supplied to both Arduino and driver. Verify PWM pins are set as outputs and
analogWritevalues are >0. - Spinning in circles: Sensor readings are wrong – swap sensor connections or invert logic in code. Also check for loose wires.
- Sensors not detecting line: Adjust the threshold potentiometer on digital modules. For analog, calibrate threshold or change distance to ground.
- Robot overshoots curves: Reduce speed, increase turn aggressiveness, or implement PID.
- Battery voltage drops quickly: Use a separate battery for motors and a regulated 5V for Arduino. Motors can draw several hundred mA; AA alkaline cells may not last long. Consider a LiPo pack with a voltage regulator.
Going Further: Expanding Your Robot
Once your basic line follower works reliably, try these enhancements:
- Add more sensors: Use an array of 5–8 IR sensors for smoother detection and better curve handling. The robot can interpolate between sensors for sub-sensor resolution.
- Implement speed control on curves: Use a PID controller that outputs not only direction but also reduces overall speed on sharp turns.
- Add a buzzer or LED indicator: Signal when the robot loses the line or during calibration.
- Use a display: Connect an I2C OLED to show speed, error, or PID values for debugging.
- Program a line-following maze solver: Store turn decisions to navigate a predefined maze (left-hand rule).
- Upgrade to a more powerful microcontroller: Like ESP32 for Wi-Fi control or Teensy for high-speed PID.
Conclusion
Programming a line-following robot is an achievable project that introduces you to embedded systems, sensor integration, motor control, and feedback loops. Start with the simple two-sensor approach and gradually refine your code for smoother behavior. The skills you learn here transfer directly to more complex autonomous robots, drones, and industrial automation. For further reading, check out the Arduino built-in examples and the Pololu line follower kits which offer excellent hardware and documentation. Happy building!