资讯动态

旋转备用联合出清模型:原理、Matlab实现与出清价格分析

发布时间:2026/9/10 5:44:10 来源:尧图企业网站定制
1. 为什么要单独研究旋转备用出清1.1 旋转备用在电力市场里的角色做电力市场出清模型的朋友应该都有体会能量市场大家做得很多但真正把辅助服务一起纳入联合优化的时候问题就开始复杂了。旋转备用Spinning Reserve是所有辅助服务品类里最特殊的一个它要求机组处于同步运行状态能够随时响应调度指令在规定时间内完成出力调整。也就是说不是所有机组都有资格提供旋转备用你停机了就是停机了冷备用、热备用都无法替代旋转备用的实时响应能力。那为什么要把旋转备用单独拿出来做市场出清模型核心原因在于旋转备用的成本结构和能量不同。能量市场的报价反映的是发电变动成本而旋转备用一旦被调用机组就失去了在能量市场出售这部分电量的机会这个损失叫作机会成本。更麻烦的是旋转备用的容量占用和能量出清互相耦合——一台机组如果把自己的容量全部报给能量市场它就没有办法再提供备用反过来预留了备用容量就压缩了它在能量市场的竞价空间。所以主辅助服务市场出清绝不是一个简单的“能量一个场、备用一个场”的独立问题而是需要在一个统一框架下联合优化。我接下来说的这套Matlab代码实现解决的就是这个联合出清问题。它把能量市场出清与旋转备用市场出清合并进同一个优化模型用线性规划求解最终同时得到机组出力计划、备用容量分配、能量出清价和备用容量出清价。这套模型在学术研究中很常见也是目前国内电力现货市场“电能量辅助服务”联合出清的基本思路之一适合电力市场方向的研究生、售电公司分析人员以及电网调度侧做市场规则验证的工程师参考。1.2 联合出清与顺序出清的取舍先聊一个每个做辅助服务出清的人都会面对的问题到底是顺序出清还是联合出清顺序出清很简单先跑能量市场跑完拿到机组出力和边际电价再根据结果安排备用容量。这种方式的优点在于规则清晰、计算拆分缺点是能量市场出清结果未必给备用留出足够的容量空间。极端情况下可能出现能量市场把某台大机组的容量全部用满到了备用市场发现系统旋转备用不足再回头强行压低能量市场出清结果造成二次调整和不公平的结算。联合出清则把能量和备用放进同一个目标函数里让优化器来自动权衡“发一度电”和“留一兆瓦备用”哪个更有价值。从数学本质上讲联合出清是在满足负荷平衡、备用容量需求、机组出力上下限等全部约束的前提下求总购电成本最小化。这样处理的好处是备用容量占用不会凭空消失它在目标函数里天然抬高了机组的综合边际成本出清价格能够更真实地反映系统资源稀缺性。我个人的观点是研究阶段优先做联合出清模型它更贴近“市场出清”这个词的本质。等联合出清的代码跑通了再去对比顺序出清的结果你会非常直观地看到两种机制对机组备用分配和价格的影响差异。这也是很多论文的标准做法。2. 出清模型的数学表达2.1 目标函数设计思路旋转备用出清模型的目标函数我在项目里用的是这样一个形式在实际建模中我假设所有机组在出清时刻都处于运行状态因此目标函数只包含两部分能量费用和备用容量费用。能量费用用机组的能量报价曲线通常近似为分段线性函数乘以出力计算备用容量费用用备用报价乘以备用容量计算。目标函数可以表达为[ \min \sum_{i1}^{N} C_i(P_i) \sum_{i1}^{N} R_i \cdot RC_i ]其中(P_i) 是机组 i 的有功出力(C_i(P_i)) 是机组 i 的能量成本函数简化为线性函数 (C_i(P_i) a_i P_i b_i u_i)(R_i) 是机组 i 提供的旋转备用容量(RC_i) 是机组 i 的备用容量报价为什么用线性函数而不是二次函数这是我在实际项目中踩过的一个选择点。线性规划求解效率高、收敛稳定出清价格信息提取非常方便——对偶乘子就是价格。二次规划虽然能更精细地反映机组煤耗曲线但求解速度和数值稳定性都要差一些而且对于市场出清模型分段线性报价更接近真实市场规则。国内电力现货市场目前采用的分段报价机制本质上就是线性化处理。2.2 约束条件逐条拆解模型的核心约束我在代码里写全了这样几条第一条是系统功率平衡约束[ \sum_{i1}^{N} P_i D ]D 是系统总负荷。这个约束的拉格朗日乘子就是系统能量的边际价格——也就是我们常说的系统能量出清价SMPSystem Marginal Price。很多做市场分析的朋友问我出清价到底怎么从模型里拿答案就在这条约束的对偶变量里。第二条是旋转备用容量需求约束[ \sum_{i1}^{N} R_i \geq R_{req} ](R_{req}) 是系统所需旋转备用容量。旋转备用需求怎么定工程实践里比较通用的做法是按“最大单机容量”设定也就是系统需要保证任何一台机组跳闸后剩余旋转备用仍然能满足负荷需求。也有的规则按系统峰值负荷的某个百分比比如5%~10%设定。在代码里我把 (R_{req}) 设成参数方便不同场景切换。第三条是机组出力与备用容量耦合约束[ P_i R_i \leq P_{i,\max} ][ P_i \geq P_{i,\min} ][ 0 \leq R_i \leq R_{i,\max} ]这里最关键的是 (P_i R_i \leq P_{i,\max})。它表达的是物理事实一台机组的出力加上它预留的备用容量不能超过机组的最大技术出力。这个约束直接体现了“能量和备用竞争同一块容量”的本质也是联合出清和顺序出清结果差异的主要来源。第四条是旋转备用响应能力约束[ R_i \leq r_{i,10} \cdot 10 ]这表示机组提供的旋转备用量必须在10分钟内能够完全响应即满足10分钟爬坡速率约束。如果机组爬坡速率很快这个约束自然松弛如果机组爬坡慢即使有富余容量也不能提供同样多的备用。这是旋转备用区别于其他备用品种的重要约束很多初学做模型的人容易漏掉。2.3 旋转备用价格怎么算能量出清价的提取上面说了取功率平衡约束的对偶乘子。备用容量出清价格则要取备用容量需求约束的对偶乘子。值得特别注意的是当备用容量需求约束紧即 (\sum R_i R_{req})时备用价格等于边际机组提供一单位备用的边际成本而当备用容量充裕、约束松弛时备用出清价格会趋向于零。这不是模型bug而是市场实际的反映——备用不缺的时候额外的备用容量不会产生价值。另外还有一个容易被忽略的细节就是能量价格与备用价格之间的联动关系。为什么备用价格很少等于备用报价因为那些同时处于能量出清边际的机组多提供一单位备用就意味着少发一单位电这部分能量收益损失必须通过备用价格补偿。这就是为什么很多文献强调旋转备用的完整成本应该包含“运行成本增量 机会成本”两个部分。我建议在代码求完价格后把每台机组的机会成本单独算出来你会看到它和备用出清价格之间高度相关。3. Matlab代码实现落地3.1 建模工具选型Matlab做优化建模我在这个项目里用的是Yalmipgurobi的组合方案。纯新手可能对Yalmip比较陌生我稍微解释一下Yalmip是Matlab下一个免费的开源建模工具它把线性规划、整数规划、二次规划等不同类型的问题统一成一套建模语法底层再调用不同的求解器比如gurobi、cplex、mosek等。用Yalmip有几个好处。第一是建模速度快它允许直接用符号变量定义优化变量写约束的方式和数学表达式几乎一一对应调试起来非常方便。第二是换求解器成本低同一套代码在gurobi和cplex之间切换只需要改一行设置。第三是拿对偶乘子方便yalmiptime里提供了dual(constraint)接口直接提取约束对应的拉格朗日乘子不需要手动翻译KKT条件。不过有一点我要提醒Yalmip安装后一定要自己装一个求解器它本身不包含求解功能。如果用的是sedumi或linprog也能跑小算例但速度太慢商用问题建议还是gurobi或cplex有学术license免费申请。3.2 核心代码框架下面这段是模型的核心实现我省略了数据读取和结果绘图部分保留最关键的建模段。代码里机组数量、负荷数据、备用需求这些都是按矩阵和向量组织的方便扩展到更大规模的系统。%% 旋转备用联合出清模型 % 决策变量: P_i 出力, R_i 备用容量 % 目标: 能量成本 备用容量费用最小化 % 求解器: gurobi via yalmip % 基础数据3机系统示例 Pmax [100; 150; 200]; % 最大出力 MW Pmin [20; 30; 40]; % 最小出力 MW a [0.20; 0.25; 0.18]; % 能量报价 元/MWh b [50; 60; 55]; % 空载成本 元/h RC [30; 35; 28]; % 备用容量报价 元/MWh Rmax [50; 60; 70]; % 最大可提供旋转备用 MW ru10 [6; 8; 10]; % 10分钟爬坡速率 MW/min D 300; % 系统总负荷 MW Rreq 100; % 旋转备用需求 MW % 定义优化变量 P sdpvar(3, 1); R sdpvar(3, 1); % 目标函数 Objective a*P b*ones(3,1) RC*R; % 约束 Constraints []; % 功率平衡 Constraints [Constraints, sum(P) D]; % 旋转备用需求 Constraints [Constraints, sum(R) Rreq]; % 机组出力上下限 Constraints [Constraints, P Pmin]; Constraints [Constraints, P Pmax]; % 出力和备用耦合约束 Constraints [Constraints, P R Pmax]; % 备用上下限和爬坡约束 Constraints [Constraints, R 0]; Constraints [Constraints, R Rmax]; Constraints [Constraints, R ru10 * 10]; % 求解 ops sdpsettings(solver, gurobi, verbose, 0); optimize(Constraints, Objective, ops); % 提取结果 P_result value(P); R_result value(R); energy_price dual(Constraints(1)); % 功率平衡约束对偶乘子 reserve_price dual(Constraints(2)); % 备用需求约束对偶乘子 fprintf(能量出清价: %.2f 元/MWh\n, energy_price); fprintf(备用容量出清价: %.2f 元/MWh\n, reserve_price); fprintf(各机组出力: ); disp(P_result); fprintf(各机组备用: ); disp(R_result);这段代码虽然短但已经把上面数学模型里的每一个约束都实现了。我特别说一下约束矩阵的索引问题在Matlab代码里我用Constraints(1)指向功率平衡约束用Constraints(2)指向备用需求约束。这里有个坑——如果你在约束定义过程中临时改变了约束声明顺序索引就会对不上。所以我一般建议在约束写完之后用assign或直接打印Constraints来核对顺序不然拿到的对偶乘子可能张冠李戴。3.3 算例与参数设置我测试用的是3机系统目的就是快速验证模型逻辑数据设计也有讲究1号机组最大100MW能量报价0.20元/MWh备用报价30元/MWh属于典型高成本小机组2号机组最大150MW能量报价0.25元/MWh备用报价35元/MWh典型中间层3号机组最大200MW能量报价0.18元/MWh备用报价28元/MWh明显是经济型大机组负荷D取300MW备用需求Rreq取100MW。这个备用需求不是拍脑袋设的按N-1准则近似当最大机组200MW时取50%作为需求也可以认为系统要求提供最大机组一半容量的旋转备用实际调度中取100MW比较符合中小型测试系统的常见设定。运行代码后会看到3号机组由于能量报价最低会优先加载出力1号和2号机组再根据各自的备用报价情况承担备用任务。具体的出力分配要看优化结果因为备用容量报价在目标函数里直接和能量报价竞争不同报价组合会得到完全不同的分配结果。建议读者拿到代码后试着改三组场景把备用需求从100MW提高到180MW观察出力计划和备用计划的重新分配把1号机组的备用报价从30降到10观察1号机组是否会从“纯出力”变成“大量备用”把负荷提高到400MW此时系统资源变得紧张观察能量出清价和备用价格的变化幅度实测下来第2种场景最能体现联合出清的耦合效应报价调整后1号机组的出力可能会下降把容量让给备用而3号机组则补上出力的缺口——这就是容量在能量和备用之间再分配的过程。4. 仿真结果分析4.1 看什么指标模型跑完很多人拿到结果不知道先看什么。我通常按这个顺序来分析第一步看出力计划是否合理。每台机组的出力是否在上下限内是否出现了某台机组出力非常低却承担大量备用的“割裂”情况。如果出现这种情况往往不是模型错了而是备用报价太低优化器发现让这台机组多发备用、少发电更加经济此时要额外检查它申报的备用容量是否匹配机组的实际爬坡能力。第二步看备用分配结果。备用容量应该优先分配给备用报价低、且爬坡速率满足要求的机组。如果结果里出现了备用报价高但被大量调用、备用报价低反而不调用的情况大概率是约束设置有问题重点检查耦合约束 (P_i R_i \leq P_{i,\max}) 是否写成了 (P_i \leq P_{i,\max}) 和 (R_i \leq R_{i,\max}) 两条独立约束。第三步也是最关键的一步看两个出清价格。能量出清价反映的是系统边际能量成本备用容量出清价反映的是系统边际备用成本。可以对比这两个价格和机组报价的关系正常情况下出清价会落在一个合理区间内不会低于所有报价也不会远高于报价上限。如果你发现备用价格波动非常剧烈试试看备用需求约束是不是处于“临界紧”状态——即约束刚好等于需求任何微小参数变化都会导致对偶乘子跳变。第四步有条件的话做灵敏度分析。我项目里会固定负荷不变从80MW到150MW循环改变备用需求画出备用价格和能量价格的曲线。这条曲线能非常直观地告诉你系统备用资源是在哪个节点开始变稀缺的。价格拐点对应的备用需求值就是系统备用容量的价值边界也是市场规则设计者关心的关键参数。4.2 结果合理性校验模型结果合不合理除了看数值范围还要做一套基础校验。我在代码里加了一个自检函数包括功率平衡校验(|\sum P_i - D| epsilon)备用需求校验(\sum R_i \geq R_{req})机组限幅校验(P_{i,\min} \leq P_i \leq P_{i,\max})容量耦合校验(P_i R_i \leq P_{i,\max})爬坡校验(R_i \leq ru_{i,10} \cdot 10)这些校验如果有一项不满足就要回头检查约束定义。特别提醒一点Yalmip给的是数值解浮点误差是存在的所以校验时不要用严格等号要设置一个1e-6级别的容忍度。另外价格合理性需要结合经济学的直觉来检查。如果能量出清价明明很高备用价格却非常低说明备用约束很不紧备用资源充裕反之如果备用价格高于多台机组的备用报价上限说明备用需求设置可能超出了系统物理能力建议下调需求或放开部分机组的备用上限。5. 常见问题与排查实录5.1 Yalmip建模报错我见过的初学者错误里最高频的是优化变量维度和数据矩阵维度不匹配。比如sdpvar(3,1)定义的是列向量但后面用行向量加载数据某些约束运算会直接报维度错误。这个问题解决起来最简单——在定义变量和数据后在求解前加一行size(P)和size(Pmax)肉眼核对维度。第二个高频问题是约束列表拼接方式错误。正确拼接方式是Constraints []; Constraints [Constraints, sum(P) D]; Constraints [Constraints, P Pmin];注意这里用的是英文逗号或者空格不是分号。如果用了分号Constraints会变成一个矩阵而不是一个约束列表后续optimize函数无法正常解析。这个问题报错信息不太直观往往是“Index exceeds array bounds”这类让人摸不着头脑的提示我一开始也被坑过。5.2 模型不可行怎么排查模型不可行infeasible problem是出清模型里另一个高频问题。根源通常是约束之间互相矛盾。最常见的情况是负荷和备用需求加起来超过了系统总容量。比如本算例中总最大出力是450MW如果你设负荷350MW加上备用需求150MW合计500MW模型必然无解除非允许机组全部满载且备用为0但那样备用约束又不满足。解决办法是把模型改成可诊断的格式。Yalmip里有个非常实用的工具叫optimize的第三个返回值diagnostics optimize(Constraints, Objective, ops); if diagnostics.problem ~ 0 disp(问题诊断:); disp(diagnostics.info); enddiagnostics.info会给出求解器返回的具体问题类型。如果是不可行可以用gurobi的 IISIrreducible Infeasible Subsystem功能我自己的检查方法是逐步注释掉约束跑一次模型看看注释掉哪个约束之后模型变得可行——这个约束十有八九就是矛盾源。5.3 数值问题与求解器参数第三个故障点是数值稳定性问题。当机组的成本系数数量级差异很大时比如一个0.01一个5000gurobi求解过程中可能出现数值警告甚至错误结果。我在这个模型里就遇到过备用报价从20元到500元不等能量报价从0.15元到0.9元量级差异还不算特别大但扩展到几百台机组时很容易出问题。两个应对方法。一是数据归一化把成本系数统一到同一个数量级二是在sdpsettings里开启更好的数值处理选项ops sdpsettings(solver, gurobi, gurobi.NumericFocus, 2, gurobi.ScaleFlag, 2);另外Yalmip默认使用的求解容差是1e-6如果模型规模较大可以放宽到1e-4或1e-3能有效减少无意义的微小迭代。我实测中小模型用1e-6没问题但扩展到100台机组以上容差太紧可能让求解时间成倍增加。5.4 对偶乘子提取失败Yalmip里提取对偶乘子需要特别小心一点必须保证优化问题存在唯一最优解并且约束引用正确。如果提取出来是NaN多半是问题求解失败或者是非凸问题。旋转备用出清模型我按线性规划写理论上一定收敛但如果数据里有非线性表达式比如把成本写成二次函数、又把备用约束写成非线性Yalmip可能会调用非线性求解器此时对偶乘子就不一定可靠了。建议所有对偶分析都基于线性模型完成。如果确实需要二次成本函数优先考虑用分段线性逼近而不是直接求解二次规划——前者在出清价提取上更规范。6. 模型扩展方向这套基础模型跑通之后有两条扩展路径比较推荐。一条是引入机组启停状态即把0-1整数变量加进去变成混合整数规划MILP这样可以研究“启停备用”与“旋转备用”的分层协调问题。另一条是引入网络安全约束直流潮流约束把模型从单节点扩展为多节点网络这时候备用出清价就会带上位置信号形成分区域的备用价格。我个人在实际操作中更推荐先做第二条扩展因为电力市场的空间维度是躲不开的。单节点模型虽然学术上方便但现实中“本地备用”和“全网备用”的价值差异非常大如果忽略线路传输约束容易低估局部尖峰负荷地区的备用稀缺性。加入直流潮流约束的思路也很直接就是把每一台机组和每一个负荷映射到节点上再增加线路传输容量约束。Yalmip对这类多节点直流潮流建模支持得很好你只需要提前把节点导纳矩阵和线路功率传输分布因子矩阵算好剩下的就是拼接约束。做扩展时还有个体会不要一上来就把模型做得很复杂。先把单节点线性模型的结果吃透画出出力分配图和价格曲线理解了每个参数对结果的影响再逐步增加复杂度。否则模型出了问题你可能都不知道是物理约束错了还是网络数据错了。这个经验适用于所有做优化建模的朋友。以后如果要接论文或者项目结题可以在模型基础上补充时序联动的细节比如96点时段的滚动出清以及备用市场与调频市场的联合出清。核心建模逻辑都一样只是约束数量和变量维度变大代码框架不需要推倒重来。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价