ARTICLE DETAIL

资讯详情

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

考虑电池特性分布的储能电站多时间尺度源储荷协调调度Matlab实现

考虑电池特性分布的储能电站多时间尺度源储荷协调调度Matlab实现 做电力系统优化调度的朋友应该都有这种感觉储能一接入问题就复杂了好几倍尤其是当站内储能不是一台新设备而是一批特性各异的电池单元时调度策略的构建就更头疼了。考虑特性分布的储能电站接入的电网多时间尺度源储荷协调调度策略这个题目本质上就是在解决两件事一是储能电站里那些SOH、内阻、自放电率各不相同的电池单体或电池簇如何在并网之后不拖累电网调度二是源、储、荷三方在日前、日内、实时三个时间尺度上如何协调才能既保证运行安全又把成本压下来。这篇文章我以Matlab代码实现为主线把整套思路的模型怎么建、约束怎么写、代码怎么搭、坑怎么避完整拆解一遍。适合正在做储能并网优化、微电网能量管理或者刚接触多时间尺度调度但又不想只看理论推导的研究生和一线工程师参考。在往下拆之前先说明我的一个基本立场调度策略这种题目论文里可以写得很漂亮但落到Matlab代码上拼的不是花哨的改进算法而是模型是否可解、数据是否对齐、边界条件是否物理。所以我全文都围绕能跑的模型来讲。1. 为什么要关注储能特性分布与多时间尺度协调1.1 储能特性分布从哪来对调度有什么影响储能电站投运几年后电池单体之间的差异会越来越明显。即便同一批出厂运行温度场不均、充放电深度不同、自放电率有差异都会导致SOH分散。SOH不一致带来的直接后果是可用容量不像铭牌那样整齐若按统一模型调度某些单元的SOC会先顶到上限导致整站实际可调功率低于计划值另一些可能被反复过放加速老化甚至触发保护。这就是考虑特性分布在工程上不是锦上添花而是保命需求的原因。在建模时我不会把所有储能等效成一个大电池——这在单台小容量储能的研究里勉强还能接受但站级储能这样处理误差会大到让人怀疑人生。更合理的方式是把储能单元按特性分群SOH相近、内阻相近、温度特性一致的归为一组每组用一个等效模型描述再在站控层汇总成调整后的可调功率区间。这样既不会引入几百个电池单体的爆炸式变量又能保留特性差异对调度边界的影响。可以说分群等效是考虑特性分布落地到优化模型里的桥梁。例如某个100MW/200MWh的独立储能电站统计电池簇SOH分布在85%到98%之间。如果按SOH90%这个平均值来算可用容量实际运行时SOH85%的簇会提前达到SOC上限整站可放功率立刻缩水而SOH98%的簇本来还有余量却因为平均化模型约束了它的出力。分群之后调度器知道哪几组能多充、哪几组能多放可调功率区间才真正反映了现场状态。1.2 多时间尺度到底解决什么问题电网调度天然是多时间尺度的。日前调度基于预测做24小时发电计划但风电光伏预测误差在6到24小时尺度上可能达到20%以上日内滚动预测误差缩小了但负荷波动依然存在到了实时运行阶段分钟级的爬坡和扰动又需要自动发电控制去消纳。储能参与后问题从机组跟着负荷走变成源、储、荷一起协调时间尺度不拉开计算规模会爆炸时间尺度不衔接调度指令会打架。用一个不太精确但好理解的类比日前计划相当于你早上按天气预报决定穿什么衣服日内滚动相当于出门前再看一眼窗外再调整实时调整则是到了楼下感觉冷临时加件外套。每一层的决策范围不同但最终目标都是同一个——在不确定性存在的情况下让系统走得稳、成本低。我经常被问为什么不用一个整体模型把所有时间尺度同时优化答案很简单第一变量规模会大到难以求解第二预测精度随时间尺度变化日前不知道15分钟后的细节实时又必须快速出解。多时间尺度的本质是渐进式决策用长时间尺度的粗计划定基调用短时间尺度的细调整来纠偏。1.3 源储荷协调为什么比传统源荷协调更优传统调度是源随荷动机组去追负荷曲线储能接入后多了一个可充可放、响应快的调节资源负荷也不再是被动接受者。源储荷协调的目标是让发电、储能、柔性负荷三方共同分担不确定性。例如风电大发时储能充电消纳弃风负荷高峰时储能放电削峰可平移负荷错峰生产预测偏差来临时储能和负荷一起响应机组不需要频繁大幅度爬坡。在成本上储能的度电成本虽然不低但相比让火电机组深度调峰、启停调峰很多场景下依然划算负荷侧的需求响应成本更低只是可调容量往往受限。把它们纳入同一优化框架本质上是在做一个多资源组合优化谁便宜谁先动谁响应快谁承担短时调节谁容量大谁做长时支撑。这种协调在Matlab实现里最终体现为目标函数里不同资源有不同的成本系数约束条件里给定各自的调节边界求解器在同一个模型内自动完成资源排序。你不需要拍脑袋决定先调储能还是先调负荷只要系数和边界设得对优化结果自然会告诉你答案。2. 数学模型这样建求解才不会跑偏2.1 决策变量与时间尺度的对应关系在建模型之前先要把决策变量按时间尺度分清楚。我习惯在Matlab中用矩阵维度来区分日前时间尺度决策变量是逐时或15分钟的机组出力、储能充放电功率、联络线功率、可平移负荷安排时间维度T_day 24或96日内时间尺度以未来4到6小时为滚动窗口15分钟一个时段只对当前窗口内变量做精细优化已经执行的日前计划作为硬性边界或软约束实时时间尺度5分钟甚至1分钟一个时段决策对象以储能响应和负荷微调为主机组出力通过爬坡约束限定在日前/日内计划附近避免机组频繁折腾。这三种尺度不是三个独立模型而是同一个模型的变体。关键区别在于滚动窗口大小、预测数据精度、决策变量自由度。我在代码里通常只写一个模型函数用输入参数控制时间尺度和预测数据而不是复制三套代码。这样的好处是修改约束或目标函数时只需改一处后面调试省很多事。这里有一个容易被忽视的建模问题跨时间尺度的变量耦合。例如日内滚动优化时当前时段之前的SOC、机组出力已经由日前计划决定不能再自由变化所以要把它们作为模型参数传进去。如果你把所有变量都定义成sdpvar求解器会认为历史状态还可以调整结果就会前后矛盾。我的做法是在模型函数入口用一组init变量传递已确定的状态。2.2 目标函数如何兼顾经济性与惩罚项目标函数一般分两大部分运行成本和惩罚项。运行成本包含火电燃料成本、储能充放电损耗成本、弃风弃光惩罚、需求响应补偿成本。最常见的目标函数是min ∑(C_g,i * P_g,i,t C_ch * P_ch,t C_dis * P_dis,t C_cur * P_cur,t C_load * P_load_shift,t)其中C_g,i可以是分段线性成本C_cur表示弃风弃光惩罚系数这个系数设置很关键。设太低模型会倾向于大量弃风设太高又会让模型为了少弃一点风而让储能过度充放、机组过度调节。我的经验是惩罚系数不要拍脑袋定先跑一次不带惩罚的优化看自然弃风量再按弃风损失的大致数量级去设惩罚系数通常比燃料成本高一个到两个数量级即可。还需要注意储能充放电同时进行不仅是浪费还会造成SOC计算混乱。要么引入二进制变量约束互斥要么用一组约束让同一时段充电功率和放电功率乘积为0。二进制变量在MatlabYalmip里就是binvar但如果时间尺度T很大二进制变量会拖慢求解速度。工程上很多情况下可以用一个小量松弛或者直接限定SOC变化方向来规避但严格起见我做严肃的算例仿真时还是会保留互斥约束毕竟多几个binvar换来的是物理合理性。2.3 约束条件的工程化取舍约束条件里最核心的是功率平衡约束、储能SOC递推约束、容量与功率上下限约束、机组爬坡约束以及网络约束。功率平衡约束是等式约束必须严格满足否则优化结果不物理。SOC递推约束是SOC_i,t1 SOC_i,t η_ch * P_ch,i,t * Δt / E_i - P_dis,i,t * Δt / (η_dis * E_i)这里有个工程细节充放电效率η的取值。锂离子电池的充放电效率通常在95%左右但这是电池本体的效率站级还要算上PCS变流器损耗、变压器损耗和站用电综合效率可能只有87%-92%。如果你直接用电池本体效率建模调度出来的结果会偏乐观。我一般会把效率拆成充电效率和放电效率两个参数放进SOC递推式两侧这样更接近实际。网络约束潮流约束是最容易让求解器崩溃的部分。如果不做输电网级研究我倾向于用直流潮流模型替代交流潮流只保留节点功率平衡、线路潮流限值、相角约束。如果研究的是单节点或辐射状配电网甚至可以直接简化成不考虑网损的功率平衡重点是源储荷协调而不是网架。先把核心问题跑通再逐步加网络约束这是避免建模即翻车的正确路径。2.4 特性分布如何进入约束边界前面说特性分群这里给出更具体的做法。设储能站有N组单元每组i的特性参数包括额定容量E_i、当前SOH_i、最大允许充放电功率P_ch_max_i和P_dis_max_i、初始SOC、SOH衰减速率。把SOH乘进容量后实际可用容量是E_avail,i SOH_i * E_i。调度时SOC的上下限也要随之调整比如考虑到过放风险低SOH单元的SOC下限要抬高。更进一步特性分布不仅影响静态边界还影响动态特性。比如高内阻单元在同样功率下发热更厉害温度升高又加速老化。如果要精细刻画可以在目标函数里加上老化成本项用SOH衰减率乘以当前单元功率的平方惩罚过度压榨低健康单元。这个做法增加了模型复杂度但对延长站级寿命很有意义也是考虑特性分布区别于等效大电池的真正体现。我在代码里给每个储能单元都留了独立的参数结构体数组目的就是方便做这种差异化建模。3. Matlab代码实现的骨架与细节3.1 为什么用Yalmip而不是手写求解接口Matlab里做优化调度主流方案是YalmipCplex/Gurobi。Yalmip是一个建模层它把优化问题用自然的方式表达出来再交给底层求解器求解。相比直接用Cplex的Matlab接口手写矩阵A、b、Aeq、beqYalmip的代码可读性高太多尤其当约束条件多、指标变量多的时候手写矩阵很容易出现行列对不上的低级错误而且极难排查。我推荐的组合是Yalmip Gurobi或者Yalmip Cplex。Gurobi在混合整数线性规划上通常比Cplex快遇到MILP规模大的问题更稳。需要提醒Yalmip只是建模工具不负责求解所以一定要安装底层求解器并配置好路径。很多人以为装了Yalmip就能求解结果运行时报错没有可用的求解器这个坑我踩过不止一次。3.2 核心代码结构与关键片段我会把整个程序分为几块参数定义、数据加载、模型构建、求解、结果后处理。下面给出模型构建部分的骨架以日前调度的MILP模型为例。%% 参数定义 T 24; % 时段数1小时粒度 N_g 3; % 机组数量 N_s 4; % 储能单元组数 E_nom [100 80 120 90]; % 各单元额定容量/MWh SOH [0.98 0.93 0.88 0.95]; E_avail E_nom .* SOH; % 实际可用容量 P_ch_max [25 20 30 22]; % 各单元最大充电功率/MW P_dis_max [25 20 30 22]; % 各单元最大放电功率/MW P_g0 [40 50 30]; % 机组初始出力/MW SOC0 [0.50 0.50 0.50 0.50]; %% 决策变量 P_g sdpvar(N_g, T, full); P_ch sdpvar(N_s, T, full); P_dis sdpvar(N_s, T, full); SOC sdpvar(N_s, T1, full); u_ch binvar(N_s, T, full); % 充电状态0/1 u_dis binvar(N_s, T, full); % 放电状态0/1 P_cur sdpvar(T, 1, full); % 弃风弃光功率 P_shift sdpvar(T, 1, full); % 可平移负荷调整量 %% 目标函数 Objective sum(sum(Cg .* P_g), all) sum(C_cur .* P_cur) ... sum(sum(C_ch .* P_ch), all) sum(sum(C_dis .* P_dis), all) ... C_dr * sum(abs(P_shift)); %% 约束条件 Constraints []; % 功率平衡源储放电弃风 负荷储充电可平移调整 Constraints [Constraints, sum(P_g,1) sum(P_dis,1) P_re P_cur ... P_load sum(P_ch,1) P_shift]; % 储能SOC递推 for i 1:N_s for t 1:T Constraints [Constraints, SOC(i,t1) SOC(i,t) ... (eta_ch(i)*P_ch(i,t) - P_dis(i,t)/eta_dis(i)) * dt / E_avail(i)]; end end % SOC上下限注意考虑SOH后的边界 for i 1:N_s Constraints [Constraints, SOC_min(i) SOC(i,:) SOC_max(i)]; Constraints [Constraints, SOC(i,T1) SOC0(i)]; % 典型日场景才需要的周期末约束 end % 充放电互斥 Constraints [Constraints, u_ch u_dis 1]; Constraints [Constraints, 0 P_ch P_ch_max .* u_ch]; Constraints [Constraints, 0 P_dis P_dis_max .* u_dis]; %% 求解与结果提取 ops sdpsettings(solver, gurobi, verbose, 2, mipgap, 0.001); sol optimize(Constraints, Objective, ops); if sol.problem 0 P_g_opt value(P_g); P_ch_opt value(P_ch); P_dis_opt value(P_dis); SOC_opt value(SOC); else yalmiperror(sol.problem); end这段代码里有几个细节值得重点说。一是P_load和P_re在代码里是外部数据P_re是风光出力P_cur表示弃风弃光量等式左边加上P_cur是为了让预测出力不能被完全消纳时多余部分以弃风形式平衡。二是SOC周期末约束如果是连续调度场景不要轻易加这个约束因为让储能从任意状态回到初始值可能会牺牲经济性只有研究典型日的时候才需要加。三是充放电互斥约束里P_ch_max和u_ch之间要广播维度Matlab里如果P_ch_max是行向量直接用会报错我建议初始化时就把P_ch_max定义成列向量或者用repmat扩展。3.3 求解器参数设置的几个要点求解MILP时mipgap的设置直接影响求解时间和解质量。设0.011%的最优性差距在很多工程场景下够用设0.0001会很慢。我一般用0.001到0.005之间先快速拿到可行解再确认关键指标是否稳定。如果求解时间过长可以尝试限定TimeLimit比如120秒让求解器返回当前最优整数解虽然不保证全局最优但对滚动调度基本够用。还需要注意数值尺度。优化模型里的量纲差容易让求解器数值病态比如容量是1000MWh成本是0.5元/kWh两者差距好几千倍。Yalmip内部一般会做一定的预处理但如果求解时警告scaling problems就要自己统一单位。我的习惯是功率用MW容量用MWh成本用万元/h或元/MWh所有量纲都在注释里写清楚。这个习惯在调试时能帮你省下大量时间。4. 多时间尺度协调的实操流程与结果分析4.1 数据准备预测数据处理的第一铁律多时间尺度调度对预测数据的精度要求是分层的。先收集三类数据负荷预测曲线、风电/光伏出力预测曲线、储能单元状态数据SOC、SOH、可用容量。数据颗粒度要与时间尺度匹配日前用小时级日内用15分钟级实时用分钟级。在做代码之前第一件事是把所有预测数据对齐到同一个时间轴单位统一缺失值要用插值补全而不是让NaN进入优化模型。这里我想强调一个新手常忽略的点预测数据的误差不是平均误差而是带有时间相关性的。如果直接拿理想预测曲线做日前计划然后假设日内和实时只是微调会低估实际运行困难。建议在仿真时对日前预测、日内预测分别叠加不同幅度的误差扰动测试策略的鲁棒性。这也是论文审稿人最喜欢追问的地方之一。4.2 滚动时域调度的实现套路日内滚动调度的实现思路是在当前时刻k用未来K个时段的预测数据求解一个有限时域优化问题只执行第一个时段的决策指令到下一时刻k1更新预测数据重新求解。这个循环在Matlab里就是一个for循环但要注意已执行指令的固化已经发生的储能SOC、机组出力、负荷调整量要作为初值传入新模型不能从头自由优化。伪代码如下这是我在实际程序里的写法for k 1:T_total - K % 取当前窗口内的预测数据 P_load_w load_fcst(k:kK-1); P_re_w re_fcst(k:kK-1); % 更新初始状态 P_g_init P_g_opt(k); SOC_init SOC_opt(k); % 调用同一个模型函数 [P_g_w, P_ch_w, P_dis_w] unit_dispatch_model(P_load_w, P_re_w, SOC_init, ...); % 只记录第一个时段结果 P_g_opt(k1) P_g_w(1); SOC_opt(k1) SOC_w(1); end注意如果控制周期是15分钟而日前计划粒度是1小时那么日内优化的第一个指令时长也是15分钟就需要把日前计划和日内计划的变量时间轴做细粒度对齐。这个对齐问题看起来简单实际最容易出错因为索引偏移差一个点整个结果就会错位。我的排查经验是先用固定数据测试将滚动调度的结果与日前计划叠加对比如果曲线在时段边界不连续大概率是索引错位。4.3 结果分析从调度曲线到评价指标优化跑完后不要只画几张调度曲线就收工至少要检查三类内容功率平衡是否严格满足、SOC是否在安全区间内波动、源储荷三方各自的调节量是否合理。我通常画三张图第一张是源储荷功率平衡堆叠图可以直观看到谁在什么时段出力第二张是各储能单元的SOC曲线重点看低SOH单元是否被过度使用第三张是机组出力与日前计划的偏差曲线反映滚动调度的纠偏效果。评价指标方面常用的是运行总成本、弃风弃光率、储能循环次数、负荷峰值削减率。如果要论证考虑特性分布的价值应该做对比实验将特性分群建模的结果与等效大电池建模的结果放在同一算例下对比看成本上升多少、SOC越限次数降低多少。这种对比实验在论文和项目报告里都很有说服力。5. 常见问题与排查技巧实录5.1 求解提示不可行先查约束还是先查数据模型不可行是优化调度里最常见的问题。我的排查顺序是第一步查等式约束的左右两边数值量级第二步查SOC递推公式里的量纲第三步查二进制变量是否和连续变量维度匹配。很多时候不可行是因为SOC初始值和上下限矛盾比如初始SOC低于SOC_min或者是因为负荷数据里有异常尖峰超出了所有机组加储能的出力上限。Yalmip里有个很好用的命令在求解不可行时可以调用diagnostics optimize(Constraints, Objective, ops); if diagnostics.problem 1 % 输出冲突约束的检查 checks check(Constraints); [minviol, loc] min(checks); % loc对应的约束就是最接近不可行的约束 endcheck(Constraints)返回每个约束的残差最小值对应的约束就是最可疑的位置。这个功能我几乎每次调试都会用比盲猜约束省太多时间。5.2 求解慢、内存爆炸的常见原因变量规模大是主要原因尤其是引入了大量二进制变量。优化方向是第一减少不必要的二进制变量比如某储能单元充放电策略固定时直接固定u_ch和u_dis第二减少长时域同时求解的次数把24小时切成4个6小时区间分别求解中间用SOC耦合第三去掉对结果影响很小的惩罚项和约束。还有一种情况是目标函数里有abs()或max/min函数Yalmip会引入额外的辅助变量能用线性重组代替的要尽量代替。5.3 结果异常功率不平衡、SOC越限有时候求解显示成功但结果明显不物理。功率不平衡最常见的原因是P_re风光出力在等式约束里符号搞反了。储能SOC越限则多是因为SOC递推约束没有加上界或者加的是终端约束而不是全时段约束。还有一种隐蔽问题储能效率和容量的单位不一致比如容量用MWh、功率用MWΔt用小时没问题但如果Δt是分钟转换成小时时漏了除以60SOC就会漂移。我把这些整理成一张速查表方便查阅典型现象可能原因排查建议模型提示不可行等式约束量级不一致、SOC初值与边界矛盾用check()定位最可疑约束检查SOC初值是否在上下限内求解时间过长MILP二进制变量过多、数值尺度差减少二进制变量、限制求解时间、统一量纲调度曲线在时段边界不连续滚动窗口索引偏移用固定数据测试逐点对比计划值SOC曲线整体漂移效率、容量、时间单位不一致检查SOC递推式中的eta和dt单位储能同时充放电未加互斥约束或互斥约束失效检查u_chu_dis1是否加入约束组求解成功但成本异常高惩罚系数设置不合理对比不同惩罚系数量级参考自然弃风量5.4 我的调试习惯与避坑经验最后分享几个个人习惯。第一写一个傻瓜测试把储能容量设成非常大、效率设成1.0看模型是否退化成纯源荷协调问题结果是否符合直觉。如果这个测试都过不了说明模型框架有逻辑错误不要急着调参数。第二每次改动模型后保留一份旧版本代码并记录改动前后目标函数的变化。优化问题最容易出现改了这里好了那里坏的隐性问题。第三结果图不要只存final版本把每次仿真的关键指标总成本、弃风率、求解时间写进一个log文件方便回溯。这几个习惯陪我度过了很多个debug的深夜也给我省下了大量重复实验的时间。根据我的项目经验这类考虑特性分布的储能电站接入的电网多时间尺度源储荷协调调度策略的Matlab实现最容易提升成果质量的地方反而在模型层之外一是数据颗粒度与时间尺度的匹配是否严谨二是对比实验是否设计得有说服力。模型和代码只是交通工具真正让结果可信的是你对物理过程的理解和实验设计的细致程度。
返回列表