Changelog#
Upcoming version (not yet released)#
Changed#
Task package load failures during
mjlabimport now print the full traceback (and the entry point’s module path) tostderrinstead of just the exception message, making it easier to pinpoint the source of import errors when running commands likelist-envs(#910). Contribution by @saikishor.
Version 1.3.0 (April 14, 2026)#
Added#
Added
ManagerBasedRlEnvCfg.auto_resetflag. WhenTrue(default),step()continues to reset done environments in place and returns the post-reset observation. WhenFalse,step()skips the reset block and returns the terminal observation directly; the caller must callreset(env_ids=...)for done environments before the nextstep()or aRuntimeErroris raised. Enables access to the true terminal state for algorithms that need it. Note that mjlab’s bundledtrain.pyuses rsl_rl’sOnPolicyRunner, which does not drive manual resets, soauto_reset=Falseis intended for custom training loops (#900).Added
ActuatorCfg.viscous_dampingfor passive velocity proportional damping (f = -b·v), distinct from the PD derivative gaindampingused by position and velocity actuators. Maps to<joint damping>for JOINT transmission and<tendon damping>for TENDON transmission. Defaults toNone(preserves the XML value).Added
RecorderManagerfor logging observations, actions, or arbitrary environment data during rollouts. Implement aRecorderTermsubclass and register it in therecordersdict onManagerBasedRlEnvCfg. The manager providesrecord_pre_reset,record_post_reset, andrecord_post_steplifecycle hooks with no opinion on how data is stored.Added
termination_curriculum()for scheduling changes to termination term parameters during training, matching the existingreward_curriculumpattern. Both now share a single internal engine with init-time validation of stage ordering, field existence, and param keys.Added
reducefield toMetricsTermCfg. Settingreduce="last"reports the value from the final step of the episode rather than the episode mean, which is useful for binary success metrics.Added
RelativeJointPositionActionfor joint position control relative to the current configuration. The target iscurrent_pos + action * scale, so a zero action holds the current configuration rather than commanding the default pose.Added
pair_friction()for randomizing geom-pair friction overrides (pair_frictioninmjModel), with anisotropic=Trueoption that mirrors the symmetric tangent and roll axes so single-axis randomization does not leave the paired axis stale.Added
STAIRS_TERRAINS_CFGterrain preset for progressive stair curriculum training and@terrain_presetdecorator for composing terrain configurations from reusable presets.Added cartpole balance and swingup tasks (
Mjlab-Cartpole-BalanceandMjlab-Cartpole-Swingup) with a tutorial that walks through building an environment from scratch.Added 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 (#777).
Added
margin,gap, andsolmixfields toCollisionCfgfor per geom contact parameter configuration (#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.clipfor clamping processed actions after scale and offset (#771).Added
qfrc_actuatorandqfrc_externalgeneralized force accessors toEntityData.qfrc_actuatorgives actuator forces in joint space (projected through the transmission).qfrc_externalrecovers the generalized force from body external wrenches (xfrc_applied) (#776).Added
RewardBarPanelto the Viser viewer, showing horizontal bars for each reward term with a running mean over ~1 second (#800).Added
per_substepflag toMetricsTermCfgfor 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/InstinctMJto 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 (#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 (#862). Contribution by @pthangeda.
Changed#
Renamed the
list_envsconsole script tolist-envsfor consistency with the other hyphenated entry points (viz-nan,export-scene). Invoke viauv run list-envs.ActuatorCfg.armatureandActuatorCfg.frictionlossnow default toNoneinstead of0.0.Nonepreserves 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, passarmature=0.0orfrictionloss=0.0explicitly.Actuator delay is now configured inline on any
ActuatorCfgsubclass (e.g.BuiltinPositionActuatorCfg(..., delay_min_lag=2, delay_max_lag=5)) instead of wrapping withDelayedActuatorCfg.DelayedActuator,DelayedActuatorCfg, andDelayedBuiltinActuatorGroupare removed.Removed
delay_targetfromActuatorCfg. Delay now always applies to the actuator’scommand_fieldautomatically. Multi-target delay (delay_target=("position", "velocity")) is no longer supported.XmlPositionActuatorCfg,XmlVelocityActuatorCfg,XmlMotorActuatorCfg, andXmlMuscleActuatorCfgare replaced by a singleXmlActuatorCfgthat auto detects the actuator type from XML. Passcommand_field=...to override detection.Replaced the viser viewer internals with the
mjviserpackage. 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’sMjlabViserScenesubclass (#839).In curriculum terrain mode, each terrain type now gets exactly one column (
num_colsis set tolen(sub_terrains)). Theproportionfield now controls robot spawning distribution across columns rather than column count. Random mode is unchanged (#811).BoxSteppingStonesTerrainCfgstone size now decreases with difficulty, interpolating from the large end ofstone_size_rangeat difficulty 0 to the small end at difficulty 1 (#785).Removed deprecated
TerrainImporterandTerrainImporterCfgaliases. UseTerrainEntityandTerrainEntityCfginstead (#667).Entity.clear_state()is deprecated. UseEntity.reset()instead.clear_stateonly 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. Useqfrc_actuatororqfrc_externalinstead (#776).get_wandb_checkpoint_pathnow filters checkpoints server-side via thepatternparameter, avoiding unnecessary pagination and tolerance to corrupted metadata (#898).
Fixed#
trainandplaynow print a top-level usage message when invoked with-h/--helpand no task argument, pointing users atlist-envsand<TASK> --help(#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 byMjSpec.attach(). UseMujocoCfgto set simulation options instead (#885).Fixed
SceneEntityCfgnames and IDs ordering mismatch whenpreserve_order=False(#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"(#867). Contribution by @gokulp01.export-scenenow 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 (#858).electrical_power_costnow usesqfrc_actuator(joint space) instead ofactuator_force(actuation space) for mechanical power computation. Previously the reward was incorrect for actuators with gear ratios other than 1 (#776).create_velocity_actuatorno longer setsctrllimited=Truewithinheritrange=1.0. This caused aValueErrorfor continuous joints (e.g. wheels) that have no position range defined (#787).write_root_com_velocity_to_simno longer fails with tensorenv_idson floating base entities (#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
ctrlinto the CPUMjDatabefore callingmj_forward. Actuators that compute torques in Python (DcMotorActuator,IdealPdActuator) previously showed incorrect contact forces because the viewer ran withctrl=0(#786).BoxSteppingStonesTerrainCfgno 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 (#785).dr.pseudo_inertiano 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) (#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 (#734, #537).Native viewer now syncs
qpos0when domain randomized, fixing incorrect body positions afterdr.joint_default_posrandomization (#760).command_manager.compute()is now called duringreset()so that derived command state (e.g. relative body positions in tracking environments) is populated before the first observation is returned (#761).RayCastSensorwithray_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 (#775).
Version 1.2.0 (March 6, 2026)#
Breaking API changes
randomize_fieldno longer exists. Replace calls with typed functions from the newdrmodule (e.g.dr.geom_friction,dr.body_mass).EventTermCfgno longer acceptsdomain_randomization. The@requires_model_fieldsdecorator on eachdrfunction takes care of field expansion automatically.Scene.to_zip()is deprecated. UseScene.write(path, zip=True).RslRlModelCfgno longer acceptsstochastic,init_noise_std, ornoise_std_type. Usedistribution_cfginstead (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_impulseevent 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 ofObjReftoframefor per-site raycasting with independent body exclusion. New properties:num_frames,num_rays_per_frame. NewRayCastDatafields:frame_pos_wandframe_quat_w.RingPatternCfgray pattern for concentric ring sampling around each frame.TerrainHeightSensor, aRayCastSensorsubclass that computes per-frame vertical clearance above terrain (sensor.data.heights). Velocity task configs now use it forfeet_clearance,feet_swing_height, andfoot_height, replacing the previous world-Z proxy that was incorrect on rough terrain.Cloud training support via SkyPilot 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.fovyand 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
drmodule (mjlab.envs.mdp.dr) replacingrandomize_fieldwith typed per-field domain randomization functions. Each function automatically recomputes derived fields viaset_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 inSceneEntityCfg(camera_names/light_names).dr.pseudo_inertiafor physics-consistent randomization ofbody_mass,body_ipos,body_inertia, andbody_iquatvia the pseudo-inertia matrix parameterization (Rucker & Wensing 2022). Replaces the removeddr.body_inertia/dr.body_iquat.dr.geom_sizewith automatic recomputation ofgeom_rboundandgeom_aabbfor broadphase consistency.dr.tendon_armatureanddr.tendon_frictionloss.dr.body_quat,dr.geom_quat, anddr.site_quatwith RPY perturbation composed onto the default quaternion.Extensible
OperationandDistributiontypes. 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 thedrmodule.dr.mat_rgbafor per-world material color randomization. Tints the texture color, useful for randomizing appearance of textured surfaces. Material names are now supported inSceneEntityCfg(material_names).Fixed
dr.effort_limitsdrifting on repeated randomization.Fixed
dr.body_com_offsetnot triggeringset_const.
export-sceneCLI 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_cfgnow randomizes cube color (dr.geom_rgba) on every reset whencam_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 (pressQ) update correctly when the corresponding fields are randomized. See Domain Randomization for viewer-specific caveats.MaterialCfg.geom_names_exprfor assigning materials to geoms by name pattern duringedit_spec.TerrainEntityCfgnow exposestextures,materials, andlightsas configurable fields (previously hardcoded). Settextures=(),materials=()to use flatdr.geom_rgbainstead of the default checker texture.DebugVisualizernow supports ellipsoid visualization viaadd_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, andang_vel_z(#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_l2now respectsSceneEntityCfg.actuator_ids, allowing penalization of a subset of actuators instead of all of them (#703). Contribution by @saikishor.Terrain is now a proper
Entitysubclass (TerrainEntity). This allows domain randomization functions to target terrain parameters (friction, cameras, lights) viaSceneEntityCfg("terrain", ...).TerrainImporter/TerrainImporterCfgremain as aliases but will be deprecated in a future version.Added
upload_modeloption toRslRlBaseRunnerCfgto control W&B model file uploads (.ptand.onnx) while keeping metric logging enabled (#654).Scene.write(output_dir, zip=False)exports the scene XML and mesh assets to a directory (or zip archive). ReplacesScene.to_zip().Entity.write_xml()andScene.write()now apply XML fixups (empty defaults, duplicate nested defaults) and strip buffer textures thatMjSpec.to_xml()cannot serialize.fix_spec_xmlandstrip_buffer_texturesutilities inmjlab.utils.xml.
Changed#
Native viewer now syncs
xfrc_appliedto the render buffer and draws arrows for any nonzero applied forces. Mouse perturbation forces are converted toqfrc_applied(generalized joint space) so they coexist with programmatic forces onxfrc_appliedwithout conflict.ViewerConfig.OriginType.WORLDnow configures a free camera at the specified lookat point instead of auto tracking a body. A newAUTOorigin type (now the default) preserves the previous auto tracking behavior.Upgraded
rsl-rl-libfrom 4.0.1 to 5.0.1.RslRlModelCfgnow usesdistribution_cfgdict instead ofstochastic/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_lengthto catch contacts across decimation substeps. Reward and termination functions readforce_historywith a configurableforce_threshold.Replaced the single
scaleparameter inDifferentialIKActionCfgwith separatedelta_pos_scaleanddelta_ori_scalefor 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_fnuses internalMjSpec.attach(prefix=...)(#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
ffmpegformediapyviaimageio-ffmpeg, removing the requirement for a systemffmpeginstall. Thanks to @rdeits-bd for the suggestion.Fixed
height_scanreturning ~0 for missed rays; now defaults tomax_distance. Replacedclip=(-1, 1)withscalenormalization in the velocity task config. Thanks to @eufrizz for reporting and the initial fix (#642).Fixed ghost mesh visualization for fixed-base entities by extending
DebugVisualizer.add_ghost_meshto optionally acceptmocap_posandmocap_quat(#645).Fixed viser viewer crashing on scenes with no mocap bodies by adding an
nmocapguard, matching the native viewer behavior.Fixed offscreen rendering artifacts in large vectorized scenes by applying a render local extent override in
OffscreenRendererand restoring the original extent on close.Fixed
RslRlVecEnvWrapper.unwrappedto return the base environment, ensuring checkpoint state restore and logging work correctly when wrappers such asVideoRecorderare enabled.
Version 1.1.1 (February 14, 2026)#
Added#
Added reward term visualization to the native viewer (toggle with
P) (#629).Added
DifferentialIKActionfor 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).
Fixed#
Fixed
play.pydefaulting to the base rsl-rlOnPolicyRunnerinstead ofMjlabOnPolicyRunner, which caused aTypeErrorfrom an unexpectedcnn_cfgkeyword argument (#626). Contribution by @griffinaddison.
Changed#
Removed
body_mass,body_inertia,body_pos, andbody_quatfromFIELD_SPECSin domain randomization. These fields have derived quantities that requireset_constto recompute; without that call, randomizing them silently breaks physics (#631).Replaced
moviepywithmediapyfor video recording.mediapyhandles cloud storage paths (GCS, S3) natively (#637).
Version 1.1.0 (February 12, 2026)#
Added#
Added RGB and depth camera sensors and BVH-accelerated raycasting (#597).
Added
MetricsManagerfor logging custom metrics during training (#596).
Added many new terrains including
HfDiscreteObstaclesTerrainCfg,HfPerlinNoiseTerrainCfg,BoxSteppingStonesTerrainCfg,BoxNarrowBeamsTerrainCfg,BoxRandomStairsTerrainCfg, and more. Added flat patch sampling for heightfield terrains (#542, #581).Added site group visualization to the Viser viewer (Geoms and Sites tabs unified into a single Groups tab) (#551).
Added
env_idsparameter toEntity.write_ctrl_to_sim(#567).
Changed#
Upgraded
rsl-rl-libto 4.0.0 and replaced the custom ONNX exporter with rsl-rl’s built-inas_onnx()(#589, #595).sim.forward()is now called unconditionally after the decimation loop. See When do I need to call sim.forward()? for details (#591).Unnamed freejoints are now automatically named to prevent
KeyErrorduring entity init (#545).
Fixed#
Fixed
randomize_pd_gainscrash withnum_envs > 1(#564).Fixed
ctrl_idsindex error with multiple actuated entities (#573). Reported by @bwrooney82.Fixed Viser viewer rendering textured robots as gray (#544).
Fixed Viser plane rendering ignoring MuJoCo size parameter (#540).
Fixed
HfDiscreteObstaclesTerrainCfgspawn height (#552).Fixed
RaycastSensorvisualization ignoring the all-envs toggle (#607). Contribution by @oxkitsune.
Version 1.0.0 (January 28, 2026)#
Initial release of mjlab.