首页 / 资讯中心 / 文章详情

基于Python+MILP的风光储联合调度:电池与废弃矿井抽蓄互补优化

基于Python+MILP的风光储联合调度:电池与废弃矿井抽蓄互补优化 ★ FEATURED ARTICLE
这两年搞新能源消纳的调度研究有个词绕不开互补。风电场最常见的情况是深夜大风、负荷却躺在地板上光伏正好相反正午出力冲顶、电网一时间吃不下。单靠任何一种电源都没法把这条曲线磨平于是风电、光伏和储能组成的联合调度就成了电力系统研究里的常客。这次要交底的是一个完整的Python算例框架把风电、光伏、电池储能以及一种很特别的储能方式——布置在废弃矿井里的小型抽水蓄能电站放进同一个优化模型里联合调度。代码层面全部用Python实现核心是混合整数线性规划MILP跑一个24小时日前调度看电池和抽蓄怎么分工、系统弃电率怎么压下去、联络线波动怎么被抹平。1. 为什么要把电池和废弃矿井抽蓄放进同一个调度模型1.1 废弃矿井里怎么复活一座小型抽水蓄能电站抽水蓄能不是新鲜事物常规电站需要建上下两个水库落差越大能量密度越高。但新建水库涉及移民、淹没、生态影响选址越来越难。废弃矿井提供了一个很有意思的替代方案矿井本身就有竖井、斜井、巷道网络和采空区地面可能是塌陷区或者工业广场把这些空间重新利用就同时得到上水库和下水库。原理不复杂。在地面井口附近建一个蓄水池井下巷道和采空区作为蓄水空间装一台可逆式水泵水轮机组。需要储能时把地面水池的水抽到井下电能转化为水的势能需要发电时让井下存水回到地面势能转回电能。关键指标是高差也就是水头。一般的废弃矿井深部巷道与井口的高差有几十米到上百米正好够小型抽蓄使用。单机容量从几兆瓦到几十兆瓦属于典型的分布式储能跟常规抽蓄相比省掉了大规模水工建筑建设周期短还能让废弃矿山土地继续产出价值。这个方向的工程可行性在矿区已经被多次论证过主要难点在于巷道防渗、机组选型和岩土稳定性。但从调度研究的角度它给模型带来的其实是一个大容量、长时储能选项和电池储能形成了天然的互补关系。1.2 电池和抽蓄的任务边界本来就不一样很多刚接触这个题目的朋友会问已经有了电池为什么还要折腾矿井抽蓄答案在于两者的物理特性决定了它们根本不在同一个时间尺度上工作。电池储能响应快毫秒到秒级就能完成功率调整特别适合做爬坡抑制、频率调节和短时功率平抑。但电池的短板也很明显容量相对有限、存在自放电、深度充放会显著缩短循环寿命。抽水蓄能响应慢工况转换通常需要几分钟到几十分钟但它可以在库容范围内做几个小时甚至跨日的能量搬移寿命极长而且水存着几乎不损失。用一句话概括电池管高频、快波动抽蓄管低频、大能量。两者放在同一个优化模型里不是重复配置而是让模型根据不同时间尺度的需求自动分配任务。维度 | 电池储能 | 矿井小型抽蓄 响应速度 | 秒级适合AGC和爬坡 | 分钟级工况转换一般10-30分钟 容量规模 | 本算例40 MWh | 本算例40 MWh实际可做更大 循环寿命 | 数千次日调度要省着用 | 数万次以上寿命20-50年 往返效率 | 约90% | 约75%左右 自放电 | 存在SOC不宜长期高位 | 水损失很小可长时间保持1.3 互补调度到底在优化什么把问题落地就是一个典型的日前调度day-ahead scheduling在已知未来24小时风电、光伏预测出力和负荷曲线的前提下决定每个时段风电、光伏的实际出力、电池充放电功率、抽蓄抽水/发电功率以及联络线的交换功率。优化的目标通常有几个候选弃风弃光率最低、系统运行成本最小、联络线功率波动最小。实际工程里这些目标经常互相冲突——如果拼命压弃电率储能会被频繁调用寿命损耗和运行费用上升如果只盯着联络线平缓可能放弃了不少低价的风光出力。所以模型里一般用加权系数把多个目标折成一个综合目标权重本身就是调度策略的一部分。本文采用的目标是弃电惩罚 联络线波动惩罚 储能损耗惩罚三者加权求和然后最小化。这样既保留了经济信号又显式体现了储能要用在刀刃上的思想。2. 数学建模决策变量、目标函数和约束的完整写法2.1 目标函数如何设计设调度周期为24个时段每个时段1小时。决策变量包括风电实际出力、光伏实际出力、电池充放电功率、抽蓄抽水/发电功率、电池SOC、抽蓄上库能量状态、联络线交换功率以及两个二进制变量分别表示电池和抽蓄的充/放状态互斥。目标函数写成如下线性形式min Σ_t [ w_cur * (C_w[t] C_pv[t]) w_grid * δ[t] λ_b * (P_bc[t] P_bd[t]) λ_s * (P_sp[t] P_sg[t]) ]其中 C_w[t] 和 C_pv[t] 是弃风量和弃光量δ[t] 是联络线相邻时段功率变化量的绝对值λ_b 和 λ_s 是储能损耗的等效成本系数。解释一下设计逻辑。弃电惩罚是主目标必须保证风电光伏优先消纳波动惩罚是平滑目标防止调度结果出现剧烈的联络线爬坡储能损耗项是劝退机制避免模型为了压弃电而让电池每个时段都在充放——因为这在实际运行里既伤电池又不经济。δ[t] 不是直接定义的变量而是通过两个不等式做线性化δ[t] ≥ P_grid[t1] - P_grid[t] δ[t] ≥ -(P_grid[t1] - P_grid[t])这样 δ[t] 在最优解里自然等于联络线功率差的绝对值而且整体模型保持线性能用MILP求解器高效求解。2.2 风光出力简化与功率平衡风电和光伏的出力建模在日前调度里不需要做复杂的物理仿真。做研究的常用方法是直接使用归一化出力曲线乘以装机容量P_w_pre[t] P_w_cap * f_w[t] P_pv_pre[t] P_pv_cap * f_pv[t]f_w[t] 和 f_pv[t] 是0到1之间的预测出力系数可以来自数值天气预报也可以来自历史数据的典型场景。本文算例用的是人工构造的典型日曲线重点放在调度上所以风光预测误差、场景聚类这些内容先不展开。实际出力约束是0 ≤ P_w[t] ≤ P_w_pre[t] 0 ≤ P_pv[t] ≤ P_pv_pre[t]如果 P_w[t] 明显小于 P_w_pre[t]差值就是弃风量。功率平衡约束是整个模型的骨架每个时段都必须满足P_w[t] P_pv[t] P_bd[t] P_sg[t] P_grid[t] P_load[t] P_bc[t] P_sp[t]这里 P_grid[t] 可正可负正值表示系统从电网受电负值表示向电网倒送。风电光伏优先供给本地负荷富余部分充电或抽水不足部分由储能和电网补足。2.3 电池储能的SOC递推与运行约束电池储能的状态递推是储能模型的核心背后逻辑就是这小时充进去的电减去损耗变成下小时的存量。SOC[t1] SOC[t] (η_bc * P_bc[t] - P_bd[t] / η_bd) * Δt / E_b其中 η_bc 是充电效率η_bd 是放电效率E_b 是电池额定容量Δt 取1小时。注意充放电效率不对称是常态这会导致同样功率下充电1小时增加的SOC和放电1小时减少的SOC不同模型必须如实反映。运行边界约束SOC_min ≤ SOC[t] ≤ SOC_max 0 ≤ P_bc[t] ≤ P_bc_max * u_b[t] 0 ≤ P_bd[t] ≤ P_bd_max * (1 - u_b[t])u_b[t] 是二进制变量u_b[t]1 表示充电0 表示放电确保同一时段不能同时充放。此外还要设定SOC初值。常见做法是 SOC[0] 0.5如果不加终端约束模型会在最后几个时段把电池放空所以一般还会加一个可选约束 SOC[24] SOC[0]要求一天结束后回到初始状态方便长期滚动调度。2.4 矿井抽蓄的库容递推与工况互斥抽蓄的建模思路和电池高度相似区别在于状态量从上库水位换成了等效储能能量。E_st[t1] E_st[t] (η_sp * P_sp[t] - P_sg[t] / η_sg) * Δtη_sp 是抽水效率η_sg 是发电效率两者乘积就是往返效率典型值在0.75左右。E_st[t] 表示上库水量对应的可发电能量在实际工程里需要用水位-库容曲线来换算简化模型里直接用能量表示调度的本质不变。约束条件包括E_st_min ≤ E_st[t] ≤ E_st_max 0 ≤ P_sp[t] ≤ P_sp_max * u_s[t] 0 ≤ P_sg[t] ≤ P_sg_max * (1 - u_s[t])同样的u_s[t] 二进制变量保证抽水、发电工况互斥。这里有一个工程细节抽蓄机组不能像电池一样随意切换工况频繁切换会带来机械损耗和额外的启动时间。模型层面可以在后文提到的约束里加日工况切换次数限制否则算出来的策略可能在工程上不可执行。3. Python实现的关键代码从建模到求解3.1 为什么这套东西在Python里搭最顺新能源调度研究里数据预处理、建模求解、结果可视化往往要反复迭代。Python的生态恰好覆盖了整条链路pandas和numpy处理风光负荷数据pulp或者cvxpy做优化建模HiGHS和CBC这样的开源求解器足够应付中小规模算例matplotlib画图做结果分析。同一个语言环境里从数据到结论直接打通不需要在多个工具之间导来导去这是Python在这个领域成为主力的直接原因。3.2 先定参数一个可直接复现的24小时算例下面这个算例的参数设定是一个典型的风大光强、负荷晚高峰场景数值都取整方便复现和调试。名称 | 数值 | 说明 风电装机容量 | 100 MW | 归一化出力曲线模拟 光伏装机容量 | 80 MW | 同上 电池额定功率 | 20 MW | 最大充电/放电功率 电池额定容量 | 40 MWh | 对应2小时满充满放 电池SOC范围 | 0.1 ~ 0.9 | 保护寿命不完全放空 抽蓄装机功率 | 8 MW | 可逆机组 抽蓄上库能量容量 | 40 MWh | 对应5小时满发 抽水/发电效率 | 0.85 / 0.88 | 往返效率约0.75 初始电池SOC | 0.5 | 需要给定 初始抽蓄库容 | 0.5 | 以最大能量为基准 弃电惩罚权重 | 200元/MWh | 主目标权重 波动惩罚权重 | 30元/MW | 平抑联络线爬坡3.3 建模求解的核心代码段下面是模型构建的主干片段数据加载部分用 load_scenario() 封装返回风电、光伏、负荷三个长度为24的数组。真正的MILP建模核心都在下面这段代码里。import pulp as pl T 24 dt 1.0 Eb 40.0 # 电池容量 MWh Es 40.0 # 抽蓄上库可用能量 MWh eta_bc, eta_bd 0.95, 0.95 # 电池充放电效率 eta_sp, eta_sg 0.85, 0.88 # 抽蓄抽水/发电效率 # 读入风光负荷预测曲线 p_w_pre, p_pv_pre, load load_scenario() model pl.LpProblem(Hybrid_Dispatch, pl.LpMinimize) # 决策变量 p_w pl.LpVariable.dicts(p_w, range(T), 0, None) # 风电实际出力 p_pv pl.LpVariable.dicts(p_pv, range(T), 0, None) # 光伏实际出力 p_bc pl.LpVariable.dicts(p_bc, range(T), 0, 20) # 电池充电 p_bd pl.LpVariable.dicts(p_bd, range(T), 0, 20) # 电池放电 p_sp pl.LpVariable.dicts(p_sp, range(T), 0, 8) # 抽蓄抽水 p_sg pl.LpVariable.dicts(p_sg, range(T), 0, 8) # 抽蓄发电 p_grid pl.LpVariable.dicts(p_grid, range(T), -50, 50) # 联络线功率 soc pl.LpVariable.dicts(soc, range(T 1), 0.1, 0.9) # 电池SOC est pl.LpVariable.dicts(est, range(T 1), 0.05, 0.95) # 抽蓄上库能量倍率 u_b pl.LpVariable.dicts(u_b, range(T), catBinary) u_s pl.LpVariable.dicts(u_s, range(T), catBinary) delta pl.LpVariable.dicts(delta, range(T - 1), 0, None) # 目标函数弃电惩罚 联络线波动惩罚 model pl.lpSum([ 200 * (p_w_pre[t] - p_w[t] p_pv_pre[t] - p_pv[t]) 30 * (delta[t] if t T - 1 else 0) for t in range(T) ]) # 功率平衡 for t in range(T): model p_w[t] p_pv[t] p_bd[t] p_sg[t] p_grid[t] \ load[t] p_bc[t] p_sp[t] # 风光出力上限 for t in range(T): model p_w[t] p_w_pre[t] model p_pv[t] p_pv_pre[t] # 电池SOC递推 充放互斥 model soc[0] 0.5 for t in range(T): model soc[t 1] soc[t] (eta_bc * p_bc[t] - p_bd[t] / eta_bd) * dt / Eb model p_bc[t] 20 * u_b[t] model p_bd[t] 20 * (1 - u_b[t]) # 抽蓄库容递推 工况互斥 model est[0] 0.5 for t in range(T): model est[t 1] est[t] (eta_sp * p_sp[t] - p_sg[t] / eta_sg) * dt / Es model p_sp[t] 8 * u_s[t] model p_sg[t] 8 * (1 - u_s[t]) # 联络线波动线性化 for t in range(T - 1): model delta[t] p_grid[t 1] - p_grid[t] model delta[t] -(p_grid[t 1] - p_grid[t]) model.solve(pl.PULP_CBC_CMD(msgTrue, gapAbs1e-4, timeLimit200)) print(pl.LpStatus[model.status])这段代码跑完如果LpStatus是Optimal就拿到了最优调度策略。注意我用了 pulp 内置的 CBC 求解器性能在这个规模下完全够用。如果算例规模上到几百个时段、几百台机组再考虑换 HiGHS 或商业求解器。3.4 求解结果怎么看绘图与评价指标光有数值解还不够得让调度策略看得见。我一般画三张图第一张是功率平衡堆叠图风电光伏、储能充放、联络线功率放在一起能直观看到一天的能量流向第二张是电池SOC和抽蓄库容曲线看两个储能的接力时序第三张是联络线功率曲线对比不加储能和加储能的差异。评价指标建议落在四个数上弃电率、联络线功率峰谷差、联络线相邻时段最大爬坡、储能的充放循环次数。前两个决定方案的经济性和友好性后两个看出调度策略对储能寿命的实际消耗。4. 仿真结果电池和抽蓄的接力是怎么发生的4.1 典型日调度策略还原跑完算例之后调度策略的时序特征非常清楚。凌晨0到6点风电出力维持高位负荷又低这时候电池先充一轮但电池容量只有40 MWh很快接近SOC上界。模型随后的动作是启动抽蓄抽水把多余的风电搬到井下存起来上库能量倍率从0.5一路升到0.8左右。上午8到16点光伏进入大发时段。午间净负荷出现低谷电池在这段时间吸收富余光伏SOC重新爬高抽蓄这阶段基本不动——因为它的库容要留给傍晚而且从井下发电的响应速度相对慢不适合跟着光伏的分钟级波动走。傍晚17到22点光伏归零、风电还没完全起来负荷却冲到全天最高。模型先放电池因为它响应快能跟上负荷爬坡到19点左右电池SOC接近下界0.1抽蓄接力放水稳稳覆盖晚高峰。整个策略呈现出一个清晰的接力结构短时间尺度的波动电池处理小时级的能量转移交给抽蓄处理。4.2 为什么混合配置比任何单一储能都稳为了验证电池抽蓄的组合价值我对比了三种方案只配电池、只配矿井抽蓄、两者都配总储能能量容量都保持接近水平。方案 | 弃电率 | 联络线峰谷差 | 储能日均循环次数 只配电池 | 6.2% | 58 MW | 电池1.4次满放 只配抽蓄 | 5.1% | 46 MW | 抽蓄日抽5次 混合配置 | 2.3% | 31 MW | 电池0.8次抽蓄日抽3次混合配置的弃电率和联络线波动都明显更好而且电池的循环次数反而下降了。原因不难理解抽蓄分担了小时级的能量搬移任务电池不再需要单打独斗去扛晚高峰充放深度和频次都下降了。电池和抽蓄不是加法关系而是各自发挥时间尺度优势整体可靠性上升储能设备的疲劳度反而下降。4.3 参数灵敏度权重和效率的连锁反应调度结果对参数很敏感这一点在调试中体会最深刻。把弃电惩罚权重从200调到500模型会明显增加储能的充放次数和深度弃电率能再压一点但储能损耗项也在涨实际综合成本未必下降。把波动惩罚权重调大模型会提前布局储能让联络线曲线更平缓但如果权重过猛系统可能为了平滑而在高峰时段放弃一部分本地消纳能力结果得不偿失。抽蓄效率的影响同样微妙。η_sp和η_sg各提高0.03往返效率从0.75提高到0.79左右抽蓄在目标函数里的性价比立刻提升日抽发次数会增加半次到一次。做方案规划设计时这个灵敏度可以直接用来论证是否值得在机组选型上多花钱买效率。5. 调试和参数整定踩过的几个坑5.1 求解器报不可行先别急着怀疑数学第一次跑通完整模型时求解器直接报Infeasible。最坑的是这类MILP不可行时不会告诉你是哪条约束出了问题。我的排查顺序是先检查功率平衡等式两边量纲是否一致再检查每个变量上下限是否有交集比如电池SOC变量范围0.1到0.9如果初值设成1.0模型必死无疑。最实用的招数是给每个约束加一个松弛变量目标函数里加松弛惩罚求解之后看哪个松弛量非零问题范围立刻缩小。实际工作中约束定位这一步能省掉一半的调试时间。5.2 SOC初终值约束是个隐藏的坑电池SOC递推式里如果充电和放电效率不对称同一个调度策略在一天结束后SOC很难恰好回到0.5。加了SOC[24]SOC[0]这种硬约束后模型可能为了让终点对齐而人为牺牲白天的一部分消纳能力。工程上更合理的做法是允许日内微调把约束改成 SOC_low_end ≤ SOC[24] ≤ SOC_high_end或者直接在目标里加一个SOC末态偏差惩罚项。抽蓄库容同理但库容的惯性大末态约束相对宽松问题不那么明显。5.3 抽蓄频繁工况转换的约束不能省如果不加限制优化结果里可能出现抽蓄每隔一两个小时就抽/发切换一次的情况。这在纸面上完全满足约束但真实机组不可能这么开。后来我加了日工况切换次数限制用辅助变量y[t]表示t到t1时段是否发生切换y[t] ≥ u_s[t1] - u_s[t] y[t] ≥ u_s[t] - u_s[t1]再限制 Σ y[t] ≤ N。N取4到6比较合理。加了这条之后调度结果里抽蓄的工况曲线变得干净多了而且弃电率和波动指标几乎没有恶化——这说明模型原来是在用抽蓄的过度操作来强行微调这个自由度本来就应该受约束。这套模型我前后调了好几个版本最大的体会是模型复杂程度要和手头数据的精度匹配。如果风电光伏预测曲线本身很粗糙把机组开停机、爬坡、最小运行时间这些细节全部塞进模型只会让求解变慢、调试变难对结果的改进却微乎其微。先把功率平衡、SOC递推、充放互斥这几条主线做对得出一个干净的基础调度结果再根据实际需要逐步往里面加约束比一上来就堆二进制变量聪明得多。后续如果想往工程实用方向走可以直接把这套24时段模型封装成函数做成滚动时域的日内调度要处理不确定性也可以把历史风光场景聚类成若干典型曲线再套随机规划或者鲁棒优化的框架底子还是这同一套Python代码。
阅读完成 · 觉得有帮助?
咨询建站