[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
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
Comments