FOC中的Clarke变换_TI和ST电机控制库的源码实现
FOC中的PARK变换_TI和ST电机控制库的源码实现
FOC中的反PARK变换_TI和ST电机控制库的源码实现
无刷电机的控制不可脱离转子的位置信息。知道转子的位置反馈是对无刷电机控制的前提。通常分为有感和无感。
有感:即为有位置传感器,像霍尔、光电增量式编码器、旋转变压器、磁编等等。
无感:则是电机不需要位置传感器,通过电机自身的信息计算或则估计转子的位置。
基于模型法的位置/转速观测器有三部分,反电动势、磁链信息观测、位置误差信号解耦位置/转速观测。反电动势或磁链信息观测方法的不同,模型有自适应法、扩展卡尔曼滤波器法、磁链观测法、状态观测法、滑膜观测法。
反电动势过零检测法应用广泛,实现简单、技术成熟。
忽略电动机电枢反应,无刷直流电动机在稳态运行过程中,通过检测关断相的反电动势过零点获得转子的位置信号,进行对逆变器开关导通顺序切换,控制电机运动。
电机静止和低速时,反电动势很小或者为0,无法获取转子位置信号,电机低速时性能较差,启动时需要开环启动。
反电动势零点 | 变化趋势 |
---|---|
C- | C相反电动势又正到负 |
B+ | B相反电动势又负到正 |
A- | A相反电动势又正到负 |
C+ | C相反电动势又负到正 |
B- | B相反电动势又正到负 |
A+ | A相反电动势又负到正 |
可以直接得到电机端的电压方程:
{ U A = R i A + L d i A d t + e A + U N U B = R i B + L d i B d t + e B + U N U C = R i C + L d i C d t + e C + U N ① \left\{ \begin{array}{l} U_A=Ri_A+L\frac{di_A}{dt}+e_A+U_N\\ U_B=Ri_B+L\frac{di_B}{dt}+e_B+U_N\\ U_C=Ri_C+L\frac{di_C}{dt}+e_C+U_N\\ \end{array} \right. ① ⎩⎨⎧UA=RiA+LdtdiA+eA+UNUB=RiB+LdtdiB+eB+UNUC=RiC+LdtdiC+eC+UN①
假设c相不导通时有:
{ U C = e C + U N i A + i B = 0 e A + e B = 0 ② \left\{ \begin{array}{l} U_C=e_C+U_N\\ i_A+i_B=0\\ e_A+e_B=0\\ \end{array} \right. ② ⎩⎨⎧UC=eC+UNiA+iB=0eA+eB=0②
可以得到反电动势:
e C = U C − 1 2 ( U A + U B ) e_C=U_C-\frac{1}{2}\left( U_A+U_B \right) eC=UC−21(UA+UB)
同理可以得到A、B相的的反电动势
{ e A = U A − 1 2 ( U C + U B ) e B = U B − 1 2 ( U A + U C ) e C = U C − 1 2 ( U A + U B ) \left\{ \begin{array}{l} e_A=U_A-\frac{1}{2}\left( U_C+U_B \right)\\ e_B=U_B-\frac{1}{2}\left( U_A+U_C \right)\\ e_C=U_C-\frac{1}{2}\left( U_A+U_B \right)\\ \end{array} \right. ⎩⎨⎧eA=UA−21(UC+UB)eB=UB−21(UA+UC)eC=UC−21(UA+UB)
通过这三个方差去判断反电动势的正负变化得到零点。但是还可以优化一下,方便编程。
①和②重新组合一下合一得到:
U A + U B + U C = e C + 3 U N U_A+U_B+U_C=e_C+3U_N UA+UB+UC=eC+3UN
当Ec=0时候满足:
U A + U B + U C = 3 U N ③ U_A+U_B+U_C=3U_N ③ UA+UB+UC=3UN③
可以得到:
{ 3 e A = 3 U A − 3 U N 3 e B = 3 U B − 3 U N 3 e C = 3 U C − 3 U N \left\{ \begin{array}{l} 3e_A=3U_A-3U_N\\ 3e_B=3U_B-3U_N\\ 3e_C=3U_C-3U_N\\ \end{array} \right. ⎩⎨⎧3eA=3UA−3UN3eB=3UB−3UN3eC=3UC−3UN
**因为在在相位换向的瞬间,由于直流电平或电源板的寄生电感和电容,可能会出现高dV /dt和dI/dt毛刺。可能会对计算的中性点电压有错误。**通过丢弃前几次扫描来克服这问题。(比如从负到正,先一直为负,检测到4次为正时,标志到了过零点检测,算是滤波)
在代码中,这是通过名为“NOISE_WIN”的功能实现的。持续时间取决于电源开关,电源板设计,相电感和驱动的直流电。此参数取决于系统,并且在电动机的低速范围内设置为较大的值。随着速度的增加,由于Bemf零交叉点也以更高的速度变得越来越近,所以逐渐降低了该持续时间。
在有效的传感控制中,Bemf的零交叉点从相位换向时刻偏移了30º。因此,在借助六个过零事件来运行无传感器BLDC电动机之前,有必要计算与该30º延迟角相对应的时间延迟获得精确的换向点。这是通过实现位置插值功能来实现的。
相应的时间延迟以采样时间段的数量表示,并存储在变量CmtnDelay中。
Time delay = CmtnDelay .Ts = T(a/360) = VirtualTimer.Ts(a/360) = VirtualTimer . Ts/12
其中,Ts是采样时间段,VirtualTimer是计时器,用于对转子上一圈旋转期间的采样周期数进行计数。
typedef struct { Uint32 CmtnTrig; // Output: Commutation trigger output (0 or 0x00007FFF)
_iq Va; // Input: Motor phase a voltage referenced to GND (pu)
_iq Vb; // Input: Motor phase b voltage referenced to GND (pu)
_iq Vc; // Input: Motor phase c voltage referenced to GND (pu)
_iq Neutral; // Variable: 3*Motor netural voltage (pu)
Uint32 RevPeriod; // Variable: revolution time counter (Q0)
Uint32 ZcTrig; // Variable: Zero-Crossing trig flag (0 or 0x00007FFF)
Uint32 CmtnPointer; // Input: Commutation state pointer input (0,1,2,3,4,5)
_iq DebugBemf; // Variable: 3*Back EMF = 3*(vx=vn), x=a,b,c (pu)
Uint32 NoiseWindowCounter;// Variable: Noise windows counter (Q0)
Uint32 Delay30DoneFlag; // Variable: 30 Deg delay flag (0 or 0x0000000F)
Uint32 NewTimeStamp; // Variable: Time stamp (Q0)
Uint32 OldTimeStamp; // History: Previous time stamp (Q0)
Uint32 VirtualTimer; // Input: Virtual timer (Q0)
Uint32 CmtnDelay; // Variable: Time delay in terms of number of sampling time periods (Q0)
Uint32 DelayTaskPointer; // Variable: Delay task pointer, see note below (0 or 1)
Uint32 NoiseWindowMax; // Variable: Maximum noise windows counter (Q0)
Uint32 CmtnDelayCounter; // Variable: Time delay counter (Q0)
Uint32 NWDelta; // Variable: Noise windows delta (Q0)
Uint32 NWDelayThres; // Variable: Noise windows dynamic threshold (Q0)
int32 GPR1_COM_TRIG; // Variable: Division reminder
int32 Tmp; // Variable: Temp. variable
} CMTN;
/*
Note:
DelayTaskPointer = 0, branch for #COUNT_DWN
DelayTaskPointer = 1, branch for #CHK_TRIGGER
*/
/*-----------------------------------------------------------------------------
Default initalizer for the CMTN object.
-----------------------------------------------------------------------------*/
#define CMTN_DEFAULTS { 0, \
0, \
0, \
0, \
0, \
0, \
0, \
0, \
0, \
0, \
0, \
0, \
0, \
0, \
0, \
1, \
0, \
0, \
0, \
0, \
0, \
0, \
}
/*----------------------------------------------------------------------------------------------
CMTN_TRIG Macro Definition
----------------------------------------------------------------------------------------------*/
#define CMTN_TRIG_MACRO(v) \
\
/* Always clear flags on entry*/ \
v.CmtnTrig = 0; \
v.ZcTrig = 0; \
\
/* Neutral voltage calculation (3*motor Neutral voltage)*/ \
v.Neutral = v.Va + v.Vb + v.Vc; \
\
/* Commutation State table Tasks*/ \
/* State s1: current flows to motor windings from phase A->B, de-energized phase = C*/ \
if (v.CmtnPointer == 0) \
{ \
v.DebugBemf = _IQmpy(_IQ(3),v.Vc) - v.Neutral; \
if (v.DebugBemf > 0) \
v.NoiseWindowCounter = 0; \
else /* Zero crossing Noise window processing*/ \
NOISE_WINDOW_CNT_MACRO(v); \
} /* else if-end: State s1*/ \
\
/* State s2: current flows to motor windings from phase A->C, de-energized phase = B*/ \
else if (v.CmtnPointer == 1) \
{ \
v.DebugBemf = _IQmpy(_IQ(3),v.Vb) - v.Neutral; \
if (v.DebugBemf < 0) \
v.NoiseWindowCounter = 0; \
else /* Zero crossing Noise window processing*/ \
NOISE_WINDOW_CNT_MACRO(v); \
} /* else if-end: State s2*/ \
\
/* State s3: current flows to motor windings from phase B->C, de-energized phase = A*/ \
else if (v.CmtnPointer == 2) \
{ \
v.DebugBemf = _IQmpy(_IQ(3),v.Va) - v.Neutral; \
if (v.DebugBemf > 0) \
v.NoiseWindowCounter = 0; \
else /* Zero crossing Noise window processing*/ \
NOISE_WINDOW_CNT_MACRO(v); \
} /* else if-end: State s3*/ \
\
/* State s4: current flows to motor windings from phase B->A, de-energized phase = C*/ \
else if (v.CmtnPointer == 3) \
{ \
v.DebugBemf = _IQmpy(_IQ(3),v.Vc) - v.Neutral; \
if (v.DebugBemf < 0) \
v.NoiseWindowCounter = 0; \
else /* Zero crossing Noise window processing*/ \
NOISE_WINDOW_CNT_MACRO(v); \
} /* else if-end: State s4*/ \
\
/* State s5: current flows to motor windings from phase C->A, de-energized phase = B*/ \
else if (v.CmtnPointer == 4) \
{
/*计算三十度延迟的flag置位*/ \
v.Delay30DoneFlag = 0; /* clear flag for delay calc in State 5*/ \
\
v.DebugBemf = _IQmpy(_IQ(3),v.Vb) - v.Neutral; \
if (v.DebugBemf > 0) \
v.NoiseWindowCounter = 0; \
else /* Zero crossing Noise window processing */ \
NOISE_WINDOW_CNT_MACRO(v); \
} /* else if-end: State s5 */ \
\
/* State s6: current flows to motor windings from phase C->B, de-energized phase = A*/ \
else if (v.CmtnPointer == 5) \
{ \
v.DebugBemf = _IQmpy(_IQ(3),v.Va) - v.Neutral; \
if (v.DebugBemf < 0) \
v.NoiseWindowCounter = 0; \
else /* Zero crossing Noise window processing*/ \
NOISE_WINDOW_CNT_MACRO(v); \
/*计算三十度延迟*/
DELAY_30DEG_MACRO(v); \
} /* else if-end: State s6*/ \
\
/* Zero crossing to Commutation trigger delay*/ \
v.CmtnTrig = 0; /* Always clear flag on entry */ \
/* 得到过零点后,进行30度延迟 */ \
if (v.DelayTaskPointer > 0) /* v.DelayTaskPointer = 1 for #CHK_TRIGGER*/ \
{ \
if (v.ZcTrig != 0) \
{ \
/* Substract NoiseWindowMax to compensate the advanced zero-crossing validation point */\
v.CmtnDelayCounter = v.CmtnDelay - v.NoiseWindowMax; \
v.DelayTaskPointer = 0; /* v.DelayTaskPointer = 0 for #COUNT_DWN*/ \
} \
} \
else /* v.DelayTaskPointer = 0 for #COUNT_DWN */ \
{
/* 计数减到0,得到换向点*/ \
v.CmtnDelayCounter -= 1; \
if (v.CmtnDelayCounter == 0) \
{ \
v.CmtnTrig = 0x00007FFF; /* Yes!- Set trigger. This is used */ \
/* as an input to "MOD6_CNTR" module that changes the commutation sequence.*/ \
\
v.DelayTaskPointer = 1; /* v.DelayTaskPointer = 1 for #CHK_TRIGGER*/ \
} \
}
/*----------------------------------------------------------------------------------------------
NOISE_WINDOW_CNT Macro Definition
----------------------------------------------------------------------------------------------*/
// 滤噪窗口,认为过了NoiseWindowMax个计数才算过零点
#define NOISE_WINDOW_CNT_MACRO(v) \
if (v.CmtnDelay >= v.NWDelayThres) /* noise window is fixed Value*/ \
v.NoiseWindowMax = v.NWDelayThres - v.NWDelta; \
else /* noise window adjusted dynamically*/ \
v.NoiseWindowMax = v.CmtnDelay - v.NWDelta; \
\
v.NoiseWindowCounter += 1; \
\
if (v.NoiseWindowCounter == v.NoiseWindowMax) /* zc must occur max_noise_window times*/ \
{ \
v.ZcTrig = 0x00007FFF; /* Yes! Set trigger */ \
v.NoiseWindowCounter = 0; \
}
/*----------------------------------------------------------------------------------------------
DELAY_30DEG Macro Definition
----------------------------------------------------------------------------------------------*/
// 30度角的延迟计算
#define DELAY_30DEG_MACRO(v) \
/* Delay 30 deg calculator*/ \
if (v.Delay30DoneFlag == 0) \
{
/*更新时间计数、计算上一圈花了多少时间*/ \
v.OldTimeStamp = v.NewTimeStamp; \
v.NewTimeStamp = v.VirtualTimer; \
v.Tmp = v.NewTimeStamp - v.OldTimeStamp; \
\
if (v.Tmp > 0) /* Period = NewTimeStamp - OldTimeStamp*/ \
v.RevPeriod = v.Tmp; \
else /* If Period is negative, allow "wrapping" */ \
v.RevPeriod = 0x00007FFF + v.Tmp; \
\
v.RevPeriod &= 0x0000FFFF; \
/* T/12算下一圈30度角延迟计数*/ \
v.CmtnDelay = v.RevPeriod/12; /* Division quotient*/ \
/* 算余数,大于6则再+1*/
v.GPR1_COM_TRIG = v.RevPeriod - v.CmtnDelay*12; /* Division reminder*/ \
if (v.GPR1_COM_TRIG >= 6) \
v.CmtnDelay += 1; /* if Division reminder >= 6, rounding division quotient*/ \
v.Delay30DoneFlag = 0x0000000F; /* flag indicates "gone through" once*/ \
} /* if-end: v.Delay30DoneFlag == 0*/
参考学习:
TI Digital Motor Control,DMC MATH_V13.1
[1]李伟.无位置传感器 BLDC 电机控制器研究[D].吉林:吉林大学.2014:7
[2]韩芳.双模型无刷直流电动机控制器设计与实现[D].成都.电子科技大学.2015.4
[3]刘雨锋.无刷直流电机无位置传感器关键控制技术研究[D].江西.江西理工大学.2019.5
[4]童小健.无位置传感器永磁无刷直流电机控制策略研究[D].深圳.深圳大学.2015.4
[5]李自成.无刷直流电机无位置传感器控制关键技术研究[D].武汉.华中科技大学.2010.3
被抛弃的写随笔公众号改写技术文章了,感兴趣的可以关注公众号:王崇卫