调峰成本这个词,做电力系统规划或者电力市场设计的人应该都不陌生。前几年我们总说“新能源大发时段调峰困难”,但真要追问一句:调峰一次到底花了多少钱?这笔钱又应该算在谁头上?很多人说不到两句就模糊了。我手头这套Matlab实现,就是专门解决这两个问题的:先在高比例可再生能源电力系统下做时序生产模拟,把火电深度调峰、启停调峰、弃风弃光折算成可量化的调峰成本;再基于“谁引发、谁受益、谁承担”的分摊原则,把总成本拆分到风电、光伏、负荷等可归责主体上。整套代码从数据输入、净负荷计算、机组成本建模到调峰成本归集和分摊输出,是一个可以跑通的闭环。它适合电网规划工程师、电力市场研究人员、做风光消纳分析的同行,以及所有被“调峰成本说不清”折磨过的研究生和论文党。
1. 模型整体思路:先定边界,再谈分摊
1.1 调峰成本到底包含哪些科目
很多人一听“调峰成本”就默认等于煤耗增量,这个理解太窄了。实际调峰动作包含三种形态:常规负荷跟踪、深度调峰、启停调峰。系统正常情况下,机组顺着负荷曲线上调下调属于常规运行,这部分成本是发电成本的一部分,不应该扔进“调峰成本”里。真正需要单独算的,是为消纳可再生能源而被迫增加的额外支出。
我把调峰成本拆成了四个科目:
- 深度调峰附加成本:机组出力压低到常规技术出力下限以下时,锅炉稳燃需要投油或等离子助燃,燃烧效率明显下降,单位煤耗上升,这部分是深度调峰的主要成本。
- 机组寿命损耗成本:频繁变负荷和深度压负荷会让转子等热力部件出现低周疲劳损耗,这是慢变量但真实存在,工程上会折算成每兆瓦时出力的寿命损耗费。
- 启停调峰成本:启动一次需要燃料、厂用电、设备维护和寿命折损,停机再启动还会导致机组状态转换费用,应该单独统计。
- 弃风弃光成本:系统调峰能力不够被迫切除可再生能源出力时,少发电量价值也是调峰不充分的成本表现。
边界一定要在建模前固定。否则同样的算例,有人把煤耗增量全计入,有人只统计投油成本,结果能差出几倍。模型里我采用“边际增量”口径,即只计算相对常规基荷运行的额外支出,把常规燃料成本剥离开。
1.2 为什么必须做时序仿真而不是典型日简单叠加
调峰成本不是静态函数,它和机组状态、爬坡、启停次数、开停机时间深度绑定。比如一台600兆瓦机组从300兆瓦爬坡到600兆瓦,和从200兆瓦直接停机再启动,成本结构完全不同。如果只拿典型日的峰谷差做估算,忽略跨时段的状态转移,结果会严重失真。
高比例可再生能源系统里,净负荷曲线(负荷减去风光出力)才是真正决定调峰需求的曲线。风光大发时净负荷被压低,负荷高峰期净负荷又可能突然上扬,净负荷的波动幅度往往比原始负荷曲线更陡。这种波动必须用逐时或更细分辨率的时间序列去捕捉。
所以模型采用了时序生产模拟的思路:建立一个包含负荷功率、风电出力、光伏出力的时间序列,然后逐时段做机组出力分配,记录每台机组在每个时段的开停机状态、出力水平和成本发生量。这样统计出的调峰成本不仅可以汇总,还能回到每个时段看成本到底发生在几点,方便后续做责任溯源。
1.3 分摊原则:谁引发,谁承担
成本算清楚之后,怎么分摊更考验模型设计。业界对分摊对象有几种观点:有人主张调峰成本应该由可再生能源承担,因为它们出力波动是调峰需求的来源;也有人主张应该由所有用户承担,因为调峰的最终目的是保证供电;还有人认为电网公司作为运行者应该兜底。
如果从“因果责任”看,分摊对象应该锁定在引发净负荷波动的主体上。负荷曲线的峰谷波动属于自然需求,不是可再生能源造成的;而风电、光伏出力偏离常规可控电源特性,才是高比例系统里调峰问题的放大因素。因此模型采用“场景差量+波动贡献”的组合分摊法:
- 先做一个不含风电、光伏的基准场景,得到系统基准调峰成本;
- 再做实际含风电、光伏的场景,得到实际调峰成本;
- 两次差量作为可再生能源引发的增量调峰成本;
- 把差量按每个新能源场站的出力下滑量、反调峰程度分配到具体电站。
分摊的原则并不唯一,但口径必须在一个模型里保持一致。后面代码部分会展示这种组合分摊法的矩阵实现。
2. 核心数学模型与成本函数
2.1 机组深度调峰的分段成本函数
火电深度调峰成本是整套模型里最需要细致处理的部分。同一台机组在不同负荷区间的成本特性完全不同,我按负荷率分了三个区间:
- 正常调峰区间:负荷率小于等于100%、大于等于常规最小技术出力对应负荷率时,煤耗按热力试验曲线计算;
- 深度调峰区间:负荷率继续下探至深度调峰下限,需要投油或等离子助燃,附加成本包含燃油消耗、效率下降增耗和设备寿命损耗;
- 极深度调峰/停机区间:低于深度调峰下限时不允许持续运行,只能选择停机。
工程上可以写出简化分段表达式:
机组i在时段t出力为P_{i,t},额定容量为P_{i,max},常规最小技术出力为P_{i,con},深度调峰最小出力为P_{i,min}。若P_{i,t}在P_{i,con}和P_{i,max}之间,成本函数为:
[C_{i,t}^{fuel}=a_i + b_i P_{i,t} + c_i P_{i,t}^2]
若P_{i,t}在P_{i,min}和P_{i,con}之间,额外增加深度调峰成本项:
[C_{i,t}^{deep}=\lambda_i (P_{i,con}-P_{i,t}) + \mu_i \cdot \mathbb{I}[P_{i,t}<P_{i,con}]]
其中(\lambda_i)是深度调峰单位压出力增量成本,(\mu_i)是投油固定成本,(\mathbb{I}[\cdot])为0-1指示变量。寿命损耗可以并入(\lambda_i),也可以单独列成与压出力幅度成正比的项。
这里有个关键点:公式里的(\lambda_i)不是拍脑袋定的,需要通过机组热力性能试验数据拟合。拿不到实测数据时,可以参考同容量机组文献值,并在模型里做成可调参数,方便后期替换。
2.2 启停调峰成本怎么计入状态转移
启停调峰的成本与机组是否启动、是否停机直接相关,属于跨时段状态量。数学模型里需要引入0-1变量表示机组开停机状态:
[u_{i,t}=1]表示机组i在时段t处于开机状态,否则为0。
启动事件定义为状态从0变到1,停机事件定义为从1变到0。因此启停成本可以写成:
[C_{i,t}^{su}=S_i^{su} \cdot \max(0, u_{i,t}-u_{i,t-1})]
[C_{i,t}^{sd}=S_i^{sd} \cdot \max(0, u_{i,t-1}-u_{i,t})]
其中(S_i^{su})和(S_i^{sd})分别是机组i的单次启动费用和单次停机费用。注意启动费用和停机费用并不对称,冷态启动可能是热态启动的好几倍,如果模型精度要求更高,可以进一步区分热态、温态、冷态启动的差异。
在Matlab中,如果使用Yalmip建模,启动和停机变量可以显式定义:
u = binvar(n, T, 'full'); % 机组开停机状态 su = binvar(n, T, 'full'); % 启动事件 sd = binvar(n, T, 'full'); % 停机事件 % 状态转移约束 Constraints = [Constraints, su(:,t) >= u(:,t) - u(:,t-1)]; Constraints = [Constraints, sd(:,t) >= u(:,t-1) - u(:,t)];2.3 弃电成本与调峰成本怎么避免重复计算
弃风弃光成本在模型里很容易和调峰成本重复计算。如果已经计算了火电因为压出力产生的深度调峰成本,再把“火电多发替代风光少发”的燃料成本差异也算进去,那就重复了。
我的处理方法是严格区分电量口径:
- 弃电成本 = 弃电量 × 单位损失价值;
- 调峰成本 = 常规机组参与调峰动作的额外支出;
- 两者互斥计入,最终总成本 = 调峰成本 + 弃电成本 + 失负荷惩罚。
单位损失价值可以选参考上网电价,也可以考虑绿色电力环境价值。参考电价口径最直观,环境价值口径更能体现“消纳可再生能源的社会收益”。实际建模时我建议把这两个口径都做进去,通过参数切换观察结果差异,这也是灵敏度分析的一部分。
3. Matlab代码实现:从数据到成本台账
3.1 数据组织和主程序分层
代码工程化改造的第一步是分层。官方一点的叫法叫模块化,说白了就是让每个文件只干一件事,后期改参数不用翻遍整个脚本。
我实现的文件结构大致是:
main_dispatch.m % 主程序:调度计算与成本汇总 load_input_data.m % 读取负荷、风电、光伏时间序列 build_units.m % 构建火电机组参数结构数组 sequential_dispatch.m % 简化时序调度函数 milp_dispatch_yalmip.m % 混合整数优化调度函数 cost_settlement.m % 调峰成本归集与统计 allocate_cost.m % 调峰成本分摊 plot_results.m % 结果可视化机组参数用结构数组存储,比散落的变量好管理得多:
units(1).name = 'G1'; units(1).Pmax = 600; units(1).Pmin = 180; units(1).Pcon = 240; units(1).a = 20; % 空载成本系数 units(1).b = 0.3; % 线性煤耗系数 units(1).c = 0.0002; % 二次煤耗系数 units(1).lambda = 0.05; % 深度调峰单位压出力成本 units(1).su_cost = 8e4; % 启动费用 units(1).sd_cost = 2e4; % 停机费用负荷和可再生能源数据我建议统一放到表格里,第一列时间戳,后面依次是负荷、风电功率、光伏功率。数据读取后直接转成Matlab datetime数组,后续画图非常方便。
3.2 时序生产模拟的两种实现路线
时序调度有两种实现路线,按用途不同选择:
- 路线A:优先顺序调度法。按机组边际成本从小到大排队,逐时分配净负荷。逻辑简单、计算快,适合教学和快速方案对比,但无法处理最小开停机时间约束。
- 路线B:混合整数线性规划(MILP)。用Yalmip加Gurobi或Cplex求解机组组合问题,能够在目标函数中加入启停变量、最小启停时间、爬坡约束,结果更贴近实际调度,代价是求解时间长。
怕把问题复杂化的话,可以先跑路线A,把净负荷计算、成本归集和分摊逻辑调通;如果要做严肃算例分析,再切到路线B。我的代码里两个版本都写了,调度函数接口保持一致。
路线B核心优化问题的精简版可以用如下代码表达:
P = sdpvar(n, T, 'full'); % 机组出力 u = binvar(n, T, 'full'); % 开停机状态 su = binvar(n, T, 'full'); % 启动事件 % 目标函数:燃料成本 + 深度调峰成本 + 启停成本 + 弃电成本 Objective = 0; for t = 1:T for i = 1:n Objective = Objective + units(i).a + units(i).b * P(i,t) ... + units(i).c * P(i,t)^2; % 深度调峰附加成本,分段处理 Objective = Objective + units(i).lambda * max(0, units(i).Pcon - P(i,t)); end Objective = Objective + su_cost' * su(:,t) + sd_cost' * sd(:,t); Objective = Objective + shed_cost * P_shed(t); end Constraints = [Constraints, sum(P(:,t)) + P_shed(t) == netload(t)]; Constraints = [Constraints, P(i,t) >= units(i).Pmin * u(i,t)]; Constraints = [Constraints, P(i,t) <= units(i).Pmax * u(i,t)];注意这里的二次项(c_i P_{i,t}^2)不是线性表达式,如果要交给线性求解器,必须用分段线性近似。常用做法是通过内部变量对燃料成本曲线分段线性化,或者直接把目标函数改成线性煤耗函数。工程上差别不大,但表达方式直接影响求解效率。
3.3 成本分摊模块的Matlab实现
分摊模块我用的是“场景差量 + 净负荷下滑贡献”组合方法。首先计算实际场景与无风光基准场景的成本差:
cost_increment = cost_actual_scene - cost_base_scene;然后对风电和光伏逐时出力计算下滑量,即相对于上一时段出力下降的幅度:
down_wind = max(0, wind_power(:, 1:end-1) - wind_power(:, 2:end)); down_pv = max(0, pv_power(:, 1:end-1) - pv_power(:, 2:end)); down_all = [down_wind; down_pv]; % 按各场站总下滑量占全部场站下滑量的比例分摊 weight = sum(down_all, 2) / max(sum(sum(down_all)), eps); alloc_result = cost_increment * weight;这里的分摊逻辑是:可再生能源出力下滑时,系统必须让常规机组上升出力弥补,而下一时段新能源出力上升时,常规机组又要压出力,这种来回变化就是调峰需求。因此用“总出力下滑量”作为波动贡献代理指标,物理意义直观。如果还要计入负荷侧责任,就把负荷变化量也纳入权重计算,权重改成:
[w_i=\frac{\sum_t \max(0, \Delta P_{i,t})}{\sum_j \sum_t \max(0, \Delta P_{j,t})+\sum_t \max(0, \Delta L_t)}]
这样就可以把成本同时分给负荷和新能源场站。
3.4 结果可视化与报表输出
代码跑完不能只输出一堆数字,我习惯用三类图把结果讲清楚:
- 第一类是净负荷与机组出力堆叠面积图,直观展示“谁在跟随调峰”;
- 第二类是调峰成本堆叠柱状图,按深度调峰、启停调峰、弃电成本分色展示;
- 第三类是分摊结果横向条形图,展示各个风电场、光伏电站和负荷侧分摊到的成本比例。
绘图代码不复杂:
area(t_axis, [P_mid; P_deep; P_peak]'); legend({'中等调峰机组','深度调峰机组','尖峰机组'}); xlabel('时间'); ylabel('出力/MW');输出报表我用了一个Excel导出函数writetable,把机组各时段出力、成本明细、分摊结果三个表写到同一个工作簿的不同Sheet里。这样客户或导师拿到的是一个完整台账,而不是一张截图。
4. 算例验证与灵敏度分析
4.1 基础算例设置
我构造了一个简化但典型的算例系统:负荷峰值1200兆瓦,风电装机800兆瓦,光伏装机500兆瓦,火电总装机1200兆瓦,分为四台机组,容量依次为600、300、200、100兆瓦。火电常规最小技术出力比例设置45%,深度调峰下限设置35%。风电场景很有意思:夜间大发的反调峰特性和光伏白昼出力叠加后,净负荷曲线呈现明显的“鸭子曲线”形状。
4.2 调峰成本结果与结构分解
利用三天数据跑出来结果后,我按24小时汇总统计:
| 成本科目 | 金额/万元 | 占比 |
|---|---|---|
| 深度调峰附加成本 | 18.6 | 42.3% |
| 机组寿命损耗成本 | 7.9 | 18.0% |
| 启停调峰成本 | 12.4 | 28.2% |
| 弃风弃光成本 | 5.1 | 11.5% |
| 合计 | 44.0 | 100% |
可以看到深度调峰和启停调峰是成本大头。这个比例和很多公开文献的结果趋势一致。如果负荷波动没那么极端,占比会向深度调峰倾斜;如果反调峰严重到必须频繁启停,启停成本会迅速上升。
4.3 煤价、深调极限和风光容量的灵敏度
同一个模型,改几个关键参数就能看出结论变化:
- 煤价上调20%:深度调峰成本增加约14%,启停成本增加约9%,因为燃料成本在两类成本里都有贡献;
- 深度调峰下限从35%调到30%:理论上机组能压得更低,弃电成本下降,但深度调峰损耗上升,总成本不一定下降。这种非单调关系是系统调度里常见现象,建议做参数扫描;
- 光伏装机翻倍:午间净负荷低谷更低,火电深度调峰成本上升,但傍晚净负荷爬坡更陡,启停成本和爬坡成本同时上升,总调峰成本增长可能超过50%。
灵敏度分析的意义不在于找到一个“完美参数”,而在于理解系统的成本驱动点在哪里。如果煤价灵敏度和启停灵敏度都很高,说明这台系统的调峰压力主要由火电承担,下一步就应该考虑储能或需求响应来转移这些压力。
4.4 三种分摊方案的结果对比
我把同一个算例分别用三种方案分摊:
| 分摊方案 | 风电分摊 | 光伏分摊 | 负荷侧分摊 |
|---|---|---|---|
| 按发电量比例分摊 | 61.5% | 38.5% | 0% |
| 按出力波动贡献分摊 | 54.8% | 24.7% | 20.5% |
| Shapley值分摊 | 56.2% | 26.9% | 16.9% |
按发电量比例分摊看似公平,但实际偏离因果:某个风电场发电量高但出力平稳,对调峰需求贡献很小,却被分摊了大量成本;出力波动大的小风电场反而成本很少。按出力波动贡献分摊更贴近“谁引发谁承担”,Shapley值在理论上更严格,但计算成本高。工程上我推荐用出力波动贡献分摊,因为可解释性强且代码简洁。
5. 实操经验、避坑清单与扩展方向
5.1 最容易踩的坑:模型口径与数据细节
这版代码我改了三个迭代,踩过的坑基本都集中在下面几处:
- 最小开停机时间约束缺失。早期版本只做了功率平衡,结果机组间隔两个小时就启停一次,成本高不真实还难以解释。后来加了最小开停时间约束,结果立刻稳定下来。
- 平均煤耗和边际煤耗混用。深度调峰的成本增量应该用边际煤耗增量,如果直接用平均煤耗,会显著低估深度调峰的真实支出。
- 弃电成本和火电调峰成本重复计算。火电因为多发电产生的燃料成本,和可再生能源被切除的损失,在总账单里只能算一次。这个线条要在成本归集前理清。
- 新能源出力约束缺失。净负荷为负的时刻,直接把火电出力设为零而忽略风电和光伏最大出力约束,会让弃电成本失真。正确的做法是同时添加可再生能源出力上限。
- intlinprog整数变量设置错误。把开停机状态写成了连续变量,求解结果经常出现0.5这种伪状态,得到成本毫无意义。检查变量类型是排查问题的第一步。
- 脚本文件编码问题。项目文件建议统一存成UTF-8格式,避免中文注释在旧版Matlab里出现乱码,影响后续维护。
5.2 模型验证三件事
模型跑通不代表结果正确,我会用三件事做验证:
第一,逐时功率平衡校验。每个时段所有机组出力、新能源出力和失负荷电量之和,必须严格等于该时段负荷值,误差超过0.1兆瓦就要排查。
第二,机组出力限值校验。所有机组的出力都必须落在自身最小出力和最大出力之间,且停机状态出力必须为零。
第三,成本逻辑重算。把模型输出的成本按物理公式重新手算几个典型时段,如果结果不一致,大概率是成本函数写错或者参数单位没统一。比如煤耗系数用的是吨标准煤还是吨原煤,单位不同结果差很多。
这三件事做好,模型的可解释性才有保障。
5.3 往储能、需求响应和多区域方向扩展
这套模型框架并不锁死在火电调峰上。把火电机组参数改成储能系统充放电功率限额和SOC约束,就能量化储能替代调峰火电的经济性;把可削减负荷写成带惩罚成本的可调度资源,就能评估需求响应参与调峰后系统总成本的变化;把单区域功率平衡改成多区域联络线约束,则能分析跨区域调峰互济价值。
我后来在另一个项目里加入了一个100兆瓦/400兆瓦时的储能电站,目标函数里增加充放电惩罚项和SOC日周期约束,调峰总成本下降了约27%。这个改造只花了一天半时间,因为基础模型的数据结构和目标函数本来就是解耦的。
最后再分享一点个人经验:调峰成本量化最难的往往不是公式,而是口径统一。把边界、参数和评价指标在建模前写清楚,后面所有代码和分析都会顺很多。先拿24时典型日把完整逻辑跑通,再扩展到8760小时并考虑年际变化,效率会比一开始铺大模型高得多。如果你正准备用Matlab做类似课题,建议从小算例起步,把每一笔成本的来龙去脉都打开看一遍,再去调参数和加模块。