两阶段鲁棒优化在微网容量配置中的应用与CCG求解实践 📅 发布时间:2026/9/11 22:51:09 👁 浏览次数: 简介这是一份面向微电网规划与优化调度研究者的MATLAB代码及配套文档资源适用于电气工程、新能源方向的本硕博学生与工程师用于解决含风电、光伏、储能与燃气轮机的微网电源容量优化配置问题。核心思路采用两阶段鲁棒优化框架第一阶段决策储能、风电、光伏的容量第二阶段优化风光燃储的实际出力有效应对新能源出力的不确定性。压缩包共19个文件、3.76MB包含6个docx文档两阶段鲁棒构建过程、模型说明、CCG算法求解等、5个m代码文件主程序及MP、SP模块、2篇PDF参考文献、1份典型日数据Excel和3张矩阵推导图从原理推导到代码实现形成完整链路。已有1328人学习浏览。除规划模型外还涉及鲁棒机组组合与两阶段鲁棒经济调度方法配套参考文献与实现效果截图适合正在做微网鲁棒优化相关毕设或科研的读者直接参考。1. 两阶段鲁棒优化在微网容量配置里的定位微网电源容量配置的难点不在“选多大风机、配多少光伏”而在于这些设备投进去之后要面对一整年波动的负荷、光照和风电出力。传统的确定性优化把风光当成固定曲线算出来的容量往往在典型日工况下很漂亮可一旦遇到连续阴天或者负荷尖峰系统就得靠柴油机和储能硬扛甚至出现切负荷。两阶段鲁棒优化解决的就是这个问题第一阶段先定设备装多少第二阶段在“最坏情况”下检验这套配置是否扛得住扛不住就反馈回去调整容量反复迭代直到收敛。这个方向的核心价值不是算法本身多深奥而是它把容量配置的价值判断标准从“平均性能最优”改成了“最恶劣场景下可接受”。对做微网规划、园区能源站设计、海岛独立供电这类场景的工程师来说用两阶段鲁棒优化比用蒙特卡洛模拟更省时间比用随机规划更保守可控且不确定集参数可以直接对齐历史数据的波动范围。这篇文章直接从数学模型讲到可运行的代码框架再讲到不确定集怎么定、求解器参数怎么调、工程落地时哪些坑必须绕开。2. 两阶段鲁棒容量配置的数学模型与求解框架2.1 为什么是“两阶段”决策变量与不确定变量的时序拆分容量配置问题天然具有两阶段结构。第一阶段决策是设备选型和容量包括光伏装机容量、风机台数、储能额定功率与容量、柴油发电机台数甚至包括变流器容量。这些决策在项目生命周期内基本不变一旦施工完成很难低成本调整。第二阶段决策是运行调度包括每小时各机组出力、储能充放电功率、弃风弃光电量、切负荷量属于“看到不确定量实现后”再做的调整。两阶段鲁棒优化的标准形式是 min_x c^T x max_u min_y d^T y其中 x 代表容量配置u 代表不确定参数y 代表运行决策。外层 min 是投资成本最小化内层 max-min 是在最坏不确定性场景下让运行成本最小化。这个结构与微网规划天然契合因为设备选型和运行调度在时间尺度上本来就是一前一后两个决策层。需要注意的是这里的目标函数里投资成本和运行成本必须放到同一个量纲里比较。常见做法是把运行成本乘上一个寿命期系数比如项目寿命 20 年或者用年化投资成本系数 CRFCapital Recovery Factor把一次性投资摊到每年。否则第一阶段会倾向于拼命加设备因为运行成本省下来的钱在目标函数里占比太小算法会忽略运行层面的经济性。2.2 微网容量配置的完整数学模型目标函数分为两阶段。第一阶段是投资成本最小化第二阶段是最坏场景下的运行成本最小化两者通过耦合约束连接。下面给出一个包含光伏、风电、柴油发电机、储能和负荷的通用模型。决策变量定义如下x_pv、x_wt 为光伏和风电的装机容量kWx_es_e、x_es_p 为储能容量kWh和功率kWx_dg 为柴油发电机台数y_g,t 为机组 g 在时刻 t 的出力kWy_ch,t、y_dch,t 为储能充放电功率kWy_soc,t 为储能荷电状态kWhy_curt,t 为弃风弃光电量kWy_shed,t 为切负荷量kW。不确定参数包括光伏实际出力 pv_t、风电实际出力 wt_t、负荷功率 load_t。目标函数第一部分是投资成本min C_inv c_pv·x_pv c_wt·x_wt c_es_e·x_es_e c_es_p·x_es_p c_dg·x_dg。目标函数第二部分是运行成本max_u min_y C_ope Σ_t (Σ_g c_g·y_g,t c_curt·y_curt,t c_shed·y_shed,t)其中 c_g 是柴油发电机单位发电成本c_curt 是弃风弃光惩罚c_shed 是切负荷惩罚。约束条件分四组。功率平衡约束要求任意时刻总出力等于负荷加上储能充电减去放电y_g,t pv_t wt_t y_dch,t - y_ch,t y_shed,t load_t - y_curt,t。光伏和风机的实际出力不能超过容量乘以归一化出力系数且弃电量非负0 ≤ pv_t ≤ x_pv·η_pv,t0 ≤ wt_t ≤ x_wt·η_wt,ty_curt,t ≥ 0。储能约束包括充放电功率上限、SOC 递推关系和容量上下限0 ≤ y_ch,t ≤ x_es_p0 ≤ y_dch,t ≤ x_es_py_soc,t y_soc,t-1 η_ch·y_ch,t - y_dch,t/η_dchSOC_min·x_es_e ≤ y_soc,t ≤ SOC_max·x_es_e且一个调度周期始末 SOC 相等。柴油发电机约束是输出功率上下限y_dg_min·x_dg ≤ y_g,t ≤ y_dg_max·x_dg。这里最关键的是功率平衡约束中负荷项是不确定参数光伏和风电出力也是不确定参数三者同时波动互相之间还有相关性。如果只把光伏当作不确定量而把负荷取确定值算出来的容量配置在负荷高峰叠加光伏低谷时会直接穿透。2.3 CCG 算法把 max-min 问题拆成主问题和子问题两阶段鲁棒模型直接求解求解器解不了因为内层 max-min 本身是一个嵌套优化。业界最常用的求解方法是 CCGColumn and Column Generation列与约束生成算法核心思路是把原问题拆成一个主问题和一个子问题迭代求解。主问题MP是min c^T x θ约束条件包含第一阶段所有投资约束以及一个辅助变量 θ 和一系列已经找到的“最坏场景”下的运行约束。每次迭代往里加一个新的场景相当于把子问题返回的确定性场景固定下来重新求解容量配置。子问题SP是给定第一阶段决策 x*求解 max_u min_y d^T y得到一个最坏场景 u* 和对应的运行成本 Q(x*, u*)。这个内层 max-min 问题需要通过对偶变换或者 KKT 条件转成单层 max 问题然后求解。对偶变换是标准做法内层 min 是线性规划直接取对偶变成 max和外层 max 合并成一个 max 问题目标函数里会出现对偶变量乘不确定参数的项。CCG 的收敛过程是这样先给一个初始场景比如典型日的晴天天求主问题得到一组容量配置然后带进子问题去找最坏场景如果子问题返回的最坏运行成本高于主问题里的 θ就把这个场景对应的运行变量和约束加进主问题重新求解。重复这个过程直到上下界间隙小于设定阈值。这个方法比 Benders 分解的实用优势在于每轮迭代加入的是完整的运行场景约束信息量更大通常十几轮就能收敛到非常小的间隙Benders 分解每次只加一条割平面几百轮迭代是常事。对于容量配置这种第一、第二阶段变量都很多的模型CCG 是首选。注意子问题的对偶变换要求内层 min 问题必须是线性规划。如果储能 SOC 递推公式里加入了充放电状态整数变量子问题就不再是纯 LP对偶变换会失效。这是后续排错最常踩的坑。3. 基于 Python GuRoBi 的两阶段鲁棒容量配置实现3.1 参数设置与不确定集合构造先定义基础参数。以一个 100 kW 级工业园区微网为例负荷年峰值 180 kW光伏和风电的归一化出力曲线取历史典型数据储能用磷酸铁锂电池柴油发电机作为后备电源。单位投资成本光伏 3500 元/kW风电 6000 元/kW储能电池 1500 元/kWh储能 PCS 800 元/kW柴油发电机 1200 元/kW。运行成本里柴油发电机 1.2 元/kWh弃风弃光惩罚 0.3 元/kWh切负荷惩罚 10 元/kWh。不确定集合构造采用盒式不确定集这是工程上最常用、最保守性可调的方式。光伏和风电的出力不确定范围取预测值的 ±20%负荷不确定范围取预测值的 ±10%。不确定预算 Γ 表示一个调度周期内最多有多少个时刻出现极端偏差这是控制保守度的核心旋钮。3.2 CCG 主循环的完整代码import gurobipy as gp from gurobipy import GRB import numpy as np # 参数设定 T 24 # 调度周期时段数 n_scenarios 20 # 子问题抽样数量 # 投资成本参数 c_pv 3500.0 # 光伏单位容量投资成本元/kW c_wt 6000.0 # 风电单位容量投资成本元/kW c_es_e 1500.0 # 储能单位容量投资成本元/kWh c_es_p 800.0 # 储能功率转换成本元/kW c_dg 1200.0 # 柴油发电机单位容量投资成本元/kW # 运行成本参数 c_dg_op 1.2 # 柴油发电机发电成本元/kWh c_curt 0.3 # 弃风弃光惩罚成本元/kWh c_shed 10.0 # 切负荷惩罚成本元/kWh # 不确定参数的实际波动范围 pv_max 150.0 # 光伏装机容量上限kW wt_max 80.0 # 风电装机容量上限kW # 归一化出力曲线模拟数据实际项目替换为历史数据 pv_norm np.array([ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.05, 0.15, 0.3, 0.5, 0.7, 0.85, 0.9, 0.8, 0.6, 0.4, 0.2, 0.1, 0.05, 0.0, 0.0, 0.0, 0.0, 0.0 ]) wt_norm np.random.uniform(0.2, 0.9, T) # 风电归一化出力 load_norm np.array([ 0.5, 0.45, 0.42, 0.4, 0.38, 0.35, 0.4, 0.6, 0.8, 0.9, 0.95, 1.0, 0.92, 0.85, 0.8, 0.82, 0.9, 1.0, 0.95, 0.8, 0.7, 0.6, 0.55, 0.5 ]) load_base 180.0 # 负荷基准值kW # 不确定集合参数 Gamma_pv 12 # 光伏不确定预算一天24小时内最多12小时出现最大偏差 Gamma_wt 10 # 风电不确定预算 Gamma_load 12 # 负荷不确定预算 delta_pv 0.20 # 光伏波动范围 ±20% delta_wt 0.20 delta_load 0.10 def build_uncertain_set(): 构造盒式不确定集合的边界 pv_lb pv_norm * (1 - delta_pv) pv_ub pv_norm * (1 delta_pv) wt_lb wt_norm * (1 - delta_wt) wt_ub wt_norm * (1 delta_wt) load_lb load_norm * (1 - delta_load) load_ub load_norm * (1 delta_load) return { pv_lb: pv_lb, pv_ub: pv_ub, wt_lb: wt_lb, wt_ub: wt_ub, load_lb: load_lb, load_ub: load_ub }主循环里最核心的部分是子问题的重建模和对偶转换。为了不让文章变成单纯贴代码这里拆开讲子问题的内层 min 是关于运行变量 y 的线性规划给定不确定参数 u 后可以直接用 GuRoBi 求解。但外层还有一个 max 在选 u所以不能直接求。标准做法是把内层 min 取对偶变成 max 问题和外层 max 合并。对偶变换后目标函数里会出现 u^T·π 这种双线性项π 是对偶变量。双线性项需要用大 M 法线性化或者用 GuRoBi 的非线性求解能力处理。3.3 子问题对偶变换与线性化对偶变换后的子问题形如max α^T·u β约束条件是 A^T·π ≤ dπ ≥ 0且 π 与 u 之间存在双线性项 π·u。这个问题的难点在于 u 是连续变量且落在盒式不确定集内π 是连续变量乘积项 u_t·π_t 是非凸的。工程上的标准解法是引入辅助变量 z_t u_t·π_t然后用大 M 法线性化。步骤是先把 u_t 当作取值在上下限之间的连续变量离散化成若干档位或者利用不确定集的预算约束结构把 u_t 表示为 u_t u_t^nominal ξ_t·Δu_t其中 ξ_t ∈ [0,1] 表示偏差比例Δu_t 是对称波动幅度。这样处理后双线性项变成 ξ_t·π_t仍然是非线性的需要引入二元变量来线性化。最常见的工程简化方法是不处理对偶而是利用 CCG 的一个变种。既然不确定集合是盒式的最坏场景一定出现在盒式集合的顶点附近。工程做法是把不确定变量的每个时刻都取到上界或下界然后用组合枚举加上不确定预算约束来生成候选场景。实际上 CCG 的很多工业实现就是先把盒式集合按顶点离散化生成一组“候选极端场景”再在子问题里选择一个最坏场景。下面是这个方案的完整实现# 子问题求解枚举候选极端场景 def solve_subproblem(x_pv, x_wt, x_es_e, x_es_p, x_dg, uncertain_data): 两阶段鲁棒子问题在盒式不确定集内找最坏场景并计算运行成本 这里采用场景枚举法对每个时刻独立取上界或下界再按不确定预算筛选 pv_lb, pv_ub uncertain_data[pv_lb], uncertain_data[pv_ub] wt_lb, wt_ub uncertain_data[wt_lb], uncertain_data[wt_ub] load_lb, load_ub uncertain_data[load_lb], uncertain_data[load_ub] worst_cost 0.0 best_scene None # 生成候选场景每个时刻从{上界, 下界}取值的组合很多 # 工程简化只取两种极端模式 - 光伏低风电低负荷高 和 光伏低风电低负荷低 # 第一种最考验供电可靠性第二种最考验储能容量 scenes [] # 场景1光伏上界、风电上界、负荷下界最乐观 scenes.append({ pv: pv_ub, wt: wt_ub, load: load_lb, desc: 最乐观场景 }) # 场景2光伏下界、风电下界、负荷上界最恶劣 scenes.append({ pv: pv_lb, wt: wt_lb, load: load_ub, desc: 最恶劣场景 }) # 场景3光伏下界、风电上界、负荷上界光伏不足但风电尚可 scenes.append({ pv: pv_lb, wt: wt_ub, load: load_ub, desc: 光伏不足场景 }) # 场景4光伏上界、风电下界、负荷上界风电不足 scenes.append({ pv: pv_ub, wt: wt_lb, load: load_ub, desc: 风电不足场景 }) for scene in scenes: cost solve_dispatch(x_pv, x_wt, x_es_e, x_es_p, x_dg, scene[pv], scene[wt], scene[load]) if cost worst_cost: worst_cost cost best_scene scene return worst_cost, best_scenesolve_dispatch 是内层运行优化输入是第一阶段决策给出的容量参数和某个确定性的风光荷场景输出是运行成本最优值。这是一个标准 LP直接在solve_dispatch里建模即可注意储能约束要保证一个调度周期始末 SOC 相等。3.4 主问题与 CCG 主循环收敛控制def c_and_cg_main(max_iter30, tolerance0.01): CCG 主循环迭代求解容量配置 uncertain_data build_uncertain_set() # 初始场景取最恶劣场景作为候选场景集合 initial_scene { pv: uncertain_data[pv_lb], wt: uncertain_data[wt_lb], load: uncertain_data[load_ub] } scenes [initial_scene] upper_bound float(inf) lower_bound 0.0 for iteration in range(max_iter): # 求解主问题在已发现的候选场景集合上做确定性容量配置 x_pv, x_wt, x_es_e, x_es_p, x_dg, theta solve_master(scenes) # 下界主问题直到当前迭代为止所有场景的运行成本之和的下确界 lower_bound theta # 求解子问题找到比现有场景更恶劣的场景 worst_cost, worst_scene solve_subproblem( x_pv, x_wt, x_es_e, x_es_p, x_dg, uncertain_data ) # 上界当前配置的真实总成本投资成本 最坏场景运行成本 inv_cost (c_pv * x_pv c_wt * x_wt c_es_e * x_es_e c_es_p * x_es_p c_dg * x_dg) upper_bound inv_cost worst_cost print(f迭代 {iteration1}: 下界{lower_bound:.2f}, 上界{upper_bound:.2f}, f间隙{(upper_bound-lower_bound)/upper_bound*100:.2f}%) # 收敛判定 gap (upper_bound - lower_bound) / upper_bound if gap tolerance: break # 如果新场景的运行成本比已有场景高加入主问题 if worst_cost theta: scenes.append(worst_scene) return { pv: x_pv, wt: x_wt, es_e: x_es_e, es_p: x_es_p, dg: x_dg, theta: theta, iterations: iteration 1 }Gamma_pv、Gamma_wt、Gamma_load这些不确定预算参数在场景枚举法里不直接参与子问题建模但如果用对偶变换法就需要落实到约束里。上面的实现里用了四类固定场景来近似最坏情况这个方法在工程上非常实用代价是会牺牲一定的保守性。如果要做严格的最坏场景搜索就得回到双线性项线性化的路子。4. 不确定集合参数标定与保守度调节4.1 盒式不确定集的关键参数边界与预算盒式不确定集合的表达式是 u_t u_t_nom ξ_t·Δu_t其中 u_t_nom 是预测值Δu_t 是最大偏差ξ_t ∈ [0,1] 是偏差比例。不确定预算 Γ 约束了 Σξ_t ≤ Γ表示一天内最多多少个时刻可以同时达到最大偏差。工程上 Γ 的取值建议是 T 的三分之一到二分之一。Γ 取 24 表示所有时刻都按最坏情况考虑这在物理上几乎不可能发生会造成容量配置过度保守投资成本上升百分之二三十以上。边界 Δu_t 的标定不能用全年的最大偏差否则极端天气的偶发波动会被放大成常态。更合理的做法是取历史数据的 P90 或 P95 分位数偏差。比如有三年逐小时光伏出力数据先算每个时刻的预测误差再取误差分布的 90% 分位数作为 Δu_t。这样过滤掉 10% 的极端情况剩下的波动幅度是系统设计要覆盖的主流风险。提示Δu_t 取 P90 还是 P95 取决于项目对供电可靠性的要求。独立微网带医院、数据中心这类一二级负荷取 P99 也不过分。并网型微网有主网兜底P85-P90 就够了多余的保守性直接换算成设备投资浪费。4.2 参数灵敏度分析的工程操作容量配置做完之后必须做敏感度分析否则决策层不敢拍板。分析方法是从基准参数出发每次只动一个参数记录容量配置结果和总成本变化。需要扫的参数包括光伏波动幅度 δ_pv、负荷波动幅度 δ_load、不确定预算 Γ、切负荷惩罚 c_shed、储能单位投资成本 c_es_e。下面这张表是一种典型输出实际项目里用真实数据重新跑一遍参数变化光伏容量变化储能容量变化总投资变化最坏场景运行成本δ_pv 0.20→0.305%12%8%基本不变δ_load 0.10→0.2015%25%18%略有下降Γ_pv 12→183%8%5%基本不变c_shed 10→5020%35%28%显著下降c_es_e 1500→1000不变40%12%下降敏感度分析的主要结论通常非常明确负荷不确定范围的扩大对容量配置影响最大切负荷惩罚设得越高系统越偏向多装储能和多配柴油机光伏不确定范围的扩大主要影响储能配置对光伏装机本身影响不大因为光伏装机的上限通常受屋顶面积或土地指标限制。这段分析的代码实现很简单上面 CCG 函数外面套一层循环改参数记录结果。建议每次跑完把容量配置结果输出成 CSV方便做对比图表和写报告。4.3 不确定预算与运行成本的量化关系Γ 从 0 变化到 T 时系统总成本会呈现一个先快速上升后趋缓的曲线形状。Γ0 对应确定性优化成本最低Γ24 对应全时段最坏情况成本最高而且高得离谱。曲线拐点通常在 ΓT/3 附近过了拐点再增加 Γ容量配置变化不大总成本增长也有限说明系统已经从“优化容量”变成“堆容量”了。选择 Γ 的工程技巧是画这条曲线后找拐点或者设定一个可接受的成本增幅阈值比如“相比确定性优化结果成本增幅不超过 15%”然后反推最大可接受的 Γ。这种方法比拍脑袋定 Γ 有说服力得多也容易在项目评审会上讲清楚。5. 模型落地中的 4 个边界条件与工程避坑5.1 多时段耦合计费机制替代平均电价容量配置优化有一个常见误区把运行成本简化成“买电单价 × 电量”的线性函数。工商业微网大部分执行峰谷分时电价甚至还有需量电费。这种情况下第二阶段运行优化里必须加入分时电价向量峰时电价可能是谷时的三四倍这会直接影响储能“低充高放”的收益模式。模型改动很小只需要把目标函数里的购电成本项从 λ·P_buy_t 改成 λ_t·P_buy_t其中 λ_t 是时刻 t 的电价。储能系统的容量配置结果会明显偏向“把峰时段的负荷平移掉”配置的储能容量和功率都会和平均电价模型计算结果不同。如果微网还涉及需量电费需要在模型里增加一个 max 约束即月内任意时刻的购电功率不超过需量上限。5.2 储能寿命衰减与充放电深度限制两阶段鲁棒模型里储能 SOC 上下限一般取 0.1 和 0.9这个默认值来自锂电池循环寿命和 DOD放电深度的折中考虑。很多项目方希望把 SOC 下限压到 0说“买了容量不用太亏”实际算下来电池循环寿命会缩短一半以上全生命周期度电成本大幅上升。工程上处理储能寿命有三种做法。第一种最简单SOC 上下限直接限制在 [0.1, 0.9]第二种复杂一点加入循环寿命约束把全年累计充放电量限制在电池额定循环次数对应的吞吐量以内第三种是目标函数里加入寿命衰减惩罚每次充放电依据 DOD 折算成等效循环超过阈值后每 kWh 充放电量乘以一个惩罚系数。第一种做法最适合容量配置阶段后面两种更适合运行优化模型。5.3 已有电源改造与逐小时时间尺度细化存量微网改造项目的模型和新建项目有一个关键差异第一阶段决策里一部分变量是固定值。已有 500 kW 柴油发电机就是 x_dg ≥ 500 的约束甚至直接固定 x_dg500只优化新增光伏和储能。这种情况下两阶段鲁棒优化仍然适用只需要把固定变量的上下限改掉模型不用动。时间尺度的选择也要注意。容量配置阶段用 24 时段日调度模型足够因为装机容量的决策对小时级波动不敏感但对日内峰谷差敏感。如果项目涉及季节性储能跨周、跨月调峰需要把时间尺度拉长到 8760 小时这时两阶段模型里第二阶段的变量规模会爆炸式增长通常需要先用聚类算法把全年 8760 小时压缩成 12-24 个典型日再跑鲁棒优化。5.4 求解器参数配置与 CCG 收敛加速GuRoBi 求解主问题的时候默认参数在中小规模模型上表现不错但容量配置问题常见几十万个约束和变量需要手动调参数。最实用的两个参数是 MIPGap 和 TimeLimit。主问题每次迭代求的是整数或混合整数规划MIPGap 设 0.01 通常够用设 0.001 会让每次求解慢 3 到 5 倍但对最终容量配置结果影响很小。CCG 的收敛加速有几个技巧。第一个是热启动上一轮主问题的解作为下一轮主问题的初始可行解用 GuRoBi 的 WarmStart 接口传入。第二个是场景过滤子问题每轮返回的最坏场景如果其运行成本和已有场景集合里最高运行成本差距小于 1%跳过不加入主问题避免场景数量膨胀。第三个是子问题求解时间限制如果子问题本身很大设一个时间限制用可行解而不是最优解来近似最坏场景CCG 的迭代次数会增多但每轮更快综合时间往往更少。第四个技巧是初始场景的质量用历史数据里真实出现过的“最差日”作为初始场景往往能让 CCG 提前 3-5 轮收敛。本文还有配套的精品资源点击获取