Skip to content

Timeline

The baked result of a sequence: what every robot did, when every step ran, how every signal moved, and where every object was — for one cycle.

tl = scene.simulate_sequence("cycle")

tl.duration                      # cycle time, seconds
tl.step_span("feed").duration    # one step's baked interval
tl.signal("eye").rising_edges()  # when the beam broke
tl.min_clearance()               # tightest approach over the whole cycle
tl.export_usd("cycle.usda", fps=60)

The bake is deterministic — the same scene produces a bit-identical timeline every run — which is what makes these values usable as regression assertions.

SequenceTimeline

A baked sequence rollout: per-robot joint tracks, grasped-object motion, signal waveforms, and step spans (the timing chart).

branches property

branches

The path the bake took through branching steps, in resolution order: (sequence, step name, arm index). Untaken arms have no spans — this is how a timeline says which way it went.

contacts property

contacts

Touch episodes of a physics bake, in opening order: dicts with a, b (scene names; robot/link for an arm part), start, end (s), position (world, where the touch began) and peak_force (N, the episode's largest total contact force). Empty on a kinematic bake. Only pairs involving a dynamic body are recorded — "part hit the stopper at t=5.6 s with 8 N" is one entry here.

duration property

duration

Cycle time in seconds.

physics property

physics

The physics engine this bake stepped under ("rapier"), or None for the purely kinematic bake. A physics bake is deterministic per machine and build — not the cross-platform bit-identity the kinematic bake guarantees.

robots property

robots

Instance names of the robots on this timeline, in scene order.

scenario property

scenario

The scenario this bake ran under; None is the unmodified scene (baseline).

sequences property

sequences

Names of the sequences this timeline was rolled from, in scan order — the programs to_script can export.

signals property

signals

Signal waveforms as (name, [(time, value), ...]) edge lists.

step_spans property

step_spans

(step name, start, end) per step, in execution order.

trajectory property

trajectory

The sole robot's cycle track (see robot_trajectory; with several robots this is ambiguous — name one).

base_pose method descriptor

base_pose(t, robot=None)

Where a mounted robot's base was at time tNone for a robot bolted to the floor, whose base is a scene constant.

busy_seconds method descriptor

busy_seconds(robot=None)

Seconds a robot spent in motion (overlapping move intervals merged).

carve_stock method descriptor

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

Carves stock with the cutter swept along this cycle: a voxel subtraction in the stock's frame, returning the machined part as a mesh plus removed/remaining volume. Presentation and numbers — the cut can never contradict the plan in a kinematic world.

conveyor_stalls method descriptor

conveyor_stalls()

Stretches where a running conveyor drove under a tracked object that made almost no progress along the belt — a queue seating against its stopper (by design) or a genuine jam (not): dicts with object, device, start, end. The detector reports arrest; only the author knows intent.

diff method descriptor

diff(
    trace,
    *,
    tolerance=0.05,
    signals=None,
    align_on=None,
    io=None,
)

Compares this bake with a controller trace (bt.trace.load — a CSV path / text, a {name: [(t, value), ...]} dict, or a Trace) edge by edge, by name: matched edges within tolerance seconds, missing ones (baked, never seen), extra ones (seen, never baked). align_on= names a signal whose first rising edge sets the trace's clock against the bake's; signals= picks the names to judge; io= renames binding tags to point names. Returns a bt.trace.TraceDiff (ok, signals, findings(), to_markdown(), to_json()) — the offline commissioning check.

export_handshake_spec method descriptor

export_handshake_spec(path, io=None)

Writes handshake_spec() to path (Markdown).

export_script method descriptor

export_script(
    path,
    sequence=None,
    dialect="urscript",
    name=None,
    inputs=None,
    outputs=None,
    speed_scale=1.0,
    blend_radius=0.0,
    tcp_speed=0.25,
    tcp_accel=1.2,
    move_to_start=True,
    node=None,
    io=None,
)

Writes to_script output to path (see there for the semantics).

export_usd method descriptor

export_usd(path, fps=60.0, start=None, end=None)

Bakes the whole cycle to a USD animation layer (see Scene.export_usd): every robot + every obstacle, with grasped objects riding, releasing, resting — and handed over — exactly as simulated. A sole robot exports under the historical Robot prim; with several, each lands at /World/<sanitized instance name>. The extension picks the serialization: .usda text, .usdc/.usd binary crate at roughly half the size.

start/end clip the export to a window of the cycle. A line's full run is mostly repetition — one steady-state takt carries the whole story at a fraction of the bytes, which is what makes a line recording shippable at all.

feed_report method descriptor

feed_report(toolpath=None)

Feed adherence of a toolpath the cycle ran (StartToolpath). toolpath=None means the only one; name it when the cycle cuts several.

footfalls method descriptor

footfalls(robot=None)

The steps a walking robot's legs took, as (leg, lift, land, (x, y, z)) in landing order: the foot left its previous anchor at lift and has stood at the position since land. Empty unless the robot walks its vehicle (a bt.Gait on its mount).

grasp_report method descriptor

grasp_report(
    min_touches=2,
    grip_force_n=None,
    mu=None,
    payload_kg=None,
    safety_factor=2.0,
    max_slip_m=0.01,
)

Every grasp this bake performed — one dict per attach…release stretch, annotated from the physics contact record and checked against what numbers are at hand. Fields: object, robot, link, start, end, held_to_end, touched (tool link → peak contact force N around the attach; empty on a kinematic bake), released_touching (the release happened inside the squeeze — author "open, then detach"), mass_kg (as the bake resolved it; None without physics authoring), max_accel (largest carry acceleration, m/s²), slip_m (how far the part strayed from riding its carrier — friction holds under a gripper drive only, None on welds), and checks:

  • touch — at least min_touches distinct tool links touching at the attach (skip on a kinematic bake).
  • releasewarn when released inside the squeeze.
  • payload — grasped mass ≤ payload_kg, when both are known.
  • grip_forcegrip_force_n × mu × touching surfaces ≥ mass × (g + max_accel) × safety_factor, when force, mu, and mass are known (on a kinematic bake two surfaces are assumed).
  • hold — a friction hold's measured slip_m stays within max_slip_m (skip on welds and kinematic bakes).

The numbers default from what the cell already knows: a catalog gripper welded on with attach_tool supplies grip_force_n (its grip_force_min_n — the weakest stated setting — else grip_force_max_n) and payload_kg from its own specs, and mu defaults to the most slippery authored pairing of the touching links' materials and the part's (min rule — conservative for a holding check). Explicit arguments always win; the values used are reported back as grip_force_n / payload_limit_kg / mu.

handshake_spec method descriptor

handshake_spec(io=None)

The handshake specification of this bake as Markdown: every line between controllers — handshake signals, robot start / done / program handshakes, device command and in-position lines — with direction, both ends (node and channel when bound), the steps that write and wait on it, and its waveform (high spans, or the robot's start pulses and busy spans). The draft of the robot ⇔ PLC interface sheet, per scenario. io= projects a newer assignment onto a bake made before the wiring.

min_clearance method descriptor

min_clearance(dt=0.01)

The tightest robot-to-environment approach over the cycle, sampled every dt seconds against the scene the timeline was baked from (carried and conveyed objects replay their baked motion; robot-robot contact is already a hard rollout error). Raises when the cell has nothing to measure.

moves method descriptor

moves(robot=None)

A robot's move intervals as (label, start, end) — the intervals a motion (by name) or ramp drove it.

object_pose method descriptor

object_pose(name, t)

World pose of a grasped/tracked object at time t.

object_visible method descriptor

object_visible(name, t)

Whether a tracked object should be drawn at t. False only while it is stowed — waiting in a magazine, or taken off the line.

paint_report method descriptor

paint_report(
    target,
    robot=None,
    tcp_link=None,
    gate=None,
    standoff=None,
    max_incidence=Ellipsis,
    max_range=None,
    dt=0.01,
)

Checks what the robot actually did against the teaching rules of a spray program: every dt while the gun was spraying — gate high (or no gate) and inside a feed stroke, the same two triggers spray_coat uses — the TCP's spray axis is cast at target and the standoff and incidence read off the hit. standoff is the acceptable band in meters, max_incidence the steepest acceptable angle in radians. at on the issues is timeline seconds.

The baked twin of Scene.check_paint: that one checks the authored path before any robot is involved; this one includes whatever the solver did with the free spin and the tolerance it was given.

process_spans method descriptor

process_spans(robot=None)

The (start, end, brush) intervals a robot's toolpath moves spent spraying — the program's own process trigger, as opposed to rapids, gun-off moves, and the approach planned in from wherever the robot stood; brush is None in a program that names none. Merged, in time order. Empty when the robot ran no toolpath: then there is no program to say when the process was on, and spray_coat / paint_report take the whole timeline as process time.

robot_busy method descriptor

robot_busy(robot=None)

Intervals a robot was driven by a motion, ramp or toolpath, merged where they touch — the "busy" contact a robot controller would show a PLC, synthesized from the bake (a robot has no signal lane). Robot defaults to the scene's first.

robot_trajectory method descriptor

robot_trajectory(robot=None)

A robot's cycle joint track as a [Trajectory] (CSV/JSON export, joint access). Step boundaries land in segment_ends.

sample method descriptor

sample(t, robot=None)

A robot's joint positions at time t (clamped to the cycle).

settled_at method descriptor

settled_at(name)

When the object came to rest for good — the start of its trailing hold — or None while it was still moving (or held, or never tracked) at the horn. On a physics bake this is the moment the engine put the body to sleep for the last time.

signal method descriptor

signal(name)

The named waveform lane — an internal signal, a sensor, or a device's running state.

spray_coat method descriptor

spray_coat(
    target,
    applicator=None,
    robot=None,
    tcp_link=None,
    patch_size=0.005,
    dt=0.01,
    gate=None,
    spec=None,
    max_incidence=Ellipsis,
    facing=None,
    facing_tolerance=Ellipsis,
    occlusion=True,
    style="auto",
    paint_color=None,
    substrate=None,
)

Sprays applicator along this cycle and reports the film left on target: a thickness map as a colored mesh plus the numbers a paint engineer reads — in-spec area, holidays, paint used.

What sprays comes from the program: a toolpath whose strokes name brushes (scene.define_brush) sprays each with that brush's applicator, flow and trigger timing, and applicator may be left out; one that names none sprays every feed move with applicator (the dict bt.paint.applicator(...) builds), which is then required. The applicator's footprint is calibrated geometry, not fluid dynamics: no air flow, no electrostatics, so the electrostatic wrap around edges is not modeled and the absolute micrometers are only as good as the pattern fed in. Relative structure — lap streaks, thick corners, the film left by a stroke that lost speed — is the robust part, because the walk runs on the baked trajectory.

Two triggers decide when paint flows, and both must agree: gate names the PLC's enable signal (without one it is taken as always on), and the program's own trigger is the feed strokes of the toolpath the robot was running — rapids and the approach planned in from wherever the robot stood never spray, however the enable was authored. A timeline that ran no toolpath has no program to say when the process was on, so there the enable alone decides. spec is the acceptable film band in meters. style picks how the film map is coloured: "amount" (a sequential ramp, light to dark — how much paint; in paint_color if given, so it looks like the coat going on) or "spec" (diverging over the band: neutral on target, blue thin, red thick — the verdict); "auto" is "spec" when a spec was given. Bare patches wear substrate, or the target's own colour.

Statistics run over the surface the gun addressed — in range and within max_incidence of square on. A part's back face is not a holiday, and neither is the rim of a panel sprayed from above, which would otherwise swamp the film map with one grazing band. Deposition ignores the limit, so paint stays conserved.

facing names the job by the way it faces — a world direction, with only patches whose normal lies within facing_tolerance of it counted ((0, 0, 1) for "the top"). Without it the addressed set depends on the path: a rim swings into the mask as the gun turns around past the edge, so lengthening the overtravel quietly changes every denominator. Name the face for numbers that compare across programs.

step_span method descriptor

step_span(name)

The baked interval of the named step (assertion-friendly view of one step_spans row).

to_script method descriptor

to_script(
    sequence=None,
    dialect="urscript",
    name=None,
    inputs=None,
    outputs=None,
    speed_scale=1.0,
    blend_radius=0.0,
    tcp_speed=0.25,
    tcp_accel=1.2,
    move_to_start=True,
    node=None,
    io=None,
)

Renders one program of this timeline as a vendor robot script — the same steps that drove the simulation, with real I/O: inputs maps signal/device/robot names to digital input ports (level waits), outputs maps signal/device names to digital output ports (coil writes). Timers become sleeps; moves are the rollout's own planned sparse paths.

The program is named after the sequence (name overrides). The sequence must drive exactly one robot — a multi-robot cell exports one script per program. Approximations (unmapped device commands, waits that ran beside a move in simulation) are raised as Python warnings; what cannot be expressed at all (any_of waits, conveyor tracking) raises ValueError.

utilization method descriptor

utilization(robot=None)

Fraction of the cycle a robot spent moving, 0..1 — the line-balancing number. The bottleneck is whoever sits near 1, and this is what predicts where moving a spot lands the takt.

utilizations method descriptor

utilizations()

{robot: utilization} for every robot on the timeline.

vehicle_airborne method descriptor

vehicle_airborne(name)

Seconds the vehicle spent off its starting ground: every span that moves it, plus every hold above the altitude it started at — exact, the spans are closed form. For an aerial machine this is the motor-on time a declared flight_time_min must cover (hover at a station counts, waiting on the pad does not); for a ground machine it is simply its driving time. A vehicle that never drove flew 0 s.

with_trigger_signal method descriptor

with_trigger_signal(
    name="spraying", gate=None, robot=None, dt=0.01
)

The timeline with the effective spray trigger — the enable signal AND the program's own (feed strokes, brush lead/lag included) — written as signal name, replacing any lane of that name. What a timing chart shows as "spraying", and what a spray-cone effect (scene.add_spray_cone) should bind to; declare name with scene.define_signal first so the effect can be bound. Nothing else about the timeline changes.

Span

Returned by SequenceTimeline.step_span: one step's interval, in a form that reads well in an assertion (assert tl.step_span("feed").end <= 2.0).

Span

One step's baked interval on a timeline.

duration property

duration

end - start.

end property

end

End time in seconds.

name property

name

Step name.

start property

start

Start time in seconds.

SignalTrack

Returned by SequenceTimeline.signal. One boolean waveform lane — an internal signal, a sensor, or a device's running state — with edge and duty queries on top of it.

SignalTrack

A signal/sensor/device waveform lane on a baked timeline.

edges property

edges

(time, new value) edges, starting with (0, initial).

kind property

kind

Where the lane comes from: "signal" (internal relay, or a lane synthesized under a signal's name), "sensor" (input) or "device" (running / moving output).

name property

name

Lane name.

falling_edges method descriptor

falling_edges()

Times the lane turns OFF.

high_spans method descriptor

high_spans()

(start, end) intervals the lane is ON; an interval still open at the cycle end closes at duration.

high_total method descriptor

high_total()

Total ON time over the cycle.

rising_edges method descriptor

rising_edges()

Times the lane turns ON (the initial level at 0 is not an edge).

value_at method descriptor

value_at(t)

The level at time t.

Clearance

Returned by SequenceTimeline.min_clearance: the tightest robot-to-environment approach over the cycle, with the time and the pair it happened at. It compares against plain floats, so assert tl.min_clearance() > 0.05 works directly.

Clearance

distance property

distance

Distance in meters (0 while touching).

pair property

pair

The touching (robot side, obstacle) names while in contact.

t property

t

When it first happens (seconds on the timeline).