Line Following

This page is still under development!

Algorithms for following the white lines on the RoboRugby table were discussed at the lecture in week 3.  This is a brief outline of some the possibilities, with some example programs (or fragments of programs).

Using One Sensor

With only one sensor, the options are limited.  The first basic approach is to drive straight while the sensor sees white (on the line) and turn when it sees black (off the line).  The problem is to know which way to turn - the single sensor tells you that you have wandered off the line, but gives no information about which side.  If you turn the wrong way for too long, you will find the line again, but pointing the wrong way!

A solution discussed in the lecture was to turn one way, looking for the line, but stop after a certain time (or angle).  Then turn the other way.  The assumption is that you have not wandered very far off the line, so if you do not find it quickly by turning one way, it must be the other way.

Here is an example - the details of how fast and how far to turn will vary, depending on the speed of your robot and where you have mounted the sensor.  Details of how to turn and how to decide whether the sensor is on a line are hidden in functions in this fragment of C, so that you can concentrate on the algorithm.

/* Line following example - one sensor.  Basic algorithm is:
   If on the line, drive straight.  If off the line, spin left for up to 0.2s.
   If line not found, spin right until find line (should really put limit on this).
   Keep doing all that until hit something or STOP is pressed...  */
   
   do 
   {
      if (line())  // if sensor is on the line
      {
         forward(80);   // drive straight
         printf("on line\n");
         beep();  // beep also wastes about 0.2s
      } 
      else // sensor is off the line
      {
         printf("off line\n");
         spinleft(50);  // try spinning left a bit - it can't be too far away...
         limit = seconds() + 0.2;   // allow 0.2 seconds to find line
         while(!line() && (seconds() < limit))
         {    // do nothing until we find the line, or time is up
         }
         // if we found the line, we can skip the next bit and go back to the top
         if (!line())  // if still no line must have hit time limit
         {
            spinright(50);    //try spinning right
            while(!line())
            {   // do nothing until line found (no time limit - dangerous)
            }
         }  // end of if(!line())
      }  // end of else
      
   }  while((!stop_button())&&(checkBump==0))  // repeat until bump or STOP

   ao();  // stop motors - it is all over
   printf("stopped\n");
            

The alternative approach is to deliberately drive in a curve while on the line, so that you know which side of the line you will drive off.  Then drive in a curve in the opposite direction until you find the line again.  This is a very simple algorithm, and easy to program - see the example below. However, it only really works well on straight lines - it can handle gentle curves, but not corners.  You will have to experiment with motor speeds to get your robot to drive in suitable curves - try one motor at 100 and the other at 20 to get started.

/* Line following example - one sensor.  Basic algorithm is:
   If on the line, drive in curve to left.  If off the line, drive in curve to right.
   Keep doing that until hit something or STOP is pressed...  */
   
   do 
   {
      if (line())  // if sensor is on the line
      {
         curveleft();   // drive, curving slightly left
         printf("on line\n");
         beep();      // beep also wastes about 0.2s
      }
      else              // sensor is off the line
      {
         curveright();  // drive, curving slightly right
         printf("off line\n");
      }
   }  while((!stop_button())&&(checkBump==0))  // repeat until bump or STOP

   ao();  // stop motors - it is all over
   printf("stopped\n");
            

Using Two Sensors

With two sensors, the first decision is what state of the sensors you regard as "on the line". The simplest option is to drive straight while both sensors see white (on the line). Then if one sensor sees black while the other sees white, you know which side you have wandered off, and you can curve or turn in the correct direction.  This is good for following a single line, but its behaviour at junctions is unpredictable.   You also have to decide what to do if neither sensor sees the line - you could reverse, or you could start hunting as in the first example above.

Alternatively, you could follow the edge of a line, by regarding "on the line" as one sensor seeing white while the other sees black.  Depending on which way around you define this, you could follow the left or right edge of the line, and thus take the left or right fork at a junction.

The second decision is how far apart to place the sensors.  If you want them both to see the line at the same time, you cannot have them more than about 15mm apart - the line is only about 19mm wide.  Putting the sensors closer together will allow the robot to wander a little further before it notices any change.  It also means that you have less time to respond to a change before both sensors move off the line, and you lose your direction information.

Note also that if you put the sensors very close together, each will respond to the light from the other as well as its own light.  This extra light will reduce the values given for black and white, so you may have to re-adjust your threshold value to suit.

This example has been tested using two sensors separated by a Lego brick (about 8mm), but the exact spacing is not critical.  It can follow the Z-shaped line on the RoboRugby table without any problems, but gets stuck when it comes to the end of a line - what to do there will depend on your strategy.  Details of the sensors and threshold value are hidden in a function - these will be different for each robot.  Other functions take care of moving the robot - these will also be specific to each robot.

/* Line following example - two sensors.
   The aim is to keep both sensors on the line.  Basic algorithm is:
   If both on the line, drive straight.  
   If off the line to the left, spin right.
   If off the line to the right, spin left.
   If both sensors are off the line, reverse (not a great solution!).
   Keep doing all that until hit something or STOP is pressed...  */
   
   while ((!stop_button()) && (checkBump()==0))
   {
      if (checkLine()==3)     // we are on the line
      {
         forward(100);  // drive forwards at full speed
         beep();  // give some feedback and waste some time
      }
      else if (checkLine()==2)  // we are off to the left
      {
         spinright(100);     // so turn to the right
      }
      else if (checkLine()==1)  // we are off to the right
      {
         spinleft(100);     // so turn to the left
      }
      else   // we have no idea where we are
      {
         reverse(50);  // so reverse and hope to get back on the line
      }
   }  // end of while loop
   ao();  // stop motors when exit from while loop
			

The checkLine() function could look like the example below.  Port numbers and threshold values will be different for each robot.

/* Function checkLine checks the state of two line sensors.
   It takes no arguments.
   It returns an integer:  0 means no line seen, 
                           1 means off line to the right, 
                           2 means off line to the left,
                           3 means on the line  */

// define the ports and the black/white threshold
#define LSENS analog(2)
#define RSENS analog(3)
#define THRESH 50

int checkLine(void)
{
    if((LSENS<THRESH) && (RSENS<THRESH))  // both sensors on line
      {
        return 3;  // return the value 3 - all is well
    }
    else if (RSENS<THRESH)    // right sensor still on the line
      {
        return 2;  // we have wandered off to the left
    }
    else if (LSENS<THRESH)    // left sensor still on line
      {
        return 1;   // we have wandered off to the right
    }
    else           // neither sensor on line - we are lost!
      {
        return 0;      
    }
}  // end of checkLine function
			

 

Copyright 2008, UCD School of Electrical, Electronic and Mechanical Engineering. Contact