Skip to content

Use solver-reported accelerations and track runtime gravity in PhysX and OvPhysX IMU/PVA sensors - #7416

Merged
kellyguo11 merged 13 commits into
developfrom
antoiner/fix-imu-solver-accelerations
Sep 5, 2026
Merged

Use solver-reported accelerations and track runtime gravity in PhysX and OvPhysX IMU/PVA sensors#7416
kellyguo11 merged 13 commits into
developfrom
antoiner/fix-imu-solver-accelerations

Conversation

@AntoineRichard

@AntoineRichard AntoineRichard commented Aug 28, 2026

Copy link
Copy Markdown
Collaborator

Description

Three related defects in the IMU and PVA sensors, across PhysX and OvPhysX. They were originally split across this PR and #7538 (now closed as superseded); they are combined here because they touch the same kernels and could not be reviewed or merged independently.

1. Accelerations were finite-differenced (#1294)

Both sensors computed linear acceleration by finite-differencing body velocity between updates. This made the reading depend on the sensor update period, produced a zero or stale reading on the first update, and generated large spurious spikes whenever velocities were written directly (resets, teleports).

PhysX: the sensors now read RigidBodyView.get_accelerations() and transport it to the sensor frame as a_sensor = a_com + α×r + ω×(ω×r). The IMU adds the gravity bias; PVA does not, since PVA reports kinematic acceleration.

OvPhysX: the sensors read accelerations through the RIGID_BODY_ACCELERATION tensor binding with the same transport math. An earlier revision of this PR guarded that behind hasattr(TT, "RIGID_BODY_ACCELERATION") and fell back to finite differencing, because the binding had not shipped yet. It has since shipped and the project pins ovphysx==0.5.11 exactly, so the fallback was unreachable on every supported install and has been removed along with its duplicate kernels and previous-velocity state.

This also aligns PhysX and OvPhysX with Newton, whose PVA already computed acceleration from body_qdd with exactly this transport.

get_accelerations() accuracy

Verified on Isaac Sim 6.0.1 with a constant applied force (expected a = F/m):

mass solver acc expected rel. err
1 kg 0.00199992 0.002 0.004%
1 g 2.00004 2.0 0.002%
1 µg 2000.0 2000.0 <0.001%

The historical small-mass inaccuracy that motivated the finite-difference implementation is no longer present.

2. OvPhysxManager.get_gravity() returned a stale value

set_gravity() authored the new gravity into OvStage and applied it to the running simulation, but get_gravity() read SimulationCfg.gravity, which the setter never touched. Every call after a live update returned the construction-time value, silently and permanently:

get_gravity before set : (0.0, 0.0, -9.81)
set_gravity called with: (0.0, 0.0, -3.72)
get_gravity after  set : (0.0, 0.0, -9.81)

The manager now tracks the applied gravity rather than writing back to cfg.gravity. Writing back would have been wrong: both _call_physx and _call_ovphysx in randomize_physics_scene_gravity resample from env.sim.cfg.gravity as the pristine base, so mutating it would make successive "add"/"scale" randomizations compound.

3. Sensors snapshotted gravity at initialization

IMU and PVA read gravity once in _initialize_impl and baked it into a static buffer, so gravity randomized at runtime never reached the accelerometer bias or the projected gravity direction. This affected both backends and was independent of defect 2 — fixing get_gravity() alone would not have changed sensor behavior.

Both sensors now re-read scene gravity on every update, skipping the work when it is unchanged. This matters more after change 1, not less: the IMU reading is now literally solver_a + (−g), so a stale g enters as a clean additive bias. Randomizing to Mars gravity made a resting body read +9.81 instead of +3.72 — a systematic 6.09 m/s² offset on every sample.

Gravity is scene-wide on both backends, so the bias and the gravity direction are passed to the kernels by value rather than as per-body buffers. The PhysX recorded launches re-bind the parameter with set_param_by_name when it changes, mirroring how env_mask is already handled. If per-env gravity ever reaches these backends these go back to arrays, as Newton already does.

Breaking change

The PhysX PVA sensor's GRAVITY_VEC_W proxy array becomes the internal _gravity_vec_w scene-wide vector, matching what the OvPhysX PVA sensor already called it. It was absent from the data class, the type stubs and the docs. Consumers wanting this quantity should read pva_sensor.data.projected_gravity_b. The identically-named attribute on the asset data classes is untouched.

Known remaining gaps (not addressed here)

  • GRAVITY_VEC_W is still snapshotted at construction in the asset data classes (rigid_object_data.py, rigid_object_collection_data.py, articulation_data.py) for both backends. Newton fixed its three by binding ProxyArray(model.gravity[:world_count]) zero-copy, which is not possible here: OvPhysX exposes no scene-gravity tensor binding, and PhysX exposes gravity only as a host-side Float3. A fix must use the same pull-based refresh used here, and first needs a decision on whether to converge PhysX/OV onto Newton's m/s² convention (they currently store a normalized direction), which would touch consumers in isaaclab_experimental and isaaclab_tasks_experimental.
  • Newton's Imu applies no gravity bias at all, unlike the PhysX and OvPhysX IMUs. This looks like a cross-backend behavioral gap rather than a staleness bug.

Fixes #1294

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • Breaking change (existing functionality will not work without user modification)
  • Documentation update

Release backport

  • Backport this pull request to the active release branch after it merges into develop

Screenshots

Not applicable — no visual change.

Checklist

Docker and GPU tests run on demand. Push the commits you want tested, then
comment run-ci on the pull request.

  • I have read and understood the contribution guidelines
  • I have run the pre-commit checks with ./isaaclab.sh --format
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • I have added a changelog fragment under source/<pkg>/changelog.d/ for every touched package (do not edit CHANGELOG.rst or bump extension.toml — CI handles that)
  • I have added my name to the CONTRIBUTORS.md or my name already exists there

Testing

All suites run locally, one file at a time:

Suite Result
isaaclab_physx IMU 11 passed
isaaclab_physx PVA 11 passed
isaaclab_physx recorded-launch 13 passed
isaaclab_ov IMU (default / -k cuda) 12 passed, 14 skipped / 12 passed
isaaclab_ov PVA (default / -k cuda) 13 passed, 15 skipped / 13 passed
isaaclab_ov physics 53 passed

Regression coverage, each verified to fail without its fix:

  • test_set_gravity_writes_and_releases_ovstage_control_resources — asserts get_gravity() round-trips after a successful set_gravity(), still reports the previously applied value when the OvStage write fails, and leaves cfg.gravity untouched as the randomization base. Without the fix: assert (0.0, 0.0, -9.81) == approx((0.0, 0.0, -3.72)), reproducing the reported symptom.
  • test_sensor_tracks_runtime_gravity_changes and test_imu_replayed_launch_applies_new_gravity_bias — a mid-run gravity change must reach the replayed recorded launch, with an explicit guard that the test is on the recorded path rather than the eager fallback.
  • test_velocity_writes_do_not_produce_spurious_acceleration (IMU and PVA) — velocity teleports must not produce an acceleration spike. Without the fix the PVA case fails with a 99.9998 difference, exactly the 0.1 / dt = 100 m/s² finite-difference artifact.

The OvPhysX IMU tests were updated where they encoded finite-difference artifacts as expected values — most notably test_gravity_at_rest, whose old scenario never had the ball at rest (it fell continuously while zero velocity was rewritten each step). It now applies a real support force so the body is genuinely at rest and the accelerometer reads the bias alone, as a real IMU on a table does.

@AntoineRichard
AntoineRichard requested a review from a team August 28, 2026 15:16
@github-actions github-actions Bot added bug Something isn't working isaac-lab Related to Isaac Lab team labels Aug 28, 2026
@greptile-apps

greptile-apps Bot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

The PR replaces finite-difference IMU acceleration with solver-reported COM acceleration and transports it to the configured sensor frame, while retaining an OVPhysX fallback for older wheels.

  • Adds solver-acceleration kernels for PhysX and OVPhysX IMUs.
  • Adds acceleration-buffer handling to the PhysX recorded-launch path.
  • Reworks PhysX tests around applied forces and direct velocity writes.
  • The OVPhysX optional-binding selection does not currently handle a present-but-unavailable binding.

Confidence Score: 4/5

The OVPhysX fallback should be corrected before merging because a wheel with an exposed but unusable acceleration binding can prevent the IMU from initializing.

The solver-acceleration transport is consistent with repository spatial-vector and COM conventions, but optional OVPhysX capability handling calls a raising binding API before the documented finite-difference fallback can be selected.

Files Needing Attention: source/isaaclab_ov/isaaclab_ov/sensors/imu/imu.py

Important Files Changed

Filename Overview
source/isaaclab_ov/isaaclab_ov/sensors/imu/imu.py Adds runtime solver-acceleration selection, but checks enum presence rather than whether the optional tensor binding can actually be acquired.
source/isaaclab_ov/isaaclab_ov/sensors/imu/kernels.py Adds COM-to-sensor acceleration transport using the OV tensor’s documented linear-first spatial layout.
source/isaaclab_physx/isaaclab_physx/sensors/imu/imu.py Integrates the acceleration view into eager and recorded updates, including pointer-stability validation and invalidation.
source/isaaclab_physx/isaaclab_physx/sensors/imu/kernels.py Replaces elapsed-time velocity differencing with solver acceleration and rigid-body transport terms.
source/isaaclab_physx/test/sensors/test_imu.py Updates behavior tests to validate force-derived acceleration and suppression of velocity-write spikes.
source/isaaclab_physx/test/sensors/test_imu_pva_recorded_launch.py Extends recorded-launch fixtures and assertions to cover stable acceleration buffers and refreshed acceleration values.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    Step[Physics step] --> Solver[Solver-reported COM acceleration]
    Solver --> PhysX[PhysX IMU kernel]
    Solver --> Capability{OV acceleration binding usable?}
    Capability -->|Yes| OVKernel[OV solver-acceleration kernel]
    Capability -->|No| Fallback[OV finite-difference kernel]
    PhysX --> Transport[Transport COM acceleration to sensor offset]
    OVKernel --> Transport
    Transport --> Output[Body-frame IMU acceleration plus gravity bias]
Loading

Reviews (1): Last reviewed commit: "Use solver-reported accelerations in Phy..." | Re-trigger Greptile

Comment thread source/isaaclab_ov/isaaclab_ov/sensors/imu/imu.py Outdated

@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

Reviewed the PhysX and OvPhysX IMU transition from velocity finite-differencing to solver-reported accelerations, including center-of-mass transport, gravity bias, buffer caching, recorded launches, fallback behavior, tests, and release notes. No candidate findings were supplied or supported for posting.

  • Design and architecture: The backend split is coherent: PhysX uses RigidBodyView.get_accelerations(), while OvPhysX selects the solver path only when RIGID_BODY_ACCELERATION is exposed and otherwise retains the finite-difference path. PVA behavior remains separate and unchanged.
  • API: The existing ImuData.lin_acc_b shape and body-frame convention are preserved, while its PhysX semantics intentionally become independent of sensor update period and direct velocity writes. Both affected packages include changelog fragments, and the OvPhysX documentation describes its version-dependent behavior.
  • Implementation: The new acceleration buffers are connected consistently through acquisition, typed views, pointer-stability checks, kernel launches, and invalidation. Both kernels apply the documented COM-to-sensor transport terms and gravity bias. PhysX regression and recorded-launch coverage were updated; the residual validation gap is that the new OvPhysX solver-binding path was wheel-skipped locally.

No blocking issues. No inline issue met the actionable-evidence threshold; the assessment above records the review feedback.

Automated review; human maintainers own approval decisions.

set_gravity() authored the new gravity into OvStage and applied it to the running
scene, but get_gravity() read SimulationCfg.gravity, which the setter never touched.
Every call after a live update returned the construction-time value.

Track the applied gravity on the manager instead of writing back to cfg.gravity:
randomization terms resample from cfg.gravity as the nominal base, so mutating it
would compound successive 'add'/'scale' randomizations.
Imu and Pva read scene gravity once in _initialize_impl and baked it into a
static buffer, so gravity randomized at runtime never reached the accelerometer
bias or the projected gravity direction. Both backends were affected.

Refresh the buffer on every update, skipping the refill when the scene gravity is
unchanged. The buffer is filled in place rather than reallocated: the PhysX
sensors consume it through a recorded wp.Launch that holds the array pointer, and
PVA additionally exposes it as the public GRAVITY_VEC_W proxy.

Newton's Pva already passes model.gravity into its kernel each update, so it
needed no change.
…y buffer

PhysX and OvPhysX gravity is scene-wide, so broadcasting one vector into a
num_bodies buffer and re-filling it on change was the wrong shape for the data.
The kernels now take gravity_bias_w by value.

This removes the allocation and the device fill entirely; a gravity change is
just a Python-level reassignment. The PhysX recorded launch bakes the value in
at record time, so _update_buffers_impl re-binds the parameter with
set_param_by_name when it changes, mirroring how env_mask is already handled.

If per-env gravity ever reaches these backends the parameter goes back to an
array, as Newton already does.
Combines the OvPhysxManager.get_gravity fix and the IMU/PVA runtime gravity
tracking with the solver-reported acceleration work so both ship as one PR.

Conflict resolutions:
- imu kernels: keep the solver-acceleration transport math, with the gravity
  bias taken by value rather than indexed from a per-body array.
- recorded-launch test harness: keep the accelerations-based fixture and thread
  the gravity sink through it.
- the replayed-bias test no longer zeroes finite-difference state, which the
  solver-acceleration path removed.
The scalar conversion predated the solver-acceleration kernel, so the merge left
imu_update_solver_acc_kernel still declaring gravity_bias_w as an array while the
sensor passed a vec3f. Every OV IMU test that exercised the solver path failed to
launch.
Mirrors the IMU change: PhysX and OvPhysX gravity is scene-wide, so the unit
gravity direction no longer needs a per-instance buffer. The kernels take
gravity_vec_w by value and the PhysX recorded launch re-binds it on change.

The PhysX sensor's public-looking GRAVITY_VEC_W proxy array becomes the internal
_gravity_vec_w, matching what the OvPhysX sensor already called it. It was absent
from the data class, the type stubs and the docs; consumers wanting this quantity
should read data.projected_gravity_b. The identically-named attribute on the asset
data classes is untouched.
@AntoineRichard AntoineRichard changed the title Use solver-reported accelerations in PhysX and OvPhysX IMU sensors Use solver-reported accelerations and track runtime gravity in PhysX and OvPhysX IMU/PVA sensors Sep 4, 2026
The IMU and PVA sensors selected between a solver-acceleration kernel and a
finite-difference kernel on hasattr(TT, "RIGID_BODY_ACCELERATION"). That guard
was written before the binding shipped; the project now pins ovphysx==0.5.11
exactly, and 0.5.11 exposes the binding, so the fallback branch is unreachable on
every supported install.

Removes both duplicate kernels, the prev_lin_vel_w / prev_ang_vel_w state, the
dt-caching update() overrides (PhysX has no equivalent), and the reset-kernel
arguments that cleared the dropped buffers. The surviving kernels take the plain
imu_update_kernel / pva_update_kernel names.
Dropping the finite-difference fallback left _acc_binding assigned in three places
and read in none; read_into creates the binding lazily, so the binding_for call was
redundant too.

Also corrects documentation that still described the removed behavior: the OvPhysX
Imu and Pva class notes advertised a numerical-differentiation fallback and a
200 Hz timestep recommendation, and BasePva told users accelerations may be
differentiated from velocities depending on the backend. No backend does that any
more -- Newton already read body_qdd, and PhysX and OvPhysX now read the solver.
@kellyguo11

Copy link
Copy Markdown
Contributor

run-ci

@isaaclab-bot isaaclab-bot Bot added the ci:run-docker Trigger the on-demand Docker and GPU CI workflow label Sep 4, 2026
@isaaclab-bot isaaclab-bot Bot removed the ci:run-docker Trigger the on-demand Docker and GPU CI workflow label Sep 4, 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 4, 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 4, 2026
@kellyguo11
kellyguo11 merged commit 2d88d27 into develop Sep 5, 2026
52 of 53 checks passed
@kellyguo11
kellyguo11 deleted the antoiner/fix-imu-solver-accelerations branch September 5, 2026 01:41
@isaaclab-bot

isaaclab-bot Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Backported to release/3.0.0 as fa92933.

isaaclab-bot Bot pushed a commit that referenced this pull request Sep 5, 2026
…and OvPhysX IMU/PVA sensors (#7416)

# Description

Three related defects in the IMU and PVA sensors, across PhysX and
OvPhysX. They were originally split across this PR and #7538 (now closed
as superseded); they are combined here because they touch the same
kernels and could not be reviewed or merged independently.

## 1. Accelerations were finite-differenced (#1294)

Both sensors computed linear acceleration by finite-differencing body
velocity between updates. This made the reading depend on the sensor
update period, produced a zero or stale reading on the first update, and
generated large spurious spikes whenever velocities were written
directly (resets, teleports).

**PhysX**: the sensors now read `RigidBodyView.get_accelerations()` and
transport it to the sensor frame as `a_sensor = a_com + α×r + ω×(ω×r)`.
The IMU adds the gravity bias; PVA does not, since PVA reports kinematic
acceleration.

**OvPhysX**: the sensors read accelerations through the
`RIGID_BODY_ACCELERATION` tensor binding with the same transport math.
An earlier revision of this PR guarded that behind `hasattr(TT,
"RIGID_BODY_ACCELERATION")` and fell back to finite differencing,
because the binding had not shipped yet. It has since shipped and the
project pins `ovphysx==0.5.11` exactly, so the fallback was unreachable
on every supported install and has been removed along with its duplicate
kernels and previous-velocity state.

This also aligns PhysX and OvPhysX with Newton, whose PVA already
computed acceleration from `body_qdd` with exactly this transport.

### `get_accelerations()` accuracy

Verified on Isaac Sim 6.0.1 with a constant applied force (expected `a =
F/m`):

| mass | solver acc | expected | rel. err |
|---|---|---|---|
| 1 kg | 0.00199992 | 0.002 | 0.004% |
| 1 g | 2.00004 | 2.0 | 0.002% |
| 1 µg | 2000.0 | 2000.0 | <0.001% |

The historical small-mass inaccuracy that motivated the
finite-difference implementation is no longer present.

## 2. `OvPhysxManager.get_gravity()` returned a stale value

`set_gravity()` authored the new gravity into OvStage and applied it to
the running simulation, but `get_gravity()` read
`SimulationCfg.gravity`, which the setter never touched. Every call
after a live update returned the construction-time value, silently and
permanently:

```
get_gravity before set : (0.0, 0.0, -9.81)
set_gravity called with: (0.0, 0.0, -3.72)
get_gravity after  set : (0.0, 0.0, -9.81)
```

The manager now tracks the applied gravity rather than writing back to
`cfg.gravity`. Writing back would have been wrong: both `_call_physx`
and `_call_ovphysx` in `randomize_physics_scene_gravity` resample from
`env.sim.cfg.gravity` as the pristine base, so mutating it would make
successive `"add"`/`"scale"` randomizations compound.

## 3. Sensors snapshotted gravity at initialization

IMU and PVA read gravity once in `_initialize_impl` and baked it into a
static buffer, so gravity randomized at runtime never reached the
accelerometer bias or the projected gravity direction. This affected
**both** backends and was independent of defect 2 — fixing
`get_gravity()` alone would not have changed sensor behavior.

Both sensors now re-read scene gravity on every update, skipping the
work when it is unchanged. This matters more after change 1, not less:
the IMU reading is now literally `solver_a + (−g)`, so a stale `g`
enters as a clean additive bias. Randomizing to Mars gravity made a
resting body read +9.81 instead of +3.72 — a systematic 6.09 m/s² offset
on every sample.

Gravity is scene-wide on both backends, so the bias and the gravity
direction are passed to the kernels **by value** rather than as per-body
buffers. The PhysX recorded launches re-bind the parameter with
`set_param_by_name` when it changes, mirroring how `env_mask` is already
handled. If per-env gravity ever reaches these backends these go back to
arrays, as Newton already does.

## Breaking change

The PhysX PVA sensor's `GRAVITY_VEC_W` proxy array becomes the internal
`_gravity_vec_w` scene-wide vector, matching what the OvPhysX PVA sensor
already called it. It was absent from the data class, the type stubs and
the docs. Consumers wanting this quantity should read
`pva_sensor.data.projected_gravity_b`. **The identically-named attribute
on the asset data classes is untouched.**

## Known remaining gaps (not addressed here)

- `GRAVITY_VEC_W` is still snapshotted at construction in the **asset**
data classes (`rigid_object_data.py`, `rigid_object_collection_data.py`,
`articulation_data.py`) for both backends. Newton fixed its three by
binding `ProxyArray(model.gravity[:world_count])` zero-copy, which is
not possible here: OvPhysX exposes no scene-gravity tensor binding, and
PhysX exposes gravity only as a host-side `Float3`. A fix must use the
same pull-based refresh used here, and first needs a decision on whether
to converge PhysX/OV onto Newton's m/s² convention (they currently store
a normalized direction), which would touch consumers in
`isaaclab_experimental` and `isaaclab_tasks_experimental`.
- Newton's `Imu` applies no gravity bias at all, unlike the PhysX and
OvPhysX IMUs. This looks like a cross-backend behavioral gap rather than
a staleness bug.

Fixes #1294

## Type of change

- Bug fix (non-breaking change which fixes an issue)
- Breaking change (existing functionality will not work without user
modification)
- Documentation update

## Release backport

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

## Screenshots

Not applicable — no visual change.

## Checklist

Docker and GPU tests run on demand. Push the commits you want tested,
then
comment `run-ci` on the pull request.

- [x] I have read and understood the [contribution
guidelines](https://isaac-sim.github.io/IsaacLab/main/source/refs/contributing.html)
- [x] I have run the [`pre-commit` checks](https://pre-commit.com/) with
`./isaaclab.sh --format`
- [x] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [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/<pkg>/changelog.d/` for every touched package (do **not** edit
`CHANGELOG.rst` or bump `extension.toml` — CI handles that)
- [x] I have added my name to the `CONTRIBUTORS.md` or my name already
exists there

## Testing

All suites run locally, one file at a time:

| Suite | Result |
|---|---|
| `isaaclab_physx` IMU | 11 passed |
| `isaaclab_physx` PVA | 11 passed |
| `isaaclab_physx` recorded-launch | 13 passed |
| `isaaclab_ov` IMU (default / `-k cuda`) | 12 passed, 14 skipped / 12
passed |
| `isaaclab_ov` PVA (default / `-k cuda`) | 13 passed, 15 skipped / 13
passed |
| `isaaclab_ov` physics | 53 passed |

Regression coverage, each verified to fail without its fix:

- `test_set_gravity_writes_and_releases_ovstage_control_resources` —
asserts `get_gravity()` round-trips after a successful `set_gravity()`,
still reports the previously applied value when the OvStage write fails,
and leaves `cfg.gravity` untouched as the randomization base. Without
the fix: `assert (0.0, 0.0, -9.81) == approx((0.0, 0.0, -3.72))`,
reproducing the reported symptom.
- `test_sensor_tracks_runtime_gravity_changes` and
`test_imu_replayed_launch_applies_new_gravity_bias` — a mid-run gravity
change must reach the *replayed* recorded launch, with an explicit guard
that the test is on the recorded path rather than the eager fallback.
- `test_velocity_writes_do_not_produce_spurious_acceleration` (IMU and
PVA) — velocity teleports must not produce an acceleration spike.
Without the fix the PVA case fails with a `99.9998` difference, exactly
the `0.1 / dt = 100 m/s²` finite-difference artifact.

The OvPhysX IMU tests were updated where they encoded finite-difference
artifacts as expected values — most notably `test_gravity_at_rest`,
whose old scenario never had the ball at rest (it fell continuously
while zero velocity was rewritten each step). It now applies a real
support force so the body is genuinely at rest and the accelerometer
reads the bias alone, as a real IMU on a table does.

---------

Co-authored-by: Kelly Guo <kellyg@nvidia.com>

(cherry picked from commit 2d88d27)
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

None yet

Development

Successfully merging this pull request may close these issues.

2 participants