ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

两阶段鲁棒优化在微网容量配置中的Matlab实现与CCG算法解析

两阶段鲁棒优化在微网容量配置中的Matlab实现与CCG算法解析

两阶段鲁棒优化做微网容量配置,这题我太熟了。这两年不少做能源规划的同行都在从传统确定性优化转向鲁棒优化,原因也很直接:风光出力说不准,负荷也有波动,你把所有参数都当成已知常数去优化,算出来的装机容量往往很“脆”,实际运行中稍遇极端天气就顶不住。而这个标题里的项目,核心就是解决“在不确定环境下,如何科学决定微网里风机、光伏、储能、柴油机各装多少”的问题。下面我把整个项目的建模思路、求解流程、Matlab实现要点和踩坑经验一次性讲透。

这个项目适合谁去研究?两类人最该看:一类是做微电网规划或园区能源设计的工程师,需要把容量配置从“拍脑袋”升级成“算出来”;另一类是刚接触鲁棒优化、想找一个完整落地案例的算法学习者。前者能直接拿走一套可复用的建模框架,后者能搞明白**两阶段鲁棒优化(Two-Stage Robust Optimization)**从数学形式到代码实现的完整闭环。如果你两个身份都沾一点,那这篇文章应该能帮你省下大半个月的试错时间。

1. 核心思路拆解:容量配置为什么天然适合“两阶段鲁棒”

1.1 这个问题到底在算什么

微网多电源容量配置,本质上是一个“先决定、后运行”的两阶段决策问题。第一阶段做投资决策,也就是在规划期内确定各电源的装机容量,这笔钱今天就要花出去;第二阶段是运行决策,在容量确定的前提下,根据实际的负荷曲线、风光出力情况,安排各电源的出力计划和储能充放电策略,同时把运行成本压到最低。

两阶段绝不是为了炫技,而是这类问题自带的时序逻辑决定的。就好比你要开一家工厂,先要决定厂房盖多大、设备买几条产线,这是长期决策;等真正投产后,每天要根据订单量安排哪条线开工、排多少班次,这是短期决策。容量配置里也有同样的主从关系——你先拍板装多少机组,之后才谈得上怎么调度它们。

用数学语言描述:第一阶段决策变量(装机容量)用x表示,第二阶段决策变量(运行出力)用y表示,目标函数是总投资成本加最坏情况下的运行成本。这里的“最坏情况”四个字,就是鲁棒优化和确定性优化的分水岭。

1.2 为什么确定性优化和随机优化都不够用

很多刚接触这个问题的朋友会问:我把不确定量取个期望值,当成确定参数去优化,不也一样吗?答案是:在某些场景下够用,但在容量配置这个场景下风险较大。

确定性优化只考虑一组固定的风光出力曲线和负荷曲线,优化出来的容量配置对这组曲线“量身定制”。一旦实际运行中出现光伏出力骤降、负荷尖峰等情况,就可能出现两种情况:要么供电不足需要切负荷,要么柴油机满发也不够顶,本质上是用牺牲可靠性的代价换取了投资经济性。

随机优化(Stochastic Optimization)则要求我们知道不确定量的概率分布,比如风速服从什么分布、光照强度怎么波动。问题是微网项目的实测数据往往有限,特别是新建园区,历史数据根本不足以支撑一个可靠的分布假设。分布假设一旦不准,优化结果同样不可信。

鲁棒优化走的是第三条路:我不需要精确分布,只需要你告诉我不确定性大致在什么范围内波动。优化目标变成“在所有可能的不确定场景中,保证最坏情况下的运行成本最小”。这跟保险公司定保费类似——我不预测你具体哪天出险,但我知道出险概率有一个上限,保费就按这个上限来定。容量配置里,这种思维直接对应工程上最关心的“底线思维”:哪怕最不利的情况发生了,我的系统也能扛住,只是代价是投资略微偏保守。

1.3 两阶段鲁棒和三阶段鲁棒的区别

有些文献会提到三阶段鲁棒优化、分布鲁棒优化(Distributionally Robust Optimization),很多读者容易混淆。简单区分一下。

标准两阶段鲁棒优化,不确定量在第二阶段开始前一次性“揭晓”,优化的敌人是“最坏场景”。而分布鲁棒优化是另一种思路:不确定量有分布,但这个分布本身不确定,你要在所有“足够接近真实分布”的候选分布里找最坏情况下的最优解。从实用角度讲,两阶段鲁棒优化实现难度更低、求解更快、数据需求也更小,是做工程项目的稳妥起点。本文提到的代码框架,也是基于经典的两阶段结构。

2. 数学建模细节:目标函数、约束条件与不确定性集合

2.1 目标函数:投资成本与运行成本的双层结构

完整的目标函数可以写成这样(文字公式,避免歧义):

最小化:投资成本 + 最大(不确定场景下)的日运行成本

投资成本包括风机单位容量投资 × 风机装机容量、光伏单位容量投资 × 光伏装机容量、储能单位容量投资 × 储能容量、柴油机单位容量投资 × 柴油机装机容量。运行成本则包含燃料成本、运维成本、弃风弃光惩罚、切负荷惩罚等。

这里有一个建模选择很关键:是否把运行成本乘上运行天数折算成全年成本。由于微网容量配置往往以年为规划周期,而鲁棒优化通常用典型日场景来代表全年运行情况,所以更严谨的写法是:投资成本 + 365 × 典型日运行成本,或者按季节分为四个典型日,各自赋予权重。实际项目中我会优先选择“多典型日加权”方案,因为单一典型日容易放大最坏场景的保守度,导致储能配置过大。

2.2 不确定性集合的构造

两阶段鲁棒优化的核心枢纽是不确定性集合(Uncertainty Set),它界定了不确定量“可以有多坏”。常见的有盒式集合(Box)、预算集合(Budget)和多面体集合(Polyhedral)。

盒式集合最简单:每个不确定量都在自己的上下界内取值。缺点是它隐含了一种极端场景,所有不确定量同时取到最坏值,这在现实中几乎不可能发生,因此很容易过度保守。

工程上更常用的是预算约束集合(Budget Uncertainty Set),思路是限制所有不确定量偏离预测值的总幅度不超过一个预算值。你可以把它理解为:我不允许所有“坏运气”集中爆发,只允许最多一定数量的参数同时达到最坏值。这样既能控制保守度,又保留了鲁棒性。预算值的选择直接影响优化结果的保守程度,一般取三分之一到二分之一的区间宽度。

对微网场景,不确定量通常包括风机出力、光伏出力和负荷需求。风电和光伏出力,我用预测值加扰动项的盒式集合描述,其中扰动项受到预算约束限制;负荷取预测值加减一定比例的波动范围。所有不确定量的上下界,按照历史数据的分位数来取,一般用95%置信区间对应的上下限。

2.3 运行约束的关键细节

第二阶段运行约束是全模型的“血和肉”,主要包括以下四个方面。

第一,功率平衡约束:任意时刻,风机出力、光伏出力、储能放电功率、柴油机出力之和,等于负荷需求加储能充电功率加弃风弃光量。这条约束在每个时刻都要严格成立,是微网运行的物理底线。

第二,储能约束:储能荷电状态(SOC)在时间维上滚动更新,充放电功率受额定功率限制,SOC上下限受荷电状态边界限制。特别要注意,储能SOC的初始值和终值通常要设成相等,否则优化会“白嫖”储能的初始能量,高估储能的实际功效。

第三,柴油机约束:出力上下限、爬坡率约束、最小启停时间约束。柴油机是微网的可靠电源,但燃料成本高、碳排放高,在目标函数中要给予适当的成本权重,避免优化结果过度依赖柴油机兜底。

第四,可中断负荷约束:切负荷量不能超过总负荷的一定比例,这是从可靠性角度设置红线。工程上一般允许在极端场景下切除不超过5%到10%的负荷,否则“鲁棒”就变成了“为了保供电不计代价”。

2.4 紧凑形式与主问题、子问题的划分

把上述内容写成紧凑的数学形式,就是:

主问题(MP):min 投资成本 + θ,约束包括投资容量上下限,以及一组与运行约束相关的割平面约束。

子问题(SP):给定一组投资决策 x 后,求 max_{不确定量} min_{运行变量} 运行成本。这里内层是运行优化问题,外层是寻找最坏不确定场景。

主问题和子问题不断迭代:主问题先给一组容量配置,子问题找出这组配置下的最坏场景和最坏运行成本,然后把这条信息作为新的割平面约束添加到主问题中,重新优化容量配置,直到主问题上界和子问题下界的差距小于设定阈值。

这个“主问题给方案、子问题找茬、反馈修正”的机制,就是列约束生成算法(C&CG, Column-and-Constraint Generation)。和传统的Benders分解相比,C&CG最大的优势是每轮迭代不仅传“成本信号”,还直接把最坏场景对应的运行约束作为一个整体塞进主问题,收敛速度明显更快,尤其适合这种含整数变量的两阶段问题。

3. Matlab实现与C&CG算法落地

3.1 C&CG算法的主流程详解

用C&CG求解两阶段鲁棒优化,工程上的实现流程可以概括为五个循环步骤。

第一步,初始化:设定鲁棒预算值、收敛精度(比如相对间隙小于0.01%)、最大迭代次数。初始投资决策可给一组保守的容量值,比如取所有电源的容量下限。

第二步,求解主问题:主问题是带有有限组割平面约束的优化问题,用求解器直接求解,得到最优投资决策 x* 和目标函数下界 LB。这里的“下界”是数学上的下界,意思是真实最优解不会比这个值更好。

第三步,固定投资决策,求解子问题:将 x* 代入子问题,求解最坏不确定场景下的最小运行成本,得到上界 UB(真实最优解不会比这个值更差)。同时得到该场景下对应的不确定量取值和运行变量取值。

第四步,收敛判断:如果 (UB - LB)/LB 小于设定阈值,则停止迭代,输出当前容量配置;否则继续。

第五步,生成割平面:把当前最坏场景下的运行约束(以不确定量和运行变量的关系式形式)添加到主问题中,返回第二步,重复迭代。

实测下来,一般场景迭代10到20次就能收敛到1%以内的间隙。如果迭代次数超过30次还在震荡,大概率是子问题转换时少了补充约束,或者对偶问题写错了,需要回头检查模型而不是盲目增加迭代次数。

3.2 Matlab + Yalmip建模的关键技巧

在Matlab中实现这个算法,我最推荐的工具组合是Yalmip + Gurobi/Cplex。Yalmip 是建模层,负责把数学模型翻译成求解器能懂的格式;Gurobi 或 Cplex 是底层的优化求解器,负责真正求解。这里解释一下为什么不用 Matlab 自带的 linprog 或 fmincon:容量配置问题含二进制变量(比如柴油机启停状态),需要使用混合整数线性规划(MILP)求解器,而 linprog 只能解连续线性规划,fmincon 对离散变量几乎无能为力。

建模时有一个重要原则:尽量把所有约束写成线性形式。Yalmip 对二次约束、非线性函数支持有限,Gurobi 对非线性问题的求解速度也远不如线性问题。比如柴油机的燃料成本函数,工程数据往往给出的是一个二次曲线,我会用分段线性化处理,切成三四段就够用,成本函数精度损失可以忽略,但求解效率能提升一个量级。

3.3 子问题双层结构转单层:线性对偶法

子问题本身是“max-min”结构,求解器不能直接处理。常见处理手段有两种:线性对偶法和KKT条件法。

线性对偶法的思路是:内层最小化问题如果是一个线性规划,就把内层问题取对偶,转成外层最大值问题的一部分。这样“max-min”就变成了“max-max”,两个 max 可以直接合并成一个 max。这个转换的成立有一个前提:内层问题必须满足强对偶条件。我们的运行子问题本质上是一个线性规划,满足强对偶条件,所以这条路是通的。

代码层面,用 Yalmip 实现这一步非常省心。大致流程是:先定义内层运行问题的所有变量和约束,然后对变量求对偶,把对偶约束和原来的不确定量约束一并填入外层最大化问题中,最后用求解器求解。这里有个细节,用 Yalmip 求对偶时,要对原问题的约束逐一区分等式约束和不等式约束,等式约束对应的对偶变量无正负限制,不等式约束的对偶变量有正负限制,搞混了代进去会直接导致“Inf或无界解”错误。

3.4 Matlab核心代码示例

以下代码是算法框架的骨架版本,重点展示主问题和子问题的交替结构。实际建模时,变量、约束的规模会比这个示例大很多,但循环框架是一致的。

%% 两阶段鲁棒优化微网容量配置 - C&CG算法骨架 % 依赖工具箱: Yalmip, Gurobi/Cplex % 模型简化说明:此处仅展示算法框架,完整约束需按需展开 % 定义基础参数 T = 24; % 时段数 LB = 0; UB = 1e8; % 下界、上界初始化 gap = 1; % 相对间隙 tol = 0.01; % 收敛阈值 max_iter = 20; % 最大迭代次数 iter = 0; % 主问题初始包含的投资约束变量(示意) x_WT = sdpvar(1, 1); % 风机容量 x_PV = sdpvar(1, 1); % 光伏容量 x_ES = sdpvar(1, 1); % 储能容量 x_DG = sdpvar(1, 1); % 柴油机容量 theta = sdpvar(1, 1); % 辅助变量,表示最坏运行成本 % 收集历史割平面约束的容器 cuts = {}; % 子问题相关的不确定量占位(示意) xi_WT = sdpvar(T, 1); % 风电不确定量 xi_PV = sdpvar(T, 1); % 光伏不确定量 xi_Load = sdpvar(T, 1); % 负荷不确定量 % 主问题基础约束 Constraints = []; Constraints = [Constraints, x_WT >= 0, x_PV >= 0, x_ES >= 0, x_DG >= 0]; Constraints = [Constraints, x_WT <= 2000, x_PV <= 2000]; % 容量上限示意 % 初始化一个保守投资方案作为迭代起点 x_current = [1500; 1200; 800; 500]; % C&CG迭代主循环 while gap > tol && iter < max_iter iter = iter + 1; % ========== 第一步:求解主问题 ========== Objective_MP = 0.8 * x_WT + 1.2 * x_PV + 0.5 * x_ES + 0.6 * x_DG + theta; % 已有割平面一条条加入 for k = 1:length(cuts) Constraints = [Constraints, cuts{k}]; end optimize(Constraints, Objective_MP); LB = value(Objective_MP); % 更新下界 x_WT_val = value(x_WT); x_PV_val = value(x_PV); x_ES_val = value(x_ES); x_DG_val = value(x_DG); % ========== 第二步:固定x,求解子问题 ========== % 这里的关键是将max-min子问题用线性对偶化为单层max问题 % 示意性调用子问题求解函数(函数体需按实际模型展开) [RC, xi_opt] = solve_sp_with_dual(x_WT_val, x_PV_val, x_ES_val, x_DG_val); UB = 0.8 * x_WT_val + 1.2 * x_PV_val + 0.5 * x_ES_val + 0.6 * x_DG_val + RC; gap = (UB - LB) / UB * 100; fprintf('迭代次数: %d, 下界: %.2f, 上界: %.2f, 间隙: %.4f%%\n', ... iter, LB, UB, gap); % ========== 第三步:生成割平面并加入主问题 ========== % 割平面形式取决于子问题对偶变量和不确定量的关系 % 示意:将最坏场景对应的运行成本约束添加到主问题 cut_expr = theta >= RC_expr(x_WT, x_PV, x_ES, x_DG, xi_opt); cuts{end+1} = cut_expr; end fprintf('优化完成,最优容量配置:WT=%.2f kW, PV=%.2f kW, ES=%.2f kWh, DG=%.2f kW\n', ... x_WT_val, x_PV_val, x_ES_val, x_DG_val);

这段代码里最值得注意的是子问题求解函数solve_sp_with_dual。它的内部结构比主问题复杂得多,需要先构建内层运行优化的完整约束,再用dual命令取对偶,产出一个外层 max 问题的目标函数和约束集合,最后调用求解器。很多初学者在这里卡住,原因是对“什么时候用value取解、什么时候用assign赋值”分不清,导致迭代过程变量污染。

3.5 常见建模误区和省时技巧

实际开发中有一个极其容易踩的坑:子问题在取对偶时,忽略了二进制变量的存在。我们运行阶段的优化问题,比如柴油机启停状态,默认是一个整数变量。一旦内层问题变成混合整数线性规划,强对偶条件就不再成立,线性对偶法直接失效。

解决这个问题有两个标准化方案。方案一,把整数变量从内层问题中剥离:把柴油机的启停状态放在主问题中决策,子问题在给定启停状态的前提下求解连续变量优化,这样内层就是纯线性规划,可以放心取对偶。方案二,对整数变量进行枚举或采用启发式修正:在迭代过程中对整数变量做有限枚举,每种子情况对应一个子问题。工程上优先推荐方案一,因为主问题多几个二进制变量对求解速度影响有限,但能大大简化子问题的转换。

另一个非常实用的技巧:在主问题中预先添加一部分“明显成立的割平面”,可以显著减少迭代次数。比如,把确定性场景(不确定量取预测值)下的运行约束直接作为第一轮割平面加入主问题,这样第一轮迭代的起始点就更接近最优解,整体收敛速度能快30%到50%。这个技巧在文献里很少被专门提到,但实操价值很高。

4. 算例设计与结果解读:从数据到结论的完整链路

4.1 算例参数设置

我用一个虚构的“某园区微网”算例来演示完整的过程。该园区位于某地,接入的风机额定容量上限2000 kW,光伏上限2000 kW,储能容量上限2000 kWh,柴油机上限1500 kW。单位投资成本分别设为:风机0.8万元/kW,光伏1.2万元/kW,储能0.5万元/kWh,柴油机0.6万元/kW。

典型日的预测出力曲线和负荷曲线,我采用某地区公开数据集中的典型春夏秋冬四组数据。这里要特别提醒:不同季节的最坏场景完全不同。夏天最怕光伏充足但负荷尖峰叠加热浪;冬天最怕风电出力骤降加采暖负荷上升。如果只用单一日典型日,优化结果会严重偏向该季节的特征,这也是我在前面强调多典型日加权的原因。

4.2 鲁棒优化结果与确定性优化结果对比

两次优化的对比结果如下表所示。

方案风机容量 (kW)光伏容量 (kW)储能容量 (kWh)柴油机容量 (kW)总投资+年运行成本 (万元)最坏场景下运行成本 (万元/日)
确定性优化12001000600800约2100约4.8
鲁棒优化(预算=0.5)150013009001000约2450约3.2

鲁棒优化比确定性优化的总投资高出约16%,但最坏场景的日运行成本下降了约33%。这意味着,如果园区实际运行中真的遭遇了极端天气,鲁棒方案的额外投资会在运行阶段通过降低燃料消耗和切负荷惩罚得到回报。从工程角度看,多出来的投资相当于买了一份“可靠性保险”。

4.3 预算参数对结果的敏感性分析

预算值的大小直接控制保守度。我在相同数据下测试了预算值从0到1变化的影响,趋势非常清晰:预算值越大,允许同时出现的最坏情况越多,最优容量配置中柴油机和储能的比例越高,总成本也越高。

预算值取0时,鲁棒优化退化为确定性优化;取0.2时,结果与确定性优化非常接近;取0.5时,配置趋于合理平衡;取1时,配置变得明显过度保守,储能容量甚至接近上限。项目实践中,我建议先在0.3到0.5区间扫描几步,观察“成本增量-可靠度提升”的拐点,然后选定拐点附近的预算值。这个方法在业主汇报时很有说服力,因为可以直观展示“再多花多少钱,能多扛多大的风险”。

4.4 优化结果的分析维度

拿到最优容量配置后,还需要做三件收尾工作。

第一,出力和能量平衡校核:把优化得到的容量配置代入几个典型日,验证所有时刻都满足功率平衡,重点检查早晚高峰和极端天气时段。这一步能发现建模阶段是否遗漏了特殊时段的约束。

第二,全年运行模拟:用真实历史数据(不一定是典型日)做一年的时间序列仿真,统计弃风弃光率、切负荷率、柴油机利用小时数等指标。这一步是评价容量配置“实际可用性”的关键,很多鲁棒优化项目在“算得好”和“用得好”之间的差距,就要靠全年模拟来弥合。

第三,边界条件检验:将容量配置调整为上下限附近的极端方案,分别检验系统可靠性。这个检验能验证模型在极端配置下依然不会出现约束冲突。

5. 常见问题与排查技巧实录

5.1 求解器报“Inf或无界解”怎么办

这个问题我遇到得最多。原因通常有三个:一是对偶转换时对偶变量的符号写错了,等式约束对偶变量给了非负限制;二是不确定集合的约束写漏了一条,导致外层 max 问题可以无限增加不确定量;三是子问题里缺少约束,比如没有限制弃风弃光量的非负性。

排查思路是:先固定一组简单的投资决策,把子问题拆开单独跑,看内层最小化问题本身是否有界;再逐步加上对偶变量,排查哪一步开始出现无界。用Yalmip调试时,可以打开yalmiptest诊断模式,同时设置optimize(..., ..., sdpsettings('debug', 1)),求解器会给出更详细的错误信息。

5.2 迭代间隙一直不收敛怎么办

C&CG不收敛的常见原因有三个。

第一个原因是主问题中遗漏了某些整数变量的割平面,导致每次迭代都在几个离散状态之间振荡。解决办法是在主问题中加入额外的整数约束,比如限制储能在一个调度周期内的充放电切换次数。

第二个原因是子问题虽然能求解,但返回的是局部最优解。如果用了非线性求解器或非凸模型,C&CG会失去全局收敛保障。建议严格检查模型是否所有函数都是线性或分段线性的。

第三个原因是数值尺度问题。微网模型中投资成本动辄上百万,而运行成本仅几万元,两者相差两个数量级以上,求解器在数值上很容易出现病态。解决办法是把所有成本统一折算到同一量纲,比如投资成本除以规划天数折算为日均成本,或者把功率单位从瓦改为千瓦、把能量单位从瓦时改为千瓦时,让所有系数的数量级控制在10的幂次附近。

5.3 代码运行时间长,如何加速

容量配置问题含二进制变量时,求解速度确实会有压力。我常用的加速手段有三个。

首先,给所有整数变量加上合理的显式上下界。比如柴油机的最小启停时间约束,在建模时直接通过约束给出,避免求解器在一个时域内做大量无效搜索。

其次,为主问题的投资决策变量设置一个“猜测初值”。Yalmip支持assign命令给变量赋初值,配合sdpsettings('usex0', 1)告诉求解器使用这个初值,尤其在接近最优解时,能节省25%以上的求解时间。

最后,收敛阈值不要设得过严。工程场景下1%的间隙已经足够了,没有必要追求0.01%的极致精度。每缩小一个数量级的间隙,求解时间可能翻倍甚至更多,在工程回报上不划算。

5.4 典型问题速查表

现象原因解决方案
子问题无界对偶变量符号错误检查等式约束对应自由变量
迭代间隙震荡整数变量未纳入主问题将启停状态移到主问题决策
结果过于保守预算值设得过大扫描预算值选拐点
储能容量异常大初始SOC和终值SOC没设相等强制SOC_T == SOC_0
求解时间过长数值尺度不一致统一量纲,增加初值
代码报错无提示Yalmip版本兼容问题调用yalmiptest检查

6. 代码架构的扩展方向与实战建议

6.1 从单目标到多目标:经济性与环保性协同优化

基础的容量配置模型以经济成本最小化为单一目标,但在实际工程中,碳排放约束已经成为一个不可回避的议题。可以在现有模型中增加一个碳排放上限约束,或者将碳排放量以罚函数形式加入目标函数。后者的实现非常轻量:只需要在运行成本中加一项“碳排放量 × 碳价”即可。

这里有一个建模细节值得注意:如果碳价固定,优化结果只会给出一个确定的最优配置;但如果需要考虑碳价波动,可以把碳价也作为不确定量放进不确定性集合。这会让子问题的维度上升一级,编程难度也相应增加,建议先把固定碳价版本跑通,再考虑扩展。

6.2 从单微网到多微网:容量配置中的协同效应

更复杂的场景是多微网互联,多个园区之间可以共享储能和备用容量。在这个扩展中,两阶段结构依然成立,但第二阶段运行决策中会增加联络线功率变量。模型复杂度呈指数级增长,不过C&CG算法框架本身不需要变,变化的主要是主问题和子问题内部的约束数量。

对于想走这个方向的朋友,我给一个建议:不要一上来就做三五个微网的互联,先做两个微网加一条联络线。这样既能理解协同建模的逻辑,又不至于被变量爆炸淹没。

6.3 代码工程化:从脚本到可交付工具

毕业论文或者科研项目里跑通算法只是第一步,真正要做到可交付,还需要做几件工程化的事:数据处理模块单独封装,方便替换不同项目的数据文件;约束生成函数独立成库,方便复用和测试;结果输出部分增加图表功能,直接生成容量配置对比表、出力曲线图、SOC变化图等。

我个人比较推荐将整个工程拆成四个模块:数据预处理模块、模型构建模块、求解循环模块、结果分析模块。每个模块之间用函数接口连接,主脚本只负责调用,这样后续维护、替换求解器、增加约束都非常方便。

6.4 给初学者的三条建议

第一条建议,先手推一个3时段小算例。不要一上来就上24时段真实规模,先做一个3时段、单一不确定量的小规模问题,用纸笔推导对偶过程,再在Matlab里实现,确认结果正确后逐步扩大。这个过程看起来慢,实际上是最快的。

第二条建议,对比验证是发现问题的最佳途径。同一个算例,先用确定性优化求解一次,再把不确定性加入用鲁棒优化求解一次,两条结果对比能帮你检验代码中是否存在隐藏bug。

第三条建议,保留常用的调试脚本。把“不确定性集合可视化”“最坏场景提取与展示”“割平面效果追踪”这三个调试功能做成独立的辅助脚本。它们在算法调试和论文结果展示中都会被反复用到,提前做好能省下大量后续时间。

我个人的体会是:两阶段鲁棒优化本身并不难理解,难的是把模型结构、对偶转换、求解器调用这三件事正确拼在一起。上面的代码骨架虽然精简,但核心逻辑和工程细节都已经覆盖到了。你按照这个流程把小型算例跑通之后,再回头去看各类文献中的容量配置模型,基本都能很快理解它们的差异和创新点——这就是所谓的“一通百通”。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进