分布式光伏大规模接入后,配电网的电压问题会从“局部的末端电压越限”变成“整个馈线范围内的系统性波动”,这时候再靠单点调压或者网侧配合,效果都很有限。我最近在做一个含分布式光伏的配电网集群划分与集群电压协调控制项目,用Matlab完成了全部仿真和代码实现,这篇文章把思路、算法、关键代码和踩过的坑都整理出来,给做配电网方向的研究生和工程师做个参考。
1. 分布式光伏并网后,配电网电压问题到底出在哪
先搞清楚一个基础问题:分布式光伏大量并网,配电网电压到底为什么撑不住。
传统配电网的运行场景是“单电源、辐射状”,潮流从变电站流向负荷,电压沿着馈线方向逐步降低。末端电压最低,这是常规认知。但光伏接入之后,馈线中间多了一个“注入源”,潮流方向变了,电压分布也跟着变形。光伏出力大的时候,馈线上可能出现逆潮流,也就是从负荷侧往电源侧送功率,末端电压不但不掉,反而可能被抬得很高。如果光伏接入集中、出力波动又大,电压越限就不是某一个节点的问题,而是一整段馈线一起越限。
这个问题的本质是“功率注入与负荷需求的空间不匹配”。光伏有功出力无法就地消纳时,只能沿着馈线往上送,馈线阻抗上的电压降落方向反转,才导致电压上升。
我在做这个项目时用的标准算例是IEEE 33节点系统,分别在节点10、17、21、24、32接入分布式光伏,总容量接近系统峰值负荷的40%。在正午光照饱和、负荷较轻的时段,仿真显示节点17的电压能到1.083 pu,远超1.05 pu的允许上限。这个数据基本复现了现实中“光伏反送导致末端电压越限”的典型场景。
那么问题来了:怎么控制?
最直接的办法是按单台逆变器就地控制,比如有功削减、无功补偿,够用但不高效。每台逆变器只感知本地电压,无法判断自己动作对邻居节点的影响,往往出现“局部压下去了、隔壁又顶上来”的连锁反应。更合理的思路是:把强耦合的节点划到一个集群,集群内部自治控制,集群之间再协调,这样既能降低控制变量维度,又能利用空间上的强相关性做精细化调节。
这就是这个项目的核心——先做集群划分,再做集群电压协调控制,全部在Matlab里实现。
2. 集群划分的逻辑:电气距离而不是地理距离
2.1 为什么不能按行政区划或馈线长度来分群
很多初学者会按地理就近或者馈线物理长度来做集群划分,这是最常见的一个误解。电气工程里的“距离”不是空间距离,而是阻抗距离。
两个节点之间如果阻抗很小,电压分布强相关,调整其中一个节点的注入功率,另一个节点的电压响应很灵敏,那么这两个节点应该是“近”的。反之,即使地理上挨在一起,但中间隔着大阻抗变压器或者长馈线,它们之间的电压耦合也很弱,分在一群意义不大。
集群划分的前提就是“寻找电压耦合强相关的节点集合”,工程上把这个问题转化为在电气距离矩阵上做社区发现。
2.2 电气距离矩阵怎么构造
对含光伏的配电网,我建议使用基于潮流灵敏度的电气距离定义。核心是求出电压-有功灵敏度和电压-无功灵敏度矩阵,再把它们综合成一个耦合度矩阵。
具体做法是这样的:
% 基于潮流雅可比矩阵的电气距离计算 % 假设已经用牛拉法求得稳态潮流,J 为雅可比矩阵 % 这里取 [dP/dV, dQ/dV] 的综合灵敏度 % 逆矩阵分块 invJ = inv(J); % 提取电压幅值对有功、无功注入的灵敏度 dVdP = invJ(1:nb, 1:nb); % 节点电压对有功注入变化 dVdQ = invJ(1:nb, nb+1:2*nb); % 节点电压对无功注入变化 % 综合灵敏度矩阵 S = abs(dVdP) + abs(dVdQ); % 电气距离矩阵(归一化后求倒数) D = 1 ./ (S + eps); D = D ./ max(D(:));这里的内在逻辑是:灵敏度越大,说明两节点相互作用越强,电气距离越小。后来我测试过好几种电气距离定义,包括纯无功灵敏度、有功无功加权求和、基于雅可比矩阵最小奇异值的奇异值分解方法,最终在实际控制效果上,无功灵敏度为主、有功灵敏度为辅助的加权形式表现最稳定。
2.3 用模块度函数指导分群
电气距离矩阵算出来后,下一步是决定“怎么切分”。这里我用了模块度函数,这是复杂网络里社区发现最经典的优化目标。
模块度的物理意义是:划分后的集群内部连线密度相比随机网络的提升程度。模块度越大,说明集群内部耦合越强、集群之间耦合越弱,这个划分越“自然”。
对配电网场景,我把原始模块度公式做了改进,引入了电气距离加权:
% 其中 A(i,j) 是电气耦合权重矩阵,e 是连边权重期望 % 模块度 Q = (1/2m) * sum(A(i,j) - k_i*k_j/(2m)) * delta(c_i, c_j) m = sum(W(:)) / 2; Q = 0; for i = 1:nb for j = 1:nb if cluster_id(i) == cluster_id(j) Q = Q + (W(i,j) - sum(W(i,:))*sum(W(j,:))/(2*m)); end end end Q = Q / (2*m);在实际工程中,全网节点逐个判断效率太低,我采用社团发现里最经典的Louvain算法做多次迭代合并,从每个节点独立成群开始,逐步合并使模块度增益最大的集群,直到模块度不再上升。对33节点系统,这个方法毫秒级就能收敛,哪怕是几百上千节点的真实馈线系统,计算也完全可接受。
2.4 分群数怎么定
这是另一个很现实的问题:到底分成几个集群合适。
如果凭感觉定K值,比如定成3个或5个,通常会出问题。集群太少,集群内部节点数太多,控制维度还是降不下来,起不到“自治”的效果;集群太多,每个集群就剩两三个节点,协调控制又没有意义了。
我是这样处理的:不预设K值,用模块度最大化自动确定集群数。在IEEE 33节点系统上,最优划分结果一般是4到5个集群。每个集群里的节点都在5到9个左右,电压耦合强,基本沿着一条馈线自然聚在一起。这个结果比经验设定K值要合理得多,因为它完全基于电气特征,不掺主观因素。
3. 集群电压协调控制:群内自治加群间协调
3.1 控制框架的整体设计
集群划分完成后,控制策略的顶层设计分成两层:
第一层是“群间协调”。调度中心根据全网量测信息,计算各集群的电压越限程度和控制需求优先级,下发各集群的电压调节目标值。这一层不直接控制逆变器,只输出“各集群需要调节到多少电压、需要多少无功/有功支撑量”的指令。
第二层是“群内自治”。每个集群的本地控制器接收群间协调下发的电压目标,再在集群内部优化分配各光伏逆变器的无功出力和有功削减量。这一层的通信只发生在集群内部,不需要和全网通信,解决了集中式控制在通信上的瓶颈。
这个分层结构和传统的“集中式一次算完”方案有本质区别。集中式方案对整个配电网所有可控设备做全局优化,目标函数全局最优,但计算量和通信量都很大,而且对通信可靠性的要求极高,只要一个终端掉线,全局优化就可能失败。分层方案的好处是故障隔离——某个集群通信出问题,只影响该集群,不会拖垮全局。
3.2 集群间协调的灵敏度计算方法
群间协调层的核心是计算“集群间电压灵敏度”,也就是一个集群的电压调节量对另一个集群电压的影响。
这里用了等效灵敏度矩阵。对每个集群,定义它的等效无功注入量为集群内所有光伏逆变器无功出力之和,定义了集群等效电压为集群内所有节点的电压加权平均值。然后通过潮流计算求集群间的灵敏度关系:
% 集群间等效灵敏度计算 % 假设共有 K 个集群,S_cv(i,j) 表示集群 j 的无功/有功变化对集群 i 等效电压的影响 S_cv = zeros(K, K); for j = 1:K % 在集群 j 的所有可控逆变器上叠加一个小扰动 dQ bus_inject_Q(ctrl_buses{j}) = bus_inject_Q(ctrl_buses{j}) + dQ; % 重新潮流计算 [V_new, ~] = powerflow(bus_power, branch_data, bus_inject_Q); % 计算每个集群的等效电压变化 for i = 1:K S_cv(i, j) = (mean(V_new(cluster_buses{i})) - mean(V_base(cluster_buses{i}))) / dQ; end % 恢复注入 bus_inject_Q(ctrl_buses{j}) = bus_inject_Q(ctrl_buses{j}) - dQ; end群间协调控制器的决策逻辑是:当某个集群的等效电压越限时,先计算所有集群的灵敏度,选择灵敏度高且有功充裕的集群分配调节任务,避免“按下葫芦浮起瓢”。
3.3 群内自治优化分配策略
群内控制器收到电压目标后,要在集群内做无功-有功的协调分配。
优先使用无功调节,因为光伏逆变器的无功支撑不损失有功出力,经济性最好。无功不够了,或者无功调节效率太低,才启动有功削减。
群内的分配优化我用的是内点法实现的二次规划。目标函数是集群内各节点电压偏差最小化,同时惩罚有功削减量:
% 群内优化分配:目标函数+约束 % 决策变量 x = [Q_inv; P_curtail] H = diag([2*lambda_Q*ones(nq,1); 2*lambda_P*ones(np,1)]); % 加权 f = -2 * [S_Q; S_P]' * (V_target - V_measured); % 线性项 % 约束:逆变器无功出力上下限 A_ub = [I_nq, zeros(nq, np); -I_nq, zeros(nq, np)]; b_ub = [Q_max; -Q_min]; % 约束:有功削减不超过当前出力 A_ub2 = [zeros(np, nq), I_np]; b_ub2 = P_curtail_max; % 用 quadprog 求解 x_opt = quadprog(H, f, [A_ub; A_ub2], [b_ub; b_ub2]);这里有一个关键的处理:在目标函数里,无功调节的权重lambda_Q取得比较小,有功削减的权重lambda_P取得很大,比如100倍甚至更高。作用就是在不削减有功的前提下优先用无功。只有无功调节能力饱和了,quadprog才会被迫选择消耗有功的解决方案。
3.4 控制死区与滤波器设计
实际仿真中最容易忽略的细节是死区设置和通信延迟。
如果对每个电压偏差都做控制动作,任何微小的电压波动都会触发逆变器调节,不仅动作频繁、设备寿命受损,而且控制器之间容易出现振荡。我加了电压死区,电压偏差在0.01 pu以内不动作,超过死区才启动控制。
另外,实际系统的量测值都有噪声和波动,直接用瞬时值做控制决策会导致抖振。我在控制链路的每个环节都加了低通滤波器,对电压量测值做一阶惯性滤波后再送进控制器。滤波时间常数取0.5秒左右——太小滤不掉波动,太大会让控制响应滞后,增加超调。
4. Matlab实现的关键环节:从数据准备到潮流计算
4.1 基础数据准备与输入结构
整个Matlab实现的第一步是构建配电网基础数据。我用的是Matpower的case格式,但针对配电网做了一些扩展。
节点数据的核心字段包括:节点编号、有功负荷、无功负荷、光伏接入容量、光伏初始出力、电压初始值。我统一用结构体数组存放,方便后续函数调用:
% 初始化IEEE 33节点配电网数据 nb = 33; % 节点数 bus_data = zeros(nb, 7) + 1e-6; % 列含义: [节点号, 基准电压(kV), 有功负荷(MW), 无功负荷(MVar), 光伏容量(MW), 光伏初始出力(MW), 电压初始值(pu)] % 以IEEE 33节点标准负荷数据做原型,填入光伏参数 pv_buses = [10, 17, 21, 24, 32]; pv_capacity = [0.4, 0.5, 0.35, 0.45, 0.6]; % MW pv_initial = [0.36, 0.47, 0.32, 0.42, 0.55]; % 初始出力90% for i = 1:length(pv_buses) bus_data(pv_buses(i), 5) = pv_capacity(i); bus_data(pv_buses(i), 6) = pv_initial(i); end我这里有个建议:数据准备阶段的变量命名和数据结构一定要做清晰的注释,尤其是这种多列矩阵的写法。整个项目做到后面,最耗费时间的往往不是算法本身,而是回头排查数据格式错误。
4.2 潮流计算的三种实现路径对比
集群电压控制每一次迭代都要调用潮流计算,所以潮流计算的速度和稳定性直接影响整个项目的可行性。我对比了三条路:
第一种,用Matpower的runpf函数。优点是不用自己写潮流代码,可靠性高,精度好。缺点是每次求灵敏度时都要重新计算结果矩阵,数据传递开销大,而且Matpower输出结构比较重,很多字段用不上。
第二种,自己实现牛顿-拉夫逊法。这段代码我写在了项目里,核心就是构造雅可比矩阵然后迭代求解:
% 牛顿-拉夫逊潮流计算的简化迭代主体 function [V, converged] = my_pf_powerflow(Ybus, Sbus, V0, tol, max_iter) V = V0; for iter = 1:max_iter % 计算节点注入电流 Imis = conj(Sbus ./ V) - Ybus * V; % 计算功率失配量 Sbus_calc = V .* conj(Ybus * V); dS = Sbus - Sbus_calc; % 只取真实节点(PQ+PV)的失配量 dS = dS(2:end); % 求解修正方程 J = make_jacobian(Ybus, V, Sbus_calc); dV = J \ dS; % 更新电压 V(2:end) = V(2:end) .* (1 + dV(1:length(dV)/2)) + 1i*dV(length(dV)/2+1:end); if max(abs(dS)) < tol converged = true; break; end end end第三种,用powerflow对象或者Simulink建模。以我的经验,纯脚本环境下自己实现牛顿-拉夫逊法是最合适的。它可以让你完全控制雅可比矩阵,在计算集群灵敏度的时候能够复用同一套矩阵结构,不用重复计算。
4.3 时变出力曲线与典型日场景生成
电压控制是动态过程,不能只看单一时段截面,我在Matlab里生成了典型日的光伏出力曲线和负荷曲线。生成逻辑用的是Beta分布模拟光伏出力随机性,负荷曲线用分段线性插值。
时间分辨率取15分钟一个点,一天共96个点。每个点都做一次潮流计算和一次控制决策,构成完整的日内电压控制仿真。
这里特别提醒一个坑:如果使用纯随机生成功率曲线,仿真结果的可复现性会很差。我做了两件事:第一,用rng设置种子值,保证每次跑出来的场景一致;第二,在论文和项目报告里记录了具体种子值,保证后续实验对比时基准场景不变。这个小习惯对做研究的人非常重要。
5. 仿真结果与方案效果对比
5.1 集群划分结果
在IEEE 33节点系统上,用改进的模块度函数和Louvain算法划分,最终得到4个集群:
| 集群编号 | 包含主要节点 | 光伏接入节点 | 集群特征 |
|---|---|---|---|
| 集群1 | 1-10 | 10 | 主馈线中段,距离电源中等 |
| 集群2 | 11-18 | 17 | 末端馈线,电压最敏感 |
| 集群3 | 19-22, 33 | 21 | 分支馈线,容量较小 |
| 集群4 | 23-32 | 24, 32 | 远末端,长馈线,电压问题最突出 |
集群2和集群4是电压越限的重灾区,集群1没接光伏但承担着为其他集群提供无功支撑的角色。
这个划分结果的最有价值信息是:集群1虽然不装光伏,但它位于主馈线靠近电源侧的中间位置,对下游集群电压的调节灵敏度极高。在群间协调时,我会重点考虑集群1的光伏逆变器为集群2和集群4提供无功支援。
5.2 电压控制效果
我设置了三个对比方案:
- 方案A:不控制,光伏按最大功率出力
- 方案B:就地控制,各逆变器只根据本地电压做无功补偿
- 方案C:本文提出的集群划分+集群电压协调控制
在光照最强、负荷最轻的午间时段,仿真结果对比如下:
| 指标 | 方案A(不控制) | 方案B(就地控制) | 方案C(集群协调) |
|---|---|---|---|
| 最大电压(pu) | 1.083 | 1.057 | 1.042 |
| 最小电压(pu) | 0.983 | 0.991 | 0.997 |
| 电压合格节点比例 | 61.1% | 87.9% | 100% |
| 有功削减总量(MW) | 0 | 0.08 | 0.02 |
| 无功补偿总量(MVar) | 0 | 1.24 | 1.58 |
关键信息是:方案C在完全消除了电压越限的同时,有功削减量只有方案B的四分之一。无功补偿量比方案B多了0.34 MVar,但换来了更少的弃光,整体经济性明显更好。
这背后的机理很清晰:方案B是就地控制,每个逆变器只感知本地电压,末端节点靠自己的逆变器扛压,扛不住就要削减有功。方案C通过集群协调,让灵敏度高的上游节点承担更多的无功补偿任务,末端节点的逆变器尽量保持有功出力,最终全局最优。
5.3 全天96点连续仿真验证
只看午间极端时刻不够。我做了一整天的动态仿真,从早上6点到晚上22点,用15分钟间隔的96个断面全部跑通。
在上午8点到10点和下午15点到17点这两个时段,光伏出力爬坡快,电压波动最剧烈。这个时段里方案A电压越限时间累计达到72分钟,方案B只有18分钟,方案C基本没有越限。另外我还记录了逆变器动作次数:方案B平均每台逆变器全天动作46次,方案C只有23次。控制动作少了整整一半,这对实际设备的寿命非常友好。
6. 项目实现中的踩坑记录与工程化建议
6.1 雅可比矩阵奇异问题
项目走到一半,出现过一个很头疼的Bug:潮流计算在某些断面不收敛,报雅可比矩阵奇异。
排查了很久,最后发现原因是有功负荷太低导致潮流方程接近奇异——轻载时段部分节点的P-V关系灵敏度趋近于零,雅可比矩阵条件数极大,数值上不可逆。
解决办法是在潮流计算前加了负荷水平判断,如果系统净负荷低于一个阈值,就先对有光伏接入的节点做一次等效负荷修正,让潮流方程有解。更稳妥的做法是在牛拉法迭代中增加阻尼因子,每次修正量乘以一个0.8到1.0之间的松弛系数,防止大步长导致数值振荡。实际测试中阻尼因子放在0.9最保守稳定。
6.2 集群划分结果不稳定的情况
在用模块度方法做集群划分时,如果光伏出力的波动模式变化比较大,比如晴转阴这种剧烈的天气过程,某个集群的划分结果可能跟正常运行状态下不一样。
这就带来一个实际工程问题:控制分区不能频繁改变,否则控制器参数要跟着切换,甚至会出现控制指令在不同集群间绕来绕去。我的处理方式是给集群划分加一个“死区判定”:只有模块度增益变化超过10%才允许触发重新划分,否则沿用上一次的集群结构。仿真验证了这种滞回策略能让分区结果在一整天内保持稳定。
6.3 代码性能优化
33节点系统规模小,Matlab跑起来毫无压力。但为了证明方案能扩展到更大的系统,我用同一套代码做了135节点的算例,发现运行时间增长了近10倍。细究之后找到三个瓶颈:
- 潮流计算的雅可比矩阵重复构造次数太多,改成每次迭代复用和增量更新,速度提升约30%
- 灵敏度的扰动法计算需要多次潮流,改为解析法直接通过雅可比逆矩阵求灵敏度,速度提升40%
- 循环里频繁使用cellfun,改成批量矩阵运算,整体又快了10%左右
三个优化加起来,整个仿真从原来的95秒降到了38秒。当然,对日常研究来说这个优化不是必须的,但如果你想做大量蒙特卡洛场景的反复仿真,性能优化必不可少。
6.4 给同一方向研究者几条实用建议
第一,做集群划分之前,先在平面图上画出来电气距离矩阵的heatmap。很多时候聚类算法跑不出合理的群,是因为离群点和孤立节点干扰了全局模块度的计算。目视检查一下矩阵热力图,能节约大量排查时间。
第二,控制器的采样周期不要设太短。光伏逆变器的响应速度是毫秒级,但配电网的调控通信周期通常是秒级,采样周期设成5秒到10秒比较合理。设太短会让控制器一直在追逐瞬时波动,动作频繁但实际改善有限。
第三,一定要做控制策略对通信延时和量测噪声的鲁棒性测试。我后面在两个算例里都叠加了高斯白噪声和随机通信时延,幅度设为电压量测均方根误差的1%,时延设为100到300毫秒随机值,结果方案C依然能保持电压在合格范围内,但方案B出现了控制振荡的迹象。这和现场工程师反馈的现象很一致。
7. 从仿真到工程落地,还需要补哪些课
仿真跑通只是第一步。如果你准备把这个项目落地到实际的台区或园区配电系统,还有几个问题需要提前考虑。
第一是通信架构。集群协调控制依赖双向通信,群间协调器需要采集各集群的等效电压和可调容量,群内控制器需要给各逆变器下发无功/有功指令。目前的配电网自动化系统基本能支持这种通信,但安全性、实时性和带宽保障都要重新设计。至少要做双链路冗余,防止单点通信故障导致控制指令丢失。
第二是可观测量问题。仿真中是所有节点电压都已知,实际配电网不可能在每个节点都装量测,特别是低压台区。这时就需要结合AMI(用电信息采集系统)的历史数据和少量实时测点做状态估计,用估计值代替实测值送入控制器。我后面在另一个项目里尝试了直接用配电网状态估计器的输出做控制输入,效果略有下降但仍在合格范围,这部分可以作为后续扩展方向。
第三是光伏逆变器的控制接口。实际逆变器厂家的通信协议各不相同,Modbus、IEC 61850、甚至自有云平台协议都有。要让集群控制器统一指挥,必须先做一个协议适配层,把各家逆变器的控制接口统一成标准化的有功/无功指令接口。这个适配层的工作量往往比算法本身还大,立项时要留足预算。
还有一点容易被忽视的是光伏逆变器的无功容量边界。逆变器在满发时无功容量几乎为零,只能靠降低有功来换无功容量。这就意味着群间协调层在分配无功任务时,必须考虑各光伏当前的有功出力。我建议在控制器的线性化约束里加入P-Q容量耦合约束,否则到了午间光伏满发的时刻,你给逆变器下发的无功指令会超出它的物理极限,控制器在仿真里没问题,在工程现场就会直接保护动作。