
ROS 2 导航系统 Nav2 实战:从定位、代价地图到规划控制
Nav2 最容易被误解成“ROS 2 里的路径规划器”。实际上,它更像一个移动机器人导航操作系统:接收目标、管理行为树、调用全局规划、局部控制、代价地图、定位、恢复行为、速度平滑和任务服务器。
如果只把 Nav2 当成一个 A* + DWA 插件,很快会在真实机器人上遇到这些问题:
- 地图有了,但机器人不知道自己在哪;
- 路径能生成,但底盘不跟;
- 局部避障不断触发恢复;
- RViz 里 TF 树飘来飘去;
- 仿真正常,实物在门口抖动;
- 一换发行版,
cmd_vel消息类型和参数默认值变了。
这篇文章按工程落地的顺序拆 Nav2,而不是按文档目录逐项解释。
一、Nav2 的本质:行为树调度的导航系统
Nav2 的官方介绍里,它提供 perception、planning、control、localization、visualization、behaviors 等能力,并用 Behavior Tree 执行复杂机器人行为。官方 Concepts 文档也把它拆成 Behavior Trees、Navigation Servers、State Estimation、Environmental Representation 等部分。 [¹]
一条典型数据流如下:
可以把 Nav2 分成 6 层:
| 层 | 关键模块 | 作用 |
|---|---|---|
| 任务层 | bt_navigator、waypoint follower、route server | 决定如何完成导航任务 |
| 全局规划 | planner_server | 从当前位置到目标点生成全局路径 |
| 路径处理 | smoother_server | 让全局路径更平滑、更可执行 |
| 局部控制 | controller_server | 根据局部代价地图和路径输出速度 |
| 环境表示 | global/local costmap | 把地图、传感器、禁行区、膨胀层变成可规划代价 |
| 状态估计 | AMCL / SLAM / robot_localization / TF | 提供机器人位姿和速度 |
如果一个机器人“看起来 Nav2 不工作”,先问它坏在哪一层,而不是盲目调 planner 参数。
二、先把 TF 和状态估计做对
Nav2 依赖一条稳定 TF 链:
map -> odom -> base_link -> sensor frames
这三个 frame 的职责不同:
| frame | 谁发布 | 含义 |
|---|---|---|
map | AMCL / SLAM / 全局定位 | 全局地图坐标 |
odom | 里程计 / EKF | 连续但会漂移的局部坐标 |
base_link | robot_state_publisher / 控制器 | 机器人本体 |
常见做法:
- 已有静态地图:
map_server + AMCL,用激光匹配地图; - 边建图边导航:
slam_toolbox或其它 SLAM; - 户外/GPS:
robot_localization融合 wheel odom、IMU、GPS; - 视觉/深度方案:VIO / VSLAM 补充轮速里程计。
真实机器人最常见的失败不是算法差,而是 TF 时间、坐标轴、传感器外参错:
ros2 run tf2_tools view_frames
ros2 topic hz /tf
ros2 topic echo /odom
ros2 topic echo /scan
ros2 run tf2_ros tf2_echo map base_link
调 Nav2 前先确认:
/scan或点云在 RViz 里和机器人方向一致;- 机器人前进时
/odom的 x 正方向增加; - 机器人转动时 yaw 符号符合右手系;
base_link到激光雷达的 static transform 准确;- 所有节点使用同一时间源,仿真时
/clock一致。
三、Costmap 是 Nav2 的工程核心
Nav2 的 planner 和 controller 不是直接看传感器,而是看 costmap。costmap 把环境分成可走、障碍、膨胀、未知、禁行、限速等代价。
典型配置:
global_costmap:
global_costmap:
ros__parameters:
global_frame: map
robot_base_frame: base_link
resolution: 0.05
track_unknown_space: true
plugins: ["static_layer", "obstacle_layer", "inflation_layer"]
local_costmap:
local_costmap:
ros__parameters:
global_frame: odom
robot_base_frame: base_link
rolling_window: true
width: 4.0
height: 4.0
resolution: 0.05
plugins: ["obstacle_layer", "inflation_layer"]
global costmap 更多用于全局规划,local costmap 更多用于局部避障。两者可以共用传感器,也可以分层配置。
调 costmap 的关键变量:
| 参数 | 影响 | 经验 |
|---|---|---|
robot_radius / footprint | 机器人是否能过窄门 | 真实尺寸加安全余量 |
inflation_radius | 离障碍多远开始变贵 | 过大绕远,过小贴边 |
cost_scaling_factor | 障碍代价衰减 | 影响贴边程度 |
observation_sources | 哪些传感器进障碍层 | 先少后多,逐个验证 |
clearing / marking | 障碍清除和标记 | 动态障碍场景必调 |
update_frequency | 更新频率 | 低了避障慢,高了吃 CPU |
典型调试方法:
- RViz 只看 local costmap;
- 推机器人走一圈,看障碍是否稳定;
- 用纸箱/人腿做动态障碍,看是否及时 mark/clear;
- 关闭 planner,只让 controller 跟简单路径;
- 最后再加 keepout zone、speed filter、语义层。
四、Planner Server:选对全局规划器
Nav2 的 Planner Server 托管一个或多个 planner 插件。不同机器人运动模型不同,规划器不应该乱选。
| 机器人 | 推荐起步 | 说明 |
|---|---|---|
| 差速/全向,室内 AMR | NavFn / Smac 2D | 简单稳定,先跑通 |
| Ackermann / 车式底盘 | Smac Hybrid-A* | 考虑最小转弯半径 |
| 大车、需要倒车/曲率约束 | Smac State Lattice / Hybrid-A* | 路径更可执行 |
| 走图网络/园区道路 | Route Server | 基于导航图,不是纯 freespace |
官方 Smac Planner 文档说明,nav2_smac_planner 包提供优化过的 A* 框架,包括 2D A*、Hybrid-A* 和 State Lattice 三类规划器,面向不同机器人平台。 [²]
如果你的机器人是差速小车,先不要上复杂规划器。先用 2D planner 跑通,再根据路径质量和运动约束升级。复杂规划器会带来更多参数:转弯半径、角度离散、解析扩展、启发函数、平滑策略。
五、Controller Server:局部跟踪才是“能不能走好”
Controller Server 是 Nav2 里最靠近底盘的一层。官方文档说明它接收路径和插件名称,调用 controller、progress checker、goal checker,并持有 local costmap。 [³]
常见 controller:
| Controller | 适合 | 特点 |
|---|---|---|
| DWB | 差速/全向小车 | ROS1 DWA 思路延续,参数多,可解释 |
| Regulated Pure Pursuit | 差速、Ackermann、小车 | 简洁、稳定、适合多数室内机器人 |
| MPPI | 高动态/复杂约束 | 采样优化,算力需求更高 |
| Rotation Shim + primary controller | 需要先转向再跟踪 | 对差速机器人很实用 |
局部控制常见症状:
| 症状 | 多半原因 |
|---|---|
| 机器人原地抖 | 速度阈值、角速度采样、goal checker 太紧 |
| 贴障碍太近 | inflation 半径/代价衰减不合适 |
| 路径看着对但不走 | controller 没收到有效 path 或 TF 超时 |
| 一直 recovery | progress checker 判定失败 |
| 到点后转来转去 | yaw tolerance 太小或定位抖动 |
调 controller 的顺序:
- 降低最大速度;
- 放宽 goal tolerance;
- 确认 local costmap 稳定;
- 确认 odom 没明显延迟;
- 再调 planner 和 smoother。
不要一上来改 50 个参数。每次只改一类参数,并保存 rosbag。
六、Smoother 和 Velocity Smoother:不要混在一起
Nav2 里有两个“平滑”概念:
| 模块 | 平滑对象 | 位置 |
|---|---|---|
| Smoother Server | 全局路径 | planner 之后、controller 之前 |
| Velocity Smoother | 速度命令 | controller 之后、底盘控制器之前 |
路径平滑让全局路径更适合机器人执行。Nav2 Simple Smoother / Constrained Smoother 是这里的典型插件。
速度平滑处理的是 cmd_vel。官方 velocity smoother 文档强调,它用于降低加速度和 jerk,减少对电机和硬件控制器的冲击,也能插值到更高频率发布速度命令。 [⁴]
真实机器人推荐打开 velocity smoother,特别是:
- 电机速度环比较硬;
- 轮子打滑;
- 局部控制器输出跳变;
- 低速时有 deadband;
- 底盘控制器频率高于 Nav2 controller server。
七、行为树:Nav2 的任务编排层
Nav2 默认不是简单的“规划一次、跟随一次”。它用 Behavior Tree 做编排:
行为树让你能插入:
- 重规划频率;
- 路径截断;
- planner/controller 切换;
- costmap 清理;
- dock / undock;
- keepout / speed zone;
- 任务级失败策略。
但初期不要急着改 BT XML。先用默认树跑通,再把恢复行为、重规划周期、路径平滑节点按需求加入。
八、真实机器人上线顺序
1. 单独验证底盘
先确认 ros2_control 能手动控制底盘:
ros2 topic pub /cmd_vel geometry_msgs/msg/TwistStamped ...
ros2 topic echo /odom
ros2 run tf2_tools view_frames
底盘不能稳定响应时,不要调 Nav2。
2. 单独验证定位
静止时:
map -> base_link不应明显跳动;- 激光和地图重合;
- AMCL 粒子云收敛。
移动时:
odom -> base_link连续;map -> odom可以缓慢校正,但不应剧烈跳;- 轮速里程计和激光/视觉定位差距可解释。
3. 单独验证 costmap
目标:
- 障碍能进入 local costmap;
- 障碍移开后能清除;
- 机器人 footprint 不穿墙;
- 未知区域策略符合场景。
4. 跑短距离目标
先在 1–2 米内低速测试:
- 直线到点;
- 原地转向;
- 绕小障碍;
- 窄门;
- 人突然走近;
- 目标取消。
5. 长距离、多目标、异常恢复
最后再测:
- waypoint following;
- 自动重规划;
- 电梯口/窄通道;
- 低电量回充;
- 网络延迟;
- 传感器丢帧;
- 急停恢复。
九、一个可执行的参数基线
室内差速 AMR 起步可以保守一点:
controller_server:
ros__parameters:
controller_frequency: 20.0
min_x_velocity_threshold: 0.001
min_theta_velocity_threshold: 0.001
progress_checker_plugins: ["progress_checker"]
goal_checker_plugins: ["goal_checker"]
controller_plugins: ["FollowPath"]
progress_checker:
plugin: "nav2_controller::SimpleProgressChecker"
required_movement_radius: 0.3
movement_time_allowance: 10.0
goal_checker:
plugin: "nav2_controller::SimpleGoalChecker"
xy_goal_tolerance: 0.20
yaw_goal_tolerance: 0.30
stateful: True
velocity_smoother:
ros__parameters:
smoothing_frequency: 40.0
max_velocity: [0.35, 0.0, 0.8]
min_velocity: [-0.10, 0.0, -0.8]
max_accel: [0.5, 0.0, 1.2]
max_decel: [-0.6, 0.0, -1.4]
velocity_timeout: 1.0
这不是最优值,只是安全起点。上线前必须按机器人质量、轮径、地面摩擦、传感器延迟重新标定。
十、排障清单
| 问题 | 先看什么 |
|---|---|
| 点目标没反应 | bt_navigator、action server、lifecycle 状态 |
| 有全局路径但不动 | controller server、local costmap、cmd_vel |
| cmd_vel 有但底盘不动 | velocity smoother、ros2_control、硬件接口 |
| 机器人走偏 | odom、轮径、轮距、IMU yaw |
| 遇障碍不避让 | local costmap observation source |
| 一直清 costmap | recovery 条件、progress checker |
| 定位突然跳 | AMCL 参数、地图质量、TF 时间戳 |
| 窄门过不去 | footprint、inflation、controller 速度 |
| 到目标前停住 | goal checker、minimum velocity、deadband |
| 仿真正常实物失败 | 传感器延迟、底盘限幅、地面摩擦 |
结论:Nav2 的难点不在“能不能跑 demo”,而在系统边界。定位、costmap、planner、controller、velocity smoother、ros2_control 每层都要能单独验证。只有这样,机器人在真实环境里出问题时,你才知道该改算法、改参数、改硬件,还是改 TF。
参考资料
[¹] Nav2 Concepts: https://docs.nav2.org/concepts/index.html
[²] Nav2 Smac Planner: https://docs.nav2.org/configuration/packages/configuring-smac-planner.html
[³] Nav2 Controller Server: https://docs.nav2.org/configuration/packages/configuring-controller-server.html
[⁴] Nav2 Velocity Smoother: https://docs.nav2.org/configuration/packages/configuring-velocity-smoother.html