Nav2 Navigation Stack - rolling  main
ROS 2 Navigation Stack
unique_multi_tb3_simulation_launch.py
1 # Copyright (c) 2018 Intel Corporation
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14 
15 """
16 Example for spawning multiple robots in Gazebo.
17 
18 This is an example on how to create a launch file for spawning multiple robots into Gazebo
19 and launch multiple instances of the navigation stack, each controlling one robot.
20 The robots co-exist on a shared environment and are controlled by independent nav stacks.
21 """
22 
23 import os
24 from pathlib import Path
25 import tempfile
26 from typing import TypedDict
27 
28 from ament_index_python.packages import get_package_share_directory
29 from launch import LaunchDescription
30 from launch.actions import (AppendEnvironmentVariable, DeclareLaunchArgument, ExecuteProcess,
31  GroupAction, IncludeLaunchDescription, LogInfo, OpaqueFunction,
32  RegisterEventHandler)
33 from launch.conditions import IfCondition
34 from launch.event_handlers import OnShutdown
35 from launch.launch_description_sources import PythonLaunchDescriptionSource
36 from launch.substitutions import LaunchConfiguration, TextSubstitution
37 from nav2_common.launch import LaunchConfigAsBool
38 
39 
40 class RobotConfig(TypedDict):
41  """TypedDict for robot configuration."""
42 
43  name: str
44  x_pose: float
45  y_pose: float
46  z_pose: float
47  roll: float
48  pitch: float
49  yaw: float
50 
51 
52 def generate_launch_description() -> LaunchDescription:
53  # Get the launch directory
54  bringup_dir = get_package_share_directory('nav2_bringup')
55  launch_dir = os.path.join(bringup_dir, 'launch')
56  sim_dir = get_package_share_directory('nav2_minimal_tb3_sim')
57 
58  # Names and poses of the robots
59  robots: list[RobotConfig] = [
60  {
61  'name': 'robot1',
62  'x_pose': 0.0,
63  'y_pose': 0.5,
64  'z_pose': 0.01,
65  'roll': 0.0,
66  'pitch': 0.0,
67  'yaw': 0.0,
68  },
69  {
70  'name': 'robot2',
71  'x_pose': 0.0,
72  'y_pose': -0.5,
73  'z_pose': 0.01,
74  'roll': 0.0,
75  'pitch': 0.0,
76  'yaw': 0.0,
77  },
78  ]
79 
80  # Simulation settings
81  world = LaunchConfiguration('world')
82 
83  # On this example all robots are launched with the same settings
84  map_yaml_file = LaunchConfiguration('map')
85  graph_filepath = LaunchConfiguration('graph')
86 
87  autostart = LaunchConfigAsBool('autostart')
88  rviz_config_file = LaunchConfiguration('rviz_config')
89  use_robot_state_pub = LaunchConfigAsBool('use_robot_state_pub')
90  use_rviz = LaunchConfigAsBool('use_rviz')
91  log_settings = LaunchConfiguration('log_settings', default='true')
92 
93  # Declare the launch arguments
94  declare_world_cmd = DeclareLaunchArgument(
95  'world',
96  default_value=os.path.join(sim_dir, 'worlds', 'tb3_sandbox.sdf.xacro'),
97  description='Full path to world file to load',
98  )
99 
100  declare_map_yaml_cmd = DeclareLaunchArgument(
101  'map',
102  default_value=os.path.join(bringup_dir, 'maps', 'tb3_sandbox.yaml'),
103  description='Full path to map file to load',
104  )
105 
106  declare_graph_file_cmd = DeclareLaunchArgument(
107  'graph',
108  default_value=os.path.join(bringup_dir, 'graphs', 'turtlebot3_graph.geojson'),
109  )
110 
111  declare_robot1_params_file_cmd = DeclareLaunchArgument(
112  'robot1_params_file',
113  default_value=os.path.join(
114  bringup_dir, 'params', 'nav2_params.yaml'
115  ),
116  description='Full path to the ROS2 parameters file to use for robot1 launched nodes',
117  )
118 
119  declare_robot2_params_file_cmd = DeclareLaunchArgument(
120  'robot2_params_file',
121  default_value=os.path.join(
122  bringup_dir, 'params', 'nav2_params.yaml'
123  ),
124  description='Full path to the ROS2 parameters file to use for robot2 launched nodes',
125  )
126 
127  declare_autostart_cmd = DeclareLaunchArgument(
128  'autostart',
129  default_value='false',
130  description='Automatically startup the stacks',
131  )
132 
133  declare_rviz_config_file_cmd = DeclareLaunchArgument(
134  'rviz_config',
135  default_value=os.path.join(bringup_dir, 'rviz', 'nav2_default_view.rviz'),
136  description='Full path to the RVIZ config file to use.',
137  )
138 
139  declare_use_robot_state_pub_cmd = DeclareLaunchArgument(
140  'use_robot_state_pub',
141  default_value='True',
142  description='Whether to start the robot state publisher',
143  )
144 
145  declare_use_rviz_cmd = DeclareLaunchArgument(
146  'use_rviz', default_value='True', description='Whether to start RVIZ'
147  )
148 
149  # Start Gazebo with plugin providing the robot spawning service
150  world_sdf = tempfile.mktemp(prefix='nav2_', suffix='.sdf')
151  world_sdf_xacro = ExecuteProcess(
152  cmd=['xacro', '-o', world_sdf, ['headless:=', 'False'], world])
153  start_gazebo_cmd = ExecuteProcess(
154  cmd=['gz', 'sim', '-r', '-s', world_sdf],
155  output='screen',
156  )
157 
158  remove_temp_sdf_file = RegisterEventHandler(event_handler=OnShutdown(
159  on_shutdown=[
160  OpaqueFunction(function=lambda _: os.remove(world_sdf))
161  ]))
162 
163  # Define commands for launching the navigation instances
164  nav_instances_cmds = []
165  for robot in robots:
166  params_file = LaunchConfiguration(f"{robot['name']}_params_file")
167 
168  group = GroupAction(
169  [
170  IncludeLaunchDescription(
171  PythonLaunchDescriptionSource(
172  os.path.join(launch_dir, 'rviz_launch.py')
173  ),
174  condition=IfCondition(use_rviz),
175  launch_arguments={
176  'namespace': TextSubstitution(text=robot['name']),
177  'rviz_config': rviz_config_file,
178  }.items(),
179  ),
180  IncludeLaunchDescription(
181  PythonLaunchDescriptionSource(
182  os.path.join(bringup_dir, 'launch', 'tb3_simulation_launch.py')
183  ),
184  launch_arguments={
185  'namespace': robot['name'],
186  'map': map_yaml_file,
187  'graph': graph_filepath,
188  'use_sim_time': 'True',
189  'params_file': params_file,
190  'autostart': autostart,
191  'use_rviz': 'False',
192  'use_simulator': 'False',
193  'headless': 'False',
194  'use_robot_state_pub': use_robot_state_pub,
195  'x_pose': TextSubstitution(text=str(robot['x_pose'])),
196  'y_pose': TextSubstitution(text=str(robot['y_pose'])),
197  'z_pose': TextSubstitution(text=str(robot['z_pose'])),
198  'roll': TextSubstitution(text=str(robot['roll'])),
199  'pitch': TextSubstitution(text=str(robot['pitch'])),
200  'yaw': TextSubstitution(text=str(robot['yaw'])),
201  'robot_name': TextSubstitution(text=robot['name']),
202  }.items(),
203  ),
204  LogInfo(
205  condition=IfCondition(log_settings),
206  msg=['Launching ', robot['name']],
207  ),
208  LogInfo(
209  condition=IfCondition(log_settings),
210  msg=[robot['name'], ' map yaml: ', map_yaml_file],
211  ),
212  LogInfo(
213  condition=IfCondition(log_settings),
214  msg=[robot['name'], ' params yaml: ', params_file],
215  ),
216  LogInfo(
217  condition=IfCondition(log_settings),
218  msg=[robot['name'], ' rviz config file: ', rviz_config_file],
219  ),
220  LogInfo(
221  condition=IfCondition(log_settings),
222  msg=[
223  robot['name'],
224  ' using robot state pub: ',
225  use_robot_state_pub,
226  ],
227  ),
228  LogInfo(
229  condition=IfCondition(log_settings),
230  msg=[robot['name'], ' autostart: ', autostart],
231  ),
232  ]
233  )
234 
235  nav_instances_cmds.append(group)
236 
237  set_env_vars_resources = AppendEnvironmentVariable(
238  'GZ_SIM_RESOURCE_PATH', os.path.join(sim_dir, 'models'))
239  set_env_vars_resources2 = AppendEnvironmentVariable(
240  'GZ_SIM_RESOURCE_PATH',
241  str(Path(os.path.join(sim_dir)).parent.resolve()))
242 
243  # Create the launch description and populate
244  ld = LaunchDescription()
245  ld.add_action(set_env_vars_resources)
246  ld.add_action(set_env_vars_resources2)
247 
248  # Declare the launch options
249  ld.add_action(declare_world_cmd)
250  ld.add_action(declare_map_yaml_cmd)
251  ld.add_action(declare_graph_file_cmd)
252  ld.add_action(declare_robot1_params_file_cmd)
253  ld.add_action(declare_robot2_params_file_cmd)
254  ld.add_action(declare_use_rviz_cmd)
255  ld.add_action(declare_autostart_cmd)
256  ld.add_action(declare_rviz_config_file_cmd)
257  ld.add_action(declare_use_robot_state_pub_cmd)
258 
259  # Add the actions to start gazebo, robots and simulations
260  ld.add_action(world_sdf_xacro)
261  ld.add_action(start_gazebo_cmd)
262  ld.add_action(remove_temp_sdf_file)
263 
264  for simulation_instance_cmd in nav_instances_cmds:
265  ld.add_action(simulation_instance_cmd)
266 
267  return ld