🗺️ FOC 算法完全图解:流程与运行图

把 FOC 一次画全:ISR 级完整流程大图、启动-运行-保护状态机、PWM 中断时序、硬件信号链、SVPWM 扇区、知识思维导图——每张图都配逐框讲解与代码,看完这一页,FOC 在你脑中应该"有图"。
FOC 全景 ISR 流程图 状态机 SVPWM 思维导图 时序图
🎯 本页学习目标
1. 能沿着总流程图,从「ADC 注入组采样完成中断」一口气讲到「写比较寄存器」,说清①~⑧每一步做了什么、对应哪行代码
2. 能解释电流采样为什么必须落在 PWM 中心,以及单电阻采样的窗口约束
3. 能默写 Clark / Park / 反 Park 公式(恒幅值系数 2/3),并说出电流环 Kp、Ki 与带宽 ωc 的换算关系
4. 能画出上电→校准→运行→保护的状态机,说出每个状态的转换条件与故障恢复策略
5. 能按执行时间预算表判断一个 ISR 会不会超时,并说清软硬件两级保护如何配合
建议用时:约 55 分钟(先通读图,再逐节精读,配合自测)

1 一张图看懂 FOC:硬件信号链 + ISR 总流程

FOC(Field Oriented Control,磁场定向控制)在数学上只有一句话:把三相电流搬到与转子同步旋转的 d/q 坐标系里,当成两个直流量用 PI 稳稳拉住。但落到嵌入式上,它是一个以 PWM 周期为节拍、由 ADC 中断驱动的实时循环。本节先看两张大图:第一张是硬件信号链(电流从哪来、PWM 到哪去),第二张是ISR 级总流程(每个中断周期 CPU 依次做的 8 件事)。理论动画版的直观演示见 本站 01-02 · 核心原理动画演示;代码级逐步实现见 本站 04-02 · 固件与电机控制

1.1 两张大图:硬件信号链 + ISR 总流程(全页主角)

第一张是硬件信号链——FOC 的「物理舞台」:下行链路是 MCU 算出的占空比经栅极驱动变成三相桥的开关动作;上行链路是相电流经采样电阻与运放调理回到 ADC,编码器角度回到 MCU——一去一回,闭环才成立。

母线 Udc(12~48V) DC− / GND 采样电阻 + 运放调理 ADC 注入组·同步 MCU Cortex-M4F @170MHz ① 电流采样换算 ② Clark ③ Park ④ PI 电流环(×2) ⑤ 限幅圆 ⑥ 反Park ⑦ SVPWM ⑧ 写 CCRx + 保护 栅极驱动 死区/自举/保护 三相逆变桥(6×MOSFET) U V W PMSM 三相永磁同步 编码器(θ/ω) Ia/Ib 电压信号(上行反馈) 六路互补 PWM(下行) Udc 分压检测(限幅圆用)
图① FOC 硬件信号链:蓝色为运算与数据流,橙色为功率/模拟链路,绿色虚线为反馈。母线电压、相电流、转子角度三路信号齐备,FOC 才有"眼睛"。

第二张是 ISR 级总流程大图——全页的主角图。每个 PWM 周期(10~40kHz 对应 100~25μs),ADC 注入组转换完成都会触发一次中断,CPU 在中断里按①~⑧的固定顺序跑一遍。两个菱形分支尤其要记住:入口的状态检查限幅圆判断——它们是"程序逻辑"与"物理约束"两道闸门。

⚡ ADC 注入组采样完成中断(JEOC) 状态机 = RUN 且无 FAULT ? 置 FAULT / 封波 (MOE=0,六管全关) ① 读相电流 Ia、Ib(ADC 注入组,Ic=−Ia−Ib) ② Clark 变换:Ia,Ib → Iα,Iβ(三相→两相静止) ③ Park 变换(θ 来自编码器)→ Id,Iq(旋转直流量) ④ d/q 轴 PI 电流环(积分抗饱和 + 输出限幅) ⑤ 电压限幅圆:|Us| ≤ Usmax = Udc/√3 |Us| > Usmax ? 等比缩小 Ud、Uq (方向不变,模长压回圆内) ⑥ 反 Park 变换:Vd,Vq → Vα,Vβ(旋转→静止) ⑦ SVPWM:扇区 N → T1,T2,T0 → 三相 CCRx ⑧ 写定时器比较寄存器 CCRx(影子寄存器) 清 ADC/JEOC 标志 → 中断返回(等下一个 PWM 周期) 编码器 θe (电角度,第 7 节) Udc 母线电压 (分压采样,周期更新) 整个循环以 PWM 频率逐拍执行:20kHz → 每拍 50μs,①~⑧ 全部计算 必须在下一拍到来前完成(见第 9 节执行时间预算)
图② FOC ISR 总流程(全页主图):绿色=中断边界,蓝色=坐标变换,橙色=控制器与调制,红色=分支与保护动作。编号①~⑧与第 12 节参考代码逐行对应。

1.2 逐框讲解:①~⑧ 每一步在做什么

编号框名输入 → 输出一句话要点
读相电流ADC 原始码值 → Ia、Ib(A)注入组自动采两相;Ic = −Ia − Ib(基尔霍夫);先减零点偏置再乘增益换算
Clark 变换Ia、Ib → Iα、Iβ三相 120° 系 → 两相正交静止系,变量从 3 个降到 2 个,公式见第 3 节
Park 变换Iα、Iβ + θe → Id、Iq静止系 → 旋转系,正弦量变直流量,PI 才能无静差;θe 是整个系统的"基准"
PI 电流环 ×2Id*/Iq* 与 Id/Iq 误差 → Vd、Vqd 轴目标通常为 0;q 轴目标来自速度环;必须带积分抗饱和与输出限幅
电压限幅圆(Vd,Vq) → 限幅后 (Vd,Vq)逆变器物理上给不出超出 Usmax=Udc/√3 的矢量,先把模长压回圆内再继续
反 ParkVd、Vq + θe → Vα、Vβ把"该施加的电压矢量"变回静止系,是 Park 的转置(负角度旋转)
SVPWMVα、Vβ → 扇区 N、T1/T2/T0、CCRx用相邻两个基本矢量 + 零矢量合成任意矢量;先判扇区再算作用时间
写比较寄存器CCRx → 硬件 PWM写影子寄存器,下一个更新事件原子生效——绝不中途改波形

关键框对应的代码骨架如下(完整 80 行 ISR 见第 12 节):

/* ① 读相电流:先减零偏,再按运放增益换算成安培 */
float ia = ((float)ADC->JDR1 - i_offset_a) * ADC_TO_AMP;
float ib = ((float)ADC->JDR2 - i_offset_b) * ADC_TO_AMP;
float ic = -(ia + ib);                       /* 基尔霍夫: Ia+Ib+Ic=0 */

/* ②③ 变换(等幅值,系数 2/3 见第 3 节) */
float ialpha = ia;
float ibeta  = (ia + 2.0f*ib) * ONE_OVER_SQRT3;
float id =  ialpha*cos_t + ibeta*sin_t;      /* cos_t/sin_t 由查表得到 */
float iq = -ialpha*sin_t + ibeta*cos_t;

/* ④⑤ PI + 限幅(抗饱和细节见第 4 节) */
vd = pi_update(&pi_d, id_ref - id);
vq = pi_update(&pi_q, iq_ref - iq);
limit_circle(&vd, &vq, usmax);               /* |Us| ≤ Udc/√3 */

/* ⑥⑦⑧ 反 Park + SVPWM + 写寄存器 */
valpha = vd*cos_t - vq*sin_t;
vbeta  = vd*sin_t + vq*cos_t;
svpwm_update(valpha, vbeta, udc);            /* 内部写 TIM1->CCR1/2/3 */

1.3 FOC 知识思维导图:八大分支一张网

把 FOC 的知识点挂成一棵树,点分支标题可折叠/展开。每一支在本站都有对应的深入页:坐标变换与三环见 04-02 固件与电机控制,SVPWM 动手实验见 02-13 功率级拓扑与 SVPWM 交互实验室,调参见 02-11 FOC 调参整定实战手册,无感观测器见 04-17 状态观测器全解

FOC 磁场定向控制
📐 坐标变换
Clark / 反 Clark(三相 ↔ αβ)
Park / 反 Park(αβ ↔ dq)
恒幅值系数 2/3 与等功率对比
θe 电角度:旋转坐标系的基准
🔬 电流采样
低边电阻 + 运放调理(主流)
PWM 中心触发,避开开关噪声
单电阻采样:窗口约束与移相
双采样 / 三电阻 / 相线霍尔
零点偏置标定(上电采平均)
⚡ SVPWM 与功率级
扇区判断 N 与作用时间 T1/T2
七段式中心对齐波形
母线利用率 √3/2(比 SPWM 高 15%)
死区效应与补偿(ΔV=4Td·fs·Udc/π)
互锁/自举/过流比较器封波
🎯 三环控制
电流环:零点对消 Kp=Lωc、Ki=Rωc
速度环:PI + 编码器测速(M/T/M-T)
位置环:P 或 PP,输出端反馈
带宽分离原则:内环是外环 5~10 倍
抗饱和 clamping / 反算式
📡 编码器与角度
增量 ABZ / 绝对值 SPI(AS5047 等)
电角度 = 机械角 × 极对数 p
零点校准:d 轴对齐法
非线性误差 / 延迟补偿(ω·Δt)
🛡️ 保护机制
硬件级:比较器 + DAO 直接封波
软件级:过流/过压/欠压/超温/堵转
阈值 + 去抖计数(防毛刺误触发)
FAULT 恢复条件与重试策略
🌀 观测器与无感
滑模观测器 SMO(反电动势法)
龙伯格 / EKF(见 04-17)
高频注入 HFI(零速无感)
磁链观测与 PLL 提取角度
🧪 参数辨识与高级
Rs:直流注入压降法
L:电压脉冲 / 短路电流斜率
磁链:空载反电动势反推
弱磁(id<0)与 MTPA 一句话
💡 本页与深入页的分工:本页负责「把流程/结构/时序画全」——先在这里建立完整的地图,再按需跳转:想动手跑代码看 04-02,想看动画直觉看 01-02,想玩 SVPWM 波形看 02-13,想调参看 02-11。面试前的查漏补缺见 06-11 保研复试面试题库

2 为什么电流采样点必须落在 PWM 中心

总流程图第①框"读相电流"看起来只是读两个寄存器,真正讲究的是什么时候读。采样时刻选错,采到的不是电流而是开关噪声;窗口不够,采到的是未稳定的过渡过程。这一节把"时机"讲透。

2.1 ADC 触发时机:中心对齐 PWM 的下桥臂采样窗口

工程上最常用低边采样电阻(shunt 串在下桥臂与地之间):只有当下桥臂导通时,相电流才流过采样电阻,R 上的电压才等于相电流。而中心对齐 PWM 有个天然的好性质:每个周期的正中央,三相的下管都处于导通状态(占空比越接近 50%,窗口越宽)。于是标准做法是——用定时器更新/谷底事件触发 ADC,让采样点正对周期中心,既保证下管导通,又离两个开关沿最远,信噪比最好。

开关沿 = 噪声禁区 安全采样窗口 安全采样窗口 高=上管导通 低=下管导通 ADC 触发(定时器 TRGO / 更新事件)= 周期正中心 ISR:①~⑧ 计算 ISR:①~⑧ 计算 1 × Tpwm(50μs @20kHz) 相端电压
图③ 采样窗口示意:中心对齐 PWM 每周期正中心三相下管同时导通,此处采样 = 电流稳定 + 远离开关沿。触发后立刻进 ISR 完成①~⑧,波形写入影子寄存器、下个周期生效。

2.2 单电阻采样的窗口约束与双采样

为了省成本/体积,不少驱动板只在母线上放一颗采样电阻(单电阻采样):它测的是母线电流,在一个 PWM 周期内,不同的开关状态下母线电流分别等于不同的相电流——于是一个周期内要在两个不同时刻各采一次,才能重建 Ia、Ib 两相。这带来著名的窗口约束:

方案采样电阻数优点代价与约束典型场景
三电阻低边3(可省为 2)窗口最宽、最稳健;三相直读互为校验运放 ×3,布线对称要求高服务器风扇、云台、大多数学习板
双电阻低边2两相直读,Ic 由 KCL 算出;窗口宽两路运放;精度一致性要求主流关节驱动器(ODrive 等)
单电阻(母线)1成本最低、体积最小、无共模问题窗口随矢量移动、需移相与最小脉宽补偿、算法复杂低成本家电、风机、紧凑模组
在线式/相线磁霍尔/集成与开关状态解耦,任意时刻可采带宽/温漂/成本;小电流精度差大功率或对隔离有要求的场合
⚠️ 实践提醒:调参时发现"电流波形一半是好的一半是花的",八成是占空比进入窄脉冲区后采样窗口塌了,而不是 PI 没调好——先检查采样方案在极端占空比下的行为,再怀疑控制器。

3 坐标变换四件套:Clark / 反 Clark / Park / 反 Park

总流程图的②③⑥三框其实是"一套数学、四个方向"。它们不创造能量,只做换视角:同一个物理电流,在三相静止系、两相静止系、旋转系里有三套坐标——FOC 全部魔法的前提,是选对了"那个让它看起来像直流"的视角。

3.1 四个公式(恒幅值变换,系数 2/3)

Clark 变换:三相静止(abc)→ 两相静止(αβ)。等幅值形式下,α 轴对齐 A 相绕组:

Iα = (2/3)·[ Ia − (1/2)Ib − (1/2)Ic ] , Iβ = (2/3)·(√3/2)·[ Ib − Ic ] = (Ib − Ic)/√3

把 Ic = −Ia − Ib 代入化简,就得到代码里最常见的两行:

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

Park 变换:静止(αβ)→ 旋转(dq),θe 为转子电角度:

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

反 Park 变换:旋转(dq)→ 静止(αβ),把 PI 算出的电压矢量转回静止系(角度取负,矩阵即 Park 的转置):

Vα = Vd·cos θe − Vq·sin θe , Vβ = Vd·sin θe + Vq·cos θe

反 Clark 变换:静止(αβ)→ 三相(abc),SVPWM 之外若直接用正弦调制会用到它:

Va = Vα , Vb = −Vα/2 + (√3/2)·Vβ , Vc = −Vα/2 − (√3/2)·Vβ
/* 工程实现要点:两次乘法搞定 Park/反Park —— cos_t、sin_t 用查表+插值,不用 libm */
float cos_t = cos_lut(theta), sin_t = sin_lut(theta);   /* 查表 ~几十个周期 */
/* Park */
float id =  ialpha*cos_t + ibeta*sin_t;
float iq = -ialpha*sin_t + ibeta*cos_t;
/* 反 Park(只换符号) */
float valpha = vd*cos_t - vq*sin_t;
float vbeta  = vd*sin_t + vq*cos_t;

3.2 等幅值 vs 等功率,以及 θ 从哪里来

💡 为什么必须变换?不变换,三相电流是三个互相耦合的 120° 正弦量,PID 对正弦量的稳态误差不为零(增益无穷大才无静差);变换到 dq 后是两个独立直流量,普通 PI 就能零稳态误差——坐标变换的本质是把交流控制问题变成直流控制问题。直觉动画见 01-02 核心原理动画演示

4 三环设计:电流环的 PI 与带宽,速度环/位置环的级联

总流程图第④框里的两个 PI 是最内环;速度环、位置环跑在更慢的节拍上(主循环或低速中断),它们的输出就是电流环的 Iq 目标。三环是典型的级联结构,这一节先讲最内环的"数学设计",再看整个级联塔。

4.1 PI 参数物理意义、积分抗饱和、输出限幅与解耦补偿

电流环被控对象近似一阶 RL 负载 G(s) = 1/(Ls + R)。PI 控制器的 Kp 决定"响应多快"(比例把误差立刻顶上去),Ki 决定"稳态误差多快被消掉"(积分慢慢把静差清零)。但积分是把双刃剑:输出已经饱和限幅时,误差还在被积分,积分器会攒下一大坨"用不上的控制量"(windup),退饱和后造成超时超调甚至振荡。两种主流抗饱和:

float err = iq_ref - iq;
float u_unsat = kp*err + integ;          /* 试算未限幅输出 */
if ((u_unsat > VMAX && err > 0) ||       /* 饱和且还在同向推 */
    (u_unsat < VMIN && err < 0)) {
    /* 停止积分,只保留比例项 */
} else {
    integ += ki * err * dt;              /* 正常积分 */
}
float vq = clamp(kp*err + integ, VMIN, VMAX);

抗饱和之外,还要处理输出限幅交叉耦合。永磁同步电机在 dq 系的电压方程里有两个旋转电动势/交叉耦合项:

vd = R·id + L·did/dt − ωe·L·iq  vq = R·iq + L·diq/dt + ωe·(L·id + ψf)

d 轴电流会"窜"进 q 轴方程(−ωe·L·iq),反之亦然。解决办法是在 PI 输出后叠加前馈解耦项:

vd* = PI_d(id*) − ωe·L·iq  vq* = PI_q(iq*) + ωe·L·id(磁链项 ψf·ωe 已含在反电动势里)

要不要加?经验法则:ωe·L·i 相对 R·i 或母线余量很小时(低速、小电感),可以不加,PI 自己能兜住;高速或 L 大时必须加,否则两个轴互相拖拽、动态时 iq 超调。加错 L 参数反而引入扰动——所以先跑干净 PI,再按需补解耦,一次只动一样(见 02-11 FOC 调参整定实战手册)。输出侧还要保证 vd² + vq² ≤ Usmax²(即总流程图第⑤框的限幅圆)。

4.2 电流环带宽设计:零点对消法与 Kp/Ki 换算

最实用的整定思路是零点对消:让 PI 的零点正好抵消被控对象的极点 R/L,开环就变成纯积分器,闭环是一阶系统,带宽由你说了算:

PI 零点 = Ki/Kp,令 Ki/Kp = R/L(零点对消极点)⇒ 闭环传函 = 1/(s/ωc + 1),ωc = Kp/L
Kp = L·ωc ,Ki = R·ωc(先定带宽,再算参数,两行搞定电流环)

4.3 速度环与位置环:级联结构、带宽分离与编码器测速

θ* 位置环 P(输出端反馈) ≈10~100Hz ω* 速度环 PI + 前馈 ≈100~500Hz Iq* 电流环 ×2 PI(d/q)+限幅圆 ≈0.5~2kHz Vd,Vq SVPWM+逆变 10~40kHz PMSM+编码器 θ 反馈 ω 反馈 Id,Iq 反馈 带宽分离原则:内环带宽取外环的 5~10 倍,外环才敢把内环当成"听话的执行器"
图④ 三环级联:电流环每 PWM 周期执行(总流程图④框),速度环 1~5kHz,位置环 ~1kHz 或在主循环。关节机器人位置环常用输出端编码器反馈以消传动误差。

5 SVPWM 七段式:扇区判断、矢量时间与母线利用率

总流程图第⑦框把 (Vα,Vβ) 变成三路占空比。SVPWM 的思路:三相桥只有 8 种开关状态——6 个非零基本矢量把 αβ 平面均分成 6 个扇区,加 2 个零矢量;任何目标矢量都用"扇区相邻两个基本矢量 + 零矢量"按时间加权合成。想边转边看的交互实验见 02-13 功率级拓扑与 SVPWM 交互实验室

5.1 扇区判断 N 与矢量作用时间 T1、T2

α β 内切圆 = Usmax = Udc/√3(线性区) V4(100) V6(110) V2(010) V3(011) V1(001) V5(101) V0/V7=零矢量 Vref(落在扇区Ⅰ) T1·V4 T2·V6 γ = Vref 相对扇区起始边的夹角;Vref 由 V4 作用 T1、V6 作用 T2、零矢量作用 T0 合成
图⑤ SVPWM 矢量扇区图:括号内是三相开关状态(S=上管导通)。Vref 矢量角 γ 决定相邻矢量各自分担多少时间;超出内切圆即过调制,电流环失控。

扇区判断不需要 atan2,用三个符号函数拼出判据 N,一次判断落在哪个扇区:

B0 = sign(Vβ), B1 = sign(√3·Vα − Vβ), B2 = sign(−√3·Vα − Vβ), N = B0 + 2·B1 + 4·B2
N315462
扇区Ⅰ(0~60°)Ⅱ(60~120°)Ⅲ(120~180°)Ⅳ(180~240°)Ⅴ(240~300°)Ⅵ(300~360°)

确定扇区后,设 γ 为 Vref 在扇区内相对起始边的角度,作用时间(Ⅰ 扇区为例):

T1 = √3·Ts·|Vref|/Udc · sin(60° − γ)  T2 = √3·Ts·|Vref|/Udc · sin(γ)  T0 = Ts − T1 − T2
/* 扇区Ⅰ的完整数值示例: Udc=24V, Ts=50μs(20kHz), Vref=8V, γ=25° */
/* T1 = 1.732*50e-6*8/24*sin(35°) = 16.6μs */
/* T2 = 1.732*50e-6*8/24*sin(25°) = 12.2μs */
/* T0 = 50 - 16.6 - 12.2 = 21.2μs  → 两端各分 10.6μs(七段式) */

5.2 七段式波形与马鞍波:母线利用率提升 15.5%

把 T0 拆成两半放两端、T1/T2 各拆两半对称放中间,得到中心对称的七段序列(以扇区Ⅰ为例:V0→V4→V6→V7→V6→V4→V0)。中心对齐让每次切换只动一个开关管——开关损耗最小,谐波也最友好。

V0V4V6 V7V6V4V0 T0/2T1/2T2/2 T0/2T2/2T1/2T0/2 U V W 上管导通=高 每段只切一相
图⑥ 七段式开关序列(扇区Ⅰ,U 相占空比最大):形状中心对称、两端各留半个零矢量。序列随扇区轮换,三相占空比随之滚动。

马鞍波与母线利用率:七段式的本质是往三相调制波里注入了零序共模分量 ucm = −(max+min)/2,波形从正弦变成"马鞍"。收益:纯正弦 SPWM 相电压幅值上限是 Udc/2,马鞍波把上限抬到 Udc/√3 ≈ 0.577·Udc,等效基波幅值提升 √3/2 ≈ 1.155 倍(约 15.5%)——同样的电池,多榨出 15% 的转速/力矩余量。

SVPWM 基波可用幅值 = 1.155(SPWM 为 1.0) 马鞍波 正弦 SPWM 注入 u_cm = −(max+min)/2 后:包络被"压平抬高",同样的母线电压可输出更大的基波
图⑦ 马鞍波 vs 正弦调制:顶部平、谷底也平,单相峰值反而更低;绿色虚线是等母线下 SVPWM 能给出的基波幅值,即 √3/2 增益的来源。

6 死区效应与补偿:被吃掉的电压与低速嘶嘶声

第⑦框算出的占空比交给硬件后,还要被死区"打劫"一次。死区是防止上下管直通的保命机制,但它在每个开关沿都会让输出电压偏离指令值——这是 FOC 低速性能的"天生宿敌"。

6.1 死区:物理原因、电压损失定量与补偿方法

ΔV₁ ≈ (4/π)·Udc·Td·fs = 4·Td·fs·Udc/π  (例: Udc=24V, Td=500ns, fs=20kHz → ΔV₁≈0.76V)

补回来有三种主流方法:

方法原理优点代价/风险
按电流方向移相(前馈补偿)按各相电流符号,把该相占空比直接补上 Δd = ±Td·fs(即 ±Td/Ts 的伏秒)实现两条 if;响应即时依赖电流符号判断;过零附近符号抖动会补错,常配滞环/小电流区冻结补偿
闭环扰动观测补偿把"指令电压 − 实际基波电压"的差当作扰动,用积分器/观测器在线估出 ΔV 并回注(死区效应被自动吸收,含管压降)不依赖精确 Td,连器件压降一起补带宽要远低于电流环;参数不当易振荡
过零钳位检测补偿检测相电流被钳在 0 的区段,只在该相钳位时补偿针对性强,不影响非钳位区检测阈值难定,零速小电流仍困难
/* 前馈死区补偿(每相一条): 符号判断加滞环,小电流区保持上次补偿方向 */
float ia_comp = 0;
if (ia >  I_HYST)      ia_comp = +TD_FS;      /* Td*fs = 0.01(占空比) */
else if (ia < -I_HYST) ia_comp = -TD_FS;
else                   ia_comp = ia_comp;     /* 过零区: 冻结,防止抖动 */
duty_a += ia_comp;  duty_b += 0;  duty_c += 0;   /* 三相各算各的,再统一限幅 */
💡 判断要不要补:算一下 ΔV₁ 占 Usmax 的比例——<1% 可不补;3% 以上或电机有低速力控需求(关节机器人正是),前馈补偿必做。硬件侧则尽量选支持自适应死区的栅极驱动,把 Td 本身压小,比任何算法都干净。

7 编码器与电角度校准:θ 的来龙去脉

总流程图③⑥两框都依赖 θe——它错了,坐标系就转错,力矩方向全错。这一节讲 θe 的物理含义、两种主流编码器、以及上电后的"找北"流程。

7.1 增量 ABZ / 绝对值 SPI 编码器;电角度 ≠ 机械角度

θe = ( θmech × p + θe_offset ) mod 360°  例: p=14,机械角转 1°,电角度走 14°

7.2 零点校准流程与编码器的非理想性

d 轴对齐法——上电校准的标准操作,本质是"用 FOC 自己给转子定位":

1给定 id
强制 Vd 为正、Vq=0(开环电压或电流闭环),产生指向 d 轴的定子磁场
2转子对齐
永磁转子被吸到 d 轴方向并锁住(能听到"咔"的锁定感)
3记录编码器
读下此刻编码器原始角,记为零点偏移 θe_offset(编码器读数此刻 = 电角度 0)
4验证
开环慢转一圈,检查电角度连续无跳变、方向与相序定义一致
5存参数
把 offset、极对数、方向写进非易失存储,下次上电直接用
⚠️ 非理想因素:编码器非线性——磁编码器受安装偏心/芯片线性度影响,有几角分到零点几度的周期误差,径向/切向力矩纹波的元凶之一,可按机械角查表校正;② 延迟——采样到生效隔 1.5 拍,高速时用 θe' = θe + ωe·1.5·Tpwm 补;③ 方向/相序——装配差异导致"正电压指令反转",校准时务必确认方向标志;④ Z 脉冲丢失或 SPI CRC 错,按第 10 节进 FAULT 而不是带病运行。

校准后的 θe 质量,直接决定总流程图③⑥两框的坐标系质量——所谓"FOC 调不好",相当一部分是"θe 就不准"。这一步的实操细节同样见 02-15 传感器与关节标定专题

8 代码运行图:上电 → 校准 → 运行 → 保护的状态机

总流程图是"一拍之内"的微观流程;把它撑起来的是"整个生命周期"的宏观骨架——状态机。任何一份正经的 FOC 固件(SimpleFOC 的 initFOC、ODrive 的 state machine、ST MC-SDK 的 State machine)都能归约到下面这张图。面试时能白板画出它,基本等于"真写过驱动器"。

8.1 状态机总图:七个状态与全部转换条件

POWER_ON_SELFTEST RAM/外设自检·ADC 零偏标定 ENCODER_CALIB 编码器通信/方向判定 PARAM_IDENTIFY 可选:辨识 Rs/L/ψf ALIGN d 轴对齐,记 θe_offset READY 待机封波,等待使能 RUN ISR:总流程①~⑧ 逐拍执行 主循环:速度/位置环+指令解析 FAULT 封波 MOE=0 记录故障码/时间戳 严重故障:锁定 需重新上电清零 自检通过 可选 校准通过/跳过辨识 参数写入 offset 存入 NVS 收到使能(CAN/上位机) 去使能/停机 对齐超时/失锁 过流/超温/堵转/丢编码器 故障已消失+重试<N 次 重复故障×M
图⑧ FOC 固件生命周期状态机:绿=安全态,蓝=校准链路,紫虚线=可选辨识,橙=正常运行(唯一允许输出转矩的状态),红=故障与锁定。任何状态任何异常,终点都是 FAULT——先安全,再谈性能。

8.2 各状态做什么:任务、耗时与失败去向

状态典型耗时做哪些事进入下一状态的条件失败去向
POWER_ON_SELFTEST10~100ms时钟/外设/存储校验;ADC 采样零偏标定(所有开关关断,采均值);比较器自检全部通过FAULT(自检失败码)
ENCODER_CALIB<10ms(绝对式)SPI 读数 CRC 校验;缓慢转动判方向;增量式在此做 Z 搜索读数稳定且方向确定FAULT(编码器丢失)
PARAM_IDENTIFY(可选)0.5~3sRs 直流注入、L 电感脉冲、磁链反推(见第 11 节)辨识收敛、残差小跳过,用上次/默认参数
ALIGN0.3~1s给定 d 轴电流对齐转子,记录 θe_offset,写入 NVS对齐稳定且角度单调FAULT(超时/失锁)
READY常驻待机PWM 封波;监听 CAN 指令;持续自检(母线电压/温度)收到使能且无故障
RUN持续ISR 跑①~⑧;主循环跑速度/位置环、轨迹插补、指令解析任何保护触发 → FAULT
FAULT持续至恢复六管全关(MOE=0);锁存故障码;按策略尝试恢复故障消失 + 重试未超限重复故障 → 锁定

9 中断时序图与执行时间预算:ISR 晚一拍会发生什么

FOC 是硬实时任务:每一拍都有"采样时刻"和"输出截止时刻"。这一节把 PWM 周期、ADC 触发、ISR 执行画在同一条时间轴上,并给出各步骤的周期数预算——学会用它判断"我的 ISR 来不来得及"。

9.1 PWM 中断时序图与执行时间预算表:触发、转换、执行、截止

ADC 触发(周期中心) ADC 触发 下一拍截止 ADC 转换 ≈2μs(注入组流水) ISR 正常 ①~⑧ ≈15μs ✓ 远早于 445 处下一拍 ISR 超时 被更高优先级中断抢占/计算超支 → 跨过 570 处截止线 错过下一拍采样 → 电流重建错拍、θ 补偿失配 → 电流环啸叫/振荡 025μs50μs 75μs100μs 例: 20kHz, Tpwm=50μs
图⑨ PWM 中断时序:绿色=触发点,蓝=ADC 转换,橙=ISR 执行,红=越界部分。健康判据:ISR 结束时刻距下一拍触发仍有大段余量(至少留 40~50% 空闲给通信与其他中断)。
⚠️ ISR 超时的连锁反应:① 错过下一次 ADC 触发,采样被推迟一拍,电流值与 θ 失配,等效在电流环里插入额外纯延迟——延迟超过 T/4 左右相位裕度就崩,表现为"一给速度就啸叫";② 用旧数据算 SVPWM,输出矢量方向错误;③ 抖动污染速度环测速,位置环跟着抖;④ 长期超支触发看门狗复位,关节在运动中突然失力——比不振更危险。

Cortex-M4F @170MHz(关节驱动主流)、单精度浮点、sin/cos 查表为基准的典型预算(20kHz 节拍,周期 50μs ≈ 8500 周期):

步骤对应总图框典型耗时约折合周期提速手段
中断进入/退出(压栈出栈)~0.3μs~50
读相电流+零偏/增益换算~0.4μs~70定点 Q15 或硬件 ADC 增益
取编码器角度+延迟补偿③输入~0.2μs~35SPI+DMA 预取,ISR 只取数
Clark 变换~0.1μs~17乘加重排,FPU 单周期 FMLA
Park 变换(含 sin/cos 查表)~0.4μs~70CORDIC 硬件(G4/H7)或 256 点表
d/q 两路 PI+抗饱和+前馈~0.4μs~70合并为 2×(2 FMLA)
限幅圆判断与缩放~0.1μs~17避免除法:用倒数乘法
反 Park 变换~0.3μs~50复用 sin/cos 缓存
SVPWM(扇区+T1/T2+占空比)~0.6μs~100查表化/最小-最大法求零序
写 CCRx+时基诊断(测量 ISR 用时)~0.2μs~35影子寄存器原子写
合计(典型)~3.0μs~510占周期 6%,健康
合计(未优化 float 库调用)~25μs~4200占 50%——上线前必须优化

10 保护机制:硬件级封波与软件级阈值

状态机的 FAULT 从哪来?答案是两条独立的保护链:硬件链"快但糙",软件链"细但慢"。两者缺一不可——只靠软件保护,CPU 哪怕忙 10μs 都可能来不及救硬件。

10.1 两级保护与 FAULT 恢复条件:比较器封波 + 软件阈值去抖

保护项检测来源典型阈值(关节驱动)去抖策略动作
过流(相)shunt+比较器 / ADC峰值 ≥ 1.5~2×额定电流硬件瞬时;软件连续 2~3 拍封波 → FAULT
过压(母线)Udc 分压 ADC>1.2×额定母线(如 60V@48V)连续 5~10ms封波(禁止续流回馈)/ 卸放
欠压(母线)Udc 分压 ADC<0.8×额定连续 10~100ms降额 → 停机(可自愈)
堵转编码器 + 指令速度≈0 且 |Iq|>阈值 持续 >200ms时间窗判定限流 → FAULT
超温NTC/驱动器 TSD>85~100℃(降额)/ >110℃(停机)慢速滑动平均先降额,后 FAULT
编码器失效SPI CRC / Z 脉冲超时连续帧错误或角度跳变 >90°/拍连续 3 帧立即 FAULT(θ 失效=力矩失控)
通信丢失CAN 心跳超时>5~10 个周期无指令超时计数进入安全停机(关节阻尼模式)

FAULT 之后什么时候允许"复活"?恢复条件表:

故障类型恢复条件恢复方式限制
欠压母线电压回到阈值上方并稳定自动回 READY重试计数 +1,超 N 次锁定
堵转负载移除/速度恢复自动或指令恢复累计堵转次数记录,频繁堵转要报警
超温温度回落 >滞回带(如 <70℃)自动回 READY滞回防"开机-过热"振荡
过流(软件级)指令归零后重使能需指令恢复同帧重复触发 → 升级为硬件级处理
过流(硬件比较器)不允许软件恢复锁定,须断电排查说明可能已发生短路/炸管前兆
编码器失效通信恢复且重新校验 offset重新走 ALIGN 链路不允许跳过校准直接 RUN
✅ 一句话总结两级保护:硬件保护保"炸不炸",软件保护保"好不好"、并给出可解释的故障码。人形机器人整机还会在这之上叠第三层——整机控制器收到关节 FAULT 码后决定"单关节停机"还是"全身坐下",这属于机器人软件栈的职责。

11 高级话题一览:弱磁、MTPA、参数辨识与无感

到上一节为止,本页已经把"教科书 FOC"画全了。真实关节驱动器还会在这副骨架上加四个高频词——这里各给"一段话版本",深入内容按链接跳转。

11.1 弱磁(id<0)、MTPA、参数辨识与无感一览

参数辨识三件套与无感(互链观测器页)——这些参数正是第 4.2 节 Kp=L·ωc、Ki=R·ωc 的输入:

参数辨识方法原理一句话
定子电阻 Rs直流注入锁住转子,注入已知直流电流 I,测相端压降 V → Rs=(V/2−压降误差)/I(考虑半桥压降)
电感 Ld/Lq电压脉冲 / 短路电流斜率加阶跃电压,电流按指数上升 di/dt=(V−Ri)/L,拟合斜率得 L;或用高频注入测阻抗
磁链 ψf空载/恒速反电动势反推开环拖动到匀速 ωe,测线反电动势 V → ψf = V/(√3·ωe)(线电压口径时除 √3)

这些参数(第 8 节 PARAM_IDENTIFY 状态)正是第 4 节 Kp=L·ωc、Ki=R·ωc 的输入,也是第 11.1 节弱磁调度的依据——参数辨识是"自适应/高性能 FOC"的地基。ST MC-SDK 的 Motor Profiler、TI InstaSPIN 都是自动化这套流程的工具(见文末资源卡)。

无感(Sensorless):用估计器替代编码器,从中高压调制过程反推转子位置——中高速用反电动势类观测器(SMO/龙伯格/EKF+PLL),零低速用高频注入(HFI)。工程化细节、收敛性与本站统一的观测器框架见 04-17 状态观测器全解(龙伯格/EKF/SMO);无感 FOC 的官方实现范本见文末 TI/Microchip 应用笔记。

💡 人形机器人用哪个?关节驱动几乎全部用双编码器有感方案——力控要求低速大力矩、零速稳定,这正是无感的死区。无感主要出现在风扇/泵/高速空心杯等辅助执行器上。面试时能说清"为什么这里不上无感"比背无感公式更加分(更多面试题见 06-11 保研复试面试题库)。

12 完整 ISR 参考代码:与总流程图逐框对应

下面这份约 80 行的参考实现把本页所有图"落成代码":每个注释块的编号 ①~⑧ 与图②总流程图完全一致,可直接对照阅读。代码为教学向的清晰版本(平台无关 C,面向 STM32 风格外设),生产版还需加临界区保护与参数合法性校验。

/* =====================================================================
   foc_current_loop_isr.c —— FOC 电流环 ISR 参考实现(对应图② 总流程①~⑧)
   平台: 任意 Cortex-M(示例按 STM32 风格), PWM 20kHz, 中心对齐, 低边双电阻
   编译: 单精度硬件浮点; sin/cos 用查表, 全程禁止调用通用 libm
   ===================================================================== */
#include <stdint.h>

/* ---------- 控制器与系统状态(由状态机初始化, ISR 只读/自更新) ---------- */
typedef struct { float kp, ki, integ, out_min, out_max; } pi_t;   /* PI + 抗饱和限幅 */
static pi_t pi_d = { .kp=0.35f, .ki=180.0f, .out_min=-22.0f, .out_max=22.0f };
static pi_t pi_q = { .kp=0.40f, .ki=200.0f, .out_min=-22.0f, .out_max=22.0f };

static volatile float iq_ref = 0.0f;      /* 速度环输出, 主循环 1kHz 更新 */
static volatile float id_ref = 0.0f;      /* SPM 默认 0; 弱磁时 <0 */
static volatile uint8_t run_flag = 0;     /* 状态机 = RUN 时置 1 */

static float i_off_a, i_off_b;            /* ① 电流零偏(自检阶段标定) */
static float theta_e = 0.0f;              /* 电角度(编码器任务预取+延迟补偿) */
static float udc = 24.0f;                 /* 母线电压(低速任务周期刷新)   */
static float sincos_cos, sincos_sin;      /* 每拍一份, Park/反Park 共用   */

extern float sin_lut(float th); extern float cos_lut(float th);  /* 查表+线性插值 */
extern float adc_to_amp(uint16_t raw, float off);                /* 码值→安培    */
#define PWM_PERIOD_CNT 4250u               /* 例: 170MHz/40kHz 上下计数     */

/* ---------- 单步 PI(含 clamping 抗饱和, 对应总图④) ---------- */
static inline float pi_step(pi_t *p, float err)
{
    float unsat = p->kp * err + p->integ;                     /* 先试算未限幅输出 */
    if ((unsat >  p->out_max && err > 0.0f) ||
        (unsat <  p->out_min && err < 0.0f)) {
        return (unsat > 0) ? p->out_max : p->out_min;         /* 饱和且同向: 停积分 */
    }
    p->integ += p->ki * err * (1.0f / 20000.0f);              /* 正常积分, dt=1/20kHz */
    unsat = p->kp * err + p->integ;
    if (unsat >  p->out_max) return p->out_max;               /* 输出限幅 */
    if (unsat <  p->out_min) return p->out_min;
    return unsat;
}

/* ---------- SVPWM: 扇区+作用时间+占空比(对应总图⑦, 详见第 5 节) ---------- */
static void svpwm(float valpha, float vbeta, float udc_bus, uint16_t ccr[3])
{
    float ts = 1.0f / 20000.0f;                               /* 开关周期        */
    float k  = SQRT3 * ts / udc_bus;                          /* 公共系数        */
    /* 判扇区 N: 三个符号函数拼出 1..6(避免 atan2) */
    float b0 = (vbeta > 0) ? 1.0f : -1.0f;
    float b1 = (SQRT3*valpha - vbeta > 0) ? 1.0f : -1.0f;
    float b2 = (-SQRT3*valpha - vbeta > 0) ? 1.0f : -1.0f;
    int n = (int)(b0 + 2.0f*b1 + 4.0f*b2);
    /* 作用时间(以扇区Ⅰ为例, 其余扇区按表轮换 Vx 组合, 此处仅示意) */
    float t1 = k * ( SQRT3*valpha - vbeta) * 0.5f;            /* V4 时间(已数值化) */
    float t2 = k * vbeta;                                     /* V6 时间           */
    if (t1 + t2 > ts) { float s = ts/(t1+t2); t1*=s; t2*=s; } /* 过调制保护: 等比压缩 */
    float ta = ts - t1 - t2;                                  /* 零矢量总时间      */
    /* 七段式中心对齐 → 三相比较值(零矢量对半分, 中心对称) */
    ccr[0] = (uint16_t)((0.5f*(ta*0.5f + t1 + t2) / ts) * PWM_PERIOD_CNT);
    ccr[1] = (uint16_t)((0.5f*(ta*0.5f + t2)       / ts) * PWM_PERIOD_CNT);
    ccr[2] = (uint16_t)((0.5f*(ta*0.5f)            / ts) * PWM_PERIOD_CNT);
}

/* ========================= 主 ISR: 每 50μs 一次 ========================= */
void ADC1_2_IRQHandler(void)                                  /* JEOC 触发进入 */
{
    GPIO_DIAG->BSRR = DIAG_PIN;                               /* 时基测量: 置高 */
    /* 【入口分支】状态检查: 非 RUN 直接清标志返回(总图左侧"否"支路) */
    if (!run_flag) { ADC1->ISR = ADC_FLAG_JEOC; GPIO_DIAG->BSRR = DIAG_PIN<<16; return; }

    /* ① 读相电流: 注入组 JDR1/JDR2 已是同一时刻的 Ia/Ib */
    float ia = adc_to_amp(ADC1->JDR1, i_off_a);
    float ib = adc_to_amp(ADC1->JDR2, i_off_b);
    float ic = -(ia + ib);                                    /* 基尔霍夫补第三相 */

    /* ② Clark 变换(等幅值): 三相 → αβ */
    float ialpha = ia;
    float ibeta  = (ia + 2.0f*ib) * ONE_OVER_SQRT3;

    /* ③ Park 变换: θe 由编码器任务预取并做了 ω·1.5T 延迟补偿 */
    sincos_cos = cos_lut(theta_e);  sincos_sin = sin_lut(theta_e);
    float id =  ialpha*sincos_cos + ibeta*sincos_sin;
    float iq = -ialpha*sincos_sin + ibeta*sincos_cos;

    /* ④ d/q 双 PI(clamping 抗饱和内置) + 解耦前馈(高速建议开) */
    float vd = pi_step(&pi_d, id_ref - id) - we_est * LQ * iq; /* we_est/LQ 见 04-02 */
    float vq = pi_step(&pi_q, iq_ref - iq) + we_est * (LD*id + PSI_F);

    /* ⑤ 电压限幅圆: Usmax = Udc/√3, 等比缩放(方向不变) */
    float us = vd*vd + vq*vq, usmax = udc * ONE_OVER_SQRT3;
    if (us > usmax*usmax) { float s = usmax / sqrtf_approx(us); vd*=s; vq*=s; }

    /* ⑥ 反 Park: 旋转 dq → 静止 αβ(同一组 sin/cos, 只换符号) */
    float valpha = vd*sincos_cos - vq*sincos_sin;
    float vbeta  = vd*sincos_sin + vq*sincos_cos;

    /* ⑦ SVPWM: 扇区→T1/T2→占空比(内部含过调制压缩) */
    uint16_t ccr[3];
    svpwm(valpha, vbeta, udc, ccr);

    /* ⑧ 写比较寄存器: 影子寄存器, 下个更新事件原子生效 */
    TIM1->CCR1 = ccr[0];  TIM1->CCR2 = ccr[1];  TIM1->CCR3 = ccr[2];

    /* 收尾: 软件看门狗喂狗 + 清中断标志(否则会立刻重进) */
    ADC1->ISR = ADC_FLAG_JEOC;
    GPIO_DIAG->BSRR = DIAG_PIN<<16;                           /* 时基测量: 拉低 */
}
💡 对照读法:先看入口分支(非 RUN 直接退)→ ①→⑧ 顺序与图②完全一致;③ 的 θe 不在 ISR 里现读 SPI,而是由低速任务"预取+延迟补偿"后发布——把慢操作挪出硬实时路径,是 ISR 编写的第一原则。如果把这段代码与 04-02 的 SimpleFOC loopFOC() 并排看,你会发现库只是把①~⑧拆进了对象方法里。

13 常见误区

✗ "零点对消法套公式就完事,不用管参数准不准":零点对消的前提是被控对象 1/(Ls+R) 的 L、R 准确。R 随温升可漂移 30~40%,L 随磁饱和下降,手册值在关节电机上偏差 20% 很常见——参数不准则 Ki/Kp≠R/L,零极点不再对消,轻则超调变大、重则高频振荡。量产前必须实测辨识(或按电流工况分段修正参数)。
✗ "Park 变换用什么角度都行,转起来自然就对":必须用电角度 θe = 机械角 × 极对数 p + 零点偏置,且方向约定(编码器计数方向、q 轴超前/滞后)全链路一致。用错角度等于坐标系转错位,iq 不再对应转矩轴,轻则力矩打折、重则正反馈飞车;高转速下还要补 ω·1.5T 的一拍延迟(见第 12 节代码注释③)。
✗ "抗饱和就是把积分器清零/随便钳住":clamping 的方向有讲究:只在「输出已到限幅且误差会继续把输出顶在限幅上」时冻结同方向积分;方向钳反或无条件清零,退出饱和会变慢甚至反向超调。另外限幅后的实际输出要回写进积分路径,保证控制器状态与真实输出一致。
✗ "死区补偿就是把 PWM 时间拉长一点":死区损失的是伏秒(电压幅值):ΔV≈Td·Udc·fs,方向由相电流极性决定——补的是电压矢量,不是时间;而且补偿过量时,零电流附近电流极性频繁翻转会引起来回过补,低速抖动反而加剧。工程做法是按 sign(i) 增减 ΔV 折算的占空比,并在小电流区设「不补偿死区」。
✗ "单电阻采样窗口不够,凑合采一下也能用":单电阻方案必须在一个周期内、两个不同有效矢量下各采一次电流才能重构三相,窗口宽约 (1−Dmax)·T/2 量级;低速某相占空比趋 0、高速深调制时窗口会被压没。此时电流重构失真,必须用零矢量移相、最小脉宽约束或窗口不足拍修补——不是「凑合」能解决的事(见第 2.2 节)。
✗ "编码器零点校准带载做也一样":校准是给 d 轴通固定电流把转子拉到已知电角度;带载/有摩擦重力时,转子会被拉离对齐位置甚至拉不动,零点随之整体偏移。零点偏 Δθ,转矩恒打 cos(Δθ) 折扣且 dq 耦合加剧。校准必须空载(或脱开负载)进行,结果掉电保存,复装后不得再动(见第 7 节)。

14 小结与思考

📌 本节小结:① FOC 的每个 PWM 周期 = 一次"采样完成中断"驱动的①~⑧流水线:读电流→Clark→Park→双 PI→限幅圆→反 Park→SVPWM→写比较寄存器;② 采样点必须落在 PWM 中心(下管全导通+远离开关沿),单电阻方案还要过"窗口约束"这一关;③ 坐标变换四件套只是换视角,恒幅值系数 2/3,θe=机械角×p+offset;④ 电流环两行整定 Kp=L·ωc、Ki=R·ωc,三环遵守"内环是外环 5~10 倍"的带宽分离;⑤ 生命周期状态机 SELFTEST→CALIB→ALIGN→READY→RUN→FAULT 是一切 FOC 固件的骨架,硬件比较器封波是不可逾越的安全底线。
🤔 思考题: 1. 把 PWM 频率从 20kHz 提到 40kHz,电流环带宽理论上可以翻倍,但死区电压损失 ΔV₁ 也翻倍——关节驱动器该怎么权衡?(提示:ΔV₁/Usmax 比例、MOSFET 开关损耗、低速力控纹波三方面)
2. 状态机里 PARAM_IDENTIFY 辨识出的 L 偏大 20%,电流环会发生什么?(提示:零点对消被破坏,Ki/Kp ≠ R/L,阶跃响应出现什么形状?是否需要在线辨识+自适应 PI?)
3. 编码器零点校准若在带 0.1 N·m 摩擦力矩的条件下进行,转子会停在哪个平衡位置、零点往哪个方向偏?对一个 7 对极电机,偏 5° 电角度相当于多少转矩损失?不拆减速器的前提下,你有什么离线验证零点是否准的办法?

15 本节自测

1. 中心对齐 PWM + 低边采样电阻方案中,把 ADC 触发点放在 PWM 周期正中央,最主要的原因是?
💡 B 正确:低边电阻只有下管导通时才流过相电流,中心对齐 PWM 的周期正中央恰好三相下管全导通,且离开关沿最远(米勒平台抖动、di/dt 感应尖峰都集中在开关沿附近),是信噪比最优的"甜点"采样点。A 错:ADC 转换快慢由采样保持电路与转换时钟决定,与触发时刻落在周期的哪里毫无关系;C 错:编码器 Z 脉冲到达时刻由转子位置决定,与 PWM 中心没有固定对应关系,零点校准另有一套对齐流程;D 错:CAN 中断调度是软件层面的事,不能也不应反过来决定 ADC 硬件触发的相位——触发源必须是定时器硬件事件。
2. 用"零点对消法"整定电流环:先选定闭环带宽 ωc,则 PI 参数应取?
💡 C 正确:令 PI 零点 Ki/Kp 等于被控对象极点 R/L 即"零点对消",闭环化为带宽 ωc 的一阶系统,解得 Kp=L·ωc、Ki=R·ωc。例:L=50μH、R=50mΩ、ωc=2π×1kHz → Kp≈0.31、Ki≈314;注意 ωc 别超过 fs/10~20。A 错在把 L、R 记反——那样零点位置差出数量级,闭环退化为欠阻尼二阶系统,阶跃响应振荡;B 错在量纲不通:Kp 的单位是 V/A(即 Ω),L·ωc 恰好是 Ω,而 ωc/L 的量纲完全对不上,物理上不成立;D 错:Kp、Ki 各有明确出处(对消条件+带宽定义各定一个),与 ωc 的平方没有任何关系。
3. 关于 SVPWM 的电压利用率,正确的是?
💡 B 正确:注入零序共模分量后调制波变马鞍,线性区相电压幅值上限从 Udc/2 提升到 Udc/√3≈0.577Udc,等效基波幅值提升 2/√3≈1.155 倍(约 15.5%)。A 错:Udc/2 是 SPWM 的上限,SVPWM 正是靠零序注入突破它的,说"相同"就抹掉了 SVPWM 的核心价值;C 错:线性区内相电压幅值不可能达到 Udc(那已是深度过调制,波形严重失真),Udc 是母线电压而不是相电压上限;D 错:利用率由调制策略(是否注入零序)决定,与载波频率无关——载波频率只影响开关损耗与电流纹波。
4. 运行中硬件比较器检测到过流、已由硬件自动封波(六管全关)。正确的处理方式是?
💡 C 正确:硬件级过流意味着可能已发生短路/退饱和等危险前兆,属于"不允许软件恢复"的故障:必须锁定故障、记录故障码、断电排查(见第 10 节恢复条件表)。A 错:清标志重新使能等于带病运行,故障根因未除,下次过流再来时封波未必来得及,是炸管的标准剧本;B 错:调高阈值治标不治本,还把真正的过流保护窗口撕开一道口子——阈值失去意义,故障时保护的及时性被牺牲;D 错:开环模式失去电流闭环,堵转下相电流只受很小的阻抗限制,几毫秒内就能超过功率器件的安全工作区。
5. 电流环 ISR 的执行时间偶然超过一个 PWM 周期,最先出现的后果是?
💡 A 正确:FOC 是硬实时任务,超时意味着错过下一拍采样,等效引入额外纯延迟,电流与 θ 失配让 Park 坐标系"转错位",轻则啸叫重则失稳。B 错:延迟不是"这一次晚一点"就完了——它破坏的是本拍输出与下一拍采样的时序一致性,误差会经闭环逐拍累积放大,不存在"没有任何影响";C 错:ADC 触发由定时器硬件决定,不存在"编码器补采"的机制,编码器链路与相电流采样本来就是两条独立链路,谁也替不了谁;D 错:SVPWM 只是调制计算模块,内部没有任何"自动降频保护"逻辑,超时只能靠软件时基监测(DWT/看门狗)发现,预算纪律是 ISR 用时 < 周期 50% 并验收"最大值"。
6. 采用等幅值 Clark 变换(三相满足 ia+ib+ic=0),由 ia、ib 计算 αβ 分量,正确的是?
💡 D 正确:等幅值约定下 iα=ia、iβ=(ia+2ib)/√3——从完整式 iβ=(1/√3)(ia/2+ib−ic/2) 代入 ic=−ia−ib 化简即得,αβ 合成矢量幅值与原相电流幅值相等,这就是"等幅值"。A 错:iβ=ib 幅值相位都不对,且没有利用 ic=−ia−ib 化简,把"三相→两相"的 2/3 系数直接漏掉了化简步骤;B 错:iβ 的分子应为 ia+2ib 而不是 ia−ib,组合与符号全错,合成矢量轨迹是错的;C 错:系数写成了 1/3 而不是 1/√3,β 分量整体缩到 0.577 倍,Park 之后的 iq 随之失真,电流环等效增益打折扣。另注意等功率变换(系数 √(2/3))是另一种约定,全链路必须统一,混用是最常见的翻车点。
7. 关于逆变器死区引起的电压误差 ΔV≈Td·Udc·fs,下列说法正确的是?
💡 A 正确:ΔV=Td·Udc·fs 只由死区时间、母线电压、开关频率决定,与转速无关;但反电动势随转速下降,低速时可用电压余量小,ΔV 占比急剧上升,表现为零电流附近的极性翻转纹波与低频啸叫。B 错:正好说反——高速时反电动势大、ΔV 占可用电压比例小,低速才是死区效应最严重的工况;C 错:ΔV 与 fs 成正比而不是反比,PWM 频率越高每秒"白丢"的伏秒越多,这正是 40kHz 以上高频化要重新审视死区的理由;D 错:死区误差方向取决于相电流极性,在电流过零附近来回跳变,带来的是相位畸变与 6 倍频谐波,力控场景直接表现为低速力矩纹波,必须按第 6 节的方法补偿。
8. 电流环 PI 采用 clamping(钳位)抗饱和,积分器"停止累加"的正确触发条件是?
💡 D 正确:clamping 只在"输出饱和 + 误差与饱和方向同号(继续积分只会加深饱和)"时冻结同方向积分;误差反向(要把输出拉回线性区)时积分必须照常工作,否则退出饱和会被人为拖慢。A 错:误差不为零是闭环常态,若据此冻结积分,稳态误差永远消不掉,PI 退化成纯 P;B 错:每周期清零等于没有积分器,阶跃响应只剩比例项,静差与抗扰能力双双崩塌,与"抗饱和"的初衷完全相反;C 错:提前钳位让输出还没饱和就丢失积分能力,响应变慢——抗饱和的正确姿势是"饱和了才管,不饱和不管",而不是未雨绸缪地锁死。

16 面试问答:FOC 高频四连问

按「一句话结论 + 要点展开」组织——先给结论再展开,是面试答题的标准节奏。更多真题与逐题解析见 06-11 保研复试面试题库

Q1:为什么电流环可以用零点对消整定,速度环/位置环却不用这一招?
一句话结论:零点对消的前提是"被控对象模型准",电流环恰好满足,速度环不满足。要点:①电流环对象是精确的一阶模型 G(s)=1/(Ls+R)——电气时间常数小、参数漂移慢、无外部状态耦合,只要 L、R 实测准确,Ki/Kp=R/L 把零极点精确对消,闭环就是干净的带宽 ωc 一阶惯性;②速度环对象是"电流源→转矩→1/(Js) 积分→负载",J 随末端抓取的物体实时变化,还有摩擦非线性、机械谐振、编码器测速噪声——想"对消"的对象本身在变,对消失效时误差动态不受控;③失配后果不对称:电流环参数偏 20% 只是带宽/阻尼略偏,速度环硬套对消可能直接振荡。所以电流环解析整定、速度环按带宽+相位裕度经验整定、位置环再叠前馈,是"模型置信度决定整定方法"的典型案例。
Q2:FOC 电机一上电就抖动甚至飞车,你的排查顺序是什么?
一句话结论:按"角度错 → 采样错 → 参数错 → 时序错"的顺序查,从概率最大、代价最小的先入手。要点:①电角度链路:零点校准值是否丢失/复装后未重校、编码器方向与 q 轴方向约定是否一致、极对数 p 有没有配错——飞车案例一大半出在角度;②电流采样:ADC 触发点是否落在 PWM 中心、采样通道与相序映射有没有接反、偏置(offset)校准是否做过;③参数与整定:Kp/Ki 是否差数量级、输出限幅与抗饱和方向、电流环带宽是否超过 fs/10;④软件时序:ISR 是否超时、中断优先级是否被通信打断、保护阈值有没有生效。动手原则:示波器抓封波脚与相电流波形,先看"保护动没动",再逐环断开验证——千万别上来就盲改 Kp。
Q3:为什么电流采样必须卡在 PWM 中心?单电阻方案为什么还多一条"窗口约束"?
一句话结论:PWM 中心是"三相下管全导通 + 离开关沿最远"的唯一甜点,低边采样在这里才能读得准、少受干扰;单电阻还要保证一个周期内能在两个不同有效矢量下各采一次。要点:①低边电阻只有下管导通时流过相电流,中心对齐 PWM 的周期正中央三相下管同时导通,是唯一能"一拍读全"的时刻;②开关沿附近有米勒平台抖动与 di/dt 感应尖峰,中心点信噪比最好;③单电阻用母线电阻重构三相,需要两个不同有效矢量时刻的电流,当某相占空比趋近 0 或 1 时,采样窗口(≈(1−Dmax)·T/2 量级)被压没,低速轻载与高速深调制各触一界;④工程对策:零矢量移相、最小脉宽限制、窗口不足拍用上一拍值/观测器修补——这些对策直接决定单电阻方案的调速范围边界。
Q4:死区补偿怎么做?为什么"补过头"比"不补"更难排查?
一句话结论:按相电流极性给输出电压叠加一个 ΔV≈Td·Udc·fs 的伏秒修正,并在零电流附近留一段"不补偿区";补过头会在零电流附近形成极限环振荡。要点:①机理:死区让每相每拍固定损失 Td·Udc 的伏秒,方向由电流极性决定(哪侧管子续流),所以补偿=按 sign(i) 增减 ΔV 折算的占空比;②补过头的后果:电流过零附近极性频繁翻转,补偿方向跟着来回跳,叠加测量噪声形成低速抖动/啸叫的极限环——不补只是电压偏小、力矩略打折,补过头是主动注入振荡,症状更隐蔽;③工程细节:小电流区设不补偿死区(滞环)、补偿量随母线电压实时折算、PWM 频率越高补偿量越大;④进阶答法:把死区当作已知结构性扰动,用扰动观测器/重复控制在线吸收,可同时吃掉死区与管压降两类非线性。

17 参考来源与延伸资源

以下资源均经人工访问核实(2026-09),官方文档负责"权威口径",中文专栏与视频负责"直觉入门"。建议配合本站深入页按需取用。

本页公式与图表的直接出处:

📖
⭐ SimpleFOC 官方文档(Arduino-FOC)
开源 FOC 库的官方文档:控制模式、电流采样、编码器接入、PID 调参一应俱全,Theory corner 有简明的变换推导。入门把闭环跑通的第一站。
官方文档⭐ 必收入门/实现
📘
ST X-CUBE-MCSDK(Motor Control SDK 6.x)官方页
ST 官方电机控制 SDK:PMSM FOC 固件库 + MC Workbench 图形化配置 + Motor Profiler 参数辨识,工业级工程范本,状态机与保护实现值得精读。
官方文档工程范本参数辨识
📗
ST Wiki:MC SDK 6.x 入门指南
从 Workbench 建工程到生成代码、观测变量的完整上手路径,配合本页第 8 节状态机理解其 six-step/state-machine 设计。
官方文档上手教程
📄
TI 应用报告 SPRABZ2:Sensorless FOC of 3-Phase PMSM
TI 经典无感 FOC 应用报告(C2000/F2803x):坐标变换、观测器、工程实现的完整公式与代码框架,把本页第 11 节"一段话版"展开成 60 页细节。
官方文档⭐ 深度无感 FOC
📄
TI 用户指南 SPRUHJ1:InstaSPIN-FOC
TI 自动参数辨识 + 无感控制的完整用户指南:Rs/L/磁链辨识流程(对应本页第 11 节)、FAST 观测器与电机启动策略。
官方文档参数辨识
📄
Microchip AN1299:PMSM 无传感器 FOC(PLL 估计器)
dsPIC33 上的无感 FOC 完整应用笔记:单电阻电流重建(对应本页 2.2 的窗口约束实战)与 PLL 角度提取,代码全开源。
官方文档单电阻采样
📄
Microchip AN1078:基于滑模观测器(SMO)的无感 FOC
官方文档滑模观测器
📘
ODrive 官方文档
机器人圈最流行的开源大功率 FOC 驱动:编码器校准(对应本页第 7 节)、状态机与错误码(对应第 8、10 节)都有可直接对照的工程实现。
官方文档⭐ 必收工程实现
📗
ODrive Control 手册(三环控制与调参)
ODrive 的电流环/速度环/位置环参数说明与整定建议,是本页第 4 节三环设计在真实产品上的落地参照。
官方文档三环调参
📝
知乎专栏:FOC 中的 SVPWM 原理细讲
中文社区对扇区判断、T1/T2 推导讲得极细的高赞文章,配本页第 5 节图⑤对照阅读,公式恐惧症也能看懂。
知乎专栏⭐ 推荐SVPWM
📝
知乎专栏:FOC 和 SVPWM 技术常识综述
把 FOC 的常见概念(为什么变换、零矢量作用、母线利用率)串成一篇综述,适合当"预习/复习"材料。
知乎专栏综述
📝
知乎专栏:从理论到硬件,SVPWM 和 FOC 上手教程
从 BLDC 基本原理到硬件选型再到 FOC 实现的完整路线,与本站「理论→硬件→软件」三站结构互相印证。
知乎专栏入门路线
💻
CSDN:SimpleFOC(八)——理论+实践深度分析 SVPWM
SimpleFOC 源码级的 SVPWM 实现剖析(含七段式与代码),把本页第 12 节参考代码里的 svpwm() 讲得更透。
CSDN源码剖析
🎬
B 站:灯哥开源「手把手教写FOC算法」系列
从无刷电机概念到闭环位置控制的高播放量实战系列,逐集把代码敲出来——看别人怎么把本页的图变成会转的电机。
B 站视频⭐ 实战代码向
🎬
B 站:「手把手带你学FOC」系列(含最通俗的 SVPWM)
动画+实验结合的 FOC 系列课程,SVPWM 一讲用最直观的几何方式讲扇区与作用时间,配合本页第 5 节食用。
B 站视频动画直观
🔗 站内配套页面:04-02 固件与电机控制(代码级流水线) · 01-02 核心原理动画演示 · 02-13 功率级拓扑与 SVPWM 交互实验室 · 02-11 FOC 调参整定实战手册 · 04-17 状态观测器全解 · 06-11 保研复试面试题库