ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

配电网韧性提升:移动应急电源动态调度的MILP建模与实现

配电网韧性提升:移动应急电源动态调度的MILP建模与实现 上篇写预配置的时候我把应急移动电源MPS的选址和容量配置讲完了。那一步解决的是灾前决策在台风预警发布之后把有限的三五台移动电源摆在哪些位置才能让后续最坏情况下的恢复期望值最大。但真正动手做动态调度之后我才意识到预配置只是这道题的前半截——故障发生后的十几个小时里MPS去哪里、供多少功率、什么时候撤离转场才是整个韧性链条里最烧脑的部分。动态调度要回答的问题是具体的已知哪些支路断了、哪些负荷窝在孤岛里、抢修队大概什么时候把哪条线路修好站在当前时点上每一台MPS是继续蹲在原来接入点放电还是拔掉电缆转场去下一个失电区域到达新接入点之后功率怎么定什么时候动身才能避免在路上浪费掉最宝贵的“供电窗口”这些决策交织在一起构成了一个典型的时空耦合混合整数规划问题。这篇就把MPS动态调度的完整实现拆开讲数学模型怎么搭、Matlab里怎么用YalmipGurobi落代码、在33节点配网算例上跑出来的韧性曲线长什么样以及我复现过程中踩进去的四个坑。内容偏工程和代码适合正在复现配电网韧性、移动储能应急供电这类方向或者需要从论文公式走向可运行代码的人。这篇是下篇默认你已经看过上篇的预配置模型但如果你只对动态调度部分感兴趣也可以直接往下看。1. 动态调度到底在“调”什么先从预配置的衔接说起1.1 预配置与动态调度的分工预配置和动态调度在时间尺度和决策性质上是两类问题。预配置发生在灾前决策变量是“若干个候选站点放哪几台MPS、每台多大容量”它要处理的是未来故障场景的不确定性常见做法是随机规划、鲁棒优化或者两者的混合。动态调度发生在灾后决策窗口是故障发生到完全复电这段时间决策变量变成“每台MPS每个时刻在哪、给谁供电、供多少、什么时候转场”。这里有一个很多人忽略的点预配置决定的是MPS的“初始可用状态”而动态调度决定的是MPS的“全程可用状态”。一台MPS就算预配置位置再正确如果调度模型僵化、不会跟着修复进程转移它的价值也会打个大折扣。反过来如果调度模型能动态调整那么预配置即使稍微偏离最优最终的韧性指标也不会差太多。这有点像备货和配送的关系仓库建得好不好是一回事配送路径和时机才决定了货能不能真正到达用户手里。所以可以这样理解两个环节的分工上篇解决“从哪个起点出发”这篇解决“怎么走完整个旅程”。动态调度里的“动态”二字指的不是时间维度本身而是决策要对系统状态——故障修复进度、负荷变化、MPS余量——做出响应。修复进度一变MPS的走位就要跟着变这才是“动态”的核心。1.2 三类核心决策变量位置、功率、孤岛归属MPS动态调度的决策变量可以归成三类。第一类是位置状态变量用二进制变量 s(k,t,i) 表示第 k 台MPS在时段 t 是否接入候选节点 i。第二类是功率与能量变量包括每台MPS在接入节点的输出功率 P(k,t,i)、总放电功率 Pdis(k,t)、剩余电量 E(k,t)。第三类是移动动作变量 mv(k,t,i,j)表示第 k 台MPS在时段 t 是否“正在从 i 向 j 转移”。这个变量是动态调度区别于普通储能调度的关键也是模型复杂度上升的主要来源。实际建模型中位置和移动变量不是互相独立的。一个基本规则是任何时刻一台MPS要么停在某个接入点供电要么在路网某条弧上移动二者只能选其一。为了实现这一规则我引入了一个“位置唯一性”约束对每个时段 t、每台 MPS k所有接入状态和所有移动弧状态求和不超过1。这个约束是所有后续约束的地基——如果它不成立模型很容易解出“一边在路上跑、一边还在给老节点供电”的荒谬方案。孤岛归属变量 z(i,t) 也需要说明它表示失电区域节点 i 在时段 t 是否被纳入MPS供电范围。在很多论文里这个变量和配电网开关状态变量一起决定了哪些负荷被“圈进”孤岛。复现时我做了简化候选接入节点 i 与某个失电区域一一对应接入即意味着该区域被恢复供电。对研究动态调度的主线来说这个简化不会改变结论方向但能显著降低变量规模让模型在Matlab里跑得更快。1.3 为什么不能把动态调度当机组组合做第一次看这类问题的朋友容易产生一个疑问MPS动态调度跟传统的机组组合unit commitment或者多时段经济调度不是一回事吗都是“离散开停机/位置变量连续功率变量目标最大化”的组合。区别非常大。机组组合假设发电单元固定在线发出的电通过输电网全网消纳只要网络不越限功率想去哪就去哪。MPS不一样它必须通过道路网络迁移到特定的接入节点才能给那块区域供电。移动过程需要时间期间不能供电还在耗电接入之后也不是全网络消纳而是只能给局部孤岛供电。所以MPS调度是“交通网络上的离散移动”和“配电网上的连续潮流”耦合在一起的问题建模时必须显式引入时空网络。另一个差别在于动态调度的目标更偏向“恢复关键负荷”而不是“最小化发电成本”。配电网韧性场景下负荷有不同的重要度权重目标通常写成最大化加权恢复电量。权重差一个数量级调度结果就会完全不同现实中你会毫不犹豫地把最后一度电留给医院和通信基站模型里的权重就是干这个用的。2. 时空-能量耦合模型我最终采用的MILP表达2.1 目标函数加权恢复电量与移动惩罚我复现时采用的目标函数是最大化故障周期内的加权恢复用电量并叠加一个极小的移动惩罚项形式如下max Σ_t Σ_i w_i · P_rec(i,t) · Δt − γ · Σ_{k,t,i,j} mv(k,t,i,j)其中 w_i 是负荷区域 i 的重要度权重P_rec(i,t) 是 t 时段区域 i 的恢复功率Δt 是时段长度。γ 我取 1e-3 量级纯粹用于抑制数值上出现的“无意义空跑”——因为移动本身不会给目标函数带来直接收益不加这个惩罚项有时求出来的路径会有微小的无意义抖动。权重 w_i 建议作为一个可配置参数暴露出来。我在算例里把关键负荷区域比如医院、指挥中心设为1.0普通商业负荷0.5一般居民负荷0.1。这样模型在能量紧张时自然会先砍低权重负荷的恢复而不是平均分配。如果去掉权重改成总恢复电量最大解出来往往是“一团和气地均匀恢复”这在韧性场景下是不符合实际需求的。2.2 位置与移动约束时空网络上的MPS运动学位置与移动约束是这套模型的骨架。设 τ(i,j) 为从候选节点 i 到 j 的旅行时段数由道路距离除以平均行驶速度向上取整得到。约束包含三部分第一位置唯一性Σ_i s(k,t,i) Σ_{(i,j)} mv(k,t,i,j) ≤ 1对任意 k,t 成立。第二移动与接入的衔接。MPS 只有处在节点 i 的接入状态才能从 i 出发去 j到达 j 之后必须占用 j 的接入状态不能“悬空”。用代码表达就是% 从 i 出发前必须处于 i 的接入状态 Constraints [Constraints, mv(k,t,i,j) s(k,t,i)]; % 到达 jttau_ij时段后必须处于 j 的接入状态 if t tau(i,j) T Constraints [Constraints, mv(k,t,i,j) s(k,ttau(i,j),j)]; end第三旅程覆盖互斥。如果 MPS 在 t 时刻从 i 出发前往 j那么在 [t, tτ-1] 整个移动窗口内它都不能接入任何节点。这个约束是很多人漏掉的后面坑一会详细讲。如果候选节点少、时段粒度粗也可以把移动做进一步简化假设“一个时段内可以在任意候选节点间转移”此时 τ 全部等于1。这个简化在论文复现中很常见但会乐观估计 MPS 的机动性。我最终版保留了真实旅行时间用向上取整的时段整数量化简单且物理可解释。2.3 能量-功率耦合与负荷恢复约束能量约束主要是一条SOC递推式E(k,t1) E(k,t) − Pdis(k,t)·Δt − E_move(i,j)·mv(k,t,i,j)其中 E_move(i,j) 是移动一次消耗的能量我按“移动基础功率 × 旅行时间 单位里程电耗 × 道路距离”计算。注意移动能耗要和 mv 变量相乘只有真正发生了转移这部分的能量才从SOC里扣掉否则模型会凭空多出一份“白送”的移动能耗导致结果过度乐观或者无解。功率约束也比较直接MPS的输出功率只有在其接入状态下才允许非零且不能超过最大输出功率。这里需要警惕的就是 BigM 的取值后面会单独讲。负荷恢复约束是P_rec(i,t) 不能超过该区域 t 时段的负荷需求也不能在没有MPS接入时非零。如果有MPS接入则该区域的恢复功率等于各MPS在该节点注入功率之和简化模型中忽略网损和电压约束。复现版本里我用的是“区域功率平衡容量校核”的简化潮流没有逐节点迭代Distflow。原因很简单动态调度重点在于MPS的时空决策潮流细节虽然影响最终电压但它会让模型变成更复杂的非线性/高阶约束求解难度指数上升。论文里如果严格做了Distflow线性化效果会更好如果你想扩展可以把Distflow加入约束组模型的骨架不需要改。3. 从公式到MatlabYalmip建模的关键实现3.1 工程化代码组织Matlab 上做这种MILP建模最优选择就是 Yalmip 统一建模 Gurobi/CPLEX 求解器。Yalmip 的语法直观尤其擅长处理二进制变量和稀疏约束。我建议不要把代码全部塞在一个 main 里而是按下面这个结构组织后续调试会轻松很多main_mps_dispatch.m ├── data │ ├── case33bw.m % 33节点配电系统数据 │ └── fault_scenario.m % 故障支路、修复时序、负荷权重 ├── lib │ ├── build_spacetime.m % 生成候选节点、距离矩阵、旅行时间 │ ├── build_yalmip_model.m % 整个MILP模型 │ ├── solve_and_postprocess.m │ └── rolling_horizon.m % 滚动时域闭环调度 └── plot ├── plot_trajectory.m % MPS轨迹/甘特图 └── plot_recovery_curve.m % 恢复率曲线这个结构的好处是每个文件职责单一数据文件单独放是方便你换算例系统比如换成IEEE123节点时不用动模型代码模型函数单独放是方便你在不换数据的情况下调试算法比如把开环换成滚动时域。3.2 变量定义与约束写入在 Yalmip 里定义变量时我建议把维度一次性定义清楚并用有意义的缩写注释。以下是我代码里的核心变量定义s binvar(nMPS, T, nCand, full); % s(k,t,i): k在第t时段是否接入节点i mv binvar(nMPS, T, nCand, nCand, full); % mv(k,t,i,j): 转移动作/移动弧 P sdpvar(nMPS, T, nCand, full); % P(k,t,i): 接入节点i时的注入功率 Pdis sdpvar(nMPS, T, full); % 总放电功率 E sdpvar(nMPS, T1, full); % SOC曲线 P_rec sdpvar(nCand, T, full); % 各区域恢复功率约束写入尽量用向量化的方式。比如位置唯一性如果一层层循环逐个约束写Yalmip 解析很慢直接写成Constraints [Constraints, sum(s,3) sum(sum(mv,3),4) ones(nMPS,T)];一行搞定。这里 sum(s,3) 把候选节点维求和得到 nMPS×T 的矩阵sum(sum(mv,3),4) 把所有移动弧求和得到 nMPS×T和矩阵每个元素对应一台MPS一个时段的全部状态。这种写法在变量规模增大后优势非常明显。目标函数和求解部分也是几行的事情obj -sum(w .* P_rec, all) * dt 1e-3 * sum(mv, all); ops sdpsettings(solver, gurobi, verbose, 2, gurobi.MIPGap, 0.01); sol optimize(Constraints, obj, ops);如果没装 Gurobi可以临时把 solver 换成 intlinprog 之类的内置求解器但求解速度会明显下降建议正式跑数还是用商用求解器。3.3 求解配置与滚动时域扩展求解完成后先检查 sol.problem 是否为0再通过 value() 取回变量if sol.problem 0 s_val value(s); mv_val value(mv); P_val value(P); P_rec_val value(P_rec); E_val value(E); else [primalres, ~, ~, ~] check(Constraints); % 查看最大约束违反量定位无解原因 end滚动时域调度的扩展在工程上非常有意义。思路是并不一次性解完24小时而是每个决策周期只看未来 H 个时段的窗口求解后只执行第一个时段的决策然后滚动推进。Matlab 里用 for 循环嵌套即可核心是把当前时段的初始状态位置、SOC硬约束进模型同时把窗口外的变量去掉或固定。我在 lib/rolling_horizon.m 里实现了这个逻辑后面算例会给出 H 取不同值时的性能差异。这个扩展之所以重要是因为现实中故障修复时间高度不确定。开环优化假设抢修队第6小时修好支路实际上可能第4小时就通了也可能拖到第9小时。一旦实际情况偏离预测开环方案就局部失灵滚动时域每次根据最新状态重新优化才能把不确定性“滚掉”。4. 33节点算例实测韧性曲线是怎么被拉回来的4.1 算例参数与故障场景我用的是 IEEE 33 节点配电系统基准电压12.66kV系统参数直接取标准数据。MPS 设置为3台每台容量300kWh最大输出功率100kW平均移动速度20km/h移动基础功率15kW移动每小时耗能15kWh单位里程电耗0.6kWh/km。时段分辨率取1h仿真周期24h。故障场景设定为主馈线发生三处断线支路5-6、9-10、14-15 同时故障形成三个失电区域。修复时序设置为14-15支路在第6小时修复、9-10支路在第12小时修复、5-6支路在第18小时修复。这就意味着区域C在第6小时后恢复主网供电区域B在第12小时后恢复区域A最晚恢复。候选接入节点我取了三个区域的主干节点节点6对应区域A节点10对应区域B节点14对应区域C。权重分别设为0.5、1.0、0.3其中区域B是关键负荷区。节点之间的道路距离我按 5~12km 随机生成并固定下来旅行时间用距离除以速度向上取整得到。下表是不同策略的对比结果数据是我在这个简化算例下跑出来的示例结果趋势比绝对数值更有参考价值。方案加权恢复率关键负荷恢复率累计恢复能量(MWh)无MPS0%0%0静态部署预配置后MPS不动61.3%77.8%28.4动态调度开环全局优化82.4%94.1%38.7动态调度滚动时域 H678.9%90.5%36.24.2 结果曲线怎么读轨迹图、恢复率、SOC跑完之后最直观的输出是MPS轨迹图我习惯画成类似甘特图的形式横轴是时间纵轴是三台MPS每一段用不同颜色表示位于哪个接入节点灰色段表示在移动。从图上能一眼看出调度方案的“走位”是否合理。我跑出来的开环结果里三台MPS的走位很有代表性MPS1 先蹲在区域B支援关键负荷等到区域B在12小时恢复主网后它没有继续待着而是转场去了还没恢复的区域AMPS2 从区域C出发在区域C修复后立刻转移到区域A接力MPS3 则一直在区域A通过节点6供电。三台车形成了交错接力的节奏而不是各自蹲守原区域。这就是“动态”二字的直接体现。对比静态部署方案动态调度多出来的收益主要来自两处一是区域修复后及时撤出MPS把能量留给仍然失电的区域二是通过提前预估旅行时间避免出现“MPS到新区域时没电了”的尴尬局面。在结果图里你可以看到静态部署区域B的关键负荷在12小时后依然有功率供给但此时其实已经不需要了而区域A的负荷却因为能量不足只能降级恢复。4.3 数量、容量、窗口长度的敏感性做 SCI 复现时光跑一组结果是不够的审稿人肯定会追问参数的敏感性。我快速跑了几组敏感1MPS数量。1台MPS时加权恢复率只有大约 47%因为一台车只能蹲一个区域而且移动到另一个区域时原区域就彻底没人管2台时到 68%3台到 82%。数量从2到3收益仍然不小但这个收益受候选节点数限制——当每个区域都有车占住后再加车就只能挤在同一个节点收益骤降。敏感2MPS容量。把单台容量从300kWh提到500kWh恢复率提升并不明显大约 84%。原因是容量在算例里已经超过了单区域剩余时段的负荷需求瓶颈变成了MPS数量和移动时间。反过来容量降到100kWh恢复率掉到 55% 左右因为车还没到下一个区域就接近没电机动性被能量硬卡住。敏感3滚动时域窗口 H。H2时恢复率掉到 70% 左右而且轨迹图显示MPS频繁转场典型的“只见树木不见森林”H6恢复到79%H8以上基本接近开环水平。实际工程中H取“最长旅行时间2”以上比较稳妥既能顾及中期态势又不至于让每轮优化太重。5. 复现中我踩过的四个坑从无解到错误解5.1 坑一移动与供电互斥没锁住我第一版模型跑出来的解非常离谱某台MPS明明在移动弧上已经“出发”了同一时段它原来的接入节点还在往外输出功率。问题出在互斥约束只约束了出发时刻没有约束移动过程中的所有时段。具体说我一开始写的出发衔接是 mv(k,t,i,j) s(k,t,i)这只能保证“出发那一刻它确实在 i”但移动过程跨多个时段中间这些时段 s 变量是自由的模型当然会搞出“人在路上电还在供”的方案。解决方式是在移动窗口内的每个时段都加互斥约束从 t 出发、旅行时间为 τ则对 t ∈ [t, tτ-1]都有 Σ_i s(k,t,i) ≤ 1 − mv(k,t,i,j)。写成向量化形式也不难。这个坑给的经验是涉及移动/转移类模型中状态互斥必须作用在整个转移区间而不是只作用在端点。5.2 坑二移动能耗没进SOC递推第二个坑更隐蔽。第一版我为了快速验证模型在SOC递推里只写了放电项没写移动能耗项。结果三台MPS从区域A转到区域C再到区域B行程几十公里SOC曲线纹丝不动——这等于MPS是“白嫖”的移动不用消耗能量。看起来目标函数值很好但物理上完全站不住脚。把移动能耗项加进递推之后模型反而先报了一波无解。一开始我以为建模错了检查后才发现是好几条转场路线的距离太远、移动能耗加起来太大而MPS的初始SOC又相对有限导致某些看似“很灵活”的走位根本不可行。这正是真实系统里的硬约束。这个经历说明一个问题模型越贴近物理解的数量越少但剩下的解才真正可信。复现论文时如果发现结果好得过分优先检查是不是有物理约束被忽略了。5.3 坑三BigM取值太松复现时有一版求解非常慢Gurobi跑了快一小时MIPgap才降到20%。最后定位到原因是BigM系数写得太大写“P ≤ BigM * s”我当时顺手填了1e5。线性规划松弛之后MPS在不接入节点时也能把P撑到很大整个下界松得没法看分支定界效率极低。正确的做法是把BigM取到物理上最紧的上界比如最大输出功率100kW或者再乘个1.1的安全系数。改成这种紧BigM之后同一模型几分钟就收到1%的MIPgap。这条经验对所有MILP建模都适用BigM不是随便取个“够大”的数就行取多大会直接影响求解器的收敛曲线。5.4 坑四时序索引差一个时段还有一个非常容易犯的低级错误时序索引偏移。旅行时间为2个时段从 t 出发到达时刻应该是 t2但我在一条约束里写成了 t1导致MPS到达后“多赚”了一个时段的供电时间。这种错误在最终结果里不太容易被肉眼看出来但如果你把轨迹图和区域复电时刻对齐会发现MPS在某些区域的接入时间比理论上多了整整1小时。定位这类问题的办法是写一个小规模人工可验证的用例一台MPS、两个候选节点、固定旅行时间手动推一遍期望的轨迹再跑模型对比。把这套冒烟测试放在代码目录里以后改模型时跑一遍就能快速排除低级错误。6. 给复现者的几点建议如果你想把这个模型复现到自己数据集上我的建议是先跑通小规模冒烟测试再上大规模算例。我调试时先用一个6节点简化系统、12个时段、1台MPS模型只有几十个变量几秒出解。确认位置唯一性、移动衔接、SOC递推、目标函数四块逻辑都对之后再切换到33节点系统、3台MPS、24时段这时候绝大多数报错都是规模问题而不是逻辑问题。模型参数建议全部做成可配置项——权重、惩罚系数、BigM、旅行时间、修复时序都要能从配置文件读进来。一区论文里的参数设置往往直接影响结论复现时不要硬编码否则后面做敏感性分析时你会想砸电脑。如果后续想从“复现”走向“工程落地”滚动时域闭环是必须做的一步。开环优化适合离线的性能上限评估现场调度面对的是不断变化的修复进度和负荷曲线只有滚动修正才能让MPS的计划真正跟得上现实。代码上可以把滚动窗口 H 设为可配置跑一遍 H 的敏感性你就能直观看到“短视”和“远见”之间的差距。我在复现这套动态调度时最深的体会是MPS的机动性是珍贵的但也是有代价的——移动期间不能供电还要消耗自身能量。一个调度方案真正的难点不是“去哪”而是“什么时候舍得放弃眼前能供的负荷转而去救远处更重要的负荷”。把这个权衡完完整整建进模型、让优化器替你做取舍韧性曲线自然会被拉起来。
返回列表