Team Twelve/Paper
From Maslab 2006
| Maslab teams |
|---|
| Team 1 · 2 · 3 · 4 · 5 · 6 · 7 · 8 · 9 · 10 · 11 · 12 · 13 · 15 |
| Team Twelve's Journal · Paper |
Contents |
Strategy
General Robot Construction
Our general strategy for the construction of our robot was to keep it simple to maximize our chances to win the competition, rather than trying something new. For the robot body, we did not want anything to fancy, just something simple with a robust design. This is why we chose a simple square shape for the body as to maximize the amount of room we had available to capture balls as well as enough area to place all of our devices. A ball gate (as opposed to a robot arm, or roller device) was constructed to easily gather balls by opening and closing. Balls could be scored by simply lifting the gate, and driving forward to let the balls roll out.
Scoring
The basic idea for finding balls was to simply wallfollow until a ball was found, then attempt to capture. If a ball was successfully captured, then it would return to wallfollowing, but this time it would be looking for goals. When a goal is found it will attempt to score. The robot currently doesn't have any way of knowing whether it scored or not. With this in mind, the robot ignores the powerball, and makes no distinction between the power goal and any other goal.
We concluded that this strategy would work best if the balls and goals were uniformly spread across the field, because if the robot finds a ball, it would not take long to find a goal and score. However, the worst-case scenario for this strategy would be if all of the balls were clustered together, and all of the goals were clustered together at the opposite end of the field. This would be bad because if the robot picked up one ball, the robot would have to travel across the field to score, and travel all the way back to grab another ball. Clearly a strategy which collects more than one ball at a time, would dominate in this scenario, because it would be able to save a lot of time. However, we felt that the latter scenario was unlikely.
Mechanical Design
Ball Handling
Mechanical elements of the design were completed before the behavior system was finalized so it includes some features intended uniquely to hold the green ball. The gate was a piece of sheet metal bent at a 90 degree angle with the bend on the axis of rotation of the gate. This feature allowed the gate to have an additional holding area when the concave face of the gate was facing down. This is intended for holding the green ball apart from other red balls what have already been found and captured.
The robot had a ball holding bay capable of holding over five balls. A gate driven by a servo through a four bar linkage governed if balls were able to roll out of the bay. The opening of the bay was thirteen inches wide to allow for low tolerances in software. The back of the bay was a concave parabola. Therefore the furthest back point was on center so that driving forward and stopping should cause the balls to exit approximately on center. Further any that ricochet off the back wall will pass through a single point eight inches beyond the front of the robot. This feature allows for a wide opening, a large ball capacity and accurate ball emission.
Driving
Wheel slippage is a main cause for error in dead reckoning navigation. In order to combat this we tried to increase the frictional force at the driving wheels. We did this first by gluing sandpaper to the wheels to increase the coefficient of friction. Second, we put the battery and computer above the rear wheels to increase the normal force they support. As F=f N, we have maximized both terms that define the frictional force. One caster was placed on each front corner. The resulting four points of contact are not problematic as 90% of the weight is on the drive wheels, so that if one point lifts off the ground it will be one of the castors.
The wheel position was also monitored by usage of quad phase encoders on each drive wheel. Four IR sensors were used for navigation. One sensor was facing towards each side on the front to detect walls, one facing forward, and one in the bay to detect the successful capture of balls.
Software Design
Summary
The approach to controlling the robot is constructed through a collection of threads. Each thread is responsible for controlling some sensory or output device of the robot. For example, one thread might be processing input from the camera, another thread might process input from the IR sensors, and another producing the controlling output to the motors.
These threads have the ability to stop and start one another through a governing thread. This governing thread is responsible for making sure all threads are in sync. One example scenario that the governing thread keeps track of is to make sure that no two threads try to access the motor-controlling thread at the same time. The governing thread is never stopped and is responsible for the initialization and termination of all other threads.
The advantage of having a threading based approach, is once the system has been constructed and tested, it is very easy to implement threads and use them concurrently. A thread used to monitor when the robot is stuck (team12.behaviors.StuckDetect) was easily implemented in less than twenty lines. This allows the creator of these threads to focus on behavior of the thread on an abstract level (executing commands like turnLeft and turnRight, instead of increase PWM until gyro reads 90 degrees to the left, etc.). This decoupling of the control is essential in the simplicity of the Behavior System, explained in the next section.
Behavior System
Each of the aforementioned threads (except for the governing thread) are implemented as extensions of the team12.behaviors.Behavior class. The Behavior abstract class provides a wrapper around the functionality associated with a Java Thread. Its subclasses need only to provide the implementation for a run() method, which will be responsible for the main body of the thread. The Behavior abstract class also provides functionality for safely stopping and starting itself by means of a isRunning boolean variable used to control any loops in the run() method. Example extensions of the Behavior class can be found in the team12.behaviors package.
Each Behavior also provides the ability to execute a callback method. If a Behavior would like to execute some outside method when some condition is reached inside of the Behavior's run() method, they can call doAction(), which executes some callback object with a method, which was given to the Behavior at the time of its creation. The Behavior System makes use of this in detecting red balls for example. When the scanForRedBalls Behavior is processing the input from the camera and registers that it sees a red ball, it executes the doAction() method. The particular callback method that was given to scanForRedBalls stops itself (the scanForRedBalls Behavior) and starts the captureBall Behavior. These callback methods are given to the Behaviors by the governing thread. Examples of this are in the team12.Brain.winMaslab() method.
Vision Processing
Vision processing is encapsulated by the team12.Eye class. This class provides methods for reading in BufferedImages from the Logitech camera and processing them. Behaviors have also been created that make repeated calls to these methods.
The algorithm used to process the BufferedImages is a fairly simple one. First, the BufferedImage is transformed into the HSV space. It scans from left to right across the pixels until it finds a set number of pixels within a certain color threshold. The procedure then explores the pixels around this group of uni-colored pixels until the bounds of this blob of pixels are fitted to a rectangle. This blob is then instatiated as a VisionObject. There are a few objects in the team12.objects package that can be used to identify these objects such as Ball and Goal.
The image processing algorithm only processes redballs and yellow goals at this point. The power ball, blue lines on walls, and blue ticks are all ignored at this point. Since the vision does not read blue lines, we sometimes had the problem that people wearing red or yellow shirts that were in the camera's view were realized by the vision processing. The pixel by pixel method was also a bit slow, allowing only for a processing frequency of 10 frames per second. Furthere, a computationally expensive algorithm in a multi-threaded system, takes up a lot of processing time, and prevents other threads from running smoothly.
Motor Control
The team12.Bot class handles the motor control. Briefly, the motors are controlled by a pd-controller. That is, only linear and derivate error terms are used to calculate what PWM values to send to the motors. The Bot class maintains a thread which is constantly looping the pd-controller on the motors. The pd-controller polls two variables whose values represent the desired angular speed of each motor. The pd-controller reads in the actual angular speed from the wheels and adjusts the motors' PWM values according to the desired angular speed. Outside Behaviors set these desired angular speed values through the interface that the Bot class provides. This pd-controller runs on its own thread and executes for the same duration that the governing thread executes.
The advantage of having this pd-controler running on its own thread, and to have a nice interface to it, is that a Behavior can easily set the wheels to go a certain velocity, but not have to worry about the details of making sure that the wheels are going the correct velocity, and changing it if needed. In other words, this puts a nice abstraction layer over motor control so that Behaviors can concentrate on executing their behavior.
The readings from the quad phase encoders are sometimes unreliable. To counter-act this, the Bot class averages the readings from the encoders over twenty samples. While this helps smooth out the readings from the encoders, it also means that the pd-controller takes a longer time to adjust PWM values.
Wall Following
Wall following for our robot is simply implemented as a Behavior. The run() method of the Behavior consists of a while loop that monitors the readings of the three IR sensors mounted at the front of the robot. If any of these sensors reads that a wall is close, the robot turns to align itself with the wall. It then drives straight while continuing to monitor the IR sensors for a change in the wall's direction. The robot repeats this loop until the governing thread stops it.
Performance
Hardware
The quad phase encoders probably provided the biggest pain to work with since they needed to be precisely aligned with the wheels in order to return accurate readings. However, reliable casters and the sand paper on the wheels helped to stabilize the robot's movement.
Although in theory the parabolic ball bay wall makes the balls focus to a center point, in practice, it proved to be not so effective. When the robot moved forward to release the balls, many times the balls would bounce on one of the sides of the ball bay on its way out.
A lot of teams complained about the noisiness of the gyro device, but our team found that after a few seconds of calibration, the gyro was excellent for making approximate turns.
Software
The Behavior System proved very difficult to implement. The stop() and start(Action) have to wait until the main loop of the Behavior finishes execution. This was the source of the threading deadlock that occured in the final competition. When a Behavior is trying to stop, let's say, because another behavior wants to start, it must wait until its run() method naturally finishes execution. However, if the Behavior trying to stop, is waiting on the Behavior trying to start, then the robot's Behavior System will go into deadlock and never recover. Even if the Behavior System removes complexity, if it's not implemented correctly, it can cause a barrage of headaches.
All in all, the addition of new concurrent threads slows down the overall behavior of the robot. For example, the wall following Behavior works well when running by itself, but when the image processing is concurrently run, the wall following begins to bump the robot into walls because it can process the IR input as fast. The effect of running only two Behaviors is not that drastic, but when more than five Behaviors are running at the same time (which is true in our system at some point), the effects are substantial.
The restriction imposed by the moderately slow processor (it needed to be slow as to not draw too much current from the battery) was daunting, and our team realized that it may have been advantageous to work in a lower-level language such as C to write our procedures. Further, the code style that was used to write the Java classes and its corresponding methods seemed very C like, in that very little state was kept in Java Objects and more in local variables used within methods (very procedural based, instead of object based).
Conclusions and Suggestions
Building robots is fun but extremely difficult. Writing software for them entails dealing with the physical reality that the robot exists in and all of its continuous imperfections. The biggest headache we encounter was getting the encoders to work, because the ability to drive straight was a prerequisite to many of the other software Behaviors. I would recommend getting the construction of the robot done as soon as possible so that a thorough amount of testing and tweaking can be accomplished.
If time permits, a testing framework would be one way of eliminating the need to use the physical robot to test the software. This testing framework would emulate real-world input from the sensors in a virtual space so that execution of software can be tested without the need to stop the mechanical engineers.
In the end, our team acquired a great deal of valuable knowledge not only in designing and constructing a robot, but also in working with a team and delegating and parallelizing tasks. We are all much better mechanical and computer engineers because of it. The amount of time you put into Maslab, you get back double in experience.
