第4章 DWA算法深度剖析

DWA,全称 Dynamic Window Approach,中文叫“动态窗口法”。
说白了,它就是个“贪心”的局部规划器——每一小步都选当前最划算的速度组合。
我最早接触 DWA 是在一个差速底盘的项目上,当时机器人总在走廊里左右乱晃,后来才发现是评价函数权重没配好。嗯,这一章咱们就把 DWA 的底裤扒干净。

DWA 动态窗口法 · 核心流程 速度采样空间 (v, ω) 动态窗口 轨迹预测 前向模拟 N 步 评价函数 heading + velocity + clearance 选择最优 (v*, ω*) 最大化总评分 循环迭代 · 每个控制周期

4.1 动态窗口——速度采样空间

DWA 的第一步,就是找出所有“可能的速度组合”。
注意,不是所有速度都能用。受限于电机加减速、最大速度、安全刹车距离,真正可用的 (v, ω) 其实被限制在一个“动态窗口”里。

动态窗口的约束条件:
  • 自身最大/最小速度:v ∈ [v_min, v_max],ω ∈ [ω_min, ω_max]
  • 电机加速度限制:当前速度加减速极限 (v₀ ± a_v·Δt)
  • 安全制动:能在碰到障碍物之前刹停 (最大减速度)

我个人习惯把这三个约束画成三个圆(在速度空间里),它们的交集就是真正的“动态窗口”。
曾经有个项目,机器人一转弯就抖动,我查了半天发现是加速度限制设得太宽,窗口里包含了太多急转弯速度组合。嗯,调小加速度上限,抖动立刻消失。

4.2 轨迹预测——前向模拟

对窗口里的每一组 (v, ω),DWA 都会“脑补”未来一段轨迹。
默认预测 3~4 秒,步长 0.1~0.2 秒。说白了,就是拿运动模型往前推演,看看机器人会走到哪里。

# 伪代码示意:轨迹预测
def predict_trajectory(v, omega, dt, steps):
    x, y, theta = 0, 0, 0
    traj = []
    for _ in range(steps):
        x += v * cos(theta) * dt
        y += v * sin(theta) * dt
        theta += omega * dt
        traj.append((x, y))
    return traj

这里有个坑:如果你用的机器人是阿克曼底盘,千万别直接用差速模型。我见过有人把 car-like 机器人当差速算轨迹,结果规划出来的路径根本转不过弯。

4.3 评价函数——三要素加权

每条预测轨迹都会被打分。DWA 的标准评价函数包含三个子项:

子项含义我的经验
heading朝向误差:轨迹终点与目标点的角度差权重太大容易“直愣愣”冲向目标,忽略障碍物
velocity速度大小:鼓励快速移动我一般给 0.1~0.3,太快容易刹不住
clearance离最近障碍物的距离这个权重低了会撞墙,高了会绕远路
调参小技巧: 我通常先固定 heading 权重为 1.0,然后调 clearance 让机器人刚好能通过窄门。velocity 权重最后加,用来微调速度。曾经在仓库项目中,clearance 权重从 0.2 提到 0.5,机器人就不再蹭货架了。

4.4 避坑指南 & 个人经验

DWA 看起来简单,但实际调参时容易踩坑。我列几个常见的:

  • 窗口太小导致无解:如果动态窗口里所有轨迹都会碰撞,DWA 会直接卡住。解决办法是增大刹车距离或降低最大速度。
  • 评价函数归一化:三个子项的量纲不同,必须归一化到 [0,1]。我习惯用当前窗口内的最大值做分母。
  • 预测步数太多:预测 5 秒以上计算量暴增,而且远距离轨迹不准确。我一般设 3 秒,步长 0.15 秒。
⚠️ 曾经踩过的雷: 有一次我把 max_vel_x 设得很大,但加速度很小,导致动态窗口里全是低速组合,机器人慢得像蜗牛。后来把加速度和最大速度匹配起来,问题解决。记住:动态窗口的“动态”二字,就是强调它每帧都在变。

4.5 评价函数权重速查表

下面是我自己整理的一个初始参考范围,适合差速底盘室内导航:

参数典型范围说明
heading_weight0.8 ~ 1.5指向目标的重要性
velocity_weight0.1 ~ 0.4速度激励
clearance_weight0.2 ~ 0.8避障敏感度

你想想看,如果机器人总在空旷区域急刹车,大概率是 clearance 权重太高了。反过来,如果它贴着墙走,那就是 heading 权重过大。

最后说一句:DWA 的代码实现其实只有几百行,但调参能调一整天。