简介:这份PDF笔记面向正在学习或准备实现FOC(磁场定向控制)的电机控制初学者与嵌入式开发者,系统梳理了从理论推导到算法落地的关键环节。内容围绕FOC核心思想与超前角控制展开,依次讲解Clark变换的等幅值与恒功率系数推导、Park变换及逆变换的数学过程与程序实现,并深入SVPWM的扇区判断、矢量作用时间计算、过调制处理与切换点确定,还涉及马鞍波成因与零序分量注入SPWM。PID部分覆盖离散化、位置式与增量式算法区别、参数整定及积分抗饱和方法,磁链圆限制则说明MAX_MODULE与START_INDEX的确定思路。资源包为1个PDF文件,约7.34MB,结构按知识点分节编排,便于对照公式与代码逐项理解。目前已有1926人学习,适合希望把数学推导与Simulink仿真、算法实现衔接起来的中级学习者查漏补缺。
1. FOC 的本质:为什么“超前 90 度”这四个字能解释整套算法
很多人第一次看 FOC 框图,会被 Clark、Park、SVPWM、PID 这一串模块绕晕,觉得这是一堆数学变换的堆叠。但把整张图压缩成一句话,其实只有一件事:想尽办法让定子磁场始终超前转子磁场 90 度。转矩公式 T = Fr·sinθ 里,θ 取 90 度时 sinθ 最大,这就是电机能输出最大转矩的条件。所有变换、所有扇区判断、所有作用时间计算,最终都服务于这个几何关系。
理解这一点之后,电角度 θ 的地位就变了:它不是什么神秘参数,只是一个“转子现在在哪”的指示器。因为 abc 三相轴是固定的,而我们需要生成的磁场方向是旋转的,只有知道转子当前方向,才能把“超前 90 度”这个目标分解到固定坐标轴上,进而算出每一相 PWM 该给多少占空比。电角度怎么定义都行,定义成 d 轴与 α 轴的夹角只是为了 Park 变换时投影系数正好是三角函数值,算起来顺手。搞懂这个动机,后面几大变换的推导就不再是玄学,而是一条必然的路径。
2. Clark 变换:降维解耦为什么必须做,系数又为什么选等幅值
2.1 从三相到两相,本质是消掉一个自由度
三相电流并不是三个独立变量。由基尔霍夫电流定律,任一时刻流入节点的电流之和等于流出之和,即 Ia + Ib + Ic = 0。这意味着三相系统实际只有两个自由度,第三个量是冗余的。Clark 变换做的就是把这个三维约束下的平面,映射到一个两维正交坐标系(α、β)上,把“难以辨明和控制的三相相位差 120° 波形”降维成两维矢量。
推导的起点是投影。把三相轴上的电流投影到 α 轴和 β 轴上,α 轴通常与 a 相轴重合,β 轴超前 90 度,于是得到:
# Clark 变换:三相 abc -> 两相 alpha-beta # k 为变换系数,等幅值变换取 2/3,恒功率变换取 sqrt(2/3) def clark(ia, ib, ic, k): i_alpha = k * (ia - 0.5 * ib - 0.5 * ic) # a 相投影到 alpha 轴 i_beta = k * (0.8660254 * ib - 0.8660254 * ic) # sqrt(3)/2 倍的 b、c 差 return i_alpha, i_beta代码里 0.8660254 就是 √3/2,来自 b、c 相轴与 α 轴夹角 120° 的正弦投影。注意这里 ic 在 i_alpha 里的系数是 -0.5,正是 cos120° 的值。整个变换没有非线性运算,只有加法和常数乘法,这也是它能在 MCU 上以极低成本跑起来的原因。
2.2 等幅值系数 2/3 与恒功率系数 √(2/3) 的取舍
系数 k 的选择不是数学上唯一的,它取决于你希望变换前后保持什么不变量。等幅值变换要求变换后的矢量幅值等于原三相相电流的峰值,推导时令 Ia=1、Ib=Ic=-0.5,代入后要求 i_alpha 的幅值为 1,解出 k=2/3。恒功率变换则要求变换前后功率相等,三相功率为 3/2·I²·R,两相功率为 k²·I²·R,令两者相等解出 k=√(2/3)。
实际 FOC 设计里几乎都用等幅值变换,原因很实际:如果采用等功率变换,SVPWM 环节得到的矢量幅值会变成相电压峰值的 1.5 倍,直接冲出空间矢量的圆圈,产生过调制。这是一个典型的“数学上更优雅、工程上更翻车”的例子。等幅值变换还有个附带好处:由于 Ia+Ib+Ic=0,i_alpha 可以化简为只含 ia 和 ib 的形式,采样电路只需要采 A、B 两相电流,省掉一路运放和 ADC 通道,成本和复杂度都降下来。这是设计取舍落到硬件上的直接体现。
2.3 程序实现里的定点细节
看一段典型的定点实现:
alphaBeta_t Clark(abc_t input){ alphaBeta_t output; output.i_alpha = input.ia; // I_alpha = Ia output.i_beta = (input.ia + 2*input.ib) * 0.577350269; // I_beta = (Ia+2Ib)/sqrt(3) return output; }这里 0.577350269 就是 1/√3。为什么 i_beta 能写成 (Ia+2Ib)/√3?因为把 ic = -Ia-Ib 代入 β 分量表达式后化简得到。这种化简在定点运算里很关键:少一次乘法和一次减法,在中断里每毫秒跑几千次,累积起来就是 CPU 占用率的差别。注意这段代码默认输入已经满足 Ia+Ib+Ic=0,如果采样有偏置误差导致三者和不为零,β 分量就会引入一个直流偏置,表现为电流环里一个甩不掉的稳态误差,这是后面避坑章节要展开的点。
3. Park 变换:旋转坐标系为什么能把交流量变成直流量
3.1 线性化的本质是换参考系
Clark 之后得到的 α、β 分量仍然是随转子旋转的正弦量,对 PI 控制器来说,跟踪一个正弦给定永远存在幅值和相位误差,这就是为什么直接在三相或两相静止坐标系上做电流环效果很差。Park 变换的思路是:把参考坐标系绑到转子上,跟着它一起转。在转子看来,定子电流矢量是静止的,d 轴分量和 q 轴分量都是直流量,PI 控制器对直流给定的跟踪是无静差的。
数学上这就是一个旋转矩阵:
# Park 变换:alpha-beta -> d-q,theta 为电角度 def park(i_alpha, i_beta, theta): cos_t = math.cos(theta) sin_t = math.sin(theta) i_d = i_alpha * cos_t + i_beta * sin_t # d 轴:与转子磁链同向 i_q = -i_alpha * sin_t + i_beta * cos_t # q 轴:超前 d 轴 90 度,产生转矩 return i_d, i_q矩阵形式是 [[cosθ, sinθ], [-sinθ, cosθ]],行列式为 1,是一个纯旋转。i_d 控制励磁,i_q 控制转矩,两者解耦之后可以分别用两个 PI 环独立调节,这就是“磁场定向”四个字的真正含义。很多教程只贴公式不讲为什么,结果读者记不住正负号;只要记住这是一个把静止系投影到旋转系的旋转矩阵,符号就不会写反。
3.2 定点实现与 Q15 格式
实际代码里三角函数不会实时算,而是查表加插值:
dq_t Park(alphaBeta_t input, int16_t theta){ dq_t output; Trig_Components TempSinCos = Trig_Functions(theta); // 查表得 sin/cos float cos_da = (float)(TempSinCos.hCos * DIV_Q15); // Q15 转浮点 float sin_da = (float)(TempSinCos.hSin * DIV_Q15); output.id = input.i_alpha*cos_da + input.i_beta*sin_da; output.iq = -input.i_alpha*sin_da + input.i_beta*cos_da; return output; }hCos、hSin 是 Q15 定点数,范围 -32768 到 32767,乘以 DIV_Q15(即 1/32768)还原成 -1 到 1 的浮点。这里有个血泪经验:如果电角度 θ 的标定和实际转子位置差了一个固定偏移,Park 出来的 i_d 和 i_q 就会互相串扰,表现为给 i_q 阶跃时 i_d 也跟着跳,电流环怎么调都不对。这不是 PID 参数问题,是角度对齐问题,后面会专门讲怎么排查。
3.3 Park 逆变换:把控制量送回静止系
PI 环算出的 V_d、V_q 还在旋转系里,必须转回 α、β 才能交给 SVPWM。旋转矩阵是正交矩阵,逆等于转置,所以逆变换就是把矩阵转置一下:
alphaBeta_t Rev_Park(dq_t input, int16_t theta){ alphaBeta_t output; Trig_Components TempSinCos = Trig_Functions(theta); float cos_da = (float)(TempSinCos.hCos * DIV_Q15); float sin_da = (float)(TempSinCos.hSin * DIV_Q15); output.i_alpha = input.id*cos_da - input.iq*sin_da; output.i_beta = input.id*sin_da + input.iq*cos_da; return output; }对比正变换,符号从“加 sin”变成“减 sin”,这就是转置的直接结果。正逆变换共用同一张 sin/cos 表,省一次查表开销。到这里,从三相采样到两相控制量、再回到两相给定,整条链路的数学闭环就完整了,剩下的问题只是“怎么把这两个电压矢量变成六路 PWM”。
4. SVPWM:平均值等效原理与扇区作用时间的推导
4.1 六个基本矢量与伏秒平衡
三相逆变桥有 6 个开关管,每个桥臂上下互补,共 8 种开关状态,对应 8 个基本电压矢量:6 个非零矢量(幅值 2/3·Udc,方向间隔 60°)和 2 个零矢量。SVPWM 的理论基础是平均值等效:在一个 PWM 周期 T 内,用相邻两个非零矢量加零矢量按时间加权,合成出期望的参考电压矢量,使平均值与给定相等。用伏秒平衡写出来就是:
U_ref·T = U_x·T_x + U_y·T_y + U_0·T_0,且 T_x + T_y + T_0 = T
其中 U_x、U_y 是所在扇区的两个相邻非零矢量。这个式子的物理意义很直白:一个周期内“电压对时间的积分”要等于期望矢量对时间的积分,这样电机绕组看到的平均磁通才和理想圆轨迹一致。
4.2 扇区判断:三个不等式定出 N
参考矢量落在哪个 60° 扇区,用三个中间变量判断:
// 扇区判断:Uref1/Uref2/Uref3 三个投影量的符号决定 A/B/C float Uref1 = U_beta; float Uref2 = 0.8660254*U_alpha - 0.5*U_beta; // sqrt(3)/2 * Ua - 1/2 * Ub float Uref3 = -0.8660254*U_alpha - 0.5*U_beta; int A = (Uref1 > 0) ? 1 : 0; int B = (Uref2 > 0) ? 1 : 0; int C = (Uref3 > 0) ? 1 : 0; int N = A + 2*B + 4*C; // N 与扇区一一对应这三个式子的几何来源是把参考矢量分别投影到三个相隔 60° 的法线方向上,符号组合唯一确定扇区。N 的取值 1 到 6 对应扇区 I 到 VI,映射关系是固定的,写代码时直接查表即可。这里容易踩的坑是 Uref2、Uref3 的系数符号写反,导致扇区整体偏移 60°,表现为输出电压波形畸变、电流出现六次谐波。
4.3 作用时间:两种推导路径
作用时间的计算有两种常见方法。方法一从正弦定理出发,在扇区 I 内由几何关系直接得到:
T_6 = (√3·T/Udc)·U_beta T_4 = (√3·T/Udc)·(√3/2·U_alpha - 1/2·U_beta) T_0 = T_7 = (T - T_4 - T_6)/2
方法二则把参考矢量投影到 α、β 轴,令两轴分量相等,解线性方程组,得到形式上略有不同但数学等价的结果。两种方法在扇区边界处的数值精度表现不同,方法二在定点实现里更稳,因为它避免了 sinθ 的直接计算,全部用 U_alpha、U_beta 的线性组合。定义调制比 m = √3·|U_ref|/Udc,线性调制区要求 m ≤ 1,对应 |U_ref| ≤ Udc/√3。对比 SPWM 的调制比上限 1(对应相电压峰值 Udc/2),SVPWM 的直流母线利用率高出约 15.47%,这就是它在电机驱动里几乎取代 SPWM 的根本原因。
4.4 七段式开关顺序与马鞍波
零矢量的分配有自由度,但为了降低开关损耗和谐波,通常采用七段式对称序列,例如扇区 I 用 0→4→6→7→7→6→4→0。每次状态切换只改变一个桥臂,且零矢量时间平均分给两个零状态,使 PWM 波形对称。这样做的直接结果是相电压波形呈现“马鞍波”形状——顶部被削平再凹陷,这正是注入零序分量后的效果。从频域看,马鞍波等效于在正弦调制波上叠加了一个三次谐波零序分量,把相电压峰值压低了,从而在不增加母线电压的前提下多榨出 15% 的基波幅值。理解这一点,就明白 SVPWM 和“零序注入 SPWM”本质上是同一件事的两种实现路径。
5. 避坑与排查:那些让电流环调不好的真实原因
5.1 现象:给 i_q 阶跃,i_d 跟着跳
原因通常是电角度零位没对齐。Park 变换依赖 θ 的绝对准确性,如果编码器零位和转子 d 轴差了一个固定角,d、q 轴之间就存在交叉耦合,i_q 的阶跃会通过旋转矩阵漏到 i_d 上。解决方法是做一次角度标定:强制 i_d = 某值、i_q = 0,观察转子被拉到 d 轴方向,记录此时的角度读数作为零偏,写进代码补偿。
5.2 现象:低速时电流波形有周期性畸变,高速反而正常
多半是死区补偿没做或做错。逆变桥上下管切换时必须留死区防止直通,死区会导致实际输出电压与给定有偏差,低速时反电动势小,这个偏差占比大,畸变明显。解决思路是根据相电流方向对占空比做前馈补偿,电流为正时补一点、为负时减一点,补偿量约等于死区时间除以 PWM 周期再乘 Udc。
5.3 现象:积分项越积越大,退饱和后响应迟钝
这是积分饱和的典型表现。当给定突变或负载冲击导致误差长时间同号,积分项会累积到远超输出限幅的值,等误差反向时积分还要“爬”很久才能退出饱和,表现为明显的滞后。常见做法是积分抗饱和:输出限幅时停止积分累加,或者采用反计算法把超出的部分反馈回积分器。位置式 PID 和增量式 PID 在这件事上的表现不同,增量式天然带一点抗饱和能力,但位置式配合显式限幅更直观。
5.4 现象:SVPWM 输出在高速时出现波形削顶
这是过调制。当 |U_ref| 超过 Udc/√3,作用时间 T_x + T_y 会大于 T,零矢量时间为负,代码里如果不做钳位就会算出非法占空比。解决方法是加过调制处理:按比例缩放 T_x、T_y 使其和不超过 T,或者切换到六步模式。磁链圆限制里的 MAX_MODULE 就是干这个的,它把参考矢量幅值钳在圆圈内,防止数值溢出。
5.5 现象:定点运算下电流环偶尔发散
检查 Q15 格式的乘法是否溢出。i_alpha 和 cos_da 相乘时,如果两者都接近满量程,中间结果会超出 32 位定点范围。常见做法是先做移位再乘,或者用饱和加法指令。这类问题在仿真里看不出来,只有上实物才暴露,属于典型的“仿真一时爽,上板火葬场”。
6. 磁链圆限制与设计取舍:为什么工程上总要留一手
磁链圆限制的核心是把参考电压矢量的模长钳在一个圆内,防止过调制和磁饱和。确定 MAX_MODULE 时,理论值是 Udc/√3,但实际工程里通常再留 5% 到 10% 的裕量,因为母线电压有纹波、死区补偿有误差、采样有噪声,贴着理论边界跑迟早翻车。START_INDEX 则用于在过调制区做平滑过渡,避免矢量幅值突变引起的转矩冲击。
这里有一个值得停下来想的设计取舍:等幅值变换让 SVPWM 的矢量幅值刚好落在圆圈内,数学上干净;恒功率变换让功率计算更直接,但矢量幅值放大 1.5 倍,必须额外做缩放才能不过调制。选哪个不是对错问题,而是你更愿意把复杂度放在变换环节还是调制环节。我一般倾向于等幅值,因为调试时示波器上看到的矢量幅值直接对应相电压峰值,排查问题少一层换算。
验证这套逻辑是否真的跑通,有个笨但有效的办法:在空载下给定一个恒定的 i_q,让电机匀速转,用示波器看三相电流是否正弦、看马鞍波顶部是否对称、看 i_d 是否稳定在零附近。三个现象同时成立,说明 Clark、Park、SVPWM、角度对齐整条链路都是通的;任何一个不对,就按第 5 章的排查顺序往回倒。从那以后我每次改完变换系数或扇区表,都会强制走一遍这个三步验证,不再靠“感觉波形还行”来判断。希望帮到你。
本文还有配套的精品资源,点击获取