0
$\begingroup$

SO basically what I am trying to do is load multiple parameters from one yaml file and distribute them to multiple nodes. I have provided the code below. Basically instead of launch parameters I want to load them all through a single yaml. Any help would be greatly apprecaited.

`import os import yaml from ament_index_python.packages import get_package_share_directory from launch import LaunchDescription from launch.actions import IncludeLaunchDescription, ExecuteProcess, GroupAction from launch.launch_description_sources import PythonLaunchDescriptionSource from launch_ros.actions import Node, PushRosNamespace from launch.substitutions import LaunchConfiguration

def generate_launch_description(): # Load parameters from yaml file launch_params_path = os.path.join(get_package_share_directory('ros2_bringup'), 'config', 'parms.yaml') with open(launch_params_path, 'r') as file: launch_params = yaml.safe_load(file)['jetson_1']['ros__parameters']

# namespace = launch_params.get('namespace', '')
# camera_type = launch_params.get('camera_type', '')
# serial = launch_params.get('serial', '')
# sonar = launch_params.get('sonar', '')
# cam_topic = launch_params.get('cam_topic', '')
# frequency = launch_params.get('frequency', '')

# Paths to other files
microstrain_launch_file = os.path.join(get_package_share_directory('microstrain_inertial_examples'),
                                       'launch', 'cv7_launch.py')
microstrain_params_file = os.path.join(get_package_share_directory('microstrain_inertial_examples'),
                                       'config', 'cv7', 'cv7.yml')
empty_params_file = os.path.join(get_package_share_directory('microstrain_inertial_driver'),
                                 'config', 'empty.yml')
sonar_config_file = os.path.join(get_package_share_directory('imagenex831l_ros2'), 'cfg', 'sonar.yaml')

cam_driver_launch_file = os.path.join(get_package_share_directory('spinnaker_camera_driver'),
                                      'launch', 'driver_node.launch.py')
gui_launch_file = os.path.join(get_package_share_directory('custom_guyi'),
                               'launch', 'gui.launch.py')

# Nodes and actions
included_cam_launch = GroupAction(
    actions=[
        PushRosNamespace(namespace),
        IncludeLaunchDescription(
            PythonLaunchDescriptionSource(cam_driver_launch_file),
            launch_arguments={'camera_type': camera_type, 'serial': serial, 'frame_rate': frequency}.items()
        )
    ]
)

ping1d_node = Node(
    package='ms5837_bar_ros',
    executable='bar30_node',
    namespace=namespace,
    output="screen"
)

base_to_range = Node(
    package='tf2_ros',
    executable='static_transform_publisher',
    output='screen',
    namespace=namespace,
    arguments=['0.0', '0.0', '0.0', '0', '0.0', '0.0', 'base_link', 'bar30_link']
)

included_imu_launch = GroupAction(
    actions=[
        PushRosNamespace(namespace),
        IncludeLaunchDescription(
            PythonLaunchDescriptionSource(microstrain_launch_file),
            launch_arguments={'namespace': namespace}.items()
        )
    ]
)

included_sonar_launch = GroupAction(
    actions=[
        PushRosNamespace(namespace),
        IncludeLaunchDescription(
            PythonLaunchDescriptionSource(os.path.join(get_package_share_directory('imagenex831l_ros2'),
                                                       'launch', 'sonar.launch.py')),
            launch_arguments={'sonar': sonar, 'device': namespace, 'config': sonar_config_file}.items()
        )
    ]
)

included_screen_launch = GroupAction(
    actions=[
        PushRosNamespace(namespace),
        IncludeLaunchDescription(
            PythonLaunchDescriptionSource(gui_launch_file),
            launch_arguments={'cam_topic': cam_topic, 'device': namespace}.items(),
        )
    ]
)

debayer_node = Node(
    package='ros2_bringup',
    executable='debayer.py',
    name='debayer',
    namespace=namespace,
    output='screen',
    parameters=[{'cam_topic': cam_topic, 'device': namespace}]
)

nodes = [
    included_cam_launch,
    ping1d_node,
    base_to_range,
    included_imu_launch,
    included_sonar_launch,
    included_screen_launch,
    debayer_node
]

# Topics to record
topics = [
    '/jetson_1/image/compressed',
    '/jetson_1/bar30/depth',
    '/jetson_1/bar30/pressure',
    '/jetson_1/bar30/temperature',
    '/jetson_1/imagenex831l/range',
    '/jetson_1/imu/data',
    '/jetson_1/ekf/status',
    '/jetson_1/imagenex831l/range_raw'
]

return LaunchDescription(
    nodes + [
        ExecuteProcess(
            cmd=['ros2', 'bag', 'record'] + topics,
            output='screen'
        )
    ]
)

`

$\endgroup$

1 Answer 1

1
$\begingroup$

You can define the parameters for multiple nodes within one parameter file like such:

from launch import LaunchDescription
from launch_ros.actions import Node
from ament_index_python.packages import get_package_share_directory
import os

def generate_launch_description():
    pkg_share = get_package_share_directory('package_where_your_config_is')
    ld = LaunchDescription()
    talker_node = Node(
        package="demo_nodes_cpp",
        executable="talker",
        name="talker_node",
        parameters=[os.path.join(pkg_share, 'config', config.yaml')] 
    )
    listener_node = Node(
        package="demo_nodes_py",
        executable="listener",
        name="listener_node",
        parameters=[os.path.join(pkg_share, 'config', config.yaml')] 
    )
    ld.add_action(talker_node)
    ld.add_action(listener_node)
    return ld

where the config.yaml then is:

/talker_node:
  ros__parameters:
    talker_parameter_1: 1.0
    talker_parameter_2: 2.0
/listener_node:
  ros__parameters:
    listener_parameter_1: 1.0
    listener_parameter_2: 2.0

You must only know what the node names or set them like I did in the minimal example. It parses only the parameters that follow the name of the node, since it's parsed in a struct-like fashion.

Concerning namespaces, you can

namespace_variable = LaunchConfiguration('namespace_launchArgument')
declare_namespace_cmd = DeclareLaunchArgument(
name='namespace_launchArgument',
default_value='foonamespace',
description='Namespace for all Nodes.')
ld.add_action(declare_namespace_cmd)

and hand over the namespace to any Node by adding it as another argument, e.g.:

talker_node = Node(
        package="demo_nodes_cpp",
        executable="talker",
        name="talker_node",
        namespace=namespace_variable ,
        parameters=[os.path.join(pkg_share, 'config', config.yaml')] 
    )

The beauty of this is that LaunchArguments can be passed to "child"-launch-files, see ROS2 Docs on the topic

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.