☰
FOC闭环控制全链路拆解:坐标变换、SVPWM与PID限幅
2026/10/31 3:14:49 网站建设 项目流程

简介:一份围绕FOC(磁场定向控制)整理的PDF学习笔记,面向正在入门或已具备一定电机控制基础的嵌入式与算法学习者,用于打通从数学推导到代码实现的完整链路。全包仅1个PDF文件,约7.34MB,内容按专题分章组织,便于顺序阅读或按需查阅。

笔记从FOC核心思想与超前角控制讲起,逐项展开Clark变换的等幅值、恒功率系数推导及程序实现,Park变换与逆变换的数学过程,SVPWM的扇区判断、非零与零矢量作用时间计算、过调制处理、扇区切换点确定,并解释马鞍波成因与零序分量注入SPWM算法。PID部分覆盖离散化、位置式与增量式差异、参数整定及积分抗饱和的成因与应对;末尾给出磁链圆限制中MAX_MODULE与START_INDEX的取值思路和实现。已有1920人学习。

1. FOC 知识地图:五个模块串成一条控制链

学 FOC 最容易犯的错,是把它当成五个孤立的知识点:Clark 变换、Park 变换、SVPWM、PID、磁链圆限制。真正串起来看,它是一条闭环链:采样三相电流 → Clark 降维到 αβ 静止坐标系 → Park 旋转到 dq 坐标系 → 两个 PI 调节器分别把 Id、Iq 拉到目标值 → 反 Park 回到 αβ 坐标系 → SVPWM 合成电压矢量 → 输出六路 PWM。整条链路只有一个目的:让定子磁场始终超前转子 d 轴 90 度,也就是把电流全部压在 q 轴上,扭矩最大、效率最高。

这份笔记的价值,在于它把每个模块的数学推导、程序实现和典型问题都完整写了一遍,尤其是几大变换的系数选择、SVPWM 的扇区与作用时间两种推法、积分抗饱和和磁链圆限制。它不面向零基础:如果你还没搞清 dq 轴是什么,读起来会很吃力;但如果你已经能跑通一套开环控制、正在被波形不对、电流上不去这类问题卡住,这份笔记基本就是一份能对照查错的清单。数学推导和仿真模型两边都覆盖到了,动手前先看一眼,能省掉大量试错时间。

2. 坐标变换:Clark、Park 与反 Park 的推导和实现

2.1 Clark 变换到底在降什么维

三相绕组在空间上相差 120 度,三个电流互相耦合,任意时刻只有两个自由度(因为三相对称,三相电流之和恒为零)。Clark 变换做的事情,就是用基尔霍夫电流定律这个约束,把三个变量压成两个:一个落在 A 相轴线上叫 Iα,一个与其垂直叫 Iβ。

推导起点是节点电流守恒:Ia + Ib + Ic = 0,所以Ic = −Ia − Ib,三元方程组直接降成二元。再按空间投影关系写出来就是:

Iα = k · (Ia − Ib/2 − Ic/2) Iβ = k · (√3/2) · (Ib − Ic)

把Ic代进去,得到实际工程里最常用的那一版:

Iα = k · (3/2) · Ia Iβ = k · (√3/2) · (Ia + 2Ib)

这一步的意义不只是少算一个变量。它把「三相相位差 120 度、难以辨明和控制」的波形,变成了两个正交的直流量,后面的旋转、解耦、闭环才有地方下手。工程上还有个附带好处:采样电路只需要两路电流传感器,第三相用 KCL 反推,成本和复杂度都下来了。

2.2 等幅值变换与恒功率变换:系数怎么选

k 取多少,直接决定了整个系统的数值标定。两种取法各有依据:

变换类型系数 k变换后幅值功率关系适用场景
等幅值变换2/3与原相电流峰值相等功率不守恒,差 3/2 倍FOC 主流选择
恒功率变换√(2/3) ≈ 0.8165放大 √(3/2) 倍变换前后功率相等理论分析、功率计算

验证方法很直接:代入Ia = 1, Ib = −0.5, Ic = −0.5(三相对称峰值),算出 Iα 应该等于 1 还是 1.225,就能反推 k。等幅值系数下Iα = 1.5k = 1,得k = 2/3;恒功率系数下1.5k = √(3/2),得k = √(2/3)。

FOC 里几乎一律选等幅值。原因不在电流环本身,而在 SVPWM:如果采用恒功率变换,送到 SVPWM 的电压矢量幅值会变成相电压峰值的 1.5 倍,直接顶出六边形内切圆,产生过调制,波形削顶。这一点是很多人推导时没感觉、上机才出问题的地方。

2.3 Park 变换:把坐标系焊在转子上

Clark 之后电压电流已经是两维了,但仍是交流量,PI 控制器对正弦给定永远存在稳态误差,没法做到无静差。Park 变换解决的就是这个问题:把参考坐标系从静止的 αβ 轴,换成跟着转子一起转的 dq 轴。

转过 θ 角之后,原本在 αβ 里画圆的量,在 dq 里就变成了两个常数。稳态下 Id、Iq 都是直线,PI 的积分项才能把误差真正积掉。这也是整个 FOC 之所以叫「磁场定向」的关键一步:定向的参照物就是转子磁链方向。

数学上就是一次旋转投影:

Id = Iα·cosθ + Iβ·sinθ Iq = −Iα·sinθ + Iβ·cosθ

注意 θ 是电角度,不是机械角度,两者差一个极对数。如果电机是多极对,机械角乘以极对数才是电角度;这个换算漏掉,波形会周期性乱掉,且极对数越多错得越离谱。

2.4 反 Park 与代码实现:定点数才是真正容易出错的地方

反 Park 是 Park 的逆运算。写成矩阵形式时,正变换的矩阵是正交矩阵(旋转矩阵),它的逆矩阵等于转置,所以系数不需要求逆,直接交换 sin 的符号即可:

Iα = Id·cosθ − Iq·sinθ Iβ = Id·sinθ + Iq·cosθ

实际代码里,角度通常以 Q15 定点格式存放,sin/cos 查表出来是 int16,取值范围 ±32768。必须显式换算成浮点或 Q 格式再参与乘法,否则结果会被截断成 0 或溢出:

// Park 变换:αβ -> dq,theta 为电角度(Q15 标幺) dq_t Park(alphaBeta_t input, int16_t theta) { dq_t output; Trig_Components sc = Trig_Functions(theta); // 查表得到 sin/cos,int16 范围 float cos_da = (float)(sc.hCos * DIV_Q15); // DIV_Q15 = 1/32768,归一化 float sin_da = (float)(sc.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; }

这段代码里有三个参数值得盯住:DIV_Q15是归一化因子,漏乘就是数值差 32768 倍;Trig_Functions返回的是有符号定点值,角度跨过 0 和 2π 边界时符号翻转必须正确;hCos/hSin的名字别搞反,写反了电机能转但力矩方向是错的,表现为电流很大、转速上不去。

反 Park 同理,只是把 id、iq 换成 i_alpha、i_beta 的目标值。两段代码合起来看,会发现一个对称性:正变换和反变换的差别只在 sin 项的正负,这也是旋转矩阵正交性的直接体现。写代码时把这个对称性对齐,调参阶段能少怀疑一半的符号问题。

3. SVPWM:扇区判断、作用时间与七段式开关顺序

3.1 平均值等效:SVPWM 到底在合成什么

三相逆变桥有六个开关管,共有 8 种开关组合。其中 6 种是有效状态,对应 6 个幅值为2Udc/3的基本电压矢量,空间上依次相差 60 度;另外 2 种是上下桥臂全通或全断,输出为零,叫零矢量。

SVPWM 的理论基础是平均值等效原理:在一个开关周期内,用相邻的两个非零矢量加零矢量,按时间加权,合成出任意方向、任意幅值的参考电压矢量。写成伏秒平衡就是:

Uref · Ts = Ux · T1 + Uy · T2 + U0 · T0 Ts = T1 + T2 + T0

因为只有两个非零矢量参与合成,所以参考矢量必须落在相邻两矢量张成的 60 度扇区里,这也是必须先判扇区的原因。合成出来的矢量以固定角速度旋转、幅值不变,逼近理想磁链圆,实际磁通就跟着这个圆走。

这里有个容易被忽略的换算:基本矢量幅值是2Udc/3,而相电压峰值是Udc/√3。两个数不一样,推导作用时间时用哪个,取决于你把参考矢量定义在什么标度上。混用会直接导致输出电压偏大或偏小 15% 以上。

3.2 扇区判断:三个参考量与 N 值表

判扇区的做法不是算 atan2,那样太慢。工程做法是用三个线性组合的符号来编码:

Uref1 = Uβ Uref2 = (√3/2)·Uα − (1/2)·Uβ Uref3 = −(√3/2)·Uα − (1/2)·Uβ

分别判断三者是否大于零,得到 A、B、C 三个布尔量,再按N = A + 2B + 4C编码。N 与扇区的对应关系是固定的:

N315462
扇区IIIIIIIVVVI

这个表的顺序不是 1 到 6,很多人第一眼看觉得莫名其妙。它其实是三根轴的符号组合按二进制排出来的结果,背下来比推导更划算。代码里通常直接写一个 switch,把 N 映射成扇区号,再用扇区号去索引后面的时间计算。

// 扇区判断:只需要三次比较,无三角函数、无开方 uint8_t Sector_Detect(float Ualpha, float Ubeta) { float u1 = Ubeta; float u2 = 0.8660254f * Ualpha - 0.5f * Ubeta; // √3/2 与 1/2 float u3 = -0.8660254f * Ualpha - 0.5f * Ubeta; uint8_t A = (u1 > 0.0f) ? 1 : 0; uint8_t B = (u2 > 0.0f) ? 1 : 0; uint8_t C = (u3 > 0.0f) ? 1 : 0; return A + 2 * B + 4 * C; // 得到 N,再查表换成扇区号 }

3.3 作用时间:方法一与方法二其实是同一件事

以扇区 I 为例,相邻矢量是 U4 和 U6。用正弦定理列方程,解出两个非零矢量的作用时间:

T6 = (√3 · Ts / Udc) · Uβ T4 = (√3 · Ts / Udc) · (√3/2 · Uα − 1/2 · Uβ) T0 = T7 = (Ts − T4 − T6) / 2

令k = √3·Ts/Udc,就是笔记里那张速查表的来源。六个扇区各推一遍会得到六组式子,但结构高度重复:每个扇区都能写成k乘以某两个参考量的组合。方法二走的是另一条路,先把参考矢量投影到两个基本矢量方向上,用两轴分量相等列方程,解出来的结果和方法一逐项对应,只是中间变量换了名字。

实际写代码时不必抄六组公式,而是先算三个通用量:

X = k · Uβ Y = k · (√3/2 · Uα + 1/2 · Uβ) Z = k · (−√3/2 · Uα + 1/2 · Uβ)

再按扇区号做一次查表,决定 T1、T2 取 X、Y、Z 中的哪两个(含符号)。这样代码只有十几行,六个扇区共用一套逻辑,出错面小得多。这是整份笔记里最值得抄进工程的一段结构。

扇区IIIIIIIVVVI
T1−ZZX−X−YY
T2XY−YZ−Z−X

3.4 七段式开关顺序与切换点

T1、T2 只是时长,还要决定这七段怎么排。分配原则有两条:每次切换只改变一相的开关状态;零矢量时间平均分给 T0 和 T7,让 PWM 波形对称。满足这两条就能最大限度压低开关损耗和諧波。

扇区 I 的顺序是0 → 4 → 6 → 7 → 7 → 6 → 4 → 0,对应三相占空比按Ta ≤ Tb ≤ Tc排列。六个扇区排完,会得到一个规律:每个扇区的切换点都是(Ts − T1 − T2)/4、+T1/2、+T2/2这三个值的不同组合。所以代码里通常只算三个比较值 Tcm1、Tcm2、Tcm3,再按扇区号分配到三个通道。

这里是最容易出错的地方:T1、T2 的顺序是按开关切换顺序给的,不是按矢量编号大小给的。扇区 I 里先作用的是 U6 后是 U4,写反了波形仍然对称、电机仍然能转,但电流谐波明显变大,示波器上看相电流会有毛刺。这种问题不会报错,只能靠对比波形发现。

3.5 过调制处理与马鞍波的来源

调制比定义为m = √3·|Uref| / Udc。要保证合成矢量不超出六边形内切圆,必须m ≤ 1,即|Uref| ≤ Udc/√3 ≈ 0.577·Udc。而 SPWM 的线性区上限是Udc/2,两者比值2/√3 ≈ 1.1547,这就是 SVPWM 母线电压利用率高出 15.47% 的由来。

超过这个值就进入过调制区:T1 + T2 > Ts,零矢量时间为负。处理方法有两类,一类是等比缩放让 T1 + T2 正好等于 Ts,牺牲波形保真度换取幅值;另一类是直接截断,把超出部分丢掉。前者线性度好但输出电压达不到理论值,后者幅值到位但谐波急剧上升,还可能引起电流环振荡。

至于常说的「马鞍波」,本质是零序分量注入。理想的 SVPWM 相电压波形不像正弦,而是在正弦上叠加了一个三次谐波形状的分量,峰值被削平,看起来像马鞍。反过来看,如果用 SPWM 生成三相正弦,再往三相调制波里注入−(max + min)/2这个零序分量,得到的结果与 SVPWM 完全等价。这条等价关系很值得记住:它把「空间矢量」这个几何视角和「载波调制」这个时域视角连了起来,调波形时两边可以互相验证。

4. 两个限幅:PID 积分抗饱和与磁链圆限制

4.1 离散化:位置式与增量式的差别

连续域的 PID 是u(t) = Kp·e(t) + Ki·∫e dt + Kd·de/dt。搬到 MCU 上要离散化,积分变累加、微分变差分,于是分成两种写法。位置式直接算输出绝对值:

u(k) = Kp·e(k) + Ki·Σe(j) + Kd·(e(k) − e(k−1))

增量式算的是本次输出相对上次的增量:

Δu(k) = Kp·(e(k) − e(k−1)) + Ki·e(k) + Kd·(e(k) − 2e(k−1) + e(k−2)) u(k) = u(k−1) + Δu(k)

两者在数学上等价,工程表现差别很大:

对比项位置式增量式
输出形式直接输出控制量输出增量,需累加
积分项全量累加,易饱和只累加本次误差,天然限幅
切换冲击从手动切自动时冲击大冲击小,易做无扰切换
计算量需要保存历史误差只需保存前两次误差
典型用途速度环、位置环电流环

4.2 电流环为什么常用增量式

电流环的采样频率高(通常与 PWM 同频,十几 kHz 以上),给定变化快,且直接对应硬件占空比。用增量式有三个实际好处:积分项只是Ki·e(k)一项,不会无限累加,天然抗饱和;输出的是占空比增量,累加后自然落在[0, Ts]区间内;从使能切到运行状态时不需要清积分,切换平滑。

代价是必须保证u(k−1)的初值正确。初值给 0,第一次使能时输出会有一个爬升过程;初值给上一次的占空比,切换就无扰。这个小细节在调试时表现为「上电瞬间电机抖一下」,很难从波形上定位。

4.3 参数整定:从内环到外环

整定顺序不能反,必须先电流环、再速度环、最后位置环。原因是外环的输出是内环的给定,内环没调好,外环调出来的参数全是错的。

电流环的带宽通常取开关频率的 1/10 到 1/20,比例增益按电感量反推:Kp ≈ L · ωc,积分增益按电阻和带宽算Ki ≈ R · ωc。这样算出来的值只能当起点,实际还要考虑采样延迟和 PWM 更新延迟,通常要把 Kp 再往下压一点,否则容易出现高频振荡。

速度环的带宽一般取电流环的 1/5 到 1/10。位置环再低一个数量级。三级带宽拉开,系统才不会互相打架。整定时先把 Ki 置零,只加 Kp 到临界振荡,再回退到 60% 左右,最后加 Ki 消除稳态误差——这个流程很老套,但跳过它直接试参数,多半会陷入「调好电流环速度环又坏了」的循环。

4.4 积分饱和:概念、后果与三种抗法

积分饱和指的是:执行机构已经到物理极限(比如占空比已经 100%),但误差还在,积分项继续累加,越积越大。等误差反向时,积分项需要很长时间才能退回去,这段时间里系统完全失控,表现为大幅超调和长时间振荡。

三种常见处理方式:

积分钳位(clamping):输出饱和时停止积分累加,只在未饱和时更新积分项。实现最简单,一行判断即可,缺点是在饱和边界附近可能出现抖动。

积分分离:误差绝对值超过阈值时把 Ki 置零,只用 PD;误差回落到阈值内再恢复积分。适合启动、突加负载这类大误差场景,但阈值不好定,定小了退饱和慢,定大了等于没做。

反计算(back-calculation):把饱和前后输出的差值,乘一个系数反馈回积分项:

u_sat = clamp(u, umin, umax) integral += Ki·e + Kt·(u_sat − u) // Kt 为反计算系数,常取 1/Tt

这种做法过渡最平滑,代价是多一个需要整定的参数 Kt。工程上电流环用钳位就够,速度环和位置环用反计算效果更好。

4.5 磁链圆限制:MAX_MODULE 与 START_INDEX

磁链圆限制是在反 Park 之后、SVPWM 之前加的一道保护:把参考电压矢量的幅值限制在六边形内切圆之内,防止过调制。原理很朴素——如果|Uref| > MAX_MODULE,就按比例同乘一个缩放因子,方向不变、幅值压回边界。

MAX_MODULE 的取值由母线电压决定:理论上限是Udc/√3,实际要留 3% 到 5% 的余量,因为采样误差、死区补偿误差和母线波动都会让实际值偏大。如果是 Q15 定点实现,还要换算成对应的标幺值,换算错了限制就形同没做。

START_INDEX 解决的是另一个问题:限圆需要算幅值,也就是开方。MCU 上做浮点开方开销不小,所以常见做法是设一个阈值——幅值低于 START_INDEX 时直接跳过限圆判断,因为小矢量本来就不会超界。这个阈值定得太低,起不到省算力的作用;定得太高,会在临界区出现限圆不连续,波形上看到的是电压矢量幅值跳变。

限圆代码的核心就几行,但边界条件必须写全:

// 磁链圆限幅:超界则等比缩放,方向不变 float module = sqrtf(Ualpha * Ualpha + Ubeta * Ubeta); if (module > MAX_MODULE) { float scale = MAX_MODULE / module; // 只在超界时才算除法 Ualpha *= scale; Ubeta *= scale; }

注意MAX_MODULE是幅值不是平方值,比较时两边标度必须一致。见过把平方值拿去和幅值比较的写法,结果限幅阈值偏小一个量级,电机电流永远上不去,查了很久才发现是量纲问题。

5. 易错点:五个最容易出问题的细节

5.1 电角度零点与方向定义错位

易漏点:电角度的零点取在哪根轴上、正方向是顺时针还是逆时针,笔记里可以随便定,但代码里必须自洽。

为什么漏:很多人以为角度只是给 Park 变换用的,实际上它同时影响 Park、反 Park、SVPWM 扇区判断、以及编码器安装偏置的补偿。四处用的角度定义稍有不一致,系统仍然能闭环,只是力矩偏小、效率下降,看不出明显故障。

怎么补:固定一个定义后,做一次开环验证——给固定的 Ud、Uq(比如 Ud=0,Uq=一个小值),手动转转子一圈,观察电流波形是否始终与反电动势同相。如果相位差固定偏移,就是零点偏置问题;如果相位差随角度变化,就是方向定义反了。

5.2 等幅值与恒功率系数混用

易漏点:Clark 换了系数,但 SVPWM 那边的调制比公式没跟着改。

为什么漏:两处代码在不同文件里,系数以宏或魔数的形式散落各处,改一处忘一处。表现出来是电流环能收敛,但电压利用率偏低或直接过调制削顶。

怎么补:把变换系数、母线电压、调制比上限收敛到一个头文件里,全工程只引用同一个符号。上线前做一次数值对拍:给三相对称电流,算出的 Iα 峰值应等于相电流峰值(等幅值)或 1.225 倍(恒功率),用这个方法一次性验完。

5.3 调制比与矢量幅值混淆

易漏点:m ≤ 1对应的不是|Uref| ≤ Udc/2,而是Udc/√3。

为什么漏:SPWM 的直觉太强,很多人下意识把 Udc/2 当成线性区上限,导致输出电压少了 15%,或者反过来把限幅值设成 Udc,直接进过调制。

怎么补:在代码里把MAX_MODULE = Udc / √3 × 0.95写成带注释的常量,注释里写明推导来源。任何改动这个值的地方都要求同步更新注释,避免下一个人凭直觉改。

5.4 采样时刻与 PWM 更新时刻没对齐

易漏点:电流采样点应该落在下桥臂导通的中间时刻,而不是 PWM 周期边界。周期边界正是开关动作的时刻,采样到的全是开关噪声。

为什么漏:这个属于硬件与软件的配合,纯看代码看不出问题。表现是电流波形毛刺大、PI 输出抖,加滤波能压下去但会引入相位滞后。

怎么补:把 ADC 触发源设成定时器的中点触发,与 PWM 中心对齐模式配合。改完后对比采样波形,毛刺应该明显下降。如果还有残余,再检查采样电阻的走线和运放的建立时间。

5.5 抗饱和只限积分不限输出

易漏点:只做了积分钳位,忘了对最终输出做上下限约束。

为什么漏:积分项确实不涨了,但Kp·e(k)这一项在大误差时本身就足以让输出越界。输出越界后送到 SVPWM 就是过调制,形成新的饱和回路。

怎么补:输出限幅和积分抗饱和必须成对出现,且输出限幅要在最后一道做。检查方法很简单:人为把给定拉到远超量程,观察输出的占空比是否严格停在上下限,同时误差反向时能否在一个控制周期内退出来。

6. 复习清单:把笔记压成一张自测表

复习这类偏数学的控制方向,我不建议从头重读推导,效率太低。更划算的做法是按「能不能默写出来」自测,把不会的挑出来重点看。下面这张表是我自己复盘中总结的检查项,按从输入到输出的顺序排,哪一格卡住就翻回对应章节。

检查项能默写能解释为什么对应模块
Clark 变换的完整推导与 KCL 约束坐标变换
等幅值 / 恒功率系数的验证方法坐标变换
Park 与反 Park 的矩阵关系坐标变换
六个基本矢量的幅值与空间角度SVPWM
扇区判断的 N 值编码与对应表SVPWM
T1、T2 的两种推法与等价性SVPWM
七段式顺序与三个切换点的关系SVPWM
调制比 m 的上限与 15.47% 的来源SVPWM
位置式与增量式的积分项差异PID
三种积分抗饱和的适用场景PID
MAX_MODULE 的取值与余量磁链圆限制
START_INDEX 存在的意义磁链圆限制

自测时有个技巧:不要只看公式,要能说出这个公式在代码里对应哪一行、哪个变量。比如看到T6 = k·Uβ,脑子里要立刻浮现出「k 是 √3Ts/Udc,Uβ 是 Park 反变换输出的第二个分量」。公式和变量对不上的地方,就是理解断层的地方。

还有一条自查路径值得单独走一遍:拿一组具体的数值手算全程。给定Udc = 24V、目标|Uref| = 8V、角度 30 度,从 αβ 分量算到扇区号,再算 T1、T2,最后算出三个占空比。全程手算一遍,比读十遍推导有用——所有量纲、系数、符号的错误都会在这个过程中暴露出来。

从那以后我每次改完变换或 SVPWM 的代码,都强制走一遍「开环给固定矢量 → 示波器看三相占空比 → 和手算值逐个比对」这个流程,不管改动多小。翻车最惨的一次就是改了一个系数宏,觉得不可能有问题,结果调了两天才发现是量纲错了。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询