BLDC有感FOC算法理论及其STM32软硬件实现
位置传感器:旋转编码器 MCU:STM32F405RGT6 功率MOS驱动芯片:DRV8301
全文均假设在无弱磁控制的情况下
FOC算法理论
首先,我们要知道FOC是用来干什么的?有什么用?相比于BLDC的六步方波驱动有什么优点?
传统的六步方波驱动由于产生的磁场旋转运动不连续,导致电机转子受的驱动力矩发生突变(转矩脉动),即使通过增加电机极对数也不能的很好解决这一问题。另外由于方波驱动产生的驱动力不能全部的用于转子切线方向的转矩,还有一部分力损失在转子径向,以上在运动学层面可以理解为推箱子的力和箱子运动的方向不完全重合(未能产生最大的驱动转矩),在能耗层面可以理解为电能没有全部转化为电机的动能(电磁效率小于1,而FOC能使电磁效率为1。注意,即使是基于转子磁场的定向控制,整体的功率因素也不能达到1,而是会随着定子电流矢量的变化而变化),一部分的能量以其它的形式损失了。所以,FOC驱动BLDC是有必要的。
所以,我们现在就清楚了要拿FOC来干什么,我们的目的就是通过FOC算法控制三相绕组产生一种旋转磁场,该磁场可以最大化转子受到的驱动转矩,使得转动平稳的同时降低不必要的能耗。同时,通过测量转子磁链的位置,可以控制定子电流矢量使得定子磁链和转子磁链以一个相同的角速度旋转,从而达到同步的目的,这样两者磁链夹角一定从而保证了转矩的恒定。
参考于《现代电机控制技术》-王成元,要控制转子受到的驱动转矩,根源在于控制定子电流矢量的幅值和相对于转子磁链的空间相位角β,当该空间相位角β为90°时,驱动转矩达到最大,其中转子磁链方向为转子磁极方向,如以下公式所示:
$$ t_{e}=P_{0} Ψ_{f} i_{s} sin(β) $$
上式中P0为电机磁极对数,Ψf为转子磁链,is为定子电流矢量
Vector control, also called field-oriented control (FOC), is a variable-frequency drive (VFD) control method in which the stator currents of a three-phaseAC electric motor are identified as two orthogonal components that can be visualized with a vector. One component defines the magnetic flux of the motor, the other the torque. The control system of the drive calculates the corresponding current component references from the flux and torque references given by the drive's speed control. Typically proportional-integral (PI) controllers are used to keep the measured current components at their reference values. The pulse-width modulation of the variable-frequency drive defines the transistor switching according to the stator voltage references that are the output of the PI current controllers.[1]
以上参考自维基百科
好了,现在目标已经有了,该如何去实现FOC呢?
下面需要线性代数和电子技术的基础。下图1表示了一种基本的BLDC星形接法。

图1 BLDC简单的星形接法以及三相绕组的23=8的通电状态
可以从上图1中看出,每相绕组一端接在N-MOS驱动半桥的中间,另一端与其它绕组相连,我们这里把每相半桥的高边MOS导通低边MOS截至定义为二进制1,高边MOS截至低边MOS导通定义为二进制0,那么三相绕组一共有23=8种状态,以ABC相为顺序的二进制序列有
| U0 | U1 | U2 | U3 | U4 | U5 | U6 | U7 |
| [0 0 0] | [0 0 1] | [0 1 0] | [0 1 1] | [1 0 0] | [1 0 1] | [1 1 0] | [1 1 1] |
以上8种二进制序列在上图1中右半部分已经标出,其中U0与U7为零矢量,故在图1中没有画出。Unum命名方式的num对应二进制序列的十进制值,我们可以看到每一种状态都有一种方向,各自的方向由二进制序列对应的MOS开关状态产生的磁场方向决定,如序列为[1 1 1]即状态为U7时ABC绕组的电流方向如图1中红色矢量所示。在图中画出的U1~U6将一周分为六个扇区。
好,现在假设电机正在转动,那么我们可以通过编码器测得电机转子的实时角度和B相和C相的相电流(如图1所示,相电流在下桥臂导通时采样),由KCL定律可以获得A相的相电流。那么此刻ABC三相的相电流如果不加以控制的话是不会正好在转子当前角度的情况下对转子有最大的转矩,此刻的三相相电流就是实际的控制系统输出量,我们理想的三相相电流输出量应该满足前述条件,所以我们可以用PID控制使得理想与实际相电流之差为零向量。
但是同时控制一个向量的三个相电流变量繁琐且难以控制,另外一个问题是,当我们通过编码器测得电机转子的实时角度后,如何确定这个理想相电流向量?我们前面已经讲到,这个电流向量应该使得转子受到的切向力最大,径向力为零,即定子电流矢量的幅值和相对于转子磁链的空间相位角β为90°。在下图2中画出了实际输出的三相电流和电机转子的实时位置。

图2 clarke变换和park变换
在上图2的左图中,向量Id表示电机转子磁场实时的方向,Iq表示转子磁场切向方向,ABC为三相电流,我们可以由右手定则得到定子磁场方向。最好的情况是,此时三相电流产生的定子磁场方向与Iq方向重合,与Id方向正交,换句话说就是定子磁场与转子磁场的相互作用力全都作用于转子的切向方向,径向方向相互作用力为零,这样的情况下,效率最高,转矩最大。然而通常的情况是,实际的定子磁场总在Id方向有一定的分量,这就需要我们设计一个控制器使得Iq分量为我们想要的一个值同时Id方向上的分量为零。
由此,我们控制三相电流的目标就转换成了控制Iq和Id两个分量,实际与理想三相相电流的比较只需要在新坐标系FrameIq-Id或者Frameθ进行。图2中θ角表示电机转子的角度。由三相相电流坐标系到Frameθ坐标系可由下面的转换矩阵表示:(其中2/3为等幅值转换系数,等幅值转换不改变转换后电流的峰值,也可以用等功率转换系数(2/3)0.5)
$$ \left[ \begin{matrix} Id \\ Iq \end{matrix} \right]=\frac{2}{3}\left[ \begin{matrix} \cos(\theta) & \cos(\theta-\frac{2\pi}{3}) & \cos(\theta+\frac{2\pi}{3}) \\ -\sin(\theta) & -\sin(\theta-\frac{2\pi}{3}) & -\sin(\theta+\frac{2\pi}{3}) \end{matrix} \right]\cdot\left[ \begin{matrix} Ia \\ Ib \\ Ic \end{matrix} \right] \tag{1} $$
事实上,上式(1)直接把三相相电流转化为了Id-Iq形式,中间包含了clarke变换和park变换。在上图2中的中间部分和右部分分别表示的是clarke变换和park变换。两者的含义分别为:
- clarke变换将三相相电流表示在FrameI_alpha-I_beta中,该直角坐标系的I_alpha轴与相电流方向a重合。
- park变换进一步将在FrameI_alpha-I_beta中表示的相电流表示在一个相对于坐标系FrameI_alpha-I_beta旋转了θ角的坐标系Frameθ中,Frameθ坐标系固定在转子磁场上随转子一同旋转。
所以我们也可将上式(1)分步走,先进行clarke变换,如下式所示:
$$ \left[ \begin{matrix} I alpha \\ I beta \end{matrix} \right]=\frac{2}{3}\left[ \begin{matrix} 1 & -\frac{1}{2} & -\frac{1}{2} \\ 0 & \frac{\sqrt{3}}{2} & -\frac{\sqrt{3}}{2} \end{matrix} \right]\cdot\left[ \begin{matrix} Ia \\ Ib \\ Ic \end{matrix} \right] \tag{2} $$
变换然后进行park变换,如下式所示:
$$ \left[ \begin{matrix} Id \\ Iq \end{matrix} \right]=\left[ \begin{matrix} \cos(\theta) & \sin(\theta) \\ -\sin(\theta) & \cos(\theta) \end{matrix} \right]\cdot\left[ \begin{matrix} I alpha \\ I beta \end{matrix} \right] \tag{3} $$
好现在我们通过使用线性变换将电流矢量变换成在Frameθ中表示的Iq和Id,将理想的Iqideal和Idideal与这个二维向量作差,就可以得到要输入到PID控制器的差值Iqerror和Iderror,如下式:
$$ \left[ \begin{matrix} Id_{error} \\ Iq_{error} \end{matrix} \right]= \left[ \begin{matrix} Id_{ideal} \\ Iq_{ideal} \end{matrix} \right]- \left[ \begin{matrix} Id \\ Iq \end{matrix} \right] \tag{4} $$
现在我们已经得到了要消灭的差值Iqerror和Iderror,那么PID控制器以该值为输入,该如何输出PWM波来驱动MOS管使得相电流达到理想状态呢?
我们自然而然可以想到,控制系统把这个差值二维向量额外作用在原来的三相电流二维向量上,得到的新二维向量就正好满足Iq=Iqideal和Id=Idideal,那么一切就大功告成了。那么现在问题又来了,这个差值二维向量在二维坐标系中的方向并不是某一个固定的方向,我们前面讲到ABC三相的上下桥臂导通一共可以产生8种电压矢量(注意定子电流矢量和电压矢量一般不重合,二者的夹角受定子电流矢量幅值影响,但可以通过PI控制器控制电压以控制电流矢量),但这8种方向矢量都是有固定的方向,如图3所示:

图3 SVPWM示意
这里为了讨论方便,一般会把得到的差值Iq和Id通过逆park变换重新转换成在FrameI_alpha-I_beta中表示的向量,直接求公式(3)的逆即可,如图3中左半部分所示。经过逆park变换得到的新的I_alpha和I_beta合成的合矢量在图3中中间部分表示,可以看到,单纯的U0~U7任一基向量无法合成上述的合矢量,但是这个合矢量相邻的两个基矢量却可以通过平行四边形合成该上述合矢量,如图3右半部分所示。
所以我们可以根据伏秒平衡,在一个控制周期内控制相邻基向量各自的导通时间,这样的一个控制周期单元整体上就相当于合成了一个上述的合矢量。在图3的例子中,合矢量位于六个扇区的第一扇区,那么我们就用U4和U6来合成合矢量,设合矢量与x轴正半轴的夹角为θ,则该合矢量与相邻的两个基向量满足以下关系:
$$ \frac{U_{合}}{\sin(\frac{2\pi}{3})}=\frac{U_{6}\frac{T_6}{T}}{\sin(\theta)}=\frac{U_{4}\frac{T_4}{T}}{\sin(\frac{\pi}{3})-\theta} \tag{5} $$
由上式(5)可以得到U4和U6的导通时间T4和T6:
$$ T_{4}=\frac{2}{\sqrt{3}}\sin(\frac{\pi}{3}-\theta)\frac{U_{合}}{U_4}T \tag{6} $$
$$ T_{6}=\frac{2}{\sqrt{3}}\sin(\theta)\frac{U_{合}}{U_6}T \tag{7} $$
得到了一个控制周期内的相邻矢量导通时间后,如何调整各个矢量的导通顺序呢?一般的做法是采用中心对齐模式,这样的做法是能够减少MOS开关的次数,如图4所示:

图4 PWM波时序图
可以看到,在上图4中,除了T4和T6,还有剩下的时间由T0和T7填充,即一个控制周期时间T=T4+T6+T0+T7。上图4中被分为8个列空间,每个列空间处于哪种状态由ABC三个半桥的导通状态决定,如第一个列空间ABC对应的二进制序列为[0 0 0],所以状态为U0,时间为0.5T0,第三个列空间ABC对应的二进制序列为[1 1 0],所以状态为U6,时间为0.5T6。
好,FOC理论讲解完毕,现在我们来通过STM32跑FOC算法来控制一个带编码器的三相无刷直流电机。
STM32直流无刷电机FOC设计
位置传感器:旋转编码器 MCU:STM32F405RGT6 功率MOS驱动芯片:DRV8301
我这里MCU使用的是STM32F405RGT6,位置传感器使用1000线正交编码器,功率MOS驱动芯片使用TI的DRV8301,省去了PWM死区等等的烦恼,另外DRV8301内部提供了一个电源BUCK,我们板子需要的5V电源可以直接从该芯片取,省下了一个降压到5V的AMS1117。
工欲善其事必先利其器,所以我们先从硬件电路板设计说起[...此处省略好多字...]

OK,硬件电路板讲完了,接下来就是STM32软件设计,这里使用的是HAL库,虽说我之前用的一直是标准库,刚上手是有点不习惯的,但是也就那么一回事,各种外设和GPIO都是配置好的,写好了就不用管,真正要做的是FOC算法的实现。我这里给出一张软硬件结构图:

图5 软硬件梗概图
其中有几个要注意的点:
- 由于我们的采样电阻采用了二电阻采样,所以电压采样应发生在BC半桥下桥臂导通的时候,为了精准采样,在STM32内部使用TIM1的通道4PWM触发采样ADC
- 二电阻采样得到BC相电流,A相电流通过KCL定律算得,除了采样电阻的电压外,我们还应该采集总的驱动电压用以计算式(6)和式(7)
下面说明一下代码里的一些主要的函数和设计:
PWM触发ADC采样
/** Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time. */
sConfig.Channel = ADC_CHANNEL_10;
sConfig.Rank = 1;
sConfig.SamplingTime = ADC_SAMPLETIME_3CYCLES;
/** Configures for the selected ADC injected channel its corresponding rank in the sequencer and its sample time */
sConfigInjected.InjectedChannel = ADC_CHANNEL_10;
sConfigInjected.InjectedRank = 1;
sConfigInjected.InjectedNbrOfConversion = 1;
sConfigInjected.InjectedSamplingTime = ADC_SAMPLETIME_15CYCLES;
sConfigInjected.ExternalTrigInjecConvEdge = ADC_EXTERNALTRIGINJECCONVEDGE_RISING;
// 设置TIM1 CC4上升沿触发采样
sConfigInjected.ExternalTrigInjecConv = ADC_EXTERNALTRIGINJECCONV_T1_CC4;
sConfigInjected.AutoInjectedConv = DISABLE;
sConfigInjected.InjectedDiscontinuousConvMode = DISABLE;
sConfigInjected.InjectedOffset = 0;
可以看到,ADC同时配置了规则转换模式和注入模式,注入模式采样由TIM1的CC4上升沿触发中断。然而由于硬件的限制(DRV8301只有两个采样口,要三相电阻采样的话需要额外采样),我们是双电阻采样,所以采样的时刻必须有BC两相下桥臂导通,所以,实际程序中只使能了注入模式转换完成中断。
在程序中,设置PWM输出为中央对齐模式(正如我们理论部分讲到的),设置TIM1的CCR4为1,ARR为3500,PWM模式为PWM2,显然我们的目的是使得控制周期一开始时TIM1的CH4通道就发生上升沿触发ADC采样,因为这个时候ABC三相对应为0矢量U0,这个时候BC的下桥臂都导通,由于电机的绕组为感性负载,电流不能突变,这个时候测得的电流可以近似为相电流。
htim->Instance->CCR4 = 1;
HAL_TIM_PWM_Start_IT(htim, TIM_CHANNEL_4);
ADC被TIM1 CH4上升沿触发中断后,便进入中断服务函数,在这个函数里,我们通过库函数获取BC相的AD值,转化为电流后便可以应用于FOC算法。
PWM中央对齐模式配置
前面讲到,为了减小MOS的开关次数,我们需要设置PWM为中央对齐模式,具体ARR,PWM配置如下:
// 设置PWM波输出为中央对齐模式
htim1.Init.CounterMode = TIM_COUNTERMODE_CENTERALIGNED3;
htim1.Init.Period = TIM_1_8_PERIOD_CLOCKS;
htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim1.Init.RepetitionCounter = TIM_1_8_RCR;
htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
// 设置为PWM2模式时,CNT<CCR时,输出低电平,否则输出高电平
sConfigOC.OCMode = TIM_OCMODE_PWM2;
sConfigOC.Pulse = 0;
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfigOC.OCNPolarity = TIM_OCNPOLARITY_HIGH;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
sConfigOC.OCIdleState = TIM_OCIDLESTATE_RESET;
sConfigOC.OCNIdleState = TIM_OCNIDLESTATE_RESET;
sBreakDeadTimeConfig.OffStateRunMode = TIM_OSSR_ENABLE;
sBreakDeadTimeConfig.OffStateIDLEMode = TIM_OSSI_ENABLE;
sBreakDeadTimeConfig.LockLevel = TIM_LOCKLEVEL_OFF;
sBreakDeadTimeConfig.DeadTime = TIM_1_8_DEADTIME_CLOCKS;
sBreakDeadTimeConfig.BreakState = TIM_BREAK_DISABLE;
sBreakDeadTimeConfig.BreakPolarity = TIM_BREAKPOLARITY_HIGH;
sBreakDeadTimeConfig.AutomaticOutput = TIM_AUTOMATICOUTPUT_DISABLE;
获得电机转子的相角
前面我们已经获得了BC相的相电流,为了实现FOC我们还需要一个电机转子的相角。在这里我么通过正交编码器实现相角的测量。由于现实中PMSM和BLDC电机都是具有多对磁极,编码器测得的角度为机械角,电角应该等于机械角乘以磁极对数然后对2π取余。在这之前,我们需要对准机械角度零位和电角度零位,在程序初始化时,使得电机转到0电角度(通过设定一个Iq实现,电角度一直为某一个值),记此刻的机械角度为offset,在之后的电角度计算中都要减去这个offset。
PI电流环控制器
现在实际相电流已知,电相角已知,那么我们就可以通过上文理论部分讲到的变换公式将实际相电流变换到framedq坐标系,然后与理想的Iq和Id比较得到差值,然后送入PI控制器转化为要合成的电压矢量,通过以下函数判断所在扇区,并输出各个电压基矢量应该持续的时间。
int SVM(float alpha, float beta, float* tA, float* tB, float* tC) {
int Sextant;
// 根据alpha和beta算出目标空间矢量在哪个扇区中
if (beta >= 0.0f) {
if (alpha >= 0.0f) {
//quadrant I
if (one_by_sqrt3 * beta > alpha)
Sextant = 2; //sextant v2-v3
else
Sextant = 1; //sextant v1-v2
} else {
//quadrant II
if (-one_by_sqrt3 * beta > alpha)
Sextant = 3; //sextant v3-v4
else
Sextant = 2; //sextant v2-v3
}
}
......
}
// 由平行四边形分解将目标空间矢量解耦到相邻的两个基向量上,由伏秒平衡算出每个基向量持续时间
switch (Sextant) {
// sextant v1-v2
case 1: {
// Vector on-times
float t1 = alpha - one_by_sqrt3 * beta;
float t2 = two_by_sqrt3 * beta;
// PWM timings
// 采用中央对齐模式
// 分别算出A、B、C三相的下桥臂导通时间,tA/2是因为基矢量0时间等于基矢量7时间
*tA = (1.0f - t1 - t2) * 0.5f;
*tB = *tA + t1;
*tC = *tB + t2;
break;
}
......
}
}
速度和位置环控制器
速度环一般使用PI控制器,位置环使用P控制器
实验效果展示
见本专栏下其它博文
BLDC有感FOC算法理论及其STM32软硬件实现的更多相关文章
- 有感FOC算法学习与实现总结
文章目录 基于STM32的有感FOC算法学习与实现总结 1 前言 2 FOC算法架构 3 坐标变换 3.1 Clark变换 3.2 Park变换 3.3 Park反变换 4 SVPWM 5 反馈部分 ...
- 【C#代码实战】群蚁算法理论与实践全攻略——旅行商等路径优化问题的新方法
若干年前读研的时候,学院有一个教授,专门做群蚁算法的,很厉害,偶尔了解了一点点.感觉也是生物智能的一个体现,和遗传算法.神经网络有异曲同工之妙.只不过当时没有实际需求学习,所以没去研究.最近有一个这样 ...
- FOC 算法基础之欧拉公式
文章目录 欧拉公式 几何意义 复数平面 动态过程 加法 FOC电压矢量的推导 总结 参考 FOC中电压矢量合成的推导,对于欧拉公式的几何意义做了一个全面的回顾. 欧拉公式 欧拉是一个天才,欧拉公式甚至 ...
- BLDC 无刷电机FOC驱动 STM32官方培训资料
STM32 PMSM FOC SDK V3.2 培训讲座一http://v.youku.com/v_show/id_XNTM2NjgxMjU2.html?from=s1.8-1-1.2STM32 PM ...
- KNN 算法-理论篇-如何给电影进行分类
公号:码农充电站pro 主页:https://codeshellme.github.io KNN 算法的全称是K-Nearest Neighbor,中文为K 近邻算法,它是基于距离的一种算法,简单有效 ...
- 强化学习中REIINFORCE算法和AC算法在算法理论和实际代码设计中的区别
背景就不介绍了,REINFORCE算法和AC算法是强化学习中基于策略这类的基础算法,这两个算法的算法描述(伪代码)参见Sutton的reinforcement introduction(2nd). A ...
- FM算法(一):算法理论
主要内容: 动机 FM算法模型 FM算法VS 其他算法 一.动机 在传统的线性模型如LR中,每个特征都是独立的,如果需要考虑特征与特征直接的交互作用,可能需要人工对特征进行交叉组合:非线性SVM可 ...
- 算法理论——PLA
全称 perceptron learning algrithm 用武之地 二值分类问题,资料线性可分 算法核心(以二维平面为例) 找到一条直线WTX=0,一边全为+1,另一边全为-1.找到了这条线(即 ...
- EM算法理论与推导
EM算法(Expectation-maximization),又称最大期望算法,是一种迭代算法,用于含有隐变量的概率模型参数的极大似然估计(或极大后验概率估计) 从定义可知,该算法是用来估计参数的,这 ...
随机推荐
- HBase 数据存储结构
在HBase中, 从逻辑上来讲数据大概就长这样: 单从图中的逻辑模型来看, HBase 和 MySQL 的区别就是: 将不同的列归属与同一个列族下 支持多版本数据 这看着感觉也没有那么太大的区别呀, ...
- APICloud Avm.js跨端框架的优势
AVM(Application-View-Model)是APICloud推出的一个跨端的高性能 JavaScript框架,更趋近于原生的编程体验,它提供简洁的模型来分离应用的用户界面.业务逻辑和数据模 ...
- 【Azure 云服务】Azure Cloud Service在发布新部署后遇见不能RDP(远程连接)到实例时如何处理?
Azure 云服务是PaaS 的一个示例. 与 Azure 应用服务一样,此技术设计用于支持可缩放.可靠且运营成本低廉的应用程序. 同样,应用服务托管在虚拟机 (VM) 上,Azure 云服务也是如此 ...
- shell脚本,mysql数据库的备份-2[mysqldump]
# 数据库IPIP=127.0.0.1# 数据库端口PORT=3306# 数据库用户USER=root# 数据库密码PASSWORD=****# 要备份的数据库TARGET_DB=database_n ...
- ICPC题目选讲
Traveling in the grid world 题目描述 有一个 \(n\times m\) 的格点图,两点之间走他们的连线,但是这条连线不能恰好覆盖其他整点.还要求相邻两步之间的连线不能斜率 ...
- golang 微服务以及相关web框架
golang 中国gocn golang Applicable to all database connection pools xorm是一个简单而强大的Go语言ORM库,通过它可以使数据库操作非常 ...
- Java并发编程之队列
Deque(双端队列) 子接口BlockingDeque,实现类如下: ArrayDeque:大下可变的数组双端队列,不允许插入null LinkedList:大小可变的链表双端队列,允许插入null ...
- vue中常见的问题以及解决方法
有一些问题不限于 Vue,还适应于其他类型的 SPA 项目. 1. 页面权限控制和登陆验证 页面权限控制 页面权限控制是什么意思呢? 就是一个网站有不同的角色,比如管理员和普通用户,要求不同的角色能访 ...
- 图解双链表(Java实现)
原创公众号:bigsai 文章已收录在 全网都在关注的数据结构与算法学习仓库 前言 前面有很详细的讲过线性表(顺序表和链表),当时讲的链表以但链表为主,但实际上在实际应用中双链表的应用多一些就比如Li ...
- 微信小程序--简约风博客小程序(基于云开发 - 全开源)
微信小程序--简约风博客小程序(基于云开发 - 全开源) 项目启动纯属突发奇想,想看看博客小程序,例如wehalo博客小程序,但是感觉自建平台还要浪费自己的服务器算力,还没有访问量,省省吧. 本着白嫖 ...