System Identification with SysId
Hook
Your elevator kV is 2.4 according to your best guess from motor specs. You deploy and the carriage overshoots by 15 cm. You tune it down to 2.1 and it undershoots. You spend two hours at practice tweaking a number that you could have measured in five minutes. WPILib’s SysId tool does exactly this measurement — it drives your mechanism with precise voltage sequences, logs the response, and fits a mathematical model to your real hardware. The result is kS, kV, and kA values accurate to your actual robot, not a generic estimate.
Core concept
System identification (SysId) is the process of measuring a mechanism’s dynamic properties by applying known inputs and recording the outputs. WPILib’s SysId tool runs two test routines — quasistatic and dynamic — to isolate static friction (kS), velocity gain (kV), and acceleration gain (kA) from real robot data via linear regression.
Why guess is worse than measure
The feedforward equation is:
voltage = kS * sign(velocity) + kV * velocity + kA * acceleration
Each constant corresponds to a physical property:
kS— voltage to overcome static friction and start movingkV— voltage per unit velocity (back-EMF of motor + load)kA— voltage per unit acceleration (rotational inertia of mechanism)
These depend on:
- Motor model and its actual efficiency (not nameplate)
- Gearbox efficiency at your specific ratio
- Mechanism mass and geometry
- Battery state of charge during measurement
- Bearing and chain friction on this specific robot
Motor datasheets give you free-speed and stall torque, but they don’t account for your specific gearbox, chain tension, or the weight of whatever is attached. Measuring kV and kA on the actual robot accounts for all of this automatically.
The two SysId test routines
Quasistatic test
The robot slowly ramps voltage from 0 upward. “Quasistatic” means nearly at rest — acceleration is intentionally kept near zero so the kA term contributes nothing. This isolates:
voltage ≈ kS + kV * velocity (since kA * acceleration ≈ 0)
Plotting voltage vs. velocity gives a straight line. The slope is kV. The y-intercept is kS (the voltage required at zero velocity to keep the mechanism barely moving).
slope = kV
intercept = kS
The quasistatic test is run in both directions (forward and reverse) to capture any directional asymmetry in friction.
Dynamic test
The robot applies a large fixed voltage suddenly, causing rapid acceleration. This isolates:
voltage ≈ kS + kV * velocity + kA * acceleration
With kS and kV already known from the quasistatic test, the residual (voltage − kS − kV * velocity) plotted vs. acceleration gives a line whose slope is kA.
residual = kA * acceleration
slope = kA
Together, the two tests give all three constants.
Running SysId — step by step
1. Instrument your robot code
Add SysIdRoutine to your subsystem. WPILib’s SysIdRoutine class handles logging voltage, velocity, and position automatically:
import edu.wpi.first.wpilibj2.command.sysid.SysIdRoutine;
import edu.wpi.first.units.Units;
// In your subsystem:
private final SysIdRoutine sysId = new SysIdRoutine(
new SysIdRoutine.Config(), // default: 1 V/s ramp, 7 V max, 10 s timeout
new SysIdRoutine.Mechanism(
(voltage) -> motor.setVoltage(voltage.in(Units.Volts)),
(log) -> {
log.motor("elevator")
.voltage(Units.Volts.of(motor.getBusVoltage() * motor.getAppliedOutput()))
.linearPosition(Units.Meters.of(encoder.getPosition()))
.linearVelocity(Units.MetersPerSecond.of(encoder.getVelocity()));
},
this
)
);
// Expose commands for the test routines:
public Command sysIdQuasiForward() {
return sysId.quasistatic(SysIdRoutine.Direction.kForward);
}
public Command sysIdQuasiReverse() {
return sysId.quasistatic(SysIdRoutine.Direction.kReverse);
}
public Command sysIdDynamicForward() {
return sysId.dynamic(SysIdRoutine.Direction.kForward);
}
public Command sysIdDynamicReverse() {
return sysId.dynamic(SysIdRoutine.Direction.kReverse);
}
Bind these commands to controller buttons in RobotContainer:
// Button bindings for SysId (only used during characterization, not competition)
operatorController.a().whileTrue(elevator.sysIdQuasiForward());
operatorController.b().whileTrue(elevator.sysIdQuasiReverse());
operatorController.x().whileTrue(elevator.sysIdDynamicForward());
operatorController.y().whileTrue(elevator.sysIdDynamicReverse());
2. Collect data on the robot
Deploy the code. In the Driver Station, enable the robot. Run each routine in sequence, holding the button for 3–5 seconds per test. The data is logged to a WPILOG file on the roboRIO’s internal storage.
Conditions for valid data:
- Battery above 12 V (freshly charged) — voltage sag during the test corrupts kV measurement
- Mechanism within normal operating range — not at a mechanical limit during the sweep
- Mechanism is zeroed and not oscillating before you start
- Run both forward AND reverse for each test type (4 tests total)
- Keep the mechanism moving through a reasonable speed range — too short a range means poor regression accuracy
Never run SysId with a brownout-prone battery or when the mechanism is near its travel limits. The dynamic test applies high voltage suddenly — if the mechanism is near the end of travel, it will hit the hard stop. Set software limits to cut off well before the physical ends.
3. Download and analyze logs
Use the Data Log Tool (part of WPILib installation) or the Shuffleboard log viewer to download the .wpilog file from the roboRIO. Then open the WPILib SysId Analyzer (standalone application in the WPILib installation) and load the log file.
The analyzer:
- Splits the log into the four test segments automatically.
- Filters high-frequency noise from velocity signals.
- Runs linear regression on each test.
- Reports kS, kV, kA with 95% confidence intervals and an R-squared quality metric.
4. Evaluate data quality
The SysId Analyzer provides diagnostic plots:
- Velocity vs. time — should be smooth; jagged lines indicate encoder noise or too-short logging window
- Quasistatic fit — data points should cluster tightly around the regression line (R^2 > 0.95 is good)
- Dynamic fit — slope gives kA; scatter here is more normal because acceleration is noisier than velocity
If R^2 is below 0.90 on the quasistatic test, your velocity signal is too noisy. Apply a moving average filter to encoder velocity or increase the encoder resolution.
Reading the output table
After analysis, SysId presents a table like this:
Constant | Value | Units
----------|-----------|----------
kS | 0.112 | V
kV | 2.843 | V·s/m
kA | 0.063 | V·s²/m
kP (LQR) | 5.21 | V/m
R^2 (qs) | 0.987 |
R^2 (dyn) | 0.961 |
kS,kV,kAare plugged directly intoElevatorFeedforwardorSimpleMotorFeedforward.kP (LQR)is an optional suggested proportional gain derived from an LQR calculation — it is a starting point, not a final tuned value.R^2near 1.0 means the linear model fits the data well.
Applying the results
// Before SysId — guessed values:
ElevatorFeedforward ff = new ElevatorFeedforward(0.1, 0.5, 2.4, 0.05);
// After SysId — measured values:
ElevatorFeedforward ff = new ElevatorFeedforward(0.112, 0.48, 2.843, 0.063);
The change looks small, but the feedforward accuracy improvement often eliminates the need for integral gain entirely — which reduces windup risk and makes the controller more robust.
SysId for other mechanism types
| Mechanism | WPILib log type | Notes |
|---|---|---|
| Drivetrain (tank) | linearPosition / linearVelocity | Run each side separately |
| Drivetrain (swerve) | Drive modules individually | Use SysId per module |
| Flywheel | angularPosition / angularVelocity | Use rad or rotations |
| Arm | angularPosition / angularVelocity | Only characterizes kV/kA; kG needs separate static test |
| Elevator | linearPosition / linearVelocity | Standard linear characterization |
For an arm, note that kG (gravity compensation) cannot be directly measured by SysId’s standard routines, because gravity is a position-dependent disturbance, not a velocity/acceleration phenomenon. Measure kG separately: disable all PID and feedforward, then find the voltage at which the arm holds still at horizontal (0°). That voltage is kG.
Key takeaways
- SysId measures kS, kV, and kA directly from your robot’s hardware — more accurate than guessing from motor specs.
- The quasistatic test (slow ramp) isolates kS and kV by keeping acceleration near zero.
- The dynamic test (sudden large voltage) isolates kA after kS and kV are subtracted.
- Four test runs are required: quasistatic forward, quasistatic reverse, dynamic forward, dynamic reverse.
- Good data requires a charged battery, soft limits active, and enough travel range to cover the mechanism’s typical operating speed.
- kG for arms is not directly measured by SysId — find it with a separate static holding test.
Common confusions
“SysId gave me kV = 3.1 but my elevator crawls at 3 V.” Your unit conversion may be wrong. If your encoder returns rotations and you forgot to apply the position conversion factor before running SysId, kV is in V·s/rotation rather than V·s/m. Re-run with correct unit conversion applied to the encoder.
“R^2 is 0.78 — do I use these constants?” That is too low. Re-run the quasistatic test with a freshly charged battery. Also check that you are running the test over the full useful speed range of the mechanism — if the mechanism only moves 5 cm before stopping, the regression has very little data.
“The analyzer says my kA is negative.” This is physically impossible (a negative inertia would mean the motor accelerates with no energy input). This almost always means your position/velocity and voltage signs are inconsistent — your encoder returns negative velocity when the motor applies positive voltage. Fix your inversion settings and re-run.
“I ran SysId on the drivetrain but the shooter still needs separate characterization.” Correct — each mechanism has its own friction, inertia, and back-EMF. You must run SysId separately for every mechanism you want to characterize (elevator, shooter, arm, drive).
Challenge
Below is a simplified SysId output table. Read the values and write code that applies them to a SimpleMotorFeedforward for a flywheel. Then compute the expected feedforward voltage at three target velocities using the measured constants.
SysId output (flywheel, angular velocity in rad/s): kS = 0.095 V kV = 0.038 Vs/rad kA = 0.002 Vs^2/rad R^2 quasistatic = 0.994 R^2 dynamic = 0.971
Compute feedforward at: 100 rad/s, 200 rad/s, 300 rad/s (Flywheel is at constant velocity — assume zero acceleration, so kA term = 0) Formula: ff = kS + kV * velocity (sign = +1 for forward)
Stuck? Show hint
ff = kS + kV * velocity. For 100 rad/s: 0.095 + 0.038*100 = 0.095 + 3.8 = 3.895. The kA term (0.002) is zero because acceleration = 0 at constant velocity.
What is the primary purpose of the quasistatic SysId test routine?
What’s next
The next lesson introduces state-space control — a framework that models mechanisms with multiple coupled variables simultaneously, going beyond the single-input single-output structure of PID. It is the mathematical foundation for Kalman filters and LQR controllers.