Publisher and Subscriber - dhanushshettigar/Getting-Started-With-ROS2 GitHub Wiki
- Background
- Create a Package
- Publisher Node
- Subscriber Node
- Customize Package File
- Customize Setup File
- Build the Package
- Test the Package
- Conclusion
Create nodes that pass information in the form of string messages to each other over a topic. The example used here is a simple “talker” and “listener” system; one node publishes data and the other subscribes to the topic so it can receive that data.
Navigate into the ros2_ws
directory.
Recall that packages should be created in the src
directory, not the root of the workspace. So, navigate into ros2_ws/src
.
cd ros2_ws/src
Run the package creation command:
ros2 pkg create --build-type ament_python --license Apache-2.0 py_pubsub
This command will create a package named py_pubsub
Navigate into ros2_ws/src/py_pubsub/py_pubsub
. Recall that this directory is a Python package with the same name as the ROS 2 package it’s nested in.
cd py_pubsub/py_pubsub
Now Create a new file named publisher_member_function.py
.
sudo nano publisher_member_function.py
Copy this below python code and save the file.
import rclpy
from rclpy.node import Node
from std_msgs.msg import String
class MinimalPublisher(Node):
def __init__(self):
super().__init__('minimal_publisher')
self.publisher_ = self.create_publisher(String, 'topic', 10)
timer_period = 0.5 # seconds
self.timer = self.create_timer(timer_period, self.timer_callback)
self.i = 0
def timer_callback(self):
msg = String()
msg.data = 'Hello World: %d' % self.i
self.publisher_.publish(msg)
self.get_logger().info('Publishing: "%s"' % msg.data)
self.i += 1
def main(args=None):
rclpy.init(args=args)
minimal_publisher = MinimalPublisher()
rclpy.spin(minimal_publisher)
# Destroy the node explicitly
# (optional - otherwise it will be done automatically
# when the garbage collector destroys the node object)
minimal_publisher.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()
Now Create a new file named subscriber_member_function.py
.
sudo nano subscriber_member_function.py
Copy this below python code and save the file.
import rclpy
from rclpy.node import Node
from std_msgs.msg import String
class MinimalSubscriber(Node):
def __init__(self):
super().__init__('minimal_subscriber')
self.subscription = self.create_subscription(
String,
'topic',
self.listener_callback,
10)
self.subscription # prevent unused variable warning
def listener_callback(self, msg):
self.get_logger().info('I heard: "%s"' % msg.data)
def main(args=None):
rclpy.init(args=args)
minimal_subscriber = MinimalSubscriber()
rclpy.spin(minimal_subscriber)
# Destroy the node explicitly
# (optional - otherwise it will be done automatically
# when the garbage collector destroys the node object)
minimal_subscriber.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()
Now the directory should have these files:
__init__.py
publisher_member_function.py
subscriber_member_function.py
You may have noticed in the return message after creating your package that the fields description
and license
contain TODO
notes. That’s because the package description and license declaration are not automatically set, but are required if you ever want to release your package. The maintainer
field may also need to be filled in.
From ros2_ws/src/py_pubsub
cd ..
Open package.xml
sudo nano package.xml
Input your name and email on the maintainer
line if it hasn’t been automatically populated for you.
<maintainer email="your_email">your_name</maintainer>
Then, edit the description
line to summarize the package:
<description>summarize your package</description>
After the lines above, add the following dependencies corresponding to your node’s import statements:
<exec_depend>rclpy</exec_depend>
<exec_depend>std_msgs</exec_depend>
This declares the package needs rclpy
and std_msgs
when its code is executed.
Examine the file.
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3">
<name>py_pubsub</name>
<version>0.0.1</version>
<description>A Simple Publisher and Subscriber</description>
<maintainer email="[email protected]">dhanush</maintainer>
<license>Apache-2.0</license>
<test_depend>ament_copyright</test_depend>
<test_depend>ament_flake8</test_depend>
<test_depend>ament_pep257</test_depend>
<test_depend>python3-pytest</test_depend>
<exec_depend>rclpy</exec_depend>
<exec_depend>std_msgs</exec_depend>
<export>
<build_type>ament_python</build_type>
</export>
</package>
Don’t forget to save once you’re done editing.
Open the setup.py
file.
sudo nano setup.py
Again, match the maintainer
, maintainer_email
, description
and license
fields to your package.xml
:
maintainer='dhanush',
maintainer_email='[email protected]',
description='A Simple Publisher and Subscriber',
license='Apache-2.0',
Add the following line within the console_scripts
brackets of the entry_points
field:
entry_points={
'console_scripts': [
'talker = py_pubsub.publisher_member_function:main',
'listener = py_pubsub.subscriber_member_function:main',
],
},
Examine the file.
from setuptools import find_packages, setup
package_name = 'py_pubsub'
setup(
name=package_name,
version='0.0.0',
packages=find_packages(exclude=['test']),
data_files=[
('share/ament_index/resource_index/packages',
['resource/' + package_name]),
('share/' + package_name, ['package.xml']),
],
install_requires=['setuptools'],
zip_safe=True,
maintainer='dhanush',
maintainer_email='[email protected]',
description='A Simple Publisher and Subscriber',
license='Apache-2.0',
tests_require=['pytest'],
entry_points={
'console_scripts': [
'talker = py_pubsub.publisher_member_function:main',
'listener = py_pubsub.subscriber_member_function:main',
],
},
)
Don’t forget to save once you’re done editing.
You likely already have the rclpy
and std_msgs
packages installed as part of your ROS 2 system. It’s good practice to run rosdep in the root of your workspace (ros2_ws) to check for missing dependencies before building:
rosdep install -i --from-path src --rosdistro jazzy -y
Go to the workspace's root.
cd ../..
Build your new package:
colcon build --packages-select py_pubsub
The output should appear as Summary: 1 package finished
Open a new terminal, navigate to ros2_ws
, and source the setup files:
source install/setup.bash
Now run the talker node:
ros2 run py_pubsub talker
The terminal should start publishing info messages every 0.5 seconds, like so:
[INFO] [minimal_publisher]: Publishing: "Hello World: 0"
[INFO] [minimal_publisher]: Publishing: "Hello World: 1"
[INFO] [minimal_publisher]: Publishing: "Hello World: 2"
[INFO] [minimal_publisher]: Publishing: "Hello World: 3"
[INFO] [minimal_publisher]: Publishing: "Hello World: 4"
...
Open another terminal, source the setup files from inside ros2_ws
again.
source install/setup.bash
Start the listener node:
ros2 run py_pubsub listener
The listener will start printing messages to the console, starting at whatever message count the publisher is on at that time, like so:
[INFO] [minimal_subscriber]: I heard: "Hello World: 10"
[INFO] [minimal_subscriber]: I heard: "Hello World: 11"
[INFO] [minimal_subscriber]: I heard: "Hello World: 12"
[INFO] [minimal_subscriber]: I heard: "Hello World: 13"
[INFO] [minimal_subscriber]: I heard: "Hello World: 14"
Enter Ctrl+C
in each terminal to stop the nodes from spinning.
You created two nodes to publish and subscribe to data over a topic. Before running them, you added their dependencies and entry points to the package configuration files.