Skip to content

Commit e59303f

Browse files
committed
Add MuJoCo gravity compensation schema classes
Add MujocoRigidBodyPropertiesCfg with gravity_compensation_scale (body-level, written to mjc:gravcomp) and MujocoJointDrivePropertiesCfg with gravity_compensation (joint-level, written to mjc:actuatorgravcomp) for the Newton/MuJoCo simulation backend. Both classes use the _usd_namespace and _usd_attr_name_map metadata so the modify functions handle them generically with no backend-specific branching. Also extends modify_rigid_body_properties to support _usd_attr_name_map, matching the joint drive path, for backends with non-camelCase attribute names.
1 parent 53678cd commit e59303f

7 files changed

Lines changed: 158 additions & 5 deletions

File tree

source/isaaclab/config/extension.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[package]
22

33
# Note: Semantic Versioning is used: https://semver.org/
4-
version = "4.6.2"
4+
version = "4.6.3"
55

66
# Description
77
title = "Isaac Lab framework for Robot Learning"

source/isaaclab/docs/CHANGELOG.rst

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,27 @@
11
Changelog
22
---------
33

4+
4.6.3 (2026-04-14)
5+
~~~~~~~~~~~~~~~~~~~
6+
7+
Added
8+
^^^^^
9+
10+
* Added :class:`~isaaclab.sim.schemas.MujocoRigidBodyPropertiesCfg` with
11+
``gravity_compensation_scale`` for body-level gravity compensation in the
12+
Newton (MuJoCo) backend.
13+
* Added :class:`~isaaclab.sim.schemas.MujocoJointDrivePropertiesCfg` with
14+
``gravity_compensation`` for joint-level gravity compensation in the
15+
Newton (MuJoCo) backend.
16+
17+
Changed
18+
^^^^^^^
19+
20+
* Extended :meth:`~isaaclab.sim.schemas.modify_rigid_body_properties` to support
21+
``_usd_attr_name_map`` metadata, matching the joint drive path. This enables
22+
solver backends with non-camelCase attribute names (e.g. MuJoCo ``mjc:gravcomp``).
23+
24+
425
4.6.2 (2026-04-14)
526
~~~~~~~~~~~~~~~~~~~
627

source/isaaclab/isaaclab/sim/__init__.pyi

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ __all__ = [
4646
"JointDrivePropertiesCfg",
4747
"MassPropertiesCfg",
4848
"MeshCollisionPropertiesCfg",
49+
"MujocoJointDrivePropertiesCfg",
50+
"MujocoRigidBodyPropertiesCfg",
4951
"PhysxJointDrivePropertiesCfg",
5052
"PhysxRigidBodyPropertiesCfg",
5153
"RigidBodyPropertiesCfg",
@@ -212,6 +214,8 @@ from .schemas import (
212214
JointDrivePropertiesCfg,
213215
MassPropertiesCfg,
214216
MeshCollisionPropertiesCfg,
217+
MujocoJointDrivePropertiesCfg,
218+
MujocoRigidBodyPropertiesCfg,
215219
PhysxJointDrivePropertiesCfg,
216220
PhysxRigidBodyPropertiesCfg,
217221
RigidBodyPropertiesCfg,

source/isaaclab/isaaclab/sim/schemas/__init__.pyi

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ __all__ = [
3434
"JointDrivePropertiesCfg",
3535
"MassPropertiesCfg",
3636
"MeshCollisionPropertiesCfg",
37+
"MujocoJointDrivePropertiesCfg",
38+
"MujocoRigidBodyPropertiesCfg",
3739
"PhysxJointDrivePropertiesCfg",
3840
"PhysxRigidBodyPropertiesCfg",
3941
"RigidBodyPropertiesCfg",
@@ -76,6 +78,8 @@ from .schemas_cfg import (
7678
JointDrivePropertiesCfg,
7779
MassPropertiesCfg,
7880
MeshCollisionPropertiesCfg,
81+
MujocoJointDrivePropertiesCfg,
82+
MujocoRigidBodyPropertiesCfg,
7983
PhysxJointDrivePropertiesCfg,
8084
PhysxRigidBodyPropertiesCfg,
8185
RigidBodyPropertiesCfg,

source/isaaclab/isaaclab/sim/schemas/schemas.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,7 @@ def modify_rigid_body_properties(
326326
# read solver-specific metadata from the cfg instance
327327
namespace = getattr(cfg, "_usd_namespace", None)
328328
applied_schema = getattr(cfg, "_usd_applied_schema", None)
329+
attr_name_map = getattr(cfg, "_usd_attr_name_map", {})
329330

330331
# convert to dict, filtering out class metadata (underscore-prefixed keys)
331332
cfg_dict = {k: v for k, v in cfg.to_dict().items() if not k.startswith("_")}
@@ -335,14 +336,13 @@ def modify_rigid_body_properties(
335336
value = cfg_dict.pop(attr_name, None)
336337
safe_set_attribute_on_usd_schema(usd_rigid_body_api, attr_name, value, camel_case=True)
337338

338-
# set solver-specific properties using class metadata (namespace + applied schema)
339+
# set solver-specific properties using class metadata (namespace + applied schema + attr name map)
339340
if applied_schema:
340341
if applied_schema not in rigid_body_prim.GetAppliedSchemas():
341342
rigid_body_prim.AddAppliedSchema(applied_schema)
342343
for attr_name, value in cfg_dict.items():
343-
safe_set_attribute_on_usd_prim(
344-
rigid_body_prim, f"{namespace}:{to_camel_case(attr_name, 'cC')}", value, camel_case=False
345-
)
344+
usd_attr_name = attr_name_map.get(attr_name, to_camel_case(attr_name, "cC"))
345+
safe_set_attribute_on_usd_prim(rigid_body_prim, f"{namespace}:{usd_attr_name}", value, camel_case=False)
346346
# success
347347
return True
348348

source/isaaclab/isaaclab/sim/schemas/schemas_cfg.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,34 @@ class PhysxRigidBodyPropertiesCfg(RigidBodyPropertiesCfg):
143143
"""The mass-normalized kinetic energy threshold below which an actor may participate in stabilization."""
144144

145145

146+
@configclass
147+
class MujocoRigidBodyPropertiesCfg(RigidBodyPropertiesCfg):
148+
"""MuJoCo-specific rigid body properties.
149+
150+
Extends :class:`RigidBodyPropertiesCfg` with properties that are only supported by the
151+
Newton (MuJoCo) simulation backend.
152+
153+
See :meth:`modify_rigid_body_properties` for more information.
154+
155+
.. note::
156+
If the values are None, they are not modified. This is useful when you want to set only a subset of
157+
the properties and leave the rest as-is.
158+
"""
159+
160+
# -- Class metadata (not dataclass fields) --
161+
# Prim attribute namespace for solver-specific fields.
162+
_usd_namespace = "mjc"
163+
# Mapping from cfg field names to USD attribute names.
164+
_usd_attr_name_map = {"gravity_compensation_scale": "gravcomp"}
165+
166+
gravity_compensation_scale: float | None = None
167+
"""Scale factor for gravity compensation for the body [dimensionless]. Defaults to None
168+
(attribute not written to USD).
169+
170+
In MuJoCo, ``0.0`` means no compensation and ``1.0`` means full compensation.
171+
"""
172+
173+
146174
@configclass
147175
class CollisionPropertiesCfg:
148176
"""Properties to apply to colliders in a rigid body.
@@ -300,6 +328,32 @@ class PhysxJointDrivePropertiesCfg(JointDrivePropertiesCfg):
300328
"""
301329

302330

331+
@configclass
332+
class MujocoJointDrivePropertiesCfg(JointDrivePropertiesCfg):
333+
"""MuJoCo-specific joint drive properties.
334+
335+
Extends :class:`JointDrivePropertiesCfg` with properties that are only supported by the
336+
Newton (MuJoCo) simulation backend.
337+
338+
See :meth:`modify_joint_drive_properties` for more information.
339+
340+
.. note::
341+
If the values are None, they are not modified. This is useful when you want to set only a subset of
342+
the properties and leave the rest as-is.
343+
"""
344+
345+
# -- Class metadata (not dataclass fields) --
346+
# Prim attribute namespace for solver-specific fields.
347+
_usd_namespace = "mjc"
348+
# Mapping from cfg field names to USD attribute names.
349+
_usd_attr_name_map = {"gravity_compensation": "actuatorgravcomp"}
350+
351+
gravity_compensation: bool | None = None
352+
"""Whether to enable gravity compensation on the joint. Defaults to None
353+
(attribute not written to USD).
354+
"""
355+
356+
303357
@configclass
304358
class FixedTendonPropertiesCfg:
305359
"""Properties to define fixed tendons of an articulation.

source/isaaclab/test/sim/test_schemas.py

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,76 @@ def test_multi_instance_schema_detection_on_tendon_joints(setup_simulation):
271271
assert result is False, "Prim without tendon root schema should return False"
272272

273273

274+
@pytest.mark.isaacsim_ci
275+
def test_mujoco_gravity_compensation_scale_on_rigid_body(setup_simulation):
276+
"""Test that gravity_compensation_scale is written as mjc:gravcomp on rigid body prims."""
277+
sim, _, _, _, _, _ = setup_simulation
278+
stage = sim_utils.get_current_stage()
279+
280+
sim_utils.create_prim("/World/cube", prim_type="Cube", translation=(0.0, 0.0, 0.62))
281+
rigid_cfg = schemas.MujocoRigidBodyPropertiesCfg(gravity_compensation_scale=0.5)
282+
schemas.define_rigid_body_properties("/World/cube", rigid_cfg)
283+
284+
prim = stage.GetPrimAtPath("/World/cube")
285+
attr = prim.GetAttribute("mjc:gravcomp")
286+
assert attr.IsValid(), "mjc:gravcomp attribute not found on rigid body prim"
287+
assert attr.Get() == pytest.approx(0.5)
288+
289+
290+
@pytest.mark.isaacsim_ci
291+
def test_mujoco_gravity_compensation_scale_not_set_when_none(setup_simulation):
292+
"""Test that mjc:gravcomp is not written when gravity_compensation_scale is None."""
293+
sim, _, _, _, _, _ = setup_simulation
294+
stage = sim_utils.get_current_stage()
295+
296+
sim_utils.create_prim("/World/cube2", prim_type="Cube", translation=(1.0, 0.0, 0.62))
297+
rigid_cfg = schemas.MujocoRigidBodyPropertiesCfg()
298+
schemas.define_rigid_body_properties("/World/cube2", rigid_cfg)
299+
300+
prim = stage.GetPrimAtPath("/World/cube2")
301+
attr = prim.GetAttribute("mjc:gravcomp")
302+
assert not attr.IsValid(), "mjc:gravcomp should not be set when gravity_compensation_scale is None"
303+
304+
305+
@pytest.mark.isaacsim_ci
306+
def test_mujoco_joint_gravity_compensation_written(setup_simulation):
307+
"""Test that gravity_compensation is written as mjc:actuatorgravcomp on joint prims."""
308+
sim, _, _, _, _, _ = setup_simulation
309+
stage = sim_utils.get_current_stage()
310+
311+
sim_utils.create_prim("/World/jgc_test", prim_type="Xform")
312+
sim_utils.create_prim("/World/jgc_test/body0", prim_type="Cube")
313+
sim_utils.create_prim("/World/jgc_test/body1", prim_type="Cube")
314+
UsdPhysics.RevoluteJoint.Define(stage, "/World/jgc_test/body1/joint0")
315+
316+
joint_cfg = schemas.MujocoJointDrivePropertiesCfg(gravity_compensation=True)
317+
schemas.modify_joint_drive_properties("/World/jgc_test", joint_cfg)
318+
319+
joint_prim = stage.GetPrimAtPath("/World/jgc_test/body1/joint0")
320+
attr = joint_prim.GetAttribute("mjc:actuatorgravcomp")
321+
assert attr.IsValid(), "mjc:actuatorgravcomp not set on joint prim"
322+
assert attr.Get() is True
323+
324+
325+
@pytest.mark.isaacsim_ci
326+
def test_mujoco_joint_gravity_compensation_not_set_when_none(setup_simulation):
327+
"""Test that mjc:actuatorgravcomp is not written when gravity_compensation is None."""
328+
sim, _, _, _, _, _ = setup_simulation
329+
stage = sim_utils.get_current_stage()
330+
331+
sim_utils.create_prim("/World/jgc_test2", prim_type="Xform")
332+
sim_utils.create_prim("/World/jgc_test2/body0", prim_type="Cube")
333+
sim_utils.create_prim("/World/jgc_test2/body1", prim_type="Cube")
334+
UsdPhysics.RevoluteJoint.Define(stage, "/World/jgc_test2/body1/joint0")
335+
336+
joint_cfg = schemas.MujocoJointDrivePropertiesCfg()
337+
schemas.modify_joint_drive_properties("/World/jgc_test2", joint_cfg)
338+
339+
joint_prim = stage.GetPrimAtPath("/World/jgc_test2/body1/joint0")
340+
attr = joint_prim.GetAttribute("mjc:actuatorgravcomp")
341+
assert not attr.IsValid(), "mjc:actuatorgravcomp should not be set when gravity_compensation is None"
342+
343+
274344
"""
275345
Helper functions.
276346
"""

0 commit comments

Comments
 (0)