Enable gravity compensation in Isaaclab with Newton + Mujoco backend#4847
Enable gravity compensation in Isaaclab with Newton + Mujoco backend#4847vidurv-nvidia wants to merge 3 commits into
Conversation
Greptile SummaryThis PR adds gravity compensation support at two levels for the Newton/MuJoCo simulation backend: a body-level Key observations:
Confidence Score: 4/5
Sequence DiagramsequenceDiagram
participant User as User / Script
participant Cfg as ArticulationCfg /<br/>RigidBodyPropertiesCfg
participant IL as IsaacLab<br/>Articulation.__init__
participant USD as USD Stage<br/>(Joint / RigidBody Prims)
participant Newton as Newton<br/>ModelBuilder
participant MuJoCo as MuJoCo Solver /<br/>MJCF XML
User->>Cfg: Set gravity_compensation=True<br/>Set gravity_compensation_scale=0.5
User->>IL: Articulation(cfg)
IL->>IL: super().__init__(cfg)<br/>(spawns USD prims)
IL->>USD: _write_actuator_gravity_comp_to_usd()<br/>→ find_matching_prims + regex match<br/>→ mjc:actuatorgravcomp=True on joint prims
User->>USD: modify_rigid_body_properties()<br/>→ mjc:gravcomp=0.5 on rigid body prims
User->>IL: sim.reset()
IL->>Newton: instantiate_builder_from_stage()<br/>(reads USD attributes)
Newton->>Newton: model.mujoco.gravcomp<br/>model.mujoco.jnt_actgravcomp
Newton->>MuJoCo: SolverMuJoCo(model)<br/>→ actuatorgravcomp on joints in MJCF XML
Last reviewed commit: a29ac11 |
AntoineRichard
left a comment
There was a problem hiding this comment.
Ideally, we really do not want the articulation class to set USD level properties. Rather than piggy backing on the Actuator overload, could we use JointDrivePropertiesCfg?
| def _write_actuator_gravity_comp_to_usd(self) -> None: | ||
| """Write ``mjc:actuatorgravcomp = True`` to joint USD prims for actuators with gravity_compensation=True. | ||
|
|
||
| Must be called after spawning (joint prims exist) but before ``sim.reset()`` | ||
| so Newton reads the attribute during ``instantiate_builder_from_stage()``. | ||
| """ | ||
|
|
||
| gc_actuators = { | ||
| name: cfg for name, cfg in self.cfg.actuators.items() if getattr(cfg, "gravity_compensation", None) | ||
| } | ||
| if not gc_actuators: | ||
| return | ||
|
|
||
| _JOINT_TYPES = {"PhysicsRevoluteJoint", "PhysicsPrismaticJoint", "PhysicsSphericalJoint", "PhysicsD6Joint"} | ||
|
|
||
| for artic_prim in find_matching_prims(self.cfg.prim_path): | ||
| joint_prims = get_all_matching_child_prims( | ||
| artic_prim.GetPath().pathString, | ||
| predicate=lambda prim: prim.GetTypeName() in _JOINT_TYPES, | ||
| ) | ||
| for joint_prim in joint_prims: | ||
| joint_name = joint_prim.GetName() | ||
| for actuator_cfg in gc_actuators.values(): | ||
| if any(re.fullmatch(expr, joint_name) for expr in actuator_cfg.joint_names_expr): | ||
| safe_set_attribute_on_usd_prim(joint_prim, "mjc:actuatorgravcomp", True, camel_case=False) | ||
| break | ||
|
|
There was a problem hiding this comment.
I feel like this is bandaid that should not be needed. This class does not intended to do USD level modifications of the assets.
There was a problem hiding this comment.
This is more a newton level test. Not sure it should be in lab.
| "mujoco==3.5.0", | ||
| "mujoco-warp==3.5.0.2", | ||
| "newton==1.0.0rc1", | ||
| "newton @ git+https://github.com/newton-physics/newton.git@813f828336bc6cd05f6d9d4839c09a1cda2ea7dd", |
There was a problem hiding this comment.
| "newton @ git+https://github.com/newton-physics/newton.git@813f828336bc6cd05f6d9d4839c09a1cda2ea7dd", | |
| "newton==1.0.0", |
| gravity_compensation: bool | None = None | ||
| """Whether to perform gravity compensation for this group of actuators. Defaults to None. | ||
|
|
||
| .. note:: | ||
|
|
||
| This attribute is only supported by the Newton (MuJoCo) simulation backend. | ||
| When using other backends (e.g. PhysX), this setting is silently ignored. | ||
| """ |
There was a problem hiding this comment.
Why is it needed here? If the schema is attached to bodies why do we have that in the actuator config?
There was a problem hiding this comment.
It looks like it's so that the articulation can catch it and apply it? (But it's very unlikely we're going to go that route)
| cfg: A configuration instance. | ||
| """ | ||
| super().__init__(cfg) | ||
| self._write_actuator_gravity_comp_to_usd() |
There was a problem hiding this comment.
Let's not do that here. This class is not meant to write to USD.
AntoineRichard
left a comment
There was a problem hiding this comment.
Clicked the wrong button ...
Ideally, we really do not want the articulation class to set USD level properties. Rather than piggy backing on the Actuator overload, could we use JointDrivePropertiesCfg?
There was a problem hiding this comment.
Code Review — PR #4847: Enable gravity compensation in IsaacLab with Newton + MuJoCo backend
Summary
This PR adds body-level (mjc:gravcomp) and joint-level (mjc:actuatorgravcomp) gravity compensation to the IsaacLab schemas layer, enabling Newton/MuJoCo gravity compensation via standard USD attribute pipeline. The latest commits address maintainer feedback by moving joint-level gravity comp from the Newton Articulation class to JointDrivePropertiesCfg in the schemas module.
Design Assessment: ✅ Correct Approach
The PR correctly places gravity compensation in the schemas layer (RigidBodyPropertiesCfg for body-level, JointDrivePropertiesCfg for joint-level) rather than in Newton-specific code. This follows the IsaacLab config → USD attributes → Newton ModelBuilder → MuJoCo pipeline. The @apply_nested decorator integration is correct.
Architecture Impact: Low-Medium
- Two new optional fields on existing config classes (non-breaking)
- Attributes are backend-agnostic at the USD level (written as
mjc:*prefixed attributes) - No cross-module API changes
Implementation Verdict: 🟡 Needs Work Before Merge
The core gravity compensation logic is correct, but the branch is severely behind develop and the newton dependency pin directly contradicts maintainer feedback.
🔴 Critical Issues
1. Branch is massively behind develop — merge conflicts confirmed
- PR targets
developwhich is atisaaclab==4.5.24/isaaclab_newton==0.5.9 - This PR's branch is at
isaaclab==4.5.7/isaaclab_newton==0.4.2 mergeable: CONFLICTING— GitHub confirms merge conflicts exist- The
setup.pystructure has fundamentally changed ondevelop(dependencies moved frominstall_requirestoextras_require["all"], package list expanded significantly) - Action: Rebase onto develop. All version numbers, changelogs, and setup.py changes need to be re-done against HEAD.
2. Newton pinned to unreleased git commit — reviewer explicitly requested newton==1.0.0
setup.pystill has:newton @ git+https://github.com/newton-physics/newton.git@813f828...- Maintainer comment from Mar 13 explicitly suggested
newton==1.0.0 developbranch already usesnewton==1.0.0- Action: Pin to
newton==1.0.0(or later stable release).
🟡 Medium Issues
3. Newton E2E test may not belong in IsaacLab
test_actuator_gravity_comp.pyimportsnewton,newton.solvers.SolverMuJoCo, creates Newton models, and exports MJCF XML- Maintainer feedback (Mar 13): "This is more a newton level test. Not sure it should be in lab."
- The schema-level unit tests in
test_schemas.pyare appropriate — they verify USD attributes without Newton dependency - Action: Consider moving the E2E test to the newton project, or at minimum mark it with a skip condition if newton is not installed.
4. Spurious whitespace deletion in CHANGELOG
The diff removes a blank line between changelog sections — a drive-by change that will create unnecessary merge conflicts.
🟢 Minor / Nits
5. gravity_compensation_scale docstring should document the expected value range
MuJoCo's gravcomp is a multiplier where 0.0 = no compensation and 1.0 = full compensation. The docstring should mention this to help users.
6. Test validation helper skips are correct but fragile
The hardcoded skip lists in _validate_rigid_body_properties_on_prim and _validate_joint_drive_properties_on_prim will accumulate technical debt as more backend-specific attributes are added. Not blocking.
✅ What's Good
- Gravity comp values are correctly
pop()-ed from the cfg dict before the PhysX attribute loop — no leakage intophysxRigidBody:*or USD drive attributes Nonehandling is correct: attributes are only written when explicitly set- Schema test coverage for both positive and negative cases (attribute set and not-set)
- RST changelog formatting is correct (underline lengths match section titles)
- Docstrings note Newton-only scope with appropriate
.. note::directives
Test Coverage: Adequate
- 4 new schema-level tests for body and joint gravity comp (positive + negative)
- 1 E2E test through Newton/MJCF (questionable location, see #3)
- Tests correctly use
@pytest.mark.isaacsim_ci
CI Status
- Only
labelercheck ran (pass) — no substantive CI (likely due to stale branch)
Branch Status: ❌ CONFLICTING
Must rebase onto develop before merge is possible.
Bottom Line
The core schema changes are clean and follow the right pattern. But this PR cannot merge in its current state due to: (1) massive divergence from develop causing merge conflicts, (2) newton dependency still pinned to a git commit instead of a stable release. Rebase, fix the newton pin, and re-evaluate the E2E test location.
| @@ -22,7 +22,7 @@ | |||
| # newton | |||
| "mujoco==3.5.0", | |||
| "mujoco-warp==3.5.0.2", | |||
There was a problem hiding this comment.
Newton pin must use a stable release, not a git commit.
Maintainer feedback from Mar 13 explicitly asked for newton==1.0.0. The develop branch already uses newton==1.0.0. A git commit pin is not acceptable for a merge into develop — it's non-reproducible for users without git access and bypasses release validation.
| "mujoco-warp==3.5.0.2", | |
| "newton==1.0.0", |
|
|
||
| This attribute is only supported by the Newton (MuJoCo) simulation backend. | ||
| When using other backends (e.g. PhysX), the attribute is written to USD but has no effect. | ||
| """ |
There was a problem hiding this comment.
The docstring should document the expected range to match MuJoCo semantics.
| """ | |
| gravity_compensation_scale: float | None = None | |
| """Scale factor for gravity compensation for the body. Defaults to None (attribute not written to USD). | |
| A value of ``0.0`` means no gravity compensation; ``1.0`` means full compensation (the body | |
| behaves as if gravity does not act on it). Values between 0 and 1 provide partial compensation. | |
| .. note:: | |
| This attribute is only supported by the Newton (MuJoCo) simulation backend. | |
| When using other backends (e.g. PhysX), the attribute is written to USD but has no effect. | |
| """ |
| @@ -0,0 +1,192 @@ | |||
| # Copyright (c) 2022-2026, The Isaac Lab Project Developers (https://github.com/isaac-sim/IsaacLab/blob/main/CONTRIBUTORS.md). | |||
There was a problem hiding this comment.
Consider moving this E2E test out of IsaacLab.
Maintainer feedback (Mar 13): "This is more a newton level test. Not sure it should be in lab."
The test imports newton and newton.solvers.SolverMuJoCo, builds Newton models, and exports MJCF XML. This is integration testing the Newton pipeline, not IsaacLab schemas. The schema-level unit tests in test_schemas.py already validate that the USD attributes are written correctly.
Options:
- Move this test to the Newton project
- Add a
pytest.importorskip("newton")guard at minimum - Mark with a dedicated
@pytest.mark.newtonmarker
|
|
||
| # Note: Semantic Versioning is used: https://semver.org/ | ||
| version = "4.5.6" | ||
| version = "4.5.7" |
There was a problem hiding this comment.
Version will conflict on rebase. develop is at 4.5.24. This will need to be updated after rebasing.
|
|
||
| # Note: Semantic Versioning is used: https://semver.org/ | ||
| version = "0.4.0" | ||
| version = "0.4.2" |
There was a problem hiding this comment.
Version will conflict on rebase. develop is at 0.5.9. This will need to be updated after rebasing.
| @@ -35,7 +48,6 @@ Changed | |||
| control, and ``wp.synchronize()`` before stopping to ensure accurate | |||
| GPU timing. | |||
|
|
|||
There was a problem hiding this comment.
Spurious blank line removal between changelog sections. This is a drive-by change that will cause unnecessary merge conflicts.
| 4.5.3 (2026-03-05) |
55c2386 to
5c744aa
Compare
Add gravity_compensation field to JointDrivePropertiesCfg and handle it in modify_joint_drive_properties() by writing mjc:actuatorgravcomp to USD joint prims. Also add body-level gravity_compensation_scale to RigidBodyPropertiesCfg (mjc:gravcomp on rigid body prims). Both features are Newton/MuJoCo backend specific and are written to USD at spawn time through the schemas layer.
5c744aa to
36d8d92
Compare
|
@AntoineRichard I have update the approach to adding joint level gravity compensation - now using JointDrivePropertiesCFg. I am inclined to keep the end to end to test for sanity check but if you think the test is too out of scope for IsaacLab, I can remove it. |
Signed-off-by: myurasov-nv <168484206+myurasov-nv@users.noreply.github.com>
There was a problem hiding this comment.
Follow-up review (cfaf218): New commits revert the newton==1.0.0 stable pin back to a git commit in isaaclab_newton/setup.py and isaaclab_visualizers/setup.py (3 occurrences). If the new cubric/renderer features require unreleased Newton APIs, those APIs should land in a Newton release first — otherwise revert to newton==1.0.0 per previous maintainer feedback.
The change in newton commit was when @myurasov-nv merged develop. The develop is not pegged against a set release anymore but instead to a commit of newton. |
|
Thanks for adding this nice feature to IsaacLab: I am a bit concerned on exposing a newton attribute through current "PhysxSchema" I'd say IsaacLab current schema is very confusing, and thats our fault, we are basically using is PhysxSchema, and mapping newton attribute from physx. So if it is physxSchema, let's not add a newton attribute there. A better way to do it would be adding a gravity compensation through event, please reference below, you could have a unified event (thats the best) or a newton only event that changes attribute at startup phase, (reference below). |
AntoineRichard
left a comment
There was a problem hiding this comment.
There are a couple small changes to do and it should be good!
| # ------------------------------------------------------------------- | ||
|
|
||
|
|
||
| @pytest.mark.isaacsim_ci |
There was a problem hiding this comment.
We don't need to mark that for CI
| assert attr.Get() is True | ||
|
|
||
|
|
||
| @pytest.mark.isaacsim_ci |
| # ------------------------------------------------------------------- | ||
|
|
||
|
|
||
| @pytest.mark.isaacsim_ci |
There was a problem hiding this comment.
This is not needed
| * Fixed mask type handling in ``test_rigid_object_collection_iface.py`` to use | ||
| consistent mask types across backends. | ||
|
|
||
|
|
| control, and ``wp.synchronize()`` before stopping to ensure accurate | ||
| GPU timing. | ||
|
|
||
|
|
There was a problem hiding this comment.
@vidurv-nvidia could you make sure there is no needless conflicts in the Changelog?
|
Deprecated |
…ields (#5275) # Description Splits IsaacLab's USD-physics cfg classes into solver-common base classes and backend-specific subclasses, and refactors the writers (`modify_*_properties`, `spawn_rigid_body_material`) so that schema application is data-driven rather than hard-coded per-class. Prepares the schema layer for multi-backend support (PhysX today, Newton/Mjc next) without polluting base classes with silently-ignored fields or stamping backend-specific schemas onto prims that didn't opt in. ## Architecture Two layered concepts: 1. **Per-declaring-class routing.** Each cfg field's USD namespace is determined by the class that declares it (walking the MRO). Base-class fields write under `physics:*`; subclass fields write under their own namespace (`physxRigidBody:*`, etc.). When a `PhysxRigidBodyPropertiesCfg` instance is written, base fields still go under `physics:*` because `_usd_namespace` is read from the declaring class via `__dict__`, not via `getattr` (which would hit the subclass override). 2. **Per-field exceptions.** Some "universal physics" fields have no USD path except through a backend-namespaced attribute today (e.g., `disable_gravity` only exists at `physxRigidBody:disableGravity`). These are declared as `_usd_field_exceptions = {applied_schema: (namespace, [fields...])}` on the base class; the writer applies the exception schema only when one of the listed fields is non-None. The single helper `_apply_namespaced_schemas(prim, cfg, cfg_dict)` in `schemas.py` does both passes for every writer (rigid body, collision, articulation root, joint drive, mesh collision, rigid-body material). ## Design constraints **One cfg class per spawner slot.** Spawners (`UsdFileCfg`, `MeshCuboidCfg`, etc.) carry a single field for each property group: `rigid_props: RigidBodyBaseCfg | None`, `collision_props: CollisionBaseCfg | None`, `joint_drive_props: JointDriveBaseCfg | None`, etc. The user cannot pass two cfgs into the same slot, so the cfg class hierarchy must be **single-rooted per spawner field** — one base class per group, with backend-specific subclasses below. This rules out a "PhysX cfg sits next to a Newton cfg as siblings" design and drives several placement decisions: | Constraint | Consequence | |---|---| | Universal-physics fields must be reachable from any backend's cfg | Goes on the **base** class, not a sibling backend cfg. Users on Newton-only deployments can use `RigidBodyBaseCfg(disable_gravity=True)` without importing `isaaclab_physx`. | | A PhysX-namespaced field whose semantics are universal (e.g., `disable_gravity`) | Lives on the base but routes to the PhysX namespace via `_usd_field_exceptions`. The base stays backend-clean; the writer dispatches the PhysX write only when the field is non-None. | | Writer logic must not branch on cfg subclass | Every writer is the same code path regardless of subclass. The cfg metadata (`_usd_namespace`, `_usd_applied_schema`, `_usd_field_exceptions`) drives behavior; the writer is a pure data interpreter. | | Adding a new backend (Newton, Mjc) | Requires a new subclass with its own `_usd_namespace` / `_usd_applied_schema`. No spawner-side changes, no writer-side changes, no base-cfg-side changes. | | A field has multiple USD paths today (one PhysX-namespaced, one Newton-namespaced) | Belongs on the **PhysX subclass**, not the base. A future `NewtonArticulationRootPropertiesCfg` will own the same conceptual field on the Newton side. ("Rule 2" — e.g., `enabled_self_collisions`.) | | A field has only one USD path today, namespaced under PhysX, but the conceptual quantity is universal | Belongs on the **base** with an `_usd_field_exceptions` entry. ("Rule 1" — e.g., `disable_gravity`, `articulation_enabled`, `contact_offset`, `rest_offset`, `max_joint_velocity`.) When Newton ships its own native attribute, the exception namespace switches transparently with no API change. | ## Field placement ### Base (solver-common) classes — `physics:*` namespace via `UsdPhysics.*API` | Cfg class | Field | USD attribute | |---|---|---| | `RigidBodyBaseCfg` | `rigid_body_enabled` | `physics:rigidBodyEnabled` | | `RigidBodyBaseCfg` | `kinematic_enabled` | `physics:kinematicEnabled` | | `CollisionBaseCfg` | `collision_enabled` | `physics:collisionEnabled` | | `MassPropertiesCfg` | `mass` | `physics:mass` | | `MassPropertiesCfg` | `density` | `physics:density` | | `RigidBodyMaterialBaseCfg` | `static_friction` | `physics:staticFriction` | | `RigidBodyMaterialBaseCfg` | `dynamic_friction` | `physics:dynamicFriction` | | `RigidBodyMaterialBaseCfg` | `restitution` | `physics:restitution` | | `JointDriveBaseCfg` | `drive_type` | `drive:<axis>:physics:type` | | `JointDriveBaseCfg` | `max_force` | `drive:<axis>:physics:maxForce` | | `JointDriveBaseCfg` | `stiffness` | `drive:<axis>:physics:stiffness` | | `JointDriveBaseCfg` | `damping` | `drive:<axis>:physics:damping` | | `MeshCollisionBaseCfg` | `mesh_approximation_name` | `physics:approximation` (token) | | `ArticulationRootBaseCfg` | `fix_root_link` | (synthesizes `UsdPhysics.FixedJoint`) | `JointDriveBaseCfg` and `MeshCollisionBaseCfg` use the typed `UsdPhysics.DriveAPI` / `UsdPhysics.MeshCollisionAPI` accessors at the writer level (multi-instance namespace and `TfToken` with `allowedTokens`, respectively); all other base fields flow through the helper's per-class routing. ### PhysX subclasses — `physx*:*` namespaces, `Physx*API` schemas | Cfg class | `_usd_namespace` | `_usd_applied_schema` | Adds fields | |---|---|---|---| | `PhysxRigidBodyPropertiesCfg` | `physxRigidBody` | `PhysxRigidBodyAPI` | `linear_damping`, `angular_damping`, `max_linear_velocity`, `max_angular_velocity`, `max_depenetration_velocity`, `max_contact_impulse`, `enable_gyroscopic_forces`, `retain_accelerations`, solver iter counts, sleep / stabilization thresholds | | `PhysxCollisionPropertiesCfg` | `physxCollision` | `PhysxCollisionAPI` | `torsional_patch_radius`, `min_torsional_patch_radius` | | `PhysxArticulationRootPropertiesCfg` | `physxArticulation` | `PhysxArticulationAPI` | `enabled_self_collisions`, solver iter counts, sleep / stabilization thresholds | | `PhysxJointDrivePropertiesCfg` | `physxJoint` | `PhysxJointAPI` | (currently empty; reserved for future PhysX-only knobs) | | `PhysxRigidBodyMaterialCfg` | `physxMaterial` | `PhysxMaterialAPI` | `compliant_contact_stiffness`, `compliant_contact_damping`, `friction_combine_mode`, `restitution_combine_mode` | | `PhysxConvexHullPropertiesCfg` | `physxConvexHullCollision` | `PhysxConvexHullCollisionAPI` | `hull_vertex_limit`, `min_thickness` | | `PhysxConvexDecompositionPropertiesCfg` | `physxConvexDecompositionCollision` | `PhysxConvexDecompositionCollisionAPI` | hull / voxel / shrink-wrap tunables | | `PhysxTriangleMeshPropertiesCfg` | `physxTriangleMeshCollision` | `PhysxTriangleMeshCollisionAPI` | `weld_tolerance` | | `PhysxTriangleMeshSimplificationPropertiesCfg` | `physxTriangleMeshSimplificationCollision` | `PhysxTriangleMeshSimplificationCollisionAPI` | `simplification_metric`, `weld_tolerance` | | `PhysxSDFMeshPropertiesCfg` | `physxSDFMeshCollision` | `PhysxSDFMeshCollisionAPI` | `sdf_margin`, `sdf_narrow_band_thickness`, `sdf_resolution`, etc. | ### `_usd_field_exceptions` table These fields are declared on a *base* class but the only USD path today goes through a non-base namespace. Each entry says: "if any listed field on this cfg is non-None, apply the exception schema and write that one attribute under the exception namespace." All other fields on the cfg follow the per-declaring-class routing rule. | Base cfg class | Exception schema | Namespace | Field(s) | Why on the base | |---|---|---|---|---| | `RigidBodyBaseCfg` | `PhysxRigidBodyAPI` | `physxRigidBody` | `disable_gravity` | Per-body gravity exclusion is universal physics; PhysX honors per-body, Newton consumes the same attribute via the bridge resolver (scene-level today; per-body fix is a Newton-side kernel change, not a cfg-API change) | | `CollisionBaseCfg` | `PhysxCollisionAPI` | `physxCollision` | `contact_offset`, `rest_offset` | Collision-pair generation distance and rest gap are universal physics; Newton importer consumes both via PhysX bridge to populate `Model.shape_collision_radius` / `_thickness` (`import_usd.py:2104, 2111`) | | `ArticulationRootBaseCfg` | `PhysxArticulationAPI` | `physxArticulation` | `articulation_enabled` | PhysX honors at sim time; IsaacLab Newton wrapper reads it as a spawn-time guard at `rigid_object.py:1035`. Universal user-facing intent | | `JointDriveBaseCfg` | `PhysxJointAPI` | `physxJoint` | `max_joint_velocity` | Sole USD path to `Model.joint_velocity_limit` in Newton (no `newton:*` equivalent today). The exception namespace switches transparently when Newton ships `newton:maxJointVelocity` as a registered applied API | When any exception field is non-None, the corresponding `Physx*API` schema is applied to the prim. When all exception fields are None, no PhysX schema is stamped — Newton-targeted prims authored from `*BaseCfg` stay free of PhysX schemas they didn't opt in to. ## Field renames (with deprecation aliases) To enforce the convention that python `snake_case` cfg field names map identity-style to USD `camelCase` attribute names, two legacy fields were renamed. Both keep the old name as a deprecation alias forwarded via `__post_init__` (emits `DeprecationWarning`, scheduled for removal in 5.0). | Old name | New name | USD attribute | |---|---|---| | `JointDriveBaseCfg.max_velocity` | `max_joint_velocity` | `physxJoint:maxJointVelocity` | | `JointDriveBaseCfg.max_effort` | `max_force` | `drive:<axis>:physics:maxForce` | ## Type of change - New feature (non-breaking change which adds functionality) - Breaking change (existing functionality will not work without user modification) The split is non-breaking at the spawner-cfg level — every base-class type accepts any subclass via polymorphism, and every legacy `RigidBodyPropertiesCfg` / `JointDrivePropertiesCfg` / `CollisionPropertiesCfg` / `ArticulationRootPropertiesCfg` / `MeshCollisionPropertiesCfg` / `RigidBodyMaterialCfg` / `FixedTendonPropertiesCfg` / `SpatialTendonPropertiesCfg` import path continues to work via deprecation-alias subclasses and `__getattr__` shims on `isaaclab.sim`, `isaaclab.sim.schemas`, and `isaaclab.sim.schemas.schemas_cfg`. Direct attribute access to the renamed fields still works through deprecation aliases. Removal scheduled for 5.0. The breaking aspect: cfg classes in `isaaclab_physx.sim.schemas` and `isaaclab_physx.sim.spawners.materials` are physically relocated. Anyone importing from internal paths (rather than `isaaclab.sim`) needs to update. ## Migration ```python # Before import isaaclab.sim as sim_utils rigid_props = sim_utils.RigidBodyPropertiesCfg(disable_gravity=True, linear_damping=0.1) joint_props = sim_utils.JointDrivePropertiesCfg(max_effort=80.0, max_velocity=5.0) collision_props = sim_utils.CollisionPropertiesCfg(contact_offset=0.02, torsional_patch_radius=1.0) material = sim_utils.RigidBodyMaterialCfg(static_friction=0.7, compliant_contact_stiffness=1000.0) # After (PhysX-targeted) import isaaclab.sim as sim_utils from isaaclab_physx.sim.schemas import ( PhysxRigidBodyPropertiesCfg, PhysxJointDrivePropertiesCfg, PhysxCollisionPropertiesCfg, ) from isaaclab_physx.sim.spawners.materials import PhysxRigidBodyMaterialCfg rigid_props = PhysxRigidBodyPropertiesCfg(disable_gravity=True, linear_damping=0.1) joint_props = PhysxJointDrivePropertiesCfg(max_force=80.0, max_joint_velocity=5.0) collision_props = PhysxCollisionPropertiesCfg(contact_offset=0.02, torsional_patch_radius=1.0) material = PhysxRigidBodyMaterialCfg(static_friction=0.7, compliant_contact_stiffness=1000.0) # After (Newton-targeted — base classes only, no PhysX schemas applied) from isaaclab.sim.schemas import RigidBodyBaseCfg, JointDriveBaseCfg, CollisionBaseCfg from isaaclab.sim.spawners.materials import RigidBodyMaterialBaseCfg rigid_props = RigidBodyBaseCfg(disable_gravity=True) # only base + exception fields available joint_props = JointDriveBaseCfg(max_force=80.0, max_joint_velocity=5.0) material = RigidBodyMaterialBaseCfg(static_friction=0.7) ``` Spawner type annotations remain unchanged — they accept any subclass via polymorphism. ## Internal helper ```python def _apply_namespaced_schemas(prim, cfg, cfg_dict): # 1. Per-field exceptions: pop listed fields, apply exception schema if any non-None, # write under exception namespace. # 2. Per-declaring-class routing: walk MRO to find each remaining field's owner class; # write under that class's _usd_namespace; apply that class's _usd_applied_schema. ``` Used by all five `modify_*_properties` writers and `spawn_rigid_body_material`. Replaced ~125 lines of duplicated gating logic with a single ~30-line helper. ## Side change: configclass `source/isaaclab/isaaclab/utils/configclass.py:_process_mutable_types` now detects string-form `ClassVar` annotations under PEP 563 (`from __future__ import annotations`) so it doesn't wrap `ClassVar[dict]` defaults in `field(default_factory=...)`. Matches Python stdlib `dataclasses` semantics. No pre-existing IsaacLab class used `ClassVar` inside a `@configclass` block, so the change has no effect on existing code; it enables the `ClassVar` metadata pattern this PR introduces. ## Test plan - [x] `test_schemas.py` (38 → 40 tests): all schema-cfg classes write correct attributes under the right namespace; PhysX schemas are NOT applied when only base/UsdPhysics fields are set; deprecation aliases (`max_velocity` → `max_joint_velocity`, `max_effort` → `max_force`) forward correctly and emit `DeprecationWarning`. **40 passed.** - [x] `test_schemas_shim.py`: legacy import paths (`isaaclab.sim.schemas.RigidBodyPropertiesCfg` etc.) resolve via `__getattr__` shims. **All passing.** - [x] `test_articulation.py`, `test_rigid_object_iface.py`, `test_valid_configs.py`, `test_spawn_*` — no regressions. - [x] Full suite (`./isaaclab.sh -t`): 8768/9205 pass, 437 unrelated baseline failures (rendering, `omni.physics.tensors.api` missing, OSC controller, `install_ci`, `pyglet`, Newton env-path, Anymal-C determinism). Zero new regressions; +123 passing tests vs. earlier state. - [x] `./isaaclab.sh -f` (pre-commit) clean. ## Supersedes Together with #5276, supersedes #4847 and #5203 with a cleaner schema-layer design. ## Checklist - [x] I have read and understood the [contribution guidelines](https://isaac-sim.github.io/IsaacLab/main/source/refs/contributing.html) - [x] I have run the [`pre-commit` checks](https://pre-commit.com/) with `./isaaclab.sh --format` - [x] I have made corresponding changes to the documentation (changelog fragments under `source/isaaclab/changelog.d/`) - [x] My changes generate no new warnings - [x] I have added tests that prove my fix is effective or that my feature works - [x] I have updated the changelog (fragment-based system) and the corresponding version in the extension's `config/extension.toml` file - [x] I have added my name to the `CONTRIBUTORS.md` or my name already exists there --------- Co-authored-by: ooctipus <zhengyuz@nvidia.com>
) # Description Adds Newton-native and MuJoCo-specific schema cfg classes to `isaaclab_newton.sim.schemas`, following the base/subclass framework from #5275. All new cfgs use the per-declaring-class MRO routing in `_apply_namespaced_schemas` — no backend-specific branching in any writer. Depends on #5275. ## New cfgs ### MuJoCo (Newton MuJoCo kernel, `mjc:*` namespace) | Class | Field | USD attribute | Applied schema | |---|---|---|---| | `MujocoRigidBodyPropertiesCfg` | `gravcomp` | `mjc:gravcomp` | None (raw attr) | | `MujocoJointDrivePropertiesCfg` | `actuatorgravcomp` | `mjc:actuatorgravcomp` | `MjcJointAPI` | Body-level `gravcomp` must be set for joint-level `actuatorgravcomp` to have any effect. The spawner auto-enables `MujocoRigidBodyPropertiesCfg(gravcomp=1.0)` when joint-level actuator gravcomp is requested without body-level gravcomp. ### Newton-native (`newton:*` namespace) | Class | Fields | USD attributes | Applied schema | |---|---|---|---| | `NewtonCollisionPropertiesCfg` | `contact_margin`, `contact_gap` | `newton:contactMargin`, `newton:contactGap` | `NewtonCollisionAPI` | | `NewtonMeshCollisionPropertiesCfg` | `max_hull_vertices` | `newton:maxHullVertices` | `NewtonMeshCollisionAPI` | | `NewtonMaterialPropertiesCfg` | `torsional_friction`, `rolling_friction` | `newton:torsionalFriction`, `newton:rollingFriction` | `NewtonMaterialAPI` | | `NewtonArticulationRootPropertiesCfg` | `self_collision_enabled` | `newton:selfCollisionEnabled` | `NewtonArticulationRootAPI` | ## Design constraints Same single-cfg-per-spawner-slot rule as #5275. Newton cfgs subclass the same base classes as PhysX cfgs; each declares `_usd_namespace`/`_usd_applied_schema` (ClassVar) and fields that auto-camelCase to their USD attr names. Per-declaring-class MRO routing handles mixed PhysX+Newton cfg hierarchies correctly. ## Field renames (with deprecation aliases through 5.0) | Old | New | Reason | |---|---|---| | `gravity_compensation_scale` | `gravcomp` | Single word identity: `gravcomp` → `mjc:gravcomp` | | `gravity_compensation` | `actuatorgravcomp` | Single word identity: `actuatorgravcomp` → `mjc:actuatorgravcomp` | ## Type of change - New feature (non-breaking) Forwarding shims on `isaaclab.sim.schemas` keep existing imports working. Deprecation aliases keep old field names working through 5.0. ## Test plan - [x] MuJoCo tests: `mjc:gravcomp` / `mjc:actuatorgravcomp` written when set, not written when None - [x] Newton collision, material, articulation-root: attrs written, schemas applied only when non-None - [x] Deprecation alias tests for renamed fields - [x] `test_schemas.py` 46/46 pass — no regressions - [x] Pre-commit clean ## Supersedes Together with #5275, supersedes #4847 and #5203. --------- Co-authored-by: Kelly Guo <kellyg@nvidia.com> Co-authored-by: Antoine RICHARD <antoiner@nvidia.com>
Description
Adds gravity compensation support for both rigid bodies (body-level) and joints (joint-level) when using the Newton/MuJoCo
backend.
Body-level: A new gravity_compensation_scale field on RigidBodyPropertiesCfg writes the mjc:gravcomp attribute to rigid
body USD prims, allowing Newton's MuJoCo solver to apply partial or full gravity compensation per body (0.0 = no
compensation, 1.0 = full).
Joint-level: A new gravity_compensation field on JointDrivePropertiesCfg writes mjc:actuatorgravcomp to joint USD prims
via modify_joint_drive_properties(). This is applied at spawn time through the schemas layer and flows automatically
through FileCfg.joint_drive_props.
The pipeline is: IsaacLab config → USD attributes → Newton ModelBuilder → MuJoCo solver / MJCF XML.
Type of change
Checklist
pre-commitchecks with./isaaclab.sh --formatconfig/extension.tomlfileCONTRIBUTORS.mdor my name already exists there