想象你要训练一位新员工送外卖,他叫阿蓝。上岗第一天,老师傅给了他三样东西:一张全城地图、一台实时路况播报器,还有一本《老司机出车手册》。地图帮他规划跨城大方向,路况播报帮他躲开临时拥堵,手册则告诉他堵车了该怎么办、迷路了找谁求助——这不就是导航吗?没错,阿蓝其实是一台自主移动机器人(AMR),而 ROS2 里的 Navigation2(简称 Nav2)导航栈,就是他的这套“导航大脑套装”。 Nav2 是 ROS2 生态下最主流的开源导航框架,覆盖了移动机器人从“我在哪”(定位)到“怎么走”(全局规划)、“怎么避开突发障碍”(局部规划)、“哪条路更安全”(代价地图)、再到“遇到麻烦怎么办”(行为树)的完整链条。这篇文章会用阿蓝送外卖的故事,把每个模块的角色、核心算法和部署调参的关键讲清楚。读完你会发现,所谓自主导航,本质上就是把一位老司机的本事,逐条写成了代码。

岗位关联

对 AI 从业者来说,Nav2 是通往移动机器人实战的“必修驾驶课”:掌握它的模块划分与调参思路,就能快速搭建可落地的仓储、配送、巡检机器人原型,也是各类 AMR 岗位面试的高频考点。

目录
  1. 可视化演示
  2. Nav2架构总览
  3. 全局路径规划
  4. 局部避障控制
  5. 代价地图建模
  6. 定位技术
  7. 行为树决策
  8. 部署与调参
  9. AI 应用场景
  10. Python 演示
  11. 常见误区
  12. 面试题

可视化演示

展示Nav2全局路径规划与代价地图、局部避障控制器实时速度调整及AMCL粒子滤波定位原理,切换模式理解自主移动机器人导航核心流程。

本节配有交互动效(进入视口后自动加载,约 3KB)。

Nav2全局路径规划与代价地图 全局规划器在静态代价地图上规划从当前位置到目标的最优路径,输出路径点序列供局部控制器跟踪 Costmap2D为每个栅格赋予0-255代价值:0为自由空间,255为致命障碍,中间值表示风险区域 Start → Global Planner → Waypoints → Goal Nav2采用模块化插件架构,全局规划器工作在静态代价地图上不考虑动态障碍物

Nav2架构总览

阿蓝的公司不大,但部门分得很清:调度室看地图定路线,司机手握方向盘躲障碍,安全员随时更新路况,还有一位“老法师”负责反复确认“咱们现在到哪了”。Nav2 就是把这家公司直接搬进了 ROS2:每个部门是一个独立节点,部门之间用“话题”和“服务”对讲机通信,整个公司的规章制度则写进 YAML 参数文件统一配置。

Nav2 的核心组件包括 PlannerServer(全局规划)、ControllerServer(局部控制)、Costmap2D(代价地图)、AMCL(定位)和 BehaviorTree(行为树)。它们都有完整的“生命周期”:先配置(configure)、再激活(activate)、出状况能停用(deactivate)——就像每位新员工都要先培训再上岗,出了岔子能随时叫停,而不用重启整个公司。

整个导航过程可以看成一条函数流水线:先定位输出位姿,再结合地图算全局路径,最后由局部控制器产出速度指令。用数学语言写出来:

每个格子 c_{ij} 标记可通行(0)或障碍(1)。

这是机器人在世界坐标系中的位置和朝向。

路径是地图上一串有序坐标点,相当于地图软件给的那条蓝色路线。

输出线速度 v 与角速度 ω,直接驱动底盘电机。

所以导航不是“自动驾驶一个函数搞定”,而是像公司一样各部门各管一段、靠通信总线协作。阿蓝能稳稳送单,靠的是整条流水线每个环节都不掉链子——Nav2 就是这套流水线的标准化实施方案。

u(t)=D(P(L(M), zt))\displaystyle u(t) = D\big(P(L(M),\, z_t)\big)
这条公式是导航主链路的“一句话总结”:传感器数据 z_t 进入定位器 L,找出机器人在地图 M 上的位置;全局规划器 P 据此画出大路线;局部控制器 D 最终“踩油门打方向盘”。
  1. 地图 M预先建好的栅格地图,相当于阿蓝手里的城市地图。
    M={cij∣cij∈{0,1}}\displaystyle M = \{c_{ij} \mid c_{ij} \in \{0, 1\}\}
  2. 定位 L把实时传感器观测 z_t 与地图“对表”,输出位姿。
    L(M,zt)↦(x,y,θ)\displaystyle L(M, z_t) \mapsto (x, y, \theta)
  3. 全局规划 P根据位姿和目标点求整条路径。
    P(⋅)↦Γ={p0,p1,…,pn}\displaystyle P(\cdot) \mapsto \Gamma = \{p_0, p_1, \dots, p_n\}
  4. 局部控制 D沿路径前方一小段生成实时速度指令。
    D(Γ,zt)↦u=(v,ω)\displaystyle D(\Gamma, z_t) \mapsto u = (v, \omega)

全局路径规划

出发前,阿蓝打开地图软件输入目的地,软件立刻给出“全程走主路、上高架”的方案。它不需要关心每个路口的红绿灯,只要大方向对、还能绕开已知的修路段。这就是全局路径规划:先在完整地图上找到“战略级”路线,细节留给之后的局部控制。

Nav2 的全局规划器在收到目标点后,基于静态栅格地图和全局代价地图执行搜索算法,找出从当前位置到目标的最优路径。默认实现是 NavFn(A* 的一种工程化变体),搜索出来的折线路径还会经过平滑处理,去掉锯齿,方便底盘平滑跟踪。A* 会在栅格上不断扩展“看起来最有希望”的格子,衡量标准是估价函数:

每走一格代价加 1;若考虑地形,也可为不同格子设不同权重。

这是欧氏距离,它永远不大于真实最短距离,能保证搜索找到最优解。

A* 每次都从优先队列中取出 f 最小的节点继续扩展,像扫地雷一样逐格推进。

地图软件推荐“最快路线”时也在做类似的事:已开的路段是 g,前方预估是 h。当然,地图软件不知道路上临时发生的事故——那是局部避障的活,也是下一章阿蓝要练的“真车技”。

f(n)=g(n)+h(n)\displaystyle f(n) = g(n) + h(n)
f 是“总预计代价”:g 是已经走过的距离(实付),h 是到终点还要的距离(预估)。A* 每次优先挑 f 最小的格子扩展,最终找到一条代价最小的完整路径。
  1. 已有代价 g(n)从起点一路走到格子 n 实际花的步数。
    g(n)=g(parent(n))+1\displaystyle g(n) = g(\mathrm{parent}(n)) + 1
  2. 启发估计 h(n)n 到目标点的预估最短距离。
    f(n)=g(n)+h(n)\displaystyle f(n) = g(n) + h(n)

局部避障控制

真正上路后,阿蓝瞪大眼睛盯着车头前方两三米:突然窜出的电动车、路边刚停下的货车,都是地图上没有的“临时剧情”。全局规划帮不了这种忙,得靠临场反应——这正是局部规划器存在的意义。

局部规划器以 10~20Hz 的高频率运行,只参考全局路径前方一小段,结合最新激光雷达扫描,实时算出 (v, ω) 速度指令。Nav2 默认的 DWA(动态窗口法)思路很朴素:把未来 1 秒内所有可能的速度组合“撒网采样”,模拟出候选轨迹,再逐条打分,选最好的那条执行。评分函数是:

夹角越大扣分越多,鼓励机器人“别绕远路”。

距离越远越安全,会直接撞上障碍的轨迹优先淘汰。

相同安全性和朝向得分下,选更快的。

每轮都挑总评分最高的速度组合作为指令下发。

就像老司机在路口同时瞟三样东西——前方目标、侧方行人、脚下油门——靠经验和权重瞬间做出决定。DWA 把这套“瞬间决策”量化成了数学题,让阿蓝每秒钟能做几十次判断,遇到临时冒出来的障碍也能从容躲开。

G(v,ω)=α⋅heading(v,ω)+β⋅dist(v,ω)+γ⋅vel(v,ω)\displaystyle G(v,\omega) = \alpha \cdot \mathrm{heading}(v,\omega) + \beta \cdot \mathrm{dist}(v,\omega) + \gamma \cdot \mathrm{vel}(v,\omega)
这是一道“多选题加权题”:每个候选速度都要回答三个问题——朝目标方向吗(heading)?离障碍够远吗(dist)?跑得快吗(vel)?α、β、γ 是这三问的权重。
  1. 朝向项 heading轨迹末端朝向与目标方向的夹角。
    heading=180∘−Δθ\displaystyle \mathrm{heading} = 180^\circ - \Delta\theta
  2. 安全项 dist轨迹上离最近障碍物的距离。
    dist(v,ω)=min⁡p∈traj∥p−o∥\displaystyle \mathrm{dist}(v,\omega) = \min_{p \in \mathrm{traj}} \| p - o \|
  3. 速度项 vel速度绝对值,鼓励尽快到达。
    u∗=arg⁡max⁡v,ωG(v,ω)\displaystyle u^* = \arg\max_{v,\omega} G(v,\omega)

代价地图建模

老司机心里有张“路况热力图”:哪条巷子窄、哪个商圈人挤人、哪个路口刚发生剐蹭,都会标成红色警戒区。阿蓝的这张热力图,在 Nav2 里叫代价地图(Costmap),它决定规划器“敢不敢走某条路”。

代价地图是分层结构:静态层(Static Layer)来自事先建好的栅格地图;障碍层(Obstacle Layer)由激光雷达实时扫描填充,行人、推车一出现就立刻“上色”;膨胀层(Inflation Layer)把障碍物向外“加胖”,让规划器对这段距离保持敬畏。整个地图持续更新,就像路况播报从未停过。膨胀层对障碍物周围格子的代价按指数衰减计算:

计算每个格子距离所有障碍物的最小欧氏距离。

内切半径以内的代价设为致命,规划器永远不敢进。

超过膨胀半径的格子完全不受影响。

距离每增加一点,代价就明显回落,形成“热力图渐变”。

正是这张持续刷新、层层标注的热力图,让阿蓝的规划器“自觉地”远离货架角、石墩和刚出现的行人,而不是真撞上去才后悔。代价地图就是机器人对世界的“敬畏心”。

C(d)=252⋅e−d−riro−ri,ri<d<ro\displaystyle C(d) = 252 \cdot e^{-\frac{d - r_i}{r_o - r_i}}, \quad r_i < d < r_o
想想烛光的光晕:离蜡烛(障碍物)越近越亮(代价越高),直到伸手就烫伤(252 分致命);超过光晕范围(r_o)就完全无感(0 分)。
  1. 距离 d格子中心到最近障碍物的距离。
    d=min⁡o∈O∥p−o∥\displaystyle d = \min_{o \in \mathcal{O}} \| p - o \|
  2. 内切半径 r_i小于它就贴脸碰撞。
    d≤ri⇒C=254\displaystyle d \le r_i \Rightarrow C = 254
  3. 膨胀半径 r_o超过它作用消失。
    d≥ro⇒C=0\displaystyle d \ge r_o \Rightarrow C = 0
  4. 指数衰减中间区域按指数递减。
    C(d)=252⋅e−d−riro−ri\displaystyle C(d) = 252 \cdot e^{-\frac{d - r_i}{r_o - r_i}}

定位技术

开车到陌生街区,阿蓝会不断确认“我现在在哪个位置”:瞥见一家便利店、一个路牌,就对照地图修正判断。这个过程叫定位,Nav2 里的方案叫 AMCL(自适应蒙特卡洛定位)。

AMCL 的本质是粒子滤波。它撒出上千个“假说粒子”,每个粒子代表一个可能的机器人位姿;机器人一边动一边看雷达,同时依赖 TF 坐标树把激光数据换算到底盘坐标系,再与地图对齐——这相当于“传感器融合”:里程计给出“我大概走了多远”的预测,雷达扫描给出“我看到的世界长这样”的证据。观测与地图越吻合的粒子权重越高,最后重采样,让粒子簇渐渐收敛到真实位置。权重更新公式是:

上千个粒子就像上千个“可能站在哪”的猜测。

每一束激光都打分,整体越吻合似然越高。

与地图吻合的粒子权重大幅上升,不符的权重趋近于零。

低权重假说被淘汰,高权重假说被“复制”,粒子越聚越准。

阿蓝每到一个路口都像在“看路牌猜自己在哪”,猜错的方向被逐渐淘汰,剩下的越来越集中——粒子从一脸迷茫到锁定唯一答案,这就是定位收敛。没有这步,再好的路径规划都是空中楼阁。

wti∝wt−1i⋅p(zt∣xti)\displaystyle w^i_t \propto w^i_{t-1} \cdot p(z_t \mid x^i_t)
粒子就是“我在哪”的嫌疑犯清单,雷达观测就是目击证词:证词越符合清单里某个假说,这个假说的可信度(权重)就越高。
  1. 粒子位姿 x^i_t第 i 个假说,包含位置和朝向。
    xti=(xi,yi,θi)\displaystyle x^i_t = (x^i, y^i, \theta^i)
  2. 观测似然 p(z_t|x^i_t)假设机器人在 x^i_t,雷达扫出的样子和真实观测 z_t 有多像。
    p(zt∣xti)=∏jp(ztj∣xti)\displaystyle p(z_t \mid x^i_t) = \prod_j p(z_t^j \mid x^i_t)
  3. 权重更新用贝叶斯相乘再归一化。
    wti=wt−1i⋅p(zt∣xti)∑jwtj\displaystyle w^i_t = \frac{w^i_{t-1} \cdot p(z_t \mid x^i_t)}{\sum_j w^j_t}
  4. 重采样按权重抽签,留下高权重粒子。
    {xti}∼resample({xtj,wtj})\displaystyle \{x^i_t\} \sim \mathrm{resample}(\{x^j_t, w^j_t\})

行为树决策

老司机遇到路堵,不会原地发呆,而是按手册来:先试着绕一小段;绕不过就倒车换条道;再不行就靠边呼叫调度。这套“先试 A、不行试 B、再不行求助”的流程,在 Nav2 里就是行为树(Behavior Tree)。

Nav2 用行为树编排导航任务。树的叶子是具体动作(“转到目标点”“跟踪全局路径”“恢复位姿”),父节点是控制逻辑:Sequence(顺序)、Fallback(选择)、Parallel(并行)。每个节点运行后返回三种状态之一:Success(成功)、Failure(失败)、Running(执行中)。两种核心控制节点的语义可以用逻辑运算表达:

遇到第一个 Failure 立即短路返回失败,省时省力。

常用于“先试正常导航、不行走恢复、再不行求助”的容错链。

父节点遇到 Running 就挂起等待,避免重复下发指令。

有了行为树,阿蓝面对突发状况不再“原地死机”,而是像老司机一样按手册有序地轮换方案。这也是 Nav2 能长时间自主运行、遇到故障自动恢复而不需要人工介入的核心秘密。

Sequence(T1,T2)=T1∧T2,Fallback(T1,T2)=T1∨T2\displaystyle \text{Sequence}(T_1, T_2) = T_1 \wedge T_2, \qquad \text{Fallback}(T_1, T_2) = T_1 \vee T_2
顺序节点像“连续打卡”:一关不过就整体失败;选择节点像“备胎清单”:头一个不行就换下一个,有一个成功就成功。
  1. Sequence 与运算所有子节点都成功才算成功。
    Sseq=⋀i=1nTi\displaystyle S_{\mathrm{seq}} = \bigwedge_{i=1}^{n} T_i
  2. Fallback 或运算任一子节点成功即成功。
    Sfb=⋁i=1nTi\displaystyle S_{\mathrm{fb}} = \bigvee_{i=1}^{n} T_i
  3. 节点状态 Running节点正在执行中。
    T→{Success,Failure,Running}\displaystyle T \to \{\text{Success}, \text{Failure}, \text{Running}\}

部署与调参

新车到手都要磨合:方向盘虚位、刹车灵敏度都按司机的习惯调过才顺手。阿蓝的“车”——底盘、传感器加导航参数——也要这样调试之后才能正式接单。

部署 Nav2 要依次做好四件事:提供机器人 URDF 模型、搭好 TF 坐标树、接上激光雷达与里程计话题、用 YAML 参数文件配置规划器/代价地图/行为树。调参时常用 RViz 可视化工具观察路径、代价地图和粒子分布,再结合场景特点调整膨胀半径、速度上限和 DWA 权重——这就是“场景适配”与“性能优化”。其中最基础的模型是差速底盘的运动学:

轮子的角速度乘轮半径得到线速度。

两轮同速则直行,均值就是底盘前进速度。

L 是左右轮之间的距离(轮距),轮距越大,同样速差下转弯越平缓。

坐标变换矩阵把雷达坐标系中的点转换到机器人本体坐标。

磨合期结束后,阿蓝在测试场地跑通了直行、绕障、回充整套动作,老师傅才放心让它去仓库接单。部署调参就是让导航栈从“能跑 demo”变成“在真实场景里好用”的最后一公里。

v=vL+vR2,ω=vR−vLL\displaystyle v = \frac{v_L + v_R}{2}, \qquad \omega = \frac{v_R - v_L}{L}
这就像两人抬轿子:两人同速走,轿子直行(v 是平均速度);一快一慢,轿子就转弯(ω 取决于速度差),速度差越大转得越急。
  1. 左右轮速 v_L、v_R左右驱动轮的线速度。
    vL=ωL⋅r,vR=ωR⋅r\displaystyle v_L = \omega_L \cdot r, \quad v_R = \omega_R \cdot r
  2. 线速度 v底盘前进的快慢取左右均值。
    v=vL+vR2\displaystyle v = \frac{v_L + v_R}{2}
  3. 角速度 ω由左右轮速差产生转弯。
    ω=vR−vLL\displaystyle \omega = \frac{v_R - v_L}{L}
  4. TF 坐标变换把传感器数据映射到底盘坐标系。
    pbase=Tbaselaser⋅plaser\displaystyle p_{\mathrm{base}} = T_{\mathrm{base}}^{\mathrm{laser}} \cdot p_{\mathrm{laser}}

AI 应用场景

仓储物流 AGV 搬运机器人

阶段:导航规划与定位(Nav2 系统集成)

解决的问题:传统磁条/二维码 AGV 改线必须停机铺设轨道,成本高、柔性差;采用 Nav2 后,全局规划、代价地图和 AMCL 定位让路线可以通过软件随时调整,货架布局变了也不用动地面。

具体案例:某电商仓 30 台搬运机器人在 1.2m 宽的货架通道间穿梭,拣货员频繁走动。全局规划器在通道间选择最优路线,DWA 以高频避让人员,膨胀半径按车身半宽 0.35m 调校后,货架对接精度稳定在 ±3cm。

医院配送机器人

阶段:局部避障与任务决策(DWA + 行为树)

解决的问题:医院走廊动态障碍极多——轮椅、输液架、病床随时出没,固定路线完全不可行;同时机器人需要在电梯超时、道路被堵时自主决策。

具体案例:某三甲医院送药机器人高峰时段穿行门诊楼,行为树编排“等待电梯→进梯→避让→送达”全流程;电梯超时触发 Fallback 重新排队;雷达实时发现轮椅时,DWA 主动降速绕行。

园区巡检机器人

阶段:长时间自主巡航与回充管理(全局规划 + 行为树)

解决的问题:安保巡检路线长、需全天候自主运行,同时必须解决低电量自动回充和任务恢复的问题。

具体案例:某工业园区 2.3km 巡检路线,机器人每小时巡逻一圈;电量低于 20% 时行为树切换到“回充导航”,充电完成后自动回到中断点继续巡检,全程无需人工干预。

Python 演示

python77 行
import heapq GRID = [    [0, 0, 0, 1, 0, 0, 0],    [0, 1, 0, 1, 0, 0, 0],    [0, 1, 0, 0, 0, 1, 0],    [0, 0, 0, 1, 0, 1, 0],    [0, 0, 1, 0, 0, 0, 0],]START = (0, 0)GOAL = (4, 6)  def heuristic(a, b):    return abs(a[0] - b[0]) + abs(a[1] - b[1])  def astar(grid, start, goal):    rows, cols = len(grid), len(grid[0])    frontier = [(heuristic(start, goal), 0, start)]    g_cost = {start: 0}    came_from = {start: None}    while frontier:        _, g, node = heapq.heappop(frontier)        if node == goal:            path = []            while node is not None:                path.append(node)                node = came_from[node]            return path[::-1]        for dx, dy in ((1, 0), (-1, 0), (0, 1), (0, -1)):            nx, ny = node[0] + dx, node[1] + dy            if nx < 0 or nx >= rows or ny < 0 or ny >= cols:                continue            if grid[nx][ny] == 1:                continue            new_cost = g + 1            if new_cost < g_cost.get((nx, ny), float("inf")):                g_cost[(nx, ny)] = new_cost                came_from[(nx, ny)] = node                heapq.heappush(frontier, (new_cost + heuristic((nx, ny), goal), new_cost, (nx, ny)))    return []  def follow_path(grid, path, dynamic_obs):    rows, cols = len(grid), len(grid[0])    plan = []    robot = None    for cell in path:        if robot is None:            robot = cell            plan.append(robot)            continue        nxt = cell        if any(abs(nxt[0] - o[0]) + abs(nxt[1] - o[1]) <= 1 for o in dynamic_obs):            for dx, dy in ((0, 1), (1, 0), (0, -1), (-1, 0)):                cand = (robot[0] + dx, robot[1] + dy)                if 0 <= cand[0] < rows and 0 <= cand[1] < cols:                    if grid[cand[0]][cand[1]] != 1 and cand != robot and cand != nxt:                        nxt = cand                        break        robot = nxt        plan.append(robot)    return plan  def main():    path = astar(GRID, START, GOAL)    print("全局路径:", path)    obs = [(2, 3)]  # 动态行人    plan = follow_path(GRID, path, obs)    print("实际轨迹:", plan)    print("到达终点:", plan[-1] == GOAL)  if __name__ == "__main__":    main()
目的:用 Python 复刻 Nav2 的两段核心逻辑——A* 全局路径规划,以及沿路径移动时遇到动态障碍物的局部避障绕行。
  1. frontier = [(heuristic(start, goal), 0, start)]优先队列初始只放起点,存“f 值、g 值、坐标”三件套,对应 DWA 之前的 A* 搜索入口。
  2. _, g, node = heapq.heappop(frontier)每次弹出 f 最小的节点,就是 A* “先扩展最有希望的格子”。
  3. if new_cost < g_cost.get((nx, ny), float("inf")):只有找到更短的到达路径才更新,保证最终路径全局最优。
  4. if any(abs(nxt[0] - o[0]) + abs(nxt[1] - o[1]) <= 1 for o in dynamic_obs):用曼哈顿距离判断路径点旁是否有动态障碍,模拟 DWA 的“雷达实时发现行人”。
  5. if grid[cand[0]][cand[1]] != 1 and cand != robot and cand != nxt:在相邻格中挑一个空闲格绕行,模拟局部规划器临时微调轨迹。

常见误区

误区“我装了 Nav2 的包,然后运行它,机器人自己就会导航了吧?不是开箱即用吗?”很多初学者照着官方 demo 跑成功后,以为把自己的机器人接上 Nav2 也能零配置直奔终点。

正解Nav2 不是成品 App,而是“司机框架”。你的机器人必须自带底盘驱动(里程计)、激光雷达话题、URDF 模型、TF 树,还要先建好一张地图,并针对自己的底盘尺寸调好膨胀半径和速度限制。没这些,阿蓝上车连“自己的手脚在哪”都不知道。

为何常见:官方 demo 的模拟器把传感器、地图、底盘全配好了,让人误以为这些都是 Nav2 自带的。

误区“你看激光雷达一直在扫描,数据实时更新,这不就是知道我在哪吗?”很多初学者把“感知”当成“定位”。

正解雷达输出的是“我看见的距离”,不是“我在哪里”。想知道“我在哪”,必须把雷达扫描和已有地图“对表”——这正是 AMCL 做的事。给一台没有地图的机器人一帧扫描数据,它依然不知道自己身处何方,就像闭着眼摸到一堵墙,摸得出墙却不知道自己在哪个城市。

为何常见:可视化界面里雷达点云非常直观,初学者容易把“看到了什么”和“知道在哪”混为一谈。

误区“全局规划已经算出最优路线了,局部规划照着走不就行了?还要它有什么用?”

正解全局路径在“地图上的世界”最优,但真实世界有地图上没有的行人、推车、临时护栏。全局重规划频率低(几秒甚至更慢),局部 DWA 却以 10~20Hz 实时避障。两者是“战略”与“战术”的关系,缺一不可。

为何常见:在静态仿真里跑,全局路径看起来完美无缺,让人忽视了真实环境的动态性。

误区“既然要远离障碍物,那把代价地图膨胀半径调到最大,机器人岂不更安全?”

正解膨胀半径过大会把窄通道、门缝、货架间的缝隙全部标红甚至标成不可通行,机器人会因此绕远路,极端情况下目标点周围全是“禁行区”,导致导航失败。膨胀半径要结合机器人半径和通道宽度综合权衡——安全与可达是一对矛盾。

为何常见:直觉上“离障碍越远越好”,但忽略了地图拓扑里“必经之路”的存在。

误区“车轮转几圈走了多远是数得出来的,里程计应该一直很可靠吧?”

正解轮式里程计在刚起步时误差很小,但打滑、路面不平、轮胎磨损会让误差不断累加——这叫累积漂移,跑几十米后可能偏出几十厘米,在货架间就是撞墙事故。因此 Nav2 用 AMCL 持续拿激光雷达和地图去校准里程计,而不是只信轮子。

为何常见:启动瞬间定位看起来非常准,给用户造成“一直准”的错觉。

面试题

Q1. 在 ROS2 Nav2 中,负责从起点到终点规划整体大路线的组件是?

单选题 · easy

  • A.全局路径规划器← 正确答案
  • B.局部避障控制器
  • C.粒子滤波定位器
  • D.激光雷达驱动器

答案:A。全局规划器在静态代价地图上做 A* 搜索,给出整条路径,相当于高德地图规划路线;局部控制器只负责实时躲障碍,定位器回答“我在哪”,雷达是传感器而非规划组件。

Q2. AMCL 定位技术的核心算法是?

单选题 · easy

  • A.粒子滤波方法← 正确答案
  • B.卡尔曼滤波方法
  • C.帕累托最优方法
  • D.遗传进化算法

答案:A。AMCL 全称 Adaptive Monte Carlo Localization(自适应蒙特卡洛定位),本质是粒子滤波——用上千粒子表示位姿假说,用观测更新权重再重采样;卡尔曼滤波适合线性高斯系统,不是 AMCL 的核心。

Q3. DWA 局部规划算法首先在什么空间里采样候选运动?

单选题 · easy

  • A.速度空间 (v, ω)← 正确答案
  • B.位置空间 (x, y)
  • C.频率空间 (f, t)
  • D.颜色空间 (R, G, B)

答案:A。DWA 全称 Dynamic Window Approach,在当前速度约束出的“动态窗口”内采样线速度 v 与角速度 ω 的组合,再模拟轨迹并评分,而不是直接在地图上枚举位置。

Q4. A* 算法的估价函数 f(n)=g(n)+h(n) 中,h(n) 表示什么?

单选题 · mid

  • A.起点到 n 的实际代价
  • B.从 n 到终点的启发式估计← 正确答案
  • C.整张地图的格子数量
  • D.栅格地图的分辨率数值

答案:B。g 是已经走过的实际代价,h 是当前节点到终点的估计距离(如欧氏距离),f 是总估计;A* 每次优先扩展 f 最小的节点,在 h 可采纳时保证最优。

Q5. 代价地图中“膨胀层”的主要作用是?

单选题 · mid

  • A.给障碍物周围附加危险代价← 正确答案
  • B.压缩地图占用的存储空间
  • C.提高激光雷达的测量精度
  • D.加快全局路径的搜索速度

答案:A。膨胀层把障碍物向外“加胖”,用指数衰减给周围格子标代价,让规划器自动远离危险区;它不改变地图存储、不影响雷达硬件,也不改变搜索算法本身。

Q6. 行为树中 Fallback(选择)节点遇到第一个子节点返回成功时?

单选题 · mid

  • A.立即返回成功给父节点← 正确答案
  • B.继续尝试后面的子节点
  • C.返回失败并中断整棵树
  • D.进入等待状态反复重试

答案:A。Fallback 的语义是“或”:只要一个子节点成功就整体成功,并停止尝试后续分支。这正是“先绕行、不行换道、再不行求助”恢复逻辑的实现基础。

Q7. 差速驱动机器人的角速度 ω 与左右轮速 v_L、v_R 的关系是?

单选题 · mid

  • A.ω = (v_R - v_L) / L← 正确答案
  • B.ω = (v_L + v_R) / L
  • C.ω = (v_R - v_L) × L
  • D.ω = v_R × v_L / L

答案:A。左右轮速相等则直行;速度差越大转弯越急。角速度等于轮速差除以轮距 L,这是差速底盘运动学的基本公式,也是部署时配置底盘参数的理论依据。

Q8. 以下哪些属于 Nav2 导航栈的核心组件?

多选题 · hard

  • A.全局路径规划器← 正确答案
  • B.局部避障控制器← 正确答案
  • C.代价地图管理节点← 正确答案
  • D.机器人模型可视化工具

答案:ABC。Nav2 核心由 Planner、Controller、Costmap、AMCL、BehaviorTree 等构成;RViz 只是调试时用的可视化工具,并不属于导航栈本身,初学者常把它们混为一谈。

Q9. 关于 AMCL 粒子滤波定位,下列说法正确的有?

多选题 · hard

  • A.每个粒子代表一个位姿假说← 正确答案
  • B.观测与地图越吻合的粒子权重越高← 正确答案
  • C.定位过程不需要使用任何地图
  • D.重采样会淘汰低权重粒子← 正确答案

答案:ABD。粒子=“我在哪”的假说;雷达观测与地图匹配后用似然更新权重;重采样淘汰低权重、复制高权重粒子。若没有地图,“对表”就无从谈起,所以 C 错误。

Q10. 关于代价地图各层与更新机制,下列说法正确的有?

多选题 · extreme

  • A.静态层来自预先构建的栅格地图← 正确答案
  • B.障碍层由实时传感器数据驱动更新← 正确答案
  • C.膨胀层代价与距离呈线性反比关系
  • D.行人等动态障碍物会实时进入障碍层← 正确答案

答案:ABD。静态层来自建图结果;障碍层用实时激光数据填充,动态障碍物一旦出现就被标记;膨胀层代价按指数衰减而非线性反比,所以 C 错误。理解各层数据来源是调参的基础。