
开头先讲一个真实感触风光储互补调度这件事很多人一开始以为它是个“算法问题”跑到最后才发现它其实是“时序问题”加“工程取舍问题”。我接这个课题时的场景很典型——有风机、有光伏板、有电池储能甚至还有一个废弃矿井改造的小型抽水蓄能设备都齐了但调度策略还是“看着天发电缺了再放”结果弃电率居高不下电池衰减又快。后来把风电、光伏和储能的互补调度运行做成Python代码实验才算真正把这块骨头啃下来。这篇文章的定位很明确给正在做新能源调度研究、或者想用Python实现风光储协同优化的朋友提供一套从物理机理、数学建模到代码落地、再到实验验证的完整思路。不灌水不堆理论尽量把我在实际调试中遇到的问题、走过的弯路、以及最后验证可行的方案都摊开讲。适合有基础Python和优化求解器使用经验的研究生、工程师也适合刚入门但愿意啃硬骨头的新手慢慢读。1. 风光发电的波动是本质问题不是设备问题先聊一个容易被忽略的起点风电和光伏的波动不是设备故障也不是运维不到位而是物理规律决定的。风从哪来、太阳什么时候照都不以我们的意志为转移。调度研究之所以存在就是因为发电侧不可控、负荷侧不确定中间的储能又存在效率损耗和寿命衰减三种因素叠在一起才形成了复杂的优化问题。1.1 风电和光伏的“错峰”规律风电出力通常有明显的夜间高、白昼低特征尤其在春秋季和冬季夜间大风频繁光伏出力则恰好相反——白天晴朗时段达到峰值夜晚和阴天则接近于零。这两种电源在日尺度上天然存在一个“错峰”窗口白天光伏顶上、夜间风电顶上。但问题在于光有“日互补”是远远不够的因为遇到连续阴雨天加静风期风电光伏同时疲软这时候就必须靠储能来承担跨时段的电量搬运。季节尺度的互补性也很关键。我国很多地区夏季光照强但风速偏低冬季风速大而日照短真正理想的“风光年互补”场景并不普遍。所以调度模型不能只看一天要看连续多日、甚至跨季度的电量平衡。这也是我在代码里坚持用滚动窗口而不是静态全时段优化的原因之一。1.2 电池与废弃矿井抽蓄两条互补的储能技术路线储能端我同时研究了锂电池和废弃矿井小型抽水蓄能这两者的物理特性和调度约束差别非常大特性锂电池储能废弃矿井抽水蓄能响应速度毫秒到分钟级分钟级启停有最小时间约束能量转换效率充放电综合效率约85%-95%抽水/发电综合效率约65%-75%功率与容量耦合功率与容量可独立配置功率取决于水头与机组容量取决于上下库容寿命特征循环次数衰减明显设备寿命长主要受机械维护影响地理限制基本无必须有合适的废弃矿井或地形落差这两条路线不是互斥的反而互补电池适合秒级到小时级的快速调频和削峰抽蓄适合日尺度到周尺度的能量搬移。废弃矿井改造的小型抽水蓄能虽然单机容量不大但它解决了两个现实痛点一是矿区废弃后基础设施闲置二是锂电池的规模化成本压力。需要注意的是抽蓄电站的最小抽水/发电功率通常不能随意设定必须满足机组稳定运行区间这一点在数学建模里要单独处理否则优化结果在工程上根本执行不了。1.3 调度研究的真正难点时序、预测与滚动把物理规律映射到调度模型后核心难点就浮出水面了风光出力是时序数据调度决策必须考虑“现在做什么、未来可能发生什么”而未来又只能靠预测预测必然有误差。这意味着寻优不能只看单一时段而是要在时间轴上做“前瞻性安排”——这正是模型预测控制MPC思想的核心。打个比方就像开车去一个陌生城市你手里有地图预测数据但路况会变所以不能一次性把全程路线锁死而是开一段、看一段、再修正一段。调度也一样早上7点做的计划到了10点发现风突然大了必须重新计算。所以我的代码框架采用的是“滚动时域优化”每个调度周期以最新预测数据重算未来若干小时的计划只执行当前时段的指令到下一个周期再更新。2. 把调度问题写成数学语言目标函数与约束的取舍这一节从数学建模视角拆解整个问题。很多人一上来就写代码结果模型缺胳膊少腿后面跑出来的结果完全没法看。建模的意义在于用一套规范的语言把工程需求翻译成可求解的数学问题它是整个Python实现的基石。2.1 决策变量机组出力和储能功率如何离散化我采用的调度时间尺度是15分钟一个时点一天96个时段整个滚动窗口取24小时也就是每个优化周期内396个时点24小时×96个点滚动窗口为24小时。决策变量分两类各时段的风电出力、光伏出力、储能充放电功率、以及与电网的交互功率对于抽水蓄能还需要定义“抽水工况”“发电工况”“停机工况”的整数状态变量这一点和电池储能不同电池的连续充放电功率可以直接用连续变量表示而抽蓄机组存在离散工况转换。风电光伏的出力一般不作为自由决策变量而是作为已知输入由预测数据给定调度模型决定的是“用多少、存多少、弃多少”。为了描述弃风弃光我在模型里引入了弃电变量它在目标函数中的惩罚系数通常低于储能充电的“收益”这样模型不会为了堆高储能SOC而随意弃电也不会为了追求表面上的高消纳而让储能过度充放、加剧衰减。2.2 目标函数从最小化弃电量到全生命周期成本早期版本的模型目标函数很简单就是最小化弃风弃光电量。但很快发现这个目标会导致“储能被当成垃圾桶”——只要有弃电就拼命充电结果电池循环次数飙升、寿命骤减综合经济账一算反而亏了。后来我调整了目标函数采用“运行成本最小化”框架主要包含以下部分弃电惩罚项弃风弃光量乘惩罚电价倒逼系统优先消纳新能源储能充放电损耗与衰减成本锂电池的循环衰减按等效循环次数折价抽蓄机组的启停磨损也计入惩罚与电网交互成本从电网购电有成本向电网售电有收益负荷缺电惩罚如果风光储联合出力满足不了负荷则按缺电惩罚系数计入这个系数必须设得非常大否则模型会选择牺牲负荷来省钱。目标函数的数学形式大致是[ \min \sum_{t} \left( \alpha \cdot P_{curtail,t} \beta \cdot P_{buy,t} - \gamma \cdot P_{sell,t} \delta_{batt} \cdot C_{batt,t} \delta_{phs} \cdot C_{phs,t} \lambda \cdot P_{loss,t} \right) ]其中 (P_{curtail,t}) 是弃电功率(P_{buy,t}) 和 (P_{sell,t}) 是购售电功率(C_{batt,t}) 和 (C_{phs,t}) 分别是电池和抽蓄的运行衰减指标(P_{loss,t}) 是失负荷功率(\lambda) 是失负荷惩罚系数通常取极大的值比如缺电成本的百倍。这里的参数取值直接影响调度行为需要结合具体电价机制和储能寿命参数调优。2.3 约束条件功率平衡、SOC、水量平衡与机组工况约束是调度模型的“骨架”。我梳理了一下必须处理的核心约束包括功率平衡约束每个时点风电出力光伏出力储能放电功率购电功率 负荷功率储能充电功率弃电功率售电功率电池储能约束SOC递推方程 (SOC_{t1}SOC_t \eta_{ch}P_{ch,t}\Delta t - P_{dis,t}\Delta t / \eta_{dis})同时SOC不能超过上下限充放电功率不能超过额定值抽水蓄能约束上水库水量递推、下水库水量平衡、水库库容上下限、机组功率必须在最小稳定出力以上且抽水/发电/停机状态互斥爬坡约束风电、光伏的出力变化率一般不做限制但电池和抽蓄机组的功率变化率在实际工程中要约束防止指令过激导致设备损坏。其中抽蓄的水量平衡约束是建模中最容易写错的地方。很多论文直接照搬通用储能模型用“能量”替代“水量”忽略了抽水和发电的效率差异以及水量守恒的空间关系。我的做法是用上水库水量作为状态变量抽水时水量增加、发电时水量减少二者都乘以各自的效率系数同时严格保证上库水量上下限。这样模型才能真实反映废弃矿井改造项目的物理限制。2.4 滚动时域预测控制为什么静态计划在现实中不可用如果直接用一天的完整预测数据做一次性优化然后全天照着执行会出两个问题一是预测误差会随时间累积午后突然来的乌云会让午间计划彻底失效二是储能SOC的“后见之明”——静态优化的结果总是过度依赖未来时段的风光信息而后面的预测根本不准导致实际的SOC轨迹和计划严重偏离。滚动时域优化MPC的解法是每个控制周期比如每15分钟根据最新的预测数据重新求解未来24小时的调度计划但只执行第一个时段的决策到下一个时段再滚一次。这样做的好处是计划始终基于最新信息执行则始终保守稳健。代价也很明显计算量大增、预测模型需要频繁滚动调用。后面代码部分我会给出一个高效的实现方式。3. 数据和预测一套可复现的调度实验长什么样调度代码写得再漂亮没有靠谱的数据也是空中楼阁。做这行的人都懂一句话垃圾进垃圾出。数据质量和预测精度直接决定调度策略的有效性。这一节我详细讲数据从哪来、怎么清洗、预测怎么做以及预测误差最后如何传导到调度决策上。3.1 数据来源与预处理我用的实验数据分两类一类是公开的风光实测出力数据我有参考过NRW等开放数据集以及某些气象站再分析数据另一类是模拟数据——基于典型气象年的风速、辐照度曲线加随机扰动生成。模拟数据的好处是可控性好方便做对照实验但它脱离了真实现场参数标定要做仔细。数据预处理的坑特别多辐照度转光伏出力要区分直流侧和交流侧功率逆变器效率、组件温度系数、MPPT跟踪精度都会影响最终出力曲线简单用线性系数转换的结果只能作为参考风速转风电出力风速-功率曲线不是一根直线有切入风速、额定风速、切出风速三个拐点风速在切出风速附近时出力会直接跳变到零这个非线性特性要在预处理时就处理好缺失值处理夜间光伏记录为0是正常的但通信中断导致的“0”是异常值两种“零”不能混为一谈。我实际用过的清洗逻辑是结合相邻时点的功率变化率和同一时刻前几天的出力水平做判断变化率超过阈值且持续一段时间时按线性插值或相似日替换处理。3.2 基线预测与误差分析调度实验里预测模块我用过两种基线持久性预测和简单回归模型。持久性预测的逻辑是“下一时刻等于当前时刻”在超短期1小时以内预测中效果意外地好因为它充分利用了气象过程的连续性尤其是风速小时级内的惯性很强。中长期的预测则需要引入数值天气预报数据或时序模型如ARIMA、LSTM等考虑到公众号篇幅这里不展开预测算法本身只说一个关键点预测误差的分布和相关性比预测精度本身更重要。为什么因为调度优化对这个误差的敏感度是非线性的风光预测都偏小时系统会过度依赖新能源导致储能被提前榨干都偏大时弃电率又会飙升。我在代码里加入了一个“误差情景生成”模块通过历史预测残差的协方差矩阵生成多个可能的情景再在优化模型里做鲁棒校正。这个思路类似于随机规划的雏形但实现成本低很多。3.3 预测误差如何传导到调度决策举个我实验中的具体案例某天下午2点的光伏预测值为60MW实际出力只有40MW预测绝对误差20MW。如果调度模型没有预留响应能力系统会按60MW出力去做计划此时储能已经按计划开始放电负荷侧也已经安排好了。实际出力一降功率平衡瞬间被打破要么紧急切负荷要么从电网高价购电。这个传导路径说明预测模块的误差最后都会变成调度执行的“后悔值”——也就是实际成本超过计划成本的部分。为了抑制这种偏差我在约束中加入了一个“备用容量”条件每个时点必须保留一定的功率调节空间电池的一部分充放电能力时刻处于待命状态这部分容量不参与经济效益优化纯粹作为安全垫。虽然会牺牲少量经济性但对系统可靠性提升非常大。工程调度首先求稳其次求好。4. Python代码实现从数据到调度策略的完整路径这一节是全文重点。代码层面的框架设计思路直接决定实验迭代的效率和研究结论的可信度。我采用的Python技术栈是pandas做时序数据管理scipy做插值PuLP/CBC做混合整数线性规划求解matplotlib做可视化。整套代码没有采用重型商用求解器原因是课题组预算有限且CBC对中小规模算例已经足够快。4.1 数据结构设计数据层的核心是一个以时间为索引的DataFrame列包含预测风速/辐照度、预测风光出力、负荷需求、分时电价等。储能状态单独用一个类封装成员变量包括SOC、上水库水量、循环次数等状态量。调度结果则统一存成一个字典列表每个元素是一个时段的所有决策变量实际值。这样设计的好处是数据输入、优化求解、结果解析三个环节可以完全解耦后续换预测模型或换求解器时不需要改动数据结构。import pandas as pd import numpy as np from datetime import timedelta # 生成15分钟间隔的时间索引长度96*7 一周 time_idx pd.date_range(start2024-01-01, freq15min, periods96 * 7) # 模拟风光出力风速用Weibull分布辐照度按日正弦曲线加云层扰动 np.random.seed(42) wind_speed np.random.weibull(2.2, sizelen(time_idx)) * 8.0 solar_irr np.maximum(0, np.sin(2 * np.pi * (time_idx.hour time_idx.minute / 60) / 24 - 0.5)) solar_irr solar_irr * (solar_irr 0) np.random.uniform(0, 0.05, len(time_idx)) # 简化的风机功率曲线忽略变桨细节 def wind_power(wind_speed, rated2.0, v_in3, v_r12, v_out25): p np.zeros_like(wind_speed) mask_work (wind_speed v_in) (wind_speed v_r) mask_rated (wind_speed v_r) (wind_speed v_out) p[mask_work] rated * (wind_speed[mask_work] - v_in) / (v_r - v_in) p[mask_rated] rated return p # 光伏出力假设与辐照度成正比且受逆变器效率0.94限制 pv_capacity 1.5 # MW wind_capacity 2.0 # MW pv_power pv_capacity * solar_irr * 0.94 wind_power_out wind_power(wind_speed, ratedwind_capacity) df pd.DataFrame({ wind_pred: wind_power_out, pv_pred: pv_power, load: np.interp(np.linspace(0, len(time_idx), len(time_idx)), [0, 0.25, 0.5, 0.75, 1.0], [[80, 90, 110, 70, 60]] * 1) [:, 0] # 简化示例 }, indextime_idx)上面的代码是一个极简的模拟数据生成器实际实验中需要在风速序列中加入时空相关性并让负荷曲线符合“早晚高峰、午间低谷”的真实形状。模拟数据虽然没有直接用实测数据那么有说服力但胜在可复现不信的朋友可以直接把随机种子换成自己的实际测量数据。4.2 用PuLP建立MILP调度模型建模选用PuLP是因为它免费、语法直观、和CBC求解器组合的开源方案对中小问题很友好。调度模型本身是一个混合整数线性规划MILP整数变量主要来自抽蓄机组的状态切换。如果全部用连续变量近似求解会快很多但抽蓄的“最小稳定出力”约束就必须丢掉做出来的计划在实际矿井电站里根本执行不了所以我保留了整数变量。核心建模代码如下关键片段import pulp def build_schedule_model(df, battery, phs, price): periods list(range(len(df))) model pulp.LpProblem(Wind_PV_Storage_Cooperation, pulp.LpMinimize) # 决策变量 p_pv {t: df[pv_pred].iloc[t] - curtail_pv[t] for t in periods} # 这里展开写会很多实际用 pulp.LpVariable # 实际建模时每个时段需要这些变量curtail_pv, curtail_wind, p_charge_batt, # p_discharge_batt, p_pump, p_generate, p_buy, p_sell, p_loss, soc, water_level # 目标函数简化版 cost_terms [] for t in periods: cost_terms.append(price[penalty_curtail] * (curtail_wind[t] curtail_pv[t])) cost_terms.append(price[buy][t] * p_buy[t] / 1000) # 从电网购电成本 cost_terms.append(price[sell][t] * p_sell[t] / 1000) # 售电收益设为负 cost_terms.append(price[penalty_loss] * p_loss[t]) model pulp.lpSum(cost_terms) # 约束功率平衡 for t in periods: model ( df[wind_pred].iloc[t] - curtail_wind[t] df[pv_pred].iloc[t] - curtail_pv[t] p_discharge_batt[t] p_generate[t] p_buy[t] df[load].iloc[t] p_charge_batt[t] p_pump[t] p_sell[t] p_loss[t], fpower_balance_{t} ) # ... 其余约束SOC递推、水量平衡、机组状态互斥等 return model这段代码省去了所有变量的详细声明是为了不把篇幅拖得过长。真实实验里变量声明和约束构建大概两百多行全部写完会让文章变味我这里只展示骨架。一个关键的经验是PuLP变量命名不要用太长的字符串CBC求解器在输出日志时会把所有变量名打出来命名过长非常影响调试效率。4.3 滚动优化的主循环滚动优化的麻烦在于每个15分钟周期内都要用最新预测重新建一次模型、解一次模型、取第一个时段的决策、然后丢弃其他时段的决策。听起来简单但如果不注意性能一天96个周期跑下来可能比传统静态优化慢几十倍。我的做法是把“重新建模”的对象约束在滚动窗口内的时段索引上每次只新建模型对象因为旧模型的所有约束都基于旧数据但数据和约束构建采用向量化预计算避免在循环体内逐时点调用pandas接口。对于96×7天的算例CBC能在几秒内完成整个滚动过程表现完全可接受。def rolling_horizon_sim(df, battery, phs, horizon24 * 4): # 24小时 96个点? 实际horizon指步数 exec_history [] t_start 0 total_periods len(df) while t_start total_periods: # 获取预测窗口数据这里简化假设真实数据和预测一致 window_idx slice(t_start, min(t_start horizon, total_periods)) window_data df.iloc[window_idx].copy() model build_schedule_model(window_data, battery, phs, price_profile) solver pulp.PULP_CBC_CMD(msg0, timeLimit5) model.solve(solver) if pulp.LpStatus[model.status] Optimal: # 只保存第一个时段的决策 exec_history.append(get_first_step_decision(model)) t_start 1滚动窗长的选择值得单独说一句窗长过短模型看不到夜间的风电出力会把白天的光伏全拿来发电导致储能空置窗长过长计算负担大且远期预测误差大。实验下来24小时窗口对日尺度互补调度是一个比较折中的选择。读者如果处理跨天能量搬移问题比如周末低谷充电、周一高峰放电可以把窗长加大到48小时但对应预测数据要从数值天气预报获取误差会明显增大需要权衡。4.4 电池衰减惩罚项的实现细节电池衰减建模在代码里很容易被做成“简单的线性惩罚”但真实电池衰减是非线性的SOC接近满充或空放时循环寿命折损显著增大高倍率充放电也会加速老化。线性化处理在调度模型里是可以接受的近似但惩罚系数怎么取需要标定。我用的方法是对每一次充放电决策计算电池的“等效循环损耗”公式简化为[ C_{loss,t} \alpha_1 \cdot (SOC_t - 0.5)^2 \alpha_2 \cdot (P_{rate,t})^2 ]其中 (P_{rate,t}) 是充放电功率与额定功率的比值。这个二次项在MILP里不好直接展开我用分段线性函数近似它在SOC或者功率区间内打几个折点每个折点对应一个线性段效果接近二次惩罚但依然保持线性约束结构。这样处理之后模型不会再出现“为了弃10度电、让电池充5度电再放4度电白白消耗0.3%循环寿命”这种经济账算不过来的操作。5. 实验结果互补调度到底带来了多少收益代码跑通之后最关键的环节是用实验回答一个问题和“各自为政”的简单策略相比互补调度到底带来了哪些量化的收益。这一节我分享一组有代表性的实验场景以及结果的意义解读。5.1 场景设置与对比基准我设计了三组算例进行对比算例说明预期验证点Case 1纯风电光伏无储能作为基准下界看清弃电和缺电的严重程度Case 2风电光伏锂电池储能验证电池在小时级削峰填谷的作用Case 3风电光伏锂电池抽水蓄能验证“电池兜底抽蓄搬移”双储能的互补价值三组算例使用同一组模拟风、光、负荷数据每周96×7个时点同一套分时电价峰、谷、平三段保证比较公平。负荷曲线采用典型的工商业日负载曲线早晚高峰明显夜间低谷明显。储能初始SOC都设为50%抽蓄上水库初始水量设为50%库容。5.2 结果解读消纳率、购电成本和储能利用效率实验结果显示三组算例的差异非常明显Case 1中中午光伏大发但负荷较低系统弃电率超过25%夜间风电高峰时段又出现大量弃风同时晚高峰负荷尖峰时段还需要从电网大量购电日购电成本高企Case 2加入电池后弃电率降到约12%购电成本下降明显但电池SOC频繁在20%-90%区间波动按等效循环折旧估算一年折损的电池寿命成本相当可观Case 3在电池之外叠加了抽蓄调度模型会把日尺度的大电量搬移任务交给抽水蓄能电池只负责功率快速响应和短时平衡弃电率进一步降到约4%电池的等效循环次数比Case 2下降了近40%综合成本最低。从这个对比能看出互补调度的核心价值不是单纯“多装储能”而是“让不同储能干各自最擅长的事”。电池响应快、效率高适合削峰填谷和调频抽蓄容量大、寿命长但响应慢适合跨时段的能量搬移和大规模消纳。两者组合起来才能在系统层面实现“效率、成本、可靠性”的三角平衡。5.3 关键指标的计算方法实验做完不能只给一个定性结论指标定义要能复现弃电率( \text{弃电率} \frac{\sum_t (弃风电量 弃光电量)}{\sum_t (理论风光发电量)} \times 100% )分母用的是不考虑弃电时的满发发电量自消纳率本地风光发电被负荷直接消纳的比例等于 ( \frac{\sum_t \min(风光出力, 剩余负荷)}{ \sum_t 风光出力 } )储能利用率储能的放电电量占其额定容量小时数的比例反映储能设备被调用的充分度综合成本购电成本 弃电惩罚 储能衰减折算 失负荷惩罚四者加总后除以总用电量得到单位用电成本。这些指标在代码里实现起来很简单但很多研究者只盯住“弃电率”一个指标这是不够的。弃电率降下来了如果电池衰减成本、购电成本上去了整个方案依然不经济。调度的本质是多目标权衡任何单一指标都会偏。6. 实战中容易踩的坑求解时间、数据陷阱与模型病态这一节是经验沉淀把我在写代码和跑实验过程中反复踩过的坑梳理一遍希望对读者有直接帮助。6.1 求解时间失控从3秒到3分钟的排查链路最初版本的全时段96点静态优化CBC求解只需要3秒但切到24小时滚动整数抽蓄约束之后单次求解时间直接飙到3分钟。问题出在抽蓄机组的状态约束写得太松——约束矩阵里多了大量变量之间的互斥约束可行域变成高维空间里的稀薄集合CBC的整数分支定界搜索路径变得极其低效。排查思路是逐层缩减约束先用LP松弛看求解时间确认线性部分没问题再切片到6小时窗口的小规模子问题逐个添加整数约束找到是哪个约束触发了求解爆炸。最终发现是“抽水、发电、停机三状态全选”的互斥约束写法用了三组二元变量给CBC制造了太多对称性。改用1-of-N约束把三组二元变量合并成一个整数变量取值0/1/2求解时间立刻降到2秒以内。6.2 数据陷阱局部夜里光伏出力为负做数据清洗时遇到过很离谱的情况夜间辐照度传感器输出有微小负值代入光伏出力计算后出现了“负出力”也就是光伏板反向消耗电力这显然是数据错误。我最初的清洗逻辑只是把负值强制归零但后来发现还会导致储能SOC递推出现轻微漂移。最终的处理是在生成DataFrame之前就统一做了物理合理性过滤光伏出力不允许为负风速不允许超过历史极值变率负荷数据不允许跳变超过设定阈值。6.3 模型病态系数数量级相差悬殊导致求解精度问题目标函数里购电单价大约是0.6到1.0元/度而失负荷惩罚系数我是按500元/度设置的两者差了三个数量级。CBC求解时数值尺度相差过大的系数会让预处理的容差判断失真可能出现“明明不该切负荷却为了极小成本差切了负荷”的反直觉结果。解决方法是做归一化把目标函数的单位统一折算为“万元/兆瓦时”人为保证各项系数的数量级差距在100倍以内。同时启用CBC的ratio参数做更保守的数值容差设定。这个问题在论文复现中常被忽视但实际工程中必须处理。6.4 模型验证的坑SOC轨迹“悄悄超限”建完模型后我用模拟数据验证发现电池SOC偶发超限比如计划SOC是95%实际记录却跑到98%。排查半天发现不是约束失效而是滚动模拟的“执行”环节代码只记录了第一个时段的决策值之后又用递推公式手动计算SOC两个模块用的充放电效率定义不一致——优化模型内部用的是“充电功率x效率入储能功率”而后处理模块用的是“入储能功率/效率充电功率”一来一回就差了接近3个百分点。最后统一了效率定义方向并加了单元测试保证了优化变量和结果后处理的一致性。这种错误很难用肉眼发现但会在长时间仿真后积累成明显的能量不平衡现象调试时可以从“能量总账”入手快速定位。7. 扩展方向从研究实验走向真正的工程应用代码跑通、实验做完课题算是有阶段性成果了。但距离工程落地还有很长一段路。这里分享几个我觉得最有价值的扩展方向也算是对后来者的一个提示。7.1 从确定性优化走向随机优化目前的滚动时域模型里预测误差主要通过安全备用容量来被动防御本质上是“对抗误差”的思路。更进一步的做法是随机规划为每个时点生成若干个风光出力情景在目标函数中直接对情景求期望让调度计划本身对不确定量具有鲁棒性。情景数的增加会让模型规模成倍增长但现代求解器加上分解算法比如Benders分解完全可以处理。7.2 把市场化交易纳入调度框架我现在的实验还是“固定分时电价”直接把电价作为输入参数。但实际上如果系统参与电力现货市场储能会在电价低谷充电、高峰放电目标函数里的购售电价会变成动态变化的决策相关量。更进一步可以做成“电价响应型”调度储能策略会反过来影响市场出清价格这就从“价格接受者”变成了“价格影响者”建模框架要重写。7.3 废弃矿井抽蓄的参数化建模废弃矿井改造项目的最大特点是“场地条件千差万别”不同矿井的巷道长度、高差、硐室容量都不一样。如果把矿井物理参数也纳入建模做一套“给定矿井参数自动生成调度模型”的代码框架这个课题的可迁移性会大幅提升。我目前的数据是用一个典型参数模板生成的后续如果有实际项目的实测数据模型标定和验证的力度会更有说服力。这一块还在继续完善中后面如果有新的进展会再整理出来分享。