Our warehouse robot platform now runs end to end on a simulated site: a dashboard for operators, an edge server that drives the floor, and the devices themselves. This post shows what it does, how it keeps people and machines safe, and what we have run to prove it.
The platform controls a fleet of forklifts, robot arms, conveyors and cameras in a warehouse. It is built as three layers that talk only through defined interfaces, so each can be replaced or upgraded on its own.
What operators, supervisors and maintenance staff see. A live floor map, alarms, commands, history and analytics across several sites, signed in through an identity provider with seven roles.
Runs on site. It admits devices, keeps the live picture of the floor, publishes the permit to operate, routes the forklifts without collisions, turns what the cameras see into pallet jobs, and survives the loss of a node or of the WAN.
An agent on every machine and a physical simulation around them: geometry, batteries, radio dead zones, people walking the aisles, MuJoCo arm physics and rendered camera pictures. Robots, shapes and instructions are data in YAML packs, not code.
One action touches every layer. This is the path a fleet pause takes, and the one we time.
Sent as the signed-in person, with an idempotency key so a retry never acts twice. Resuming asks for a step-up sign-in.
sent once, as a named personThe request is written to the audit trail in Postgres with who, what and when, then sent to the site.
audit + lifecycleThe command service passes it to the safety supervisor. The stop goes out before the store is waited for, so a slow disk never delays it.
no wait on storageThe supervisor marks every device held in the next permit. Vehicles on VDA 5050 get a pause order, one by one.
permit every 500 msEvery device is in HOLD and the dashboard shows who is held and why. Resuming the fleet needs a second person.
measured ~100 ms · limit 1 sScreenshots taken on 18 October 2026 with all three layers running together: the simulated floor, the edge, and the cloud and dashboard. We struck the faults on purpose and cleared them afterwards. The yellow labels are ours; everything else is what the screens showed.
The simulated world drawn in the browser, and streamed to Foxglove or Lichtblick as well. Here a person has stepped into arm-01's cell (F24) and agf-03's E-stop is pressed (F01). The panel on the left lists what is struck, what is held and what is offline.
What camera cam-01 sees, rendered by the world from a seed: 6 m up, 9.4 mm of floor per pixel, with sunlight, shadows, lens blur and sensor noise. Each picture comes with its depth, a segmentation and instance masks, and the true pose of every pallet. The edge's pallet detector is held to that truth, within 3 cm and 2 degrees.
Depth
Segmentation
InstancesEvery device on the site, live, in one list. Each class reports what matters for it, and a class the dashboard has never seen is shown from the factsheet the device sends. The registry is the edge's own record of what it has in service.
The floor from the site package, with every vehicle moving as the site reports it. Arms, conveyors and cameras stand where the package puts them, filled while they work.
An E-stop on agf-03 (F01) and a radio dead zone on agf-04 (F05), struck through the edge. agf-04 never looks live while it is silent: it greys out with the age of its last position. The edge declares it LOST and keeps its reserved floor until it reports again (rule R1).
Most systems stop a robot when something goes wrong. This one inverts it. A machine starts new motion only while it holds a fresh permit, so silence from the server is itself a stop.
The edge sends a permit every 500 ms. A permit is valid for one second on the device's own clock, and it names any device that is held. If permits stop, because the edge crashed, the broker went down or the network was cut, every supervised device holds within a second of the last one.
A permit from an older epoch is ignored, so a frozen leader that wakes up after a takeover cannot send a machine on its way. The highest epoch seen is kept across a loss of power.
Alarm raised, nothing is sent to it, and a forklift keeps its reserved floor until it is heard again.
Over 0.5 m outside what is reserved: it and its neighbours are held until an operator places it, even through a restart.
An arm is held first. The forklift is released into the station only when the arm's own state shows the hold.
A light curtain or laser scanner sees someone: the arm is held within one permit, about 150 ms, and stays held until the guard is reset.
Localization score under 0.6: paused until it is back at 0.8. An operator can vouch for it for 5 minutes at most.
Alarm raised and the device's data is ordered by when it arrived, not by what it claims.
The permit is withdrawn and everything that needs one holds.
Every vehicle it routes is paused, and released only by the supervisor that paused it.
A P0 alarm stands until the device says it is over. Nothing is dispatched to it meanwhile.
Safety events and events are written durably before the edge confirms them. A device keeps its own write-ahead log and resends until the edge confirms. No safety event was lost in the storm, failover and outage runs listed below.
A three-node edge elects one leader by lease. Two never serve at once. A frozen leader that wakes stops its edge before it sends anything.
Seven roles. Step-up sign-in before a resume, a second person before a fleet resume, and every command in an audit trail with an idempotency key.
Devices enrol over EST and connect with mTLS. Configuration bundles are signed with cosign keys (ECDSA P-256), proven on probation and rolled back if a device does not come up healthy.
Each fault above can be struck on the simulated floor on demand, and a test checks what the edge does against the simulator's ground truth. Two more, a state stall and a failed update, are tested the same way.
Where an industry standard exists we use it, so vendors' vehicles, tools and viewers work without translation.
Every row has a limit written down before the test. The bar shows the worst figure we measured as a share of that limit. All were measured in simulation on a single machine.
Recovery is measured in seconds, not milliseconds: a reboot storm of 120 devices is fully back in about 3 s (limit 30 s). At 1,000 devices all return 29.3 s after the storm. After the leader is killed, another member serves after about 5 s and every device is back ONLINE 9 to 10 s after the kill.
The automated tests behind the platform, and what they check.
| Recorded long run | What it did | Result |
|---|---|---|
| Traffic, 8 simulated hours | 20 forklifts, 6,826 journeys on a larger simulated site | 0 footprint overlaps · longest deadlock 10.9 s |
| A full shift at 20x speed | The edge and the simulated floor as separate systems: 149 pallets arrived, 22,381 m driven, 2,500 routes | 0 safety violations · 0 orders resent |
| 72-hour WAN outage | 438 safety events, 8,760 events and 291,610 telemetry samples held back, then replayed | Each once, in order, safety first · 10 runs of 10 passed |
| Reboot storm, 1,000 devices | Every device power-cycled at once | All back in 29.3 s · no safety event lost |
| Leader killed without warning | Three edge nodes, then the new leader frozen past its lease | Successor in 3.4 to 7.7 s · never two leaders · woken leader stands by |
| Eight-hour mixed-vendor shift | Forklifts of two vendor profiles on one floor | 21.0 pallets/h through the peak · none of 3,169 routes cancelled |
| Eight days of floor time | 192 hours of operation, run at 20x speed | No leak · 4,293 pallets |
| Operator load | 500 dashboard sessions each following every device of three sites | p95 153 ms against a 500 ms limit |
Counts are of tests, not of checks: one test can cover several cases.
All of the figures above come from simulation. No physical robot has run on the platform yet.
Devices, people, radio and physics are simulated. A real machine's I/O is exercised only on a simulated bench. On a real floor the cell's own safety circuit acts first; our hold is the second layer.