Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Writing a Composable Node (C++) Tutorial #4106

Merged
merged 7 commits into from
Feb 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions source/Concepts/Intermediate/About-Composition.rst
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ Additionally, once a component is created, it must be registered with the index
# To register multiple components in the same shared library, use multiple calls
# rclcpp_components_register_nodes(talker_component "composition::Talker2")

For an example, :doc:`check out this tutorial <../../Tutorials/Intermediate/Writing-a-Composable-Node>`

.. note::

In order for the component_container to be able to find desired components, it must be executed or launched from a shell that has sourced the corresponding workspace.
Expand Down
2 changes: 1 addition & 1 deletion source/Releases/Release-Dashing-Diademata.rst
Original file line number Diff line number Diff line change
Expand Up @@ -499,7 +499,7 @@ If not present, registration macros must be added to the project's CMake.
add_library(listener src/listener.cpp)
rclcpp_components_register_nodes(listener "composition::Listener")

For more information on composition, see `the tutorial <https://index.ros.org/doc/ros2/Tutorials/Composition/>`__
For more information on composition, see `the tutorial <../Tutorials/Intermediate/Writing-a-Composable-Node>`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm on the fence about this one. I know the change targets rolling, but I think I'd prefer not to change the Dashing stuff at this point. So I'll suggest just removing this for now.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I only found this because I found it via grep when looking for an existing tutorial. I'm not tied to it, but if someone else stumbles on this page and wants to follow through with this, then I figure the extra link will be helpful.


rclpy
^^^^^
Expand Down
1 change: 1 addition & 0 deletions source/Tutorials/Intermediate.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ Intermediate
Intermediate/Creating-an-Action
Intermediate/Writing-an-Action-Server-Client/Cpp
Intermediate/Writing-an-Action-Server-Client/Py
Intermediate/Writing-a-Composable-Node
Intermediate/Composition
Intermediate/Monitoring-For-Parameter-Changes-CPP
Intermediate/Monitoring-For-Parameter-Changes-Python
Expand Down
2 changes: 2 additions & 0 deletions source/Tutorials/Intermediate/Composition.rst
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ Background

See the :doc:`conceptual article <../../Concepts/Intermediate/About-Composition>`.

For information on how to write a composable node, :doc:`check out this tutorial <Writing-a-Composable-Node>`.

Run the demos
-------------

Expand Down
170 changes: 170 additions & 0 deletions source/Tutorials/Intermediate/Writing-a-Composable-Node.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
Writing a Composable Node (C++)
===============================

.. contents:: Table of Contents
:depth: 2
:local:

Starting Place
--------------

Let's assume that you have a regular ``rclcpp::Node`` executable that you want to run in the same process as other nodes to enable more efficient communication.

We'll start from having a class that directly inherits from ``Node``, and that also has a main method defined.

.. code-block:: c++

namespace palomino
{
class VincentDriver : public rclcpp::Node
{
// ...
};
}

int main(int argc, char * argv[])
{
rclcpp::init(argc, argv);
rclcpp::spin(std::make_shared<palomino::VincentDriver>());
rclcpp::shutdown();
return 0;
}

This will typically be compiled as an executable in your Cmake.

.. code-block:: cmake

# ...
add_executable(vincent_driver src/vincent_driver.cpp)
# ...
install(TARGETS vincent_driver
DESTINATION lib/${PROJECT_NAME}
)

Code Updates
------------

Add the Package Dependency
^^^^^^^^^^^^^^^^^^^^^^^^^^

Your `package.xml <https://github.com/ros2/demos/tree/{REPOS_FILE_BRANCH}/composition/package.xml>`__ should have a dependency on ``rclcpp_components``, a la

.. code-block:: xml

<depend>rclcpp_components</depend>

Alternatively, you can independently add a ``build_depend/exec_depend``.

Class Definition
^^^^^^^^^^^^^^^^

The only change to your class definition that you may have to do is ensure that `the constructor for the class <https://github.com/ros2/demos/tree/{REPOS_FILE_BRANCH}/composition/src/talker_component.cpp>`__ takes a ``NodeOptions`` argument.

.. code-block:: c++

VincentDriver(const rclcpp::NodeOptions & options) : Node("vincent_driver", options)
{
// ...
}

No More Main Method
^^^^^^^^^^^^^^^^^^^

Replace your main method with a ``pluginlib``-style macro invocation.

.. code-block:: c++

#include <rclcpp_components/register_node_macro.hpp>
RCLCPP_COMPONENTS_REGISTER_NODE(palomino::VincentDriver)

.. caution::
If the main method you are replacing contains a ``MultiThreadedExecutor``, be sure to make note and ensure that your container node is multithreaded.
See section below.

CMake Changes
^^^^^^^^^^^^^
First, add ``rclcpp_components`` as a dependency in your CMakeLists.txt with:

.. code-block:: cmake

find_package(rclcpp_components REQUIRED)
DLu marked this conversation as resolved.
Show resolved Hide resolved

Second, we're going to replace our ``add_executable`` with a ``add_library`` with a new target name.

.. code-block:: cmake

add_library(vincent_driver_component src/vincent_driver.cpp)

Third, replace other build commands that used the old target to act on the new target.
i.e. ``ament_target_dependencies(vincent_driver ...)`` becomes ``ament_target_dependencies(vincent_driver_component ...)``

Fourth, add a new command to declare your component.

.. code-block:: cmake

rclcpp_components_register_node(
vincent_driver_component
PLUGIN "palomino::VincentDriver"
EXECUTABLE vincent_driver
)

Fifth and finally, change any installation commands in the CMake that operated on the old target to install the library version instead.
For instance, do not install either target into ``lib/${PROJECT_NAME}``.
Replace with the library installation.

.. code-block:: cmake

ament_export_targets(export_vincent_driver_component)
install(TARGETS vincent_driver_component
EXPORT export_vincent_driver_component
ARCHIVE DESTINATION lib
LIBRARY DESTINATION lib
RUNTIME DESTINATION bin
)


Running Your Node
-----------------

See the :doc:`Composition tutorial <Composition>` for an in-depth look at composing nodes.
The quick and dirty version is that if you had the following in your Python launch file,

.. code-block:: python

from launch_ros.actions import Node

# ..

ld.add_action(Node(
package='palomino',
executable='vincent_driver',
# ..
))

you can replace it with

.. code-block:: python

from launch_ros.actions import ComposableNodeContainer
from launch_ros.descriptions import ComposableNode

# ..
ld.add_action(ComposableNodeContainer(
name='a_buncha_nodes',
namespace='',
package='rclcpp_components',
executable='component_container',
composable_node_descriptions=[
ComposableNode(
package='palomino',
plugin='palomino::VincentDriver',
name='vincent_driver',
# ..
extra_arguments=[{'use_intra_process_comms': True}],
),
]
))

.. caution::

If you need multi-threading, instead of setting your executable to ``component_container``, set it to ``component_container_mt``
Loading