From 70709a105af643ee6e37d0f0abeec6dc9bca9409 Mon Sep 17 00:00:00 2001 From: Mustafa Haiderbhai Date: Tue, 22 Sep 2026 21:44:47 -0700 Subject: [PATCH 1/2] Fix Warp reorient reset sampling around the lower joint limit The reset_hand kernel mapped [-1, 1] noise onto a range centred on the lower joint limit, so hand joints always reset below their default and could leave the limits. Map the noise to [0, 1] to match sample_joint_positions_within_limits used by the torch reorientation tasks. Fixes #4404 --- ...iderbhai-warp-reorient-reset-sampling.skip | 1 + .../test/core/test_reorient_utils.py | 38 +++++++++++++++++++ ...aiderbhai-warp-reorient-reset-sampling.rst | 6 +++ .../core/reorient/reorient_warp_env.py | 2 +- 4 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 source/isaaclab_tasks/changelog.d/mhaiderbhai-warp-reorient-reset-sampling.skip create mode 100644 source/isaaclab_tasks_experimental/changelog.d/mhaiderbhai-warp-reorient-reset-sampling.rst diff --git a/source/isaaclab_tasks/changelog.d/mhaiderbhai-warp-reorient-reset-sampling.skip b/source/isaaclab_tasks/changelog.d/mhaiderbhai-warp-reorient-reset-sampling.skip new file mode 100644 index 000000000000..f2165d072d06 --- /dev/null +++ b/source/isaaclab_tasks/changelog.d/mhaiderbhai-warp-reorient-reset-sampling.skip @@ -0,0 +1 @@ +Added a regression test for the Warp reorientation reset sampling; test-only change. diff --git a/source/isaaclab_tasks/test/core/test_reorient_utils.py b/source/isaaclab_tasks/test/core/test_reorient_utils.py index 15e9b1b810d0..db3bbd762fae 100644 --- a/source/isaaclab_tasks/test/core/test_reorient_utils.py +++ b/source/isaaclab_tasks/test/core/test_reorient_utils.py @@ -5,8 +5,11 @@ """Tests for the helpers the reorientation and hand-over tasks share.""" +import isaaclab_tasks_experimental.core.reorient.reorient_warp_env as reorient_warp_env +import numpy as np import pytest import torch +import warp as wp import isaaclab_tasks.core.reorient.utils as reorient_utils @@ -45,6 +48,41 @@ def test_sample_joint_positions_within_limits_rejects_invalid_scale(noise_scale) ) +def test_warp_reset_hand_samples_the_same_range_as_the_torch_helper(): + """Verify the Warp reorientation reset spreads joints on both sides of the default, within the torch range.""" + num_envs, noise_scale = 512, 0.5 + default_position = torch.tensor([[0.1, -0.2]]).repeat(num_envs, 1) + lower = torch.tensor([[-1.0, -2.0]]).repeat(num_envs, 1) + upper = torch.tensor([[1.0, 2.0]]).repeat(num_envs, 1) + outputs = [wp.zeros((num_envs, 2), dtype=wp.float32, device="cpu") for _ in range(5)] + rng_state = wp.array(np.arange(num_envs, dtype=np.uint32), dtype=wp.uint32, device="cpu") + + wp.launch( + reorient_warp_env.reset_hand, + dim=num_envs, + inputs=[ + wp.from_torch(default_position), + wp.from_torch(torch.zeros_like(default_position)), + wp.from_torch(lower), + wp.from_torch(upper), + noise_scale, + 0.0, + wp.array(np.ones(num_envs, dtype=bool), dtype=wp.bool, device="cpu"), + 2, + rng_state, + *outputs, + ], + device="cpu", + ) + joint_position = wp.to_torch(outputs[0]) + + # Same endpoints as sample_joint_positions_within_limits: interpolate from the default toward each limit. + assert torch.all(joint_position >= default_position + noise_scale * (lower - default_position) - 1.0e-6) + assert torch.all(joint_position <= default_position + noise_scale * (upper - default_position) + 1.0e-6) + assert torch.all((joint_position < default_position).any(dim=0)) + assert torch.all((joint_position > default_position).any(dim=0)) + + def test_episode_error_recorder_reports_threshold_independent_statistics(): """Verify episode error summaries contain no success-threshold policy.""" recorder = reorient_utils.EpisodeErrorRecorder(num_envs=3, device="cpu") diff --git a/source/isaaclab_tasks_experimental/changelog.d/mhaiderbhai-warp-reorient-reset-sampling.rst b/source/isaaclab_tasks_experimental/changelog.d/mhaiderbhai-warp-reorient-reset-sampling.rst new file mode 100644 index 000000000000..2bbcf0722187 --- /dev/null +++ b/source/isaaclab_tasks_experimental/changelog.d/mhaiderbhai-warp-reorient-reset-sampling.rst @@ -0,0 +1,6 @@ +Fixed +^^^^^ + +* Fixed the Warp reorientation reset sampling hand joint positions around the + lower joint limit instead of between the lower and upper limits, matching the + torch reorientation tasks. diff --git a/source/isaaclab_tasks_experimental/isaaclab_tasks_experimental/core/reorient/reorient_warp_env.py b/source/isaaclab_tasks_experimental/isaaclab_tasks_experimental/core/reorient/reorient_warp_env.py index 1b68d553b56f..c2a34edc9da6 100644 --- a/source/isaaclab_tasks_experimental/isaaclab_tasks_experimental/core/reorient/reorient_warp_env.py +++ b/source/isaaclab_tasks_experimental/isaaclab_tasks_experimental/core/reorient/reorient_warp_env.py @@ -163,7 +163,7 @@ def reset_hand( delta_max = upper_limits[env_id, dof_id] - default_joint_pos[env_id, dof_id] delta_min = lower_limits[env_id, dof_id] - default_joint_pos[env_id, dof_id] - rand_delta = delta_min + (delta_max - delta_min) * 0.5 * dof_pos_noise + rand_delta = delta_min + (delta_max - delta_min) * 0.5 * (dof_pos_noise + wp.float32(1.0)) pos = default_joint_pos[env_id, dof_id] + reset_dof_pos_noise * rand_delta dof_vel_noise = wp.randf(rng_state[env_id], wp.float32(-1.0), wp.float32(1.0)) From 7a7d2ad904a30b36a99ccea9df52c7a043098055 Mon Sep 17 00:00:00 2001 From: Mustafa Haiderbhai Date: Tue, 22 Sep 2026 22:31:09 -0700 Subject: [PATCH 2/2] Remove Warp reorient reset sampling regression test Co-Authored-By: Claude Opus 5.5 (1M context) --- ...iderbhai-warp-reorient-reset-sampling.skip | 1 - .../test/core/test_reorient_utils.py | 38 ------------------- 2 files changed, 39 deletions(-) delete mode 100644 source/isaaclab_tasks/changelog.d/mhaiderbhai-warp-reorient-reset-sampling.skip diff --git a/source/isaaclab_tasks/changelog.d/mhaiderbhai-warp-reorient-reset-sampling.skip b/source/isaaclab_tasks/changelog.d/mhaiderbhai-warp-reorient-reset-sampling.skip deleted file mode 100644 index f2165d072d06..000000000000 --- a/source/isaaclab_tasks/changelog.d/mhaiderbhai-warp-reorient-reset-sampling.skip +++ /dev/null @@ -1 +0,0 @@ -Added a regression test for the Warp reorientation reset sampling; test-only change. diff --git a/source/isaaclab_tasks/test/core/test_reorient_utils.py b/source/isaaclab_tasks/test/core/test_reorient_utils.py index db3bbd762fae..15e9b1b810d0 100644 --- a/source/isaaclab_tasks/test/core/test_reorient_utils.py +++ b/source/isaaclab_tasks/test/core/test_reorient_utils.py @@ -5,11 +5,8 @@ """Tests for the helpers the reorientation and hand-over tasks share.""" -import isaaclab_tasks_experimental.core.reorient.reorient_warp_env as reorient_warp_env -import numpy as np import pytest import torch -import warp as wp import isaaclab_tasks.core.reorient.utils as reorient_utils @@ -48,41 +45,6 @@ def test_sample_joint_positions_within_limits_rejects_invalid_scale(noise_scale) ) -def test_warp_reset_hand_samples_the_same_range_as_the_torch_helper(): - """Verify the Warp reorientation reset spreads joints on both sides of the default, within the torch range.""" - num_envs, noise_scale = 512, 0.5 - default_position = torch.tensor([[0.1, -0.2]]).repeat(num_envs, 1) - lower = torch.tensor([[-1.0, -2.0]]).repeat(num_envs, 1) - upper = torch.tensor([[1.0, 2.0]]).repeat(num_envs, 1) - outputs = [wp.zeros((num_envs, 2), dtype=wp.float32, device="cpu") for _ in range(5)] - rng_state = wp.array(np.arange(num_envs, dtype=np.uint32), dtype=wp.uint32, device="cpu") - - wp.launch( - reorient_warp_env.reset_hand, - dim=num_envs, - inputs=[ - wp.from_torch(default_position), - wp.from_torch(torch.zeros_like(default_position)), - wp.from_torch(lower), - wp.from_torch(upper), - noise_scale, - 0.0, - wp.array(np.ones(num_envs, dtype=bool), dtype=wp.bool, device="cpu"), - 2, - rng_state, - *outputs, - ], - device="cpu", - ) - joint_position = wp.to_torch(outputs[0]) - - # Same endpoints as sample_joint_positions_within_limits: interpolate from the default toward each limit. - assert torch.all(joint_position >= default_position + noise_scale * (lower - default_position) - 1.0e-6) - assert torch.all(joint_position <= default_position + noise_scale * (upper - default_position) + 1.0e-6) - assert torch.all((joint_position < default_position).any(dim=0)) - assert torch.all((joint_position > default_position).any(dim=0)) - - def test_episode_error_recorder_reports_threshold_independent_statistics(): """Verify episode error summaries contain no success-threshold policy.""" recorder = reorient_utils.EpisodeErrorRecorder(num_envs=3, device="cpu")