☰
移动储能提升配电网韧性的两阶段预布局与动态调度Matlab实现
2026/10/21 7:16:11 网站建设 项目流程

做配电网韧性研究,最近两年绕不开“移动储能”这个词。台风、冰灾、暴雨一轮接一轮,配电网大面积停电的新闻大家没少看。传统的固定储能容量固定、位置固定,灾害来了只能守着一亩三分地;而移动储能可以灾前停到预测关键节点、灾后根据实际故障情况开到需要的地方去,天生就是提升配电网韧性的重要手段。所以“面向配电网韧性提升的移动储能预布局与动态调度策略”这类题目,在不少论文、基金和毕设里反复出现,但很多方案只给思路不给可复现代码,尤其IEEE33节点版本完整跑通的案例并不多。我最近把一套基于Matlab的完整实现从头复现了一遍,两阶段模型、Yalmip求解、全套出图脚本都在,这篇就把从建模到实现的细节一次讲透。适合正在做配电网韧性、综合能源系统或者储能调度方向的研究生和工程师参考,哪怕你之前没碰过移动储能,只看这一个项目也能快速上手。

1. 为什么要用移动储能扛配电网韧性?问题拆解与核心思路

对于做电力系统的人,“可靠性”是天天挂嘴边的:用户平均停电时间、停电频率、供电可用率,这些都是经典指标。但极端灾害下的韧性,完全是另一套游戏规则。

1.1 从“可靠性”到“韧性”:极端事件里配电网需要什么

可靠性描述的是长期统计平均意义上的供电能力,指标用SAIDI、SAIFI这些来刻画;而韧性更多关注的是“小概率、大影响”事件下,系统能不能扛住、恢复得快不快。举个直观例子:一座城市一年到头没停过几次电,可靠性指标很漂亮,但台风来了直接大面积停电24小时,这时候可靠性指标几乎说明不了问题,真正需要评估的是“台风下能撑多久、故障之后多久恢复”。

典型的韧性曲线是一条“三段式”:事件发生前系统性能处于正常水平,事件冲击期间性能快速跌落,事件结束后系统逐步爬坡恢复。三个关键量化指标:跌落深度、爬坡速度、恢复时间。配电网韧性提升,本质上就是做两件事:让性能曲线跌得浅一点、恢复得快一点。落到工程上,常见手段包括网架加固、合理配置分布式电源、部署储能设备、优化抢修策略,而移动储能正好能同时影响“跌落深度”和“恢复速度”两个环节。

1.2 移动储能为什么是“救火队员”,而不是万能药

固定储能的好处是成本相对低、维护方便,但它的位置和容量都固定。灾害场景下,故障点位置是动态变化的,固定储能守在原地经常帮不上忙。移动储能像消防车,平时停在车场,灾前按预测开往高风险区域,灾后按实际故障再次调整位置,通过充放电为孤岛提供支撑。这个特性让它天然适合做韧性资源:灾前可以预置到预测的薄弱点,灾后可以奔赴真实故障形成的孤岛,一台车可能服务多个节点,比多台固定储能的灵活性高不少。

但它不是万能药。容量有限,撑不了太长时间;移动需要时间,灾后道路受阻可能根本到不了位;接入位置受配电网拓扑和道路条件限制,不是想停哪就停哪。所以需要数学优化把“几台车放哪、什么时候去哪、充多少电放多少电”算清楚。大家在网上看到的很多“完美复现”代码,核心都在解决这个优化问题,区别只是模型细节、场景生成方式和求解算法不同。

1.3 “预布局”和“动态调度”是一件事还是两件事

两者是耦合的:灾前没有把车放到合适区域,灾后想调度也来不及,因为道路通行时间受灾害影响;灾后的动态调度又必须服从灾前预布局给出的初始状态。这就形成了典型的两阶段决策问题:第一阶段灾前预布局,第二阶段灾后动态调度,目标统一为最小化全过程的失负荷量。

一个完整实现必须同时考虑车辆的时间窗、电池SOC、配电网潮流约束和故障发生的不确定性。如果你拿到一个实现里只做了静态选址而没有动态调度,那它基本只是半成品;反过来只做灾后调度不考虑灾前预置,模型再精细也脱离实际,因为你没法回答“车从哪里来”的问题。我复现IEEE33节点方案时,最看重的就是这个两阶段逻辑是否闭环。

2. 模型怎么建:两阶段优化框架的数学拆解

复现的第一步不是敲代码,而是把数学模型吃透。我看到的这个方案总体逻辑是:以配电网的DistFlow潮流模型为基础,把每台移动储能建模成“可移动的电池”,决策分为两个阶段,统一在一个优化框架里求解。

2.1 阶段一:灾前预布局模型

预布局的时间尺度通常是灾害预警发出后到灾害正式发生前的几小时到十几小时。这个阶段里,调度中心已经获知预测的台风路径、可能受影响的线路集合,最常见的做法是生成若干故障场景,然后在所有场景下寻找一个“最不后悔”的初始布局方案。决策变量包括:每台MESS的初始接入节点、是否预留在某些关键区域、各车携带的初始电量。

为什么不用确定性模型?因为预布局是在灾害发生前做出的,当时只能看到预测信息。如果只针对一个故障场景优化,真实故障一旦与预测不符,布局就废了。所以我在实现里用的是多场景随机优化:生成若干种故障组合,目标函数设为所有场景下期望失负荷最小化,同时让布局方案在这些场景里都有较好表现。约束相对简单:每台车只能接入一个节点,同一个节点最多接入一台车,车辆总数不超过车队规模。

这里要提醒一点:预布局并不一定要把所有车都接入电网。有些车可以停在候选接入点但不接线路,等故障信息明确后再接入。模型里用初始接入节点的0-1变量区分“布置位置”和“是否立刻接入”,这个细节会显著影响最优结果,复现时要留意原方案的语义定义。

2.2 阶段二:灾后动态调度模型

灾后决策才真正复杂。假设整个研究周期是24小时,按15分钟一个时段,就是96个调度时段。动态调度要回答:每台MESS在什么时间接入哪个节点,接入期间以什么功率放电,是否需要移动到下一个节点。

约束分成四块。第一是配电网潮流约束,采用DistFlow模型线性化形式,把线路潮流和节点电压的二次项做变量替换,得到线性方程组。第二是储能运行约束,包含充放电功率上下限、同一时刻不能同时充放电、SOC递推关系、末日SOC范围要求。第三是移动约束:车辆从一个节点移动到另一个节点需要时间,这个时间根据距离和速度换算,用一个时间窗逻辑表示“车辆在某时段正在路上,无法接入电网”。第四是系统级约束,比如辐射状运行、线路容量、节点电压上下限。

目标函数在所有阶段一致:最小化整个事件周期内所有节点的重要度加权失负荷量,再加上一点MESS移动惩罚项。所谓重要度加权,就是给医院、通信、关键用户更高的权重系数,这样优化算法会优先保重要负荷,更贴近实际。纯学术版本可以不加权重,但工程评审时加权模型的说服力会强很多。

2.3 不确定性建模:场景法还是鲁棒优化

我复现的版本采用的是随机场景优化,故障场景通过线路故障概率独立抽样获得,每个场景赋予概率权重。好处是物理意义清晰、求解速度快、和传统随机规划框架无缝衔接;坏处是极端低概率场景可能被平均掉,万一真实场景就是那个低概率极端事件,方案表现可能不够好。

如果你想追求更强的保守性,可以改用两阶段鲁棒优化:外层选最坏故障场景,内层做调度决策,用列与约束生成算法迭代求解。但CCG的实现复杂度明显高,需要维护主问题和子问题的对偶形式,对新手不友好。我的建议是先把随机场景版本跑通,再考虑升级成鲁棒版本。鲁棒版本的Matlab实现本质是在第一阶段的迭代循环里反复调用MILP求解器,代码量会多出一倍,而且调参的坑更多。

3. IEEE33节点算例与参数设计:复现的第一步

不管模型多漂亮,落到代码里第一步是把算例数据搞清楚。IEEE33节点是配电网研究里最经典的标准算例之一,大家口径一致,适合做复现验证。

3.1 IEEE33节点系统:公共参数与潮流基准

IEEE33节点系统额定电压12.66kV,基准容量一般是10MVA,包含33个节点、32条支路,另有5条联络开关支路,总共37条支路。标准总负荷约3715kW加2300kvar,最大负荷节点一般集中在末端,满载时末端电压会明显偏低,大约0.9p.u.左右,这是公开基准数据。潮流收敛以后,系统总网损大约在200kW量级,这些都可以作为数据验证的参照。

注意一个最常见的坑:很多教材给的是标幺值,代码里容易把功率基准弄错。我复现时习惯先把所有参数转成标幺值,然后写一个简单的前推回代潮流程序验证一次,确保节点电压分布和标准结果一致,再进优化模型。算例数据本身没有验证就直接跑优化,出了毛病很难定位是数据问题还是模型问题。这个步骤看似额外,实际能省下大量排查时间。

3.2 移动储能参数与候选接入点选择

移动储能参数不是标准算例自带的,需要自己定义。我用的是一组比较常见的设定:三台移动储能,单台容量500kWh,额定充放电功率100kW,最大SOC范围0.1到0.9,移动速度取20km/h,模拟灾后道路受阻的情况。候选接入点我选了8个,原则是:重要负荷附近、负荷密度高、交通相对可达、尽量分布在网络不同区域。

为什么候选点不能选太多?因为每台MESS在每个时段在每个候选点上的接入状态一旦变成0-1变量,组合数量会爆炸。8个候选点、3台车、96时段,0-1变量规模已经到几千,再翻倍就很难求解了。实际工程里调度人员也不可能让车随便停,候选点本来就是预设的应急接入点,所以这个限制是合理的。如果你想做更贴近实际的研究,可以把候选点分成“必选重要节点”和“可选一般节点”两类,但模型复杂度会直线上升。

3.3 灾害场景与故障集合构造

灾害场景的构造直接影响优化结果。我采用的是:把每条待研究支路按停电概率独立抽样,概率用故障率乘以线路长度换算,再结合台风强度的提升系数,抽样若干次生成几十个场景。场景生成后要检查一遍:是否存在全系统完好的场景?是否存在大到多个片区同时停电的场景?如果所有场景都太温和,优化模型会把车随便放;如果都太极端,模型可能无解。我一般控制在20到50个场景,覆盖2到6条线路同时故障的组合,并在场景概率上做归一化。

这里有个经验:场景数量不是越多越好。25个场景通常已经能让布局方案稳定;增加到100个场景,目标值变化很小,但求解时间可能从几分钟变成半小时。复现实验时可以先跑25个场景,后期为了出图好看或者验证收敛性再补跑更多场景。还有一个技巧:把场景生成函数做成固定随机种子,保证每次复现结果一致,否则写论文时会发现同一组参数每次都得到不同结果,非常头疼。

4. Matlab实现全流程:Yalmip+Cplex/Gurobi落地

模型清楚了,下一步就是代码。先说明工具链:Matlab R2020b以上,Yalmip做建模框架,求解器用Cplex或者Gurobi。为什么不用Matlab自带的intlinprog?因为问题里有一堆二阶锥和线性化后的约束,用Yalmip写起来非常直观,而且可以一行切换求解器。intlinprog面对几千个0-1变量和几万条约束,求解速度惨不忍睹,我亲测过,同一问题Yalmip+Cplex只要几分钟,intlinprog跑半小时还在原地打转。

4.1 工具链选择:为什么用Yalmip而不是手写求解器

Yalmip是一种建模语言,让你用接近数学表达的方式写约束,然后自动转成求解器能吃的标准形式。它在学术界用得非常广,约束怎么写、变量怎么定义都有成熟的函数,尤其是binvar、sdpvar、optimize这三板斧,基本覆盖了所有需求。对比手写分支定界或单纯形法,Yalmip省掉的编码量不是一点半点,而且模型改起来极其方便,今天换一个目标函数,明天改一条约束,都只是改几行Yalmip语句的事。

Cplex和Gurobi都是商业求解器,处理MILP能力强,尤其擅长处理大规模0-1整数变量。组合起来以后,代码的数学模型可读性很高,别人接手你的代码,看Yalmip约束基本就能还原你的模型,这也是论文复现项目特别看重的一点。

4.2 目录结构与数据准备

我建议目录这样组织:

mess_resilience/ main_pre_dispatch.m load_data.m build_33bus.m generate_scenarios.m build_mess_model.m solve_milp.m plot_results.m

load_data.m里放节点和支路数据,build_33bus.m负责生成IEEE33的拓扑邻接矩阵和阻抗参数,generate_scenarios.m负责抽样产生故障场景,build_mess_model.m是整个项目的核心,所有变量和约束都在这里定义,solve_milp.m调用求解器,plot_results.m负责输出图片和表格。命名规则清晰,后面换算例换参数也不用动主文件。

数据准备阶段,有两个容易被忽视的细节:一是负荷时序曲线,真实项目里典型日负荷曲线不是平的,我会用一组归一化日负荷系数乘上各节点基础功率,白天的系数在0.8到1.0之间,夜间低谷在0.4左右;二是移动时间矩阵,由候选接入点之间的道路距离除以移动速度得到,不是简单直线距离,复现时可以用坐标距离近似,但要注意加上道路曲折系数,我一般取1.2到1.5。

4.3 两阶段MILP的核心代码骨架

下面给出核心部分的伪代码骨架,Matlab/Yalmip风格:

% 参数读取 [bus, branch] = load_33bus(); num_scene = 25; % 场景数 num_t = 96; % 时段数 num_mess = 3; % 移动储能数量 cand = [3, 6, 8, 13, 18, 22, 25, 30]; % 候选接入节点 % 生成场景:线路故障集合 scene_broken = generate_scenarios(branch, num_scene); % 定义决策变量 X_init = binvar(num_mess, length(cand)); % 灾前接入位置 X_sched = binvar(num_mess, length(cand), num_t); % 灾后接入位置 P_dch = sdpvar(num_mess, length(cand), num_t); % 放电功率 P_ch = sdpvar(num_mess, length(cand), num_t); % 充电功率 SOC = sdpvar(num_mess, num_t); % 电量状态 P_cut = sdpvar(33, num_t); % 失负荷量 % 约束定义... % 目标函数... optimize(Constraints, Objective, sdpsettings('solver','cplex','verbose',2));

这里注意重点:灾后调度变量X_sched是多了一维时间的,而灾前X_init是一组与时间无关的0-1变量,这两个变量通过“初始接入位置约束”联系起来:每台MESS在第一个时段只能接入X_init指定的节点。

每个场景的失负荷变量P_cut和线路潮流变量需要按场景分别定义,这里就有两种写法。第一种写法是“场景展开的大MILP”,把25个场景复制出25组动态调度变量,共享同一组灾前布局变量,求解一次得到所有决策。第二种写法是“顺序求解”,先把预布局解固定,再针对每个场景解动态调度。我复现时选了第一种,因为原方案要的是联合优化,场景展开写起来虽然占内存,但逻辑直接、结果更优。

代码实现时用for循环拼约束,把场景维度嵌进去。循环内部把每个场景的DistFlow约束、储能约束和失负荷约束都写到总约束集里。变量维度一大,Matlab的sdpvar拼接要特别小心,索引对不上会静默出错,我建议先写一个“调试模式”:2个场景、24时段、1台车跑通,确认结果合理再放开到完整配置。

4.4 求解与出图:从结果变量到可视化

求解完成后,结果变量的提取有一些经验。先用value()取X_init、X_sched、P_dch、SOC、P_cut等变量的数值。画图时我至少输出四张图:第一张是33节点拓扑图上标注MESS灾前位置和灾后移动轨迹;第二张是各时段失负荷功率曲线,对比有/无MESS;第三张是每台MESS的SOC变化曲线;第四张是关键节点电压剖面。代码里用plot和text画拓扑图就够,不用专门的可视化工具箱。

如果求解速度太慢,一个非常实用的技巧是:先用默认参数跑一次,找出哪些0-1变量几乎不变,然后把这些变量的取值固定下来,作为下一轮求解的初始可行解,能大幅缩短MIP的上下界收敛时间。另一个技巧是在sdpsettings里设置'cplex.mip.tolerances.mipgap', 0.01,把最优性间隙从默认的1e-4放宽到1%,结果只差零点几个百分点,但速度能快好几倍。科研复现时你甚至可以用两条路线对比,看目标值差异是否在可接受范围,如果确实在1%以内,就可以放心用宽松gap跑大规模测算。

5. 结果怎么看:复现正确性验证与灵敏度分析

把代码跑通不算复现成功,必须验证结果合理,才算真正落地。很多号称“完美复现”的项目,图很漂亮,但仔细一查漏洞百出。我给自己定了三道验证关,也建议你照做。

5.1 先验证标准算例:潮流与负荷曲线

我拿到任何复现代码,第一件事是跑基准场景:不看任何MESS,只做IEEE33潮流,检查末端电压和总网损。如果末端最低电压在0.90到0.93p.u.之间,说明数据基本正确;如果出现低于0.5p.u.或者高于1.1p.u.,一定是数据或模型有误。

再验证负荷曲线:把每个节点一天96个时段的负荷加起来,除以总基准功率,应该等于1.0。以前复现时吃过亏,某节点的负荷写成了瞬时kW而不是功率系数,结果优化出一个永远不可能完成的充电计划。还有一个容易踩的点:IEEE33节点标准数据是三相平衡的,而你优化模型可能是单相模型,复现时要注意基准电压和功率是否单位匹配,否则算出来的电压、功率全部偏小十倍。

5.2 韧性提升指标怎么算

推荐用三个指标:系统总失电量,单位kWh;最大恢复时间,指最后一个失负荷节点恢复供电的时间;韧性面积,把负荷曲线下的缺口面积算出来。以我复现的这个方案为例,无MESS时,某严重场景下的总失电量大约在4到6MWh;加入三台优化布局的MESS后,可以压到2到3MWh,同时最大恢复时间缩短约40%。当然具体数值依赖场景,不要直接照搬。

为了说明优化布局的价值,常规做法是设置三组对照组:无MESS、MESS随机预布局、优化预布局。表格式对比更直观。

方案总失电量/kWh最大恢复时间/h韧性面积/p.u.
无MESS约5200约8.0约0.35
随机预布局约4100约6.5约0.28
优化预布局约2600约4.5约0.18

强调一下,这个表只是示意,你自己复现时数值会不同,但优化预布局一定显著优于随机预布局。如果跑出来优化预布局和随机预布局几乎没差别,那大概率是场景太温和或者候选点选得不好,需要回头检查。这里有一个很隐蔽的错误:如果场景生成里包含了大量不需要移动储能的小故障,那么所有预布局方案的表现都会被平均到接近,优化差异被稀释。解决方法是固定一个“基准极端场景”,单独对比不同布局在该场景下的表现。

5.3 关键参数的灵敏度规律

灵敏度分析是论文里常用的手段,但对工程方案选择也有实际意义。我跑下来比较明显的有三条规律。

第一,MESS数量从2台增加到3台,系统失电量下降显著,但从3台增加到4台,下降幅度就明显放缓,说明边际收益递减,这为“配多少台车”提供了直接依据。第二,单台容量从250kWh增加到500kWh收益很大,从500kWh增加到1000kWh收益放缓,考虑到单台容量增加会抬高成本和尺寸,实际配置不能只看曲线。第三,移动速度从10km/h提到30km/h,最大恢复时间明显缩短,但速度再往上走收益有限,因为超过一定速度后瓶颈变成了放电功率而不是赶路时间。

除了这三条,还有一个容易被忽视的变量:预布局时间窗。如果预警时间只有2小时,车辆只能覆盖近端候选点,优化效果受限;如果预警时间有12小时,车辆就能高速到达远端关键节点,韧性提升效果完全不同。这正好呼应了模型第一阶段的意义:预布局不是简单选点,而是在信息时间和物理约束之间做权衡。灵敏度曲线建议每组参数至少跑5个点,这样画出来的图才有说服力。

6. 复现中的常见问题和避坑清单

这块单独拎出来,是因为我最初复现的时候,至少有三分之一的时间花在排查怪问题上。有些错误非常隐蔽,报错信息还很友好,让你以为模型没问题,其实是约束漏了或参数错了。

6.1 高频坑速查表

现象可能原因解决方法
模型不可行/无解某个时段SOC强制同时充放电检查储能约束,加充放电互斥约束
目标值为0,明显异常失负荷变量没有与节点功率平衡方程关联,成了自由变量检查节点功率平衡是否漏写P_cut
迭代求解极慢BigM取得过大按各约束实际物理上限收紧M值
结果与参考值差很大负荷功率基准值搞错统一标幺后再进模型,先用潮流验证
场景抽样全是温和场景故障概率设得太低调整概率,用多场景覆盖2-6条断线组合
求解器报错无界目标函数里缺了变量惩罚项失负荷和充放电变量加小权重正则项
Cplex报许可证错误环境变量未配置把license文件路径配好,并确认求解器接口版本与Matlab兼容

6.2 Big-M取值与数值稳定性

凡是把非线性或逻辑关系线性化的地方都会出现大M。我第一次写的时候图省事给所有地方都M=1e6,结果求解器在数值上非常不稳定,置信区间不收敛。后面学乖了,所有大M都按物理上限定:线路功率M用线路容量上限加一点余量,储能接入状态M用最大放电功率,节点电压偏差M用0.2p.u.。原则是“能取多小取多小,但必须严格大于变量现实上界”。

另外有个细节:Yalmip里对不等式约束用implies或者binvar逻辑时,要注意避免变量之间形成循环依赖,否则会产生隐式非线性,求解器会报警。遇到这种情况,把循环依赖的变量关系展开成显式约束。比如“接入状态为1则不能同时移动”这类约束,写成X_move + X_access <= 1即可,不要用implies去套,后者既慢又容易出数值问题。

6.3 求解时间爆炸的降维手段

如果25个场景、96时段、3台车、8个候选点的组合让你等半小时以上,有几个降维手段按优先级排序:第一,把时段分辨率从15分钟改成30分钟,损失一点精度但速度翻倍;第二,把候选点从8个减少到6个,二次减少组合;第三,固定部分时段的接入变量,只优化关键时段的调度;第四,用两阶段分解迭代求解。最推荐的还是前两个,操作成本最低。

在代码里加个“调试模式”:用2个场景、24时段、1台车先跑通,确认结果和约束无误后再放开到完整配置。这个习惯帮我省了大量debug时间。还有一个经验是观察求解日志里的gap收敛曲线,如果gap卡在某个值下不去,多半是某个整数变量在多个场景之间震荡,这时候给该变量加一条对称破缺约束,例如限制第一台车必须接入编号最小的候选点,往往立竿见影。

最后再聊两句个人经验。

我前前后后把移动储能预布局与动态调度这类项目复现过几个版本,最深的体会是:不要听到“完美复现”就直接双击运行。任何一份代码,拿到手都要先过“数据验证、基准验证、逻辑验证”三道关。数据验证是把负荷、线路、储能参数逐项核对;基准验证是跑无故障场景看潮流是否合理;逻辑验证是人为构造一个极端场景,看模型反应是否符合直觉。这三点过了,代码才算是真正属于你的。

再分享一个小技巧:移动储能项目最容易被忽略的地方是“车辆在路上”的时段。很多初版模型只约束了接入才能放电,忘记约束移动过程中无法接入,导致算法安排车辆一边赶路一边放电,结果自然不可信。你只要在MESS的接入状态变量上加一条“如果该时段处于移动状态,接入状态等于0”的约束,这个问题就解决了。把这个细节处理好,你复现出来的结果才会在评审和答辩时站得住。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询