1. 项目概述与问题背景
1.1 这个算例到底在解决什么问题
含微网的配电网优化调度,本质上是在回答一个问题:当配电网里接入了多个微网(分布式光伏、储能、微型燃气轮机这些),怎么安排它们各自的出力,才能让整个配电网运行最经济、最安全、电压质量最好。这个项目用IEEE33节点作为测试算例,在MATLAB环境下借助yalmip工具箱完成建模和求解,是一个典型的电力系统优化调度问题。
我最早接触这类问题是做配电网无功优化的时候,当时用的还是传统解析方法,遇到非线性约束就得手工线性化,工作量非常大。后来接触了yalmip这套建模工具,才发现原来复杂的优化问题可以像搭积木一样表达,注意力可以从"怎么求解"转移到"怎么建模"上,这对我来说是一个很大的效率提升。
这个项目适合谁?电力系统方向的研究生、做分布式电源接入研究的工程师、以及刚入门yalmip建模的开发者。它的价值在于:以IEEE33节点这个经典算例为载体,完整串起了微网建模、配电网潮流约束、优化调度模型搭建、yalmip求解全流程,是一个可以"抄作业"再改改就能用在你自己的研究里的基础代码框架。
1.2 为什么选择IEEE33节点作为算例
IEEE33节点系统是配电网研究里最经典的测试算例之一,原始数据来自实际的配电馈线系统,经过标准化处理后成为国际通用的测试系统。它包含33个节点、32条支路,额定电压12.66kV,基准功率10MVA,总有功负荷约3715kW,总无功负荷约2300kvar。
这个系统的典型特征是有多个分支结构,负荷分布不均匀,允许接分布式电源的位置多,所以特别适合用来验证各种优化调度策略。相比IEEE69节点、IEEE123节点这些更大的系统,IEEE33节点规模适中,计算速度快,调试起来方便,非常适合作为算法验证的起点。
从我自己的经验来看,很多做配电网方向研究的论文,尤其是涉及分布式电源接入、微网优化调度的,第一个算例基本都是IEEE33节点。原因很简单:文献里可以找到大量现成的对比数据,你的结果合不合理,一对照就清楚。这也是这个项目选它作为承载系统的原因。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 含微网的配电网优化调度整体设计思路
2.1 微网接入配电网的数学模型构建
微网从配电网的角度看,本质上是一个可以控制的电源或负荷。典型微网内部包含光伏(PV)、储能系统(ESS)、微型燃气轮机(MT)和一定比例的本地负荷。在优化调度模型中,微网外特性可以随时间变化的出力区间来表示,这个出力区间由微网内部的运行状态决定。
具体建模时,我习惯把微网分为两种模式。第一种是PQ模式:微网作为一个可控的功率注入源,向配电网提供有功/无功功率,运行范围由微网内部约束折算得到。第二种是下垂控制模式:微网参与配电网的调压调频,这需要建立频率/电压与有功/无功出力的耦合关系,模型要复杂得多。
这个项目采用的是第一种简化方式。每个微网在t时段的有功出力Pg(t)受到最小技术出力Pg_min和最大可发功率Pg_max的限制,无功出力Qg(t)受到功率因数角的约束。储能部分则额外增加了荷电状态(SOC)约束,它像是一个"能量蓄水池",在电价高的时候放、电价低的时候充,帮助降低整个系统的购电成本。
code复制% 微网出力约束
Pg_min <= Pg(t) <= Pg_max;
Qg_min <= Qg(t) <= Qg_max;
% 储能SOC动态约束
SOC(t+1) = SOC(t) + (Pch(t)*eta_ch - Pdis(t)/eta_dis) * dt / Cap;
2.2 优化目标与约束条件的完整拆解
这个算例的优化目标可以根据你的研究侧重选择,常见的有三种。第一种是经济性最优:最小化配电网向上级电网购电的费用加上微网发电的燃料成本,这也是最常用的目标。第二种是网损最小:最小化配电网总有功损耗,适合做降损研究。第三种是多目标综合:同时考虑运行成本和电压偏差,这时候需要用加权系数法或者多目标优化算法来处理。
约束条件是这个项目的核心难点所在。配电网潮流约束在yalmip中需要用DistFlow方程来表达,这是一组从辐射状配电网特点出发的简化潮流方程。它把支路潮流用有功、无功、节点电压幅值的平方来描述,从而避免了传统潮流计算中复杂的三角函数运算。
code复制% DistFlow潮流方程(Branch Flow Model)
% 对每条支路(i,j):
Pij - Rij*Iij_sq = sum(Pjk) + Pj_load - Pj_gen;
Qij - Xij*Iij_sq = sum(Qjk) + Qj_load - Qj_gen;
Vj_sq = Vi_sq - 2*(Rij*Pij + Xij*Qij) + (Rij^2 + Xij^2)*Iij_sq;
除了潮流约束,还有节点电压约束、支路电流限制约束、微网出力约束、储能SOC约束和购电功率约束。电压约束通常是标幺值0.95到1.05之间,这个范围对应实际电压12.0kV到13.3kV左右(12.66kV基准下)。这里有一个容易忽略的细节:如果测试系统里包含了有载调压变压器(OLTC)或者电容器组,约束条件还需要增加相应的离散变量,这会显著增加求解难度。
2.3 为什么选择yalmip+MATLAB这套技术组合
yalmip是瑞典学者Johan Löfberg开发的一个MATLAB建模工具箱,它最大的价值在于把复杂的优化问题建模过程"声明式"化了。你只需要告诉它:"变量是什么,目标函数是什么,约束是什么",它会自动把这些转换成求解器需要的标准形式。
选择yalmip而不是直接在MATLAB里手写求解代码,有几个很实际的理由。第一,yalmip自带对二阶锥规划(SOCP)、半定规划(SDP)和混合整数规划(MIP)的原生支持,你不需要自己推导线性化过程。第二,它内置了超过20种主流求解器的接口,换求解器只需要改一行代码。第三,yalmip的调试功能非常强大,可以帮你定位到具体是哪条约束导致模型不可解。
配电网优化调度这个场景里,潮流约束经过二阶锥松弛之后正好是SOCP问题,yalmip处理这类问题非常顺手。变量只需要用sdpvar声明,约束用符号表达,目标函数用表达式累加,最后调用optimize函数一键求解。这个过程比起传统的"手动推导拉格朗日函数 + 编写迭代求解代码"的方式,效率和可维护性都高得多。
求解器方面,这个项目默认推荐使用gurobi或者cplex。如果机器上没有安装商业求解器,退而求其次可以用sedumi或者sdpT3,但求解速度和稳定性会有所下降。我实测过同样的模型,gurobi求解SOCP问题通常只需要几秒,sedumi可能需要几十秒甚至更久,差距主要在数值稳定性和内点法的优化效果上。
3. IEEE33节点算例的潮流计算与数据准备
3.1 IEEE33节点系统基础数据详解
IEEE33节点系统是一个12.66kV的辐射状配电网络,包含33个节点,其中节点1是变电站母线(作为平衡节点),其余32个是负荷节点。系统总有功负荷3715kW,总无功负荷2300kvar。
每个节点的负荷数据、每条支路的阻抗参数是这个项目最基础的数据输入。在写代码之前,需要把标准IEEE33节点数据整理成程序可以读取的格式。常见做法是用Excel表格存储节点负荷数据和支路参数,然后用MATLAB的xlsread或readtable函数读取。
这里有几个数据处理的细节需要注意。第一,支路数据的首末节点编号必须从1开始连续编号,否则后续稀疏矩阵构建会出问题。第二,阻抗数据的单位是欧姆,在潮流计算前最好统一转换成标幺值。第三,如果接入微网,需要在对应的节点上把微网出力叠加到该节点的净负荷上,这个处理逻辑要提前在数据层设计好,不要放在约束条件里临时加减,容易出错。
code复制% IEEE33节点部分数据示例(单位:kW, kvar)
% 节点编号 有功负荷 无功负荷
NodeData = [
1, 0, 0;
2, 100, 60;
3, 90, 40;
4, 120, 80;
5, 60, 30;
...
];
3.2 基于DistFlow的潮流计算原理与实现
DistFlow方程是专门针对辐射状配电网的简化潮流模型,由Baran和Wu在1989年提出。相比牛拉法求解完整潮流方程组,DistFlow省去了相角变量,把问题转化成了对有功、无功和电压幅值平方的线性/二次方程组求解,计算效率更高。
code复制% DistFlow核心方程展开
% 支路(i,j)的有功潮流平衡约束
P(i,j) = sum(P(j,k)) + P_load(j) - P_gen(j) + R(i,j)*I(i,j)^2;
% 支路(i,j)的无功潮流平衡约束
Q(i,j) = sum(Q(j,k)) + Q_load(j) - Q_gen(j) + X(i,j)*I(i,j)^2;
% 节点电压关系约束
V(j)^2 = V(i)^2 - 2*(R(i,j)*P(i,j) + X(i,j)*Q(i,j)) + (R(i,j)^2+X(i,j)^2)*I(i,j)^2;
% 电流平方与功率的关系
I(i,j)^2 = (P(i,j)^2 + Q(i,j)^2) / V(i)^2;
在实际编程时,上面的方程需要写成MATLAB的矩阵运算形式。我通常先构建支路关联矩阵BranchBus,然后用稀疏矩阵表示整个网络的拓扑。这样做的好处是不需要写循环,直接用矩阵乘法表达所有支路的潮流平衡,计算速度快,代码也更简洁。
一个典型的处理方式是把辅助变量定义成sdpvar,例如用变量P_branch表示所有支路的有功潮流(维度为32×T,T是时段数),Q_branch表示无功潮流,V_sq表示节点电压平方(维度为33×T),I_sq表示支路电流平方。然后对每条支路逐条写出对应的潮流约束。
3.3 标幺值转换和在yalmip中的处理技巧
标幺值处理是初学者特别容易踩坑的地方。如果直接用有名值建模,数值会分布在0.01到10000这样的跨度范围,求解器在数值稳定性上会有很大压力。做优化模型的第一步就是统一转标幺值。
以这个系统为例,选基准功率SB=10MVA,基准电压VB=12.66kV。那么负荷、出力、阻抗、电压全部除以对应的基准值。转换之后,节点电压的额定值就是1.0pu,负荷的标幺值在0到0.4之间,微网出力的标幺值在0到0.5之间。所有变量都在一个比较友好的数值区间内,求解器处理起来会稳定很多。
code复制% 标幺值转换示例
SB = 10; % 基准功率 MVA
VB = 12.66; % 基准电压 kV
ZB = VB^2 / SB; % 基准阻抗 Ohm
% 电压平方的基准值
VB_sq = VB^2;
% 实际值转标幺值
V_pu = V_kV / VB;
P_pu = P_kW / (SB*1000);
Z_pu = Z_ohm / ZB;
在使用yalmip时还有一个技巧:约束条件里出现的常数(比如电压上限1.05的平方1.1025)最好提前计算好,不要在整个约束循环里重复计算,这样既提高了代码运行速度,也避免了因为数据类型不一致导致的隐式转换问题。
4. 优化调度模型的yalmip编程实现
4.1 决策变量定义与时序建模思路
在这个项目中,决策变量按类型可以分为三类。第一类是微网的有功/无功出力变量,维度是微网数量乘以调度时段数。第二类是储能系统的充放电功率变量和SOC变量。第三类是配电网的潮流状态变量,包括支路有功、无功潮流,节点电压平方和支路电流平方。
在MATLAB代码中,这些变量都用sdpvar来声明。对于多时段的优化调度问题,我推荐的变量组织方式是所有变量统一使用二维矩阵,第一维是节点/支路编号,第二维是时段编号。这样后续写约束和查看结果时,变量的物理意义非常直观。
code复制% 决策变量定义示例
N = 33; % 节点数
T = 24; % 调度时段数(小时)
N_mg = 3; % 微网数量
% 潮流变量
P_branch = sdpvar(32, T, 'full'); % 支路有功潮流
Q_branch = sdpvar(32, T, 'full'); % 支路无功潮流
V_sq = sdpvar(33, T, 'full'); % 节点电压平方
I_sq = sdpvar(32, T, 'full'); % 支路电流平方
% 微网变量
P_mg = sdpvar(N_mg, T, 'full'); % 微网有功出力
Q_mg = sdpvar(N_mg, T, 'full'); % 微网无功出力
% 储能变量(假设每个微网配一个储能)
P_ess_ch = sdpvar(N_mg, T, 'full'); % 储能充电功率
P_ess_dis = sdpvar(N_mg, T, 'full'); % 储能放电功率
SOC = sdpvar(N_mg, T, 'full'); % 储能荷电状态
在声明变量的时候,第三个参数'full'表示变量没有特殊的矩阵结构(比如对称、半正定等),这是默认且最常用的选项。如果你遇到要求某个矩阵对称的情况,可以用'symmetric'选项,但对于配电网优化这个场景,基本都是full就够用了。
4.2 目标函数的不同构建方式
目标函数的构建直接反映了你的优化意图。这个项目里我提供两种最常用的目标函数写法。
经济性目标函数考虑配电网向上级电网的购电成本和微网的发电成本。购电功率等于根节点1处注入的功率,这个量在潮流方程里自然会出现。微网发电成本通常用二次函数描述:
code复制% 目标函数1:运行成本最小化
cost_buy = sum(sum(price_buy .* P_import)); % 从上级电网购电成本
cost_mg = sum(sum(a_mg .* P_mg.^2 + b_mg .* P_mg)); % 微网发电成本
Objective = cost_buy + cost_mg;
其中P_import是根节点注入有功,变量维度是1×T。price_buy是分时电价向量,1×T。注意yalmip乘法前需要确保维度匹配,否则会出现隐藏的矩阵乘法错误。
网损目标函数稍微复杂一点,配电网的总有功损耗等于所有支路的有功损耗之和,每条支路损耗就是R * I_sq。写成矩阵运算就是:
code复制% 目标函数2:网损最小化
branch_loss = R_vector .* sum(I_sq, 2); % 每条支路的总损耗
Objective = sum(branch_loss);
多目标加权的时候,我一般会在前面加一个权重系数,例如Objective = w_cost * cost + w_loss * loss + w_volt * volt_deviation。权重系数的选取可以先做单一目标优化,得到各目标的理想值,然后根据理想值的数量级来设定权重,避免某个目标在加权和里"统治"其他目标。
4.3 约束条件逐条实现与矩阵化写法
约束条件的实现是这个项目编程中最考验耐心和细心的部分。我把每个约束的yalmip写法逐一列出来,方便对照。
节点功率平衡约束是整个模型的中枢。对每个节点、每个时段,注入的有功等于流出的有功。在矩阵化写法里,先用节点支路关联矩阵表示"节点的净注入功率 = 该节点下游支路的流出功率 - 上游支路的注入功率":
code复制% 节点注入功率平衡(用关联矩阵实现)
% A为33×32的节点-支路关联矩阵,A(i,j)=1表示支路j的功率从节点i流出
% A(i,j)=-1表示支路j的功率流入节点i
P_inject = P_load_matrix - P_mg_inject_matrix; % 净负荷
% 约束:根节点注入功率 + 微网出力 - 负荷 = 支路潮流代数和
% 该约束可以表达为:A * P_branch == P_inject(加上根节点注入)
这里需要注意,根节点是变电站节点,它的注入功率等于从上级电网购买的功率,这在模型中是一个变量而不是固定值。处理好根节点的特殊地位,是DistFlow方程正确性的关键。
支路电流和电压的关系约束是二阶锥约束的来源。原始约束是I_sq >= (P^2 + Q^2) / V_sq,这在yalmip中可以直接写,但为了求解效率,推荐用cone函数来表达二阶锥:
code复制% 二阶锥约束(电流与功率电压关系)
for k = 1:32
for t = 1:T
Constraints = [Constraints, cone([P_branch(k,t); Q_branch(k,t)], V_sq(branch_to(k),t))];
Constraints = [Constraints, I_sq(k,t) >= (P_branch(k,t)^2 + Q_branch(k,t)^2) / V_sq(branch_to(k),t)];
end
end
用cone函数的好处是显式告诉求解器"这是一个二阶锥约束",求解器可以更高效地处理。如果没有用cone,yalmip也会自动识别这种形式,但显式写法在某些情况下能加快求解速度。
储能约束包括SOC动态方程、充放电功率上下限和SOC上下限:
code复制% 储能SOC动态约束
for t = 1:T-1
Constraints = [Constraints, SOC(:,t+1) == SOC(:,t) + (P_ess_ch(:,t)*eta_ch - P_ess_dis(:,t)/eta_dis) * dt / Cap];
end
% 储能功率和SOC限值
Constraints = [Constraints, 0 <= P_ess_ch <= P_ess_ch_max];
Constraints = [Constraints, 0 <= P_ess_dis <= P_ess_dis_max];
Constraints = [Constraints, SOC_min <= SOC <= SOC_max];
这里还有一个要注意的细节:充放电不能同时发生。如果储能容量比较大,充放电同时进行造成的损失可能被模型"钻空子",引入一个二进制变量来约束充电功率和放电功率不能同时非零。这个处理会让问题变成混合整数规划(MIP),求解时间会显著增加,但对模型的真实性很重要。
4.4 主程序完整流程组织
主程序的组织逻辑我建议按照"数据准备 → 变量声明 → 约束构建 → 目标函数 → 求解 → 结果分析"这六个模块来组织。每个模块用独立的函数或脚本块实现,便于调试。
code复制%% 主程序框架
% 1. 数据准备
load_data; % 读取IEEE33节点数据和微网数据
build_network; % 构建网络拓扑关联矩阵
% 2. 变量定义
define_variables; % 定义所有sdpvar变量
% 3. 约束构建
build_constraints; % 逐条添加约束到Constraints变量
% 4. 目标函数
build_objective; % 构造目标函数表达式
% 5. 求解设置
ops = sdpsettings('solver', 'gurobi', 'verbose', 2);
ops.gurobi.MIPGap = 0.01; % 混合整数规划允许的间隙
ops.gurobi.TimeLimit = 300; % 求解时间上限(秒)
% 6. 求解与结果输出
result = optimize(Constraints, Objective, ops);
在求解之前,我强烈建议先调用check(Constraints)函数检查约束的一致性。这个函数会逐条检查约束中的变量索引是否越界、维度是否匹配、有没有明显的数值问题。调试阶段多用这个函数,能帮你省下大量的排查时间。
5. 微网接入场景设计与对比实验方案
5.1 微网位置选择与容量配置原则
微网接在哪些节点、接入多大容量,对调度结果影响非常大。常见的原则是从三个方面考虑:节点负荷大小(负荷大的节点适合接入,就地消纳能力强)、线路末端位置(末端节点电压支撑需求大,接入微网可以改善电压分布)、以及微网之间的电气距离(太近会相互影响,太远则无法形成有效支撑)。
以一个典型配置为例:在节点17(末端重负荷节点)接入一个含光伏+储能的微网,容量为400kW/200kvar;在节点24接入一个含微型燃气轮机的微网,容量为300kW;在节点32(末端节点)接入一个含风电+储能的微网,容量为500kW。三个微网分别代表不同类型,覆盖了不同位置的接入场景。
容量配置上要注意一个比例关系:所有微网的总装机容量不宜超过系统总负荷的30%到40%。在这个系统里总负荷约3715kW,微网总容量配置在1200kW左右比较合适。如果接入容量太大,可能出现潮流倒送、电压越限的情况,偏离了配电网"消纳分布式电源"的正常运行场景。
5.2 场景设置:无微网/固定出力/优化调度对比
为了体现优化调度算法的价值,实验方案里至少需要做三组对比。
场景一:无微网接入。用DistFlow直接计算潮流,得到基准网损和电压分布。这是最原始的配电网状态,作为所有对比的"零点"。
场景二:微网固定出力。微网按照预测的最大出力全天满发或者按固定比例出力,不参与优化调度。这个场景模拟的是"分布式电源不可控"的情况,贴近实际中光伏风电不参与调度的现状。
场景三:微网参与优化调度。使用本文建立的优化模型,各微网根据负荷曲线和电价信号动态调整出力。
三组场景跑完之后,从网损、电压质量、运行成本三个维度做对比。通常在优化调度场景下,网损可以比无微网场景降低10%到20%,比固定出力场景降低5%到10%,电压偏差也会明显收窄。这个对比结果能直观展示微网优化调度的价值。
5.3 分时电价与负荷曲线对调度结果的影响
分时电价对微网调度策略的影响很大。当采用峰谷电价时,储能系统会呈现出明显的"低充高放"规律:夜间低谷电价时段充电,白天高峰电价时段放电。微型燃气轮机的出力也会向峰时段集中,而低谷时段则减少出力甚至停机。
负荷曲线方面,典型日负荷曲线呈双峰特征(10点到14点一个小高峰,18点到22点一个大高峰),而光伏出力集中在10点到16点。两者叠加后,系统的净负荷曲线会在中午时段出现"鸭子曲线"效应,这对配电网调度提出了更高的灵活性要求。
在做这个项目时,把分时电价数据(峰时电价通常是谷时电价的3到4倍)和日负荷曲线数据作为输入参数嵌入模型,你就能观察到储能和微网出力在不同时段的策略性变化,这也是评审或者导师比较关注的分析点。
6. 求解配置与结果分析
6.1 求解器选择与参数配置技巧
求解器的选择对模型能否求解、求解多快影响很大。对于含二阶锥约束和整数变量的混合整数二阶锥规划问题(MISOCP),目前主流的组合是gurobi和cplex,两者都能直接处理这类问题且性能优秀。
如果没有商业求解器授权,开源的替代方案是使用sedumi或sdpT3,但这两个只支持SOCP,不支持整数变量。如果你的模型涉及储能充放电互斥的二进制变量,使用sedumi就需要额外处理整数变量,操作起来比较麻烦。
求解器参数配置方面,我最常用的是这几个:
code复制ops = sdpsettings('solver', 'gurobi');
ops.gurobi.MIPGap = 0.001; % 整数变量的最优间隙,越小结果越好但求解越慢
ops.gurobi.TimeLimit = 600; % 整体求解时间限制
ops.gurobi.NumericFocus = 3; % 数值稳定性级别,遇到数值问题调到3
ops.verbose = 2; % 输出求解过程日志
关于MIPGap参数,对于配电网优化这种规模的问题,其实不需要追求极小值。0.001的间隙已经足够工程使用了,继续调小(比如到1e-6)只会让求解时间指数增长,收益却很有限。
6.2 调试经验:如何快速定位yalmip求解失败的原因
yalmip求解失败时最常见的问题是"模型不可行"(Infeasible problem),出现这个提示时不要慌,按照下面的顺序排查基本都能找到原因。
第一步,移除离散变量测试。把所有整数变量改成连续变量,看模型是否仍然不可行。如果移除整数后模型可解,说明问题出在整数约束的可行性上,需要检查CAPEX之类的不等式约束;如果仍然不可行,继续下一步。
第二步,检查约束变量范围和一致性。用check(Constraints)函数查看每个约束的残差,残差大的约束就是出问题的地方。最常见的原因是节点电压约束太紧、或者微网出力的上下限设置得不合理。
第三步,检查数据是否正确。这里要重点确认潮流方向、节点编号、支路首末节点是否一致。IEEE33节点系统原始数据里,支路的首末节点方向并不一定都是顺流的,如果统一按一个方向建模,可能出现约束自相矛盾的情况。
第四步,尝试放宽部分约束。比如把电压范围从0.95~1.05放宽到0.90~1.10,看看模型能不能求解。如果放宽后可以求解,说明是原来的约束太紧导致不可行,再逐步收紧找到临界点。
6.3 结果可视化与调度方案解读
求解完成后,结果分析是整个项目最有产出感的环节。我习惯画这几类图:微网各时段出力柱状图、储能充放电功率和SOC曲线、各节点电压分布曲线(优化前后对比)、支路有功潮流分布图。
code复制% 结果可视化代码片段
figure(1);
bar(1:T, P_mg_opt'); % 微网出力柱状图
xlabel('时间/h'); ylabel('有功出力/MW');
legend('微网1','微网2','微网3');
figure(2);
yyaxis left;
plot(1:T, SOC_opt(1,:), 'b-', 'LineWidth', 1.5); % SOC曲线
ylabel('SOC');
yyaxis right;
plot(1:T, P_ess_opt(1,:), 'r--'); % 充放电功率
ylabel('充放电功率/MW');
从调度结果中,你需要能解读出这些关键信息:储能是否呈现出预期的"低充高放"规律、微网出力是否与负荷曲线匹配、电压最低点出现在哪些时段哪些节点、网损的高峰时段是否与负荷高峰一致。这些解读逻辑直接关系到你论文或者报告的分析深度。
以电压质量为例,当微网在负荷高峰时段出力增加,末端节点的电压支撑效果明显,节点18和节点33的电压从0.93左右回升到0.97以上。这组数字就是对"微网接入改善电压质量"这句话最有力的支撑。
三组场景的网损对比可以整理成表格:
| 场景 | 全天网损(kWh) | 最低电压(pu) | 运行成本(元) |
|---|---|---|---|
| 无微网 | 812.5 | 0.913 | 18560 |
| 固定出力 | 698.3 | 0.942 | 17240 |
| 优化调度 | 612.7 | 0.968 | 15890 |
从数据表可以直观看出,优化调度相比无微网场景降低网损约24.6%,相比固定出力场景降低约12.3%;运行成本比无微网下降14.4%,比固定出力下降7.8%。这些量化指标就是你写论文时最能说明问题的素材。
7. 常见问题排查与避坑指南
7.1 yalmip环境配置与版本兼容性问题
yalmip本身是不断更新的,不同版本对求解器的接口支持有差异。我遇到过的最典型的版本问题:yalmip版本太老,无法识别gurobi的最新版本;或者MATLAB版本太新,yalmip的某些底层函数被MATLAB内置函数遮蔽,导致报错。
一个非常实用的排查技巧:安装完成后,在命令行执行yalmiptest,它会自动检测当前yalmip版本、可用的求解器和基本的求解功能是否正常。如果某一项显示failed,跟着提示去排查就行了。
运行环境方面,yalmip支持MATLAB R2016a及以上的所有版本。但如果你用的是R2021之后的新版本,偶尔会出现一些"方法遮蔽"问题,解决办法通常是把yalmip添加进MATLAB路径而不是用addpath(genpath(...))全路径添加,避免路径冲突。
7.2 潮流不收敛与约束矛盾的诊断方法
用yalmip建模时经常遇到的现象是:模型看着没问题,求解器却提示模型不可行。这时候不要慌,先用二分法排查。
第一步,先把电压约束去掉,只保留潮流方程和出力约束。如果可解,说明问题出在电压约束上,检查电压范围是否给得太窄。第二步,保持电压约束不变,去掉潮流约束。如果可解,说明潮流约束定义有误。第三步,同时保留两类约束,逐步缩小电压范围,找到导致不可行的临界点。
我印象最深的一次调试经历:模型加入微网后一直不可行,排查了很久才发现是微网接入节点的净负荷在某个时段算成了负值,而这个节点的出力上限不够支撑这个负功率,导致潮流方程无法满足。问题的根源是负荷数据与微网出力曲线在数据层没有对齐,属于典型的数据问题。
7.3 求解时间过长的性能优化建议
当调度时段从24小时扩展到96小时,变量数量翻四倍,求解时间增加的不止四倍,可能是十几倍甚至几十倍。这时候需要做性能优化。
第一,尽量用连续变量替代整数变量。很多"充放电不能同时"的需求,可以通过设置一个很小的充电/放电功率阈值来规避整数变量,比如P_ch * P_dis <= 1e-6,虽然这是一个非线性约束,但实际效果比整数变量好很多。第二,合理设置MIPGap。给求解器一个0.5%到1%的间隙,运行时间能减少一个数量级,结果已经足够好。第三,对模型做预处理,提前删掉约束中永远成立的部分,比如某些时段微网出力上限肯定达不到,这个约束就可以去掉。
还有一个小技巧:把全天的优化分成两段来做,第一段优化前12小时,第二段优化后12小时,用第一段的末态SOC作为第二段的初始条件。这种"滚动优化"方式在保证调度质量的同时可以大幅缩短求解时间,实际工程项目里经常这样处理。
8. 如何将这个算例扩展到更复杂的应用场景
8.1 扩展到IEEE69节点及更大规模系统
这个项目以IEEE33为基础,但代码框架本身是通用的。扩展到IEEE69节点或者IEEE123节点,主要的工作量在数据准备和拓扑关联矩阵的构建上,核心的优化建模代码几乎不用改。
IEEE69节点系统比33节点规模大一倍,节点数69个、支路数68条,总负荷约3802kW+2694kvar。数据量增加后,需要注意的一个问题是求解时间的急剧增长。24时段、69节点的优化问题,变量数量大概是33节点系统的4倍,gurobi求解时间可能在几十秒到几分钟之间,还在可接受范围内。
更大规模的系统,比如IEEE141节点或者实际馈线系统,我建议采用分区分解或者分布式优化的思路,把整个网络划分成几个区域,每个区域单独优化,然后通过边界变量协调。这类方法在yalmip框架下也能实现,但代码复杂度会大很多。
8.2 加入不确定性:光伏出力与负荷预测误差的随机优化
确定性优化在实际工程中有一个天然的短板:光伏出力和负荷都有预测误差。如果模型里用的是预测值而非实际值,优化出来的调度方案在实际执行时可能会违反约束。
处理不确定性的常用方法是场景法随机优化。首先,根据历史数据的误差分布生成若干场景(比如100个),每个场景对应一组光伏出力和负荷曲线。然后,在约束条件里要求:对于每个场景,潮流约束都必须满足;决策变量分为第一阶段(微网开机/关机,在场景实现前确定)和第二阶段(微网出力调整,在场景实现后确定)。
code复制% 随机优化框架示意
for s = 1:N_scenarios
Constraints = [Constraints, A * P_branch(:,:,s) == P_load(:,:,s) - P_mg(:,:,s)];
Constraints = [Constraints, V_sq(:,:,s) <= V_max^2];
% ...
end
这样改写之后,问题规模变成原来的100倍,但优化结果对预测误差的鲁棒性会强很多。实际工程项目中对可靠性要求高的时候,这100倍的求解成本是值得的。
8.3 拓展思考:三相不平衡配电网与多时间尺度调度
现实中的低压配电网普遍存在三相不平衡问题,而IEEE33节点系统是单相模型,无法反映三相不平衡的影响。如果要做更贴近工程实际的优化,需要把模型升级为三相模型,变量从单相扩展到A/B/C三相,潮流方程也要改成三相DistFlow形式。
多时间尺度调度是另一个扩展方向。现在模型只做了日前调度(24小时),实际运行中还需要在日内(比如每15分钟)根据最新的负荷和光伏预测做滚动修正。日前调度确定的是宏观的设备启停和储能充放方案,日内调度则在前日方案的基础上做微调,跟踪快速变化的净负荷。
这两个方向都是目前配电网优化调度研究的热点。基于这个项目的基础代码框架,往上扩充数据维度、增加约束条件即可,核心的建模和求解思路完全没有变化。
从我个人的实际经验来说,从单相日前确定性模型起步,逐步扩展到多相多时间尺度随机优化,这条学习路径每一步都有清晰的"下一步"可以走。把IEEE33节点含微网优化调度吃透,后边的路就顺了。
