目录
矢量控制原理
矢量控制框图
电流采样方式
电流在整个控制过程中的传递
采样关键点
三电阻
双电阻
单电阻
三者对比
坐标变换
dq轴电流的PI控制
启动方式
启动波形
脉冲注入
高频注入
Startup
预定位到指定角度
PulseInject_api
hfi_api
Speed loop
speed_angle
Protector
Phase_Over_Current_Check 相电流过流保护
Bus_Over_Current_Check 母线电流过流保护
Vbus_OverVoltage_Check 过压保护
Vbus_UnderVoltage_Check 欠压保护
LoseSpeed_Check 失速保护
ZeroSpeed_Check 零速保护
Stall_Check_Sensorless 堵转保护(无位置传感器)
Current_Sensor_Check 相电流中点检测保护
矢量控制原理
矢量控制框图
PMSM 的矢量控制也称为磁场定向控制(Field Oriented Control,FOC) 。在 FOC 中,电机的定子电流被分解为用于产生磁场的直流电流(励磁电流)与用于控制转矩的交轴电流(转矩电流),通过对转速和电流的双环控制实现电机的高性能运行。
PMSM 矢量控制算法中,除角度变量为 Q16(0~65535)格式外,其余算法中控制参数均为 Q15(-32768~32767)格式。
可以说矢量控制的前提就在三相电流的采样上,采样之后经过Clarke和Park变换,变成便于控制的D、Q轴坐标系。
所以三相电流采样是矢量控制的核心!
电流采样方式
电流在整个控制过程中的传递
采样关键点
采样的关键是需要在三相逆变器高端关闭,低端打开的情况下进行采样,这是整体的采样点。
因此,采样会存在窗口时间,因为ADC转换完成需要一定数量级的时间,也就是说,在ADC转换完成之前,桥低端是不能关闭的,在这里,双电阻和单电阻采样需要考虑窗口时间的限制,而三电阻采样则不存在窗口时间(PWM占空比接近100%),可以根据SVPWM当前所在象限,进行分类,只需要采集其中不受窗口时间限制的两相电流,然后根据 Ia+Ib+Ic=0,进行电流的重构。
所以,电流采样的核心在:硬件电路的设计与采样时机的把握。
三电阻
在三个桥臂的下臂MOS管基极串分流器再用运放放大。
这种方法主要是为了节省隔离器件成本。并且需要在固定时刻采样的值才有效。
什么时候采样合适?
如下图所示,在不同扇区需要采样的相电流,共同点是避免去采样PWM占空比接近100%的那一相电流。
怎么采样?
ST的电机库的做法,通过TIMER_CH4作为ADC采样的触发信号,而采样则可以通过修改TIM_CCR4寄存器去改变采样点,相当灵活的做法。
双电阻
三电阻可减小至两个 (母线电流检测不可少),缺少的一相由计算得出。
根据基尔霍夫电流定律,Ic=-Ia-Ib。
怎么采样?
双电阻采样无法避免窗口时间,所以需要限制最终PWM的占空比,为ADC转换预留足够的时间
单电阻
为进一步节省成本发展出直接在母线电阻上检测三相电流的方法。
这种方法需要分时采样,不同时刻的电流代表不同相的电流。
为了便于理解整个采样的过程,为了表示逆变器的开关管的状态,
Sa表示A相的上下管,同理Sb表示B相的上下管;
这里规定:
- Sa = 1表示上管导通,下管断开;
- Sa = 0表示下管导通,上管断开;
Sb和Sc以此类推;
Sa Sb Sc:100
Sa Sb Sc:110
SVPWM不同扇区做成对应表格就是:
开关状态 | AH | BH | CH | 电流 |
0 | 0 | 0 | 0 | 0 |
1 | 1 | 0 | 0 | IA |
2 | 1 | 1 | 0 | −IC |
3 | 0 | 1 | 0 | IB |
4 | 0 | 1 | 1 | −IA |
5 | 0 | 0 | 1 | IC |
6 | 1 | 0 | 1 | −IB |
7 | 1 | 1 | 1 | 0 |
因此,单电阻采样,需要在一个PWM周期内进行两次采样,具体如下图所示:
图中的SAL,SBL,SCL分别对应整流桥的下管,因此在一个周期内分别进行了Sample 1和Sample 2这两次采样,对照上表可以推出;
- Sample 1:采集了开关管状态为SAL SBL SCL:101==>SAH SBH SCH:010,此时采样电流为 IB;
- Sample 2:采集了开关管状态为SAL SBL SCL:100==>SAH SBH SCH:011,此时采样电流为 −IA;
搞懂原理了,那么什么时候去采?
三者对比
电流采样 | 成本 | 算法 |
单电阻 | 低 | 复杂 |
双电阻 | 适中 | 适中 |
三电阻 | 高 | 简单 |
参考网址:
FOC 电流采样方案对比(单电阻/双电阻/三电阻) - 小麦大叔 - 博客园
坐标变换
Clark变换:把ADC采集到的三相定子电流从三轴(相差120°)定子坐标系转化成两轴(相差90°)定子坐标系。
Park变换:把Iα与Iβ从两轴(相差90°)定子坐标系 转化成 两轴转子旋转坐标系(相对于转自来说是静止的)。
电流变化:
dq轴电流的PI控制
P就是比例,误差信号err*比例因子Kp,但P的影响会随着误差err逐渐接近于0而减小,导致系统始终存在一个微小的静态误差。
I(积分)就是用来消除静态误差的,I对静态误差进行连续的积分。随着时间的推移,静态误差不断的累积变大,再将这个累积误差*Ki,作为I的输出。
在FOC的算法里,我们可能会用到PI控制器的地方:
- 电流环的Q轴(控制转子的转矩)
- 电流环的D轴(控制转子磁通)
- 速度环(控制转速)
- 位置环(控制位置)
- 电流观测器PLL(转速及角度估算)
串联PI、并联PI、位置式、增量式PI等。
启动方式
I/F电流/频率、V/F电压/频率、HF、DIRECT
I/F电流/频率:根据永磁同步电机负载特性,给变频器设置合适的电流-频率比,使得电机输出转矩与不同转速下的负载相匹配,以达到较高的运行效率。I/F控制策略运行在速度开环、电流闭环的状态。
启动波形
绿色线-相电流、黄色线-估计的电角度
1是初始位置识别,脉冲注入法,无抖动启动。
2开环运行,IF与VF开环运行
3开环到闭环的切换
4闭环运行
脉冲注入
根据定子铁芯非线性饱和效应的特征,当向定子绕组施加一系列等宽的短时检测电压矢量,可通过母线响应电流的大小来辨别转子初始位置区间。
高频注入
高频注入算法作用:用于无传感模式静止及低速情况下电机的位置识别。
高频注入算法通过向定子线圈注入特定的电压载波信号,藉由电机d-g轴电感的差异特性,进而产生与转子角度误差相关的电流信号,再依此电流信号进行估测角度的修正以达到无感测启动之目的。
高频注入算法利用电机d-g轴电感的差异特性,适合凸极特性较好的内嵌式永磁同步电机(IPMSM)使用。(因为内嵌式的PMSM两个轴电感值差距较大,而表贴式两者相近)
其实没太明白它们的区别?
Startup
这个模块主要提供无感FOC开环启动的一些函数。
感觉启动方面特别的乱,捋一捋:
问题一:高频注入和vf,if这些启动方式的关系是啥?
问题二:FOC开环启动曲线,到底是个啥子?
预定位到指定角度
预定位到指定角度上 FOC的预定位不再是六步换相那样通电流,而设置iqRef。
问题是,开环阶段怎么能直接设置iqRef呢?
看不懂进行不下去了……感觉都是参数的互相传递。
PulseInject_api
脉冲注入的五种状态机,分别对应着不同的函数
而且发现只有PulseInject_api这个文件,却找不到PulseInject这个文件,但有PulseInject.h头文件。
同时在PulseInject_api里面有很多函数,应该是在PulseInject里面的,也没办法具体的观察。
/*!
* @brief Pre_positioning motor to an expected angle.
*预定位到指定角度上 FOC的预定位不再是六步换相那样通电流,而设置iqRef
* @param[in] focVarsCtrl: pointer to FOC_VARS_CTRL structure
* @param[in] startUp: pointer to START_UP structure
* @return none
*/
void Angle_Prepositioning_Process(FOC_VARS_CTRL *focVarsCtrl, START_UP *startUp)
{
static uint32_t s_timeBase = 0;
++s_timeBase;
if (s_timeBase <= startUp->pStCfg->alignTime)
{
startUp->pStCtrl->startUpElecTheta = startUp->pStCfg->alignAngle;
focVarsCtrl->elecAngle = startUp->pStCtrl->startUpElecTheta;
focVarsCtrl->idRef = 0;
if (s_timeBase <= startUp->pStCfg->slopeDuration1)
{
focVarsCtrl->iqRef = (startUp->pStCfg->curRamp1Init + Math_Mpy(s_timeBase, startUp->pStCtrl->curAddSlope1)) * pFocVarsCfg->motorDir;
//iqRef,q轴参考电流
}
else
{
focVarsCtrl->iqRef = startUp->pStCfg->alignCur * pFocVarsCfg->motorDir;
}
}
else
{
#if (defined MOTOR_POLES_OBTAIN)
startUp->pStCtrl->startUpFlag = 2;
#else
s_timeBase = 0;
startUp->pStCtrl->alignState = 1;
startUp->pStCtrl->startUpFlag = 3;
#endif
}
脉冲注入其实有两部份工作要做:1脉冲宽度的自识别(PulseInject_SelfLearn());2脉冲注入。
脉冲注入后,得到六向母线电流,根据电感的饱和特性,计算转子的初始位置。
PulseInject_DataHandle()该函数处理数据只需要总线电流,因此初始位置检查功能也适用于bldc控制。
void PulseInject_DataHandle(PULSE_INJECT_DATA *pulseInj);
hfi_api
和脉冲注入一样都是只有头文件,找不到源文件。
但通过头文件也可以简单了解到那些函数用于高频注入。
能了解到的就这么多……
Speed loop
speed_angle
这个模块主要提供速度斜坡与速度环的一些功能参数。
和BLDC里面的一些函数与配置很相似,但也有一些不同的点:
PI:Speedloop变量PI参数计算,它根据速度将速度环路PI参数分为三个部分,低速时使用低速PI,高速时使用高速PI,中间速度线性过渡。
/*!
* @brief Speedloop variable PI parameters calculate, it divides the speed loop PI parameters into three sections
according to the speed, low speed PI is used at low speed, high speed PI is used at high speed, and the
intermediate speed is linearly transitioned.
*Speedloop变量PI参数计算,它根据速度将速度环路PI参数分为三个部分,低速时使用低速PI,高速时使用高速PI,中间速度线性过渡。
* @param[in] freq: motor electrical frequency pu value in Q15
* @param[in] asrVarPI: pointer to ASR_SPEEDVARPI structure
* @param[in] asrPidCof: pointer to PID_REGULATOR_COF structure
* @return none
*/
void ASR_SpeedVarPICalc(int16_t freq, ASR_SPEEDVARPI *asrVarPI, PID_REGULATOR_COF *asrPidCof)
{
uint16_t s_absFre = 0;
s_absFre = Math_Qabs(freq);
if (s_absFre > asrVarPI->frepuH)
{
asrPidCof->kpPu = asrVarPI->kpH;
asrPidCof->kiPu = asrVarPI->kiH;
}
else if (s_absFre > asrVarPI->frepuL)
{
asrPidCof->kpPu = asrVarPI->kpL + Math_Mpy((s_absFre - asrVarPI->frepuL), asrVarPI->kpGain);
asrPidCof->kiPu = asrVarPI->kiL + Math_Mpy((s_absFre - asrVarPI->frepuL), asrVarPI->kiGain);
}
else
{
asrPidCof->kpPu = asrVarPI->kpL;
asrPidCof->kiPu = asrVarPI->kiL;
}
}
而且还提到了一个新的控制对象:S curve .
我推测应该是速度曲线的意思,有很多的函数是围绕它来写的。
Protector
电机保护策略功能函数,相比于BLDC控制多了很多功能。
Phase_Over_Current_Check 相电流过流保护
电机相电流超过设定阈值,且连续达到设定次数(防误报)时,上报相电流过流故障
/*!
* @brief Phase over current protection. When the amplitude of the phase current
* synthesis vector exceeds the threshold value, phase over current fault is reported.
*
* @param[in] focVars: pointer to FOC_VARS_CTRL structure
* @return status: fault diagnosis status, 0 OK; 1 fault; 2 unknown
*/
int16_t Phase_Over_Current_Check(FOC_VARS_CTRL *focVars)
{
static int16_t cnta = 0;
int16_t status = DIAGNOSTIC_OK;
uint16_t statusTemp = 0;
if (focVars->idq > MAX_IPHASE_THRESHOLD)
{
statusTemp |= 0x1;
if (cnta < IPHASE_DBC)
{
cnta++;
}
else
{
cnta = 0;
Fault_Report(OVER_CURRENT_PHASE_FAILURE);
}
}
else
{
cnta = 0;
}
if (statusTemp != 0)
{
status = DIAGNOSTIC_FAIL;
}
return status;
}
Bus_Over_Current_Check 母线电流过流保护
Vbus_OverVoltage_Check 过压保护
Vbus_UnderVoltage_Check 欠压保护
LoseSpeed_Check 失速保护
失速,指的就是速度控制不住了,忽然加速
/*!
* @brief Motor Lose Speed protection.
*
* @param[in] speed: speed feedback pu value
* @return none
*/
void LoseSpeed_Check(int16_t speed)
{
static int16_t oldSpeed = 0;
static uint16_t cntLoseSpeed = 0;
if (g_startUpCtrl.startUpFlag > 3)
{
if ((Math_Qabs(speed) > LOSE_SPEED_THRESHOLD) || ((int16_t)Math_Mpy(pFocVarsCfg->motorDir, speed) < 0) || (Math_Qabs(speed - oldSpeed) > LOSE_SPEED_ERR_THRESHOLD))
//转速超过设定阈值或当前转速与上一次转速差值大于设定阈值,且连续达到 10 次时上报失速故障(LOSE_SPEED_FAILURE)。
{
if (cntLoseSpeed < LOSE_SPEED_DBC)
{
cntLoseSpeed++;
}
else
{
cntLoseSpeed = 0;
oldSpeed = 0;
g_protector.loseSpeedProFlag = 1;
#if (defined STARTUP_CHECK_RESTART_ENABLE)
#else
Fault_Report(LOSE_SPEED_FAILURE);//上报失速错误
#endif
}
}
else
{
cntLoseSpeed = 0;
// Fault_Clear(LOSE_SPEED_FAILURE);
}
oldSpeed = speed;
}
}
ZeroSpeed_Check 零速保护
/*!
* @brief Motor Zero Speed protection.
*转速低于设定阈值,且连续达到 2 次时上报零速故障(ZERO_SPEED_FAILURE)。
* @param[in] none
* @return none
*/
void ZeroSpeed_Check(int16_t speed)
{
static uint16_t s_zeroSpeedCnt = 0;
if (g_startUpCtrl.startUpFlag >= 6)
{
if (Math_Qabs(speed) < ZERO_SPEED_THRESHOLD)
{
if (s_zeroSpeedCnt < ZERO_SPEED_DBC)
{
s_zeroSpeedCnt++;
}
else
{
s_zeroSpeedCnt = 0;
g_protector.zeroSpeedProFlag = 1;
#if (defined STARTUP_CHECK_RESTART_ENABLE)
#else
Fault_Report(ZERO_SPEED_FAILURE);
#endif
}
}
else
{
s_zeroSpeedCnt = 0;
// Fault_Clear(ZERO_SPEED_FAILURE);
}
}
}
Stall_Check_Sensorless 堵转保护(无位置传感器)
可通过两种方式判断无传感模式下发生堵转情况:
1. 方差与平均转速平方的比例超过设定阈值。
2. 反电动势幅值与当前估算转速的比例超过设定阈值。
/*!
* @brief Motor stall protection detection for position sensorless application.
*
* @param[in] pHandle: pointer to SPEED_RAMP_HANDLE structure
* @param[in] smc: pointer to SMC_ESTIMATOR structure
* @return status: fault diagnosis status, 0 OK; 1 fault; 2 unknown
*/
int16_t Stall_Check_Sensorless(SPEED_RAMP_HANDLE *pHandle, SMC_ESTIMATOR *smc)
{
int16_t status = DIAGNOSTIC_OK;
static int16_t bemfReliableCnt = 0;
int32_t avgSquareSpeed = 0;
int32_t bemfReliableThreshold = 0;
#if ((defined FLUX_OBSERVE) || (defined MRAS_OBSERVE))
int32_t estBemfSq = 0;
#endif
if ((g_startUpCtrl.startUpFlag >= 3) && (g_startUpCtrl.startUpFlag < 6))
{
//ASR_AvgSpeedCalc(pHandle);
avgSquareSpeed = (int32_t)(pHandle->avgSpeedFbk) * (int32_t)(pHandle->avgSpeedFbk);
bemfReliableThreshold = avgSquareSpeed;
g_bemfReliableThreshold = bemfReliableThreshold;
#if (defined SMC_OBSERVE)
Smc_GetBemfSq(smc);
// g_estBemfDebug = (smc->estBemfSq * BEMF_CONSISTENT_THRESHOLD) >> 4;
if ((((smc->estBemfSq * BEMF_CONSISTENT_THRESHOLD) >> 4) < bemfReliableThreshold))
{
if (++bemfReliableCnt >= BEMF_DBC)
{
status = DIAGNOSTIC_FAIL;
bemfReliableCnt = 0;
g_protector.stallProFlag = 1;
#if (defined STARTUP_CHECK_RESTART_ENABLE)
#else
Fault_Report(MOTOR_STALL_FAILURE);
#endif
}
}
else
{
bemfReliableCnt=0;
}
#elif (defined FLUX_OBSERVE)//磁链观测
estBemfSq = (g_fluxObserver.emfAlpha * g_fluxObserver.emfAlpha + g_fluxObserver.emfBeta * g_fluxObserver.emfBeta) >> 8;
g_estBemfDebug = estBemfSq * BEMF_CONSISTENT_THRESHOLD;
if (((estBemfSq * BEMF_CONSISTENT_THRESHOLD) < bemfReliableThreshold) && (pHandle->varianceSpeed < 500000))
//保护方差与反电动势
{
if (++bemfReliableCnt >= BEMF_DBC)
{
status = DIAGNOSTIC_FAIL;
bemfReliableCnt = 0;
g_protector.stallProFlag = 1;
#if (defined STARTUP_CHECK_RESTART_ENABLE)
#else
Fault_Report(MOTOR_STALL_FAILURE);
#endif
}
}
else
{
bemfReliableCnt = 0;
}
#elif (defined MRAS_OBSERVE)
estBemfSq = (g_mrasObserver.vdFilter * g_mrasObserver.vdFilter + g_mrasObserver.vqFilter * g_mrasObserver.vqFilter) >> 8;
// g_estBemfDebug = estBemfSq * BEMF_CONSISTENT_THRESHOLD;
if (((estBemfSq * BEMF_CONSISTENT_THRESHOLD) < bemfReliableThreshold) && (pHandle->varianceSpeed < 500000))
{
if (++bemfReliableCnt >= BEMF_DBC)
{
status = DIAGNOSTIC_FAIL;
bemfReliableCnt = 0;
g_protector.stallProFlag = 1;
#if (defined STARTUP_CHECK_RESTART_ENABLE)
#else
Fault_Report(MOTOR_STALL_FAILURE);
#endif
}
}
else
{
bemfReliableCnt = 0;
}
#endif
}
else
{
// g_estBemfDebug = 0;
// g_bemfReliableThreshold = 0;
}
return status;
}
Current_Sensor_Check 相电流中点检测保护
上电初始化阶段调用,获取三相电流中点偏移大小,超过 5%中点偏移电压
#if (CUR_SAMPLE_MODE == THREE_SHUNT_SAMPLE) //电流传感器采样零点漂移检查,电流采样ADC零漂移值超过限制,这是故障。
/* 2048 *5% = 102 */
if (Math_Qabs(adcSampleReal->icOffset - 2048) > 102)
{
Fault_Report(PHASEC_CURRENT_MIDPOINT_FAILURE);
}
else
{
Fault_Clear(PHASEC_CURRENT_MIDPOINT_FAILURE);
}
#else
#endif