ros2probe is a drop-in replacement for
rosbag2andros2 topicthat does not perturb the system it observes. It records and monitors ROS 2 traffic from outside the DDS domain. No extra subscriber, no observer-induced drops, far less CPU and memory. It reports exactly the loss the real subscriber saw, and the CLI mirrors ROS 2, so you just swapros2forrp.
Code · Paper (arXiv) · Project page
Why this exists
Every standard observer (rosbag2, ros2 topic echo / hz, the ros2 daemon,
DDS vendor monitors) sees your data by subscribing. That adds a DataReader, the
publisher sends an extra copy, and near link saturation that copy steals
bandwidth from the real subscriber. The observer also reads a different copy, so
the loss it reports is not the loss your subscriber actually experienced. This
is structural to DDS pub/sub, not a bug in any one tool.
What ros2probe does differently
- Passive eBPF wire tap. Reads a kernel copy of RTPS off the wire.
- Never joins the domain. No participant, no DataReader, nothing added to the wire.
- Reads the same packets the subscriber gets. The loss and latency it reports match what the subscriber actually saw (recall 1.0).
- Full reconstruction in userspace. Topic graph, per-topic metrics, and message streams, independent of the DDS vendor.
Results on real hardware
3 platforms (laptop, Jetson, Raspberry Pi), 2 DDS implementations (Fast DDS, Cyclone DDS), 7 workloads, wired and wireless, two QoS settings.
| ros2probe | existing ros2 tools | |
|---|---|---|
| Loss it causes on the subscriber (recording the full near-GbE workload) | 0% | up to 75.5% (rosbag2) |
| Loss it reports vs what the subscriber saw | exact, recall 1.0 | 0.09 (rosbag2 at 10% loss) |
| Discovery graph perturbation | within 0.5% | up to 2.6x inflation |
| Observer CPU | up to 7x lower | baseline |
| Observer memory | up to 28x lower (1.7 MB vs ~47 MB) | baseline |
On a Raspberry Pi 4B, ros2 topic hz saturates a CPU core while ros2probe stays under 30%.
Usage. You already know the commands
Install once, then replace ros2 with rp:
ros2 topic hz /scan -> rp topic hz /scan
ros2 bag record /scan -> rp bag record /scan
Recordings are written as MCAP and replay with ros2 bag play. Scripts and CI
that parse hz output keep working unchanged.
ros2probe also ships a GUI (rp gui) with a live ROS graph, a per-topic monitor, and an MCAP recorder.
Status
- Works today on RTPS-based DDS. Tested on Fast DDS and Cyclone DDS.
- Shared-memory (SHM) transport is a structural limit, not a blind spot. The
topic graph is still recovered passively from discovery on the network, but
SHM payloads never reach the wire, so observing them falls back to a
short-lived, namespace-isolated shadow subscriber that joins the domain. For
those topics ros2probe still works, but gives up its probe-effect-free
guarantee. Network-transported topics keep the full benefit. - Zenoh (rmw_zenoh) uses a different wire protocol and is planned. RViz
integration is on the roadmap.
Links
- Project page (more figures and experiment plots): ros2probe · Non-intrusive Observability for ROS 2
- Code: GitHub - csi-dgist/ros2probe: Host-level observability for ROS 2 middleware traffic, without creating any ROS 2 subscriptions. · GitHub
- Paper (arXiv): [2606.10746] ros2probe: Non-intrusive, Kernel-selective Observability for Robot Operating System 2 Middleware
Feedback, issues, and real use cases are very welcome.
If you are curious about my other work, see https://hun0130.github.io/.




