Skip to Content
architecturesubsystem-architecture

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:

  1. Swerve - Omnidirectional drivetrain
  2. Superstructure - Main coordinator and state machine
  3. ElevatorPivot - Vertical and angular positioning
  4. Intake - Ground coral intake mechanism
  5. EndEffector - Scoring and game piece manipulation
  6. Indexer - Game piece transfer between intake and end effector
  7. Climber - End-game climbing mechanism
  8. 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 └── ObjectDetection

Coordinated 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 motors

Why 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 effector

Subsystem 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 # Simulation

Example: 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 CommandScheduler
  • periodic2() 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 Position

Coordination Example

Intaking Sequence:

  1. Driver input: Right trigger pressed
  2. Robot.java: Calls superstructure.requestIntake(true)
  3. Superstructure: Transitions to INTAKING_CORAL_PREPARE
  4. Superstructure: Commands subsystems:
    • elevatorPivot.requestPursueSetpoint(lowHeight, intakeAngle)
    • intake.requestIntakeWithSpin()
    • indexer.requestIntake()
    • endeffector.requestIntakeCoral()
  5. Sensors detect piece: endeffector.hasCoral() returns true
  6. 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

Additional Resources