Creating Publisher and Subscriber Nodes in ROS 2 with Python
Last updated: December 22, 2024
1. Introduction
In this lesson, we will learn how to create ROS 2 publisher and subscriber nodes using Python. We will:
- Understand what a ROS 2 package is.
- Learn about the purpose of a publisher and a subscriber.
- Explore how to use ROS 2 CLI tools to interact with nodes and topics.
- Implement a simple talker (publisher) and listener (subscriber) node in the talker_listener package.
Complete code: talker_listener_py on Edreate GitHub
1a. What is a ROS 2 Package?
A ROS 2 package is a structured directory that contains nodes, libraries, launch files, and configuration files. It acts as a modular component of your ROS 2 project. The package is defined by a package.xml
file and typically built with colcon
. For Python-based ROS 2 nodes, we often use the ament_python
build type.
1b. What is a Publisher?
A publisher is a ROS 2 node element that sends messages over a specified topic. Other nodes can subscribe to that topic to receive data. Publishers are created using create_publisher()
, where you specify the message type, topic name, and queue size.
1c. What is a Subscriber?
A subscriber is a ROS 2 node element that listens for messages on a specified topic. It uses a callback function to process incoming data. Subscribers are created using create_subscription()
, providing the message type, topic name, callback, and queue size.
2. Step-by-Step Implementation
2a. Prerequisites
Make sure you have:
- ROS 2 Humble or later installed.
colcon
build tool installed.- Python 3 installed.
Install necessary dependencies if you haven’t already:
sudo apt update
sudo apt install python3-colcon-common-extensions
2b. Set Up the Workspace
Create a new workspace and source ROS 2:
mkdir -p ~/ros2_ws/src
cd ~/ros2_ws
source /opt/ros/humble/setup.bash
Create a new Python-based ROS 2 package named talker_listener
:
2c. Create the Talker (Publisher)
Navigate into the talker_listener
directory and create a file named talker.py
:
talker.py:
import rclpy
from rclpy.node import Node
from std_msgs.msg import String
class Talker(Node):
def __init__(self):
super().__init__("talker_py")
self.publisher = self.create_publisher(String, "talker", 10)
self.timer = self.create_timer(1.0, self.talker_callback)
self.talker_count = 0
def talker_callback(self):
msg = String()
msg.data = f"Hello from talker: {self.talker_count}"
self.publisher.publish(msg)
self.get_logger().info(f"Published message: {msg.data}")
self.talker_count += 1
def main(args=None):
rclpy.init(args=args)
talker = Talker()
rclpy.spin(talker)
if __name__ == "__main__":
main()
Update setup.py
Open setup.py
and update it to include the talker entry point:
from setuptools import find_packages, setup
package_name = "talker_listener_py"
setup(
name=package_name,
version="0.0.1",
packages=find_packages(exclude=["test"]),
data_files=[
(
"share/ament_index/resource_index/packages",
["resource/" + package_name],
),
(
"share/" + package_name,
["package.xml"],
),
# Launch file
(
"share/talker_listener_py/launch",
["launch/talker_listener_py.launch.py"],
),
],
install_requires=["setuptools"],
zip_safe=True,
maintainer="edreate",
maintainer_email="edreate.dev@gmail.com",
description="Basic talker listener node in python.",
license="MIT",
tests_require=["pytest"],
entry_points={
"console_scripts": [
"talker = talker_listener_py.talker:main",
"listener = talker_listener_py.listener:main",
],
},
)
2d. Build and Run the Talker Node
From the root of your workspace:
cd ~/ros2_ws
colcon build --symlink-install
Terminal Output Example:
Starting >>> talker_listener_py
Finished <<< talker_listener_py [1.81s]
Summary: 1 package finished [2.30s]
Source the newly built setup files:
source install/setup.bash
Now run the talker node:
ros2 run talker_listener_py talker
Expected Terminal Output:
[INFO] [1734783147.274020622] [talker_py]: Published message: Hello from talker: 0
[INFO] [1734783148.251519650] [talker_py]: Published message: Hello from talker: 1
[INFO] [1734783149.251430572] [talker_py]: Published message: Hello from talker: 2
[INFO] [1734783150.251331157] [talker_py]: Published message: Hello from talker: 3
Leave this running, or open a new terminal for the next steps.
3. Create the Listener (Subscriber)
In the talker_listener
package, create listener.py
:
listener.py:
import rclpy
from rclpy.node import Node
from std_msgs.msg import String
class Listener(Node):
def __init__(self):
super().__init__("listener_py")
self.subscriber = self.create_subscription(String, "talker", self.listener_callback, 10)
def listener_callback(self, msg: String):
self.get_logger().info(f"Listener received messaged: {msg.data}")
def main(args=None):
rclpy.init(args=args)
listener = Listener()
rclpy.spin(listener)
if __name__ == "__main__":
main()
Add Listener to setup.py
Update setup.py
to include the listener node:
entry_points={
'console_scripts': [
'talker = talker_listener_py.talker:main',
'listener = talker_listener_py.listener:main'
],
}
Rebuild the package:
cd ~/ros2_ws
colcon build --symlink-install
source install/setup.bash
Terminal Output Example:
Starting >>> talker_listener_py
Finished <<< talker_listener_py [1.76s]
Summary: 1 package finished [2.27s]
3a. Run the Listener Node
In a new terminal (with the workspace sourced):
ros2 run talker_listener_py listener
Expected Terminal Output:
[INFO] [1734783204.283136954] [listener_py]: Listener received messaged: Hello from talker: 0
[INFO] [1734783205.251829147] [listener_py]: Listener received messaged: Hello from talker: 1
[INFO] [1734783206.251647974] [listener_py]: Listener received messaged: Hello from talker: 2
4. ROS 2 CLI Tools
You can verify your running nodes and topics using the ROS 2 CLI:
List active nodes:
ros2 node list
Expected Output:
/listener_py
/talker_py
List available topics:
ros2 topic list
Expected Output:
/parameter_events
/rosout
/talker
Echo messages from the talker topic (in another terminal):
ros2 topic echo /talker
Expected Output:
data: 'Hello from talker: 1'
---
data: 'Hello from talker: 2'
---
These tools help verify that nodes and topics are working as expected.
5. Summary
In this lesson, we:
- Created a ROS 2 package using
ament_python
. - Implemented a talker node (publisher) and a listener node (subscriber).
- Verified their functionality using ROS 2 CLI tools such as
ros2 node list
andros2 topic echo
.
Complete code: talker_listener_py on Edreate GitHub
This fundamental setup of publishers and subscribers forms the building block for more complex robotic applications in ROS 2.