Sensor-Bridge: Robotics Sensor Pipeline
Sensor-Bridge is a Rust crate for robotics sensor ingestion, filtering, aggregation, and output. It pairs a no_std-compatible core with standard-library pipeline, metrics, network driver, and CLI layers.

Goal
Robotics sensor paths need bounded handoff behavior more than clever architecture. An IMU can produce samples at 1 kHz, LiDAR can push hundreds of thousands of points per second, and downstream consumers often run at different rates. Sensor-Bridge keeps that path explicit: synchronous stages, bounded queues, measured latency, visible drops, and backpressure strategies that match the sensor type.
The project is not a full robotics middleware stack. It is a small library for building a sensor data path where allocation, queue depth, shutdown, and timing behavior are easy to inspect.
Crate layers
The repository is split by feature boundary:
| Layer | Role |
|---|---|
buffer |
cache-padded SPSC ring buffer for one producer and one consumer |
stage |
transform, filter, fusion, Kalman, complementary, and coordinate stages |
sensor |
IMU, LiDAR, and mock sensor traits and readings |
pipeline |
standard-library multi-stage runner and builder |
drivers |
UDP and TCP JSON receivers behind the network feature |
sinks |
CSV, UDP, tap, and WebSocket outputs |
metrics |
latency histograms, jitter tracking, counters, dashboard, JSON and CSV export |
The low-level core supports default-features = false. Network ingestion, WebSocket output, the CLI, dashboards, and multi-threaded runners stay behind std, network, websocket, or cli features.
Pipeline model
The main MultiStagePipelineBuilder wires four stages:
- ingestion,
- filtering,
- aggregation,
- output.
Each stage runs on its own OS thread. The current multi-stage runner connects those threads with bounded crossbeam channels and drains in-flight work during shutdown. That detail matters: the project includes a lock-free SPSC ring buffer, but the high-level four-stage runner is a bounded-channel design, not a claim that every pipeline hop uses the custom ring.
For single-threaded composition, PipelineBuilder and PipelineRunner provide map, filter, and then combinators. For lower-level work, RingBuffer<T, N> exposes a cache-padded SPSC queue with acquire-release ordering and power-of-two capacity.
Sensor and driver surface
Sensor integration goes through the Sensor trait. Drivers are expected to make sample() non-blocking and return timeout when no reading is ready. UDP and TCP drivers keep the async runtime at the network boundary: receiver tasks decode JSON packets into a channel, then the synchronous pipeline consumes readings without making the rest of the graph async.
The included examples cover:
- mock IMU through a moving-average filter,
- IMU plus barometer fusion,
- a full four-stage pipeline,
- latency and dashboard demos,
- complementary-filter IMU fusion against simulated truth,
- UDP sensor ingestion with the Python mock sender.
Backpressure and drops
Backpressure is configurable rather than hidden. The project exposes block, drop, and sample strategies, plus an AdaptiveController with high and low watermarks. Hysteresis prevents rapid oscillation between normal and backpressured states.
That makes the data-loss policy explicit:
- block preserves data but pushes latency upstream,
- drop keeps latency bounded when old readings are no longer useful,
- sample degrades input rate when consumers fall behind.
The network and pipeline metrics count dropped packets or readings, so a dashboard or JSON export can show whether the selected policy is working.
Zero-copy and allocation control
ObjectPool<T> and BufferPool provide reusable storage through RAII handles. SharedData<T> wraps read-only shared values in Arc<T> without adding interior mutability. The target is not “zero allocations everywhere.” The target is to keep hot-path allocation visible and avoid per-reading allocation after warmup where the chosen pool fits the workload.
Performance evidence
The repository includes Criterion benches for ring buffers, pipeline throughput, channels, and allocation pools. The reference numbers in the project docs were measured on an Apple M2 MacBook Air running macOS 15 with Rust stable 1.89:
| Metric | Result |
|---|---|
| Ring buffer push | ~0.3 ns |
| Ring buffer pop | ~9 ns |
| Channel latency | ~20 ns |
| Stage processing | ~2.2 B items/s |
| Four-stage pipeline throughput | >1 M items/s |
Those are microbenchmarks. The performance doc explicitly warns that CPU class, governor state, cache effects, and background load move the numbers. The useful engineering result is not the absolute nanosecond value. It is that the repo has a repeatable cargo bench surface and documents where benchmark interpretation gets fragile.
Operational limits
Sensor-Bridge deliberately keeps fan-in and fan-out explicit. SPSC is the low-level queue primitive. The high-level runner supports bounded stage-to-stage channels, but complex graphs still need deliberate wiring. no_std applies to the core modules, not to the CLI, network drivers, metrics dashboard, or thread-spawning pipeline.
Related notes
Interrupts, DMA, and Ring Buffers, IMU Calibration and Drift, PID Tuning and Step Response, and Design Verification and Test.