
上篇我们把灾前预配置那部分跑通了MPS放在哪几个点、每台带多少容量、和DG与变电站的配合怎么处理。这篇把后半段补完——灾后动态调度。还是那套思路MPS不是配完就完事了极端事件发生之后配电网会裂成好几个孤岛MPS必须根据孤岛内的负荷重要程度、剩余电量和线路状态在时间轴上不断切换接入点。这一步的建模和求解比预配置要明显复杂一截尤其当你开始写代码、调求解器的时候。这篇文章写给三类人一是正在复现这类SCI一区论文、被公式和代码折磨的研究生二是做配电网韧性、孤岛恢复方向的横向项目想把论文算法落到实际程序的工程师三是刚入门YalmipGurobi想知道这类MISOCP问题怎么组织代码、怎么排查问题的人。我会把数学模型、代码结构、求解器设置、常见报错一次说清楚。1. MPS动态调度我们到底在优化什么1.1 灾前预配置和灾中动态调度怎么分工灾前预配置回答的是“MPS放在哪里”。它基于气象预报、灾害损失概率分布决定哪些变电站或重要节点附近预置移动电源预配置目标函数里一般会同时考虑购买或租赁成本、预置费用、期望负荷削减惩罚。很多论文会把它写成两阶段随机优化或者分布鲁棒优化上篇我们已经在Matlab里把这个阶段跑通了。动态调度回答的是“MPS怎么跑”。灾害发生、系统进入孤岛运行后负荷曲线在变、DG在变、某些线路随时可能再次故障MPS不能完全固定在预配置点不动。它需要在时间尺度上决定什么时候接到哪个节点、输出多少功率、什么时候必须留出迁移时间、迁移过程中如何保证孤岛供电不中断。动态调度的输入是预配置阶段的输出也就是各台MPS的初始位置和初始能量以及灾害发生后系统拓扑、可用线路、负荷预测和DG出力曲线。这两个阶段最大的区别在于预配置是“不确定下的决策”动态调度是“确定场景下的时序决策”。因此下篇的模型里时间和状态变量会成倍增加比如位置指示变量要写成三维数组(MPS编号 × 节点 × 时段)而不是预配置阶段简单的二维布置矩阵。代码组织方式也要跟着改。1.2 动态调度的时间尺度和对象时间尺度上这类工作一般取灾后24小时到72小时时间步长1小时或者15分钟。我的复现习惯是先按1小时步长把完整流程跑通确认结果合理后再尝试细化到30分钟或更小步长。MPS的迁移时间通常考虑为1个时间步长如果步长太小迁移时段的建模就要引入启动变量和持续时间约束复杂度迅速上升。调度对象包括移动储能电源MPS、分布式电源DG、可恢复负荷、分段开关和联络开关。MPS在动态调度里本质是一个“可控移动电源”。它既能作为孤岛的主电源也能作为DG的补充。建模时要把它的有功输出、无功输出、剩余电量、位置状态、迁移状态全部纳入优化否则会出现“一台MPS同时给两个孤岛供电”这种物理上不可能的结果。1.3 复现前必须想清楚的问题动笔写代码之前先把下面几个问题理清楚否则后面反复返工第一论文里MPS到底是移动储能还是移动柴发储能需要考虑能量状态SOC的时间耦合柴发只需要考虑出力上限和迁移时间。标题写的是“应急移动电源”很多这类文献实际上默认是电池储能所以SOC状态方程不能漏。第二潮流用的是线性DistFlow还是二阶锥DistFlow有的论文为了和MISOCP求解框架匹配会直接用线性化潮流有的为了精确反映电压和无功会用二阶锥松弛。这直接决定你的约束长什么样。第三网络拓扑是固定还是动态重构动态调度阶段如果允许开关操作改变孤岛拓扑那么拓扑变量也要加入优化问题从MILP变成MISOCP求解难度完全不同。很多复现文章里“动态调度”其实没有做重构只是把预配置点的连接状态随时间变化拓扑仍然是灾后固定的。2. 动态调度的数学模型目标函数和关键约束2.1 目标函数加权恢复负荷与切换惩罚的平衡动态调度的目标很直接在调度周期内最大化恢复的负荷总量同时照顾负荷重要程度。我在复现时使用的是加权能量恢复形式max ∑_{t1}^{T} ∑_{i1}^{N} ω_i · P_{i,t}^{rec} · Δt − λ · ∑_{m1}^{M} ∑_{t2}^{T} a_{m,t}其中 P_{i,t}^{rec} 是节点 i 在时段 t 恢复的有功负荷ω_i 是负荷权重λ 是对MPS切换动作的惩罚系数。a_{m,t} 是迁移启动变量取值1表示第 m 台MPS在时段 t 开始从原节点迁移到新节点。权重 ω_i 的设定非常关键。论文里一般把负荷分成一级、二级、三级一级权重可以取10二级取3三级取1。如果所有权重都取1优化结果会变成“谁近就恢复谁”完全失去工程意义。λ 也不能设得太大。我踩过这个坑把 λ 设为和负荷权重同一量级结果MPS干脆一动不动。后来把 λ 设为最大日负荷恢复收益的千分之一才得到合理的“必要迁移”行为。2.2 DistFlow潮流约束与二阶锥形式动态调度里最核心的物理约束是潮流约束。配电网辐射状结构下最常用的是DistFlow分支潮流方程。我复现的版本先使用了忽略网损的线性DistFlow跑通后又升级到二阶锥松弛版本。线性DistFlow对于每个时段 t 和每条支路 (i,j) 满足P_{ij,t} Σ_{k: j→k} P_{jk,t} P_{j,t}^{inj} Q_{ij,t} Σ_{k: j→k} Q_{jk,t} Q_{j,t}^{inj} U_{j,t} U_{i,t} − 2 (r_{ij} P_{ij,t} x_{ij} Q_{ij,t})节点注入功率 P_{j,t}^{inj} P_{j,t}^{DG} P_{j,t}^{MPS} − P_{j,t}^{rec}。这个形式在Matlab里用sdpvar加上索引循环非常容易写问题规模不大时求解也很快。如果原论文用的是二阶锥则要在支路功率和电压之间加上标准SOC松弛约束。Yalmip写法可以简化成norm([2*P_ij; 2*Q_ij; U_i - U_j], 2) U_i U_j一类的形式。注意SOC松弛只能放宽最终求解出来如果松弛紧度不满足要求需要检查对偶间隙或者加大惩罚项。2.3 MPS位置状态与迁移时间约束MPS动态调度的难点在于位置变量和迁移变量的耦合。每台MPS同一时刻只能在一个节点或者处于迁移状态。我用的约束是Σ_{i1}^{N} z_{m,i,t} a_{m,t} ≤ 1, ∀m,t如果第 m 台MPS在 t−1 时段位于节点 i在 t 时段位于节点 j 且 i ≠ j则必须触发迁移启动变量a_{m,t} ≥ z_{m,i,t-1} z_{m,j,t} − 1, ∀i≠j, ∀m, ∀t≥2当 a_{m,t}1 时MPS在这个时段不能输出功率0 ≤ P_{m,t}^{MPS} ≤ P_{m}^{max} · (1 − a_{m,t})这个写法有一个隐含假设迁移耗时1个时段迁移完成后立刻可以接入新节点。如果论文考虑了更长的迁移时间比如2小时就必须增加“迁移持续时间”约束否则MPS可以在两个节点间瞬移结果看起来很漂亮物理上完全不可行。2.4 储能能量状态约束MPS作为移动储能还要满足能量状态方程E_{m,t1} E_{m,t} − P_{m,t}^{MPS} · Δt并且有上下限约束 E_{min} ≤ E_{m,t} ≤ E_{max}。初始时刻 E_{m,1} 来自预配置阶段给出的初始SOC一般取0.8到0.9。如果考虑充电能量方程里加充电功率项但灾后孤岛场景里MPS充电的机会很少大部分论文直接忽略。能量约束要特别注意单位统一。如果功率单位是MW时间步长是小时那么能量单位是MWh。我在第一次复现时就没注意把功率单位当成MWh导致SOC曲线不是超过上限就是掉到零以下整个模型怎么调都不可行。2.5 网络拓扑成环问题隐藏的坑如果动态调度允许开关重构还必须约束网络不出现环网。最常用的方法是单商品流约束对每个恢复节点在网络图上引入一个虚拟流量 f根节点电源点发出流量其他节点消耗单位流量每条通电线路上流量不超过一个大M倍的线路状态变量。这样既保证连通性又保证辐射状无环结构。在Yalmip里这个约束写起来不复杂但坑在根节点的选择。MPS接入节点、DG节点、变电站节点都可以作为根节点。如果一个孤岛同时包含一台MPS和一个DG流量可以从MPS节点发出DG节点也必须被覆盖。我建议把“所有含电源的节点集合”作为候选根节点引入一个辅助二值变量选择根节点否则可能出现孤岛内有电源却无法供电的矛盾结果。3. MatlabYalmipGurobi代码实现的关键步骤3.1 代码整体框架分模块组织动态调度复现的Matlab代码我建议按五个模块组织数据导入模块、变量定义模块、约束构建模块、求解模块、结果绘图模块。项目从一个小算例开始IEEE 33节点配电网3台MPS24个时段可用线路若干。这个规模下如果不做任何预处理二值变量大概千量级Gurobi可以在几分钟内得到一个可用的MIP解。我代码里的核心结构是这样的% 变量定义 z_mps binvar(nm, nb, T, full); % MPS位置 a_mv binvar(nm, T, full); % 迁移启动 p_mps sdpvar(nm, T, full); % MPS有功输出 q_mps sdpvar(nm, T, full); % MPS无功输出 e_mps sdpvar(nm, T, full); % MPS能量状态 p_rec sdpvar(nb, T, full); % 各节点恢复有功负荷 ...位置变量我用了三维二值变量维度是MPS数×节点数×时段数。对于33节点系统这个变量规模不算大但如果换成IEEE 123节点三层循环构建约束会非常痛苦。建议先在候选节点集合里做预筛选只允许MPS接入那些在灾后仍带电或可通过操作恢复的节点。3.2 约束构建循环一定要向量化很多初学者会在Matlab里写三层嵌套循环来构建约束比如“for m, for i, for j, for t”结果构建约束的时间比求解时间还长。我的经验是把不涉及迁移耦合的潮流约束、能量约束全部写成矩阵形式只有位置迁移耦合约束才用循环。迁移耦合约束尤其要注意“机器生成约束爆炸”的问题。如果对所有 i≠j 都生成约束33节点系统一台MPS就会生成超过1000条约束。实际上MPS不会从任意节点直接跳到任意节点只需要在候选可达节点对之间生成a_move z_old z_new - 1即可。这一步能让约束数量减少一半以上。3.3 二阶锥约束的写法Yalmip对二阶锥的支持依赖变量向量的拆分方式。正确写法是使用norm(...) ...的形式。比如对于一条支路写cons [cons, norm([2*p_ij; 2*q_ij; u_i - u_j], 2) u_i u_j];如果误写成平方形式Yalmip会把它当非凸约束处理报错或者求解器直接退出。遇到这类问题优先检查有没有把u_i和u_j写成V_i^2而不是V_i。二阶锥形式里母线电压变量一定是电压平方项不能直接使用电压标幺值。3.4 求解器设置与MIP参数动态调度最终是一个MISOCP或者MILP问题。我用的是Yalmip Gurobi组合求解选项非常重要options sdpsettings(solver, gurobi, ... gurobi.mipgap, 0.02, ... gurobi.mipfocus, 2, ... gurobi.timeLimit, 1800, ... gurobi.nodemethod, 2, ... verbose, 2); optimize(cons, objective, options);mipgap设成0.02表示最优性间隙2%即可停止。SCI一区复现阶段这个精度足够判断算法规律不必追求0.001%的间隙。mipfocus设为2是让Gurobi记忆节点信息对于大规模整数问题能显著加速。timeLimit 1800秒防止程序无限跑下去。如果求解结束sol.problem不等于0我会用yalmip(diagnostics)查看求解器返回的具体错误。90%的情况可以归结为两类模型不可行、求解器未安装或接口路径未配置。3.5 结果导出和可视化复现动态调度最少要画三张图MPS位置时间热力图、SOC能量曲线、恢复负荷功率曲线。第一张图能直观看出MPS什么时段在哪个节点第二张图验证能量约束是否正确第三张图和论文中的负荷恢复曲线做对比。画图时有个经验不要把48个时段、33个节点全画在一个图上否则读者完全看不出规律。我一般只画MPS接入节点随时间的变化用阶梯图节点编号为纵轴时间为横轴再加上关键时段的负荷恢复功率柱状图。4. 算例复现结果与调参经验4.1 典型算例设定我的复现算例取IEEE 33节点系统12、18、25号节点布置了3台MPS容量都是1MW/4MWh。灾害后系统分裂为三个孤岛负荷权重按一级、二级、三级取10、3、1灾害持续24小时。DG在孤岛内的最大出力按原论文数据设置但我会做一组对照一组是MPS固定不移动另一组是允许MPS动态切换接入点。这个规模在Gurobi下考虑二阶锥潮流、迁移耦合、储能能量约束大约包含1500多个整数变量求解时间在10到30分钟之间limit 30分钟gap 2%以内。如果是线性DistFlow求解时间能压到5分钟以内。4.2 动态调度相对固定布置的效果差异在我跑的算例里动态调度比固定布置额外提升了大约12%到18%的关键负荷恢复量具体数值随DG出力和负荷权重变化。原因是MPS可以在孤岛负荷高峰到来前预迁移到另一个孤岛用同一台电源分时支撑多个区域。这个现象和论文里的结论方向一致动态调度的价值在于“错峰支援”。需要注意的是如果时间步长是1小时而MPS迁移时间恰好也是1小时那么迁移过程中该MPS彻底离线原本由它支撑的负荷必须切换到其他DG或者其他MPS。因此在优化结果里MPS不会频繁迁移。如果你看到代码结果中MPS每2小时就换一个节点多半是迁移惩罚系数没调好。4.3 三个关键参数的影响权重系数 ω_i 影响恢复顺序权重差异越大优化越倾向先恢复关键负荷甚至不惜牺牲大量三级负荷。迁移惩罚 λ 影响迁移次数过大MPS不动过小MPS乱动。时间步长影响模型的精度1小时步长通常会忽略掉半小时内负荷的快速变化导致恢复策略偏乐观。初始SOC的影响也很大。如果预配置阶段分配MPS时没有考虑初始SOC动态调度阶段可能出现第一时段就超出放电功率限制。建议把能量约束里的初始状态作为预配置阶段的一个输出参数传递过来而不是在下篇重新赋值。4.4 怎么用结果反推论文逻辑复现过程中我习惯把优化得到的MPS移动路径和论文里的路径图叠加对比。如果论文里MPS在某个时段移动而我的结果里没有移动通常不是求解错误而是权重或惩罚参数不一致。这时候优先检查负荷权重其次是可用线路集合。还有一种情况需要特别注意论文的算例里T是24小时但实际日志显示灾害影响主要集中在第6到第18小时。这时候直接把全时段放进去优化会稀释目标函数的差异导致MPS的迁移策略看起来“提前迁移”或者“延迟动作”。合理做法是设置一个恢复截止时间只在关键时段内允许迁移其他时段MPS只维持连接。5. 常见问题排查与避坑实录5.1 Infeasible problem八成是拓扑约束或迁移耦合写错了模型报infeasible的时候我的第一步是取消MPS的迁移约束允许它每个时段任意瞬移。如果这样能求解问题一定出在迁移耦合如果还是不可行再检查潮流约束和能量约束。99%的情况是约束逻辑冲突比如同一节点同时要求连接两台MPS或者MPS迁移时段内还被要求给节点供电。还有一种常见冲突来自“孤岛内至少有一个电源”约束。如果某个孤岛初始时只挂了一台MPS而该MPS在时段t迁移走了这段时间孤岛无电源如果还存在“所有已恢复节点必须带电”的约束模型自然会不可行。解决方法是允许该孤岛的负荷在MPS迁移期间降为0而不是强制保持供电。5.2 求解时间太长从三个方面压缩一是缩小时间窗口先跑12小时版本验证逻辑再扩到24小时。二是预剪枝候选接入点MPS不应该能接入所有33个节点实际可行的接入点也就十几个。三是放宽MIP gap到5%先看解的形态确认没有建模错误后再收紧到2%。如果Gurobi在30分钟内连一个可行解都找不到说明初始可行解质量太差。可以在求解选项里开启启发式options sdpsettings(options, gurobi.heuristics, 0.2);或者在模型里人工固定一部分MPS的初始位置作为热启动让Gurobi从这些可行解出发搜索。我在复现时发现固定第一时段的MPS位置能大幅减少首次可行解的搜索时间。5.3 二阶锥约束一直报“无法确定凸性”检查三类问题电压变量是否用了平方项Yalmip是否把变量写成矩阵后norm判断出错是否混用了和方向。把约束单独拿出来跑一个小例子只含一条支路、两个节点确定这个小例子能求解再放回大模型。5.4 初始位置和预配置结果联动的坑最容易被忽略的是预配置阶段的节点编号和动态调度阶段的节点编号必须完全一致。如果预配置阶段做的是改进后的33节点系统动态调度也必须使用同一套数据。不能因为换了负荷数据就导致MPS初始位置在节点18但动态调度的候选节点集合里根本没有18。5.5 环境配置问题很多人在复现的第一步就被环境卡住。Yalmip需要是较新版本Gurobi需要申请学术许可并在Matlab里配置路径。可以执行yalmiptest检查求解器是否被识别。如果显示Gurobi不可用多半是gurobi_setup没有运行或者Matlab当前路径没有包含Gurobi的Matlab接口文件夹。我个人在实际操作中最大的体会是这类动态调度复现数学模型本身并不难难的是把预配置和动态调度两个阶段的数据流打通。MPS初始位置、初始SOC、候选接入点集合这三样东西必须从预配置阶段的输出里显式地保存下来作为下篇的动态调度输入。如果你只照着公式重新建模而忽略了和前文代码接口的衔接后面所有结果都会对不上。最后再分享一个小技巧在做MIP求解前先固定所有二值变量求解一次连续松弛问题能得到目标函数的上界这个上界和整数解之间的差距直接反映模型松紧程度。如果你的连续松弛上界比整数解高了30%以上大概率是某个大M常数取得太大或者二级锥松弛过紧这时候不是急着调求解器而是回头检查松弛建模是否合理。