本帖最后由 jixulifu2 于 2024-8-18 23:36 编辑
Field-Oriented Control (FOC) —— 磁场定向控制
磁场定向控制(Field-Oriented Control,简称FOC)是一种先进的电机控制策略,主要用于交流电机(如感应电机和永磁同步电机)的高性能控制。FOC的主要目的是通过精确控制电机的磁场和转矩,来实现电机的高效和精确控制。
FOC的工作原理
FOC的核心思想是将电机的旋转坐标系(dq坐标系)与电机的磁场方向对齐,从而将电机的多相(通常是三相)电流分解成两个独立的分量:励磁电流(d轴电流)和转矩电流(q轴电流)。这种分解使得电机的控制变得更加直观和简单,因为可以通过单独控制这两个分量来分别控制电机的磁场和转矩。
下面是FOC经典框图
HXS320F280025C是昊芯浮点DSC平台上的最新产品,基于自主研发的H28x内核,32位浮点RISC-V DSP架构,支持三角函数数学单元和CRC扩展指令集,完美适配FOC。该片增加了可配置逻辑模块(CLB),用户可添加自定义逻辑:支持多种通信端口,增强型控制外设,可满足电机驱动/控制、光伏逆变器和数字电源等行业的产品布局;基于FPU浮点处理单元,支持FOC算法、有感/无感角度分析、多电平控制、复杂电力电子拓扑算法以及宽带半导体驱动等应用。
今天我们手撸一个简易FOC开环框架:
思路解析解析:
刨去三环,编码器和3相全桥电路,我们要实现的是输入Ud.Uq,angle,输出ABC桥控制信号,也就是占空比信息。
Ud、Uq:通常称为 dq 坐标系。这个坐标系包括 d 轴(励磁轴)和 q 轴(转矩轴)。Ud 表示 d 轴上的电压分量,Uq 是施加在 q 轴上的电压分量,用于控制转矩电流 Iq。转矩电流 Iq 与电机产生的转矩有关,通过控制 Uq,可以调整电机的转矩。这两个值是我们人为设定的,所以只需要在后面代码中传入参数计算就可以。
Ud、Uq与Uαβ的关系:无刷电机通常由UVW三相组成,为了方便计算,需要把三相中每一项的分量隐射到一个直角坐标系Uα,Uβ中,使用我们最常见的三角函数解一下,如下图示意
公式:
Uα=Udcos(θ)−Uqsin(θ)
Uβ=Udsin(θ)+Uqcos(θ)
- Uα 和 Uβ 分别是 αβ 坐标系下的电压分量。
- Ud和 Uq分别是 dq 坐标系下的电压分量。
- θ 是电机定子磁场角度。
求出Uα和Uβ后传入SVPWM模块
SVPWM介绍:SVPWM的核心是将电机所需的电压表示为一系列空间矢量,并通过选择适当的电压矢量来合成期望的输出电压。这种方法能够更高效地利用逆变器的电压和电流容量,进而提高电机驱动系统的整体效率。
放两张教材里的图吧,详细介绍的话可以单独开一个篇章,有兴趣的小伙伴自行查看吧
接下来是Clark变换:
主要用于将三相静止坐标系下的信号转换到两相静止坐标系下,理解了Clark也就理解了反Park
- 减少维度:从三个变量减少到两个变量,简化了控制系统的设计。
- 保持功率不变:Clark变换是等功率变换,即变换前后系统的总功率保持不变。
- 便于控制:两相坐标系下的信号更容易被控制算法处理,特别是对于矢量控制技术而言
接下来就愉快的手撸代码吧:
延续上一期的工程,新建一个FOC.c和FOC.h
#include "FOC.h"
#include <math.h>
#define PWM_Period
#define _PI 3.14159265359
#define _PI_2 1.57079632679
#define _PI_3 1.0471975512
#define _2PI 6.28318530718
#define _3PI_2 4.71238898038
#define _PI_6 0.52359877559
#define _SQRT3 1.73205080757
float voltage_power_supply;
// 设置PWM
// 输入参数:
// - Uq: q轴电压分量
// - Ud: d轴电压分量
// - angle_el: 电角度
void SetSVPWM(float Uq, float Ud, float angle_el)
{
float Uref;
float U_alpha,U_beta;
float T0,T1,T2;
float Ta,Tb,Tc;
int sector;
//反Park
U_alpha=Ud*cos(angle_el)-Uq*sin(angle_el);
U_beta=Ud*sin(angle_el)+Uq*cos(angle_el);
//计算参考电压矢量的幅值
Uref=_sqrt(U_alpha*U_alpha + U_beta*U_beta) / voltage_power_supply;
//六边形的内切圆(SVPWM最大不失真旋转电压矢量赋值)根号3/3
if(Uref> 0.577)Uref= 0.577;
if(Uref<-0.577)Uref=-0.577;
//判断参考电压矢量所在扇区:
angle_el = Angle_deal(angle_el+_PI_2);
sector = (angle_el / _PI_3) + 1;
//计算两个相邻电压矢量作用时间
T1 = _SQRT3*sin(sector*_PI_3 - angle_el) * Uref;
T2 = _SQRT3*sin(angle_el - (sector-1.0)*_PI_3) * Uref;
T0 = 1 - T1 - T2;
switch(sector)
{
case 1:
Ta = T1 + T2 + T0/2;
Tb = T2 + T0/2;
Tc = T0/2;
break;
case 2:
Ta = T1 + T0/2;
Tb = T1 + T2 + T0/2;
Tc = T0/2;
break;
case 3:
Ta = T0/2;
Tb = T1 + T2 + T0/2;
Tc = T2 + T0/2;
break;
case 4:
Ta = T0/2;
Tb = T1+ T0/2;
Tc = T1 + T2 + T0/2;
break;
case 5:
Ta = T2 + T0/2;
Tb = T0/2;
Tc = T1 + T2 + T0/2;
break;
case 6:
Ta = T1 + T2 + T0/2;
Tb = T0/2;
Tc = T1 + T0/2;
break;
default: // 错误状态
Ta = 0;
Tb = 0;
Tc = 0;
}
//输出PWM,配置占空比
EPWM_setCounterCompareValue(epwm1Info.epwmModule, EPWM_COUNTER_COMPARE_A, Ta*PWM_Period);
EPWM_setCounterCompareValue(epwm2Info.epwmModule, EPWM_COUNTER_COMPARE_A, Tb*PWM_Period);
EPWM_setCounterCompareValue(epwm3Info.epwmModule, EPWM_COUNTER_COMPARE_A, Tc*PWM_Period);
}
//将输入的角度 angle 归一化到 [0, 2π) 区间内。
float Angle_deal(float angle)
{
float a = fmod(angle, _2PI);
return a >= 0 ? a : (a + _2PI);
}
代码中并没有角度获得方式,可以在主循环while(1)里模拟一个自增的开环角度,当然也可以来自编码器、霍尔或者无传感器算法估算的转子位置。
受限于手头并没有实物,我们就不做演示了。