☰
FOC 从推导到落地:Clarke/Park 变换、SVPWM 与电流环调试
2026/10/15 10:19:18 网站建设 项目流程

简介:这份PDF笔记面向正在学习或准备实践FOC(磁场定向控制)的电机控制初学者与嵌入式开发者,系统整理了从理论推导到算法实现的完整脉络。内容围绕FOC核心思想展开,涵盖Clark变换与Park变换及其逆变换的数学推导、等幅值与恒功率变换系数的选择依据、SVPWM的扇区判断与矢量作用时间计算、过调制处理及马鞍波成因,并延伸至PID位置式与增量式算法、积分抗饱和方法以及磁链圆限制中MAX_MODULE与START_INDEX的确定。资源为1个PDF文件,压缩包约7.34MB,结构按知识点分节编排,便于按模块查阅与对照推导。目前已有1887人学习,适合希望打通FOC数学原理与工程实现之间衔接、补齐变换与调制细节的读者参考。

1. 从三条曲线到一张磁链圆:FOC 学习笔记到底该记什么

很多人第一次调 FOC,示波器上三根相电流像三条打架的蛇,转速一给就抖,电流环一上就啸叫。你翻遍教程,Clarke、Park、SVPWM、PID 每个词都认识,连起来就是跑不通。问题不在你笨,在于大部分资料把「数学推导」和「工程落地」切成了两段:推导只讲公式不讲为什么这么定义,代码只贴函数不讲参数从哪来。这份笔记想干的事,就是把 FOC 几大变换的数学推导、SVPWM 的扇区与作用时间、PID 与积分抗饱和、磁链圆限制串成一条线,让你从「知道有这几个模块」走到「自己能推、能写、能调」。适合刚上手电机控制的学生、转行做电控的嵌入式工程师,以及调了半天电流环还是发烫的从业者。下面按我实际踩过的顺序讲,不按教科书目录讲。

2. 三大变换的数学推导:从三相到两相到底在转什么

2.1 Clarke 变换的等幅值约定与系数 2/3

Clarke 变换的目标是把三相 120 度对称的量压到两相正交坐标。设三相为 $i_a, i_b, i_c$,且满足 $i_a + i_b + i_c = 0$,定义:

$$ i_\alpha = \frac{2}{3}\left(i_a - \frac{1}{2}i_b - \frac{1}{2}i_c\right) $$

$$ i_\beta = \frac{2}{3}\cdot\frac{\sqrt{3}}{2}(i_b - i_c) = \frac{1}{\sqrt{3}}(i_a + 2i_b) $$

这里系数取 $2/3$ 是等幅值约定:变换后 $\alpha\beta$ 矢量的模等于相电流幅值。另一种常见约定是等功率(系数 $\sqrt{2/3}$),两者只差一个比例,但混用会让你的 PID 参数整定全乱。我一般统一用等幅值,因为后面 SVPWM 的电压矢量模长直接对应相电压幅值,物理直觉清楚。

代码落地时不用每次算三角函数,直接写死系数:

import numpy as np def clarke_amplitude(ia, ib, ic): # 等幅值 Clarke,输入三相瞬时电流 # 前提:ia + ib + ic ≈ 0,硬件采样要保证 i_alpha = (2.0/3.0) * (ia - 0.5*ib - 0.5*ic) i_beta = (1.0/np.sqrt(3)) * (ia + 2.0*ib) return i_alpha, i_beta

逻辑说明:第二行用 $i_a + 2i_b$ 是因为代入 $i_c = -i_a - i_b$ 化简后得到,比直接写 $i_b - i_c$ 少一次减法。参数上唯一要盯的是采样三相之和是否接近零,如果传感器有偏置,$i_\alpha$ 会带直流分量,Park 之后就是转速频率的波动,表现为转矩脉动。

2.2 Park 变换与角度对齐:θ 从哪来、错了会怎样

Park 变换把静止的 $\alpha\beta$ 转到随转子旋转的 $dq$:

$$ i_d = i_\alpha \cos\theta + i_\beta \sin\theta $$

$$ i_q = -i_\alpha \sin\theta + i_\beta \cos\theta $$

$\theta$ 是转子电角度。表贴式永磁同步电机常用 $i_d = 0$ 控制,所以 $i_q$ 就是转矩电流。角度对齐是 FOC 最容易被忽略的一步:如果 $\theta$ 有固定偏差,$i_d$ 和 $i_q$ 会互相串,你给 $i_q$ 指令,实际有一部分变成了励磁,电机发热但不出力。

def park(ialpha, ibeta, theta_elec): # theta_elec 为电角度,单位 rad cos_t = np.cos(theta_elec) sin_t = np.sin(theta_elec) id_ = ialpha * cos_t + ibeta * sin_t iq_ = -ialpha * sin_t + ibeta * cos_t return id_, iq_

参数说明:$\theta$ 必须用电角度,等于机械角乘以极对数。常见翻车是编码器零点和电机 d 轴没对齐,上电先做一次对齐:给 d 轴通一个小电流,把转子吸到已知位置,再把编码器读数记为零点。这一步不做,后面所有推导都白搭。

2.3 反 Park 与逆 Clarke:把电压指令送回三相

电流环输出 $v_d, v_q$,要变回三相占空比,先反 Park:

$$ v_\alpha = v_d \cos\theta - v_q \sin\theta $$

$$ v_\beta = v_d \sin\theta + v_q \cos\theta $$

再逆 Clarke 得到三相电压,或者直接送 SVPWM。逆 Clarke 等幅值形式:

$$ v_a = v_\alpha $$

$$ v_b = -\frac{1}{2}v_\alpha + \frac{\sqrt{3}}{2}v_\beta $$

$$ v_c = -\frac{1}{2}v_\alpha - \frac{\sqrt{3}}{2}v_\beta $$

def inv_park(vd, vq, theta_elec): cos_t = np.cos(theta_elec) sin_t = np.sin(theta_elec) valpha = vd * cos_t - vq * sin_t vbeta = vd * sin_t + vq * cos_t return valpha, vbeta def inv_clarke(valpha, vbeta): va = valpha vb = -0.5*valpha + (np.sqrt(3)/2)*vbeta vc = -0.5*valpha - (np.sqrt(3)/2)*vbeta return va, vb, vc

这三组变换合起来就是 FOC 的骨架。推导不难,难在每一步的系数约定和角度定义要前后一致。我习惯在代码里把「等幅值」写进注释,换人接手也不会混。

3. SVPWM 的过程:六个扇区、七段式与作用时间怎么算

3.1 电压矢量与扇区判断

三相逆变器有 8 种开关状态,对应 6 个非零矢量和 2 个零矢量。把 $v_\alpha, v_\beta$ 送进来,先判断落在哪个 60 度扇区。常用判据:

$$ A = v_\beta $$

$$ B = \frac{\sqrt{3}}{2}v_\alpha - \frac{1}{2}v_\beta $$

$$ C = -\frac{\sqrt{3}}{2}v_\alpha - \frac{1}{2}v_\beta $$

根据 $A, B, C$ 的正负组合得到扇区号。这一步用整数运算就能做,不用查表。

def svpwm_sector(valpha, vbeta): # 返回扇区 1~6 A = vbeta B = (np.sqrt(3)*valpha - vbeta) / 2.0 C = (-np.sqrt(3)*valpha - vbeta) / 2.0 # 符号函数构造扇区索引 N = (A > 0) + 2*(B > 0) + 4*(C > 0) # N 与扇区映射,常见做法用查表 sector_map = {3:1, 1:2, 5:3, 4:4, 6:5, 2:6} return sector_map[N]

逻辑说明:$N$ 是三位二进制编码,映射表因 $A,B,C$ 定义顺序不同会有差异,一定要拿已知矢量验证,比如 $v_\alpha>0, v_\beta=0$ 应该落在扇区 1。我见过有人直接抄表不验证,结果电机转起来像步进电机。

3.2 相邻矢量作用时间与七段式对称

以扇区 1 为例,相邻矢量是 $V_1$ 和 $V_2$,作用时间:

$$ T_1 = \frac{\sqrt{3} T_s}{U_{dc}} \left(v_\alpha - \frac{v_\beta}{\sqrt{3}}\right) $$

$$ T_2 = \frac{\sqrt{3} T_s}{U_{dc}} \cdot \frac{2 v_\beta}{\sqrt{3}} $$

其中 $T_s$ 是 PWM 周期,$U_{dc}$ 是母线电压。零矢量时间 $T_0 = T_s - T_1 - T_2$,平均分给 $V_0$ 和 $V_7$ 构成七段式,对称输出谐波小。

def svpwm_times(valpha, vbeta, udc, ts): sector = svpwm_sector(valpha, vbeta) # 以扇区1为例,其他扇区做角度旋转 if sector == 1: t1 = (np.sqrt(3)*ts/udc) * (valpha - vbeta/np.sqrt(3)) t2 = (np.sqrt(3)*ts/udc) * (2*vbeta/np.sqrt(3)) # 其余扇区省略,实际用旋转或查表 t0 = ts - t1 - t2 return t1, t2, t0

参数说明:$U_{dc}$ 要实测,不能拿标称值,母线跌落会让实际输出电压偏小。$T_s$ 一般取 10kHz~20kHz,太高开关损耗大,太低电流纹波大。$T_1+T_2$ 超过 $T_s$ 说明电压指令超出六边形内切圆,必须做限幅,这就是下一章的磁链圆限制。

3.3 从作用时间到比较寄存器

七段式把 $T_0/2, T_1, T_2, T_0/2$ 对称排布,得到三个比较值 $T_a, T_b, T_c$,再换算成定时器 CCR。不同扇区的排布不同,工程上常用「先算基础值再按扇区重排」的写法,减少重复代码。写完后用示波器看相电压,应该是马鞍波,如果看到明显平顶,多半是限幅没做对。

4. PID 与积分抗饱和:电流环为什么一给大指令就失控

4.1 电流环 PI 的参数从电机参数来

电流环被控对象近似一阶,$G(s) = \frac{1}{Ls + R}$。用零极点对消,令 $K_p/K_i = L/R$,闭环带宽由 $K_p$ 决定。常见做法:

$$ K_p = \omega_c L $$

$$ K_i = \omega_c R $$

$\omega_c$ 取电流环带宽,一般 1kHz~2kHz。这样整定比盲试快得多。

def current_pi_gains(L, R, wc): # L 单位 H,R 单位 ohm,wc 单位 rad/s kp = wc * L ki = wc * R return kp, ki

参数说明:$L$ 和 $R$ 用万用表和 LCR 表实测,别信手册典型值。带宽不能超过开关频率的 1/10,否则采样和 PWM 延迟会让相位裕度不够,表现为高频啸叫。

4.2 积分抗饱和的三种写法

电压指令超出母线能输出的范围时,积分器还在累加,退饱和要很久,这就是积分饱和。现象是给一个大阶跃,电流冲上去后回落很慢,甚至振荡。三种常见做法:

方法思路适用
钳位法积分项单独限幅简单,效果一般
反算法用饱和误差回灌积分器退饱和快
条件积分饱和时停止积分实现最简

我一般用反算法,退饱和最干脆:

class PIController: def __init__(self, kp, ki, out_min, out_max): self.kp, self.ki = kp, ki self.out_min, self.out_max = out_min, out_max self.integral = 0.0 def update(self, error, dt): p_term = self.kp * error # 先算未限幅输出 unsat = p_term + self.integral + self.ki * error * dt # 限幅 sat = max(self.out_min, min(self.out_max, unsat)) # 反算:把饱和误差回灌,kaw 取 1/ki 或经验值 kaw = 1.0 / self.ki if self.ki != 0 else 0.0 self.integral += self.ki * error * dt + kaw * (sat - unsat) return sat

逻辑说明:kaw是反算增益,取 $1/K_i$ 是常见起点,实际调试时看退饱和速度微调。注意dt要稳定,用定时器中断里实测的周期,不要用理论值。

4.3 速度环与电流环的带宽分离

速度环带宽一般取电流环的 1/5~1/10,否则两个环互相耦合,表现为低速爬行或高速抖动。速度环输出是 $i_q$ 指令,所以速度环的限幅就是最大转矩电流。调试顺序永远是先电流环、再速度环,电流环没调好就上速度环,等于在沙地上盖楼。

5. 磁链圆限制与避坑:电压矢量超出六边形之后

5.1 磁链圆限制的几何含义

SVPWM 能输出的最大不失真电压矢量是六边形内切圆,半径 $U_{dc}/\sqrt{3}$。如果 $v_\alpha, v_\beta$ 的模超过这个值,$T_1+T_2 > T_s$,必须限幅。常见做法是等比例缩放:

$$ k = \frac{U_{max}}{\sqrt{v_\alpha^2 + v_\beta^2}} $$

当 $k < 1$ 时,$v_\alpha, v_\beta$ 同乘 $k$。这样保持电压矢量方向不变,只压模长,转矩响应会变软但不会失控。

def flux_circle_limit(valpha, vbeta, udc): umax = udc / np.sqrt(3) mag = np.hypot(valpha, vbeta) if mag > umax: scale = umax / mag return valpha*scale, vbeta*scale return valpha, vbeta

参数说明:$U_{dc}$ 用实时采样值,母线波动时限制跟着变。有些方案用六边形直接限幅,输出电压略高但谐波大,看应用取舍。

5.2 避坑记录:五个我实际踩过的坑

现象一:上电电机啸叫,电流波形毛刺。原因:PWM 死区没补偿,低压时死区占比大。解决:加死区补偿,或者提高母线电压测试。

现象二:给定转速后电机反转。原因:编码器计数方向或相序接反。解决:交换任意两相线,或软件里取反角度。

现象三:电流环一上就振荡。原因:带宽设太高,超过开关频率 1/10。解决:降 $K_p$,先保证稳定再提带宽。

现象四:大指令下退饱和慢。原因:积分器没有抗饱和。解决:按 4.2 加反算,kaw从 $1/K_i$ 起调。

现象五:高速时电流波形失真。原因:电压矢量超出内切圆没限幅。解决:加 5.1 的磁链圆限制,并确认 $U_{dc}$ 采样准确。

提示:每次改参数只改一个,改完记录波形。FOC 调试是排除法,不是玄学。

6. 进阶技巧:用 Python 仿真验证你的推导再上硬件

硬件调试成本高,烧管、炸电容都是钱。我习惯先在 Python 里把整套 FOC 跑一遍,验证变换系数、扇区判断、限幅逻辑,再移植到 MCU。下面是一个最小仿真框架,跑一个电流环阶跃,看 $i_q$ 跟踪。

import numpy as np import matplotlib.pyplot as plt # 电机参数 R, L = 0.5, 0.001 wc = 2*np.pi*1000 kp, ki = wc*L, wc*R ts = 1e-4 # 10kHz udc = 24.0 pi = PIController(kp, ki, -udc/np.sqrt(3), udc/np.sqrt(3)) iq_ref = 1.0 iq = 0.0 log = [] for k in range(2000): err = iq_ref - iq vq = pi.update(err, ts) # 简化:只跑 q 轴,d 轴为 0 diq = (vq - R*iq) / L * ts iq += diq log.append(iq) plt.plot(log) plt.xlabel('step') plt.ylabel('iq (A)') plt.title('current loop step response') plt.grid(True) plt.show()

逻辑说明:这里忽略了反电动势和交叉耦合,只验证 PI 和限幅逻辑。实际移植时把 Clarke、Park、SVPWM 按第 2、3 章补上。参数上ts要和 MCU 中断周期一致,udc用实测值。看阶跃响应有没有超调、退饱和快不快,调好了再上硬件,能省很多返工。

我自己的习惯是:任何新电机,先仿真跑通,再拿低压小电流上电,最后才加满载。这套流程帮我省过至少两次炸管。希望帮到你。

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

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

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

立即咨询