It is often considered that writing a publisher in Robot Operating Systems (ROS) is far easier than working with the subscriber. On most accounts, this is true, given that publishing is a minimalist task – We only feed values to the robot or robot in simulation. To be quite frank, that is the extent to which publishers in ROS work. In fact, some professionals would even state that it is analogous to a print( ) or display function!
ROS Publishers
Since robots primarily intend to automate what humans would otherwise do, we will draw parallels between simple man-made paraphernalia, and the robot’s construction to build perspective. Therefore, it is crucial that the reader understands what ROS nodes and ROS topics are. Here’s a quick recap of nodes and topics in ROS.
A ROS node is a computational process which runs as a program in a package. Since we can have several nodes running concurrently, nodes are analogous to human organs, wherein each organ (node) performs a dedicated task for the entire human (robot) to function in the desired manner.
If you wish to establish which nodes are presently active in your package, simply execute the following commands.
# only if the master node is not running $ roscore # to list all active nodes $ rosnode list # to obtain info on a specific node $ rosnode info /<node_name>
Another alternative would be to use rqt graphs to display the tree diagram to understand the flow of data between nodes.
A ROS topic is essentially a named bus over which nodes exchange messages. They may be thought of as radio channels on which information is sent and received. Note that every topic has a unique format of data being exchanged.
The idea of Publishing information arises from the question of how to direct a robot to do something since we are controlling it. Note that we will maintain communication with the topics individually: we write publishers for specific topics and not the whole robot as such.
Now that some clarity on the notion of publishers has been provided, we shall look at a simple template for writing publishers in Python.
Template
Python
# Do not skip line 2 #!/usr/bin/env python import rospy # the following line depends upon the # type of message you are trying to publish from std_msgs.msg import String def publisher(): # define the actions the publisher will make pub = rospy.Publisher( '/<topic_name>' , String, queue_size = 10 ) # initialize the publishing node rospy.init_node( '<node_name>' , anonymous = True ) # define how many times per second # will the data be published # let's say 10 times/second or 10Hz rate = rospy.Rate( 10 ) # to keep publishing as long as the core is running while not rospy.is_shutdown(): data = "The data that you wish to publish." # you could simultaneously display the data # on the terminal and to the log file rospy.loginfo(data) # publish the data to the topic using publish() pub.publish(data) # keep a buffer based on the rate defined earlier rate.sleep() if __name__ = = '__main__' : # it is good practice to maintain # a 'try'-'except' clause try : publisher() except rospy.ROSInterruptException: pass |
Explanation
- Code execution begins from the try and except clause. The publisher function is called from here.
- Define the publisher function:
- the first field indicates the name of the topic to which you wish to publish the data. For example, /odom or /rosout.
- the second field indicates the type of data being published. String, Float32 or Twist are a few examples.
- the last field declares the limit of number of messages that may be queued to the topic.
- We follow this with node initialization.
- The rest of the template is self-explanatory (refer to the comments in the code).
Example
This example implements a code for publishing data to the /rosout topic. While there is a much easier way of doing this (using rostopic echo), it serves as an easily comprehensible demonstration.
The goal is to publish data to the /cmd_vel (command velocity) topic and therefore increase the speed of the bot. As a result of the while loop as shown in the template, we will notice an acceleration in the robot.
Python
#!/usr/bin/env python3 import rospy from geometry_msgs.msg import Twist def increase(Twist): print ( 'We are in the callback function!' ) velo_msg = Twist # l = Twist.linear.x # print(l) # define the rate at which # we will be publishing the velocity. rate = rospy.Rate( 5 ) # prompt the user for the acceleration value speed = float ( input ('By how much would \ you like to accelerate? ')) while not rospy.is_shutdown(): # increase the current velocity by the speed defined. velo_msg.linear.x = (Twist.linear.x) + speed # publish the increased velocity pub.publish(velo_msg) print ( 'Publishing was successful!' ) rate.sleep() def main(): print ( "In main..." ) # initializing the publisher node rospy.init_node( 'Velocity_publisher' , anonymous = True ) sub = rospy.Subscriber( '/cmd_vel' , Twist, increase) rospy.spin() if __name__ = = '__main__' : try : pub = rospy.Publisher( '/cmd_vel' , Twist, queue_size = 10 ) main() except rospy.ROSInterruptException: pass |
Code Explanation
- We start by defining the publishing node ‘pub’. Here we define the topic (/cmd_vel) to which it will publish messages of type Twist. Then we move to main().
- Initializing the node is key. Without this, our master node (roscore) will not be able define the flow of information between all nodes.
- In order to increase the velocity of the robot, we need to know its current velocity. Therefore we subscribe to the command velocity topic.
- The callback function in the rospy.Subscriber( ) command is increase().
- Inside the increase( ) function:
- Start by obtaining the velocity onto a variable – here it is velo_msg. Notice how velo_msg is of type Twist.
- Now define the rate at which values will be published.
- The user is now prompted to choose the change in speed.
- In the while loop:
- We obtain the x component of the linear velocity. We may use the y component as well depending on the application.
- The x component is now increased by the amount specified by the user.
- Now the velocity is published and the while loop is rerun.
Deploying ROS
Input (Terminal):
$roscore #open new terminal $source devel/setup.bash #launch turtlebot3 on gazebo $roslaunch turtlebot3_gazebo turtlebot3_empty_world.launch #open new terminal #give turtlebot3 some starting velocity $roslaunch turtlebot3_teleop turtlebot3_teleop_key.launch #now press w #open a new terminal #run your python script from your package $rosrun <pkg_name> publisher.py
Output:
In main... In the callback function. 0.01 By how much would you like to increase the speed? Publishing Successful!