简介:本资源是一套面向电力系统优化研究者与高校研究生的MATLAB实战代码,聚焦主动配电网多源协同最优潮流问题,采用混合整数二阶锥规划(MISOCP)建模方法,有效解决分布式电源消纳、峰谷差调控及‘源–荷–储’协同运行等核心挑战。压缩包共3个文件(2个MATLAB主程序脚本+1张IEEE 33节点配电网结构图),总大小仅139KB,轻量紧凑,便于快速复现与二次开发;其中.m文件封装了YALMIP建模框架、MISOCP求解器调用逻辑及典型设备(分布式电源、储能、EV、无功补偿装置)的数学模型与约束条件。已有448人学习下载,代码结构清晰、注释完整,直接支持在标准IEEE 33节点系统上开展多时间尺度经济调度仿真,可作为课程设计、科研入门及算法对比实验的可靠基准实现。 搞配电网优化这些年,最绕不开的就是最优潮流(OPF)这道坎。以前做输电网OPF,连续变量、凸问题,随便丢给求解器就跑得很舒服。可一旦进入主动配电网的领域,光伏、储能、可投切电容器、有载调压变压器(OLTC)、甚至网络重构开关全都掺和进来,模型立刻变成离散与连续变量混杂的混合整数非凸问题,传统方法基本歇菜。这也是我整理这套基于混合整数二阶锥规划(MISOCP)的主动配电网最优潮流Matlab代码的初衷:用二阶锥松弛把非凸潮流方程变成可求解的凸约束,用整数变量描述离散设备,最后交给商用求解器拿到全局最优解。今天把这套开源代码的建模思路、代码结构、实操步骤和踩坑记录完整写出来,做主动配电网规划、调度或学术研究的朋友可以直接拿去改。
这套代码到底解决什么问题,我先说人话版本。传统OPF只算“给定网络下发电机怎么出力最便宜”,而主动配电网里你要同时决定OLTC抽头放几档、电容器组投几组、储能是充电还是放电、分布式光伏能不能削减出力,还要保证电压不越限、潮流不反送。这些决策里既有连续量(有功无功)又有整数量(档位、组数、状态),强行用连续优化做必然失真,用启发式算法又拿不到最优解。MISOCP就是目前工程和学术上公认的“能保全局最优、又能在合理时间内跑完”的折中方案。我也在代码里把潮流模型、整数设备建模、目标函数、求解器配置都拆开了,方便你按需改。
1. 这套代码到底解决什么问题
1.1 传统最优潮流为什么不够用
配电网在传统模式下是被动网络,拓扑固定、电源单一,潮流方向基本从变电站向负荷单向流动,调度员能做的主要是调节变压器分接头和无功补偿。这种情况下用经典OPF模型就够了,变量连续、约束连续,内点法一类的连续优化算法就能解决。但现在电网里分布式电源渗透率越来越高,潮流方向开始变得双向化,电压问题也从“越下限为主”变成“上下限都可能越”,控制手段也从单一的分接头调节变成了多类型设备协调。
主动配电网的核心特征就是“主动管理”,你需要主动调节OLTC、主动投切电容器、主动控制储能充放电、甚至主动改变网络拓扑。这些动作有一个共同点:都是离散的。OLTC的分接头只能是一档一档地动,电容器组的投切也是整组整组地切,储能虽然在功率上连续,但充放电状态不是一个连续可微的状态,充放电切换本质是二元决策。把这些离散变量塞进传统OPF里,问题就变成了混合整数非线性规划(MINLP),这类问题在数学上极难处理,全局最优解很难找到。
这也是我在代码里选择MISOCP而不是直接硬刚MINLP的原因。MISOCP把非凸潮流方程通过二阶锥松弛转化为凸约束,剩下的非凸性只保留在整数变量上。整数变量带来的分支定界难度,远比非凸连续变量带来的多局部最优问题可控。只要二阶锥松弛在目标函数诱导下是紧的,解出来的结果就是原问题的全局最优解,这一点在配电网辐射状拓扑下已经有大量理论证明。
1.2 代码适用范围与边界
这套代码不是万金油,它有明确的使用场景。它针对的是辐射状(辐射形)主动配电网,常见的IEEE 33节点、IEEE 69节点、以及实际馈线改造后的算例都可以用。模型里支持分布式光伏、储能系统、OLTC、可投切电容器组、以及联络开关重构(不过重构开关一多,整数变量会爆炸,求解时间会明显拉长,这点后面会详细说)。
适用方向大概有三类:一是配电网日前优化调度,目标可以是网损最小、电压偏差最小或运行成本最小;二是分布式电源规划后的运行校核,固定DG位置和容量后看主动管理手段能不能解决电压越限;三是做算法对比研究,用MISOCP结果作为“最优解基准”,去评估启发式算法、强化学习方法的误差。我自己在多个项目里都是用这套代码作为基准线来用的,效果一直比较稳定。
边界也要说清楚:代码针对的是三相平衡模型,也就是正序单相模型。三相不平衡场景需要扩展为三相DistFlow,模型复杂度会明显上升;网络拓扑如果不是辐射状而是弱环网,二阶锥松弛的精确性需要额外验证;另外如果DG是逆变器型且需要考虑详细过流约束,这里的恒定功率因数模型也需要扩展。这些都是代码的已知边界,使用时要有预期。
2. 数学模型与松弛思路拆解
2.1 DistFlow潮流方程
配电网最优潮流建模,现阶段公认最友好的是DistFlow方程。它的核心思想是把每条支路的有功潮流P、无功潮流Q、节点电压幅值V、支路电流幅值I之间的关系写成递归形式。对于辐射状网络,DistFlow可以精确描述潮流分布,而且是“支路导向”的,每个约束只涉及局部变量,这在优化求解器里非常加分。
先说连续变量部分。对于节点i流向节点j的支路,记支路电阻为r、电抗为x,支路有功为P_ij、无功为Q_ij,支路电流平方为l_ij,节点i和j的电压平方分别为v_i和v_j,则DistFlow方程可以写成:
P_ij - r_ij * l_ij - sum(P_jk) + P_gen,j - P_load,j = 0
Q_ij - x_ij * l_ij - sum(Q_jk) + Q_gen,j - Q_load,j = 0
v_j = v_i - 2 * (r_ij * P_ij + x_ij * Q_ij) + (r_ij^2 + x_ij^2) * l_ij
这套方程没有近似,是精确的交流潮流在辐射状网络下的等价描述。真正麻烦的是最后那个电流平方约束:
l_ij * v_i = P_ij^2 + Q_ij^2
这个约束是非凸的,而且它把电压和电流、有功和无功耦合在一起,是问题变难的核心。传统做法是直接把它线性化,比如忽略网损,但那样精度不够;或者保留非线性,做成MINLP,那样求解效率极低。MISOCP的思路则完全不同:不直接处理这个等式,而是把它松弛成一个凸锥约束。
2.2 二阶锥松弛的原理
要理解二阶锥松弛,你可以把上面那个非凸等式看成“要求电流平方和电压平方的乘积必须精确等于视在功率平方”。这个等式像一个卷曲的曲面,是非凸的。但如果我们把它放宽成“电流平方和电压平方的乘积必须大于等于视在功率平方”,就得到:
l_ij * v_i >= P_ij^2 + Q_ij^2
这还没有直接变成锥形式。标准做法是引入一个辅助变量,把这个不等式改写为:
|| [2 * P_ij; 2 * Q_ij; v_i - z_ij^2 * l_ij] ||_2 <= v_i + z_ij^2 * l_ij
其中 z_ij^2 = r_ij^2 + x_ij^2。这个约束就是一个标准的二阶锥约束(SOC),它描述的是一个旋转锥。通俗地理解,原问题要求一个点在一条非凸曲线“精确”运动,松弛后允许它在曲线外侧的锥形区域内运动,而凸优化算法在这个区域里能保证收敛到全局最优。
为什么要做这个松弛?因为凸优化问题有成熟的求解算法,而且MISOCP问题可以通过分支定界框架在有限时间内找到全局最优解。松弛是否精确,取决于目标函数和约束是否“推动”松弛变量向边界收缩。在配电网网损最小化问题中,目标函数包含支路电流平方l_ij的加权和,这会把l_ij往下压,从而让松弛尽量紧;而电压幅值约束和负荷约束会限制v_i的取值范围。所以大多数辐射状配电网算例中,SOC松弛的间隙很小,结果可靠。
我在代码里采用的就是上述标准SOC松弛。运行时我会额外输出每个SOC约束的松弛间隙,也就是左边l_ij*v_i和右边P_ij^2+Q_ij^2的相对偏差,如果最大间隙超过1e-3,就需要检查模型或算例参数是否有异常。
2.3 离散变量建模
连续潮流方程解决之后,真正让模型变成“混合整数”的是下面这几种设备。我把代码里支持的离散设备逐一说明,方便你扩展时参考。
OLTC有载调压变压器:分接头档位T可以看成整数变量,电压变换比表示为1 + delta * T,其中delta为每档变比步长。模型中通常不直接引入非线性乘积,而是通过线性化或big-M方式处理。简单做法是把OLTC支路当作一个特殊的两个节点之间的连接,将电压关系改成带整数变量的约束,并用McCormick包络或分段线性化处理变比与电压的乘积项。
可投切电容器组:电容器组的无功输出等于每组的无功容量Q_step乘以投切组数n,其中n是整数变量。这个建模相对简单,因为无功输出是整数乘以常数,没有变量乘积。一个技巧是为n设置上限,避免求解器在分支定界时搜索无界空间。比如每组50kvar,最多10组,那么无功输出就是50*n,n∈[0,10]。
储能系统:储能的有功输出P_ess可以是连续变量(充电为负、放电为正),但充放电状态必须用二元变量区分。用u_ch和u_dis两个0-1变量,并约束u_ch+u_dis≤1,防止同时充放电。充放电功率上限分别乘以对应状态变量,再额外串联SOC(荷电状态)动态方程,这个动态方程是跨时段的,使得整个问题从单时段变成多时段最优潮流,复杂度会提升一个量级。
网络重构开关:每条联络开关和分段开关对应一个二进制变量,表示开关闭合(1)或打开(0)。重构的好处是可以改变潮流路径,但坏处是每多一个开关变量,分支定界搜索树就多一层。33节点系统一般只有5个联络开关,还可以接受;如果是实际馈线几十个联络开关,MISOCP求解时长会变得非常恐怖。
下表是我在代码里定义的离散设备建模方式:
| 设备类型 | 整数/二进制变量 | 建模要点 | 典型约束 |
|---|---|---|---|
| OLTC | 档位整数T | 变比线性化、限制档位范围 | 电压关系约束、档位上下限 |
| 电容器组 | 投切组数整数n | 无功输出Q=n*Q_step | 组数上下限 |
| 储能 | 充电/放电二进制u | 防同时充放电、SOC递推 | u_ch+u_dis≤1、SOC范围、功率上限 |
| 联络开关 | 二进制s | 控制支路是否投运、保证辐射状 | 支路潮流big-M、环路约束 |
2.4 目标函数与整体约束
代码里默认目标函数是“网损最小”,这也是重构和电容优化最常用的目标。网损表达式为:
min sum( r_ij * l_ij )
这个目标简单且能有效驱动SOC松弛紧化。如果你想改成功率损耗最小化、电压偏差最小化,或者多目标加权,也只要替换目标函数和对应变量即可。比如目标改成运行成本最小,那就要给分布式光伏上网功率、储能放电功率加成本系数;目标改成电压偏差最小,则需要对电压平方v_i与参考值偏差做绝对值或平方处理,并引入辅助变量。
整体约束分四类。第一类是潮流平衡约束,包括节点有功平衡、无功平衡和电压降落方程;第二类是安全运行约束,包括节点电压上下限、支路电流上限、变压器容量上限;第三类是设备运行约束,如OLTC档位范围、电容器组数、储能SOC和充放电功率;第四类是辐射状拓扑约束,如果启用重构功能,需要保证所有闭合支路构成一棵连通树,通常用生成树约束或节点-支路关联约束来建模。
我把这些约束封装在代码的模型构建函数中,每一类都有独立的函数模块,你可以按需注释或关闭。例如,只想算基础SOCP最优潮流,就把整数设备相关的约束全部置零;想加储能,就新增一个储能节点列表和相关动态约束。
3. Matlab代码实现与架构
3.1 文件结构
我习惯把“数据”、“模型”、“求解”三部分分开,这样改算例不用翻模型,改模型不用动数据。代码目录结构如下:
MISOCP_ADN_OPF/ ├── main.m % 主程序,设置算例和求解器 ├── data/ │ ├── case33bw.m % IEEE 33节点配电网算例 │ └── load_profile.m % 负荷和DG时序曲线 ├── model/ │ ├── build_network.m % 读取网络参数,初始化变量 │ ├── build_socp_constraints.m % 构建DistFlow和SOC约束 │ ├── build_device_constraints.m % 构建整数设备约束 │ └── build_objective.m % 构建目标函数 ├── solve/ │ └── solve_misocp.m % 调用YALMIP和求解器 ├── plot/ │ └── plot_result.m % 结果可视化 └── README.mdmain.m是入口。运行时先加载算例数据,然后依次调用模型构建函数,最后调用求解函数。如果你只在33节点系统上跑,基本不用改任何代码;换算例时只需在data目录里增加一个case文件,并修改main里的算例名称。
我在文件里大量使用了结构体变量,比如mpc.bus、mpc.branch、mpc.gen,这样数据和模型之间的接口很清楚,不怕变量名冲突。有朋友问为什么不用全局变量,因为全局变量在函数调用多的时候非常容易出错,尤其是当你想把代码封装成函数库时,全局变量会拖垮可维护性。用结构体传入传出是最稳的做法。
3.2 YALMIP建模与求解器配置
所有优化模型我都是用YALMIP搭建的。YALMIP是Matlab下的建模工具箱,最大的好处是模型表达式写起来和数学公式几乎一一对应,调试起来很直观。求解核心MISOCP问题需要至少一个支持混合整数二阶锥的求解器,我用的是Cplex和Gurobi,两者都可以。如果你手头没有商业求解器,可以先用SCIP等开源求解器跑小规模算例,但工程上还是建议想办法拿个Cplex或Gurobi的学术授权。
先看一个简化版的YALMIP建模片段,以基础DistFlow约束为例:
%% 变量定义 v = sdpvar(nb, 1); % 节点电压平方 Ui l = sdpvar(nl, 1); % 支路电流平方 lij P = sdpvar(nl, 1); % 支路有功 Q = sdpvar(nl, 1); % 支路无功 Pg = sdpvar(ng, 1); % 分布式电源有功 Qg = sdpvar(ng, 1); % 分布式电源无功 %% 节点功率平衡约束 Constraints = []; for k = 1:nl i = branch_from(k); j = branch_to(k); % 节点j的有功平衡 Constraints = [Constraints, ... P(k) - r(k)*l(k) - sum(P(children{j})) + Pg(j) - Pd(j) == 0]; % 节点j的无功平衡 Constraints = [Constraints, ... Q(k) - x(k)*l(k) - sum(Q(children{j})) + Qg(j) - Qd(j) == 0]; % 电压降落方程 Constraints = [Constraints, ... v(j) == v(i) - 2*(r(k)*P(k) + x(k)*Q(k)) + (r(k)^2+x(k)^2)*l(k)]; end %% 二阶锥松弛 for k = 1:nl i = branch_from(k); z2 = r(k)^2 + x(k)^2; Constraints = [Constraints, ... [2*P(k); 2*Q(k); v(i) - z2*l(k)] ... in cone(v(i) + z2*l(k))]; endin cone是YALMIP内置的锥约束构造函数,它直接声明一个二阶锥约束。如果你的YALMIP版本较老,也可以写成:
Constraints = [Constraints, norm([2*P(k); 2*Q(k); v(i)-z2*l(k)], 2) ... <= v(i) + z2*l(k)];两种写法等价。我个人更喜欢in cone,因为它在传给求解器时锥结构更清晰,求解器识别更准,数值稳定性也好一些。
3.3 整数设备建模代码示例
以电容器组为例。设电容器组最大组数为Nc_max,每组无功为Qc_step,节点j处安装电容器组。投切组数用整数变量nc,无功输出为Qc = Qc_step * nc。YALMIP代码如下:
nc = intvar(1, 1); % 整数变量,表示投切的组数 Qc = Qc_step * nc; Constraints = [Constraints, 0 <= nc <= Nc_max, ... Qc >= 0];这个看起来很简单,但有一个容易被忽略的点:YALMIP在把整数变量乘常数传给求解器后,会自动生成二进制展开或交给求解器处理,但如果你把nc直接乘上另一个连续变量,就会引入双线性项,问题性质立刻变坏。所以在建模时,我要求自己始终把“整数变量与常数相乘”和“整数变量与变量相乘”区分开,后者要额外处理。
储能建模稍微复杂一点。假设储能的充放电效率分别为eta_ch和eta_dis,最大充放电功率都是Pmax,SOC上下限是[SOC_min, SOC_max],时间间隔为dt。用u_ch和u_dis两个二元变量区分状态:
u_ch = binvar(T, 1); u_dis = binvar(T, 1); P_ch = sdpvar(T, 1); P_dis = sdpvar(T, 1); SOC = sdpvar(T, 1); % 同一时刻只能充电或放电 for t = 1:T Constraints = [Constraints, u_ch(t) + u_dis(t) <= 1, ... 0 <= P_ch(t) <= Pmax * u_ch(t), ... 0 <= P_dis(t) <= Pmax * u_dis(t)]; end % SOC递推 for t = 2:T Constraints = [Constraints, ... SOC(t) == SOC(t-1) + eta_ch * P_ch(t) * dt - P_dis(t) * dt / eta_dis]; end % SOC上下限 Constraints = [Constraints, SOC_min <= SOC <= SOC_max];这里有几个细节要注意。充电功率和放电功率要用两个连续变量加两个二元变量来建模,而不能用单一连续变量和正负号来表示,否则无法表达“充电效率”和“放电效率”不同的实际情况。其次,SOC递推是跨时段的,这意味着求解器需要在时间维度上同时优化所有时段的决策,而不是逐个时段独立求解。如果时间步长是1小时且优化24小时,那么模型规模大约是单时段的24倍,求解时间也会大幅增加。
OLTC建模我采用固定分接头位置对应的变比作为整数档位,然后将变比统一放入支路电压方程中。具体来说,OLTC支路可以等效为一个理想变压器串联一个阻抗,理想变压器的变比n_tap是整数变量与额定变比的组合。约束中引入辅助变量处理变比与电压的乘积:
tap = intvar(1, 1); % OLTC档位 n_tap = 1 + tap_step * tap; % 变比与档位线性关系 % 在OLTC支路上,i侧电压与j侧电压的关系 % 引入辅助变量 w = n_tap * v_j,再用big-M线性化 w = sdpvar(1, 1); M = 10; % 足够大的常数 Constraints = [Constraints, ... w <= n_tap * v_j + M * (1 - z), ... % 这种离散线性化写法需要仔细处理 ...实际上OLTC的线性化是整段代码里最绕的地方,容易写错。我在代码里专门写了build_oltc_constraints.m,用big-M和二进制展开把二次项线性化,并给出了详细注释。如果只是做初步优化,可以先把OLTC的档位固定,跑通之后再启动档位优化,这样能降低排查难度。
4. 实操运行与结果解读
4.1 算例配置与参数设置
我默认算例是IEEE 33节点系统,这个系统有32条支路、5条联络开关、峰荷约3.7MW,非常适合做主动配电网方法验证。在算例里,我在部分节点接入分布式光伏和储能,在变电站出口设置OLTC,在无功敏感节点设置可投切电容器组,这样主动配电网的几类典型控制手段都有了。
运行前需要检查几个参数:基准容量baseMVA,我通常取10MVA或100MVA,这会影响标幺值下的电阻电抗数量级,进而影响数值稳定性;电压上下限,一般取0.95到1.05p.u.;每个时段的负荷曲线和DG出力曲线,我放在load_profile.m里,需要根据实际数据改。如果是做单时段断面优化,可以把T设为1;多时段优化把T设为24,对应一天的调度。
求解器参数方面,Cplex和Gurobi都有大量可调参数。我最常用的是设置求解时间上限,比如600秒,防止分支定界陷入“无限跑”。同时设置相对MIP gap,比如1e-4,当目标函数值相对界差小于这个比例时就提前停止,这能显著缩短求解时间,而且工程上1e-4的误差完全可以接受。Gurobi中对应的命令是gurobi_options.MIPGap = 1e-4,Cplex中对应的参数是cplex_options.mip.tolerances.mipgap = 1e-4。
4.2 main.m中怎么一步步跑起来
首先在Matlab中把当前路径切到MISOCP_ADN_OPF目录,然后在命令行运行:
mainmain函数会依次做以下几件事:加载算例数据、生成潮流模型、添加设备约束、求解模型、保存结果、画图。如果你在运行过程中遇到YALMIP提示找不到求解器,先检查yalmiptest,然后确认Cplex或Gurobi的路径已经被添加到Matlab路径中。一个常见坑是你在系统里装了求解器,但Matlab通过YALMIP调用时找不到对应mex文件,这时需要运行求解器自带的安装脚本,或手动添加路径。
跑通后的输出大致如下:
Setting up OPF model... Number of continuous variables: 289 Number of integer variables: 19 Number of constraints: 764 Solver: Gurobi 10.0 Objective: 0.1856 p.u. MIP gap: 0.0001 Elapsed time: 32.4 s这个输出意味着模型构建正确,求解器在32秒内找到了MIP gap为0.0001的解。不同机器和不同求解器版本运行时间会有差异,但数量级可以参考。
4.3 结果怎么看
结果存储在results结构体里,包括每个节点的电压幅值、每条支路的电流和功率、每台设备的最优档位和投切状态。我写了一个plot_result.m,自动画出三种图:节点电压分布曲线、支路电流热力图、各部分设备状态时间序列。看结果时,我一般先看电压曲线是否落在0.95到1.05之间;再看设备状态是否合理,比如OLTC一整天只动作了几次,而不是频繁抖动;最后看网损对比,和基础潮流结果比一比,主动管理手段通常能降低网损5%到20%,如果结果不降反升,很大概率是模型约束或者数据出了问题。
一个重要的验证手段是:把整数设备全部固定为初始值,先跑一次纯SOCP连续优化;然后再启动整数优化,看结果是否更优。如果纯连续SOCP的网损和MISOCP的网损几乎一样,可能是整数设备约束写得太松,或者这些设备在当前负荷水平下本来就不需要动作。反之,如果MISOCP结果明显优于此,说明主动管理手段确实发挥了作用。
5. 常见问题与排查技巧实录
5.1 求解器报错与安装问题
我在各种机器上遇到过不少YALMIP调用求解器的问题,这里整理成速查表:
| 现象 | 可能原因 | 解决办法 |
|---|---|---|
YALMIP提示No suitable solver | 求解器未安装或路径未设置 | 运行yalmiptest查看已识别求解器,安装Cplex/Gurobi并添加路径 |
Gurobi报License Error | 许可证未配置或过期 | 重新配置license文件,确认环境变量GRB_LICENSE_FILE |
Cplex报CPXERROR | 模型数值病态或内存不足 | 检查参数数量级,尝试放大baseMVA,或减少MIP求解时间 |
| 求解时间过长 | 整数变量太多或MIP gap设置过严 | 增大MIP gap,禁用网络重构开关,减少时间步数 |
有一个容易忽略的细节:YALMIP识别求解器需要Matlab能找到求解器的Java或mex接口。如果同时装了Cplex和Gurobi,YALMIP默认可能选择一个不适合MISOCP的版本,建议在求解前显式指定:
ops = sdpsettings('solver', 'gurobi', 'verbose', 2);这样就不会因为默认求解器选错导致“慢”或者“无法处理锥约束”。
5.2 SOC松弛不精确怎么处理
二阶锥松弛最怕的一件事就是解出来之后发现松弛间隙太大,也就是原问题的解并不在锥边界上。这种情况通常发生在目标函数对支路电流没有足够“吸引力”时。比如目标函数改为“电压偏差最小”时,电压越下限的惩罚可能和l_ij无关,求解器就没有动力把l_ij压到锥边界,结果可能解出一个物理上不可实现的“低估网损”的状态。
处理办法有几种。第一,在目标函数中增加一个很小的网损惩罚项,比如0.0001 * sum(r_ij * l_ij),这能有效缩小松弛间隙而不改变主目标太多。第二,检查电压上下限约束,如果电压范围设置得过于宽松,松弛间隙容易变大,收窄到0.95-1.05通常有帮助。第三,在求解后显式计算最大松弛间隙:
gap = 0; for k = 1:nl lhs = l(k) * v(branch_from(k)); rhs = P(k)^2 + Q(k)^2; gap = max(gap, abs(lhs - rhs)); end如果gap大于1e-3,就在约束里逐个找出对应支路并分析,是负荷过大导致电流越大,还是目标函数没压住电流,基本都能定位。
我在代码里把松弛间隙计算也放进了输出,方便你直接复制使用。
5.3 整数变量爆炸与求解性能优化
多时段模型最常见的坑是整数变量随T线性增长。如果24个时段各有5个OLTC档位、10组电容器、2组储能开关状态,那你至少有24*(5+10+2)=408个整数变量,而且还有大量连续变量。Gurobi和Cplex虽然能处理几百个整数变量,但如果不加限制,分支定界树会非常庞大。
我常用的性能优化技巧有三个。第一,先做“时段聚类”,把相似负荷曲线归并成少数典型时段,用典型时段求解,再把结果映射回全时段,工程上误差很小。第二,给整数变量设置合理的上下界和初始值,YALMIP支持assign函数给变量赋初值,有初值的MIP问题分支定界能剪掉大量无效分支。第三,固定部分设备状态,比如夜间光伏不出力时,光伏逆变器不投运,直接把对应状态变量置零,减少搜索空间。
还有一个非常实用的技巧是分段松弛整数变量,也就是先把整数变量放松为连续变量,跑一个LP或SOCP下界解,把这个解作为分支定界的初始界,可以大幅压缩求解时间。代码里我加了一个参数relax_int设为1,用于模式切换。
5.4 结果不合理时的检查顺序
如果模型跑完但结果明显不合理,比如电压全部贴着上限、网损大得离谱、OLTC整天乱动,我会按以下顺序排查。第一步检查数据单位是否统一,配电系统经常混用kW/MW和kvar/Mvar,一个数量级的错误就会导致结果异常。第二步检查功率平衡约束的方向,DistFlow中负荷项的符号非常容易写反。第三步检查YALMIP变量定义是否重复,模型函数如果被调用多次,变量没清空会累积重复约束。第四步把结果代入一个独立的潮流计算工具(OpenDSS、Matpower等)验证,MISOCP得到的设备状态应该能通过标准潮流验证。
我自己在开发这套代码时,有一次怎么调电压都越下限,后来发现是load_profile.m里的负荷曲线单位是kW,但模型里用的基准功率是MVA,导致负荷被放大了1000倍。这种低级错误在数据处理环节很难一眼发现,一定要有“独立验算”这一步。
最后再分享一个小技巧:如果你不只是想跑通代码,而是想基于它写论文或做对比实验,建议把MISOCP结果与一个常规的连续SOCP结果、以及一个MINLP直接求解结果一起列出来。很多审稿人关心的不只是最终结果多好,而是“改进是来自松弛方法,还是来自主动管理设备”。这套代码因为把两类因素解耦了,正好能回答这个问题。这也是我当初整理这个开源项目的核心原因:不是为了炫技,而是想让做配电网优化的人少走点弯路。
本文还有配套的精品资源,点击获取