这两年,配电网韧性和移动储能成了不少项目组反复讨论的关键词。台风、冰灾这类极端事件一旦发生,配电网最容易出现长时间停电,传统抢修手段很难在几小时内恢复大面积供电。这时候,固定储能站只能照顾到本站附近的负荷,移动储能车则可以灵活换位,把宝贵的电能送到最需要的负荷节点。我最近完整复现了一套面向配电网韧性提升的移动储能预布局与动态调度策略,算例采用 IEEE33 节点系统,优化模型和求解流程全部用 Matlab 代码实现。这篇文章就把这套策略从问题建模、求解框架到代码落地、算例分析的全过程拆开讲,方便正在做配电网韧性、微网应急供电、移动储能优化配置方向的同学快速上手。如果你是想在工程预演中快速搭一个可复用的计算框架,或者正在为“储能车停在哪儿、灾后往哪儿跑”这类问题发愁,这篇文章应该能帮你少走不少弯路。我不会只贴代码,更多会讲清楚几个关键判断:为什么用移动储能而不是固定储能,预布局和动态调度怎么统一建模,C&CG 求解框架在 Matlab 里怎么落地,以及我在复现过程中踩过的几个有代表性的坑。1. 项目背景:移动储能如何服务配电网韧性提升1.1 韧性提升到底提升什么先说清楚“韧性”和常规“可靠性”的区别。可靠性通常关注故障发生的频率和平均停电时间,比如一年用户平均停电多少小时,指标是 SAIDI、SAIFI 这一类。而韧性更关注极端事件发生后的“损失规模”和“恢复速度”,也就是说,当系统遭遇比设计准则更严酷的故障时,能不能让关键负荷坚持住,能不能快速恢复,这几个维度才是韧性研究的重心。极端灾害和普通故障不一样,有可能同时造成多条线路停运,形成多个孤岛。常规的开关重构只能解决部分联络问题,在分布式电源容量不足时,负荷依然切掉。这时候,移动储能就派上用场了。它可以像一个“可移动的备用电源”,在灾前预置到风险较高的区域,灾后根据实时网络故障情况再“跑”到最缺电的位置,为关键负荷提供短时支撑。韧性提升的落脚点,最终要落到量化指标上。最朴素也最常用的一种做法,是用“系统在故障期间实际获得的电量”和“所有负荷正常用电需求电量”的比值来衡量。比值越接近 1,说明极端事件下的供电保障做得越好。后面建立优化模型时,我们就会围绕这个思路设计目标函数和约束条件。1.2 移动储能相对固定储能的核心优势固定储能和移动储能看起来都是在合适的位置放电,但本质上差别很大。固定储能选址一旦确定就不可更改,如果故障点距离储能接入点太远,或者线路被切断,储能再好也送不出去。移动储能可以在预布局阶段选择一个相对合理的落点,故障动态阶段再通过道路系统把储能车调度到真正需要的位置,相当于把“选址灵活性”和“运行灵活性”结合了起来。从投资利用率角度看,固定储能很难兼顾多个位置的应急需求,相当于每个站点都要配置一份备用容量,成本很高;移动储能车可以按风险变化在多个节点之间轮换,一辆车就能覆盖更大范围的应急需求。当然,移动储能的劣势也很明显:车辆在移动期间无法供电,到达新位置也需要时间,因此“预布局”和“动态调度”必须放在同一个优化框架里考虑,否则很容易出现储能车赶到了但负荷已经停了太久的情况。下表是我在复现时用来对比两者特征的简化表,可以直观看到差异。对比项固定储能移动储能接入位置一次性选址灾前预置,灾后可迁移故障适应性依赖固定线路连通性可根据故障动态调整利用率低峰时段容易闲置多场景复用,利用率更高移动过程供电不存在移动期间不可供电,需建模投资与运维单点重复配置单台覆盖范围更大选择移动储能并不意味着固定储能没有价值,两者可以互补。只是当我们特别关注极端事件后的韧性恢复时,移动储能的机动性往往比增加固定容量更划算,这也是我选择复现这类策略的原因。1.3 为什么选 IEEE33 节点系统作为复现对象IEEE33 节点系统是一个很经典的配电网算例,基准电压 12.66kV,包含 1 个根节点、32 条支路和若干联络开关,节点数不多不少,非常适合研究馈线故障、负荷转供、分布式电源接入等问题。相比动辄上百节点的实际配电网,IEEE33 节点既能体现潮流的空间特性,又不会让优化模型在规模上失控,用来验证移动储能预布局与调度策略再合适不过。另外,IEEE33 系统的网络结构公开、数据统一,不同文献、不同团队做出来的结果有可比性,这给“复现”提供了很大的便利。我在实现时并不需要从零构建复杂的拓扑数据,只需要把标准支路参数、节点负荷数据翻译成 Matlab 可读的结构体,再统一处理成优化模型需要的矩阵形式。对于初学者来说,从这个算例入手最容易建立“模型-代码-结果”的完整闭环。2. 问题建模:预布局与动态调度的数学本质2.1 极端事件与场景集合怎么构造移动储能的预布局动作发生在灾害来临之前,但故障到底发生在哪几条线路上、断开几条支路,事前并不知道。为了在数学上刻画这种不确定性,我采用多场景鲁棒优化的思路:预先构造一组极端故障场景,每个场景对应一组线路同时断开的组合。场景集合的构造既要覆盖可能出现的最坏情况,又不能规模爆炸。最直接的方法是先识别系统中的脆弱线路,通常是对负荷影响大、处于关键供电通道上的支路;然后在脆弱线路里按“同时故障线路数上限”做抽样,形成候选故障线路集合。比如设置最多同时断开 3 条支路,就从候选支路中随机组合出 20 个场景。这个方式简单有效,也方便后续调节鲁棒保守程度。还有一个细节值得注意:故障场景并不是对称的。不同场景下,同一储能车的初始位置相同,但后续移动路径、接入节点可能完全不同,所以预布局变量必须独立于场景,而动态调度变量要随场景变化。这也是两阶段优化的基本结构:第一阶段做不依赖于具体故障场景的部署决策,第二阶段针对每个具体场景再优化。2.2 目标函数与韧性评估指标优化目标的核心是“让极端场景下的负荷削减量尽量小”。可以直接把多个场景的失负荷量加权求和作为目标,权重可以取场景概率,也可以取为 1,代表等概率最恶劣情况。为了更贴近韧性评价,我会额外加入一个所有场景统一计算损失函数的形式:min Σ_s w_s · Σ_t Σ_i P_cut(s,i,t)其中 s 是场景,t 是时间段,i 是节点,P_cut 是切负荷量,w_s 是场景权重。如果要考虑移动储能车在不同位置的调度成本,也可以加入“移动启用费用”和“充放电损耗”,但要注意别让权重失衡。否则优化结果会为了省一点移动成本而牺牲大量负荷恢复,这不是我们想要的。为了最后直观评估韧性,我在代码里另外算一个指标 RI:RI = 1 - ( Σ_s Σ_t Σ_i P_cut(s,i,t) ) / ( Σ_s Σ_t Σ_i P_load(s,i,t) )RI 越接近 1,代表极端事件下恢复水平越高。这个指标不参与优化,只用于后处理和不同方案对比。实践上,如果 RI 从 0.45 提升到 0.78,对关键负荷的保障效果差异是非常明显的。2.3 核心约束:潮流、储能、移动与网络辐射状约束部分是这个模型的真正难点,我把它拆成四类写。第一类是潮流与电压约束。在配电网里,我采用支路潮流模型,把支路功率、节点电压、电流都写成变量。由于原始的非线性潮流方程求解困难,通常采用二阶锥松弛,把电压平方和电流平方设为新变量,原问题就变成可以被商业求解器直接处理的凸优化子问题。用公式简写就是每条支路要满足首末端功率平衡、电压降落方程和容量限制,再加节点注入功率平衡约束。松弛是否严格,需要在求解后检查松弛间隙,这一点我在后面避坑章节还会专门说。第二类是储能运行约束。每台移动储能车都有荷电状态(SOC)动态方程:SOC(v,t) = SOC(v,t-1) + η_ch·P_ch(v,t) - (1/η_dis)·P_dis(v,t)同时,放电功率不能超过额定上限,SOC 也要保持在上下限之间,比如 0.2 到 0.9。此外,同一时刻不能同时充电和放电,这个约束看起来简单,但写成线性表达式时需要引入一个二进制变量,比如用 z 表示充电状态,用 1-z 表示放电状态。第三类约束是移动储能的“位置-时间”耦合约束。这是移动储能和普通储能的本质区别。每台储能车在每个时间段必须恰好位于一个节点,位置变量是 0-1 变量;如果从节点 i 移动到节点 j 需要耗费 delta 个时段,那么在移动到达 j 之前,该车辆不能在其他任何节点放电。这部分约束是最容易写错的地方,我会在后面的代码和避坑章节重点讲。第四类约束是网络辐射状约束。配电网在正常运行和故障隔离后,线路需要保证连通且无环。在多场景模型中,每个场景的拓扑是确定的,但可以操作联络开关进行网络重构。为了保持可行性,需要加入保证辐射状结构的约束,常见做法是用单商品流或独立节点生成树约束。在 IEEE33 节点这个小系统上,用生成树约束可以接受,但大规模算例就要留意计算量。3. 求解框架与 Matlab 代码实现3.1 两阶段鲁棒优化的整体结构我们面对的是一个“先部署、后响应”的决策过程,自然对应两阶段优化模型。第一阶段变量是移动储能车的预布局位置和初始 SOC;第二阶段变量包括每个场景下的储能车实时位置、充放电功率、网络开关状态以及切负荷量。从数学形式上看,这是一个 min-max-min 结构:min_y { C_pre(y) + max_{s∈S} min_{z∈F(s)} C_ope(z, y) }也就是,在第一阶段决策 y 时,第二阶段要考虑最不利的场景 s 下,最优运行决策 z 带来的运行损失。如果只用期望场景优化,得到的结果往往低估不确定性;如果针对每个单场景都强制满足,又可能过于保守。鲁棒优化就是希望在两者之间寻找一个平衡点。在 Matlab 中我用 YALMIP 做建模平台,外部调用 Gurobi 或 CPLEX 求解混合整数规划。YALMIP 的最大好处是可以用符号逻辑构建约束,比如 binvar、sdpvar,代码可读性很好,适合复现和二次修改。如果你更习惯直接用求解器的 API 建模,也可以,只是变量矩阵比较复杂时维护成本会高不少。3.2 C&CG 求解流程及伪代码两阶段鲁棒优化最常用的精确算法是列与约束生成(C&CG)。它的思路是交替求解主问题和子问题:主问题先假设只有一个或几个已知场景,得到预布局方案;子问题根据当前预布局方案,找出让运行损失最大的最恶劣场景;如果存在更恶劣的场景,就把这个场景相关的变量和约束补充到主问题里,继续迭代,直到上下界收敛。我给一个简化的 C&CG 伪代码,方便理解整体流程:初始化:给定初始场景 s0,设置 LB = -inf,UB = +inf,k = 0。 while (UB - LB) > ε: 1. 求解主问题,得到预布局 y_k 和目标值 obj_master。 更新 LB = max(LB, obj_master)。 2. 固定 y_k,对所有可能场景 s 求解子问题,得到运行成本 obj_sub。 找到使 obj_sub 最大的场景 s*,更新 UB = min(UB, C_pre + obj_sub)。 3. 将场景 s* 对应的第二阶段变量和约束加入主问题。 4. k = k + 1。 end while在实际复现时,子问题不一定非得遍历所有场景。我们可以先用一个场景池做快速搜索,再在一些极端故障组合中找更恶劣场景,能显著降低迭代次数。对于 IEEE33 节点这种规模,遍历几十个场景其实还好,但如果把系统放大到几百个节点,就要考虑用启发式方法加速场景识别。3.3 Matlab 代码结构与关键代码片段我的代码仓库目录大致是这样的:|-- main.m # 主程序,设置参数并启动求解 |-- init_IEEE33.m # 初始化IEEE33节点网络数据 |-- generate_scenarios.m # 生成极端故障场景 |-- build_storage_model.m # 构建移动储能数学模型 |-- solve_master.m # C&CG主问题求解 |-- solve_sub.m # C&CG子问题求解 |-- plot_results.m # 结果可视化其中build_storage_model.m是最核心的部分,我贴一段位置变量的关键代码:%% 移动储能车位置变量与约束 % x(v, i, t) = 1 表示第v辆车在t时段位于节点i x = binvar(nVehicle, nNode, nTime, 'full'); cons_x = []; % 每辆车每个时段只能在一个位置 for v = 1:nVehicle for t = 1:nTime cons_x = [cons_x, sum(x(v, :, t)) == 1]; end end % 移动过程约束:从i到j至少需要delta个时段 % y_move(v,i,j,t)=1 表示第v辆车在t时段开始从i移动到j y_move = binvar(nVehicle, nNode, nNode, nTime, 'full'); for v = 1:nVehicle for i = 1:nNode for j = 1:nNode if i ~= j for t = 1:nTime-delta(i,j)+1 % 如果开始移动,则起点在t时刻占位,终点在t+delta到达 cons_x = [cons_x, ... x(v,i,t) + y_move(v,i,j,t) <= 1, ... x(v,j,t+delta(i,j)) >= y_move(v,i,j,t)]; end end end end end这段代码里用了两个变量来表示位置和移动事件,配合起来就能保证“移动期间不能供电”和“到达后下一时刻可接入”的逻辑。实际建模时还要加一些冗余约束,比如一个车辆不能同时开启两个方向的移动,否则模型虽然可行,但求解速度会变慢。4. 算例分析:预布局节点怎么选、储能车怎么跑4.1 参数设置与场景数据为了能让结果可复现,我在init_IEEE33.m里使用了标准的 IEEE33 节点拓扑,基准功率 1MVA,基准电压 12.66kV。移动储能参数假设如下表所示。参数名称数值移动储能车数量2 辆单台储能容量1 MWh最大充/放电功率0.5 MW初始 SOC 范围0.2 - 0.9充/放电效率0.95 / 0.95时间分辨率1 小时调度总时段8 小时场景数量6 个极端场景最大同时断线数3 条场景选取上,我以支路 3-4、5-6、8-9、13-14、26-27 这些负荷较重或位于馈线末端的关键支路为主要候选,随机组合出 6 个典型极端场景。在这个设置下,预布局阶段解出来的运行成本、切负荷量以及韧性指标都符合物理直觉。4.2 预布局结果解读在本算例的参数下,优化得到的预布局方案是:第一辆车停在节点 18,第二辆车停在节点 25。单看拓扑,节点 18 和节点 25 都位于网络末端区域,但周围负荷密度并不低。为什么不是停在根节点附近?因为如果所有储能车都停在靠近变电站的位置,故障一旦断开上游路线,末端负荷依然孤立无援;如果停在太偏远的末端,又可能因为道路距离和移动时间太长,无法在窗口期内支援其他区域。优化模型自动折中了这两点。另外,预布局结果还与负荷时序有关。如果某个时段末端负荷特别高,储能车会倾向于靠近该区域。我试过把模拟时间设为 24 小时,并加入峰谷负荷曲线,预布局位置会在馈线中段和末端之间来回选择,说明这种策略对负荷时间特性的敏感度很高。做工程应用时,至少要基于当地的典型日负荷曲线来定预布局方案,不能只看平均值。4.3 动态调度过程与恢复效果以其中一个故障场景为例:假设在 t=1 时,支路 8-9 和 25-29 同时断开,导致节点 18 所在的支路末端以及节点 25 周边进入孤岛状态。优化后的动态调度结果大概是:t=1:储能车1在节点18放电,储能车2在节点25放电; t=2:储能车2感知到节点25下游负荷较低,开始向节点29移动; t=3:储能车2到达节点29,接入并恢复该区域关键负荷; t=4:储能车1继续在节点18放电,同时等待网络重构结果; t=5:联络开关闭合,部分负荷转供,储能车1陆续降低出力;可以看到,储能车并不是从一开始就满功率放电,而是结合网络重构的时序动态调整。早期孤岛内负荷需求量很大,储能车高功率输出;随着上游开关恢复供电,储能车会降低出力次数,保留一部分 SOC 给可能出现的二次故障。这就是“动态调度”和“静态应急电源”最大的区别。4.4 与无储能、固定储能的对比结果为了更清楚地体现移动储能的优势,我设置了三个对比方案:无储能、固定储能(两套固定储能分别在节点 18 和 25)、移动储能(预布局加动态调度)。在相同故障场景和负荷时序下,结果如下表。方案总切负荷量 (MWh)韧性指标 RI无储能10.850.42固定储能6.720.64移动储能3.960.78固定储能能解决一部分末端孤岛供电,但因为位置固定,遇到故障点刚好阻断储能接入线路时,就无能为力了。移动储能通过灾后移动到孤岛内部,能够显著减少切负荷量。从算例看,RI 从 0.64 提升到 0.78,对应总切负荷量下降了接近一半。这个结果也说明,仅靠增配储能容量不一定是最优解,把储能车部署到“对的时间”和“对的位置”,效果往往比单纯多买电池更明显。5. 避坑指南与扩展建议5.1 求解速度慢怎么办IEEE33 节点加上两个储能车、六个场景,规模不大,但如果把所有时段和所有节点的变量一次性铺开,混合整数规划还是会变得很臃肿。第一次跑的时候,我遇到主问题求解时间超过十分钟的情况,后来通过三个手段解决。第一,给初始解。先用启发式方法生成一个可行的预布局方案,作为主问题的 MIP start。比如可以按“离脆弱线路近”的规则预先指定储能车停靠节点,再让优化器在这个基准上调整,能省去大量分支定界时间。第二,加对称破除约束。两辆车参数相同时,预布局结果会存在大量对称解,变量置换后目标一样。加入“第一辆车位置编号小于第二辆车位置编号”之类的约束,可以砍掉近一半搜索空间。第三,在子问题里先求解线性松弛版本,用目标值来判断该场景是否是潜在最恶劣场景。如果某个松弛场景的目标值排名靠后,大概率不会成为真正的恶劣场景,可以先不加入主问题,减少主问题负担。5.2 DistFlow 二阶锥松弛细节在 YALMIP 里写 DistFlow 二阶锥约束时,要注意变量替换后的松弛形式。简单来说,对每条支路都需要满足:V_j <= V_i - 2(R·P + X·Q) + (R^2 + X^2)·I_ij其中 R、X 是支路电阻和电抗,V 和 I 是平方形式的变量。这个约束直接写在 YALMIP 里就是锥约束。常用写法是:cons = [cons, V(j) - V(i) + 2*(R*P_ij + X*Q_ij) - (R^2+X^2)*I_ij <= 0];松弛之后还需要检查误差。我一般会在结果更新后计算每条支路的松弛间隙:gap_ij = |P_ij^2 + Q_ij^2 - V_i * I_ij|如果某个支路的间隙远远大于 1e-4,说明这个支路很可能发生了电压越限或潮流不符合物理规律,需要检查负荷参数和网络数据。多数情况下,IEEE33 节点算例只要电压约束、容量约束不过于紧张,松弛误差都很小。5.3 移动时间建模的常见错误移动时间是整个模型里最容易出错的地方。常见一个错误是:只把“移动终点节点”的接入时间约束了,却没有限制“移动起点节点”在移动期间不能供电。结果就是储能车一边移动一边还在给原来的节点放电,模型看起来可行,实际上却是物理悖论。正确的做法是同时约束起点和终点的位置状态。我建议把移动过程拆分为两个时间点:开始移动的时刻 t 和到达目标的时刻 t+delta。在 t 到 t+delta-1 之间,该车辆不能出现在任何节点,可以通过“每辆车每时段必须位于一个节点”的约束结合移动状态实现。如果时间步长比较粗(比如1小时),可以假设车辆在整小时段内处于“不可用”状态,到达后的下一个小时才能接入。这种近似对结果影响不大,但模型复杂性会小很多。还有一个容易忽略的问题:移动时间和道路距离不能直接用欧氏距离计算。IEEE33 节点只是电网拓扑,不代表车辆行驶路径。我在代码里用了一个距离矩阵,将节点之间道路距离折算成小时,数值要设置合理,否则预布局决策会过度乐观。5.4 鲁棒保守程度如何调节鲁棒优化的保守度由场景集合和故障组合上限共同决定。如果你把所有可能故障组合都纳入最恶劣场景,求解结果会非常保守,储能车几乎要覆盖所有末端节点,这在工程上并不现实。反之,如果只选个别场景,决策又会过于激进。我在复现时增加了一个参数 Gamma,表示“同时断线数量上限”。Gamma 越大,考虑的最恶劣情况越严重,系统韧性指标通常会提升,但需要的储能容量和调度成本也会增加。实际使用时,可以基于历史极端事件统计来设定 Gamma,比如过去五年最大同时断线数是 4,就设为 4,不要凭感觉加大。另外,还可以把多个场景赋予不同概率权重,然后用条件风险价值指标替代期望值,这样能兼顾极端小概率事件和平均表现。这个扩展用 C&CG 框架也能实现,只是子问题的目标函数要调整一下,代码改动并不大。最后再分享一点个人的实际操作体会:复现这套策略,最难的不是储能本身的充放电方程,而是“位置”和“时间”这两个维度交叉产生的约束。建议你先把移动约束单独抽出来做一个最小算例,比如 5 节点 2 时段,验证位置变量和移动变量的逻辑能跑通,再把它放回 IEEE33 节点系统里做完整算例。另外,预布局结果对场景集和负荷时序非常敏感,不要指望一套参数在所有系统上都成立,真正做项目时一定要用当地线路数据和负荷曲线重新校准。这样复现出来的代码,才不是一纸空文,而是可以支撑工程判断的“活”工具。
这两年,配电网韧性和移动储能成了不少项目组反复讨论的关键词。台风、冰灾这类极端事件一旦发生,配电网最容易出现长时间停电,传统抢修手段很难在几小时内恢复大面积供电。这时候,固定储能站只能照顾到本站附近的负荷,移动储能车则可以灵活换位,把宝贵的电能送到最需要的负荷节点。我最近完整复现了一套面向配电网韧性提升的移动储能预布局与动态调度策略,算例采用 IEEE33 节点系统,优化模型和求解流程全部用 Matlab 代码实现。这篇文章就把这套策略从问题建模、求解框架到代码落地、算例分析的全过程拆开讲,方便正在做配电网韧性、微网应急供电、移动储能优化配置方向的同学快速上手。如果你是想在工程预演中快速搭一个可复用的计算框架,或者正在为“储能车停在哪儿、灾后往哪儿跑”这类问题发愁,这篇文章应该能帮你少走不少弯路。我不会只贴代码,更多会讲清楚几个关键判断:为什么用移动储能而不是固定储能,预布局和动态调度怎么统一建模,C&CG 求解框架在 Matlab 里怎么落地,以及我在复现过程中踩过的几个有代表性的坑。1. 项目背景:移动储能如何服务配电网韧性提升1.1 韧性提升到底提升什么先说清楚“韧性”和常规“可靠性”的区别。可靠性通常关注故障发生的频率和平均停电时间,比如一年用户平均停电多少小时,指标是 SAIDI、SAIFI 这一类。而韧性更关注极端事件发生后的“损失规模”和“恢复速度”,也就是说,当系统遭遇比设计准则更严酷的故障时,能不能让关键负荷坚持住,能不能快速恢复,这几个维度才是韧性研究的重心。极端灾害和普通故障不一样,有可能同时造成多条线路停运,形成多个孤岛。常规的开关重构只能解决部分联络问题,在分布式电源容量不足时,负荷依然切掉。这时候,移动储能就派上用场了。它可以像一个“可移动的备用电源”,在灾前预置到风险较高的区域,灾后根据实时网络故障情况再“跑”到最缺电的位置,为关键负荷提供短时支撑。韧性提升的落脚点,最终要落到量化指标上。最朴素也最常用的一种做法,是用“系统在故障期间实际获得的电量”和“所有负荷正常用电需求电量”的比值来衡量。比值越接近 1,说明极端事件下的供电保障做得越好。后面建立优化模型时,我们就会围绕这个思路设计目标函数和约束条件。1.2 移动储能相对固定储能的核心优势固定储能和移动储能看起来都是在合适的位置放电,但本质上差别很大。固定储能选址一旦确定就不可更改,如果故障点距离储能接入点太远,或者线路被切断,储能再好也送不出去。移动储能可以在预布局阶段选择一个相对合理的落点,故障动态阶段再通过道路系统把储能车调度到真正需要的位置,相当于把“选址灵活性”和“运行灵活性”结合了起来。从投资利用率角度看,固定储能很难兼顾多个位置的应急需求,相当于每个站点都要配置一份备用容量,成本很高;移动储能车可以按风险变化在多个节点之间轮换,一辆车就能覆盖更大范围的应急需求。当然,移动储能的劣势也很明显:车辆在移动期间无法供电,到达新位置也需要时间,因此“预布局”和“动态调度”必须放在同一个优化框架里考虑,否则很容易出现储能车赶到了但负荷已经停了太久的情况。下表是我在复现时用来对比两者特征的简化表,可以直观看到差异。对比项固定储能移动储能接入位置一次性选址灾前预置,灾后可迁移故障适应性依赖固定线路连通性可根据故障动态调整利用率低峰时段容易闲置多场景复用,利用率更高移动过程供电不存在移动期间不可供电,需建模投资与运维单点重复配置单台覆盖范围更大选择移动储能并不意味着固定储能没有价值,两者可以互补。只是当我们特别关注极端事件后的韧性恢复时,移动储能的机动性往往比增加固定容量更划算,这也是我选择复现这类策略的原因。1.3 为什么选 IEEE33 节点系统作为复现对象IEEE33 节点系统是一个很经典的配电网算例,基准电压 12.66kV,包含 1 个根节点、32 条支路和若干联络开关,节点数不多不少,非常适合研究馈线故障、负荷转供、分布式电源接入等问题。相比动辄上百节点的实际配电网,IEEE33 节点既能体现潮流的空间特性,又不会让优化模型在规模上失控,用来验证移动储能预布局与调度策略再合适不过。另外,IEEE33 系统的网络结构公开、数据统一,不同文献、不同团队做出来的结果有可比性,这给“复现”提供了很大的便利。我在实现时并不需要从零构建复杂的拓扑数据,只需要把标准支路参数、节点负荷数据翻译成 Matlab 可读的结构体,再统一处理成优化模型需要的矩阵形式。对于初学者来说,从这个算例入手最容易建立“模型-代码-结果”的完整闭环。2. 问题建模:预布局与动态调度的数学本质2.1 极端事件与场景集合怎么构造移动储能的预布局动作发生在灾害来临之前,但故障到底发生在哪几条线路上、断开几条支路,事前并不知道。为了在数学上刻画这种不确定性,我采用多场景鲁棒优化的思路:预先构造一组极端故障场景,每个场景对应一组线路同时断开的组合。场景集合的构造既要覆盖可能出现的最坏情况,又不能规模爆炸。最直接的方法是先识别系统中的脆弱线路,通常是对负荷影响大、处于关键供电通道上的支路;然后在脆弱线路里按“同时故障线路数上限”做抽样,形成候选故障线路集合。比如设置最多同时断开 3 条支路,就从候选支路中随机组合出 20 个场景。这个方式简单有效,也方便后续调节鲁棒保守程度。还有一个细节值得注意:故障场景并不是对称的。不同场景下,同一储能车的初始位置相同,但后续移动路径、接入节点可能完全不同,所以预布局变量必须独立于场景,而动态调度变量要随场景变化。这也是两阶段优化的基本结构:第一阶段做不依赖于具体故障场景的部署决策,第二阶段针对每个具体场景再优化。2.2 目标函数与韧性评估指标优化目标的核心是“让极端场景下的负荷削减量尽量小”。可以直接把多个场景的失负荷量加权求和作为目标,权重可以取场景概率,也可以取为 1,代表等概率最恶劣情况。为了更贴近韧性评价,我会额外加入一个所有场景统一计算损失函数的形式:min Σ_s w_s · Σ_t Σ_i P_cut(s,i,t)其中 s 是场景,t 是时间段,i 是节点,P_cut 是切负荷量,w_s 是场景权重。如果要考虑移动储能车在不同位置的调度成本,也可以加入“移动启用费用”和“充放电损耗”,但要注意别让权重失衡。否则优化结果会为了省一点移动成本而牺牲大量负荷恢复,这不是我们想要的。为了最后直观评估韧性,我在代码里另外算一个指标 RI:RI = 1 - ( Σ_s Σ_t Σ_i P_cut(s,i,t) ) / ( Σ_s Σ_t Σ_i P_load(s,i,t) )RI 越接近 1,代表极端事件下恢复水平越高。这个指标不参与优化,只用于后处理和不同方案对比。实践上,如果 RI 从 0.45 提升到 0.78,对关键负荷的保障效果差异是非常明显的。2.3 核心约束:潮流、储能、移动与网络辐射状约束部分是这个模型的真正难点,我把它拆成四类写。第一类是潮流与电压约束。在配电网里,我采用支路潮流模型,把支路功率、节点电压、电流都写成变量。由于原始的非线性潮流方程求解困难,通常采用二阶锥松弛,把电压平方和电流平方设为新变量,原问题就变成可以被商业求解器直接处理的凸优化子问题。用公式简写就是每条支路要满足首末端功率平衡、电压降落方程和容量限制,再加节点注入功率平衡约束。松弛是否严格,需要在求解后检查松弛间隙,这一点我在后面避坑章节还会专门说。第二类是储能运行约束。每台移动储能车都有荷电状态(SOC)动态方程:SOC(v,t) = SOC(v,t-1) + η_ch·P_ch(v,t) - (1/η_dis)·P_dis(v,t)同时,放电功率不能超过额定上限,SOC 也要保持在上下限之间,比如 0.2 到 0.9。此外,同一时刻不能同时充电和放电,这个约束看起来简单,但写成线性表达式时需要引入一个二进制变量,比如用 z 表示充电状态,用 1-z 表示放电状态。第三类约束是移动储能的“位置-时间”耦合约束。这是移动储能和普通储能的本质区别。每台储能车在每个时间段必须恰好位于一个节点,位置变量是 0-1 变量;如果从节点 i 移动到节点 j 需要耗费 delta 个时段,那么在移动到达 j 之前,该车辆不能在其他任何节点放电。这部分约束是最容易写错的地方,我会在后面的代码和避坑章节重点讲。第四类约束是网络辐射状约束。配电网在正常运行和故障隔离后,线路需要保证连通且无环。在多场景模型中,每个场景的拓扑是确定的,但可以操作联络开关进行网络重构。为了保持可行性,需要加入保证辐射状结构的约束,常见做法是用单商品流或独立节点生成树约束。在 IEEE33 节点这个小系统上,用生成树约束可以接受,但大规模算例就要留意计算量。3. 求解框架与 Matlab 代码实现3.1 两阶段鲁棒优化的整体结构我们面对的是一个“先部署、后响应”的决策过程,自然对应两阶段优化模型。第一阶段变量是移动储能车的预布局位置和初始 SOC;第二阶段变量包括每个场景下的储能车实时位置、充放电功率、网络开关状态以及切负荷量。从数学形式上看,这是一个 min-max-min 结构:min_y { C_pre(y) + max_{s∈S} min_{z∈F(s)} C_ope(z, y) }也就是,在第一阶段决策 y 时,第二阶段要考虑最不利的场景 s 下,最优运行决策 z 带来的运行损失。如果只用期望场景优化,得到的结果往往低估不确定性;如果针对每个单场景都强制满足,又可能过于保守。鲁棒优化就是希望在两者之间寻找一个平衡点。在 Matlab 中我用 YALMIP 做建模平台,外部调用 Gurobi 或 CPLEX 求解混合整数规划。YALMIP 的最大好处是可以用符号逻辑构建约束,比如 binvar、sdpvar,代码可读性很好,适合复现和二次修改。如果你更习惯直接用求解器的 API 建模,也可以,只是变量矩阵比较复杂时维护成本会高不少。3.2 C&CG 求解流程及伪代码两阶段鲁棒优化最常用的精确算法是列与约束生成(C&CG)。它的思路是交替求解主问题和子问题:主问题先假设只有一个或几个已知场景,得到预布局方案;子问题根据当前预布局方案,找出让运行损失最大的最恶劣场景;如果存在更恶劣的场景,就把这个场景相关的变量和约束补充到主问题里,继续迭代,直到上下界收敛。我给一个简化的 C&CG 伪代码,方便理解整体流程:初始化:给定初始场景 s0,设置 LB = -inf,UB = +inf,k = 0。 while (UB - LB) > ε: 1. 求解主问题,得到预布局 y_k 和目标值 obj_master。 更新 LB = max(LB, obj_master)。 2. 固定 y_k,对所有可能场景 s 求解子问题,得到运行成本 obj_sub。 找到使 obj_sub 最大的场景 s*,更新 UB = min(UB, C_pre + obj_sub)。 3. 将场景 s* 对应的第二阶段变量和约束加入主问题。 4. k = k + 1。 end while在实际复现时,子问题不一定非得遍历所有场景。我们可以先用一个场景池做快速搜索,再在一些极端故障组合中找更恶劣场景,能显著降低迭代次数。对于 IEEE33 节点这种规模,遍历几十个场景其实还好,但如果把系统放大到几百个节点,就要考虑用启发式方法加速场景识别。3.3 Matlab 代码结构与关键代码片段我的代码仓库目录大致是这样的:|-- main.m # 主程序,设置参数并启动求解 |-- init_IEEE33.m # 初始化IEEE33节点网络数据 |-- generate_scenarios.m # 生成极端故障场景 |-- build_storage_model.m # 构建移动储能数学模型 |-- solve_master.m # C&CG主问题求解 |-- solve_sub.m # C&CG子问题求解 |-- plot_results.m # 结果可视化其中build_storage_model.m是最核心的部分,我贴一段位置变量的关键代码:%% 移动储能车位置变量与约束 % x(v, i, t) = 1 表示第v辆车在t时段位于节点i x = binvar(nVehicle, nNode, nTime, 'full'); cons_x = []; % 每辆车每个时段只能在一个位置 for v = 1:nVehicle for t = 1:nTime cons_x = [cons_x, sum(x(v, :, t)) == 1]; end end % 移动过程约束:从i到j至少需要delta个时段 % y_move(v,i,j,t)=1 表示第v辆车在t时段开始从i移动到j y_move = binvar(nVehicle, nNode, nNode, nTime, 'full'); for v = 1:nVehicle for i = 1:nNode for j = 1:nNode if i ~= j for t = 1:nTime-delta(i,j)+1 % 如果开始移动,则起点在t时刻占位,终点在t+delta到达 cons_x = [cons_x, ... x(v,i,t) + y_move(v,i,j,t) <= 1, ... x(v,j,t+delta(i,j)) >= y_move(v,i,j,t)]; end end end end end这段代码里用了两个变量来表示位置和移动事件,配合起来就能保证“移动期间不能供电”和“到达后下一时刻可接入”的逻辑。实际建模时还要加一些冗余约束,比如一个车辆不能同时开启两个方向的移动,否则模型虽然可行,但求解速度会变慢。4. 算例分析:预布局节点怎么选、储能车怎么跑4.1 参数设置与场景数据为了能让结果可复现,我在init_IEEE33.m里使用了标准的 IEEE33 节点拓扑,基准功率 1MVA,基准电压 12.66kV。移动储能参数假设如下表所示。参数名称数值移动储能车数量2 辆单台储能容量1 MWh最大充/放电功率0.5 MW初始 SOC 范围0.2 - 0.9充/放电效率0.95 / 0.95时间分辨率1 小时调度总时段8 小时场景数量6 个极端场景最大同时断线数3 条场景选取上,我以支路 3-4、5-6、8-9、13-14、26-27 这些负荷较重或位于馈线末端的关键支路为主要候选,随机组合出 6 个典型极端场景。在这个设置下,预布局阶段解出来的运行成本、切负荷量以及韧性指标都符合物理直觉。4.2 预布局结果解读在本算例的参数下,优化得到的预布局方案是:第一辆车停在节点 18,第二辆车停在节点 25。单看拓扑,节点 18 和节点 25 都位于网络末端区域,但周围负荷密度并不低。为什么不是停在根节点附近?因为如果所有储能车都停在靠近变电站的位置,故障一旦断开上游路线,末端负荷依然孤立无援;如果停在太偏远的末端,又可能因为道路距离和移动时间太长,无法在窗口期内支援其他区域。优化模型自动折中了这两点。另外,预布局结果还与负荷时序有关。如果某个时段末端负荷特别高,储能车会倾向于靠近该区域。我试过把模拟时间设为 24 小时,并加入峰谷负荷曲线,预布局位置会在馈线中段和末端之间来回选择,说明这种策略对负荷时间特性的敏感度很高。做工程应用时,至少要基于当地的典型日负荷曲线来定预布局方案,不能只看平均值。4.3 动态调度过程与恢复效果以其中一个故障场景为例:假设在 t=1 时,支路 8-9 和 25-29 同时断开,导致节点 18 所在的支路末端以及节点 25 周边进入孤岛状态。优化后的动态调度结果大概是:t=1:储能车1在节点18放电,储能车2在节点25放电; t=2:储能车2感知到节点25下游负荷较低,开始向节点29移动; t=3:储能车2到达节点29,接入并恢复该区域关键负荷; t=4:储能车1继续在节点18放电,同时等待网络重构结果; t=5:联络开关闭合,部分负荷转供,储能车1陆续降低出力;可以看到,储能车并不是从一开始就满功率放电,而是结合网络重构的时序动态调整。早期孤岛内负荷需求量很大,储能车高功率输出;随着上游开关恢复供电,储能车会降低出力次数,保留一部分 SOC 给可能出现的二次故障。这就是“动态调度”和“静态应急电源”最大的区别。4.4 与无储能、固定储能的对比结果为了更清楚地体现移动储能的优势,我设置了三个对比方案:无储能、固定储能(两套固定储能分别在节点 18 和 25)、移动储能(预布局加动态调度)。在相同故障场景和负荷时序下,结果如下表。方案总切负荷量 (MWh)韧性指标 RI无储能10.850.42固定储能6.720.64移动储能3.960.78固定储能能解决一部分末端孤岛供电,但因为位置固定,遇到故障点刚好阻断储能接入线路时,就无能为力了。移动储能通过灾后移动到孤岛内部,能够显著减少切负荷量。从算例看,RI 从 0.64 提升到 0.78,对应总切负荷量下降了接近一半。这个结果也说明,仅靠增配储能容量不一定是最优解,把储能车部署到“对的时间”和“对的位置”,效果往往比单纯多买电池更明显。5. 避坑指南与扩展建议5.1 求解速度慢怎么办IEEE33 节点加上两个储能车、六个场景,规模不大,但如果把所有时段和所有节点的变量一次性铺开,混合整数规划还是会变得很臃肿。第一次跑的时候,我遇到主问题求解时间超过十分钟的情况,后来通过三个手段解决。第一,给初始解。先用启发式方法生成一个可行的预布局方案,作为主问题的 MIP start。比如可以按“离脆弱线路近”的规则预先指定储能车停靠节点,再让优化器在这个基准上调整,能省去大量分支定界时间。第二,加对称破除约束。两辆车参数相同时,预布局结果会存在大量对称解,变量置换后目标一样。加入“第一辆车位置编号小于第二辆车位置编号”之类的约束,可以砍掉近一半搜索空间。第三,在子问题里先求解线性松弛版本,用目标值来判断该场景是否是潜在最恶劣场景。如果某个松弛场景的目标值排名靠后,大概率不会成为真正的恶劣场景,可以先不加入主问题,减少主问题负担。5.2 DistFlow 二阶锥松弛细节在 YALMIP 里写 DistFlow 二阶锥约束时,要注意变量替换后的松弛形式。简单来说,对每条支路都需要满足:V_j <= V_i - 2(R·P + X·Q) + (R^2 + X^2)·I_ij其中 R、X 是支路电阻和电抗,V 和 I 是平方形式的变量。这个约束直接写在 YALMIP 里就是锥约束。常用写法是:cons = [cons, V(j) - V(i) + 2*(R*P_ij + X*Q_ij) - (R^2+X^2)*I_ij <= 0];松弛之后还需要检查误差。我一般会在结果更新后计算每条支路的松弛间隙:gap_ij = |P_ij^2 + Q_ij^2 - V_i * I_ij|如果某个支路的间隙远远大于 1e-4,说明这个支路很可能发生了电压越限或潮流不符合物理规律,需要检查负荷参数和网络数据。多数情况下,IEEE33 节点算例只要电压约束、容量约束不过于紧张,松弛误差都很小。5.3 移动时间建模的常见错误移动时间是整个模型里最容易出错的地方。常见一个错误是:只把“移动终点节点”的接入时间约束了,却没有限制“移动起点节点”在移动期间不能供电。结果就是储能车一边移动一边还在给原来的节点放电,模型看起来可行,实际上却是物理悖论。正确的做法是同时约束起点和终点的位置状态。我建议把移动过程拆分为两个时间点:开始移动的时刻 t 和到达目标的时刻 t+delta。在 t 到 t+delta-1 之间,该车辆不能出现在任何节点,可以通过“每辆车每时段必须位于一个节点”的约束结合移动状态实现。如果时间步长比较粗(比如1小时),可以假设车辆在整小时段内处于“不可用”状态,到达后的下一个小时才能接入。这种近似对结果影响不大,但模型复杂性会小很多。还有一个容易忽略的问题:移动时间和道路距离不能直接用欧氏距离计算。IEEE33 节点只是电网拓扑,不代表车辆行驶路径。我在代码里用了一个距离矩阵,将节点之间道路距离折算成小时,数值要设置合理,否则预布局决策会过度乐观。5.4 鲁棒保守程度如何调节鲁棒优化的保守度由场景集合和故障组合上限共同决定。如果你把所有可能故障组合都纳入最恶劣场景,求解结果会非常保守,储能车几乎要覆盖所有末端节点,这在工程上并不现实。反之,如果只选个别场景,决策又会过于激进。我在复现时增加了一个参数 Gamma,表示“同时断线数量上限”。Gamma 越大,考虑的最恶劣情况越严重,系统韧性指标通常会提升,但需要的储能容量和调度成本也会增加。实际使用时,可以基于历史极端事件统计来设定 Gamma,比如过去五年最大同时断线数是 4,就设为 4,不要凭感觉加大。另外,还可以把多个场景赋予不同概率权重,然后用条件风险价值指标替代期望值,这样能兼顾极端小概率事件和平均表现。这个扩展用 C&CG 框架也能实现,只是子问题的目标函数要调整一下,代码改动并不大。最后再分享一点个人的实际操作体会:复现这套策略,最难的不是储能本身的充放电方程,而是“位置”和“时间”这两个维度交叉产生的约束。建议你先把移动约束单独抽出来做一个最小算例,比如 5 节点 2 时段,验证位置变量和移动变量的逻辑能跑通,再把它放回 IEEE33 节点系统里做完整算例。另外,预布局结果对场景集和负荷时序非常敏感,不要指望一套参数在所有系统上都成立,真正做项目时一定要用当地线路数据和负荷曲线重新校准。这样复现出来的代码,才不是一纸空文,而是可以支撑工程判断的“活”工具。