资讯详情

资讯详情

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

做电网调度优化的朋友应该都有体会储能电站建起来不难调度起来才是真正的麻烦。尤其是系统里同时有火电、新能源、多座储能电站和可调负荷还要考虑储能内部各电池簇的健康状态和SOC差异时普通的“单电池等效模型”根本扛不住。这篇博客想完整记录我最近落地过一个项目——考虑特性分布的储能电站接入的电网多时间尺度源储荷协调调度策略Matlab代码实现。标题听着长拆开看就是三层意思储能建模要按特性分布处理不能一个SOC走天下调度要分日前、日内、实时三个时间尺度协同不能一把尺子量到底源、储、荷三方要联合优化不能各自为政。本文从模型原理、数学结构、代码实现到调试踩坑完整走一遍适合正在做储能调度、微电网能量管理或者电网优化运行方向的同学参考也适合刚入门想找一份可复现代码的读者。1. 项目内涵拆解三个关键词背后的真实工程痛点1.1 什么是“特性分布”储能建模为什么不能把储能站当作一个大电池很多论文和Demo里储能电站的建模方式就是一个等效大电池给一个总容量、总功率上限、一个SOC变量整个电站的充放电行为就看这一组变量。这种做法在早期仿真没问题但拿到实际工程里会有很大的执行偏差。真实储能电站是由几十个甚至上百个电池簇构成的每个簇的SOC、容量衰减程度、内阻、可用的功率上限都不一样。调度层下发一个总功率指令场站层按平均或按比例分配到各簇结果往往是SOC低的簇还在放电、SOC高的簇还在充电轻则加速老化重则触顶越限直接导致调度指令不可执行。项目标题里强调的“特性分布”核心思想就是把储能电站内部的多样性显式建模而不是用一个理想化的聚合体掩盖差异。具体做法一般有两种一种是将电池簇按当前SOC或健康状态SOH做聚类分群每个群等效为一个有独立SOC、独立约束的储能单元另一种是不聚类直接对每个电池簇单独建模适用于电池数量少或者需要精细化寿命管理的场景。我采用的方案是K-means聚类分群加等效聚合。原因很直接一个电站十几个电池簇还可以逐个建模几十上百个簇就要面临变量爆炸的问题混合整数规划根本扛不住。聚类分群的好处是把特性相近的簇合并用少数几个等效储能单元去表达整站的调度可行域保留最重要差异性信息的同时把模型规模控制在可求解范围内。1.2 多时间尺度“源储荷协调”到底在协调什么多时间尺度的逻辑来自电网运行的真实节奏。不同资源响应速度完全不同火电机组启停需要提前数小时确定爬坡需要分钟到小时级储能是秒级到分钟级响应负荷侧的需求响应则可以在日内根据电价或激励信号灵活调整。如果用同一个时间粒度做优化必然失真。项目里的协调框架分成三层日前调度时间分辨率1小时求解未来24小时的机组启停、储能充放电计划、可调负荷计划。这一步用的是日前预测数据精度低但覆盖时间长主要解决“明天怎么安排”的问题。日内滚动每隔15分钟滚动一次优化未来4小时使用超短期预测和最新实测状态固定日前决定的机组启停状态重新优化各机组出力和储能功率解决“预测变了怎么办”的问题。实时分配分钟甚至秒级执行把调度偏差或自动发电控制信号在机组和储能之间分配储能作为快速调节资源优先响应解决“实际和计划不一致”的问题。三层结构之间靠一致性约束衔接。日内优化不是完全推翻日前计划而是以日前计划为基准做增量修正用追踪惩罚项保证最终的运行点不会偏离机组组合和经济调度的总体安排太远。源储荷协调则体现在三个维度时间上源侧的火电提供基荷和慢调节储能负责快调节和跨时段能量搬移负荷侧提供柔性空间上不同节点接入的储能和可调负荷共同缓解线路阻塞目标上弃风弃光惩罚、切负荷惩罚和运行经济性统一在一个目标函数里权衡。少任何一方其他两方的调度压力都会显著上升。2. 源储荷协调调度的数学模型与约束体系2.1 目标函数设计经济性、清洁性与安全性的加权权衡目标函数是调度的“指挥棒”。项目里我把它设计成四项相加的形式每一项都有明确的工程含义常规机组运行成本采用二次函数拟合煤耗包含启停成本这是传统经济调度的主体。储能充放电老化折算成本储能不能白用每次充放电都会消耗循环寿命。给单位充放电功率一个成本系数可以让优化器主动减少不必要的深度充放避免储能成为“免费的调峰工具”。弃风弃光惩罚新能源优先消纳惩罚系数取一个较大的值迫使优化器优先考虑新能源出力。切负荷惩罚这是最后的保底手段惩罚系数最大正常情况下不会出现。目标函数可以写成min Σ_t Σ_g (a_g·P_g² b_g·P_g c_g·u_g) % 机组运行成本 Σ_t Σ_s λ_s·(P_dis P_ch) % 储能老化成本 Σ_t ρ_wind·(P_wind_avail - P_wind) % 弃风惩罚 Σ_t ρ_load·P_curtail % 切负荷惩罚这里有个值得注意的细节机组运行成本里的二次项在Yalmip里可以直接用quadratic形式表达但如果你用的求解器不支持二次目标比如某些MILP求解器配置需要做分段线性化。我的实际建议是如果算例规模不大就直接保留二次项用Gurobi或Cplex的MIQP能力如果规模大就把煤耗曲线分三段线性化转成纯MILP求解稳定性会更好。2.2 储能特性分布的数学建模细节储能分群之后每一个等效储能单元k的参数由分群内所有电池簇聚合得到能量容量E_k Σ E_i群内所有簇的容量相加功率上限P_max,k Σ P_max,i群内所有簇的功率上限相加初始SOCSOC0,k (Σ E_i·SOC_i) / E_k按能量加权平均SOC动态方程是储能模型的核心SOC_k(t1) SOC_k(t) - (η_ch·P_ch,k(t)·Δt)/E_k (P_dis,k(t)·Δt)/(η_dis·E_k)注意充放电效率的位置不一样充电时能量存入电池要打折扣所以乘在功率前面放电时能量取出也要打折扣所以除在效率上。很多初写代码的人容易把这两个效率位置搞反导致能量不守恒SOC越跑越偏。SOC上下限约束不能简单用全局统一值。特性分布建模的优势就在这里分群后SOC偏低的群给更紧的下限约束比如不能低于15%避免过放SOC偏高的群给更紧的上限约束比如不能超过90%防止过充。如果全站用一个SOC建模从均值看可能很安全但实际可能已经有一部分簇越限了。同一时刻不能同时充放电的约束我建议用大M法加入二进制变量P_ch,k(t) ≤ M·b_k(t) P_dis,k(t) ≤ M·(1-b_k(t))有些简化模型用P_ch·P_dis0的互补形式表达这在非线性求解器里能用但在MILP里不行。工程上还是大M法最稳。2.3 源网荷储的耦合约束体系除了储能自身约束整个系统还需要满足以下耦合约束功率平衡约束所有电源出力加储能放电减储能充电等于总负荷减切负荷。这是系统级约束所有变量通过这一条绑在一起。机组出力上下限与爬坡约束爬坡约束要考虑相邻时段的出力差限制这是日前调度中决定机组能否快速跟踪新能源波动的关键约束。旋转备用约束系统需要预留正负备用容量一般按负荷的一定比例或者新能源出力的预测误差设定。储能可以承担一部分备用但要注意留出功率余量。线路潮流约束如果采用直流潮流模型每条线路的潮流是节点注入功率的线性组合需要限制在传输容量范围内。这正好体现了“储能接入位置”的价值——接在阻塞节点附近的储能对缓解线路过载的作用远大于接在电源侧的储能。可调负荷约束可调负荷的调节量有上下限同时累计调节量受能量约束不能无限调下去。这些约束在代码里需要一条一条写清楚错一条就会导致不可行或者结果明显不合理。我在实际调试中发现新手最容易漏的是备用约束——觉得功率平衡满足就够了结果新能源出力预测偏高时实际运行时根本没有调节手段这就是备用约束存在的意义。3. Matlab实现框架与关键代码解析3.1 整体代码结构整个Matlab工程的目录结构大致如下├── data/ % 输入数据负荷曲线、风电出力、机组参数、储能参数 ├── clustering/ % 储能特性分群与聚合 ├── day_ahead/ % 日前调度优化 ├── intraday/ % 日内滚动优化 ├── real_time/ % 实时功率分配 ├── result/ % 结果输出与绘图 └── main.m % 主程序入口主程序的执行流程是读数据→储能分群聚合→日前优化→日内滚动循环→实时分配模拟→结果可视化。三段时间尺度共用一个数据类状态变量在每层之间传递这样能保证各层模型看到的状态一致。3.2 储能分群与聚合参数计算代码储能分群是本项目最核心的预处理步骤。代码用K-means对电池簇的SOC序列或当前特征向量聚类% 储能电池簇特性分群 % SOC_all: n_battery × T 矩阵每行是一个电池簇的SOC时间序列 % 或者只用当前SOC一维特征做聚类实际项目里建议用多维特征 SOC_now SOC_all(:, end); % 取当前时刻所有簇的SOC K 3; % 分群数量根据电池簇数量和经验确定 % K-means聚类 [idx, center] kmeans(SOC_now, K, Distance, sqeuclidean, Replicates, 5); % 聚合每个群的等效参数 for k 1:K cluster_idx find(idx k); E(k) sum(E_battery(cluster_idx)); % 等效能量容量 Pmax(k) sum(Pmax_battery(cluster_idx)); % 等效功率上限 SOC0(k) sum(E_battery(cluster_idx) .* SOC_now(cluster_idx)) / E(k); end这段代码有几个细节要注意。第一Replicates参数建议设置成5以上K-means对初始中心敏感多跑几次取最优结果避免分群不稳定导致调度结果跳变。第二如果电池簇容量差异很大直接用SOC做特征会忽略容量因素建议把特征拼成[SOC, 容量归一化]的二维向量效果更好。第三离群点要提前剔除比如SOC异常低的故障电池簇不应该参与聚合否则会拉低整个群的下限。3.3 日前调度Yalmip建模核心代码日前调度是整个策略的决策基础。我用Yalmip建模求解器用的Gurobi。代码如下% 日前调度优化 T 24; % 日前时间范围单位小时 nG 2; % 常规机组数量 nS 3; % 储能等效集群数量分群得到 % 决策变量 P_G sdpvar(nG, T, full); % 机组出力 u binvar(nG, T, full); % 机组启停状态1为开机 P_W sdpvar(1, T, full); % 风电实际出力 P_W_curt sdpvar(1, T, full); % 弃风量 P_S_ch sdpvar(nS, T, full); % 储能充电功率 P_S_dis sdpvar(nS, T, full); % 储能放电功率 SOC_S sdpvar(nS, T1, full); % 储能SOCT1个时刻 b_ch binvar(nS, T, full); % 充电状态标志 P_load_curt sdpvar(1, T, full); % 切负荷量 % 目标函数 Objective 0; for t 1:T for g 1:nG Objective Objective a(g)*P_G(g,t)^2 b(g)*P_G(g,t) c(g)*u(g,t); end for k 1:nS Objective Objective lambda(k) * (P_S_ch(k,t) P_S_dis(k,t)); end Objective Objective rho_wind * P_W_curt(t) rho_load * P_load_curt(t); end % 约束集合 Constraints []; % 功率平衡 for t 1:T Constraints [Constraints, sum(P_G(:,t)) P_W(t) sum(P_S_dis(:,t)) ... - sum(P_S_ch(:,t)) P_load(t) - P_load_curt(t)]; end % 机组出力上下限与爬坡 for t 1:T for g 1:nG Constraints [Constraints, P_min(g)*u(g,t) P_G(g,t) P_max(g)*u(g,t)]; if t 1 Constraints [Constraints, -R_down(g) P_G(g,t) - P_G(g,t-1) R_up(g)]; end end end % 储能SOC动态方程 dt 1; % 时间间隔1小时 for k 1:nS Constraints [Constraints, SOC_S(k,1) SOC0(k)]; for t 1:T Constraints [Constraints, SOC_S(k,t1) SOC_S(k,t) ... - eta_ch(k) * P_S_ch(k,t) * dt / E(k) ... P_S_dis(k,t) * dt / (eta_dis(k) * E(k))]; end end % 储能功率上下限与SOC约束 for k 1:nS for t 1:T Constraints [Constraints, 0 P_S_ch(k,t) Pmax(k) * b_ch(k,t)]; Constraints [Constraints, 0 P_S_dis(k,t) Pmax(k) * (1-b_ch(k,t))]; Constraints [Constraints, SOC_min(k) SOC_S(k,t1) SOC_max(k)]; end Constraints [Constraints, SOC_S(k,end) SOC_target(k)]; % 日末SOC约束 end % 线路潮流约束直流潮流略 % Constraints [Constraints, -PL_max PTDF * P_inj PL_max]; % 求解 ops sdpsettings(solver, gurobi, showprogress, 1, mipgap, 0.001); optimize(Constraints, Objective, ops);这里面有两处个人经验。第一处是日末SOC约束SOC_S(k,end) SOC_target(k)这个约束非常重要不加的话优化器会在最后时段把所有储能放空导致第二天没有容量可调。目标值一般设为与当天初始SOC接近保证储能跨日运行有连续性。第二处是MIP Gap设为0.0010.1%就够用了没必要追求到0否则求解时间会指数增长。3.4 日内滚动与实时功率分配的实现思路日内滚动优化的核心是一个不断推进的循环每次只下发当前时段的控制指令但优化会看到未来数小时% 日内滚动优化主循环 T_roll 4 * 4; % 4小时15分钟一个点共16个断面 M 96; % 全天15分钟断面数 for t_roll 1:M % 取最新超短期预测负荷和风电 P_load_hat load_forecast_intraday(t_roll : t_rollT_roll-1); P_wind_hat wind_forecast_intraday(t_roll : t_rollT_roll-1); % 固定日前决策的机组启停状态 u_fixed u_dayahead(:, ceil(t_roll/4)); % 调用日内优化子函数 [P_G_cmd, P_S_cmd] intraday_optimize(... P_load_hat, P_wind_hat, SOC_now, u_fixed, ...); % 只下发第一步指令 dispatch_to_units(P_G_cmd(:,1)); dispatch_to_storage(P_S_cmd(:,1)); % 更新实测状态进入下一个滚动时刻 SOC_now update_measured_state(SOC_now); end日内优化的目标函数里我会额外加一项“追踪日前计划”的惩罚项系数取一个适中的值。太小的话日内会大幅偏离日前机组组合的最优经济点太大的话又失去了滚动修正的意义。这个系数我在算例里调到0.05-0.1之间效果比较好具体取值和系统规模有关需要敏感性分析。实时分配不再做优化求解而是用规则表处理先计算系统频率偏差对应的功率调节量按“储能优先、水电其次、火电最后”的顺序分配。储能的分配按各集群SOC情况加权SOC高、能放电的集群多承担上调出力SOC低、能充电的集群多承担下调出力。这样既快速又安全完全不需要求解器介入。4. 算例验证与结果对比分析4.1 算例系统与参数配置算例系统我设计了一个伪30节点改造系统包含2台火电机组总装机240MW、1个风电场装机120MW、1个光伏电站装机80MW、2座储能电站和可调负荷。参数名称数值火电装机2×120 MW风电场装机120 MW光伏装机80 MW储能电站140MW/80MWh电池簇20个储能电站230MW/60MWh电池簇15个峰值负荷320 MW负荷预测误差日前3%日内1%风电预测误差日前12%日内4%4.2 结果对比特性分布建模与传统聚合模型为了验证特性分布模型的价值我做了对比实验同样的系统、同样的参数一组用考虑SOC分布的分群储能模型另一组用传统单电池等效模型。优化目标完全相同看最终运行结果对比指标传统聚合模型特性分布模型总运行成本万元86.782.3弃风率%4.82.1储能充放电循环深度均值87%52%储能SOC越限次数120求解时间秒1835总运行成本降低大约5%成本下降主要来自弃风率的降低。这背后的逻辑是传统聚合模型低估了储能的实际可调节能力把本来在安全范围内的功率分配组合当成越限排除掉了白白浪费了部分调节空间。更重要的差异在SOC越限次数上。传统模型只有一组SOC全局变量从约束数学上看永远不越限但实际按比例分配到各电池簇后个别簇的SOC早就突破了边界。特性分布模型因为显式保留了各群SOC的差异调度指令下发到每个群后是可执行、可落地的。4.3 多时间尺度协调带来的量化收益我还测试了“只有日前调度没有日内滚动”和“三层完整协调”两种模式。在风电预测误差6%的场景下完整协调模式的弃风率比单日前模式低3.1个百分点切负荷少了一半多。原因不复杂日前调度只根据晚间预测做决策第二天早上风速突然上升时日前计划已经固定机组爬坡来不及调整只能弃风。而日内滚动每15分钟就能感知到预测变化提前重新安排储能充放电把风功率高峰存下来等到晚高峰再放出去。可以说储能的价值有80%是靠日内滚动实现的日前调度只是搭了一个安全的框架。5. 常见问题与调试经验实录5.1 求解器选择与求解效率问题Yalmip支持的求解器有多个我强烈建议优先选Gurobi或Cplex这类商业MILP求解器。之前试过用求解器自带的默认选项或用开源的CBC小容量测试没问题一旦储能分群数超过3个、机组超过5台MIP Gap半天降不下去求解时间从几十秒飙到几十分钟。如果确实没有商业求解器授权也有替代方案把二次目标线性化减少二进制变量的规模比如不把机组启停当变量而是在日前用优先顺序法预排启停日内只做经济调度。这样模型退化为线性规划用Yalmip自带求解器也能跑得动代价是全局最优性变差。效率优化的另一个关键是减少二进制变量。储能同时充放电互斥约束里的b_ch变量数量等于储能集群数×时间断面数一旦日内滚动调用几十次这部分开销会累积。我的做法是如果储能集群数少就保留二进制变量如果集群数多先按SOC状态预处理排除一部分时段的互斥可能性比如SOC很低时根本不可能放电直接固定b_ch1变量数就降下来了。5.2 约束冲突与不可行问题跑优化最头疼的就是输出“Problem is infeasible”尤其是加了备用约束和多节点潮流约束之后。这时不要手工瞎猜哪个约束的问题直接用Gurobi的IIS不可行子系统分析功能它会用最小集合告诉你是哪几条约束一起导致无解。一般情况下问题出在这三种情形第一是机组启停状态不合理。日前调度决定某台机组关机但日内滚动把它当成必须开机来约束前后层模型不一致导致无解。解决办法是日内优化里所有涉及机组状态的约束都要读取日前决策值不能重新定义。第二是储能SOC上下限和日末SOC目标值矛盾。比如初始SOC为20%日末目标要求回到50%但中间最大充电量算下来根本不够约束就冲突了。解决办法是把日末SOC目标值做成软约束加一个松弛变量目标函数里给一个足够大的惩罚系数这样模型永远不会因这一个约束而无解。第三是负荷和新能源预测值更新后备用约束可能远超系统可用容量。这属于数据问题需要检查是不是滚动窗口内预测突变太夸张或者备用系数设得不合理。我一般会加一个备用短缺的松弛变量通过惩罚系数控制既保证模型不会无解又能让备用约束在大多数场景下严格执行。5.3 SOC漂移与结果振荡问题用等效聚合模型时最常遇到的隐藏坑是SOC漂移。具体表现为分群聚合时用能量加权算SOC0但后续SOC动态方程里各群的充放电效率都取了全站平均值少数效率异常的电池簇会让SOC计算越来越偏离实际。解决方法有两个一个是在SOC动态方程里对每个分群分别标定效率另一个是加一个“SOC校正量”反馈每运行一个滚动周期用实测的集群平均SOC更新模型里的SOC0相当于闭环校正。第二个方法在工程实现上更实用因为效率标定本身就需要现场试验数据。结果振荡的问题则通常出现在目标函数里多个惩罚项权重接近时。比如弃风惩罚和储能老化成本权重相当优化器在相邻滚动窗口里反复切换“弃风”和“充放电”两种策略导致出力和SOC曲线出现锯齿状波动。解决办法是给日内目标函数加上控制动作变化量的二次惩罚限制相邻时段功率调整幅度相当于加了软爬坡约束曲线立刻平滑下来。5.4 分群结果不稳定与参数调优K-means分群每次运行结果都可能不同这在调度系统里是不能接受的——同一个输入昨天分群和今天分群不同最优结果和成本就不可比。我在这里踩过很深的坑。建议的解决方案有三个层次。第一层设定固定的随机种子保证可复现。第二层用历史SOC数据代替当前时刻单断面SOC做聚类特征相当于给每个电池簇打一个长期行为标签分群结果更稳定。第三层如果效果还不够可以改用层次聚类或者按照“充电偏好”“放电深度”等业务规则直接手工分群——规则分群虽然不如数据驱动精细但胜在稳定可靠。实际工程里调度系统的稳定性和可解释性有时候比模型精度更重要。6. 后续可扩展的方向与我个人的操作心得这个项目做完之后我最真实的体会是特性分布建模表面看只是把储能“拆开算”但带来的连锁变化远超预期。它不仅提高了指令可执行性还让调度员能直观看到哪些储能群处于高SOC需要优先放电、哪些处于低SOC需要优先充电。这种透明度在工程验收和现场运维环节很有价值。后续可以考虑加两个方向一是在分群里加入SOH健康状态维度让老化程度不同的电池簇自动分配到不同的调度角色退役组的循环次数自动降低二是把多时间尺度策略接到实际的EMS系统里日前和日内模块已经具备了接口条件实时分配模块需要根据现场通信延迟做进一步改造。最后分享一个小技巧如果你也想在Matlab里复现或改造这个项目建议一开始不要直接跑24小时全场景先用2台机组、1个储能群、6小时时间窗的小算例把代码跑通再逐步扩大规模。电力系统优化项目的调试绝大多数时间花在“不可行”和“结果诡异”上小算例可以让你快速定位问题而不是面对两百多个变量无从下手。储能分群的K值也从2开始试看结果差异再决定是否增加到3或4——不是分群越多越好每个群必须有足够数量的电池簇支撑等效模型的统计意义否则个别电池簇的行为会主导整个群反而失稳。
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →