Team One/Final Paper
From Maslab 2007
Contents |
Overall Strategy
Kilroy was designed to match the task at hand. Nothing complicated, just the following: navigate a complicated field via simple rules, find balls, and put them in yellow goals. Oh, and to be doable in under a month.
Omni-wheels gave unmatched maneuverability. A rubber-band roller effortlessly picked up balls and deposited them into goals. For navigating, we had 13 bump sensors spread around the perimeter to give 360 coverage. If we hit a wall we knew it and could back tangently away. We also had 2 long range IR rangefinders that were placed on a rotating platform (akin to RADAR) which gave 360 degree IR coverage for finding walls. This was crucial to quickly and intelligently navigating the field without doing any actual mapping.
Mechanical
Omni-Wheels
The use of omni-wheels was probably one of the greatest advantages our robot had. It allowed full mobility in any direction, to effortlessly back away from walls, and to maneurver around walls while maintaining the robot's eye on balls or goals.
Our omni-wheel design involves the use of three wheels spaced 120 degrees apart on a circular base. The wheels we used are available at www.omniwheel.com for 4-10 dollars each. To get an idea of how omni-wheels work, go to http://www.youtube.com/watch?v=t1GVBouubww , or simply youtube or google "omni wheel robots."
One way to use omni-wheels is to use two wheels to drive forward and the third wheel to steer. We used this concept to fully utilize the speed of each motor. When we tried to use a PID controller to drive straight using only two wheels, the robot could only drive as fast as its slowest wheel. However, if both forward motors are set to drive forward at full power, while the back wheel steers, the robot can move noticeable faster (while also giving the allusion of extreme intoxication, or giddiness, as it slides around back and forth to hone in on balls).
However, if the team would like to access the more advanced properties of omni-wheels, either math, trial&error, or intuition may be needed. To translate at any angle without rotating, the power given to each motor is a simple set of sine functions, one function per wheel, out of phase by exactly 2/3*Pi (ie, 120 degrees).
What are the downsides to omni-wheels? Whether you build or buy your own, you might have to machine a bracket for it (which is not as hard as it sounds). You also need to have three, equal motors that have enough torque to move the robot (the battery is heavy). It took some trial and error to find three good motors, but it is possible. All the motors we used are stock MASLab high torque motors. Also, if you would want (heaven forbid) to do mapping or odometry, omni-wheels will require a bit of math. That may or may not be an issue, but it was not for us since we did not choose to do any mapping or odometry.
Try it out, omni-wheels are easy! (and are easier for coders to deal with).
Ball Capture Mechanism
A roller is a proven and effective way to capture balls. The inspiration for our roller comes from our experiences in 2.007. Chris used this same design on his 2.007 robot that made it to the top 16 in the competition.
Rubber bands allow the capture of balls with minimum frictional resistance. An earlier design was made with foam and mouse pad that had a lot of grip, but too much resistance. In our design the speed of the roller was key to its functionality. With the low resistance of the rubber bands, there was no need for high torque to capture balls. Our roller operated off a high speed motor with a timing belt drive to minimize slip.
The bin was made of 1/32 sheet aluminum and was designed with a slight downward slant and a central ridge to passively funnel balls to the center of the bin and out of the robot. Since the balls were always theoretically resting against the inside of the roller, a short range IR sensor at that location acted as a break beam to alert Kilroy he had captured at least one ball. We tried to make a break beam out of photo-diodes, but could not get a range of more than 3 inches. There may be a better way to build break-beams without using IRs, but we did not have time to figure it out.
Sweeping Sensor Suite (SSS)
The curious “radar” looking device perched atop Kilroy is what we call a Sweeping Sensor Suite (SSS). It is two long-range IR sensors mounted to a servo motor. By gearing the motor 2:1 we achieve a 360 degree sweep (the out-of-the-box servo only does 180). Other people claim you can hack the servo to make it go 360 without messing with gears, but we never tried it (what is a robot without gears anyways?).
The purpose of the SSS was to achieve with a few sensors what other groups achieved with many: grab information about wall distances at multiple angles around the robot. However, by putting sensors on a rotating platform, the noise is amplified and the data becomes less reliable. Ultimately we only pulled data from the IRs while Kilroy was at a stop (if he bumps a wall in the front he stops and does a scan to decide which way to go).
Having two IRs was for redundancy and data smoothing. We originally wanted to use an ultrasound on the SSS, but did not have enough time to get it working reliably. (Actually we think an ultrasound is ideal for our desired functionality)
Bump Sensors
Kilroy had 360 degree bump sensor coverage. Bump sensor are easy to use and give you very reliable information (ie, “I have been BUMPED”). Other sensors such as IR sensors are susceptible to noise. With the right bump sensor array design, noise and other inaccuracy can be completely eliminated.
Our bump sensor array consists of a total of 13 bump sensors in a ring. Each bump sensor was connected to two other bump sensors via a sheet metal bracket. These brackets were attached to the bump sensor surface using a piece of mousepad witch allowed the brackets more freedom of movement if hit. Two or three bump sensors were wired in parallel to provide information to the orc board through one port. There were a total of 6 bump “regions” which told the robot which direction had been bumped.
If you don’t have experience with mechanical design, Nintendo buttons are much easier to use. Unlike the whisker-type bump sensors which prove unreliable if attached to a mass, the Nintendo buttons are robust and have their own sliding bearings which allow it to support attached masses.
LED Lights & The Breadboard
28 LED lights were added to Kilroy. 4 large LEDs (ala RadioShack) were used as debugging tools to indicate things such as state, posession of a ball, sight of a red object, and sight of a yellow object. The other 24 LEDs were lined around the perimeter and turned on in succession to give the illusion of rotating UFO lights. The LEDs (and the CPU fans) needed 12 volts, which came off the computer's power line. A breadboard was the best way to give power to all the necessary electronics. The LEDs also needed to be controlled through computer logic (ala the orcboard's digital I/O ports). Transistors on the breadboard, with the digital I/O's attached to the gates, allowed us to turn the LEDs on and off at will. While initially done "just to do it" because our computer was dead and we suddenly had a lot of free time, it was one of the best things we could have done. Breadboards, transistors, and LEDs should be one of the first things teams should get their hands on. It will also make you appear incredibly nerdy (I mean, l337), even if your team is a bunch of mechanical engineers.
Software
The software consists of 8 threads. The actual thinking is done by a Stack-variant Finite State machine, in the FSM Arbiter thread.
Main Program Thread
This thread held the pointer to all the other threads, kept track of time, started the other threads when “A” was pressed on the orcpad, and stopped all of the threads when time ended. On the topic of pressing "A", it is hugely important to be able to "turn on" your robot's code and let it "warm up" before actually letting your robot loose on the field. Our frame rates (and SSS rotation speeds) jumped noticiable after about a minute of warm-up time.
FSM Arbiter Thread
This thread is where most of the magic happens. Our robot uses an Stack-variant Finite State Machine; new states are added to the stack, and as states are completed they are removed from the stack. This gives the robot a sense of “memory.” If it is trying to pick up a ball (S_pickUpRed) and hits a wall, it adds S_AvoidWall to the stack. It then executes the AvoidWall state, moves away from the wall, and removes it from the stack. It then continues with the previous state, in this case, S_pickUpRed was the highest state on the stack.
Originally we had split the FSM thread into two separate threads – an Executor thread that executes states and an Arbiter thread that updates states. This would allow us to add more scripted behavior, such as thread sleeps and PID while loops in the execute code. Then if the Arbiter thread updated the state, it would stop the Executor thread and restart it in a new state. Unfortunately, java is stupid and there are no good, sanctioned methods of killing and restarting threads. I had it working, but it made it much harder to debug and understsand why the FSM was behaving the way it was. In the end, I bit the bullet and combined the entire FSM into a single thread. The problem is that any loops, PID controller code loops, and sleeps had to be removed and replaced with clever semaphores (Ugh!).
Camera Thread (& Image Processing)
The camera thread took images, processed them, and continually updated important variables that the Arbiter thread used. Every ten frames were also post-processed and sent to the botclient for output. Unfortunately, the botclient will drop packets if you try and update the images too often.
The first thing image processing does is convert to HSV (thanks ImageUtils). A scan thru the entire image is then performed. Three things are done: any Red is put into a Boolean Matrix (true for Red, false for not red), yellow is put into a separate Boolean matrix, and the blue line is found by taking all wall-blue pixels and finding the average y-value for each column. If a threshold is not met for each column, the average “average-y value” is assigned to fill in the holes. The final average-y values are then smoothed using a running average algorithm.
After this initial run, the image routine then searches for yellow objects by searcing the yellow Boolean matrix. It finds multiple objects by drawing vertical lines at the minX and maxX for each object. It then does a second run by drawing horizontal lines. This routine will work for finding multiple red balls in an object, but if two red balls overlap, or if two red balls overlap vertically (like one ball hovers above the other) the algorithm will classify them as a single object. This is actually desirable!
After finding all yellow objects, the image processing then eliminates all red pixels that exist inside the “bound box” of all yellow objects. This is to prevent the robot from chasing balls it has already scored. This is also a serious problem if the robot is SO close to a goal that it can only see the top cross bar and not the sides.
Red objects are then found the same way yellow objects are.
Red and yellow objects are held in V_Object arrays which is then passed back to the CameraThread. The only thing we used was the nearest object of each color. We realized early on we could not trust the robot to hone in and pick up balls by using the camera (frame rate early on was too slow). Instead, the nearest objects’ positions were turned into gyro angles. Therefore, the robot did not see and drive up to balls: instead, the camera talked to the gyro which talked to the robot, which gave the illusion of seeing balls and driving towards them.
Originally, I used a recursive "paint-fill" algorithm to find and parse out all of the objects on the screen. That means that any objects that were not touching were treated as seperate objects (ie, each patch of red was a different red object). While this sounds great, there were two flaws. 1: recursion of a single large object up close (like a goal) would cause a stack overflow (10,000+ recursion calls on the stack!), and 2: when looking at a single ball, you might think it is really three or four smaller red objects. Or worse, you may see a goal as being made up of smaller patches of yellow objects, which makes it MUCH harder to parse out balls you have already scored! (scroll down to see an example image of a ball in which the lighting has screwed the ability to see it as completely red). Take this as an example that easier can in fact be better!
Sensor Thread
Self-explanatory, but there is where the bump sensors queried themselves and set public variables as to whether they had been triggered or not.
SSS Thread
This thread directly controlled the servo's motions and recorded the values of both IRs. After each data capture, the thread then printed the new values graphically to the botClient. The SSS thread had many accessor methods which allowed the FSM Arbiter to grab the processed IR data and make decisions based on the data.
UFO Lights
24 LEDs were fixed around the perimeter of the robot. To turn them on and off in sequence to give the illusion of rotating lights (like a UFO) a clock signal needed to be given to them. This is what this thread was for. Simple really. The thread sleep time changed depending on how close the nearest red ball was (so the lights sped up slightly as it came closer to a ball). This clock signal was also sent to the big LED on top of the rotating IRs, resembling the appereance of the "engine on" red lights that flash on the underside of aircraft.
Safety Thread
This thread oversaw all the other threads, made sure none had shut down or prematurely stopped, verified that the Arbiter was not stuck in any given state for too long, and checked to see if the Arbiter was oscillating back into a given state too often. While all of this functionality was coded, it was not tested enough for us to feel comfortable with it, so we only allowed the Safety thread to have the power to shut down states that the robot appeared to be stuck executing. Time-out functions such as this come highly recommended!
Communicator Thread & Debugging
The botclient has a field in which the user can send strings to the robot (in hindsight, I should have written the code to talk through the command line instead, so I could drive the robot W,A,S,D style, but oh well). Talking to the robot was enormously helpful in debugging. Tweaking PID contants? Why change, compile, send, and start the robot when you can just send the changes through the Communicator and change them on the fly? Cannot figure out which motor is attached where, or whether it is inverted or not? Turn on the motor thru the Communicator.
The botclient is also good tool (though I wish it could handle more information before dropping packets). The SSS thread prints in radar scope format what the IRs see as they sweep around the robot. This allowed us to figure out exactly how the IR sees objects and to understand how it makes its decisions. Below one can see how the left and right IRs view two wall segments coming together.
Overall Performance
Excellent planning, good design practices, and hundreds of hours in lab culminated in a robot that performed beyond our wildest expectations. Everything we set out to do with Kilroy, both mechanically and code-wise, we accomplished. There was not a thing we designed or wanted to implement that was cut from the final design.
We played to our strengths and invested time into mechanical features that would simplify code. We realized we could not possibly solve huge problems such as odometry with omni-wheels, or mapping, so we kept things simple and did not bite off more than we could chew. We had a fairly detailed schedule and designed the robot to be built in under two weeks. Aside from a few set-backs, we keep to schedule and delivered the features on time.
In the competition we tied Yellow Hats for first place with 4 goal scores, and 3 ball posessions.
Conclusions/Suggestions
Start early. No seriously. Do not even wait to be emailed the tutorials, look up the previous years and start doing them. Start IAP with your image processing routines completely finished (remember to take care of balls in goals). Also have your robot design mostly finished. Have a schedule prepared and stick to it. Give yourself a week to debug and a week for everything to fail before the week to debug.
Invest some time (and man-power) into the mechanical side. Make a strong push to finish the robot ASAP (try to mostly finish it in the first week). It is hard to program when you do not have a robot, so make some of the programmers build too. Do not be afraid to make something a little more complicated mechanically if it will make things easier to code! IE, bump sensors, omni-wheels, etc….
Modularity is key! Design it to be easy to take apart and easy to tack on new functionality. Both our roller and roller drive motor were mounted to the top level of Kilroy so the top could be taken off completely without having to realign the belt.
Bump sensors are GOD. Seriously, use them. They are simple to implement and give very reliable information. Even if you choose to rely on IR or Ultrasound, try to use bump sensors as a back-up in case one of the other sensors is noisy or unreliable.
1/32 sheet aluminum is awesome and easy to use (bend with your hands and cut with scissors)! Go the Central Machine Shop in the sub-basement of building 34 (6.001/6.002 lab building) for any and all building materials you could want. Often they give you scraps and don’t charge you for them!
Use Omni-wheels! They are easy and give your robot much more freedom to move around in small spaces as well as keeping the camera’s focus on the ball or the goal (they are easy to code for, no matter what anyone says).
LEDs are awesome. Not just for kicks, but as a debugging tool, they are unmatched. The botclient is slow, drops packets, and can be difficult to decipher if you have a lot of debugging info.
Having a good team is aboslutely key. Pick a team of individuals that posses a wide range of skills and specialties.
Good debugging tools is key to success in any software project. Invest time, lots of time, in writing good debugging tools. We are very glad we did. As mentioned above, the Communicator was the best debugging tool we could have invested time in.
Any further questions feel free to email us!
