Skip to content

Scene

A scene under construction. worlds: parallel copies; dt: control period in seconds; roots: your own card folders (materials/, objects/, ...: e.g. cards made by alloy.create or alloy.io), searched before the shipped and imported libraries; liquid_accuracy: "default" (liquid particles 4 mm apart) or "super" (2 mm, about 16x the cost; a build warns when the default is too coarse for an opening or a stream); cloth_accuracy: "default", "super" (nearer convergence, 1.4x the cloth cost) or "fast" (the cloth solver's own 10 iterations, 0.4x the default's cloth cost, CPU 0.25x; a drape stretches 12 %); soft bodies use it too. Example: s = alloy.Scene(worlds=64); s.add_ground(); s.add_sphere("ball", 0.05, pos=(0, 0, 0.3)).

liquid_accuracy property writable

liquid_accuracy: str

"default" (liquid particles 4 mm apart) or "super" (2 mm, about 16x the cost); set it before building.

add_ground

add_ground(name: str = 'ground', *, z: float = 0.0, material: str = GROUND_MATERIAL, friction: float | None = None) -> str

An infinite static plane at height z. Example: scene.add_ground().

add_box

add_box(name: str, size: Sequence[float] | None = None, *, half_extents: Sequence[float] | None = None, pos: Any = (0.0, 0.0, 0.0), **kw: Any) -> str

A box. size: full edge lengths (x, y, z) in m; or half_extents. Keywords: see add_sphere.

Example: scene.add_box("cube", size=(0.1, 0.1, 0.1), pos=(0, 0, 1), mass=1.0).

add_sphere

add_sphere(name: str, radius: float, *, pos: Any = (0.0, 0.0, 0.0), quat_xyzw: Any = None, quat_wxyz: Any = None, rpy: Any = None, material: str = RIGID_MATERIAL, mass: float | None = None, density: float | None = None, friction: float | None = None, restitution: float | None = None, static: bool = False, rigid: bool | None = None, kinematic: bool = False) -> str

A sphere. Shared keywords of every add_*:

  • pos: (x, y, z) in m, or (worlds, 3) for one start per world;
  • orientation: one of quat_xyzw, quat_wxyz or rpy (roll, pitch, yaw in radians);
  • material: a name from alloy.materials() (or add_material); mass (kg) or density (kg/m^3), friction (Coulomb mu) and restitution override it for this object only;
  • static=True: fixed in place (tables, walls, bins);
  • kinematic=True: moved only by sim.set_pose / sim.drive (a tilting tray, a moving wall); other objects, liquids and cloth feel it, gravity and contacts do not move it;
  • rigid=True: always a rigid body. Without it a soft material (rubber, foam) can make the object a soft body (sim.routes() says which), which is far slower; rigid=False asks for a soft body.

Example: scene.add_sphere("ball", 0.05, pos=(0, 0, 0.5), material="steel_carbon").

add_cylinder

add_cylinder(name: str, radius: float, height: float, *, pos: Any = (0.0, 0.0, 0.0), **kw: Any) -> str

A solid cylinder along its local z axis; height is the full height. Keywords: see add_sphere.

Example: scene.add_cylinder("can", 0.033, 0.12, pos=(0, 0, 0.06), mass=0.35).

add_capsule

add_capsule(name: str, radius: float, height: float, *, pos: Any = (0.0, 0.0, 0.0), **kw: Any) -> str

A capsule along local z; height is the straight part between the two caps. See add_sphere.

Example: scene.add_capsule("rod", 0.01, 0.2, pos=(0, 0, 0.3)).

add_mesh

add_mesh(name: str, path: str | Path | None = None, *, vertices: Any = None, faces: Any = None, scale: float | Sequence[float] = 1.0, collision: str = 'convex', pos: Any = (0.0, 0.0, 0.0), **kw: Any) -> str

A rigid triangle mesh from an OBJ/STL file or from vertices (V, 3) and faces (F, 3) arrays.

collision: "convex" (hull; fast), "decompose" (convex parts), "sdf" (exact, for cups, bins, holes) or "mesh"; with "sdf" or "mesh" alloy.build uses Newton's contact pipeline (MuJoCo's own collision would fill the hollow). "decompose" splits the mesh into at most 32 convex parts with CoACD at build, once per mesh (cached under $ALLOY_CACHE_DIR/hulls), or uses a <stem>.hulls.obj shipped beside the mesh file; it needs pip install "alloy-engine[import]" (CoACD, trimesh) and a closed mesh. For cups, bins and holes prefer add_open_box / add_open_cylinder / add_block_with_hole (static boxes: exact and fast). Faces wind counter-clockwise seen from outside.

Example: scene.add_mesh("cup", vertices=v, faces=f, collision="sdf", static=True).

add_cloth

add_cloth(name: str, size: Sequence[float] = (1.0, 1.0), *, pos: Any = (0.0, 0.0, 0.5), resolution: int | Sequence[int] = 21, thickness: float = 0.002, material: str = CLOTH_MATERIAL, mass: float | None = None, friction: float | None = None, quat_xyzw: Any = None, quat_wxyz: Any = None, rpy: Any = None, self_contact: bool = True) -> str

A cloth sheet, flat in its local x-y plane, centred at pos. size: (x, y) in m; resolution: vertices along the longer side (or (nx, ny)); mass: total kg (else from the material and thickness). thickness: the contact thickness (particles of radius thickness / 2; a fabric bends as its card says at any thickness); the health monitor flags penetration past half of it, so thinner sheets need a smaller dt. self_contact (default True): the sheet collides with itself, so a fold or a pile stays in layers thickness apart instead of passing through itself. With two or more cloth or soft objects in a scene they always collide with each other and with themselves. Turning it off makes a lone sheet cheaper to step (cost: docs/guide/deformables.md) when it never touches itself (a flag, a sheet that hangs or lies flat).

Example: scene.add_cloth("towel", size=(0.5, 0.5), pos=(0, 0, 0.6), resolution=25, mass=0.1).

add_rod

add_rod(name: str, points: Any = None, *, radius: float, length: float | None = None, pos: Any = (0.0, 0.0, 0.0), direction: Sequence[float] = (1.0, 0.0, 0.0), material: str = _rods.ROD_MATERIAL, segments: int | None = None, clamp: str | None = None, mass: float | None = None, friction: float | None = None, route: str | None = None) -> str

An elastic rod (a cable, wire or cord) of round section radius (m) along points (a polyline, relative to pos), or length along direction from pos. It bends, twists and stretches as its material says (E, nu), rolls on surfaces and can be gripped. segments: rigid segments along it (>= 3; default: enough that it sags under its own weight within 5 % of the continuum). clamp: "start", "end" or "both" holds that end where it is built; a clamped rod steps on the elastic-rod solver (rod_vbd) unless route="chain" (with clamp="start": far faster beside a robot; docs/guide/rods.md). Read its centre line as "<name>.points", its twist per joint as "<name>.twist"; push or twist it with sim.apply_wrench.

Example: scene.add_rod("cable", length=0.5, radius=0.002, pos=(0, 0, 0.6), clamp="start").

add_liquid

add_liquid(name: str, size: Sequence[float], *, pos: Any = (0.0, 0.0, 0.5), material: str = LIQUID_MATERIAL, shape: str = 'box', temperature: float | None = None, heat_material: str | None = None) -> str

Liquid (SPH particles) at rest, filling a box of full size (x, y, z) m centred at pos, or with shape="cylinder" an upright cylinder of diameter x (= y) and height z: liquid already in a container, or a puddle. Match the container: in a round cup use shape="cylinder"; a box in a round cup is not at rest (its corners stick out), collapses and jets over the rim. To fill a container use add_pour: a block released in the air splashes several percent out when it lands (flat-impact pressure jets, real physics).

The engine picks the particle size and fills the shape with whole particles (sim.liquid_volume(name)). temperature: start temperature in K; the liquid then takes heat (Scene.set_temperature). Example: scene.add_liquid("water", size=(0.07, 0.07, 0.04), pos=(0, 0, 0.024), shape="cylinder") (in a round cup of inner radius 0.035 m at the origin).

add_pour

add_pour(name: str, volume: float, pos: Any, *, rate: float = 0.0002, speed: float = 0.5, material: str = LIQUID_MATERIAL, temperature: float | None = None, heat_material: str | None = None) -> str

Pour volume m^3 of liquid from a nozzle at pos straight down at rate m^3/s, leaving at about speed m/s (a square nozzle of whole particles; the exit speed is set so the rate is exact). This is how to fill a container: a block of liquid dropped from above splashes several percent over the rim (flat-impact pressure jets, real physics), a stream does not. The particles wait at the nozzle until released: sim.state()["<name>.active"] marks the poured ones. The energy a pour brings in (and takes out when particles retire) is booked as external work, so the health monitor's energy check stays on. temperature: the liquid's start temperature in K (it then takes heat).

Example: scene.add_pour("water", 200e-6, pos=(0, 0, 0.15)) (200 ml over 1 s into a cup below).

add_robot

add_robot(name: str, path: str | Path | None = None, *, mjcf: str | None = None, pos: Any = (0.0, 0.0, 0.0), quat_xyzw: Any = None, quat_wxyz: Any = None, rpy: Any = None, contact_gap: float | None = None, sysid: Any = None) -> str

A robot from an MJCF / URDF / USD file, or an MJCF string (mjcf=). Its base is welded to the world unless the file gives it a joint. Bodies and joints are named "/" in the sim; MJCF <position> actuators follow sim.set_targets. pos (worlds, 3) starts a free-base robot (MJCF <freejoint/> on its root body) at one position per world. contact_gap (m): how far apart its shapes start making contacts; default 5 mm as every scene object (an MJCF geom's own gap is kept); 0.1 gives the importer's old default, which fills the contact buffer in packed scenes. sysid: a file from python -m alloy.sysid fit, its dict or the fit's result (fitted joint parameters; docs/guide/sysid.md).

Example: scene.add_robot("arm", mjcf=open("arm.xml").read(), pos=(0, 0, 0)).

add_object

add_object(name: str, card: str, *, pos: Any = (0.0, 0.0, 0.0), quat_xyzw: Any = None, quat_wxyz: Any = None, rpy: Any = None, static: bool | None = None, temperature: float | None = None, heat_material: str | None = None, anchor: str = 'auto') -> str

An object from the card library by card name (alloy.api.catalog.objects() lists them; on a fresh install, with the import extra, also the Hub's object library, fetched at build or by alloy fetch). temperature (K) heats a particle object (MPM or SPH card) as Scene.set_temperature does. anchor: the point of the object that goes to pos: "center" (its bounding box's centre), "base" (its bottom's centre: pos z is what it stands on), "origin" (its card's frame), or "auto" (default): "base" for scanned objects from an imported library (YCB, Google Scanned Objects, RoboCasa, ...; their frames sit centimetres off the object), "origin" for Alloy's own library objects, whose frames are set on purpose (a cup's base), so pos is where an object stands either way. What you set is what you read: "<name>.pos" in sim.state() and trajectories is that anchor point (it turns with the object), sim.set_pose(name, pos=...) puts it there, and "<name>.body_pos" is the card frame's origin (for objects placed by an anchor off their origin).

Example: scene.add_object("peg", "peg_d12", pos=(0, 0, 0.04)).

add_material

add_material(name: str, *, density: float, friction: float = 0.5, restitution: float = 0.3, like: str | None = None) -> str

A new rigid material (or a copy of like with these values). Example::

scene.add_material("rubber_pad", density=1100, friction=1.2, restitution=0.2)

set_friction

set_friction(a: str, b: str, mu: float) -> None

Coulomb friction between two objects (overrides their materials' combined value).

Example: scene.set_friction("finger_pads", "cube", 1.2).

set_inertia_floor

set_inertia_floor(on: bool = True) -> None

Turn the inertia floor on (the default) or off. The floor gives light slender free bodies (a 16 g marker pen) at least m·(9 mm)² of rotational inertia at the default rigid step, so a rim contact cannot spin them up until a world blows up; they then spin and roll slower about their thin axis (the build warns, naming them; sim.routes() says so). Off: exact rolling, which needs a smaller rigid step for such parts.

Example: scene.set_inertia_floor(False).

randomize

randomize(name: str, field: str, lo: float, hi: float, when: str = 'build') -> None

Draw field per world uniformly in [lo, hi] (seeded by alloy.build(seed=)). Fields: x, y, z (start position, m), density, friction (static; the object's dynamic friction is capped at lo), youngs, or a card path such as "material.surface.mu_dynamic". For a free-base robot: x, y, z of its base. Robots, their links and joints, and "gravity" take the fields of docs/guide/randomize_robots.md (mass_scale, friction, kp_scale, ...); when="reset" draws those again at every sim.reset().

Example: scene.randomize("cube", "x", -0.005, 0.005).

add_push

add_push(name: str, force: Any, every: Any = (4.0, 8.0), duration: float = 0.1) -> None

Push body name at random times, per world: after a wait drawn in every = (lo, hi) seconds, a force drawn in +-force (N, per world axis) is held for duration s (docs/guide/randomize_robots.md).

Example: scene.add_push("go2/base", force=(80, 80, 0), every=(4.0, 8.0)).

load classmethod

load(source: str | Path, *, worlds: int | None = None) -> Scene

An editable Scene from a library scene name, scene card, Scene JSON or robot file (see alloy.load).

Example: scene = alloy.Scene.load("demo_peg_d12"); scene.add_sphere("ball", 0.01, pos=(0, 0, 0.1)).

build

build(worlds: int | None = None, device: str | None = None, seed: int = 0, *, profile: str = 'standard', plugins: Sequence[Any] = ()) -> Any

Same as alloy.build(self, ...): returns an alloy.Sim. Example: sim = scene.build(worlds=64).

names

names() -> list[str]

Every object and robot name, in insertion order. Example: ["ground", "cube"].

summary

summary() -> dict[str, Any]

A short JSON-ready description. Example: {"worlds": 1, "dt": 0.01, "objects": {"cube": "box"}}.

to_dict

to_dict() -> dict[str, Any]

Plain JSON-ready data. Example: alloy.Scene.from_dict(json.loads(json.dumps(scene.to_dict()))).

from_dict classmethod

from_dict(data: dict[str, Any]) -> Scene

A Scene from to_dict() data. Example: alloy.Scene.from_dict(scene.to_dict()).

to_json

to_json() -> str

to_dict() as a JSON string. Example: Path("scene.json").write_text(scene.to_json()).

save

save(directory: str | Path) -> Path

Write the engine's YAML cards (materials/, objects/, scenes/scene.yaml) to directory; returns the scene file. Example: scene.save("my_scene") then alloy.load("my_scene/scenes/scene.yaml").