☰
Ubuntu下AirSim与UE4源码编译:自定义无人机仿真场景搭建实战
2026/10/2 1:02:36 网站建设 项目流程

1. 为什么要在Ubuntu上折腾AirSim和Unreal4

很多人第一次接触无人机仿真,都是先从AirSim的二进制安装包开始的。Windows下装好依赖、跑起例程,几分钟就能看到一架无人机在模拟环境里起飞降落。但真到了要自己改场景、换机型和写感知算法的时候,Windows的自带环境往往不够用,很多研究者手里也只剩Linux环境。AirSim本身是UE4插件,而UE4官方在Linux上支持得并不差,加上Ubuntu服务器或开发机在深度学习、ROS生态上的天然优势,这套组合就成了做视觉导航、路径规划、多机协同实验的常见起点。

AirSim是微软开源的无人机与汽车仿真平台,底层基于Unreal Engine 4渲染与物理引擎,上层提供Python和C++ API,支持高逼真的视觉渲染、IMU/GPS噪声模拟、多机协作和px4/ArduPilot的硬件在环接入。UE4则是Epic Games推出的开源游戏引擎,在AirSim里负责画面、碰撞和环境物理。Ubuntu上把两者拼起来,整个过程的复杂度比Windows高不少,但一旦跑通,后面所有实验都会非常顺手。这篇东西面向的是想在Ubuntu 22.04或20.04下从零搭起一套自定义无人机仿真场景的人,包括编译UE4、编译AirSim插件、搭建自己的地图、配置无人机参数和通过Python API控制无人机起飞。

整个过程踩坑很多,我尽量把每个环节的原理、选择和能跳的坑都写清楚。如果你只是想在Windows下快速体验,这篇同样有帮助,但步骤里涉及Ubuntu部分可以略过。如果你是在服务器上跑,没有显示器也没有显卡,那最后面关于渲染模式和无人驾驶模式的部分要仔细看,很关键。

2. 整体设计与方案选型

2.1 为什么用源码编译而不是直接下载二进制

AirSim官方提供了已编译的UE4插件包,但在Windows和Linux下体验差别很大。Linux下官方二进制包更新节奏慢,很多时候跟Unreal版本不匹配,而且无法自己改AirSim源码去调试。所以我建议在Ubuntu上直接用源码编译。源码编译的过程不会太痛苦,但需要装Unreal Engine本身的源码包。

这涉及一个很实际的问题:Unreal Engine 4有两个安装方式。一个是从Epic Games Launcher安装,另一个是从GitHub拉源码。AirSim官方建议用Launcher安装的稳定版UE4,因为GitHub源码分支官方没有逐一验证,经常遇到一些编译不过的小问题。Ubuntu上Epic Games Launcher可用,但它是32位辅助进程的坑很多,我自己第一次装的时候花了整整一下午。如果你的Ubuntu是22.04或24.04,Launcher的依赖补丁有时会失效。所以我更推荐用GitHub源码加特定版本tag的方式,虽然下载大,但能保证和AirSim的版本兼容。

说到版本匹配,这是整套流程的关键。AirSim每个release都会绑定一个UE4版本,比如AirSim v1.8.1对应UE4 4.27,AirSim v1.7.0对应UE4 4.24。如果你用的是AirSim主分支,那就必须用和主分支绑定的UE4 commit。官方文档会标注Recommended Release,一般存在AirSim的README里。选版本时不要贪新,Unity和UE4互相折腾过的朋友应该懂我的意思,同一个版本组合一旦跑通,就别轻易升。

2.2 Ubuntu版本和服务器/桌面环境的取舍

Ubuntu版本直接影响编译工具链和依赖包。AirSim和UE4在Ubuntu 18.04、20.04、22.04上都有成功案例,我在20.04和22.04上都跑通过。22.04的GCC和CMake版本较新,但对UE4的编译器要求刚好匹配,反而是新系统自带的高版本有时会让UE4编译失败,需要单独降级GCC。如果你完全没有经验,建议直接用Ubuntu 20.04,因为官方对20.04的测试最充分。如果你有NVIDIA显卡想做逼真渲染,记得在装系统前先确认显卡驱动支持,我见过太多人装完显卡驱动后黑屏,然后跑回来问是不是UE4的问题,其实那是显示驱动的锅。

桌面环境方面,纯Server版可以让UE4跑起来,但你要改场景、拖蓝图节点,没有桌面环境会很痛苦。我建议装桌面版Ubuntu,或者Server版加一个轻量桌面。远程开发场景下,可以用X2Go或者VNC连桌面,但延迟会影响操作手感和调参效率,能本地跑就本地跑。有意思的是,服务器上做实时渲染也不是完全不可能,用虚拟显示设备Xvfb可以把渲染画面转到内存,再用AirSim的屏幕API取图,后面会讲到。

2.3 AirSim、UE4、PX4/ArduPilot的关系

很多新人对这三个东西之间的关系特别容易混淆。UE4负责游戏级别的地图渲染和物理碰撞,AirSim是以UE4插件形式存在的一套无人机动力学模型、传感器模型和多机调度逻辑,而PX4、ArduPilot是真正的飞控固件。AirSim可以在无飞控模式下用内置的simple flight模型控制无人机,也可以接上PX4做完整软硬件在环仿真。

在自定义仿真场景这件事上,你至少需要改造两个层面。第一个是地图和景观层,由UE4项目承载,包括地面、建筑、树木、光照、碰撞体等。第二个是无人机和环境交互层,包括无人机起始位置、传感器挂点、物理材质和水面反射等,这层有AirSim参与的成分。有些教程只教你放一个场景进去,但没告诉你怎么让无人机落在地面上而不是穿模掉进地面以下,这些问题在后面真正测试的时候会冒出来。

3. 环境准备与依赖安装

3.1 安装Unreal Engine 4.27源码版

Unreal Engine的源码包需要Epic账号,从GitHub拉取。先注册Epic账号,然后在个人设置里关联GitHub账号,这样才能访问EpicGames/UnrealEngine这个私有仓库。整个过程不难,但必须先关联,否则git clone的时候会提示仓库不存在。

关于UE4版本,我强烈建议用4.27的最后一个release tag,比如4.27.2-release。AirSim主线对4.27的支持最好,而且4.27的Linux版编译问题在社区里积累了大量解决方案,遇到问题好搜。下载时不要直接clone主分支,那个分支体积巨大,而且编译配置可能和AirSim不匹配,用以下命令拉指定tag更稳妥:

git clone -b 4.27.2-release https://github.com/EpicGames/UnrealEngine.git UE4_4.27 cd UE4_4.27 ./Setup.sh ./GenerateProjectFiles.sh make

在下载前先检查磁盘空间。UE4源码解压后大约28GB,编译完大约增加60到90GB,整个流程下来占用100GB以上很常见。如果磁盘不够,后面编译到一半会报错,而且有些错误提示非常隐晦,会让你误以为是源码问题。我建议至少预留150GB可用空间。

3.2 编译依赖与常见缺失库

在Linux下编译UE4,依赖的包相当多,包括libx11-dev、libxrandr-dev、libxi-dev、libgl1-mesa-dev、libssl-dev等。官方文档列了一堆,但每个Ubuntu版本名称可能不同,缺了什么库编译时就报什么错。我整理了一个常见的安装命令组合,在Ubuntu 20.04和22.04上实测可用:

sudo apt update sudo apt install build-essential git unzip python3 python3-pip \ libx11-dev libxrandr-dev libxi-dev libgl1-mesa-dev libglu1-mesa-dev \ libssl-dev libncurses5-dev libcrypto++-dev libfreetype6-dev \ libxinerama-dev libxcursor-dev libxdamage-dev libxcomposite-dev \ libasound2-dev libpulse-dev libudev-dev libdbus-1-dev \ libnss3-dev libatk1.0-dev libatk-bridge2.0-dev libcups2-dev \ libdrm-dev libxkbcommon-dev libxcb-keysyms1-dev libxcb-image0-dev \ libxcb-icccm4-dev libxcb-shm0-dev libxcb-render-util0-dev

还有几个是UE4自己需要的,比如libc++-dev和libc++abi-dev,在部分Ubuntu版本上需要额外装。另外如果你的是Ubuntu 22.04以上,可能还需要装Python2环境,因为UE4的构建脚本早期依赖Python2,新版本虽然换成了Python3,但有些辅助脚本还是会有问题。

编译UE4本身非常耗时,四十分钟到几个小时不等,取决于CPU核心数和内存大小。我建议设置MAKEFLAGS来控制并行度,避免内存不足导致编译进程被杀。在16G内存的机器上,make -j4比较稳定,32G内存可以用-j8,但再往上就很可能出问题。

3.3 安装虚幻引擎插件时需要注意的坑

UE4编译完成后,需要验证能否正常启动。在终端运行./Engine/Binaries/Linux/UnrealEditor,如果启动界面正常弹出,说明编译成功。如果启动时提示缺少Vulkan库,需要安装libvulkan1和libvulkan-dev。如果你没有GPU,UE4也可以运行,但只能用OpenGL或者软件渲染模式,这种模式下无法做复杂的场景渲染,跑AirSim的无人机视觉会比较吃力。

编译完UE4后,你会得到一个UE4开发环境,但它本身并不包含PlacedActors等所有引擎自带资产。这里有个容易忽略的点:AirSim作为一个插件,要在UE4的插件目录里编译,还需要一个UE4的C++工程来承载。如果只编译了引擎不建工程,AirSim的插件格式验证会失败。所以下一节说的构建UE4工程,不是可选项,而是必需项。

4. 编译AirSim插件并创建UE4无人机工程

4.1 拉取AirSim源码与编译

AirSim本身不需要单独安装到系统,它的核心是UE4插件和一个Python/C++客户端库。插件部分要放进UE4工程的Plugins目录里编译,Python客户端通过pip安装。

首先要拉AirSim源码,建议用稳定版本而不是主分支。我用的比较多的是v1.8.1,它对UE4 4.27的支持最好:

git clone https://github.com/microsoft/AirSim.git cd AirSim git checkout v1.8.1 ./setup.sh ./build.sh

setup.sh会下载AirSim依赖的第三方库,包括MavLinkCom、eigen等。如果网络不稳定,这一步会失败,多试几次即可。build.sh会先编译AirSim的核心库,再编译Unreal插件,最后生成一个Unreal/Plugins/AirSim目录,这个目录就是后续要放进UE4工程里的插件包。

顺便说一句,如果你只需要Python API来做控制,不打算打开UE4编辑器,仍然需要执行setup.sh和build.sh,因为Python包内部依赖AirSim编译出的C++库。否则调用airsim.Client()时会报找不到libAirSimClient之类的问题。

4.2 创建UE4空工程并集成AirSim插件

AirSim官方提供了一个模板项目,但我不想直接用模板,因为自定义场景的需求往往要在模板上大改,直接看模板反而容易一头雾水。更好的方式是自己创建一个空的UE4 C++工程,然后把AirSim插件放进去。

打开UE4编辑器,选择C++类、Basic Code模板。如果是通过命令行创建工程,可以用以下命令:

cd UE4_4.27/Engine/Build/BatchFiles ./RunUAT.sh BuildCookRun -project=/path/to/MyProject/MyProject.uproject \ -build -nop4 -utf8output -unattended -cook -stage -package \ -clientconfig=Development -serverconfig=Development \ -platform=Linux -target=MyProject

这里要说明一下,AirSim要求工程必须至少有一个C++类,纯Blueprint工程不被插件识别。所以即便你后面全用蓝图拖节点,也必须在创建工程时选C++模板,然后添加一个空的C++类到工程里。

创建完工程后,把AirSim的Unreal/Plugins/AirSim目录复制到工程的Plugins目录下,然后关闭编辑器,在工程根目录生成并重新编译工程文件:

cd /path/to/MyProject /usr/bin/env bash Engine/Build/BatchFiles/Linux/Build.sh MyProjectEditor Development Linux \ -project="/path/to/MyProject/MyProject.uproject" -waitmutex

如果没有Engine/Build/BatchFiles这个路径,说明你的UE4没有完整编译,回到上一节搞定。

4.3 配置场景的PlayerStart和无人机关卡

无人机仿真的核心逻辑是:AirSim用PlayerStart来确定无人机初始位置,用GameMode来管理环境规则。自定义场景时,你要做三件事。

第一,打开地图文件,把默认的地面体改为AirSim的BlocksEnvironment类型,或者建一个空的Level并手动添加一个StaticMesh作为地面,否则无人机启动时会掉进一个无底深渊。

第二,在场景中放置一个PlayerStartActor,这决定了无人机的出生点。你可以把它放在屋顶、停车场中央、空地等任意位置,但必须保证出生点上方没有遮挡物,否则无人机起飞检测会失败或者螺旋桨直接撞到障碍物。经验做法是放在空地上方2米左右,AirSim会自动调整高度到地面以上。

第三,把World Settings中的GameMode Override改为AirSimGameMode,否则UE4不会加载AirSim的仿真逻辑。这一步很多人漏掉,结果发现无人机模型不显示,但日志里没有报错,就是这个原因。

以上操作在做完后,建议先保存地图,再运行Play按钮测试。如果运行后看到左下角有一些AirSim的初始化日志,说明插件加载成功。如果没看到,检查一下是不是没有把AirSim插件放进工程Plugins目录,或者工程没有正确识别插件。

5. 自定义无人机场景实操:从默认地图到真实场景

5.1 修改默认地图和增加自定义建筑模型

AirSim默认的Blocks地图就是一个建筑街区的样例,对大多数测试来说够用,但如果你想做更细粒度的场景,比如某个工厂厂区、校园广场或者农场环境,需要自己加场景模型。UE4里添加自定义模型有两种方式:一种是从外部导入FBX或OBJ文件,另一种是直接用内置的Basic Shape组件搭建立方体、平面和圆柱。

如果你手头有区域的CAD模型或者3D扫描文件,用FBX导入是首选。导入时注意单位设置,UE4的默认单位是厘米,如果源模型单位是米,缩放会乱。一个简单的检查方法:导入后看模型的包围盒大小,一个正常的人模型应该在一米八左右。如果导入的模型特别大,说明单位没换算对。

用Basic Shape搭场景虽然看起来简陋,但很方便用来做避障和导航实验。我记得有一次为了验证无人机的激光雷达检测效果,搭了一个由低矮立方体组成的迷宫,跑起来比直接用复杂建筑模型还高效。如果你是要做视觉研究,可以先用Basic Shape搭一个大致布局,再用带贴图的高精度模型替换,两者之间的逻辑完全一样。

5.2 设置光源、天空盒和碰撞体

自定义场景最容易被忽视的就是光照。UE4默认的全动态光照对性能要求很高,而且有时会导致场景过曝或过暗,AirSim的视觉API取出来图像跟实际环境差别巨大。我建议在自定义地形场景中至少烘焙一次Lightmap,或者直接使用定向光加天空球的基础方案。

天空球可以手动放置一个BP_Sky_Sphere,或者直接用UE4自带的世界设置。这里有个小细节:要打开Project Settings > Engine > Rendering,把Auto Exposure关掉,否则无人机旋转时相机会自动调整曝光,导致视觉里程计算法在剧烈光照变化下崩溃。稳定曝光是无人机视觉仿真中最容易踩坑的地方,很多人觉得AirSim的视觉图像不稳定,其实不是AirSim的问题,是UE4的自动曝光默认开着。

碰撞体这方面,如果你是从一个空白场景开始,记得给地面和建筑加碰撞体,否则无人机会直接穿透下去。UE4里StaticMesh默认会有简单的盒体碰撞,但有时候不够准确。特别是如果你想模拟激光雷达或者碰撞检测,必须手动调整碰撞体的形状和大小,否则雷达点云数据会穿过障碍物。

5.3 添加多无人机出生点和起始朝向

AirSim v1.8.1以后支持多无人机,在settings.json的Vehicles中配置多个实例即可。但多无人机在自定义场景中要额外处理队友之间的初始位置和朝向,否则刚启动就撞在一起。

我在做多机协同实验时,会在场景中放多个PlayerStart,然后把每个PlayerStart的坐标和朝向写到settings.json里。AirSim会按顺序匹配这些出生点。配置示例:

{ "SettingsVersion": 1.2, "SimMode": "Multirotor", "Vehicles": { "Drone1": { "VehicleType": "SimpleFlight", "InitialPosition": {"X": 0.0, "Y": 0.0, "Z": 0.0}, "InitialRotation": {"Yaw": 0.0, "Pitch": 0.0, "Roll": 0.0} }, "Drone2": { "VehicleType": "SimpleFlight", "InitialPosition": {"X": 10.0, "Y": 0.0, "Z": 0.0}, "InitialRotation": {"Yaw": 90.0, "Pitch": 0.0, "Roll": 0.0} } } }

这里有个容易迷糊的点:InitialPosition是相对PlayerStart位置的偏移,不是世界绝对坐标。如果你只在场景中放了一个PlayerStart,第二个无人机会在你设置的位置生成,但这个位置的参考系是相对第一个PlayerStart的。所以多机实验时,最好在所有无人机都放在同一个PlayerStart附近,然后用偏移量去区分位置。

5.4 保存并启动仿真关卡

一切配置好之后,在编辑器里点击运行。如果你不需要打开编辑器窗口,可以将工程打包成一个独立可执行文件。打包之前建议先用编辑器运行一遍,确认无编译错误。独立可执行文件的好处是启动快,而且可以在没有UI的服务器上运行,只要后端加上-nullrhi参数。

如果打包过程中遇到cook失败,多半是因为缺少某个资源或者引用了不存在的模型。排查方式是看日志输出中失败的资源路径,回到UE4编辑器中打开对应资产,确认是否有效。

6. AirSim配置文件解析与无人机参数调优

6.1 settings.json的完整结构

AirSim的配置文件位于~/Documents/AirSim/settings.json,这个路径在Linux下就是$HOME/Documents/AirSim/settings.json。文件内容直接影响无人机物理模型、相机参数、云台和传感器配置。

最常用的配置块包括:

{ "SettingsVersion": 1.2, "SimMode": "Multirotor", "ClockSpeed": 1.0, "PhysicsEngine": "FastPhysics", "CameraDefaults": { "CaptureSettings": [ {"ImageType": 0, "Width": 640, "Height": 480, "FOV_Degrees": 90}, {"ImageType": 1, "Width": 640, "Height": 480, "FOV_Degrees": 90}, {"ImageType": 2, "Width": 640, "Height": 480, "FOV_Degrees": 90} ] } }

ImageType 0是场景相机,1是深度相机,2是分割相机。如果你要做视觉定位,建议深度图分辨率至少为640x480,如果做语义分割则可以用1280x720,分割图的分辨率会影响标注准确性,但过高的分辨率也会显著拖慢推理速度。

6.2 调整无人机性能与仿真物理参数

AirSim默认的无人机参数偏向于中等体型消费级无人机,但不同研究需求对动力学特性的要求不同。比如你如果要测试一个高机动控制器,默认参数会显得太笨重。

调整物理参数时,重点关注以下几个字段:

  • Mass:无人机质量,单位kg。默认1.0kg,如果负载大需要相应增加。
  • MaxThrust:最大推力,单位N。这个值影响爬升速率和抗风性能,如果设置过小,无人机会一直推油门但稳不住高度。
  • MaxAngularVelocity:最大角速度,单位rad/s。调大可以提高机动性,但会让仿真更容易发散。
  • DragCoeff:阻力系数,影响平飞速度。默认值在低速下表现良好,但高速时会过于理想化。

这些参数在AirSim源码中的对应位在AirLib/src/vehicles/multirotor/firmwares/simple_flight下,如果你熟悉C++可以直接改源码重新编译。如果不想碰源码,可以在settings.json的Vehicles.Drone1.ParameterSets里用physics设置部分参数,但很多参数并没有全部暴露给配置文件,此外的工作仍需改源码。

6.3 多相机传感器挂载与视角调整

无人机视觉实验往往需要不止一个相机,AirSim支持在一个无人机上挂载多个相机,每个相机可以有不同的姿态角、视场角和渲染模式。配置位置在Vehicles.Drone1.Cameras,每个相机用"CameraName": {...}定义。

相机挂载时有个关键点是姿态角的朝向约定。AirSim里相机的X轴是前方,Y轴朝右,Z轴朝下,这个坐标系统跟ROS中常用的REP-103坐标完全不同。如果你在ROS里用camera optical frame,需要额外做一次旋转。我记得第一次接相机数据的时候,图像上下左右颠倒,查了半天才发现是相机坐标系和ROS坐标系映射的问题。

另外,如果你想模拟鱼眼相机或者广角相机,直接调节FOV_Degrees到180以上是不够的,AirSim只支持最大120度视场角的普通透视相机,超过这个值会出现畸变严重或者渲染错误。鱼眼效果需要自己改AirSim源码里的相机模型,或者在渲染后再做后处理。

7. 用Python API控制无人机并验证自定义场景

7.1 安装AirSim Python客户端并建立连接

AirSim的Python端很简单,用pip安装:

pip install airsim

然后在Python脚本中创建客户端。但这里要注意:如果你是从源码构建AirSim,系统里默认的Python包还是二进制版,版本可能不匹配。最好在AirSim源码目录的PythonClient下运行脚本,那边的包是刚编译好的,或者手动把AirSim/PythonClient加入PYTHONPATH。

连接无人机的标准姿势:

import airsim import time client = airsim.MultirotorClient() client.confirmConnection() client.enableApiControl(True) client.armDisarm(True) # 起飞 client.takeoffAsync().join() client.moveToZAsync(-3, 1).join() print("drone took off!")

confirmConnection会一直等待仿真器就绪,如果连不上,大概率是仿真器没开。enableApiControl是夺取控制权的关键,如果你不调用它,AirSim会认为你只是旁观者,遥控器或简单飞行控制器仍然控制无人机。

7.2 在自定义场景中巡航测试

场景搭建完成后,至少要做一次手动飞行和自动飞行测试。自动飞行测试建议先跑一段简单的矩形航线,验证无人机能在自建场景中正常移动,不会穿模或者撞到未知碰撞体。

矩形航线的核心逻辑是逐点移动,我用得比较多的是moveToPositionAsync和moveOnPathAsync两个API。前者适合精确到点,后者适合批量路径。测试脚本大概长这样:

import airsim import time client = airsim.MultirotorClient() client.confirmConnection() client.enableApiControl(True) client.armDisarm(True) client.takeoffAsync().join() # 起飞后先上到3米高度 client.moveToZAsync(-3, 1).join() points = [ airsim.Vector3r(10, 0, -3), airsim.Vector3r(10, 10, -3), airsim.Vector3r(0, 10, -3), airsim.Vector3r(0, 0, -3), ] # 沿路径飞行,速度1米每秒 client.moveOnPathAsync(points, 1.0, 5, airsim.DrivetrainType.ForwardOnly, airsim.YawMode(False, 0), 20, 1).join()

跑完这个测试,你基本能确认场景是否适合飞行。如果无人机在飞行到某个位置时偏离航线,大概率是那附近有碰撞体或者地面高度突变。这时候需要回到UE4编辑器里检查场景模型和碰撞体,而不是调飞控参数。

7.3 用计算机视觉接口获取场景图像

自定义场景最终是为了用视觉数据训练和测试算法。AirSim的视觉API支持获取多种图像类型。获取场景图和深度的代码:

responses = client.simGetImages([ airsim.ImageRequest("0", airsim.ImageType.Scene), airsim.ImageRequest("0", airsim.ImageType.DepthPerspective), ]) for response in responses: if response.pixels_as_float: # 深度图 import numpy as np depth = np.array(response.image_data_float, dtype=np.float32).reshape(response.height, response.width) # 转成8位显示 depth_image = np.clip(depth / 10.0, 0.0, 1.0) else: # 场景图 import numpy as np img = np.frombuffer(response.image_data_uint8, dtype=np.uint8).reshape(response.height, response.width, 3)

这里有个阿里嘎多但必须注意的点:深度图的数值含义。DepthPerspective的深度值表示的是相机光心到环境中点的距离,不是点到相机平面的垂直距离。这个区别在做点云重建时很关键,如果你直接用深度值当Z坐标,点云会变形。只有DepthPlanar的深度值才是垂直距离,两者不能混用。我见过不少人在这个坑里浪费了好几天,明明仿真数据完全正常,但重建出来的点云总是不对,就是因为用错了深度类型。

7.4 保存图像和时间戳,构建自己的数据集

做视觉导航实验时,光获取一张张图像还不够,还需要保存时间戳、无人机姿态、相机内外参等信息。AirSim的图像响应里自带time_stamp字段,而姿态和相机参数可以用simGetGroundTruthKinematics和simGetCameraInfo获取。

建议在自定义场景中先做一套时间同步的流程。我的习惯是:获取一次图像,同时获取一次无人机位置和姿态,存为一个JSON条目,最后把所有JSON合并成一个excel或csv。这样后面做视觉里程计测试时,可以直接用时间戳对数据做对齐,而不必依赖系统时间。仿真系统有一个ClockSpeed参数,如果你设置了非1.0的值,系统时间戳和仿真时间戳会不一致,直接用系统时间会错乱。

下面是一个保存图像和状态数据的示例:

import json import os import time save_dir = "./airsim_dataset" os.makedirs(save_dir, exist_ok=True) for i in range(100): responses = client.simGetImages([...]) # 获取图像 kinematic = client.simGetGroundTruthKinematics() data = { "timestamp": responses[0].time_stamp, "position": {"x": kinematic.position.x_val, "y": kinematic.position.y_val, "z": kinematic.position.z_val}, "orientation": {"pitch": kinematic.orientation.pitch, "roll": kinematic.orientation.roll, "yaw": kinematic.orientation.yaw} } # 分别保存图像和元数据 filename = os.path.join(save_dir, f"frame_{i:05d}") # 保存图像... with open(filename + "_meta.json", "w") as f: json.dump(data, f) time.sleep(0.05)

这个方法虽然简单,但已经能满足大部分视觉SLAM和无人机导航研究初期的数据需求。更高阶的用simSaveImage接口直接保存png,一样可行,但速度慢一些,批量采集时还是用内存处理快。

8. 常见问题与排查技巧实录

8.1 编译失败类:UE4编译卡死或报错

如果你在编译UE4时遇到fatal error: 'SDL.h' file not found之类的报错,说明SDL2的开发库没有装好。在Ubuntu 22.04上,需要额外安装libsdl2-dev,否则UE4音频模块编译不过。这个报错出现的位置非常靠后,很容易让人以为编译快完成了,实际上还差得远。

还有一种情况是编译过程中某个模块出现语法错误,但源码本身没问题。这时候首先检查是不是内存不足,或者并行编译任务开得太多。make -j8在16GB内存机器上偶尔会被OOM killer干掉,日志里没有明显的错误信息,但编译进程突然退出。用make -j4重试往往就能解决。

8.2 AirSim插件编译后无法在UE4中启用

把AirSim插件复制到工程Plugins目录后,打开工程如果看不到AirSim插件启用,最可能的原因是插件的目标平台与当前编译平台不一致。AirSim在Linux下会编译出libAirSim.so和libVehicles.so,如果你的系统缺少某些库,插件加载会失败,但UE4界面上可能只显示一个“禁用”状态,不显示根因。

排查方式是看UE4编辑器日志,通常在~/Library/Logs/Unreal Engine/Editor.log或/var/log/application.log里查找与AirSim相关的关键字。最常见的是缺少libMavLinkCom.so的对齐库,这时候去AirSim的build/output/lib目录下看看.so文件是否都有,并用ldd检查依赖。

8.3 无人机不动、不起飞或者直接掉下去

无人机在自定义场景中最常见的故障就是不起飞。先确认是否已经执行了enableApiControl(True)和armDisarm(True),很多新手的代码顺序写反了,导致起飞指令被忽略。

如果指令顺序没问题但无人机还是不动,检查一下当前场景的地面材质。AirSim要求地面必须有碰撞体并且碰撞响应正常。如果你用的地面是UE4里用Landscape地形创建的地面,注意Landscape默认碰撞是开启的,但有些自定义StaticMesh地面没有开启Collision,无人机起飞时虽会显示起飞成功,但实际位置在无限掉落。判断的方法是打开UE4编辑器,选中地面物体看Details面板里Collision是不是Enable。

如果无人机一直下落,多半是地面没有碰撞体,或者无人机出生点距离地面太远导致重力把无人机拉到了地下。这种时候直接把PlayerStart放到地面上方1到2米处即可。

8.4 画面渲染异常或图像全黑

全黑问题几乎是每个AirSim新手都会遇到的。首先检查是不是相机朝向你的方向不对,比如相机正对天空或正对地面。其次检查相机的CaptureSettings中的ImageType是不是设成了Depth或Segmentation,这两种图默认输出灰度图或标签图,直接拿来看是黑的。

深度图全黑通常是因为深度值太大或太小。AirSim的深度图默认单位是米,默认的far plane可能设置得太大,导致近处物体深度值映射后很暗。可以设置DepthCameraFOV和DepthTargeted相关参数来调整,或者在获取深度图后手动做归一化,比如depth = depth / 30.0再显示。

8.5 仿真卡顿、FPS过低

卡顿是仿真最常见的性能问题。如果你的目的是真机移植或者实时交互,最好用有GPU的机器,并且关闭UE4的实时阴影和某些后期处理效果。在Project Settings里可以把阴影质量调到中低,关掉体积云和天空大气散射效果,AirSim的视觉质量仍然很高。

如果必须用无GPU的服务器或虚拟机跑,可以用-nullrhi启动UE4,然后改用AirSim的RenderMode设为NoDisplay,这样无人机动力学和传感器仍然模拟,但不会实际渲染图像。对于算法验证来说这样足够,但别指望这种方法能出图像。

VMware虚拟机用户要特别留意:UE4的渲染在VMware里可能会异常,因为VMware的OpenGL支持很弱。如果没有物理GPU直通,我的建议是不要在虚拟机里跑AirSim,直接在实体机装Ubuntu,或者用Docker加上NVIDIA GPU支持,都比虚拟机靠谱得多。

8.6 无法连接Python客户端

这个问题的概率也不低,尤其是你在同一台机器上运行UE4和Python脚本时,常常因为局域网防火墙或端口冲突导致连接不上。AirSim默认用RPC端口41451,如果这个端口被占用,仿真器会使用41452等后续端口。

排查时可以看UE4日志里提示的实际端口号,然后让Python客户端显式指定端口:

client = airsim.MultirotorClient(ip="127.0.0.1", port=41452) client.confirmConnection()

如果是在远程服务器上运行仿真器,本地跑Python客户端,ip要改成服务器的内网IP或公网IP,同时确保防火墙放行TCP 41451端口。AirSim的RPC协议没有加密,不推荐直接暴露在公网上,实在需要远程访问,建议走SSH隧道。

9. 场景扩展与二次开发

9.1 接入ROS、PX4和ArduPilot

自定义场景跑通之后,下一步大概率是你想把AirSim接进自己的控制栈里。AirSim官方提供了ROS接口,方式有两种:一种是在AirSim的ros/src目录里编译airsim_ros_pkgs,另一种是通过Python脚本把获取的状态和图像转发到ROS topic。前者性能更好,后者实现更快。

PX4和ArduPilot的支持方式和ROS略有不同,AirSim提供的是UDP连接,飞控固件在仿真环境下再通过MAVLink与AirSim通信。这个配置涉及端口和固件参数,不建议一上来就搞,先把Simple Flight模式跑通,再逐步切到PX4。

9.2 多机集群仿真

多机仿真需要你提前修改场景里多个PlayerStart的位置和朝向,还要保证每台无人机有独立的配置文件接口。AirSim支持多Vehicle,但Python客户端要分别连接不同的无人机,示例可以在PythonClient目录下的multirotor/multi_vehicle_control.py中找到,它演示了同时控制多台无人机起飞和移动的方式。多机实验对场景的性能要求更高,一台低端GPU可能带不动太多相机流,如果只是做动力学和控制,可以关闭部分视觉渲染。

9.3 天气、风场与动态障碍物

如果要测试无人机在环境扰动下的鲁棒性,AirSim提供了参数设置,包括风、雨、雪等天气选项。其中风参数通过simSetWind接口设置,可以指定风速和风向,对无人机动力学的影响比较明显。动态障碍物则需要你手动在UE4场景中编写蓝图逻辑,比如移动的车辆或行人,这已经超出AirSim本身的范畴,但UE4的蓝图系统让这块实现成本并不高。

我自己的经验是,先保证静态场景稳定运行,再逐步加入动态元素。一次加太多动态物体,很难判断是无人机控制问题还是环境碰撞问题。

9.4 自定义传感器:添加激光雷达、摄像头与视觉传感器

AirSim内置了激光雷达,配置方式是在settings.json中给Vehicle添加Lidar配置。但自定义高精雷达需要改源码或者用API,AirSim没有完全开放雷达模型的点云处理逻辑。如果你只是想测Lidar点云数据,建议直接用内置Lidar,它的点云格式和ROS兼容性较好,可以减少很多绕弯的功夫。

视觉传感器方面,AirSim的相机模型比较简化,没有畸变参数。如果你在做相机标定相关研究,可能需要在后端加上自己的失真模型。这个确实麻烦,但AirSim本身不是相机标定软件,它的定位是仿真数据生成和算法验证平台,作用域不同。

10. 写在最后的实操经验

我花了这么多篇幅讲流程,其实里面最值钱的不是步骤本身,而是你做每一步时能不能理解它后面的逻辑。无人机仿真场景搭建这件事,本质上是把真实世界的无人机实验搬到一个可复现的虚拟环境里。你在场景里填的每一个建筑、放的每一棵树,都在影响视觉算法看到的数据分布,所以我在实际做项目时,一直强调“场景要和最终应用场景尽量对齐”。如果只是算法的跑通验证,那就用默认Blocks场景就够了;如果是要验证视觉导航算法的真实表现,那我建议你花时间做一个与最终部署环境相似度足够高的自定义场景。

AirSim在Ubuntu上其实并没有官方“图形化一键安装”的方式,每个环节都是命令行和配置文件堆出来的,但这也给了我们最大的灵活性。你可以把一个场景封装成Docker镜像,也可以把场景参数散落到配置文件里,用脚本批量生成不同难度的实验环境。

如果你在搭建过程中卡住了,先别急着怀疑AirSim有问题。我的经验是,绝大多数问题都出在UE4版本、编译参数、场景碰撞体配置这三块。把这三块逐一确认清楚,整个框架跑通大概率只是时间问题。最后说一句,不管你是想跑视觉SLAM、多机协同还是高机动控制,一个你没亲手搭过的仿真环境,可能比一个没调参的控制器更让你头疼。听话,先从自定义场景开始,别一上来就惦记着PX4和集群。

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

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

立即咨询