ROS2 Core Communication
Node
What is a node
A node is the smallest execution unit in ROS 2. Each node is a process that uses ROS 2 APIs to communicate with other nodes. In a robot system, nodes are usually split by responsibility, for example:
- Sensor data acquisition
- Data processing and perception
- Motion control
- User interaction
Node characteristics
| Characteristic | Description |
|---|---|
| Lightweight | A system can run many small nodes instead of one large process |
| Distributed | Nodes can run across different machines |
| Loosely coupled | Nodes communicate through topics/services/actions |
| Composable | Multiple nodes can be combined into larger functions |
| Independent lifecycle | Each node can be started, stopped, and restarted independently |
Node naming rules
- Node names must be unique within the same namespace.
- Use letters, numbers, and underscores.
- Names are case-sensitive.
- Prefer descriptive names, such as
camera_driverorpath_planner.
Hello World Node Example
Create a Python package
cd ~/workspace/src
ros2 pkg create pkg_helloworld_py --build-type ament_python --dependencies rclpy --node-name helloworldWrite node code
Edit pkg_helloworld_py/pkg_helloworld_py/helloworld.py:
import rclpy
from rclpy.node import Node
class HelloWorldNode(Node):
def __init__(self):
super().__init__('helloworld')
self.timer = self.create_timer(0.5, self.timer_callback)
def timer_callback(self):
self.get_logger().info('Hello World')
def main(args=None):
rclpy.init(args=args)
node = HelloWorldNode()
rclpy.spin(node)
node.destroy_node()
rclpy.shutdown()Build and run
cd ~/workspace
colcon build --packages-select pkg_helloworld_py
source install/setup.bash
ros2 run pkg_helloworld_py helloworldNext steps
After this section, continue with:
- Topic communication
- Service communication
- Action communication
Topic Communication
Topic overview
What is topic communication
Topics use a publish/subscribe model. Publishers send messages to a topic, and subscribers receive messages from that topic. Publishers and subscribers do not need to know each other directly.
Topic characteristics
| Characteristic | Description | Typical use |
|---|---|---|
| Asynchronous | No request/response blocking | Sensor streams |
| One-to-many | Multiple subscribers can receive the same message | Data broadcast |
| Loosely coupled | Nodes only depend on topic name + message type | Modular systems |
| Streaming-friendly | Good for continuous data | Telemetry and perception |
Topic naming rules
- Topic names can be absolute (start with
/) or relative. - Use lowercase letters, numbers, underscores, and
/. - Keep namespace hierarchy clear.
Examples:
- Recommended:
/cmd_vel,/camera/image_raw,/sensor/front_camera/image - Not recommended:
/MyTopic(uppercase)
Topic communication example
Create package
cd ~/workspace/src
ros2 pkg create pkg_topic --build-type ament_python --dependencies rclpy std_msgs --node-name publisher_demoPublisher (publisher_demo.py)
import rclpy
from rclpy.node import Node
from std_msgs.msg import String
class TopicPub(Node):
def __init__(self):
super().__init__('publisher_node')
self.pub = self.create_publisher(String, '/topic_demo', 10)
self.timer = self.create_timer(1.0, self.pub_msg)
def pub_msg(self):
msg = String()
msg.data = 'Hi, I send a message.'
self.pub.publish(msg)
def main(args=None):
rclpy.init(args=args)
node = TopicPub()
rclpy.spin(node)
node.destroy_node()
rclpy.shutdown()Subscriber (subscriber_demo.py)
import rclpy
from rclpy.node import Node
from std_msgs.msg import String
class TopicSub(Node):
def __init__(self):
super().__init__('subscriber_node')
self.sub = self.create_subscription(String, '/topic_demo', self.sub_callback, 10)
def sub_callback(self, msg):
self.get_logger().info(msg.data)
def main(args=None):
rclpy.init(args=args)
node = TopicSub()
rclpy.spin(node)
node.destroy_node()
rclpy.shutdown()Update setup.py
Add console scripts:
'publisher_demo = pkg_topic.publisher_demo:main',
'subscriber_demo = pkg_topic.subscriber_demo:main',Build and run
cd ~/workspace
colcon build --packages-select pkg_topic
source install/setup.bashTerminal A:
ros2 run pkg_topic publisher_demoTerminal B:
ros2 run pkg_topic subscriber_demoOptional verification:
ros2 topic list
ros2 topic echo /topic_demoService Communication
Service overview
What is service communication
Services use request/response communication. A client sends a request, and a server processes it and returns a response.
Services vs topics
| Feature | Services | Topics |
|---|---|---|
| Pattern | Request/response | Publish/subscribe |
| Sync behavior | Usually synchronous from caller perspective | Asynchronous |
| Best for | Short operations and queries | Continuous streams |
| Return value | Required | Not required |
Service interface example
example_interfaces/srv/AddTwoInts:
- Request:
int64 a,int64 b - Response:
int64 sum
Service communication example
Create package
cd ~/workspace/src
ros2 pkg create pkg_service --build-type ament_python --dependencies rclpy example_interfaces --node-name server_demoService server (server_demo.py)
import rclpy
from rclpy.node import Node
from example_interfaces.srv import AddTwoInts
class ServiceServer(Node):
def __init__(self):
super().__init__('service_server')
self.srv = self.create_service(AddTwoInts, '/add_two_ints', self.callback)
def callback(self, request, response):
response.sum = request.a + request.b
self.get_logger().info(f'request: a={request.a}, b={request.b}, sum={response.sum}')
return response
def main(args=None):
rclpy.init(args=args)
node = ServiceServer()
rclpy.spin(node)
node.destroy_node()
rclpy.shutdown()Service client (client_demo.py)
import rclpy
from rclpy.node import Node
from example_interfaces.srv import AddTwoInts
class ServiceClient(Node):
def __init__(self):
super().__init__('service_client')
self.client = self.create_client(AddTwoInts, '/add_two_ints')
while not self.client.wait_for_service(timeout_sec=1.0):
self.get_logger().info('service not available, waiting...')
def send_request(self, a, b):
req = AddTwoInts.Request()
req.a = a
req.b = b
return self.client.call_async(req)
def main(args=None):
rclpy.init(args=args)
node = ServiceClient()
future = node.send_request(10, 90)
rclpy.spin_until_future_complete(node, future)
if future.result() is not None:
node.get_logger().info(f'Result: {future.result().sum}')
else:
node.get_logger().error('Service call failed')
node.destroy_node()
rclpy.shutdown()Update setup.py
'server_demo = pkg_service.server_demo:main',
'client_demo = pkg_service.client_demo:main',Build and run
cd ~/workspace
colcon build --packages-select pkg_service
source install/setup.bashTerminal A:
ros2 run pkg_service server_demoTerminal B:
ros2 run pkg_service client_demoManual CLI test:
ros2 service list
ros2 interface show example_interfaces/srv/AddTwoInts
ros2 service call /add_two_ints example_interfaces/srv/AddTwoInts "{a: 1, b: 4}"Action Communication
Action overview
What is action communication
Actions are designed for long-running tasks that need:
- Progress feedback during execution
- Final result when done
- Optional cancellation while running
Action vs service
| Feature | Action | Service |
|---|---|---|
| Task duration | Long-running | Short |
| Feedback | Supported | Not supported |
| Cancellation | Supported | Not supported |
| Return model | Goal + Feedback + Result | Request + Response |
Action communication example
You can quickly verify actions with turtlesim:
- Start turtlesim:
ros2 run turtlesim turtlesim_node- Send an action goal:
ros2 action send_goal /turtle1/rotate_absolute turtlesim/action/RotateAbsolute "{theta: 1.57}"- Watch available actions and interfaces:
ros2 action list
ros2 action info /turtle1/rotate_absolute
ros2 interface show turtlesim/action/RotateAbsoluteExpected result:
- The turtle rotates to the target angle.
- The action returns a final result when complete.
Next steps
After core communication, continue with:
- ROS 2 advanced interfaces and middleware
- ROS 2 tools and launch
- ROS 2 simulation and vision