简介:这份笔记面向正在学习或实现FOC电机控制的读者,围绕磁场定向控制的核心链路整理数学推导与工程实现思路,适合具备一定电机学、C语言和Simulink基础、希望把公式落到代码中的初中级开发者。内容从FOC本质与超前角控制切入,依次梳理Clark、Park及逆变换的公式推导与程序实现,并展开SVPWM的扇区判断、非零与零矢量作用时间、过调制处理和切换点确定,还涉及PID离散化、位置式与增量式差异、参数整定及积分抗饱和,最后说明磁链圆限制中MAX_MODULE与START_INDEX的确定和实现。包内为1个PDF,压缩包约7.34MB,目录按知识点组织,便于按模块查阅和对照代码练习。已有1920人学习,可作为FOC学习中的公式速查、推导复盘、算法实现参考,也能为课程设计、毕业设计和驱动器调试提供对照。
1. 调 FOC 电流环最难受的时刻,不是电机不转
调 FOC 电流环最难受的时刻,不是电机不转,而是它转得挺顺、相电流看着也像正弦,可给定 iq=1A,实际只有 0.5A,角度还偏了三十度。翻寄存器手册翻了两天没找到问题,最后回到草稿纸把 Clark 变换重推一遍,才发现代码里系数按恒功率取的,SVPWM 那边按等幅值算,两边差了一个因子。这类问题手册不会写,只有把数学推导从头走一遍才看得见。
这份 FOC 学习笔记就是冲着这类问题整理的。它不教电机怎么绕线,而是把 FOC 里绕不开的几块硬骨头——Clark、Park、逆 Park 的数学推导,SVPWM 的扇区判断与作用时间,PID 离散化与积分抗饱和,磁链圆限制——按“先推公式、再写函数”的顺序过一遍,每个结论后面都跟一段可以编译的 C 代码。适合已经能让电机转起来、但说不清每个系数怎么来的嵌入式开发者,也适合刚学电机控制、想把课本公式敲成代码的学生。下面按笔记的组织顺序拆,顺带补上我实际调机时踩过的坑。
2. 笔记的结构:六块内容怎么串成一条从电流采样到 PWM 的链
2.1 六块内容不是并列,是一条单向依赖链
初看目录容易以为六块内容是并列的知识点,实际动手写代码就会发现它们是一条链:FOC 本质决定控制目标(定子电流矢量超前转子 d 轴 90 度),Clark 把三相电流降成 αβ 两相,Park 把 αβ 转到随转子旋转的 dq,PID 在 dq 上做闭环算出 ud、uq,逆 Park 再把 ud、uq 转回 αβ,SVPWM 把 αβ 电压矢量变成三相占空比,磁链圆限制卡在逆 Park 之前,防止合成矢量超出线性调制区。任何一环的坐标系约定不一致,波形就会以“看着对、实际不对”的方式翻车。
笔记的目录大致是这么组织的:
| 模块 | 输入 | 输出 | 关键公式或参数 |
|---|---|---|---|
| FOC 本质 | 转子电角度 | 控制目标 | 电流矢量超前 d 轴 90° |
| Clark | ia、ib、ic | iα、iβ | 等幅值系数 2/3 |
| Park | iα、iβ、θ | id、iq | 正余弦表 |
| PID | id_ref、iq_ref | ud、uq | Kp、Ki、抗饱和 |
| 磁链圆限制 | ud、uq | ud'、uq' | MAX_MODULE、START_INDEX |
| 逆 Park | ud'、uq'、θ | uα、uβ | 正交矩阵转置 |
| SVPWM | uα、uβ、Udc | 三相比较值 | 扇区 N、T1、T2 |
把这张表贴在显示器边上,调机时对着看,比翻笔记正文快。我一般会在每个函数的入口打印一次输入输出,跑一遍开环就能确认哪一段的幅值或角度对不上。
2.2 定点还是浮点:代码里的取舍
笔记里的 Park 函数没有直接调 sinf/cosf,而是走了一张正余弦表,返回 Q15 格式的整数,再乘 1/32768 转成浮点。这样做的好处很直接: Cortex-M0/M3 这类没有 FPU 的芯片,一次 sinf 动辄几百个周期,电流环 20kHz 根本扛不住;查表加一次乘法,几十个周期就够。代价是角度分辨率,表长 1024 点时,最小角约 0.35 度,低速轻载时能看出一点转矩脉动,但对风扇、云台这类负载完全够用。
typedef struct { int16_t hCos; int16_t hSin; } Trig_Components; #define DIV_Q15 (1.0f / 32768.0f) /* hAngle: 0~65535 对应 0~2π,返回 Q15 格式的 sin/cos */ Trig_Components Trig_Functions(int16_t hAngle);这里有两个容易忽略的点。一是角度标幺化的位宽要和定时器一致,如果电角度用 0~65535 表示一圈,那么 65536/1024 就是查表步长,取整时的截断误差会累积成固定角度偏差;二是 hSin、hCos 返回的是有符号 Q15,乘以 DIV_Q15 之后范围是 -1 到 1,别再把符号位当无符号处理。实际工程里我会把表长做成宏,方便在 Flash 紧张的芯片上换成 256 点。
2.3 没有硬件怎么验证:Simulink 对拍加手算单点
笔记里给了 Simulink 仿真的思路,但没有展开。我的做法是三层对拍:第一层手算,固定 θ=30°、ia=1、ib=-0.5、ic=-0.5,手算出 iα=1、iβ=0.866、id=1、iq=0,把结果写死在单元测试里;第二层 MATLAB 对拍,把 C 函数用 MATLAB Function 块包起来,和 Simulink 里搭的三相逆变器加电机模型跑同一组输入,看两条曲线的误差是否在 1e-4 以内;第三层上板,用 DAC 或者 PWM 加 RC 滤波把 iα、iβ、马鞍波引出来,示波器上看形状。
% 单点对拍:固定角度和三相电流,验证 Clark/Park 链路 theta = pi/6; ia = 1.0; ib = -0.5; ic = -0.5; i_alpha = ia; i_beta = (ia + 2*ib)/sqrt(3); id = i_alpha*cos(theta) + i_beta*sin(theta); iq = -i_alpha*sin(theta) + i_beta*cos(theta); fprintf('i_alpha=%.6f i_beta=%.6f id=%.6f iq=%.6f\n', ... i_alpha, i_beta, id, iq);这段脚本的价值不在算得多准,而在于它把“角度单位是弧度还是标幺值”这类低级错误挡在烧板之前。我见过有人把电角度直接按弧度传给查表函数,结果电机堵转发热,查了一下午才定位到单位。
3. Clark 与 Park:两个坐标变换的推导、系数和 C 函数
3.1 Clark 变换:从基尔霍夫电流定律到两行代码
推导的起点很朴素:三相星形接法没有中线,任一时刻 ia+ib+ic=0,三个未知数其实只有两个独立,所以可以降到两维。把三相轴放在平面上,a 轴指向 0°,b 轴 120°,c 轴 240°,把三个电流投影到正交的 α、β 轴上,得到 iα = k(ia - ib/2 - ic/2),iβ = k(√3/2)(ib - ic)。再把 ic = -ia - ib 代进去,就得到笔记里那两行:
typedef struct { float ia, ib, ic; } abc_t; typedef struct { float i_alpha, i_beta; } alphaBeta_t; alphaBeta_t Clark(abc_t input) { alphaBeta_t output; /* 已代入 ic = -ia - ib,等幅值系数 k = 2/3 被消掉 */ output.i_alpha = input.ia; /* 1/√3 ≈ 0.577350269 */ output.i_beta = (input.ia + 2.0f * input.ib) * 0.577350269f; return output; }代码里看不到 2/3,是因为 ic 已经被消掉,系数在化简过程中自然约掉了。这也是很多人第一次看这段代码觉得“少了点什么”的原因。参数上,输入是三相瞬时电流,单位安培,输出 iα、iβ 也是安培;0.577350269 是 1/√3 的浮点近似,如果你用 double 可以写得更长,但在单精度下这个精度足够。采样电路只需要采 a、b 两相,c 相由 KCL 算出来,省一路运放和 ADC,这是笔记里明确提到的成本收益。
3.2 等幅值还是恒功率:2/3 和 √(2/3) 怎么选
系数 k 有两种取法:等幅值取 2/3,恒功率取 √(2/3)。前者的物理意义是变换前后矢量幅值不变,三相电流峰值 1A,变换后 iα、iβ 合成的矢量幅值还是 1A;后者保证变换前后功率相等,但矢量幅值会变成原来的 √(3/2) 倍。笔记里写得很清楚:FOC 里一般用等幅值,因为 SVPWM 的线性调制区是按相电压峰值 Udc/√3 划的,如果 Clark 用了恒功率系数,逆变换出来的电压矢量会大出 1.5 倍,直接冲出磁链圆,表现为过调制、波形削顶。
| 变换类型 | 系数 k | iα 表达式 | 合成矢量幅值 | 适用场景 |
|---|---|---|---|---|
| 等幅值 | 2/3 | ia | 等于相电流峰值 | FOC + SVPWM |
| 恒功率 | √(2/3) | (2/3)(ia - ib/2 - ic/2) 的 √(3/2) 倍 | 相电流峰值的 √(3/2) 倍 | 功率分析、Simulink 功率测量 |
实际项目里我会在头文件里放一个#define CLARK_K,Clark 和逆 Clark 共用同一个宏,避免一边改一边忘。这个习惯来自一次真实的翻车:调试时把 Clark 改成恒功率想看功率波形,忘了改逆变换,电机直接啸叫。
3.3 Park 与逆 Park:正余弦表的精度决定电流环噪声
Park 变换的几何意义是把静止的 αβ 坐标系转到随转子一起转的 dq 坐标系,转轴角度就是电角度 θ。投影公式是 id = iαcosθ + iβsinθ,iq = -iαsinθ + iβcosθ。写成代码就是四个乘法和两个加法,成本很低,真正的成本在 θ 的获取和正余弦的精度上。
typedef struct { float id, iq; } dq_t; dq_t Park(alphaBeta_t input, int16_t theta) { dq_t output; Trig_Components sc = Trig_Functions(theta); float cos_da = sc.hCos * DIV_Q15; /* Q15 转浮点,范围 -1~1 */ float sin_da = 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; } alphaBeta_t Rev_Park(dq_t input, int16_t theta) { alphaBeta_t output; Trig_Components sc = Trig_Functions(theta); float cos_da = sc.hCos * DIV_Q15; float sin_da = sc.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; }参数 theta 用的是 0~65535 的标幺电角度,不是弧度,这一点在函数签名里就该注释清楚。逆变换的矩阵是正交矩阵,逆等于转置,所以只需要把 sin 项的符号翻过来,不需要真的求逆,这也是笔记里强调“电角度定义可以随便,但 dq 轴定义不变”的原因:无论 θ 取 d 轴与 α 轴夹角还是与 a 轴夹角,只要正变换和逆变换用的是同一个 θ,闭环就能成立。
3.4 把三个变换串成一个电流采样中断
单独看每个函数都简单,串起来才是工程。一个典型的电流环中断里,执行顺序是:ADC 采样 ia、ib,Clark 得到 iα、iβ,Park 得到 id、iq,和给定值比较后送 PID,PID 输出 ud、uq,经磁链圆限制,逆 Park 回 αβ,最后送 SVPWM。整条链路里 θ 必须用同一个时刻的值,如果 Park 用的是上一个周期的角度、逆 Park 用的是当前角度,高速时会出现明显的角度滞后,表现为 iq 跟不上给定、效率下降。
我一般会在中断入口先把编码器或观测器的角度锁存到一个局部变量,后面所有变换都用这个副本,中断末尾再更新。这个细节笔记里没有展开,但它是电流环能不能跑满带宽的前提。
4. SVPWM 与电流环:扇区判断、七段式序列和磁链圆限制
4.1 六个基本矢量与伏秒平衡
三相逆变器有六个开关管,上下桥臂互补,有效开关状态有八个:六个非零矢量加两个零矢量。笔记里的编号按三相开关字 S1S2S3 的二进制值来,100 记作 U4、110 记作 U6、010 记作 U2、011 记作 U3、001 记作 U1、101 记作 U5,幅值都是 2Udc/3,相邻矢量间隔 60°。任意一个参考电压矢量 Uref 落在某个 60° 扇区里,都可以用相邻两个非零矢量加零矢量按伏秒平衡合成:Uref·Ts = Ux·Tx + Uy·Ty + U0·T0。
这里 Udc 是母线电压,Ts 是 PWM 周期。笔记里特意推了一遍 2Udc/3 的来历:三相桥臂中点相对电源负极的电压只有 0 和 Udc 两种,但电机绕组中性点浮空,相电压要减去中性点电位,最后合成矢量的幅值就是 2Udc/3。这个推导不难,但跳过去的话,后面算作用时间时很容易把 Udc 和 2Udc/3 搞混。
4.2 扇区判断:N = A + 2B + 4C
扇区判断不用 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 和扇区的对应关系是固定的:3 对应 I,1 对应 II,5 对应 III,4 对应 IV
本文还有配套的精品资源,点击获取