Given a MuJoCo model and recorded sensor data, find parameters that make simulation match reality. By default, the library uses nonlinear least-squares with box constraints to minimize the difference between measured and simulated (predicted) outputs. Residuals can be modified by static or optimized parameters, such as weights and time-delays.
The optimizer uses Gauss-Newton with finite-difference Jacobians. Each
parameter perturbation requires an independent simulation rollout. All
of them execute in a single batched call to mujoco.rollout, parallelized
across threads.
You provide:
- One or more
ModelSequenceseach bundling a singleMjSpecwith one or more sequences of measured data. All will be optimized jointly. - A
ParameterDictdefining differentiableParameters with bounds. - Callbacks to apply
Parameters to anMjSpec(individually or jointly) - (optional) Functions (
build_model,custom_rollout,modify_residual) that override the default residual function behavior.
The framework:
- Composes user code into a residual function (
build_residual_fn). - Optimizes parameters via batched parallel rollouts (
optimize). - Saves results and generates an HTML report (
save_results,default_report).
You can optimize any parameter that differentiably modifies the final residuals. Common use cases include:
Physics parameters settable on MjSpec. Most parameters in MjSpec
can be easily set directly by user-provided callbacks:
| Target | Approach |
|---|---|
| Contact sliding friction | spec.pair("cp").friction[0] = p.value[0] |
| Joint damping | spec.joint("j1").damping = p.value[0] |
Convenience functions are provided for common system identification parameterizations that cannot be trivially applied to an MjSpec:
| Target | Approach |
|---|---|
| Body mass | body_inertia_param(..., InertiaType.Mass) |
| Body mass + center of mass | body_inertia_param(..., InertiaType.MassIpos) |
| Full inertia (10-D) | body_inertia_param(..., InertiaType.Pseudo) |
| Actuator P/D gains | apply_pdgain(spec, "act1", p.value) |
Full inertia uses the pseudo-inertia Cholesky parameterization (Rucker & Wensing 2022), guaranteeing physical consistency without singularities.
Measurement parameters such as sensor delays, gains, and biases are properties of the measurement system, not the physics model. The library provides utilities for applying these corrections to the residual after rollout. They are functionally complete but their API is not yet final.
import mujoco
from mujoco import sysid
# 1. Load model and define parameters.
spec = mujoco.MjSpec.from_file("robot.xml")
model = spec.compile()
def set_link1_mass(spec, p):
spec.body("link1").mass = p.value[0]
params = sysid.ParameterDict()
params.add(sysid.Parameter(
"link1_mass", nominal=2.0, min_value=0.5, max_value=5.0,
modifier=set_link1_mass))
# 2. Load and package measured data.
# arrays assumed to be in MuJoCo order, otherwise pass names argument
control = sysid.TimeSeries.from_control_names(times, ctrl_array, model)
measureddata = sysid.TimeSeries.from_names(times, measurement_array, model)
initial_state = sysid.create_initial_state(model, qpos_0, qvel_0)
ms = sysid.ModelSequences("robot", spec, "traj_1", initial_state, control, measureddata)
# 3. Build residual, optimize, save.
residual_fn = sysid.build_residual_fn(models_sequences=[ms])
opt_params, opt_result = sysid.optimize(initial_params=params, residual_fn=residual_fn)
sysid.save_results("results/", [ms], params, opt_params, opt_result, residual_fn)default_report generates an interactive HTML report with videos, measurement comparisons,
parameter tables, and confidence intervals.
