diff --git a/notebooks/getting_started.ipynb b/notebooks/getting_started.ipynb
index 46139eef4a..52042db59b 100644
--- a/notebooks/getting_started.ipynb
+++ b/notebooks/getting_started.ipynb
@@ -58,7 +58,7 @@
"source": [
"## Discovering Available Environments\n",
"\n",
- "RoboDisco provides 15 PyBullet-based robotic manipulation environments.\n",
+ "RoboDisco provides 22 PyBullet-based robotic manipulation environments.\n",
"Let's register them all and see what's available."
]
},
diff --git a/predicators/envs/README.md b/predicators/envs/README.md
index d461aec15f..c9ded6402e 100644
--- a/predicators/envs/README.md
+++ b/predicators/envs/README.md
@@ -1,15 +1,13 @@
# PyBullet Environments
-**RoboDisco** (Robot Model Discovery Benchmark) — a robotic
-world-model learning and causal-discovery suite. The envs ship as part
-of the [predicators](../../README.md) repository and are exposed
-through a standard [Gymnasium](https://gymnasium.farama.org/) API.
+**RoboDisco** (Robot Model Discovery Benchmark) is a suite of 22 environments for robotic world-model learning and causal discovery.
+The envs ship as part of the [predicators](../../README.md) repository and are exposed through a standard [Gymnasium](https://gymnasium.farama.org/) API.
🌐 **Project page:**
-Each environment features a Fetch or Panda robot interacting with objects
-on a tabletop. The same envs are used by predicators' planning research
-code and can be consumed independently of the planner.
+Each environment features a Fetch or Panda robot interacting with objects on a tabletop.
+Five of them (Balloons, Boil, Bridge, Domino and Fan) are the benchmark domains of the EMPIRIC experiments; their benchmark settings live in [`scripts/configs/empiric/envs.yaml`](../../scripts/configs/empiric/envs.yaml).
+The same envs are used by predicators' planning research code and can be consumed independently of the planner.
## Installation
@@ -19,9 +17,8 @@ From the repo root:
pip install -e .
```
-This installs the agent solvers and the RoboDisco envs together. The
-package is slightly heavy because it bundles both — a lighter
-envs-only install is future work.
+This installs the agent solvers and the RoboDisco envs together.
+The package is slightly heavy because it bundles both; a lighter envs-only install is future work.
## Quick Start (Gymnasium API)
@@ -49,64 +46,83 @@ env.close()
The Gymnasium wrapper exposes:
-- `obs`: a 1-D `float32` numpy array of object features (PyBullet body ids
- and other `sim_features` are excluded).
-- `action_space`: the underlying robot's joint action space, as a
- `gymnasium.spaces.Box`.
+- `obs`: a 1-D `float32` numpy array of object features (PyBullet body ids and other `sim_features` are excluded).
+ Its layout comes from the objects of the first training task, so objects that only appear in other tasks (the BusyBoard and Laser test tasks add some) are missing from `obs`; read them from `info["state"]`.
+- `action_space`: the underlying robot's joint action space, as a `gymnasium.spaces.Box`.
- `reward`: `1.0` when all goal predicates are satisfied, `0.0` otherwise.
- `terminated`: `True` when the goal is reached.
- `truncated`: `True` when the episode hits the 500-step limit.
-- `info["state"]`: the full object-centric `predicators.structs.State`
- for the current step (predicates, types, sim state).
+- `info["state"]`: the full object-centric `predicators.structs.State` for the current step (predicates, types, sim state).
- `info["goal_reached"]`: shortcut for `env.goal_reached()`.
+- `env.reset(options={"train_or_test": "test", "task_idx": 0})` selects the task to play (training task 0 by default), and `env.reset(seed=s)` reseeds the environment.
## Walkthroughs
-- **Notebook:** [`notebooks/getting_started.ipynb`](../../notebooks/getting_started.ipynb)
- — interactive walkthrough with rendering.
-- **Smoke script:** [`scripts/robodisco_getting_started.py`](../../scripts/robodisco_getting_started.py)
- — non-interactive smoke test that mirrors the notebook and resets every
- env to verify installation health.
+- **Notebook:** [`notebooks/getting_started.ipynb`](../../notebooks/getting_started.ipynb), an interactive walkthrough with rendering.
+- **Smoke script:** [`scripts/robodisco_getting_started.py`](../../scripts/robodisco_getting_started.py), a non-interactive smoke test that mirrors the notebook and resets every env to verify installation health.
## Environments
Status legend:
-- **Tasks** — the env's task generator produces multiple init states and goals (✅) versus only a single fixed configuration (❌).
-- **Skills** — `predicators/ground_truth_models//options.py` exposes a non-empty set of primitive options (✅) versus an empty set or no factory (❌).
-- **Demos** — `python predicators/main.py --env --approach oracle --seed 0 --num_train_tasks 1 --num_test_tasks 1 --timeout 60` solves the test task end-to-end (✅) versus failing during planning, execution, or sampler grounding (❌).
+- **Preview** - one task being solved, by planning with ground-truth models or by a scripted skill sequence, from the [RoboDisco site](https://yichao-liang.github.io/robodisco-site/); click it for the environment's page.
+- **Tasks** - the task generator varies the initial state across tasks (✅) or starts every task from one layout (❌).
+- **Skills** - `predicators/ground_truth_models//options.py` builds a non-empty skill library (✅) or none (❌); see [Skill libraries](#skill-libraries).
+- **Oracle** - the approach that solves the test task with `python predicators/main.py --env --approach --seed 0 --num_train_tasks 1 --num_test_tasks 1 --timeout 60` in the default configuration: `oracle` (bilevel planning with ground-truth operators and samplers) or `oracle_process_planning` (planning with ground-truth process models); ❌ when neither does.
+- Names in **bold** are the five benchmark domains.
-| Environment | Preview | Description | Tasks | Skills | Demos |
+| Environment | Preview | Description | Tasks | Skills | Oracle |
|---|---|---|:---:|:---:|:---:|
-| `robodisco/Ants-v0` |  | Place food items near ants on a tabletop | ❌ | ✅ | ❌ |
-| `robodisco/Balance-v0` |  | Balance blocks on a beam by pressing buttons | ✅ | ✅ | ❌ |
-| `robodisco/Barrier-v0` |  | Move blocks past barriers to target locations | ❌ | ❌ | ❌ |
-| `robodisco/Blocks-v0` |  | Stack and arrange blocks on a table | ✅ | ✅ | ✅ |
-| `robodisco/Boil-v0` |  | Fill and boil water using a jug, faucet, and burner | ✅ | ✅ | ✅ |
-| `robodisco/Circuit-v0` |  | Assemble circuit components (batteries, wires, switch) | ❌ | ✅ | ✅ |
-| `robodisco/Coffee-v0` |  | Operate a coffee machine: plug in, brew, pour, serve | ✅ | ✅ | ❌ |
-| `robodisco/Cover-v0` |  | Place blocks to cover target regions | ✅ | ✅ | ✅ |
-| `robodisco/Domino-v0` |  | Set up domino chains with fans, balls, and ramps | ✅ | ✅ | ✅ |
-| `robodisco/Fan-v0` |  | Use fans to blow lightweight objects to goals | ✅ | ✅ | ✅ |
-| `robodisco/Float-v0` |  | Float light blocks by filling a container with water | ❌ | ✅ | ✅ |
-| `robodisco/Grow-v0` |  | Grow plants by watering them | ✅ | ✅ | ✅ |
-| `robodisco/Laser-v0` |  | Align lasers and mirrors to hit targets | ❌ | ✅ | ❌ |
-| `robodisco/MagicBin-v0` |  | Sort objects into magic bins that transform them | ❌ | ❌ | ❌ |
-| `robodisco/Switch-v0` |  | Toggle switches to open doors and move objects | ❌ | ❌ | ❌ |
-
-The Demos column was verified by running the oracle command above on
-every env. Failing envs typically need additional `CFG` overrides or
-hit known issues (missing NSRTs, sampler-grounding errors, or
-execution drift); they may still be useful as targets for skill or
-NSRT learning research. A handful of envs (`Circuit`, `Cover`, `Laser`,
-`Switch`) also currently fail to instantiate through the Gymnasium
-wrapper with parser defaults — pass `cfg_overrides={...}` to
-`robodisco.make(...)` or call `utils.update_config({...})` before
-`make()` to supply the missing fields.
+| `robodisco/Ants-v0` | [](https://yichao-liang.github.io/robodisco-site/envs/ants.html) | Sort food by whether it attracts ants, stacking same-coloured items | ✅ | ✅ | `oracle` |
+| `robodisco/Balance-v0` | [](https://yichao-liang.github.io/robodisco-site/envs/balance.html) | Equalise two plates on a beam, then press the button | ✅ | ✅ | `oracle` |
+| **`robodisco/Balloons-v0`** | [](https://yichao-liang.github.io/robodisco-site/envs/balloons.html) | Free clipped balloons so they lift a box to hang inside a target band | ✅ | ✅ | ❌ |
+| `robodisco/Barrier-v0` | [](https://yichao-liang.github.io/robodisco-site/envs/barrier.html) | Raise and lower barriers with the switches that control them | ✅ | ❌ | ❌ |
+| `robodisco/Blocks-v0` | [](https://yichao-liang.github.io/robodisco-site/envs/blocks.html) | Rearrange blocks into the requested towers | ✅ | ✅ | `oracle` |
+| **`robodisco/Boil-v0`** | [](https://yichao-liang.github.io/robodisco-site/envs/boil.html) | Fill jugs at a faucet and boil them on a burner without spilling | ✅ | ✅ | `oracle_process_planning`\* |
+| **`robodisco/Bridge-v0`** | [](https://yichao-liang.github.io/robodisco-site/envs/bridge.html) | Glue identical blocks into an n-shaped bridge whose joints cure over time | ✅ | ✅ | `oracle_process_planning`\* |
+| `robodisco/BusyBoard-v0` | [](https://yichao-liang.github.io/robodisco-site/envs/busyboard.html) | Light the right lamps on a board of switches with hidden wiring | ✅ | ✅ | ❌ |
+| `robodisco/Circuit-v0` | [](https://yichao-liang.github.io/robodisco-site/envs/circuit.html) | Wire a battery to a bulb and switch it on | ✅ | ✅ | `oracle` |
+| `robodisco/Coffee-v0` | [](https://yichao-liang.github.io/robodisco-site/envs/coffee.html) | Brew a jug of coffee and pour it into every cup | ✅ | ✅ | ❌ |
+| `robodisco/Cover-v0` | [](https://yichao-liang.github.io/robodisco-site/envs/cover.html) | Place blocks so they cover target regions | ✅ | ✅ | `oracle` |
+| `robodisco/Crane-v0` | [](https://yichao-liang.github.io/robodisco-site/envs/crane.html) | Swing a hinged ram to knock a crate onto a landing pad | ✅ | ✅ | `oracle` |
+| **`robodisco/Domino-v0`** | [](https://yichao-liang.github.io/robodisco-site/envs/domino.html) | Place as few dominoes as possible so a pushed chain topples the target | ✅ | ✅ | ❌ |
+| **`robodisco/Fan-v0`** | [](https://yichao-liang.github.io/robodisco-site/envs/fan.html) | Switch banks of fans to blow a ball through a walled grid to a target | ✅ | ✅ | ❌ |
+| `robodisco/Float-v0` | [](https://yichao-liang.github.io/robodisco-site/envs/float.html) | Raise the water level to lift a floating block within reach | ✅ | ✅ | ❌ |
+| `robodisco/Grow-v0` | [](https://yichao-liang.github.io/robodisco-site/envs/grow.html) | Grow plants by pouring from the jug of matching colour | ✅ | ✅ | `oracle_process_planning` |
+| `robodisco/IceRink-v0` | [](https://yichao-liang.github.io/robodisco-site/envs/icerink.html) | Push sliding tiles onto matching targets across a low-friction rink | ✅ | ✅ | ❌ |
+| `robodisco/Laser-v0` | [](https://yichao-liang.github.io/robodisco-site/envs/laser.html) | Place mirrors to route a laser beam onto the targets | ✅ | ✅ | ❌ |
+| `robodisco/Launcher-v0` | [](https://yichao-liang.github.io/robodisco-site/envs/launcher.html) | Cock a spring launcher to knock only the top block off a tower | ✅ | ✅ | `oracle` |
+| `robodisco/MagicBin-v0` | [](https://yichao-liang.github.io/robodisco-site/envs/magic-bin.html) | Make blocks vanish by dropping them in a bin that a switch activates | ❌ | ❌ | ❌ |
+| `robodisco/Magnets-v0` | [](https://yichao-liang.github.io/robodisco-site/envs/magnets.html) | Steer coloured pieces into slots with a magnetic wand | ✅ | ✅ | `oracle` |
+| `robodisco/Switch-v0` | [](https://yichao-liang.github.io/robodisco-site/envs/switch.html) | Set a light's colour with a power switch and a colour switch | ✅ | ❌ | ❌ |
+
+All columns were checked on October 1, 2026.
+Every environment builds, resets, steps and renders through the Gymnasium wrapper with the Quick Start configuration.
+Circuit, Laser and Switch fail with a PyBullet joint error when another environment was built earlier in the same process, so build them in a fresh process.
+
+\* Boil and Bridge solve with `oracle_process_planning` under the settings of their oracle tests ([`test_oracle_process_planning_boil.py`](../../tests/approaches/test_oracle_process_planning_boil.py), [`test_oracle_process_planning_bridge.py`](../../tests/approaches/test_oracle_process_planning_bridge.py)); they do not solve in the default configuration.
+
+Laser and BusyBoard start every training task from one layout and use a different one for testing; MagicBin keeps one layout and varies only the goal.
+
+The other ❌ entries in the Oracle column, in the default configuration:
+Barrier, MagicBin and Switch have no ground-truth operators or processes;
+Domino and Fan fail an assertion while building their ground-truth processes;
+Coffee fails to read a cup's pose;
+Balloons, BusyBoard, Float, IceRink and Laser run but do not solve the test task.
+The clips on the project page come from the benchmark settings or from scripted skill sequences, as each environment's page says.
+
+## Skill libraries
+
+`CFG.skill_library` selects the skills that `get_gt_options` builds:
+
+- `"composite"` (the default) is each environment's own skills, such as `PickJug`, `SwitchFaucetOn` or `Push`, with task knowledge built in.
+ These are the skills of the Skills column.
+- `"primitive"` is one domain-general library, identical in every environment that supports it: `MoveTo[x, y, z, yaw, tilt]`, `MoveLinear[dx, dy, dz, step]`, `MoveUntilContact[dx, dy, dz, step]`, `Gripper[width, force]` and `Wait[steps]` (see [`skill_factories/primitives.py`](../ground_truth_models/skill_factories/primitives.py)).
+ The agent supplies the grasp points, push strokes and release moments itself.
+ Balloons, Boil, Bridge, Domino and Fan support it.
## Per-environment configuration
-The RoboDisco envs read from predicators' global `CFG` object, which
-normally gets populated by `predicators/main.py`'s command-line parser.
+The RoboDisco envs read from predicators' global `CFG` object, which normally gets populated by `predicators/main.py`'s command-line parser.
For library use, set it explicitly:
```python
@@ -138,23 +154,21 @@ Each env can be used directly via predicators' `BaseEnv` interface:
```python
from predicators import utils
from predicators.envs.pybullet_blocks import PyBulletBlocksEnv
+from predicators.structs import Action
utils.reset_config({"num_train_tasks": 5, "num_test_tasks": 5})
env = PyBulletBlocksEnv(use_gui=False)
state = env.reset("train", 0)
for _ in range(50):
- action = env.action_space.sample()
+ action = Action(env.action_space.sample())
state = env.step(action)
```
-This gives you direct access to `env.predicates`, `env.types`,
-`env.goal_predicates`, `env.get_train_tasks()`, etc., without flattening
-the state into a `Box` observation.
+This gives you direct access to `env.predicates`, `env.types`, `env.goal_predicates`, `env.get_train_tasks()`, etc., without flattening the state into a `Box` observation.
## Developing new envs
-For a guide on writing new PyBullet environments, see
-[`docs/envs/pybullet-guide.md`](../../docs/envs/pybullet-guide.md).
+For a guide on writing new PyBullet environments, see [`docs/envs/pybullet-guide.md`](../../docs/envs/pybullet-guide.md).
## Predicators planning framework
diff --git a/predicators/envs/gymnasium_wrapper.py b/predicators/envs/gymnasium_wrapper.py
index 98a65cb797..6f57f5edf4 100644
--- a/predicators/envs/gymnasium_wrapper.py
+++ b/predicators/envs/gymnasium_wrapper.py
@@ -1,6 +1,6 @@
"""RoboDisco: a Gymnasium-API wrapper for predicators' PyBullet envs.
-RoboDisco (Robot Model Discovery Benchmark) exposes the 15 native
+RoboDisco (Robot Model Discovery Benchmark) exposes the 22 native
``predicators.envs.pybullet_*`` environments through a standard
``gymnasium.Env`` interface so the suite can be used as a robot
model-discovery benchmark independent of the predicators planning
@@ -200,24 +200,37 @@ def close(self) -> None:
("robodisco/Ants-v0", "predicators.envs.pybullet_ants:PyBulletAntsEnv"),
("robodisco/Balance-v0",
"predicators.envs.pybullet_balance:PyBulletBalanceEnv"),
+ ("robodisco/Balloons-v0",
+ "predicators.envs.pybullet_balloons:PyBulletBalloonsEnv"),
("robodisco/Barrier-v0",
"predicators.envs.pybullet_barrier:PyBulletBarrierEnv"),
("robodisco/Blocks-v0",
"predicators.envs.pybullet_blocks:PyBulletBlocksEnv"),
("robodisco/Boil-v0", "predicators.envs.pybullet_boil:PyBulletBoilEnv"),
+ ("robodisco/Bridge-v0",
+ "predicators.envs.pybullet_bridge:PyBulletBridgeEnv"),
+ ("robodisco/BusyBoard-v0",
+ "predicators.envs.pybullet_busyboard:PyBulletBusyBoardEnv"),
("robodisco/Circuit-v0",
"predicators.envs.pybullet_circuit:PyBulletCircuitEnv"),
("robodisco/Coffee-v0",
"predicators.envs.pybullet_coffee:PyBulletCoffeeEnv"),
("robodisco/Cover-v0", "predicators.envs.pybullet_cover:PyBulletCoverEnv"),
+ ("robodisco/Crane-v0", "predicators.envs.pybullet_crane:PyBulletCraneEnv"),
("robodisco/Domino-v0",
"predicators.envs.pybullet_domino.env:PyBulletDominoEnv"),
("robodisco/Fan-v0", "predicators.envs.pybullet_fan:PyBulletFanEnv"),
("robodisco/Float-v0", "predicators.envs.pybullet_float:PyBulletFloatEnv"),
("robodisco/Grow-v0", "predicators.envs.pybullet_grow:PyBulletGrowEnv"),
+ ("robodisco/IceRink-v0",
+ "predicators.envs.pybullet_icerink:PyBulletIceRinkEnv"),
("robodisco/Laser-v0", "predicators.envs.pybullet_laser:PyBulletLaserEnv"),
+ ("robodisco/Launcher-v0",
+ "predicators.envs.pybullet_launcher:PyBulletLauncherEnv"),
("robodisco/MagicBin-v0",
"predicators.envs.pybullet_magic_bin:PyBulletMagicBinEnv"),
+ ("robodisco/Magnets-v0",
+ "predicators.envs.pybullet_magnets:PyBulletMagnetsEnv"),
("robodisco/Switch-v0",
"predicators.envs.pybullet_switch:PyBulletSwitchEnv"),
]
diff --git a/scripts/robodisco_getting_started.py b/scripts/robodisco_getting_started.py
index 353415e617..35469780a8 100644
--- a/scripts/robodisco_getting_started.py
+++ b/scripts/robodisco_getting_started.py
@@ -27,7 +27,7 @@
robodisco.register_all_environments()
env_ids = sorted(robodisco.get_all_env_ids())
print(f"[1/6] Found {len(env_ids)} environments")
-assert len(env_ids) == 15, f"Expected 15 environments, got {len(env_ids)}"
+assert len(env_ids) == 22, f"Expected 22 environments, got {len(env_ids)}"
for eid in env_ids:
print(f" {eid}")
@@ -132,7 +132,9 @@
print(f"\nSummary: {len(ok)}/{len(env_ids)} envs reset cleanly.")
if fail:
- print("Failing envs (require additional CFG to instantiate):")
+ print("Failing envs (Circuit, Laser and Switch fail when another env "
+ "was built earlier in the same process; see "
+ "predicators/envs/README.md):")
for eid, kind, msg in fail:
print(f" {eid}: {kind}: {msg}")
diff --git a/tests/envs/test_robodisco.py b/tests/envs/test_robodisco.py
index 5bc7a4bbb7..f08e66220c 100644
--- a/tests/envs/test_robodisco.py
+++ b/tests/envs/test_robodisco.py
@@ -35,18 +35,18 @@ def rgb_env():
def test_register_all_environments_count():
- """register_all_environments() registers all 15 envs."""
+ """register_all_environments() registers all 22 envs."""
register_all_environments()
rd_ids = {
eid
for eid in gymnasium.registry if eid.startswith("robodisco/")
}
- assert len(rd_ids) == 15
+ assert len(rd_ids) == 22
-def test_get_all_env_ids_returns_15():
- """get_all_env_ids() returns exactly 15 ids."""
- assert len(get_all_env_ids()) == 15
+def test_get_all_env_ids_returns_22():
+ """get_all_env_ids() returns exactly 22 ids."""
+ assert len(get_all_env_ids()) == 22
def test_get_all_env_ids_prefix():
@@ -60,7 +60,7 @@ def test_register_is_idempotent():
"""register_all_environments() is safe to call multiple times."""
register_all_environments()
register_all_environments()
- assert len(get_all_env_ids()) == 15
+ assert len(get_all_env_ids()) == 22
# ---------------------------------------------------------------------------