Scavenger's code runs on a single thread. It sequentially passes through the three time periods. Each time period contains a while loop that calls a function at the beginning to determine the territory to execute an action in. Each loop then calls lower-level task functions, which in turn call motion functions.
The motion functions are governed by proportional control. We originally considered a proportional, integral and derivative (PID) controller, but the settings were too velocity-specific. The proportional controller adds a correction to the given velocity that is directly proportional to the error between the current heading and the desired heading.
MoveToPointLong() uses data from an overhead Vision Positioning System (VPS) to determine if Scavenger has reached its destination. The overhead VPS includes an overhead camera that tracks the fiducial pattern (a QR code) of the robots to pinpoint their location. Due to problems we encountered with our freewheel, we did not use the distance measurement from the freewheel as another measure of the distance travelled, although this would be reliable in a robust robot.
The task functions also use VPS data to determine their feasibility of execution. (VPS data include the owners of the territories, the number of balls remaining in the territory, the amount of time left before the next ball can be mined if all 5 balls for the minute have been mined (i.e. the rate limit) and the current score of the robot.) For example, captureTerritory() is not called if the territory already belongs to us, and the wheel used to spin the gearbox stops spinning the moment control of the territory is gained. In mineBalls(), the robot only moves towards the lever when the rate limit for the territory allows balls to be mined, and the robot only continues collecting balls if the previous ball was collected successfully (which is reflected in a decrease in the number of balls remaining in the territory).
Scavenger's Fiducial Pattern
Safety mechanisms have been incorporated in Scavenger to increase the probability of it completing its tasks.
If the robot strays from its heading by an angle greater than a certain threshold (DRIVE_BOUND = 5 deg), it will first rotate before continuing with (or beginning) its motion in a straight line.
The robot will always try to choose a path such that it does not run into the opponent (or at least that is what we desired it to do - we later discovered that there are instances where it does not behave in such a manner). The robot will also not deposit balls in a territory in which the opponent is present. (However, we realized that we could perform this check and still have a disrupted deposition if the opponent rushes into us at the final moment. We should have performed this check the instant before opening the "fence" to let the balls out, instead of at the beginning of depositBalls().)
If the robot fails to capture a territory or mine balls from it, it will not immediately try again. Instead, it will move back a short distance and re-align itself before going back to the gearbox or the lever to try again. (In the case of the lever, the robot will return all the way back to the pivot since there is less room for error.) This gives it a higher chance of success for the second try.
For insurance, Scavenger will proceed to deposit balls the moment it has at least 13 balls in Period 2 and at least 10 balls in Period 3.
Timeouts have been incorporated in Scavenger at various levels.
If the robot does not rotate to its desired heading or travel to a particular place before a certain time, it will proceed with the timeout procedure (robotWiggle()) and then return an error to the higher-level function that called it.
We intended the robot to return an error as well if the encoder wheel stopped spinning, i.e. it has collided with an opponent or an obstacle. This timeout, unfortunately, turned out to be absent as our encoder failed on the eve of the competition just minutes before impound.
If the robot fails to change the owner of the territory after attempting to spin the gearbox in captureTerritory(), an error is returned to the higher-level function that called it. If the robot fails to increase its score after attempting to mine a ball, it also returns an error. If either of these tasks fail two times in a row, the robot re-determines which territory to go to.
In order to implement these timeouts, some functions were made to return integers rather than remain as void functions to pass the error (if any) up along the hierarchy. In most cases, Scavenger would re-determine a point to go to once a drive error occured.
The following is the source code for our robot: umain.c