Converting a Simple Autonomous program to a Command based autonomous program
This document describes how to rewrite a simple autonomous into a command based autonomous. Hopefully, going through this process will help those more familiar with the older simple autonomous method understand the command based method better. By re-writing it as a command based program, there are several benefits in terms of testing and reuse. For this example, all of the logic is abstracted out into functions primarily so that the focus of this example can be on the structure.
The initial autonomous code with loops
The code above aims a shooter, then it spins up a wheel and, finally, once the wheel is running at the desired speed, it shoots the frisbee. The code consists of three distinct actions: aim, spin up to speed and shoot the Frisbee. The first two actions follow a command pattern that consists of four parts:
1. Initialization: Seen in lines 2 & 10, prepares for the action to be per- formed.
2. Condition: Seen in lines 3 & 11, keeps the loop going while it is satisfied.
3. Execution: Seen in lines 4 & 12, repeatedly updates the code to try to make the condition false.
4. End: Seen in lines 7 & 15, performs any cleanup and final task before moving on to the next action.
The last action seen in line 18 only has an explicit initialize, though depending on how you read it, it can implicitly end under a number of conditions. The most obvious one two in this case are when it's done shooting or when autonomous has ended.
Rewriting it as Commands
The same code can be rewritten as a CommandGroup that groups the three actions, where each action is written as it's own command. First, the command group will be written, then the commands will be written to accomplish the three actions. This code is pretty straightforward. It does the three actions sequentially, that is one after the other. Line 3 aims the robot, then line 4 spins the shooter
2up and, finally, line 5 actually shoots the frisbee. The addSequential() method sets it so that these commands run one after the other.
The Aim command
As you can see, the command reflects the four parts of the action we discussed earlier. It also has the interrupted() method which will be discussed below. The other significant difference is that the condition in the isFinished() is the opposite of what you would put as the condition of the while loop, it returns true when you want to stop running the execute method as opposed to false. Initializing, executing and ending are exactly the same, they just go within their respective method to indicate what they do.
SpinUpShooter command
The spin up shooter command is very similar to the Aim command, it's the same basic idea.
Shoot command
The shoot command is the same basic transformation yet again, however it is set to end immediately. In CommandBased programming, it is better to have it's isFinished method return true when the act of shooting is finished, but this is a more direct translation of the original code.
Benefits of the command based approach
Why bother re-writing the code as CommandBased? Writing the code in the CommandBased style offers a number of benefits:
- Re-Usability You can reuse the same command in teleop and multiple autonomous modes. They all reference the same code, so if you need to tweak it to tune it or fix it, you can do it in one place without having to make the same edits in multiple places.
- Testability You can test each part using tools such as the SmartDashboard to test parts of the autonomous. Once you put them together, you'll have more confidence that each piece works as desired.
- Parallelization If you wanted this code to aim and spin up the shooter at the same time, it's trivial with CommandBased programming. Just use AddParallel() instead of AddSequential() when adding the Aim command and now aiming and spinning up will happen simultaneously.
- Interruptibility Commands are interruptible, this provides the ability to exit a command early, a task that is much harder in the equivalent while loop based code.