Team Two/Paper
From Maslab 2006
| Maslab teams |
|---|
| Team 1 · 2 · 3 · 4 · 5 · 6 · 7 · 8 · 9 · 10 · 11 · 12 · 13 · 15 |
| Team Two's Journal · Paper |
Contents |
OVERALL STRATEGY
Our team decided at the start of Maslab that our overall strategy for success would be to “keep it simple.” There were two major decisions we needed to make about the robot before we could begin any implementation: “What will our robot’s ball collection mechanism be?” and “How will our robot’s behaviors be organized in software?” We decided that a spinning paddle wheel would be the easiest to implement, as well as the most reliable mechanism for ball collection. Furthermore, we decided that we wanted use an emergent approach in our software since that would allow us to layer many simple behaviors together to create an overall complex behavior. As far as deciding which behaviors would be executed by our robot at any one time, we decided we would designate a priority number to each behavior such that the robot would be able to decide which behavior to execute by choosing the behavior with the highest priority.
MECHANICAL DESIGN AND SENSORS
The central mechanical component of our robot was the ball collection mechanism. All other mechanical design issues centered around this component. We choose the spinning paddle wheel for a number of reasons. We did not want a complicated ball collection mechanism. The paddle wheel promoted simplicity because it only required one motor to implement it. Hence, to change from collecting to expelling balls we simply switched the polarity of the motor. Furthermore, the paddle wheel effectively controlled the number of balls we decided to spit out at any one goal by spitting out one ball per quarter revolution. We also wanted the balls to be collected and expelled in a last in first out manner so that when we got the powerball we would be able to expel it right away. The paddle wheel was situated in the front and center of our robot so that balls straight ahead would naturally be centered on the paddle wheel.
We decided to make our robot relatively circular so that it would have a lesser tendency of getting stuck on the playing field. The camera was mounted above the paddle wheel in the front and the center of the robot so that the a ball in the center of the robot’s vision would be equally centered on the robot’s paddle wheel. The container for the balls was shaped (Figure 1.0) so that gravity would always push the balls against the paddle wheel making the balls ready to be expelled. The entire robot was also titled forward (Figure 1.1) by having the back castor height taller than the wheel axle heights to give the balls a tendency to roll to the front of the robot.
In line with our theme of simplicity, we used very few sensors. We used three short IR sensors, positioned in the front, left, and right, for forward obstacle avoidance. Finally, we used the camera for ball, goal, and barcode detection.
We encountered a few major problems with our mechanical and sensor design. After mock contest two, we discovered that our paddle wheel would periodically get jammed when trying to expel balls. This happened because two balls would sometimes get trapped underneath the paddle. We solved this problem by forging a large metal wedge to put on each paddle (Figure 1.2).
The wedge would scoop one ball into the paddle wheel while also pushing the ball behind it away from the paddle wheel. The wedge solved our two-ball-under-paddle problem. A second problem we encountered was that our camera was mounted much too high above the ground to get a good view of balls within an 18in radius of our robot. We solved this problem somewhat by creating our own camera mount for the camera that put the camera as close to the paddle wheel as possible. The camera was still much higher than we would have liked. For future teams reference, the paddle wheel idea may not be the best simply because it causes the camera to be in such a high position above the ground. Finally, a sensor problem we encountered was that our robot would sometimes get stuck in corners or trapezoids when its left and right sensors were tripped repeatedly. We solved this problem in software by implementing a behavior history list. When the robot’s left IR-sensor was tripped, the robot would check the behavior history list for a right IR-sensor being tripped recently. If the right IR-sensor had been tripped recently the robot would understand it was stuck in a corner and subsequently turn 100 degrees to get out of the situation.
SOFTWARE
As mentioned earlier the overall strategy of our robot was to keep it simple. We didn't worry about the power ball, the power goal, mapping, or any other advanced behaviors. Our team agreed that if we focused on one, very simple task, we could spend our precious time making the code robust enough to handle every variation and situation in which that task could occur.
Put simply, the task we wanted to accomplish was: wander around, if you see a ball, pick it up, if you see a goal, empty the ball hopper into the goal. We knew, based on previous competitions, that if we could score at least one or two goals, our robot would be doing all right.
Implementation
However, having a strategy of how our robot would ideally score goals is not enough. In order to make our code robust, and eliminate our chances of getting stuck, we implemented thread controllers for the IR sensor, as well as a thread controller for the camera.
Threads
Three instances of the IR Controller are instantiated at start up, and each is passed an IR Sensor object corresponding to the physical position of the IR sensor. The IR Controller in essence is a thread which constantly checks the value returned by it's IR Sensor's range function.
When the IR Controller senses an obstacle in the path of the IR Sensor closer than a fixed cut-off distance, it adds an 'Avoid Wall Behavior' to the priority queue, specifying which direction the robot should turn to avoid a collision.
Aside from the IR Controller thread, the Camera Controller thread is also constantly running, checking pictures for objects of interest (balls and goals). Our image reader works by scanning the pixels from left to right, and grouping them into blocks of similar color according to the threshold constants we set. This list of blocks are then passed to a goal reader and a red ball reader. Each reader analyzes the list of blocks and filters out the blocks of interest (usually by color) and checks the list for blocks that meet certain the size requirements. The readers also offer functions which return the area of largest ball (or goal) and coordinates of a certain block.
Behaviors
Once the image reader knows where the balls and goals are, it will queue up the proper behavior. The existing logic, is that if there is at least one ball in the hopper, the 'Score Goal Behavior' is added to the queue, otherwise, the 'Collect Ball Behavior' is added.
The 'Score Goal Behavior' uses a centering algorithm to center on a goal, and then spins the paddle wheel backwards to release the balls. In order to ensure the balls don't get stuck in the paddle wheel, we use a system of spinning the paddle backwards to release a ball, and then spinning it forward to load another ball into the release chamber. Once this process is repeated enough times such that all the balls are released, the ball counter is set back to zero and the robot turns 180 degrees (this is to avoid the problem of spotting a ball in the goal.
The 'Collect Ball Behavior' uses the same centering algorithm to center on a ball. The robot then moves forward while spinning the paddle wheel forward. This technique reliably picked up balls.
Alternatively, if the image reader find nothing, the 'Wander Behavior' is continuously run. This behavior simply tells the robot to move forward. To implement the behavior system in java we used a priority blocking queue.
At the heart of our robot is the Priority Behavior Queuing system. All actions of our robot are governed by the small while loop inside our start function. Throughout the course of 5 minutes we just repeatedly tell the PriorityBlockingQueue to run the top behavior in the queue. The ordering of the behaviors is simple, from highest priority to lowest priority, AvoidWallBehavior, ScoreGoalBehavior, CollectBallBehavior, and WanderBehavior. This simple ordering of very small behaviors is really what drives our emergent robot.
If at any point we have some kind of while loop that might have to be interrupted we have a simple boolean interrupt system set up. So if at any point there has been added a new Behavior to the Queue, the system will stop the present behavior and re-peek the top of the Priority Queue. This ensures that Behaviors with higher precedence like AvoidWallBehavior will get performed even while we were in the middle of centering on a ball. Once a behavior has been performed of course it removes itself from the queue – there is one exception to this, WanderBehavior never gets removed from the queue.
To accomplish this task of having an intelligent Queue that reorders itself we have created an inner class to Robot (which has access to the PriorityQueue itself) which implements a BehaviorQueueable interface and gets passed around to different behaviors so they can manipulate the Queue and add or delete themselves easily. It is this extremely object-oriented technique which allows the PriorityQueue to be so versatile and useful as it can be morphed by the very objects that it holds. The only piece left to this puzzle is how to allow multiple threads to access this queue and not have it miss queued behaviors. Luckily in the java libraries is a version of PriorityQueue called PriorityBlockingQueue that performs all the synchronization automatically: locking out the queue while adding and reordering and similarly while reading from the queue. So there we have it, our own emergent brain and decision maker.
Similar to the BehaviorQueable interface, we wrote another inner class to Robot which implements the RobotControllable interface. This interface allows anything passed a RobotControllable object the ability to control the motors or sensors. This way combined with the Queue, we only ever have one thread accessing the motors and telling them what to do. All actions that are performed by the robot in the physical world are run from RobotControllable methods and are performed by the same thread – the main thread that tells each behavior to perform its runBehavior method.
Adapting to unexpected situations
While testing our robot and putting it through many rigorous trials, we found that the number one concern for game day would be getting stuck, either on a pointed corner that was in our robot's blind spot, or having a problem with the caster getting stuck. These issues were resolved by implementing two counter-stuck strategies. The first we called ?the crazy Ivan? (named after a soviet union submarine maneuver), in the wander behavior a random number generator was called and based on a hard set probability we would possibly add the 'Crazy Ivan Behavior'. This behavior would have the robot slowly spin 180 degrees, and then spin back 180 degrees. As a side effect, this maneuver helped us find balls that were in the middle of the playing field.
To avoid all other getting stuck scenarios, we implemented a hard-coded backup mechanism. Where in every 1 minute increment the robot would move backward for half a second, regardless of what behavior was occurring at the moment.
OVERALL PERFORMANCE
When everything came together for the final competition, even though there was a slight imperfection in aligning with the goal correctly, our robot performed fairly well. It never became stuck, it explored the field well, it collected balls without problems, and it even found and approached a goal. Our robot pretty much did everything it was supposed to do. Even though in the end it only scored two points, we all feel pretty confident in its overall ability. With just a little more time, we could have worked out that small bug in centering on the goals, and then we would have a robot very capable of scoring reliably.
Conclusion/Suggestions
Try to keep your robot lightweight. Of all the robots in the competition, ours seemed like it was one of the heaviest. A heavy robot degrades its ability to start and stop moving precisely. It requires more motor power to get it moving, and it takes longer for it to stop moving. This is especially problematic for turning, when the robot may need to be fairly accurate for centering on objects. When we tried to apply our pegbot code on our final robot for centering on balls, we found that our robot would overshoot terribly. We had to alter our code considerably to take this into account. Of course, every robot will end up being somewhat heavier than the pegbot, but if you can help it, try to keep it light.
Our mechanical design of using the paddle wheel also introduced a complication that we didn't like. It forced us to place the camera quite high, about 8.5 inches above the ground. Because of this, our robot could not see anything right in front of it. Our robot was blind to anything within a foot and a half or so from it. This causes it to miss a good number of balls. We suggest using a design that will allow you to place the camera as low as possible.
Finally, getting stuck is a huge problem. And it's probably the most overlooked. We feel that this problem is what causes most robots to fail on the playing field. We spent a long time tweaking our code to help ensure that our robot would be able to get out of stuck situations, or would not get into those situations in the first place. We did not have enough time to cover all of the situations our robot could get stuck in, unfortunately, and so this was definitely one of the areas we were most worried about with out robot. Make sure your robot has reliable ways of getting unstuck.
Our team had a lot of fun in Maslab this year. Several of us are even thinking about doing it again in the future. It was challenging to say the least, and definitely requires dedication and a serious time commitment. But, creating a working robot in one month is undeniably pretty awesome.
