Skip to content

Add TorchRL environment wrapper for isaaclab_rl - #7502

Open
theap06 wants to merge 4 commits into
isaac-sim:developfrom
theap06:apaninga/torchrl-wrapper
Open

Add TorchRL environment wrapper for isaaclab_rl#7502
theap06 wants to merge 4 commits into
isaac-sim:developfrom
theap06:apaninga/torchrl-wrapper

Conversation

@theap06

@theap06 theap06 commented Sep 2, 2026

Copy link
Copy Markdown

Description

Adds isaaclab_rl.torchrl.IsaacLabTorchRLWrapper, a torchrl.envs.EnvBase implementation over Isaac Lab's batched environments, together with a torchrl extra, API docs, and tests.

Validated on an A100-40GB (Isaac Sim 6.0, torchrl 0.13.3, 64 envs per task):

Check Result
test_torchrl_wrapper_specs.py (kit-free) passed
test_torchrl_wrapper.py (first 5 registered tasks) 2 passed
Same checks over Ant, Ant-Direct, Cartpole, Cartpole-Direct, Cartpole-Camera, Cartpole-Camera-Direct, Humanoid-Direct, Velocity-Flat-AnymalD, Reorient-Cube-Shadow-OpenAI-FF-Direct 9/9 OK, no NaNs, terminal observation reported on every done row
Cartpole, 200 steps, vs torchrl.envs.libs.isaac_lab.IsaacLabWrapper 12.5k env-steps/s with terminal observations on done rows; upstream 9.6k (native_autoreset=True) / 10.4k (False) with NaN next-observations on done rows

@theap06
theap06 requested a review from a team September 2, 2026 23:05
@github-actions github-actions Bot added documentation Improvements or additions to documentation isaac-lab Related to Isaac Lab team infrastructure labels Sep 2, 2026
@greptile-apps

greptile-apps Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds an optional TorchRL dependency and a TorchRL EnvBase adapter for batched Isaac Lab environments, including structured specifications, terminal-observation handling, documentation, and tests.

  • Converts Gymnasium observation and action spaces into batched TorchRL specifications.
  • Preserves Isaac Lab same-step autoreset behavior and terminal observations in TorchRL rollouts.
  • Adds kit-free contract tests and simulator-backed rollout coverage.
  • The constructor currently admits non-RL manager-based environments that cannot satisfy the wrapper contract.

Confidence Score: 4/5

The PR should not merge until the constructor stops accepting non-RL ManagerBasedEnv variants or fully supports their different interface.

The new type gate promises support for two environment classes that lack the spaces and five-value step result the wrapper unconditionally requires, causing construction or stepping to fail for those accepted inputs.

Files Needing Attention: source/isaaclab_rl/isaaclab_rl/torchrl/vecenv_wrapper.py

Important Files Changed

Filename Overview
source/isaaclab_rl/isaaclab_rl/torchrl/vecenv_wrapper.py Adds the TorchRL adapter and specification conversion, but its accepted-type check includes non-RL manager-based environments that fail the required construction and step contracts.
source/isaaclab_rl/test/test_torchrl_wrapper_specs.py Thoroughly tests specifications, buffer cloning, terminal observations, reset behavior, clipping, device placement, and lifecycle using an RL-contract fake, but does not cover the newly accepted non-RL manager-based classes.
source/isaaclab_rl/test/test_torchrl_wrapper.py Adds simulator-backed rollouts validating TorchRL specifications, finite-horizon termination behavior, and terminal observations across registered RL tasks.
pyproject.toml Adds TorchRL 0.13 or newer as an isolated optional dependency.
docs/source/api/lab_rl/isaaclab_rl.rst Publishes the new TorchRL wrapper in the isaaclab_rl API documentation.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart LR
  T[TorchRL TensorDict action] --> W[IsaacLabTorchRLWrapper]
  W --> E[Isaac Lab batched RL environment]
  E --> S[Observation, reward, termination, truncation, extras]
  S --> F{Final observation available?}
  F -->|Yes| O[Use terminal observation on done rows]
  F -->|No| N[Invalidate done-row observations]
  O --> R[TorchRL next TensorDict]
  N --> R
  R --> A[Masked autoreset request]
  A --> C[Return current post-reset Isaac Lab observations]
Loading

Reviews (1): Last reviewed commit: "Add Kit-dependent integration test for t..." | Re-trigger Greptile

Comment thread source/isaaclab_rl/isaaclab_rl/torchrl/vecenv_wrapper.py Outdated

@isaaclab-review-bot isaaclab-review-bot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Isaac Lab Review Bot

Adds a direct TorchRL EnvBase wrapper with specs, auto-reset handling, optional dependency metadata, documentation, and tests. Two contract mismatches need correction: the constructor accepts non-RL manager environments that _step cannot consume, and actions are not transferred back to the simulation device when the wrapper uses a different TensorDict device.

  • Design and architecture: The direct EnvBase integration preserves batched observation groups and explicitly handles Isaac Lab’s same-step auto-reset behavior. However, the accepted-type set includes ManagerBasedEnv and its Warp counterpart even though the implementation requires the five-value RL step() contract; construction should be restricted to supported RL environment classes.
  • API: The public wrapper is exported, documented, and backed by a dedicated torchrl extra. The constructor’s runtime acceptance currently conflicts with its annotation and error message by permitting non-RL manager environments, while the documented device option implies cross-device TensorDict support that the action path does not fully implement.
  • Implementation: Spec conversion, buffer cloning, finite-horizon handling, and terminal-observation behavior are covered by focused tests. Before stepping, actions must be moved from the wrapper device to self.unwrapped.device; otherwise a non-default wrapper device produces device-mismatched simulator inputs.

Minor fixes needed. Posted 2 actionable findings inline.

Automated review; human maintainers own approval decisions.

Comment thread source/isaaclab_rl/isaaclab_rl/torchrl/vecenv_wrapper.py Outdated
if self._clip_actions is not None:
actions = torch.clamp(actions, -self._clip_actions, self._clip_actions)

obs_dict, rew, terminated, truncated, extras = self.env.step(actions)

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.

🟡 Warning · Implementation — Actions not moved to simulation device

The docstring advertises device as the device the tensordicts live on, and outputs are converted via the TensorDict(device=...) construction. Actions, however, are forwarded to self.env.step unchanged, so a wrapper constructed with a device different from self.unwrapped.device (e.g. device="cpu" over a CUDA env) hands device-mismatched tensors to the simulator. Convert actions to the environment device before stepping.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

The supported env classes already move the action to their own device at the top of step(): DirectRLEnv.step does action = action.to(self.device), ManagerBasedRLEnv.step calls process_action(action.to(self.device)), and DirectRLEnvWarp / ManagerBasedRLEnvWarp do the same before the Warp copy. A wrapper constructed with device="cpu" over a CUDA env therefore steps correctly as is, so I left this unchanged.

@theap06
theap06 force-pushed the apaninga/torchrl-wrapper branch from 6fffac0 to ebd47fc Compare September 3, 2026 00:32
IsaacLabTorchRLWrapper implements torchrl.envs.EnvBase directly on the
batched Isaac Lab environment instead of going through GymWrapper, so
the Warp-based environments work too and tensordicts stay on the
simulation device. Observation groups map to one Composite spec each,
done/terminated/truncated are separate keys, and the "next" observation
of done rows carries extras["final_obs"] when the environment captures
it. Reset requests that TorchRL issues after a step with done
environments are served from the current observations, since Isaac Lab
already reset those environments inside step().

Adds a torchrl extra, the API docs entry, and the changelog fragments.
Exercise spec conversion and the step/reset contract against a fake
environment that mimics Isaac Lab's same-step auto-reset and in-place
buffers, without launching Isaac Sim.
Mirror test_rsl_rl_wrapper.py: wrap the first five registered tasks and
run check_env_specs and random-action rollouts through them.
@theap06
theap06 force-pushed the apaninga/torchrl-wrapper branch from ebd47fc to 703180a Compare September 3, 2026 00:37
or :class:`DirectRLEnv`.
"""
# NOTE: import here (not at module level) to avoid loading heavy env classes before Isaac Sim is initialized.
from isaaclab.envs import DirectRLEnv, ManagerBasedRLEnv

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I left it here just for performance reasons; however, open to migrating this to be global

@kellyguo11

Copy link
Copy Markdown
Contributor

thanks for the PR! do you happen to have an example cfg with torch RL that can be used for testing? maybe a cartpole training configuration?

@theap06

theap06 commented Sep 3, 2026

Copy link
Copy Markdown
Author

@kellyguo11 yeah! I'm running some cartpole ppo experiments. I also wanted to ask if it makes sense to keep this as a standalone env or subclass of torchRL's IsaacLabWrapper.

@theap06

theap06 commented Sep 3, 2026

Copy link
Copy Markdown
Author

@kellyguo11 this is the PR for the IsaacLab Wrapper pytorch/rl#2937

@kellyguo11

Copy link
Copy Markdown
Contributor

I think a standalone env class would make sense so that we can align it with the Isaac Lab design more closely and will be easier for us to maintain. would be good to have the cartpole example along with the PR so that we can review the full training setup.

@theap06
theap06 requested a review from hujc7 as a code owner September 3, 2026 04:17
train_ppo builds a Gaussian MLP actor, an MLP critic (on the "critic"
observation group when the task has a flat one), TorchRL's Collector,
GAE and ClipPPOLoss, and runs the collect/update loop with TensorBoard
logging of the losses and Isaac Lab's episode statistics. Actor
checkpoints reload into make_actor. TorchRlPpoCfg is a single flat
config so task configs and Hydra overrides stay short.

Wire a torchrl backend into the unified train entrypoint (--rl_library
torchrl) and register torchrl_cfg_entry_point PPO configs on
Isaac-Cartpole and Isaac-Cartpole-Direct.
@theap06
theap06 force-pushed the apaninga/torchrl-wrapper branch from 3b89ab9 to 53ba73a Compare September 3, 2026 04:33
@theap06

theap06 commented Sep 3, 2026

Copy link
Copy Markdown
Author

@kellyguo11 ran a ppo experiment with cartpole and torchrl learns faster and its final policy balances the pole more reliably. The reward curve is more noisy later in training though.
image

@theap06

theap06 commented Sep 3, 2026

Copy link
Copy Markdown
Author

The only difference in the experiment setups which is what likely caused the noise was that RSL-RL uses adaptive kl while torchRL here just uses ppo loss. I can change that parameter setup, but the curves demonstrate that the wrapper works soundly with isaac lab

@kellyguo11 kellyguo11 moved this to In review in Isaac Lab Sep 3, 2026
@kellyguo11

Copy link
Copy Markdown
Contributor

Thanks, we'll let the team do a more thorough review of the changes.

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

AI-generated review (Codex)

This review was produced by an AI coding agent and should be validated by a human maintainer.

I would request changes before merging.

Findings

  • Important — pyproject.toml:156, test_torchrl_wrapper_specs.py:20: torchrl was added to pyproject.toml but not uv.lock or the CI environment. uv export --frozen --extra torchrl reports that the extra is undefined in the locked metadata, and both new test suites use importorskip, so standard CI skips all TorchRL coverage. Regenerate uv.lock and install the extra in the isaaclab_rl test lane.

  • Important — vecenv_wrapper.py:196, train_torchrl.py:106: converted DirectMARLEnv tasks never receive terminal observations. The MARL environment stores them under extras[agent]["final_obs"], while multi_agent_to_single_agent passes that structure unchanged and the wrapper only checks top-level extras["final_obs"]. Timeouts therefore produce NaN next-observations instead of values suitable for PPO bootstrapping. Convert per-agent terminal observations alongside normal observations and add a MARL-timeout regression test.

  • Important — vecenv_wrapper.py:141: the public device option converts outputs but not inputs. With a CUDA simulation wrapped using device="cpu", TorchRL produces CPU actions and _step forwards them directly into CUDA task code. Move actions to self.unwrapped.device before stepping and test cross-device operation.

  • Important — ppo.py:29: the exported PPO API accepts any IsaacLabTorchRLWrapper, but assumes flat continuous actions and flat "policy" observations. A (2, 3) Box produces (N, 3) actions rather than (N, 2, 3); Discrete/MultiDiscrete spaces receive Gaussian floats; Composite policy observations reach an MLP expecting a tensor. Either implement flattening/reshaping and categorical policies or reject unsupported specs explicitly.

  • Important — ppo.py:45: when a "critic" group is Composite, make_critic silently switches to "policy", discarding privileged observations. This contradicts ppo_cfg.py:33. Flatten/encode Composite critic observations or raise an unsupported-space error.

  • Important — vecenv_wrapper.py:196: extras["final_obs"] persists between steps, but the wrapper treats key presence as validity for every current done row. Visualizer-triggered resets occur after normal terminal-observation capture, so they can receive a stale terminal observation from an earlier episode. Clear/carry a validity mask per step and capture manual-reset observations before reset.

  • Important — tools/environ_docs.py:52: the generated environment browser was not refreshed. tools/update_environments_rst.py --check fails, and regeneration adds TorchRL to both Cartpole rows. The browser also lacks a torchrl extra mapping and enables playback even though dispatch.py:30 has no TorchRL play backend. Commit the generated rows, add the extra mapping, and either implement playback or make the UI/docs explicitly train-only.

  • Important — train_torchrl.py:116, ppo.py:103: KeyboardInterrupt unwinds train_ppo before the collector and TensorBoard writer are closed, then is suppressed and reported like successful completion. Other exceptions additionally bypass environment cleanup. Put collector, writer, and environment cleanup in finally blocks and report interruptions distinctly.

Validation

  • git diff --check: passed.
  • Changelog validation: passed.
  • Root pyproject tests: 11 passed.
  • Environment documentation check: failed as described.
  • TorchRL unit suite: skipped because TorchRL is absent.
  • Full simulator tests were not rerun.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation infrastructure isaac-lab Related to Isaac Lab team

Projects

Status: In review

Development

Successfully merging this pull request may close these issues.

5 participants