1 · The yard and the rovers
September 7, 2026 · the static scene was built and previewed before any motion control
The yard is 20 × 16 m and fenced, with two buildings, a long low wall and a shallow ramp. Eight yellow inspection markers stand around it. They are visual posts with no collision geometry. Six rovers start in a column at one end, 2 m apart. The ramp is solid geometry, but the mission routes avoid it.
Each rover is a 4.18 kg RC vehicle built from MuJoCo primitives: a chassis, a battery pack, a sensor housing, an antenna, four sprung wheel carriers and two steering knuckles at the front. All four wheels are driven and the front two steer. A rover moves only through its tyre contacts: the controller sets wheel speeds and steering angles, and MuJoCo computes the motion. A live run never writes a chassis pose, pushes a body or moves anything kinematically. Recorded poses are written only to render a run that has already finished.
Body
- 4.18 kg in total: chassis 2.20, battery 0.60, sensor housing 0.12, antenna and lights 0.04 kg
- Four suspension carriers at 0.08 kg, two steering carriers at 0.03 kg, four wheels at 0.21 kg
- Wheel radius 0.09 m, wheelbase 0.38 m, track 0.48 m
Drive and steering
- Four velocity-controlled wheels: command ±12 rad/s, torque limit ±1.2 N·m, gain 0.35 N·m·s/rad
- Front steering ±0.65 rad (±37.2°): position gain 35 N·m/rad, damping 2 N·m·s/rad, torque limit ±2 N·m
- Ackermann wheel angles, different inner and outer wheel speeds, forward command up to 0.65 m/s
Suspension, tyres, clocks
- Passive suspension: ±0.025 m travel, 1700 N/m spring, 24 N·s/m damping
- Tyre friction 1.1 sliding, 0.01 m torsional, 0.001 m rolling, four-dimensional contact
- Physics at 250 Hz (implicit-fast integration), control at 10 Hz
Across the fleet that makes 24 wheel-velocity actuators and 12 steering actuators, all of them force-limited. There are nine cameras: overview, overhead, a detail view and a front camera on each rover's nose.
2 · A packet-level radio
radio.py: a discrete packet channel running beside MuJoCo; it does not simulate radio waves
MuJoCo knows where each antenna is. A separate model decides whether a message arrives, and it steps with the physics every 4 ms. Every application message is 14 bytes, and the channel charges airtime for the bytes actually sent.
| Field | Bytes | Content |
|---|---|---|
| Kind | 1 | seen, claim or done |
| Marker, origin rover, relay TTL | 3 | one byte each |
| Sequence | 2 | with the origin, the key used for deduplication |
| Observation time | 4 | milliseconds |
| X, Y | 4 | centimetres; the marker for seen and done, the claimant's own position for claim |
| Total | 14 | 46.336 ms on air |
The radio settings are SF7, 125 kHz bandwidth, coding rate 4/5, an explicit header, a CRC and an eight-symbol preamble. With those, a 14-byte message spends 46.336 ms on air (Semtech SX1276 formula). Received power follows a log-distance law (31.7 dB at 1 m, exponent 2.7) from a 14 dBm transmitter, minus 12 dB for every building footprint the straight line crosses.
A packet fails at a receiver if:
- either radio is off;
- the receiver is transmitting itself (the radios are half duplex);
- the signal is below −123 dBm, or more than 7.5 dB under the noise;
- another transmission reaches the receiver within 6 dB of its strength at any moment of its airtime.
Each rover holds up to 16 packets in its queue. It waits a random backoff before sending, checks roughly whether the channel is busy, and may use at most 0.08 s of airtime per second, with 0.2 s of burst credit. On top of all this, a seeded 2% of deliveries are dropped at random.
The degraded case
R0's radio is off from 8 to 28 s and R3's from 36 to 64 s. From 18 to 76 s, receiver noise rises to −54 dBm within 7 m of the yard centre and −74 dBm outside it, and random loss rises to 30% everywhere. This is a set stress test, not a model of any real interference source. Recovery uses the same queues and gossip as normal operation.
| Direct transport, radio seed 0 | Healthy | Degraded |
|---|---|---|
| Delivered to a receiver | 4,502 | 3,039 |
| Lost: random loss | 101 | 384 |
| Lost: radio off | 0 | 197 |
| Lost: noise | 0 | 157 |
| Lost: collision | 22 | 70 |
| Lost: half duplex | 10 | 28 |
| Queue overflows (never sent) | 9 | 0 |
| Delivery ratio | 97.1% | 78.4% |
3 · What is real Reticulum
--reticulum: six genuine network stacks on one simulated channel
With --reticulum, each rover runs its own Reticulum 1.5.2 stack in a separate local process.
Each stack has one custom interface, connected only to the simulation. Shared-instance connections, transport
routing and external interfaces are switched off. When a rover sends, its stack builds a real frame and the
simulation puts that frame's bytes on the same simulated LoRa channel. Only frames the channel delivers reach
the receiving stack. That stack decodes the frame and passes the 14-byte payload to the application through
its endpoint callback.
Reticulum wraps the 14-byte message in a 33-byte frame, which takes 71.936 ms on air instead of 46.336 ms. The channel charges all of that overhead.
Exercised
- Reticulum PLAIN broadcast framing
- Frame construction and decoding by the real stack
- Delivery through the endpoint callback
- Duplicate deliveries, which PLAIN destinations allow and the application filters out
Not exercised
- Encrypted sessions and identity discovery
- Reticulum's multi-hop routing: relaying is the application's own one-hop TTL
- RNode or any radio hardware
- LAN or Internet links
Always simulated
- Signal loss over distance, building attenuation and noise
- Collisions, half duplex, queues and the airtime budget
- Outages, the noise zone and random loss
| Healthy radio, seeds 0–2 | Direct | Reticulum |
|---|---|---|
| Bytes on air per message | 14 | 33 |
| Airtime per message | 46.336 ms | 71.936 ms |
| Mean delivery latency (airtime plus the 4 ms tick) | 48 ms | 72 ms |
| Frames sent per run | 910–930 | 643–659 |
| Bytes on air per run | 12,740–13,020 | 21,219–21,747 |
| Queue overflows per run | 0–9 | 72–198 |
| Mean delivery ratio | 96.3% | 86.4% |
The larger frames use up the airtime budget sooner. In every healthy Reticulum run, each scout used 12.16 s of airtime: 169 frames, within one frame of the maximum the budget allows in 150 s. With fewer frames getting out, more messages overflowed the 16-packet queues. The latency figure covers only airtime and tick rounding; it leaves out time spent waiting in the queue and the wait for the next 10 Hz agent update. The lower Reticulum mean comes from one seed (chapter 8).
4 · Rules, not learning
agent.py: local observations and delivered bytes in; speed, steering and messages out
Nothing in this project is learned. Every rover runs the same short program, and its role follows from its number. Scouts R0, R2 and R4 drive fixed survey routes and report what they see. Inspectors R1, R3 and R5 go to markers they know about and inspect them. An inspection counts only when the inspector holds still for one second within 0.8 m of the marker, with the marker in view.
The agent code has no access to MuJoCo. On each 10 Hz control tick it receives a read-only observation and the bytes delivered to it since the last tick. It returns a speed, a steering angle and its outgoing messages. The dashboard's rover positions and inspection count belong to the observer and never reach the agents.
What a rover senses
- Perfect odometry (position, heading, speed) with no noise
- A marker detector: 2.5 m range, ±65° field of view, blocked by obstacles (checked with MuJoCo ray casts)
- Unlabelled positions of other rovers within 2 m, in every direction, ignoring obstacles
- A public map of the buildings, with no marker positions
What it believes
- A marker belief records when it was observed, who sent it, the packet ID and when it was delivered
- Newer observations replace older ones
- Lost or still-queued messages change nothing
- A rover can know a marker is done without knowing where it is
How it decides
- Go to the nearest known marker that is not yet done
- Send a claim every 6 s; a claim lasts 15 s. Give way to a claimant more than 0.6 m closer; near ties go to the lower ID
- Scouts relay each message once; a sighting is re-sent at most every 8 s; one completion record is re-shared every 5.5 s
- Plan paths with A* on a 0.5 m grid; keep patrolling through radio outages
5 · Three fixes from testing
September 7, 21:57–22:33 PDT · found and fixed during testing, before the validation runs
6 · Complete, not faster
18 runs of 150 simulated seconds: 2 transports × 3 radio cases × radio seeds 0, 1 and 2
Geometry, starting poses, routes, physics and policy were the same in every run. The radio seed changes only backoff timing and which deliveries the random loss drops; it does not represent a different site. With the radio off, randomness has no way to affect motion, so the three repeats are identical.
| Transport / case | 8 / 8 | Completion, seeds 0 · 1 · 2 | Mean | Duplicates | Delivery | Completion known |
|---|---|---|---|---|---|---|
| Direct / healthy | 3 / 3 | 57.9 · 46.6 · 40.5 s | 48.3 s | 0.0 | 96.3% | 100% |
| Direct / degraded | 3 / 3 | 39.5 · 53.1 · 37.6 s | 43.4 s | 1.3 | 79.2% | 100% |
| Reticulum / healthy | 3 / 3 | 47.3 · 39.5 · 54.5 s | 47.1 s | 0.0 | 86.4% | 100% |
| Reticulum / degraded | 3 / 3 | 47.8 · 38.4 · 52.7 s | 46.3 s | 1.0 | 79.8% | 100% |
| Radio off (each transport) | 3 / 3 | 38.4 · 38.4 · 38.4 s | 38.4 s | 9.0 | nothing sent | 35.4% |
Completion means every marker has been inspected at least once. Duplicates are extra inspections of an already inspected marker by another inspector, over the full 150 s. Completion known is the share of the 48 rover-and-marker completion records that the rovers hold locally at 150 s. Means are over the three seeds.
7 · Packets changed decisions
CAUSAL_EVIDENCE.json · every remote route change checked against the receiver's delivery log
A rover can act on a remote report only if the channel actually delivered it. The logs make that checkable. Every route change records the packet behind the belief and when it was delivered, and the simulation records when the receiving rover first saw that marker with its own detector. A route change counts as remote if its belief came from a delivered packet and the receiver had not yet seen the marker itself. Each one was then matched against the channel's delivery record for that receiver.
| Transport / case | Remote route changes, seeds 0 · 1 · 2 | Receiver never saw the marker | First inspections chosen from a report |
|---|---|---|---|
| Direct / healthy | 20 · 15 · 13 | 7 | 13 / 24 |
| Direct / degraded | 18 · 13 · 11 | 4 | 10 / 24 |
| Reticulum / healthy | 6 · 8 · 17 | 7 | 4 / 24 |
| Reticulum / degraded | 10 · 5 · 11 | 3 | 0 / 24 |
| Radio off | 0 · 0 · 0 | – | 0 / 24 |
| All 12 radio runs | 147 | 21 | 27 / 96 |
Here is one remote decision, from the direct, healthy, seed 0 run. Scout R2's packet 8 was on air from 0.644 to 0.690 s and reached inspector R5 on the next 4 ms tick, at 0.692 s, received at −38.7 dBm. At its next control tick, 0.8 s, R5 turned toward marker M7. R5 did not see M7 with its own detector until 127.8 s.
The film at the top of the page shows another, about 6 s into the video. At simulated 11.6 s, inspector R3 turns toward M1 after R1's packet 61 arrived at 11.56 s. R3 never saw M1 itself in that run; R1 inspected it at 15.6 s. A unit test checks the same boundary in isolation. One agent receives a delivered report and another receives nothing: only the recipient changes its target, and an older report from a third rover cannot overwrite the newer belief.
8 · The Reticulum outlier
Reticulum, healthy radio, seed 0: the lowest delivery ratio of all 12 radio runs
One run delivered far less than the runs around it. The Reticulum healthy run with seed 0 delivered 66.7% of receiver opportunities, lower than any degraded run (77.8% to 81.0%). On its own it pulls the Reticulum healthy mean down to 86.4%; seeds 1 and 2 delivered 96.6% and 96.0%. The extra losses are collisions and half-duplex misses. Random loss was ordinary, and the queues overflowed less often than in the other seeds. So the difference lies in transmissions overlapping in time, not in fuller queues.
| Healthy radio | Reticulum, seed 0 | Reticulum, seed 1 | Reticulum, seed 2 | Direct, seed 0 |
|---|---|---|---|---|
| Frames sent | 649 | 643 | 659 | 927 |
| Delivered to a receiver | 2,165 | 3,104 | 3,164 | 4,502 |
| Lost: collision | 704 | 27 | 40 | 22 |
| Lost: half duplex | 322 | 10 | 16 | 10 |
| Lost: random loss | 54 | 74 | 75 | 101 |
| Queue overflows | 72 | 148 | 198 | 9 |
| Delivery ratio | 66.7% | 96.6% | 96.0% | 97.1% |
| Remote route changes | 6 | 8 | 17 | 20 |
| Completion / duplicates | 47.3 s / 0 | 39.5 s / 0 | 54.5 s / 0 | 57.9 s / 0 |
The mission itself got through. All eight markers were inspected in 47.3 s with no duplicates, and at 150 s every rover held all eight completion records. It made only 6 remote route changes, against 8 and 17 in the other two healthy Reticulum seeds.
9 · Next steps
Each step is measured against the 18 runs above, on the same layout and seeds
The radio parameters are assumptions, not measurements. The controllers read ideal simulator observations, not camera images. The rovers still bump each other by a few millimetres.