I won a Solarbotics Sumovore kit during the 2004 Western Canadian Robot Games. I usually scratch build my own chassis but since this fell into my lap I decided to see what I could do with it. Currently, I plan to make it part of Climber's Murderous Robot Swarm but figured I would start out by seeing how good I can make it follow a line (all Murderous Robot Swarms have to start somewhere.) I adapted the code from my Gunnagitcha competition line-following robot. Although the sensor suite isn't quite as optimized for line following as Gunnagitcha's is I was stll able to make it follow a line very accurately and quite quickly. A bot like this could easily score over a thousand points on the line-following tracks that appeared at the 2004 games.
Here is my copy of the integrator. Click on it for a larger pic.
Here is my large line following torture track I have the back of
the board with the "integrator" above. It's a little more
challenging. I use this to train my line followers. The sumovore
with the atmel brainboard can tackle this track except for the tiny
loop in the center against the bottom edge. Click on it for a
larger pic.
Because the guys at Solarbotics are so cool I decided to whip up this web page and put it on the net so other owners of sumovores could try this code out and see how well it does at their line following competitions. I assume the reader understands basic electronics and is able to download a program to their bot. A link to the complete source code appears at the end of this web page. I use avr-libc and gcc for all my AVR programming but this code should be easy to adapt for other compilers.
One of my primary goals for this version was to keep the kit as stock as possible and do most of the work in software. The only physical mods were to add ways the user can change system parameters without having to reprogram the bot by adding a couple of configuration jumpers. Version two of the Killer Sumovore Line Following Robot will have more mods.
First, make a configuration jumper. The brainboard has three headers for auxilliary functions or servos. We'll make use of header three. Take a spare .1 inch center three-pin female header, snip out the center pin and solder a 500 ohm resistor between the two remaining pins. When this "jumper" is installed on the header it will pull that corresponding pin on the microcontroller low. When removed the mega8's internal pullup resistor will pull it high. Header three will be used to choose the bot's speed. Some really complex tracks are tough to stay on the line when going at top speed. By default the bot will go slowly for maximum accuracy. Installing the jumper will make the bot go top speed.
IMPORTANT: do not be tempted to solder a wire between the pins on this jumper. If you make a mistake in the software and output a value to the corresponding pin the jumper is attached to you could end up with a short circuit and all the sumovore's magic smoke will then escape. The 500 ohm resistor ensures that if a mistake is made only 10 milliamps will flow at most. That's well within the safety zone. Because the mega8's internal pullup resistors are 10-20k a 500 ohm resistor is small enough so that the tap into the resulting voltage divider is close enough to ground to be visible to the processor as a low.
Now let's take a look at the software. HERE is the code. Print it out and follow along as I describe it below.
When I program a bot using the Atmel processor I always have a function called init. This function does all of the setup that I like to get done. It sets the output ports, turns on any internal pullups, sets up internal registers and other stuff that is usually only called once. Note that I have a big comment block at the beginning that has the pinout of the processor I am using. Very handy to have.
The motortab table was something I put in way back when I noticed that two motors could run at slightly different speeds at the same level of PWM. That would mean when I programmed the bot to go straight it wouldn't. Kind of annoying. Normally, when you want the motors to run at a predictable speed you measure the RPM and adjust accordingly. I don't happen to have an encoder on my sumorvore's wheels (yet) so I compensate by putting in a table like this that I can tweak later. At this time the motors seem to run ok so I will leave the table symmetrical but leave it in in case I want to change it later.
There are three indexes to this table. The first is for the speed we want to run the motor at relative to the top speed, the second is to choose the overall bot top speed and the last is for which motor, left or right. The top speed is set by the configuration jumper I mentioned above. Normally I have four top speeds for my bots but, for now, we will confine ourselves to just two. When the jumper is installed speed will be set to 1 (the max).
We have 5 sensors out front to detect white/black surfaces. What I did is divide these sensors into two groups. The middle three are for pretty ordinary line following. If the middle sensor sees the line go straight, if the right sensor sees the line turn right and turn left if the left sensor sees the line. That simple strategy will follow just about any simple line track. However, if we want to compete with this bot we need to be able to deal with more than that. The tough tracks have gaps, switchbacks and 90 degree turns. It is for this we need to use the outermost sensors for a very special job: record if they saw a line recently off to either side.
When the bot comes across the 90 degree turn or switchback it will leave the line. The bot then needs to know what to do. It could stop and search for the line but the bot may end up finding the wrong line depending on the track and how it was programmed. In addition, we need to differentiate between a gap and a sharp turn. If it's a gap we need to keep going. If not, turn in the right direction.
That's where the outermost sensors come in to play. If the bot lost the line because of a sharp turn one of those sensors has recently detected the line off to the side. We'll use that to decide what to do.
My code has two variables, lastleft and lastright. These are 32 bit integers and represent the number of mainline cycles that has gone by since the last time the left or right outer sensor was tickled. Whenever getsensres is called these outer sensors are checked and if they see a line they get zero. If they don't, they are incremented until they reach the timeout limit. When the bot leaves the line these values are checked. If they are below the timeout limit then the bot thinks it has encountered a very sharp turn and swivels on spot in that direction to try and pick up the line. The timeout is very important. The bot must also deal with gaps and the timeout is used to differentiate between them and sharp turns.
The amount of time that tells if if we lost the line because of a gap or switchback is determined by the maxlast array. We compare the lastleft or lastright value against this. If it's less, then turn. If not, it's a gap. The reason this is an array is because we can choose the bot's speed. The faster the bot is going then the shorter we want the timeouts to be. It's an array of two because we have two possible speeds determined by the jumper I described above. The topspeed variable stores this information.
When the bot decides that the reason it has lost the line is because of a switchback or 90 degree turn it will slam on the brakes by going reverse for a short time the length of which is determined, again, by our speed and the stoptime array. It will then turn on the spot in the specified direction until one or more of the three middle sensors see something and return to normal line following.
Because we can't measure the speed of the wheels (yet) these timeouts and whatnot will depend a lot on the charge left in the batteries. I typically pick values that work well when the bot has been recently charged but after running for a few minutes. Nicads and nimh batteries have a very flat discharge curve shortly after they start discharging. This makes them easy to predict.
One of the most challenging things I found I had to deal with into making the sumovore into a killer line-following robot was the distance between the line sensors and the way my code works. Because the sensors are fairly far apart it's possible for them to lose sight of the line even while the bot is over top of it. If that happens and the outer sensors were recently tickled it will think it has lost the line and needs to make a sharp turn. It will then slam on the brakes, turn for a very short distance and pick up the line again. This can manifest itself as jerky movements shorty after the bot moves past a spot where the line crosses over itself. That's why I have the linetimeout variable. If we lose the line for a short period of time then do nothing and hope for the best. It's not infallible, though. A better way to mitigate the effect would be if the sensors were a little closer together. Maybe later I will try some hacks out that we can use to help us out here.
Currently, my sumovore with this version of the code will solve my copy of the WCRG "Integrator" track with (so far) 100% reliability (i.e. no user intervention at all) in 27 seconds. That would gather 311 points assuming no points for the bonus given for a scratchbuilt robot. If it does as well on the other tracks this bot would gather over 1200 points.
This picture is a 30 second exposure of the sumovore solving a line-following track. The pink streaks are the LEDS on the top and the power LED. They look sort of pinkish because of the white balancing my new digital camera was doing. Because I wanted the LED streaks to be as bright as possible I had the apeture wide open and the room dimly illuminated by a little battery powered flourescent light.
Here are a couple more pics of the sumovore on my torture tracks.
Enjoy the code and good luck on your next competition. Come back later. I intend to expand my sumorvore by adding more sensors, a way to get more user input and add an alphanumeric LCD display. Also, It's likely I will modify the code once in a while as I find improvements or decide to incorporate a suggestion from someone who has tried it out. I will keep a version number in the code that you can compare against the one you have. If it's different then it (probably) has gotten better!
Hey, if your sumovore running my program wins a prize email me and let me know! I would love to hear about it!
HERE is the code.
Update: Sun April 10, 2005
The 1st place winner of the line-following competition at the 2004 Eastern Canadian Robot games was Nicholas Barker with "Truffle Pig." His bot's software was based on the above code. Congratulations Nicholas!
Update: Mon May 23, 2005
The 1st place winner of the line-following competition at the 2005 Western Canadian Robot games was, again, Nicholas Barker with "Truffle Pig." He cleaned my bot's clock by a wide margin. Congrats again Nicholas! And, yup, it's time to update my bot.
Update: Thu June 16, 2005
The 1st place winner of the advanced line follower at the Vancouver Robotics Games on saturday June 11 was Clinton Blackmore's "Circuit Breaker;" an unmodified Sumovore with the code from above. Way to go Clinton!
Update: Thu Nov 14, 2007
Truffle Pig took second place at the 2007 Western Canadian Robot games. This bot would have taken first (and pummled my bot) had it not been for a single pick up. However, Truffle Pig came back strong and took first at the CNRG in the Ontario Science Centre. Nicholas tells me that there are other groups and competitors coming forward with advanced line followers.
Last modified: April 11, 2005 - CAN'T.. STOP.. BUILDING.. STUFF!! Ain't it great?