在电力电子和并网控制领域,锁相环(PLL)技术堪称电网同步的"心脏"。传统方案通常依赖现成的Simulink模块搭建,但今天我们要玩点更硬核的——完全用C语言实现基于双二阶广义积分器(DSOGI)的锁相环,直接嵌入Simulink仿真环境。这种做法的优势在于:
DSOGI-PLL的核心创新在于其正交信号生成方式。不同于常规的基于延迟或变换的方法,它通过两个耦合的二阶广义积分器,在产生同相信号的同时,自然衍生出精确的正交分量。这种结构对电网谐波和电压畸变具有天然免疫力。
DSOGI的离散化实现基于以下差分方程:
code复制y1[k] = α*(0.5*(u[k]+u[k-1]) - y1[k-2]) + k*y1[k-1]
y2[k] = α*(y1[k] - y2[k-2]) + k*y2[k-1]
其中关键参数:
代码中的结构体设计体现了状态保存思想:
c复制typedef struct {
float alpha; // 归一化截止频率
float k; // 阻尼系数
float u_prev[2]; // 环形缓冲区存储历史输入
float y1_prev[2];// 主通道状态存储器
float y2_prev[2];// 正交通道状态存储器
} DSOGI_Type;
更新函数的关键操作:
调试技巧:alpha参数决定带宽,初始值建议设为2π500.01(对应50Hz基频,10ms采样)。k值首次调试可从0.7开始,观察阶跃响应调整。
传统鉴相器在电网频率偏移时性能下降,本方案采用改进的反正切法:
c复制float Phase_Detector(float y1, float y2) {
float mag = sqrtf(y1*y1 + y2*y2) + 1e-6f; // 防除零
return atan2f(y1/mag, y2/mag); // [-π, π]范围输出
}
动态PI调节器实现:
c复制typedef struct {
float kp; // 比例系数(典型值0.1~1.0)
float ki; // 积分系数(典型值0.01~0.1)
float freq_base; // 额定频率(50/60Hz)
float integral; // 抗饱和积分器
float max_offset;// 最大频率偏移限制(±5Hz)
} PLL_Controller;
float PLL_Update(PLL_Controller *c, float phase_error, float dt) {
// 限幅处理
phase_error = fmaxf(fminf(phase_error, 0.5f), -0.5f);
// PI计算
float freq_offset = c->kp * phase_error + c->ki * c->integral;
// 积分抗饱和
if(fabsf(freq_offset) < c->max_offset) {
c->integral += phase_error * dt;
}
return c->freq_base + freq_offset;
}
c复制static DSOGI_Type sogi_ctx = {0};
static PLL_Controller pll_ctx = {
.kp = 0.5f, .ki = 0.05f, .freq_base = 50.0f
};
c复制#define MDL_INITIALIZE_SIZES
static void mdlInitializeSizes(SimStruct *S) {
ssSetNumInputPorts(S, 1); // 电压输入
ssSetInputPortWidth(S, 0, 1);
ssSetNumOutputPorts(S, 3); // y1,y2,phase输出
ssSetOutputPortWidth(S, 0, 1);
ssSetOutputPortWidth(S, 1, 1);
ssSetOutputPortWidth(S, 2, 1);
}
c复制static void mdlOutputs(SimStruct *S, int_T tid) {
// 获取仿真步长
float dt = ssGetStepSize(S);
// 接口处理
float u = *ssGetInputPortRealSignal(S,0);
float *y1 = ssGetOutputPortRealSignal(S,0);
float *y2 = ssGetOutputPortRealSignal(S,1);
float *phase = ssGetOutputPortRealSignal(S,2);
// 执行PLL流程
DSOGI_Update(&sogi_ctx, u, y1, y2);
float err = Phase_Detector(*y1, *y2);
*phase = PLL_Update(&pll_ctx, err, dt);
}
c复制typedef struct {
int32_t alpha; // Q15格式
int32_t k; // Q15格式
int32_t u_prev[2]; // Q12格式
int32_t y1_prev[2]; // Q12格式
int32_t y2_prev[2]; // Q12格式
} DSOGI_Q15_Type;
c复制#pragma CODE_SECTION(PLL_ISR, ".text:fastcode")
__interrupt void PLL_ISR(void) {
static uint32_t phase_acc = 0;
// 1. ADC采样触发
int16_t adc_val = AdcRead(CH1);
// 2. 执行PLL算法
int32_t y1, y2;
DSOGI_Q15_Update(&sogi_q15, adc_val, &y1, &y2);
int32_t err = Phase_Detector_Q15(y1, y2);
uint32_t freq = PLL_Q15_Update(&pll_q15, err);
// 3. 相位累加器更新
phase_acc += freq;
// 4. 清除中断标志
PieCtrlRegs.PIEACK.all = PIEACK_GROUP1;
}
c复制#define MOVING_AVG_LEN 5
float MovingAvg(float new_val) {
static float buf[MOVING_AVG_LEN] = {0};
static uint8_t idx = 0;
buf[idx] = new_val;
idx = (idx + 1) % MOVING_AVG_LEN;
float sum = 0;
for(uint8_t i=0; i<MOVING_AVG_LEN; i++) {
sum += buf[i];
}
return sum / MOVING_AVG_LEN;
}
c复制void SafetyMonitor(float u, float y1, float y2) {
static uint16_t fault_cnt = 0;
// 幅值检测
float mag = sqrtf(y1*y1 + y2*y2);
if(mag < 0.1f || mag > 1.5f) {
fault_cnt++;
} else {
fault_cnt = 0;
}
// 连续10次异常触发保护
if(fault_cnt > 10) {
SystemReset();
}
}
| 测试场景 | 锁定时间 | 相位误差 | 频率跟踪误差 |
|---|---|---|---|
| 正常电网 (THD<3%) | <20ms | ±0.3° | ±0.05Hz |
| 谐波注入 (THD=15%) | <30ms | ±0.8° | ±0.1Hz |
| 频率阶跃 (±2Hz) | <50ms | ±1.2° | ±0.2Hz |
| 电压跌落 (50%) | <40ms | ±0.5° | ±0.15Hz |
DSOGI参数:
code复制α = 2π * f_bandwidth * T_s
典型值:f_bandwidth=25Hz → α=0.785 (10kHz采样时)
code复制临界阻尼:k = 1 - α/2
建议取值:0.6~0.8
PI调节器参数:
code复制经验公式:kp ≈ 2π * f_bandwidth / V_nom
示例:f_bandwidth=10Hz, V_nom=311V → kp≈0.2
code复制ki = (2π*f_bandwidth)^2 / (10*kp)
接上例:ki≈0.02
现场调试步骤:
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| 相位抖动大 | 1. 采样不同步 2. k值过小 |
1. 检查定时器配置 2. 增大k值 |
| 频率跟踪慢 | PI参数过保守 | 按20%步长增大kp |
| 电网畸变时失锁 | α值过大 | 减小α值提高滤波效果 |
| 启动时振荡 | 初始状态未复位 | 上电时清零所有状态变量 |
| DSP计算溢出 | Q格式选择不当 | 改用Q23格式或降低输入幅值 |
c复制// 根据运行状态动态调整kp
if(fabsf(phase_error) > 0.5f) {
pll_ctx.kp = 1.0f; // 大误差时快速响应
} else {
pll_ctx.kp = 0.2f; // 小误差时保持稳定
}
c复制// 在DSOGI输出后追加谐波陷波
float Harmonic_Notch(float y, float freq) {
static float buf[3] = {0};
float a1 = -2*cosf(2*M_PI*freq/fs);
float b0 = 1.0f/(1 + a1 + 1);
buf[2] = buf[1];
buf[1] = buf[0];
buf[0] = y;
return b0*(buf[0] + a1*buf[1] + buf[2]);
}
c复制// 1kHz执行PLL核心
// 10kHz执行信号采集和预处理
void MultiRate_Handler(void) {
static uint8_t cnt = 0;
// 高速任务
float u = AdcReadAndFilter();
// 低速任务
if(++cnt >= 10) {
cnt = 0;
DSOGI_Update(&sogi, u, &y1, &y2);
Phase_Update();
}
}