mirror of
https://github.com/huggingface/lerobot.git
synced 2026-05-11 14:49:43 +00:00
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1ec9392bcb | |||
| 84b34ae75c | |||
| ff267c772b | |||
| 652b1b854d | |||
| 8831b3c47b |
+77
-3
@@ -55,7 +55,8 @@ To make your environment loadable from the Hub, your repository must contain at
|
||||
|
||||
**`env.py`** (or custom Python file)
|
||||
|
||||
- Must expose a `make_env(n_envs: int, use_async_envs: bool)` function
|
||||
- Must expose a `make_env(n_envs: int, use_async_envs: bool, **kwargs)` function
|
||||
- The function should accept `**kwargs` to allow users to pass custom configurations
|
||||
- This function should return one of:
|
||||
- A `gym.vector.VectorEnv` (most common)
|
||||
- A single `gym.Env` (will be automatically wrapped)
|
||||
@@ -99,6 +100,8 @@ Create an `env.py` file with a `make_env` function:
|
||||
```python
|
||||
# env.py
|
||||
import gymnasium as gym
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
def make_env(n_envs: int = 1, use_async_envs: bool = False):
|
||||
"""
|
||||
@@ -250,6 +253,76 @@ envs_dict = make_env(
|
||||
)
|
||||
```
|
||||
|
||||
### Custom Configuration via kwargs
|
||||
|
||||
Hub environments can accept custom configurations through keyword arguments. This is useful for parameterizing tasks, loading different objects, or overriding default settings:
|
||||
|
||||
```python
|
||||
from pathlib import Path
|
||||
|
||||
# Pass a config file path
|
||||
envs_dict = make_env(
|
||||
"nvkartik/isaaclab-arena-envs:envs/microwave_g1.py",
|
||||
n_envs=4,
|
||||
trust_remote_code=True,
|
||||
config_path=Path("/path/to/my_config.yaml"),
|
||||
)
|
||||
|
||||
# Pass config overrides as a dictionary
|
||||
envs_dict = make_env(
|
||||
"nvkartik/isaaclab-arena-envs:envs/microwave_g1.py",
|
||||
n_envs=4,
|
||||
trust_remote_code=True,
|
||||
config_overrides={
|
||||
"scene.object": "microwave",
|
||||
"sim.dt": 0.01,
|
||||
},
|
||||
)
|
||||
|
||||
# Combine config path with overrides
|
||||
envs_dict = make_env(
|
||||
"username/my-env",
|
||||
n_envs=4,
|
||||
trust_remote_code=True,
|
||||
config_path="configs/gr1_pick_place.yaml",
|
||||
config_overrides={"scene.table_objects": ["apple", "banana", "cup"]},
|
||||
)
|
||||
```
|
||||
|
||||
Any keyword arguments you pass will be forwarded to the hub environment's `make_env` function. Check the environment's documentation for supported configuration options.
|
||||
|
||||
### Using Custom kwargs with lerobot-eval
|
||||
|
||||
When evaluating policies using the `lerobot-eval` CLI, you can pass custom kwargs to hub environments using the `--env_kwargs.` prefix:
|
||||
|
||||
```bash
|
||||
lerobot-eval \
|
||||
--policy.path=user123/example-policy-checkpoint \
|
||||
--env=user123/example-sim-backend \
|
||||
--eval.batch_size=1 \
|
||||
--eval.n_episodes=10 \
|
||||
--env_kwargs.task_id=demo_task_alpha \
|
||||
--env_kwargs.agent_profile=arm_v2 \
|
||||
--env_kwargs.target_item=object_red \
|
||||
--env_kwargs.run_mode=offscreen \
|
||||
--env_kwargs.enable_sensors=true \
|
||||
--env_kwargs.record_output=true \
|
||||
--env_kwargs.output_horizon=10 \
|
||||
--env_kwargs.output_stride=15 \
|
||||
--env_kwargs.state_features=joint_angles \
|
||||
--env_kwargs.visual_streams=front_camera
|
||||
```
|
||||
|
||||
All `--env_kwargs.*` arguments will be collected into a dictionary and passed as keyword arguments to the hub environment's `make_env` function. This allows you to:
|
||||
|
||||
- Pass configuration file paths
|
||||
- Override default settings
|
||||
- Specify custom task parameters
|
||||
- Control simulation options (headless mode, camera settings, etc.)
|
||||
- Select different embodiments or objects
|
||||
|
||||
The hub environment's `make_env` function receives these as regular keyword arguments, so check the environment's documentation for the available options.
|
||||
|
||||
## URL Format Reference
|
||||
|
||||
The hub URL format supports several patterns:
|
||||
@@ -266,7 +339,7 @@ The hub URL format supports several patterns:
|
||||
For benchmarks with multiple tasks (like LIBERO), return a nested dictionary:
|
||||
|
||||
```python
|
||||
def make_env(n_envs: int = 1, use_async_envs: bool = False):
|
||||
def make_env(n_envs: int = 1, use_async_envs: bool = False, **kwargs):
|
||||
env_cls = gym.vector.AsyncVectorEnv if use_async_envs else gym.vector.SyncVectorEnv
|
||||
|
||||
# Return dict: {suite_name: {task_id: VectorEnv}}
|
||||
@@ -388,8 +461,9 @@ pip install gymnasium numpy
|
||||
Your `env.py` must expose a `make_env` function:
|
||||
|
||||
```python
|
||||
def make_env(n_envs: int, use_async_envs: bool):
|
||||
def make_env(n_envs: int, use_async_envs: bool, **kwargs):
|
||||
# Your implementation
|
||||
# kwargs can include config_path, config_overrides, etc.
|
||||
pass
|
||||
```
|
||||
|
||||
|
||||
@@ -38,6 +38,8 @@ class EvalPipelineConfig:
|
||||
seed: int | None = 1000
|
||||
# Rename map for the observation to override the image and state keys
|
||||
rename_map: dict[str, str] = field(default_factory=dict)
|
||||
# Additional kwargs to pass to hub environments (e.g., config_path, config_overrides, custom params)
|
||||
env_kwargs: dict = field(default_factory=dict)
|
||||
# Explicit consent to execute remote code from the Hub (required for hub environments).
|
||||
trust_remote_code: bool = False
|
||||
|
||||
|
||||
@@ -105,6 +105,7 @@ def make_env(
|
||||
use_async_envs: bool = False,
|
||||
hub_cache_dir: str | None = None,
|
||||
trust_remote_code: bool = False,
|
||||
**kwargs,
|
||||
) -> dict[str, dict[int, gym.vector.VectorEnv]]:
|
||||
"""Makes a gym vector environment according to the config or Hub reference.
|
||||
|
||||
@@ -118,6 +119,9 @@ def make_env(
|
||||
hub_cache_dir (str | None): Optional cache path for downloaded hub files.
|
||||
trust_remote_code (bool): **Explicit consent** to execute remote code from the Hub.
|
||||
Default False — must be set to True to import/exec hub `env.py`.
|
||||
**kwargs: Additional keyword arguments passed to the hub environment's `make_env` function.
|
||||
Useful for passing custom configurations like `config_path`, `config_overrides`, etc.
|
||||
|
||||
Raises:
|
||||
ValueError: if n_envs < 1
|
||||
ModuleNotFoundError: If the requested env package is not installed
|
||||
@@ -149,9 +153,11 @@ def make_env(
|
||||
# import and surface clear import errors
|
||||
module = _import_hub_module(local_file, repo_id)
|
||||
|
||||
# call the hub-provided make_env
|
||||
# call the hub-provided make_env with any additional kwargs
|
||||
env_cfg = None if isinstance(cfg, str) else cfg
|
||||
raw_result = _call_make_env(module, n_envs=n_envs, use_async_envs=use_async_envs, cfg=env_cfg)
|
||||
raw_result = _call_make_env(
|
||||
module, n_envs=n_envs, use_async_envs=use_async_envs, cfg=env_cfg, **kwargs
|
||||
)
|
||||
|
||||
# normalize the return into {suite: {task_id: vec_env}}
|
||||
return _normalize_hub_result(raw_result)
|
||||
|
||||
@@ -311,20 +311,27 @@ def _import_hub_module(local_file: str, repo_id: str) -> Any:
|
||||
return module
|
||||
|
||||
|
||||
def _call_make_env(module: Any, n_envs: int, use_async_envs: bool, cfg: EnvConfig | None) -> Any:
|
||||
def _call_make_env(module: Any, n_envs: int, use_async_envs: bool, cfg: EnvConfig | None, **kwargs) -> Any:
|
||||
"""
|
||||
Ensure module exposes make_env and call it.
|
||||
Ensure module exposes make_env and call it with any additional kwargs.
|
||||
|
||||
Args:
|
||||
module: The imported hub module containing make_env.
|
||||
n_envs: Number of parallel environments.
|
||||
use_async_envs: Whether to use AsyncVectorEnv or SyncVectorEnv.
|
||||
**kwargs: Additional keyword arguments to pass to the hub's make_env function.
|
||||
Common examples include config_path, config_overrides, etc.
|
||||
"""
|
||||
if not hasattr(module, "make_env"):
|
||||
raise AttributeError(
|
||||
f"The hub module {getattr(module, '__name__', 'hub_module')} must expose `make_env(n_envs=int, use_async_envs=bool)`."
|
||||
f"The hub module {getattr(module, '__name__', 'hub_module')} must expose `make_env(n_envs=int, use_async_envs=bool, **kwargs)`."
|
||||
)
|
||||
entry_fn = module.make_env
|
||||
# Only pass cfg if it's not None (i.e., when an EnvConfig was provided, not a string hub ID)
|
||||
if cfg is not None:
|
||||
return entry_fn(n_envs=n_envs, use_async_envs=use_async_envs, cfg=cfg)
|
||||
return entry_fn(n_envs=n_envs, use_async_envs=use_async_envs, cfg=cfg, **kwargs)
|
||||
else:
|
||||
return entry_fn(n_envs=n_envs, use_async_envs=use_async_envs)
|
||||
return entry_fn(n_envs=n_envs, use_async_envs=use_async_envs, **kwargs)
|
||||
|
||||
|
||||
def _normalize_hub_result(result: Any) -> dict[str, dict[int, gym.vector.VectorEnv]]:
|
||||
|
||||
@@ -43,6 +43,17 @@ lerobot-eval \
|
||||
|
||||
Note that in both examples, the repo/folder should contain at least `config.json` and `model.safetensors` files.
|
||||
|
||||
You can also evaluate a model on a Hub environment with custom kwargs:
|
||||
```
|
||||
lerobot-eval \
|
||||
--policy.path=HF_USER/HF_REPO \
|
||||
--env=HF_USER/HF_REPO \
|
||||
--eval.batch_size=1 \
|
||||
--eval.n_episodes=10 \
|
||||
--env_kwargs.environment=env_A \
|
||||
--env_kwargs.embodiment=emb_B \
|
||||
```
|
||||
|
||||
You can learn about the CLI options for this script in the `EvalPipelineConfig` in lerobot/configs/eval.py
|
||||
"""
|
||||
|
||||
@@ -521,6 +532,7 @@ def eval_main(cfg: EvalPipelineConfig):
|
||||
n_envs=cfg.eval.batch_size,
|
||||
use_async_envs=cfg.eval.use_async_envs,
|
||||
trust_remote_code=cfg.trust_remote_code,
|
||||
**cfg.env_kwargs,
|
||||
)
|
||||
|
||||
logging.info("Making policy.")
|
||||
|
||||
@@ -266,3 +266,65 @@ def test_make_env_from_hub_async():
|
||||
|
||||
# clean up
|
||||
env.close()
|
||||
|
||||
|
||||
def test_make_env_from_hub_with_kwargs():
|
||||
"""Test that kwargs are correctly passed to hub environment's make_env."""
|
||||
hub_id = "lerobot/dummy-hub-env"
|
||||
|
||||
# Test with config_path kwarg
|
||||
envs_dict = make_env(
|
||||
hub_id,
|
||||
n_envs=1,
|
||||
trust_remote_code=True,
|
||||
config_path="/path/to/config.yaml",
|
||||
)
|
||||
env = envs_dict["cartpole_suite"][0]
|
||||
|
||||
assert hasattr(env, "hub_config")
|
||||
assert env.hub_config["config_path"] == "/path/to/config.yaml"
|
||||
env.close()
|
||||
|
||||
# Test with config_overrides dict
|
||||
envs_dict = make_env(
|
||||
hub_id,
|
||||
n_envs=1,
|
||||
trust_remote_code=True,
|
||||
config_overrides={"scene.object": "microwave", "sim.dt": 0.01},
|
||||
)
|
||||
env = envs_dict["cartpole_suite"][0]
|
||||
|
||||
assert env.hub_config["config_overrides"]["scene.object"] == "microwave"
|
||||
assert env.hub_config["config_overrides"]["sim.dt"] == 0.01
|
||||
env.close()
|
||||
|
||||
# Test with arbitrary extra kwargs
|
||||
envs_dict = make_env(
|
||||
hub_id,
|
||||
n_envs=1,
|
||||
trust_remote_code=True,
|
||||
custom_param="value",
|
||||
another_param=42,
|
||||
)
|
||||
env = envs_dict["cartpole_suite"][0]
|
||||
|
||||
assert env.hub_config["extra_kwargs"]["custom_param"] == "value"
|
||||
assert env.hub_config["extra_kwargs"]["another_param"] == 42
|
||||
env.close()
|
||||
|
||||
# Test combining config_path, config_overrides, and extra kwargs
|
||||
envs_dict = make_env(
|
||||
hub_id,
|
||||
n_envs=2,
|
||||
trust_remote_code=True,
|
||||
config_path="my_config.yaml",
|
||||
config_overrides={"robot": "gr1"},
|
||||
task_name="pick_and_place",
|
||||
)
|
||||
env = envs_dict["cartpole_suite"][0]
|
||||
|
||||
assert env.hub_config["config_path"] == "my_config.yaml"
|
||||
assert env.hub_config["config_overrides"]["robot"] == "gr1"
|
||||
assert env.hub_config["extra_kwargs"]["task_name"] == "pick_and_place"
|
||||
assert env.num_envs == 2
|
||||
env.close()
|
||||
|
||||
Reference in New Issue
Block a user