Skip to content

[Task Clean-up] Dexterous Part 3/9: Add success-rate metrics to the reorientation Direct tasks - #6413

Merged
hujc7 merged 9 commits into
isaac-sim:developfrom
hujc7:jichuanh/task-cleanup-dex-part03
Jul 24, 2026
Merged

[Task Clean-up] Dexterous Part 3/9: Add success-rate metrics to the reorientation Direct tasks#6413
hujc7 merged 9 commits into
isaac-sim:developfrom
hujc7:jichuanh/task-cleanup-dex-part03

Conversation

@hujc7

@hujc7 hujc7 commented Jul 8, 2026

Copy link
Copy Markdown
Collaborator

Review Map

PR Status Depends on Exact changes
#6411 Part 1/9: Newton cloner/cubric/visualizer fixes merged
#6412 Part 2/9: OVPhysX articulation + manager runtime merged
📌 #6413 Part 3/9: Reorient Direct, torch (this PR) changes
#6414 Part 4/9: MARL-to-single-agent fix + handover/camera Direct #6413 changes
#6418 Part 5/9: Reorient manager counterparts #6413 changes
#6421 Part 6/9: Handover + camera manager counterparts #6413, #6414, #6418 changes
#6410 Part 7/9: Environment overview docs #6421 changes
#6415 Part 8/9: Benchmark success-rate utilities #6421 changes
#6582 Part 9/9: Warp variants → experimental (draft; merges last) #6413 changes
#6324 [DO-NOT-MERGE] Lumped validation reference ALL

Summary

  • Adds a behavioral Metrics/success_rate signal (goal-reach streaks per episode) and threshold-independent episode orientation-error diagnostics to the Direct reorientation environments; success gates task health, reward stays diagnostic.
  • Task logic is torch-first per the mainline convention (plain torch buffers; .torch accessors only at the core-lib boundary). This supersedes the earlier warp-first revision of this PR; the warp implementation moved to isaaclab_tasks_experimental ([Task Clean-up] Dexterous Part 9/9: Move the warp Direct task variants to isaaclab_tasks_experimental #6582).
  • Lands shared helpers in isaaclab_tasks.core.utils (EpisodeErrorRecorder, sample_joint_positions_within_limits) with torch math tests; fixes hand resets below lower joint limits.

Stacking

  • First stack PR; based on develop.

Validation

Review history

@greptile-apps

greptile-apps Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds behavioral Metrics/success_rate and threshold-independent Diagnostics/episode_min_orientation_error_* metrics to the Direct reorientation environments, and fixes hand resets that could place joints below their lower position limits. New shared helpers (EpisodeErrorRecorder, sample_joint_positions_within_limits) land in isaaclab_tasks.core.utils with unit tests.

  • EpisodeErrorRecorder tracks per-episode minimum orientation error across all active environments, emitting mean/median/p90 aggregates on reset; the recorder carries no success threshold, keeping measurement decoupled from policy.
  • sample_joint_positions_within_limits replaces the old reset logic that could violate lower joint limits by interpolating uniformly between the default position and each joint's sampled limit endpoint, with a final clamp as a safety net.
  • The core reward computation is refactored into direct_reorient_reward in the MDP rewards module, and the compute_rewards wrapper now delegates to it; find_joints/find_bodies replace the manual index-and-sort loops for joint and body discovery.

Confidence Score: 4/5

Safe to merge; all new behaviour is additive (metrics, diagnostics, safer resets) and the reward math is functionally unchanged from the original.

The core reward logic, success tracking, and joint-reset fix are all correct. The two findings are cosmetic: an unreachable torch.abs() on a provably non-negative value, and a repeated quaternion-distance computation (up to three times per step) that produces identical results each time. Neither affects training correctness or runtime safety.

reorient_direct_env.py and mdp/rewards.py are worth a second look for the redundant evaluate_reorient_success calls; no other files require special attention.

Important Files Changed

Filename Overview
source/isaaclab_tasks/isaaclab_tasks/core/utils.py New shared utilities: EpisodeErrorRecorder for threshold-independent episode-minimum tracking, and sample_joint_positions_within_limits for safe joint resets; logic is correct and well-guarded.
source/isaaclab_tasks/isaaclab_tasks/core/reorient/mdp/rewards.py Adds direct_reorient_rotation_distance, evaluate_reorient_success, and direct_reorient_reward helpers; a redundant torch.abs() on an already-non-negative value is present in evaluate_reorient_success, and the orientation error is computed multiple times per step.
source/isaaclab_tasks/isaaclab_tasks/core/reorient/reorient_direct_env.py Wires success-rate metrics, orientation-error diagnostics, and fixed joint-reset sampling into the env; evaluate_reorient_success is called up to three times per step (once in _get_dones, once in _get_rewards for the recorder, once inside direct_reorient_reward).
source/isaaclab_tasks/test/core/test_core_utils.py Unit tests for EpisodeErrorRecorder and sample_joint_positions_within_limits; covers boundary cases, invalid inputs, and empty-episode edge cases.
source/isaaclab_tasks/changelog.d/task-cleanup-dex-part03.minor.rst Changelog fragment documenting the added success metrics and joint-limit reset fix.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant Env as ReorientDirectEnv
    participant Dones as _get_dones()
    participant Rewards as _get_rewards()
    participant DRR as direct_reorient_reward()
    participant Rec as EpisodeErrorRecorder
    participant Reset as _reset_idx()

    Env->>Dones: step()
    Dones->>Dones: "evaluate_reorient_success() [if max_consecutive_success > 0]"
    Dones-->>Env: (terminated, truncated)

    Env->>Rewards: _get_rewards()
    Rewards->>Rewards: evaluate_reorient_success() → orientation_error
    Rewards->>Rec: update(orientation_error)
    Rewards->>DRR: compute_rewards() → direct_reorient_reward()
    DRR->>DRR: evaluate_reorient_success() [3rd call, same data]
    DRR-->>Rewards: reward, goal_resets, successes, consecutive_successes
    Rewards-->>Env: total_reward

    Env->>Reset: _reset_idx(env_ids)
    Reset->>Reset: "_last_episode_success = successes >= threshold"
    Reset->>Rec: "reset(env_ids) → {mean, median, p90}"
    Reset->>Reset: log Metrics/success_rate
    Reset->>Reset: "log Diagnostics/episode_min_orientation_error_*"
    Reset->>Reset: sample_joint_positions_within_limits()
    Reset-->>Env: done
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant Env as ReorientDirectEnv
    participant Dones as _get_dones()
    participant Rewards as _get_rewards()
    participant DRR as direct_reorient_reward()
    participant Rec as EpisodeErrorRecorder
    participant Reset as _reset_idx()

    Env->>Dones: step()
    Dones->>Dones: "evaluate_reorient_success() [if max_consecutive_success > 0]"
    Dones-->>Env: (terminated, truncated)

    Env->>Rewards: _get_rewards()
    Rewards->>Rewards: evaluate_reorient_success() → orientation_error
    Rewards->>Rec: update(orientation_error)
    Rewards->>DRR: compute_rewards() → direct_reorient_reward()
    DRR->>DRR: evaluate_reorient_success() [3rd call, same data]
    DRR-->>Rewards: reward, goal_resets, successes, consecutive_successes
    Rewards-->>Env: total_reward

    Env->>Reset: _reset_idx(env_ids)
    Reset->>Reset: "_last_episode_success = successes >= threshold"
    Reset->>Rec: "reset(env_ids) → {mean, median, p90}"
    Reset->>Reset: log Metrics/success_rate
    Reset->>Reset: "log Diagnostics/episode_min_orientation_error_*"
    Reset->>Reset: sample_joint_positions_within_limits()
    Reset-->>Env: done
Loading

Comments Outside Diff (1)

  1. source/isaaclab_tasks/isaaclab_tasks/core/reorient/reorient_direct_env.py, line 202-230 (link)

    P2 Triple orientation-error computation per step

    evaluate_reorient_success is called up to three times each step with the same inputs: once here for the recorder (line 203), once inside direct_reorient_reward via compute_rewards (line 210), and once in _get_dones when max_consecutive_success > 0 (line 252). All three calls produce identical results because no simulation step occurs between them. The raw orientation_error returned by the direct_reorient_reward path is already available inside that function; surfacing it as a fifth return value (or computing it once before the call and passing it in) would eliminate the duplicate work.

Reviews (1): Last reviewed commit: "Add success-rate metrics to the reorient..." | Re-trigger Greptile

Comment on lines +132 to +133
orientation_error = direct_reorient_rotation_distance(object_quat, target_quat)
return torch.abs(orientation_error) <= success_tolerance, orientation_error

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 torch.abs() is redundant here. direct_reorient_rotation_distance computes 2 * arcsin(clamp(norm, max=1)), where the clamped norm is in [0, 1] and arcsin returns [0, π/2], so the product is always in [0, π]. Applying torch.abs on a provably non-negative tensor is a no-op and slightly misleads readers into thinking the value could be negative.

Suggested change
orientation_error = direct_reorient_rotation_distance(object_quat, target_quat)
return torch.abs(orientation_error) <= success_tolerance, orientation_error
orientation_error = direct_reorient_rotation_distance(object_quat, target_quat)
return orientation_error <= success_tolerance, orientation_error

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!

@AntoineRichard AntoineRichard left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Do we want to move more of our logic to pure warp? Worth to discuss during our Thursday architecture meeting. I am also seeing dramatic speed-ups for newton when we start writing more kernels in the tasks. One of my concerns would mostly be on readability this is quite loaded, and some users may struggle to figure things out with this little information to go on. I think we should be a bit more didactic in the kernel descriptions.

Comment on lines +36 to +179
@wp.kernel
def _out_of_reach_kernel(
object_pos_w: wp.array(dtype=wp.vec3f),
env_origins: wp.array(dtype=wp.vec3f),
target_pos_e: wp.array(dtype=wp.vec3f),
fall_distance: float,
out_of_reach: wp.array(dtype=wp.bool),
):
i = wp.tid()
out_of_reach[i] = wp.length(object_pos_w[i] - env_origins[i] - target_pos_e[i]) >= fall_distance


@wp.kernel
def _full_obs_kernel(
joint_pos: wp.array2d(dtype=wp.float32),
joint_vel: wp.array2d(dtype=wp.float32),
lower: wp.array2d(dtype=wp.float32),
upper: wp.array2d(dtype=wp.float32),
vel_scale: float,
object_pos_w: wp.array(dtype=wp.vec3f),
env_origins: wp.array(dtype=wp.vec3f),
object_quat: wp.array(dtype=wp.quatf),
object_lin_vel: wp.array(dtype=wp.vec3f),
object_ang_vel: wp.array(dtype=wp.vec3f),
in_hand_pos_e: wp.array(dtype=wp.vec3f),
goal_quat: wp.array(dtype=wp.quatf),
body_pos_w: wp.array2d(dtype=wp.vec3f),
body_quat_w: wp.array2d(dtype=wp.quatf),
body_vel_w: wp.array2d(dtype=wp.spatial_vectorf),
finger_ids: wp.array(dtype=wp.int32),
force: wp.array2d(dtype=wp.vec3f),
torque: wp.array2d(dtype=wp.vec3f),
wrench_ids: wp.array(dtype=wp.int32),
force_scale: float,
with_forces: int,
actions: wp.array2d(dtype=wp.float32),
out: wp.array2d(dtype=wp.float32),
):
"""Direct full observation / full state, matching the torch concatenation order.

Launched over ``(num_envs, obs_dim)``: each thread walks a branch ladder over the
segment boundaries and writes one output column, so warps (32 consecutive columns)
stay branch-uniform except at segment boundaries.
"""
i, j = wp.tid()
num_joints = joint_pos.shape[1]
num_fingers = finger_ids.shape[0]
# segment boundaries, in column order
end_joint = 2 * num_joints
end_object = end_joint + 13
end_goal = end_object + 11
end_tip_pos = end_goal + 3 * num_fingers
end_tip_quat = end_tip_pos + 4 * num_fingers
end_tip_vel = end_tip_quat + 6 * num_fingers
end_wrench = end_tip_vel
if with_forces != 0:
end_wrench += 6 * num_fingers
# hand: normalized DOF positions, scaled DOF velocities
if j < num_joints:
out[i, j] = 2.0 * (joint_pos[i, j] - lower[i, j]) / (upper[i, j] - lower[i, j]) - 1.0
elif j < end_joint:
out[i, j] = vel_scale * joint_vel[i, j - num_joints]
# object pose and velocities (environment frame position)
elif j < end_object:
k = j - end_joint
if k < 3:
p = object_pos_w[i] - env_origins[i]
out[i, j] = p[k]
elif k < 7:
out[i, j] = object_quat[i][k - 3]
elif k < 10:
out[i, j] = object_lin_vel[i][k - 7]
else:
out[i, j] = vel_scale * object_ang_vel[i][k - 10]
# goal: in-hand anchor, goal rotation, and the goal-to-object rotation difference
elif j < end_goal:
k = j - end_object
if k < 3:
out[i, j] = in_hand_pos_e[i][k]
elif k < 7:
out[i, j] = goal_quat[i][k - 3]
else:
# quat_inverse == conjugate for these unit quaternions, matching
# isaaclab.utils.math.quat_mul/quat_conjugate semantics
qe = object_quat[i] * wp.quat_inverse(goal_quat[i])
out[i, j] = qe[k - 7]
# fingertips: environment-frame positions, rotations, spatial velocities
elif j < end_tip_pos:
out[i, j] = fingertip_pos_col(body_pos_w, env_origins, finger_ids, i, j - end_goal)
elif j < end_tip_quat:
out[i, j] = fingertip_quat_col(body_quat_w, finger_ids, i, j - end_tip_pos)
elif j < end_tip_vel:
out[i, j] = fingertip_vel_col(body_vel_w, finger_ids, i, j - end_tip_quat)
# fingertip force/torque sensors (full state only; absent when with_forces == 0)
elif j < end_wrench:
if with_forces == 1:
k = j - end_tip_vel
c = k % 6
if c < 3:
out[i, j] = force_scale * force[i, wrench_ids[k // 6]][c]
else:
out[i, j] = force_scale * torque[i, wrench_ids[k // 6]][c - 3]
else:
# full state requested but the sensor has no data yet: zero block
out[i, j] = 0.0
# actions
else:
out[i, j] = actions[i, j - end_wrench]


@wp.kernel
def _reduced_obs_kernel(
body_pos_w: wp.array2d(dtype=wp.vec3f),
env_origins: wp.array(dtype=wp.vec3f),
finger_ids: wp.array(dtype=wp.int32),
object_pos_w: wp.array(dtype=wp.vec3f),
object_quat: wp.array(dtype=wp.quatf),
goal_quat: wp.array(dtype=wp.quatf),
actions: wp.array2d(dtype=wp.float32),
out: wp.array2d(dtype=wp.float32),
):
"""Direct reduced (OpenAI) observation, matching the torch concatenation order."""
i = wp.tid()
num_fingers = finger_ids.shape[0]
for f in range(num_fingers):
fp = body_pos_w[i, finger_ids[f]] - env_origins[i]
out[i, 3 * f + 0] = fp[0]
out[i, 3 * f + 1] = fp[1]
out[i, 3 * f + 2] = fp[2]
idx = 3 * num_fingers
p = object_pos_w[i] - env_origins[i]
# quat_inverse == conjugate for these unit quaternions, matching
# isaaclab.utils.math.quat_mul/quat_conjugate semantics
qe = object_quat[i] * wp.quat_inverse(goal_quat[i])
out[i, idx + 0] = p[0]
out[i, idx + 1] = p[1]
out[i, idx + 2] = p[2]
out[i, idx + 3] = qe[0]
out[i, idx + 4] = qe[1]
out[i, idx + 5] = qe[2]
out[i, idx + 6] = qe[3]
idx += 7
for a in range(actions.shape[1]):
out[i, idx + a] = actions[i, a]

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Why are they not in the re-orient kernel file?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

It's direct env specific. Is it still prefer to be moved given it's not used by others?

Comment on lines 246 to 248
self.successes = torch.zeros(self.num_envs, dtype=torch.float, device=self.device)
self.consecutive_successes = torch.zeros(1, dtype=torch.float, device=self.device)
self._last_episode_success = torch.zeros(self.num_envs, dtype=torch.bool, device=self.device)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

If we are going full warp why not use wp.zeros?

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.

warp envs will be seperate (under warp/), so we can keep the torch for core/ and contrib/

Comment on lines +334 to +347
wp.launch(
ema_actuation_kernel,
dim=(self.num_envs, self._actuated_dof_ids_wp.shape[0]),
inputs=[
wp.from_torch(self.actions),
self._lower_limits_wp,
self._upper_limits_wp,
self._actuated_dof_ids_wp,
self.cfg.act_moving_average,
self._prev_targets_wp,
self._cur_targets_wp,
],
outputs=[self._compact_targets_wp],
device=self._compact_targets_wp.device,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

For extra speeeeeed we could cache the kernel on the first exec using the record launch command.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Will defer those for now. is there a util class merged?

Comment on lines 359 to 364
# RSL-RL holds the observation reference across the next env.step, so hand out
# a per-step snapshot rather than the persistent kernel output buffer.
observations = {"policy": obs.clone()}
if self.cfg.asymmetric_obs:
observations["critic"] = self.compute_full_state()
observations["critic"] = self.compute_full_state().clone()
return observations

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Rather than cloning outside the kernel launch the kernel launch could write to safe pre-allocated warp arrays with cached torch bindings?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

rl lib could take the output and keep it more than one step. So properly using a persistent buffer would require a bit more changes. Deferring it for now.

Comment on lines 438 to 442
self.episode_length_buf = torch.where(
torch.abs(rot_dist) <= self.cfg.success_tolerance,
goal_reached,
torch.zeros_like(self.episode_length_buf),
self.episode_length_buf,
)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Can't the kernel above do the torch.where?

Comment on lines 453 to 455
self.extras.setdefault("log", {})["Metrics/success_rate"] = (
self._last_episode_success[env_ids].float().mean().item()
)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This will force a cuda sync, there is a way to avoid this,

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

What's the suggested way?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Should have UTs on these kernels?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Added.

@hujc7
hujc7 force-pushed the jichuanh/task-cleanup-dex-part03 branch from b1e2a42 to cf21603 Compare July 15, 2026 09:12
Add a behavioral Metrics/success_rate signal (goal-reach streaks per
episode) and threshold-independent episode orientation-error diagnostics
to the Direct reorientation environments, with the shared helpers in
isaaclab_tasks.core.utils and torch math tests. The task logic is
torch-first per the mainline convention; success gates task health while
reward stays diagnostic. Also fix hand resets that could initialize
joints below their lower position limits.
@hujc7
hujc7 force-pushed the jichuanh/task-cleanup-dex-part03 branch from ef2b22c to 6e8a63e Compare July 17, 2026 00:44
@hujc7 hujc7 changed the title [Task Clean-up] Dexterous Part 3/11: Add success-rate metrics to the reorientation Direct tasks [Task Clean-up] Dexterous Part 3: Add success-rate metrics to the reorientation Direct tasks Jul 17, 2026
@hujc7 hujc7 changed the title [Task Clean-up] Dexterous Part 3: Add success-rate metrics to the reorientation Direct tasks [Task Clean-up] Dexterous Part 3/11: Add success-rate metrics to the reorientation Direct tasks Jul 17, 2026
hujc7 added a commit that referenced this pull request Jul 17, 2026
…nager runtime (#6412)

## Summary

- Fixes OVPhysX actuator joint indices to follow the common actuator
indexing contract.
- Fixes OVPhysX initialization alongside Kit by reusing Kit's registered
PhysX schema provider.
- Fixes the OVPhysX manager to support both the declared public runtime
API and the current runtime API.
- Regression tests included. Validated by full dexterous training runs
on the OVPhysX backend; split out of the lumped validation branch #6324
(Part 2 of 11).

## Dependencies

- None.

## Series review map

Full integrated diff + training/validation evidence: the lumped
validation PR #6324
(DO-NOT-MERGE).

| Part | PR |
|---|---|
| Docs: regenerate the environment overview table |
#6410 |
| Part 1/11: Newton runtime fixes (cloner rows, cubric fallback, viz
teardown) | #6411 |
| **Part 2/11: OVPhysX runtime fixes (this PR)** |
#6412 |
| Part 3/11: success-rate metrics for the Direct reorientation tasks |
#6413 |
| Part 4/11: RSL-RL training for the handover Direct task |
#6414 |
| Part 5/11: success-rate support in the benchmark utilities |
#6415 |
| Part 6/11: renderer presets for the Direct camera task |
#6416 |
| Part 7/11: OVPhysX presets for the dexterous tasks |
#6417 |
| Part 8/11: Allegro manager counterpart |
#6418 |
| Part 9/11: Shadow + OpenAI manager counterparts |
#6419 |
| Part 10/11: Shadow camera manager counterpart |
#6420 |
| Part 11/11: Shadow handover manager counterpart |
#6421 |


---
### Exact changes in this PR

- OVPhysX backend changes + tests:
1f7a433
@hujc7 hujc7 changed the title [Task Clean-up] Dexterous Part 3/11: Add success-rate metrics to the reorientation Direct tasks [Task Clean-up] Dexterous Part 3/8: Add success-rate metrics to the reorientation Direct tasks Jul 17, 2026
@hujc7
hujc7 requested a review from StafaH July 17, 2026 23:51
@hujc7

hujc7 commented Jul 18, 2026

Copy link
Copy Markdown
Collaborator Author

Do we want to move more of our logic to pure warp? Worth to discuss during our Thursday architecture meeting. I am also seeing dramatic speed-ups for newton when we start writing more kernels in the tasks. One of my concerns would mostly be on readability this is quite loaded, and some users may struggle to figure things out with this little information to go on. I think we should be a bit more didactic in the kernel descriptions.

@AntoineRichard For this PR, I am updating the torch impl. The already implemented warp one is moving to experimental folder

@hujc7
hujc7 requested a review from AntoineRichard July 18, 2026 09:34
Fold the reviewed lump changes that belong to this part's content:

- Share per-family sim settings through task-cfg base mixins.
- Deduplicate backend scene presets via inner-class defaults and nest
  single-consumer helper cfgs in the shadow-hand Direct cfg.
- Compute the orientation error through
  isaaclab.utils.math.quat_error_magnitude and delete the local
  direct_reorient_rotation_distance primitive.
- Rename direct_reorient_reward to reorient_reward: shared symbols
  carry no paradigm prefix.

Source commits on the lump branch: 2c22af0, 792e400,
42675b6, c6140f9, 173e9dc.
@hujc7
hujc7 requested a review from ooctipus as a code owner July 18, 2026 11:10
@hujc7
hujc7 requested a review from a team July 18, 2026 11:10
hujc7 added 4 commits July 20, 2026 15:32
Part 3 share of the lump readability round (cdaadac): the Direct
cfg files keep only task values; asset and marker cfgs, name lists,
backend presets, and noise cfgs move to shadow_hand_common and
allegro_hand_common, task geometry to reorient_common, and
reorient_task_base is removed. The Shadow Direct files carry the
workflow marker in their names.
The shadow_hand_env_cfg -> shadow_hand_direct_env_cfg rename landed at
this layer while two consumers kept importing the removed module name,
so the part tree no longer stood alone (caught by arm-ci kitless
rendering collection). Pin the camera cfg import to
shadow_hand_direct_env_cfg and the handover robot cfg import to
shadow_hand_common, matching the final tree.
The rename round updated the camera gym-registration entry-point
strings to shadow_hand_direct_camera_env* at this layer, but the
camera modules here still carry their pre-rename names, so every
registry-driven cfg load failed at import (isaaclab_tasks suites,
forbidden-imports test, registered-tasks rendering). Point the
registrations back at the modules that exist at this layer.
hujc7 added a commit that referenced this pull request Jul 22, 2026
…isualizer teardown (#6411)

## Review Map

- **Exact changes**: a stacked PR's page shows the cumulative diff of
its dependency chain; the link pins the commit range that is the PR's
own contribution.
- Links pin specific SHAs and can go stale after a branch update — the
table on #6324 is refreshed first.

| PR | Status | Depends on | Exact changes |
|---|---|---|---|
| #6410 [Docs] Environment overview regen |
![](https://img.shields.io/github/pulls/detail/state/isaac-sim/IsaacLab/6410?label=)
| — | — |
| 📌 #6411 Part 1/8: Newton cloner/cubric/visualizer fixes (this PR) |
![](https://img.shields.io/github/pulls/detail/state/isaac-sim/IsaacLab/6411?label=)
| — | — |
| #6412 Part 2/8: OVPhysX articulation + manager runtime |
![](https://img.shields.io/github/pulls/detail/state/isaac-sim/IsaacLab/6412?label=)
| — | — |
| #6413 Part 3/8: Reorient Direct, torch |
![](https://img.shields.io/github/pulls/detail/state/isaac-sim/IsaacLab/6413?label=)
| — | — |
| #6414 Part 4/8: MARL-to-single-agent fix + handover/camera Direct |
![](https://img.shields.io/github/pulls/detail/state/isaac-sim/IsaacLab/6414?label=)
| #6413 |
[changes](https://github.com/isaac-sim/IsaacLab/pull/6414/changes/6e8a63e4e028b2d43676ea30c446b9dc9068c7b5..5cb00e7cb5cc813b202521272e043007cd255194)
|
| #6418 Part 5/8: Reorient manager counterparts |
![](https://img.shields.io/github/pulls/detail/state/isaac-sim/IsaacLab/6418?label=)
| #6413 |
[changes](https://github.com/isaac-sim/IsaacLab/pull/6418/changes/79f87501ac4c81de93a71dab00dc443da62113aa..e7c9a9a3fae3a7972b0c5165ae683abffb7d0e0f)
|
| #6421 Part 6/8: Handover + camera manager counterparts |
![](https://img.shields.io/github/pulls/detail/state/isaac-sim/IsaacLab/6421?label=)
| #6413, #6414, #6418 |
[changes](https://github.com/isaac-sim/IsaacLab/pull/6421/changes/01c9f4d8c5c35a5688b2a5bb90209e16b8f81b99..835a5815ec49b11aada1d20a76c177054505e6e7)
|
| #6415 Part 7/8: Benchmark success-rate utilities + docs |
![](https://img.shields.io/github/pulls/detail/state/isaac-sim/IsaacLab/6415?label=)
| #6413, #6414, #6418, #6421 |
[changes](https://github.com/isaac-sim/IsaacLab/pull/6415/changes/e1abb6b1790ccc57af42551eebccf743633f1f13..d6348539aa8032d9668c20a8fea462c5d88d3af9)
|
| #6582 Part 8/8: Warp variants → experimental (draft; merges last) |
![](https://img.shields.io/github/pulls/detail/state/isaac-sim/IsaacLab/6582?label=)
| #6413 |
[changes](https://github.com/isaac-sim/IsaacLab/pull/6582/changes/21dbb1769c4e30c8e9e5b0f563c2dae24c230349..83e1587cadd9712a60615ed2a3cb2d177c2ac24d)
|
| #6324 [DO-NOT-MERGE] Lumped validation reference |
![](https://img.shields.io/github/pulls/detail/state/isaac-sim/IsaacLab/6324?label=)
| ALL | — |

## Summary

- Fixes Newton cloner label rows, the cubric IAdapter version audit
(exact-match fallback to the CPU hierarchy path), and visualizer
teardown.
- Retains an in-tree `ignore_paths` workaround for custom-frequency USD
traversal; it becomes redundant once the Newton pin advance
(#6584) merges — this PR then
only needs a rebase.

## Stacking

- Independent; based on `develop`.

## Review history

- Approved. The Newton pin + MuJoCo overrides were split out to
#6584 via revert commits
(2026-07-17) so this PR's CI runs against develop's pins.
@kellyguo11

Copy link
Copy Markdown
Contributor

can you also update the environments.rst doc page to include the new physics backends supported? they should also apply to all the shadow hand variants right?

@StafaH StafaH 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.

Thanks @hujc7, left some comments but nothing major/blocking.

),
init_state=RigidObjectCfg.InitialStateCfg(pos=(0.0, -0.17, 0.56), rot=(0.0, 0.0, 0.0, 1.0)),
)
default = physx

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.

Let's default to newton_mjwarp (here and elsewhere) since our base installation for IsaacLab 3.0 will have newton and not physx

cone="elliptic",
update_data_interval=2,
iterations=100,
# save_to_mjcf="AllegroHand.xml",

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.

remove?

impratio=10.0,
cone="elliptic",
update_data_interval=2,
iterations=100,

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.

For values matching the defaults we can just remove from here (iterations, solver, debug mode).

nconmax=70,
impratio=10.0,
cone="elliptic",
update_data_interval=2,

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.

is 2 deliberate here? It's an interesting choice, what does 2 do vs 1 (updating state every step)?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

This is common for mywarp config in the repo. seems to improve stability.

reward = torch.where(goal_distance >= fall_distance, reward + fall_penalty, reward)
resets = torch.where(goal_distance >= fall_distance, torch.ones_like(reset_buf), reset_buf)
num_resets = torch.sum(resets)
finished_successes = torch.sum(successes * resets.float())

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.

finished_successes = (successes * resets).sum()

resets = torch.where(goal_distance >= fall_distance, torch.ones_like(reset_buf), reset_buf)
num_resets = torch.sum(resets)
finished_successes = torch.sum(successes * resets.float())
consecutive_successes = torch.where(

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.

mean_successes = finished_successes / num_resets.clamp_min(1)
updated_consecutive_successes = (
    averaging_factor * mean_successes
    + (1.0 - averaging_factor) * consecutive_successes
)
consecutive_successes = torch.where(
    num_resets > 0,
    updated_consecutive_successes,
    consecutive_successes,
)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

done.

@@ -0,0 +1,32 @@
# Copyright (c) 2022-2026, The Isaac Lab Project Developers (https://github.com/isaac-sim/IsaacLab/blob/main/CONTRIBUTORS.md).
# All rights reserved.

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.

Similar comment about common.py

Comment on lines 246 to 248
self.successes = torch.zeros(self.num_envs, dtype=torch.float, device=self.device)
self.consecutive_successes = torch.zeros(1, dtype=torch.float, device=self.device)
self._last_episode_success = torch.zeros(self.num_envs, dtype=torch.bool, device=self.device)

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.

warp envs will be seperate (under warp/), so we can keep the torch for core/ and contrib/

@@ -0,0 +1,137 @@
# Copyright (c) 2026, The Isaac Lab Project Developers (https://github.com/isaac-sim/IsaacLab/blob/main/CONTRIBUTORS.md).

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.

Let's avoid adding files in isaaclab_tasks other than the tasks themselves. If its useful for more than one, let's promote it to the global MDP and global utils in isaaclab core

Applies the P3-owned share of the lump review-response commits:

- Default the dexterous tasks to newton_mjwarp (from 3f9ce324).
- Drop solver defaults matching the backend + inline the scene preset
  via preset() instead of a wrapper class (from 29e189bf).
- Simplify the Direct reorientation reward computation (from bfb73735).
- Source the actuated-joint and fingertip body-name lists from the
  robot assets instead of the config module (from 34676102).

Deferred to their owning layers: the core/utils relocation (S17) lands
at the manager layer where reorient/mdp/events.py is introduced, and the
handover default/preset changes land with the handover part.
@github-actions github-actions Bot added the asset New asset feature or request label Jul 23, 2026
@hujc7 hujc7 changed the title [Task Clean-up] Dexterous Part 3/8: Add success-rate metrics to the reorientation Direct tasks [Task Clean-up] Dexterous Part 3/9: Add success-rate metrics to the reorientation Direct tasks Jul 23, 2026
from isaaclab.utils.configclass import configclass

from isaaclab_tasks.core.reorient.config.shadow_hand.shadow_hand_env_cfg import ShadowHandRobotCfg
from isaaclab_tasks.core.reorient.config.shadow_hand.shadow_hand_common import ShadowHandRobotCfg

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.

Why does the base env config need to import the robot config? This should be handled in the per-robot config, and probably just use MISSING until then

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

defer the fixes to next PR. CI has been slow



@configclass
class ObjectCfg(PresetCfg):

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.

nit: CubeObjectCfg or CubeCfg?

actuators={},
articulation_root_prim_path="",
)
ovphysx = RigidObjectCfg(

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.

looks identical to physx, ovphysx = physx?

OBJECT_CFG,
OPENAI_ACTION_NOISE_CFG,
OPENAI_OBSERVATION_NOISE_CFG,
ROBOT_CFG,

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.

Some stuff is capital, some stuff is not, seems inconsistent

The vision env renders through the Isaac RTX tiled camera, whose render
products require the Fabric cloning path. The Newton backend disables
Fabric cloning, so under Newton the rgb annotator has no render products
at num_envs > 1 and the default RGB/depth/semantic render fails with
"Annotator rgb is not attached to any render products".

The shared PhysicsCfg/RobotCfg/ObjectCfg now default to Newton, so the
camera env inherited a Newton default it cannot render with. Override the
camera env's backend PresetCfgs to default to PhysX in __post_init__;
Newton stays selectable via physics=newton_mjwarp for the depth-only
Newton-warp-renderer benchmark path.
@hujc7

hujc7 commented Jul 24, 2026

Copy link
Copy Markdown
Collaborator Author

can you also update the environments.rst doc page to include the new physics backends supported? they should also apply to all the shadow hand variants right?

defer to #6410

@hujc7
hujc7 merged commit 4c9e5b7 into isaac-sim:develop Jul 24, 2026
41 checks passed
rwiltz added a commit to rwiltz/IsaacLab that referenced this pull request Jul 28, 2026
The skills gate fails on develop because
use-sensors-actuators/examples.md points at
reorient/config/shadow_hand/shadow_hand_env_cfg.py, which isaac-sim#6413 renamed
in July. The gate only runs for PRs that touch skills/, so the breakage
sat unnoticed until the next such PR.

Point the reference at shadow_hand_common.py rather than the renamed
shadow_hand_direct_env_cfg.py. The rename also split the config, and it
is common.py that carries the actuators block the surrounding checklist
asks the reader to inspect: joint_names_expr, stiffness, damping, and
armature.
hujc7 added a commit that referenced this pull request Jul 29, 2026
… and enable handover Direct RSL-RL (#6414)

## Review Map

- **Exact changes**: a stacked PR's page shows the cumulative diff of
its dependency chain; the link pins the commit range that is the PR's
own contribution. This PR (#6414) is now rebased directly onto
`develop`, so its own **Files changed** tab is its contribution.
- Links pin specific SHAs and can go stale after a branch update — the
table on #6324 is refreshed first.

| PR | Status | Depends on | Exact changes |
|---|---|---|---|
| #6411 Part 1/9: Newton cloner/cubric/visualizer fixes |
![](https://img.shields.io/github/pulls/detail/state/isaac-sim/IsaacLab/6411?label=)
| — | merged |
| #6412 Part 2/9: OVPhysX articulation + manager runtime |
![](https://img.shields.io/github/pulls/detail/state/isaac-sim/IsaacLab/6412?label=)
| — | merged |
| #6413 Part 3/9: Reorient Direct, torch |
![](https://img.shields.io/github/pulls/detail/state/isaac-sim/IsaacLab/6413?label=)
| — | merged |
| 📌 #6414 Part 4/9: MARL-to-single-agent fix + handover/camera Direct
(this PR) |
![](https://img.shields.io/github/pulls/detail/state/isaac-sim/IsaacLab/6414?label=)
| — (on develop) | Files changed tab |
| #6418 Part 5/9: Reorient manager counterparts |
![](https://img.shields.io/github/pulls/detail/state/isaac-sim/IsaacLab/6418?label=)
| #6413 |
[changes](https://github.com/isaac-sim/IsaacLab/pull/6418/changes/d29afc75e71..707d37f8f99)
|
| #6421 Part 6/9: Handover + camera manager counterparts |
![](https://img.shields.io/github/pulls/detail/state/isaac-sim/IsaacLab/6421?label=)
| #6414, #6418 |
[changes](https://github.com/isaac-sim/IsaacLab/pull/6421/changes/707d37f8f99..b50ac8906fc)
|
| #6410 Part 7/9: Environment overview docs |
![](https://img.shields.io/github/pulls/detail/state/isaac-sim/IsaacLab/6410?label=)
| #6421 |
[changes](https://github.com/isaac-sim/IsaacLab/pull/6410/changes/b50ac8906fc..af259c0778d)
|
| #6415 Part 8/9: Benchmark success-rate utilities |
![](https://img.shields.io/github/pulls/detail/state/isaac-sim/IsaacLab/6415?label=)
| #6421 |
[changes](https://github.com/isaac-sim/IsaacLab/pull/6415/changes/b50ac8906fc..c7f2f019d8b)
|
| #6582 Part 9/9: Warp variants → experimental (draft; merges last) |
![](https://img.shields.io/github/pulls/detail/state/isaac-sim/IsaacLab/6582?label=)
| — |
[changes](https://github.com/isaac-sim/IsaacLab/pull/6582/changes/d29afc75e71..167c28578b3)
|
| #6324 [DO-NOT-MERGE] Lumped validation reference |
![](https://img.shields.io/github/pulls/detail/state/isaac-sim/IsaacLab/6324?label=)
| ALL | — |

## Summary

Converts the two-hand Shadow Hand **handover** task from multi-agent
(MARL) to **single-agent** (enabling RSL-RL training), fixes the
underlying MARL→single-agent observation bridge, and consolidates the
shared Shadow Hand definitions into the asset.

- **MARL → single-agent bridge (general, broader than this task).**
Single-agent RL libraries train `DirectMARLEnv` tasks via
`multi_agent_to_single_agent`; the bridge dropped the latest
observations from the public buffer. Fixed generally — RSL-RL
observations read from the env-owned `obs_buf`, stored by `reset` like
`step` on all env bases (`DirectRLEnv`, `DirectMARLEnv`, the
experimental warp base). Every MARL task + single-agent runner benefits;
handover is the first consumer.
- **Handover Direct → single-agent + RSL-RL.** Single-agent conversion,
RSL-RL runner config, shared identity in `handover_common`, success-rate
reward metrics, and a fix for its Newton construction failure (see
below).
- **Shadow Hand Newton robot → the asset.** Moved the Newton (MJWarp)
robot cfg into `isaaclab_assets` as `SHADOW_HAND_NEWTON_CFG` (beside
`SHADOW_HAND_CFG`); reorient and handover consume it as **siblings**
(neither imports the other). Reorient uses the default gains; handover
overrides only the finger stiffness/damping to `20/2` for its catch.
- **Shadow camera cleanup.** Renamed the camera Direct modules to
`shadow_hand_direct_camera_env[_cfg]`; moved cube-keypoint math to the
shared, unit-tested `reorient/mdp/observations.py` (replacing the
per-env `compute_keypoints` shim); and added OVPhysX physics presets to
the handover and camera Direct environments. The camera env config is
otherwise unchanged from develop.

### Behavior changes

**Reorient-on-Newton is unchanged.** The Newton hand definition moves
**verbatim** into `SHADOW_HAND_NEWTON_CFG` — same joint expressions,
effort limits, gains, friction and armature — so its config diff vs
`develop` is purely the move, and the Direct env config has no diff at
all.

Handover changes in three ways:

- Its **default physics backend moves from PhysX to Newton** (MJWarp),
matching the reorientation tasks. Pass `physics=physx` for the previous
backend.
- Its `distal_passive` actuator group is **removed**. It targeted
`robot0_(FF|MF|RF|LF)J0` — joints that **do not exist** on the Newton
asset (its fingers are numbered `J1`–`J4`/`J5`), so constructing the
environment raised `No joints found for actuator group`. The `fingers`
group already drives those joints, and the comment justifying the group
(USD-baked `stiffness=286/damping=57`) does not apply to this asset,
which authors no drive stiffness at all.
- Two orientation fixes: the goal orientation was initialized to a
180-degree rotation instead of identity, and the Newton root rotation
replaced the asset's baked rotation instead of composing with it,
leaving both palms rotated 90 degrees.

Two pre-existing discrepancies were found while verifying against the
asset and are left for a follow-up, since each is a behavior change
needing its own validation: the config sets `armature=2e-3` where the
asset authors `0.0002`, and the per-finger `J1`/`J2` pair is coupled by
a fixed tendon that the MJWarp solver currently skips — the actuator
gains are what hold that pair together today, which is why the `J4`
knuckle-abduction joints cannot simply be added to the driven set.

## Notes

- Rebased onto **latest develop**; clean 3-commit history: `Add
single-agent flattening for MARL Direct environments` / `Convert Shadow
Hand handover to single-agent Direct with RSL-RL` / `Clean up Shadow
reorient/camera tasks and consolidate the Newton hand`.

## Validation

- Kit-free suite green: handover, keypoint math, the MARL adapter, and
new checks pinning the RSL-RL wrapper observation contract (verified to
fail against the previous implementation).
- Camera Direct reaches training takeoff under `rsl_rl` (frame-verified
videos); its env config now matches develop's established vision env.
- **Reorient-on-Newton vs `develop`** (`rsl_rl`, 2048 envs, 1500
iterations, 4 runs per config): this branch spans 524–664 reward,
`develop` spans 654–822 — overlapping distributions whose per-config
spreads (140–168) exceed the gap between them. The move is additionally
verified at the config level: the Newton robot configuration is
character-for-character identical to `develop`'s.
- **Handover-on-Newton** trains end-to-end on this branch (reward 1314),
which the `develop` config cannot do at all — it fails during
construction. Full evidence: #6324.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

asset New asset feature or request isaac-lab Related to Isaac Lab team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants