You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Extends the Mosaico M0 demo with a fleet compose file so cross-robot
forensic queries have more than one sequence to filter on. The single-
robot variant proves the plumbing; the fleet variant is what makes the
.Q catalog queries interesting.
docker-compose.fleet.yml spins up three independent sensor-demos
(warehouse-A on 18081, warehouse-B on 18082, outdoor-yard on 18083)
each with its own bridge sharing a single mosaicod + postgres. Each
bridge tags ingested sequences with distinct robot_id metadata.
scripts/trigger-fleet-faults.sh injects three heterogeneous fault
signatures so the resulting Mosaico catalog has visible variance:
robot-01 warehouse-A LIDAR noise_stddev=0.5 (range std spikes)
robot-02 warehouse-B IMU failure (different sensor)
robot-03 outdoor-yard LIDAR drift_rate=0.5 (range mean shifts)
medkit_params.yaml: widen ring buffer to 15 s pre + 10 s post (was
10 + 2) so each snapshot carries enough pre-fault baseline for
drift-vs-noise comparison; lift max_bag_size_mb to 2000 so rosbag2
does not split the recording mid-flight (the gateway only serves the
first split).
bridge.py: honor POST_FAULT_WAIT_SEC (default 12 s) before downloading,
so the longer duration_after_sec still finishes before the bridge
grabs the bag.
README updated with fleet quick start, file inventory, and refreshed
numbers ("25 s snapshot", "~500 MB MCAP") consistent with the new
ring buffer sizes.
Verified end-to-end on this machine: 3 fault injections produce 3
sequences in mosaicod, all listable via MosaicoClient.list_sequences()
and tagged with the expected metadata. Cold start to last verified
ingest ~75 s.
Copy file name to clipboardExpand all lines: demos/mosaico_integration/README.md
+39-10Lines changed: 39 additions & 10 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -2,7 +2,12 @@
2
2
3
3
End-to-end pipeline demonstrating that **medkit fault snapshots flow into Mosaico as queryable, structured forensic data** - with no robot hardware and no recording 24/7. Designed as a homework artifact for the [Mosaico Labs](https://github.com/mosaico-labs) team.
4
4
5
-
> Robot stack runs in Docker. Inject high noise on the simulated LiDAR. The medkit gateway detects it via the standard `/diagnostics` ROS topic, confirms the fault, and flushes its 10-second pre-fault + 2-second post-fault ring buffer to an `.mcap` file. A small Python bridge listens on the gateway's `/faults/stream` SSE endpoint, downloads the bag via REST, and ingests it into mosaicod via Apache Arrow Flight using Mosaico's own Python SDK. From `docker compose up` to a queryable `Sequence` in mosaicod takes about a minute.
5
+
> Robot stack runs in Docker. Inject high noise on the simulated LiDAR. The medkit gateway detects it via the standard `/diagnostics` ROS topic, confirms the fault, and flushes its 15-second pre-fault + 10-second post-fault ring buffer to an `.mcap` file. A small Python bridge listens on the gateway's `/faults/stream` SSE endpoint, downloads the bag via REST, and ingests it into mosaicod via Apache Arrow Flight using Mosaico's own Python SDK. From `docker compose up` to a queryable `Sequence` in mosaicod takes about a minute.
6
+
7
+
There are two variants of the stack:
8
+
9
+
-**Single-robot** (`docker-compose.yml`): one sensor-demo + one bridge. Good for stepping through the pipeline the first time and for the notebook.
10
+
-**Fleet** (`docker-compose.fleet.yml`): three sensor-demos (warehouse-A, warehouse-B, outdoor-yard) each with its own bridge, all ingesting into one shared mosaicod. Produces three heterogeneous fault snapshots (LiDAR noise, IMU failure, LiDAR drift) under distinct `robot_id` metadata so cross-robot queries actually have something to filter on.
The notebook connects to `localhost:16726` (mosaicod Arrow Flight) and runs four queries against your freshly-ingested fault snapshot, ending with a three-panel time-series plot showing the LiDAR noise spike alongside a stationary IMU - exactly the kind of cross-topic forensic correlation Mosaico is designed for.
31
36
37
+
### Fleet variant
38
+
39
+
```bash
40
+
# Three robots + three bridges + shared mosaicod. Ports 18081/2/3 for the
41
+
# robots, 16726 for mosaicod (same as single-robot).
42
+
docker compose -f docker-compose.fleet.yml up -d
43
+
44
+
# Wait ~25s after all containers are healthy so the ring buffer has a
45
+
# pre-fault baseline, then inject three different fault signatures:
docker compose -f docker-compose.fleet.yml down -v
56
+
```
57
+
32
58
## Architecture
33
59
34
60
```
@@ -92,19 +118,21 @@ The same draft works on the **read** side too, as long as the consumer imports `
92
118
93
119
## Smart snapshots, not 24/7 recording
94
120
95
-
This is the value prop: each entry in the Mosaico catalog is **only the 12 seconds around a confirmed fault**, not hours of "nothing is happening" telemetry. With three faults injected the catalog totals ~140 MB, indexed and searchable. A naive 24/7 recording of the same four sensors at the same rates would be roughly 120 GB per robot per day. The pipeline preserves the signal and discards the noise.
121
+
This is the value prop: each entry in the Mosaico catalog is **only the 25 seconds around a confirmed fault** (15 s pre-fault baseline + 10 s post-fault), not hours of "nothing is happening" telemetry. Three fleet snapshots together weigh roughly 1.5 GB of MCAP - a naive 24/7 recording of the same four sensors at the same rates would be closer to 120 GB per robot per day. The pipeline preserves the signal and discards the noise.
|`bridge/bridge.py`| Subscribes SSE, downloads bag, calls `RosbagInjector`. Honors `POST_FAULT_WAIT_SEC` (default 12s) before download so the post-fault ring segment is finalized |
@@ -130,8 +158,9 @@ This is the value prop: each entry in the Mosaico catalog is **only the 12 secon
130
158
3.**Faults from the legacy diagnostic path land under `apps/diagnostic-bridge`**, not `apps/lidar-sim`. The diagnostic_bridge node is what owns the snapshot bag in this demo.
131
159
4.**Mosaico read-side registry**: even with PR #368 installed, you must `import mosaicolabs.models.futures.laser` before reading `LaserScan` data. Otherwise the topic reader raises `No ontology registered with tag 'laser_scan'`. The bridge does not need this (write side resolves adapters by ROS msg type) but the notebook does.
132
160
5.**Not all 5 listed topics actually land in Mosaico**: `/diagnostics` drops silently because no adapter is registered. The medkit ring buffer captures it; Mosaico just does not know what to do with it. See the table above.
133
-
6.**Initial post-fault wait**: medkit holds the rosbag2 writer open for `duration_after_sec` (2s) after `fault_confirmed`. The bridge sleeps 2.5s before downloading to make sure the file is finalized.
134
-
7.**Gateway port conflict on dev boxes**: we publish on `18080` and `16726` instead of the conventional `8080` and `6726` because dev machines often have other demos running. Adjust if you prefer defaults.
161
+
6.**Initial post-fault wait**: medkit holds the rosbag2 writer open for `duration_after_sec` (10s in this config) after `fault_confirmed`. The bridge waits `POST_FAULT_WAIT_SEC` seconds (default 12) before downloading so the trailing ring segment is finalized.
162
+
7.**Gateway port conflict on dev boxes**: the single-robot stack publishes on `18080` and `16726`; the fleet stack uses `18081/18082/18083` for the three gateways with `16726` shared by mosaicod. Adjust if you prefer defaults.
163
+
8.**`rosbag2` file splitting**: if `max_bag_size_mb` is hit mid-recording, `rosbag2` splits into `_0.mcap`, `_1.mcap`, ... and the medkit gateway serves only the first split. The 2 GB cap in `medkit_params.yaml` is there to prevent splitting for any realistic 25 s snapshot.
0 commit comments