Two-Color Sensor PID Line Following Robot

by AfrelEdTech in Circuits > Robots

17 Views, 0 Favorites, 0 Comments

Two-Color Sensor PID Line Following Robot

サムネイル.png

Line following is one of the most common beginner challenges in robotics—but doing it smoothly and accurately is not always easy.

Many simple robots use a single color sensor to detect whether they are on or off the line. However, this can lead to jerky motion and poor handling around curves.

In this project, we use two color sensors—one on each side of the line—and apply PID control (Proportional, Integral, Derivative) in C to build a stable and responsive robot.

Supplies

  1. LEGO SPIKE Prime
  2. 2 × LEGO Color Sensors
  3. 2 × Motors (left and right wheels)
  4. 1 PC with Windows 10 or 11 operating system

Build the Robot

  1. Right Motor : Port A
  2. Left Motor : Port B
  3. Right Color Sensor : Port C
  4. Left Color Sensor : Port D

Build a simple differential drive robot with two motors and two color sensors mounted at the front, slightly apart, so they can detect both sides of the black line.

Try to keep the sensors at the same height. The optimal sensor distance may vary depending on your track’s line thickness—test and adjust!

Why Two Sensors? and What Is PID Control?

With two sensors, you can compute an "error" that tells how far off-center the robot is:

// Calculate error (difference between right and left sensor values)
float error = (float)valR - (float)valL;
  1. If the right sensor sees more white (higher value), and the left sees black, the robot is drifting right → error is positive
  2. If the left sensor sees more white → drifting left → error is negative

This error value is used in the PID calculation.


PID stands for:

  1. P: Proportional — corrects based on how far off the robot is (the error)
  2. I: Integral — corrects small errors that accumulate over time (long-term drift)
  3. D: Derivative — reacts to sudden changes in error

Each part of PID helps in a different way to make the robot follow the line more smoothly.

Implement PID Control

We started by using only P and D, and set I = 0. This makes the robot more stable during initial tuning.


Full code:

pup_motor_t *motorR, *motorL;
pup_device_t *ColorSensorR, *ColorSensorL;

void Main(intptr_t exinf)
{
// Wait for startup (3s)
dly_tsk(3000000);
// PID control variables
int32_t valL, valR; // Reflection values from left and right sensors
int32_t lspeed, rspeed; // Motor speeds for left and right
int32_t base_speed = 30; // Base speed for motors
float mv; // Manipulated variable
float kp = 0.8f; // Proportional gain
float ki = 0.0f;   // Integral gain
float kd = 1.0f; // Derivative gain
float integral = 0.0f; // Integral term
float prev_error = 0.0f; // Previous error

// Initialize motors and sensors
motorR = pup_motor_init(PBIO_PORT_ID_A,PUP_DIRECTION_CLOCKWISE);
motorL = pup_motor_init(PBIO_PORT_ID_B,PUP_DIRECTION_COUNTERCLOCKWISE);
ColorSensorR = pup_color_sensor_get_device(PBIO_PORT_ID_C);
ColorSensorL = pup_color_sensor_get_device(PBIO_PORT_ID_D);

while(1) {
// Get sensor values
valL = pup_color_sensor_reflection(ColorSensorL);
valR = pup_color_sensor_reflection(ColorSensorR);

// Calculate error (difference between right and left sensor values)
float error = (float)valR - (float)valL;

// PID calculation
integral += error;
float derivative = error - prev_error;
mv = kp * error + ki * integral + kd * derivative;
prev_error = error;

// Calculate motor speeds based on PID output
lspeed = base_speed - (int32_t)mv;
rspeed = base_speed + (int32_t)mv;

// Limit motor speeds
if (lspeed > 100) lspeed = 100;
if (lspeed < -100) lspeed = -100;
if (rspeed > 100) rspeed = 100;
if (rspeed < -100) rspeed = -100;

// Set motor power
pup_motor_set_power(motorL,lspeed);
pup_motor_set_power(motorR,rspeed);
}
}

Tuning the Gains — Especially Ki

Once P and D are tuned, try adding a small I (Integral) term to correct for long-term drift.

Be careful:

In C, the control loop runs very fast—much faster than in block-based environments. This means the integral value accumulates error at each cycle, and with such a fast loop, it can grow too large very quickly, leading to unstable or erratic motion.

That’s why Ki needs to be very small. In our case, we settled on:

int32_t base_speed = 30; // Base speed for motors
float kp = 0.8f; // Proportional gain
float ki = 0.00005f; // Integral gain
float kd = 1.0f; // Derivative gain

Tune gradually, test frequently.

Speed Up the Robot

Once your PID values are stable, try increasing the robot’s speed.

But be aware:

Simply increasing the speed without adjusting the PID values may cause the robot to wobble or lose the line—especially in curves.

You may need to reduce the PID gains slightly to keep things stable at higher speeds. In our case, we settled on:

int32_t base_speed = 50; // Base speed for motors
float kp = 0.6f; // Proportional gain
float ki = 0.00001f; // Integral gain
float kd = 0.8f; // Derivative gain

See It in Action

lineFollower2color

【Free Trial】Start C Language Programming With Afrel's Educational Materials

Are you intrigued by the idea of programming your SPIKE™ Prime with C? Did you know that Afrel is a key information provider for SPIKE™ Prime?


To meet the demand of those who want to "learn more about SPIKE™ Prime " and "try programming in C," Afrel has developed and sells educational materials for learning C language programming with SPIKE™ Prime.

Stepping up from Python to C is an excellent opportunity to further enhance your programming skills. Learning C will give you a deeper understanding of how computers work and enable more advanced control.


Afrel's C language materials are designed for beginners, ensuring a smooth learning experience. Why not take this chance to discover the joy of controlling SPIKE™ Prime with C and expand your programming horizons?


If you're ready to tackle C language programming, click the link below for more details.


Learn more about SPIKE™ Prime C Language Programming Materials here