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
Download env.py from INSAIT-Institute/LADDER-SimplerEnv-Long: direct link, hf CLI and curl.
- Browser
- Download file 6.9 kB
-
https://huggingface.co/INSAIT-Institute/LADDER-SimplerEnv-Long/resolve/main/env.py
- Command line
-
hf download hf://INSAIT-Institute/LADDER-SimplerEnv-Long/env.py
-
curl -L -o env.py https://huggingface.co/INSAIT-Institute/LADDER-SimplerEnv-Long/resolve/main/env.py
6.9 kB
| # 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 | |
| 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]()}} | |