一、总体思路
我想让无人机在三维栅格里自己学会怎么飞。环境是10×10×10的网格,起点(2,2,2),终点(9,9,8),中间摆了些障碍物。用的算法是离线Q-learning,核心想法很简单:无人机每次走一步,环境给个奖惩,久而久之它就摸清哪条路最划算。
二、环境建模
状态就是坐标(x,y,z),地图大小10×10×10。动作设计得比较灵活:除了悬停,有53种移动方式,包括轴向、斜向,还能一步跨两格。总共54个动作。这样一来,无人机既能小步腾挪,也能大步流星。
动作集A = {(Δx,Δy,Δz) | Δx,Δy,Δz ∈ {-2,-1,0,1,2}, 不全为0} ∪ {(0,0,0)}
约束条件也很直白:别飞出地图,别撞上障碍物,最后得抵达终点。
三、Q-learning怎么更新
Q表是个四维数组:Q(x,y,z,a)。每次走完一步,用这个公式更新:
Q(s,a) ← Q(s,a) + α [ R + γ·max_a' Q(s',a') - Q(s,a) ]
学习率α和探索率ε都是动态衰减的,训练到后面越来越相信自己的经验。α从0.25降到0.05,ε随时间指数衰减,前期多试错,后期守成。
动作选择用ε-贪心:大部分时间选当前最优动作,偶尔随机浪一下,避免陷入局部最优。
四、奖励函数
奖励函数是整个训练的关键。我设了这么几种情况:
- 到达终点:大奖励3500,但还要扣掉走的路程和步数,逼着它找捷径。
- 碰撞或越界:直接罚1500,让无人机长记性。
- 悬停:小罚10,催它别发呆。
- 正常走:奖励和方向挂钩,接近终点有正反馈,远离就吃亏;同时每步距离和步数都会惩罚,促使路径又短又直。
具体表达式就是原文那个分段函数。核心意图无非是:必须到终点,必须不撞墙,路径得短,步数得少。
$$ R(s,a)= \begin{cases} 3500 - 15 \cdot d_{step} - 5 \cdot N_{step} & \text{到达终点}\ -1500 & \text{碰撞障碍物/越界}\ 30 \cdot (d_{cur}-d_{next}) -10 \cdot d_{step} -2 \cdot N_{step} -10 & \text{悬停}\ 30 \cdot (d_{cur}-d_{next}) -10 \cdot d_{step} -2 \cdot N_{step} & \text{正常飞行} \end{cases} $$
五、训练流程
大致过程如下:
- 初始化地图、起点终点、障碍物、54个动作。
- 建个四维Q表,全零。
- 设定超参数:α、γ、ε、训练轮数、最大步数。
- 开始循环:
- 每轮开始,无人机回到起点,路径清空。
- 每一步,先筛掉那些会出界或撞上障碍物的动作,再按ε-贪心选一个。
- 执行动作,得到新坐标和奖励。
- 更新Q值。
- 如果到了终点或撞了,就结束本轮。
- 训练完,用Q表跑一遍最优路径,从起点开始贪心选动作。
- 画图:奖励收敛、距离收敛、三维路径。
六、MATLAB代码片段及结果
下面是核心代码片段(完整的训练循环和绘图部分):
for episode = 1:max_episode
% 动态衰减学习率α和探索率ε
% 无人机重置到起点
for step = 1:max_step
% 1. 动作剪枝:剔除无效动作
% 2. ε-贪心选择动作
% 3. 执行动作,得到s'和R
% 4. 更新Q:Q(s,a) <- Q(s,a) + α[R + γ maxQ(s',a') - Q(s,a)]
% 5. 终止判断
% 6. 保存路径
end
% 记录本轮总奖励、距离等
end
% 最优路径
final_steps = size(path,1)-1;
final_dist = 0;
for i = 1:final_steps
final_dist = final_dist + norm(path(i+1,:)-path(i,:));
end
fprintf('\n=========================================\n');
fprintf(' 最优步数:%d 步\n', final_steps);
fprintf(' 最短距离:%.4f\n', final_dist);
fprintf(' 避障状态:无碰撞\n');
fprintf('=========================================\n');
%% 绘图
figure('Color','w','Position',[80,80,1200,380]);
subplot(1,3,1);
plot(1:max_episode, reward_curve, 'b-','LineWidth',1.6);
xlabel('训练轮数'); ylabel('总奖励'); title('奖励收敛曲线'); grid on;
subplot(1,3,2);
plot(1:max_episode, episode_path_distance, 'r-','LineWidth',1.6);
xlabel('训练轮数'); ylabel('路径总距离'); title('距离收敛曲线'); grid on;
subplot(1,3,3);
hold on; grid on; axis equal; view(3);
xlabel('X'); ylabel('Y'); zlabel('Z');
title(sprintf('最优路径 | 步数:%d | 距离:%.2f | 无碰撞',final_steps,final_dist));
xlim([1 x_max]); ylim([1 y_max]); zlim([1 z_max]);
plot3(start_state(1),start_state(2),start_state(3),'rs','MarkerSize',11,'LineWidth',2);
plot3(end_state(1),end_state(2),end_state(3),'gd','MarkerSize',11,'LineWidth',2);
scatter3(obstacle(:,1),obstacle(:,2),obstacle(:,3),100,'k','filled');
plot3(path(:,1),path(:,2),path(:,3),'m-','LineWidth',2.8,'MarkerSize',5);
legend('起点','终点','障碍物','最优路径','Location','best');


