A Simple Publisher Node
The Code¶
Copy all the code below into your publisher.py
file and review the annotations to understand how it all works.
Tip
Don't forget the Shebang! See below for further details...
#!/usr/bin/env python3
# A simple ROS2 Publisher
import rclpy # (1)!
from rclpy.node import Node
from example_interfaces.msg import String # (2)!
class SimplePublisher(Node): # (3)!
def __init__(self):
super().__init__("simple_publisher") # (4)!
self.my_publisher = self.create_publisher(
msg_type=String,
topic="my_topic",
qos_profile=10,
) # (5)!
publish_rate = 1 # Hz
self.timer = self.create_timer(
timer_period_sec=1/publish_rate,
callback=self.timer_callback
) # (6)!
self.get_logger().info(
f"The '{self.get_name()}' node is initialised." # (7)!
)
def timer_callback(self): # (8)!
ros_time = self.get_clock().now().seconds_nanoseconds()
topic_msg = String()
topic_msg.data = f"The ROS time is {ros_time[0]} (seconds)."
self.my_publisher.publish(topic_msg)
self.get_logger().info(f"Publishing: '{topic_msg.data}'")
def main(args=None): # (9)!
rclpy.init(args=args)
my_simple_publisher = SimplePublisher()
rclpy.spin(my_simple_publisher)
my_simple_publisher.destroy_node()
rclpy.shutdown()
if __name__ == '__main__': # (10)!
main()
-
rclpy
is the ROS Client Library for Python.This is a vital import that allows us to create ROS nodes and initialise them on the ROS network.
We also import the
Node
class from therclpy.node
library. This is a ready-made Python Class that contains all the necessary functionality that a Python ROS Node might need, so we'll use this as the basis for our own node (which we'll create shortly). -
We also need to import the
String
message type from theexample_interfaces.msg
library for publishing our messages. -
We create a Python class called
SimplePublisher()
, which we'll use to encapsulate all the functionality of our node.The vast majority of the functionality of this node is inherited from the
rclpy.node
,Node()
Class which we imported above. -
Using the
super()
method we call the__init__()
method from the parent Node class that ourSimplePublisher
class is derived from.We provide a name here, which is the name that will be used to register our node on the ROS network (we can call the node anything that we want, but it's a good idea to call it something meaningful).
-
We then use the
create_publisher()
method (inherited from theNode
class) in order to provide our node with the ability to publish messages to a ROS Topic. When calling this we provide 3 key bits of information:-
msg_type
: The type of message that we want to publish.In our case, a
String
message from theexample_interfaces.msg
module. -
topic
: The name of the topic that we want to publish these messages to.This could be an existing topic (in which case, we'd need to make sure we used the correct message type), or a new topic (in which case, the name can be anything we want it to be).
In our case, we want to create a new topic on the ROS network called
"my_topic"
. -
qos_profile
: A queue size, which is a "Quality of Service" (QoS) setting which limits the amount of messages that are queued in a buffer.In our case, we're setting this to
10
, which is generally appropriate for most of the applications that we'll be working on.
-
-
Here, we're calling the
create_timer()
method, which we'll use to control the rate at which messages are published to our topic. Here we define 2 things:-
timer_period_sec
: The rate at which we want the timer to run. This must be provided as a period, in seconds. In the line above, we have specified a publishing frequency (in Hz):publish_rate = 1 # Hz
So the associated time period (in seconds) is:
\(T = \frac{1}{f}\) -
callback
: This is a function that will be executed every time the timer elapses at the desired rate (1 Hz). We're specifying a function calledtimer_callback
, which we'll define later on in the code...
-
-
Finally, we use the
get_logger().info()
method to send a Log message to the terminal to inform us that the initialisation of our node is complete. -
Here we define the timer callback function. Anything in here will execute at the rate that we specified when we created the
create_timer()
instance before. In our case:- Use the
get_clock()
method to get the current ROS Time. - Instantiate a
String()
message (defined astopic_msg
). - Populate this message with data. In our case, a statement that includes the ROS Time, as obtained above.
- Call the
publish()
method of ourmy_publisher
object, to actually publish this message to the"my_topic"
topic. - Send the message data to the terminal as a log message as well, so that we can see what it is when our Node is actually running.
- Use the
-
With the functionality of our
SimplePublisher
class now established, we define amain()
function for the Node. This will be fairly common to most Python Nodes that we create, with the following 5 key processes:- Initialise the
rclpy
library. - Create an instance of our
SimplePublisher()
node. - "Spin" the node to keep it alive so that any callbacks can execute as required (in our case here, just the
timer_callback()
). - Destroy the node once termination is requested (triggered by entering Ctrl+C in the terminal).
- Shutdown the
rclpy
library.
- Initialise the
-
Finally, we call the
main()
function to set everything going. We do this inside anif
statement, to ensure that our node is the main executable (i.e. it has been executed directly (viaros2 run
), and hasn't been called by another script)
Defining Package Dependencies¶
We're importing a couple of Python libraries into our node here, which means that our package has two dependencies: rclpy
and example_interfaces
:
Its good practice to add these dependencies to your package.xml
file. Locate this file (ros2_ws/src/part1_pubsub/package.xml
), open it up and find the following line:
rclpy
is therefore already defined as an execution dependency (which means that our package needs this library in order to execute our code), but we need to add example_interfaces
as well, so add the following additional line underneath:
Job done. Save the file and close it.
The Shebang¶
The very first line of code looks like a comment, but it is actually a very crucial part of the script:
This is called the Shebang, and it tells the operating system which interpreter to use to execute the code. In our case here, it tells the operating system where to find the right Python interpreter that should be used to actually run the code.