去年我在复现一篇SCI一区论文时,第一次把“MPS预配置”这四个字真正吃透。简单说,配电网韧性提升就是让电网在台风、冰灾这种极端事件面前不仅扛得住,还能快速恢复;而MPS——也就是应急移动电源/移动储能装置——最关键的动作不是灾后匆匆赶路,而是灾前把电源放到最可能救命的节点上。这个“灾前放哪里、放几台”的决策,就是MPS预配置。这篇内容围绕Matlab代码实现展开,重点拆解《基于配电网韧性提升的应急移动电源预配置和动态调度》的上半部分,也就是预配置建模与仿真复现。适合正在做配电网韧性、移动储能调度、SCI论文复现的电力专业研究生和工程师参考。
这系列工作分上下两部分:上篇解决“提前布局”的问题,下篇解决“灾害发生后,已经出发的MPS怎么动态走位、什么时间接入系统”的问题。两个问题必须放在一起看,但预配置是第一步,也是最容易在复现时被低估的一步。你后面做的动态调度再漂亮,如果预配置阶段把MPS放错了节点,恢复效果直接打折,这是我在实测复现里最深的体会。
1. 灾害场景下的配电网韧性缺口:为什么MPS预配置值得单独深挖
1.1 MPS在韧性提升链条中的位置
配电网的传统可靠性指标,比如SAIDI、SAIFI,衡量的是长期平均停电水平。但极端灾害不一样,台风过境可能让几十条线路同时故障,变电站失压,部分区域直接形成孤岛。韧性强调的是系统在极端事件冲击下维持功能、快速恢复的能力,它关心的不是“一年平均停几次电”,而是“一场大灾下来,关键负荷能不能供上,恢复速度有多快”。
在这次复现的项目里,MPS指的是移动应急电源,可以是移动储能车、应急发电车,也可以是一体化移动储能装置。它最大的特点就是“可移动”:灾害发生前,它可以在配电网里选取若干个节点提前接入;灾害发生后,它又可以被调度到故障区域临时供电。所以MPS在韧性提升链条中处于一个非常有价值的位置——你不必为了小概率灾难在每座变电站都建永久电源,只需要准备几台MPS,放在最合理的位置,就可以在灾后覆盖多条关键路径。
1.2 预配置与动态调度的分工
很多第一次接触这个方向的同学容易把预配置和动态调度混为一谈。我复现时把这个问题拆成两阶段来理解。
第一阶段是预配置,发生在灾害来临前。此时气象预警已经发出,但具体哪条线路会断、故障范围多大还不确定。决策者要根据预测信息,把有限的MPS预先配置到备选节点上,并确定配置的容量类型。这个阶段的特点是“决策之后无法改变”,MPS一旦落地,位置就固定了,后续只有等灾后才能再次移动。
第二阶段是动态调度,发生在灾害发生后。此时故障场景逐渐清晰,MPS可以沿着道路网络向负荷点移动,但受到交通状况、移动时间和电量约束的影响,调度策略需要动态更新。这和预配置最大的区别在于——动态调度是“在信息逐步揭示中持续决策”,预配置是“在不确定性未揭示时一次性决策”。
这也就解释了为什么论文标题把两者并列:预配置决定了韧性提升的“上限”,动态调度决定这个上限能达到“多少”。我在复现过程中把预配置结果单独做了一套算例,就是因为后续动态调度要用到这些位置信息作为输入,如果这里就出错,下篇根本推不动。
1.3 我复现时对“预配置”的三层含义的理解
第一层是位置选择:在配电网的所有节点中,哪些节点适合放MPS?不是每个节点都适合,比如有些节点是纯负荷节点,但周围道路条件差,灾后MPS很难转移;有些节点靠近关键负荷中心,但容量太小接入不进去。论文里通常会预设一个候选节点集合,实际工程中还要考虑场地、交通、接入电压等级等因素。
第二层是容量配置:每台MPS的容量大小、功率等级怎么选。是放一台大容量的,还是放两台小容量的?我的复现经验是,把MPS容量离散为几种典型规格,用整数变量建模,求解规模会增加一些,但结果更贴近实际。
第三层是数量分配:手头一共有多少台MPS可用,是全部预配置出去,还是预留一部分应急备用?论文里一般会设置一个可用MPS总数上限,预配置数量不能超过这个上限。有些文献还会加一个“最少预配置数量”的约束,防止优化结果一台都不放——那在韧性场景里是不现实的。
2. MPS预配置数学模型的逐层拆解:目标、变量与约束
这一节我直接把我复现代码时用的模型骨架写出来。不同论文的细节会有些差异,但主干基本一致。你只要把每一块的数学含义搞明白,换到任何一篇SCI论文的模型上都不会卡壳。
2.1 目标函数:预配置成本与失负荷惩罚的权衡
MPS预配置的总目标可以写成最小化“预配置成本 + 灾害场景下的期望失负荷惩罚成本”。这个复合目标非常关键:如果你只最小化预配置成本,那结论肯定是“什么都不要放”;但如果只最小化失负荷,你就会把所有MPS全部塞满,完全不考虑经济性。所以目标函数必须在两者之间取平衡。
我使用的符号约定如下:
- $C^{pre}_{k}$:第k台MPS的预配置成本;
- $x_{i,k}$:0-1变量,候选节点i是否配置第k台MPS;
- $p_s$:第s个故障场景发生的概率;
- $z_{j,t,s}$:节点j在时段t、场景s下的切负荷量;
- $\lambda_j$:节点j的失负荷惩罚权重,常与负荷等级相关。
目标函数写成: $$ \min \sum_{i \in \Omega_{cand}} \sum_{k \in \mathcal{K}} C^{pre}{k} x{i,k} + \sum_{s \in \Omega_s} p_s \sum_{t \in \mathcal{T}} \sum_{j \in \mathcal{N}} \lambda_j z_{j,t,s} $$
这里有个细节值得展开:为什么失负荷要乘概率p_s?因为灾害场景是不确定的,你不能保证某种故障一定发生。用期望值做目标,相当于把所有可能的故障场景按概率加权求平均,这是随机优化里的标准做法。也有一些论文用鲁棒优化,只关心“最坏情况下失负荷最小”,这时目标就是max-min形式,但计算复杂度会显著上升。我的复现建议是:如果你是为了理解原理,先从期望值模型入手;如果你要复现一区论文的保守决策结果,再上鲁棒模型。
2.2 第一阶段变量与第二阶段变量的边界
预配置模型的难点之一是把变量按决策阶段分清楚:
第一阶段变量(灾前确定):
- $x_{i,k}$:候选节点i是否配置第k台MPS;
- $y_{i,k}$:如果MPS功率/容量也是决策量,可以用整数编号表示不同容量档位。
第二阶段变量(灾后场景s中确定):
- $z_{j,t,s}$:节点j在t时段场景s下的切负荷量;
- $P^{MPS}_{j,t,s}$:场景s中t时段节点j处MPS向配电网注入的有功功率;
- $Q^{MPS}_{j,t,s}$:对应的无功功率;
- $P^{flow}_{ij,t,s}$:支路ij的有功潮流;
- $V_{j,t,s}$:节点电压幅值。
我在代码里用不同的变量名前缀来区分两个阶段:x_开头的全部是一阶段,z_、p_、v_开头的都是二阶段。这样写的好处是不容易在约束里把变量混用——我第一次复现时就是把x变量误用进了场景循环,结果所有场景都看到同一个配置结果,排查了很久才发现。
2.3 关键约束:数量、容量、潮流与切负荷限制
MPS预配置模型的约束按功能可以分为四组:
第一组,预配置位置和数量约束。每个候选节点上至多配置一台MPS: $$ \sum_{k \in \mathcal{K}} x_{i,k} \le 1, \quad \forall i \in \Omega_{cand} $$ 整个系统配置的MPS总数不能超过可用台数 $N^{MPS}{max}$: $$ \sum{i \in \Omega_{cand}} \sum_{k \in \mathcal{K}} x_{i,k} \le N^{MPS}_{max} $$
第二组,MPS容量与注入功率的耦合约束。如果节点i配置了第k台MPS,那么灾后任何时段MPS注入功率都不能超过该台MPS的额定功率上限: $$ 0 \le P^{MPS}{j,t,s} \le \sum{k \in \mathcal{K}} P^{max}k x{j,k}, \quad \forall j,t,s $$ 这个约束非常重要,它把预配置的0-1决策和灾后的连续调度功率联系了起来。如果x=0,MPS功率必须为0;如果x=1,MPS功率上限才打开。类似的约束还有MPS的电量上限,可以用一个简化公式表示:灾后各时段累计放电量不超过额定电量。
第三组,配电网运行约束。这里要考虑灾害后配网可能已经破坏,需要用DistFlow潮流方程描述功率平衡: $$ P^{inj}{j,t,s} = \sum{l \in \delta(j)} P^{flow}_{jl,t,s} $$ 同时线路潮流不超过容量,节点电压保持在允许范围内。论文里通常会做线性化处理,把DistFlow方程简化成线性形式,这样整个模型就变成混合整数线性规划(MILP),可以直接用Gurobi、CPLEX这类求解器求解。
第四组,切负荷约束。切负荷量不能超过该节点原有负荷水平: $$ 0 \le z_{j,t,s} \le L_{j,t} $$ 有些论文还会加一个“关键负荷必须优先恢复”的约束,通过把不同负荷节点的λ_j设置成不同大小来实现。比如医院、通信基站这些节点λ_j取很大的惩罚系数,普通居民负荷取小一些。
2.4 不确定性的两种建模路线:场景还是鲁棒预算
我在复现初期最纠结的问题是:到底用场景法还是用鲁棒预算不确定集?两条路线各有适用条件。
场景法(随机规划)的做法是生成大量故障场景,每条线路以一定的概率被破坏,再用蒙特卡洛或K-means聚类把场景削减到几十个典型场景。这种做法优点是可以直接考虑场景概率,目标函数比较直观;缺点是场景数量一多,MILP规模会迅速膨胀,求解时间成倍增加。
鲁棒预算法的做法是不需要枚举场景,而是用一个不确定集合来描述可能发生的故障线路集合,比如“最多同时有Γ条线路故障”。目标函数变成最坏情况下的失负荷最小化。这个模型更保守,也更贴合“极端灾害下最坏情况”的思维方式,但求解时需要用到列与约束生成(C&CG)或Benders分解。
我复现代码时采用的是“场景法+期望值目标”的方式,因为作为上篇的预配置模块,场景法的代码结构更容易被读代码的人理解,而且后续做动态调度时可以很自然地复用同一个场景集合。如果你要复现的论文里明确写了鲁棒优化,那需要额外写一个CCG循环,我后面下篇动态调度时会再展开。
3. IEEE 33节点算例的数据准备:从论文参数到Matlab可计算矩阵
建模写代码之前,百分之八十的时间花在数据准备上。SCI论文往往只给算例网络名称和几个关键参数,具体到每条线路的阻抗、每个节点的负荷基准值,都得自己找、自己整理。
3.1 测试系统基础数据和节点/支路矩阵构建
这一项目我们采用经典的IEEE 33节点配电系统做测试,这是配电网韧性研究里最常用的算例网络之一,系统基准电压12.66kV,基准功率一般取10MVA,一共有33个节点、32条支路、5条联络开关支路,总负荷大约3715kW加2300kvar。
我建议把数据组织成两个矩阵,和Matpower的格式保持兼容。第一个是节点参数矩阵bus,每一行对应一个节点,列依次是:节点编号、节点类型、有功负荷(kW/kW基准)、无功负荷、电压幅值初值。第二个是支路矩阵branch,每一行对应一条支路,列依次是:支路编号、首端节点、末端节点、电阻(欧姆)、电抗(欧姆)、线路容量上限。
以部分数据为例,首段线路的数据大致如下表:
| 支路编号 | 首端节点 | 末端节点 | 电阻(Ω) | 电抗(Ω) | 容量上限(kVA) |
|---|---|---|---|---|---|
| 1 | 1 | 2 | 0.0922 | 0.0470 | 5000 |
| 2 | 2 | 3 | 0.4930 | 0.2511 | 3000 |
| 3 | 3 | 4 | 0.3660 | 0.1864 | 2500 |
| ... | ... | ... | ... | ... | ... |
把数据写成Matlab脚本时,我习惯直接用一个load_ieee33.m函数返回结构体:
function data = load_ieee33() data.baseMVA = 10; data.baseKV = 12.66; data.bus = [ 1 1 0 0 10.0; 2 1 100 60 10.0; 3 1 90 40 10.0; % ... 后续节点 ]; data.branch = [ 1 1 2 0.0922 0.0470 5000; 2 2 3 0.4930 0.2511 3000; % ... 后续支路 ]; end这里有一个关键细节:所有数据要么转成标幺值,要么统一转成有名值的相同量纲。我最早直接把欧姆和kW混合算,结果潮流线性化系数差了三个数量级,Gurobi直接报数值病态Illegal numeric value。后来全部用标幺值建模,问题就消失了。
3.2 候选预配置节点如何筛选
MPS不是每个节点都能放,需要先确定候选节点集合。论文一般不会把所有33个节点都作为候选,那样模型复杂度太高。我复现时的筛选逻辑是:
第一,优先选择关键负荷节点附近。核心负荷越多的节点,灾后供电迫切性越高,MPS放在这里可以最直接地支撑负荷。
第二,考虑网络拓扑结构。比如联络开关附近的节点、长馈线的末端节点、支路发生故障后容易形成孤岛的节点,都是潜在候选。可以用“节点失效影响度”或者“负荷供应脆弱性”来量化。
第三,参考N-1预想故障分析。依次断开系统内重要支路,统计失负荷节点,把受影响最严重的几个节点挑选出来作为候选。这个方法很朴素,但非常好用,复现起来也快。
在我的项目里,候选节点集合最终选为IEEE 33节点系统中的节点5、8、13、24、29,共5个节点。这个集合覆盖了不同馈线方向和关键负荷区域,可以让MPS配置方案具有代表性。你也可以根据需要扩大候选集合,但每增加一个候选点,整数变量数量会线性增加,求解时间随之上涨。
3.3 故障场景生成与场景削减
预配置阶段最耗时的不是建模,而是场景处理。我首先给每条线路设定一个故障概率,这个概率反映极端灾害下线路中断的可能性,通常取在0.05~0.2之间,具体数值取决于灾害等级和线路抗灾能力。然后我生成一个随机损坏状态向量,长度等于支路数,每一位为1表示该支路故障,0表示正常。我用一个简化的蒙特卡洛循环生成例如500个原始场景。
原始场景太多没办法直接扔进MILP,需要做场景削减。我采用同步回代消除法,核心步骤是:
- 定义两个场景之间的距离度量,常用的是故障线路集合的对称差集大小;
- 计算每个场景与其它场景的距离,找到距离最近的两个场景;
- 将其中概率较小的场景删除,并把它的概率叠加到保留的场景上;
- 重复操作,直到场景数降为目标值,比如保留20个典型场景。
这个算法的Matlab实现并不复杂,我代码里用一个约80行的函数scenario_reduction.m完成。减完之后的场景集合会存在一个矩阵scenarios.mat里,每一行是一个场景的故障线路状态,每一列对应一条线路是否损坏,同时还有一个概率向量weights。
我特别想提醒一句:场景削减的目标不是“砍到最小”,而是“在保留灾害空间分异性的前提下压缩规模”。如果你把场景削减到只剩三五个,每个场景的故障模式都天差地别,期望值会失真;但如果保留到80个以上,又是“边际递增、回头求解器”的噩梦。我常用的是20个场景,预配置模型的求解时间大约在几十秒到几分钟之间,比较适合反复调试。
3.4 MPS参数和成本参数的标定
MPS参数设定是论文复现中自由度最大的地方,因为不同论文的移动电源类型不一样。我按“移动储能车”这个定位来标定:
| 参数 | 数值 | 说明 |
|---|---|---|
| 单台MPS额定功率 | 300 kW | 也可以按100/300/500 kW分档 |
| 单台MPS额定容量 | 600 kWh | 对应2小时满功率放电 |
| 可用MPS总数 | 3~5台 | 灵敏度分析时从2台测到6台 |
| 单位预配置成本 | 500元/台(或者折算成本) | 也可以在500到2000之间调 |
| 失负荷惩罚系数λ | 关键负荷5000元/MWh | 普通负荷2000元/MWh |
注意,预配置成本不是MPS的购买成本,而是“提前布放并接入”这个过程产生的费用,包括运输、接入、调试、占用场地等。因为MPS本质上是资产,论文里更关心的是布局决策,不是资产采购。所以这部分成本在目标函数里的相对大小非常重要,它直接决定了优化结果是倾向多布放还是少布放。我在下篇里会对成本参数做灵敏度分析。
4. Matlab代码实现:Yalmip建模的关键片段与求解细节
4.1 程序目录结构与运行入口
我复现MPS预配置项目时采用的目录结构如下:
mps_pre_positioning/ ├─ run_pre_positioning.m % 主脚本 ├─ data/ │ ├─ load_ieee33.m % 生成节点支路数据 │ ├─ gen_scenarios.m % 生成/削减故障场景 │ └─ scenarios.mat % 削减后的场景与概率 ├─ model/ │ ├─ build_mps_model.m % 构建Yalmip优化模型 │ └─ solve_milp.m % 调用Gurobi/CPLEX求解 └─ utils/ ├─ scenario_reduction.m % 场景削减 └─ plot_pre_result.m % 结果可视化主脚本run_pre_positioning.m是入口,依次调用数据加载、场景生成、模型构建和求解。这样拆分的目的是把“优化模型”和“数据准备”分离。我调试的时候只需要改数据参数,不用动模型函数,特别方便。
4.2 变量定义和数据载入
在build_mps_model.m里,我先加载数据和场景,再定义变量。这里给出核心片段的示意代码:
function result = build_mps_model(data, scen, params) candNodes = params.candNodes; % 候选节点集合 numScen = size(scen.prob, 1); % 场景数 numPeriod = params.numPeriod; % 时段数 numBus = size(data.bus, 1); numMPS = params.numMPS; % 可用MPS台数 % 第一阶段变量:候选节点i是否配置第k台MPS x = binvar(length(candNodes), numMPS, 'full'); % 第二阶段变量:切负荷量 [节点 × 时段 × 场景] z = sdpvar(numBus, numPeriod, numScen, 'full'); % 第二阶段变量:MPS注入功率 [节点 × 时段 × 场景] pMps = sdpvar(numBus, numPeriod, numScen, 'full');这里有一个很常见的坑:SDPVAR变量的维度顺序是否和后面约束一致。我建议把“节点、时段、场景”这个顺序固定下来,在所有约束里都按这个顺序循环,不要一会儿节点在前、一会儿场景在前,否则你会在调索引上花掉大量时间。
然后定义目标函数:
Cpre = params.Cpre; % 单台预配置成本 lambda = params.lambda; % 各节点失负荷惩罚系数 obj = 0; % 预配置成本 obj = obj + sum(Cpre * x(:)); % 期望失负荷惩罚成本 for s = 1:numScen w = scen.prob(s); for t = 1:numPeriod obj = obj + w * sum(lambda .* z(:, t, s)); end end4.3 约束逐条实现与求解器配置
约束部分的代码我把关键几组列出来。第一组是每个候选节点最多配一台MPS:
cons = []; for i = 1:length(candNodes) cons = [cons, sum(x(i, :)) <= 1]; end % 总台数约束 cons = [cons, sum(x(:)) <= numMPS];第二组是MPS功率与预配置变量耦合:
PmaxMPS = params.PmaxMPS; % 单台MPS额定功率 for j = candNodes' idx = find(candNodes == j); % 候选节点索引 for t = 1:numPeriod for s = 1:numScen cons = [cons, pMps(j, t, s) <= PmaxMPS * sum(x(idx, :))]; cons = [cons, pMps(j, t, s) >= 0]; end end end第三组是节点功率平衡约束。这里用线性DistFlow简化,实际代码里我封装成一个子函数add_power_balance_cons。伪代码如下:
function cons = add_power_balance_cons(cons, data, V, pFlow, pMps, z, params) for s = 1:params.numScen for t = 1:params.numPeriod for j = 1:data.numBus % 支路注入功率 - 流出功率 = 节点负荷 - 切负荷 - MPS注入 cons = [cons, sum(pFlow(fromNode == j, t, s)) ... - sum(pFlow(toNode == j, t, s)) ... == data.bus(j, loadCol) - z(j, t, s) + pMps(j, t, s)]; end end end end这个线性化潮流里我把电压幅值也线性化了,模型就变成纯MILP。如果你想复现更精确的交流潮流,需要在第二阶段引入电压乘积项,但那会让模型变成MIQP甚至MINLP,求解难度高很多。大多数SCI里做MPS预配置都采用线性化潮流,因为决策问题的核心在“位置和容量的权衡”,不需要精确到电压相角。
第四组是切负荷上限约束:
for s = 1:numScen for t = 1:numPeriod cons = [cons, z(:, t, s) >= 0]; cons = [cons, z(:, t, s) <= loadDemand(:, t)]; end end求解器配置方面,我使用Gurobi:
ops = sdpsettings('solver', 'gurobi', 'verbose', 2, ... 'gurobi.MIPGap', 0.01, ... 'gurobi.TimeLimit', 600, ... 'gurobi.Threads', 4); optimize(cons, obj, ops);MIPGap取1%是我反复调试后的选择。取0.1%可以,但场景多的时候求解时间会翻几倍;工程上1%的优化间隙对预配置决策完全够用。取5%则结果波动太大,可能位置都会选错。
4.4 结果提取与绘图
求解完成后,用value提取变量值:
xVal = round(value(x)); zVal = value(z); pMpsVal = value(pMps); % 输出预配置方案 for i = 1:length(candNodes) k = find(xVal(i, :) > 0.5); if ~isempty(k) fprintf('节点%d 配置第%d台MPS\n', candNodes(i), k); end end注意x是0-1变量,提取时我用round处理,防止求解器返回0.9999这种数值。这样做的原因是Gurobi虽然会输出整数解,但由于浮点运算,个别变量可能带有微小误差,直接判断大于0.5比较稳妥。
绘图部分我用Matlab自带的图形函数,把33节点拓扑画出来,再用不同颜色标注MPS预配置节点和故障线路,这样一眼就能看出方案的空间分布。保存图形时用exportgraphics,比print更清晰,PDF格式适合写论文。
5. 复现实验中的结果分析、参数影响与避坑记录
5.1 不同MPS数量下的预配置方案对比
我用同样的故障场景集合,把MPS可用台数从2台逐步增加到6台,得到的预配置结果如下:
| MPS台数 | 预配置节点 | 配置台数 | 期望失负荷(MWh) | 相对失负荷下降 |
|---|---|---|---|---|
| 2台 | 5, 24 | 2台 | 28.5 | 基准 |
| 3台 | 5, 13, 29 | 3台 | 21.2 | 25.6% |
| 4台 | 5, 8, 13, 29 | 4台 | 16.8 | 41.1% |
| 5台 | 5, 8, 13, 24, 29 | 5台 | 14.1 | 50.5% |
| 6台 | 5, 8, 13, 24, 29 | 5台 | 14.1 | 50.5% |
这个结果非常符合工程直觉:从2台增加到5台,韧性提升效果明显;但到第6台时,系统已经“没有更好的地方可放”了,期望失负荷不再下降,说明边际效益趋于零。我在论文复现报告里专门画了这个边际效益曲线,用来解释为什么MPS数量不是越多越好——这其实对应了目标函数里预配置成本与失负荷惩罚之间的平衡。
从节点选择看,节点5和节点29在所有方案中几乎是必选的,因为这两个节点分别处于不同馈线的关键位置,覆盖能力很强。节点8在中段,节点13在负荷集中区域。这个规律可以作为后续工程应用的参考:预配置优先覆盖“馈线主干分叉点”和“高负荷密度末端”。
5.2 不确定预算与场景概率的灵敏度
我做的第二组实验是改变场景概率的构造方式。前面我设定每条线路故障概率在0.1附近,现在我把大灾害场景的概率提高,比如把某些主干线路的故障概率从0.1提高到0.3,预配置方案发生了明显变化:MPS向主干线路附近的节点集中,而不是均匀散布。
这背后的逻辑是:期望值目标函数中,概率越大的场景权重越高,优化结果会优先照顾高概率故障场景的供电恢复。所以如果你发现复现结果里MPS配置点看起来“不直觉”,先检查场景概率是不是设置得不合理,而不要急着改模型。
我还测试了失负荷惩罚系数λ的变化。λ从2000元/MWh提到10000元/MWh时,优化方案偏向于多配置MPS、覆盖更多节点;反过来,λ降低时,方案开始省钱,可能只配置2台。这说明λ和Cpre之间的比值是整篇论文里最敏感的一对参数,写复现报告时必须做灵敏度分析。
5.3 常见报错与排查思路
复现过程中我踩过的坑不少,这里挑几个最典型的列出来,都是可以直接对照排查的:
| 现象 | 原因 | 解决方案 |
|---|---|---|
| Gurobi报Illegal numeric value | 数据量纲不统一或参数过大 | 统一转成标幺值,建议baseMVA=10,电压标幺值=1.0 |
| Yalmip报Variable x not in model | 约束里用了x但因为索引写错,x没有实际参与约束 | 检查dimensions和sdpvar定义维度,用size(x)打印确认 |
| 求解结果x全部为0 | 预配置成本Cpre设置过高,或MPS功率/电量参数过小 | 降低Cpre,或检查pMps上限约束是否正确耦合x |
| 场景数大,求解时间超过几小时 | MILP规模爆炸 | 增加MIPGap到0.02,或把场景削减到15~20个,或用Benders分解 |
| 期望失负荷结果为0 | 惩罚系数λ太小或切负荷约束松弛过大 | 调大λ,或检查负荷数据是否被错误标幺化 |
| 功率平衡约束无解 | 孤岛场景下没有电源,潮流无法平衡 | 在MPS覆盖不到的孤岛节点允许一定切负荷比例,不要强制供电 |
我特别提醒一点:当你看到“无解”或者“x全为0”的时候,先不要怀疑求解器,应该先做可行性检查。我调试时经常用Yalmip的check函数:
disp(check(cons));这个命令会列出每条约束是否满足,一目了然。如果某一组约束显示“Not satisfied”,直接定位到对应的循环,九成都是索引写错了。这个方法比无脑打印变量值高效得多。
5.4 预配置结果如何衔接下篇动态调度
预配置阶段输出的是三样东西:候选节点的x配置决策、灾害场景集合和概率、以及每个场景下MPS供电功率的“可用空间”。动态调度阶段会在此基础上做三件事:
第一,根据灾后实时获取的故障信息,筛选出实际发生故障的线路集合,不再使用场景期望,而是用确定性场景或者滚动更新的预测场景。
第二,让MPS具备移动能力。上篇里MPS是“固定接入”,下篇里它会从一个节点移动到另一个节点,所以需要增加移动时间约束、道路通行约束、逐时段位置状态变量。
第三,把上篇的“预配置位置”作为下篇动态调度的初始条件。也就是说,动态调度不能随意改变灾前已经准备好的重要位置,但可以在MPS完成供电任务后转移到其他节点。这部分我做的时候踩了很多坑,尤其是MPS在途中移动时对配电网的支撑能力会暂时消失,需要精确建模。
这也就是为什么标题分“上下”两篇:没有预配置的动态调度是“没有起点的奔跑”,没有动态调度的预配置是“孤立的选址方案”,两者合在一起才构成完整的韧性提升闭环。
后面我会把动态调度部分的代码整理出来,重点讲清楚如何在Yalmip里引入连续时间状态的移动MPS约束,以及怎么把上篇生成的scenarios.mat继续复用。预配置场景文件、线路损坏状态矩阵和候选节点编号这些中间结果,我建议你提前统一存成结构体,不然后面做下篇时你会因为变量维度对不上而反复回头改数据。