Swerve Project Generator
Tuner X measures your drivetrain and writes one file. The rest of the swerve project is already written, so you swap that file in and drive. Have the robot assembled and up on blocks.
- An assembled drivetrain: eight TalonFX, four CANcoders, one Pigeon 2, one CANivore.
- Phoenix Tuner X connected, with firmware current on every device.
- Module anatomy and field-centric driving from How Swerve Works.
- A tape measure, and the robot up on blocks.
Start from the workshop project
Download the swerve project below. It is the code on this page, and steps 5 and 6 are the only two edits it needs.
Download Swerve Project (v3.0, Commands v3)What the generator writes
The generator can write a whole project. Only one file comes back with you: src/main/java/frc/robot/generated/TunerConstants.java.
It has thirteen device IDs, kDriveGearRatio, kSteerGearRatio and kWheelRadius. Per module it holds an X and Y offset from the robot's center, which is what kinematics runs on. There is a CANcoder offset per module as well, measured with that wheel straight. steerGains, driveGains, kSlipCurrent and kSpeedAt12Volts are estimates you replace over the next two lessons.
The copy checked into the workshop project belongs to somebody else's robot. It is square, with modules 10 inches out, 7.36:1 on the drive, and fake IDs. Deploy it unchanged and the code looks for motors that are not on your bus.
Six steps, in order
- Put every device on one CAN bus with a unique ID, and write the IDs down corner by corner. You want thirteen listed, no duplicate-ID warning, no red firmware badge. A missing device is a wiring fault.
- Open the swerve project generator, under Mechanisms, behind a New Project button. It opens by asking for dimensions rather than for code.
- Measure the robot and enter four numbers.
- Wheelbase, labeled FL to BL: front-to-back distance between module centers.
- Trackwidth, labeled FL to FR: side-to-side distance between module centers.
- Wheel radius: half the wheel's diameter, measured on a wheel already driven on. The field asks for radius, not diameter.
- Drive gear ratio: motor rotations per wheel rotation, off the module's spec sheet.
Take them off the real robot, not the CAD you meant to build. Wheelbase and trackwidth are the pair people swap, and on a square robot that stays hidden until it turns.
- With the robot on blocks, the wizard drives the modules one at a time. Hold each wheel straight when it asks: that measurement becomes the corner's CANcoder offset. Half a degree of error per module walks the robot sideways over a long drive. If the corner moving is not the one the wizard named, two CAN IDs are swapped.
- Generate the constants and replace
src/main/java/frc/robot/generated/TunerConstants.javawith the file it writes. Replace the whole file, since offsets, inversions and IDs all come from the same run. You should see your own CAN IDs and akCANBusline naming your CANivore. - Set your team number in
.wpilib/wpilib_preferences.json, which ships as5712, and deploy to SystemCore. The driver station lists Teleop as a selectable mode. Modules point straight when you enable, and nothing spins on its own.
Full simulation
Hardware Simulation ran your code on the laptop against real motors on the bench. This project also runs with no hardware at all. Phoenix 6 simulates every TalonFX, CANcoder and the Pigeon, and CommandSwerveDrivetrain runs a physics update every 4 ms whenever it finds itself in simulation. The wheels turn, odometry counts them, and the pose moves.
- Choose Simulate Robot Code from the same toolbar menu, the line above Hardware Sim Robot Code.
- In the simulation window, drag your controller onto Joystick[0], pick Teleoperated, and select the Teleop OpMode.
- Open AdvantageScope, connect it to the simulator, and drag
Drivetrain/Poseonto a 2D field view.
You should see: the robot drawn on the field, moving when you push the stick and stopping when you let go. The example constants work here unchanged, because nothing goes looking for real devices. Every later swerve page that says "in the simulator" means this.
Three files, one drivetrain
The drivetrain is three classes. TunerConstants.java is Tuner X's, from your robot: every number about it, hand-edited only when calibration says to. CommandSwerveDrivetrain.java is Tuner X's too, lightly edited. It holds the motors, odometry, the simulation thread, and forward matched to your alliance color.
DriveMechanism.java is the workshop's, written by hand. It implements Mechanism, owns a CommandSwerveDrivetrain as a private field, and is what an OpMode talks to. That keeps the code you write out of the files Tuner X writes. It also narrows the drivetrain's hundreds of methods down to the few the rest of the robot needs.
Both applyRequest(...) and seedFieldCentric() return commands, and setControl(...) sends one request straight through. Two more read the drivetrain back: getPose() and getFieldVelocity(). The camera comes in later, through addVisionMeasurement(...).
The drivetrain default command
A Mechanism has no default command until you give it one, and a canceled command leaves its last request in the motors. The arm gets away with that, because its last request is a position and it holds there. A drivetrain's last request is a speed.
So teleop gives the drivetrain a real default, the only setDefaultCommand call in the workshop code. It sits in TeleopOpMode rather than Robot because it needs that mode's controller.
public TeleopOpMode(Robot robot) { final DriveMechanism drivetrain = robot.drivetrain; // In WPILib, X points forward and Y points left. The sticks read the other way around, so // each axis below gets a minus sign. drivetrain.setDefaultCommand( drivetrain.applyRequest( () -> drive .withVelocityX(-driver.getLeftY() * maxSpeed) // left stick up = forward .withVelocityY(-driver.getLeftX() * maxSpeed) // left stick left = left .withRotationalRate( -driver.getRightX() * maxAngularRate))); // right stick left = turn left // Left bumper: make the robot's current facing the new "forward". driver.leftBumper().onTrue(drivetrain.seedFieldCentric()); }applyRequest(...) is built on runRepeatedly, so it re-reads the sticks and sends a fresh request every loop. Let go and the command is still running, asking for zero. Full stick asks for maxSpeed, which comes straight out of TunerConstants.kSpeedAt12Volts.
A default command has to require its own mechanism and no other. Break that and the scheduler throws an IllegalArgumentException at runtime, not a compile error.
The left bumper runs seedFieldCentric(): whatever way the robot faces becomes forward for the sticks. It never says where the robot is on the field. Swerve Calibration sets it beside resetPose(Pose2d), the call that does.
Check your work
Robot on blocks for the first two checks, then on the floor with room around it. Keep a hand on disable.
- Select Teleop and enable, hands off the controller. All four wheels stay still and each module holds the angle it had. Creeping means an off-center stick or a wrong deadband.
- Push the left stick forward: four modules pointing the same way, four wheels turning the same direction. A wheel turning backwards is an inversion; a module facing sideways is a CANcoder offset. Push the right stick sideways and the modules splay into a rotation pattern.
- On the floor, point the robot away from you and push forward. It drives away in a straight line. Turn it 90 degrees in place, push forward again, and it goes the same direction across the floor. Press the left bumper and forward becomes whatever way it is facing now.
- Let go of everything. The robot stops and stays stopped, because its default command is asking for zero.
Three ways this goes wrong
- A device the driver station cannot find. You deployed with the example file, or yours landed outside
src/main/java/frc/robot/generated/. ThekCANBusname has to match your CANivore. - One module fighting the other three. Its inversion or its offset is wrong. Re-run the module test for that corner rather than editing the file: the two are measured together.
- Nothing moves and no error appears. Check that Teleop is selected and the robot enabled. If both are right, the
setDefaultCommandline is missing, and a drivetrain nobody commands looks exactly like broken wiring.
Swerve Calibration is next. It replaces the generator's estimates with numbers measured off your robot.
Check yourself
You finish the Tuner X wizard. Which file do you take back to the workshop project?
The wizard says it is testing the front-left module, and the back-right wheel turns. What is wrong?
You want a command that drives the robot in a square. Which file does it talk to?
In teleop you let go of every stick. Why does the robot stop?
You press the left bumper, which runs seedFieldCentric(). What changed?
One module drives the wrong way while the other three go straight. What do you do?