[Task Clean-up] Dexterous Part 4/9: Fix MARL-to-single-agent training and enable handover Direct RSL-RL - #6414
Conversation
Greptile SummaryThis PR enables RSL-RL PPO training on the Shadow Hand handover Direct task by fixing the MARL-to-single-agent adapter to expose live observations via
Confidence Score: 3/5Not safe to merge as-is: The source/isaaclab_tasks/isaaclab_tasks/core/handover/handover_env.py — the import from Important Files Changed
Sequence Diagram%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
participant RSL as RSL-RL Runner
participant Adapter as multi_agent_to_single_agent (Env)
participant MARL as HandoverEnv (DirectMARLEnv)
RSL->>Adapter: reset(seed, options)
Adapter->>MARL: reset(seed, options)
MARL-->>Adapter: obs (per-agent dict), extras
Adapter->>Adapter: _convert_observations(obs)
Adapter-->>RSL: "{"policy": concat_obs}, extras"
RSL->>Adapter: _get_observations()
Note over Adapter: NEW: exposes live obs for RSL-RL direct access
Adapter->>MARL: _get_observations()
MARL-->>Adapter: obs (per-agent dict)
Adapter->>Adapter: _convert_observations(obs)
Adapter-->>RSL: "{"policy": concat_obs}"
RSL->>Adapter: step(action)
Adapter->>Adapter: split action by agent
Adapter->>MARL: "step({right_hand: a0, left_hand: a1})"
MARL-->>Adapter: obs, rewards, terminated, time_outs, extras
Adapter->>Adapter: _convert_observations(obs)
Adapter-->>RSL: obs, sum(rewards), AND(terminated), AND(time_outs), extras
RSL->>Adapter: "episode_length_buf = value"
Note over Adapter: Property setter forwards write to MARL env
Adapter->>MARL: "episode_length_buf = value"
%%{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 RSL as RSL-RL Runner
participant Adapter as multi_agent_to_single_agent (Env)
participant MARL as HandoverEnv (DirectMARLEnv)
RSL->>Adapter: reset(seed, options)
Adapter->>MARL: reset(seed, options)
MARL-->>Adapter: obs (per-agent dict), extras
Adapter->>Adapter: _convert_observations(obs)
Adapter-->>RSL: "{"policy": concat_obs}, extras"
RSL->>Adapter: _get_observations()
Note over Adapter: NEW: exposes live obs for RSL-RL direct access
Adapter->>MARL: _get_observations()
MARL-->>Adapter: obs (per-agent dict)
Adapter->>Adapter: _convert_observations(obs)
Adapter-->>RSL: "{"policy": concat_obs}"
RSL->>Adapter: step(action)
Adapter->>Adapter: split action by agent
Adapter->>MARL: "step({right_hand: a0, left_hand: a1})"
MARL-->>Adapter: obs, rewards, terminated, time_outs, extras
Adapter->>Adapter: _convert_observations(obs)
Adapter-->>RSL: obs, sum(rewards), AND(terminated), AND(time_outs), extras
RSL->>Adapter: "episode_length_buf = value"
Note over Adapter: Property setter forwards write to MARL env
Adapter->>MARL: "episode_length_buf = value"
|
| ) | ||
|
|
||
| from isaaclab_tasks.core.handover.handover_env_cfg import HandoverEnvCfg | ||
| from isaaclab_tasks.core.handover.mdp.rewards import evaluate_handover_success, handover_reward |
There was a problem hiding this comment.
Missing module:
isaaclab_tasks.core.handover.mdp.rewards
handover_env.py imports evaluate_handover_success and handover_reward from isaaclab_tasks.core.handover.mdp.rewards, but no such module exists anywhere in the repository — there is no mdp/ subdirectory under core/handover/. The PR description names PR #6413 as the only declared code dependency (for isaaclab_tasks.core.utils), but that PR's description only mentions "core.utils helpers" and does not describe adding a handover-specific mdp/rewards.py. Without this file the task class will raise ModuleNotFoundError on import, blocking all training runs.
| def _get_observations(self) -> VecEnvObs: | ||
| """Return current observations through the single-agent interface.""" | ||
| return self._convert_observations(self.env._get_observations()) |
There was a problem hiding this comment.
Unnecessary
_get_observations() call in state-as-observation mode
When _state_as_observation=True, _get_observations still calls self.env._get_observations() (which may trigger sensor data collection) and then immediately discards the result inside _convert_observations, which pivots to self.env.state(). The call is harmless but wasteful — consider short-circuiting before the inner call, e.g. returning {"policy": self.env.state()} directly when in state-as-observation mode.
Such implementation should not be accepted as it is trying to fix a problem a RL library has (require a private method, that is not part of the standard API Gymnasium / Petting-Zoo Parallel API to initialize its logic) from Isaac Lab side. There is nothing to fix here. The fix must be implemented in the RL library to do not depend on the private @kellyguo11 for viz |
| def _convert_observations(self, obs: dict[AgentID, ObsType]) -> VecEnvObs: | ||
| """Convert multi-agent observations to the single-agent policy observation.""" | ||
| if self._state_as_observation: | ||
| obs = {"policy": self.env.state()} | ||
| # concatenate agents' observations | ||
| # FIXME: This implementation assumes the spaces are fundamental ones. Fix it to support composite spaces | ||
| else: | ||
| obs = { | ||
| "policy": torch.cat( | ||
| [obs[agent].reshape(self.num_envs, -1) for agent in self.env.possible_agents], dim=-1 | ||
| ) | ||
| } | ||
| return {"policy": self.env.state()} | ||
| return { | ||
| "policy": torch.cat( | ||
| [obs[agent].reshape(self.num_envs, -1) for agent in self.env.possible_agents], dim=-1 | ||
| ) | ||
| } | ||
|
|
||
| return obs, extras | ||
| def _get_observations(self) -> VecEnvObs: | ||
| """Return current observations through the single-agent interface.""" | ||
| if self._state_as_observation: | ||
| # the state replaces the observations entirely; skip computing them | ||
| return {"policy": self.env.state()} | ||
| return self._convert_observations(self.env._get_observations()) |
AntoineRichard
left a comment
There was a problem hiding this comment.
My concern is that this PR is doing some real (important and needed) work on MARL and RSL-RL while hiding that under a RSL-RL only refactor.
Also same concerns around moving entirely to warp.
5918610 to
392e7d9
Compare
…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
…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 |  | — | — | | 📌 #6411 Part 1/8: Newton cloner/cubric/visualizer fixes (this PR) |  | — | — | | #6412 Part 2/8: OVPhysX articulation + manager runtime |  | — | — | | #6413 Part 3/8: Reorient Direct, torch |  | — | — | | #6414 Part 4/8: MARL-to-single-agent fix + handover/camera Direct |  | #6413 | [changes](https://github.com/isaac-sim/IsaacLab/pull/6414/changes/6e8a63e4e028b2d43676ea30c446b9dc9068c7b5..5cb00e7cb5cc813b202521272e043007cd255194) | | #6418 Part 5/8: Reorient manager counterparts |  | #6413 | [changes](https://github.com/isaac-sim/IsaacLab/pull/6418/changes/79f87501ac4c81de93a71dab00dc443da62113aa..e7c9a9a3fae3a7972b0c5165ae683abffb7d0e0f) | | #6421 Part 6/8: Handover + camera manager counterparts |  | #6413, #6414, #6418 | [changes](https://github.com/isaac-sim/IsaacLab/pull/6421/changes/01c9f4d8c5c35a5688b2a5bb90209e16b8f81b99..835a5815ec49b11aada1d20a76c177054505e6e7) | | #6415 Part 7/8: Benchmark success-rate utilities + docs |  | #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) |  | #6413 | [changes](https://github.com/isaac-sim/IsaacLab/pull/6582/changes/21dbb1769c4e30c8e9e5b0f563c2dae24c230349..83e1587cadd9712a60615ed2a3cb2d177c2ac24d) | | #6324 [DO-NOT-MERGE] Lumped validation reference |  | 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.
…eorientation Direct tasks (#6413) ## 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 | |---|---|---|---| | #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](https://github.com/isaac-sim/IsaacLab/pull/6413/changes/f4895f0f9ee..d29afc75e71) | | #6414 Part 4/9: MARL-to-single-agent fix + handover/camera Direct |  | #6413 | [changes](https://github.com/isaac-sim/IsaacLab/pull/6414/changes/d29afc75e71..b10a84948f8) | | #6418 Part 5/9: Reorient manager counterparts |  | #6413 | [changes](https://github.com/isaac-sim/IsaacLab/pull/6418/changes/d29afc75e71..707d37f8f99) | | #6421 Part 6/9: Handover + camera manager counterparts |  | #6413, #6414, #6418 | [changes](https://github.com/isaac-sim/IsaacLab/pull/6421/changes/707d37f8f99..b50ac8906fc) | | #6410 Part 7/9: Environment overview docs |  | #6421 | [changes](https://github.com/isaac-sim/IsaacLab/pull/6410/changes/b50ac8906fc..af259c0778d) | | #6415 Part 8/9: Benchmark success-rate utilities |  | #6421 | [changes](https://github.com/isaac-sim/IsaacLab/pull/6415/changes/b50ac8906fc..c7f2f019d8b) | | #6582 Part 9/9: Warp variants → experimental (draft; merges last) |  | #6413 | [changes](https://github.com/isaac-sim/IsaacLab/pull/6582/changes/d29afc75e71..167c28578b3) | | #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` (#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 - Kit-free torch math + utils tests green; Direct state training on Newton: success rate 0.85–1.00, reward ≈3600–3900 at 2000 iterations, frame-verified policy video. Full integrated evidence: #6324. ## Review history - The earlier warp-era review rounds on this PR are superseded by the torch restack (2026-07-16); the warp implementation and its review-polished kernels live on in #6582.
648a77b to
c82f493
Compare
| ("default", ["rgb", "depth", "semantic_segmentation"]), | ||
| ("full", ["rgb", "depth", "semantic_segmentation"]), | ||
| ("rgb", ["rgb"]), | ||
| ("rgb_depth", ["rgb", "depth"]), |
There was a problem hiding this comment.
No longer applies — the rgb_depth preset was dropped and the camera config matches develop again.
| ) | ||
|
|
||
| gym.register( | ||
| id="Isaac-Reorient-Cube-Shadow-Camera-Benchmark-Direct", |
There was a problem hiding this comment.
As discussed offline, we can move benchmark to contrib. (if this is handled in a future PR please ignore)
There was a problem hiding this comment.
Taking the follow-up option — the ID is still registered unchanged; moving it needs a deprecated alias since the quickstart docs reference it.
| self.env.episode_length_buf = value | ||
|
|
||
| @property | ||
| def obs_buf(self) -> VecEnvObs: |
There was a problem hiding this comment.
It might be cleaner to do something like:
def reset(...):
obs, extras = self.env.reset(seed, options)
self._obs_buf = self._convert_observations(obs)
return self._obs_buf, extras
def step(...):
...
self._obs_buf = self._convert_observations(obs)
return self._obs_buf, ...
@property
def obs_buf(self):
return self._obs_buf
So obs buffer stays true to it's propery, instead of being a conversion mechanism
There was a problem hiding this comment.
Done in f142a22, as sketched — stored in reset()/step(), obs_buf is now a plain property. Also removes the repeated conversion greptile flagged.
| @@ -0,0 +1,82 @@ | |||
| # Copyright (c) 2022-2026, The Isaac Lab Project Developers (https://github.com/isaac-sim/IsaacLab/blob/main/CONTRIBUTORS.md). | |||
There was a problem hiding this comment.
AI Review: The PR changes RslRlVecEnvWrapper.get_observations() from actively computing observations to unconditionally reading env.obs_buf, but there is no wrapper-level regression test. Please add tests showing that get_observations() exactly matches the most recent reset and step outputs, including a noisy/non-idempotent observation case. We should also verify every supported environment family exposes obs_buf.
There was a problem hiding this comment.
There wasn't one. 41b7ed5 adds kit-free test_rsl_rl_wrapper_observations.py: the wrapper returns the env buffer, tracks updates, never calls the private method, and DirectRLEnv publishes it in reset and step. Verified to fail against the previous implementation.
| # concatenate agents' observations | ||
| @episode_length_buf.setter | ||
| def episode_length_buf(self, value: torch.Tensor) -> None: | ||
| self.env.episode_length_buf = value |
There was a problem hiding this comment.
This might need to be something like: self.env.episode_length_buf.copy_(value)? Not entirely sure though.
There was a problem hiding this comment.
Changed in 6ec4220. Rebinding left anything holding a reference to the wrapped buffer stale; the in-place write keeps them in sync. The test now covers values and buffer identity.
| import isaaclab.sim as sim_utils | ||
| from isaaclab.markers import VisualizationMarkersCfg | ||
|
|
||
| from isaaclab_assets.robots.shadow_hand import ( |
There was a problem hiding this comment.
Cleaner to write:
from isaaclab_assets.robots.shadow_hand import (
SHADOW_ACTUATED_JOINT_NAMES as ACTUATED_JOINT_NAMES,
SHADOW_FINGERTIP_BODY_NAMES as FINGERTIP_BODY_NAMES,
)
There was a problem hiding this comment.
ruff splits it back under our isort config (combine-as-imports isn't enabled), so the current shape is what the formatter produces.
| self.goal_pos = torch.zeros((self.num_envs, 3), dtype=torch.float, device=self.device) | ||
| self.goal_pos[:, :] = torch.tensor([0.0, -0.64, 0.54], device=self.device) | ||
| # goal = object default position + shared offset (mirrors HandoverCommand.__init__) | ||
| self.goal_pos[:, :] = self.object.data.default_root_pose.torch[:, :3].to(self.device) + torch.tensor( |
There was a problem hiding this comment.
Here and elsewhere, what's going on with this self.device stuff? Are the buffers on the wrong device? Everything should already be on the same device. I would remove all this .to(device) stuff, maybe add a test instead? to(device) could be expensive to always be calling in hot paths.
There was a problem hiding this comment.
Removed. Both were in __init__ rather than a hot path, but no-ops on correctly-placed data.
| static_friction=1.0, | ||
| dynamic_friction=1.0, | ||
| ), | ||
| render_interval=2, |
There was a problem hiding this comment.
here and elsewhere let's make the default for base envs be render_interval = decimation? Seems like default behaviour should be to render at every physics step?
There was a problem hiding this comment.
Restored in 4144bf8. develop had render_interval=decimation and this PR had hard-coded it to 2 — same value today, but the coupling was lost. It was the only such site.
| # The Newton hand inherits the shared actuator from ``SHADOW_HAND_NEWTON_CFG``, | ||
| # overriding only the finger gains: the catch needs a higher nominal gain | ||
| # (20.0/2.0) than reorient's base. Per-hand prim path and init pose also differ here. | ||
| newton_cfg = SHADOW_HAND_NEWTON_CFG.replace( |
There was a problem hiding this comment.
newton_mjwarp_cfg, since kamino is unknown right now
There was a problem hiding this comment.
Renamed, both sites.
| init_state=RigidObjectCfg.InitialStateCfg(pos=(0.0, -0.39, 0.54), rot=(0.0, 0.0, 0.0, 1.0)), | ||
| ) | ||
| default = physx | ||
| ovphysx = physx # OvPhysX is PhysX-based; reuse the PhysX-tuned rigid sphere |
There was a problem hiding this comment.
Comment not really necessary
Add helpers in envs/utils/marl.py (with tests) to flatten a multi-agent Direct env's per-agent action/observation dicts into single-agent tensors, wire them through DirectRLEnv and the RSL-RL vecenv wrapper, and mirror the path in the experimental warp Direct env. Lets a task authored as multi-agent train with a single-agent RL library.
Convert the two-hand Shadow Hand handover task from multi-agent (MARL) to single-agent, add an RSL-RL training configuration, shared identity in handover_common, success-rate reward metrics, and mdp helpers.
- Store the converted observations in the multi-agent-to-single-agent adapter instead of recomputing them on every obs_buf access. - Cover the latest-observation contract for both the concatenated and state-as-observation modes. - Drop redundant device transfers and a duplicated comment in the handover environment. - Rename the Newton hand config and trim an unnecessary comment.
Add kit-free checks that the wrapper reads the environment-owned observation buffer, tracks its updates, and never falls back to the private environment observation method. Also correct the handover comments: the gain override replaces the per-joint mapping, so it applies to the whole actuator group rather than the fingers alone.
1567317 to
41b7ed5
Compare
The multi-agent-to-single-agent adapter rebound the wrapped environment's episode-length buffer, so anything holding a reference to it silently went stale. Copy into the existing buffer instead.
The simulation config hard-coded the render interval to a literal that happened to match the decimation, so changing the decimation would have silently desynchronised rendering. Restore the reference.
AntoineRichard
left a comment
There was a problem hiding this comment.
Still some comment issues, but overall it looks fine
|
|
||
| # return observations, rewards, resets and extras | ||
| # store the returned buffer so RslRlVecEnvWrapper.get_observations() can read env.obs_buf | ||
| self.obs_buf = {"policy": self.torch_obs_buf.clone()} |
There was a problem hiding this comment.
I think this is coming from som rl libraries messing with the data from the simulator inplace
| @@ -258,10 +275,14 @@ class PhysicsCfg(PresetCfg): | |||
| update_data_interval=2, | |||
There was a problem hiding this comment.
Why not the update data interval to 4?
There was a problem hiding this comment.
Raised to 4 in 4fec9dd.
Every other task pairs update_data_interval with num_substeps 1:1 — all eight sites are 2/2 — and this PR raised handover's substeps to 4 without updating the interval, so 4 restores that invariant.
Validated at 1500 iterations on both 2048 and 8192 envs: reward 1341 and 1483, no NaN. The 8192 run is the scale that motivated the substep bump.
| # 4 substeps (vs the reorient task's 2): sustained ball-palm contact against the | ||
| # distal joints explodes ~0.7% of 8192 envs to NaN at 2 substeps (zero-action | ||
| # probe, 300 steps); 4 substeps shows none. |
There was a problem hiding this comment.
I think this comment could be trimmed
There was a problem hiding this comment.
Cut to two lines in 4fec9dd. Dropped the probe details (~0.7% of 8192 envs, zero-action probe, 300 steps), kept the reason for 4 substeps.
| # Newton's importer bakes the asset's native root orientation into the | ||
| # root joint (see the note on SHADOW_HAND_NEWTON_CFG.init_state), so the | ||
| # task rotation must compose with that base rotation rather than replace | ||
| # it — replacing left both palms heading 90 degrees off and the object | ||
| # never rested in the right hand. | ||
| # Composed in float64 via the shared (x, y, z, w) quaternion product, | ||
| # matching the previously used wp.quatd math bit-for-bit. |
There was a problem hiding this comment.
I think this comment could be trimmed
There was a problem hiding this comment.
Cut from 6 lines to 3 in 4fec9dd, dropping the migration note (matching the previously used wp.quatd math bit-for-bit). The reason stays: Newton's importer bakes the root orientation, so the task rotation composes rather than replaces it — otherwise both palms end up rotated 90 degrees.
| # The Newton hand inherits the shared actuator from ``SHADOW_HAND_NEWTON_CFG``, raising its | ||
| # gains: the catch needs a higher nominal gain (20.0/2.0) than reorient's base. Per-hand prim | ||
| # path and init pose also differ here. |
There was a problem hiding this comment.
comment can be trimmed
There was a problem hiding this comment.
Removed entirely in 4fec9dd — it repeated the docstring 20 lines above.
| The Newton variant reuses the shared actuator defined on ``SHADOW_HAND_NEWTON_CFG``, | ||
| raising its gains to ``20.0`` / ``2.0`` -- the smallest tested setting at which the | ||
| handover policy learns the catch (mean reward at iter 200 / 2048 envs ~27 -> ~777 vs | ||
| reorient's base gains). The scalar replaces the per-joint gain mapping, so this applies | ||
| to every joint in the group, wrists included. |
There was a problem hiding this comment.
This here are traces that could be trimmed / removed.
There was a problem hiding this comment.
Trimmed in 4fec9dd — dropped the tuning trace (mean reward at iter 200 / 2048 envs ~27 -> ~777) and kept only why the gains are raised.
Drop tuning traces and a comment that duplicated the enclosing docstring, keeping the reasons behind each setting. Raise the MJWarp data-update interval to match the substep count.
The Shadow Hand yellow-background render test arrived from develop importing the camera environment under its previous module name, which this branch renames. The merge was textually clean but left the import unresolvable, so the test process died during collection.
Needed the change to unblock. Will sync offline to followup
Review Map
develop, so its own Files changed tab is its contribution.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.
DirectMARLEnvtasks viamulti_agent_to_single_agent; the bridge dropped the latest observations from the public buffer. Fixed generally — RSL-RL observations read from the env-ownedobs_buf, stored byresetlikestepon all env bases (DirectRLEnv,DirectMARLEnv, the experimental warp base). Every MARL task + single-agent runner benefits; handover is the first consumer.handover_common, success-rate reward metrics, and a fix for its Newton construction failure (see below).isaaclab_assetsasSHADOW_HAND_NEWTON_CFG(besideSHADOW_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 to20/2for its catch.shadow_hand_direct_camera_env[_cfg]; moved cube-keypoint math to the shared, unit-testedreorient/mdp/observations.py(replacing the per-envcompute_keypointsshim); 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 vsdevelopis purely the move, and the Direct env config has no diff at all.Handover changes in three ways:
physics=physxfor the previous backend.distal_passiveactuator group is removed. It targetedrobot0_(FF|MF|RF|LF)J0— joints that do not exist on the Newton asset (its fingers are numberedJ1–J4/J5), so constructing the environment raisedNo joints found for actuator group. Thefingersgroup already drives those joints, and the comment justifying the group (USD-bakedstiffness=286/damping=57) does not apply to this asset, which authors no drive stiffness at all.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-3where the asset authors0.0002, and the per-fingerJ1/J2pair 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 theJ4knuckle-abduction joints cannot simply be added to the driven set.Notes
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
rsl_rl(frame-verified videos); its env config now matches develop's established vision env.develop(rsl_rl, 2048 envs, 1500 iterations, 4 runs per config): this branch spans 524–664 reward,developspans 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 todevelop's.developconfig cannot do at all — it fails during construction. Full evidence: [DO-NOT-MERGE][Task Clean-up] Dexterous: lumped validation branch (series reference) #6324.