-
Notifications
You must be signed in to change notification settings - Fork 2
Lesson 3: Subsystems
James Li edited this page May 27, 2025
·
16 revisions
Think of each subsystem as a self-contained box that manages a specific robot mechanism.
- Keeps code organized by robot functions (eg. drivetrain, intake)
- Improve readability
- Makes testing easier
- Smart motor controller with integrated encoder (can translate mechanical motion into code representing an angle, position, speed, etc)
- Supports advanced features
- Built in PID controls
- Motion profiling
- Current limiting
- Tells the motor to spin forward/backward at x percent power (Motor output power is in between -1.0 to 1.0):
.Set(ControlMode::PercentOutput, value) - Returns motor's encoder position:
.GetSelectedSensorPosition() - Tunes motor's built-in PID controllers:
.Config_kP(...),.Config_kI(...),.Config_kD(...),.Config_kF(...) .SetNeutralMode(Brake/Coast)
For each subsystem (eg. Wrist), you may see:

Each plays a different role to make code clean, testable, and adaptable.
- Makes it easier to switch between real robot and simulation
- Supports hardware abstraction
- Enables unit testing without needing the robot
- Encourages clean separation of logic
aka the main logic of the subsystem
- Owns an instance of
WristIO - Receives sensor data from
WristIO - Tells
WristIOwhat motor outputs to apply - Holds PID loops, state machines, etc.
Think of it as the "brain of the wrist"
aka the Java interface file
- Defines the contract between the logic and the hardware
- Includes methods like
updateInputs(),setVoltage(), etc. that must be provided by the real/sim classes.
public interface WristIO {
public default void updateInputs(WristIOInputs inputs) {}
public default void setVoltage(Voltage voltage) {}
public default void setBrakeMode(boolean enable) {}
/** Reset the motor encoder to the specified position. */
public default void resetPosition(Angle position) {}
}- It ensures that implementations like
WristIOTalonFXorWristIOSimwill work when usingWrist.java
Implementation example:
public class WristTalonFX implements WristIO {
@Override
public void setVoltage(Voltage voltage) {
motor.setControl(voltageRequest.withOutput(voltage.in(Volts)));
}
// more implementation...
}No matter the type of implementation, follow this interface
aka real hardware implementation
- Implements
WristIOusing a TalonFX motor - Talks to real motor controllers
- Updates sensor readings and sends voltage outputs
Plug in this file for real-world operation
aka simulation implementation
- Mocks sensor values and simulates movement
- Lets you run and test
Wrist.javawithout a robot - Implements
WristIOusing WPILib's sim features
Swap this in when testing in the simulation
Wrist.java
↕ (uses)
WristIO.java (interface)
↕ (implemented by)
WristIOTalonFX.java ← for real robot
WristIOSim.java ← for sim/testing
Choose which implementation to use based on which real/sim build mode you want to run
- Inside
RobotContainer, create your subsystem like this:
// For real robot
Wrist wrist = new Wrist(new WristIOTalonFX());
// For sim
Wrist wrist = new Wrist(new WristIOSim());- If the subsystem uses motors/sensors
- Systems that want to be simulated
- Subsystems that's hardware might change later
