ARTICLE · INTELLIGENCE

战地情报 · 详情页

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

微电网调度中的两阶段鲁棒优化:从不确定集到CCG算法实践

微电网调度中的两阶段鲁棒优化:从不确定集到CCG算法实践 做微电网调度做到第三年的时候我彻底对确定性优化失去了幻想。那段日子我手里的园区微电网项目反复出现同一个尴尬场面光伏预测模型精度已经调到96%以上可每到夏天午后一片云飘过来光伏出力五分钟内掉一半柴油机爬坡跟不上系统只能紧急切除部分负荷。事后复盘问题不在预测算法而在调度模型本身——它默认了一个“确定的未来”一旦现实偏离方案就失效。后来我下定决心把两阶段鲁棒优化从论文搬到工程现场这篇手记记录的就是模型设计、算法选型、求解器调试到最终落地的全过程。如果你正在做微电网的日前调度、日内调整或者被新能源预测误差反复折磨这篇文章应该能帮你少走几个月的弯路。1. 为什么我放弃了确定性优化1.1 一次让我脸上挂不住的“高预测准确率事故”去年七月的一个下午气象预报显示晴转多云光伏预测功率曲线和实测曲线几乎重合误差不到4%。我按惯例运行了确定性日前调度模型得到一份“理论上最优”的柴油机启停计划白天光伏出力充足柴油机全部停机储能低价充电静待夜间放电。结果下午三点十分一大片积雨云从西边压过来光伏出力从2.8 MW一分钟内掉到1.2 MW。柴油机从冷态启动到并网需要12分钟期间储能以最大功率放电也无法完全补上缺口频率一度跌破49.5 Hz保护装置直接切掉了一整条生产线的负荷。车间主任打电话来的时候我确实很没面子。这件事让我意识到预测准确率只是一个统计数字它衡量的是“平均偏差”而微电网运行真正要命的是那些平均之外的极端场景。一台出力为0的柴油机面对半小时内2 MW的功率缺口什么都不做等于把系统的生存押在天气的善意上。1.2 确定性优化的盲区只相信一个“预期未来”确定性优化的逻辑很简单输入一组预测值光伏出力、负荷、电价求解一组最优决策变量让总成本最小。它的数学本质是给定唯一确定的不确定参数预测值优化一个唯一确定的决策方案。这句话听起来平淡但在工程上意味着所有备用容量、机组启停、储能策略都是针对某一个“想象出来的未来”量身定做的。如果3小时后实际光伏比预测低20%这个方案不会主动预留额外的爬坡容量如果实际负荷比预测高15%它也不会提前多安排一台机组在线。所有应对突发情况的裕度都只能靠模型里是否显式加入了备用约束来体现而加了备用约束本质上已经是某种“人工鲁棒化”只是缺乏系统性的方法。所以确定性优化不是不优秀它优秀的前提是预测足够准。而微电网恰恰是最不满足这个前提的场景之一光伏受云层遮挡影响剧烈负荷受生产计划波动影响用户侧还经常出现无法预知的冲击性负载。面对这样的扰动需要一种直接把“不确定性”放进化问题的建模方法。1.3 平均意义的最优可能在极端情况下变成最差有人可能会说预测误差的期望是零啊今天高估20%明天低估20%平均下来不就很准吗问题在于微电网的成本函数不是对称的。光伏被高估时系统缺电可能需要以高价从主网购电或切除负荷产生巨额赔款光伏被低估时系统多出来的电可以通过储能或售电消化损失相对较小。同样是20%的误差一正一负带来的代价完全不对等。平均值掩盖了这种严重的不对称性。我希望得到的是这样一个方案不管光伏、负荷在未来怎么波动只要它们落在某个合理的范围内系统都能通过第二阶段调整安全度过且总成本不会突破某个可接受的上限。这就是两阶段鲁棒优化的核心目标——不是让平均成本最低而是让最坏情况下的成本可控。对于微电网这种“先保生存、再讲经济”的系统这个逻辑远比单纯的最小期望成本更贴合实际。2. 两阶段鲁棒优化的核心思想先定计划再随机应变2.1 第一阶段锁定“基调”第二阶段做“微调”两阶段鲁棒优化对应着微电网调度天然存在的两层决策结构。第一阶段是“日前决策”发生在不确定性实现之前此时我们不知道明天的光伏到底发多少、负荷到底用多少但必须提前决定那些难以快速改变的变量比如各台柴油机的启停状态储能系统的充放电计划或最大功率预留与上级配电网约定的购电容量上限可平移负荷的调度时段安排这些决策一旦锁定日内很难快速反转。第二阶段是“日内调整”发生在不确定性实现之后我们观测到实际的光伏出力和负荷水平在满足系统安全约束的前提下调整可以快速响应的变量比如柴油机的实际出力点储能的实际充放电功率从主网购电的实际功率我用一个旅行类比来向团队成员解释第一阶段相当于你出发前订好城市和酒店第二阶段是到达之后根据天气临时调整每天去哪些景点、吃什么。两个阶段缺一不可——只做第一阶段等于完全不考虑变故只做第二阶段等于否认有些决策必须提前锁定。2.2 min-max-min把天气当成一个对抗性对手两阶段鲁棒优化的数学模型通常写成下面的形式min_x c^T x max_{u∈U} min_y d^T y s.t. A x ≤ b B x E y ≤ g F u这个式子看起来很吓人但拆开来看并不难。最外层的min_x是第一阶段决策我们希望能最小化总成本最内层的min_y是第二阶段决策给定一种“天气场景”后我们总能以最优方式调整出力中间的max_u是最关键的部分——不确定性参数u在不确定集U内取值它站在我们的对立面试图找到让我们成本最高的那个场景。这就是典型的“先决策、后博弈、再应对”结构。用大白话说我们先把能定的计划定下来然后假设天气会对我们最不利最后在不利场景下尽量把损失压到最小。这个对手并非真实存在它是数学上用来逼近最坏情况的工具。最终的方案也许不是成本最低的但它能保证只要实际场景落在不确定集内无论发生什么系统都不会崩溃成本都不会失控。2.3 一句话版本不追求“平均最好”只保证“最坏不崩”我跟项目负责人汇报的时候对方听不懂min-max-min我换了个说法我不再问“明天最可能花多少钱”而是问“明天不管出什么幺蛾子最多会花多少钱我希望把上限压住”。这个视角转变非常重要。确定性优化像是一个只考虑“晴空万里”的出行计划两阶段鲁棒优化则像一个考虑“大到暴雨、航班取消、酒店满房”的备选方案池。它牺牲了一部分平均工况下的经济性换取的是极端工况下的安全底线。对于微电网来说这个“底线”往往意味着能否避免切负荷、能否避免大面积停电、能否避免超出联络线容量造成的事故。3. 不确定集建模鲁棒性的旋钮到底拧到哪3.1 箱式不确定集简单、直观、但容易过度保守不确定集U是两阶段鲁棒优化里最需要工程直觉的地方它直接决定了模型的保守程度。最初上手时我用的是最经典的箱式不确定集把每个不确定参数限制在一个区间内u ∈ [u_avg - Δ, u_avg Δ]其中u_avg是预测值Δ是预测误差的上限。这个集合的优点是建模极其简单Gurobi里写两个上下界约束就完事缺点也同样明显它隐含假设每个不确定参数可以同时取最坏值。以光伏和负荷为例如果把它们都框成箱式集合最坏场景就会是“光伏取最低值负荷取最高值”但这在现实中几乎不可能出现——光伏低谷往往对应阴天阴天温度低、负荷通常也低。为了这个“永远不可能发生”的场景模型会让柴油机近乎全天在线储能始终预留最大容量运行成本高得离谱。我在第一轮测试中算出来的调度方案比确定性优化的成本高出42%这还没算日内调整已经足以让投资人皱眉。3.2 预算不确定集限制“同时作怪”的参数个数解决过度保守的一个主流方法是预算不确定集Budget Uncertainty Set它额外加入一个约束限制不确定参数偏离预测值的“总量”Σ |u_i - u_avg_i| / Δ_i ≤ ΓΓ就是我们常说的鲁棒预算可以理解为“允许几个参数同时作怪”。Γ0时退化为确定性模型Γ越大模型越保守成本越高安全性也越高。工程上需要根据系统允许的风险水平来选Γ——如果微电网供电可靠性要求高、切负荷代价大Γ可以取稍大的值如果经济性压力大Γ取较小值即可。这里有一个关键经验Γ不应该拍脑袋定而应该用历史数据回测。我通常的做法是把过去一年的光伏和负荷预测误差拿出来统计实际发生过的最大累计偏离度再乘一个1.1~1.2的安全系数作为日常调度的Γ值。这样既有了数据支撑又留了工程裕度。3.3 光照与负荷的相关性一个经常被忽略的坑箱式不确定集还有一个隐藏问题就是忽略了变量之间的相关性。我遇到过很荒诞的算例结果最优调度方案在最坏场景下凌晨三点出现了“光伏出力从零突增到额定值”这种物理上不可能的情况原因是模型把每个时段的独立箱式约束都推到了极端但凌晨光伏本来就是0这个“最坏场景”完全是用数学构造出来的假象。要解决这个问题更合理的做法是把历史误差向量做协方差分析用主成分方向构造椭圆体不确定集或者用多面体不确定集包络误差样本的凸包。椭圆体不确定集在数学表达上更漂亮但线性化处理复杂工程中我更多使用多面体集合把历史样本中的极端点提取出来要求不确定参数落在这些点的凸包内。这样既保留了最坏情况覆盖能力从物理上筛掉了那些完全不可能出现的场景组合。提示不确定集的形状往往比不确定集的大小更影响求解结果。建模时先画出历史误差的散点图看看真实误差分布的轮廓再决定用箱式、预算式还是多面体式不要一上来就套公式。4. CCG算法求解手记主问题-子问题迭代里的细节4.1 算法主流程两阶段鲁棒优化不能直接丢给求解器因为max-min式子本身是一个双层优化。目前最主流的求解算法是列与约束生成法Column-and-Constraint Generation简称CCG我最初的代码框架大致如下初始化: LB -inf, UB inf, k 1 选一个初始不确定场景 u(1)通常取预测值 循环: 1. 求解主问题 MP(k): min c^T x θ s.t. A x ≤ b θ ≥ d^T y_i B x E y_i ≤ g F u_i, i 1..k → 得到 x(k), θ(k), 目标值 objMP LB max(LB, objMP) 2. 修复 x x(k)求解子问题 SP(k): max_{u∈U} min_y d^T y s.t. B x(k) E y ≤ g F u → 得到最坏场景 u(k1)子问题目标值 objSP UB min(UB, c^T x(k) objSP) 3. 如果 UB - LB ε退出 4. 否则把新场景 u(k1) 对应的变量 y(k1) 和约束加入主问题 k k 1回到步骤1主问题负责给出一个“当前已知最坏场景集合下最优”的决策方案并提供下界子问题则负责寻找比当前已知场景更恶劣的新场景并提供上界。当上下界收敛到足够接近时我们就认为找到了最优的鲁棒方案。4.2 为什么在实际项目中我选CCG而不是Benders第一次尝试求解两阶段鲁棒问题时我本能地用了Benders分解因为它原理更经典、文献更多。但实际跑下来Benders的收敛速度真的让人着急。Benders每次只往主问题里添加一条割平面相当于每次只告诉主问题“你这个方案在某个场景下会超支”而CCG每次直接加入一整套新的场景约束和决策变量信息量大得多。在微电网这种模型规模不算大的场景中CCG通常十几轮就能收敛Benders往往要几十轮甚至上百轮而且中途还容易出现振荡。用表格对比一下两者在工程上的直观感受对比维度CCGBenders分解主问题添加的信息完整新场景及约束一条割平面典型收敛轮数10~20轮50轮以上对偶变量依赖依赖子问题对偶解依赖子问题对偶解实现复杂度中等中等偏低多阶段扩展性可扩展嵌套CCG较困难当然Benders在特定问题结构下有它的优势但在微电网两阶段鲁棒调度上CCG是更务实的选择。很多开源库和论文代码也默认实现了CCG踩坑成本更低。4.3 主问题规模膨胀的规避手段CCG迭代中最让人头疼的问题是主问题规模会随迭代次数快速增长——每加一个场景就新增一组决策变量和约束。我印象最深的一次模型跑到第14轮时主问题的约束数量从最初的800条涨到超过5000条单次求解时间从3秒涨到40秒整个迭代变得异常缓慢。后来我用了几个实用的规避手段截断无用场景在每轮迭代后检查主问题中各场景约束的影子价格如果某些历史场景在所有迭代中从未激活即非绑定约束可以考虑将它们移除只保留“有价值的”极端场景。限制最大迭代数实际操作中很多模型在10轮以内已经达到了工程可接受的精度没必要强求到严格收敛。我会设置一个最大迭代轮数超过后取当前UB对应的方案。固定第一阶段变量如果项目时间紧可以先跑几轮CCG然后固定第一阶段变量只优化第二阶段这样能快速得到一个可行方案再逐步放松。5. 子问题单层化把双层优化变成单层模型的工程细节5.1 三种路线KKT、强对偶、枚举CCG里最难啃的骨头是子问题的求解。子问题的结构是max_u min_y求解器没法直接处理这种双层结构必须先把它单层化。实践中通常有三条路线KKT条件法把内层min_y的必要条件KKT条件作为约束加入外层再通过大M法线性化互补松弛条件。好处是适用性强坏处是KTT条件里包含非线性项大M法的参数选择非常敏感数值上容易出问题。强对偶法利用线性规划的强对偶定理把内层min_y替换为对偶问题max_λ和外层的max_u合并问题变成单层max问题。这是我在实际项目中最常用的方案。场景枚举法把不确定集离散成有限个场景逐个枚举求取最坏值。只适用于不确定集特别小的场合连续不确定集基本不可行但在做快速验证时可以用于交叉检验。我强烈建议只要问题能写出对偶形式优先选强对偶法。它不像KKT那样需要处理互补松弛条件的线性化模型的数值稳定性好很多。5.2 强对偶转换后的双线性项与大M线性化强对偶转换本身不复杂但转换之后通常会出现一个棘手的双线性项不确定变量u与对偶变量λ的乘积u·λ。这个乘积既不是凸的也不是凹的无法直接交给求解器。这是两阶段鲁棒优化数值实现中最常见的“隐性杀手”。一种工程处理思路是把不确定变量离散化。比如把光伏出力误差从连续区间[-Δ, Δ]均匀离散成20个点每个离散点对应一个二进制变量u·λ就可以等价改写为线性约束加一个大M约束。代价是离散精度越高整数变量越多求解越慢。我当时试过把每个不确定参数离散成12档模型规模爆炸到求解器在五分钟内给不出可行解后来把档位缩减到5档速度和精度才勉强平衡。另一种思路是利用不确定集的特殊结构。预算不确定集常常可以用对偶变量做更漂亮的变形最终得到的模型没有双线性项。这部分推导在文献里有很多变体实际操作中我建议大家先写好通用的大M线性化版本再根据具体集合结构做优化。注意大M的取值一定要做灵敏度实验。我遇到过M设得太小导致求解器直接报“不可行”M设得太大导致所有约束的数值精度失控。常规做法是取约束中变量量纲最大值的50~100倍再验证一遍最优解是否发生变化。5.3 对偶变量的提取与校验子问题用强对偶法单层化后对偶变量的提取也容易踩坑。我最早用Gurobi的Python接口时需要通过Pi属性提取对偶值但因为子问题在原问题上做了一些等价变换提取出来的对偶变量值经常对不上原问题的经济含义。调试了很久才发现问题出在对偶问题之间的符号约定上——Gurobi默认对约束给出非负对偶变量如果原约束是或者等式提取逻辑就要做相应调整。我建议在实现时写一个简单的小算例做校验构造一个只有两三个变量的确定性线性规划手算出对偶解再对比求解器提取的结果。确认无误后再跑完整的鲁棒优化否则后面出了问题很难分清楚是模型错误还是提取错误。6. 跑模型时踩过的坑6.1 大M值不是越大越好这个坑在5.2节提过但值得单独拿出来再强调一次。我在做双线性项线性化时一度为了“确保等价”把M设成1e7结果连续好几次求解器返回的对偶变量数值很不稳定还出现主问题与子问题往返振荡、上下界不收敛的情况。后来逐步把M调小发现M5e3时模型不仅收敛计算时间还缩短了30%。原因是过大的M会把原本稀疏的约束矩阵变成数值病态的矩阵求解器内部做浮点运算时误差被放大导致退化解增多。很多论文里写“M取一个足够大的数”但足够大不等于越大越好而是要在保持等价性的前提下尽量小。6.2 对偶变量不唯一导致最坏场景来回跳运行中期我遇到一个很怪异的现象算法已经迭代了十几轮UB和LB却始终无法收敛而且子问题找出的最坏场景在几个固定场景之间反复横跳。后来定位到原因子问题线性规划出现了退化即同一个最优值对应多个对偶解每次求解器返回的“最坏场景”是这些解里的一个呈现随机性。解决这个问题的有效办法是给子问题加一个微小的正则项比如用ε·‖λ‖²二次项替代对偶变量中的自由项或者在对偶问题的目标函数里加一个极小权重系数来打破对称性。虽然这会让子问题的最优解有极其微小的偏差但对整体迭代收敛的稳定性帮助非常大。6.3 伪最坏场景凌晨的光伏波动问题3.3节提到过这个坑但它背后的教训值得再展开一点。第一次用箱式不确定集跑出“凌晨光伏从零突增”的伪最坏场景后我意识到问题出在不确定集没有引入时序耦合关系。光伏出力的不确定偏移量在白天是显著的夜间应该接近零独立箱式约束却允许每个时段都取最大偏离。后来的修复方式有两种把不确定偏移量按时段乘以一个可变的缩放系数白天系数为1夜间系数接近0对不确定偏移量按日总辐射量做归一化处理确保各时段的总偏离不会超过物理可达到的上限。这类“伪场景”不仅会浪费备用容量还会误导调度人员对系统风险的认识——看到最坏情况下凌晨还要保留大量柴油机备用第一反应就是模型太保守从而丧失对结果整体的信任。所以不确定集的物理一致性比数学表达更重要。6.4 收敛判据的选择工程上别太贪CCG迭代的收敛判据通常是上下界相对误差(UB - LB) / LB ≤ ε学术论文常设置ε 0.01但工程调度场景中一味追求1%的精度并不可取。我做过对比实验同样一个微电网模型ε0.05时只需8轮迭代ε0.01时需要16轮但两者的调度方案成本只相差2.3%而极端场景下的系统安全表现几乎完全一致。更重要的是16轮的求解时间接近8轮的三倍对于日内滚动调度来说这半小时的计算时间会压缩后续人工复核的窗口。我现在的经验是应用场景建议收敛判据说明日前调度提前24h0.01~0.03时间充裕可追求高精度日内滚动提前4h0.05快速给出可用方案实时调整提前15min0.10以可行性为主严格最优不再是首要目标7. 两阶段鲁棒优化的适用边界与落地建议7.1 适用场景的判断标准不是所有微电网调度问题都适合用两阶段鲁棒优化。根据这段时间的实战经验我认为满足以下条件时才值得用它不确定性显著光伏、风电、负荷的预测误差对系统安全有实质性影响简单预测修正无法消除决策有明确的先后顺序日前必须预先决定一些“难以快速改变”的变量日内才能做调整两阶段结构贴合实际系统对低概率高影响事件敏感切负荷、频率越限等事故的代价足够高值得牺牲部分经济性来换取安全裕度有较可靠的历史数据支撑不确定集建模如果连基本的历史误差统计都没有不确定集的参数只能靠猜结果的可信度会大打折扣。7.2 不建议使用的场景有两类项目我反而建议谨慎使用两阶段鲁棒优化。一类是预测误差本身极小比如负荷相对平稳、光伏占比很低的系统鲁棒优化带来的成本提升可能超过安全收益性价比很低。另一类是第二阶段调整手段有限的系统——如果第二阶段变量很少可调整空间很小鲁棒优化本质上就是在额外惩罚第一阶段这时候用MPC或随机规划可能更合适。此外还有一点容易被忽略两阶段鲁棒优化输出的是一组“保守”的日前计划它本身没有说明这个计划到底有多保守。如果团队缺乏做鲁棒性回测的经验直接上线可能引发运行人员对结果的不信任——他们看到柴油机频繁在线会质疑模型是不是坏了。我当时花了很多精力做历史场景回测用过去365天的实测数据逐一验证调度方案才让各方接受了多出的一笔备用成本。7.3 落地时的四点建议从确定性模型起步先把确定性的MILP调度模型跑熟验证设备的启停约束、爬坡约束、储能SOC逻辑确保所有约束在物理上是合理的再引入鲁棒优化。否则鲁棒优化会把某个隐藏的建模错误放大成莫名其妙的“最坏场景”。用历史场景做鲁棒性验证把历史实测光伏和负荷作为测试场景检查调度方案是否在不切负荷的前提下满足所有约束。这个验证结果能直观地体现鲁棒优化的价值也方便向非技术背景的决策者解释。对不确定集参数做灵敏度分析Γ和Δ每个都做3~5组对比实验画出一条“成本随鲁棒性提升而增加”的曲线让项目组根据这条曲线选择可接受的折中点。保留应急修正机制鲁棒优化不能完全替代实时安全监控第二阶段约束也可能在极端情况下失效。我最终的方案里仍然保留了自动切负荷和低频减载装置作为最后防线鲁棒优化负责的是“减少调用最后防线的概率”而不是替代它。最后分享一个个人体会。刚做完两阶段鲁棒优化模型那阵子我满脑子都是数学变换和求解器参数直到一次系统真遇到罕见的连续阴雨天气光伏出力连续三天低于预测值30%模型给出的调度方案硬是让系统一次都没有切负荷我才真正理解“微电网的生存法则”这句话的分量。优化算法不是万能的但对那些“平均意义上的好方案扛不住极端扰动”的微电网项目来说两阶段鲁棒优化确实是一种值得投入的生存策略。如果你也在调试类似的模型先从不确定集和数据质量入手大概率能少走我当初走的那些弯路。
RELATED READING

延伸阅读

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