Skip to content

[Newton] Refresh FK before ray-cast sensor reads - #7523

Merged
kellyguo11 merged 2 commits into
isaac-sim:developfrom
ooctipus:fix/newton-raycaster-lazy-fk
Sep 4, 2026
Merged

[Newton] Refresh FK before ray-cast sensor reads#7523
kellyguo11 merged 2 commits into
isaac-sim:developfrom
ooctipus:fix/newton-raycaster-lazy-fk

Conversation

@ooctipus

@ooctipus ooctipus commented Sep 3, 2026

Copy link
Copy Markdown
Collaborator

Description

Fixes #7236.

Newton ray-cast tasks read NewtonManager.get_state_0().body_q inside their graph-capturable query pipeline. After an in-step joint or root-state write, the reset masks are current but the derived body_q remains stale until forward kinematics runs. This made the first sensor observation after a reset use the previous pose.

This fixes the stale read at its ownership boundary instead of adding eager simulator synchronization to the RL environment loops:

  • NewtonManager._update_sensor_tasks() obtains state through the guarded get_state() accessor before BVH refit and sensor graph capture/replay.
  • The graph-captured raycast callback keeps using raw get_state_0() state, so forward() is never captured.
  • The ray-caster's direct get_world_poses() accessor applies the same lazy-FK rule.
  • The renderer's now-redundant state refresh is removed because the sensor-task scheduler owns freshness for both renderer and raycast consumers.

This is an alternative to the in-step synchronization part of #7516. Its regex and legacy tracked-target fixes are independent of this PR.

Architecture and performance

The state access boundary owns FK freshness; environment stepping remains unaware of individual sensor requirements. The guard runs only when a sensor or renderer update is requested. Rendering does not gain an additional refresh because its existing call moved into the shared scheduler, and Newton's device-resident reset masks limit the actual FK work to invalidated worlds/articulations.

Type of change

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

Release backport

  • This PR already targets the active release branch; do not backport it again.

Screenshots

Not applicable.

Validation

  • Added observable regressions for sensor-data reads and direct pose-getter reads immediately after a carrier root-pose write, with no intervening simulation step or FK-sensitive asset getter. Both tests failed before the fix by exactly the authored displacement in eager and CUDA-graph modes.
  • uv run --frozen --extra test python -m pytest source/isaaclab_newton/test/sensors/test_newton_raycast_sensor.py -vv (16 passed)
  • uv run --frozen --extra test python -m pytest source/isaaclab_newton/test/physics/test_newton_manager_abstraction.py source/isaaclab/test/sim/test_newton_manager_visualization_state.py -q (194 passed)
  • uv run --frozen isaaclab -f (all checks passed)

Checklist

  • I have read and understood the contribution guidelines
  • I have run the pre-commit checks
  • No standalone documentation change is required; the ownership rule is documented at the access boundaries
  • My changes generate no new warnings
  • I have added tests that prove the fix is effective
  • I have added a changelog fragment for the touched package
  • My name already exists in CONTRIBUTORS.md

@ooctipus
ooctipus requested a review from a team September 3, 2026 07:46
@github-actions github-actions Bot added bug Something isn't working isaac-lab Related to Isaac Lab team labels Sep 3, 2026
@greptile-apps

greptile-apps Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR moves Newton FK freshness into the shared sensor-task scheduler and direct ray-caster pose accessor, preventing the first observation after a state write from using stale body transforms.

  • Refreshes derived Newton state before BVH refits and sensor graph execution.
  • Keeps graph-captured callbacks on the raw state accessor so forward kinematics is not captured.
  • Removes the renderer’s now-redundant explicit refresh.
  • Adds eager and CUDA-graph regression coverage for sensor and direct-pose reads.

Confidence Score: 4/5

The PR appears safe to merge, with a non-blocking performance issue from unnecessarily refitting shared sensor BVHs on repeated unchanged-state reads.

The refreshed state reaches BVH refit and graph execution in the correct order, but the new scheduler call also marks sensor state dirty every time and therefore repeats avoidable refit work.

Files Needing Attention: source/isaaclab_newton/isaaclab_newton/physics/newton_manager.py

Important Files Changed

Filename Overview
source/isaaclab_newton/isaaclab_newton/physics/newton_manager.py Centralizes state refresh before sensor execution, but currently re-dirties and refits sensor BVHs on repeated unchanged-state reads.
source/isaaclab_newton/isaaclab_newton/renderers/newton_warp_renderer.py Removes a redundant renderer refresh because the shared scheduler now refreshes state before refitting and rendering.
source/isaaclab_newton/isaaclab_newton/sensors/ray_caster/newton_raycast_sensor.py Refreshes pending FK for direct pose reads while preserving raw-state access inside graph-captured callbacks.
source/isaaclab_newton/test/sensors/test_newton_raycast_sensor.py Adds regressions covering sensor-data and direct-pose reads immediately after carrier pose writes.
source/isaaclab_newton/test/physics/test_newton_manager_abstraction.py Extends scheduler-ordering coverage to require state refresh before BVH refit and rendering.

Sequence Diagram

sequenceDiagram
    participant C as Sensor or renderer
    participant M as NewtonManager
    participant FK as Forward kinematics
    participant B as Sensor BVH
    participant Q as Captured query callback
    C->>M: _update_sensor_tasks()
    M->>FK: get_state() / forward()
    FK-->>M: Refreshed body_q
    M->>B: Refit using current state
    M->>Q: Capture or replay
    Q->>M: get_state_0()
    Q-->>C: Current sensor output
Loading

Reviews (1): Last reviewed commit: "Refresh Newton sensor state before ray c..." | Re-trigger Greptile


state = cls.get_state_0()
# Resolve pending FK before entering the graph-capturable sensor pipeline.
state = cls.get_state()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Avoid redundant sensor BVH refits

Every sensor-task update now calls get_state(), whose Newton path runs forward() and marks sensor state dirty even when no FK work is pending. Repeated ray-caster or renderer reads without an intervening state change therefore rerun the shared shape and particle BVH refits, adding avoidable work for every consumer read.

Knowledge Base Used: Newton backend

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

@isaaclab-review-bot isaaclab-review-bot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isaac Lab Review Bot

The PR coherently moves FK freshness into the Newton state-access boundary, but FK can mutate body_q without invalidating the sensor BVH cache, leaving immediate scene queries with fresh transforms and stale acceleration bounds.

  • Design and architecture: Having NewtonManager.get_state() refresh derived transforms before the shared sensor-task pipeline is coherent and keeps forward() outside graph-captured callbacks. However, this ownership boundary must also propagate FK-driven transform changes to the BVH dirty state.
  • API: Public signatures and return types remain unchanged, renderer refresh behavior is routed through the shared scheduler, and the Newton package includes the required changelog fragment.
  • Implementation: At _update_sensor_tasks(), get_state() may run FK and mutate _state_0.body_q in place. The following identity comparison therefore does not set _sensor_state_dirty, so an already-cleared dirty flag can prevent BVH refitting even though body transforms changed. The FK refresh path should explicitly invalidate the sensor BVH when it resolves pending transforms.

Minor fixes needed. Posted 1 actionable finding inline.

Automated review; human maintainers own approval decisions.


state = cls.get_state_0()
# Resolve pending FK before entering the graph-capturable sensor pipeline.
state = cls.get_state()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Warning · Implementation — Resolved FK does not invalidate BVH cache

get_state() may run forward(), which rewrites _state_0.body_q in place, so the object identity check on the next line never fires and _sensor_state_dirty stays False after the previous update cleared it. The subsequent query then uses freshly transformed ray origins against BVH bounds refit from pre-FK poses, so targets moved by the same reset can be missed. Set _sensor_state_dirty = True when pending FK was resolved.

@ooctipus ooctipus self-assigned this Sep 3, 2026
@ooctipus ooctipus moved this to In progress in Isaac Lab Sep 3, 2026
@kellyguo11

Copy link
Copy Markdown
Contributor

run-ci

@isaaclab-bot isaaclab-bot Bot added ci:run-docker Trigger the on-demand Docker and GPU CI workflow and removed ci:run-docker Trigger the on-demand Docker and GPU CI workflow labels Sep 3, 2026
@ooctipus

ooctipus commented Sep 4, 2026

Copy link
Copy Markdown
Collaborator Author

run-ci

@isaaclab-bot isaaclab-bot Bot added ci:run-docker Trigger the on-demand Docker and GPU CI workflow and removed ci:run-docker Trigger the on-demand Docker and GPU CI workflow labels Sep 4, 2026
@kellyguo11
kellyguo11 merged commit 8365c57 into isaac-sim:develop Sep 4, 2026
51 of 52 checks passed
@github-project-automation github-project-automation Bot moved this from In progress to Done in Isaac Lab Sep 4, 2026
@isaaclab-bot

isaaclab-bot Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Backported to release/3.0.0 as 2af0251.

isaaclab-bot Bot pushed a commit that referenced this pull request Sep 4, 2026
# Description

Fixes #7236.

Newton ray-cast tasks read `NewtonManager.get_state_0().body_q` inside
their graph-capturable query pipeline. After an in-step joint or
root-state write, the reset masks are current but the derived `body_q`
remains stale until forward kinematics runs. This made the first sensor
observation after a reset use the previous pose.

This fixes the stale read at its ownership boundary instead of adding
eager simulator synchronization to the RL environment loops:

- `NewtonManager._update_sensor_tasks()` obtains state through the
guarded `get_state()` accessor before BVH refit and sensor graph
capture/replay.
- The graph-captured raycast callback keeps using raw `get_state_0()`
state, so `forward()` is never captured.
- The ray-caster's direct `get_world_poses()` accessor applies the same
lazy-FK rule.
- The renderer's now-redundant state refresh is removed because the
sensor-task scheduler owns freshness for both renderer and raycast
consumers.

This is an alternative to the in-step synchronization part of #7516. Its
regex and legacy tracked-target fixes are independent of this PR.

## Architecture and performance

The state access boundary owns FK freshness; environment stepping
remains unaware of individual sensor requirements. The guard runs only
when a sensor or renderer update is requested. Rendering does not gain
an additional refresh because its existing call moved into the shared
scheduler, and Newton's device-resident reset masks limit the actual FK
work to invalidated worlds/articulations.

## Type of change

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

## Release backport

- [x] <!-- backport-active-release --> This PR already targets the
active release branch; do not backport it again.

## Screenshots

Not applicable.

## Validation

- Added observable regressions for sensor-data reads and direct
pose-getter reads immediately after a carrier root-pose write, with no
intervening simulation step or FK-sensitive asset getter. Both tests
failed before the fix by exactly the authored displacement in eager and
CUDA-graph modes.
- `uv run --frozen --extra test python -m pytest
source/isaaclab_newton/test/sensors/test_newton_raycast_sensor.py -vv`
(16 passed)
- `uv run --frozen --extra test python -m pytest
source/isaaclab_newton/test/physics/test_newton_manager_abstraction.py
source/isaaclab/test/sim/test_newton_manager_visualization_state.py -q`
(194 passed)
- `uv run --frozen isaaclab -f` (all checks passed)

## Checklist

- [x] I have read and understood the contribution guidelines
- [x] I have run the pre-commit checks
- [x] No standalone documentation change is required; the ownership rule
is documented at the access boundaries
- [x] My changes generate no new warnings
- [x] I have added tests that prove the fix is effective
- [x] I have added a changelog fragment for the touched package
- [x] My name already exists in `CONTRIBUTORS.md`

(cherry picked from commit 8365c57)
ooctipus pushed a commit that referenced this pull request Sep 4, 2026
# Description

This PR fixes the two remaining ray-caster path-matching failures:

1. The ray-caster camera tutorial counted four sensors for two camera
prims because `/World/Origin_.*/CameraSensor` allowed `.*` to cross path
separators under whole-path regular-expression matching.
2. Legacy Newton multi-mesh ray casters could fail to associate
tracked-target sites when registration used a regular-expression path
but initialization resolved it to concrete prim paths.

The stale post-reset Newton sensor result reported in #7236 is now fixed
at the sensor state boundary by #7523, which this branch inherits from
`develop`. The earlier environment-loop synchronization workaround and
its tests/changelog fragments have therefore been removed from this PR.

## Changes

- Restrict the tutorial environment wildcard to one path segment with
`/World/Origin_[^/]+/CameraSensor`.
- Associate legacy Newton tracked-target site labels by configuration
order instead of path-expression strings.
- Remove the obsolete `--enable_cameras` argument from the tutorial
smoke case.
- Add regression coverage for regular-expression target resolution.

Fixes #6572

Related: #7236, resolved by #7523.

## Type of change

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

## Release backport

- [x] Backport this pull request to the active release branch after it
merges into `develop`

## Screenshots

Not applicable.

## Validation

- `uv run python -m pytest
source/isaaclab_newton/test/sensors/test_newton_raycast_sensor.py -vv`
(18 passed, including the #7523 FK regressions in eager and CUDA-graph
modes)
- `uv run python -m pytest
source/isaaclab/test/app/test_standalone_scripts.py::test_commands_respect_script_launcher_capabilities
-vv` (1 passed)
- Verified the ad-hoc tracked-target regression fails with the old
string-key association and passes with this fix in eager and CUDA-graph
modes.
- Verified the exact clone-plan topology from #6572 passes on the merged
branch in eager and CUDA-graph modes.
- `ISAACLAB_CHANGELOG_BASE_REF=upstream-develop uv run isaaclab -f`

The full standalone Newton multi-object demo smoke could not launch in
this checkout because the newly merged installer guard rejects combining
the downloaded Isaac Sim package with the active Python virtual
environment; it exited before application startup.

## Checklist

- [x] I have read and understood the contribution guidelines
- [x] I have run the pre-commit checks
- [x] I have made corresponding changes to the tutorial and smoke
configuration
- [x] My changes generate no new warnings
- [x] I have added tests that prove the remaining fix is effective
- [x] I have added changelog fragments for the touched source packages
- [x] My name already exists in `CONTRIBUTORS.md`
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working isaac-lab Related to Isaac Lab team

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

3 participants