Team Three/Final Paper
We began thinking about our mechanical design during winter break. Something we did well was thorough research of past years’ work, poring through the past wikis and watching several of the past years’ videos. This gave us a pretty good idea of what worked and what didn’t work, which helped us cut down a lot of time in the design process as we had less than four weeks to get a final product.
The basic robot design that we decided upon was a circular drive base rotating at the center to minimize the chance of collisions with walls, a rubber band intake roller to gather balls and deposit them into a ramp, an Archimedes screw to transport balls from the ramp to the hopper/tub, and a hopper/tub to sort the balls and score them.
We split our robot into three distinct layers, each with its own purpose. The bottom layer took in the balls, the second layer housed our electronics, and our third layer sorted, stored, and scored the balls.
The main feature of our bottom layer was our ball intake mechanism - a rubber band roller. We decided to use this because it seemed to have worked well in the past and was a simple enough concept. However, there were still quite a few things to think about and test - i.e. the height of the roller shaft off the ground, the size of the rollers, the number of rubber bands, etc.
Since the ball size changed pretty dramatically from last year (2.5” to 1.75”?), we just guessed at a height and then laser-cut several hole heights to see which fit best. When in doubt, try things out! (And even if you’re pretty confident, always prototype beforehand.)
We prototyped our rubber band roller pretty early on (the first week), but what gave us the most trouble was the slant in the back that bridged the gap between the roller and the bottom of the screw. Oftentimes, the ball would get stuck in that area between two walls or just not enter the screw exactly where we wanted it to. Eventually, we happened upon a configuration of a kind of v-shaped slant that brought the ball to the screw pretty nicely. We used “magic metal,” or bendable aluminum that stayed its shape very well, to make the slant.
In addition to the support walls connecting the first layer to the second layer, we had two wheels on the left and right, and a ball caster in the back. We added two extremely low friction sliders made of UHMV in the front on the bottom of the base to help keep the robot stable in case it ever tilted forward as it was moving.
We made the decision not to put any electronics on the bottom layer because it made reaching anything very difficult.
We used angle brackets to keep walls perpendicular to the horizontal layers. To fit pieces together, we used t-nutting and slots. The tolerance for 6mm MDF for the IDC laser cutter turned out to be pretty good, so we didn’t need to change any dimensions in our CAD to account for uneven cutting.
The drive system also housed on the bottom layer. We used the provided Pololu motors (http://www.pololu.com/product/1443/) to direct drive the provided Banebots wheels (http://banebots.com/pc/WHB-WS-398/T80P-395BA-HS4). This was plenty of power to drive our heavy robot. We were doubtful that this would work, but we tested this by putting over 20 lbs. of weights on the drive base and saw that the motors did have enough torque to move 20+ lbs. with ease. Direct drive if possible, as it eliminates unnecessary complexity.
This mechanism brought us a lot of headaches, not just because of the length of time it took to make it, but also the amount of times we had to keep tweaking things. It was hard to find the right distance between each bend, the correct radius, height of the screw, etc. In the end, the only thing we can say is test, test, and test again!
We prototyped with a small screw first (about half the height of our final one) before moving on to the larger one. In total, we made about four iterations of screws. By the end, we could make a screw in about 3 hours. We bent wire around a PVC pipe, then cut up wire pieces to provide further support for the screw.
You SHOULD support the screw - otherwise, when it gets jammed, the screw could get bent out of shape quite a bit. We had a central rod going through the whole screw, and attached the screw to the rod by drilling two holes into the rod. To keep the screw in place and attach the support pieces, we used a strong adhesive called JB Weld (the quick-drying one). It may have smelled bad, but it got the job done.
Furthermore, we added a bushing to the bottom of the screw to help it turn in place with minimal friction in the bottom layer better. In terms of the support pieces of MDF we used, we had two side walls, two wooden rods, a ledge that would let the balls fall out, and a mini-wall in between the wooden rods that would let the ball climb up the screw. The correct distances between everything was worked out through the earlier prototypes. Finally, we placed the motor at the top of the screw and connected the motor shaft to the screw with a connector we made. Using a lathe, we drilled into the quarter-inch thick rod to make it hollow and then added holes on the side, tapped them, and used them for four set screws.
As the layer whose only job was to house the electronics, the second layer was fairly simple. It had lots of holes to screw the electronics down, and just as many slot holes to connect to the first layer. In addition, there were large holes (such as behind the ultrasonics) to help stuff wires through.
Think carefully about where you want to place things - i.e. we placed the switch in the back for ease of access during the competition. For the same reason, we put the tablet in the back so that we could easily put it in or take it out to change files. We originally had the tablet standing straight up, but we then moved it closer to the middle to move our robot’s center of gravity forward. Because our top layer was slanted, that meant that the tablet wouldn’t fit unless we slanted it.
The camera went in the front and we placed ultrasonics around the camera at 15 degree angles. For us, the placement of electronics changed many times, so save copies of the correct hole dimensions and distances - construction lines in SolidWorks are your friends. If things start to get too messy, just save newer versions. We used Dropbox to share files, so there would occasionally be conflicted files which can be quite annoying to sort out. We started to announce to the team beforehand if any of us were going to edit a certain file, which worked out pretty well.
When possible, consolidate your electronics - i.e. we stacked the Cytron motor controllers using spacers and put a relay on a screw side wall. Otherwise, space on the layers can get pretty tight. The solution isn’t necessarily just to make larger layers, because that could make it harder for the robot to maneuver around.
The top layer featured a mount for the color sensor (later turned into an IR sensor), a ball sorter, two ways for the balls to be dispensed (one at a time vs. all at once), and a high and low-scoring ramp.
The whole layer was tilted at an angle (around 5 degrees or so) to let the balls roll towards the front thanks to the magical ability of gravity.
We originally bought our own color sensor to distinguish between red and green balls, but didn’t use it because we didn’t have the firmware for it. In hindsight, we should have just written our own firmware. Next, we planned to use a webcam, but realized very late there wasn’t enough bandwidth on one USB root hub (ahhh).
In the end, we didn’t have enough time to order the color sensor that the staff already wrote firmware for, so we just used an IR sensor that could detect when a ball had reached the top and come off the screw. However, we couldn’t get color sorting done in time on the software side, which led to us scoring a red ball in a reactor during the final competition by accident. LEARN FROM OUR MISTAKES.
The various iterations of sensor mounts we made were made of clear acrylic so that the sensors could have a clear view of the whole ball. Once the sensor determined that there was a ball there, our sorter would move either left or right depending on the ball color to let the ball through. The left side had a Pacman-like shape to dispense green balls one at a time, while the right side was just a rod to let as many red balls through at one time as we liked. The Pacman and rod were connected to servos.
To prevent the balls from falling out the top layer, we put up a wire fence. Basically, we stuck screws upside-down and wrapped thin wire around the body of the screw. It was sturdy enough to hold and didn’t take much time to make. In addition, it was easily adjustable if we wanted to move the heights up or down, or bend a part of the fence a certain way. This came in handy because lo and behold, our ball got stuck several times at the top when we tested it. Eventually, we got it to the point where the ball flowed smoothly from the sorter down to the Pacman structure.
Our high-scoring ramp was connected to our third, and last, servo. We initially had problems with the ball not rolling onto the ramp 100% of the time, so we put in guide walls and fine-tuned the length based off of many tests. If it was too short, the ball still wouldn’t roll on the ramp every time, and if it was too long, the ball would get stuck.
We originally had planned to just use one ramp to score both high and low (just move the robot back to score low), but the height of the high ramp proved to be too high. Balls coming off of it would bounce if they hit the ground, meaning that our shots would be unreliable.
Hence came the idea of a lower ramp in between the second and third layers. Because most of our robot was already CADed by that point, we had to work with what we had. We were toying with the idea of making another ramp out of MDF, but decided it’d be too complicated. Thankfully, we chose to make our lower ramp out of the magic metal and bend it into the shape we wanted. (When you have a choice between an easier and a harder solution, always take the easier one.)
We took several measurements to determine where to best attach it to our robot and how exactly it should be bent. As with everything that month, it took a lot longer than expected. Essentially, the ramp looked like a kind of funnel. We drilled holes in the metal after it was bent and used screws to attach the ramp to the Archimedes screw side walls. There were three main constraining factors with the placement - it had to end above the camera on the second layer (for obvious reasons), begin below the high-scoring ramp on the third layer, and still have enough of an angle for the ball to be able to roll down smoothly into the lower port of the reactor.
We initially had planned to implement a more complicated strategy but, in the end, our strategy ended up being pretty simple. Our robot followed walls until it saw a green ball in the first two minutes of the game at which point it would try to collect it. If it saw a reactor starting at 15 seconds into the game, it would try to score on it. It would continue to try to score on reactors within the first two minutes with 15 second breaks between try to score to ensure it did not get stuck trying to target reactors and had time to wall follow and collect balls.
In the last minute, the robot did not try to score on reactors and tried to collect red balls. The primary reason we had to change our strategy from what we had originally planned was that we could not in the end get internal ball sorting by color to work reliably and had to divide our time in the game according to what balls we were trying to collect at the time.
The programming for our robot was all carried out in Java and was divided into three main categories: vision, control and other useful classes. Our interface for our vision code consisted of a class Vision which initialized a video feed from our webcam and included public functions that updated the important vision values stored in fields of the class and functions that returned these values. The core of the vision code was a group of filters written in frag files which, when used with the library ShaderCL, filtered the webcam input using our tablet’s GPU. This turned out to be much faster than using OpenCV. It allowed us to get an average frame rate of around 15 fps. We started off with OpenCV and just applying a simple blur filter and some thresholding puts us at around 20 fps so if we were to do our entire vision in OpenCV we would’ve gotten frame rates of probably 5 fps. Our control code consisted of several classes which implemented different strategies and initial code used to test individual functions such as wall following, ball collecting and reactor scoring. The remainder of our code consisted of other useful classes including a PID class and a class Hopper designed to control ball movement within our robot. Below are the names and descriptions of the important classes in the final version of our code.
Vision.java This class was instantiated with a camera number and the width and height of the camera output. In our final code, we created an instance of vision and updated it in a while loop using a function update() approximately 15 times per second. We stored key values in an array of doubles which was then used by a separate thread in which we ran our control code to determine motor values and state transitions.
blur.frag Applies a gaussian blur filter when used in combination with ShaderCL.
modColorize.frag Looks at each pixel and classify the colour as either yellow, green, red, purple, white or blue when used in combination with ShaderCL.
eliminateTop.frag Eliminates pixels detected above the strips of blue, teal and yellow lining the tops of the walls in order to ensure only balls on the field are targeted.
eliminateBottom.frag Eliminates the pixels a certain below the teal at a distance from the teal in a certain proportion to the height of the teal to ensure balls in the low ports of the reactors were not targeted instead of balls on the field.
modObjRec.frag Computed key values from a colorized image in a form enabling them to be recovered from the image returned by the GPU. For example, a pixel that was detected as the center of a red ball had a scaled version of its radius returned in the red of its RGB values. Similarly the height of vertical line of blue pixels was returned in their blue values. These heights/radii were used to calculate distances to objects in the field.
ReactorBall.java This class formed the basis of our final code. The main method created and updated an instance of Vision as indicated above in its main thread. It created an instance of ReactorBall in a separate thread in which its method loop() was run. The method loop() used a while loop to update the robot’s sensor data, target ball or reactor, state and motor inputs once every 30 milliseconds. The constructor of ReactorBall took in an array of doubles which contained the important vision values continuously being updated in the main thread.
Ball.java This class was identical in structure to ReactorBall except it did not have reactor targeting implemented and only navigated the field and collected balls. This class was intended to be a backup for ReactorBall in case our reactor alignment code consistently was hurting our performance - we had only a few hours to test our reactor alignment code before the final competition.
ReactorAlignControl.java This class consisted of the logic in ReactorBall including states and motor inputs to align and score on a reactor only. Its purpose was to test and refine reactor alignment by itself.
RobotControlWallFollow.java This class consisted of the logic in ReactorBall and Ball including states and motor inputs to follow walls. It omitted several states used in our final code which we only realized were necessary when we combined wall following with ball collection and reactor alignment and tested the system as a whole.
TestBallCollectAltered.java This class consisted of the logic in ReactorBall and Ball including states and motor inputs to collect balls. This code seemed to be fairly reliable when combined with the other tasks and was mostly unchanged in ReactorBall and Ball.
PID.java This class was a fairly standard implementation of a PID. The primary added feature was the the integral value was capped at a maximum value and a minimum value (with respect to the proportional value) set in the class to prevent the integral value from growing too large and dominating the output of the PID.
Hopper.java This class separately managed individual functions of servos that managed the flow of collected balls within our robot. Hopper was particularly useful in scoring when our scoring routine after alignment was coded as several calls to functions in Hopper.
Advice for Future Teams
• Before you decide to become a team, discuss with each other what your level of commitment is. Make sure that everyone is on the same page in terms of how dedicated your team will be or else you will end up annoyed at each other. In the words of the director Ariel Wexler, you will live, eat, breathe, and sleep MASLAB. If you want to do well, be prepared to go all in. We made the mistake of having too many other commitments. Three of us (Kath, Melody, and Vincent) did UPOP which was a horrible, horrible, did we say horrible? mistake. It took most of the day and just drained us of all our energy and really threw a kink in our mechanical system’s progress. Although we tried to offset this by starting the mechanical design over winter break, it still wasn’t enough. Angel signed up for the 18.05 ASE which took a bit of time, and Matthew had a UROP and got pink eye which took him out for a week. If we were to do this again, we would definitely keep our IAP completely free except for MASLAB.
• Once you have your team, start before IAP! The four weeks or so that you have during IAP is not enough time to get everything together. We felt that if we had a week (or even a day!) or so more, our robot could function so much better. We started the mechanical design during winter break and this really helped us get things going once IAP began. One good thing to do is to watch videos of past competitions and read through the wikis of past years. There is a lot of good advice that will keep you from wasting a lot of time.
• Make sure that at least two of your team members are skilled with SolidWorks. If this is not the case, learn SolidWorks over winter break. Lynda is a great and free resource for MIT students.
• Prototype. Prototype. Prototype. Prototype early, prototype often. This way you can quickly determine if a mechanism will work before you finalize your mechanical design. This can be done with cardboard, duct tape, zip ties, and a power drill (for the motor). Make sure to determine what dimensions everything needs to be. Once you finalize your systems, finalize it all in CAD. This will help you visualize where everything goes and makes it easy to lasercut all your parts super fast.
• KISS - Keep It Super Simple (or Keep It Simple, Stupid). With respect to the mechanical design, minimize the number of fasteners. T-nuts and slots are your friend. Charles Guan’s Instructable, How to Build Everything Really Really Fast has a lot of good tips on this and other good design principles. http://www.instructables.com/id/How-to-Build-your-Everything-Really-Really-Fast/
• Make sure that your mechanical design is easy to assemble, disassemble, and replace. In other words, make it modular and easy to work with. One more very important thing to check is that all hardware fits, a mistake many a team (and ours) made.
• The same principle applies to your electrical system. Design mounting holes in your CAD. Determine where you will route wires. Twist your wires to keep things neat and minimize EMI (yes, we had this problem). Use filter capacitors to clean your power rails. Make and label connectors so that you can easily disconnect and reconnect components without having to search through your code to know what plugs into what. (Male and female headers are a cheap and easy way to do this). Neat wiring takes a little more time at first, but will save a lot of headaches when debugging.
• Use MDF. Yes, it is not as pretty as acrylic. But it is super cheap (see Home Depot, buy a ton at the beginning if it is not provided) and easy to work with. You can laser cut it super fast and drill into it as needed.
• Loctite ALL set screws and important fasteners. This prevents them from rattling out of place and they are still possible to remove if necessary.
• Test EVERYTHING. We battled issues with hardware all through the month, which wasted TONS of time.
• Read all the documentation for all electronics THOROUGHLY. You will discover all kinds of interesting information, like how the ultrasonic sensors should be connected to the power after the ground (we have no idea why…) and what the interrupt lines are for the Maple.
• Learn how to to use a multimeter, especially the DC voltage and continuity test functions. The continuity test is especially useful for checking that all connections are as they should be before you even power anything on. This saves you the headache of tracking down and replacing components if there is a short anywhere.
• We had a lot of problems with sensors - in particular the ultrasonic sensors. We initially tried to do everything except for ball targeting and reactor targeting using only the ultrasonics. By the end, we ended up backing up the sonars with distances calculated using the camera. If we were to do MASLAB again, we would not have relied on the sonars and instead started by trying to only use our camera to determine distances.
• We also had somewhat noisy distance readings from our camera since we based them off heights of blue in specific vertical columns of pixels. It would have been much better to have taken the average distance over a range of columns to determine distance from walls.
• We ran into a lot of problems with the color sensor and using multiple cameras in different threads. If you plan on doing anything like this, we would recommend trying these ideas out early on to make sure they actually work and coming up with alternatives if they do not.
• Organized code is essential - we had somewhat messy state transitions which made it very hard to debug. Also, randomized timeouts ensure that your robot never gets stuck. We applied these when we were stuck in a state for too long or when our encoders indicated we had moved very little over a recent time period. THIS SAVED US IN THE FINAL COMPETITION (and the lack of timeouts was the doom of many other teams).
• Another thing we could have done better mechanically would be to incorporate electrical components and wiring earlier on.
• Come up with a schedule and try your best to stick with it! Set hard schedules and try to meet them, but update them as necessary. Keep going back to it so that you don’t fall too far behind. Make sure that you will have something together in time. If you can drive, you can compete! (But actually.)
• True Value Hardware and Radioshack are your friends if lab runs out of supplies and you need something ASAP - they’re right on Mass Ave. and not too far past Random Hall!
• Make friends with the staff. They have gone through MASLAB at least once before and they have lots of advice and want to help. Ask them if you have any uncertainty about anything!
• Have fun! Regardless of whether you do well or not, know that a lot of robotics competitions is based off of sheer luck. Give it your all, and you won’t have any regrets. Instead, you’ll come out with a lot of knowledge to kick butt in future years ;)
After spending literally all of IAP in lab, we were rewarded when we got second place at the final competition.
Things will happen on competition day that you couldn’t have anticipated - the mats on the floor of 26-100 weren’t flat (which led to one team losing their match!), an audience member wore a shirt with the exact same color as a reactor (causing one team to try releasing a ball), and BotClient was down at the beginning (causing the teams in the first match not to move…). Test as MUCH as possible once you get to 26-100 on competition day.
It was sad to see many of the teams - who pulled just as many all-nighters as us and whose robots were moving even 20 minutes before the competition - have a robot malfunction during their matches (usually for the stupidest of reasons). You can only do your best to help prevent these as much as possible, but things will happen. Our sonars died during the competition almost each round, and we once ran the wrong file by accident. We cannot stress enough how much time-outs saved us when we got stuck, either on the mat or on a corner.
A special shout-out to Benny and the Jets for helping our team out during IAP and for a really commendable performance during the final.
Given that only one member on our team had extensive robotics experience and the fact that we battled hardware issues all month (gyro, Cytron, Maple, ultrasonics, everything), we were extremely pleased with the results. We all learned a lot and had a blast at the same time! If you don’t, you’re doing MASLAB wrong.