Scenes & objects¶
A Scene is the full world description that gets handed to a
SimBackend: robot, objects, and workspace bounds.
Scene¶
from robosandbox.types import Scene, SceneObject, Pose
from pathlib import Path
scene = Scene(
robot_urdf=Path("/path/to/robot.urdf"), # None → built-in 6-DOF arm
robot_config=Path("/path/to/robot.yaml"), # sibling auto-discovered if None
objects=(
SceneObject(id="red_cube", kind="box", size=(0.02, 0.02, 0.02),
pose=Pose(xyz=(0.4, 0.0, 0.05)),
rgba=(0.85, 0.2, 0.2, 1.0), mass=0.05),
),
workspace_aabb=((-0.5, -0.5, 0.0), (0.5, 0.5, 0.8)),
table_height=0.0,
gravity=(0.0, 0.0, -9.81),
)
It is a frozen dataclass. sim.load(scene) turns it into MuJoCo XML.
SceneObject kinds¶
kind |
size semantics |
Notes |
|---|---|---|
box |
(sx, sy, sz) half-extents (m) |
Free body with freejoint. |
sphere |
(radius,) |
— |
cylinder |
(radius, half_height) |
— |
mesh |
unused — dims from the mesh | Needs mesh_sidecar or mesh_path. |
drawer |
(width_y, depth_x, height_z) inner drawer |
Articulated: static cabinet + prismatic inner body + handle. |
All primitives spawn with a freejoint unless kind says otherwise.
pose is the initial pose; physics takes over from there.
Meshes¶
There are two mutually exclusive mesh paths:
Bundled YCB — set mesh_sidecar to a sidecar YAML file, or use
the YAML task shortcut mesh: "@ycb:<id>":
Bring-your-own — set mesh_path to an OBJ/STL. CoACD decomposes it
into convex hulls; hulls are cached at
~/.cache/robosandbox/mesh_hulls/. Needs the meshes extra:
uv pip install -e 'packages/robosandbox-core[meshes]'.
SceneObject(
id="widget",
kind="mesh",
size=(0.0,), # unused for mesh
mesh_path=Path("/abs/path/to/widget.obj"),
collision="coacd", # or "hull" if already convex
pose=Pose(xyz=(0.4, 0.0, 0.05)),
mass=0.1,
)
collision="hull" skips CoACD — the sandbox trusts the mesh is
already convex. Use "coacd" for concave objects.
If you want to pre-decompose a mesh once and keep the result:
python scripts/decompose_mesh.py \
--input /path/to/drill.obj \
--out-dir assets/objects/custom/drill \
--name drill --mass 0.3 --center-bottom
Drawer primitive¶
This is the only articulated primitive in v0.1.
- id: drawer_a
kind: drawer
size: [0.15, 0.12, 0.05] # (width_y, depth_x, height_z) of inner drawer
pose:
xyz: [0.42, 0.0, 0.08]
rgba: [0.55, 0.35, 0.2, 1.0]
drawer_max_open: 0.12 # cap on slide travel (m)
The SceneObject.id names the sliding body (observable as
obs.scene_objects["drawer_a"]). A sibling <id>_handle body is also
tracked so skills can grasp it.
See the open_drawer / close_drawer skills
and the open_drawer task for how the
displaced success criterion reads the slide motion.
Bundled YCB catalog¶
Ten pre-decomposed objects ship with core:
| YCB id | Description | Mass (kg) |
|---|---|---|
003_cracker_box |
cracker box | 0.411 |
005_tomato_soup_can |
tomato soup can | 0.349 |
006_mustard_bottle |
mustard bottle | 0.603 |
011_banana |
banana | 0.066 |
013_apple |
apple | 0.068 |
024_bowl |
bowl (hollow; 11 hulls) | 0.147 |
025_mug |
mug (handled; 15 hulls) | 0.118 |
035_power_drill |
power drill | 0.895 |
042_adjustable_wrench |
adjustable wrench | 0.252 |
055_baseball |
baseball | 0.148 |
From Python:
from robosandbox.tasks.loader import list_builtin_ycb_objects
list_builtin_ycb_objects()
# ['003_cracker_box', '005_tomato_soup_can', ..., '055_baseball']
From a YAML task, use the @ycb:<id> shorthand. The loader resolves
it to the bundled sidecar.
Robots¶
The default built-in robot is a simple 6-DOF arm defined directly in
MJCF. To use your own robot, set
robot_urdf:
Scene(
robot_urdf=Path("/path/to/ur5.urdf"), # .urdf or .xml
robot_config=Path("/path/to/ur5.robosandbox.yaml"), # optional sibling
objects=(...),
)
robot_config is a sidecar YAML mapping RoboSandbox roles (arm joint
list, gripper primary joint, end-effector TCP, home pose) onto the
robot's element names. See
custom-arm tutorial for the full
schema.
The bundled Franka Panda lives at
packages/robosandbox-core/src/robosandbox/assets/robots/franka_panda/
— its panda.robosandbox.yaml is the reference schema.
Procedural scenes¶
from robosandbox.scene.presets import tabletop_clutter
scene = tabletop_clutter(n_objects=5, seed=0)
# Franka + 5 non-overlapping YCB objects. seed 0 is bit-exact deterministic.
See examples/procedural_scene.py for a runnable end-to-end flow.
Full YAML task schema¶
Used by every file under
packages/robosandbox-core/src/robosandbox/tasks/definitions/.
name: pick_ycb_mug
prompt: "pick up the mug"
seed_note: "Optional free-text context for the task author."
scene:
robot_urdf: "@builtin:robots/franka_panda/panda.xml" # or a path
robot_config: "@builtin:robots/franka_panda/panda.robosandbox.yaml"
objects:
- id: mug
kind: mesh
mesh: "@ycb:025_mug"
pose:
xyz: [0.42, 0.0, 0.045]
# quat_xyzw: [0, 0, 0, 1] (optional)
# mass: 0.12 (optional; mesh mass defaults to sidecar)
# rgba: [r, g, b, a] (optional)
success:
kind: lifted # lifted | moved_above | displaced | all | any
object: mug
min_mm: 50
randomize: # optional — per-seed jitter
xy_jitter: 0.03 # ±3 cm
yaw_jitter: 1.57 # ±90°
@builtin: resolves inside the installed package. @ycb:<id>
resolves to a bundled YCB sidecar. See
recording & export for what the runner
does with the success criterion.
Related¶
- Skills & agents — what consumes a Scene.
- Custom arm — dropping in a URDF.
- Custom task — authoring a YAML task.