Unit 7 · Lesson 11

Common Swerve Libraries

This entire unit has built swerve from first principles — which means you now understand every layer that pre-built libraries abstract away. That understanding is exactly what you need to choose the right library for your team, integrate it correctly, and fix it when it misbehaves. This lesson maps the landscape of swerve library options and gives you the mental model to navigate it.

By the end of this lesson, you will:

  • Describe the CTRE Phoenix 6 Swerve API (TunerX-generated code) and how it maps to what you've built
  • Describe REV MAXSwerve and its SparkMax-based architecture
  • Explain the community "BaseFalconSwerve" template pattern and when teams use it
  • Use the decision framework to choose between custom code and a pre-built library for a given team context
  • Read unfamiliar swerve library code by mapping its concepts to the architecture you've learned
  • State the 2910 philosophy on code ownership and why we build before we borrow

Why This Lesson Exists

Most FRC teams don't build swerve from scratch. They use a pre-built library — CTRE's generated code, REV's MAXSwerve example, or a community template. This is entirely reasonable: well-maintained libraries are tested, updated, and documented. Using one isn't cheating — it's pragmatic engineering.

The problem is that teams who use a library without understanding what it does can't fix it when something goes wrong. At competition, at 7 AM, when the robot is doing something unexpected, "I don't know what this code does" is the most dangerous state to be in. The eleven lessons before this one were about building that understanding. Now you can look at any swerve library and see through it.

The Library Landscape

🔶 CTRE Phoenix 6 Swerve Official
Generated from Tuner X; TalonFX + CANcoder + Pigeon2
✓ Auto-generated from your hardware config
✓ Runs onboard PID at 1 kHz via Phoenix Pro
✓ Maintained by CTRE — updates each season
✓ Integrates PathPlanner natively
✗ Requires Phoenix Pro license for full performance
✗ Generated code harder to customize
✗ Abstracts away the architecture you've learned
🟢 REV MAXSwerve Official
SparkMax/SparkFlex + Through-Bore Encoder; NEO/Vortex
✓ Clean example code from REV
✓ No license fee
✓ Well-documented hardware kit
✗ SparkMax control runs on roboRIO (not onboard)
✗ CAN latency limits steering responsiveness
✗ Through-Bore Encoder on DIO instead of CAN
🏗️ Custom (This Unit) 2910 Standard
Hand-written from Lessons 2–10; full understanding and ownership
✓ Every line understood by the team
✓ Adapted precisely to your hardware/sensors
✓ Debuggable without external documentation
✗ More initial work to build correctly
✗ Team responsible for updates each season
🌐 Community Templates Community
Team 364 BaseFalconSwerve, FRC 6328 YAGSL, etc.
✓ Battle-tested by multiple teams
✓ Fast to deploy for new teams
✗ Variable maintenance quality
✗ Difficult to customize deeply
✗ Risk: team doesn't understand what it does

CTRE Phoenix 6 Swerve: What's Under the Hood

CTRE's Tuner X can generate a complete swerve drivetrain from your measured hardware constants. The generated code uses a different paradigm than what you've built — instead of ChassisSpeeds objects, it uses SwerveRequest objects. But it maps directly to the same underlying pipeline.

CTRE Phoenix 6 Swerve — SwerveRequest pattern (generated code)
// Generated by Tuner X — TunerConstants.java
public static final TunerConstants.SwerveDrivetrain DriveTrain =
  new TunerConstants.SwerveDrivetrain(
    TunerConstants.DrivetrainConstants,
    TunerConstants.FrontLeft,
    TunerConstants.FrontRight,
    TunerConstants.BackLeft,
    TunerConstants.BackRight
  );

// SwerveRequest objects replace ChassisSpeeds in commands
private final SwerveRequest.FieldCentric m_driveReq =
  new SwerveRequest.FieldCentric()
    .withDeadband(MaxSpeed * 0.1)
    .withRotationalDeadband(MaxAngularRate * 0.1)
    .withDriveRequestType(DriveRequestType.OpenLoopVoltage);

// Command — same driver axis pattern, different output type
private Command driveCommand() {
  return drivetrain.applyRequest(() ->
    m_driveReq
      .withVelocityX(-m_driver.getLeftY() * MaxSpeed)
      .withVelocityY(-m_driver.getLeftX() * MaxSpeed)
      .withRotationalRate(-m_driver.getRightX() * MaxAngularRate));
}

// The field-oriented transform happens INSIDE SwerveRequest
// The robot's heading is read automatically from the Pigeon 2
// Your fromFieldRelativeSpeeds() call from Lesson 9 is implicit here
← click a highlighted token
💡 SwerveRequest = ChassisSpeeds + control mode in one object

CTRE's SwerveRequest pattern combines what you've learned as separate steps: the velocity values (which you computed and passed to drive(ChassisSpeeds)) and the control mode (open-loop voltage vs. velocity closed-loop) are bundled into a single request object. SwerveRequest.FieldCentric handles the fromFieldRelativeSpeeds() rotation internally. If you understand the pipeline from Lessons 3–9, you can read this code fluently.

REV MAXSwerve: SparkMax Architecture

MAXSwerve modules use SparkMax controllers and a REV Through-Bore Encoder on a DIO port for absolute position. The key architectural difference from the CTRE stack: SparkMax PID control runs on the roboRIO over CAN (not onboard the motor controller), so steering latency is slightly higher. For most FRC applications this is immaterial, but understanding it explains some of the tuning differences.

Feature2910 Custom (CTRE)REV MAXSwerve
Drive motorTalonFX (Falcon/Kraken)SparkMax (NEO/Vortex)
Steering motorTalonFXSparkMax (NEO 550)
Absolute encoderCANcoder (CAN bus)Through-Bore (DIO port)
GyroPigeon 2 (CAN bus)ADIS16470 or NavX (SPI)
Steering PID locationOnboard TalonFX (1 kHz)roboRIO via SparkMax (50 Hz)
Offset calibration toolPhoenix Tuner XREV Hardware Client or code
CAN devices per drivetrain12 (8 motors + 4 CANcoders + Pigeon)8 (8 motors, no CAN encoder)

The Decision Framework

Library selection — select your team's situation
What describes your team's programming situation?
← select a situation

Reading Library Code You Didn't Write

Whether you adopt a library or inherit code from a previous programmer, the skill of reading unfamiliar swerve code is essential. The mental model from this unit is your decoder ring. For any swerve codebase:

  1. Find the drive() entry point — every swerve subsystem has one. Look for a method that accepts velocity inputs and routes them to the module hardware. In CTRE's library, it's applyRequest(). In custom code, it's usually drive(ChassisSpeeds). This is your anchor.
  2. Trace the kinematics call — find where toSwerveModuleStates() (or its equivalent) is called. This is the math layer. Confirm it's there and that desaturateWheelSpeeds() is applied after it.
  3. Find the module class — every library has a per-module class or record. Find setDesiredState() (or its equivalent). Confirm optimize() is called, and that there's a near-zero guard.
  4. Find the constants file — gear ratios, wheel diameter, CAN IDs, offsets. Confirm they match your physical hardware. Wrong constants here produce wrong behavior everywhere.
  5. Find the odometry/estimator update — look for update() being called in a periodic method. Confirm the heading is provided with the correct sign convention.
🔍 LRI Perspective: "The library is a starting point, not a finished product"

I've watched teams walk into practice day with generated CTRE swerve code that they deployed and haven't read. When something goes wrong — wrong module driving direction, wrong field orientation, offset drift — they don't know where to look. The generated code is excellent starting code. But "generated" doesn't mean "requires no understanding." You still have to know what the constants mean, which direction is positive, and what each request type does. The teams that dominate late in the season are the teams that understand their code regardless of where it came from.

🔌 System Check — Before Using a Pre-Built Library

Regardless of which library you use:

  • Read the constants file completely. Every constant that maps to physical hardware (gear ratio, wheel diameter, CAN IDs, offsets) must match your specific robot. Generated code uses example values — they must be replaced with measured values.
  • Run the single-module isolation test from Lesson 5 before running all four modules. Command one module to 0°, 90°, 180°. Confirm the steering responds correctly. Command a 0.5 m/s drive forward. Confirm the wheel spins the right direction. Do this for each module before full-system testing.
  • Confirm the field orientation convention. Push joystick forward → robot drives toward the far wall (not toward your feet). If orientation is reversed, find the library's heading sign convention and correct it.

Knowledge Check

1. A teammate downloads the CTRE Tuner X generated swerve code and deploys it without modifying the constants file. The robot drives, but one module consistently points 47° off from the others. What is almost certainly wrong?

  • A The CTRE library has a bug in that specific module's PID.
  • B The generated constants file uses example CANcoder offsets. The actual offset for that module was never measured in Tuner X and entered. The placeholder value (often 0.0) is wrong for the physical hardware.
  • C The gear ratio constant is wrong, causing the module to drive 47% slower.
  • D The CAN ID for that module is conflicting with another device.

2. REV MAXSwerve runs the steering PID on the roboRIO at 50 Hz rather than onboard the SparkMax. What is the practical consequence compared to onboard Phoenix 6 PID at 1 kHz?

  • A The robot cannot achieve field-oriented control at 50 Hz.
  • B The steering correction loop runs 20× slower (50 Hz vs 1 kHz), which means corrections to wheel angle have more latency. For most FRC applications this is imperceptible, but at very high speeds or with noisy sensors it can slightly reduce steering responsiveness and require more conservative PID gains.
  • C The SparkMax cannot execute position control at all — REV only supports velocity mode.
  • D roboRIO PID is inherently more accurate than onboard PID because it has access to more sensors.

3. You're reading CTRE's generated swerve code and find SwerveRequest.FieldCentric being used. You've never seen this class before. Based on what you learned in this unit, what does it do?

  • A It's a new type of kinematics that replaces the WPILib kinematics classes.
  • B It's CTRE's equivalent of calling ChassisSpeeds.fromFieldRelativeSpeeds() and passing the result to drive(). The request bundles the velocity inputs, applies the field-to-robot heading rotation internally using the Pigeon 2, and sends the resulting module states to hardware — all in one object.
  • C It replaces the CANcoder as the steering feedback source.
  • D It enables the robot to follow GPS-based field coordinates automatically.
💪 Practice Prompt

Read and Map an Unfamiliar Swerve Library

  1. Go to CTRE's swerve example or open the Tuner X generated project for your robot. Find the five architectural elements: the drive entry point, the kinematics call, the per-module class, the constants file, and the odometry update. Write down where each lives (file name, approximate line number). This mapping exercise verifies you understand the structure.
  2. In the constants file of the chosen library, find the four CANcoder offset values. Compare them to the values you measured in Lesson 2. If they don't match, update them. Re-deploy and confirm the module 47° problem from Q1 doesn't affect your robot.
  3. Find where the field-to-robot heading rotation is applied in the library you chose. Is it in a SwerveRequest object, in the drive command's execute(), or somewhere else? Write a one-sentence explanation of how it corresponds to the fromFieldRelativeSpeeds() call you wrote in Lesson 9.
  4. Stretch goal: Read the YAGSL (Yet Another Generic Swerve Library) documentation at yagsl.gitbook.io. YAGSL supports both CTRE and REV hardware through a JSON configuration system. Identify how it handles the five architectural elements above. Compare its approach to both the custom code from this unit and CTRE's generated code. Which would you choose for a new team and why?