Subsystems Readme
Subsystems
Subsystems are the building blocks of a WPILib command-based robot. Each subsystem owns a piece of hardware (a drivetrain, a turret, a shooter) and exposes a safe public API that commands use to make the hardware do things. The command scheduler guarantees that only one command controls a subsystem at a time, which prevents two pieces of code from fighting over the same motor.
This folder contains one subfolder per mechanism on the robot. Each folder holds the subsystem class, its commands, its configuration, and the hardware abstractions it needs.
Folder layout
Every mechanism folder follows the same structure. Not every mechanism needs
every subfolder — a simple roller intake may not need a devices/ folder if the
base IO layer is sufficient.
subsystems/<mechanism>/
├── <Mechanism>Subsystem.java Main subsystem class
├── README.md Documentation for this mechanism
├── commands/
│ ├── <Action>Command.java One class per command
│ └── <Mechanism>SubsystemCommandFactory.java
├── config/
│ ├── <Mechanism>SubsystemConfig.java
│ └── <Mechanism>MotorConfig.java (if motors need their own settings)
├── devices/ Concrete motor/sensor wrappers (optional)
│ ├── <Mechanism>Motor.java Real hardware implementation
│ └── <Mechanism>SimMotor.java Simulation implementation
└── io/ AdvantageKit IO layer (optional)
├── <Mechanism>IO.java IO interface + logged inputs
└── <Mechanism>IO<Impl>.java Hardware or sim implementation
devices/ vs. io/ — what goes where?
These two folders serve different purposes and a subsystem may have one, both, or neither:
devices/contains concrete motor and sensor wrapper classes. These extend the shared motor abstractions (see the shared framework README) and handle device-specific setup like encoder scaling, current limits, and inversion. The turret uses this pattern becauseAbstractSetAndSeekSubsystemalready defines the IO contract through its motor interface.io/contains AdvantageKit IO interfaces and their implementations. The IO interface declares what data the subsystem reads (inputs) and what it writes (outputs). Separate real and sim implementations let you run the exact same subsystem logic with or without hardware. The drivebase and vision subsystems use this pattern because their data flow is more complex than a single motor.
A mechanism that uses AbstractSetAndSeekSubsystem typically only needs
devices/ because the base class handles IO through the motor interface. A
mechanism with custom sensor pipelines or third-party library integration (like
YAGSL or PhotonVision) typically uses io/.
Inheritance hierarchy
All subsystems, commands, configs, and factories extend shared base classes in
the shared/ package. The hierarchy keeps common
behavior in one place and lets each mechanism focus on what makes it unique.
WPILib SubsystemBase
└─ AbstractSubsystem<TConfig> shared/subsystems/
├─ AbstractSetAndSeekSubsystem shared/subsystems/
│ └─ TurretSubsystem, etc. subsystems/<mechanism>/
└─ AbstractMotorSubsystem shared/subsystems/
└─ AbstractVelocitySubsystem shared/subsystems/
└─ ShooterSubsystem, etc. subsystems/<mechanism>/
WPILib Command
└─ AbstractSubsystemCommand shared/commands/
└─ AbstractSetAndSeekCommand shared/commands/
└─ MoveTurretToAngleCommand subsystems/<mechanism>/commands/
AbstractSubsystemCommandFactory shared/commands/
└─ AbstractSetAndSeekCommandFactory shared/commands/
└─ TurretSubsystemCommandFactory subsystems/<mechanism>/commands/
AbstractConfig shared/config/
├─ AbstractSetAndSeekSubsystemConfig shared/config/
│ └─ TurretSubsystemConfig subsystems/<mechanism>/config/
└─ AbstractMotorConfig shared/config/
└─ MotorConfig shared/config/
See the shared framework README for details on what each base class provides.
Mechanism index
Each mechanism has its own README with an overview, how-it-works guide, configuration reference, and code-structure table. Click through for details.
| Mechanism | Description | Status |
|---|---|---|
| AprilTag Vision | Estimates robot pose from AprilTag camera data | Implemented |
| Drivebase | Swerve drivetrain for field movement and autonomous paths | Implemented |
| Feeder | Transports FUEL from the intake into the indexer lane | Planned |
| Gameplay State | Tracks the current game phase and scoring mode | Implemented |
| Harvester | Collects game pieces from the field using rollers | Planned |
| Indexer | Gates FUEL release into the shooter for timed shots | Planned |
| Intake | Collects FUEL from the field and passes it to the feeder | Planned |
| Robot Pose | Single authoritative source of robot pose on the field | Implemented |
| Shooter | Spins flywheels to launch FUEL at scoring targets | Planned |
| Turret | Single-axis rotating mount for aiming the shooter | Implemented |
Adding a new subsystem
- Create a new folder under
subsystems/named after the mechanism (lowercase, no spaces). - Add a
README.mdfollowing the standard template (see any implemented subsystem for the section order: Overview, How it works, Configuration, Code structure, Status/TODO). - Create the subsystem class extending
AbstractSubsystem(orAbstractSetAndSeekSubsystemfor profiled mechanisms). - Add a config class extending
AbstractConfig(orAbstractSetAndSeekSubsystemConfig) and register it inSubsystemsConfig.java. - Add the subsystem's entry to
subsystems.json,subsystems-sim.json, andsubsystems-test.jsonin thedeploy/folder. - Create a
commands/folder with at least a command factory class. - Instantiate the subsystem in
RobotContainerin the correct dependency order (see the copilot instructions for wiring rules).
Characterizing a motor with SysId
Every motor-driven subsystem needs accurate feedforward gains (kS, kV, kA) for
smooth control. The SysId tuning guide
covers the full process from running the four test routines through entering
corrected gains in subsystems.json.
The most important thing to remember: divide kV and kA by 2π after reading them from the SysId tool. This corrects a unit conversion issue in WPILib's logging layer. kS does not need correction. The guide includes a worked example and a sanity-check formula so you can verify the gains are reasonable before deploying.