Skip to main content

Command-based Programming


The command-based program design allows programming to follow a set design pattern. The specific command system that WPILib uses follows a declarative programming style. The emphasis is, instead, on what the program should do rather than how to do it. This minimizes the iteration-by-iteration robot logic needed to write out a certain action. Very simply, you can bind some actions to buttons/triggers, such as the example below:

aButton.whenPressed(intake::run);

To learn more about the command-based programming we use, please look here.

Terminology

Subsystem

A subsystem is the basic unit of robot organization in the design-based paradigm. Subsystems encapsulate lower-level robot hardware (such as motor controllers, sensors, and/or pneumatic actuators), and define the interfaces through which that hardware can be accessed by the rest of the robot code. Subsystems allow users to “hide” the internal complexity of their actual hardware from the rest of their code - this both simplifies the rest of the robot code and allows changes to the internal details of a subsystem without also changing the rest of the robot code.

Essentially, subsystems are classes that define specific mechanisms on the robot. They extend SubsystemBase.

Command

A command defines high-level robot actions or behaviors that utilize the methods defined by the subsystems. A command is a simple state machine that is either initializing, executing, ending, or idle. Users write code specifying which action should be taken in each state. Simple commands can be put together into “command groups” to accomplish more complicated tasks. Commands, including command groups, implement the Command interface.

How Commands are Run

For a more detailed explanation, please see the Command Scheduler.

Commands are run by the CommandScheduler, which is a singleton at the base of the command system. The command scheduler is in charge of polling buttons for new commands to schedule, checking the resources required by those commands to avoid conflicts, executing currently-scheduled commands, and removing commands that have finished or been interrupted. The scheduler’s run() method may be called from any place in the user’s code.

Multiple commands can run concurrently, as long as they do not require the same resources on the robot. Resource management is handled on a per-subsystem basis: commands may specify which subsystems they interact with, and the scheduler will never schedule more than one command requiring a given subsystem at a time. This ensures that, for example, users will not end up with two different pieces of code attempting to set the same motor controller to different output values. If a new command is scheduled that requires a subsystem already in use, it will either interrupt the currently-running command that requires that subsystem (if the command has been scheduled as interruptible) or not schedule the new command.

Subsystems can also be associated with “default commands” that will be automatically scheduled when no other command is currently using the subsystem. This is useful for continuous “background” actions such as controlling the robot drivetrain or keeping an arm held at a setpoint.

When a command is scheduled, its initialize() method is called once. Its execute() method is then called once per call to CommandScheduler.getInstance().run(). A command is unscheduled and has its end(boolean interrupted) method called when either its isFinished() method returns true, or else it is interrupted (either by another command with which it shares a required subsystem, or by being canceled).

Command Groups

It is often desirable to build complex commands from simple pieces. This is achievable by composing commands into “command groups.” A command group is a command that contains multiple commands within it, which run either in parallel or in sequence. The command-based library provides several types of command groups for teams to use, and users are encouraged to write their own if desired. As command groups themselves implement the Command interface, they are recursively composable - one can include command groups within other command groups. This provides an extremely powerful way of building complex robot actions with a simple library.

There are a few different types of command groups that are essential to command-based programming. These are:

  • Sequential Command Group
    • A SequentialCommandGroup runs a list of commands in sequence - the first command will be executed, then the second, then the third, and so on until the list finishes. The sequential group finishes after the last command in the sequence finishes. It is therefore usually important to ensure that each command in the sequence does actually finish (if a given command does not finish, the next command will never start!).
  • Parallel Command Group
    • A ParallelCommandGroup runs a set of commands concurrently - all commands will execute at the same time. The parallel group will end when all commands have finished.
  • Parallel Race Group
    • A ParallelRaceGroup is much like a ParallelCommandGroup, in that it runs a set of commands concurrently. However, the race group ends as soon as any command in the group ends - all other commands are interrupted at that point.
  • Parallel Deadline Group
    • A ParallelDeadlineGroup also runs a set of commands concurrently. However, the deadline group ends when a specific command (the “deadline”) ends, interrupting all other commands in the group that are still running at that point.

What's the benefit?

When comparing command-based programming to a comparable design composed of switch statements or if-else statements, one will notice that the same behavior can be accomplished across all styles of programming. So what is the benefit of the command-based paradigm?

  1. Modularity and Code Organization
    • Commands encapsulate individual actions (e.g. driving, shooting, lifting)
    • Subsystems manage hardware components, making debugging easier
  2. Reusability and Scalability
    • Commands can be reused across different modes (Autonomous & Teleop)
    • Command Groups allow for complex sequences of actions
  3. Improved Readability and Maintainability
    • Eliminates annoying while loops
    • Each subsystem contains functions and periodic methods that happen every loop. Usually, this eliminates complex state logic
    • Easier for new programmers to understand and contribute
  4. Development timeframe
    • Commands largely simplify complex actions to just a few lines of code. This is the most important benefit of the Command-based paradigm. In FRC, we have approximately 6 weeks to build, program, test, and make changes to our robot. If we spent all of our programming time writing while loops and if statements, we would likely be unable to finish coding the robot.

Instance Command Factory Methods

Learn more about organizing command-based robot projects here.

One of the benefits of command groups is their ability to define complex actions in just a few short lines. While this can be accomplished by creating a class for each command group, we can also use inline commands to accomplish the same functionality. A short example, taken from WPILib documentation, is below:

intakeButton.whileTrue(intake.runIntakeCommand());
Command intakeAndShoot = intake.runIntakeCommand().alongWith(new RunShooter(shooter));
Command autonomousCommand = Commands.sequence(
intake.runIntakeCommand().withTimeout(5.0),
Commands.waitSeconds(3.0),
intake.runIntakeCommand().withTimeout(5.0)
);