Skip to content

Fix MJCF D6 collapse dropping distinct axes and leaving unused D6 axes free - #751

Open
aidaodedjl wants to merge 1 commit into
isaac-sim:mainfrom
aidaodedjl:fix/mjcf-d6-collapse-preserve-axes
Open

Fix MJCF D6 collapse dropping distinct axes and leaving unused D6 axes free#751
aidaodedjl wants to merge 1 commit into
isaac-sim:mainfrom
aidaodedjl:fix/mjcf-d6-collapse-preserve-axes

Conversation

@aidaodedjl

Copy link
Copy Markdown

Description

Fixes the PhysX D6 collapse step of the MJCF import pipeline (Isaac Sim 6.0, mjc_to_physx_conversion_utils) dropping distinct joint DOFs and leaving unused D6 axes free. Reported in isaac-sim/IsaacLab#6854.

Problem

When several single-axis joints connect the same body pair (e.g. a humanoid hip/shoulder/spine authored as three hinge joints — the standard authoring style in MJCF), _convert_overconstrained_group_to_d6() collapses them into one D6 joint. The axis of each source joint was derived from physics:axis alone.

But mujoco-usd-converter always exports single-axis joints x-aligned and encodes the real MJCF axis direction in localRot0/localRot1. So physics:axis is X for every joint in such a group. Three consequences:

  1. Lost DOFs: every joint after the first collided on the rotX token and was dropped with a "duplicates D6 axis" warning — even when its physical axis was distinct and exactly representable by a D6. A 3-hinge hip collapsed to a 1-DOF joint.
  2. Wrong rotation directions: the D6 frame was left unrotated, so surviving axes rotated about the wrong directions whenever the MJCF axes were not cardinal.
  3. Spurious free DOFs: the unused D6 axes were never locked, so the collapsed joint gained free translational DOFs in PhysX (the "robot explodes / drifts away" symptom in the report).

Fix

  • Recover the physical axis of each source joint from localRot0/localRot1 (_joint_axis_directions()).
  • Build a right-handed orthonormal D6 basis from the group's axes via Gram–Schmidt, and assign each joint to a basis column by dot product (_assign_axes_to_d6_basis()). The D6 frame now encodes the real axis directions.
  • Joints that genuinely cannot be represented (duplicate / non-orthogonal axes) are still dropped, but with a physically accurate warning.
  • Explicitly lock all six D6 axes except those backed by a source joint.
  • Record the source MJCF joint name on each used axis as mjcf:<axis>:name for traceability.

Validation

Unit tests (test_mjc_d6_collapse.py, plain pxr, no Kit): 8 tests covering 3-distinct-axes groups, duplicate-axis drops, mixed revolute/prismatic groups, 2-joint groups, per-axis limits/drives landing on the correct axis, unused-axis locking, and mjcf:<axis>:name metadata.

End-to-end (Linux, real pipeline: mujoco-usd-converter 0.5.0 → convert_mjc_to_physxcombine_overconstrained_joints_to_d6), original vs patched module on the issue's own repro models (ProtoMotions soma23_humanoid.xml / smpl_humanoid.xml) plus a minimal rotated-triad case:

Metric before after
soma23: D6 joints with per-axis mjcf:<axis>:name metadata 0/22 22/22
soma23: D6 joints with a free translational axis 22/22 0/22
smpl: D6 joints with axis metadata 0/23 23/23
smpl: D6 joints with a free translational axis 23/23 0/23
rotated triad (two 45° diagonal hinges): preserved rotational DOFs 1/3 3/3

In the rotated-triad case the old code logged j_b duplicates D6 axis 'rotX' ... DOF will be lost for a joint whose physical axis was perfectly distinct — with the fix all three DOFs survive with correct per-axis limits ([-45,45], [-30,60], [-90,90]), the D6 frame reproduces the diagonal world directions, and all translation axes are locked.

Checklist

  • Bug fix (non-breaking change which fixes an issue)
  • Regression tests added (standalone_tests/test_mjc_d6_collapse.py, runs with plain pxr)
  • Validated end-to-end against the issue's repro models

Refs isaac-sim/IsaacLab#6854

…s free

When the MJCF importer collapses an over-constrained group of single-axis
joints (e.g. a humanoid hip authored as three hinges) into one D6 joint,
the axis of each source joint was derived from physics:axis alone.
mujoco-usd-converter always exports single-axis joints x-aligned and
encodes the real MJCF axis direction in localRot0/localRot1, so
physics:axis is X for every joint in such a group. Consequences:

- Every joint after the first collided on the rotX token and was dropped,
  losing its DOF even when its physical axis was distinct (and exactly
  representable by a D6).
- The D6 frame was left unrotated, so the surviving axes rotated about the
  wrong directions whenever the MJCF axes were not cardinal.
- The five unused D6 axes were never locked, so the collapsed joint gained
  spurious free translational (and sometimes rotational) DOFs in PhysX.

Recover the physical axis of each source joint from localRot0/localRot1,
build a right-handed orthonormal D6 basis from the group's axes via
Gram-Schmidt, and assign each joint to a basis column by dot product.
Joints that cannot be represented (duplicate/non-orthogonal axes) are
still dropped but with a physically accurate warning. All six D6 axes are
now explicitly locked except the ones backed by a source joint, and each
used axis records the source MJCF joint name in mjcf:<axis>:name.

Refs isaac-sim/IsaacLab#6854
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant