Hyperframes 这个词我第一次看到的时候,下意识以为是某个影视后期术语。后来钻到 ArduPilot 的开发群里,才搞明白它指的是什么:一套专门处理“任意几何多旋翼机架”的飞控方案与工具链。简单说,传统多旋翼飞控只认几种预设构型,X 形、加字形、六轴、八轴,你只能在菜单位里挑。而 Hyperframes 的思路是把你机架上每个电机的位置、朝向、旋向直接告诉飞控,剩下的事情交给算法去算。
这套东西能解决一个很实际的痛点:异形机架、非对称布局、倾斜电机、环形结构,这些“看起来能飞”的机器,过去往往要自己啃混控代码,或者反复试错。现在可以直接把几何参数变成一组可控的混控参数,普通用户也能在不过度改动固件的情况下把一架“野路子”机架调稳。适合人群很清楚:硬件玩家、飞控二次开发者、搞赛道无人机结构设计的人,以及想理解多旋翼底层控制分配算法的朋友。这篇就把原理、参数计算、实际配置和我会踩的坑一次性说透。
1. Hyperframes 解决的是什么问题
1.1 传统多旋翼构型预设的局限
玩过 Mission Planner 或者 QGroundControl 的人应该都有印象:刷完 ArduPilot 固件,第一步就是去设置 Frame Class 和 Frame Type。四旋翼要选 X 还是 +,六旋翼要选哪个方向的布局,八旋翼还要分清是不是同轴共桨。选好之后飞控就知道“给每个电机分配多少油门、多少横滚修正量”。这套机制在标准机架上非常好用,飞控固件里预先存了一套混控表。
但问题是,这套混控表有一堆隐含假设。大多数预设构型默认电机到重心的距离完全相等,默认电机全部垂直安装,默认推力方向都沿机体 Z 轴,默认每个电机的最大能力一致。一旦你的机架不满足其中一个条件,预置参数就开始别扭了。比如常见的 FPV 穿越机,为了减小阻力或者避开镜头视角,经常把前臂做得短一点、后臂长一点,或者干脆做成环形的圈机。再比如载重无人机,用户为了避开挂载点,会把电池往一侧偏,导致重心偏移。
这时候你再选“X 型四旋翼”,飞控的数学模型和实际物理完全不匹配。最直接的结果就是:油门响应不线性,某个电机容易过热,偏航修正方向错乱,甚至解锁就翻。我见过不止一个朋友把异形机架的问题当成 PID 调不好的问题来搞,折腾一个月,最后发现是混控系数本身就不对。这就是 Hyperframes 这类方案出现的背景:与其让飞控去凑预设,不如让机架几何自己说话。
1.2 Hyperframes 的核心思路:几何即参数
Hyperframes 的思路其实很朴素:你把机架的几何描述给飞控,飞控自动推导出每个电机的混控系数。它不需要你去改底层控制代码,也不需要每次换机架都重新编译固件。它的核心是一套坐标系约定和一套求解方法,外加一个配套的脚本工具链。
在 ArduPilot 生态里,Hyperframes 相关的项目由开发者社区推动,最终落地方式不是“换了新固件”,而是把计算流程拆成了两部分。第一部分是你自己或者在电脑上完成的:定义机架的几何参数,解算出每个电机对总推力、横滚、俯仰、偏航的贡献系数。第二部分是飞控端的工作:把解出来的系数填进自定义矩阵参数,飞控就能把这架怪机器当作一个常规多旋翼来解算姿态控制。
我自己的理解是,它把“机架设计”从选择题变成了填空题。过去你做一台异形机,必须想办法把它的几何塞进某个预设的抽屉里;现在你只要把每个电机的坐标、朝向、旋向填进去,控制分配部分会自动适配。这个思路放到工程上是非常优雅的,因为它把“结构自由度”和“控制算法”彻底解耦了。你可以在机架上大胆做文章,随后只需要重新算一遍矩阵,而不是反过来被软件限制住。
2. 关键原理:从电机几何到混控矩阵
2.1 多旋翼控制分配的本质
要理解 Hyperframes 的工作方式,得回到多旋翼最基础的动力学模型。飞控本质上同时在处理四件事:总升力、滚转力矩、俯仰力矩、偏航力矩。每个电机对这四个输出都有贡献。升力来自旋翼转速的平方和,力矩则来自“力臂叉乘推力”和“旋翼反扭矩”。
你可以想象几个人抬一块木板。木板的升降取决于所有人一起用力的大小,木板往哪边倾斜则取决于每个人站在什么位置、沿什么方向发力。电机就是这些人,机臂就是他们的站位,桨盘方向就是他们的发力方向。预设构型相当于把“站位”强行固定成几种标准形状,Hyperframes 则是把每个人的站位和发力方向精确测量出来,再统一计算怎么分配用力。
具体到数学上,对第 i 个电机,它的推力 f_i 沿某个方向向量 n_i,位置向量是 r_i。它对机体产生的滚转、俯仰力矩是 r_i × f_i,偏航力矩则主要来自旋翼反扭矩,方向由旋向决定。把所有电机的贡献放在一起,控制分配问题就变成了一个线性方程组。
2.2 坐标系约定与电机描述方式
所有计算之前,先约定坐标系。很多开源飞控沿用 NED 机体坐标系:原点在飞控板安装位置,X 轴指向机头前方,Y 轴指向右侧,Z 轴指向下方。听起来简单,但实际填写时非常容易出问题,因为 Z 轴向下这个约定和我们高中物理里“竖直向上为正”的习惯完全相反。
坐标有了,接下来要描述电机。需要几个信息:位置坐标 (x, y, z),单位是米;推力方向是个三维单位向量,默认垂直安装的电机就是 (0, 0, -1),因为是向下推;旋向,顺时针还是逆时针,这个决定偏航力矩的符号。
用标准 X 四旋翼举个例子。假设臂长 0.25 米,四个电机的坐标按右前、右后、左后、左前排列,分别是 (0.25, 0.20, 0)、( -0.25, 0.20, 0)、( -0.25, -0.20, 0)、(0.25, -0.20, 0)。推力方向全是 (0, 0, -1)。这时候看横滚力矩,左右两侧电机的位置正好产生正负相反的贡献;看俯仰力矩,前后两侧电机位置产生正负相反的贡献。这就是标准混控表的来源。
如果你做一个非对称机架,比如前臂 20 厘米、后臂 30 厘米,那四个电机坐标就不再对称,预置混控表自然就不成立了。用代码直接一算,每个电机的贡献会自动体现臂长差异,前电机需要更多的油门权重来平衡后电机更长的力臂。
2.3 伪逆求解与混合系数的意义
把所有电机的贡献叠起来,会得到一个矩阵 A,构造方式是每个电机对应一列,四行分别代表总推力、滚转力矩、俯仰力矩、偏航力矩。理论上,你要的电机转速组合是 A 的逆矩阵作用在期望的力和力矩向量上。但电机数量不是固定的,四旋翼是 4×4,六旋翼是 4×6,八旋翼是 4×8,这时候矩阵就不是方阵了,不存在普通意义上的逆。
解决方法是伪逆。伪逆给出一组误差最小、同时能量也最小的转速分配。放到多旋翼上的工程含义是:在电机数量多于 4 个时,系统有一定冗余,伪逆会自动把负载尽量均匀地摊到各个电机上,而不是让某几个电机猛出力、另外几个干瞪眼。这也是为什么 Hyperframes 能支持倾斜电机——即便某个电机不能直接产生某个方向的力矩,伪逆也会根据所有电机的合力去折中分配。
我提供一个最简的 Python 计算示例,直接用 NumPy 就能跑。这里没有涉及任何复杂算法,只是把前文说的几何关系变成代码:
import numpy as np # 每个电机的坐标 [x, y, z],单位米 positions = np.array([ [ 0.25, 0.20, -0.03], # 电机1:右前 [-0.25, 0.20, -0.03], # 电机2:右后 [-0.25, -0.20, -0.03], # 电机3:左后 [ 0.25, -0.20, -0.03], # 电机4:左前 ]) # 每个电机的推力方向,垂直向下为 [0, 0, -1] directions = np.array([ [0, 0, -1], [0, 0, -1], [0, 0, -1], [0, 0, -1], ]) # 旋向:逆时针+1,顺时针-1 spin = np.array([-1, 1, -1, 1]) # 反扭矩系数与推力系数之比,取决于桨叶尺寸,先取一个估算值 k_ratio = 0.02 n = len(positions) # 总推力贡献:每个电机在 Z 轴方向的推力分量 T = np.array([directions[i][2] for i in range(n)]) # 滚转力矩:r × f 的 X 轴分量 Mx = np.array([ positions[i][1] * directions[i][2] - positions[i][2] * directions[i][1] for i in range(n) ]) # 俯仰力矩:r × f 的 Y 轴分量 My = np.array([ positions[i][2] * directions[i][0] - positions[i][0] * directions[i][2] for i in range(n) ]) # 偏航力矩:主要由旋翼反扭矩决定,方向取决于旋向 Mz = np.array([spin[i] * k_ratio for i in range(n)]) A = np.vstack([T, Mx, My, Mz]) # 伪逆:从期望的 [T, Mx, My, Mz] 解算出各电机转速平方 mixer = np.linalg.pinv(A) print("混控矩阵(行=电机,列=T/R/P/Y):") print(mixer)输出的矩阵就是每个电机对四个通道的贡献。把每行的数值按照 T、R、P、Y 的对应关系填入飞控自定义矩阵参数,你的几何就在真正起作用了。代码里的 k_ratio 我一般先用桨叶经验值,后面在实测里修正,这个后面细说。
3. 实操:把 Hyperframes 思路落到一台自定义机架上
3.1 从机架设计到混控参数表
说一堆理论不如走一遍完整流程。我用自己做过的一台“前短后长”非对称四旋翼来演示。这台机器的前臂 0.20 米,后臂 0.30 米,电机全部垂直安装,旋向一正一反交错。飞控安装点就在重心附近,坐标略有偏移也无所谓,先按网格中心算。
机架参数如下表:
| 电机 | 位置 (x, y, z) | 推力方向 | 旋向 |
|---|---|---|---|
| 电机1 | (0.20, 0.18, 0.02) | (0, 0, -1) | 反转 |
| 电机2 | (-0.20, 0.18, 0.02) | (0, 0, -1) | 正转 |
| 电机3 | (-0.30, -0.18, 0.02) | (0, 0, -1) | 反转 |
| 电机4 | (0.30, -0.18, 0.02) | (0, 0, -1) | 正转 |
把这组数据代进前面那个 Python 脚本,跑出来的混控矩阵就不是标准 X 的对称结果了。你会发现电机 3 和电机 4 因为后臂更长,对俯仰轴的力臂也更大,所以它们在俯仰通道上的系数会自动被压低一些。伪逆算法为了让前后俯仰力矩匹配,会倾向于给前电机稍大的俯仰修正量,而后电机在油门总量上承担更多权重。这个结果非常合理,因为后臂更长,等效杠杆更大,不需要像短臂那样拼命加力。
拿到矩阵后,还要做一步归一化处理。ArduPilot 自定义矩阵参数里,各个系数一般落在 -1 到 1 范围,用于表示每个电机的“相对修正比例”。我习惯把每个电机整行的绝对值最大值缩放成 1,这样保证所有通道的量纲一致。这个归一化在之后的 PID 调节里很重要,不然某个电机可能会出现修正指令饱和,飞起来会莫名抽动。
3.2 写入飞控参数和地面检查
打开 Mission Planner,把 Frame Class 设成四旋翼或对应的自定义类型,Frame Type 选 Custom。然后进入参数列表,逐个找 MOT_ROLL1、MOT_PITCH1、MOT_YAW1、MOT_THST1 这些项,把你算出来的四行系数分别填进去。不同固件版本界面略有差异,但参数名基本一致。
需要同时确认的基础参数还包括 MOT_PWM_MIN(通常 1000)、MOT_PWM_MAX(通常 2000)、MOT_SPIN_ARM(解锁怠速,我会给 1300 左右)。有一点很关键:MOT_THST1 到 MOT_THST4 之和最好归一成约 1.0 的基准,否则你解算出的油门全开分配会和实际最大推力不匹配。这是我第一次用自定义矩阵时忽略掉的地方,后果是悬停油门太低、线性度差。
写完参数不能直接起飞。第一步,拆掉所有螺旋桨。第二步,上电后在 Mission Planner 的电机测试界面里逐个解锁测试。你需要确认:电机 1 在飞控指令下转动时,对应到机架上确实是电机 1 的位置;电机 2、3、4 依次对应。这个步骤看似无聊,但十个炸机事故里有一半是因为电机编号和几何位置对应错位。
确认完编号,再手动扭转机体,观察飞控的修正方向是否和几何方向一致。把机头向右倾斜,飞控应该让右侧电机增加推力把机体抬回水平;机头上仰,应该让前侧电机增加推力。如果在倾斜测试中发现某个通道的修正是反向的,直接检查对应 MOT_ROLLx 或 MOT_PITCHx 的符号,而不是去动 PID。
3.3 首次试飞和参数细调
地面检查全部通过后,可以上桨试飞。我的习惯是第一次解锁后先在地面低油门推至刚有浮力,确认所有电机转速同步、机体没有明显抖动,然后立刻收油。这一步能排除一半的混控符号错误。如果一推油门就侧翻,基本不是 PID 问题,而是某个电机的力矩方向写反了,优先复查反扭矩正负。
能稳定悬停起来之后,再从日志里看悬停油门百分比。对于非对称机架,前后电机的悬停油门应该略有差异,但差值不该太大。如果某个电机在日志里长期比另一些高出 15% 以上,大概率是重心和飞控原点不重合,也可能是 THST 权重没算准。这时候先调整电池位置让重心尽可能贴住飞控测量中心,比在参数里硬补偿要靠谱。
之后就是常规的 PID 调整。我把谢的动作留到后面,这里只说一个经验:自定义矩阵机架的默认 PID 往往偏软,尤其是横滚俯仰的阻尼。因为非对称机架不同方向的转动惯量差异很大,一套 PID 很难同时照顾所有轴,建议适当调低 P,先把曲线调稳再逐步拉高响应。
4. 常见问题与排查技巧实录
4.1 典型故障速查表
我自己做过的几台自定义机架,加上帮朋友调过的异形四轴,遇到的问题其实非常集中。整理成表格放在下面,方便对照排查:
| 现象 | 大概率原因 | 处理办法 |
|---|---|---|
| 解锁后某个电机狂转 | 电机编号与几何位置不对应 | 重新核对 MOT_ 参数顺序 |
| 推油门瞬间侧翻 | ROLL/PITCH 或 YAW 符号取反 | 锁定符号错误,翻转系数正负 |
| 悬停时机体周期性抖动 | THST 权重不均匀 | 重新计算归一化矩阵 |
| 急推油门时机架扭转 | 偏航反扭矩系数偏差 | 调整 Mz 行的 k_ratio |
| 某个电机温度明显偏高 | 重心偏移或伪逆分配不均 | 先调重心,再检查混控 |
| 低油门失衡、高油门正常 | MOT_SPIN_ARM 或怠速设置不当 | 适当提高怠速值 |
前面几项都好理解,唯独“急推油门时机架扭转”很多人没意识到。这个问题的根源在于桨的反扭矩和转速平方相关,但线性分配矩阵是用常数系数描述的。如果你选用的电机和桨搭配偏大,反扭矩增长会比矩阵模型的预期更猛,大油门时偏航力矩就会失衡。我一般通过实测来修:先悬停,然后快速推满油门,观察日志里偏航角速度的偏差方向,调整 k_ratio 直到急加减油门时机头不偏。
4.2 坐标系和符号的坑
混控计算出的系数只是一堆数,真正的考验是正负号。我的习惯是把每个轴的正方向打印成一张纸贴在机架上,调试时对着看。飞控解锁后,你用手倾斜机体,飞控的姿态传感器会根据预设的方向发出修正指令,如果某个通道完全反向,立刻去参数表里把对应的整列系数取负号。
这里有个容易忽略的细节:Z 轴向下时,绕 Z 轴的扭矩正负和大部分人的直觉相反。逆时针旋转的旋翼产生一个沿 Z 轴为正的力矩,还是负的,取决于你的右手定则怎么套。我建议不要在纸面上纠结,直接用“桨叶转向测试”来定:给每个电机单独解锁空转,记下旋转方向,然后按旋向正负填入代码。这样至少保证半数的符号问题被提前消灭。
另外,如果你做带倾角的电机,比如越障机把电机向外倾斜 10 度,那就不只是数学计算了,还有一个物理上的副作用:电机推力被分解成垂直分量和水平分量,水平分量会产生额外的横滚俯仰力矩,这类力矩在电机转速变化时并不线性,所以倾斜角越大,你的混控模型越需要多点校准,而不是纯靠伪逆一次到位。
4.3 我最想分享的几个实操心得
全文最想说的其实是这一点:Hyperframes 的整套思路,最大价值不在于算出一张参数表,而在于强迫你去理解机架的物理结构。我第二次调自定义四轴的时候,试着不依赖脚本,手算第一版的粗矩阵,结果从中发现了我的机架存在一个很隐蔽的设计缺陷——前后臂刚度差异太大,导致大动态时机架在俯仰方向出现扭转弹性。这类结构性问题是任何矩阵都救不了的,只能靠结构优化。
关于参数填写,我强烈建议保留每次的计算脚本和机架参数快照。因为换一块飞控、升级一次固件,参数可能要全部重新填一遍。有脚本的情况下,五分钟就能恢复配置。我吃过一次亏,升级固件后电机编号顺序变了,没有及时复查,第一次试飞直接抖断了一个机臂。从那以后我把“脚本 + 参数截图 + 电机编号表”做成固定模板,每次动配置都更新一遍。
最后一个技巧:做完静态测试后,记得用一次短航程飞行刷一遍日志,然后用 ArduPilot 自带的日志回放工具检查姿态异常。不要在真机上手感差不多就直接飞远航。控制器能悬停和能持久稳定是完全两回事,尤其异形机架的振动特性通常比标准机架复杂,回放日志能帮你区分“手感问题”和“参数问题”。
我在实际使用中的体会是,Hyperframes 这类工具把“自定义机架”的门槛拉低了一大截,但它没有把“理解机架”的门槛也降低。所有自动求解的混控矩阵都建立在准确的几何描述上,你只要在参数上偷一点懒,飞控就会在天空上惩罚你。如果你正在折腾一台标准预设搞不定的怪机架,不妨从写坐标那一刻开始,重新对待一次机架设计。那一步做完,后面很多坑都会自己消失。