这轮升级不是简单的版本号跳动,UE4 AirSim从旧版升到1.3.1,插件结构、代码接口、配置字段都有不小的变化。我这次把一套很久没动过的无人机仿真项目升上去,中间踩了不少坑,也摸清了这套版本的关键差异。如果你是拿到1.3.1源码准备迁移,或者正被各种编译报错和配置失效折腾,这篇实操记录应该能省你不少时间。
1. 升级前的整体认知:AirSim 1.3.1这次改了什么
1.1 为什么要在UE4里跑AirSim
UE4 AirSim本质上是一个运行在虚幻引擎里的仿真插件。底层用UE4的渲染和物理系统负责场景、光照、碰撞和刚体动力学,AirSim插件本身加上传感器模型、飞行控制器模型、通讯协议和外部SDK接口。你平时跑一辆车、一架多旋翼,所有视觉数据、IMU数据、气压计数据,都是在这个引擎环境里实时算出来的。外部Python脚本通过TCP或共享内存和UE4里的仿真环境通信,控制油门、偏航、悬停、降落,整个过程和真机非常接近。
这套东西的优点在于它省了真实硬件。我在某项目中试过用真机做视觉避障测试,每一次飞行都要担心炸机、电池不够、GPS信号漂移,机动范围很受限制。仿真环境里随时重置位置,可以批量跑几千次随机场景,数据打上标签,算法效果好坏一测便知。另外一个关键点就是它跑在UE4上意味着你能用现成的美术资产,室外城市、室内仓库、森林草地都能搭出来,精度和氛围都要比简单网格场景好很多。
1.2 1.3.1带来的核心变化
升级前我用的还是AirSim 1.2.x系列,整体工作正常,但有几个痛点一直绕不过去:多相机配置比较复杂、仿真速度受渲染瓶颈限制、外部SDK连接偶发掉线。这次升到1.3.1之后,我明显感觉到几处变化。
首先是官方对UE4版本的支持范围更明确。1.3.1推荐的引擎版本已经覆盖到4.24、4.25这些较新版本,老版本编译时那种“这个模块在该引擎版本下已废弃”的报错少了很多。其次是设置文件的解析方式变了。1.3.1对settings.json的校验更严格,某些旧写法虽然能通过解析,但会被忽略或者直接报错,如果把旧配置原封不动拿过来,可能会出现很隐蔽的行为变化。
另外,1.3.1在传感器模型上有较多调整。比如DepthCamera的成像参数、IMU的噪声模型、以及室外场景中光照对Segmentation的影响,这些细节在算法层面非常敏感。过去我跑语义分割任务时,分割图和实际物体类别偶尔对不上,升级后重新标定了一些参数,整体稳定了不少。还有一点值得注意,1.3.1对渲染和物理线程的调度策略有调整,跳帧和资源抢占的问题比旧版轻一些,但代价是如果你没按新版规范配置更新率,某些系统上反而会掉帧。
1.3 升级前需要确认的前置条件
不要一上来就拉源码编译。我强烈建议先列一个检查清单,逐项确认完之后再动工。
第一项是UE4引擎版本。AirSim 1.3.1虽然兼容范围大一些,但它依然不是“所有UE4版本通用”。如果安装的引擎版本过老,比如4.18、4.19,新插件大概率编译失败。以我这次使用的环境为例,用的是UE4.25,配合1.3.1比较顺畅。
第二项是操作系统的构建环境。Windows上需要Visual Studio 2019,并且要安装“使用C++的游戏开发”工作负载。旧版本的时候VS2017还能凑合,到了1.3.1,一些第三方库和编译目标对VS2019更友好。Linux环境下则需要Clang版本和UE4自带工具链匹配。
第三项是原有工程的数据备份。AirSim升级不只是替换插件,它还包含了Shaders、Content、Plugins这些目录的联动改动。直接覆盖有风险,我建议先把整个UE4项目复制一份作为备份,然后升级那一次不心疼。
最后一项是外部脚本和依赖库的兼容性。旧版AirSim对应的Python API版本是0.x,接口名比如client.takeoff、client.moveToZ这些在1.3.1中依然保留,但某些参数的默认值变了,比如moveToPositionAsync的velocity和timeout_sec。如果脚本里直接用了旧版参数而没做兼容,很可能会报参数错误。升级前先用pip检查一下当前安装的AirSim库版本,确保和1.3.1配套。
2. 升级实操:从旧版本到1.3.1的完整过程
2.1 拉取对应分支与目录结构
AirSim的源码托管在公开仓库,我通常直接找ta对应的release标签。这里所有代码都在分支或者tag里,然后拉取对应版本。1.3.1的源码目录结构和老版本基本一致,核心部分包括:
Unreal/Plugins/AirSim:UE4插件本体,包含源码、资源、着色器。AirLib:跨平台的底层库,包含多旋翼动力学、传感器模型、MavLink通信。MavLinkCom:MavLink协议的封装层,主要和飞控通信相关。PythonClient:Python API客户端示例。Examples:一些示例脚本和数据集处理工具。
拉取源码后第一件事是先看一眼README里的版本兼容表。源码里的cmake/CMakeLists.txt这类文件可以看出来它依赖的第三方库版本。如果之前定制过AirSim的源码,比如在某处改过动力学参数或者传感器噪声,这部分修改需要在新版本上重新打补丁,不能直接复制文件。
我的建议是:先不要急着改代码,把新版本源码编译通过、把带场景示例跑起来,确认环境OK再去迁移你自己的定制逻辑。这样能避免“一次改动太多,出问题都不知道怪谁”的尴尬。
2.2 编译插件的完整命令与关键参数
在Windows上编译UE4 AirSim插件,核心就一条命令。进入源码根目录后执行:
build.cmd --target=UE4 --ue-version=4.25这条命令实际上做了好几件事。它会先构建AirLib及其依赖的外部第三方库,比如rpclib、MavLink库;然后把编译产物复制到UE4的插件目录结构里。UE4引擎中新建项目时,就是靠这个编译出的二进制文件来知道你这个插件有哪些模块和类。
这里有几个参数需要特别注意。--ue-version是告诉构建脚本要用哪个UE4版本的工具链。如果你想在多个引擎版本之间切换,一定要确保UE4引擎的安装路径里包含对应的版本号,构建脚本才能定位正确。另一个常用参数是--clean,它会清除之前的构建缓存。升级到1.3.1时,我建议先执行一次带--clean的构建,避免旧版本的中间文件干扰判断。
如果编译过程中报错,不要慌,大部分都是因为缺少依赖。常见错误如下:
- 找不到
libclang或者clang相关库,这种情况通常是你的系统缺少LLVM工具链。 - 报
UnrealBuildTool错误,基本就是--ue-version没写对,或者UE4安装路径没有被系统识别。 - 编译时卡在
PythonClient相关部分,这种多是因为Python环境变量设置不对,尤其是Anaconda和系统Python冲突的时候。
编译完成后,在Unreal/Plugins/AirSim/Source下会生成AirSim插件文件和动态库,这就代表编译通过了。
2.3 处理模块引用与依赖冲突
编译通过只是第一步,真正把AirSim放进你的UE4工程里,还需要处理模块引用关系。如果你的项目创建时是纯蓝图项目,没有C++类,那么得先给项目添加一个C++基础类,让UE4生成Source目录和.Target.cs文件。AirSim插件是以模块方式加载的,纯蓝图项目根本识别不了插件里的C++模块。
我在迁移时遇到过一个比较典型的问题:同一个项目里之前装了另一个仿真插件,它也用自己的第三方库,导致AirSim的某些符号重复定义或者版本冲突。解决方法是检查.uproject文件里的AdditionalDependencies,把不必要的插件模块去掉,再在项目.Build.cs文件里确认AirSim模块被正确引用。
另一个典型问题是UE4引擎版本改动后,项目里旧编译的二进制文件与新版插件不完全匹配。如果你是从很老的项目升级,比如原项目基于4.22甚至4.20,那么建议直接在UE4里删除Binaries、Intermediate目录后重新让引擎生成。这个动作看起来有点暴力,但能解决大量莫名崩溃和白色材质问题。
2.4 场景内容迁移与资源缺失补救
插件编译通过后,你进入UE4编辑器时会看到AirSim插件的类出现在Actor列表里。接下来要把Environment相关Actor或SimMode配置挂到场景上。这里我踩过一个坑,初始场景加载后地面是纯黑色的,什么模型都不显示,原因是AirSim 1.3.1的资源目录里包含了新的Block和Warehouse场景文件,而我的项目地图还引用着旧版的模型路径。
处理方式是先在插件自带示例地图中跑一遍,比如Blocks地图。这个地图内容很丰富,有建筑物、道路、地面装饰,适合测试车辆和无人机。把它的World Settings、Pawn类设置、以及PlayerStart位置记录下来,再对照自己的场景进行修改。如果你需要使用室内场景,官方还提供了Warehouse地图,可以在1.3.1发布包里直接找。
3. 升级后的settings.json配置与参数调整
3.1 新旧字段对照:多旋翼和汽车模型的配置变化
AirSim的配置文件在项目目录下的Saved/AppData里。这个文件路径比较隐蔽,很多人一开始找不到。它是由AirSim插件在运行第一次时自动生成的,初次运行过后你就能看到内容了。1.3.1在配置字段上有不少变化,我整理了一下自己项目中实际改动的部分:
| 配置字段 | 旧版写法 | 1.3.1推荐/要求 | 说明 |
|---|---|---|---|
| SimMode | "SimMode": "Multirotor" | "SimMode": "Multirotor" | 取值变为枚举校验,尽量小写规范 |
| ClockSpeed | "ClockSpeed": 1.0 | "ClockSpeed": 1.0 | 新增了取值范围约束,部分极端值不允许 |
| Wind | "Wind": {"Enabled": true} | "Wind": {"Enabled": true, "Speed": 5.0} | 新版本要求风速值非必填但有默认值 |
| Camera.CaptureSettings | 旧格式直接定义 | "CaptureSettings"对象内字段分级更明确 | 不同相机类型需要的字段不同 |
| Segmentation | "Segmentation": {} | 需要指定"ObjectID"映射和着色规则 | 否则分割图可能全黑 |
| Recording | "Recording": {"Enabled": true} | "Recording": {"Folder": ""} | 新增了Folder字段指定记录目录 |
要注意SimMode的取值。1.3.1支持Multirotor、Car、ComputerVision三种模式,但如果你在配置里写错了大小写,不会报提示,只是启动后模式不对。如果你在跑视觉算法而不需要物理仿真,直接使用ComputerVision模式,配合几个静态相机做图像输出,效率会高很多。
3.2 仿真时间与速度校准:ClockSpeed到底怎么调
ClockSpeed这个参数我单独拿出来讲,因为它特别容易配置出错。它的含义是仿真时钟相对真实时间快慢的倍率。1.0表示实时,2.0表示仿真速度是真实世界的两倍,也就是1秒真实时间走2秒仿真时间。我在跑深度强化学习训练时经常用到2.0到5.0,因为算法需要几千个回合,真实时间跑根本受不了。
但在1.3.1中,把ClockSpeed调太高会带来两个副作用。第一个是物理步长变大,无人机姿态结算精度下降,严重的时候会出现“原地抖动”或者“穿透地面”的情况。第二个是某些依赖环境时间戳的传感器接口会变得不稳定,比如你同时开多个相机并启用记录,时间同步可能会错位。
我建议根据你的目标来定:验证基本控制逻辑时设1.0,跑强化学习批量训练时设2.0或3.0,超过3.0之后先检查传感器数据完整性。另外,ClockSpeed并不是越大越好,它受CPU物理线程性能限制,我的实际测试中,物理线程满载时设置5.0和设置3.0实际跑出来的速度可能是一样的。
3.3 相机与传感器配置调整
1.3.1中多相机挂载方式有变化。旧版里要在配置中写多个"Cameras"对象,并用"CameraName"区分。新版对相机参数校验更严格,你如果只写一个简单相机,系统会自动生成默认RGB相机,但如果你想用Snow、DepthPlanar这些特殊模式,就必须显式声明。
我这次主要跑的是语义分割和深度估计。语义分割相机的配置需要同时设置ImageType为Segmentation,并且在Segmentation里配置好mesh的ObjectID。关键点是,1.3.1中如果某个物体没有给ObjectID,它在分割图里会被渲染成纯黑,这和旧版默认白色效果不太一样。所以升级后如果你发现分割图全是黑的,多半是ObjectID映射问题。
至于雷达和超声波,配置上没有太大门槛,"Enabled": true加上"Range": 20.0这样的参数就能跑。但注意,激光雷达点在1.3.1里默认使用了新的噪声模型,如果你更在意地面真值精度,可以把"NoiseModel"设为"NoNoise"以禁用噪声。
4. 升级后踩过的坑与排查实录
4.1 场景加载后崩溃和黑屏问题
一次升级后最常见的问题就是启动直接崩溃。我遇到的最奇葩的一次是:项目能正常启动,但一旦加载特定场景就弹窗报错,提示缺少某个材质。后来发现是场景地图里引用了一个旧版AirSim的材质资源,而这个材质在新版插件中已经被移除。解决方法也不难,打开场景中的World Outliner,把引用旧版visual effect的Actor删掉,重新添加到场景中。如果Debug信息指向某个具体资源,直接在Content Browser里搜索,找到后右键“Replace References”,替换为普通的基本材质就能解决。
还有一种黑屏情况是UE4编辑器打不开场景,但编译和配置都没问题。这种通常是场景的DefaultGameMode没设置,或者Pawn类型和AirSim不匹配。我建议在新场景里新建一个空的Map,然后手动添加一个AirSimGameMode,或者使用官方示例的GameMode。
4.2 飞控与仿真数据不一致
升级后另一个容易困惑的是无人机起飞后姿态数据返回乱跳。我的实测过程是这样的:用client.getMultirotorState()读姿态,位置和速度都很正常,但姿态角偶尔会跳变到很大值。后来排查意识到这和我把ClockSpeed调到3.0有关,姿态解算在高倍速模式下对IMU噪声更敏感,导致角速度积分漂移。把速度调回1.0之后,姿态回归正常,数据也很平滑。
如果你也遇到类似数据漂移,可以从三个方面排查:先确认ClockSpeed是否过高;再检查settings.json里的IMU参数,看"NoiseScale"是否设置得过大;最后检查飞控模型是否被外力干扰,比如撞到了建筑物面片。物理引擎的碰撞响应在1.3.1里有变化,低速擦碰可能不会被检测到,但高速下会有明显的能量反弹。
4.3 图像返回黑图或尺寸不符
我跑视觉任务多,图像流出了问题最麻烦。旧版项目中用simGetImage("0", ImageType.Scene)来取图,升级后返回数据偶尔是空的。这不是API变了,而是相机名称变了。在新版中,相机名称默认变成了"front_center"、"left"、"right"这些语义化命名,而不再单纯是"0"、"1"这种编号。解决办法是先用simGetCameraInfo()查看当前相机列表,把脚本里的相机名称同步改掉。
尺寸问题则更隐蔽。升级前我配置了640x480分辨率,输出时正常。升级后同样配置,输出的图片却变成了横向拉伸状。原因是1.3.1中相机传感器的视场角和屏幕宽高比会相互影响,你需要显式设置FOV和Resolution。具体来说,在settings.json的CaptureSettings里加上"Width": 640, "Height": 480, "FOV_Degrees": 90,对齐后才不会有黑边或者拉伸。
4.4 性能下降/掉帧严重
性能问题常出现在大型场景中。1.3.1在渲染管线中增加了一些新的后处理效果,如果场景里大量使用实时光源,每帧的GPU开销会显著增加。我在仓库场景中跑四旋翼,帧率从120掉到了40。
优化手段我试过几种:把UE4的Screen Percentage降低到50到75,牺牲点画质换取帧率;关闭体积雾和动态阴影,改用静态光照烘焙;还有就是把AirSim的CaptureSettings里不用的传感器设成"Enabled": false,避免每帧都去做无用计算。最有效的还是最后一种,关掉空闲传感器之后帧率马上提升了。
4.5 升级后Python API连不上仿真环境
最后说一个非常容易踩的坑:仿真跑起来了,Python端却连不上。AirSim外部通信使用RPC,默认端口是41451,如果主程序启动时被防火墙拦截,Python客户端会直接报Timeout。在Windows上首次运行AirSim时,系统会弹出防火墙提示,务必允许访问。如果没弹窗,可以手动在防火墙高级设置里添加端口规则,或者临时关闭防火墙测试一次,确认是这个原因再加白名单。
另外一个原因是Windows网络沙箱或者虚拟机网络模式冲突。如果你把UE4装在虚拟机里,比如某些云主机,外部脚本在主宿主机上访问虚拟机内的AirSim,简单的端口映射可能不够,还需要检查UE4进程是否监听在正确地址上。官方文档里有说明,默认绑定的是本地回环地址127.0.0.1,如果需要在其他机器上访问,就得改一些参数。我当时没深究这个跨机器方向,只确认了本机RPC通了就用。
5. 升级后的使用心得与一点建议
到了这一步,我的UE4 AirSim已经稳定运行在1.3.1上了。回看整个升级过程,最大的感受是:新版不像旧版那样“随便写点配置就能跑”,它的模块化、规范性要求更高,但换来的是更稳定的传感器输出和更清晰的模块边界。如果你之前大量定制过旧版内容,升级时务必预留出足够的时间专门处理编译和配置不兼容问题。
我个人在两个地方获得了明显提升:一个是多传感器同步问题。旧版中启动深度相机和分割相机后,图像时间戳经常对不上,1.3.1里这个问题改善了很多,时间戳一致性更可靠。另一个是仿真速度稳定性。跑批量策略评估时,高倍速模式下的物理表现不再像以前那样出现大幅度波动,这对算法横向对比很有帮助。
如果你也准备升级,我的建议是:在动手之前先把当前环境和官方1.3.1发布说明逐条核对一遍,尤其是UE4版本和VS版本,这是后续所有操作的地基。编译遇到报错时,先去检查第三方依赖和中间缓存,不要盲目改代码或者换工程。配置方面,如果新版本对settings字段校验更严,那就按新版规范调整,别为了省事保留旧写法,否则后期排查更浪费时间。
最后再分享一个小技巧:升级后先跑通官方自带的示例地图,再多放几个不同的仿真场景切换一遍。这样你能快速验证插件本身是否正常,然后再把重心放到自己的项目和算法上。经过这一轮折腾,我一直保留着两个版本的插件目录,一个是旧版备份,用于参照比较配置差异;一个是新版,用于实际开发。遇到行为差异时,两个版本同时对比初始化日志和配置参数,定位问题会快很多。希望这篇记录能帮你少走点弯路。