Distributed robotics · ESP32 · ESP-NOW · WebAssembly
Small ESP32 robots run the floor themselves. They grip each parcel and carry it to the right dock, plan their own routes, and team up to carry a crate too heavy for one. There's no central server: every robot runs the same planner on the same shared picture, so they agree without negotiating. Amazon-style systems need a central planner and codes stuck all over the floor; this needs a phone camera and a radio.
The floor below is live, not a video. It runs the robots' actual firmware, compiled to WebAssembly in your browser. Drop parcels, add robots, switch one off, and watch the swarm re-plan.
Same C++ that's flashed onto the robots (core/brain.h), driving a physics model of the floor with camera noise, motor mismatch, dropped radio packets and a gripper that misses 5% of the time. Proven in simulation; carrying needs a gripper on the physical robots, which run push mode until one is fitted.
Click the floor to drop a parcel · switch to heavy crate for a load one robot can't move · click a robot to cut its battery (click again to revive) · drag a dock to move it. In Morning rush there are two docks: parcels alternate between them.
Every robot broadcasts a heartbeat 10× a second and counts who it hears. Switch a robot off and the rest notice within a second.
Each robot runs the same plan on the same snapshot, so they agree without negotiating. Big jobs wait until enough robots are free; a spare robot doubles up on a parcel.
A robot lines up, drives in slowly, grips, then tugs back a few cm: a held parcel follows, a missed one doesn't. Then it carries the parcel to a free slot, keeping it clear of walls and other parcels.
Nobody tells a robot a crate is heavy. If it drives and nothing moves, it radios for help; two robots grip it side by side and drive as one wide vehicle: ant-style teamwork.
If every robot has tried and the load won't move, it's flagged stuck for a human instead of being shoved forever. It re-opens when more robots come online.
Like a shift manager, with no manager: carriers wait beside a full dock, ready the moment it clears; idle robots learn where trucks unload and wait just outside that bay, clear of where the next box will land. Parcels wait 54% less for pickup.
| Test | What has to hold | Result |
|---|---|---|
| 8 scenarios × 60 random seeds | All delivered (to the right dock), work split, crates carried in pairs, zero collisions. Seeds vary camera noise, motor mismatch (±8%), lost radio packets and missed grips. Carry vs push, same seeds: | 480 vs 462 / 480 |
| Parcels right against the walls | 200 random floors. A pusher can't get behind a parcel on a wall; a gripper takes it from the open side. | 200 vs 118 / 200 |
| Dense floors | 200 crowded floors: 6–10 robots, 10–14 parcels, two docks. All delivered with no collisions; carrying vs pushing (42,992 collision events). Of the 7 carry failures, 5 deliver everything but two robots touch. | 193 vs 15 / 200 |
| Parcels dragged along a wall | Every seeded run, carry mode. | 0 |
| Energy per delivered parcel | Energy model (motors, driver loss, gripper peak-and-hold), to be calibrated on the real robots. Carry vs push: | 20 vs 28 mWh |
| Floor manager | 40 ten-minute shifts of trucks arriving at random at 2–3 bays, the same trucks with the manager rules off and on. Mean wait before a robot picks a parcel up: | 14.8 → 7.5 s |
| Capacity | Trucks arriving faster than the robots can clear them, 3 × 2 m floor. Parcels sorted per hour with 10 robots, and how much of ten times one robot's rate that keeps: | 1,502 /h · 68% |
| Leaderless consensus | Every robot computes the identical plan from the same snapshot, in any order. | unit-tested |
| Camera pipeline | Synthetic tilted-camera frames: robot position, heading, parcel position error; parcel colour picks the right dock. | ≤ 1.3 mm · 0.4° · 2.8 mm |
Kiva figures: Wurman, D'Andrea & Mountz, "Coordinating Hundreds of Cooperative, Autonomous Vehicles in Warehouses", AI Magazine 29(1), 2008.
On a 1.5 × 1 m floor, throughput stops growing at about 4 robots (≈ 800 parcels/h): the bays can't be unloaded and the docks can't clear any faster. Six more robots add no parcels, only 74% more energy per parcel.
It showed the swarm collapsing as robots were added. The real cause: simulated trucks dropped boxes 13 cm from robots, boxing them in for good. A 30 cm "don't drop next to a robot" rule (a bay light on a real floor) took one layout from 36 to 134 parcels per shift.
Each failed run is labelled by what was left on the floor, then traced to its cause: a crate pair whose grip face flickered, a hold-and-wait deadlock at the dock, a parcel dragged out of its dock. When every robot ends up holding a parcel and none can move, all of them agree, without talking, which one sets its parcel down. Same 1,380 test runs: 97.3% → 99.35% (McNemar p = 8 × 10⁻⁷).
Docks that drive to the busy bay: +20% at best. The mathematically optimal job assignment: worse (it reshuffles jobs every frame). Right of way by priority on free paths: 14% faster but robots bumped. Capping the fleet: saves energy, adds no parcels. Collision-free motion with a proof (Voronoi cells): robots stopped touching but started deadlocking.
A queueing model (Mean Value Analysis), fed only measured one-robot timings, predicts throughput within 7% up to 8 robots. The 12% it overshoots at 10 robots is traffic: the most any better traffic planner could add.
Design, every benchmark with its confidence interval, the rejected ideas and the papers this builds on: PAPER.md ↗
06. Known limits