Instructions to use INSAIT-Institute/LADDER-SimplerEnv-Long with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- LeRobot
How to use INSAIT-Institute/LADDER-SimplerEnv-Long with LeRobot:
# No code snippets available yet for this library. # To use this model, check the repository files and the library's documentation. # Want to help? PRs adding snippets are welcome at: # https://github.com/huggingface/huggingface.js
- Notebooks
- Google Colab
- Kaggle
File size: 6,900 Bytes
21fed8e | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 | # Copyright (c) 2026 INSAIT. MIT License (see LICENSE).
"""LADDER SimplerEnv-Long for LeRobot EnvHub: 54 long-horizon WidowX tasks on SAPIEN 3 / ManiSkill 3.
Each task is one suite of LeRobot's ``{suite: {task_id: env}}``; its 24 episodes are the 24 object
layouts LADDER defines (24 parallel envs on one GPU). See README.md.
"""
import os
import re
import sys
REPO_ID = "INSAIT-Institute/LADDER-SimplerEnv-Long"
_HERE = os.path.dirname(os.path.abspath(__file__))
if not os.path.exists(os.path.join(_HERE, "_ladder_envhub.py")):
# `make_env("<repo>")` downloads only this file: fetch the rest of the same revision.
from huggingface_hub import snapshot_download
_revision = os.path.basename(_HERE)
_HERE = snapshot_download(REPO_ID, revision=_revision if re.fullmatch(r"[0-9a-f]{40}", _revision) else None)
if _HERE not in sys.path:
sys.path.insert(0, _HERE)
from functools import partial # noqa: E402
from typing import Any # noqa: E402
import _ladder_envhub as lh # noqa: E402
EPISODES_PER_TASK = 24
# ManiSkill reads its asset directory once, when first imported; the tree is filled on the first reset.
_MANISKILL_DIR = lh.cache_dir() / "maniskill3"
os.environ.setdefault("MS_ASSET_DIR", str(_MANISKILL_DIR))
LEROBOT_SIM = lh.lerobot_sim("widowx", fps=5)
def task_names() -> list[str]:
"""The suite's task names, from LADDER's own registry."""
from ladder.components.inference.robots.simpler.composition.specs import SPECS
return sorted(spec.name for spec in SPECS)
class SimplerEnvLong(lh.LadderVectorEnv):
suite = "simpler"
robot = "widowx"
resizable = False # LADDER's SimplerEnv server renders its 480x640 camera
episodes_per_task = EPISODES_PER_TASK
fps = 5
@classmethod
def task_budget(cls, task: str) -> int:
from ladder.components.inference.robots.simpler.composition.specs import SPECS
return next(spec.max_steps for spec in SPECS if spec.name == task)
def __init__(self, *args: Any, **kwargs: Any) -> None:
super().__init__(*args, **kwargs)
self._env = None
# Once per process, before the server reports ready; the scene is built on the first reset.
if os.environ["MS_ASSET_DIR"] == str(_MANISKILL_DIR):
_stage_maniskill_assets()
_seed_sapien_physx()
_point_vulkan_at_nvidia()
def _backend_reset(self, episode_ids: list[int]) -> list[Any]:
if self._env is None:
from ladder.components.inference.robots.simpler.env_builder import ABS_CONTROL_MODE
from ladder.components.inference.robots.simpler.simpler_env import SimplerEnv, SimplerEnvConfig
# The settings of LADDER's server config, robots/simpler/configs/widowx_composition.yaml.
self._env = SimplerEnv(
SimplerEnvConfig(
env_name=f"{self.task}-v0",
robot_name="widowx",
scene_name="bridge_table_1_v1",
rgb_overlay_path="", # ManiSkill3 picks the overlay from env_name
sim_freq=500,
control_mode=ABS_CONTROL_MODE,
control_freq=5,
max_episode_steps=0, # the task's own budget, 100 steps per subgoal
raytracing=False,
n_envs=self.num_envs,
episode_layouts=EPISODES_PER_TASK,
)
)
return self._env.reset([e % EPISODES_PER_TASK for e in episode_ids])
def _backend_step(self, actions: list[Any]) -> list[Any]:
return self._env.step(actions)
def _backend_close(self) -> None:
if self._env is not None:
self._env.close()
self._env = None
def _stage_maniskill_assets() -> None:
"""ManiSkill reads one asset tree: LADDER's bridge scenes and ManiSkill's WidowX model side by side."""
merged = _MANISKILL_DIR
if not (merged / ".complete").exists():
assets, widowx = lh.fetch("simpler_assets"), lh.fetch("widowx")
(merged / "data" / "robots").mkdir(parents=True, exist_ok=True)
for entry in (assets / "data").iterdir():
link = merged / "data" / entry.name
if entry.name != "robots":
_link(link, entry)
for entry in (assets / "data" / "robots").iterdir() if (assets / "data" / "robots").is_dir() else ():
_link(merged / "data" / "robots" / entry.name, entry)
_link(merged / "data" / "robots" / "widowx", widowx / "data" / "robots" / "widowx")
(merged / ".complete").touch()
def _link(link, target) -> None:
if not link.is_symlink():
try:
link.symlink_to(target)
except FileExistsError: # another process made it
pass
def _seed_sapien_physx() -> None:
"""SAPIEN downloads its PhysX GPU library from GitHub on first use; use LADDER's pinned copy."""
import sapien.physx
library = os.path.join(os.path.expanduser("~"), ".sapien", "physx", sapien.physx.version(), "libPhysXGpu_64.so")
if not os.path.exists(library):
os.makedirs(os.path.dirname(library), exist_ok=True)
temporary = f"{library}.{os.getpid()}"
os.symlink(lh.fetch("sapien_physx") / "libPhysXGpu_64.so", temporary)
os.replace(temporary, library)
def _point_vulkan_at_nvidia() -> None:
"""SAPIEN renders with Vulkan. A container may have the NVIDIA driver but no ICD file naming it."""
if os.environ.get("VK_ICD_FILENAMES"):
return
for directory in ("/etc/vulkan/icd.d", "/usr/share/vulkan/icd.d"):
if os.path.exists(os.path.join(directory, "nvidia_icd.json")):
return
icd = lh.cache_dir() / "nvidia_icd.json"
if not icd.exists():
icd.write_text('{"file_format_version":"1.0.0","ICD":{"library_path":"libGLX_nvidia.so.0","api_version":"1.3.0"}}')
os.environ["VK_ICD_FILENAMES"] = str(icd)
def vector_env_fns(
task: str = "",
n_envs: int = EPISODES_PER_TASK,
**options: Any,
) -> dict[str, dict[int, Any]]:
"""One factory per task, ``{task: {0: factory}}``: every task, or the comma-separated ``task``.
LeRobot builds only the tasks it runs (``--env.task``), so listing all 54 costs nothing.
"""
names = lh.select_tasks(task, task_names())
if os.environ["MS_ASSET_DIR"] == str(_MANISKILL_DIR):
_stage_maniskill_assets() # download before the server reports ready
lh.fetch("sapien_physx")
return {name: {0: partial(SimplerEnvLong, name, n_envs, **options)} for name in names}
def make_env(n_envs: int = EPISODES_PER_TASK, use_async_envs: bool = False, task: str = "", **options: Any):
"""One task as a batched vector env (``--env.use_native_batch``); LeRobot otherwise uses ``vector_env_fns``."""
name = lh.one_task(task, task_names())
return {name: {0: vector_env_fns(name, n_envs, **options)[name][0]()}}
|