两阶段鲁棒优化与CCG算法:微网电源容量配置实战 简介一份面向微电网规划研究的两阶段鲁棒优化源码包聚焦风电、光伏、储能与燃气轮机的容量配置问题。程序以不确定性建模为基础一阶段决策储能、风电、光伏的容量二阶段优化实际出力并借助列与约束生成CCG算法求解两阶段鲁棒问题适合电力系统方向的学生和科研人员参考。资源共含19个文件以MATLAB代码.m为主辅以docx模型说明、PDF学术文献、xlsx典型日数据、插图及readme文本压缩包整体约3.76MB。文档部分对模型构建、矩阵推导和CCG求解过程做了拆解并附有鲁棒机组组合、微网经济调度等参考文献便于对照代码理解方法细节。目前已有1331人学习下载对打算入门两阶段鲁棒优化或开展微网容量配置研究的读者颇有参考价值。1. 两阶段鲁棒优化算法微网电源容量配置的“最坏情况”设计逻辑做微网电源容量配置最怕的不是平均负荷而是极端工况。光伏午间大发、傍晚陡降负荷在高温天直线飙升如果你按一个确定性的典型日做规划配置出来的储能容量和柴油机台数大概率在连续阴雨天的晚高峰直接翻车要么切负荷要么让柴油机超额定功率运行整个微网的可靠性承诺变成一张废纸。两阶段鲁棒优化算法就是用来解决这个问题的把光伏出力、负荷这些不确定量建模成一个集合第一段先定购建方案买多少光伏、风机、储能、柴油机第二段在最坏的不确定性实现下做运行调度让即便碰到集合里最恶劣的那一天系统依然有可行运行方式。它跟随机规划的区别在于随机规划要给不确定性配概率分布而鲁棒优化只用给不确定量的波动范围和预算值数据需求更少更适合早期规划阶段没有完整历史曲线的场景。我在这类项目里最常见的落地场景是园区微网或海岛微网设备容量几兆瓦到几十兆瓦投资决策周期十年以上运行调度按小时级颗粒度做。这套方法能直接回答“储能配多少能扛住最坏天气”“柴油机是不是必须留一台作备用”这种董事会要听的问题。本文从建模、求解到参数设置和避坑按一条能复现的主线讲完。2. 决策结构两层拆解先定“买什么”再算“怎么用”2.1 为什么必须两层决策不能一步到位微网容量配置从数学上看是一个混合整数规划嵌套的问题购建变量光伏台数、风机台数、储能额定容量、柴油机台数是整数或半连续变量一旦定下来至少未来十年不变运行变量每小时出力、储能充放电功率、柴油机启停是连续整数混合变量每天都可以重新优化。由于投资决策的生命周期远长于运行调度的生命周期这两个决策天然分属不同阶段。如果只用一层确定性优化把未来25年的8760小时负荷和光伏曲线全部当作已知参数求解器会给出一个“看起来最优”的方案但这个方案在真实运行中没有任何保护光伏按预测出力的80%出力负荷按预测值的120%出现储能SOC穿底只能甩负荷。两阶段鲁棒优化的做法是第一阶段固定购建变量x第二阶段面对一个不确定集合U求在最坏情况下的运行成本最小化。整体目标函数写成 min_x (投资成本 max_u min_y 运行成本)其中y是运行变量u是不确定量。这个结构决定了求解思路不能直接丢给一个求解器必须做主子问题迭代。2.2 目标函数和约束怎么写投资年值、运行成本与功率平衡目标函数先落到可以编程的数学形式。第一阶段目标为年化投资成本把设备全寿命周期的总投入用等年值系数折算成每年的现金流公式是 C_inv Σ_i r_i / (1 - (1 r_i)^(-L_i)) × 单位投资成本 × 容量其中r_i是折现率L_i是设备寿命。这样规划周期里的投资和运行成本才可加。第二阶段运行成本是每小时的总费用包括柴油机油耗、购电费用、储能老化折算和弃光弃风惩罚其中购电费只在并网型微网里出现离网微网则只有柴油和储能老化项。约束条件分两层。第一层是购建变量本身的边界和耦合光伏和风机的安装空间上限、储能额定功率与额定容量之间的倍率关系、柴油机台数整数约束。第二层是运行阶段约束每个时段必须满足电功率平衡光伏出力 风机出力 柴油机出力 储能放电 购电 负荷 储能充电 弃光/弃风同时储能SOC要满足时序递推关系柴油机出力要在最小技术出力与额定容量之间。这里有一个很关键的建模细节不确定性进入约束的方式。一般把光伏出力和负荷写成“预测值 波动量”波动量落在给定的上下界内。2.3 不确定性集合的三种常见构造方式及选型理由盒式集合最直接每个不确定参数独立地在区间内波动u ∈ [u_min, u_max]。它的缺点是太保守因为实际工程中不太可能所有光伏出力同时降到最低、所有负荷同时升到最高。所以工程上很少单独用盒式集合。预算集合是在盒式的基础上加一个调节参数Γ表示“最坏情况下同时偏离预测值的时段数或参数个数有限”。比如光伏在一天24小时里有Γ个小时可以同时取到最坏值其余时间取预测值。这种集合比较实用预算值Γ从0到24扫描一遍就能得到从确定性到完全保守的一条曲线方便业主看到“多花钱买多少可靠性”。椭球集合用二次约束限定不确定性向量的范数数学形式更光滑但引入二次项后求解难度上升我在工程里用得少。3. 用CCG求解两阶段鲁棒模型主问题与子问题迭代3.1 从Benders到CCG为什么列与约束生成更适合这个模型两阶段鲁棒优化最常见的大规模求解方法有Benders分解和列与约束生成CCGColumn-and-Constraint Generation。Benders分解把第二阶段的运行变量投影到第一阶段的割平面里用对偶乘子加割但容易遇到多面体对偶退化的问题割质量差迭代几百轮还不收敛。CCG的思路是主问题里显式加入第二阶段的运行变量和约束每轮迭代把最坏场景对应的那组变量和约束“列”进主问题里让主问题规模逐步增大但每个割是多面体的顶点约束收敛速度比Benders的割快很多。所以目前主流论文和工程实现里CCG几乎是标准做法。CCG的完整流程是初始化一个最小不确定集合K{u0}求解主问题得到购建方案x和运行方案y然后把x代入子问题求解得到最坏场景u和运行成本再把u*作为新列加回主问题重复直到上界和下界差值小于阈值。子问题的目标是求“给定购建方案之后最坏情况下的运行成本”也就是max_{u∈U} min_{y∈F(x,u)} cost(y)。3.2 Python里搭一个CCG迭代骨架子问题用对偶变换还是枚举我在实际工程里不能用纯枚举来遍历连续不确定性集合那会让规模爆炸。常见做法是用对偶变换把子问题内层的min问题对偶成max问题与外层的max合并成一个单层max问题然后用求解器直接求解。下面是一个可运行的骨架依赖Gurobi和NumPy。import numpy as np import gurobipy as gp from gurobipy import GRB # 输入: 候选设备参数、预测负荷与预测光伏出力 T 24 # 调度时段数 load_fc np.array([...]) # 24h预测负荷 pv_fc np.array([...]) # 24h预测光伏出力 M 1e6 # 大M惩罚系数 def build_master(uncertainty_scenarios): m gp.Model(master) # 第一层决策: 储能额定容量(连续) 和 柴油机台数(整数) E_rated m.addVar(lb0, ub5000, namestorage_capacity) D_cnt m.addVar(lb0, ub3, vtypeGRB.INTEGER, namediesel_count) # 每个已发现的场景对应一组运行变量 run_vars [] for s in uncertainty_scenarios: p_ch m.addVars(T, lb0, ub500, namefch_{s}) p_dis m.addVars(T, lb0, ub500, namefdis_{s}) p_diesel m.addVars(T, lb0, ub800, namefde_{s}) soc m.addVars(T 1, lb100, ub5000, namefsoc_{s}) run_vars.append((p_ch, p_dis, p_diesel, soc)) m.update() return m, run_vars def solve_subproblem(x_rated, load, pv): sub gp.Model(subproblem) # 对偶变量 不确定性变量u_pv, u_load u_pv sub.addVars(T, lb-0.3, ub0.3, nameu_pv) # 光伏波动比例 u_load sub.addVars(T, lb-0.2, ub0.2, nameu_load) # 运行变量... # 目标为最坏运行成本, 采用对偶合并方法 sub.setObjective(gp.quicksum(...), senseGRB.MAXIMIZE) sub.Params.NonConvex 2 # 含双线性项时必须开启 sub.optimize() return sub.ObjVal, [u_pv[i].X for i in range(T)], [u_load[i].X for i in range(T)]这段原型代码里有三个参数值得说明。第一个是储能SOC的范围我把lb设成100而不是0这是为了避免电池深度放电导致寿命损耗模型失真工程上用20%的DOD下限比较常见。第二个是光伏波动的上下限±30%这个值来自同类地理位置的实测偏差统计不建议只拍脑袋填最好用过去一年的历史曲线按小时算偏差分位数。第三个是Gurobi的NonConvex参数设成2因为子问题合并对偶之后会引入不确定量和运行变量的乘积项形成双线性约束现代求解器必须显式开启非凸处理才能收敛。3.3 CCG收敛判据上下界怎么取、什么时候停主问题解出来之后把主问题的目标函数值作为下界LB把子问题在最坏场景下的运行成本加上第一阶段投资成本作为上界UB当UB和LB的相对差小于1%或绝对差小于一个给定金额时停止。收敛曲线往往前几轮下界快速爬升后面变平我一般设最多50轮迭代如果30轮还没收敛先回去查子问题对偶变换是不是漏了约束或目标符号搞反了。这里有个工程上容易踩的坑第一阶段买储能和柴油机的决策变量直接进主问题但主问题里每个场景都复制了一套运行变量场景加多了之后主问题维度飞涨。解决办法是加“场景聚合”在每轮迭代里只把当前最坏场景加进主问题不把上一轮的差场景也重复加进去。CCG的优势就在于对每个新场景完整复制一套变量而不是加一条割所以主问题的变量数量是线性增长的。4. 一个可复现的中等规模微网算例参数表与结果判断4.1 微网拓扑与设备选型先假定一个典型的并网型园区微网光伏装机可选项在05 MWp风电可选项03 MW储能可选项05 MWh/输出功率02 MW柴油发电机可选项03台单机额定功率800 kW同时保留从配电网购电的联络线最大购电功率1.5 MW。负荷是厂区生产负荷峰值约4.2 MW光伏和负荷的不确定性用预算集合约束。这个拓扑覆盖了最常见的三种情况可再生的光伏和风机、可双向调度的储能、可紧急出力的柴油机。需要说明的是算例中风机选型不做过细的风速曲线拟合而是直接用一个典型日出力系数乘以额定容量因为容量配置阶段的重点在“总量多少”不在逐叶片的气动模型。这符合工程惯例规划阶段的仿真颗粒度不需要跟详细设计阶段一致。4.2 参数表投资成本、寿命、效率与不确定性预算参数项数值范围说明光伏单位投资26003200 元/kWp含支架、逆变器、施工按地区差异取值风机单位投资45005500 元/kW含基础、箱变、吊装储能单位容量投资12001600 元/kWh磷酸铁锂系统含PCS和BMS储能单位功率投资8001200 元/kW变流器与接入成本柴油机单位投资15002000 元/kW含机房、油路、环保处理储能充放电效率92% 单程往返效率约85%88%柴油机油耗210230 g/kWh按75%负荷率取值低负荷油耗更大光伏寿命25 年组件线性衰减按0.5%/年折算储能寿命10 年或6000次循环两者取先到者算法折现率6%8%对投资年值影响较大做敏感性分析不确定性预算 Γ412按24时段从4开始扫观察成本上升斜率这张表的前四行是采购部门能直接对价的参数后两行直接决定结果的合理性。光伏寿命25年是我做方案时的基准值但储能必须按10年周期做替换成本计算因为目前磷酸铁锂电池的日历寿命和循环寿命不支持25年一次性投资。如果一个方案在计算年值时不把储能替换成本计入结果会系统性偏向储能导致柴油机配得偏少。4.3 结果判断目标函数值、最坏场景调度与IIS模型跑完后输出三个关键结果来判断方案是否合理。第一是总目标函数值即年化投资成本加上最坏场景年运行成本单位元/年。第二是最坏场景的时序调度结果看那个场景是连续阴天日照归零、还是41℃高温负荷飙升、还是两者叠加如果最坏场景里储能SOC在末端掉到下限且柴油机满发说明这个解是“紧”的预算值再加一点就会导致不可行或成本跳变。第三是直接看购电功率序列是否在某些时段顶到联络线上限如果顶满且持续超过4小时说明并网依赖过强这在离网场景里直接是违规设计。判断方案是否“经济上最优”不能只看目标函数一个数还要看设备容量是否跑出整数解的边界。比如储能容量解是4.98 MWh基本等于5 MWh上限这时候应该回查是不是储能年成本最低导致“越多越省”的边界效应往往是光伏弃电量大、储能通过峰谷套利把成本赚回来了。这种边界解需要做敏感性分析把储能上限拉到6 MWh和4 MWh各跑一次如果目标函数变化很小说明解的物理意义不强需要在报告中说明“配5 MWh是因为上限约束不是成本拐点”。注意如果模型报不可行先检查是不是第一阶段购建变量过小导致第二阶段任意场景下都没有可行调度比如柴油机台数为0、储能容量为0、同时联络线功率上限过低。Gurobi的IIS报告能帮你快速定位是哪些约束冲突但IIS找出来的约束组有时包含多个不相干的约束需要用二分法手动剔除无关约束。5. 避坑指南五个最常翻车的点与排查路径5.1 子问题对偶符号搞反上下界一直不收敛现象CCG迭代到第10轮UB始终不降LB只在一个小范围内波动gap一直卡在30%以上。原因子问题内层是min问题转对偶时把min操作符直接替换成max却没有把约束条件里的不等号方向同步翻转。比如原来约束是“储能放电功率 ≤ 储能功率上限”对偶后应该是“对偶变量 ≥ 0”而不是≤。解决对偶变换后先拿一个固定x做一次子问题求解手算或者与单层MILP结果对比校验目标函数值是否一致。我自己的习惯是对偶代码写完先跑一个只有一个时段的最小例子拿纸笔验一遍。5.2 不确定性集合包含“不可能场景”导致储能容量被顶到上限现象最优结果里储能容量等于上限而且最坏场景是个完全不合理的组合光伏出力0%且负荷峰值同时出现在连续48小时。原因预算集合只约束了“同时偏离的时段数”没有约束“光伏和负荷的同时偏离方向是否互斥”。其实光伏在夜间本来就是0夜间负荷高峰期光伏贡献已经是0你再让光伏在最坏方向偏离实际上是空集。解决把集合按“日时段类型”拆分比如白天时段只允许光伏和负荷同向波动夜间时段只允许负荷波动或者在集合里加入光伏和负荷波动量之和的绝对值上限。我用后者比较多因为它不用改求解器结构只是在U的定义里加一个线性不等式。5.3 储能SOC递推约束导致子问题非凸现象子问题求解报“QCQP or bilinear constraints”错误或者NonConvex设为2之后求解速度从几秒涨到几分钟。原因有的建模把SOC公式写成 Soc_t Soc_{t-1} efficiency × P_charge而P_charge和P_discharge同时是连续变量和不确定性相关变量一旦SOC约束与不确定性相乘且被翻到对偶层就会出现双线性项。解决优先把SOC递推改成线性形式用“净电量 充电量×效率 − 放电量/效率”的方式合并成单一变量只在运行约束层保留充电和放电各自非负。双线性项如果无法避免就把子问题里SOC的下界设得更保守一些减少求解器的搜索范围。5.4 购电电价函数里含阶梯电价模型无端产生“峰谷套利幻觉”现象搭配储能的方案年运行成本低得离谱储能容量很小但收益极高仔细检查发现是电价函数里阶梯电量段的边际电价低于谷段电价模型让储能在一个时段里用阶梯低价“充电”再在同一个时段的高阶价格放电实际上在一个小时内反复进出同一段价格区间物理上不可能。原因阶梯电价的阶跃条件是按月度累计电量做的但模型把它近似成了单时段斜率电价。解决把阶梯电价改成月度累计电量约束用线性化方式引入月度累计辅助变量更简单的做法是算一个月度平均购电价把模型退化成单一电价再做一个阶梯电价偏差的敏感性分析看储能配量对电价曲线是否过度敏感。5.5 收敛标准设太松方案自己跟自己打架现象同一套输入数据跑两次一次收敛阈值1%一次收敛阈值0.5%两次结果相差一台柴油机。原因CCG的上下界收敛曲线并不是单调的UB和LB的gap在1%附近会出现平台期此时购建方案已经发生变化但目标函数值变化微小。解决把收敛阈值设到0.5%或绝对值5000元/年并且加一条“方案稳定性”检查连续两轮的主问题x相同才认为收敛不只看目标函数gap。这是我在项目评审里被业主追问“为什么两版报告设备数量不一致”之后总结的血泪经验从那以后每轮迭代都输出x的变化记录。6. 预算值校准与权衡曲线让鲁棒方案从“纸面最优”变成“敢投钱”两阶段鲁棒模型跑出来的方案如果不做预算值敏感性分析本质上只是一个数学解不是决策依据。我一般会在主方案算完后固定其他参数不动只把Γ从0到24扫描一遍其中0代表确定性模型24代表完全盒式保守每个Γ跑一次CCG得到一条“年化成本 vs 最坏场景运行成本”的权衡曲线。这条曲线的形状决定了业主应该选哪个方案如果Γ从0到6时年化成本上升不超过8%而最坏场景下的失负荷时间从50小时降到0那这个钱值得花如果Γ超过10后成本直线上升而可靠性收益变小那说明系统里边际投资全被用在对抗极低频的极端天气上不如把预算放到备用柴油机上。做这条曲线时有一个细节每个Γ点都应该在相同的收敛阈值和相同的时间离散度下计算最好用同一台机器顺序跑避免并行计算时求解器线程数不同导致数值抖动。每条曲线点大约耗费1530分钟整条曲线放一个晚上跑完是合理的。跑完之后还有一个必要动作把Γ8的最坏场景时序调度导出成图表和确定性方案的典型日调度放在一起对比给业主讲清楚“多出来的投资花在了哪个时段、哪台设备上”。这个比任何ppt里的算法名词都有说服力。另外我在交付报告里一定会放一个“方案稳健性复核表”把最优方案在盒式集合、预算集合、以及基于历史实测数据的Monte-Carlo模拟三种验证方式下的失负荷期望值列出来。鲁棒优化的结果本身就是保守的但如果它在实测数据的随机模拟下依然表现良好那就证明这个方案的“保守方向”选对了。如果随机模拟里它频繁触发柴油机启停说明预算方向选偏了回去调整不确定性集合。这两年做了几个现场容量配置评审我自己的习惯是保留每次CCG迭代的trace文件——每轮的LB、UB、x*和gap都存成一个CSV。业主问到“为什么这里配这么大储能”时直接翻出第一轮和第八轮的对比看着下界一步步从400万爬到760万购建方案从0台柴油机变成2台比现场解释半小时都管用。这不是炫技是让鲁棒优化这种黑匣子算法在工程决策里变得可审计。希望帮到你。本文还有配套的精品资源点击获取