
AirSim 这个东西做无人机和自动驾驶算法的人基本都绕不开。它本质上是微软开源的一套高保真仿真平台底层基于虚幻引擎把物理模型、传感器模型、自动驾驶接口全给包了一层。而当你真正把 AirSim 装好、编译好、第一次启动的时候决定“仿真环境到底长什么样、传感器装在哪、飞控怎么连”的并不是那一大堆 C 代码而是一个躺在 Documents/AirSim 目录下的 JSON 文件——文件名是settings.json很多人容易记成setting.json后面我会专门说这个坑。我印象特别深AirSim 1.3.1 这个版本是目前社区里用得最多的稳定版之一官方文档虽然写得很全但对新手来说太散了很多参数注释不全枚举值也没有集中说明。这篇文章我就拿 1.3.1 版本为例把settings.json里那些常见和不常见的配置项一个一个拆开讲告诉你每一项到底控制什么、怎么调、默认值是多少、实际跑的时候会在哪里踩坑。内容不追求面面俱到但凡是你在做项目时大概率会碰到的我都会带上实操经验来聊。1. 初识setting.jsonAirSim的配置中枢1.1 文件位置与JSON格式约定先说位置。Windows 下AirSim 启动后会在Documents\AirSim\目录下寻找settings.jsonLinux 下对应路径是~/Documents/AirSim/settings.json。如果你从来没有手动创建过这个文件AirSim 会用内置默认配置直接跑默认是 SimpleFlight 多旋翼场景是 Blocks。但只要你做了任何定制就强烈建议把文件放在上述路径下不要放到项目目录里AirSim 默认不扫描项目目录下的配置。这个文件的格式就是标准 JSON没有注释、没有尾逗号所有键名必须用双引号。很多新手第一次改配置文件喜欢在某个参数后面加一行// 说明结果整个配置直接失效AirSim 启动后完全忽略你的文件恢复默认设置。这一点我在第 5 部分会再强调因为实际踩坑概率真的很高。顺便说一句AirSim 对 JSON 的解析策略比较“宽容”——它不会因为单个键名错误就直接崩溃而是会打警告然后使用默认值。这意味着你很容易改错参数却毫无察觉直到仿真里某个行为不正常才发现。所以我个人的习惯是每次改完配置先看一眼 AirSim 启动时的日志输出确认自己期望的配置项确实被加载了。1.2 一份典型文件的核心骨架在逐个参数讲解之前先给你看一份我在 1.3.1 版本上跑过的比较完整的settings.json后面所有解析都围绕这个骨架展开{ SettingsVersion: 1.2, SimMode: Multirotor, ClockSpeed: 1, ClockType: SteppableClock, OriginGeopoint: { Latitude: 47.641468, Longitude: -122.140165, Altitude: 10 }, TimeOfDay: { Enabled: true, StartDateTime: 2019-05-09 10:00:00, CelestialClockSpeed: 1 }, Weather: { Enabled: true, Rain: 0.5, Snow: 0.2, Fog: 0.1 }, CameraDefaults: { CaptureSettings: [ { ImageType: 0, Width: 960, Height: 540, FOV_Degrees: 90 } ] }, Sensors: { Gps: { SensorType: 3, Enabled: true, Eph: 1.5, Epv: 1.5 }, Imu: { SensorType: 2, Enabled: true }, Lidar1: { SensorType: 6, Enabled: true, NumberOfChannels: 16, PointsPerSecond: 100000, X: 0, Y: 0, Z: -1, RotX: 0, RotY: 0, RotZ: 0 } }, Vehicles: { PX4: { VehicleType: PX4Multirotor, UseSerial: false, UseTcp: true, TcpPort: 4560, ControlIp: 127.0.0.1, Sensors: { Gps: { SensorType: 3, Enabled: true } } } } }这份配置本身就能跑起来一架 PX4 多旋翼、一个 GPS、一个 IMU、一个 16 线激光雷达场景带天气和真实时间光照。下面我按逻辑分组把每个区块内部的东西展开说。2. 核心运行参数决定仿真怎么跑2.1 SettingsVersion与SimModeSettingsVersion是 AirSim 用来判断配置结构版本的字段。1.3.1 版本对应的值是 1.2。如果你拿 1.3.1 的编辑器打开一个SettingsVersion为 1.1 的旧配置AirSim 会尝试兼容解析但不会保证所有新特性都生效。反过来如果你把 1.2 写在一个比较老的 AirSim 版本上跑程序可能直接忽略部分字段。所以我在升级 AirSim 版本之后第一件事就是检查SettingsVersion是否需要同步调整。SimMode是最关键的顶层开关它决定仿真器的运行模式。最常用的三个值Multirotor多旋翼模式默认加载四旋翼物理模型支持 PX4 和 SimpleFlight 两种自驾仪。Car汽车模式车辆物理模型基于 PhysX。ComputerVision计算机视觉模式没有载具物理模型只有相机和传感器常用于纯视觉数据集采集或分割标注。我见过不少新手在SimMode里填了小写multirotor结果 AirSim 完全不受影响似的创建了默认载具日志里却提示找不到匹配模式。这个枚举是区分大小写的必须严格用官方的驼峰命名。另外一个跟SimMode容易混淆的是顶层和Vehicles里的VehicleType。顶层SimMode决定全局默认载具类型而Vehicles里可以覆盖全局创建多种不同类型的载具。如果你只设置了SimMode: Car而没有写VehiclesAirSim 就会给你一架默认的 PhysXCar如果你想同时跑一辆车和一架多旋翼就得用Vehicles精确声明。2.2 时钟、天气与光照参数ClockSpeed控制仿真时钟相对真实时间的倍率。默认值是 1也就是实时。做强化学习训练的人经常把它调到 5 甚至 10因为算法需要快速采样大量状态数据真实时间太慢了。但实际上ClockSpeed调太高会导致物理引擎步长不稳定出现无人机乱飞、碰撞检测失效之类的问题。我的经验是仅在不需要精确物理状态、只采集图像时调到 2~3做无人机控制训练时尽量保持 1 或者 0.5。ClockType在 1.3.1 版本里常见值是SteppableClock。这个类型允许你通过 API 手动推进仿真时间适合做确定性回归测试。默认情况下用ScalableClock更直观它配合ClockSpeed直接缩放时间。实际项目里我建议明确写出来不要依赖默认值因为 AirSim 不同小版本的默认ClockType可能有差异。天气参数块是这样的Weather: { Enabled: true, Rain: 0.5, Snow: 0.2, Fog: 0.1, Dust: 0, Maple: 0 }所有天气值都是 0 到 1 的浮点数0 表示关闭1 表示最大强度。注意天气系统只在支持该功能的场景下生效比如 Blocks 环境可用一些纯室内场景根本不会渲染雨雪。要动态控制天气可以通过 AirSim 的 Python API 的simSetWeatherParameter接口设置文件只是在启动时给定初值。时间光照方面1.3.1 引入了TimeOfDay块TimeOfDay: { Enabled: true, StartDateTime: 2019-05-09 10:00:00, CelestialClockSpeed: 1, StartDeltaTime: 0 }Enabled为 false 时场景用固定的太阳光照为 true 时太阳会按真实天文规律移动。StartDateTime格式是YYYY-MM-DD HH:MM:SSCelestialClockSpeed控制太阳移动速度的倍率比如设为 60则仿真中一小时的太阳位置变化相当于真实一天。这个功能对做视觉 SLAM 测试很有用因为光照角度变化会直接影响特征点提取稳定性。2.3 地图原点与坐标设置OriginGeopoint指定仿真的地理原点字段包含Latitude、Longitude、Altitude。AirSim 内部坐标是 NED北东地机体坐标系GPS 传感器会基于这个原点把内部坐标转换成经纬高。所以如果你的算法依赖真实 GPS 坐标务必把原点设置到你的测试场地附近否则 GPS 输出会和真实位置偏差很大。这个字段有个细节Altitude默认是 0单位是米。但你实际在 PX4 里看到的起飞高度是基于气压计的相对高度而不是这个绝对海拔。所以做 PX4 仿真时OriginGeopoint更多影响的是 EKF 融合里的地球参考系而不是你肉眼看到的气压高度。还有一个容易被忽略的顶层字段GlobalOrigin。它在虚幻引擎坐标系里定义世界原点OriginGeopoint是地理坐标两者必须匹配。1.3.1 中通常你只需要设置OriginGeopoint系统会自动映射。只有当你自定义了地图、改了虚幻引擎的世界偏移时才需要手动设置GlobalOrigin。我自己做自定义场景加载时踩过一次光改了地理原点忘了世界偏移结果 GPS 和视觉里程计的坐标系始终对不上最后检查日志才发现是 GlobalOrigin 的问题。3. 传感器配置给载具装上“眼睛和耳朵”3.1 相机参数详解相机配置可以在两个层级写顶层CameraDefaults对所有载具生效载具内部的Cameras只对单个载具生效。1.3.1 中CameraDefaults.CaptureSettings是一个数组每个元素对应一种图像渲染类型。ImageType是一个数字枚举常见值包括0场景Scene1深度平面DepthPlanar2深度透视DepthPerspective3分割Segmentation4表面法线SurfaceNormals5红外Infrared每个CaptureSettings里的关键参数有Width、Height、FOV_Degrees。如果你同时需要彩色图和深度图就要在CaptureSettings数组里写两个元素分别指定ImageType和分辨率。注意不同图像类型不能用同一条设置否则后写的会覆盖前面的输出。这里说一个实操中很容易弄错的地方FOV_Degrees是水平视场角不是垂直视场角。有些人在配置里直接照搬相机标定得到的垂直视场角结果渲染出来的画面明显“窄”了。水平视场角需要根据标定结果做换算FOV_horizontal 2 * atan(tan(FOV_vertical/2) * aspect_ratio)。我平时做双目相机仿真时就因为这个换算不对画出图像后左右目的视差完全不正常排查了很久才意识到问题。相机还可以通过X、Y、Z、Roll、Yaw、Pitch来设置相对载具的安装位置和姿态。这里有个重要细节坐标系是 NEDYaw顺时针为正。如果你把相机装在机头前方 0.5 米处配置是X: 0.5而不是像某些 ROS 工具里用-0.5。3.2 GPS、IMU、距离传感器与Lidar配置传感器配置统一放在Sensors块里。每个传感器有一个自定义名称例如Gps、Imu内部用SensorType数字来标识类型。1.3.1 核心的类型有SensorType类型说明1Barometer气压计2Imu惯性测量单元3Gps全球定位系统4Magnetometer磁力计5Distance距离传感器6Lidar激光雷达每个传感器至少有SensorType和Enabled两个字段。GPS 还有Eph水平误差和Epv垂直误差默认单位是米。做 GPS 拒止算法测试时可以把Eph调大模拟城市峡谷里的多径效应非常方便。IMU 的配置相对简单但在 1.3.1 中它支持Noise相关的参数我没有在官方默认配置里看到所以如果你想模拟噪声需要额外在Sensors块中加上Noise子字段。不过说实话AirSim 的 IMU 噪声模型比真实硬件要干净很多做高精度 VIO 评估时最好还是用真实传感器采集的数据离线回放或者对 IMU 输出加一层人为扰动。距离传感器Distance的参数主要是MinDistance和MaxDistance单位米还有DrawDebugPoints调试开关。它返回的是射线检出的最近距离适合做简单的避障测试。Lidar 的配置项比较多而且经常被问。核心参数有NumberOfChannels通道数常见 16、32、64。PointsPerSecond每秒点数决定了点云密度。HorizontalFOVStart、HorizontalFOVEnd水平扫描范围单位是度。VerticalFOVUpper、VerticalFOVLower垂直扫描范围。RotX、RotY、RotZ雷达安装姿态。X、Y、Z雷达安装位置。我实际测试过PointsPerSecond设置过大会导致 CPU 占用暴涨尤其是在 64 线雷达配高分辨率图像的时候。16 线雷达我一般设置在 100000 到 200000 之间64 线设置在 500000 以上才比较像真实雷达。注意这个雷达到底是“机械式旋转”还是“固态式”AirSim 默认实现是线性扫描的旋转雷达没有单独指定扫描模式的参数所以如果你的算法依赖某些固态式雷达模型建议在数据后处理里自行模拟。3.3 传感器位置姿态与多传感器协同每个传感器都支持X、Y、Z、Roll、Yaw、Pitch这些相对载具坐标系的位姿参数。这在多传感器融合项目里特别重要因为外参标定的正确与否直接决定融合效果。举个例子你有一个双目相机和一个 16 线激光雷达安装位置都在机头前方但高度不同。如下配置Sensors: { Lidar1: { SensorType: 6, Enabled: true, NumberOfChannels: 16, PointsPerSecond: 100000, X: 0.2, Y: 0, Z: -0.1, RotY: 0, RotZ: 0 } }, Cameras: { front_left: { CaptureSettings: [ { ImageType: 0, Width: 640, Height: 480, FOV_Degrees: 60 } ], X: 0.1, Y: -0.15, Z: -0.05, Yaw: 0, Pitch: 0, Roll: 0 } }如果你不写这些外参AirSim 会把传感器放在载具原点也就是机体质心位置。很多人在融合算法里发现点云和图像对应不上其实不是标定算法的问题而是仿真里面外参根本没设置。我强烈建议在项目开始前把每个传感器的外参列表打印出来和配置文件一一比对省得后面返工。4. 车辆与外部控制配置4.1 多旋翼与PX4连接配置Vehicles是顶层最重要的区块之一。在多旋翼模式下最常见的是PX4Multirotor和SimpleFlight。SimpleFlight是 AirSim 内置的一个简单飞控不需要额外启动任何软件适合快速跑视觉算法。它不包含真实飞控的复杂状态估计和控制逻辑所以飞行品质比较“理想化”。PX4Multirotor则通过 MAVLink 协议连接外部 PX4 飞控。1.3.1 版本里的关键配置是Vehicles: { PX4: { VehicleType: PX4Multirotor, UseSerial: false, UseTcp: true, TcpPort: 4560, ControlIp: 127.0.0.1, LocalIp: 127.0.0.1, ApiPort: 41451, ApiServerPort: 41451 } }UseSerial设为 true 时AirSim 通过串口与飞控通信配置项里要加SerialPort和BaudRate常用波特率是 115200。UseTcp设为 true 时AirSim 作为 TCP 服务器监听TcpPort。PX4 SITL 默认使用 UDP 14540 通信而 AirSim 1.3.1 里通常需要用mavlink_parameters配置转发这里我强烈建议去查 AirSim 官方 PX4 搭建文档因为它要求你本地跑一个 MAVLink 路由进程才能把 PX4 SITL 和 AirSim 联通。ApiPort和ApiServerPort是我见过最容易被搞混的一对。ApiPort是 AirSim 后端 RPC 服务监听的端口Python API 客户端client MultirotorClient(ip127.0.0.1, port41451)连的就是它。ApiServerPort是某些版本里用于启动外部服务的端口二者默认常常是同一个值 41451。如果你同时跑多个仿真实例就必须为每个实例分配不同的ApiPort和SimPort车辆间通信端口。另一个重要的顶层参数是LocalHostIp默认值127.0.0.1。当 AirSim 运行在一台机器、控制程序运行在另一台机器时必须把这个 IP 改成和控制端可达的网卡 IP。反之如果你想用局域网里的另一台机器作为驾驶舱来显示画面还需要设置ApiServerPort的监听地址。跨机部署时这组 IP 和端口配置就是头号故障源。4.2 汽车模式与载具物理属性汽车模式对应SimMode: Car默认VehicleType是PhysXCar。1.3.1 里你可以给车辆配置Vehicles: { Car1: { VehicleType: PhysXCar, DefaultVehicleState: Armed, X: 0, Y: 0, Z: 1, Yaw: 0 } }DefaultVehicleState可以设为Armed或Disarmed。如果设为Disarmed车辆不会响应油门输入这在你只做感知算法验证时比较安全不会因为误触油门导致碰撞。汽车速度控制方面AirSim 提供了MaxSpeed之类的物理参数吗准确说1.3.1 的 PhysXCar 在settings.json中支持在VehicleType同级的MaxVehicleCount、PawnPaths等外部配置但车辆物理性能参数如最大速度、发动机扭矩主要通过代码里的 CarConfig 默认值控制配置文件能改的并不多。做高精度车辆控制仿真时我更推荐用 AirSim 的 API 直接控制油门、刹车、方向盘而不是去折腾物理参数。一个常见的误解是“我在配置文件里设置了X、Y、Z车辆为什么没出现在我期望的位置”这个坐标在 AirSim 里是相对场景原点的。如果你的自定义地图原点不在你想着的位置车辆自然就跑到“地图没建模”的地方看起来像没设置成功。所以先确认地图原点再设置车辆坐标。4.3 API接口、遥控器与外部通信参数SubWindows是一个很有意思的配置块它允许你在仿真窗口里实时显示指定相机或传感器的画面。例如SubWindows: [ { WindowID: 0, CameraID: 0, ImageType: 3, Visible: true }, { WindowID: 1, CameraID: 1, ImageType: 0, Visible: true } ]ImageType0 是场景3 是分割。这个功能在做语义分割数据采集时非常实用左边窗口看原图右边窗口看分割标注直接能判断类别映射是否正确。不过 SubWindows 会增加渲染负载尤其是在 4K 分辨率下建议只在调试时开启正式采集时关掉。遥控器配置主要在RC块比如RC: { RemoteControlType: 1, JoystickIndex: 0 }RemoteControlType0 是无遥控器1 是 USB 遥控器通过 AirSim 内部映射2 是 MAVLink 遥控器由外部飞行控制器转发 RC 消息。在用真实遥控器操作 PX4 多旋翼仿真时用RemoteControlType2 比较合适这样按键映射和真实飞行更一致。但注意AirSim 自己支持的手机虚拟遥控器 App 在 1.3.1 里已经基本不怎么维护了不如直接用 QGroundControl 或 MAVProxy 来发 RC 指令。外部通信这块除了前面提到的ApiPort还有一个经常被忽略的RpcEnabled: true参数。如果你通过脚本连接 AirSim 却没有反应先检查这个参数是否被显式设置成 false。有人为了安全把 RPC 关掉后忘了开结果所有 Python API 调用都失败只报连接拒绝错误看起来像防火墙问题。5. 实操经验与问题排查5.1 我踩过的坑JSON格式与默认值覆盖先回答文章标题里的一个小问题AirSim 的默认配置文件叫settings.json不是setting.json。虽然只有一字之差但如果你真的建了一个setting.jsonAirSim 完全不会读所有配置都不生效。这个问题在百度、GitHub Issue 里反复出现我早期也栽过。JSON 格式这块最典型的坑有三个第一不能在 JSON 文件里写注释。很多人喜欢在参数后面加//说明但 AirSim 内置的 JSON 解析器不认注释遇到注释会直接跳过解析甚至忽略整个文件。如果你实在需要注释可以在配置文件旁边放一个settings_notes.md或者在仓库里维护一份带注释的模板我都是这么干的。第二不能有尾逗号。像下面这种写法非常常见{ SimMode: Multirotor, }最后一个键值对后面多了一个逗号严格 JSON 解析会报错。AirSim 的日志可能不会直接标红但你会发现某些配置没有生效。我的经验是每次改完配置先拿到任意 JSON 校验工具里过一遍哪怕只是在线校验也能省下大量排查时间。第三枚举值大小写必须准确。SimMode是Multirotor不是multirotorVehicleType是PX4Multirotor不是px4Multirotor。很多枚举值在错误时不会立刻报错只是被忽略然后你看到默认载具出现了还以为“多旋翼配置没写对”。解决方法是看 AirSim 启动日志第一部分的配置加载摘要它会列出最终生效的 SimMode 和 VehicleType。关于默认值覆盖还有一个血泪教训。AirSim 的配置解析是“先加载全部默认值再用用户 JSON 逐层覆盖”。这意味着你在Sensors里只定义了一个Gps那么其他传感器如 IMU、Barometer仍然会以默认配置存在。如果你故意想禁用某个默认传感器必须显式写Enabled: false。我遇到过有人在配置里只写了一个 Lidar但仿真里始终有 IMU 数据输出他没意识到那是默认配置自动加上的最后以为是代码 bug排查了两天。5.2 常见问题速查表我把实际使用中高频出现的问题整理成一张表方便你排查问题现象可能原因解决方法配置完全不生效文件名写错或路径不对确认是settings.json位于 Documents/AirSim 下启动后还是默认多旋翼SimMode拼写错误或大小写不对检查日志中加载的 SimMode 值传感器数据一直为 0传感器Enabled未设为 true显式设置Enabled: truePX4 连接不上UseTcp与UseSerial配置冲突二选一同时设置TcpPort或串口参数Python API 连接被拒ApiPort不一致或RpcEnabled: false确认设置RpcEnabled: trueAPI 端口一致图像分辨率不是预期值图像尺寸被默认值覆盖人为补全Width/Height并放到正确层级天气参数不生效当前场景不支持天气换成 Blocks 等室外场景检查Weather.Enabled地图坐标和 GPS 对不上OriginGeopoint与地图原点不一致统一设置OriginGeopoint和地图原点这些问题的共同点是AirSim 很少会因为你写错配置而直接崩溃它只是静默地使用默认值导致你看到的结果和预期不符。所以排查配置问题第一步永远是看启动日志。5.3 版本演进1.3.1相比旧版本的变化AirSim 1.3.1 和更早版本相比配置结构上有几个明显变化这里列出来供从老版本升级的读者参考。最大的变化是SettingsVersion从 1.0/1.1 升级到了 1.2Sensors结构正式化传感器不再只是顶层CameraDefaults里顺带一提而是有了独立的SensorType枚举和Sensors配置块。如果你是从 1.0 时代直接跳过来的会发现老的SimMode: Drone已经不复存在必须写Multirotor或具体的VehicleType。还有一个重要变化是Weather参数从只读试验状态变成了可配置。早期版本里天气只能通过 Python API 动态设置1.3.1 允许在settings.json里初始化和持久化天气类型。对我这种做感知算法的人来说这个改变很实用因为可以在不同种子场景间快速切换测试天气影响。另外OriginGeopoint的解析在 1.3.1 中改成了按“度”而不是按“度分秒”处理。有人拿旧的 GPS 坐标直接填发现自己写的是Latitude: 47 30 00这种字符串解析直接失败。1.3.1 里必须使用十进制小数例如47.5。这一点在官方文档里写得很隐蔽我在迁移配置时踩过一次。1.3.1 还新增了UsageScenario和PawnPaths这类偏底层的配置项。UsageScenario用于指定载具的用途比如UsageScenario: 0对应不指定1对应无人机配送之类的场景它会影响仿真的默认物理参数。PawnPaths用来引用自制的骨骼网格体Pawn路径如果你导入过自定义飞机或汽车模型就会用到它。这两个字段不是每次都需要但在定制化项目中是绕不开的。最后1.3.1 对CameraDefaults和Vehicles的优先级做了更清晰的区分。当顶层和车辆内部同时定义了Cameras时车辆内部的配置会覆盖顶层同名相机的设置。这个逻辑在旧版本里比较混乱新版修正后反而更容易理解但也意味着如果你在顶层配置了相机又在车辆内部重新定义同名字段顶层的某些参数会被覆盖成默认值而不是进行合并。结尾关于AirSim配置的一点个人心得settings.json看似只是一个 JSON 文件实际上它决定了你整个仿真实验的“公平性”。我在做传感器融合算法对比实验时会把不同算法的测试固定在同一份配置文件上确保所有传感器外参、噪声水平、场景初始条件完全一致。这种做法不仅让对比结果更可信也让别的研究者可以精确复现我的实验。如果你现在正在为 AirSim 的配置焦头烂额我的建议是不要把配置当成一次性工作。维护一份自己的配置模板里面写清每个参数的作用和默认值每次升级 AirSim 版本时花 10 分钟 diff 一下旧配置和新文档的差异能帮你规避掉大多数坑。最后分享一个小技巧AirSim 的日志文件默认写在Documents/AirSim/下文件名为airsim.log。当配置看起来没生效或者传感器数据异常时别急着改代码先打开这个日志搜settings或者Sensor关键词很多时候一眼就能看出问题出在哪个字段上。这个习惯我养成了很久实际排查效率提高得不是一点半点。