Robotics StackExchange | Archived questions

[ROS2] Launch same node several times using same launch file

Hello everyone!

Perhaps, a bit confusing title, I will try to explain what I want to do:

I am using ROS2 Foxy Fitzroy. I have a C++ node named 'dataprocessor' which is essentially just a publisher. I have written a launch file takes in arguments and launches the 'dataprocessor' with a user-defined namespace, named 'dataprocessorlaunch.py'. Now, I want to write a another launch file 'dataprocessorsalllaunch.py', which launches 2 instances of 'dataprocessor' using the child launch file, but under different namespaces.

I succeed in launching 2 instances of 'dataprocessors' separately (using the 'dataprocesorlaunch.py' with namespace 'tmlm' and using parent launch file 'dataprocessorsalllaunch.py' with just one instance there under namespace 'tblm'). But when I try to Include the same launch description twice with different it fails. I believe it should be possible to launch it from one single launch file, because these essentially become different objects.

This is "dataprocessorlaunch.py":

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

def generate_launch_description():
    value = LaunchConfiguration('namespace', default = 'tb_lm')
    ld = LaunchDescription()
    config = os.path.join(
        get_package_share_directory('loading_motor_dt'),
    'config',
    'params.yaml'
    )

    data_processor=Node(
       package = 'loading_motor_dt',
       namespace = value,
       name = 'data_processor',
       executable = 'currentVoltageFlow',
       parameters = [config]
    )
   d.add_action(data_processor)
   return ld

This is "dataprocessorall_launch.py": import os

from click import launch
from ament_index_python.packages import get_package_share_directory
from launch import LaunchDescription
from launch_ros.actions import Node
from launch.actions import IncludeLaunchDescription
from launch.launch_description_sources import PythonLaunchDescriptionSource

def generate_launch_description():
    ld = LaunchDescription()

    package_prefix = get_package_share_directory('loading_motor_dt')
    src = PythonLaunchDescriptionSource([package_prefix,'/launch/data_processor_launch.py'])
    launch_tm = IncludeLaunchDescription(src,launch_arguments = {'namespace': 'tm_lm'}.items())
    launch_tb = IncludeLaunchDescription(src,launch_arguments = {'namespace': 'tb_lm'}.items())
    print(launch_tb)
    print(launch_tm)
    ld.add_action(launch_tm)
    ld.add_action(launch_tb)
    return ld

And the following is the error I get when I launch the "dataprocessorall_launch.py"

[INFO] [launch]: All log files can be found below /home/sejego/.ros/log/2022-04-08-21-37-47-943060-pop-os-21257
[INFO] [launch]: Default logging verbosity is set to INFO
<launch.actions.include_launch_description.IncludeLaunchDescription object at 0x7f3e72e0c9a0>
<launch.actions.include_launch_description.IncludeLaunchDescription object at 0x7f3e73791880>
Task exception was never retrieved
future: <Task finished name='Task-2' coro=<LaunchService._process_one_event() done, defined at /opt/ros/foxy/lib/python3.8/site-packages/launch/launch_service.py:226> exception=RuntimeError("ExecuteProcess action 'currentVoltageFlow-1': executed more than once: <launch_ros.actions.node.Node object at 0x7f3e723b2700>")>
Traceback (most recent call last):
  File "/opt/ros/foxy/lib/python3.8/site-packages/launch/launch_service.py", line 228, in _process_one_event
    await self.__process_event(next_event)
  File "/opt/ros/foxy/lib/python3.8/site-packages/launch/launch_service.py", line 248, in __process_event
    visit_all_entities_and_collect_futures(entity, self.__context))
  File "/opt/ros/foxy/lib/python3.8/site-packages/launch/utilities/visit_all_entities_and_collect_futures_impl.py", line 45, in visit_all_entities_and_collect_futures
    futures_to_return += visit_all_entities_and_collect_futures(sub_entity, context)
      File "/opt/ros/foxy/lib/python3.8/site-packages/launch/utilities/visit_all_entities_and_collect_futures_impl.py", line 45, in visit_all_entities_and_collect_futures
        futures_to_return += visit_all_entities_and_collect_futures(sub_entity, context)
      File "/opt/ros/foxy/lib/python3.8/site-packages/launch/utilities/visit_all_entities_and_collect_futures_impl.py", line 45, in visit_all_entities_and_collect_futures
        futures_to_return += visit_all_entities_and_collect_futures(sub_entity, context)
      [Previous line repeated 3 more times]
      File "/opt/ros/foxy/lib/python3.8/site-packages/launch/utilities/visit_all_entities_and_collect_futures_impl.py", line 38, in visit_all_entities_and_collect_futures
    sub_entities = entity.visit(context)
  File "/opt/ros/foxy/lib/python3.8/site-packages/launch/action.py", line 108, in visit
    return self.execute(context)
  File "/opt/ros/foxy/lib/python3.8/site-packages/launch_ros/actions/node.py", line 453, in execute
    ret = super().execute(context)
  File "/opt/ros/foxy/lib/python3.8/site-packages/launch/actions/execute_process.py", line 781, in execute
    raise RuntimeError(
RuntimeError: ExecuteProcess action 'currentVoltageFlow-1': executed more than once: <launch_ros.actions.node.Node object at 0x7f3e723b2700>
Task exception was never retrieved
future: <Task finished name='Task-8' coro=<LaunchService._process_one_event() done, defined at /opt/ros/foxy/lib/python3.8/site-packages/launch/launch_service.py:226> exception=RuntimeError('Signal event received before subprocess transport available.')>
Traceback (most recent call last):
  File "/opt/ros/foxy/lib/python3.8/site-packages/launch/launch_service.py", line 228, in _process_one_event
    await self.__process_event(next_event)
  File "/opt/ros/foxy/lib/python3.8/site-packages/launch/launch_service.py", line 248, in __process_event
    visit_all_entities_and_collect_futures(entity, self.__context))
  File "/opt/ros/foxy/lib/python3.8/site-packages/launch/utilities/visit_all_entities_and_collect_futures_impl.py", line 38, in visit_all_entities_and_collect_futures
    sub_entities = entity.visit(context)
  File "/opt/ros/foxy/lib/python3.8/site-packages/launch/action.py", line 108, in visit
    return self.execute(context)
  File "/opt/ros/foxy/lib/python3.8/site-packages/launch/actions/opaque_function.py", line 75, in execute
    return self.__function(context, *self.__args, **self.__kwargs)
  File "/opt/ros/foxy/lib/python3.8/site-packages/launch/actions/execute_process.py", line 443, in __on_signal_process_event
    raise RuntimeError('Signal event received before subprocess transport available.')
RuntimeError: Signal event received before subprocess transport available.
[INFO] [currentVoltageFlow-1]: process started with pid [21259]
[ERROR] [currentVoltageFlow-1]: process[currentVoltageFlow-1] failed to terminate '5' seconds after receiving 'SIGINT', escalating to 'SIGTERM'
[INFO] [currentVoltageFlow-1]: sending signal 'SIGTERM' to process[currentVoltageFlow-1]
[ERROR] [currentVoltageFlow-1]: process has died [pid 21259, exit code -15, cmd '/home/sejego/dev_ws/install/loading_motor_dt/lib/loading_motor_dt/currentVoltageFlow --ros-args -r __node:=data_processor -r __ns:=/tm_lm --params-file /home/sejego/dev_ws/install/loading_motor_dt/share/loading_motor_dt/config/params.yaml'].

From the result of printing the Launch Descriptions, I can see that these are different instances, therefore I assume they should be perfectly executable? Unless I misunderstood something:

<launch.actions.include_launch_description.IncludeLaunchDescription object at 0x7f3e72e0c9a0>
<launch.actions.include_launch_description.IncludeLaunchDescription object at 0x7f3e73791880>

Basically, the Node launches, then it dies, another one gets launched, but dies a few seconds after. Can't really understand where the problem could be, hope you can help!

Asked by sejego on 2022-04-08 13:42:37 UTC

Comments

Answers

Okay, after 3 hours or so I figured it out!

So, the problem seems to be with the shared src variable in the data_processor_all_launch.py. I slightly changed both files to enable launching the data_processor_launch.py with user-defined namespaces and to accept the namespace argument from top-level data_processor_all_launch.py.

This is now the data_processor_all_launch.py :

import os

from ament_index_python.packages import get_package_share_directory
from launch import LaunchDescription
from launch_ros.actions import Node
from launch.actions import DeclareLaunchArgument
from launch.actions import IncludeLaunchDescription
from launch.actions import GroupAction
from launch.substitutions import TextSubstitution
from launch.launch_description_sources import PythonLaunchDescriptionSource
from launch_ros.actions import PushRosNamespace
from launch.substitutions import LaunchConfiguration

def generate_launch_description():

ld = LaunchDescription()

package_prefix = get_package_share_directory('loading_motor_dt')
load_data_processor = PythonLaunchDescriptionSource([package_prefix,'/launch/data_processor_launch.py'])
traction_data_processor = PythonLaunchDescriptionSource([package_prefix,'/launch/data_processor_launch.py'])

launch_traction = GroupAction(
    actions=[
        # push-ros-namespace to set namespace of included nodes
        #PushRosNamespace('tb_lm_left'),
        IncludeLaunchDescription(load_data_processor, launch_arguments = {'namespace':'tb_tm'}.items()),
    ]
)
launch_loading = GroupAction(
    actions=[
        # push-ros-namespace to set namespace of included nodes
        #PushRosNamespace('tb_tm'),
        IncludeLaunchDescription(traction_data_processor, launch_arguments = {'namespace':'tb_tm'}.items()),
    ]
)

ld.add_action(launch_traction)
ld.add_action(launch_loading)
return ld

This is now the data_processor_launch.py:

import os
from ament_index_python.packages import get_package_share_directory
from launch import LaunchDescription
from launch_ros.actions import Node
from launch.substitutions import LaunchConfiguration
from launch_ros.actions import PushRosNamespace
from launch.actions import GroupAction

def generate_launch_description():

    ns = LaunchConfiguration('namespace', default = 'tb_tm')

    ld = LaunchDescription()
    config = os.path.join(
        get_package_share_directory('loading_motor_dt'),
        'config',
        'params.yaml'
    )

    data_processor=Node(
        package = 'loading_motor_dt',
        name = 'data_processor',
        executable = 'currentVoltageFlow',
        parameters = [config]
    )

    data_processor_with_ns = GroupAction(
        actions=[
            PushRosNamespace(ns),
            data_processor,
        ]
    )

    ld.add_action(data_processor_with_ns)
    return ld

And finally the result:

sejego@pop-os:~/dev_ws$ ros2 node list
/tb_lm_left/data_processor
/tb_tm/data_processor

Asked by sejego on 2022-04-09 02:26:56 UTC

Comments

I think you wanted other argument here?

#PushRosNamespace('tb_lm_left'),
        IncludeLaunchDescription(load_data_processor, launch_arguments = {'namespace':'tb_tm'}.items()),

Asked by ljaniec on 2022-04-09 20:52:16 UTC

@ljaniec you are right, sorry for confusion. I edited my asnwer so it will not be confusing for others. Have a nice day!

Asked by sejego on 2022-04-21 16:07:36 UTC