尧图精选

UE4+AirSim无人机强化学习实战:环境搭建、PPO训练与奖励设计

🕒 发布时间:2026/10/2 14:31:06 📁 来源:尧图网络
简介基于虚幻引擎4与AirSim仿真环境的无人机自主导航与目标跟踪强化学习项目面向计算机、人工智能、物联网等专业学生及企业开发者既可用于课程设计、大作业、毕业设计或初期项目演示也适合作为强化学习入门实战的参考素材。项目代码已经过实际测试并成功运行内部包含完整的训练流程、仿真环境交互逻辑、结果可视化模块与必要的运行说明。资源压缩包为ZIP格式共646个文件大小约148.98MB文件构成以191个Python脚本、167个C头文件、16个C源文件为核心并配有116张运行截图、65个Markdown笔记、11个Shell脚本以及PDF文档、批处理命令、工程配置和版本管理辅助文件目录层次分明便于按模块查找。当前已有190人学习下载借助该项目可系统理解AirSim环境搭建、强化学习状态与奖励设计、无人机自主导航与目标跟踪的实现流程既能直接运行体验也方便在此基础上二次开发与功能扩展。1. 把 UE4AirSim 的无人机强化学习项目拆开这不是炼丹是搭飞行训练闭环一个听起来很“飞”的毕业设计题目让无人机在 UE4 引擎渲染的三维场景里靠强化学习自己学会自主导航并在目标移动时完成跟踪。如果你也选了这个方向大概率会遇到三类东西AirSim 的环境配置、强化学习算法的训练脚本、以及一个怎么调都不收敛的奖励函数。这套课设/大作业/毕设资源正好把这三件事拼成了完整闭环——UE4 提供场景和物理、AirSim 提供飞行器仿真、强化学习算法负责决策。拿到手不是只能看曲线跑 demo而是能照着它的状态定义、动作接口和奖励构造把“自主导航目标跟踪”这个组合任务从零复现出来。适合正在做毕设又不想从空环境开始搭的人也适合已经跑通基本仿真、想快速照一套成熟框架改自己任务的研究生。下面按我自己的拆解顺序来写从环境搭起到算法落地再到那些训练时最折磨人的坑。2. AirSim 环境搭建与 UE4 联动先把训练场地立起来2.1 环境基线AirSim 和 UE4 的版本要锁对AirSim 本质上是 UE4 的一个插件不是独立仿真器。这意味着 UE4 版本和 AirSim 版本是强耦合的换错一个就会遇到编译不过、插件加载失败、连上客户端后拿不到图像数据等问题。常见做法是先确定你下载到的这份资源里 AirSim 代码对应的 UE4 版本再按它的 release 说明去装配套引擎。不同版本之间的 API 有差异比如simGetVehiclePose这样的接口在较新版本里可能被合并或改名遇到报错时第一反应应该是查版本差异而不是怀疑代码写错。我自己一般会额外确认三件事操作系统是 Windows 还是 Linux、是否打算接 PX4 真实固件、以及是否要用 WSL2。如果你要做的是纯仿真训练直接走 AirSim 自带的 SimpleFlight 物理模型就行如果想更逼近真机官方也支持通过 WSL2 跑 PX4 软件在环仿真这也是最近 PX4 社区里比较主流的做法。下面这张表是我在这个资源里核对完的基线组合供你对照组件推荐选择备注引擎UE4 4.244.26 区间项目源码里标注的 UE 版本范围AirSim与新版本 UE 对应的版本核对 release 说明先锁版本再装Python 客户端与 AirSim 版本一致的airsim包用pip install airsim安装时注意版本号固件模式SimpleFlight默认需要 PX4 时另配 WSL2 环境环境变量也要提前配合好。UE4 编辑器启动后需要用Play按钮进入场景AirSim 才会创建载具。这里有个新手容易卡住的点很多人直接在编辑界面里看半天发现脚本连不上其实是因为场景没运行起来。启动顺序一定是先开 UE4 场景再跑 Python 训练脚本。2.2 载具与传感器配置settings.json 里最关键的三组字段AirSim 的配置都集中在settings.json里文件位于Documents\AirSim目录下。资源包里大概率带了一份现成的但我建议你打开看几个关键字段别拿到就无脑用。第一是SimMode它决定仿真器里创建的是无人机还是汽车这个资源的目标是无人机所以应该写Multirotor。顺带一提AirSim 也支持 Car 模式但如果你的毕设是飞行器写错模式会导致客户端接口都调用不了。第二是传感器组。导航和跟踪任务分别依赖不同的传感器导航要 GPS 和 IMU跟踪要前置摄像头。摄像头可以做成第一视角图像输入也可以只作为录制评估画面的工具。下面这段settings.json是我根据这个项目改出来的一份最小配置重点是把 IMU 的采样率、GPS 的开关和一张用于录制画面的前置摄像头配好{ SettingsVersion: 1.2, SimMode: Multirotor, ClockSpeed: 1, Vehicles: { Drone1: { VehicleType: SimpleFlight, AutoCreate: true, RC: { RemoteControlID: 0 }, Sensors: { Imu: { SensorType: 2, Enabled: true, UpdateRate: 200 }, Gps: { SensorType: 3, Enabled: true, UpdateRate: 50 } }, Cameras: { front_center: { CaptureSettings: [ { Width: 640, Height: 480, ImageType: 0 } ] } } } } }这份配置里有几个参数值得说清楚。ClockSpeed控制仿真时钟相对真实时间的倍率设为 1 是真实速度如果训练时想加速采样可以调大到 2 或更高但要注意物理计算精度可能会下降。UpdateRate是传感器采样频率单位是赫兹无人机控制领域常见 IMU 采样率在 200Hz 左右这个值不要拍脑袋乱设采样率不足会导致姿态估计抖采样率太高又可能拖慢仿真。Camera的ImageType为 0 表示场景视图用于录制验证视频足够如果要做图像输入训练通常还需要加载深度图那就要加ImageType为 2 的深度相机条目。2.3 Python 客户端与 UE4 渲染进程的连接环境跑起来后训练脚本通过 AirSim 的 Python API 和仿真器通信。通信底层用的是 RPC所以脚本和 UE4 编辑器不要求在同一台机器上只要 IP 和端口能通就行。本地训练时直接localhost连接。这里有一个我强烈建议你踩过一遍的习惯在跑完整训练前先写一个最小连接脚本确认环境通别一上来就加载神经网络。import airsim client airsim.MultirotorClient() client.confirmConnection() print(client.getMultirotorState()) client.enableApiControl(True) client.armDisarm(True) print(drone ready)这段代码的作用有三个建立连接、确认飞行器状态可读、把控制权限从手动切到 API。confirmConnection会阻塞重试所以它返回成功后基本可以断定网络层没问题。enableApiControl(True)是把飞行器控制权交给脚本armDisarm(True)是解锁电机缺一步后面起飞指令都不会生效。我在检查资源时发现很多报错都出在armDisarm之前导致takeoff指令发出去但是无人机没反应这不是代码问题是飞控状态机不允许。环境层确认完毕之后才算真正进入了算法部分。3. 强化学习算法选型PPO 为核心状态动作空间写清楚3.1 为什么选 PPO连续控制任务里的性价比方案无人机速度和姿态控制都是连续动作这类问题用传统 DQN 并不合适因为 DQN 天然面向离散动作空间。可选的连续控制算法有 DDPG、SAC、PPO 等其中 PPO 在这类项目里出现频率最高。原因很实际它对超参数没有那么敏感而且训练稳定性好更适合课设和毕设这种时间有限的场景。SAC 虽然采样效率更高但调温系数、双 Q 网络的复杂度对新手来说并不友好一旦初始化没设好效果反而不如 PPO。用一句话概括 PPO 的核心它通过裁剪的目标函数限制策略更新的步长避免一次更新太大把策略学坏了。这个特性在实际训练中表现为“曲线虽然波动但不会直接崩掉”。这个资源里提供的算法主干就是 PPO目标跟踪部分是叠加在同一个策略网络上的输出头设计不是另起一套算法这点在看代码时要注意别误以为它用了分层强化学习结构。如果你之前跑过离散动作的强化学习这里最需要转变的认知是动作从“选一个”变成“输出一个多维向量”。比如导航任务的动作维度可能是三到四维包括前后速度、左右速度、升降速度甚至偏航角速度。动作空间定义不好后面所有训练都是白费功夫。3.2 状态空间与动作空间不归一化就等着发散去逸强化学习里状态定义决定了算法的信息天花板。这个项目里导航和跟踪共用一套状态特征我拆出来把它归类成三块无人机自身状态、目标状态、相对状态。自身状态包括位置、速度、姿态角和角速度目标状态包括目标位置、目标速度相对状态是目标和无人机的差值比如相对距离、相对方位角。跟踪任务尤其依赖相对状态因为纯距离信息无法判断目标在哪个方位需要方位角的配合。动作空间方面常见的做法有两种一种是直接输出速度设定值另一种是输出期望姿态角。前者训练起来更直接因为速度响应比姿态角响应更平稳后者更像真机控制器层级结构但训练初期容易因为姿态控制不稳而出界。我翻了下这个资源使用的是速度控制方式动作维度设为四维分别是三个方向的速度分量和偏航角速度。下面是状态拼接的示意代码这种写法在资源里多处出现import numpy as np def build_observation(drone_state, target_state): drone_pos_norm drone_state[position] / 200.0 drone_vel_norm drone_state[velocity] / 10.0 attitude_norm drone_state[attitude] / np.pi target_pos_norm target_state[position] / 200.0 rel_pos (target_state[position] - drone_state[position]) / 200.0 return np.concatenate([ drone_pos_norm, drone_vel_norm, attitude_norm, target_pos_norm, rel_pos ])两个细节是这种人常踩坑的第一除以200.0和10.0这些常数是把物理量压缩到大致[-1, 1]区间如果不归一化神经网络输入尺度差异过大训练很容易出现梯度爆炸或发散这是我看过最多人翻车的地方第二相对状态rel_pos比绝对位置更有用因为智能体要学习的是“朝目标靠近”而不是记忆某个绝对坐标绝对坐标换一个起点就没意义了相对坐标天然具备泛化性。3.3 训练主循环从一次交互到一组更新PPO 的训练循环结构和普通强化学习不完全一样它属于 on-policy 方法要先采集一批数据再用这批数据更新策略然后丢弃旧数据。代码结构上通常分为采样和更新两个阶段。这个资源里的主循环大致下面这样for epoch in range(total_epochs): for step in range(buffer_capacity): action, log_prob policy.sample(obs) next_obs, reward, done, info env.step(action) buffer.push(obs, action, reward, done, log_prob) obs next_obs if done: obs env.reset() for k in range(ppo_epochs): loss policy.update(buffer.sample(minibatch_size)) buffer.clear()循环里最需要注意的参数有两个buffer_capacity和ppo_epochs。buffer_capacity是每次更新前收集的样本数PPO 对样本量要求不低我见过很多不收敛的情况就是采样步数太少策略还没看到多样数据就开始更新导致方差极大。ppo_epochs是同一批数据的复用次数一般 3 到 10 之间设太大会在旧数据上过拟合反而伤害策略性能。policy.update内部同时计算策略损失和价值损失价值网络的误差也会反过来影响策略更新方向。这里要明白一个关键点强化学习训练和监督学习不同loss下降并不直接代表策略变好它只是说明当前批次数据下的拟合程度。真正要看的是环境交互中得到的平均回报reward和回合步长episode_length。这两个指标我会在下一章重点讲。4. 自主导航与目标跟踪的奖励塑造把“飞过去”变成可优化的数字4.1 为什么奖励函数决定成败稀疏奖励的教训最简单粗暴的奖励设计是到达目标给 100碰撞给 -100其它时候给 0。这种稀疏奖励在仿真里训练极慢因为初期策略完全随机几乎不可能靠碰运气到达目标也就拿不到任何正向反馈整个训练死循环在“随机飞→撞墙→重置”里。正确的做法是把单步奖励拆解成连续可学习的信号让智能体每一步都能知道自己在变好还是变坏。这个导航任务的奖励项我拆成了四个部分距离惩罚、前进奖励、碰撞惩罚、到达奖励。距离惩罚是负的等于当前距离与上一步距离的差值靠近目标会得到正增量前进奖励鼓励无人机保持合理的速度避免原地悬停“白嫖”时间碰撞惩罚是硬约束到达奖励用于标记回合终止条件。下面是简化后的奖励计算代码def nav_reward(self, state, next_state, info): # 计算与目标的距离 d_now np.linalg.norm(next_state[pos] - next_state[target]) d_prev np.linalg.norm(state[pos] - state[target]) # 靠近目标为正奖励远离为负奖励 distance_improve d_prev - d_now # 速度分量正向激励但抑制过大速度 speed np.linalg.norm(next_state[vel]) speed_penalty 0.1 * max(0.0, speed - 3.0) # 碰撞直接扣大分 if info[collision]: return -100.0, True # 到达目标则回合完成 if d_now 1.5: return 100.0, True reward distance_improve - speed_penalty return reward, False这段代码里有几个参数直接决定训练效率。d_now 1.5是到达判定阈值设太大导致无人机在目标附近悬停也算成功设太小导致很难到达训练时间暴增speed - 3.0是速度上限约束我的习惯是先放宽后收紧初期让无人机敢飞后期再要求它飞得稳。距离改善量distance_improve单位是米直接作为奖励的主项这比用负距离本身做奖励更好因为负距离方案下无人机学到的是“离得越近越好”但梯度在整个空间比较平坦而改善量给的是每一步的学习信号收敛更快。4.2 目标跟踪任务的奖励叠加距离、视野角与速度匹配目标跟踪和自主导航在奖励结构上有本质区别。导航的终点是固定的无人机只要走到目标位置就算成功跟踪的目标是动态的无人机不仅要保持距离还要保持在目标视野范围内跟踪失败的判定往往不是“距离为 0”而是“距离超过阈值”或“目标丢失”。因此跟踪奖励必须额外考虑两个维度方位角偏差和速度匹配度。方位角偏差用来衡量无人机是否正对目标飞行。如果无人机距离很近但一直在目标侧面绕圈这时距离惩罚很小但它并没有完成正面跟踪。速度匹配度用于目标运动时的跟随效果目标速度为 2m/s无人机以 1m/s 跟随则距离会逐渐拉大必须对速度差进行惩罚。下面是跟踪奖励的叠加代码def tracking_reward(self, state, target_vel): rel_pos state[rel_pos] rel_speed state[vel] - target_vel dist np.linalg.norm(rel_pos) # 方位角惩罚 heading_diff np.arctan2(rel_pos[1], rel_pos[0]) heading_penalty 0.3 * np.abs(heading_diff) # 速度匹配惩罚 speed_diff np.linalg.norm(rel_speed) speed_penalty 0.2 * speed_diff # 距离惩罚 dist_penalty 0.5 * abs(dist - 3.0) reward -(dist_penalty heading_penalty speed_penalty) if dist 4.0 and dist 2.0 and speed_diff 1.0: reward 5.0 return reward这段代码的核心是三个惩罚项的加权求和。注意abs(dist - 3.0)意味着最优距离是 3 米距离太近或太远都会扣分这模拟了真实无人机跟拍场景中需要保持的安全和安全拍摄距离。heading_diff用的是相对位置的角度差目标在无人机的哪个方向无人机机头就应指向哪个方向。speed_diff是速度矢量差不是简单速度大小差方向不一致时速度差依然很大。最后那个if判断是稠密奖励的补充区域触达和速度匹配同时满足时给额外奖励让无人机学会稳定悬停在目标侧方。4.3 训练曲线怎么看三个关键指标和它们对应的玄学训练跑起来之后大多数人会盯着loss曲线其实这是最没信息量的图。更有用的是收益曲线episode_reward、回合步长episode_length以及价值损失value_loss。收益曲线上升说明策略确实在改进回合步长变化能看出行为模式转变比如导航任务初期步长很长是因为无人机到处乱逛后期步长变短是因为还没走几步就撞墙被重置再后来变长才是真正学会了绕障。这三个阶段对应完全不同的训练状态只看收益曲线容易误判。另外一个常见操作是把每个 episode 结束时的状态记录下来比如是否到达、是否碰撞、最终距离。这些离散信息比 reward 本身更容易定位问题。我一般会在训练时把数据写成 CSV 存下来画图方便答辩时候还能当证据。下面的代码展示怎么记录每次 episode 的指标import csv with open(train_log.csv, a) as f: writer csv.writer(f) writer.writerow([ episode, episode_reward, episode_length, reached_target, collision, final_distance ])reached_target和collision是布尔值但在统计时可以直接计算“成功率”和“碰撞率”。这是评估泛化能力最硬的两个指标。看曲线时一个常见的认知误区是“reward 高等于任务完成”实际上因为奖励函数里有避障惩罚无人机完全可以通过停在原地拿一个不高不低的 reward这种行为在曲线上看不出异常只有看成功率才会暴露。所以奖励塑造的下一步永远是统计任务层面的指标而不是只看奖励总和。5. 训练飞不起来、撞上去了、API 报错UE4AirSim 强化学习避坑记录5.1 现象无人机一开局就原地打转reward 完全不变原因状态归一化没做好尤其没有把相对位置除以一个尺度常数。我曾看到有同学直接拿原始坐标塞进策略网络UE4 场景里一单位就是米坐标从几十到几百而网络权重初始化基本在零点几这个量级输入一进网络就是极大的数梯度直接爆炸。 解决所有状态量除以场景尺度目标位置和相对位置除以 200速度除以 10姿态角除以np.pi。这个操作虽然简单但对训练收敛的影响比换任何一个算法都大。如果换了归一化之后曲线还是平的检查一下是不是actions也超出了网络输出的tanh范围速度上限要和动作 scale 对应上。5.2 现象训练到一万步时 UE4 渲染帧率暴跌训练速度慢到没法忍原因默认设置下 UE4 编辑器在 Play 时会渲染整个场景图像质量开到高后 GPU 被渲染管线占满而 AirSim 的物理和传感器更新也走同一帧循环ClockSpeed还是 1训练效率极低。 解决把 UE4 的 World Settings 里 GameMode 的渲染质量调低或者直接使用 AirSim 的 headless 模式让它不渲染画面只跑物理。另一个操作是把ClockSpeed调大到 2 或 3加快仿真时钟推进速度。注意ClockSpeed不是越大越好超过 3 后物理求解步长不变但每次同步的时间步变大无人机可能直接穿模。我的习惯是 2 以下最稳遇到训练速度瓶颈优先降分辨率而不是强行提速。5.3 现象AirSim Python API 调用报错比如simGetVehiclePose不存在原因AirSim 版本和 Python 客户端版本不匹配。simGetVehiclePose是较早版本里的接口新版本改成了getMultirotorState或其它的状态获取方式。UE4 环境加载的插件和你pip install的airsim包不是同一版本时最容易出现这种问题。 解决先确认 UE4 插件版本对应的 Python API 版本。把资源代码里所有状态获取调用统一封装成一个兼容层只保留client.getMultirotorState()这种大版本之间稳定存在的接口再从这里解析位置、速度、姿态。我自己后来凡是接到新环境都会先跑一遍最小连接脚本把所有要用的 API 全调一遍确认无报错再继续避免训练到一半才发现是接口问题。5.4 现象碰撞检测频繁误报无人机明明离墙还有一米却判定碰撞原因AirSim 的碰撞检测以 UE4 物理碰撞体为准UE4 场景里有些物体默认生成的碰撞体贴合不够精细比如树木的碰撞体覆盖范围明显比视觉模型大导致无人机离树冠还有距离就触发了碰撞。 解决在 UE4 编辑器里检查场景物体的碰撞体形状给障碍物手动替换为与视觉轮廓接近的凸碰撞体。如果不想改场景就换一个判定策略不依赖collision标志改用高度和位置阈值来判断是否碰撞。下面这段代码是常见做法info client.simGetCollisionInfo() if info.has_collided: self.collision_count 1 done True # 或使用备用判定 if drone_pos[z] ground_z_threshold: done True碰撞误报的危害在于它会给策略错误的负反馈让无人机学会“远离一切障碍物”包括本来可以通过的窄缝。项目里如果用的是碰撞信息作为终止条件我建议同时采集无人机位置在代码里加入碰撞位置的打印日志训练后回看哪些位置频繁误报去 UE4 里针对性修复碰撞体。5.5 现象同一份奖励代码别人能跑通你跑就发散原因这是强化学习训练里最常见的伪“玄学”现象根子在于随机种子、初始化权重和 PPO 更新步数的微小差异被累积放大。奖励函数没问题但超参数敏感度不同特别是ppo_epochs和minibatch_size没有随环境复杂度调整。 解决把随机种子固定下来在训练开始处设置np.random.seed、torch.manual_seed并记录每次实验的配置。同时把ppo_epochs从 10 降到 4 到 6 试试如果之前是 3 就升到 5。模型初始值对策略影响很大建议用同一个固定种子做基准实验改奖励或动作空间时只动一个变量否则你根本没法定论是奖励函数问题还是噪声影响。6. 从重建到答辩评估脚本、录制飞行轨迹和最该养成的验证习惯资源拿到手后最忌讳直接跑完训练就觉得结束了。毕设答辩时评委问的不只是“你用了 PPO”更会问“你怎么证明它学会了”。要回答这个问题最好给出一套可重复的评估脚本固定多个随机种子跑足几十个回合统计成功率、平均到达时间、碰撞率以及目标跟踪任务里的平均距离误差。下面是一个很基础的评估代码框架for seed in [0, 1, 2]: set_seed(seed) success 0 collision 0 for episode in range(50): obs env.reset() done False while not done: action policy.act(obs, deterministicTrue) obs, reward, done, info env.step(action) if info[reach_target]: success 1 if info[collision]: collision 1 print(fseed{seed}, success_rate{success/50}, collision_rate{collision/50})这个脚本里deterministicTrue很关键评估时不能加噪声否则策略的稳定性会被随机性掩盖。success_rate和collision_rate是最终要进论文的两个硬指标比 reward 数值有说服力得多。如果目标跟踪任务就把指标换成平均跟踪距离和丢失次数这也需要提前定义好“丢失”的距离阈值比如超过 8 米算丢失。录制飞行轨迹也是我强烈建议你在答辩前完成的工作。AirSim 的 Python API 可以逐帧读取相机图像保存成本地图片再用 OpenCV 合成为视频整个过程可以完全自动化。录制时需要注意推理阶段要关掉奖励和训练相关的日志画面上只保留无人机视角加目标框这样的视频展示效果远好于直接放终端日志。如果时间紧张至少也要保存一份airsim_recording的传感器数据里面有位置、速度、姿态的轨迹能画成三维轨迹图放进论文。最后说一个我自己的习惯。从那以后我每次拿到新的 AirSim 环境都会强制走一遍“最小连接脚本 状态打印 单步控制测试”这三件事加起来不到十分钟但能把版本不匹配、接口过期、控制权限没打开这些最底层的问题一次性排查干净。很多同学卡在训练跑不起来一查全是这类低级环境问题却以为是算法问题白白调了一周超参数。如果你也打算用这份資源起步建议先别急着改奖励把环境跑通了再说。希望帮到你。本文还有配套的精品资源点击获取
上一篇/下一篇内容由系统自动关联 返回资讯列表 →