Vulcan is Crevolution Robotics' 2026 FRC competition robot. Built on a swerve drivetrain with WPILib, CTRE Phoenix 6, PathPlanner, and PhotonVision.
src/main/java/frc/robot/
├── Main.java # Entry point
├── Robot.java # TimedRobot lifecycle
├── RobotContainer.java # Subsystem wiring & button bindings
├── RobotCommands.java # Composed command sequences
├── Telemetry.java # Drivetrain NetworkTable publishing
├── ShiftHelpers.java # 2026 game shift-period logic
├── drivetrain/
│ └── CommandSwerveDrivetrain.java # CTRE swerve wrapper
├── generated/
│ └── TunerConstants.java # Auto-generated swerve config (Tuner X)
├── intake/
│ ├── Intake.java # Slide + roller subsystem
│ ├── IntakeCommands.java # Simple intake command factories
│ ├── IntakeConfig.java # IDs, PID gains, limits
│ ├── CompressIntakeCommand.java # Double-oscillation compression sequence
│ └── HomeIntakeCommand.java # Limit-switch homing
├── shooter/
│ ├── Shooter.java # Flywheel subsystem + SOTM math
│ ├── ShooterCommands.java # Aim-and-shoot / stop factories
│ ├── ShooterConfig.java # IDs, PID gains, distance→RPM table
│ └── ShooterTuningBench.java # Manual RPM tuning command
├── treadmill/
│ ├── Treadmill.java # Belt conveyor subsystem
│ ├── TreadmillCommands.java # State command factories
│ └── TreadmillConfig.java # Motor ID & voltage constants
├── indexer/
│ ├── Indexer.java # Two-stage indexer subsystem
│ ├── IndexerCommands.java # State command factories
│ └── IndexerConfig.java # Motor IDs & voltage constants
├── climber/
│ ├── Climber.java # Hook actuator subsystem
│ ├── ClimberCommands.java # Climb command factory
│ └── ClimberConfig.java # IDs, Motion Magic gains, targets
└── vision/
├── PhotonVisionSystem.java # 4-camera pose estimation
├── VisionCommands.java # Teleop/auton auto-aim commands
├── DriveToPoseCommand.java # PID drive-to-pose command
├── TeleopAxisLockedDrive.java # Climb-approach drive (X-axis locked)
├── ApriltagTarget.java # AprilTag ID → 3D offset mapping
└── LoggableRobotPose.java # Pose3d + timestamp wrapper
Class: CommandSwerveDrivetrain (extends CTRE TunerSwerveDrivetrain)
Config: TunerConstants (auto-generated by Tuner X — do not hand-edit)
Swerve drivetrain using CTRE Phoenix 6. All constants (wheel offsets, encoder IDs, PID gains) live in TunerConstants. The robot runs in field-centric mode by default.
- Max teleop speed: 65% of
kSpeedAt12Volts - Max angular rate: 0.8 rotations/second
- Odometry is fused with PhotonVision AprilTag estimates each loop
Class: Intake
Config: IntakeConfig
Controls two mechanisms:
| Mechanism | Control |
|---|---|
| Slide | Motion Magic (position) |
| Rollers | Voltage open-loop; motor 2 is a configured Follower (opposed) |
Slide States:
| State | Description |
|---|---|
RETRACTED |
Fully retracted home position |
EXTENDED |
Fully deployed position |
CUSTOM |
Arbitrary position (used by CompressIntakeCommand) |
HOMING |
Applies kHomingVolts until limit switch triggers |
Roller States: IDLE, INTAKE, EJECT
The limit switch at home zeroes the encoder every loop it remains pressed, providing drift-free position after homing. Within 0.025 rotations of target, a gravity feedforward holds position instead of running Motion Magic.
CompressIntakeCommand runs a 7-state double-oscillation sequence to seat a game piece:
WAIT → COMPRESS_ONE_THIRD → EXTEND_FIRST → COMPRESS_TWO_THIRDS → EXTEND_SECOND → COMPRESS_FULL → DONE
Class: Treadmill
Config: TreadmillConfig
Belt conveyor that moves game pieces between the intake and indexer. Runs on the CANivore bus.
| State | Voltage |
|---|---|
IDLE |
kTreadmillIdleVolts |
INTAKE |
kTreadmillInVolts |
EJECT |
kTreadmillEjectVolts |
Class: Indexer
Config: IndexerConfig
Two-stage indexer with separate motors for initial staging and final feeding. Uses voltage open-loop control.
| State | Description |
|---|---|
IDLE |
Both motors stopped |
INTAKE_BOTH |
Both stages run forward (used for feeding the shooter) |
QUEUE_TO_FINAL |
Moves piece from initial stage to final stage |
FEED_SHOOTER |
Final stage only — direct feed into flywheel |
EJECT |
Both stages reversed |
Class: Shooter
Config: ShooterConfig
Four-motor 2x2 flywheel: Motor 1 is the leader, Motor 2 follows aligned, Motors 3 and 4 follow opposed. Uses VelocityVoltage closed-loop control (Phoenix 6 Slot 0).
Shooter States:
| State | Behavior |
|---|---|
IDLE |
Flywheels coast to zero |
SHOOTING |
Runs SOTM math and sets RPM from the distance→RPM table |
TUNING |
Uses manually-set RPM from ShooterTuningBench |
HOMING |
Reserved for hood homing (hood hardware not installed) |
Shoot-on-the-Move (SOTM) Kinematics:
Each loop, the shooter:
- Gets the robot's velocity from the drivetrain odometry.
- Applies a 5-loop moving-average filter to smooth velocity noise.
- Converts to field-relative velocity components.
- Looks up time-of-flight for the current static distance.
- Computes a "virtual target" =
rawTarget − (velocity × ToF). - Uses the virtual distance for RPM lookup and the virtual angle as
mTargetHeading.
A non-linear angular correction is also applied at extreme side angles to compensate for hub geometry (cubic curve, max kMaxHeadingOffsetDegrees = 5°).
Zone logic: The shooter automatically selects between a scoring hub target (when the robot is within BLUE/RED_SCORING_*_X) and passing targets (mid-field, high/low selected by robot Y position relative to field center).
Distance → RPM table (kShooterMapData in ShooterConfig):
| Distance (m) | RPM | ToF (s) |
|---|---|---|
| 1.900 | 3022 | 0.850 |
| 2.135 | 3022 | 0.870 |
| 2.282 | 3022 | 0.890 |
| 2.619 | 3339 | 0.940 |
| 2.865 | 3460 | 0.970 |
| 3.076 | 3486 | 1.000 |
| 3.585 | 3655 | 1.090 |
| 3.614 | 3996 | 1.100 |
| 3.850 | 4109 | 1.120 |
| 4.100 | 4395 | 1.150 |
Values are linearly interpolated. Points above/below the table are clamped to the nearest endpoint.
Class: Climber
Config: ClimberConfig
Hook actuator using Motion Magic with Elevator_Static gravity feedforward. Targets are defined as rotations in ClimberConfig and correspond to retracted and climbed positions.
Class: PhotonVisionSystem
Four cameras running PhotonVision for AprilTag-based odometry: FL (Front Left), FR (Front Right), BL (Back Left), BR (Back Right). Camera-to-robot transforms are defined as Transform3d constants in PhotonVisionSystem.
Pose filtering rules:
- Multi-tag solves are always accepted.
- Single-tag solves are accepted only when pose ambiguity
< 0.2and tag distance< 4.0 m.
Each loop, hub heading and distance are recalculated from odometry pose and field geometry — no camera visibility is required for the shooter to function.
Accepted pose estimates are forwarded to RobotContainer via a Consumer<LoggableRobotPose> callback, which feeds them into swerve odometry with addVisionMeasurement.
Other vision classes:
VisionCommands—teleopAlignandautonAutoAimusing tangential velocity feedforwardDriveToPoseCommand— PID drive-to-pose; translates slower when heading error > 90°TeleopAxisLockedDrive— Climb-approach command that locks the X axis while allowing Y strafe, with alliance-aware hard limits
File: RobotContainer.java
The root wiring class. Instantiates all subsystem singletons, registers PathPlanner named commands, builds the autonomous chooser, and binds every button.
Key fields:
MaxSpeed— 65% ofkSpeedAt12VoltsMaxAngularRate— 0.8 rot/sautoChooser—SendableChooser<Command>shown on SmartDashboarddebugField—Field2dfor simulation pose visualization
Climb target poses (used by getClosestClimbTarget()):
| Alliance | Position | Pose |
|---|---|---|
| Blue | HIGH | (1.183, 4.728) facing 0° |
| Blue | LOW | (0.901, 2.756) facing 180° |
| Red | HIGH | (15.653, 5.310) facing 0° |
| Red | LOW | (15.312, 3.335) facing 180° |
File: RobotCommands.java
Static factory class for composed command sequences. All methods return a Command.
| Method | Description |
|---|---|
extendIntake() |
Sets slide to EXTENDED |
retractIntake() |
Sets slide to RETRACTED |
runIntakeRollers() |
Runs rollers at INTAKE voltage; gated to slide position ≥ 2.35 rot |
runRollers() |
runEnd version; only runs if slide state is EXTENDED |
ejectRollers() |
runEnd eject; only runs if slide state is EXTENDED |
extendAndRunIntakeRollers() |
Extends, waits until deployed, then starts rollers |
stopIntakeRollers() |
Sets rollers to IDLE |
toggleIntakeRollers() |
Stateful toggle (on/off per press) |
smartAlign(...) |
Teleop shoot: aims drivetrain + spins up + fires once at setpoint |
autonSmartAlign(...) |
Auton shoot: same as above but stationary |
manualFire() |
runEnd on Indexer INTAKE_BOTH — blind fire |
indexerEject() |
runEnd on Indexer EJECT |
treadmillRun() |
runEnd on Treadmill INTAKE |
treadmillEject() |
runEnd on Treadmill EJECT |
superstructureControl(...) |
Combined prime/fire state machine driven by trigger axes |
alignToClimb(...) |
Retracts intake → drives to alliance-specific climb pose |
performClimb() |
Retracts intake + runs climber in parallel |
teleopClimbSlide(...) |
TeleopAxisLockedDrive to the nearest climb target |
stopAll() |
Stops rollers, treadmill, indexer, and shooter simultaneously |
smartAlign sequence detail:
parallel(
1. drivetrain rotation PID toward getTargetHeading()
+ 0.15 rad/s friction feedforward when not at goal
2. ShooterCommands.aimAndShoot() ← spins up immediately
3. WaitUntil(isAtSetpoint()).withTimeout(1.5s)
.andThen( parallel( treadmillRun(), manualFire() ) )
)
File: ShiftHelpers.java
Utility class for 2026 game shift-period logic. Determines which alliance has the current offensive turn based on match time and shift intervals. Used to adjust autonomous and teleop behavior accordingly.
File: Telemetry.java
Publishes drivetrain state to NetworkTables each loop via telemeterize(SwerveDriveState). Logs:
- Robot pose (struct)
- Module states (speeds, angles)
- Module targets
- Chassis speeds
- Odometry frequency
Registered with drivetrain.registerTelemetry(logger::telemeterize) in RobotContainer.
Both controllers are Xbox controllers (WPILib CommandXboxController).
| Input | Action |
|---|---|
| Left Stick (X/Y) | Field-centric translation |
| Right Stick (X) | Rotation |
| Start | Reset gyro heading (Blue = 0°, Red = 180°) |
| Right Bumper (hold) | smartAlign — auto-aim drivetrain + spin up + fire when ready |
| Right Trigger (hold) | Drive at 70% speed (precision mode) |
| Left Trigger (hold) | Run intake rollers (only while slide is extended) |
| Left Bumper (hold) | Shooter tuning bench (TUNING state) |
| A | Run CompressIntakeCommand (double-oscillation compress sequence) |
| B | Extend intake slide |
| X | Retract intake slide |
| Y | Home intake (drive to limit switch, zero encoder) |
| Input | Action |
|---|---|
| Left Trigger (hold) | Eject intake rollers |
| Right Trigger (hold) | Run intake rollers |
| Left Bumper | Retract intake slide |
| Right Bumper | Extend intake slide |
| A (hold) | Run treadmill (INTAKE direction) |
| B (hold) | Manual fire — runs indexer INTAKE_BOTH (blind fire) |
| X (hold) | Indexer eject |
| Y (hold) | Treadmill eject |
| D-Pad Up | Home intake (limit-switch homing) |
| D-Pad Right | Bump tuning RPM up (+25 RPM per press) |
| D-Pad Left | Bump tuning RPM down (−25 RPM per press) |
Tuning workflow: Hold Driver Left Bumper to activate
ShooterTuningBench. Use Operator D-Pad Right/Left to adjust target RPM in 25 RPM steps while watchingTuningBench/TargetRPMon SmartDashboard. Release the bumper to return to normal SOTM operation.
Autonomous routines are built with PathPlanner and selected via SendableChooser on SmartDashboard (Auto Selector).
Available auto options (all Left-side routines also have mirrored Right-side variants):
| Chooser Name | Description |
|---|---|
L-1114 |
1114-style 3-piece auto |
L-BeesSwoop |
Bees swoop path |
L-BeesSwoopInverted |
Inverted swoop variant |
L-BeesSwoopLong |
Extended swoop |
L-DoubleSwoop |
Two-swoop sequence |
L-DoubleSweep |
Two-sweep sequence |
L-WaitDip |
Wait then dip path |
Named commands registered with PathPlanner:
| Name | Command |
|---|---|
AutoSmartAlign |
RobotCommands.autonSmartAlign(drivetrain) |
IntakeExtend |
Extend intake slide |
IntakeExtendAndRunRollers |
Extend then start rollers |
IntakeRetract |
Retract intake slide |
IntakeRunRollers |
Start rollers (position-gated) |
IntakeStopRollers |
Stop rollers |
| File | Contents |
|---|---|
TunerConstants.java |
Swerve module offsets, CAN IDs, PID gains — regenerate with Tuner X |
IntakeConfig.java |
Slide motor ID, roller motor IDs, gear ratio, PID/MM gains, soft limits, voltages |
ShooterConfig.java |
Motor IDs, velocity PID gains, distance→RPM interpolation table, heading correction constants |
TreadmillConfig.java |
Motor ID, voltage constants, current limit |
IndexerConfig.java |
Motor IDs, voltage constants |
ClimberConfig.java |
Motor ID, Motion Magic gains, target rotation positions |
Adding a new shooter map point:
Edit kShooterMapData in ShooterConfig. Format is {distance_meters, rpm, time_of_flight_seconds}. The third column is optional but required for SOTM correction to be accurate at that distance. Points are automatically interpolated — no code changes needed elsewhere.
Swerve calibration:
Re-run Tuner X and regenerate TunerConstants.java whenever modules are re-zeroed or gearboxes are swapped. Do not hand-edit that file.