docs/source/envhub.mdx
The EnvHub feature allows you to load simulation environments directly from the Hugging Face Hub with a single line of code. This unlocks a powerful new model for collaboration: instead of environments being locked away inside monolithic libraries, anyone can publish custom environments and share them with the community.
EnvHub lets you create custom robotics simulation environments with your own robot models and scenarios, and make them easily usable by anyone through the LeRobot framework.
EnvHub packages are stored on the Hugging Face Hub, and can be seamlessly pulled and used in your AI robotics projects through LeRobot with a single line of code.
Thanks to EnvHub, you can:
This design means you can go from discovering an interesting environment on the Hub to running experiments in seconds, or create your own custom robot and environment without worrying about dependency conflicts or complex installation procedures.
When you create an EnvHub package, you can build anything you want inside it and use any simulation tool you like: this is your own space to play with. The only requirement is that the package contains an env.py file that defines the environment and allows LeRobot to load and use your EnvHub package.
This env.py file needs to expose a small API so LeRobot can load and run it. In particular, you must provide a make_env(n_envs: int = 1, use_async_envs: bool = False) or make_env(n_envs: int = 1, use_async_envs: bool = False, cfg: EnvConfig) function, which is the main entry point for LeRobot. It should return one of:
gym.vector.VectorEnv (most common)gym.Env (will be automatically wrapped){suite_name: {task_id: VectorEnv}} (for multi-task benchmarks)You can also pass an EnvConfig object to make_env to configure the environment (e.g. the number of environments, task, camera name, initial states, control mode, episode length, etc.).
Finally, your environment must implement the standard gym.vector.VectorEnv interface so it works with LeRobot, including methods like reset and step.
Loading an environment from the Hub is as simple as:
from lerobot.envs.factory import make_env
# Load a hub environment (requires explicit consent to run remote code)
env = make_env("lerobot/cartpole-env", trust_remote_code=True)
To make your environment loadable from the Hub, your repository must contain at minimum:
env.py (or custom Python file)
make_env(n_envs: int, use_async_envs: bool) functiongym.vector.VectorEnv (most common)gym.Env (will be automatically wrapped){suite_name: {task_id: VectorEnv}} (for multi-task benchmarks)requirements.txt
README.md
.gitignore
my-environment-repo/
├── env.py # Main environment definition (required)
├── requirements.txt # Dependencies (optional)
├── README.md # Documentation (recommended)
├── assets/ # Images, videos, etc. (optional)
│ └── demo.gif
└── configs/ # Config files if needed (optional)
└── task_config.yaml
Create an env.py file with a make_env function:
# env.py
import gymnasium as gym
def make_env(n_envs: int = 1, use_async_envs: bool = False):
"""
Create vectorized environments for your custom task.
Args:
n_envs: Number of parallel environments
use_async_envs: Whether to use AsyncVectorEnv or SyncVectorEnv
Returns:
gym.vector.VectorEnv or dict mapping suite names to vectorized envs
"""
def _make_single_env():
# Create your custom environment
return gym.make("CartPole-v1")
# Choose vector environment type
env_cls = gym.vector.AsyncVectorEnv if use_async_envs else gym.vector.SyncVectorEnv
# Create vectorized environment
vec_env = env_cls([_make_single_env for _ in range(n_envs)])
return vec_env
Before uploading, test your environment locally:
from lerobot.envs.utils import _load_module_from_path, _call_make_env, _normalize_hub_result
# Load your module
module = _load_module_from_path("./env.py")
# Test the make_env function
result = _call_make_env(module, n_envs=2, use_async_envs=False)
normalized = _normalize_hub_result(result)
# Verify it works
suite_name = next(iter(normalized))
env = normalized[suite_name][0]
obs, info = env.reset()
print(f"Observation shape: {obs.shape if hasattr(obs, 'shape') else type(obs)}")
env.close()
Upload your repository to Hugging Face:
# Install huggingface_hub if needed
pip install huggingface_hub
# Login to Hugging Face
hf auth login
# Create a new repository
hf repo create my-org/my-custom-env
# Initialize git and push
git init
git add .
git commit -m "Initial environment implementation"
git remote add origin https://huggingface.co/my-org/my-custom-env
git push -u origin main
Alternatively, use the huggingface_hub Python API:
from huggingface_hub import HfApi
api = HfApi()
# Create repository
api.create_repo("my-custom-env", repo_type="space")
# Upload files
api.upload_folder(
folder_path="./my-env-folder",
repo_id="username/my-custom-env",
repo_type="space",
)
from lerobot.envs.factory import make_env
# Load from the hub
envs_dict = make_env(
"username/my-custom-env",
n_envs=4,
trust_remote_code=True
)
# Access the environment
suite_name = next(iter(envs_dict))
env = envs_dict[suite_name][0]
# Use it like any gym environment
obs, info = env.reset()
action = env.action_space.sample()
obs, reward, terminated, truncated, info = env.step(action)
For reproducibility and security, pin to a specific Git revision:
# Pin to a specific branch
env = make_env("username/my-env@main", trust_remote_code=True)
# Pin to a specific commit (recommended for papers/experiments)
env = make_env("username/my-env@abc123def456", trust_remote_code=True)
# Pin to a tag
env = make_env("username/[email protected]", trust_remote_code=True)
If your environment definition is not in env.py:
# Load from a custom file
env = make_env("username/my-env:custom_env.py", trust_remote_code=True)
# Combine with version pinning
env = make_env("username/[email protected]:envs/task_a.py", trust_remote_code=True)
For better performance with multiple environments:
envs_dict = make_env(
"username/my-env",
n_envs=8,
use_async_envs=True, # Use AsyncVectorEnv for parallel execution
trust_remote_code=True
)
The hub URL format supports several patterns:
| Pattern | Description | Example |
|---|---|---|
user/repo | Load env.py from main branch | make_env("lerobot/pusht-env") |
user/repo@revision | Load from specific revision | make_env("lerobot/pusht-env@main") |
user/repo:path | Load custom file | make_env("lerobot/envs:pusht.py") |
user/repo@rev:path | Revision + custom file | make_env("lerobot/envs@v1:pusht.py") |
For benchmarks with multiple tasks (like LIBERO), return a nested dictionary:
def make_env(n_envs: int = 1, use_async_envs: bool = False):
env_cls = gym.vector.AsyncVectorEnv if use_async_envs else gym.vector.SyncVectorEnv
# Return dict: {suite_name: {task_id: VectorEnv}}
return {
"suite_1": {
0: env_cls([lambda: gym.make("Task1-v0") for _ in range(n_envs)]),
1: env_cls([lambda: gym.make("Task2-v0") for _ in range(n_envs)]),
},
"suite_2": {
0: env_cls([lambda: gym.make("Task3-v0") for _ in range(n_envs)]),
}
}
When loading environments from the Hub:
env.py before loadingrequirements.txt for suspicious packagesExample of safe usage:
# ❌ BAD: Loading without inspection
env = make_env("random-user/untrusted-env", trust_remote_code=True)
# ✅ GOOD: Review code, then pin to specific commit
# 1. Visit https://huggingface.co/trusted-org/verified-env
# 2. Review the env.py file
# 3. Copy the commit hash
env = make_env("trusted-org/verified-env@a1b2c3d4", trust_remote_code=True)
Here's a complete example using the reference CartPole environment:
from lerobot.envs.factory import make_env
import numpy as np
# Load the environment
envs_dict = make_env("lerobot/cartpole-env", n_envs=4, trust_remote_code=True)
# Get the vectorized environment
suite_name = next(iter(envs_dict))
env = envs_dict[suite_name][0]
# Run a simple episode
obs, info = env.reset()
done = np.zeros(env.num_envs, dtype=bool)
total_reward = np.zeros(env.num_envs)
while not done.all():
# Random policy
action = env.action_space.sample()
obs, reward, terminated, truncated, info = env.step(action)
total_reward += reward
done = terminated | truncated
print(f"Average reward: {total_reward.mean():.2f}")
env.close()
make_env APIYou must explicitly pass trust_remote_code=True:
env = make_env("user/repo", trust_remote_code=True)
The hub environment has dependencies you need to install:
# Check the repo's requirements.txt and install dependencies
pip install gymnasium numpy
Your env.py must expose a make_env function:
def make_env(n_envs: int, use_async_envs: bool):
# Your implementation
pass
The make_env function must return:
gym.vector.VectorEnv, orgym.Env, or{suite_name: {task_id: VectorEnv}}The EnvHub ecosystem enables exciting possibilities:
As more researchers and developers contribute, the diversity and quality of available environments will grow, benefiting the entire robotics learning community.