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_wxyzorrpy(roll, pitch, yaw in radians); material: a name fromalloy.materials()(oradd_material);mass(kg) ordensity(kg/m^3),friction(Coulomb mu) andrestitutionoverride it for this object only;static=True: fixed in place (tables, walls, bins);kinematic=True: moved only bysim.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=Falseasks 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 "<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").