Using ROS 2 Intra‑Process Communication to Reduce Latency in High‑Frequency Robot Loops
Learn how ROS 2 intra‑process communication cuts latency and CPU use by letting nodes share messages via shared memory instead of DDS.
10 May 2026, 17:47 UTC

Problem: High‑frequency sensor loops add latency
In many robotics applications a sensor publishes data at 100 Hz or higher while a controller subscribes to compute commands. When each node lives in its own process, ROS 2 routes every message through the DDS middleware, which adds serialization, deserialization, and copying overhead. This latency can become noticeable in tight control loops, especially on low‑power CPUs.
How ROS 2 intra‑process transport works
ROS 2’s middleware abstraction (RMW) lets a publisher and subscriber exchange messages via shared memory when they belong to the same process. If the QoS policy intra_process is set to true, the RMW layer detects matching topic names and types and passes a pointer to the message instead of copying it through DDS. The result is a latency reduction that can be an order of magnitude and far less CPU usage at high rates.
Enabling intra‑process with QoS
In C++ you create a rclcpp::QoS object and call .intra_process(true) before using it to create a publisher or subscriber. Setting the flag on only one side still allows intra‑process transport because the middleware negotiates the best available path; however, for deterministic behavior set the flag on both publisher and subscriber.
Worked example: merging talker and listener
#include
#include
class Talker : public rclcpp::Node {
public:
Talker() : Node("talker") {
auto qos = rclcpp::QoS(10).intra_process(true);
publisher_ = this->create_publisher("chatter", qos);
timer_ = this->create_wall_timer(
std::chrono::milliseconds(10),
[this]() {
auto msg = std_msgs::msg::String();
msg.data = "Hello " + std::to_string(count_++);
publisher_->publish(msg);
});
}
private:
rclcpp::Publisher::SharedPtr publisher_;
rclcpp::TimerBase::SharedPtr timer_;
size_t count_ = 0;
};
class Listener : public rclcpp::Node {
public:
Listener() : Node("listener") {
auto qos = rclcpp::QoS(10).intra_process(true);
subscription_ = this->create_subscription(
"chatter", qos,
[](const std_msgs::msg::String::SharedPtr msg) {
RCLCPP_INFO(this->get_logger(), "I heard: '%s%'", msg->data.c_str());
});
}
private:
rclcpp::Subscription::SharedPtr subscription_;
};
int main(int argc, char ** argv) {
rclcpp::init(argc, argv);
auto node = std::make_shared("combined");
auto talker = std::make_shared();
auto listener = std::make_shared();
rclcpp::spin(node);
rclcpp::shutdown();
return 0;
}
Trade‑offs and limitations
Intra‑process communication only works when nodes share a process; mixing intra‑ and inter‑process nodes on the same topic can cause missed messages if QoS settings differ. Because messages appear instantaneous inside a process, real‑time latency issues can be hidden during testing. Always benchmark with an inter‑process configuration to verify worst‑case latency.
Actionable closing
If you have high‑frequency loops, try merging the publisher and subscriber into a single executable with intra‑process QoS enabled. Measure latency with ros2 topic latency or a custom timer, compare to the baseline, and keep the inter‑process version as a reference for performance regression tests.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.