.pgm + .yaml 地图格式与代价含义nav2_params.yaml 的关键参数(代价地图 inflation_radius、规划器插件、控制器插件、行为树流程)map → odom → base_link 的 TF 树关系,以及 AMCL 粒子滤波定位原理Navigation2(Nav2)是 ROS2 官方生态下的生产级自主导航框架,由 Open Robotics 与社区共同维护,截至 2025 年已有超过 100 家公司采用。它把「从 A 点安全走到 B 点」这一件事拆成一组松耦合、可插拔的模块,你既可以用默认插件快速跑通,也可以像换乐高积木一样替换其中的规划器、控制器、行为树。
Nav2 是 ROS1 move_base 的「精神继承者」,但架构有本质区别:
| 对比项 | ROS1 move_base | ROS2 Nav2 |
|---|---|---|
| 调度方式 | 写死的状态机(global planner → local planner) | 行为树(Behavior Tree)驱动,可自由编排任务逻辑 |
| 通信 | Topic/Service,依赖 Master | Topic/Service/Action(导航目标用 Action,带进度反馈),DDS 去中心化 |
| 规划器 | global_planner / navfn 为主 | Navfn / SmacPlanner(Hybrid-A*) / ThetaStar 等多种插件 |
| 控制器 | DWA / TEB(ROS1 版) | DWB / RegulatedPurePursuit / TEB(ROS2 版)/ MPPI |
| 代码组织 | navigation 单一仓库 | navigation2 仓库拆成数十个 nav2_* 包,独立迭代 |
Nav2 的六大核心模块以及它们在导航中的分工:
| 模块 | 对应包 | 职责 | 类比 |
|---|---|---|---|
| 行为树 BT Navigator | nav2_bt_navigator | 顶层调度:按 XML 行为树决定「先规划→再跟随→失败就恢复」的流程 | 大脑皮层(总指挥) |
| 规划器 Planner | nav2_planner(nav2_navfn_planner / nav2_smac_planner 等) | 全局路径:在地图上算一条从起点到目标的长距离路径 | 导航软件(看路) |
| 控制器 Controller | nav2_controller(nav2_dwb_controller / nav2_regulated_pure_pursuit_controller 等) | 局部控制:把全局路径变成速度指令(cmd_vel),实时避障 | 双手方向盘(开车) |
| 代价地图 Costmap | nav2_costmap_2d | 维护「哪里能走、哪里不能走」的 2D 代价网格(全局 + 局部两张) | 实时地图(眼睛+记忆) |
| 定位 AMCL | nav2_amcl | 粒子滤波定位:根据激光+里程计估计机器人在 map 坐标系里的位姿 | GPS(我在哪) |
| 恢复行为 Recovery | nav2_recoveries(nav2_behaviors) | 卡住时自救:Spin(原地转)、BackUp(后退)、Wait(等待障碍清除) | 脱困技巧(自救) |
整条数据流大致是:目标 pose → BT Navigator → Planner 算全局路径 → Controller 算 cmd_vel → 底盘执行,期间Costmap 持续更新障碍、AMCL 持续更新位姿,一旦控制器卡住就触发 Recovery。
推荐 Ubuntu 22.04 + ROS2 Humble(最稳、教程最多),或 Ubuntu 24.04 + ROS2 Jazzy(更新,但个别包如 cartographer_ros 官方源暂未发布,需源码编译)。安装 Nav2 与建图工具:
sudo apt update
sudo apt install ros-humble-navigation2 ros-humble-nav2-bringup # Nav2 核心
sudo apt install ros-humble-slam-toolbox # 建图:SLAM Toolbox
sudo apt install ros-humble-cartographer ros-humble-cartographer-ros # 建图:Cartographer(Humble 有,Jazzy 需源码编译)
# 可选:小乌龟仿真(最常用的 Nav2 入门载体)
sudo apt install ros-humble-turtlebot3*
ros-humble-* 换成 ros-jazzy-*;cartographer_ros 在 Jazzy 官方源暂未发布(约截至 2025 年),想用 Cartographer 建议留在 Humble,或从 GitHub 源码编译。每次新开终端都要先 source 环境(可把全局 setup.bash 写进 ~/.bashrc,工作空间的 setup.bash 手动 source,详见 04-09 页):
source /opt/ros/humble/setup.bash # 全局环境
echo "source /opt/ros/humble/setup.bash" >> ~/.bashrc # 可选:写进 .bashrc 免重复
仿真载体可选小乌龟 TurtleBot3(教程最多)、Nav2 官方示例机器人,或你自己的差速/阿克曼底盘。人形机器人的轮式移动底座完全可以直接套用差速小车这一套。
导航的第一步是建一张栅格地图(Occupancy Grid Map)——把环境切成一个个小格子,每个格子标注「空闲 / 占用 / 未知」。这由 SLAM(同时定位与建图)算法完成:机器人一边走、一边用激光雷达扫环境、一边估计自己的位姿。
| 方案 | 算法特点 | 优点 | 缺点 | 适用 |
|---|---|---|---|---|
| SLAM Toolbox | 基于粒子滤波(GMapping 的 ROS2 正规继承者)+ 图优化可选 | 安装简单、稳定、支持终生建图与地图序列化 | 大场景回环能力弱于 Cartographer | 绝大多数入门与中小场景 |
| Cartographer | Google 开源,基于图优化 + 子图(submap)+ 回环检测 | 大场景、回环鲁棒、可 2D/3D、可多传感器融合 | 参数多、调参门槛高、Jazzy 未发布 | 大型/复杂环境、追求精度 |
| GMapping(ROS1 经典) | 纯粒子滤波 FastSLAM 2.0 | 历史悠久、资料多 | ROS1 专属,ROS2 里已被 SLAM Toolbox 取代 | 仅 ROS1 项目对照参考 |
日常推荐 SLAM Toolbox 起步;场景很大、回环很多时再上 Cartographer。两者输出的都是同一种栅格地图,下游 Nav2 用法完全一致。
# 终端 1:启动机器人仿真(以小乌龟为例,含底盘+雷达+TF)
export TURTLEBOT3_MODEL=burger
ros2 launch turtlebot3_gazebo turtlebot3_world.launch.py
# 终端 2:启动 SLAM Toolbox(在线同步建图)
ros2 launch slam_toolbox online_async_launch.py
# 终端 3:启动 RViz2 看地图,再用键盘/手柄遥控小车走遍房间
ros2 run turtlebot3_teleop teleop_keyboard
遥控小车慢速、完整地走遍整个房间,让激光雷达扫到每一面墙,RViz2 里地图会逐渐被填满。
建图满意后,保存地图(RViz 打开时地图不变即可保存):
# Nav2 用 map_saver_cli(注意不是 ROS1 的 map_server map_saver)
ros2 run nav2_map_server map_saver_cli -f ~/map
会生成两个文件:map.pgm(灰度图片)与 map.yaml(元数据)。示例 map.yaml:
image: map.pgm
mode: trinary # trinary(三值)/ scale / raw
resolution: 0.05 # 每像素 = 0.05 米
origin: [-1.0, -1.0, 0.0] # 地图左下角像素在 map 坐标系下的位姿 [x,y,yaw]
negate: 0 # 0 = 黑是占用;1 = 反转
occupied_thresh: 0.65 # 占用概率 >= 0.65 视为「占用」
free_thresh: 0.196 # 占用概率 <= 0.196 视为「空闲」
.pgm 是普通灰度图,黑 = 障碍,白 = 可通行,灰(常为 205)= 未知。加载到 Nav2 后,代价地图把这些像素换算成 0~255 的代价值,数值越大越「危险」:
| 代价类别 | 数值 | 含义 | RViz 中颜色 |
|---|---|---|---|
| FREE_SPACE(空闲) | 0 | 完全可走 | 浅色/白 |
| INSCRIBED_INFLATED(内切膨胀) | 253 | 机器人「内切圆」会撞到的区域,致死 | 深色/紫红 |
| LETHAL_OBSTACLE(致死障碍) | 254 | 真实障碍物本体 | 黑 |
| NO_INFORMATION(未知) | 255 | 没扫到、不知道能否走 | 灰/蓝紫 |
关键概念是膨胀(Inflate):为了不让机器人「贴墙走」,代价地图会以 inflation_radius 为半径,把障碍物附近的格子代价值从 254 向外逐级衰减到 0。机器人中心离障碍越近,代价越高,规划器和控制器就自然绕开。
Nav2 的所有行为都由一个参数文件控制,默认叫 nav2_params.yaml(由 ros2 launch nav2_bringup bringup_launch.py 加载)。下面拆解最关键的几块。
全局代价地图(global_costmap)给规划器用、范围大、更新慢;局部代价地图(local_costmap)给控制器用、范围小、更新快(跟随机器人移动)。两者结构相同:
local_costmap:
ros__parameters:
update_frequency: 5.0 # 更新频率(Hz)
publish_frequency: 2.0 # 发布频率(Hz)
global_frame: odom # 局部地图坐标系(跟随机器人,用 odom)
robot_base_frame: base_link
rolling_window: true # 局部地图用滚动窗口
width: 3 # 窗口宽(米),rolling_window 时生效
height: 3
resolution: 0.05 # 分辨率(米/格)
robot_radius: 0.22 # 机器人半径(圆形底盘);footprint 二选一
plugins: ["static_layer", "obstacle_layer", "inflation_layer"]
inflation_layer:
plugin: "nav2_costmap_2d::InflationLayer"
inflation_radius: 0.55 # ★ 膨胀半径:越大越远离障碍,越大越难钻窄缝
cost_scaling_factor: 3.0 # ★ 代价衰减因子:越大衰减越快(越贴墙)
obstacle_layer:
plugin: "nav2_costmap_2d::ObstacleLayer"
observation_sources: scan
scan: {topic: /scan, data_type: LaserScan, clearing: true, marking: true}
inflation_radius 决定「离障碍多远开始绕」,要大于机器人半径、略小于最窄门宽的一半;cost_scaling_factor 越大,代价从障碍向外衰减越快,机器人越「敢贴墙」。贴墙蹭到家具调大前者,太保守钻不过门调小前者。| 插件 | 包名(plugin 字段) | 算法 | 适用 |
|---|---|---|---|
| NavfnPlanner | nav2_navfn_planner/NavfnPlanner | Dijkstra(在势场/栅格上) | 差速、全向,入门默认 |
| SmacPlanner2D | nav2_smac_planner/SmacPlanner2D | 2D A* / Hybrid-A* | 差速,较快 |
| SmacPlannerHybrid | nav2_smac_planner/SmacPlannerHybrid | Hybrid-A*(考虑朝向,支持 Dubin/Reeds-Shepp) | 阿克曼/车式底盘 |
| ThetaStarPlanner | nav2_theta_star_planner/ThetaStarPlanner | Theta*(任意角度路径) | 追求更短更直路径 |
planner_server:
ros__parameters:
plugins: ["GridBased"]
use_sim_time: true
GridBased:
plugin: "nav2_navfn_planner/NavfnPlanner"
tolerance: 0.5 # 目标点容差(米)
use_astar: false
| 插件 | plugin 字段 | 算法 | 适用 |
|---|---|---|---|
| DWB | dwb_core::DWBLocalPlanner | 采样轨迹打分(ROS1 DWA 的继承者) | 差速,经典稳妥 |
| RegulatedPurePursuit | nav2_regulated_pure_pursuit_controller::RegulatedPurePursuitController | 纯追踪 + 速度调节 | 差速,默认推荐,参数少 |
| TEB(ROS2) | teb_local_planner::TebLocalPlannerROS | 时间弹性带(Timed Elastic Band),优化时间与位姿 | 车式/阿克曼,需源码编译(非 Nav2 官方包) |
| MPPI | nav2_mppi_controller::MPPIController | 模型预测路径积分(采样+GPU 可选) | 高动态、对平滑要求高 |
controller_server:
ros__parameters:
plugins: ["FollowPath"]
FollowPath:
plugin: "nav2_regulated_pure_pursuit_controller::RegulatedPurePursuitController"
desired_linear_vel: 0.22 # 期望线速度(m/s)
max_linear_vel: 0.26
min_lookahead_dist: 0.3 # 前视距离(纯追踪核心参数)
max_lookahead_dist: 0.9
Nav2 用行为树 XML 描述导航逻辑,默认行为树叫 navigate_to_pose_w_replanning_and_recovery.xml,核心流程:
bt_navigator:
ros__parameters:
default_nav_to_pose_bt_xml: "<path>/navigate_to_pose_w_replanning_and_recovery.xml"
该行为树大致按顺序执行这些节点(节选,实际 XML 里是 <Sequence> / <Fallback> / <RecoveryNode> 组合):
行为树的好处:你可以自己写 XML,插入「到门口先停一下」「绕行检测」等自定义逻辑,而不必改 C++ 源码 —— 这是 Nav2 相比 move_base 最大的升级点。
机器人要在地图里导航,必须先知道「我在地图里的哪个位置、朝哪个方向」。Nav2 默认用 AMCL(Adaptive Monte Carlo Localization,自适应蒙特卡洛定位)。
AMCL 是粒子滤波的一种:MCL 在整张地图上撒一堆「粒子」(每个粒子是一个可能的位姿假设),然后反复做两步 —— 运动更新(里程计动了,粒子跟着动)与测量更新(激光扫到墙,离真实墙更吻合的粒子获得更高权重,并重采样)。几轮之后粒子收敛成一团,就是机器人的真实位姿。「Adaptive」指它用 KLD 采样动态调整粒子数量:不确定时粒子多(最多 max_particles),确定时粒子少(最少 min_particles),省算力。
AMCL 刚启动时粒子均匀撒满整张地图,所以第一件事是告诉它「我大概在哪」——在 RViz2 里用 2D Pose Estimate 按钮(或发布 /initialpose 话题)在地图上点一下机器人真实位置并拖出朝向:
ros2 topic pub /initialpose geometry_msgs/msg/PoseWithCovarianceStamped \
"{header: {frame_id: 'map'}, pose: {pose: {position: {x: 0.0, y: 0.0, z: 0.0}, \
orientation: {x: 0.0, y: 0.0, z: 0.0, w: 1.0}}}, }" --once
max_particles 调大(如 5000),但会增加 CPU 占用。把前面全部串起来,跑通「建图 → 保存 → 导航 → 避障」的完整闭环。
export TURTLEBOT3_MODEL=burger
ros2 launch turtlebot3_gazebo turtlebot3_world.launch.py # 终端 1:仿真
ros2 launch slam_toolbox online_async_launch.py # 终端 2:建图
ros2 run turtlebot3_teleop teleop_keyboard # 终端 3:遥控建图
ros2 run nav2_map_server map_saver_cli -f ~/map # 保存 ~/map.pgm + ~/map.yaml
# 建图完成后,关掉 slam_toolbox,改跑导航(加载刚保存的地图 + 启动 AMCL + Nav2)
ros2 launch nav2_bringup bringup_launch.py map:=/home/$USER/map.yaml use_sim_time:=true
bringup_launch.py 会一次性启动:map_server(加载地图)、AMCL(定位)、planner_server、controller_server、bt_navigator、代价地图、RViz2 等,并自动读取 nav2_params.yaml。
导航途中在机器人前方扔一个箱子(或让一个人挡路),观察两件事:
人形机器人的导航需求与轮式机器人高度重叠:自主移动到指定点、跟随人、巡游、避障。这些「去那里」的顶层能力完全可以直接复用 Nav2 的成熟框架,而不用重复造轮子。
/scan(LaserScan),直接喂给 Nav2 的 obstacle_layer;或单独用一个 2D 雷达 + 一个 3D 雷达分层使用。| 维度 | 轮式(差速/阿克曼) | 足式(双足人形 / 四足) |
|---|---|---|
| 全局规划 | Nav2 Planner(2D 代价地图) | Nav2 Planner 同样适用(把足式整体当作一个「点」) |
| 局部规划/控制 | DWB / RegulatedPurePursuit 输出 cmd_vel | 常用深度强化学习(DRL)运动控制器输出步态/落脚点,而非简单 cmd_vel |
| 障碍表达 | 2D 代价足够 | 需额外考虑台阶、斜坡、崎岖地形(3D/高程) |
| 恢复行为 | Spin / BackUp / Wait | 需配合步态:原地转向、侧移、跨步,难度更高 |
结论:全局「去哪儿」用 Nav2,局部「怎么走」分家—— 轮式用经典规划控制器,足式常见做法是 Nav2 出全局路径 + 强化学习局部策略负责落足与平衡(如四足/人形常用的 RL 步态控制器)。这也是为什么「Nav2 是人形机器人导航的地基,而不是全部」。
| 症状 | 原因 | 解法 |
|---|---|---|
| 「Timed out waiting for transform from base_link to map」 | TF 树断了:map → odom 或 odom → base_link 缺一环 | 确认 AMCL(发布 map→odom)与里程计/robot_localization(发布 odom→base_link)都在跑;用 ros2 run tf2_tools view_frames 看 TF 树 |
| 机器人在 RViz 里飘 / 穿墙 | AMCL 初始位姿给错,或里程计漂移 | 重新点 2D Pose Estimate;检查里程计标定 |
| 导航「抖」或原地打转 | 控制器前视距离 / 速度参数不合适 | 调 min/max_lookahead_dist、max_linear_vel,或换 RegulatedPurePursuit |
| CPU 满载、地图卡顿 | 代价地图分辨率太高 / 更新频率太高 | resolution 从 0.05 降到 0.1,降低 update_frequency |
| 找不到对方节点 / 通信异常 | DDS 发现失败(多机、Docker、混杂 RMW) | 统一用 CycloneDDS:sudo apt install ros-humble-rmw-cyclonedds-cpp 并 export RMW_IMPLEMENTATION=rmw_cyclonedds_cpp |
| 仿真里 TF 老报错 | use_sim_time 没对齐 | 所有 launch 都加 use_sim_time:=true(或参数文件里统一设) |
.pgm + .yaml;③ 代价地图 0=空闲、254=致死障碍、255=未知,inflation_radius 定绕障距离、cost_scaling_factor 越大越贴墙;④ AMCL 粒子滤波 + KLD 自适应粒子数,靠 map→odom→base_link TF 链定位;⑤ 人形机器人「全局用 Nav2、局部足式用 RL 步态」分层解耦。.pgm + .yaml 栅格地图;导航配置集中在 nav2_params.yaml(代价地图 inflation_radius、规划器、控制器、行为树);AMCL 用粒子滤波定位,靠 map→odom→base_link 的 TF 链串联;实战按「仿真→建图→存图→加载导航→2D Goal Pose→避障恢复」跑通,人形机器人复用 Nav2 做全局规划、局部再分轮式与足式处理。
inflation_radius 调得比房间门宽的一半还大,会发生什么?为什么?📅 资料整理更新至 2026-08 · 关键事实(包名、命令、参数名、插件名)已经 web_search 核实。