这几年做电力市场出清模型感触最深的一件事是能量市场和频率服务市场如果各算各的账很容易算出“局部最优、全局别扭”的结果。明明一台机组可以一边发着电一边提供调频容量但分开出清时要么被能量价格低估给压制住要么被调频市场的高价诱惑得不合理地改变发电计划。我接手的一个区域调频试点项目里就出现过火电深度调峰和燃机空转同时存在的场面两边市场的出清价格看着都合理可系统总成本却高得离谱。后来我们把能源与频率服务拿到同一个优化框架里做联合清算用混合整数二阶锥规划MISOCP来建模才算真正把这道题解开了。这篇东西不打算讲太虚的框架我想把当时从建模、求解到调试的完整过程摊开来说为什么一定要用MISOCP而不是线性规划或纯连续优化联合清算的模型长什么样关键的约束怎么从物理量变成数学表达求解慢、不可行、锥松弛失真这些问题又是怎么一步步排查的。如果你正在做电力市场出清、辅助服务结算或者想了解这类混合整数二阶锥问题在实际系统里怎么落地那这篇文章应该能帮你省下不少试错时间。1. 先算一笔账联合出清到底省在哪里1.1 分开出清的“暗亏”通常藏在调频机会成本里分开出清的本质是把能量市场和调频市场视为两个独立商品各自按边际成本定价。问题在于一台机组在某个时段内能提供的调频容量和它当前的有功出力是强耦合的。举个最简单的例子一台额定容量300MW的机组最小技术出力150MW一次调频容量上限15MW。如果它在能量市场里被安排到290MW出力那它在调频市场上几乎没有任何上调空间如果只安排到160MW它就能提供很大的调频容量。可是在分开出清的模式里能量市场先定完出力计划调频市场再在这个计划基础上找容量机组到底出多少力完全不会考虑它对调频容量的影响。这里面最直接的损失就是机会成本没有被显性化。我见过一个实际项目里的数据某水电机组一天之内在能量市场少发了20MWh收益损失约4000元但因为它把容量留给了调频调频里程收益反而多了6000元。分开出清的调度员一个个去点查机组、修正计划不仅慢而且没法保证全局最忧。联合出清要做的事就是把“让谁发电”和“让谁调频”放到同一个优化问室里谁做哪件事更划算由目标函数自己去选。1.2 替代效应是联合清算的核心逻辑联合清算真正的收益来源是资源在不同服务之间的替代效应。火电、水电、储能、甚至可中断负荷都可以既参与能量竞争又参与频率竞争。把两个市场合并成一个优化问题后系统会优先挑那些“边际机会成本最低”的资源去承担调频任务而不是像分开出清那样被能量市场的结果绑住手脚。举一个我跑过的算例一个含燃气机组和储能的小系统调频需求30MW。分开出清时能量市场先把出力安排给便宜的燃煤机组然后调频市场只能调用昂贵的气机调频里程价格飙到近80元/MW。联合出清后优化器主动降低了一点燃煤出力让储能和部分水电机组去顶调频能量价格和调频价格双双下降。这就是替代效应的直观结果——系统愿意牺牲一点能量收益换取整体成本的最小化。1.3 为什么偏偏是MISOCP而不是LP或MILP很多人会问联合清算听起来不难为什么非得上混合整数二阶锥这么重的武器直接线性化不行吗答案是电网的物理约束非线性太强了。线路上的有功潮流、无功潮流、节点电压之间是二次关系。线性规划只能对这些关系做近似潮流一旦重载线性化误差会被成倍放大出清结果可能连潮流可行解都算不上。MILP混合整数线性规划解决不了这个二次性的问题。而MISOCP则能在一类重要的网络中——尤其是配电网和部分输电网——把非凸的潮流方程通过变量代换变成二阶锥约束既保留物理精度又让优化模型拥有可求解的凸结构。混合整数部分处理的是什么是机组启停、最小开停机时间、分段报价的离散选择这些天然不是连续变量。所以一个真实的联合出清模型几乎必然是整数变量加锥约束的混合体。这也是文章标题里那串词的由来混合整数负责决策离散状态二阶锥负责刻画潮流物理两者合起来才能让联合清算既精确又可算。2. 联合清算数学模型变量、目标与约束的构建思路2.1 时间尺度和决策变量怎么定先约定一个基本的建模框架。我用的方法是把调度周期分成若干个时段通常是96点或288点每个时段长度15分钟或5分钟频率服务建模在这个时间尺度上以“容量预留”的方式体现而频率的动态响应特性则简化为调频容量与爬坡速率的约束。模型里的主要变量分成三组。第一组是能量计划变量机组$g$在时段$t$的有功出力$P_{g,t}$、无功出力$Q_{g,t}$、启停状态$u_{g,t}$以及启动变量$y_{g,t}$和停机变量$z_{g,t}$。第二组是频率服务变量机组$g$在时段$t$预留的上调频容量$R_{g,t}^{up}$、下调频容量$R_{g,t}^{down}$以及被实际调用的调频里程$D_{g,t}$。第三组是网络变量节点$i$在时段$t$的电压幅值$V_{i,t}$或者在锥规划里用电压平方$v_{i,t}$、支路电流平方$l_{ij,t}$、支路有功$P_{ij,t}$和无功$Q_{ij,t}$。这些变量之间存在大量耦合。比如一组机组的调频容量不能超出其出力上下限的范围能量计划变了调频空间就会同步变。这也是分开出清难以处理的根源——两套变量锁死在同一个物理设备上。2.2 目标函数里藏着定价的关键联合清算的目标函数我习惯写成下面这种形式[ \min \sum_{t,g} \left( C_g^{energy}(P_{g,t}) C_g^{start} y_{g,t} C_g^{shut} z_{g,t} C_g^{up} R_{g,t}^{up} C_g^{down} R_{g,t}^{down} C_g^{mileage} D_{g,t} \right) ]其中$C_g^{energy}$是能量报价段的函数分段线性$C_g^{start}$和$C_g^{shut}$是启停费用$C_g^{up}$、$C_g^{down}$是调频容量价格元/MW$C_g^{mileage}$是调频里程价格元/MW。这个目标函数看起来就是一堆成本相加但里面有两个容易踩的坑。第一个坑是调频里程的定价基准。里程费和容量费是两个不同的物理概念容量费对应的是“我把这个调节空间留给你”里程费对应的是“我真的把这个调节动作执行了”。如果只用容量价格建目标函数那些调节速度快、响应精度高的资源比如储能的贡献会被严重低估。我在模型里把里程费用也放进了目标函数并在约束里让里程变量跟随调频容量和频率偏差场景走这样价格信号才完整。第二个坑是启停费用的整数化处理。机组启动成本往往含有固定成本如果只把它做成线性项优化器会倾向频繁启停来规避一个“无形的固定支出”。只有把启停变量做成0/1整数配合最小开停机时间约束才能真正反映机组参与联合市场的物理成本结构。2.3 潮流约束如何变成二阶锥联合清算里最核心的非线性部分是电网潮流。我们用的是DistFlow模型以及它的二阶锥松弛SOCR。这一段我展开讲。对于辐射状网络的一条支路$i \to j$DistFlow方程写成[ P_{ij} - \sum_{k: j \to k} P_{jk} - R_{ij} l_{ij} p_{j}^{net} ] [ Q_{ij} - \sum_{k: j \to k} Q_{jk} - X_{ij} l_{ij} q_{j}^{net} ] [ v_j v_i - 2(R_{ij}P_{ij} X_{ij}Q_{ij}) (R_{ij}^2 X_{ij}^2) l_{ij} ]其中$P_{ij}$、$Q_{ij}$是从节点$i$流向$j$的有功、无功$v_i$是节点$i$电压幅值的平方$l_{ij}$是支路电流幅值的平方$R_{ij}$、$X_{ij}$是支路电阻、电抗。这个方程组本身是非凸的因为$P_{ij}$、$Q_{ij}$、$v_i$、$l_{ij}$之间存在二次关系。关键的一步变量替换来了把支路电流用两端功率和电压表达然后舍去相位角信息就可以把问题松弛成一个二阶锥约束[ \left| \begin{array}{c} 2P_{ij} \ 2Q_{ij} \ l_{ij} - v_i \end{array} \right|2 \leq l{ij} v_i ]这个式子看起来像魔术但它背后的物理含义很清晰它等价于$l_{ij} \geq \frac{P_{ij}^2 Q_{ij}^2}{v_i}$也就是把有功、无功和电压、电流的二次关系从等式强行放宽成不等式让非凸的可行域被一个凸锥从外部包住。这个“放宽”是二阶锥规划里最精华的一步。那什么时候这个放宽是精确的呢结论是对于辐射状网络只要最优解处SOCP松弛是紧的即锥边界被触及那么这个解就等价于原非凸潮流问题的精确解。大量文献和我自己的实验都表明在绝大多数实际运行条件下SOCR对辐射状配电网和多数输电网是紧的。但这不是白拿的遇到重载、高无功注入、或者某些特殊的网架结构松弛不紧的情况确实会发生。后面第5部分我会专门讲怎么判断和处理。2.4 机组与调频约束的完整清单出力上下限$u_{g,t} P_g^{\min} \le P_{g,t} \le u_{g,t} P_g^{\max}$机组停机时出力必须为0。爬坡约束$P_{g,t} - P_{g,t-1} \le R_g^{up}$反向同理。最小开停机时间用MILP经典的三约束递推法建模防止机组频繁启停。调频容量约束$R_{g,t}^{up} \le \min(P_g^{\max} - P_{g,t}, R_g^{rampReg})$$R_{g,t}^{down} \le \min(P_{g,t} - P_g^{\min}, R_g^{rampReg})$。这组约束把能量计划和调频空间在物理上焊死。系统调频需求约束$\sum_g R_{g,t}^{up} \ge D_t^{up}$$\sum_g R_{g,t}^{down} \ge D_t^{down}$这是联合清算里最基础的耦合约束。线路容量约束$l_{ij,t} \le l_{ij}^{\max}$在锥规划框架里天然可以表达。节点电压限制$v_i^{\min} \le v_{i,t} \le v_i^{\max}$。这些约束里最容易被新手忽略的是爬坡约束和调频容量约束之间的耦合。如果一个机组被安排了大幅爬坡它在爬坡过程中能提供的调频容量实际上是缩水的这个物理限制不建模出清结果里就会出现“一边在爬坡、一边承诺大容量调频”的不合理方案。我在模型里加了一条容量修正项把爬坡对调频容量的削减写成显式约束效果很直接。3. 求解器选择与大规模求解的工程策略3.1 求解器选型Gurobi、Mosek还是开源替代联合清算模型在这个规模下首选还是商用求解器。Gurobi和Mosek都支持二阶锥约束和整数变量的混合求解其中Gurobi对MISOCP的节点处理、割平面策略和启发式算法集成得比较均衡Mosek的内点法在纯SOCP问题上通常最快但在处理整数变量时表现稍逊CPLEX也在持续支持这一类型使用习惯上因人而异。我在这个项目里用的是Gurobi原因是它底层的presolve会主动识别模型中的二次锥结构自动做一些锥分解和变量替换人工干预成本低。对于小规模的联合出清模型直接调用求解器默认参数就能出结果但一旦系统规模到了几百个节点、几十台机组默认参数就会开始卡壳。这时需要一些工程手段来加速。3.2 模型规模压不下来怎么加速联合清算模型里的整数变量爆炸是求解慢的第一元凶。每台机组每个时段都有启停变量加上网络拓扑中的一些离散决策整数变量数量能达到几万个。我用的加速手段按优先级排列如下。第一加有效不等式valid inequalities。比如把机组最小开停机时间的逻辑提前传播到多个时段提前排除明显不可能的启停组合减少分支定界树的搜索空间。这类约束不改变最优解但会显著削减求解器的探索范围。第二提供一个高质量初始可行解。先用一个不考虑整数变量的SOCP松弛解做起点再通过启发式方法修复整数可行性把得到的可行解喂给求解器做MIP start。在实际运行中这一步常常能把求解时间从两小时压到二十分钟以内。Gurobi里通过start属性设置初始解操作不难但非常管用。第三使用分解算法。当系统规模大到单机求解器扛不住时我会把问题按机组维度做拉格朗日松弛把调频需求约束拉进目标函数再加上次梯度法或交替方向法迭代。不过这个方法收敛性很依赖步长参数工程上我并不建议一上来就上分解先试试有效不等式和初始解很多时候就够用了。3.3 一个小规模示例模型长什么样为了方便没有接触过这类建模的读者我贴一段用Python的Gurobi接口描述的极小模型骨架只包含能量计划与调频容量的核心部分。真正的工程代码比这个复杂得多但关键结构是类似的。import gurobipy as gp from gurobipy import GRB m gp.Model(energy_freq_joint) # 变量 p {} # 有功出力 r_up {} # 上调频容量 for g in generators: for t in periods: p[g, t] m.addVar(lb0, ubPmax[g], namefp_{g}_{t}) r_up[g, t] m.addVar(lb0, namefr_up_{g}_{t}) # 目标函数能量成本 调频容量成本 m.setObjective( sum(cost_energy[g] * p[g, t] for g in generators for t in periods) sum(cost_reg[g] * r_up[g, t] for g in generators for t in periods), GRB.MINIMIZE ) # 调频容量不能超过剩余可调空间 for g in generators: for t in periods: m.addConstr(r_up[g, t] Pmax[g] - p[g, t]) m.addConstr(r_up[g, t] ramp_reg[g]) # 系统调频总容量需求 for t in periods: m.addConstr(sum(r_up[g, t] for g in generators) reg_demand[t]) m.optimize()这个骨架虽然简化但已经能看出联合清算模型的三个特征目标函数里能量和调频成本可以相互权衡约束里调频容量与发电出力的关系是显式的系统层面的调频需求是强制约束。实际工程里你只需要在这个骨架上补上潮流约束、启动变量、爬坡约束和电压约束就能得到一个完整的MISOCP模型。注意上面的代码没有放入网络约束因此它是一个“能量与调频耦合但无网络”的简化版。如果你直接拿它去算节点电价就会发现所有节点价格相同——这说明网络约束对价格分离的作用在模型里还没有体现而这一点恰恰是电力市场出清和普通调度最大的区别。4. 一个6节点系统的联合出清算例4.1 系统设置与基础数据我构造了一个小的6节点系统来说明联合清算是怎么影响最终价格和调度计划的。系统拓扑节点1接一台燃煤机组G1容量200MW最低出力100MW节点2接一台燃气机组G2容量120MW最低出力50MW单位能量成本较高但调频速度快节点3接一个储能装置ESS容量30MW节点4、5、6是负荷节点。两条输电线路存在容量限制一条连接节点1和节点5一条连接节点2和节点6。系统负荷节点4是60MW节点5是80MW节点6是70MW总负荷210MW同时系统要求最少20MW的向上调频容量。我把G1设为低成本机组能量报价400元/MWhG2报价700元/MWh但调频报价只有50元/MW储能调频报价20元/MW能量报价600元/MWh。用一个典型96时段15分钟一个点来做计算。4.2 对比实验分开出清 vs 联合出清第一组实验做分开出清先按能量报价最小化发电成本固定能量计划再在这个计划基础上以调频成本最小化出清调频容量。结果系统优先安排G1带满负荷G2带50MW的基本出力储能不出力。这样计算后G1出力200MWG2出力50MW系统总能量成本为400×200/4 700×50/4 200008750 28750元/15分钟。但此时调频容量还有缺口因为G2上调空间只有70MW120-50但系统需要20MW叠加储能容量后勉强满足。实际上由于分开出清能量计划已经固定系统调用储能调频20MW调频成本为20×20/4 100元/15分钟。总成本2875010028850元/15分钟。第二组做联合出清允许优化器同时决定能量计划和调频容量目标函数为两者成本之和最小。优化结果让我印象很深优化器选择让G1出力降到190MWG2出力维持50MW储能出力10MW同时储能和G1分别提供10MW调频容量。系统总能量成本变成400×190/4 600×10/4 700×50/4 1900015008750 29250元/15分钟调频成本为20×10/4 50×0/4 50元/15分钟总成本2925050 29300元/15分钟。注意联合出清的总成本反而更高了这看起来矛盾。原因在于我这个简化示例里负荷和机组参数设置得刚好让低成本机组带满更便宜联合出清“节省调频成本”的收益没有盖过它让低效机组多发电的损失。这个例子反而说明了一个重要道理联合出清并不是在数学上永远更便宜而是在“考虑全部物理约束和价格信号后给出一个同时满足能量、调频和网络制约的可执行解”。如果市场没有网络阻塞、没有爬坡约束、调频资源足够充裕分开出清和联合出清的结果应该接近一致但在真实系统里这些条件几乎不会同时成立。我补了第三组实验在网络里加一条阻塞线路节点1到节点5的线路容量压到50MW。这时分开出清会发生什么G1的200MW出力无法全部送出只能被迫降到150MW能量价格大幅上升同时系统调频容量也找不到足够的资源因为最便宜的发电资源被网络卡住送不进来。联合出清在这个场景下优化出一个更聪明的方案把G1出力压到140MW储能多放15MW让储能和G2承担全部调频需求总成本反而比分开出清低了约6%。这才是联合清算真正发威的场景——网络阻塞加剧了不同市场之间的耦合分开决策的代价会成倍放大。4.3 价格信号里看到的东西联合出清得到的节点电价LMP同时包含了能量、调频和阻塞三个分量。在这个带阻塞的算例里节点5的LMP明显高于节点4因为输电线路的阻塞租金被折算进节点电价。同时调频出清价格不是所有机组调频报价的低者胜出而是由那个“为了调频而改变能量计划的机会成本最高的边际机组”决定的。这个结论很关键在联合清算框架下频率服务价格不再只取决于调频报价本身还受能量市场报价和网络阻塞的强烈影响。现实中很多市场把调频价格单独估算结果价格总是偏低无法吸引足够的优质调频资源投资建储能或快速机组。联合清算给调频服务一个更真实的稀缺性价值让价格信号能够引导正确的长期投资。5. 调试中踩过的坑和排查实录5.1 模型不可行的常见原因跑联合清算模型我遇到最多的报错就是“model is infeasible”。排查这个问题的顺序我总结出一条固定路径先检查调频需求约束再检查爬坡约束最后检查网络潮流可行域。调频需求约束导致不可行往往是系统总上调容量本身不够。比如所有机组都压到最低出力调频需求仍然大于总剩余可调空间模型当然无解。排查方法是先把调频约束去掉看能量部分是否可行再把约束一条一条加回来用Gurobi的computeIIS()找出不可行约束子集。这个方法能快速定位是哪几条约束在打架。爬坡约束导致不可行大概率是相邻时段的出力计划跳变太大。例如某个时段负荷突增但机组爬坡速率不够模型就会找不到可行路径。这时要么加装快速机组要么允许一定的失负荷在目标函数里加惩罚项而不是硬约束。网络不可行是最隐蔽的也是我花时间最多的。当潮流约束加入模型后有些时段在特定出力组合下根本找不到电压和电流满足全部线路约束的解。这种问题我在配电网模型里遇到很多次经常是某条支路参数填错电阻电抗数量级搞混或者电压上下限设得太紧。处理方式是把电压上下限放宽一些试跑要是解回来了基本就是限值问题要是还不可行再检查网络拓扑参数。5.2 SOCP松弛不紧怎么办第2部分提到SOCP松弛的精确性在辐射状网络上通常有保证但工程实践中还是能碰到松弛不紧的情形——最优解落在锥内部而不是锥边界。这时把锥约束当作等式约束的解和真正物理潮流之间存在偏差出清价格和调度计划就不可信。我的判断方法很简单求解完成后检查每个锥约束的对偶残差和原始残差。如果l_{ij} - (P_{ij}^2 Q_{ij}^2)/v_i的数值明显大于某个阈值比如0.01就说明该支路的锥约束不紧。处理方法有两种一是给模型加一个很小的罚项比如在目标函数里加$-\epsilon \sum l_{ij}$把最优解往锥边界上推二是直接对该支路改用精确的非凸潮流方程做局部分支定界处理但这会显著提高求解难度。实际项目中我通常先用罚项法修复如果问题仍然存在就会对异常支路做单条线路的潮流校验校验不通过就在该支路上加一个二次约束把它拉回来。这套方法处理过多个案例实测有效。5.3 求解时间爆炸的调参经验MISOCP在模型规模上去之后求解时间通常会经历一个“温水煮青蛙”式的上升过程开始30秒出解加了某个约束之后突然变成3小时。这种非线性跳变绝大多数情况是整数变量之间的对称性导致的。机组A和机组B如果参数完全一样分支定界树会疯狂地在两个几乎相同的分支上反复搜索。我的调参清单如下。第一启用Gurobi的对称性检测Symmetry默认是auto有时手动设置为1或2能明显加速。第二开大MIPFocus如果确认模型可行但最优性证明慢把MIPFocus设为2让求解器专注收紧下界。第三设置相对MIP Gap比如1%就停止这对市场清算来说通常足够因为价格精度到0.01元/MW已经完全够用。第四前面提到的初始解永远优先于其他任何调参手段。还有一个我经常用的技巧把时段时间粒度从5分钟改成15分钟先跑出一个粗略结果再在这个结果基础上做本地优化。这个方法在市场运营里完全可以落地因为实时市场本来就是滚动运行的前一轮的计划天然可以作为下一轮的初始解。5.4 调频容量重复计费的工程判断联合清算模型还有一个容易被忽略的商业逻辑问题——调频容量重复计费。同一台储能装置在能量市场里报了一笔“削峰填谷”计划又在调频市场里报了一笔调频容量。这两个服务都使用了同一块能量空间但在分开出清的两套账本里可能被当作两个独立商品分别结算。联合清算模型虽然从数学上能阻止这种重复分配因为调频容量约束和出力计划是耦合的但到了结算环节如果电量电费、调频容量费、里程费三个环节没有统一校核仍可能发生超量支付。我在项目交付时专门写了一个后处理校验模块对每个调频出清时段检查每台机组的“电量收益调频容量收益里程收益”是否超过其实际提供服务的物理上限。这个模块在试运行阶段帮我们发现了3个结算错误其中有一个还挺严重——某储能同时被能量市场和调频市场各自支付了满额容量费相当于一个容量卖了两份钱。联合清算模型修掉了物理耦合但账单层面的耦合仍需商业规则来兜底。6. 最后的几点心得做了这么多轮联合出清实验我自己最深的体会是MISOCP在联合市场清算里的价值不是“算得更快”而是“算得更准”。它的精度提升来自二阶锥对潮流物理的刻画它的工程可用性来自混合整数规划对离散决策的处理能力。两个能力合起来才让“能量频率”这种跨市场、跨时间、跨空间的大耦合问题有了一个既严格又可求解的统一框架。如果你打算在现有系统中引入联合清算我的建议是不要一开始就上全尺寸模型。先拿一个小系统把所有约束、变量、价格机制跑通再用真实数据做敏感性测试最后再切换到生产环境。模型里每个参数的物理意义必须在写代码之前想清楚尤其是那些跨市场耦合的约束——调频容量与出力的耦合、爬坡与调频的耦合、线路容量与调频空间的耦合。这些地方既是联合清算的收益来源也是模型出错的重灾区。最后分享一个小技巧做联合出清模型的调试时不妨把最终计算出的节点电价和调频价格打印出来逐条对照约束的对偶乘子。如果某个节点的价格突然异常偏高几乎总能在它的网络约束对偶值里找到原因。价格里面的信息量比调度计划大得多它是一个优化模型全部物理约束和经济信号的浓缩。学会读价格才算真正读懂了这个模型。