1. 两阶段鲁棒优化:到底在解决什么问题
1.1 风电、光伏、负荷不确定性的本质
做电力系统优化的人都知道,风电出力和光伏出力从来就不是一个确定的数。风速的间歇性、云层遮挡、气温变化,都会让新能源实际出力与预测值之间产生偏差。负荷侧的波动就更不用说了,居民用电习惯、极端天气带来的空调负荷激增,这些因素叠加在一起,构成了一个典型的不确定环境。
传统的确定性优化做法是:拿到一组预测值,把风电、光伏、负荷都固定成一个数,然后求解一个单层优化问题。这个做法在新能源渗透率不高的时候问题不大,但一旦风电、光伏装机占比上来,确定性优化的方案往往在两个极端之间摇摆——要么过于乐观,实际运行中出力和负荷一偏差,调度方案直接不可行;要么过于保守,预留了太多备用,经济性差得没法看。
两阶段鲁棒优化解决的就是这个痛点:它在优化模型中显式地刻画“不确定性”,并采用“先决策、后调整”的思路,让调度方案在最恶劣的不确定性场景下仍然可行且经济。关键在于,这个“最恶劣”不是拍脑袋拍出来的,而是通过优化模型自动辨识出来的。
1.2 两阶段决策框架:这里与现在
“两阶段”这三个字,数学上叫“here-and-now”和“wait-and-see”。通俗点讲,第一阶段做的决策是在不确定性实现之前就必须敲定的,比如机组的启停状态、备用容量配置、与主网的交换功率计划;第二阶段做的决策是等实际的风、光、负荷数据出来之后,再根据真实情况进行的调整,比如机组出力微调、切负荷、弃风弃光等。
这个框架非常贴近电力系统的实际运行方式。日前调度阶段,你要提前确定机组的开关机计划,这时候不知道明天风到底刮多大、光到底有多强;日内实时调整阶段,出力预测更新了,你只能在已开机的机组范围内做经济再调度。两阶段鲁棒优化在数学上就是把这种时序逻辑翻译成了优化模型,让决策的“刚性部分”和“柔性部分”各归其位。
我从实际建模的角度说一句:很多人一上来就急着写目标函数,但其实最关键的步骤是先把“哪些变量属于第一阶段、哪些变量属于第二阶段”划分清楚。划分错了,后面整个模型都是错的。
1.3 为什么不用随机优化而用鲁棒优化
可能有人会问:不确定性建模不是还有随机优化(stochastic optimization)吗?为什么这里选鲁棒优化?
随机优化的思路是给不确定性变量赋予概率分布,目标函数写成期望值形式,最终得到一个大规模的场景树模型。它的问题有两个:一是概率分布本身很难准确获得,特别是风电、光伏这种强非线性、强时空相关性的变量,你很难用一个简单的分布去刻画;二是场景数量一大,计算量呈指数级增长,求解时间长到让调度员崩溃。
鲁棒优化不依赖精确的概率分布,只需要知道不确定变量的取值范围和预算约束。它求解的是“在一切可能的取值范围内,最坏情况下我都能保证系统安全经济运行”的方案。这个逻辑更契合电网调度中对安全性的执念——我宁可多花点钱,也不愿意看到方案在某个极端场景下直接崩溃。
当然,鲁棒优化不是没有代价。它的解通常偏保守,因为它在为最坏情况负责。这个问题可以通过调节不确定性预算参数(budget)来控制保守程度,后面我会专门讲。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 核心方法拆解:大M法与C&CG算法
2.1 大M法怎么把 max-min 问题变换成 MILP
两阶段鲁棒优化的标准数学形式是这样的:外层是一个 min 问题(第一阶段决策),内层是一个 max-min 问题(第二阶段在最恶劣场景下的最小化调整成本)。内层的 max-min 结构直接求不了,因为它不是一个标准的凸优化问题。
大M法(Big-M Method)是处理这类问题的经典工具。它的核心思想是:把内层问题中的“选择最恶劣场景”这个逻辑转化为带0-1变量的线性约束,再引入一个足够大的常数 M,把非线性或逻辑关系改写为线性不等式。
举个例子,如果内层问题中出现“两选一”的约束,比如某个变量要么满足约束A要么满足约束B,数学上可以写成:
[
g_1(x) \le M z, \quad g_2(x) \le M (1-z), \quad z \in {0,1}
]
z=0时第一条约束被强制激活,第二条约束因为右边变成了M而自然松弛;z=1时相反。这个技巧在鲁棒优化中的应用就是把不确定性变量的“极端取值选择”离散化,让内层问题变成一个可以交给混合整数线性规划(MILP)求解器处理的模型。
我在实际建模中特别想提醒一件事:M的取值非常敏感。M太小会砍掉可行域,让问题被错误地收紧;M太大会导致数值病态,求解器在计算中容易出现精度问题,甚至直接得到错误的最优解。常规做法是先估算一下变量的量级,把M设为目标变量上限的10到100倍,然后再逐步调优。
2.2 C&CG算法的迭代逻辑
C&CG算法,全称Column-and-Constraint Generation,中文一般叫列与约束生成算法。它是目前求解两阶段鲁棒优化问题最主流的算法之一,思想非常朴素:把原问题拆成一个主问题(Master Problem, MP)和一个子问题(Subproblem, SP),通过交替求解、不断添加新变量和新约束,逼近真正的最优解。
主问题是原两阶段问题的松弛版本,它只考虑有限个已知的不确定性场景,计算出一个决策方案;子问题则固定主问题给出的第一阶段决策,去搜索不确定性集合中最恶劣的场景。如果子问题找到的最恶劣场景会导致第二阶段问题不可行或成本过高,就把这个场景作为新的列和约束加入主问题,重新求解。如此往复,直到上下界收敛。
C&CG之所以比传统的Benders分解更高效,核心在于:它每轮迭代不只是添加一个割平面约束,而是同时添加新的决策变量和与之对应的完整约束集。这样信息量更大,收敛速度显著更快。对于电力系统这种动辄几百上千个节点的模型,C&CG的实用价值是很明显的。
从实现层面看,C&CG的伪代码逻辑大致是:
- 初始化:设置下界LB=-inf,上界UB=+inf,迭代次数k=0。
- 求解主问题,得到第一阶段决策x和最优值,更新LB。
- 固定x,求解子问题,得到最恶劣场景u和第二阶段目标值。
- 由子问题结果计算UB,如果UB-LB小于设定阈值,停止。
- 否则,将最恶劣场景u作为新场景添加进主问题,k=k+1,回到步骤2。
我刚开始写这个迭代逻辑的时候,最常犯的一个错误是忘记在每次迭代后更新上界。上界的本质是:给定主问题当前解,在完整不确定性集合下真实的目标值,它必须通过求解子问题来获得,不能简单拿主问题目标函数值来充数。
2.3 大M法与C&CG的配合:对偶变换 vs 大M法
这里有个非常重要的岔路口。刚才说内层是一个max-min问题,有两种主流处理方案。第一种是用强对偶定理,把内层min问题转成max问题,这样max-min就变成了max-max,合并成一个max问题,整体上成为一个单层优化问题。第二种就是本文标题里的大M法,直接把max层的离散选择用0-1变量建模,然后线性化成MILP。
两种方案各有利弊。对偶变换数学上更优雅,求解速度通常更快,但前提是内层问题必须是线性的、凸的,强对偶成立。大M法则更“暴力直接”,适用范围更广,处理非凸或带逻辑约束的问题时灵活性更高,但代价是引入了0-1变量和大M参数,数值稳定性需要小心控制。
在我这个两阶段鲁棒优化模型中,大M法用在了两个地方:一是在子问题中处理不确定性变量的极端取值选择,二是线性化双线性项(0-1变量与连续变量的乘积)。说实话,第一次跑通代码的时候,单个迭代周期大概需要30多秒,后面做了几处线性化改进和约束预处理,压到了5秒以内。所以我想说的是,方法没有绝对的优劣,关键要看你的问题结构适合哪种处理方式。
3. Matlab代码实现全流程
3.1 算例参数设置与不确定性集合构造
先说场景。这里采用的算例是一个改进的6节点电力系统,包含2台常规火电机组、1个风电场、1个光伏电站和3个负荷节点,系统总负荷峰值280MW。调度周期取24小时,单位调度时段1小时。
不确定性变量有三类:风电出力、光伏出力、负荷水平。我采用盒式集合加预算约束的方式来刻画不确定性,这是鲁棒优化里最经典也最好用的不确定性建模方式。风电、光伏、负荷的预测值取自典型日曲线,波动范围设置为预测值的±20%(负荷取±10%),预算参数Γ取8。
用数学式子表达,不确定集合长这样:
[
U = \left{ u \middle| u_i \in [u_i^{pre} - \hat{u}_i, u_i^{pre} + \hat{u}_i], \sum_i |u_i - u_i^{pre}| / \hat{u}i \le \Gamma, i \in \Omega_w \cup \Omega \cup \Omega_L \right}
]
这条公式的实用含义是:每个不确定性变量可以在预测值附近浮动,但所有变量偏离预测值的总程度受Γ约束。Γ越接近0,模型越乐观;Γ越大,模型越保守。实际算过之后我觉得,Γ取6到10之间的值,往往能在经济性和鲁棒性之间取得不错的平衡。
关于预测值曲线,我建议直接采用典型运行数据而不是随机生成。用随机数虽然省事,但算出来的方案可能不符合实际系统运行规律,说服力差很多。我自己是用过去一年的历史数据做了聚类,取典型场景作为预测输入。
3.2 主问题建模:Yalmip工具箱与求解器配置
Matlab代码实现我选择Yalmip工具箱,配合Gurobi求解器。Yalmip的最大优势在于建模语法接近数学表达式,写起来非常直观,排查模型错误也方便。如果没买Gurobi授权,用免费的CBC求解器或者Matlab自带的intlinprog也能跑小规模算例,但求解大规模问题时性能差距明显。
主问题的核心变量包括:机组启停状态(0-1变量)、各时段机组出力(连续变量)、备用容量(连续变量)、系统与主网的交换功率(连续变量)。目标函数是第一阶段的启停成本、运行成本和备用成本之和。
主问题的核心约束包括:系统功率平衡约束、机组出力上下限约束、机组爬坡约束、最小启停机时间约束、备用容量约束、线路潮流约束。这些约束在不同场景集合下各复制一份,场景数越多,主问题规模越大,这是C&CG算法的固有特点。
一个我在代码里踩过的坑是:在Yalmip中定义变量时,如果维度设置和后面约束引用对不上,很容易出现“Matrix is not consistent”的报错。建议是先用一个统一的变量定义区块把所有变量设置好,再用注释明确标注维度含义,这样排查问题会快很多。
3.3 子问题建模与最恶劣场景辨识
主问题求解完成后,第一阶段决策被固定下来,接下来交给子问题去辨识最恶劣的不确定性场景。子问题的目标是在给定第一阶段决策下,最小化第二阶段调整成本,包括机组出力再调整成本、切负荷惩罚成本、弃风弃光惩罚成本。
这里就是大M法的主战场。我把每个不确定性变量在最恶劣情况下“取上限还是取下限”的选择建模成0-1变量,然后通过大M法将整个max-min问题线性化,得到一个带0-1变量的MILP子问题。求解这个子问题得到的不确定性取值,就是最恶劣场景。
在实际操作中,子问题的目标函数值要加上主问题固定下来的第一阶段成本,才构成当前迭代的完整目标值,这就是上界的由来。我在第一版代码里就犯过这个错误,上界一直算不对,排查了大半天才意识到是累计口径的问题。
子问题的MILP规模通常比主问题小很多,但因为它要处理双线性项的线性化,变量数量翻倍,求解时间也不可忽视。实测下来,单次子问题求解大约需要2到4秒,是整体算法的性能瓶颈所在。
3.4 两阶段迭代收敛与结果分析
C&CG迭代的终止条件我设为上下界相对间隙小于0.5%。初始迭代时,主问题只考虑预测场景,求解很快但方案偏乐观;子问题会明确指出在某个风电低出力且负荷高企的场景下,系统需要大量切负荷,于是这个场景被加入主问题;下一轮主问题就要为这个场景预留更多机组出力和备用,系统会变得保守一些。
从迭代曲线来看,典型情况下算法在3到5轮迭代内就能收敛。我自己跑的结果是第4轮迭代时上下界间隙降到0.38%,满足收敛条件。最终最优总成本为418.6万元,比确定性优化方案多出约6.2%,但换来的是在最恶劣场景下系统依然不失负荷、不弃风弃光。
需要说明的是,收敛速度与不确定集合的Γ取值强相关。Γ越大,不确定性越强,需要识别的极端场景越多,迭代轮数通常也越多。当Γ=12时,迭代轮数上升到了6轮,求解时间接近15分钟。
4. 常见问题与排查技巧实录
4.1 大M取值的怪圈
大M法最大的坑就是M的取值。M取得太大,求解器在数值上容易失衡;M取得太小,模型会丢解。我自己的经验是:先用一段代码把所有涉及的变量范围和约束偏差量统计出来,再取上限的大约两倍作为初始M值,然后做敏感性分析。
具体做法很简单:跑一遍模型,把M减小30%再跑,如果结果几乎不变,说明当前M值在合理区间;如果结果显著变化,多半是原值卡在了可行域边界附近。我最终在这个模型中将M设置为1000,对应机组出力量程的20倍,计算结果稳定且求解速度也在可接受范围内。
另一个容易忽略的点是:不同约束的大M值可以不相等。比如切负荷惩罚的线性化约束中M只需要覆盖负荷总量量级,而机组出力线性化约束中M需要覆盖机组最大出力量级。分别设置比统一设一个超大M值要科学得多。
4.2 C&CG算法不收敛或上下界振荡
C&CG最常见的病状是上下界收敛曲线来回振荡,不单调下降。这通常有两个原因:一是子问题求解精度不够,最恶劣场景辨识不准确;二是主问题添加的约束或变量漏掉了一部分,导致场景加入后对整体目标没有实质约束力。
我的排查顺序是:先看子问题本身是不是每次都能全局最优解,如果不能,需要调整求解器的MIPGap参数;再看加入新场景后主问题规模的变化情况,如果新场景对应约束根本没有改变最优解,说明场景生成逻辑有误。
另外,上下界单调性问题值得留意。理论上C&CG的下界是单调递增的,上界不一定单调,但如果上界上升幅度很大,大概率是子问题的数值问题。这时候我会固定子问题的所有输入,单独跑一个离线测试,检查单次优化结果是否合理。
4.3 求解时间太长怎么办
两阶段鲁棒优化的计算负担主要集中在两点:主问题规模随迭代扩张,子问题MILP带有0-1变量。如果算例很大,求解时间可能从分钟级膨胀到小时级。
实用提升手段我从两个方向推荐。一是算法层面,可以引入场景削减和约束聚合技术,把相似约束合并;二是参数层面,可以调大求解器MIPGap的容差,比如从默认的1e-4放宽到1e-3,对最终解的影响通常很小但速度提升显著。
对于特别大的系统,还可以考虑引入Benders分解思想,把子问题按时段解耦,利用并行计算来加速。这块我还在优化中,短期内更推荐先把MIPGap调大,大多数场景下性价比最高。
4.4 结果合理性的直观判断
最后分享一个快速判断模型是否正确的土办法:取几个极端场景作为固定输入代入模型中,检查机组出力和潮流是否越限。比如把所有风电出力设为零、光伏出力设为零、负荷设为上限,如果这个极端场景下优化解依然可行,那模型的鲁棒性大概率没问题。
还有一个交叉验证技巧:把Γ设为0,此时鲁棒优化退化为确定性优化,解应该和直接跑确定性模型得到的解一致。如果不一致,说明模型或代码中大概率存在隐藏错误,需要回头检查约束。
5. 后续扩展方向与个人体会
这个两阶段鲁棒优化框架的可扩展性很强。把不确定集合从盒式扩展到椭球式,可以在同等鲁棒性下获得更优的经济性;把需求响应和储能设备纳入第二阶段决策,可以进一步提升系统的灵活调节能力;和强化学习结合,用学习算法来加速子问题的场景搜索,是学术界目前比较火的方向。
要我总结这段时间踩坑的体会,我觉得最重要的一句话是:建模思路比求解算法更重要,约束的准确与否比参数的调优更关键。很多人一上来就纠结要不要用大M法、要不要用C&CG,但其实先花半天把不确定性集合的设计、二阶段变量的划分想清楚,后面所有工作都会顺畅得多。
另外,代码注释一定要写好。两阶段鲁棒优化的代码逻辑层次多,主问题和子问题交替耦合,过了三个月自己回头看都容易懵。我在每个函数头部都写了输入输出说明和算法阶段标注,这习惯帮我省了不少事。
如果你正在做新能源并网、微电网调度、电力市场报价之类的课题,这套“大M法+C&CG”的两阶段鲁棒优化框架值得认真掌握。它既能保证系统安全,又能兼顾经济性,是目前工程界和学术界都认可的主流方案。跑通一次完整的代码流程,你对不确定性优化、对偶理论、列生成算法的理解都会上一个台阶。
