架构:内核与数据流
CRSF 数据流如何化为扑翼与尾翼:模块边界、两个混控内核,以及在链路断开时让翼龙存活的有限状态机。
信号通路
PteronautOS 是 ExpressLRS 4.x 的一个分支,它将接收机的「PWM 直通」末端替换为专用的飞行控制层。数据路径被刻意设计得很短:
CRSF radio link
│ 2.4 GHz SX1280
▼
rx_main / CRSF decoder → ChannelData[] (raw 172…1811 µs-window values)
▼
Ornithopter::update() → _readChannels() → voice* fields
▼ kernel select: activeProfile ≥ GEARBOX_2VTAIL_1RUD ?
_computeServoMixer() OR _computeGearboxMixer()
▼
_f[] (indexed by ServoFunc tag, µs)
▼
PWM output stage → funcMap channels → wing / rudder / motor servos
四个结构性思路让这台野兽保持稳定、代码保持精简:
- 通道尽早归一化。172–1811 CRSF 窗口内的每个原始值都会一次性映射到 −1…+1(
_crsfToNorm)或百分比,只有该单位流经混控器——不再重复魔法常量。 -
输出是打标签的,而非固定引脚。混控器写入
_f[ServoFunc]。某个标签驱动哪个物理 PWM 输出由配置文件的funcMap稍后决定——参见 混控器参考 .重新分配模型引脚绝不会触及混控器的数学运算。 - 内核是编译期家族、运行期选择。波形扑翼与变速箱控制器都存在于二进制中;活动配置文件在每个更新节拍选择内核。
- 失控保护不是事件,而是状态。链路丢失、台架模式与摇杆覆盖是
update()中的显式闸门,因此无线电静默时伺服器绝不会失控。
两大内核:波形与齿轮箱
内核是将归一化通道转换为伺服函数 µs 的模块。PteronautOS 有两个族,由混控配置文件编号区分:
| 族 | 配置文件 | 执行器 | 机型原型 |
|---|---|---|---|
| 波形(伺服)内核 | 0 SERVO_2WING · 1 SERVO_2WING_1RUD · 2 SERVO_4WING | 扑翼伺服器(2–4)、冠舵 | 直驱伺服扑翼机——经典的 PteronautOS |
| 变速箱内核 | 3 GEARBOX_2VTAIL_1RUD · 4 GEARBOX_1MOT_2VTAIL · 5 GEARBOX_1MOT_2VTAIL_1RUD · 6 GEARBOX_1ELE_1RUD · 7 GEARBOX_1MOT_1ELE_1RUD | 电机电调、V 尾或升降舵 + 方向舵 | 带常规尾翼的齿轮/电机扑翼机 |
选择发生在 update() 中,通过单一测试 PROFILE_IS_GEARBOX(activeProfile ≥ 3)。配置文件本身是运行时值:它始于构建标志 MIXER_PROFILE(旧式 ORNITHOPTER_GEARBOX=1 仍映射到配置文件 5),但可通过 setOrnithopterProfile() 从 WebUI 实时切换——固件无需重新烧录即可瞄准新机架。共有八个配置文件;其精确的 funcMap 布局已列于混控器参考中。
波形内核(舵机扑翼)
波形内核是大多数人所说的「PteronautOS」:两个机翼伺服器运行相位连续的振荡,其形状在每个周期被雕琢。其构造:
- 带滞回的扑翼闸门。油门必须越过扑翼阈值(默认原始值 ≈303,≈1.08 ms PWM 等值)才能进入扑翼;只有低于阈值 − 50 滞回才退出,因此边缘油门绝不会让机翼抖动。
- 频率。FlappingOscillator 随真实经过时间推进(µs 时钟,dt 上限 100 ms)。节拍目标通过配置文件的
throttleFrequencyMix混合 CH6 扑翼频率通道与油门,范围 0.5 Hz…flapBaseFreq/10。 - 振幅。油门占伺服速度上限的百分比
ampMax = degPerSec / (2·f)(其中degPerSec = 60°/servoSpeed_ms),乘前硬性上限 55°。因此振幅在高频下自然坍缩——机翼从不要求超出伺服器所能提供的量。 - 凶猛度。下冲程(动力)与上冲程(回复)的凶猛度按冲程塑造,由升降舵非对称调制(拉起强化下冲程,推杆强化上冲程)并叠加油门耦合,然后在形状函数前限定于 0…8。
- 偏航差动。方向舵加入左右相反的凶猛度(
rudderFerocityRange)与左右相反的振幅(rudderAmplitudeDifferential),而冲程反转相位在两翼之间保持共享——转向权来自非对称停留,而非失步的机翼。 - 几何与输出。塑造后的 −1…+1 波形被缩放为角度,与副翼/升降舵操纵(±60° 权限 × 比例)混合,由滑翔/扑翼中心微调偏移,钳制于 0…180°,再通过可配置的
servoMinUs…servoMaxUs包络(默认 988…2012)按函数微调转换为 µs。 - 滑翔分支。低于扑翼阈值时振荡器衰减,机翼保持静态滑翔角——台架与发射的理想状态。
齿轮箱内核(电机+尾翼)
变速箱内核以电机驱动系加常规尾翼换取纯扑翼。由于电机连续旋转,该内核每个节拍无状态——无振荡器——取而代之:
- 将油门(已武装)映射为 1000…2000 µs 电调命令(
SF_MOTOR),解除武装时空转; - 混合尾翼:V 尾配置文件使用升降副翼混合(副翼+升降舵 / 副翼−升降舵),常规配置文件使用独立的升降舵面;
- 让 Zephyrus(启用时)将横滚与俯仰 PID修正注入尾翼伺服器,钳制于 ±250 µs(
ZEPHYR_GEARBOX_CLAMP_US),使传感器毛刺永远无法使伺服器停转; - 保持与波形内核相同的方向舵混控器及 µs/微调/钳制包络。
一架扑翼机甚至可以同时携带两种性格:以波形内核扑翼起飞,以变速箱巡航——运行时配置文件切换就是 update() 中的那一行。
状态:链路、台架与失控保护
update() 以固定约 10 ms 节拍从 WiFi/伺服循环调用,并在任何混控器运行前通过三个标志进行闸控:
| 状态 | 条件 | 行为 |
|---|---|---|
| 正常飞行 | linkUp + 武装通道在线 | 完整内核输出跟随摇杆 |
| 台架 / 面板模式 | benchMode(WebUI 面板,无 RC 链路) |
注入中立摇杆,油门停在扑翼阈值之下——滑翔 + 微调编辑实时可见 |
| 摇杆覆盖 | stickOverride(虚拟通道) |
固件注入的通道值完全取代 CRSF(伺服扫掠、测试) |
| 失控保护 | 链路丢失(onLinkDown / enterFailsafe) |
所有函数居中到 1500 µs,振荡器复位,电机 → 最小;CRSF 丢包也在 RX 层居中 |
丢失时居中的行为被有意地重复于两层:CRSF 栈对通道数据做失控保护,而 Ornithopter 层对几何(含振荡状态)做失控保护。因此无线电死亡时留下的是可滑翔、可操纵的机架——绝非狂乱扑腾的机架。
源码地图
| 模块 | 路径 | 职责 |
|---|---|---|
| Ornithopter 层 |
src/lib/Ornithopter/
|
内核选择、混控数学、波形振荡器、配置文件、微调 |
| 飞行配置文件与常量 |
src/lib/Ornithopter/OrnithopterConfig.h
|
MixerProfile 枚举、ServoFunc 标签、funcMap、所有数值默认值 |
| 振荡器 / 形状 |
src/lib/Ornithopter/OrnithopterWaveform.h
|
相位积分、凶猛度塑造、反转逻辑 |
| Zephyrus(可选) |
src/lib/Zephyrus/
|
MPU6050 + Mahony AHRS、双 PID、冠/尾翼修正 |
| PWM 输出级 |
src/lib/ServoOutput/devServoOutput.cpp
|
与 CRSF 节拍同步写入 µs |
| WebUI 配置 |
src/lib/WIFI/devWIFI.cpp
|
运行时配置文件 + 调校持久化(/pteronautos) |
再下一层——数字究竟产生何种波形,以及每个配置文件如何将函数接线到 PWM 输出——位于 混控器参考 ,而伺服时序包络记录于 伺服内核 一文。