Using a light sensor to follow a line

From ROBOTC API Guide
Jump to: navigation, search
ArduinoArduino Tutorials and Guided ProjectsLego + Arduino, Mobile Robotics Platform → Using an active light sensor to follow a line

Contents

Video

Lego+Arduino robot following a line using the light sensor


Why follow a line?

Following a line is an easy way to guide your robot without having to worry about positional errors caused by problems in turning or even just driving straight. By making the robot follow the line, you know exactly where it will go and where it will end up. Plus, it can be much shorter to code a long period of line following than to calculate the distances for turns and straights individually.

The primary downside to line following, however, is that there is not always a line to follow. We can place them for the robot in environments we control, but that may not always be an option. Additionally, when time is a constraint, the robot takes much longer to get from place to place following a line than driving forward and turning, simply because the robot has to edge its way up the line instead of just driving straight along it.

Programming a Line Following Pattern

We want the robot to move along a line, or really, a border between a light and dark area (the dark being the line, the light being the rest of the surface)

To do this, we're going to make use of our old friend, the if() else statement.

if (SensorValue[lightSensor] > 60) //if it sees a dark surface
 {
   //Swing turn left 
   motor[leftServo] = 80
 }
else // if it sees a light surface
 {
   //Swing turn right
   motor[rightServo] = 80
 }

This bit of code will cause the robot to turn left if it detects a dark surface, and right if it detects a light surface. On the boundary between light and dark (the line) the robot will switch between the two fairly rapidly and evenly, meaning that the robot will inch its way forward by alternating its wheels as it detects the two sides of the line. Note that the speed value on the motor is lower than we have used previously. This is because the robot may lose the line if it goes too far in one iteration of the loop and places its sensor on a surface beyond the strip of dark or light it is following, causing it to turn the wrong way, which may cause the robot to go in circles or even turn around and follow the line in the other direction. Be aware that the thinner the band of light or dark the robot is trying to follow, the greater the chance of it turning too far and losing the line.

Notepad.gif NOTE: The line following pattern will work no matter which servo you assign to the if() and else statements. Certain situations may make it more beneficial to turn right when it is light, and others to turn right when it is dark. Use your judgement based on the task you are attempting to accomplish.

When we place the code in an infinite while() loop, we get the following program:

#pragma config(CircuitBoardType, typeCktBoardUNO)
#pragma config(PluginCircuitBoard, typeShieldDFRobotMotor)
#pragma config(UART_Usage, UART0, uartSystemCommPort, baudRate200000, IOPins, dgtl1, dgtl0)
#pragma config(Sensor, anlg0,  lightSensor,    sensorReflection)
#pragma config(Sensor, dgtl3,  lightLED,       sensorDigitalOut)
#pragma config(Motor,  motor_5,         rightServo,    tmotorInternalHBridgeSinglePWM, openLoop, reversed, IOPins, dgtl5, dgtl4)
#pragma config(Motor,  motor_6,         leftServo,     tmotorInternalHBridgeSinglePWM, openLoop, reversed, IOPins, dgtl6, dgtl7)
//*!!Code automatically generated by 'ROBOTC' configuration wizard               !!*//
 
task main()
{
  while (true)
    {
     if (SensorValue[lightSensor] > 60) //if it sees a dark surface
      {
        //Swing turn left 
        motor[leftServo] = 80
      }
     else // if it sees a light surface
      {
        //Swing turn right
        motor[rightServo] = 80
      }
    }
 
}


Setting a Time Limit on Line Following

There may be a time when you don't want the robot to just keep following the line forever. If this is the case, we need to find a way to change the while() statement in order to compare it to the time. Unfortunately, we can't use wait1MSec() to count time because it does not return a value that the while statement can put into its condition. Loops and branching statements only work if they can return a boolean (true or false) to evaluate based on the logical statement in their condition. So, we need something that returns a numerical value that we can compare to another number.

To get what we want, we need to access the internal timers on the Arduino. There are four timers in total, designated "T1", "T2", "T3", and "T4". We'll only be accessing T1 for the moment, with a function, ClearTimer (), and a variable, time1[ ].

ClearTimer () simply resets the timer in parentheses to zero

ClearTimer (/* the timer you want to clear, T1, T2, T3, or T4 */)//resets timer value to zero

This is important because the timer begins to count the minute power comes into the Arduino. If the timer is not zeroed, the program may either perform the line following for a shorter time than intended, or skip over the while() statement with the line following commands altogether, simply because the timer value is well above the set value in the while() statement's condition.

The variable time1[ ] reads the value of the timer in brackets in millisecond units. Thus we can put it into the condition to compare it to the length of time we want the loop to run.

So, to put a time limit on the line following, we can modify the while condition to look something like this:

ClearTimer(T1);
 while (time1 [T1] < 10000) // keeps line following on for 10 seconds
    {
     if (SensorValue[lightSensor] > 60) //if it sees a dark surface
      {
        //Swing turn left 
        motor[leftServo] = 80
      }
     else // if it sees a light surface
      {
        //Swing turn right
        motor[rightServo] = 80
      }
    }

Replace the original loop in the program, and you get:

#pragma config(CircuitBoardType, typeCktBoardUNO)
#pragma config(PluginCircuitBoard, typeShieldDFRobotMotor)
#pragma config(UART_Usage, UART0, uartSystemCommPort, baudRate200000, IOPins, dgtl1, dgtl0)
#pragma config(Sensor, anlg0,  lightSensor,    sensorReflection)
#pragma config(Sensor, dgtl3,  lightLED,       sensorDigitalOut)
#pragma config(Motor,  motor_5,         rightServo,    tmotorInternalHBridgeSinglePWM, openLoop, reversed, IOPins, dgtl5, dgtl4)
#pragma config(Motor,  motor_6,         leftServo,     tmotorInternalHBridgeSinglePWM, openLoop, reversed, IOPins, dgtl6, dgtl7)
//*!!Code automatically generated by 'ROBOTC' configuration wizard               !!*//
 
task main()
{
 ClearTimer(T1);
 
 while (time1 [T1] < 10000) // keeps line following on for 10 seconds
    {
     if (SensorValue[lightSensor] > 60) //if it sees a dark surface
      {
        //Swing turn left 
        motor[leftServo] = 80
      }
     else // if it sees a light surface
      {
        //Swing turn right
        motor[rightServo] = 80
      }
    }
 
}

Notepad.gif NOTE: For longer programs that include multiple instances of line following, you may want to create a line following function with a variable for time

Personal tools
Namespaces
Variants
Actions
Navigation
Toolbox