ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

共享储能优化配置与调度:碳交易与波动惩罚的Matlab实现

共享储能优化配置与调度:碳交易与波动惩罚的Matlab实现 做电力系统优化的人应该都有这种体感储能电站的论文这两年多到看标题就想划走但真正能直接拿去改改参数、换换数据就能跑的代码反而少见。这篇要聊的模型标题很长——《考虑碳交易与电网交互波动惩罚的共享储能电站优化配置与调度模型研究Matlab代码实现》但落点其实很实在当储能电站不是给单一风电场或光伏电站服务而是要同时应对多个用户、还要把碳交易收益和电网交互功率波动一起塞进优化目标时容量怎么配、功率怎么调、收益怎么算这三个问题会互相纠缠。这篇文章我会用MatlabYalmip的视角把模型从问题拆解到代码实现讲清楚包括每一层的决策变量、约束条件怎么写、波动惩罚项怎么处理才不会把求解器搞崩以及跑算例时最容易踩的几个坑。这篇内容的适用对象很明确正在做储能优化配置方向毕业设计或论文复现的研究生以及刚接触共享储能商业模式、想把碳交易机制纳入规划的工程师。如果你只是想知道共享储能能赚多少钱这篇可能偏硬但如果你需要的是一个能落地、能复现、能改参数出图表的建模思路那这篇应该能帮你省下不少翻文献的时间。1. 这个模型到底在解决什么问题共享储能背后的三重矛盾1.1 共享储能为什么比自建储能更值得研究先说共享储能电站和传统储能电站的本质区别。传统储能通常是新能源场站自建目的很单纯弃风弃光多了就充电峰谷电价差大了就放电收益来源单一容量配置只需要服务自家场站。而共享储能电站是独立投资、独立运营的第三方主体它同时服务多个新能源场站或多个用户侧主体通过容量租赁、调峰辅助服务、峰谷套利等渠道回收成本。这种模式的好处是提高了储能利用率。自建储能的利用率受限于单个场站的发电曲线而共享储能可以把多个场站的负荷曲线错峰叠加电池的充放电次数和能量吞吐量都能上去。但问题也随之而来服务对象多了优化配置就不能再拍脑袋定容量必须考虑不同用户的用电特性、消纳需求、以及储能调度策略如何影响长期收益。所以共享储能的优化配置天然是个配置调度耦合的问题——配置决定调度的物理边界调度决定配置的经济回报。1.2 碳交易怎么切入储能的经济性评估碳交易是这两年电力系统优化模型里最常被加进去的机制。它的逻辑链条是这样新能源发电替代火电出力减少了碳排放这部分减排量可以在碳交易市场转化为收益反过来如果系统还得依赖火电平衡那就要购买碳排放配额。共享储能在这个过程中扮演的角色是减排催化剂——储能吸收多余新能源电量、减少弃电相当于间接替代了火电出力所以在目标函数里可以给储能一个碳收益项。但注意储能的碳收益不是直接少排了多少吨碳而是围绕系统的碳排放配额与实际排放量差值来计算的。这个差值怎么定义直接决定模型的目标函数形式。有的论文用基准线法有的用配额法我这里用的是比较常见的配额法系统根据发电量或用电量获得初始配额实际碳排放量超过配额就要在碳市场购买低于配额就可以出售获利。1.3 电网交互波动惩罚被很多论文忽略的隐性成本第三个矛盾是储能电站与外部电网的交互功率波动。很多基础配置模型只关心储能自身的充放电功率、SOC约束、以及收益最大化却忽略了一个工程现实储能电站从电网取电或向电网送电的功率曲线如果波动过于剧烈会对电网造成冲击尤其在接入点比较薄弱的区域这种冲击会被调度中心视作不合格的并网行为。电网交互波动惩罚就是对这种工程约束的经济化表达。模型里加一个惩罚项当储能与电网交互功率的变化率超过某个阈值时惩罚成本上升从而在优化过程中逼着储能调度曲线变得更平滑。这也是这个标题里最有区分度的一点——大部分基础储能配置模型不会考虑这个约束而实际工程中它往往比碳交易更影响调度结果。2. 双层模型拆解上层定容量下层定曲线2.1 上层决策变量容量配置不是拍脑袋共享储能电站的优化配置通常采用双层模型结构英文里叫Bi-level Optimization。上层是规划层决策变量可以简化为一组储能额定功率 (P_{ess}^{rated})单位MW储能额定容量 (E_{ess}^{rated})单位MWh各用户/新能源场站的容量租赁比例或接入容量上层的目标函数一般是全寿命周期内的净收益最大化或综合成本最小化。成本项包括初始投资成本功率成本容量成本、运行维护成本、充电购电成本收益项包括向用户收取的容量租赁费、峰谷套利收益、辅助服务收益、碳交易收益。这里要注意投资成本是一次性成本但收益是逐时段的所以需要用等年值法或折现率把全寿命周期的现金流折算到同一年上去。常见做法是使用等年值系数把初始投资成本折算成年值再与年运行收益相加。2.2 下层调度约束充放电功率、SOC与生命周期下层是运行层决策变量是每个调度时段 (t) 的充放电功率、是否充电的0-1状态变量、以及各时段与电网交互的购售电功率。约束条件比上层复杂得多主要包括储能SOC递推约束[ SOC_{t1} SOC_t (P_{ch,t} \cdot \eta_{ch} - \frac{P_{dis,t}}{\eta_{dis}}) \cdot \Delta t / E_{ess}^{rated} ]充放电功率上下限[ 0 \le P_{ch,t} \le P_{ess}^{rated} \cdot u_{ch,t}, \quad 0 \le P_{dis,t} \le P_{ess}^{rated} \cdot u_{dis,t} ] 其中 (u_{ch,t} u_{dis,t} \le 1)保证同一时段不能同时充放电。SOC上下限[ SOC_{min} \le SOC_t \le SOC_{max} ]调度周期始末SOC相等保证储能可持续运行。各新能源场站/用户的功率平衡约束。这里有个工程经验要提SOC的上下限不建议直接取0和1锂离子电池的实际运行区间一般取0.1~0.9。这个区间设置直接影响电池循环寿命也让调度模型更接近真实。稍微复杂一点的模型还会把SOC分段线性化用来估算电池健康状态衰减但如果只是做配置层面的优化固定效率固定SOC区间的模型已经够用。2.3 双层耦合关系投资决策如何影响运行边界双层模型的核心难点在于上下层之间的耦合关系。上层选定的 (P_{ess}^{rated}) 和 (E_{ess}^{rated}) 会直接进入下层约束作为充放电功率和SOC递推公式的边界参数而下层的调度结果——各时段的充放电量、碳交易量、与电网交互功率曲线——又会反过来决定上层目标函数里的运行收益项。这种双向耦合导致没法把两层各自单独求解必须通过迭代或数学转化手段统一求解。在Matlab实现中处理双层模型有三种常见策略第一种是用KKT条件把下层模型转化为约束后并入上层第二种是采用粒子群、遗传算法等启发式算法做外层寻优内层调用线性规划或二次规划求解调度第三种是如果模型规模不大直接枚举容量候选集用配置-调度-评估的循环暴力搜索。实际代码实现中第二种最多因为KKT转化对非凸问题很容易出问题而暴力搜索在网格粒度稍细一点时就慢到怀疑人生。后面我会专门讲为什么我最终选了启发式规划器嵌套的方案。3. 碳交易机制的数学建模配额、碳价与收益3.1 碳交易的核算口径碳交易机制要在优化模型里落地首先要确定核算口径。共享储能场景下我建议把整个园区或微网视为一个碳排放主体系统的碳排放主要来自从电网购电所对应的火电发电排放。设电网的综合碳排放因子为 (EF_{grid})单位是 kgCO2/kWh那么t时段系统从电网购电 (P_{buy,t}) 对应的碳排放量为[ E_{emissions,t} EF_{grid} \cdot P_{buy,t} \cdot \Delta t ]系统获得的初始配额 (E_{allowance}) 可以按年度总用电量的一定比例设定也可以与新能源发电量挂钩[ E_{allowance} \gamma \cdot (E_{load}^{total} E_{ess}^{charges}) ]其中 (E_{load}^{total}) 是用户总负荷电量(E_{ess}^{charges}) 是储能充电电量(\gamma) 是配额系数。实际碳排放量低于配额时剩余配额可以在碳市场出售高于配额时不足部分需要购买。这样碳成本/收益项就是[ C_{carbon} P_{carbon} \cdot (E_{emissions,total} - E_{allowance}) ]当 (E_{emissions,total} E_{allowance}) 时 (C_{carbon} 0)也就是收益。3.2 碳价与参数灵敏度碳价的设置对配置结果影响很大。现阶段国内碳市场成交价波动区间比较大从每吨几十元到上百元都有。文献里常用的是50~100元/吨的区间。如果你做敏感性分析可以从30元/吨扫到150元/吨看储能配置容量和总收益的变化趋势。以下是一组算例参数的参考取值我自己跑下来结果比较稳定参数取值说明电网碳排放因子 (EF_{grid})0.581 kg/kWh区域电网平均排放因子初始配额系数 (\gamma)0.85按总用电量的85%免费发放配额碳价 (P_{carbon})80 元/吨可按敏感性分析调整储能效率 (\eta_{ch}/\eta_{dis})0.95 / 0.95磷酸铁锂典型值储能单位功率成本800 元/kW含PCS等设备储能单位容量成本1200 元/kWh含电池组与BMS这里的配额系数0.85是个关键假设。如果取1.0那就是完全免费配额储能减少购电反而不会产生额外碳收益碳交易机制对优化结果的影响就弱很多。取0.8以下惩罚意味太强可能导致模型过度激励储能扩容。建议在这个参数上做敏感性分析而不是拍脑袋固定一个数。3.3 碳交易项在目标函数里的工程处理很多第一次建碳模型的同学会把碳排放约束写成硬约束例如系统碳排放不得超过配额X吨。实际求解时这种硬约束经常导致模型无解或可行域极小尤其当新能源出力差、必须从电网大量购电时。更好的做法是把碳交易项写成目标函数中的软性成本项让模型自动权衡购买配额和减少购电/增加储能哪个更经济。这也是商业上真实的决策逻辑——碳成本只是众多成本中的一项没有哪个企业会为了碳达标直接把生产线停了。4. 电网交互波动惩罚的刻画方式与参数讨论4.1 波动惩罚的三种常见数学形式电网交互波动惩罚是标题里比较有技术含量的一部分。先定义变量(P_{grid,t}) 表示t时段储能电站与电网的交互功率正值表示从电网购电负值表示向电网送电。波动惩罚要抑制的是相邻时段交互功率的剧烈变化也就是[ \Delta P_t |P_{grid,t} - P_{grid,t-1}| ]这个 (\Delta P_t) 的惩罚项有三种常见的写法我列个对比形式数学表达式优点缺点线性惩罚(C_{fluc} \lambda \cdot \sum |P_{grid,t} - P_{grid,t-1}|)线性模型好求解Yalmip直接用惩罚力度偏软可能出现小幅高频波动二次惩罚(C_{fluc} \lambda \cdot \sum (P_{grid,t} - P_{grid,t-1})^2)对大波动惩罚更重曲线更平滑引入二次项MILP变成MIQP阈值惩罚(C_{fluc} \lambda \cdot \sum \max(0, |P_{grid,t} - P_{grid,t-1}| - \Delta P_{max}))与实际并网考核规则一致需要引入辅助变量模型复杂度增加我实际项目中最常用的是线性惩罚原因很简单能够在保持MILP结构的前提下把惩罚项通过引入辅助变量 (u_t^, u_t^-) 拆成两个正偏差变量[ P_{grid,t} - P_{grid,t-1} u_t^ - u_t^-, \quad u_t^, u_t^- \ge 0 ][ |P_{grid,t} - P_{grid,t-1}| u_t^ u_t^- ]这样波动惩罚项就变成了线性约束Gurobi或Cplex求解时不会破坏MILP结构。如果只是单纯在目标函数里加绝对值不引入偏差变量Yalmip里也能通过abs()函数直接写但个人建议还是显式写出辅助变量和约束方便调试也方便看拉格朗日乘子和对偶变量的值。4.2 惩罚因子选多大才合理惩罚因子 (\lambda_{fluc}) 的取值没有统一标准它本质上是一个权重用来权衡平滑曲线和牺牲套利收益两者之间的关系。我用过的经验范围是(\lambda_{fluc}) 取值为峰谷电价差单价的10%~50%。举个例子如果峰谷套利的电价差是0.7元/kWh那么 (\lambda_{fluc}) 设在0.07~0.35元/kWh之间比较合理。设太小惩罚项可有可无储能调度曲线基本跟不设惩罚一样可能在午间光伏大发时段出现功率大起大落设太大模型会过度平滑导致储能几乎不在电价尖峰时段放电配置容量也跟着缩水。最终目标应该是保证调度曲线平滑度明显提升同时总收益下降不超过5%~10%在这个区间内取一个平衡点。这里有一个快速检验方法先跑一遍不带波动惩罚的模型统计相邻时段交互功率差值的平均绝对值再跑带惩罚的模型如果平均波动幅度下降了30%以上且收益损失在可接受范围内说明惩罚系数取得合适。4.3 波动惩罚项对配置结果的影响逻辑波动惩罚不仅影响调度曲线还会反过来影响容量配置。这个传导逻辑值得展开说一下。上层在做容量优化时它要评估多配一组储能电池能不能带来额外收益。没有波动惩罚时新增容量可以在电价低谷大量充电、高峰大量放电边际收益明显但有波动惩罚后储能在高峰放电后不能立刻在低谷充电充放电之间要有爬坡过渡这就导致储能利用率下降等效循环次数减少边际收益也跟着下降。所以在高波动惩罚系数下上层模型最终选出的额定容量往往会比无惩罚时低10%~20%同时最优功率容量比即储能时长会略微拉长。这个趋势如果没在论文里讨论到审稿人大概率会问。实际复现时你应该画一张不同惩罚因子下最优配置结果对比表非常直观也很有说服力。5. Matlab代码实现路线从目标函数到求解器调用5.1 建模前的数据准备Matlab实现的第一步不是写代码而是把数据整理成能被Yalmip识别的格式。我们需要的典型数据文件包括负荷数据24h或8760h的用户侧负荷曲线单位kW新能源出力数据风电/光伏的归一化出力序列分时电价表峰、平、谷时段的购电价和售电价电网交互功率的初始上下限值碳交易参数配额系数、碳价、排放因子储能技术参数充放电效率、SOC范围、成本系数这里最花时间的往往是新能源出力数据的清洗。实际数据里经常有缺失值、负值异常和零值密集段。我在第一次跑这个模型时直接用原始光伏出力数据结果调度模型把储能安排得乱七八糟——因为凌晨时段偶尔冒出几个尖峰毛刺模型以为是可充电源。后来做了移动平均平滑和限幅处理结果才正常。建议所有时序数据在进入模型前先画图看一眼确认趋势合理再做归一化。5.2 用Yalmip写上层配置-下层调度循环的骨架下面这段是我常用的共享储能优化配置代码框架YalmipGurobi组合。这里只展示核心结构重点看双层迭代和惩罚项是怎么落地的。%% 参数初始化 T 24; % 调度时段数 N_pop 30; % 粒子群种群数量 max_iter 50; % 外层迭代次数 % 储能成本参数 c_power 800; % 单位功率成本 元/kW c_energy 1200; % 单位容量成本 元/kWh life_year 10; % 寿命年限 r_discount 0.08; % 折现率 % 电网交互波动惩罚系数 lambda_fluc 0.15; % 元/kWh % 分时电价峰平谷三段 price_buy [0.35*ones(T/3,1); 0.8*ones(T/3,1); 1.2*ones(T/3,1)]; price_sell price_buy * 0.85; %% 外层粒子群算法优化上层配置 for iter 1:max_iter for k 1:N_pop % 从种群中取出候选配置 P_rated pop_power(k); % 候选额定功率 kW E_rated pop_energy(k); % 候选额定容量 kWh % 内层求解当前配置下的最优调度MILP [revenue, P_grid] solve_dispatch(P_rated, E_rated, ... load_data, pv_data, price_buy, price_sell, ... lambda_fluc, carbon_params); % 计算上层净收益 invest_cost c_power * P_rated c_energy * E_rated; annual_invest invest_cost * (r_discount*(1r_discount)^life_year) ... / ((1r_discount)^life_year - 1); fitness(k) revenue - annual_invest; end % 更新粒子群位置、速度此处省略标准PSO更新代码 end内层调度函数solve_dispatch用Yalmip建模关键片段如下function [revenue, P_grid] solve_dispatch(P_rated, E_rated, ...) %% 定义决策变量 P_ch sdpvar(T, 1); % 充电功率 P_dis sdpvar(T, 1); % 放电功率 u_ch binvar(T, 1); % 充电状态 u_dis binvar(T, 1); % 放电状态 SOC sdpvar(T1, 1); % SOC状态变量 P_buy sdpvar(T, 1); % 从电网购电功率 P_sell sdpvar(T, 1); % 向电网售电功率 delta_up sdpvar(T-1, 1); % 交互功率正向波动 delta_down sdpvar(T-1, 1); % 交互功率负向波动 %% 约束条件 C []; C [C, 0 P_ch P_rated * u_ch]; C [C, 0 P_dis P_rated * u_dis]; C [C, u_ch u_dis 1]; SOC(1) 0.2 * E_rated; % 初始SOC C [C, 0.1*E_rated SOC 0.9*E_rated]; for t 1:T C [C, SOC(t1) SOC(t) (P_ch(t)*0.95 - P_dis(t)/0.95)]; end C [C, SOC(T1) SOC(1)]; % 周期始末SOC一致 %% 电网交互功率与波动惩罚约束 P_grid P_buy - P_sell; C [C, 0 P_buy 5000, 0 P_sell 5000]; for t 2:T % 相邻时段交互功率差值拆成正负偏差 C [C, P_grid(t) - P_grid(t-1) delta_up(t-1) - delta_down(t-1)]; C [C, delta_up(t-1) 0, delta_down(t-1) 0]; end %% 目标函数 revenue_elec sum(P_dis .* price_sell) - sum(P_ch .* price_buy); carbon_cost carbon_params.carbon_price * ... (sum(P_buy) * carbon_params.grid_ef - carbon_params.allowance); fluctuation_cost lambda_fluc * sum(delta_up delta_down); objective revenue_elec - carbon_cost - fluctuation_cost; %% 求解 ops sdpsettings(solver, gurobi, verbose, 0); optimize(C, -objective, ops); end这段代码里我要特别说明两个设计。第一SOC递推约束我直接用E_rated做了归一化这样SOC变量保持在0~1区间数值稳定性好第二电网交互功率我这里用P_buy和P_sell两个非负变量相减来表示这比用一个有正有负的变量更规范因为后续要区分购电和售电价格。5.3 为什么不直接做KKT转化很多人问为什么不用KKT条件把双层模型转化成单层MILP一起求解这个问题我实践之后的体会是KKT转化在理论上完美但工程实现时非常痛苦。下层如果包含二元变量KKT转化的强对偶条件基本没法直接写就算下层全是连续变量转化后的互补松弛约束会引入大量非线性项还得做大M法线性化而大M的取值又直接影响求解稳定性。相比之下粒子群/遗传算法外层寻优K层内MILP精确求解的组合更贴近工程实践。内层MILP本身是凸问题Gurobi可以在几十毫秒到几秒内求解完成外层种群设30~50个个体迭代50次也就是求解1500~2500次MILP总耗时在几分钟到一小时内。对于配置规划这类离线计算场景这个速度完全可接受而且代码可控性高出了问题容易定位。6. 跑通算例之后我踩过的几个坑6.1 非线性项处理的第一反应第一次写目标函数时我顺手把 (\Delta P_t) 的平方项直接写进Yalmip想着Gurobi能处理MIQP。结果在SOC区间约束和二进制状态变量叠加下求解时间从几秒飙到几百秒甚至频繁报数值问题。后来换成线性惩罚辅助变量拆分求解时间降回原来的水平结果曲线也很平滑。我的建议是除非二次型对结果有不可替代的意义否则优先选线性惩罚尤其在做大规模长时间序列8760h场景时线性化是必须的。6.2 双层迭代不收敛问题出在离散噪声外层用PSO时经常遇到迭代曲线锯齿状剧烈波动的现象。刚开始我以为算法参数没调好后来分析发现问题不在PSO而在内层调度问题对配置参数不连续。当额定功率从1200kW变成1210kW时内层MILP的最优调度可能跳变到完全不同的运行模式比如从每天两充两放跳到深度一充一放导致目标函数值突变。这种离散噪声会让标准PSO收敛很慢。我用的处理办法是在适应度评估里加一个小惩罚如果候选配置相比上一代全局最优的容量变化幅度超过30%在适应度函数里加一个探索惩罚项抑制粒子盲目跳跃。另外把粒子群粒子的速度上限设小一档比如速度限幅设为搜索空间宽度的10%收敛稳定性明显改善。6.3 结果合理性检验三个必须看的指标模型跑完不要直接拿结果去写论文先做三件事第一绘制储能的充放电功率曲线检查是否频繁在相邻时段出现充电-放电-充电的锯齿模式。如果出现说明或者电价数据有问题或者辅助服务收益项界定不清真实系统里不会有人这样操作电池。第二检查储能年循环次数。按我的经验日循环次数一般在0.8~1.5次之间折合成年循环次数为300~500次。如果你算出来的年循环次数超过800次说明模型在过度使用电池全寿命周期成本核算一定有问题实际电池早衰减了。第三做惩罚系数灵敏度测试。固定其他参数把 (\lambda_{fluc}) 从0逐步增加到目标值看最优配置容量是否单调不增。如果出现容量忽大忽小的非单调现象多半是双层嵌套求解时陷入了局部最优需要增大PSO种群规模或调整参数范围重新搜索。6.4 关于Matlab版本和求解器的几点备注YalmipGurobi的组合在Matlab环境里是跑这类模型的主力方案。我自己在用的Matlab版本是R2023a之后的版本Yalmip选的是GitHub上持续维护的版本Gurobi用的是学术授权。需要提醒的是几个环境兼容问题Yalmip对较新的Matlab版本偶尔会有适配滞后如果遇到求解器识别不了先检查Yalmip是否最新。Gurobi的Matlab接口在Windows和Linux下的安装路径不一样Linux下建议配置环境变量GUROBI_HOME后在startup.m里手动添加路径。如果机器内存有限8760h全年场景建议把SOC约束写成矩阵方式一次性添加不要用for循环逐条[C, C [C, ...]]拼接否则构建约束的时间比求解还长。这些坑大多不会出现在论文的模型描述里但复现过的人应该都懂。回到我自己的应用感受。共享储能配置与调度模型最迷人的地方不是单点技术多高深而是碳交易、电网波动惩罚、容量规划和时序调度这几件原本分散在不同体系里的事情能统一收拢到一个目标函数下互相权衡。把Matlab代码跑通只是第一步真正有价值的是在跑算例的过程中理解每个参数背后的工程含义——配额的松紧如何影响储能投资意愿波动惩罚的权重如何改变调度行为这些结论在同质化严重的储能论文里反而是最能让工作被记住的部分。希望这篇文章能帮你少走一些弯路。
返回列表