☰
虚拟电厂多时间尺度调度中的储能衰减建模与Matlab复现
2026/10/12 17:06:56 网站建设 项目流程

当风电光伏装机占比一路走高之后,电网调度里出现了一个此前没这么尖锐的矛盾:系统要足够的灵活性来应对出力波动,可灵活性资产里最顺手的储能,每动作一次都在消耗寿命。之前我接到一篇 SCI 论文的复现任务,标题正好把这两个问题放在一起:高比例可再生能源并网如何平衡灵活性与储能成本,用虚拟电厂做多时间尺度调度,再给储能加一个衰减建模。这个题目第一眼看不算复杂,真正把公式转成 Matlab 代码的时候,才体会到最值得做的是储能衰减和调度模型的耦合。

这篇帖子写给谁?打算做论文复现、拿代码对比算法、或者想搞明白虚拟电厂调度怎么落地的人。我会把模型设计逻辑、衰减建模的常见做法和代码改进点、Matlab 求解路径、以及我在复现时踩过的坑都过一遍。代码片段给的是能直接跑通思路的示意版本,参数则全部可调,方便你对着自己的算例改。

1. 为什么要把“灵活性与储能成本”绑在一起建模

1.1 高比例可再生能源并网后的“灵活性”到底是什么

先把“灵活性”这个词说透。电力系统运行本质上是实时平衡,发用两侧每一秒钟都要相等。传统火电时代,发电机组的出力曲线相对可控,调度员按负荷预测安排开机和出力就行。新能源比例上来之后,系统净负荷变成了“负荷减去风光出力”,这条曲线比原负荷曲线陡得多、抖得多:一片云飘过来,光伏可能几分钟内掉一半;傍晚风电爬坡和负荷晚高峰叠在一起,净负荷可以在几个小时内跳变几十兆瓦。

灵活性指的就是系统在不改变长期装机结构的前提下,在给定时间尺度内调整出力的能力。工程上通常拆成四个指标:上调爬坡、下调爬坡、上调备用、下调备用。举个例子,未来15分钟如果光伏突然降低5MW,那么系统里必须有某个资源能在15分钟内多发出5MW,这就是上调灵活性;反过来,如果风电大发导致消纳困难,系统要能在15分钟内少消纳或者多充电5MW,这就是下调灵活性。

储能之所以成为高比例可再生能源系统的核心灵活性资源,是因为它正反向都能调,响应速度以毫秒到分钟计,不依赖一次能源供应。但它有一个致命短板:容量有限,寿命有限。每充放一个循环,电池内部的化学活性物质就在缓慢减少,SOH(健康状态)会从100%往下走。如果我们只把灵活性当作“能不能爬坡”来建模,那储能怎么用都是最优的;一旦把寿命损耗换算成钱,储能就不是怎么用都划算了。灵活性越强、储能动作越猛,电池成本越高,这就是标题里说的“平衡”。

1.2 储能衰减:最容易被调度模型“做假账”的成本

我自己在做复现时发现,很多论文的储能模型非常简单:SOC有上下限,充放电功率有约束,效率给定,完事。这种模型忽略了两个关键事实:第一,储能充放电的深度和次数直接影响寿命;第二,电池可用容量会随着使用天数增加而逐年下降,不是恒定参数。

从电化学角度看,电池老化主要来自负极SEI膜持续生长、锂离子损失、电解液分解这几个过程,影响因素包括温度、充放电倍率、放电深度DOD和SOC工作窗口。工程上处理这些物理过程时不可能在优化模型里求解电化学偏微分方程,所以一般用两类近似:一类是等效循环寿命法,另一类是经验老化模型。

等效循环寿命法很直观。假设某个型号电池在100%放电深度下循环次数是3000次,在50%放电深度下是6000次,那么一次100%深度循环消耗的寿命资产就是1/3000,一次50%深度循环消耗1/6000。调度模型知道每次充放电的深度,就能把寿命损耗折算成成本。

经验老化模型则更接近实验曲线,常用形式是容量损失率与累积安时吞吐量的指数关系。实际复现中,这种模型可以直接查数据表,也可以拟合成分段线性函数,嵌入优化求解器。难点在于参数标定,不同厂家、不同材料体系的电池差异很大,论文一般会给出一个实验范围内的拟合结果,复现时需要把论文里的曲线重新读出来,或者先用公开的典型参数跑通框架,再替换成自己的实验数据。

如果调度目标里没有衰减成本,优化器一定会做出一种很“激进”的决策:风光大发时拼命充电,负荷高峰时拼命放电,因为电费差的套利收益是正的,而电池损耗是零成本。这种结果画出来曲线很好看,弃电率很低,但储能实际运行一两年就废了。说白了,不看衰减成本等于给储能做假账,把设备损耗转嫁给后续年份。

1.3 多时间尺度调度:把不同时间颗粒度的矛盾分层解决

为什么单靠一个日前优化不够?因为风光预测误差会随着预测时长的缩短而减小。提前24小时预测光伏,误差可能到20%以上;提前1小时预测,误差可能只有5%左右。如果只做一个日前调度,为了保证全天平衡,模型不得不预留大量备用容量,灵活性成本被高估;如果只做实时调度,没有前瞻性,储能的充放电策略会变得短视,无法为晚高峰提前腾出容量。多时间尺度调度就是把这层矛盾分层解决。

典型结构是三层:日前调度、日内滚动、实时控制。日前调度以1小时为间隔,基于未来24小时的风光负荷预测,确定机组启停、储能充放电计划、备用容量安排,时间颗粒粗,但能保证全天能量平衡和电量成本最优化。日内滚动调度通常每15分钟或5分钟执行一次,预测时域缩短到未来2到4小时,模型滚动刷新,用来跟踪日前计划并修正预测偏差。实时控制则在秒级到分钟级响应,处理实际功率与计划之间的偏差,通常由自动发电控制或者储能本地控制器完成。

储能衰减在三个尺度上都有体现。日前尺度关注全天循环次数和充放电深度,日内尺度关注每一次滚动窗口内的动作强度,实时尺度则涉及高频小幅充放是否要计入寿命损耗。我把衰减成本同时放进日前和日内目标函数之后,模型会自动在“多用储能换灵活性”和“少充放保寿命”之间权衡,而不是用一种拍脑袋的固定策略。

2. 虚拟电厂如何把分散资源装进一个优化框架

2.1 资源侧画像:风光储荷缺一不可

虚拟电厂和传统发电厂最大的区别,是它不依赖一个物理集中式的电站,而是把分布式光伏、风电、储能、柔性负荷、小型机组聚合起来,对外像一个电厂一样参与调度和交易。复现代码时,第一步就是把每类资源的数学模型定义清楚。

不可控电源方面,分布式光伏和风电是出力主体,它们的预测出力曲线作为输入数据,实际出力与预测的偏差在日内和实时阶段处理。可控电源方面,可以布置一台5MW左右的小型燃气机组作为可靠备用,特点是爬坡快、成本高,适合在极端场景下兜底;如果算例里没有,也可以设置成外购电接口。储能方面,容量、最大充放电功率、效率、SOC上下限、初始SOH这些参数都要单独配置。柔性负荷方面,可削减负荷和可转移负荷一起建模,比如空调、充电桩、一部分工业负荷,它们可以通过补偿成本调用,是灵活性资源的补充。

聚合方式在Matlab代码里通常体现为把所有设备的参数导入同一个结构体,比如params.ess.Ecap、params.pv.Pmax,这样便于后续统一建模。模型内部不需要区分谁是业主,只要保证节点功率平衡,并在目标函数里给不同资源设置不同的边际成本。

2.2 日前-日内-实时三层架构怎么搭

我复现时的做法是把三层调度拆成三个独立函数,数据流单向传递:日前结果传给日内,日内结果传给实时,这样调试起来很清楚。

| 阶段 | 时间间隔 | 预测时域 | 决策重点 | 执行频率 | | 日前 | 1h | 24h | 机组启停、储能全天计划、备用容量 | 每天一次 | | 日内 | 5-15min | 2-4h | 滚动修正、弃电调整、储能充放电 | 每15分钟 | | 实时 | 1-5min | 5-15min | 功率跟踪、调频响应 | 秒级-分钟级 |

日前模型的核心是能量平衡和备用约束,目标函数以运行成本为主。日内模型引入了滚动优化思想,每15分钟根据最新的风光实测和超短期预测,对日前计划做修正。这里有个容易被忽视的细节:日前模型给出的是“计划值”,日内模型不必照单全收,而是以跟踪偏差惩罚为软约束,让模型在成本和跟踪精度之间自行取舍。实时控制层则简单一些,用比例积分或者模型预测控制,把日内计划与实际功率的偏差降到最低。

三层架构的价值在于,误差在不同层级被逐级消化,而不是全部压给一次优化。日前算错的部分,日内还能救;日内没救回来的部分,实时再调。储能衰减成本在每一层都会被重复评估,所以我在日内模型里也加了衰减成本项,避免滚动窗口内的储能被频繁深度充放。

2.3 灵活性平衡如何量化并进入目标函数

灵活性不能只是一个概念,进入模型必须变成可计算的量。我在代码里把它拆成两类:一类是备用约束,一类是灵活性收益或惩罚。

备用约束的写法不复杂。假设需要上调备用,那么任意时刻t,可控机组剩余可增出力加上储能可放电功率之和,必须大于等于风光预测误差的某个置信区间。下调备用同理,要求可控机组剩余可减出力加上储能可充电功率之和,足够应对风光超发。这个约束写成Matlab代码就是一组线性不等式,Yalmip处理起来很轻松。

但只满足备用约束还不够,因为模型可能会选择“刚刚好”的备用容量,而实际上更高比例的灵活性在未来能带来更大收益。所以我还建议在目标函数中加入灵活性效益项。简单做法是给上调备用和下调备用赋予一个价值系数,让优化器权衡“多留一份灵活性”与“多花一份成本”孰轻孰重。更精细的做法是引入灵活性不足惩罚:如果某个时段预测误差超过了可用备用,系统需要被迫切负荷或者弃电,对应一个比较高的惩罚成本。这样一来,灵活性和成本就不再是并列的两个目标,而是统一在同一个经济目标里。

3. 储能衰减建模:复现时最考验功夫的部分

3.1 先搞清楚老化的物理来源和工程近似

我在1.2里简单提过,储能老化不是单一机制,而是多种电化学副反应叠加的结果。负极端SEI膜会持续增厚,消耗锂离子;高温下电解液分解加快;大倍率充电容易在负极表面析出锂金属;深度放电会让正极结构不稳定。实际调度模型不可能追踪这么多机理,所以工程上普遍把老化折算成寿命损耗。

最常用也最好理解的是循环次数-放电深度曲线。厂商标定的数据一般写成表格:100% DOD下N次循环,80% DOD下N2次循环,50% DOD下N3次循环。调度模型里,我们把一次充放电循环的寿命损耗定义为1/N(DOD)。如果某天储能完成了若干次不同深度的循环,总寿命损耗就是每个循环损耗的累加。

更准确一点的做法是Ah吞吐量经验模型。它认为容量损失率与累积安时吞吐量呈幂函数关系,同时受温度影响。这里不展开具体参数,因为不同论文给出的拟合值差异很大,复现时一定要以目标文献的曲线为准。如果文献给的是曲线图,可以用数据提取工具把点读出来,再拟合成分段线性函数,这样就能无缝嵌入优化问题。

3.2 把衰减成本写进优化目标:线性化是关键

在Matlab里用Yalmip和Gurobi求解时,最怕引入非线性项,因为这会让问题从MILP变成MINLP,求解时间暴涨。衰减成本天然含绝对值:储能净功率为正表示放电,为负表示充电,值的大小都造成寿命损耗。解决办法是把净功率拆成两个非负变量:充电功率Pch和放电功率Pdis,然后给它们加一个“不能同时为正”的约束,通常用两个二进制变量实现。

目标函数里的衰减项可以写成:

C_deg = lambda_deg * sum(Pch + Pdis) * dt;

lambda_deg是边际老化成本系数,单位是元/kWh,含义是每吞吐一度电平均消耗多少电池寿命对应的经济损失。它的数值可以从循环寿命曲线反推:电池系统每kWh容量成本除以总可吞吐电量,再乘以效率修正系数。比如电池容量成本为1500元/kWh,总可吞吐电量按5000次*1kWh计,那么平均每吞吐1kWh的老化成本大约是0.3元。这个值只是示意,不同参数差很多,但要的就是这个量级。

如果嫌弃单一常系数不够精细,可以按SOC区间分段标定老化系数。低SOC区间和大DOD条件下的老化成本更高,高SOC浅充浅放更低。将SOC分成若干段,每一段对应一个线性老化项,整体仍然是线性模型。我在复现中试过,分层段数越多,结果越接近半经验模型,但增加段数会引入更多整数变量,求解时间变长。一般三段就够了。

3.3 SOH闭环:不是算完一天就结束

这是我复现中最想强调的一点。仅仅把衰减成本加进目标函数,还只是一个“开环”的调度:储能容量当成常数用,算出来的SOH下降也只是事后统计,不会反馈到后续调度里。实际物理过程是,随着容量衰减,储能的可用充放电功率和能量都下降,如果第二年还按第一年的容量做调度,计划必然失真。

所以我在代码里加了一个SOH闭环循环。外层按天迭代,每天的调度完成后,根据当天储能实际充放电的深度和次数计算寿命损耗,更新SOH,再将新的容量和功率限值代入第二天的调度模型。

for day = 1:365 params.ess.Ecap = params.ess.Ecap0 * soh(day); params.ess.Pmax = params.ess.Pmax0 * soh(day); [plan, result] = run_dispatch(params, forecast(:, :, day)); loss = cal_degradation(result.ess_profile); soh(day + 1) = max(soh_min, soh(day) - loss); end

这个闭环会让结果发生明显变化:没有SOH反馈时,储能容量下降带来的影响被忽略,全年成本被低估,后半段调度计划偏乐观;加入闭环后,模型会提前一些放慢储能吞吐节奏,用适度的弃电换取更长的寿命。这也正是标题里“平衡”二字的真正含义。

4. Matlab代码复现路径:从工具箱配置到算例出图

4.1 环境与求解器配置

复现这套模型,我建议的软件组合是Matlab + Yalmip + Gurobi。Yalmip是一个建模层,负责把优化问题描述清楚,然后调用底层的求解器。Gurobi是高性能商用求解器,对MILP问题求解速度非常快,学术许可申请也方便;如果暂时没有许可,也可以用Cbc或者默认求解器先跑通小算例。

第一次配置完Yalmip后,直接在命令行输入:

yalmiptest

看到所有求解器状态OK,就说明环境没问题。如果只想验证衰减建模逻辑,可以先把问题全部设成线性,用Gurobi跑;验证完再考虑加整数变量。别一开始就上多时段MILP,那样报错都不好定位。

4.2 算例参数设置:不要照抄论文,要能自洽

复现论文代码最容易犯的错误是参数取值随意。我建议做一套自洽的参数,哪怕数值不跟文献完全一致,只要逻辑成立,结果趋势就是可信的。下面是我用的模拟项目X的默认参数,你可以直接替换成自己的数据。

| 对象 | 参数 | 数值 | | 光伏 | 装机容量 | 40 MW | | 风电 | 装机容量 | 30 MW | | 负荷 | 峰值 | 60 MW | | 储能 | 容量 / 功率 | 20 MWh / 5 MW | | 储能 | SOC初值 / 下限 / 上限 | 0.5 / 0.1 / 0.9 | | 储能 | 初始SOH | 100% | | 储能 | 老化成本系数 | 0.3 元/kWh(示意) | | 外购电 | 电价区间 | 0.4-1.1 元/kWh | | 弃风弃光 | 惩罚单价 | 1.0 元/kWh |

场景设计建议至少三种:平稳天气、大风多云天气、极端波动天气。平稳天气主要看正常调度逻辑,大风多云天气能考验日内滚动修正能力,极端波动天气则用来检验备用约束和储能衰减成本的取舍。我个人习惯把场景数据存成.mat文件,主程序只负责读文件和跑模型,后处理再单独写绘图脚本。

4.3 核心代码路径与运行顺序

工程化程度好一点的复现代码,目录结构大概长这样:

main_VPP.m set_params.m load_scenario.m day_ahead_model.m intraday_model.m storage_degradation.m plot_results.m

主程序先配参数,再读场景,然后进入日循环。每一日的调度结果是结构体,包含储能充放电曲线、SOC曲线、备用利用率和各项成本。先把基础平台跑通,再逐步加衰减建模和SOH反馈,这是最稳的复现顺序。

日前模型的核心Yalmip代码片段类似下面这样,变量、约束、目标一目了然:

% 日前调度模型示意 T = 24; Pch = sdpvar(T, 1); Pdis = sdpvar(T, 1); SOC = sdpvar(T, 1); u_ch = binvar(T, 1); u_dis = binvar(T, 1); C = []; % SOC初值与迭代 C = [C, SOC(1) == 0.5 * params.ess.Ecap]; C = [C, SOC(2:T) == SOC(1:T-1) + (params.ess.eta_ch * Pch(1:T-1) - Pdis(1:T-1) / params.ess.eta_dis) * 1 / params.ess.Ecap]; % 充放电互斥 C = [C, Pch >= 0, Pch <= params.ess.Pmax * u_ch]; C = [C, Pdis >= 0, Pdis <= params.ess.Pmax * u_dis]; C = [C, u_ch + u_dis <= 1]; % SOC限值 C = [C, 0.1 * params.ess.Ecap <= SOC <= 0.9 * params.ess.Ecap]; % 节点功率平衡等约束略 objective = sum(c_buy .* Pbuy) + sum(price_curtail .* Pcurtail) + lambda_deg * sum(Pch + Pdis); ops = sdpsettings('solver', 'gurobi', 'verbose', 2, 'gurobi.TimeLimit', 300); optimize(C, objective, ops);

求解完成后,直接value(SOC)就能取到SOC曲线,value(Pdis)取放电功率曲线。日内模型结构类似,只是预测时域缩短,每15分钟滚动一次,目标函数里额外加了跟踪日前计划的偏差惩罚。

5. 复现中踩过的坑:收益、振荡和结果怎么读

5.1 衰减系数过小或过大,结果都会失真

我第一次跑模型,把老化成本系数lambda_deg设得很低,结果储能几乎全程满充满放,弃电率很低,算出来的运行成本好像很优秀。但把SOH反馈打开看,一年之后SOH掉到80%,第二年模型直接可用容量大幅缩水,前期的“优秀”全是透支未来换来的。

反过来把系数调高到每kWh 2元,储能就基本不动作了,所有灵活性都靠天然气机组和外购电扛,系统成本反而升高,储能成了摆设。这说明衰减系数不能拍脑袋定,要做敏感性分析:让系数从0开始逐渐增大,画出储能年吞吐量、弃电率和系统总成本三组曲线,在吞吐量开始快速下降的位置附近取拐点值。这个操作虽然基础,但对最终结论的可信度提升非常大。

5.2 多时间尺度模型的收敛与求解时间问题

另一个常见问题是求解时间失控。日内模型每15分钟执行一次,如果有24个时段还带二进制变量,一天下来要跑近百次优化,任何一次超时都会拖慢整体仿真。我处理这类问题的经验有三条。

第一,尽量让约束线性化。衰减成本不用绝对值,而是拆成Pch和Pdis两个变量,再配合互斥约束,而不是直接写abs(Pess),更不要用一个二进制变量控制充放方向后就写Pess * (1 - u)之类的乘积项。第二,减少日内模型的预测时域,没必要滚动24小时,4小时甚至2小时窗口足够覆盖超短期预测的有效信息。第三,可以先跑连续松弛版本,把二进制变量放开,检验初值和约束是否合理,确认模型不病态,再切回整数版本跑正式结果。按照这三条,我的单日仿真从十几分钟压缩到三分钟左右,结论不发生变化。

5.3 不同衰减模型下的调度差异:结果对比要看什么

复现完成后,我做了三组对比:无衰减模型、线性吞吐量衰减模型、半经验衰减模型。同一场景下,结果趋势如下表所示,具体数值会随参数变化,但规律基本一致。

| 模型 | 储能日均吞吐量 | 弃电率 | 全年SOH下降 | 系统日均总成本 | | 无衰减 | 很高 | 最低 | 最快 | 纸面上最低 | | 线性吞吐量 | 中等 | 略升高 | 中等 | 真实成本中等 | | 半经验衰减 | 降低约20% | 明显升高 | 明显放缓 | 综合最优 |

最容易被带偏的读法是只看弃电率。如果为了把弃电率从8%压到4%,让储能每天多跑好几个深度循环,省下来的弃电惩罚可能还没有电池折旧多。正确做法是把SOH下降折算成等值成本,画进系统总成本,看不同灵活性裕度下总成本曲线的拐点。这个拐点对应的调度策略,才是工程上真正应该用的方案,而不是追求任何单一指标的最优。

6. 从论文复现到工程应用:这套模型还能怎么改

6.1 从离线优化到在线滚动调度

论文里的多时间尺度调度往往是离线仿真,读历史数据、跑优化、统计结果,但工程落地时需要做成在线调度系统。差别在于在线系统对求解时间更敏感,还要接实时数据和下发指令的接口。

我建议把日前模型和日内模型分别封装成函数,输入输出全部标准化:输入是预测曲线、当前SOH、当前SOC,输出是储能出力计划、备用缺口提示。这样后续接入实际系统时,只需要换数据接口,不需要重写优化内核。在线滚动时还要注意数据时间对齐,Matlab侧拿到的SCADA数据如果有时延,优化结果下发就已经失效,这是工程里比算法本身更容易翻车的地方。

6.2 与市场机制衔接:储能报价要跟着衰减成本走

如果把这套模型放到电力市场环境里,储能就不只是被调度的资产,而是可以申报容量和价格的参与者。衰减建模的价值在于能给出动态报价:浅充浅放的报价可以低一些,深度充放要报价更高才能覆盖寿命损失。这个思路有点像出租车司机,跑短途和跑长途的价格策略完全不同。

具体实现可以把不同DOD区间对应的老化成本做成一张报价表,日内模型每15分钟根据当前SOC位置自动选择最优报价区间。这个模块和当前很多聚合商的实际需求贴合得很紧,也是论文模型向工程落地拓展时比较有说服力的一个方向。

6.3 不确定性建模的进一步扩展

如果还想在现有框架上做创新,方向不外乎三条:随机优化、鲁棒优化、学习型调度。随机优化用场景树描述预测误差,两阶段决策自然适配日前和日内的两层结构。鲁棒优化把预测误差放进区间集合,求最坏情况下的最优解,解的结果偏保守,但更扛揍。学习型算法可以用来调整柔性负荷补偿参数或者储能的衰减系数,让模型自适应工况变化。

我的实际体会是,不管往哪个方向扩展,先把衰减建模和SOH反馈的基础版本跑扎实,再往上叠加不确定性处理,否则出了问题很难分辨是优化模型本身的问题,还是老化参数的锅。这套复现流程走完一遍,后续加新模块会顺畅很多。我下一轮准备把温度对储能老化的影响也加进模型里,看看全年收益对环境的敏感性会不会反过来影响日前调度策略。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询