This demo provides examples of three different ways to use the rclcpp_components API to compose multiple nodes in a single process.
This ROS 2 package consists of the following demo applications:
dlopen_composition
linktime_composition
manual_composition
Run the commands below to build the ROS 2 package:
colcon build --packages-up-to composition
Running manual_composition
compiles an executable that runs the following 4 components:
- Talker: A ROS 2 component that publishes a string
- Listener: A ROS 2 component that prints the received string from Talker
- Server: A ROS 2 component that adds two integers and outputs its result to Client
- Client: A ROS 2 component that sends two integers to Server and prints the received result from Server
ros2 run composition manual_composition
This runs dlopen_composition
which is an alternative to run-time composition by creating a generic container process and explicitly passing the libraries to load without using ROS interfaces.
The process will open each library and create one instance of each “rclcpp::Node” class in the library.
ros2 run composition dlopen_composition `ros2 pkg prefix composition`/lib/libtalker_component.so `ros2 pkg prefix composition`/lib/liblistener_component.so
Similar to previous, this runs linktime_composition
which links all classes from libraries that are registered under the library_path with the linker.
ros2 run composition linktime_composition
Rather than using the command line tool to run each composition, we can automate this action with ros2 launch
functionality:
ros2 launch composition composition_demo_launch.py
When executed correctly, strings should be printed to terminal similar to what is shown below:
[INFO] [1674528188.026468320] [talker]: Publishing: 'Hello World: 1'
[INFO] [1674528188.027043857] [listener]: I heard: [Hello World: 1]
[INFO] [1674528189.026414368] [talker]: Publishing: 'Hello World: 2'
[INFO] [1674528189.026742015] [listener]: I heard: [Hello World: 2]
[INFO] [1674528189.032512995] [Server]: Incoming request: [a: 2, b: 3]
[INFO] [1674528189.032815843] [Client]: Got result: [5]
[INFO] [1674528190.026455807] [talker]: Publishing: 'Hello World: 3'
[INFO] [1674528190.026795770] [listener]: I heard: [Hello World: 3]
[INFO] [1674528191.026457639] [talker]: Publishing: 'Hello World: 4'
[INFO] [1674528191.026801926] [listener]: I heard: [Hello World: 4]
[INFO] [1674528191.032377264] [Server]: Incoming request: [a: 2, b: 3]
[INFO] [1674528191.032604427] [Client]: Got result: [5]
[INFO] [1674528192.026428269] [talker]: Publishing: 'Hello World: 5'
[INFO] [1674528192.026537974] [listener]: I heard: [Hello World: 5]
[INFO] [1674528193.026437034] [talker]: Publishing: 'Hello World: 6'
[INFO] [1674528193.026767708] [listener]: I heard: [Hello World: 6]
[INFO] [1674528193.032377748] [Server]: Incoming request: [a: 2, b: 3]
[INFO] [1674528193.032603036] [Client]: Got result: [5]
#...
Note that manually-composed components will not be reflected in the
ros2 component list
command line tool output.
When executed correctly, strings should be printed to terminal similar to what is shown below:
INFO] [1674529118.496557668] [dlopen_composition]: Load library /opt/ros/rolling/lib/libtalker_component.so
[INFO] [1674529118.496774575] [dlopen_composition]: Instantiate class rclcpp_components::NodeFactoryTemplate<composition::Talker>
[INFO] [1674529118.503388909] [dlopen_composition]: Load library /opt/ros/rolling/lib/liblistener_component.so
[INFO] [1674529118.503739855] [dlopen_composition]: Instantiate class rclcpp_components::NodeFactoryTemplate<composition::Listener>
[INFO] [1674529119.503505873] [talker]: Publishing: 'Hello World: 1'
[INFO] [1674529119.503770137] [listener]: I heard: [Hello World: 1]
[INFO] [1674529120.503572362] [talker]: Publishing: 'Hello World: 2'
[INFO] [1674529120.503888374] [listener]: I heard: [Hello World: 2]
[INFO] [1674529121.503503459] [talker]: Publishing: 'Hello World: 3'
[INFO] [1674529121.503628269] [listener]: I heard: [Hello World: 3]
[INFO] [1674529122.503557862] [talker]: Publishing: 'Hello World: 4'
[INFO] [1674529122.503894772] [listener]: I heard: [Hello World: 4]
[INFO] [1674529123.503574524] [talker]: Publishing: 'Hello World: 5'
[INFO] [1674529123.503884894] [listener]: I heard: [Hello World: 5]
#...
Note that dlopen-composed components will not be reflected in the
ros2 component list
command line tool output.
When executed correctly, strings should be printed to terminal similar to what is shown below:
[INFO] [1674528568.091949637] [linktime_composition]: Load library
[INFO] [1674528568.091995119] [linktime_composition]: Instantiate class rclcpp_components::NodeFactoryTemplate<composition::Client>
[INFO] [1674528568.098833910] [linktime_composition]: Instantiate class rclcpp_components::NodeFactoryTemplate<composition::Listener>
[INFO] [1674528568.100669644] [linktime_composition]: Instantiate class rclcpp_components::NodeFactoryTemplate<composition::Server>
[INFO] [1674528568.102665704] [linktime_composition]: Instantiate class rclcpp_components::NodeFactoryTemplate<composition::Talker>
[INFO] [1674528569.104717098] [talker]: Publishing: 'Hello World: 1'
[INFO] [1674528569.105206993] [listener]: I heard: [Hello World: 1]
[INFO] [1674528570.099206827] [Server]: Incoming request: [a: 2, b: 3]
[INFO] [1674528570.099376432] [Client]: Got result: [5]
[INFO] [1674528570.104656875] [talker]: Publishing: 'Hello World: 2'
[INFO] [1674528570.105069514] [listener]: I heard: [Hello World: 2]
[INFO] [1674528571.104710545] [talker]: Publishing: 'Hello World: 3'
[INFO] [1674528571.105150094] [listener]: I heard: [Hello World: 3]
[INFO] [1674528572.099350955] [Server]: Incoming request: [a: 2, b: 3]
[INFO] [1674528572.099628903] [Client]: Got result: [5]
[INFO] [1674528572.104631322] [talker]: Publishing: 'Hello World: 4'
[INFO] [1674528572.104911174] [listener]: I heard: [Hello World: 4]
[INFO] [1674528573.104596009] [talker]: Publishing: 'Hello World: 5'
[INFO] [1674528573.104751214] [listener]: I heard: [Hello World: 5]
#...
Note that linktime-composed components will not be reflected in the
ros2 component list
command line tool output.
When executed correctly, strings should be printed to terminal similar to what is shown below:
[INFO] [launch]: All log files can be found below /root/.ros/log/2024-05-04-23-37-06-363020-d8ff93e471d7-9387
[INFO] [launch]: Default logging verbosity is set to INFO
[INFO] [component_container-1]: process started with pid [9402]
[component_container-1] [INFO] [1714865826.695090046] [my_container]: Load Library: /opt/ros/rolling/lib/libtalker_component.so
[component_container-1] [INFO] [1714865826.696388047] [my_container]: Found class: rclcpp_components::NodeFactoryTemplate<composition::Talker>
[component_container-1] [INFO] [1714865826.696435882] [my_container]: Instantiate class: rclcpp_components::NodeFactoryTemplate<composition::Talker>
[INFO] [launch_ros.actions.load_composable_nodes]: Loaded node '/talker' in container '/my_container'
[component_container-1] [INFO] [1714865826.702958710] [my_container]: Load Library: /opt/ros/rolling/lib/liblistener_component.so
[component_container-1] [INFO] [1714865826.703401061] [my_container]: Found class: rclcpp_components::NodeFactoryTemplate<composition::Listener>
[component_container-1] [INFO] [1714865826.703414344] [my_container]: Instantiate class: rclcpp_components::NodeFactoryTemplate<composition::Listener>
[INFO] [launch_ros.actions.load_composable_nodes]: Loaded node '/listener' in container '/my_container'
[component_container-1] [INFO] [1714865827.701941449] [talker]: Publishing: 'Hello World: 1'
[component_container-1] [INFO] [1714865827.702047599] [listener]: I heard: [Hello World: 1]
[component_container-1] [INFO] [1714865828.701984118] [talker]: Publishing: 'Hello World: 2'
[component_container-1] [INFO] [1714865828.702154523] [listener]: I heard: [Hello World: 2]
[component_container-1] [INFO] [1714865829.702004471] [talker]: Publishing: 'Hello World: 3'
[component_container-1] [INFO] [1714865829.702176059] [listener]: I heard: [Hello World: 3]
[component_container-1] [INFO] [1714865830.701876733] [talker]: Publishing: 'Hello World: 4'
[component_container-1] [INFO] [1714865830.701965546] [listener]: I heard: [Hello World: 4]
[component_container-1] [INFO] [1714865831.701885355] [talker]: Publishing: 'Hello World: 5'
[component_container-1] [INFO] [1714865831.701984823] [listener]: I heard: [Hello World: 5]
Q
: Why use node composition?
A
: Node composition avoids the overhead of marshalling and unmarshaling messages by allowing nodes to be instantiated within the same process.