ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

储能参与调峰的配置方案与经济性分析复现思路

储能参与调峰的配置方案与经济性分析复现思路

做储能配置与经济性分析这个方向,最怕的不是建模算不出来,而是拿到一篇论文标题后发现根本没给代码。标题写着“EI复现”,点进去一看,正文是空的,关键词和摘要也全空。这种情况我碰到太多次了,尤其是“参与调峰的储能系统配置方案及经济性分析”这类题目——它本质上是一个典型的双层优化问题:外层决定储能配多大容量、多大功率,内层决定储能怎么充放电,最后还要把全生命周期的经济账算清楚。这篇我就把整个复现思路拆开讲:从调峰建模的数学逻辑,到容量功率的参数设计,再到经济性口径、Matlab求解框架,最后是大家最容易踩的五个坑。

1. 先把调峰问题翻译成数学语言:储能参与调峰的建模逻辑

1.1 调峰的本质是削减峰谷差

电网的负荷曲线一天之内变化很大。早上八九点到中午、傍晚六七点往往是高峰,后半夜是低谷。发电侧为了满足峰段需求,得随时准备开更多的机组;而低谷时段机组又不能完全停机,否则再启动成本很高。储能参与调峰,本质上就是“削峰填谷”:负荷低谷时给储能充电,负荷高峰时储能放电,把原来波峰波谷明显的净负荷曲线,尽量拉平。

这里必须先分清概念:储能调峰和储能套利不是一回事。调峰的收益来源是“让电网少调用高成本机组”或者“削峰填谷后的市场补偿”,套利的收益来源是峰谷电价差。实际工程项目里两者经常同时发生,但建模目标不一样。像这个标题里的“参与调峰”,更常见的目标是:在满足削峰需求的前提下,让储能配置的全生命周期总成本最小,或者让综合收益最大。

生活里可以这么理解:你办公室里中午用电高峰空调全开,配了一个大充电宝在凌晨自动充满,午后放电撑住电压,月底看电费单省了一笔。这个充电宝该买多大容量、功率,就是你每天充放电策略叠加出来的结果——但要买多大,得先把“每天怎么充放”定下来,这就是双层优化的雏形。

1.2 目标函数和约束:一个完整的调峰优化模型怎么搭

调峰优化模型的目标函数可以写成最小化削峰后的净负荷峰谷差:

[ \min ; \max_{t} \left[ P_{load}(t) + P_{ch}(t) - P_{dis}(t) \right] - \min_{t} \left[ P_{load}(t) + P_{ch}(t) - P_{dis}(t) \right] ]

其中 (P_{load}(t)) 是预测负荷,(P_{ch}(t)) 是充电功率,(P_{dis}(t)) 是放电功率。净负荷就是原始负荷叠加储能动作后的曲线,峰谷差越小,调峰效果越好。

也可以写成总成本最小化的形式:

[ \min ; C_{inv} + C_{om} + C_{buy} - R_{peak_shaving} ]

也就是“投资成本 + 运维成本 + 购电成本 - 调峰收益”最小,这样配置方案和经济性分析就自然地结合在了一起。

约束条件里有几个是必须写的,一个都不能省:

  • 功率平衡:每个时段的净负荷等于原始负荷加储能充电减储能放电,这是最底层的物理约束;
  • SOC递推:(SOC(t+1) = SOC(t) + (\eta_c P_{ch}(t) - \frac{P_{dis}(t)}{\eta_d})\Delta t / E),充电效率系数 (\eta_c) 和放电效率系数 (\eta_d) 都要显式建进去,不然储能好像“凭空消失能量”;
  • 充放电功率限幅:(0 \le P_{ch}(t) \le P_{rate}),(0 \le P_{dis}(t) \le P_{rate});
  • 充放电互斥:同一时段不能既充又放,引入0-1变量;
  • SOC边界与初末值:SOC不能越界,一个调度周期结束时的SOC一般要求回到初始水平,否则会白白“借”能量。

如果你在论文里看到的模型没有SOC递推公式,或者说充放电互斥约束是空的,那大概率是为了凑版面简化掉的,复现时不要照着抄,要补回来。因为SOC递推直接决定了储能的容量能不能真正支撑你设计的充放电策略,漏掉它等于整个配置方案是空算。

2. 容量功率不是拍脑袋:配置参数设计与初值求取

2.1 从负荷曲线出发反推储能规模和功率

很多人拿到题目就先找优化算法,其实第一步应该是看数据。储能配置方案的核心决策变量是两个:额定功率 (P_{rate}) 和额定容量 (E_{rate})。它们不能盲目枚举,需要有初值。

经验做法是先画负荷曲线,看峰谷差 (\Delta P = P_{load,max} - P_{load,min})。储能功率初值一般取峰谷差的 15% 到 30%,然后容量按“可持续参与调峰的时长”来定。市场上常见的调峰储能是 2 小时、4 小时、6 小时配置。比如某地区典型日负荷曲线峰谷差是 20MW,那你先取 (P_0 = 4\text{MW} \sim 6\text{MW}),容量 (E_0 = 4\text{MW} \times 4\text{h} = 16\text{MWh}) 或者 6 小时版本的 24MWh,作为枚举搜索的中心。

为什么要这样反推?因为配置方案它不是一个孤立变量,是和日充放电策略强耦合的。你让一个 1MWh 的小电池去削 10MW 的峰,一天充放电再多次,物理上也削不动。反过来,你配一个 100MWh 的巨型储能,却只削 1MW 的峰,利用率太低,经济性必挂。所以外层枚举配置,内层跑调度策略,两层迭代才能得到靠谱的方案。

2.2 经济性参数的工程取值区间

这个题目最关键的是“经济性分析”,而所有经济性分析都建立在参数假设上。参数不同,结论天差地别。表里我给出一组我在复现类似题目时常用的工程估值区间,供参考:

参数取值区间说明
储能系统单位造价1200–1800 元/kWh含电池、PCS、BMS、集装箱、安装,不含土建
PCS功率单元350–600 元/kW双向变流器,与容量分开计
运维费率初始投资的2%–5%/年含人工、备件、定期巡检
调峰补偿或峰谷价差0.5–1.5 元/kWh视当地市场规则而定
贴现率6%–8%一般取8%,偏保守
计算期10–15 年储能设计寿命常见10年,可延保至15年

注意,上面这些值是工程可研和行业报价的常见区间,不是哪个特定项目的数据。你在不同论文里看到的参数可能差几倍,那不是别人算错,而是假设条件不同。复现时务必先把自己手里的场景参数对齐,再去调整配置变量。

还有一个很容易忽略的:电池衰减。锂电池不是“第一年多大,以后永远多大”,到寿命末期容量可能衰减到初始的80%左右。严肃一点的经济性分析会按寿命期内逐年容量衰减曲线做修正,简单一点的做法是取全周期平均可用容量折算。如果论文里完全没提衰减,那经济性结果必然是偏乐观的。

3. 算经济账的三层口径:从静态回收期到全生命周期NPV

3.1 静态回收期为什么只能用来“初筛”

静态回收期的公式很简单:投资总额除以年净收益。假设一套 5MW/20MWh 的储能系统,总投资 3000 万元,年净收益 400 万元,静态回收期就是 7.5 年。这个数字看上去很直观,但它没有考虑资金时间价值,也没有考虑项目后期运维费用上涨、电池容量衰减导致的收益递减。

实际工程里,静态回收期只适合用来快速比较几个候选方案。两个项目一个回收期 6 年、一个 7.5 年,大概率 6 年那个更值得优先做。但如果你要在 7 年和 8 年之间做精确决策,必须上动态经济性评价。

3.2 净现值计算里最容易错的三处地方

动态经济性分析的标尺是净现值(NPV),公式是:

[ NPV = \sum_{t=0}^{T} \frac{CF_t}{(1+r)^t} ]

其中 (CF_t) 是第 t 年的净现金流,(r) 是贴现率,(T) 是计算周期。现金流以为正收入、投资为负支出,NPV > 0 说明项目可行。

这里有两个极容易出错的地方。第一是“计算期”和“寿命期”搞混。储能使用 10 年就衰减到报废,你非要按 15 年计算,后 5 年的收益假设根本不成立。第二是对“年净收益”的构成理解不统一。参与调峰的收益可能包含调峰补偿和峰谷套利两部分,但有些复现代码把储能充电时的购电成本漏算了,相当于只算卖电收入不算买电支出,结果自然漂亮得吓人。

我按 5MW/20MWh、总投资 3000 万元、每天净收益 1.2 万元、年运行 330 天、年运维费 90 万元、贴现率 8%、寿命 10 年算了一笔账:

  • 年收益:(1.2 \times 330 = 396) 万元
  • 年运维费:(3000 \times 3% = 90) 万元
  • 年净现金流:(396 - 90 = 306) 万元
  • 10 年年金现值系数:((1 - 1.08^{-10}) / 0.08 \approx 6.71)
  • NPV:(306 \times 6.71 - 3000 \approx -946) 万元

这个结果是负的,说明按这笔参数,项目收益率达不到 8%,经济性不过关。这不是配置错了,而是这套参数下储能造价还是偏高、或收益被低估。复现经济性分析时,得到负NPV是很正常的,重要的是能解释为什么负、哪个参数主导了结论。

3.3 残值与税收:可别小看最后那一笔

经济性分析的最后一个口径是残值。寿命期末,电池虽然衰减了,但还有回收价值——正极材料回收、结构件回收,一般取初始投资的 3% 到 8%。不能说“寿命到了就全归零”,也不能夸大残值,因为回收成本会吃掉相当大一部分。另外,如果项目进入电力市场参与交易,还要考虑增值税、所得税,这些在粗略的研究中可以简化,但在可研级项目中必须列明。复现论文时,先看原文有没有残值和税费项,再看它用的是税前还是税后现金流,这样你算出来的内部收益率才能跟论文对得上。

4. Matlab落地:求解器选型、程序结构与核心代码骨架

4.1 外层配置搜索和内层优化调度的模块划分

Matlab代码实现该怎么组织?我建议主程序分三段:

  1. 参数初始化段:读入负荷曲线、电价或调峰补偿曲线、储能技术参数(效率、SOC上下限)、经济性参数(造价、贴现率、寿命);
  2. 配置与调度迭代段:外层遍历候选功率容量组合,内层构建目标函数和约束调用求解器,返回每个配置下的调度结果;
  3. 经济性后处理段:把每个配置的充放电曲线换算成年度收益、投资成本、NPV、回收期,输出对比表。

内层调度模型建议用标准混合整数线性规划。为什么必须用MILP而不是启发式算法?因为充放电互斥约束引入了0-1变量,同时目标函数里含有max/min非线性项,但可以用辅助变量线性化。线性化之后的模型,商业求解器能保证收敛到全局最优。启发式算法只能给“一个可行解”,无法证明它是最优配置。

4.2 变量维度估算与求解器适配

假设全天 24 个时段,储能候选 20 组容量、20 组功率,内层单次求解的决策变量包括:

  • 24 个时段的充电功率 + 24 个放电功率,共 48 个连续变量;
  • 24 个充电状态 + 24 个放电状态,共 48 个0-1变量;
  • 加SOC递推所需的25个连续变量。

单次求解量级在100多个变量,对任何开源或商业求解器都是小意思。外层20×20=400次内层求解,每次不到1秒,整个程序跑完几分钟内搞定。所以不要一上来就套遗传算法,先把精确模型跑通再谈加速。

如果负荷数据是 8760 小时逐时曲线,直接建模型变量会到上万个,轻则求解变慢,重则内存爆掉。这时才需要做典型日选择或者场景削减,一般取 4 个季节典型日、每个典型日 24 时段。

4.3 可直接参考的Matlab代码骨架

下面的代码是调峰优化模型的核心骨架,重点是SOC约束和充放电互斥约束的写法:

% 参数 T = 24; % 时段数 dt = 1; % 时间间隔,小时 P_load = [...] % 预测负荷曲线,1xT E_rated = 20; % 储能额定容量,MWh P_rated = 5; % 储能额定功率,MW eta_c = 0.95; % 充电效率 eta_d = 0.95; % 放电效率 soc_min = 0.1; soc_max = 0.9; % SOC边界 soc_0 = 0.2; % 初始SOC,占总容量比例 % 变量 P_ch = sdpvar(1, T); % 充电功率 P_dis = sdpvar(1, T); % 放电功率 u_ch = binvar(1, T); % 充电状态 u_dis = binvar(1, T); % 放电状态 soc = sdpvar(1, T+1); % SOC,含初值 % 目标:最小化净负荷峰谷差 P_net = P_load + P_ch - P_dis; obj = max(P_net) - min(P_net); % 约束 cons = []; % SOC递推 for t = 1:T cons = [cons, soc(t+1) == soc(t) + ... (eta_c * P_ch(t) - P_dis(t) / eta_d) * dt / E_rated]; end % SOC边界与初值 cons = [cons, soc(1) == soc_0]; cons = [cons, soc(T+1) == soc_0]; % 周期末回到初值 cons = [cons, soc_min <= soc <= soc_max]; % 功率限幅 cons = [cons, 0 <= P_ch <= P_rated]; cons = [cons, 0 <= P_dis <= P_rated]; % 充放电互斥 cons = [cons, P_ch <= 1e6 * u_ch]; % 大M约束 cons = [cons, P_dis <= 1e6 * u_dis]; cons = [cons, u_ch + u_dis <= 1]; % 求解 ops = sdpsettings('solver', 'gurobi', 'verbose', 0); optimize(cons, obj, ops);

注意目标函数里的max(P_net) - min(P_net)严格来说不是线性表达式,实际写成MILP时要用辅助变量替换:引入 (M_{max})、(M_{min}),让 (M_{max} \ge P_{net}(t))、(M_{min} \le P_{net}(t)),目标改成 (M_{max} - M_{min})。上面骨架里用sdpvar的max/min写法在建模层会自动转化,但如果你手写约束流派,一定要记得线性化这一步。

还要补充两点:大M的取值不是越大越好。你写1e6只是示意,实际用P_rated + P_load_max + 1就够,太大容易造成数值病态。另外,SOC的单位要统一,有的论文用百分比,有的用MWh,soc如果定义成0到1的比例,那么在递推公式里要除以 (E_{rated});如果你定义成MWh,就不要除。单位不统一是复现结果对不上的头号原因。

5. 复现过程中最容易翻车的五个问题

5.1 SOC初值决定结果“假优”还是“真优”

这个坑我踩过不止一次。如果SOC初始值设成0.2,周期末不约束回到0.2,那么储能相当于多了一块免费充入的能量,调度结果必然虚高。解决办法很简单:首尾SOC一致。但在某个具体项目的复现里,原文写的是“调度周期内SOC保持合理范围”,并没有明确首尾一致,这时你就要根据原文的投资逻辑去判断——如果原文目标函数只算调峰收益不算储能充放电的购电成本,那它大概率没有考虑能量守恒,结果参考价值有限。复现时按首尾一致来处理,才是工程上能落地的方案。

5.2 时间分辨率太粗导致“假削峰”

24点曲线是一小时一个点,对于慢变化的调峰场景勉强够用,但要小心:如果负荷曲线在某小时内有一个短时尖峰,一小时平均后尖峰被抹平了,你的储能配置就会偏小。反过来,如果负荷曲线原始数据本身就是5分钟级,而你强行聚合成1小时,也会把短时负荷峰值抹掉。复现时我建议先看数据源的时间粒度,再决定优化模型的分辨率。如果两者不匹配,至少要做一个敏感性分析:用24点算一版、用96点算一版,对比配置结果的变化幅度。

5.3 双层迭代的一致性校验

外层枚举配置,内层求解调度,听起来简单,实际运行时经常出现“内层返回的是上一轮配置的缓存结果”。原因很隐蔽:在主循环里用了eval或assign没有清空变量,导致不同配置下用的还是旧的目标函数系数。排查方法是在每次内层求解后打印一组已知最优解的判断变量,例如某个时段的放电功率,看看是否随配置变化而合理变化。如果一排功率完全不变,那一定是变量覆盖出了bug。

5.4 峰谷时段定义直接改变收益

同一个负荷曲线,“高峰期”定义成8点到12点还是18点到22点,对应的储能放电时段完全不同。有的论文用的是固定分时电价峰谷时段,有的用的是基于净负荷曲线动态划分峰谷段。复现时如果只看“收益”不看“时段划分逻辑”,很容易得到数字上对不上的结果。正确的做法是先从论文的文字描述里提取出时段划分规则,再在代码中把这条规则显式写成参数配置,而不是靠读负荷曲线猜。

5.5 论文结果核对的顺序问题

拿到一篇论文,不要急着跑图表,先按下面三步核对:

  1. 单位核对:kW、MW、kWh、MWh、万元、亿元,差1000倍很常见;
  2. 参数核对:把论文正文和附录里出现的所有经济性参数列成一张表,逐个与代码里的常量比对;
  3. 场景核对:论文用的负荷曲线是哪类行业负荷、哪个季节、抽样天数,决定了它结果不可直接迁移到你自己的场景。

走完这三步再跑程序,你会发现很多“复现不出来”根本不是代码问题,而是参数抄错了。

复现这类“EI题目”做多了,我的体感是:真正卡人的地方从来不在算法本身,而在参数口径和模型细节。你拿到一个储能配置题目,先花一个小时把原文的场景参数表格化、把目标函数和约束条件按自己习惯重写一遍,再动手写Matlab,后面会顺畅得多。代码只是把模型“翻译”成机器能算的形式,模型本身如果理解不透,换什么求解器都白搭。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进