Swerve Drive Tuning
Three measurements on carpet replace the drive numbers the generator guessed: wheel radius, top speed, and the current where a tire lets go. Then the drive motors get a real velocity loop.
- Modules zeroed and steer gains tuned, from Swerve Calibration.
- AdvantageScope connected to the robot. Two of the measurements are read off a live plot.
- Phoenix Tuner X, and six meters of clear carpet.
- A tape measure, and a wall you may push against.
Wheel radius
Measure the radius and the top speed while the drive request is still open-loop voltage: kSpeedAt12Volts means the speed at 12 volts applied.
Odometry counts wheel rotations and multiplies by a radius. You want the effective one: the wheel squashed under the robot's weight and sunk into carpet. It shrinks as the tread wears, so repeat this late in the season.
- Tape the floor at the robot's front edge. Restart the code so
Drivetrain/Posereads (0, 0). - Drive straight forward, slowly, about five meters. A wheel that spins under hard acceleration counts distance the robot never travels.
- Tape the front edge again. The gap between marks is the actual distance, and
Drivetrain/Poseat the end of the run is the reported one.
Run it three times in each direction and average. Put the result in kWheelRadius, redeploy, and repeat until tape and odometry agree.
If it got worse, you inverted the ratio: a robot that over-reports needs a smaller radius.
Top speed and slip current
Do the radius first, because this speed is wheel rotations times that radius. Find six meters of clear floor on the surface you compete on. Carpet and a shop floor give different answers.
- Full stick forward. Hold until the speed stops climbing, then let go well before the wall.
- Plot
Drivetrain/TranslationSpeedMpsand read the plateau, not the spike.
That number goes in kSpeedAt12Volts. It measures the robot. The driver cap is maxSpeed in TeleopOpMode, which reads it straight back. Expect a plateau near the 4.54 the file shipped with. Wildly off means the file: check the radius, then check kDriveGearRatio against your modules.
Stator current is proportional to torque, so a stator limit caps how hard a wheel twists. Set it where the tire loses grip. Torque above that point polishes carpet.
- Drive the robot against a wall on carpet and square it up so all four wheels point into the wall.
- In Tuner X, open one drive motor on Voltage Out and plot its velocity and its stator current.
- Ramp the voltage up slowly from zero, watching both traces.
Current climbs while velocity sits at zero. Then velocity jumps and current drops in the same instant. That is the tire letting go. Read the current at the top of the climb, just before the drop.
Stalled motors get hot fast
A drive motor pushing a wall it cannot move is a stalled motor. Keep each ramp to a couple of seconds, back off to zero between attempts, and give the motor a minute. One person on the disable, somebody else on the laptop.
That number goes in kSlipCurrent. You may be measuring the limit, not the tire. The shipped 120 A is itself a stator limit, and driveInitialConfigs sets 70 A on the supply. If the trace flattens at 120 A and the wheel never breaks loose, raise the constant temporarily and ramp again. Then redeploy and floor it from a dead stop. The robot should launch without the squeal and sideways hop of wheel spin.
Close the drive loop
A drive motor holds a speed. On a velocity loop the feedforward does nearly all of the work. The order is kV, then kS, then kP. The file starts you at kP 0.2 and kV 0.124.
Tune it on the ground, not on blocks. A wheel in the air carries no load, and gains found there will not hold a speed under a robot. Plot the same two signals as the steer gains, speed this time. A constant gap is kV. A gap only at low speed is kS. A slow recovery after a change of direction is kP.
Switch the drive request
Up to here the stick position went straight to volts. In TeleopOpMode.java, change DriveRequestType.OpenLoopVoltage to DriveRequestType.Velocity. It comes from the same class, so the import already covers it.
No branch makes this edit for you. 1-Swerve, 2-Logging and the project the Tuner X generator writes all ship open loop. Make it last. If the robot drives worse than it did on volts, go back to the gains.
The deadband
The same request line sets withDeadband(maxSpeed * 0.1) and withRotationalDeadband(maxAngularRate * 0.1), throwing away the bottom 10% of both sticks. At 4.54 m/s that is everything under about 0.45 m/s, which open-loop driving could not hold anyway. A tuned velocity loop can, so the deadband now discards control you paid for. Shrink it rather than deleting it: the deadband keeps a worn stick's drift from creeping the robot across the field.
- Put the robot on blocks. Enable, and take your hands off the controller.
- Watch the speed component of
Drivetrain/ModuleTargets. It should be flat zero. - Halve the deadband, redeploy, repeat. When the targets start twitching, go back one value.
Set it per controller, for the worst one you will compete with.
Check your work
One run tells you whether the page landed, on the surface you compete on. Tape a start mark, restart the code so Drivetrain/Pose reads (0, 0), and drive a square: three meters forward, three left, three back, three right.
You should see
The robot back on the tape, and Drivetrain/Pose near (0, 0) after twelve meters. In AdvantageScope, measured module traces sitting on the commanded ones through all four corners. Hands off the sticks, the speed component of Drivetrain/ModuleTargets flat at zero.
- Module zeros
- A module a degree off steers the robot sideways the whole way, and no radius correction fixes a heading error. Go back to Swerve Calibration and re-save the zeros.
- Drive gains
Velocitywith untuned gains chases a speed the motor cannot hold. Sluggish or surging is kV. A hum at constant speed is kP too high.- Nothing is broken
- Odometry measures from where the code started, not from a point on the field. Nothing here sets one. Vision fixes it in Workshop 6.
Check yourself
TeleopOpMode.java on 1-Swerve: what does the drive request read before you edit it?
Tape says 4.80 m, odometry says 5.00 m, and kWheelRadius is 2.167 in. What goes in the file?
Why does the wheel radius get measured before top speed?
Current climbs while velocity sits at zero, then velocity jumps and current drops. What goes in kSlipCurrent?
With the drive loop closed, why shrink the 10% deadband instead of deleting it?