Skip to content

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

Open
hujc7 wants to merge 5 commits into
isaac-sim:developfrom
hujc7:jichuanh/task-cleanup-dex-part03
Open

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

Conversation

@hujc7

@hujc7 hujc7 commented Jul 8, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Adds a behavioral Metrics/success_rate signal (goal-reach streaks per episode) and threshold-independent episode orientation-error diagnostics to the existing Direct reorientation environments, per the Task Clean-up standard (success rate gates task health; reward stays diagnostic).
  • Lands the shared helpers in isaaclab_tasks.core.utils (EpisodeErrorRecorder, sample_joint_positions_within_limits) with unit tests.
  • Fixes hand resets that could initialize joints below their lower position limits.
  • Validated by full training runs on PhysX, Newton, and OVPhysX (Shadow Direct reaches success-rate 1.0 on PhysX); split out of the lumped validation branch [DO-NOT-MERGE][Task Clean-up] Dexterous: lumped validation branch (split into Parts 1-11) #6324 (Part 3 of 11).

Dependencies

  • None.

Review updates (2026-07-09)

  • Warp reward wrappers now take caller-owned output buffers (no per-step allocations); the Direct env allocates them once.
  • The task-constants module gains the per-variant scalar values (decimation, episode length, noise, reward scales) consumed by the Direct cfgs and later by the manager counterparts.

The Direct reorientation environments now log a behavioral
Metrics/success_rate signal (goal-reach streaks per episode) and
threshold-independent episode orientation-error diagnostics, per the
Task Clean-up standard where success rate gates task health and reward
stays diagnostic. Shared helpers land in isaaclab_tasks.core.utils.

Also fixes hand resets that could initialize joints below their lower
position limits.

Validated by full training runs on PhysX, Newton, and OVPhysX; the
Shadow Direct task reaches success-rate 1.0 on PhysX.
@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!

hujc7 added 2 commits July 9, 2026 01:05
The shared general functions move from torch.jit.script to Warp
kernels with signature-identical wrappers; randomized parity tests
against the replaced torch implementation guard the numerics on CPU
and CUDA. The Direct env's jit delegation shims lose their decorators
(TorchScript cannot compile Warp launches).
The shared reward/success math moves to Warp kernels with parity tests
against the previous torch implementation, and the task-defining
constants (goal-marker position, fingertip names) move to a shared
constants module consumed by the Direct cfg and, later, the manager
counterparts.
hujc7 added 2 commits July 9, 2026 08:21
The reward wrappers now require caller-owned output buffers so the hot
loop allocates nothing per step; the Direct environment allocates them
once in __init__. The task-constants module gains the per-variant
scalar values consumed by the Direct cfgs and, later, the manager
counterparts.
The reward kernels launch directly on Warp-native environment state
(ProxyArray views) with caller-owned, view-cached buffers, so the hot
loop allocates and converts nothing per step. The task-constants module gains the per-variant
scalar values consumed by the Direct cfgs and, later, the manager
counterparts.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

isaac-lab Related to Isaac Lab team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant