ARTICLE · INTELLIGENCE

战地情报 · 详情页

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

混合不确定性下风光储微电网容量鲁棒协同优化方法

混合不确定性下风光储微电网容量鲁棒协同优化方法 1. 为什么容量规划必须面对“混合不确定性”1.1 从一次实际规划项目说起之前在做微电网前期可研时设备选型一度是“按最恶劣情况取系数”光伏按 0.8 折减、风机按 0.9 折减、储能按 2 小时配置结果项目投资预算高出预期很多业主差点放弃。后来换了一套思路把风、光、负荷的不确定性拆开建模一部分用概率场景描述一部分用区间范围描述再做鲁棒优化最后得到的配置方案既没有牺牲供电可靠性又显著降低了初始投资。这个项目让我意识到风光储微电网容量规划并不是“套公式选设备”而是一个典型的“混合不确定性条件下的协同优化”问题。本文就围绕这个话题把整个建模思路、数学表达、Python 教学示例和工程落地经验完整梳理一遍。适合正在做微电网设计、储能配置、新能源接入方案的研究生、工程师和项目经理。读完你至少能回答三个问题混合不确定性到底是什么鲁棒优化怎么用于容量规划风、光、储容量如何协同优化而不是分别估算1.2 风光储微电网容量规划的本质先看一个典型的园区微电网系统屋顶光伏、小型风电机组、电池储能、本地负荷可能还有柴油发电机或并网联络线。容量规划要做的事情是在不违背供电可靠性、经济性、物理运行约束的前提下确定光伏装机容量、风电装机容量、储能额定功率、储能额定容量这几个关键数字。听起来不难但难点在于输入数据本身不可靠。光伏出力依赖于辐照度风电出力依赖于风速负荷也有明显的季节波动和随机波动。如果只取一个典型值配置结果可能在大多数日子是浪费的在极端日子却不够用。如果全部按最恶劣情况配置投资又过高。所以工程上更合理的做法是把不确定性问题显式建模让优化算法自动权衡“多配一点”和“少配一点”带来的成本与风险。1.3 混合不确定性的含义很多文献会单独谈不确定性但实际工程中往往同时存在两类不确定性概率不确定性可以用历史数据或概率分布描述。比如风速服从 Weibull 分布光伏预测误差近似正态分布负荷预测误差也可以近似建模。区间不确定性只知道参数的大致范围但无法给出精确分布。比如某些数据来自人工上报、跨区域统计或极端天气情景只能给出下限和上限。“混合不确定性”就是同时考虑这两种类型。风光资源、负荷既有统计规律又存在难以用单一分布刻画的边界情况。如果只用随机优化可能低估极端场景如果只用传统鲁棒优化又会因为“最坏情况”过于保守而导致成本虚高。因此近年来的研究方向是把随机规划和鲁棒优化结合形成分布鲁棒优化或混合鲁棒优化模型这也是本文的核心技术主线。1.4 鲁棒优化与协同优化的关系鲁棒优化最核心的思想是不是优化“期望值”而是寻找一个在不确定性集合内所有可能取值下都可行的方案。它的优点是不需要非常精确的概率分布只需要不确定性集合缺点是如果不合理设置集合边界结果会保守。协同优化则强调光伏、风电、储能三者不是彼此独立的“拼装”而是共享同一个容量配置决策和同一套运行策略。光伏装多了储能可能可以少配一点储能功率大了可能可以替代部分风电出力。只有把投资决策和运行模拟放在同一个优化框架里才能得到全局最优方案。本文使用的建模路线是“两阶段协同优化”第一阶段确定容量第二阶段在不同不确定性场景下模拟运行通过场景约束或惩罚项把运行可行性反馈到容量决策中。这是一种非常适合工程的建模框架也是目前风光储微电网容量规划的主流做法。2. 协同优化总体思路与求解框架2.1 系统的能量流拆解在建模之前先明确系统组成。一个简化的风光储微电网可以抽象为四个电源侧/负荷侧节点光伏阵列出力受辐照度影响只在天晴时段有功率。风电机组出力随风速变化可能昼夜都有输出。储能电池既可以放电支撑负荷也可以充电消纳多余风光。负荷本地用电需求通常按日负荷曲线或年度时序负荷给出。如果不考虑并网售电那么系统约束可以简化为任意时刻“光伏出力 风电出力 储能放电功率 ≥ 负荷功率”同时“光伏出力 风电出力 - 负荷功率”超过储能充电上限时需要弃风弃光。实际规划时还需要考虑设备的爬坡约束、储能SOC上下限、变压器容量、逆变器效率等。但教学模型中我会先保留最核心的功率平衡和储能能量平衡再说明扩展方向。2.2 决策变量、参数与目标函数决策变量分两类投资决策变量风电装机容量 P_wkW光伏装机容量 P_pvkW储能额定功率 P_bkW储能额定容量 E_bkWh运行决策变量各时段储能充放电功率各时段实际弃风弃光量各时段是否切负荷目标函数通常是“初始投资等年值 年运行维护费用 缺电惩罚费用”最小。投资费用可以按等年值折算避免只看静态回收期。不确定参数包括风资源乘子风电出力与装机容量的比值即容量因子光资源乘子光伏出力与装机容量的比值负荷功率在鲁棒优化框架下这些参数不再是一个固定数而是落在一个不确定性集合内。建模的关键就是怎样定义这个集合使得方案在集合内所有情况下都能满足约束。2.3 两阶段优化框架两阶段优化的逻辑是第一阶段决定容量。这个决策在不确定参数实现之前就要做出也就是“这里建多大”。第二阶段在给定容量和特定风光负荷场景下决定储能充放电、弃风弃光等运行策略也就是“实际怎么运行”。用数学语言描述可以写成min 投资成本 运行成本 s.t. 对每一个不确定性场景 s ∈ S 存在可行的运行策略 y_s 满足 功率平衡约束 储能约束 设备容量约束这里的关键是“对每一个场景 s 都必须存在可行运行策略”而不是“平均意义上可行”。这就是鲁棒可行性的体现。如果不确定性场景数量很多可以通过KKT条件或线性规划对偶变换把内层运行优化问题等价替换成一组线性约束从而得到一个单层优化问题。这种处理方式在学术文献中称之为“鲁棒对等模型”。3. 数学模型从确定性到混合鲁棒优化3.1 参数与集合定义为了方便代码实现先定义一组简化但完整的符号符号含义示例值T运行时段集合24小时S不确定性场景集合3个场景P_w / P_pv风电、光伏装机待优化P_b / E_b储能功率与容量待优化wf_s场景 s 风电容量因子0.8pv_s场景 s 光伏容量因子0.9D_s(t)场景 s 下 t 时段负荷80~120 kWeff_c / eff_d储能充电、放电效率0.95SOC_min / SOC_max储能SOC上下限0.1 / 0.9IC_w / IC_pv风电、光伏单位投资7000/5000 元/kWIC_p / IC_e储能单位功率/容量投资1500/2000 元/kWh需要注意的是以上“示例值”只是用于教学演示真实项目必须根据设备厂商报价、当地资源条件和负荷数据重新标定。3.2 确定性基础模型如果不考虑不确定性只取一个典型场景模型就是典型的最优容量配置min 成本函数 s.t. 风电出力 光伏出力 储能放电 - 储能充电 ≥ 负荷 储能能量转移约束 充放电功率不越限 各容量非负这个模型可以快速给出一版参考方案但它有天然缺陷换一组风资源数据结果可能完全不同。所以确定性模型只适合做初始估算不适合直接作为工程结论。3.3 基于场景的鲁棒约束把确定性模型扩展为多个场景后核心变化是功率平衡约束必须对所有场景成立对每个场景 s每个时段 t P_w * wf_s(t) P_pv * pv_s(t) P_b_discharge(s,t) - P_b_charge(s,t) ≥ D_s(t)储能SOC约束也要分场景独立模拟因为每个场景下充放电策略不同。容量变量 P_w、P_pv、P_b、E_b 在所有场景之间共用这是“协同”的关键同一个容量配置必须能应对所有预设不确定性场景。这种处理方式是一种比较直观的鲁棒可行性建模。如果场景集合包含风电、光伏、负荷的各种极端组合那么得到的配置就能抗住这些极端情况。更严格的鲁棒优化还可以用“盒式不确定集合 对偶变换”把连续区间不确定性转化为有限约束但核心思想是一样的让方案对所有可能情况都可行。3.4 混合不确定性如何体现我所说的“混合不确定性”体现在建模元素的三类组合中负荷用一个区间范围表示不指定具体分布。风电和光伏用地形场景或概率场景表示带有一定统计特征。极端天气作为额外场景加入约束区间和概率同时存在。在代码实现中最常见的做法是构造一个“场景集合”其中既包含正常概率场景也包含极端区间场景然后在所有场景上强制约束可行。这样既吸收了概率信息又保留了鲁棒边界。4. Python 教学示例基于场景的鲁棒协同容量规划4.1 环境准备本文示例使用 Python 3.9 以上版本用到两个开源库PuLP线性规划建模工具自带 CBC 求解器。Pandas / Matplotlib数据处理与结果可视化。如果还没有安装可以使用 pip 安装pip install pulp pandas matplotlib需要特别说明的是这个教学示例不是完整的工程软件而是一个“最小可运行模型”。真实项目中建议用 Pyomo 或 GAMS 配合 Gurobi、CPLEX 等商业求解器以支持更大规模的时序优化。4.2 项目文件结构建议按下面的结构组织代码方便后续扩展wind_solar_storage_planning/ │ ├── data/ │ └── scenarios.csv ├── src/ │ └── plan_model.py ├── results/ │ └── config_result.csv └── README.md下面先创建数据文件data/scenarios.csv它保存不同场景下的风电、光伏容量因子和负荷倍率scenario,wt_factor,pv_factor,load_factor,weight normal,1.00,1.00,1.00,0.70 wind_low,0.55,1.05,1.05,0.15 sun_low,1.05,0.45,1.05,0.15这里前三列分别表示风电容量因子、光伏容量因子和负荷倍率。weight表示该场景在运行成本加权中的权重。注意场景集合可以扩充但每增加一个场景约束数量也会增加求解规模会变大。4.3 创建基础负荷曲线实际优化不能只看一个时间点至少要用 24 小时负荷曲线。示例中假设典型日负荷曲线为# 文件路径data/load_profile.py def get_load_profile(): 返回 24 小时典型日负荷曲线单位 kW。 实际项目请用本地负荷数据替换。 load_profile [ 60, 55, 50, 45, 45, 50, 70, 95, 110, 120, 125, 120, 115, 110, 105, 100, 105, 115, 125, 120, 105, 90, 75, 65 ] return load_profile这段数据的含义是凌晨负荷低白天和傍晚负荷高。用代码把负荷曲线独立成函数是为了后续替换真实数据时不需要改动主模型。4.4 主模型代码下面是核心优化模型代码使用 PuLP 实现。由于教学示例聚焦思路我做了三点简化用日负荷曲线代表全年运行不展开 8760 小时。储能运行方式简化为每个场景独立模拟。忽略充放电功率同时为 0 的整数变量约束通过目标函数中的惩罚项避免无意义充放电。# 文件路径src/plan_model.py import pulp import pandas as pd # 读取场景数据 scenario_df pd.read_csv(data/scenarios.csv) load_profile get_load_profile() T len(load_profile) # 24 小时 scenarios scenario_df[scenario].tolist() # 投资成本参数元/kW 或 元/kWh IC_wind 7000 IC_pv 5000 IC_bess_power 1500 IC_bess_energy 2000 annual_factor 0.1 # 投资等年值系数实际按寿命与折现率计算 # 创建优化问题 prob pulp.LpProblem(Microgrid_Capacity_Planning, pulp.LpMinimize) # 容量决策变量 P_w pulp.LpVariable(wind_capacity, lowBound0) P_pv pulp.LpVariable(pv_capacity, lowBound0) P_b pulp.LpVariable(bess_power, lowBound0) E_b pulp.LpVariable(bess_energy, lowBound0) # 每个场景下的运行变量 discharge {} charge {} soc {} for s in scenarios: for t in range(T): discharge[(s, t)] pulp.LpVariable(fdischarge_{s}_{t}, lowBound0) charge[(s, t)] pulp.LpVariable(fcharge_{s}_{t}, lowBound0) soc[s] pulp.LpVariable(fsoc_{s}_final, lowBound0) # 目标函数投资等年值 运行惩罚 # 为了引导模型优先使用风光出力给弃电和缺电设置较高惩罚 penalty_curtail 300 penalty_shortage 800 objective annual_factor * ( IC_wind * P_w IC_pv * P_pv IC_bess_power * P_b IC_bess_energy * E_b ) # 功率平衡约束中用一个变量表示缺电量和弃电量 shortage {} curtail {} for s in scenarios: for t in range(T): shortage[(s, t)] pulp.LpVariable(fshortage_{s}_{t}, lowBound0) curtail[(s, t)] pulp.LpVariable(fcurtail_{s}_{t}, lowBound0) objective penalty_shortage * shortage[(s, t)] objective penalty_curtail * curtail[(s, t)] prob objective # 约束 soc_min, soc_max 0.1, 0.9 charge_eff 0.95 discharge_eff 0.95 hours_per_step 1 # 每个时段 1 小时 for s in scenarios: wt_f scenario_df.loc[scenario_df[scenario] s, wt_factor].values[0] pv_f scenario_df.loc[scenario_df[scenario] s, pv_factor].values[0] load_f scenario_df.loc[scenario_df[scenario] s, load_factor].values[0] weight scenario_df.loc[scenario_df[scenario] s, weight].values[0] for t in range(T): load_t load_profile[t] * load_f wind_power P_w * wt_f pv_power P_pv * pv_f # 功率平衡风光出力 储能放电 - 储能充电 缺电 负荷 弃电 prob (wind_power pv_power discharge[(s, t)] - charge[(s, t)] shortage[(s, t)] load_t curtail[(s, t)]) # 储能充放电功率不超过额定功率 prob discharge[(s, t)] P_b prob charge[(s, t)] P_b # 储能容量与日净充电量约束 # 简化处理日净充电量不能超过储能容量允许范围 net_charge pulp.lpSum(charge[(s, t)] * charge_eff - discharge[(s, t)] / discharge_eff for t in range(T)) prob net_charge E_b * soc_max prob net_charge E_b * soc_min # 求解 solver pulp.PULP_CBC_CMD(msgTrue) prob.solve(solver) print(求解状态:, pulp.LpStatus[prob.status]) print(风电装机容量 (kW):, round(P_w.varValue, 2)) print(光伏装机容量 (kW):, round(P_pv.varValue, 2)) print(储能额定功率 (kW):, round(P_b.varValue, 2)) print(储能额定容量 (kWh):, round(E_b.varValue, 2))这段代码里最需要注意的是功率平衡方程我把“缺电”和“弃电”都建模成带惩罚的变量。这样模型不会为了满足约束而强行配置过大的储能而是在经济性和可靠性之间找到平衡。如果惩罚系数设置足够高缺电量会趋近于 0相当于要求方案在全部场景下都满足负荷。4.5 运行结果与验证在命令行执行cd wind_solar_storage_planning python src/plan_model.py如果一切正常你会看到类似下面的输出求解状态: Optimal 风电装机容量 (kW): 65.32 光伏装机容量 (kW): 82.15 储能额定功率 (kW): 21.08 储能额定容量 (kWh): 46.35由于示例数据偏简化具体数值会随参数变化。你更应关注的是不同不确定性场景带来的容量差异。如果把场景数据改成只有正常场景会发现光伏和风电的总装机容量明显下降一旦加入wind_low和sun_low两个极端场景容量会上升这正是鲁棒约束起作用的体现。5. 结果分析、可视化与模型扩展5.1 结果可视化为了让结果更直观可以把不同场景下的容量画成柱状图对比。新增一个绘图脚本# 文件路径src/plot_result.py import matplotlib.pyplot as plt import numpy as np categories [wind_capacity, pv_capacity, bess_power, bess_energy] deterministic [68.12, 75.30, 15.20, 32.10] robust [65.32, 82.15, 21.08, 46.35] x np.arange(len(categories)) width 0.35 fig, ax plt.subplots() rects1 ax.bar(x - width/2, deterministic, width, label确定性优化) rects2 ax.bar(x width/2, robust, width, label混合鲁棒优化) ax.set_ylabel(配置容量) ax.set_title(不同优化策略下的容量配置对比) ax.set_xticks(x) ax.set_xticklabels(categories) ax.legend() plt.tight_layout() plt.savefig(results/config_compare.png, dpi150)这里的数据是我为了演示写的示例数据跑的时候应替换成你自己的求解结果。通过对比图可以明显看到鲁棒优化结果中储能功率和容量上升而风电或光伏的容量不一定同步上升这反映了“风光储协同互补”的特性。5.2 如何扩展到全年 8760 小时教学示例用了 24 小时负荷曲线但工程级规划一般要求用全年 8760 小时或至少春夏秋冬四个典型日。扩展方法是从历史数据中提取 12 个典型日每个季节取 3 个代表日。每个典型日对应一组风光出力和负荷曲线。在模型中增加一个“时段索引”维度由原来的一维t扩展为二维(day, t)。代价是优化变量和约束数量会增长数十倍这时 PuLP 的内置 CBC 求解器可能不够高效建议切换到 Gurobi 或 CPLEX。Pyomo 框架对商业求解器的支持更友好也更容易表达复杂约束。5.3 从鲁棒优化到分布鲁棒优化如果数据足够多还能把模型扩展为分布鲁棒优化。思路是不是要求所有场景都可行而是要求“在最坏的概率分布下期望成本可控”。这需要引入矩信息或 Wasserstein 距离约束模型复杂度明显提升。对于工程项目通常先用基于场景的鲁棒优化打底如果发现过于保守再考虑分布鲁棒优化。6. 常见问题与排查思路实际写代码和调模型时大家最容易遇到下面几类问题我把典型的错误现象、原因和解决方法整理成一个排查表。问题现象可能原因排查与解决思路求解状态一直是 Infeasible约束条件互相矛盾例如负荷过高但容量上限过低先去掉缺电和弃电变量检查单一场景下是否有解再检查储能SOC约束是否设置不合理结果全部为 0目标函数或惩罚项系数异常导致模型认为不配置设备最优检查投资成本是否远大于缺电惩罚把缺电惩罚调大再试求解时间过长场景数或时段数过多变量规模过大缩减场景数量用典型日代替滚动模拟改用商业求解器容量结果不稳定数据单位不统一例如负荷用 kW光伏容量因子却写成 MW 级别统一单位检查场景文件中的因子是否在合理范围内储能容量偏大但功率偏小储能能量约束太宽松或SOC上下限设置不当检查SOC_min和SOC_max确认日净充电量的表达是否正确目标函数数值非常大惩罚系数过大或等年值系数取值不当把成本统一换算到“每年”后比较量级如果你在求解中看到“Unbounded”状态通常意味着模型缺少必要的上下界约束。例如投资成本为正但缺少“容量必须非负”的显式约束。PuLP 中lowBound0已经处理了这个问题但如果你后续修改代码时把lowBound删掉就可能触发。7. 工程落地与最佳实践建议7.1 数据治理比算法更重要在真实项目中不管用随机优化、鲁棒优化还是启发式算法最终结果都取决于输入数据的质量。建议把风光资源数据、负荷数据、设备参数按统一格式存档并记录数据来源和时间范围。比如风速数据来自气象站还是中尺度再分析数据辐照度是水平面还是倾斜面这些细节直接影响容量因子标定结果。7.2 场景缩减与极端场景构造不确定性场景数量不是越多越好。如果历史数据有 1000 个日场景直接全部带入优化会让模型规模爆炸。工程上常用两种方法用聚类方法把场景缩减到 10~50 个代表场景。在缩减后的场景基础上手动加入“极端场景”例如连续阴雨天、冬季大风无光日。后者是鲁棒思想最直接的体现哪怕概率很低只要后果严重就应该参与约束。7.3 求解器选择与性能调优小规模教学案例用 PuLP 自带的 CBC 足够。工程级项目建议使用 Pyomo 或 GAMS 建模方便切换求解器。求解线性规划时优先用 Gurobi、CPLEX它们在数值稳定性和求解速度上明显优于开源求解器。如果模型变成混合整数规划例如需要建模储能是否参与调峰求解难度会急剧上升应尽量保持模型线性。7.4 与电力系统仿真工具配合容量规划模型只是第一步输出结果后还要放到时序仿真工具中验证。你可以用 Python 的mosaik、pandapower或者仿真软件如 HOMER、MATLAB/Simulink对一个完整年度逐小时验证供电可靠性。如果仿真中发现切负荷时间超标就回到优化模型中提高缺电惩罚系数或增加极端场景进行迭代。7.5 安全边界与生产环境注意事项这个主题虽然是规划阶段问题但涉及到实际项目时仍需注意所有设备容量配置结果应乘上合理的安全裕度不能把优化值直接当作设备招标参数。储能 SOC 管理要考虑电池寿命不能长期在 90% 以上或 10% 以下运行。涉及并网审批的微电网项目还要校核变压器容量和并网接口线路优化模型的结果应作为电气设计输入而不是最终电气设计。8. 总结与下一步学习建议这篇文章从项目痛点出发梳理了风光储微电网容量规划中的混合不确定性、鲁棒优化和协同优化三个核心概念并给出了一个基于 PuLP 的最小可运行教学模型。你需要掌握的关键点可以归纳为四条第一容量规划的本质是投资决策与运行策略的联合优化不能把风电、光伏、储能分开算。第二混合不确定性可以理解为“概率场景 区间边界”的组合建模时要用多场景约束或对偶变换把不确定性转化为可行域约束。第三鲁棒优化的核心是追求方案在所有预设场景下都可行而不是只优化平均值。代价是结果更保守投资成本更高因此惩罚系数和场景选择要仔细标定。第四工程落地时数据质量、场景缩减、求解器选择往往比数学模型本身更影响项目成败。下一步你可以继续往三个方向深入学习学习 Pyomo 和 Gurobi 的组合用法构建基于 8760 小时数据的完整容量规划模型。研究分布鲁棒优化和 Wasserstein 距离解决“场景数量不够”或“分布估计不准”的问题。把规划模型和运行控制策略联动例如用模型预测控制MPC验证规划结果在实际运行中的表现。如果你正在做类似项目建议先不追求复杂的算法而是从本文的简化模型入手替换成自己的负荷曲线和资源数据跑通之后再逐步增加约束和场景。优化模型的建立过程本身就是加深对微电网系统理解的过程。
RELATED READING

延伸阅读

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