mjlab/docs/source/changelog.rst
Upstream Snapshot 32a241c28f
Some checks failed
nightly / Test against latest dependencies (py3.10) (push) Has been cancelled
nightly / Test against latest dependencies (py3.13) (push) Has been cancelled
tests / tests (3.13, locked) (push) Has been cancelled
tests / tests (3.13, unlocked) (push) Has been cancelled
tests / pyright (3.10) (push) Has been cancelled
tests / lint-format (push) Has been cancelled
tests / tests (3.10, locked) (push) Has been cancelled
tests / tests (3.11, locked) (push) Has been cancelled
tests / tests (3.12, locked) (push) Has been cancelled
tests / pyright (3.11) (push) Has been cancelled
tests / pyright (3.12) (push) Has been cancelled
tests / pyright (3.13) (push) Has been cancelled
tests / ty-check (3.10) (push) Has been cancelled
tests / ty-check (3.11) (push) Has been cancelled
tests / ty-check (3.12) (push) Has been cancelled
tests / ty-check (3.13) (push) Has been cancelled
tests / stubs (push) Has been cancelled
tests / smoke-test (push) Has been cancelled
Docker / check_paths (push) Has been cancelled
docs / build (push) Has been cancelled
Docker / build (push) Has been cancelled
Import upstream snapshot c19f713c415a699a79d71cd96aa13c3104a05047
Upstream: https://github.com/michaelgillett/mjlab
Upstream-Commit: c19f713c415a699a79d71cd96aa13c3104a05047
Upstream-Branch: main
2026-08-28 15:42:17 +08:00

1045 lines
58 KiB
ReStructuredText

=========
Changelog
=========
Upcoming version (not yet released)
-----------------------------------
Changed
^^^^^^^
- Bumped ``rsl-rl-lib`` from 5.4.2 to 5.5.0. This update removes the ``logger_type``
attribute of the ``rsl_rl.utils.Logger``, so code that previously checked
``logger.logger_type`` must instead check the type of ``logger.writer``.
Version 1.6.0 (August 8, 2026)
------------------------------
.. admonition:: Breaking API changes
:class: attention
- ``CollisionCfg`` now requires ``contype``, ``conaffinity``, ``condim``,
and ``priority`` to be explicit instead of silently defaulting to
MuJoCo's values, and dict values for these fields must cover every
matched geom (add a catch-all ``".*"`` entry).
- ``CommandTerm._update_command`` now takes an ``env_ids`` argument:
``None`` on the regular per-step update and the reset environment ids
when called from ``reset()``. Custom command terms must add the
parameter (construction raises a ``TypeError`` with migration
instructions otherwise) and scope any per-step state advance, such as
a motion frame index, to ``env_ids``.
- ``ViewerConfig`` is now keyword-only; positional construction no
longer works.
.. admonition:: Highlights
:class: note
- Upgraded to MuJoCo and MuJoCo Warp 3.11.
- Upgraded ``rsl-rl-lib`` to 5.4.2.
Added
^^^^^
- Added ``GeomCfg``, exposed as the ``geoms`` field on ``EntityCfg``, a spec
editor that matches geoms by name and patches their attributes. Supports
``group`` (so a geom can collide without being drawn) and all collision
attributes; unset attributes are left untouched. Contribution by
@bd-pmorais.
- Added ``diffuse``, ``specular``, ``ambient``, ``active``, and
``attenuation`` fields to ``LightCfg`` for configuring light color and
falloff. Contribution by @bd-pmorais.
- Added ``random``, ``file``, ``cubefiles``, ``gridsize``, ``gridlayout``,
``nchannel``, ``hflip``, and ``vflip`` fields to ``TextureCfg``, so textures
can be loaded from image files instead of only built-in patterns.
``width`` and ``height`` are now optional, since file-based textures take
their size from the image. Contribution by @bd-pmorais.
- Added light domain randomization functions: ``dr.light_diffuse``,
``dr.light_specular``, ``dr.light_ambient``, ``dr.light_attenuation``,
``dr.light_cutoff``, and ``dr.light_exponent``. Contribution by @bd-pmorais.
- Added ``reduce="sum"`` to ``MetricsTermCfg`` for reporting the accumulated
episode total (e.g. episodic reward, total distance traveled) instead of a
per-step average. Contribution by @bd-mlutter
- Added ``ViewerConfig.geom_group`` and ``ViewerConfig.site_group`` to
control which geom and site visualization groups the offscreen renderer
draws. Defaults match MuJoCo's (groups 0 through 2), so rendering is
unchanged unless configured. Contribution by @bd-mlutter.
- Added ``dr.mat_texid`` to randomize which texture fills a given
``mjtTextureRole`` slot (RGB by default) of each selected material,
sampling uniformly from ``asset_cfg.texture_names``. Contribution by
@bd-pmorais.
.. figure:: _static/changelog/mat_texid_dr.gif
:width: 30%
Changed
^^^^^^^
- Bumped ``mujoco`` and ``mujoco-warp`` from 3.10 to 3.11, and regenerated the
bundled MuJoCo type stubs.
- Bumped ``rsl-rl-lib`` from 5.4.0 to 5.4.2.
- ``CollisionCfg`` and ``GeomCfg`` now share one write path, and mjlab warns
when a ``GeomCfg`` collision patch is overwritten by a ``CollisionCfg``.
- Changed the default MuJoCo Warp render background to solid black
(``0, 0, 0, 1``), matching MuJoCo's native renderer. Contribution by
@bd-pmorais.
- The offscreen renderer now works on a copy of the ``MjModel``, so its
render-only tweaks (extent, shadows, reflections, offscreen size) no
longer leak into the shared model. Contribution by @bd-mlutter.
- ``ViewerConfig`` is now keyword-only, with fields grouped and documented.
Contribution by @bd-mlutter.
- ``ViewerConfig.fovy`` now also applies to the ``ASSET_ROOT`` and
``ASSET_BODY`` tracking cameras instead of being silently ignored; leave
it at ``None`` (the default) to keep the model value. Contribution by
@bd-mlutter.
- ``auto_reset`` and an explicit ``reset()`` now leave identical command and
event timer state (:issue:`1133`).
Fixed
^^^^^
- The Viser motion scrubber's Start Here button no longer computes relative
body poses from stale pre-scrub kinematics, which could spuriously
terminate the episode on the next step.
- Mid-episode lifting command resamples now refresh kinematics and the
multi-cube reward cache, so observations and rewards no longer see
pre-teleport object positions for one step after each resample.
- ``UniformVelocityCommand``'s ``init_velocity_prob`` path no longer writes
the previous episode's terminal pose back into the sim on reset. It read
derived kinematics before ``forward()`` ran and rewrote the root pose; it
now reads only qpos for the orientation and writes only the root velocity.
- ``init_velocity_prob`` now applies on episode reset only. A mid-episode
timer resample used to also teleport the root velocity, which ran after
``step()``'s forward and left velocity observations stale for one step.
- ``Entity.set_joint_position_target`` and its velocity/effort/tendon/site
siblings now select the outer product when both ``env_ids`` and the element
ids are tensors, instead of pairing them elementwise.
- ``MotionCommand`` now refreshes kinematics after a timer-expiry resample
(finite ``resampling_time_range``), matching its wraparound path.
- ``CircularBuffer`` lag retrieval now clamps to the oldest retained frame;
a lag beyond the buffer length used to wrap around to a newer frame.
- Camera sensor caches are now invalidated after ``sense()``, so a
pre-sense read with ``clone_data=True`` can no longer pin the previous
step's frame into the observations (mirrors the raycast fix for
:issue:`998`).
- ``reset(env_ids=...)`` no longer appends a frame to every env's observation
history and delay buffers; only the reset envs receive their post-reset
frame. Previously, each manual partial reset gave the other envs a duplicate
frame, shortening their effective history and drifting their delay
schedules.
- ``reset(env_ids=...)`` no longer advances stateful commands in
environments that were not reset. Previously a partial reset gave every
environment an extra command update, so with ``auto_reset=False`` a
``MotionCommand`` reference motion played at twice the normal speed and
could teleport non-reset robots via the wraparound resample. The adaptive
sampling EMA also no longer folds on resets, matching auto-reset
training dynamics. :issue:`1138`
- Interval event timers are now resampled on episode reset for function-based
terms, as documented. Previously the countdown carried across episodes, so a
new episode's first ``push_robot`` in the velocity and tracking tasks could
fire arbitrarily soon after spawn; with mixed function and class interval
terms, reset also wrote into the wrong timer slots. Note this changes push
timing relative to earlier training runs, and an interval term whose range
exceeds the episode length is now re-armed on every reset and will never
fire.
- ``RayCastSensorCfg.include_geom_groups`` now raises on values outside
``[0, mjNGROUP)`` instead of silently excluding every geom.
- Geoms with a negative group no longer pick up group 5's visibility toggle in
the Viser viewer.
Version 1.5.3 (July 22, 2026)
-----------------------------
Changed
^^^^^^^
- The Viser reward bar panel's term cap is now configurable via
``ViewerConfig.reward_bar_max_terms``, so environments with more than 20
reward terms can show them all. Defaults to 20, preserving previous behavior.
:issue:`1079`
Fixed
^^^^^
- Bumped ``pillow`` (12.3.0), ``onnx`` (1.22.0), and ``soupsieve`` (2.9.1) in the
lockfile to pick up security fixes.
- Fixed raycast sensor debug visualization and observations lagging one step
behind the sensed hits. ``sense()`` rebinds the hit tensors after the cache had
already been repopulated by a pre-sense reward read, so ``.data`` returned the
previous step's hits; the cache is now invalidated after ``postprocess_rays``.
With ``ray_alignment="yaw"`` this made debug rays appear tilted by the foot's
per-step motion instead of vertical. :issue:`998`
- Restored ONNX uploads and W&B run metadata for velocity and manipulation
training when using RSL-RL's current ``WandbLogWriter`` logger name.
- The Viser reward bar panel no longer *silently* drops reward terms beyond
``max_terms``; it now emits a warning listing the hidden terms. Previously
environments with more than 20 reward terms had the overflow disappear from
the bar panel with no indication. :issue:`1079`
- Fixed the ``terrain_levels_vel`` curriculum promoting every env from level 0
to level 1 on the initial reset, ignoring ``max_init_terrain_level=0``. Before
the first step the robot sits at its spawn pose rather than a walked-to
position, so the distance check was spurious; terrain levels are now frozen on
that first reset. :issue:`1094`
- Fixed the velocity task's actor ``joint_pos`` observation not being biased by
the ``encoder_bias`` domain randomization, so the encoder bias only affected
actions and never the observed joint positions. The actor now observes biased
joint positions while the critic keeps the true (unbiased) values as
privileged information, matching the tracking task.
See `discussion #1065 <https://github.com/mujocolab/mjlab/discussions/1065>`_.
- Hardened ``fit_terrain_normal`` against non-finite raycast hits. A single env
with a diverged state produced a NaN/Inf covariance that made
``torch.linalg.eigh`` raise and abort the whole batch; such rows now fall back
to the up vector. This stops the hard crash so a diverged env can be reset
normally; it does not by itself make a diverged env's downstream reward finite.
:issue:`912`
- Enabled ``obs_normalization`` on the Go1 velocity actor and critic to match
the other velocity tasks. Without it, extreme-but-finite observations on rough
terrain drove value/policy divergence that eventually surfaced as a
``normal expects all elements of std >= 0.0`` crash. Note that Go1 velocity
checkpoints trained before this change carry no normalizer buffers and will no
longer load; retrain from scratch. :issue:`870` :issue:`1044` :issue:`1053`
- Fixed ``ContactSensor`` air-time tracking accumulating float32 sim-clock
differences, whose quantization error grows with the clock magnitude and made
``compute_first_contact`` / ``compute_first_air`` miss touchdowns on long runs.
The exact float64 substep ``dt`` is now accumulated instead. :issue:`1101`
- Bumped ``mujoco-warp`` to 3.10.0.3, fixing a CUDA 700 illegal memory access in
``smooth.crb`` triggered by startup mass domain randomization (via
``set_const``) once ``num_envs >= 128`` on consumer Ada GPUs. :issue:`1108`
Version 1.5.2 (July 17, 2026)
-----------------------------
Fixed
^^^^^
- Fixed CUDA illegal memory accesses when domain randomization triggers
``set_const`` with multiple environments. ``actuator_acc0`` is now expanded
per environment before MuJoCo Warp recomputes it.
- Fixed ``MaterialCfg.reflectance`` being ignored when building the MuJoCo
spec. Contribution by @bd-pmorais.
Version 1.5.1 (July 15, 2026)
-----------------------------
Added
^^^^^
- Added ``MeshCfg``, a spec editor that matches mesh assets by name and edits
their asset-level attributes. The first attribute is ``maxhullvert``, which
caps the collision convex hull's vertex count to lower narrowphase cost.
- Added ``SimulationCfg.broadphase`` and ``SimulationCfg.broadphase_filter``
to configure MuJoCo Warp's broadphase collision algorithm and
bounding-volume filters.
Changed
^^^^^^^
- Enabled skybox rendering for camera sensors. Contribution by @bd-pmorais.
- Bumped the minimum ``mujoco-warp`` to 3.10.0.2, which fixes ``qfrc_constraint``
being populated incorrectly across vectorized environments (:issue:`1086`).
Earlier 3.10.0.x releases are no longer supported.
- Command delay on fusable actuators (ideal PD, DC motor) now applies one shared
lag per environment across all fused actuators sharing a delay config, matching
the built-in actuator path, rather than an independent lag per actuator group
(:issue:`1035`).
Fixed
^^^^^
- Fixed ``TerrainGenerator`` overwriting custom geom names set by sub-terrain
functions with the default ``terrain_{i}`` name. Only unnamed geoms are now
auto-named.
- Fixed ``TorchArray`` not expanding world-shared model fields to ``nworld``
with mujoco_warp 3.10.0.2, which allocates them as real size-1 arrays
instead of stride-0 broadcast views. Multi-env indexing of fields like
``soft_joint_pos_limits`` raised ``IndexError`` during resets (:issue:`1093`).
- Fixed ``mdp.bad_orientation`` returning NaN when float32 rounding in
``quat_apply_inverse`` pushed the projected-gravity z-component slightly
outside ``[-1, 1]``, making ``torch.acos`` return NaN and silently
suppressing the termination for flipped robots. The argument is now clamped
to ``[-1, 1]``.
- Fixed a crash when using command delay on ideal PD (or other custom)
actuators whenever ``num_envs`` differed from the number of delayed targets,
and fused ideal PD and DC motor actuators sharing a transmission and delay
config into a single gather, delay, control-law evaluation, and control
write, removing per-group host overhead (:issue:`1035`).
Version 1.5.0 (June 28, 2026)
-----------------------------
Added
^^^^^
- Added ``reduce="max"`` to ``MetricsTermCfg`` for reporting episode-peak values
(e.g. peak power, peak contact force) without needing stateful wrapper classes.
- Added ``BuiltinDcMotorActuator``, a native MuJoCo ``<dcmotor>`` wrapper.
Supports voltage / position / velocity input modes with back-EMF,
configurable motor constants, and optional integral, slew, inductance,
thermal, LuGre, and cogging extensions.
- Added ``scale_with_difficulty`` to ``HfRandomUniformTerrainCfg``. When
enabled, the noise amplitude scales with difficulty (flat at 0, full
``noise_range`` at 1) so the terrain progresses in a curriculum. Defaults to
``False``, preserving the previous difficulty-independent behavior.
- Added material domain randomization functions for MuJoCo Warp RGB rendering:
``dr.mat_emission``, ``dr.mat_specular``, ``dr.mat_shininess``, and
``dr.mat_texrepeat``.
Changed
^^^^^^^
- Bumped ``rsl-rl-lib`` from 5.2.0 to 5.4.0.
- Bumped ``mujoco`` and ``mujoco-warp`` to 3.10, both pinned from PyPI. The
``py.mujoco.org`` nightly index and the ``mujoco-warp`` git pin are dropped, so
resolution no longer breaks when nightly wheels are garbage-collected.
.. warning::
``SimulationCfg.ls_parallel`` is deprecated and now ignored, since parallel
linesearch was removed upstream in MuJoCo Warp. Setting it emits a
``DeprecationWarning``; remove it from any ``SimulationCfg`` you construct.
- Curriculum-mode terrain difficulty is now deterministic across rows
and reaches the configured ``difficulty_range`` endpoints
(:issue:`1027`).
- Heightfield terrains now color by absolute height with a diverging palette
(cool below the ground plane, green at ground level, warm above) on a fixed
scale, replacing the per-patch normalization. Color is now consistent across
terrains, and low-amplitude terrain such as ``random_rough`` reads as gently
tinted ground instead of high-contrast noise.
- ``BoxNestedRingsTerrainCfg`` now builds uniform-height concentric ridges
whose separating gaps widen with difficulty, replacing the random per-ring
heights. Rings are colored by height (like the other terrains) and the outer
border matches the ring height.
- Terrain generation no longer prints timing information to stdout.
Fixed
^^^^^
- Fixed domain randomization events that target different ``axes`` of the same
model field (e.g. two ``dr.geom_size`` events scaling axis 0 and axis 1
separately) silently clobbering each other. Each event now writes back only
the axes it targeted, so per-axis events compose (:issue:`1042`).
- Regenerated the bundled MuJoCo type stubs, which had drifted from the
installed mujoco version. CI now regenerates them and fails if they are
stale, so they stay in sync going forward. Run ``make stubs`` to update them
(:issue:`1048`).
- Fixed ``select_gpus`` crashing when ``CUDA_VISIBLE_DEVICES`` contains MIG
UUIDs instead of numeric indices.
- Fixed pyramid-stairs terrains (``BoxPyramidStairsTerrainCfg``,
``BoxInvertedPyramidStairsTerrainCfg``, and ``BoxOpenStairsTerrainCfg``)
leaving an empty, geometry-free border at difficulty 0, where the step
height collapses to zero. The flat border frame is now always generated as
solid geometry flush with the ground (:issue:`1033`).
- Fixed ``HfPerlinNoiseTerrainCfg`` failing to compile at difficulty 0, where
the target height collapses to zero and MuJoCo rejects the non-positive
heightfield size.
- Fixed ``BoxRandomGridTerrainCfg`` producing NaN colors (and failing to build)
at difficulty 0, where the grid height is zero and the color normalization
divided by zero.
- Fixed the center platform z-fighting with surrounding geometry in
``BoxRandomGridTerrainCfg`` (grid cells were left underneath the platform) and
``BoxRandomSpreadTerrainCfg`` (the platform duplicated the floor surface).
- Fixed ``BoxNarrowBeamsTerrainCfg`` square platform corners protruding between
the beams at high difficulty; the platform now shrinks to stay within the
beams' angular coverage.
- Fixed ``BoxSteppingStonesTerrainCfg`` reconfiguring abruptly at a difficulty
threshold, where the stone grid re-tiled as its spacing crossed an integer
boundary, and leaving an oversized gap around the center platform. The grid is
now difficulty-independent and the platform snaps to it as a clean island.
- Fixed ``train --video``, ``play``, and ``demo`` crashing with ``OpenGL
platform library not loaded`` on headless Linux hosts that don't pre-set
``MUJOCO_GL``. The default is now applied in ``mjlab/__init__.py`` (Linux
only) so it takes effect before mujoco's GL backend selection runs.
- Fixed motion tracking re-anchoring to a stale robot pose after a mid-episode
motion resample. ``MotionCommand._update_command`` now calls ``sim.forward()``
after resampling so relative body poses read the post-teleport state
(:issue:`1068`).
Version 1.4.0 (May 26, 2026)
----------------------------
Added
^^^^^
- Added ``BuiltinPdActuator``, the implicit-integration version of
``IdealPdActuator``. Same interface (position + velocity targets,
kp/kd gains), but expresses the PD as native MuJoCo ``<position>``
and ``<velocity>`` elements so the ``implicit`` / ``implicitfast``
integrators include the kp/kd derivatives in their velocity update.
The actuator stays stable at gain/timestep combinations where
explicit Python PD would diverge, which matters when you want to
run a real motor's stiff on-board PD gains in sim. ``effort_limit``
is enforced as a sum-clamp on the two PD terms via
``jnt_actfrcrange`` (or ``tendon_actfrcrange``). Supported by
``dr.pd_gains`` and ``dr.effort_limits``.
- Added ``mdp.projected_gravity_from_sensor``, an observation that derives
projected gravity from a ``framezaxis`` up-vector sensor (negated) rather
than from the root body orientation. Unlike ``mdp.projected_gravity``, it
reflects the sensor's site frame, so it can observe IMU mounting domain
randomization (e.g. via ``dr.site_quat``). Go1 and G1 ship an
``imu_upvector`` sensor for this.
- Added ``DebugVisualizer.add_box`` for drawing an axis-oriented box
primitive, mirroring ``add_ellipsoid``. Supported by both the native
and Viser viewers. ``size`` is the box half-extents (:issue:`992`).
- Added ``--log-root`` CLI option to ``train``, ``play``, and ``evaluate``
scripts for choosing where training logs are stored. Defaults to
``logs/rsl_rl`` (unchanged behavior). Useful for directing outputs to a
scratch disk or shared mount.
- ``RewardManager``, ``TerminationManager``, and ``MetricsManager`` now
validate that every term function returns a tensor of shape
``(num_envs,)`` when evaluated, raising a clear ``ValueError``
naming the offending term instead of silently broadcasting or crashing
with an opaque error later during training.
- Added ``ContactSensor.primary_names`` property to expose the resolved
primary names in the order they appear along the per-contact axis of the
output tensors. This makes it possible to map a contact-data column back
to the primary it belongs to (:issue:`914`).
- Added per-world mesh variant support via ``VariantEntityCfg``. Each
world in a batched simulation can now use a different mesh asset for
the same logical entity (e.g. world 0 holds a cube, world 1 a
sphere). Variants are passed as a ``dict[str, Callable]`` of named
spec callables; the optional ``assignment`` field controls how worlds
map to variants and accepts ``None`` (uniform), a ``dict[str, float]``
of per-variant weights, or a custom ``Callable[[int], Sequence[int]]``.
Mesh-derived constants (collision bounds, body inertials, subtree
mass, inverse weights) are compiled per-variant and stored as
per-world arrays in the Warp model, so domain randomization, the
native viewer, the offscreen renderer, and the Viser viewer all pick
up the variant assignment automatically. Variants must share the
same kinematic structure (same bodies, joints, joint types); only
mesh geoms may differ. Assignment is fixed at simulation init. See
:ref:`heterogeneous_worlds` for usage. With help from @XiangruiJiang.
- Per-world mesh variants now support per-variant materials and textures.
Each variant can reference its own named material, which is automatically
prefixed and scattered via ``geom_matid`` alongside the existing
``geom_dataid`` table. Variants without a material get ``matid = -1``.
Contribution by @omarrayyann.
- Added ``dr.geom_matid`` to randomize which baked material each geom uses
per environment, sampling uniformly from ``asset_cfg.material_names``.
Contribution by @bd-pmorais.
Changed
^^^^^^^
- ``Entity`` now raises a clear error at construction when its spec contains
more than one freejoint. An entity models a single system rooted at one
body, so it has at most one freejoint; a second one was previously accepted
silently and only surfaced later as a cryptic shape mismatch when writing
root state. Model each detached floating body as its own entry in
``SceneCfg.entities`` instead.
- Changed ``compute_root_relative_mpkpe`` to re-anchor the reference to the
robot's root each step, removing yaw drift as well as translation so it
measures intrinsic body pose error.
- Changed ``compute_joint_velocity_error`` from an L2 norm to a per-joint
RMS, so it no longer scales with the number of joints.
- Bumped ``mujoco`` to 3.8 and ``mujoco-warp`` to 3.8.0. The ``multiccd``
enable flag was removed in mujoco 3.8 (it became default-on), so configs
that listed ``"multiccd"`` in ``MujocoCfg.enableflags`` need to drop it.
- Camera segmentation now matches ``mujoco_warp``'s typed segmentation
output. ``CameraSensorData.segmentation`` stores ``(object_id,
object_type)`` pairs in shape ``[B, H, W, 2]`` instead of the previous
legacy geom-id-only layout. Contribution by @tkelestemur.
- Sped up ``RayCaster`` post-processing by removing boolean-mask indexing
operations and replacing them with ``masked_fill_`` plus a clamped-distance
formulation of ``hit_pos_w`` that places misses at the world origin. This
removes all CUDA syncs from the ray post-process, letting the CPU thread
proceed while GPU-based sensing runs. Contribution by @bd-pdomanico.
- Bumped ``rsl-rl-lib`` from 5.0.1 to 5.2.0. This brings ``torch.compile`` support for
PPO and Distillation, and optional std clamping and constant std in
``GaussianDistribution``. No code changes required on the mjlab side.
- ``TerrainEntityCfg`` debug visualization sites (environment origins,
terrain origins, flat patches) are now off by default. Set
``debug_vis=True`` to re-enable them. The sites inflated ``nsite`` and
caused a measurable slowdown in the per-step ``site_local_to_global``
kernel (:issue:`942`).
- Task package load failures during ``mjlab`` import now print the full
traceback (and the entry point's module path) to ``stderr`` instead of
just the exception message, making it easier to pinpoint the source of
import errors when running commands like ``list-envs`` (:issue:`910`).
Contribution by @saikishor.
- Clarified ``ContactSensor`` shape conventions: per-contact fields
(``found``, ``force``, ``torque``, ``dist``, ``pos``, ``normal``,
``tangent``) have shape ``[B, P * num_slots, ...]`` while per-primary
air-time fields (``current_air_time``, ``last_air_time``,
``current_contact_time``, ``last_contact_time``) have shape ``[B, P]``,
where ``P`` is the number of resolved primaries (:issue:`914`).
- Event functions now share a single ``resolve_env_ids`` helper to expand
``env_ids=None`` to all environments, replacing five copies of the same
guard. ``push_by_setting_velocity`` and ``apply_external_force_torque``
accept ``env_ids=None`` too, so they work as global-time interval terms.
Documented when to use ``apply_external_force_torque`` (a constant,
self-managed wrench) versus ``apply_body_impulse`` (transient, automatic
impulses) versus ``push_by_setting_velocity`` (an instantaneous velocity
kick).
Fixed
^^^^^
- Removed use of deprecated ``warp-lang`` symbols (``wp.context.runtime``
and ``wp.context.Device``) that were dropped in newer ``warp-lang``
releases, causing ``AttributeError: module 'warp' has no attribute
'context'`` at import/runtime. mjlab now uses
``wp.get_cuda_driver_version()`` and ``wp.Device`` instead
(:issue:`967`). Contribution by @rdeits.
- Fixed the tracking ``evaluate`` script scoring each metric against the
next motion frame; the reference is now snapshotted before each step to
match the reward.
- Fixed the tracking end-effector metrics silently scoring zero for an
unknown body name; they now raise ``ValueError``.
- Fixed ``compute_mpkpe`` measuring root-relative instead of global error;
it now uses the global reference ``body_pos_w`` (:issue:`1006`).
- Fixed heavy flicker in offscreen training videos on rough-terrain tasks.
The renderer recomputed its context "neighbor" robots every frame from
``env_origins``, which the terrain curriculum mutates on reset, so the
neighbor set kept changing and robots popped in and out. The neighbor
set is now computed once and cached (:issue:`979`).
- Fixed command delay only applying to an actuator's position target.
``IdealPdActuator`` and ``DcMotorActuator`` also use velocity and effort, which
arrived undelayed and out of sync; all command targets now share one delay.
Zero-reference setups are unaffected.
- Fixed duplicate random seeds across nodes in multi-node training. The
per-process seed offset in ``scripts/train.py`` now uses the global
``RANK`` instead of ``LOCAL_RANK``. Contribution by @bd-pdomanico.
- Fixed ``apply_body_impulse`` firing an impulse on the very first step (and
the first step after every reset) instead of starting with a cooldown as
documented. The cooldown is now sampled lazily on the first call so impulse
timing is decorrelated from episode resets (:issue:`973`).
- Fixed ``dr.pd_gains`` and ``dr.effort_limits`` silently no-oping when
passed an ``Operation`` object (e.g. ``dr.scale``) instead of a string.
Both functions now accept ``Operation | str`` like every other DR event
and raise ``ValueError`` for unsupported operations (:issue:`971`).
- Fixed ``ContactSensor`` with ``global_frame=True`` and
``reduce`` ∈ {``"none"``, ``"mindist"``, ``"maxforce"``} producing forces
rotated onto the wrong axis. The contact-frame→world rotation matrix had
its columns ordered ``[tangent, tangent2, normal]`` instead of
``[normal, tangent, tangent2]``, projecting the normal-force component
onto a tangent direction. Contribution by @bd-pdomanico.
- Fixed ``extras["log"]`` entries written by reward terms (e.g. ``Metrics/*``
values in velocity tasks) being silently discarded on any step where at
least one environment resets. ``_reset_idx`` was clearing the dict after
``reward_manager.compute()`` had already populated it. The clear now
happens at the top of ``step()`` and ``reset()`` so that all entries
survive (:issue:`957`).
- Fixed ``ContactSensor.compute_first_contact`` and ``compute_first_air``
occasionally missing events when a contact began or ended right at the
last physics substep of a control step. ``current_contact_time`` /
``current_air_time`` accumulate in float32 and can drift a few ULPs past
``dt``, but the default ``abs_tol`` of ``1e-8`` sat at the noise floor
and rejected the comparison. Raised the default to ``1e-6``, which stays
well below typical control ``dt`` while comfortably covering float32
accumulation noise (:issue:`933`). Contribution by @paLeziart.
- Fixed ``out_of_terrain_bounds`` using stale terrain dimensions. It read
``TerrainGeneratorCfg.num_cols`` directly, which is ignored in curriculum
mode (the generator uses ``len(sub_terrains)`` columns instead), and it
did not account for ``border_width``. The termination now reads the
effective grid shape from ``terrain.terrain_origins`` and includes the
border in the footprint, so robots no longer reset while still on valid
terrain (or fail to reset after running off it) (:issue:`923`).
- ``ObservationManager`` now skips observation groups that end up with
zero active terms (e.g. all terms set to ``None``) with a log message,
instead of crashing later in ``torch.stack``/``torch.cat``. This lets
a shared runner config define groups that become empty under certain
runtime flags (e.g. model-specific terms all disabled for one variant).
The whole group can still be set to ``None`` to disable it explicitly.
- Fixed a runtime broadcast error in ``ContactSensor`` when combining
``num_slots > 1`` with ``track_air_time=True`` and more than one primary.
Air-time tracking now reduces ``found`` across slots so that a primary is
considered in contact when any of its slots reports a match (:issue:`914`).
- Updated the ``create_new_task.ipynb`` Colab tutorial to import
``XmlActuatorCfg`` instead of the removed ``XmlVelocityActuatorCfg``.
Added a regression test (``tests/test_notebooks.py``) that parses each
notebook cell and verifies that every ``from mjlab... import X``
reference resolves, so future renames in the mjlab public API can't
silently rot the tutorials (:issue:`913`).
- Fixed ``ObservationManager`` silently sharing a single ``NoiseModelCfg``
instance across observation groups that declared terms with the same
name. ``_group_obs_class_instances`` was keyed by term name alone, so
the last group processed in ``_prepare_terms`` overwrote earlier
groups' instances. Symptoms included the wrong noise config being
applied, shared per-episode state for ``NoiseModelWithAdditiveBias``
(e.g. bias drawn from the wrong ``bias_noise_cfg``), and missed
``reset()`` calls for overwritten instances. Instances are now keyed
by ``(group_name, term_name)`` so each group owns its own noise model.
- Fixed ``CurriculumManager.get_active_iterable_terms`` raising
``TypeError`` when a term's state was a dict. The dict branch indexed
the output list by term name instead of appending to the local ``data``
list. No in-tree caller currently invokes this method, so the bug was
latent.
Version 1.3.0 (April 14, 2026)
------------------------------
Added
^^^^^
- Added ``ManagerBasedRlEnvCfg.auto_reset`` flag. When ``True`` (default),
``step()`` continues to reset done environments in place and returns the
post-reset observation. When ``False``, ``step()`` skips the reset block
and returns the terminal observation directly; the caller must call
``reset(env_ids=...)`` for done environments before the next ``step()``
or a ``RuntimeError`` is raised. Enables access to the true terminal
state for algorithms that need it. Note that mjlab's bundled ``train.py``
uses rsl_rl's ``OnPolicyRunner``, which does not drive manual resets, so
``auto_reset=False`` is intended for custom training loops (:issue:`900`).
- Added ``ActuatorCfg.viscous_damping`` for passive velocity proportional
damping (``f = -b·v``), distinct from the PD derivative gain ``damping``
used by position and velocity actuators. Maps to ``<joint damping>`` for
JOINT transmission and ``<tendon damping>`` for TENDON transmission.
Defaults to ``None`` (preserves the XML value).
- Added :class:`~mjlab.managers.RecorderManager` for logging observations,
actions, or arbitrary environment data during rollouts. Implement a
:class:`~mjlab.managers.RecorderTerm` subclass and register it in the
``recorders`` dict on ``ManagerBasedRlEnvCfg``. The manager provides
``record_pre_reset``, ``record_post_reset``, and ``record_post_step``
lifecycle hooks with no opinion on how data is stored.
- Added :func:`~mjlab.envs.mdp.curriculums.termination_curriculum` for
scheduling changes to termination term parameters during training,
matching the existing ``reward_curriculum`` pattern. Both now share a
single internal engine with init-time validation of stage ordering,
field existence, and param keys.
- Added ``reduce`` field to ``MetricsTermCfg``. Setting ``reduce="last"``
reports the value from the final step of the episode rather than the
episode mean, which is useful for binary success metrics.
- Added :class:`~mjlab.envs.mdp.actions.RelativeJointPositionAction` for
joint position control relative to the current configuration. The target is
``current_pos + action * scale``, so a zero action holds the current
configuration rather than commanding the default pose.
- Added :func:`~mjlab.envs.mdp.dr.pair_friction` for randomizing geom-pair
friction overrides (``pair_friction`` in ``mjModel``), with an
``isotropic=True`` option that mirrors the symmetric tangent and roll
axes so single-axis randomization does not leave the paired axis stale.
- Added ``STAIRS_TERRAINS_CFG`` terrain preset for progressive stair
curriculum training and ``@terrain_preset`` decorator for composing
terrain configurations from reusable presets.
- Added cartpole balance and swingup tasks (``Mjlab-Cartpole-Balance`` and
``Mjlab-Cartpole-Swingup``) with a :ref:`tutorial <tutorial-cartpole>`
that walks through building an environment from scratch.
- Added :ref:`motion imitation <motion-imitation>` documentation with
preprocessing instructions. The README now links here instead of the
BeyondMimic repository, which produced incompatible NPZ files when used
with mjlab (:issue:`777`).
- Added ``margin``, ``gap``, and ``solmix`` fields to ``CollisionCfg``
for per geom contact parameter configuration (:issue:`766`).
- NaN guard now captures mocap body poses (``mocap_pos``, ``mocap_quat``)
when the model has mocap bodies, enabling full state reconstruction in
the dump viewer for fixed-base entities.
- Implemented ``ActionTermCfg.clip`` for clamping processed actions after
scale and offset (:issue:`771`).
- Added ``qfrc_actuator`` and ``qfrc_external`` generalized force accessors
to ``EntityData``. ``qfrc_actuator`` gives actuator forces in joint space
(projected through the transmission). ``qfrc_external`` recovers the
generalized force from body external wrenches (``xfrc_applied``)
(:issue:`776`).
- Added ``RewardBarPanel`` to the Viser viewer, showing horizontal bars for
each reward term with a running mean over ~1 second (:issue:`800`).
- Added ``per_substep`` flag to ``MetricsTermCfg`` for evaluating metrics
once per physics substep inside the decimation loop. The per substep
values are averaged within each environment step, so episode averages
remain comparable to regular per step metrics.
- Added ``project-instinct/InstinctMJ`` to the research page's list of
projects built on mjlab.
- Added a Checkpoints tab to the Viser play viewer for hot-swapping
checkpoints without restarting. Works with local directories and W&B
runs (:issue:`751`). Contribution by @omarrayyann.
- Added ``"segmentation"`` camera data type for per-pixel geom ID output
alongside RGB and depth, and a multi-cube goal-conditioned lifting task
(``Mjlab-Multi-Cube-Seg-Yam``) that uses it (:issue:`862`).
Contribution by @pthangeda.
Changed
^^^^^^^
- Renamed the ``list_envs`` console script to ``list-envs`` for consistency
with the other hyphenated entry points (``viz-nan``, ``export-scene``).
Invoke via ``uv run list-envs``.
- ``ActuatorCfg.armature`` and ``ActuatorCfg.frictionloss`` now default to
``None`` instead of ``0.0``. ``None`` preserves the value defined in the
XML. Previously, builtin actuators would silently overwrite XML joint and
tendon properties with zero when these fields were not explicitly set.
To restore the old behavior, pass ``armature=0.0`` or ``frictionloss=0.0``
explicitly.
- Actuator delay is now configured inline on any ``ActuatorCfg`` subclass
(e.g. ``BuiltinPositionActuatorCfg(..., delay_min_lag=2, delay_max_lag=5)``)
instead of wrapping with ``DelayedActuatorCfg``. ``DelayedActuator``,
``DelayedActuatorCfg``, and ``DelayedBuiltinActuatorGroup`` are removed.
- Removed ``delay_target`` from ``ActuatorCfg``. Delay now always applies to
the actuator's ``command_field`` automatically. Multi-target delay
(``delay_target=("position", "velocity")``) is no longer supported.
- ``XmlPositionActuatorCfg``, ``XmlVelocityActuatorCfg``, ``XmlMotorActuatorCfg``,
and ``XmlMuscleActuatorCfg`` are replaced by a single ``XmlActuatorCfg`` that auto
detects the actuator type from XML. Pass ``command_field=...`` to override detection.
- Replaced the viser viewer internals with the ``mjviser`` package. Scene
creation, mesh conversion, and overlay rendering (contacts, forces,
inertia, tendons, joints, frames) are now provided by mjviser. The viewer
exposes a new Visualization tab for overlay controls and a Groups tab for
geom/site visibility. Debug visualization and warp tensor conversion remain
in mjlab's ``MjlabViserScene`` subclass (:issue:`839`).
- In curriculum terrain mode, each terrain type now gets exactly one column
(``num_cols`` is set to ``len(sub_terrains)``). The ``proportion`` field
now controls robot spawning distribution across columns rather than column
count. Random mode is unchanged (:issue:`811`).
- ``BoxSteppingStonesTerrainCfg`` stone size now decreases with difficulty,
interpolating from the large end of ``stone_size_range`` at difficulty 0
to the small end at difficulty 1 (:issue:`785`).
- Removed deprecated ``TerrainImporter`` and ``TerrainImporterCfg`` aliases.
Use ``TerrainEntity`` and ``TerrainEntityCfg`` instead (:issue:`667`).
- ``Entity.clear_state()`` is deprecated. Use ``Entity.reset()`` instead.
``clear_state`` only zeroed actuator targets without resetting actuator
internal state (e.g. delay buffers), which could cause stale commands
after teleporting the robot to a new pose.
- Removed ``EntityData.generalized_force``. The property was bugged (indexed
free joint DOFs instead of articulated DOFs) and the name was ambiguous.
Use ``qfrc_actuator`` or ``qfrc_external`` instead (:issue:`776`).
- ``get_wandb_checkpoint_path`` now filters checkpoints server-side via the
``pattern`` parameter, avoiding unnecessary pagination and tolerance to
corrupted metadata (:issue:`898`).
Fixed
^^^^^
- ``train`` and ``play`` now print a top-level usage message when invoked
with ``-h`` / ``--help`` and no task argument, pointing users at
``list-envs`` and ``<TASK> --help`` (:issue:`905`).
- Fixed ghost geom filtering in the Viser viewer. Ghost geoms were selected
by collision flags, so collision-disabled robot geoms appeared as ghosts.
The viewer now uses visual alpha to determine which geoms to render.
- Scene now warns when an attached entity or terrain spec has non-default
``<option>`` fields (e.g. ``<flag contact="disable"/>``), which are
silently dropped by ``MjSpec.attach()``. Use ``MujocoCfg`` to set
simulation options instead (:issue:`885`).
- Fixed ``SceneEntityCfg`` names and IDs ordering mismatch when
``preserve_order=False`` (:issue:`876`). Contribution by @jsw7460.
- Fixed ONNX export path resolution in the velocity, manipulation, and
tracking runners when a parent directory name contains the word
``"model"`` (:issue:`867`). Contribution by @gokulp01.
- ``export-scene`` now writes only referenced assets and places them
correctly under the output directory. Previously, asset keys containing
path traversal could write files outside the output directory, and all
spec assets were included regardless of whether the scene XML referenced
them (:issue:`858`).
- ``electrical_power_cost`` now uses ``qfrc_actuator`` (joint space) instead
of ``actuator_force`` (actuation space) for mechanical power computation.
Previously the reward was incorrect for actuators with gear ratios other
than 1 (:issue:`776`).
- ``create_velocity_actuator`` no longer sets ``ctrllimited=True`` with
``inheritrange=1.0``. This caused a ``ValueError`` for continuous joints
(e.g. wheels) that have no position range defined (:issue:`787`).
- ``write_root_com_velocity_to_sim`` no longer fails with tensor ``env_ids``
on floating base entities (:issue:`793`).
- Joint limits for unlimited joints are now set to [-inf, inf] instead of
[0, 0]. Previously the zero range caused incorrect clamping for entities
with unlimited hinge or slide joints.
- Contact force visualization now copies ``ctrl`` into the CPU ``MjData``
before calling ``mj_forward``. Actuators that compute torques in Python
(``DcMotorActuator``, ``IdealPdActuator``) previously showed incorrect
contact forces because the viewer ran with ``ctrl=0``
(:issue:`786`).
- ``BoxSteppingStonesTerrainCfg`` no longer creates a large gap around the
platform. Stones are now only skipped when their center falls inside the
platform; edges that extend under the platform are allowed since the
platform covers them (:issue:`785`).
- ``dr.pseudo_inertia`` no longer loads cuSOLVER, eliminating ~4 GB of
persistent GPU memory overhead. Cholesky and eigendecomposition are now
computed analytically for the small matrices involved (4x4 and 3x3)
(:issue:`753`).
- Set terrain geom mass to zero so that the static terrain body does not
inflate ``stat.meanmass``, which made force arrow visualization invisible
on rough terrain (:issue:`734`, :issue:`537`).
- Native viewer now syncs ``qpos0`` when domain randomized, fixing incorrect
body positions after ``dr.joint_default_pos`` randomization
(:issue:`760`).
- ``command_manager.compute()`` is now called during ``reset()`` so that
derived command state (e.g. relative body positions in tracking
environments) is populated before the first observation is returned
(:issue:`761`).
- ``RayCastSensor`` with ``ray_alignment="yaw"`` or ``"world"`` now correctly
aligns the frame offset when attached to a site or geom with a local offset
from its parent body. Previously only ray directions and pattern offsets were
aligned, causing the frame position to swing with body pitch/roll
(:issue:`775`).
Version 1.2.0 (March 6, 2026)
-----------------------------
.. admonition:: Breaking API changes
:class: attention
- ``randomize_field`` no longer exists. Replace calls with typed functions
from the new ``dr`` module (e.g. ``dr.geom_friction``, ``dr.body_mass``).
- ``EventTermCfg`` no longer accepts ``domain_randomization``. The
``@requires_model_fields`` decorator on each ``dr`` function takes care
of field expansion automatically.
- ``Scene.to_zip()`` is deprecated. Use ``Scene.write(path, zip=True)``.
- ``RslRlModelCfg`` no longer accepts ``stochastic``, ``init_noise_std``,
or ``noise_std_type``. Use ``distribution_cfg`` instead
(e.g. ``{"class_name": "GaussianDistribution", "init_std": 1.0,
"std_type": "scalar"}``). Existing checkpoints are automatically
migrated on load.
Added
^^^^^
- Added ``"step"`` event mode that fires every environment step.
- Added ``apply_body_impulse`` event for applying transient external wrenches
to bodies with configurable duration and optional application point offset.
- ONNX auto-export and metadata attachment for manipulation tasks (lift cube)
on every checkpoint save, matching the velocity and tracking task behavior.
- Multi-frame ``RayCastSensor``: pass a tuple of ``ObjRef`` to ``frame`` for
per-site raycasting with independent body exclusion. New properties:
``num_frames``, ``num_rays_per_frame``. New ``RayCastData`` fields:
``frame_pos_w`` and ``frame_quat_w``.
- ``RingPatternCfg`` ray pattern for concentric ring sampling around each
frame.
- ``TerrainHeightSensor``, a ``RayCastSensor`` subclass that computes
per-frame vertical clearance above terrain (``sensor.data.heights``).
Velocity task configs now use it for ``feet_clearance``,
``feet_swing_height``, and ``foot_height``, replacing the previous
world-Z proxy that was incorrect on rough terrain.
- Cloud training support via `SkyPilot <https://skypilot.readthedocs.io/>`_
and Lambda Cloud, with documentation covering setup, monitoring, and
cost management.
- W&B hyperparameter sweep scripts that distribute one agent per GPU
across a multi-GPU instance.
- Contributing guide with documentation for shared Claude Code commands
(``/update-mjwarp``, ``/commit-push-pr``).
- Added optional ``ViewerConfig.fovy`` and apply it in native viewer camera
setup when provided.
- Native viewer now tracks the first non-fixed body by default (matching
the Viser viewer behavior introduced in
``716aaaa58ad7bfaf34d2f771549d461204d1b4ba``).
- New ``dr`` module (``mjlab.envs.mdp.dr``) replacing ``randomize_field``
with typed per-field domain randomization functions. Each function
automatically recomputes derived fields via ``set_const``. Highlights:
- Camera and light randomization: ``dr.cam_fovy``, ``dr.cam_pos``,
``dr.cam_quat``, ``dr.cam_intrinsic``, ``dr.light_pos``,
``dr.light_dir``. Camera and light names are now supported in
``SceneEntityCfg`` (``camera_names`` / ``light_names``).
- ``dr.pseudo_inertia`` for physics-consistent randomization of
``body_mass``, ``body_ipos``, ``body_inertia``, and ``body_iquat``
via the pseudo-inertia matrix parameterization (Rucker & Wensing
2022). Replaces the removed ``dr.body_inertia`` /
``dr.body_iquat``.
- ``dr.geom_size`` with automatic recomputation of ``geom_rbound``
and ``geom_aabb`` for broadphase consistency.
- ``dr.tendon_armature`` and ``dr.tendon_frictionloss``.
- ``dr.body_quat``, ``dr.geom_quat``, and ``dr.site_quat`` with RPY
perturbation composed onto the default quaternion.
- Extensible ``Operation`` and ``Distribution`` types. Users can define
custom operations and distributions as class instances and pass them
anywhere a string is accepted. Built-in instances (``dr.abs``,
``dr.scale``, ``dr.add``, ``dr.uniform``, ``dr.log_uniform``,
``dr.gaussian``) are exported from the ``dr`` module.
- ``dr.mat_rgba`` for per-world material color randomization. Tints
the texture color, useful for randomizing appearance of textured
surfaces. Material names are now supported in ``SceneEntityCfg``
(``material_names``).
- Fixed ``dr.effort_limits`` drifting on repeated randomization.
- Fixed ``dr.body_com_offset`` not triggering ``set_const``.
- ``export-scene`` CLI script to export any task scene or asset_zoo entity
(``g1``, ``go1``, ``yam``) to a directory or zip archive for inspection
and debugging.
- ``yam_lift_cube_vision_env_cfg`` now randomizes cube color (``dr.geom_rgba``)
on every reset when ``cam_type="rgb"``.
- The native viewer now reflects per-world DR changes to visual model fields
on each reset. Geom appearance, body and site poses, camera parameters,
and light positions are all synced from the GPU model before rendering.
Inertia boxes (press ``I``) and camera frustums (press ``Q``) update
correctly when the corresponding fields are randomized. See
:doc:`randomization` for viewer-specific caveats.
- ``MaterialCfg.geom_names_expr`` for assigning materials to geoms by
name pattern during ``edit_spec``.
- ``TerrainEntityCfg`` now exposes ``textures``, ``materials``, and
``lights`` as configurable fields (previously hardcoded). Set
``textures=()``, ``materials=()`` to use flat ``dr.geom_rgba``
instead of the default checker texture.
- ``DebugVisualizer`` now supports ellipsoid visualization via
``add_ellipsoid``.
- Interactive velocity joystick sliders in the Viser viewer. Enable the
joystick under Commands/Twist to override velocity commands with manual
sliders for ``lin_vel_x``, ``lin_vel_y``, and ``ang_vel_z``
(`#666 <https://github.com/mujocolab/mjlab/issues/666>`_).
- Per-term debug visualization toggles in the Viser viewer. Individual
command term visualizers (e.g. velocity arrows) can now be toggled
independently under Scene/Debug Viz.
- Viewer single-step mode: press RIGHT arrow (native) or click "Step"
(Viser) to advance exactly one physics step while paused.
- Viewer error recovery: exceptions during stepping now pause the viewer
and log the traceback instead of crashing the process.
- Native viewer runs forward kinematics while paused, keeping
perturbation visuals accurate.
- Viewer speed multipliers use clean power-of-2 fractions (1/32x to 1x).
- Visualizers display the realtime factor alongside FPS.
- ``joint_torques_l2`` now respects ``SceneEntityCfg.actuator_ids``,
allowing penalization of a subset of actuators instead of all of them
(`#703 <https://github.com/mujocolab/mjlab/pull/703>`_). Contribution by
`@saikishor <https://github.com/saikishor>`_.
- Terrain is now a proper ``Entity`` subclass (``TerrainEntity``). This
allows domain randomization functions to target terrain parameters
(friction, cameras, lights) via ``SceneEntityCfg("terrain", ...)``.
``TerrainImporter`` / ``TerrainImporterCfg`` remain as aliases but will be
deprecated in a future version.
- Added ``upload_model`` option to ``RslRlBaseRunnerCfg`` to control W&B model
file uploads (``.pt`` and ``.onnx``) while keeping metric logging enabled
(`#654 <https://github.com/mujocolab/mjlab/pull/654>`_).
- ``Scene.write(output_dir, zip=False)`` exports the scene XML and mesh
assets to a directory (or zip archive). Replaces ``Scene.to_zip()``.
- ``Entity.write_xml()`` and ``Scene.write()`` now apply XML fixups
(empty defaults, duplicate nested defaults) and strip buffer textures
that ``MjSpec.to_xml()`` cannot serialize.
- ``fix_spec_xml`` and ``strip_buffer_textures`` utilities in
``mjlab.utils.xml``.
Changed
^^^^^^^
- Native viewer now syncs ``xfrc_applied`` to the render buffer and draws
arrows for any nonzero applied forces. Mouse perturbation forces are
converted to ``qfrc_applied`` (generalized joint space) so they coexist
with programmatic forces on ``xfrc_applied`` without conflict.
- ``ViewerConfig.OriginType.WORLD`` now configures a free camera at the
specified lookat point instead of auto tracking a body. A new ``AUTO``
origin type (now the default) preserves the previous auto tracking
behavior.
- Upgraded ``rsl-rl-lib`` from 4.0.1 to 5.0.1. ``RslRlModelCfg`` now
uses ``distribution_cfg`` dict instead of ``stochastic`` /
``init_noise_std`` / ``noise_std_type``. Existing checkpoints are
automatically migrated on load.
- Reorganized the Viser Controls tab into a cleaner folder hierarchy:
Info, Simulation, Commands, Scene (with Environment, Camera, Debug Viz,
Contacts sub-folders), and Camera Feeds. The Environment folder is
hidden for single-env tasks and the Commands folder is hidden when no
command terms are active.
- Viser camera tracking is now enabled by default so the agent stays in
frame on launch.
- Self collision and illegal contact sensors now use ``history_length`` to
catch contacts across decimation substeps. Reward and termination functions
read ``force_history`` with a configurable ``force_threshold``.
- Replaced the single ``scale`` parameter in ``DifferentialIKActionCfg`` with
separate ``delta_pos_scale`` and ``delta_ori_scale`` for independent scaling
of position and orientation components.
- Improved offscreen multi environment framing by selecting neighboring
environments around the focused env instead of first N envs.
- Tuned tracking task viewer defaults for tighter camera framing.
- Disabled shadow casting on the G1 tracking light to avoid duplicate
stacked shadows when robots are close.
Fixed
^^^^^
- Fixed actuator target resolution for entities whose ``spec_fn`` uses
internal ``MjSpec.attach(prefix=...)``
(`#709 <https://github.com/mujocolab/mjlab/issues/709>`_).
- Fixed viewer physics loop starving the renderer by replacing the single
sim-time budget with a two-clock design (tracked vs actual sim time).
Physics now self-corrects after overshooting, keeping FPS smooth at all
speed multipliers.
- Bundled ``ffmpeg`` for ``mediapy`` via ``imageio-ffmpeg``, removing the
requirement for a system ``ffmpeg`` install. Thanks to
`@rdeits-bd <https://github.com/rdeits-bd>`_ for the suggestion.
- Fixed ``height_scan`` returning ~0 for missed rays; now defaults to
``max_distance``. Replaced ``clip=(-1, 1)`` with ``scale`` normalization
in the velocity task config. Thanks to `@eufrizz <https://github.com/eufrizz>`_
for reporting and the initial fix (`#642 <https://github.com/mujocolab/mjlab/pull/642>`_).
- Fixed ghost mesh visualization for fixed-base entities by extending
``DebugVisualizer.add_ghost_mesh`` to optionally accept ``mocap_pos`` and
``mocap_quat`` (`#645 <https://github.com/mujocolab/mjlab/pull/645>`_).
- Fixed viser viewer crashing on scenes with no mocap bodies by adding
an ``nmocap`` guard, matching the native viewer behavior.
- Fixed offscreen rendering artifacts in large vectorized scenes by applying
a render local extent override in ``OffscreenRenderer`` and restoring the
original extent on close.
- Fixed ``RslRlVecEnvWrapper.unwrapped`` to return the base environment,
ensuring checkpoint state restore and logging work correctly when wrappers
such as ``VideoRecorder`` are enabled.
Version 1.1.1 (February 14, 2026)
---------------------------------
Added
^^^^^
- Added reward term visualization to the native viewer (toggle with ``P``) (`#629 <https://github.com/mujocolab/mjlab/pull/629>`_).
- Added ``DifferentialIKAction`` for task-space control via damped
least-squares IK. Supports weighted position/orientation tracking,
soft joint-limit avoidance, and null-space posture regularization.
Includes an interactive viser demo (``scripts/demos/differential_ik.py``) (`#632 <https://github.com/mujocolab/mjlab/pull/632>`_).
Fixed
^^^^^
- Fixed ``play.py`` defaulting to the base rsl-rl ``OnPolicyRunner`` instead
of ``MjlabOnPolicyRunner``, which caused a ``TypeError`` from an unexpected
``cnn_cfg`` keyword argument (`#626 <https://github.com/mujocolab/mjlab/pull/626>`_). Contribution by
`@griffinaddison <https://github.com/griffinaddison>`_.
Changed
^^^^^^^
- Removed ``body_mass``, ``body_inertia``, ``body_pos``, and ``body_quat``
from ``FIELD_SPECS`` in domain randomization. These fields have derived
quantities that require ``set_const`` to recompute; without that call,
randomizing them silently breaks physics (`#631 <https://github.com/mujocolab/mjlab/pull/631>`_).
- Replaced ``moviepy`` with ``mediapy`` for video recording. ``mediapy``
handles cloud storage paths (GCS, S3) natively (`#637 <https://github.com/mujocolab/mjlab/pull/637>`_).
.. figure:: _static/changelog/native_reward.png
:width: 80%
Version 1.1.0 (February 12, 2026)
---------------------------------
Added
^^^^^
- Added RGB and depth camera sensors and BVH-accelerated raycasting (`#597 <https://github.com/mujocolab/mjlab/pull/597>`_).
- Added ``MetricsManager`` for logging custom metrics during training (`#596 <https://github.com/mujocolab/mjlab/pull/596>`_).
- Added terrain visualizer (`#609 <https://github.com/mujocolab/mjlab/pull/609>`_). Contribution by
`@mktk1117 <https://github.com/mktk1117>`_.
.. figure:: _static/changelog/terrain_visualizer.jpg
:width: 80%
- Added many new terrains including ``HfDiscreteObstaclesTerrainCfg``,
``HfPerlinNoiseTerrainCfg``, ``BoxSteppingStonesTerrainCfg``,
``BoxNarrowBeamsTerrainCfg``, ``BoxRandomStairsTerrainCfg``, and
more. Added flat patch sampling for heightfield terrains (`#542 <https://github.com/mujocolab/mjlab/pull/542>`_, `#581 <https://github.com/mujocolab/mjlab/pull/581>`_).
- Added site group visualization to the Viser viewer (Geoms and Sites
tabs unified into a single Groups tab) (`#551 <https://github.com/mujocolab/mjlab/pull/551>`_).
- Added ``env_ids`` parameter to ``Entity.write_ctrl_to_sim`` (`#567 <https://github.com/mujocolab/mjlab/pull/567>`_).
Changed
^^^^^^^
- Upgraded ``rsl-rl-lib`` to 4.0.0 and replaced the custom ONNX
exporter with rsl-rl's built-in ``as_onnx()`` (`#589 <https://github.com/mujocolab/mjlab/pull/589>`_, `#595 <https://github.com/mujocolab/mjlab/pull/595>`_).
- ``sim.forward()`` is now called unconditionally after the decimation
loop. See :ref:`faq-sim-forward` for details (`#591 <https://github.com/mujocolab/mjlab/pull/591>`_).
- Unnamed freejoints are now automatically named to prevent
``KeyError`` during entity init (`#545 <https://github.com/mujocolab/mjlab/pull/545>`_).
Fixed
^^^^^
- Fixed ``randomize_pd_gains`` crash with ``num_envs > 1`` (`#564 <https://github.com/mujocolab/mjlab/pull/564>`_).
- Fixed ``ctrl_ids`` index error with multiple actuated entities (`#573 <https://github.com/mujocolab/mjlab/pull/573>`_).
Reported by `@bwrooney82 <https://github.com/bwrooney82>`_.
- Fixed Viser viewer rendering textured robots as gray (`#544 <https://github.com/mujocolab/mjlab/pull/544>`_).
- Fixed Viser plane rendering ignoring MuJoCo size parameter (`#540 <https://github.com/mujocolab/mjlab/pull/540>`_).
- Fixed ``HfDiscreteObstaclesTerrainCfg`` spawn height (`#552 <https://github.com/mujocolab/mjlab/pull/552>`_).
- Fixed ``RaycastSensor`` visualization ignoring the all-envs toggle (`#607 <https://github.com/mujocolab/mjlab/pull/607>`_).
Contribution by `@oxkitsune <https://github.com/oxkitsune>`_.
Version 1.0.0 (January 28, 2026)
--------------------------------
Initial release of mjlab.