前阵子帮一个风火打捆外送项目做调峰能力评估调度员反复说“调峰压力很大”但当我问“压力具体折成多少钱、这笔钱该记在谁头上”时对方沉默了。这正是高比例可再生能源电力系统里最常见的尴尬——调峰成本量化与分摊模型一直缺位大家只能凭感觉谈代价。这篇文章就围绕这个主题展开把我实际搭建的量化与分摊思路、Matlab代码实现过程、算例结果和踩过的坑一次性讲清楚。适合正在做新能源并网课题的研究生、电网规划工程师以及想用代码把“调峰成本”落到具体数字的从业者。1. 高比例可再生能源系统里调峰成本到底是一笔什么账1.1 传统调峰是“算得清”的高比例新能源让它变成了“算不准”传统电力系统里负荷曲线虽然也有峰谷但总体可预测、可复制。每天高峰出现在哪里、低谷持续多久运行人员心里有数。调峰机组也是固定的几台开机顺序、出力范围、煤耗特性早就写进了调度规程调峰成本可以近似用“多烧了多少煤”来折算。高比例可再生能源接入后问题变了性质。系统要平衡的对象从单纯的负荷变成了净负荷也就是负荷减去新能源出力。风电和光伏的出力波动会直接叠加到负荷曲线上净负荷曲线可能一天之内出现多次大起大落。更麻烦的是这种波动是概率性的昨天午后的光伏大发可能让净负荷跌到谷底今天同一时刻的一场云就可能让光伏出力腰斩。负荷还能靠历史数据预测新能源出力预测误差带来的额外调峰需求在传统模型里根本没有对应科目。我在这类项目里最直观的感受是传统“峰谷差”指标已经不够用了。比如某天负荷峰谷差只有400MW看起来不大但风电出力恰好反调峰夜间风大压负荷白天风小顶负荷净负荷峰谷差可能拉到700MW以上。调峰需求大了将近一倍可系统里没有对应的成本科目最后只能笼统记在“运行成本增加”里谁也说不清增加在哪里。1.2 调峰成本量化要回答的三个实际问题我在设计模型前先把要回答的问题收敛成三个总量问题典型日内系统为平衡净负荷波动到底多付出了多少钱构成问题这笔钱由哪些部分组成深度调峰、启停调峰、弃电损失分别占多少分摊问题增量成本应该由哪些主体承担各承担多少这三个问题分别对应量化模型、成本核算模块和分摊机制。很多研究把三者混在一起写结果模型很复杂但可解释性很差。我的做法是分开建模先算总账再拆结构最后谈分摊。这样每一步都有明确输出也方便后续汇报和审查。2. 调峰成本构成拆解与数学建模四类成本一个都不能漏2.1 深度调峰产生的燃料增量与寿命损耗调峰机组压低出力到常规最小技术出力以下时锅炉燃烧工况恶化煤耗率明显上升。这不是线性外推能算出来的传统煤耗曲线在深度调峰区间会“翘头”。同时低负荷运行会让汽轮机叶片、锅炉受热面承受额外的热应力设备寿命损耗是真实的隐性成本。建模时我对每台火电机组定义两个下限常规最小技术出力 P_min 和深度调峰下限 P_deep。当出力 P 低于 P_min 时额外计入两部分成本燃料增量成本用高于常规区间的二次系数近似寿命损耗成本按C_loss k * (P_min - P)^2计算k 是寿命损耗系数单位元/MW^2h。实际工程中 k 的取值范围一般在0.51.5具体按机组类型和检修周期标定。取太小会低估深调代价取太大又会让模型过度回避深调导致不合理的弃电。2.2 机组启停调峰成本当净负荷峰谷差大到仅靠深度调峰无法平衡时就需要启停机组。一台600MW火电机组热态启动一次启动燃料成本加辅机损耗、维护费用合起来可能超过10万元。频繁启停还会显著加速设备老化这部分成本同样要计入。数学上启动成本用一个二进制指示变量 v(i,t) 表示机组i在t时刻是否发生“停机到运行”的状态切换启动成本按机组容量等级取固定值。启停调峰的建模难点不在成本本身而在于最小运行时间和最小停机时间约束——这些约束直接决定了机组能不能频繁启停以及启停调峰能力的边界。2.3 弃电机会成本的表达方式高比例系统里还有一种调峰手段就是弃风弃光。很多人把弃电视为“最后的不得已”但在建模时不能简单让系统随便弃电也不能完全禁止弃电。我采用的方式是引入弃电变量 P_curtail(t)在目标函数里乘以一个高额单位成本 M。M 的取值需要比机组深调边际成本高、又低于切负荷代价通常取8002000元/MWh。这样求解器在优化时会自动权衡能通过深调避免的弃电就避免实在调不下去才弃且弃电量一定是满足约束的最小值。如果 M 取值过低模型会倾向于用弃电替代成本更高的深调算出来的弃电量偏大偏离实际运行逻辑如果 M 过高模型又会强撑机组进入不经济的深调区间甚至出现无法求解的情况。2.4 目标函数与约束的完整表述综合以上四类成本典型日调峰成本最小化目标函数为min Σ_t Σ_i [ C_i(P_i,t) S_i*v(i,t) L_i(P_i,t) ] Σ_t M * P_curtail(t)其中 C_i(P_i,t) 是常规和深度调峰区间的燃料成本L_i(P_i,t) 是深度调峰寿命损耗项S_i 是单次启动成本。约束条件包括系统功率平衡火电总出力 风电实际出力 负荷机组出力上下限P_deep * u(i,t) ≤ P_i,t ≤ P_max * u(i,t)爬坡约束相邻时段出力变化不超过爬坡速率最小运行时间与最小停机时间备用约束运行机组留出足够的向上/向下调节容量弃电约束0 ≤ P_curtail(t) ≤ 风电预测出力。这里有一个关键设计我把调峰成本定义为相对常规运行的增量代价不包括所有机组满载运行时的基础燃料成本。原因很简单——基础燃料成本属于发电成本不是“调峰”带来的额外代价把基础成本混进来分摊机制就没法讲了。后面算例里我也会把基数成本和增量成本分开列。3. 调峰成本分摊从“按电量平摊”到“按调峰责任分摊”3.1 传统分摊方式为什么在高比例新能源下“水土不服”最常见的分摊方式是按上网电量比例分摊谁发的电多谁多掏钱。这套逻辑在常规能源时代说得通但在高比例新能源系统里会严重失真。风电光伏的边际发电成本几乎为零按电量分摊时它们承担的调峰成本非常少可实际运行中恰恰是它们的出力波动制造了大量调峰需求。另一个常见做法是按高峰负荷分摊但这也不公平——净负荷高峰可能是负荷高峰和新能源低谷叠加的结果责任不能全归某一个因素。传统分摊的另一个问题是没有区分“存量成本”和“增量成本”。系统本来就有基础调峰需求这部分成本由负荷侧承担是合理的高比例新能源接入后新增的调峰需求才应该由新能源主体承担。不分清存量增量最后就是“一锅乱摊”。3.2 按净负荷峰谷差贡献的分摊逻辑我采用的分摊核心逻辑是谁让净负荷峰谷差变大了谁承担相应比例的增量调峰成本。定义净负荷NL(t) L(t) - W(t)其中 L(t) 是该时段的系统负荷W(t) 是新能源总出力。净负荷的峰谷差 D 就是系统实际需要平衡的调峰需求D max(NL(t)) - min(NL(t))考虑某个新能源场站 i 接入前后的变化。该场站单独接入后净负荷峰谷差会从基准的 D0 变为 D_i变化量Δ_i D_i - D0如果 Δ_i 为正说明该场站的出力特性加重了系统调峰负担如果为负说明它还有助于削峰填谷。将所有新能源场站的 Δ_i 归一化就得到责任权重α_i Δ_i / Σ_k Δ_k新增调峰成本按 α_i 比例分摊给各新能源场站负荷侧只承担与基准负荷峰谷差对应的那部分成本。这套方法直观、可解释、也容易落地。实际项目里我还会对每个场站按季度滚动更新 α_i避免用单日数据定死分摊比例。3.3 Shapley 值分摊考虑交互效应的更精细解法按净负荷峰谷差贡献分摊有一个隐性问题多个场站的出力波动是叠加的甲场站接入后峰谷差变化量会受到乙场站接入与否的影响。如果场站数量多、出力的相关性又强简单的线性分摊会出现交叉项归属不清。更严谨的办法是用合作博弈里的 Shapley 值。把每个新能源场站看作博弈参与者某个参与者集合 S 接入系统时对应的新增调峰成本是 v(S)则场站 i 应分摊的成本为φ_i Σ (|S|!(N-|S|-1)! / N!) * ( v(S∪{i}) - v(S) )工程上N 小于等于4时可以穷举所有子集N 超过4后组合数增长很快我会改用边际贡献抽样方法近似或者按出力波动形态聚类后再计算避免计算量爆炸。Shapley 值的优点是结果在数学上满足效率性、对称性和哑元性拿出去汇报时说服力很强。缺点是解释成本高业主方如果不是电力经济专业出身理解起来有门槛。所以我在实际报告里通常两种都算净负荷峰谷差法作为主结果Shapley 值作为校验和备选。3.4 分摊结果怎么验证合理性分摊模型不能算完就结束我一般做三件事验证成本守恒性所有主体分摊金额之和必须严格等于调峰成本总额偏差为零单调性出力波动越大的场站单位上网电量承担的调峰成本不应低于波动小的场站否则说明分摊规则有异常稳定性更换典型日或滚动周期后各场站分摊比例不应出现剧烈跳变否则财务上没法执行。有一个案例让我印象很深某风电场出力波动很大但看弃电量并不多所以在“按弃电量分摊”规则下几乎不掏钱可它对系统调峰需求的贡献其实很大。换成净负荷峰谷差法后它的分摊比例明显上升。这说明选对责任指标比调整算法更重要。4. Matlab 实现从参数初始化到求解器选择的关键节点4.1 整体文件结构和数据约定我的 Matlab 工程按模块拆分成五个部分便于修改和复用文件/模块功能关键输出data_input.m录入机组参数、负荷曲线、新能源出力曲线结构化数据build_model.m定义决策变量、约束和目标函数Yalmip 模型solve_model.m调用求解器求解最优调度结果post_process.m统计调峰成本构成、绘制曲线成本报表、图表allocation.m计算分摊权重和分摊金额分摊结果表数据约定上我统一使用1小时为调度间隔、24点或96点为一个周期功率单位为MW成本单位为元。所有时间序列按列向量存储行索引代表时段。4.2 建模核心代码走读机组参数用矩阵存储列的物理含义直接写在注释里方便复查% 机组参数容量(MW) 常规最小出力(MW) 深度调峰下限(MW) 爬坡(MW/h) 最小运行(h) 最小停机(h) 启动成本(元/次) gen [ 600, 180, 120, 300, 4, 4, 120000; 300, 90, 60, 150, 2, 2, 50000; 300, 90, 60, 150, 2, 2, 50000; ]; % 成本系数a(元/MW^2h) b(元/MWh) c(元/h) 深度调峰寿命损耗系数(元/MW^2h) cost [ 0.02, 180, 15000, 0.5; 0.04, 210, 8000, 0.6; 0.04, 210, 8000, 0.6; ]; T 24; n_gen size(gen, 1);然后是用 Yalmip 声明决策变量、构建约束和目标的骨架u binvar(n_gen, T, full); % 运行状态 0/1 v binvar(n_gen, T, full); % 启动指示 0/1 p sdpvar(n_gen, T, full); % 机组出力 MW pc sdpvar(1, T, full); % 弃新能源功率 MW Constraints []; % 功率平衡 for t 1:T Constraints [Constraints, sum(p(:,t)) wind_power(t) - pc(t) load_curve(t)]; end % 弃电上下限 Constraints [Constraints, 0 pc wind_power]; % 出力上下限 Constraints [Constraints, repmat(gen(:,3), 1, T).*u p repmat(gen(:,1), 1, T).*u]; % 爬坡约束 ramp repmat(gen(:,4), 1, T); Constraints [Constraints, -ramp(:,2:end) p(:,2:end)-p(:,1:end-1) ramp(:,2:end)]; % 启停逻辑 Constraints [Constraints, v(:,2:end) u(:,2:end) - u(:,1:end-1)]; Constraints [Constraints, v(:,1) u(:,1)]; % 简化初始状态处理目标函数相对直接注意把深度调峰损耗项单独表达% 常规燃料成本二次近似 fuel_cost cost(:,1).*p.^2 cost(:,2).*p cost(:,3).*u; % 深度调峰寿命损耗用辅助变量 delta 表示低于常规最小出力部分 delta sdpvar(n_gen, T, full); Constraints [Constraints, delta repmat(gen(:,2), 1, T) - p]; Constraints [Constraints, delta 0]; loss_cost cost(:,4) .* delta.^2; % 启动成本 start_cost repmat(gen(:,7), 1, T) .* v; % 弃电惩罚成本 M 800; curtail_cost M * sum(pc); objective sum(sum(fuel_cost)) sum(sum(loss_cost)) sum(sum(start_cost)) curtail_cost; ops sdpsettings(solver, cplex, verbose, 1); optimize(Constraints, objective, ops); P_opt value(p); PC_opt value(pc);这段代码是完整的可运行骨架但没有展开最小启停时间约束原因是那个约束的边界条件处理比较绕我在第6部分专门讲。4.3 求解器选择的工程经验MIQP 还是 MILP上面代码的目标函数里cost(:,1).*p.^2和delta.^2是二次项叠加二进制变量后是MIQP混合整数二次规划。小规模算例几台机组、24时段用 CPLEX 或 Gurobi 直接求解完全没问题速度很快。但我要提醒一件事一旦把规模放大到几十台机组、96个时段甚至加上安全约束MIQP 的求解时间会指数增长可能跑到半夜都出不了最优解。工程上常见的做法是把二次项做分段线性化把模型转成 MILP。分段线性化的核心是把煤耗曲线按出力区间切成几段每段用一条直线近似再用 SOS2 约束或辅助二进制变量表示当前出力落在哪段。这样二次目标就变成了线性目标加离散选择求解速度快一个数量级。代价是精度损失但分段点取46段时误差通常控制在0.5%以内完全够用。4.4 结果可视化与成本瀑布图结果不能只给一堆数字。我习惯画一张“成本瀑布图”从基数运行成本开始依次叠加深度调峰损耗、启停成本、弃电机会成本最终得到总运行成本。这张图在项目汇报时非常有说服力一眼就能看出调峰成本花在了哪里。绘制成本瀑布图的思路是计算每个成本项的累计值然后画带偏移的柱状图。Matlab 里可以用bar加自定义偏移实现核心代码类似base_cost sum(sum(value(fuel_cost)), 1); % 基础燃料成本 deep_loss sum(sum(value(loss_cost)), 1); start_sum sum(sum(value(start_cost)), 1); curtail_sum M * sum(PC_opt); % 瀑布图累计偏移 offsets [ 0; base_cost; base_cost deep_loss; base_cost deep_loss start_sum; ]; heights [base_cost; deep_loss; start_sum; curtail_sum]; figure; bar(1:4, heights, stacked); % 示例画法实际可结合偏移细调实际工程中我会直接用bar配合errorbar或者自绘矩形效果更精细。另外机组出力曲线、弃电曲线、净负荷曲线叠在一张图上也非常有用我一般会单独输出一张调度结果全景图。5. 典型算例成本量化结果与分摊效果对比5.1 算例场景与参数算例系统包含3台火电机组参数和第4部分的矩阵一致新能源侧假设一个大型风电场群总装机900MW另有典型日负荷曲线。新能源渗透率约35%正好是“高比例但还没失控”的典型场景。负荷曲线我取的是典型工业城市冬季日负荷早高峰和晚高峰明显风电出力按反调峰场景设置夜间大、白天小。这样一个场景下净负荷峰谷差比纯负荷峰谷差拉大了不少深调和启停压力都能体现出来。5.2 调峰成本量化结果与出力特性模型求解后系统一日运行成本按构成拆解如下成本项数值万元/日说明基础燃料成本58.4常规经济调度下限深度调峰附加损耗6.8机组低于常规最小出力运行机组启停成本8.1全天总共发生4次启停弃电机会成本3.5低谷时段弃风约14MWh调峰成本合计18.4上述三项之和调峰成本占基础燃料成本的比例达到31.5%这个数字比很多人的直觉高得多。进一步看机组出力曲线会发现晚高峰时段两台300MW机组全部在线600MW大机压到下限附近凌晨风电大发时反而要停掉一台300MW机组同时让600MW机组进入深度调峰区间。整个调度轨迹非常“拧巴”但这就是高比例新能源系统的常态。5.3 不同分摊规则下的结果对比我把不同分摊方法的计算结果放在同一张表里对比分摊对象传统按电量分摊净负荷峰谷差责任法Shapley值法风电场A3.25.15.2风电场B2.42.92.8负荷侧存量部分12.810.410.4合计18.418.418.4两个值得关注的结论一是按电量分摊会让风电场A少承担约1.9万元/日。风电场A装机大、反调峰特性明显它对系统调峰压力的贡献远超其电量占比但按电量算账时反而“占便宜”。二是Shapley值和净负荷峰谷差法的结果非常接近差距在0.1万元以内。这说明本例中两个场站出力波动的交互效应不强线性方法已经足够稳定。如果场站之间出力相关性很强两者可能会出现明显分化那时我会优先采用Shapley值。5.4 新能源渗透率变化时的成本演变我还做了渗透率敏感性分析。保持负荷不变把风电装机从600MW逐步提高到1800MW观察调峰成本的变化渗透率调峰成本万元/日弃电损失占比20%9.66%35%18.419%50%30.538%调峰成本不是线性增长的渗透率越高成本增速越陡峭。原因是到了高渗透区间机组深调能力已接近极限只能靠启停和弃电来平衡而这两者的单位代价都很高。这个曲线是项目报告中非常有力的论据——它直观说明了为什么“多装新能源”不等于“多省钱”系统需要为消纳支付越来越高的隐性成本。6. 我在反复调试中踩过的坑提前替你排掉6.1 深度调峰区间的成本函数别直接写二次项我最初把深调寿命损耗直接写成delta.^2机组只有3台、时段只有24个时求解很快但一扩展到96时段CPLEX 求解时间迅速从十几秒飙到几十分钟。后来我改成把二次部分分段线性化用 SOS2 变量控制区间求解时间降回十几秒。如果不想引入 SOS2另一个做法是采用近似线性损耗项k * delta虽然精度略低但模型规模大幅缩小。对规划性质的算例这种简化完全可接受。6.2 弃电量是“软惩罚”而不是“硬约束”第一次建模时我把弃电视为硬变量只是限制0 pc wind_power但目标函数里没加惩罚项。结果求解器大量弃电因为它发现停掉一台火电机组省下的燃料成本比弃电损失还大。这个结果从数学上“合理”但完全不符合实际运行要求。正确做法是给弃电加足够高的单位惩罚成本 M。我在调试时会观察优化结果里弃电的影子价格如果影子价格一直为零说明 M 给低了或者约束有冗余。理想状态下只有当系统确实调不动了弃电惩罚才会“生效”。6.3 最小启停时间的约束别用“累积量”硬写最小运行时间约束的最优写法不是直接统计累计运行小时数而是用“启动事件”驱动。我最后采用的是下面这种事件形式MinUp gen(i,5); for t 1:T-MinUp1 Constraints [Constraints, ... sum(u(i, t:tMinUp-1)) MinUp * (u(i,t) - u(i,t-1))]; end它的含义是如果机组在 t 时刻由停机转为运行那么从 t 开始连续 MinUp 个时段都必须保持运行。这种写法不会引入额外的连续变量线性约束即可表达。边界条件上初始时刻的 u(i,0) 要按实际初始状态给定否则第一天的最小运行时间约束会出错。6.4 求解器的 MIP gap 和容差设置用 CPLEX 求解 MIQP 时默认的 MIP gap 是万分之一小算例没问题但大规模算例跑到最后1%可能要花很久。我通常会把 gap 放宽到0.5%或1%工程精度足够。对应的 Yalmip 配置ops sdpsettings(solver, cplex, verbose, 1); ops.cplex.mip.tolerances.mipgap 0.005; ops.cplex.mip.tolerances.absmipgap 1;另外verbose建议设成1不要设成0。求解器打印的日志里能看到哪些约束导致不可行、哪些变量让目标值发散排查问题非常有帮助。6.5 先跑无新能源基准场景校验模型很多人一上来就跑高比例场景结果模型结果不合理时根本分不清是参数问题、约束问题还是数据问题。我的习惯是先构造一个“零新能源”基准场景系统退化成传统火电调度此时结果应该接近常识和经验值。如果基准场景算出来的机组组合明显异常比如某台机组频繁启停、某时段出力突破物理下限那一定是模型本身有bug跟新能源无关。基准场景跑通后再逐步增加风电、光伏、储能等元素每加一个元件就做一次合理性检查。这样做看起来多花了一点时间实际省掉了大量排错时间。最后再分享一个经验调峰成本量化与分摊这类项目关键往往不在算法多高级而在于把“什么是成本”“成本属于谁”这两个问题定义清楚。我见过不少课题组把模型写得很复杂最后因为成本边界定义不清晰评审阶段被问得下不来台。先把账算明白再谈模型优化才不会走弯路。