简介:本资源是一套面向高校本科生毕业设计、课程设计与无人机仿真学习的完整UE4+Airsim无人机飞行模拟仿真平台源码项目,聚焦三维环境建模、飞控逻辑集成与传感器仿真等核心环节,有效解决学生在无人系统开发中缺乏可运行、可调试、可扩展仿真基座的实践痛点。压缩包共500个文件,含228个UE4资产(uasset)、155个C++头文件(hpp)与51个实现文件(cpp),覆盖飞行控制API(如MultirotorApiBase、SimModeBase)、仿真接口(WorldSimApi、PawnSimApi)、通信模块(RpcLibClient/Server)及硬件抽象层(DirectInputJoyStick、PIPCamera)等关键组件,整体大小为112.54MB。已有1140人下载学习,项目经助教审定、本地编译验证通过,评审分达98分,代码结构清晰、注释规范,配套bat脚本(update_mavlibkcom.bat)便于环境快速配置,适合作为毕业设计原型或进阶仿真实验的可靠起点。 这两年无人机方向的毕业设计,越来越多的同学把目光投向“仿真平台”而不是直接做真机——原因其实很现实:预算有限、场地受限、试错成本高,真机炸一次就是上千块。UE4加AirSim这套组合,算是目前开源方案里最顺手的搭配之一。AirSim是微软开源的高保真无人机和自动驾驶仿真器,底层跑在UE4引擎上,本身就能模拟飞行动力学、视觉传感器、激光雷达、GPS/IMU等一整套机载设备,并且对外提供Python、C++还有ROS2接口。你在仿真环境里完成的轨迹规划、目标识别、视觉定位这些算法,后面要迁移到真机上也有比较平滑的路径。这篇文章会把我从环境搭建、传感器配置到底层API调用的全流程经验整理出来,重点讲代码结构、参数设置和踩过的坑,适合正在做无人机仿真、视觉感知或者路径规划类毕设的同学参考。
1. 项目背景与技术选型思路
1.1 为什么是UE4加AirSim
先说结论:无人机仿真平台不是只有AirSim一个选择,Gazebo+PX4、Webots、甚至自己用Unity写一套都有,但AirSim在“视觉保真度”和“接口成熟度”上的优势非常明显。
AirSim本身是微软研究院开源的,它在设计之初就瞄准了“逼真的视觉物理环境”。意思就是,它不只是一个简单的飞行物理模拟器,而是把无人机运行在一个完整的虚幻引擎场景里。这个场景有光照、有阴影、有材质反射,有街道、树木、建筑物,甚至还有天气系统。你要做视觉感知方向的毕设,比如无人机目标检测、语义分割、视觉避障,这就非常关键——在Gazebo那种偏工程风格的场景里,你用传统视觉算法都不会有那种“真实感”的挑战,但用AirSim得到的数据就非常接近真实传感器。
从另一个维度看,AirSim的物理飞行动力学模型也相当细致。它不是简简单单给个加速度让飞机飞,而是基于旋翼空气动力学模型去估算转速、力矩、阻力,再通过Unreal Engine的物理引擎去做碰撞检测和响应。虽然比不了真机飞控那么精确,但对毕业设计阶段做控制算法验证、轨迹生成测试,精度已经够用了。
1.2 方案替代品对比
做个横向对比,你可能就明白为什么这套方案值得选。
| 方案 | 视觉保真度 | 物理真实度 | 传感器丰富度 | 上手难度 | 接口丰富度 | 适合方向 |
|---|---|---|---|---|---|---|
| AirSim + UE4 | 高 | 中高 | 高 | 中 | Python/C++/ROS2 | 视觉感知、路径规划、控制 |
| Gazebo + PX4 | 低 | 高 | 中 | 中高 | ROS生态好 | 飞控层面开发 |
| Webots | 低 | 中 | 中低 | 低 | Python/ROS | 入门教学 |
| 自研Unity仿真 | 中 | 低 | 依赖自研 | 高 | 自研 | 有特定定制需求 |
我周围有同学选Gazebo做室内物流无人机的,也有选AirSim做视觉导航的。如果你的毕设重点放在“无人机看到了什么、如何理解环境、如何基于理解做出决策”这个链条上,AirSim基本是天花板级别的开源选择。如果只是想做纯飞控层面的姿态控制、通讯协议,那Gazebo配合PX4 SITL可能更对口。我自己做仿真选型时,最看重的是图像数据能不能方便地输出、场景能不能自由搭建,因为后续算法验证要反复用,AirSim这一点做得比Gazebo好太多。
1.3 毕设选题评估:这个平台能做出什么成果
很多同学担心“只是部署了源码,会不会显得工作量不够”。实际上,基于这个平台做出来的毕设可以很有深度,举几个方向:
- 基于深度图像的无人机避障算法,把AirSim当成数据生成器,用仿真数据训练+真机场景微调。
- 语义分割辅助的无人机自主起降场地识别,利用AirSim的分割相机直接输出场景中物体实例ID。
- 基于纯视觉的无人机定位与建图(Visual SLAM),AirSim可以方便地同步高频相机数据和IMU数据。
- 多无人机协同编队,AirSim本身支持多无人机实例,可以在一个场景里同时控制多台。
所以从就业和深造的角度看,这套平台也不是“摆设”项目,它能直接支撑你后续的研究方向。接下来我把整个实现过程拆开讲。
2. 开发环境搭建全流程
2.1 版本选型与工具链准备
AirSim和UE4的版本匹配问题是新手入坑的第一道坎,也是大多数人卡最久的地方。AirSim官方文档虽然列出了几个版本,但实际构建时往往因为UE4版本过新或过旧导致编译不过。
我实测比较稳的组合是:
- Windows 10/11 64位系统
- Visual Studio 2019(16.11以上,注意安装“使用C++的游戏开发”工作负载)
- .NET Framework 4.8 SDK和运行时
- UE4.25或UE4.26源码版
- AirSim官方master分支(或v1.5.0 release)
这里强调一下:AirSim需要的是Unreal Engine的源码版,不是Epic Launcher里下载的标准版。因为AirSim要作为UE4插件运行,需要访问引擎内部模块的头文件。
UE4源码版获取方式和常规下载不太一样。你得先在Epic官网注册账号,关联GitHub账号,然后从Epic的GitHub仓库获取私有仓库访问权限。这个权限一般审核很快,几分钟到几小时不等。拿到权限后clone分支记得选4.25,例如:
git clone -b 4.25 https://github.com/EpicGames/UnrealEngine.git UE4_25这一步会下载十几个GB的代码,需要耐心。下载完成后,在UE4_25目录下运行Setup.bat,这会安装依赖,然后再运行GenerateProjectFiles.bat生成Visual Studio工程,最后在VS里编译整个UE4引擎。全程编译时间取决于机器性能,好一点的机器大概一两个小时,内存最好32GB起步,硬盘建议SSD。
2.2 AirSim源代码构建流程
UE4引擎跑起来之后,接下来处理AirSim本身。
git clone https://github.com/microsoft/AirSim.git cd AirSim .\build.cmdbuild.cmd脚本会自动完成AirSim插件、Python模块和MavLinkCom的构建。构建完成时会生成AirSim\Unreal\Plugins\AirSim这个插件目录,这是后续要用的核心。
如果你只是想快速把AirSim用起来,不用每次改AirSim源码,那么构建完成之后就可以跳过UE4工程编译,直接在UE4里新建一个空项目,把AirSim插件复制到项目的Plugins目录下即可。记得同时把AirSim\Unreal\Plugins\AirSim\Content里的内容复制到项目的Content目录里,否则场景里缺少蓝图资源,会出现无人机模型加载不出来的情况。
2.3 构建过程常见坑与提速技巧
编译过程中最容易遇到的问题主要有这几个:
- 编译时提示缺少XXX.h,大概率是UE4源码版本与AirSim不匹配,检查你clone的UE4分支。
- 构建到一半卡住不动,一般是Shader编译阶段,这是CPU密集型任务,不用管它,耐心等待即可。
- 如果首次构建时间过长,可以尝试关闭杀毒软件,因为Windows Defender实时扫描对大量小文件读取的性能影响非常大。
将AirSim作为插件嵌入你自己的项目时,有一个操作细节很容易出错:创建UE4 C++项目时,如果你没有选择“带有初学者内容包”这个选项,场景里是没有默认地板和天空光照的,AirSim无人机加载出来飞不起来,因为没地面碰撞。
我自己习惯先创建一个带初学者内容包的空模板项目,然后把AirSim插件复制进去,再在项目设置里把默认地图设为AirSim提供的AirSimGameMode。这样启动时就能直接进入飞行场景,省去手动配置的麻烦。
3. 平台整体架构与核心模块拆解
3.1 四层架构设计
AirSim的代码结构不是一锅粥,它采用了清晰的模块化分层。理解这套分层,能帮你后续快速定位问题。
从底往上分为四层:
- 物理层(Physics):基于UE4的PhysX物理引擎,处理碰撞响应、刚体运动。AirSim在此基础上实现了旋翼动力学模型,计算推力与力矩。
- 传感器层(Sensors):包括相机、激光雷达、IMU、气压计、GPS、磁力计,用独立线程采集数据。
- 车辆层(Vehicle):定义飞行器或者车辆的属性和控制接口,多旋翼由多旋翼飞行控制器模型管理。
- 管理层(World):处理UE4世界中的放置、状态同步、天气系统等。
这四层之间通过AirLib库内部的接口解耦,AirLib是独立于UE4的C++库,即使不跑UE4,也可以单独在命令行环境里仿真飞行动力学。设计这套架构的初衷,是让物理与渲染完全解耦,方便单元测试,也方便把仿真核心嵌入到不同渲染引擎。
3.2 飞行动力学模块原理
AirSim的飞行动力学模型支持两种模式:简单飞行(SimpleFlight)和PX4真机固件模式。
SimpleFlight是默认模式,它并不要求一个真实的PX4固件,而是在AirSim内部用几何方法计算升力和力矩。对每一组螺旋桨转速指令,模型会估算升力系数、阻力系数与环境空气密度,最终计算出合力。这个计算频率默认是1000Hz,也就是说每一毫秒更新一次飞行器力学状态,以保证姿态控制稳定。
简化的动力学方程大致是:
- 每个旋翼产生升力F = k * w²,k是升力系数,w是旋翼转速
- 总推力是所有旋翼推力之和
- 无人机加速度a = (总推力 - F_g) / m,F_g是重力
- 姿态角通过力矩方程解算,再传递给UE4物理引擎去更新场景中的刚体位置
注意,这里的参数都是可配置的。在settings.json中,你可以修改无人机质量、惯性张量、升力系数、旋翼数量,甚至把四旋翼改成六旋翼。对做控制方向毕设的同学来说,调整这些参数来观察无人机响应变化,本身就是很好的实验材料。
3.3 传感器模拟原理
传感器是AirSim的重头戏,也是毕设中大量数据的来源。
相机传感器的实现核心是UE4的SceneCaptureComponent。简单说,AirSim在场景里附加了一个独立于主摄像头的捕获组件,它能把当前视角的画面渲染到离屏缓冲区。同时,通过自定义材质替换,AirSim可以对渲染结果做后处理,输出不同的图像类型:
- 场景图像(Scene):RGB合成图像
- 深度图像(Depth):每个像素代表摄像机到物体的距离
- 分割图像(Segmentation):每个像素代表场景对象的ID
- 表面法线图像(Surface Normal):输出物体表面法线方向
- 红外图像(Infrared):模拟热成像
深度图的工作原理是基于UE4的SceneDepth,再通过像素级换算得到距离值,除以FarPlane归一化。分割图的原理更有趣,AirSim会给场景中的每个物体分配一个随机颜色,用这个颜色编码实例ID,输出时再根据索引表解码。
激光雷达传感器则是通过射线检测实现的。AirSim对每个激光发射方向打出一条LineTrace,检测与场景物体的碰撞点,碰撞距离、相对坐标经过处理后打包成点云。雷达的扫描线数、视场角、水平角分辨率、扫描频率这些参数都支持自定义,对做3D点云处理的同学非常友好。
GPS、IMU、气压计这类传感器主要是基于飞行状态量加噪声模型生成的。AirSim在默认参数里设置了高斯噪声和随机偏差,用来模拟真实传感器的不确定性。你可以在settings.json中关闭噪声、改变取样频率,来满足不同实验的需求。
3.4 数据输出通路设计
AirSim的数据输出有两种:场景内数据(通过模拟器的图像API)和场景外数据(通过RPC通信)。
RPC(Remote Procedure Call)是整个AirSim对外的核心通道。AirSim内部实现了一个基于TCP的RPC服务器,默认监听端口41451。Python客户端与C++客户端都通过这个端口发送命令,例如请求图像、设置控制指令、查询状态。这个机制的好处是,算法程序与仿真器完全解耦,你可以在一台电脑上跑仿真,在另一台电脑上跑算法,通过局域网连接。
另外,AirSim提供了“录制”功能,可以将飞行过程中采集到的图像、状态数据、时间戳打包成文件,用于后续离线训练。默认开启方式是调用Python接口:
client.startRecording() # 飞行过程... client.stopRecording()录制数据默认保存在AirSim\Unreal\Plugins\AirSim\Content\Documentation下的文件夹中,包含image_*.png和时间戳文件。这个功能比你自己逐帧保存更高效,因为在快照时API会同步保存姿态和传感器信息,省去了数据对齐的麻烦。
4. 无人机模型配置与场景搭建
4.1 使用settings.json配置无人机传感器
AirSim的所有配置都集中在项目目录下的settings.json文件中,格式为标准JSON。系统启动时,AirSim会读取这个文件来初始化车辆、传感器和场景参数。
一个最基础的多旋翼配置如下:
{ "SettingsVersion": 1.2, "SimMode": "Multirotor", "ClockSpeed": 1, "Vehicles": { "Drone1": { "VehicleType": "SimpleFlight", "DefaultVehicleState": "Armed", "Sensors": { "MyCamera": { "SensorType": 1, "Enabled": true, "CaptureSettings": [ { "ImageType": 0, "Width": 640, "Height": 480, "FOV_Degrees": 90, "AutoExposureSpeed": 1.0 } ] } } } } }注意几个关键字段:
SettingsVersion:当前AirSim的配置文件版本号,必须保留,否则AirSim可能忽略部分配置。SimMode: 可选Multirotor、Car或者ComputerVision。做无人机必须设为Multirotor。ClockSpeed:仿真时钟倍速,1表示实速,大于1会加速物理和逻辑运行。训练时可以把速度调高,比如设置为2或3,批量采集数据能省不少时间。DefaultVehicleState:设置为Armed表示启动时无人机立即进入可飞状态,否则需要手动遥控切换。
在配置无人机时,另一件容易忽略的事是“坐标定位”。默认生成的无人机在场景原点附近,但往往落在墙角或者地面上。你可以在Vehicles.Drone1节点下加上X、Y、Z偏移量来调整起飞点,结合UE4编辑器里的坐标信息把它放到跑道中央。
4.2 传感器参数详解
传感器的配置涉及很多细节,逐个讲一下:
相机传感器(SensorType=1)最常用。CaptureSettings是一个数组,关键在于区分ImageType:
| ImageType值 | 类型 | 说明 |
|---|---|---|
| 0 | Scene | 正常RGB图像 |
| 1 | DepthPlanner | 深度图,按平面视角输出 |
| 2 | DepthPerspective | 透视深度图,常用于避障 |
| 3 | Segmentation | 语义分割图 |
| 4 | SurfaceNormals | 表面法线图 |
| 5 | Infrared | 红外图像 |
分辨率与FOV设置直接决定数据尺度。如果后续算法要对RGB和深度图做像素级对齐,务必保持两种图像的分辨率完全一致。自动曝光(AutoExposureSpeed)默认值可能导致图像明暗变化大,做视觉识别时建议手动把曝光调成固定值,否则训练集里会混入太多光照变化样本。
激光雷达传感器配置如下:
"Lidar1": { "SensorType": 6, "Enabled": true, "NumberOfLasers": 16, "PointsPerScan": 2200, "HorizontalFOVStart": -120, "HorizontalFOVEnd": 120, "VerticalFOVUpper": 15, "VerticalFOVLower": -15, "RotationsPerSecond": 10, "Range": 100 }NumberOfLasers控制点云层数,PointsPerScan是每层扫描点数,这两个值决定点云密度。HorizontalFOV和VerticalFOV决定扫描视场范围。做无人机建图实验时,通常把RotationsPerSecond调高一点,避免运动造成点云畸变。
IMU和气压计的配置相对简单,主要是产生噪声的参数。GPS则提供经纬度和高度信息,默认是模拟的真实世界地理坐标系,你需要在HomeGeoPoint中设置仿真环境的原点经纬度。如果只是室内视觉实验,可以忽略GPS坐标系校准。
4.3 场景搭建与视觉保真度调试
AirSim自带的环境是Blocks,一堆颜色各异的立方体组成的世界。这个环境适合做视觉SLAM和基础导航,但要说美观和真实感,它还差得远。如果你希望视觉感知算法在接近真实的场景中验证,需要自己搭建或导入场景。
有两种常见的做法:第一种,在UE4商城购买或下载免费场景资源,导入后用UE4编辑器手工放置物体、布置光照;第二种,使用AirSim提供的RoadMap和ProceduralCity场景,快速生成大范围城市环境。
搭建场景时有两个关键因素直接影响AirSim的传感器输出:
第一是碰撞体积。UE4里一个静态网格体即使看起来是实体,如果碰撞体积配置不对,AirSim的物理引擎不会把它当作障碍物。比如一扇透明玻璃墙,如果碰撞盒是整块墙的Box,仿真时无人机遇到了会撞墙,但视觉看到的是透明玻璃,深度图和激光雷达点云也会把它识别为墙面。这类“视觉和物理不一致”问题,最容易让做视觉避障的同学抓狂。
第二是Lighting和PostProcess设置。AirSim相机输出的RT图会受场景后处理影响,如果后处理开启雾效,图像里远处物体会发白,这既可能是有意模拟雾天环境,也可能是干扰。进行算法测试前,建议把后处理里的AutoExposure、Bloom等效果关闭或调低,只保留基本光照。AirSim也提供了天气控制接口,可以在运行时设置雾、雨、云层等效果。
4.4 多无人机配置方法
如果毕设涉及多无人机编队或协同感知,AirSim的配置也支持得比较优雅。只需要在Vehicles下增加多个无人机节点即可:
"Vehicles": { "UAV1": { "VehicleType": "SimpleFlight", "X": 0, "Y": 0, "Z": 0 }, "UAV2": { "VehicleType": "SimpleFlight", "X": 5, "Y": 0, "Z": 0 }, "UAV3": { "VehicleType": "SimpleFlight", "X": 0, "Y": 5, "Z": 0 } }Python端通过airsim.MultirotorClient()创建多个客户端实例,在构造时指定车辆名称:
client1 = airsim.MultirotorClient() client2 = airsim.MultirotorClient() client1.confirmConnection() client2.confirmConnection()每个客户端可以分别控制对应名称的无人机。需要注意,多无人机同时飞行时物理资源开销会成倍增加,如果无人机数量超过4台,建议把画面质量降低,或者用多台机器分布式运行。
5. 功能实现与API调用实操
5.1 Python API快速上手
AirSim的Python API是使用频率最高的接口,安装在PythonClient目录下。确认AirSim构建成功之后,运行以下命令安装客户端库:
pip install airsim也可以手动cd AirSim/PythonClient,执行pip install .。
使用Python接口的基本流程如下:
import airsim client = airsim.MultirotorClient() client.confirmConnection() client.enableApiControl(True) client.armDisarm(True) # 起飞到2米高度 client.takeoffAsync().join() client.moveToZAsync(-2, 1).join()注意moveToZAsync中Z坐标是负数,因为UE4采用Z轴向上坐标系,负数表示高度高于原点。刚接触的人经常把Z写成正数,结果飞机直接往下钻地底。
5.2 图像数据获取与保存
获取图像数据是最核心的API调用之一,代码也很简洁:
responses = client.simGetImages([ airsim.ImageRequest("0", airsim.ImageType.Scene, False, False), airsim.ImageRequest("0", airsim.ImageType.DepthPerspective, True, True), airsim.ImageRequest("0", airsim.ImageType.Segmentation, False, False) ]) # 获取第一张图像 img_response = responses[0] if img_response.pixels_as_float: # 深度图 import numpy as np depth_img = np.array(img_response.image_data_float, dtype=np.float32) depth_img = depth_img.reshape(img_response.height, img_response.width) else: # RGB图 import numpy as np img1d = np.fromstring(img_response.image_data_uint8, dtype=np.uint8) rgb_img = img1d.reshape(img_response.height, img_response.width, 3)这里的第一个参数是相机名称,默认主相机是"0"。如果配置了多个相机,对应名称如"MyCamera"也要在这里使用。深度图返回的是原始浮点数据,单位是米,保存为PNG时记得做归一化。
我有一个实际建议:不要用cv2.imwrite直接保存深度图,因为OpenCV默认会压缩成8位图,深度信息直接丢失。要么用numpy.save保存原始矩阵,要么把深度范围映射到16位PNG再保存。
5.3 手动飞行与遥控器控制
在仿真平台上开发算法时,经常需要先手动飞一段,看场景数据结构是否正确,再切换到API自动飞行。
AirSim支持在场景里按F1键弹出控制帮助,鼠标与键盘组合控制无人机运动。如果你有USB遥控器,比如FlightMode遥控器或者PS4/Xbox手柄,也可以在UE4的输入设置里映射油门、俯仰、偏航通道。
需要注意,手动飞行模式与API控制模式是互斥的。如果要让API接管控制权,必须调用enableApiControl(True);否则即使你调用takeoffAsync,无人机会原地悬停响应遥控器指令。从API模式切回手动模式,则调用enableApiControl(False)。
操控手感方面,AirSim默认的按键速度比较激进,新手容易瞬间冲高然后撞树。建议先在settings.json里把Speed相关参数调低,跑几次熟了再加难。
5.4 C++ API扩展开发
Python接口适合快速验证和算法开发,但如果要自研UE4场景内的功能模块、自定义控制逻辑甚至写新的传感器插件,就需要使用C++ API。
AirSim的C++接口核心类是MultirotorRpcLibClient,使用方式如下:
#include "vehicles/multirotor/api/MultirotorRpcLibClient.hpp" #include <iostream> int main() { msr::airlib::MultirotorRpcLibClient client; client.confirmConnection(); client.enableApiControl(true); client.armDisarm(true); client.takeoffAsync(5)->waitOnLastTask(); auto state = client.getMultirotorState(); std::cout << "Position: " << state.getPosition().x() << ", " << state.getPosition().y() << ", " << state.getPosition().z() << std::endl; return 0; }C++ API和Python API是一一对应的,大部分函数名相同,迁移成本低。开发新传感器模块或者自定义飞行控制律时,你甚至可以跳过RPC层,直接在AirLib的Vehicles接口里继承类实现控制逻辑。这部分适合学有余力的同学深入探索,对后续去无人机公司做飞控或仿真开发是很好的敲门砖。
6. 与ROS2和外部算法对接
6.1 ROS2桥接方案
AirSim官方提供了一套ROS节点,支持ROS1和ROS2两种版本。通过ROS桥接,AirSim可以接入整个ROS生态,比如和Rviz联动显示点云、把图像发布到话题上供视觉节点订阅。
ROS2的启动流程大致是:
cd AirSim/ros2 colcon build source install/setup.bash ros2 launch airsim_ros2 airsim_node.launch.py启动后,AirSim中的数据会被发布到多个话题中,比如/airsim/camera/0/image_raw发布RGB图像,/airsim/lidar/1发布点云。同一时间,ROS2里也可以通过/airsim/vel_cmd话题给无人机发送速度指令。
这套桥接对做路径规划或者视觉算法的同学非常友好,因为你可以直接复用ROS生态里成熟的算法包,例如move_base、gmapping、OctoMap等等,而不用自己实现消息工具链。
6.2 路径规划算法验证平台
无人机路径规划是毕设的热门方向之一。在AirSim平台上做路径规划的常规流程是:
- 在场景中设定航点(Waypoint),让无人机按预设轨迹飞行,这测试的是轨迹跟踪能力。
- 在场景中随机插入障碍物,无人机通过机载传感器感知环境,实时重规划路径,这测试的是避障算法。
AirSim的视野窗口提供了世界坐标和无人机的局部坐标,可以方便地在地图坐标系与机体坐标系之间做转换。用雷达点云数据做碰撞检测时,把点云从机体坐标系通过欧拉角变换到世界坐标系,之后再做体素化、膨胀,就能生成无人机可以飞行的安全走廊。
有一个比较实用的技巧:AirSim的simGetCollisionInfo接口可以查询无人机是否发生了碰撞。你可以把它当作硬性评估指标,判断路径规划算法是否真的避开了障碍物,而不只是看轨迹折线好不好看。
6.3 数据集生成流水线
如果需要用AirSim生成大规模数据集用于训练,我建议把整个数据采集做成流水线,而不是手动操作。
具体思路是:
- 用Python脚本随机初始化无人机位置和航向。
- 控制无人机按预设路线飞行,同时周期性采集RGB图和分割图。
- 将图像数据、相机位姿、IMU数据、深度图打包存储。
- 对数据进行后处理,比如合成雾天、雨天效果,丰富数据多样性。
AirSim的天气API可以设置多云、下雨、起雾等环境参数,结合simSetTimeOfDay控制光照方向,理论上可以生成覆盖全天气、全时段的数据集。这对语义分割、目标识别类模型的泛化性帮助极大,比自己拍几百张真实照片省时省力得多。
7. 常见问题与排查记录
7.1 环境构建问题
| 问题 | 现象 | 排查方案 |
|---|---|---|
| UE4源码编译失败 | VS报错找不到某个模块 | 检查UE4分支版本是否与AirSim发布的版本匹配 |
| AirSim build.cmd卡住 | 长时间停留在“Copying plugins” | 关闭杀毒软件,检查磁盘空间 |
| 启动项目后黑屏 | UE4编辑器黑屏无场景 | 确认使用了带初学者内容包的项目模板 |
| 插件启用报错 | “Plugin 'AirSim' failed to load” | 检查Unreal版本,重新编译AirSim插件 |
我遇到最典型的问题是:从GitHub拉取AirSim master分支后,编译通过但启动UE4编辑器时报插件加载失败。后来发现是VS版本太新(VS2022),CMake在生成项目时选择了错误的平台工具集,导致与UE4.25不兼容。换成VS2019编译就好了。所以版本匹配优先度最高。
7.2 运行性能问题
仿真平台的性能直接影响数据采集效率,做视觉类实验时经常需要高分图像,但渲染负担很重。
如果运行过程中帧率过低,考虑以下优化:
- 降低分辨率到640x480或更低,先用小尺寸验证算法。
- 关闭垂直同步以及不必要的后处理效果。
- 在
settings.json中设置ClockSpeed=1时,物理计算和渲染同步,如果物理计算跟不上,可以调低物理刷新率。 - 使用独立显卡运行UE4项目。AirSim在集成显卡上基本不可用。
另外补充一点:仿真器时钟速度不等于渲染帧率。AirSim可以跑在“非实时”模式,比如设置ClockSpeed=2,此时物理仿真速度加倍,但画面可能会卡顿。如果是批量采集数据,可以接受卡顿,你会发现生成数据的速度明显加快。
7.3 数据输出问题
| 问题 | 现象 | 排查方案 |
|---|---|---|
| 深度图全黑 | 调用simGetImages返回的深度数据全为0 | 确认ImageType设为DepthPerspective而不是Scene,且pixels_as_float=True |
| 分割图都是纯色 | 所有物体输出同一种颜色 | 检查场景物体的SemanticSegmentationID是否被正确分配 |
| 相机图像上下颠倒 | 图像坐标系错误 | 图像数据按行主序排列时注意原始数组的reshape维度顺序 |
| 点云数据为空 | Lidar返回空数组 | 确认Lidar传感器Enable=true,且扫描角度范围配置正确 |
分割图颜色异常这个问题值得展开。AirSim的分割ID分配规则是:物体启用了“启用语义分割自定义”后,可以在UE4编辑器里手动指定ObjectID,否则AirSim会为每个物体自动分配一个ID。自动分配在不同启动之间可能会变化,所以模型训练前一定要先采集一份标注映射表记录下来,否则换了环境重新启动,分割ID和生产数据时的ID可能就对不上了。
7.4 API连接问题
Python客户端连不上AirSim是新手最常遇到的问题。
client = airsim.MultirotorClient() client.confirmConnection()执行这段代码时如果长时间没有响应,多半是对方AirSim项目没有启动。确认仿真器在运行且SimMode为Multirotor。如果已经启动,检查防火墙是否拦截了TCP 41451端口。还有一种情况是场景里有多台无人机时,默认连接会尝试连上第一台,如果你的无人机不叫Drone1而是其他名字,连接时也可以显式指定名称:
client = airsim.MultirotorClient(vehicle_name="UAV2")7.5 物理仿真异常
有同学反映,无人机起飞之后飘得厉害、或者直接落地反弹。这通常是因为settings.json中物理参数不合适。
几个常见调参点:
Mass:无人机重量,默认约1kg。如果场景里加载了额外的载荷模型,需要调大。MaxThrust:最大推力,默认约35N。如果觉得动力不足飞不起来,调大。DragCoefficient:阻力系数,调大可以让无人机更“稳”,但机动性会变差。Rotors:旋翼数量,默认是4。改成6旋翼时,动力结构和控制模型都会改变。
这些参数直接影响飞行性能,做控制算法实验前,最好先做一组“参数响应测试”,记录在不同参数下的姿态响应,方便后续对照分析。
8. 源码阅读与二次开发建议
8.1 AirSim源码目录速览
如果要做深入二次开发,需要了解AirSim的代码结构。
AirSim/ ├── AirLib/ │ ├── include/ │ │ ├── api/ # RPC接口定义 │ │ ├── sensors/ # 各类传感器抽象 │ │ ├── vehicles/ # 车辆基类和控制器 │ │ └── physics/ # 飞行动力学模型 │ └── src/ ├── Unreal/ │ ├── Plugins/AirSim/ # UE4插件代码 │ └── Env/ ├── PythonClient/ # Python API ├── MavLinkCom/ # MavLink通信组件 └── ros/ ros2/ # ROS桥接AirLib是核心库,独立于UE4引擎运行。你甚至可以在纯C++环境测试飞行动力学。Unreal插件的职责主要是把AirLib与UE4场景绑定,比如把传感器数据从AirLib传输到UE4渲染管线。
8.2 二次开发切入点
二次开发最常见的三种场景:
第一,新增自定义传感器。在AirLib的sensors目录下添加新类,继承SensorBase,实现getOutput之类的接口,然后在UE4插件里注册它。这个路径适合对硬件层面感兴趣的同学。
第二,修改机载计算机模型。AirSim支持在无人机模型上挂载额外的“计算机”,模拟板载芯片的算力延迟。你在配置节点中增加Computation配置项,可以模拟计算耗时、任务排队等效果,对于做边缘人工智能实验有真实感加成。
第三,接入外部飞控固件。AirSim最强大的功能之一是可以与PX4真实飞控联动。通过MavLink通信协议,AirSim把传感器数据发送给PX4固件,PX4运行控制算法把输出指令返回给AirSim,机型姿态由AirSim物理引擎更新。这意味着,你在仿真里跑的就是真机飞控,后续迁移真机时基本没有代码改动。这个联动方案配置稍微复杂,要安装QGroundControl地面站并配置PX4 SITL,但效果非常值得一试。
8.3 用Blueprints还是C++做扩展
UE4的项目逻辑可以用蓝图(Blueprints)实现,也可以用C++实现。AirSim本身是C++写的,如果只是布置场景、放置物体、调光照,用蓝图足够了;但如果要在场景里添加动态障碍物、编写自定义交互逻辑,建议用C++。
比如模拟“飞鸟撞击无人机”这种动态威胁场景,用蓝图做障碍物的随机移动、碰撞响应,虽然能跑,但逻辑复杂后性能很受影响。换成C++的方式用Actor类实现,移动逻辑和碰撞检测都写在C++中,再用蓝图把这个类生成实例放在场景里,可维护性和帧率都更好。
8.4 代码阅读建议
如果你打算把AirSim源码作为毕业设计的一部分来讲解,我建议先读这几个文件:
AirLib/include/vehicles/multirotor/MultiRotorParams.hpp:无人机参数定义AirLib/include/sensors/SensorBase.hpp:传感器基类AirLib/include/physics/PhysicsWorld.hpp:物理世界整体运行流程AirLib/include/api/RpcLibServerBase.hpp:RPC服务端逻辑
读这些文件的顺序是从配置到物理,再到对外接口。看懂之后,答辩时评委问“你的仿真平台是如何工作的”,你就能从参数配置、物理模拟、数据输出三个层面清晰讲出来,而不是只能说“我安装了一个AirSim”。
我个人在实际开发仿真平台时还有一个习惯:在编写算法之前,先跑一遍官方示例中的折返航线,确认无人机的位置控制、图像采集都正常,再开始动自己的算法。因为仿真系统环节多,任何一个部分出错都可能导致算法测试结果不可靠。先把基础链路打稳,后面所有实验都能省很多冤枉时间。AirSim这套平台本身功能很厚,细节也多,只要愿意在说明书和源码上花时间,能挖出的东西远超你最初写在毕业设计任务书上的那几行字。
本文还有配套的精品资源,点击获取