Skip to content

Commit 146a5c0

Browse files
kevinzakkaaftersomemath
authored andcommitted
System identification toolbox for MuJoCo.
This resulted from a lengthy collaboration with @kevinzakka, @jonathanembleyriches, @nimrod-gileadi, @gizemozd, @quagla, and @yuval.
1 parent be93e8e commit 146a5c0

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+10711
-0
lines changed

python/build_requirements.txt

Lines changed: 663 additions & 0 deletions
Large diffs are not rendered by default.

python/mujoco/sysid/README.md

Lines changed: 637 additions & 0 deletions
Large diffs are not rendered by default.

python/mujoco/sysid/__init__.py

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
# Copyright 2026 DeepMind Technologies Limited
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
# ==============================================================================
15+
"""Practical system identification for MuJoCo."""
16+
17+
from mujoco.sysid._src import model_modifier as model_modifier
18+
from mujoco.sysid._src import parameter as parameter
19+
from mujoco.sysid._src import plotting as plotting
20+
from mujoco.sysid._src import signal_modifier as signal_modifier
21+
from mujoco.sysid._src.io import save_results as save_results
22+
from mujoco.sysid._src.model_modifier import InertiaType as InertiaType
23+
from mujoco.sysid._src.model_modifier import apply_body_inertia as apply_body_inertia
24+
from mujoco.sysid._src.model_modifier import apply_dgain as apply_dgain
25+
from mujoco.sysid._src.model_modifier import (
26+
apply_param_modifiers as apply_param_modifiers,
27+
)
28+
from mujoco.sysid._src.model_modifier import (
29+
apply_param_modifiers_spec as apply_param_modifiers_spec,
30+
)
31+
from mujoco.sysid._src.model_modifier import apply_pdgain as apply_pdgain
32+
from mujoco.sysid._src.model_modifier import apply_pgain as apply_pgain
33+
from mujoco.sysid._src.model_modifier import body_inertia_param as body_inertia_param
34+
from mujoco.sysid._src.model_modifier import remove_visuals as remove_visuals
35+
from mujoco.sysid._src.optimize import calculate_intervals as calculate_intervals
36+
from mujoco.sysid._src.optimize import optimize as optimize
37+
from mujoco.sysid._src.parameter import Parameter as Parameter
38+
from mujoco.sysid._src.parameter import ParameterDict as ParameterDict
39+
from mujoco.sysid._src.plotting import plot_sensor_comparison as plot_sensor_comparison
40+
from mujoco.sysid._src.plotting import render_rollout as render_rollout
41+
from mujoco.sysid._src.residual import BuildModelFn as BuildModelFn
42+
from mujoco.sysid._src.residual import CustomRolloutFn as CustomRolloutFn
43+
from mujoco.sysid._src.residual import ModifyResidualFn as ModifyResidualFn
44+
from mujoco.sysid._src.residual import build_residual_fn as build_residual_fn
45+
from mujoco.sysid._src.residual import (
46+
construct_ts_from_defaults as construct_ts_from_defaults,
47+
)
48+
from mujoco.sysid._src.residual import model_residual as model_residual
49+
from mujoco.sysid._src.residual import residual as residual
50+
from mujoco.sysid._src.signal_modifier import apply_bias as apply_bias
51+
from mujoco.sysid._src.signal_modifier import apply_delay as apply_delay
52+
from mujoco.sysid._src.signal_modifier import (
53+
apply_delayed_ts_window as apply_delayed_ts_window,
54+
)
55+
from mujoco.sysid._src.signal_modifier import apply_gain as apply_gain
56+
from mujoco.sysid._src.signal_modifier import (
57+
apply_resample_and_delay as apply_resample_and_delay,
58+
)
59+
from mujoco.sysid._src.signal_modifier import get_sensor_indices as get_sensor_indices
60+
from mujoco.sysid._src.signal_modifier import normalize_residual as normalize_residual
61+
from mujoco.sysid._src.signal_modifier import weighted_diff as weighted_diff
62+
from mujoco.sysid._src.signal_transform import SignalTransform as SignalTransform
63+
from mujoco.sysid._src.timeseries import SignalType as SignalType
64+
from mujoco.sysid._src.timeseries import TimeSeries as TimeSeries
65+
from mujoco.sysid._src.trajectory import ModelSequences as ModelSequences
66+
from mujoco.sysid._src.trajectory import SystemTrajectory as SystemTrajectory
67+
from mujoco.sysid._src.trajectory import create_initial_state as create_initial_state
68+
from mujoco.sysid._src.trajectory import sysid_rollout as sysid_rollout
69+
from mujoco.sysid.report.defaults import default_report as default_report
70+
from mujoco.sysid.report.defaults import (
71+
default_report_matplotlib as default_report_matplotlib,
72+
)

python/mujoco/sysid/_src/__init__.py

Whitespace-only changes.

python/mujoco/sysid/_src/io.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
"""I/O utilities for saving system identification results."""
2+
3+
import os
4+
import pathlib
5+
import pickle
6+
from collections.abc import Sequence
7+
8+
import scipy.optimize as scipy_optimize
9+
from absl import logging
10+
11+
from mujoco.sysid._src import parameter
12+
from mujoco.sysid._src.optimize import calculate_intervals
13+
from mujoco.sysid._src.trajectory import ModelSequences
14+
15+
16+
def save_results(
17+
experiment_results_folder: str | os.PathLike,
18+
models_sequences: Sequence[ModelSequences],
19+
initial_params: parameter.ParameterDict,
20+
opt_params: parameter.ParameterDict,
21+
opt_result: scipy_optimize.OptimizeResult,
22+
residual_fn,
23+
):
24+
experiment_results_folder = pathlib.Path(experiment_results_folder)
25+
if not experiment_results_folder.exists():
26+
experiment_results_folder.mkdir(parents=True, exist_ok=True)
27+
logging.info("Experiment results will be saved to %s", experiment_results_folder)
28+
29+
initial_params.save_to_disk(experiment_results_folder / "params_x_0.yaml")
30+
opt_params.save_to_disk(experiment_results_folder / "params_x_hat.yaml")
31+
32+
with open(os.path.join(experiment_results_folder, "results.pkl"), "wb") as handle:
33+
pickle.dump(opt_result, handle, protocol=pickle.HIGHEST_PROTOCOL)
34+
35+
# TODO: these intervals should be part of the params object.
36+
residuals_star, _, _ = residual_fn(opt_result.x, opt_params, return_pred_all=True)
37+
covariance, intervals = calculate_intervals(residuals_star, opt_result.jac)
38+
with open(os.path.join(experiment_results_folder, "confidence.pkl"), "wb") as handle:
39+
pickle.dump(
40+
{"cov": covariance, "intervals": intervals},
41+
handle,
42+
protocol=pickle.HIGHEST_PROTOCOL,
43+
)
44+
45+
# Dump identified models to disk.
46+
for model_sequences in models_sequences:
47+
model_sequences.spec.to_file(
48+
(experiment_results_folder / f"{model_sequences.name}.xml").as_posix()
49+
)
50+
51+
# Log nominal compared to initial.
52+
x0 = initial_params.as_vector()
53+
x_nominal = initial_params.as_nominal_vector()
54+
logging.info(
55+
"Initial Parameters\n%s",
56+
initial_params.compare_parameters(x0, opt_result.x, measured_params=x_nominal),
57+
)

0 commit comments

Comments
 (0)