STAR: act firmware commands out in the viewer (motion playback) - #1468
Open
stefangolas wants to merge 10 commits into
Open
stefangolas wants to merge 10 commits into
stefangolas wants to merge 10 commits into
Conversation
…and the model says what a grip takes
…until it has played
stefangolas
force-pushed
the
star-motion
branch
from
October 3, 2026 16:42
92c2666 to
3dfbff2
Compare
stefangolas
marked this pull request as ready for review
October 4, 2026 14:17
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
STAR: act firmware commands out in the viewer (motion playback)
The viewer can play what a command does while the device does it. Each command a simulated STAR driver sends is read for the motion its drives make, sent to the pages, and the command is held until every page has played it; the model then records where the command ended, which is where the pages have just brought everything. Timings come from measured profiles (
MOTION_PROFILES.md).Architecture: how a command becomes a motion
The path a command takes has five hops:
(module, command, params)to its optionalmotion_listener.star_motion()(inpylabrobot/hamilton/star/motion.py) decodes the command into a plain dict - akind, what moves (arm X, channel Y and heights, grip widths, tip handovers), and the timings fromMOTION_PROFILES.md. A command that moves nothing decodes toNone.act_out(command, motion)holds the command and broadcasts the payload to the pages over the websocket. With no page connected, nothing is waited for.playMotionapplies the profiled progress per frame, and reportsmotion_done. The slowest page sets the pace; a background page answers at once.pylabrobot/hamilton/star/- the viewer package stays machine-agnostic.visualizer3D/server.pytakes any device's motion payload through a genericact_out(command, motion); it knows no STAR types.kind,moves, ...). The JS side is pure kinematics: the page decides nothing about machines, it only plays what it is told.STARSimulationDriver.motion_listenerisNoneunless a viewer is attached (seemotion_demo.py). With no viewer, behaviour is identical to main.Files this PR adds, and what each is for
pylabrobot/hamilton/star/motion.pypylabrobot/hamilton/star/motion_tests.pypylabrobot/hamilton/star/MOTION_PROFILES.mdpylabrobot/visualizer3D/static/motion_profile.jst. Parameterized per move by the payload.pylabrobot/visualizer3D/static/motion_player.jsstep(dt)that applies profiled progress each frame, and reports when the motion is over.pylabrobot/visualizer3D/static/motion.jspylabrobot/visualizer3D/static/carry.jsmotion.jsneeds to hand a plate over and put it back.pylabrobot/visualizer3D/motion_player_tests.mjsnode --test), run frommotion_tests.pywhere Node is present.pylabrobot/visualizer3D/motion_demo.pyFiles this PR touches, and why
hamilton/star/driver/simulator.py- themotion_listenerhook: an optional awaited callback before each command is answered. Inert when unset.hamilton/star/driver/features/pipettes.py- two pure helpers: a CO-RE grip tool's face distance and grip-line overhang, read off the resource model.hamilton/star/driver/features/head96.py-_spots_under_shafts(offset): which rack spot each shaft stands over for an offset (a pure query; behaviour is unchanged).hamilton/star/driver/features/core_grippers.py- a_handoverstash (what a grip takes, where it will hang; what a release puts down, where it lands) so a viewer can act the command out, and the_hang_locationhelper factored out of_hang_held_resource_on_the_front_tool.visualizer3D/server.py- the generic motion channel:act_out,motion_donebookkeeping, and themodels_drawnhandshake (a run waits until a page has its models on screen).visualizer3D/static/transport.js-send()to the server, and themotionmessage dispatch.visualizer3D/static/live.js- scene operations the motions need:readAxis/setAxis(draw a resource along one axis),reattach(hand a resource to a new holder),turnTo.visualizer3D/static/models.js- themodels_drawnreport once every model file of a scene is on screen.visualizer3D/static/app.js- the registrations: step the motion per frame, playmotionmessages, drop motions on a scene rebuild.visualizer3D/browser_tests.py- the Windows headless-Chrome install paths, so the browser battery runs where Chrome is installed but not on PATH.Behaviour changes
None. Nothing here changes what firmware is sent to a live or simulated STAR: the driver change is an optional awaited callback; every other change is additive (new files, a new server method, viewer JS). With no viewer attached, a simulated run sends the same commands in the same order and answers them from the same model.
Demos
Five runnable demos exercise the playback end to end (
python -m pylabrobot.visualizer3D.<name>):motion_demo.py- a facility, a viewer, motions played as the run goes.ripple_demo.py- the Y ripple: one aspirate over five plates, the channels fanning out in Y and rippling back along the direction of travel.head96_demo.py- the 96 head: a full plate of tips, a full-plate aspirate, a full-plate dispense.iswap_demo.py- the iSWAP: a plate moved between the carrier sites the turned gripper can reach, on an otherwise empty deck.core_gripper_demo.py- the CO-RE gripper: grip, carry, and set a plate down.Notes for reviewers
Args/Returns/Raisesdocstrings; no lint rule enforces docstrings).