Topic hub

ROS 2 and middleware

How ROS 2 programs are built and connected. The node graph, parameters, launch and tf2, the rmw layer with DDS and Zenoh, QoS rules that silently block messages, ros2_control, micro-ROS and the current distributions.

Updated 14 Nov 2026 · ROS 2, node, topic, service, action, parameters, launch, tf2, DDS, QoS, rmw, Zenoh, ros2_control, micro-ROS, Jazzy, Lyrical

Overview

ROS 2 is not an operating system. It is a set of libraries, tools and conventions for building robot software as a graph of processes that exchange typed messages. Underneath sits a middleware layer, DDS by default, that handles discovery and transport. Most open robotics software, from drivers to MoveIt 2 and Nav2, assumes it. This hub is for engineers writing their first nodes or deciding how to structure a larger system. Code on this site is tested on ROS 2 Jazzy Jalisco (LTS, May 2024).

Key ideas

The graph: nodes, topics, services and actions

A node is one unit of computation, such as a camera driver or a planner. Nodes talk in three ways. A topic is anonymous publish and subscribe: any number of publishers, any number of subscribers, one message type. It suits sensor streams and state. A service is a request and a response, for short calls such as “load this map”. An action is a long-running goal with feedback and cancellation, such as “drive to this pose”, built from topics and services underneath. Each node also has typed parameters that can be set at start-up or changed while it runs. Launch files, written in Python, XML or YAML, start and configure many nodes together. tf2 keeps a time-stamped tree of coordinate frames, so any node can ask for the transform between two frames at a given time.

cameradetectortopic /imageplannermap_serverservice: requestresponsemissionnavigatoraction: goalfeedback, then resultyour nodesrclpy, rclcpprcl (C core)rmw interfaceFastDDSCycloneConnextZenohnetwork
Left, the three ways ROS 2 nodes communicate. Right, the layers between your code and the network. Client libraries sit on rcl, and the rmw interface lets you swap the middleware without changing node code.

Middleware, discovery and QoS

ROS 2 reaches the middleware through the rmw interface. In ROS 2 Lyrical Luth the default implementation is rmw_fastrtps_cpp (eProsima Fast DDS), and Cyclone DDS, RTI Connext and Zenoh are also Tier 1, according to the Lyrical supported platforms page. With DDS there is no central master: nodes discover each other on the network, and ROS_DOMAIN_ID separates groups that share one network. Zenoh uses a router for discovery instead.

Every publisher and subscriber carries a QoS profile: reliability (reliable or best effort), durability (volatile or transient local), history depth, deadline and liveliness. When the two ends are incompatible, they never connect. You get a warning in the log and no messages. A reliable subscriber will not accept data from a best-effort publisher, which is the classic reason a node “receives nothing” from a sensor driver. The block below runs in the Jazzy container and shows it.

# ros2
import rclpy
from rclpy.qos import QoSProfile, ReliabilityPolicy, HistoryPolicy
from std_msgs.msg import Int32


def count_received(pub_rel: ReliabilityPolicy, sub_rel: ReliabilityPolicy) -> int:
    node = rclpy.create_node("qos_demo")
    pub_qos = QoSProfile(depth=10, history=HistoryPolicy.KEEP_LAST, reliability=pub_rel)
    sub_qos = QoSProfile(depth=10, history=HistoryPolicy.KEEP_LAST, reliability=sub_rel)
    received = []
    pub = node.create_publisher(Int32, "counter", pub_qos)
    node.create_subscription(Int32, "counter", lambda msg: received.append(msg.data), sub_qos)
    for i in range(40):
        pub.publish(Int32(data=i))
        rclpy.spin_once(node, timeout_sec=0.05)
    node.destroy_node()
    return len(received)


rclpy.init()
matched = count_received(ReliabilityPolicy.BEST_EFFORT, ReliabilityPolicy.BEST_EFFORT)
mismatched = count_received(ReliabilityPolicy.BEST_EFFORT, ReliabilityPolicy.RELIABLE)
rclpy.shutdown()
print(f"best effort -> best effort: received {matched > 0}")
print(f"best effort -> reliable:    received {mismatched} messages")

The second case prints 0 and logs an incompatible QoS warning for the reliability policy. Durability matters too. The tf2 static broadcaster in tf2_ros publishes /tf_static with transient local durability, so a node that starts late still receives every static transform.

ros2_control and micro-ROS

ros2_control separates controllers from hardware. Its controller manager matches the command and state interfaces that each controller needs with those that each hardware component provides, and reports an error on a conflict. The hardware is described in the robot’s URDF. The same controllers, from the ros2_controllers repository, then run against a real arm, a simulator or a mock. micro-ROS brings nodes, publish and subscribe, services and lifecycle onto microcontrollers through a C API based on rcl. It uses eProsima Micro XRCE-DDS, with an agent on a larger computer bridging to the rest of the ROS 2 graph, and supports FreeRTOS, Zephyr and NuttX.

Distributions

A new ROS 2 distribution comes out each May. Releases in even years are LTS and are supported for about five years, and releases in odd years are supported for 1.5 years, according to the ROS 2 release schedule. The ROS 2 distributions page listed these supported releases on 14 November 2026.

Distribution Released End of life Notes
Lyrical Luth 22 May 2026 May 2031 LTS and the latest release. Tier 1 on Ubuntu 26.04 and Windows 11
Kilted Kaiju 23 May 2025 December 2026 Non-LTS
Jazzy Jalisco 23 May 2024 May 2029 LTS. Tier 1 on Ubuntu 24.04. Used for code on this site
Humble Hawksbill 23 May 2022 May 2027 LTS
Rolling Ridley Continuous None Development distribution

macOS is a Tier 3 platform for Lyrical, which means building from source. On a Mac, run ROS 2 in a Linux container or virtual machine.

Where to start

Hello ROS 2 writes a publisher and subscriber and runs them in the Jazzy container. The ROS 2 talker, listener and tf2 project adds parameters, launch and transforms. Keep the ROS 2 CLI cheat sheet open while you work, and see the URDF and MJCF reference for robot descriptions. The ROS platform page lists the main packages.

Tools and platforms

Tool What for Licence
rclpy and rclcpp Python and C++ client libraries Apache-2.0
Fast DDS Default DDS implementation Apache-2.0
Cyclone DDS Alternative DDS implementation EPL-2.0 or EDL-1.0
rmw_zenoh Zenoh-based middleware for ROS 2 Apache-2.0
ros2_control Controller manager and hardware abstraction Apache-2.0
micro-ROS ROS 2 on microcontrollers Apache-2.0
RViz and rosbag2 3D visualisation, and recording and replay BSD-3-Clause-Clear, Apache-2.0
SROS2 Security tooling: keys, certificates, access control Apache-2.0

Common mistakes

  • Mismatched QoS between a driver and your subscriber. Check with ros2 topic info -v.
  • Two robots on one network with the same ROS_DOMAIN_ID, each seeing the other’s topics.
  • Blocking inside a callback, which stalls every other callback on a single-threaded executor.
  • Publishing transforms with the wrong timestamp, so tf2 lookups fail or extrapolate.
  • Mixing distributions across machines. Messages may not match.
  • Building on a non-LTS release for a product that must run for years.

Going deeper