ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

考虑特性分布的储能接入电网多时间尺度源储荷协调调度策略

考虑特性分布的储能接入电网多时间尺度源储荷协调调度策略 最近两年我一直在跟储能参与电网调度的项目打交道发现一个很有意思的现象很多人做“储能调度”的时候还是把储能电站当成一个“大水缸”来用——只有一个总容量、一个总功率一条SOC曲线跑天下。但实际上储能电站内部是几十上百个PCS和电池簇的组合各个簇的健康状态、可用容量、SOC区间可能差异很大把它们当成完全同质的单体去调度要么浪费容量要么容易把某些簇充坏。这正是“考虑特性分布的储能电站接入的电网多时间尺度源储荷协调调度策略”这个方向要解决的核心问题把储能特性分布真正写进优化模型再放到多时间尺度的源-储-荷协调框架里去求解最后用Matlab把整套策略跑通。这篇文章我会从问题背景、建模方法、三层协调架构、Matlab实现细节和我实际跑代码踩过的坑五个部分来讲适合正在做电力系统优化调度方向课题、或者准备用Matlab搭建储能调度仿真平台的研究生和工程师参考。1. 为什么要把储能电站当成“一群电池”而不是“一台电池”1.1 新能源渗透率上来以后电网的调峰调频困境风电和光伏的出力本质上是一个“看天吃饭”的过程而且跟负荷的走势往往错位。白天光照强的时候工商业负荷还没到顶峰光伏却呼呼往上涨晚上负荷上来了光伏又归零。这就导致净负荷曲线呈现出典型的“鸭型曲线”——午后谷底傍晚陡升。传统火电机组有最小技术出力限制有爬坡率限制要它在几个小时内大幅升降出力成本和风险都不小。正常情况下调度员手里的手段有三个调节火电、调节水电、切负荷。但风电光伏装机比例越来越高以后光靠火电爬坡去追净负荷经常会出现“顶峰爬不动、低谷压不下”的窘境。储能电站接入以后相当于给电网加了一个“快速调节器”低谷时充电、高峰时放电从原理上可以大大缓解调峰压力。不过储能电站不是一台单独的大电池它是一个由主变压器、升压变、PCS、电池簇、BMS、EMS组成的完整系统。一个百兆瓦时级的储能站内部可能有几十台PCS、上百个电池簇。我跟现场人员交流的时候发现他们最头疼的不是充放电策略的“大方向”而是同一时刻哪些簇能充、哪些簇不能充、充多少不会触发热失控保护。这就引出了“特性分布”的概念。1.2 储能电站内部的非一致性到底是怎么来的储能电站里的电池簇不是同一天出厂的就算同一批货运行环境也不完全一样。靠近集装箱风口的温度低一些靠里面的温度高一些经常处于高SOC区间的簇衰减得快一些均衡做得好的簇健康状况就好一些。这些差异会体现在四个层面SOH差异电池健康状态不同实际可用容量就会不同。一个SOH 85%的簇标称100Ah实际只能放出85Ah。如果调度模型里统一按100Ah去算真实运行时这个簇必然提前放空触发BMS保护。SOC区间的可用性差异有些簇因为均衡策略的原因可运行的SOC范围是10%-90%有些因为长时未均衡只能跑30%-80%。聚合模型不会区分这些但实际运行时每一个簇都有自己的保护区间。功率响应能力差异电池簇的内阻不同相同PCS功率指令下老簇的端电压波动更大更容易触及电压保护限值。换句话说不是每个簇都能按额定功率满打满算去执行。寿命衰减速度差异高温区域、高倍率运行的簇寿命衰减更快。调度策略如果总是让所有簇平均出力实际上是在把健康簇的寿命补贴给不健康簇。传统做法是把整个储能电站等效成一台“理想电池”给定总容量、总功率、总SOC然后按平均SOC判断能不能充放电。这在理论层面很简洁但到了工程层面就会踩坑某天调度指令要求储能充电20MWh聚合模型算下来总SOC在安全范围内可实际站内有几个簇已经处于SOC 95%的保护状态根本充不进去另外几个簇SOC偏低充电电流又过大会导致内部温升过高。BMS为了保护电池只能降功率或者切除部分簇最后实际充进去的可能只有计划的六成。这就是不考虑特性分布的代价。1.3 源、储、荷三方各自的时间响应特性差异多时间尺度协调调度的本质原因是电网里不同资源的时间响应特性完全不一样。火电机组从接到AGC指令到出力变化到目标值需要经历锅炉燃烧、汽轮机调速、发电机励磁等一系列过程分钟级到十几分钟级才算真正稳住水电机组快一些但也受来水流量和水头变化影响储能本质上是一个电力电子设备毫秒级就能响应但它的能量是有限的需要提前预留足够的充放电空间负荷侧的空调、热水器、工业可中断负荷响应速度各有差别还要考虑人的舒适度和生产工艺约束。这些不同响应速度的资源如果放在同一个时间尺度上做决策会出现一种矛盾时间尺度取大了储能和负荷的快速调节能力被“平均化”掉了没法发挥它们跟踪波动的优势时间尺度取小了火电这种慢机组的启停和爬坡约束又很难在短周期内全局优化。所以更合理的做法是分层日前做全局粗决策日内做滚动修正实时做快速分配。三层各管一段各取所长。这也是“多时间尺度”这几个字真正的意义。2. 特性分布如何进入优化模型从物理量到数学约束2.1 用逐簇变量而不是聚合变量去描述储能要把“特性分布”写进调度模型最直接的做法是放弃“一个储能电站一个变量”的建模方式改成逐簇建模。假设站内共有 (N_e) 个电池簇每个簇 i 在时刻 t 的变量包括充放电功率、SOC、SOH、可用容量等。这样做的好处是所有非一致性差异都能显式表达。举个例子簇 i 的可用容量可以写成 (E_{i,t} E_{rated,i} \cdot SOH_{i,t})其中 (E_{rated,i}) 是额定容量(SOH_{i,t}) 是时变健康状态。如果聚合建模你只能用 (E_{total,t} \sum E_{rated,i})自然没法体现SOH差异。另外每个簇的SOC上下限可以写成 ([SOC_{min,i}, SOC_{max,i}])而不用被迫所有人共用同一个 ([SOC_{min}, SOC_{max}])。电源侧和负荷侧同样可以用分模型来描述。源侧包括火电、风电、光伏、水电每个机组有自己的出力上下限、爬坡约束、最小启停时间风电光伏在日前用预测曲线在日内用超短期预测修正。负荷侧可以把一部分柔性负荷建出来比如可转移负荷的总电量约束和单时段最大调整量约束。2.2 目标函数不是只追“成本最低”这么简单常规调度模型的目标函数一般写系统总运行成本最小包括火电煤耗成本、机组启停成本、弃风弃光成本、失负荷惩罚等。加入储能特性分布以后还要把储能寿命折损成本放进来。否则优化器会很“聪明”地让某些簇高频深度充放电表面上运行成本很好实际上电池寿命被大幅压缩。关于储能寿命折损学术和工程界常用两种近似方式。一种是基于吞吐量的线性折损(C_{life} \sum_{i,t} \lambda_i \cdot |P_{i,t}^{dis}| \cdot \Delta t)用一个每MWh损耗成本系数 (\lambda_i) 来刻画。SOH低的簇 (\lambda_i) 大一些优化器自然倾向于少调用它。另一种是建立SOC与循环次数的等效老化模型更精确但非线性更强在Matlab里实现时通常需要分段线性化处理。综合下来目标函数可以写成[ \min \sum_{t1}^{T} \left[ \sum_{g \in G} (a_g P_{g,t}^2 b_g P_{g,t} c_g) \sum_{i1}^{N_e} \lambda_i (P_{i,t}^{ch} P_{i,t}^{dis}) C_{curtail,t} C_{load_cut,t} \right] ]其中 (C_{curtail,t}) 是弃风弃光惩罚(C_{load_cut,t}) 是切负荷惩罚。注意这里的储能寿命成本是按簇累计的不是按全站总额定功率近似——这就是特性分布在目标函数层面的体现。2.3 关键约束怎么写才不会被求解器“钻空子”约束条件是调度模型里最考验细节的部分。我按源、储、荷三类来梳理源侧约束每台火电机组出力上下限 (P_{g,min} \le P_{g,t} \le P_{g,max})爬坡约束 (-R_g^{down} \le P_{g,t1} - P_{g,t} \le R_g^{up})风电光伏的出力区间按预测值给上限。这里要注意风电光伏在目标函数中不占煤耗成本所以容易在约束松的时候被大量消纳这是好事但要确保不会违反系统旋转备用约束。储能侧约束这是特性分布建模的重点。每个簇的功率约束[ -P_{i}^{dis,max} \le P_{i,t}^{dis} \le 0, \quad 0 \le P_{i,t}^{ch} \le P_{i}^{ch,max} ]而且同一个簇不能同时充放电[ P_{i,t}^{ch} P_{i,t}^{dis} P_{i,t}^{net}, \quad P_{i,t}^{ch} \cdot P_{i,t}^{dis} 0 ]实际建模时为了避免乘积引入非线性通常引入0-1变量 (u_{i,t}^{ch}) 和 (u_{i,t}^{dis}) 来做互斥约束。SOC动态方程[ SOC_{i,t1} SOC_{i,t} \frac{\eta_{ch} P_{i,t}^{ch} \Delta t}{E_{i}} - \frac{P_{i,t}^{dis} \Delta t}{E_{i} \eta_{dis}} ]每个簇的SOC上下限按各自的特性区间 (SOC_{min,i} \le SOC_{i,t} \le SOC_{max,i}) 来约束。有些文献还会加一个末尾时刻SOC回归约束比如 (SOC_{i,T} \ge SOC_{i,0})这是为了确保第二天有充足的调节空间非常实用。荷侧约束可中断负荷的最大调整量、可转移负荷的总能量守恒、需求响应后的舒适度温度区间等都按实际可控范围建模。我在实际项目里常把负荷分成“刚性负荷柔性负荷”两部分柔性负荷只占总负荷的一小部分比如5%-10%但恰恰是这一小部分帮了大忙。3. 多时间尺度协调调度的三层架构日前、日内、实时到底各管什么3.1 为什么不能只做一个时间尺度如果只做一个日前调度把未来24小时一次性算完最大的问题是对预测误差无能为力。风电光伏的日前预测误差在15%-25%之间很常见等第二天实际运行时真实的净负荷可能跟预测值差很多日前定好的储能充放电计划就错位了。如果只做实时调度完全不考虑未来就会出现“短视困境”储能很可能在下午低谷时把电放光了到了晚上高峰反而没电可用或者火电频繁启停来应对短时波动经济性非常差。所以合理的框架必须是三层递进每层负责不同的时间尺度和决策粒度。3.2 三层架构的决策内容与信息传递在我实现过的模型里三层分别是这样设置的日前调度层时间尺度为未来24小时分辨率1小时24个时段。决策变量包括火电机组启停计划、机组基点功率、储能各簇的充放电计划、柔性负荷的日前安排。这个阶段的风电光伏数据用日前预测曲线。目标函数是整体经济成本输出结果作为日内层的“基准轨迹”。日内滚动层滚动窗口设为未来4小时分辨率15分钟每15分钟滚动优化一次。用超短期预测数据修正日前计划主要调整储能和柔性负荷火电因为启停状态已经确定了就不轻易动只微调出力。这层本质上是带约束的MPC把日前层的结果当作参考轨迹尽量贴近同时处理预测偏差。实时调度层时间尺度为未来15分钟到1小时分辨率5分钟或1分钟。接入实测的负荷、风电光伏数据主要动作是AGC分配把储能电站的调节量分解到具体PCS和电池簇同时监测有没有簇触及保护限值需要切除。这一层在Matlab仿真中通常是简化的重点看功率分配逻辑是否正确。三层之间的信息传递很关键。日前层给日内层传“计划值”日内层给实时层传“修正量”和“可调裕度”。可调裕度是一个特别实用的概念每个时段储能还能充多少、还能放多少、火机还有多少上调下调空间这些信息汇总成一个“安全走廊”。实时层只要在这个走廊内操作系统全局就是安全的。3.3 各层之间的滚动协调如何避免“计划漂移”滚动优化做多了会有一个典型问题——计划漂移。简单说就是日内层每15分钟滚动一次每一轮都在修正计划如果修正权重设置不当修正后的出力曲线会逐渐偏离日前最优值导致火电的煤耗上升储能的SOC调度也变得不可预测。解决计划漂移的常用办法是在日内层的目标函数中加入向日前计划靠拢的偏移惩罚项。比如实时出力偏离日前计划值时增加一个二次惩罚[ J_{intra} J_{actual} \sum_{t} \rho \left( P_{g,t}^{intra} - P_{g,t}^{dayAhead} \right)^2 ]这个惩罚系数 (\rho) 不能太大太大会让日内层失去修正能力预测误差来了也不敢动也不能太小太小就漂移了。我一般先设 (\rho0.1)观察实际修正效果再调节。这个细节在实际代码调参的时候特别重要。4. Matlab代码实现的关键工程细节4.1 模型求解器的选型与建模语言Matlab环境下做优化调度目前最常见的组合是Yalmip Gurobi/Cplex。Yalmip是一个建模工具箱它最大的好处是让你可以用接近数学表达的方式写约束和目标而不用手动把一切转化为矩阵形式大幅降低出错概率。Gurobi和Cplex都是商业求解器对大规模MILP问题求解速度极快。如果你的课题组没有商业求解器授权可以先用Yalmip内置的sedumi或glpk验证小规模模型等模型跑通了再切到Gurobi跑大规模算例。我自己的习惯是用R2023b版本Yalmip用的是最新版。安装方式不复杂把下载的Yalmip文件夹加入Matlab路径即可。有个小坑Yalmip老版本对optimize函数的返回参数定义跟新版本略有差异如果报fun相关错误优先检查Yalmip版本别一上来就怀疑自己的模型错了。4.2 代码结构怎么拆分才不容易乱一个完整的调度仿真程序我习惯拆成五个模块模块名主要作用关键函数/脚本01_case_parameters定义系统参数、机组参数、储能簇参数、负荷参数case_parameters.m02_scene_generation生成风电、光伏、负荷的场景数据wind_pv_load_data.m03_model_definition构建目标函数与约束条件build_model_dayAhead.m04_solver_core调用优化求解器并提取结果run_optimization.m05_result_analysis展示调度结果、绘制图表、计算指标plot_schedule.m每个模块独立成文件通过函数接口传递数据这样后续想改储能簇数量、想换场景数据、想调时间尺度都不用动核心模型代码。4.3 关键代码段逐簇储能建模怎么实现假设站内有10个电池簇每个簇有独立的额定容量、初始SOC、SOH、充放电效率。建模的核心是给每个簇单独定义变量和约束。% 储能簇参数定义 N_e 10; % 簇数量 E_rated 5 * ones(1, N_e); % 额定容量 MWh SOH [0.95, 0.92, 0.88, 0.97, 0.90, 0.85, 0.93, 0.91, 0.89, 0.96]; % 健康状态 E_avail E_rated .* SOH; % 实际可用容量 MWh SOC0 0.5 * ones(1, N_e); % 初始SOC SOC_min 0.1 * ones(1, N_e); % 各簇SOC下限 SOC_max 0.9 * ones(1, N_e); % 各簇SOC上限 P_ch_max 1.2 * ones(1, N_e); % 各簇最大充电功率 MW P_dis_max 1.2 * ones(1, N_e); % 各簇最大放电功率 MW % 定义变量 T 24; % 日前调度时段数 P_ch sdpvar(N_e, T); P_dis sdpvar(N_e, T); SOC sdpvar(N_e, T 1); u_ch binvar(N_e, T); % 充电状态 u_dis binvar(N_e, T); % 放电状态接着写SOC动态约束和互斥约束用循环方式逐簇逐时段构建Constraints []; for i 1:N_e Constraints [Constraints, SOC(i, 1) SOC0(i)]; for t 1:T % 功率上下限 Constraints [Constraints, 0 P_ch(i, t) u_ch(i, t) .* P_ch_max(i)]; Constraints [Constraints, 0 P_dis(i, t) u_dis(i, t) .* P_dis_max(i)]; % 同时充放电互斥 Constraints [Constraints, u_ch(i, t) u_dis(i, t) 1]; % SOC动态 Constraints [Constraints, SOC(i, t1) SOC(i, t) ... (eta_ch * P_ch(i, t) - P_dis(i, t) / eta_dis) * delta_t / E_avail(i)]; % SOC上下限 Constraints [Constraints, SOC_min(i) SOC(i, t1) SOC_max(i)]; end end目标函数里加储能寿命折损lambda 50 * (1 (1 - SOH)); % 每一簇的寿命折损成本系数 Cost_storage sum(sum(lambda .* (P_ch P_dis) * delta_t));这里lambda的设置就是特性分布的体现SOH越低的簇调度成本系数越大优化器自然会优先调用健康簇减少不健康簇的深度充放电。整条逻辑非常直觉也不难实现。4.4 三层模型如何串联三层模型串联的关键是数据接口。我的做法是先跑日前模型把所有变量的结果存入结构体result_DA。日内模型读取result_DA中未来4小时的计划值作为参考轨迹加上超短期预测偏差重新优化储能和柔性负荷。实时模型读取日内模型最新结果的前15分钟计划按AGC增量分配逻辑分解到每个PCS。在实际代码中我会用一个for循环模拟全天的日内调度过程每15分钟调用一次optimize。仿真一天的耗时主要取决于变量规模和求解器性能。10簇、24时段、0-1变量480个的日前模型Gurobi大概几秒到十几秒就能解完日内模型窗口短、变量少单次求解亚秒级就能完成。5. 跑通代码后的避坑经验5.1 场景数据的选择直接决定结论的可靠性调度策略好不好很大程度取决于你用的风电光伏场景数据。我见过不少代码用一条正弦波或者一条理想化曲线做测试出来的调度结果当然漂亮但拿到真实数据上一跑就露馅。更稳妥的做法是拿当地电网的真实历史数据做聚类分析挑出典型日场景。比如把一年365天的风电出力聚类成3-5类典型场景再对应生成负荷场景。如果要做不确定性分析可以在此基础上加扰动在预测曲线上叠加正态分布扰动项 ( \epsilon \sim N(0, \sigma^2) )通过蒙特卡洛采样生成多个日内场景逐个跑日内层模型统计系统的弃风率和失负荷率。5.2 SOC动态方程的一致性是最容易出错的点逐簇建模之后SOC更新公式里有一个非常容易忽略的问题公式里的 (E_{avail}) 到底该用额定容量还是SOH修正后的容量。如果用额定容量来算SOC得到的SOC会虚高因为分母没有考虑容量衰减如果某些簇SOH只有85%它的SOC从100%放到0%实际放出的电量只能是额定容量的85%但SOC计算如果按100%算调度结果就会高估可用能量。我实际调试时因为这个坑浪费过两天时间最后给每条SOC曲线加了个全放空验证才定位到问题。另一个细节是充放电效率的位置。SOC动态公式有两种写法效率在功率前或者效率在功率后。两种写法物理意义不同尤其对于大功率充放电场景日终SOC差异非常明显。我建议用[ SOC_{t1} SOC_t \frac{\eta_{ch} P_{ch} \Delta t}{E} - \frac{P_{dis} \Delta t}{E \eta_{dis}} ]这个写法的含义是充电时电量乘以效率存入放电时按效率折算消耗更符合实际物理过程。5.3 求解器报不可行怎么排查模型写完之后第一次跑optimize报不可行infeasible几乎是必然的。我的排查套路是分三步第一步把目标函数改为常数0只求解可行性问题。如果仍然不可行说明约束之间存在矛盾。第二步把储能的SOC上下限放宽到[0, 1]把功率约束全部乘以一个松弛系数比如放宽1.2倍逐步缩小问题范围看是哪条约束卡住。第三步检查时段衔接处的约束尤其是日前模型结束时SOC是否必须回到初始值附近这个约束在包含末时段时容易与其他约束冲突。有次我在加“末尾SOC回归”约束时忘了考虑SOH导致的容量下降结果调度策略为了让SOC 1.0回归到0.5强行让高SOH簇多充、低SOH簇多放约束互相牵制整体不可行。后来把回归目标按各个簇的可用容量加权问题就解决了。5.4 计算规模太大时怎么剪枝考虑特性分布的逐簇建模本质上是拿计算复杂度换建模精度。如果储能站有100个簇逐簇建模的变量数和约束数都会暴涨Gurobi也不一定吃得消。工程上常用的做法是“分档聚簇”按SOH和SOC区间把100个簇聚成3-5类每类内部参数取代表值这样的话模型变量规模从100个簇降到5个簇计算速度大幅提升物理含义也基本保留。另外还可以使用对称性削弱方法。如果若干个簇参数完全相同它们之间在优化问题上是对称的会增加分支定界树的搜索量。可以加一个排序约束把变量顺序锁定比如 (P_{i,t} \ge P_{j,t})从结构上削弱对称性。这类技巧在MILP求解中很管用。5.5 结果可视化要注意什么调度结果画图的时候除了常规的电源出力堆叠图、储能SOC曲线图我强烈建议加一张“储能簇级充放电分配热力图”。横轴是时间纵轴是簇编号颜色代表充放电功率大小。这张图能非常直观地看出特性分布是否真的起作用了SOH低的簇应该明显“冷”一些SOH高的簇承担更多调节任务。如果所有簇颜色几乎一样说明模型里特性分布的约束没有真正激活得回头检查参数设置。6. 一些个人经验最后分享一点我自己的体会。做这种调度优化课题不要一上来就追求模型面面俱到。先把不考虑特性分布的聚合模型跑通得到基准结果再逐簇建模把SOC动态和SOH折损加进去对比两边结果的差异最后再加多时间尺度滚动结构。每加一层都单独验证输出指标是否合理。这样出问题的时候你能清楚知道是建模层的问题、求解器的问题还是参数的问题而不是混在一起无从下手。如果你正准备用Matlab复现这个策略建议先搭一个小算例3台火电、2台风机、1个光伏电站、1个储能站里面分5个簇、若干柔性负荷跑通三层调度流程再逐步扩大系统规模。小算例跑起来只要几秒调试体验好得多。储能特性分布这个方向表面上看是建模精度问题实际上是在回答一个更现实的问题在储能设备老化差异不可避免的情况下怎么把每一度电的能量安全、高效地调度出去。这个问题的答案值得用细致的模型和扎实的代码慢慢打磨。
返回列表