Tutorials/Arduino Projects/Mobile Robotics/VEX/Using the line tracker to follow a line

From ROBOTC API Guide
Jump to: navigation, search

Why line following?

Following a line is one of the easiest ways for a robot to successfully and accurately navigate. It is a determined path, and good programming can ensure results that are far more consistent than if the robot was simply told where to go without any reference. It is the difference between you finding your way to the bathroom normally, and being told the steps you need to take, then blindfolded and sent on your way.

This is all well and good, but how do we get our robot to follow the line? It's actually pretty simple. Let's write a program.

Notepad.gif NOTE: The following instructions apply to a robot following a dark line on a light surface. If you are following a light line on a dark surface (for example, on VEX competition fields), you will need to adjust the code accordingly. The line should also be between 0.25" and 0.75" wide.

Configuring RobotC

In addition to the servos, you will need to configure:

  • analog input 0 as "leftIR", sensor type 'Line Follower'
  • analog input 1 as "centerIR", sensor type 'Line Follower'
  • analog input 2 as "rightIR", sensor type 'Line Follower'
  • digital input 2 as "button", sensor type 'Touch'.

Your configuration code should look like this:

#pragma config(CircuitBoardType, typeCktBoardUNO)
#pragma config(UART_Usage, UART0, uartSystemCommPort, baudRate200000, IOPins, dgtl1, dgtl0)
#pragma config(Sensor, anlg0,  leftIR,         sensorLineFollower)
#pragma config(Sensor, anlg1,  centerIR,       sensorLineFollower)
#pragma config(Sensor, anlg2,  rightIR,        sensorLineFollower)
#pragma config(Sensor, dgtl2,  button,         sensorTouch)
#pragma config(Motor,  servo_10,        rightServo,    tmotorServoContinuousRotation, openLoop, reversed, IOPins, dgtl10, None)
#pragma config(Motor,  motor_11,        leftServo,     tmotorServoContinuousRotation, openLoop, IOPins, dgtl11, None)
//*!!Code automatically generated by 'ROBOTC' configuration wizard               !!*//

Programming

The concept

How does one actually go about following a line? There are many ways to do it, ranging from extremely simple zig-zagging to calculus-based PID control. We are going to use our three sensors to our advantage. Basically, if the robot is functioning properly, there are three possible states:

  • The center sensor seeing the line
  • The left sensor seeing the line
  • The right sensor seeing the line.

If we use these conditions to alter the way the robot drives so that it always steers towards the line, the robot will effectively be following the line.

How the robot will follow the line.

The code

The first thing to write is the calibration code. It is very similar to what we saw in the light sensors lesson, so we won't look at it in-depth. The important thing to note is that we are using only one sensor, as line trackers don't have the photoresistor's problem of variance between sensors. As before, when running the program, you will simply need to position the center line tracker on the light (non-line) surface and the black (line) surface before pressing the bump switch, which will start the program.

Here's the calibration code:

  int lowIR = 1000; //Arbitrary high number so the calibration will work
  int highIR = 0;
 
  //Auto-calibration ended by button press. The button will begin the line following.
  //During this time, the robot should be moved so that it sees both black and white - 
  //you could 'scan' it across the line.
  while(!SensorValue[button]) {
    if(SensorValue[centerIR] > highIR) {
      highIR = SensorValue[centerIR];
      } else if(SensorValue[centerIR] < lowIR) {
      lowIR = SensorValue[centerIR];
    }
  }

Now we can get into the line following code. But before that, we need to consider this: How do we test if a sensor is seeing 'dark'? At first, something like this may come to mind (remember, higher sensor values indicate a darker surface):

if(SensorValue(centerIR) > 500) {
  //Do something
}

However, due to differing light conditions and materials used to make the line, dark may be a different value. You might then think of using the values stored in our calibration routine:

if(SensorValue(centerIR) >= highIR) {
  //Do something
}

But this will only trigger if the sensor sees exactly the darkest spot on the line. This has many problems and would not likely work because of sensor differences. We want to leave tolerance for these sorts of things. To do that, we are going to generate a threshold value from the average of the white and black values. This means that if the sensor reads more than this average, we can be sure the robot is seeing black. If it sees less, then the robot will be seeing white.

Here's how we find the threshold by taking the average of the high and low calibration values:

//Set threshold as the average of light and dark.
int threshold = (lowIR + highIR) / 2;

Now we can write the code to steer or drive straight depending on which sensor sees dark:

while(true)
{
  // RIGHT sensor sees dark:
  if(SensorValue(rightIR) > threshold)
  {
    // counter-steer right:
    motor[leftServo]  = 40;
    motor[rightServo] = 0;
  }
  // CENTER sensor sees dark:
  if(SensorValue(centerIR) > threshold)
  {
    // go straight
    motor[leftServo]  = 40;
    motor[rightServo] = 40;
  }
  // LEFT sensor sees dark:
  if(SensorValue(leftIR) > threshold)
  {
    // counter-steer left:
    motor[leftServo]  = 0;
    motor[rightServo] = 40;
  }
}

Putting it all together, your program should look like this:

#pragma config(CircuitBoardType, typeCktBoardUNO)
#pragma config(UART_Usage, UART0, uartSystemCommPort, baudRate200000, IOPins, dgtl1, dgtl0)
#pragma config(Sensor, anlg0,  leftIR,         sensorLineFollower)
#pragma config(Sensor, anlg1,  centerIR,       sensorLineFollower)
#pragma config(Sensor, anlg2,  rightIR,        sensorLineFollower)
#pragma config(Sensor, dgtl2,  button,         sensorTouch)
#pragma config(Motor,  servo_10,        rightServo,    tmotorServoContinuousRotation, openLoop, reversed, IOPins, dgtl10, None)
#pragma config(Motor,  motor_11,        leftServo,     tmotorServoContinuousRotation, openLoop, IOPins, dgtl11, None)
//*!!Code automatically generated by 'ROBOTC' configuration wizard               !!*//
 
task main()
{
  int lowIR = 1000; //Arbitrary high number so the calibration will work
  int highIR = 0;
 
  //Auto-calibration ended by button press. The button will begin the line following.
  //During this time, the robot should be moved so that it sees both black and white - 
  //you could 'scan' it across the line.
  while(!SensorValue[button]) {
    if(SensorValue[centerIR] > highIR) {
      highIR = SensorValue[centerIR];
      } else if(SensorValue[centerIR] < lowIR) {
      lowIR = SensorValue[centerIR];
    }
  }
 
  //Set threshold as the average of light and dark.
  int threshold = (lowIR + highIR) / 2;
 
  // Light following loop
  while(true)
  {
    // RIGHT sensor sees dark:
    if(SensorValue(rightIR) > threshold)
    {
      // counter-steer right:
      motor[leftServo]  = 40;
      motor[rightServo] = 0;
    }
    // CENTER sensor sees dark:
    if(SensorValue(centerIR) > threshold)
    {
      // go straight
      motor[leftServo]  = 40;
      motor[rightServo] = 40;
    }
    // LEFT sensor sees dark:
    if(SensorValue(leftIR) > threshold)
    {
      // counter-steer left:
      motor[leftServo]  = 0;
      motor[rightServo] = 40;
    }
  }
}

Run the program, and you should see the robot follow the line smoothly!