每年台风季,调度屏上总有那么几个节点让我觉得特别无力。线路跳闸不可怕,可怕的是关键负荷跟着一起躺平——医院、泵站、通信基站一旦断电,抢修压力直接翻倍。这时候如果有一台能"跑"的储能车,提前停在预测的关键位置,故障后又可以根据现场情况挪位置、调出力,整个恢复过程会从容很多。这正是"面向配电网韧性提升的移动储能预布局与动态调度策略"要解决的问题。这篇文章以IEEE33节点系统为例,完整拆解这套策略的建模思路、求解过程和Matlab复现时的关键细节。适合正在做配电网韧性、分布式储能优化调度的研究生,以及想用标准算例验证算法效果的工程师。1. 问题本质:为什么韧性提升要依赖移动储能1.1 配电网韧性,跟传统可靠性是两码事很多刚接触这个方向的人会把韧性和可靠性混在一起,这是第一个容易踩的坑。传统可靠性研究的是日常运行中的随机故障,指标大多是平均停电时间、平均停电频率这类长期统计量,关注的是"大部分时间能不能稳定供电"。而韧性研究的对象是极端事件,比如台风、冰冻、暴雨等小概率大冲击场景,关注的是"扛不扛得住、恢复得快不快"。用一条曲线来理解韧性最直观:横轴是时间,纵轴是系统性能。事件发生前系统处于正常水平,事件冲击瞬间性能陡降,随后是持续一段时间的性能低谷,直到抢修完成、负荷全部恢复,曲线回到正常水平。韧性提升要做的事情,就是让曲线往三个方向走——让下降幅度更小、让低谷期更短、让恢复过程更平滑。这里面每一段都有文章可做,移动储能主要作用在"低谷期供电"和"恢复期支撑"这两个阶段。极端事件还有一个特点让优化问题变得棘手:故障信息不确定。台风过境前你只知道可能有几条线路要断,但具体是哪几条、断多久,根本没法提前确定。这就决定了决策必须分阶段做,灾前先拍板一部分动作,灾后再根据实际情况做调整。1.2 移动储能凭什么能派上用场固定储能装在一个地方,只能服务周边负荷,一旦所在馈线故障,储能再能打也被困在孤岛里。移动储能不一样,它本质上是"储能+运输载体"的组合。以某跨平台系统的实际配置为例,一台移动储能车通常有几百千瓦的额定功率、上千千瓦时的容量,可以在几小时内从一个节点转移到另一个节点。这意味着三件事:灾前可以预布局,把储能车提前开到风险区域的薄弱节点,避免故障后想用用不上;灾中可以根据实际故障情况重新部署,哪条馈线断了就开过去支援,灵活度远高于固定储能;同一个时段储能车在哪里,决定了它能给哪些负荷供电,这种时空耦合特性使得优化问题的维度和难度都上了一个台阶。你可能会问,配电网里不是还有网络重构、应急发电车这些手段吗?应急发电车和移动储能的区别在于,发电车烧油、响应快,但成本和噪音排放是问题;移动储能静音、零排放、能量可双向流动,还能在故障前后反复调度,策略上更灵活。网络重构只改变拓扑,不解决电源不足的问题,移动储能则是实打实地给孤岛提供一个可控电源。1.3 为什么必须"预布局+动态调度"两步走如果故障场景完全已知,那就是一个确定性的部署优化问题,把储能车放到最优位置就结束了。但实际灾害场景做不到这一点,所以决策必须拆成两个阶段。第一阶段叫预布局,发生在灾前。这时候只有灾害预测信息,比如哪些线路故障概率高、哪些负荷重要等级高,决策内容是每台移动储能初始放在哪个节点。第二阶段叫动态调度,发生在灾后。故障场景逐渐明朗,决策内容包括储能什么时候从哪个节点搬到哪个节点、在每个节点的充放电功率、哪些负荷可以恢复、网络怎么重构。用一个生活类比就很好理解:台风登陆前,救援队会把救生艇提前部署到地势低洼的社区,这是预布局。台风过后,根据哪片区域实际积水最严重,再把救生艇调过去,这是动态调度。两阶段决策不是多此一举,而是极端事件本身的不确定性逼出来的必然结构。2. 数学模型:两层协同优化框架怎么搭2.1 目标函数:失负荷成本最小化是核心整套模型的目标函数,中心项是系统失负荷成本最小化。但这里有个细节需要注意——不同负荷的重要程度完全不一样,医院手术室的负荷和普通商业负荷不能混为一谈。所以目标函数里要对每个节点设置权重系数,一级负荷权重最高,三级负荷权重最低。我复现时建议把权重直接乘在失负荷量上,形式大概是这样:$$\min \sum_{t \in T} \sum_{i \in N} \omega_i \cdot P_{i,t}^{curt} \cdot \Delta t + \sum_{t \in T} \sum_{k \in K} \left( c^{op} \cdot P_{k,t} + c^{mv} \cdot M_{k,t} \right)$$其中第一项是失负荷惩罚成本,反映韧性水平,量纲注意要和第二项统一;第二项是移动储能运行成本,包括充放电运维成本和移动成本系数乘上移动标志变量。这一项看起来不大,但加进去之后储能不会毫无意义地到处乱跑,模型行为更符合实际调度习惯。2.2 预布局约束:第一阶段决策变量预布局阶段的决策变量是最简单的,每个移动储能是否放置在各个候选节点,用0-1变量表示。约束也很直接:任意一台移动储能只能选择一个预布局位置;所有储能数量不超过可用车队规模;每个节点的接入容量有上限,不能把两台大容量储能堆在同一个节点浪费资源。严格来说,第一阶段还应该考虑预布局位置是否接入配电网、是否具备并网条件。在IEEE33节点系统里,多数节点都具备接入条件,但复现时建议把候选节点集合限定一下,比如只允许接入负荷节点,剔除纯联络节点,这样结果更可信。2.3 动态调度约束:第二阶段决策变量动态调度是模型里最复杂的部分,核心是移动储能的时间-空间-能量三重耦合。空间维度上,需要定义储能的位置状态变量,表示某时刻某储能是否在某个节点。每个时段的约束是:一台储能要么停在一个节点,要么在路上移动,二者必居其一。这个逻辑看着简单,写错位置约束会导致很离谱的结果——比如储能上一时段还在节点A,下一时段没动却出现在节点B。时间维度上,储能从节点i移动到节点j需要花费一定的时间,这个时间折算成时段数τ。出发约束要求它在移动前必须待在出发节点,到达约束要求它在移动完成后才能接入新节点。简化处理时可以认为所有节点间移动耗时均为一个时段,但更严谨的做法是引入节点间距离矩阵,再除以移动速度换算成时段数。能量维度上,就是经典的储能运行约束:充放电功率不能同时为正,需要引入充放电状态0-1变量;各时段荷电状态满足递推关系:$SOC_{t+1} = SOC_t + \eta_{ch} P_t^{ch} \Delta t - P_t^{dis} \Delta t / \eta_{dis}$;SOC有上下限,一般取0.1到0.9;调度周期末可以加一个SOC恢复约束,让储能回到合理荷电水平,方便下一轮调度。2.4 DistFlow潮流与配电网络约束配电网潮流约束,我用的是线性化DistFlow模型,也就是Branch Flow Model的线性形式。对每条支路$(i,j)$,核心方程有两条:电压关系:$V_j = V_i - 2(r_{ij}P_{ij} + x_{ij}Q_{ij})$;功率平衡:节点注入功率等于流出支路功率之和加上本地负荷消耗,再减去储能充放电功率。这里有一个细节我吃过大亏:IEEE33节点系统基准电压是12.66kV,但计算时全部用标幺值。很多复现结果对不上,就是因为母线电压的基准值没统一,电压约束0.95到1.05的上下限范围写得完全不合逻辑。故障线路的处理方式比较简单——故障支路的通断状态置0,该支路从网络中移除。如果允许主动孤岛运行,还需要额外处理孤岛内的电压和频率约束;纯线性DistFlow框架下一般都假设孤岛能稳定运行,只做功率平衡约束。3. 核心难点:不确定性建模与模型线性化3.1 场景法还是鲁棒优化,怎么选灾害不确定性怎么放进模型,是整个复现最核心的决策点。主流的做法有两类。第一类是场景法,也叫随机规划。根据历史台风路径和线路故障概率,用蒙特卡洛抽样生成若干组故障场景,每组场景包含一组断线集合。然后把目标函数写成所有场景失负荷成本的期望,调度变量做成随场景变化的。优点是模型直观、好理解,用确定性求解器就能解;缺点是场景数量少了怕覆盖不全,数量多了求解时间爆炸。第二类是两阶段鲁棒优化。第一阶段预布局在不确定实现前决策,第二阶段动态调度在不清楚集合中找到最恶劣场景下的应对。不确定性集合通常用基数约束描述,比如"最多同时断开3条线路"。鲁棒解不依赖具体场景概率,适合概率信息缺失的情况,但求解需要列约束生成(C&CG)迭代,代码复杂度明显更高。我的复现方案采用两阶段框架,预布局固定,动态调度用一个场景集合描述灾害可能性,场景集按概率权重参与目标函数。代码里留了一个切换开关,可以改成严格的两阶段鲁棒加C&CG,后面详讲。3.2 移动储能时空耦合约束的具体写法这是最容易被代码坑住的地方。我用二进制变量$p_{k,i,t}$表示储能k在时段t是否位于节点i,$s_{k,i,j,t}$表示储能k在时段t是否正在从i向j移动。每个时段的状态唯一性约束:$$\sum_{i} p_{k,i,t} + \sum_{i}\sum_{j \neq i} s_{k,i,j,t} = 1, \quad \forall k, t$$出发约束:如果$s_{k,i,j,t}=1$,意味着上一时段储能必须在节点i,表达式为:$$s_{k,i,j,t} \le p_{k,i,t-1}, \quad \forall k, i, j, t$$移动耗时的处理:如果节点i到节点j需要τ个时段,则储能到达j的时刻最早是$t+\tau$。为了简化实现,很多复现直接令τ=1,即从一个节点到另一个节点恰好占用一个时段。如果想做精确的行驶时间建模,可以增加一个出发标志变量$d_{k,i,j,t}$,表示在时段t开始从i出发,经过τ个时段后置位相应的$p_{k,j,t+\tau}$。这个写法更严谨,但约束和变量数量会显著增加。我在实际调试中发现,直接暴力展开所有i、j组合会让变量爆炸式增长,33节点下$33 \times 32$的移动变量矩阵在24个时段里要建几万个二进制变量。加速的技巧是先把节点间距离矩阵算好,过滤掉距离过远的节点对,或者只允许储能沿连通路径移动,别让它在所有节点对之间自由乱跑。3.3 保证辐射状拓扑的约束配电网正常运行时候是辐射状结构,故障后可以通过联络开关重构,但结构仍然是辐射状。这个"无环"约束有两种常见建模方法。一种是用生成树约束,借助单商品流思想:变电站节点作为根节点,从根向每个负荷节点发一个单位的虚拟流,每个节点只能有一个父节点,且父节点只能来自电气连通方向。这个约束在MILP里是天然线性的,写法和网络流约束完全一致。另一种相对暴力的方式是列出一组破坏约束,把系统中所有可能形成环路的支路子集枚举出来,要求每个环路中至少断开一条支路。但IEEE33节点的环路数量不少,枚举麻烦且约束数量庞大,我建议用单商品流,通用性更好。线性化的问题同样值得说。完整的DistFlow方程里含有电流平方项与电压乘积项,是非凸的,MILP没法直接处理。复现方案直接采用单商品流约束+线性化DistFlow,忽略$l_{ij}$高阶项,在10MVA基准功率下误差可以控制在工程可接受范围内。如果想更精确,可以用二阶锥松弛,把不等式写成凸约束,配合商用求解器也能解,但求解时间几乎翻倍。4. 求解策略:C&CG两阶段鲁棒求解全过程4.1 为什么用两阶段鲁棒而不是一把梭直接把两阶段模型当成一个大的混合整数规划丢给求解器,理论上是可行的,但实际根本算不动。原因在于第二阶段动态调度变量要依赖场景变化,不等于是为每个场景复制一份同样的变量。一旦场景数量上到几十个,变量和约束会膨胀到几十万甚至上百万级别,MILP求解器的分支定界树几乎没有收敛的可能。两阶段鲁棒的核心思路是"先粗后细":第一阶段只做少量全局决策,第二阶段针对最恶劣场景做精细应对。C&CG算法把这个思路落到迭代框架里,每一轮只增加最关键的约束,而不是把所有场景一次性塞进模型。4.2 主问题与子问题的拆解用C&CG求解两阶段鲁棒优化,需要把问题拆成主问题和子问题。主问题对应预布局加动态调度约束,决策变量包括储能预布局位置$z_{k,i}$、动态调度变量$x_t$,以及一个替代最恶劣场景失负荷成本的辅助变量$\eta$。在第$m$轮迭代中,主问题里已经有前$m-1$轮生成的最恶劣场景对应的调度约束和割约束。求解主问题得到当前最优预布局方案和失负荷成本下界。子问题则固定主问题给出的预布局位置$z^*$,在最不确定集合中找一个故障场景,使得该场景下的失负荷成本最大化。这个"max-min"的双层结构先对内部最小化问题做对偶变换,变成单层的最大化问题,再求解得到最恶劣场景以及对应的对偶乘子。第r轮迭代生成的C&CG割约束递推形式为:$$\eta \ge \sum_{i,t} \omega_i P_{i,t}^{curt, r} + \sum_{k,i,t} \lambda_{k,i,t}^r \cdot (z_{k,i} - z_{k,i}^r)$$这个割约束的意义在于:当前的主问题解如果导致某个场景下失负荷成本过高,就把这个成本对应关系强行"压"回主问题,迫使下一轮预布局方案调整。4.3 C&CG迭代收敛流程整个算法流程可以表述为:初始化:给定一组初始故障场景,置迭代计数为0;求解主问题,得到预布局方案$z^*$和失负荷成本下界LB;将$z^*$代入子问题,求解得到最恶劣场景和对应失负荷成本,更新上界UB;如果UB与LB的相对间隙小于设定阈值,停止迭代;否则将最恶劣场景加入主问题,生成新的割约束,返回第2步。收敛判据我一般取相对间隙小于0.1%或者迭代次数达到20次。实际复现时发现,前几轮gap收敛很快,到后期速度明显变慢,典型的下降曲线是指数衰减特征的近似。一个加速小技巧是每轮求解子问题后,把找到的最恶劣场景先检查是否已经在主问题场景列表中,若存在说明已经收敛,可以提前跳出循环,省掉后面的冗余迭代。5. Matlab复现实操:代码骨架与关键节点5.1 代码模块怎么划分一份能反复复现的项目代码,建议按模块拆分成统一风格的脚本和函数,我在某跨平台系统的复现版本里用的目录结构是这样的:数据文件夹:存放IEEE33节点的母线、支路参数表,以及生成的故障场景矩阵;模型函数:用Yalmip定义变量、目标函数和各组约束,每个约束函数独立实现;求解脚本:一个主脚本负责组装模型、调用求解器、调度C&CG迭代;结果可视化:画出预布局位置、储能运行轨迹、负荷恢复曲线、电压分布。模块独立最大的好处是调试方便。比如DistFlow约束有问题,直接单独跑约束函数,断点检查中间变量,不用在整个模型里找。5.2 IEEE33节点数据准备IEEE33节点系统是配电网优化领域的标准测试算例,33个节点、32条支路,基准电压12.66kV,总有功负荷3715kW,总无功负荷2300kvar,网络中含5条联络开关支路。数据准备阶段重点做三件事:第一,把母线、支路参数表读入成结构化数组。原始算例数据格式很乱,为了方便Yalmip索引,我习惯把母线节点编号重排成1到33的连续编号,支路统一按起点-终点方向存储,注意保持所有支路的方向一致。第二,根据总负荷按比例分配给各节点,得到每个节点的有功和无功负荷向量。这个分配比例在不同文献里可能不一样,但只要保证总数一致、权重设置合理,结果有可比性即可。第三,生成故障场景矩阵。我用的是蒙特卡洛抽样:假设每条线路的故障概率独立,比如0.3,每次抽样得到一组故障支路集合,重复抽样生成50个场景。场景太多则抽样时间太长,场景太少则覆盖不全,我建议先跑20个场景验证代码正确性,再逐步加到50到100个做正式实验。5.3 用Yalmip写约束的实战要点Yalmip是Matlab下最顺手的建模工具,配合Gurobi用起来很顺手。下面的代码片段是我复现DistFlow电压约束时的写法,注意用矩阵运算代替for循环:% 定义变量 V = sdpvar(nb, nt, 'full'); % 节点电压幅值平方 Pij = sdpvar(nl, nt, 'full'); % 支路有功 Qij = sdpvar(nl, nt, 'full'); % 支路无功 % DistFlow 电压约束 Constraints = []; for t = 1:nt for l = 1:nl i = branch(l, 1); j = branch(l, 2); Constraints = [Constraints, V(j,t) == V(i,t) - 2*(r(l)*Pij(l,t) + x(l)*Qij(l,t))]; end end这套代码直观、易读,但大规模场景下循环速度慢。优化做法是用稀疏矩阵把支路首末端节点映射成关联矩阵,一次性拼出向量化的DistFlow约束,速度能提升5到10倍,代码更容易出bug,建议先跑通再优化。调用求解器的写法很固定:ops = sdpsettings('solver', 'gurobi', 'gurobi.MIPGap', 0.001, 'verbose', 2); optimize(Constraints, Objective, ops);这个MIPGap参数我实际用下来定在0.1%比较合理。太严了求解时间成倍增加,0.001的gap已经能保证结果在工程上有足够精度。5.4 结果可视化与分析复现完成之后,最有价值的三个图建议一定画出来。第一个是预布局位置图,在IEEE33节点网络拓扑图上把每台移动储能的初始部署位置标出来,直观看出算法倾向于把储能车放到哪些负荷权重高、离变电站远的末端节点。第二个是储能动态调度轨迹图,横轴是时间,纵轴是节点编号,把每台储能车的时空轨迹画成折线。这个图能帮你验证时空耦合约束是否真的生效——正常情况下储能车的移动路径应该是相邻节点逐步移动的,如果出现跨节点瞬移,基本可以确定位置约束写错了。第三个是负荷恢复率曲线,横轴时间是调度周期,纵轴是恢复负荷占总负荷的比例,曲线越高越好。这个图是论文中的核心结果,也是验证算法有效性的关键指标。我复现后跑出的典型结果是:使用两台移动储能,在台风工况下比无储能场景的失负荷成本降低40%左右,比固定储能(储能位置固定在变电站旁)降低15%左右。这个数值不一定和你的场景完全一致,但量级关系能说明移动储能的时空可调度价值。6. 复现踩坑记录与效率优化6.1 常见问题速查表我在复现过程中整理了一张踩坑速查表,基本覆盖了新手最容易卡住的地方:症状根因解决办法电压约束大量越界标幺值基准没统一检查基准电压12.66kV与基准功率10MVA的换算关系储能位置变量出现瞬移出发/到达约束不完整增加$s_{k,i,j,t} \le p_{k,i,t-1}$形式的时序约束求解时间过长无法收敛场景数量过大或MILP变量爆炸固定储能位置先跑连续松弛,再启用二进制变量子问题无法对偶内部有二进制变量耦合将子问题中的调度变量约束写成线性形式,或者利用大M法拆解结果振荡不收敛C&CG割约束推导错误检查对偶变量与主问题变量的对应关系,建议用穷举小算例验证Yalmip报"solver not found"Gurobi路径未配置用yalmiptest命令检查求解器识别情况,添加license路径储能同时充放电缺少状态互斥约束增加充放电状态变量及$u^{ch} + u^{dis} \le 1$约束6.2 求解效率优化的四个实战技巧模型规模放大之后,求解时间会从秒级涨到分钟级甚至小时级,这时候优化求解效率是必做的功课。第一个技巧是热启动。把上一轮C&CG迭代的预布局变量值作为下一轮主问题初始解传入,能显著减少分支定界树的搜索空间。Yalmip里用assign和optimize的warmstart参数即可实现。第二个技巧是变量降维。预布局阶段候选节点往往会给出几十个,但实际只有两三台储能,理论上只需要枚举有限的组合。可以用约束老实枚举,也可以先对负荷重要性排序,筛掉明显不可能最优的候选节点,把问题规模缩小。第三个技巧是场景缩减。50个场景全跑慢得让人崩溃,用K-medoids聚类或者概率距离法把场景缩到10个左右,失负荷成本的偏差通常能控制在3%以内,求解时间却可以缩短到原来的十分之一。第四个技巧是松弛试探。第一轮先解储能不能移动的版本,也就是把动态调度退化为固定储能,用这个结果作为移动储能模型下界的估计。这样能提前判断当前参数设置下算法是否合理,避免错误参数下白跑几小时。我个人在实际操作中的体会是,这套移动储能预布局与动态调度策略最花时间的不是数学推导,而是把每一个环节的状态变量和索引理清楚。预布局阶段看似简单,实际决策时宁可把储能放在负荷密度高且和关键负荷处在同一馈线群的节点,也不要只看线路故障概率而忽略了网络拓扑的连通关系。最后再分享一个小技巧:调试时把Yalmip的showprogress选项打开,每次C&CG迭代都能看到上下界的变化趋势,一旦gap不再下降,问题多半出在割约束生成或者子问题对偶推导上,排查起来能省下大量的时间。这套代码跑通之后,换到更大的配电网算例甚至接入实际网络数据,核心框架都不需要大改,这是我认为这个方向最值得投入时间的原因。
每年台风季,调度屏上总有那么几个节点让我觉得特别无力。线路跳闸不可怕,可怕的是关键负荷跟着一起躺平——医院、泵站、通信基站一旦断电,抢修压力直接翻倍。这时候如果有一台能"跑"的储能车,提前停在预测的关键位置,故障后又可以根据现场情况挪位置、调出力,整个恢复过程会从容很多。这正是"面向配电网韧性提升的移动储能预布局与动态调度策略"要解决的问题。这篇文章以IEEE33节点系统为例,完整拆解这套策略的建模思路、求解过程和Matlab复现时的关键细节。适合正在做配电网韧性、分布式储能优化调度的研究生,以及想用标准算例验证算法效果的工程师。1. 问题本质:为什么韧性提升要依赖移动储能1.1 配电网韧性,跟传统可靠性是两码事很多刚接触这个方向的人会把韧性和可靠性混在一起,这是第一个容易踩的坑。传统可靠性研究的是日常运行中的随机故障,指标大多是平均停电时间、平均停电频率这类长期统计量,关注的是"大部分时间能不能稳定供电"。而韧性研究的对象是极端事件,比如台风、冰冻、暴雨等小概率大冲击场景,关注的是"扛不扛得住、恢复得快不快"。用一条曲线来理解韧性最直观:横轴是时间,纵轴是系统性能。事件发生前系统处于正常水平,事件冲击瞬间性能陡降,随后是持续一段时间的性能低谷,直到抢修完成、负荷全部恢复,曲线回到正常水平。韧性提升要做的事情,就是让曲线往三个方向走——让下降幅度更小、让低谷期更短、让恢复过程更平滑。这里面每一段都有文章可做,移动储能主要作用在"低谷期供电"和"恢复期支撑"这两个阶段。极端事件还有一个特点让优化问题变得棘手:故障信息不确定。台风过境前你只知道可能有几条线路要断,但具体是哪几条、断多久,根本没法提前确定。这就决定了决策必须分阶段做,灾前先拍板一部分动作,灾后再根据实际情况做调整。1.2 移动储能凭什么能派上用场固定储能装在一个地方,只能服务周边负荷,一旦所在馈线故障,储能再能打也被困在孤岛里。移动储能不一样,它本质上是"储能+运输载体"的组合。以某跨平台系统的实际配置为例,一台移动储能车通常有几百千瓦的额定功率、上千千瓦时的容量,可以在几小时内从一个节点转移到另一个节点。这意味着三件事:灾前可以预布局,把储能车提前开到风险区域的薄弱节点,避免故障后想用用不上;灾中可以根据实际故障情况重新部署,哪条馈线断了就开过去支援,灵活度远高于固定储能;同一个时段储能车在哪里,决定了它能给哪些负荷供电,这种时空耦合特性使得优化问题的维度和难度都上了一个台阶。你可能会问,配电网里不是还有网络重构、应急发电车这些手段吗?应急发电车和移动储能的区别在于,发电车烧油、响应快,但成本和噪音排放是问题;移动储能静音、零排放、能量可双向流动,还能在故障前后反复调度,策略上更灵活。网络重构只改变拓扑,不解决电源不足的问题,移动储能则是实打实地给孤岛提供一个可控电源。1.3 为什么必须"预布局+动态调度"两步走如果故障场景完全已知,那就是一个确定性的部署优化问题,把储能车放到最优位置就结束了。但实际灾害场景做不到这一点,所以决策必须拆成两个阶段。第一阶段叫预布局,发生在灾前。这时候只有灾害预测信息,比如哪些线路故障概率高、哪些负荷重要等级高,决策内容是每台移动储能初始放在哪个节点。第二阶段叫动态调度,发生在灾后。故障场景逐渐明朗,决策内容包括储能什么时候从哪个节点搬到哪个节点、在每个节点的充放电功率、哪些负荷可以恢复、网络怎么重构。用一个生活类比就很好理解:台风登陆前,救援队会把救生艇提前部署到地势低洼的社区,这是预布局。台风过后,根据哪片区域实际积水最严重,再把救生艇调过去,这是动态调度。两阶段决策不是多此一举,而是极端事件本身的不确定性逼出来的必然结构。2. 数学模型:两层协同优化框架怎么搭2.1 目标函数:失负荷成本最小化是核心整套模型的目标函数,中心项是系统失负荷成本最小化。但这里有个细节需要注意——不同负荷的重要程度完全不一样,医院手术室的负荷和普通商业负荷不能混为一谈。所以目标函数里要对每个节点设置权重系数,一级负荷权重最高,三级负荷权重最低。我复现时建议把权重直接乘在失负荷量上,形式大概是这样:$$\min \sum_{t \in T} \sum_{i \in N} \omega_i \cdot P_{i,t}^{curt} \cdot \Delta t + \sum_{t \in T} \sum_{k \in K} \left( c^{op} \cdot P_{k,t} + c^{mv} \cdot M_{k,t} \right)$$其中第一项是失负荷惩罚成本,反映韧性水平,量纲注意要和第二项统一;第二项是移动储能运行成本,包括充放电运维成本和移动成本系数乘上移动标志变量。这一项看起来不大,但加进去之后储能不会毫无意义地到处乱跑,模型行为更符合实际调度习惯。2.2 预布局约束:第一阶段决策变量预布局阶段的决策变量是最简单的,每个移动储能是否放置在各个候选节点,用0-1变量表示。约束也很直接:任意一台移动储能只能选择一个预布局位置;所有储能数量不超过可用车队规模;每个节点的接入容量有上限,不能把两台大容量储能堆在同一个节点浪费资源。严格来说,第一阶段还应该考虑预布局位置是否接入配电网、是否具备并网条件。在IEEE33节点系统里,多数节点都具备接入条件,但复现时建议把候选节点集合限定一下,比如只允许接入负荷节点,剔除纯联络节点,这样结果更可信。2.3 动态调度约束:第二阶段决策变量动态调度是模型里最复杂的部分,核心是移动储能的时间-空间-能量三重耦合。空间维度上,需要定义储能的位置状态变量,表示某时刻某储能是否在某个节点。每个时段的约束是:一台储能要么停在一个节点,要么在路上移动,二者必居其一。这个逻辑看着简单,写错位置约束会导致很离谱的结果——比如储能上一时段还在节点A,下一时段没动却出现在节点B。时间维度上,储能从节点i移动到节点j需要花费一定的时间,这个时间折算成时段数τ。出发约束要求它在移动前必须待在出发节点,到达约束要求它在移动完成后才能接入新节点。简化处理时可以认为所有节点间移动耗时均为一个时段,但更严谨的做法是引入节点间距离矩阵,再除以移动速度换算成时段数。能量维度上,就是经典的储能运行约束:充放电功率不能同时为正,需要引入充放电状态0-1变量;各时段荷电状态满足递推关系:$SOC_{t+1} = SOC_t + \eta_{ch} P_t^{ch} \Delta t - P_t^{dis} \Delta t / \eta_{dis}$;SOC有上下限,一般取0.1到0.9;调度周期末可以加一个SOC恢复约束,让储能回到合理荷电水平,方便下一轮调度。2.4 DistFlow潮流与配电网络约束配电网潮流约束,我用的是线性化DistFlow模型,也就是Branch Flow Model的线性形式。对每条支路$(i,j)$,核心方程有两条:电压关系:$V_j = V_i - 2(r_{ij}P_{ij} + x_{ij}Q_{ij})$;功率平衡:节点注入功率等于流出支路功率之和加上本地负荷消耗,再减去储能充放电功率。这里有一个细节我吃过大亏:IEEE33节点系统基准电压是12.66kV,但计算时全部用标幺值。很多复现结果对不上,就是因为母线电压的基准值没统一,电压约束0.95到1.05的上下限范围写得完全不合逻辑。故障线路的处理方式比较简单——故障支路的通断状态置0,该支路从网络中移除。如果允许主动孤岛运行,还需要额外处理孤岛内的电压和频率约束;纯线性DistFlow框架下一般都假设孤岛能稳定运行,只做功率平衡约束。3. 核心难点:不确定性建模与模型线性化3.1 场景法还是鲁棒优化,怎么选灾害不确定性怎么放进模型,是整个复现最核心的决策点。主流的做法有两类。第一类是场景法,也叫随机规划。根据历史台风路径和线路故障概率,用蒙特卡洛抽样生成若干组故障场景,每组场景包含一组断线集合。然后把目标函数写成所有场景失负荷成本的期望,调度变量做成随场景变化的。优点是模型直观、好理解,用确定性求解器就能解;缺点是场景数量少了怕覆盖不全,数量多了求解时间爆炸。第二类是两阶段鲁棒优化。第一阶段预布局在不确定实现前决策,第二阶段动态调度在不清楚集合中找到最恶劣场景下的应对。不确定性集合通常用基数约束描述,比如"最多同时断开3条线路"。鲁棒解不依赖具体场景概率,适合概率信息缺失的情况,但求解需要列约束生成(C&CG)迭代,代码复杂度明显更高。我的复现方案采用两阶段框架,预布局固定,动态调度用一个场景集合描述灾害可能性,场景集按概率权重参与目标函数。代码里留了一个切换开关,可以改成严格的两阶段鲁棒加C&CG,后面详讲。3.2 移动储能时空耦合约束的具体写法这是最容易被代码坑住的地方。我用二进制变量$p_{k,i,t}$表示储能k在时段t是否位于节点i,$s_{k,i,j,t}$表示储能k在时段t是否正在从i向j移动。每个时段的状态唯一性约束:$$\sum_{i} p_{k,i,t} + \sum_{i}\sum_{j \neq i} s_{k,i,j,t} = 1, \quad \forall k, t$$出发约束:如果$s_{k,i,j,t}=1$,意味着上一时段储能必须在节点i,表达式为:$$s_{k,i,j,t} \le p_{k,i,t-1}, \quad \forall k, i, j, t$$移动耗时的处理:如果节点i到节点j需要τ个时段,则储能到达j的时刻最早是$t+\tau$。为了简化实现,很多复现直接令τ=1,即从一个节点到另一个节点恰好占用一个时段。如果想做精确的行驶时间建模,可以增加一个出发标志变量$d_{k,i,j,t}$,表示在时段t开始从i出发,经过τ个时段后置位相应的$p_{k,j,t+\tau}$。这个写法更严谨,但约束和变量数量会显著增加。我在实际调试中发现,直接暴力展开所有i、j组合会让变量爆炸式增长,33节点下$33 \times 32$的移动变量矩阵在24个时段里要建几万个二进制变量。加速的技巧是先把节点间距离矩阵算好,过滤掉距离过远的节点对,或者只允许储能沿连通路径移动,别让它在所有节点对之间自由乱跑。3.3 保证辐射状拓扑的约束配电网正常运行时候是辐射状结构,故障后可以通过联络开关重构,但结构仍然是辐射状。这个"无环"约束有两种常见建模方法。一种是用生成树约束,借助单商品流思想:变电站节点作为根节点,从根向每个负荷节点发一个单位的虚拟流,每个节点只能有一个父节点,且父节点只能来自电气连通方向。这个约束在MILP里是天然线性的,写法和网络流约束完全一致。另一种相对暴力的方式是列出一组破坏约束,把系统中所有可能形成环路的支路子集枚举出来,要求每个环路中至少断开一条支路。但IEEE33节点的环路数量不少,枚举麻烦且约束数量庞大,我建议用单商品流,通用性更好。线性化的问题同样值得说。完整的DistFlow方程里含有电流平方项与电压乘积项,是非凸的,MILP没法直接处理。复现方案直接采用单商品流约束+线性化DistFlow,忽略$l_{ij}$高阶项,在10MVA基准功率下误差可以控制在工程可接受范围内。如果想更精确,可以用二阶锥松弛,把不等式写成凸约束,配合商用求解器也能解,但求解时间几乎翻倍。4. 求解策略:C&CG两阶段鲁棒求解全过程4.1 为什么用两阶段鲁棒而不是一把梭直接把两阶段模型当成一个大的混合整数规划丢给求解器,理论上是可行的,但实际根本算不动。原因在于第二阶段动态调度变量要依赖场景变化,不等于是为每个场景复制一份同样的变量。一旦场景数量上到几十个,变量和约束会膨胀到几十万甚至上百万级别,MILP求解器的分支定界树几乎没有收敛的可能。两阶段鲁棒的核心思路是"先粗后细":第一阶段只做少量全局决策,第二阶段针对最恶劣场景做精细应对。C&CG算法把这个思路落到迭代框架里,每一轮只增加最关键的约束,而不是把所有场景一次性塞进模型。4.2 主问题与子问题的拆解用C&CG求解两阶段鲁棒优化,需要把问题拆成主问题和子问题。主问题对应预布局加动态调度约束,决策变量包括储能预布局位置$z_{k,i}$、动态调度变量$x_t$,以及一个替代最恶劣场景失负荷成本的辅助变量$\eta$。在第$m$轮迭代中,主问题里已经有前$m-1$轮生成的最恶劣场景对应的调度约束和割约束。求解主问题得到当前最优预布局方案和失负荷成本下界。子问题则固定主问题给出的预布局位置$z^*$,在最不确定集合中找一个故障场景,使得该场景下的失负荷成本最大化。这个"max-min"的双层结构先对内部最小化问题做对偶变换,变成单层的最大化问题,再求解得到最恶劣场景以及对应的对偶乘子。第r轮迭代生成的C&CG割约束递推形式为:$$\eta \ge \sum_{i,t} \omega_i P_{i,t}^{curt, r} + \sum_{k,i,t} \lambda_{k,i,t}^r \cdot (z_{k,i} - z_{k,i}^r)$$这个割约束的意义在于:当前的主问题解如果导致某个场景下失负荷成本过高,就把这个成本对应关系强行"压"回主问题,迫使下一轮预布局方案调整。4.3 C&CG迭代收敛流程整个算法流程可以表述为:初始化:给定一组初始故障场景,置迭代计数为0;求解主问题,得到预布局方案$z^*$和失负荷成本下界LB;将$z^*$代入子问题,求解得到最恶劣场景和对应失负荷成本,更新上界UB;如果UB与LB的相对间隙小于设定阈值,停止迭代;否则将最恶劣场景加入主问题,生成新的割约束,返回第2步。收敛判据我一般取相对间隙小于0.1%或者迭代次数达到20次。实际复现时发现,前几轮gap收敛很快,到后期速度明显变慢,典型的下降曲线是指数衰减特征的近似。一个加速小技巧是每轮求解子问题后,把找到的最恶劣场景先检查是否已经在主问题场景列表中,若存在说明已经收敛,可以提前跳出循环,省掉后面的冗余迭代。5. Matlab复现实操:代码骨架与关键节点5.1 代码模块怎么划分一份能反复复现的项目代码,建议按模块拆分成统一风格的脚本和函数,我在某跨平台系统的复现版本里用的目录结构是这样的:数据文件夹:存放IEEE33节点的母线、支路参数表,以及生成的故障场景矩阵;模型函数:用Yalmip定义变量、目标函数和各组约束,每个约束函数独立实现;求解脚本:一个主脚本负责组装模型、调用求解器、调度C&CG迭代;结果可视化:画出预布局位置、储能运行轨迹、负荷恢复曲线、电压分布。模块独立最大的好处是调试方便。比如DistFlow约束有问题,直接单独跑约束函数,断点检查中间变量,不用在整个模型里找。5.2 IEEE33节点数据准备IEEE33节点系统是配电网优化领域的标准测试算例,33个节点、32条支路,基准电压12.66kV,总有功负荷3715kW,总无功负荷2300kvar,网络中含5条联络开关支路。数据准备阶段重点做三件事:第一,把母线、支路参数表读入成结构化数组。原始算例数据格式很乱,为了方便Yalmip索引,我习惯把母线节点编号重排成1到33的连续编号,支路统一按起点-终点方向存储,注意保持所有支路的方向一致。第二,根据总负荷按比例分配给各节点,得到每个节点的有功和无功负荷向量。这个分配比例在不同文献里可能不一样,但只要保证总数一致、权重设置合理,结果有可比性即可。第三,生成故障场景矩阵。我用的是蒙特卡洛抽样:假设每条线路的故障概率独立,比如0.3,每次抽样得到一组故障支路集合,重复抽样生成50个场景。场景太多则抽样时间太长,场景太少则覆盖不全,我建议先跑20个场景验证代码正确性,再逐步加到50到100个做正式实验。5.3 用Yalmip写约束的实战要点Yalmip是Matlab下最顺手的建模工具,配合Gurobi用起来很顺手。下面的代码片段是我复现DistFlow电压约束时的写法,注意用矩阵运算代替for循环:% 定义变量 V = sdpvar(nb, nt, 'full'); % 节点电压幅值平方 Pij = sdpvar(nl, nt, 'full'); % 支路有功 Qij = sdpvar(nl, nt, 'full'); % 支路无功 % DistFlow 电压约束 Constraints = []; for t = 1:nt for l = 1:nl i = branch(l, 1); j = branch(l, 2); Constraints = [Constraints, V(j,t) == V(i,t) - 2*(r(l)*Pij(l,t) + x(l)*Qij(l,t))]; end end这套代码直观、易读,但大规模场景下循环速度慢。优化做法是用稀疏矩阵把支路首末端节点映射成关联矩阵,一次性拼出向量化的DistFlow约束,速度能提升5到10倍,代码更容易出bug,建议先跑通再优化。调用求解器的写法很固定:ops = sdpsettings('solver', 'gurobi', 'gurobi.MIPGap', 0.001, 'verbose', 2); optimize(Constraints, Objective, ops);这个MIPGap参数我实际用下来定在0.1%比较合理。太严了求解时间成倍增加,0.001的gap已经能保证结果在工程上有足够精度。5.4 结果可视化与分析复现完成之后,最有价值的三个图建议一定画出来。第一个是预布局位置图,在IEEE33节点网络拓扑图上把每台移动储能的初始部署位置标出来,直观看出算法倾向于把储能车放到哪些负荷权重高、离变电站远的末端节点。第二个是储能动态调度轨迹图,横轴是时间,纵轴是节点编号,把每台储能车的时空轨迹画成折线。这个图能帮你验证时空耦合约束是否真的生效——正常情况下储能车的移动路径应该是相邻节点逐步移动的,如果出现跨节点瞬移,基本可以确定位置约束写错了。第三个是负荷恢复率曲线,横轴时间是调度周期,纵轴是恢复负荷占总负荷的比例,曲线越高越好。这个图是论文中的核心结果,也是验证算法有效性的关键指标。我复现后跑出的典型结果是:使用两台移动储能,在台风工况下比无储能场景的失负荷成本降低40%左右,比固定储能(储能位置固定在变电站旁)降低15%左右。这个数值不一定和你的场景完全一致,但量级关系能说明移动储能的时空可调度价值。6. 复现踩坑记录与效率优化6.1 常见问题速查表我在复现过程中整理了一张踩坑速查表,基本覆盖了新手最容易卡住的地方:症状根因解决办法电压约束大量越界标幺值基准没统一检查基准电压12.66kV与基准功率10MVA的换算关系储能位置变量出现瞬移出发/到达约束不完整增加$s_{k,i,j,t} \le p_{k,i,t-1}$形式的时序约束求解时间过长无法收敛场景数量过大或MILP变量爆炸固定储能位置先跑连续松弛,再启用二进制变量子问题无法对偶内部有二进制变量耦合将子问题中的调度变量约束写成线性形式,或者利用大M法拆解结果振荡不收敛C&CG割约束推导错误检查对偶变量与主问题变量的对应关系,建议用穷举小算例验证Yalmip报"solver not found"Gurobi路径未配置用yalmiptest命令检查求解器识别情况,添加license路径储能同时充放电缺少状态互斥约束增加充放电状态变量及$u^{ch} + u^{dis} \le 1$约束6.2 求解效率优化的四个实战技巧模型规模放大之后,求解时间会从秒级涨到分钟级甚至小时级,这时候优化求解效率是必做的功课。第一个技巧是热启动。把上一轮C&CG迭代的预布局变量值作为下一轮主问题初始解传入,能显著减少分支定界树的搜索空间。Yalmip里用assign和optimize的warmstart参数即可实现。第二个技巧是变量降维。预布局阶段候选节点往往会给出几十个,但实际只有两三台储能,理论上只需要枚举有限的组合。可以用约束老实枚举,也可以先对负荷重要性排序,筛掉明显不可能最优的候选节点,把问题规模缩小。第三个技巧是场景缩减。50个场景全跑慢得让人崩溃,用K-medoids聚类或者概率距离法把场景缩到10个左右,失负荷成本的偏差通常能控制在3%以内,求解时间却可以缩短到原来的十分之一。第四个技巧是松弛试探。第一轮先解储能不能移动的版本,也就是把动态调度退化为固定储能,用这个结果作为移动储能模型下界的估计。这样能提前判断当前参数设置下算法是否合理,避免错误参数下白跑几小时。我个人在实际操作中的体会是,这套移动储能预布局与动态调度策略最花时间的不是数学推导,而是把每一个环节的状态变量和索引理清楚。预布局阶段看似简单,实际决策时宁可把储能放在负荷密度高且和关键负荷处在同一馈线群的节点,也不要只看线路故障概率而忽略了网络拓扑的连通关系。最后再分享一个小技巧:调试时把Yalmip的showprogress选项打开,每次C&CG迭代都能看到上下界的变化趋势,一旦gap不再下降,问题多半出在割约束生成或者子问题对偶推导上,排查起来能省下大量的时间。这套代码跑通之后,换到更大的配电网算例甚至接入实际网络数据,核心框架都不需要大改,这是我认为这个方向最值得投入时间的原因。