含储能特性分布的源储荷多时间尺度协调调度Matlab实现 如果你最近也在做储能调度相关的仿真应该会经常看到“源储荷协调”“多时间尺度”这类词。我前几个月亲手用Matlab实现了一个叫“考虑特性分布的储能电站接入的电网多时间尺度源储荷协调调度策略”的完整项目从建模到代码再到结果分析踩了不少坑也理顺了很多之前没想明白的点。这篇博文就把整个项目从头到尾拆开讲一遍内容偏工程实操适合做电力系统优化调度方向的同学、刚入门或正在做类似仿真的工程师参考。项目的核心其实就一句话电网里接入的储能电站不是一个“理想大水桶”每台储能单元的容量、健康状态、充放电效率都有差异调度时得把这些特性分布考虑进去同时在日前、日内、实时几个时间尺度上把电源、储能、负荷协调起来优化。下面我按思路、建模、代码、实验、避坑五个部分展开。1. 项目到底在解决什么问题1.1 为什么储能不能当成一个“大水桶”国内很多储能调度研究在最开始都会做一个简化假设把整个储能电站看成一台“聚合储能”容量是各台之和最大功率也是各台之和充放电效率取一个平均值。这个假设在项目初期可以理解但一旦落到实际运行场景问题就很明显。一个数百兆瓦时的储能电站往往由几十个甚至上百个电池簇并联组成。电池簇之间因为出厂批次不同、运行温度不同、循环次数不同实际容量和健康状态SOHState of Health会慢慢分开。有的簇SOH还有95%有的已经掉到80%内阻变大、可用容量变小、充放电损耗也更高。如果调度模型里把整个电站当成一个均质储能那么指令下发后很可能出现“部分单元过充过放部分单元根本没有用上”的情况长期跑下去个别电池簇会加速老化甚至触发保护动作整个电站的可用性都会下降。所以这个项目把“特性分布”作为一个中心词来对待不是锦上添花而是把储能从“单体等效模型”换成“集群差异化模型”让每个储能单元在调度模型里有自己独立的容量、功率上限、效率、SOC约束和老化成本。1.2 单时间尺度调度为什么不够用电网调度如果只做一个时间尺度通常面临一个矛盾日前计划的颗粒度太粗根本吃不下当天突发的新能源功率波动和负荷变化。比如我做了个日前优化按小时算好未来24小时每台机组出多少电、储能充多少放多少但到了实际运行环节光伏突然被云遮住风电预测误差超过20%这时如果还死守日前计划要么靠旋转备用硬扛要么就得弃风弃光经济性和清洁能源消纳率都不理想。反过来只做实时调度也不行。实时调度虽然能跟上变化但它没有全局视野容易做出“眼前最优、整体次优”的决策比如为了应付某一刻的功率缺口让储能深度放电结果晚上负荷尖峰时储能没电了。这种情况本质上是在单时间尺度下“只盯着局部看不到明天”。所以现实且工程上可落地的方案就是多层次时间尺度配合。我这次做的是两层结构在Matlab里实现最上面一层是日前调度时间粒度为1小时决策周期24小时做的是全天的资源安排下面一层是日内滚动优化时间粒度缩小到15分钟滚动窗口设为4小时根据更新的超短期预测把日前计划逐步修正。两层模型共用同一套储能特性分布数据和源、储、荷设备模型只是目标函数和约束细节上有差异。1.3 源、储、荷三个角色为什么要放进同一个模型很多储能调度项目会默认负荷不可调机组出力恒定只把储能当“削峰填谷的工具”。这种做法做演示没问题离真实电网的协调运行差得比较远。实际上源侧的常规机组有爬坡速率限制和最小出力约束新能源机组的出力具有随机性荷侧有一部分可通过需求响应调整的灵活性负荷用户侧还有电动汽车充电桩这类可平移负荷。储能如果只跟电源配合不考虑负荷的可调节空间相当于放弃了一部分成本更低、响应更快的手段。反过来如果只跟负荷配合电源侧的硬约束又可能会卡住整体可行性。这个项目把源、储、荷三个角色放到同一个优化问题里统一用一组决策变量描述它们的出力/用电计划共享同一条功率平衡约束。这样做的效果是动态的光伏大发时常规机组压到最低出力储能充电可调负荷增加用电晚高峰时储能放电可调负荷削减常规机组爬坡加出力三者互相补位。本质上不是让某一侧单独去“硬扛”而是让整个系统在不同的时间尺度上分工配合。2. 调度策略的框架与数学模型2.1 多时间尺度框架怎么定我在项目里定义了一条比较清晰的时间线。日前调度在当天00:00前完成生成未来24小时、每小时一个点的计划决策内容包括常规机组开关机和出力计划、储能每个单元的充放电功率与SOC轨迹、可调负荷基线调整量、弃风弃光量。日内滚动优化从当日00:00开始每15分钟滚动执行一次每次优化未来4小时16个点用最新的超短期预测刷新输入同时对日前计划偏离量加惩罚避免大幅度偏离已下发计划导致实际运行混乱。这里有一个细节值得说日内滚动优化的决策周期4小时为什么定成这个长度。太短了比如1小时优化器看不到负荷晚高峰的到来很容易在滚动窗口末尾把储能放空太长了比如8小时超短期预测在后半段的精度明显下降优化器的“远见”是假的。4小时在预测精度和前瞻视野之间比较平衡这也是我实测多个窗口长度之后定的。2.2 日前调度模型的构成日前调度模型是典型的大规模线性规划问题。目标函数我分了五块成本全部做量纲统一后相加常规机组燃料成本用二次函数描述我在实现里将其分段线性化处理机组启停成本只在日前模型中考虑日内模型中默认机组状态不变储能充放电的运行维护成本按充放电电量计费弃风弃光惩罚成本这里要设置一个足够高的惩罚系数确保优化器只有在系统实在消纳不了时才弃新能源可调负荷调整补偿成本负荷上调和下调都产生费用费用系数不同。约束条件里最重要的是功率平衡约束公式可以写成P_g P_w P_s_discharge P_load P_s_charge P_curtail。其次是常规机组出力上下限和爬坡约束储能单元各自的充放电功率上下限、SOC递推关系和SOC上下限以及可调负荷调整量的上下限。关于储能SOC递推我使用的是离散形式SOC(k1) SOC(k) ηc * Pc(k) * Δt / Q - Pd(k) * Δt / (ηd * Q)。其中ηc和ηd分别是充电和放电效率Q是储能单元额定容量Δt是时间步长日前是1小时日内是0.25小时。每个储能单元都有自己的效率参数这本身就是“特性分布”在模型里最直观的体现。2.3 特性分布怎么落到模型里特性分布不是简单地把参数改一改就行关键在怎么建模才贴近实际情况。我在项目里采用了“参数分布成本惩罚”两个层面的处理方式。参数分布层面按SOH把储能电站内的单元分成若干个集群每个集群有自己的额定容量Q、额定功率上限Pmax、充电效率ηc、放电效率ηd、SOC约束区间。例如SOH高的集群可以允许SOC在0.1到0.9之间运行SOH低的集群把范围收缩到0.2到0.8避免深度充放电进一步加速老化。这些参数全部从一组基础数据里读取形成一个表格化的“储能特性参数矩阵”这样换数据集时只需要改表格不用改模型。成本惩罚层面我在目标函数里给每台储能单元加了一个老化惩罚项大小与该单元的放电深度DOD正相关。简化表达为老化成本 k_dod * (SOC低点到高点之差)^2这个二次项可以从0到1连续变化。这样做的好处是优化器会自动避开让某个单元频繁深度充放电转而把功率需求分摊到更多单元上结果是整个储能电站的SOC轨迹更均衡而不是“逮着一组电池使劲薅”。2.4 日内滚动修正模型日内滚动优化与日前模型最大的不同在于目标函数里多了一个“计划偏离惩罚”。决策变量在每个优化周期都会重新求解但它们被要求尽量贴近日前计划中同一时刻的值。实现方式是对比日前计划与日内求解结果的偏差并对偏差施加线性惩罚。我测试过如果不加这个惩罚日内优化经常“推翻重来”比如某台机组在日前计划里是满发到日内优化里突然被压到最小出力另一个时段又爬上去实际调度根本没法执行。加了偏离惩罚之后日内优化只会在“系统安全不可行”或“经济性差异非常大”的时候才去改变日前计划整体运行既能响应新信息又不会太跳脱。3. Matlab实现的关键细节3.1 建模工具与求解器选择这个项目用的是Matlab下的Yalmip工具箱做建模求解器选的是某个成熟商业求解器的Matlab接口。Yalmip的好处在于它把变量、约束、目标函数的定义方式和求解器解耦写起来跟数学公式的思维方式很像调试时也容易定位约束冲突。而且它自带头文件式的报错提示能直接告诉你有问题的约束在下标哪里这一点比手写矩阵系数要省心太多。求解器方面如果模型全线性不含0-1变量直接用求解器连续求解就好速度很快。如果加了机组启停变量或者储能充放电状态变量模型会变成混合整数线性规划求解时间会明显拉长。最终我把日前模型做成包含0-1变量的MILP日内模型默认机组状态保持不变只保留连续变量这样两层模型的求解时间都控制在分钟级别以内符合在线滚动调度的要求。3.2 核心变量定义与构造思路在Matlab自己编码时变量定义这一关一定要想清楚特别是储能变量多了一个“单元维度”比普通单储能模型复杂不少。我的定义方式如下n_G 6; % 常规机组数 n_S 4; % 储能单元集群数 n_W 2; % 新能源场站数 n_L 3; % 可调负荷数 T 24; % 日前时段数 % 常规机组出力 P_g sdpvar(n_G, T, full); % 储能充电和放电功率第三维区分充电/放电 P_s_charge sdpvar(n_S, T, full); P_s_discharge sdpvar(n_S, T, full); % SOC变量时段数比决策时段多一个初值24个末值 SOC sdpvar(n_S, T1, full); % 风电/光伏出力小于等于预测值 P_w sdpvar(n_W, T, full); % 可调负荷调整量 P_l sdpvar(n_L, T, full);这里建议把储能充电功率和放电功率分开定义不要合在一个变量里用正负号区分。虽然合并写法代码更短但我试过后续加约束、读结果、画图都容易搞混而且从数值求解角度分开后每列变量语义更清晰调试时看求解结果比较直观。要是担心“同时充放电”的冗余解可以通过约束P_s_charge和P_s_discharge的互斥条件来处理。3.3 核心约束的代码写法约束定义是整个模型最核心的部分我最开始写的时候因为功率平衡约束少加了一项导致求解器一直报不可行排查了很久。下面给出关键约束的示范代码注意每一段约束我都用“约束累加”的方式写方便后期用Yalmip直接传给求解器。% 节点/系统功率平衡各电源出力之和等于负荷与充电功率之和 Constraints []; Constraints [Constraints, ... sum(P_g,1) sum(P_w,1) sum(P_s_discharge,1) ... base_load(1:T) sum(P_l,1) sum(P_s_charge,1)]; % 常规机组上下限约束 for i 1:n_G Constraints [Constraints, ... P_gmin(i) P_g(i,:) P_gmax(i)]; end % 储能充放电功率上下限 for s 1:n_S Constraints [Constraints, ... 0 P_s_charge(s,:) P_cmax(s,:)]; Constraints [Constraints, ... 0 P_s_discharge(s,:) P_dmax(s,:)]; end % SOC递推约束SOC_{k1} SOC_k ηc*Pc*Δt/Q - Pd*Δt/(ηd*Q) for s 1:n_S Constraints [Constraints, ... SOC(s,2:end) SOC(s,1:end-1) ... eta_c(s) * P_s_charge(s,:) * dt / Q_s(s) ... - P_s_discharge(s,:) * dt / (eta_d(s) * Q_s(s))]; end % SOC上下限 for s 1:n_S Constraints [Constraints, ... SOC_min(s) SOC(s,:) SOC_max(s)]; end % 新能源出力不超过预测值 Constraints [Constraints, ... 0 P_w P_w_forecast];有个细节要注意SOC变量的维度是n_S×(T1)这里T1的最后一个点是周期末的SOC快照便于观察周期结束时储能有没有回到合理范围。如果要做连续多日仿真应该把最后一点SOC作为下一日的初始SOC传下去而不是每次从固定初值开始。3.4 求解效率调优我第一次把整套模型写完后跑一次日前调度花了十几分钟还经常不收敛后来才意识到问题出在几个地方。第一MILP的0-1变量过多。如果对每个机组的每个时段都引入开机变量和停机变量变量规模会爆炸。我改成只对常规机组的开停机状态在日前模型里建模日内模型完全不考虑启停只优化出力这样大幅减少了整数变量数量求解时间明显降下来。第二目标函数里的二次燃料成本直接用sdpvar二次项时Yalmip会将其交给求解器做二次规划收敛速度会比较慢。我用分段线性化把二次曲线近似为若干段线性函数整个模型变成纯线性规划稳定性提升不少。第三约束里尽量不要出现两个决策变量相乘的情况这类双线性项会让问题变成非凸问题求解器基本束手无策。遇到这类需求要么通过代数转换避开要么用分段线性化处理。改完这三个点之后日前模型求解时间大约在20到50秒日内模型单次滚动在5秒以内完全满足我仿真几百个场景的需求。4. 仿真算例设计与结果解读4.1 算例参数怎么设测试系统我用了考虑负荷和新能源接入的某个标准30节点测试系统的修改版常规机组6台新能源场站2个一个风电场、一个光伏电站储能电站由3个特性不同的储能集群组成。三类储能集群的参数我设计成有区分度集群1SOH最高容量最大效率也高集群2是中等状态集群3容量小且效率低SOC运行范围也最窄。这样做是为了让特性分布在结果里“看得见”如果三个集群参数完全一样那就退化回聚合储能模型了。负荷曲线和新能源预测数据采用典型日数据并叠加随机误差。日前预测误差我设置成标准差为10%的高斯扰动日内超短期预测误差标准差设为3%这样两层时间尺度在数据精度上的差异就体现出来了。4.2 三组对比实验的设计为了让项目结果有说服力我设计了三个方案进行对比方案A是基准方案不含储能只做常规机组经济调度这是对照组的origin状态。方案B是储能接入但采用聚合模型只做日前调度不考虑特性分布也不做日内滚动。方案C就是本项目的完整策略储能集群差异化建模日前加日内滚动协调。三个方案跑完后我对比了系统总运行成本、弃风弃光率、储能单元SOC极差、常规机组爬坡次数这几个指标。结果符合预期方案C比方案B的总成本下降了约4%比方案A下降了约7%弃风弃光率下降幅度更明显。更关键的是SOC均衡度方案B中所有储能单元的SOC轨迹完全重合因为模型里它们就是同一个变量而方案C中不同SOH的储能集群SOC轨迹明显拉开低SOH集群的充放电深度明显更浅这正好说明了特性分布确实改变了调度决策。4.3 从输出曲线里看什么这个项目画图我用的是Matlab的figure加plot重点看四类曲线常规机组出力堆叠图、储能各集群的充放电功率和SOC轨迹、新能源出力及弃风弃光量、系统总负荷与净负荷曲线。最值得仔细看的是净负荷曲线变化。如果储能策略有效“净负荷”曲线应该是被削峰填谷后的平滑形态。我印象最深的是方案B虽然也削峰填谷但由于聚合模型没有考虑不同储能单元的效率差异优化器总是优先用“平均效率”去决策实际落到低效率集群时损耗偏大最终净负荷曲线比方案C的略差。这种差别从单日曲线看不大但累计到一个月成本差异就非常明显。还有一点看储能SOC曲线是否越界。方案B由于所有储能当成一个SOC曲线看着很平缓但实际上单个低SOH集群的真实SOC区间可能早就越限了。方案C的每个集群SOC曲线都严格落在对应区间内这才是调度方案可以下发给实际设备执行的前提。5. 常见问题与排查手册5.1 不可行解怎么排查我在项目开发中遇到过多次Yalmip报infeasible这一步要系统排查最笨但最有效的方法是逐段注释约束。先把储能约束全部注释掉跑通一个最简版然后再把储能约束逐块加回来当加某一类约束时报错问题就定位在那部分。另外从数据角度检查一下量纲。这个问题出现过一次负荷数据单位是MW新能源预测数据单位是kW结果功率平衡约束内部差了1000倍模型看起来怎么会可行。单位统一这件事放在数据预处理函数里统一处理好不要在建模代码里再去乘系数否则很容易出错。5.2 求解时间爆炸怎么办如果模型本身不复杂但求解速度特别慢优先怀疑是不是无意中引入了整数变量。比如储能充放电功率如果用了二元变量做互斥约束每个时段多一组0-1变量百来个时段就是几百个整数变量求解时间翻十倍很正常。我的经验是先跑一次纯连续模型确认结果合理后再考虑要不要加整数约束。5.3 滚动优化里SOC越跑越漂的问题这个问题在日内滚动时特别容易碰到。因为每次滚动优化的决策周期只有4小时优化器会在这个窗口内尽量使用储能调节但不考虑窗口结束后的事情结果就是SOC被逐次优化“推”到边界长期滚动后漂移出合理范围。解决办法有两种。一是给SOC设定一个“参考轨迹”让调度倾向于回归到日前计划的SOC曲线上。二是每次滚动求解后把实际执行的SOC末端值作为下一次滚动的初值并且在目标函数中对偏离参考SOC给予惩罚。我两种都试过参考轨迹方式更稳定推荐优先采用。5.4 一些数据层面的思考储能特性参数分布如果做得太细每个单元都单独建模模型规模会大到没法在线求解。我最终选择按SOH分簇一个电站分成三到五个集群这样既保留了特性差异又控制了变量数量。实际上如果项目后续做更大规模的系统可以考虑把储能集群也聚合到更粗的层级上前提是参数差异不能太大不然聚合误差又会变大。这个项目做下来我最深的体会是调度模型本身并不追求数学上多复杂真正有价值的是对运行细节的刻画和参数之间的耦合。特性分布模型加多时间尺度协调本质上就是在“计算精度”和“可计算性”之间找一个实用的平衡点。如果让我重新做一遍我会把储能特性参数模块从一开始就独立成函数而不是反复修改变量和约束这样换数据集、换参数时改动最小调试效率也会高很多。