【更新公告】
- 2025.07.05 - 08.17 修改部分:1 基本概念 ~ 4.2.3 空间矢量脉宽调制(SVPWM)
- 2025.09.21 - 10.07 修改部分:4.2.4 比例积分(PI)双环控制
本文是在本人 2024 年本科毕业设计期间的自学记录,更多是对网上资料的摘抄引用整理,不出于任何商业目的,如有侵权,请联系删除,谢谢!本文写作时间较为匆忙,存在许多表述不严谨甚至错误之处,欢迎在评论区指正!感谢广大网友!目前处于研究生阶段,选择电机方向进行深造,会对本文继续更新完善!
本文推荐前往 PC 端网页食用。
※ 强烈推荐阅读:
- 夏长亮 著《无刷直流电机控制系统》
- 袁雷 著《现代永磁同步电机控制原理及 MATLAB 仿真》
0 主要引用出处
- 文段:
- 瑞萨电子官网 何谓BLDC电机 | Renesas、BLDC电机的控制 | Renesas 瑞萨电子
- MATLAB 官网 克拉克和帕克变换、计算最大转矩电流比 (MTPA) 和弱磁运行的参考电流
- ★知乎稚晖君【自制FOC驱动器】深入浅出讲解FOC算法与SVPWM技术
- ★CSDN 博主 爱出名的狗腿子博客专栏电机控制
- ★灯神 写一个比SimpleFOC更好的库
- ★Switchcraft Vector control for dummies、Space Vector PWM Intro
- ★知乎玻璃伞 彻底吃透SVPWM如此简单
- 视频:
- 广州大学《机电一体化》课程视频 无刷直流电动机的特点、结构、原理和控制
- 德州仪器培训视频 深入理解无刷直流电机(BLDC)原理以及控制
- ★EEWORLD 大学堂 matlab与电机控制
- EEWORLD 大学堂 无刷直流电机基础 - 驱动控制
- b 站 up 主爱上半导体视频 无刷直流电机的工作原理
- 油管 How To Mechatronics How Brushless Motor and ESC Work and How To Control them using Arduino
- 油管 Jantzen Lee The Clarke and Park transformations (Episode 8)
- 书籍:
- 胡敏强等 著《电机学》
- 阮新波 著《电力电子技术》
- 夏长亮 著《无刷直流电机控制系统》
- 袁雷 著《现代永磁同步电机控制原理及 MATLAB 仿真》
1 基本概念
1.1 BLDC 与 PMSM
永磁无刷电机,也称为电子换向电动机,是一种没有电刷和换向器的电动机,根据转子永磁体位置调整定子电流以产生相应转矩。夏长亮老师在其书《无刷直流电机控制系统》中写道,国内外对无刷直流电机的定义一般有两种:
- 一种定义认为只有梯形波/方波无刷直流电机才可以被称为无刷直流电机(BLDC,即 Brushless DC Motor),而正弦波无刷电机则被称为永磁同步电机(PMSM,即 Permanent Magnet Synchronous Motor);

- 另一种定义认为梯形波/方波无刷电机和正弦波无刷电机都是无刷直流电机。
但迄今为止,还没有一个公认的统一标准对无刷直流电机进行准确的分类或者定义。本文采用第一种定义,以反电动势(可对转子外施扭矩,测量定子开路的 A 相电压从而得反电势波形)区分二者。一般来说,BLDC 的定子绕组通常采用集中整距绕组,具有梯形波反电势;而 PMSM 则往往使用分布短距绕组,具有正弦波反电势。不过,二者对应绕组并不绝对,若还要深究二者区别可参看:知乎彻底搞懂BLDC与PMSM的区别。
![]()
| 电机 | PMSM(永磁同步电机) | BLDC(无刷直流电机) |
|---|---|---|
| 换相方式 | 正弦波换相(Sine wave commutation) | 梯形波换相(Trapezoidal commutation) |
| 控制算法 | 通常使用 FOC(磁场定向控制) | 通常使用六步换相控制 |
| 电动势波形(EMF) | 正弦波 | 梯形波 |
| 转矩输出 | 更平滑(几乎无力矩波动) | 有一定转矩脉动 |
| 复杂度和成本 | 控制更复杂,成本略高 | 控制简单,成本较低 |
| 应用场景 | 高频精密控制、电动汽车电机等 | 泵、风扇、电动工具、小型电动车 |
1.2 物理结构
永磁有刷直流电动机和永磁无刷直流电动机,在结构上除了有无电刷之外,还有一个重要区别在于,无刷电机中将电枢和磁极的位置进行了互换,即电枢固定不动放在定子上,而磁极放到旋转的转子上,这样电机结构简单,便于电子换向的实现。
永磁有刷电机:
永磁无刷电机(内转子式):
无刷电机由电动机本体、位置传感器和控制器三部分组成。电动机本体的主要部件是定子和转子。转子由永磁体、铁芯和支撑部件构成。永磁体通常采用径向充磁的铁氧体或多铁硼,做成瓦片形或环形,贴装在转子铁芯表面,这种结构称之为表面贴装式。


1.3 工作特性(可以先跳过)
无刷电机具有串励直流电机启动特性和并励直流电机调速特性。无刷电机通常无励磁绕组,但是其特性与有刷电机的特性有相通之处:
- 启动特性与串励直流电机相似

串励电动机有很大的起动转矩,很强的过载能力。启动瞬间,电机转速接近零,反电动势极小,E ≈ 0,电枢电流 Ia 达到最大值。由于励磁电流与电枢电流相同,电流同时增强了主磁场和电枢磁场,产生极大转矩,此时铁芯还未饱和,有 。

虽然无刷电机的磁场由永磁体提供(固定),但启动电流可以很大(由控制器设定)。此外,电子控制器可以选择最佳换相角度,最大化电磁转矩。所以,启动瞬间的转矩输出接近串励电机的“强磁+大电流”特性。
- 调速特性与并励直流电机相似

由于励磁绕组与电枢绕组是并联接入电源的,所以并励电动机如果端电压 U 不变, 不变,则 If 也不变,当负载电流很小时,电枢反应去磁作用也很小,可认为磁通
为常数, 根据
,电磁转矩 T 和电枢电流 Ia 成正比,
是通过坐标原点的直线,如下图中的实线所示。另外,直线末端的弯曲则是因为,当负载电流较大时,由于电枢反应的去磁作用增大(近似可看成与负载电流成正比),使每极磁通减少,这时电磁转矩略有减小。

无刷电机的永磁转子提供固定磁场(类似并励的“恒励磁”),控制器通过调节供电电压/占空比/PWM 频率,线性地改变绕组电流和反电动势,从而进行线性调速。所以,无刷电机的速度控制特性就像并励电机一样“线性、可控”。
2 各电机比较
《电机学》书中写道,由于电刷和换向器存在机械接触,换回时产生的换向火花会引起电刷和换回器磨损、电磁干扰、噪声等问题,导致电机可靠性较差,易产生故障需要经常维护,限制了有刷直流电动机的应用场合。要根本解决这些问题,就必须去掉电刷和换向器,消除机械接触,这就促成了永磁无刷直流电动机的出现和发展。
各电机比较具体可看笔者的另一篇博客:【无刷电机学习】各种电机优势比较-CSDN博客,本文不再赘述。
| 特性 | 无刷直流电机 | 永磁有刷直流电动机 | 交流感应电动机 |
| 定子 | 多相绕组 | 永磁 | 多相绕组事 |
| 转子 | 永磁 | 绕组 | 线绕组或笼型绕组 |
| 转子位置传感器 | 需要 | 不需要 | 不需要 |
| 电滑动接触火花 | 无 | 有,换向器与电刷 | 无,或可能有集电环 |
| EMC干扰 | 较低 | 高 | 低 |
| 可闻噪声 | 较低 | 高 | 低 |
| 电子控制器 | 必需 | 不是必需,调速时需要 | 不是必需,调速时需要 |
| 使用电源 | DC | DC | AC |
| 使用电压范围 | 高,受功率器件耐压限制 | 较低,受换向器耐压限制 | 高 |
| 机械特性 | 接近线性 | 线性 | 非线性 |
| 起动转矩倍数 | 较高 | 较高 | 较低 |
3 基本原理
推荐先观看 b 站 up 主爱上半导体视频:无刷直流电机的工作原理,本部分作为视频笔记帮助回顾。
3.1 单相无刷电机
初中关于通电螺线管的物理小实验大家应该都不太陌生,基于右手螺旋定则可判断通过直流电螺线圈的极性方向:
下图中的单相无刷电机(外转子式)则正是通过变换流入 a、b 的电流方向,从而改变线圈极性,并基于“同性相斥、异性相吸”产生转动。

其中,定子的上下线圈的绕向相反,则通电时极性相同:

流入 a、b 的电流方向则是通过 H 桥上下桥臂的开关组合进行改变(电流的方向可控制电机正反转):

利用单片机进行控制时,H 桥上的 S1 - S4 这四个开关由 MOS 管来代替,这样便可通过对 MOS 管进行脉宽调制(PWM,即 Pulse Width Modulation)来控制转速:保持 S2 导通,按一定频率开断 S3 ,即对 S3 门极输入 PWM 波进行控制。
【(知识复习)PWM 调速】
显然,占空比越高,等效输出的电压就越高。电机线圈电压越大显然线圈电流越大,对应的磁场强度越大,则电机扭矩/转速越大。
【为什么 PWM 频率 f_PWM 应该远高于电机时间常数 τ 的倒数?】
当开关频率 f_PWM 较低时,电机表现为反复加速和减速,这是由于电感电流(下图黄线)会发生较明显的升降,导致输出转矩变大变小。
而当我们将 PWM 的频率 f_PWM 增加至一个合理值时,电感电流将平稳许多,这将提高调速性能。该合理值应远大于 1/τ,为什么呢?
电机常数 τ(τ = L / R,将目前电路视为一阶零状态响应 RL 串联电路模型),用于表示电流变化的快慢,即电流每次要明显上升或下降,至少需要 τ 秒。若 PWM 周期 T ≪ τ(即 f_PWM ≫ 1/τ),则说明电流在每个 PWM 周期内变化很少、较为平滑。
所以,PWM 频率要足够快(远大于 1/τ),才能让电流来不及产生明显的波动,近似恒定,从而使控制效果稳定。
3.2 三相无刷电机
三相无刷电机的三个线圈则是彼此独立的。由于依次导通单个线圈的方式线圈利用率低,故常采取星形连接一次导通两相或三相。
外转子:

内转子:

显然,之前的四个 MOS 管已经无法满足驱动要求,此时就需要采用三相逆变器(Three-Phase Inverter)对电机进行驱动:

其中一个磁极受力矢量动图如下:

4 驱动方法及相关控制代码
4.1 BLDC——六步换相控制
4.1.1 基本原理
从 U 相向 W 相通电,则会产生 2 个方向不同的磁场矢量,而这两个磁场矢量可以合成一个指向右下 30° 方向的总磁场。

【(知识复习)磁感应强度(磁通密度)B 与磁通量
辨析】
磁场的大小和方向可用基本物理量磁感应强度来描述,用符号 B 表示,单位是 T(特斯拉),是一个矢量。文中的磁场矢量指的就是 B。
通过磁场中某一面积的磁感应线数称为通过该面积的磁通量,简称磁通,用符号
表示。在国际单位制中它的单位是 Wb (韦伯),它是一个标量。

以极对数(Number of Pole Pairs)为 1 的内转子 BLDC 为例。如上述所示,每次同时控制两个线圈导通,按顺序从 1 - 6 变更通电模式,则合成磁场矢量将顺时针旋转。一般将切换这 6 种通电模式来控制电机的控制方法称为“六步换相控制(Six-Step Commutation)”,或称“梯形控制(Trapezoidal Control)”。
【(知识复习)极对数 p】
极对数是磁极的对数,决定了电角度与机械角度之间的关系:
其中,
(Electrical Angle):表示的是旋转磁场的角度,它决定了驱动波形、换相等;
(Mechanical Angle):表示的是转子实际旋转的角度,对应物理旋转角度。
【换相】
电机转动时,为了始终让转矩方向合适,需要不断切换供电相位(U、V、W三相),这个操作叫“换相”。
将“通电模式 1”改为“通电模式 2”,由图可知合成磁场矢量的方向将变化 60°,转子在磁力作用下发生旋转。接下来,从“通电模式 2”改为“通电模式 3”,则合成磁场的方向将再次变化 60°,转子将再次被该变化所吸引而转动。
六步换相这样“通电两相、关闭一相”的控制方式,刚好适配 BLDC 这种“恒定电压段 + 快速过渡段”的梯形波 EMF。该模式控制方法简单、成本低,且不需要复杂矢量变换,但是其旋转动作较为生硬,有时还会发出噪音。

下图三个电机均采用六步换相控制,从左到右依次为每 60°、每 30°、每 15°(均指机械角度)进行一次换相:
【此处的 3 个角度如何得到?】
由前文可知,单极对电机转子旋转一圈需换相 6 次,故每个极对需要进行 6 次换相,我们可以推知,极对数为 2 的转子旋转一圈需完成 12(2 * 6)次换相,则每隔 30°(360° / 12)的机械角度换相一次;极对数为 4 的转子旋转一圈需完成 24(4 * 6)次换相,则每隔 15°(360° / 24)的机械角度换相一次。
由此观之,极对数越多,控制精度越高。
下图所示即换向逻辑,图中 A(U)、B(V)、C(W) 三个字母代表相(Phase);H 和 L 分别代表高侧(High Side)和低侧(Low Side):

基于 DSP28335 的控制各扇区对应开关管导通代码编写如下:
void MOS_Q41PWM(void)
{ // 通电相位:V- U+
EALLOW;
EPwm1Regs.AQCSFRC.bit.CSFA = 0; // 1A 无效
EPwm1Regs.AQCSFRC.bit.CSFB = 1; // 1B 强制低
EPwm2Regs.AQCSFRC.bit.CSFA = 1; // 2A 连续低(在下一个 TBCLK 边沿发生作用)
EPwm2Regs.AQCSFRC.bit.CSFB = 2; // 2B 连续高
EPwm3Regs.AQCSFRC.bit.CSFA = 1;
EPwm3Regs.AQCSFRC.bit.CSFB = 1;
EPwm1Regs.AQCTLA.bit.CAU = AQ_SET; // CTR = CAU 时,将 ePWM1A 置高
EPwm1Regs.AQCTLA.bit.CAD = AQ_CLEAR;// CTR = CAD 时,将 ePWM1A 置低
EPwm1Regs.AQCTLB.bit.CBU = AQ_CLEAR;
EPwm1Regs.AQCTLB.bit.CBD = AQ_CLEAR;
EPwm2Regs.AQCTLA.bit.CAU = AQ_CLEAR;
EPwm2Regs.AQCTLA.bit.CAD = AQ_CLEAR;
EPwm2Regs.AQCTLB.bit.CBU = AQ_SET; // CTR = CBU 时,将 ePWM2B 置高
EPwm2Regs.AQCTLB.bit.CBD = AQ_SET; // CTR = CBD 时,将 ePWM2B 置高
EPwm3Regs.AQCTLA.bit.CAU = AQ_CLEAR;
EPwm3Regs.AQCTLA.bit.CAD = AQ_CLEAR;
EPwm3Regs.AQCTLB.bit.CBU = AQ_CLEAR;
EPwm3Regs.AQCTLB.bit.CBD = AQ_CLEAR;
EDIS;
}
void MOS_Q16PWM(void)
{ // 通电相位:U+ W-
EALLOW;
EPwm1Regs.AQCSFRC.bit.CSFA = 0;
EPwm1Regs.AQCSFRC.bit.CSFB = 1;
EPwm2Regs.AQCSFRC.bit.CSFA = 1;
EPwm2Regs.AQCSFRC.bit.CSFB = 1;
EPwm3Regs.AQCSFRC.bit.CSFA = 1;
EPwm3Regs.AQCSFRC.bit.CSFB = 2;
EPwm1Regs.AQCTLA.bit.CAU = AQ_SET; // CTR = CAU 时,将 ePWM1A 置高
EPwm1Regs.AQCTLA.bit.CAD = AQ_CLEAR;
EPwm1Regs.AQCTLB.bit.CBU = AQ_CLEAR;
EPwm1Regs.AQCTLB.bit.CBD = AQ_CLEAR;
EPwm2Regs.AQCTLA.bit.CAU = AQ_CLEAR;
EPwm2Regs.AQCTLA.bit.CAD = AQ_CLEAR;
EPwm2Regs.AQCTLB.bit.CBU = AQ_CLEAR;
EPwm2Regs.AQCTLB.bit.CBD = AQ_CLEAR;
EPwm3Regs.AQCTLA.bit.CAU = AQ_CLEAR;
EPwm3Regs.AQCTLA.bit.CAD = AQ_CLEAR;
EPwm3Regs.AQCTLB.bit.CBU = AQ_SET; // CTR = CBU 时,将 ePWM3B 置高
EPwm3Regs.AQCTLB.bit.CBD = AQ_SET;
EDIS;
}
void MOS_Q63PWM(void)
{ // 通电相位:V+ W-
EALLOW;
EPwm1Regs.AQCSFRC.bit.CSFA = 1;
EPwm1Regs.AQCSFRC.bit.CSFB = 1;
EPwm2Regs.AQCSFRC.bit.CSFA = 0;
EPwm2Regs.AQCSFRC.bit.CSFB = 1;
EPwm3Regs.AQCSFRC.bit.CSFA = 1;
EPwm3Regs.AQCSFRC.bit.CSFB = 2;
EPwm1Regs.AQCTLA.bit.CAU = AQ_CLEAR;
EPwm1Regs.AQCTLA.bit.CAD = AQ_CLEAR;
EPwm1Regs.AQCTLB.bit.CBU = AQ_CLEAR;
EPwm1Regs.AQCTLB.bit.CBD = AQ_CLEAR;
EPwm2Regs.AQCTLA.bit.CAU = AQ_SET; // CTR = CAU 时,将 ePWM2A 置高
EPwm2Regs.AQCTLA.bit.CAD = AQ_CLEAR;
EPwm2Regs.AQCTLB.bit.CBU = AQ_CLEAR;
EPwm2Regs.AQCTLB.bit.CBD = AQ_CLEAR;
EPwm3Regs.AQCTLA.bit.CAU = AQ_CLEAR;
EPwm3Regs.AQCTLA.bit.CAD = AQ_CLEAR;
EPwm3Regs.AQCTLB.bit.CBU = AQ_SET; // CTR = CBU 时,将 ePWM3B 置高
EPwm3Regs.AQCTLB.bit.CBD = AQ_SET;
EDIS;
}
void MOS_Q32PWM(void)
{ // 通电相位:V+ U-
EALLOW;
EPwm1Regs.AQCSFRC.bit.CSFA = 1;
EPwm1Regs.AQCSFRC.bit.CSFB = 2;
EPwm2Regs.AQCSFRC.bit.CSFA = 0;
EPwm2Regs.AQCSFRC.bit.CSFB = 1;
EPwm3Regs.AQCSFRC.bit.CSFA = 1;
EPwm3Regs.AQCSFRC.bit.CSFB = 1;
EPwm1Regs.AQCTLA.bit.CAU = AQ_CLEAR;
EPwm1Regs.AQCTLA.bit.CAD = AQ_CLEAR;
EPwm1Regs.AQCTLB.bit.CBU = AQ_SET; // CTR = CBU 时,将 ePWM1B 置高
EPwm1Regs.AQCTLB.bit.CBD = AQ_SET;
EPwm2Regs.AQCTLA.bit.CAU = AQ_SET; // CTR = CAU 时,将 ePWM2A 置高
EPwm2Regs.AQCTLA.bit.CAD = AQ_CLEAR;
EPwm2Regs.AQCTLB.bit.CBU = AQ_CLEAR;
EPwm2Regs.AQCTLB.bit.CBD = AQ_CLEAR;
EPwm3Regs.AQCTLA.bit.CAU = AQ_CLEAR;
EPwm3Regs.AQCTLA.bit.CAD = AQ_CLEAR;
EPwm3Regs.AQCTLB.bit.CBU = AQ_CLEAR;
EPwm3Regs.AQCTLB.bit.CBD = AQ_CLEAR;
EDIS;
}
void MOS_Q25PWM(void)
{ // 通电相位:U- W+
EALLOW;
EPwm1Regs.AQCSFRC.bit.CSFA = 1;
EPwm1Regs.AQCSFRC.bit.CSFB = 2;
EPwm2Regs.AQCSFRC.bit.CSFA = 1;
EPwm2Regs.AQCSFRC.bit.CSFB = 1;
EPwm3Regs.AQCSFRC.bit.CSFA = 0;
EPwm3Regs.AQCSFRC.bit.CSFB = 1;
EPwm1Regs.AQCTLA.bit.CAU = AQ_CLEAR;
EPwm1Regs.AQCTLA.bit.CAD = AQ_CLEAR;
EPwm1Regs.AQCTLB.bit.CBU = AQ_SET; // CTR = CBU 时,将 ePWM1B 置高
EPwm1Regs.AQCTLB.bit.CBD = AQ_SET;
EPwm2Regs.AQCTLA.bit.CAU = AQ_CLEAR;
EPwm2Regs.AQCTLA.bit.CAD = AQ_CLEAR;
EPwm2Regs.AQCTLB.bit.CBU = AQ_CLEAR;
EPwm2Regs.AQCTLB.bit.CBD = AQ_CLEAR;
EPwm3Regs.AQCTLA.bit.CAU = AQ_SET; // CTR = CAU 时,将 ePWM3A 置高
EPwm3Regs.AQCTLA.bit.CAD = AQ_CLEAR;
EPwm3Regs.AQCTLB.bit.CBU = AQ_CLEAR;
EPwm3Regs.AQCTLB.bit.CBD = AQ_CLEAR;
EDIS;
}
void MOS_Q54PWM(void)
{ // 通电相位:V- W+
EALLOW;
EPwm1Regs.AQCSFRC.bit.CSFA = 1;
EPwm1Regs.AQCSFRC.bit.CSFB = 1;
EPwm2Regs.AQCSFRC.bit.CSFA = 1;
EPwm2Regs.AQCSFRC.bit.CSFB = 2;
EPwm3Regs.AQCSFRC.bit.CSFA = 0;
EPwm3Regs.AQCSFRC.bit.CSFB = 1;
EPwm1Regs.AQCTLA.bit.CAU = AQ_CLEAR;
EPwm1Regs.AQCTLA.bit.CAD = AQ_CLEAR;
EPwm1Regs.AQCTLB.bit.CBU = AQ_CLEAR;
EPwm1Regs.AQCTLB.bit.CBD = AQ_CLEAR;
EPwm2Regs.AQCTLA.bit.CAU = AQ_CLEAR;
EPwm2Regs.AQCTLA.bit.CAD = AQ_CLEAR;
EPwm2Regs.AQCTLB.bit.CBU = AQ_SET;
EPwm2Regs.AQCTLB.bit.CBD = AQ_SET;
EPwm3Regs.AQCTLA.bit.CAU = AQ_SET;
EPwm3Regs.AQCTLA.bit.CAD = AQ_CLEAR;
EPwm3Regs.AQCTLB.bit.CBU = AQ_CLEAR;
EPwm3Regs.AQCTLB.bit.CBD = AQ_CLEAR;
EDIS;
}
4.1.2 系统框图及代码

基于 DSP28335 的控制开关管占空比代码编写如下(占空比从何而来将会在后文转速测算处给出):
void Svpwm_Outpwm(Uint16 duty)
{
EPwm1Regs.CMPA.half.CMPA = duty;
EPwm1Regs.CMPB = duty;
EPwm2Regs.CMPA.half.CMPA = duty;
EPwm2Regs.CMPB = duty;
EPwm3Regs.CMPA.half.CMPA = duty;
EPwm3Regs.CMPB = duty;
}
另附:
【1】基于 DSP28335 的六步换向 ePWM 初始化代码:
#define ISR_FREQUENCY 12.5
#define SYSTEM_FREQUENCY 150
float32 T = 0.001/ISR_FREQUENCY;
// T为采样周期(s),其中开关频率ISR_FREQUENCY数值为12.5(kHz),故转换为s作为单位时需要*1/1000。
// 开关频率ISR_FREQUENCY此处设为12.5(12.5kHZ),则采样周期T为0.00008s即0.08ms(80us)。
// 在电机控制中,采样频率一般与开关频率相同。
void EPWM_int(void)
{
// 150MHz,即1s之中计数150M次,则一个采样周期内计数(150M*T)次
// 注意!赋予寄存器的为计数值!
// 因为在向上下模式计数时,Tpwm = 2*TBPRD*T(TBCLK),所以TBPRD(即PeriodMax)为一个采样周期计数值的1/2,即(150M*T)/2次
PWM_PeriodMax = SYSTEM_FREQUENCY*1000000*T/2; // 6000
// PWM_HalfPerMax = PWM_PeriodMax/2; // HalfPerMax 为 TBPRD/2
PWM_Deadband = 2.0*SYSTEM_FREQUENCY;
EALLOW;
/* 初始化 EPWM1-EPWM3 时基周期寄存器 */
EPwm1Regs.TBPRD = PWM_PeriodMax; // Set timer period 1500
EPwm2Regs.TBPRD = PWM_PeriodMax; // Set timer period 1500
EPwm3Regs.TBPRD = PWM_PeriodMax; // Set timer period 1500
/* 初始化 EPWM1-EPWM3 时基相位寄存器 */
EPwm1Regs.TBPHS.half.TBPHS = 0x0000; // Phase is 0
EPwm2Regs.TBPHS.half.TBPHS = 0x0000; // Phase is 0
EPwm3Regs.TBPHS.half.TBPHS = 0x0000; // Phase is 0
// Clear counter
EPwm1Regs.TBCTR = 0x0000;
EPwm2Regs.TBCTR = 0x0000;
EPwm3Regs.TBCTR = 0x0000;
/* 初始化 EPWM1-EPWM3 时基控制寄存器 */
// 计数模式 CTRMODE,0x2(10):向上-下计数
// 计数寄存器装载相位寄存器使能位 PHSEN,0x0:禁止装载
// 高速时基时钟分频位 HSPCLKDIV,0x0:/1
// 时基时钟分频位 CLKDIV,0x0:/1
// TBCLK = SYSCLKOUT/(HSPCLKDIV × CLKDIV)
EPwm1Regs.TBCTL.bit.CTRMODE = 0x2;
EPwm1Regs.TBCTL.bit.PHSEN = 0x0;
EPwm1Regs.TBCTL.bit.HSPCLKDIV = 0x0;
EPwm1Regs.TBCTL.bit.CLKDIV = 0x0; //?? 0 60M / 1*1*2 / 2*1500 = 10K
EPwm2Regs.TBCTL.bit.CTRMODE = 0x2;
EPwm2Regs.TBCTL.bit.PHSEN = 0x0;
EPwm2Regs.TBCTL.bit.HSPCLKDIV = 0x0;
EPwm2Regs.TBCTL.bit.CLKDIV = 0x0;
EPwm3Regs.TBCTL.bit.CTRMODE = 0x2;
EPwm3Regs.TBCTL.bit.PHSEN = 0x0;
EPwm3Regs.TBCTL.bit.HSPCLKDIV = 0x0;
EPwm3Regs.TBCTL.bit.CLKDIV = 0x0;
/* 初始化 EPWM1-EPWM3 计数比较控制寄存器 */
EPwm1Regs.CMPCTL.bit.SHDWAMODE = 0x0;
EPwm1Regs.CMPCTL.bit.SHDWBMODE = 0x0; //Active Counter-CompareA(CMPA) Load From Shadow Select Mode 0
EPwm1Regs.CMPCTL.bit.LOADAMODE = 0x0; //HIKE, P113 Load registers every ZERO 0 TBCTR=0
EPwm1Regs.CMPCTL.bit.LOADBMODE = 0x0;
EPwm2Regs.CMPCTL.bit.SHDWAMODE = 0x0;
EPwm2Regs.CMPCTL.bit.SHDWBMODE = 0x0; //Active Counter-CompareA(CMPA) Load From Shadow Select Mode 0
EPwm2Regs.CMPCTL.bit.LOADAMODE = 0x0; //HIKE, P113 Load registers every ZERO 0 TBCTR=0
EPwm2Regs.CMPCTL.bit.LOADBMODE = 0x0;
EPwm3Regs.CMPCTL.bit.SHDWAMODE = 0x0;
EPwm3Regs.CMPCTL.bit.SHDWBMODE = 0x0; //Active Counter-CompareA(CMPA) Load From Shadow Select Mode 0
EPwm3Regs.CMPCTL.bit.LOADAMODE = 0x0; //HIKE, P113 Load registers every ZERO 0 TBCTR=0
EPwm3Regs.CMPCTL.bit.LOADBMODE = 0x0;
// Setup compare
EPwm1Regs.CMPA.half.CMPA = 0x0; // 1350 占空比/ 1500
EPwm1Regs.CMPB = 0x0; // 同样
EPwm2Regs.CMPA.half.CMPA = 0x0; // 1350 占空比/ 1500
EPwm2Regs.CMPB = 0x0;
EPwm3Regs.CMPA.half.CMPA = 0x0; // 1350 占空比/ 1500
EPwm3Regs.CMPB = 0x0;
// 死区控制寄存器 DBCTL
// 死区模块输出控制 OUT_MODE,3(11):使能双边延时
EPwm1Regs.DBCTL.bit.OUT_MODE = 3; // DB_FULL_ENABLE;//Dead-bandis fully enabledfor both rising-edge delay onoutput EPWMxA and falling-edge 死去延时对于EPWMxA 上升沿 EPWMxB 下降沿
EPwm2Regs.DBCTL.bit.OUT_MODE = 3; // DB_FULL_ENABLE;//Dead-bandis fully enabledfor both rising-edge delay onoutput EPWMxA and falling-edge 死去延时对于EPWMxA 上升沿 EPWMxB 下降沿
EPwm3Regs.DBCTL.bit.OUT_MODE = 3; // DB_FULL_ENABLE;//Dead-bandis fully enabledfor both rising-edge delay onoutput EPWMxA and falling-edge 死去延时对于EPWMxA 上升沿 EPWMxB 下降沿
// 极性选择控制 POLSEL,0:都不翻转
EPwm1Regs.DBCTL.bit.POLSEL = 0; // Active low (AL)mode.Both EPWMxA and EPWMxB are 不可以反相 inverted
EPwm2Regs.DBCTL.bit.POLSEL = 0; // Active low (AL)mode.Both EPWMxA and EPWMxB are 不可以反相 inverted
EPwm3Regs.DBCTL.bit.POLSEL = 0; // Active low (AL)mode.Both EPWMxA and EPWMxB are 不可以反相 inverted
// 死区模块输入控制 IN_MODE,2(10):ePWMxA 是上升沿延时输入源,ePWMxB 是下降沿输入源
EPwm1Regs.DBCTL.bit.IN_MODE = 2; // EPWMxA In (from the action-qualifier)is the source for both falling-edge and rising-edge delay 输入死去延时信号
EPwm2Regs.DBCTL.bit.IN_MODE = 2; // EPWMxA In (from the action-qualifier)is the source for both falling-edge and rising-edge delay 输入死去延时信号
EPwm3Regs.DBCTL.bit.IN_MODE = 2; // EPWMxA In (from the action-qualifier)is the source for both falling-edge and rising-edge delay 输入死去延时信号
/* 初始化 EPWM1-EPWM3 死区上升沿、下降沿延时寄存器 */ \
/* PWM_Deadband = 2.0*SYSTEM_FREQUENCY */ \
/* 计算边沿延时的计算公式:FED=DBFED*T(TBCLK); RED=DBRED*T(TBCLK) */
EPwm1Regs.DBRED = PWM_Deadband; //EPWM1_MIN_DB //Dead-Band Generator Rising Edge Delay Register
EPwm1Regs.DBFED = PWM_Deadband; //EPWM1_MIN_DB;
EPwm2Regs.DBRED = PWM_Deadband; //EPWM1_MIN_DB //Dead-Band Generator Rising Edge Delay Register
EPwm2Regs.DBFED = PWM_Deadband; //EPWM1_MIN_DB;
EPwm3Regs.DBRED = PWM_Deadband; //EPWM1_MIN_DB //Dead-Band Generator Rising Edge Delay Register
EPwm3Regs.DBFED = PWM_Deadband; //EPWM1_MIN_DB;
// 动作连续软件强制寄存器 AQCSFRC
EPwm1Regs.AQCSFRC.all = 0x00;
EPwm2Regs.AQCSFRC.all = 0x00;
EPwm3Regs.AQCSFRC.all = 0x00;
EDIS; // Disable EALLOW
}
【2】基于 DSP28335 的过功率保护代码:
void HVDMC_Protection(void)
{
EALLOW;
EPwm1Regs.TZSEL.bit.CBC6 = 0x1;
EPwm2Regs.TZSEL.bit.CBC6 = 0x1;
EPwm3Regs.TZSEL.bit.CBC6 = 0x1;
EPwm1Regs.TZSEL.bit.OSHT1 = 1; //enable TZ1 for OSHT
EPwm2Regs.TZSEL.bit.OSHT1 = 1; //enable TZ1 for OSHT
EPwm3Regs.TZSEL.bit.OSHT1 = 1; //enable TZ1 for OSHT
EPwm1Regs.TZCTL.bit.TZA = TZ_FORCE_LO; // EPWMxA will go low
EPwm1Regs.TZCTL.bit.TZB = TZ_FORCE_LO; // EPWMxB will go low
EPwm2Regs.TZCTL.bit.TZA = TZ_FORCE_LO; // EPWMxA will go low
EPwm2Regs.TZCTL.bit.TZB = TZ_FORCE_LO; // EPWMxB will go low
EPwm3Regs.TZCTL.bit.TZA = TZ_FORCE_LO; // EPWMxA will go low
EPwm3Regs.TZCTL.bit.TZB = TZ_FORCE_LO; // EPWMxB will go low
EDIS;
}
【3】基于 DSP28335 的控制电机启停代码:
void STOP_CAR(void) // 上下桥臂全为低,关闭六个管
{
EALLOW;
EPwm1Regs.AQCSFRC.bit.CSFA=1;
EPwm1Regs.AQCSFRC.bit.CSFB=1;
EPwm2Regs.AQCSFRC.bit.CSFA=1;
EPwm2Regs.AQCSFRC.bit.CSFB=1;
EPwm3Regs.AQCSFRC.bit.CSFA=1;
EPwm3Regs.AQCSFRC.bit.CSFB=1;
EDIS;
}
void START_CAR(void) // 上下桥臂 对称互补
{
EALLOW;
EPwm1Regs.AQCSFRC.all = 0x00;
EPwm2Regs.AQCSFRC.all = 0x00;
EPwm3Regs.AQCSFRC.all = 0x00;
EDIS;
}
4.1.3 速度和扭矩波动原因
下图中青色矢量表示转子(永磁体)磁场方向与大小、紫色矢量表示定子磁场方向与大小:

从图中可以看出,六步换相存在两个主要问题:
- 定子的磁动势矢量被固定在了 6 个方向上,在各方向之间的切换是跳变的,无法连续旋转;
- 二者磁场夹度一直在 60° 和 120° 之间波动,这使得我们无法持续获得最大扭矩(磁场夹度为 90° 时,扭矩最大)。
这些便是速度和扭矩波动背后的原因,而磁场定向控制(即后文的 FOC 控制)便可很好地解决这些问题。

4.2 PMSM——磁场定向控制(FOC)
关于 FOC 控制大佬们已写得足够详尽,具体可以阅读:
矢量控制,又称磁场定向控制(FOC,即Field-Oriented Control),其中定子三相电流被变换分解为两个正交分量,其中一个分量定义了电机的磁场,另一个分量定义了转矩。控制系统根据速度控制部分给出的磁通和转矩参考值,计算出相应的电流分量参考值。
首先,让我们对 FOC 的控制过程有个大致了解。

由动图观之,FOC 控制过程可归纳如下:
- 对三相电流采样得 Ia、Ib、Ic(或记作IU、IV、IW);
- 应用 Clarke 变换和 Park 变换将三相电流 Ia、Ib、Ic 转换为 Iq、Id 电流(Ia、Ib、Ic 经Clarke 变换得到 Iα、Iβ,再经 Park 变换得到 Iq、Id);
- 将所得电流 Iq、Id 与期望值 Iq_ref、Id_ref(由上分析知,Id_ref 一般是 0)进行比较计算,得出的误差作为 PI 控制器的输入;
- 由 PI 控制器输出电压 Vq、Vd。此时电压仍为旋转坐标系中的变量,所以在将电压给到电机之前,需要将其转换为三相电压;
- Vq、Vd 经反 Park 变换得到 Vα、Vβ,再经反 Clarke 变换或其他方式合成电压空间矢量,输入 SVPWM 模块进行调制,输出控制三相逆变器的 MOS 管开关的编码值,驱动电机;
- 循环上述过程。
让我们一个一个来。
4.2.1 三相电流采样
由于电机工作的电流一般很大,所以采样电阻的阻值非常小,甚至和导线的电阻接近了,因而实际的采样电路PCB设计的时候还有一些讲究,比如使用开尔文接法(Kelvin Connections)【可参阅知乎博文:开尔文接法在电力电子中的应用有哪些?,而开尔文接法的实际应用案例可参看笔者博客【硬件设计】电流、电压采样电路硬件方案(附实例)中的 1.2.3.3】。根据基尔霍夫电流定律(在任一时刻,流入节点的电流之和等于流出节点的电流之和:Ia + Ib + Ic = 0),我们实际电路设计时可以不使用三个采样器,只需要两个就够了。

4.2.2 ★Clarke、Park 变换
4.2.2.1 id* = 0 控制策略思路
我们知道,当转子和定子磁场夹角重合时,力矩为 0;而当二者夹角逐渐增大到 90° 时,便可获得最大力矩:
下图中紫色矢量为定子磁场矢量,而灰色矢量则指向与转子磁场相同的方向。我们期望紫色矢量领先灰色矢量 90°。假设此时紫色矢量仅领先 45°,而时序图上对应的相位波形亦超前 45°。此时虽有助于产生力矩,但并非是我们想要的最大力矩。

接下来,我们将紫色矢量沿着两个正交轴进行分解(注意这个变换分解,后面要考):沿着灰/蓝色矢量或转子磁场方向的轴称为直轴(d 轴),而与直轴正交的另一轴称为交轴(q 轴)。
【(知识复习)直轴和交轴的位置】
d 轴位于磁极,即永磁体中心线上;q 轴位于两极的中心线上,即相邻两块永磁体的中心线上。

我们由图可知,交轴电流 Iq 有助于产生扭矩,而直轴电流 Id 则不会产生任何扭矩,因此,为了获得最大扭矩,我们可以使用两个 PI 控制器:一个强制使 Id 归零,而另一个使 Iq 最大化。当直轴分量完全减小至零时,定子磁场矢量便于转子磁场矢量正好成 90°:
这便是我们常说的 id* = 0 控制策略,即令直轴参考量 Id_ref = 0。
【电枢反应角度】
id* = 0 控制策略令电枢反应只有交轴电枢反应,没有直轴反应,即电枢合成磁动势
正好落在 q 轴上,电流超前角 γ 等于 0。
4.2.2.2 变换公式
欸等一下,不是三相电吗?怎么直接从紫色矢量正交分解然后完事儿了?
实际上,这个紫色矢量是由三相电合成的一个空间矢量(将会在后文中推导其具体形式):

上图中,红、绿、蓝仨矢量分别代表A 相、B 相和 C 相电压矢量,三者合成的紫色总矢量为一空间矢量(可代表定子磁场和合成电压矢量)。灰色矢量仍表示为转子磁场方向。蓝色和黄色箭头则分别表示直轴和交轴方向。而强制直轴分量为零、同时允许交轴分量增长的操作,在这些矢量上的影响就是这样:

显然,我们根本不想将三个正弦量加入控制,因为要对于非线性的信号进行准确控制就要使用复杂的高阶控制器,这对于建模成本、处理器算力、控制实时性等都是不利的。而 Clarke 变换和 Park 变换会将静止的定子参考坐标转换为旋转参考坐标,使我们不再需要直接控制交流电流,只需直接控制直轴和交轴电流即可。
而这两个变换在动图中所表现的形式正是紫色矢量的正交分解:

以下部分灯哥的解释和推导都非常非常清楚,笔者会将链接附上,并截选大致思路与公式在此。
4.2.2.2.1 Clarke 变换

就像将视在功率分为有功功率和无功功率一样,Clarke 变换可视为将三相电流转换为产生扭矩的电流 Iα 和产生磁通的电流 Iβ。
所谓 Clarke 变换,实际上就是降维解耦的过程,把三相静止坐标系 A-B-C 变换为两相静止坐标系 α-β(公式推导详见灯哥的 3.1 克拉克变换),变换公式如下:
式中,Clarke 变换矩阵为 ,系数 2/3 是通过等幅值约束条件得到的,表明在变换前后电流的幅值不变;而当采用功率不变作为约束条件时,该系数则应为
。
【系数 2/3 怎么来的?】
假设 Ia = -1,根据基尔霍夫电流定律,有 Ia + Ib + Ic = 0,又电路为三相对称绕组,则Ib = Ic = 1/2。将这三个参数带入上式中,得到:
我们可以发现,如果没有 2/3 这一系数,Iα 将是 Ia 的 3/2 倍,而我们希望而二者大小相等,所以乘上 2/3 进行抵消。
通过基尔霍夫电流定律,我们可以消去变量 Ic,从而对 Clarke 变换进行化简如下:

4.2.2.2.2 Park 变换
Clarke 变换减少了一个维度,且变换前后电流的幅值与角频率一致,但是新的变量还是非线性的(正弦),Park 变换的工作便是将它们线性化。这个“从静止参考系移动到旋转参考系”的过程通俗地来说,就是我们现在要从旋转木马旁边的地上,跳到其中一匹木马背上,这样就方便我们锁定和我们一同旋转的其他木马。

其中,Iq-Id 坐标系随转子转动,d 轴在此处设定为指向电机的 N 极,Iq-Id 坐标系因转动而造成的与 Iα-Iβ 坐标系(固定在定子上)的差角 θ,即称为电角度(该值就是编码器测得的转子实时旋转角度)。通过几何推导(不会推可以看3.3 帕克变换和Park变换和反Park变换的公式推导),可得:
式中,,
为电机电角度。相应地,反 Park 变换矩阵即为 Park 变换矩阵求逆所得:
。
以 Vector control for dummies — Switchcraft 中的动图做总结:

基于 DSP28335 的坐标变换代码编写如下:
void CLARKE_Cale(p_CLARKE pV)
{
// 前提为满足基尔霍夫电流定律:ia+ib+ic=0
// Ialpha = ia;
// Ibeta = sqrt(3)/3 * (ia + 2*ib);
pV->Alpha = pV->As;
pV->Beta = _IQmpy((pV->As + _IQmpy2(pV->Bs)), _IQ(0.57735026918963)); // sqrt(3)/3 = 0.577
}
void PARK_Cale(p_PARK pV)
{
// Id = Ialpha * cos(theta) + Ibeta * sin(theta);
// Iq = Ibeta * cos(theta) - Ialpha * sin(theta);
pV->Ds = _IQmpy(pV->Alpha,pV->Cosine) + _IQmpy(pV->Beta,pV->Sine);
pV->Qs = _IQmpy(pV->Beta,pV->Cosine) - _IQmpy(pV->Alpha,pV->Sine);
}
void IPARK_Cale(p_IPARK pV)
{
// Ualpha = Ud * cos(theta) - Uq * sin(theta);
// Ubeta = Ud * sin(theta) + Uq * cos(theta);
pV->Alpha = _IQmpy(pV->Ds, pV->Cosine) - _IQmpy(pV->Qs, pV->Sine);
pV->Beta = _IQmpy(pV->Ds, pV->Sine) + _IQmpy(pV->Qs, pV->Cosine);
}
其中,所用结构体封装如下:
typedef struct { _iq As; // Input: phase-a
_iq Bs; // Input: phase-b
_iq Cs; // Input: phase-c
_iq Alpha; // Output: a-axis
_iq Beta; // Output: b-axis
} CLARKE ,*p_CLARKE ;
#define CLARKE_DEFAULTS {0,0,0,0,0}
typedef struct { _iq Alpha; // Input: a-axis
_iq Beta; // Input: b-axis
_iq Angle; // Input: angle (pu)
_iq Ds; // Output: d-axis
_iq Qs; // Output: q-axis
_iq Sine;
_iq Cosine;
} PARK , *p_PARK ;
#define PARK_DEFAULTS {0,0,0,0,0,0,0}
typedef struct { _iq Alpha; // Output: d-axis
_iq Beta; // Output: q-axis
_iq Angle; // Input: angle (pu)
_iq Ds; // Input: d-axis
_iq Qs; // Input: q-axis
_iq Sine; // Input: Sine
_iq Cosine; // Input: Cosine
} IPARK , *p_IPARK;
#define IPARK_DEFAULTS {0,0,0,0,0,0,0}
4.2.3 ★空间矢量脉宽调制(SVPWM)
前文曾提过,PMSM 的反电动势波形为正弦波,也就是说,当匀速转动它的转子时,用示波器观察它开路的三相定子输出电压,将会看到两两相位差为 120° 的三相正弦电压。那么反过来,我们可以通过在定子上输入三相正弦电压,来驱动转子平稳运行。
可是我们最终的控制对象是 MOS 管的开通和关断,即只有电压最大值和 0 两个状态,那该如何生成连续变化的正弦波呢?在《电力电子技术》中我们学到,可以采用正弦脉宽调制(SPWM,即 Sinusoidal PWM),再通过低通滤波器,便可得到等效的正弦波。
【(知识复习)SPWM】
三相桥式 SPWM 逆变电路的控制是用一条等腰三角波与三条幅值及频率相同但相位各相差 120° 的正弦波进行比较,从而得到三个桥臂的控制信号,即控制信号 uA 与三角波比较得到 T1 的控制信号;uB 与 uC 同三角波比较得到 T3 和 T5 的通断控制信号。
但是,在 FOC 控制中我们一般采用的是空间矢量脉宽调制(SVPWM,即 Space Vector Pulse Width Modulation)而不是 SPWM。知乎天斟在《电机控制——聊聊SPWM和SVPWM》中一针见血地指出:“SVPWM 与 SPWM 的思路相反,一开始就从产生旋转矢量的这一结果入手。由于 SPWM 最终的目的就是为了产生一个空间上的旋转磁动势,那么 SVPWM 就不拘泥于单独产生三相电压,而是通过设置开关管的通断,直接在电机中形成一个旋转的空间矢量,从而产生一个旋转的磁动势。”
4.2.3.1 空间矢量
问题来了,这个由三相正弦电压合成的空间矢量具体是什么样子呢?
- 【时间上】设三相正弦电压瞬时值为:
- 【空间上】三相绕组在空间互差120°,其物理位置可用复数表示:
- a 相轴指向 0°:单位向量
;
- b 相轴相对 a 相逆时针转 120°:
;
- c 相轴相对 a 相顺时针转 120°:
。
- a 相轴指向 0°:单位向量

定义初始合成矢量为
将三相电压代入可得
把 cos(θ∓2π3) 展开
把它们代回去并分别按实、虚部相加
-
实部:
-
虚部:
于是有
该初始空间矢量(下方动图中黑色的矢量)幅值不变,为相电压幅值 Um 的 1.5 倍,旋转角速度为 ω(= 2πf), 旋转方向由三相电压的相序决定。
由于定子电压空间矢量方向和其所产生的磁场矢量方向一致(可通过前文所给的通电螺线管辅助理解。特别注意,这里指的电压和磁场并非时间相量,而是空间矢量,注意区分!),如果三个相绕组结构一样,那么产生的合成磁场的大小也是每个线圈产生磁场幅值的 1.5 倍。因为转子永磁体会努力旋转到内部磁力线和外部磁场方向一致,所以这个矢量其实就可以表征我们希望转子旋转到的方向,也即所需要生成的磁场方向。

但是为了归一化使矢量幅值等于相电压幅值 Um,从而简化控制系统设计,空间电压矢量一般会人为地乘上一个 2/3 系数,即让
故有
【等价于 Clarke 变换的复数形式】
直接将上式用欧拉公式
展开,你将会发现:
这里的系数 2/3 和前面等幅值形式的 2/3 作用一致,都是用于归一化。
综上,我们可以得到:

所以,我们怎么才能单单用 PWM 来重现这个空间电压矢量 V 呢?
4.2.3.2 基本原理
该部分将大量引用稚晖君的文字,以及西南交通大学宋文胜老师课件《第六章 脉宽调制技术》。
推荐阅读:知乎玻璃伞 彻底吃透SVPWM如此简单
相比 PWM 和 SPWM,基于 SVPWM 的逆变器将逆变器在降低电压谐波和损耗方面将逆变器的控制性能提升到一个新的高度,且易于数字化实现,适合 DSP 等高性能处理器进行数字控制。该部分将详细讲解 SVPWM 的基本原理。
首先,让我们从三相桥式逆变器入手,以其中一种电路通断状态为例(此时即为矢量 V1(100)状态):

此时电源及电机绕组等效电路如下图,其中 Udc 为直流电源电压:
因此状态 V1(100)时电机中三个相电压(相电压是每相相对于电机中间连接点的电压)可以表示为:UaN = 2/3Udc,UbN = UcN = -1/3Udc(其实就是一个分压电路)。

电路有 2^3 = 8 个开关状态,对应着 8 种工作模式。8 种工作模式又分别对应 8 种矢量,其中包括 6 个非零矢量:V1(100),V2(110),V3(010),V4(011),V5(001),V6(101)和 2 个零矢量:V0(000),V7(111)。(这里非零矢量的编号顺序看似很奇怪,其实是按下面图中矢量逆时针排布的顺序。另一种常见的则是用 SaSbSc 对应的二进制进行编号。)

仍以上文提到的空间矢量 V1(编号 100,UaN = 2/3Udc,UbN = UcN = -1/3Udc)为例,如下图进行矢量合成:

由图可知,合成矢量大小应为 Udc,方向水平指向右侧。和前文一致地,此处同样人为地乘上 2/3 系数,则矢量 V1 大小被修改为 2/3Udc。故 8 种开关状态对应的空间矢量如下图所示,空间电压矢量 Vk 定义如下:

上面的第二个公式就有很强的既视感了,和我们在 4.2.1 引言中提到的空间电压矢量公式形式完全一致,不过只是将三个正弦电压替换成了开关函数、前面的相电压幅值被替换为了直流电源电压。矢量V1(100)的编号代入公式可得 ,和前述一致。
将 6 个有效矢量和 2 个零矢量画出。相邻的有效矢量在空间上相差 π/3,这六个矢量形成一个正六边形。这六个有效矢量将复平面分成六个区域,分别记为 I,II,III,IV,V,VI。

可以注意到,两个零矢量(V0(000),V7(111))其实和原点重合了,因为这两个状态下电机中产生力矩的磁场为 0。
那么这里问题就来了:由这 6 个空间电压矢量只能产生 6 个方向的力矩,我们怎么产生任意方向的力矩呢?答案就是:使用这 6 个空间电压矢量作为基向量来合成任意矢量。在每一个扇区,选择相邻两个电压矢量以及零矢量,按照伏秒平衡原则来合成每个扇区内的任意电压矢量:
离散化后等效为下式:
式中,各变量含义如下:
- Vref:我们期望得到的电压矢量;
- T:一个 PWM 周期;
- Ux、Uy、Tx、Ty:看完后面所举的例子就懂了;
- U0*:指的是两个零矢量,可以是 V0 也可以是 V7,零矢量的选择比较灵活,主要考虑通过合理选择使得开关状态变化尽可能少,以降低开关损耗,并让空间电压矢量的切换更平顺。
所以上面公式的含义就是:我们可以周期性地在不同空间电压矢量之间切换,只要合理地配置不同基向量在一个周期中的占空比,就可以合成出等效的任意空间电压矢量了。
假设现在需要产生电压矢量 Vref,其位置位于扇区 I 内, 介于 V1 和 V2 之间。设 Va 和 Vb 分别是 V1 和 V2 上的矢量,二者合成得到 Vref。

在一个周期 Tc 内,由伏秒平衡可得(把前面的式子左侧的 T 除到右边去了):
由正弦定理(各边和它所对角的正弦值的比相等) ,我们可以得到:
又由 |V1| = |V2| = 2/3Udc,所以可以计算得到 T1 和 T2(具体推导见下):
其中,m 为 SVPWM 的调制系数(即调制比,也称调制度):
显然在电流环控制过程中 m 设置得越大,代表了期望力矩越大(正比)。
【具体推导步骤(以求解 T1 为例)】
代入已知量:
化简:
在一个开关周期 Tc 内,设 T0 和 T7 分别是零矢量 V0 和 V7 的作用时间,其表达式如下:
另外,如果我们将PWM波形设定为中央对齐模式对称配置零矢量,则此时
现在一个周期内所有状态的持续时间我们都得到了,还差一个顺序,也就是各个状态切换的顺序。问题来了,反正是做积分,重要的是持续时间而不是顺序,难道不是任意顺序都可以嘛?是的,理论上任何切换顺序都是可行的,但是实际中我们需要考虑更多限制,比如因为 MOS 管存在开关损耗,所以我们希望能尽量减少 MOS 管的开关次数。另外,当 PWM 输出波形是对称的时(即采用七段式 SVPWM 调制法(V0 → V1 → V2→ V7 → V2 → V1 → V0 )),谐波主要集中在开关频率和两倍开关频率的附近,这种模式下谐波幅值是三种排列模式中最小的。结合以上因素考虑,我们就可以设计出下面的切换顺序(一个开关周期内,共有 6 次开关切换):

至此,SVPWM 的工作完成了,我们得到了每一时刻所需要的空间电压矢量以及它们持续的时间,在处理器中赋值给对应通道的捕获比较寄存器产生相应的三个 PWM 波形,控制 MOS 管的开关,进而产生我们期望的电压电流及力矩。
用 Space Vector PWM Intro — Switchcraft 中的动图做总结(动图中的空间矢量按二进制编号):

void SVPWM_Cale(p_SVPWM pV)
{
// Vref1 = Ubeta;
// Vref2 = (sqrt(3) * Ualpha - Ubeta) / 2;
// Vref3 = (-sqrt(3) * Ualpha - Ubeta) / 2;
pV->tmp1 = pV->Ubeta;
pV->tmp2 = - _IQdiv2(pV->Ubeta) + _IQmpy(_IQ(0.866), pV->Ualpha); // 0.866 = sqrt(3) / 2
pV->tmp3 = - _IQdiv2(pV->Ubeta) - _IQmpy(_IQ(0.866), pV->Ualpha);
if(pV->tmp1 > _IQ(0.0))
pV->tmpNA = 1;
else
pV->tmpNA = 0;
if(pV->tmp2 > _IQ(0.0))
pV->tmpNB = 1;
else
pV->tmpNB = 0;
if(pV->tmp3 > _IQ(0.0))
pV->tmpNC = 1;
else
pV->tmpNC = 0;
pV->tmpN = pV->tmpNA + 2*pV->tmpNB + 4*pV->tmpNC;
switch(pV->tmpN)
{
case 3:
pV->VecSector = 1;
break;
case 1:
pV->VecSector = 2;
break;
case 5:
pV->VecSector = 3;
break;
case 4:
pV->VecSector = 4;
break;
case 6:
pV->VecSector = 5;
break;
case 2:
pV->VecSector = 6;
break;
}
pV->tmpA = _IQmpy(_IQ(1.73205081*0.001/12.5/24), pV->Ubeta);
pV->tmpB = _IQmpy(_IQ(1.73205081*0.001/12.5/24), ( _IQmpy( _IQ(0.86602540), pV->Ualpha) + _IQdiv2(pV->Ubeta) ) );
pV->tmpC = _IQmpy(_IQ(1.73205081*0.001/12.5/24), (-_IQmpy( _IQ(0.86602540), pV->Ualpha) + _IQdiv2(pV->Ubeta) ) );
switch(pV->VecSector)
{
case 1:
{
pV->T1 = -pV->tmpC;
pV->T2 = pV->tmpA;
}
break;
case 2:
{
pV->T1 = pV->tmpC;
pV->T2 = pV->tmpB;
}
case 3:
{
pV->T1 = pV->tmpA;
pV->T2 = -pV->tmpB;
}
break;
case 4:
{
pV->T1 = -pV->tmpA;
pV->T2 = pV->tmpC;
}
break;
case 5:
{
pV->T1 = -pV->tmpB;
pV->T2 = -pV->tmpC;
}
break;
case 6:
{
pV->T1 = pV->tmpB;
pV->T2 = -pV->tmpA;
}
break;
}
// 过调制处理
if(pV->T1 + pV->T2 > _IQ(0.001/12.5))
{
pV->T1 = _IQdiv(_IQmpy(_IQ(0.001/12.5), pV->T1), (pV->T1 + pV->T2));
pV->T2 = _IQdiv(_IQmpy(_IQ(0.001/12.5), pV->T2), (pV->T1 + pV->T2));
}
else
{
pV->T1 = pV->T1;
pV->T2 = pV->T2;
}
// 扇区内合成矢量切换点时间计算
// 此处为7段式,两个零矢量000 111 111插在中间,000均分插在两端
pV->ta = _IQdiv4((_IQ(0.001/12.5) - (pV->T1 + pV->T2)));
pV->tb = pV->ta + _IQdiv2(pV->T1);
pV->tc = pV->tb + _IQdiv2(pV->T2);
// 输出调制信号
switch(pV->VecSector)
{
case 1:
{
pV->Tcm1 = pV->ta;
pV->Tcm2 = pV->tb;
pV->Tcm3 = pV->tc;
}
break;
case 2:
{
pV->Tcm1 = pV->tb;
pV->Tcm2 = pV->ta;
pV->Tcm3 = pV->tc;
}
break;
case 3:
{
pV->Tcm1 = pV->tc;
pV->Tcm2 = pV->ta;
pV->Tcm3 = pV->tb;
}
break;
case 4:
{
pV->Tcm1 = pV->tc;
pV->Tcm2 = pV->tb;
pV->Tcm3 = pV->ta;
}
break;
case 5:
{
pV->Tcm1 = pV->tb;
pV->Tcm2 = pV->tc;
pV->Tcm3 = pV->ta;
}
break;
case 6:
{
pV->Tcm1 = pV->ta;
pV->Tcm2 = pV->tc;
pV->Tcm3 = pV->tb;
}
break;
}
// 调制信号处理,生成输入到MCU中的调制信号
pV->Tcm1 = _IQmpy(6000, _IQdiv(pV->Tcm1, _IQ(0.001/12.5/2)) );
pV->Tcm2 = _IQmpy(6000, _IQdiv(pV->Tcm2, _IQ(0.001/12.5/2)) );
pV->Tcm3 = _IQmpy(6000, _IQdiv(pV->Tcm3, _IQ(0.001/12.5/2)) );
}
另附:
【1】ePWM初始化:
#define ISR_FREQUENCY 12.5
#define SYSTEM_FREQUENCY 150
float32 T = 0.001/ISR_FREQUENCY;
// T为采样周期(s),其中开关频率ISR_FREQUENCY数值为12.5(kHz),故转换为s作为单位时需要*1/1000。
// 开关频率ISR_FREQUENCY此处设为12.5(12.5kHZ),则采样周期T为0.00008s即0.08ms(80us)。
// 在电机控制中,采样频率一般与开关频率相同。
void EPWM3_int(void)
{
// 150MHz,即1s之中计数150M次,则一个采样周期内计数(150M*T)次
// 注意!赋予寄存器的为计数值!
// 因为在向上下模式计数时,Tpwm = 2*TBPRD*T(TBCLK),所以TBPRD(即PeriodMax)为一个采样周期计数值的1/2,即(150M*T)/2次
PWM_PeriodMax = SYSTEM_FREQUENCY*1000000*T/2; // 6000
PWM_HalfPerMax = PWM_PeriodMax/2; // HalfPerMax 为 TBPRD/2
PWM_Deadband = 2.0*SYSTEM_FREQUENCY;
EALLOW;
/* SYNCOSEL:同步信号输出选择。00:同步输出信号与该模块的同步输入信号ePWMxSYNCI相同 */
EPwm1Regs.TBCTL.bit.SYNCOSEL = 0;
EPwm2Regs.TBCTL.bit.SYNCOSEL = 0;
EPwm3Regs.TBCTL.bit.SYNCOSEL = 0;
/* PHSEN:计数寄存器装载相位寄存器使能位。1:当同步信号到来的时候,计数寄存器装载相位寄存器的值 */
EPwm1Regs.TBCTL.bit.PHSEN = 1;
EPwm2Regs.TBCTL.bit.PHSEN = 1;
EPwm3Regs.TBCTL.bit.PHSEN = 1;
/* 初始化 EPWM1-EPWM3 时基周期寄存器 */
/* PeriodMax = SYSTEM_FREQUENCY*1000000*T/2,即全周期计数值的1/2 */
EPwm1Regs.TBPRD = PWM_PeriodMax; // 6000
EPwm2Regs.TBPRD = PWM_PeriodMax;
EPwm3Regs.TBPRD = PWM_PeriodMax;
/* 初始化 EPWM1-EPWM3 时基相位寄存器 */
EPwm1Regs.TBPHS.half.TBPHS = 0;
EPwm2Regs.TBPHS.half.TBPHS = 0;
EPwm3Regs.TBPHS.half.TBPHS = 0;
/* 初始化 EPWM1-EPWM3 时基控制寄存器 */
// 0xA00A:10 1 000 000 0 00 1 0 10
// 仿真模式位FREE,SOFT - 10:当仿真事件到来时时基计数器自由运行;
// 相位方向位PHSDIR - 1:当时基计数器配置为向上-下模式时,这个位才起作用。这个位置1,则当同步信号到来时计数器装载相位寄存器的值后向下计数。
// 时基时钟分频位CLKDIV - 000:/1。
// 高速时基时钟分频位HSPCLKDIV - 000:/1。这两位决定了时基时钟分频值TBCLK = SYSCLKOUT / (HSPCLKDIV × CLKDIV) = SYSCLKOUT。
// 软件强制同步脉冲SWFSYNC - 0:写0没有效果
// 同步信号输出选择SYNCOSEL - 00:选择ePWMxSYNCO信号输出源为ePWMxSYNCI。
// 周期寄存器装载影子寄存器选择PRDLD - 1:禁止使用影子寄存器。
// 计数寄存器装载相位寄存器使能位PHSEN - 0:禁止装载。
// 计数模式CTRMODE - 10:向上-下计数。一般情况下,计数模式只设置一次,如果需要改变模式,那么将会在下一个TBCLK的边沿生效
EPwm1Regs.TBCTL.all = 0xA00A;
EPwm2Regs.TBCTL.all = 0xA00A;
EPwm3Regs.TBCTL.all = 0xA00A;
/* 初始化 EPWM1-EPWM3 计数比较控制寄存器 */
EPwm1Regs.CMPCTL.all = 0;
EPwm2Regs.CMPCTL.all = 0;
EPwm3Regs.CMPCTL.all = 0;
/* 初始化 EPWM1-EPWM3 动作控制寄存器 A 寄存器 */
// 00 00 01 10 00 00
// CBD + CBU - 0000:当向上和下计数时,时基计数器的值与CMPB寄存器的值相等时,不动作。
// CAD - 01:当向下计数时,时基计数器的值与CMPA寄存器的值相等时,清零:使ePWMxA输出低
// CAU - 10:当向上计数时,时基计数器的值与CMPA寄存器的值相等时,置位:使ePWMxA输出高 该设置下,比较值越小,占空比越大,因为:比较值越小,则越先开始作用这个矢量,自然其占空比就越大
// PRD - 00:当时基计数器的值与周期寄存器的值相等时不动作
// ZRO - 00:当时基计数器的值等于0时不动作。
EPwm1Regs.AQCTLA.all = 0x0060;
EPwm2Regs.AQCTLA.all = 0x0060;
EPwm3Regs.AQCTLA.all = 0x0060;
/* 初始化 EPWM1-EPWM3 的死区控制寄存器 */
// 死区模块输入控制IN_MODE - 00:ePWMxA是双边沿延时输人源
// 极性选择控制POSEL - 10:ePWMxA不翻转,ePWMxB翻转
// 死区模块输出控制OUT_MODE - 11:使能双边沿延时
EPwm1Regs.DBCTL.all = 0x000B;
EPwm2Regs.DBCTL.all = 0x000B;
EPwm3Regs.DBCTL.all = 0x000B;
/* 初始化 EPWM1-EPWM3 死区上升沿、下降沿延时寄存器 */
/* v.Deadband = 1.5*SYSTEM_FREQUENCY */
/* 计算边沿延时的计算公式:FED=DBFED*T(TBCLK); RED=DBRED*T(TBCLK) */
EPwm1Regs.DBFED = PWM_Deadband;
EPwm1Regs.DBRED = PWM_Deadband;
EPwm2Regs.DBFED = PWM_Deadband;
EPwm2Regs.DBRED = PWM_Deadband;
EPwm3Regs.DBFED = PWM_Deadband;
EPwm3Regs.DBRED = PWM_Deadband;
EPwm1Regs.PCCTL.all = 0;
EPwm2Regs.PCCTL.all = 0;
EPwm3Regs.PCCTL.all = 0;
EPwm1Regs.TZSEL.all = 0;
EPwm2Regs.TZSEL.all = 0;
EPwm3Regs.TZSEL.all = 0;
EDIS; /* Disable EALLOW*/
}
【2】PWM输出:
注意!!!一定要结合电机实际绕组顺序对各高桥臂开关管进行 PWM 输出选择!如下图中,逆时针来看电机绕组顺序为 A、C、B,则输出时将 SVPWM 的 tcm2 和 tcm3 对调如下方代码所示。

void Svpwm_Outpwm(void)
{
EPwm1Regs.CMPA.half.CMPA = Svpwmdq.Tcm1;
EPwm2Regs.CMPA.half.CMPA = Svpwmdq.Tcm3;
EPwm3Regs.CMPA.half.CMPA = Svpwmdq.Tcm2;
}
4.2.4 ★比例积分(PI)双环控制
4.2.4.1 比例积分微分(PID)控制原理
推荐阅读:CSDN博主skythinker616的PID超详细教程——PID原理+串级PID+C代码+在线仿真调参-CSDN博客
先让我们来浅浅复习一下 PID 控制相关的知识。以下文段部分截取自夏长亮老师的《无刷直流电机控制系统》。

标准 PID 控制器的基本原理是根据设定值与实际值之间的偏差 e(t),按比例-积分-微分的线性组合关系构成控制量 x(t) ,利用控制量 x(t) 再对控制对象进行控制。连续控制系统 PID 控制规律形式为
式中, 为比例常数;
为积分时间常数;
为微分时间常数。
实际控制系统中,PID 控制器不一定都包含比例、积分和微分 3 个环节,可以是比例、比例积分和比例微分等多种组合形式,无刷直流电机控制系统中最常见的是比例积分(PI)形式,因为微分环节虽然能有效地减小超调和缩小最大动态偏差,但同时易使系统受到高频干扰影响。
【为何微分环节易使系统受到高频干扰影响?】
- 从时域理解
微分环节就是对输入信号做导数:
如果输入信号里包含高频成分(比如传感器噪声),那么微分会让这些高频分量被放大。
- 从频域理解
在频域里,微分相当于一个 高通滤波器:
其中
,所以增益 =
,故有:高频分量(ω 大) → 增益很大 → 被大幅放大;低频分量(ω 小) → 增益有限 → 放大不明显。
所以,引入微分环节就意味着系统里本来有的高频噪声(采样电流、位置传感器、开关噪声)会被其大大放大,而且放大后的噪声将直接进入控制器输出,表现为电流抖动、转矩脉动、机械振动。
【那只用 P 环进行电机控制可以吗?】
仅有 P 环是不够的,因为单纯的比例运算会导致在同等输出力矩下,大负载时达到稳定速度会变慢(载大负载时惯性大),而小负载时稳定速度会变快。换言之,仅有 P 环无法使得电机根据负载自适应调整力矩输出。而 PI 控制器中的 I 环就为我们解决了这个问题。
I 环实际上就是由一个系数 Ki 和一个对误差在时间上进行不断积分的积分项组成的。也就是说,当这个误差如果存在的时间越长,这个积分值就会越来越大,直到变为 0 为止。最后,这个积分值会乘上系数 Ki,进行一个 Ki 的比例缩放后叠加在电机力矩上。
总而言之,当有了 I 环后,一切就不同了:当这个误差很久都没有被 P 环调节过来时,I 环的积分就会不断的积分这个误差,使得电机的输出力越来越大,最终让电机实现更快速的纠偏。
现代电机控制系统为了提高系统的可靠性,一般使用数字式 PID 控制器。因此,连续型 PID 控制算法不能直接使用,需要对上式进行离散化处理,得到离散 PID 控制律的差分方程 (也称位置式 PID 控制算法)
式中, 为积分系数;
为微分系数;T 为采样周期;e(k)、e(k-1) 为第 k、k-1 采样时刻输入的偏差值。
有一个网站可以通过自己设置模拟飞行器参数,来直观理解PID:Webpack App (rossning92.github.io),可以玩一玩感受一下。
4.2.4.2 FOC 中的双环控制

在 4.2.2.1 分析中我们得出的结论:“交轴电流 Iq 有助于产生扭矩,而直轴电流 Id 则不会产生任何扭矩”。因此,为了获得最大扭矩,我们可以使用两个 PI 控制器:一个使 Id 归零,而另一个使 Iq 最大化。由于我们采用 id* = 0 策略,故 Id 的参考量已知为 0,那么 Iq 的参考量如何得到呢?因为 Iq 控制转矩电流,直接对应电机的输出转矩,而速度又和扭矩正相关,所以我们就可以把速度误差转化为 Iq 的电流参考,再交给电流环去调节。
综上,我们需要 2 个电流环(内环):一个控制 d 轴电流(Id),一个控制 q 轴电流(Iq);1 个转速环(外环):将速度 PI 的输出作为转矩电流 Iq 参考值。
-
内层 电流环 → 响应最快,抑制电流波动,保证转矩控制精度。
-
外层 速度环 → 调整转速误差,给电流环提供参考值。

4.2.4.2.1 电流环控制
此处为何只用到了比例积分(PI)控制而没有引入微分(D)呢?除了前文所说的微分环节易使系统受到高频干扰外,稚晖君在文章中说:“如果推导一下电压和电流的传递函数会发现这其实就是一个一阶惯性环节(而且实际上我们可以通过零极点对消来简化掉 PI 参数,只需要控制一个参数即电流带宽即可)”。
【(知识复习)一阶惯性环节】
一阶惯性环节数学形式是
K:系统的稳态增益;
T:时间常数(决定响应快慢)。
为何此处传递函数为一阶惯性环节呢? PMSM 在 d-q 坐标系下的数学模型为(推导过程可查阅资料,先记结论)
d 轴的电压方程中,含有 q 轴的量,而 q 轴的电压方程中含有 d 轴的量,这种现象被称为交叉耦合。把 d 轴等式右边与 id 无关的量、 q 轴等式右边与 iq 无关的量都移到等式左边,然后各用一个新的变量代替等式左边的量,我们就可以得到:
则 d 轴电流的响应只与 ucd 有关、q 轴电流的响应只与 ucq 有关,d 轴和 q 轴相互独立,可以单独设计控制器分别控制 d 轴和 q 轴的电流。
根据上式,电机 d 轴电流响应的传递函数可写为
电机 q 轴电流响应的传递函数可写为
显然,二者均为一阶惯性环节,且其惯性环节的时间常数分别为 和
。

常用的 PI 控制器传递函数为
以 d 轴为例,使用 PI 控制器来控制电流,那么系统的 d 轴传递函数为
【零极点对消】
上式展开是一个二阶系统,而二阶系统调起来比一阶复杂。如果我们令控制器的零点等于被控对象某个极点,就可以把系统阶数降低,让控制器调参变为只需调一个参数来决定闭环极点,从而更容易按带宽直接设定增益。
- PI 控制器 Gcd(s)
其分子多项式
给出控制器的零点
。
- 被控对象(电机 d 轴电流回路)Gpd(s)
它的极点在
。
接下来,我们让控制器的零点位置和被控对象的极点位置重合,即令
,代入公式有
移项可得
故我们可以设 ,系统分子里出现的
就能与分母里的
抵消,闭环传递函数便从二阶系统化简为了一阶系统
其时间常数为 。
那么, 和
应取多大合适呢?我们可以从时间常数 T 的选取上入手,而时间常数 T 又与带宽频率息息相关。
【(知识复习)带宽频率(截止频率)
】
将频率特性的幅值下降到零频率幅值的 0.707 处的频率
,称为系统的带宽频率。
此处稍微推导一下为何
:
代入公式
两边平方
便易得上方等式。
当
时,
,输出 ≈ 输入,几乎无衰减 → 系统能跟踪。
当
时,幅值掉到
(约 0.707,-3 dB),再高就明显衰减。
总结:在频率低于
的输入下,系统几乎能“跟得上”;而超过
的输入,输出幅值明显衰减,响应变钝。而我们电流环的任务就是快速、准确地让实际电流 I 跟踪 Iref,如果电流环带宽太小,它跟踪不动,实际电流反应慢,就会导致转矩响应慢,让电机拖沓,甚至不稳定。
根据 和
,闭环系统的响应带宽与比例增益的关系为
又 ,故我们就可以通过对电流带宽的选定来确定 PI 控制器的两个关键参数
和
。
由于我们要输出的电压其实是 ud 和 uq,而不是 ucd 和 ucq,故我们可以将前式
移项可得
对应控制框图如下


4.2.4.2.2 转速环控制
与转速控制器相比,电流控制器一般响应比较快,因此在设计转速控制器时,可以把电流内环简化为一个单位环节,即认为给定的参考电流约等于实际电流,这样不需要再带着电流环的复杂传递函数去推导。就相当于,司机看仪表盘控制速度(转速环),只管给出对应的油门,而不用管汽车发动机油门执行器(电流环)是不是会有延迟。
对于转动系统,有:
其中,:作用在转子上的合转矩;J:转动惯量;
:机械角速度。
电机的合转矩来源于电磁转矩 (由电流产生)和负载转矩
(外部阻力/负载带来的)力学平衡方程写作:
在拉普拉斯变换下:
于是动力学方程在 s 域里变成:
对应控制框图如下

在自动控制的学习中,我们知道,当求输入 作用下的闭环传递函数
时,需将干扰项(这里为负载转矩
)置零。此时被控对象为一典型的积分环节
。
又 PI 控制器传递函数为:
故此处闭环传递函数为:
- 零点:
- 极点:
令
根据求根公式可得:
面对这一传递函数,如果我们试图像处理电流环那样降阶,我们会发现零极点始终无法对消。
【为何无法对消?】
要相消必须满足某个极点等于零点,即把
代入分母应得到 0:
将
代入分母:
只有在
时为零。因为通常 J > 0(非零惯量),且若我们希望保持积分作用则
,所以右边一般不为零。因此零点和极点不会相等,不能相消。
此处闭环传函的分母是二阶多项式,可以和标准二阶形式对比:
比较两边可得:
于是:
【(知识复习)标准二阶形式】
标准的二阶系统传递函数一般写成:
参数如下:
自然频率
(rad/s)
表征系统的“固有振动频率”,即系统没有阻尼时的振荡频率。
决定了系统的响应速度,
越大,系统响应越快。
阻尼比 ζ(无量纲)
表征阻尼大小与临界阻尼的比值。
不同阻尼比对应不同动态特性:
ζ > 1:过阻尼,收敛但速度慢
ζ = 1:临界阻尼,最快无振荡收敛
0 < ζ < 1:欠阻尼,存在振荡(常见)
ζ = 0:无阻尼,系统无限震荡
阻尼频率
欠阻尼时的实际振荡频率。
极点位置
系统极点为:
- 实部
→ 决定衰减速度
虚部
→ 决定振荡频率
让我们重新回到之前的机械平衡方程
实际上该方程忽略了摩擦阻尼,只考虑转动惯量和负载转矩,而更一般的转矩平衡方程其实是:
其中,B 为阻尼系数, 即为阻尼转矩(与速度成正比的摩擦力矩)。由于电机本身的摩擦非常小,B 几乎等于 0,所以在“惯量 + PI 控制”构成的闭环里,缺少一个自然的阻尼项,系统就容易表现为“欠阻尼”,也就是快速上升但会冲过头。
于是我们想到,通过控制器引入一个虚拟阻尼 ,这就等价于系统多了一个“阻尼器”用来调节阻尼比。

接下来,让我们来推导一下新的闭环传递函数。
仍然将负载转矩项 置零,则机械运动方程为:
误差信号 ,则控制器输出(即电磁转矩命令)为:
把 代入机械方程可得:
把所有含 的项移到左边:
因此闭环传递函数为:
把 代入,分子与分母同时乘以 s(去掉分式),则
分子变为:
分母变为:
得到最终多项式:
把分母写成标准二阶形式
,可得
所以虚拟阻尼
提高阻尼比 ζ:
因此增加
可以减小超调、提高阻尼,而不必单纯增大
(这样也会影响其它性能)。
比较总结如下:
参数 无 有 影响 自然频率 不变 无影响 阻尼比 ζ 增加阻尼 动态响应 可能振荡或超调大 响应更平稳、超调小 改善系统稳定性 控制器设计需求 全靠 提供阻尼
虚拟阻尼帮忙减轻控制压力 更易调参
- 零点
分子为一次多项式,所以只有一个零点:
- 极点
极点是分母的根,满足
用二次方程解得两个极点
判别式
决定极点是实的还是共轭复数(欠阻尼、临界或过阻尼)。
要发生零点极点相消,必须有某个极点等于零点,即 。
把零点 代入分母,要求分母在该 s 处为零:
得到:
也就是说:只有当控制器参数 和
、机械参数 J 和阻尼
满足上述精确关系时,分子那一零点才能与某个分母极点重合并相消。
-
相消掉的零点:
该点与分母的一个极点 重合并被消去,其在代数上仍存在但在闭环传函中不显现。
-
剩下的实际闭环极点:
我们可以发现,当 可以调节时,分母的一次项系数
能移动极点的位置,可以把某极点移动到零点的位置,从而实现相消。若没有
,分母一次项只能是
,移动极点的自由度少一维。
相消之后,闭环传递函数化为一阶,代入并化简可得:
这是一个一阶惯性环节,时间常数 。根据前文时间常数与带宽频率的关系,我们可以得到:
故我们可得:
式中, 为转速环的控制带宽。由于转速控制器的设计中,假设电流内环响应速度较快,把电流内环看作单位环节,因此
应该远小于电流环的带宽
,故一般地,
(把转速环带宽选得比电流环小一个数量级,是为了让内环在外环工作频率范围内可以近似看作“静态增益 ≈ 1、相位 ≈ 0”,得到更简单、稳定且鲁棒的级联控制结构)。
理论上可以根据等式相消降阶,但严格相消对模型精度要求极高,若真实 J 或 与设想存在估计误差,或
和
有偏差,所谓“相消”就会不完全,反而可能留下低频残留或引入不稳定。对此,在实际情况中,我们可以把对
、
设计到远离虚轴的地方,这样即使它们没有准确对消,产生的影响也比较小。
基于 DSP28335 的 FOC 算法(本节参数整定过程尚未涉及)代码编写如下:
// 编码器角度计算
QEPEncoder_Cale((p_EQEP) &EQEPPare);
// 输出电角度 θe
Speed_QEPPare.ElecTheta = EQEPPare.ElecTheta;
Speed_QEPPare.DirectionQep = (int32)(EQEPPare.DirectionQep);
// 速度计算
Speed_QEP_Cale((p_Speed_QEP) &Speed_QEPPare);
// 输出机械角速度 ωm
// ADC 采样
ADC_Sample();
TaskTimePare.pwmisr_conut++;
// 一个采样周期结束中断一次。T = 0.08ms; 25*T = 2ms
if(TaskTimePare.pwmisr_conut == 25)
{
TaskTimePare.pwmisr_conut = 0;
// 速度环 2ms刷新一次 =========================================================
// ωm*
knob_control(); // 通过调节电位器旋钮输入速度目标值
pi_spd.Ref = pi_spd.Ref*20.0; // 该系数可自己调节一下
// ωm
//pi_spd.Fbk = Speed_QEPPare.Speed;//Q24
pi_spd.Fbk = _IQ(Speed_QEPPare.SpeedRpm/60)-_IQ(2.5);
PI_Controller((p_PI_Control) &pi_spd);
pi_spd.OutF = _IQmpy(FilK1,pi_spd.OutF)+_IQmpy(FilK2,pi_spd.Out);//Q24
// 输出 iq*
// =======================================================================
}
// 【FOC步骤1】进行Clarke变换==============================================
// 对 PMSM 进行电流采样
ClarkeI.As = ADCSampPare.PhaseA_Curr;
ClarkeI.Bs = ADCSampPare.PhaseB_Curr;
// Clarke 变换
CLARKE_Cale((p_CLARKE) &ClarkeI);
// ====================================================================
// 【FOC步骤2】进行Park变换================================================
// 输入 Clarke 变换所获得的 Iα 与 Iβ
ParkI.Alpha = ClarkeI.Alpha;
ParkI.Beta = ClarkeI.Beta;
// 通过编码器读取电角度信息
ParkI.Angle = EQEPPare.ElecTheta;
ParkI.Sine = _IQsinPU(ParkI.Angle);//Q24
ParkI.Cosine = _IQcosPU(ParkI.Angle);//Q24
// Park 变换
PARK_Cale((p_PARK) &ParkI);
// ====================================================================
// 【FOC步骤3】进行D、Q轴的 PI 闭环运算=======================================
// D轴电流环
// id* = 0
pi_id.Ref = _IQ(0.0); // 强制直轴分量为零
// D轴 PI 控制
pi_id.Fbk = ParkI.Ds;
PI_Controller((p_PI_Control) &pi_id);
pi_id.OutF = _IQmpy(FilK1, pi_id.OutF) + _IQmpy(FilK2, pi_id.Out);
// 输出 ud
// Q轴电流环
// iq*
pi_iq.Ref = pi_spd.Out; // 允许交轴分量增长
// Q轴 PI 控制
pi_iq.Fbk = ParkI.Qs;
PI_Controller((p_PI_Control) &pi_iq);
pi_iq.OutF = _IQmpy(FilK1, pi_iq.OutF) + _IQmpy(FilK2, pi_iq.Out);
// 输出 uq
// 【1】纯开环运行模式
if(logicContr.Run_mode == 1)
{
// ud = 0
IparkU.Ds = 0;
// uq = ωm*
IparkU.Qs = pi_spd.Ref;
}
// 【2】速度和Id电流闭环正转运行模式
if(logicContr.Run_mode == 2)
{
// ud = D轴 PI 输出
IparkU.Ds = pi_id.OutF;
// uq = iq*
IparkU.Qs = pi_spd.OutF;
}
// 【3】速度和Id电流闭环反转运行模式
if(logicContr.Run_mode == 3)
{
// ud = - D轴 PI 输出
IparkU.Ds= -pi_id.OutF;
// uq = - iq*
IparkU.Qs= -pi_spd.OutF;
}
// // 【4】速度、id和iq电流闭环运行模式
// if(logicContr.Run_mode == 4)
// {
// // ud = D轴 PI 输出
// IparkU.Ds= pi_id.OutF;
// // uq = Q轴 PI 输出
// IparkU.Qs= pi_iq.OutF;
// }
// ====================================================================
// 【FOC步骤4】进行Park反运算==============================================
// 角度信息
IparkU.Sine = ParkI.Sine; // = _IQsinPU(ParkI.Angle);
IparkU.Cosine = ParkI.Cosine; // = _IQcosPU(ParkI.Angle);
IPARK_Cale((p_IPARK) &IparkU);
// 输出 uα、uβ
// ====================================================================
// 【FOC步骤5】将SVPWM输出================================================
Svpwmdq.Ualpha = IparkU.Alpha;//Q24
Svpwmdq.Ubeta = IparkU.Beta; //Q24
SVPWM_Cale((p_SVPWM) &Svpwmdq);
Svpwm_Outpwm();
// ====================================================================
// 【FOC算法结束】========================================================
其他参数整定的方法可参看:PI调节器参数整定(双闭环FOC) - 知乎、【PMSM】一. 经典电流环、速度环设计(上) - 知乎 、永磁矢量控制_沉沙丶的博客-CSDN博客。此外,袁雷老师的《现代永磁同步电机控制原理及 MATLAB 仿真》 一书中也有详细描述。
关于如何调试 FOC 可参看知乎问题:如何调试永磁同步电机有感foc? - 知乎 (zhihu.com)。
5 转子位置信息的获取和转速测算
那么如何保持转子和定子二者磁场正交呢?
- 确定转子位置;
- 基于转子位置,确定定子磁场矢量的期望方向,使它与转子磁场正交;
- 对三相电流进行控制,使其产生所需的定子磁场矢量。


无刷电机的控制是配合着转子(永磁体)的位置(角度)进行的,那如何获知控制所需的转子的位置信息?一般有有传感器和无传感器两种方案:
- 有传感器方案一般采用霍尔元件、光栅编码器、转速反馈频率信号(FG trace)、旋转变压器(Resolver)等方式。
| 电机类型 | 传感器种类 | 主要用途 | 特征 |
|---|---|---|---|
| BLDC | 霍尔效应传感器 | 梯形波、120度通电控制 | 每60度获取一次信号,价格较低,不耐热。 |
| PMSM | 光电编码器 | 正弦波控制、矢量控制 | 有增量型(可得知原位置开始的移动距离)和绝对型(可得知当前位置的角度)两种。分辨率高,但抗震性、防尘能力较弱,成本较高。 |
| 转角传感器 | 正弦波控制、矢量控制 | 分辨率高,适用于恶劣环境。 |
- 而无传感器方案一般采用反电动势检测等方式。
5.1 BLDC——霍尔传感器
霍尔传感器在N极靠近时输出高电平、S极靠近时输出低电平:

霍尔传感器并不会提供转子在扇区内的精确位置,但可以检测转子何时从一个扇区过渡到另一个扇区。以单相无刷电机为例,每当转子转过90度,霍尔信号输出电平交换一次。旋转一周则改变4次。

一般采用加装霍尔元件的方法,来防止在临界位置时电平紊乱输出的干扰:

多数无刷电机在其非驱动端上的定子中嵌入了三个霍尔传感器。根据这三个霍尔传感器信号的组合,就能决定换向的精确顺序。每次换向,都有一个绕组连到控制电源的正极,第二个绕组连到负极,第三个处于失电状态。其中,绕组加电的顺序由六步换向定义。下图为三个60°间距霍尔传感器输出的高低电平波形及实际安装位置:

或采用120°间距排布:
可利用三相霍尔元件的高低电平状态来进行扇区判断:
Hall_Three.HallUVW[0] = GpioDataRegs.GPCDAT.bit.GPIO67 & 0x01; // HALL1 U
Hall_Three.HallUVW[1] = GpioDataRegs.GPCDAT.bit.GPIO68 & 0x01; // HALL2 V
Hall_Three.HallUVW[2] = GpioDataRegs.GPCDAT.bit.GPIO69 & 0x01; // HALL3 W
Hall_Three.Hall_State = Hall_Three.HallUVW[0] + (Hall_Three.HallUVW[1] << 1) + (Hall_Three.HallUVW[2] << 2);
通过霍尔元件进行转速测算的原理用一句话概括,即计算两次换相事件的相隔时间。基于 DSP28335 的测速代码编写如下:
if(Hall_Three.Hall_State != Hall_Three.OldHall_State) // 换相时刻
{
Hall_Three.static_count = 0;
// CPU定时器计数寄存器(TIMH:TIM)。
// TIM 寄存器保存当前 32 位定时器计数值的低 16 位。每隔(TDDRH:TDDR+1)个时钟周期,TIMH:TIM 减 1,其中 TDDRH:TDDR 为定时器预定标分频系数。
// 当TIMH:TIM递减到 0 时,TIMH:TIM寄存器重新转载 PRDH:PRD 寄存器保存的周期值,并产生定时器中断TINT信号
Hall_Three.Time0count = CpuTimer0Regs.TIM.all; // 最大30000000
// 假定转速大于(1/6/2 / 0.2s)(r/s)=0.4167r/s=25r/m
// 两次换相时刻发生于同一CpuTimer0周期内
Hall_Three.Speed_timecount = Hall_Three.Old_Time0count - Hall_Three.Time0count; // 两次换相的时间间隔计数差值
if(Hall_Three.Speed_timecount < 0) // 两次换相时刻发生于不同CpuTimer0周期
Hall_Three.Speed_timecount = Hall_Three.Speed_timecount + 30000000;
// T0 = (一周期计数值 / 150)us = (30000000 / 150)us = 200000us
// 间隔计数值/150 = (间隔计数值/一周期计数值 * 一周期计数值 / 150)us = 间隔计数值/一周期计数值 * T0 = 间隔t us
Hall_Three.Speed_timecount = Hall_Three.Speed_timecount / 150; // 将计数差值处理为单位为us的时间间隔
Hall_Three.Speed_timecountFitter = _IQ10mpy(HallK1, Hall_Three.Speed_timecountFitter)+_IQ10mpy(HallK2, Hall_Three.Speed_timecount);
Hall_Three.Old_Time0count = Hall_Three.Time0count; // 储存当前换相时刻的Time0count
// n(RPS) = (1/6/极对数)(r) / (换相间隔时间)(s)
// n(RPM) = 60 * (1/6/极对数)(r) / (换相间隔时间)(s) = (10 / 极对数 / 换相间隔时间s)(RPM)
// Speed_RPM = speed_coff / Speed_timecountFitter = (1/极对数)*2^24 / 换相间隔时间us = (2^24 / 10 * 1/10^6) * 10/极对数/换相间隔时间s
// = 1.6777248*n(RPM) = 1.6777248*60*n(RPS) = 100.663488*n(RPS)
Hall_Three.Speed_RPM = (Hall_Three.speed_coff/Hall_Three.Speed_timecountFitter);
Hall_Three.IQSpeed_RPM = _IQ12toIQ(Hall_Three.Speed_RPM);
}
else if(Hall_Three.Hall_State == Hall_Three.OldHall_State) // 位置检测和上次处于同一区间
{
Hall_Three.static_count++;
if(Hall_Three.static_count >= 5000)
{
Hall_Three.static_count = 0;
STOP_CAR( );
Hall_Three.Speed_RPM = 0;
}
}
Hall_Three.OldHall_State = Hall_Three.Hall_State; // 储存当前位置
其中,两极对电机的极对数及相关参数设置如下:
Hall_Three.Poles = 2;
Hall_Three.speed_coff = _IQ(0.166667) * 6 / Hall_Three.Poles;
// _IQ(0.166667) * 6 / Hall_Three.Poles = 0.166667 * 16777216.0 * 6 / 2 = 8388624.8 约等于 2^24 / 2 = 8388608
5.2 PMSM——增量式编码器
5.2.1 编码器测速代码
基于DSP28335的代码编码器相关代码及注释书写如下:
#define MOTOR_LINE 1000 // 编码器线数(光栅数),即旋转一圈产生的脉冲数
#define PI 3.14159265358979
extern EQEP EQEPPare;
extern Speed_QEP Speed_QEPPare;
extern float32 T;
void QEPEncoder_init(void)
{
EALLOW;
EQep1Regs.QDECCTL.all = 0x0000; // QEP解码控制寄存器
// QSRC(15~14位) - 00:位置计数器选择正交计数模式
// XCR(11位) - 0:2倍外部时钟频率,上下边沿计数
// SWAP(10位) - 0:内部不交换正交脉冲信号
EQep1Regs.QEPCTL.all = 0x821E; // QEP控制寄存器
// 1000 0010 0001 1110:
// 1000:
// FREE,SOFT - 10:仿真挂起对其无影响,调试中断时不复位EQEP;
// PCRM - 00:位置计数器复位模式为当索引(Index)事件发生时触发QPOSCNT复位。QPOSCNT计数值从0~4N线性发化,即仍位置计数寄存器中可直接得到不转子位置成比例关系的位置计数值QP0SCNT。
// 0010:
// SEI - 00:索引事件初始位置计数器SEI不动作;
// IEI - 10:索引事件初始位置计数器IEI的初始位置计数器在QEPI(即Z信号)上升沿
// 0001:
// SW1 - 0:软件初始化位置计数器不动作;
// SEL - 0:在QEPS的上升沿锁存位置计数器的值;
// IEL - 01:在索引信号上升沿锁存位置计数器
// 1110:
// QPEN - 1:正交位置计数器使能;
// QCLM - 1:当单位时间事件发生时,锁存数据;
// UTE - 1:使能单位定时器;
// WDE - 0:禁止QEP看门狗
EQep1Regs.QPOSCTL.all = 0x0000; // QEP位置比较控制寄存器
// PCSHDW - 0:禁止映射寄存器,立即加载;映射寄存器的加载
EQep1Regs.QUPRD = 1500000; // QEP单位时间周期寄存器。此寄存器中的值为单位时间周期值,1500000代表设置频率为100Hz
EQep1Regs.QCAPCTL.all = 0x8075; // QEP捕获控制寄存器
// 1000 0000 0111 0101:
// CEN - 1:使能 QEP 捕捉单元
// CCPS - 111:CAPCLK = SYSCLK / 128,CAP捕获128分频
// UPPS - 0101:UPEVNT = QCLK / 32,单位位置32分频
EQep1Regs.QPOSMAX = 4 * MOTOR_LINE; // QEP最大位置计数器寄存器。乘4是因为:通过梱测 QEPA 和 QEPB 信号的边沿为位置计数器提供计数时钟 QCLK,因此 eQEP 逻辑产生的时钟频率是输入时钟频率的 4 倍。
// 位置计数器的值是脉冲的累积,当位置计数器达到这个值时,会自动回滚到零。
EDIS;
// EQEP
EQEPPare.PolePairs = 2;
EQEPPare.initial_angle = _IQ(0.0);
// Speed_QEP
// n = BaseRpm = 60*BASE_FREQ/(极对数PolePairs)
// BASE_FREQ = n*PolePairs/60
Speed_QEPPare.BaseRpm = 3000;
// 速度系数K1计算 ==========================================================
// 单圈总脉冲数为C(=4*1000),每T(=0.08ms)进行一次测算,T时间内统计到的编码器脉冲数为M0
// M法测速:转速n = (M0/(C*T))(r/s) = (60 * (极对数*M0/C) * (C/极对数) / (C * T))(r/min) = (1 / ((PolePairs/60)*T) * deltaE)(r/min)
// 机械角度增量deltaM = M0/C (pu) 电角度增量deltaE = 极对数*deltaM = 极对数*M0/C(pu)
// n = (1 / ((PolePairs/60)*T)) * deltaE
// 1 = (1 / ((n*PolePairs/60)*T)) * deltaE = (1 / (BASE_FREQ*T)) * deltaE = K1 * deltaE
// 即,如果K1 = 1 / (BASE_FREQ*T),则输出的Speed = _IQ(1.0)
Speed_QEPPare.K1 = _IQ21( 1 / ((Speed_QEPPare.BaseRpm*EQEPPare.PolePairs/60)*T) ); // 1 / (BASE_FREQ*T)
// =====================================================================
// 滤波系数K2、K3计算 =======================================================
// 采样周期T = 0.00008s = 80us;截止频率fc = 5Hz,时间常数Tc = 1/(2*pi*fc)
// 一阶低通滤波器
// s域:Y(s)/R(s) = 1/(Tc*s + 1)
// 时域:dY(t)/dt = 1/Tc * [R(t) - Y(t)]
// 离散化[Y(k)-Y(k-1)]/T = 1/Tc * [R(k) - Y(k)]
// 所以有:Y(k) = Tc/(Tc + T) * Y(k-1) + T/(Tc + T) * R(k)
// 令 K2 = Tc/(Tc + T) = 1/(1 + T * 2*pi*fc),K3 = 1 - K2
Speed_QEPPare.K2 = _IQ(1 / (1 + T * 2*PI*100));
Speed_QEPPare.K3 = _IQ(1) - Speed_QEPPare.K2;
// =====================================================================
}
void QEPEncoder_Cale(p_EQEP pV)
{
// 状态寄存器 QEPSTS
// 正交方向标志位 QDF:0-逆时针;1-顺时针
pV->DirectionQep = EQep1Regs.QEPSTS.bit.QDF;
// EQEP1的位置计数器寄存器 QPOSCNT
// 用QEP的计数值 RawTheta 来表示的电机实际角度
pV->RawTheta = EQep1Regs.QPOSCNT + pV->CalibratedAngle; // CalibratedAngle电机A相绕组和码盘Index信号之间的夹角的对应计数值
if(pV->RawTheta < 0)
pV->RawTheta = pV->RawTheta + EQep1Regs.QPOSMAX;
else if(pV->RawTheta > EQep1Regs.QPOSMAX)
pV->RawTheta = pV->RawTheta - EQep1Regs.QPOSMAX;
// 计算机械角度 ====================================================================================================
// MechTheta = (0.9999/total count) * 角度计数值
// 即用一无量纲比例数(pu)来表示机械角度。例如,如果求得机械角度=0.6,即完整一周旋转的60%。
pV->MechTheta = pV->MechScaler * pV->RawTheta;//Q24
// ================================================================================================================
// 计算电角度 ======================================================================================================
// 机械角除以一整圈(360 度)时的余数。可确保生成的电角度始终位于 _IQ(0.0) 和 _IQ(1.0) 之间。
// 如果 MechTheta 为 _IQ(0.6)(完整旋转的 60%) ,则电角度将为 (2 * _IQ(0.6) - _IQ(1.0)) = _IQ(0.2)。
// pV->ElecThetaYS = pV->MechTheta % _IQ(1.0);
pV->ElecThetaYS = (pV->PolePairs * pV->MechTheta) % _IQ(1.0);
pV->ElecTheta = pV->ElecThetaYS + pV->initial_angle;//Q24
// if(pV->ElecTheta > _IQ(1.0))
// pV->ElecTheta -= _IQ(1.0) ;
// else if(pV->ElecTheta < _IQ(0.0))
// pV->ElecTheta += _IQ(1.0);
// ================================================================================================================
// QEP中断标志寄存器 QFLG
if(EQep1Regs.QFLG.bit.IEL == 1) // 索引事件锁存中断标志 IEL - 1:中断产生
{
pV->IndexSyncFlag = 0x00F0;
pV->QepCountIndex = EQep1Regs.QPOSILAT; // 当索引事件发生时,位置计数器的值会加载到这个寄存器(QEP索引位置加载寄存器) QPOSILAT 中
// QEP中断清除寄存器 QFLG
EQep1Regs.QCLR.bit.IEL = 1; // 清除索引事件锁存中断标志 IEL - 1:清除中断标志
}
if(EQep1Regs.QFLG.bit.UTO == 1) // 单位时间事件中断标志 UTO - 1:中断产生
{
// QEP状态寄存器 QEPSTS
// COEF 捕获溢出错误标志位:0-无意义;1-在QEP捕获计时器发生溢出
// CDEF 捕获方向错误标志位:0-无意义;1-在捕获事件发生时,方向改变
if((EQep1Regs.QEPSTS.bit.COEF || EQep1Regs.QEPSTS.bit.CDEF))
EQep1Regs.QEPSTS.all = 0x000C; // C(1100):COEF-1;CDEF-1
// QEP捕获周期锁存寄存器 QCPRDLAT —— QEP边沿捕获周期锁存寄存器
else if(EQep1Regs.QCPRDLAT != 0xffff)
pV->QepPeriod = EQep1Regs.QCPRDLAT;
}
}
void Speed_QEP_Cale(p_Speed_QEP pV)
{
if((pV->ElecTheta < _IQ(0.9)) & (pV->ElecTheta > _IQ(0.1))) //
pV->Tmp = _IQmpy(pV->K1, (pV->ElecTheta - pV->OldElecTheta));//Q21
else // 0.9-0.0-0.1的过零过程不可用上式计算速度
pV->Tmp = _IQtoIQ21(pV->Speed);//Q21
// 速度滤波(K2、K3 推导见上)
pV->Tmp = _IQmpy(pV->K2, pV->Tmp) + _IQmpy(pV->K3, _IQtoIQ21(pV->Speed));//Q21
// 限幅在 (-1, 1)之间
pV->Tmp=_IQsat(pV->Tmp, _IQ21(1), _IQ21(-1));//Q21
pV->Speed = _IQ21toIQ(pV->Tmp);//Q24
pV->OldElecTheta = pV->ElecTheta;//Q24
pV->SpeedRpm = _IQmpy(pV->BaseRpm, pV->Speed);//Q0
if(pV->SpeedRpm < 0)
pV->SpeedRpm = -pV->SpeedRpm;
}
代码中对所得速度采用了一阶滤波,其相关资料如下:
5.2.2 转子初始角度修正
上文代码中,有一极易忽视又至关重要的变量 CalibratedAngle。该变量为电机A相绕组和码盘Z相信号(Index信号)之间的夹角的对应计数值,用于对转子初始角度进行修正。
在电机通电前,电机转子处于一未知位置,而 FOC 算法的关键便是基于转子位置生成与之方向正交的磁场以驱动转子旋转,故而在驱动电机旋转之前必须要对电机转子初始角度进行修正。转子初始角度修正一般采用磁定位的方法,即通过给定子绕组通入一已知大小和方向的电流 is ,以产生恒定的磁场,吸引转子旋转至与定子绕组磁链重合位置,从而得到转子的初相位。在此,笔者将自己本科毕业论文截取部分进行说明:

电机各绕组抱轴代码及注释如下:
void AnglePhase_init(void)
{
// 当转子磁链与 A 轴重合的时候,逆变器的开关状态为:SA:SB:SC—1:0:0
// 因此可以得到 ia = iDC,ib = ic = −iDC/2(iDC为母线电流)
// 通过 Clarke 变换有:
// Ialpha = ia = iDC
// Ibeta = sqrt(3)/3 * (ia + 2*ib)
// = sqrt(3)/3 * (iDC + 2*(−iDC/2)) = 0
// theta = 0,则通过 Park 变换有:
// Id = Ialpha * cos(theta) + Ibeta * sin(theta)
// = iDC * cos(theta) = iDC
// Iq = Ibeta * cos(theta) - Ialpha * sin(theta)
// = -iDC * sin(theta) = 0
// theta = -90°,则通过 Park 变换有:Id = 0;Iq = iDC
if(mod == 1) // 与 A 轴重合
{
IparkU.Ds = _IQ(10);
IparkU.Qs = 0;
ParkI.Angle = 0;
}
else if(mod == 2)
{
IparkU.Ds = 0;
IparkU.Qs = _IQ(10);
ParkI.Angle = _IQ(-0.25);
}
// 当转子磁链与 B 轴重合的时候,逆变器的开关状态为:SA:SB:SC—0:1:0
// 因此可以得到 ib = iDC,ia = ic = −iDC/2(iDC为母线电流)
// 通过 Clarke 变换有:
// Ialpha = ia = −iDC/2
// Ibeta = sqrt(3)/3 * (ia + 2*ib)
// = sqrt(3)/3 * (−iDC/2 + 2*(iDC)) = sqrt(3)/2 * iDC
// theta = 0,则通过 Park 变换有:
// Id = Ialpha * cos(theta) + Ibeta * sin(theta)
// = −iDC/2
// Iq = Ibeta * cos(theta) - Ialpha * sin(theta)
// = sqrt(3)/2 * iDC
else if(mod == 3)
{
IparkU.Ds = _IQ(-5);
IparkU.Qs = _IQ(0.866025404*10);
ParkI.Angle = 0;
}
// 当转子磁链与 C 轴重合的时候,逆变器的开关状态为:SA:SB:SC—0:0:1
// 因此可以得到 ic = iDC,ia = ib = −iDC/2(iDC为母线电流)
// 通过 Clarke 变换有:
// Ialpha = ia = −iDC/2
// Ibeta = sqrt(3)/3 * (ia + 2*ib)
// = sqrt(3)/3 * (−iDC/2 + 2*(−iDC/2)) = -sqrt(3)/2 * iDC
// theta = 0,则通过 Park 变换有:
// Id = Ialpha * cos(theta) + Ibeta * sin(theta)
// = −iDC/2
// Iq = Ibeta * cos(theta) - Ialpha * sin(theta)
// = -sqrt(3)/2 * iDC
else if(mod == 4)
{
IparkU.Ds = _IQ(-5);
IparkU.Qs = _IQ(-0.866025404*10);
ParkI.Angle = 0;
}
ParkI.Sine = _IQsinPU(ParkI.Angle);//Q24
ParkI.Cosine = _IQcosPU(ParkI.Angle);//Q24
}
5.2.3 编码器差分信号处理
部分增量式编码器输出的是两两反相的 3 对差分脉冲信号:A+和A-、B+和B-、Z+和Z-。针对这 3 对差分脉冲信号,需设计差分电路或采用现成芯片处理为 3 路单端信号,再输出给 MCU。推荐参考资料如下:
- 编码器差分转单端电路(含原理图),基于STM32编码器接口模式的程序设计及电路设计_差分转单端芯片-CSDN博客
- 并行正交编码器接口计算说明书_am26c32原理图-CSDN博客
- 差分驱动芯片使用总结_am26ls32-CSDN博客
- 基于FPGA和ABZ增量式编码器的转子位置检测_stm32abz信号怎么计算位置-CSDN博客
若期望输出信号电压为 3.3V,笔者推荐采用 TI 公司的 AM26LV32 芯片进行处理。
5.3 反电动势
在既定电机磁场和绕组数固定的情况下,反电动势的幅度与电机的旋转速度成正比。当电机在控制模式下运转多个换向周期直到获得一定速度后,无传感器测量便能够确定转子位置。无传感器控制的无刷电机适合安装在难以检修的位置,或在多灰尘、多油的环境中运行,但不适合需要较低速度的应用,因为此时反电机势很小而难以测量(故其启动需要特殊方法),会造成工作效率不高。

一般监测未被驱动相的反电动势变化信息(过零点信息)推算转子角度:

基于反电动势测量转子位置的控制器,在启动时会面临额外的挑战:转子静止时不会产生反电动势(只有当转子磁场切割定子线圈时才会产生反电动势)。目前经常采用的启动方法为“三段式”启动。一般来说,三段式启动包括定位、加速、切换三个过程。
以下引用一篇硕士论文(吴财源.基于DSC的无刷直流伺服电机驱动器设计与研究[D]. 华南理工大学, 2011)举例说明反电势过零检测的硬件电路设计。
反电动势过零点硬件检测电路原理图如图 2-14 所示,端电压分压信号经过滤波后输入至比较器,与参考电压 Uref 进行比较。当非导通相反电势 eC = 0 时,有 UCG = 0,此时通过比较器 ZC 输出低电平,即得到反电势过零点信号。
关于构建中性点和过零检测参考资料:
- 无刷直流电机虚拟中性点分析-CSDN博客
- 无位置传感器的电机控制_无位置传感器控制原理-CSDN博客
- BLDC六部换相——过零点检测电路分析_反电动势与中性点电压比较检测过零点-CSDN博客
- 【零基础玩转BLDC系列】基于反电动势过零检测法的无刷直流电机控制原理_无刷电机反电动势检测电路-CSDN博客
- 【无刷电机】无感方波驱动方案-CSDN博客
- 直流无刷电机无感检测电路原理_一种直流电机的电流检测电路-CSDN博客
6 系统设计
6.1 考虑因素

功率管设计需考虑以下问题:

其中开关损耗有如下公式:

6.2 硬件与仿真资料推荐
【推荐开源工程】
- 技术干货 |【自制】FOC驱动板 (qq.com)
- 做了个无刷电机控制器,1.5KW大功率,开源了! (qq.com)
- 做了个FOC驱动器,成本仅50元,开源了!精度很高!
- 开源基于STM32F401单片机的FOC驱动器(SimpleFOC)
- GitHub - Staok/iBLDC
- GitHub - ohBrooks/MY_FOC_Driver: FOC Learning
- 关于本项目 — [野火]电机应用开发实战指南—基于STM32 文档 (embedfire.com)
- 逐飞科技基于STC的无感无刷驱动开源项目
- 【超硬货】FOC(电机矢量控制)程序分块细解 - 知乎、FOC(电机矢量控制)的“大地图”(算法架构) - 知乎 (zhihu.com)
- SimpleFOC、ODrive和VESC 教程链接汇总_odrive和simplefoc哪个好-CSDN博客
- 基于DSP的三相开关霍尔永磁同步电机控制_dsp 电机通信方式-CSDN博客
- 【一文讲通】BLDC的六步法&PMSM的FOC法综合_bldc和pmsm电机的驱动电路-CSDN博客
【Simulink 仿真模型搭建】
- FOC中的Clarke变换和Park变换详解(动图+推导+仿真+附件代码) - 知乎 (zhihu.com)
- 【永磁同步电机】SVPWM控制算法+Matlab/Simulink仿真详解 - 知乎 (zhihu.com)
- 永磁同步电机(PMSM)磁场定向控制(FOC)及Matlab/Simulink仿真分析-CSDN博客
- Simulink仿真---SVPWM算法_svpwm时间计算-CSDN博客
- Simulink —— BLDC基础及六步转速控制模型_bldc电机的simulink仿真-CSDN博客
- Simulink —— PMSM基础及FOC控制模型-CSDN博客
- 直流无刷电机及Matlab/Simulink驱动仿真_无刷电机仿真-CSDN博客
- 永磁同步电机矢量控制Matlab/Simulink仿真教程_哔哩哔哩_bilibili

本文围绕无刷直流电机(BLDC)展开,介绍其定义、与其他电机的比较及基本原理。详细阐述了驱动方法,如六步换相控制、正弦波控制和磁场定向控制(FOC),还提及转子位置信息获取和转速测算方式,包括霍尔传感器、增量式编码器和反电动势法,最后给出系统设计考虑因素。








9219

被折叠的 条评论
为什么被折叠?



