我在实验室那台两轮差速底盘上调试全自主避障时,最抓狂的是动态路径跟踪和实时障碍物规避明明分开跑都没问题,合在一起就乱套。后来我把差动驱动的运动学约束、路径跟踪的前视几何、避障的速度空间采样重新梳理了一遍,再用Matlab搭起整套仿真链路,问题才真正被解决。
这篇文章是那次调试之后沉淀下来的完整笔记。内容围绕三点展开:差动驱动机器人怎么建模、复杂环境里的动态路径跟踪和实时避障怎么选型、以及Matlab代码从仿真到实车落地时最容易踩的坑。适合正在做移动机器人相关毕设、参加机器人竞赛、或者自己搭底盘做实验的同学参考。全程以一个室内二维平面场景为主线,所有算法都遵循同一个原则——先保证安全,再谈跟踪精度。
1. 差动驱动底盘的运动学模型:两个轮子如何撑起"全自主"这个前提
很多做避障的同学一上来就写DWA、写势场,结果代码跑起来完全不像仿真里那么顺,原因往往不在避障算法本身,而是底盘模型压根没建对。差动驱动的运动学是整个系统里最基础、也最容易被一笔带过的部分,这里值得花点时间展开。
1.1 差动驱动的结构本质:为什么它能原地旋转、能沿任意曲率行走
所谓差动驱动,就是两个独立驱动的轮子分布在车体两侧,前/后方再配一个随动支撑轮或万向轮。转向完全靠左右轮速差产生:左右轮同速同向就直行,轮速差越大转弯越急,左右轮反向旋转就能原地掉头。
我用一个比较接地气的类比:差速底盘有点像双人划船,两个人同桨同速,船直着走;一个人用力一个人轻划,船就转圈;两个人反向划桨,船原地掉头。左右轮的线速度就是那两个桨,三个状态——直线、大转弯、原地自转——全都由轮速差这一个变量控制。
这个结构对避障算法来说非常重要。复杂环境里经常出现需要急转弯甚至原地调头的狭长走廊、直角弯,差速底盘的最小转弯半径是零,意味着避障算法在速度空间里能找到更多可行轨迹。换成阿克曼底盘,原地掉头基本不可能,很多局部避障策略在物理上就做不出来。
1.2 从轮速到位姿:正逆运动学的建立与离散化
差速底盘的正运动学建模,核心是把左右轮线速度vL、vR合成车体中心的线速度v和角速度w:
% 基础公式 v = (vL + vR) / 2; % 车体中心线速度 w = (vR - vL) / B; % B 是左右轮轮距这里的B是左右轮中心之间的距离,不是你随手量的车宽。很多同学直接拿底盘外壳宽度当轮距,导致仿真里转得过来、实车转不过来,误差就是这么来的。
有了v和w,接下来用离散化递推更新位姿。模型假设机器人做圆弧运动,当前位姿[x, y, theta],控制周期dt内状态按如下方式更新:
function state = update_kinematics(state, v, w, dt) x = state(1); y = state(2); th = state(3); x_next = x + v * cos(th) * dt; y_next = y + v * sin(th) * dt; th_next = th + w * dt; state = [x_next, y_next, th_next]; end这个函数是后面所有仿真的地基。路径跟踪和避障输出的(v, w)都要经过它才能变成机器人的实际位置,如果模型更新有误,后面所有评估都失真。
逆运动学同样常用,给定期望的(v, w),反解左右轮速:
vL = v - B * w / 2; vR = v + B * w / 2;注意这里有个隐含约束:vL、vR都不能超过电机最大转速对应的线速度上限。逆解结果超出约束时,不能直接截断某一侧,而应该整体等比例缩放,否则会打破左右轮比例,导致机器人实际走的圆弧不是你想走的圆弧。
1.3 为什么全自主避障研究普遍采用差动驱动而不是阿克曼、全向轮
我自己的实验平台上试过三种底盘。阿克曼底盘在高速室外场景确实有优势,转向机构决定它的运动更"像车",但室内走廊和动态障碍物场景里,它没法原地掉头,倒车避障还得单独设计一套逻辑;全向轮底盘理论上可以平移,控制维度多了vy,灵活性拉满,但轮子结构复杂、地面要求高,实际跑起来反而容易因为某个方向的微小漂移让控制器来回修正。
这三种底盘放在一起对比会更直观:
| 底盘类型 | 最小转弯半径 | 控制维度 | 原地自转 | 典型场景 | 避障算法友好度 |
|---|---|---|---|---|---|
| 差动驱动 | 0 | v, w | 支持 | 室内狭窄空间 | 高,速度空间二维采样 |
| 阿克曼 | 大于0 | v, 转向角 | 不支持 | 室外高速巡航 | 中,候选轨迹受非完整性约束 |
| 全向轮 | 0 | vx, vy, w | 支持 | 精密对接、狭小工位 | 高但三个通道易耦合抖动 |
全自主差动驱动机器人的避障采样空间是二维的(v, w),计算量天然可控。全向轮底盘虽然多了平移自由度,但控制器一旦在某个方向上有偏差,三个通道互相牵扯,反而更容易把系统调发散。这也是为什么很多做移动机器人避障的竞赛和课程项目都默认选择差速底盘——它把"全自主"里最难处理的环境约束问题,压缩成一个足够表达、又足够简单的问题。
2. 路径跟踪的核心矛盾:不是画得出来,而是跟得准、跟得稳
路径跟踪和路径规划最大的区别在于:规划回答"往哪走",跟踪回答"怎么控制轮子才能贴着那条路走"。很多人的路径跟踪一开始效果差,未必是算法选错,而是误差定义、控制律参数、路径预处理三个环节没配合好。
2.1 误差从哪来:横向偏差与航向偏差的定义与计算
在我调试的差速底盘场景里,参考路径是一串离散的航点path = [x_ref, y_ref]。机器人当前位置到路径的偏差,我不会只看欧氏距离,而是拆成两个量:
- 横向偏差
e_c:机器人到最近路径段的垂直距离,用它判断机器人是不是偏离了车道中心线。 - 航向偏差
e_h:最近路径段的朝向角和机器人当前航向角的差值,用它判断车头是否对准了路径方向。
横向偏差的计算,在Matlab里最直接的方式是遍历路径点,找到离当前位置最近的点,然后用向量叉积判断偏差符号:
function [e_c, e_h] = get_path_error(pose, path) % pose: [x, y, theta] dx = path(:,1) - pose(1); dy = path(:,2) - pose(2); [~, idx] = min(dx.^2 + dy.^2); if idx == length(path) idx = idx - 1; end % 路径段向量和机器人相对起点的向量 seg_vec = [path(idx+1,1)-path(idx,1), path(idx+1,2)-path(idx,2)]; rel_vec = [pose(1) - path(idx,1), pose(2) - path(idx,2)]; % 叉积决定横向偏差符号 cross_val = seg_vec(1)*rel_vec(2) - seg_vec(2)*rel_vec(1); e_c = cross_val / norm(seg_vec); e_h = atan2(seg_vec(2), seg_vec(1)) - pose(3); end这个误差定义比单纯"到目标点距离"实用得多。因为跟踪是沿着整条路径走的,机器人在路径中间某点附近可能离终点很远,但横向偏差很小,说明当前控制是好的;反之距离目标点很近但如果横着插进路径,横向偏差很大,控制需要先纠正方向。
2.2 纯追踪算法:一个圆轨拟合的前视几何逻辑
差速底盘路径跟踪我推荐从纯追踪(Pure Pursuit)入手,因为它只用到一个几何量——前视距离,物理意义非常清晰,也特别适合差速底盘输出(v, w)的圆弧运动。
算法的核心就一句话:从当前位置出发,沿路径往前找到距离为Ld的前视点,然后计算一条能连接当前位置和前视点的圆弧,控制机器人沿这条圆弧走。
function [v_cmd, w_cmd] = pure_pursuit(pose, path, Ld, v_max) % 找前视点 distances = sqrt((path(:,1)-pose(1)).^2 + (path(:,2)-pose(2)).^2); idx = find(distances >= Ld, 1, 'first'); if isempty(idx) idx = length(path); end target = path(idx, :); % 前视点相对机器人航向的角度差 alpha = atan2(target(2)-pose(2), target(1)-pose(1)) - pose(3); % 圆弧曲率,负号保证角速度方向正确 kappa = 2 * sin(alpha) / Ld; v_cmd = min(v_max, 0.4); w_cmd = v_cmd * kappa; end这里的alpha是前视点相对机器人航向的方向角差,kappa是圆弧曲率。角速度命令w = v * kappa,即线速度乘以曲率,这个形式跟差速底盘的运动学是天然匹配的。
前视距离Ld是整个算法最关键的一个参数,我实际测试出来的规律非常明显:
Ld太小,比如小于0.3m,机器人像喝醉了一样左右摇摆,横向偏差一直在零附近震荡。Ld太大,比如超过1.5m,机器人会切内弯,转弯处会明显抄近道,离参考路径越来越远。- 经验取值是让
Ld跟着速度走,速度高时看得远一点,速度低时看得近一点,我常用Ld = 0.5 + 1.2 * v。
从控制角度看,纯追踪其实是一个以alpha为误差、增益约等于2v/Ld的比例控制器。alpha小时sin(alpha)≈alpha,角速度跟角度差近似成正比,所以调Ld本质上就是在调比例增益。
2.3 参考路径预处理:航点重采样、平滑与连续化
路径跟踪效果不好,有时候不是控制的问题,而是参考路径本身太毛糙。原始路径往往是全局规划器给的一堆离散航点,点之间距离可能差很大,还可能有尖锐转角,直接丢给纯追踪很容易出问题。
我在仿真里先对路径做三件事:
- 重采样:按固定间距比如0.05m线性插值,保证前视点搜索时路径密度足够,不会因为相邻点距离太大而跳变。
- 平滑:对重采样后的路径做三次样条或移动平均,去掉尖角。纯追踪本身不要求路径平滑,但路径上如果存在90度折角,机器人到拐角附近时前视点会突然从拐角前跳到拐角后,导致角速度命令跳变。
- 去重:删除重合点和距离过近的点,避免最近点搜索出现歧义。
预处理之后还要给路径配上速度规划。我的做法很朴素:先遍历路径计算每一点的曲率,曲率大的地方把v_cmd降下来,弯道之后再加回去。这样路径跟踪和后面的避障融合时,速度命令不会频繁到达上限,给局部避障留出了速度余量。
3. 实时避障的三条技术路线对比:势场、直方图与动态窗口的选型思考
动态路径跟踪解决的是"怎么沿着既定路线走"的问题,实时避障解决的是"半路杀出个障碍物怎么办"的问题。我在选避障算法时对比过人工势场、向量场直方图和动态窗口法这三条路线,各有各的适用场景,但最终实车用的是动态窗口法。
3.1 人工势场法:直观但容易卡在局部极小值
人工势场法(Artificial Potential Field)的思路很直观:目标点产生引力,把机器人往目标方向拉;障碍物产生斥力,把机器人往远处推;合力方向就是期望运动方向。
引力: F_att = -k_att * (p - p_goal) 斥力: F_rep = k_rep * (1/d - 1/d0) / d^2 * n它的优点是代码量极小、实时性极高,做快速原型验证特别方便。但有一个致命问题:局部极小值。比如机器人、障碍物、目标点排成一条线,引力和斥力恰好大小相等方向相反,机器人会卡在原地打转,怎么都出不来。
我在测试里复现过这个场景——一条窄通道正中放一个箱子,机器人走到箱子正前方就停住了,左右试探一系列小扰动也出不来。有人会加随机扰动让它跳出局部极小值,但扰动幅度小了没用,大了容易撞障碍物,在动态障碍物场景里并不靠谱。人工势场法更适合做静态障碍物快速避让的Demo,复杂动态环境用它作为主力避障器,调试成本非常高。
3.2 向量场直方图(VFH+):把传感器数据转成方向候选
向量场直方图(Vector Field Histogram)的逻辑跟势场完全不同,它先把激光或超声波数据映射到一个局部栅格地图,再按角度投影成极坐标直方图,找出没有障碍物阻挡的可行方向扇区,然后从这些扇区里选一个代价最低的方向作为前进方向。
VFH+的优点是对传感器噪声比较鲁棒,因为它把空间分块统计,单点噪声不会直接改变输出方向。计算量也小,很适合跑在嵌入式板卡上。
但我试下来发现一个问题:VFH+给的是"方向",对差速底盘来说还得自己去拍板用什么线速度。通道宽时方向很顺,速度可以快;通道窄时方向虽然没有撞,但速度不减,车照样冲进危险区。所以我在集成VFH+时还得额外写一套速度-安全距离映射规则,相当于半套避障逻辑,整体复杂度反而上去了。
3.3 动态窗口法DWA:为什么我最终把它作为实车首选
动态窗口法(Dynamic Window Approach)解决了我前面说的两个痛点:它直接输出(v, w),而且天然考虑了差速底盘的动力学约束。
DWA的思路是这样的:
- 在速度空间
(v, w)里按分辨率采样出大量候选速度对。 - 用三个约束筛掉不可行的候选:
- 加速度约束:当前速度加减速度极限内能到达的速度才是可达的,相当于"刹车距离内能做到的动作"。
- 碰撞约束:按候选速度模拟未来一小段时间的轨迹,轨迹上与障碍物最近距离要大于机器人膨胀半径。
- 速度物理约束:不能超过最大线速度、最大角速度。
- 用评价函数给剩余候选打分,选最高分作为输出。
评价函数我常用的是三项加权:
heading:轨迹终点方向与目标方向的夹角,越接近越高分。clearance:轨迹离障碍物最近距离,越大越安全。velocity:速度本身,倾向于走得快一些。
score = a * heading + b * clearance + c * velocityMatlab里DWA的核心循环可以浓缩成下面这一段:
function best = dwa_control(pose, vel, obstacles, goal, params) v_min = max(0, vel(1) - params.max_acc * params.dt); v_max = min(params.vmax, vel(1) + params.max_acc * params.dt); w_min = max(-params.wmax, vel(2) - params.max_wacc * params.dt); w_max = min(params.wmax, vel(2) + params.max_wacc * params.dt); best = [0, 0, -inf]; for v = v_min:params.v_res:v_max for w = w_min:params.w_res:w_max traj = simulate_traj(pose, v, w, params.predict_steps, params.dt); if is_collision_free(traj, obstacles, params.robot_radius) score = eval_score(v, w, traj, goal, obstacles); if score > best(3) best = [v, w, score]; end end end end end我选DWA的原因很具体:它的采样空间和差速底盘的输出空间完全一致,不需要额外做方向到速度的转换;动态障碍物场景下,每次控制周期都重新采样规划,天然具备实时反应能力;而且仿真里容易可视化——把候选轨迹画出来,一眼就能看出为什么某个速度选了没选,调试起来非常直观。
三种避障算法放在一起,选型时大概是这样:
| 避障算法 | 输入 | 输出 | 动态障碍 | 局部极小 | 计算量 |
|---|---|---|---|---|---|
| 人工势场 | 距离向量 | 合力方向 | 一般 | 有 | 低 |
| 向量场直方图 | 点云/栅格 | 方向扇区 | 一般 | 无 | 低 |
| 动态窗口法 | 距离+速度 | v, w | 好 | 无 | 中 |
4. 路径跟踪与避障融合:仲裁逻辑、速度平滑与状态估计
把路径跟踪和避障单独跑通,只是第一步。真正让整套系统在全自主场景里稳定运行的,是两者之间的融合策略。我在调试过程中发现,融合做不好,算法再先进也是纸上谈兵。
4.1 全局路径跟踪与局部避障的目标冲突
一个很典型的冲突场景:机器人沿着参考路径走,前方出现一个障碍物,路径跟踪希望机器人继续沿中线走,DWA为了保证安全距离却给出一条贴墙的弧线。两个模块互相拉扯,输出的w一会正一会负,机器人在原地扭来扭去,像在跳机械舞。
我的解决方案是一个两层状态机,而不是无脑地求两个控制量的加权平均:
if min_obs_dist > safe_dist % 安全模式:跟踪参考路径 [v_cmd, w_cmd] = pure_pursuit(pose, ref_path, params.Ld, params.vmax); elseif min_obs_dist > danger_dist % 过渡模式:压低速度,保持当前避障方向 v_cmd = 0.3 * params.vmax; w_cmd = last_w; % 保持上次有效转向 else % 避障模式:DWA主导 [v_cmd, w_cmd] = dwa_control(pose, vel, obstacles, target, params); end这里有个关键设计:从避障切回跟踪时,一定要加滞回(hysteresis)。比如障碍物距离低于0.3m进入避障,但要等到距离大于0.5m才切回跟踪,否则机器人会贴着安全边界来回切换,反复横跳。这个滞回带在实际调试中比任何权重改进都有效。
还有一个技巧:避障模式里DWA的目标点不要设成全局终点,而是设成路径跟踪当前的"前视点"。这样机器人即使绕行,也是在朝路径前方绕,绕完之后能自然回到参考路径上,而不是被DWA带得偏离路径越来越远。
4.2 输出速度跳变是杀手:限加速度与平滑滤波
融合策略做好之后,下一个坑是速度跳变。路径跟踪模式下可能输出v=0.5,避障模式下瞬间变成v=0.1,底盘机械结构受不了,IMU数据也抖得不行,甚至导致电机过流保护触发,整车直接断电——这个问题我实车踩过。
解决办法是在最终输出前加一层速度平滑,核心是两个环节:
- 限加速度:命令速度不能比上一周期速度突变超过
max_acc * dt。 - 一阶低通滤波:进一步平滑高频抖动。
% 加速度限制 v_cmd = clamp(v_cmd, last_v - max_acc*dt, last_v + max_acc*dt); w_cmd = clamp(w_cmd, last_w - max_wacc*dt, last_w + max_wacc*dt); % 低通平滑 alpha = 0.4; v_smooth = alpha * v_cmd + (1 - alpha) * last_v_smooth; w_smooth = alpha * w_cmd + (1 - alpha) * last_w_smooth;alpha取0.3到0.5之间比较合理。太小响应迟钝,机器人过弯会滞后;太大又起不到平滑效果。我的经验是alpha=0.4时,避障响应和速度稳定性平衡得最好。
4.3 传感器噪声与状态抖动:避障算法为什么"抽风"
仿真里激光数据是干净的,实车上传感器噪声却能让DWA的输出"发疯"。最典型的情况是单帧激光最小值跳动:一帧读到0.35m,下一帧读到1.2m,明明障碍物没动,DWA的速度却一会儿大一会儿小。
我排查了很久才发现,问题不在DWA,而在数据预处理。激光打到某些反光表面或玻璃上,会产生飞点,直接取最小距离会被这些假数据牵着走。我现在的方法是:
- 对原始距离序列做一次中值滤波,窗口大小取5到7,能有效去掉飞点。
- 用欧氏聚类把相邻障碍物点聚成簇,取每个簇的中心和最近距离,而不是用全局最小距离。
- 对连续帧的障碍物位置做一维卡尔曼滤波,让障碍物坐标平稳变化,DWA的参考输入也随之平稳。
卡尔曼滤波在Matlab里实现一维版本很快,状态量取障碍物距离d,观测是激光测得距离,预测和更新两步就能跑起来。加了滤波之后,DWA输出的w基本不再出现无规则抖动,避障行为变得可预测,这是整个融合系统稳定运行的关键一步。
5. Matlab仿真从模型到可视化:一份可以直接改的主循环骨架
很多文章讲避障算法只讲数学原理,不讲怎么让整个仿真跑起来。我带过一个做竞赛的同学,他照着论文把DWA的核心函数写出来了,但不知道怎么把机器人的位置更新、传感器模拟、路径可视化和避障决策串成一个闭环,最后卡了将近一周。下面这部分就是解决这个问题的。
5.1 仿真环境怎么搭:地图、障碍物与机器人表示
Matlab里做二维避障仿真,不需要引入重型物理引擎。最简单的方案是:
- 地图用坐标范围描述,比如10m乘10m的空间。
- 静态障碍物用矩形列表表示,每个矩形记录
[x, y, 宽, 高, 航向]。 - 动态障碍物用位置和速度向量表示,每个控制周期更新位置。
- 机器人用一个小矩形+自身朝向表示,用
patch函数画出来,更新时改XData、YData。
我之前的主循环里,障碍物是这个结构:
% 静态矩形障碍物 static_obs = [ 3.0, 2.0, 1.5, 0.8, 0; 6.0, 5.0, 1.0, 1.2, pi/4; ]; % 动态圆形障碍物:位置、半径、速度 dyn_obs = struct('pos', [7.0, 3.0], 'r', 0.3, 'vel', [-0.4, 0.2]);传感器模拟用简单的射线扫描:从机器人位置出发,向周围360度等间隔发射射线,每条射线检测与所有障碍物的最近交点,返回距离。这个函数只要几十行,跑起来比真实激光数据干净,足够验证避障逻辑。
5.2 主循环:感知-决策-执行三步怎么串起来
仿真主循环分成感知、决策、执行三步,代码结构清晰,后面改成真实机器人时也容易映射到传感器读数、算法模块和电机控制上。
dt = 0.1; T = 30; for t = 0:dt:T % 1. 感知:模拟激光扫描,得到障碍物距离集合 ranges = lidar_scan(robot, [static_obs; dyn_obs]); % 2. 决策:安全则路径跟踪,危险则DWA避障 if min(ranges) > params.safe_dist [v_cmd, w_cmd] = pure_pursuit(robot, ref_path, params.Ld, params.vmax); mode = 'track'; else [v_cmd, w_cmd] = dwa_control(robot, vel, obstacle_clusters, ref_path(idx_fwd,:), params); mode = 'avoid'; end % 3. 平滑、执行、更新 [v_cmd, w_cmd] = smooth_cmd(v_cmd, w_cmd, last_smooth, dt, params); robot = update_kinematics(robot, v_cmd, w_cmd, dt); vel = [v_cmd, w_cmd]; % 更新动态障碍物位置 dyn_obs.pos = dyn_obs.pos + dyn_obs.vel * dt; % 画图 plot_frame(robot, ref_path, ranges, mode); end控制周期dt=0.1s,也就是10Hz的控制频率。这个频率下,simulate_traj的预测步数如果取20步,每步0.1s,就是向未来看2秒,DWA计算量在10Hz下完全跑得动,可视化也不会卡。
5.3 调试可视化:只看曲线没用,要看时间和空间双轴
仿真调试时,只画一条机器人轨迹是远远不够的。我每次跑完一条路线,都会同时画这几样东西:
- 参考路径、机器人历史轨迹、当前位置和前视点。
- 当前帧的激光射线,能看到传感器"看到"了什么。
- 机器人的朝向矩形,标注当前模式是track还是avoid。
- 速度、角速度随时间变化的曲线,用来判断控制指令是否平滑。
画图时有个性能细节:不要每帧都plot新建图形对象,而是先figure并保存句柄,每帧只改XData、YData,最后用drawnow limitrate刷新,这样仿真帧率和实时性都好很多。我之前直接每帧plot,跑到20秒之后动画开始卡顿,改成句柄更新后顺畅不少。
可视化还有一个容易被忽略的价值:它能把DWA的"决策过程"展示出来。把采样的候选轨迹全部画成半透明线段,评分最高的加亮,你一眼就能看出为什么机器人在某个时刻选择了减速而不是绕行。这个视角对调参的帮助远大于看最终轨迹曲线。
6. 从仿真到实车:三个我踩过的坑与排查链路
仿真跑得再漂亮,到了实车总是会冒出意想不到的问题。这一节我复盘了三个自己踩过的坑,每个都附上完整的排查链路,给后续做差速底盘避障的同学一个参考。
6.1 坑一:轮子打滑让里程计漂移,纯追踪在长走廊偏航
现象:仿真中纯追踪效果很好,实车在空旷场地跑也没问题,但一上长走廊,第一圈还行,第二圈开始明显偏航,越跑越偏。
排查链路:
- 先怀疑运动学模型参数,重新测量轮距和轮半径,排除参数错误。
- 再检查电机控制频率,确认
v、w到电机vL、vR的反解没有截断错误。 - 最后让底盘低速直线跑5米,看里程计反推的位置是不是真的走了5米——一测发现只有4.2米。
根因是轮子在光滑地面上打滑,里程计认为走了5米,实际只走了4.2米,累计误差让纯追踪的前视点不断偏移。
解决方案不复杂:换成摩擦系数更大的橡胶轮,起步和刹车的加速度调小,避免打滑;同时在航向估计里加入陀螺仪的角速度积分,对里程计的theta做互补滤波。修完之后,长走廊偏航问题基本消失。这也印证了第一节说的,刚性轮无滑动假设在真实环境里要打很多折扣,传感器修正比单纯调算法参数更有效。
6.2 坑二:动态障碍物突然变向,DWA窗口来不及反应
现象:一个人匀速横穿机器人前方的路径,DWA能稳稳避开;但他走一半突然停住并反向走,机器人直接撞上去。
排查链路:
- 打开DWA的轨迹可视化,发现预测轨迹只向后模拟了3步,也就是0.3秒,这么短的预测时长根本来不及感知"对方反向运动"。
- 检查发布到DWA的障碍物速度信息,发现小车侧除了距离,完全没有障碍物速度输入,DWA把动态障碍物当成了瞬时的静态障碍物,每个周期重新采样,所以"反应"总是慢半拍。
- 计算了一下刹车距离:0.6m/s的最大速度配0.5m/s²的加速度,刹车停下需要0.36m,加上传感器测距误差和安全余量,膨胀半径只有0.25m是明显不够的。
解决方案是三个同时改:
- 预测步数从3步增加到15步,让DWA在决策时看到未来1.5秒内会不会和障碍物轨迹相交。
- 障碍物按速度外推,用简单的碰撞时间TTC判断,如果TTC小于2秒就强制减速。
- 最大线速度从0.6m/s降到0.4m/s,膨胀半径从0.25m加大到0.35m。
这组改动之后,动态障碍物场景的成功率明显上升。我个人感受很直观:在安全性这件事上,速度上限和膨胀半径的安全裕量,比算法本身的复杂度和炫技重要得多。
6.3 坑三:参数整定没有顺序,最后全都乱
现象:路径跟踪的前视距离、DWA的权重系数、加速度限制等一堆参数一起调,今天调好了,明天换一个场景又崩,最后参数之间互相干扰,根本不知道是哪个参数导致的。
我后来总结了一套调整顺序,基本稳定:
- 先关掉避障,在空旷场地上单独调纯追踪的
Ld,直到路径跟踪的横向偏差稳定在一个范围内。 - 再放静态障碍物,调DWA的膨胀半径和
b权重,保证静态场景不碰撞。 - 然后放动态障碍物,调最大速度、加速度限制、预测步数,确保动态场景能安全通过。
- 最后做长距离随机路线测试,检查偶发的速度震荡和模式切换抖动,再微调平滑系数。
一张初始参数表可以这么设:
| 参数 | 含义 | 初始范围 | 调试规律 |
|---|---|---|---|
| Ld | 纯追踪前视距离 | 0.5 - 1.2 m | 偏小则振荡,偏大则切弯 |
| 膨胀半径 | 障碍物安全距离 | 车宽一半 + 0.2 m | 偏小易碰撞,偏大绕路 |
| DWA权重(a,b,c) | 方向、间隙、速度 | 1.0, 1.5, 0.8 | 权重悬殊会锁死行为 |
| 最大加速度 | 线速度变化率 | 0.4 - 0.6 m/s² | 太大易漂移,太小反应慢 |
| 最大角速度 | 转向变化率 | 1.0 - 1.5 rad/s | 走廊转向不够就调高 |
这套流程跑下来,我个人最大的体会是:避障系统的上限不取决于单点算法多聪明,而取决于你愿不愿意留出速度、距离和参数上的安全冗余。把最大速度降下来、把膨胀半径留足、把加速度限制写紧一点,成功率往往比各种高级手段更明显。如果你也在做类似的差速底盘项目,建议严格按照先静态后动态的顺序去调参,八成能少走很多弯路。