ROS2仿真建图导航全流程:从Gazebo环境到Nav2自主导航 开始之前想先问你一个问题是不是收藏了一堆 ROS 入门资料结果打开全是 Ubuntu 18.04 ROS1 Melodic或者下载好 Gazebo 之后小车模型还没动起来半天就过去了目前 ROS2 已经成为机器人开发的主流选择无论是学校里的课题、毕业设计还是企业里的导航应用绝大多数新项目都开始基于 ROS2 落地。Gazebo 仿真、SLAM 建图、Nav2 自主导航这三件事基本上构成了一个完整机器人项目的“最小闭环”机器人能在仿真环境中感知周围环境、构建地图、规划路径并自主移动。这篇文章会围绕 ROS2 Humble 版本从环境搭建开始逐步完成一个差速小车就是大家喜欢叫“小黄鸭”的那种模型的仿真环境搭建再实现激光 SLAM 建图最后用 Nav2 完成自主导航。整个流程会尽量给出完整命令、配置文件和代码每一步说明“为什么要这么做”。无论你是刚装好 Ubuntu 的零基础新手还是准备把 ROS2 项目直接复用到毕业设计中的同学这篇文章都值得收藏备用。文章内容以 Gazebo RViz2 Nav2 为主线不涉及 TurtleBot3 之外的自定义复杂底盘也不讨论真实硬件部署重点把仿真环境下的整套流程跑通。1. 先弄清几个核心概念很多同学刚接触这套技术栈时经常被 SLAM、导航、仿真这几个词绕晕。先把核心概念理清楚后面操作时会更有方向。1.1 ROS2 是什么ROS2Robot Operating System 2是一套面向机器人开发的分布式通信框架。它不是操作系统而是运行在 Linux 等系统之上的一层中间件负责解决机器人各个模块之间的通信问题。比如激光雷达数据要发给建图算法建图结果要发给导航模块导航结果要发给底盘驱动这些模块之间的数据传递、进程管理、消息定义都是 ROS2 负责的事情。ROS2 相比 ROS1 最大的变化是使用了 DDSData Distribution Service作为底层通信中间件天然支持分布式、实时性和多机通信。对于做毕设或者入门学习的同学来说主要感受到的区别就是启动方式变了不再用 roscore、工作空间编译方式变了用 colcon 而不是 catkin、功能包结构也变了。1.2 Gazebo 仿真环境Gazebo 是一个三维机器人仿真环境内置物理引擎、传感器模型和环境模型。在 Gazebo 里可以给机器人添加激光雷达、摄像头、IMU 等传感器也可以设置墙壁、障碍物、坡度等环境元素。使用仿真环境的最大价值在于在没有实体硬件的情况下可以验证 SLAM 算法、导航算法、路径规划逻辑是否正确。比如你不想直接在小车上烧录代码测试撞墙可以先在 Gazebo 里把参数调好。1.3 SLAM 建图SLAM 全称 Simultaneous Localization and Mapping中文意思是“同时定位与建图”。简单理解就是机器人在一个未知环境中移动时一边确定自己所在的位置一边构建环境地图。在 ROS2 生态中常用的是 Cartographer 和 SLAM Toolbox。本文会以 SLAM Toolbox 为例因为它在 ROS2 中集成度高、配置简洁对新手很友好。建图输出的地图是一个二维栅格地图通常以 pgm 图片和 yaml 配置文件保存这张地图后续会直接交给导航模块使用。1.4 Nav2 自主导航Nav2 是 ROS2 的导航框架功能非常庞大包含全局路径规划、局部路径规划、行为树、代价地图、AMCL 定位等模块。一句话概括它的作用机器人已经有一张地图也知道自己在地图中的位置Nav2 负责规划出一条从当前位置到目标点的路径并控制机器人沿路径安全移动。导航过程中机器人需要避开动态障碍物、在狭窄通道调整方向、到达目标点后停止。这些复杂决策在 Nav2 中通过“行为树”来组织管理。用一张简图理解整个流程Gazebo 仿真环境小车 激光雷达 障碍物 ↓ SLAM 建图SLAM Toolbox RViz2 ↓ 保存地图pgm yaml ↓ Nav2 自主导航地图载入 AMCL 定位 路径规划2. 环境准备从零搭建 ROS2 Humble 开发环境在做任何功能包开发之前先把环境准备好。下面给出的是基于 Ubuntu 22.04 ROS2 Humble 的方案这也是目前稳定性最高、教程最多的组合。如果你想用 Ubuntu 24.04 ROS2 Jazzy部分命令需要微调建议先按 Humble 跑通全流程。2.1 安装 Ubuntu 22.04如果你已经安装好 Ubuntu跳过这一节。如果是从零开始推荐在 Windows 上使用 VMware 或 VirtualBox 安装虚拟机也可以直接双系统安装。虚拟机方式对新手最友好建议分配至少 4 核 CPU、8GB 内存硬盘 60GB 以上因为 Gazebo 和 ROS2 整体占用空间不小。2.2 安装 ROS2 HumbleROS2 官方提供了一键式安装脚本但为了让大家理解每一步这里拆开执行。先设置编码和软件源sudo apt update sudo apt install locales sudo locale-gen en_US en_US.UTF-8 sudo update-locale LC_ALLen_US.UTF-8 LANGen_US.UTF-8 export LANGen_US.UTF-8添加 ROS2 软件源sudo apt install software-properties-common curl sudo add-apt-repository universe sudo curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.key -o /usr/share/keyrings/ros-archive-keyring.gpg写入软件源列表echo deb [arch$(dpkg --print-architecture) signed-by/usr/share/keyrings/ros-archive-keyring.gpg] http://packages.ros.org/ros2/ubuntu $(. /etc/os-release echo $UBUNTU_CODENAME) main | sudo tee /etc/apt/sources.list.d/ros2.list /dev/null更新并安装 ROS2 Humble 桌面版包含 RViz2、演示程序等sudo apt update sudo apt upgrade sudo apt install ros-humble-desktop安装完成后把 ROS2 环境配置写入~/.bashrc这样每次打开终端不用手动 sourceecho source /opt/ros/humble/setup.bash ~/.bashrc source ~/.bashrc验证是否安装成功ros2 --version如果输出类似ros2 0.19.x或更高版本号说明安装成功。2.3 安装 Gazebo 与 Navigation2ROS2 Humble 默认集成了 Gazebo 11即ros-humble-gazebo-ros-pkgs直接安装即可sudo apt install ros-humble-gazebo-ros-pkgs安装 Nav2 导航框架和 SLAM Toolboxsudo apt install ros-humble-nav2-bringup sudo apt install ros-humble-slam-toolbox安装建图、导航过程中需要用到的一些功能包sudo apt install ros-humble-cartographer ros-humble-cartographer-ros sudo apt install ros-humble-navigation2 ros-humble-nav2-bringup sudo apt install ros-humble-turtlebot3-gazebo这里说明一下TurtleBot3 在 Gazebo 中有现成的模型包非常适合做教学演示。本文后面的案例将从 TurtleBot3 的差速小车模型入手你完全可以把它替换成自己的小黄鸭模型或自制底盘模型核心原理一致。2.4 安装开发工具ROS2 中功能包编译使用colcon需要单独安装sudo apt install python3-colcon-common-extensions sudo apt install python3-rosdep sudo apt install gitrosdep用于安装功能包的依赖项后面编译自己写的功能包时非常常用。2.5 创建并初始化工作空间ROS2 的代码组织方式是“工作空间workspace 功能包package”。先创建自己的工作空间mkdir -p ~/ros2_ws/src cd ~/ros2_ws colcon build如果当前还没有任何功能包colcon build会直接构建一个空环境。构建完以后每次打开新终端需要 source 工作空间echo source ~/ros2_ws/install/setup.bash ~/.bashrc source ~/.bashrc到这里环境准备基本完成。接下来我们创建一个自己的 ROS2 功能包为后面编写小车模型和控制逻辑做准备。3. 创建 ROS2 功能包写第一个可运行节点很多入门教程会在这一步直接让你启动 TurtleBot3 的 Gazebo 仿真但那样很难理解功能包的组织结构。所以我们先手动创建一个功能包写一个最简单的节点把“编译-运行-通信”这个链路理解清楚。3.1 功能包结构说明在 ROS2 中功能包是最基本的组织单元。一个 Python 功能包通常包含my_robot/ ├── package.xml # 功能包元信息名称、依赖、版本 ├── setup.py # Python 包的安装配置仅 Python 功能包 ├── setup.cfg # 插件入口配置 ├── resource/ # 资源标记文件 ├── launch/ # 存放 launch 文件启动多个节点 ├── urdf/ # 存放机器人模型文件URDF/Xacro ├── config/ # 存放参数配置文件 └── my_robot/ # Python 包目录存放节点代码3.2 创建功能包用ros2 pkg create命令创建功能包cd ~/ros2_ws/src ros2 pkg create my_robot --build-type ament_python --dependencies rclpy geometry_msgs nav_msgs sensor_msgs tf2_ros这个命令会自动生成一个 Python 功能包并声明依赖rclpyROS2 Python客户端库、geometry_msgs几何消息、nav_msgs导航消息、sensor_msgs传感器消息、tf2_rosTF 坐标变换。3.3 编写第一个发布者节点在my_robot/my_robot/目录下创建一个publisher_node.py文件写一个最简单的速度发布器#!/usr/bin/env python3 # 文件路径src/my_robot/my_robot/publisher_node.py import rclpy from rclpy.node import Node from geometry_msgs.msg import Twist class VelocityPublisher(Node): def __init__(self): super().__init__(velocity_publisher) self.publisher_ self.create_publisher(Twist, /cmd_vel, 10) self.timer self.create_timer(1.0, self.publish_velocity) def publish_velocity(self): msg Twist() msg.linear.x 0.2 msg.angular.z 0.0 self.publisher_.publish(msg) self.get_logger().info(Publishing: linear.x%.2f, angular.z%.2f % (msg.linear.x, msg.angular.z)) def main(argsNone): rclpy.init(argsargs) node VelocityPublisher() rclpy.spin(node) node.destroy_node() rclpy.shutdown() if __name__ __main__: main()这段代码逻辑很简单创建一个节点每隔 1 秒向/cmd_vel话题发布一个速度消息消息内容为线速度 0.2 m/s、角速度 0 rad/s。需要注意的是/cmd_vel是 ROS2 中底盘速度控制的约定话题格式是geometry_msgs/Twist。Gazebo 中的差速小车插件会订阅这个话题从而驱动机器人移动。3.4 配置 setup.py 与 setup.cfg修改setup.py把入口点配置进去# 文件路径src/my_robot/setup.py from setuptools import setup package_name my_robot setup( namepackage_name, version0.0.0, packages[package_name], data_files[ (share/ament_index/resource_index/packages, [resource/ package_name]), (share/ package_name, [package.xml]), ], install_requires[setuptools], zip_safeTrue, maintaineryour_name, maintainer_emailyour_emailexample.com, descriptionMy robot package for ROS2, licenseApache-2.0, entry_points{ console_scripts: [ velocity_publisher my_robot.publisher_node:main, ], }, )3.5 编译并运行cd ~/ros2_ws colcon build --packages-select my_robot source install/setup.bash ros2 run my_robot velocity_publisher如果终端中持续输出Publishing: linear.x0.20, angular.z0.00说明节点运行正常。这时再开一个终端用ros2 topic echo查看话题消息source ~/.bashrc ros2 topic echo /cmd_vel你会看到类似下面的输出linear: x: 0.2 y: 0.0 z: 0.0 angular: x: 0.0 y: 0.0 z: 0.0 ---到这里ROS2 的“创建功能包 → 编写节点 → 编译 → 运行 → 查看话题”基础链路已经走通。接下来进入重点在 Gazebo 中搭建仿真环境。4. 仿真环境搭建URDF 模型 Gazebo RViz2Gazebo 仿真需要一个机器人模型。这里使用 TurtleBot3 的模型示例它自带差速驱动插件和激光雷达插件非常适合 SLAM 和导航演示。官方提供了一键式启动方式我们先从最简单的启动命令开始再看如何拆解模型文件。4.1 一键启动 TurtleBot3 仿真安装完ros-humble-turtlebot3-gazebo后先设置模型环境变量echo export TURTLEBOT3_MODELburger ~/.bashrc source ~/.bashrc启动一个空的 Gazebo 世界ros2 launch turtlebot3_gazebo turtlebot3_world.launch.py这个命令会同时启动Gazebo 仿真环境加载 TurtleBot3 Burger 模型机器人模型中的激光雷达传感器发布/scan话题机器人模型中的 TF 坐标变换发布odom、base_link、laser等坐标系。启动完成后Gazebo 窗口中应当能看到一个小车模型周围有墙壁和障碍物。RViz2 也会自动打开可以看到激光雷达的点云数据。如果 Gazebo 窗口长时间不出现常见原因是显卡兼容性问题可以尝试在终端设置export LIBGL_ALWAYS_SOFTWARE1再重新启动。4.2 让小车动起来在启动 TurtleBot3 仿真的同时打开另一个终端发布速度指令ros2 topic pub --rate 1 /cmd_vel geometry_msgs/msg/Twist {linear: {x: 0.15, y: 0.0, z: 0.0}, angular: {x: 0.0, y: 0.0, z: 0.2}}观察 Gazebo 窗口小车应该会边前进边转弯。这个操作验证了仿真环境中的差速驱动插件正常工作。按CtrlC停止发布。4.3 自定义模型的思路如果你不想使用 TurtleBot3 的默认模型也可以自己写 URDF/Xacro 文件。这里以一个极简差速小车为例展示模型文件的基本结构。在my_robot/urdf/目录下创建my_robot.urdf.xacro?xml version1.0? robot namemy_robot xmlns:xacrohttp://www.ros.org/wiki/xacro !-- 底盘 -- link namebase_link visual geometry box size0.3 0.2 0.08/ /geometry origin rpy0 0 0 xyz0 0 0.04/ /visual collision geometry box size0.3 0.2 0.08/ /geometry origin rpy0 0 0 xyz0 0 0.04/ /collision inertial mass value2.0/ inertia ixx0.01 iyy0.01 izz0.01 ixy0.0 ixz0.0 iyz0.0/ /inertial /link !-- 激光雷达 -- link namelaser_link visual geometry cylinder radius0.03 length0.02/ /geometry origin rpy0 0 0 xyz0 0 0.09/ /visual /link joint namelaser_joint typefixed parent linkbase_link/ child linklaser_link/ origin xyz0 0 0.08 rpy0 0 0/ /joint /robot这只是一个最简单的模型描述没有添加 Gazebo 插件差速驱动和激光雷达插件。实际项目中还需要在模型文件尾部添加gazebo标签引入插件。考虑到篇幅这里不展开全部插件配置重点是想让大家理解URDF/Xacro 描述的只是机器人长什么样而 Gazebo 插件才决定它在仿真中能不能动、能不能感知环境。4.4 使用 RViz2 查看传感器数据RViz2 是 ROS2 的可视化工具。在仿真运行过程中打开一个新终端ros2 run rviz2 rviz2在 RViz2 窗口中需要手动添加若干个显示项显示项话题/类型作用RobotModel/robot_description显示机器人模型LaserScan/scan显示激光雷达点云TF/tf显示坐标系变换Map/map显示地图建图或导航时使用添加 LaserScan 后需要把 Fixed Frame 设置为base_link或odom否则可能没有数据。这个设置项在 RViz2 左上角的 Global Options 里。到这里仿真环境已经搭建完成。接下来实现 SLAM 建图。5. SLAM 建图让机器人构建环境地图机器人在未知环境中移动通过激光雷达扫描周围环境逐步构建出环境地图。这个过程就是 SLAM。5.1 为什么要用 SLAM ToolboxROS2 中建图方案比较多主流的有Cartographer谷歌开源方案精度高但配置复杂特别依赖调参SLAM Toolbox专为 ROS2 设计的异步 SLAM 库配置简洁适合入门和学习基于 GMapping 的移植版本ROS1 时代常用ROS2 中整洁度不高。从学习成本角度推荐使用 SLAM Toolbox。它支持在线建图也能在导航时提供持续性地图匹配。5.2 启动仿真环境先把小车放置在 Gazebo 世界环境中。前面已经启动过一次 TurtleBot3 世界如果关掉了重新启动即可ros2 launch turtlebot3_gazebo turtlebot3_world.launch.py5.3 启动 SLAM Toolbox 在线建图新建一个终端启动 SLAM Toolbox 节点ros2 run slam_toolbox async_slam_toolbox_node为了让这个节点正确工作需要给它提供必要的参数激光雷达话题名称、机器人坐标系、里程计坐标系等。更推荐的做法是写一个 launch 文件。在my_robot/launch/目录下创建slam_launch.py# 文件路径src/my_robot/launch/slam_launch.py import os from launch import LaunchDescription from launch.actions import DeclareLaunchArgument, ExecuteProcess from launch.substitutions import LaunchConfiguration from launch_ros.actions import Node def generate_launch_description(): use_sim_time LaunchConfiguration(use_sim_time, defaulttrue) return LaunchDescription([ DeclareLaunchArgument( use_sim_time, default_valuetrue, descriptionUse simulation time if true), Node( packageslam_toolbox, executableasync_slam_toolbox_node, nameslam_toolbox, outputscreen, parameters[{ use_sim_time: use_sim_time, base_frame: base_link, odom_frame: odom, map_frame: map, scan_topic: /scan, mode: mapping, }], ), ])启动方式改为ros2 launch my_robot slam_launch.py注意launch 文件中的包名必须是my_robot而且 launch 目录需要在setup.py的data_files中声明否则ros2 launch可能找不到文件。修改setup.py在data_files中添加 launch 目录data_files[ (share/ament_index/resource_index/packages, [resource/ package_name]), (share/ package_name, [package.xml]), (share/ package_name /launch, [launch/slam_launch.py]), ],5.4 保存地图建图时手动发布速度指令让小车在地图中缓慢穿行。RViz2 中可以实时观察地图构建情况。地图构建完成后执行ros2 run nav2_map_server map_saver_cli -f ~/ros2_ws/map这条命令会在~/ros2_ws/目录下生成两个文件map.pgm栅格地图图片map.yaml地图描述文件包含分辨率、原点坐标、占据概率阈值等参数。查看map.yamlimage: map.pgm mode: trinary resolution: 0.05 origin: [-4.0, -4.0, 0.0] negate: 0 occupied_thresh: 0.65 free_thresh: 0.25其中resolution表示每个像素对应的实际距离米origin表示地图左下角在世界坐标系中的位置。导航时Nav2 会读取这个文件把地图加载到代价地图中。5.5 建图过程中的操作技巧控制小车速度不要过快建议线速度 0.15 m/s 以下角速度 0.3 rad/s 以下遇到回环时绕一圈回到之前经过的位置可以停下几秒钟让 SLAM 算法完成回环检测地图边缘不需要完全闭合但主要的墙壁、障碍物边界必须清晰如果发现地图明显漂移优先检查 TF 是否正确发布特别是odom → base_link的变换。6. 自主导航Nav2 完整接入地图构建完成后接下来就是重头戏让机器人在已经构建好的地图中自主导航到目标点。这涉及 Nav2 框架的核心模块定位AMCL、全局路径规划、局部路径规划、代价地图更新等。6.1 Nav2 的核心模块Nav2 的架构可以粗略分成三层模块作用配置项Map Server加载静态地图提供/map话题map 文件路径AMCL蒙特卡洛定位在地图中估计机器人位置粒子数量、激光模型参数Planner全局路径规划搜索从起点到终点的路径planner_pluginsController局部路径规划沿全局路径行驶并避障controller_pluginsBehavior Tree组织导航行为决定导航过程中先做什么再做什么bt_navigator 配置在 Nav2 的配置中最核心的是nav2_params.yaml文件。这个文件包含 Nav2 所有模块的参数是整个导航系统的心脏。6.2 编写 Nav2 参数文件在my_robot/config/目录下创建nav2_params.yaml。下面给出一个精简但能运行的参数配置# 文件路径src/my_robot/config/nav2_params.yaml amcl: ros__parameters: use_sim_time: True alpha1: 0.2 alpha2: 0.2 alpha3: 0.2 alpha4: 0.2 alpha5: 0.2 base_frame_id: base_link global_frame_id: map lambda_short: 0.1 laser_likelihood_max_dist: 2.0 laser_max_range: 100.0 laser_min_range: -1.0 laser_model_type: likelihood_field max_beams: 60 max_particles: 2000 min_particles: 200 odom_frame_id: odom pf_err: 0.05 pf_z: 0.99 recovery_alpha_fast: 0.0 recovery_alpha_slow: 0.0 resample_interval: 1 robot_model_type: differential save_pose_rate: 0.5 sigma_hit: 0.2 tf_broadcast: true transform_tolerance: 1.0 update_min_a: 0.2 update_min_d: 0.25 z_hit: 0.5 z_max: 0.05 z_rand: 0.5 z_short: 0.05 scan_topic: /scan bt_navigator: ros__parameters: use_sim_time: True global_frame: map robot_base_frame: base_link odom_topic: /odom bt_loop_duration: 10 default_bt_xml_filename: navigate_to_pose_w_replanning_and_recovery.xml plugin_lib_names: - nav2_compute_path_to_pose_action_bt_node - nav2_follow_path_action_bt_node - nav2_back_up_action_bt_node - nav2_spin_action_bt_node - nav2_wait_action_bt_node - nav2_assisted_teleop_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_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_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_distance_traveled_condition_bt_node controller_server: ros__parameters: use_sim_time: True controller_frequency: 20.0 min_x_velocity_threshold: 0.001 min_y_velocity_threshold: 0.001 min_theta_velocity_threshold: 0.001 progress_checker_plugins: [progress_checker] goal_checker_plugins: [general_goal_checker] controller_plugins: [FollowPath] progress_checker: plugin: nav2_controller::SimpleProgressChecker required_movement_radius: 0.5 movement_time_allowance: 10.0 general_goal_checker: stateful: True xy_goal_tolerance: 0.25 yaw_goal_tolerance: 0.25 FollowPath: plugin: nav2_rotation_shim_controller::RotationShimController primary_controller: dwb_core::DWBLocalPlanner angular_dist_threshold: 0.785 forward_sampling_distance: 0.5 rotate_to_heading_angular_vel: 1.8 transform_tolerance: 0.1 # DWB 参数 min_vel_x: 0.0 min_vel_y: 0.0 max_vel_x: 0.22 max_vel_y: 0.0 max_vel_theta: 1.0 min_speed_xy: 0.0 max_speed_xy: 0.22 min_speed_theta: 0.0 # 采样参数 sim_time: 1.7 vx_samples: 20 vy_samples: 5 vtheta_samples: 20 path_distance_bias: 32.0 goal_distance_bias: 24.0 occdist_scale: 0.01 forward_point_distance: 0.325 stop_on_obstacle: false scaling_dist: 0.3 max_scaling_factor: 0.2 publish_trajectory_cloud: true local_costmap: local_costmap: ros__parameters: use_sim_time: True robot_base_frame: base_link global_frame: odom rolling_window: true width: 3 height: 3 resolution: 0.05 footprint: [[-0.16, -0.16], [-0.16, 0.16], [0.16, 0.16], [0.16, -0.16]] plugins: [voxel_layer, inflation_layer] inflation_layer: plugin: nav2_costmap_2d::InflationLayer cost_scaling_factor: 3.0 inflation_radius: 0.55 voxel_layer: plugin: nav2_costmap_2d::VoxelLayer enabled: True publish_voxel_map: True origin_z: 0.0 z_resolution: 0.2 z_voxels: 16 max_obstacle_height: 2.0 mark_threshold: 0 observation_sources: scan scan: topic: /scan max_obstacle_height: 2.0 clearing: True marking: True data_type: LaserScan raytrace_max_range: 3.0 raytrace_min_range: 0.1 obstacle_max_range: 2.5 obstacle_min_range: 0.1 global_costmap: global_costmap: ros__parameters: use_sim_time: True robot_base_frame: base_link global_frame: map robot_radius: 0.2 resolution: 0.05 track_unknown_space: true plugins: [static_layer, inflation_layer] static_layer: plugin: nav2_costmap_2d::StaticLayer map_subscribe_transient_local: True inflation_layer: plugin: nav2_costmap_2d::InflationLayer cost_scaling_factor: 3.0 inflation_radius: 0.55 map_server: ros__parameters: use_sim_time: True yaml_filename: /home/your_name/ros2_ws/map/map.yaml这个文件看起来很长但真正需要关注的就是几个关键点controller_server中的max_vel_x和max_vel_theta控制小车最大行驶速度和旋转速度local_costmap和global_costmap中的inflation_radius控制膨胀半径值越大路径离障碍物越远map_server中的yaml_filename必须改成你自己的地图路径。6.3 编写导航启动文件在my_robot/launch/目录下创建navigation_launch.py一次性启动 Nav2 的各个模块# 文件路径src/my_robot/launch/navigation_launch.py import os from ament_index_python.packages import get_package_share_directory from launch import LaunchDescription from launch.actions import DeclareLaunchArgument from launch.substitutions import LaunchConfiguration from launch_ros.actions import Node def generate_launch_description(): use_sim_time LaunchConfiguration(use_sim_time, defaulttrue) map_yaml_path LaunchConfiguration(map_yaml_path, defaultos.path.join( os.path.expanduser(~), ros2_ws, map, map.yaml)) nav2_params_file os.path.join( get_package_share_directory(my_robot), config, nav2_params.yaml) return LaunchDescription([ DeclareLaunchArgument( map_yaml_path, default_valuemap_yaml_path, descriptionFull path to map yaml file), # 地图服务 Node( packagenav2_map_server, executablemap_server, namemap_server, outputscreen, parameters[{use_sim_time: use_sim_time, yaml_filename: map_yaml_path}], ), # AMCL 定位 Node( packagenav2_amcl, executableamcl, nameamcl, outputscreen, parameters[os.path.join(get_package_share_directory(my_robot), config, nav2_params.yaml)], ), # 生命周期管理 Node( packagenav2_lifecycle_manager, executablelifecycle_manager, namelifecycle_manager_navigation, outputscreen, parameters[{use_sim_time: use_sim_time, autostart: True, node_names: [map_server, amcl, controller_server, planner_server, bt_navigator]}], ), ])注意这段代码使用了nav2_params.yaml中多个节点的共享参数启动时 AMCL 等节点会从同一个 yaml 文件中读取各自命名空间下的参数。因此nav2_params.yaml中需要包含planner_server和bt_navigator等节点的配置前面给出的配置已覆盖大部分核心参数实际使用时可结合官方参数文件补全。还需要把config目录在setup.py中声明data_files[ ... (share/ package_name /config, [config/nav2_params.yaml]), (share/ package_name /launch, [launch/slam_launch.py, launch/navigation_launch.py]), ],6.4 启动自主导航导航必须在 Gazebo 仿真和 SLAM 建图完成之后进行。如果之前已经退出按顺序重新执行终端 1 —— 启动仿真环境ros2 launch turtlebot3_gazebo turtlebot3_world.launch.py终端 2 —— 启动导航ros2 launch my_robot navigation_launch.py终端 3 —— 打开 RViz2ros2 run rviz2 rviz2在 RViz2 中把 Fixed Frame 设置为map添加 RobotModel、Map、LaserScan、TF、Path、ParticleCloud 等显示项点击顶部菜单栏的2D Pose Estimate在地图中点击并拖动手动给机器人一个初始位置估计。这一步非常重要如果初始位置给得不准后续导航定位会失败点击Nav2 Goal在地图上选择一个目标点Nav2 会开始规划路径并控制小车移动。6.5 导航过程中的关键技术点AMCL 定位粒子收敛后RViz2 中的绿色箭头会聚集到机器人真实位置附近说明定位成功如果机器人导航方向不对可能是初始位姿给反了重新用2D Pose Estimate校正导航过程中遇到动态障碍物局部代价地图会实时更新机器人会规划局部避障路径目标点设置超出地图范围时导航会失败需要在 RViz2 中把目标点设置在地图内部。7. 导航参数调优从“能跑”到“跑得好”很多同学完成上面的步骤后发现机器人虽然能导航但路径很绕、速度很慢或者在窄通道里反复横跳。这些都是参数问题。7.1 速度参数调节在nav2_params.yaml的controller_server中FollowPath: min_vel_x: 0.0 max_vel_x: 0.22 max_vel_theta: 1.0如果觉得机器人太慢可以适当调大max_vel_x如果觉得转弯太猛可以调小max_vel_theta。7.2 膨胀半径调节inflation_radius越大机器人离障碍物越远路径越保守inflation_radius越小机器人越容易贴近障碍物穿过窄缝但碰撞风险增大。TurtleBot3 这类小车建议inflation_radius设置为 0.2 ~ 0.55 之间。如果你自己的机器人宽度更大需要按比例调整。7.3 代价地图的 footprint 设置footprint参数描述机器人在地图上的轮廓单位是米。如果设置得比实际模型小机器人可能会在仿真中穿过墙壁设置得过大机器人在窄通道中会明显感觉无法通行。TurtleBot3 Burger 使用footprint: [[-0.16, -0.16], [-0.16, 0.16], [0.16, 0.16], [0.16, -0.16]]这表示一个边长 0.32 米的正方形轮廓。7.4 定位失败时的恢复策略如果 AMCL 粒子发散RViz2 中粒子非常分散说明定位丢失。解决方式停止导航重新用2D Pose Estimate手动指定初始位置调大max_particles例如改为 3000提高定位精度调小update_min_a和update_min_d让定位更新频率更高但计算量也会增加。8. 常见问题与排查思路下面整理 ROS2 Gazebo SLAM Nav2 常见问题做一个排查表格。遇到问题时可以在命令行中配合ros2 topic list、ros2 topic echo、ros2 node list等工具逐步定位。问题现象常见原因解决思路ros2 launch找不到 launch 文件launch 目录未在setup.py中声明在data_files中增加 launch 路径后重新colcon buildGazebo 启动后无小车模型未设置TURTLEBOT3_MODEL环境变量执行export TURTLEBOT3_MODELburgerRViz2 中 LaserScan 无数据Fixed Frame 设置错误或/scan话题未发布将 Fixed Frame 改为base_link执行ros2 topic list检查/scan建图时地图漂移严重里程计 TF 不准或小车速度过快降低车速检查odom → base_link变换导航启动后机器人不动AMCL 定位未收敛或初始位姿未设置在 RViz2 中手动指定初始位姿观察粒子分布导航规划失败代价地图膨胀半径过大或地图未正确加载检查map_server的 yaml 路径调小inflation_radius导航到目标点后停不准xy_goal_tolerance过大将xy_goal_tolerance调小例如 0.1编译功能包报依赖错误缺少对应依赖安装执行sudo apt install ros-humble-package终端中文乱码或 ROS2 环境变量失效~/.bashrc中 source 冲突注释旧版 ROS 的 source 行保留 ROS2 的 source排查问题时可以按以下顺序检查ros2 node list确认所有节点是否启动ros2 topic list确认话题是否正常发布ros2 topic echo /scan确认激光雷达数据是否正常ros2 topic echo /odom确认里程计是否发布ros2 run tf2_tools view_frames生成 TF 树检查坐标系是否完整。9. 最佳实践与毕设复用建议这套 ROS2 仿真 SLAM 导航技术栈不仅适合学习也非常适合作为毕业设计的技术底座。下面给一些实用建议。9.1 毕设题目如何从这套流程中落地如果你准备把 ROS2 导航作为毕设题目可以考虑以下几个方向基于 Gazebo 的移动机器人导航仿真平台设计基于 ROS2 的室内自主导航系统设计与实现融合激光雷达和里程计的多传感器建图算法研究面向仓储场景的 ROS2 自主搬运机器人仿真基于 Nav2 的差速机器人动态避障策略研究。省去找一台昂贵实体机器人、调试驱动、处理电池续航等硬件问题的时间把核心精力放在算法和系统设计上这是仿真方案在毕设中的最大优势。9.2 工程化建议版本锁定在文档中记录你使用的 Ubuntu 版本、ROS2 发行版、Gazebo 版本、Nav2 版本方便复现模型驱动启动仿真、建图、导航尽量都用 launch 文件完成不要每次都敲一长串命令参数配置分离所有调优参数放在config/目录下的 yaml 文件中不要硬编码在节点代码里地图管理建图完成的地图文件建议单独备份并命名包含日期或版本例如map_20260101.yaml日志记录在节点代码中用self.get_logger().info()输出关键信息方便定位问题适度使用 rosdep在新环境编译功能包前执行rosdep install --from-paths src --ignore-src -r -y检查依赖。9.3 进一步学习路径完成本文的全流程后下一步可以围绕这些方向继续深入Cartographer 调参学习栅格建图、子图匹配、回环检测原理代价地图详解深入理解 StaticLayer、ObstacleLayer、InflationLayer 的代价计算方式DWB 局部规划器调整采样参数理解轨迹评价函数行为树学习如何自定义导航行为树 XML加入自定义节点多传感器融合在仿真中加入摄像头、IMU结合 Kalman 滤波或 EKF 进行位姿估计真机迁移将 Nav2 参数迁移到真实底盘重点校准里程计和激光雷达外参。9.4 一个可以立刻上手的改进任务如果你觉得当前导航效果还不够理想可以尝试做一个简单的改进任务在 Gazebo 世界中添加一个移动障碍物。在 Gazebo 中直接插入一个小的立方体或球体然后重新启动导航观察机器人遇到动态障碍物时是否会停下来、重新规划路径。通过这个实验你能直观理解局部代价地图的更新机制也能在答辩时展示“动态避障”相关的结果。10. 写在最后这篇文章从 ROS2 环境搭建开始完成了工作空间创建、功能包编写、Gazebo 仿真、SLAM 建图、Nav2 自主导航的全链路实践。你可以把整套流程当作一个“最小可复用的机器人项目模板”换掉 URDF 模型、换掉地图、调一组参数就能适配不同的机器人平台。最后提醒一点ROS2 版本迭代较快Gazebo 和 Nav2 的参数也在持续更新。如果你使用的不是 Humble 版本或者 Ubuntu 版本不同个别命令和参数文件需要按官方文档调整。遇到问题时善用ros2 topic list、ros2 node list、ros2 doctor这些基础工具再结合日志信息定位远比盲目重装环境高效。如果你在实操过程中遇到问题欢迎在评论区留言带上终端报错信息和版本号我也会尽量帮忙排查。觉得这篇文章有用的话可以收藏备用后续我会继续更新 Cartographer 调参、代价地图原理、自定义机器人建模等进阶内容。