On This Page

Commands

Although you can avoid it in some simple cases, doing anything complex with your FRC robot will involve creating commands. These respond to joysticks and buttons, run your autoonomous routines, and do other maintenance tasks.

In addition to the usual constructor, commands have four lifecycle methods: initialize, execute, isFinished, and end. These methods are called by the command scheduler (and never by you). By overriding the implementation of these methods, you can change the behaviour of the command.

Lifecycle methods of a command: initialize, execute, isFinished, and end
The scheduler calls the four lifecycle methods of a command. This starts with initialize when the command is first scheduled, then execute and isFinished are called in alternation. Finally end is called either when isFinished returns trure, or when the command is interrupted.

void initialize() 🔗

void execute() 🔗

boolean isFinished() 🔗

void end(boolean interrupted) 🔗

These methods (as well as subsystem periodic methods and any Triggers you have created) all run in a single shared thread, which is expected to run fifty times a second. This means that they all share a total time budget of 20ms. It is important that these commands execute quickly, so avoid using any long-running loops, sleeps, or timeouts. The scheduler will only run one command lifecycle method (initialize, isFinished, execute, end) or subsystem periodic at a time, so if you stay within this framework you don’t have to worry about being thread-safe.

Generally commands exist in order to do something with a subsystem, like run motors. It’s very important that you never have two commands trying to control the same motor. WPILIB’s solution to this is called “subsystem requirements”. Generally you will pass the subsystems into the command’s constructor (along with any other configuration) to be stored for later use. In the constructor, you should also call addRequirements(...) with any subsystems used by the command. In some complex cases, you may use a subsystem without requiring it, say because you are only reading sensor data and setting any motor speeds.

A programmer needs to be familiar with the various command-related tricks available in WPILIB. I’ve divided them here into six groups:

These might seem a little complex and daunting, but the good news is that if you use them effectively your code will become simpler and easier to read. There are many subtle gotchas about combining commands, and these help you to navigate them safely.

Command groups 🔗

Diagram showing SequentialCommandGroup, ParallelCommandGroup, ParallelRaceGroup and ParallelDeadlineGroup
SequentialCommandGroup runs each command in turn until the last finishes. ParallelCommandGroup runs the commands in parallel, until they all finish. ParallelRaceGroup runs until the fastest command finishes. ParallelDeadlineGroup runs until the first command finishes.

These classes group togather one or more commands and execute them all in some order. They inherit the subsystem requirements of all of their sub-commands. The sub-commands can be specified either in the constructor, or by subclassing and using addCommands.

SequentialCommandGroup 🔗

ParallelCommandGroup 🔗

ParallelRaceGroup 🔗

ParallelDeadlineGroup 🔗

Commands used in groups 🔗

The following commands are useful to build command groups. Some of them take commands as arguments, and their subsystem requirements are inherited.

Runnable wrappers 🔗

Here are some wrappers that turn runnables (e.g. lambda expressions) into commands. These can be used in command groups, but they are also used in RobotContainer to create command on-the-fly. When using these methods, please remember to add the subsystem(s) as the last parameter(s) to make subsystem requirements work correctly.

Wrapper initialize execute end isFinished
InstantCommand arg 1 empty empty true
RunCommand empty arg 1 empty false
StartEndCommand arg 1 empty arg 2 false
FunctionalCommand arg 1 arg 2 arg 3 arg 4

Command decorators 🔗

These are methods that are provided by all Commands and allow you to create new commands that modify the underlying command in some way, or implicitly create command groups. These can be used as an alternative way to write command groups, but are also used when creating commands on-the-fly in RobotContainer.

(I have omitted a few of the more esoteric decorators for brevity.)

Running commands 🔗

There are generally three ways to run a command:

Triggers 🔗

Triggers are objects that run some command when some event takes place, like a button being pressed. The easiest way to create a trigger is by using a CommandJoystick or CommandXboxController. Trigger objects don’t need to be stored.

CommandJoystick joystick = new CommandJoystick(0);

joystick.button(1).toggleOnTrue(new MyCommand(...))

It is also possible to create triggers from any Boolean supplier:

new Trigger(() -> subsystem.getLimitSwitch()).whileTrue(...)
Comparison of onFalse, onTrue, toggleOnFalse, toggleOnTrue, whilefalse, and whileTrue
onTrue starts when a button is pressed and usually ends on its own. whileTrue starts when the button is pressed and runs until it is released. toggleOnTrue turns on or off in alternation every time the button is pressed. onFalse, whileFalse, and toggleOnFalse do the same, but when the button is released.

Some trigger methods should be passed a command to run:

Some trigger methods create new triggers:

Of these, you will probably use onTrue (for instant commmands), whileTrue (to run while pressed), toggleOnTrue (to turn on or off when pressed), and debounce (to smooth noisy signals) most often.

Default commands 🔗

Sometimes you want a command to run all the time on some subsystem, unless you have something more specific to run. This is the “default command” for that subsystem.

The most commonly encountered example of a default command is the “Arcade Drive” command, which connects a joystick to the drive subsystem. This will run all of the time, except when you engage some autonomous driving routine.

To set the default command for a subsystem, simply call setDefaultCommand(). Each subsystem can only have (at most) one default command. When using default commands, it is important that all commands using that subsystem have their requirements set correctly; this ensures that the scheduler will deschedule the default command when they are scheduled.

// in RobotContainer.java, in configureBindings()
m_driveSubsystem.setDefaultCommand(
    new ArcadeDriveCommand(m_driveSubsystem,
        () -> m_joystick1.getX(), // double supplier for turn
        () -> m_joystick1.getY()); // double supplier for speed

Autonomous commands 🔗

TODO

Esoteric commands 🔗

These commands are used only in very specific circumstances.

See also 🔗