ARTICLE · INTELLIGENCE

战地情报 · 详情页

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

微电网两阶段鲁棒优化调度实战:CCG求解与参数调优

微电网两阶段鲁棒优化调度实战:CCG求解与参数调优 微电网的能量管理调度说穿了就是一场跟不确定性掰手腕的游戏。光伏和风电出力飘忽不定负荷说涨就涨分时电价还会跟着波动——如果你只按预测值去做日前计划第二天实际运行八成要出问题要么弃光限电要么被迫切负荷要么柴油机烧得比计划多得多。我最早做微电网系统调度时用的就是经典的确定性优化模型说白了就是拿昨天的天气预报去买明天的菜结果被现实教育了好几次。后来把两阶段鲁棒优化引入微电网调度才真正理解了什么叫为最坏情况留一手。这篇文章把我实战中踩过的建模坑、CCG求解套路、参数调节经验一并整理出来供正在折腾微电网调度优化的同行参考。内容偏工程向适合有一定优化建模基础、想快速上手鲁棒优化的朋友。1. 为什么微电网调度必须面对不确定性1.1 不只在光伏和风电负荷本身也是乱来的微电网系统中的不确定性来源远比一般教科书里写的要复杂。最直观的是光伏出力和风电出力多云天气下光伏出力可以在几分钟内从额定功率跌到20%风电更是受风速影响剧烈波动。但很多刚上手的人会忽略负荷侧的不确定性——居民的日常用电有很强的随机性尤其是夏季空调负荷往往是早上预测一个值到了下午直接超预期20%。除此之外现货市场的购电价格、柴油机的燃料成本波动也都属于影响调度方案的不可控因素。这些不确定性叠加起来会让传统的确定性调度模型显得非常脆弱。确定性模型的做法是把所有随机变量取一个预测值通常是期望值或某个典型场景值然后在这个理想世界里求最优解。但现实是当预测值和实际值出现偏差时原计划的可行性和经济性都会受到冲击。比如日前计划安排储能夜间充电、白天放电结果白天光伏出力突降这时候储能可能已经被放到SOC下限根本无力支撑最终只能启动柴油机高价顶上甚至切负荷。1.2 确定性优化、随机优化、鲁棒优化怎么选面对不确定性优化领域有三条主流路线确定性优化、随机规划stochastic programming和鲁棒优化。确定性优化最简单但抗风险能力差适合对预测精度极有信心的场景。随机规划则假设不确定参数服从已知概率分布通过采样生成多个场景来求期望最优解精度好但计算量爆炸而且概率分布本身就很难准确获取。鲁棒优化走的是另一条路不假设概率分布只定义不确定参数的取值范围不确定集然后优化最坏情况下的系统运行成本。它的核心逻辑很好理解——既然我摸不准明天的光伏到底发多少电那我就假设它可能落在某个区间内任意取值并求在这个区间内最恶劣情形下的最优调度方案。这样得到的解不一定是在每个场景下成本最低的但能保证在任何场景下都可行、都不会出大问题。两阶段鲁棒优化在微电网里格外合适是因为微电网调度天然就有先决策、后调整的两阶段性日前阶段要提前定下机组启停、储能充放电计划等无法轻易更改的决策日内阶段则可以根据实际运行情况对机组出力、储能功率、切负荷量等进行快速调整。前者叫here-and-now决策后者叫wait-and-see决策。两阶段鲁棒优化正好把这个结构嵌进了数学模型中。2. 两阶段鲁棒优化的模型搭建思路2.1 先决策后调整数学模型长什么样先给出一个标准的微电网两阶段鲁棒调度模型框架。假设微电网系统包含光伏、风电、柴油发电机、储能和负荷。第一阶段变量记作 x代表日前必须确定的变量包括柴油机的启停状态、日前计划的储能充放电功率、与主网的交换功率计划等。这些变量一旦确定在日内阶段只能微调不能随意推翻。第二阶段变量记作 y代表不确定参数实现之后才能做出的调整决策包括柴油机实际出力、储能实际充放电功率、弃风弃光量、切负荷量等。不确定参数 u 则包括了光伏出力、风电出力、负荷需求、购电价等实际值与预测值的偏差。整个模型的目标函数是典型的三层结构min_x { c^T x max_{u∈U} min_{y∈F(x,u)} d^T y }翻译成人话就是第一阶段的投资/计划成本 c^T x加上在最坏不确定性场景下的第二阶段运行成本 d^T y 的最大值两者之和最小化。内层的 min 表示在给定第一阶段决策 x 和不确定性 u 后微电网运行调度会以最低成本做出响应中间的 max 则是说敌对的自然会在不确定集 U 内选择让我们最难受的那个场景。这个 max-min 结构就是两阶段鲁棒优化最核心、也最让新手头大的地方。直接求解不行因为内层本身就是一个优化问题外层又在优化一个包含优化问题的目标这是个三层嵌套问题。主流解法是把它拆成一个主问题 一个子问题交替迭代求解也就是列约束生成算法Column-and-Constraint GenerationCCG。2.2 不确定集盒式、预算约束与保守度不确定集 U 的选择是整个模型中最考功力的环节。用得最多的是盒式不确定集box uncertainty set也就是每个不确定参数在一个区间内独立波动u_i ∈ [u_i^min, u_i^max]盒式集的好处是简单直观坏处是太保守。它假设所有不确定参数同时取到最坏值这在现实中几乎不可能发生——光伏不会同时从100%跌到20%、负荷又同时从100%涨到130%、电价还恰好飙到最高。如果按这个最极端情况设计调度结果就是你为了一个几乎不可能出现的场景付出了极高的日常运行成本。因此更常用的是带预算约束的多面体不确定集。在盒式集基础上加一个总偏差预算约束 ΓΣ_i ( |u_i - u_i^nom| / Δu_i ) ≤ Γ其中 u_i^nom 是预测值Δu_i 是最大允许偏差。Γ 就是不确定预算它限制了所有不确定参数实际偏离预测值的总幅度不能超过 Γ。Γ 越小模型越乐观、成本越低Γ 越大模型越保守、抗风险能力越强。实际操作中一般取 Γ 为区间 [0, N] 内的整数N 是不确定参数的个数然后通过扫描不同 Γ 值来画帕累托曲线最终由决策者拍板。2.3 约束条件里容易翻车的细节约束条件是模型的正菜也是新手容易翻车的重灾区。以我常用的微电网模型为例核心约束有这几类功率平衡约束柴油机出力 光伏出力 风电出力 储能放电 购电 切负荷量 负荷需求 储能充电 售电。注意这是一个瞬时平衡等式在鲁棒优化里第二阶段的内层 min 问题中它要作为含不确定参数 u 的约束处理是u的函数。柴油机运行约束包括出力上下限、爬坡约束、最小启停时间约束。第一阶段决定启停状态第二阶段决定出力两者之间通过启停状态变量耦合。储能约束SOC递推方程、SOC上下限、充放电功率上下限、充放电状态互斥。SOC递推式里有一个易错点如果你允许储能同时处于充电和放电状态通过互斥逻辑约束那么第二阶段子问题就引入了二进制变量内层 min 变成混合整数规划对偶化会变得困难得多。我的做法是直接建模成可正可负的单变量功率配合SOC递推避免在子问题里出现二进制变量。电网交互约束与主网的交换功率在一个区间内连续可调但很多微电网场景下你还得考虑向上级电网购电的阶梯电价结构这会引入二进制变量。如果这个量在第二阶段是可调的同样会让子问题变味需要仔细处理。3. 实战CCG算法的完整求解流程3.1 主问题与子问题怎么划分两阶段鲁棒优化最主流的求解算法就是CCG。它的思路是把原问题拆成一个主问题Master Problem, MP和一个子问题Subproblem, SP通过子问题找最坏场景、主问题吸收最坏场景的方式不断迭代逼近最优解。主问题包含第一阶段变量 x以及一组不断增加的最坏场景下的第二阶段变量 y。每轮迭代子问题找出的最坏场景会被作为一个新的离散场景 u_k 加入主问题同时为这个场景引入一组新的第二阶段变量 y_k 和相关约束。主问题最初往往只包含第一阶段约束和一个无用的初始场景然后逐步扩大规模。子问题则是在给定第一阶段变量 x 的取值后求解内部的 max_{u∈U} min_{y∈F(x,u)} d^T y 这一层。这个 max-min 结构没法直接扔给求解器需要先处理内层的 min内层是线性规划可以通过对偶理论转化为对偶最大化问题把原来的 max-min 变成 max-max即直接求一个最大化问题。这里唯一需要小心的是内层对偶问题中目标函数会出现 u 与对偶变量的乘积项双线性项这正是最坏场景搜索的难点。3.2 大M法、KKT还是对偶重写处理子问题内层 min 有三种常见路线。最推荐的是对偶重写将内层LP写出对偶形式把 max-min 变成单层的 max 问题。目标函数中的 u 和对偶变量 π 相乘构成双线性项典型做法是利用不确定集 u∈U 的极值点性质——对于多面体不确定集最坏场景一定在极值点上取得。这样可以把双线性项线性化对每个不确定参数如果其对偶系数为正u 取区间上界如果系数为负u 取区间下界。再结合预算约束 Γ这个推理需要扩展成判断哪些参数最值得偏离预测值通常转化为带选择变量的混合整数线性规划或者直接用求解器处理带双线性项的二次约束问题。另一条路线是用KKT条件。将内层 min 替换为它的KKT条件Stationarity Primal feasibility Dual feasibility Complementarity slackness其中互补松弛条件是非线性的需要用大M法引入二进制变量转成线性约束。KKT路线的优势是结构清晰缺点是大M的选取非常影响求解稳定性而且变量膨胀得很厉害。我实测下来中小规模微电网问题几十个时段、十来个不确定参数两条路线都跑得动但对偶重写收敛更快代码也更干净。补充一个我在代码里实测的结论如果你用YALMIP工具箱搭模型可以用optimize命令直接求解子问题里的 max-min 嵌套结构YALMIP支持部分鲁棒优化语法但在较大实例上速度惨不忍睹建议还是手动拆解。3.3 核心代码框架与迭代参数设置下面给出一个用MATLAB YALMIP CPLEX/Gurobi 实现CCG迭代的简化框架其中主问题、子问题都用YALMIP建模% 初始化 LB -inf; UB inf; x0 []; k 1; eps 1e-3; while UB - LB eps % --- 主问题 --- MP build_master(k); % 含x与k个最坏场景的y_k optimize(MP, c*x sum(d*y_k), sdpsettings); x_current value(x); LB c*x_current sum(d*value(y_k)); % --- 子问题 --- SP build_subproblem(x_current); % 对偶化的max问题 optimize(SP, -obj_sp, sdpsettings); UB min(UB, c*x_current value(obj_sp)); u_worst value(u); % --- 添加最坏场景到主问题 --- MP_add_cut(u_worst); % 添加新场景及其约束 k k 1; end几个关键细节一是上下界的定义。标准CCG中主问题给出下界因为主问题只考虑了部分场景是原问题的一个松弛子问题给出上界因为给定了一个可行 x得到的是原问题的一个可行解对应的成本。二是收敛判据一般用 (UB - LB)/UB 小于某个阈值比如0.1%。三是子问题里的 u 要声明为优化变量且只能在不确定集 U 内取值这样才能求出最坏场景。迭代次数方面我实际跑的24时段微电网模型不确定参数约8个Γ取24时CCG一般512轮收敛。对比Benders分解主子问题对偶割平面法CCG在主问题里加的是一整个最坏场景及对应变量而不是一条割平面收敛速度显著更快——这是我推荐CCG而不是传统Benders的最重要原因。4. 参数标定与结果解读实践4.1 不确定预算Γ怎么定才不保守也不冒险Γ的选择直接决定调度方案的性格也是实战中最需要反复试验的旋钮。我常用的做法是先做一个基准案例Γ0即完全确定性场景记录最小运行成本然后逐步增大Γ每次跑一遍完整的CCG流程记录最坏场景下的运行成本和对应的调度方案特征。以我手头一个典型系统为例光伏500kW、风电300kW、柴油机200kW、储能250kWh峰值负荷400kW。Γ0时日运行成本大约8200元Γ2时最坏情形成本上升到8600元左右增幅约5%Γ4时成本进一步到8900元增幅约8.5%但当Γ再大之后成本增长明显放缓曲线趋于平缓。观察调度方案会发现一个有趣现象Γ增大到一定程度后方案的主要变化不再是多开柴油机而是改变储能的充放电策略和弃风弃光策略——比如预留更多储能容量应对晚间光伏归零的场景、在负荷低谷期多充电以摊平风险。这说明鲁棒优化不是简单粗暴地多备一台柴油机而是让系统各处都在为不确定性留余量理解这一点对向领导或业主解释方案价值非常有用。4.2 鲁棒解的成本组成与决策差异鲁棒解和确定性解的成本差距主要体现在三块柴油机油耗因为爬坡和启停变动而增加、储能因更频繁的充放电导致循环寿命损耗增大、以及可能需要向电网购买更高价格电量。把这些拆开看能帮你弄清楚鲁棒化的代价花在哪了。在我的案例中确定性解里储能一天完成1.2个完整循环鲁棒解Γ3降到0.9个循环。表面上看储能利用得少了成本应该下降但因为柴油机在更多时段处于运行状态燃料成本反而上升。这个看似矛盾的结果其实揭示了一个很实在的道理鲁棒优化的目标是最坏情形下的总成本不是最大化储能利用率。你觉得储能没吃饱很浪费但如果光伏失守时储能恰好放空了那才是最贵的教训。另外鲁棒解还有一个容易被人忽视的好处切负荷量显著减少。确定性方案在最坏场景下可能需要切负荷50kWh鲁棒方案把切负荷压到了5kWh以内。对工业园区一类用户来说一次非计划停电或限电的损失可能远超多烧的柴油成本这往往才是微电网配置鲁棒调度的真正经济驱动力。4.3 求解效率与规模化实践24时段、单微电网实例对现代求解器来说是小菜一碟但这几年微电网群多个微电网互联和综合能源系统电-气-热耦合成为热点问题规模指数级上升。我的经验是在两阶段鲁棒优化框架下处理大规模问题有四个务实的加速手段第一场景缩减。不确定参数虽然多但很多是高度相关的比如同一片区域的光伏出力10个光伏电站的波动几乎同涨同跌。可以用主成分分析或K-means聚类把不确定性结构化地压缩再去构建不确定集能大幅减少子问题中的变量数量。第二热启动。CCG每轮迭代都重新解同一个主问题只是多了几个约束把上一轮的解作为初始可行起点能省不少求解时间。第三并行化多Γ扫描。如果要画Γ-成本曲线不同Γ值之间彼此独立直接并行跑就好。第四数值缩放。微电网模型里量纲差异巨大功率可能是几百kW成本系数可能是几元/kWhSOC在0到1之间——好的缩放在实际求解中能免去一半以上的数值问题。5. 常见问题与排坑记录5.1 子问题对偶无界与病态数值CCG最经典的翻车现场子问题对偶化之后求解器警告模型无界unbounded。这通常是因为原内层min问题本身不可行——给定第一阶段决策 x 后第二阶段在某些不确定性场景下无论如何都无法满足功率平衡和储能约束。根源往往出在第一阶段决策太激进比如把储能放电计划排得过满没给日内调整留余地。排查思路先检查第一阶段决策的可行性看功率平衡是否留了足够裕度再看是否有约束被遗漏比如储能初始SOC和末端SOC约束。一个工程性解法是给子问题增加一个人工场景兜底机制在所有不确定参数的预测值场景下验证第二阶段模型可行如果连这个场景都撑不过去那问题一定在约束建模上。数值病态的大头则是大M取值KKT路线里互补松弛约束的大M如果取得过大超过1e6求解器在数值上就会很痛苦轻则收敛慢重则给出错误的最优性判断。5.2 收敛慢或者根本不收敛先查这几处CCG不收敛或者收敛速度拖沓九成出在三个地方。一是迭代间隙里UB来回震荡但LB爬不上去。这个症状通常说明子问题找出的最坏场景其实没有抓住要害问题多出在不确定集的定义上——预算约束Γ写错了或者某些参数被错误地固定到了名义值。二是主问题规模增长过快每轮迭代加的变量太多导致单轮求解时间爆炸。这时要考虑是不是在建模时把本来独立的场景变量写重复了导致冗余约束。三是子问题求出的最坏场景和上一轮重复这通常意味着主问题已经包含了最优解的最坏场景集只是上下界的间距还没拉到阈值内把收敛判据从绝对值改成相对值UB-LB/UB 就能解决。经验之谈我踩过最大的一次坑是在SOC递推约束里把时段索引写错了1格导致储能状态变量跨时段错位。鲁棒优化里这类bug特别隐蔽因为主问题在迭代中始终可行只是子问题给出的最坏场景总在同一片区域打转让我一度怀疑是算法问题。排查此类问题最有效的方式是写一个小的确定性版本把 Γ 设为0跑通之后再逐步放开。鲁棒部分出bug通常在最外层就能暴露出来。5.3 求解器选型与参数调优速查关于求解器我给的总体建议是中小规模用Gurobi或CPLEX都行两者对LP/QP/MIP的求解性能都足够优秀CCG框架下主问题和子问题基本都是MILP或LP不需要花哨的功能。YALMIP负责建模求解器用Gurobi做后端是我最顺手的组合。开源方案的话CoPT和HiGHS在部分LP子问题上表现不错但MILP性能相比商业求解器仍有差距迭代次数多的大实例不建议。给一个我常用的求解器参数配置主问题开启presolve、使用MIP gap阈值1e-4子问题是纯LP时把与方法相关的数值容差设置为1e-7如果子问题引入了二进制变量比如带预算选择的线性化则保持MIP gap在1e-4即可。另外强烈建议开启solver的deterministic mode鲁棒优化的迭代结果需要可复现性否则调参时你根本分不清是算法改进还是求解器的随机抖动。6. 实测结果与个人体会最后聊几句个人在实际项目里的心得。两阶段鲁棒优化不是银弹它的适用前提是你真的说不清不确定参数的分布但能拍着胸脯给出取值范围。如果你的历史数据充足、分布特性稳定随机规划也许更划算因为鲁棒优化的保守性会平白牺牲一部分经济性。反过来如果你的微电网里光伏渗透率很高又牵涉到不能断电的关键负荷鲁棒优化的最坏情形兜底价值就会非常突出。我自己的经验法则是先用确定性模型跑通业务流程再做一个Γ从0到最大值的灵敏度扫描把成本-风险曲线画出来给业主看让他直观地知道多安全一档需要多付多少钱。这个曲线本身往往比最终的调度方案更能说服人。现实世界里业主真正关心的是风险敞口能不能量化、可控而不是精确到小数点后三位的数学最优。两阶段鲁棒优化恰好给了你这个工具——它把底气变成了可计算、可对话的东西。最后再分享一个实践中的小细节鲁棒优化跑出来的方案在执行层面一定要和运行人员讲清楚最坏场景是什么样。我见过不少调度员拿到鲁棒方案后发现实际运行中没有出现设想的恶劣场景就误以为模型不够聪明偷偷调回原来的确定性策略。其实预先告诉团队这个方案在光伏全拉胯、负荷全拉满时依然撑得住他们反而会越来越信任模型。技术方案落地一半靠算法另一半靠把算法讲成人话。
RELATED READING

延伸阅读

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