RoboCatz.com

Line Following

RobotC Include File

Exercise in Line Following (for RobotC)

If you have RobotC, here is a good exercise in the task of LineFollowing. This exercise walks you through the different steps in setting up a line following task and running it from within the main task. The purpose of using a separate task for line following is so you can break from the line following based on the use of existing until functions such as untilSonar(), untilTouch(), untilDistance(), wait(), etc. Once the condition has been met in the until function, then stop the line following task to stop following the line.

exercise-in-line-following.htm


The getLightPercent() and getLightPortion() functions below may need to be created by you. The code below is more of a pseudocode than any actual code you can copy-and-paste into your program.

Line Following Methods (Example Programs)

Most line following programs utilize some formula for determining if the robot should move to the left, or right, in order to stay on the line. Whether the line is straight, curved, or a combination of both, the robot will use a simple formula to determine its position in relation to the line and how it should move in order to stay on the line.

There are dozens (probably hundreds) of different programs that can be written to help the robot follow a line. Several of the various programs are discussed and illustrated below.

Principle: If the robot has only one light sensor, the line following program will instruct the robot to follow only one side of the line (left or right depending on the formulas used in the program and the orientation of the motors in the robot design).

For NXT, many of the programs below use either "Calibrated" or "Un-Calibrated" light sensors. The are practical advantages and disadvantages to each. Some programs may work better with un-calibrated sensors--especially those that pass values directly to the steering option of the Move block. Programs with "Calibrated" light sensors may work in different environments and under different conditions without modification--all that needs to be done is to re-calibrate the sensors.

Method 1: Steering in the Move block

The Mindstorms NXT software provides a Move block which allows you to specify the amount of Steering in the form of an amount that varries between -100 and +100. Values less than 0 will steer the robot in one direction. Values greater than 0 will steer the robot in the other direction. If the value is equal to 0 then the robot will drive straight.

The basic idea in using this block for line following is that you want to get the robot to steer in one direction if the light sensor sees the black line and to steer in the other direction if the light sensor sees the white surface background.

There are two basic ways of creating the formula.

  1. Light Sensor Value minus a constant (k) equals the new steering amount
  2. Constant (k) minus the Light Sensor Value equals the new steering amount
Which formula you decide to use will depend on whether you want the robot to follow the left side or the right side of the line. The other part of the formula you need to determine is the value for the constant (k). In general, the value of the constant (k) should be set to the midpoint of the light range. The "light range" is the difference between the lowest and highest values obtained from the light sensor. For example, if the value of the light sensor on the white background is 50 and the value of the light sensor on the black line is 25, then the value of the constant (k) should be the midpont of those two values (50+25)/2 which equals 37.5. The duration of the Motor block should be sent to infinity. And the combination of light sensor, Subtraction with k, and Motor block should be placed within an infinite loop. The power level for the Motor block should be set to a low value at first and then gradually increase this to see how well the robot is able to follow the line

Tip: Which formula you decide to use will depend on whether you want the robot to follow the left side or the right side of the line.
Pseudocode for the smallest form of this program using Un-Calibrated light sensors:

k Minus Light Value equals Steering Formula: k - value = Steering

Light Value minus k equals Steering
Formula: value - k = Steering

Programs for Un-Calibrated Light Sensors

Tip: The smallest form of this program works best if using Un-Calibrated light sensors.

Download this program


Download this program

Method 2: Steering in the Move block Adjusted by a Factor

Problem: There is a potential problem if you use the above programs with a robot that has calibrated light sensors. The Midpoint of the light range for a calibrated light sensor has the value of 50. If the values returned by the light sensor range from 0 to 100, then the Midpoint is 50. The problem is that: Any values of Steering greater than 40 or less than -40 will result in the robot wheels moving in opposite directions. This has the undesireable effect of making the robot possibly move backwards. Our goal is to get the robot to move forward as it follows the line.

Try It: Run the two programs in the previous section using Calibrated light sensors. Remember that the value for N has to be the Midpoint of the light range. So, if you are using calibrated light sensors, then you will need to change the program to make N equal to 50.

Do you see the problem? The robot "overcompensates" (i.e., it turns to sharply) which makes it difficult to follow the line. There are a couple of things we can do to the program to make it work better with calibrated light sensors. If the value generated for the steering is too great, we can divide the generated value by a factor to reduce it.

Pseudocode for the larger form of this program using Calibrated light sensors:

k Minus Light Value divided by a factor equals Steering
Formula: (k - value) / factor = Steering

Light Value minus k divided by a factor equals Steering
Formula: (value - k) / factor = Steering
Tip: If you calibrate the light sensors using the Calibrate block, then the highest value (white background) would be 100 and the lowest value (black line) would be 0. This would make the Midpoint of the light range equal to 50 where (100+0)/2 = 50.
This larger form of the program is for use with Calibrated light sensors which have a midpoint of 50 in the values returned from dark to light. Although the Move block accepts steering values from -100 to +100, in general, steering values less than -40 or greater than +40 will result in the robot wheels turning in opposite directions for a very sharp turn. This amount of turning may be too great for the robot to remain on the line. Therefore, it may be necessary to reduce the value sent as the amount of steering. Dividing by a "factor" can help reduce this steering amount. The value for the factor depends on the degree of turns and speed of the robot. In general, you can use a larger "factor" for lines that are straight and for robots that are able to travel faster without losing the line. If the robot is on a line with curves or if the robot frequently loses the line, then you need to use a factor which is smaller.


Download this program


Download this program

Method 3: Proportional Steering

The basic idea in this line following program is make the light sensor value a portion (dividing by 100) and convert it to a steering scale (multiplying by 80 and then subtracting 40). Steering has a range of -100 to +100. However, if you test the Move block, you will find that values of steering less than -80 or greater than +80 have no change in performance. Effectively, the range for steering is from -80 to +80. Also note that when any values for steering are more than +40 or less than -40, the two motors will move in opposite directions. If the motors move in opposite directions then the robot will not have smooth movement. Therefore, to keep both motors moving forward, the range for steering will be limited to -40 to +40.

Pseudocode:
Light Sensor value divided by 100 times 80 minus 40.


Formulas:
   Light Sensor / 100 * 80 - 40 = Steering for the Move Block
which simplifies to:
   Light Sensor * .80 - 40 = Steering for the Move Block

Download this program

To change which side of the line the robot follows:

Example in RobotC


#include "LineFollowing.c";

task main() {
  initLightSensor();
  while(true) {
    if(getLightPercent()<50) {
      nSyncedMotors = synchCB;  // setting up C as master, B as slave
      nSyncedTurnRatio = (getLightPercent() * 2);
      motor[motorC] = 70;         // set the speed of the master motor
    } else {
      nSyncedMotors = synchBC;  // setting up B as master, C as slave
      nSyncedTurnRatio = (int) 100 - ((getLightPercent() - 50) * 2);
      motor[motorB] = 70;         // set the speed of the master motor
    }
  }
}
Download this program

Method 4: Switch on Midpoint of Light Range

The basic idea in this line following program is to have the robot move either the left or the right motor depending on the value of the light sensor. The values generated by the light sensor will range from low (on the black line) to high (on the white background). A logic switch is used to choose between to options: either move motor B or move motor C. The logic switch is controlled by the light sensor. A criteria is entered for comparison to determine which path the switch should choose. The criteria is set to the midpoint of the light range. For example, if the value of the light sensor on the white background is 50 and the value of the light sensor on the black line is 25, then the value of the midpoint of the range is (50+25)/2 which equals 37.5. If you are using Calibrated light sensors, then the Midpoint would be set to 50. If you are using Un-Calibrated light sensors, then you will need to determine the Midpoint through measuring the light values on the background and on the line.

Tip: If you calibrate the light sensors using the Calibrate block, then the highest value (white background) would be 100 and the lowest value (black line) would be 0. This would make the Midpoint of the light range equal to 50 where (100+0)/2 = 50.

Download this program

Follow one side of the line:


Download this program

To follow the other side of the line, change the comparison option from "less than" (<) to "greater than" (>):


Download this program

The Switch block allows you to enter a criteria for comparison and to specify what action to take if the observed light value is either "Greater (>)" or "Less Than (<)" the criteria. This choice allows directs the robot to follow either the Left or Right side of the line. If the robot is following the wrong side of the line, then change the direction of the comparison from (>) to (<) or from (<) to (>).

Tip: The direction of the comparison option determines if the robot follows the left or right side of the line.

Example in RobotC


#include "LineFollowing.c";

task main() {
  initLightSensor();
  int defaultPower = 50;
  while(true) {
    if(getLightPercent() < 50) {
      motor[motorB] = 0;
      motor[motorC] = defaultPower;
    } else {
      motor[motorB] = defaultPower;
      motor[motorC] = 0;
    }
  }
}
Download this program

Method 5: Stepped Switching Based on Light Range

The basic idea in this line following program is to increase the number of steps of steering. The typical implementation will have steps such as:
  1. Turn Sharply to the Left (Motor B 60% power; Motor C Stopped)
  2. Turn Gradually to the Left (Motor B 60% power; Motor 30% power)
  3. Go Straight (Motors B and C 60% power)
  4. Turn Gradually to the Right (Motor B 30% power; Motor 60% power)
  5. Turn Sharply to the Right (Motor B Stopped; Motor C 60% power)

Download this program

The program shown above assumes the light sensor has been Calibrated for the white background and black line. A "Calibrated" light sensor will return values that range from 0 to 100. Calibrating the light sensor helps to keep the program simple.

The range of values returned by a calibrated light sensor is 100. Divide this by 20 to get the five steps needed for the switch. The options for the Switch are shown below:


How it works.

The possible values returned from the Light sensor are from 0 to +100. Dividing this by 20 will give us a value which is probably rounded to the integer value when used in the Switch block. Each "Tab" in the Switch block corresponds to an integer value for the input. The integer values range from 0 to 4.

Example in RobotC


#include "LineFollowing.c";

task main() {
  initLightSensor();
  int defaultPower = 60;
  while(true) {
    int currLight = getLightPercent();
    if(currLight <= 20) {
      motor[motorB] = 0;
      motor[motorC] = defaultPower / 2;
    } else if(currLight <= 40) {
      motor[motorB] = defaultPower / 2;
      motor[motorC] = defaultPower;
    } else if(currLight <= 60) {
      motor[motorB] = defaultPower;
      motor[motorC] = defaultPower;
    } else if(currLight <= 80) {
      motor[motorB] = defaultPower;
      motor[motorC] = defaultPower / 2;
    } else {
      motor[motorB] = defaultPower / 2;
      motor[motorC] = 0;
    }
  }
}
Download this program

Method 6: Proportional Power Level

The basic idea in this line following program is to increase the power proportionally based on the values from the Light Sensor. The program assumes the light sensor has been Calibrated.

Pseudocode:
Divide the light sensor value by 100 which converts the difference to a portion that ranges from 0 to 1.
Now multiply that portion times the maximum power level to get the power for the first motor.
Subtract the power for the first motor from the maximum power level to get the power for the second motor.


Formulas:
   LightSensor / 100 * MaxPower = Power For Motor1
and
   MaxPower - Power For Motor1 = Power For Motor2

Download this program


Since the light range for a "Calibrated" Light Sensor is 100, we can divide the current light value by 100 to convert it to a portion to be multiplied by the Default Power level. This becomes the power level for Motor B. The difference between the Default Power Level and the power for Motor B becomes the power for Motor C.

You can change which side of the line the robot follows by changing which motor gets the initial power.

Example in RobotC


#include "LineFollowing.c";

task main() {
  initLightSensor();
  int defaultPower = 90;
  float currPower = 0;
  while(true) {
    currPower = getLightPortion() * defaultPower;
    motor[motorB] = currPower;
    motor[motorC] = defaultPower - currPower;
  }
}
Download this program

Method 7: Proportional Power Level With Constant

The basic idea in this line following program is to increase the speed of the robot by adding a constant level of power to each motor in addition to the proportionally adjusted power from the Light Sensor.

Tip: Try to keep the robot moving forward by adding a constant power to both motors.
Pseudocode:
Divide the light sensor value by 100 which converts the difference to a portion that ranges from 0 to 1.
Now multiply that portion times the maximum power level and add a constant to get the power for the first motor.
Multiply that portion times the maximum power level and subtract that product from the maximum power level to get the power for the second motor.


Formulas:
   LightSensor / 100 * MaxPower + Constant = Power For Motor1
and
   MaxPower - (LightSensor / 100 * MaxPower) + Constant = Power For Motor2

Download this program


Since the light range for a "Calibrated" Light Sensor is 100, we can divide the current light value by 100 to convert it to a portion to be multiplied by the Default Power level. Then add the Constant power. This becomes the power level for Motor B. The difference between the Default Power Level and the product of the Default power and the light sensor becomes the power for Motor C. Don't forget to add the Constant power to Motor C as well.

In summary, both motors get the Constant power. But one motor gets more, or less, of the variable power depending on the value obtained from the light sensor.

Tuning


Troubleshooting

Example in RobotC


#include "LineFollowing.c";

task main() {
  initLightSensor();
  int defaultPower = 55;
  int constantPower = 10;
  float currPower = 0;
  while(true) {
    currPower = getLightPortion() * defaultPower;
    motor[motorB] = currPower + constantPower;
    motor[motorC] = (defaultPower - currPower) + constantPower;
  }
}
Download this program

Method 8: Proportional Power (PID Minus the ID)

The basic idea in this line following program is to adjust the speed of each motor of the robot by adding (or subtracting) a variable level of power from a constant level of power.

Background: This method is based on a part of the classic PID controller. PID is short for Proportional, Integral, Derivative. A PID controller calculates an "error" value as the difference between a measured light value and the desired value (usually 50% if following a line). The controller attempts to minimize the error through the use of a formula. The PID controller formula involves three separate parameters: the proportional, the integral and derivative. Using a NXT robot, we can obtain excellent line following using just the "P" part of the formula. The Integral and Derivative are not needed at this level.

Tip: Adjust the performance of the robot by increasing or decreasing the variable amount of power used to calculate the product: Kp.
Pseudocode:
A "target" level of light is set to be 50%. Now take the current level of light and subtract it from the target level. The difference will be negative or positive depending on whether the current level is higher or lower than the target. Then divide this difference by 50 to convert it to a number between -1 and +1. Multiply that ratio by the amount of variable power. Then add this product to one motor and subtract the product from the other motor.

Formulas:
   Kp = ((LightSensor - 50) / 50) * Variable Power Level
and
   defaultPower + Kp = Power For Motor1
and
   defaultPower - Kp = Power For Motor2

Download this program

Since the light range for a "Calibrated" Light Sensor is 100, we can divide the current light value by 100 to convert it to a portion to be multiplied by the Default Power level. This becomes the power level for Motor B. The difference between the Default Power Level and the power for Motor B becomes the power for Motor C.

Tuning


Troubleshooting

Example in RobotC


#include "LineFollowing.c";

task main() {
  initLightSensor();
  int constantPower = 45;
  int variablePower = 20;
  float Kp = 0;
  while(true) {
    Kp = ((getLightPercent() - 50) / 50) * variablePower;
    motor[motorB] = constantPower + Kp;
    motor[motorC] = constantPower - Kp;
  }
}
Download this program
Or, to go in the opposite direction, use:

#include "LineFollowing.c";

task main() {
  initLightSensor();
  int constantPower = 45;
  int variablePower = 20;
  float direction = -1;
  float Kp = 0;
  while(true) {
    Kp = ((getLightPercent() - 50) / 50) * variablePower;
    motor[motorB] = direction * (constantPower + Kp);
    motor[motorC] = direction * (constantPower - Kp);
  }
}

Method 9: Proportional Power Level With 2 Light Sensors

The basic idea in this line following program is to get the robot to follow the line using a light sensor on each side of the line. Optimally, the two light sensors should each see Grey (half over the line and half over the white background). This program will subtract the value from one sensor from the value of the other sensor (i.e., it will calculate the difference between the two sensors). If the difference between the sensors is zero (i.e., each sensor is receiving the same amount of light), then the robot will apply 50% power to each motor (driving the robot straight). If one sensor sees more light than the other, then the difference between them will not be zero. The robot will then apply a greater percentage of power to one motor than to the other.

Pseudocode:
Calculate the difference between the light sensors and add 100 to the Difference.
Then divide that sum by 200 which converts the difference to a portion that ranges from 0 to 1.
Now multiply that portion times the maximum power level to get the power for the first motor.
Subtract the power for the first motor from the maximum power level to get the power for the second motor.


Formulas:
   ( LightSensor2 - LightSensor1 + 100 ) / 200 * MaxPower = Power For Motor1
and
   MaxPower - Power For Motor1 = Power For Motor2

Download this program

Tuning
To change which side of the line the robot follows, just change either:
  1. Which motor gets the initial power level.
  2. Which light sensor gets subtracted from the other (see below).
If this doesn't work, ...      try this one.


Since the light range for a "Calibrated" Light Sensor is 100, we can divide the current light value by 100 to convert it to a portion to be multiplied by the Default Power level. This becomes the power level for Motor B. The difference between the Default Power Level and the power for Motor B becomes the power for Motor C.

Example in RobotC


#include "LineFollowing.c";
#pragma config(Sensor, S2, lightSensorB, sensorLightInactive)
// Initialize a second light sensor.  
// The first one is already initialized in the "include" file above.

float getLightDiffPortion() {  
  float result;  
  long numerator1;  
  long numerator2;  
  long numerator;  
  // Normalize the two light sensor readings (i.e., put on a scale from 1 to 100)
  numerator1 = (float) (SensorRaw[lightSensorA] - tLowLight) / (tHighLight - tLowLight) * 100;
  numerator2 = (float) (SensorRaw[lightSensorB] - tLowLight2) / (tHighLight2 - tLowLight2) * 100;
  numerator = (float) numerator1 - numerator2 + 100;
  // Add 100 to the difference.  The resulting values should range from 0 to 200
  result = (float)  numerator / 200;
  // Divide by 200 to get a portion (ranging from 0 to 1) 
  //    of the power that should be applied to one motor
  return result;
}

task main() {
  initLightSensor();
  SensorType[S2] = sensorLightActive;  // Turn light 'on' if it is currently 'off'

  int defaultPower = 90;
  float currPower = 0;
  while(true) {
    currPower = getLightDiffPortion() * defaultPower;
    motor[motorB] = currPower;
    motor[motorC] = defaultPower - currPower;
  }
}
Download this program

Method 10: Two Light Sensors Controlling Steering

The basic idea in this line following program is to get the robot to follow the line using a light sensor on each side of the line. The robot should follow the center of the line. The robot attempts to minimize the differences between the two light sensors. Optimally, the two light sensors should each see Grey (half over the line and half over the white background). This program will subtract the value from one sensor from the value of the other sensor (i.e., it will calculate the difference between the two sensors). If the difference between the sensors is zero (i.e., each sensor is receiving the same amount of light), then the robot will set the steering in the Move block to zero (makes the robot move straight). If one sensor sees more light than the other, then the difference between them will not be zero. The robot will then apply a greater amount of steering (positive or negative) to the Move block causing the robot to turn one way or the other.

Calculate the difference between the light sensors which will result in a number from -100 to +100.
Multiple that difference by 0.40 to convert it to a value which ranges from -40 to +40.
Use this product as the value for Steering in a Move block.
Formulas:
   ( LightSensor2 - LightSensor1 ) * 0.40 = Steering for the Move Block

Download this program

How it works.

The Move block accepts a numeric value for Steering as input. The possible ranges are from -100 to +100. However, values less than -80 or greater than +80 do not alter the performance of the motor. Therefore, the effective range for steering is from -80 to +80. Values of steering which are below -40 or above +40 will result in the one of the motors moving backward (in the reverse direction). If our goal is to keep the robot moving foward, then we should avoid generating values which are below -40 or above +40.

When substracting the value from one light sensor from the other, the range of possible values is from -100 to +100. But we only want our steering values to range from -40 to +40. So, just multiply the difference by 0.40 to convert it to the range we want to use.

Requires the use of "Calibrated" Light Sensors so that the range of differences is from -100 to +100.

Tuning

If the turns are more gradual and less sharp or if the robot is moving at a slower speed, you can change the multiplier to 0.30 or 0.20 instead of 0.40. The robot will turn less sharply as the value of the multiplier is decreased. If the multiplier is reduced to zero (0.00), the robot will not turn at all.

Troubleshooting

  1. Reversing the sign of the multiplier from +0.40 to -0.40, or
  2. Switching which sensor is "subtracted" from the other (see below).
If this arrangement does not follow the line,      try this one.


Method 11: Color Sensor to Control Power

The basic idea in this line following program is to get the robot to follow the line using a color light sensor. The robot will try to follow the center of the line. If oriented properly, the color light sensor will "see" more green light when the sensor is toward one side of the black line and will "see" more blue light when the sensor is more toward the other side of the line. Internally, the sensor has a green and a blue LED light. However, the Green and Blue LEDs are next to each other and cast their light slighly to one side or the other of the light sensor. Using this difference in the relative positioning of the Blue and Green LED allows the robot to detect if the sensor is too far to the left or right of the line.

If the sensor is directly over the line, the sensor will see black. If the sensor is close to the line but off to one side, it will see blue. If it is close to the line but off to the other side, it will see green. If the sensor is completely off of the line, it will see white.

Pseudocode:
Color sensor uses a Green LED and Blue LED which are offset from each other.
Measure the color of light reflected from the surface.
If White, then you are off of the line (Oops). Force a turn to get back on the line.
If Green, turn one way and set a value for turning if you eventually see White.
If Blue, turn the other way and set a value for turning if you eventually see White.
If Black, just go straight.

Download this program

Orient the Color sensor onto the robot so that it will produce green light if one one side of the line and blue light if on the other side of the line.

Color sensors return a numeric value for the color where 1=Black, 2=Blue, 3=Green, 6=White.

Example in RobotC


#pragma config(Sensor, S3, colorPort, sensorCOLORFULL)

task main() {
  wait1Msec(200); // Give sensor time to initialize
  int lMotor = 70;
  int rMotor = 70;
  int cValue = 0;
  int turn = 0;

  while( true) {

    int cValue = SensorValue[colorPort];
    if(cValue == WHITECOLOR) { // You are off of the line. Force the robot to turn
      cValue = turn;
    }
    switch (cValue)  {
      case BLACKCOLOR: lMotor=70; rMotor=70; break; // Go Straight
      case BLUECOLOR: turn = BLUECOLOR; lMotor=70; rMotor=30; break;
      case GREENCOLOR: turn = GREENCOLOR; lMotor=30; rMotor=70; break;
    }
    motor[motorB] = lMotor;
    motor[motorC] = rMotor;
  }
}
Download this program

Method 12: Color Sensor to Control Steering

The basic idea in this line following program is to get the robot to follow the line using a color light sensor which tries to follow the center of the line. If oriented properly, the color light sensor will more green light when the sensor is toward one side of the black line and will show more blue light when the sensor is more toward the other side of the line. Internally, the sensor has a green and a blue LED light. However, the Green and Blue LEDs are next to each other and cast their light slighly to one side or the other of the light sensor. Using this difference in the relative positioning of the Blue and Green LED allows the robot to detect if the sensor is too far to the left or right of the line.

If the sensor is directly over the line, the sensor will see black. If the sensor is close to the line but off to one side, it will see blue. If it is close to the line but off to the other side, it will see green. If the sensor is completely off of the line, it will see white.

Pseudocode:
Color sensor uses a Green LED and Blue LED which are offset from each other.
Measure the color of light reflected from the surface.
If White, then you are off of the line (Oops). Force a turn to get back on the line.
If Green, turn one way and set a value for turning if you eventually see White.
If Blue, turn the other way and set a value for turning if you eventually see White.
If Black, just go straight.

Download this program

Orient the Color sensor onto the robot so that it will produce green light if one one side of the line and blue light if on the other side of the line.

Color sensors return a numeric value for the color where 1=Black, 2=Blue, 3=Green, 6=White.

Method 13: Wall Following

The basic idea in this wall following program is to get the robot to follow along the side of a wall. The touch sensor must be mounted onto the side of the robot. If the touch sensor is not pressed, the switch in the program will use the motor block which will steer the robot toward the wall. Once the touch sensor is pressed, the switch in the program will use the motor block which steers the robot slightly away from the wall.

Pseudocode:
If touch sensor is not touched, steer toward the wall.
If the touch sensor is pressed, then steer away from the wall.

Download this program


Method 14: PID Controller

Some of the examples above implemented simple proportional control systems where the error (deviation) from an expected value was used proportionally to control the motor power (i.e., the greater the error--or deviation--the greater the adjustment made to the motor power).

One problem with relying only on the current error value is that the robot will still oscillate as it traverses the line. Although the oscillations will decrease over time. A more sensitive line following program will keep track of these oscillations over time and try to use that information to help further adjust the power to the motors. One way of keeping track of the information in the oscillations is to keep a cumulative record of them and to store this cumulative value in the term we call the "Integral".

#include "LineFollowing.c";

// The value for the integralConstant needs to be adjusted (or "tuned") as needed

task main() {
  initLightSensor();
  int constantPower = 45;
  int variablePower = 20;
  int integralConstant = 8;
  float Kp = 0;
  float Ki = 0;
  while(true) {
    Kp = ((getLightPercent() - 50) / 50);
    Ki = Ki + ((getLightPercent() - 50) / 50);
    motor[motorB] = constantPower + (Kp * variablePower) + (Ki * integralConstant);
    motor[motorC] = constantPower - (Kp * variablePower) - (Ki * integralConstant);
  }
}
The next improvement to the above code is to add a "derivative" component which will examine the change in error (i.e., whether it is increasing or decreasing). For example, if the error is increasing, then you may want to apply more adjustments to reduce the error.

#include "LineFollowing.c";

// The values for the integralConstant and derivativeConstant need to be adjusted (or "tuned") as needed

task main() {
  initLightSensor();
  int constantPower = 45;
  int variablePower = 20;
  int integralConstant = 8;
  int derivativeConstant = 15;
  float previousError = 0;
  float Kp = 0;
  float Ki = 0;
  float Kd = 0;
  while(true) {
    Kp = ((getLightPercent() - 50) / 50);
    Ki = Ki + ((getLightPercent() - 50) / 50);
    Kd = (Kp - previousError);
    motor[motorB] = constantPower + (Kp * variablePower) + (Ki * integralConstant) + (Kd * derivativeConstant);
    motor[motorC] = constantPower - (Kp * variablePower) - (Ki * integralConstant) - (Kd * derivativeConstant);
    previousError = Kp;  // Each time through the loop we set the new value for the previousError
  }
}
The procedure for tuning the values of Kp, Ki, and Kd are as follows:
  1. Set the variablePower to zero (0).
  2. Set the integralConstant and the derivativeConstant to zero (0).
  3. Increase the variablePower until the robot starts to be able to follow the line (even though it may oscillate). This means, adjusting the value for variablePower higher until you see the robot oscillating as it travels the line.
  4. Now reduce the variablePower to approximately half of that value. Although this change will cause the robot to be unable to follow the line, we will adjust the other variables accordingly.
  5. Increase the integralConstant until the robot starts to be able to follow the line again.
  6. As the robot follows the line, it may run over (or into) something that causes it to lose the line. Increase the derivativeConstant if that happens so that the robot will be faster at re-acquiring the line.