好像PWM整流器又回到大家的视线了,OBC用三相PWM整流拓扑主要原因是可以双向变换,后面DC-DC再用三相CLLC就能完美配合。
以上是我开始接触这一块的背景,然后花了一些时间来学习PWM整流器的一些基础控制。
主要参考文献:张兴教授的 《PWM整流器及其控制 第二版》。
仿真环境:Plecs 4.1.2
目前PWM整流器主要的控制方法是在dq同步旋转坐标系上控制,电压外环的输出是输入电流的d轴的给定。通过前馈解耦的方法来分离dq轴的耦合量,得到控制的Vd, Vq后,再通过坐标系变换将dq变换为abc,可见图一所示。
(图一 PWM整流器的控制框架)
然后根据PWM调制方法,用SVPWM或者用三次谐波叠加的SPWM,可见图二,套路都是差不多的。
(图二 叠加三次谐波的SPWM)
我把坐标变换和控制做成了代码的方式,主要是方便移植到DSP,当然这得感谢Plecs仿真软件提供了非常完善的C代码调试环境。本文这些发出来的只是一些前期工作,后续还有很多有待完善的地方,还请各位大佬赐教指点。比如还可以优化锁相环,简化svpwm发波程序。目前只是实现了功能,做的非常粗糙。后续还要做出硬件来根据实际情况来调整代码,调试的内容将在后续的文章中更新。
下面是plecs环境中的仿真模型先是功率部分:
(图三 主功率)
采样部分:
(图四 采样信号)
控制部分用C代码实现,所以比较简单。为了便于调试,我输出了三个PI环的输入和输出,控制dq和电角度。
(图五 控制部分)
仿真时间10秒,控制环计算周期100KHz,开关频率100KHz,功率32KW,电感量1mH,ADC采样频率100KHz,输出电压800V。
1,输出电压和输出电解电容纹波电流:
2,输入电流和电压:(备注:前面几百毫秒没发波)另外目前的锁相环还未完全调试好,在电网电压输入不平衡时,输入电流波形会变得很烂,这个问题在前言中也提到了,我后续会继续优化锁相环代码或者引入正负序电流的分别控制,提升输入不平衡时的电流thd。
3,稳态输入电流:THD还可以。
下面是前馈解耦合的三个PI环:
4,电压外环:主要用于控制输出电压稳定,另外该环的输出值决定了网侧输入电流的峰值大小。所以该环的输出一定要根据实际功率进行限制,否则等着吃炸鸡。
5,D轴电流内环:该控制环的给定就是电压外环的输出,主要控制网侧输入电流值。
6,Q轴电流内环:为了实现PFC,通常直接把给控制的给定写0。
7,经过前馈解耦后输出到PWM模块的控制DQ:
8,SPWM输出的占空比信号:
仿真代码:
code declarations
#include "math.h"
//
// PI Coefficient DEFINE
//
#define ssrf_ts 1e-5
#define ssrf_kp 1
#define sser_ki 0.002
#define sser_kd 0
#define ssrf_up_limt 10
#define ssrf_low_limt -10
#define sqrt3 1.732
#define sqrt3div2 0.866
#define value_2pi 6.2831852
#define value_pi 3.1415926
#define value_pi6 0.3183098
#define PI 3.1415926
#define WL 2*PI*50*1e-3
#define VLOOP_TS 1e-5
#define VLOOP_KP 5
#define VLOOP_KI 20
#define VLOOP_KD 0
#define Vloop_UP 70
#define Vloop_LOW 0
#define DLOOP_TS 1e-5
#define DLOOP_KP 10
#define DLOOP_KI 25
#define DLOOP_KD 0
#define Dloop_UP 50
#define Dloop_LOW 0
#define QLOOP_TS 1e-5
#define QLOOP_KP 10
#define QLOOP_KI 25
#define QLOOP_KD 0
#define Qloop_UP 50
#define Qloop_LOW 0
//
// data struct define
//
typedef struct PI_CTRL_DATA_TAG {
double ts;
double kp;
double ki;
double kd;
double up_limt;
double low_limt;
double a0;
double a1;
double a2;
double out;
double out_1;
double error;
double error_1;
double error_2;
} PI_CTRL_DATA_DEF;
typedef struct integreator_tag{
double ts;
double ki;
double out_1;
double out;
} INTER_DEF;
typedef struct ABC_ab_dq_TAG {
double A;
double B;
double C;
double alpha;
double beta;
double d;
double q;
}ABC_ab_dq_DEF;
typedef struct sinwt_coswt_tag{
double sinwt;
double coswt;
}sinwt_coswt_DEF;
typedef struct dsrf_spll_tag{
float theta;
float vd_p;
float vq_p;
float vd_n;
float vq_n;
} DSRF_SPLL_DEF;
//
// FUNCTION DES
//
double inter(INTER_DEF *inter, double input)
{
inter->out = inter->ki * inter->ts * input + inter->out_1;
inter->out_1 = inter->out;
return (inter->out);
}
double pi_func(PI_CTRL_DATA_DEF *pi, double give, double sen)
{
pi->error = give - sen;
pi->a0 = pi->kp + pi->ki * pi->ts + pi->kd / pi->ts;
pi->a1 = pi->kp + 2 * pi->kd / pi->ts;
pi->a2 = pi->kd / pi->ts;
pi->out = pi->out_1 + pi->a0 * pi->error - pi->a1 * pi->error_1 + pi->a2 * pi->error_2;
// Limt
if(pi->out > pi->up_limt) pi->out = pi->up_limt;
if(pi->out < pi->low_limt) pi->out = pi->low_limt;
pi->out_1 = pi->out;
pi->error_1 = pi->error;
pi->error_2 = pi->error_1;
return(pi->out);
}
// clark tran
void abc_to_alpha_beta_clark_tran(ABC_ab_dq_DEF *ac_data)
{
ac_data->alpha = (0.6666) * (ac_data->A - 0.5 * ac_data->B - 0.5 * ac_data->C);
ac_data->beta = (0.6666) * (sqrt3div2 * ac_data->B - sqrt3div2 * ac_data->C);
}
// DQ
void alpha_beta_to_dq_tran(ABC_ab_dq_DEF *ac_data, sinwt_coswt_DEF *wt)
{
ac_data->d = ac_data->alpha * wt->coswt + ac_data->beta * wt->sinwt;
ac_data->q = -ac_data->alpha * wt->sinwt + ac_data->beta * wt->coswt;
}
// Inverse DQ transformation
void inverse_dq_to_alpha_beta_tran(ABC_ab_dq_DEF *ac_data, sinwt_coswt_DEF *wt)
{
ac_data->alpha = wt->coswt * ac_data->d - wt->sinwt * ac_data->q;
ac_data->beta = wt->sinwt * ac_data->d + wt->coswt * ac_data->q;
}
// Inverse clarke transformation
void inverse_clark_alpha_beta_to_abc_tran(ABC_ab_dq_DEF *ac_data)
{
ac_data->A = ac_data->beta;
ac_data->B = ac_data->beta * (-0.5) + ac_data->alpha * 0.5 * sqrt3;
ac_data->C = ac_data->beta * (-0.5) - ac_data->alpha * 0.5 * sqrt3;
}
// INIT PID coefficient
PI_CTRL_DATA_DEF ssrf_pi = {
ssrf_ts,
ssrf_kp,
sser_ki,
sser_kd,
ssrf_up_limt,
ssrf_low_limt,
0, 0, 0,
0, 0,
0, 0, 0
};
PI_CTRL_DATA_DEF vloop_pi = {
VLOOP_TS,
VLOOP_KP,
VLOOP_KI,
VLOOP_KD,
Vloop_UP,
Vloop_LOW,
0, 0, 0,
0, 0,
0, 0, 0
};
PI_CTRL_DATA_DEF d_ctrl_pi = {
DLOOP_TS,
DLOOP_KP,
DLOOP_KI,
DLOOP_KD,
Dloop_UP,
Dloop_LOW,
0, 0, 0,
0, 0,
0, 0, 0
};
PI_CTRL_DATA_DEF q_ctrl_pi = {
QLOOP_TS,
QLOOP_KP,
QLOOP_KI,
QLOOP_KD,
Qloop_UP,
Qloop_LOW,
0, 0, 0,
0, 0,
0, 0, 0
};
INTER_DEF inter_spll = {VLOOP_TS, 1, 0, 0};
ABC_ab_dq_DEF Vac = {0, 0, 0, 0, 0, 0, 0};
ABC_ab_dq_DEF Iac = {0, 0, 0, 0, 0, 0, 0};
ABC_ab_dq_DEF ctrl_ac = {0, 0, 0, 0, 0, 0, 0};
sinwt_coswt_DEF wt = {0, 0};
// DSRF SPLL
DSRF_SPLL_DEF dsrf_spll_abc = {0, 0, 0, 0};
void dsrf_spll_func(float va, float vb, float vc, DSRF_SPLL_DEF *drsf)
{
static float vd_p_ave = 0, vq_p_ave = 0, vd_n_ave = 0, vq_n_ave = 0;
static float theta = 0;
float sintheta = sin(theta);
float costheta = cos(theta);
float sintheta2 = sin(theta*2.0);
float costheta2 = cos(theta*2.0);
float ualha = 0.6666 * (va - 0.5 * vb - 0.5 * vc);
float ubeta = 0.6666 * (0.866 * vb - 0.866 * vc);
float vd_p = ualha*costheta + ubeta*sintheta;
float vq_p =-ualha*sintheta + ubeta*costheta;
float vd_n = ualha*costheta - ubeta*sintheta;
float vq_n = ualha*sintheta + ubeta*costheta;
float vd_p_o = (vd_p-vd_n_ave*costheta2)-(vq_n_ave*sintheta2);
float vq_p_o = (vq_p+vd_n_ave*sintheta2)-(vq_n_ave*costheta2);
float vd_n_o = (vd_n-vd_p_ave*costheta2)+(vq_p_ave*sintheta2);
float vq_n_o = (vq_n-vd_p_ave*sintheta2)-(vq_p_ave*costheta2);
//
vd_p_ave = vd_p_ave*0.99f + vd_p_o*0.01f;
vq_p_ave = vq_p_ave*0.99f + vq_p_o*0.01f;
vd_n_ave = vd_n_ave*0.99f + vd_n_o*0.01f;
vq_n_ave = vq_n_ave*0.99f + vq_n_o*0.01f;
float dds_kp = 1.0f, dds_ki = 0.002f, dds_vqi = 0;
float dds_vqp = -vq_p_o * dds_kp; // err = 0-vq_p_o
dds_vqi += -vq_p_o * dds_ki;
float dds_vqo = dds_vqp + dds_vqi;
theta -= dds_vqo * value_2pi * 0.0001f; // (45-65)*(1/10khz)*2pi
if(theta > value_2pi){theta -= value_2pi;}
if(theta < 0){theta += value_2pi;}
float utheta = theta;
// float ud, uq;
// float itheta = theta + value_pi - value_pi6; // vab -> va: vab+pi=vba,vba-pi/6=va
// ud = ud * 0.98f + vd_p * 0.02f;
// uq = uq * 0.98f + vq_p * 0.02f;
//updata
drsf->theta = utheta;
drsf->vd_p = vd_p_ave;
drsf->vq_p = vq_p_ave;
drsf->vd_n = vd_n_ave;
drsf->vq_n = vq_n_ave;
}
//
// output function code//
#include "math.h"
// Define Input data
Vac.A = InputSignal(0, 0);
Vac.B = InputSignal(0, 1);
Vac.C = InputSignal(0, 2);
Iac.A = InputSignal(0, 3);
Iac.B = InputSignal(0, 4);
Iac.C = InputSignal(0, 5);
double vdc_sen = InputSignal(0, 6);
double vdc_set = InputSignal(0, 7);
double tsw = InputSignal(0, 8);
// Run Control function
// Run ssrf pll
dsrf_spll_func(Vac.A, Vac.B, Vac.C, &dsrf_spll_abc);
double theta = dsrf_spll_abc.theta;
OutputSignal(0, 0) = theta;
wt.sinwt = sin(theta);
wt.coswt = cos(theta);
// abc_to_alpha_beta_clark_tran
abc_to_alpha_beta_clark_tran(&Vac);
abc_to_alpha_beta_clark_tran(&Iac);
// alpha_beta_to_dq_tran
// alpha_beta_to_dq_tran(&Vac, &wt);
Vac.d = dsrf_spll_abc.vd_p;
Vac.q = dsrf_spll_abc.vq_p;
alpha_beta_to_dq_tran(&Iac, &wt);
//
// Current inner loop D Q Control
//
double vloop_outer = pi_func(&vloop_pi, vdc_set, vdc_sen);
double d_ctrl_feed = pi_func(&d_ctrl_pi, vloop_outer, Iac.d);
ctrl_ac.d = Vac.d - d_ctrl_feed - Iac.q * WL;
// Limt the control d and q output
if(ctrl_ac.d > 400) { ctrl_ac.d = 400;}
double q_ctrl_feed = pi_func(&q_ctrl_pi, 0, Iac.q);
ctrl_ac.q = Vac.q + Iac.d * WL - q_ctrl_feed;
// if(ctrl_ac.q > 30) { ctrl_ac.q = 30;}
// if(ctrl_ac.q < -30) { ctrl_ac.q = -30;}
// inverse_dq_tran
inverse_dq_to_alpha_beta_tran(&ctrl_ac, &wt);
//inverse_clark_tran
inverse_clark_alpha_beta_to_abc_tran(&ctrl_ac);
//
//svpwm
//
int A_sector = 0;
int B_sector = 0;
int C_sector = 0;
if (ctrl_ac.A > 0)
{
A_sector = 1;
}
else
{
A_sector = 0;
}
if (ctrl_ac.B > 0)
{
B_sector = 2;
}
else
{
B_sector = 0;
}
if (ctrl_ac.C > 0)
{
C_sector = 4;
}
else
{
C_sector = 0;
}
int sector = A_sector + B_sector + C_sector;
//
// VA VB VC TIMER
// Inverse clarke transformation
//
float ta = tsw * (1/vdc_sen) * ctrl_ac.beta * 1.732 ;
float tb = (ctrl_ac.alpha * 1.5 + ctrl_ac.beta * 0.866) * tsw * (1/vdc_sen);
float tc = (ctrl_ac.beta * 0.866 - 1.5 * ctrl_ac.alpha) * tsw * (1/vdc_sen);
float T1 = 0;
float T2 = 0;
switch(sector)
{
case 1:
{
T1 = tc;
T2 = tb;
}
break;
case 2:
{
T1 = tb;
T2 = -ta;
}
break;
case 3:
{
T1 = -tc;
T2 = ta;
}
break;
case 4:
{
T1 = -ta;
T2 = tc;
}
break;
case 5:
{
T1 = ta;
T2 = -tb;
}
break;
case 6:
{
T1 = -tb;
T2 = -tc;
}
break;
}
float sw_a_on = 0;
float sw_b_on = 0;
float sw_c_on = 0;
float X = (tsw - T1 - T2) * 0.25 ;
float Y = T1 * 0.5 + X;
float Z = T2 * 0.5 + Y;
switch(sector)
{
case 1:
{
sw_a_on = Y;
sw_b_on = X;
sw_c_on = Z;
}
break;
case 2:
{
sw_a_on = X;
sw_b_on = Z;
sw_c_on = Y;
}
break;
case 3:
{
sw_a_on = X;
sw_b_on = Y;
sw_c_on = Z;
}
break;
case 4:
{
sw_a_on = Z;
sw_b_on = Y;
sw_c_on = X;
}
break;
case 5:
{
sw_a_on = Z;
sw_b_on = X;
sw_c_on = Y;
}
break;
case 6:
{
sw_a_on = Y;
sw_b_on = Z;
sw_c_on = X;
}
break;
}
//
// output
//
OutputSignal(0, 1) = ctrl_ac.d;
OutputSignal(0, 2) = ctrl_ac.q;
OutputSignal(0, 3) = wt.sinwt;
OutputSignal(0, 4) = wt.coswt;
OutputSignal(0, 5) = sw_a_on;
OutputSignal(0, 6) = sw_b_on;
OutputSignal(0, 7) = sw_c_on;
//
// PI DEBUG
//
OutputSignal(0, 8) = vdc_set;
OutputSignal(0, 9) = vdc_sen;
OutputSignal(0, 10) = vloop_outer;
OutputSignal(0, 11) = vloop_outer;
OutputSignal(0, 12) = Iac.d;
OutputSignal(0, 13) = d_ctrl_feed;
OutputSignal(0, 14) = 0;
OutputSignal(0, 15) = Iac.q;
OutputSignal(0, 16) = q_ctrl_feed;
OutputSignal(0, 17) = Vac.d;
OutputSignal(0, 18) = Vac.q;