ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

单相并网逆变器SOGI-PLL锁相环原理与C语言实现

单相并网逆变器SOGI-PLL锁相环原理与C语言实现 简介本资源是一套面向电力电子与嵌入式系统开发者的二阶广义积分器锁相环SOGI-PLLC语言实现代码专为单相电网相位实时跟踪场景设计适用于DSP平台上的实时控制开发尤其契合使用CCS集成环境进行电力电子控制算法验证的工程师与高校研究者。压缩包共4个文件2个C源文件、2个头文件总大小仅2KB结构精炼头文件定义SOGI-PLL核心参数与函数接口C文件分别实现SOGI模块与PLL主控逻辑涵盖鉴相、二阶广义积分滤波及压控振荡器等关键环节代码已通过DSP平台部署及HIL半实物仿真验证具备工程可用性。目前已有6398人学习下载读者可直接复用该轻量级、高精度相位同步方案快速集成至有源滤波器、并网逆变器或智能电表等实际项目中并基于清晰的模块划分开展参数调优与性能分析。 做单相并网逆变器或者单相PFC整流器的朋友应该都有过这种经历电网电压相位过零检测加查表生成正弦波形负载一拉起来或者电网电压畸变的时候相位跳得厉害轻则电流波形毛刺明显严重的时候直接触发过流保护。后来换成普通的单同步坐标系锁相环又发现单相系统只有一个电压相量没有天然的d-q正交分量做Park变换根本不够用。这时候二阶广义积分器锁相环SOGI-PLL就派上用场了。SOGI-PLL做的事情简单说就是先用二阶广义积分器把单相电压虚构出一个正交分量把“单相”在软件里变成“伪两相”然后就能像三相系统一样用同步旋转坐标锁相。它兼有锁相和滤波两重功能不像过零检测那样对谐波和畸变毫无抵抗力也不像固定频率的带通滤波器那样在电网频率漂移时束手无策。这篇文章我会把SOGI-PLL的原理、离散化方法、完整的C语言代码、参数整定依据、工程现场容易踩的坑一次讲清楚。适合正在做单相并网逆变器、单相PFC、UPS或者APF的嵌入式工程师和电力电子工程师参考。1. 为什么单相锁相要专门搞SOGI1.1 单相系统的天然缺陷先看三相锁相为什么简单。三相电压经过Clark变换得到αβ轴分量再经过Park变换得到d-q轴分量。锁相的核心就是让q轴电压等于零此时d轴对准电网电压矢量方向旋转角θ就是电网相位。整个过程行云流水因为三相系统天然就有两个正交分量数学工具随便用。单相系统只有一个电压标量问题就大了。你只有一个相量怎么旋转坐标变换连Park变换的输入都凑不齐。很多初做单相的人第一反应是过零检测ZCD用比较器把正弦波变成方波捕获上升沿/下降沿来得到相位。这个方法不能说完全不能用但它有两个硬伤。第一个硬伤是更新率太低。50Hz电网下过零检测一个周期最多更新两次相位也就是100Hz的刷新率。负载突变、电压跌落这种需要毫秒级响应的事情它完全跟不上。第二个硬伤是过零点太脆弱。电网电压在零点附近斜率最大没错但谐波畸变、开关毛刺、直流偏置都会让过零判断出现抖动甚至误触发。你可以加迟滞比较器可以在软件里做滤波但治标不治本信号畸变严重的时候照样跳相位。后来有人想了个折中办法用电网频率做开环查表假设频率恒定为50Hz相位按时间匀速增长。这种方式在弱电网下完全不成立频率一旦漂移相位误差会线性累积时间越长偏得越多。1.2 SOGI在锁相环里的位置SOGI解决的核心问题就是给单相系统“无中生有”地构造出一个虚拟正交相量。输入信号vg经过SOGI后输出两个信号一个与原信号同相位、同幅值的带通输出vα一个滞后50度在中心频率附近严格滞后90度的低通输出vβ。有了vα和vβ单相系统在数学层面就有了两个正交分量接下来就可以完全照搬三相锁相环的套路Park变换、PI调节器、压控振荡器。SOGI-PLL的整体信号流是这样的电压采样vg → SOGI正交信号发生器 → vα/vβ → Park变换 → vq作为锁相误差 → PI调节器 → 角频率ω → 对ω积分得到相位θ → θ反馈给Park变换同时作为SOGI的实时谐振频率。注意这里的ω不是固定的50Hz对应角频率而是锁相环实时估计出来的频率。SOGI中所有与频率相关的系数都会跟着这个ω走所以它叫广义积分器广义就广义在频率可变。电网频率从49Hz漂到51HzSOGI依然能保持近乎完美的正交输出这是固定频率滤波器做不到的。之前听一个朋友说“单相并网锁相只要把SOGI想成一个可以随频率自动调整的90度移相器就行了。”这个理解方向是对的移相器的角色就是SOGI而移相精度由频率自适应保证。2. 二阶广义积分器的正交信号生成原理2.1 传递函数与幅相特性SOGI的连续域传递函数标准写法如下其中k是阻尼系数ω是谐振角频率这里就是锁相环输出的角频率估计值α路带通输出 D(s) k·ω·s / (s² k·ω·s ω²)β路低通输出 Q(s) k·ω² / (s² k·ω·s ω²)在谐振点sjω处D(jω)的幅值为1相移为0度Q(jω)的幅值同样为1但相移为-90度。也就是说在中心频率上vα跟输入电压完全同相且等幅vβ滞后vα90度且等幅。这就是我们需要的正交信号。从滤波特性看α路对中心频率附近的信号是带通衰减两侧的频率成分β路对低频是低通对中心频率附近也有平坦的增益。这意味着SOGI本身就是一个谐振型滤波器对输入电压里的谐波成分有自然的抑制作用。电网里常见的3次、5次、7次谐波经过SOGI之后都会被打掉一大截锁相环拿到的信号是相对干净的基波。这一点在实际工频应用中非常值钱比单纯过零检测加低通滤波器的方案不知道高到哪里去了。当输入信号频率偏离SOGI中心频率ω时两个输出的幅值和相位关系都会出现偏差。偏离越远vβ滞后vα的角度越偏离90度幅值也开始失衡。这就是为什么必须把锁相环估计的ω实时回馈给SOGI做频率自适应的原因。只要ω跟真实电网频率靠得足够近在大多数应用场景下比如±2Hz偏移正交误差都在可接受范围内相位锁定精度能控制在1度以内。2.2 从连续域到C代码的离散化连续域公式在仿真软件里可以直接画传递函数但写进C代码就必须要离散化。离散化方法选不好仿真和实机表现会差很多。工程里SOGI最常用的离散化有两种前向欧拉法和双线性变换法Tustin自己推导过就能发现SOGI的状态方程非常适合前向欧拉。把SOGI写成状态方程形式dx1/dt k·ω·(vg - x1) - ω·x2 dx2/dt ω·x1其中vαx1vβx2。前向欧拉离散化后x1 Ts·[k·ω·(vg - x1) - ω·x2] x2 Ts·ω·x1这个形式在C语言里写起来非常直接每个采样周期做两次乘加和两次加法占用的计算量几乎可以忽略。双线性变换在数学上更精确但实现起来稍麻烦而且SOGI的双线性离散化会引入代数环需要额外处理前馈信号代码量会大不少。实际工程中在采样频率20kHz、工频50Hz的条件下前向欧拉法引入的相位误差本身就在0.5度以内已经完全可以接受。我的建议是除非你系统采样率特别低比如低于5kHz否则直接用前向欧拉简单可靠也容易排查问题。3. 完整的C语言实现3.1 数据结构设计先放出我常用的SOGI-PLL结构体把SOGI状态、锁相环状态、PI调节器状态全部封装在一起。这样做主要有三个原因一是多实例复用方便比如做三相不平衡检测时需要多个SOGI直接声明多个结构体就行二是代码可读性好每个模块边界清楚三是在中断服务函数里传参方便不需要使用大量全局变量。typedef struct { /* SOGI参数 */ float k; /* SOGI阻尼系数典型值sqrt(2)1.414 */ float x1; /* α路状态变量对应vα */ float x2; /* β路状态变量对应vβ */ /* PLL参数 */ float kp; /* PI比例系数 */ float ki; /* PI积分系数 */ float w; /* 估计角频率 rad/s */ float theta; /* 输出相位角 rad */ float integral; /* PI积分器状态 */ float wNom; /* 额定角频率如2π*50 */ /* 采样与归一化 */ float ts; /* 采样周期 s */ float vgPeak; /* 电网电压峰值标幺基准用于归一化 */ /* 输出 */ float vAlpha; float vBeta; float vq; float wOut; } SogiPll;这里特意说明一个容易被忽略的点采样电压进SOGI之前先要做归一化。如果你用ADC直接采到的原始值比如180012位ADC或者经过电压传感器换算后的实际电压比如340V直接送进锁相环PI参数就得跟着量程变。我习惯把所有电压都归一化到峰值1.0的标幺值这样锁相环误差信号的幅值范围固定PI参数标定一次就能在不同电压等级的平台上复用。归一化方法很简单vg_pu vg_raw / VG_PEAK。VG_PEAK可以通过电压传感器变比换算也可以直接在示波器上读稳态电压峰值填进去。归一化之后如果电网电压跌落到0.5pu锁相环增益会降低动态响应变慢这是正常现象很多工程上的低压穿越策略就是基于这个特性做的。初始化函数如下void sogi_pll_init(SogiPll *pll, float ts, float k, float kp, float ki) { pll-ts ts; pll-k k; pll-kp kp; pll-ki ki; pll-x1 0.0f; pll-x2 0.0f; pll-theta 0.0f; pll-integral 0.0f; pll-wNom 2.0f * 3.14159265358979f * 50.0f; pll-w pll-wNom; pll-vgPeak 1.0f; pll-vAlpha 0.0f; pll-vBeta 0.0f; pll-vq 0.0f; pll-wOut pll-wNom; }3.2 核心update函数逐段解读下面这段是完整的一次锁相环更新每个采样周期调用一次。我加了详细注释方便对照前面给的离散化公式看。float sogi_pll_update(SogiPll *pll, float vgRaw) { float ts pll-ts; float k pll-k; float w pll-w; float sinT, cosT; /* 1. 输入电压归一化并扣除直流偏置直流偏置问题见第5节 */ float vg vgRaw / pll-vgPeak; /* 2. SOGI状态方程更新前向欧拉离散化 */ float dx1 k * w * (vg - pll-x1) - w * pll-x2; float dx2 w * pll-x1; pll-x1 ts * dx1; pll-x2 ts * dx2; pll-vAlpha pll-x1; pll-vBeta pll-x2; /* 3. Park变换取q轴分量作为锁相误差 */ sinT sinf(pll-theta); cosT cosf(pll-theta); /* 锁定时vAlpha≈cos(theta)vBeta≈sin(theta)vq≈0 */ pll-vq -pll-vAlpha * sinT pll-vBeta * cosT; /* 4. PI调节器输出角频率并做积分器限幅 */ pll-integral ts * pll-ki * pll-vq; if (pll-integral 200.0f) pll-integral 200.0f; if (pll-integral -200.0f) pll-integral -200.0f; pll-w pll-wNom pll-kp * pll-vq pll-integral; /* 5. 频率限幅防止异常状态下锁相环失锁 */ if (pll-w 2.0f * 3.14159265358979f * 55.0f) pll-w 2.0f * 3.14159265358979f * 55.0f; if (pll-w 2.0f * 3.14159265358979f * 45.0f) pll-w 2.0f * 3.14159265358979f * 45.0f; /* 6. 相位积分并约束到[0, 2π)区间避免浮点累计误差 */ pll-theta ts * pll-w; if (pll-theta 2.0f * 3.14159265358979f) pll-theta - 2.0f * 3.14159265358979f; else if (pll-theta 0.0f) pll-theta 2.0f * 3.14159265358979f; pll-wOut pll-w; return pll-theta; }这段代码有一个重要细节SOGI的频率输入pll-w是第4步才更新出来的而第2步的SOGI状态更新用的还是上一个采样周期的w。也就是说SOGI和PLL之间存在一个采样周期的延迟。在20kHz采样率下这个一周期延迟对50Hz信号的影响大约0.9度相位对锁相环动态稳定性的影响也极小工程上完全可以在接受范围内。如果你追求极致相位精度可以在第4步更新完w之后再用新的w算一次SOGI状态但这会加倍计算量一般没必要。如果你在代码里用了sinf/cosf注意保证编译链接时数学库已经正确链接。对大多数带FPU的MCUCortex-M4F、Cortex-M7、C2000系列直接调用硬件数学库函数即可。如果是极端抠成本的8位单片机可能要考虑查表代替sinf/cosf但这种场景下跑SOGI-PLL本来就很吃力我更建议把锁相更新频率降下来做多周期分摊。3.3 一个可以上板的主循环示例实际工程里SOGI-PLL的调用位置通常是在ADC采样完成中断或者PWM定时器中断里与控制算法同步执行。伪代码示例如下#define CONTROL_FREQ 20000.0f #define CONTROL_TS (1.0f / CONTROL_FREQ) SogiPll pll; void adc_isr(void) { float vgMeas adc_read_voltage(); /* 读取电网电压采样值 */ float theta sogi_pll_update(pll, vgMeas); /* 后续并网控制代码电流环、dq变换、SVPWM等 */ } void main(void) { /* 初始化参数 * k1.414, 带宽fn100Hz对应的Kp888.6, Ki394784 */ sogi_pll_init(pll, CONTROL_TS, 1.414f, 888.6f, 394784.0f); /* 开启ADC中断后所有工作都放在中断里完成 */ while (1) { /* 主循环可以做通信、显示、保护逻辑 */ } }中断频率必须和初始化时填的Ts严格一致。如果实际采样率是20kHz初始化时填的Ts就应该是0.00005秒少一个零多一个零都会导致参数标定全部偏移。4. 参数设计与调优实测4.1 SOGI的k值怎么选SOGI里的k值直接决定正交信号发生器的带宽和阻尼特性。从传递函数看SOGI是一个二阶系统阻尼比ζ k/2。标准做法取k√2对应阻尼比0.707此时幅频响应特性最平坦兼顾动态响应和超调量这也是绝大多数文献默认的取值。k的具体影响体现在两个对立的方向k偏大比如2.0以上SOGI带宽变大对输入信号的跟踪速度快动态响应好。但滤波能力下降谐波抑制效果变差输出信号里会残留更多高频分量最终锁相角度上会出现更多纹波。k偏小比如0.8以下SOGI带宽变窄滤波效果更好输出信号更干净但正交信号生成的动态过程变慢锁相环整体响应变迟钝。如果电网电压突然跌落或相位跳变重新锁定需要更长时间。我的习惯是常规并网逆变器场景直接上k1.414不需要犹豫。如果电网谐波非常严重且动态响应要求不高可以降到1.0左右但不建议再低。低于0.8之后SOGI的带宽太窄频率突变时会有明显的锁相延迟。4.2 锁相环PI参数从闭环带宽反推SOGI-PLL的PLL部分本质是一个以vq为误差、输出角频率的PI调节器。在锁定点附近做小信号分析锁相环的闭环传递函数可以近似为H(s) (Kp·s Ki) / (s² Kp·s Ki)这是一个标准的二阶系统自然振荡角频率ωn和阻尼比ζ与PI参数的关系为Kp 2·ζ·ωn Ki ωn²设计时先确定想要的锁相带宽即自然振荡角频率。一般工频锁相取50Hz到200Hz比较合适。带宽越高锁相动态响应越快但对谐波和噪声的衰减能力越差带宽越低抗干扰能力越强但相位跟踪变慢。对常规并网逆变器我一般取100Hz左右作为初始设计点。令ζ0.707f_n100Hz那么ωn2π×100≈628.3 rad/s于是Kp 2×0.707×628.3 ≈ 888.6 Ki 628.3² ≈ 394784这两个值直接填进代码即可。需要注意这里的输入信号vq是标幺值幅值≈1.0如果你们系统的电压没有归一化PI参数要按实际幅值反比例缩放否则锁相环增益会偏大或偏小。4.3 不同场景下的参数选择参考同样是SOGI-PLL不同应用场景的参数倾向差别挺大。下面这个表是我在几个实际项目里的经验值可以直接作为初值参考应用场景k值锁相带宽Kp/Ki参考说明常规单相并网逆变器1.414100HzKp888, Ki394784均衡考虑动态与滤波弱电网/强谐波环境1.050HzKp444, Ki98696优先保证锁相角度稳定动态响应要求高比如UPS切换1.414200HzKp1777, Ki1579136锁相速度快但需要入口滤波配合三相不平衡检测DSOGI1.41480-120Hz同上取中间值每相独立SOGIPI共用或独立均可调参顺序上我的建议是先固定k1.414把PI带宽从100Hz开始往高或者往低扫观察实际运行中并网电流THD和锁相角阶跃响应找到最合适的点。不要一上来就两个参数一起调否则出了问题根本不知道是SOGI响应慢还是PLL带宽不够。5. 工程现场最容易踩的坑5.1 直流偏置污染β路这个坑是实际调试中非常容易遇到的。电网电压采样调理电路即使做了交流耦合也会因为运放偏置、地电位差等原因引入一个很小的直流偏置。比如信号调理把±300V电网电压转成±3V直流偏置可能只有10mV看起来无伤大雅。但SOGI的β路传递函数Q(s)在s0处的直流增益并不是零。把s0代入Q(s)得到Q(0)k当k1.414时直流增益就是1.414。这意味着输入里的一点点直流分量会被β路直接放大并保留在输出中而α路是带通特性直流被天然滤除。结果就是vα和vβ不再正交锁相环锁出来的相位会带上一个低频纹波。这个问题的实际表现很典型示波器上看锁相角θ会发现它在一个固定角度附近以50Hz或100Hz的频率抖动或者看vq波形本来应该近似等于0结果有一个明显的直流偏置叠加。很多刚做单相锁相的人都遇到过这种“相位怎么调都调不平”的情况根源就在这里。解决方法很简单在SOGI之前加一个直流偏置消除环节。工程上最省事的方法是用一个慢速低通滤波器估计直流分量然后从原始采样值里减掉float dcEst 0.0f; #define DC_ALPHA 0.0015f /* 截止频率约5Hz采样率20kHz时 */ float vgDcRemoved vgRaw - dcEst; dcEst DC_ALPHA * (vgRaw - dcEst);这个低通滤波器的时间常数大约1/2π×5≈32ms不会影响50Hz基波信号但能快速跟踪缓慢变化的直流偏置。把它放在归一化之前或者之后都可以我是放在ADC采样之后、SOGI之前。用上之后vq上的直流偏置纹波基本消失锁相角度干净了很多。5.2 采样率与离散化带来的相位误差前向欧拉离散化本身会带来一个约半个采样周期的时间延迟折算成相位误差约为ω·Ts/2。在20kHz采样率下50Hz工频对应的相位误差大约0.45度如果采样率降到10kHz这个误差会翻倍到0.9度左右。对于纯锁相来说这个误差会被PI调节器在稳态时“吃掉”所以锁相角本身没有问题。但它会影响一个东西并网电流的参考相位。如果你直接用锁出来的θ去生成电流参考波形这个0.5度左右的相位偏差会在功率因数和有功功率计算中体现出来。并网测试要求功率因数做到0.99以上时0.5度相位误差对应的功率因数大约0.99996其实不影响但在高精度计量或者并网规范测试里这个误差还是值得处理的。补偿方法有两种。一种是在输出相位时减去半个采样周期的积分量theta_comp theta - 0.5×ts×w。另一种是把相位积分改成梯形积分theta ts×(ww_prev)/2。这两种做法都很轻量实际项目中我一般是直接加个0.5×ts×w的补偿项代码改动最小。5.3 上电初始化慢与重新锁定如果SOGI-PLL从零状态初始化上电瞬间x1和x2都是0锁相环需要先建立正交信号然后PI再慢慢收敛整个过程可能需要几十毫秒。对大多数并网应用来说几十毫秒可以接受但如果你做的是UPS从旁路到逆变器的无缝切换这个时间就太长了。一个非常有效的初始化技巧上电后第一次采样得到电压瞬时值vg0直接把x1预置为vg0/VG_PEAKx2预置为0θ预置为0。因为电网电压是连续变化的x1初始值接近真实电网电压SOGI就能从接近稳态的位置开始运行锁定时间可以缩短一半以上。如果上电时你还能大概估算出电网相位比如通过过零检测捕获到第一次过零可以把θ也预置到对应相位锁相环几乎是瞬时就锁定。但要注意过零检测的过零点判断本身就有误差粗估可以别指望它预置得很准。另一个相关问题是电压跌落恢复后的重新锁定。当电网电压突然跌落到很低的水平vq误差信号会变得很小PI积分器如果保持之前的输出恢复后可能有一个较大的相位调整过程。我习惯在检测到电压有效值低于某阈值时把PI积分器清零并让锁相环保持自由运行状态等电压恢复后再重新投入闭环。这个策略类似于通信里的“失步重捕”能避免恢复瞬间的大角度阶跃。5.4 采样噪声与开关毛刺电网电压采样信号里经常混杂着开关器件的高频毛刺。如果ADC采样点恰好落在PWM开关沿附近毛刺尖峰直接进来vq会有明显跳变锁相角也会跟着抖一下。处理办法有几个层面。首先是硬件层面采样调理电路要加RC低通滤波截止频率大概在几kHz到几十kHz之间把开关频率分量的幅值压下去。其次是软件层面最有效的做法是把ADC采样触发点安排在开关周期的中间位置也就是远离开关沿的地方这对PFC和逆变器来说都是常规操作。如果这样还不够可以在SOGI前面加一个简单的滑动平均滤波器或者把锁相环更新频率降为控制频率的一半、做两次采样平均后再进SOGI。实测下来效果很好代码也就多几行。另外一个经验是锁相环的更新频率不一定非要跟控制频率完全一致。控制环路跑20kHz锁相环完全可以每两个采样周期10kHz才更新一次用上一拍的采样值或者用两次采样的平均值。锁相环本质是个慢环路用不着跟电流环路一样快。降低锁相更新频率还能减少计算负担本文还有配套的精品资源点击获取
返回列表