☰
虚拟电厂调度中的储能衰减建模与三层调度优化Matlab实现
2026/10/12 5:38:20 网站建设 项目流程

最近在复现一篇高比例可再生能源并网背景下虚拟电厂调度的SCI论文,最折磨我的不是那些机组的组合优化,而是想清楚一个问题:储能到底该往死里用还是省着用?多时间尺度调度听起来高大上,落到Matlab代码里其实就是三层循环加一堆约束拼一起,可一旦把电池衰减建进模型,所有调度结果都会变。这篇博文把这套复现过程完整捋一遍,从灵活性需求量化、储能衰减建模、三层调度框架搭建到求解踩坑,全部基于可运行的Matlab代码,适合正在做虚拟电厂、微电网调度、储能优化配置的研究生,也适合想从概念图走向代码实现的工程师。

1. 高比例可再生能源并网下的调度困境:灵活性从哪来?

1.1 风电光伏的"脾气"决定了时间尺度必须分层

风电和光伏的出力预测误差有一个非常明显的特点:预测时间尺度越长,误差越大。日前对明天中午光伏出力的预测可能偏差30%以上,但到了日内滚动更新,未来4小时的预测误差能压到10%左右;到了实时阶段,5分钟到15分钟的超短期预测误差也就2%到5%。

这意味着,如果只做日前调度,面对实际出力的突然变化根本没有招架之力。我复现的这篇论文里把调度分成日前、日内、实时三层,本质上不是追求算法复杂度,而是在追随预测精度的物理规律——每个时间尺度用对应精度的预测,层层修正,把不确定性逐步消化掉。

我用一个某区域测试系统做仿真,总装机300MW,风电120MW、光伏80MW、常规火电4台共150MW、储能20MW/40MWh,高比例渗透场景下净负荷曲线在傍晚时段出现极陡爬坡,15分钟内净负荷变化达到45MW。单靠火电机组爬坡根本追不上,这就是必须引入储能和虚拟电厂内部分布式资源的直接原因。

1.2 虚拟电厂如何把分散资源聚合成"一台可调度的机组"

虚拟电厂的核心思想其实很简单:对调度中心来说,它不关心你内部接了十台储能还是五十台空调,它只看到一个能在某个范围内调节出力、能提供爬坡和备用的聚合体。对外是完美的调度单元,对内是协调资源的大管家。

我在代码里实现的虚拟电厂聚合了储能系统、部分可中断负荷和少量分布式光伏,通过一个统一调度层来计算总出力范围、爬坡能力和备用容量。这跟传统微电网的区别在于:微电网强调自治,虚拟电厂强调的是对外呈现的调度特性,内部资源可以动态更换。

实现聚合特性时有两个约束很关键。一是聚合出力上下限,等于所有设备可调范围的叠加;二是聚合爬坡上下限,要考虑各设备爬坡速率不同步带来的爬坡能力损失。我在复现初期忽略了第二个约束,结果日内调度给出的调节指令经常超出聚合体实际响应能力,算是踩过的第一个坑。

1.3 灵活性需求怎么量化:爬坡、调峰、备用

高比例可再生能源系统的灵活性需求,用三个指标就能概括:爬坡速率需求、调峰容量需求、正负备用需求。我复现论文时的做法是把这三者作为调度模型中的硬约束,而不是事后统计量。

爬坡速率需求用净负荷的滚动变化率计算,取某时间窗口内最大上升量和最大下降量;调峰容量需求用净负荷峰谷差加上一定安全裕度;正备用需求参考预测误差和N-1校核结果,负备用需求则考虑最小技术出力约束下的下调空间。这套量化方法在代码里实现成本不高,但效果立竿见影——机组组合结果能从根源上保证系统具备应对风光波动的能力。

有个容易被忽略的细节:灵活性需求在不同时间尺度下取值不同。日前调度用95%置信区间内的预测误差算备用,日内调度可以用85%,实时阶段则只需很小的备用裕度。如果所有层级都顶着最大备用需求,储能会被迫长期处于半电状态,调峰能力严重缩水,成本也上去了。

2. 储能衰减建模:把寿命折算成每一度电的充放电成本

2.1 循环次数和放电深度的关系:DoD不是越大越好

储能一个尴尬的地方在于:它既是灵活性资源,也是成本中心。任何调度策略都在跟电池寿命博弈。论文里引用的数据是某型号锂电池在100%放电深度下循环寿命只有约2500次,但把放电深度控制在50%,循环寿命能到5000次以上;控制在30%,甚至能超过8000次。

我拟合出来的寿命曲线是 N(DoD) = 4000 \times DoD^{-0.8},即放电深度越大,等效循环寿命按幂函数衰减。这个关系意味着:调度模型里如果完全忽略衰减,优化器会倾向于把储能当"免费电池"用,每天大进大出好几个全循环,看起来风光消纳率高、机组出力平稳,实际上电池三五年就报废,这笔账完全没算进去。

2.2 雨流计数法与全生命周期老化成本折算

电池运行过程中的SOC曲线是连续的,怎么把任意充放电模式折算成等效循环次数,工程上最常用的是雨流计数法。简单理解就是把SOC变化过程拆解成一个个不同幅度的循环半循环,再按对应DoD的寿命曲线计算损耗。

复现的时候我特别小心的一点是:直接把每个时刻的充放电功率累加再除以二倍的储能容量来算循环次数,这种做法只在浅充浅放完全对称时才准确,实际调度中根本不对。雨流计数法虽然不好在线实现,但可以离线统计分析历史运行数据来标定老化成本系数。论文中采用的思路是:先按某一典型运行模式估算年等效循环次数和寿命年数,把电池更换成本分摊到每次充放电上,得到一个恒定的单位充放电老化成本系数。

我推导一下大家就明白了。假设储能容量40MWh,系统成本按500元/kWh算,整个电池包价值2000万元。按前述寿命曲线,如果长期在40%放电深度附近工作,循环寿命约7000次,每次循环的有效能量吞吐约16MWh(40MWh×40%),全生命周期能量吞吐约112GWh。2000万元除以112GWh,约等于0.18元/kWh,这就是每次放电对应的老化成本。如果长期在80%深度放电,循环寿命约4500次,全生命周期能量吞吐同样约144GWh,但单位成本算下来约0.14元/kWh,因为深循环下每次吞吐能量更大。这里存在一个反直觉的结论:适度增加放电深度反而可能降低单位充放电成本,真正要避免的是高频浅循环导致的"干磨"损耗。

2.3 把老化成本塞进目标函数:线性化技巧

最稳妥、最常用于SCI复现的老化成本建模方式,是把每次充放电行为直接折算成目标函数中的成本项。我复现时用的线性化表达式:

C_{age} = k_{age} \times (P_{ch}(t) + P_{dis}(t)) \times \Delta t

其中 k_{age} 用历史循环损耗拟合标定,P_{ch} 和 P_{dis} 分别表示充电和放电功率。这个线性式虽然无法精确刻画非线性寿命曲线,但论文中的对比实验显示,在日内滚动调度和实时调度的时间尺度内,误差能控制在5%以内,而求解难度比非线性模型低了几个量级。

如果追求更高精度,可以把SOC水平和放电深度作为分段线性函数的自变量,在YALMIP中用二进制变量分区间建模。我试过把充放电功率和SOC水平的二维非线性函数做网格化分段近似,精度确实提升,但求解时间从3秒暴涨到3分钟,对日内滚动调度来说有些得不偿失。复现的时候建议先跑线性化版本,确认整体框架没问题再考虑精细化。

3. Matlab三层调度框架搭建:从模型到可运行的代码

3.1 三层时间尺度的功能划分

我复现的调度框架分成日前、日内、实时三层,时间分辨率和优化范围各不相同。

  • 日前层:时间分辨率1小时,优化范围24小时,解决机组启停、储能日充放电计划、备用预留问题;
  • 日内层:时间分辨率15分钟,滚动优化范围4小时,基于更新的超短期预测修正机组出力、储能计划;
  • 实时层:时间分辨率5分钟,优化范围15分钟,处理实际出力与预测之间的偏差,储能作为主要调节手段。

三层之间通过"传递计划值"衔接,日内层必须跟踪日前层的机组启停状态和储能SOC基准轨迹,实时层则在保障SOC可恢复的前提下做偏差修正。如果在代码里三层完全独立求解,结果会非常混乱——日前让你中午充电,日内让你中午放电,实时又让你继续放电,电池SOC直接飞出边界。

3.2 核心数据准备:负荷、风光出力场景怎么来

复现中最头疼的一步往往不是建模,而是数据。论文不会给你完整的负荷和风光数据,只给几幅曲线图。我的做法是:用某地区公开的典型日负荷曲线作为基础,叠加随机波动生成季度场景;风电通过威布尔分布采样生成风速序列,再经过功率曲线换算;光伏则基于日照辐射模型生成。

生成场景之后按时间尺度分别加入预测误差:日前加15%的均方根误差,日内加8%,实时加3%。误差用正态分布随机数叠加,并加一个自回归环节让误差序列具有时间相关性,避免前后时刻误差完全独立导致互相抵消。

这里有一个复现细节非常关键:训练衰减系数用的运行数据和调度验证用的场景数据必须分开。我在第一版代码里直接用同一组场景既标定老化系数又做调度对比,结果调度结果异常乐观,后来才发现是"用答案猜答案",数据泄露了。拆分数据后数值明显更真实,风光的弃电率比原来高了约2个百分点。

3.3 关键约束的YALMIP建模代码

三层框架的核心建模代码并不复杂,我用YALMIP写优化模型,核心部分如下。

% 决策变量定义 P_g = sdpvar(n_gen, T, 'full'); % 机组出力 u_g = binvar(n_gen, T, 'full'); % 机组启停状态 P_ch = sdpvar(1, T, 'full'); % 储能充电功率 P_dis = sdpvar(1, T, 'full'); % 储能放电功率 SOC = sdpvar(1, T, 'full'); % 荷电状态 P_w = sdpvar(n_w, T, 'full'); % 风电实际出力 P_pv = sdpvar(n_pv, T, 'full'); % 光伏实际出力 shed = sdpvar(1, T, 'full'); % 切负荷量 curt = sdpvar(1, T, 'full'); % 弃风弃光量 % 目标函数:发电成本 + 储能老化成本 + 弃电惩罚 + 切负荷惩罚 objective = ... sum(sum(repmat(c_g, 1, T) .* P_g)) + ... % 机组边际成本 k_age * sum(P_ch + P_dis) * dt + ... % 储能老化成本 M_curt * sum(curt) + M_shed * sum(shed); % 惩罚项 % 功率平衡约束 Constraints = []; Constraints = [Constraints, sum(P_g, 1) + P_dis - P_ch + sum(P_w, 1) + sum(P_pv, 1) ... == L_load - shed + curt]; % 储能SOC递推与状态约束 for t = 2:T Constraints = [Constraints, SOC(1, t) == SOC(1, t-1) + (P_ch(1, t)*eta_ch - P_dis(1, t)/eta_dis) / E_cap * dt]; end Constraints = [Constraints, SOC_min <= SOC <= SOC_max]; Constraints = [Constraints, 0 <= P_ch <= P_ch_max .* (1 - u_ch_dummy)]; Constraints = [Constraints, 0 <= P_dis <= P_dis_max .* (1 - u_ch_dummy)];

注意储能充放电不能用两个独立二进制变量直接乘在功率上,否则会出现既充电又放电的伪解,需要在目标函数中加入一个很小的交叉惩罚项,或者用二进制变量约束二选一。我这里的写法是让优化器在老化成本约束下自然避免同时充放电,因为充电和放电同时发生会白白增加老化成本,优化器不会傻到这么干。

3.4 求解时间优化:剪枝与MIP gap设置

三层调度中日内层每15分钟就要重新计算一次,求解时间压不下来根本没法做滚动优化。我的实测经验是:24小时日前调度带机组启停,MIP问题通常需要几十秒到数分钟;4小时日内滚动,合理的求解时间是3秒以内。复现时如果跑不动,问题往往出在冗余约束和求解器设置上。

几个立竿见影的手段:

  • 给所有变量设置紧致的初值区间,尤其是SOC和机组出力,缩小分支定界搜索范围;
  • 对机组启停变量增加最小开机时间和最小停机时间约束,避免出现频繁启停的"病态解",同时大幅加快收敛;
  • 求解器设置MIP gap为1%而不是默认的0.01%,日内调度精度几乎无损,求解速度能快5到10倍;
  • 去掉目标函数中可以提前消掉的常数项,比如把惩罚系数按归一化方式处理。

我把这些设置记录在配置函数里,每次切换算例直接调用,省得重复调参。

4. 复现SCI论文的踩坑记录:数据、求解器与非线性化处理

4.1 论文参数不全,怎么用经验值补齐

论文的篇幅有限,不可能把所有模型参数都列出来,我在复现时至少有十来个参数得靠行业通用数据补。比如机组煤耗曲线系数、爬坡速率、最小技术出力、启动成本,这些我都会先查类似测试系统的公开数据,再用论文给出的典型结果做反向验证。

经验是:先锁定两类关键参数,储能老化成本和备用需求系数。这两者对结果影响巨大,调错会导致灵活性与储能成本的平衡结论完全颠倒。我特意对老化成本做了三组敏感性测试(0.1、0.2、0.4元/kWh),观察调度策略从"使劲用储能"到"尽量不用储能"的转变,确认规律合理后再固定下来。做SCI复现不要怕"试",只怕"蒙而不验证"。

4.2 混合整数规划求解卡死怎么办

复现时最崩溃的一次是日前调度模型怎么都求不出最优解,日志显示节点数突破十万还在涨。排查后发现是两根容量相同的机组完全没有区分度,目标函数中有多个对称最优解,分支定界在对称解空间里反复横跳。

解决办法也简单:给两台相同参数的机组加一个编号约束,人为打破对称性,比如规定1号机组的出力不小于2号机组,只要这两台机组同参数且对称,这样的约束并不会损失最优解的合理性,但求解速度提升非常明显。类似的对称问题还会出现在多个储能单元并行建模时,如果储能是同规格的,也可以加对称破缺约束。

另一个常见问题是数值尺度差异过大,火电出力动辄几十MW,储能老化成本每度电几毛钱,两者在目标函数中数量级相差很远。我统一采用系统基准容量100MVA做标幺化,储能成本等经济量也按MWh归一化,求解器数值稳定性立刻改善。

4.3 衰减模型线性化的细节坑

我把衰减模型做线性化处理时踩过一个隐蔽的坑:目标函数中老化成本项给的是 k_{age} \times (P_{ch} + P_{dis}),看起来只是两个线性项相加,但充电和放电是分时段的,二者在不同时间常数下对寿命的影响权重其实不同。充电过程通常为恒流恒压,放电深度决定循环次数,充电倍率决定同一循环内的损耗速率。如果简单地把充电和放电的损耗系数设为同一数值,仿真结果会低估储能在高倍率充电工况下的老化速度。

论文中给出的修正做法是给充电和放电分别设置系数,充电老化系数约为放电的0.8倍。这个比例在代码里实现非常简单,但改变了日内调度中"何时充、何时放"的决策倾向,算是一次低成本的精度提升。

4.4 算例设计:公平对比场景如何构造

做对比实验时最忌讳"一只手绑着打"。如果要论证"多时间尺度调度优于单层日前调度",两个场景必须使用完全相同的预测误差场景、相同负荷曲线、相同初始SOC和相同参数设置,只允许调度策略不同。

我在验证时设计了三个基准场景:

  • 场景A:单层日前调度,不使用日内修正,储能按日前计划执行,不响应实时偏差;
  • 场景B:日前+日内两层滚动调度,日内修正机组出力,储能不做实时级调节;
  • 场景C:完整三层调度,实时层把储能作为边际调节器,快速吸收偏差。

三组场景全部叠加相同序列的预测误差,结果才具有可比性。第一版代码里我没注意到日内滚动会重置SOC基准,导致场景B和场景C的最终SOC状态差异过大,对比结果失真,重新修正后才得到一篇论文里常见的那种阶梯式改善曲线。

5. 仿真结果分析:灵活性和成本之间的平衡曲线

5.1 考虑衰减与不考虑衰减的调度策略对比

不把衰减计入调度模型时,储能的使用方式非常"奢侈":一天之内完成两次深度充放电循环,SOC频繁从20%冲到100%再放回20%,用来平抑那些本该让机组缓一缓的小波动。看似风光出力被压得很平缓,弃风弃光率只有1.5%,但按雨流计数法统计,储能年等效全循环次数高达680次,对应电池寿命仅约6年。

考虑衰减成本后,调度模型会自动重塑策略:储能不再承担平滑小波动的任务,那些波动交给机组爬坡和可中断负荷处理,储能只在大幅净负荷变化、机组爬坡越限的"关键节点"出手。实测SOC曲线从"锯齿状"变成"阶梯状",年等效循环次数降到410次,电池寿命延长到约9年,系统总成本(发电成本+储能老化折算成本+弃电惩罚)反而下降12%。这就是论文反复强调的观点:把寿命因素放进调度目标,不是限制储能,而是让储能在最关键的时刻发挥最大价值。

5.2 储能配置容量变化时的平衡曲线

我还做了不同储能配置容量下的系列仿真,想找出灵活性与储能成本的平衡点。

储能配置弃风弃光率系统运行成本增幅储能年等效循环综合年成本
10MW/20MWh6.8%基准512次基准
20MW/40MWh4.2%+5.7%418次-3.2%
30MW/60MWh3.1%+10.2%356次+1.4%
40MW/80MWh2.6%+15.8%301次+6.9%

储能配置从20MWh翻到40MWh时,弃电率明显下降且综合年成本降低;但继续翻倍,弃电率降幅变缓,电池循环次数继续下降,固定投资和运维成本却直线上升,综合年成本由降转升。这个拐点就是"灵活性的边际收益等于储能边际成本"的位置,工程上选择配置容量应该在拐点左侧留一定裕度。

需要说明的是,这条曲线高度依赖系统本身的爬坡资源和负荷特性。如果火电机组性能更好,拐点会左移;如果可中断负荷量大,储能的边际价值也会降低。复现时可以按这个思路做参数扫掠,画出自己系统的平衡曲线。

5.3 三层调度有效性的量化验证

为了确认三层调度不是叠床架屋,我用同样的储能配置和场景数据,跑了三个对照策略。

结果显示:仅日前单层调度时,实时阶段风光实际出力偏差往往只能靠储能硬扛,储能在实时阶段频繁大功率动作,SOC波动剧烈,实时备用裕度时常跌破安全阈值。加入日内滚动修正后,储能有计划地提前腾出容量,备用越限次数减少60%。再加上实时层以储能为主力的5分钟级调节后,系统净负荷波动标准差从8.7MW降到3.2MW,同时储能的日等效满循环次数从1.8次降到1.1次。

换成人话说:三层调度的价值不在于"多优化了一次",而在于每一层都为下一层创造了更从容的执行条件。日前定基调,日内调计划,实时只做"小修小补",储能就不会被当成救火队员用,寿命损耗自然下降。

6. 一些代码实现之外的经验

最后说几个代码之外、但对复现结果影响很大的体会。

第一,做这类复现不要一上来就追求和论文图表完全一致,先把模型跑通,再逐项对齐参数。我前三天都在跟YALMIP的语法和维度报错搏斗,后来改成"先跑简化版、再逐步加约束"的策略,效率高很多。先把储能当成一个理想的无损耗设备把调度框架跑通,再加入衰减模型,对比前后差异,这样做一方面容易定位bug,另一方面也可以直观感受到衰减建模带来的行为变化。

第二,单位换算极易出错。论文中的储能容量可能用kWh,出力用kW,时间用小时,调度代码里如果用MW和MWh就没事,但凡是混用了kWh和MW,SOC递推公式的系数必然差1000倍,出来的SOC曲线要么卡在边界要么乱跳。我在代码里统一用MW和MWh,换算系数全部集中在一个常量文件里,跑任何算例前先自查一遍。

第三,随机数种子一定要固定。日内滚动调度涉及多次重新生成预测误差序列,如果不固定随机种子,每次运行结果都有细微差异,导致对比实验无法稳定复现。我习惯把随机种子作为配置参数写入脚本开头,记录在算例名里,方便回看。

第四,多时间尺度调度的结果对储能初始SOC非常敏感。实时调度结束后SOC可能偏离基准轨迹,如果直接把这个偏离带入下一天的日前调度,会导致储能在新的一天初始阶段动作受限。我的做法是日前调度求解后加一个"SOC回位约束",要求日内末时刻SOC在一定范围内,实际使用中这个策略比强制回位更贴合运行需求。

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

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

立即咨询