![]() |
|
My other sites |
|
|
|
|||
| [Home] [Projects] [Hardware] [Software] [Books] [Links] [Downloads] | |||
|
|
|||
|
|
|||
This lesson shows one way to interface to the Analog Line sensors, and then assembles the things we've learned so far into a SUMO program.
BOB has three IR Reflective sensors. These can be used to detect the presence the ground, or they can be used to detect the difference between a black and white surface for Sumo or Line following. In this lesson we'll use them to detect the white line at the edge of a Sumo ring.
A lot of IR sensors produce an analog output where the voltage is somehow proportional to the amount of reflected IR light. In the past I've tried several different interface styles with the OOPic.
My first interface attempt just used the basic oA2D object which gives a 0-255 value representing 0 - 5V (if the 5V reference is used). The main program then just compared the oA2D value with some threshold to decide if the 'bot was over a white line. This worked fine, but it was a bit slow. I found that if I did too many comparisons like this my basic Sumo loop ran pretty slow (only several loops a second)
Next I tried assembling some Virtual circuits to read the voltage and then compare the value with a threshold and come up with a digital output. I used an oA2D and an oCompare object. This worked out quite well, with the main program running much faster now that it only needed to check a digital flag. The only downside was that the oCompare objected chewed up an extra 4 bytes of object memory, and since there were 3 sensors this meant an extra 12 bytes of the available 86 bytes.
Finally I came across the oA2DX variant of the analog input object. It only uses 2 extra bytes, and I can configure it to give me a digital flag indicating if the sensor is looking at a black or white surface. The trick with the oA2DX is that you can provide an Offset which is added to the +/- 127 range to move the center point to a particular threshold, then the Negative flag can be read to determine if the current value is above or below the threshold. The only trick is determining what the Offset should be set to.
As always, in this program (PBot_BOB-D_VC6.osc)
I start using the I/O definitions described on the main BOB [Software
page].
I'm going to use nearly all the BOB definitions. These are:
Just looking at the Line sensors for a moment... here are the Objects that I need for this Lesson:
// Analog Line Sensors
oA2DX LeftLine = New oA2DX; // Left Line Sensor
oA2DX RightLine = New oA2DX; // Right Line Sensor
In my programs, I always use the SetupIO( ) function configure the OOPic
"Objects" required to interface to the hardware.
In the case of the oA2DX objects, I'm going to initially configure them so
that they give me a value form 0 to 255 by setting the Offset to 127.
You may need to read the manual page for oA2DX several times before really
figuring out how it works.
Here is just the first part of the Setup code
for this lesson:.
//-----------------------------------------------------------------
// SetupIO()
// Configure all the IO Objects here
//-----------------------------------------------------------------
Void SetupIO( Void )
{
// --------------------------------
// setup the ActionTimer Timer for 1/10th Second ticks
// Preload counter with 5 second delay
// --------------------------------
ActionTimer.PreScale = CLOCK_PRESCALE;
ActionTimer.ClockIn.Link(ooPIC.Hz60);
ActionTimer = FIVE_SECONDS;
ActionTimer.Operate = cvTrue;
// --------------------------------
// setup the Line Sensors
// --------------------------------
LeftLine.IOLine = IO_LINE_LEFT;
LeftLine.Center = 127;
LeftLine.Operate = cvTrue;
RightLine.IOLine = IO_LINE_RIGHT;
RightLine.Center = 127;
RightLine.Operate = cvTrue;
Once the the two line objects are up and running, I can then use them to calculate a new Offset value. I do this in a function that I call AFTER the 5 second delay. This way I know that BOB has been placed on the ground and is ready to run. I've created a function called CalibrateLineSensors( ) to do the calculations:
I need to come up with threshold that is half way between the dark and light reflection values. Since BOB will start out on a dark surface (away from the Sumo edge) I can assume that the current values of the two line sensors are an indication of the "Dark" voltage, which will be a "high" number. I also know that the highly reflective white line gives a very "low" value (near zero) so I will choose a threshold that is half the current average of the two sensor readings.
This is a bit tricky, so follow along. I first read the two line sensors (I don't need the middle one), add them together and then divide by 4. If I just wanted the average value I would divide by 2, but I actually want half of the average so I divide by 4. Notice that I don't use the divide operator "/", instead I use the Shift operator ">>" as it's the fastest way to divide by a power of 2 (2,4,8,16,32 etc).
Then I reduce the current center value (127) by the threshold. This has the effect of lowering the reported value such that any voltage below the threshold will be negative, and any voltage above the threshold will be positive. Since white lines give low voltages, a negative value can be interpreted as a "white line". The oA2D object has a convenient flag property called Negative which I can now use directly to indicate the presence of a white line. Clear as mud right.... I know, it took me a while to figure it out. Here's the code.
//-----------------------------------------------------------------
// CalibrateLineSensors()
// This function sets the line sensor centers to the line threshold
// A white surface will then have a negative sensor value
// A black surface will then have a positive sensor value
//-----------------------------------------------------------------
Void CalibrateLineSensors(Void)
{
Byte Level;
// Read the two line sensors and determine a line threshold
// at 1/2 of the dark level (>>2 = divide by 4)
Level = (LeftLine.Value + RightLine.Value) >> 2 ;
// Set the offset such that levels below the threshold will appear negative
LeftLine.Center = 127 - Level;
RightLine.Center = 127 - Level;
}
I decided that a Sumo program was the ideal way to illustrate using the line sensors, as well as all the other objects we've been playing with, so I took my favorite Sumo strategy and built the main program.
This strategy only has 4 basic priorities.
Each of these conditions is turned into one of more IF statements that test the Line and Proximity sensors. The tests are performed in order of the priority list above. I used some of the Lesson C functions to control the motor drive. I've also used the Status LEDs to indicate which priority level is running.
Here's the code:
//-----------------------------------------------------------------
// BOB Main Program
//-----------------------------------------------------------------
Void Main(Void)
{
// Always set up the required IO and VC's first.
SetupIO();
// Wait for 5 second "Start Up" to elapse
While(ActionTimer.NonZero);
// Assume that BOB is on black surface by now. Calibrate sensors
CalibrateLineSensors();
// Run SUMO rules.
While (cvTrue)
{
// Run down the SUMO priority list
// Priority 1: Avoid the border
If (LeftLine.Negative)
{
// Set debug Status
LEDs.Value = 1;
// If we see the left line, backup & start Slow Spin Right
Drive(-80, -80, 5);
Brake();
Drive(40, -40, 0);
// Set timeout in case we get stuck seeking
ActionTimer = SEEK_TIMER ;
}
Else If (RightLine.Negative)
{
// Set debug Status
LEDs.Value = 1;
// If we see right line, backup & start Slow Spin Left
Drive(-80, -80, 5);
Brake();
Drive(-40, 40, 0);
// Set timeout in case we get stuck seeking
ActionTimer = SEEK_TIMER ;
}
// Priority 2: Push the opponent
Else If ((LeftEye == OPPONENT) & (RightEye == OPPONENT))
{
// Go forward
Drive(100, 100, 0);
// Set debug Status
LEDs.Value = 2;
}
// Priority 3: Turn towards the opponent
Else If (LeftEye == OPPONENT)
{
// Turn left towards the target
Drive(20, 100, 0);
// Set debug Status
LEDs.Value = 4;
}
Else If (RightEye == OPPONENT)
{
// Turn right towards the target
Drive(100, 20, 0);
// Set debug Status
LEDs.Value = 4;
}
// Priority 4: Move forward If seek timeout has expired.
Else
{
// Has Seek timer reached zero?
If (ActionTimer.Value == 0)
{
// Go forward
Drive(70, 70, 0);
// Set debug Status
LEDs.Value = 0;
}
else
{
// Set debug Status
LEDs.Value = 8;
}
}
}
}
To test the program I dropped BOB down into the middle of a Sumo ring (see my [download] page for Sumo rules and ring construction). With no opponents he drives to the edge of the ring, then slowly sweeps the ring for an opponent. After 5 seconds he gives up and heads out across the ring again. If I add an opponent into the ring, he turns towards the opponent and pushes hard. You go BOB!
Don't forget the full source code for this example is available on the [Download] page (PBot_BOB-D_VC6.osc).
Next, in [Lesson E] I will look at the Wheel Rotation encoders and start thinking about closed loop control..
Web content is copyright © PhilBot.com
2005, Deep Creek Lake, MD.
Contact: Phil Malone 301.387.2331, webmaster
@
PhilBot.com