ROS 2 Humble DDS Tuning Cuts Planner Latency 40%

Autonomous Vehicles
Date:October 31, 2026
Topic:
ROS 2 Humble DDS Tuning Cuts Planner Latency 40%
⏱ 3 min read

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.

yaml
# Cyclone DDS XML snippet for planner-critical topics
<domain>
  <topic>
    <name>planning/trajectory</name>
    <reliability>reliable</reliability>
    <history>keep-last</history>
    <depth>1</depth>
    <deadline>
      <sec>0</sec>
      <nanosec>5000000</nanosec>
    </deadline>
    <lifespan>
      <sec>0</sec>
      <nanosec>20000000</nanosec>
    </lifespan>
  </topic>
</domain>

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.

ParameterBaselineTunedWhy
ReliabilityBest EffortReliableGuarantees delivery for safety-critical path
History Depth101Eliminates queueing delay; only latest trajectory matters
DeadlineInfinite5msTriggers callback on late data; lets planner skip stale work
TransportUDP MulticastUDP Unicast + Shared MemoryRemoves switch hops for intra-node pub/sub
💡
TipSet history depth to 1 on the planner's subscription. The planner only ever needs the freshest trajectory. Older samples are waste.

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.

cpp
// Publisher side: loaned message for zero-copy
auto loaned_msg = publisher->loan_message();
if (loaned_msg) {
  auto& msg = loaned_msg.get();
  fill_trajectory(msg);
  publisher->publish(std::move(loaned_msg));
}

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.

âš ī¸
WarningDeadline callbacks run on the DDS receive thread. Do not lock mutexes or call heavy logic. Set an atomic flag; let the planner loop check it.

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.

â„šī¸
NoteNext step: Generate your DDS QoS XML from a YAML schema checked into version control. Treat middleware config like code — review, test, version.
Share𝕏 Twitterin LinkedInin Whatsapp