Skip to content

Why Command-Based? Subsystems and the Scheduler

learnfrc.com
learnfrc.comAuthor
Veer Bajaj
Veer BajajMaintainer

The command-based framework organizes code around two abstractions:

  • Subsystems — a collection of hardware that works as a unit (a drivetrain, an arm, an intake). The subsystem owns its motors and sensors and exposes clean public methods like setSpeed() or getAngle(). The rest of the code can’t touch the motors directly — only through the subsystem.
  • Commands — actions the robot performs (drive forward, raise the arm, run the intake). Commands use subsystems to do their work.

This separation keeps your code organized: hardware details stay inside subsystems, and behavior lives in commands.

The recommended way is to subclass SubsystemBase (Java/C++):

public class Intake extends SubsystemBase {
private final PWMSparkMax m_motor = new PWMSparkMax(5);
public void run() { m_motor.set(0.8); }
public void stop() { m_motor.set(0.0); }
@Override
public void periodic() {
// Called automatically every 20 ms by the scheduler
}
}

A subsystem’s periodic() is run automatically by the scheduler — perfect for logging sensor values or running closed-loop updates.

The CommandScheduler is a singleton that runs the whole system. You start it by calling CommandScheduler.getInstance().run() once per loop — and the Command Robot template does this for you inrobotPeriodic(). Each loop, the scheduler:

  1. Runs each subsystem’s periodic().
  2. Polls triggers/button bindings.
  3. Runs the currently scheduled commands.
  4. Handles commands that have finished or been interrupted.

Requirements: the resource-management rule

Section titled “Requirements: the resource-management rule”

The single most important rule: each command declares which subsystems it requires. The scheduler guarantees that no two commands require the same subsystem at the same time. If a new command needs a subsystem already in use, the old command is interrupted (by default). This prevents two pieces of code from fighting over the same motor — a classic source of bugs.

A subsystem can have a default command that runs whenever no other command requires it. The classic example: the drivetrain’s default command reads the joysticks and drives the robot, so the robot is always drivable unless something else (like an auto-aim command) takes over.

drivetrain.setDefaultCommand(
drivetrain.run(() -> drivetrain.arcadeDrive(driver.getLeftY(), driver.getRightX())));

With subsystems + the scheduler + requirements, complex robots stay manageable: each mechanism is isolated, conflicts are impossible by design, and behavior is composed from small, reusable commands. This is why command-based is the recommended structure for nearly all teams.

  • Subsystems encapsulate hardware; commands encapsulate behavior.
  • Subclass SubsystemBase to create a subsystem; its periodic() runs automatically.
  • The CommandScheduler runs everything; the Command Robot template calls it in robotPeriodic().
  • Each command declares required subsystems, so two commands can never fight over one subsystem.
  • Default commands run when nothing else needs the subsystem — e.g., joystick driving.

This lesson was adapted from learnfrc.com.