Skip to content

Commit 0fd4c45

Browse files
committed
Streamline Python launchfiles to promote best practices (ros2#5413)
Signed-off-by: Emerson Knapp <[email protected]>
1 parent a89fe9e commit 0fd4c45

18 files changed

+255
-379
lines changed
Lines changed: 73 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -1,96 +1,84 @@
1-
import os
2-
3-
from ament_index_python import get_package_share_directory
4-
51
from launch import LaunchDescription
6-
from launch.actions import DeclareLaunchArgument
7-
from launch.actions import GroupAction
8-
from launch.actions import IncludeLaunchDescription
9-
from launch.launch_description_sources import PythonLaunchDescriptionSource
10-
from launch.substitutions import LaunchConfiguration
11-
from launch.substitutions import TextSubstitution
12-
from launch_ros.actions import Node
13-
from launch_ros.actions import PushRosNamespace
2+
from launch.actions import DeclareLaunchArgument, GroupAction, IncludeLaunchDescription
3+
from launch.substitutions import LaunchConfiguration, PathJoinSubstitution
4+
from launch_ros.actions import Node, PushROSNamespace
5+
from launch_ros.substitutions import FindPackageShare
146

157

168
def generate_launch_description():
9+
launch_dir = PathJoinSubstitution([FindPackageShare('demo_nodes_cpp'), 'launch', 'topics'])
10+
return LaunchDescription([
11+
# args that can be set from the command line or a default will be used
12+
DeclareLaunchArgument('background_r', default_value='0'),
13+
DeclareLaunchArgument('background_g', default_value='255'),
14+
DeclareLaunchArgument('background_b', default_value='0'),
15+
DeclareLaunchArgument('chatter_ns', default_value='my/chatter/ns'),
1716

18-
# args that can be set from the command line or a default will be used
19-
background_r_launch_arg = DeclareLaunchArgument(
20-
'background_r', default_value=TextSubstitution(text='0')
21-
)
22-
background_g_launch_arg = DeclareLaunchArgument(
23-
'background_g', default_value=TextSubstitution(text='255')
24-
)
25-
background_b_launch_arg = DeclareLaunchArgument(
26-
'background_b', default_value=TextSubstitution(text='0')
27-
)
28-
chatter_ns_launch_arg = DeclareLaunchArgument(
29-
'chatter_ns', default_value=TextSubstitution(text='my/chatter/ns')
30-
)
17+
# include another launch file
18+
IncludeLaunchDescription(
19+
PathJoinSubstitution([launch_dir, 'talker_listener.launch.py'])
20+
),
3121

32-
# include another launch file
33-
launch_include = IncludeLaunchDescription(
34-
PythonLaunchDescriptionSource(
35-
os.path.join(
36-
get_package_share_directory('demo_nodes_cpp'),
37-
'launch/topics/talker_listener.launch.py'))
38-
)
39-
# include another launch file in the chatter_ns namespace
40-
launch_include_with_namespace = GroupAction(
41-
actions=[
42-
# push_ros_namespace to set namespace of included nodes
43-
PushRosNamespace('chatter_ns'),
44-
IncludeLaunchDescription(
45-
PythonLaunchDescriptionSource(
46-
os.path.join(
47-
get_package_share_directory('demo_nodes_cpp'),
48-
'launch/topics/talker_listener.launch.py'))
49-
),
50-
]
51-
)
22+
# include a Python launch file in the chatter_py_ns namespace
23+
GroupAction(
24+
actions=[
25+
# push_ros_namespace first to set namespace of included nodes for following actions
26+
PushROSNamespace(LaunchConfiguration('chatter_ns')),
27+
IncludeLaunchDescription(
28+
PathJoinSubstitution([launch_dir, 'talker_listener_launch.py'])),
29+
]
30+
),
5231

53-
# start a turtlesim_node in the turtlesim1 namespace
54-
turtlesim_node = Node(
55-
package='turtlesim',
56-
namespace='turtlesim1',
57-
executable='turtlesim_node',
58-
name='sim'
59-
)
32+
# include a xml launch file in the chatter_xml_ns namespace
33+
GroupAction(
34+
actions=[
35+
# push_ros_namespace first to set namespace of included nodes for following actions
36+
PushROSNamespace('chatter_xml_ns'),
37+
IncludeLaunchDescription(
38+
PathJoinSubstitution([launch_dir, 'talker_listener_launch.xml'])),
39+
]
40+
),
6041

61-
# start another turtlesim_node in the turtlesim2 namespace
62-
# and use args to set parameters
63-
turtlesim_node_with_parameters = Node(
64-
package='turtlesim',
65-
namespace='turtlesim2',
66-
executable='turtlesim_node',
67-
name='sim',
68-
parameters=[{
69-
'background_r': LaunchConfiguration('background_r'),
70-
'background_g': LaunchConfiguration('background_g'),
71-
'background_b': LaunchConfiguration('background_b'),
72-
}]
73-
)
42+
# include a yaml launch file in the chatter_yaml_ns namespace
43+
GroupAction(
44+
actions=[
45+
# push_ros_namespace first to set namespace of included nodes for following actions
46+
PushROSNamespace('chatter_yaml_ns'),
47+
IncludeLaunchDescription(
48+
PathJoinSubstitution([launch_dir, 'talker_listener_launch.yaml'])),
49+
]
50+
),
7451

75-
# perform remap so both turtles listen to the same command topic
76-
forward_turtlesim_commands_to_second_turtlesim_node = Node(
77-
package='turtlesim',
78-
executable='mimic',
79-
name='mimic',
80-
remappings=[
81-
('/input/pose', '/turtlesim1/turtle1/pose'),
82-
('/output/cmd_vel', '/turtlesim2/turtle1/cmd_vel'),
83-
]
84-
)
52+
# start a turtlesim_node in the turtlesim1 namespace
53+
Node(
54+
package='turtlesim',
55+
namespace='turtlesim1',
56+
executable='turtlesim_node',
57+
name='sim'
58+
),
8559

86-
return LaunchDescription([
87-
background_r_launch_arg,
88-
background_g_launch_arg,
89-
background_b_launch_arg,
90-
chatter_ns_launch_arg,
91-
launch_include,
92-
launch_include_with_namespace,
93-
turtlesim_node,
94-
turtlesim_node_with_parameters,
95-
forward_turtlesim_commands_to_second_turtlesim_node,
60+
# start another turtlesim_node in the turtlesim2 namespace
61+
# and use args to set parameters
62+
Node(
63+
package='turtlesim',
64+
namespace='turtlesim2',
65+
executable='turtlesim_node',
66+
name='sim',
67+
parameters=[{
68+
'background_r': LaunchConfiguration('background_r'),
69+
'background_g': LaunchConfiguration('background_g'),
70+
'background_b': LaunchConfiguration('background_b'),
71+
}]
72+
),
73+
74+
# perform remap so both turtles listen to the same command topic
75+
Node(
76+
package='turtlesim',
77+
executable='mimic',
78+
name='mimic',
79+
remappings=[
80+
('/input/pose', '/turtlesim1/turtle1/pose'),
81+
('/output/cmd_vel', '/turtlesim2/turtle1/cmd_vel'),
82+
]
83+
),
9684
])

source/Tutorials/Intermediate/Launch/Using-ROS2-Launch-For-Large-Projects.rst

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ Now let's create a new ``turtlesim_world_3_launch.py`` file similar to ``turtles
125125

126126
.. literalinclude:: launch/turtlesim_world_3_launch.py
127127
:language: python
128-
:emphasize-lines: 19
128+
:emphasize-lines: 12
129129

130130
Loading the same YAML file, however, will not affect the appearance of the third turtlesim world.
131131
The reason is that its parameters are stored under another namespace as shown below:
@@ -167,29 +167,25 @@ However, if the launch file contains a large number of nodes, defining namespace
167167
To solve that issue, the ``PushRosNamespace`` action can be used to define the global namespace for each launch file description.
168168
Every nested node will inherit that namespace automatically.
169169

170-
To do that, firstly, we need to remove the ``namespace='turtlesim2'`` line from the ``turtlesim_world_2.launch.py`` file.
171-
Afterwards, we need to update the ``launch_turtlesim.launch.py`` to include the following lines:
170+
.. attention:: ``PushROSNamespace`` has to be the first action in the list for the following actions to apply the namespace.
171+
172+
To do that, firstly, we need to remove the ``namespace='turtlesim2'`` line from the ``turtlesim_world_2_launch.py`` file.
173+
Afterwards, we need to update the ``launch_turtlesim_launch.py`` to change the ``IncludeLaunchDescription(... 'turtlesim_world_2_launch.py' ...)`` value to the following:
172174

173175
.. code-block:: Python
174176
175177
from launch.actions import GroupAction
176178
from launch_ros.actions import PushRosNamespace
177179
178180
...
179-
turtlesim_world_2 = IncludeLaunchDescription(
180-
PythonLaunchDescriptionSource([os.path.join(
181-
get_package_share_directory('launch_tutorial'), 'launch'),
182-
'/turtlesim_world_2.launch.py'])
183-
)
184-
turtlesim_world_2_with_namespace = GroupAction(
181+
GroupAction(
185182
actions=[
186-
PushRosNamespace('turtlesim2'),
187-
turtlesim_world_2,
183+
PushROSNamespace('turtlesim2'),
184+
IncludeLaunchDescription(PathJoinSubstitution([launch_dir, 'turtlesim_world_2.launch.py'])),
188185
]
189-
)
186+
),
190187
191-
Finally, we replace the ``turtlesim_world_2`` to ``turtlesim_world_2_with_namespace`` in the ``return LaunchDescription`` statement.
192-
As a result, each node in the ``turtlesim_world_2.launch.py`` launch description will have a ``turtlesim2`` namespace.
188+
As a result, each node in the ``turtlesim_world_2_launch.py`` launch description will have a ``turtlesim2`` namespace.
193189

194190
4 Reusing nodes
195191
^^^^^^^^^^^^^^^
@@ -215,7 +211,7 @@ In addition to that, we have passed it ``target_frame`` launch argument as shown
215211

216212
.. literalinclude:: launch/launch_turtlesim_launch.py
217213
:language: python
218-
:lines: 21-26
214+
:lines: 16-19
219215

220216
This syntax allows us to change the default goal target frame to ``carrot1``.
221217
If you would like ``turtle2`` to follow ``turtle1`` instead of the ``carrot1``, just remove the line that defines ``launch_arguments``.

source/Tutorials/Intermediate/Launch/Using-Substitutions.rst

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -185,14 +185,13 @@ To do this, create following file in the ``launch`` folder of the ``launch_tutor
185185

186186
.. literalinclude:: launch/example_main_launch.py
187187
:language: python
188-
:lines: 16-20
188+
:lines: 14-18
189189

190190
The ``launch_arguments`` dictionary with ``turtlesim_ns`` and ``use_provided_red`` arguments is passed to the ``IncludeLaunchDescription`` action.
191-
The ``TextSubstitution`` substitution is used to define the ``new_background_r`` argument with the value of the ``background_r`` key in the ``colors`` dictionary.
192191

193192
.. literalinclude:: launch/example_main_launch.py
194193
:language: python
195-
:lines: 22-26
194+
:lines: 19-23
196195

197196

198197
3 Substitutions example launch file
@@ -290,37 +289,36 @@ Now create the substitution launch file in the same folder:
290289
:language: python
291290

292291
The ``turtlesim_ns``, ``use_provided_red``, and ``new_background_r`` launch configurations are defined.
293-
They are used to store values of launch arguments in the above variables and to pass them to required actions.
292+
They are used to represent values of launch arguments in the above variables and to pass them to required actions.
294293
These ``LaunchConfiguration`` substitutions allow us to acquire the value of the launch argument in any part of the launch description.
295294

296295
``DeclareLaunchArgument`` is used to define the launch argument that can be passed from the above launch file or from the console.
297296

298297
.. literalinclude:: launch/example_substitutions_launch.py
299298
:language: python
300-
:lines: 13-24
299+
:lines: 14-25
301300

302301
The ``turtlesim_node`` node with the ``namespace`` set to ``turtlesim_ns`` ``LaunchConfiguration`` substitution is defined.
303302

304303
.. literalinclude:: launch/example_substitutions_launch.py
305304
:language: python
306305
:lines: 26-31
307306

308-
Afterwards, the ``ExecuteProcess`` action called ``spawn_turtle`` is defined with the corresponding ``cmd`` argument.
309-
This command makes a call to the spawn service of the turtlesim node.
307+
The next action, ``ExecuteProcess``, is defined with the corresponding ``cmd`` argument to call the spawn service of the turtlesim node.
310308

311-
Additionally, the ``LaunchConfiguration`` substitution is used to get the value of the ``turtlesim_ns`` launch argument to construct a command string.
309+
Additionally, the ``LaunchConfiguration`` substitution is used to provide the value of the ``turtlesim_ns`` launch argument in the command string.
312310

313311
.. literalinclude:: launch/example_substitutions_launch.py
314312
:language: python
315313
:lines: 32-41
316314

317315
The same approach is used for the ``change_background_r`` and ``change_background_r_conditioned`` actions that change the turtlesim background's red color parameter.
318-
The difference is that the ``change_background_r_conditioned`` action is only executed if the provided ``new_background_r`` argument equals ``200`` and the ``use_provided_red`` launch argument is set to ``True``.
316+
The difference is that the next action is only executed if the provided ``new_background_r`` argument equals ``200`` and the ``use_provided_red`` launch argument is set to ``True``.
319317
The evaluation inside the ``IfCondition`` is done using the ``PythonExpression`` substitution.
320318

321319
.. literalinclude:: launch/example_substitutions_launch.py
322320
:language: python
323-
:lines: 42-67
321+
:lines: 51-72
324322

325323
4 Build the package
326324
^^^^^^^^^^^^^^^^^^^
Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
from launch import LaunchDescription
22
from launch.actions import IncludeLaunchDescription
3-
from launch.launch_description_sources import PythonLaunchDescriptionSource
4-
from launch.substitutions import PathJoinSubstitution, TextSubstitution
3+
from launch.substitutions import PathJoinSubstitution
54
from launch_ros.substitutions import FindPackageShare
65

76

@@ -12,17 +11,15 @@ def generate_launch_description():
1211

1312
return LaunchDescription([
1413
IncludeLaunchDescription(
15-
PythonLaunchDescriptionSource([
16-
PathJoinSubstitution([
17-
FindPackageShare('launch_tutorial'),
18-
'launch',
19-
'example_substitutions.launch.py'
20-
])
14+
PathJoinSubstitution([
15+
FindPackageShare('launch_tutorial'),
16+
'launch',
17+
'example_substitutions_launch.py'
2118
]),
2219
launch_arguments={
2320
'turtlesim_ns': 'turtlesim2',
2421
'use_provided_red': 'True',
25-
'new_background_r': TextSubstitution(text=str(colors['background_r']))
22+
'new_background_r': colors['background_r'],
2623
}.items()
2724
)
2825
])

0 commit comments

Comments
 (0)