Skip to content

Introduce Lab Simulation for Visualization and Robotics - #2

Merged
marpaia merged 18 commits into
masterfrom
marpaia/simulator
Aug 17, 2026
Merged

Introduce Lab Simulation for Visualization and Robotics#2
marpaia merged 18 commits into
masterfrom
marpaia/simulator

Conversation

@marpaia

@marpaia marpaia commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

No description provided.

marpaia added 18 commits August 11, 2026 16:31
The event vocabulary, the lab.sim-trace.v0 document, its summary
derivation, and the facility description are documents, not runtime
behavior, so they live with the other formats. lab-runtime re-exports
them unchanged and keeps only the ports: EventSink, the sinks, and the
clock-coupled TraceSink. lab-scene can now read traces and facilities
without a lab-runtime edge.
A scene node's geometry can now be a real asset: AssetCatalog resolves
station kinds, labware ids, carrier ids, and 'room' against a facility's
assets directory, falling back to the dimensioned box that every
consumer can draw. bundle_assets copies referenced files beside the
scene and relativizes their paths, so a scene bundle is self-contained.
The USD export composes assets with prim references; glTF keeps the
fallback box and records the asset path in extras, since core glTF
cannot reference external meshes. Nodes gain rotation_z_deg for placed
stations.
facility.toml gains a [room] section (kit floor and walls, or an
environment asset key) and per-station position_mm/rotation_deg, with
placements validated against the room bounds. lab scene --facility
checks the plan's stations, resolves real meshes from the facility's
assets directory, lays stations out on the floor plan, and bundles
referenced assets beside the scene. Without a facility the schematic
row layout renders exactly as before.
lab scene --animated writes time samples over the same emission the
static layer uses: one timecode per simulated second, labware holding
home until each confirmed handoff and arriving on the destination's top
two seconds later, and a synthesized pipetting head per liquid handler
that follows the frames' deck coordinates and hides between programs.
Every geometry prim now binds a UsdPreviewSurface material by semantic,
so Omniverse, Blender, and usdview shade the bench without extra work.
Animated labware under a rotated ancestor is refused, not mis-posed.
ACES tone mapping, image-based lighting from a procedural room
environment, and a shadow-mapped key light replace the flat shading.
Mesh geometry loads its glTF asset and swaps out the fallback box,
keeping the box on failure. The facility's room shell renders inside-out
so the camera sees in from anywhere, nodes honor rotation_z_deg, and the
camera frames whatever was built: a bare bench or a whole room.
The player script ships inside the binary and reads the same two
documents the web player and USD stage consume: it builds Blender
objects from scene.json, keyframes labware and pipetting heads from
sim-trace.json, lights with the built-in sky or a supplied HDRI, and
renders through camera presets with EEVEE (preview) or path-traced
Cycles (final). ffmpeg assembles a movie when present; frames stay
either way. Blender is found, never bundled, and a missing install is
an error naming the fix. The end-to-end check is gated on LAB_BLENDER,
the same pattern as the Opentrons simulator tests.
Every renderer interprets the same scene and trace documents: assets
are facility-owned references with box fallbacks, the facility authors
the room, USD carries the animation, and the schematic tier stays
first-class. Scanned environments compose in later as the room asset
without changing any format.
Stations now carry cosmetic part assemblies built from the same
primitives every renderer draws: the STAR gets its towers, gantry beam,
back panel, front glass, and status light; the cycler its base, lid
seam, vents, and light; and every station stands on a bench. Parts are
children of the station node with a material vocabulary (frame, panel,
glass, accent) honored by the web player, glTF, USD, and Blender; the
station's own geometry stays the body the seat math measures.

Running the harness against a real Blender 5.2 fixed what only a real
Blender could: keyframe interpolation rides the new-keyframe preference
(the fcurve API changed shape across 4/5), the EEVEE identifier is
probed across generations, camera clip ranges suit millimeter scenes,
script failures now fail the process (--python-exit-code), and the room
shell renders without driving the camera framing. Facility and
run-document errors no longer print their cause twice.
From a package directory, the three commands take no arguments:
lab build compiles the manifest's default target into .lab/build, and
lab simulate, lab scene, and lab render walk that target's waves in
order. The facility resolves by convention: facility.toml at the
package root, then the manifest's [build] facility pointer under
facilities/, with --facility always winning. lab render is the whole
flow in one command — it regenerates the simulation and the animated
scene idempotently before Blender runs, so a fresh checkout needs only
build and render.

golden-gate now defaults to the workcell-star target and keeps its
facility at the root, so its README flow is three bare commands.
A named run directory keeps its exact previous behavior on every
command.
A preview render is dominated by per-frame overhead, so --jobs splits
the frame range across several Blender processes writing distinct
frames into the shared directory; the timeline is identical in every
chunk, so frames line up exactly. Previews default to half the cores
(capped at four) and path-traced finals to one process, which already
saturates the GPU. The player only touches use_nodes on datablocks
that still lack a node tree, so current Blenders stop warning about
its removal.
A preview render is dominated by per-frame overhead, so --jobs splits
the frame range across several Blender processes writing distinct
frames into the shared directory; the timeline is identical in every
chunk, so frames line up exactly. Previews default to half the cores
(capped at four) and path-traced finals to one process, which already
saturates the GPU. The player only touches use_nodes on datablocks
that still lack a node tree, so current Blenders stop warning about
its removal.
Each step fingerprints its inputs (file identity, size, mtime) and the
settings that shape its output, and stamps the fingerprint beside the
result: simulate over the run documents and facility, scene over those
plus the trace and assets, render over the scene, trace, and camera
settings. Rerunning with nothing changed reports up to date and does
no work; touching any input regenerates exactly the stale steps.
Preview renders now default to every core instead of four.
Uniform speedup wastes most of a movie on motionless holds: a Golden
Gate wave is nine hours of thermocycling around nine minutes of
pipetting. lab render now warps time instead: motion plays at
--speedup (default 60, slow enough to watch the head work), and every
hold between motions compresses to --hold-seconds of footage, however
long it really is. The warped trace lands beside the renders and the
player runs it at speedup one; --uniform keeps real proportions. An
animation render also owns its frames directory outright now, so stale
frames from an earlier, longer cut cannot ride into the movie.
lab render only renders: it consumes the outputs of lab simulate and
lab scene and names whichever is missing, so iterating on a simulation
never costs frames. Two freshness bugs made the pipeline look coupled
anyway: fingerprints hashed path strings as typed, so the same files
went stale whenever the working directory changed, and a re-run step
rewrote byte-identical output with a fresh mtime, cascading staleness
downstream. Fingerprints now canonicalize their paths and every step
writes only when its bytes actually changed.
@marpaia
marpaia merged commit 6178712 into master Aug 17, 2026
1 check passed
@marpaia
marpaia deleted the marpaia/simulator branch August 17, 2026 00:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant