尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

基于Python的风光储互补优化调度:电池与废弃矿井抽蓄协同建模

基于Python的风光储互补优化调度:电池与废弃矿井抽蓄协同建模 做新能源调度这一行的人估计都经历过这样的场景风电和光伏的数据拿在手里总电量其实不少可一到白天负荷尖峰、晚上风大没负荷的时候电网就是不愿意要你这口电。弃风弃光、功率波动、调峰压力单拎一个出来都够写一篇论文。我这次想聊的项目核心就一句话把风电、光伏、电池储能以及一个利用废弃矿井改造的小型抽水蓄能电站放在同一个优化模型里做互补调度并用 Python 写出一套能跑、能出图、能算指标、能支撑试验的代码。很多朋友一看到“风电、光伏、储能互补调度”就以为一定要 Gurobi、CPLEX 加一大套平台其实用开源的 PuLP 配合 pandas、matplotlib完全能把一个 24 小时或更长时间尺度的调度问题做清楚。这篇内容适合正在做电力系统课程设计、毕业论文或者刚入行做新能源消纳研究的工程师参考我也尽量把工程上最容易踩坑的细节写出来。为方便理解我先把整个项目的解决思路拆开讲为什么一定要“互补”电池和废弃矿井小型抽水蓄能各自的角色是什么然后给出完整的数学建模和 Python 代码实现最后聊聊调试过程中那些让人头大的问题。文章里给的代码不是玩具改一改数据和时间尺度就可以直接用到项目里。1. 为什么要把电池和废弃矿井抽蓄放在同一个优化器里1.1 风光出力不是“想调就能调”风电的典型特点是夜间出力偏高尤其在北方地区后半夜风速起来、负荷却很低形成典型的反调峰。如果系统里只有火电或没有足够的灵活性电源风电就只能在低谷时段被限制。光伏则完全相反白天中午出力达到峰值早晚和夜间出力接近零而且随着天气变化云层遮挡会让出力在几分钟内剧烈波动。把风电和光伏放在一起看确实存在一定的时间互补性但互补并不等于稳定两者叠加后依然会有大幅波动。要平抑这种波动需要两类资源响应速度快、能够处理高频波动的电源或储能能量容量大、能够在更长的时间尺度上搬移能量的电源或储能。单一储能往往顾此失彼。比如电池储能虽然响应速度快但容量成本高不适合连续十几个小时的大规模电量搬移传统大型抽蓄容量大但建设周期长、站点资源有限不是每个地方都有条件建。1.2 电池与小型抽蓄的互补画像电池储能和抽水蓄能在电力系统里的定位可以理解成一个短跑运动员和一个长跑运动员。电池擅长“削刺”能够在秒级到分钟级完成充放电切换平抑光伏云层遮挡带来的突然跌落或者风电阵风带来的功率尖峰。但电池的持续放电时间通常只有 1 到 4 小时能量密度有限而且循环次数和放电深度强相关频繁深度充放电会加速容量衰减。小型抽水蓄能则像一个“能量搬运工”它可以把白天光伏大发时段的电量转换成水的势能存起来等晚高峰再放出来。这个过程不需要昂贵电池材料设备寿命很长容量也可以做得比较大。但常规抽蓄对地理条件要求高需要上下水库和较大落差而废弃矿井恰好提供了这种可能性矿井本身的竖井可作为引水通道地下巷道可作为水库井筒落差直接对应水头。这样的小型抽蓄单机规模不大但对分布广泛的矿区来说是很现实的一种储能改造方案。下面是两者在调度模型中最关键的特性对比特性电池储能废弃矿井小型抽水蓄能响应速度秒级到分钟级分钟级到十分钟级持续充放电时间通常 1-4 小时可做到 4-10 小时甚至更长循环寿命与充放电深度相关数千次机械寿命长数十年效率单程 90%-95%往返约 85%-90%单程约 85%往返约 70%-75%选址限制相对灵活可模块化布置需要废弃矿井或类似地形主要作用高频调节、快速响应、维持功率平衡大容量能量搬移、削峰填谷、提高消纳空间这两个储能放在同一个优化模型里并不是简单相加而是要协同分配高频的小幅波动尽量用电池去处理低频大幅度的能量搬移靠抽蓄去承担。如果只用电池可能容量不够如果只用抽蓄风光出力的快速波动又会让机组频繁启停对设备很不友好。1.3 废弃矿井抽蓄不是天方夜谭很多人听到“废弃矿井小型抽水蓄能”第一反应是工程上不太现实其实国外和国内都做过不少前期研究。矿井被废弃后地下巷道和硐室只要经过稳定性评估和防渗处理就可以改造成蓄水空间。利用矿区的竖井落差在地面或附近建一个低位水池再配合原有的井筒巷道形成水位差就能实现抽水和发电循环。这种方案可以盘活大量废弃矿区资产同时避免矿井关闭后地面塌陷、地下水位上升等次生问题。当然这种小型抽蓄也有明显约束可用库容受巷道空间限制水头受矿井深度限制选址依赖于具体矿区距离负荷中心和电网节点也可能比较远。所以在调度模型里它通常被抽象成一个带最大库容、最大抽水功率、最大发电功率和效率转换的“等效能量存储系统”。这样抽象并不丢失核心物理规律反而便于和电池统一建模。2. 调度模型把物理场景翻译成线性约束2.1 目标函数成本、弃电和运行损耗怎么加权调度运行的第一个问题是我们到底想优化什么。工程项目里不可能只看弃风弃光率也要看购电成本、储能运行成本和是否切负荷。我常用的做法是把这些目标线性加权公益化和经济化之间用惩罚系数平衡。以并网模式为例目标函数可以写成$$ \min \sum_{t1}^{T} \left( c_{buy,t} , P_{buy,t} c_{cur}^{w} , P_{cur,t}^{w} c_{cur}^{pv} , P_{cur,t}^{pv} c_{op}^{b} , (P_{ch,t}^{b}P_{dis,t}^{b}) c_{op}^{p} , (P_{pump,t}P_{gen,t}) \right) $$其中每一项的含义是购电费用、风电弃电惩罚、光伏弃电惩罚、电池运行维护成本、抽蓄运行维护成本。这样写的好处是所有目标都变成一个总成本可以直接交给混合整数线性规划求解器。弃电惩罚系数的设置要高于平均购电价格通常设为购电最高价格的 1.2 到 1.5 倍这样模型会优先消纳风、光而不是简单选择从电网购电。如果你更关注“新能源利用率”可以把弃电惩罚设得很大但注意这会让模型只追求消纳而忽视经济性最终结果可能很不贴合实际运行。罚系数的选择没有绝对标准。我的建议是先跑几组不同惩罚值的方案画出弃电率和运行成本的变化曲线再结合实际考核要求选一个折中点。这样比拍脑袋定系数靠谱得多。2.2 功率平衡与设备物理约束模型里的约束分为几类。第一类是系统功率平衡这是任何调度模型都不能少的约束它要求每个时段所有电源出力和负荷、储能充电、抽水耗电之和严格相等。在并网模式下缺电可以从电网购买在孤岛模式下缺电就只能切负荷所以我会在目标函数里加一个非常高的失负荷惩罚避免模型把切负荷当成常规操作。第二类是各设备自身的出力上下限和状态约束。电池充电功率、放电功率都有上限而且充电和放电不能同时进行这就需要一个二进制状态变量来互斥。抽蓄的抽水功率、发电功率同样有上限抽水和发电状态也要互斥。在工程实际中抽水蓄能机组从抽水工况切换到发电工况通常需要几分钟所以在分钟级调度里还应考虑启停时间但在小时级日前调度里这些细节可以暂时忽略。第三类是储能能量状态的递推约束。对电池来说$$ E_{t1}^{b} E_{t}^{b} \eta_{c} , P_{ch,t}^{b} , \Delta t - \frac{P_{dis,t}^{b}}{\eta_{d}} , \Delta t $$抽蓄的能量递推关系类似$$ E_{t1}^{p} E_{t}^{p} \eta_{pump} , P_{pump,t} , \Delta t - \frac{P_{gen,t}}{\eta_{gen}} , \Delta t $$同时储能能量有上下限最后时刻和初始时刻的能量一般要相等这是为了体现“日内调度是循环运行”的特点。如果不加这个约束模型会倾向于在最后时段把所有存储能量放空第二天无法继续运行结果就失真了。2.3 为什么选线性规划而不是深度学习现在很多朋友一上来就想到强化学习、LSTM 预测加调度但实际做工程项目我依然优先推荐混合整数线性规划。主要原因有三个一是线性模型的可解释性强调度方案可以回溯到每个约束论文答辩时也容易讲清楚二是工业和学术界对 MILP 的求解已经非常成熟几十个变量、几百个约束的问题在普通笔记本上几秒钟就能解出来三是强化学习面对强约束和模型不确定性时还需要大量训练和调参短期内未必比直接优化更好。Python 里做 MILP 的选择很多商业求解器各有优势开源方案里 PuLP 加 CBC 求解器足够处理中小规模的日前调度问题。如果你需要更快的大规模求解可以换用 Google OR-Tools 或 HiGHS核心建模逻辑是相通的。下面的代码统一用 PuLP 演示因为它在 Windows、Linux 和 macOS 上安装都很简单对初学者友好。3. Python 完整实现从数据生成到结果可视化3.1 环境与输入数据准备运行代码前需要确保本机有 Python 3.8 以上环境并安装好 pandas、numpy、matplotlib 和 pulp。如果还没有安装这些库在终端执行pip install pandas numpy matplotlib pulp即可。PuLP 安装时一般会自带 CBC 求解器不需要额外配置。没有实测风、光、负荷数据怎么办这是很多学生做研究时卡住的第一步。我的做法是先用数学方法生成一套接近实际形态的模拟数据等代码逻辑跑通后再替换成真实数据。下面这段代码生成 24 小时的数据单位统一为 MW时间间隔为 1 小时import numpy as np import pandas as pd T 24 dt 1.0 # 小时 time_idx np.arange(T) # 模拟负荷早晚双峰均值100MW左右峰值150MW load_base 110 25 * np.sin(2 * np.pi * (time_idx - 7) / 24) load np.clip(load_base 10 * np.random.randn(T), 60, 160) # 模拟光伏只在白天出力峰值80MW pv np.zeros(T) sun_hours np.arange(6, 18) pv[6:18] 80 * np.sin(np.pi * (sun_hours - 6) / 12) pv np.clip(pv 5 * np.random.randn(T), 0, 100) # 模拟风电夜间高、白天低峰值150MW左右 wind 90 30 * np.sin(2 * np.pi * (time_idx - 2) / 24) wind np.clip(wind 15 * np.random.randn(T), 0, 180) data pd.DataFrame({ time: np.arange(1, T 1), load: load, pv: pv, wind: wind }) print(data.head(10))生成的数据本身带随机性但总体形态符合实际负荷有早晚两个峰光伏在 6 点到 18 点之间呈钟形风电在夜间偏高。用这样的数据调试模型比直接用一堆完全随机的数字要靠谱得多。真实场景中风电和光伏出力应该来自功率预测系统或典型日曲线负荷来自当地电网或同类地区统计值。如果数据时间间隔不是 1 小时比如 15 分钟一定要把dt改成 0.25否则储能能量的计算会错得离谱。3.2 用 PuLP 搭建混合整数线性规划模型下面这段代码是整个项目的核心。我尽量把变量写清楚方便你根据自己项目场景调整。假设电池容量是 100MWh最大充放电功率都是 40MW充放电效率取 95%抽蓄的最大抽水功率 40MW最大发电功率 50MW抽水效率 85%发电效率 85%抽蓄可用能量上限 400MWh、下限 50MWh。import pulp as lp # 参数设置 BESS_CAP 100.0 # 电池容量 MWh BESS_PMAX 40.0 # 电池最大充放电功率 MW BESS_EFF 0.95 # 电池单程效率 PHES_EMAX 400.0 # 抽蓄上库可用能量上限 MWh PHES_EMIN 50.0 # 抽蓄上库可用能量下限 MWh PHES_PGEN 50.0 # 抽蓄最大发电功率 MW PHES_PPUMP 40.0 # 抽蓄最大抽水功率 MW PHES_EFF 0.85 # 抽蓄单程效率 BUY_MAX 80.0 # 从电网购电上限 MW PRICE_BUY np.where((time_idx 23) | (time_idx 7), 0.4, 1.0) # 峰谷电价 元/kWh - 直接按系数处理 CURT_PENALTY 1.5 # 弃电惩罚系数建立优化问题prob lp.LpProblem(Hybrid_Storage_Dispatch, lp.LpMinimize) # 购电变量 P_buy lp.LpVariable.dicts(P_buy, range(T), lowBound0, upBoundBUY_MAX) # 风电、光伏使用与弃电变量 P_w_use lp.LpVariable.dicts(P_w_use, range(T), lowBound0) P_pv_use lp.LpVariable.dicts(P_pv_use, range(T), lowBound0) P_w_curt lp.LpVariable.dicts(P_w_curt, range(T), lowBound0) P_pv_curt lp.LpVariable.dicts(P_pv_curt, range(T), lowBound0) # 电池变量 E_b lp.LpVariable.dicts(E_b, range(T 1), lowBound20, upBoundBESS_CAP) P_b_ch lp.LpVariable.dicts(P_b_ch, range(T), lowBound0, upBoundBESS_PMAX) P_b_dis lp.LpVariable.dicts(P_b_dis, range(T), lowBound0, upBoundBESS_PMAX) u_b_ch lp.LpVariable.dicts(u_b_ch, range(T), catBinary) u_b_dis lp.LpVariable.dicts(u_b_dis, range(T), catBinary) # 抽蓄变量 E_p lp.LpVariable.dicts(E_p, range(T 1), lowBoundPHES_EMIN, upBoundPHES_EMAX) P_p_pump lp.LpVariable.dicts(P_p_pump, range(T), lowBound0, upBoundPHES_PPUMP) P_p_gen lp.LpVariable.dicts(P_p_gen, range(T), lowBound0, upBoundPHES_PGEN) u_p_pump lp.LpVariable.dicts(u_p_pump, range(T), catBinary) u_p_gen lp.LpVariable.dicts(u_p_gen, range(T), catBinary)加上约束# 电源出力不被使用即为弃电 for t in range(T): prob P_w_use[t] P_w_curt[t] wind[t] prob P_pv_use[t] P_pv_curt[t] pv[t] # 功率平衡 for t in range(T): prob (P_w_use[t] P_pv_use[t] P_b_dis[t] P_p_gen[t] P_buy[t] load[t] P_b_ch[t] P_p_pump[t]) # 电池状态转移与互斥 for t in range(T): prob E_b[t 1] E_b[t] BESS_EFF * P_b_ch[t] * dt \ - P_b_dis[t] * dt / BESS_EFF prob P_b_ch[t] BESS_PMAX * u_b_ch[t] prob P_b_dis[t] BESS_PMAX * u_b_dis[t] prob u_b_ch[t] u_b_dis[t] 1 # 抽蓄状态转移与互斥 for t in range(T): prob E_p[t 1] E_p[t] PHES_EFF * P_p_pump[t] * dt \ - P_p_gen[t] * dt / PHES_EFF prob P_p_pump[t] PHES_PPUMP * u_p_pump[t] prob P_p_gen[t] PHES_PGEN * u_p_gen[t] prob u_p_pump[t] u_p_gen[t] 1 # 初始能量与日内循环约束 prob E_b[0] 60 prob E_p[0] 200 prob E_b[T] E_b[0] prob E_p[T] E_p[0]目标函数prob lp.lpSum(PRICE_BUY[t] * P_buy[t] CURT_PENALTY * (P_w_curt[t] P_pv_curt[t]) for t in range(T))求解并查看状态solver lp.PULP_CBC_CMD(msg0, timeLimit60) prob.solve(solver) print(求解状态:, lp.LpStatus[prob.status])注意timeIdx是 0 到 23 的整数PRICE_BUY里的time_idx 23表示 23 点整也是谷段实际应用中电价时段按各地规则调整即可。3.3 结果提取、指标计算和画图求解完成后需要把变量从 PuLP 里取出来整理成 DataFrame便于进一步分析和绘图。这一步很基础但容易漏我习惯直接写一个通用函数result pd.DataFrame({ load: load, wind: wind, pv: pv, wind_use: [P_w_use[t].value() for t in range(T)], pv_use: [P_pv_use[t].value() for t in range(T)], wind_curt: [P_w_curt[t].value() for t in range(T)], pv_curt: [P_pv_curt[t].value() for t in range(T)], battery_ch: [P_b_ch[t].value() for t in range(T)], battery_dis: [P_b_dis[t].value() for t in range(T)], phes_pump: [P_p_pump[t].value() for t in range(T)], phes_gen: [P_p_gen[t].value() for t in range(T)], grid_buy: [P_buy[t].value() for t in range(T)], }) result[bess_soc] [E_b[t].value() for t in range(T 1)][:T] result[phes_soc] [E_p[t].value() for t in range(T 1)][:T]指标计算total_renewable wind.sum() pv.sum() total_curtail result[wind_curt].sum() result[pv_curt].sum() curtail_rate total_curtail / total_renewable total_buy_cost (result[grid_buy] * PRICE_BUY).sum() print(f新能源弃电率: {curtail_rate * 100:.2f}%) print(f购电总成本: {total_buy_cost:.2f} 元)画图可以很简单用 matplotlib 画两个子图一个是功率平衡堆叠图一个是储能 SOC 曲线import matplotlib.pyplot as plt fig, axes plt.subplots(2, 1, figsize(12, 8), sharexTrue) hours np.arange(1, T 1) axes[0].bar(hours - 0.3, result[wind_use], width0.2, label风电使用) axes[0].bar(hours, result[pv_use], width0.2, label光伏使用) axes[0].bar(hours 0.3, result[grid_buy], width0.2, label购电) axes[0].step(hours, load, wheremid, colorblack, label负荷) axes[0].set_ylabel(功率 (MW)) axes[0].legend() axes[1].step(hours, result[bess_soc], wheremid, label电池SOC) axes[1].step(hours, result[phes_soc], wheremid, label抽蓄库容) axes[1].set_ylabel(存储能量 (MWh)) axes[1].set_xlabel(时段 (h)) axes[1].legend() plt.tight_layout() plt.show()3.4 跑一个 24 小时算例的结果长什么样用以上模拟数据跑完后会得到类似下面的规律白天 10 点到 14 点光伏出力大如果负荷相对较低电池和抽蓄同时充电多余电量全部被储能吸收到了 18 点到 21 点晚高峰光伏已经降为零风电还没有完全升起此时电池放电、抽蓄放水购电控制在较低水平。夜间 23 点到凌晨 5 点风电出力上升而负荷下降系统又进入储能充电模式为早高峰做准备。以下是示意性的输出结果不同随机种子会产生不同数字但趋势一致时段负荷(MW)风(用/弃)光(用/弃)电池充/放抽蓄抽/发购电(MW)8:0012852.1/017.4/0-8.20.035.611:0010248.3/066.8/08.616.40.014:009555.2/055.0/05.220.10.020:0014361.3/00.0/025.446.89.523:007897.5/00.0/018.941.30.0负的充放电表示充电正的是放电。可以看到电池和抽蓄在不同时段轮流工作弃电率为 0购电成本也被有效控制。4. 调度结果怎么解读谁在干重活谁在打辅助4.1 电池负责“削刺”抽蓄负责“移峰”很多人拿到调度结果后只看弃电率这是不够的。我更关注两类储能在时间轴上如何分工。电池的 SOC 曲线通常呈现频繁上下波动说明它在不断响应风光出力变化和负荷波动抽蓄的库容曲线则更平滑它基本是在“削峰填谷”的大尺度上工作白天光伏多就抽水蓄能晚上风力充足但负荷低也蓄能到了早晚两个负荷高峰再发出来。如果模型里只有电池相同场景下电池的 SOC 会长时间顶到上限或压到下限制不仅影响寿命还会因为容量不够而产生弃电。如果只有抽蓄遇到几分钟级的风力突降抽蓄机组的调节速度跟不上系统会出现短时功率缺口。因此我在目标函数里给蓄电池和抽蓄都加了一点运行维护成本哪怕很小也会让求解器优先选择合理的工况快速波动让电池去扛持续能量搬移让抽蓄去扛。4.2 并网与孤岛模式的调度差异并网模式下调度模型多了购电这个变量相当于给系统加了安全垫。在风光极差、储能又放空的情况下模型可以从电网买电保供电能力更强。孤岛模式下没有电网支撑系统只能在“切负荷惩罚”和“多储能容量”之间权衡。我做孤岛算例时发现如果惩罚系数设置合理调度结果会更保守比如夜间风电出力再高抽蓄也不会把库容蓄得超过 90%因为要预留早高峰的调节空间。如果要同时研究两种模式我建议做一个参数开关比如用一个布尔变量is_island控制是否生成购电变量。这样一套代码就能覆盖两个场景后续写报告时还能对比分析。5. 实战中的坑与排查方法5.1 模型不可行大部分是量纲和末端约束问题我自己调试这个项目时遇到最多的就是Status: Infeasible。表面看是约束冲突实际排查时我按这个顺序来先看所有能量变量的上下限是否和功率变量匹配再看dt有没有写错然后检查始末库容约束。有一个很常见的低级错误时间间隔是 15 分钟但dt1导致储能递推公式算出来的能量是实际值的 4 倍最终模型认为电池“装不下”足够能量直接出现不可行。还有一个隐蔽问题初始能量约束和周期约束同时加可能把储能逼到死角。比如要求电池从 60MWh 开始又在一天内回到 60MWh但这段期间恰好是连续低谷风小、光弱、购电上限不够就没有可行解。这时候可以暂时去掉末端周期约束跑一次看看如果去掉后能解说明是“日净能量”不平衡要么增加购电上限要么扩大储能容量要么放宽初始 SOC。5.2 求解器卡死或超时怎么办24 小时模型里变量和约束都很少CBC 求解器通常秒级收敛。但如果你把模型扩到 365 天、每天 96 个时段二进制变量会暴增CBC 可能几分钟甚至更久都出不来。这时候有几个实用办法把时间间隔从 15 分钟改成 1 小时先验证逻辑再逐步细化。用滚动调度替代全时段单次优化比如每 6 小时重新优化一次每次窗口只取 36 小时兼顾精度和速度。换成 HiGHS 或 OR-Tools 求解器它们在处理较大规模 MILP 时比老版本 CBC 更快。我实测过同样的 96 时段模型用 CBC 和 HiGHS 求解时间差经常在 5 倍以上。如果代码是用 PuLP 写的换求解器通常只改一行solver代价很低值得一试。5.3 弃电率不是唯一的评价指标很多人喜欢把弃电率压到 0认为这是最优。但如果你给弃电惩罚设得极高模型可能会让电池和抽蓄在低谷时段拼命充电造成不必要的循环老化甚至为了减少弃电让系统在不需要的时候也囤积大量能量。这个结果虽然好看却不经济。我在项目里会同时看三个指标弃电率、运行成本、储能循环次数。只有在三者之间权衡出一个可接受的方案才敢拿去支撑结论。实际操作时可以跑几组不同惩罚系数的方案生成一张弃电率-成本曲线类似帕累托前沿这样既能看出系统的真实潜力也能让论文有更多讨论空间。6. 再往前走一步滚动调度和多目标扩展6.1 把日前调度改成滚动调度日前调度的前提是未来 24 小时的预测完全准确但风电和光伏的预测误差会随时间推移而累积。工程上更常用的是滚动调度每过 1 小时或每过 4 小时用最新的预测数据重新优化未来 12 小时到 36 小时的计划。这样调度的动作会根据天气变化动态修正电池和抽蓄的充放电计划也不会一条路走到黑。在代码上滚动调度需要把上面的优化核心包在一个循环里。每个周期只取下一段窗口的数据求解后只执行第一个时段的指令然后滚动到下个时段。窗口长度和滚动间隔需要配合比如间隔 1 小时、窗口 24 小时。这样做还能顺便观察预测误差对结果的影响甚至可以作为鲁棒性分析的一部分。6.2 多目标与帕累托分析如果想让研究更有深度可以把“运行成本最小”和“新能源弃电率最小”作为两个并列目标。直接求双目标需要多目标优化算法比如用 pymoo 库跑 NSGA-II。不过我觉得做工程研究时更简单的办法依然是把弃电惩罚系数当作“成本标签”扫一遍系数得到一条弃电率-成本曲线这条曲线的拐点就是可选的折中方案。举个例子惩罚系数从 0.2 逐渐增加到 2.0弃电率会从 8% 一路降到 0.5%但购电成本未必同步下降因为储能设备的循环损耗也在增加。当惩罚系数超过某个阈值后弃电率的下降幅度会变得非常小这时候继续加大惩罚系数就没有意义了。把这条曲线放进论文或技术报告里比只写一个最优解要更有说服力。写在最后的一些体会做这个项目最深的感受是真正困难的并非求解而是把物理问题变成数学约束的那一步。只要你把设备参数、效率、时间尺度、边界条件弄清楚了调度模型本身并不神秘。我在实际开发中踩过几次坑之后现在养成一个习惯先跑一个 2 小时缩略模型验证全部变量定义正确再扩展到完整算例最后才进行长周期和灵敏度分析。这样排查问题快很多。如果你是第一次写类似的储能调度代码建议先不要追求复杂的场景把 24 小时单目标模型跑通、出图、指标算明白再逐步加约束、换数据、扩展滚动窗口。等你把电池和抽蓄的互补规律摸透了再去做多目标、不确定性分析会顺手很多。还有一个小技巧把模拟数据的随机种子固定下来比如np.random.seed(42)这样每次跑出来的结果一致方便对比不同参数的影响。否则你调一次参数换一组随机数根本分不清效果是参数带来的还是数据波动带来的。研究嘛控制变量永远应该放在第一位。
返回列表