From 64b2eaa5e55f053554a72ff69e75a5e8e4646d11 Mon Sep 17 00:00:00 2001 From: "David V. Lu" Date: Mon, 15 Jan 2024 13:28:23 -0500 Subject: [PATCH 1/7] Writing a Composable Node (C++) Tutorial --- .../Intermediate/About-Composition.rst | 2 + source/Releases/Release-Dashing-Diademata.rst | 2 +- source/Tutorials/Intermediate.rst | 1 + source/Tutorials/Intermediate/Composition.rst | 2 + .../Writing-a-Composable-Node.rst | 158 ++++++++++++++++++ 5 files changed, 164 insertions(+), 1 deletion(-) create mode 100644 source/Tutorials/Intermediate/Writing-a-Composable-Node.rst diff --git a/source/Concepts/Intermediate/About-Composition.rst b/source/Concepts/Intermediate/About-Composition.rst index 4a6a9399ee3..5ae3134efef 100644 --- a/source/Concepts/Intermediate/About-Composition.rst +++ b/source/Concepts/Intermediate/About-Composition.rst @@ -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 more details, `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. diff --git a/source/Releases/Release-Dashing-Diademata.rst b/source/Releases/Release-Dashing-Diademata.rst index 1b715590163..9a26bb7699e 100644 --- a/source/Releases/Release-Dashing-Diademata.rst +++ b/source/Releases/Release-Dashing-Diademata.rst @@ -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 `__ +For more information on composition, see `the tutorial <../Tutorials/Intermediate/Writing-a-Composable-Node>` rclpy ^^^^^ diff --git a/source/Tutorials/Intermediate.rst b/source/Tutorials/Intermediate.rst index aba392c653e..ce416511732 100644 --- a/source/Tutorials/Intermediate.rst +++ b/source/Tutorials/Intermediate.rst @@ -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 diff --git a/source/Tutorials/Intermediate/Composition.rst b/source/Tutorials/Intermediate/Composition.rst index b36933da647..62512076ff1 100644 --- a/source/Tutorials/Intermediate/Composition.rst +++ b/source/Tutorials/Intermediate/Composition.rst @@ -21,6 +21,8 @@ Background See the :doc:`conceptual article <../../Concepts/Intermediate/About-Composition>`. +For information on how to write a composable node, `check out this tutorial ` + Run the demos ------------- diff --git a/source/Tutorials/Intermediate/Writing-a-Composable-Node.rst b/source/Tutorials/Intermediate/Writing-a-Composable-Node.rst new file mode 100644 index 00000000000..8687ce845b2 --- /dev/null +++ b/source/Tutorials/Intermediate/Writing-a-Composable-Node.rst @@ -0,0 +1,158 @@ +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 you also have 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()); + 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 +------------ + +Class Definition +^^^^^^^^^^^^^^^^ + +The only change to your class definition that you may have to do is ensure that the constructor for the class 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(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 with + +.. code-block:: cmake + + find_package(rclcpp_components REQUIRED) + +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 `Composition tutorial ` 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`` \ No newline at end of file From 684bc5afc5d3106588aa42894ea26d54005c7386 Mon Sep 17 00:00:00 2001 From: "David V. Lu!!" Date: Mon, 15 Jan 2024 18:21:22 -0500 Subject: [PATCH 2/7] Update Writing-a-Composable-Node.rst Whitespaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaace --- .../Writing-a-Composable-Node.rst | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/source/Tutorials/Intermediate/Writing-a-Composable-Node.rst b/source/Tutorials/Intermediate/Writing-a-Composable-Node.rst index 8687ce845b2..e305f3ce484 100644 --- a/source/Tutorials/Intermediate/Writing-a-Composable-Node.rst +++ b/source/Tutorials/Intermediate/Writing-a-Composable-Node.rst @@ -13,7 +13,7 @@ Let's assume that you have a regular ``rclcpp::Node`` executable that you want t We'll start from having a class that directly inherits from ``Node``, and you also have a main method defined. .. code-block:: c++ - + namespace palomino { class VincentDriver : public rclcpp::Node @@ -30,7 +30,7 @@ We'll start from having a class that directly inherits from ``Node``, and you al return 0; } -This will typically be compiled as an executable in your Cmake. +This will typically be compiled as an executable in your Cmake. .. code-block:: cmake @@ -47,7 +47,7 @@ Code Updates Class Definition ^^^^^^^^^^^^^^^^ -The only change to your class definition that you may have to do is ensure that the constructor for the class takes a ``NodeOptions`` argument. +The only change to your class definition that you may have to do is ensure that the constructor for the class takes a ``NodeOptions`` argument. .. code-block:: c++ @@ -59,7 +59,7 @@ The only change to your class definition that you may have to do is ensure that No More Main Method ^^^^^^^^^^^^^^^^^^^ -Replace your main method with a ``pluginlib``-style macro invocation. +Replace your main method with a ``pluginlib``-style macro invocation. .. code-block:: c++ @@ -67,8 +67,8 @@ Replace your main method with a ``pluginlib``-style macro invocation. 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. + 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 ^^^^^^^^^^^^^ @@ -84,10 +84,10 @@ Second, we're going to replace our ``add_executable`` with a ``add_library`` wit 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. +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. +Fourth, add a new command to declare your component. .. code-block:: cmake @@ -97,12 +97,12 @@ Fourth, add a new command to declare your component. 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. +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 @@ -130,7 +130,7 @@ The quick and dirty version is that if you had the following in your Python laun # .. )) -you can replace it with +you can replace it with .. code-block:: python @@ -155,4 +155,4 @@ you can replace it with )) .. caution:: - If you need multi-threading, instead of setting your executable to ``component_container``, set it to ``component_container_mt`` \ No newline at end of file + If you need multi-threading, instead of setting your executable to ``component_container``, set it to ``component_container_mt`` From 5d50ea7cd74b3ffa0cfe242d56f750c0c63c8d7b Mon Sep 17 00:00:00 2001 From: "David V. Lu!!" Date: Wed, 31 Jan 2024 15:08:42 -0500 Subject: [PATCH 3/7] Apply suggestions from code review Co-authored-by: Chris Lalancette Signed-off-by: David V. Lu!! --- source/Concepts/Intermediate/About-Composition.rst | 2 +- source/Tutorials/Intermediate/Composition.rst | 2 +- .../Tutorials/Intermediate/Writing-a-Composable-Node.rst | 9 +++++---- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/source/Concepts/Intermediate/About-Composition.rst b/source/Concepts/Intermediate/About-Composition.rst index 5ae3134efef..5af9c377f7f 100644 --- a/source/Concepts/Intermediate/About-Composition.rst +++ b/source/Concepts/Intermediate/About-Composition.rst @@ -54,7 +54,7 @@ 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 more details, `check out this tutorial <../Tutorials/Intermediate/Writing-a-Composable-Node>` +For an example, :doc:`check out this tutorial <../Tutorials/Intermediate/Writing-a-Composable-Node>` .. note:: diff --git a/source/Tutorials/Intermediate/Composition.rst b/source/Tutorials/Intermediate/Composition.rst index 62512076ff1..45b4120b74f 100644 --- a/source/Tutorials/Intermediate/Composition.rst +++ b/source/Tutorials/Intermediate/Composition.rst @@ -21,7 +21,7 @@ Background See the :doc:`conceptual article <../../Concepts/Intermediate/About-Composition>`. -For information on how to write a composable node, `check out this tutorial ` +For information on how to write a composable node, :doc:`check out this tutorial `. Run the demos ------------- diff --git a/source/Tutorials/Intermediate/Writing-a-Composable-Node.rst b/source/Tutorials/Intermediate/Writing-a-Composable-Node.rst index e305f3ce484..9ad49e3febf 100644 --- a/source/Tutorials/Intermediate/Writing-a-Composable-Node.rst +++ b/source/Tutorials/Intermediate/Writing-a-Composable-Node.rst @@ -10,7 +10,7 @@ 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 you also have a main method defined. +We'll start from having a class that directly inherits from ``Node``, and that also has a main method defined. .. code-block:: c++ @@ -22,7 +22,7 @@ We'll start from having a class that directly inherits from ``Node``, and you al }; } - int main(int argc, char* argv[]) + int main(int argc, char * argv[]) { rclcpp::init(argc, argv); rclcpp::spin(std::make_shared()); @@ -51,7 +51,7 @@ The only change to your class definition that you may have to do is ensure that .. code-block:: c++ - VincentDriver(const rclcpp::NodeOptions& options) : Node("vincent_driver", options) + VincentDriver(const rclcpp::NodeOptions & options) : Node("vincent_driver", options) { // ... } @@ -115,7 +115,7 @@ Replace with the library installation. Running Your Node ----------------- -See the `Composition tutorial ` for an in-depth look at composing nodes. +See the :doc:`Composition tutorial ` 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 @@ -155,4 +155,5 @@ you can replace it with )) .. caution:: + If you need multi-threading, instead of setting your executable to ``component_container``, set it to ``component_container_mt`` From 55b1dccb7f47fd96dd40df3c2b394854e2713d60 Mon Sep 17 00:00:00 2001 From: "David V. Lu" Date: Wed, 31 Jan 2024 15:14:14 -0500 Subject: [PATCH 4/7] Add Package.xml note --- .../Tutorials/Intermediate/Writing-a-Composable-Node.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/source/Tutorials/Intermediate/Writing-a-Composable-Node.rst b/source/Tutorials/Intermediate/Writing-a-Composable-Node.rst index 9ad49e3febf..c5dda066cef 100644 --- a/source/Tutorials/Intermediate/Writing-a-Composable-Node.rst +++ b/source/Tutorials/Intermediate/Writing-a-Composable-Node.rst @@ -44,6 +44,15 @@ This will typically be compiled as an executable in your Cmake. Code Updates ------------ +Add the Package Dependency +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Your ``package.xml`` should have a dependency on ``rclcpp_components``, a la + +.. code-block:: xml + + rclcpp_components + Class Definition ^^^^^^^^^^^^^^^^ From ec37066b72f87e9f2d3f150e4ea680d3c548aca9 Mon Sep 17 00:00:00 2001 From: "David V. Lu" Date: Wed, 31 Jan 2024 15:17:06 -0500 Subject: [PATCH 5/7] white space --- source/Tutorials/Intermediate/Writing-a-Composable-Node.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/Tutorials/Intermediate/Writing-a-Composable-Node.rst b/source/Tutorials/Intermediate/Writing-a-Composable-Node.rst index c5dda066cef..db056ab7775 100644 --- a/source/Tutorials/Intermediate/Writing-a-Composable-Node.rst +++ b/source/Tutorials/Intermediate/Writing-a-Composable-Node.rst @@ -47,7 +47,7 @@ Code Updates Add the Package Dependency ^^^^^^^^^^^^^^^^^^^^^^^^^^ -Your ``package.xml`` should have a dependency on ``rclcpp_components``, a la +Your ``package.xml`` should have a dependency on ``rclcpp_components``, a la .. code-block:: xml From 605d1a737c7b471647e8af72517735a79c16a109 Mon Sep 17 00:00:00 2001 From: "David V. Lu" Date: Wed, 31 Jan 2024 15:27:44 -0500 Subject: [PATCH 6/7] Add links to demos --- .../Tutorials/Intermediate/Writing-a-Composable-Node.rst | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/source/Tutorials/Intermediate/Writing-a-Composable-Node.rst b/source/Tutorials/Intermediate/Writing-a-Composable-Node.rst index db056ab7775..46b8e627503 100644 --- a/source/Tutorials/Intermediate/Writing-a-Composable-Node.rst +++ b/source/Tutorials/Intermediate/Writing-a-Composable-Node.rst @@ -47,16 +47,18 @@ Code Updates Add the Package Dependency ^^^^^^^^^^^^^^^^^^^^^^^^^^ -Your ``package.xml`` should have a dependency on ``rclcpp_components``, a la +Your `package.xml `__ should have a dependency on ``rclcpp_components``, a la .. code-block:: xml rclcpp_components +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 takes a ``NodeOptions`` argument. +The only change to your class definition that you may have to do is ensure that `the constructor for the class `__ takes a ``NodeOptions`` argument. .. code-block:: c++ @@ -81,7 +83,7 @@ Replace your main method with a ``pluginlib``-style macro invocation. CMake Changes ^^^^^^^^^^^^^ -First, add ``rclcpp_components`` as a dependency with +First, add ``rclcpp_components`` as a dependency in your `CMakeLists.txt `__ with .. code-block:: cmake From 3958cb69ddd8d9d095b20ee6658c13f4da689622 Mon Sep 17 00:00:00 2001 From: "David V. Lu!!" Date: Fri, 16 Feb 2024 11:28:14 -0500 Subject: [PATCH 7/7] Apply suggestions from code review Co-authored-by: Chris Lalancette Signed-off-by: David V. Lu!! --- source/Concepts/Intermediate/About-Composition.rst | 2 +- source/Tutorials/Intermediate/Writing-a-Composable-Node.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source/Concepts/Intermediate/About-Composition.rst b/source/Concepts/Intermediate/About-Composition.rst index 5af9c377f7f..a6cbdb5dcc1 100644 --- a/source/Concepts/Intermediate/About-Composition.rst +++ b/source/Concepts/Intermediate/About-Composition.rst @@ -54,7 +54,7 @@ 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>` +For an example, :doc:`check out this tutorial <../../Tutorials/Intermediate/Writing-a-Composable-Node>` .. note:: diff --git a/source/Tutorials/Intermediate/Writing-a-Composable-Node.rst b/source/Tutorials/Intermediate/Writing-a-Composable-Node.rst index 46b8e627503..9f559aa2444 100644 --- a/source/Tutorials/Intermediate/Writing-a-Composable-Node.rst +++ b/source/Tutorials/Intermediate/Writing-a-Composable-Node.rst @@ -83,7 +83,7 @@ Replace your main method with a ``pluginlib``-style macro invocation. CMake Changes ^^^^^^^^^^^^^ -First, add ``rclcpp_components`` as a dependency in your `CMakeLists.txt `__ with +First, add ``rclcpp_components`` as a dependency in your CMakeLists.txt with: .. code-block:: cmake