In this laboratory lesson, we will implement our first Ros two node in C plus plus that will use the
publisher subscriber communication protocol to publish a message within our Ros two topic.
Let's start by opening Visual Studio code.
And here let's open the robots workspace.
So let's open a folder.
And let's open the folder that we called Arduino Bot Workspace.
Inside the workspace in the source folder, we already find the two packages we created in the previous
lesson and so the Arduino Pi examples and the Arduino, but CP examples.
So let's open the CP examples package where the Ros two package create command is already automatically
created various folder and files.
And this is the typical structure.
So this is the typical convention that is used for each C plus plus package in Ros two.
The include sub folder.
So this one contains another sub folder that has the same name of the package.
And so Arduino CP examples.
And this will contain the definitions of function and classes in C plus plus instead, the source folder
contains the C plus plus files that define the behavior of function and classes, which is where we
will insert the logic of each Ros two node.
So within this source folder, let's create a new file and let's call this one simple publisher dot
CP here.
Let's start by including the CP library.
So let's include the CP, and from this one, let's take the CP CP.
And this library will allow us to use all the functionalities of Ros two within our script so we can
use it to create a new Ros two node by creating a new class called, for example, Simple Publisher
that inherits from the node class that is defined in the CPP package.
And so let's inherit from the node class.
First among the public methods of the simple publisher class.
Among the public one.
Let's start by defining the constructor of the class.
That is the function that has the same name of the class.
So simple publisher.
And that is automatically executed when we create a new object of this class.
And so when initializing a new object of the simple publisher class, the first step that we want to
do is to initialize the component of the base class.
So of the node class.
By executing the constructor of the node class, that is the base class.
And let's assign it a name to our node that we call simple publisher.
Then among the private attributes.
So here, let's add some support variables here.
Let's open and close the parentheses.
And here among the private variables, let's add some support variables.
So let's start by adding an unsigned int called counter.
And this will count the number of messages that are published within the Ros2 topic.
So let's initialize this 1 to 0 when we execute the constructor.
So let's set the counter variable to zero.
Now that we have initialized the Ros two Node class, let's create a new publisher object using the
Create Publisher function.
So here in the constructor let's use the Create publisher function that is defined in the node class
from which we are inheriting.
And let's save the content so the output of this function in a new private variable.
So here let's declare a new private variable as an object of the CPP publisher class.
And we need to indicate here the message type.
So the type of interface to be used for the message exchange.
In practice, we need to specify the type of message the publisher object will send through the topic.
For example, let's assume that we want to send text messages, so we want to send strings and the string
message type is defined in another rescue library.
So let's include from the standard messages library from the messages that are defined in this library.
Let's include the string dot Http.
And let's use it.
So here when we are declaring the publisher object among the angular parentheses of this template class,
let's use the message type standard messages.
And from the messages of this library, let's take the string message.
And let's take a shared pointer to this class and let's call this shared pointer Pope.
Now we can finally store the output of the create publisher function within the Pope.
Shared pointer.
So within the Pope variable and also inside the angle brackets of the Create publisher class, we still
need to indicate the type of message that we want to publish in the topic.
So still from the standard messages library, from the messages of this library, let's use the string.
And then instead in the parentheses we need to set the name of the topic in which we want the messages
to be published.
So let's call this one for example chapter.
And furthermore, we also need to indicate the size of the queue, namely the size of the message queue
that works as a buffer.
If the subscriber to this topic is not receiving and processing messages fast enough, let's set this
one, for example, to ten.
At this point of the node initialization.
Let's create a new timer object using the create wall timer function.
So create wall timer.
And this is still a function that is available from the node class from which we are inheriting.
And let's call this function and again, among the private variables of the simple publisher class.
So here let's define a new shared pointer to a class of type RCL, CP timer base, and let's take a
shared pointer to this class and let's call this one timer.
And now we can use this object.
So this variable timer we just created to store the output of the create world timer function.
So timer equals to create world timer.
This function allows us to define a Ros two timer whose task is to repeatedly execute a specific function
at a certain frequency.
So the create timer function takes as input.
So here between the parentheses, the frequency at which we want to execute the function.
For example, let's assume that we want to execute it with a frequency of one second.
So first, let's start by including here, the Chrono Library.
And this is a plain cplusplus library and let's use so using namespace.
Chrono
literals in order to use the definition of the one second.
So in order to create a world timer that will expire each second.
Then in order to create this timer function, we also need to pass as input the name of the function
that we want to execute at regular time intervals so that we want to execute every time the timer expires.
To do so, let's use the bind function from the standard Cplusplus library and we want to execute the
timer callback function.
So timer callback, which we are going to define inside the same class.
So inside the simple publisher class.
So.
Let's use the simple publisher in order to declare that the timer callback will be defined in the simple
publisher class.
And then let's also use the this pointer to indicate that we want to use the version of this class that
is defined in this current object.
As last step for the constructor.
So here let's also print an informative message in the terminal using the CP info function which is
defined still in the CP library that we have already imported.
So let's use the RCL CP info.
And this function takes as input the logger scope which we can obtain with the get logger function which
we have already inherits from the node class.
And it also takes the message.
So the text that we want to print in the terminal.
And so for example, let's print the message publishing at one hertz and this will be the one that we
are going to view to visualize in the terminal.
With this, the constructor of the Simple Publisher class is complete and now we have to define the
timer callback function.
In fact, here.
Visual Studio code is telling us that the timer callback function is not yet defined.
So let's define this function here.
Still, within the simple publisher class, and the goal of this function is to publish a new message
within the chapter topic every time this function is executed.
So first we create the message to be published in the topic, which is a string message.
Let's call this variable message and let's create an instance of the string message that is defined
in the Standard Messages library.
And let's change the content of this message to Hello Ross to followed by the counter and then followed
by the variable counter.
So let's convert first.
To string the counter and then also let's increase its value for the next execution of this function.
Then we can use the publish function that is available in the publisher object.
So let's take the put object.
And on this let's execute the publish function in order to publish the message that we have just created.
With this, the simple publisher class is complete and now we can move on.
So at the end of the class definition to define the main function.
So the one that is automatically executed when the script starts.
So let's define the main function.
And let's return zero if everything goes well.
And here as a first step, we need to initialize the Ros two with the function init from the CPP library
and also to this one we pass the argument of the main.
Then we can also create a pointer to an object of the simple publisher class.
So let's call this one node and let's use the function make shared to create a shared pointer of the
simple publisher class.
And then let's keep this node up and running, thus keeping the timer and the publisher active so that
they can continuously send messages within the chapter topic using the Rql CPP spin function.
And so here, let's pass the node.
Finally, if we terminate the execution of the node with control C, we need to ensure that the simple
publisher node is correctly destroyed by using the shut down function.
Now this is all and we can proceed to execute this node.
But first we need to instruct the compiler on how it should build our script and make it an executable
in Ros two.
To do this, we need to modify the file cmakelists.txt, which was automatically created within the
Arduino CP examples package.
First, to facilitate reading of this file, let's delete all the commented rows that were automatically
added.
So let's remove all of these.
And you can think of this file as a sort of instruction sheet that tells the compiler how it should
translate our cplusplus scripts into executable files first before building our script and making them
executable.
We need to declare the support packages that we use so the dependencies that we use to develop the functionalities
of our node.
And if you remember correctly, we used the CPP library, so let's use a new instruction, find package.
And so let's declare the usage of the CPP package.
And this is required.
And also we used the definition of the standard messages library.
So let's copy this instruction and let's change the name of the package to standard messages.
And now we can proceed actually to instruct the compiler on how it should build and install our script
with an Add.
Executable instruction.
So let's call our executable simple publisher and its source code is located in the source folder.
And in this simple publisher dot CPP script, Then let's add the dependencies.
Let's declare the dependencies to this executable with the almond target.
Dependencies.
So to the simple publisher executable.
Let's add the dependencies from the CPP library.
The standard messages.
And finally, we can install this script with the instruction install.
And so the target, the executable that we want to install is the one that is called Simple Publisher.
And also we need to specify where we want to install it.
So the destination will be the lib folder and then the subfolder.
We want to install this one in the folder that has the same name of the package.
So the Arduino CPP examples package.
And this is contained in a useful variable of the CMake that is called Project name.
So basically this variable will contain the name of the package that is Arduino, but CPP examples.
Finally before building the workspace so we can save this file, we need also to declare the dependencies
that we use so our xcp standard messages also in the package dot XML file.
So this other file here that was also automatically created when we use the Ros2 package, create command.
So here we need to add two dependencies.
So with the tag depend, let's declare the dependencies from the CPP library and also from the standard
messages library.
Let's save this file and these are the only two libraries that we need in the simple publisher script
so we can finally execute our node.
And let's open a new Linux terminal.
Let's go into the workspace and let's use the command called build.
This will build the entire workspace again.
Here we can see that there is an error since in the CMake list, this should be targets.
Okay, let's save again.
And let's build again our workspace.
And this command will build the entire workspace again, including also the new Cplusplus node.
Now that the build is successfully complete, we can split the terminal.
So let's open a new window and here let's source the workspace.
So the setup dot bash.
And this way the Arduino CP example package is recognized in the environment and we can proceed to start
the node with the command Ros run followed by the name of the package that is Arduino boot CP examples
so we can press tab to use the Ros2 Autocompletion of the packages.
And also if we press tab twice, we can see that Ros is automatically able to detect that in the Arduino
board.
CP Examples exists the simple publisher node.
So let's run this simple publisher node, let's press enter and we can see that it appears the message
publishing at one hertz in the terminal.
So to verify that indeed the node is running and is publishing messages within the chatter topic, let's
open another window of the terminal and let's take a look to the list of all the topics that are currently
available in Ros two with the Command Ros two topic list.
And as the name of this command suggests, it will show you all the topics that are currently available
in Ros two.
And among these we can see that the chatter topic, which is exactly the name you gave to our topic
in the publisher node appears.
For example, if we stop the execution of the publisher node and we launch again the command Ros2 topic
list, we can see that the chapter topic is no longer available.
Since the node that was publishing it is not longer running.
And by relaunching the node and relaunching again, the command Ros2 topic list, the chapter topic
is back there.
We can also see which messages are now traveling in the topic using the command Ros2 topic Echo, followed
by the name of the topic, which is chatter.
So let's press enter and all the messages that are now published within this channel are printed in
the console.
And so we can see that each message contains the string.
Hello Ros2 followed by a counter.
So a number that increases at each new messages that we are publishing.
So for example, if we stop the execution of the publisher, we can see that in the terminal there are
no longer printed any new messages, since no one is publishing on this topic.
And when we launch it again, the count restarts.
There are two other Ros, two useful commands that provide additional information about a topic, and
these are Ros two topic info followed by the name of the topic from which we want to get more information.
So let's take the chatter topic and here we can see that the message type that is published in this
topic is of type string as we have defined.
And also there is only one node that is publishing in this topic.
Also, if we use the flag variables, we also get more information about this topic.
For example, we can see also the name of the node that is actually interacting with this topic.
That is the simple publisher node.
Finally, one last rose to comment that we are going to use in this lesson is the ros2 topic.
Earth's still followed by the name of the topic that is chatter.
So let's press enter.
And this command analyzes the messages that are published in the topic and calculates the frequency
of the publishing.
In this case, as you can see that it calculates a frequency of one earth.
So the rate, the average rate is one hertz, which is exactly the value that we have set for the timer
in the simple publisher.
So here we set a delay of one second in the timer.
So basically we are publishing one message per second.