基于双层优化的储能参与电力市场交易决策建模与Matlab实现

基于双层优化的储能参与电力市场交易决策建模与Matlab实现 两年前我接到一个课题要研究储能参与电力市场的交易决策问题。一开始我有点不以为然储能套利嘛拿历史现货电价曲线跑一个线性规划低充高放完事。结果把初版结果拿给合作单位看对方只问了一句你这套策略如果真的执行——低谷时段储能在全功率充电高峰时段又在全功率放电——当地的现货出清价还会跟你算的一模一样吗我确实被问住了。这个问题把我带进了双层优化这个坑也正好对应这个课题的真正名字储能参与现货电能量-调频辅助服务市场的双层交易决策研究最后基于Matlab做了完整实现。这篇文章从我实际建模、推导、编码到调试的完整过程出发把这套方法的思路和关键细节都摊开讲。适合正在做储能市场收益评估、电力市场课题开题或者想学习双层优化在Matlab里怎么落地的人参考。先说结论双层模型并不是为了显得高级而硬凑出来的框架而是价格反馈效应真实存在时单层模型根本捕获不了的那部分收益损失必须用专门的结构去刻画。1. 先算清楚账储能参与两类市场的收益到底怎么来很多初学者一上来就写优化模型但对“储能到底靠什么赚钱”这件事没吃透。这个部分我建议先反复想清楚因为后面所有约束和目标函数本质上都是在精确描述这笔账。1.1 现货电能量市场峰谷价差不是白拿的现货电能量市场的逻辑非常简单电力价格随时间波动储能作为“时间搬运工”在价格低的时段充电在价格高的时段放电赚取价差。但实际操作里有好几个约束挡在前面第一储能不能同时充电和放电这是一个二进制互斥约束。第二电池有SOC上下限你不能把电充到105%也不能放电放到0%。第三充放电都有效率损耗充进去1MWh放出来可能只有0.95MWh。这些约束不是边界条件那么简单它们直接决定了套利策略的空间。举个例子一组5MW/10MWh的磷酸铁锂储能充电效率95%放电效率95%峰谷价差如果是0.5元/kWh理论上每度电能赚0.5元但你充进去的电要打两次折扣实际利润大约是0.5 - 0.2/0.95/0.95 × 0.95的计算结果远远低于表面价差。很多刚接触这个领域的人把价格曲线一拉按理想效率算出一个看起来很美的收益最后发现根本跑不出来就是因为这些效率项和SOC约束没有被正确处理。1.2 调频辅助服务市场容量费与里程费两笔账调频辅助服务市场的收益结构和电能量市场完全不同。它不是靠电量价差赚钱而是靠“你为系统提供了调节能力”来赚钱。国内多数市场的调频补偿分成两部分容量补偿和里程补偿。容量补偿是说你预留了多少MW的调节能力系统按照中标容量给你固定费用相当于“占位费”。里程补偿是说你实际跟随调频信号动了多少按照调频里程结算相当于“计程车费”。储能参与调频有明显优势因为它的响应速度是秒级调节精度也高所以在调频性能指标上通常比火电高不少。但要注意调频不是免费午餐储能如果要提供上调频容量就必须留出对应的功率空间这块空间就不能同时用来做电能量市场的放电。于是问题就来了同一块电池每个时段要么去套利要么去调频或者两者按比例分配。这个比例怎么定就是交易决策的核心问题之一。1.3 两块收益共用一块电池机会成本才是建模核心把上面两部分放在一起看真正的建模难点不是单个市场的收益计算而是两个市场在同一个储能设备上的耦合。这个耦合体现在三方面。功率耦合约束充电功率加下调频容量不能超过额定功率放电功率加上调频容量同样不能超过额定功率。假设电池额定功率5MW如果你已经报了3MW的调频容量那最多只能再充2MW或者再放2MW这个物理限制必须写进约束。SOC耦合约束调频动作会改变SOC。如果你已经处在SOC上限附近理论上你没法再接受“向上调频”也就是需要继续吸收功率的调节指令因为电池满了。这个约束在简化模型里经常被忽略但在实际运行中非常关键后面我会专门讲这个坑。机会成本层面的耦合这是最微妙的。每个时段的储能容量是有限的你选择多参与调频就意味着放弃了这个时段的部分电能量套利收益反过来你选择多套利就放弃了调频补偿。收益最大化不是孤立地算两个市场的账而是要比较两个市场的边际收益把容量配置到收益更高的地方。很多人把两个市场分开建模再相加结果总是做不出合理的策略原因就在这里机会成本没有进入目标函数容量配置就失去了依据。2. 为什么必须上“双层模型”价格接受者假设在这里失效了很多教科书里的储能优化模型本质上都是“价格接受者”模型把市场价格当作给定的常数输入储能只负责调整自己的充放电策略来适应这个价格。这个假设在什么情况下成立当你的储能规模相对于整个市场非常小你的策略无法影响市场价格的时候成立。2.1 单层模型的隐藏假设与适用边界单层模型的目标函数通常长这样最大化总收入减去总成本其中每个时段的售电收入等于该时段市场价乘以放电功率购电成本等于该时段市场价乘以充电功率。市场价格是一个参数向量完全不受决策变量影响。这种模型的好处是简单、可解、好收敛适合做小规模分布式储能或用户侧储能的初步收益测算。但一旦研究对象变成大规模独立储能电站、共享储能或者储能聚合商问题就来了。当你的储能在低谷时段大规模充电低谷负荷被抬升低谷电价就会上涨高峰时段大规模放电峰时供给增加高峰电价就会被压低。这就是经济学里的价格反馈效应策略改变了市场环境而改变后的市场环境又反过来影响策略收益。单层模型完全没有办法刻画这个循环。它的结果本质上是一个“策略收益上限”而不是真实可得的收益。用这个上限去做投资决策往往过于乐观。2.2 双层博弈结构储能报价如何影响出清价要捕捉价格反馈效应需要把问题从“给定价格做优化”升级为“价格由市场出清决定而市场出清又受储能策略影响”的双层博弈结构。在这个结构里上层是储能运营商的决策问题选择每个时段的充放电功率、调频容量目标是自己利润最大化。下层是市场运营机构ISO/交易中心的出清问题在收到所有市场参与者的报价后通过求解一个经济调度或机组组合问题确定各机组出力和市场价格。储能运营商的策略会作为参数进入下层模型充放电功率参与系统的功率平衡调频容量参与调频容量约束下层模型求解出来的边际价格又反过来决定储能上层目标函数里的收益。这就是典型的Stackelberg博弈储能是领导者市场出清是跟随者。这个结构用生活化的例子类比就是普通小商贩去批发市场进货货价是固定的他只需要决定进多少货但如果一个大型连锁超市决定大规模采购某个商品它的采购量很可能会推高整个市场的批发价这时候“价格”就不是外生常数而是自身决策的结果。双层模型就是把这种“大鱼吃鱼饵”式的反馈效应纳入决策框架。2.3 反馈效应到底有多大一个直观的数值感觉在没有跑仿真之前你可能会觉得价格反馈效应是二阶量影响不大。但在某些参数设置下它实际上相当可观。一个粗略的估算逻辑是这样的如果某地区高峰时段负荷是1000MW而一个储能电站的放电功率是50MW占负荷的5%供需曲线的斜率决定了价格对负荷变化的敏感度。在高峰时段供需紧张的区域供需曲线往往比较陡峭5%的供给增加完全可能把边际电价压低5%-10%。低谷时段同理50MW的充电功率对低谷负荷的抬升效果也可能让低谷电价上涨3%-8%。对于峰谷价差本身就比较小的市场这种双向挤压可能把价差吃掉20%甚至更多。换句话说你用单层模型算出来的收益如果是100万双层模型算出来的实际收益很可能只有80万-85万。这20万的差距对于一个储能投资项目来说就是完全不同的IRR和投资决策结论。这也是为什么这一类研究在业内越来越受重视。3. 双层交易决策模型数学框架与求解路线这一章把数学模型讲清楚。我会用一个相对简化的版本来说明核心结构但保证所有符号和逻辑都是可以落到代码里的。3.1 上层储能运营商的收益最大化模型上层模型的时间尺度通常取日前市场也就是把一天分成T个时段每个时段15分钟或1小时。为了说清楚逻辑我用1小时一个时段T24来举例实际代码里扩展到96个时段完全一致。决策变量包括每个时段的充电功率Pch(t)、放电功率Pdis(t)、上调频容量Ru(t)、下调频容量Rd(t)、SOC状态变量以及两个二进制变量来约束充放电互斥。目标函数是全天总收益最大化收益由四部分组成放电收入Σ λ(t) × Pdis(t)充电成本Σ λ(t) × Pch(t)调频容量收入Σ [π_ru(t) × Ru(t) π_rd(t) × Rd(t)]调频里程收入Σ [α × π_mileage(t) × (Ru(t) Rd(t))]其中λ(t)是时段t的现货出清价π_ru、π_rd是上下调频容量补偿价格π_mileage是调频里程价格α是里程系数。这里的关键点是λ(t)不是外部给定参数而是下层市场出清模型的对偶变量拉格朗日乘子它与上层的Pdis(t)、Pch(t)之间存在耦合关系。这就是双层问题的核心。约束条件分为两类储能自身的运行约束以及下层出清模型转换过来的约束。储能自身约束包括SOC递推、SOC上下限、充放电功率上下限、充放电互斥、调频容量与功率耦合约束等这些在第四章代码部分会详细给出YALMIP写法。3.2 下层市场出清模型与KKT条件为了不让问题复杂到没法求解下层通常不做机组组合UC而是做经济调度ED也就是说机组启停状态固定只优化各机组出力。这个简化在学术界是可接受的因为本文关注的核心是储能策略对出清价的边际影响引入整数变量会让下层变成一个混合整数规划KKT条件不再适用求解难度陡增。下层的决策变量是各常规机组出力Pg(i,t)目标函数是最小化系统总发电成本Σ Σ C_i × Pg(i,t)。约束包括功率平衡约束Σ Pg(i,t) Pdis(t) - Pch(t) Load(t)以及每台机组的出力上下限约束、爬坡约束、线路潮流约束。为了讲清楚KKT推导先忽略爬坡和网络约束只保留功率平衡和出力上下限。对这样一个线性规划写出拉格朗日函数L Σ C_i Pg_i λ(Load - Σ Pg_i - Pdis Pch) Σ μ_max,i(Pg_i - Pg_max,i) - Σ μ_min,i(Pg_i - Pg_min,i)其中λ是功率平衡约束的对偶乘子对应系统边际电价μ_max,i和μ_min,i分别是机组出力上下限对应的对偶乘子。下层最优解的一阶必要条件stationarity条件是KKT的一部分是C_i - λ μ_max,i - μ_min,i 0这组等式的经济含义非常直观每台在线机组的边际成本经过上下限约束的影子价格修正之后等于系统边际电价。这正是经济调度中“等微增率”原则的数学表达。3.3 双层转单层互补松弛条件的线性化处理KKT条件中最难处理的是互补松弛条件。以机组出力上限约束为例互补松弛写成0 ≤ μ_max,i ⊥ (Pg_max,i - Pg_i) ≥ 0这个“⊥”符号表示两个条件至少一个取0要么机组出力没有达到上限此时上限约束不紧乘子μ_max,i 0要么乘子大于0此时机组出力严格卡在上限即Pg_i Pg_max,i。这个逻辑是“非此即彼”的本身是强非线性的没法直接丢给线性规划求解器。标准的处理办法是大M法。引入一个二进制变量z_max,i把互补松弛条件拆成两组线性约束0 ≤ μ_max,i ≤ M × z_max,i 0 ≤ Pg_max,i - Pg_i ≤ M × (1 - z_max,i)当z_max,i 1时第一组约束强制μ_max,i可以取大于0的值第二组约束的右侧变成0到M实际上把上限约束放开当z_max,i 0时μ_max,i被强制为0第二组约束强制Pg_i Pg_max,i。这样就把“二选一”的逻辑完整表达成了线性约束。对μ_min,i和对应的互补条件做同样的处理把所有KKT条件stationarity、primal feasibility、dual feasibility、complementarity作为约束并入上层模型双层问题就转化成了一个单层的数学规划带均衡约束MPEC问题。但这里还有一个坑必须绕过去就是目标函数里的双线性项。上层目标函数里有λ(t) × Pdis(t) 和 λ(t) × Pch(t)现在λ是下层KKT引入的变量Pdis、Pch也是变量它们相乘就是两个连续变量的乘积这是非凸双线性项求解器根本处理不了。解决办法是利用下层的强对偶条件。因为下层是线性规划原问题和对偶问题的最优值相等。通过强对偶等式可以把目标函数中λ和P的乘积项替换成机组出力、乘子、负荷等变量的线性组合。我从对偶问题出发对偶目标函数是max -Σ μ_max,i Pg_max,i Σ μ_min,i Pg_min,i λ(Load - Pdis Pch)根据强对偶它等于原问题目标函数Σ C_i Pg_i。我真正需要的是λ(Pdis - Pch)这一项的表达。把强对偶等式整理一下就能得到λ(Pdis - Pch) Σ C_i Pg_i Σ μ_max,i Pg_max,i - Σ μ_min,i Pg_min,i - λ × Load代入上层目标函数后所有双线性项全部消失整个问题变成一个混合整数线性规划MILP。这一步是整个求解路线里最耗脑细胞但也是最核心的一步很多复现论文代码的人卡在“求解器报非凸”这个问题上往往就是因为这里没有处理干净。3.4 求解器选型与算法流程转化完成之后问题就是一个标准MILP。可选求解器很明确如果模型规模不大T24或T96机组数量在10台以内Matlab自带的intlinprog完全够用如果规模较大或者需要反复跑多组灵敏度分析建议装Gurobi或CPLEX通过YALMIP调用求解速度会明显更快如果不想碰YALMIP直接用intlinprog的矩阵形式建模也可以但可读性会差很多尤其在调试大M约束的时候你会疯掉。我用的是YALMIP加Gurobi的组合建模效率高调试方便求解速度快。YALMIP本身是一个Matlab优化建模工具箱支持各种求解器接口对MILP、MIQP这类问题的支持都很成熟。每次求解前设置一下求解器参数ops sdpsettings(solver, gurobi, verbose, 2, gurobi.MIPGap, 0.001);整个问题的求解流程是先定义参数和变量再写储能自身约束接着把下层KKT条件和大M约束加进来最后用强对偶替换目标函数中的双线性项调用YALMIP求解对结果做后处理和可视化。4. Matlab代码实现我有话要说的几个关键环节这一章是实操重点。我会把代码组织方式、核心建模写法和调优过程都讲清楚。代码本身不算复杂但有几个细节如果不注意跑出来的结果会让你怀疑人生。4.1 代码结构与参数定义我的建议是把代码拆成四个文件param.m定义所有参数build_model.m构建优化模型solve_model.m调用求解器post_process.m做结果分析和绘图。这样好处是后续做灵敏度分析时只需要改param.m模型文件不用动。参数定义部分储能的关键参数如下%% 储能参数 param.Pmax 5; % 额定功率 MW param.Emax 10; % 额定容量 MWh param.eta_ch 0.95; % 充电效率 param.eta_dis 0.95; % 放电效率 param.soc0 0.2; % 初始SOC param.soc_min 0.1; % SOC下限 param.soc_max 0.9; % SOC上限 param.M 500; % 大M常数根据乘子量级调整 %% 时间参数 param.T 24; % 时段数扩展到96没问题 param.dt 1; % 时段长度小时 %% 市场价格参数简化示例 param.lambda_load [...]; % 负荷预测曲线MW param.pi_ru [...]; % 上调频容量价格元/MW param.pi_rd [...]; % 下调频容量价格元/MW param.pi_mileage [...]; % 调频里程价格元/MW param.alpha 2.0; % 调频里程系数注意单位统一问题。功率用MW能量用MWh时间用小时价格用元/MW或元/MWh。如果搞混了MW和kW或者元/kWh和元/MWhSOC递推约束会差1000倍结果要么不可行要么出现明显荒谬的充电策略。这是我最常遇到的低级错误检查优先级最高。4.2 YALMIP建模核心变量与约束怎么写变量定义部分直接按数学模型的决策变量来写Pch sdpvar(T, 1); % 充电功率 Pdis sdpvar(T, 1); % 放电功率 Ru sdpvar(T, 1); % 上调频容量 Rd sdpvar(T, 1); % 下调频容量 SOC sdpvar(T1, 1); % SOC变量T1是为了包含初始和期末状态 u_ch binvar(T, 1); % 充电状态标志 u_dis binvar(T, 1); % 放电状态标志储能自身约束的写法我用一个for循环逐时段添加代码可读性更好Cons []; Cons [Cons, SOC(1) param.soc0]; for t 1:T % SOC递推约束 Cons [Cons, SOC(t1) SOC(t) param.eta_ch * Pch(t) * param.dt ... - Pdis(t) / param.eta_dis * param.dt]; % 功率上下限与充放电互斥 Cons [Cons, 0 Pch(t) param.Pmax * u_ch(t)]; Cons [Cons, 0 Pdis(t) param.Pmax * u_dis(t)]; Cons [Cons, u_ch(t) u_dis(t) 1]; % 调频容量与功率的耦合约束 Cons [Cons, Pch(t) Ru(t) param.Pmax]; Cons [Cons, Pdis(t) Rd(t) param.Pmax]; Cons [Cons, Ru(t) 0, Rd(t) 0]; % SOC在提供调频容量后的安全边界约束 Cons [Cons, SOC(t1) param.eta_ch * Ru(t) * param.dt param.soc_max]; Cons [Cons, SOC(t1) - Rd(t) * param.dt / param.eta_dis param.soc_min]; end最后两个约束是我特别要强调的它们保证了即使调频指令让储能朝边界方向动作SOC也仍然在安全范围内。很多人第一次建模时会漏掉结果求解器给出的调频容量在SOC靠近边界时依然很大在工程上完全不可行。这个约束不要求对调频信号做精确仿真但给出了一个合理的包络。目标函数需要配合下层强对偶替换后的表达式来写不能直接写λ*Pdis因为此时λ是变量。实际写法是先把下层KKT变量λ、μ定义出来再把目标函数写成完全线性化的形式% 下层对偶变量 lambda_market sdpvar(T, 1); mu_max sdpvar(num_gen, T); mu_min sdpvar(num_gen, T); % 目标函数强对偶替换后的线性形式 Objective % 这里写替换后的线性目标见3.3节具体的替换公式在手推一次之后代入即可。这里我强烈建议不要试图在代码里自动推导直接在纸上推清楚再写进目标函数比调试一个自动推导脚本快得多。4.3 调频收益项的等效处理调频里程收益里有一个α系数它在目标函数里会把里程价格和容量价格合并成一个等效容量价格。实际代码中可以把参数预合并减少目标函数的项数% 等效调频容量收益价格 pi_eq_ru param.pi_ru param.alpha * param.pi_mileage; pi_eq_rd param.pi_rd param.alpha * param.pi_mileage;收益率 Σ (pi_eq_ru * Ru pi_eq_rd * Rd)这样表达式更干净也便于做灵敏度分析。α的取值需要根据当地市场的调频信号统计特性来确定我目前用的是2.0也就是1MW的调频容量平均对应2MW的调频里程这是参考了几个典型市场数据后取的中间值。4.4 调试中踩过的三个坑与解决过程第一个坑是双线性项处理不彻底。第一次实现时我没有做强对偶替换目标函数里保留了lambda和P乘积YALMIP直接报错说问题非凸。我一度以为需要换一个非线性求解器试了fmincon之后发现结果不稳定而且初值依赖非常严重。后来老老实实手推了强对偶把λ(Pdis-Pch)替换成线性表达式问题转成MILP之后Gurobi几乎是一秒内稳定收敛。这个教训说明模型结构的问题不能靠换求解器硬扛转换成合适的形式才是正道。第二个坑是大M常数的取值。开始我为了“安全”把M设成了1e6。结果求解器返回的互补条件虽然满足但某些乘子出现了非常大的数值震荡解的稳定性和合理性都很差。后来我改变了策略先跑一次不考虑互补约束的松弛LP统计对偶乘子的实际量级然后把M设置为该量级的5到10倍。在我这个储能模型里M取500左右就非常稳。大M不是越大越好过大的M会让求解器在数值上出现灾难性的误差。第三个坑是SOC的调频边界约束。第一次跑通模型后输出的Ru在某些时段填满了整个剩余功率空间而对应时段的SOC已经接近上限。表面上看起来所有约束都满足因为PchRu≤Pmax已经写了但实际运行中这个时候如果收到向上调频指令让储能继续充电电池会直接充满甚至过充。加上SOC(t1)η_ch×Ru×dt≤soc_max这个约束之后调频容量在SOC边界附近自动降下来了。这个约束在数学上是保守的它假设了最恶劣的调频方向但正是这种保守性才让策略在工程上可执行。5. 算例结果与决策规律仿真跑完我看到的几个结论代码跑通只是第一步更重要的是通过算例理解模型的输出和内在规律。我做了一组典型日仿真参数设置如下表所示。参数数值备注储能额定功率5 MW磷酸铁锂储能额定容量10 MWh2小时系统充电效率0.95放电效率0.95初始SOC20%SOC运行范围10%-90%低谷现货价200 元/MWh0:00-6:00高峰现货价700 元/MWh18:00-21:00调频容量补偿20 元/MW调频里程补偿15 元/MW调频里程系数2.05.1 典型日决策结果什么时候套利、什么时候留调频仿真结果清晰地呈现出了一个“时间分工”的规律。在低谷时段0:00-6:00现货价格很低但没有出现全功率充电的情况因为储能要预留部分容量参与下调频。只要低谷时段的下调频补偿收益相对于充电套利而言更高模型就会自动压低充电功率把一部分功率空间让给调频。在傍晚高峰时段18:00-21:00现货价格飙到700元/MWh这时候套利收益远高于调频补偿模型几乎是全功率放电只保留很少的调频容量。中间时段9:00-16:00现货价格在300-400元/MWh之间波动调频收益的相对价值提升模型会明显增加调频容量占比。这个结果非常符合经济直觉容量总是流向边际收益更高的市场。有意思的是SOC轨迹并不像我最初想象的那样“谷底充满、峰顶放空”。因为调频容量也消耗SOC空间所以SOC在非套利时段也会因为调频动作的预留而产生波动。这再次说明联合优化的必要性如果你把调频当外挂模块套利和调频分开求解SOC轨迹会对不上两个市场会同时争夺同一批能量做出来的策略是完全不可信的。我把典型时段的数据整理成表方便对照查看决策的数值量级时段充电功率(MW)放电功率(MW)上调频容量(MW)下调频容量(MW)SOC(%)2:002.800.52.2358:001.201.81.05214:0003.01.20.87019:0005.00.10.13023:000.601.51.8455.2 单层与双层模型收益对比差距不是小数目这组算例最让我印象深刻的结果是对比单层与双层模型的收益差异。在我的参数设置下单层模型预估的日收益是12400元。双层模型在计入价格反馈效应后实际收益只有10100元左右差距约18.5%。这个差距的来源很好解释。我专门把单层模型最优策略重新代入了下层出清模型发现储能在低谷时段2.8MW充电负荷抬高了约3-4分的低谷电价高峰时段5MW的放电又压低了约5分的峰时电价。一进一出实际价差比单层模型假设的价差缩水了近20%。这18.5%的收益差不是模型误差而是储能自身策略带来的真实价格冲击成本。如果站在投资决策的角度看这个问题结论会更严肃。假设一个储能电站全年运行330天单层模型估算的年收益是409万元双层模型修正后是333万元差了76万元。对一个几千万元投资的储能项目来说这76万直接改变收益率好几个百分点。论文里常说“双层模型结果更保守、更接近真实”这句话放到具体数字里才有说服力。5.3 灵敏度分析容量、补偿价格如何改变最优策略我只做了两个方向的灵敏度分析但结论已经很有参考价值。第一个方向是容量变化。当储能规模从5MW/10MWh逐步提高到20MW/40MWh双层模型与单层模型的收益差距从大约10%扩大到25%左右。原因很明显储能规模越大它对市场价格的影响力越强价格反馈效应越显著。这给实际工程一个判断依据如果你建的储能场站容量占当地峰值负荷的比例小于1%单层模型基本够用如果超过3%-5%就必须认真对待双层模型给出的修正。第二个方向是调频补偿价格变化。把调频容量补偿从20元/MW提高到60元/MW后储能最优策略中的套利时段明显减少原本在早高峰和晚高峰之间的一些小价差套利被放弃转而把容量留给调频。这说明调频收益在合理参数区间内的吸引力并不低联合优化能让储能在不同市场之间“见风使舵”这是分开建模完全做不到的。6. 从复现到延伸后续可以继续深化的方向模型和代码跑通之后我结合自己做课题的经验梳理几个它自然延伸的方向这些也经常是论文深化和实际项目落地的突破口。6.1 考虑现货价格与调频信号的随机性日前市场虽然会提前发布预测价格但实际价格总是有偏差。调频信号的随机性更明显它本质上是一个秒级波动的随机过程。如果要求策略在多种场景下都表现稳定可以考虑随机优化或分布鲁棒优化。实现上比较成熟的路径是场景法生成多组现货价格场景和调频里程系数场景对每个场景求解一遍双层模型再通过场景聚合得到一套综合决策。代价就是计算量成倍增长。T96、10个场景、20台机组的情况下MILP规模会非常可观。我的经验是先聚类场景比如用K-means把1000组历史场景聚类成5到10个典型场景再分别求解效果和全场景集差不多但求解时间大幅下降。这也是为什么我把代码做了模块化设计——换场景输入只需要改参数文件模型文件完全不用动。6.2 把电池老化成本纳入目标函数储能充放电循环会导致电池寿命衰减这在长时间尺度交易决策里是不能忽略的隐性成本。一个比较初级的做法是在目标函数里增加一个循环成本项每次充放电循环按放电量乘一个单位退化成本系数。更精细的做法是用DoD放电深度相关的循环寿命曲线把不能直接线性化的曲线分段线性化作为约束加入模型。加入老化成本之后模型的行为会发生明显变化它不再追求最大化单日套利收益而是会在套利收益和电池寿命之间做权衡。典型的表现是浅充浅放的策略会增加深度循环的次数会减少。这一点对实际工程非常重要因为收益表上的利润如果完全被替换成本吃掉了项目经济性就要打很大的问号。6.3 做研究时的一个提醒不要为了双层而双层最后分享一个我在理论和实践之间来回碰壁后得到的体会。双层模型不是万能的也不是所有储能市场决策都需要上双层。如果你的研究对象是户用储能、小型工商业储能或者整个储能电站装机容量占当地负荷的比例不到1%那么价格反馈效应微乎其微单层模型已经足够强行套用双层模型反而会把问题复杂化审稿人也会质疑建模必要性。判断要不要用双层模型的建议标准很简单先做一次单层模型把最优策略拿回市场出清模型里核算一遍实际收益如果两者差异超过5%就说明反馈效应不可忽略双层模型是必要的如果差异只有1%-2%说明你的储能规模对市场来说还很小单层模型完全够用。我在复核几个不同数据集的算例时发现很多情况下单双层差异并没有论文里说的那么夸张真正关键的是市场供需曲线的斜率也就是价格弹性。这一点在写论文和做报告时提前想清楚可以少走很多弯路。