参与调峰的储能系统配置方案及经济性分析(Matlab代码实现)
最近帮某项目复现了一篇EI会议上关于“参与调峰的储能系统配置方案及经济性分析”的论文,前后断断续续折腾了两周。说实话,这种题目看着挺唬人,但拆开来看,它本质上是一个“容量配置+运行调度+经济评价”的组合优化问题。难点不在于Matlab代码本身,而在于怎么把论文里的数学模型吃透,再转化成可求解的代码。
这篇博文我就从复现者的视角,把整个思路、模型、代码骨架和踩过的坑完整梳理出来。内容主要适合三类人:正在复现EI/SCI论文的研究生、做储能规划的工程师、想用Matlab做电力系统优化分析的朋友。如果你只是想知道“储能配多大合适”,那这篇内容对你也有参考价值,但需要一定的运筹学基础,至少要知道线性规划和混合整数规划是什么。
1. 整体设计与复现思路拆解
1.1 这个题目到底在问什么
“参与调峰的储能系统配置方案及经济性分析”可以拆成三个层次:储能系统的容量配置、储能参与调峰的运行策略、以及配置方案的经济性评估。
这三件事是耦合的。你不能先随便定一个容量,再去算它怎么调峰,最后再判断是否划算。因为调峰效果取决于充放电策略,充放电策略会影响收益,收益反过来又决定容量配置是否合理。所以这个题目是一个典型的规划-运行一体化优化问题。
我在复现过程中发现,EI论文里通常不会把它写成一个大而全的单层优化,而会采用“两层迭代”或者“多场景运行模拟+全生命周期成本核算”的方式。我这次复现采用的是一个相对经典的单层混合整数线性规划(MILP)框架,把24小时(或96个时段)的功率调度作为运行层约束,把储能额定功率、额定容量作为规划层决策变量,一起丢进目标函数里求最小值,这样逻辑比较好讲清楚,代码也好调试。
1.2 EI论文复现的核心难点在哪里
很多人在复现论文时习惯先把代码跑起来,再去读公式。这个顺序在简单仿真类题目里问题不大,但在这个题目里是行不通的。因为储能配置问题最大的坑不是代码报错,而是模型写错了代码也照样能运行,只是结果怎么解释都不对。
就比如储能荷电状态(SOC)的递推公式,很多论文里写的是:
[ SOC(t) = SOC(t-1) + \eta_{ch} \cdot P_{ch}(t) \cdot \Delta t - \frac{P_{dis}(t) \cdot \Delta t}{\eta_{dis}} ]
这个公式本身不难,但一旦你的单位不统一,比如功率用MW、时间用小时、能量用MWh,看起来对,算出来SOC全飞了。更隐蔽的是,有些论文把充放电效率和损耗同时放在两个地方,导致能量不守恒。复现的时候一定要先还原模型,再写代码,否则后面排错会非常痛苦。
1.3 选择Matlab实现的原因
用Matlab做这个题目,最大的优势在于建模语言和求解器生态。Matlab自带的优化工具箱能解决一部分线性规划问题,但更推荐使用Yalmip工具箱,配合Gurobi或Cplex求解MILP问题。Yalmip的语法非常接近数学表达式,比如目标函数和约束条件几乎是一行行直接翻译过来的,这对复现论文特别友好。
如果不是Matlab,用Python的Pyomo也可以,但Yalmip在矩阵化约束和多时段建模方面写起来更直观,而且配套的文档和示例非常多,学术复现场景里用得更普遍。
1.4 复现的基本流程和技术路线
我这次复现的大致流程是:
- 梳理原论文的模型,把目标函数、约束条件、决策变量全部列出来。
- 造一套典型的负荷数据,可以是某地区典型日负荷曲线,也可以自己生成一组带峰谷特性的数据。
- 用Yalmip建模,先跑一个简化版本(24个时段、单台火电机组),验证逻辑正确。
- 扩展到96时段、多机组版本,加入机组组合、启停约束。
- 进行经济性分析,计算LCOS、NPV、IRR、投资回收期。
- 做敏感性分析,观察电池寿命、电价差、单位投资成本变化对最优配置的影响。
这套流程下来,基本上就把一篇EI论文从“口号”变成了“可以复盘的成果”。
2. 核心模型与公式推导
2.1 目标函数:最小化什么才合理
这个题目的目标函数一般有两种写法:一是最小化总成本,二是最大化净收益。从数学上讲两者经常等价,但在实际写代码时,我建议统一用“最小化总成本”的形式,因为储能系统的收益来源多且边界模糊,直接当成成本函数里的负项处理,表达起来更干净。
我采用的综合成本表达式为:
[ min \quad C_{total} = C_{inv} + C_{oper} - B_{peak} ]
其中,(C_{inv})为储能年度化投资成本,(C_{oper})为运行维护成本,(B_{peak})为参与调峰带来的收益,或者说由于削减峰值负荷而节省的购电成本/容量费用。
投资成本需要把一次性投资折算成等年值,公式是:
[ C_{inv} = \frac{C_{unit} \cdot E_{rated}}{CRF} ]
其中,(CRF)为资金回收系数,计算方式为:
[ CRF = \frac{r(1+r)^n}{(1+r)^n - 1} ]
(r)是贴现率,(n)是储能寿命年限。这个折算逻辑很多人会漏掉,如果不折算,直接把一次性成本丢进目标函数,那么运行层和规划层的量纲不匹配,最终配出来的容量会偏小或者偏大,完全失真。
2.2 决策变量体系和运行约束
决策变量分两类。
第一类是规划层变量:
- 储能额定功率 (P_{rated})
- 储能额定容量 (E_{rated})
- 储能安装数量 (N_{bat})(如果有模块化配置)
第二类是运行层变量:
- 每个时段的储能充放电功率 (P_{ch}(t))、(P_{dis}(t))
- 每个时段的SOC值 (SOC(t))
- 机组出力 (P_{g}(t))
- 机组启停状态 (u(t))(在机组组合扩展里需要)
约束条件一般包括五大类:
- 系统功率平衡约束:负荷加充电功率等于机组出力加放电功率。
- 常规机组出力上下限约束。
- 储能充放电功率约束:充放电不能同时进行,且不能超过额定功率。
- 储能容量约束和SOC动态约束,以及SOC上下限约束。
- 调峰效果约束,如削减后的净负荷峰谷差不超过某一阈值。
2.3 把“参与调峰”建模成约束
“参与调峰”这四个字要落地,关键就在怎样用一个数学表达式把调峰需求表达出来。我看到很多复现代码在这个地方处理得很模糊,最后只是让储能“跟着负荷曲线充放电”,完全没有调峰的概念。
这里我用了两个量化指标:
第一个是净负荷峰谷差率:
[ R_{peak_valley} = \frac{P_{net_max} - P_{net_min}}{P_{net_max}} ]
第二个是调峰率提升幅度。
在约束里可以写成:
[ P_{net_max} \leq P_{base_max} - \Delta P_{peak} ]
也可以把峰谷差限定在一个给定值以内。我习惯用百分比的表述方式,比如要求“配置储能后净负荷峰谷差率不超过40%”。
这样做的好处是,目标函数里不需要引入惩罚系数,也不会为了“看起来在调峰”而强行在目标函数里加一个没有经济含义的调峰项。约束的形式干净,求解器跑起来也更稳定。
2.4 为什么不能忽略充放电互斥约束
储能充放电互斥约束是一个看似细节、实则非常关键的约束。如果你不写:
[ P_{ch}(t) + P_{dis}(t) \leq P_{rated} ]
那么优化器为了满足功率平衡,可能在同一个时段既充电又放电,白白产生损耗,结果经济性还很“好”,这显然是错的。
更严格的做法是引入二进制变量 (b(t)),满足:
[ 0 \leq P_{ch}(t) \leq P_{rated} \cdot b(t) ] [ 0 \leq P_{dis}(t) \leq P_{rated} \cdot (1 - b(t)) ]
有些简单场景下,如果你确定电价信号在同一时段只有一个方向有利可图,可以不做互斥约束来提升求解速度。但稳妥起见,论文复现场景下还是老老实实加上二进制变量,这样结果才有说服力。
3. Matlab代码实现与数据设计
3.1 数据准备:怎么构造典型负荷曲线
复现这个题目,最难找的数据是负荷曲线。公开的电网负荷数据不完整,没有统一标准。我这次复现采用的是某示范工程公开文献中给出的典型日负荷曲线,然后做了归一化处理:把最大负荷标幺为100MW,最小负荷约62MW,峰谷差率大约38%,再用96个时段表示一天,每15分钟一个点。
数据生成部分我直接用Matlab数组写入:
% 基础参数 T = 96; % 时段数,96个15分钟点 base_load = [62, 63, 64, 65, 63, 62, 63, 64, 66, 68, ... 74, 80, 88, 95, 100, 99, 96, 92, 86, 82, ... % ... 这里实际项目中要补全96个值 76, 72, 68, 65, 63, 62]; % 实际复现时我用更平滑的方式生成,保证峰谷走势符合典型日规律 P_load = interp1(1:24, base_load, linspace(1, 24, T), 'spline'); P_load = P_load / max(P_load) * 100; % 标幺后乘100MW基准这样生成的曲线保留了典型日用电信心的“早高峰晚高峰双峰”特征,调度结果才有实际意义。
3.2 Yalmip建模核心代码
Yalmip建模的核心思路,是把上一节里的数学表达式“翻译”成代码。我给出简化版本的框架。
% 决策变量 P_ch = sdpvar(T, 1); % 充电功率 P_dis = sdpvar(T, 1); % 放电功率 SOC = sdpvar(T+1, 1); % 荷电状态,多一个时刻存初始值 b = binvar(T, 1); % 充放电互斥二进制变量 % 规划层决策变量 P_rated = sdpvar(1, 1); % 额定功率 E_rated = sdpvar(1, 1); % 额定容量 % 目标函数 C_inv = k_inv * E_rated / CRF; % 年度化投资成本 C_oper = sum(P_ch + P_dis) * c_om; % 简化运维成本 Benefit = sum(P_dis) * p_peak; % 调峰收益简化为削峰带来的容量收益 Objective = C_inv + C_oper - Benefit; % 约束条件 Constraints = []; % 功率平衡 for t = 1:T Constraints = [Constraints, P_load(t) + P_ch(t) == P_g(t) + P_dis(t)]; end % 储能充放电约束 Constraints = [Constraints, 0 <= P_ch <= P_rated * b]; Constraints = [Constraints, 0 <= P_dis <= P_rated * (1 - b)]; % SOC动态 eta_ch = 0.95; eta_dis = 0.95; dt = 0.25; % 15分钟换算成小时 for t = 1:T Constraints = [Constraints, SOC(t+1) == SOC(t) ... + eta_ch * P_ch(t) * dt - P_dis(t) * dt / eta_dis]; end % SOC上下限 Constraints = [Constraints, 0.1 * E_rated <= SOC(1:T) <= 0.9 * E_rated]; Constraints = [Constraints, SOC(1) == 0.2 * E_rated]; Constraints = [Constraints, SOC(T+1) == 0.2 * E_rated]; % 末端SOC回归 % 调峰效果约束 P_net = P_load + P_ch - P_dis; Constraints = [Constraints, max(P_net) - min(P_net) <= 0.4 * max(P_net)]; % 求解 options = sdpsettings('solver', 'gurobi', 'verbose', 0); optimize(Constraints, Objective, options);3.3 参数设置的经验分享
参数设置是整个复现里最容易翻车的地方。我踩过的坑主要有三个。
第一个坑是效率系数的位置。如果充电效率放在SOC递推里,那充电功率本身就不要乘效率;如果还乘了一次,就是双重折扣,能量凭空少了10%。很多新手代码跑完之后储能“永远充不满”,大概率是这个问题。
第二个坑是SOC初始值设定。初始SOC设定为额定容量的20%,末端也强制回到20%。如果设成0,前面还好,后面为了满足末端SOC约束,优化器会强迫放电,导致最终配置方案严重偏向大容量,结果失真。
第三个坑是调峰约束的松紧程度。如果直接要求峰谷差率降到20%以下,在当前负荷曲线和储能容量范围内可能根本无解。我建议先用一个较宽松的约束,比如40%或者45%,跑通之后再逐步收紧,观察配置结果的变化趋势,再反推论文中“最优配置”是怎么来的。
3.4 典型运行结果长什么样
我复现得到的一组典型结果是这样的:
在96个时段、最大负荷100MW、峰谷差率38%的场景下,最优配置为储能额定功率22MW、额定容量88MWh,SOC运行范围在10%-90%之间。配置储能后,净负荷峰值从100MW降到了91MW,峰谷差率降到28.5%。电池的充放电运行曲线呈现出典型的“两充两放”特征:凌晨低谷充电、早高峰放电、中午光伏大发时充电或空闲、晚高峰再放电。
这里要注意的是,“两充两放”不是我们强行加给模型的,而是峰谷电价和净负荷曲线相互作用下自然形成的优化结果。如果在论文复现中你的储能日循环次数非常高,比如一天充放五六次,那大概率是电价信号设置出了问题,储能变成了一台“高频套利机”,这在调峰场景里不现实。
4. 经济性分析的关键指标与计算逻辑
4.1 经济性分析的基本框架
储能经济性分析不能只看初始投资和每天赚多少电费。一个完整的分析框架至少要包含全生命周期的视角和多个收益来源。
储能参与调峰的收益来源主要有三个:
- 峰谷套利收益:低电价时充电、高电价时放电,获取电价差收益。
- 容量费用节省收益:通过削减最大需量,降低两部制电价中的基本电费。
- 延缓输配电投资收益:如果调峰能延缓新建机组或线路的投产,这部分也算作系统收益。
在代码复现中,前两个收益容易建模,第三个需要额外假设,一般论文里都只是定性讨论,我也没有把它计入定量分析,避免主观假设太多导致结果被质疑。
4.2 几个不能回避的经济指标
经济性分析的输出指标里,我重点计算了四个:平准化储能成本(LCOS)、净现值(NPV)、内部收益率(IRR)、动态投资回收期(DPBT)。
LCOS的计算方式是:
[ LCOS = \frac{全生命周期总成本}{总放电量} ]
它考虑了投资成本、运维成本、充电成本、残值等,是一个“储能发一度电需要花多少钱”的指标。
NPV的计算公式:
[ NPV = \sum_{t=1}^{n} \frac{CF_t}{(1+r)^t} - I_0 ]
IRR则令NPV等于0反解出(r),动态投资回收期也是基于累计净现金流折现后转为正值的时间。
我强烈建议在复现时把这三个指标全部算出来,而不是只给一个NPV。因为EI论文的评审专家和读者非常喜欢看敏感性分析,如果你手里有IRR和回收期,做敏感性分析时说服力会强很多。
4.3 经济性分析结果示例
我的复现项目里,结合前述配置结果,假设系统寿命20年、贴现率8%、单位投资成本1500元/kWh、运维成本30元/kW·年、峰谷电价差0.65元/kWh,计算得到的指标如下表:
| 指标 | 数值 |
|---|---|
| 额定功率/容量 | 22MW / 88MWh |
| LCOS | 0.38元/kWh |
| 全生命周期净现值NPV | 约1.6亿元 |
| 内部收益率IRR | 11.5% |
| 动态投资回收期 | 7.8年 |
这个结果在经济性上算是基本可行的,IRR超过8%的贴现率,说明项目具有投资吸引力。但注意,不同地区的峰谷电价差、充放电策略、年循环次数差异很大,这个结果不能直接套用,一定得用本地参数重算。
4.4 配置方案对比与敏感性分析怎么做
做完优化求解后,我习惯再做一组“方案对比表”,从优化结果周围取几个不同的配置点,分别计算调峰效果和经济性。比如这样:
| 配置方案 | 额定功率 | 额定容量 | 峰谷差率 | IRR | 回收期 |
|---|---|---|---|---|---|
| 方案A | 15MW | 60MWh | 32.0% | 9.2% | 9.5年 |
| 方案B(最优) | 22MW | 88MWh | 28.5% | 11.5% | 7.8年 |
| 方案C | 30MW | 120MWh | 26.2% | 10.1% | 8.9年 |
你会发现,从A到B,容量增加,峰谷差率下降,IRR上升;但从B到C,容量继续增加,峰谷差率只降了2.3个百分点,IRR反而下降了。这说明存在一个“最优经济规模”,超过这个规模后边际收益递减,储能就不是投资而是负担了。这种分析在论文里非常讨喜,能直接支撑结论。
另外,敏感性分析我一般对三个变量做三组扫描:单位投资成本、峰谷电价差、储能循环寿命。最关键的结论是:峰谷电价差对IRR的影响呈线性甚至超线性,而循环寿命对IRR的影响比较温和,这提醒我们在配置方案里要把“可用天数”和“日循环次数”做保守估计。
5. 常见问题与排错经验
5.1 求解器报错“无可行解”
这是最让人崩溃的问题。大概率原因有三个:调峰约束设太紧、SOC末端约束太苛刻、充放电功率与额定功率关系写错。
排查方法很简单:先把调峰约束去掉,看能不能求解成功。如果能,说明是约束过强。再把SOC末端约束从“等于0.2”改成“大于等于0.15”,或者直接去掉,让模型自由决定末端SOC。如果这样还是无解,就要重点检查功率平衡方程左右两端是否对齐,特别是负荷、充电、放电、机组出力的正负号。
我在复现中遇到过一个问题:功率平衡式里我写的是“负荷+充电=机组出力+放电”,但某个版本的代码里我把充电、放电都放到了右侧,结果约束从等式变不等式,整体就变得非常奇怪。排查了很久才发现是转移项时正负号没处理干净。
5.2 计算时间长到想放弃
96时段、多机组、带二进制变量的MILP模型,求解时间可能长达几十分钟。如果你的模型跑了一个小时还没结果,问题往往出在约束写法。
我推荐先做这两个优化:
第一,在SOC递推公式里不要把SOC写成连续变量带(T+1)个节点,可以尝试写成一个全时段的矩阵运算,减少循环次数。第二,把不参与决策的常数项移出目标函数,不要让它造成压测压力。
另外一个很实用的小技巧是:先用24时段模型跑通逻辑,再扩展96时段。24时段的MILP求解时间通常在几十秒以内,等确认模型没问题了再跑高精度场景,能节约一晚上调试时间。
5.3 SOC曲线不对劲:充放电循环次数过高
如果你发现SOC曲线呈现锯齿状,频繁在上下限之间撞墙,那基本可以肯定是电价信号或充放电互斥约束的问题。
一种情况是电价峰值中间有极小的凹陷,导致优化器利用这个凹陷进行“微充电”,随后继续放电,造成不必要的循环。这种情况下,最简单的处理是在运行层对相邻时段的充放电状态变化次数做限制,或者引入最小充放电持续时间约束。虽然会让模型复杂不少,但输出的结果明显更符合实际操作规律。
另一种情况是充放电互斥约束漏写,导致同时充放电,SOC看起来变化不大但功率数据诡异。这种问题只要SDPVAR变量分开定义,再写互斥二进制约束就能解决。
5.4 目标函数结果不合理:投资成本正负号错了
我调试时有一版代码跑出来储能配置高达150MW/600MWh,明显偏离实际。检查后发现是目标函数里投资成本项被写成了负号,相当于系统安装储能越多,成本越低,优化器当然想法设法把容量推到上限。
这种问题在代码层面只是一个小符号,但如果没及时发现,后面算出的经济性指标会完全失去意义。所以拿到结果后,第一步永远是对量级做“常识性验证”。储能容量和当地峰谷差是不是同一个数量级,IRR是否在合理区间,日循环次数是否不超过两次,这些一眼就能看出来的基本逻辑,能在你浪费大量时间之前挡住大部分模型错误。
5.5 SOC初始值和末端值带来的偏差
很多复现版本直接设初始SOC = 0,末端SOC不加约束,结果储能系统在一整天里一直在放电,看起来像一台独走电源。实际上这是模型偷懒,在没有任何剩余价值激励的情况下,优化器倾向于在最后时段放掉所有电量,因为电能放在电池里没有任何收益。
解决办法是强制末端SOC等于初始SOC,或者给末端SOC剩余电量设置一个价值系数。我最终采用前者,简单直接,且符合储能系统日循环运行的实际习惯。如果你有多日连续运行的场景,那应当采用滚动时域调度或者给SOC一个终止惩罚项,否则结果也会有边界失真。
这段代码还能往哪里扩展
复现完这个题目之后,我心里最大的感受是:这个模型的生命力远比题目本身要强。储能参与调峰只是其中一个应用场景,只要你把目标函数里的收益项从“峰谷套利”换成“新能源弃电消纳收益”,或者把约束条件里的负荷曲线换成某个风电场或光伏电站的净出力序列,整个模型就能迁移到新能源配储、多能互补、需求响应等场景里。
如果再加入火电机组的启停成本和爬坡约束,还能扩展到目前很热门的“源网荷储一体化”分析。我在这个项目里试了一下,效果相当不错,求解时间虽然翻了几倍,但结果的信息量完全不一样。
如果你正在复现类似的论文,我最后的建议是:不要一上来就追求完全复现,先把最核心的单目标、单场景、24时段模型跑通,再去逐步加入复杂度。模型的价值在于能够讲清楚道理,代码只是表达道理的工具。能复现出“为什么是这个配置”“为什么经济性可行”的完整逻辑链,这篇论文你才算真正读懂了。