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
|