单相并网逆变器SOGI-PLL锁相环原理与C语言实现
2026/8/30 6:15:30 网站建设 项目流程

简介:本资源是一套面向电力电子与嵌入式系统开发者的二阶广义积分器锁相环(SOGI-PLL)C语言实现代码,专为单相电网相位实时跟踪场景设计,适用于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. 为什么单相锁相要专门搞SOGI

1.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漂到51Hz,SOGI依然能保持近乎完美的正交输出,这是固定频率滤波器做不到的。

之前听一个朋友说:“单相并网锁相,只要把SOGI想成一个可以随频率自动调整的90度移相器就行了。”这个理解方向是对的,移相器的角色就是SOGI,而移相精度由频率自适应保证。

2. 二阶广义积分器的正交信号生成原理

2.1 传递函数与幅相特性

SOGI的连续域传递函数标准写法如下,其中k是阻尼系数,ω是谐振角频率(这里就是锁相环输出的角频率估计值):

α路(带通输出): D(s) = k·ω·s / (s² + k·ω·s + ω²)

β路(低通输出): Q(s) = k·ω² / (s² + k·ω·s + ω²)

在谐振点s=jω处,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α=x1,vβ=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直接采到的原始值比如1800(12位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的MCU(Cortex-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) { /* 初始化参数: * k=1.414, 带宽fn=100Hz对应的Kp=888.6, Ki=394784 */ 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带宽变窄,滤波效果更好,输出信号更干净,但正交信号生成的动态过程变慢,锁相环整体响应变迟钝。如果电网电压突然跌落或相位跳变,重新锁定需要更长时间。

我的习惯是,常规并网逆变器场景直接上k=1.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.707,f_n=100Hz,那么ωn=2π×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.414100HzKp=888, Ki=394784均衡考虑动态与滤波
弱电网/强谐波环境1.050HzKp=444, Ki=98696优先保证锁相角度稳定
动态响应要求高(比如UPS切换)1.414200HzKp=1777, Ki=1579136锁相速度快,但需要入口滤波配合
三相不平衡检测(DSOGI)1.41480-120Hz同上取中间值每相独立SOGI,PI共用或独立均可

调参顺序上我的建议是:先固定k=1.414,把PI带宽从100Hz开始,往高或者往低扫,观察实际运行中并网电流THD和锁相角阶跃响应,找到最合适的点。不要一上来就两个参数一起调,否则出了问题根本不知道是SOGI响应慢还是PLL带宽不够。

5. 工程现场最容易踩的坑

5.1 直流偏置污染β路

这个坑是实际调试中非常容易遇到的。电网电压采样调理电路即使做了交流耦合,也会因为运放偏置、地电位差等原因引入一个很小的直流偏置。比如信号调理把±300V电网电压转成±3V,直流偏置可能只有10mV,看起来无伤大雅。

但SOGI的β路传递函数Q(s),在s=0处的直流增益并不是零。把s=0代入Q(s),得到Q(0)=k,当k=1.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×(w+w_prev)/2。这两种做法都很轻量,实际项目中我一般是直接加个0.5×ts×w的补偿项,代码改动最小。

5.3 上电初始化慢与重新锁定

如果SOGI-PLL从零状态初始化,上电瞬间x1和x2都是0,锁相环需要先建立正交信号,然后PI再慢慢收敛,整个过程可能需要几十毫秒。对大多数并网应用来说几十毫秒可以接受,但如果你做的是UPS从旁路到逆变器的无缝切换,这个时间就太长了。

一个非常有效的初始化技巧:上电后第一次采样得到电压瞬时值vg0,直接把x1预置为vg0/VG_PEAK,x2预置为0,θ预置为0。因为电网电压是连续变化的,x1初始值接近真实电网电压,SOGI就能从接近稳态的位置开始运行,锁定时间可以缩短一半以上。

如果上电时你还能大概估算出电网相位(比如通过过零检测捕获到第一次过零),可以把θ也预置到对应相位,锁相环几乎是瞬时就锁定。但要注意,过零检测的过零点判断本身就有误差,粗估可以,别指望它预置得很准。

另一个相关问题是电压跌落恢复后的重新锁定。当电网电压突然跌落到很低的水平,vq误差信号会变得很小,PI积分器如果保持之前的输出,恢复后可能有一个较大的相位调整过程。我习惯在检测到电压有效值低于某阈值时,把PI积分器清零并让锁相环保持自由运行状态,等电压恢复后再重新投入闭环。这个策略类似于通信里的“失步重捕”,能避免恢复瞬间的大角度阶跃。

5.4 采样噪声与开关毛刺

电网电压采样信号里经常混杂着开关器件的高频毛刺。如果ADC采样点恰好落在PWM开关沿附近,毛刺尖峰直接进来,vq会有明显跳变,锁相角也会跟着抖一下。

处理办法有几个层面。首先是硬件层面,采样调理电路要加RC低通滤波,截止频率大概在几kHz到几十kHz之间,把开关频率分量的幅值压下去。其次是软件层面,最有效的做法是把ADC采样触发点安排在开关周期的中间位置,也就是远离开关沿的地方,这对PFC和逆变器来说都是常规操作。如果这样还不够,可以在SOGI前面加一个简单的滑动平均滤波器,或者把锁相环更新频率降为控制频率的一半、做两次采样平均后再进SOGI。实测下来效果很好,代码也就多几行。

另外一个经验是,锁相环的更新频率不一定非要跟控制频率完全一致。控制环路跑20kHz,锁相环完全可以每两个采样周期(10kHz)才更新一次,用上一拍的采样值或者用两次采样的平均值。锁相环本质是个慢环路,用不着跟电流环路一样快。降低锁相更新频率还能减少计算负担

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询