diff --git a/src/my_robot_bringup/launch/master_launch.py b/src/my_robot_bringup/launch/master_launch.py
new file mode 100755
index 0000000..ee5e81c
--- /dev/null
+++ b/src/my_robot_bringup/launch/master_launch.py
@@ -0,0 +1,352 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+# ============================================================================
+# master_launch.py —— 机器人系统总启动文件
+# 启动顺序: origincar_base → LiDAR → TTS服务 → USB相机+二维码 → 障碍物检测 → 路径规划 → 轨迹跟随 → VLM
+# 用法:
+# ros2 launch my_robot_bringup master_launch.py
+# ros2 launch my_robot_bringup master_launch.py use_qr:=false
+# ros2 launch my_robot_bringup master_launch.py use_vlm:=false
+# ros2 launch my_robot_bringup master_launch.py use_planner:=false
+# ============================================================================
+import os
+from ament_index_python.packages import get_package_share_directory
+from launch import LaunchDescription
+from launch.actions import (
+ DeclareLaunchArgument,
+ IncludeLaunchDescription,
+ TimerAction,
+ LogInfo,
+)
+from launch.conditions import IfCondition
+from launch.launch_description_sources import PythonLaunchDescriptionSource
+from launch.substitutions import LaunchConfiguration
+from launch_ros.actions import Node
+from launch_ros.substitutions import FindPackageShare
+
+
+def generate_launch_description():
+
+ # ================================================================
+ # 路径
+ # ================================================================
+ origincar_dir = get_package_share_directory('origincar_base')
+ lslidar_dir = get_package_share_directory('lslidar_driver')
+ obstacle_dir = get_package_share_directory('obstacle_scanner')
+ planner_dir = get_package_share_directory('planner')
+ vlm_dir = get_package_share_directory('vlm_detect')
+
+ # ================================================================
+ # Launch 参数 —— 可通过命令行灵活切换
+ # ================================================================
+
+ # 模块开关
+ use_base = LaunchConfiguration('use_base', default='true')
+ use_lidar = LaunchConfiguration('use_lidar', default='true')
+ use_qr = LaunchConfiguration('use_qr', default='true')
+ use_tts = LaunchConfiguration('use_tts', default='true')
+ use_obstacle = LaunchConfiguration('use_obstacle', default='true')
+ use_planner = LaunchConfiguration('use_planner', default='true')
+ use_vlm = LaunchConfiguration('use_vlm', default='true')
+
+ # 车辆模式
+ akmcar = LaunchConfiguration('akmcar', default='true')
+ carto_slam = LaunchConfiguration('carto_slam', default='false')
+
+ # 摄像头设备
+ camera_device = LaunchConfiguration('camera_device', default='/dev/video0')
+
+ # TTS 参数
+ audio_sink = LaunchConfiguration('audio_sink',
+ default='alsa_output.usb-C-Media_Electronics_Inc._USB_Audio_Device-00.analog-stereo')
+ tts_speed = LaunchConfiguration('tts_speed', default='1.5')
+
+ # 地图文件 (AMCL 定位用)
+ map_file = LaunchConfiguration('map_file', default=os.path.join(
+ planner_dir, 'maps', 'real_5x5.yaml'))
+
+ # Planner / Tracker 参数文件
+ planner_params_file = LaunchConfiguration('planner_params_file', default=os.path.join(
+ planner_dir, 'config', 'real_hybrid_astar.yaml'))
+ tracker_params_file = LaunchConfiguration('tracker_params_file', default=os.path.join(
+ planner_dir, 'config', 'real_pure_pursuit.yaml'))
+ amcl_params_file = LaunchConfiguration('amcl_params_file', default=os.path.join(
+ planner_dir, 'config', 'real_amcl.yaml'))
+
+ # VLM 服务器地址
+ vlm_host = LaunchConfiguration('vlm_host', default='http://192.168.10.189:8000')
+
+ # 通用
+ use_sim_time = LaunchConfiguration('use_sim_time', default='false')
+
+ # ================================================================
+ # 阶段 1: 底盘驱动 + EKF + TF + IMU (t=0s)
+ # ================================================================
+ origincar_bringup = IncludeLaunchDescription(
+ PythonLaunchDescriptionSource([
+ origincar_dir, '/launch', '/origincar_bringup.launch.py'
+ ]),
+ condition=IfCondition(use_base),
+ launch_arguments={
+ 'akmcar': akmcar,
+ 'carto_slam': carto_slam,
+ }.items(),
+ )
+
+ # ================================================================
+ # 阶段 2: 激光雷达 (t=2s)
+ # ================================================================
+ lidar_launch = IncludeLaunchDescription(
+ PythonLaunchDescriptionSource([
+ lslidar_dir, '/launch', '/lsn10_launch.py'
+ ]),
+ condition=IfCondition(use_lidar),
+ )
+
+ # ================================================================
+ # 阶段 3: TTS 语音播报服务 + 二维码播报桥接 (t=3s)
+ # tts_server: Piper 离线 TTS + espeak 降级,提供 /tts/speak 服务
+ # qr_tts_bridge: 订阅 qr_results,调用 /tts/speak
+ # ================================================================
+ tts_server = Node(
+ package='vlm_detect',
+ executable='tts_server',
+ name='tts_server',
+ output='screen',
+ condition=IfCondition(use_tts),
+ parameters=[{
+ 'audio_sink': audio_sink,
+ 'tts_speed': tts_speed,
+ }],
+ )
+
+ qr_tts_bridge = Node(
+ package='vlm_detect',
+ executable='qr_tts_bridge',
+ name='qr_tts_bridge',
+ output='screen',
+ condition=IfCondition(use_tts),
+ )
+
+ # ================================================================
+ # 阶段 4: USB 摄像头 + 二维码识别 (t=4s)
+ # usb_cam: 驱动 USB 摄像头,发布 /image_raw
+ # qr_detect: 订阅 /image_raw,识别二维码,发布 qr_results
+ # ================================================================
+ usb_camera = Node(
+ package='usb_cam',
+ executable='usb_cam_node_exe',
+ name='usb_cam',
+ output='screen',
+ condition=IfCondition(use_qr),
+ parameters=[{
+ 'video_device': camera_device,
+ 'image_size': [640, 480],
+ 'pixel_format': 'YUYV',
+ 'framerate': 30.0,
+ }],
+ )
+
+ qr_detect = Node(
+ package='qr_detection',
+ executable='qr_detect',
+ name='qr_detect',
+ output='screen',
+ condition=IfCondition(use_qr),
+ parameters=[{'image_topic': '/image_raw'}],
+ )
+
+ # ================================================================
+ # 阶段 5: 障碍物检测 (t=5s)
+ # ================================================================
+ obstacle_launch = IncludeLaunchDescription(
+ PythonLaunchDescriptionSource([
+ obstacle_dir, '/launch', '/obstacle_scanner.launch.py'
+ ]),
+ condition=IfCondition(use_obstacle),
+ )
+
+ # ================================================================
+ # 阶段 6: 地图服务器 + AMCL 定位 (t=7s)
+ # ================================================================
+ map_server_node = Node(
+ package='nav2_map_server',
+ executable='map_server',
+ name='map_server',
+ output='screen',
+ condition=IfCondition(use_planner),
+ parameters=[{'yaml_filename': map_file,
+ 'use_sim_time': use_sim_time}],
+ )
+
+ amcl_node = Node(
+ package='nav2_amcl',
+ executable='amcl',
+ name='amcl',
+ output='screen',
+ condition=IfCondition(use_planner),
+ parameters=[amcl_params_file,
+ {'use_sim_time': use_sim_time}],
+ )
+
+ lifecycle_manager = Node(
+ package='nav2_lifecycle_manager',
+ executable='lifecycle_manager',
+ name='lifecycle_manager_localization',
+ output='screen',
+ condition=IfCondition(use_planner),
+ parameters=[{'use_sim_time': use_sim_time,
+ 'autostart': True,
+ 'node_names': ['map_server', 'amcl']}],
+ )
+
+ # ================================================================
+ # 阶段 7: Hybrid A* 路径规划 + Pure Pursuit 轨迹跟随 (t=9s)
+ # ================================================================
+ planner_node = Node(
+ package='planner',
+ executable='grid_astar_theta_node',
+ name='grid_astar_theta_planner',
+ output='screen',
+ condition=IfCondition(use_planner),
+ parameters=[planner_params_file,
+ {'use_sim_time': use_sim_time,
+ 'obstacles_topic': '/obstacles'}],
+ )
+
+ tracker_node = Node(
+ package='planner',
+ executable='topology_pure_pursuit_node.py',
+ name='topology_pure_pursuit',
+ output='screen',
+ condition=IfCondition(use_planner),
+ parameters=[tracker_params_file,
+ {'use_sim_time': use_sim_time}],
+ )
+
+ # ================================================================
+ # 阶段 8: VLM 图生文 (t=11s)
+ # vlm_node 内部自带 TTS 服务客户端,识别完自动调用 /tts/speak
+ # 注意: TTS 服务和桥接已在阶段 3 启动,这里只启动 vlm_node
+ # ================================================================
+ vlm_launch = IncludeLaunchDescription(
+ PythonLaunchDescriptionSource([
+ vlm_dir, '/launch', '/vlm_detect.launch.py'
+ ]),
+ condition=IfCondition(use_vlm),
+ launch_arguments={
+ 'vlm_host': vlm_host,
+ 'use_tts': 'false', # tts_server 已在阶段 3 启动
+ 'use_qr_tts': 'false', # qr_tts_bridge 已在阶段 3 启动
+ }.items(),
+ )
+
+ # ================================================================
+ # 组装 —— 通过 TimerAction 保证先后顺序
+ # ================================================================
+ return LaunchDescription([
+
+ # 模块 开关 参数
+ DeclareLaunchArgument('use_base', default_value='true',
+ description='底盘驱动 + EKF + TF'),
+ DeclareLaunchArgument('use_lidar', default_value='true',
+ description='激光雷达 (lsn10)'),
+ DeclareLaunchArgument('use_qr', default_value='true',
+ description='USB 摄像头 + 二维码识别'),
+ DeclareLaunchArgument('use_tts', default_value='true',
+ description='TTS 语音播报服务 + 二维码播报桥接'),
+ DeclareLaunchArgument('use_obstacle', default_value='true',
+ description='障碍物检测'),
+ DeclareLaunchArgument('use_planner', default_value='true',
+ description='路径规划 + 轨迹跟随'),
+ DeclareLaunchArgument('use_vlm', default_value='true',
+ description='VLM 图生文'),
+ DeclareLaunchArgument('akmcar', default_value='true',
+ description='车辆模型 (true=阿克曼, false=麦轮)'),
+ DeclareLaunchArgument('carto_slam', default_value='false',
+ description='使用 Cartographer 替代 EKF'),
+ DeclareLaunchArgument('camera_device', default_value='/dev/video0',
+ description='USB 摄像头设备路径'),
+ DeclareLaunchArgument('audio_sink',
+ default_value='alsa_output.usb-C-Media_Electronics_Inc._USB_Audio_Device-00.analog-stereo',
+ description='音频输出设备 (PulseAudio sink)'),
+ DeclareLaunchArgument('tts_speed', default_value='1.5',
+ description='TTS 语速倍率 (0.5~2.0)'),
+ DeclareLaunchArgument('map_file', default_value=os.path.join(
+ planner_dir, 'maps', 'real_5x5.yaml'),
+ description='预建地图 YAML 文件'),
+ DeclareLaunchArgument('planner_params_file', default_value=os.path.join(
+ planner_dir, 'config', 'real_hybrid_astar.yaml'),
+ description='Hybrid A* 规划器参数文件'),
+ DeclareLaunchArgument('tracker_params_file', default_value=os.path.join(
+ planner_dir, 'config', 'real_pure_pursuit.yaml'),
+ description='Pure Pursuit 跟踪器参数文件'),
+ DeclareLaunchArgument('amcl_params_file', default_value=os.path.join(
+ planner_dir, 'config', 'real_amcl.yaml'),
+ description='AMCL 定位参数文件'),
+ DeclareLaunchArgument('vlm_host', default_value='http://192.168.10.189:8000',
+ description='VLM 服务器地址'),
+ DeclareLaunchArgument('use_sim_time', default_value='false',
+ description='使用仿真时间'),
+
+ LogInfo(msg='========================================'),
+ LogInfo(msg=' Robot System Bringup'),
+ LogInfo(msg='========================================'),
+
+ # t=0s: 底盘 + EKF
+ TimerAction(period=0.0, actions=[
+ LogInfo(msg='[1/8] Starting base driver + EKF + TF + IMU ...'),
+ origincar_bringup,
+ ]),
+
+ # t=2s: 激光雷达
+ TimerAction(period=2.0, actions=[
+ LogInfo(msg='[2/8] Starting LiDAR (lsn10) ...'),
+ lidar_launch,
+ ]),
+
+ # t=3s: TTS 服务 + 二维码播报桥接
+ TimerAction(period=3.0, actions=[
+ LogInfo(msg='[3/8] Starting TTS Server + QR-TTS Bridge ...'),
+ tts_server,
+ qr_tts_bridge,
+ ]),
+
+ # t=4s: USB 摄像头 + 二维码识别
+ TimerAction(period=4.0, actions=[
+ LogInfo(msg='[4/8] Starting USB Camera + QR Detection ...'),
+ usb_camera,
+ qr_detect,
+ ]),
+
+ # t=5s: 障碍物检测
+ TimerAction(period=5.0, actions=[
+ LogInfo(msg='[5/8] Starting Obstacle Scanner ...'),
+ obstacle_launch,
+ ]),
+
+ # t=7s: 地图 + AMCL 定位
+ TimerAction(period=7.0, actions=[
+ LogInfo(msg='[6/8] Starting Map Server + AMCL localization ...'),
+ map_server_node,
+ amcl_node,
+ lifecycle_manager,
+ ]),
+
+ # t=9s: 路径规划 + 轨迹跟随
+ TimerAction(period=9.0, actions=[
+ LogInfo(msg='[7/8] Starting Hybrid A* Planner + Pure Pursuit Tracker ...'),
+ planner_node,
+ tracker_node,
+ ]),
+
+ # t=11s: VLM 图生文
+ TimerAction(period=11.0, actions=[
+ LogInfo(msg='[8/8] Starting VLM ...'),
+ vlm_launch,
+ ]),
+
+ LogInfo(msg='========================================'),
+ LogInfo(msg=' All modules started!'),
+ LogInfo(msg='========================================'),
+ ])
diff --git a/src/my_robot_bringup/launch/master_launch.py.bak b/src/my_robot_bringup/launch/master_launch.py.bak
new file mode 100755
index 0000000..82f7aab
--- /dev/null
+++ b/src/my_robot_bringup/launch/master_launch.py.bak
@@ -0,0 +1,218 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+# ============================================================================
+# master_launch.py — 机器人系统总启动文件
+# 启动顺序: origincar_base → LiDAR → EKF → SLAM → Nav2 → VLM_Detect
+# 用法:
+# ros2 launch my_robot_bringup master_launch.py
+# ros2 launch my_robot_bringup master_launch.py use_vlm:=false
+# ros2 launch my_robot_bringup master_launch.py vlm_host:=http://X.X.X.X:8000
+# ============================================================================
+## 障碍物检测 路径规划 轨迹跟随 重定位
+import os
+from ament_index_python.packages import get_package_share_directory
+from launch import LaunchDescription
+from launch.actions import (
+ DeclareLaunchArgument,
+ IncludeLaunchDescription,
+ TimerAction,
+ LogInfo,
+)
+from launch.conditions import IfCondition
+from launch.launch_description_sources import PythonLaunchDescriptionSource
+from launch.substitutions import LaunchConfiguration
+from launch_ros.actions import Node
+from launch_ros.substitutions import FindPackageShare
+
+
+def generate_launch_description():
+
+ # ================================================================
+ # 包路径
+ # ================================================================
+ origincar_dir = get_package_share_directory('origincar_base')
+ lslidar_dir = get_package_share_directory('lslidar_driver')
+ cyy_slam_dir = get_package_share_directory('cyy_slamtoolbox')
+ gc_nav_dir = get_package_share_directory('gc_navigation2_slamtoolbox')
+ vlm_dir = get_package_share_directory('vlm_detect')
+
+ # ================================================================
+ # Launch 参数 — 可通过命令行覆盖
+ # ================================================================
+
+ # 模块开关
+ use_base = LaunchConfiguration('use_base', default='true')
+ use_lidar = LaunchConfiguration('use_lidar', default='true')
+ use_slam = LaunchConfiguration('use_slam', default='true')
+ use_nav2 = LaunchConfiguration('use_nav2', default='true')
+ use_vlm = LaunchConfiguration('use_vlm', default='true')
+ use_tts = LaunchConfiguration('use_tts', default='true')
+
+ # 底盘模式
+ akmcar = LaunchConfiguration('akmcar', default='true')
+ carto_slam = LaunchConfiguration('carto_slam', default='false')
+
+ # SLAM / Nav2 配置文件
+ slam_params_file = LaunchConfiguration('slam_params_file', default=os.path.join(
+ cyy_slam_dir, 'config', 'mapper_params_online_async.yaml'))
+ nav2_params_file = LaunchConfiguration('nav2_params_file', default=os.path.join(
+ gc_nav_dir, 'params', 'gc_navigation_slam.yaml'))
+
+ # VLM 推理服务地址 (Windows WSL)
+ vlm_host = LaunchConfiguration('vlm_host', default='http://192.168.10.189:8000')
+
+ # 通用
+ use_sim_time = LaunchConfiguration('use_sim_time', default='false')
+
+ # ================================================================
+ # 阶段 1: 底盘驱动 + EKF + TF + IMU (t=0s)
+ #
+ # origincar_bringup 内部已包含:
+ # - base_serial.launch.py 底盘驱动 (origincar_base_node)
+ # - robot_mode_description robot_state_publisher (TF from URDF)
+ # - imu_filter_madgwick IMU 姿态滤波
+ # - robot_localization ekf_node EKF 融合 (odom + IMU → odom_combined)
+ # - joint_state_publisher 关节状态发布
+ # - static TF: base_footprint→gyro_link, base_link→laser
+ # ================================================================
+ origincar_bringup = IncludeLaunchDescription(
+ PythonLaunchDescriptionSource([
+ origincar_dir, '/launch', '/origincar_bringup.launch.py'
+ ]),
+ condition=IfCondition(use_base),
+ launch_arguments={
+ 'akmcar': akmcar,
+ 'carto_slam': carto_slam,
+ }.items(),
+ )
+
+ # ================================================================
+ # 阶段 2: 激光雷达 (t=2s)
+ # lsn10_launch.py 启动 LS-N10 型号激光雷达驱动
+ # 如有其他型号,改包名和 launch 文件名即可
+ # ================================================================
+ lidar_launch = IncludeLaunchDescription(
+ PythonLaunchDescriptionSource([
+ lslidar_dir, '/launch', '/lsn10_launch.py'
+ ]),
+ condition=IfCondition(use_lidar),
+ )
+
+ # ================================================================
+ # 阶段 3: SLAM Toolbox — 实时建图 (t=5s)
+ # 需要: /scan (LiDAR) + odom→base_link TF (EKF)
+ # 输出: map→odom TF + /map topic
+ # ================================================================
+ slam_toolbox_node = Node(
+ package='slam_toolbox',
+ executable='async_slam_toolbox_node',
+ name='slam_toolbox',
+ output='screen',
+ condition=IfCondition(use_slam),
+ parameters=[slam_params_file,
+ {'use_sim_time': use_sim_time}],
+ )
+
+ # ================================================================
+ # 阶段 4: Nav2 导航栈 (t=8s)
+ # 需要: /map (SLAM) + /scan + 完整 TF 树 (map→odom→base_link→laser)
+ # 输出: /cmd_vel → origincar_base → 底盘运动
+ # ================================================================
+ nav2_launch = IncludeLaunchDescription(
+ PythonLaunchDescriptionSource([
+ FindPackageShare('nav2_bringup'), '/launch', '/navigation_launch.py'
+ ]),
+ condition=IfCondition(use_nav2),
+ launch_arguments={
+ 'use_sim_time': use_sim_time,
+ 'params_file': nav2_params_file,
+ }.items(),
+ )
+
+ # ================================================================
+ # 阶段 5: VLM 图生文 + TTS 语音播报 (t=10s)
+ # 完全独立于导航栈,只依赖 WSL 上 vlm_server.py 已启动
+ # ================================================================
+ vlm_launch = IncludeLaunchDescription(
+ PythonLaunchDescriptionSource([
+ vlm_dir, '/launch', '/vlm_detect.launch.py'
+ ]),
+ condition=IfCondition(use_vlm),
+ launch_arguments={
+ 'vlm_host': vlm_host,
+ 'use_tts': use_tts,
+ }.items(),
+ )
+
+ # ================================================================
+ # 组装 — 按时间线顺序,TimerAction 保证先后
+ # ================================================================
+ return LaunchDescription([
+
+ # —— 参数声明 ——
+ DeclareLaunchArgument('use_base', default_value='true',
+ description='底盘驱动 + EKF + TF'),
+ DeclareLaunchArgument('use_lidar', default_value='true',
+ description='激光雷达 (lsn10)'),
+ DeclareLaunchArgument('use_slam', default_value='true',
+ description='slam_toolbox 建图'),
+ DeclareLaunchArgument('use_nav2', default_value='true',
+ description='Nav2 导航栈'),
+ DeclareLaunchArgument('use_vlm', default_value='true',
+ description='VLM 图生文 + TTS'),
+ DeclareLaunchArgument('use_tts', default_value='true',
+ description='TTS 语音播报开关'),
+ DeclareLaunchArgument('akmcar', default_value='true',
+ description='阿克曼底盘 (true=阿克曼, false=差速)'),
+ DeclareLaunchArgument('carto_slam', default_value='false',
+ description='使用 Cartographer 替代 EKF'),
+ DeclareLaunchArgument('slam_params_file', default_value=os.path.join(
+ cyy_slam_dir, 'config', 'mapper_params_online_async.yaml'),
+ description='SLAM 参数文件'),
+ DeclareLaunchArgument('nav2_params_file', default_value=os.path.join(
+ gc_nav_dir, 'params', 'gc_navigation_slam.yaml'),
+ description='Nav2 参数文件'),
+ DeclareLaunchArgument('vlm_host', default_value='http://192.168.10.189:8000',
+ description='VLM 推理服务地址'),
+ DeclareLaunchArgument('use_sim_time', default_value='false',
+ description='使用仿真时间'),
+
+ # —— 时间线日志 ——
+ LogInfo(msg='========================================'),
+ LogInfo(msg=' Robot System Bringup'),
+ LogInfo(msg='========================================'),
+
+ # t=0s: 底盘 + EKF
+ TimerAction(period=0.0, actions=[
+ LogInfo(msg='[1/5] Starting base driver + EKF + TF + IMU ...'),
+ origincar_bringup,
+ ]),
+
+ # t=2s: 激光雷达
+ TimerAction(period=2.0, actions=[
+ LogInfo(msg='[2/5] Starting LiDAR (lsn10) ...'),
+ lidar_launch,
+ ]),
+
+ # t=5s: SLAM
+ TimerAction(period=5.0, actions=[
+ LogInfo(msg='[3/5] Starting slam_toolbox ...'),
+ slam_toolbox_node,
+ ]),
+
+ # t=8s: Nav2
+ TimerAction(period=8.0, actions=[
+ LogInfo(msg='[4/5] Starting Nav2 navigation ...'),
+ nav2_launch,
+ ]),
+
+ # t=10s: VLM
+ TimerAction(period=10.0, actions=[
+ LogInfo(msg='[5/5] Starting VLM + TTS ...'),
+ vlm_launch,
+ ]),
+
+ LogInfo(msg='========================================'),
+ LogInfo(msg=' All modules started!'),
+ LogInfo(msg='========================================'),
+ ])
diff --git a/src/my_robot_bringup/package.xml b/src/my_robot_bringup/package.xml
new file mode 100644
index 0000000..165b974
--- /dev/null
+++ b/src/my_robot_bringup/package.xml
@@ -0,0 +1,23 @@
+
+
+
+ my_robot_bringup
+ 1.0.0
+ Master launch file for the entire robot system
+ sunrise
+ Apache-2.0
+
+ ament_cmake
+ ament_cmake_python
+
+ origincar_base
+ lslidar_driver
+ slam_toolbox
+ nav2_bringup
+ vlm_detect
+ robot_localization
+
+
+ ament_python
+
+
diff --git a/src/my_robot_bringup/setup.py b/src/my_robot_bringup/setup.py
new file mode 100644
index 0000000..b011b58
--- /dev/null
+++ b/src/my_robot_bringup/setup.py
@@ -0,0 +1,21 @@
+from setuptools import setup
+from glob import glob
+import os
+
+package_name = 'my_robot_bringup'
+
+setup(
+ name=package_name,
+ version='1.0.0',
+ packages=[],
+ data_files=[
+ ('share/' + package_name, ['package.xml']),
+ ('share/' + package_name + '/launch', glob('launch/*.py')),
+ ],
+ install_requires=['setuptools'],
+ zip_safe=True,
+ maintainer='sunrise',
+ maintainer_email='sunrise@todo.todo',
+ description='Master bringup for robot system',
+ license='Apache-2.0',
+)
diff --git a/src/cyy_navigation2/CMakeLists.txt b/src/navigation/cyy_navigation2/CMakeLists.txt
similarity index 100%
rename from src/cyy_navigation2/CMakeLists.txt
rename to src/navigation/cyy_navigation2/CMakeLists.txt
diff --git a/src/cyy_navigation2/bt/follow_point.xml b/src/navigation/cyy_navigation2/bt/follow_point.xml
similarity index 100%
rename from src/cyy_navigation2/bt/follow_point.xml
rename to src/navigation/cyy_navigation2/bt/follow_point.xml
diff --git a/src/cyy_navigation2/bt/nav_to_pose_with_consistent_replanning_and_if_path_becomes_invalid.xml b/src/navigation/cyy_navigation2/bt/nav_to_pose_with_consistent_replanning_and_if_path_becomes_invalid.xml
similarity index 100%
rename from src/cyy_navigation2/bt/nav_to_pose_with_consistent_replanning_and_if_path_becomes_invalid.xml
rename to src/navigation/cyy_navigation2/bt/nav_to_pose_with_consistent_replanning_and_if_path_becomes_invalid.xml
diff --git a/src/cyy_navigation2/bt/navigate_through_poses_w_replanning_and_recovery.xml b/src/navigation/cyy_navigation2/bt/navigate_through_poses_w_replanning_and_recovery.xml
similarity index 100%
rename from src/cyy_navigation2/bt/navigate_through_poses_w_replanning_and_recovery.xml
rename to src/navigation/cyy_navigation2/bt/navigate_through_poses_w_replanning_and_recovery.xml
diff --git a/src/cyy_navigation2/bt/navigate_to_pose_w_replanning_and_recovery.xml b/src/navigation/cyy_navigation2/bt/navigate_to_pose_w_replanning_and_recovery.xml
similarity index 100%
rename from src/cyy_navigation2/bt/navigate_to_pose_w_replanning_and_recovery.xml
rename to src/navigation/cyy_navigation2/bt/navigate_to_pose_w_replanning_and_recovery.xml
diff --git a/src/cyy_navigation2/bt/navigate_to_pose_w_replanning_goal_patience_and_recovery.xml b/src/navigation/cyy_navigation2/bt/navigate_to_pose_w_replanning_goal_patience_and_recovery.xml
similarity index 100%
rename from src/cyy_navigation2/bt/navigate_to_pose_w_replanning_goal_patience_and_recovery.xml
rename to src/navigation/cyy_navigation2/bt/navigate_to_pose_w_replanning_goal_patience_and_recovery.xml
diff --git a/src/cyy_navigation2/bt/navigate_w_recovery_and_replanning_only_if_path_becomes_invalid.xml b/src/navigation/cyy_navigation2/bt/navigate_w_recovery_and_replanning_only_if_path_becomes_invalid.xml
similarity index 100%
rename from src/cyy_navigation2/bt/navigate_w_recovery_and_replanning_only_if_path_becomes_invalid.xml
rename to src/navigation/cyy_navigation2/bt/navigate_w_recovery_and_replanning_only_if_path_becomes_invalid.xml
diff --git a/src/cyy_navigation2/bt/navigate_w_replanning_distance.xml b/src/navigation/cyy_navigation2/bt/navigate_w_replanning_distance.xml
similarity index 100%
rename from src/cyy_navigation2/bt/navigate_w_replanning_distance.xml
rename to src/navigation/cyy_navigation2/bt/navigate_w_replanning_distance.xml
diff --git a/src/cyy_navigation2/bt/navigate_w_replanning_only_if_goal_is_updated.xml b/src/navigation/cyy_navigation2/bt/navigate_w_replanning_only_if_goal_is_updated.xml
similarity index 100%
rename from src/cyy_navigation2/bt/navigate_w_replanning_only_if_goal_is_updated.xml
rename to src/navigation/cyy_navigation2/bt/navigate_w_replanning_only_if_goal_is_updated.xml
diff --git a/src/cyy_navigation2/bt/navigate_w_replanning_only_if_path_becomes_invalid.xml b/src/navigation/cyy_navigation2/bt/navigate_w_replanning_only_if_path_becomes_invalid.xml
similarity index 100%
rename from src/cyy_navigation2/bt/navigate_w_replanning_only_if_path_becomes_invalid.xml
rename to src/navigation/cyy_navigation2/bt/navigate_w_replanning_only_if_path_becomes_invalid.xml
diff --git a/src/cyy_navigation2/bt/navigate_w_replanning_speed.xml b/src/navigation/cyy_navigation2/bt/navigate_w_replanning_speed.xml
similarity index 100%
rename from src/cyy_navigation2/bt/navigate_w_replanning_speed.xml
rename to src/navigation/cyy_navigation2/bt/navigate_w_replanning_speed.xml
diff --git a/src/cyy_navigation2/bt/navigate_w_replanning_time.xml b/src/navigation/cyy_navigation2/bt/navigate_w_replanning_time.xml
similarity index 100%
rename from src/cyy_navigation2/bt/navigate_w_replanning_time.xml
rename to src/navigation/cyy_navigation2/bt/navigate_w_replanning_time.xml
diff --git a/src/cyy_navigation2/bt/odometry_calibration.xml b/src/navigation/cyy_navigation2/bt/odometry_calibration.xml
similarity index 100%
rename from src/cyy_navigation2/bt/odometry_calibration.xml
rename to src/navigation/cyy_navigation2/bt/odometry_calibration.xml
diff --git a/src/cyy_navigation2/config/nav2_params.yaml b/src/navigation/cyy_navigation2/config/nav2_params.yaml
similarity index 100%
rename from src/cyy_navigation2/config/nav2_params.yaml
rename to src/navigation/cyy_navigation2/config/nav2_params.yaml
diff --git a/src/cyy_navigation2/launch/car_bringup.launch.py b/src/navigation/cyy_navigation2/launch/car_bringup.launch.py
similarity index 100%
rename from src/cyy_navigation2/launch/car_bringup.launch.py
rename to src/navigation/cyy_navigation2/launch/car_bringup.launch.py
diff --git a/src/cyy_navigation2/launch/cyy_nav.launch.py b/src/navigation/cyy_navigation2/launch/cyy_nav.launch.py
similarity index 100%
rename from src/cyy_navigation2/launch/cyy_nav.launch.py
rename to src/navigation/cyy_navigation2/launch/cyy_nav.launch.py
diff --git a/src/cyy_navigation2/launch/cyy_nav_box.launch.py b/src/navigation/cyy_navigation2/launch/cyy_nav_box.launch.py
similarity index 100%
rename from src/cyy_navigation2/launch/cyy_nav_box.launch.py
rename to src/navigation/cyy_navigation2/launch/cyy_nav_box.launch.py
diff --git a/src/cyy_navigation2/maps/cyy_map.data b/src/navigation/cyy_navigation2/maps/cyy_map.data
similarity index 100%
rename from src/cyy_navigation2/maps/cyy_map.data
rename to src/navigation/cyy_navigation2/maps/cyy_map.data
diff --git a/src/cyy_navigation2/maps/cyy_map.pgm b/src/navigation/cyy_navigation2/maps/cyy_map.pgm
similarity index 100%
rename from src/cyy_navigation2/maps/cyy_map.pgm
rename to src/navigation/cyy_navigation2/maps/cyy_map.pgm
diff --git a/src/cyy_navigation2/maps/cyy_map.yaml b/src/navigation/cyy_navigation2/maps/cyy_map.yaml
similarity index 100%
rename from src/cyy_navigation2/maps/cyy_map.yaml
rename to src/navigation/cyy_navigation2/maps/cyy_map.yaml
diff --git a/src/cyy_navigation2/maps/cyy_map1.pgm b/src/navigation/cyy_navigation2/maps/cyy_map1.pgm
similarity index 100%
rename from src/cyy_navigation2/maps/cyy_map1.pgm
rename to src/navigation/cyy_navigation2/maps/cyy_map1.pgm
diff --git a/src/cyy_navigation2/maps/cyy_map1.yaml b/src/navigation/cyy_navigation2/maps/cyy_map1.yaml
similarity index 100%
rename from src/cyy_navigation2/maps/cyy_map1.yaml
rename to src/navigation/cyy_navigation2/maps/cyy_map1.yaml
diff --git a/src/cyy_navigation2/package.xml b/src/navigation/cyy_navigation2/package.xml
similarity index 100%
rename from src/cyy_navigation2/package.xml
rename to src/navigation/cyy_navigation2/package.xml
diff --git a/src/cyy_navigation2/param/nav2_params.yaml b/src/navigation/cyy_navigation2/param/nav2_params.yaml
similarity index 100%
rename from src/cyy_navigation2/param/nav2_params.yaml
rename to src/navigation/cyy_navigation2/param/nav2_params.yaml
diff --git a/src/cyy_navigation2/param/slam_toolbox_localization.yaml b/src/navigation/cyy_navigation2/param/slam_toolbox_localization.yaml
similarity index 100%
rename from src/cyy_navigation2/param/slam_toolbox_localization.yaml
rename to src/navigation/cyy_navigation2/param/slam_toolbox_localization.yaml
diff --git a/src/cyy_slamtoolbox/CMakeLists.txt b/src/navigation/cyy_slamtoolbox/CMakeLists.txt
similarity index 100%
rename from src/cyy_slamtoolbox/CMakeLists.txt
rename to src/navigation/cyy_slamtoolbox/CMakeLists.txt
diff --git a/src/cyy_slamtoolbox/config/angular_filter_example.yaml b/src/navigation/cyy_slamtoolbox/config/angular_filter_example.yaml
similarity index 100%
rename from src/cyy_slamtoolbox/config/angular_filter_example.yaml
rename to src/navigation/cyy_slamtoolbox/config/angular_filter_example.yaml
diff --git a/src/cyy_slamtoolbox/config/box_filter_example.yaml b/src/navigation/cyy_slamtoolbox/config/box_filter_example.yaml
similarity index 100%
rename from src/cyy_slamtoolbox/config/box_filter_example.yaml
rename to src/navigation/cyy_slamtoolbox/config/box_filter_example.yaml
diff --git a/src/cyy_slamtoolbox/config/footprint_filter_example.yaml b/src/navigation/cyy_slamtoolbox/config/footprint_filter_example.yaml
similarity index 100%
rename from src/cyy_slamtoolbox/config/footprint_filter_example.yaml
rename to src/navigation/cyy_slamtoolbox/config/footprint_filter_example.yaml
diff --git a/src/cyy_slamtoolbox/config/intensity_filter_example.yaml b/src/navigation/cyy_slamtoolbox/config/intensity_filter_example.yaml
similarity index 100%
rename from src/cyy_slamtoolbox/config/intensity_filter_example.yaml
rename to src/navigation/cyy_slamtoolbox/config/intensity_filter_example.yaml
diff --git a/src/cyy_slamtoolbox/config/laser_filter_config.yaml b/src/navigation/cyy_slamtoolbox/config/laser_filter_config.yaml
similarity index 100%
rename from src/cyy_slamtoolbox/config/laser_filter_config.yaml
rename to src/navigation/cyy_slamtoolbox/config/laser_filter_config.yaml
diff --git a/src/cyy_slamtoolbox/config/mapper_params_lifelong.yaml b/src/navigation/cyy_slamtoolbox/config/mapper_params_lifelong.yaml
similarity index 100%
rename from src/cyy_slamtoolbox/config/mapper_params_lifelong.yaml
rename to src/navigation/cyy_slamtoolbox/config/mapper_params_lifelong.yaml
diff --git a/src/cyy_slamtoolbox/config/mapper_params_localization.yaml b/src/navigation/cyy_slamtoolbox/config/mapper_params_localization.yaml
similarity index 100%
rename from src/cyy_slamtoolbox/config/mapper_params_localization.yaml
rename to src/navigation/cyy_slamtoolbox/config/mapper_params_localization.yaml
diff --git a/src/cyy_slamtoolbox/config/mapper_params_offline.yaml b/src/navigation/cyy_slamtoolbox/config/mapper_params_offline.yaml
similarity index 100%
rename from src/cyy_slamtoolbox/config/mapper_params_offline.yaml
rename to src/navigation/cyy_slamtoolbox/config/mapper_params_offline.yaml
diff --git a/src/cyy_slamtoolbox/config/mapper_params_online_async.yaml b/src/navigation/cyy_slamtoolbox/config/mapper_params_online_async.yaml
similarity index 100%
rename from src/cyy_slamtoolbox/config/mapper_params_online_async.yaml
rename to src/navigation/cyy_slamtoolbox/config/mapper_params_online_async.yaml
diff --git a/src/cyy_slamtoolbox/config/mapper_params_online_sync.yaml b/src/navigation/cyy_slamtoolbox/config/mapper_params_online_sync.yaml
similarity index 100%
rename from src/cyy_slamtoolbox/config/mapper_params_online_sync.yaml
rename to src/navigation/cyy_slamtoolbox/config/mapper_params_online_sync.yaml
diff --git a/src/cyy_slamtoolbox/config/mask_filter_example.yaml b/src/navigation/cyy_slamtoolbox/config/mask_filter_example.yaml
similarity index 100%
rename from src/cyy_slamtoolbox/config/mask_filter_example.yaml
rename to src/navigation/cyy_slamtoolbox/config/mask_filter_example.yaml
diff --git a/src/cyy_slamtoolbox/config/median_filter_example.yaml b/src/navigation/cyy_slamtoolbox/config/median_filter_example.yaml
similarity index 100%
rename from src/cyy_slamtoolbox/config/median_filter_example.yaml
rename to src/navigation/cyy_slamtoolbox/config/median_filter_example.yaml
diff --git a/src/cyy_slamtoolbox/config/median_spatial_filter_example.yaml b/src/navigation/cyy_slamtoolbox/config/median_spatial_filter_example.yaml
similarity index 100%
rename from src/cyy_slamtoolbox/config/median_spatial_filter_example.yaml
rename to src/navigation/cyy_slamtoolbox/config/median_spatial_filter_example.yaml
diff --git a/src/cyy_slamtoolbox/config/multiple_filters_example.yaml b/src/navigation/cyy_slamtoolbox/config/multiple_filters_example.yaml
similarity index 100%
rename from src/cyy_slamtoolbox/config/multiple_filters_example.yaml
rename to src/navigation/cyy_slamtoolbox/config/multiple_filters_example.yaml
diff --git a/src/cyy_slamtoolbox/config/pass_through_example.yaml b/src/navigation/cyy_slamtoolbox/config/pass_through_example.yaml
similarity index 100%
rename from src/cyy_slamtoolbox/config/pass_through_example.yaml
rename to src/navigation/cyy_slamtoolbox/config/pass_through_example.yaml
diff --git a/src/cyy_slamtoolbox/config/polygon_filter_example.yaml b/src/navigation/cyy_slamtoolbox/config/polygon_filter_example.yaml
similarity index 100%
rename from src/cyy_slamtoolbox/config/polygon_filter_example.yaml
rename to src/navigation/cyy_slamtoolbox/config/polygon_filter_example.yaml
diff --git a/src/cyy_slamtoolbox/config/range_filter_example.yaml b/src/navigation/cyy_slamtoolbox/config/range_filter_example.yaml
similarity index 100%
rename from src/cyy_slamtoolbox/config/range_filter_example.yaml
rename to src/navigation/cyy_slamtoolbox/config/range_filter_example.yaml
diff --git a/src/cyy_slamtoolbox/config/scan_blob_filter_example.yaml b/src/navigation/cyy_slamtoolbox/config/scan_blob_filter_example.yaml
similarity index 100%
rename from src/cyy_slamtoolbox/config/scan_blob_filter_example.yaml
rename to src/navigation/cyy_slamtoolbox/config/scan_blob_filter_example.yaml
diff --git a/src/cyy_slamtoolbox/config/sector_filter_example.yaml b/src/navigation/cyy_slamtoolbox/config/sector_filter_example.yaml
similarity index 100%
rename from src/cyy_slamtoolbox/config/sector_filter_example.yaml
rename to src/navigation/cyy_slamtoolbox/config/sector_filter_example.yaml
diff --git a/src/cyy_slamtoolbox/config/shadow_filter_example.yaml b/src/navigation/cyy_slamtoolbox/config/shadow_filter_example.yaml
similarity index 100%
rename from src/cyy_slamtoolbox/config/shadow_filter_example.yaml
rename to src/navigation/cyy_slamtoolbox/config/shadow_filter_example.yaml
diff --git a/src/cyy_slamtoolbox/config/slam_toolbox_default.rviz b/src/navigation/cyy_slamtoolbox/config/slam_toolbox_default.rviz
similarity index 100%
rename from src/cyy_slamtoolbox/config/slam_toolbox_default.rviz
rename to src/navigation/cyy_slamtoolbox/config/slam_toolbox_default.rviz
diff --git a/src/cyy_slamtoolbox/config/speckle_filter_example.yaml b/src/navigation/cyy_slamtoolbox/config/speckle_filter_example.yaml
similarity index 100%
rename from src/cyy_slamtoolbox/config/speckle_filter_example.yaml
rename to src/navigation/cyy_slamtoolbox/config/speckle_filter_example.yaml
diff --git a/src/cyy_slamtoolbox/launch/cyy_slam_toolbox_launch.launch.py b/src/navigation/cyy_slamtoolbox/launch/cyy_slam_toolbox_launch.launch.py
similarity index 100%
rename from src/cyy_slamtoolbox/launch/cyy_slam_toolbox_launch.launch.py
rename to src/navigation/cyy_slamtoolbox/launch/cyy_slam_toolbox_launch.launch.py
diff --git a/src/cyy_slamtoolbox/launch/cyy_slam_toolbox_location.launch.py b/src/navigation/cyy_slamtoolbox/launch/cyy_slam_toolbox_location.launch.py
similarity index 100%
rename from src/cyy_slamtoolbox/launch/cyy_slam_toolbox_location.launch.py
rename to src/navigation/cyy_slamtoolbox/launch/cyy_slam_toolbox_location.launch.py
diff --git a/src/cyy_slamtoolbox/launch/filter.launch.py b/src/navigation/cyy_slamtoolbox/launch/filter.launch.py
similarity index 100%
rename from src/cyy_slamtoolbox/launch/filter.launch.py
rename to src/navigation/cyy_slamtoolbox/launch/filter.launch.py
diff --git a/src/cyy_slamtoolbox/package.xml b/src/navigation/cyy_slamtoolbox/package.xml
similarity index 100%
rename from src/cyy_slamtoolbox/package.xml
rename to src/navigation/cyy_slamtoolbox/package.xml
diff --git a/src/gc_navigation2_real/CMakeLists.txt b/src/navigation/gc_navigation2_real/CMakeLists.txt
similarity index 100%
rename from src/gc_navigation2_real/CMakeLists.txt
rename to src/navigation/gc_navigation2_real/CMakeLists.txt
diff --git a/src/gc_navigation2_real/behavior_tree/nav_to_pose_ackermann.xml b/src/navigation/gc_navigation2_real/behavior_tree/nav_to_pose_ackermann.xml
similarity index 100%
rename from src/gc_navigation2_real/behavior_tree/nav_to_pose_ackermann.xml
rename to src/navigation/gc_navigation2_real/behavior_tree/nav_to_pose_ackermann.xml
diff --git a/src/gc_navigation2_real/config/slam_toolbox_async_real.yaml b/src/navigation/gc_navigation2_real/config/slam_toolbox_async_real.yaml
similarity index 100%
rename from src/gc_navigation2_real/config/slam_toolbox_async_real.yaml
rename to src/navigation/gc_navigation2_real/config/slam_toolbox_async_real.yaml
diff --git a/src/gc_navigation2_real/config/slam_toolbox_localization_real.yaml b/src/navigation/gc_navigation2_real/config/slam_toolbox_localization_real.yaml
similarity index 100%
rename from src/gc_navigation2_real/config/slam_toolbox_localization_real.yaml
rename to src/navigation/gc_navigation2_real/config/slam_toolbox_localization_real.yaml
diff --git a/src/gc_navigation2_real/launch/real_bringup.launch.py b/src/navigation/gc_navigation2_real/launch/real_bringup.launch.py
similarity index 100%
rename from src/gc_navigation2_real/launch/real_bringup.launch.py
rename to src/navigation/gc_navigation2_real/launch/real_bringup.launch.py
diff --git a/src/navigation/gc_navigation2_real/launch/real_mapping.launch.py b/src/navigation/gc_navigation2_real/launch/real_mapping.launch.py
new file mode 100644
index 0000000..a6c6e8d
--- /dev/null
+++ b/src/navigation/gc_navigation2_real/launch/real_mapping.launch.py
@@ -0,0 +1,103 @@
+#!/usr/bin/env python3
+# ============================================================================
+# real_slam_mapping.launch.py
+# 功能:实车 SLAM 建图启动文件(无 Gazebo 仿真)
+#
+# 适用场景:
+# - 在实际场地遥控机器人建立环境地图
+# - 在已有地图基础上增量更新
+# - 单独调试 slam_toolbox 建图参数
+#
+# 架构:
+# origincar_base (bringup) — 底盘串口驱动 + EKF 里程计 + IMU 滤波 + TF
+# lslidar_driver — 镭神激光雷达驱动 → /scan
+# async_slam_toolbox_node — 激光 SLAM 建图(mapping 模式)
+# 发布 map→odom_combined TF
+# 发布 /map 话题(实时地图)
+#
+# 使用方式:
+# ros2 launch gc_navigation2_real real_slam_mapping.launch.py
+#
+# 保存地图:
+# ros2 service call /slam_toolbox/serialize_map slam_toolbox/srv/SerializePoseGraph \
+# "{filename: '/home/guoch/test_ws/src/gc_navigation2_real/maps/my_map'}"
+# ros2 run nav2_map_server map_saver_cli -f
+# ============================================================================
+
+import os
+from ament_index_python.packages import get_package_share_directory
+from launch import LaunchDescription
+from launch.actions import DeclareLaunchArgument, IncludeLaunchDescription
+from launch.substitutions import LaunchConfiguration
+from launch_ros.actions import Node
+from launch.launch_description_sources import PythonLaunchDescriptionSource
+
+
+def generate_launch_description():
+ """生成 LaunchDescription,启动实车底盘 + 雷达 + slam_toolbox 建图"""
+
+ # ============================ 1. 包路径 =========================================
+ pkg_dir = get_package_share_directory('gc_navigation2_real')
+ origincar_base_dir = get_package_share_directory('origincar_base')
+
+ # ============================ 2. 声明启动参数 ====================================
+ use_sim_time = LaunchConfiguration('use_sim_time', default='false')
+
+ # slam_params_file: slam_toolbox 配置文件
+ # slam_params_file = LaunchConfiguration(
+ # 'slam_params_file',
+ # default=os.path.join(pkg_dir, 'config', 'slam_toolbox_async_real.yaml'))
+
+ # map_file_name: 可选,加载已有序列化地图继续建图
+ map_file_name = LaunchConfiguration('map_file_name', default='')
+
+ # ============================ 3. 实车底盘 bringup ===============================
+ # 启动 origincar_base:串口驱动 + EKF + IMU 滤波 + TF 发布
+ # 注:origincar_bringup 已包含 robot_state_publisher + joint_state_publisher + 静态 TF
+ origincar_bringup = IncludeLaunchDescription(
+ PythonLaunchDescriptionSource(
+ [origincar_base_dir, '/launch', '/origincar_bringup.launch.py']),
+ )
+
+ # ============================ 4. 镭神激光雷达驱动 ===============================
+ # 启动 lslidar_driver → /scan 话题
+ lslidar_launch = IncludeLaunchDescription(
+ PythonLaunchDescriptionSource(
+ [get_package_share_directory('lslidar_driver'),
+ '/launch', '/lsn10_launch.py']),
+ )
+
+ # ============================ 5. slam_toolbox 建图节点 ============================
+ slam_toolbox_node = Node(
+ package='slam_toolbox',
+ executable='async_slam_toolbox_node',
+ name='slam_toolbox',
+ output='screen',
+ parameters=[
+ slam_params_file,
+ {
+ 'use_sim_time': False,
+ 'map_file_name': map_file_name,
+ },
+ ],
+ )
+
+ # ============================ 6. 组装 LaunchDescription =========================
+ return LaunchDescription([
+ DeclareLaunchArgument(
+ 'use_sim_time',
+ default_value='false',
+ description='使用仿真时间(实车必须为 false)'),
+ DeclareLaunchArgument(
+ 'slam_params_file',
+ default_value=os.path.join(pkg_dir, 'config', 'slam_toolbox_async_real.yaml'),
+ description='slam_toolbox 参数配置文件路径'),
+ DeclareLaunchArgument(
+ 'map_file_name',
+ default_value='',
+ description='已有序列化地图路径(.posegraph),留空则从零开始建图'),
+
+ origincar_bringup,
+ lslidar_launch,
+ slam_toolbox_node,
+ ])
diff --git a/src/gc_navigation2_real/launch/real_nav2_slam.launch.py b/src/navigation/gc_navigation2_real/launch/real_nav2_slam.launch.py
similarity index 100%
rename from src/gc_navigation2_real/launch/real_nav2_slam.launch.py
rename to src/navigation/gc_navigation2_real/launch/real_nav2_slam.launch.py
diff --git a/src/gc_navigation2_real/launch/real_nav2_slam_online.launch.py b/src/navigation/gc_navigation2_real/launch/real_nav2_slam_online.launch.py
similarity index 100%
rename from src/gc_navigation2_real/launch/real_nav2_slam_online.launch.py
rename to src/navigation/gc_navigation2_real/launch/real_nav2_slam_online.launch.py
diff --git a/src/gc_navigation2_real/launch/real_slam_mapping.launch.py b/src/navigation/gc_navigation2_real/launch/real_slam_mapping.launch.py
similarity index 100%
rename from src/gc_navigation2_real/launch/real_slam_mapping.launch.py
rename to src/navigation/gc_navigation2_real/launch/real_slam_mapping.launch.py
diff --git a/src/gc_navigation2_real/maps/Readme.txt b/src/navigation/gc_navigation2_real/maps/Readme.txt
similarity index 100%
rename from src/gc_navigation2_real/maps/Readme.txt
rename to src/navigation/gc_navigation2_real/maps/Readme.txt
diff --git a/src/gc_navigation2_real/package.xml b/src/navigation/gc_navigation2_real/package.xml
similarity index 100%
rename from src/gc_navigation2_real/package.xml
rename to src/navigation/gc_navigation2_real/package.xml
diff --git a/src/gc_navigation2_real/params/gc_navigation_slam_real.yaml b/src/navigation/gc_navigation2_real/params/gc_navigation_slam_real.yaml
similarity index 100%
rename from src/gc_navigation2_real/params/gc_navigation_slam_real.yaml
rename to src/navigation/gc_navigation2_real/params/gc_navigation_slam_real.yaml
diff --git a/src/gc_navigation2_slamtoolbox/CMakeLists.txt b/src/navigation/gc_navigation2_slamtoolbox/CMakeLists.txt
similarity index 100%
rename from src/gc_navigation2_slamtoolbox/CMakeLists.txt
rename to src/navigation/gc_navigation2_slamtoolbox/CMakeLists.txt
diff --git a/src/gc_navigation2_slamtoolbox/config/mapper_params_lifelong.yaml b/src/navigation/gc_navigation2_slamtoolbox/config/mapper_params_lifelong.yaml
similarity index 100%
rename from src/gc_navigation2_slamtoolbox/config/mapper_params_lifelong.yaml
rename to src/navigation/gc_navigation2_slamtoolbox/config/mapper_params_lifelong.yaml
diff --git a/src/gc_navigation2_slamtoolbox/config/mapper_params_localization.yaml b/src/navigation/gc_navigation2_slamtoolbox/config/mapper_params_localization.yaml
similarity index 100%
rename from src/gc_navigation2_slamtoolbox/config/mapper_params_localization.yaml
rename to src/navigation/gc_navigation2_slamtoolbox/config/mapper_params_localization.yaml
diff --git a/src/gc_navigation2_slamtoolbox/config/mapper_params_offline.yaml b/src/navigation/gc_navigation2_slamtoolbox/config/mapper_params_offline.yaml
similarity index 100%
rename from src/gc_navigation2_slamtoolbox/config/mapper_params_offline.yaml
rename to src/navigation/gc_navigation2_slamtoolbox/config/mapper_params_offline.yaml
diff --git a/src/gc_navigation2_slamtoolbox/config/mapper_params_online_multi_async.yaml b/src/navigation/gc_navigation2_slamtoolbox/config/mapper_params_online_multi_async.yaml
similarity index 100%
rename from src/gc_navigation2_slamtoolbox/config/mapper_params_online_multi_async.yaml
rename to src/navigation/gc_navigation2_slamtoolbox/config/mapper_params_online_multi_async.yaml
diff --git a/src/gc_navigation2_slamtoolbox/config/mapper_params_online_sync.yaml b/src/navigation/gc_navigation2_slamtoolbox/config/mapper_params_online_sync.yaml
similarity index 100%
rename from src/gc_navigation2_slamtoolbox/config/mapper_params_online_sync.yaml
rename to src/navigation/gc_navigation2_slamtoolbox/config/mapper_params_online_sync.yaml
diff --git a/src/gc_navigation2_slamtoolbox/config/nav_through_poses_ackermann.xml b/src/navigation/gc_navigation2_slamtoolbox/config/nav_through_poses_ackermann.xml
similarity index 100%
rename from src/gc_navigation2_slamtoolbox/config/nav_through_poses_ackermann.xml
rename to src/navigation/gc_navigation2_slamtoolbox/config/nav_through_poses_ackermann.xml
diff --git a/src/gc_navigation2_slamtoolbox/config/nav_to_pose_ackermann.xml b/src/navigation/gc_navigation2_slamtoolbox/config/nav_to_pose_ackermann.xml
similarity index 100%
rename from src/gc_navigation2_slamtoolbox/config/nav_to_pose_ackermann.xml
rename to src/navigation/gc_navigation2_slamtoolbox/config/nav_to_pose_ackermann.xml
diff --git a/src/gc_navigation2_slamtoolbox/config/slam_toolbox_async.yaml b/src/navigation/gc_navigation2_slamtoolbox/config/slam_toolbox_async.yaml
similarity index 100%
rename from src/gc_navigation2_slamtoolbox/config/slam_toolbox_async.yaml
rename to src/navigation/gc_navigation2_slamtoolbox/config/slam_toolbox_async.yaml
diff --git a/src/gc_navigation2_slamtoolbox/config/slam_toolbox_localization.yaml b/src/navigation/gc_navigation2_slamtoolbox/config/slam_toolbox_localization.yaml
similarity index 100%
rename from src/gc_navigation2_slamtoolbox/config/slam_toolbox_localization.yaml
rename to src/navigation/gc_navigation2_slamtoolbox/config/slam_toolbox_localization.yaml
diff --git a/src/gc_navigation2_slamtoolbox/config/slam_toolbox_mapping.yaml b/src/navigation/gc_navigation2_slamtoolbox/config/slam_toolbox_mapping.yaml
similarity index 100%
rename from src/gc_navigation2_slamtoolbox/config/slam_toolbox_mapping.yaml
rename to src/navigation/gc_navigation2_slamtoolbox/config/slam_toolbox_mapping.yaml
diff --git a/src/gc_navigation2_slamtoolbox/launch/gc_nav2_with_amcl.launch.py b/src/navigation/gc_navigation2_slamtoolbox/launch/gc_nav2_with_amcl.launch.py
similarity index 100%
rename from src/gc_navigation2_slamtoolbox/launch/gc_nav2_with_amcl.launch.py
rename to src/navigation/gc_navigation2_slamtoolbox/launch/gc_nav2_with_amcl.launch.py
diff --git a/src/gc_navigation2_slamtoolbox/launch/gc_nav2_with_slam.launch.py b/src/navigation/gc_navigation2_slamtoolbox/launch/gc_nav2_with_slam.launch.py
similarity index 100%
rename from src/gc_navigation2_slamtoolbox/launch/gc_nav2_with_slam.launch.py
rename to src/navigation/gc_navigation2_slamtoolbox/launch/gc_nav2_with_slam.launch.py
diff --git a/src/gc_navigation2_slamtoolbox/launch/gc_nav2_with_slam_online.launch.py b/src/navigation/gc_navigation2_slamtoolbox/launch/gc_nav2_with_slam_online.launch.py
similarity index 100%
rename from src/gc_navigation2_slamtoolbox/launch/gc_nav2_with_slam_online.launch.py
rename to src/navigation/gc_navigation2_slamtoolbox/launch/gc_nav2_with_slam_online.launch.py
diff --git a/src/gc_navigation2_slamtoolbox/launch/gc_nav2_with_slam_online_real.launch.py b/src/navigation/gc_navigation2_slamtoolbox/launch/gc_nav2_with_slam_online_real.launch.py
similarity index 100%
rename from src/gc_navigation2_slamtoolbox/launch/gc_nav2_with_slam_online_real.launch.py
rename to src/navigation/gc_navigation2_slamtoolbox/launch/gc_nav2_with_slam_online_real.launch.py
diff --git a/src/gc_navigation2_slamtoolbox/launch/gc_nav_slamtoolbox_lifelong.launch.py b/src/navigation/gc_navigation2_slamtoolbox/launch/gc_nav_slamtoolbox_lifelong.launch.py
similarity index 100%
rename from src/gc_navigation2_slamtoolbox/launch/gc_nav_slamtoolbox_lifelong.launch.py
rename to src/navigation/gc_navigation2_slamtoolbox/launch/gc_nav_slamtoolbox_lifelong.launch.py
diff --git a/src/gc_navigation2_slamtoolbox/launch/gc_nav_slamtoolbox_location.launch.py b/src/navigation/gc_navigation2_slamtoolbox/launch/gc_nav_slamtoolbox_location.launch.py
similarity index 100%
rename from src/gc_navigation2_slamtoolbox/launch/gc_nav_slamtoolbox_location.launch.py
rename to src/navigation/gc_navigation2_slamtoolbox/launch/gc_nav_slamtoolbox_location.launch.py
diff --git a/src/gc_navigation2_slamtoolbox/launch/gc_nav_slamtoolbox_on_async.launch.py b/src/navigation/gc_navigation2_slamtoolbox/launch/gc_nav_slamtoolbox_on_async.launch.py
similarity index 100%
rename from src/gc_navigation2_slamtoolbox/launch/gc_nav_slamtoolbox_on_async.launch.py
rename to src/navigation/gc_navigation2_slamtoolbox/launch/gc_nav_slamtoolbox_on_async.launch.py
diff --git a/src/gc_navigation2_slamtoolbox/launch/gc_slam_mapping.launch.py b/src/navigation/gc_navigation2_slamtoolbox/launch/gc_slam_mapping.launch.py
similarity index 100%
rename from src/gc_navigation2_slamtoolbox/launch/gc_slam_mapping.launch.py
rename to src/navigation/gc_navigation2_slamtoolbox/launch/gc_slam_mapping.launch.py
diff --git a/src/gc_navigation2_slamtoolbox/maps/my_map.data b/src/navigation/gc_navigation2_slamtoolbox/maps/my_map.data
similarity index 100%
rename from src/gc_navigation2_slamtoolbox/maps/my_map.data
rename to src/navigation/gc_navigation2_slamtoolbox/maps/my_map.data
diff --git a/src/gc_navigation2_slamtoolbox/maps/my_map.pgm b/src/navigation/gc_navigation2_slamtoolbox/maps/my_map.pgm
similarity index 100%
rename from src/gc_navigation2_slamtoolbox/maps/my_map.pgm
rename to src/navigation/gc_navigation2_slamtoolbox/maps/my_map.pgm
diff --git a/src/gc_navigation2_slamtoolbox/maps/my_map.posegraph b/src/navigation/gc_navigation2_slamtoolbox/maps/my_map.posegraph
similarity index 100%
rename from src/gc_navigation2_slamtoolbox/maps/my_map.posegraph
rename to src/navigation/gc_navigation2_slamtoolbox/maps/my_map.posegraph
diff --git a/src/gc_navigation2_slamtoolbox/maps/my_map.yaml b/src/navigation/gc_navigation2_slamtoolbox/maps/my_map.yaml
similarity index 100%
rename from src/gc_navigation2_slamtoolbox/maps/my_map.yaml
rename to src/navigation/gc_navigation2_slamtoolbox/maps/my_map.yaml
diff --git a/src/gc_navigation2_slamtoolbox/maps/zhihui.data b/src/navigation/gc_navigation2_slamtoolbox/maps/zhihui.data
similarity index 100%
rename from src/gc_navigation2_slamtoolbox/maps/zhihui.data
rename to src/navigation/gc_navigation2_slamtoolbox/maps/zhihui.data
diff --git a/src/gc_navigation2_slamtoolbox/maps/zhihui.pgm b/src/navigation/gc_navigation2_slamtoolbox/maps/zhihui.pgm
similarity index 100%
rename from src/gc_navigation2_slamtoolbox/maps/zhihui.pgm
rename to src/navigation/gc_navigation2_slamtoolbox/maps/zhihui.pgm
diff --git a/src/gc_navigation2_slamtoolbox/maps/zhihui.posegraph b/src/navigation/gc_navigation2_slamtoolbox/maps/zhihui.posegraph
similarity index 100%
rename from src/gc_navigation2_slamtoolbox/maps/zhihui.posegraph
rename to src/navigation/gc_navigation2_slamtoolbox/maps/zhihui.posegraph
diff --git a/src/gc_navigation2_slamtoolbox/maps/zhihui.yaml b/src/navigation/gc_navigation2_slamtoolbox/maps/zhihui.yaml
similarity index 100%
rename from src/gc_navigation2_slamtoolbox/maps/zhihui.yaml
rename to src/navigation/gc_navigation2_slamtoolbox/maps/zhihui.yaml
diff --git a/src/gc_navigation2_slamtoolbox/package.xml b/src/navigation/gc_navigation2_slamtoolbox/package.xml
similarity index 100%
rename from src/gc_navigation2_slamtoolbox/package.xml
rename to src/navigation/gc_navigation2_slamtoolbox/package.xml
diff --git a/src/gc_navigation2_slamtoolbox/params/gc_navigation_amcl.yaml b/src/navigation/gc_navigation2_slamtoolbox/params/gc_navigation_amcl.yaml
similarity index 100%
rename from src/gc_navigation2_slamtoolbox/params/gc_navigation_amcl.yaml
rename to src/navigation/gc_navigation2_slamtoolbox/params/gc_navigation_amcl.yaml
diff --git a/src/gc_navigation2_slamtoolbox/params/gc_navigation_slam.yaml b/src/navigation/gc_navigation2_slamtoolbox/params/gc_navigation_slam.yaml
similarity index 100%
rename from src/gc_navigation2_slamtoolbox/params/gc_navigation_slam.yaml
rename to src/navigation/gc_navigation2_slamtoolbox/params/gc_navigation_slam.yaml
diff --git a/src/gc_navigation2_slamtoolbox/params/gc_navigation_slam.yaml.bak b/src/navigation/gc_navigation2_slamtoolbox/params/gc_navigation_slam.yaml.bak
similarity index 100%
rename from src/gc_navigation2_slamtoolbox/params/gc_navigation_slam.yaml.bak
rename to src/navigation/gc_navigation2_slamtoolbox/params/gc_navigation_slam.yaml.bak
diff --git a/src/gc_navigation2_slamtoolbox/params/gc_navigation_slam.yaml.bak2 b/src/navigation/gc_navigation2_slamtoolbox/params/gc_navigation_slam.yaml.bak2
similarity index 100%
rename from src/gc_navigation2_slamtoolbox/params/gc_navigation_slam.yaml.bak2
rename to src/navigation/gc_navigation2_slamtoolbox/params/gc_navigation_slam.yaml.bak2
diff --git a/src/gc_navigation2_slamtoolbox/world/zhihui/model.config b/src/navigation/gc_navigation2_slamtoolbox/world/zhihui/model.config
similarity index 100%
rename from src/gc_navigation2_slamtoolbox/world/zhihui/model.config
rename to src/navigation/gc_navigation2_slamtoolbox/world/zhihui/model.config
diff --git a/src/gc_navigation2_slamtoolbox/world/zhihui/model.sdf b/src/navigation/gc_navigation2_slamtoolbox/world/zhihui/model.sdf
similarity index 100%
rename from src/gc_navigation2_slamtoolbox/world/zhihui/model.sdf
rename to src/navigation/gc_navigation2_slamtoolbox/world/zhihui/model.sdf
diff --git a/src/gc_navigation_fish/CMakeLists.txt b/src/navigation/gc_navigation_fish/CMakeLists.txt
similarity index 100%
rename from src/gc_navigation_fish/CMakeLists.txt
rename to src/navigation/gc_navigation_fish/CMakeLists.txt
diff --git a/src/gc_navigation_fish/launch/gc_navigation.launch.py b/src/navigation/gc_navigation_fish/launch/gc_navigation.launch.py
similarity index 100%
rename from src/gc_navigation_fish/launch/gc_navigation.launch.py
rename to src/navigation/gc_navigation_fish/launch/gc_navigation.launch.py
diff --git a/src/gc_navigation_fish/maps/test_map.pgm b/src/navigation/gc_navigation_fish/maps/test_map.pgm
similarity index 100%
rename from src/gc_navigation_fish/maps/test_map.pgm
rename to src/navigation/gc_navigation_fish/maps/test_map.pgm
diff --git a/src/gc_navigation_fish/maps/test_map.yaml b/src/navigation/gc_navigation_fish/maps/test_map.yaml
similarity index 100%
rename from src/gc_navigation_fish/maps/test_map.yaml
rename to src/navigation/gc_navigation_fish/maps/test_map.yaml
diff --git a/src/gc_navigation_fish/package.xml b/src/navigation/gc_navigation_fish/package.xml
similarity index 100%
rename from src/gc_navigation_fish/package.xml
rename to src/navigation/gc_navigation_fish/package.xml
diff --git a/src/gc_navigation_fish/param/gc_navigation.yaml b/src/navigation/gc_navigation_fish/param/gc_navigation.yaml
similarity index 100%
rename from src/gc_navigation_fish/param/gc_navigation.yaml
rename to src/navigation/gc_navigation_fish/param/gc_navigation.yaml
diff --git a/src/gc_navigation_fish/param/navigation_test.yaml b/src/navigation/gc_navigation_fish/param/navigation_test.yaml
similarity index 100%
rename from src/gc_navigation_fish/param/navigation_test.yaml
rename to src/navigation/gc_navigation_fish/param/navigation_test.yaml
diff --git a/src/gc_slam_toolbox_fish/CMakeLists.txt b/src/navigation/gc_slam_toolbox_fish/CMakeLists.txt
similarity index 100%
rename from src/gc_slam_toolbox_fish/CMakeLists.txt
rename to src/navigation/gc_slam_toolbox_fish/CMakeLists.txt
diff --git a/src/gc_slam_toolbox_fish/config/gc_2d.lua b/src/navigation/gc_slam_toolbox_fish/config/gc_2d.lua
similarity index 100%
rename from src/gc_slam_toolbox_fish/config/gc_2d.lua
rename to src/navigation/gc_slam_toolbox_fish/config/gc_2d.lua
diff --git a/src/gc_slam_toolbox_fish/launch/catograph.launch.py b/src/navigation/gc_slam_toolbox_fish/launch/catograph.launch.py
similarity index 100%
rename from src/gc_slam_toolbox_fish/launch/catograph.launch.py
rename to src/navigation/gc_slam_toolbox_fish/launch/catograph.launch.py
diff --git a/src/gc_slam_toolbox_fish/package.xml b/src/navigation/gc_slam_toolbox_fish/package.xml
similarity index 100%
rename from src/gc_slam_toolbox_fish/package.xml
rename to src/navigation/gc_slam_toolbox_fish/package.xml
diff --git a/src/navigation/obstacle_nav2/CMakeLists.txt b/src/navigation/obstacle_nav2/CMakeLists.txt
new file mode 100755
index 0000000..d04e73f
--- /dev/null
+++ b/src/navigation/obstacle_nav2/CMakeLists.txt
@@ -0,0 +1,76 @@
+cmake_minimum_required(VERSION 3.8)
+project(obstacle_nav2)
+
+if(NOT CMAKE_CXX_STANDARD)
+ set(CMAKE_CXX_STANDARD 17)
+endif()
+
+find_package(ament_cmake REQUIRED)
+find_package(nav2_costmap_2d REQUIRED)
+find_package(pluginlib REQUIRED)
+find_package(rclcpp REQUIRED)
+find_package(tf2 REQUIRED)
+find_package(tf2_ros REQUIRED)
+find_package(tf2_geometry_msgs REQUIRED)
+find_package(obstacle_scanner REQUIRED)
+find_package(geometry_msgs REQUIRED)
+find_package(nav_msgs REQUIRED)
+find_package(std_msgs REQUIRED)
+
+add_library(obstacle_array_layer SHARED
+ src/obstacle_array_layer.cpp
+)
+target_include_directories(obstacle_array_layer PUBLIC
+ $
+ $
+)
+ament_target_dependencies(obstacle_array_layer
+ nav2_costmap_2d
+ pluginlib
+ rclcpp
+ tf2
+ tf2_ros
+ tf2_geometry_msgs
+ obstacle_scanner
+ geometry_msgs
+ nav_msgs
+ std_msgs
+)
+
+pluginlib_export_plugin_description_file(nav2_costmap_2d obstacle_nav2_plugins.xml)
+
+install(TARGETS obstacle_array_layer
+ ARCHIVE DESTINATION lib
+ LIBRARY DESTINATION lib
+ RUNTIME DESTINATION bin
+)
+install(DIRECTORY include/ DESTINATION include)
+install(FILES obstacle_nav2_plugins.xml DESTINATION share/${PROJECT_NAME})
+
+if(BUILD_TESTING)
+ find_package(ament_cmake_gtest REQUIRED)
+ ament_add_gtest(test_obstacle_array_layer test/test_obstacle_array_layer.cpp
+ src/obstacle_array_layer.cpp
+ )
+ target_include_directories(test_obstacle_array_layer PRIVATE
+ $
+ )
+ ament_target_dependencies(test_obstacle_array_layer
+ nav2_costmap_2d
+ pluginlib
+ rclcpp
+ tf2
+ tf2_ros
+ tf2_geometry_msgs
+ obstacle_scanner
+ geometry_msgs
+ nav_msgs
+ std_msgs
+ )
+endif()
+
+install(DIRECTORY config launch behavior_tree
+ DESTINATION share/${PROJECT_NAME}
+)
+
+ament_package()
diff --git a/src/navigation/obstacle_nav2/behavior_tree/nav_to_pose_ackermann.xml b/src/navigation/obstacle_nav2/behavior_tree/nav_to_pose_ackermann.xml
new file mode 100755
index 0000000..49ade2e
--- /dev/null
+++ b/src/navigation/obstacle_nav2/behavior_tree/nav_to_pose_ackermann.xml
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/navigation/obstacle_nav2/config/nav2_params.yaml b/src/navigation/obstacle_nav2/config/nav2_params.yaml
new file mode 100755
index 0000000..4f25cdd
--- /dev/null
+++ b/src/navigation/obstacle_nav2/config/nav2_params.yaml
@@ -0,0 +1,319 @@
+# ============================================================================
+# nav2_params.yaml — Odometry-only obstacle navigation
+#
+# No static map, no AMCL, no SLAM.
+# Both costmaps are rolling windows in odom. The launch file can rewrite every
+# global_frame leaf when a different connected odometry frame is required.
+# Global planner: Smac Hybrid A* (Reeds-Shepp)
+# Local controller: MPPI (Ackermann)
+# ============================================================================
+
+bt_navigator:
+ ros__parameters:
+ use_sim_time: False
+ global_frame: odom
+ robot_base_frame: base_footprint
+ odom_topic: /odom_combined
+ bt_loop_duration: 50
+ default_server_timeout: 20
+ # Injected by obstacle_nav2.launch.py from this package's share directory.
+ default_nav_to_pose_bt_xml: ""
+ plugin_lib_names:
+ - nav2_compute_path_to_pose_action_bt_node
+ - nav2_compute_path_through_poses_action_bt_node
+ - nav2_smooth_path_action_bt_node
+ - nav2_follow_path_action_bt_node
+ - nav2_spin_action_bt_node
+ - nav2_wait_action_bt_node
+ - nav2_back_up_action_bt_node
+ - nav2_drive_on_heading_bt_node
+ - nav2_clear_costmap_service_bt_node
+ - nav2_is_stuck_condition_bt_node
+ - nav2_goal_reached_condition_bt_node
+ - nav2_goal_updated_condition_bt_node
+ - nav2_globally_updated_goal_condition_bt_node
+ - nav2_is_path_valid_condition_bt_node
+ - nav2_initial_pose_received_condition_bt_node
+ - nav2_reinitialize_global_localization_service_bt_node
+ - nav2_rate_controller_bt_node
+ - nav2_distance_controller_bt_node
+ - nav2_speed_controller_bt_node
+ - nav2_truncate_path_action_bt_node
+ - nav2_truncate_path_local_action_bt_node
+ - nav2_goal_updater_node_bt_node
+ - nav2_recovery_node_bt_node
+ - nav2_pipeline_sequence_bt_node
+ - nav2_round_robin_node_bt_node
+ - nav2_transform_available_condition_bt_node
+ - nav2_time_expired_condition_bt_node
+ - nav2_path_expiring_timer_condition
+ - nav2_distance_traveled_condition_bt_node
+ - nav2_single_trigger_bt_node
+ - nav2_is_battery_low_condition_bt_node
+ - nav2_navigate_through_poses_action_bt_node
+ - nav2_navigate_to_pose_action_bt_node
+ - nav2_remove_passed_goals_action_bt_node
+ - nav2_planner_selector_bt_node
+ - nav2_controller_selector_bt_node
+ - nav2_goal_checker_selector_bt_node
+ - nav2_controller_cancel_bt_node
+ - nav2_path_longer_on_approach_bt_node
+ - nav2_wait_cancel_bt_node
+ - nav2_spin_cancel_bt_node
+ - nav2_back_up_cancel_bt_node
+ - nav2_drive_on_heading_cancel_bt_node
+
+bt_navigator_rclcpp_node:
+ ros__parameters:
+ use_sim_time: False
+
+controller_server:
+ ros__parameters:
+ use_sim_time: False
+ controller_frequency: 20.0
+ FollowPath:
+ plugin: "nav2_mppi_controller::MPPIController"
+ time_steps: 36
+ model_dt: 0.05
+ batch_size: 1000
+ vx_std: 0.2
+ vy_std: 0.0
+ wz_std: 0.4
+ vx_max: 0.5
+ vx_min: -0.30
+ vy_max: 0.0
+ wz_max: 1.5
+ iteration_count: 1
+ temperature: 0.3
+ gamma: 0.015
+ motion_model: "Ackermann"
+ visualize: false
+ TrajectoryVisualizer:
+ trajectory_step: 5
+ time_step: 3
+ AckermannConstraints:
+ min_turning_r: 0.4
+ critics: ["ConstraintCritic", "CostCritic", "GoalCritic", "GoalAngleCritic", "PathAlignCritic", "PathFollowCritic", "PathAngleCritic", "PreferForwardCritic"]
+ ConstraintCritic:
+ enabled: true
+ cost_power: 1
+ cost_weight: 4.0
+ GoalCritic:
+ enabled: true
+ cost_power: 1
+ cost_weight: 5.0
+ threshold_to_consider: 1.4
+ GoalAngleCritic:
+ enabled: true
+ cost_power: 1
+ cost_weight: 3.0
+ threshold_to_consider: 0.5
+ PreferForwardCritic:
+ enabled: true
+ cost_power: 1
+ cost_weight: 5.0
+ threshold_to_consider: 0.5
+ CostCritic:
+ enabled: true
+ cost_power: 1
+ cost_weight: 3.81
+ critical_cost: 300.0
+ consider_footprint: true
+ collision_cost: 1000000.0
+ near_goal_distance: 1.0
+ trajectory_point_step: 2
+ PathAlignCritic:
+ enabled: true
+ cost_power: 1
+ cost_weight: 14.0
+ max_path_occupancy_ratio: 0.05
+ trajectory_point_step: 4
+ threshold_to_consider: 0.5
+ offset_from_furthest: 20
+ use_path_orientations: false
+ PathFollowCritic:
+ enabled: true
+ cost_power: 1
+ cost_weight: 5.0
+ offset_from_furthest: 5
+ threshold_to_consider: 1.4
+ PathAngleCritic:
+ enabled: true
+ cost_power: 1
+ cost_weight: 2.0
+ offset_from_furthest: 4
+ threshold_to_consider: 0.5
+ max_angle_to_furthest: 1.0
+ forward_preference: true
+
+controller_server_rclcpp_node:
+ ros__parameters:
+ use_sim_time: False
+
+local_costmap:
+ local_costmap:
+ ros__parameters:
+ update_frequency: 5.0
+ publish_frequency: 2.0
+ transform_tolerance: 0.5
+ global_frame: odom
+ robot_base_frame: base_footprint
+ use_sim_time: False
+ rolling_window: true
+ width: 3
+ height: 3
+ resolution: 0.05
+ footprint: "[[0.14, 0.085], [0.14, -0.085], [-0.14, -0.085], [-0.14, 0.085]]"
+ footprint_padding: 0.02
+ track_unknown_space: false
+ plugins: ["obstacle_array_layer", "inflation_layer"]
+ obstacle_array_layer:
+ plugin: "obstacle_nav2::ObstacleArrayLayer"
+ enabled: true
+ topic: /obstacles
+ obstacle_timeout: 0.5
+ transform_tolerance: 0.2
+ default_obstacle_radius: 0.05
+ minimum_obstacle_radius: 0.02
+ maximum_obstacle_radius: 0.50
+ extra_inflation: 0.02
+ inflation_layer:
+ plugin: "nav2_costmap_2d::InflationLayer"
+ cost_scaling_factor: 3.0
+ inflation_radius: 0.55
+ always_send_full_costmap: True
+ local_costmap_client:
+ ros__parameters:
+ use_sim_time: False
+ local_costmap_rclcpp_node:
+ ros__parameters:
+ use_sim_time: False
+
+global_costmap:
+ global_costmap:
+ ros__parameters:
+ update_frequency: 1.0
+ publish_frequency: 1.0
+ transform_tolerance: 0.5
+ global_frame: odom
+ robot_base_frame: base_footprint
+ use_sim_time: False
+ rolling_window: true
+ width: 10
+ height: 10
+ resolution: 0.05
+ footprint: "[[0.14, 0.085], [0.14, -0.085], [-0.14, -0.085], [-0.14, 0.085]]"
+ footprint_padding: 0.02
+ track_unknown_space: false
+ plugins: ["obstacle_array_layer", "inflation_layer"]
+ obstacle_array_layer:
+ plugin: "obstacle_nav2::ObstacleArrayLayer"
+ enabled: true
+ topic: /obstacles
+ obstacle_timeout: 0.5
+ transform_tolerance: 0.2
+ default_obstacle_radius: 0.05
+ minimum_obstacle_radius: 0.02
+ maximum_obstacle_radius: 0.50
+ extra_inflation: 0.02
+ inflation_layer:
+ plugin: "nav2_costmap_2d::InflationLayer"
+ cost_scaling_factor: 3.0
+ inflation_radius: 0.55
+ always_send_full_costmap: True
+ global_costmap_client:
+ ros__parameters:
+ use_sim_time: False
+ global_costmap_rclcpp_node:
+ ros__parameters:
+ use_sim_time: False
+
+planner_server:
+ ros__parameters:
+ planner_plugins: ["GridBased"]
+ use_sim_time: False
+ GridBased:
+ plugin: "nav2_smac_planner/SmacPlannerHybrid"
+ downsample_costmap: false
+ downsampling_factor: 1
+ tolerance: 0.25
+ allow_unknown: false
+ max_iterations: 1000000
+ max_on_approach_iterations: 1000
+ max_planning_time: 5.0
+ motion_model_for_search: "REEDS_SHEPP"
+ angle_quantization_bins: 72
+ analytic_expansion_ratio: 3.5
+ analytic_expansion_max_length: 3.0
+ minimum_turning_radius: 0.40
+ reverse_penalty: 1.3
+ change_penalty: 0.0
+ non_straight_penalty: 1.2
+ cost_penalty: 2.0
+ retrospective_penalty: 0.015
+ # 5 m covers the rolling planning horizon without the startup and memory
+ # cost of the previous 20 m (401-cell) Hybrid-A* lookup table.
+ lookup_table_size: 5.0
+ cache_obstacle_heuristic: false
+ viz_expansions: false
+ smooth_path: True
+ smoother:
+ max_iterations: 1000
+ w_smooth: 0.3
+ w_data: 0.2
+ tolerance: 1.0e-10
+ do_refinement: true
+ refinement_num: 2
+
+planner_server_rclcpp_node:
+ ros__parameters:
+ use_sim_time: False
+
+smoother_server:
+ ros__parameters:
+ use_sim_time: False
+ smoother_plugins: ["simple_smoother"]
+ simple_smoother:
+ plugin: "nav2_smoother::SimpleSmoother"
+ tolerance: 1.0e-10
+ max_its: 1000
+ do_refinement: True
+
+behavior_server:
+ ros__parameters:
+ costmap_topic: local_costmap/costmap_raw
+ footprint_topic: local_costmap/published_footprint
+ cycle_frequency: 10.0
+ behavior_plugins: ["spin", "backup", "wait"]
+ spin:
+ plugin: "nav2_behaviors/Spin"
+ backup:
+ plugin: "nav2_behaviors/BackUp"
+ backup_dist: 0.8
+ backup_speed: 0.18
+ wait:
+ plugin: "nav2_behaviors/Wait"
+ wait_duration: 0.5
+ global_frame: odom
+ robot_base_frame: base_footprint
+ transform_tolerance: 0.5
+ use_sim_time: False
+ simulate_ahead_time: 2.0
+ max_rotational_vel: 1.0
+ min_rotational_vel: 0.4
+ rotational_acc_lim: 3.2
+
+robot_state_publisher:
+ ros__parameters:
+ use_sim_time: False
+
+waypoint_follower:
+ ros__parameters:
+ loop_rate: 20
+ use_sim_time: False
+ stop_on_failure: false
+ waypoint_task_executor_plugin: "wait_at_waypoint"
+ wait_at_waypoint:
+ plugin: "nav2_waypoint_follower::WaitAtWaypoint"
+ enabled: True
+ waypoint_pause_duration: 200
diff --git a/src/navigation/obstacle_nav2/include/obstacle_nav2/obstacle_array_layer.hpp b/src/navigation/obstacle_nav2/include/obstacle_nav2/obstacle_array_layer.hpp
new file mode 100755
index 0000000..ab5cb48
--- /dev/null
+++ b/src/navigation/obstacle_nav2/include/obstacle_nav2/obstacle_array_layer.hpp
@@ -0,0 +1,106 @@
+#ifndef OBSTACLE_NAV2__OBSTACLE_ARRAY_LAYER_HPP_
+#define OBSTACLE_NAV2__OBSTACLE_ARRAY_LAYER_HPP_
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "nav2_costmap_2d/costmap_layer.hpp"
+#include "nav2_costmap_2d/layered_costmap.hpp"
+#include "nav2_costmap_2d/costmap_2d.hpp"
+#include "rclcpp/rclcpp.hpp"
+#include "obstacle_scanner/msg/obstacle_array.hpp"
+#include "tf2_ros/buffer.h"
+
+namespace obstacle_nav2
+{
+
+class ObstacleArrayLayer : public nav2_costmap_2d::CostmapLayer
+{
+public:
+ struct CircleObstacle
+ {
+ double center_x{0.0};
+ double center_y{0.0};
+ double radius{0.0};
+ };
+
+ struct SnapshotBounds
+ {
+ bool valid{false};
+ double min_x{0.0};
+ double min_y{0.0};
+ double max_x{0.0};
+ double max_y{0.0};
+ };
+
+ ObstacleArrayLayer();
+ ~ObstacleArrayLayer() override;
+
+ void onInitialize() override;
+ void updateBounds(
+ double robot_x, double robot_y, double robot_yaw,
+ double * min_x, double * min_y,
+ double * max_x, double * max_y) override;
+ void updateCosts(
+ nav2_costmap_2d::Costmap2D & master_grid,
+ int min_i, int min_j, int max_i, int max_j) override;
+ void reset() override;
+ bool isClearable() override;
+ void activate() override;
+ void deactivate() override;
+
+ static void rasterizeCircle(
+ nav2_costmap_2d::Costmap2D & grid,
+ double cx, double cy, double radius,
+ double resolution, double origin_x, double origin_y);
+ static double clampRadius(double radius, double min_r, double max_r, double default_r);
+ static std::vector transformSnapshot(
+ const obstacle_scanner::msg::ObstacleArray & snapshot,
+ const std::string & global_frame,
+ tf2_ros::Buffer & tf_buffer,
+ const rclcpp::Duration & timeout);
+ static SnapshotBounds applySnapshot(
+ nav2_costmap_2d::Costmap2D & grid,
+ const std::vector & obstacles,
+ double resolution, double origin_x, double origin_y);
+
+private:
+ void obstacleCallback(const obstacle_scanner::msg::ObstacleArray::SharedPtr msg);
+ static SnapshotBounds mergeBounds(
+ const SnapshotBounds & first, const SnapshotBounds & second);
+ void clearExpiredObstacles(const rclcpp::Time & now);
+
+ // Note: node_ (LifecycleNode::WeakPtr), tf_, layered_costmap_, and name_
+ // are inherited from nav2_costmap_2d::Layer.
+
+ rclcpp::Subscription::SharedPtr obstacle_sub_;
+ rclcpp::CallbackGroup::SharedPtr callback_group_;
+ std::unique_ptr callback_executor_;
+ std::thread callback_thread_;
+ std::atomic_bool callback_stop_{false};
+ std::mutex data_mutex_;
+
+ rclcpp::Time last_obstacle_time_;
+ bool has_received_obstacles_{false};
+ SnapshotBounds current_bounds_;
+ SnapshotBounds pending_clear_bounds_;
+ std::uint64_t bounds_generation_{0};
+ std::uint64_t bounds_generation_used_{0};
+
+ std::string topic_;
+ std::string global_frame_;
+ double obstacle_timeout_{0.5};
+ double transform_tolerance_{0.2};
+ double default_obstacle_radius_{0.05};
+ double minimum_obstacle_radius_{0.02};
+ double maximum_obstacle_radius_{0.50};
+ double extra_inflation_{0.02};
+};
+
+} // namespace obstacle_nav2
+
+#endif // OBSTACLE_NAV2__OBSTACLE_ARRAY_LAYER_HPP_
diff --git a/src/navigation/obstacle_nav2/launch/obstacle_nav2.launch.py b/src/navigation/obstacle_nav2/launch/obstacle_nav2.launch.py
new file mode 100755
index 0000000..e98f616
--- /dev/null
+++ b/src/navigation/obstacle_nav2/launch/obstacle_nav2.launch.py
@@ -0,0 +1,145 @@
+#!/usr/bin/env python3
+# ============================================================================
+# obstacle_nav2.launch.py
+#
+# Odometry-only Nav2 navigation using obstacle_scanner detections.
+#
+# Architecture:
+# origincar_base (bringup) — chassis serial driver + EKF + TF
+# lslidar_driver — lidar → /scan
+# obstacle_scanner (optional) — /scan → /obstacles
+# Nav2 navigation_launch — rolling costmaps + Hybrid A* + MPPI
+#
+# No map_server, AMCL, or slam_toolbox in the default path.
+#
+# Usage:
+# ros2 launch obstacle_nav2 obstacle_nav2.launch.py
+# ros2 launch obstacle_nav2 obstacle_nav2.launch.py enable_motion:=true
+# ============================================================================
+
+import os
+from ament_index_python.packages import get_package_share_directory
+from launch import LaunchDescription
+from launch.actions import (
+ DeclareLaunchArgument,
+ GroupAction,
+ IncludeLaunchDescription,
+)
+from launch.conditions import IfCondition
+from launch.launch_description_sources import PythonLaunchDescriptionSource
+from launch.substitutions import LaunchConfiguration, PythonExpression
+from launch_ros.actions import SetRemap
+from nav2_common.launch import RewrittenYaml
+
+
+def generate_launch_description():
+ """Odometry-only obstacle navigation."""
+
+ pkg_dir = get_package_share_directory('obstacle_nav2')
+ nav2_bringup_dir = get_package_share_directory('nav2_bringup')
+
+ # ========================== Launch Arguments ==============================
+ use_sim_time = LaunchConfiguration('use_sim_time', default='false')
+ global_frame = LaunchConfiguration('global_frame', default='odom')
+ use_static_map = LaunchConfiguration('use_static_map', default='false')
+ map_yaml = LaunchConfiguration('map_yaml', default='')
+ enable_motion = LaunchConfiguration('enable_motion', default='false')
+ start_base = LaunchConfiguration('start_base', default='true')
+ start_lidar = LaunchConfiguration('start_lidar', default='true')
+ start_obstacle_scanner = LaunchConfiguration('start_obstacle_scanner', default='true')
+
+ nav2_param_path = os.path.join(pkg_dir, 'config', 'nav2_params.yaml')
+ nav_to_pose_bt_path = os.path.join(
+ pkg_dir, 'behavior_tree', 'nav_to_pose_ackermann.xml')
+ configured_params = RewrittenYaml(
+ source_file=nav2_param_path,
+ root_key='',
+ param_rewrites={
+ 'global_frame': global_frame,
+ 'default_nav_to_pose_bt_xml': nav_to_pose_bt_path,
+ },
+ convert_types=True,
+ )
+ base_cmd_vel_topic = PythonExpression([
+ "'/cmd_vel' if '", enable_motion,
+ "' == 'true' else '/cmd_vel_hardware_disabled'",
+ ])
+
+ # ========================== Base Bringup ==================================
+ origincar_bringup = IncludeLaunchDescription(
+ PythonLaunchDescriptionSource(
+ [get_package_share_directory('origincar_base'),
+ '/launch', '/origincar_bringup.launch.py']),
+ condition=IfCondition(start_base),
+ )
+ safe_base_bringup = GroupAction([
+ SetRemap(src='cmd_vel', dst=base_cmd_vel_topic),
+ origincar_bringup,
+ ])
+
+ # ========================== Lidar Driver ==================================
+ lslidar_launch = IncludeLaunchDescription(
+ PythonLaunchDescriptionSource(
+ [get_package_share_directory('lslidar_driver'),
+ '/launch', '/lsn10_launch.py']),
+ condition=IfCondition(start_lidar),
+ )
+
+ # ========================== Obstacle Scanner ==============================
+ obstacle_scanner_launch = IncludeLaunchDescription(
+ PythonLaunchDescriptionSource(
+ [get_package_share_directory('obstacle_scanner'),
+ '/launch', '/obstacle_scanner.launch.py']),
+ condition=IfCondition(start_obstacle_scanner),
+ )
+
+ # ========================== Nav2 Navigation ===============================
+ navigation_launch = IncludeLaunchDescription(
+ PythonLaunchDescriptionSource(
+ [nav2_bringup_dir, '/launch', '/navigation_launch.py']),
+ launch_arguments={
+ 'use_sim_time': use_sim_time,
+ 'params_file': configured_params,
+ 'autostart': 'true',
+ }.items(),
+ )
+ # ========================== Assembly ======================================
+ return LaunchDescription([
+ DeclareLaunchArgument(
+ 'use_sim_time',
+ default_value='false',
+ description='Use simulation (Gazebo) clock'),
+ DeclareLaunchArgument(
+ 'global_frame',
+ default_value='odom',
+ description='Global frame for both costmaps'),
+ DeclareLaunchArgument(
+ 'use_static_map',
+ default_value='false',
+ description='Reserved: enable static map + AMCL (not yet implemented)'),
+ DeclareLaunchArgument(
+ 'map_yaml',
+ default_value='',
+ description='Reserved: path to map YAML file'),
+ DeclareLaunchArgument(
+ 'enable_motion',
+ default_value='false',
+ description='Route Nav2 cmd_vel to the real base topic'),
+ DeclareLaunchArgument(
+ 'start_base',
+ default_value='true',
+ description='Start the base driver, EKF, and robot TF publishers'),
+ DeclareLaunchArgument(
+ 'start_lidar',
+ default_value='true',
+ description='Start the lidar driver'),
+ DeclareLaunchArgument(
+ 'start_obstacle_scanner',
+ default_value='true',
+ description='Also start the obstacle_scanner node'),
+
+ safe_base_bringup,
+ lslidar_launch,
+ obstacle_scanner_launch,
+ navigation_launch,
+ ])
diff --git a/src/navigation/obstacle_nav2/obstacle_nav2_plugins.xml b/src/navigation/obstacle_nav2/obstacle_nav2_plugins.xml
new file mode 100755
index 0000000..844d060
--- /dev/null
+++ b/src/navigation/obstacle_nav2/obstacle_nav2_plugins.xml
@@ -0,0 +1,7 @@
+
+
+
+ Dynamic obstacle layer from obstacle_scanner/ObstacleArray messages.
+
+
+
diff --git a/src/navigation/obstacle_nav2/package.xml b/src/navigation/obstacle_nav2/package.xml
new file mode 100755
index 0000000..8a1c2fc
--- /dev/null
+++ b/src/navigation/obstacle_nav2/package.xml
@@ -0,0 +1,36 @@
+
+
+
+ obstacle_nav2
+ 0.0.0
+ Odometry-only Nav2 navigation using obstacle_scanner detections as a dynamic costmap layer.
+ sunrise
+ Apache-2.0
+
+ ament_cmake
+
+ rclcpp
+ nav2_costmap_2d
+ pluginlib
+ tf2
+ tf2_ros
+ tf2_geometry_msgs
+ obstacle_scanner
+ geometry_msgs
+ nav_msgs
+ std_msgs
+
+ launch
+ launch_ros
+ nav2_bringup
+ nav2_common
+ origincar_base
+ lslidar_driver
+
+ ament_cmake_gtest
+
+
+ ament_cmake
+
+
+
diff --git a/src/navigation/obstacle_nav2/src/obstacle_array_layer.cpp b/src/navigation/obstacle_nav2/src/obstacle_array_layer.cpp
new file mode 100755
index 0000000..5f959c1
--- /dev/null
+++ b/src/navigation/obstacle_nav2/src/obstacle_array_layer.cpp
@@ -0,0 +1,399 @@
+#include "obstacle_nav2/obstacle_array_layer.hpp"
+
+#include
+#include
+#include
+#include
+#include
+
+#include "geometry_msgs/msg/point_stamped.hpp"
+#include "pluginlib/class_list_macros.hpp"
+#include "tf2_geometry_msgs/tf2_geometry_msgs.hpp"
+
+namespace obstacle_nav2
+{
+
+ObstacleArrayLayer::ObstacleArrayLayer() = default;
+ObstacleArrayLayer::~ObstacleArrayLayer()
+{
+ callback_stop_.store(true, std::memory_order_release);
+ if (callback_executor_) {
+ callback_executor_->cancel();
+ }
+ if (callback_thread_.joinable()) {
+ callback_thread_.join();
+ }
+}
+
+void ObstacleArrayLayer::onInitialize()
+{
+ // CRITICAL: Match the internal costmap size to the master costmap.
+ // CostmapLayer::matchSize() is normally called during updateMap(),
+ // but obstacles arrive during CONFIGURE phase when subscriptions
+ // are active. We need the buffer sized now so rasterizeCircle works.
+ CostmapLayer::onInitialize();
+ if (layered_costmap_) {
+ auto * master = layered_costmap_->getCostmap();
+ resizeMap(
+ master->getSizeInCellsX(), master->getSizeInCellsY(),
+ master->getResolution(),
+ master->getOriginX(), master->getOriginY());
+ }
+
+ // Lock the base-class LifecycleNode weak_ptr
+ auto node = node_.lock();
+ if (!node) {
+ RCLCPP_ERROR(
+ rclcpp::get_logger("obstacle_array_layer"),
+ "Failed to lock node in onInitialize");
+ return;
+ }
+
+ // Parameters (prefixed with layer name per Nav2 convention)
+ node->declare_parameter(name_ + ".enabled", true);
+ node->declare_parameter(name_ + ".topic", std::string("/obstacles"));
+ node->declare_parameter(name_ + ".obstacle_timeout", 0.5);
+ node->declare_parameter(name_ + ".transform_tolerance", 0.2);
+ node->declare_parameter(name_ + ".default_obstacle_radius", 0.05);
+ node->declare_parameter(name_ + ".minimum_obstacle_radius", 0.02);
+ node->declare_parameter(name_ + ".maximum_obstacle_radius", 0.50);
+ node->declare_parameter(name_ + ".extra_inflation", 0.02);
+
+ node->get_parameter(name_ + ".enabled", enabled_);
+ node->get_parameter(name_ + ".topic", topic_);
+ node->get_parameter(name_ + ".obstacle_timeout", obstacle_timeout_);
+ node->get_parameter(name_ + ".transform_tolerance", transform_tolerance_);
+ node->get_parameter(name_ + ".default_obstacle_radius", default_obstacle_radius_);
+ node->get_parameter(name_ + ".minimum_obstacle_radius", minimum_obstacle_radius_);
+ node->get_parameter(name_ + ".maximum_obstacle_radius", maximum_obstacle_radius_);
+ node->get_parameter(name_ + ".extra_inflation", extra_inflation_);
+
+ global_frame_ = layered_costmap_->getGlobalFrameID();
+
+ // Costmap plugins are configured after the parent node has entered its
+ // executor. Use an explicit callback group so this late-created subscription
+ // is guaranteed to be serviced on Humble.
+ callback_group_ = node->create_callback_group(
+ rclcpp::CallbackGroupType::MutuallyExclusive, false);
+ rclcpp::SubscriptionOptions subscription_options;
+ subscription_options.callback_group = callback_group_;
+ obstacle_sub_ = node->create_subscription(
+ topic_, rclcpp::QoS(10).reliable(),
+ std::bind(&ObstacleArrayLayer::obstacleCallback, this, std::placeholders::_1),
+ subscription_options);
+ callback_executor_ = std::make_unique();
+ callback_executor_->add_callback_group(
+ callback_group_, node->get_node_base_interface());
+ callback_stop_.store(false, std::memory_order_release);
+ callback_thread_ = std::thread(
+ [this]() {
+ while (!callback_stop_.load(std::memory_order_acquire)) {
+ callback_executor_->spin_once(std::chrono::milliseconds(100));
+ }
+ });
+
+ current_ = true;
+ has_received_obstacles_ = false;
+ last_obstacle_time_ = node->now();
+}
+
+void ObstacleArrayLayer::activate() {}
+void ObstacleArrayLayer::deactivate() {}
+
+void ObstacleArrayLayer::reset()
+{
+ std::lock_guard lock(data_mutex_);
+ pending_clear_bounds_ = mergeBounds(pending_clear_bounds_, current_bounds_);
+ if (pending_clear_bounds_.valid) {
+ ++bounds_generation_;
+ }
+ resetMap(0, 0, getSizeInCellsX(), getSizeInCellsY());
+ current_bounds_ = SnapshotBounds();
+ has_received_obstacles_ = false;
+}
+
+bool ObstacleArrayLayer::isClearable()
+{
+ return true;
+}
+
+void ObstacleArrayLayer::updateBounds(
+ double robot_x, double robot_y, double /*robot_yaw*/,
+ double * min_x, double * min_y,
+ double * max_x, double * max_y)
+{
+ auto node = node_.lock();
+ if (!node) {return;}
+
+ bool enabled;
+ node->get_parameter_or(name_ + ".enabled", enabled, true);
+ if (!enabled) {return;}
+
+ std::lock_guard lock(data_mutex_);
+ if (layered_costmap_->isRolling()) {
+ updateOrigin(
+ robot_x - getSizeInMetersX() / 2.0,
+ robot_y - getSizeInMetersY() / 2.0);
+ }
+ clearExpiredObstacles(node->now());
+
+ const auto bounds = mergeBounds(current_bounds_, pending_clear_bounds_);
+ if (bounds.valid) {
+ touch(bounds.min_x, bounds.min_y, min_x, min_y, max_x, max_y);
+ touch(bounds.max_x, bounds.max_y, min_x, min_y, max_x, max_y);
+ }
+ bounds_generation_used_ = bounds_generation_;
+}
+
+void ObstacleArrayLayer::updateCosts(
+ nav2_costmap_2d::Costmap2D & master_grid,
+ int min_i, int min_j, int max_i, int max_j)
+{
+ auto node = node_.lock();
+ if (!node) {return;}
+
+ bool enabled;
+ node->get_parameter_or(name_ + ".enabled", enabled, true);
+ if (!enabled) {return;}
+
+ std::lock_guard lock(data_mutex_);
+ updateWithMax(master_grid, min_i, min_j, max_i, max_j);
+ if (bounds_generation_used_ == bounds_generation_) {
+ pending_clear_bounds_ = SnapshotBounds();
+ }
+}
+
+// ============================================================================
+// Static helpers
+// ============================================================================
+
+void ObstacleArrayLayer::rasterizeCircle(
+ nav2_costmap_2d::Costmap2D & grid,
+ double cx, double cy, double radius,
+ double resolution, double origin_x, double origin_y)
+{
+ if (radius < 0.0) {
+ return;
+ }
+
+ // Handle zero radius: mark only the cell containing the center
+ if (radius <= 0.0) {
+ unsigned int cx_cell, cy_cell;
+ if (grid.worldToMap(cx, cy, cx_cell, cy_cell)) {
+ grid.setCost(cx_cell, cy_cell, nav2_costmap_2d::LETHAL_OBSTACLE);
+ }
+ return;
+ }
+
+ double radius_sq = radius * radius;
+ unsigned int size_x = grid.getSizeInCellsX();
+ unsigned int size_y = grid.getSizeInCellsY();
+
+ // Bounding box in cell indices
+ int ix_min = static_cast(std::floor((cx - radius - origin_x) / resolution));
+ int iy_min = static_cast(std::floor((cy - radius - origin_y) / resolution));
+ int ix_max = static_cast(std::ceil((cx + radius - origin_x) / resolution));
+ int iy_max = static_cast(std::ceil((cy + radius - origin_y) / resolution));
+
+ if (ix_min < 0) {ix_min = 0;}
+ if (iy_min < 0) {iy_min = 0;}
+ if (ix_max >= static_cast(size_x)) {ix_max = static_cast(size_x) - 1;}
+ if (iy_max >= static_cast(size_y)) {iy_max = static_cast(size_y) - 1;}
+
+ for (int j = iy_min; j <= iy_max; ++j) {
+ for (int i = ix_min; i <= ix_max; ++i) {
+ double wx, wy;
+ grid.mapToWorld(
+ static_cast(i),
+ static_cast(j), wx, wy);
+ double dx = wx - cx;
+ double dy = wy - cy;
+ if (dx * dx + dy * dy <= radius_sq) {
+ grid.setCost(
+ static_cast(i),
+ static_cast(j),
+ nav2_costmap_2d::LETHAL_OBSTACLE);
+ }
+ }
+ }
+}
+
+double ObstacleArrayLayer::clampRadius(
+ double radius, double min_r, double max_r, double default_r)
+{
+ if (!std::isfinite(radius) || radius <= 0.0) {
+ return default_r;
+ }
+ if (radius < min_r) {
+ return min_r;
+ }
+ if (radius > max_r) {
+ return max_r;
+ }
+ return radius;
+}
+
+std::vector ObstacleArrayLayer::transformSnapshot(
+ const obstacle_scanner::msg::ObstacleArray & snapshot,
+ const std::string & global_frame,
+ tf2_ros::Buffer & tf_buffer,
+ const rclcpp::Duration & timeout)
+{
+ std::vector transformed;
+ transformed.reserve(snapshot.obstacles.size());
+ if (snapshot.obstacles.empty()) {
+ return transformed;
+ }
+
+ const rclcpp::Time stamp(snapshot.header.stamp);
+ const auto transform = timeout.nanoseconds() > 0 ?
+ tf_buffer.lookupTransform(global_frame, snapshot.header.frame_id, stamp, timeout) :
+ tf_buffer.lookupTransform(global_frame, snapshot.header.frame_id, stamp);
+
+ for (const auto & obstacle : snapshot.obstacles) {
+ geometry_msgs::msg::PointStamped input;
+ geometry_msgs::msg::PointStamped output;
+ input.header = snapshot.header;
+ input.point.x = obstacle.center_x;
+ input.point.y = obstacle.center_y;
+ input.point.z = 0.0;
+ tf2::doTransform(input, output, transform);
+ transformed.push_back({output.point.x, output.point.y, obstacle.radius});
+ }
+ return transformed;
+}
+
+ObstacleArrayLayer::SnapshotBounds ObstacleArrayLayer::applySnapshot(
+ nav2_costmap_2d::Costmap2D & grid,
+ const std::vector & obstacles,
+ double resolution, double origin_x, double origin_y)
+{
+ grid.resetMap(0, 0, grid.getSizeInCellsX(), grid.getSizeInCellsY());
+ SnapshotBounds bounds;
+ for (const auto & obstacle : obstacles) {
+ rasterizeCircle(
+ grid, obstacle.center_x, obstacle.center_y, obstacle.radius,
+ resolution, origin_x, origin_y);
+ if (!bounds.valid) {
+ bounds.valid = true;
+ bounds.min_x = obstacle.center_x - obstacle.radius;
+ bounds.min_y = obstacle.center_y - obstacle.radius;
+ bounds.max_x = obstacle.center_x + obstacle.radius;
+ bounds.max_y = obstacle.center_y + obstacle.radius;
+ } else {
+ bounds.min_x = std::min(bounds.min_x, obstacle.center_x - obstacle.radius);
+ bounds.min_y = std::min(bounds.min_y, obstacle.center_y - obstacle.radius);
+ bounds.max_x = std::max(bounds.max_x, obstacle.center_x + obstacle.radius);
+ bounds.max_y = std::max(bounds.max_y, obstacle.center_y + obstacle.radius);
+ }
+ }
+ return bounds;
+}
+
+ObstacleArrayLayer::SnapshotBounds ObstacleArrayLayer::mergeBounds(
+ const SnapshotBounds & first, const SnapshotBounds & second)
+{
+ if (!first.valid) {
+ return second;
+ }
+ if (!second.valid) {
+ return first;
+ }
+ SnapshotBounds merged;
+ merged.valid = true;
+ merged.min_x = std::min(first.min_x, second.min_x);
+ merged.min_y = std::min(first.min_y, second.min_y);
+ merged.max_x = std::max(first.max_x, second.max_x);
+ merged.max_y = std::max(first.max_y, second.max_y);
+ return merged;
+}
+
+void ObstacleArrayLayer::clearExpiredObstacles(const rclcpp::Time & now)
+{
+ if (!has_received_obstacles_) {
+ return;
+ }
+ if ((now - last_obstacle_time_).seconds() <= obstacle_timeout_) {
+ return;
+ }
+ pending_clear_bounds_ = mergeBounds(pending_clear_bounds_, current_bounds_);
+ if (pending_clear_bounds_.valid) {
+ ++bounds_generation_;
+ }
+ resetMap(0, 0, getSizeInCellsX(), getSizeInCellsY());
+ current_bounds_ = SnapshotBounds();
+ has_received_obstacles_ = false;
+}
+
+// ============================================================================
+// Private
+// ============================================================================
+
+void ObstacleArrayLayer::obstacleCallback(
+ const obstacle_scanner::msg::ObstacleArray::SharedPtr msg)
+{
+ auto node = node_.lock();
+ if (!node) {return;}
+
+ bool enabled;
+ node->get_parameter_or(name_ + ".enabled", enabled, true);
+ if (!enabled) {return;}
+
+ std::vector transformed;
+ try {
+ transformed = transformSnapshot(
+ *msg, global_frame_, *tf_,
+ rclcpp::Duration::from_seconds(transform_tolerance_));
+ } catch (const tf2::TransformException & e) {
+ RCLCPP_WARN_THROTTLE(
+ node->get_logger(), *node->get_clock(), 5000,
+ "ObstacleArrayLayer: TF failed at message stamp: %s", e.what());
+ return;
+ }
+
+ std::vector valid_obstacles;
+ valid_obstacles.reserve(transformed.size());
+ const double origin_x = getOriginX();
+ const double origin_y = getOriginY();
+ const double map_x_max = origin_x + getSizeInMetersX();
+ const double map_y_max = origin_y + getSizeInMetersY();
+
+ for (const auto & obs : transformed) {
+ if (!std::isfinite(obs.center_x) || !std::isfinite(obs.center_y)) {
+ continue;
+ }
+
+ double radius = clampRadius(
+ obs.radius, minimum_obstacle_radius_, maximum_obstacle_radius_,
+ default_obstacle_radius_);
+ double effective_r = radius + extra_inflation_;
+
+ if (obs.center_x + effective_r < origin_x ||
+ obs.center_x - effective_r > map_x_max ||
+ obs.center_y + effective_r < origin_y ||
+ obs.center_y - effective_r > map_y_max)
+ {
+ continue;
+ }
+ valid_obstacles.push_back({obs.center_x, obs.center_y, effective_r});
+ }
+
+ {
+ std::lock_guard lock(data_mutex_);
+ const SnapshotBounds previous_bounds = current_bounds_;
+ current_bounds_ = applySnapshot(
+ *this, valid_obstacles, getResolution(), origin_x, origin_y);
+ pending_clear_bounds_ = mergeBounds(pending_clear_bounds_, previous_bounds);
+ if (previous_bounds.valid || current_bounds_.valid) {
+ ++bounds_generation_;
+ }
+ has_received_obstacles_ = current_bounds_.valid;
+ last_obstacle_time_ = node->now();
+ }
+}
+
+} // namespace obstacle_nav2
+
+PLUGINLIB_EXPORT_CLASS(
+ obstacle_nav2::ObstacleArrayLayer,
+ nav2_costmap_2d::Layer)
diff --git a/src/navigation/obstacle_nav2/test/test_obstacle_array_layer.cpp b/src/navigation/obstacle_nav2/test/test_obstacle_array_layer.cpp
new file mode 100755
index 0000000..7b34e03
--- /dev/null
+++ b/src/navigation/obstacle_nav2/test/test_obstacle_array_layer.cpp
@@ -0,0 +1,247 @@
+#include
+
+#include
+#include
+#include
+
+#include "obstacle_nav2/obstacle_array_layer.hpp"
+#include "nav2_costmap_2d/costmap_2d.hpp"
+#include "nav2_costmap_2d/layered_costmap.hpp"
+#include "nav2_util/lifecycle_node.hpp"
+#include "obstacle_scanner/msg/obstacle.hpp"
+#include "obstacle_scanner/msg/obstacle_array.hpp"
+#include "rclcpp/rclcpp.hpp"
+#include "tf2_ros/buffer.h"
+
+using obstacle_nav2::ObstacleArrayLayer;
+
+static constexpr double kResolution = 0.05;
+static constexpr double kOriginX = -1.0;
+static constexpr double kOriginY = -1.0;
+
+// ============================================================================
+// rasterizeCircle tests
+// ============================================================================
+
+TEST(ObstacleArrayLayerTest, RasterizeCircleMarksLethalCells)
+{
+ nav2_costmap_2d::Costmap2D grid(40, 40, kResolution, kOriginX, kOriginY);
+
+ ObstacleArrayLayer::rasterizeCircle(
+ grid, 0.0, 0.0, 0.1,
+ kResolution, kOriginX, kOriginY);
+
+ // center cell should be lethal
+ unsigned int mx, my;
+ grid.worldToMap(0.0, 0.0, mx, my);
+ EXPECT_EQ(grid.getCost(mx, my), nav2_costmap_2d::LETHAL_OBSTACLE);
+
+ // cell outside radius should be free
+ grid.worldToMap(0.3, 0.3, mx, my);
+ EXPECT_EQ(grid.getCost(mx, my), nav2_costmap_2d::FREE_SPACE);
+}
+
+TEST(ObstacleArrayLayerTest, RasterizeCircleDisplacedCenter)
+{
+ nav2_costmap_2d::Costmap2D grid(40, 40, kResolution, kOriginX, kOriginY);
+
+ ObstacleArrayLayer::rasterizeCircle(
+ grid, -0.5, -0.5, 0.15,
+ kResolution, kOriginX, kOriginY);
+
+ unsigned int mx, my;
+ grid.worldToMap(-0.5, -0.5, mx, my);
+ EXPECT_EQ(grid.getCost(mx, my), nav2_costmap_2d::LETHAL_OBSTACLE);
+
+ grid.worldToMap(0.5, 0.5, mx, my);
+ EXPECT_EQ(grid.getCost(mx, my), nav2_costmap_2d::FREE_SPACE);
+}
+
+TEST(ObstacleArrayLayerTest, RasterizeCircleAtCostmapEdge)
+{
+ nav2_costmap_2d::Costmap2D grid(40, 40, kResolution, kOriginX, kOriginY);
+
+ // should not crash or access out of bounds
+ ObstacleArrayLayer::rasterizeCircle(
+ grid, 0.9, 0.9, 0.2,
+ kResolution, kOriginX, kOriginY);
+
+ unsigned int mx, my;
+ grid.worldToMap(0.85, 0.85, mx, my);
+ ASSERT_LT(mx, 40u);
+ ASSERT_LT(my, 40u);
+ EXPECT_EQ(grid.getCost(mx, my), nav2_costmap_2d::LETHAL_OBSTACLE);
+}
+
+TEST(ObstacleArrayLayerTest, RasterizeCircleEntirelyOutsideCostmap)
+{
+ nav2_costmap_2d::Costmap2D grid(40, 40, kResolution, kOriginX, kOriginY);
+
+ EXPECT_NO_THROW(
+ ObstacleArrayLayer::rasterizeCircle(
+ grid, 5.0, 5.0, 0.1,
+ kResolution, kOriginX, kOriginY));
+}
+
+TEST(ObstacleArrayLayerTest, RasterizeCircleZeroRadius)
+{
+ nav2_costmap_2d::Costmap2D grid(40, 40, kResolution, kOriginX, kOriginY);
+
+ ObstacleArrayLayer::rasterizeCircle(
+ grid, 0.0, 0.0, 0.0,
+ kResolution, kOriginX, kOriginY);
+
+ unsigned int mx, my;
+ grid.worldToMap(0.0, 0.0, mx, my);
+ EXPECT_EQ(grid.getCost(mx, my), nav2_costmap_2d::LETHAL_OBSTACLE);
+}
+
+TEST(ObstacleArrayLayerTest, RasterizeCircleAllCellsInRadiusAreLethal)
+{
+ nav2_costmap_2d::Costmap2D grid(40, 40, kResolution, kOriginX, kOriginY);
+
+ ObstacleArrayLayer::rasterizeCircle(
+ grid, 0.0, 0.0, 0.1,
+ kResolution, kOriginX, kOriginY);
+
+ // spot-check cells within radius are lethal
+ unsigned int mx, my;
+ grid.worldToMap(0.05, 0.0, mx, my);
+ EXPECT_EQ(grid.getCost(mx, my), nav2_costmap_2d::LETHAL_OBSTACLE);
+
+ grid.worldToMap(0.0, 0.05, mx, my);
+ EXPECT_EQ(grid.getCost(mx, my), nav2_costmap_2d::LETHAL_OBSTACLE);
+}
+
+// ============================================================================
+// clampRadius tests
+// ============================================================================
+
+TEST(ObstacleArrayLayerTest, ClampRadiusPassesThroughValid)
+{
+ EXPECT_DOUBLE_EQ(ObstacleArrayLayer::clampRadius(0.1, 0.02, 0.5, 0.05), 0.1);
+ EXPECT_DOUBLE_EQ(ObstacleArrayLayer::clampRadius(0.02, 0.02, 0.5, 0.05), 0.02);
+ EXPECT_DOUBLE_EQ(ObstacleArrayLayer::clampRadius(0.5, 0.02, 0.5, 0.05), 0.5);
+}
+
+TEST(ObstacleArrayLayerTest, ClampRadiusFallsBackForNonPositive)
+{
+ constexpr double kDefault = 0.05;
+ EXPECT_DOUBLE_EQ(ObstacleArrayLayer::clampRadius(0.0, 0.02, 0.5, kDefault), kDefault);
+ EXPECT_DOUBLE_EQ(ObstacleArrayLayer::clampRadius(-0.1, 0.02, 0.5, kDefault), kDefault);
+}
+
+TEST(ObstacleArrayLayerTest, ClampRadiusFallsBackForNonFinite)
+{
+ constexpr double kDefault = 0.05;
+ EXPECT_DOUBLE_EQ(ObstacleArrayLayer::clampRadius(NAN, 0.02, 0.5, kDefault), kDefault);
+ EXPECT_DOUBLE_EQ(
+ ObstacleArrayLayer::clampRadius(INFINITY, 0.02, 0.5, kDefault), kDefault);
+ EXPECT_DOUBLE_EQ(
+ ObstacleArrayLayer::clampRadius(-INFINITY, 0.02, 0.5, kDefault), kDefault);
+}
+
+TEST(ObstacleArrayLayerTest, ClampRadiusClampsBelowMinimum)
+{
+ EXPECT_DOUBLE_EQ(ObstacleArrayLayer::clampRadius(0.01, 0.02, 0.5, 0.05), 0.02);
+}
+
+TEST(ObstacleArrayLayerTest, ClampRadiusClampsAboveMaximum)
+{
+ EXPECT_DOUBLE_EQ(ObstacleArrayLayer::clampRadius(0.6, 0.02, 0.5, 0.05), 0.5);
+}
+
+TEST(ObstacleArrayLayerTest, InitializesEnabledStateFromDefaultParameter)
+{
+ auto node = std::make_shared("obstacle_layer_test");
+ nav2_costmap_2d::LayeredCostmap layered_costmap("odom", true, false);
+ layered_costmap.resizeMap(40, 40, kResolution, kOriginX, kOriginY);
+ tf2_ros::Buffer tf_buffer(node->get_clock());
+ ObstacleArrayLayer layer;
+
+ layer.initialize(
+ &layered_costmap, "obstacle_layer", &tf_buffer, node,
+ rclcpp::CallbackGroup::SharedPtr());
+
+ EXPECT_TRUE(layer.isEnabled());
+}
+
+TEST(ObstacleArrayLayerTest, EmptySnapshotClearsPreviousObstacle)
+{
+ nav2_costmap_2d::Costmap2D grid(40, 40, kResolution, kOriginX, kOriginY);
+ const std::vector occupied{{0.0, 0.0, 0.1}};
+
+ const auto occupied_bounds = ObstacleArrayLayer::applySnapshot(
+ grid, occupied, kResolution, kOriginX, kOriginY);
+
+ unsigned int mx, my;
+ ASSERT_TRUE(grid.worldToMap(0.0, 0.0, mx, my));
+ EXPECT_TRUE(occupied_bounds.valid);
+ EXPECT_EQ(grid.getCost(mx, my), nav2_costmap_2d::LETHAL_OBSTACLE);
+
+ const auto cleared_bounds = ObstacleArrayLayer::applySnapshot(
+ grid, {}, kResolution, kOriginX, kOriginY);
+
+ EXPECT_FALSE(cleared_bounds.valid);
+ EXPECT_EQ(grid.getCost(mx, my), nav2_costmap_2d::FREE_SPACE);
+}
+
+TEST(ObstacleArrayLayerTest, TransformSnapshotUsesMessageTimestamp)
+{
+ auto clock = std::make_shared(RCL_SYSTEM_TIME);
+ tf2_ros::Buffer buffer(clock);
+ buffer.setUsingDedicatedThread(true);
+
+ geometry_msgs::msg::TransformStamped old_transform;
+ old_transform.header.frame_id = "odom";
+ old_transform.child_frame_id = "laser_link";
+ old_transform.header.stamp = rclcpp::Time(9, 800000000, RCL_SYSTEM_TIME);
+ old_transform.transform.translation.x = 100.0;
+ old_transform.transform.rotation.w = 1.0;
+ ASSERT_TRUE(buffer.setTransform(old_transform, "test", false));
+
+ geometry_msgs::msg::TransformStamped exact_transform = old_transform;
+ exact_transform.header.stamp = rclcpp::Time(10, 0, RCL_SYSTEM_TIME);
+ exact_transform.transform.translation.x = 2.0;
+ ASSERT_TRUE(buffer.setTransform(exact_transform, "test", false));
+
+ obstacle_scanner::msg::ObstacleArray snapshot;
+ snapshot.header.frame_id = "laser_link";
+ snapshot.header.stamp = exact_transform.header.stamp;
+ obstacle_scanner::msg::Obstacle obstacle;
+ obstacle.center_x = 1.0;
+ obstacle.center_y = 0.5;
+ obstacle.radius = 0.04;
+ snapshot.obstacles.push_back(obstacle);
+
+ const auto transformed = ObstacleArrayLayer::transformSnapshot(
+ snapshot, "odom", buffer, rclcpp::Duration::from_seconds(0.0));
+
+ ASSERT_EQ(transformed.size(), 1u);
+ EXPECT_NEAR(transformed[0].center_x, 3.0, 1e-9);
+ EXPECT_NEAR(transformed[0].center_y, 0.5, 1e-9);
+ EXPECT_NEAR(transformed[0].radius, 0.04, 1e-9);
+}
+
+TEST(ObstacleArrayLayerTest, EmptySnapshotDoesNotRequireTransform)
+{
+ auto clock = std::make_shared(RCL_SYSTEM_TIME);
+ tf2_ros::Buffer buffer(clock);
+ obstacle_scanner::msg::ObstacleArray snapshot;
+ snapshot.header.frame_id = "missing_frame";
+ snapshot.header.stamp = rclcpp::Time(10, 0, RCL_SYSTEM_TIME);
+
+ const auto transformed = ObstacleArrayLayer::transformSnapshot(
+ snapshot, "odom", buffer, rclcpp::Duration::from_seconds(0.0));
+
+ EXPECT_TRUE(transformed.empty());
+}
+
+int main(int argc, char ** argv)
+{
+ rclcpp::init(argc, argv);
+ testing::InitGoogleTest(&argc, argv);
+ const int result = RUN_ALL_TESTS();
+ rclcpp::shutdown();
+ return result;
+}
diff --git a/src/zbw_slamtoolbox/CMakeLists.txt b/src/navigation/zbw_slamtoolbox/CMakeLists.txt
similarity index 100%
rename from src/zbw_slamtoolbox/CMakeLists.txt
rename to src/navigation/zbw_slamtoolbox/CMakeLists.txt
diff --git a/src/zbw_slamtoolbox/config/mapper_params_online_async.yaml b/src/navigation/zbw_slamtoolbox/config/mapper_params_online_async.yaml
similarity index 100%
rename from src/zbw_slamtoolbox/config/mapper_params_online_async.yaml
rename to src/navigation/zbw_slamtoolbox/config/mapper_params_online_async.yaml
diff --git a/src/zbw_slamtoolbox/config/mapper_params_online_sync copy.yaml b/src/navigation/zbw_slamtoolbox/config/mapper_params_online_sync copy.yaml
similarity index 100%
rename from src/zbw_slamtoolbox/config/mapper_params_online_sync copy.yaml
rename to src/navigation/zbw_slamtoolbox/config/mapper_params_online_sync copy.yaml
diff --git a/src/zbw_slamtoolbox/config/navigation.yaml b/src/navigation/zbw_slamtoolbox/config/navigation.yaml
similarity index 100%
rename from src/zbw_slamtoolbox/config/navigation.yaml
rename to src/navigation/zbw_slamtoolbox/config/navigation.yaml
diff --git a/src/zbw_slamtoolbox/launch/navigation.launch.py b/src/navigation/zbw_slamtoolbox/launch/navigation.launch.py
similarity index 100%
rename from src/zbw_slamtoolbox/launch/navigation.launch.py
rename to src/navigation/zbw_slamtoolbox/launch/navigation.launch.py
diff --git a/src/zbw_slamtoolbox/launch/slamtoolbox.launch.py b/src/navigation/zbw_slamtoolbox/launch/slamtoolbox.launch.py
similarity index 100%
rename from src/zbw_slamtoolbox/launch/slamtoolbox.launch.py
rename to src/navigation/zbw_slamtoolbox/launch/slamtoolbox.launch.py
diff --git a/src/zbw_slamtoolbox/package.xml b/src/navigation/zbw_slamtoolbox/package.xml
similarity index 100%
rename from src/zbw_slamtoolbox/package.xml
rename to src/navigation/zbw_slamtoolbox/package.xml