Swerve Hardware Overview
Before the first line of code is deployed to a swerve robot, twelve CAN devices need to be configured, calibrated, and verified. This lesson covers everything that happens in Phoenix Tuner X and Constants.java before you touch the SwerveModule class — the work that determines whether the robot drives correctly or drives sideways.
By the end of this lesson, you will:
- Plan a 13-device CAN bus with unique, organized IDs and document it in
Constants.java - Explain what a CANcoder offset is, why it's required, and how to measure it in Phoenix Tuner X
- Configure a
CANcoderConfigurationin code with the correct magnet offset - Initialize a
TalonFXdrive motor and steering motor with the correct gear ratio, neutral mode, and current limits for swerve - Initialize a
Pigeon2gyroscope and verify heading output - Convert encoder ticks to meters per second using the drive gear ratio and wheel circumference
CAN Bus Planning: Twelve Devices, One Network
The CAN bus is a single serial network. Every device — motor controllers, encoders, gyros — shares it. Each device must have a unique integer ID from 0 to 62. At 12 swerve devices plus a PDH and any mechanism motors, you can easily have 20+ devices on one bus. Collisions and conflicts become likely unless IDs are planned before the first wire is crimped.
The 2910 convention groups devices by type, not by module. This makes debugging easier: if motor IDs 1–4 are all drive motors, a missing ID in the 1–4 range instantly points to a drive motor problem. Click any cell in the map below to see the device it represents.
CANcoder Offset Calibration: The Most Critical Step
The CANcoder reports absolute wheel angle. But "absolute" is relative — the sensor doesn't know which direction is "straight forward" for your robot. That definition is called the magnet offset, and you set it once in Phoenix Tuner X during initial hardware bringup.
If the offset is wrong, the steering PID will command the wheels to what it thinks is "straight" — but the wheels will actually point at a random angle. The robot will drive sideways, spin, or refuse to go straight, and no amount of code debugging will fix it. This is a hardware calibration problem, not a code problem.
double constant in your Constants.Swerve class, in rotations (Phoenix 6 uses rotations as its native unit). Example: kFrontLeftOffset = -0.413. Negate the raw reading — Phoenix 6's MagnetSensorSettings.withMagnetOffset() takes a value that cancels the offset, so the negation is intentional.getAbsolutePosition() to SmartDashboard and enable the robot. All four values should read approximately 0.0 with the wheels in their straight-forward position. If any module reads a non-zero value, re-measure that module's offset in Tuner X and update the constant.The Constants File: Your Hardware Contract
All gear ratios, offsets, wheel dimensions, and CAN IDs live in Constants.java. This file is the single source of truth for your drivetrain's physical properties. Click the highlighted tokens in the code below to understand each constant's physical meaning.
public static final class Swerve {
// ── Module physical geometry ──────────────────
public static final double kTrackWidthMeters = 0.546; // L-R wheel spacing
public static final double kWheelBaseMeters = 0.546; // F-B wheel spacing
public static final double kWheelDiameterMeters= 0.1016;// 4 inches = 0.1016 m
public static final double kWheelCircumference =
kWheelDiameterMeters * Math.PI;
// ── Drive motor gear ratio ────────────────────
// MK4i L2 = 6.75:1 (motor rotations per wheel rotation)
public static final double kDriveGearRatio = 6.75;
public static final double kSteerGearRatio = 150.0 / 7.0; // ≈21.43:1
// ── Velocity conversion ───────────────────────
// rotations/sec → meters/sec
public static final double kDriveConversionFactor =
kWheelCircumference / kDriveGearRatio;
// ── CAN IDs ───────────────────────────────────
public static final int kFLDrive=1, kFRDrive=2, kBLDrive=3, kBRDrive=4;
public static final int kFLSteer=11,kFRSteer=12,kBLSteer=13,kBRSteer=14;
public static final int kFLEncoder=21,kFREncoder=22,kBLEncoder=23,kBREncoder=24;
public static final int kPigeonId=30;
// ── CANcoder offsets (negated Tuner X readings)
public static final double kFLOffset = -0.413;
public static final double kFROffset = -0.278;
public static final double kBLOffset = 0.135;
public static final double kBROffset = -0.091;
// ── Drive limits ──────────────────────────────
public static final double kMaxSpeedMetersPerSecond = 4.5;
public static final double kMaxAngularRadPerSecond = 2 * Math.PI;
public static final double kDriveCurrentLimitAmps = 40;
public static final double kSteerCurrentLimitAmps = 20;
}
}
Phoenix 6 Device Configuration
Phoenix 6 uses a configuration object pattern: you build a config object, set all its properties, then apply it to the device with device.getConfigurator().apply(config). This is done once in the constructor. The key configurations for swerve are covered below.
CANcoderConfiguration ccConfig = new CANcoderConfiguration();
ccConfig.MagnetSensor
.withMagnetOffset(magnetOffsetRotations)
.withAbsoluteSensorDiscontinuityPoint(0.5)
.withSensorDirection(SensorDirectionValue.CounterClockwise_Positive);
m_cancoder.getConfigurator().apply(ccConfig);
// ── Drive motor configuration ─────────────────────
TalonFXConfiguration driveConfig = new TalonFXConfiguration();
driveConfig.MotorOutput
.withNeutralMode(NeutralModeValue.Brake);
driveConfig.CurrentLimits
.withStatorCurrentLimit(Constants.Swerve.kDriveCurrentLimitAmps)
.withStatorCurrentLimitEnable(true);
driveConfig.Feedback
.withSensorToMechanismRatio(Constants.Swerve.kDriveGearRatio);
m_driveMotor.getConfigurator().apply(driveConfig);
// ── Steering motor configuration ──────────────────
TalonFXConfiguration steerConfig = new TalonFXConfiguration();
steerConfig.MotorOutput
.withNeutralMode(NeutralModeValue.Brake);
steerConfig.CurrentLimits
.withStatorCurrentLimit(Constants.Swerve.kSteerCurrentLimitAmps)
.withStatorCurrentLimitEnable(true);
steerConfig.Feedback
.withSensorToMechanismRatio(Constants.Swerve.kSteerGearRatio)
.withRemoteCANcoder(m_cancoder);
m_steerMotor.getConfigurator().apply(steerConfig);
When I inspect a swerve robot and the code deploys but the wheels don't track straight, the first thing I ask is: "Did you verify your CANcoder offsets after the last time someone manually rotated a module?" A single accidental bump of a module during transport or pit setup changes the wheel angle but leaves the encoder reading the same. The offset is now wrong. This is why 2910's pre-match protocol always includes a Shuffleboard check: all four CANcoder absolute positions should read near 0.0 when the wheels are straight. That check takes 15 seconds and has prevented dozens of match-day failures.
Gear Ratio Conversions: From Ticks to Reality
The TalonFX reports position in rotations of the motor shaft. To get wheel rotations, divide by the gear ratio. To get distance traveled, multiply by wheel circumference. Phoenix 6's withSensorToMechanismRatio(gearRatio) does this conversion automatically — after applying it, getPosition().getValueAsDouble() returns wheel rotations directly.
| What you want | Formula | Phoenix 6 method |
|---|---|---|
| Wheel rotations from motor rotations | motorRot ÷ gearRatio | Automatic after withSensorToMechanismRatio() |
| Wheel distance (meters) | wheelRot × circumference | Multiply getPosition() by kWheelCircumference |
| Wheel velocity (m/s) | motorRPS ÷ gearRatio × circumference | Multiply getVelocity() by kWheelCircumference |
| Steering angle (rotations) | Direct CANcoder reading | m_cancoder.getAbsolutePosition().getValueAsDouble() |
Before running any swerve code, complete in this exact order:
- Open Tuner X. Confirm all 13 expected devices appear by name and CAN ID. No yellow warnings, no "No Firmware" entries.
- For each CANcoder: read Absolute Position with wheels pointing forward. Record the values. These become your offset constants (negated) in Constants.java.
- For each TalonFX: run a self-test snapshot. Confirm no fault flags. Check firmware versions match your Phoenix 6 vendordep version.
- Verify the Pigeon 2 by rotating the robot by hand on blocks. Confirm heading changes appropriately in SmartDashboard (clockwise should increase heading if using standard WPILib convention with
Rotation2d.fromDegrees(-pigeon.getYaw())). - Deploy your Constants file and confirm SmartDashboard shows all four CANcoder absolute positions near 0.0 with wheels in the forward position.
Knowledge Check
1. You deploy your swerve code for the first time. All wheels spin but the robot drives sideways when you push forward. The most likely cause is:
2. Your SDS MK4i module uses an L2 gear ratio (6.75:1). The TalonFX reports a velocity of 30 rotations/second. What is the wheel's velocity in meters/second? (Wheel diameter = 4 inches = 0.1016 m.)
3. Why is NeutralModeValue.Brake required for steering motors on a swerve drive, while it is a judgment call for drive motors?
Configure Your Hardware Before Touching SwerveModule
- Complete your
Constants.Swerveclass with all values from the Lesson 1 practice prompt plus the four CANcoder offsets measured in Tuner X. Confirm your gear ratio and wheel diameter match your physical module spec sheet (SDS documentation is at swervedrivespecialties.com). - Using the formula
kWheelCircumference / kDriveGearRatio, compute thekDriveConversionFactorfor your module. Add a comment showing the calculation explicitly — this number will appear in five places in your code and must be correct. - Open Tuner X and perform a self-test snapshot on each TalonFX drive and steering motor. Screenshot or record any fault flags. Document which motors you expect to be inverted (on SDS MK4i: drive motors on the left side typically need inversion to produce positive-forward output). Add these as boolean constants:
kFLDriveInverted = true, etc. - Stretch goal: Write a short autonomous test command (or just code in teleopInit for testing) that does nothing except publish the absolute position of all four CANcoders to SmartDashboard. Deploy it and manually turn each wheel by hand. Confirm the readings change correctly and that zero-position readings are near your stored offsets. This verifies your CAN bus, your IDs, and your encoder communication in one step.