Subsystems
An FRC robot is composed of several subsystems. Each subsystem controls one part of the robot, and they work together to perform complete actions. The 2026 game, REBUILT, is a shooting game, so many robots use subsystems such as:
-
A drive base that moves the robot.
-
An intake that collects game pieces.
-
An indexer that moves game pieces toward the shooter.
-
A shooter or turret that aims and launches game pieces.
-
A vision system that helps estimate the robot's position.
How the subsystems work together
RobotContainer
creates and connects everything
│
┌───────────────────┴───────────────────┐
│ │
Driver controls Autonomous routine
│ │
└──────────────► Commands ◄─────────────┘
│
tell subsystems what to do
│
┌──────────┬───────────┼──────────┬──────────┐
▼ ▼ ▼ ▼ ▼
Drive base Intake Indexer Shooter Vision
│ │ │ ▲ │
│ └──► ball ──┴─────────►│ │
│ │ │
└──────── moves the robot ◄──── aiming data ─┘
Each subsystem controls one part of the robot, but commands coordinate them to perform a complete action. For example, a shooting command can use vision to find the target, aim the shooter, run the indexer, and keep the drive base pointed in the correct direction.
Subsystems that do not share hardware can run at the same time. The command scheduler prevents two commands from controlling the same subsystem at once.
Writing a subsystem
A subsystem should own the hardware for one mechanism and provide small methods that commands can call. This simplified introductory example keeps the motor directly inside the subsystem so that the basic idea is easier to see. Simbot's full robot code separates hardware into IO files, as explained in Why Does a Subsystem Have Several Files? later on this page.
If CAN IDs and buses are new to you, review How the Devices Communicate first.
package frc.robot.subsystems;
import edu.wpi.first.wpilibj2.command.SubsystemBase;
import com.ctre.phoenix6.hardware.TalonFX;
public class IntakeSubsystem extends SubsystemBase {
private static final int MOTOR_ID = 10;
private static final String CAN_BUS = "rio";
private static final double INTAKE_VOLTAGE = 6.0;
private static final double EJECT_VOLTAGE = -6.0;
private final TalonFX intakeMotor;
public IntakeSubsystem() {
intakeMotor = new TalonFX(MOTOR_ID, CAN_BUS);
}
public void intake() {
intakeMotor.setVoltage(INTAKE_VOLTAGE);
}
public void eject() {
intakeMotor.setVoltage(EJECT_VOLTAGE);
}
public void stop() {
intakeMotor.setVoltage(0);
}
}
IntakeSubsystem extends SubsystemBase, which allows WPILib's command scheduler to treat it as a subsystem. Its constructor creates the TalonFX using the specified CAN ID and CAN bus.
This class has two main responsibilities:
- It owns the hardware. Other classes should control the intake through this subsystem instead of accessing
intakeMotordirectly. - It provides actions while hiding hardware details. A command can call
intake(),eject(), orstop()without needing to know the motor's CAN settings or output values.
The constants INTAKE_VOLTAGE and EJECT_VOLTAGE determine how strongly and in which direction the motor runs. The opposite signs make the motor turn in opposite directions. Keeping those values outside the action methods makes them easy to tune without changing the subsystem's behavior.
Constructing the subsystem
Defining a subsystem class does not create the subsystem. You must construct it in RobotContainer.java:
private final IntakeSubsystem intake = new IntakeSubsystem();
This creates one IntakeSubsystem object when RobotContainer is created. That object initializes the intake hardware and is the shared instance used when you configure commands and controller buttons.
Construct each subsystem only once. Commands should receive the existing subsystem through their constructors instead of creating a new one. Otherwise, multiple objects could try to represent and control the same physical hardware.
Why Does a Subsystem Have Several Files?
Our 2026 robot Simbot Tim uses several files to define a subsystem. For example, the Intake subsystem has:
IntakeConstants.java
IntakeIO.java
IntakeIOHardware.java
IntakeIOSim.java
IntakeSubsystem.java
These files separate where data comes from from what the robot does with that data.
| File | Responsibility |
|---|---|
IntakeIO.java | Defines the values the subsystem can receive and the actions it can request. It is the shared contract for every implementation. |
IntakeIOHardware.java | Talks to real motors and sensors directly. Basic requests, such as applying voltages to motors, are defined here. |
IntakeIOSim.java | Produces the same inputs using a physics simulation instead of real hardware. |
IntakeSubsystem.java | Contains the mechanism's control logic. It uses the IO interface, whether it's simulation or real hardware, to provide abstractions for commands. |
This structure gives us benefits:
- the subsystem logic does not contain vendor-specific motor-controller code;
- hardware communication is only accessible by implementing IO;
- hardware and simulation can use the same subsystem;