ros2 / launch_ros Goto Github PK
View Code? Open in Web Editor NEWTools for launching ROS nodes and for writing tests involving ROS nodes.
License: Apache License 2.0
Tools for launching ROS nodes and for writing tests involving ROS nodes.
License: Apache License 2.0
Parameters can be passed to Node actions as dictionaries as of ros2/launch#138. This is done by writing a temporary parameter yaml file. The temporary file's name should be kept track of and deleted on shutdown to avoid files taking up too much space (hypothetically, in the case where nodes are set to respawn on shutdown, if there's an error that keeps restarting the node, many files could be generated (depending on how it's implemented)).
This task is a followup from https://github.com/ros2/launch/pull/138/files#diff-d78062ac1f3a77c95190e470302b7e69R188.
Required Info:
list_foo= LaunchConfiguration('list_foo')
declare_namespace_cmd = DeclareLaunchArgument(
'list_foo',
default_value=[],
description='a list')
if using an empty list as default_value an error occurs
no error. since in python this is possible:
node.declare_parameter('list_foo', [], ParameterType.PARAMETER_STRING_ARRAY)
error (i can post tomorrow the actual error)
regards magnglb
Parameter files are passed to the CLI as a path but the substitution logic should stay in launch (and shouldn't be moved to the node).
Required Info:
`
xyz_cmd= Node(
package=package;
node_executable='node_executable',
node_name=node_name'
node_namespace = node_namespace ,
output='screen',
parameters = [];
arguments = [('__log_level:=WARN')])
`
arguments = [('__log_level:=WARN')]
leads to: " [WARN] [rcl]: Found log level rule '__log_level:=WARN'. This syntax is deprecated. Use '--ros-args --log-level WARN' instead.
[joint_state_publisher-1] [WARN] [rcl]: Found log level rule '__log_level:=WARN'. This syntax is deprecated. Use '--ros-args --log-level WARN' instead."
**arguments = [(--ros-args --log-level WARN)]**
This does not work
In the launch.py file the log-level has to be defined like that
arguments = ['--ros-args', '--log-level', 'DEBUG'],
The node namespace is always specified when launching a Node action (defaults to /
), but the node name is optional.
When parameters are passed to a Node action as a dictionary, they are written to a yaml file. Yaml files of parameters currently require the node name to be specified.
Because of this, if a dictionary of parameters is passed to a Node action, there is a requirement that the node name is also specified.
This requirement can be removed if parameters are passed individually on the command line to the node in a way that doesn't require the node's name (feature not implemented in rcl yet), or if yaml files are made to no longer require the node name.
When a XML launch file declares parameters for a node the order of the param
tags is not maintained if some use name
and some use from
:
launch_ros/launch_ros/launch_ros/actions/node.py
Lines 210 to 228 in 025596b
As a consequence when multiple of the param
tags with different attributes set the same parameter the actually set value is unexpected since it might not be the last one.
This issue was raised as part of #97 and previously discussed in ros2/launch#208.
Both ros2 launch
and ros2 test
inject an included description that creates an rclpy node. This node is often useful for testing, though not always needed, and necessary to launch certain actions (e.g. LifecycleNode
). The problem is that it can cause confusion and user error.
Related to testing, we end up with two types of launch test files: those that can be executed with launch_test
and those that require using ros2 test
. As evidenced by #97, this can lead to seemingly broken launch files, which are actually just user error (executing with launch_test
instead of ros2 test
).
Related to using ros2 launch
, if a user decides to include the default description themselves (perhaps a rare case), they will end up starting multiple nodes with the same name. This currently results in the following warning, but may be escalated to an error in the future:
[WARN] [rcl.logging_rosout]: Publisher already registered for provided node name. If this is due to multiple nodes with the same name then all logs for that logger name will go out over the existing publisher. As soon as any node with that name is destructed it will unregister the publisher, preventing any further logs for that name from being published on the rosout topic.
I've opened this ticket to discuss alternatives to relying on the CLI tools for including launch_ros description.
Proposed solutions:
launch_test
or ros2 test
.I don't have any solid ideas at the moment for how to accomplish (2). But I think it makes sense in any solution that we guard against including the default description multiple times.
Required Info:
Given the following talker_launch.py
from launch import LaunchDescription
import launch.actions
import launch_ros.actions
def generate_launch_description():
return LaunchDescription([
launch_ros.actions.Node(
package='demo_nodes_cpp',
node_executable='talker',
output='screen',
node_name='talker')
])
And the following file with name include_launch.py
that includes talker_launch.py
:
from launch import LaunchDescription
from launch.actions import IncludeLaunchDescription
from launch.actions import GroupAction
from launch.launch_description_sources import PythonLaunchDescriptionSource
from launch.substitutions import ThisLaunchFileDir
from launch_ros.actions import PushRosNamespace
def generate_launch_description():
return LaunchDescription([
GroupAction([
PushRosNamespace('talker_ns'),
IncludeLaunchDescription(
PythonLaunchDescriptionSource([ThisLaunchFileDir(), '/talker_launch.py']))
])
])
We would expect the node name to be: /talker_ns/talker
.
However, it is: /talker
But, if we provide node_namespace=''
to the action in talker_launch.py
, and launch include_launch.py
, then the node name is /talker_ns/talker
Both launch XML and YAML frontends currently coexist with the YAML parsing of substitution text in Node
parameters. This has caused us issues in the past (e.g. ros2/launch#226 (comment)), forcing additional quotes as escape mechanisms. At the time we deemed this as a situation we'd rarely find in the field.
Well, as you can see in RobotWebTools/rosbridge_suite#433, specifically on commit RobotWebTools/rosbridge_suite@d466c89, it's not as rare as we thought.
There, I'm forced to surround a *
with two '
on each side.
Why a *
needs this? Because it's later used as a Node
parameter and parameters are parsed as YAML, that parses that *
as a malformed alias (see PyYAML documentation about aliases).
Why two '
? The inner pair prevents *
from being parsed as a YAML alias, and the outer pair is removed by the frontend for consistency -- otherwise one has to remember that some values given in e.g. XML are parsed using YAML under the hood and some are not.
IMHO the deeper, unresolved issue that #31 partially addressed is that Substitutions in launch
are always text and it is up to the receiving end to do any additional parsing, opening the door to things like this.
API was deprecated in #122.
After Foxy is released we can remove the deprecated API in preparation for the next major release.
There's a great potential on the testing framework that would be great to have in Crystal as well, specially for testing packages released for multiple distro versions (in this case, considering Crystal as well)
I guess it would be as simple as doing a bloom release for Crystal, unless the API doesn't allow it or requires backporting other changes.
I working with ROS2 Dashing on Ubuntu Bionic. I want to pass parameters to a node with a yaml file.
While I was looking for a solution, I came across a solution for ROS2 crystal on this post https://stackoverflow.com/questions/53917469/how-to-launch-a-node-with-a-parameter-in-ros2.
Unfortunately, that does not seem to work under ROS2 dashing. I created a fork of the ROS2 realsense driver (https://github.com/AndreasAZiegler/ros2_intel_realsense/tree/serial_no_param/realsense_ros2_camera) and my goal is to be able to pass the serial number via a parameter. With
ros2 run realsense_ros2_camera realsense_ros2_camera __params:=/paht/to/config/parameters.yaml
passing the parameter works fine but when I start the node with the launch python file
ros2 launch realsense_ros2_camera ros2_intel_realsense.launch.py
it ignores the parameter. The path to the parameters.yaml is correct (I print it in the launch python script).
Node name uniqueness is not enforced in ROS2. It seems to be uncertain whether-or-when it will be so. In the meantime, there is a lot of user confusion that can happen around having multiple nodes with the same name, since much of ROS2 assumes they are uniquely named.
One user-helping mitigation we can introduce is: Within a single invocation of ros2 launch
, log a warning message for duplicate node names before actually starting them. This will highlight one source of duplicate node names.
Limitations:
I'm not yet familiar enough with this code to understand how to implement. I assume at some point launch has a fully substituted flattened list of things to start, at which point we'd have all the node names expanded out and can detect duplicates.
Required Info:
I'm not sure if this issue is launch or launch_ros related. I've got another issue open here: ros2/launch#359
It appears that a lifecycle_node can deadlock with the launch service when shutting down. Both the launch service and the lifecycle_node are waiting on the launch service's _process_one_event to return. I'm looking for tips for how to continue debugging this.
I can get this to happen by running ros2 test lifecycle/test/test_lifecycle.py
in a loop. Eventually the test will hang after the active tests and before the post-shutdown tests run.
I can determine that launch_testing is stuck in self._launch_service.run()
I can determine the launch service is stuck here
Furthermore, I can print some information about what's in the entity_futures list:
<Task pending coro=<LaunchService._process_one_event() running at /home/pete.baughman/gc/apex_ws/build/launch/launch/launch_service.py:286> wait_for=<Future
pending cb=[Task._wakeup()] created at /usr/lib/python3.6/asyncio/base_events.py:295> created at /home/pete.baughman/gc/apex_ws/build/launch/launch/launch_service.py:355>
[
<FrameSummary file /home/pete.baughman/gc/apex_ws/build/launch/launch/launch_service.py, line 414 in run>,
<FrameSummary file /usr/lib/python3.6/asyncio/base_events.py, line 471 in run_until_complete>,
<FrameSummary file /usr/lib/python3.6/asyncio/base_events.py, line 438 in run_forever>,
<FrameSummary file /usr/lib/python3.6/asyncio/base_events.py, line 1451 in _run_once>,
<FrameSummary file /usr/lib/python3.6/asyncio/events.py, line 145 in _run>,
<FrameSummary file /usr/lib/python3.6/asyncio/tasks.py, line 261 in _wakeup>,
<FrameSummary file /usr/lib/python3.6/asyncio/tasks.py, line 180 in _step>,
<FrameSummary file /home/pete.baughman/gc/apex_ws/build/launch/launch/launch_service.py, line 355 in run_async>
]
<Future pending cb=[_chain_future.<locals>._call_check_cancel() at /usr/lib/python3.6/asyncio/futures.py:403] created at /usr/lib/python3.6/asyncio/base_events.py:295>
[
<FrameSummary file /usr/lib/python3.6/asyncio/tasks.py, line 180 in _step>,
<FrameSummary file /home/pete.baughman/gc/apex_ws/build/launch/launch/launch_service.py, line 287 in _process_one_event>,
<FrameSummary file /home/pete.baughman/gc/apex_ws/build/launch/launch/launch_service.py, line 307 in __process_event>,
<FrameSummary file /home/pete.baughman/gc/apex_ws/build/launch/launch/utilities/visit_all_entities_and_collect_futures_impl.py, line 38 in visit_all_entities_and_collect_futures>,
<FrameSummary file /home/pete.baughman/gc/apex_ws/build/launch/launch/action.py, line 89 in visit>,
<FrameSummary file /home/pete.baughman/gc/apex_ws/build/launch/launch/actions/opaque_function.py, line 75 in execute>,
<FrameSummary file /home/pete.baughman/gc/apex_ws/build/launch_ros/launch_ros/actions/lifecycle_node.py, line 101 in _on_change_state_event>,
<FrameSummary file /usr/lib/python3.6/asyncio/base_events.py, line 655 in run_in_executor>,
<FrameSummary file /usr/lib/python3.6/asyncio/futures.py, line 431 in wrap_future>,
<FrameSummary file /usr/lib/python3.6/asyncio/base_events.py, line 295 in create_future>
]
I'm not so good at asyncio futures, but it looks like I've got one task pending that was created here and another future in the event queue that came from a launch_ros/actions/lifecycle_node that's also waiting for _process_one_event in the launch_service.
Does any of this look familiar to anybody? Any tips for how to continue debugging this hang?
Trying to run this launch test:
python3 -m launch_testing.launch_test src/ros2/launch_ros/launch_testing_ros/test/examples/talker_listener_launch_test.py
Yields:
Traceback (most recent call last):
File "src/ros2/launch_ros/launch_testing_ros/test/examples/talker_listener_launch_test.py", line 72, in test_talker_transmits
node = launch_context.locals.launch_ros_node
File "build/launch/launch/launch_context.py", line 137, in __getattr__
', '.join(_dict.keys())
AttributeError: context.locals does not contain attribute 'launch_ros_node', it contains: []
Ability to call launch
omitting the package_name
argument given that the launch file full path is provided.
E.g.
$ ros2 launch ./my_launch.launch.py
Beside mimicking roslaunch
, this feature comes handy when one wants to run a launch file that is not in a proper ROS package. Even more so since launch files are now Python scripts.
I do not know about the difficulties here, there seems to be a somewhat related todo comment tho.
This is a modified version of launch_ros/examples/pub_sub_launch.py
here.
# Copyright 2018 Open Source Robotics Foundation, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Launch a talker and a listener."""
import os
import sys
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..')) # noqa
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..', 'launch')) # noqa
from launch import LaunchDescription
from launch import LaunchIntrospector
from launch import LaunchService
from launch_ros import get_default_launch_description
import launch_ros.actions
def main(argv=sys.argv[1:]):
"""Main."""
ld = LaunchDescription([
launch_ros.actions.Node(
package='demo_nodes_cpp', node_executable='talker', output='screen',
env={'asd': '1'},
remappings=[('chatter', 'my_chatter')]),
launch_ros.actions.Node(
package='demo_nodes_cpp', node_executable='listener', output='screen',
remappings=[('chatter', 'my_chatter')]),
])
print('Starting introspection of launch description...')
print('')
print(LaunchIntrospector().format_launch_description(ld))
print('')
print('Starting launch of launch description...')
print('')
# ls = LaunchService(debug=True)
ls = LaunchService()
ls.include_launch_description(get_default_launch_description())
ls.include_launch_description(ld)
return ls.run()
if __name__ == '__main__':
main()
This line has been added to the original file:
env={'asd': '1'},
It should work as the original example.
It fails to find the rmw implementation:
Starting introspection of launch description...
<launch.launch_description.LaunchDescription object at 0x7fcf4cfff3c8>
├── ExecuteProcess(cmd=[ExecInPkg(pkg='demo_nodes_cpp', exec='talker'), LocalVar('remapping 1')], cwd=None, env={'asd': '1'}, shell=False)
└── ExecuteProcess(cmd=[ExecInPkg(pkg='demo_nodes_cpp', exec='listener'), LocalVar('remapping 1')], cwd=None, env=None, shell=False)
Starting launch of launch description...
[INFO] [launch]: All log files can be found below /home/ivanpauno/.ros/log/2019-04-11-16-24-06-965421-71c1584df2a2-5104
[INFO] [launch]: Default logging verbosity is set to INFO
[INFO] [talker-1]: process started with pid [5113]
[INFO] [listener-2]: process started with pid [5114]
[talker-1] terminate called after throwing an instance of 'rclcpp::exceptions::RCLError'
[talker-1] what(): failed to initialized rcl init options: failed to find shared library of rmw implementation. Searched rmw_fastrtps_cpp, at /home/ivanpauno/ros2_ws/src/ros2/rmw_implementation/rmw_implementation/src/functions.cpp:129, at /home/ivanpauno/ros2_ws/src/ros2/rcl/rcl/src/rcl/init_options.c:55
[ERROR] [talker-1]: process has died [pid 5113, exit code -6, cmd '/home/ivanpauno/ros2_ws/install/demo_nodes_cpp/lib/demo_nodes_cpp/talker chatter:=my_chatter'].
In the spirit of PushRosNamespace
, a PushRemapping
action would add a remapping to be applied to all Node
s within the scope.
It solves the problem of applying the same remapping (possibly conditioned) to multiple nodes (on possibly on different launch descriptions).
This involves creating a PushRemapping
, perhaps passing tuples of remappings to it. Which then get added to the context's launch configurations. It involves also extending the Node
action, particularly perform_substitutions
to add the remappings on the context.
Required Info:
Create the following two launch files:
foo.launch.xml
<launch>
<group>
<include file='bar.launch.py'>
</include>
</group>
</launch>
bar.launch.py
import launch
import launch_ros
def generate_launch_description():
return launch.LaunchDescription([
launch.actions.DeclareLaunchArgument(name='bar_arg', default_value='True'),
launch_ros.actions.ComposableNodeContainer(
package='rclcpp_components', node_executable='component_container',
node_name='my_container', node_namespace='',
composable_node_descriptions=[
launch_ros.descriptions.ComposableNode(
package='demo_nodes_cpp',
node_plugin='demo_nodes_cpp::Talker',
parameters=[{'use_sim_time': launch.substitutions.LaunchConfiguration('bar_arg')}]
),
]
),
# launch_ros.actions.Node(
# name='talker', package='demo_nodes_cpp', node_executable='talker', output='screen',
# parameters=[{'use_sim_time': launch.substitutions.LaunchConfiguration('bar_arg')}]),
])
Launch foo.launch.xml
:
ros2 launch foo.launch.xml
No launch errors.
Launch error:
[INFO] [launch]: All log files can be found below /home/jacob/.ros/log/2020-01-16-13-53-42-380127-warner-9368
[INFO] [launch]: Default logging verbosity is set to INFO
[INFO] [component_container-1]: process started with pid [9379]
[ERROR] [launch]: Caught exception in launch (see debug for traceback): launch configuration 'bar_arg' does not exist
[INFO] [component_container-1]: sending signal 'SIGINT' to process[component_container-1]
[INFO] [component_container-1]: process has finished cleanly [pid 9379]
[DEBUG] [launch.launch_context]: emitting event synchronously: 'launch.events.IncludeLaunchDescription' [DEBUG] [launch.launch_context]: emitting event synchronously: 'launch.events.IncludeLaunchDescription' [INFO] [launch]: All log files can be found below /home/jacob/.ros/log/2020-01-16-13-54-08-734132-warner-9451 [INFO] [launch]: Default logging verbosity is set to DEBUG [DEBUG] [launch]: processing event: '' [DEBUG] [launch]: processing event: '' ✓ '' [DEBUG] [launch]: processing event: '' [DEBUG] [launch]: processing event: '' ✓ '' [INFO] [component_container-1]: process started with pid [9462] [DEBUG] [launch.launch_context]: emitting event: 'launch.events.process.ProcessStarted' [DEBUG] [launch]: processing event: '' [DEBUG] [launch]: processing event: '' ✓ '' Executing () created at /usr/lib/python3.6/asyncio/queues.py:74> took 0.252 seconds [DEBUG] [launch]: Traceback (most recent call last): File "/opt/ros/eloquent/lib/python3.6/site-packages/launch/launch_service.py", line 381, in run_async await process_one_event_task File "/usr/lib/python3.6/asyncio/coroutines.py", line 126, in send return self.gen.send(value) File "/opt/ros/eloquent/lib/python3.6/site-packages/launch/launch_service.py", line 291, in _process_one_event await self.__process_event(next_event) File "/usr/lib/python3.6/asyncio/coroutines.py", line 110, in __next__ return self.gen.send(None) File "/opt/ros/eloquent/lib/python3.6/site-packages/launch/launch_service.py", line 311, in __process_event visit_all_entities_and_collect_futures(entity, self.__context)) File "/opt/ros/eloquent/lib/python3.6/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/eloquent/lib/python3.6/site-packages/launch/action.py", line 108, in visit return self.execute(context) File "/opt/ros/eloquent/lib/python3.6/site-packages/launch_ros/actions/load_composable_nodes.py", line 171, in execute self._load_in_sequence(self.__composable_node_descriptions, context) File "/opt/ros/eloquent/lib/python3.6/site-packages/launch_ros/actions/load_composable_nodes.py", line 151, in _load_in_sequence self._load_node(next_composable_node_description, context) File "/opt/ros/eloquent/lib/python3.6/site-packages/launch_ros/actions/load_composable_nodes.py", line 114, in _load_node context, composable_node_description.parameters File "/opt/ros/eloquent/lib/python3.6/site-packages/launch_ros/utilities/evaluate_parameters.py", line 145, in evaluate_parameters output_params.append(evaluate_parameter_dict(context, param)) File "/opt/ros/eloquent/lib/python3.6/site-packages/launch_ros/utilities/evaluate_parameters.py", line 69, in evaluate_parameter_dict evaluated_value = perform_substitutions(context, list(value)) File "/opt/ros/eloquent/lib/python3.6/site-packages/launch/utilities/perform_substitutions_impl.py", line 26, in perform_substitutions return ''.join([context.perform_substitution(sub) for sub in subs]) File "/opt/ros/eloquent/lib/python3.6/site-packages/launch/utilities/perform_substitutions_impl.py", line 26, in return ''.join([context.perform_substitution(sub) for sub in subs]) File "/opt/ros/eloquent/lib/python3.6/site-packages/launch/launch_context.py", line 184, in perform_substitution return substitution.perform(self) File "/opt/ros/eloquent/lib/python3.6/site-packages/launch/substitutions/launch_configuration.py", line 99, in perform "launch configuration '{}' does not exist".format(expanded_variable_name)) launch.substitutions.substitution_failure.SubstitutionFailure: launch configuration 'bar_arg' does not exist[ERROR] [launch]: Caught exception in launch (see debug for traceback): launch configuration 'bar_arg' does not exist
[DEBUG] [launch.launch_context]: emitting event: 'launch.events.Shutdown'
[DEBUG] [launch]: processing event: '<launch.events.shutdown.Shutdown object at 0x7fb51d289c88>'
[DEBUG] [launch]: processing event: '<launch.events.shutdown.Shutdown object at 0x7fb51d289c88>' ✓ '<launch.event_handlers.on_shutdown.OnShutdown object at 0x7fb51d2965f8>'
[DEBUG] [launch.launch_context]: emitting event synchronously: 'launch.events.process.SignalProcess'
[DEBUG] [launch]: processing event: '<launch.events.shutdown.Shutdown object at 0x7fb51d289c88>' ✓ '<launch.event_handlers.on_shutdown.OnShutdown object at 0x7fb5204265c0>'
[DEBUG] [launch]: processing event: '<launch.events.shutdown.Shutdown object at 0x7fb51d289c88>' ✓ '<launch.event_handlers.on_shutdown.OnShutdown object at 0x7fb52dde0e10>'
Executing <Task finished coro=<LaunchService._process_one_event() done, defined at /opt/ros/eloquent/lib/python3.6/site-packages/launch/launch_service.py:289> result=None created at /opt/ros/eloquent/lib/python3.6/site-packages/launch/launch_service.py:359> took 0.743 seconds
[DEBUG] [launch]: processing event: '<launch.events.process.signal_process.SignalProcess object at 0x7fb51d343748>'
[DEBUG] [launch]: processing event: '<launch.events.process.signal_process.SignalProcess object at 0x7fb51d343748>' ✓ '<launch.event_handler.EventHandler object at 0x7fb51d296908>'
[INFO] [component_container-1]: sending signal 'SIGINT' to process[component_container-1]
[DEBUG] [launch.launch_context]: emitting event synchronously: 'launch.events.process.ProcessStdout'
[DEBUG] [launch]: processing event: '<launch.events.process.process_stdout.ProcessStdout object at 0x7fb52041cf28>'
[DEBUG] [launch]: processing event: '<launch.events.process.process_stdout.ProcessStdout object at 0x7fb52041cf28>' ✓ '<launch.event_handlers.on_process_io.OnProcessIO object at 0x7fb51d2965c0>'
[INFO] [component_container-1]: process has finished cleanly [pid 9462]
[DEBUG] [launch.launch_context]: emitting event: 'launch.events.process.ProcessExited'
[DEBUG] [launch]: processing event: '<launch.events.process.process_exited.ProcessExited object at 0x7fb52041cdd8>'
[DEBUG] [launch]: processing event: '<launch.events.process.process_exited.ProcessExited object at 0x7fb52041cdd8>' ✓ '<launch.event_handlers.on_process_exit.OnProcessExit object at 0x7fb51d296518>'
[DEBUG] [launch]: processing event: '<launch.events.process.process_exited.ProcessExited object at 0x7fb52041cdd8>' ✓ '<launch.event_handlers.on_process_exit.OnProcessExit object at 0x7fb51d296630>'
If we remove the <group>
tag in foo.launch.xml
, then there is no issue.
If we use a Node action instead of a composable node, then there is no issue.
Passing a lifecycle node executable to a launch_ros.actions.Node
fails rather silently as in the below example. The mistake is hard to spot and should throw an obvious error pointing out the mistake (in this case that node_executable lifecycle_talker does not spin a lifecycle node).
from launch import LaunchDescription
from launch_ros.actions import Node
def generate_launch_description():
return LaunchDescription([
Node(package='lifecycle', node_executable='lifecycle_talker',
node_name='lc_talker', output='screen'),
Node(package='lifecycle', node_executable='lifecycle_listener', output='screen'),
Node(package='lifecycle', node_executable='lifecycle_service_client', output='screen')
])
[INFO] [lifecycle_talker-1]: process started with pid [30809]
[INFO] [lifecycle_listener-2]: process started with pid [30810]
[INFO] [lifecycle_service_client-3]: process started with pid [30811]
[lifecycle_service_client-3] [ERROR] [lc_client]: Service /lc_talker/change_state is not available.
[INFO] [lifecycle_service_client-3]: process has finished cleanly [pid 30811]
Required Info:
pete@pete-VirtualBox:~$ docker pull osrf/ros2:nightly
pete@pete-VirtualBox:~$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
osrf/ros2 nightly 0f21b27cbc11 23 minutes ago 3.62GB
. . .
pete@pete-VirtualBox:~$ docker run --rm -it -v ~/launch_ros:/launch_ros osrf/ros2:nightly
root@1b2a1736429f:/# cd launch_ros/
root@1b2a1736429f:/launch_ros# colcon build
root@1b2a1736429f:/launch_ros# colcon test
Starting >>> launch_ros
Finished <<< launch_ros [4.76s]
Starting >>> launch_testing_ros
Starting >>> ros2launch
Starting >>> test_launch_ros
Finished <<< ros2launch [3.88s]
Finished <<< test_launch_ros [5.58s] [ with test failures ]
Finished <<< launch_testing_ros [11.5s]
Summary: 4 packages finished [16.7s]
1 package had test failures: test_launch_ros
root@1b2a1736429f:/launch_ros# colcon test-result --verbose
. . .
[ERROR] [launch]: Caught exception in launch (see debug for traceback): package 'demo_nodes_py' found at '/opt/ros/foxy', but libexec directory '/opt/ros/foxy/lib/demo_nodes_py' does not exist
These tests should pass, I'm running them unmodified
It looks like something is wrong with how demo_nodes_py is used or maybe built
Maybe this is helpful:
root@1b2a1736429f:/launch_ros# find /opt -iname demo_nodes_py
/opt/ros/foxy/lib/python3.6/site-packages/demo_nodes_py
/opt/ros/foxy/share/demo_nodes_py
/opt/ros/foxy/share/colcon-core/packages/demo_nodes_py
/opt/ros/foxy/share/ament_index/resource_index/packages/demo_nodes_py
It looks like the correct path might be /opt/ros/foxy/lib/python3.6/site-packages/demo_nodes_py instead of /opt/ros/foxy/lib/demo_nodes_py?
Required Info:
Check out a clean workspace
colcon build --packages-up-to test_launch_ros
colcon test --packages-up-to test_launch_ros
test/test_launch_ros/frontend/test_node_frontend.py passes
TypeError: parse_substitution() got an unexpected keyword argument 'optional'
We don't have a SetRemapping
action.
It could be useful, is the same remapping has to be applied in many nodes.
We could store them in the LaunchContext
. The group action will do its magic for limiting their scope.
Previous discussion: ros2/ros2_documentation#302 (comment)
We could do something similar for parameters.
But it won't set a global parameter, as in ROS 1, but it'll set the same parameter in each node in the scope.
Previous discussion:
ros2/ros2_documentation#302 (comment)
Required Info:
It is currently not trivial to set a boolean parameter via a launch file.
return LaunchDescription([
DeclareLaunchArgument(
'use_sim_time',
default_value='True',
description='Use simulation (Gazebo) clock if true'),
Node(
package='robot_state_publisher',
node_executable='robot_state_publisher',
node_name='robot_state_publisher',
output='screen',
parameters=[{'use_sim_time': LaunchConfiguration('use_sim_time')}],
arguments=[urdf])
])
Robot state publisher sets the boolean parameter use_sim_time
to true
Robot state publisher complains that parameter use_sim_time
is a string
A workaround for this to work is to use IfConditions
and UnlessConditions
.
return LaunchDescription([
DeclareLaunchArgument(
'use_sim_time',
default_value='True',
description='Use simulation (Gazebo) clock if true'),
Node(
package='robot_state_publisher',
node_executable='robot_state_publisher',
node_name='robot_state_publisher',
output='screen',
parameters=[{'use_sim_time': True}],
arguments=[urdf],
condition=IfCondition(LaunchConfiguration('use_sim_time'))),
Node(
package='robot_state_publisher',
node_executable='robot_state_publisher',
node_name='robot_state_publisher',
output='screen',
parameters=[{'use_sim_time': False}],
arguments=[urdf],
condition=UnlessCondition(LaunchConfiguration('use_sim_time')))
])
Add a launch action that will allow for a single component to be loaded into a component container. This would be a simplified representation or alias to the larger component actions.
In Dashing, the ComposableNodeContainer action and the corresponding ComposableNode description were introduced. A ComposableNode description also takes a managed node (aka lifecycle node). However, configuring and activating a lifecycle node by a ComposableNode description (instead of a LifecycleNode action) fails:
on_inactive_node_B_handler = launch.actions.RegisterEventHandler(
launch_ros.event_handlers.OnStateTransition(
target_lifecycle_node=node_B,
goal_state='inactive',
entities=[configure_node_A]))
> Caught exception in launch (see debug for traceback): OnStateTransition
requires a 'LifecycleNode' action as the target
Using a matcher is also not possible but gives an exception.
on_inactive_node_B_handler = launch.actions.RegisterEventHandler(
launch_ros.event_handlers.OnStateTransition(
matcher=launch_ros.events.lifecycle.matches_node_name("/example/node_B"),
goal_state='inactive',
entities=[configure_node_A]))
> ... return lambda action: action.node_name == node_name
AttributeError: 'Shutdown' object has no attribute 'node_name'
When calling matches_action with a ComposableNode description is simply does nothing:
configure_node_B = launch.actions.EmitEvent(
event=launch_ros.events.lifecycle.ChangeState(
lifecycle_node_matcher=launch.events.matchers.matches_action(node_B),
transition_id=lifecycle_msgs.msg.Transition.TRANSITION_CONFIGURE,
)
)
Probably, the whole matching and event system for lifecycle action and events has to be extended for ComposbleNode descriptions.
As a starting for experiments, you may use the damped_pendulum_with_transport_delay_as_composed_node.launch.py of the fmi_adapter_examples package, cf. https://github.com/boschresearch/fmi_adapter_ros2/blob/0.1.5/fmi_adapter_examples/launch/damped_pendulum_with_transport_delay_as_composed_node.launch.py and remove comments in lines 90 to 103 and 106 to 108.
Required Info:
<launch>
<!-- Fails -->
<!--node
pkg="demo_nodes_cpp"
exec="listener"
output="screen"
name="demo_listener_1">
</node-->
<!-- Fails -->
<!--node
pkg="demo_nodes_cpp"
exec="listener_2"
output="screen"
name="demo_listener"
args="">
</node-->
<!-- Succeeds with dummy args -->
<node
pkg="demo_nodes_cpp"
exec="listener"
output="screen"
name="demo_listener_3"
args="foo">
</node>
</launch>
Uncomment the node tags in the above launch file (one at a time) and launch.
All nodes in the example launch successfully.
The first two nodes fail to launch.
The first with the error:
AttributeError: Attribute args of type <class 'str'> not found in Entity node
And the second with the error:
lark.exceptions.ParseError: Unexpected end of input! Expecting a terminal of: [Terminal('DOLLAR'), Terminal('UNQUOTED_STRING'), Terminal('DOLLAR')]
This is a feature request for launching multiple composable nodes into the same process. The acceptance criteria for this ticket is limited to launching dynamically composable rclcpp non-lifecycle nodes into a container process.
launch_ros
must pass arguments to the composable nodes where "arguments" means:
rcl
based node
rclcpp
An exception is use_global_arguments
which should always be false
so a composed node is not affected by arguments passed to the container process.
Arguments does not include environment variables like RMW_IMPLEMENTATION
which apply to the whole container process.
PRs
launch_ros.ComposableNode
, launch_ros.actions.LoadComposableNodes
, launch_ros.actions.ComposableNodeContainer
plus examples from #9See slide 8 of https://roscon.ros.org/2018/presentations/ROSCon2018_launch.pdf
ExecuteComposableNodeProcess(
package='rclcpp_components', container_executable='rclcpp_node_container', output='screen',
composable_node_descriptions=[
ComposableNodeDescription(
package_name='demo_nodes_cpp', node_plugin_name='talker',
name='my_talker'),
ComposableNodeDescription(
package_name='demo_nodes_cpp', node_plugin_name='listener'
name='my_listener')
]
)
ExecuteComposableNodeProcess
should start a container executable with a defined API. This allows a user to create a custom container process (e.g. create a container with a custom executor). The API may be a config file and/or a ROS API. The acceptance criteria for this ticket is limited to a ROS service API that can load and unload composable nodes (see also ros2/rcl_interfaces#60).
There must be a new ROS package (rclcpp_components
?) with a container process for rclcpp nodes. The acceptance criteria for this task is to be able to use either a single threaded executor or multithreaded executor in a container process. For example, this could be two container processes, or one with a CLI argument to choose the executor.
Launching a composable node requires a common API for constructing a node.
One option is to mimic ROS 1 nodelets which required a default constructor on the node and had an init()
member function to initialize the nodes with parameters.
Another option is having a constructor with one argument and and a factory class to call it.
Regardless this task should require ros2/rclcpp#492 so there is one object NodeArguments/NodeOptions
so new arguments to a node don't require changing the signature of a function.
This ticket includes creating a CMake API for creating a composable node. This API should
main()
Currently if 2 launch files are launched using launch_ros
, 2 nodes named launch_ros
are created.
The nodes should have different names.
In the other ros2
tools it is currently done by using the process ID https://github.com/ros2/ros2cli/blob/4059a2f84addbbaab121b7f92b872dd5acffa4b3/ros2cli/ros2cli/node/direct.py#L36-L37'
Provide an example on how to use the XML API, i.e., if I have a launch file, how can I get it running? Do I need to create a package, do I need to add it in package.xml
whatsoever...
Especially since the release of Eloquent Elusor, the launch system became way more practical (imho) and is one of the great features of ROS1. Nonetheless, I struggle to get it running. I already asked on answers.ros, receiving no answer.
When using the ros2 launch CLI, tab autocomplete for pwd files persists even after a valid package name is specified in the first argument. This makes it annoying to use the CLI in busy directories, particularly when you'd just like to check for the available launch files in the package specified.
Required Info:
ros2 launch foo<TAB TAB>
should enumerate all packages and files in PWD that start with the string foo
ros2 launch foo bar<TAB TAB>
should enumerate only launch files in package foo
that start with the string bar
Instead, available launch file names are interleaved among the long list of files in the pwd, even though they would be invalid inputs given such paths do not exist in the package's launch folder.
Such plagues me when invoking ros2 launch
from a freshly spawned shell in my busy ~/
directory.
Perhaps add args such as --package, -p
, --launchfile, -l
, --file, -f
to hint the autocomplete?
ros2 launch -p foo<TAB TAB>
should enumerate only packages that start with the string foo
ros2 launch -f foo<TAB TAB>
should autocomplete file paths that start with the string foo
ros2 launch -p foo -l bar<TAB TAB>
should autocomplete launch file names that exist in package foo
that start with the string bar
Perhaps keep the existing positional package+launchfile args, and introduce a --file, -f
arg when wishing to autocomplete general file paths?
Allow launch_ros.actions.ComposableNodeContainer
to contain launch_ros.actions.LifecycleNode
objects which are also registered as Composable Nodes. It is possible to register an rclcpp::LifecycleNode
as a Comoposable node (see example here) but it is not possible in launch_ros
right now to both reference these nodes as LifecycleNode
objects (to allow functions like state transitions) and also combine them into a ComposableNodeContainer
.
Currently ros2 launch
autocompletes with the list of all packages.
We could provide a way to register launchfiles to the index in CMake and use that resource to autocomplete only packages that have launchfiles, and only files that are launch files.
For modularity the user might want to split a launch file into separate files which are started independently. One example would be a base_launch.py
file which gets started once and a dev_launch.py
file which contains stuff which the developer iterates on which is being started / stopped repeatedly.
For nodes running in separate processes these launch files can be started independently. What is not supported at the moment is for both launch files to contribute components into the same process. E.g. the second launch file want to inject a component into a process which was started by the first launch file.
The goal would be to support this use case within launch.
Required Info:
Save the following as test.launch.xml
<?xml version="1.0"?>
<launch>
<node pkg="demo_nodes_cpp" exec="talker" output="screen" args=" ">
</node>
</launch>
Run it with
ros2 launch --debug test.launch.xml
Node would successfully launch using whatever name is its default.
An exception is raised.
$ ros2 launch --debug tf2_ros_py/src/mypkg/launch/test.launch.xml
[DEBUG] [launch.launch_context]: emitting event synchronously: 'launch.events.IncludeLaunchDescription'
[DEBUG] [launch.launch_context]: emitting event synchronously: 'launch.events.IncludeLaunchDescription'
[INFO] [launch]: All log files can be found below /home/sloretz/.ros/log/2019-08-29-14-21-16-169948-87cee88a3136-25020
[INFO] [launch]: Default logging verbosity is set to DEBUG
[DEBUG] [launch]: processing event: '<launch.events.include_launch_description.IncludeLaunchDescription object at 0x7f768f3ae4e0>'
[DEBUG] [launch]: processing event: '<launch.events.include_launch_description.IncludeLaunchDescription object at 0x7f768f3ae4e0>' ✓ '<launch.event_handlers.on_include_launch_description.OnIncludeLaunchDescription object at 0x7f7684a17358>'
[DEBUG] [launch]: processing event: '<launch.events.include_launch_description.IncludeLaunchDescription object at 0x7f768032ddd8>'
[DEBUG] [launch]: processing event: '<launch.events.include_launch_description.IncludeLaunchDescription object at 0x7f768032ddd8>' ✓ '<launch.event_handlers.on_include_launch_description.OnIncludeLaunchDescription object at 0x7f7684a17358>'
[DEBUG] [launch]: Traceback (most recent call last):
File "/home/sloretz/workspaces/ros2-dev0/build/launch/launch/launch_description_sources/any_launch_file_utilities.py", line 53, in get_launch_description_from_any_launch_file
return loader(launch_file_path)
File "/home/sloretz/workspaces/ros2-dev0/build/launch/launch/launch_description_sources/frontend_launch_file_utilities.py", line 35, in get_launch_description_from_frontend_launch_file
return parser.parse_description(root_entity)
File "/home/sloretz/workspaces/ros2-dev0/build/launch/launch/frontend/parser.py", line 102, in parse_description
actions = [self.parse_action(child) for child in entity.children]
File "/home/sloretz/workspaces/ros2-dev0/build/launch/launch/frontend/parser.py", line 102, in <listcomp>
actions = [self.parse_action(child) for child in entity.children]
File "/home/sloretz/workspaces/ros2-dev0/build/launch/launch/frontend/parser.py", line 85, in parse_action
return instantiate_action(entity, self)
File "/home/sloretz/workspaces/ros2-dev0/build/launch/launch/frontend/expose.py", line 38, in instantiate_action
action_type, kwargs = action_parse_methods[entity.type_name](entity, parser)
File "/home/sloretz/workspaces/ros2-dev0/build/launch_ros/launch_ros/actions/node.py", line 234, in parse
kwargs['node_name'] = kwargs['name']
KeyError: 'name'
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "/home/sloretz/workspaces/ros2-dev0/build/launch/launch/launch_service.py", line 377, in run_async
await process_one_event_task
File "/usr/lib/python3.6/asyncio/coroutines.py", line 126, in send
return self.gen.send(value)
File "/home/sloretz/workspaces/ros2-dev0/build/launch/launch/launch_service.py", line 287, in _process_one_event
await self.__process_event(next_event)
File "/usr/lib/python3.6/asyncio/coroutines.py", line 110, in __next__
return self.gen.send(None)
File "/home/sloretz/workspaces/ros2-dev0/build/launch/launch/launch_service.py", line 307, in __process_event
visit_all_entities_and_collect_futures(entity, self.__context))
File "/home/sloretz/workspaces/ros2-dev0/build/launch/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 "/home/sloretz/workspaces/ros2-dev0/build/launch/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 "/home/sloretz/workspaces/ros2-dev0/build/launch/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 "/home/sloretz/workspaces/ros2-dev0/build/launch/launch/action.py", line 112, in visit
return self.execute(context)
File "/home/sloretz/workspaces/ros2-dev0/build/launch/launch/actions/include_launch_description.py", line 128, in execute
launch_description = self.__launch_description_source.get_launch_description(context)
File "/home/sloretz/workspaces/ros2-dev0/build/launch/launch/launch_description_source.py", line 84, in get_launch_description
self._get_launch_description(self.__expanded_location)
File "/home/sloretz/workspaces/ros2-dev0/build/launch/launch/launch_description_sources/any_launch_description_source.py", line 53, in _get_launch_description
return get_launch_description_from_any_launch_file(location)
File "/home/sloretz/workspaces/ros2-dev0/build/launch/launch/launch_description_sources/any_launch_file_utilities.py", line 56, in get_launch_description_from_any_launch_file
raise InvalidLaunchFileError(extension, likely_errors=exceptions)
launch.invalid_launch_file_error.InvalidLaunchFileError: Caught exception when trying to load file of format [{}]:
[ERROR] [launch]: Caught exception in launch (see debug for traceback): Caught exception when trying to load file of format [{}]:
[DEBUG] [launch.launch_context]: emitting event: 'launch.events.Shutdown'
[DEBUG] [launch]: processing event: '<launch.events.shutdown.Shutdown object at 0x7f768032d5c0>'
[DEBUG] [launch]: processing event: '<launch.events.shutdown.Shutdown object at 0x7f768032d5c0>' ✓ '<launch.event_handlers.on_shutdown.OnShutdown object at 0x7f767c0ecba8>'
^C[WARNING] [launch]: user interrupted with ctrl-c (SIGINT)
[DEBUG] [launch]: processing event: '<launch.events.shutdown.Shutdown object at 0x7f768032d5c0>' ✓ '<launch.event_handlers.on_shutdown.OnShutdown object at 0x7f768f41e4a8>'
Executing <Task finished coro=<LaunchService._process_one_event() done, defined at /home/sloretz/workspaces/ros2-dev0/build/launch/launch/launch_service.py:285> result=None created at /home/sloretz/workspaces/ros2-dev0/build/launch/launch/launch_service.py:355> took 0.998 seconds
The launch file works fine if I add a name
attribute.
<?xml version="1.0"?>
<launch>
<node pkg="demo_nodes_cpp" exec="talker" output="screen" name="demo_talker" args=" ">
</node>
</launch>
Similar to #53, but with a different attribute on the node
tag.
Required Info:
Using a launch file like:
import launch
import launch_ros.actions
#Argument names
NODE_NAME = "node_name"
OUTPUT = "output"
def generate_launch_description():
ld = launch.LaunchDescription([
launch.actions.DeclareLaunchArgument(
NODE_NAME,
default_value="node_name",
),
launch.actions.DeclareLaunchArgument(
OUTPUT,
default_value="screen"
),
])
node = launch_ros.actions.Node(
package="package_name",
node_executable="executable_name",
output=launch.substitutions.LaunchConfiguration(OUTPUT),
node_name=launch.substitutions.LaunchConfiguration(NODE_NAME),
)
ld.add_action(node)
return ld
Then run:
ros2 launch example_launch.py
I would expect the output to default to 'screen', but be settable from the cli with output:=log
or from another launch file that includes this one.
Launch fails with message
[ERROR] [launch]: Caught exception in launch (see debug for traceback): <launch.substitutions.launch_configuration.LaunchConfiguration object at 0x7f5f04242ac8> is not a valid output configuration
I have two ComposableNode in launch.py and execute them in a single process. However, there is not any performance improvement compared with running them in two processes respectively, in terms of latency and throughput(measured by ros2 topic delay
and ros2 topic hz
). Is it expected?
After the observation, I would like to make them run in a single process with memory zero-copy. As far as I know, it can be set by intra_process_comm of rclcpp::NodeOptions, but I don't know how to make it in launch file. Anyone can help?
Required Info:
Example launch test file:
import sys
import unittest
from launch import LaunchDescription
from launch.actions import OpaqueFunction
from launch_ros.actions import ComposableNodeContainer
from launch_ros.descriptions import ComposableNode
import pytest
@pytest.mark.rostest
def generate_test_description(ready_fn):
return LaunchDescription([
ComposableNodeContainer(
package='rclcpp_components',
node_executable='component_container',
node_name='my_node_container',
node_namespace='',
composable_node_descriptions=[
ComposableNode(
package='composition',
node_plugin='composition::Talker'
)
],
output='screen'
),
OpaqueFunction(function=lambda context: ready_fn()),
])
class TestMyNode(unittest.TestCase):
def test_something(self):
print('We do not see this message', file=sys.stderr)
The tests runs and passes without errors.
Different runtime errors depending how you run the test.
launch_test
[INFO] [launch]: All log files can be found below /home/jacob/.ros/log/2019-12-16-17-23-38-303988-warner-30644 [INFO] [launch]: Default logging verbosity is set to INFO test_something (example_launch_test.TestMyNode) ... We do not see this message ok
Ran 1 test in 0.000s
OK
[INFO] [component_container-1]: process started with pid [30649]
[ERROR] [launch]: Caught exception in launch (see debug for traceback): context.locals does not contain attribute 'launch_ros_node', it contains: [event]
[INFO] [component_container-1]: sending signal 'SIGINT' to process[component_container-1]
[ERROR] [component_container-1]: process has died [pid 30649, exit code -2, cmd '/opt/ros/eloquent/lib/rclcpp_components/component_container --ros-args -r __node:=my_node_container'].
Ran 0 tests in 0.000s
OK
ros2 test
[INFO] [launch]: All log files can be found below /home/jacob/.ros/log/2019-12-16-17-21-29-265673-warner-30543 [INFO] [launch]: Default logging verbosity is set to INFO test_something (example_launch_test.TestMyNode) ... We do not see this message ok
Ran 1 test in 0.000s
OK
Task exception was never retrieved
future: <Task finished coro=<LaunchService._process_one_event() done, defined at /opt/ros/eloquent/lib/python3.6/site-packages/launch/launch_service.py:289> exception=RuntimeError('Signal event received before subprocess transport available.',)>
Traceback (most recent call last):
File "/opt/ros/eloquent/lib/python3.6/site-packages/launch/launch_service.py", line 291, in _process_one_event
await self.__process_event(next_event)
File "/opt/ros/eloquent/lib/python3.6/site-packages/launch/launch_service.py", line 311, in __process_event
visit_all_entities_and_collect_futures(entity, self.__context))
File "/opt/ros/eloquent/lib/python3.6/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/eloquent/lib/python3.6/site-packages/launch/action.py", line 108, in visit
return self.execute(context)
File "/opt/ros/eloquent/lib/python3.6/site-packages/launch/actions/opaque_function.py", line 75, in execute
return self.__function(context, *self.__args, **self.__kwargs)
File "/opt/ros/eloquent/lib/python3.6/site-packages/launch/actions/execute_process.py", line 383, in __on_signal_process_event
raise RuntimeError('Signal event received before subprocess transport available.')
RuntimeError: Signal event received before subprocess transport available.
[INFO] [component_container-1]: process started with pid [30555]
Task exception was never retrieved
future: <Task finished coro=<LaunchService._process_one_event() done, defined at /opt/ros/eloquent/lib/python3.6/site-packages/launch/launch_service.py:289> exception=InvalidHandle('Tried to use a handle that has been destroyed.',)>
Traceback (most recent call last):
File "/opt/ros/eloquent/lib/python3.6/site-packages/launch/launch_service.py", line 291, in _process_one_event
await self.__process_event(next_event)
File "/opt/ros/eloquent/lib/python3.6/site-packages/launch/launch_service.py", line 311, in __process_event
visit_all_entities_and_collect_futures(entity, self.__context))
File "/opt/ros/eloquent/lib/python3.6/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/eloquent/lib/python3.6/site-packages/launch/action.py", line 108, in visit
return self.execute(context)
File "/opt/ros/eloquent/lib/python3.6/site-packages/launch_ros/actions/load_composable_nodes.py", line 167, in execute
self.__target_container.node_name
File "/opt/ros/eloquent/lib/python3.6/site-packages/rclpy/node.py", line 1201, in create_client
with self.handle as node_capsule:
File "/opt/ros/eloquent/lib/python3.6/site-packages/rclpy/handle.py", line 150, in enter
return self._get_capsule()
File "/opt/ros/eloquent/lib/python3.6/site-packages/rclpy/handle.py", line 132, in _get_capsule
raise InvalidHandle('Tried to use a handle that has been destroyed.')
rclpy.handle.InvalidHandle: Tried to use a handle that has been destroyed.
[ERROR] [component_container-1]: process[component_container-1] failed to terminate '5' seconds after receiving 'SIGINT', escalating to 'SIGTERM'
[INFO] [component_container-1]: sending signal 'SIGTERM' to process[component_container-1]
[ERROR] [component_container-1]: process has died [pid 30555, exit code -15, cmd '/opt/ros/eloquent/lib/rclcpp_components/component_container --ros-args -r __node:=my_node_container'].
Ran 0 tests in 0.000s
OK
The first error (running with launch_test
) is because launch_ros_node
is not added to the launch context, which LoadComposableNode
action assumes exists:
launch_ros/launch_ros/launch_ros/actions/load_composable_nodes.py
Lines 186 to 190 in c37340d
This is closely related to #97.
I'm not sure about the second error.
In particular, it would be really convenient to be able to start a node container just before some actions to load components into it. Maybe not a bug, but certainly a big usability improvement.
Required Info:
Launch the following launch file:
from launch import LaunchDescription
from launch_ros.actions import ComposableNodeContainer
from launch_ros.actions import LoadComposableNodes
from launch_ros.descriptions import ComposableNode
def generate_launch_description():
return LaunchDescription([
ComposableNodeContainer(
package='rclcpp_components', node_executable='component_container',
node_name='my_container', node_namespace=''
),
LoadComposableNodes(
composable_node_descriptions=[
ComposableNode(
package='composition',
node_plugin='composition::Talker'
)
],
target_container='my_container'
),
])
A container node starts and the composition talker component is loaded.
The container does not start and so the LoadComposableNodes
I understand that the LoadComposableNodes
action is blocking until the service is available. But my intuition says that since the ComposableNodeContainer action appears first, it should be started before the loading action starts.
After talking with @wjwwood, it sounds like we should update the implementation of LoadComposableNodes
so it waits for the service in a separate thread so that it doesn't block the asyncio loop.
Required Info:
Given the following modified MinimalTimer
node from ROS2 example package.
#include <chrono>
#include <memory>
#include "rclcpp/rclcpp.hpp"
using namespace std::chrono_literals;
class MinimalTimer : public rclcpp::Node
{
public:
MinimalTimer()
: Node("minimal_timer", "timer_ns")
{
RCLCPP_INFO(this->get_logger(), "The namespace is %s", this->get_namespace());
timer_ = create_wall_timer(
500ms, std::bind(&MinimalTimer::timer_callback, this));
}
private:
void timer_callback()
{
RCLCPP_INFO(this->get_logger(), "Hello, world!");
}
rclcpp::TimerBase::SharedPtr timer_;
};
int main(int argc, char * argv[])
{
rclcpp::init(argc, argv);
rclcpp::spin(std::make_shared<MinimalTimer>());
rclcpp::shutdown();
return 0;
}
and the following timer_launch.py
from launch import LaunchDescription
import launch.actions
import launch.substitutions
import launch_ros.actions
def generate_launch_description():
"""Launch a talker and a listener."""
return LaunchDescription([
launch_ros.actions.Node(
package='examples_rclcpp_minimal_timer',
node_executable='timer_member_function',
output='screen')
])
If launching using timer_launch.py
we expect the node name to be /timer_ns/minimal_timer
as a namespace parameter was passed to the node constructor.
However, it is /minimal_timer
.
Running the executable does yield the expected node name.
I am receiving a misleading/wrong warning when trying to pass arguments to the ros2 launch
command.
Required Info:
Given a launch file with any LaunchArgument
launch_description = LaunchDescription([
DeclareLaunchArgument(
'my_argument',
default_value = 'some_default',
description = 'some description'),
Then launching the launch file as follows:
ros2 launch my_launchfile.launch.py my_argument:=some_custom_value
Launch file started without any complains and launch argument successfully remapped.
[WARN] [rcl]: Found remap rule 'my_argument:=some_custom_value'. This syntax is deprecated. Use '--ros-args --remap my_argument:=some_custom_value' instead.
Similarly, when using the proposed syntax, the substitution does not take place.
Please feel free to transfer to ticket to a more appropriate repo if applicable.
This is a modified version of launch_ros/examples/pub_sub_launch.py
here.
# Copyright 2018 Open Source Robotics Foundation, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Launch a talker and a listener."""
import os
import sys
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..')) # noqa
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..', 'launch')) # noqa
from launch import LaunchDescription
from launch import LaunchIntrospector
from launch import LaunchService
from launch_ros import get_default_launch_description
import launch_ros.actions
def main(argv=sys.argv[1:]):
"""Main."""
ld = LaunchDescription([
launch_ros.actions.Node(
package='demo_nodes_cpp', node_executable='talker', output='screen',
node_name='my_node',
parameters=[{'a': 1, 'b': 'a', 'c': ['list', 'of', 'stuff']}],
remappings=[('chatter', 'my_chatter')]),
launch_ros.actions.Node(
package='demo_nodes_cpp', node_executable='listener', output='screen',
remappings=[('chatter', 'my_chatter')]),
])
print('Starting introspection of launch description...')
print('')
print(LaunchIntrospector().format_launch_description(ld))
print('')
print('Starting launch of launch description...')
print('')
# ls = LaunchService(debug=True)
ls = LaunchService()
ls.include_launch_description(get_default_launch_description())
ls.include_launch_description(ld)
return ls.run()
if __name__ == '__main__':
main()
I only added:
node_name='my_node',
parameters=[{'a': 1, 'b': 'a', 'c': ['list', 'of', 'stuff']}],
It should work as the original example.
It fails when loading parameters.
Starting introspection of launch description...
<launch.launch_description.LaunchDescription object at 0x7fe2636a25c0>
├── ExecuteProcess(cmd=[ExecInPkg(pkg='demo_nodes_cpp', exec='talker'), LocalVar('node name'), LocalVar('parameter 1'), LocalVar('remapping 1')], cwd=None, env=None, shell=False)
└── ExecuteProcess(cmd=[ExecInPkg(pkg='demo_nodes_cpp', exec='listener'), LocalVar('remapping 1')], cwd=None, env=None, shell=False)
Starting launch of launch description...
[INFO] [launch]: All log files can be found below /home/ivanpauno/.ros/log/2019-04-11-15-07-37-242126-71c1584df2a2-4887
[INFO] [launch]: Default logging verbosity is set to INFO
[INFO] [talker-1]: process started with pid [4896]
[INFO] [listener-2]: process started with pid [4897]
[talker-1] terminate called after throwing an instance of 'std::runtime_error'
[talker-1] what(): Failed to parse parameters from file '/tmp/launch_params_au9d6q_k': Sequence should be of same type. Value type 'bool' do not belong at line_num 8, at /home/ivanpauno/ros2_ws/src/ros2/rcl/rcl_yaml_param_parser/src/parser.c:891
[ERROR] [talker-1]: process has died [pid 4896, exit code -6, cmd '/home/ivanpauno/ros2_ws/install/demo_nodes_cpp/lib/demo_nodes_cpp/talker __node:=my_node __params:=/tmp/launch_params_au9d6q_k chatter:=my_chatter'].
Content of /tmp/launch_params_au9d6q_k
ivanpauno@71c1584df2a2:~/ros2_ws/src/ros2/launch_ros/launch_ros/examples$ cat /tmp/launch_params_au9d6q_k
/:
my_node:
ros__parameters:
a: 1
b: a
c: !!python/tuple
- list
- of
- stuff
If I change the parameter arg to:
parameters=[{'a': 1, 'b': 'a', 'c': ['a', 'b', 'c']}],
It works. Generated parameters file:
/:
my_node:
ros__parameters:
a: 1
b: a
c: !!python/tuple
- a
- b
- c
Numbers inside the 'c' list also work well, like ['10', '1', '2'].
As far as I've seen, the problem appears when one of the parameters is a list of strings, and one of the list items is more that one character long.
Required Info:
import launch_ros
help(launch_ros.actions.LifecycleNode)
Or use an IDE that shows type annotation information and view the annotation info for the LifecycleNode
class.
Help will show useful information about the required arguments to the LifecycleNode
constructor (as well as other related constructions like Node, ExecuteProcess, Action)
Only the information for the node_name
argument and an opaque **kwargs
argument is given, as well as an unapologetic caveat.
class LifecycleNode(launch_ros.actions.node.Node)
| Action that executes a ROS lifecycle node.
|
| Method resolution order:
| LifecycleNode
| launch_ros.actions.node.Node
| launch.actions.execute_process.ExecuteProcess
| launch.action.Action
| launch.launch_description_entity.LaunchDescriptionEntity
| builtins.object
|
| Methods defined here:
|
| __init__(self, *, node_name:str, **kwargs) -> None
| Construct a LifecycleNode action.
|
| Almost all of the arguments are passed to :class:`Node` and eventually
| to :class:`launch.actions.ExecuteProcess`, so see the documentation of
| those classes for additional details.
|
| This action additionally emits some event(s) in certain circumstances:
|
| - :class:`launch.events.lifecycle.StateTransition`:
|
| - this event is emitted when a message is published to the
| "/<node_name>/transition_event" topic, indicating the lifecycle
| node represented by this action changed state
|
| This action also handles some events related to lifecycle:
|
| - :class:`launch.events.lifecycle.ChangeState`
|
| - this event can be targeted to a single lifecycle node, or more than
| one, or even all lifecycle nodes, and it requests the targeted nodes
| to change state, see its documentation for more details.
|
This could be fixed with the either use of the merge_args
library or the metaprogramming techniques described by its author here: https://chriswarrick.com/blog/2018/09/20/python-hackery-merging-signatures-of-two-python-functions/
The launch-prefix
option comes handy when debugging/profiling, however it is not 'easily' accessible as of now, I am referring to this example. This scheme offers a fine granularity as which node should benefit what prefix, however sometimes one may simply want to launch an entire launch file under a given prefix.
Given that the run
verb provides a --prefix
option, the launch
verb could similarly provide a --launch-prefix
option.
One possible implementation boils down to including the desired launch file in a LaunchDescription
which sets the launch configuration appropriately, something similar to this script.
I am trying to write launch file for python package, and for some reason I am not able to build the package correctly to use with the launch file. So I tried the tutorial, and I am seeing the same issue. I don't know if I am missing something, or just doing it incorrectly.
This might be related to build, and maybe i should open a ticket there?
Required Info:
ros2 launch ros2launch ros2launch example.launch.py
ros2 launch launch_ros pub_sub_launch.py
Launches example
file 'example.launch.py' was not found in the share directory of package 'ros2launch' which is at '/home/yathartha/Desktop/test/install/ros2launch/share/ros2launch'
And similarly:
file 'pub_sub_launch.py' was not found in the share directory of package 'launch_ros' which is at '/home/yathartha/Desktop/test/install/launch_ros/share/launch_ros'
A means of setting node_name
overrides for a multi-node rclpy process such as that given in ros2/launch#204.
This also brings into question the naming of the class launch_ros.actions.Node
since it currently can launch a multi-node process. Perhaps that should fail on trying to launch a multi-node process and for that, use a launch_ros.actions.MultiNode
action.
Note: This is a different problem to the composable node launch. Here the nodes are brought into the same process via ... the process, not the launcher. I suspect the same situation would arise linking two c++ classes with nodes and instantiating them in the same application.
Required Info:
Specifically, I've noticed that colons (:
) and tab characters are not working.
I ran into this issue trying to pass a URDF from xacro to robot_state_publisher, like the example here. For some reason, if the URDF/xacro file contains a colon (:
) or a tab character inside of an XML comment, then launch fails.
Here's my example URDF file foo.urdf
:
<?xml version="1.0" ?>
<robot>
<!--: -->
</robot>
And my launch file:
<launch>
<node pkg="demo_nodes_cpp" exec="talker" output="screen">
<param name="foo" value="$(command 'cat foo.urdf')" />
</node>
</launch>
The launch file succeeds and we see the talker node chatting.
Error:
[ERROR] [launch]: Caught exception in launch (see debug for traceback): mapping values are not allowed here
in "<unicode string>", line 3, column 7:
<!--: -->
^
The error seems to specifically occur if there is a colon followed by a space. E.g. making the following change works fine:
- <!--: -->
+ <!--:-->
Replacing the colcon with a tab character also triggers the same error:
[ERROR] [launch]: Caught exception in launch (see debug for traceback): while scanning for the next token
found character '\t' that cannot start any token
in "<unicode string>", line 3, column 7:
<!-- -->
^
Required Info:
Pass the yaml file to launch.py, but it doesn't take effect.
import os
import launch
from launch_ros.actions import ComposableNodeContainer
from launch_ros.descriptions import ComposableNode
def generate_launch_description():
param_file_path = /my/file/path
container = ComposableNodeContainer(
node_name='my_container',
node_namespace='',
package='rclcpp_components',
node_executable='component_container',
composable_node_descriptions=[
ComposableNode(
package='my_package',
node_plugin='namespace::Class',
node_name='my_node',
parameters=[param_file_path],),
],
output='screen',
)
return launch.LaunchDescription([container])
my_node:
ros__parameters:
enabled_param1: true
enabled_param2: true
In trying to use launch_ros
with composable nodes, I got a hard crash trying to process a parameter file. The syntax that it choked on is not even used by my composable node, but it looks like to_parameters_list
crashed the entire launch file nonetheless.
Debug output of ros2 launch
with crash trace:
[DEBUG] [launch.launch_context]: emitting event synchronously: 'launch.events.process.ProcessStderr'
[DEBUG] [launch]: processing event: '<launch.events.process.process_started.ProcessStarted object at 0x7f391403b668>'
[DEBUG] [launch]: processing event: '<launch.events.process.process_started.ProcessStarted object at 0x7f391403b668>' ✓ '<launch.event_handlers.on_process_start.OnProcessStart object at 0x7f391410a748>'
/opt/ros/master/install/lib/python3.6/site-packages/launch_ros/utilities/to_parameters_list.py:49: YAMLLoadWarning: calling yaml.load() without Loader=... is deprecated, as the default Loader is unsafe. Please read https://msg.pyyaml.org/load for full details.
context, normalize_parameter_dict(yaml.load(f))
Executing <Handle <TaskWakeupMethWrapper object at 0x7f39140b23a8>(<Future finis...events.py:295>) created at /usr/lib/python3.6/asyncio/queues.py:74> took 1.263 seconds
[DEBUG] [launch.launch_context]: emitting event synchronously: 'launch.events.process.ProcessStdout'
[DEBUG] [launch.launch_context]: emitting event synchronously: 'launch.events.process.ProcessStderr'
[DEBUG] [launch.launch_context]: emitting event synchronously: 'launch.events.process.ProcessStdout'
[DEBUG] [launch]: Traceback (most recent call last):
File "/opt/ros/master/install/lib/python3.6/site-packages/launch/launch_service.py", line 350, in run
self.__loop_from_run_thread.run_until_complete(run_loop_task)
File "/usr/lib/python3.6/asyncio/base_events.py", line 484, in run_until_complete
return future.result()
File "/opt/ros/master/install/lib/python3.6/site-packages/launch/launch_service.py", line 240, in __run_loop
await process_one_event_task
File "/usr/lib/python3.6/asyncio/coroutines.py", line 126, in send
return self.gen.send(value)
File "/opt/ros/master/install/lib/python3.6/site-packages/launch/launch_service.py", line 177, in _process_one_event
await self.__process_event(next_event)
File "/usr/lib/python3.6/asyncio/coroutines.py", line 110, in __next__
return self.gen.send(None)
File "/opt/ros/master/install/lib/python3.6/site-packages/launch/launch_service.py", line 197, in __process_event
visit_all_entities_and_collect_futures(entity, self.__context))
File "/opt/ros/master/install/lib/python3.6/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/master/install/lib/python3.6/site-packages/launch/action.py", line 60, in visit
return cast(Optional[List[LaunchDescriptionEntity]], self.execute(context))
File "/opt/ros/master/install/lib/python3.6/site-packages/launch_ros/actions/load_composable_nodes.py", line 171, in execute
self._load_in_sequence(self.__composable_node_descriptions, context)
File "/opt/ros/master/install/lib/python3.6/site-packages/launch_ros/actions/load_composable_nodes.py", line 151, in _load_in_sequence
self._load_node(next_composable_node_description, context)
File "/opt/ros/master/install/lib/python3.6/site-packages/launch_ros/actions/load_composable_nodes.py", line 114, in _load_node
context, composable_node_description.parameters
File "/opt/ros/master/install/lib/python3.6/site-packages/launch_ros/utilities/to_parameters_list.py", line 60, in to_parameters_list
raise RuntimeError('invalid parameter value {}'.format(repr(value)))
RuntimeError: invalid parameter value (-7, -34, -47)
Relevant part of parameter file hardware.yaml
:
bno055_driver:
ros__parameters:
port: /dev/imu
frame_id: imu_link
frequency: 30.0
self_manage: true
use_magnetometer: false
angular_velocity_stdev: 0.01
linear_acceleration_stdev: 0.0015
magnetic_field_stdev: 0.0000005
orientation_stdev: 0.000001
# these calibration values need to be determined on a per-device basis.
calibration/accelerometer_offset: [-7, -34, -47]
calibration/magnetometer_offset: [275, -193, -104]
calibration/gyroscope_offset: [-3, -3, -1]
calibration/accelerometer_radius: 1000
calibration/magnetometer_radius: 842
Relevant part of launch file hardware.launch.py
:
from pathlib import Path
from ament_index_python.packages import get_package_share_directory
from launch import LaunchDescription
from launch_ros.actions import Node, ComposableNodeContainer
from launch_ros.descriptions import ComposableNode
def generate_launch_description():
hardware_config = Path(get_package_share_directory('openrover_demo'), 'config', 'hardware.yaml')
assert hardware_config.is_file()
return LaunchDescription([
ComposableNodeContainer(
node_name='container',
node_namespace='openrover',
package='rclcpp_components',
node_executable='component_container',
composable_node_descriptions=[
ComposableNode(package='openrover_core', node_plugin="openrover::Rover",
node_namespace='/openrover',
node_name='driver', parameters=[hardware_config]),
ComposableNode(package='openrover_core', node_plugin="openrover::RoverSerial",
node_namespace='/openrover',
node_name='serial', parameters=[hardware_config]),
]),
Node(
package='bno055_driver',
node_executable='bno055_driver',
output='screen',
parameters=[hardware_config]
)
])
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.