Last Updated: 3/9/2026
Subsystem Architecture
This document explains how subsystems are organized and interact in the BREAD5940 2025 robot code.
Subsystem Overview
The robot has 8 major subsystems that work together to accomplish game tasks:
- Swerve - Omnidirectional drivetrain
- Superstructure - Main coordinator and state machine
- ElevatorPivot - Vertical and angular positioning
- Intake - Ground coral intake mechanism
- EndEffector - Scoring and game piece manipulation
- Indexer - Game piece transfer between intake and end effector
- Climber - End-game climbing mechanism
- Vision Systems - AprilTag tracking and object detection
Subsystem Hierarchy
Independent Subsystems
Swerve and Vision Systems operate independently:
Swerve
└── Controls: 4 swerve modules
└── Sensors: Gyro, odometry
└── Purpose: Robot movement
Vision Systems
├── PhotonAprilTagVision
├── RealSenseVision
└── ObjectDetectionCoordinated Subsystems
Superstructure coordinates multiple subsystems:
Superstructure (State Machine)
├── ElevatorPivot
│ └── Controls: Elevator motor, pivot motor
├── Intake
│ └── Controls: Intake rollers, deployment
├── EndEffector
│ └── Controls: Coral/algae manipulation
├── Indexer
│ └── Controls: Transfer mechanism
└── Climber
└── Controls: Climb motorsWhy Superstructure?
The Superstructure pattern solves a critical problem: coordinating multiple subsystems safely.
Without Superstructure
❌ Problem:
// Dangerous! No coordination
intake.deploy();
elevatorPivot.moveTo(highPosition); // Might collide!
endeffector.intake();With Superstructure
✅ Solution:
// Safe! Superstructure ensures valid transitions
superstructure.requestIntake(true);
// Superstructure coordinates:
// 1. Deploy intake
// 2. Wait for safe position
// 3. Lower elevator if needed
// 4. Activate end effectorSubsystem Communication Patterns
1. Direct Control (Swerve)
Commands directly control independent subsystems:
public class MoveToPoseCommand extends CommandBase {
private final Swerve swerve;
@Override
public void execute() {
ChassisSpeeds speeds = controller.calculate(...);
swerve.requestVelocity(speeds, true);
}
}2. Request Pattern (Superstructure)
Requests trigger state machine transitions:
public class AutoPlaceCommand extends CommandBase {
private final Superstructure superstructure;
@Override
public void initialize() {
superstructure.requestPrePlace(true, Level.L4);
}
@Override
public void execute() {
if (atTarget()) {
superstructure.requestScore(true);
}
}
}3. Sensor Queries
Subsystems expose sensor data:
if (endeffector.hasCoral()) {
// React to game piece possession
}
if (elevatorPivot.atSetpoint()) {
// Mechanism is in position
}IO Abstraction Pattern
Subsystems use a hardware abstraction layer for testability and simulation.
Pattern Structure
Subsystem/
├── Subsystem.java # Main logic
├── SubsystemIO.java # Hardware interface
├── SubsystemIOReal.java # Real hardware
└── SubsystemIOSim.java # SimulationExample: Intake IO
SubsystemIO.java (Interface):
public interface IntakeIO {
public static class IntakeIOInputs {
public double motorVelocity = 0.0;
public double motorCurrent = 0.0;
public boolean hasPiece = false;
}
public void updateInputs(IntakeIOInputs inputs);
public void setVoltage(double volts);
}SubsystemIOReal.java (Hardware):
public class IntakeIOReal implements IntakeIO {
private final TalonFX motor;
@Override
public void updateInputs(IntakeIOInputs inputs) {
inputs.motorVelocity = motor.getVelocity().getValue();
inputs.motorCurrent = motor.getStatorCurrent().getValue();
}
@Override
public void setVoltage(double volts) {
motor.setVoltage(volts);
}
}Benefits:
- Testable without hardware
- Replay-compatible logs
- Easy simulation
- Hardware swapping
Subsystem Lifecycle
Initialization
In RobotContainer.java:
public class RobotContainer {
public static final Swerve swerve = new Swerve(...);
public static final ElevatorPivot elevatorPivot = new ElevatorPivot();
public static final Intake intake = new Intake();
public static final Superstructure superstructure =
new Superstructure(elevatorPivot, intake, endeffector, indexer, climber);
}Periodic Execution
In Robot.java:
@Override
public void robotPeriodic() {
CommandScheduler.getInstance().run(); // Runs all subsystem periodic()
// Additional periodic methods
RobotContainer.superstructure.periodic2();
RobotContainer.intake.periodic2();
// ...
}Why periodic2()?
periodic()is called by CommandSchedulerperiodic2()provides additional update cycle- Allows state machine updates after command execution
State Management
Subsystem States
Each subsystem maintains its own state:
public enum IntakeState {
IDLE,
INTAKING,
INTAKING_WITH_SPIN,
L1_INTAKE,
SPITTING,
UNJAMMING,
HOMING
}State Transitions
Request-based:
public void requestIntake() {
requestedState = IntakeState.INTAKING;
}
public void periodic2() {
IntakeState nextState = currentState;
if (currentState == IntakeState.IDLE) {
if (requestedState == IntakeState.INTAKING) {
nextState = IntakeState.INTAKING;
}
}
if (currentState != nextState) {
currentState = nextState;
}
}Subsystem Interactions
Game Piece Flow
Game pieces move through multiple subsystems:
1. Ground Coral
↓
2. Intake (picks up)
↓
3. Indexer (transfers)
↓
4. EndEffector (holds)
↓
5. Scoring PositionCoordination Example
Intaking Sequence:
- Driver input: Right trigger pressed
- Robot.java: Calls
superstructure.requestIntake(true) - Superstructure: Transitions to
INTAKING_CORAL_PREPARE - Superstructure: Commands subsystems:
elevatorPivot.requestPursueSetpoint(lowHeight, intakeAngle)intake.requestIntakeWithSpin()indexer.requestIntake()endeffector.requestIntakeCoral()
- Sensors detect piece:
endeffector.hasCoral()returns true - Superstructure: Transitions to
IDLE_HAS_CORAL
Safety Mechanisms
1. Collision Avoidance
Problem: Elevator and intake could collide
Solution: Superstructure enforces safe transitions
if (needsToRaise && pivotAngle < SAFE_ANGLE) {
// Rotate pivot to safe angle first
elevatorPivot.requestPursueSetpoint(currentHeight, SAFE_ANGLE);
} else {
// Safe to raise elevator
elevatorPivot.requestPursueSetpoint(targetHeight, targetAngle);
}2. State Validation
Problem: Invalid state transitions
Solution: State machine validates requests
if (systemState == SuperstructureState.CLIMBER_DEPLOYED) {
// Can't intake while climbing!
if (requestIntake) {
requestIntake = false; // Ignore invalid request
}
}3. Timeout Protection
Problem: Subsystems stuck in transition
Solution: Time-based fallbacks
double stateElapsedTime = Timer.getFPGATimestamp() - stateStartTime;
if (stateElapsedTime > 2.0 && !atSetpoint()) {
// Timeout - transition to safe state
nextState = SuperstructureState.IDLE_EMPTY;
}Logging and Telemetry
All subsystems log their state via AdvantageKit:
private void logTelemetry() {
Logger.recordOutput("Superstructure/SystemState", systemState);
Logger.recordOutput("Superstructure/Level", level);
Logger.recordOutput("Superstructure/HasCoral", endeffector.hasCoral());
Logger.recordOutput("Superstructure/HasAlgae", endeffector.hasAlgae());
}View in AdvantageScope:
- Open log file
- Navigate to subsystem namespace
- See state transitions and sensor values
Best Practices
1. Encapsulation
✅ Good: Subsystem controls its own hardware
public class Intake extends SubsystemBase {
private final TalonFX motor;
public void requestIntake() {
motor.set(0.8); // Internal control
}
}❌ Bad: External control of hardware
public TalonFX getMotor() {
return motor; // Don't expose hardware!
}2. State Queries
✅ Good: Expose semantic state
public boolean hasPiece() {
return beamBreak.get() < 0.5;
}❌ Bad: Expose raw sensor values
public double getBeamBreakVoltage() {
return beamBreak.get(); // Too low-level
}3. Request Pattern
✅ Good: Request-based control
public void requestScore(boolean request) {
this.requestScore = request;
}❌ Bad: Direct state setting
public void setState(State state) {
this.state = state; // Bypasses validation!
}Subsystem Dependency Graph
Key Points:
- Commands control Swerve and Superstructure
- Superstructure controls sub-subsystems
- Vision provides data to Swerve (odometry)
- No circular dependencies
Next Steps
- Learn about State Machine Design
- Explore individual Subsystem Documentation
- Review Controls Configuration