========= 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 `_. - 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 ```` 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 ```` and ```` 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 ```` for JOINT transmission and ```` 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 ` that walks through building an environment from scratch. - Added :ref:`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 `` --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 ``