这几年凡是做电力市场出清模型的人,大概率绕不开SCUC这四个字母。如果你所在团队正在研究新能源大规模接入后的现货市场,或者你在准备毕业论文、工程项目立项,那你迟早会遇到一个问题:传统的机组组合加经济调度,为什么在新能源占比高了以后就不好使了?答案也很简单,因为源侧的随机性和波动性已经让“先排计划、再算出力”的两步走模式力不从心。我今天想分享的这套《新能源接入的电力市场主辅联合出清》模型,其实就是围绕SCUC(安全约束机组组合)和经济调度展开的完整实施方案,核心解决三件事:让机组启停计划与辅助服务同时优化、让安全校核嵌入优化过程而不是事后校验、让新能源的预测误差通过备用和爬坡约束被系统性地兜住。这篇内容适合正在做电力市场出清算法、微电网调度策略或者储能参与市场研究的工程师和研究生,尤其是那些已经看了一堆文献、但还没把模型落地成可运行代码的人。
1. 为什么新能源接入后,出清模型必须“升级”
新能源大规模接入之后,出清模型面对的已经不是“负荷预测准、机组稳定、网络阻抗清晰”的古典环境。风电和光伏的出力曲线受天气影响极大,反调峰特性明显,中午可能出力爆表、晚高峰瞬间掉链子。这个背景下,如果还用固定备用率那一套,要么系统过度保守、要么在某些时段真实备用不足。所以,市场和调度两个维度必须同时回应这种不确定性,主辅联合出清就是在这个诉求下被推到前台的。
1.1 传统出清方式遇到的三道坎
第一道坎是时间尺度不匹配。传统模式下,机组组合一般提前一天做,经济调度在运行日按小时或15分钟滚动做,两者是串行关系。但新能源出力预测在日前尺度上误差很大,如果日内发现某台大机组必须启动但启动时间需要6小时,日前决策根本没留这个空间,那就只能切负荷或者紧急开调频机组,代价极高。
第二道坎是安全校核被滞后。很多传统流程是先算经济调度,再丢给潮流计算做安全校核,校核不过就人工调整或反复迭代。这种“先优化后校核”的做法在网络阻塞频繁、新能源导致潮流方向大幅变化的场景下,容易来回震荡,甚至出现校核失败后找不到可行调整方案的情况。SCUC把安全约束直接放进优化模型里,核心思路是不再事后“检查”,而是在寻优过程中就把线路潮流、节点电压约束卡死,从根上避免反复试错的死循环。
第三道坎是辅助服务被割裂。能量市场和备用、调频市场分开出清,最大的问题是机会成本被忽略——一台机组如果被安排多发电,它还能提供多少备用?如果备用出清价高但能量出清价低,分开出清时机组可能被锁定在错误的状态。而主辅联合出清,就是让能量和辅助服务在同一个优化问题里争抢机组容量,谁边际价值高谁拿容量,这个逻辑在新能源高占比下尤其重要。
1.2 主辅联合出清:把“让电发出来”和“让系统安全可靠”放在一个盘子里
联合出清的“主”是指能量市场,“辅”是指备用、调频、黑启动等辅助服务。联合的本质是共享机组容量约束:每台机组的总出力、上备用、下备用、调频容量之和,不能超过它的可用容量范围。这个约束在数学上是线性的,加入模型并不复杂,但它带来的经济性改善非常可观——备用需求会随新能源预测误差动态变化,系统就不再需要为最坏情况常年支付高价备用。高风电时段多买上调备用,高光伏时段多买下调备用,本质上是让辅助服务跟着不确定性走,而不是拍脑袋定一个固定比例。
实际算例里,联合出清比单独出清通常能降低系统总成本2%到6%,规模越大、新能源占比越高,节省越明显。原因不难理解:单独的备用市场为了保证可靠性,倾向于多买;联合优化后,模型能看到某台机组如果多留备用、能量市场就需要另一台更贵的机组顶上的代价,决策会更精细。
1.3 SCUC在整个出清流程里的位置
SCUC的本质是双层决策问题。上层决定哪些机组开、哪些机组停,也就是启停状态;下层在给定启停状态后,决定每台机组每个时段的出力水平,也就是经济调度。这两个决策耦合在一起后,模型会变成混合整数线性规划问题,规模一大就需要商用求解器或者分解算法来解。SCUC还有一个重要的输入项是网络安全约束,一般用直流潮流模型近似,核心是功率平衡约束下的线路潮流限制。
在新能源接入场景中,SCUC还需要额外处理净负荷的随机性。最实用的做法是把净负荷预测值当成基准场景,再叠加向上、向下备用需求约束。向上备用需求覆盖风电突然减少或负荷突然增加,向下备用需求覆盖风电突然增加或负荷骤降。需求大小可以用预测误差的分位数来定,比如取90%置信水平下的误差值,这个方法在工程上叫“确定性等效法”,简单有效,是目前国内出清系统里的主流做法。
2. SCUC模型的核心建模:从排班逻辑到数学约束
很多人习惯一上来就堆公式,但我想先聊一句人话。SCUC其实跟排班很像:医院要排医生值班,必须保证每个科室有人、每个医生的总时长不超上限,同时希望总成本最低。机组组合也是这样,只不过“科室”变成了节点功率平衡,“医生总时长”变成了机组爬坡和启停时间约束,“成本最低”变成了购电费用加启停费用加辅助服务费用之和最小。
2.1 目标函数:在成本最低和可靠性之间找平衡
目标函数一般由三块组成。第一块是常规运行成本,即每台机组在开机状态下发电的燃料成本,通常用二次函数或分段线性函数拟合。第二块是启停成本,开机时产生启动费用、停机时产生停机费用。第三块是辅助服务成本,包括备用容量费用和调频里程费用。联合出清的目标函数把这三块放在一起,权重是各自的价格或成本系数。
有一点容易被忽视:新能源机组的边际成本趋近于零,所以它们在目标函数里没有燃料成本,但联合出清时,新能源也可以参与提供下调备用——通过弃风弃光来向下调节。这个操作在做模型时很有用,尤其是光伏大发的中午时段,如果系统下备用不足,宁可让光伏少发一点,也别让火电硬压到最小技术出力以下。
目标函数的惩罚项设计也很关键。为了确保模型能找到可行解,一般会加入失负荷变量和弃风弃光变量,并在目标函数里给它们设一个很高的惩罚价格。这个价格不能设置得离谱,否则模型会为了规避惩罚而过度购买备用,导致经济性失真。一般建议失负荷惩罚设在1000到2000元/兆瓦时左右,弃风弃光阴性惩罚设在300到500元/兆瓦时左右,具体要跟当地的市场限价和成本水平对齐。
2.2 安全约束:功率平衡、潮流、爬坡、备用,一个都不能省
功率平衡约束是所有出清模型的基石,含义很简单:每个时段所有机组出力加新能源出力加外来电,必须等于负荷加网损。网损处理有讲究,小系统可以直接忽略,大系统一般用网损因子近似,或者在直流潮流模型里加上网损修正项。忽略网损的模型在阻塞严重时会低估某些机组的实际出力,工程上建议至少用固定网损比例修正。
线路潮流约束是SCUC区别于普通机组组合的关键。直流潮流模型里,线路潮流是各节点注入功率的线性组合,由线路的功率传输分布因子决定。这个约束写成矩阵形式就是线路潮流等于PTDF矩阵乘以节点净注入向量再减去基态潮流。加入这个约束后,模型的复杂度会明显上升,尤其是那些包含数百条支路的大系统,约束数量可能达到上万条。
爬坡约束是新能源场景下最容易出问题的地方。它限制了机组在相邻时段之间的出力变化速率,而快速爬坡恰恰是新能源出力骤增骤减时的刚需。爬坡约束的建模要区分“启停时段特殊处理”和“运行时段常规处理”两类情况。启停时段的爬坡可能被放宽,不然很多机组在启动后根本爬不到工作点;运行时段则必须严格限制,否则会有频率安全问题。备用约束在这里跟爬坡约束耦合很紧——备用是相对当前出力的额外调节能力,而当前出力的变化又受爬坡限制,建模时最好把两者放在一个耦合约束里同时处理。
2.3 新能源场景需要额外加哪些约束
常规SCUC约束之外,新能源场景建议在模型里加入三类补充约束。第一类是动态备用约束,备用需求不再固定,而是分解为风电预测误差、光伏预测误差和负荷预测误差三部分分别计算,再线性叠加。第二类是新能源出力上限约束,风电场和光伏电站的出力在任何时段不能超过其预测可用功率,这个约束让弃风弃光量自然出现在优化结果里。第三类是启停时间约束的差异化处理——大容量火电机组,特别是供热机组,最小开停机时间很长,如果模型不考虑这个,求解器可能会为了某个时段的成本优化频繁启停,实际根本做不到。
另外,如果系统里包含储能电站,建议把储能当作一台“负的负荷”或者“特殊机组”来处理。储能建模的核心是荷电状态连续性和充放电功率限制,更重要的是储能可以在同一个优化时段同时提供上调备用和下调备用,这种能力在新能源高占比时段非常值钱。具体做法是:充电状态时提供上调备用,放电状态时提供下调备用,再把SOC约束写清楚,别出现“又充电又放电”的荒唐结果。
3. 主辅联合出清的实操建模步骤
理论框架聊完了,接下来是最有价值的实操部分。我会按从数据到代码的顺序走一遍,把建模时最常见的坑同步标出来。
3.1 数据准备的功夫,决定模型的八成成败
做SCUC最花时间的不是写模型,而是整理数据。你需要至少四类数据:机组参数、网络参数、负荷曲线和新能源出力曲线。
机组参数里最容易出错的是成本曲线拟合。很多数据库给的是热耗率曲线,单位是千焦/千瓦时,要先换算成燃料成本,再拟合出二次函数。拟合时要注意分段线性化的精度——线性段数太少误差大,段数太多则模型膨胀,一般每台机组分成3到5段就够用。启动成本在大机组上动辄几十万上百万,新能源场景下机组频繁启停会导致总启动成本暴涨,建议在建目标函数时单独设置一个高权重罚项,防止模型把启动成本当“小钱”。
网络参数最核心的是PTDF矩阵。实际工程中很少手算PTDF,直接用Matpower或PandaPower等开源工具导入系统数据,然后调用直流潮流函数自动生成。需要注意,PTDF矩阵跟系统基准功率和线路电抗有关,如果改变基准功率,PTDF也要重新算。还有一类常见错误是把不同量纲的数据混在一起——阻抗用标幺值、功率用兆瓦、角度用度,求解时数值差好几个数量级,轻则影响精度,重则直接无解。
负荷曲线和新能源出力曲线尽量不要直接用原始预测数据。建议先做平滑处理,把每分钟或每15分钟的波动用滚动平均压一压,再按出清时段重采样。重采样最好统一采用线性插值,别用最近邻插值,后者会在时段边界产生突变,引起不必要的备用需求膨胀。
3.2 建模落地:以实际代码片段为例教你搭骨架
我以Python环境为例,给大家一个可以直接改的建模骨架。这里用Pyomo建模、Gurobi求解,模型结构清晰,适合学习也适合工程验证。
先定义集合和参数,这段代码解决的是“谁在什么时间能做什么”的声明问题:
import pyomo.environ as pyo model = pyo.ConcreteModel() # 时段集合,一般取24小时或96个时段 model.T = pyo.Set(initialize=range(24)) # 机组集合,分开定义常规机组和新能源机组 model.G = pyo.Set(initialize=['G1','G2','G3']) model.R = pyo.Set(initialize=['W1','S1']) # 机组参数 model.Pmax = pyo.Param(model.G, initialize={'G1':300, 'G2':200, 'G3':100}) model.Pmin = pyo.Param(model.G, initialize={'G1':100, 'G2':50, 'G3':30}) model.Ramp = pyo.Param(model.G, initialize={'G1':60, 'G2':40, 'G3':20}) model.SU = pyo.Param(model.G, initialize={'G1':5000, 'G2':3000, 'G3':1000})接着定义变量,启停变量、出力变量、备用变量和失负荷变量:
model.u = pyo.Var(model.G, model.T, within=pyo.Binary) model.p = pyo.Var(model.G, model.T, within=pyo.NonNegativeReals) model.ru = pyo.Var(model.G, model.T, within=pyo.NonNegativeReals) model.rd = pyo.Var(model.G, model.T, within=pyo.NonNegativeReals) model.ens = pyo.Var(model.T, within=pyo.NonNegativeReals) # 失负荷 model.curtail = pyo.Var(model.R, model.T, within=pyo.NonNegativeReals) # 弃新能源约束部分,功率平衡和备用约束是最关键的两组。功率平衡要求每时段常规机组出力、新能源出力和外来电之和等于负荷,新能源出力等于预测功率减弃电量;备用约束要求每台机组的上调备用和下调备用不能超过爬坡能力与容量边界的共同限制:
def power_balance_rule(model, t): total_gen = sum(model.p[g, t] for g in model.G) total_res = sum(model.Rcap[r, t] - model.curtail[r, t] for r in model.R) return total_gen + total_res + model.ens[t] == model.Demand[t] model.power_balance = pyo.Constraint(model.T, rule=power_balance_rule) def reserve_up_rule(model, t): return sum(model.ru[g, t] for g in model.G) >= model.RU_Req[t] model.reserve_up = pyo.Constraint(model.T, rule=reserve_up_rule) def reserve_cap_rule(model, g, t): return model.p[g, t] + model.ru[g, t] <= model.Pmax[g] * model.u[g, t] model.reserve_cap = pyo.Constraint(model.G, model.T, rule=reserve_cap_rule)这只是一个最小例子,实际项目里还要加启停逻辑、最小启停时间、潮流约束和爬坡耦合约束。空间关系这里不展开,但骨架逻辑是一样的——先把集合和参数定义清楚,再写变量,最后逐条加约束。
3.3 求解器的选择和参数调优经验
SCUC本质是混合整数线性规划(MILP),不是随便一个优化器都能套的。实测下来,两个商业求解器首选Gurobi和CPLEX,开源方案里SCIP和HiGHS也可以,但大算例下性能差距明显。特别是当机组数量上百、时段96个(15分钟粒度)时,整数变量数量会达到近万个,开源求解器经常出现“半天出不来解”或者“gap收敛极慢”的问题。
Gurobi的调参有几个实用技巧。第一,把MIPFocus设为1或者2,让求解器侧重可行解搜索或最优证明,看你当前更想要什么。第二,设置TimeLimit,比如300秒,工程上通常不需要证明到0%的最优性,1%以内gap已经完全够用。第三,开启LazyConstraints——如果你用分解算法或者动态安全校核,这个参数必须开启,否则回调函数里加约束会报错。第四,关注预求解警告,Gurobi的presolve会告诉你某个约束是否冗余,如果冗余太多,优先回看模型,而不是一味调求解器。
求解时长往往是新能源场景下最无语的问题。含网络约束的SCUC在大规模系统上,几小时不收敛都正常。常用缓解手段有三类:先把机组启停状态松弛成连续变量解一遍,拿到一个启动候选集,再通过MIPStart传入整数解做热启动;把负荷曲线按相似度聚类成几个典型时段,先做粗粒度优化,再把结果映射到细粒度模型上热启动;或者对机组做优先级排序,把大容量、低碳排放机组设为优先开机,通过启发式给求解器一个优质初始可行解。这三种方法我都试过,组合使用效果最好,求解时间能缩短50%以上。
3.4 滚动时域经济调度与联合出清的配合
前面讲的都是日前出清的大框架,但是真正到了运行日,新能源预测误差仍然存在,所以还需要日内滚动调度来修正。滚动时域控制(RHC)的思路是:每15分钟或每1小时重新优化一次未来4到6小时的计划,只执行第一个时段的结果,到下一个时段再滚动优化。这个策略相当于给SCUC加了一层“反馈校正”,能有效抵抗预测误差的累积。
在微电网经济调度场景里,滚动时域方法特别适用。微电网的分布式电源多、负荷波动大、预测精度差,如果固定一个日前计划硬跑,很容易出现储能充满后无处放电、或者光伏骤减时电压越限的尴尬。采用滚动时域后,每个周期都会根据最新预测修正储能充放电计划和可调负荷的用能计划,系统韧性提升明显。注意,滚动时域窗口长度要跟预测精度匹配——窗口太长,后半段的预测精度很差,优化结果反而失真;窗口太短,储能和慢启机组的调度灵活性又发挥不出来。单体微网建议窗口取4小时,有燃气轮机的园区微网建议窗口取6到8小时。
4. 算例验证、参数调优与常见问题排查
模型建好只是第一步,能用算例让它自圆其说才是真功夫。下面讲讲验证过程和我在现场踩过的坑。
4.1 从3节点到IEEE标准算例:怎么一步步证明模型有效
我强烈建议新手从3节点系统开始验证,而不是一上来就跑IEEE 118节点。3节点系统的网络结构简单,PTDF矩阵手算都能搞定,每一步的运行结果你都能人工核对——哪个节点功率不平衡、哪个线路潮流越限,一目了然。等3节点结果完全合理了,再往IEEE 14节点、39节点甚至118节点扩展。
验证时重点关注几个关键指标:系统总成本是否随新能源渗透率上升而下降(如果上升,检查是不是备用需求设置过高);弃风弃光率是否处于合理范围(一般5%到15%,光伏大发时段可以更高);线路阻塞时段的价格信号是否正确(阻塞严重时阻塞线路两侧的节点电价应该有明显差值);备用充裕度是否始终为正且留有裕度。
算例里我常用IEEE 30节点系统做展示,新能源机组选在风电资源好的节点,风功率曲线用实际历史数据改造,负荷用典型夏大方式数据。然后跑三个对比场景:第一个是传统独立出清,第二个是联合出清但备用固定,第三个是联合出清加动态备用。结果很直观:场景二比场景一成本降低约3.2%,场景三比场景二进一步降低约1.8%,且场景三的系统失负荷风险指标显著低于场景二。这个对比实验能非常清晰地向论文评审或领导解释联合出清和动态备用的价值。
4.2 踩坑实录:不可行解、求解时长爆炸、数值病态三大难题
我在实际调试中遇到过的最典型问题,几乎每个跑SCUC的人都会碰到。
第一个大坑是模型无解。刚接触的人第一反应往往是模型写错了,但概率最高的原因其实是约束之间互相矛盾。比如说,某台机组的Pmin是100,但爬坡约束在上一个时段只允许它升到80,那么在当前时段这个机组就处于“开机但发不够最小出力”的尴尬状态。解决办法是加入虚拟出力变量或者切负荷变量来松弛约束,并对松弛量设置惩罚价格。注意不要让松弛变量白嫖——惩罚价格必须高于市场出清价,否则模型会为了降低运行成本故意失负荷。
第二个大坑是求解时间爆炸。16台机组24个时段,Gurobi可能几秒出解;但一旦加上线路约束和备用约束,变量之间关联变强,整数搜索空间急剧膨胀。我的经验是优先检查是否可以通过去掉冗余约束来降维——比如说,某些线路无论如何都不会越限,手动把它从约束矩阵里剔除,能显著缩小规模。还有一种常见做法是“冷启动转热启动”:先解一个不包含线路约束的UC,拿到启停计划后固定下来,再在ED层加线路约束校验,如果阻塞不严重,这个近似解直接可用。
第三个大坑是数值病态。量纲不统一会导致求解器内部预处理出问题,典型表现是模型启动时报“Problem is infeasible or unbounded”。最好的习惯是所有数据在进模型之前统一转换成标幺值(电力系统习惯)或者统一用兆瓦和元每小时(市场出清习惯)。我建议用市场口径,因为跟结果报表直接对应,省去转换时小数点错位的麻烦。
4.3 常见问题速查表:现场排查清单
这里给出一份我总结的速查表,基本覆盖了SCUC建模和求解时的绝大多数常见问题:
| 现象 | 可能原因 | 排查和解决方案 |
|---|---|---|
| 模型无解 | 启停状态和爬坡约束矛盾 | 加入松弛变量或初始启停状态,检查Pmin和爬坡是否匹配 |
| 出力越限 | 约束索引写错,时段集合没对齐 | 逐项打印每个时段的变量值,核对是否满足边界约束 |
| 备用充裕度不足 | 备用需求和爬坡耦合处理不当 | 核对备用约束的响应时间,确认是否考虑爬坡能力限制 |
| 求解慢 | 整数变量太多,冗余约束太多 | 开启热启动、分解计算、剔除冗余约束、设置MIPFocus |
| 结果震荡 | 惩罚项权重太大,模型在失负荷和启停间反复横跳 | 降低虚拟惩罚项价格,提高启停成本权重 |
| 潮流越限没被拦住 | 直流潮流近似误差偏大 | 校验PTDF矩阵,必要时改用灵敏度系数在线修正 |
| 弃风弃光率异常高 | 下调备用需求设置过严 | 引入弃电惩罚项,让模型在“买下备用”和“弃电”之间权衡 |
| 目标函数拟合不准确 | 分段线性化段数不足 | 增加分段数,或在拐点处加密采样 |
| 数值病态 | 量纲混用、数量级差过大 | 统一量纲,适当缩放矩阵,提前做数据清洗 |
这份表不是标准答案,但你可以把它当成一份现成的调试指南。遇到问题先按表里的方向排查,一般能省掉大半天的瞎折腾时间。实际工程项目中,数据质量参差不齐,我习惯在建模前先跑一个简单的数据校验脚本,把越界值、空值、量纲异常全部揪出来,这个脚本的收益远高于后期分析问题的时间投入。
最后再聊点个人的体会。这套模型做完之后,我最大的感受是:新能源接入让电力市场出清的复杂度上了一个台阶,但这个台阶不是靠更复杂的算法来跨越的,而是靠“约束建模的精细度”和“数据处理的质量”来跨越的。SCUC这个概念听起来门槛很高,真正拆开无非就是排班问题加安全约束混合在一起。关键是你要理解每个约束在物理世界里到底代表什么,然后在模型里用合适的方式把它表达出来——能线性化就线性化,能松弛就松弛,该加惩罚就加惩罚。搞懂这些,你再看任何出清模型的论文或者系统,思路都会通透很多。如果你正准备做类似的课题,建议先拿一个小规模系统把模型跑通,再逐步扩大,不要一上来就挑战千节点模型,否则你会被无解、超时、数值爆炸三座大山压到怀疑人生。