FOC算法
FOC(Field Oriented Control)算法
无刷电机(BLDC)基本认识
无刷电机(Brushless DC Motor,简称BLDC)是一种采用电子换向技术代替传统机械电刷和换向器的直流电动机。它通过控制器精确控制定子绕组的通电顺序和时机,驱动永磁体转子旋转,实现了高效、可靠的能量转换。
工作原理与结构
基本构造
- 定子:通常由硅钢片叠压而成,嵌有三相绕组(U、V、W),绕组按一定规律分布
- 转子:采用高性能永磁材料(如钕铁硼)制成,磁极对数根据设计需求确定
- 位置传感器:霍尔传感器、光电编码器或无传感器技术,用于检测转子位置
工作过程
- 位置检测:传感器实时检测转子磁极位置
- 信号处理:控制器根据位置信号确定当前应导通的绕组相
- 功率驱动:通过逆变电路(通常为三相全桥)向相应绕组供电
- 磁场生成:通电绕组产生旋转磁场,吸引转子永磁体转动
- 连续换向:控制器根据转子位置连续切换导通相,维持平稳旋转
电子换向 vs 机械换向
传统有刷电机依靠电刷与换向器的滑动接触实现电流换向,而无刷电机通过半导体开关器件(如MOSFET、IGBT)完成电子换向,消除了机械接触带来的诸多问题。
与有刷电机的比较
优势特性
- 高效率:无电刷摩擦损耗和接触电阻,效率通常可达85%-95%
- 长寿命:无机械磨损部件,轴承寿命成为主要限制因素
- 低维护:无需更换电刷,维护成本大幅降低
- 高功率密度:转子无绕组,可实现更高转速和更小体积
- 低电磁干扰:无换向火花,电磁兼容性优异
- 精确控制:配合现代控制算法,可实现精准的速度和位置控制
相对局限
- 成本较高:需要专用控制器和位置检测系统
- 控制复杂:需要电子换向电路和相应控制算法
- 启动特性:无传感器方案需要特殊启动策略
控制技术分类
1. 按传感器类型
- 有传感器控制:使用霍尔传感器、编码器等直接检测位置
- 无传感器控制:通过反电动势、磁链观测器等间接估算位置
2. 按控制策略
- 方波控制(六步换向):简单可靠,转矩脉动较大
- 正弦波控制(FOC):转矩平稳,效率更高,控制复杂
- 直接转矩控制(DTC):动态响应快,参数依赖性低
3. 关键技术要点
- PWM调制技术:SPWM、SVPWM等调制策略
- 保护功能:过流、过压、过热、堵转保护
- 通信接口:CAN、UART、PWM信号等控制接口
无刷电机硬件控制原理
传统的有刷电机——电流通过碳刷和换向器的滑动接触进入线圈,机械结构决定通电顺序。无刷电机则把这个机械换向的工作交给了电子电路,就像用"电子指挥家"代替了"机械开关"。
核心硬件:三相全桥逆变电路
基本架构
无刷电机控制最核心的硬件是一个三相全桥电路,它由6个功率开关管(通常是MOSFET或IGBT)组成,排列成三组桥臂:
电源正极
│
├─[Q1]───U相───[Q4]─┐
├─[Q2]───V相───[Q5]─┤──电机三相绕组
└─[Q3]───W相───[Q6]─┘
│
电源负极

开关管的控制规则
每条桥臂的上下两个开关管绝不能同时导通,否则会造成电源短路。因此控制逻辑必须确保: - 上管导通时,下管必须关断 - 下管导通时,上管必须关断 - 同一时刻通常有且只有两个开关管导通(一个上管,一个下管)
电子换向:六步换向法
基本原理
无刷电机的转子是永磁体,定子有三组绕组(U、V、W)。要让转子持续转动,就需要让磁场"追赶"着转子。具体做法是:根据转子当前的位置,给适当的两相通电,产生一个吸引转子继续前进的磁场。
六步换向序列
这是最基础的换向方式,每个电周期分为6个步骤:
| 步骤 | 导通相 | 磁场方向 | 对应转子位置 |
|---|---|---|---|
| 1 | U+ V- | 从U指向V | 0-60° |
| 2 | U+ W- | 从U指向W | 60-120° |
| 3 | V+ W- | 从V指向W | 120-180° |
| 4 | V+ U- | 从V指向U | 180-240° |
| 5 | W+ U- | 从W指向U | 240-300° |
| 6 | W+ V- | 从W指向V | 300-360° |
示例



硬件实现
简化示例

在硬件上,这六个步骤对应着六种不同的开关管组合: - 步骤1:Q1导通(U相上管),Q5导通(V相下管),其他关断 - 步骤2:Q1导通(U相上管),Q6导通(W相下管),其他关断 - 依此类推...
在上述流程,我们仅导通了2个MOS管,有一个空闲,造成了资源浪费。
实际上,我们也可以导通另一个MOS管,只要产生的磁场方向和所需相同即可,这样我们就可以合成6个标准方向的磁场了。

位置检测:换向的"眼睛"
霍尔传感器
要确定何时换向,必须知道转子当前位置。最常用的方法是使用霍尔传感器——通常三个传感器以120°电角度间隔安装在定子上。
传感器输出与换向关系
每个霍尔传感器输出数字信号(高电平或低电平),三个传感器组合出8种状态(实际有效为6种)。这6种状态正好对应六步换向的6个步骤:
| 霍尔状态 | 换向步骤 | 应导通相(U V W) |
|---|---|---|
| 101 | 步骤1 | 1 0 1 |
| 100 | 步骤2 | 1 0 0 |
| 110 | 步骤3 | 1 1 0 |
| 010 | 步骤4 | 0 1 0 |
| 011 | 步骤5 | 0 1 1 |
| 001 | 步骤6 | 0 0 1 |
根据霍尔状态选择导通相的原理是:根据预期转动方向,产生与当前磁铁方向垂直的磁场。(胡萝卜理论,磁力最大)
控制器读取霍尔信号,查表确定当前应执行哪个换向步骤。
以上是简单的理论叙述。我们控制电机,不仅仅希望简单让他转起来,我们还希望能控制它的转速、位置、扭矩等,这就是FOC算法要解决的。FOC控制就是一种对电机运动模型进行抽象化和简化,进而有规律控制各个MOS管开关和通断的过程。
FOC算法理论
引入
创造可控方向、大小的磁场


结构

其中,位置、速度由传感器获取,而电流表征磁场强度。
而电流运算与mos管开关状态怎么关联?(进而关联磁场强度)又涉及到了两个变换。且听后文分解。

从直流电到正弦波
上面我们说了,我们要控制磁场方向垂直于转动的磁铁。而磁场方向控制是通过控制六种状态其中两种的占比来实现的(通过逆变桥的 PWM 斩波)。
经过时间平滑以及MOS管滤波电感处理后,我们实际得到三个线圈的电流实际是3个相差相位120°的正弦波。
也可以这么理解:
我们把这个垂直于磁铁的磁场矢量拿出来,由比奥萨法尔定律得到的感应电流与磁场关系 $$ \boldsymbol{B}=\mu_{0}n\boldsymbol{I} $$ 我们知道,这个方向就是实际电流的方向。把它分解到三个线圈的方向,我们可以得到三相电流$i_a,i_b,i_c$
这样,三相电流就是相差相位120°的正弦波了。
接下来我们介绍怎么实现3个读取电流和1个目标电流的闭环运算控制。
Clarke变换 & Park变换

克拉克变换
-
把三相随时间变换的,相位差为120°的电流波形抽象化为三个间隔120°的矢量。
-
利用三角函数对矢量进行降维,正交分解到两个轴,从此复杂的三相变化问题就降解为了α-β坐标轴的坐标上的数值变化问题。
说明:根据基尔霍夫定律,只需要两路电流传感器即可。
我们需要知道能够使得电机旋转的$I_α$和$I_β$电流输入规律,如果我们可以知道这个能够使得电机旋转的$I_α$和$I_β$电流输入规律,我们就可以通过克拉克逆变换,把这个旋转情况下的$I_α$和$I_β$逆变换为$i_a,i_b,i_c$三相电流波形,从而就实现了用把$i_a,i_b,i_c$降维后的$I_α$和$I_β$实现对电机的控制,那么问题就没有原来我们想的直接控制$i_a,i_b,i_c$来控制电机旋转来得复杂了。
帕克变换
- 把$I_α$和$I_β$旋转到一个新的坐标系
运用
- 把读取的$i_a,i_b,i_c$分解为为$I_α$和$I_β$(Clarke变换)
- 将$I_α$和$I_β$变换到目标电流$I_d$的坐标系下(Park变换,$I_d$为目标电流,一般垂直于转子的方向)
- 对$I_f$坐标系下的两个轴的电流进行pid运算得到输出(另一方向为$I_p$,一般为0)
- 将输出重新变换回$I_α'$和$I_β'$(Park逆变换)
- 将$I_α'$和$I_β'$重新变换为输出的$i_a,i_b,i_c$(Clarke逆变换和svpwm)
Note
接下来的部分是基于移植到STM32的简化SimpleFOC平台代码编写的。这个平台实则实现了我们上面理论部分的内容,我们接下来实现控制速度、位置的编写。代码部分
基础代码
setPhaseVoltage设置相电压
这段代码的功能是根据$U_q和U_d$通过帕克逆变换和克拉克逆变换得到相电压,并通过单片机的PWM输出给到三相。
一般设置$U_d$为0,这样作用方向就垂直于angle_el了。
void setPhaseVoltage(float Uq, float Ud, float angle_el);
补充:电角度和机械角度
电角度即是我们操控DQ坐标系(或者说三相电流矢量)的方向,而机械角度则是转子的方向。
我们之前演示的转自都是一对磁极。实际上,电机可以是多个磁极,即多个极对数。可以想象,当极对数增多时,转子转过一圈,电角度会转过好几圈。(电流峰值多次出现) $$ electricalAngle = shaftAngle\times pole_pairs $$
电角度和机械角度
// shaft angle calculation
float shaftAngle(void)
{
// if no sensor linked return previous value ( for open loop )
//if(!sensor) return shaft_angle;
return sensor_direction*getAngle() - sensor_offset;
}
float electricalAngle(void)
{
return _normalizeAngle((shaft_angle + sensor_offset) * pole_pairs - zero_electric_angle);
}