Skip to content

DJ.007 (mimic-api-check) joint-limit check uses gearing × reference but PhysX mimic joints are −gearing × reference (sign error) #32

Description

@l-tschreiber-a11y

Summary

The joint-limit portion of the DJ.007 mimic-api-check (MimicAPICheck in validation.py) computes a mimic follower's expected range as reference_limit × gearing. The actual PhysxMimicJointAPI constraint is jointPos + gearing·refPos + offset = 0, i.e. the follower resolves to −gearing × reference. Because of the dropped minus sign, the check's "valid" limit window is the mirror image (across zero) of where the joint physically moves. For any mimic joint with gearing < 0 (or > 0) and tight, one-sided limits, this makes the check reject the physically-correct limits and accept limits that clamp the joint.

Affected

  • Rule: physics_driven_joints / DJ.007 — requirements/mimic-api-check.md
  • Validator: .../physics_driven_joints/validation.py, class MimicAPICheck (limit-check block)
  • Observed with simready-validate==2026.4.9; spec at NVIDIA/simready-foundation@0ed0dfbc539c9de99289771bd6848effe3ef5779

Documented relationship (the ground truth)

NVIDIA's PhysxSchema PhysxMimicJointAPI class doc states:

"interconnect the degrees of freedom of two joints according to the relationship: jointPosition + (gearing * referenceJointPosition) + offset = 0."

Solving for the follower (offset = 0): jointPosition = −gearing × referenceJointPosition.

The same convention appears in NVIDIA's own newtonMigrationChecker.py, which sets newton:mimicCoef1 = −gearing when converting PhysX→Newton, precisely because the follower is −gearing·ref.

The bug

MimicAPICheck computes the expected follower range with reference × gear_ratio (no minus sign):

if gear_ratio < 0:
    if not reference_joint_lower_limit * gear_ratio > self_joint_lower_limit:   # uses +gear
        self._AddFailedCheck(...)
    if not self_joint_upper_limit > reference_joint_upper_limit * gear_ratio:
        self._AddFailedCheck(...)
else:
    if not reference_joint_lower_limit * gear_ratio < self_joint_upper_limit:
        self._AddFailedCheck(...)
    if not self_joint_lower_limit < reference_joint_upper_limit * gear_ratio:
        self._AddFailedCheck(...)

This is correct only if the follower were gearing × reference. Since it is actually −gearing × reference, the check targets the wrong side of zero.

Concrete example (Robotiq 2F-85, Physx_parallel_grip)

  • right_outer_knuckle_joint: gearing = −1, reference finger_joint with limits 0..47.
  • Actual follower range: −(−1) × [0, 47] = [0, +47].
  • DJ.007 assumes: gearing × [0,47] = [−47, 0], so it requires self_lower < 0 and self_upper > −47.

Resulting contradiction:

Follower limits Contains real range [0,47] (works) Passes DJ.007
0..47 (physically correct) ✅ ❌ (lower = 0 not < 0)
−47..0 (what the check wants) ❌ clamps joint at 0 ✅

Setting the limits to −47..0 to satisfy the check makes the joint jam at its upper bound (0) and the gripper's right side no longer closes.

Evidence it's −gearing × reference

  1. Schema doc (above): jointPos + gearing·refPos + offset = 0.
  2. newtonMigrationChecker.py: newton:mimicCoef1 = −gearing.
  3. Direct simulation (Isaac Sim 6, PhysxMimicJointAPI, gearing=−1, follower limits widened so nothing clamps): driving finger_joint 0→47° produced right_outer_knuckle_joint 0→+47° (tracks +reference). If the follower were gearing × reference it would have gone 0→−47°.
finger_joint right_outer_knuckle_joint
0° +0°
10° +10°
20° +20°
30° +30°
40° +40°
47° +47°

Impact

Any conformant asset with a gearing < 0 (or > 0) mimic joint and a tight, one-sided follower range cannot pass DJ.007 with physically-correct limits. The only way to pass is to author limits that clamp the joint — i.e. the check pushes authors toward a broken configuration. (Mimics with wide symmetric limits like −180..180 mask the bug because they satisfy the inequality for either sign.)

Proposed fix

Use −gear_ratio when mapping the reference limits into the follower's expected range (or, equivalently, compute the follower bounds as −gearing × [ref_lower, ref_upper] and require the follower's limits to contain them). Concretely, negate the gear_ratio used in the four comparisons, or restructure to:

lo = -gear_ratio * reference_joint_lower_limit
hi = -gear_ratio * reference_joint_upper_limit
fmin, fmax = min(lo, hi), max(lo, hi)
if not (self_joint_lower_limit <= fmin and self_joint_upper_limit >= fmax):
    self._AddFailedCheck(...)   # follower limits must contain [-gearing*ref_lo, -gearing*ref_hi]

Repro steps

  1. Author a revolute joint with PhysxMimicJointAPI (gearing = −1) referencing a driver with limits 0..47, follower limits 0..47.
  2. Run simready-validate with the FET-022 / DJ.007 profile.
  3. DJ.007 fails the limit sub-check (demands lower < 0); setting −47..0 passes validation but the joint clamps in PhysX.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions