PubSubPy - arieldo/MorBot GitHub Wiki

Writing a Simple Publisher and Subscriber communication system in Python for ROS

Table of Contents

  1. Introduction
  2. Prerequisites
  3. Create a new package
  4. Create a new publisher
  5. Create a new subscriber
  6. Run the publisher and subscriber
  7. View the publisher and subscriber nodes
  8. View the publisher and subscriber topics
  9. Multiple Publishers and Subscribers in ROS

Introduction:

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.

Prerequisites:

  • 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

Step 1: Create a new ROS package

  1. Open a terminal window.
  2. Navigate to the src directory of your ROS workspace, e.g., ~/MorBot/morbot_ros/morbot_ws/src:
    cd ~/MorBot/morbot_ros/morbot_ws/src
  1. 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 

Step 2: Create a new publisher node

  1. Create a new source file inside the scripts directory. For example, create a Python file named talker.py:
    cd scripts
    touch talker.py
  1. Make the script executable:
    cd ~/MorBot/morbot_ros/morbot_ws/src/simple_pub_sub/scripts
    sudo chmod +x talker.py
  1. 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.

  1. Save the file and close the text editor.

Step 3: Create a new subscriber node

  1. Create a new source file inside the scripts directory. For example, create a Python file named listener.py:

    cd ~/MorBot/morbot_ros/morbot_ws/src/simple_pub_sub/scripts
    touch listener.py
  2. Make the script executable:

        cd ~/MorBot/morbot_ros/morbot_ws/src/simple_pub_sub/scripts
        chmod +x listener.py
  3. 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.

  1. Save the file and close the text editor.

Step 4: Build the package

  1. Open a terminal window.
  2. 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

Step 5: Run the publisher and subscriber nodes

Open three new terminal windows. In each, source your ROS workspace

  1. Open a terminal window.

  2. Run the master node:

    roscore
  1. Open a new terminal window.

  2. Run the publisher node:

    rosrun simple_pub_sub talker.py
  1. Open a new terminal window.

  2. Run the subscriber node:

    rosrun simple_pub_sub listener.py

Step 6: View the publisher and subscriber nodes

  1. Open a new terminal window.

  2. View the list of active nodes:

    rosnode list
  1. View information about the talker node:
    rosnode info /talker
  1. View information about the listener node:
    rosnode info /listener

Step 7: View the publisher and subscriber topics

  1. Open a new terminal window.

  2. View the list of active topics:

    rostopic list
  1. View information about the /chatter topic:
    rostopic info /chatter
  1. 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.

Multiple Publishers and Subscribers in ROS

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 multiple_publisher node

  1. Create a new source file inside the scripts directory. For example, create a Python file named multitopic_talker.py:
    cd ~/MorBot/morbot_ros/morbot_ws/src/simple_pub_sub/scripts
    touch multitopic_talker.py
  1. Make the script executable:
    cd ~/MorBot/morbot_ros/morbot_ws/src/simple_pub_sub/scripts
    sudo chmod +x multitopic_talker.py
  1. 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 the String and Int32 message types from the std_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 name multitopic_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 multitopic_subscriber node

  1. Create a new source file inside the scripts directory. For example, create a Python file named multitopic_listener.py:

    cd ~/MorBot/morbot_ros/morbot_ws/src/simple_pub_sub/scripts
    touch multitopic_listener.py
  2. Make the script executable:

        cd ~/MorBot/morbot_ros/morbot_ws/src/simple_pub_sub/scripts
        chmod +x multitopic_listener.py
  3. 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.

Build the package

  1. Open a terminal window.
  2. 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

Run the multitopic publisher and subscriber nodes

Open three new terminal windows. In each, source your ROS workspace

  1. Open a terminal window.

  2. Run the master node:

    roscore
  1. Open a new terminal window.

  2. Run the publisher node:

    rosrun simple_pub_sub multitopic_talker.py
  1. Open a new terminal window.

  2. 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.

Next Robot Motion simulator :

⚠️ **GitHub.com Fallback** ⚠️