1- import glob
21import os
32
4- from ament_index_python .packages import PackageNotFoundError
53from ament_index_python .packages import get_package_share_directory
64from launch import LaunchDescription
75from launch .actions import DeclareLaunchArgument
1513from launch_ros .actions import Node
1614from launch_ros .actions import SetRemap
1715
18-
19- def _find_map_yaml_in_directory (directory : str ):
20- """Prefer map.yaml; otherwise first *.yaml in directory (lexicographic)."""
21- if not os .path .isdir (directory ):
22- return None
23- primary = os .path .join (directory , 'map.yaml' )
24- if os .path .isfile (primary ):
25- return os .path .abspath (primary )
26- yamls = sorted (glob .glob (os .path .join (directory , '*.yaml' )))
27- if yamls :
28- return os .path .abspath (yamls [0 ])
29- return None
30-
31-
32- def _resolve_map_yaml (map_arg : str , robot_name : str , default_map_yaml : str , common_share : str ) -> str :
33- """
34- Resolve map for map_server: absolute yaml path, or directory, or name / relative path under
35- {robot}_description/config/maps then robot_common_launch/config/maps, else default_map_yaml.
36- """
37- raw = map_arg .strip ()
38- if not raw :
39- return default_map_yaml
40-
41- expanded = os .path .expanduser (raw )
42-
43- if os .path .isfile (expanded ):
44- return os .path .abspath (expanded )
45-
46- if os .path .isdir (expanded ):
47- found = _find_map_yaml_in_directory (expanded )
48- if found :
49- return found
50-
51- maps_roots = []
52- if robot_name :
53- try :
54- robot_share = get_package_share_directory (f'{ robot_name } _description' )
55- maps_roots .append (os .path .join (robot_share , 'config' , 'maps' ))
56- except PackageNotFoundError :
57- pass
58- maps_roots .append (os .path .join (common_share , 'config' , 'maps' ))
59-
60- for root in maps_roots :
61- candidate = os .path .normpath (os .path .join (root , expanded ))
62- if os .path .isfile (candidate ):
63- return os .path .abspath (candidate )
64- if os .path .isdir (candidate ):
65- found = _find_map_yaml_in_directory (candidate )
66- if found :
67- return found
68- # 允许写 german_poc/map 这类「子路径无后缀」,自动补 .yaml
69- if not expanded .endswith (('.yaml' , '.yml' )):
70- yaml_only = candidate + '.yaml'
71- if os .path .isfile (yaml_only ):
72- return os .path .abspath (yaml_only )
73-
74- hint = f'{ robot_name } _description/config/maps, then ' if robot_name else ''
75- print (
76- f'[navigation_isaac_gt] Could not resolve map { raw !r} under '
77- f'{ hint } robot_common_launch/config/maps; using default:\n { default_map_yaml } '
78- )
79- return default_map_yaml
16+ from robot_common_launch .common .navigation_config_utils import (
17+ default_map_yaml ,
18+ default_nav2_params_yaml ,
19+ resolve_map_yaml ,
20+ resolve_nav2_params ,
21+ )
8022
8123
8224def generate_launch_description ():
8325 common_share = get_package_share_directory ('robot_common_launch' )
84- default_params_yaml = os .path .join (
85- common_share , 'config' , 'nav2' , 'nav2_params_isaac_gt.yaml' )
26+ default_params_yaml = default_nav2_params_yaml ()
8627
87- # map 参数默认只写「地图文件夹名」;在 config/maps/<文件夹>/ 下优先 map.yaml,否则任取 *.yaml
8828 default_map_folder = 'warehouse_with_forklifts'
89- _default_map_dir = os .path .join (
90- common_share , 'config' , 'maps' , default_map_folder )
91- default_map_yaml = _find_map_yaml_in_directory (_default_map_dir )
92- if not default_map_yaml :
93- default_map_yaml = os .path .join (_default_map_dir , 'map.yaml' )
29+ _default_map_yaml = default_map_yaml ()
9430
9531 use_sim_time = LaunchConfiguration ('use_sim_time' , default = 'true' )
9632 robot = LaunchConfiguration ('robot' , default = '' )
33+ map_config_package = LaunchConfiguration ('map_config_package' , default = '' )
34+ nav_config_package = LaunchConfiguration ('nav_config_package' , default = '' )
9735 params_file = LaunchConfiguration ('params_file' , default = '' )
9836 nav2_profile = LaunchConfiguration ('nav2_profile' , default = 'default' )
9937 map_arg = LaunchConfiguration ('map' )
@@ -102,46 +40,29 @@ def generate_launch_description():
10240
10341 def resolve_isaac_gt_launch (context ):
10442 actions = []
105-
106- user_params = params_file .perform (context ).strip ()
107- if user_params :
108- actions .append (SetLaunchConfiguration ('resolved_params_file' , user_params ))
109- else :
110- robot_name = robot .perform (context ).strip ()
111- profile = nav2_profile .perform (context ).strip ().lower ()
112- chosen = default_params_yaml
113- if robot_name :
114- try :
115- robot_share = get_package_share_directory (f'{ robot_name } _description' )
116- if profile == 'map_only' :
117- map_only_params = os .path .join (
118- robot_share , 'config' , 'nav2' , 'nav2_params_isaac_gt_map_only.yaml' )
119- if os .path .exists (map_only_params ):
120- chosen = map_only_params
121- else :
122- print (
123- '[navigation_isaac_gt] nav2_profile=map_only but '
124- f'{ map_only_params !r} not found; falling back to nav2_params_isaac_gt.yaml.'
125- )
126- robot_params = os .path .join (
127- robot_share , 'config' , 'nav2' , 'nav2_params_isaac_gt.yaml' )
128- if os .path .exists (robot_params ):
129- chosen = robot_params
130- else :
131- robot_params = os .path .join (
132- robot_share , 'config' , 'nav2' , 'nav2_params_isaac_gt.yaml' )
133- if os .path .exists (robot_params ):
134- chosen = robot_params
135- except PackageNotFoundError :
136- pass
137- actions .append (SetLaunchConfiguration ('resolved_params_file' , chosen ))
138-
13943 robot_name = robot .perform (context ).strip ()
44+ map_pkg = map_config_package .perform (context ).strip ()
45+ nav_pkg = nav_config_package .perform (context ).strip ()
46+ profile = nav2_profile .perform (context ).strip ()
47+ user_params = params_file .perform (context ).strip ()
14048 map_value = map_arg .perform (context )
141- resolved_map = _resolve_map_yaml (
142- map_value , robot_name , default_map_yaml , common_share )
143- actions .append (SetLaunchConfiguration ('resolved_map_yaml' , resolved_map ))
14449
50+ chosen = resolve_nav2_params (
51+ robot_name = robot_name ,
52+ nav_config_package = nav_pkg ,
53+ nav2_profile = profile ,
54+ params_file = user_params ,
55+ default_params_yaml = default_params_yaml ,
56+ )
57+ actions .append (SetLaunchConfiguration ('resolved_params_file' , chosen ))
58+
59+ resolved_map = resolve_map_yaml (
60+ map_value ,
61+ robot_name = robot_name ,
62+ map_config_package = map_pkg ,
63+ default_map_yaml_path = _default_map_yaml ,
64+ )
65+ actions .append (SetLaunchConfiguration ('resolved_map_yaml' , resolved_map ))
14566 return actions
14667
14768 scan_topic = LaunchConfiguration ('scan_topic' , default = '/scan' )
@@ -193,8 +114,6 @@ def resolve_isaac_gt_launch(context):
193114 ),
194115 ])
195116
196- # 必须与 Nav2 / Isaac 一致使用仿真时钟;否则 TF 用 wall time、其它节点用 /clock,
197- # 会出现 map<->odom 与代价地图/点云时间差数十秒,报 Transform too old / 点云被丢弃。
198117 map_to_odom_tf = Node (
199118 package = 'tf2_ros' ,
200119 executable = 'static_transform_publisher' ,
@@ -219,7 +138,8 @@ def resolve_isaac_gt_launch(context):
219138 description = (
220139 'Map folder name under config/maps (prefer map.yaml, else first *.yaml), '
221140 'or path to .yaml / map directory, or subpath like german_poc/map (.yaml optional); '
222- 'robot package config/maps is tried before robot_common_launch'
141+ 'search order: map_config_package, {robot}_description/config/navigation.yaml, '
142+ '{robot}_description, robot_common_launch'
223143 )),
224144 DeclareLaunchArgument (
225145 'params_file' ,
@@ -230,13 +150,27 @@ def resolve_isaac_gt_launch(context):
230150 default_value = 'default' ,
231151 description = (
232152 'Nav2 preset when params_file is empty: default=nav2_params_isaac_gt.yaml; '
233- 'map_only=nav2_params_isaac_gt_map_only.yaml (static map only, no point cloud) '
234- 'if present under {robot}_description/config/nav2 '
153+ 'map_only=nav2_params_isaac_gt_map_only.yaml; routing from '
154+ '{robot}_description/config/navigation.yaml when args empty '
235155 )),
236156 DeclareLaunchArgument (
237157 'robot' ,
238158 default_value = '' ,
239- description = 'Robot name without _description suffix, e.g. fiveages_w1' ),
159+ description = 'Robot name without _description suffix, e.g. fiveages_w2' ),
160+ DeclareLaunchArgument (
161+ 'map_config_package' ,
162+ default_value = '' ,
163+ description = (
164+ 'Optional ament package for config/maps '
165+ '(e.g. fiveages_w2_common_description)'
166+ )),
167+ DeclareLaunchArgument (
168+ 'nav_config_package' ,
169+ default_value = '' ,
170+ description = (
171+ 'Optional ament package for config/nav2 '
172+ '(e.g. fiveages_w2_common_description)'
173+ )),
240174 DeclareLaunchArgument (
241175 'use_sim_time' ,
242176 default_value = 'true' ,
0 commit comments