Skip to content

Commit 2341375

Browse files
NickTziarosahcordekscottzYadunund
authored andcommitted
Add tutorial on Node Interfaces Template Class (#4992)
* Created Using-Node-Interfaces-Template-Class.rst and modified Intermediate.rst * fixing lint errors * Update source/Tutorials/Intermediate/Using-Node-Interfaces-Template-Class.rst Co-authored-by: Alejandro Hernández Cordero <ahcorde@gmail.com> Signed-off-by: Nikos Tziaros <33639811+NickTziaros@users.noreply.github.com> * Update source/Tutorials/Intermediate/Using-Node-Interfaces-Template-Class.rst Co-authored-by: Alejandro Hernández Cordero <ahcorde@gmail.com> Signed-off-by: Nikos Tziaros <33639811+NickTziaros@users.noreply.github.com> * Update source/Tutorials/Intermediate/Using-Node-Interfaces-Template-Class.rst Co-authored-by: Alejandro Hernández Cordero <ahcorde@gmail.com> Signed-off-by: Nikos Tziaros <33639811+NickTziaros@users.noreply.github.com> * Update source/Tutorials/Intermediate/Using-Node-Interfaces-Template-Class.rst Co-authored-by: Alejandro Hernández Cordero <ahcorde@gmail.com> Signed-off-by: Nikos Tziaros <33639811+NickTziaros@users.noreply.github.com> * Update source/Tutorials/Intermediate/Using-Node-Interfaces-Template-Class.rst Co-authored-by: Alejandro Hernández Cordero <ahcorde@gmail.com> Signed-off-by: Nikos Tziaros <33639811+NickTziaros@users.noreply.github.com> * Update source/Tutorials/Intermediate/Using-Node-Interfaces-Template-Class.rst Co-authored-by: Alejandro Hernández Cordero <ahcorde@gmail.com> Signed-off-by: Nikos Tziaros <33639811+NickTziaros@users.noreply.github.com> * Update source/Tutorials/Intermediate/Using-Node-Interfaces-Template-Class.rst Co-authored-by: Katherine Scott <katherineAScott@gmail.com> Signed-off-by: Nikos Tziaros <33639811+NickTziaros@users.noreply.github.com> * Update source/Tutorials/Intermediate/Using-Node-Interfaces-Template-Class.rst Co-authored-by: Katherine Scott <katherineAScott@gmail.com> Signed-off-by: Nikos Tziaros <33639811+NickTziaros@users.noreply.github.com> * Update source/Tutorials/Intermediate/Using-Node-Interfaces-Template-Class.rst Co-authored-by: yadunund <yadunund@gmail.com> Signed-off-by: Nikos Tziaros <33639811+NickTziaros@users.noreply.github.com> * Implemented Suggested Changes --------- Signed-off-by: Nikos Tziaros <33639811+NickTziaros@users.noreply.github.com> Co-authored-by: Alejandro Hernández Cordero <ahcorde@gmail.com> Co-authored-by: Katherine Scott <katherineAScott@gmail.com> Co-authored-by: yadunund <yadunund@gmail.com> (cherry picked from commit 3ac77be)
1 parent 1acf08e commit 2341375

File tree

2 files changed

+243
-0
lines changed

2 files changed

+243
-0
lines changed

source/Tutorials/Intermediate.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ Intermediate
1010
Intermediate/Writing-an-Action-Server-Client/Py
1111
Intermediate/Writing-a-Composable-Node
1212
Intermediate/Composition
13+
Intermediate/Using-Node-Interfaces-Template-Class
1314
Intermediate/Monitoring-For-Parameter-Changes-CPP
1415
Intermediate/Monitoring-For-Parameter-Changes-Python
1516
Intermediate/Launch/Launch-Main
Lines changed: 242 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,242 @@
1+
Using the Node Interfaces Template Class (C++)
2+
==============================================
3+
4+
**Goal:** Learn how to access ``Node`` information using ``rclcpp::NodeInterfaces<>``
5+
6+
**Tutorial level:** Intermediate
7+
8+
**Time:** 10 minutes
9+
10+
.. contents:: Table of Contents
11+
:depth: 2
12+
:local:
13+
14+
15+
Overview
16+
--------
17+
18+
Not all ROS Nodes are created equally!
19+
the ``rclcpp::Node`` and ``rclcpp_lifecycle::LifecycleNode`` classes do not share an inheritance tree, which means ROS developers can run into compile time type issues when they want to write a function that takes in a ROS node pointer as an argument.
20+
To address this issue, ``rclcpp`` includes the ``rclcpp::NodeInterfaces<>`` template type that should be used as the preferred convention for passing for both conventional and lifecycle nodes to functions.
21+
This `ROSCon 2023 lightning talk <https://vimeo.com/879001243#t=16m0s>`_ summarizes the issue and remedy succinctly.
22+
The following tutorial will show you how to use ``rclcpp::NodeInterfaces<>`` as reliable and compact interface for all ROS node types.
23+
24+
The ``rclcpp::NodeInterfaces<>`` template class provides a compact and efficient way to manage Node Interfaces in ROS 2. This is particularly useful when working with different types of ``Nodes``, such as ``rclcpp::Node`` and ``rclcpp_lifecycle::LifecycleNode``, which do not share the same inheritance tree.
25+
26+
1 Accessing Node Information with a ``SharedPtr``
27+
-------------------------------------------------
28+
29+
In the example below, we create a simple ``Node`` called ``Simple_Node`` and define a function ``node_info`` that accepts a ``SharedPtr`` to the ``Node``. The function retrieves and prints the name of the ``Node``.
30+
31+
.. code-block:: c++
32+
33+
#include <memory>
34+
#include "rclcpp/rclcpp.hpp"
35+
36+
void node_info(rclcpp::Node::SharedPtr node)
37+
{
38+
RCLCPP_INFO(node->get_logger(), "Node name: %s", node->get_name());
39+
}
40+
41+
class SimpleNode : public rclcpp::Node
42+
{
43+
public:
44+
SimpleNode(const std::string & node_name)
45+
: Node(node_name)
46+
{
47+
}
48+
};
49+
50+
int main(int argc, char * argv[])
51+
{
52+
rclcpp::init(argc, argv);
53+
auto node = std::make_shared<SimpleNode>("Simple_Node");
54+
node_info(*node);
55+
}
56+
57+
Output:
58+
59+
.. code-block:: console
60+
61+
[INFO] [Simple_Node]: Node name: Simple_Node
62+
63+
While this approach works well for arguments of type ``rclcpp::Node``, it does not work for other node types like ``rclcpp_lifecycle::LifecycleNode``.
64+
65+
2 Explicitly pass ``rclcpp::node_interfaces``
66+
---------------------------------------------
67+
68+
A more robust approach, applicable to all node types, is to explicitly pass ``rclcpp::node_interfaces`` as function arguments, as demonstrated in the example below.
69+
In the example that follows, we create function called ``node_info`` that take as arguments two ``rclcpp::node_interfaces``, ``NodeBaseInterface`` and ``NodeLoggingInterface`` and prints the ``Node`` name.
70+
We then create two nodes of type ``rclcpp_lifecycle::LifecycleNode`` and ``rclcpp::Node`` and pass their interfaces in ``node_info``.
71+
72+
.. code-block:: c++
73+
74+
void node_info(std::shared_ptr<rclcpp::node_interfaces::NodeBaseInterface> base_interface,
75+
std::shared_ptr<rclcpp::node_interfaces::NodeLoggingInterface> logging_interface)
76+
{
77+
RCLCPP_INFO(logging_interface->get_logger(), "Node name: %s", base_interface->get_name());
78+
}
79+
80+
class SimpleNode : public rclcpp::Node
81+
{
82+
public:
83+
SimpleNode(const std::string & node_name)
84+
: Node(node_name)
85+
{
86+
}
87+
};
88+
89+
class LifecycleTalker : public rclcpp_lifecycle::LifecycleNode
90+
{
91+
public:
92+
explicit LifecycleTalker(const std::string & node_name, bool intra_process_comms = false)
93+
: rclcpp_lifecycle::LifecycleNode(node_name,
94+
rclcpp::NodeOptions().use_intra_process_comms(intra_process_comms))
95+
{}
96+
}
97+
98+
int main(int argc, char * argv[])
99+
{
100+
rclcpp::init(argc, argv);
101+
rclcpp::executors::SingleThreadedExecutor exe;
102+
auto node = std::make_shared<SimpleNode>("Simple_Node");
103+
auto lc_node = std::make_shared<LifecycleTalker>("Simple_LifeCycle_Node");
104+
node_info(node->get_node_base_interface(),node->get_node_logging_interface());
105+
node_info(lc_node->get_node_base_interface(),lc_node->get_node_logging_interface());
106+
}
107+
108+
.. code-block:: console
109+
110+
[INFO] [Simple_Node]: Node name: Simple_Node
111+
[INFO] [Simple_LifeCycle_Node]: Node name: Simple_LifeCycle_Node
112+
113+
As functions grow in complexity, the number of ``rclcpp::node_interfaces`` arguments also increases, leading to readability and compactness issues.
114+
To make the code more flexible and compatible with different node types, we use ``rclcpp::NodeInterfaces<>``.
115+
116+
3 Using ``rclcpp::NodeInterfaces<>``
117+
------------------------------------
118+
119+
The recommended way of accessing a ``Node`` type's information is through the ``Node Interfaces``.
120+
121+
Below, similar to the previous example, a ``rclcpp_lifecycle::LifecycleNode`` and a ``rclcpp::Node`` are created.
122+
123+
.. code-block:: c++
124+
125+
#include <memory>
126+
#include <string>
127+
#include <thread>
128+
#include "lifecycle_msgs/msg/transition.hpp"
129+
#include "rclcpp/rclcpp.hpp"
130+
#include "rclcpp_lifecycle/lifecycle_node.hpp"
131+
#include "rclcpp_lifecycle/lifecycle_publisher.hpp"
132+
#include "rclcpp/node_interfaces/node_interfaces.hpp"
133+
134+
using MyNodeInterfaces =
135+
rclcpp::node_interfaces::NodeInterfaces<rclcpp::node_interfaces::NodeBaseInterface, rclcpp::node_interfaces::NodeLoggingInterface>;
136+
137+
void node_info(MyNodeInterfaces interfaces)
138+
{
139+
auto base_interface = interfaces.get_node_base_interface();
140+
auto logging_interface = interfaces.get_node_logging_interface();
141+
RCLCPP_INFO(logging_interface->get_logger(), "Node name: %s", base_interface->get_name());
142+
}
143+
144+
class SimpleNode : public rclcpp::Node
145+
{
146+
public:
147+
SimpleNode(const std::string & node_name)
148+
: Node(node_name)
149+
{
150+
}
151+
};
152+
153+
class LifecycleTalker : public rclcpp_lifecycle::LifecycleNode
154+
{
155+
public:
156+
explicit LifecycleTalker(const std::string & node_name, bool intra_process_comms = false)
157+
: rclcpp_lifecycle::LifecycleNode(node_name,
158+
rclcpp::NodeOptions().use_intra_process_comms(intra_process_comms))
159+
{}
160+
}
161+
162+
int main(int argc, char * argv[])
163+
{
164+
rclcpp::init(argc, argv);
165+
rclcpp::executors::SingleThreadedExecutor exe;
166+
auto node = std::make_shared<SimpleNode>("Simple_Node");
167+
auto lc_node = std::make_shared<LifecycleTalker>("Simple_LifeCycle_Node");
168+
node_info(*node);
169+
node_info(*lc_node);
170+
}
171+
172+
Output:
173+
174+
.. code-block:: console
175+
176+
[INFO] [Simple_Node]: Node name: Simple_Node
177+
[INFO] [Simple_LifeCycle_Node]: Node name: Simple_LifeCycle_Node
178+
179+
3.1 Examine the code
180+
~~~~~~~~~~~~~~~~~~~~
181+
182+
.. code-block:: c++
183+
184+
using MyNodeInterfaces =
185+
rclcpp::node_interfaces::NodeInterfaces<rclcpp::node_interfaces::NodeBaseInterface, rclcpp::node_interfaces::NodeLoggingInterface>;
186+
187+
void node_info(MyNodeInterfaces interfaces)
188+
{
189+
auto base_interface = interfaces.get_node_base_interface();
190+
auto logging_interface = interfaces.get_node_logging_interface();
191+
RCLCPP_INFO(logging_interface->get_logger(), "Node name: %s", base_interface->get_name());
192+
}
193+
194+
Instead of accepting ``SharedPtr`` or a node interface, this function takes a reference to a ``rclcpp::node_interfaces::NodeInterfaces`` object.
195+
Another advantage of using this approach is the support for implicit conversion of node-like objects.
196+
This means that it is possible to directly pass any node-like object to a function expecting a ``rclcpp::node_interfaces::NodeInterfaces`` object.
197+
198+
It extracts:
199+
200+
* ``NodeBaseInterface`` Provides basic node functionalities.
201+
* ``NodeLoggingInterface`` Enables logging.
202+
203+
Then, it retrieves and prints the node name.
204+
205+
.. code-block:: c++
206+
207+
class SimpleNode : public rclcpp::Node
208+
{
209+
public:
210+
SimpleNode(const std::string & node_name)
211+
: Node(node_name)
212+
{
213+
}
214+
};
215+
216+
class LifecycleTalker : public rclcpp_lifecycle::LifecycleNode
217+
{
218+
public:
219+
explicit LifecycleTalker(const std::string & node_name, bool intra_process_comms = false)
220+
: rclcpp_lifecycle::LifecycleNode(node_name,
221+
rclcpp::NodeOptions().use_intra_process_comms(intra_process_comms))
222+
{}
223+
}
224+
225+
Next, we create a ``rclcpp::Node`` as well as a ``rclcpp_lifecycle::LifecycleNode`` class. The ``rclcpp_lifecycle::LifecycleNode`` class often includes functions for the state transitions ``Unconfigured``, ``Inactive``, ``Active``, and ``Finalized``. However, they are not included for demonstration purposes.
226+
227+
.. code-block:: c++
228+
229+
int main(int argc, char * argv[])
230+
{
231+
rclcpp::init(argc, argv);
232+
rclcpp::executors::SingleThreadedExecutor exe;
233+
auto node = std::make_shared<SimpleNode>("Simple_Node");
234+
auto lc_node = std::make_shared<LifecycleTalker>("Simple_LifeCycle_Node");
235+
node_info(*node);
236+
node_info(*lc_node);
237+
}
238+
239+
In the main function, a ``SharedPtr`` to both ``rclcpp_lifecycle::LifecycleNode`` and ``rclcpp::Node`` is created.
240+
The function declared above is called once with each node type as an argument.
241+
242+
.. note:: The ``SharedPtr`` needs to be dereferenced as the template accepts a reference to the ``NodeT`` object.

0 commit comments

Comments
 (0)