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
src
directory of your ROS workspace, e.g.,~/MorBot/morbot_ros/morbot_ws/src
:
cd ~/MorBot/morbot_ros/morbot_ws/src
- Use the
catkin_create_pkg
command to create a new package.
catkin_create_pkg simple_pub_sub rospy std_msgs
Create 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
scripts
directory. 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.py
file 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
pass
let's break down talker.py
#!/usr/bin/env python
This 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 String
This 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 second
In 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:
pass
This 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
scripts
directory. 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.py
file 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 node
let'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 python
This line tells the system that this is a Python interpreter.
import rospy
from std_msgs.msg import String
This 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.bash
Open 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
talker
node:
rosnode info /talker
- View information about the
listener
node:
rosnode info /listener
-
Open a new terminal window.
-
View the list of active topics:
rostopic list
- View information about the
/chatter
topic:
rostopic info /chatter
- View the messages being published on the
/chatter
topic:
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
scripts
directory. 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.py
file 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
rospy
module, which is the Python client library for ROS. Also, import theString
andInt32
message types from thestd_msgs
package, 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_talker
and 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 = 0
This 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 += 1
These 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
scripts
directory. 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.py
file 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 data
This 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 down
This 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.bash
Open 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.py
Congratulations! 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