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
- Schema doc (above):
jointPos + gearing·refPos + offset = 0.
newtonMigrationChecker.py: newton:mimicCoef1 = −gearing.
- 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
- Author a revolute joint with
PhysxMimicJointAPI (gearing = −1) referencing a driver with limits 0..47, follower limits 0..47.
- Run
simready-validate with the FET-022 / DJ.007 profile.
- DJ.007 fails the limit sub-check (demands
lower < 0); setting −47..0 passes validation but the joint clamps in PhysX.
Summary
The joint-limit portion of the DJ.007
mimic-api-check(MimicAPICheckinvalidation.py) computes a mimic follower's expected range asreference_limit × gearing. The actualPhysxMimicJointAPIconstraint isjointPos + 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 withgearing < 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
physics_driven_joints/DJ.007—requirements/mimic-api-check.md.../physics_driven_joints/validation.py, classMimicAPICheck(limit-check block)simready-validate==2026.4.9; spec atNVIDIA/simready-foundation@0ed0dfbc539c9de99289771bd6848effe3ef5779Documented relationship (the ground truth)
NVIDIA's
PhysxSchemaPhysxMimicJointAPIclass doc states:Solving for the follower (
offset = 0):jointPosition = −gearing × referenceJointPosition.The same convention appears in NVIDIA's own
newtonMigrationChecker.py, which setsnewton:mimicCoef1 = −gearingwhen converting PhysX→Newton, precisely because the follower is−gearing·ref.The bug
MimicAPICheckcomputes the expected follower range withreference × gear_ratio(no minus sign):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, referencefinger_jointwith limits0..47.−(−1) × [0, 47] = [0, +47].gearing × [0,47] = [−47, 0], so it requiresself_lower < 0andself_upper > −47.Resulting contradiction:
[0,47](works)0..47(physically correct)lower = 0not< 0)−47..0(what the check wants)0Setting the limits to
−47..0to 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 × referencejointPos + gearing·refPos + offset = 0.newtonMigrationChecker.py:newton:mimicCoef1 = −gearing.PhysxMimicJointAPI,gearing=−1, follower limits widened so nothing clamps): drivingfinger_joint0→47°producedright_outer_knuckle_joint0→+47°(tracks+reference). If the follower weregearing × referenceit would have gone0→−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..180mask the bug because they satisfy the inequality for either sign.)Proposed fix
Use
−gear_ratiowhen 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 thegear_ratioused in the four comparisons, or restructure to:Repro steps
PhysxMimicJointAPI(gearing = −1) referencing a driver with limits0..47, follower limits0..47.simready-validatewith the FET-022 / DJ.007 profile.lower < 0); setting−47..0passes validation but the joint clamps in PhysX.