☰
基于Q-Learning的无人机三维动态避障MATLAB实现与调参实践
2026/10/21 4:39:53 网站建设 项目流程

做无人机路径规划,动态避障是绕不开的坎。二维平面上的动态避障已经够折腾了,放到三维空间里,状态空间和搜索复杂度直接上了一个量级。我自己把A*、RRT、人工势场法都试过一圈之后发现,如果任务场景相对固定、障碍物运动有规律,用强化学习里的Q-Learning来做三维动态避障,反而是一个性价比很高的选项——不需要显式建模环境,训练完直接查Q表在线决策,实时性非常好,MATLAB写起来也不复杂。这篇文章就从原理建模开始,一直讲到MATLAB代码实现、调参踩坑和仿真结果分析,适合正在做无人机避障和路径规划的同学,也适合已经看过Q-Learning原理但不知道怎么落地到三维动态场景的朋友。


1. 项目背景与整体设计思路

1.1 三维动态环境到底难在哪

无人机路径规划天然是三维问题。相比二维平面,三维空间多了高度维度,状态搜索空间是指数级膨胀的。假设栅格边长为n,二维的状态数是n^2,三维就变成n^3。如果你用12×12×12的栅格,状态数就是1728个,这个规模对Q-Learning来说非常友好;但如果你直接上50×50×50,状态数变成12.5万个,查表型Q-Learning就会开始吃力,这是后面要心里有数的。

更麻烦的是“动态”两个字。静态环境下A*跑一次就够,障碍物一旦开始移动,静态路径就成了废纸,得频繁重规划。重规划本身不复杂,复杂的是你得不断感知外部变化、不断重算。这时候Q-Learning的优势就体现出来了:它是model-free的,不需要预测障碍物下一秒在哪,而是在训练阶段把这些动态规律直接“消化”进Q表里,部署的时候就是查表,单步决策几乎零计算量。

1.2 为什么选Q-Learning而不是A*/RRT/APF

我最初也觉得,动态避障不应该用人工势场法吗?反应快、结构简单,确实是避障利器。但APF最头疼的就是局部极小值,无人机经常卡在凹形障碍区前原地打转。A*和RRT系列呢,本身是为静态规划设计的,动态场景下要么全图重算,要么频繁重新采样,实时性很难保证。

方法是否依赖环境模型动态避障方式主要瓶颈
A*依赖静态地图障碍变化后全图重规划高维搜索慢、重规划开销大
RRT/RRT*依赖碰撞检测模型动态障碍需频繁重采样和重新连接路径不平滑、实时性不稳定
人工势场法依赖势场模型反应式避障很快局部极小值问题,容易卡死
Q-Learning不需要环境模型训练阶段学到障碍时间规律,决策时查表状态离散化粒度影响质量,随机动态障碍泛化有限

所以我的结论是:如果你要做的动态避障场景是固定区域重复运行的,比如某条巡检航线、某个仓库内的无人机配送路线,障碍物的运动有一定模式,那Q-Learning就是相当合适的选择。它可以把这些时间规律学习成一套完整的经验策略。

1.3 整体方案框架

整套方案可以拆成四个环节:三维环境建模、Q-Learning训练核心、动态障碍物模拟、路径提取与可视化。

  • 三维环境建模:用均匀栅格描述空间,无人机位置离散化为整数坐标。
  • 状态与动作设计:状态就是无人机当前所在栅格坐标,动作是六个方向的单位移动。
  • 奖励函数:到达目标给高奖励,碰撞障碍物给高惩罚,每走一步给一个小惩罚,引导最短时间和安全飞行。
  • 动态障碍物模拟:让障碍物按固定轨迹周期运动,在训练时同步更新位置。

这个框架也方便后续扩展。栅格改细、障碍物数量加大、奖励函数加距离引导项,都不需要动主骨架。


2. Q-Learning核心原理与三维建模细节

2.1 强化学习五要素映射到无人机场景

Q-Learning本质上是解一个马尔可夫决策过程。在三维避障里,四个要素非常清晰:

  • 状态空间S:无人机当前所在的三维栅格坐标(x, y, z)。
  • 动作空间A:向x、y、z轴正负方向各移动一格,一共六个动作。
  • 奖励函数R:每步移动后的即时反馈,后面单独展开。
  • 折扣因子γ:未来奖励的衰减程度,γ越大越看重远期收益。

训练的目标是学出一个Q表,表中每个“状态-动作”对对应一个长期累积回报的估计值。决策时直接看当前状态哪个动作的Q值最大,就选哪个。这也是Q-Learning能在部署时做到实时决策的根本原因。

2.2 状态空间与动作空间怎么设计

状态空间我推荐直接用栅格坐标。MATLAB里计算状态索引很方便:

idx = sub2ind(gridSize, pos(1), pos(2), pos(3));

这里gridSize是[行数, 列数, 层数],sub2ind把三维坐标映射成一维索引,Q表就用这个索引当行号。

动作空间为什么是六个方向而不是26个方向?三维空间里对角线移动看似更灵活,但在栅格地图中存在“穿过障碍物棱角”的问题。一个障碍物占据一个栅格时,对角线移动可能会从八个相邻格子的缝隙里穿过去,这在路径规划里是常识性的大坑。正交移动严格沿着坐标轴方向,一个一个格子走,就不会有这种穿透风险。六个动作对入门和工程验证都够用,后面想优化路径平滑度,再加悬停动作或者后处理曲线即可。

2.3 奖励函数设计:整个项目的灵魂

奖励函数直接决定智能体学出来的行为风格。我常用的一个稳定版本是这样:

  • 到达目标点:+100
  • 碰撞静态或动态障碍物:-100
  • 每一步移动:-1
  • 可选的距离引导项:0.5 × (上一时刻到目标的距离 - 当前时刻到目标的距离)

前三条是基础。每一步的-1是时间惩罚,逼着无人机走最短时间路径,没有这一项,无人机就可能在空地上反复游荡。距离引导项的作用是解决稀疏奖励问题——单纯靠终点+100奖励,前期探索效率极低,无人机随机乱撞很久才可能碰到目标。有了距离引导,它在每步都能感到“靠近目标有奖励、远离目标有惩罚”,训练收敛速度快非常多。

不过距离引导项的权重一定要控制。我实测过权重从0.5调到2.0之后,无人机会变得非常激进,为了先缩短直线距离,宁可往障碍物方向冲。0.5这个值是一个比较稳健的起步点。

奖励更新的标准公式是:

Q(s,a) ← Q(s,a) + α × [r + γ × max_a' Q(s',a') − Q(s,a)]

2.4 关键参数怎么定

我常用的参数组合:

参数含义推荐值说明
α学习率0.1~0.3越大学得越快但容易震荡
γ折扣因子0.9~0.99越大越看重长期收益,收敛变慢
ε探索率1.0→0.05训练开始时全随机,逐步减少
ε衰减每回合衰减系数0.995衰减太快会过早收敛到次优策略
maxEpisodes最大回合数300~50012×12×12栅格实测足够

这个组合的物理意义是:初期让无人机大胆探索整个空间,随着训练进行逐渐转入利用Q表已有经验。如果ε衰减太慢,后期一直在乱飞,收敛不了;衰减太快,又会过早锁定在局部最优路径上。

2.5 动态障碍物的“时间模式”问题

这是动态避障最关键、也最容易翻车的地方。如果状态里完全不含时间信息,Q-Learning学到的只是动态障碍物长期出现的“平均风险区域”,策略会变得非常保守。想让它真正学会“等障碍物过去了再穿过去”,有一个简单实用的做法:动态障碍物按固定轨迹周期运动,每个回合开始都从初始相位起算。这样相当于状态中隐含了障碍物的相位信息,智能体能通过大量回合学到时序避让规律。

如果障碍物是完全随机出现的,Q-Learning查表策略能做的就只有绕开所有高概率危险区域了。这种情况更合理的设计是Q-Learning做主路径规划,再在局部加一个反应式避障层兜底,比如小范围的人工势场法或者动态窗口法。后面我会再细说。


3. MATLAB核心代码实现与解析

3.1 环境初始化与静态障碍物生成

主脚本开头先把空间尺寸、起点、终点和静态障碍物定义好。静态障碍物随机生成时要避开起点和终点,这个细节很多人会漏掉,一旦起点被障碍物占住,训练永远不可能成功。

%% 环境参数 gridSize = [12 12 12]; startPos = [1 1 1]; goalPos = [12 12 12]; nStaticObs = 20; nDynObs = 3; %% 随机生成静态障碍物 staticObs = zeros(0, 3); while size(staticObs, 1) < nStaticObs p = randi([1 12], 1, 3); if isequal(p, startPos) || isequal(p, goalPos) continue; end if ~ismember(p, staticObs, 'rows') staticObs(end+1, :) = p; end end

3.2 动作空间与状态索引辅助函数

六个动作分别对应坐标增减。子函数放在脚本末尾,MATLAB会自动识别。

% 动作映射:1:+x 2:-x 3:+y 4:-y 5:+z 6:-z dMov = [1 0 0; -1 0 0; 0 1 0; 0 -1 0; 0 0 1; 0 0 -1]; function idx = stateIndex(pos, gridSize) idx = sub2ind(gridSize, pos(1), pos(2), pos(3)); end

3.3 动态障碍物模拟

动态障碍物我用正弦曲线轨迹,这样方便控制周期和幅度。每个障碍物有中心点、在x和y方向上的摆动幅度,以及各自的周期。这样设定可以让障碍物的运动会重复出现,方便Q-Learning学习规律。

function obs = getDynObs(t, dynParams, gridSize) n = size(dynParams, 1); obs = zeros(n, 3); for i = 1:n % dynParams(i,:) = [中心x 中心y z 振幅x 振幅y 周期x 周期y] px = dynParams(i,1) + dynParams(i,4) * sin(2*pi*t / dynParams(i,6)); py = dynParams(i,2) + dynParams(i,5) * cos(2*pi*t / dynParams(i,7)); obs(i,1) = max(1, min(gridSize(1), round(px))); obs(i,2) = max(1, min(gridSize(2), round(py))); obs(i,3) = dynParams(i,3); end end

动态障碍物参数矩阵示例:

dynParams = [ 6 6 6 3 2 20 15; 4 9 7 2 3 25 18; 9 4 5 3 1 22 20; ];

3.4 Q-Learning主训练循环

训练循环是核心。每个回合从起点开始,重置动态障碍物相位,然后不断执行“选动作→移动→拿奖励→更新Q值”的循环。碰撞或到达目标就结束当前回合。

alpha = 0.2; gamma = 0.95; epsilon = 1.0; epsilonDecay = 0.995; minEpsilon = 0.05; maxEpisodes = 500; maxSteps = 500; numStates = prod(gridSize); numActions = size(dMov, 1); Q = zeros(numStates, numActions); episodeRewards = zeros(maxEpisodes, 1); for ep = 1:maxEpisodes pos = startPos; t = 0; % 动态障碍物相位,每个回合重置 totalReward = 0; dynObs = getDynObs(t, dynParams, gridSize); for step = 1:maxSteps sIdx = stateIndex(pos, gridSize); % epsilon-greedy 策略 if rand() < epsilon action = randi(numActions); else [~, action] = max(Q(sIdx, :)); end newPos = pos + dMov(action, :); % 边界检查:出界视为碰撞,终止回合 if any(newPos < 1) || any(newPos > gridSize) r = -100; Q(sIdx, action) = Q(sIdx, action) + alpha * (r - Q(sIdx, action)); totalReward = totalReward + r; break; end t = t + 1; dynObs = getDynObs(t, dynParams, gridSize); % 碰撞检查:静态障碍物或动态障碍物 if isObstacle(newPos, staticObs, dynObs) r = -100; Q(sIdx, action) = Q(sIdx, action) + alpha * (r - Q(sIdx, action)); totalReward = totalReward + r; break; end % 到达目标 if isequal(newPos, goalPos) r = 100; done = true; else distOld = norm(pos - goalPos); distNew = norm(newPos - goalPos); r = -1 + 0.5 * (distOld - distNew); done = false; end newIdx = stateIndex(newPos, gridSize); Q(sIdx, action) = Q(sIdx, action) + alpha * (r + gamma * max(Q(newIdx, :)) - Q(sIdx, action)); pos = newPos; totalReward = totalReward + r; if done break; end end epsilon = max(minEpsilon, epsilon * epsilonDecay); episodeRewards(ep) = totalReward; end

这里有一个容易被忽略的细节:动态障碍物更新必须放在移动之后,也就是无人机走一步,障碍物也走一步,两者同步推进时间。这样才能模拟真正“同时运动”的动态环境,而不是无人机单方面躲避静态目标。

撞到障碍物之后,我选择直接终止回合。也有人选择把无人机留在原地继续走,但实测下来那样Q表容易被绕晕,因为无人机可能卡在障碍物区域边缘反复摩擦。直接结束回合,让智能体明确知道“危险动作不能选”,收敛更快。

3.5 障碍物碰撞判断函数

function hit = isObstacle(pos, staticObs, dynObs) hit = ~isempty(find(ismember(staticObs, pos, 'rows'), 1)) || ... ~isempty(find(ismember(dynObs, pos, 'rows'), 1)); end

3.6 路径提取与三维可视化

训练完用Q表贪心提取路径,然后画三维图。

%% 用训练好的Q表提取路径 pos = startPos; path = pos; t = 0; while ~isequal(pos, goalPos) && size(path, 1) < maxSteps sIdx = stateIndex(pos, gridSize); [~, action] = max(Q(sIdx, :)); newPos = pos + dMov(action, :); if any(newPos < 1) || any(newPos > gridSize) break; end t = t + 1; dynObs = getDynObs(t, dynParams, gridSize); if isObstacle(newPos, staticObs, dynObs) break; end pos = newPos; path(end+1, :) = pos; end %% 三维可视化 figure; plot3(startPos(1), startPos(2), startPos(3), 'go', 'MarkerSize', 10, 'LineWidth', 2); hold on; plot3(goalPos(1), goalPos(2), goalPos(3), 'r*', 'MarkerSize', 14, 'LineWidth', 2); plot3(staticObs(:,1), staticObs(:,2), staticObs(:,3), 'ks', 'MarkerSize', 8, 'MarkerFaceColor', 'k'); plot3(path(:,1), path(:,2), path(:,3), 'b-o', 'LineWidth', 1.5, 'MarkerSize', 4); grid on; xlabel('X'); ylabel('Y'); zlabel('Z'); title('Q-Learning三维动态避障路径'); legend('起点', '终点', '静态障碍物', '规划路径', 'Location', 'best');

这段代码跑通之后,你会看到无人机绕开静态障碍物,同时在动态障碍物还没“飘”过来的时候穿过去。这就是Q-Learning把动态规律学进Q表的结果。


4. 仿真实验与结果分析

4.1 实验参数配置

我用下面这组参数做了一次完整测试:

参数项设定值
栅格尺寸12×12×12
静态障碍物数量20
动态障碍物数量3
起始点[1 1 1]
目标点[12 12 12]
学习率α0.2
折扣因子γ0.95
探索率ε1.0 → 0.05
训练回合数500
Q表规模1728 × 6 = 10368

4.2 训练收敛表现

从每回合奖励曲线看,前50回合奖励普遍在-300到-100之间,因为无人机经常撞上障碍物或者绕远路。100回合之后开始明显改善,300回合左右基本稳定,最终每回合平均奖励稳定在40到80之间。折算成路径长度,收敛后的路径大约70到90步,在12×12×12的栅格里,这个路径长度已经算是比较理想了。

一个比较有意思的现象是,如果动态障碍物被移除,只保留静态障碍物,收敛后的路径长度可以压缩到55步左右。动态障碍物带来的额外绕行距离,恰好说明它确实在影响规划结果,而不是一个摆设。

4.3 动态障碍物避让效果

我重点观察了无人机和三个动态障碍物相位的配合。动态障碍物在x-y平面做周期运动,无人机学到的最优策略不是远远绕开它们的整个活动范围,而是从它们的间隙中穿过去。具体来说,当障碍物摆动到一侧时,无人机从另一侧快速通过。这种时序配合能力,是静态路径规划算法很难直接做到的。

作为对比,我用A配合每步重规划做了一个对照组。A在静态地图上先算出一条最短路径,然后每步检查路径前方是否被动态障碍物堵住,堵住就重算。在12×12×12的小栅格里,A*每步重规划大约耗时毫秒级,也能完成避障,但路径明显更曲折,而且随着障碍物移动速度加快或者数量增多,重规划频率也会大幅上升,实时性会越来越差。Q-Learning则完全没有这个压力,训练好之后每次决策只是一个查表操作,耗时可以忽略不计。

4.4 算法的局限性和扩展方向

小栅格里查表Q-Learning表现不错,但有几个瓶颈是绕不过去的。

栅格分辨率一旦提高,Q表就会爆炸。50×50×50产生12.5万个状态,每个状态6个动作,Q表就有75万个浮点数,用MATLAB还能撑住,但训练收敛时间会明显拉长。再往上走,比如100×100×100,表的大小就到了600万级别,就不太现实了。这时候需要往DQN、Double DQN这类用神经网络逼近Q值的方向走,用连续状态输入替代查表。

动态障碍物如果完全是随机运动,没有固定周期和轨迹,Q-Learning会退化成学习“平均风险区域”,策略偏保守但不会特别聪明。我的建议是在主策略之上再加一个反应式避障层,Q表负责全局路径走向,势场法或者动态窗口法负责应对实时突发风险。


5. 常见问题与调参经验实录

5.1 问题速查表

实战中踩过不少坑,整理成一张速查表,遇到问题直接对着查:

症状可能原因解决办法
训练不收敛,奖励曲线剧烈震荡α过大、奖励值跨度过大把α降到0.1~0.2,奖励统一到±100量级
无人机总是撞静态障碍物ε衰减太快、碰撞惩罚不够调低ε衰减率,检查碰撞后是否终止回合并重置位置
到达不了目标点奖励稀疏、回合数不够加距离引导项,增大γ,提高episode上限
路径绕大圈,不走最短路线距离引导项权重过高把0.5系数降到0.1~0.3,或去掉距离引导只保留时间惩罚
动态障碍物碰撞率始终很高状态里没有时间相位信息、障碍物周期太快让动态障碍物按固定周期运动,每个回合重置相位
Q表索引越界或sub2ind报错坐标越界、gridSize传参不一致在边界检查前不要再调用stateIndex,检查所有函数签名

5.2 我的调参顺序心得

很多初学者一上来就把α、γ、ε、奖励权重全调一遍,结果完全不知道是谁导致的行为异常。我自己总结了一个可靠的调参顺序:先固定框架跑通,再逐个变量动。

第一步,保证奖励函数的“信号”是对的。先不加距离引导项,只保留终点+100、碰撞-100、每步-1,跑100个回合,确认无人机能偶尔到终点。如果连终点都到不了,说明环境建模或者状态索引有bug,这种时候调参是浪费时间。

第二步,调探索率衰减。这是影响收敛速度最明显的参数。ε衰减过快,策略会过早定型在某个糟糕的路线上;衰减过慢,后期还是到处乱飞。0.99到0.995之间的衰减系数通常比较平衡。

第三步,再考虑是否加距离引导项。如果无人机能到终点但路径明显不是最优,再加距离引导。加了之后路径长度会明显下降,但要随时检查路径是否变得过于激进。

5.3 几个容易踩的坑

动态障碍物的时间重置是最大的坑。如果t不随episode重置,每个回合开始障碍物相位都不一样,Q-Learning学到的就是“平均风险”而不是“时序避让”,动态避障效果会大打折扣。我调试的时候曾经把这个bug忽略了很久,后来发现只要t重置,路径立刻变得聪明起来。

另一个坑是碰撞后处理方式。如果碰撞后不终止回合,无人机有可能会在障碍物栅格附近来回弹跳,Q表学到一堆混乱的Q值。我在前面代码里采用的做法是碰撞即终止,这样Q表能明确记录“这个状态选择这个动作的后果是灾难性的”,训练效率高很多。

第三个坑和栅格粒度有关。12×12×12的栅格里,一个动态障碍物在相邻两格之间来回移动,看起来很简单。但同样的轨迹放到30×30×30的栅格里,障碍物一帧只移动0.33格,取整后常常原地不动,动态感知就弱化了。这时候需要把轨迹幅度调大,或者把栅格分辨率调低。

5.4 代码运行时的常见报错

MATLAB里最常见的报错就是sub2ind维度不匹配。原因是position可能是行向量传进去,但gridSize方向搞反了。统一用[x, y, z]的格式,gridSize定义为[row, col, layer],就不会出现这种问题。

还有一个隐藏较深的问题:ismember处理矩阵和行向量时,如果不指定'rows'参数,会按列比较,导致障碍物碰撞检测完全失效。我在辅助函数里已经写了'rows',但如果你自己扩展代码时漏掉这个参数,就会发现无人机“穿墙”如入无人之境,这个问题在调试时非常有迷惑性。


最后分享一点实际体会。Q-Learning这套东西,代码实现其实不算困难,真正决定成败的是环境建模和奖励函数设计。三维动态避障尤其如此,状态怎么离散化、动态障碍物相位怎么处理、碰撞要不要终止回合,每个细节都能让实验结果产生质变。我的建议是先用12×12×12这样的小栅格把整个流程跑通,确认路径可视化、收敛曲线都正常之后,再逐步提高栅格分辨率、增加动态障碍物数量。把基础框架吃透之后,往后换DQN也好、加局部反应式避障也好,都是在这个骨架上加血肉,不会推倒重来。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询