前阵子复现了一篇关于配电网韧性提升的中文论文,标题就是“面向配电网韧性提升的移动储能预布局与动态调度策略”,系统用的是IEEE33节点,求解工具是Matlab+YALMIP。整个过程走下来,最深的感受是:这个课题听起来高大上,但核心逻辑其实非常清晰——移动储能(MESS)怎么在灾前放、灾后动,才能最大程度保住重要负荷。这篇复现笔记不打算逐字逐句讲代码,而是把“为什么要这样做”“模型怎么建”“代码怎么组织”“哪些地方容易卡住”讲透。不管你是准备拿这个方向做毕业设计、发小论文,还是单纯想跑通一个韧性提升的算例,这篇文章应该都能帮你省下不少时间。
1. 这个课题到底在解决什么问题:一场台风暴露出的配电网短板
1.1 韧性与可靠性:两种完全不同的“停电事件”
传统配电网规划里,我们讲得最多的是可靠性。可靠性的经典场景是N-1校验——任意一条线路退出运行,系统还能保证供电。这套思路应对的是“大概率、小影响”的故障,比如某条馈线被施工挖断、某个变压器过载跳闸。
但极端自然灾害(台风、冰灾、山火)完全是另一回事。一次强台风可以在十几分钟内让十几条线路同时跳闸,配电网可能被割裂成好几个孤岛,甚至完全失去上级电源。这种“小概率、大影响”事件,就是韧性(Resilience)要对付的。学术一点的表述是:韧性是系统对极端事件的抵御、吸收、适应和恢复能力。
两者的差异直接影响建模思路。可靠性分析通常假设故障后主网还能通过联络开关转供,而韧性分析必须考虑配电网可能完全“孤岛化”——外部的电进不来,只能靠内部资源自救。这就是移动储能登场的背景。
1.2 移动储能相比固定储能的三个核心优势
固定储能(电池舱、集装箱)确实能提升配电网韧性,但它的位置一旦固定,面对不确定性极高的极端事件就很被动。假设台风导致节点18附近的线路全断,而固定储能刚好放在节点5附近,那它只能对节点5周边的小范围供电,对远端负荷无能为力。
移动储能(Mobile Energy Storage System,MESS)本质是一辆装载电池组和变流器的卡车/拖车,具备三个固定储能没有的优势:
- 灾前可预置:天气预报一旦发出极端事件预警,MESS可以提前移动到预测的故障高发区域附近,而不是被动等在那里。
- 灾后可转移:故障信息确定后,MESS可以按小时级的时间尺度从一个孤岛移动到另一个孤岛,实现“移动电源车逐岛供电”。
- 一车多用:正常运行期MESS还可以参与削峰填谷、调频辅助服务,相当于把韧性投资变成了一笔可以产生日常收益的资产。
但注意,移动储能的优势是有代价的——移动期间不能放电。如果一辆MESS从节点5移动到节点18需要花掉一个完整时间断面,那这一个小时内它就等于“消失”了。这个约束必须在模型里显式表达,否则优化结果看起来很美,实际根本不可行。
1.3 预布局和动态调度为什么要放到同一个框架里
很多初学者会把这个问题拆成两个独立的问题:灾前单独做一次选址定容,灾后再做一次调度。但这样处理有一个致命缺陷——灾前选址决策必须考虑灾后调度的可行性。
打个比方:如果预布局阶段把两台MESS都放在了节点1附近(离变电站最近),结果台风过后最需要供电的其实是远端孤岛,那灾后调度时两台MESS都要花好几个小时赶路,这期间重要负荷只能断电。反过来,如果预布局阶段把MESS放在网络中心区域,无论哪个方向出现孤岛,转移距离都相对可控。
换句话说,预布局与动态调度是两个耦合的决策阶段:第一阶段决定MESS的初始位置,第二阶段在故障场景实现后决定MESS的移动路径、接入节点和充放电功率。只有把两阶段放进同一个优化框架(要么是两阶段随机优化,要么是两阶段鲁棒优化),才能得到一个真正可用的韧性提升策略。
在代码实现层面,这也意味着我们不能先跑一个选址程序、再跑一个调度程序,而要用带“场景/不确定集合”的联合优化模型去做。YALMIP可以很好地表达这种模型,但需要理解两阶段优化的求解逻辑——这一点在第3章详细展开。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 完整数学模型:从物理过程到每一条约束的来龙去脉
2.1 目标函数:加权负荷恢复量怎么设置权重
这个课题的标准目标函数是最大化极端事件期间的总加权负荷恢复量,本质是尽量多、尽量快地恢复重要负荷。
常见的写法是:
code复制max Σ_{t∈T} Σ_{i∈N} w_i · x_i(t) · P_i^load(t) · Δt
其中:
w_i:节点i的负荷权重,反映重要程度;x_i(t):0-1变量,t时段节点i是否恢复供电;P_i^load(t):节点i的有功负荷;Δt:单位时段长度(通常取1小时)。
权重如何设置非常关键。我复现的版本里,一级负荷(医院、通信基站、应急指挥中心)权重取100,二级负荷(学校、商场)取10,三级负荷(普通居民)取1。这样优化器会优先保证一级负荷的供电,即便它只有几十千瓦,也比恢复一个几千瓦的三级负荷更“划算”。
有时候论文还会在目标函数里加上“恢复时间惩罚项”,让系统尽量早点恢复供电——因为同样恢复100kWh负荷,第2小时恢复和第20小时恢复的社会价值完全不同。如果原文没有明确,复现时可以先用纯加权负荷恢复量,逻辑简单也容易验证结果。
2.2 移动储能的时间-位置-电量耦合建模
MESS建模是整个问题最核心也最容易出错的部分。一个可靠的MESS模型必须同时刻画三个维度:在哪、跑没跑、还剩多少电。
第一部分是位置变量:
code复制z_{k,t}^i ∈ {0,1} 表示第k台MESS在t时段接入节点i
Σ_{i∈N} z_{k,t}^i = 1 // 同一时刻只能接入一个节点
注意这里用“接入”而不是“在”。MESS移动过程中没有接入任何节点,所以严格来说还应该有一个移动状态变量。简化处理时,可以让移动过程占用1个完整时段,这个时段内没有任何节点接入(即移动时段所有z_{k,t}^i=0但总量约束不成立)。不过很多复现版本为了减少整数变量,用Σ_i z_{k,t}^i =1表示“在某处或正在移动途中”,用额外的move_{k,t}变量区分移动状态。
第二部分是电量变化:
code复制E_{k,t+1} = E_{k,t} + η_ch · P_ch_{k,t} · Δt − P_dis_{k,t} · Δt / η_dis
其中η_ch和η_dis是充放电效率,分别取0.95和0.95比较合理。同时要保证:
- 电量上下限:
SOC_min · E_cap ≤ E_{k,t} ≤ SOC_max · E_cap - 充放电功率上限:
0 ≤ P_ch ≤ P_max、0 ≤ P_dis ≤ P_max - 充放电不能同时进行:
P_ch · P_dis = 0,在MILP里用附加二进制变量加big-M约束实现。
第三部分是移动约束。如果第k台MESS在t时段从节点i移动到节点j,至少需要d_{i,j}个时段才能到达。常见建模方式是定义可达域:只有距离小于单位时段内最大移动距离的节点之间才允许“连续接入”。更精确的做法是把移动变量m_{k,t}^{i,j}纳入约束,并强制移动期间MESS不充不放。
预布局与动态调度的耦合体现在初始位置约束:
code复制z_{k,1}^i = β_k^i
即第一阶段的预布局结果β直接决定了第二阶段第一个时段的接入位置。这个约束看似简单,却是两阶段问题耦合的关键。
2.3 配电网潮流与孤岛连通性约束
配电网恢复问题里的潮流通常用DistFlow分支潮流方程建模,而不是完整交流潮流。IEEE33节点系统是辐射状配电网,DistFlow足够精确且便于线性化:
code复制P_{ij,t} = Σ_{k:(j,k)∈E} P_{jk,t} + P_j^load · (1 − x_j(t)) − P_j^gen(t)
Q_{ij,t} = Σ_{k:(j,k)∈E} Q_{jk,t} + Q_j^load · (1 − x_j(t)) − Q_j^gen(t)
V_{j,t}^2 = V_{i,t}^2 − 2(r_{ij} P_{ij,t} + x_{ij} Q_{ij,t})
如果忽略网损(l_{ij}·(r_{ij}^2+x_{ij}^2)),上述方程是线性的,可以直接塞进MILP。对IEEE33这种小系统、中低负载率场景,忽略网损的误差通常在工程可接受范围内。但我建议复现时保留二阶项并用二阶锥松弛(SOCP)写出,这样更接近期刊论文的原始模型,YALMIP处理起来也只是多几行约束的事。
孤岛连通性约束是最让人头疼的部分。故障后配电网被分割成多个子图,我们必须保证每个被恢复的孤岛是连通且辐射状的,否则可能出现“节点恢复了供电但根本不在任何通电区域里”的荒谬结果。常用的数学工具是单商品流约束(Single Commodity Flow):给每个可能的孤岛假想一个虚拟源点,然后要求每个恢复节点的净注入流量恰好为1,且潮流只能从源点往负荷节点流,这样就能保证连通性。代码实现时通常要添加一组辅助变量和节点平衡约束,逻辑稍显繁琐,但却是题目能不能算下去的分水岭。
2.4 极端事件不确定性:鲁棒优化比随机优化更合适
极端事件的核心特征是什么?不知道哪条线路会断。
如果假设已知故障线路集合(比如论文给定“线路10-11和17-18同时断线”),那问题退化成确定性的灾后恢复优化,用一次求解就能完成。
但实际上台风路径、风速分布存在大量不确定性,更合理的做法是引入不确定集合描述线路故障。随机优化需要假设故障场景的概率分布,这在极端事件下往往缺乏历史数据支撑;而鲁棒优化只需要定义一个“可能会出问题”的线路集合范围(盒式不确定集合),然后在最坏情况下求最优解,对数据的要求低得多。
在两阶段鲁棒框架下,预布局阶段是“这里”决策(here-and-now),动态调度阶段是“等一等”决策(wait-and-see)。求解时要反复处理子问题的最大最小(max-min)结构,直到最坏场景下的恢复策略稳定为止。CCG(列与约束生成)算法是当前最主流的解法,第3章展开。
3. 求解框架:把两阶段决策落成一个可计算的MILP
3.1 两阶段问题拆成“预布局主问题+动态调度子问题”
两阶段鲁棒优化的标准形式是:
code复制min_β c(β) + max_{u∈U} min_{y∈F(β,u)} d(y)
其中:β是第一阶段的预布局变量,u是故障线路不确定变量,y是第二阶段调度变量。
C&CG算法把这个问题拆成主问题和子问题迭代求解:
- 主问题(Master Problem):已知若干条“最坏故障场景”,求最优的预布局方案,给出下界(LB);
- 子问题(Subproblem):固定预布局方案,求解“最坏故障场景下的最优响应”,给出上界(UB)。
判断收敛的标准是(UB − LB) / UB ≤ ε,通常取1%到3%。
这个机制类似“下棋”:你(主问题)先落子,对手(子问题)挑一个让你最难受的故障场景,你再根据这个场景调整预布局,如此反复,直到双方都无能为力。Matlab+YALMIP完全能实现这套迭代逻辑,核心代码量其实不大。
3.2 非线性项的线性化处理:big-M与二阶锥松弛
这个模型里有两类典型的非线性项需要处理:
第一类是乘积项。 比如充放电功率与接入状态的乘积,表示“只有接入节点才能放电”。处理方式是引入big-M约束。举个例子,如果P_j(t)是节点j在t时段的放电功率,z_j(t)是接入状态,那么:
code复制0 ≤ P_j(t) ≤ M · z_j(t)
M取一个足够大的数(比如该储能的最大功率乘2)。注意M不能取得太大,否则会引起数值病态,这一点在第5章踩坑环节详细说。
第二类是潮流非线性。 DistFlow方程中的V^2项、线路潮流与电压的乘积项,通过变量替换(令U_i = V_i^2)可以把大部分转为线性,剩余的非凸项用二阶锥松弛处理:
code复制l_{ij,t} ≥ (P_{ij,t}^2 + Q_{ij,t}^2) / U_{i,t}
这个约束在YALMIP里可以写成cone([P_ij; Q_ij; ...], ...)或者[P^2+Q^2 <= U·l],Gurobi/CPLEX均原生支持二阶锥约束,求解效率很高。
3.3 C&CG迭代:处理最坏故障场景的完整流程
C&CG子问题本身是一个三层结构:外层max故障场景,内层min调度目标。处理它的标准手段是强对偶——如果内层问题是线性规划,直接写出对偶问题,把“min”变成“max”,和外层“max”合并成一个单层的max问题,再交给求解器一次解决。
但现实中内层往往包含二进制变量(比如移动路径选择、孤岛接入决策),强对偶不再直接成立。此时的处理方式有两种:
- 固定整数变量后对连续部分取对偶,然后用KKT条件或大M法线性化互补条件——推导繁琐但严谨;
- 更省事的工程做法:把子问题做成MILP然后直接枚举有限个候选故障场景,用一个视作“外层max”的循环挑出最坏场景。虽然理论上的精确性弱一些,但复现论文结果够用。
我个人复现时用的就是第二种:先不追求理论上的“完全精确鲁棒最优”,而是把不确定集合离散成若干高概率故障场景(线路9-10断线、线路21-22断线、线路28-29断线等),用C&CG迭代挑出对恢复率影响最大的场景,再指导预布局。结果论文里的关键数据和曲线都能对上。
3.4 算例规模预估与求解器参数设置
以IEEE33节点、24小时、2台MESS为例,决策变量规模大概是:
| 变量类型 | 数量估算 | 说明 |
|---|---|---|
| 位置/接入变量 | 33 × 24 × 2 ≈ 1584 | 每台MESS每时段在每个节点是否接入 |
| 充放电变量 | 2 × 24 × 2 = 96 | 每台MESS每时段的充/放电功率 |
| 电量变量 | 2 × 25 = 50 | 每台MESS各时段电量 |
| 孤岛连通辅助变量 | 33 × 24 × 若干 | 取决于连通性约束写法 |
| 二阶锥约束 | 每条支路每时段 | 33节点约32条支路×24时段 |
这是个中小规模的MILP/SOCP问题。用YALMIP+Gurobi在普通笔记本上,设定MIPGap=0.01(即1%最优间隙),通常几分钟内能收敛。如果算到十几分钟还没结果,大概率是模型里有冗余约束或者big-M设置不当,不是求解器的问题。
YALMIP求解器设置的常用代码片段:
matlab复制options = sdpsettings('solver','gurobi','verbose',0, ...
'gurobi.MIPGap',0.01, ...
'gurobi.TimeLimit',600);
result = optimize(constraints, objective, options);
4. IEEE33节点系统与Matlab代码实现:一套最小可复现方案
4.1 IEEE33节点系统参数准备
IEEE33节点系统是配电网研究最经典的测试系统之一:基准电压12.66kV,基准功率10MVA,33个节点、32条支路,正常运行时由节点1(根节点,变电站母线)供电。总有功负荷约3.715MW,总无功负荷约2.3Mvar。系统有5条联络开关支路(8-21、9-15、12-22、18-33、25-29),正常断开,用于故障后的网络重构。
复现前先准备好以下数据文件:
- 支路表:起点、终点、电阻(Ω)、电抗(Ω);
- 节点负荷表:各节点有功(kW)、无功(kvar);
- 联络开关表:5条联络支路的端点。
Matpower里有现成的case33bw.m数据文件,可以直接load进来,免去手敲数据的麻烦:
matlab复制mpc = loadcase('case33bw');
branch = mpc.branch;
bus = mpc.bus;
需要注意的是:Matpower的支路数据是标幺值,需要自己转换成有名值(乘以基准阻抗Z_base = 12.66²/10 = 16.02Ω),或者干脆全程用标幺值建模。我建议统一使用标幺值,数值范围接近1,求解器收敛性和数值稳定性最好。
4.2 代码结构设计:怎么组织一个复现项目
复现论文的代码最忌讳把几千行全塞进一个main.m。一旦模型改个参数或换套故障场景,调试成本会爆炸。我使用的目录结构是:
text复制mess_resilience/
├── main.m # 主程序:设置算例、调用求解、输出结果
├── data/
│ ├── ieee33_load.m # 节点负荷数据
│ ├── ieee33_branch.m # 支路阻抗与拓扑
│ └── mess_config.m # 移动储能参数(数量、容量、功率、SOC限值)
├── model/
│ ├── build_pre_placement.m # 预布局子问题约束
│ ├── build_dispatch.m # 动态调度子问题约束
│ └── build_network.m # DistFlow与连通性约束
├── solver/
│ └── solve_ccg.m # C&CG主循环
└── plot/
└── plot_results.m # 时变负荷恢复率、MESS轨迹图
这个结构的核心思想是数据和模型分离。改一个负荷参数不需要翻动优化模型的代码;换一套故障场景只改主程序里的场景列表就行。对复现工作来说,可维护性比“一次性跑通”重要得多,因为你大概率要反复调参对比论文曲线。
4.3 核心代码片段:预布局子问题与动态调度子问题
预布局子问题的YALMIP核心代码如下(简化示意):
matlab复制n_mess = 2; % 2台移动储能
n_bus = 33;
beta = binvar(n_bus, 1); % 预布局位置变量
% 约束:总共只放 n_mess 台
Constraints = [sum(beta) == n_mess];
% 预布局的候选位置一般有限制,只能放在有道路可达、有场地条件的节点
Constraints = [Constraints, beta(candidate_nodes) == 1, ...
beta(non_candidate) == 0]; % 简化示意
% 初始阶段目标:结合不确定性,最大化最坏场景恢复率
% (这里需要接入C&CG迭代,先给一个确定性目标)
Objective = -sum(beta .* bus_load_weight); % 简化示意
动态调度子问题的核心约束:
matlab复制% 位置变量:某台MESS在t时段是否接入节点i
z = binvar(n_bus, n_time, n_mess, 'full');
% 充放电功率与电量
P_ch = sdpvar(n_mess, n_time);
P_dis = sdpvar(n_mess, n_time);
E = sdpvar(n_mess, n_time+1);
Constraints = [];
for k = 1:n_mess
for t = 1:n_time
% 同一时间只能接入一个节点
Constraints = [Constraints, sum(z(:,t,k)) == 1];
% 接入状态与初始位置耦合
if t == 1
Constraints = [Constraints, z(:,1,k) == beta];
end
% 充放电不能同时
Constraints = [Constraints, P_ch(k,t) >= 0, P_dis(k,t) >= 0];
Constraints = [Constraints, P_ch(k,t) + P_dis(k,t) <= P_max * (z任意接入状态)];
end
% 电量递推
Constraints = [Constraints, E(k,2:end) == E(k,1:end-1) + ...
eta_ch*P_ch(k,:)*dt - P_dis(k,:)*dt/eta_dis];
% SOC范围
Constraints = [Constraints, E_min <= E(k,:) <= E_max];
end
接入状态与充放电的耦合用big-M完成:
matlab复制% 只有接入节点i的MESS才能向该节点放电
for t = 1:n_time
for i = 1:n_bus
Constraints = [Constraints, ...
P_dis_node(i,t) <= M * sum(z(i,t,:))];
end
end
这段代码是示意性的,实际项目里还需要把潮流约束、线路容量约束、孤岛连通约束都加进去,但大框架就是如此。先把MESS自身的约束跑通,再逐步叠加上游系统约束,是降低调试难度的有效路径。
4.4 结果复现与可视化:怎么判断程序算对了
跑通程序只是第一步,怎么确认结果“对”才是关键。我通常从三个角度验证:
第一,目标函数值是否合理。 如果不考虑任何故障,全部负荷都能恢复,那么总恢复量应该等于3.715MWh(24小时累计前先看单位时段)。引入故障后恢复量会下降,但下降幅度与故障严重程度成正比。如果结果比“无故障上限”还高,程序一定有bug。
第二,MESS的轨迹是否符合直觉。 画出每台MESS在24小时内的接入节点变化。理想结果应该是:MESS先移动到故障孤岛中心,集中放电一段时间,电量降低后转移到下一个负荷更重要或剩余电量更匹配的孤岛。如果程序给出的轨迹是“原地不动”或者“来回乱跑”,多半是目标函数权重或移动代价没设对。
第三,与论文数值对比。 论文里通常会有几个典型场景下的“负荷恢复率对比图”,曲线大致吻合就能证明复现成功。不必强求逐点一致——论文里很多仿真参数(比如MESS容量、初始SOC、单位移动时间)在正文里根本没写全,能对上趋势就算复现完成。
可视化的核心是画配电网拓扑图叠加MESS轨迹,以及时变恢复率曲线。一个简单的恢复率绘图代码:
matlab复制figure;
plot(1:24, load_recovery_ratio, 'b-o', 'LineWidth', 1.5);
xlabel('时段 (h)'); ylabel('负荷恢复率');
grid on;
title('极端事件下的加权负荷恢复率');
5. 复现中的高发问题:这些坑我替你踩过了
5.1 二进制变量爆炸导致的计算时间失控
两阶段问题最怕的就是二进制变量成倍增长。我踩过的第一个坑是把“移动轨迹”也建模成二进制变量——每台MESS每时段在33个节点之间两两移动,一下多出33×33×24×2 ≈ 5万多个0-1变量,求解器直接瘫在内存里。
正确做法是用“接入位置”变量隐式表达移动过程,而不是把每条可能的移动路径都显式建模。即只保留z_{k,t}^i(接入节点),通过“移动需要时间”的约束限制两个相邻时段接入节点的可达性。这样二进制变量从5万降到几千,求解速度是数量级提升。
5.2 移动时间约束的建模歧义
另一个高频问题是MESS从节点i转移到节点j到底需要多久。这个时间通常取决于道路距离和车速,单位时段内不一定能从任意节点到达任意节点。
解决方法是在建模前先算一个可达矩阵:如果单位时段内MESS最大可移动距离小于节点i到j的实际距离,那么这台MESS在这两个节点之间“直接转移”被视为不可能。于是移动约束写为:
code复制如果 d(i,j) > D_max:
z_{k,t+1}^j ≤ 1 − z_{k,t}^i (禁止相邻时段直接跳变)
更精细的做法是引入“移动状态”变量m_{k,t},表示MESS在t时段处于移动状态,移动期间不能接入节点也不能充放电,移动结束后才可以出现在目标节点。复现论文时建议先按“1个时段范围内可达”来简化,跑通后再逐步细化。
5.3 big-M与单位制引发的数值病态问题
big-M选得太大会导致求解器出现大量“数值警告”,甚至得到违反物理常识的结果,比如某节点恢复了0.3MW负荷但实际该节点总负荷只有0.1MW。我见过最夸张的情况是M取1e6,结果所有约束几乎同时失去区分度,任何解都看起来可行。
经验法则是:能用物理上限推导出边界的,就不要用拍脑袋的M。比如放电功率上限是0.1MW,那么M = 0.2就绰绰有余;线路容量约束对应的M取该线路载流容量乘2即可。M大到1e3以上时,务必保持警惕,优先检查约束通过了check(Constraints)是否全部返回“双双对称”的正常值。
单位制的影响同样不能忽略。如果你坚持用有名值建模,电压是12700V、功率是3.7e6W、阻抗是0.5Ω,这些数值跨越8个数量级,二阶锥约束很快会出数值问题。我个人的习惯是全部转成标幺值,电压基准12.66kV、功率基准10MVA,所有变量都落在0到2之间,求解器跑起来稳如老狗。
5.4 一个小规模人工算例的验证思路
最有效的排错手段不是盯着代码看,而是构造一个你能手算验证的超小算例。
比如取IEEE33节点系统的一个子网络:只保留节点1-2-3这条馈线段,2号节点有1kW一级负荷,3号节点有1kW三级负荷,1台MESS容量2kWh、最大功率1kW。预布局有两种选择:放节点2或节点3。如果正好在节点3附近发生故障,MESS在节点2的恢复效果明显好于节点3。
手工推一遍这个3节点算例的最优解,然后让程序跑一遍,把目标函数值和MESS轨迹手动比对。如果这个算例都算不对,那问题一定在模型代码——赶紧回头检查约束,而不是继续调大算例规模。这个方法救了我无数次,强烈建议所有复现论文的人使用以“人肉可算”的规模作为第一道验证关卡。
另外还有两个小细节值得提一下。一是MESS的初始SOC,极端事件前默认应该预充电到满容量(比如SOC=0.95),否则灾后调度一开始就没电可放,那预布局再优也没用。二是在YALMIP里用optimize求解之前,最好先调用一次assign和check手动验证一组明显可行的解,确保站点约束、电量递推这些基础逻辑没有低级错误。
这个课题从论文复现到真正理解模型,我大概花了两周时间。最耗时的部分并不是求解器,而是反复确认“约束是不是对应物理过程”——比如移动期间到底能不能放电、停电节点能不能靠MESS独立形成孤岛供电。把这些边界条件想清楚,代码反而是水到渠成的事。如果你也在复现类似的工作,希望这篇笔记能帮你少走几段弯路。
