Summary
Implement IsaacSimulation(SimEngine) — a photorealistic, USD-native simulation backend built on NVIDIA Isaac Sim + IsaacLab 3.0. Third concrete SimEngine subclass (after MuJoCo #85 and Newton #TBD), targeted at photorealistic training data generation, teleoperation with real-hardware-fidelity scenes, and SIL/HIL pipelines (#5).
Isaac Sim's differentiating value is environment fidelity — USD scenes, Omniverse materials, PhysX GPU, and ray-traced rendering. This is the backend that powers synthetic-dataset generation pipelines (DreamGen / Replicator).
Motivation
| Capability |
MuJoCo (#85) |
Newton (#TBD) |
Isaac Sim (this issue) |
| Photorealistic rendering |
❌ |
basic OpenGL |
✅ RTX path-traced |
| USD scene format |
❌ |
partial |
✅ native |
| Omniverse material library |
❌ |
❌ |
✅ |
| Synthetic data generation |
❌ |
❌ |
✅ (Replicator) |
| IsaacLab RL environments |
❌ |
❌ |
✅ |
| Setup complexity |
low |
medium |
high |
| macOS support |
✅ |
❌ |
❌ |
| Download size |
~50 MB |
~500 MB |
~30 GB |
Isaac Sim fills the "photorealistic synthesis" slot. It is strictly opt-in — most contributors won't install it. We guard everything behind [isaac] extras and a runtime is_available() check.
Suggested release target: v0.4.0 if it fits; acceptable to slip to v0.4.1. The asset converter sub-PR can merge independently without the Isaac Sim runtime.
Design
Directory layout
strands_robots/simulation/isaac/
├── __init__.py # Export IsaacSimulation
├── simulation.py # class IsaacSimulation(SimEngine)
├── config.py # @dataclass IsaacSimConfig
├── asset_converter.py # urdf_to_usd / mjcf_to_usd (no Isaac runtime dep)
├── bridge.py # State sync bridge
├── lab/ # IsaacLab-specific helpers
│ ├── __init__.py
│ ├── env.py # IsaacLab env adapter
│ └── trainer.py # RL training wrapper
└── tests/
├── test_unit.py # No Isaac deps — runs in main CI
└── test_integ.py # @pytest.mark.isaac — nightly GPU runner
Core class
from strands_robots.simulation.base import SimEngine
from strands_robots.simulation.isaac.config import IsaacSimConfig
class IsaacSimulation(SimEngine):
"""Photorealistic USD-native simulation backend on NVIDIA Isaac Sim.
Requires Isaac Sim 5.0+, IsaacLab 3.0+, RTX GPU, and ~30 GB disk.
Imported lazily — importing this module does NOT load Isaac Sim.
"""
def __init__(self, config: IsaacSimConfig | None = None) -> None: ...
def is_available(self) -> bool:
"""Return True if Isaac Sim SDK is importable on this system."""
# --- Required SimEngine methods ---
def create_world(self, timestep=None, gravity=None, ground_plane=True) -> dict: ...
def destroy(self) -> dict: ...
def reset(self, env_ids=None) -> dict: ...
def step(self, n_steps=1) -> dict: ...
def get_state(self) -> dict: ...
def add_robot(self, name, urdf_path=None, data_config=None,
position=None, orientation=None) -> dict: ...
def remove_robot(self, name) -> dict: ...
def add_object(self, name, shape="box", **kwargs) -> dict: ...
def remove_object(self, name) -> dict: ...
def get_observation(self, robot_name=None, camera_name=None) -> dict: ...
def send_action(self, action, robot_name=None, n_substeps=1) -> None: ...
def render(self, camera_name="default", width=None, height=None) -> dict: ...
# --- Optional overrides ---
def load_scene(self, scene_path: str) -> dict: ... # USD
def run_policy(self, robot_name, policy_provider="gr00t", **kwargs) -> dict: ...
def get_contacts(self) -> dict: ...
# --- Isaac-specific extensions ---
def add_terrain(self, terrain_type="flat_plane", **kwargs) -> dict: ...
def set_joint_positions(self, robot_name, positions: dict) -> dict: ...
def get_contact_forces(self, robot_name) -> dict: ...
def set_camera_pose(self, camera_name, position, target) -> dict: ...
def record_video(self, output_path, duration=10.0, fps=30) -> dict: ...
def _resolve_usd(self, data_config: str) -> Path | None:
"""Resolve robot name to on-disk USD path."""
IsaacSimConfig
@dataclass
class IsaacSimConfig:
headless: bool = True # Set False for GUI dev
device: str = "cuda:0"
physics_dt: float = 1.0 / 60.0
rendering_dt: float = 1.0 / 30.0
enable_livestream: bool = False # Omniverse streaming
isaac_sim_path: str | None = None # Override auto-detect
replicator_enabled: bool = False # For synthetic data
rtx_mode: str = "path_traced" # path_traced | real_time | pbr
Definition of Done
Functional
Advanced
Asset converter
Registry + API
Quality
Documentation
Non-goals
- Full Omniverse integration — standalone Isaac Sim only
- Live-stream to Omniverse Cloud (post-v0.5)
- Custom USD material authoring — users bring their own USD
- Apple Silicon / Windows (Linux only; Isaac Sim requirement)
- Distributed multi-machine (single machine only)
Implementation plan (5 PRs)
feat(isaac): stub IsaacSimulation(SimEngine) + registry — skeleton with is_available(), all methods raise NotImplementedError. Works without Isaac Sim installed. ~200 LOC.
feat(isaac): asset converter (urdf_to_usd / mjcf_to_usd) — no Isaac Sim runtime dependency. Mergeable independently. ~1,500 LOC.
feat(isaac): world lifecycle + robot loading — requires Isaac Sim runtime. Integration test. ~800 LOC.
feat(isaac): observation/action/rendering — obs, actions, camera, RTX render. ~600 LOC.
feat(isaac): teleop + terrain + contact forces — advanced extensions. ~500 LOC.
Total estimated: ~3.6K LOC + ~1.5K tests + ~1K docs.
Risks & mitigations
| Risk |
Severity |
Mitigation |
| Isaac Sim SDK version churn (5.0 → 6.0) |
HIGH |
Pin isaacsim==5.* in extras; test matrix |
| IsaacLab API churn (#68 was an example) |
HIGH |
Abstract behind internal adapter; flag breakages fast |
| 30 GB download scares contributors |
MEDIUM |
Strictly opt-in via [isaac] extras; never import by default |
| USD asset licensing |
MEDIUM |
Ship converters, not pre-converted assets; MIT/Apache-licensed examples only |
| CI cost (GPU runner required) |
MEDIUM |
@pytest.mark.isaac nightly workflow, not per-PR |
Acceptance test
# Requires Isaac Sim 5.0+ + IsaacLab 3.0+
python -c "
from strands_robots.simulation import create_simulation
sim = create_simulation('isaac', headless=True, rtx_mode='real_time')
assert sim.is_available()
sim.create_world()
sim.add_robot('so100')
sim.add_object('cube', shape='box', position=[0.3, 0, 0.05], color=[1,0,0,1])
for _ in range(60):
obs = sim.get_observation('so100', camera_name='wrist')
print('camera image:', obs['image'].shape)
sim.step(1)
sim.record_video('output.mp4', duration=5)
sim.destroy()
"
Related
Summary
Implement
IsaacSimulation(SimEngine)— a photorealistic, USD-native simulation backend built on NVIDIA Isaac Sim + IsaacLab 3.0. Third concreteSimEnginesubclass (after MuJoCo #85 and Newton #TBD), targeted at photorealistic training data generation, teleoperation with real-hardware-fidelity scenes, and SIL/HIL pipelines (#5).Isaac Sim's differentiating value is environment fidelity — USD scenes, Omniverse materials, PhysX GPU, and ray-traced rendering. This is the backend that powers synthetic-dataset generation pipelines (DreamGen / Replicator).
Motivation
Isaac Sim fills the "photorealistic synthesis" slot. It is strictly opt-in — most contributors won't install it. We guard everything behind
[isaac]extras and a runtimeis_available()check.Suggested release target: v0.4.0 if it fits; acceptable to slip to v0.4.1. The asset converter sub-PR can merge independently without the Isaac Sim runtime.
Design
Directory layout
Core class
IsaacSimConfigDefinition of Done
Functional
create_simulation("isaac")returnsIsaacSimulationwhen Isaac Sim is installedcreate_simulation("isaac")raises a helpful error with install URL when missingsim.is_available()returns bool safely at runtimesim.add_robot("so100", data_config="so100")finds and loads USDsim.add_robot("unitree_g1")works with packaged USDsim.step(100)stablesim.get_observation("so100")returns joint + camera dictsim.render()returns RGB frame (path-traced or real-time per config)set_joint_positions→ stepAdvanced
sim.load_scene("path/to/scene.usd")works with IsaacLab environmentssim.add_terrain("rough", **params)generates procedural terrainsim.get_contact_forces("g1")returns per-link force vectorssim.record_video(output="demo.mp4", duration=10)works end-to-endAsset converter
urdf_to_usd(urdf_path, out_path)works for so100, g1, go2mjcf_to_usd(mjcf_path, out_path)works for MuJoCo Menagerie assets~/.cache/strands-robots/usd/Registry + API
factory._BUILTIN_BACKENDS["isaac"] = ("strands_robots.simulation.isaac.simulation", "IsaacSimulation")"isaac_sim","isaacsim","nvidia"Robot("so100", mode="sim", backend="isaac")worksQuality
pip install strands-robots[isaac]resolves (pinned Isaac Sim deps via extras)import strands_robotsmust NOT load Isaac Sim@pytest.mark.isaacintegration suite on dedicated GPU+Isaac CI runner (nightly)Documentation
docs/backends/isaac.md— installation, IsaacLab setup, troubleshootingexamples/isaac_photo_realistic_so100.py— grasp demo with RTXexamples/isaac_teleop_g1.py— joystick-driven humanoid teleopexamples/isaac_synthetic_dataset.py— Replicator-based data genNon-goals
Implementation plan (5 PRs)
feat(isaac): stub IsaacSimulation(SimEngine) + registry— skeleton withis_available(), all methods raiseNotImplementedError. Works without Isaac Sim installed. ~200 LOC.feat(isaac): asset converter (urdf_to_usd / mjcf_to_usd)— no Isaac Sim runtime dependency. Mergeable independently. ~1,500 LOC.feat(isaac): world lifecycle + robot loading— requires Isaac Sim runtime. Integration test. ~800 LOC.feat(isaac): observation/action/rendering— obs, actions, camera, RTX render. ~600 LOC.feat(isaac): teleop + terrain + contact forces— advanced extensions. ~500 LOC.Total estimated: ~3.6K LOC + ~1.5K tests + ~1K docs.
Risks & mitigations
isaacsim==5.*in extras; test matrix[isaac]extras; never import by default@pytest.mark.isaacnightly workflow, not per-PRAcceptance test
Related