Shared Subsystems SysID Guide
SysId tuning guide
This guide walks you through characterizing a motor-driven mechanism using
WPILib's SysId tool and our SysIdHelper infrastructure. Follow every step to
get feedforward and PID gains that work on the robot.
For background on feedforward, PID, and related terms, see the project glossary.
Overview
System Identification (SysId) measures how a mechanism responds to known voltages and produces three feedforward gains:
- kS — the voltage needed to overcome static friction (the "unstick" voltage).
- kV — the voltage needed per unit of speed to maintain constant velocity.
- kA — the voltage needed per unit of acceleration to change speed.
These gains let the feedforward controller predict the right voltage for any target speed, so the PID controller only has to make small corrections.
Prerequisites
Before running SysId on a mechanism:
- The subsystem extends
AbstractMotorSubsystem(either throughAbstractVelocitySubsystemorAbstractSetAndSeekSubsystem). - The command factory extends
AbstractVelocityCommandFactoryorAbstractSetAndSeekCommandFactory, which provide SysId commands automatically. - The mechanism's motor config has correct values for:
motorRotationsPerMechanismRotation(gear ratio)motorInvertedsmartCurrentLimitAmps
- SysId buttons are bound in
TriggerBindings(already wired for all subsystems). - The mechanism is physically safe to run freely in both directions. Remove game pieces, clear the area, and hold the robot securely.
Step 1: Configure SysId parameters
In subsystems.json, set two values in the mechanism's config section:
"sysIdRampRateVoltsPerSecond": 0.25,
"sysIdStepVoltage": 7.0
- Ramp rate controls how slowly voltage increases during the quasistatic
(slow ramp) test. Lower values (0.15–0.30) give more data points and a better
fit, but take longer. Start with
0.25. - Step voltage is the constant voltage applied during the dynamic (sudden
step) test. It should be high enough to get the mechanism moving quickly but
not so high that it hits a hard stop or current limit.
7.0is a safe default for most mechanisms.
Step 2: Deploy and connect
- Deploy the code to the robot:
./gradlew deploy. - Open AdvantageScope and connect to the robot at
10.71.60.2. - Enable the robot in teleop mode from the Driver Station.
- Verify the mechanism responds to normal commands before starting SysId.
Step 3: Run the four SysId tests
SysId requires four test runs. Use the controller buttons bound in
TriggerBindings:
| Test | What it does |
|---|---|
| Quasistatic forward | Slowly ramps voltage from 0 upward |
| Quasistatic reverse | Slowly ramps voltage from 0 downward |
| Dynamic forward | Applies a sudden step voltage in the forward direction |
| Dynamic reverse | Applies a sudden step voltage in the reverse direction |
For each test:
- Press the button to start the test.
- Let it run until the mechanism reaches a steady speed (quasistatic) or for 2–3 seconds (dynamic). The quasistatic tests may need 20–40 seconds for slow ramp rates.
- Press the button again or switch to a different command to stop.
- Wait 2–3 seconds between tests so the mechanism stops completely.
After all four tests, disable the robot. The SysId data is recorded in the WPILog file.
Step 4: Analyze in the SysId tool
- Open the WPILib SysId analysis tool (search "SysId" in the WPILib command palette or VS Code).
- Load the WPILog file from AdvantageScope or the robot's log directory.
- Select the correct motor label (it matches the
motorLabelparameter inSysIdHelper). - Choose "Simple" mechanism type.
- The Feedforward Analysis section reports kS, kV, and kA.
- The Feedback Analysis section reports kP — record this value too.
Write down all four values before proceeding to the next step.
Step 5: Enter gains in config (no correction needed)
Our SysIdHelper pre-multiplies position and velocity by 2π before logging.
This cancels WPILib's internal ÷2π conversion, so the SysId analysis tool sees
true radian-based values. The reported gains can be entered directly into
subsystems.json without any manual correction.
Update the mechanism's section in subsystems.json:
"kS": 0.66654,
"kV": 0.08148,
"kA": 0.00926,
"kP": 0.025503,
"kI": 0.0,
"kD": 0.0
Use the kP value reported by the SysId Feedback Analysis section as your
starting point. You will fine-tune it on the robot later (see Step 7). If SysId
did not report a kP, start with 0.01 as a conservative default.
Also update the velocity limits now that you know the real max speed:
"maximumVelocityRpm": 1300,
"maximumAccelerationRpmPerSecond": 500
Set maximumVelocityRpm to roughly 80–90% of the calculated max to leave
headroom for the PID controller to make corrections.
How to verify the gains are reasonable
Use this quick sanity check on kV:
Max achievable RPM ≈ (12V − kS) / kV / (2π / 60)
For the intake: (12 − 0.306) / 0.083 / 0.1047 ≈ 1345 RPM
If the motor is a NEO Vortex (6784 RPM free speed) with a 4:1 gear ratio, the
theoretical max mechanism speed is 6784 / 4 = 1696 RPM. Getting ~1345 RPM
under load is realistic. If the calculated max is wildly different from the
theoretical max, something is wrong.
Step 6: Deploy and smoke test
- Deploy the updated config:
./gradlew deploy. - Enable teleop and command the mechanism to a moderate target (e.g., 400 RPM for a roller, 2000 RPM for a flywheel).
- In AdvantageScope, check:
measuredRpmtracks close totargetRpm.voltageCommandVoltsis reasonable (not saturated at 12V).- The mechanism reaches the target within a few seconds.
If the mechanism barely moves, double-check that the gear ratio in the motor config matches the physical mechanism. If it overshoots wildly, verify kS and the motor direction.
Step 7: Tune kP
Once feedforward is correct, tune kP to tighten the response:
- Start with the kP value from SysId's Feedback Analysis section. If SysId did
not report one, use
0.01as a conservative default. - Command the mechanism to a target RPM and watch the error in AdvantageScope.
- If the steady-state error is too large, double kP.
- If the mechanism oscillates around the target, halve kP.
- Repeat until the error is within your
velocityToleranceRpm.
Leave kI at 0.0 unless there is a persistent steady-state error that kP cannot
fix. Leave kD at 0.0 for velocity mechanisms — derivative gain on a noisy
velocity signal usually does more harm than good.
How the 2π compensation works
WPILib's SysIdRoutineLog.angularVelocity() internally divides by 2π
(converting radians/sec to rotations/sec) before writing to the log file. The
SysId analysis tool then reads those rotations/sec values but treats them as
radians/sec, which would inflate every velocity-dependent gain by 2π ≈ 6.2832.
SysIdHelper compensates by multiplying position and velocity by 2π before
passing them to the WPILib logger. The internal ÷2π then cancels out, and SysId
sees the true radian-based values. This means gains reported by the tool are
already in the correct per-radian units and can be used directly.
If you ever bypass SysIdHelper and build a SysIdRoutine manually, you will
need to apply this same pre-multiplication or divide the reported kV, kA, and kP
by 2π after analysis.
Diagnostic telemetry
SysIdHelper logs the following values to AdvantageKit under the SysIdHelper/
prefix during SysId tests:
| Key | What it shows |
|---|---|
sysIdVelocityRadPerSec |
Mechanism velocity in rad/s (true value) |
sysIdVelocityRpm |
Mechanism velocity in RPM (converted from rad/s) |
sysIdRawMotorRpm |
Raw motor shaft RPM before gear ratio conversion |
sysIdPositionRad |
Mechanism position in radians (true value) |
sysIdVoltage |
Applied voltage in volts |
Use these to verify the SysId routine is seeing correct values. If
sysIdVelocityRpm ÷ gear ratio ≈ sysIdRawMotorRpm, the conversion chain is
correct.
Note: the values logged to AdvantageKit are the true mechanism values, not the pre-multiplied values fed to the SysId logger. Use these for debugging, not for manual gain computation.
Troubleshooting
Max calculated RPM is way above the motor's theoretical max
Your kV is too low. Re-run SysId and make sure the mechanism ran freely during the quasistatic test (no binding, no game pieces jamming the rollers).
Mechanism runs fine during SysId but not during normal operation
Check that the motor inversion (motorInverted) matches for both SysId and
normal commands. Also verify that the gear ratio is the same value in both the
motor config and the SysId conversion chain.
SysId fit is poor (R² < 0.9)
- Lower the ramp rate to get more data points in the quasistatic test.
- Make sure the mechanism is not hitting a current limit during SysId. Check
SupplyCurrentAmpsin AdvantageScope. If it is hitting the limit, raisesmartCurrentLimitAmpstemporarily during SysId, then set it back afterward. - Ensure the mechanism moves freely in both directions. A one-direction bind will corrupt the data.
Quick reference card
1. Set sysIdRampRateVoltsPerSecond and sysIdStepVoltage in subsystems.json.
2. Deploy. Run all four SysId tests.
3. Open the SysId tool and read kS, kV, kA, and kP (Feedback Analysis).
4. Enter gains directly into subsystems.json (no correction needed).
5. Deploy. Verify target tracking in AdvantageScope.
6. Tune kP until error is within tolerance.