ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

配电网最优潮流与二阶锥松弛:微电网灵活性优化及Matlab实现

配电网最优潮流与二阶锥松弛:微电网灵活性优化及Matlab实现 很多人第一次看到“考虑微电网灵活性的含分布式电源配电网二阶锥松弛最优潮流优化研究”这个题目第一反应是“这又是课题组的年度包装”但如果你真在配电网规划、微电网调度或者新能源消纳一线待过就会明白这个题目其实指向一个非常现实的痛点分布式电源多了以后传统配电网的调度逻辑已经不够用了必须换一套既能算得动、又能保证精度的数学模型。而二阶锥松弛配上最优潮流恰好是目前工程和学术界都比较认可的一条落地路径加上Matlab代码实现意味着这套方法不是停留在公式推导而是真正能跑出结果、能复现、能改参数继续做实验的。这篇文章我想把整个项目的来龙去脉讲透包括为什么配电网最优潮流会变成硬需求二阶锥松弛到底在“松”什么微电网灵活性怎么量化并塞进优化模型以及Matlab里面用YALMIP一步步建模、求解、调参、避坑的完整思路。无论你是刚接触配电网优化的研究生还是已经在做新能源调度的工程师按这条思路走下来应该能少走不少弯路。1. 先弄清楚问题本身配电网为什么越来越需要“最优潮流”1.1 分布式电源大规模接入后传统配电网的调度逻辑发生了什么变化传统配电网在很长一段时间里被当作“无源网络”来运行电能从变电站单向流向负荷调压靠有载调压变压器无功补偿靠电容器组投切。这种模式在负荷相对稳定、电源集中在输电网侧的时候是够用的。但分布式光伏、分散式风电、储能系统大量接入之后情况完全变了。潮流不再是单向的可能白天负荷低谷时光伏大发功率从10kV母线往上一级变电站倒送电压分布也不再是“首端最高、末端最低”的单一趋势而是局部节点电压被顶到上限以上变压器分接头和电容器组的调节速度又跟不上分布式电源出力的快速波动。在这种情况下“就地控制”已经不可能做到全局协调。所以必然需要一个能够统筹全网状态、在满足安全约束的前提下给出各DG出力、储能充放电、可调负荷调整策略的优化工具。这就是最优潮流(OPF)进入配电网的原因。只是配电网和老一辈输电网OPF有个显著区别配电网通常辐射状运行支路电阻电抗比R/X偏高模型中很多非线性关系不能直接忽略导致直接求解很困难。1.2 最优潮流优化的是什么“微电网灵活性”为什么被单拎出来常规的最优潮流目标函数一般是网损最小、运行成本最小或者电压偏差最小。但在微电网和主动配电网场景下还有一个非常关键的维度——灵活性。什么叫灵活性说白了就是系统应对净负荷波动和预测不确定性的调节能力。光伏一阵云飘过来出力骤降系统能不能快速顶上去风电夜间爬坡负荷水平低系统能不能把多余的电量存起来而不是切机如果优化模型完全不考虑这些得到的最优解很可能是一个“在预测曲线上看着最优、实际执行时马上越限”的方案。所以这个题目把“灵活性”和“最优潮流”放在一起本质上是在做一个带安全裕度的调度优化。而二阶锥松弛要解决的是优化模型的求解可行性问题因为含分布式电源的配电网OPF直接建模是非凸非线性优化商用求解器基本无能为力必须通过凸松弛把它变成可高效求解的二阶锥规划(SOCP)问题。整个项目的价值链条就是这样串起来的分布式电源带来运行不确定性不确定性需要灵活性应对灵活性要求进入OPFOPF需要SOCP求解SOCP需要Matlab实现。2. 二阶锥松弛(SOCP)到底是怎样“松”出来的2.1 从配电网DistFlow方程到非凸根源要理解二阶锥松弛绕不开配电网的支路潮流方程。现在学术界做配电网优化基本默认使用DistFlow形式的支路潮流模型也叫Branch Flow Model。它的好处是变量选取非常“物理”对每条支路定义首端流向末端的复功率定义节点电压幅值对每个节点建立有功、无功功率平衡方程。以辐射状配电网的一条支路为例假设支路i-j的阻抗为z_ij r_ij j*x_ij流入节点j的有功功率等式可以写成P_ij - r_ij * l_ij sum(P_jk) P_Lj - P_Gj其中l_ij表示支路电流幅值的平方P_Lj是节点j的有功负荷P_Gj是节点j注入的有功电源出力。无功功率同样有类似的等式。还有一条联系节点电压和支路潮流的方程v_j v_i - 2*(r_ij*P_ij x_ij*Q_ij) (r_ij^2 x_ij^2)*l_ij这里v_i、v_j分别表示节点i和节点j电压幅值的平方。到这为止上面的等式都还是线性的。问题出在电流的定义上线路电流与通过的功率、节点电压之间满足l_ij * v_i P_ij^2 Q_ij^2这是一个二次等式而且由于l_ij和v_i都是变量乘积导致该约束非凸。正是这个等式把整个OPF问题变成了一个非凸非线性规划YALMIP也好Gurobi也好直接丢进去是求不出全局最优解的。2.2 松弛过程等式变不等式为什么敢放心去“松”二阶锥松弛的做法很直接把上面的非凸等式“放松”成不等式。l_ij * v_i P_ij^2 Q_ij^2也可以等价写成标准二阶锥形式这也是本文Matlab代码里实际用到的形式|| 2*P_ij; 2*Q_ij; l_ij - v_i || l_ij v_i学过凸优化的人一眼能认出这是典型的旋转二阶锥约束。把等式放成不等式物理含义是允许“视在功率小于电流与电压的乘积”等于给了优化问题更大的可行域。这样放会不会导致解出来的东西根本不对这就要看目标函数的“导向”了。大多数配电网OPF目标函数是网损最小或者运行成本最小而这些目标都会倾向于把电流、网损压到最低所以松弛后的不等式在最优解处通常会自然“取等”也就是说松弛是紧的松弛解就等于原问题的全局最优解。这就是SOCP方法能成立的核心。当然紧性不是无条件成立的。理论上要求配电网是辐射状拓扑、支路阻抗在合理范围内、目标函数对电流具有单调性等。工程实践中我们不能完全依赖理论结论所以代码实现后一定要回过来检查松弛间隙——把最优解代回l_ij * v_i - (P_ij^2 Q_ij^2)看这个值是否接近0。如果偏差较大说明松弛“松过头”了此时需要调整权重或约束。2.3 微电网灵活性指标怎么放进优化模型灵活性指标并没有唯一的标准化定义不同文献差别很大但工程上无非是三条路把灵活性放到目标函数里作为惩罚项把灵活性作为约束条件或者在后验评估时再做量化。目前使用最多、实现也最方便的是“备用容量约束 爬坡约束 可调设备范围约束”的组合做法。向上灵活性在第t个时段可以这样理解系统在当前运行点上还有多少“上调能力”。如果预测净负荷突然增加某个量系统能否通过增加DG出力、加大储能放电、切除或削减部分可调负荷把这个波动吸收掉。写成约束就是sum(PG_max(t) - PG(t)) sum(Pdis_max(t) - Pdis(t)) sum(P_DR(t)) delta_up(t)其中delta_up(t)就是根据预测误差置信区间或者净负荷波动率算出来的备用需求量。为了不让模型太保守也可以把它做成软约束引入缺额变量并在目标函数中加惩罚系数。这个思路在Matlab里实现起来特别灵活因为YALMIP支持直接声明辅助变量并添加线性约束后续改参数、改场景都很快。3. Matlab建模与代码落地全流程3.1 环境准备YALMIP、求解器和IEEE算例数据开始写代码之前环境必须先搞定。我建议使用YALMIP作为建模层搭配Cplex或Gurobi作为底层求解器两者对SOCP的支持都很好。如果是学生或者没有商业求解器授权可以用Mosek的学术版效果也不错实在不行还有开源的SeDuMi、SDPT3但求解速度和稳定性会差一些验证小算例可以做24小时多时段问题会比较吃力。很多人在这一步就卡住主要问题出在求解器路径没有加到Matlab里。Cplex安装后不会自动出现在Matlab的路径中需要手动addpath而且要注意Matlab版本、Cplex版本和cplexmex文件位数必须匹配。另一个常见问题是YALMIP识别不到求解器可用solvesdp之前先执行yalmiptest或which cplex确认。IEEE 33节点系统是配电网优化最常用的测试算例数据来源通常是IEEE官方文档或者网上广泛流传的Matpower格式数据。不过Matpower自带的case33bw可能不是所有版本都有没有的话可以自己手工构建一个bus结构体或直接读取Excel数据。网络参数主要包含每个节点的有功/无功负荷、每段支路的电阻和电抗、节点电压基准值和功率基准值。这里必须提醒一个单位换算的细节IEEE 33节点系统原始阻抗数据单位大多是欧姆而优化模型里一般用标幺值所以需要把线路阻抗除以基准阻抗。基准阻抗的计算公式是Z_base V_base^2 / S_base比如基准电压V_base 12.66 kV基准功率S_base 10 MVA那么Z_base 12.66^2 / 10 16.02 Ohm。每条支路的电阻电抗都除以16.02才能得到正确标幺值。这个换算如果漏了后面所有潮流结果都会离谱。3.2 变量声明、目标函数与二阶锥约束的代码写法进入核心建模环节。下面这段代码展示了YALMIP下变量定义、目标函数和二阶锥约束的基本写法我按24时段多时段模型来写实际使用时可以根据单时段或多时段缩减时间尺度。%% 系统基础数据 nBus 33; % 节点数 nBranch 32; % 支路数 T 24; % 调度时段数 nDG 5; % 分布式电源数 r branch(:, 3); % 支路电阻标幺值 x branch(:, 4); % 支路电抗标幺值 from branch(:, 1); % 支路首端节点编号 to branch(:, 2); % 支路末端节点编号 %% 决策变量 P sdpvar(nBranch, T, full); % 支路有功单位标幺值 Q sdpvar(nBranch, T, full); % 支路无功 l sdpvar(nBranch, T, full); % 支路电流幅值平方 v sdpvar(nBus, T, full); % 节点电压幅值平方 Pg sdpvar(nDG, T, full); % DG有功出力 Qg sdpvar(nDG, T, full); % DG无功出力 Pc sdpvar(nDG, T, full); % 弃电功率目标函数我采用“网损 运行成本 弃电惩罚 灵活性缺额惩罚”的加权组合。权重系数需要根据实际工程关注点调这个没有绝对标准我的经验是网损权重相对其他运行成本可以设置得小一点因为网损在成本占比中通常不是主要部分。Objective 0; for t 1:T % 网损sum(r * l) Objective Objective sum(r .* l(:,t)); % DG发电成本简单用二次函数近似 Objective Objective sum(a_dg .* Pg(:,t).^2 b_dg .* Pg(:,t)); % 弃电惩罚 Objective Objective lambda_cur * sum(Pc(:,t)); % 灵活性缺额惩罚 Objective Objective lambda_flex * F_short(t); end约束条件分几块。第一块是所有运行变量的上下限约束Constraints []; % 电压幅值范围注意v是电压幅值平方0.95^2和1.05^2 Constraints [Constraints, 0.95^2 * ones(nBus, T) v 1.05^2 * ones(nBus, T)]; % DG出力与弃电约束 Constraints [Constraints, 0 Pg Pg_max]; Constraints [Constraints, 0 Qg Qg_max]; % 储能、可调负荷等约束根据具体设备类型补充...第二块是节点功率平衡约束和二阶锥约束。节点功率平衡用节点关联矩阵处理这里为了说明逻辑假设C_node是按“节点-支路”排列的关联矩阵每一行对应一个节点for t 1:T % 节点有功平衡C_node * (P - r*l) PG_injection - PL Constraints [Constraints, ... C_node * (P(:,t) - r .* l(:,t)) P_Gen(:,t) - P_Load(:,t)]; % 节点无功平衡C_node * (Q - x*l) Q_Gen - Q_Load Constraints [Constraints, ... C_node * (Q(:,t) - x .* l(:,t)) Q_Gen(:,t) - Q_Load(:,t)]; % 电压平方递推关系 for k 1:nBranch Constraints [Constraints, ... v(to(k), t) v(from(k), t) - 2*(r(k)*P(k,t) x(k)*Q(k,t)) ... (r(k)^2 x(k)^2)*l(k,t)]; end % 二阶锥约束核心 for k 1:nBranch Constraints [Constraints, ... cone([2*P(k,t); 2*Q(k,t); l(k,t) - v(from(k),t)], ... l(k,t) v(from(k),t))]; end endcone是YALMIP封装好的二阶锥约束函数比直接写norm(...) ...更清晰求解器识别也更准确。很多初学者直接写成norm([2*P;2*Q;l-v]) lv也能跑但遇到数值条件差的算例时显式用cone更稳一点。3.3 求解设置、结果提取与松弛间隙检查求解设置比较简单ops sdpsettings(solver, cplex, verbose, 2, showprogress, 1); sol optimize(Constraints, Objective, ops); % 检查求解状态 if sol.problem 0 disp(求解成功); else disp(sol.info); end % 提取结果 P_result value(P); V_result sqrt(value(v)); Objective_value value(Objective);求解完之后松弛间隙检查千万不能省。我的习惯是把所有支路都跑一遍gap 0; for t 1:T for k 1:nBranch lhs l(k,t) * v(from(k),t); rhs P(k,t)^2 Q(k,t)^2; gap max(gap, abs(lhs - rhs)); end end disp([最大松弛间隙, num2str(gap)]);如果gap小于1e-4标幺值体系下基本可以认为松弛是紧的结果可信。如果达到1e-2甚至更大就需要检查约束是否写错、单位是否一致、目标函数是否导致了反向激励。4. 一个典型算例的仿真设计4.1 算例场景设置与对比方案以IEEE 33节点配电系统为例我在仿真里设置了三种对比方案方案场景说明优化目标灵活性约束方案A无分布式电源纯外购电网损最小不设置方案B含光伏风电但采用传统潮流计算网损弃电最小不设置方案C含DG储能可调负荷网损弃电灵活性缺额最小设置备用和爬坡约束方案A作为基准用来量化DG接入后的改善方案B用来展示“不考虑灵活性”时系统的脆弱性方案C是本文的重点。DG接入位置一般选择负荷较重、电压偏低的末端节点比如33节点系统的18节点、22节点、25节点等位置这样能更好体现DG对电压的支撑作用。为了让仿真更有说服力光伏和风电出力曲线应该采用典型日的实测或模拟数据比如光伏用中午高、早晚低的钟形曲线风速用白天小、夜间大的曲线负荷用早晚双峰曲线。净负荷波动较大的时段通常出现在光伏快速爬升的早晨和大幅下降的傍晚这时候灵活性约束往往是最紧的。4.2 结果对比怎么看网损、电压、DG出力和灵活性指标求解完成后我一般按四个维度来整理结果。第一个是系统总网损对比方案A、B、C看DG接入和灵活性约束下网损是何走势。通常方案C会略高于方案B这不是坏事说明我们在用一定经济代价换取安全运行空间。第二个是电压分布。重点看三相平衡假设下各节点电压幅值的最大值和最小值特别是末端节点。DG接入后末端电压一般会被抬高但如果调节不当也可能越上限。通过绘制一天24小时的电压瀑布图或挑几个关键节点的电压曲线能直观看出方案C在电压控制上的优势。第三个是DG实际出力与弃电量。柔性约束下夜间风电大发、负荷低谷时可能需要限制出力就是通常说的弃风弃光。对比B和C会发现方案C因为提前预留了备用在净负荷突变时DG出力调整更平滑弃电总量可能略大但电压波动和失负荷风险明显降低。第四个是灵活性裕度。可以统计每个时段系统向上/向下可调容量与需求备用的比值方案C会稳定在1以上方案B在高峰爬坡时段很可能低于1。这样的对比能把“灵活性”从抽象名词变成可量化的曲线这也是论文或项目汇报里最加分的一张图。4.3 收敛性与求解速度的实测情况多时段24小时模型33节点系统32条支路、24个时段变量规模大概在数千个量级SOCP约束几百个。用Cplex或Gurobi求解通常都在几秒到几十秒内完成速度不是问题。如果发现求解时间异常多半是YALMIP把问题识别成了非凸的比如某个约束不小心写了二次等式Gurobi会把它当作MIQP处理。此时查看sol.solver或者ops.solver是哪个求解器在干活能帮助你定位问题。收敛性上还有一个比较隐蔽的陷阱如果目标函数里加了灵活性缺额惩罚而权重系数设置太大会导致目标值数量级和网损严重不匹配进而影响数值稳定性。我的经验是把各类成本折算到同一单位比如统一折算成“元”或统一折算成“标幺功率下的成本系数”再乘以一个接近1的调节因子这样求解器不容易出现数值病态。5. 调试经验与常见坑5.1 求解器报“不可行”或“无解”时先查哪里这是我在实际调模型中遇到最多的一个情况。YALMIP返回Infeasible problem不少人第一反应是去查约束是否写错但更常见的原因是变量取值范围给得前后矛盾。比如DG最大出力设置成标幺值2.0但网络承受能力只有1.5那么所有约束叠加在一起就是无解。建议先用“宽松模式”测试把电压上下限放宽到0.9^2和1.1^2把DG出力上限放大50%看能不能解出来。如果能解再把约束一步步收紧定位是哪条约束造成的不可行。另一个常见原因是节点功率平衡方程的符号反了。DistFlow模型中支路末端流向负荷功率平衡里的负荷应该写在等式右侧取负号。很多初学者在这里容易把P_Load和P_Gen的符号弄混导致求解结果出现负数有功或“发电”节点在抽功率。5.2 松弛间隙过大的排查思路如果求解成功但松弛间隙不满足要求优先检查网络是不是严格的辐射状结构。有些算例数据里包含联络开关默认是闭合的因此存在环网。二阶锥松弛的理论条件基于辐射状网络一旦有环松弛紧性可能立刻失效。其次是目标函数。如果目标函数不包含网损或运行成本而只是最大化DG出力那么“把电流和电压尽量做大”反而可能让松弛约束不紧。解决方案是给目标函数增加一个“电流惩罚项”比如xi * sum(r.*l)其中xi设一个很小的值不会明显改变原目标却能推动松弛取等。单位问题也会导致间隙看起来很大。如果负荷单位是kW而电压用kV功率基准和电压基准不一致算出来的l*v和P^2Q^2会差好几个数量级。检查单位是我在排查时最先做的事情。5.3 Matlab和YALMIP的怪问题速查现象可能原因处理办法yalmiptest找不到求解器求解器路径未添加到Matlab用addpath添加Cplex/Gurobi所在目录YALMIP把SOCP报成二次等式使用了l*v P^2Q^2这种等式换成cone或不等式形式求解时间特别长YALMIP将问题传递给了非线性求解器检查是否混入了非凸约束Cplex点开闪退mex文件与Matlab版本不匹配重装对应版本的求解器接口结果全是NaN变量没有初始化或单位差异过大先给定初值统一基准值想导图但报错字体纯Matlab绘图问题set(0,DefaultAxesFontName,Times New Roman)这些坑我基本都踩过说句实话大部分不是数学问题而是软件工程和数值处理问题。所以调试时不要死盯公式先从最简单的小算例跑通比如3节点或5节点系统再加复杂约束这是最稳妥的路线。6. 这个模型还能往哪个方向扩展6.1 从单时段走向多时段滚动调度和不确定性优化当前面的24时段模型跑通以后再往前一步就是融入不确定性。光伏、风电预测误差可以建模为场景集采用鲁棒优化或随机优化来求解二阶锥模型仍然可以复用只是约束数量会成倍增加。另一种做法是模型预测控制每15分钟或1小时滚动更新一次预测和优化这也是目前微电网能量管理系统比较主流的实现方式。在这个方向里“灵活性”的定义可以更贴近实时性比如引入可调负荷的响应时间、储能SOC的末端约束、多微电网互联时的功率交换上限。这些都可以在原SOCP框架内通过增加线性约束来实现不需要改变核心求解架构。6.2 从三相对称走向三相不平衡配电网实际的低压配电网大量存在三相不平衡问题单相光伏的接入更是加剧了这一现象。如果要做更精细的项目可以把DistFlow推广到三相形式节点电压变成3×1向量支路潮流变成3×3矩阵二阶锥约束同样是成立的。Matlab代码的改动量不小但整体求解范式不变这才是SOCP方法的真正优势所在。如果后续想从“配电网优化”跨到“配电网与输电网协同优化”这个模型可以作为下层问题嵌入上层处理输电网潮流和功率交互形成双层优化结构用KKT条件或启发式算法迭代求解。整个扩展路径比较平滑不会因为底层模型换掉而推翻重来。我自己在把模型从单时段拓展到24时段时最初的一个版本因为目标函数里网损项和成本项量纲没对齐结果总是不收敛。后来把所有成本都换算到元/kWh才真正稳住。这个项目的变量越多对“一致性”的要求越高这也是我觉得写代码比写公式更考验人的地方。你跟着上面的思路把33节点系统跑通一次再换成69节点甚至123节点系统其实就是改数据、改关联矩阵的事核心模型一条都不用动而且整个过程对配电网运行逻辑的理解会比看十篇论文都有用。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进