早几年我还在折腾航模的时候,最让我好奇的东西不是飞机怎么飞,而是电调盒子里那颗小小的芯片,怎么就让三根粗线像变魔术一样驱动电机高速旋转。后来我开始做手持云台,发现同样是三相无刷电机,航模电调和云台控制的逻辑完全是两个路子。我查了大量资料,从六步换向看到FOC,又从FOC一路看到SVPWM,最后在开源SimpleBGC项目里把整条线串了起来。这篇文章就是把我这一路梳理的东西讲清楚,不堆数学公式,尽量用搞工程的人的习惯来说话。适合正在入门无刷电机FOC控制、想搞懂SVPWM原理,或者手头有开源云台/航模项目想移植改造的朋友读。
先说一个容易让人误会的点:SimpleBGC严格来说并不是一套完整意义的FOC实现,它的核心是IMU姿态融合加PID,直接输出PWM占空比去驱动三相全桥,属于“电压/位置环”层面的控制。但它的电机相序配置、方向校正、死区设置、PID调参流程,以及整个“传感器—控制—功率驱动”的链路,和FOC系统高度同构。很多人正是从BGC的代码里理解了电机控制框架,再切到SimpleFOC或自己写FOC时,才能一眼看穿问题在哪。所以用它当入口,比直接啃一堆数学推导要舒服得多。
1. 为什么从SimpleBGC入手理解FOC与SVPWM
1.1 SimpleBGC到底是什么
SimpleBGC(BaseCam Control)是一套开源的云台增稳控制器项目,硬件上常见的是基于Atmel AVR单片机(比如ATmega328P、ATmega2560)的板子,配合一颗六轴/九轴IMU(MPU6050、MPU9250等),通过三相全桥电路驱动2轴或3轴无刷电机。固件里做的事情概括起来就三步:读IMU得到姿态角,用PID把当前姿态往目标姿态上拉,把PID输出换算成三相PWM占空比送给电机驱动。
这个链路非常干净,没有太多中间层,很适合作为第一份读的电机控制开源项目。相较于SimpleFOC、ODrive这些更贴近“现代FOC全家桶”的项目,SimpleBGC的代码量更小,硬件也便宜,二三十块钱的板子加两颗云台电机就能跑起来。而且它的调参软件做得挺完善,能实时看到姿态响应、PID输出、电机PWM波形,对理解反馈控制很有帮助。
不过也要说清楚,SimpleBGC默认不采集相电流,它走的是“PID算误差→直接调电压”的路线。这意味着它没有真正的电流环,所以动态响应和力矩控制精度不如FOC方案。但这正好给了我们一个对比认识:等你理解了BGC为什么“够用但不够好”,自然就想搞明白FOC到底多了什么。这篇文章后面我会把BGC缺失的那一部分——电流闭环和SVPWM——补全来讲,刚好形成一个完整的学习环。
1.2 从航模电机到云台电机,同一个三相桥的两个极端
很多人第一次接触无刷电机是从航模开始的,比如2212 920KV电机,几万转的暴力输出,一个电调几百克推力。这种电机和口袋云台里用的外转子电机,本质都是三相永磁无刷电机,定子上三个绕组,转子上贴永磁体,驱动电路也都是三相全桥。可实际控制需求差距极大:航模电机追求转速和功率密度,可以用比较暴力的换向;云台电机追求低转速下微小角度的高精度保持,转矩波动稍大一点,画面就抖得没法看。
这就像同一个发音器官,用来吼和用来说悄悄话,控制策略完全不一样。航模电调普遍用六步换向(梯形波驱动),位置传感器要么用霍尔,要么靠反电动势过零检测,在任意时刻只有两相通电,第三相悬空。这种方式简单、效率也高,但每60度电角度换相一次,会产生明显的转矩脉动。云台要的是正弦电流连续变化,让定子磁场平滑旋转,转矩波动越小越好,于是FOC和SVPWM就派上了用场。
这里有一个关键的认知点:FOC不改变电机本体,也不换桥电路,改的是大脑——软件里用什么模型去决定三相PWM的占空比。硬件上,同一块三相桥既能跑六步换向,也能跑SVPWM。这也就是为什么SimpleBGC项目对学习FOC这么友好:它的硬件结构完全可以承载FOC/SVPWM,只不过固件里用的是简化路线而已。
2. FOC控制的核心逻辑:让电流矢量追着转子磁极跑
2.1 无刷电机为什么需要换向,直流电机为什么不用
先回到基础。普通有刷直流电机里,电刷和换向器承担了“换向”任务:转子转到什么角度,电刷自动把电流分配到对应的绕组,保证定子磁场和转子磁场始终维持一个合适的角度。这个角度越接近90度,产生的力矩越大。
无刷电机把换向器去掉了,转子变成永磁体,定子变成绕组。没有机械换向了,那就必须靠外部电路“看着转子的位置,动态决定三相怎么通电”。简单说,无刷电机的控制核心就是:时刻知道转子磁极的位置,再让定子电流产生的磁场始终超前转子磁场90度电角度。谁把这个角度维持得住,谁就能拿到平滑的大扭矩。
FOC(Field Oriented Control,磁场定向控制)干的事情,就是把“维持90度”这个问题变成一个自动化问题:不停地测量转子角度,不停地计算三相电流,让电流矢量在旋转坐标系下始终落在q轴上。云台、机器人关节、电动汽车里常见的“低速大扭矩+平稳运行”需求,基本都是靠FOC来满足的。
2.2 Clark变换与Park变换:从三相绕圈到两个直流轴
直接看三相正弦电流的表达式去设计控制器很麻烦,因为三个量都在随时间变化,而且彼此相差120度。控制“三个正弦波”比控制“两个直流量”要困难得多,所以FOC发明了一组变换,把问题转化到容易处理的坐标系里。
第一步叫Clark变换,把abc三相静止坐标变换成αβ两相静止坐标。这个过程可以理解成:把三个绕组的磁动势投影到两个互相垂直的轴上。公式很简单,只需要记住两个方向系数,即1和1/√3的组合。变换完之后,三相正弦量变成了两个正交的正弦量,但它们还是交流量。
第二步叫Park变换,把αβ坐标系旋转到与转子同步的dq坐标系。关键在于这个旋转角就是当前转子的电角度,来源是编码器、霍尔或观测器。旋转之后,原本跟着转子转的电流矢量在dq坐标系里看上去就不动了:d轴电流Id是励磁分量,q轴电流Iq是转矩分量。于是“控制三相交流电”变成了“控制两个直流量”,可以像控制直流电机一样用两个PI调节器去调Id和Iq,这就是FOC最优雅的地方。
实际代码里,每次控制周期执行顺序一般是:采样相电流→Clark变换→Park变换→两个PI调节器(往往Id给0,Iq来自速度环输出)→反Park变换得到Vα、Vβ→SVPWM算出三相占空比。这套流程在高频中断里不停跑,比如16kHz,整个过程对人眼来说是瞬时的,但对电机来说每一微秒都在被精确算计。
2.3 电流环、速度环、位置环:FOC的三个嵌套回路
FOC不是单个环,而是三个嵌套的闭环。最里面是电流环,采样三相电流,控制输入是目标Iq和Id,输出是电压矢量;中间是速度环,由目标转速与实际转速的误差去生成目标Iq;最外面是位置环,由目标位置与实际位置的误差去生成目标转速。云台控制实际上往往还需要一个姿态环,属于位置环的一个特殊形态。
电流环带宽最高,必须跑得最快,通常刷新频率在10kHz到20kHz以上。速度环可以慢一些,位置环更慢。这种“内环快、外环慢”的设计是工程上的铁律:内环没跟上,外环调得再猛也只是来回震荡。SimpleBGC里的PID其实是在“位置/速度”这一层,它把PID输出直接当电压指令用,没有电流内环,所以它的模型更粗,对电机和负载变化也更敏感。
我自己上手FOC的经验是:先单独调电流环,给定一个固定Iq值,看电机能不能平稳出力;电流环稳了再上速度环;速度环稳了最后才做位置环。跳级调参是最浪费时间的做法,因为出了问题你根本不知道是哪个环在抖。
3. SVPWM:把“给三相通电”这件事做到极致
3.1 六步换向与SPWM的局限
知道了要给q轴电流,接下来就是物理层的问题:三相全桥只有6个开关管,怎么输出一个特定方向和幅值的电压矢量?最粗暴的办法是六步换向,每个电角度60度区间只给两相通电,第三相关断。这个办法的有效电压是阶梯状的,电流波形离正弦很远,转矩脉动大,噪声也大。
比六步换向好一些的是SPWM,也就是用三路正弦波和三角载波比较,生成三路PWM。SPWM让三相电流更接近正弦,但它的母线电压利用率比较低,最大能输出的相电压幅值只有Vdc/2。对于电池供电的设备,本来就宝贵的每一伏电压,浪费在驱动能力上都很可惜。
SVPWM(空间矢量PWM)不是“把三相各自调成正弦”,而是直接在矢量空间里思考:我要合成的电压矢量落在哪里,就用相邻的两个基础矢量和零矢量去凑。它把母线电压里能榨出来的部分用满了,最大线性不失真相电压幅值可以达到Vdc/√3,比SPWM整整多出约15%。对云台和航模这种讲究推重比和电池续航的场景,这15%就是实打实的体验差距。
3.2 八个基础电压矢量与扇区判断
三相全桥的每一相上管和下管互补导通,如果忽略死区,每种开关状态对应一个输出电压矢量。三个开关管总共8种组合:6种有效矢量(V1到V6)和2种零矢量(V0、V7)。把这6个有效矢量画在αβ平面上,就是一个正六边形的6个顶点,零矢量在圆心。
任何一个合法的目标电压矢量,只要落在六边形内切圆内,都可以由它所在扇区相邻的两个有效矢量拼出来。第一步就是判断目标矢量落在第几扇区。工程上常用的办法不需要算反三角函数,直接用三个表达式的正负判断即可。
// 输入 Va、Vb 为反Park变换后的alpha、beta轴电压 int svpwm_sector(float Va, float Vb) { float V1 = Vb; float V2 = 0.866f * Va - 0.5f * Vb; // sqrt(3)/2 float V3 = -0.866f * Va - 0.5f * Vb; // -sqrt(3)/2 int sector = 0; if (V1 > 0) sector |= 1; if (V2 > 0) sector |= 2; if (V3 > 0) sector |= 4; // 结果对应:3, 1, 5, 4, 6, 2(不同参考系的命名可能不一样) return sector; }这段代码是FOC工程里的经典写法,我在好几个开源项目里见过几乎一模一样的实现。它的好处是纯加减乘除,性能开销极低,适合在16kHz中断里每周期都跑。扇区判断错了的典型表现是电机会怪异地抖动或者完全转不动,后面我会专门说怎么排查。
3.3 七段式SVPWM的时序与时间计算
判断完扇区之后,要算两个相邻基础矢量各作用多长时间。假设PWM周期为Ts,目标电压矢量Vref在某个扇区由相邻矢量Vx、Vy合成,那么作用时间Tx、Ty可以由伏秒平衡解出来。工程上常用X、Y、Z三个中间变量查表,避免每个扇区都推一遍三角函数:
X = √3 * Vb * Ts / Vdc Y = (1.5*Va + 0.866*Vb) * Ts / Vdc Z = (-1.5*Va + 0.866*Vb) * Ts / Vdc不同扇区的Tx、Ty查表确定,零矢量时间T0 = Ts - Tx - Ty。为了让PWM波形对称、谐波小,工程中普遍采用七段式:把零矢量分成两段,一段放在周期开始和结束,一段放在周期中间。以第一扇区为例,开关序列为000→100→110→111→110→100→000,也就是V0→V1→V2→V7→V2→V1→V0。
各相占空比算出来之后,写入定时器比较寄存器即可。要注意的是,电流采样一般安排在整个PWM周期的中心点,也就是零矢量V7那段的中部。这个时刻三相桥臂处于固定状态,开关噪声最小,测出来的电流最能代表相电流真实值。采样时机调不对,电流环看到的就是一堆毛刺,怎么调PI都抖。我早期在这个坑里耗了整整两个晚上,最后用示波器去对PWM才意识到采样点偏离了。
3.4 五段式与过调制:省开关损耗和榨干母线电压
七段式虽然是主流,但每个PWM周期所有桥臂都在开关,开关损耗比较可观。五段式SVPWM在部分扇区让某一相保持不动作,每个周期只有五段开关序列,能减少约三分之一的开关次数。代价是电流谐波变大,波形对称性变差,对高精度应用不太友好。很多电动工具和风机方案喜欢用五段式,因为对噪声不敏感,能省电、降低MOS管发热。
另一个问题是过调制。前面说的线性调制区,参考电压矢量不能超出六边形内切圆,一旦目标矢量的幅值超过Vdc/√3,Tx+Ty就会大于Ts。这时候不能直接截断,否则电流波形畸变得厉害。处理办法有两种:
过调制I区:保留参考矢量的角度,把幅值等比例缩小,直到Tx+Ty刚好等于Ts。这种办法实现最简单,在实际代码里就是做一次归一化缩放,适用于电压需求略超线性区的情况。
过调制II区:参考矢量进一步增大,不仅幅值要限幅,输出的角度也需要修正,让基波电压继续升高。这个区域谐波和转矩脉动明显增大,一般只用在需要极限电压输出的场合。云台、机械臂这类对噪声和平稳性敏感的设备,很少会跑到过调制II区去。
如果你看到某款电调宣称“支持过调制”,多数只做了I区那一步,II区因为复杂度和收益的问题,实际落地的开源方案不多。理解这一点,你再去啃论文里那些调制比曲线就不会发懵了。
4. SimpleBGC的工程实现:原理是怎么塞进固件的
4.1 固件分层:IMU、姿态、PID、PWM
SimpleBGC固件的层次很清晰:IMU驱动读原始角速度和加速度,姿态解算得到欧拉角或四元数,控制层把目标姿态角与当前姿态角的误差送入PID,PID输出经过限幅后映射为三相PWM占空比。整个流程在定时中断里循环执行,循环频率通常就是PWM频率,比如8kHz或16kHz。
它的电机输出部分,会先根据当前转子电角度查表生成正弦波参考,再叠加PID的校正量,这其实已经有点“正弦驱动”的影子了,但因为没有电流反馈,本质上还是电压开环控制。这样做的结果是:低速时候还行,一旦云台负载变化大或者快速转动,力矩跟不上,画面就会“点头”。
这也是我建议你看SimpleBGC代码时重点关注的边界:它在什么地方用了查表,什么地方用了反馈,什么地方直接用占空比粗调。每一处简化背后都有明确的工程理由——减少计算量、减少硬件成本、满足云台的基本增稳需求。看懂了这些简化,你才会理解为什么FOC会更高级,而不是单纯觉得“BGC代码老、功能弱”。
4.2 电机方向、相序与死区:最容易翻车的地方
SimpleBGC项目里最经典的一张图,就是电机三相接线顺序与IMU安装方向的关系。相序接错了,云台的电机要么疯狂抖动,要么朝错误方向抵抗,要么直接嗡嗡响。原因是三个绕组的磁场方向不对,电流矢量与转子磁极角度的相对关系错误。
用FOC的角度来看这个问题更清楚:电流矢量是通过Park变换和转子电角度绑定的,如果位置传感器的方向与电机磁极方向不对齐,那你以为的q轴电流实际上是d轴电流,力矩瞬间塌掉。很多人在SimpleBGC的GUI里点“方向反转”或者交换两根电机线,本质上都是在校正这个对齐关系。最稳妥的办法是:开环给定一个已知方向的电压矢量,同时人工缓慢转动转子,对比目标角度与实际响应的方向关系。
死区时间也一样关键。三相全桥的上下管不能同时导通,必须插入一小段“双关断”时间,即死区时间,通常几百纳秒到一两微秒。死区设置太长会引入电压误差,太短又有直通炸管的危险。SimpleBGC这类低电压小功率板子,死区一般取0.5到1微秒,MOS管内阻大一点的还要适当放宽。
4.3 传感器选型:霍尔、编码器、无感
FOC需要转子角度,这个角度信息从哪里来,直接决定方案的复杂度和成本。SimpleBGC的云台电机通常不带霍尔,它其实更多依赖IMU来推断受力方向,并不需要精确的电角度——因为它是电压控制,不需要做电流环。但如果要做完整FOC,角度传感器就绕不开了。
常见的三种选择:霍尔传感器便宜,只能提供粗略的区间位置,适合低速但精度一般的应用;磁编码器(如AS5047P)能提供绝对角度,精度高、安装方便,是云台和机器人关节的首选;光学编码器精度最高,但成本和安装复杂度也高。航模电调普遍走“无感FOC”,不装任何传感器,靠观测器估算转子位置和反电动势,成本低但启动和低速段容易翻车。
如果你打算做口袋云台类的项目,直接上磁编码器是最省心的路线。不要试图用无感方案做低速高精度云台,那是在地狱难度里折磨自己。SimpleBGC项目中很多改装者后来放弃原版电压控制、转向FOC,第一件事就是给电机屁股加编码器。
5. 实操手记:从零配一套可复现的FOC/SVPWM实验环境
5.1 硬件清单与接线
想亲手把前面这些原理跑起来,推荐一套便宜又好复现的组合:一块支持SimpleFOC或者自己写代码的STM32开发板,比如常见的F103开发板,一块三相驱动板或者集成驱动芯片,一颗外转子云台电机,一颗AS5600磁编码器,再加一组电流采样模块。如果只想先把SimpleBGC跑通,买一块现成的BGC 3轴云台控制板,配两颗云台电机和IMU,几十块就能起步。
接线看起来复杂,其实核心就三块:电源给驱动板供电,驱动板三相输出接电机三根线,编码器/IMU的信号线接单片机的I2C或SPI。注意电机电源和单片机逻辑电平静地要共地,不然采样和通信全是毛刺。我见过很多人折腾一晚上不转,最后发现是地线没接。
这里务必提醒一点:先用稳压电源限流供电,比如限到0.5A再上电,防止接线错误直接烧板子。我操作过不下十次,每次调新硬件都这么做,从来没炸过板,倒是真见过有人信心满满直接上锂电池,然后一股青烟。
5.2 固件参数配置:极对数、相序、方向、PID
无论你最终刷SimpleBGC还是自己移植FOC代码,有四个参数是必填的:极对数、电机相序、传感器方向、PID初始值。极对数在云台电机上通常标在标签里,比如14极对,如果你的编码器是单圈绝对式,电角度就是机械角度乘以极对数然后对360度取模。
相序和方向需要在通电状态下验证,方法是让控制器输出一个恒定的电压矢量,看电机是否朝预期方向锁定。如果锁定的位置偏了60度或120度,说明相序接错了;如果方向反了,把任意两根电机线交换或者把编码器角度取反即可。记住,这一步别急着调PID,先把“角度的数字世界”和“磁场的物理世界”对齐。
PID初始值给一组保守参数,比如P给0.2、I给0.05、D给0,然后慢慢加。我习惯的调法是先把D清零,把P从0开始一点一点往上加,直到出现轻微振荡,然后回调到振荡点的一半;接着加一点I消除静态误差;最后才用D去压超调。云台电机的惯量相对固定,PID参数范围其实很集中,不需要每次都在大范围里乱试。
5.3 电流采样与PWM互补输出的配置细节
做完整FOC时,电流采样是绕不过去的一道坎。STM32的ADC和PWM定时器可以联动,在PWM周期中心触发采样。SVPWM七段式波形在中间时间段是零矢量,此时开关噪声最小,ADC在这个点采样能得到比较干净的值。实际工程里往往要连续采两三次求平均,滤掉尖峰。
互补PWM输出需要在定时器里配置死区插入,同时开启刹车功能,一旦发生过流或驱动芯片报错,立即封锁PWM输出。这部分代码别省,哪怕是小功率实验板,多一层保护也能救回不少元件。驱动芯片的数据手册里通常有明确的时序要求,照着它的时序配置即可。
如果你用现成的SimpleFOC库,这一整套其实都封装好了,你要做的就是配置引脚、设置极对数、选编码器协议。但我仍然建议你去看库里MOTOR.SVPWM那一层是怎么调用定时器和ADC的,因为面试或者自己写代码时,这些细节才是真正拉开差距的地方。
6. 常见问题与排查技巧实录
我把这些年在无刷电机FOC和SimpleBGC调试中踩过的坑整理成了一张速查表,方便你直接对照。很多问题看起来像是玄学,解剖到最后其实都是时序或者相序的问题。
| 现象 | 可能原因 | 处理办法 |
|---|---|---|
| 电机嗡嗡响但不转 | 相序错、电角度偏移、死区过大 | 交换电机两相线,校准编码器零位,减小死区 |
| 启动时抖动一下然后卡死 | 位置传感器方向反了 | 检查编码器接线,软件里反转角度方向 |
| 电流波形一堆毛刺 | ADC采样点不在PWM中心 | 配置定时器触发ADC在零矢量中点采样 |
| 电机转起来发热严重 | PWM频率太低或死区太大 | 提高到20kHz以上,按驱动芯片规格设死区 |
| 云台回中后一直慢漂 | I值太小,或IMU零漂 | 加大积分增益,校准IMU零偏 |
| 带负载时力矩明显不够 | 母线电压利用率低,或电流环限幅太小 | 确认是否跑SVPWM线性区,检查电流环输出限幅 |
| 一上电驱动芯片就烧 | 电源接反、死区没配、没有刹车保护 | 检查电源极性,确认死区时间,配置过流刹车 |
再补充几个经验。第一,PWM频率对可闻噪声影响很大,很多云台电机在8kHz下会有明显的啸叫,提到16kHz以上人耳就基本听不到了。但PWM频率越高,开关损耗越大,MOS管发热越厉害,所以不要无脑拉高。我习惯先定16kHz,如果电机温度高再考虑降频或换低导通电阻的管子。
第二,电流采样电阻的布局很关键。采样电阻要尽量靠近MOS管源极,走线要短粗,避免用飞线搭棚方式接电流采样,否则你采到的是天线信号而不是电流。如果你发现电流环在低速时数值跳得不正常,先怀疑采样,再怀疑PI参数。
第三,SimpleBGC和FOC的关系经常把人绕晕。如果你只是为了云台增稳,原版BGC已经够用;但如果你想做视觉追踪云台、机械臂关节或者电动滑板这类对力矩和动态响应有要求的项目,直接上完整FOC+SVPWM,不要在电压开环控制里去补那些永远补不齐的坑。
最后再分享一个我自己的习惯:每次调完一套电机驱动,我都会把相序、编码器方向、极对数、PWM频率、电流采样点这五件事在代码注释里写清楚,哪怕当时觉得“这怎么可能忘”。因为两周后你再回来看代码,这些东西全都会变成模糊的记忆,而它们恰恰是所有奇怪故障的根源。无刷电机控制入门最大的门槛不是数学,而是物理世界和数字世界之间那一堆对不齐的“坐标系”。把每个坐标系都对齐了,剩下的PI参数世界,就会变得友好很多。