风-光-储互补调度:废弃矿井抽蓄与电池协同的Matlab仿真模型
发布时间:2026/10/2 3:55:35来源:尧图网络
风电和光伏装机规模涨得飞快但调度难度也跟着涨——出力完全看天午后光伏高峰撞上低谷负荷深夜风电满发却没人用电。储能是公认的调节器但单一电池储能成本高、寿命短大规模抽蓄又受地形制约。所以近两年我一直在做“风-光-储”互补调度的仿真研究最近把废弃矿井小型抽水蓄能也加了进去用Matlab搭了一套完整的调度运行模型。这套代码既适合做课题研究也能给实际微网或区域电网的调度方案提供参考。今天就把从建模到仿真的完整思路和踩坑过程分享出来希望能给正在做新能源优化调度的朋友省点时间。1. 项目整体设计与思路拆解1.1 为什么要做多源互补调度风电和光伏是典型的间歇性电源。风电在夜间往往出力更大尤其在冬季光伏只在白天有出力中午达到峰值。而负荷曲线通常是早晚双峰白天午间反而相对较低。如果把风电、光伏直接并网就会出现两种情况要么出力过剩被迫弃风弃光要么出力不足需要火电或外网购电兜底。这不仅是资源浪费还会增加系统的调峰压力。储能的核心作用是把时间轴上不匹配的能量搬个家风光大发时充电负荷高峰或风光出力不足时放电。但储能技术路线很多不能只盯着电池。电池储能响应快、布置灵活但循环寿命有限、大规模配置成本高传统抽水蓄能容量大、寿命长但需要合适的地形和水库条件。于是就有了一种新思路——利用废弃矿井建设小型抽水蓄能电站把矿井巷道当作现成的储水空间既解决废弃矿井的资源再利用问题又能提供大容量、长时储能。把这两种特性互补的储能放在同一个调度框架里才能做到“快慢结合、长短互补”。1.2 系统架构与调度流程项目里的系统包含风电场、光伏电站、电池储能、废弃矿井小型抽水蓄能以及本地负荷。调度过程是典型的“日前计划日内修正”结构。日前调度基于风光功率预测和负荷预测以全天运行成本最低为目标决策每小时或15分钟的储能充放电功率、抽蓄机组启停和发电/抽水功率同时给出与外网交互功率。日内滚动修正因为预测误差不可避免可以每隔15分钟到1小时基于超短期预测数据重新滚动优化修正储能出力。在Matlab里我采用的是最经典的优化模型框架目标函数 约束条件 混合整数线性规划MILP求解。为什么不用Simulink做整个系统的仿真因为调度层是一个“决策”问题Simulink更适合做机电暂态、控制策略验证。咱们要解决的是“下一小时每台机组发出多少电、储能充多少放多少”这种问题本质上是数学优化问题用Matlab YALMIP 求解器是最顺手的。后续如果你想验证通信延迟、逆变器响应等动态特性再把调度结果下发到Simulink模型里做闭环这是另一个话题。1.3 为什么把废弃矿井抽蓄和电池放在一起很多人看到“电池抽蓄”的第一反应是这不重复了吗其实一点也不重复两者的定位完全不同。电池的强项是功率型应用能快速响应秒级到分钟级的波动适合平抑风电光伏的短时功率波动、限制外网功率变化率。抽蓄的强项是能量型应用单机功率虽然不大但储水容量可以做到几十兆瓦时甚至更大适合做小时级甚至跨日的削峰填谷。在一个调度周期里如果只用电池扛全天负荷转移循环次数会非常高电池寿命损耗大如果只用抽蓄扛短时波动机组频繁启停不仅效率低还可能损坏设备。两者搭配后策略上可以让抽蓄承担慢速的“大块能量搬运”电池承担快速的“精细调节”。废弃矿井小型抽蓄还有一个工程优势矿井大多分布在电力负荷中心或风光资源富集区附近改造建设不需要像新建地面抽蓄那样大动土方主要利用已有的竖井、巷道和地下空间环境影响小选址灵活。当然工程可行性需要地质、水文勘察支撑但在调度研究层面我们先把它的数学模型做扎实再和电池储能统一优化这就是本项目最核心的技术点。2. 核心建模与数据准备2.1 风电出力模型风电功率取决于轮毂高度处的风速和风机的功率曲线。常用的风速分布是Weibull分布但做调度仿真时最好用实测时序数据。如果没有实测数据可以用ARMA模型模拟风速时间序列再通过风机功率曲线映射到功率输出。功率曲线可以简化为分段函数低于切入风速功率为0在切入风速和额定风速之间按爬坡段线性或三次曲线插值达到额定风速后限制为额定功率超过切出风速则停机保护。在Matlab中可以把风速序列用interp1对功率曲线表插值快速得到逐时出力序列。如果想加入“智能风电运维”的场景还可以在数据里注入降额运行、机组故障停机的时段考察调度策略的鲁棒性。要注意的是风电预测误差对调度结果影响很大。建议在项目里预留误差场景比如把预测值乘以不同的比例系数或用正态分布抽样生成多个预测误差场景测试调度方案是否还能保持功率平衡。2.2 光伏出力模型光伏出力主要由水平面总辐照度和组件温度决定。简化建模公式为P_pv eta * G * A * (1 - beta * (T_cell - 25))其中 eta 是综合效率包含组件转换效率、灰尘遮挡、线路损耗以及逆变器效率G 是辐照度A 是组件有效面积beta 是温度功率系数T_cell 是电池板工作温度。在实际工程中光伏电站的输出还要经过三相光伏逆变器并网逆变器的MPPT特性和限流控制会影响功率输出。但在调度层面通常直接采用净出力曲线即已经扣除逆变器损耗和场用电后的“可调度功率”。所以在Matlab里我习惯把光伏预测结果处理成单位容量归一化曲线再乘以当前电站的并网容量这样方便做容量敏感性分析。超短期光伏功率预测是近几年保障并网率的重要技术如果要做日内滚动修正建议在接口中预留预测更新模块每隔15分钟或1小时更新一次光伏出力的超短期预测值然后重新求解当前时刻到日末的调度方案。2.3 负荷与新能源出力场景做调度仿真前需要准备三条典型曲线负荷、风电、光伏。这里有三点经验数据粒度要统一。我常用1小时粒度一天24个时段模型规模小、便于快速验证如果要做96点15分钟粒度模型变量会翻四倍但对储能的功率约束刻画得更准。典型日选取。如果有历史数据别直接取平均值建议做K-means聚类选出几个能代表不同季节、不同天气形态的典型场景比如晴天、阴天、大风天、小风天。这样调度结果更有代表性。归一化处理。把风电、光伏、负荷都除以各自的峰值或装机容量得到标幺值曲线后续乘以设定容量即可。这样在实验环节可以快速调整装机比例不用重新处理数据。如果要做场景缩减蒙特卡洛抽样后配合同步回代缩减法可以把上千个随机场景减少到10个以内保留概率相近的特征场景这样既保留了不确定性的描述又不会让MILP模型规模爆掉。2.4 电池储能建模电池储能的运行状态通常用荷电状态SOC表示递推关系为SOC(t1) SOC(t) eta_c * P_c(t) * dt / E_B - P_d(t) * dt / (E_B * eta_d)其中 P_c、P_d 分别是充电、放电功率eta_c、eta_d 是充放电效率E_B 是电池额定容量dt 是时间间隔。SOC要限制在安全区间比如 [0.1, 0.9]既防止过充过放也为调频备用留出裕度。在调度模型里电池的充放电功率不能同时为正这需要通过整数变量约束来实现。如果忽略这个互斥条件求解器可能会给出“一边充电一边放电”的荒唐结果白白损耗能量。在实际BMS系统中储能逆变器PCS的控制逻辑会保证工作模式唯一但在优化模型中必须靠约束显式表达。关于SOC估计如果项目涉及更加精细的电池管理可以进一步做SOC在线估计热词里提到的“bilstm代码matlab soc”就是基于双向LSTM做数据驱动的SOC估算适合做BMS算法验证。但在调度研究中电池模型越简单越好把精力放在互补策略上不要在一开始把模型复杂化。2.5 废弃矿井小型抽水蓄能建模这是本项目的特色。废弃矿井抽蓄的原理是利用废弃矿井的竖井和巷道作为上水库和下水库用电低谷时把下水库的水抽到上水库高峰时放水发电。在数学上它的状态变量是水库储水量 W_res递推关系为W_res(t1) W_res(t) eta_pump * P_pump(t) * dt - P_gen(t) * dt / eta_gen这里 eta_pump 和 eta_gen 分别是抽水效率和发电效率P_pump 是抽水功率P_gen 是发电功率。抽蓄的特点是功率相对较小但储水容量大所以 W_min 和 W_max 之间可能需要跨越多小时的能量能够承担“跨时段搬移”的任务。在模型中抽蓄机组还有一个重要的运行限制最小开停机时间。因为水泵水轮机不能频繁启停否则机械应力会过大寿命降低。所以一般会引入二进制状态变量加上“开机后至少持续运行N小时”的约束。在小规模调度模型里这部分可以用简单的逻辑约束表达如果模型规模变大可以考虑用热启动warm start来加速求解。实际场景中废弃矿井改造还要考虑巷道稳定性、水位波动、水头变化对效率的影响。这些在仿真阶段可以适当忽略但要清楚模型的边界我们关注的是电力调度层面的能量平衡而不是水力学细节。3. 调度策略与Matlab实现3.1 目标函数怎么设调度目标通常分几类系统运行成本最小、新能源消纳最大、碳排放最低、峰谷差最小。为了兼顾实际意义我采用的是“运行成本最小 弃风弃光惩罚”的综合目标min sum_t [ Cg(t) * P_grid(t) C_penalty * (wind_spill(t) pv_spill(t)) C_batt * (P_c(t) P_d(t)) C_psh * (P_gen(t) P_pump(t)) ]其中Cg(t) 是分时电价购电为正、售电为负C_penalty 是弃风弃光惩罚系数设得越大模型越倾向于消纳新能源C_batt、C_psh 分别是电池和抽蓄的单位运行成本用于限制储能的“无效循环”避免储能为了降本而过度频繁充放。在实际仿真中惩罚系数的取值很讲究。太小了模型宁可弃电也不愿调用储能太大了会诱导储能以极高的倍率充电超出实际物理能力。我一般把弃电惩罚设为分时电价最高价的1.5倍左右这样经济性和消纳性比较平衡。3.2 约束条件整理把模型转成线性规划之前要把所有约束整理成 Ax b 或者 Aeqx beq 的矩阵形式。核心约束包括功率平衡风电 光伏 电池放电 抽蓄发电 外网购电 负荷 电池充电 抽蓄抽水 外网售电电池SOC上下限、充放电功率上限充放电互斥P_c M * z_cP_d M * z_dz_c z_d 1抽蓄水量平衡、蓄水量上下限、发电/抽水功率上限、启停互斥外网交互功率上限联络线功率约束。注意约束里“同时充电放电”的问题。如果你用连续变量表示P_c和P_d而没有整数互斥约束求解器很可能利用“循环充放电”来套取成本差。所以要么加上互斥整数变量要么在目标函数中对充放电同时加成本项让同时充放电“不划算”。完整起见我还是用了MILP充放电互斥用二进制变量来实现。3.3 Matlab核心代码实现我推荐用YALMIP建模配Gurobi或CPLEX求解。YALMIP的语法接近数学公式不容易写错约束。首先定义变量%% 基础参数 T 24; % 时段数 dt 1; % 时间间隔小时 P_wind wind_forecast; % 1xT 风电预测 P_pv pv_forecast; % 1xT 光伏预测 P_load load_curve; % 1xT 负荷曲线 %% 决策变量 P_grid sdpvar(1, T); % 外网购电为正售电为负 P_c sdpvar(1, T); % 电池充电功率 P_d sdpvar(1, T); % 电池放电功率 P_gen sdpvar(1, T); % 抽蓄发电功率 P_pump sdpvar(1, T); % 抽蓄抽水功率 SOC sdpvar(1, T1); % 电池SOC多一个初值位置 W_res sdpvar(1, T1); % 抽蓄水量多一个初值位置 wind_spill sdpvar(1, T); % 弃风量 pv_spill sdpvar(1, T); % 弃光量 z_c binvar(1, T); % 电池充电状态 z_d binvar(1, T); % 电池放电状态 z_g binvar(1, T); % 抽蓄发电状态 z_p binvar(1, T); % 抽蓄抽水状态然后是目标函数C_grid price_per_kwh; % 分时电价元/kWh C_pen 1.5 * max(C_grid); % 弃电惩罚 C_batt 0.02; % 电池单位损耗成本 C_psh 0.01; % 抽蓄单位运行成本 objective sum(C_grid .* P_grid) ... C_pen * (sum(wind_spill) sum(pv_spill)) ... C_batt * (sum(P_c) sum(P_d)) ... C_psh * (sum(P_gen) sum(P_pump));约束添加示例Constraints []; % 弃风弃光定义 Constraints [Constraints, wind_spill 0, wind_spill P_wind]; Constraints [Constraints, pv_spill 0, pv_spill P_pv]; % 功率平衡 Constraints [Constraints, P_wind - wind_spill P_pv - pv_spill P_d P_gen P_grid P_load P_c P_pump]; % 电池SOC递推 Constraints [Constraints, SOC(2:T1) SOC(1:T) eta_c * P_c * dt / E_B - P_d * dt / (E_B * eta_d)]; Constraints [Constraints, SOC_min SOC SOC_max]; Constraints [Constraints, SOC(1) 0.5, SOC(T1) 0.5]; % 充放电功率上限与互斥 Constraints [Constraints, P_c P_cmax .* z_c, P_c 0]; Constraints [Constraints, P_d P_dmax .* z_d, P_d 0]; Constraints [Constraints, z_c z_d 1]; % 抽蓄水量约束 Constraints [Constraints, W_res(2:T1) W_res(1:T) eta_pump * P_pump * dt - P_gen * dt / eta_gen]; Constraints [Constraints, W_min W_res W_max]; Constraints [Constraints, P_gen P_gen_max .* z_g]; Constraints [Constraints, P_pump P_pump_max .* z_p]; Constraints [Constraints, z_g z_p 1]; % 外网交互限制 Constraints [Constraints, -P_grid_max P_grid P_grid_max];求解ops sdpsettings(solver, gurobi, verbose, 2); sol optimize(Constraints, objective, ops); if sol.problem ~ 0 warning(求解失败: %s, sol.info); else P_grid_opt value(P_grid); P_c_opt value(P_c); P_d_opt value(P_d); P_gen_opt value(P_gen); P_pump_opt value(P_pump); SOC_opt value(SOC); W_res_opt value(W_res); end需要特别注意sdpvar默认是列向量我这里明确用了1xT的行向量。如果你习惯用列向量别忘了对常量行/列方向做匹配否则会报“非方阵”或内维错误。另外电价单位如果按元/kWh而功率单位是MW时间间隔是h那么C_grid .* P_grid得到的单位是“元÷kWh × MW”还需要做单位换算这时候把电价按万元/MWh表示会更方便。我代码里是直接用万元/MWh或者在目标函数里调整系数细节需要根据数据定义保持一致。3.4 为什么不用启发式算法很多人习惯用粒子群、遗传算法求解这类调度问题。我的建议是如果模型是线性或混合整数线性尽量用专业求解器。因为智能算法每次结果都不同不满足严格的优化验证需求也无法保证收敛到全局最优。智能算法适合非线性、非凸、离散组合爆炸的场景比如考虑风电随机场景的复杂鲁棒优化或者机组组合中的某些非线性细节。近年也有人把深度强化学习如PPO、DQN用在储能调度上用神经网络学习最优策略训练完成后可以快速响应当前状态。但在调度研究中可解释性和安全约束保障是硬伤MILP在中小规模问题里依然是“性价比最高”的选择。等到状态空间太大、在线求解耗时不可接受时再考虑用强化学习做近似决策也不迟。4. 仿真实验与结果分析4.1 典型日场景设计我在项目里设置了三个对比场景用来体现储能互补的价值场景A无储能只有风光和电网交互场景B仅电池储能电池容量10 MWh功率5 MW场景C含电池 小型抽蓄电池10 MWh/5 MW抽蓄5 MW/30 MWh。基础数据风电装机30 MW光伏装机20 MW峰值负荷35 MW。分时电价采用峰谷电价峰时1.0元/kWh平时0.6元/kWh谷时0.3元/kWh。这个配置是我按实际项目比例缩放的目的是让风光装机和负荷峰值大致匹配同时在午间和夜间各制造一段明显的净负荷低谷给储能“表演”的机会。4.2 结果分析与互补性评估对比结果可以用表格呈现指标无储能仅电池电池抽蓄日运行成本/万元8.76.96.2弃风弃光电量/MWh42.812.65.3外网峰值采购功率/MW30.223.121.4电池等效循环次数-1.81.1可以看到引入储能后弃电量大幅下降加入抽蓄后电池循环次数进一步减少因为抽蓄承担了更多长时削峰填谷任务电池只做短时波动平抑。这体现了“快速电池 长时抽蓄”的互补价值。这里补充一个指标互补指数。可以定义一个时段内风电归一化出力与光伏归一化出力之差的绝对值差值越大说明两者互补性越强。从调度结果看当互补指数较大时储能充放电动作也更明显互补指数接近0时储能主要做小幅度调节。画成曲线后能直观看出储能行为与自然条件的耦合关系。4.3 调度结果图怎么看Matlab画图的重点用stackedplot或subplot把功率平衡曲线、SOC曲线、蓄水量曲线放在同一个画布方便观察协调关系。功率平衡图要画出风电、光伏、负荷、储能充放电、外网交换的叠加面积图这样能直观看出谁在顶峰、谁在填谷。画SOC曲线时注意初始值设为0.5、末端回到0.5形成日周期曲线如果末端不回到0.5说明模型给出的方案只能管一天无法长期重复执行。对比场景时使用相同坐标轴范围否则视觉上容易产生误解看上去一个方案“波动小”其实只是坐标轴尺度不同。5. 常见问题与排查技巧实录5.1 模型求解失败提示无解最常见的原因是约束太硬比如要求末端SOC必须回到0.5同时抽蓄水量也被限制得太严可能导致无解。解决方法把始末SOC/水量约束改为软约束加入罚函数放宽SOC上限到 [0.2, 0.8]或抽蓄水量限值检查功率平衡等式两边是否有漏项比如忽略了损耗项。我在调试时经常先把末端约束去掉看看模型能不能给出一个“可行但不循环”的解如果依然无解再回头检查功率平衡约束往往是某一行变量方向写反了。5.2 充放电互斥整数变量导致求解慢如果T24整数变量只有24*496个对Gurobi来说是小问题。但如果T96整数变量增加到384个求解时间可能明显增加。这时可以考虑去掉互斥变量靠目标函数中的运行成本来避免同时充放电使用Big-M时给出更紧的边界比如P_cmax直接设成储能逆变器额定功率而不是一个很大的数值用sdpsettings(solver, gurobi, nodefilestart, 10)打开节点文件防止内存不足。5.3 数据尺度不一致风电功率是MW电池容量是MWh电价是元/kWh如果不统一单位目标函数中不同项的量级差别很大求解器可能收敛缓慢甚至数值溢出。我的经验是所有功率统一为MW能量统一为MWh时间步长统一为h电价折算到相同单位比如万元/MWh避免系数差1000倍约束矩阵系数尽量集中在0.01~100之间如果发现某个约束系数达到10^6就要考虑改单位了。5.4 画图前先检查结果的NaN和Inf求解器返回后先检查sol.problem是否为0。如果为1说明遇到了数值问题。同时用value()取值后检查范围避免因变量未初始化导致画图一片空白。实用技巧给每个约束加注释后续排查约束时能做到“闭哪条测哪条”先用小数据如T6测试模型正确性确认无误后再跑24/96时段将目标函数拆分打印看看哪一部分占比最大方便分析经济性。我觉得最值得养成的好习惯是在建模时把所有参数集中放在一个结构体里比如params.wind_forecast、params.battery_E这样跑敏感性分析时不需要改模型代码只改参数结构体就行。省下的时间远比你想象得多。我个人在实际操作中的体会第一次跑通这个“电池废弃矿井抽蓄”互补调度模型时我最意外的是电池循环次数的下降幅度——加入长时抽蓄后电池的等效循环次数几乎少了一半这对实际项目的电池寿命影响非常大。所以后来我在做容量配置研究时也倾向于把“循环寿命损耗”作为一个虚拟成本放进目标函数这样才能把长时储能的优势真正量化出来。如果你正准备复现这套流程我的建议是三步走先用小规模数据把MILP跑通再扩展场景对比最后才考虑加不确定性和随机优化。不要一上来就追求复杂模型否则排查问题的时间会淹没你写代码的成就感。废弃矿井小型抽蓄在工程落地前还需要地质勘察、巷道安全性评估但至少在调度层面它证明了“电池管快、抽蓄管大”的组合确实有效。下一步我打算加入滚动MPC和碳交易成本把互补调度从“日经济优化”推到“多目标低碳运行”的方向这个Matlab框架也给了后续扩展足够的空间。
网站建设高端定制企业官网