基于AirSim的VTOL仿真扩展:vtol-AirLib如何搞定过渡段控制
简介VTOL-AirLib是基于AirSim的AirLib库扩展而来的C工程面向无人机与eVTOL飞行器开发者用于补全垂直起降VTOL机型的仿真与控制支持。它针对倾转旋翼机等构型提供了专门的飞行模型与控制算法可结合Unreal Engine搭建高保真虚拟场景完成控制逻辑验证、性能分析及安全性评估。压缩包共224个文件以205个hpp头文件与13个cpp源文件为主体涵盖TiltrotorApiBase、多旋翼RPC通信及安全评估等核心模块整体仅486KB结构紧凑。已有265人学习下载。借助该源码开发者可深入理解VTOL与倾转旋翼机的仿真架构复用其RPC接口和飞行控制框架在AirSim生态中快速开展二次开发与算法迭代。1. 从能垂直起降到过渡段也稳vtol-AirLib 给 AirSim 的 AirLib 补了什么做 VTOL 控制的人大多有一个共同感受多旋翼和固定翼的仿真各自都很成熟但一到过渡飞行段模型要么生硬切换要么直接拿多旋翼参数硬跑。vtol-AirLib 是在 AirSim 的 AirLib 基础上 fork 出来的 C 分支它在原版飞行器仿真库上专门扩展了针对 VTOL 飞机的附加功能重点解决垂直起降、倾转过渡和混合推力这三个原版处理得比较薄弱的环节。如果你手头有 eVTOL 或无人机仿真需求既想保住 AirLib 现成的传感器仿真和物理引擎又想能改 VTOL 的动力学模型与控制切换逻辑这个 fork 值得花半天时间拆一遍。它适合三类人做 eVTOL 控制律验证的工程师、搭无人机仿真环境的研究生、想在真实机架落地前先把控制逻辑跑通的硬件开发者。2. 为什么 VTOL 不能直接套多旋翼模型从混控抽象到切换逻辑2.1 三种 VTOL 构型在仿真模型上的本质差异很多从多旋翼转过来的开发者拿到 VTOL 任务的第一反应是我只要把两个多余电机加上去就行。这个理解在实物上错在仿真里同样错。VTOL 飞机不是多旋翼加固定翼机翼的简单叠加它的动力学本质是两套气动模型在同一个刚体上同时起作用只是权重随飞行状态变化。常见的 VTOL 构型可以分三类看。尾座式tail-sitter整机在起飞和巡航之间要做 90 度姿态翻转仿真里涉及的不只是推力分配还有体轴系与世界坐标系的重新对齐倾转旋翼tilt-rotor的旋翼和短舱相对机身转动每个时刻的推力和力矩方向都在变混控矩阵在仿真中需要按短舱倾转角实时重构升力加巡航复合式liftcruise相对简单垂直升力单元和水平推进单元完全独立但两套动力系统的响应时间和力效应在过渡段必须交叉重叠。AirLib 原版的多旋翼模型走的是电机转速指令 → 单个电机推力 → 叠加成总力/力矩这条路控制器输出被假设为转速指令模型内部用二次曲线把转速映射成推力。这条链路在四轴或六轴上没问题因为所有电机的作用方向始终垂直于机体平面。但 VTOL 里垂直升力单元需要回落巡航单元需要接力机翼产生的气动力随空速非线性上升。如果只是并行塞几个电机模型没有处理这些方向变化结果自然不对。这个 fork 的改法是把 VTOL 抽象成垂直力单元 巡航推进单元 气动舵面三层混控结构。垂直力单元沿用多旋翼的转速-推力模型巡航推进单元走固定翼的推力-空速曲线气动舵面单独查表。这样每种构型只需要在配置里声明它的单元组合和几何位置仿真内核统一处理力的叠加而不需要为每种飞机单独写一套动力学。2.2 状态估计与坐标系变换切换瞬间的跳变问题VTOL 仿真第二个容易被忽略的坎是姿态控制器的参考坐标系。多旋翼控制里期望姿态通常是四元数控制目标始终锁定在机体轴上固定翼控制里期望姿态虽然也表达成欧拉角但内侧回路关心的是迎角、侧滑角和航迹方向。这两套坐标系在过渡段同时有效切换做得不好最典型的现象就是飞机在切换瞬间出现剧烈的角速度跳变。我见过不少团队在实物上没出问题、一进仿真就炸原因就在这原版 AirSim 做机型切换时用的是分段线性切换比如空速小于某个值用多旋翼控制器大于某个值用固定翼控制器中间区域两个控制器输出直接加权。这个方案的隐患在于多旋翼控制器的输出是机体系下的角速度指令固定翼控制器的输出是气动系下的舵面偏转两者加权之后角速度指令和舵面偏转混在一起物理意义不完整。vtol-AirLib 的常见做法是在里面加了一个显式的过渡状态机而不是简单做控制输出加权。状态机里定义了四个阶段Hover、Transition、Forward、ReverseTransition。正向过渡时垂直力单元的输出按空速斜坡逐渐减小巡航单元的推力同步增加反向过渡相反。关键是切换条件同时要求空速和垂直速度速率超出阈值两个条件都要满足避免飞机在半空中来回切状态。仿真内部过渡段使用一个混叠系数把垂直力单元的拉力指令映射到世界坐标系的垂直轴再把巡航推力映射到机体前向轴两个分量通过空速调节叠加比例。这样姿态控制器端看到的是一个连续的力指令而不是两套控制器的拼接。坐标系变换还有一个容易踩的细节空速。原版 AirLib 里没有严格区分地速和空速简化处理时甚至直接拿地速替代。VTOL 过渡段恰恰对空速敏感因为机翼升力完全取决于空速而地速包括了风的影响。这个 fork 在状态更新里引入了风场向量把空速计算从机体速度模长改成了机体速度减去风速后的投影长度。这个改动直接影响过渡条件和气动系数查表调参数时要额外注意。2.3 选型对比原版、临时硬改、这个 fork 到底差在哪团队在选型时最常问的问题是我们直接用原版 AirSim再在自己的 C 代码里加一个 VTOL 模型行不行能行但要付出的工作量比想象中大。直接硬改的难点在于 AirLib 的多旋rotor 类与固定翼 airframe 类是两套独立代码中间没有预留混合接口硬加逻辑需要在每帧更新里手动合成力和力矩并且姿态控制器、状态估计器都要跟着改。我把三种方案的差异放在一个表里对比看对比维度原版 AirSim 默认自研临时硬改vtol-AirLib fork垂直/巡航双动力叠加不支持机型互斥自己写混控内置三层混控过渡段状态管理简单阈值切换状态机自研四阶段状态机空速与风速模型无风速用地速近似按需接入内置风场向量舵面与气动系数固定翼专用需独立建模随构型查表控制器切换切换瞬间跳变自己处理斜坡力指令连续过渡二次开发成本高改动侵入大高代码维护难低改配置为主从表里能看出来原版的价值在于传感器仿真和场景渲染VTOL 空气动力学本身不是它优化的重点。硬改能解决一两个具体问题但要同时处理混控、控制器切换、坐标系对齐开发周期至少一个月起步。这个 fork 的价值是把这些工程细节先实现了开发者只需要关注构型参数和控制律本身。如果你是第一次接触 VTOL 仿真我建议直接选 fork把省下来的时间花在理解过渡逻辑上而不是重新发明轮子。3. 编译与接入从拉代码到 VTOL 在仿真里跑起来3.1 依赖准备与 CMake 构建先说编译环境。AirLib 本身是跨平台的 C 库但这个 fork 的改动集中在动力学模拟部分编译方式和原版保持了一致依赖项不多CMake 3.15 以上、支持 C17 的编译器以及完整的 AirSim 代码仓库。注意fork 里引用了一些原版的头文件比如传感器、图像接口和物理引擎部分所以光拿 fork 自身不够需要把上游仓库一起放到工作区里。我一般这样组织目录结构workspace/ ├── AirSim/ # 上游原版仓库 └── vtol-AirLib/ # fork 的 AirLib 副本然后进入 fork 目录执行构建。常见做法是 fork 里带了一个 CMakeLists.txt用下面的命令配置并编译cd vtol-AirLib mkdir build cd build cmake .. -DCMAKE_BUILD_TYPERelease \ -DAIRSIM_SOURCE_DIR../../AirSim make -j$(nproc)这里AIRSIM_SOURCE_DIR是指向上游仓库的路径fork 编译时要从那里引用未改动的头文件。CMAKE_BUILD_TYPE我建议直接设 Release因为 VTOL 的动力学更新在过渡段每一帧要做多次状态插值Debug 下性能差距很明显。编译产物是一个libAirLib.a静态库后面你把自己的 C 控制程序链接上这个库就能用。如果编译过程中报找不到vehicles相关的头文件基本就是AIRSIM_SOURCE_DIR路径没指对。这个参数原版没有是 fork 后加的构建日志里会有明确提示。还有一步容易被忽略fork 里有几个新增的源文件比如 VTOL 混控器和过渡状态机的实现如果原来的编译脚本是按文件名索引的需要确认没有把新文件漏掉否则链接阶段会报一堆未定义符号。3.2 把 VTOL 参数写进 settings 文件编译只是第一步把飞机配出来才是接入核心。AirSim 的车辆配置主要在settings.json里这个 fork 在原本的车辆类型基础上增加了一个Vtol类型参数结构大致这样{ SettingsVersion: 1.2, Vehicles: { Vtol1: { VehicleType: Vtol, VtolConfig: { ModelType: LiftCruise, TransitionAirSpeed: 18.5, TransitionRate: 0.25, MaxRotorRPM: 22000, MaxCruiseThrust_N: 450, ControlMode: AutoTransition, WheelFriction: 0.8 } } } }ModelType是构型选择这里写LiftCruise对应升力加巡航复合式如果你用尾座式或倾转旋翼就换对应的枚举值。TransitionAirSpeed是过渡段的目标空速单位米每秒这个值决定了飞机会在什么速度下完成垂直升力到巡航推力的切换偏小会导致升力交接不充分偏大又会让垂直单元过长时间工作。TransitionRate是过渡执行的速率系数可以理解成混叠系数从 1 变到 0 的时间尺度0.25 意味着大约 4 秒完成过渡实际调试时我一般从 0.2 到 0.5 之间扫一遍。MaxRotorRPM和MaxCruiseThrust_N分别限制垂直单元和巡航单元的出力上限数值要和你参考的真实机型对应不要凭空填。ControlMode有AutoTransition和ManualTransition两种。前者由状态机根据空速自动触发适合控制律验证后者把过渡权交给外部程序适合测试急停、跃升这类边界工况。我建议前期用AutoTransition先把飞机跑稳再切到手动模式去压边界。WheelFriction是起降阶段的摩擦系数地面滑跑验证时很关键这一项容易被人忽略后文避坑部分我会单独讲。3.3 最小 C 例程拉起飞机并读取过渡状态配置写好后用一段最小 C 程序验证链路。以 AirLib 提供的 API 风格为参考代码大概长这样#include vehicles/vtol/VtolApi.hpp #include memory int main() { auto vtol std::make_sharedAirLib::VtolApi(Vtol1); // 上电并切换到自动模式让飞机自主进入过渡状态机 vtol-setControlMode(AirLib::VtolControlMode::Auto); vtol-arm(); // 给垂直推力指令让飞机先爬升到 10 米高度 vtol-setVerticalThrust(0.65f); // 每 100ms 读取一次当前空速和过渡阶段 while (true) { float airspeed vtol-getTrueAirspeed(); int phase static_castint(vtol-getTransitionPhase()); // phase: 0Hover 1Transition 2Forward 3ReverseTransition if (phase 1 phase 3) { vtol-setForwardThrust(0.35f); } std::this_thread::sleep_for(std::chrono::milliseconds(100)); } }这段代码的核心逻辑是先切到自动模式并解锁给垂直推力爬升然后周期读取空速和过渡阶段一旦发现处于过渡段补充巡航推力。getTrueAirspeed()返回的就是 2.2 节里提到的考虑了风速的真空速而不是地速的简单模长。getTransitionPhase()返回当前状态机的阶段编号这是 fork 新增的 API原版没有。实际调试时我习惯把这两个值打印到文件里而不是只在终端显示因为过渡段往往只有几秒终端刷新太快看不出趋势。等这条链路跑通就说明 fork 已经能正常控制并读取状态可以往控制器方向深入了。4. 核心仿真模块与参数调校电机、舵面、过渡点4.1 动力模型与舵面映射表VTOL 仿真里最容易被新手搞混的是把垂直升力单元和巡航单元简化成同一个模型。AirLib 原版里多旋翼的电机的推力模型非常简洁推力与转速平方成正比力矩反作用力与转速平方也成正比混控矩阵再做力到电机分配的逆运算。这个模型对纯旋翼机完全够用但 VTOL 的垂直单元往往是小直径高转速的螺旋桨巡航单元则是大直径低转速甚至涵道风扇两者在推力-转速曲线、响应延迟和效率曲线上的差异不可忽略。在 fork 里垂直升力单元和巡航推进单元分别用不同参数描述。垂直单元保留转速-推力二次关系但加入了一个最大升力限制模拟电机在大油门下的退磁或桨叶失速巡航单元改用推力-空速特性表因为固定翼状态下的推进效率随空速变化明显低速时效率低、高速时效率高简单二次曲线拟合不了真实趋势。舵面方面需要把方向舵、升降舵、副翼三个通道与机体轴正确对应。复合式 VTOL 在悬停阶段舵面基本不动进入巡航后舵面才逐渐介入所以映射表里要写清楚每个舵面的气动力臂和作用轴。我列出典型的参数映射作为参考参数项典型值范围作用说明RotorAxes4 组垂直方向向量定义垂直升力单元的位置与推力方向ControlSurfaceDeflectLimit25°~35°舵面最大偏转角LiftCruiseBlendMinVel12~20 m/s混叠系数开始下降的空速LiftCruiseBlendMaxVel22~30 m/s混叠系数完全为零的空速SinkRateLimit1.5~3.0 m/s过渡段允许的最大垂向速度PitchTrimAngle-2°~2°巡航配平俯仰角补偿我最常调的其实是PitchTrimAngle。复合式 VTOL 在固定翼巡航时由于机翼安装角和机体水平参考的关系飞机通常需要一个小的俯仰配平否则控制器为了维持平飞会一直输出一个偏置舵量。调试时先跑定高巡航让飞机增稳飞行 20 秒记录稳态俯仰角再做一次带配平的飞行对比舵面输出是否更接近中位这是验证配平是否正确的最直观方式。4.2 过渡段逻辑不只是一个切换开关做 VTOL 仿真的人对过渡段的态度往往两极分化要么完全忽略要么过度复杂。忽略的人直接把垂直推力降到零、巡航推力加上去结果飞机掉高度过度复杂的人写了一个包含十几个状态的切换逻辑结果参数多到根本调不过来。fork 里的过渡状态机是一个适中的方案值得仔细拆解。它把过渡过程建模成四阶段状态机正向过渡从 Hover 开始垂直单元推力开始下降巡航单元推力同步上升进入 Transition 阶段后空速逐渐增加机翼开始产生升力当空速超过某个阀值状态切到 Forward 阶段此时垂直单元完全关闭。反向过渡是反过来先减小巡航推力、增加垂直单元推力然后逐渐把速度降下来最后回到 Hover。整个过程中混叠系数blendFactor是核心变量它的物理含义是当前状态下垂直单元承担的升力占比。常见的做法是把blendFactor定义成空速的分段线性函数float computeBlendFactor(float airspeed) { float blend 1.0f; if (airspeed kBlendMinVel) { blend 1.0f; // 低速完全由垂直单元提供升力 } else if (airspeed kBlendMaxVel) { blend 0.0f; // 高速完全由机翼提供升力 } else { blend 1.0f - (airspeed - kBlendMinVel) / (kBlendMaxVel - kBlendMinVel); } return clamp(blend, 0.0f, 1.0f); }这段代码做的事很简单空速低于kBlendMinVel时升力完全来自垂直单元高于kBlendMaxVel时升力完全由机翼产生中间这一段按线性比例过渡。参数上两个速度点的差值决定了过渡的渐进程度。差值小过渡更快但也更容易出现力矩扰动差值大过渡更柔和但垂直单元工作时间变长耗能增加。我一般会把差值控制在 8~12 m/s 之间。调节时如果发现过渡段有掉高度先看SinkRateLimit是不是太小再看kBlendMinVel是否需要下调。掉高度的本质是垂直单元收力过快而机翼升力还没建立起来此时应该加宽过渡空速带而不是盲目加大巡航推力。4.3 控制器接口差异从四轴控制到模式间切换AirLib 原版的多旋翼控制器接口输入是姿态期望和油门指令输出是电机转速。固定翼控制器不同输入是目标空速、高度和航向输出是油门、升降舵、副翼、方向舵的偏转量。VTOL 因为有两套执行机构控制器接口必须同时涵盖这两类输出。fork 的做法是把控制器输出抽象成一个统一的执行指令结构里面同时包含垂直单元推力、巡航单元推力、以及三个舵面的期望偏转角。至于每类执行机构用多少由状态机根据当前阶段决定。这样上层控制律不需要关心自己是在四轴模式还是固定翼模式它只需要输出一组完整指令由混控层决定哪些指令被真正执行。这个设计最大的好处是控制器的切换不再是代码级别的跳转而是参数级别的渐变。比如在悬停阶段舵面指令直接忽略在巡航阶段垂直单元的推力指令被置零。控制器本身不需要维护任何模式状态状态的维度和模式切换的平滑性完全由混控层负责。写控制律的人只需要保证输出指令的连续性而这也恰好是现实中飞控的要求指令突变就是给执行机构送冲击。5. 避坑与排查VTOL 仿真跑起来之后的五个常见问题5.1 现象悬停时垂直方向持续抖振并发散悬停阶段飞机在垂直方向出现周期性震荡振幅随仿真时间逐渐增大最终出现发散。这通常不是控制参数的问题而是混控矩阵里垂直升力单元和巡航推进单元的力的方向没有统一到同一个坐标系。原因复合式 VTOL 的两类动力单元垂直单元方向沿机体轴垂直向下巡航单元沿机体轴前向水平。如果配置时没有把巡航单元的推力考虑进混控矩阵控制器会给垂直单元额外的修正量来抵消巡航推力带来的低头力矩这就会在垂直通道上引入一个持续的振荡激励。解决打开 fork 里的VtolMixer相关代码检查巡航推力是否加入了力的合成。常见做法是给巡航单元单独配置一个推力作用点让它的推力线经过重心或产生一个可以前馈补偿的力矩。确认混控矩阵里每个推力向量的归一化和作用点坐标都正确然后再动控制参数。5.2 现象固定翼巡航时副翼方向反效飞机在巡航状态下给定一个滚转指令飞机先朝期望方向滚随后立刻翻滚到另一侧。或者在扰动下自动增稳系统把扰动越增越大。原因舵面偏转方向的定义在 VTOL 构型里和固定翼默认约定相反。原版固定翼的气动模型里副翼正偏转对应左侧上偏、右侧下偏但 VTOL 的舵面可能挂在机翼后缘且机翼相对机身的安装角不一样导致符号约定反了。解决检查控制舵面的安装方向配置项把副翼的符号取反。这个问题的典型特征是小扰动下飞机横滚发散和控制器参数无关。排查时先冻结控制器手动给定一个固定舵偏角看飞机的滚转角速度和舵偏方向是否一致。5.3 现象过渡段收油门后掉高度进入过渡状态后垂直单元推力按斜坡下降巡航推力虽然同步增加但飞机还是在过渡段后半段掉了几米高度严重时触地。原因最常见的诱因是kBlendMinVel设置过高飞机在空速还没建立足够升力时就开始收垂直推力其次是过渡期间俯仰角变化导致垂直推力在垂直方向的分量减小。解决先把过渡空速带拉宽比如kBlendMinVel降到 14 m/s、kBlendMaxVel提到 26 m/s让垂直单元多工作一段时间。然后把过渡段的俯仰角限制放宽到 8°~10°避免姿态限制掐掉升力分量。如果还是掉高度看巡航推力的响应是否滞后确认推力曲线在低速区间有足够的出力余量。5.4 现象编译时头文件路径指向原版 AirLib这个坑跟控制无关但几乎每个第一次编译这个 fork 的人都会遇到。明明已经在 CMake 里指定了AIRSIM_SOURCE_DIR编译时还是用了原版 AirLib 的vehicles目录下的头文件导致 VTOL 相关的类全部找不到定义。原因上游原版仓库和 fork 的目录名如果相同编译器默认的 include 顺序可能优先搜到原版路径。尤其当两个仓库被分别放在不同目录、且原版路径被加在系统 include 路径里时#include vehicles/vtol/...会先从原版路径里找同名文件找不到 VTOL 头文件最后实际 include 到的是原版的旧版本。解决在 CMakeLists 里把 fork 的include目录显式放在原版之前同时检查环境变量里有没有额外的 include 路径。最笨也最有效的办法是编译时的-H参数打印头文件搜索路径看到底命中了哪个目录然后对着改。5.5 现象起飞滑跑距离明显偏短或偏长在固定翼模式下起飞飞机在跑道上滑跑的距离和预设值差得离谱要么几乎原地抬头就离地要么加速半天拉不起来。原因WheelFriction参数对滑跑影响极大但这个参数默认值通常是按理想沥青跑道设的。如果地效参数也被启用离地瞬间的升力突变会被放大。还有一点飞机的初始俯仰角如果偏大小速度下机翼就进入了高升力区飞机过早离地。解决先确认WheelFriction在你仿真的跑道场景里是否合理再检查PitchTrimAngle是否是巡航配平值而不是起飞配平值。做起降验证前我一般会先用空载跑一次定速滑行看速度曲线和理论值对不对得上再决定要不要调整机轮摩擦模型。6. 验证技巧用录制回放把过渡段调稳调试 VTOL 过渡段最怕的是什么不是参数不对而是每次改参数都要重新起飞一次在同一个飞行剖面里对比前后两次的效果。我习惯的做法是给仿真加一层动作录制与回放让问题可复现。具体做法是在代码里加一个录制线程按照固定的时间戳记录飞行的关键状态包括时间戳、三轴位置、三轴速度、姿态四元数、真空气速、当前blendFactor、垂直通道推力指令、巡航推力指令。以 50 Hz 的采样率记录 60 秒飞行数据量非常小CSV 文件也就几 MB。改参数后再次执行完全相同的控制脚本用 Python 把两次记录的过渡段曲线叠画出来看差别。import pandas as pd import matplotlib.pyplot as plt df1 pd.read_csv(run_01.csv) # 基准参数 df2 pd.read_csv(run_02.csv) # 调整后的参数 plt.figure(figsize(10, 5)) plt.plot(df1[t], df1[alt], labelbaseline alt) plt.plot(df2[t], df2[alt], labeltuned alt) plt.axvspan(df1[t].iloc[0], df1[t].iloc[-1], alpha0.05) plt.xlabel(time (s)) plt.ylabel(altitude (m)) plt.legend() plt.show()回放时要重点看三条曲线高度的谷底值出现在哪个阶段blendFactor掉到 0.5 时的高度是不是还在安全线上以及垂直通道有没有出现输出饱和。我一般给自己定三条接受标准过渡段的最小高度损失不超过 1.5 米姿态角变化率不超过每秒 20 度油门指令不出现超过 0.3 秒的饱和段。三条同时满足才认为过渡段调稳了。这个习惯是在一次调试倾转旋翼构型时养成的。那次连续调了十几组参数每次都觉得这次应该对了结果起飞后状态都不一样后来才发现前几次根本没有触发完整过渡数据样本是坏的。从那以后我每次改参数都强制走一遍完整录制回放流程先看blendFactor和时间的关系是不是预期形状再看具体曲线。这套方法虽然繁琐但对 VTOL 这种多状态强耦合的系统来说是唯一能让你逐步逼近稳定解的办法。希望帮到你。本文还有配套的精品资源点击获取