1. 为什么“源-荷-储”协同会成为配电网调度的核心命题
1.1 主动配电网到底“主动”在哪
我最早接触主动配电网这个概念,是在做分布式光伏并网仿真的时候。当时拿到一个IEEE33节点的算例,最直观的感受是:传统的配电网调度思路——也就是被动地按负荷预测去调节变电站出口电压、按最大负荷去校核线路容量——在大量分布式电源接入以后,根本玩不转。
原因不复杂。传统配电网是单相辐射状、潮流单向流动的,调度员只需要盯着变电站母线,把电压调合格、把馈线电流控制在限额内就完事。但分布式光伏、风电、储能一上来,潮流方向不再恒定,某些时段线路末端电压可能被抬到接近上限,某些时段馈线重载甚至倒送。这个时候,配电网必须“主动”起来:主动调节分布式电源出力、主动管理储能充放电、主动引导负荷参与调节。这就是主动配电网(Active Distribution Network, ADN)和传统配电网的本质区别——不是被动适应,而是主动干预。
而“源-荷-储”协同互动,就是这种主动性的具体落地手段。源(分布式光伏、风电等)、荷(可调节负荷)、储(储能系统)三者不再是各自独立的并网单元,而是被纳入同一个优化框架里协同调度。光伏出力大的时候,储能充电、负荷侧可调部分增加用电;光伏出力小或者晚高峰的时候,储能放电、可调节负荷削减用电。通过这种时间维度上的平移和互补,最终实现配电网的安全、经济、高效运行。
1.2 分布式光伏和储能接入后,传统调度为什么玩不转
我拿一个最简单的场景说:一个33节点的馈线,末端节点接了1.5MW光伏。晴天中午,负荷低谷,光伏大发,馈线末端电压会明显抬升。传统调度方案里没有光伏出力调节手段,只能靠变电站有载调压和无功补偿硬扛——但馈线末端电压和变电站出口电压之间隔着线路阻抗压降,常常调不过来。如果光伏继续增加,电压越限就得弃光。
储能接入以后,情况好了一些,但如果储能只是“固定策略”运行——比如设定晚上充、白天放,或者干脆按定时充放——仍然救不了中午的电压抬升。因为中午恰恰是光伏大发、储能通常不该充电的时段(电价高),但电压问题偏偏就出现在这个时段。所以固定策略本质上是开了环的控制,它没有感知电网状态。只有把储能纳入优化调度,让它在电压越限风险高的时段主动充电、在其他时段放电,才能真正解决问题。
这就是为什么“源-荷-储”必须是协同的、互动的,而不是各自为政。协同的含义是三者共享同一个优化目标;互动的含义是储能和负荷侧的决策会随源侧出力、电网状态的变化而动态调整。用数学语言说,这就是一个动态优化问题——决策变量随时间变化,约束条件在不同时段之间还有耦合。
1.3 IEEE33节点系统为什么成了事实标准
IEEE33节点系统在配电网研究里的地位,差不多相当于MNIST在手写数字识别里的地位——人人都在用,拿来就能跑,结果可复现。它是一个33节点、32条支路的辐射状配电网,基准电压12.66kV,基准功率10MVA,总负荷大约3715kW加2300kvar,首端节点0接上级变电站,另有5条联络开关支路可以在重构研究中闭合。
这套系统之所以流行,除了数据公开、模型简单之外,还有一个很重要的原因:它保留了配电网潮流计算的关键特征——辐射状拓扑、R/X比值偏高(不像输电网那样R远小于X)、电压对无功功率比较敏感。这意味着在IEEE33节点上做出来的调度策略,迁移到实际馈线上是有一定参考价值的。
我用这个系统做源-荷-储协同调度,核心思路可以概括成四步:第一步,在IEEE33节点上接入指定容量的分布式光伏和储能;第二步,构建以运行成本最小(或者网损最小)为目标函数的优化模型;第三步,用二阶锥规划(SOCP)把非凸潮流方程松弛成凸问题;第四步,在Matlab里用Yalmip建模、调用商业求解器求解,得到未来24小时的调度计划。
这篇文章就把这条技术链路完整拆开讲一遍,包括数学模型怎么建、代码怎么组织、参数怎么设、坑在哪里,以及Matlab实现时最容易出错的地方。
2. 三层联动:源-荷-储协同调度的数学模型拆解
2.1 目标函数:网损、成本还是电压偏差,先想清楚要什么
做优化调度,第一步不是写约束,而是想清楚目标。我见过不少同学一上来就写“综合成本最小”,但问他“综合”里包含什么,经常说不清。实际上,源-荷-储协同调度里,目标函数最常见的候选有三类:网损最小、运行成本最小、电压偏差最小(或者电压越限惩罚)。三者的区别很大。
网损最小适合纯技术分析,关注的是电网自身的经济性。运行成本最小则要考虑购电成本、分布式电源运行成本、储能充放电损耗成本、弃光惩罚等。电压偏差最小本质上是个电压质量控制问题,通常和其他目标加权组合。在工程实际里,我们通常用运行成本最小作为主目标,把网损成本、弃光惩罚都折算进成本项——这样不仅能得到调度方案,还能算出经济收益,文章和报告也更好讲。
我自己在仿真里用的是这样一个目标函数:
$$min \sum_{t=1}^{T} \left[ c_{buy,t}P_{buy,t} + \sum_{i \in \Omega_{DG}} c_{DG,i}P_{DG,i,t} + c_{loss}P_{loss,t} + c_{curt}P_{curt,t} \right]$$
其中,$c_{buy,t}$是t时段从上级电网购电的电价,$P_{buy,t}$是购电功率,$c_{DG,i}$是分布式电源的运行成本系数,$P_{DG,i,t}$是第i个分布式电源在t时段的出力,$c_{loss}$是网损折算成本,$c_{curt}$是弃光惩罚系数。T取24,对应未来一天的调度计划。
这里有个细节值得注意:购电电价是分时电价——峰时段高、谷时段低。储能协同调度的一个重要价值就是“谷充峰放”,在低电价时段充电,在高电价时段放电,在满足电网安全约束的前提下赚取峰谷价差。所以目标函数里的购电成本项,本质上是连接市场信号和物理调度的桥梁。
2.2 决策变量与约束条件的完整清单
优化模型的决策变量分三类:源侧变量(分布式光伏的有功出力、无功出力,如果有无功调节能力的线)、储侧变量(每个时段储能的充放电功率)、联络线变量(变电站出口的有功无功功率)。如果你连配电网重构一起做,还要加上联络开关状态变量——但那个问题复杂很多,这里先不谈。
约束条件可以归成五组:
第一组是潮流约束。配电网潮流用DistFlow方程描述。对于IEEE33节点这种辐射状网络,节点j的父节点是i,那么有:
$$P_{ij} - r_{ij}l_{ij} = \sum_{k \in C(j)} P_{jk} + P_{j,load} - P_{j,DG} - P_{j,dis} + P_{j,ch}$$
$$Q_{ij} - x_{ij}l_{ij} = \sum_{k \in C(j)} Q_{jk} + Q_{j,load} - Q_{j,DG}$$
$$v_j = v_i - 2(r_{ij}P_{ij} + x_{ij}Q_{ij}) + (r_{ij}^2 + x_{ij}^2)l_{ij}$$
其中$v_i$是节点电压的平方,$l_{ij}$是支路电流的平方。这个方程是非凸的——因为等式里藏着$P_{ij}^2 + Q_{ij}^2 = v_i l_{ij}$这样一个二次等式约束,直接求解很难。后面的二阶锥松弛就是针对这条路下手的。
第二组是电压和电流安全约束。节点电压要在标幺值的0.95~1.05范围内,支路电流不能超过容量上限。实际做的时候,电压约束容易被优化器“顶到极限”,所以很多人会在目标函数里加一个电压越限惩罚项——这也是个常见操作,目的就是让解更安全、更有工程意义。
第三组是分布式电源出力约束。光伏的出力上限由光照条件决定,每一时段都有对应的最大可发功率,实际出力不能超过这个上限。
第四组是储能约束。储能模型包括充放电功率上下限、SOC(荷电状态)递推方程、SOC上下限。这里最容易踩坑的是SOC的时序耦合——它把相邻时段连接起来了,不是每个时段独立的约束。
第五组是变电站出口功率约束。受变压器容量限制,出口有功无功功率都有上限。这一组约束直接决定了主动配电网和上级电网的交换功率,也是“源-荷-储”协同调度里源网协调的物理边界。
2.3 储能SOC的时序耦合:最容易被忽略的隐藏约束
很多同学第一次建储能模型,会把SOC当成每时段独立的变量来处理——每个时段写一个0.1~0.9的上下限就完了。这样模型当然能求解,但求出来的解在物理上根本不可能实现。因为储能本质上是一个能量容器,它在t时段的电量是在t-1时段电量基础上充入或放出一定能量得到的:
$$SOC_{t} = SOC_{t-1} + \eta_{ch}P_{ch,t}\Delta t - \frac{P_{dis,t}\Delta t}{\eta_{dis}}$$
其中$\eta_{ch}$和$\eta_{dis}$分别是充放电效率。SOC的初值$SOC_{0}$要么给定,要么作为待优化变量。末时段SOC通常还要约束到和初值相等(或者保持在一个水平),否则优化器会在最后一个时段把所有能量放光——这在运行上叫“储能末日效应”。
这个时序递推约束还带来一个连带问题:储能的充放电功率决策在全时间尺度上是耦合的。你不能只看一个时段决定充还是放,要把全天24小时的爬坡过程放进一个整体里优化。这正是为什么源-荷-储协同调度本质上是一个“日前调度计划”问题——在已知第二天光伏预测和负荷预测的前提下,一次性解出24小时的完整充放电计划。
我建模型的时候,习惯把SOC递推写成矩阵形式的等式约束,而不是循环里逐个写。原因有二:一是Yalmip支持向量化约束,写成矩阵形式代码更简洁;二是当T从24扩展到96(15分钟分辨率)时,矩阵形式不需要改模型结构,只是改变量维度。这个习惯对后续做滚动优化或者模型扩展非常有用。
3. Matlab实现架构:从DistFlow到二阶锥松弛
3.1 系统参数初始化与网络拓扑构建
Matlab实现的第一步是把IEEE33节点系统的网络参数导入工作区。系统本身包含33个节点、32条支路,每条支路有首端节点、末端节点、电阻(欧姆)和电抗(欧姆)参数。还有一个需要特别注意的细节:IEEE33节点系统的联络开关支路,在基础算例里是断开的,不参与潮流计算,但支路参数仍然要保留在数据文件里,方便后续做重构扩展。
我自己习惯的做法是建一个结构体数组保存所有支路数据,每一行对应一条支路,字段包括fbus、tbus、r、x,然后用稀疏矩阵把拓扑关系表达出来。判断每条支路的方向可以采用广度优先遍历:从节点0(变电站母线,配电网的根节点)开始,逐层向外扩展,每条支路的方向就是远离根节点的方向。这个步骤虽然简单,但直接关系到DistFlow方程里“父节点-子节点”的对应关系,必须确保方向一致,否则潮流约束会写错。
参数导入之后,建议先做一个连通性检查——用Matlab的graph函数构建网络,然后确认所有32个节点都从根节点可达。原因很现实:手抄IEEE33节点数据的时候很容易抄错一两个数字,导致某个节点和网络断开,而潮流方程在断开的节点上仍然“有解”(因为没有支路约束),最后算出来的调度方案看着正常,实际上那个节点的负荷全是凭空捏造出来的。
3.2 Yalmip建模:核心代码骨架
求解器选型和建模工具,我直接用的Yalmip加Cplex(或者Gurobi,两个都行,Gurobi这年头的许可证政策更宽松)。Yalmip是一个Matlab建模工具箱,好处是语法简洁、支持多种求解器切换,而且对二阶锥约束有原生支持——写约束就像写数学公式一样直观。
下面是核心建模代码骨架,包含了目标函数、DistFlow约束、电压约束和储能SOC递推:
% 定义决策变量(全部为标幺值) Pij = sdpvar(32, 24, 'full'); % 支路有功 Qij = sdpvar(32, 24, 'full'); % 支路无功 lij = sdpvar(32, 24, 'full'); % 支路电流平方 vi = sdpvar(33, 24, 'full'); % 节点电压平方 Pdg = sdpvar(33, 24, 'full'); % DG有功出力 Pch = sdpvar(33, 24, 'full'); % 储能充电功率 Pdis= sdpvar(33, 24, 'full'); % 储能放电功率 soc = sdpvar(33, 24, 'full'); % 储能SOC Pb = sdpvar(1, 24, 'full'); % 变电站出口有功 Qb = sdpvar(1, 24, 'full'); % 变电站出口无功 Constraints = []; % DistFlow约束(二阶锥松弛) for t = 1:24 for j = 2:33 i = parent(j); % 节点j的父节点 Constraints = [Constraints, ... vi(j,t) == vi(i,t) - 2*(r(i)*Pij(i,t) + x(i)*Qij(i,t)) ... + (r(i)^2 + x(i)^2)*lij(i,t)]; end for k = 1:32 i = branch(k,1); j = branch(k,2); Constraints = [Constraints, ... Pij(k,t)^2 + Qij(k,t)^2 <= vi(i,t)*lij(k,t)]; % 注意:这里的<=就是二阶锥松弛的关键,把等号放成不等号 end % 储能SOC递推 for j = 1:33 if hasStorage(j) Constraints = [Constraints, ... soc(j,t) == soc(j,t-1) + eta_ch*Pch(j,t) - Pdis(j,t)/eta_dis]; end end end写代码的时候有几个关键点要特别留意:一是所有变量都用标幺值(pu)而非有名值——IEEE33节点系统基准电压12.66kV、基准功率10MVA,标幺值处理以后数值都在0.01到1.5这个量级,求解器数值稳定性好很多;二是DistFlow里的电压约束用的是电压平方的下限0.9025和上限1.1025(对应0.95和1.05的平方),别写错成0.95和1.05。
3.3 求解器选型:为什么我劝你直接用Cplex/Gurobi
我自己早期试过用Matlab自带的fmincon去解这个非线性规划,结果很痛苦——求解时间几分钟起步,而且经常收敛到坏的局部最优点。后来换了Yalmip+Cplex的SOCP(二阶锥规划)路线,同样是24时段调度,几秒钟就能出结果,而且全局最优有保证。
这里面的原理值得说清楚。DistFlow方程里的非凸项来自支路潮流约束$P_{ij}^2+Q_{ij}^2 = v_i l_{ij}$。这个等式是非凸的,但有一个著名的松弛技巧:把等号放宽成小于等于号,也就是$P_{ij}^2+Q_{ij}^2 \leq v_i l_{ij}$。这个不等式恰好构成一个二阶锥约束,而二阶锥规划是一个凸优化问题,可以用内点法高效求解。
核心问题是:松弛之后的解,和原问题的解一样吗?答案是:在大多数辐射状配电网场景下,松弛是紧的——也就是松弛解满足等号关系,从而能够还原出真实的电压、电流和功率分布。但“大多数”不是“所有”,所以求解完要检查松弛间隙:逐条支路、逐时段检查$v_i l_{ij}$和$P_{ij}^2+Q_{ij}^2$的相对差,如果小于1e-4,就可以放心认为松驰是紧的。如果发现松弛不紧,常见做法是添加惩罚项或者调整初值再试。这个话题我后面还会专门展开。
一句话总结选型逻辑:用Cplex/Gurobi的SOCP求解器,不是因为它“高级”,而是因为它能把一个非凸不可解的问题转成凸问题,在几秒内稳定求解24时段全系统优化调度——这就是工程上能用的意思。
4. IEEE33节点数据准备与场景设计:别在源头埋雷
4.1 负荷曲线与光伏出力的典型日生成
IEEE33节点系统给的是各节点的峰值负荷数据——比如节点8的峰值有功负荷是200kW,节点24是420kW等等。要做24小时调度仿真,需要把这些峰值负荷延展成时间序列曲线。最简单也最常用的办法是采用一个典型日负荷系数曲线,把峰值乘以对应时段的负荷系数,得到该节点的逐时负荷。
这个方法虽然简单,却有充分的工程依据:配电网负荷虽然千变万化,但归一化以后的日负荷曲线形状高度相似——早晚高峰、中午低谷、夜间平段。我做仿真用的典型日负荷系数大致是这样:凌晨0点到6点系数在0.5~0.6之间浮动,中午12点到13点有个小高峰达到0.75,晚间19点到21点达到全天最高的0.95以上,其他时段在0.7~0.8之间。
光伏出力曲线的生成则需要考虑“反调峰”特性——光伏大发时段(10点到15点)恰好是负荷的午间低谷或平段。如果光伏渗透率高,这个时段就是电压抬升最危险的窗口。IEEE33节点接入光伏以后,考验的正是调度策略在这个窗口期的表现。
我建议把负荷曲线和光伏曲线都用矩阵生成——行对应节点、列对应时段,需求出力的节点把曲线填进去,没有分布式电源的节点对应行全零。这样后面在Yalmip里做约束时,矩阵运算效率高,不容易出维度不匹配的问题。
4.2 三种对比场景:不加储能、储能固定策略、协同优化
做仿真研究或者发论文,最怕的是只有一个场景——算出一个调度方案,没有对比,看不出协同控制的价值。常规做法是设置三种对照场景:
场景A是基础场景:只有光伏接入,没有储能,也不做任何优化调度(光伏满发、弃光允许但不优化)。这个场景是基准线,反映最原始的运行状态。
场景B是储能固定策略场景:储能按照“夜间低谷充电、白天高峰放电”的固定时间表运行,不感知电网状态。这个场景的储能也能削峰填谷,但调度效果取决于时间表定得好不好,模拟的是传统电池储能站的开环控制模式。
场景C是源-荷-储协同优化场景:储能充放电、光伏出力(允许弃光)、变电站出口功率全部纳入SOCP优化模型,在满足电压、电流、SOC耦合约束的前提下统一决策。
对比指标用三个:全天网损、全网最低电压(或者电压偏差积分)、全天购电成本。前两个反映电网安全与经济性,第三个反映市场运行效益。我在实际仿真中发现,场景C相对场景A,全天网损能降低15%~25%,最低电压从0.93左右抬升到0.97以上;相对场景B,网损还能再降5%~10%——这个差异值在不同接入位置和容量配置下会变,但趋势非常稳定。
4.3 结果分析:网损曲线和电压分布怎么看
拿到优化结果以后,很多人的习惯是直接拉一堆曲线出来贴进论文,但不太会“读”曲线。我个人认为,最有价值的三个视角是:网损的时间分布、电压的空间分布、储能SOC的时序走势。
网损时间分布能告诉你损耗集中在哪个时段、优化策略在哪个时段起了关键作用。以我常用的参数配置为例(光伏接在节点17和32,储能接在节点8),协同优化的网损曲线在午间光伏大发时段明显比基准场景低——原因是储能在这个时段充电,把原本要倒送或长距离传输的光伏电量就地吸收了一部分,减小了线路传输功率,损耗自然下降。
电压空间分布则是看优化后全网各节点电压的包络线。重点检查两个时段:午间光伏大发时段(电压最高点)和晚高峰负荷时段(电压最低点)。协同优化本质上是把午间富余的光伏能量“搬运”到晚高峰释放,所以这两个时段的表现都要看——只改善了午间电压却牺牲了晚高峰电压,调度方案是不合格的。
储能SOC的时序走势则直接印证了调度策略的“协同”属性:SOC在午间光伏大发时段快速上升(充电),在晚高峰时段快速下降(放电),在凌晨低谷时段平缓补电——SOC曲线像一根振荡的桥梁,把光伏出力高峰和负荷高峰连接起来。这个观察点往往是论文里最能体现“协同互动”机制的一张图。
5. 实测中踩过的坑与排错链路
5.1 二阶锥松弛不紧:怎么判断你的解可不可信
二阶锥松弛虽然好用,但它在某些配置下会失效——求出的解毫无物理意义,而你在结果里看不出来。我有一次做多时段调度,加了储能以后结果网损奇低,低到不合理的程度(比纯理论下界还低),一开始还以为是优化效果好,后来检查松弛间隙才发现,好多支路根本没满足等号关系。
排查链路是这样走的:
- 第一步,写一个后处理脚本,对每条支路每个时段计算$gap = |v_i l_{ij} - (P_{ij}^2+Q_{ij}^2)|$;
- 第二步,打印所有$gap > 1e-4$的支路和时段,观察它们的分布——是集中在某一时段?还是集中在某些支路?还是全网普遍不紧?
- 第三步,针对具体场景调整模型:最常见的原因是节点电压约束过紧(比如下限设得很高),导致松弛变量被“挤出”了合理范围。把电压下限从0.97放宽到0.95,问题通常就消失了。
这个坑的教训是:SOCP的求解器只保证松弛问题的全局最优,不保证松弛紧性。算完以后不检查紧性,相当于默认假设了一个没验证的前提。后处理脚本要多写一步,这不是可选项,是必选项。
5.2 辐射状拓扑约束:重构里最经典的坑
如果你想在源-荷-储协同之外再加配电网重构(通过联络开关调整网络拓扑),辐射状约束就是绕不开的难点。32条常闭支路加5条联络支路,随便闭合几个开关,很容易出现环网或孤岛。而IEEE33节点有环网时潮流方程不再只有DistFlow一种表达,所有基于DistFlow的SOCP模型都需要调整。
经典做法是用生成树约束(单商品流或网络流约束)保证辐射状结构,但要注意一个坑:单商品流约束如果只约束“每个节点有且仅有一个父节点”加上“根节点到各节点都有路径”,写起来简单,求解效率也不错,但严格来说需要把N节点、N-1条边满足连通性两种条件合在一起才能完全等价于辐射状。很多论文里只写了一半,导致解出来虽然没环但存在孤岛。
我自己在扩展重构模块时的做法是两步验证:第一步检查解的稀疏矩阵是否满足边数等于节点数减一;第二步从根节点做深度优先搜索,确认所有节点都被访问到。两步都通过,才认为拓扑约束是完整的。
5.3 SOC初值乱设导致的无解问题
储能SOC递推约束有个容易踩的坑:如果SOC初值设置不合理,模型会直接无解。比如你设置SOC初值为0.9,而储能容量小、负荷低谷期的充电功率又受限,约束可能要求晚间高峰放电后SOC回到0.9——但中间几个时段根本充不满那么多电,于是约束互相矛盾,Cplex报infeasible。
排查方法是:单独跑一个“仅储能调度且固定其他运行点”的线性规划,把SOC各时段可行域画出来。如果发现可行域是空的,要么降低初值要求(比如允许末SOC和初SOC不同),要么加大储能容量。还有一个小技巧:把SOC的上下限设置成初值附近的可行区间,再逐步放宽,这样既能保证初始可行点存在,又不至于把优化空间缩小得太厉害。
另外提醒一点,SOC递推里充放电效率的处理方式会影响结果。如果充放电效率不对称(充0.95、放0.95),决策变量里的虚拟“充”“放”功率和实际SOC变化之间会存在数值差。建模的时候我建议把充放电功率直接定义成“交流侧功率”,SOC递推里再乘效率系数——这样变量物理含义清晰,目标函数里的损耗项也好归因。
5.4 用24时段结果做日内滚动的一点扩展思路
最后再分享一个扩展方向。日前调度计划做完以后,很多人会问下一步做什么——答案往往是滚动优化(model predictive control, MPC)。思路很简单:把优化窗口从24小时缩短到4~6小时,每15分钟滚动一次,用最新的光伏和负荷预测更新模型,重新求解,只执行第一个时段的指令。
这个扩展在代码层面几乎不需要改模型——Yalmip的约束构建部分完全复用,只需要在外部包一层循环,每个时刻更新预测曲线、修改时钟索引。我自己实测下来的经验是:滚动窗口4小时、控制间隔15分钟,调度效果相比于日前计划在应对光伏突变时有明显改善,网损大概再降3%~5%。而且随着配电网里“云-边-端”协同架构逐步落地,这种在线滚动调度的模式,正在从论文仿真走向实际工程部署。
如果看完这篇,你能把SOCP松弛、储能时序建模、IEEE33节点数据准备这三块完整跑通,那源-荷-储协同调度的主干就算真正掌握了。剩下的,就是在接入位置、容量配置、目标函数权重上做各种变化实验——路已经铺平了。