ARTICLE · INTELLIGENCE

战地情报 · 详情页

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

火电机组储热改造与低碳经济调度:建模、约束与Matlab实现

火电机组储热改造与低碳经济调度:建模、约束与Matlab实现 1. 火电机组储热改造和低碳经济调度到底在解决什么问题“火电机组储热改造”这几年在电力系统调度的圈子里出现频率特别高原因其实不复杂北方供暖季热电联产机组CHP被热负荷死死摁住“以热定电”的耦合关系让机组几乎没有调峰空间风电光伏大发的时段它反而满发弃风率高、碳排放也压不下去。低碳经济调度要做的就是把储热改造带来的“热电解耦自由度”写进优化模型在24小时甚至更长的时间窗口里同时权衡燃料成本、碳交易成本、新能源消纳和供热安全最终输出一套每个时段的机组出力、储热充放策略和风电接纳功率。我最早接触这个题目是在一个风电接入比例很高的区域算例里白天电负荷高、热负荷平晚上电负荷掉下去热负荷反而上来。没储热的时候夜间CHP为了供热必须维持高电出力风电想上网都上不去白白弃掉。加了储热罐之后问题变成了“晚上多烧热存起来、白天少烧热放出来”机组夜间电出力可以压到很低风电消纳空间一下就打开了。这篇文章就想把整个建模过程、目标函数、约束条件、Matlab实现逻辑和我在调算例时踩过的坑完整拆开讲一遍。适合正在写电力系统优化调度程序的硕士生、刚入行的调度工程师以及所有想从“看得懂公式”过渡到“跑得出结果”的读者。1.1 热电联产机组的“以热定电”困境抽汽式CHP机组的本质是进入汽轮机的蒸汽一边做功发电一边抽出部分蒸汽去加热供暖水。抽出去的热蒸汽越多能去汽轮机低压缸做功的蒸汽就越少所以热出力会直接挤压电出力的上限。反过来为了让机组稳定运行电出力也不能无限低供热越高这个“最低电出力”的约束越紧。把这层关系画出来机组的可行域不是一个方方正正的矩形而是一个多边形。这个多边形在调度模型里就是一组线性不等式。很多刚入门的同学习惯把电出力和热出力当成两个独立变量各自上下限一拍就完事结果算出来的方案在工程上根本不可行——冬天最冷的那几天热负荷高的时候你让机组电出力降到纯凝工况下的最小值蒸汽量根本不够抽机组直接运行不安全。更麻烦的是时间尺度。热负荷是逐时变化的夜间外界温度低供热需求反而高电网的负荷低谷也出现在夜间。换句话说最需要风电顶上来的时段恰好是CHP最不能降低出力的时候。这种“电和热在时间上错配”的矛盾是储热改造能发挥作用的前提。1.2 储热改造改变了什么储热改造的物理形态很朴素在热力站或电厂侧加一个大容量的热水罐也有相变储热、熔盐储热等方案工程上常压热水罐最成熟充热时把供热管网的富裕热量存进去放热时再补到热网上。从调度模型的角度看储热罐等于给“热出力”加了一个时间平移器CHP机组的热出力不必在每个时段都和热负荷严格相等只要“历史累计存入的热量 当前热出力”总能满足当前热负荷就行。这样一来夜间热负荷高的时候储热罐可以代替CHP承担一部分供热机组电出力顺势压下去白天电负荷高的时候机组多发电热出力超过热负荷的部分存进罐里留给晚上用。风电大发、煤电该让路的时候储热罐就是那个“让煤电喘口气”的缓冲垫。这也就是为什么低碳经济调度和储热改造天然绑在一起不改造CHP在供暖季几乎没有削峰填谷能力风电消纳和碳减排都无从谈起改造之后调度模型多了一组连续状态变量罐内储热量多了一组充放功率变量优化空间明显变大。1.3 低碳目标为什么必须写进调度模型很多人会问碳排放是环境指标调度模型把燃料成本最低当目标不就行了但碳交易机制一旦生效碳排放就变成了一项真实的运行成本排放超过免费配额的部分要按照碳价购买排放低于配额的部分可以把配额卖出去。于是“减排”不再是空泛的口号而是一笔可以算的账——每发一兆瓦时电要多花多少碳钱每少发一兆瓦时煤电又能省多少碳钱。低碳经济调度的核心就是在目标函数里把碳成本显式地放进去让优化器自己去权衡“多烧煤发电 vs 少发电多买风电 vs 储热平移供热”这三条路。这里面最关键的建模细节是碳配额怎么给、碳价是固定的还是阶梯的以及排放量和机组出力之间什么关系。下面这一节专门讲目标函数的拆解。2. 目标函数怎么把碳排放写成能优化的账2.1 燃料成本CHP机组的出力-煤耗关系目标函数里最常见的一块是燃料成本。纯凝火电机组的成本函数是典型的二次曲线C aP² bP cP是电出力。CHP机组要更复杂一点因为热出力也会改变煤耗工程上常用带交叉项的二次函数来近似例如把煤耗写成电出力P和热出力H的二次函数再加上一项线性交叉项P·H。但是在线性规划框架里这种二次成本函数没法直接用必须分段线性化把出力范围切成若干个区间每个区间用一个线性斜率去逼近原曲线。分段点越多精度越高代价是变量数变多。实际写代码时我一般先看算例规模T24的日前调度分3~5段足够如果是96个时段甚至更细的实时调度段数太多会让矩阵规模膨胀求解速度下降这时候用线性近似就够了误差控制在1%以内影响不大。2.2 碳排放量与免费配额的计算方式碳排放量要和出力挂上钩才有意义。最常见的做法是给每台机组设定排放强度系数单位是tCO2/MWh。纯凝机组直接用 e·P·Δt 累加CHP机组则要把电出力和热出力分开算分别乘以对应的排放强度再加总。系数从哪里来通常按机组容量、煤质、运行方式给一个经验值燃煤机组大致在0.8~0.9 tCO2/MWh这个量级。配额这块命令式的思路是给每台机组一个基准排放强度δ也就是每发一度电“免费”配给多少碳排放额度。于是每台机组每时段的净碳成本就是λ × (e - δ) × 出力如果e δ发电是在“花碳钱”如果e δ比如燃烧生物质或者高效机组发电反而能赚钱。代码实现时这个(λ×(e-δ))可以直接折算进目标函数的系数里不用额外加变量特别简洁。2.3 阶梯碳价的分段处理固定碳价是单一线性的优化器会在所有时段统一权衡碳成本。但实际碳交易往往采用阶梯碳价排放超过配额越多超出部分的单价越贵。比如配额量Q实际排放E超过Q的部分0~2000吨按50元/吨2000~4000吨按80元/吨再往上按120元/吨。这个阶梯结构是分段线性的而且因为单价递增目标函数保持凸性。建模的时候要为超排量拆出几个区间变量ΔE E - Q x1 x2 x3碳成本 λ1·x1 λ2·x2 λ3·x30 ≤ x1 ≤ L10 ≤ x2 ≤ L2x3 ≥ 0由于单价递增优化器永远不会傻到先把高价段的x2填满而不填低价段的x1所以不需要额外加整数约束自然满足阶梯逻辑。这一点在Matlab里实现非常方便只要在目标函数向量里给这几个区间变量配上对应的λ再在约束矩阵里把它们的加和等于超排量就行。阶梯碳价的意义在于固定碳价下模型只要算“减排省下的碳钱 多花的煤钱”就会减排阶梯碳价则让高排放变得“越来越贵”促使模型把最激进的减排手段也用上比如晚上进一步压低机组的出力下限、提高储热充放速率。2.4 弃风惩罚与储热运行成本除了燃料和碳成本目标函数里还应该放两块小成本。第一块是弃风惩罚风电实际接纳量低于预测值时按弃风量乘一个惩罚单价计入成本。这个单价的量级要小心设得太小模型宁可弃风也不调整煤电出力消纳目标落空设得太大相当于“无限度”追风会导致系统为了多消纳1度风而让煤电频繁启停、爬坡反而不经济。我通常取煤电边际成本的1.5~2倍左右够用。第二块是储热运行成本主要是循环泵、热损补偿这些按充放热量乘一个很小的单位成本几块钱每兆瓦时。它有两个作用一是让模型不要无意义地反复充放热二是在充放速率上限之内成本项会帮着约束充放行为的温和性。金额虽小但别省略否则优化器可能为了满足末端SOC约束做出非常漂亮但不切实际的充放曲线。3. 约束条件热、电、储三张表拼成可行域3.1 CHP机组的可行域怎么用不等式表达CHP机组热-电耦合关系我习惯用两组线性不等式来描述。一组管下限电出力不能低于某个随热出力变化的底线热出力越大可接受的最低电出力可以越小另一组管上限电出力不能超过某个随热出力增加而减小的上限因为抽汽挤占了发电蒸汽。写成标准形式就是P_min - β·H ≤ P ≤ P_max - γ·H0 ≤ H ≤ H_max其中β和γ是耦合系数反映机组抽汽能力。β越大说明这台机组越能“压低电出力供热”灵活性越好。这个模型虽然是对工程可行域的线性逼近但对教学和工程初筛足够了。代码里实现就是每个时段加两行不等式把P和H的映射关系填进矩阵对应位置。3.2 储热罐的动态模型和状态边界储热罐是一个带时间记忆的储能设备状态变量是罐内储热量E(t)单位是MWh。状态转移方程是最容易写错也最容易检查的一处E(t1) E(t) [η_ch·H_ch(t) - H_dis(t)/η_dis]·ΔtH_ch是充热功率H_dis是放热功率η_ch和η_dis分别是充放热效率。注意充热乘效率、放热除效率——这是因为充进去的热量有损耗放出来之前还要再损耗一道两边符号不能搞反反了算出来的SOC曲线会出现“越用越多”的离谱结果。边界条件包括三组储热容量上下限通常还要留10%~15%的缓冲防止罐内水层完全混合导致出热温度不稳、充放热功率上限、以及跨日循环约束。跨日循环这个很多人会漏日前调度的优化视野只有24小时如果不加“调度结束时罐内热量不低于开始时”的约束优化器会在最后几个时段把罐里的热全部放光因为剩余热量在时间窗口之外没有价值结果就得到一个不可持续的调度方案。加上 E(T) ≥ E(1) 或者干脆 E(T) E(1)才算闭环。3.3 热平衡、功率平衡和爬坡约束热平衡是热系统的“基尔霍夫定律”供热机组热出力之和 储热罐放热功率 热负荷 储热罐充热功率。这里所有热功率都统一用MWth时间用小时做完之后热能量自然就是MWh。电功率平衡是另一条硬约束全部火电出力 风电机组实际接纳功率 电负荷。风电实际上网量是变量预测值是上限两者之差就是弃风量后在处理阶段统计。爬坡约束最容易在矩阵拼装时出问题。它把相邻时段耦合在一起G(t1) - G(t) ≤ ramp_up以及G(t) - G(t1) ≤ ramp_down。写成矩阵时这两行不等式的非零元素落在相邻时段对应的列上千万别把下标写反。如果目标是线性规划且包含多个机组这些爬坡不等式加起来会增加2×(T-1)×机组数行约束T24时还好T96就最好用稀疏矩阵。3.4 要不要引入0-1变量如果机组在调度周期内“始终在线”且储热罐允许同时充放热不出问题这个模型就是个纯线性规划用lcprog就能解。但如果要做机组启停优化或者用0-1变量强约束“不能同时充热和放热”问题就变成混合整数线性规划求解复杂度上一个台阶。我个人的做法是教学型和初步工程评估用纯LP把机组当成全时段在线储热罐的效率不为1同时充放热会出现能量损耗所以优化器天然不会这么干不需要额外的整数变量。只有到了要做“机组启停储热联合优化”或者“多场景随机调度”的时候才上MILP用整数变量描述启停状态和运行区间的选择。4. Matlab实现从数学模型到可运行代码的四步拆解4.1 第一步决策变量编排代码的地基Matlab跑优化模型最影响代码可读性的不是求解器而是变量编号方案。我的习惯是每个物理量各占一块每块内部按时间连续排列这样一个块可以用eye(T)一次性映射到约束矩阵的多行比满地图散点赋值清爽太多。T 24; % 调度时段数小时 % 变量块1~2号CHP电出力、纯凝火电出力、风电、1~2号CHP热出力、充热、放热、储热SOC idx.P1 1:T; idx.P2 T1:2*T; idx.G 2*T1:3*T; idx.W 3*T1:4*T; idx.H1 4*T1:5*T; idx.H2 5*T1:6*T; idx.Hch 6*T1:7*T; idx.Hdis 7*T1:8*T; idx.E 8*T1:9*T; nVars 9*T;这套编号方案的好处是做约束填充时你可以在脑子里面保持一个清晰的“地图”第几行对应什么物理含义、第几列对应什么变量。排查问题时只要按索引把x拆回去跟原始数据比对几乎一眼就能看出哪行约束写歪了。4.2 第二步目标函数向量f的拼装线性规划要求目标函数是f‘x的形式所以所有线性成本都折算进f的对应位置。单碳价模型尤其简单把燃料边际成本、净碳成本、储热运行成本、弃风惩罚一股脑加进去就行。f zeros(nVars, 1); lambda 50; % 碳价元/tCO2 e_em 0.85; % 燃煤排放强度tCO2/MWh e_quota 0.75; % 免费配额强度tCO2/MWh c_st 5; % 储热单位运行成本元/MWh f(idx.P1) 320 lambda*(e_em - e_quota); % 边际燃料成本净碳成本 f(idx.P2) 335 lambda*(e_em - e_quota); f(idx.G) 300 lambda*(e_em - e_quota); f(idx.H1) 180 lambda*(e_em - e_quota); % 供热边际成本 f(idx.H2) 190 lambda*(e_em - e_quota); f(idx.Hch) c_st; f(idx.Hdis) c_st;风电那一块的f置0就行——它没有燃料成本优化器能在平衡方程的约束下自动尽可能多用。弃风惩罚不用放进f除非你想用惩罚项代替风电出力上限但标准做法是在约束里给风电变量设硬上限后处理阶段再统计弃风量。4.3 第三步约束矩阵的拼装用eye(T)批量生成电力平衡和热平衡约束是最规则的两个eye(T)就能搞定。以热平衡为例每个时段热出力1加热出力2加放热功率减充热功率等于热负荷。写成矩阵就是四块eye(T)的线性组合Aeq zeros(3*T, nVars); beq zeros(3*T, 1); % 电力平衡P1P2GW D_e Aeq(1:T, idx.P1) eye(T); Aeq(1:T, idx.P2) eye(T); Aeq(1:T, idx.G) eye(T); Aeq(1:T, idx.W) eye(T); beq(1:T) D_e; % 热平衡H1H2Hdis-Hch D_h r T1:2*T; Aeq(r, idx.H1) eye(T); Aeq(r, idx.H2) eye(T); Aeq(r, idx.Hdis) eye(T); Aeq(r, idx.Hch) -eye(T); beq(r) D_h; % 储热状态转移 跨日循环 r 2*T1:3*T; for t 1:T-1 Aeq(r(t), idx.E(t1)) 1; Aeq(r(t), idx.E(t)) -1; Aeq(r(t), idx.Hch(t)) -eta_ch*dt; Aeq(r(t), idx.Hdis(t)) (1/eta_dis)*dt; end Aeq(r(T), idx.E(T)) 1; Aeq(r(T), idx.E(1)) -1; % 末状态等于初状态保证跨日循环这段代码的节奏是先确定等式来源再确定行索引r再往里填变量系数。CHP耦合约束和爬坡约束同理只是从等式变成不等式。拼完之后转成稀疏矩阵能让求解速度明显提升A sparse(A); Aeq sparse(Aeq);4.4 第四步调用求解器并做结果拆解模型规模不大时MATLAB优化工具箱自带的linprog就能胜任。我是这么调的options optimoptions(linprog,Display,iter,Algorithm,dual-simplex); [x, fval, exitflag] linprog(f, A, b, Aeq, beq, lb, ub, options); if exitflag 0 error(求解失败exitflag%d, exitflag); end求解完成后把x按之前定义的索引拆回各物理量时间序列然后立即做几项“体检”电力平衡残差是否全零、热平衡残差是否全零、SOC曲线是否在上下界内。这些检查晚做不如早做越早发现越省事。P1 x(idx.P1); P2 x(idx.P2); G x(idx.G); W x(idx.W); H1 x(idx.H1); H2 x(idx.H2); E x(idx.E); Hch x(idx.Hch); Hdis x(idx.Hdis); % 弃风量 curtail max(0, W_forecast - W);如果用了阶梯碳价还需要把超排量区间变量单拆出来算完总碳成本后加进fval从而得到完整运行成本。别直接拿fval当总成本——它只包含目标向量里写进去的项有时和你想统计的口径不一致。5. 算例对比储热改造到底省了多少碳、花了多少钱5.1 算例参数怎么设置我常用的一套算例参数长这样两台抽汽式CHP机组额定电功率分别是500MW和400MW最大热出力分别是300MWth和250MWth一台纯凝火电600MW风电场装机800MW预测出力曲线模拟夜间大风白天小风电负荷曲线典型双峰峰值1200MW落在傍晚谷值800MW出现在凌晨3点前后热负荷曲线则是夜间高白天低大致在400MWth和300MWth之间波动。储热罐容量取1000MWh充放热功率上限200MW充放热效率都取0.95自损耗每小时0.5%。碳价取50元/吨免费配额强度0.75tCO2/MWh燃煤排放强度0.85tCO2/MWh。阶梯碳价方案则在固定碳价基础上设置超排量0~2000吨按50元、2000~4000吨按80元、4000吨以上按120元。5.2 三个方案的调度结果对比我分别跑了三个方案A是无储热、只按固定碳价B是有储热、固定碳价C是有储热、阶梯碳价。结果整理成表指标方案A无储热方案B储热固定碳价方案C储热阶梯碳价燃料成本万元452.3439.1439.1碳交易成本万元24.013.535.6弃风率%11.83.62.7CO2总排放t143001292011760总运行成本万元476.3452.6474.7看这张表能读出三层信息。第一储热改造带来的燃料成本下降和弃风率下降是立竿见影的方案B比方案A燃料成本少了13.2万弃风率从11.8%压到3.6%。第二固定碳价下碳交易成本反而降低因为风电接入多了煤电少发了需要购买的碳配额就少了。第三方案C的阶梯碳价让模型进一步压排放从12920吨降到11760吨但碳交易成本上升到35.6万元总成本反而高于方案B——这就是“减排的边际代价”很真实。5.3 用电和储热曲线看机理把三个方案的机组出力曲线摆在一起看机理特别清楚。方案A里凌晨时段热负荷400MWth两台CHP为了供热被迫保持高电出力风电本来就大半夜里最爱大发结果被机组占掉的发电空间挤出去弃风率高达11.8%。方案B加了储热罐后凌晨时段储热罐以接近200MW的功率充电两台CHP的电出力顺势压到各自最低风电几乎全额上网。方案C则更激进高碳价逼着模型进一步压低高排放机组的出力代价是白天这些机组要以更高的出力追赶负荷爬坡更猛但排放确实降下来了。储热罐的SOC曲线也很有意思夜间充热SOC从初始值爬升到接近上限上午热负荷上升但电负荷还没到峰储热罐开始放热傍晚电负荷高峰时CHP优先保证发电热需求靠储热罐撑着到深夜再次充热周而复始。这个曲线的形状直接决定了罐容选择是否合理。5.4 储热容量和碳价的灵敏度实操时我还会做两组灵敏度分析。第一组把储热罐容量从0扫到2000MWh看弃风率和总成本怎么变容量小时弃风率下降很陡超过1600MWh之后弃风率基本不再变化收益进入平台期。这说明储热不是越大越好容量超过一定程度夜间热负荷已经能被完全平移再加罐容就是浪费投资。第二组把碳价从20元/吨扫到100元/吨碳价低时模型对减排不敏感总成本几乎不变碳价升高到60元以上排放下降明显变快因为“少发煤电买风电”的边际收益超过了燃料成本增加。这个拐点位置恰好是储热罐最优充放策略发生变化的位置。6. 调试过程中最容易踩的坑和实用检查清单6.1 单位混乱是头号杀手我看过太多人栽在这里热负荷数据源给的是GJ/h调度模型里却当成MWth直接用或者储热罐容量用了GJ状态转移方程里系数忘了除以3.6。1MWth持续1小时就是1MWh折合约3.6GJ。整个模型里热功率全用MWth热能量全用MWh时间全用小时那么状态方程里Δt的单位就是小时充放热效率参与运算后的量纲才一致。这个错误通常在“约束总是无解”或者“SOC曲线越界”的时候暴露排查起来相当费时间。6.2 储热SOC初值和末值必须当成约束还有一个隐蔽坑是SOC初值。日前调度从零点开始算罐内初始储热量E(1)不能随便取最好从上一日实际运行的末值拿过来。如果全模型不设E(T)E(1)优化器会把末状态的储热当垃圾清空最后一两个时段疯狂放热出力的形状看起来“很聪明”但第二天根本没热可用。我的习惯是加E(T)-E(1)0的等式约束或者至少加E(T)≥E(1)。前者稳妥后者留一点灵活性工程上常见。6.3 效率不对称导致的同时充放热“漏洞”如果充热效率比放热效率高很多模型理论上可能出现先充后放“白赚”效率差的投机行为。比如充效率0.98、放效率0.95充100放95热平衡上看不出问题但能量上亏了5%实际系统不会这么干。解决思路有两个要么给充放热功率加整
RELATED READING

延伸阅读

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