Skip to content

[pull] develop from isaac-sim:develop - #62

Merged
pull[bot] merged 3 commits into
mataylor-nvidia:developfrom
isaac-sim:develop
Sep 24, 2026
Merged

pull[bot] merged 3 commits into
mataylor-nvidia:developfrom
isaac-sim:develop

Conversation

@pull

@pull pull Bot commented Sep 24, 2026 •

Copy link
Copy Markdown

See Commits and Changes for more details.


Created by pull[bot] (v2.0.0-alpha.4)

Can you help keep this open source service alive? 💖 Please sponsor : )

Lynn-hh and others added 3 commits September 24, 2026 08:11
# Description

Follow-up to #7967, which fixed this for PhysX. The OVPhysX joint-wrench
sensor had the same extra frame transform.

OVPhysX reports `LINK_INCOMING_JOINT_FORCE` in the **child-side joint
frame at the joint anchor**, the same convention as PhysX.
`isaaclab_ov/sensors/joint_wrench/kernels.py` treated that reading as
"body1's frame at body1's origin" and shifted/rotated it again by the
child joint pose (`localPos1`/`localRot1`). Readings were therefore
wrong whenever that pose was not identity.

This PR mirrors the PhysX correction in #7967 and makes the physical
contract explicit across all three backends:
- The OVPhysX split kernel passes force and torque through; unused USD
joint-frame buffers are removed.
- The shared `test_joint_wrench_frame` lives in
`source/isaaclab/test/sensors/joint_wrench_contract.py`. Newton, PhysX,
and OVPhysX import it directly into their existing sensor suites and
provide their existing `sim` fixtures, following the FrameView
contract-test pattern.
- The runtime test utility, forwarding test functions, and circular
OVPhysX frame-transform test are removed. The same physical scene and
analytic force/torque expectations remain shared.
- A small structural test checks that every joint-wrench backend imports
the shared tests, rejects backend dependencies in the contract, and
prevents restoration of the old utility.
- OVPhysX uses the existing `device_split` CI marker so CPU and CUDA
cases run in separate processes instead of silently skipping one device.

## Measurement (OVPhysX 0.6.3, before this PR)

The setup is a 0.5 kg tool welded to an arm. The tool's COM is 0.15 m
past the wrist anchor. I authored the child and parent frames of the
weld as below and compared the tool's reading with the analytic wrench.

| Child joint frame | Sensor force | Sensor torque | Analytic (joint
frame, at anchor) | Raw OVPhysX |
|---|---|---|---|---|
| identity | (0, 0, 4.905) | (0, -0.736, 0) | (0, 0, 4.905) / (0,
-0.736, 0) | = analytic |
| 90° about z | (0, 0, 4.905) | (0, **+0.736**, 0) | (0, 0, 4.905) /
(-0.736, 0, 0) | = analytic |
| anchor offset (lever 0.25 m) | (0, 0, 4.905) | (0, **-1.717**, 0) |
(0, 0, 4.905) / (0, -1.226, 0) | = analytic |
| 90° about z + offset | (0, 0, 4.905) | (**-0.491**, **1.226**, 0) |
(0, 0, 4.905) / (-1.226, 0, 0) | = analytic |
| 90° about x | (0, 0, **-4.905**) | (0, **0.736**, 0) | (0, 4.905, 0) /
(0, 0, 0.736) | = analytic |

In every case the raw OVPhysX tensor already matches the analytic
wrench, and only the extra transform makes the sensor output wrong. With
a 90° rotation about x, the reported force even points the wrong way.

## Type of change

- Bug fix (non-breaking change which fixes an issue)
  - Readings change only for joints whose child frame is not identity.
  - The documented `incoming_joint_frame` convention is unchanged.

## Tests

Local validation with OVPhysX 0.6.3, running backends in separate
processes (CUDA on an RTX 5090):

- Newton joint-wrench suite: 14 passed.
- PhysX joint-wrench suite: 16 passed.
- OVPhysX CPU pass (`-k 'cpu or not cuda'`): 12 passed, including the
device-independent pre-init test.
- OVPhysX CUDA pass (`-k cuda`): 11 passed.
- Shared-contract structural gate: 1 passed. Removing the OVPhysX
contract import makes it fail.
- Restoring the original OVPhysX sensor and kernel makes the shared
physical test fail on both CPU and CUDA with a maximum force-component
error of 19.62 N.
- File-by-file architecture audit and `uv run --no-sync isaaclab -f`
passed.

The physical scenario count is unchanged; the existing per-backend frame
tests now collect the same test function. Full PR CI remains pending.

## Checklist

- [x] I have read and understood the contribution guidelines
- [x] I have run the `pre-commit` checks with `./isaaclab.sh --format`
- [x] I have made corresponding changes to the documentation (kernel
docstring)
- [x] I have added tests that prove my fix is effective or that my
feature works
- [x] I have added a changelog fragment for every touched package
- [x] I have added my name to the `CONTRIBUTORS.md` or my name already
exists there

cc @ooctipus

---------

Signed-off-by: Lynn <lynnhe02@gmail.com>
Co-authored-by: Octi Zhang <zhengyuz@nvidia.com>
# Description

`DifferentialInverseKinematicsAction._compute_frame_jacobian` and
`OperationalSpaceControllerAction._compute_ee_jacobian` shift the
body-origin Jacobian to the `body_offset` frame. `jacobian_b` is the
body-origin geometric Jacobian expressed in the **root** frame. The
previous correction had two problems:

1. **Translational rows.** The correction used `-[r]× J_w` with `r =
body_offset.pos` in the **body** frame. The offset point moves with `v +
w × (R_body_b r)`, so the lever arm has to be rotated into the root
frame first. The two only coincide when the body's orientation in the
root frame is identity. For the Franka IK tasks (`pos=(0, 0, 0.107)`,
hand pointing down), the lever arm's sign is effectively flipped.
2. **Angular rows.** They were multiplied by `R(body_offset.rot)`. The
offset frame is rigidly attached to the body, so its angular velocity in
the root frame equals the body's, and the angular rows must stay
unchanged.

The same terms already compute the offset frame's **pose** correctly
(`combine_frame_transforms(link_pose_b, offset)`). The OSC term also
computes its **velocity** correctly (`v + w × quat_apply(link_quat_b,
offset_pos)`, with a comment that angular velocity is unaffected). So
the Jacobian was inconsistent with both.

Both action terms apply the correction inline. They compute the body
orientation in the root frame directly from the asset data, so the
result does not depend on whether `_compute_ee_pose` ran earlier in the
step: the OSC `apply_actions` computes the Jacobian before the pose.

## Type of change

- Bug fix (non-breaking change which fixes an issue)

## Tests

- Extended `source/isaaclab/test/envs/test_diffik_jacobian_aliasing.py`
with the body-offset regression (kit-free `unit`, parametrized over
DiffIK and OSC), reusing its existing stub.
- Setup: one revolute joint about z, with the body rotated 90° about z.
- With offset `(1, 0, 0)`, the expected linear column is `z × (0, 1, 0)
= (-1, 0, 0)`. With a 90° offset rotation about x, the angular column
must stay `(0, 0, 1)`.
- **Updated**
`test_diffik_jacobian_aliasing.py::test_compute_frame_jacobian_applies_offset_once`.
- Its out-of-place reference encoded the previous formula. It now uses
non-trivial root and body orientations and the rigid-offset formula.
  - The idempotence and owned-buffer tests are unchanged and still pass.

```
pytest -q source/isaaclab/test/envs/test_diffik_jacobian_aliasing.py
# without the fix: 3 failed, 2 passed
# with the fix:    5 passed
```

**Finite-difference check** (script not included in the PR):
- Setup: random 7-joint serial chain in float64, offset `(0, 0, 0.107)`,
0.7 rad offset rotation.
- Comparison: the Jacobian of the offset frame against finite
differences of its pose.

| | max linear error | max angular error |
|---|---|---|
| previous formula | 7.9e-2 | 6.1e-1 |
| this PR | 4.7e-7 | 2.4e-7 |

## Notes for reviewers

- **Changed behavior for existing tasks.** Tasks that set `body_offset`
(e.g. the Franka lift/stack/cabinet IK tasks) will now use the correct
Jacobian. DiffIK is closed-loop, so the previous error mostly degraded
convergence rather than causing outright failures. For OSC with inertial
decoupling, the mismatch between `J` and the (correct) EE velocity is
removed.
- **Scope.** I kept the change local to the two action terms. It should
not overlap with the controller consolidation in #7854, which does not
touch these methods.

## Checklist

- [x] I have read and understood the contribution guidelines
- [x] I have run the `pre-commit` checks with `./isaaclab.sh --format`
- [x] I have added tests that prove my fix is effective or that my
feature works
- [x] I have added a changelog fragment under
`source/isaaclab/changelog.d/`
- [x] I have added my name to the `CONTRIBUTORS.md` or my name already
exists there

---------

Signed-off-by: Lynn <lynnhe02@gmail.com>
Co-authored-by: Octi Zhang <zhengyuz@nvidia.com>
@pull pull Bot locked and limited conversation to collaborators Sep 24, 2026
@pull pull Bot added the ⤵️ pull label Sep 24, 2026
@pull
pull Bot merged commit 2f1983c into mataylor-nvidia:develop Sep 24, 2026
12 of 13 checks passed
@github-actions github-actions Bot added documentation Improvements or additions to documentation isaac-lab labels Sep 24, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

⤵️ pull documentation Improvements or additions to documentation isaac-lab

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants