Set parameters to multiple rclcpp::Node instances in a single executable / share object across nodes/components

asked 2023-06-01 09:08:50 -0500

basti.hunter gravatar image

updated 2023-06-01 09:56:00 -0500

Hello,

I am looking in to a autonomy application setup, where I have one map and several "applications" (=nodes?) accessing its data. For organizational purposes I would like a separate nodes for the map and every other component, as each might have individual subscriptions. As the autonomy nodes will have to query data from the same map, I thought about moving the node to a separate component and communicate via ROS2 services. But this is to slow for the application at hand. Thus I came up with the following setup:

Main executable

#include <pluginlib/class_loader.hpp>

#include <test_pkg/TesteNode1.hpp>
#include <test_pkg/TesteNode2.hpp>
#include <map_pkg/MapNode.hpp>

int main(int argc, char** argv)
{
    setvbuf(stdout, NULL, _IONBF, BUFSIZ);
    rclcpp::init(argc, argv);
    rclcpp::executors::MultiThreadedExecutor executor;

    const auto logger = rclcpp::get_logger("OrganizerMain");
    RCLCPP_WARN(logger, "main started");

    auto map_node = std::make_shared<map_pkg::MapNode>(
            rclcpp::NodeOptions().use_intra_process_comms(true));

    auto node_1 = std::make_shared<test_pkg::TesteNode1>(
            rclcpp::NodeOptions().use_intra_process_comms(true));
    node_1->setMap(map_node->getMap());

    auto node_2 = std::make_shared<test_pkg::TesteNode2>(
            rclcpp::NodeOptions().use_intra_process_comms(true));
    node_2->setMap(map_node->getMap());

    executor.add_node(map_node);
    executor.add_node(node_1);
    executor.add_node(node_2);
    executor.spin();

    rclcpp::shutdown();

    return 0;
}

TestNode1 (TestNode2 similar)

...
TesteNode1::TesteNode1(const rclcpp::NodeOptions& _options) 
            : Node("TesteNode1", _options) {
    this->declare_parameter("test_param", "hello world");
    RCLCPP_INFO_STREAM(this->get_logger(), "test_param=" << this->get_parameter("test_param").as_string());
}
...

launch file test.py

from launch import LaunchDescription
import launch_ros.actions

def generate_launch_description():
    return LaunchDescription([
        launch_ros.actions.Node(
            namespace= "",
            package='test_pkg',
            executable='test_node',
            output='screen',
            emulate_tty=True,
            parameters=[
                { "test_param": "Hello1" }
            ],
        )
    ]
)

Output

$ ros2 launch test_pkg test.py
[INFO] [launch]: All log files can be found below /home/user/.ros/log/2023-06-01-15-41-43-435154-ubuntu-246267
[INFO] [launch]: Default logging verbosity is set to INFO
[INFO] [test_node-1]: process started with pid [246268]
[tester_node-1] [WARN] [1685626903.514494813] [OrganizerMain]: main started
[tester_node-1] [INFO] [1685626903.523197282] [MapNode]: MapNode started
[tester_node-1] [INFO] [1685626903.523408988] [MapNode]: ...
[tester_node-1] [INFO] [1685626903.532038474] [TesteNode1]: test_param=Hello1
[tester_node-1] [INFO] [1685626903.534378075] [TesteNode2]: test_param=Hello1

The issue running this is the parameter configuration, as I would like to choose different parameters for the nodes in the future and would not like to accidentally mix then in the different nodes.

Question: Is this possible in the launch file or does anyone has another idea how to set this up? Something like from launch import LaunchDescription import launch_ros.actions

def generate_launch_description():
    return LaunchDescription([
        launch_ros.actions.Node(
            namespace= "",
            package='test_pkg',
            executable='test_node',
            output='screen',
            emulate_tty=True,
            parameters=[
                { "node1" : [
                    { "test_param": "Hello1" }
                ]},
                { "node2" : [
                    { "test_param": "Hello2" }
                ]}
            ],
        )
    ]
)

TLDR;

I looked a lot into composition and use it in other places, but indicated by the lines

node_1->setMap(map_node->getMap());
node_2->setMap(map_node->getMap());

the setup needs a linking to the map inside test nodes TestNode1 and TestNode2 and the queries to the map are usually of a type response/request with request parameters (e.g. like a position of interest) so I can not use polling. Additionally I tried a service implementation, but with > 100 requests in 1s this is not usable.

edit retag flag offensive close merge delete