PubSubPy - arieldo/MorBot GitHub Wiki
Writing a Simple Publisher and Subscriber communication system in Python for ROS
- Introduction
- Prerequisites
- Create a new package
- Create a new publisher
- Create a new subscriber
- Run the publisher and subscriber
- View the publisher and subscriber nodes
- View the publisher and subscriber topics
- Multiple Publishers and Subscribers in ROS
Robotic system relies on communication between all components . Learn how to create a simple publisher and subscriber using Python in the ROS. The publisher will send messages, and the subscriber will receive and process them.
- Familiarity with ROS concepts, such as nodes, topics, and messages , IF not Check out ROS 101
- Basic understanding How To Creating New Packages in ROS
- Basic understanding of Python
- Open a terminal window.
- Navigate to the
srcdirectory of your ROS workspace, e.g.,~/MorBot/morbot_ros/morbot_ws/src:
cd ~/MorBot/morbot_ros/morbot_ws/src- Use the
catkin_create_pkgcommand to create a new package.
catkin_create_pkg simple_pub_sub rospy std_msgsCreate a new directory named scripts inside your package directory (~/MorBot/morbot_ros/morbot_ws/src/simple_pub_sub):
cd ~/MorBot/morbot_ros/morbot_ws/src/simple_pub_sub
mkdir scripts - Create a new source file inside the
scriptsdirectory. For example, create a Python file namedtalker.py:
cd scripts
touch talker.py- Make the script executable:
cd ~/MorBot/morbot_ros/morbot_ws/src/simple_pub_sub/scripts
sudo chmod +x talker.py- Open the
talker.pyfile in a text editor, and paste the following code:
#!/usr/bin/env python
# Import the required modules
import rospy
from std_msgs.msg import String
# Define the main function for the publisher
def talker():
# Initialize the ROS node with a unique name
rospy.init_node('talker', anonymous=True)
# Create a publisher object that will publish messages to the 'chatter' topic
pub = rospy.Publisher('chatter', String, queue_size=10)
# Set the publishing rate to 1 message per second
rate = rospy.Rate(1)
# Keep publishing messages until the node is shut down
while not rospy.is_shutdown():
# Create the message with the current ROS time
message = "Hello, ROS! %s" % rospy.get_time()
# Log the message to the console
rospy.loginfo(message)
# Publish the message to the 'chatter' topic
pub.publish(message)
# Sleep for a while to maintain the publishing rate
rate.sleep()
# Check if the script is being run as the main program
if __name__ == '__main__':
try:
# Call the main function to start the publisher node
talker()
except rospy.ROSInterruptException:
# If an exception occurs, exit gracefully without displaying an error message
passlet's break down talker.py
#!/usr/bin/env pythonThis line tells the system that this is a Python script, and that ROS should execute it using the Python interpreter.
import rospy
from std_msgs.msg import StringThis line imports the rospy client library and String message type from the std_msgs package.
def talker():
rospy.init_node('talker', anonymous=True)
pub = rospy.Publisher('chatter', String, queue_size=10)
rate = rospy.Rate(1) # 1 message per secondIn the talker() function, the main function for the publisher node, the talker node is initialized with a unique name using anonymous=True. A Publisher object named pub is created to publish messages of type String to the chatter topic, with a buffer of up to 10 messages. A Rate object named rate is used to define a frequency for looping, set to 1 Hz or 1 message per second, allowing for consistent message publishing.
while not rospy.is_shutdown():
message = "Hello, ROS! %s" % rospy.get_time()
rospy.loginfo(message)
pub.publish(message)
rate.sleep()This loop continues until the rospy shutdown signal is received, such as when you type Ctrl+C in the terminal window, publishing a string message containing the current ROS time to the chatter topic, logging the message to the rosout log file, and sleeping just long enough to maintain the desired rate through the loop.
if __name__ == '__main__':
try:
talker()
except rospy.ROSInterruptException:
passThis line tells Python to execute the talker() function when the script is executed directly, but not when it is imported as a module.
- Save the file and close the text editor.
-
Create a new source file inside the
scriptsdirectory. For example, create a Python file namedlistener.py:cd ~/MorBot/morbot_ros/morbot_ws/src/simple_pub_sub/scripts touch listener.py
-
Make the script executable:
cd ~/MorBot/morbot_ros/morbot_ws/src/simple_pub_sub/scripts chmod +x listener.py
-
Open the
listener.pyfile in a text editor, and paste the following code:
#!/usr/bin/env python
# Import the rospy and String message type from the std_msgs
import rospy
from std_msgs.msg import String
# Define the callback function that will be executed when a message is received on the 'chatter' topic
def callback(msg):
rospy.loginfo("I heard: %s", msg.data) # Log the received message data
# Define the main function for the subscriber node
def listener():
rospy.init_node('listener', anonymous=True) # Initialize a ROS node named 'listener', with the anonymous flag set to True
rospy.Subscriber('chatter', String, callback) # Create a subscriber that listens to the 'chatter' topic and calls the 'callback' function when a message is received
rospy.spin() # Keep the node running and processing callbacks until it is shut down
# Check if the script is being run as the main program and not imported as a module
if __name__ == '__main__':
listener() # Call the listener() function to start the subscriber nodelet's break down listener.py
This code defines a simple ROS subscriber node that listens to the 'chatter' topic for String messages. When a message is received, the callback function is called, which logs the received message data using rospy.loginfo().
#!/usr/bin/env pythonThis line tells the system that this is a Python interpreter.
import rospy
from std_msgs.msg import StringThis line imports the rospy client library and String message type from the std_msgs package.
def callback(msg):
rospy.loginfo("I heard: %s", msg.data)
This line defines the callback() function, which is the callback function for the subscriber node. The callback function is called whenever a message is received by the subscriber.
def listener():
rospy.init_node('listener', anonymous=True)
rospy.Subscriber('chatter', String, callback)
rospy.spin() In the listener node, initialize a ROS node named listener with the anonymous flag set to True. Then, create a subscriber that listens to the chatter topic and calls the callback function upon receiving a message.
The node continues running and processing callbacks until it is eventually shut down.
if __name__ == '__main__':
listener()This line tells Python to execute the listener() function when the script is executed directly, but not when it is imported as a module.
- Save the file and close the text editor.
- Open a terminal window.
- Navigate to the root of your ROS workspace,
morbot_ws,Build the package and Source it :
cd ~/MorBot/morbot_ros/morbot_ws
catkin_make
source ~/MorBot/morbot_ros/morbot_ws/devel/setup.bashOpen three new terminal windows. In each, source your ROS workspace
-
Open a terminal window.
-
Run the master node:
roscore-
Open a new terminal window.
-
Run the publisher node:
rosrun simple_pub_sub talker.py-
Open a new terminal window.
-
Run the subscriber node:
rosrun simple_pub_sub listener.py-
Open a new terminal window.
-
View the list of active nodes:
rosnode list- View information about the
talkernode:
rosnode info /talker- View information about the
listenernode:
rosnode info /listener-
Open a new terminal window.
-
View the list of active topics:
rostopic list- View information about the
/chattertopic:
rostopic info /chatter- View the messages being published on the
/chattertopic:
rostopic echo /chatter- Observe the messages being published by the 'talker' node and received by the 'listener' node in the terminal windows.
Extending the "Talker" and "Listener" Nodes in ROS with an Additional Integer Publisher and Subscriber - An Example of Multi-Topic Nodes.
In a typical ROS setup, you will often find scenarios where multiple publishers and subscribers are operating concurrently. This situation arises because different nodes can handle various aspects of a robotic system, such as sensor data acquisition, data processing, and actuator control. Therefore, multiple publishers and subscribers are necessary to facilitate inter-node communication.
- Create a new source file inside the
scriptsdirectory. For example, create a Python file namedmultitopic_talker.py:
cd ~/MorBot/morbot_ros/morbot_ws/src/simple_pub_sub/scripts
touch multitopic_talker.py- Make the script executable:
cd ~/MorBot/morbot_ros/morbot_ws/src/simple_pub_sub/scripts
sudo chmod +x multitopic_talker.py- Open the
multitopic_talker.pyfile in a text editor, and paste the following code:
#!/usr/bin/env python
# Import the required modules
import rospy
from std_msgs.msg import String, Int32
# Define the main function for the publisher
def talker():
# Initialize the ROS node with a unique name
rospy.init_node('multitopic_talker', anonymous=True)
# Create a publisher object that will publish messages to the 'chatter' topic
pub = rospy.Publisher('chatter', String, queue_size=10)
# Create a second publisher that will publish messages to the 'numbers' topic
pub2 = rospy.Publisher('numbers', Int32, queue_size=10)
# Set the publishing rate to 1 message per second
rate = rospy.Rate(1)
# Initialize an integer to publish
number = 0
# Keep publishing messages until the node is shut down
while not rospy.is_shutdown():
# Create the message with the current ROS time
message = "Hello, ROS!"
# Log the message to the console
rospy.loginfo(message)
# Log the integer to the console
rospy.loginfo(number)
# Publish the message to the 'chatter' topic
pub.publish(message)
# Publish the integer to the 'numbers' topic
pub2.publish(number)
# Increment the number
number += 1
# Sleep for a while to maintain the publishing rate
rate.sleep()
# Check if the script is being run as the main program
if __name__ == '__main__':
try:
# Call the main function to start the publisher node
talker()
except rospy.ROSInterruptException:
# If an exception occurs, exit gracefully without displaying an error message
pass- let's break down this Python script:
# Import the required modules
import rospy
from std_msgs.msg import String, Int32- Import the
rospymodule, which is the Python client library for ROS. Also, import theStringandInt32message types from thestd_msgspackage, which contains common message types used in ROS.
def talker():
rospy.init_node('multitopic_talker', anonymous=True)- Define the
talker()function, which will be executed when the node is run. Inside the function, initialize the ROS node with the namemultitopic_talkerand make it anonymous.
pub = rospy.Publisher('chatter', String, queue_size=10)
pub2 = rospy.Publisher('numbers', Int32, queue_size=10)These lines create two publisher objects. One publishes String messages to the 'chatter' topic, and the other publishes Int32 messages to the 'numbers' topic.
rate = rospy.Rate(1)
number = 0This sets the publishing rate to 1 message per second and initializes a number that will be published.
while not rospy.is_shutdown():This line starts a while loop that will continue until the node is shut down.
message = "Hello, ROS! %s"
rospy.loginfo(message)
rospy.loginfo(number)This creates a message string, logs it, and then logs the current number.
pub.publish(message)
pub2.publish(number)
number += 1These lines publish the message and the number to their respective topics, and then increment the number as a Demo ! .
-
Create a new source file inside the
scriptsdirectory. For example, create a Python file namedmultitopic_listener.py:cd ~/MorBot/morbot_ros/morbot_ws/src/simple_pub_sub/scripts touch multitopic_listener.py
-
Make the script executable:
cd ~/MorBot/morbot_ros/morbot_ws/src/simple_pub_sub/scripts chmod +x multitopic_listener.py
-
Open the
multitopic_listener.pyfile in a text editor, and paste the following code:
#!/usr/bin/env python
# Import the rospy and String, Int32 message type from the std_msgs
import rospy
from std_msgs.msg import String, Int32
# Define the callback function that will be executed when a message is received on the 'chatter' topic
def chatter_callback(msg):
rospy.loginfo("I heard on chatter: %s", msg.data) # Log the received message data
# Define another callback for the 'numbers' topic
def numbers_callback(msg):
rospy.loginfo("I heard on numbers: %d", msg.data)
# Define the main function for the subscriber node
def listener():
rospy.init_node('multitopic_listener', anonymous=True) # Initialize a ROS node named 'listener', with the anonymous flag set to True
rospy.Subscriber('chatter', String, chatter_callback) # Create a subscriber that listens to the 'chatter' topic and calls the 'chatter_callback' function when a message is received
rospy.Subscriber('numbers', Int32, numbers_callback) # Create another subscriber that listens to the 'numbers' topic and calls the 'numbers_callback' function when a message is received
rospy.spin() # Keep the node running and processing callbacks until it is shut down
# Check if the script is being run as the main program and not imported as a module
if __name__ == '__main__':
listener() # Call the listener() function to start the subscriber node- let's break down this Python script:
import rospy
from std_msgs.msg import String, Int32 These lines import the necessary ROS Python module (rospy) and the message types (String and Int32) that will be used in the node.
def chatter_callback(msg):
rospy.loginfo("I heard on chatter: %s", msg.data) # Log the received message dataThis function is called when a message is received on the chatter topic. It logs the received message data to the console.
def numbers_callback(msg):
rospy.loginfo("I heard on numbers: %d", msg.data)This function is called when a message is received on the numbers topic. It logs the received message data to the console.
def listener():
rospy.init_node('multitopic_listener', anonymous=True) # Initialize a ROS node named 'listener', with the anonymous flag set to True
rospy.Subscriber('chatter', String, chatter_callback) # Create a subscriber that listens to the 'chatter' topic and calls the 'chatter_callback' function when a message is received
rospy.Subscriber('numbers', Int32, numbers_callback) # Create another subscriber that listens to the 'numbers' topic and calls the 'numbers_callback' function when a message is received
rospy.spin() # Keep the node running and processing callbacks until it is shut downThis function initializes the subscriber node, creates two subscribers that listen to the chatter and numbers topics, and keeps the node running until it is shut down.
- Open a terminal window.
- Navigate to the root of your ROS workspace,
morbot_ws,Build the package and Source it :
cd ~/MorBot/morbot_ros/morbot_ws
catkin_make
source ~/MorBot/morbot_ros/morbot_ws/devel/setup.bashOpen three new terminal windows. In each, source your ROS workspace
-
Open a terminal window.
-
Run the master node:
roscore-
Open a new terminal window.
-
Run the publisher node:
rosrun simple_pub_sub multitopic_talker.py-
Open a new terminal window.
-
Run the subscriber node:
rosrun simple_pub_sub multitopic_listener.pyCongratulations! You have successfully created a simple publisher and subscriber using Python in ROS. This tutorial covered the basics, but there is much more to explore in the world of ROS. Keep learning and experimenting with different nodes, topics, and messages to expand your knowledge.
- Build and run a simple Robot Motion simulator Ros-turtlesim