![]() |
|
My other sites |
|
|
|
|||
| [Home] [Projects] [Hardware] [Software] [Books] [Links] [Downloads] | |||
|
|
|||
|
|
|||
| Sample Programs |
I particularly like the [OOPic] version of the Mark III, that lets me use the C programming language with a suite of predefined "Objects" to interface to the robot. In accordance with my standard philosophy of building modular, reusable code, I decided that I would design a stand-alone OOPic object class to encapsulate all of the I/O devices and capabilities on the Mark III.
The OOPic compiler is pretty cool: in addition to all the predefined Objects, it lets you, the programmer, create your own Objects. This is done by putting all the code and data for a new object in one file, and then referencing that file when you create an oUserClass object in the calling program. For example, I decided to create a PhilBot user class for the Mark III MiniSumo using all Virtual Circuits. I called it PBot_MkIII_VC.
I have recently re-written my code for the new Rev 6.0 OOPic compiler. This new version is called: [PBot_MkIII_1.0_VC6], and that's what I'm describing here.
What I wanted was a generic UserClass that I could use as the basis for all my future Mark III projects. So I sat down and figured out my requirements:
I've included the full [PBot_MkIII_1.0_VC6] Class source code on my [downloads] page, so it would probably help if you downloaded and printed it out for reference.
I
figured I'd need some help debugging my programs, so I added some
LED's to the Bot and used these to display the flags being set by the UserClass.
I purchased the Mark III Prototyping board, and mounted a bank of 8 LED to the
board. I wired these LEDs to the bank "D" digital I/O port, (I/O 24 to 31)
and tied them to the 0V rail via 200 ohm resistors (See pic at right. Click
pic for full view).
The most important task with any robot is defining the I/O allocations. I always do this FIRST, at the top of my program as constants or defines. Here's the code I used for the new MarkIII UserClass.
//-----------------------------------------------------------------Notice the standard naming convention that I use here and in other programs:
The constants are all named as IO_ + Function_ + Position.
Tip: If you create simple rules for variable names,
it's always easier to read & understand your code later on.
After defining the I/O Assignments, then I declare the various Objects. I'm going to use oA2DX for all of the analog inputs because these will let me set an offset that splits the +/- range at the threshold that I'm trying to detect. Then the yes/no level of the sensor can use the Negative property as a boolean (single bit) flag. I'll use these flags to change each status LED so that I can see the sensors working. I'll also declare an oCountdown object to use for system timing. Many programs can use the OOPic.Delay property for pausing the program, but by using the oCountdown object I can do other things while I'm waiting for the timer to elapse. Here are the objects that I declare:
//-----------------------------------------------------------------
Once the objects have been declared I define the various functions to configure the objects and virtual circuits.
Tip: I find that a consistent program structure can
make my coding and debugging life easier, so I always define two functions:
SetupIO( ) and SetupVC( ). SetupIO( ) just configures the objects that are
used to interface to the various sensors and actuators. SetupVC( ) is used
to make any Virtual Circuit connections.
Here's the SetupIO( ) function:
//-----------------------------------------------------------------
Some things to note:
Next I configure the few remaining VC Objects. All I need here are some oWire objects to transfer the oA2DX outputs to their associated LEDs. I can also invert some bits so that the required condition is indicated with a cvTrue level. Here's the SetupVC( ) code:
//-----------------------------------------------------------------The next thing I need to do is provide a way to calibrate the three line sensors. I provide two calibration routines, one for Sumo and one for LineFollowing. I need two different routines since the starting conditions are different for the two different competitions. Sumo assumes that the robot is starting out on an all-black section of the ring, so SumoCalibrate( ) takes the average of all three sensors, and then sets a threshold equal to 3/4 of this average. Line following assumes that the robot starts out straddling the white/black line, so FollowerCalibrate( ) takes the average of the two outer sensors, and then picks a threshold half way between this value and the central sensor. In both cases the routine then sets all the oA2DX .Offset values to "127 - threshold". So, any input below the threshold will read a negative value. Here's the two calibrate functions:
//-----------------------------------------------------------------
// FollowerCalibrate()
// This function is used to measure the current reflected IR from
// a White or Black Line by the Line sensors.
// The threshold is set halfway between the inner and outer sensors
// This call assumes the Bot is centered on the line.
//-----------------------------------------------------------------
Void FollowerCalibrate(Void)
{
// Set all the lines back to zero offset
LeftLine.Center = ZERO_OFFSET;
CenterLine.Center = ZERO_OFFSET;
RightLine.Center = ZERO_OFFSET;
// Read the two outer sensors & take average
TempWord = (RightLine.Value + LeftLine.Value) >> 1 ;
// Read the inner sensors & take average with outer average
TempWord = (CenterLine.Value + TempWord) >> 1 ;
// Set a new offset to make A "white line" negative.
LeftLine.Center = ZERO_OFFSET - TempWord;
CenterLine.Center = ZERO_OFFSET - TempWord;
RightLine.Center = ZERO_OFFSET - TempWord;
}
//-----------------------------------------------------------------
// SumoCalibrate()
// This function is used to measure the current reflected IR from
// the SUMO ring by the Line sensors and determines a "White Line"
// threshold by taking 3/4 of the current level.
// This call assumes the Bot is NOT on a white line at the time
//-----------------------------------------------------------------
Void SumoCalibrate(Void)
{
// Set all the lines back to zero offset
LeftLine.Center = ZERO_OFFSET;
CenterLine.Center = ZERO_OFFSET;
RightLine.Center = ZERO_OFFSET;
// Read each of the three line sensors and calculate a line level
// Equal to 3/4 of the initial level
TempWord = (RightLine.Value + CenterLine.Value + LeftLine.Value) >> 2; ;
// Set a new offset to make A "white line" negative.
LeftLine.Center = ZERO_OFFSET - TempWord;
CenterLine.Center = ZERO_OFFSET - TempWord;
RightLine.Center = ZERO_OFFSET - TempWord;
}
Tip: Notice that before taking sensor readings, it was important to set the Offset values back to 127. Although this was done when the program started, there's nothing stopping someone calling these functions more than once, so it's important to expect the unexpected and make sure the program can deal with it.
The last function that was need is an easy way to set the wheel speeds. In many cases the robot wants to move at a certain speed for a specific amount of time, so I also included this capability in the Drive( ) function. I also made a speed request of zero disable the servo drive. This eliminates any creep from a miss-calibrated servo. Remember when using the Drive( ) function that to turn RIGHT you have several choices, but all of them involve moving the LEFT wheel faster then the RIGHT. Here's the Drive( ) code:
//-----------------------------------------------------------------
// Drive(LeftSpeed, RightSpeed, DriveTime)
// This function drives both wheels at the indicated speeds
// The motion is held for "DriveTime" tenths of a second
// No time limit is placed if Time = 0
//
// Positive values are forward, Negative are backwards.
// Max range is +/- 18
//
//-----------------------------------------------------------------
Void Drive(Byte LeftSpeed, Byte RightSpeed, Byte DriveTime)
{
// Set left wheel speed (Disable if zero)
If (LeftSpeed == 0)
{
LeftWheel.Value = WHEEL_STOP ;
LeftWheel.Operate = cvFalse;
}
Else
{
LeftWheel.Operate = cvTrue;
LeftWheel.Value = WHEEL_STOP + LeftSpeed ;
}
// Set right wheel speed (Disable if zero)
If (RightSpeed == 0)
{
RightWheel.Value = WHEEL_STOP ;
RightWheel.Operate = cvFalse;
}
Else
{
RightWheel.Operate = cvTrue;
RightWheel.Value = WHEEL_STOP + RightSpeed ;
}
// Continue action for requested time period in tenths.
If (DriveTime > 0)
{
ActionTimer = DriveTime;
While(ActionTimer.NonZero);
}
}
Now that you know all about PBot_MkIII_1.1_VC6 you can forget about it, because the whole point is that you don't ever need to change it to write lots of different Mark III programs, you just need to reference it in your program. Your Sumo and Line follower programs can just concentrate on the STRATEGY. To see what I mean, I've created several sample programs. Use these as a starting point to create your own Uber clever Sumo bots.
All the OOPic 6.0 sample programs are available on my [download] page, and I explain how they work on my 6.0 [MkIII Software] page.
Web content is copyright © PhilBot.com
2005, Deep Creek Lake, MD.
Contact: Phil Malone 301.387.2331, webmaster
@
PhilBot.com