Skip to content

Scene

The cell: one or more robots, the obstacles around them, the frames they mount on, the devices and sensors that give the environment behavior, the named motions, and the sequences that drive it all.

Units are meters, and the world is Z-up. Orientations are quaternions in (x, y, z, w) order. Wherever a method takes robot=None, it acts on the scene's first robot.

A scene is also the live link to the studio: state changes made from Python are pushed to connected browsers, and edits made in the browser are visible here.

import botrail as bt

scene = bt.Scene(bt.Robot.from_urdf("arm.urdf"))
scene.add_box("table", size=(0.6, 0.6, 0.05), position=(0.4, 0.0, 0.0))
scene.load_usd("cell.usda", prefix="env")
scene.set_robot_base_pose(*scene.frame("env/World/mount"))

Scene

The cell: one or more robots in a workspace, with the obstacles, frames, sensors, devices, motions, and sequences around them. Shared with the studio server: state changes made here are pushed to connected browsers immediately.

applicator_names property

applicator_names

attachments property

attachments

Attached obstacles as (object, link) name pairs.

brush_names property

brush_names

camera_names property

camera_names

collision_warnings property

collision_warnings

Link shapes skipped for collision checking (e.g. meshes, until the mesh I/O crate lands).

device_names property

device_names

frames property

frames

All named frames as {name: (position, quaternion_xyzw)}.

joint_positions property

joint_positions

lidar_names property

lidar_names

motion_names property

motion_names

obstacle_names property

obstacle_names

robot property

robot

robot_base_pose property

robot_base_pose

World pose of the robot root as (position, quaternion_xyzw).

robots property

robots

Instance names of every robot in the scene, in insertion order.

scenario_names property

scenario_names

Defined scenario names, in authoring order (baseline — the unmodified scene — is implicit and never listed).

sensor_names property

sensor_names

sequence_names property

sequence_names

signals property

signals

Declared internal signals as (name, initial) pairs.

toolpath_names property

toolpath_names

add_beam_sensor method descriptor

add_beam_sensor(
    name,
    frm,
    to,
    radius=0.005,
    watch=None,
    watch_robot=False,
    watch_robots=None,
    mount=None,
)

Adds a photoelectric beam sensor between two world points, ON while the beam is interrupted. Watch semantics as in add_zone_sensor.

add_box method descriptor

add_box(name, size, position, quaternion=None, color=None)

Adds a box obstacle (full extents, meters). Returns the final name, which may be uniquified. Changes are pushed to connected studios.

add_camera method descriptor

add_camera(
    name,
    position=Ellipsis,
    quaternion=None,
    look_at=None,
    fov=None,
    resolution=None,
    near=None,
    far=None,
    mount=None,
    robot=None,
    link=None,
    from_catalog=None,
    revision=None,
)

Adds a camera: a named viewpoint with pinhole optics, drawn as a frustum in the studio. Presentation only — it publishes no signal and never affects planning or the cycle. position/quaternion are in the mount frame (-Z looks, +Y is image-up); look_at aims the camera at a world point instead of giving a quaternion. Mount it with mount= (a vehicle device) or robot=/link= (a wrist camera); default is a world fixture. fov is the horizontal field of view in degrees (default 60); resolution sets the frustum aspect and the pixel size of exports (default 1280x720).

from_catalog= names a sensor.camera package: its flat specs become the optics defaults (fov/resolution and, from the range specs, near/far), explicit arguments still win, and the package's identity lands on the BOM (set_part(kind="camera")). With an explicit pose, position/quaternion place the package's mount face and the optical axis follows the package's own calibration (frames.camera_frames); look_at aims the optical axis itself.

add_conveyor method descriptor

add_conveyor(
    name,
    zone_position,
    zone_size,
    velocity,
    zone_quaternion=None,
    running=True,
)

Adds a conveyor: while running, any unattached obstacle whose origin lies inside the zone box is carried at velocity (m/s). Start/stop it from sequences with bt.seq.start/bt.seq.stop.

add_cut_trace method descriptor

add_cut_trace(name, signal, robot, spin_link=None)

Binds an accumulating cut trace to a signal at a robot's TCP: while the signal is true during playback, the studio draws the TCP's trail (the cut so far) and spins spin_link if given. Pure presentation, like add_weld_flash; in USD the toolpath curves already carry the picture.

add_cylinder method descriptor

add_cylinder(
    name,
    radius,
    length,
    position,
    quaternion=None,
    color=None,
)

Adds a cylinder obstacle (URDF convention: axis along local +z).

add_field_sensor method descriptor

add_field_sensor(
    name,
    lidar,
    watch=None,
    watch_robot=False,
    watch_robots=None,
    range=None,
    sector=None,
    shadowing=True,
)

Adds a laser-scanner field sweeping through lidar: its name becomes a read-only input signal, ON while a watched body crosses the scanner's scan-plane sector. range shrinks the field radius (default: the lidar's max range) and sector=(start, end) narrows its angular window (degrees in the scan frame, 0 = +X, CCW; default: the full sweep) — one scanner carries several fields, the warning/protective field-set shape. shadowing (default on) ray-tests each candidate's origin against the other obstacles, so a body hidden behind another does not trip it. Geometry only — no per-angle rays are cast, robot links (when watched) detect by overlap alone, and a vehicle-mounted field ignores its own machine's body.

add_frame method descriptor

add_frame(name, position, quaternion=None)

Registers (or updates) a named world frame.

add_io_node method descriptor

add_io_node(
    name,
    kind="plc",
    robots=None,
    programs=None,
    uplink=None,
    channels=None,
    place=None,
    model=None,
    label=None,
)

Declares a controller / I/O node of the cell's assignment layer: kind is "plc", "safety_plc", "remote_io", "robot_controller" (with robots=[...]) or "other". programs lists the sequences this node runs (unlisted programs are placed implicitly — see the I/O map guide); uplink is the parent node ("PLC1" or ("PLC1", "PROFINET")) whose I/O a remote station or safety module belongs to; channels are the dicts the bt.io templates build (bt.io.di8(base="%IX0.0") + bt.io.do8(...) or bt.io.ur_standard()).

add_lidar method descriptor

add_lidar(
    name,
    position=Ellipsis,
    quaternion=None,
    yaw=None,
    fov=None,
    range=None,
    resolution=None,
    channels=None,
    vfov=None,
    mount=None,
    robot=None,
    link=None,
    from_catalog=None,
    revision=None,
)

Adds a LiDAR scanner: a named scan origin with a planar sweep, drawn as a sector in the studio. Presentation only — it publishes no signal and never affects planning or the cycle (a field sensor referencing it is the planned signal path). The scan plane is the local XY plane, angle 0 along +X, counter-clockwise toward +Y (the ROS laser frame); yaw aims that +X heading in the mount frame (degrees about +Z) instead of giving a quaternion. Mount it with mount= (a vehicle device) or robot=/link=; default is a world fixture. fov is the full scan angle in degrees up to 360 (default 270), range the measuring band [min, max] in meters (default (0.05, 20.0)), resolution the angular step in degrees the scan API will default to (default 0.5).

from_catalog= names a sensor.lidar package: its flat specs become the sweep defaults (fov/resolution and, from the range specs, the measuring band), explicit arguments still win, and the package's identity lands on the BOM (set_part(kind="lidar")). The given pose places the package's mount face and the scan origin follows the package's own frame (frames.lidar_frames — ROS laser convention, which is botrail's, so no rotation fix).

add_lift method descriptor

add_lift(
    name,
    car,
    zone_position,
    zone_size,
    stops,
    speed=0.5,
    axis=Ellipsis,
    zone_quaternion=None,
    start=None,
)

Adds a lift (elevator): the car obstacles ride along axis between named stops, and whatever the capture zone holds when the ride is commanded rides too — loose parts by origin, and vehicles whole (body, deck load, mounted robot). Command it with bt.seq.move_to(name, "2F") and await bt.seq.device_done(name). The zone (like the car) is authored where the car stands at start; a vehicle half out of it refuses to board by name. Doors are ordinary authoring — an add_linear_axis panel and a signal — not part of the device. Car entries name obstacles exactly, or as subtree prefixes.

add_linear_axis method descriptor

add_linear_axis(
    name,
    objects,
    axis,
    speed,
    range,
    position=0.0,
    stops=None,
)

Adds a linear axis (door / lifter / indexer) moving the listed obstacles along axis at speed, positioned within range by bt.seq.move_to; await it with bt.seq.device_done.

stops names positions along the axis ({"closed": 0.0, "open": 0.76}): bt.seq.move_to(name, "open") drives to one, and each is a read-only input lane <name>/<stop> — ON while the axis stands at it, off between stops — the limit switch an interlock waits on (bt.seq.signal("door/closed")), and a DI on the I/O list.

What the axis drives is checked against every robot each tick: a leaf closing on an arm still inside is a DeviceCollision error naming the device, the obstacle, the robot and the link.

add_mesh method descriptor

add_mesh(
    name,
    path,
    position,
    scale=None,
    quaternion=None,
    color=None,
)

Adds a mesh obstacle from an STL/OBJ file. The collision shape is a VHACD convex decomposition (computed on first load, then cached on disk); the studio renders the original mesh.

add_robot method descriptor

add_robot(
    robot,
    name=None,
    base_position=None,
    base_quaternion=None,
)

Adds another robot instance and returns its (possibly uniquified) scene-unique instance name. name defaults to the model name. Connected studios pick the new robot up immediately (the handshake is re-broadcast).

add_scenario method descriptor

add_scenario(
    name,
    signals=None,
    obstacles=None,
    joints=None,
    faults=None,
)

Defines (or replaces) a scenario — a named initial-state delta the simulate_* calls can run under. Deltas only: signals overrides declared internal-signal initial values, obstacles maps names to a position or a (position, quaternion) pair, joints maps robot instances to start configurations, and faults pins inputs for the whole run — bt.io.stuck("part_at_pick", False) ignores the sensor's geometry (or a program's set on an internal signal), bt.io.open("part_at_pick") is a broken wire (input level low, so the value follows the binding's invert). "baseline" is the reserved name of the unmodified scene. Everything is validated when the scenario is applied (at simulate), so deltas may name things authored later.

add_segment method descriptor

add_segment(
    motion,
    goal=None,
    kind="joint",
    orientation_cone=None,
    position_box=None,
    robot=None,
)

Appends a waypoint segment to motion (created when missing). goal=None captures the current configuration. Constraints: orientation_cone=(axis_local, axis_world, angle_rad) keeps the tool axis inside a cone; position_box=(min, max) keeps the TCP inside a world-aligned box. Both apply along the whole segment.

add_sink method descriptor

add_sink(
    name,
    zone_position,
    zone_size,
    source,
    zone_quaternion=None,
)

Adds the far end of a line: any unattached carrier reaching the zone goes back to source's magazine, free to be fed again.

add_source method descriptor

add_source(
    name,
    pool,
    park,
    position,
    pitch=None,
    interval=0.0,
    running=False,
)

Adds a feeder: every interval seconds while running it puts the next waiting member of pool at position.

The pool is finite because a baked timeline holds a fixed set of named object tracks — an endless line is this plus an add_sink that returns carriers to the magazine. Member i waits at park + pitch * i, and a member that does not start on its slot starts out on the line (an already-loaded belt).

add_spray_cone method descriptor

add_spray_cone(
    name, signal, robot, length=0.25, radius=0.08
)

Binds a spray-cone effect to a signal at a robot's TCP: while the signal is true during playback, the studio draws a translucent cone length long and radius wide at its base along the TCP's spray direction (its -Z), and USD export carries a beam of the same size with animated visibility. Bind it to the effective trigger a timeline writes with with_trigger_signal so it follows what actually sprayed rather than the enable alone. Pure presentation, like add_weld_flash.

add_vehicle method descriptor

add_vehicle(
    name,
    body,
    path,
    stations,
    speed=0.5,
    turn_speed=Ellipsis,
    start=None,
    ring=False,
    allow_reverse=False,
    max_grade=None,
    drive="differential",
    climb_speed=None,
    descent_speed=None,
    fixed_yaw=None,
    tray_position=None,
    tray_size=None,
    tray_quaternion=None,
)

Adds a guided transport vehicle (an AGV / AMR as the cell sees it): it drives station to station along path — straight legs at speed, in-place pivot turns at turn_speed — carrying the body obstacles rigidly. Dispatch it with bt.seq.goto(name, station) and await arrival with bt.seq.device_done(name). The arrival heading is the last leg's direction, so the waypoint before a station sets how the vehicle docks. Body entries name obstacles exactly, or as subtree prefixes ("/World/AGV" takes every obstacle under it).

Waypoints are (x, y) or (x, y, z): z is the floor height on the guidance surface, so a ramp climbs with its waypoints (the body stays level, and speed is spent along the 3D path). A path that climbs needs max_grade — the steepest rise over horizontal run the machine may take (0.10 = 10 %); without it only level paths pass validation.

drive="aerial" makes the machine a multirotor: z is its own axis (any climb, no grade rule, vertical legs fly — a ground station under an overhead waypoint is the takeoff), speed is the horizontal cruise and each leg's clock is the slower axis, max(run/speed, rise/climb_speed (or descent_speed)). The nose faces each leg's course, or holds fixed_yaw the whole flight.

add_vision_sensor method descriptor

add_vision_sensor(
    name,
    camera,
    watch=None,
    watch_robot=False,
    watch_robots=None,
    detect_range=None,
    occlusion=True,
)

Adds a vision presence sensor looking through camera: its name becomes a read-only input signal, ON while a watched body overlaps the camera's view frustum. detect_range narrows the detection band along the view axis (default: the camera's near/far clip); occlusion (default on) ray-tests each candidate's origin against the other obstacles, so a body hidden behind another does not trip it. Geometry only — no pixels are rendered or interpreted, and robot links (when watched) detect by overlap alone.

add_weld_flash method descriptor

add_weld_flash(name, signal, robot)

Binds a weld flash to a signal at a robot's TCP: while the signal is true during playback, the studio draws an arc flash there and the USD export blinks an emissive prim. Pure presentation, driven by the same baked signal a weld controller's "current on" output would be — declare the signal first (define_signal), author it from the sequence that owns the weld.

add_zone_sensor method descriptor

add_zone_sensor(
    name,
    position,
    size,
    quaternion=None,
    watch=None,
    watch_robot=False,
    watch_robots=None,
    mount=None,
)

Adds a box-shaped presence sensor: its name becomes a read-only input signal, ON while a watched body overlaps the zone. watch is a list of obstacle names (default: every obstacle); pass watch_robot=True to sense robot links too (with watch=[] for a robot-only light curtain).

allow_inter_robot_collision method descriptor

allow_inter_robot_collision(
    robot_a, link_a, robot_b, link_b
)

Excuses one link pair of two different robots from collision checking — the escape hatch for arms that share a mount plate or are meant to touch. Unlike a robot's own self-collision matrix, which is generated by sampling, inter-robot pairs are never inferred: whether two arms may touch depends on where their bases stand, so it is the author's call.

allow_link_obstacle_contact(link, obstacle, robot=None)

Marks contact between link (of robot) and obstacle as process-intended — a milling cutter in its stock. The pair stops counting as a collision (checking, planning, min_clearance); toolpath rapids deliberately ignore the exemption — while not cutting, any contact is a crash.

animate_carve method descriptor

animate_carve(
    timeline,
    stock,
    stages=None,
    voxel_size=0.001,
    cutter_radius=0.004,
    cutter_length=0.03,
    dt=0.01,
    robot=None,
    tcp_link=None,
)

Progressive material removal for a baked cycle: carves stock in stages equal time slices (default: one slice per second of cycle, capped at 240 — the display lags the tool by at most one slice, so this keeps the lag around a second), registers one display-only obstacle per changed slice (grouped under {stock}_cut/… in the scene tree, cheap AABB colliders — they never collide), and returns the timeline with the visibility windows injected: during playback — studio, USD export, and a replayed recording alike — the stock disappears as it is cut instead of starting pre-cut. The stock keeps colliding unchanged; everything here is presentation.

animate_paint method descriptor

animate_paint(
    timeline,
    target,
    applicator=None,
    stages=None,
    patch_size=0.01,
    dt=0.01,
    gate=None,
    spec=None,
    facing=None,
    facing_tolerance=Ellipsis,
    occlusion=True,
    robot=None,
    tcp_link=None,
    trigger_signal=None,
    style="amount",
    paint_color=None,
    substrate=None,
)

Progressive film build-up for a baked cycle: re-walks the coat in stages equal time slices (default: one slice per second of cycle, capped at 60 — the display lags the gun by at most one slice), registers one display-only obstacle per changed slice (grouped under {target}_film/…, cheap AABB colliders — they never collide) each carrying the film's colour key, and returns the timeline with the visibility windows injected: during playback — studio, USD export, and a replayed recording alike — the target's own colour gives way to the film building up on it. Optionally writes the effective spray trigger as signal trigger_signal (declare it first) for a timing lane and a spray-cone effect. The target keeps colliding unchanged; everything here is presentation.

Stages walk at patch_size — coarser than a spray_coat for the numbers, since a mesh per stage is what a viewer has to carry — with the same trigger rules (applicator, gate, brushes) as spray_coat. Coloured by amount by default (a build-up is about how much paint is there: the ramp — in paint_color, if given — runs from a light wash to the full colour at the spec's high edge, or the final maximum without one), on the target's own colour; style="spec" colours every stage against the band instead.

attach method descriptor

attach(name, link=None, touch_links=None, robot=None)

Attaches an obstacle to a robot link at its current relative pose — a grasp. While attached the object follows the link (live, in planning, and in playback) and collides as part of the robot. link=None uses the TCP link; touch_links=None allows contact with the link's subtree (the gripper).

auto_assign_io method descriptor

auto_assign_io(sequences=None, reassign=False)

Gives every unbound point a channel, deterministically: points in table order, channels in declaration order, on the point's host and the stations uplinked to it, first free channel of a compatible family (safety points prefer safety channels). Existing bindings are kept; reassign=True first drops the bindings an earlier run placed (hand bindings keep their channels). Points on an implicit host (<cell>, <robot>) are not placed — declare the node that runs their program. Returns the report afterwards.

bind_input method descriptor

bind_input(
    name,
    node,
    channel,
    tag=None,
    field=None,
    invert=False,
    contact=None,
    safety=False,
    voltage=None,
    logic=None,
    note=None,
)

Wires an input point ("beam_pick", "line" for a device's in-position input, "far.done" for a robot's done contact) to a channel of node. invert=True flips the wire level (NC wiring); contact ("no" / "nc"), field (the device on the far end), voltage / logic ("pnp" / "npn") and note document it.

bind_output method descriptor

bind_output(
    name,
    node,
    channel,
    tag=None,
    field=None,
    invert=False,
    contact=None,
    safety=False,
    voltage=None,
    logic=None,
    note=None,
)

Wires an output point ("conv" for a run coil, "vacuum" for a coil, "line.index" for an indexed-transfer start, "far.start" for a robot start) to a channel of node. Same keywords as bind_input.

bom method descriptor

bom()

The bill of materials derived from the scene: robots and their tools (catalog identity when loaded from the catalog), conveyors / axes / vehicles, sensors and I/O nodes — each listed whether or not it has been identified — plus every obstacle or group a part was pinned to. Identical products merge into one row with the quantity summed.

brush method descriptor

brush(name)

{"applicator", "flow", "lead", "lag"} of a declared brush.

cell_report method descriptor

cell_report(
    timelines=None,
    *,
    scenarios=None,
    deliverables=None,
    clearance_dt=Ellipsis,
    title=None,
    ground_z=0.02,
    sequences=None,
)

Gathers the cell report: robots, the cycles you pass (timelines — a SequenceTimeline, a list, or a {name: timeline} dict; each with its step spans, robot utilization and, unless clearance_dt=None, the tightest clearance re-scanned against the scene it was baked from), the I/O map's counts and findings, the scenario matrix (scenarios= a ScenarioRuns — its runs also stand in for timelines when none are given), the BOM's totals, the plan-view footprint, and the SHA-256 of every file in deliverables (external attachments with unverified provenance). sequences scopes the report's I/O summary; default all. For a common snapshot, fresh bakes and verified files, use bt.export_cell. A reading surface — pytest keeps the asserts.

check method descriptor

check(*, sequences=None, timeline=None)

Every static check in one report — the I/O lint, each sequence walked for dangling references, unidentified equipment lines and the requirement comparison (spec_short / spec_unknown). Returns a bt.select.CheckReport (ok, findings, to_json(), to_markdown()); botrail check prints the same thing.

check_collisions method descriptor

check_collisions()

Colliding pairs at the current configuration, as ((kind, name), (kind, name)) tuples with kind "link"/"obstacle".

check_paint method descriptor

check_paint(
    name,
    target,
    standoff=None,
    max_incidence=Ellipsis,
    max_range=None,
    step_pos=0.005,
    step_rot=0.05,
)

Checks a toolpath as a spray program against obstacle target before anything is baked: every feed sample (rapids are not spraying) looks along its spray axis — the TCP's -Z, against the tool axis — and reports standoff and incidence, judged against standoff (acceptable band, meters) and max_incidence (steepest acceptable angle, radians). Pure geometry: no robot is involved, so this runs before one is chosen. at on the issues is meters along the path.

Like check_toolpath, the findings are drawn on the path in the studio until the next check or edit of that path.

check_toolpath method descriptor

check_toolpath(
    name,
    robot=None,
    tcp_link=None,
    step_pos=0.005,
    step_rot=0.05,
    jump_threshold=0.5,
    axis_tolerance=0.0,
    spin="greedy",
)

Attempts every sample of a toolpath and reports all failures (unreachable / IK-branch jump / collision) without aborting — the pre-teach "which points can I not reach" face diagnosis.

clear_toolpath_marks method descriptor

clear_toolpath_marks(name)

Clears the marks a check left on toolpath name.

declare_io method descriptor

declare_io(
    name,
    role=None,
    kind=None,
    safety=False,
    pair=None,
    note=None,
)

An exception to the derivation, or an unmodelled point. role is "input" (an external contact whatever the sequences do), "output" (a coil — also promotes a magazine to a real feeder), "internal" (a relay, no I/O) or "exclude" (off the table). A name the scene does not have becomes a new declared point when role is input or output. kind overrides the channel type ("safe_di", ...), safety marks the safety class, pair names the other channel of a two-channel safety input.

define_applicator method descriptor

define_applicator(name, applicator)

Adds or replaces a toolpath (a continuous Cartesian process path, see bt.toolpath). toolpath is the dict built by bt.toolpath.builder() / bt.toolpath.from_gcode() — or its JSON string. Targets live in the part frame named by its frame key (resolved at bake time, so moving the frame re-solves the path). Declares a spray applicator under name — the dict bt.paint.applicator(...) builds — so brushes can refer to it. Validated now, not at bake time.

define_brush method descriptor

define_brush(name, applicator, flow=1.0, lead=0.0, lag=0.0)

Declares a brush: a named process setting a toolpath's strokes run with — applicator (a define_applicator name) at flow times its calibrated flow, opened lead seconds before each stroke with this brush begins and closed lag seconds after it ends. The program's own trigger, per stroke: pass the brush on ToolpathBuilder.feed(...) (or bt.paint.strokes(brush=...)), and the film integrator sprays each stroke with it; feed moves that name no brush in such a path run with the gun off.

define_signal method descriptor

define_signal(name, initial=False)

Declares (or re-initializes) an internal signal — a PLC internal relay written by bt.seq.set_signal actions and read by bt.seq.signal transitions.

detach method descriptor

detach(name)

Detaches an obstacle; its pose freezes where the robot holds it.

disallow_link_obstacle_contact(link, obstacle, robot=None)

Removes an allowed-contact entry added by allow_link_obstacle_contact.

export_bom method descriptor

export_bom(path, format=None)

Writes the BOM to path; the format follows the extension (.csv, .md, .json) unless format says otherwise.

export_interlocks method descriptor

export_interlocks(path, sequences=None)

Writes interlocks() to path; the format follows the extension (.md, .csv, .json).

export_io_list method descriptor

export_io_list(path, sequences=None)

Writes the I/O list to path; the format follows the extension (.csv, .md, .json).

export_layout method descriptor

export_layout(
    path,
    format=None,
    *,
    scale=100.0,
    units="mm",
    ground_z=0.02,
    frames=True,
    labels=True,
    reach=True,
    grid=Ellipsis,
    title=None,
)

Writes the layout sheet to path; the format follows the extension (.svg, .dxf, .json) unless format says otherwise. The other keywords are layout()'s.

export_plcopen method descriptor

export_plcopen(
    path,
    sequences=None,
    *,
    name="cell",
    cycle=True,
    task_interval_ms=10,
)

Writes plcopen() to path (.xml).

export_topology method descriptor

export_topology(
    path,
    sequences=None,
    layers=None,
    include_cosmetic=False,
)

Writes io_topology to path; the format follows the extension (.mmd / .md Mermaid, .dot / .gv Graphviz, .json).

export_usd method descriptor

export_usd(path, trajectory=None, fps=60.0, robot=None)

Writes the scene to a USD layer that opens in usdview / Omniverse / Blender. With a trajectory, an animation: robot link motion as timeSamples, obstacles as prims, grasped objects riding along — robot names the instance the trajectory belongs to (required when the scene has several). Without one, the cell as it stands: robots at their current joint positions, every visible obstacle at its pose, toolpaths and cameras — the static layer a layout is handed around as. The extension picks the serialization — .usda text, .usdc/.usd binary crate (about half the size). USD-sourced robots reference their original stage (assets copied to a sibling <stem>_assets/ directory); URDF robots are authored from the model's visuals. Returns exporter warnings.

footprint method descriptor

footprint(ground_z=0.02)

The plan-view extent of the equipment as a dict — min, max (x, y in metres), width, depth, area (m²), height (tallest non-ground item). Ground is anything whose top is at or below ground_z.

frame method descriptor

frame(name)

Pose of a named frame as (position, quaternion_xyzw) — e.g. scene.set_robot_base_pose(*scene.frame("/World/mount")).

generate_python method descriptor

generate_python()

Generates a Python script that rebuilds this scene with the botrail API (same content as the studio's "Export Python").

grasp_close method descriptor

grasp_close(
    name,
    robot=None,
    joints=None,
    closed=None,
    clearance=None,
)

Solves the joint values that close the robot's gripper on obstacle name, to a signed clearance from its surface (the default is half a millimetre of overtravel — measured to report contact reliably under physics without disturbing the part). Returns {joint: value}, ready to hand to bt.seq.ramp.

Pose the grasp first — the solve runs at the current configuration and the part's current pose, the same contract as attach. Drive joints default to every actuated joint below the tool mount (mimic followers close with their drivers); a limitless drive joint needs its fully-closed value in closed.

interlocks method descriptor

interlocks(sequences=None)

The interlock table over sequences (every sequence by default): one row per output a step switches — a signal, a device command, a robot motion or ramp, a grasp — with the condition that admits the step (the previous step's transition, an arm's condition, the cycle's last transition for a first step), the steps it follows and the inputs the condition reads, each classified (sensor, signal, device lane, device, robot), a signal traced to the program and step that writes it, an address where the host has it bound. The control designer's interlock sheet, derived rather than typed.

io_list method descriptor

io_list(format='csv', sequences=None)

The I/O list as text: format is "csv", "md" (Markdown table) or "json" (raw fields, step indices included).

io_map method descriptor

io_map()

The assignment layer as authored — nodes, bindings, declarations. Pass it to to_script(io=...) to project a newer assignment onto a timeline baked earlier.

io_points method descriptor

io_points(sequences=None)

The cell's I/O points, derived from how the sequences use the scene's names (nothing to author): sensors are inputs, coils and device commands are outputs, signals read or written across controllers are handshake wires, robots driven from another host get start/done points. sequences picks the program set (default: every sequence — pass what you would pass to simulate_sequences when alternative programs coexist). See docs/guides/io-map.md.

io_report method descriptor

io_report(sequences=None)

Lint findings over the derived I/O map: name clashes, unreferenced definitions, numeric (word/analog) points, programs on the implicit cell host. assert scene.io_report().errors() == [] is the CI form.

io_topology method descriptor

io_topology(
    format="mermaid",
    sequences=None,
    layers=None,
    include_cosmetic=False,
)

The electrical topology as text: format is "mermaid" (a flowchart LR for Markdown), "dot" (Graphviz) or "json". layers filters the edges — any of "functional", "io", "network", "wiring", "safety" (default: everything). Magazines stay out unless include_cosmetic=True.

joint_positions_of method descriptor

joint_positions_of(name)

Joint configuration of the robot instance named name.

layout method descriptor

layout(
    format="svg",
    *,
    scale=100.0,
    units="mm",
    ground_z=0.02,
    frames=True,
    labels=True,
    reach=True,
    grid=Ellipsis,
    title=None,
)

The plan-view layout sheet as text: format is "svg" (a self-contained drawing, scale pixels per metre), "dxf" (a minimal R12 file for 2D CAD, in units"mm" or "m") or "json" (the drawn items in world metres). The sheet is derived from the scene: every visible obstacle as its footprint (convex hulls of primitives, bounding boxes of meshes), robots as base marks with the catalog reach as a dashed circle, conveyor / sink zones, axis travel and vehicle routes, sensor zones and beams, named frames, labels (pinned parts first, then named groups), a metre grid and the overall dimensions. Anything whose top sits at or below ground_z is floor: drawn faint, left out of the extents.

lidar_scan method descriptor

lidar_scan(name, t=None, noise=0.0, seed=0)

Simulates one sweep of the named lidar: one ray per beam at the scanner's angular resolution, against the scene's collision shapes — obstacles and robot links alike, massing bodies included. That is the honest complement of capture_depth (the rendered meshes): what the scan sees is what the cell can hit. Blind-spot analysis in one call: 0.0 ranges are no-returns, hits names what each beam struck, points()/save_ply() give the cloud. Deterministic — two calls return identical data.

Without t the sweep sees the scene as it stands (a parked vehicle, the current joint pose). t sweeps at that instant of the last baked cycle instead — joints, moved objects and the vehicle the scanner rides all follow the timeline's tracks (clamped to the duration; simulate a sequence first).

noise adds Gaussian range noise, 1σ meters (a datasheet's ±3 cm accuracy reads as noise=0.03) — what a beam hits never changes, only how far it reports it, clamped to the measuring band. Still deterministic: the draw is a pure hash of seed, beam and instant, so the same call repeats bit-for-bit and a different seed is an independent stream.

link_pose(link_name, robot=None)

World pose of a link as (position, quaternion_xyzw).

link_pose_at(link_name, joints, robot=None)

World pose of link_name at joint configuration joints — forward kinematics without moving the robot (its current joints and any connected studio are untouched). bt.select.requirements measures taught targets through this.

load_project staticmethod

load_project(path)

Loads a .botrail project file into a fresh scene (robots included). URDF robots rebuild from the embedded XML; USD robots re-import from the referenced stage path.

load_urdf method descriptor

load_urdf(
    path,
    prefix=None,
    position=None,
    quaternion=None,
    args=None,
    geometry="visual",
    frames=True,
    package_paths=None,
)

Imports a URDF or xacro as scenery: every visual becomes an obstacle named <prefix>/<link>, posed at the model's zero configuration and placed at position / quaternion. Links that carry no geometry become named frames (see frame()), so a file can name where the next thing mounts. args fills the file's $(arg …) substitutions, which is what lets one parametric file draw every size a product is sold in. geometry="collision" reads the collision shapes instead of the visuals. Returns the obstacle names it added.

This is furniture, not a machine: joints are taken at zero and nothing here articulates. A robot is Robot.from_urdf / Robot.from_xacro and add_robot.

load_usd method descriptor

load_usd(path, prefix=None, search_paths=None)

Imports the static geometry of a USD stage (usda/usdc/usdz — references, variants, and instancing are composed) as obstacles, normalized to meters / Z-up. Leaf Xform/Scope prims become named frames (see frame()), usable as robot mount points. Obstacle and frame names are the prim paths, optionally prefixed. Returns the added obstacle names.

min_obstacle_distance method descriptor

min_obstacle_distance()

Minimum robot-obstacle distance (0 when colliding); None without obstacles.

motion_segments method descriptor

motion_segments(name)

Segments of a motion as (kind, goal_positions) tuples.

mount_robot method descriptor

mount_robot(
    device,
    offset_position=None,
    offset_quaternion=None,
    robot=None,
    gait=None,
    spin=None,
)

Puts a robot on a vehicle: from here its base is derived from that vehicle's frame, offset away, and re-derived every scan tick — an arm and a chassis become an AMR. Planned motions cannot start while the vehicle is driving (a plan is baked in world coordinates); ramps can, which is how an arm stows itself on the move.

With a gait (a bt.Gait) the robot is the vehicle's legs: it walks whenever the vehicle drives, and stands in the gait's stance when it does not. The offset then defaults to the one that puts the stance feet on the vehicle plane, and the robot is set to its stance. spin is presentation: {joint: rad/s} turned while the vehicle is off its starting ground or moving — a multirotor's propellers, signed so counter-rotating pairs read right. Continuous joints only; no check reads the phase (the collision stays the swept solid the catalog authors).

obstacle_bounds method descriptor

obstacle_bounds(name)

World-frame axis-aligned bounds of an obstacle, as (min, max). A cell that has to sit a workpiece on a pallet asks the geometry where its underside is instead of hard-coding a measured number that quietly stops matching when the mesh is rebuilt.

obstacle_color method descriptor

obstacle_color(name)

An obstacle's display colour as linear RGB, or None when it has none and the viewer picks the shading.

obstacle_material method descriptor

obstacle_material(name)

(metalness, roughness), or None when the obstacle has no authored material.

obstacle_pose method descriptor

obstacle_pose(name)

World pose of an obstacle as (position, quaternion_xyzw).

part method descriptor

part(name)

The part pinned to name as a dict (target, kind, catalog, manufacturer, model, category, description, qty, attributes), or None.

parts method descriptor

parts()

Every pinned part, in authoring order (see part() for the shape).

plan method descriptor

plan(
    goal,
    max_iters=10000,
    seed=None,
    broadcast=True,
    robot=None,
)

Plans a collision-free, time-parameterized trajectory from the current configuration to goal (joint positions in DOF order). With broadcast=True (default) the result is also pushed to connected studio clients for preview playback.

plan_motion method descriptor

plan_motion(motion, seed=None, broadcast=True)

Plans every segment of motion from the current configuration into one trajectory (rest-to-rest at segment boundaries). With broadcast=True the result is pushed to connected studios.

plan_to_pose method descriptor

plan_to_pose(
    position,
    quaternion=None,
    link=None,
    max_iters=10000,
    seed=None,
    broadcast=True,
    robot=None,
)

IK to the given pose, then plan to the found configuration.

plan_toolpath method descriptor

plan_toolpath(
    name,
    robot=None,
    tcp_link=None,
    step_pos=0.005,
    step_rot=0.05,
    jump_threshold=0.5,
    rapid_speed=None,
    axis_tolerance=0.0,
    spin="greedy",
)

Bakes a toolpath into one continuous trajectory: seed-continuous IK along the resampled path (5-DOF axis-aligned where the spin is free), collision-checked per sample, then time-parameterized in one piece with the commanded feed as a floor — the TCP holds the feed and slows only where joint limits force it. segment_ends on the result marks each move's completion time. The trajectory starts at the path's first target; author the approach separately.

spin picks how the free rotation about the tool axis is chosen: "greedy" (seed-continuous, milliseconds) or "optimize" (Descartes-style global pass over a spin grid — spends spin early to stay solvable late; seconds). axis_tolerance (rad) permits lead/tilt deviation from the authored axis on spin-free samples.

play_usd_animation method descriptor

play_usd_animation(
    path, force_transforms=False, robot_roots=None
)

Plays a baked USD recording (an Isaac Sim capture or a botrail export) on the scene's robots and broadcasts it to the studio. Joint playback is used when the layer carries JointStateAPI samples for every actuated joint; otherwise the recorded body transforms are replayed directly (force_transforms forces the latter). With several robots each is located at /World/<sanitized instance name> (the export convention); robot_roots maps instance names to prim paths when the recording placed them elsewhere. Returns {"mode", "duration", "warnings"}.

plcopen method descriptor

plcopen(
    sequences=None,
    *,
    name="cell",
    cycle=True,
    task_interval_ms=10,
)

The sequences as PLCopen XML (IEC 61131-10, TC6 v2.01): one SFC program per sequence (sequences= a subset; default all), steps with their entry actions and transitions, select as a selection divergence, and the cycle jump at the end (cycle=False parks the program in a final step). Conditions are ST expressions; device coils and commands write the I/O map's variables (declared once as resource globals, with AT addresses from PLC-side bindings); robot commands call stub function blocks the control engineer replaces — or the start / done handshake where the map says the robot is driven from another host. Opens in Beremiz / OpenPLC Editor. Deterministic (fixed timestamps).

remove_frame method descriptor

remove_frame(name)

Removes a named frame.

remove_io_node method descriptor

remove_io_node(name)

Removes a node and every binding on it.

remove_part method descriptor

remove_part(name)

Unpins the part on name.

rename_robot method descriptor

rename_robot(robot, name)

Renames a robot instance, returning the name it actually got (a name already taken is uniquified). Sequence actions, robot_done conditions and a zone sensor's watch list follow the robot, so a cell can be renamed after it has been authored.

requirements method descriptor

requirements(*, sequences=None, margin=0.1, timeline=None)

What every bill-of-materials line must be able to do, derived from the cell (payload from the tool and the grasped parts, reach from the taught targets, a beam's span, a conveyor's size and load, ...) and compared with what the chosen part says. Returns a bt.select.Requirements (rows, findings(), to_markdown(), to_json()); botrail derives and compares — it does not choose.

robot_base_pose_of method descriptor

robot_base_pose_of(name)

World base pose of the robot instance named name.

robot_of method descriptor

robot_of(name)

The model of the robot instance named name.

save_project method descriptor

save_project(path)

Saves the whole cell — robots (URDF embedded, USD by reference), joint state, obstacles, frames, motions, sequences, signals, sensors, and devices — as a .botrail project file. Plain JSON when everything is self-contained; a zip archive (project.json + assets/) when mesh files are referenced, so the file stays portable across machines.

scan_sweep method descriptor

scan_sweep(name, fps=10.0, noise=0.0, seed=0)

One sweep per frame over the whole last baked cycle, on the export grid (1/fps steps plus the final instant). The corridor survey for a riding scanner: merge the frames' points() and the drive's visibility is one cloud. noise/seed as in lidar_scan — every frame draws its own beams, so the merged cloud thickens the way a real drive's does.

sequence method descriptor

sequence(name)

Starts (or replaces) a PLC-style sequence and returns a builder: scene.sequence("pick").step("run", actions=[bt.seq.motion("go")]).

set_gripper_drive method descriptor

set_gripper_drive(
    joints=None,
    max_force=None,
    stiffness=None,
    damping=None,
    finger_mass=None,
    robot=None,
)

Declares a force-limited drive on the robot's gripper joints (design-grasping.md G3). Under physics=True the driven fingers become dynamic bodies moved by force-capped position motors: a grasped part is held by friction, so a too-weak cap or a too-fast carry slips for real, and grasp_report() reads the slip back. Without a physics backend the declaration is inert.

  • joints — driven actuated joints; default derives every actuated joint below the tool mount (mimics follow either way).
  • max_force — per-joint force cap in N (prismatic) / N·m (revolute); default is each joint's URDF effort limit.
  • stiffness, damping — motor gains; the defaults saturate the cap within ~a millimetre and bound the free speed.
  • finger_mass — mass floor per driven finger body, kg (default 0.2, a finger-plus-carriage moving mass). The engine's contact stiffness scales with the pair's masses, so a mesh-derived few-gram finger cannot develop a newton-scale clamp.
set_link_material(
    link, friction=None, restitution=None, robot=None
)

Sets a robot link's contact material for physics bakes — the name-keyed sibling of set_physics, for the surface the robot brings to a contact (a fingertip's rubber pad). Inert without a physics backend; links keep the engine default (friction 0.5, restitution 0) unless set.

set_obstacle_color method descriptor

set_obstacle_color(name, color)

Sets an obstacle's display colour, linear RGB in 0..1. None hands the shading back to the viewer. Display only — collision and planning see the same geometry either way.

set_obstacle_enabled method descriptor

set_obstacle_enabled(name, enabled)

Includes/excludes an obstacle from collision checking (it keeps rendering in the studio either way).

set_obstacle_legend method descriptor

set_obstacle_legend(name, title='', stops=None)

Attaches a colour key to an obstacle whose colours mean something — stops is a list of ((r, g, b), label) swatches top to bottom, linear RGB, empty labels allowed — or clears it with stops=None. The studio draws it beside the viewport while the obstacle is in the scene. Presentation only.

set_obstacle_material method descriptor

set_obstacle_material(name, metalness=None, roughness=None)

Sets how an obstacle's surface takes light. Passing neither knob clears the material, handing the choice back to the viewer.

set_obstacle_visible method descriptor

set_obstacle_visible(name, visible)

Hides or shows an obstacle without touching whether it collides. A hidden obstacle is still a real obstacle: this is how a workpiece carries a display mesh and its convex collision pieces at once.

set_obstacle_walkable method descriptor

set_obstacle_walkable(name, walkable=True)

Marks an obstacle's top face as a place a walking machine's feet may stand — a stair tread, a mezzanine slab. Footfalls snap onto it and the walker may touch it (nobody collision-checks a floor against the machine standing on it); everything else still collides with it normally. Only an upright box (yaw rotation is fine) can be walkable.

set_part method descriptor

set_part(
    name,
    *,
    kind=None,
    catalog=None,
    manufacturer=None,
    model=None,
    category=None,
    description=None,
    qty=1,
    attributes=None,
    **extra,
)

Pins a part — what the thing is commercially — to a resident or group by name: a robot, a tool in its stack (by its BOM row name, arm/tool, arm/tool2 — the identity of a made bracket, or the last word on a catalog one), a device, a sensor, an I/O node, an obstacle, or an obstacle group (everything under name/ — an imported subtree, a generated fence). Identity is optional and free-form: catalog ("id" or "id@revision" or (id, revision)), manufacturer, model, category ("conveyor", "structure.fence", ...), description, qty (how many the target stands for), and any further keywords or attributes={...} as free attributes (numbers are summed by bom().total(key), text is carried). Pass kind= ("robot", "tool", "device", "sensor", "io_node", "obstacle", "group") when a name lives in several name spaces. Re-pinning replaces. Returns the kind resolved. The BOM (bom()) is derived from these plus the catalog identity of robots and tools.

set_physics method descriptor

set_physics(
    name,
    dynamic=None,
    mass=None,
    friction=None,
    restitution=None,
    linear_damping=None,
    angular_damping=None,
    ccd=None,
)

Marks an obstacle for physics: dynamic=True hands its pose to the engine during a physics bake (simulate_sequence(..., physics=True)) — it falls, collides, settles. All properties are inert on a kinematic bake, so authoring them never changes an existing cell. Repeated calls merge: only the knobs you pass change. mass in kg (unset derives it from the collision shape's volume at 1000 kg/m³); ccd enables continuous collision detection for small fast parts.

set_robot_base_pose method descriptor

set_robot_base_pose(position, quaternion=None, robot=None)

Places the robot root at the world-frame pose and pushes the new state to connected studio clients.

set_tcp_target method descriptor

set_tcp_target(
    position,
    quaternion=None,
    link=None,
    max_iters=100,
    robot=None,
)

Solves IK toward the given pose (seeded from the current configuration), applies the best-effort result to the scene, and pushes it to connected studio clients. quaternion=None matches position only; link defaults to the TCP link.

show_film method descriptor

show_film(film, name=None)

Puts a film map in the picture: the coated target's own colour gives way to film's heatmap mesh, registered as a display-only obstacle named {target}_film (disabled for collision, cheap collider) with its micron colour key attached, so the studio draws the legend beside the viewport. Collision and planning still see the original target; everything here is presentation. Returns the obstacle name; remove it and re-show the target to undo.

simulate_scenarios method descriptor

simulate_scenarios(
    names,
    scenarios=None,
    dt=0.01,
    max_duration=120.0,
    plan_resolution=None,
)

Rolls the same sequences under a set of scenarios — the cell's test-case matrix in one call. scenarios=None runs baseline plus every defined scenario. A scenario that fails (bad delta, plan failure, timeout) is collected into the result's errors rather than aborting the sweep — finding the failing scenario is the point. Each run is deterministic, so coverage and cycle times off the result are CI-assertable numbers.

simulate_sequence method descriptor

simulate_sequence(
    name,
    dt=0.01,
    max_duration=120.0,
    plan_resolution=None,
    scenario=None,
    toolpath_spin=None,
    physics=None,
)

Rolls out a sequence with the PLC scan loop against a snapshot of this scene (motions plan at their step, grasped objects ride along) and returns the baked timeline. Also broadcasts the result to connected studio clients for playback.

scenario applies a named initial-state delta (add_scenario) to the snapshot first — the live scene is never touched. None and "baseline" both mean the scene as it stands.

simulate_sequences method descriptor

simulate_sequences(
    names,
    dt=0.01,
    max_duration=120.0,
    plan_resolution=None,
    scenario=None,
    toolpath_spin=None,
    physics=None,
)

Rolls out several sequences concurrently — the PLC picture of a line: one program per station plus a transfer program, each a plain serial SFC, synchronized only through signals and sensors. One scan tick advances every program in list order, so the bake stays bit-identical run to run; the result is a single timeline whose step spans carry program/step names.

Every robot, device, and written signal must be commanded by at most one of the programs — two programs driving one resource is rejected up front, like two PLC programs writing one coil. plan_resolution tightens the planner's edge-validity stride (rad, joint-space L2). The default 0.05 samples a big arm's sweep every ~10 cm of TCP travel — coarse enough to step across sheet metal, so cells full of 12 mm flanges pass 0.005.

timeline_from_trajectory method descriptor

timeline_from_trajectory(
    trajectory, robot=None, label="trajectory"
)

Wraps a planned trajectory as a single-robot SequenceTimeline so the timeline consumers — studio playback, export_usd, min_clearance — accept it without authoring a sequence. Other robots hold their current pose; objects stay static. Script export is not supported on the result.

unbind_input method descriptor

unbind_input(name, node=None)

Drops the binding of an input point — on node, or everywhere.

unbind_output method descriptor

unbind_output(name, node=None)

Drops the binding of an output point — on node, or everywhere.

IoPoint

Returned by Scene.io_points: one derived I/O point of the cell (see The I/O map).

IoPoint

One derived I/O point of the cell (see Scene.io_points).

aspect property

aspect

The facet for device commands and robot handshakes ("index", "dispatch", "station", "position", "speed", "start", "done", "program"), or None.

direction property

direction

"input" or "output", from the host's side.

host property

host

The controller that owns the point: "<cell>", "<robot name>" (implicit placement) or a declared node; None when nothing pins it.

kind property

kind

Channel type: "DI", "DO", "Word", "AO", ...

label property

label

name or name.aspect — the label the tables use.

name property

name

The scene name the point belongs to (signal, sensor, device, robot).

readers property

readers

(sequence, flat step index, step name) of the steps that read it.

safety property

safety

source property

source

The derivation rule that produced it: "sensor", "signal:handshake", "signal:internal", "signal:write-only", "signal:read-only", "device:run", "device:done", "device:command", "device:cosmetic", "robot:start", "robot:done", "robot:program".

status property

status

"unbound", "internal" (a relay, no I/O), "cosmetic" (magazine), "constant" (a coil that is on from t = 0 and never commanded).

writers property

writers

(sequence, flat step index, step name) of the steps that write the point (coil writes, device commands, robot starts).

IoReport

Returned by Scene.io_report: the findings over the derived I/O map, by severity.

IoReport

The findings of Scene.io_report().

findings property

findings

Every finding, most severe first.

ok property

ok

True when there are no errors.

to_json method descriptor

to_json()

The findings as JSON: {"ok": bool, "findings": [{"severity", "code", "message", "at": [[sequence, step index, step name], ...]}]}.

IoFinding

One entry of an IoReport.

IoFinding

at property

at

The steps the finding is attributed to, as (sequence, flat step index, step name).

code property

code

The finding code ("name_clash", "unreferenced", "word_unexpressible", "implicit_host", ...).

message property

message

severity property

severity

"error", "warning" or "info".

IoMap

Returned by Scene.io_map: the assignment layer as authored (nodes, bindings, declarations) — hand it to to_script(io=...).

IoMap

The assignment layer of a scene's I/O map (see Scene.io_map).

bindings property

bindings

(label, direction, node, channel) per binding.

decls property

decls

Declared names.

nodes property

nodes

Node names, in declaration order.

to_json method descriptor

to_json()

The layer as JSON — the same form the .botrail project stores.

Bom

Returned by Scene.bom: the bill of materials derived from the scene's parts (see Parts and the BOM).

Bom

The bill of materials Scene.bom() derives — one row per distinct product, in scene order.

rows property

rows

The rows as dicts: category, names (the residents the row stands for), manufacturer, model, catalog (id@revision), qty, description, attributes.

attribute_keys method descriptor

attribute_keys()

Every attribute column any row carries, sorted.

save method descriptor

save(path, format=None)

Writes the table to path; the format follows the extension (.csv, .md, .json) unless format says otherwise.

to_json method descriptor

to_json()

{"rows": [...], "totals": {...}}.

total method descriptor

total(key)

Σ qty × key over the rows carrying it as a number, or None when no row does (a missing figure must not read as zero).

unidentified method descriptor

unidentified()

Rows nothing identifies yet (no catalog reference, maker or model) — the purchasing to-do list.

InterlockTable

Returned by Scene.interlocks: every output a step switches against the condition that admits the step (see The interlock table).

InterlockTable

One lint finding of the I/O map. The interlock table Scene.interlocks() derives: rows are plain dicts (program, host, step, kind, target, output, condition, after, inputs); to_markdown() / to_csv() render the same rows for people and spreadsheets.

io_error property

io_error

Why hosts and addresses are missing, when the I/O map could not be derived; None when it could.

rows property

rows

One dict per output: program, host, step, kind, target, output, condition, after (the predecessor steps) and inputs (name, kind, written_by, address).

sequences property

sequences

The program set the table was taken over.

save method descriptor

save(path, format=None)

Writes the table to path; the format follows the extension (.md, .csv, .json) unless format says otherwise.

CellReport

Returned by Scene.cell_report: cycles, I/O, scenarios, machines, BOM totals, footprint and deliverable digests in one page (see Layout sheet and cell report).

CellReport

The cell report Scene.cell_report() gathers: robots, cycles, I/O, scenarios, machines, BOM totals, footprint, deliverable digests. Every section is a plain dict / list (JSON-shaped); to_markdown() renders the same data for people.

bom property

bom

BOM totals: rows, unidentified, by_category, totals.

cycles property

cycles

The cycles passed in: name, sequences, scenario, duration, steps (name, sequence, start, end), robots (robot, busy, utilization), clearance (distance, t, pair) and branches.

deliverables property

deliverables

The hashed deliverables: path, sha256, bytes.

footprint property

footprint

The plan-view footprint: min, max, width, depth, area, height.

io property

io

The I/O summary — points, by_kind, bound, unbound, internal, safety, nodes, findings — or None when the map could not be derived (see io_error).

io_error property

io_error

machines property

machines

The machine tools: name, category, manufacturer, model, catalog, door (name, drive, stroke_mm, speed, lanes, driven), buttons, controller, programs.

robots property

robots

The robots: name, dof, base, and catalog / manufacturer / model / reach when the catalog knows them.

scenarios property

scenarios

The scenario matrix: name, ok, duration, error.

title property

title

cycle_time method descriptor

cycle_time(name=None)

The cycle time of name (or of the first cycle), or None.

min_clearance method descriptor

min_clearance()

The tightest clearance over every cycle that measured one, or None.

save method descriptor

save(path, format=None)

Writes the report to path; the format follows the extension (.md, .json) unless format says otherwise.