Your motion planner hits 50Hz on the bench. Deploy it to the vehicle and it chokes at 30Hz. The culprit isn't your A* heuristic or your costmap resolution. It's the middleware moving data between perception, prediction, and planning.
The Hidden Tax of Default DDS
ROS 2 Humble defaults to Cyclone DDS with best-effort reliability and keep-last history depth of 10. Fine for telemetry. Fatal for a planning loop that needs deterministic sub-10ms end-to-end latency. Every sample lost to best-effort drops forces a re-plan. Every history sample queued behind a slow subscriber adds jitter. The result: tail latency spikes that violate your real-time budget.
"We treated DDS as plumbing. It's actually the scheduler for your dataflow.
â Staff Engineer, Autonomous Trucking
Profile Before You Patch
Instrument the full chain: sensor driver -> perception -> prediction -> planner -> control. Use ros2 topic hz --window 1000 /planning/trajectory and ros2 doctor --report to baseline. Then enable Fast DDS statistics module or Cyclone's internal metrics to see per-sample latency histograms. Look for the 99th percentile, not the mean.
Three Knobs That Moved the Needle
We tested on a 12-core ARM SoC running Ubuntu 22.04 with RT kernel. Planner input: 10Hz perception, 20Hz prediction. Output: 50Hz trajectory. Baseline P99 latency: 18.4ms. After tuning: 11.1ms. That's 40% off the tail.
| Parameter | Baseline | Tuned | Why |
|---|---|---|---|
| Reliability | Best Effort | Reliable | Guarantees delivery for safety-critical path |
| History Depth | 10 | 1 | Eliminates queueing delay; only latest trajectory matters |
| Deadline | Infinite | 5ms | Triggers callback on late data; lets planner skip stale work |
| Transport | UDP Multicast | UDP Unicast + Shared Memory | Removes switch hops for intra-node pub/sub |
Shared Memory Is Not Free
Enabling ROS 2's zero-copy loaned messages (via rclcpp::LoanedMessage) cuts memcpy overhead. But it requires the publisher and subscriber in the same process or shared memory segment. We moved perception and prediction into a single composable node container. Planner stayed isolated for fault containment. Result: 1.2ms median memcpy savings per cycle.
Deadline Misses as Control Signals
With a 5ms deadline on the trajectory topic, the DDS middleware invokes the on_requested_deadline_missed callback when perception stalls. The planner treats this as a "hold last valid trajectory" trigger instead of blocking. This converts a latency spike into a graceful degradation â exactly what ISO 21448 SOTIF expects.
Validate Under Load
Run ros2 run ros2_benchmark benchmark_publisher_subscriber with CPU stress (stress-ng --cpu 12 --io 4 --vm 2). Verify P99 stays under 12ms. Inject packet loss with tc qdisc add dev eth0 root netem loss 2%. Reliable + depth 1 must still deliver. If it doesn't, your deadline is too tight or your transport is saturated.
âĻ
Ship It Checklist
1. Map every topic in your planning critical path. 2. Assign reliability, depth, deadline per criticality. 3. Enable zero-copy for intra-process high-rate topics. 4. Add deadline-miss handlers that degrade gracefully. 5. CI gate: P99 latency < 12ms under synthetic load. 6. Document the QoS profile in your interface control document (ICD) so fleet updates don't regress it.





