Skip to content

Architecture

The code is organized around one rule: commands depend on robot behavior, not vendor hardware. That rule is what lets the same command work on the real robot, in sim, and during replay.

Composition root

RobotContainer is the composition root. It creates subsystems, selects real/sim/replay IO implementations, binds driver controls, and exposes the selected autonomous command.

Keep construction decisions there. If a subsystem needs different IO in real mode and sim mode, choose it in RobotContainer or in a small factory called by RobotContainer.

Subsystems own behavior

A subsystem should expose methods in the language of the robot:

public void setIntakeVoltage(double volts)
public boolean hasGamePiece()
public Command intakeUntilDetected()

Avoid exposing methods in the language of a device:

public TalonFX getMotor()
public StatusSignal<Angle> getRotorPosition()

Device objects belong behind IO boundaries. Commands should not know which motor controller, camera, or sensor is installed.

IO owns hardware details

The drivetrain shows the intended pattern:

  • Drive owns behavior and odometry fusion.
  • Module owns one swerve module abstraction.
  • ModuleIO describes inputs and outputs.
  • ModuleIOTalonFX talks to real CTRE hardware.
  • ModuleIOSim simulates behavior.
  • Empty IO implementations make replay safe.

Use the same shape for mechanisms when hardware behavior is non-trivial. For a very simple prototype, a direct subsystem may be acceptable temporarily, but the production version should still have a replay-safe design.

Commands are small

Commands should coordinate behavior. They should not contain vendor configuration, raw CAN logic, camera parsing, or drivetrain kinematics.

Good commands:

  • read operator intent
  • call subsystem methods
  • compose other commands
  • apply timeouts
  • express finish conditions

Bad commands:

  • construct motor controllers
  • parse NetworkTables camera data
  • configure PID slots
  • directly reset module encoders
  • silently change navigation policy based on vision health

Drivetrain public surface

Commands use Drive for:

  • current pose
  • robot-relative speeds
  • field-relative teleop driving
  • robot-relative driving
  • pose reset
  • module orientation and X-lock
  • characterization commands
  • timestamped vision measurement ingestion

That API is intentionally narrow. If a command needs something outside it, first ask whether the command is reaching below the correct abstraction.

Autonomous and teleop navigation split

Autonomous and teleop navigation use different tools:

  • BLine loads committed JSON paths for autonomous.
  • PathPlanner performs runtime pathfindToPose for teleop navigation.

Do not use PathPlanner-authored autos in this template. Do not use BLine for teleop navigation. Keeping the split makes failures easier to diagnose and keeps the autonomous workflow stable.

Logging is part of the architecture

AdvantageKit is not an add-on dashboard helper. It is the record of what the robot thought happened.

Use:

  • Logger.processInputs(...) for IO input structs.
  • Logger.recordOutput(...) for derived values, command state, selected auto, navigation state, and dashboard aliases.
  • LoggedDashboardChooser for autonomous selection.

Do not create direct NetworkTables publishers for normal telemetry. The configured AdvantageKit NT4 publisher exposes logged outputs to dashboards while preserving replay and log consistency.