Team One/Paper
From Maslab 2006
| Maslab teams |
|---|
| Team 1 · 2 · 3 · 4 · 5 · 6 · 7 · 8 · 9 · 10 · 11 · 12 · 13 · 15 |
| Team One's Journal · Paper |
We had three guiding principles for our robot design: it should be simple, it should be stupid, and it should be reliable. Also, since our team was much stronger on coding than mechanical engineering, we decided always to opt for complexity in software over complexity in hardware. For the most part, we succeeded in these principles, although some functions, particularly scoring, were not quite as reliable as we would have liked (although they were generally still pretty simple and stupid).
Contents |
Mechanical design
The Shaft
The scoring rules for this year made it advantageous to be able to deposit a specific number of balls into each mousehole, instead of just dumping all of the balls into one goal. In order to do this in the simplest way possible, we built a central shaft that could hold a line of four or five balls, with a motorized roller above the opening to pull balls into the shaft and push them back out again, one at a time. We meant to then have a sensor to detect how many balls went in or out, although in the end we were unable to make it reliable enough, and so we weren't able to control how many balls we deposited after all.
Despite how simple our design was, it was still very problematic to get working. We went through two or three different motors and about six different mountings before we settled on something, and even then we had trouble pushing balls out far enough to get them into mouseholes. High speed motors could shoot the balls several feet, but were prone to jamming; high torque motors didn't jam, but couldn't shoot the balls very far. Had we had another week or two, we might have made a seventh revision with yet another motor, but we didn't. Still, the roller was quite good at picking up balls by the end, and as our mousehole docking code was more or less nonexistent, any problems we might have had shooting balls out were more or less irrelevant.
Sensors
We used two IR range sensors for wall-following, and to avoid colliding with walls. We were very deliberate about the angles in which the IR sensors pointed; we found that having a sensor pointing exactly perpendicular to the motion of the robot made it hard to tell, when turning, whether the robot was turning towards or away from a wall. Instead, we placed the sensors at angles to avoid this problem. One thing that helped our wall-following a lot was to use proportional control to set the motor speeds, so the the robot arced around corners instead of abruptly stopping and turning.
An important problem for future teams to avoid when writing wall-following code is to make sure that the robot doesn't go in circles when it finds itself in a large open space (this happened to our robot in the final contest; fortunately, our driving was sufficiently inaccurate that it eventually spiraled into another wall and got back on course).
We used the gyro to drive straight, particularly when catching balls — the mounting of the camera meant that we had a large blind spot right in front of the roller, so whenever we picked up a ball there was a period of time during which we could not see it. We used a PI-controller to keep the robot on track.
Finally, we used the shaft encoders to determine if the wheels were stalled; we found that they were more reliable than current sensing. Unfortunately, we ended up mounting the encoders a little too close to the wheels, and the LEDs on the encoders kept getting caught on the wheels and snapping off.
IR Beacon
This deserves mention as one of the things that we didn't quite finish. Leon, our resident EE-ninja, designed an IR transmitter that could be dropped off by the robot at the start of the round. This beacon would be above the height of the walls, so that from any part of the maze, the robot could turn to face the beacon, thus determining its angular position. Although this would have greatly helped our mapping, and we already had a working prototype, we scrapped the idea in the last few days for lack of time (a day or two later, we scrapped mapping as well, so it ended up not mattering much).
Software design
The Visionary
The visionary was the part of the software responsible for visual object recognition. It runs on its own thread, continually capturing a camera image, locating features in the image, and report the features to the navigator. We used a 64×64×64 lookup table to classify colors into different categories (wall, blue line, red ball, green, etc). When processing images, the visionary makes a first pass over the image, and stores information about where various colors appear. It then goes over the various data structures it built during this preprocessing step, looking for patterns that might represent various objects. Sadly, because we were pressed for time, and had been convinced that fast vision code was really important (we're pretty sure, now, that it isn't), the vision code was rather hacked together, and quite buggy. In fact, we never really got all of the errors out of the mousehole finding code — it routinely threw ArrayOutOfBoundsExceptions (which we simply caught and ignored), and altogether didn't work too well at all. The red ball finding and barcode reading was fairly robust, at least.
The Driver
The driver consisted of a queue of tasks which it completed one by one. Each task controlled motor movements in a different way; there were tasks for driving straight, task for turning, tasks for grabbing balls, etc. The driver ran on its own thread, completing the tasks one by one, and prompted the navigator for further instructions whenever the queue was empty.
The Navigator
The navigator was responsible for overall planning; it received reports from the visionary, and assigned tasks to the driver. It also acted in part like a finite state machine — at any point, the navigator was in one of several different modes, where the current mode determined how the robot reacted to a given situation, and what modes it might switch to later on. For example, the wandering mode would queue tasks for wall-following, and would switch to the ball-acquiring mode when a ball came in sight. This double-layered system of navigator modes and driver tasks worked well as an abstraction, but was difficult to debug. We would have been better off if we had thought to use the BotClient to keep track of what the robot was doing at any given time, but we didn't do that for a long time. Silly us.
Mapping
We didn't have any mapping on contest day, but we tried to implement a topological mapping scheme. We didn't use it because large parts of it were untested, and the parts that were tested had bugs. These were mostly related to driving code for building the topological map.
To future teams, we suggest not doing anything related to mapping until wandering reliably does not get stuck. We actually followed this advice ourselves; this was fortunate, since we had to fall back on simple wandering to get around. Since this already worked well, we were in good shape for contest day.
As previously stated, the map we tried to build was topological. Since barcodes were all connected by line of sight, we decided to make those the nodes in an undirected graph of the playing field (really a digraph, but when we added an edge one way, we added it the other way).
The robot kept track of the barcodes it hadn't seen before, and when it saw a new one, it drove up to it and spun around, making links from that barcode to any others that it saw. We assigned weight one to each edge and then ran shortest paths to plan routes. This is the simplest map you can possibly have, which in our estimation is a feature.
It's pretty easy to extend, too. If you record what you think a barcode's absolute position is the first time you see it, you can use that information to correct odometry any subsequent times it comes into view. We saw an interesting paper on correcting odometry when closing loops here:
http://robots.stanford.edu/papers/thrun.map3d.html
The paper is total overkill (an omnidirectional laser scanner is way too expensive for Maslab, to start with), but we found their basic representation really helpful. We did have some very buggy code for aligning successive scans via gradient descent, but that was not even close to functional. We wish we had had more time to spend on this, as it was a fascinating and yet practical idea.
Conclusion
Strengths
- Effective wall-following — our robot was particularly good at wandering around without crashing into walls very much.
- Reliable ball-catching — using a roller to pull balls into the shaft made grabbing balls very robust, and we didn't have the problem of balls falling out of storage before we wanted to deposit them.
- Good error recovery — since it would have been very hard to make the robot avoid ever getting stuck on walls, we focused on making it very good at getting unstuck. In fact, the robot actually made mistakes far more often than it looked like, because it was usually very quick to correct itself.
Weaknesses
- Terrible goal-scoring — due mostly to lack of time, our robot was very poor at putting balls in mouseholes, partly because the vision code was bad at detecting them, and partly because even when it could, our docking code was completely unreliable.
- Defective vision code — because we tried to hard to optimize our vision code for speed, it ended up being completely incomprehensible, and full of bugs.
- Lack of planning or mapping — sadly, we didn't finish mapping in time, and thus our robot had no overall planning at all. At best, it wouldn't pick up balls when its shaft was full, but in practice, it usually didn't even know how many balls it was carrying.
Results
Thanks to our reliable wander and ball-gobbling, we scored five possession points on contest day. Thanks to our terrible goal-scoring, not a single one of those balls made it into a mousehole. Still, five points was good enough to tie for second place. Although we were very sad that our mapping didn't work out in time, we were glad that the parts we did finish worked fairly well.
Suggestions to teams for next year
We did some things well, and some things badly — if you're thinking about taking Maslab next year, learn from our successes and our mistakes.
- Although Maslab has no prerequisites, you should make sure your team has plenty of programming experience, and practice your Java beforehand. Also, if no one on your team has ever been in an autonomous robotics competition, then take 6.270 this year and let Maslab wait for next year. Autonomous robots are really hard to make, and a month is just not enough time to make all the mistakes that you need to have already made before taking Maslab.
- Unless you're course 2, or have done this sort of thing before, then building anything mechanical is probably way harder than you think it is. In particular, mechanical things a lot harder to adjust and debug than code is. Make your robot as mechanically simple as you possibly can. Also, skip the laser-cut polycarb and build your robot out of wood. Wood is way easier to work with and to make changes to later on, and it doesn't matter how sexy your robot looks if it doesn't work.
- Make sure that your team has good communication. Our team had meetings every night, where we discussed what we did that day and what we wanted to accomplish tomorrow. It was immensely helpful to always know what other people were working on and what needed to be done next.
- Also, communicate with other teams. If you think you have a brilliant idea, don't keep it a secret. MIT is full of smart people, and there's a good chance that someone else already had that same idea, or that someone else will notice a problem that you won't. It is highly unlikely that you will put yourself at any disadvantage by sharing your ideas, and it will almost certainly help you to get feedback from other people.
- When executing your design, don't focus on succeeding; focus on not failing. This might sound like a negative attitude. But in reality, the winning robot won't be the one that does everything perfectly, because none of the robots will be able to do much of anything, perfectly or otherwise. The winning robot will be the one that avoids more of the pitfalls than the losing robots.
- In the same vein, don't try to make your robot avoid ever screwing up, because that's just not possible. Instead, make it reliably able to detect and recover from its mistakes. Specifically, make sure it can always get unstuck from a wall, because no matter how awesome your mapping code is, if your robot gets permanently stuck on a wall on contest day, it won't be able to do anything, and you will be very sad. This always happens, and it's not fun to watch. If your robot gets stuck while testing and can't get out, resist the urge to help it, and don't move on to working on other things until the issue is resolved.
- Don't get too attached to the first ideas you come up with, because none of them will work. Be prepared to throw away your grand vision in favor of something modest that works reliably. Don't let yourself think that a modest-but-reliable design will lose because other robots will have awesome designs — actually, a modest-but-reliable design will win because other robots won't work.
- Everything takes longer than you think it will, even if you take that into account. This includes writing code, debugging code, building mechanical parts, debugging mechanical parts, and anything else that your think will take "only a day" or "only a few hours" or "two weeks".
- Take all of your grandiose plans for amazing metric mapping, perfect navigation and odometry, high-precision visual recognition using edge-detection, and whatever else; write them down on a piece of paper, crumple it up, and burn it. Don't even think about anything remotely fancy until your robot can do simple, stupid wall-falling using IR sensors without getting permanently stuck on walls. We cannot stress this enough. Even if you do eventually get intelligent mapping and exploring working, you will still need wandering for when all else fails (and it will), and if you don't, then wandering will be all you have. Count yourself lucky — some teams probably won't even have that.
- Before you write any major code, set up a good debugging system — make it easy to turn different categories of debug messages on and off so that you don't spend too much time sifting through printlines. Also, use the BotClient starting from day one, and have it show a heads-up-display of your robot's state: what the camera sees, where it thinks balls/mouseholes/barcodes are, what its sensor readings are, how many balls it thinks it's carrying, what mode it's in if you have an FSM-like design, etc. It's a pain at first, but it will save you countless hours of debugging. Out of all of our hindsight, this is the thing that we most wish we had done.
- Don't worry about optimizing your code for speed, not even your vision code. Really. You probably don't believe it, but your framerate is pretty much irrelevant, especially if your robot keeps getting stuck on walls because you spent your time optimizing your code for speed (or worse, trying to debug speed-optimized code, which is generally a very painful thing) instead of making your driving robust. Even if you only get a few frames per second, your robot will be able to just fine if your code is reliable. If your team is stronger with, say, Python than with Java, writing your code in Jython (as Team Thirteen did this year) is actually probably a really good idea, because ease of debugging is way more important than speed.
- Many of the other final papers from this year contain suggestions for future teams; read through all of them so as to get as many different perspectives as possible.
