Building a Line Following Robot with KY-033 Sensors for Beginners

This post contains affiliate links, marked with *. As an Amazon Associate I earn from qualifying purchases, at no extra cost to you. It helps keep the tutorials free.

The video loads from YouTube (Google), which may set cookies. Privacy policy

Quick summary

This robot follows a white line on a dark background. It uses two KY-033 infrared line-tracking sensors, an Arduino Uno and an Arduino Motor Shield Rev3. You 3D-print the chassis, wire the electronics and write if/else steering logic for two digital sensor signals. If/else code runs one action when a condition is true and another when it is false. Each sensor reports HIGH or LOW, and four cases in the code decide whether the robot drives straight, pivots (turns almost on the spot) or rotates in place. This version is the step before PID control (proportional-integral-derivative control, which scales the steering correction to how far the robot is off the line).

3D rendering of the robot designed in Autodesk Inventor: blue body with an orange top and bottom, the power switch and the OmArTronics lettering on the cover, two wheels with orange rims

How the Robot Works

The robot combines infrared line detection with differential steering. It has no steering axle; it changes direction by driving its left and right wheels at different speeds or in opposite directions. The two drive wheels sit at the rear, and a caster wheel at the front, between the two sensors, carries the front of the chassis. Left and right in this post are as seen from behind the robot, facing the direction of travel.

Arduino Uno (controller)

The Arduino Uno reads the two sensor signals, runs the decision logic and sets the motor signals. If you have never programmed an Arduino, start with the Arduino programming basics tutorial.

Arduino Motor Shield Rev3 (motor driver)

The Motor Shield Rev3 plugs onto the Uno. Its L298P chip contains two H-bridges (switch circuits that can reverse the current through a motor). The battery powers the motors through the shield, and the Arduino only sends control signals: one direction pin and one PWM (pulse width modulation) speed pin per motor. For more detail on motor driving, see the L298N motor driver guide, which covers DC motor control in depth.

Two KY-033 line-tracking sensors

Each KY-033 module has an infrared LED, an infrared receiver and a comparator, a small chip that compares two voltages. The LED shines onto the surface: white reflects most of the light back, dark absorbs it. The comparator checks the receiver signal against a threshold that you set with the potentiometer (the small adjustment screw on the board) and switches the output pin to HIGH or LOW. The usual KY-033 module outputs LOW (0) when the surface reflects the light (white tape) and HIGH (1) when the surface absorbs it (dark background). Turn the potentiometer until the output changes reliably between line and background at your mounting height.

Important: this build follows a white line on a dark background. For a black line on a white background, swap 1 and 0 in the if-conditions of loop().

Steering logic

The two sensors sit slightly outside the edges of the tape, one on each side. With the robot centered, both see the dark background and read 1. The table shows what the code does in each of the four cases: the direction and the speed it sends to each motor. HIGH is forward, LOW is backward; the speed is a PWM value from 0 (stopped) to 255 (full speed).

Left sensorRight sensorActionLeft motor (direction, speed)Right motor (direction, speed)
11drive straightHIGH, 105HIGH, 105
10pivot rightHIGH, 105LOW, 85
01pivot leftLOW, 85HIGH, 105
00rotate in place to the rightHIGH, 70LOW, 70

In a pivot, one wheel drives forward and the other backward. If the robot drifts left, the right sensor reaches the tape and reads 0, so the robot pivots right, back toward the line. A drift to the right works the same way with the left sensor. Both readings are 0 only when both sensors are over the tape at once, for example when the robot crosses it at an angle in a corner. The rotation in place then runs until one of the readings changes. If the robot leaves the line completely, both sensors read 1 again and the robot drives straight on.

Bill of Materials

Most parts are standard Arduino components; the printed parts are designed for this build. They come as STL files, the 3D model format that 3D printing software reads.

ComponentQtyRequiredPurposeNotes
Arduino Uno1YesReads the sensors and runs the steering logicAny Uno-compatible board works
Arduino Motor Shield Rev31YesDrives the two DC motorsPlugs onto the Uno
KY-033 line-tracking sensor2YesDetects the line by infrared reflectionThreshold set with the onboard potentiometer
DC motor with gearbox2YesDrives the left and right wheelsYellow TT gearmotor (the common low-cost type for small robots), 3 to 6 V
Wheels2YesDrive wheelsMust fit the motor shaft
Caster wheel1YesFront supportBall caster or small swivel wheel
3D-printed chassis1YesBase plate for motors, caster wheel, sensors, Arduino and battery, with a cover on topTwo STL files (base and cover), 170 × 149 mm
3D-printed sensor spacers2YesHold the KY-033 sensors under the front of the chassisOne per sensor, 13.5 mm high
3D-printed Arduino spacers4YesHold the Uno and the shield above the base plateOne per mounting hole of the Uno, 14 mm high
Male-to-female jumper wires6YesConnect the two sensors to the ArduinoThree per sensor (S, VCC, GND); the motor wires go straight into the screw terminals of the shield
M3 screwsSeveralYesFix the motors and spacers to the chassis
M3 threaded insertsSeveralYesThreads for the M3 screws in the printed partsPressed in with a soldering iron (see 3.1)
Battery1YesPowers the Arduino and the motors7.4 V 2S LiPo (lithium polymer battery, two cells in series)
Power switch1OptionalSwitches the robot on and offInline toggle switch in the battery + wire

Step 1: Designing the Robot in Autodesk Inventor

I designed the parts in Autodesk Inventor. The chassis is 170 mm long and 149 mm wide. The build has three kinds of printed parts:

  • Chassis: a base plate and a cover. The base plate carries the two motors, the caster wheel, the sensors, the Arduino and the battery; the cover goes on top.
  • Sensor spacers: two printed spacers, 13.5 mm high, hold the KY-033 sensors under the front of the chassis.
  • Arduino spacers: four printed spacers, 14 mm high, hold the Uno and the Motor Shield above the base plate, with the ports accessible for programming and wiring.

If you are new to 3D printing, read the Introduction to 3D Printing and 3D Design with Inventor first.

Step 2: 3D Printing the Components

I printed the parts on a Creality Ender 3. PLA (polylactic acid, a plastic that prints easily) works for this build. It softens at around 60 °C; use ABS (acrylonitrile butadiene styrene, another printing plastic) only if the robot will be exposed to more heat. After printing, remove the support material (the temporary structures the printer builds under overhanging parts) and any rough edges. Check that the screw holes and mounting features have the right size before you assemble anything.

Step 3: Assembling the Robot

Assemble the robot in this order.

3.1 Mounting the motors and wheels

  1. Secure the two DC motors to the chassis with M3 screws. The screws go into threaded inserts: small brass sleeves with an internal thread that you press into the printed holes with a hot soldering iron, so the screws do not strip the plastic. I pressed all the inserts in first, before mounting any other part. Check that each motor sits firmly and that both are aligned.
  2. Push the wheels firmly onto the motor shafts and check that they turn without wobbling.
  3. Mount the caster wheel at the front of the chassis.

3.2 Installing the electronics

  1. Fix the four Arduino spacers to the base plate and mount the Uno on them.
  2. Plug the Motor Shield Rev3 onto the Uno so that its pins sit fully in the Uno’s headers (the black socket strips along the board edges).
  3. Mount each KY-033 sensor on its spacer at the front underside of the chassis, sensor facing down.
  4. Fix the battery to the chassis so that it cannot slide; I used cable ties.

Step 4: Wiring the Electronics

The tables below list every connection, and the diagram shows the same wiring. Its battery symbol is a 9 V battery, but my robot runs on a 7.4 V LiPo.

Wiring diagram: Arduino Uno with Motor Shield Rev3, two yellow DC gearmotors on the motor terminals, two KY-033 sensors on 5 V, GND and pins 2 and 4, and a 9 V battery symbol with a power switch

Motor connections (via Motor Shield)

MotorMotor Shield terminalArduino pin (via shield)Function
Left DC motorChannel A outputDIR_L = pin 12, PWM_L = pin 3Direction and speed of the left motor
Right DC motorChannel B outputDIR_R = pin 13, PWM_R = pin 11Direction and speed of the right motor

KY-033 sensor connections

VCC is the supply pin and GND the ground pin (0 V).

KY-033 sensorSensor pinArduino pinFunction
Left sensorS (signal)Digital pin 2Left line reading (HIGH/LOW)
Left sensorVCC5 VPower supply
Left sensorGNDGNDGround
Right sensorS (signal)Digital pin 4Right line reading (HIGH/LOW)
Right sensorVCC5 VPower supply
Right sensorGNDGNDGround

Power and battery connections

VIN is the power input terminal on the Motor Shield. The barrel jack is the round power socket on the Arduino Uno.

ConnectionFromToNotes
Battery positiveBattery +Motor Shield VIN (or Arduino barrel jack)Powers Arduino and motors
Battery negativeBattery −Motor Shield GNDCommon ground
Power switch (optional)Inline on the battery + wireNo separate terminalToggle switch for on/off

Power tip: A 7.4 V LiPo battery works best because it provides stable voltage for both the Arduino and the motors. A 9 V alkaline battery will work for short tests, but it drains quickly under motor load and may cause inconsistent behavior. The motors are rated for 3 to 6 V, while the LiPo delivers 7.4 V (8.4 V when fully charged). The L298P driver on the shield drops about 2 V, and the PWM values used here (70 to 105 out of 255) lower the average motor voltage further. Do not run the motors at 255 for long periods. Check polarity before connecting the battery; reversed polarity can damage the electronics. Charge the LiPo only with a balance charger (a LiPo charger that keeps both cells at the same voltage). Stop using the pack before it drops below about 6.0 V (3.0 V per cell).

Step 5: Programming the Line-Following Robot

Here is the complete Arduino sketch (Arduino’s term for a program). The GitHub repository listed under Downloads has the full code and files.

Flowchart of the four sensor cases in the order the code checks them: 1 and 1 drive straight, 1 and 0 turn left, 0 and 1 turn right, 0 and 0 line lost, rotate

The flowchart shows the order in which the code checks the four cases. Its labels and speeds for the two pivot cases do not match the sketch; the table under How the Robot Works shows what the code does.

Complete Arduino sketch

/**
 * Author: Omar Draidrya
 * Date: 2024/12/06
 * Line-Following Robot with KY-033.
 */
// Motor control pins
#define DIR_L 12  // Left motor direction
#define DIR_R 13  // Right motor direction
#define PWM_L 3   // Left motor speed (PWM)
#define PWM_R 11  // Right motor speed (PWM)
// Sensor pins
int sensorLeft = 2;    // Left sensor
int sensorRight = 4;   // Right sensor
void setup() {
  // Configure motor pins as outputs
  pinMode(DIR_L, OUTPUT);
  pinMode(DIR_R, OUTPUT);
  pinMode(PWM_L, OUTPUT);
  pinMode(PWM_R, OUTPUT);
  // Configure sensor pins as inputs
  pinMode(sensorLeft, INPUT);
  pinMode(sensorRight, INPUT);
  // Serial communication for debugging
  Serial.begin(9600);
}
void loop() {
  // Read sensor values
  int left = digitalRead(sensorLeft);
  int right = digitalRead(sensorRight);
  // Output sensor data (debugging)
  Serial.print("Sensor Left: ");
  Serial.print(left);
  Serial.print("  |  Sensor Right: ");
  Serial.println(right);
  // Control logic
  if (left == 1 && right == 1) {
    // Drive straight
    controlMotors(HIGH, HIGH, 105, 105);
  } else if (left == 1 && right == 0) {
    // Turn right
    controlMotors(HIGH, LOW, 105, 85);
  } else if (left == 0 && right == 1) {
    // Turn left
    controlMotors(LOW, HIGH, 85, 105);
  } else {
    // Both readings 0
    // Rotate in place to find the line
    controlMotors(HIGH, LOW, 70, 70);  // Left motor forward, right motor backward
    // delay(500);  // Brief rotation
    // controlMotors(LOW, LOW, 0, 0);
  }
}
// Function to control motors
void controlMotors(int directionL, int directionR, int speedL, int speedR) {
  digitalWrite(DIR_L, directionL);
  digitalWrite(DIR_R, directionR);
  analogWrite(PWM_L, speedL);
  analogWrite(PWM_R, speedR);
}
    

5.1 Motor pin setup

#define DIR_L 12  // Left motor direction
#define DIR_R 13  // Right motor direction
#define PWM_L 3   // Left motor speed (PWM)
#define PWM_R 11  // Right motor speed (PWM)

These four pins control the two motors through the Motor Shield. A #define line gives a pin number a name. The compiler (the part of the Arduino software that translates the sketch into instructions for the board) replaces the name with the number before it builds the program. The sensor pins in 5.2 use int variables instead; for fixed pin numbers both work the same. DIR_L sets the direction of the left motor, DIR_R that of the right motor (HIGH = forward, LOW = backward on this shield). PWM_L and PWM_R set the speed of each motor with pulse width modulation, from 0 (stopped) to 255 (full speed).

5.2 Sensor pin setup

int sensorLeft = 2;  // Left sensor
int sensorRight = 4; // Right sensor

The signal pins of the two KY-033 sensors connect to digital pins 2 and 4.

5.3 The setup() function

void setup() {
  pinMode(DIR_L, OUTPUT);
  pinMode(DIR_R, OUTPUT);
  pinMode(PWM_L, OUTPUT);
  pinMode(PWM_R, OUTPUT);
  pinMode(sensorLeft, INPUT);
  pinMode(sensorRight, INPUT);
  Serial.begin(9600);
}

The setup function runs once when the Arduino starts. It sets the four motor pins as outputs and the two sensor pins as inputs. Serial.begin(9600) starts serial communication at 9600 baud (bits per second), so you can watch the sensor readings in the Serial Monitor. The Serial Monitor is the window in the Arduino software that shows the text the board sends.

5.4 Main loop sensor reading

int left = digitalRead(sensorLeft);
int right = digitalRead(sensorRight);

loop() runs over and over as long as the board has power. At the start of each pass, it reads both sensors with digitalRead(). The variable left holds the reading from the left sensor (pin 2), right the reading from the right sensor (pin 4).

5.5 Serial debugging output

Serial.print("Sensor Left: ");
Serial.print(left);
Serial.print(" | Sensor Right: ");
Serial.println(right);

Every pass of loop() prints both readings. Open the Serial Monitor (Tools > Serial Monitor, 9600 baud) to see both values live.

5.6 Decision logic

if (left == 1 && right == 1) {
  controlMotors(HIGH, HIGH, 105, 105);
} else if (left == 1 && right == 0) {
  controlMotors(HIGH, LOW, 105, 85);
} else if (left == 0 && right == 1) {
  controlMotors(LOW, HIGH, 85, 105);
} else {
  controlMotors(HIGH, LOW, 70, 70);
}

The four cases match the table in How the Robot Works. With both readings at 1, both motors run forward at 105. With left 1 and right 0, the code sets the left motor forward at 105 and the right motor backward at 85, so the robot pivots right until the sensor readings change. With left 0 and right 1, the left motor runs backward at 85 and the right motor forward at 105, so the robot pivots left. In the else branch (both readings 0) the robot rotates in place to the right at speed 70, left motor forward and right motor backward, until one of the readings changes.

5.7 The controlMotors() function

void controlMotors(int directionL, int directionR, int speedL, int speedR) {
  digitalWrite(DIR_L, directionL);
  digitalWrite(DIR_R, directionR);
  analogWrite(PWM_L, speedL);
  analogWrite(PWM_R, speedR);
}

controlMotors() bundles the four pin writes into one call: a direction (HIGH or LOW) and a speed (0 to 255) for each motor. It writes the direction to the DIR_L and DIR_R pins with digitalWrite() and the speed to the PWM_L and PWM_R pins with analogWrite(). Despite its name, analogWrite() does not output an analog voltage. It switches the pin on and off very fast (PWM), and the value 0 to 255 sets how long the pin stays on in each cycle. To change a movement, edit the numbers in the call.

Step 6: Testing and Tuning the Robot

A 50 mm wide strip of white adhesive tape on a dark background (a dark tabletop or dark poster board) works as a test track.

6.1 Prepare the test track

Start with a straight line of tape, 1 to 2 m long. Once the robot follows it reliably, add wide curves. Avoid 90-degree turns at first. My track in the video is white tape on a dark grey carpet: a closed loop of straight strips, with every change of direction split into 45-degree bends.

6.2 Sensor position and height

Mount the KY-033 sensors 5 to 15 mm above the ground. Set their spacing a few millimeters wider than the tape, so that each sensor sits outside one edge of the tape when the robot is centered. If the sensors are too high, they cannot distinguish the line from the background. If they are too low, they may scrape the surface or give inconsistent readings. If they are closer together than the tape is wide, both sit on the tape when the robot is centered, and the robot rotates instead of driving straight.

6.3 First test: Serial Monitor check

Connect the robot to your computer with the USB cable, open the Serial Monitor and move the robot over the line by hand. Each sensor should show 0 over the white tape and 1 over the dark background. If a reading does not change or flickers, adjust the KY-033 potentiometer or check the wiring. If your module shows 1 over the white tape, it switches the other way round: swap 1 and 0 in the if-conditions of loop(), as for a black line.

6.4 First run on the track

Place the robot on the track, centered on the line, and power it on. When everything works, the robot drives along the line and corrects left and right as the readings change. My robot followed the line, but it never drove smoothly; it swung from side to side the whole time.

6.5 Tuning motor speeds

If the robot overshoots turns, lower the speed values in the controlMotors() calls (105 and 85); if it stalls, raise them. Change them in steps of 5 to 10. Straight-line speed and turning speed can be tuned separately.

6.6 Tuning the rotation in place

When both sensors read 0, the robot rotates in place at speed 70. If it rotates too fast and overshoots, lower this value; if it rotates too slowly or stalls, raise it. The else branch contains two disabled lines (the // at the start turns a line into a comment). If you remove the //, the robot rotates for 500 ms (delay(500)) without reading the sensors. Then the motors stop, but only until the next pass of loop() reads the sensors and sets new motor values.

6.7 Improving turning smoothness

In the two correction cases the motors turn in opposite directions (105 forward, 85 backward), so the robot pivots almost on the spot. Bringing the two values closer together (for example 100 and 90) keeps their sum and the turning speed the same, but halves the forward movement. The pivot gets tighter. For smoother corrections, keep both motors running forward and slow down the motor on the side the robot turns toward.

Troubleshooting

Sensor and detection issues

ProblemPossible causeSolution
Robot spins in place continuouslyBoth sensors always read 0 (both see white)Turn the KY-033 potentiometer until the dark background reads 1. Check that the background is dark and that the sensor spacing is wider than the tape (see 6.2).
Robot does not detect the line at allSensor wiring is wrong or a sensor is faultyCheck that the signal wires go to pins 2 and 4 and that VCC and GND are connected. Test each sensor on its own in the Serial Monitor.
Both sensors always give the same readingSensors too close together or too high above the groundCheck the sensor spacing (see 6.2). Mount the sensors closer to the ground.
Unstable behavior or erratic movementsSensor height varies or the surface is unevenMount the sensors level and at a constant height. Test on a flat surface. Check for loose wires.
Robot follows poorly on curvesSpeed difference during pivots is too small or too largeAdjust the speed values in the two pivot cases. Try other combinations of forward and reverse speeds.
Motors are too fast or too slowPWM values do not match your motors and battery voltageChange the speed values (105, 85, 70) in steps of 5 to 10.

Motor and power issues

ProblemPossible causeSolution
Robot does not move at allBattery empty, power wiring wrong, or Motor Shield not fully plugged inCheck the battery voltage. Press the Motor Shield fully onto the Arduino headers. Check the motor terminal connections.
One motor runs, the other does notWiring fault on one motor channelCheck the motor terminal connections on the Motor Shield. Swap the motors to find out whether the motor or the shield channel is at fault.
Robot moves backward instead of forwardMotor wires reversedSwap the two wires on the affected motor terminal, or change the direction value (HIGH/LOW) in the code.

Frequently Asked Questions

Can I use the KY-033 to follow a black line on a white background?

Yes. Swap 1 and 0 in the if-conditions of loop().

Can I use a different motor driver instead of the Arduino Motor Shield Rev3?

Yes, for example the L298N module or the Adafruit Motor Shield. Update the pin definitions and, if needed, the motor control logic in the code to match the new driver.

How wide should the line be?

A width of 30 to 50 mm works with this two-sensor setup; 50 mm white adhesive tape is a good starting point. The tape has to be a little narrower than the sensor spacing (see 6.2), so that both sensors see the dark background when the robot is centered.

Downloads

Recommended Next Projects

Conclusion

This two-sensor version handles straight lines and wide curves. Each KY-033 reports only 0 or 1, so the robot either drives straight or pivots, and that causes the side-to-side swing. For smoother tracking and tighter curves, continue with the TCS34725 RGB sensor and PID control tutorial, which builds on this robot.

1 thought on “Building a Line Following Robot with KY-033 Sensors for Beginners”

Leave a Comment