System Identification (SysId)
System Identification (SysId) is the process of determining feedforward constants for your mechanism through statistical analysis of its inputs and outputs.
What is System Identification?
System Identification is the process of determining a mathematical model for the behavior of a system through statistical analysis of its inputs and outputs. This model describes how input voltage affects the way your measurements (typically encoder data) evolve over time.
The WPILib SysId tool takes a model and dataset and attempts to fit parameters which would make your model most closely match the dataset. Even an imperfect model is usually "good enough" to give accurate feedforward control of the mechanism, and even to estimate optimal gains for feedback control.
For a complete understanding of SysId theory, visit the WPILib System Identification Documentation.
The Feedforward Equations
Different mechanisms use different feedforward equations:
Simple Motor (Flywheels, Turrets, Linear Sliders)
V=Ks⋅sgn(d˙)+Kv⋅d˙+Ka⋅d¨
Elevator
V=Kg+Ks⋅sgn(d˙)+Kv⋅d˙+Ka⋅d¨
The constant term (Kg) accounts for gravity.
Arm
V=Kg⋅cos(θ)+Ks⋅sgn(θ˙)+Kv⋅θ˙+Ka⋅θ¨
The cosine term (Kg) accounts for the effect of gravity at different arm angles.
Types of Tests
A standard SysId routine consists of two types of tests, each run in both directions (forward and backward):
Quasistatic
The mechanism is gradually sped up such that the voltage corresponding to acceleration is negligible (hence, "as if static"). This helps determine Ks and Kv.
Dynamic
A constant "step voltage" is applied to the mechanism, so that the behavior while accelerating can be determined. This helps determine Ka.
Running a "backwards" test directly after a "forwards" test is generally advisable as it will more or less reset the mechanism to its original position.
Using YAMS Helper Functions
YAMS provides built-in sysId() methods on mechanism classes (Arm, Elevator, Shooter, etc.) that simplify the entire process.
This section covers SysId with REV Spark or ThriftyNova Motor Controllers. For CTRE TalonFX and TalonFXS, see the CTRE SysId with Signal Logger section below.
Prerequisites
Before running SysId:
Soft and hard limits are already configured
Gearing is correct
Mechanism circumference is set (for linear mechanisms)
Arm Example
Elevator Example
Binding to Controller Buttons
Use whileTrue() so you can release the button to immediately stop the test if the mechanism exceeds safe limits!
Using WPILib SysIdRoutine Directly
If you need more control over the SysId process, or are working with a SmartMotorController directly without a mechanism class, you can use WPILib's SysIdRoutine directly.
Creating the SysIdRoutine
The SysIdRoutine requires two objects:
Config - Test settings (ramp rate, step voltage, timeout)
Mechanism - Callbacks for driving motors and logging data
Why use duty cycle instead of voltage control?
Modern motor controllers (SparkMax, SparkFlex, TalonFX, etc.) have internal closed-loop voltage controllers. When you call a "set voltage" API, the motor controller actively adjusts output to maintain that voltage regardless of load or battery sag.
For SysId, we want raw, uncompensated motor behavior. By converting voltage to duty cycle (voltage / batteryVoltage) and using setDutyCycle(), we bypass the internal controller entirely. This produces cleaner data that more accurately represents the true motor and mechanism dynamics.
Why use MutVoltage, MutAngle, and MutAngularVelocity?
The logging callback runs every robot loop iteration (typically 50Hz). Creating new Voltage, Angle, or AngularVelocity objects each iteration would allocate memory on the heap, increasing garbage collection (GC) pressure and potentially causing loop overruns.
The Mut* (mutable) variants allow you to reuse the same object and update its value in-place with mut_replace(). This eliminates per-iteration allocations and keeps your robot code running smoothly.
Important: updateTelemetry() and simIterate() in the log callback
The log callback calls motor.updateTelemetry() and motor.simIterate() to ensure sensor data (position, velocity) is accurate at the exact moment of logging. This is critical for simulation accuracy.
When running SysId manually, you should temporarily disable or remove any updateTelemetry() and simIterate() calls from your subsystem's periodic() method. Having these calls in both places can cause duplicate updates per loop iteration, which may produce inconsistent data.
Reusable SysIdRoutine Factory Method
You can create a static helper method to generate SysIdRoutine instances for any SmartMotorController:
Then use it in your subsystem:
Binding All Four Tests
When using SysIdRoutine directly, you need to bind all four tests separately:
Only log files with a single routine in them are usable for analysis. If you run a routine on one motor and then run a routine on another motor without extracting the log or power-cycling the roboRIO in between, analysis will fail.
Running All Tests with One Command
You can combine all four tests into a single command sequence that runs automatically:
Why the delay between tests matters
When a test completes, the mechanism still has momentum (kinetic energy) from the previous motion. If you immediately start a test in the opposite direction, the motor must first counteract this residual momentum before the new test's motion begins. This "fighting against itself" period produces misleading data that can skew your feedforward constants.
For example, if a flywheel is spinning at high speed after a dynamic forward test and you immediately run dynamic reverse, the initial voltage readings will include the energy spent decelerating the flywheel - not just accelerating it in the new direction.
Recommendations:
Use at least 1-2 seconds of delay between tests for most mechanisms
For high-inertia mechanisms (heavy arms, large flywheels), use 3+ seconds or wait until the mechanism has fully stopped
Consider adding a
stopMotorcall between tests to actively brake the mechanismIf running tests manually with separate button bindings, wait for the mechanism to settle before starting the next test
If momentum is a concern, try reducing the test voltages - Lower dynamic step voltage (e.g., 4V instead of 7V) means less peak velocity and less residual momentum between tests
For mechanisms with limits (arms, elevators), you can add safety stops:
Running the Tests
Deploy your code to the robot
Ensure sufficient space - at least 10-20 feet for drivetrains, adequate range of motion for arms/elevators
Run all four tests in sequence:
Quasistatic Forward
Quasistatic Reverse
Dynamic Forward
Dynamic Reverse
Monitor carefully - stop tests early if the mechanism exceeds safe limits
Watch out for your mechanism and stop the test early if it exceeds safe limits! The routine only creates voltage commands - it is up to you to set up hard or soft limits to prevent injury or damage.
Analyzing Results
After running all tests:
Retrieve the log file from the roboRIO using the DataLogTool
Open SysId (included with WPILib)
Load the log file in the Log Loader pane
Drag entries to the Data Selector:
Find an entry containing "state" (type: string) → Test State slot
Find Position, Velocity, and Voltage entries → respective slots
Select analysis type (Simple, Elevator, or Arm)
Click Load and review diagnostics
Applying the Results
Once you have your feedforward constants (Ks, Kv, Ka, and Kg if applicable), apply them to your YAMS configuration:
Remember to also set .withSimFeedforward() with the same or similar values for accurate simulation!
YAMS vs Manual: When to Use Each
YAMS mechanism.sysId()
Standard mechanisms (Arm, Elevator, Shooter) where you're already using YAMS mechanism classes. Simplest setup.
WPILib SysIdRoutine
Loosely coupled mechanisms, custom configurations, or when you need fine-grained control over the logging process.
Both approaches produce compatible log files that work with the WPILib SysId analysis tool.
REV SysId with Status Logger
When using REV motor controllers (SPARK MAX, SPARK Flex), YAMS leverages the REV Status Logger to capture high-quality CAN status frame data. This runs automatically in the background and can provide better data than the standard WPILib DataLog approach.
How Status Logger Works
Status Logger is enabled by default in REVLib 2026+ and requires no setup. When you run SysId tests with REV motor controllers:
The standard WPILib
.wpilogfile is written to the roboRIO (usable directly in SysId)A separate
.revlogfile is also written, containing raw CAN status frames with higher fidelity
Do I need the .revlog file?
The standard .wpilog file from your SysId routine may work fine for analysis. However, the .revlog file captures raw CAN data at a higher rate and avoids timing issues from the robot loop. For the best results, especially if you encounter issues with your initial analysis, extract and convert the .revlog file.
Log File Locations
Status Logger saves .revlog files to:
roboRIO internal
/home/lvuser/logs/
USB drive (recommended)
/u/logs/
It is recommended to insert an external USB drive for storing .revlog files. This reduces clutter on the roboRIO and makes file retrieval easier.
Retrieving .revlog Files
.revlog FilesFrom roboRIO internal storage:
Use FTP to access files at /home/lvuser/logs/. See the WPILib FTP documentation for details.
From USB drive:
Either access the /u/logs/ directory with the flash drive plugged into the roboRIO, or simply unplug the USB drive and insert it into your computer.
Converting .revlog to .wpilog
.revlog to .wpilog.revlog files must be converted before use in SysId. There are two methods:
Method 1: AdvantageScope (Recommended)
Open AdvantageScope
Open your
.revlogfile directlyAdvantageScope automatically converts it to
.wpilogformatExport as
.wpilogif needed for SysId
AdvantageScope is the easiest method and handles the conversion automatically when you open the file.
Method 2: revlog-converter CLI
Use the revlog-converter NPM package for command-line conversion:
Manual Logging Control
If you need precise control over when logging occurs (e.g., only during SysId tests), you can disable auto-logging:
disableAutoLogging() must be called before any REVLib device object is created (e.g., before constructing any SparkMax or SparkFlex). The recommended placement is as the first line in robotInit().
CTRE SysId with Signal Logger
When using CTRE motor controllers (TalonFX, TalonFXS), YAMS leverages the Phoenix 6 Signal Logger instead of WPILib's DataLog. This provides several advantages:
Eliminates CAN latency issues
Supports signals faster than the 20ms main robot loop
Avoids Java garbage collection pauses affecting log quality
How YAMS Uses Signal Logger
When you call mechanism.sysId() with a CTRE motor controller, YAMS automatically:
Configures the
SysIdRoutineto log state usingSignalLogger.writeString()Sets the log callback to
null(Signal Logger captures all signals automatically)Uses
VoltageOutcontrol requests to apply voltage during tests
The resulting routine looks like this internally:
Running CTRE SysId Tests
You must manually start and stop the Signal Logger before and after running tests:
Important: Start the Signal Logger before running any tests and stop it after all four tests are complete. This ensures all test data is captured in a single log file without extraneous data from other robot actions.
Extracting and Converting CTRE Logs
CTRE logs are saved in the .hoot format, which must be converted to .wpilog before analysis in SysId.
Do not use third-party tools to convert .hoot files. A lossy conversion can impact the quality of SysId analysis, especially in simulation where it may cause SysId to fail entirely.
Step 1: Locate the Log File
After running your tests, the .hoot log file is stored on the roboRIO at:
You can also find logs on a USB drive if one was connected during testing.
Step 2: Extract Using Phoenix Tuner X
Open Phoenix Tuner X
Connect to your robot
Navigate to Tools → Extracting Signal Logs
Select your
.hootlog fileChoose WPILOG as the export format
Save the exported
.wpilogfile
Step 3: Alternative - Using owlet CLI
You can also use CTRE's owlet command-line tool:
Step 4: Load in SysId
Open the SysId tool (included with WPILib)
In the Log Loader pane, load your converted
.wpilogfileIn the Data Selector, drag the following signals:
state(string) → Test State slotTalonFX
Position→ Position slotTalonFX
Velocity→ Velocity slotTalonFX
MotorVoltage→ Voltage slot
Select your analysis type (Simple, Elevator, or Arm)
Click Load and review the diagnostics
The Signal Logger automatically captures Position, Velocity, and MotorVoltage signals from your TalonFX motors - you don't need to manually log these values like you would with REV motors.
CTRE vs REV: Key Differences
Primary Logging
Phoenix Signal Logger (.hoot)
WPILib DataLog (.wpilog) + Status Logger (.revlog)
Log Callback
null (automatic)
Manual logging required
State Logging
SignalLogger.writeString()
Default WPILib logger
Conversion Required
Yes - Tuner X or owlet
Optional - .wpilog works, .revlog is better
Signal Quality
Higher (bypasses CAN latency)
Standard loop or higher with .revlog
Setup Required
Must start/stop SignalLogger
Status Logger runs automatically
Related Documentation
Last updated