Drivebase Subsystem Readme
Drivebase subsystem
Overview
The drivebase is the robot's drivetrain — the set of wheels and motors that move the robot around the field. This robot uses a swerve drive, which means each wheel module can independently steer (rotate) and drive (spin). That gives the robot holonomic motion: it can slide sideways, drive diagonally, or spin in place, all at the same time.
The drivebase subsystem owns odometry, provides field-centric driving for teleop, and exposes pose-targeting helpers for autonomous commands.
How it works
Field-centric control
When a driver pushes the joystick forward, they expect the robot to move toward the far end of the field — not "forward" relative to whatever direction the robot happens to be facing. This is field-centric control.
The drivebase reads the robot's heading from its gyroscope and rotates the joystick input so the chassis speeds are always relative to the field. A small deadband is applied to joystick axes to filter out stick drift.
Swerve modules and YAGSL
Each swerve module has a drive motor (spins
the wheel) and a steer motor (rotates the module). The low-level control loops
that coordinate all four modules are handled by YAGSL
(Yet Another Generic Swerve Library). YAGSL reads module configuration files
from src/main/deploy/swerve/ and manages kinematics, motor
PID loops, and encoder
feedback internally.
The drivebase subsystem talks to YAGSL through the DriveBaseIO interface,
which follows the AdvantageKit IO pattern.
DriveBaseIOYagsl pulls pose, gyro, and module states from the active
SwerveDrive object for logging and telemetry.
Odometry
Odometry combines wheel
encoder readings with
gyroscope data to estimate the robot's
pose on the field. YAGSL's internal
SwerveDrivePoseEstimator runs the
Kalman filter that fuses odometry with vision
corrections forwarded by the robot state subsystem.
The drivebase exposes getFusedPose() for drivebase-internal use (commands,
AutoBuilder, heading PID). External subsystems should use
RobotPoseSubsystem.getEstimatedPose() for consistency.
addVisionMeasurement() lets the robot pose subsystem inject camera-based
corrections into YAGSL's pose estimator.
Autonomous path following
For autonomous mode, the drivebase integrates with PathPlanner through
PathPlannerCommandFactory. PathPlanner generates trajectories from deployed
.path files and feeds holonomic chassis speed
commands back into the drivebase. The drivebase's
PID-based pose-targeting helpers (translation PID and
heading PID) keep the robot tracking the planned path.
Configuration
Deploy files
Module geometry, motor types, and low-level PIDF gains live in the swerve deploy
folder at src/main/deploy/swerve/. These files are consumed directly by YAGSL
and include controller properties, module definitions, and PIDF configurations.
Subsystem tunables
Higher-level settings live in subsystems.json under driveBaseSubsystem. All
values are tunable through SmartDashboard.
| Setting | Units | Purpose |
|---|---|---|
maximumSpeedMetersPerSecond |
m/s | Top translational speed |
maximumAngularSpeedRadiansPerSecond |
rad/s | Top rotational speed |
translationPidKp, Ki, Kd |
unitless | PID for autonomous translation tracking |
rotationPidKp, Ki, Kd |
unitless | PID for autonomous heading tracking |
translationToleranceMeters |
meters | Position tolerance for pose targeting |
rotationToleranceRadians |
radians | Heading tolerance for pose targeting |
headingCorrectionEnabled |
boolean | Toggles YAGSL passive heading drift correction |
joystickTranslationScale |
0.0–1.0 | Scales joystick sensitivity |
Heading hold PID gains (p, i, d) are configured
in controllerproperties.json inside the swerve deploy folder (e.g.,
src/main/deploy/swerve/controllerproperties.json). These gains are shared
between the explicit heading hold controller and YAGSL's passive heading
correction feature, giving a single source of truth for heading tuning.
Calibration and tuning guide
This section walks through every calibration step needed to get smooth, accurate swerve behavior. Work through them in order — each step builds on the one before it. Keep the robot on blocks (wheels off the ground) for Steps 1–6 so you can spin modules safely.
Prerequisites
Before you start, make sure you have:
- The robot elevated on blocks so all four wheels spin freely.
- A laptop connected to the robot via USB or radio.
- Driver Station open and the robot enabled in teleop.
- AdvantageScope connected for telemetry.
- REV Hardware Client or Phoenix Tuner available to verify CAN device IDs.
- The deploy directory identified:
src/main/deploy/swerve/.
Step 1: Verify module identity and CAN IDs
Each module JSON file in src/main/deploy/swerve/modules/ must match the
physical corner it describes. If a file is assigned to the wrong corner, the
robot will spin out of control or drive sideways.
Open REV Hardware Client (for Spark Flex) and Phoenix Tuner (for CANcoders and the Pigeon 2) and confirm that every CAN ID in the JSON matches the hardware.
| Module | File | Drive CAN | Angle CAN | Encoder CAN |
|---|---|---|---|---|
| Front Left | frontleft.json |
12 | 10 | 11 |
| Front Right | frontright.json |
42 | 40 | 41 |
| Back Left | backleft.json |
22 | 20 | 21 |
| Back Right | backright.json |
32 | 30 | 31 |
The Pigeon 2 IMU is CAN ID 8 (set in swervedrive.json).
If a motor blinks but the wrong module moves, the CAN IDs are swapped between files. Fix them in the JSON before continuing.
Step 2: Set absolute encoder offsets
The absolute encoder offset tells YAGSL where "zero degrees" is for each module. Getting this wrong makes the wheels point in random directions at startup.
- Orient all four modules so the wheel bevels face left when viewed from above. This is the YAGSL-defined "forward" position (see the YAGSL configuration guide).
- Deploy the code and enable the robot in teleop.
- In NetworkTables (via AdvantageScope or SmartDashboard), read the value of
swerve/modules/<moduleName>/Raw Absolute Encoderfor each module. - Enter each value as
absoluteEncoderOffset(in degrees) in the corresponding module JSON file. - Redeploy and verify the wheels all point forward at startup.
Odometry drives backward? If real-life forward motion shows as backward in odometry (but rotation is correct), add
180to every module'sabsoluteEncoderOffsetand redeploy.
Step 3: Verify motor and IMU inversions
The goal is to make every sensor value increase when rotated counterclockwise (CCW-positive), which is the WPILib convention. Run each check with the robot on blocks.
Steer (angle) motors:
- Spin each module counterclockwise from a top-down view.
- Watch
swerve/modules/<moduleName>/Raw Angle Encoderin NetworkTables. - If the value decreases, set
inverted.angletotruein that module's JSON.
Drive motors:
- Spin each wheel counterclockwise (looking at the wheel from the outside).
- Watch
swerve/modules/<moduleName>/Raw Drive Encoder. - If the value decreases, set
inverted.drivetotruein that module's JSON.
IMU (Pigeon 2):
- Rotate the entire robot counterclockwise (top-down view).
- Watch
Raw IMU Yawin NetworkTables. - If it decreases, set
invertedIMUtotrueinswervedrive.json.
Our current settings are inverted.drive = true, inverted.angle = true, and
invertedIMU = false. Only change these if you replace hardware or swap module
positions.
Step 4: Verify conversion factors
Conversion factors tell YAGSL how to translate motor rotations into wheel angle (degrees) and distance (meters). Wrong values cause incorrect speeds and odometry drift.
Default values live in modules/physicalproperties.json and apply to every
module. Individual modules can override them with a conversionFactors block in
their own JSON file.
| Property | Where to find it | Current default |
|---|---|---|
| Angle gear ratio | conversionFactors.angle.gearRatio in physicalproperties.json |
21.43 (SDS MK4i) |
| Drive gear ratio | conversionFactors.drive.gearRatio in physicalproperties.json |
5.9 |
| Wheel diameter (in) | conversionFactors.drive.diameter in physicalproperties.json |
4 |
Our front modules override the angle gear ratio to 18.75 (SDS MK4n), while the back modules inherit the default 21.43 (SDS MK4i). If you swap module hardware, update these values to match. YAGSL maintains a standard conversion factors page with values for common COTS modules.
How to verify: Drive a known distance (e.g., tape measure 3 meters on the floor), then compare the odometry position in AdvantageScope. If the reported distance is off by more than 5%, your gear ratio or wheel diameter is wrong.
Step 5: Tune per-module angle (steer) PID
The angle PID controls how quickly and smoothly each
module rotates to its target heading. These gains go in each module JSON under
the angle motor's p, i, and d fields. Per-module values override the
defaults in pidfproperties.json.
Important: YAGSL enables PID wrapping on steer motors at 0° and 360°. To avoid masking issues at the wrap point, test with left/right translation (strafe), not pure rotation.
Tuning procedure (from the YAGSL tuning guide):
- Set
p,i, anddall to 0. - Increase
puntil the module starts oscillating around its target angle. - Increase
duntil the oscillation dampens and the module settles quickly without jitter. - Leave
iat 0 unless there is a persistent steady-state offset thatpcannot fix. - Repeat for each module individually — gains can vary between corners because of friction, belt tension, or mechanical differences.
Current tuned values (for reference):
| Module | Angle P | Angle D |
|---|---|---|
| Front Left | 1.763 | 38.795 |
| Front Right | 1.8952 | 56.839 |
| Back Left | 1.7059 | 17.673 |
| Back Right | 1.7059 | 17.673 |
The front and back values differ significantly because the module hardware has different gear ratios (18.75 vs 21.43). Re-tune whenever you replace a motor, belt, or module.
Step 6: Tune per-module drive PID
The drive PID controls each wheel's velocity tracking.
These gains go in each module JSON under the drive motor's p, i, and d
fields.
Tuning procedure:
- Start with the YAGSL default for Spark Flex / NEO Vortex motors:
p = 0.0020645(frompidfproperties.json). - Command a moderate forward speed and watch the wheel velocities in AdvantageScope.
- If the wheels are slow to reach target speed, increase
p. - If the wheels oscillate or jerk, decrease
p. - Leave
dat 0 for velocity control — derivative gain on a noisy velocity signal usually hurts more than it helps. - Leave
iat 0 unless there is a consistent steady-state velocity error.
Current tuned values:
| Module | Drive P |
|---|---|
| Front Left | 0.033935 |
| Front Right | 0.07003 |
| Back Left | 5.36e-5 |
| Back Right | 5.36e-5 |
The front and back modules have very different gains. This may reflect hardware differences or an incomplete tuning pass. Re-tune each module after any mechanical change.
Step 7: Run SysId for drive and angle motors (optional)
WPILib's System Identification (SysId) tool can characterize the drive and steer motors to produce feedback gains. This step is optional but recommended for competition-level accuracy.
What SysId results are used for
YAGSL only uses kP for drive motors (velocity P-loop) and kP + kD for angle motors (position PD-loop). The feedforward values (kS, kV, kA) produced by SysId are not wired into YAGSL's module control pipeline — they serve as diagnostic data for comparing module health and flagging mechanical issues (e.g., a module with a significantly different kV likely has a problem).
SysId configuration
All SysId timing is read from the sysId block in subsystems.json under
driveBaseSubsystem. The same config applies to both grouped and per-module
tests:
| Parameter | Default | Purpose |
|---|---|---|
rampRateVoltsPerSecond |
1.0 | Quasistatic voltage ramp rate (V/s) |
stepVoltage |
6.0 | Dynamic test step voltage (V) |
delaySeconds |
10.0 | Pause between test phases (s) |
quasistaticTimeoutSeconds |
40.0 | Duration of each quasistatic phase (s) |
dynamicTimeoutSeconds |
4.0 | Duration of each dynamic phase (s) |
Grouped SysId (all motors at once)
The DriveBaseSubsystemCommandFactory provides two commands that test all four
motors of the same type simultaneously:
createDriveSysIdCommand()— exercises all four drive motors.createAngleSysIdCommand()— exercises all four steer motors.
These use YAGSL's built-in SwerveDriveTest routines, which handle the
quasistatic ramp, dynamic step, and data logging automatically. In tuning mode,
the driver Y button runs angle SysId followed by drive SysId in sequence.
Per-module SysId (one motor at a time)
Per-module SysId isolates a single motor for characterization while holding all other motors at zero voltage. This is valuable because each module may have different friction, gear mesh, or wiring characteristics.
The factory provides two parameterized methods:
createDriveSysIdCommandForModule(int moduleIndex)— exercises one drive motor.createAngleSysIdCommandForModule(int moduleIndex)— exercises one angle motor.
Module index constants are defined on DriveBaseSubsystemCommandFactory:
MODULE_FRONT_LEFT (0), MODULE_FRONT_RIGHT (1), MODULE_BACK_LEFT (2),
MODULE_BACK_RIGHT (3).
In tuning mode (tuningEnabled = true), the operator controller is mapped to
per-module SysId:
| Button | Motor Type | Module |
|---|---|---|
| A | Drive | Front Left |
| B | Drive | Front Right |
| X | Drive | Back Left |
| Y | Drive | Back Right |
| Left Bumper | Angle | Front Left |
| Right Bumper | Angle | Front Right |
| Left Trigger | Angle | Back Left |
| Right Trigger | Angle | Back Right |
To run per-module SysId:
- Set
tuningEnabledtotrueinsubsystems.jsonand restart the robot code. - Place the robot on blocks with wheels free to spin.
- Enable teleop and press the operator button for the motor you want to test. Hold the button — it runs all four test phases automatically. Release to cancel early.
- Repeat for each motor.
- Disable the robot and pull the WPILog file.
- Open WPILib's SysId analysis tool, load the log, and read kP from the results for each drive motor.
- Enter the kP values into each module JSON file's
"drive"→"p"field. Use kS/kV/kA to compare modules and flag outliers.
Note: The YAGSL swerve SysId workflow is different from the project's
SysIdHelper-based workflow used by other subsystems (turret, shooter, etc.). The ÷2π correction described in the SysId tuning guide does not apply here because YAGSL handles its own unit conversions internally.
Step 8: Tune heading PID
The heading PID keeps the robot pointing at a target angle during teleop (heading hold) and autonomous (path rotation tracking). It acts on the gyroscope yaw reading.
Gains are set in two places:
- Deploy file:
controllerproperties.json→heading.p,heading.i,heading.d(loaded at startup by YAGSL). - Runtime tunables:
subsystems.json→headingKp,headingKi,headingKd(applied byDriveBaseSubsystem.refreshTunables()every cycle when not connected to FMS).
The runtime tunables override the deploy file values, so tune via SmartDashboard during practice and then copy the final values back into both files.
Tuning procedure:
- Set
headingKiandheadingKdto 0. StartheadingKpat 0.5. - Enable teleop and drive forward. Release the sticks — the robot should hold its heading.
- Push the robot off-angle by hand. It should rotate back to the original heading.
- If the correction is sluggish, increase
headingKp. - If the robot oscillates or overshoots the target angle, decrease
headingKpor add a smallheadingKd(start around 0.01). - Adjust
rotationToleranceDegrees(default 2.0°) if the robot hunts back and forth near the target — a wider tolerance allows it to settle sooner.
Current values: headingKp = 0.85, headingKi = 0, headingKd = 0.02.
Step 9: Tune path-following PID
PathPlanner uses two separate PID controllers to keep the robot on its planned trajectory during autonomous:
- Translation PID (
pathTranslationKp/Ki/Kd) — corrects position error in meters. - Rotation PID (
pathRotationKp/Ki/Kd) — corrects heading error in radians.
Both are set in subsystems.json and are tunable via SmartDashboard.
Tuning procedure:
- Create a simple autonomous path (a straight line or gentle curve).
- Deploy, run the auto, and overlay the planned vs. actual path in AdvantageScope.
- If the robot cuts corners or lags behind the path, increase the translation
Kp. - If the robot oscillates laterally along the path, decrease the translation
Kpor add a smallKd. - Repeat the same process for the rotation controller by watching heading error during turns.
Current values: pathTranslationKp = 5, pathRotationKp = 5 (all Ki and Kd are
0).
Step 10: Validate on the field
With all gains tuned on blocks, put the robot on the ground for a final checkout.
- Translate in all four directions (forward, backward, left, right). The robot should move cleanly without pulling to one side.
- Strafe diagonally. Verify the path is straight, not curved.
- Spin in place. The robot should rotate smoothly without translating.
- Drive and rotate simultaneously. Field-centric control should feel intuitive.
- Check odometry. Drive a known distance and compare the AdvantageScope pose to the real position. Heading should also match.
- Run a known autonomous path. Compare planned vs. actual trajectory in AdvantageScope.
Troubleshooting quick reference:
| Symptom | Likely cause |
|---|---|
| Robot drives sideways or at an angle | Module CAN IDs swapped between JSON files |
| Wheels point in random directions | Absolute encoder offsets are wrong (Step 2) |
| Robot spins uncontrollably | Motor inversions are wrong (Step 3) |
| Odometry drifts over time | Gear ratios or wheel diameter incorrect (Step 4) |
| Odometry shows backward when driving forward | Add 180° to all encoder offsets (Step 2) |
| Wheels stutter or vibrate | Angle or drive PID too high (Steps 5–6) |
| Wheels respond slowly | Angle or drive PID too low (Steps 5–6) |
| Robot overshoots heading target | Heading Kp too high or Kd too low (Step 8) |
| Auto path tracking is sloppy | Path-following PID needs tuning (Step 9) |
Configuration file quick reference
| Concern | File | Key fields |
|---|---|---|
| IMU type and CAN ID | swerve/swervedrive.json |
imu.type, imu.id, invertedIMU |
| Module CAN IDs | swerve/modules/<module>.json |
drive.id, angle.id, encoder.id |
| Absolute encoder offset | swerve/modules/<module>.json |
absoluteEncoderOffset (degrees) |
| Motor inversions | swerve/modules/<module>.json |
inverted.drive, inverted.angle |
| Gear ratios / wheel size | swerve/modules/physicalproperties.json |
conversionFactors.angle.gearRatio, .drive.* |
| Per-module gear overrides | swerve/modules/<module>.json |
conversionFactors (overrides physicalproperties) |
| Default motor PIDF | swerve/modules/pidfproperties.json |
drive.p/i/d/f, angle.p/i/d/f |
| Per-module motor PIDF | swerve/modules/<module>.json |
drive.p/i/d, angle.p/i/d |
| Heading PID (deploy) | swerve/controllerproperties.json |
heading.p/i/d |
| Heading PID (runtime) | subsystems.json → driveBaseSubsystem |
headingKp, headingKi, headingKd |
| Path-following PID | subsystems.json → driveBaseSubsystem |
pathTranslationKp/Ki/Kd, pathRotationKp/Ki/Kd |
| Max speeds | subsystems.json → driveBaseSubsystem |
maximumLinearSpeedFeetPerSecond, maximumAngularSpeedDegreesPerSecond |
| Current limits / ramp rate | swerve/modules/physicalproperties.json |
currentLimit.*, rampRate.* |
Code structure
| File | Purpose |
|---|---|
DriveBaseSubsystem.java |
Owns odometry, field-relative driving, holonomic pose chasing, joystick scaling, and AdvantageKit logging |
commands/MoveFieldManualCommand.java |
Applies field-relative chassis speeds from joystick input |
commands/DriveBaseSubsystemCommandFactory.java |
Wires driver axes, sets the default manual command, and exposes SysId commands via YAGSL |
commands/PathPlannerCommandFactory.java |
Builds autonomous path-following commands from deployed .auto files |
config/DriveBaseSubsystemConfig.java |
Tunables for max speed, PID gains, tolerances, and joystick sensitivity |
io/DriveBaseIO.java |
AdvantageKit IO interface — defines logged inputs for pose, gyro, and module states |
io/DriveBaseIOYagsl.java |
YAGSL hardware implementation — pulls data from the active SwerveDrive |
Status / TODO
Done
- Field-centric swerve driving with deadband and scaling.
- Odometry via YAGSL's
SwerveDrivePoseEstimator. - Vision measurement injection for pose fusion.
- PathPlanner integration for autonomous paths.
- SysId support via YAGSL helpers.
- Sim-friendly fallback when subsystem is disabled.
- Calibration and tuning guide added to this README.
TODO
- Add more autonomous poses and driver aids as needed.