一、前言
电流环是FOC控制算法中的最内环,是一个负反馈闭环调节系统,使电机以恒定电流去运转。主要流程是:电流采样->Clark变换->Park变换->反Park变换->SVPWM输出,之后再进行下一轮计算。FOC计算频率较高,是ADC采样频率,一般在10kHz到20kHz之间,整个计算过程没有用到浮点数,以提高计算效率
二、源代码解析
1、电流环控制流程
如图1所示,电流环控制流程是一个负反馈的调节过程,唯一外部变量是IRef_qd
,是外层速度环的变量,是根据目标速度和当前速度的误差,经由速度PID得出的值,该值是电流环的目标电流值,用来控制电流环电流的大小
电流环的工作流程是,先电流采样得到A和B两相的电流Ia
和Ib
;经过Clark变换得到Iα
和Iβ
,这是将电流沿坐标轴分解后的分量;经过Park变换得到Iq
和Id
,这是电流在电机转子直轴和交轴方向的分量;根据IRef_qd
,通过电流环PID计算出Uq
和Ud
,这是电压在电机转子直轴和交轴方向的分量;再通过SVPWM计算出电机ABC三相的占空比,给到电机,再进行下一轮计算
2、Clark变换
如图2所示,图中显示了α轴、β轴与ABC三相电流的关系,与代码相一致。将三相电流按照α轴、β轴向量分解:
根据三相电流标量和等于零,消掉C相电流ic
:
三相电压和电流矢量合成后,幅值都会变为原来的3/2
,为了保证是等幅变换,需要将幅值乘以2/3
,(推导过程见:https://blog.csdn.net/weixin_43900981/article/details/136289110),则:
源代码注释如下:
//输入形参:AB相电流
__weak alphabeta_t MCM_Clarke( ab_t Input )
{
alphabeta_t Output;
int32_t a_divSQRT3_tmp, b_divSQRT3_tmp ;
int32_t wbeta_tmp;
int16_t hbeta_tmp;
/* Iα = Ia*/
Output.alpha = Input.a;
/* aTemp = Ia/sqrt(3) */
/* divSQRT_3[1/sqrt(3)]的值是0x49E6, 计算方法32768/sqrt(3)=18918 */
a_divSQRT3_tmp = divSQRT_3 * ( int32_t )Input.a;
/* bTemp = Ib/sqrt(3) */
b_divSQRT3_tmp = divSQRT_3 * ( int32_t )Input.b;
/* Ib = -Ia/sqrt(3) - Ib/sqrt(3) - Ib/sqrt(3) */
/* 右移15位的原因是divSQRT_3变量是Q15类型,所以要除以32768 */
wbeta_tmp = ( -( a_divSQRT3_tmp ) - ( b_divSQRT3_tmp ) -
( b_divSQRT3_tmp ) ) >> 15;
/* 有效性判断 */
...
return ( Output );
}
2、Park变换
Park变换是将α和β分量沿着电机的d轴和q轴分解。这里解释一下,d轴称为直轴,是电机永磁转子的N极的方向,q轴为交轴,与d轴垂直,两个方向控制着电机的正反转。理想情况下,d轴电流为零,q轴控制着电机转动方向,电机负载力矩越大,则q轴电流越大。
如图3所示,θ为电角度,电角度的零点是β轴方向,我们将α和β轴分量沿着d和q轴方向分解:
源代码注释如下:
//输入形参:α和β电流分量;电角度
__weak qd_t MCM_Park( alphabeta_t Input, int16_t Theta )
{
qd_t Output;
int32_t d_tmp_1, d_tmp_2, q_tmp_1, q_tmp_2;
Trig_Components Local_Vector_Components;
int32_t wqd_tmp;
int16_t hqd_tmp;
/* 查表法计算正弦和余弦值,Q15格式 */
Local_Vector_Components = MCM_Trig_Functions( Theta );
/* q_tmp_1=Iα*cos(θ) */
q_tmp_1 = Input.alpha * ( int32_t )Local_Vector_Components.hCos;
/* q_tmp_2=Iβ*sin(θ) */
q_tmp_2 = Input.beta * ( int32_t )Local_Vector_Components.hSin;
/* Iq=Iα*cos(θ)-Iβ*sin(θ),Q15格式所以要除以32768*/
wqd_tmp = ( q_tmp_1 - q_tmp_2 ) >> 15;
/* 有效性判断 */
...
Output.q = hqd_tmp;
/* d轴电流计算 */
...
return ( Output );
}
3、电流环PID
电流环PID将监测值(电流采样)和控制量(输出电压)联系起来,形成一个闭环系统,即电流环。FOC控制中仅用到了P(比例调节)和I(积分调节),函数输入参数是d轴和q轴当前电流值,跟目标电流值的差,输出量是d轴和q轴的电压。
//输入形参:PID控制结构体;误差
__weak int16_t PI_Controller( PID_Handle_t * pHandle, int32_t wProcessVarError )
{
int32_t wProportional_Term, wIntegral_Term, wOutput_32, wIntegral_sum_temp;
int32_t wDischarge = 0;
int16_t hUpperOutputLimit = pHandle->hUpperOutputLimit;
int16_t hLowerOutputLimit = pHandle->hLowerOutputLimit;
/* P调节:KpG*误差 */
wProportional_Term = pHandle->hKpGain * wProcessVarError;
/* I系数是否有效 */
if ( pHandle->hKiGain == 0 )
{
pHandle->wIntegralTerm = 0;
}
else
{
/* I调节:累计误差+KiG*误差 */
wIntegral_Term = pHandle->hKiGain * wProcessVarError;
wIntegral_sum_temp = pHandle->wIntegralTerm + wIntegral_Term;
...
}
/* Kp=KpG/KpD Ki=KiG/KiD */
/* 为了保证计算过程不涉及浮点数,Kp和Ki用分数表示,这里需要除掉各自的分母 */
wOutput_32 = ( wProportional_Term >> pHandle->hKpDivisorPOW2 ) + ( pHandle->wIntegralTerm >> pHandle->hKiDivisorPOW2 );
...
return ( ( int16_t )( wOutput_32 ) );
}
电流环PID系数整定没有万能的公式,电路板和电机不同,PI系数数也会不同,具体情况需具体分析。空载或轻载情况下,电流环PI系数对电机的运行影响不大,过调或震荡也没有大问题,不会损坏设备。如果要跑出电机的极限参数,电流环和速度环系数还是要好好调整的,否则会因为过流或者震荡损坏设备。MCSDK给出了一种计算电流环PI系数的方法:
LS:线电感、WC:电流环带宽、VBUS:母线电压、Rshunt:采样电阻、T:PWM周期、AOP:电流放大器增益。将其改为C代码形式,计算结果仅供参考,具体情况具体分析
void PIDCal(void)
{
//母线电压 采样电阻 电流放大倍数 线电感 线电阻 截止频率rad/s pwm频率
float Vbus = 48.0, Rshunt = 0.002, Aop = 27, Ls = 0.00016, Rs = 0.027, Wc = 4000, freq = 14000.0;
float AB = Vbus * Rshunt * Aop / 3.3;
float Kip = Ls * Wc / AB;
float kii = Rs * Wc / AB / freq;
printf("KIP : %f KII : %f\n", Kip, kii);
}
4、反Park变换
反Park变换是Park变换的逆变换,将d轴和q轴的电压分量重新分解到α轴和β轴上:
//输入形参:q和d电压分量;电角度
__weak alphabeta_t MCM_Rev_Park( qd_t Input, int16_t Theta )
{
int32_t alpha_tmp1, alpha_tmp2, beta_tmp1, beta_tmp2;
Trig_Components Local_Vector_Components;
alphabeta_t Output;
Local_Vector_Components = MCM_Trig_Functions( Theta );
/* Vα= Vd*Sin(θ) + Vq*Cos(θ) */
alpha_tmp1 = Input.q * ( int32_t )Local_Vector_Components.hCos;
alpha_tmp2 = Input.d * ( int32_t )Local_Vector_Components.hSin;
Output.alpha = ( int16_t )( ( ( alpha_tmp1 ) + ( alpha_tmp2 ) ) >> 15 );
/* Vβ= Vd*Cos(θ) - Vq*Sin(θ) */
beta_tmp1 = Input.q * ( int32_t )Local_Vector_Components.hSin;
beta_tmp2 = Input.d * ( int32_t )Local_Vector_Components.hCos;
Output.beta = ( int16_t )( ( beta_tmp2 - beta_tmp1 ) >> 15 );
return ( Output );
}
5、Circle_Limitation及MMITABLE生成方法
该函数是用来限制PWM的最大可调占空比的,一般设置为90%就可以了,即32767*0.9=29490(MAX_MODULE=29490)。
//输入形参:q和d电压分量
__weak qd_t Circle_Limitation( CircleLimitation_Handle_t * pHandle, qd_t Vqd )
{
uint16_t table_element;
uint32_t uw_temp;
int32_t sw_temp;
qd_t local_vqd = Vqd;
//Vqd的平方和
sw_temp = ( int32_t )( Vqd.q ) * Vqd.q +
( int32_t )( Vqd.d ) * Vqd.d;
uw_temp = ( uint32_t ) sw_temp;
/* Vqd的平方和 大于 设置的最大可调占空比时 */
if ( uw_temp > ( uint32_t )( pHandle->MaxModule ) * pHandle->MaxModule )
{
/* 计算 Vqd的平方和 对应的数组下标 */
uw_temp /= ( uint32_t )( 16777216 );
uw_temp -= pHandle->Start_index;
/* 获取缩小比例 */
table_element = pHandle->Circle_limit_table[( uint8_t )uw_temp];
/* 等比例缩小Vqd */
sw_temp = Vqd.q * ( int32_t )table_element;
local_vqd.q = ( int16_t )( sw_temp / 32768 );
sw_temp = Vqd.d * ( int32_t )( table_element );
local_vqd.d = ( int16_t )( sw_temp / 32768 );
}
return ( local_vqd );
}
下面说一下MMITABLE
如何生成,Vqd
的平方和取值范围是0
到2*32767*32767
,将其等分为128
个区间,则每一区间长度为2*32768*32768/128=16777216
。假设MAX_MODULE
为29490
,则等比例缩小的区间是[29490*29490, 2*32767*32767]
。MAX_MODULE在第29490*29490/16777216=51
区间,可以得出START_INDEX
为51,第52区间的左值是29490*29490+16777216=886437316
,则MAX_MODULE[0]=sqrt(29490*29490/886437316)*32767=32455
,如此循环,直到23276732767,C代码计算过程如下:
const int16_t MAX_MODULE = 29490;
int MMITable(void)
{
int32_t START_INDEX = MAX_MODULE * MAX_MODULE / 16777216;
printf("START_INDEX: %d\n", START_INDEX);
int32_t END_INDEX = 2 * 32767 * 32767 / 16777216;
printf("END_INDEX: %d\n", END_INDEX);
int32_t ARR_SIZE = END_INDEX - START_INDEX + 1;
printf("ARR_SIZE: %d\n", ARR_SIZE);
// int32_t ONE_PART = ((2 * 32767 * 32767) - (MAX_MODULE * MAX_MODULE)) / ARR_SIZE;
int32_t ONE_PART = 16777216;
printf("ONE_PART: %d\n", ONE_PART);
double start = MAX_MODULE * MAX_MODULE;
double temp = 0.0;
int32_t result = 0;
for(int32_t i = 1;i <= ARR_SIZE;i++)
{
temp = start / (start + i * ONE_PART);
temp = sqrt(temp);
result = temp * 32767;
printf("%d,", result);
if(i % 10 == 0)
printf("\\\n");
}
printf("\n");
return 0;
}
三、结语
电流环分了三章来讲的,涉及的理论内容不少,因为主要是解析代码,理论部分没有展开说,还需要大家对照相关资料去看。电流环解析到此结束,后续会再讲解速度环和位置反馈的部分