拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

虚拟电厂多时间尺度调度中储能衰减成本建模与Matlab实现

虚拟电厂多时间尺度调度中储能衰减成本建模与Matlab实现

做电力系统调度优化的人,对“虚拟电厂”这个词应该都不陌生。特别是在“双碳”目标下,风电、光伏装机量越铺越大,电网的运行方式越来越接近“看天吃饭”,系统对灵活性的需求已经从原来的“锦上添花”变成了“刚性约束”。但灵活性不会凭空掉下来,储能是最直接的来源,可储能贵,而且每次充放电都在消耗寿命。这时候如果调度模型里只用固定成本系数算储能,不考虑衰减,算出来的“最优”方案很可能是拿未来换现在,短期账好看,长期账亏到心疼。所以我复现这篇SCI论文的时候,最大收获不是把调度模型跑通,而是看它怎么把“灵活性”和“储能衰减成本”放在同一个优化框架里做多时间尺度权衡。这篇博文就把我的复现思路、模型设计、Matlab实现和踩坑记录完整分享出来,适合正在做虚拟电厂、储能调度、多时间尺度优化的同学参考。

1. 高比例可再生能源并网,虚拟电厂为什么绕不开“灵活性-储能成本”这对矛盾

1.1 可再生能源出力特性给调度带来的“麻烦”到底是什么

要理解虚拟电厂(VPP)的价值,先得看它要解决的痛点。光伏在傍晚出力快速下降,偏偏晚高峰负荷上升,两个曲线一叠加,净负荷曲线出现经典的“鸭形曲线”。风电则是“反调峰”,夜里风大负荷低,白天风小负荷高。这两个特点导致传统机组要么频繁启停,要么长时间压负荷运行,运行效率低、煤耗高,而且系统在小时级、分钟级的时间尺度上都会出现供需缺口。这个缺口就是灵活性需求,本质上要求系统里必须有能在短时间响应、能快速升降功率的资源。只靠传统火电硬扛,代价是碳排放和调节成本飙升;不硬扛,就要靠储能和柔性负荷来补位。

这也是为什么所有面向高比例可再生能源的调度研究,都要把灵活性放在一个显眼的位置。电网调度不再是简单地“跟着负荷走”,而是要主动安排“向上调节能力”和“向下调节能力”。比如光伏大发的中午,需要向下调节能力来压出力、给储能充电;晚高峰光伏退坡时,又需要向上调节能力来顶上缺口。这两个方向的调节能力缺口,只要存在一个,系统就要面临拉闸限电或大量弃风弃光。所以高比例可再生能源并网之后,灵活性问题本质上是一种“能力储备”问题,而不是单纯的“电量”问题。

1.2 虚拟电厂是怎么把零散资源“聚沙成塔”的

虚拟电厂不是真实存在的发电厂,而是通过通信、控制和聚合算法,把分布式光伏、分散式风电、用户侧储能、电动汽车充电桩、空调负荷这些容量小、位置散、响应特性完全不一样的资源,统一调度成一个“虚拟机组”参与电网运行。它的核心价值在于,单看一个充电桩微不足道,但聚合几千个充电桩,响应能力就相当于一台中型调峰机组。从电网角度看,VPP就是一个可以调节功率的外部资源;从内部看,它需要协调各类资源的出力、充放电和可削减负荷。

一个完整的VPP优化模型,通常包括分布式电源模型、储能模型、柔性负荷模型和上级电网交互模型。分布式电源模型处理光伏、风电的出力上限和预测误差;储能模型处理SOC、充放电功率和寿命损耗;柔性负荷模型把可转移负荷、可削减负荷折算成可调功率区间;上级电网交互模型则描述VPP与外电网的购售电关系。把这四类模型拼装起来,再叠加各种运行约束,就构成了调度优化的基础。我复现时最大的体会是,VPP调度和传统电厂调度最大的不同在于“资源的软约束太多”,比如空调负荷不能无限制削减,电池不能每次都用满容量,光伏不能随便弃,这些细节都要在模型里有所体现。

1.3 为什么储能成本不能拍脑袋定一个固定值

很多入门级的调度模型里,储能成本往往只有一个充放电单位成本,或者干脆设成0,只约束SOC。这样确实简单,但和现实差距非常大。储能电池的寿命和充放电循环深度强相关,深度放电1000次可能就退役,浅充浅放能撑3000次,折算下来,一次深度放电的老化成本可能比市电价格还贵。如果不把这个衰减成本写进优化目标,调度器一定会拼命让储能做深度充放来“压电网成本”,表面上风光利用率很高,实际上电池用了一年半就报废,整个项目经济性一塌糊涂。

所以论文里专门做了衰减建模,把每次充放电对寿命的损耗折算成运行成本,跟着SOC和DOD动态变化。举个例子,同一块电池在SOC从0.9放到0.1,和从0.6放到0.4,后者的损伤明显小得多,但前者能给系统提供的调节电量也大得多。调度模型要回答的正是这个“多出力但更伤电池”和“少出力但寿命更长”的权衡问题。这个细节,是我觉得整篇SCI最值得复现的部分,也是“灵活性-储能成本”这对矛盾的核心解法。

2. 多时间尺度调度框架与衰减建模的完整设计

2.1 为什么非要做“日前-日内-实时”三层调度

预测精度和调度周期是一对矛盾。提前24小时的预测,对风电、光伏的出力趋势能看个大概,但具体到某个小时的风速突变、云层遮挡,误差可能很大;提前1小时的预测精度明显提升,但留给机组启停和储能规划的时间又不够。所以成熟的做法是把调度过程拆成多个时间尺度,逐级修正。

复现论文采用的三层结构比较典型:日前调度以1小时为步长,做未来24小时的资源优化配置,确定储能预留、机组组合和可调度负荷的大方案;日内滚动调度以15分钟或1小时为周期,基于最新预测每4小时滚动更新一次,修正风光出力和负荷偏差;实时调度则做秒级到分钟级的功率分配,处理偏差和突发波动。三层之间通过“计划值传递、偏差量纠正”的方式衔接,上一层的结果是下一层的边界条件,下一层只对偏差做局部调整,避免重复优化导致抖变。

调度层时间粒度主要目标决策变量
日前调度1小时,24点确定机组组合与储能计划机组启停、储能充放电方案
日内滚动15分钟或1小时修正预测偏差储能修正功率、可调负荷调整
实时控制秒级至分钟级平衡实时功率偏差储能快速出力、紧急切负荷

这套结构对于“灵活性”的意义在于:不同时间尺度需要的灵活性来源不同。日前阶段主要靠机组组合和储能预留来提供长时灵活性;日内阶段靠储能快速爬坡和柔性负荷调节;实时阶段靠储能和备用响应秒级波动。如果只做日前单阶段优化,根本没法处理分钟级的风光波动;如果只做实时优化,又没法提前安排电池的SOC空间。多时间尺度调度不是“锦上添花”,是让储能资源在不同时间尺度上都能被用起来的必要条件。

2.2 储能衰减建模:三种主流方法怎么选

储能衰减建模有三种常见思路,复现之前我专门捋了一遍。

第一种是等效循环寿命法:把不同放电深度下的循环次数折算成标准循环次数。磷酸铁锂电池在100% DOD下循环寿命约2000次,在50% DOD下可能到6000次,用一个幂函数拟合DOD-循环寿命曲线,再按“累计等效循环次数/总循环次数”计算寿命消耗。第二种是能量吞吐量法:认为电池总吞吐电能量是固定值,每次充放电消耗对应的吞吐量,除以总吞吐量就是老化比例。这个方法更简单,适合做线性化。第三种是雨流计数法,常用于电池寿命评估领域,通过统计充放电循环的完整性和放电深度计算疲劳损伤,准确但计算量大,不适合直接嵌入优化模型。

论文原文用的是等效循环法,把老化成本按充放电功率和SOC状态折算成每个时段的线性或分段线性成本,加进目标函数。好处是既能体现“深度充放更伤电池”的特点,又能保持优化模型可解。具体折算时会引入DOD与循环寿命的关系曲线,比如用式 (N_{cycle}(DOD)=N_0 \cdot (DOD)^{-k}) 来拟合,然后把单次充放电对寿命的消耗按1/N_cycle估算,再乘以电池容量和更换成本,得到单位功率的老化成本。这样做下来,储能调度的经济性评估就真实很多,不会为了省几分钱的购电费去牺牲电池寿命。

2.3 目标函数和约束条件怎么“落笔”才能贴合复现模型

这一节是核心。目标函数按论文里的做法,可以写成系统运行总成本最小化,包括:外购电成本、分布式电源发电成本、储能老化成本、弃风弃光惩罚成本和灵活性不足惩罚成本。其中外购电成本采用分时电价,灵活性不足惩罚用“无法爬坡的功率缺额乘以惩罚系数”来体现。

约束条件分为四组。功率平衡约束,即所有资源出力加总等于负荷;分布式电源约束,包括出力上下限和爬坡约束;储能约束,包括SOC递推公式、充放电功率限制、SOC上下限;备用与灵活性约束,即系统在任意时段必须具备足够的向上或向下爬坡能力来应对预测误差,这个约束正是“灵活性”落地的关键。灵活性约束通常写成可用上调容量之和大于预测误差的置信区间,或写成旋转备用约束。

在实际代码里,目标函数可以写成:

[ \min \sum_{t} \left( C_{grid,t} \cdot P_{grid,t} + C_{deg,t}(P_{ch,t}, P_{dis,t}, SOC_t) + C_{curt} \cdot P_{curt,t} + C_{flex} \cdot P_{flex,short}\right) ]

其中 (C_{deg,t}) 就是老化成本函数,它与储能的充放电功率和当前SOC相关。这个函数的线性和凸性是求解速度的关键,如果直接用二次函数,MILP求解器不一定能处理,通常需要做分段线性近似。我在复现时把SOC区间分成三段,每一段对应不同的老化惩罚系数,再通过Yalmip的sos2约束加入模型,既保留了物理特性,又保证了可解性。

3. Matlab代码实现:从数据到结果的可复现路径

3.1 先搭好代码骨架,再填模型

复现SCI论文最忌讳拿到代码就一头扎进函数堆里改。我的习惯是先搭一个清晰的代码结构,按“数据—参数—模型—求解—画图”五层拆开。项目文件夹里我通常这样组织:

  • data/:存放风光出力、负荷、电价的历史序列或典型日数据,CSV或MAT文件。
  • params/:参数初始化脚本,统一管理所有常量。
  • models/:各类资源的目标函数和约束构建函数。
  • solver/:调用Yalmip和Gurobi的求解入口。
  • results/:保存结果和图表。

这个结构的好处是,改参数、换数据、升级模型时不会牵一发而动全身。尤其是做三阶段调度,每个阶段都要复用基础数据,如果参数散落在不同脚本里,后期调试会非常痛苦。建议一开始就把所有参数集中到一个config.m里,每个阶段脚本用run('config.m')加载。另外,Matlab版本建议R2023a以上,Yalmip要用新版,求解器Gurobi或Cplex都行,如果只是复现小规模算例,也可以用内置的intlinprog顶着,但求解速度和稳定性会差一些。

3.2 参数初始化和典型日数据准备

先看参数。我的复现案例假设一个包含200MW光伏、100MW风电、50MW/200MWh储能和50MW可调负荷的虚拟电厂,与上级电网交互上限150MW。关键参数表如下:

参数取值说明
储能功率/容量50MW/200MWh锂离子电池
储能SOC范围0.1 ~ 0.9保护电池
充放电效率0.95 / 0.95往返效率约0.9
电池循环寿命5000次@80%DOD用于等效循环折算
风光预测误差15% / 10%日内/实时修正用
分时电价峰谷1.2 / 0.4 元/kWh市场电价

风光出力曲线我用论文公开数据近似生成:把典型日归一化出力曲线叠加随机扰动,再按装机容量放大。负荷曲线用地区典型夏季日曲线。这部分严格来说不是论文复现最核心的部分,但数据选得不好,后面所有结果都不可信。建议读者不要直接用随机数跑,尽量找同地区公开数据集,或者至少采用论文附录中的典型日数据。特别是储能初始SOC,必须在前一天调度结束后自然生成,不能随手给定,否则第一天会出现“凭空放电”的问题。

3.3 储能衰减成本函数怎么用Matlab写

衰减成本函数。我写的时候用的是“分段线性化”方式,避免直接在优化里用非线性函数。核心想法是:把电池SOC区间分成三段,每段对应一个老化成本系数,然后通过Yalmip的binvar和sos2约束构造成本。也可以用一个简化的二次函数近似,再用线性化工具包处理。给一个简化示例函数:

function deg_cost = battery_degradation(P_bat, SOC, params) % 简化衰减成本:与充放电功率的二次方和SOC偏离程度相关 % SOC_mid 是电池最健康的工作点,一般取0.5 alpha = params.degradation_coeff; % 元/MW^2 beta = params.soc_penalty; % 元/pu^2 deg_cost = alpha * (P_bat.^2) + beta * (SOC - 0.5).^2; end

说明:实际SCI论文里,衰减成本不会用这么简单的函数,而是基于DOD循环寿命曲线做积分或分段。这里展示的是把“深度充放惩罚”和“SOC偏移惩罚”同时表达出来的思路,可以加进目标函数,也能作为约束加入。要注意系数α、β不是随便拍的,后面第4.2节会说怎么标定。在Yalmip中,如果直接用二次函数,可以配合quadprog类求解器,但如果是MILP,建议提前用分段线性近似,否则求解器很可能报错或陷入局部解。

3.4 用Yalmip建模多时间尺度调度

Yalmip是Matlab下的建模工具,让优化模型从数学公式到代码的转换非常顺畅。三层调度里,日前模型要建24小时连续+整数变量,日内滚动模型按当前时刻滚动建窗口,实时模型则是单时段二次规划。代码骨架如下:

% 日前调度示例:24小时,时间步长1h yalmip('clear') P_ch = sdpvar(24,1); P_dis = sdpvar(24,1); SOC = sdpvar(25,1); P_grid = sdpvar(24,1); P_pv = sdpvar(24,1); % 实际出力(可能小于预测) u_ch = binvar(24,1); % 充电状态 u_dis = binvar(24,1); % 放电状态 Constraints = []; % 功率平衡 Constraints = [Constraints, P_grid + P_pv + P_dis == P_load + P_ch]; % 储能约束 for t = 1:24 Constraints = [Constraints, SOC(t+1) == SOC(t) + P_ch(t)*eta_ch - P_dis(t)/eta_dis]; Constraints = [Constraints, 0 <= P_ch(t) <= P_ch_max*u_ch(t)]; Constraints = [Constraints, 0 <= P_dis(t) <= P_dis_max*u_dis(t)]; Constraints = [Constraints, u_ch(t) + u_dis(t) <= 1]; Constraints = [Constraints, SOC_min <= SOC(t+1) <= SOC_max]; end % 目标:购电成本 + 充放电老化成本 + 弃光惩罚 Objective = sum(P_grid .* price) + sum(battery_degradation(P_ch - P_dis, SOC(1:24), params)); optimize(Constraints, Objective, sdpsettings('solver','gurobi'));

代码说明几个关键点:用binvar保证充放电不同时进行,用sdpvar声明连续决策变量,目标函数里把衰减成本作为正则项加入。实际复现时,还需要在循环里给P_pv设置上限约束,并用预测值初始化。日内滚动和实时调度的代码类似,只是时间窗口缩短,并且把日前计算出的SOC计划作为固定参量传入。注意P_ch - P_dis这种写法在二次函数里会引入交叉项,如果希望减少非线性,建议把充放电功率分开声明,目标函数写成两部分之和。

3.5 结果怎么分析才算把论文复现到位

跑完代码后,至少要画四类图:各时段电源出力堆叠图;储能SOC曲线和充放电功率;系统购电功率与实时电价的对比;考虑衰减与不考虑衰减的成本对比图。最后一类图最能说明“灵活性-储能成本平衡”的价值——不考虑衰减时,储能夜间充满、白天高峰期放出,社会总成本显得很低;考虑衰减后,调度结果会让储能更多在浅充浅放区间运行,老化成本下降,虽然购电成本可能会略微上升,但全年总经济性更好。

画图用Matlab的plot和area即可,但需要注意时间轴对齐。日内滚动和实时阶段的数据要按时间戳拼接,不要只看断点之前的曲线。我一般先把所有结果落到同一个时间向量的结构体里,再统一画图,避免因为每个阶段时间步长不同导致曲线对不上。另外,建议把SOC曲线的纵轴取值范围固定,不然很难对比不同调度策略下的SOC轨迹。

4. SCI复现过程中踩过的坑和解决记录

4.1 求解器报错和线性化处理

第一个坑是“Gurobi不收敛”或“Yalmip报错No suitable solver”。这通常是因为模型里带了非线性项,比如SOC乘以充放电功率,但求解器是MILP。解决办法是:把储能功率的二次衰减成本项分段线性化,或者采用Yalmip的implies逻辑离散。如果模型里只有连续变量,可以用非线性求解器如ipopt,但通常调度模型是MILP,所以我强烈建议把所有非线性能转化为线性的地方全部转化。

衰减成本用sos2约束或big-M分段都可以。另外,整数变量多的时候,求解会很慢,先把binvar数量降下来,比如把每个时段充放电状态合并成一个三状态整数变量(0不出力,1充电,2放电),这样不仅变量少,还天然排除同时充放,比u_ch + u_dis <= 1效率高得多。我在复现的时候最初用了两个二元变量加一个约束,结果24小时模型增加48个整数变量,求解时间将近半小时;改成三状态变量后,求解时间降到几分钟。

4.2 衰减成本系数怎么标定才不过头

很多同学复现这种论文,最迷茫的就是系数。我踩过的坑是:第一次把老化成本系数设得过大,结果储能整个调度周期几乎没有动作,灵活性全交给上级电网,从结果看一点不“绿色”;设得过小,储能又变成“无脑搬移电量”,和没衰减模型几乎一样。标定思路是:先用电池厂商给的循环寿命数据,算单位MWh吞吐量的等效老化成本,这个值就是α的数量级参考;再通过一组单时段测试,看调度模型在不同α下的储能利用率变化,取“灵敏度拐点”作为最终值。

另外SOC惩罚项β不要单独设很大,否则SOC会被强制钉在0.5,储能基本失去容量价值。最优方案应该是让SOC在0.2~0.8之间按电价动态浮动,而不是死守某个“健康值”。衰减模型的目的是让储能“少做不划算的事”,不是让储能“不做任何事”。

4.3 三层调度衔接时SOC“跳变”怎么办

日前计划给出了每小时SOC目标值,但日内在执行时,实际风光曲线与预测不同,SOC会偏离计划。如果日内模型直接以日前SOC为目标,会让储能频繁“纠正”功率,出现反调节。这个问题的标准解法是:日内滚动模型只对“日前SOC计划曲线”施加一个软约束,目标函数里加一个SOC偏差惩罚项,系数相对较小;实时调度则完全以日内最新指令为参考,不再强制追平日前。

这个分层松绑的思路,是保证三阶段调度稳定的关键。我最初强行把日前SOC作为硬约束,结果日内模型无解,改成软约束后马上正常。实际上,日前计划的意义是“提供边界和方向”,而不是“绑定每一刻的SOC”。只要日内偏差在允许范围内,就不必追求完全还原日前路径,否则调度指令会像“拉锯战”一样来回震荡。

4.4 经常遇到的其他问题和排查手段

这里整理一个速查表,适合在调试阶段对照:

现象可能原因排查/解决
求解器报infeasible功率平衡约束与变量范围矛盾检查初始SOC是否在范围内;逐步放开负荷上限
结果里储能同时充放电只设了功率上限,没设互斥用三状态整数变量或充放电状态互斥约束
衰减成本为负公式里充放电功率差值被错误平方将充放电功率分开定义,不要用差值做平方
实时调度曲线跳变时间窗口滚动重叠逻辑错误检查用于滚动的最新预测数据是否被下一阶段覆盖
画图数据错位各阶段时间步长不同统一时间戳,用分钟级时间轴重采样

除了速查表,还建议从一开始就跑小规模算例验证,比如先做6小时单阶段调度,确认结果合理再扩展24小时三层调度。我复现时前两次失败都是因为直接拿论文的大规模算例跑,数据量一大,问题被淹没在“不收敛”里,很难定位。先小后大,先简单后复杂,是复现这类优化论文最朴素但最有效的策略。

最后分享一个小技巧。我在做这种SCI复现时,会把论文里的参数表、目标函数、约束条件先整理成一个文档,把数学公式翻译成中文逻辑,再动笔写Matlab。看起来多花了一两个小时,但后面写代码时会非常快,因为你已经把“每个约束为什么存在”想清楚了。复现不是为了把作者代码抄一遍,而是把里面的工程权衡变成自己的判断力。这篇分享给的只是路径,真正的“灵活性-储能成本”平衡,还是要靠你自己手里的数据和参数去调出来。

返回列表