--- license: apache-2.0 library_name: lerobot tags: - robotics - lerobot - envhub - gymnasium - mujoco - so-101 - so-100 - simulation --- # SO101-Nexus environments Twelve SO-101 manipulation environments, loadable through [LeRobot EnvHub](https://huggingface.co/docs/lerobot/en/envhub). Six tasks on a CPU MuJoCo backend and the same six on a GPU-batched MuJoCo Warp backend, from [so101-nexus](https://github.com/johnsutor/so101-nexus). ```python from lerobot.envs.factory import make_env envs = make_env( "johnsutor/so101-nexus-envs:envs/MuJoCoPickLift-v1.py", n_envs=4, trust_remote_code=True, ) env = envs["MuJoCoPickLift-v1"][0] obs, info = env.reset(seed=0) ``` Loading `env.py` at the repository root gives `MuJoCoPickLift-v1`. Every other environment has its own file under `envs/`. ## Install ```bash pip install "so101-nexus>=0.5.0" # MuJoCo* environments pip install "so101-nexus[warp]>=0.5.0" # adds the Warp* environments (CUDA) ``` The environment code is the installed library, not this repository: these files are thin shims over `so101_nexus.envhub`, so the physics, rewards, and observation layouts are versioned and tested with the package. ## Environments | File | Task | Steps | State dim | Backend | | ----------------------------- | ----------------------------------- | ----- | --------- | ------- | | `envs/MuJoCoTouch-v1.py` | Touch the target object | 512 | 31 | MuJoCo | | `envs/MuJoCoLookAt-v1.py` | Point the wrist camera at an object | 256 | 23 | MuJoCo | | `envs/MuJoCoMove-v1.py` | Move the end-effector a set offset | 256 | 22 | MuJoCo | | `envs/MuJoCoPickLift-v1.py` | Grasp and lift an object | 1024 | 31 | MuJoCo | | `envs/MuJoCoPickAndPlace-v1.py` | Place an object on a goal disc | 1024 | 43 | MuJoCo | | `envs/MuJoCoStackCube-v1.py` | Stack one cube on another | 1024 | 43 | MuJoCo | | `envs/WarpTouch-v1.py` | Touch the target object | 512 | 31 | Warp | | `envs/WarpLookAt-v1.py` | Point the wrist camera at an object | 256 | 23 | Warp | | `envs/WarpMove-v1.py` | Move the end-effector a set offset | 256 | 22 | Warp | | `envs/WarpPickLift-v1.py` | Grasp and lift an object | 1024 | 31 | Warp | | `envs/WarpPickAndPlace-v1.py` | Place an object on a goal disc | 1024 | 43 | Warp | | `envs/WarpStackCube-v1.py` | Stack one cube on another | 1024 | 43 | Warp | State dimensions are the default observation layout; they change with the `observations` component list. Task semantics are identical across the two backends. The MuJoCo backend builds `n_envs` independent copies, stepped one after another in the calling process by default or in worker processes with `use_async_envs=True`; the Warp backend runs `n_envs` worlds inside one batched simulator. ## Observations and actions `obs_type="state"` (the default) returns: - `agent_pos`: `(n_envs, 6)` joint positions, in radians - `environment_state`: `(n_envs, state_dim)` full state vector `obs_type="pixels_agent_pos"` returns: - `agent_pos`: `(n_envs, 6)` joint positions, in radians - `pixels`: `{"wrist": ..., "overhead": ...}`, HWC uint8 images `pixels_agent_pos` carries no `environment_state`: the full task state stays in `info["privileged_state"]`, the privileged half of the asymmetric actor-critic split, so a pixels policy cannot read it out of its observation. LeRobot's `preprocess_observation` maps these to `observation.state`, `observation.environment_state`, and `observation.images.`. The language instruction for the current episode is read off `task_description`, and success is reported in `info["final_info"]["is_success"]` on the terminating step. Actions are `(n_envs, 6)` absolute joint targets in radians by default (`control_mode="pd_joint_pos"`). Delta joint modes and end-effector modes (`pd_joint_delta_pos`, `pd_ee_pose`, `pd_ee_delta_pose`) are selectable per environment. Units are the simulator's own. Datasets recorded through the library's LeRobot follower adapter store LeRobot motor units instead (degrees, with the gripper in `RANGE_0_100`); convert with `so101_nexus.dataset_row_to_sim_qpos`. ## Configuration A `HubEnvConfig` selects the environment through its `task` field: ```python from lerobot.envs.factory import make_env from lerobot.envs.configs import HubEnvConfig cfg = HubEnvConfig(hub_path="johnsutor/so101-nexus-envs", task="MuJoCoStackCube-v1") envs = make_env(cfg, n_envs=2, trust_remote_code=True) ``` A file under `envs/` pins its own id, so `task` is ignored when the hub path names one; select by `task` through the root `env.py`. `obs_type`, `observation_width`, `observation_height`, `episode_length` and `disable_env_checker` are read off the config too when it carries them (LeRobot's `LiberoEnv` carries the first four), as is a free-form `kwargs` dict (`IsaaclabArenaEnv` carries one). For the full option set without a config class, call the library entry point directly: ```python from so101_nexus.envhub import make_env envs = make_env( n_envs=2, env_id="MuJoCoStackCube-v1", obs_type="pixels_agent_pos", observation_width=224, observation_height=224, episode_length=300, control_mode="pd_joint_delta_pos", render_mode="rgb_array", ) ``` Recognized options: `env_id`, `obs_type`, `observation_width`, `observation_height`, `episode_length`, `control_mode`, `render_mode`, `disable_env_checker`, `device` (Warp only), and `config` (a fully built `so101_nexus` environment config, which overrides `obs_type` and the camera resolution). ## Notes on the Warp backend The Warp environments are natively batched on one device and speak torch tensors. The EnvHub adapter converts to NumPy at the boundary because that is what LeRobot's rollout consumes, which copies each observation to host memory every step. For GPU-resident training loops, use `gymnasium.make_vec("WarpPickLift-v1", num_envs=...)` directly. They also seed one generator for the whole batch, so a per-world seed list collapses to its first entry. `use_async_envs` is ignored here. On the MuJoCo ids it is honored, but LeRobot's own rollout indexes `VectorEnv.envs`, which Gymnasium's `AsyncVectorEnv` does not expose, so leave it off whenever LeRobot drives the environment. ## Links - Source: https://github.com/johnsutor/so101-nexus - Documentation: https://so101-nexus.com/docs - License: Apache-2.0