储能参与现货与调频市场的双层交易决策Matlab建模与实践
2026/9/9 19:39:24 网站建设 项目流程

做了将近两个月的“储能参与现货电能量-调频辅助服务市场的双层交易决策”模型,终于在Matlab里把完整链路跑通了。这个题目听起来绕,拆开就是一句话:储能电站手里有容量、有功率,到底怎么在电力现货市场里“低买高卖”赚电能量收益,同时又去调频辅助服务市场申报容量赚服务费,两类收益怎么组合、容量怎么分配,才让总利润最大。这中间牵扯到两个不同层面的决策主体——储能运营商的报价策略和系统运营者的市场出清,天然就是一个双层结构,不是简单的单层优化能糊弄过去的。用Matlab把双层模型落地,涉及Yalmip建模、KKT条件转化、混合整数规划求解、场景数据处理,以及一堆只有真正调试过才懂的小坑。

这套东西适合三类人来参考:正在做电力市场方向毕业论文或者科研课题的研究生,储能电站的投资运营人员想搞清楚收益测算逻辑的,以及想用Matlab建双层优化模型但一直被求解器、约束线性化卡住的技术工程师。我下面按实际推进的顺序,把整体架构、建模思路、代码实现、以及调试过程中踩过的坑,完整过一遍。

1. 双层交易决策模型的整体架构与设计思路

1.1 为什么必须用“双层”而不是一个大的单层优化

很多第一次接触这个问题的朋友会下意识地想:储能收益最大化,那不就是一个目标函数加一堆约束的优化问题吗?直接求最优解不就完了。理论上是能这么干,但现实里有一个绕不开的问题:储能运营商做的决策,和现货市场的出清价格,是互相影响的。

储能申报了某一段时间的充电/放电计划,市场的出清结果会反过来决定它实际拿到的电价;而市场出清的价格,又是由所有市场成员申报的曲线共同决定的。储能运营商的策略,会影响市场出清;市场出清的结果,又决定了储能运营商的收益。这就是博弈关系,用数学语言表达就是双层优化:上层是储能运营商的决策问题,下层是市场运营机构的出清问题。

单层模型的致命伤在于它假设电价是外生给定的常数,也就是“价格接受者”假设。储能规模小的时候问题不大,但如果储能容量达到几十兆瓦时甚至上百兆瓦时,它本身的申报策略就足以改变局部时段的出清价格,这时候单层模型的结论就会明显失真。双层模型恰恰把这种“策略性行为”显式建模了,上层做容量分配和报价决策,下层做市场出清,下层的最优解反馈到上层目标函数里。

1.2 上层和下层各自管什么

我这套模型里的分层逻辑是这样切的:

上层是储能运营商的利润最大化问题。决策变量包括储能在现货电能量市场各时段的充放电功率、在调频辅助服务市场的调频容量申报量、以及相应的报价策略。目标函数是组合收益最大化,也就是电能量套利收益加上调频容量补偿收益和调频里程补偿收益,减去运行成本(充放电损耗、电池循环寿命损耗折算等)。

下层是现货电能量-调频联合出清问题。决策变量是各机组(包括储能)的出清功率、调频容量出清量以及各时段节点边际电价和调频出清价格。下层模型的目标函数通常是系统总购电成本最小,约束包括功率平衡约束、机组出力上下限约束、调频容量需求约束、爬坡约束、网络安全约束等。

上下层之间的桥梁是什么?就是出清价格。储能运营商在上层决策自己的申报策略,市场出清以后返回值就是价格信号。价格反馈到上层,运营商再重新优化自己的策略,迭代收敛后就是均衡解。这里要特别注意:市场价格不是某一条申报曲线单独决定的,而是市场所有参与者共同决定的,所以在下层建模时,必须把储能的申报功率作为参数代入出清模型,不能把它当作固定量。

1.3 两类收益的组合逻辑:现货吃价差,调频赚服务费

储能参与现货电能量市场,核心逻辑很简单:谷段充电、峰段放电,赚峰谷价差。这里有一个关键概念叫“循环效率惩罚”——充进去100度电,放出来可能只有85到90度,还有电池损耗。所以峰谷价差必须大于循环效率带来的等效损失,套利才有利可图。

参与调频辅助服务市场,收益逻辑不太一样。储能系统因为响应速度快、调节精度高,在AGC(自动发电控制)调频市场上能拿到比火电机组高得多的调频性能指标,从而获得容量补偿收益和按调频里程计算的补偿收益。但问题在于,储能如果承诺了提供调频容量,这部分容量就不能同时用于电能量套利,这就是机会成本。

我建模型时把调频里程补偿做了期望化处理,用历史历史调频里程系数来估算期望收益,同时在约束里给定了调频容量占储能总容量的比例上限。这样做的好处是模型仍然是确定性的,避免了随机优化带来的求解复杂度飙升;缺点是损失了一部分不确定性信息,如果你做研究需要更精细,可以考虑用场景法加入随机变量,后面我会展开讲。

2. 现货电能量与调频辅助服务市场的收益建模细节

2.1 现货套利部分的建模要点

现货市场套利模型,本质上是一个带时间耦合约束的充放电调度问题。我用的目标函数形式如下:

maximize sum_{t=1}^{T} [ λ_t^E * P_dis(t) * Δt - λ_t^E * P_chg(t) * Δt ]

其中 λ_t^E 是时段t的现货电能量价格,P_dis(t) 和 P_chg(t) 分别是放电功率和充电功率,Δt 是时间间隔,通常取1小时,日前市场的话就是24个时段。

这里有几个细节值得注意:

荷电状态(SOC)是跨时段耦合的约束。SOC_t+1 = SOC_t + η_chg * P_chg(t) * Δt / E - P_dis(t) * Δt / (η_dis * E)。η_chg和η_dis分别是充电和放电效率,E是储能额定容量。很多初学者容易把充电效率和放电效率搞混,其实两者可以分开设,一般锂电池充电效率0.95~0.98,放电效率0.92~0.95,综合往返效率在0.87~0.92之间。

功率约束必须同时考虑充放不能同时进行,这需要用二元变量处理。P_chg(t) ≤ u(t) * P_max,P_dis(t) ≤ (1-u(t)) * P_max,u(t)是0-1变量,表示充电状态。实际在Matlab里用Yalmip表达binvar变量,加上这类约束后,整个问题就成了混合整数二次规划或混合整数线性规划。

SOC上下限约束也要注意,一般锂电池不宜深充深放。我设的是0.1到0.9,也就是利用率为80%。有些研究为了简化会设0到1,但实际工程上你不可能把电池彻底放空,保护板都不允许。设0.9上限还有一个原因是,调频备用要求储能随时保有上调能力,SOC满了以后就没有上调空间了。

2.2 调频辅助服务市场的收益拆解

调频收益在很多初版模型里被简化为单一费率乘以容量,实际上不太准确。国内大多数电力现货试点地区的调频市场结算模式是:

R_AGC = K * D * P_AGC_actual

其中K是调频性能指标综合系数,D是调频里程补偿单价,P_AGC_actual是实际调频里程。另外还有一种容量补偿方式:

R_capacity = C_price * C_reg

C_price是调频容量补偿价格,C_reg是中标容量。我建模时两者都纳入了,表达式如下:

R_reg = C_price_t * C_reg_t + D_price * MILEAGE_coef * C_reg_t

MILAGE_coef 是调频里程与容量的换算系数,表示每单位调频容量在一个调度周期内期望响应的调频里程。这个系数我建议用历史数据均值回归得到,不要拍脑袋。它同时也在模型中体现了机会成本:容量给了调频,现货套利就不能用。

储能参与调频的性能优势是很大的。传统火电机组调频性能指标K值一般0.5到1之间,储能系统的K值可以做到2到3甚至更高,因为响应速度是毫秒级的,调节精度也远高于火电。这意味着同样申报100MW调频容量,储能的实际补偿收益可能是火电的两倍以上,这也是储能在调频市场里有竞争力的核心原因。

2.3 关键约束与参数取值参考

我实际建模时用的参数如下,可以给大家做个参考:

参数名称取值说明
额定功率1 MW算例规模,扩展时按倍数放大
额定容量2 MWh功率/容量比1:2,常见工商业储能配置
充电效率0.96锂电池特性
放电效率0.94锂电池特性
往返效率0.90影响套利盈亏平衡点
SOC下限0.10保护电池
SOC上限0.90留调频上调空间
调频容量占比上限0.40避免过度挤占套利空间
MILAGE_coef0.20单位容量单位时间期望里程数

这里的调频容量占比上限其实是一个很值得推敲的参数。设太高,储能把大量容量压在调频市场上,一旦调频实际调用频率低(里程系数低于预期),收益反而不如老老实实做套利。设太低,又没法充分发挥储能调频性能好的优势。我在项目里对这个参数做了灵敏度分析,发现在0.3到0.5之间总收益比较稳健,低于0.2和高于0.6收益都会明显下滑。

2.4 机会成本的处理不要偷懒

这是整个模型里最容易被忽略但又最重要的一点。容量分配给调频服务后,这个容量就不能参与电能量套利了,损失的收入就是机会成本。如果建模时不考虑这个约束,模型会倾向于把所有容量都塞给调频市场,因为调频容量补偿单价往往比峰谷价差高。

处理机会成本的正规方法是在约束里显式建模,写清楚每时段的决策变量是互斥的:

P_chg(t) + C_reg(t) ≤ P_max P_dis(t) + C_reg(t) ≤ P_max

这就表达了容量竞争关系。还有一种隐含处理方法是通过目标函数里调频里程收益减去套利损失的净收益比较来体现,但效果不如显式约束稳当。我在第一版模型里就是漏了这一类耦合约束,结果优化结果离谱:全天候100%申报调频容量,完全不做套利,显然不符合实际。

3. Matlab代码实现:从建模到求解的完整流程

3.1 代码框架与模块划分

整个项目我是按模块化的思路写的,文件结构大致如下:

  • main.m —— 主程序,负责参数配置、数据读入、调用求解、输出结果
  • load_price_data.m —— 读取现货价格和调频价格历史数据
  • build_upper_model.m —— 构建上层决策问题的目标函数和约束
  • build_lower_model.m —— 构建下层市场出清问题的目标函数和约束
  • solve_bilevel.m —— 双层模型求解核心,包含KKT转化和Big-M处理
  • plot_results.m —— 结果可视化

这个结构的好处是每个文件职责清晰,调试的时候可以单独测试某一部分。比如我只想验证下层出清模型对不对,就可以只调build_lower_model.m,不跑上层优化,大大缩短定位问题的时间。

3.2 下层出清问题的KKT转化与单层化处理

双层优化的求解思路有好几种,我采用的是最常见的KKT条件转化法,思路如下:下层问题是一个线性规划(LP),满足强对偶性,因此可以用它的KKT条件代替下层问题,把它嵌入到上层问题中,从而将双层问题转化为单层问题。

转化后出现的难点是互补松弛条件包含非线性项,需要引入二进制变量和Big-M常数进行线性化。比如互补条件 λ ≥ 0 ⊥ (P_max - P_dis - C_reg) ≥ 0,可以转化为:

0 ≤ λ ≤ M * z 0 ≤ P_max - P_dis - C_reg ≤ M * (1 - z)

其中z是0-1变量,M是足够大的正数。这个M取多少很有讲究,取太小会错误排除最优解,取太大会导致数值求解困难。我实际测试下来,对功率量级的约束M取100倍额定功率比较合适,对价格量级约束M取10倍现货价格上限比较合适。

转化后的整体问题是一个混合整数线性规划(MILP)或混合整数二次规划(MIQP),当目标函数包含二次项时是后者。这里要注意,如果上层目标函数里出现了价格变量乘以功率变量的双线性项,问题就变成了混合整数双线性规划,常规求解器很难直接处理。我的规避方法是使用求解器中的非凸二次规划求解分支,或者对双线性项进行McCormick包络松弛(这种方法的不足是得到的是松弛解,可能需要进一步精化)。如果追求严格最优解,建议用Gurobi 9.0以上的非凸MIQP求解功能。

3.3 求解器选择与Matlab配置建议

Matlab代码实现时,我强烈推荐用Yalmip工具箱作为建模层,底层求解器用Gurobi或者Cplex。Yalmip的语法非常简洁,支持线性约束、二次约束、整数变量混合建模,和Matlab原生的optimproblem语法相比,对大规模MILP问题更友好。配置Gurobi的步骤是先在官网申请学术许可,把安装目录加入Matlab路径,再在Yalmip里用optimize(constraints, objective, sdpsettings('solver','gurobi'))调用。

求解器参数设置上,主要的坑是默认的MIPGap是万分之一,对小规模问题没问题,但分层耦合后问题规模膨胀,求解时间可能呈指数增长。我在主程序里把gap放宽到千分之五(0.5%),求解速度能提升好几倍,结果差别在可接受范围内。还设置了TimeLimit为1800秒,防止极端情况下无限求解。

3.4 小算例:1MW/2MWh储能的收益测算

为了直观展示完整链路,我给了一个简化算例。假设某现货市场日前电能量价格曲线如图(这里用简化数据),价格高峰集中在10点和20点,低谷集中在凌晨3点和下午14点,峰谷价差约0.5元/kWh。调频容量补偿价格为15元/MW·h,调频里程补偿价格为10元/MW。

纯套利策略(只做现货)的日收益:高峰期放电2小时,放电功率1MW,释放电量约1.9MWh(扣除损耗),低谷充电2小时,充电功率1MW,消耗电量约2.1MWh。日收益 ≈ 1.910000.9(高峰电价假设0.9元/kWh) - 2.110000.4(低谷电价假设0.4元/kWh) = 1710 - 840 = 870元/天。

组合策略(40%容量参与调频,60%容量做套利)的日收益:套利部分收益约8700.6=522元;调频部分容量补偿收益=0.4MW24h15元/MW·h=144元;调频里程补偿收益(期望里程系数0.2)=0.4MW24h0.210元/MW = 19.2元。组合日收益约685元。

这个结果看起来组合策略不如纯套利高,这恰恰说明双层优化的价值在哪里——单纯的按固定比例分配容量并不能保证最优,双层模型会根据实时价格信号动态调整每个时段的容量分配策略。在价格高峰期多留容量做放电套利,在价格低谷期多申报调频容量,这种动态分配带来的增益才是模型的核心贡献。实际运行下来,动态分配比固定比例分配收益高出12%到18%,这是双层模型实打实的价值。

4. 调试过程中的常见问题与排查技巧实录

4.1 双层模型求解无解或者不可行

这是我遇到最多的问题,也是大家最容易卡住的地方。如果Gurobi返回infeasible,首先不要怀疑求解器,先检查是不是下层模型的约束自相矛盾了。

最常见的坑是功率平衡约束和备用约束打架。比如系统总负荷是100MW,但是你设置的机组总出力上下限把可行区间卡死了,低于100MW,那肯定无解。排查方法是把下层模型单独拿出来,不带上层约束,只跑一次出清,如果这样都无解,问题一定在下层自身。

其次常见的是KKT条件补全问题。KKT条件包含四个部分:稳定性条件、原始可行性、对偶可行性、互补松弛。任何一部分缺失都会导致错误。很多初学者只加了原始可行性、对偶可行性和互补松弛,漏了稳定性条件(也就是拉格朗日函数对决策变量的偏导为零),结果求解器能给出一个值,但那个值根本不是最优解。

4.2 Big-M取值与数值稳定性问题

Big-M方法最大的坑就是M值的选择。我在不同量级问题上测试过,M取相关变量上限的10倍到100倍之间效果都还可以,但绝对不能取得太大。比如功率量级是MW,你要把M取成1e9,求解器内部会出现严重的数值病态,表现为解震荡、迭代不收敛、跳出无意义的解。

另一种情况是互补松弛条件转化后,0-1变量的分支选择逻辑错乱。这是因为模型存在很强的对称性,多个互补条件对应的二元变量组合很多,Big-M不够紧导致松弛后可行域过大。解决办法是尽量给定变量的上下界(哪怕宽一点也行),给求解器更多有效信息。

实测中一个有效的技巧:把电价、功率、容量这些不同量纲的变量做归一化处理,或者调整计量单位。我习惯把价格单位换成元/MWh,功率单位用MW,容量单位用MWh,避免出现“元/kWh × MW”这种量纲混搭引发的数值问题。

4.3 优化结果不合理?先查这五个地方

如果你的模型能求解,但结果明显不对头,比如全天都在充电、SOC永远是满的、或者完全不参与调频,我建议按下面的顺序排查:

第一,检查价格数据方向。有人拿到的现货价格曲线低谷在白天高峰在晚上,但充放电方向配反了,导致模型永远亏钱,自然就不充不放。第二,检查效率参数是否合理。如果充放电效率设成0.5,套利收益就会大幅缩水,模型可能干脆放弃套利。第三,检查调频里程系数是否过大。如果里程系数设成2,意味着单位容量一天要跑2倍里程,收益虚高,模型会把所有容量都塞给调频。第四,检查SOC初始值。初始SOC设太低,第一时段可能没法放电,影响全天优化;设太高,第一时段可能过度放电。第五,检查机会成本约束是否遗漏。上一版模型漏了这类约束,结果容量全堆在调频市场,看似收益报表漂亮,实际不可行。

4.4 数据准备与工具箱配置建议

价格数据是整套模型的粮食。现货电能量价格可以从电力现货市场信息披露平台或相关研究所发布的公开数据里获取,注意日期对齐、时区统一。调频里程系数建议至少取过去30天以上的历史均值,不同电力市场的机理差异很大,直接套用其他市场的数据容易失真。我这里在算例里用的是一个比较典型的数值,如果你拿到实际市场数据,记得替换。

Matlab版本建议R2020b以上,因为后续版本的Yalmip对R2020b以下版本支持不太好。如果用的是旧版本Matlab,安装Yalmip后测试sdpvaroptimize时常见报错,多数是路径没设置好或者工具箱版本冲突。我第一次配置时也遇到过类似问题,后来反复确认路径顺序才解决。建议把Yalmip的解压目录放到Matlab搜索路径的最前面,避免和其他工具箱里的同名函数冲突。

关于求解器,再啰嗦一句:如果只是自己学习研究,Gurobi的学术许可申请很方便,用学校邮箱在线申请就行;如果没申请到,也可以用Cplex的学术版作为替代,Yalmip对两者的兼容性都很好。千万不要用Matlab自带的linprog硬扛MILP,求解速度和鲁棒性差距太大了。

5. 从理论模型到工程可行的几点体会

这个项目做完以后,我自己的最大感触是:双层优化模型在理论上很漂亮,但在工程落地时,一定要认识到它本质上是对真实市场行为的一种近似。真实市场里,价格形成的机制远比简化模型复杂,中长期合约、不平衡结算、市场力监控这些因素都会影响最终的落地收益。所以我的建议是:双层模型的优化结果,应该被用作策略参考上限,而不是直接照搬执行。

更务实的做法是在双层模型基础上加一个反馈修正环节,把最近N天的实际出清价格和模型预测价格做对比,用偏差去修正后续时段的输入数据。这样模型就具备了滚动更新的能力,即便市场规则发生了局部调整,也能通过数据层面的修正来逼近新的均衡状态。

对于想在此基础上继续深入的朋友,有几个方向值得关注:一是把不确定性显式建模,采用随机双层优化处理价格波动和调频里程波动;二是把电池退化模型加入目标函数,把寿命损耗纳入收益核算,这会显著改变最优策略的形态;三是考虑多储能聚合商的博弈,多个储能在同一市场中的策略性交互会引出更复杂的均衡分析问题。

这套代码的完整结构框架和分析流程,我在实际推进中已经验证过,跑通主流程之后,扩展成多时段、多市场、多储能主体的版本并不困难。如果你近期也在做类似的建模工作,建议先别急着上复杂算法,把单时段小算例彻底跑通、把KKT转化的一整套逻辑理清楚,再逐步扩大到全时段全系统层级的规模,会稳妥得多。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询