
简介这份PDF笔记面向正在学习或准备实现FOC磁场定向控制的嵌入式与电机控制学习者系统梳理从数学推导到算法落地的完整链路。内容围绕FOC核心思想与超前角控制展开逐一推导Clark变换、Park变换及逆变换的数学过程并给出等幅值与恒功率两种变换系数的取舍依据SVPWM部分覆盖扇区判断、非零与零矢量作用时间计算、过调制处理及切换点确定还解释了马鞍波成因与零序分量注入的SPWM算法。此外包含PID离散化、位置式与增量式算法对比、参数整定与积分抗饱和方法以及磁链圆限制中MAX_MODULE与START_INDEX的确定思路并附程序实现参考。资源为1个PDF文件压缩包约7.34MB已有1926人学习适合希望把公式推导与代码实现对应起来、查漏补缺的读者按模块研读。1. 从一份手写推导笔记说起FOC 算法到底该怎么啃第一次把电机转起来的时候我盯着示波器上那三根正弦波看了半天心里其实没底——代码是抄的参数是试的SVPWM 的扇区判断写错了两个符号电机照样能转只是噪声大、发热快。后来我才明白FOC 这东西最怕“能跑就行”因为它的每一行代码背后都压着一段数学推导推导错了不一定立刻炸但一定会在某个工况下翻车。这份笔记的价值就在这儿它不教你调库而是把 Clark、Park、SVPWM、PID、磁链圆限制这几块的数学推导和程序实现一条条摆出来从等幅值变换系数为什么是 2/3到扇区矢量切换点怎么定再到积分饱和为什么会让电流环“粘”住不放。适合谁适合已经能让电机转、但说不清为什么这么转的嵌入式开发者也适合刚接触电机控制、想从公式一路走到 C 代码的学生。数学、FOC、Simulink、算法这四个词在这份材料里不是标签是四条互相咬合的线索。2. 核心变换链路从三相电流到 dq 轴的一次完整穿行2.1 先想清楚 FOC 到底在干什么笔记开篇没有急着上公式而是先抛了一个反直觉的结论电角度怎么定义其实无所谓。它可以取 a 轴和 d 轴的夹角也可以取 a 轴和 q 轴的夹角甚至取角平分线都行。原因在于电角度存在的唯一意义是“告诉算法转子现在朝哪”从而生成一个超前转子 90 度的磁场。abc 轴是固定的dq 轴是跟着转子转的只有知道转子相对固定轴的位置才能在旋转坐标系和静止坐标系之间来回投影。所以电角度是一个位置指示器不是一个物理量纲上的绝对角度。那为什么工程上普遍定义成 a 轴和 d 轴的夹角纯粹是为了 Park 和反 Park 变换算起来方便——此时投影只需要在 α 轴和 β 轴之间做乘的系数就是 sin 和 cos 的三角函数值。换成别的定义也能算只是投影关系会多绕一步。笔记里这句话我印象很深所有的变换最终结果都是为了生成超前转子 90 度的磁场。这句话把 Clark、Park、SVPWM 三件事串成了一条线后面所有推导都是为它服务的。超前角控制的原因也在这里。当磁铁磁场的相位比绕组磁场滞后 90 度时转矩最大因为 T Fr·sinθθ 取 90 度时 sinθ 取到 1。这不是玄学是叉乘的几何结果。理解这一点后面调电流环的时候就不会盲目去“加大电流”而是知道该盯的是 iq 和 id 的分配关系。2.2 Clark 变换降维解耦与那个 2/3 的来历Clark 变换的本质是降维。三相电流相位差 120 度彼此耦合难以独立控制变换到 αβ 两相静止坐标系后三维问题变成二维问题。推导起点是基尔霍夫电流定律任一时刻流入节点的电流之和等于流出之和即 Ia Ib Ic 0。有了这个约束三相里只有两相是独立的采样电路只需要采 A 相和 B 相C 相直接算出来——这是实打实的成本优化不是数学游戏。笔记里给出的变换形式是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; }这段代码短得让人怀疑但每一行都有出处。i_alpha 直接取 ia是因为等幅值变换下 α 轴与 a 轴重合投影系数为 1。i_beta 的系数 0.577350269 就是 1/√3来自 β 轴与 b 轴夹角的投影关系配合 Ia Ib Ic 0 消去 ic 后整理得到 (Ia 2Ib)/√3。注意这里用的是等幅值变换不是恒功率变换。两种系数的区别笔记讲得很清楚等幅值变换系数 k 2/3恒功率变换系数 k √(2/3)。判断依据是变换前后幅值是否相等还是功率是否相等。实际 FOC 设计里一般用等幅值变换原因很实际——如果采用等功率变换SVPWM 里得到的矢量会是相电压峰值的 1.5 倍直接超出空间矢量的圆圈产生过调制。这是个典型的“数学上更优雅、工程上更麻烦”的例子血泪经验就是别为了公式好看去选恒功率。提示采样只采两相的前提是三相三线制且中点不引出。如果硬件是三相四线或者有中点电流通路KCL 约束不成立这个简化会引入误差。2.3 Park 变换与反变换把交流量变成直流量Clark 之后虽然降到了二维但 αβ 轴上的量仍然是随转子旋转的交流量PI 控制器对交流量的跟踪存在稳态误差这是控制理论里的老问题。Park 变换的作用就是把这个交流量线性化——把旋转的 dq 轴作为参考坐标系对转子而言坐标系是静止的id 和 iq 呈现为直流量PI 就能做到无静差跟踪。数学上就是一次旋转投影dq_t Park(alphaBeta_t input, int16_t theta) { dq_t output; Trig_Components TempSinCos; float cos_da, sin_da; TempSinCos Trig_Functions(theta); // 查表得到 sin/cos 的 Q15 定点值 cos_da (float)(TempSinCos.hCos * DIV_Q15); // 除以 32768 归一化到 0~1 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; }这里有两个工程细节值得说。第一sin/cos 不是实时算的是查表加定点缩放Trig_Functions 返回的是 Q15 格式-32768 到 32767 表示 -1 到 1DIV_Q15 就是 1/32768。在资源紧张的 MCU 上实时调 sinf() 的开销足以让电流环周期抖起来查表是常见做法。第二theta 是电角度单位要和查表索引对应笔记里没写死实际移植时最容易在这里翻车——角度单位和表长不匹配波形会周期性畸变。反 Park 变换是把 PI 调节后的 ud、uq 转回 αβ 轴矩阵是正交矩阵逆矩阵等于转置所以公式就是把旋转方向反过来alphaBeta_t Rev_Park(dq_t input, int16_t theta) { alphaBeta_t output; Trig_Components TempSinCos; float cos_da, sin_da; TempSinCos Trig_Functions(theta); cos_da (float)(TempSinCos.hCos * DIV_Q15); 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; }对比正变换的符号能看出转置关系正变换里 id 的 β 项是 sin反变换里 i_alpha 的 q 项是 -sin。这个符号如果抄错电机会转但转矩脉动明显而且带载能力下降——又是一个“能跑但不对”的坑。3. SVPWM 实现拆解扇区判断、作用时间与七段式切换3.1 平均值等效原理与六个基本矢量SVPWM 的理论基础是平均值等效在一个开关周期内用基本电压矢量的不同组合让平均值等于给定电压矢量。三相逆变桥有 6 个开关管合法开关状态共 8 种其中 6 个非零矢量U1 到 U6把空间分成 6 个 60 度扇区另外两个是零矢量 U0 和 U7。任意时刻的参考矢量落在某个扇区里就用该扇区相邻的两个非零矢量加零矢量去合成。笔记里推了一个容易被忽略的点非零矢量的幅值是 2Udc/3不是 Udc。推导从三相逆变等效电路出发利用 KCL 和相电压关系最后得到 Ua 2Udc/3、Ub -Udc/3、Uc -Udc/3合成矢量模长 2Udc/3。这个 2/3 和 Clark 变换的 2/3 是同一套等幅值约定下的结果前后自洽。如果这里记成 Udc后面调制比 m 的计算就会整体偏大线性区判断失效。调制比 m √3·|Uref|/Udc线性区上限是 m ≤ 1/√3 ≈ 0.577对应相电压峰值 Udc/√3。笔记里提到 SVPWM 相比 SPWM 母线电压利用率高约 15.47%来源就是 2/√3 ≈ 1.1547。这个数字在选母线电压和电机额定电压匹配时很有用不是背下来的是推出来的。3.2 扇区判断三个不等式和 N 的编码扇区判断不复杂但写法有讲究。定义三个中间量// Uref1 Ubeta // Uref2 (sqrt(3)*Ualpha - Ubeta) / 2 // Uref3 (-sqrt(3)*Ualpha - Ubeta) / 2 // 若 Uref1 0 则 A1否则 A0B、C 同理 // N A 2*B 4*CN 与扇区的对应关系是N3 对应扇区 IN1 对应 IIN5 对应 IIIN4 对应 IVN6 对应 VN2 对应 VI。这个编码不是随便定的是三个半平面判断结果的自然二进制组合。实际写代码时我一般把这三个不等式直接内联避免中间变量命名混乱——Uref1/2/3 和 U1/U2/U3 这些名字在不同资料里指代不同抄来抄去最容易在这里出问题。注意扇区编号从 I 开始还是从 0 开始不同实现不一样。移植时先拿一组已知的 Ualpha、Ubeta 手算一遍确认扇区号对得上再往下调。3.3 作用时间计算两种方法殊途同归笔记给了方法一和方法二两套推导。方法一是从正弦定理出发直接解出 T4、T6 与 |Uref|、θ 的关系方法二是把参考矢量投影到 αβ 轴令两轴分量相等解出 t1、t2。两套方法结果一致只是切入点不同。以扇区 I 为例方法二得到// k sqrt(3) * Ts / Udc // t1 k * (sqrt(3)/2 * Ualpha - 1/2 * Ubeta) // 对应 U4 作用时间 // t2 k * Ubeta // 对应 U6 作用时间 // t0 t7 (Ts - t1 - t2) / 2 // 零矢量均分这里有个符号细节笔记特意标了“注意大小写Tt”大写 T 是矢量作用时间小写 t 是中间计算量两者在推导里交替出现抄公式时混用会导致时间算反。我见过有人把 t1 和 t2 对调电机照样转但电流波形毛刺明显因为七段式的对称性被破坏了。六个扇区的作用时间公式结构相似都是 Ualpha、Ubeta 的线性组合系数是 √3/2 和 1/2 的排列。与其死记六组公式不如记住“相邻两个非零矢量在 αβ 轴上的投影”这个几何图像现场推一遍比背表可靠。3.4 七段式切换与马鞍波基本矢量作用顺序的分配原则只有两条每次开关状态切换只改变一相零矢量时间平均分配。这样得到的 PWM 是对称的谐波分量低。扇区 I 的典型顺序是 0 → 4 → 6 → 7 → 7 → 6 → 4 → 0对应三相占空比的比较值按 t0/2、t0/2t1、t0/2t1t2 这样的节奏排布。马鞍波的形成原因笔记单独列了一节这点很关键。SVPWM 的相电压调制波不是正弦而是顶部带凹陷的马鞍形凹陷来自零序分量的注入。正因为注入了零序母线利用率才比 SPWM 高。笔记还顺带提了基于零序分量注入的 SPWM 算法——它和 SVPWM 在数学上等价只是实现路径不同一个从矢量合成出发一个从调制波注入出发。理解这层等价关系调试时就能用 SPWM 的直觉去解释 SVPWM 的波形。过调制处理是另一个容易漏的点。当 |Uref| 超过线性区上限t1 t2 Ts必须做缩放或者切到六步模式否则波形削顶、电流畸变。笔记里给了处理思路实际代码里我一般先做等比缩放简单可靠六步模式留到高速弱磁段再考虑。4. 控制环落地PID 离散化、积分抗饱和与磁链圆限制4.1 位置式与增量式 PID 的取舍笔记把 PID 的离散化单独拎出来讲因为连续域的公式搬到 MCU 上积分和微分两项的处理方式直接决定控制品质。位置式 PID 输出的是绝对量// 位置式u(k) Kp*e(k) Ki*Σe(j) Kd*(e(k)-e(k-1)) float pid_position(PID_t *p, float err) { p-integral err; // 积分累加 float out p-kp * err p-ki * p-integral p-kd * (err - p-last_err); p-last_err err; return out; }增量式输出的是本次相对上次的增量形式上是位置式的差分天然带抗饱和能力因为积分项不会无限累加——它只影响增量。但增量式对执行机构有记忆要求适合步进类负载电流环这种需要绝对电压指令的场景位置式更直观。笔记里两种都给了实现没有强行推荐这个态度是对的选哪种取决于被控对象。参数整定部分笔记讲的是工程顺序先 P 后 I 再 D电流环带宽要远高于速度环采样周期和滤波延迟要算进相位裕度。这些不是公式能推出来的是调出来的但推导能告诉你为什么调不动——比如积分时间常数小于采样周期积分项就会变成噪声放大器。4.2 积分抗饱和三种方法和对症下药积分饱和的现象很具体给定突变或者负载突变时误差长时间偏大积分项一路累加输出早就顶到限幅了还在涨等误差反向后积分项要花很长时间才能退回来表现为超调大、恢复慢严重时振荡。笔记把负面影响写得很直白没有绕弯子。抗饱和的方法笔记列了几种我按使用频率排一下。第一种是积分限幅给积分累加器设上下界实现最简单但限幅值不好定定小了等效于削弱积分作用。第二种是积分分离误差超过阈值时暂停积分只走 PD误差回到带内再恢复积分适合启动阶段防超调。第三种是反计算抗饱和用输出限幅前后的差值去反向修正积分项// 反计算抗饱和输出被限幅时把超出部分按 Kb 反馈回积分器 float out_raw pid_position(p, err); float out clamp(out_raw, OUT_MIN, OUT_MAX); p-integral p-kb * (out - out_raw); // kb 一般取 1/Ki 量级第三种在电流环里效果最好因为它让积分器“知道”输出已经饱和了不会继续盲目累加。代价是多一个参数 kb 要调。我的习惯是电流环用反计算速度环用积分限幅启动阶段再加一层积分分离三层不冲突。4.3 磁链圆限制MAX_MODULE 与 START_INDEX磁链圆限制这一节是整份笔记里最“工程”的部分。原理不复杂dq 轴的电压指令合成矢量不能超出逆变器能输出的最大圆否则过调制。但怎么限制有讲究——直接对 ud、uq 各自限幅会改变矢量方向导致转矩和磁链解耦失效正确做法是对合成模长限幅保持方向不变。笔记里 MAX_MODULE 的确定和 START_INDEX 的设定是两个关键参数。MAX_MODULE 是允许的最大矢量模长通常取线性区上限再留一点裕量START_INDEX 是开始介入限制的阈值提前一点启动避免在边界上反复触发。程序实现上一般先算模长平方和阈值平方比较超了就按比例缩放 ud、uq// 磁链圆限制保持矢量方向只缩模长 float mod_sq ud * ud uq * uq; if (mod_sq MAX_MODULE_SQ) { float scale MAX_MODULE / sqrtf(mod_sq); ud * scale; uq * scale; }用平方比较省一次开方只有真正超限时才调 sqrtf这是嵌入式里的常规优化。START_INDEX 的作用是让限制平滑介入避免在阈值附近抖动本质上是给限制器加了一段软过渡区。这两个参数没有理论最优值和母线电压、电机参数、采样周期都相关笔记给的是确定方法而不是固定数字这点很实在。5. 避坑与排查那些“能转但不对”的现场记录5.1 电机能转但噪声大、发热快现象上电后电机能转空载电流正常带一点负载就发热声音发闷。原因大概率在 Park 变换的符号或者扇区判断的编码上。正变换里 iq 的 α 项是 -sin如果写成 sinq 轴电流方向反了转矩和磁链的相位关系错位电机靠谐波转矩硬撑。排查方法给定 id0、iq固定值用示波器看三相电流是否对称正弦再看反 Park 后的 αβ 波形是否圆滑。不对称就先查 Clark 的 1/√3 系数圆滑但相位不对就查 Park 符号。5.2 电流环稳态有静差现象iq 给定阶跃后稳定值总差一点加大 Ki 能压下去但超调变大。原因通常是 Park 变换用的电角度和实际转子位置有固定偏差或者 sin/cos 查表的索引偏移。电角度偏差会让 dq 轴不完全对齐iq 里混入 id 分量PI 对混入分量无能为力。排查让电机空载匀速转观察 id 是否在 0 附近波动如果 id 有直流偏置就是角度对齐问题重新校准编码器零位或者查表偏移。5.3 SVPWM 输出在扇区边界跳变现象参考矢量扫过扇区边界时相电流出现尖刺。原因是扇区判断和作用时间计算用了不同的近似边界上 N 值跳变导致矢量选择突变。解决扇区判断的三个不等式加一点滞环或者在作用时间计算后做 t1t2 的饱和检查超了就等比缩放。另外确认七段式的零矢量分配是对称的t0 和 t7 各占一半不对称会在边界放大跳变。5.4 积分饱和导致启动过流现象启动瞬间电流冲得很高然后慢慢回落严重时触发过流保护。原因是启动误差大积分项在输出限幅后继续累加。解决启动阶段用积分分离误差带外只走 PD同时把反计算抗饱和的 kb 调大一点让积分器更快退出饱和。注意 kb 太大会让积分作用变弱稳态精度下降要折中。5.5 磁链限制介入后转矩掉得厉害现象高速段一进弱磁转矩明显下降电流却没上去。原因是 MAX_MODULE 设得太保守或者 START_INDEX 提前太多限制器在还没到线性区上限时就介入白白浪费了母线电压。排查先算理论线性区上限 √3·Udc/3和实际 MAX_MODULE 对比再看 START_INDEX 对应的模长占比。一般 START_INDEX 设在 90% 到 95% 之间留裕量但不浪费。6. 二次开发把这份笔记变成你自己的算法骨架拿到这份材料之后我的做法不是照着抄一遍而是先把它拆成三层数学层、变换层、控制层。数学层是那些推导用来验证你的理解变换层是 Clark、Park、反 Park、SVPWM 这几个纯函数输入输出明确最容易做成单元测试控制层是 PID 和磁链限制和硬件、参数强相关需要台架验证。三层分开改哪层都不容易牵连别处。具体改造上我一般先做一件事把变换层的函数全部改成定点实现和笔记里的浮点版本并行跑用同一组输入对比输出误差。笔记里的 Park 已经用了 Q15 查表但 Clark 和 SVPWM 还是浮点移植到没有 FPU 的 MCU 上时浮点运算的周期抖动会直接影响电流环。定点化的关键是统一 Q 格式比如电流用 Q12、角度用 Q15乘法后右移对齐中间结果留够位宽防溢出。这一步做完电流环周期能从几十微秒压到十几微秒效果立竿见影。第二件事是给 SVPWM 加一层在线自检。具体做法是在每个 PWM 周期结束时用实际写入的比较值反算合成矢量和给定矢量比对模长和相位偏差超过阈值就置一个标志位。这个自检在调试阶段帮我抓到过一次扇区编码错误——电机转得挺正常但反算出来的矢量在扇区边界有 30 度跳变肉眼根本看不出来。上线后这层自检可以保留只做统计不做保护相当于一个黑匣子。第三件事是参数管理。MAX_MODULE、START_INDEX、PID 三组参数、查表长度这些都不应该散落在代码里。我习惯把它们收进一个结构体上电时从 Flash 读调试时通过串口在线改改完写回。这样台架调参不用反复烧录效率高很多。下面是个简化的参数结构typedef struct { float kp_i, ki_i, kd_i; // 电流环 PID float kp_s, ki_s, kd_s; // 速度环 PID float max_module; // 磁链限制最大模长 float start_index; // 限制介入阈值 uint16_t sin_table_len; // 查表长度必须是 2 的幂 } FOC_Params_t;验证方法上我建议按“开环→电流环→速度环→带载”四步走每步都有明确的通过标准。开环看三相电流正弦度和 SVPWM 马鞍波形状电流环看阶跃响应的上升时间和超调速度环看低速平稳度和突加负载的恢复时间带载看温升和母线电流纹波。每一步不过关就停下来查别急着往上叠否则出了问题根本定位不到是哪层。最后说个习惯。从那以后我每次移植 FOC 代码都强制先跑一遍“纯数学回环”给定一组 abc 电流和电角度走完 Clark→Park→反 Park→Clark 反变换看能不能还原回原始值误差在量化精度内才算变换层没问题。这个回环不碰硬件几分钟就能跑完但能挡掉八成以上的符号错误和系数错误。希望帮到你。本文还有配套的精品资源点击获取