前言
学校电赛冬令营发了一个带传感器的无刷云台电机和一块simpleFOC驱动板,借此机会学习一下有感FOC的基本原理和使用。
前置知识
稚晖君这篇文章里讲得非常的简单易懂,推荐阅读:https://zhuanlan.zhihu.com/p/147659820
笔者在此对文章中的内容作简要的总结。
FOC简介
FOC全称Field-Oriented Control——磁场定向控制,FOC可以适用在交流感应电机及直流无刷电机,早期开发的目的为了高性能的电机应用,可以在整个频率范围内运转、电机零速时可以输出额定转矩、且可以快速的加减速。
正因为上述的各种优点,如今FOC被广泛地用于各种机器人系统的电机驱动中。
Clark变换和Park变换
Clark变换是将三相电机的电流、电压向量进行了正交化;
Park令两个正交的向量跟随转子进行旋转(对于这一步,个人理解是当向量随着转子旋转的时候,转子受到的力矩全部来源于两个向量中的q分量,因此只需控制q分量的大小就可以控制当前电机的转矩,并且可以通过减小d分量来减少其它的能量损耗)。
对于这两步变换的作用,笔者认为稚晖君做的总结非常到位:
所谓的“矢量控制”其实就是在做解耦,把相互耦合的三相磁链解耦为容易控制的交轴和直轴。整个过程就好比我们在做信号处理的时候,通过FFT把信号变换到频域进行处理之后再IFFT反变换回时域是一个道理
SVPWM技术
掌握了无刷电机输入相量的简化方法后,我们需要考虑如何进行输出,即如何将解耦后的交轴和直轴分量重新转化为电机的三个相量。
通过改变半桥开关管的通断,三相电机的三个绕组共可以产生六种不同方向的磁场。而要产生任意方向任意强度的磁场,我们需要将该方向所处扇区两边的两个矢量进行合成,这一步合成的操作就需要使用SVPWM(Space Vector Pulse Width Modulation——空间矢量脉宽调制)技术。
与我们熟悉的PWM技术不同的是,SVPWM从原本的一维标量变成了二维的矢量,它利用了电机绕组的电感特性对三个磁链(第三个是0)进行合成,实现了任意方向任意强度磁场的产生。具体实现方法见稚晖君文章。
机械角度与电角度
要实现以上操作,有一点我们不能忽略,就是我们需要知道转子当前的位置。在有感FOC中,转子的位置是通过角度传感器获取的,SimpleFOC支持四种不同类型的角度传感器。
在极对数为一的情况下,电机的机械角度和计算电压矢量时使用的角度是可以完全对应的,但是在实际的无刷电机中,极对数一般会更多:
这时候就出现了一个问题——电机的机械角度和电压矢量的角度是不相等的。从上图可以很直观地看出,在极对数为二的电机中,在电压矢量旋转了一圈后,电机实际上只转了半圈。所以要得到电角度,需要将传感器测得的机械角度进行一些换算,公式非常简单:电角度=机械角度*极对数
程序分析
SVPWM
SimpleFOC代码最核心的部分,实际上是SVPWM的实现,因此要将SimpleFOC移植至不同平台,基本上就是移植这一部分的代码。
float Uout;
if(Ud){ // 设置了不为零的d分量的情况,如果是带电流环的时候d分量可能会有输出
Uout = _sqrt(Ud*Ud + Uq*Uq) / driver->voltage_limit;//计算矢量的大小
angle_el = _normalizeAngle(angle_el + atan2(Uq, Ud));//计算矢量的方向
}else{//d分量为零的情况
Uout = Uq / driver->voltage_limit;
angle_el = _normalizeAngle(angle_el + _PI_2);//将转子角度归一化至0到2pi之间
}
sector = floor(angle_el / _PI_3) + 1;//求当前所处扇区
//根据SVPWM公式计算时间
float T1 = _SQRT3*_sin(sector*_PI_3 - angle_el) * Uout;
float T2 = _SQRT3*_sin(angle_el - (sector-1.0f)*_PI_3) * Uout;
float T0 = 0; //开发者说在供电电压比较低的时候可以不使用中心对齐,把零矢量的时间设为零
if (modulation_centered) {
T0 = 1 - T1 - T2; //当使用PWM中心对齐模式输出时计算零矢量的持续时间
}
// 根据转子所在扇区计算三个相位的时间(七段式SVPWM)
float Ta,Tb,Tc;
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;
}
//计算相电压
Ua = Ta*driver->voltage_limit;
Ub = Tb*driver->voltage_limit;
Uc = Tc*driver->voltage_limit;
break;
}
//输出
driver->setPwm(Ua, Ub, Uc);
零点检测
为了实现机械角度和电角度零点的对齐,我们需要先进行零点位置的检测。
setPhaseVoltage(voltage_sensor_align, 0, _3PI_2);//产生电角度为3π/2的磁场,把转子拖过去
_delay(700);//等待转子到位
//读取机械角度
sensor->update();
//根据极对数和机械角度计算出电角度的零点
zero_electric_angle = 0;
zero_electric_angle = electricalAngle();
//zero_electric_angle = _normalizeAngle(_electricalAngle(sensor_direction*sensor->getAngle(), pole_pairs));
_delay(20);
// stop everything
setPhaseVoltage(0, 0, 0);
_delay(200);
移植注意事项
1.配置PWM时记得设为中心对齐,并且注意每个通道占空比和电压保持时间的关系(笔者写反了一次),设置正确后三个通道产生的波形大概长这样:
其实认真观察可以看到占空比的变化就是一个马鞍波
2.程序中的所有角度量都是弧度制,范围是0~2π。对于使用I2C协议的磁编码器,SimpleFOC库中有AS5600和AS5048两款芯片的驱动代码,直接搬过来用即可。
3.注意传感器角度和电压矢量旋转的方向要一致。SimpleFOC也提供了自动检测传感器安装方向和极对数的程序,笔者移植的时候偷懒直接写死了。
if(!_isset(sensor_direction)){
// find natural direction
// move one electrical revolution forward
for (int i = 0; i <=500; i++ ) {
float angle = _3PI_2 + _2PI * i / 500.0f;
setPhaseVoltage(voltage_sensor_align, 0, angle);
sensor->update();
_delay(2);
}
// take and angle in the middle
sensor->update();
float mid_angle = sensor->getAngle();
// move one electrical revolution backwards
for (int i = 500; i >=0; i-- ) {
float angle = _3PI_2 + _2PI * i / 500.0f ;
setPhaseVoltage(voltage_sensor_align, 0, angle);
sensor->update();
_delay(2);
}
sensor->update();
float end_angle = sensor->getAngle();
setPhaseVoltage(0, 0, 0);
_delay(200);
// determine the direction the sensor moved
if (mid_angle == end_angle) {
SIMPLEFOC_DEBUG("MOT: Failed to notice movement");
return 0; // failed calibration
} else if (mid_angle < end_angle) {
SIMPLEFOC_DEBUG("MOT: sensor_direction==CCW");
sensor_direction = Direction::CCW;
} else{
SIMPLEFOC_DEBUG("MOT: sensor_direction==CW");
sensor_direction = Direction::CW;
}
// check pole pair number
float moved = fabs(mid_angle - end_angle);
if( fabs(moved*pole_pairs - _2PI) > 0.5f ) { // 0.5f is arbitrary number it can be lower or higher!
SIMPLEFOC_DEBUG("MOT: PP check: fail - estimated pp: ", _2PI/moved);
} else
SIMPLEFOC_DEBUG("MOT: PP check: OK!");
} else SIMPLEFOC_DEBUG("MOT: Skip dir calib.");
总结
经过本次SimpleFOC的移植,笔者掌握了有感FOC的基本原理,并且利用FOC实现了一些如扭矩控制,位置控制的简单功能。可惜的是,手里这块驱动板是一块不带电流反馈的简易版驱动(SimpleFOC貌似也是不久前才增加的电流环功能),因此无法做到扭矩的精确控制,笔者计划之后自行设计一块能够进行电流闭环的驱动板。
附录
参考资料:
Arduino Simple Field Oriented Control (FOC) project
【自制FOC驱动器】深入浅出讲解FOC算法与SVPWM技术
关于矢量控制clarke变化2/3 和sqrt(2/3)的理解
《DMF407电机控制专题教程》第27章 FOC
1 条评论
膜拜大佬