TurretSubsystem), dual flywheels (Flywheels), and an adjustable hood (Hood), all coordinated by LauncherSubsystem. A singleton LaunchCalculator continuously solves for the correct hood angle, flywheel RPS, and turret angle using a Newton-Raphson time-of-flight iteration with pose-differentiated defense compensation.
Architecture
LauncherSubsystem public API
launcherAimCommand() internals
isAtTarget() gates
All five conditions must be simultaneously true:
flywheelAtTarget— withinFLYWHEEL_TOLERANCE(10 RPS) of goal; tolerance widens to 30 RPS beyond 6 mhoodAtTarget— within 0.23 motor rotations of goal, and driver station is enabledturretAtTarget— within dynamic tolerance clamped between 4° and 20°, derived fromatan(0.3 / distToHub)notUnderClimb—LaunchCalculator.isUnderClimb()returns falsenotGoingToBeUnderTrench—LaunchCalculator.isApproachingTrench()returns false
All five gate booleans are published to NetworkTables for debugging:
/AutoAim/hoodGoal— current hood setpoint (motor rotations)/AutoAim/flywheelGoal— current flywheel setpoint (RPS)/AutoAim/hoodAtTarget— hood within tolerance/AutoAim/turretAtTarget— turret within tolerance/AutoAim/flywheelAtTarget— flywheels within tolerance/AutoAim/NotUnderClimb— climb safety gate/AutoAim/NotUnderTrench— trench safety gate
TurretSubsystem
A single TalonFX (CAN IDTURRET_MOTOR_ID = 23) through a gear reduction, with a hall-effect sensor (analog input HALL_EFFECT_SENSOR_ID = 0) for homing.
Constants
Public API
rotateToTargetWithCalc() — shoot-on-the-move
The default turret command solves for the best reachable target degree within soft limits, normalizing across ±360° candidates:
-FFV) cancels the turret angular velocity required to track a moving robot.
Flywheels
Two TalonFX motors (CAN IDsFLYWHEEL_ONE_ID = 20, FLYWHEEL_TWO_ID = 21) running VelocityTorqueCurrentFOC.
Key methods
Hood
A single TalonFX (CAN IDHOOD_MOTOR_ID = 22) with PositionVoltage control. Homing is performed by driving the hood at AUTO_ZERO_VOLTAGE (-1.5 V) to the hard stop, then zeroing the encoder — no hall-effect sensor is used for the hood.
Key methods
LaunchCalculator
A singleton (LaunchCalculator.getInstance()) that computes LaunchingParameters every loop, with aggressive caching to avoid re-solving when the robot has not moved significantly.
LaunchingParameters record
Caching / throttling
The cache is preserved (and returned immediately) if all of the following are true:- Pose has not moved more than 1 inch
- Heading has not changed more than 0.5°
- Chassis speed is below 0.5 in/s on both axes and 0.5°/s rotationally
- Turret angular velocity change is below 0.05 rad/s
- Filtered linear acceleration is below 0.3 m/s²
- Filtered angular acceleration is below 0.5 rad/s²
Moving-shot solver
1
Defense/push compensation
Pose-differentiated velocity (Lie-algebra
Twist2d log) is blended with wheel speeds. The blend weight scales from POSE_VELOCITY_BLEND = 0.9 at MIN_POSE_DT = 5 ms down to 0 at MAX_POSE_DT = 50 ms. When the robot is stationary (wheel speed < 0.05 m/s) pose-diff is disabled entirely to suppress estimator noise.2
Phase delay prediction
Robot state is advanced by
PHASE_DELAY = 20 ms using second-order kinematics: x = x₀ + v·t + ½a·t². The velocity at the end of the phase delay (launchSpeeds) is treated as constant for all subsequent calculations — post-launch acceleration does not affect the ball.3
Newton-Raphson TOF convergence
Up to 5 iterations of Newton’s method converge on the true time-of-flight accounting for robot velocity. Each iteration adjusts the distance estimate using:Convergence is declared when
|t_new - t_prev| < 0.001 s.4
Lookup table interpolation
LauncherConstants holds an InterpolatingDoubleTreeMap for flywheel RPS, hood angle, and time-of-flight vs. distance, populated from compDistanceData (12 points from 1 m to 8 m).Safety gates
isApproachingTrench(Pose2d, ChassisSpeeds) samples 10 lookahead poses over 0.5 seconds at the current velocity. If any sample places the turret within ±12 in (X) × ±24.97 in (Y) of a trench AprilTag (IDs 1, 6, 7, 12, 17, 22, 23, 28), the hood is forced to 0 and isAtTarget() returns false.
isUnderClimb(Pose2d turretPose) checks whether the turret is within the climb structure envelope (±23.5 in X × ±11.38 in Y of tags 15 or 31). If true, isAtTarget() returns false.