STM32单相逆变器并网控制:锁相环与双闭环实现 说来也巧这几年做分布式能源的项目单相逆变并网几乎是绕不过去的基础功。很多朋友一开始都在纠结一件事电网明明是 220V/50Hz 的交流想把自己直流端发出的电送进去凭什么要让电流大小和方向都听电网的话答案就藏在“锁相环、DQ 变换、双闭环控制”这一套组合拳里。这篇文章我把这套控制在 STM32 上的落地过程完整写出来包括数学原理、采样时序、PI 参数整定思路、实测踩过的坑谁想从零搭一台能并网的单相逆变器这篇可以直接当工程笔记来翻。1. 整体方案设计先想清楚再动焊枪1.1 并网逆变器和离网逆变器的本质区别很多人把离网逆变器做过一遍就以为并网只是“多一根线插到插座上”这个想法非常危险。离网逆变器的任务是维持一个稳定输出电压你的负载消耗多少电流是负载说了算并网逆变器恰好反过来电网电压是已经确定的刚性源你的逆变器必须输出一个“可控大小和相位的电流”注入电网。说得更直白一点离网控制的是电压并网控制的是电流。既然控制对象变了整个控制架构就得重来。并网逆变器内部一般是一个 DC/DC 前级比如 Boost加一个单相全桥 DC/AC 后级DC/AC 的输出端通过电感接到电网。电网电压是强交流源电感两端电压差决定了电流变化率所以只要我们能精确控制逆变桥输出电压的大小和相位就可以控制电感电流的波形。这个“精确控制”需要一个基准也就是电网电压的实时相位。锁相环就是拿来干这个的它给你一个不停更新的电网相位角所有后续运算都围绕这个角度转。1.2 双闭环到底在闭环什么常见的双闭环不是固定搭配要看你控制哪个量。最典型的单相并网架构是“电流内环 电压外环”或“电流内环 功率外环”。这里我把外环做成母线电压环内环做电感电流环。母线电压环负责维持直流侧电压稳定输出作为电流环的幅值指令电流环负责让电感电流实时候跟住参考信号保证并网电流正弦且和电网电压同频同相。为什么要两层环因为单靠一个电流环没法同时解决“直流母线电压抗冲击”和“并网电流波形质量”两个问题。电压环带宽要低一些滤掉功率波动电流环带宽要高一些压制谐波和快速扰动。两层环之间嵌套就形成了串级控制。实际工程里我会把电流环放在 10kHz 左右的控制周期里处理电压环可以放到 1kHz 甚至更低频率通过分时中断实现而不是傻乎乎每个周期都算一遍这样 CPU 负载能省不少。1.3 控制周期的选择STM32 主频一般做到 168MHz 或更高驱动一个 20kHz 开关频率的桥臂绰绰有余。最常见做法是让 PWM 定时器计数溢出或比较匹配时刻触发 ADC 采样再在 ADC 中断里完成整个控制运算。20kHz 意味着控制周期 50us在这个时间里要完成读取两路 ADC桥臂电流、母线电压、电网电压跑一次 SOGI-PLL做一次 Clarke/Park 变换执行两个 PI 控制器再反 Park 得到调制波更新 PWM 比较寄存器。实测在这种资源配置下整个中断只需不到 10us剩下的时间留给监控、通信、保护完全够用。2. 锁相环设计与实现别用过零检测糊弄事2.1 过零检测为什么不行新手最爱采用的方案是定时器输入捕获抓电网电压过零点然后计算频率和相位。听起来简单实际上有四个硬伤一是过零点和真实基波相位之间存在畸变角电网电压稍有谐波或毛刺零点就抖动二是每一个工频周期只更新一次相位动态响应太慢电网频率从 50Hz 跳到 50.2Hz 时你要等大半个周期才反应过来对电流控制来说已经晚了三是过零附近的电压变化率最小叠加噪声后相位误差可能达到好几度四是它天生无法提供两相正交的相位信息后面想直接用 Park 变换还得多绕一道。所以我们用闭环锁相最常见的工程方案是 SOGI-PLL二阶广义积分器锁相环。它本质上先构造一个虚拟正交信号把单相电压变成“αβ 两相静止坐标系”的虚拟两相系统然后送到 Park 变换再用 PI 调节器把 q 轴分量调到零。q 轴调零意味着锁相角完全跟踪电网正序电压相位。2.2 SOGI 正交信号发生器SOGI 的作用是输入一个正弦信号v输出两个信号一个和输入同相位同幅值的vα一个滞后 90° 的vβ。它的传递函数是经典的带通和低通组合我这里直接给出离散化后面的差分方程工程上这样写效率最高// SOGI 状态变量在 20kHz 控制周期内更新 float v_alpha, v_beta; float x1, x2; float k_sogi 1.414f; // 阻尼系数略大于 1 可加快收敛 float w_sogi 314.159f; // 标称角频率锁相环会实时更新这个值 void sogi_update(float vin) { x1 T_s * (x2 k_sogi * w_sogi * (vin - x1)); x2 T_s * (-w_sogi * x1); v_alpha x2; // 与输入同相 v_beta w_sogi * x1; // 滞后 90° }这个算法稳定、耗资源少注意一点w_sogi不能写死锁相环每次计算出的角频率要回灌给它。否则电网频率偏移时正交精度会变差进而导致解耦相位出现“稳态误差”。2.3 锁相环 PI 调节器与角频率积分锁相环的控制对象不是相位而是 q 轴分量。当锁相角θ接近真实相位θg时Park 变换得到vq ≈ Vg * (θg - θ)所以 vq 就是相位误差的等价量。把 vq 送进一个 PI 调节器输出为角频率修正量Δω然后做积分得到锁相角θfloat vq; float pll_kp, pll_ki; float omega, theta; void pll_update(void) { // park 变换, v_alpha 来自 sogi float sin_t arm_sin_f32(theta); float cos_t arm_cos_f32(theta); vq cos_t * v_beta - sin_t * v_alpha; // PI 调节, 输出角频率 float error 0 - vq; pi_integral pll_ki * error * T_s; omega 2.0f * PI * 50.0f pll_kp * error pi_integral; // 角频率积分得到角度 theta omega * T_s; if (theta 2.0f * PI) theta - 2.0f * PI; }整定 PLL 参数时有个经验kp 决定锁相带宽ki 决定稳态静差消除能力。我常用的起步值是kp 200ki 8000在 50Hz 下不会震荡动态锁相时间大约 20~30ms。当然你的采样周期必须精确建议用 PWM 中断固定周期来跑这个函数不要放在 main 循环里否则定时抖动会把环路搞垮。3. DQ 变换把正弦世界变成直流世界3.1 为什么要多此一举电网电压和电流都是 50Hz 的正弦波直接用 PI 调节器跟踪正弦量理论上也能跟踪但实际做出来电流波形会带静差谐波也会比较难看。原因是 PI 调节器对直流输入的跟踪是无差系统对交流输入的增益却有限。所以经典方法就是把静止坐标系下的正弦量变换到旋转坐标系下让被控量变成直流分量再用 PI 调。这个思路放到日常生活里打个比方你坐在旋转木马上看远处的灯灯在你眼里是静止的人就能稳稳对准你要是站在地面看就得不停转脑袋肯定瞄不准。DQ 变换就是让你坐进旋转坐标系。3.2 从 αβ 到 dq 的完整推导先有 Clarks/V 变换。单相系统只有一个物理量从 SOGI 得到正交两相后直接把vα v_alpha、vβ v_beta作为静止坐标分量。然后做 Park 变换vd cos(θ) * vα sin(θ) * vβ vq -sin(θ) * vα cos(θ) * vβ这组变换既适用于电压信号也适用于电流信号。电流环里我对电感电流做同样变换得到id_ref和iq_ref。单相并网一般只控制有功所以iq_ref 0id_ref由母线电压环输出给到。为什么让 iq 为 0因为在选定的 d 轴对齐电网电压方向后d 轴电流代表有功电流q 轴电流代表无功电流。对于普通户用光伏并网功率因数要求接近 1q 轴电流置零即可。电网电压畸变或相位跳变时vq 不为零锁相环会调整相位让 d 轴重新对齐电压矢量。3.3 DQ 变换的代码落地代码上不要用正弦库函数现算STM32 的arm_sin_f32和arm_cos_f32是查表方式比 math.h 快得多。实际代码void park_transform(float v_alpha, float v_beta, float *vd, float *vq, float theta) { float s arm_sin_f32(theta); float c arm_cos_f32(theta); *vd c * v_alpha s * v_beta; *vq -s * v_alpha c * v_beta; } void inverse_park(float vd, float vq, float *valpha, float *vbeta, float theta) { float s arm_sin_f32(theta); float c arm_cos_f32(theta); *valpha c * vd - s * vq; *vbeta s * vd c * vq; }反 Park 在电流环后面用把控制得到的vd_out、vq_out反变换回 αβ 静止坐标得到调制波。单相全桥有两种调制控制方式我这里用的是双极性 SPWM 的单臂调制实际把vβ当作调制波和载波比较生成互补 PWM。如果你想让母线电压利用率更高可以考虑在调制波中注入三次谐波或零序分量这是后话。4. 双闭环控制设计电流环与电压环的配合4.1 电流内环被控对象建模忽略开关纹波电感电流方程是L * di/dt v_inv - v_grid。其中v_inv是逆变桥输出电压平均值v_grid是电网电压。写成传递函数形式控制量到电流的传递函数是1/(Ls)的一阶纯积分再加一个由 PWM 产生的等效延时。工程整定时把电感值 L、开关频率 fs、母线电压 Vdc 三个参数搞清楚就能反推电流环 PI 增益。电流环用 PI连续域参数可以这样粗算设期望电流环带宽ωci 2π * 2000 rad/s取 PI 零点在约 1/5 带宽处则kp_i L * ωci ki_i kp_i * ωci / 5举个例子电感 2mH带宽取 2000Hzkp_i 0.002 * 2π * 2000 ≈ 25.1ki_i 25.1 * (2π*2000)/5 ≈ 63000。这两个值再根据实际示波器波形微调。方向是输出电流波形过冲大就把 kp 往下压有稳态误差就把 ki 拉起来。注意电流环输出要限幅不能让它超过调制波允许范围否则抗饱和回绕会让你环路翻车。4.2 电压外环设计直流母线电压环的输出是内环电流幅值指令母线电压的波动频率是 100Hz单相系统瞬时功率有二倍频波动所以电压环带宽不能太高一般取 10~20Hz。带宽太高会让电流参考信号带上比较明显的二倍频波动并网电流谐波会增加。我一般把电压环 PI 的kp_u 0.8ki_u 20做初值之后根据母线电压抗扰动能力调整。判断标准负载突变 30% 时母线电压跌落能被拉回 5% 以内并且不震荡。4.3 电流参考给定逻辑电压环输出的是直流量iL_d_ref但并网需要的是和电网电压同频同相的交流电流。所以我们要把直流参考量经过反 Park 变成交流指令。实际操作是在旋转坐标系里面做控制把电流误差过 PI 得到vd_out、vq_out然后反 Park 产生调制波调制波再次和电网电压前馈量相加。别忘了加电网电压前馈否则调制波的动态响应会偏慢PI 控制器还得费劲去抵消整个电网电压的 50Hz 周期扰动。单相全桥输出端电感上的电压约等于“调制波 × 母线电压”闭环的最后一级公式如下float mod_alpha v_alpha_ref v_grid_alpha; // 前馈这就是典型的前馈补偿思路显著降低电流环的周期性跟踪误差。5. STM32 工程实现细节中断、采样、启动流程5.1 ADC 采样时刻决定波形质量很多人在并在网上失败根源在采样时机。20kHz 开关频率下电感电流的纹波频率也是 20kHz。如果在 PWM 比较中点采样理论上能采到平均电流纹波对称抵消如果在开关切换瞬间采样采到的往往是毛刺最大点谐波一大片。我采用的方法是在定时器周期中点触发 ADC 注入组转换也就是 PWM 计数器向上计数到CCR/2时触发采样三个采样通道连续完成。注意 ADC 转换时间要留足我用的是 STM32 的 ADC 三通道采样时间加起来约 3us在 50us 控制周期里不算负担。5.2 控制中断任务编排我把任务分成三层最高优先级是 PWM 触发 ADC 中断里的核心控制包含 SOGI-PLL、DQ 变换、双闭环中间优先级是 1ms 心跳中断处理电压环慢速计算、功率计算、故障状态机低优先级是主循环里的监控和通信。电压环为什么放在 1ms因为电流环需要 20kHz 来精细跟踪 50Hz 正弦电压环面对的是 100Hz 纹波1ms 响应速度足够还能大幅减少 CPU 占用。伪代码结构void ADC_IRQHandler(void) { Read_ADC_Result(); // 1. SOGI PLL sogi_update(grid_voltage_measured); pll_update(); // 2. 电流采样 Park 变换 park_transform(iL_alpha, iL_beta, id_loop, iq_loop, theta); // 3. 电流 PI 控制 id_error id_ref - id_loop; iq_error iq_ref_filtered - iq_loop; vd_pi PI_update(id_error, pi_param_id); vq_pi PI_update(iq_error, pi_param_iq); // 4. 反 Park 电网前馈 inverse_park(vd_pi, vq_pi, mod_alpha, mod_beta, theta); mod_alpha grid_voltage_alpha; // 5. 限幅 更新 PWM mod_alpha Limit(mod_alpha, -0.95f, 0.95f); Update_PWM_Register(mod_alpha); }5.3 并网启动流程并网不是上电直接就合闸要分几步走。第一步母线电压软启动到设定值第二步锁相环开始跟踪电网相位观察vq误差小于阈值第三步设置电流参考为零让开环调制波等于电网电压前馈再闭合接触器或继电器第四步电流环使能参考值从零缓慢爬升到目标功率。这四步缺一不可尤其是合闸瞬间不能让电感电流发生突变否则会产生巨大冲击甚至烧掉功率管。我写了一个简单的状态机enum INVERTER_STATE { STATE_START, STATE_BUS_PRECHARGE, STATE_PLL_LOCK, STATE_RELAY_CLOSE, STATE_CURRENT_RAMP, STATE_NORMAL_RUN, STATE_FAULT };每个状态都有超时保护比如 PLL 锁定阶段最多给 200ms电流爬升阶段按每毫秒增加 0.5A 的斜率缓慢上升。实际测试中这个启动逻辑并网瞬间电网电流几乎没有冲击接触器闭合也不会听到明显“啪”的响声。6. 调试实录常见故障与排查经验6.1 锁相环无法锁定的三个原因我在调试过程中碰到过很多次锁相环锁不住或者振荡。最典型的三类一是 SOGI 输入的电压信号有小直流偏置导致正交分量输出偏置q 轴分量存在常数偏差PI 的输出就会把频率拉到一边去二是 PI 参数整得过快kp 太大导致锁相环出现极限环表现为 theta 周期震荡三是采样频率和控制频率不一致T_s写错整个离散模型就崩了。排查方法很简单先串口把vq打出来如果 vq 是一个不断振荡的正弦信号说明相位锁环在转如果 vq 相差挺大的直流优先查偏置和T_s。6.2 电流波形畸变排查顺序并网电流波形出现削顶、失真、毛刺时我习惯用排除法查四个点先看调制波限幅有没有触发限幅一般在 0.95若达到说明母线电压预留量不足需要提高母线电压或降低并网功率再看电流采样滤波时间常数采样点如果有毛刺要在 ADC 前加一阶 RC 滤波同时调整采样保持时间再查电感是否饱和电感电流太大时铁心饱和会导致电感量暴跌电流波形出现明显尖峰解决办法是增大电感体积或采用更低磁导率磁芯最后看开关管的死区补偿死区时间会导致低次谐波给调制波加一点死区补偿可以明显改善。6.3 并网保护逻辑到位再上电这里必须强调实验安全。并网实验接线看似简单实际风险很高电网侧、直流侧、功率板、示波器地线、隔离变压器之间一旦形成错误回路轻则炸机重则触电。我的安全习惯是所有控制实验先经过 1:1 隔离变压器给电网电压给直流侧加保险丝和预充电电阻功率板和信号板分开供电弱电控制盒独立接地任何改动接线都必须先断电再操作。第一次上电时不要直接接强电先用可编程电源慢慢抬升母线电压全程用示波器双通道确认调制波和驱动波形正常才敢合闸并网。7. 后续优化方向与个人心得单相逆变并网这套控制跑通之后下一步有几个很实际的优化方向。比如锁相环可以改成 DSOGI-PLL加入负序分量提取应对弱电网下的电压不平衡电流环可以加重复控制或准比例谐振控制专门抑制特定次谐波调制环节可以用单极倍频调制把等效开关频率翻倍在同样开关频率下显著降低电感纹波电流。做完这个项目最大的体会是纸上推导和实际工程差距最大的地方不在数学公式本身而在“时序和采样”这两个平时不太起眼的环节。我反复确认过的几个细节——ADC 临界点采样、控制中断的优先级设置、SOGI 的参数回调——每个都让波形和稳定性发生质变。这套系统现在跑在模拟电网的平台上母线电压稳定电流谐波基本在 3% 以内锁相动态过程 25ms 内完成。最后想提醒一句所有参数整定都必须结合自己的电感、电容、母线电压来调任何现成的参数都只能作为出发点。调试的过程会非常磨人但每解决一个波形畸变你对整个控制链路的理解就会加深一层。希望这篇实践记录能让你少走几个弯路并网系统一次点亮。