ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

考虑热备用和安全约束的机组组合优化调度:MATLAB建模与实现

考虑热备用和安全约束的机组组合优化调度:MATLAB建模与实现 做电力系统调度优化的同学应该都绕不开机组组合问题。机组组合Unit CommitmentUC说白了就是回答一个每天都在发生的问题未来24小时电网里哪几台机组该开着、哪几台该停掉、每台发多少电量才能在满足负荷和运行约束的前提下把总成本压到最低。这题目听起来像排个值班表但真做起来才发现背后全是混合整数规划、爬坡约束、启停时间限制、备用容量分配这些硬骨头。这篇笔记我结合自己用MATLAB做的一个小算例把“考虑热备用和安全约束的机组组合优化调度”从模型搭建到代码实现完整走一遍。适合正在做电力系统优化课程设计、毕设或者刚入调度岗想搞清楚SCUCSecurity-Constrained Unit Commitment安全约束机组组合原理的朋友。我最初接触这个题目的时侯以为只要把负荷平衡和机组出力上下限写上去就能跑结果第一次求解出来的方案在凌晨时段频繁启停机组调度员要是真拿着这种计划去操作电厂那边估计得骂人。后来才明白机组组合不是简单的经济调度它最大的难点在于把电力系统运行中的各种物理规律和可靠性要求翻译成数学约束。特别是热备用约束它直接决定了系统在失去一台最大机组或负荷突然上涨时能不能扛得住安全约束则决定了发出来的电能不能通过输电网送到用户端。这两类约束加进来之后问题复杂度立刻上了一个台阶但也真正贴近了工程实际。1. 先把问题说透机组组合到底在干什么1.1 火电机组的“热惯性”决定了计划不能拍脑袋电力系统有一个非常特殊的性质电能难以大规模存储发多少就要用多少系统时刻处于动态平衡之中。用户侧的空调、电机、照明负荷每时每刻都在变化调度员必须提前安排好发电侧的“兵力部署”确保负荷涨上来的时候有人能顶上。火电机组不是按一下开关就能立刻满发的设备。一台煤电机组从冷态启动到并网带满负荷往往需要几个小时甚至十几个小时因为锅炉要加热、汽轮机要暖机、各种辅机要逐步投入。如果等到负荷突增再临时开机黄花菜都凉了。所以调度部门必须提前一天甚至更早基于负荷预测结果确定次日各个时段的开机组合。这就是机组组合优化问题的来源在已知未来负荷曲线的条件下决定每台机组在哪些时段运行、哪些时段停机以及运行时发多少电。在这个决策过程中成本是核心驱动因素。不同机组的经济性差异很大大型高效机组单位发电成本低小机组高频繁启停又会带来额外的燃料消耗和设备损耗。机组组合优化的目标就是在成本和安全之间找平衡点该开的机组不能少能省的钱尽量省。1.2 热备用是系统安全的底线不是额外的发电量热备用这个概念很多刚接触电力系统优化的同学容易理解偏。热备用不是指“多留一些发电能力备用”而是指那些已经并网运行、处于热状态、可以在短时间内增加出力的机组容量。与之对应的是冷备用冷备用机组没有并网从启动到并网需要很长时间。在系统遭遇机组跳闸、负荷预测偏差、新能源出力波动等突发情况时能快速响应的只有热备用。热备用约束的工程意义非常直观系统中任何一台机组突然退出运行剩下的在线机组必须有能力在短时间内把出力顶上去否则系统频率就会跌落严重时可能导致更大范围的连锁故障。因此调度规程通常要求系统的热备用容量不低于最大单机容量或系统最大负荷的一定比例。但是热备用不是白给的。一台机组要提供热备用就必须留出可上调的出力空间这意味着它不能发到满负荷把本可以低价发出来的电量“闲置”了。从经济角度看热备用需求越大系统被迫多开机组、降低高效机组出力的可能性就越大运行成本也就越高。机组组合模型里热备用约束就是把这种“安全底线”转化为数学不等式逼着优化算法在成本和可靠性之间做一个折中。1.3 从UC到SCUC把输电网络的“水桶效应”放进来早期教材里讲的机组组合往往只考虑“机组—负荷”之间的功率平衡也就是把所有发电机和所有负荷看作一个点只要总发电量等于总负荷量就行。这种简化在电力系统发展初期、电网结构简单的时代是可以接受的。但现在的大规模互联电网输电线路的输送能力往往成为限制发电计划落地的瓶颈。举一个很典型的例子某大型高效机组位于西部电源基地低成本电量通过长距离输电线路送往东部负荷中心。如果机组组合计划只考虑总功率平衡让这台机组满发算下来成本很低但实际执行时却发现送电线路容量不够导致潮流越限。调度员拿到这种计划根本无法执行只能手工调整。安全约束机组组合SCUC就是在传统机组组合模型的基础上把输电线路的潮流约束、节点电压约束在直流潮流模型下主要是线路功率约束纳入优化模型中确保组合方案在物理上可执行。从数学模型的角度看SCUC比传统UC多了网络约束方程问题的约束规模显著增加求解难度也水涨船高。但对于现代电力系统运行而言这一步绕不开。中风、光伏等新能源大规模接入后输电通道阻塞问题越来越频繁不考虑网络约束的机组组合方案基本没有实用价值。2. 数学模型把调度经验翻译成约束条件2.1 目标函数用最少的钱办可靠的事机组组合优化的目标函数看起来简单——总运行成本最小——但拆开来其实有三部分燃料成本、启动成本和停机成本。燃料成本是运行成本的大头通常用机组出力P的二次函数近似[ C_{fuel}(P) aP^2 bP c ]其中a、b、c是机组的煤耗特性系数不同机组差异很大。用二次函数描述的燃料成本曲线有一个特点边际成本随出力增加而上升这符合热力学的实际情况——机组接近额定出力时效率下降单位发电成本上升。启动成本是机组从停机状态转为运行状态时的一次性成本包括锅炉点火、暖管、燃料消耗等费用。启动成本与停机时间长短有关停机时间越长机组冷却越充分重新启动需要消耗的能量越多。在简化模型中通常按固定值处理或者分冷启动、温启动、热启动三档。停机成本相对较小很多模型干脆把它并入启动成本或者直接忽略。用数学语言描述目标函数写成分段形式[ \min \sum_{t1}^{T}\sum_{i1}^{N} \left[F_i(P_{i,t}) SU_i \cdot s_{i,t}\right] ]其中t是时段序号i是机组序号(F_i(P))是机组i的燃料成本函数(SU_i)是启动成本(s_{i,t})是启动事件变量表示机组i在时段t是否从停机转为运行。在实际编程中二次成本函数会给混合整数规划求解器增加负担。因为intlinprog这类求解器要求目标函数是线性的处理非线性目标需要用分段线性化技巧。我在算例中先采用线性燃料成本即斜率固定即每个机组只有一个边际成本系数这样模型简洁新手容易读懂。如果需要更精确的煤耗曲线可以在跑通基础模型之后再按分段线性化的方式扩展目标函数。2.2 机组侧约束一长串物理限制一个都不能少机组组合模型里机组自身的约束是最容易遗漏、也最容易写错的。这些约束直接反映了机组的物理特性和运行规则每一条背后都有具体的工程背景。首先是出力上下限约束[ P_{i,min} \cdot u_{i,t} \le P_{i,t} \le P_{i,max} \cdot u_{i,t} ]其中(u_{i,t})是机组i在时段t的运行状态1表示运行0表示停机。这个约束的逻辑是机组停机时出力必须为0开机时出力不能低于最小技术出力低于这个值锅炉燃烧不稳定也不能超过额定最大出力。注意(u)乘在两侧才能让停机时段出力为零。其次是爬坡约束。机组出力不能瞬间变化受制于锅炉和汽轮机的热应力机组在相邻两个时段之间的出力变化量有一个上限。数学上写为[ P_{i,t} - P_{i,t-1} \le RU_i (2-u_{i,t}-u_{i,t-1}) \cdot M_i ][ P_{i,t-1} - P_{i,t} \le RD_i (2-u_{i,t}-u_{i,t-1}) \cdot M_i ]这里RU和RD分别是向上、向下爬坡速率M是一个足够大的数通常取机组的最大出力。这个写法用大M法处理启停状态切换的时段如果机组在t和t-1都处于开机状态右边就是爬坡速率本身约束生效如果有一个时段机组是停机的右边被放大到M量级约束自动放宽。这个技巧非常实用很多教材里直接写爬坡约束而没有大M项在机组没有连续运行时会导致模型无解。最小运行时间和最小停机时间约束是机组组合模型中最能体现“状态记忆”的约束也是最容易把求解器搞到崩溃的约束。机组一旦启动不能运行一两个小时代就停这会烧坏设备同样停机之后必须经过足够的冷却时间才能再次启动。这类约束在数学上有多种建模方法我采用的是启动事件变量法定义(s_{i,t})表示机组i在时段t启动从0变1(d_{i,t})表示在时段t停机从1变0。最小运行时间约束可以写成如果机组在t时刻启动那么在后续(T_{up})个时段内都必须保持运行[ \sum_{kt}^{tT_{up}-1} u_{i,k} \ge T_{up} \cdot s_{i,t} ]同理如果机组在t时刻停机那么在后续(T_{down})个时段内必须保持停机[ \sum_{kt}^{tT_{down}-1} (1 - u_{i,k}) \ge T_{down} \cdot d_{i,t} ]这种约束在电网实际运行中的意义很好理解。我一开始做模型时采用了一个极其简化的版本——只约束相邻两个时段的启停切换结果求解出来的方案让一台大型机组一天启停了4次现实中电厂绝对不可能配合这种操作这让我意识到最小启停时间是必须严格建模的。2.3 系统侧约束与直流潮流模型机组侧的约束全部齐活之后还需要把系统层面的约束加进来。最基本的系统约束是功率平衡约束[ \sum_{i1}^{N} P_{i,t} D_t ]即每个时段所有机组的出力总和必须等于负荷需求。这个约束看起来简单但要注意单位统一和网络损耗的问题。交流潮流模型里还有网损需要额外处理在直流潮流模型下通常忽略网损直接令发电等于负荷。热备用约束是安全性的核心体现[ \sum_{i1}^{N} R_{i,t} \ge R_t^{req} ]其中(R_{i,t})是机组i在时段t可提供的热备用容量。关键问题在于(R_{i,t})怎么算。最准确的模型是[ R_{i,t} \min(P_{i,max} - P_{i,t},\ RU_i) \cdot u_{i,t} ]这个式子的物理含义是一台机组能提供的备用既要看它还有多少出力空间Pmax减去当前出力又要看它的爬坡能力能不能在短时间内把电量增发出来。停机状态的机组不能提供热备用所以要乘以(u)。这个约束在MILP中需要引入辅助变量并线性化我会在代码部分演示具体做法。安全约束即网络约束。在SCUC中最常用的简化模型是直流潮流模型。直流潮流假设线路电阻远小于电抗、电压接近额定值、相角差足够小从而把交流潮流简化为线性方程组可以方便地嵌入优化模型。支路潮流的计算公式为[ P_{ij,t} b_{ij}(\theta_{i,t} - \theta_{j,t}) ]其中(b_{ij})是线路电纳(\theta)是节点相角。线路潮流必须满足容量限制[ -P_{ij,max} \le P_{ij,t} \le P_{ij,max} ]加上网络约束之后机组组合问题变成真正的SCUC问题。算出来的结果不仅告诉你哪些机组该开还能告诉你发电计划不会让任何一条线路超载。3. MATLAB代码实现从公式到可运行的优化模型3.1 建模工具怎么选YALMIP intlinprog还是直写intlinprog用MATLAB做机组组合优化建模方式主要有三条路第一手写intlinprog的矩阵参数把约束逐条变成A、b矩阵第二用YALMIP这类建模工具箱用类似数学公式的方式描述约束再调用底层的求解器第三用MATPOWER等专用电力系统分析工具自带的功能。我的建议是只要你不是在生产环境中追求极致的运行效率就用YALMIP。原因是机组组合模型的约束量很大手动整理矩阵又容易出错而且一旦模型需要调整——比如加一台机组、改一个备用比例——手写矩阵版的改动成本极高。YALMIP允许你直接用符号变量描述约束表达式代码可读性和可维护性都高出好几个档次。求解器方面如果你没有额外安装Gurobi或CPLEX用MATLAB自带的intlinprog就够了。intlinprog是MATLAB内置的混合整数线性规划求解器对于几十台机组、24个时段的算例求解速度完全可以接受。我自己的经验是10台机组以内的中小算例intlinprog通常在几秒到几十秒内就能收敛到最优解。3.2 数据准备与决策变量定义先定义一个小的测试系统。为了便于读者复现我用一个6节点、4台火电机组的系统负荷曲线也是我自己设计的典型日负荷形状。机组参数如下表所示机组所在节点Pmax(MW)Pmin(MW)爬坡(MW/h)最小运行时间(h)最小停机时间(h)启动成本(元)燃料成本系数(元/MWh)G11300601204480018G2222040903360020G3318030802240023G4410020501120028这四台机组的经济性差异比较明显G1煤耗最低、容量最大G4容量最小、成本最高。在负荷低谷时段最优方案大概率只开G1或者G1G2只有在负荷很高或者备用需求压力大的时候G3、G4才会被调用起来。系统共有7条支路拓扑设计成一个接近实际电网的环形结构。负荷主要分布在节点4、5、6其中节点4还有一台小机组G4这样能给“本地出力缓解阻塞”提供可能性。各支路参数如下支路起点终点电纳(p.u.)容量(MW)L1120.25200L2140.28180L3230.22160L4250.30180L5360.26150L6450.24150L7560.2716064时段日负荷曲线我按典型“峰谷平”三段式设置凌晨低谷约300MW白天高峰接近600MW。负荷分配到节点4、5、6的比例大概是40%、30%、30%。在MATLAB代码里数据部分长这样% 24时段负荷曲线单位MW D [320,310,300,295,300,320,380,460,520,560,580,570,... 550,540,560,590,600,580,560,540,500,460,420,360]; % 机组数据矩阵 % [Pmax Pmin RU RD MinUp MinDown StartCost FuelCost] gen [ 300, 60, 120, 120, 4, 4, 800, 18; 220, 40, 90, 90, 3, 3, 600, 20; 180, 30, 80, 80, 2, 2, 400, 23; 100, 20, 50, 50, 1, 1, 200, 28; ]; % 支路数据矩阵 % [from to b(p.u.) FlowMax(MW)] branch [ 1, 2, 0.25, 200; 1, 4, 0.28, 180; 2, 3, 0.22, 160; 2, 5, 0.30, 180; 3, 6, 0.26, 150; 4, 5, 0.24, 150; 5, 6, 0.27, 160; ];这个负荷曲线的数据量级和实际电网相比缩小了不少但保持了“凌晨低、白天高、晚高峰回落”的真实形态用来展示机组组合的逻辑已经足够。用YALMIP定义决策变量时机组状态变量是24×4的0-1矩阵出力变量是24×4的连续变量矩阵另外还需要定义启动事件变量、停机事件变量和直流潮流中的相角变量T 24; ng 4; N 6; L size(branch,1); u binvar(T, ng, full); % 机组运行状态 P sdpvar(T, ng, full); % 机组出力 su binvar(T, ng, full); % 启动事件 sd binvar(T, ng, full); % 停机事件 r sdpvar(T, ng, full); % 机组热备用容量 theta sdpvar(T, N, full); % 节点相角 Pflow sdpvar(T, L, full); % 支路潮流3.3 核心约束代码与求解流程在YALMIP中建立约束的代码基本上就是把数学模型逐条翻译过来。首先是机组自身的约束Constraints []; % 出力上下限 for t 1:T for i 1:ng Constraints [Constraints, ... gen(i,2) * u(t,i) P(t,i) gen(i,1) * u(t,i)]; end end % 爬坡约束包含启停放宽的大M项 for t 2:T for i 1:ng Constraints [Constraints, ... P(t,i) - P(t-1,i) gen(i,3) (2-u(t,i)-u(t-1,i)) * gen(i,1)]; Constraints [Constraints, ... P(t-1,i) - P(t,i) gen(i,4) (2-u(t,i)-u(t-1,i)) * gen(i,1)]; end end然后是启动事件和停机事件的定义以及最小启停时间约束。这里的关键点是启动事件变量必须由状态变量u的差分来定义同时用不等式把它的取值“夹”在合理的范围内% 启动/停机事件定义 for t 2:T for i 1:ng Constraints [Constraints, ... su(t,i) u(t,i) - u(t-1,i)]; Constraints [Constraints, ... su(t,i) u(t,i)]; Constraints [Constraints, ... su(t,i) 1 - u(t-1,i)]; Constraints [Constraints, ... sd(t,i) u(t-1,i) - u(t,i)]; Constraints [Constraints, ... sd(t,i) u(t-1,i)]; Constraints [Constraints, ... sd(t,i) 1 - u(t,i)]; end end % 最小运行/停机时间 for i 1:ng for t 2:T % 最小运行时间 uptime gen(i,5); Constraints [Constraints, ... sum(u(t:min(T, tuptime-1), i)) uptime * su(t,i)]; % 最小停机时间 downtime gen(i,6); Constraints [Constraints, ... sum(1-u(t:min(T, tdowntime-1), i)) downtime * sd(t,i)]; end end这里t从2开始是因为初始时刻t1没有前一个状态可以参照。对于机组在t1时刻的初始状态我在算例中假设所有机组在调度日开始时处于运行状态且已经满足了最小启停时间这样避免处理边界条件的复杂性。实际调度的场景中前一天的计划会给出一组初始状态需要在模型中额外加约束来衔接。接下来是系统约束和热备用约束。热备用约束的线性化是一个常见的考点% 系统功率平衡 for t 1:T Constraints [Constraints, sum(P(t,:)) D(t)]; end % 热备用约束 R_req 120; % 系统所需热备用容量MW for t 1:T for i 1:ng Constraints [Constraints, r(t,i) 0]; Constraints [Constraints, r(t,i) gen(i,1) - P(t,i)]; Constraints [Constraints, r(t,i) gen(i,3) * u(t,i)]; end Constraints [Constraints, sum(r(t,:)) R_req]; end注意热备用约束里r(t,i)同时受“剩余出力空间”和“爬坡能力”两条上限约束。第四条约束r RU*u是关键中的关键如果机组处于停机状态备用量强制为0否则模型会在停机机组上分配出不存在的备用容量。直流潮流约束和支路潮流约束如下% 构建节点电纳矩阵 B zeros(N, N); for k 1:L i branch(k,1); j branch(k,2); b branch(k,3); B(i,i) B(i,i) b; B(j,j) B(j,j) b; B(i,j) B(i,j) - b; B(j,i) B(j,i) - b; end Bred B(2:end, 2:end); % 直流潮流约束 genBus [1, 2, 3, 4]; for t 1:T Pinj zeros(N,1); for i 1:ng Pinj(genBus(i)) Pinj(genBus(i)) P(t,i); end Pinj Pinj - [0; 0; 0; 0.4*D(t); 0.3*D(t); 0.3*D(t)]; Constraints [Constraints, theta(t,1) 0]; Constraints [Constraints, Bred * theta(t,2:end) Pinj(2:end)]; end % 支路潮流计算与限制 for t 1:T for k 1:L i branch(k,1); j branch(k,2); b branch(k,3); Constraints [Constraints, ... Pflow(t,k) b * (theta(t,i) - theta(t,j))]; Constraints [Constraints, ... -branch(k,4) Pflow(t,k) branch(k,4)]; end end这个代码片段里有一个细节值得注意负荷在节点4、5、6上的分配用0.4、0.3、0.3的比例乘上总负荷。节点4有发电机G4也有负荷它的净注入是出力减去本地负荷。最后是目标函数和求解命令% 目标函数燃料成本 启动成本 FuelCost sum(sum(gen(:,8) .* P)); StartCost sum(sum(gen(:,7) .* su)); Cost FuelCost StartCost; % 设置求解器并求解 ops sdpsettings(solver, intlinprog, verbose, 2); sol optimize(Constraints, Cost, ops);在YALMIP中求解完成之后可以用value()函数提取每个变量的数值。比如u_opt value(u); P_opt value(P); Pflow_opt value(Pflow);我习惯先把结果存成表格和图形再细看每一个时段的开机组合和每条线路的潮流情况。这样可以快速发现模型中的逻辑错误比如某条线路潮流数据不符合基尔霍夫定律说明约束漏了或者方向写反了。4. 算例结果热备用和安全约束如何改变组合方案4.1 测试系统配置总览在展示结果之前先回顾一下算例的整体配置。这是一个6节点系统节点1、2、3、4分别接入一台机组节点4同时还承担一部分负荷节点5和节点6是纯负荷节点。24小时的总负荷从凌晨的295MW到晚高峰的600MW之间变化。热备用需求设为120MW。这个数值不是拍脑袋定的——在只有4台机组的小系统中最大单机容量是300MW按规程热备用应该接近这个量级但真按300MW来算的话整个算例几乎任何时段都要把所有机组全开反而看不出优化的意义。所以我把需求调到120MW既能让备用约束在某些时段改变开机方案又不至于让模型失去灵活性。实际工程中热备用需求一般是根据最大单机容量和负荷预测偏差综合计算出来的。为了对比效果我跑了三个场景场景A是基础机组组合只考虑功率平衡和机组自身约束场景B在A的基础上加入热备用约束场景C在B的基础上再加入线路潮流约束也就是完整的SCUC模型。4.2 加不加热备用约束差别有多大先看场景A的结果。凌晨谷荷时段负荷只有300MW出头G1单机组出力就能满足功率平衡虽然G1的最小出力是60MW但只要让它发到300MW就够负载了。但是问题来了G1满发时没有剩余容量一旦它跳闸整个系统立刻失去全部电源。这就是没有热备用约束时方案“经济但危险”的地方。场景B加上了120MW热备用约束之后情况立刻不一样了。凌晨时段还要再开一台机组哪怕这台机组只需发很小的功率也要维持在线状态。为什么因为G1如果发到接近满负荷它能提供的备用容量只有区区几十兆瓦不满足120MW的要求。只有另一台机组在线运行才能贡献充足的备用空间。G4虽然启动成本低、容量小但它的爬坡能力只有50MW/h燃料成本又高所以在凌晨通常选择G2作为在线备用机组。从成本数字上看场景B比场景A的日运行成本高出了一截多出来的主要是第二台机组的燃料成本和启动成本。这其实就是“可靠性是有代价的”在数学优化中的直接体现。不承担这部分成本系统就在拿安全开玩笑。我还特意看了场景B中每个时段的备用分配明细。在一部分负荷适中的时段系统把G1的出力压低到250MW左右G2发到100MW附近两台机组的可上调空间各贡献一部分凑够120MW备用。这说明热备用约束并不是简单“多开一台机组”就能满足还存在一个最优出力分配问题——哪台机组让出多少发电空间让总成本上升最小这是优化算法应该自动解决的。4.3 加不加网络约束方案怎么变场景C加入直流潮流约束之后出现了一个很有意思的现象在某些高峰时段G1的出力被压下来了改由靠近负荷中心的G4多发电。原因很简单G1位于节点1它发的电要通过线路L1和L2送到节点4、5、6的负荷区。在没有网络约束的场景B中G1满发300MW大量功率通过L2容量180MW外送结果L2严重越限。网络约束一加上优化算法不得不重新分配各台机组的出力。G1出力被限制G3和G4的出力增加。G4虽然燃料成本高但它就在节点4发电可以直接供给本地负荷几乎不占用输电通道因此在网络阻塞时段反而成了“香饽饽”。这就是SCUC的价值所在它不会让计划在纸面上很漂亮执行起来却到处碰壁。从三条线路的潮流数据可以看到加了网络约束之后所有线路的潮流都被控制在容量限值之内L2留有一定的安全裕度。整体运行成本自然也跟着涨了。我记得场景C的总成本比场景A高了大约6%到8%这个涨幅对于确保输电安全来说完全在可接受范围内。5. 新手最容易踩的坑与调试心得5.1 求解器接口与数值问题用YALMIP有个习惯要养成在求解之前先确认求解器有没有正确识别模型类型。对于纯线性目标线性约束二进制变量的MILP问题YALMIP会调用intlinprog或Gurobi但如果模型中不小心写入了非线性表达式YALMIP会尝试调用fmincon等非线性求解器求解效率和稳定性都差很多。我的做法是在求解后检查sol.info看是否返回了Successfully solved如果不是就把sol.info的内容打出来仔细看。数值问题在机组组合模型中也很常见。大M参数的选取是一个经典难题取得太大约束中的数值尺度差异过大求解器容易陷入数值不稳定取得太小又可能把有效约束错误地放宽。我在爬坡约束中把大M取为机组的Pmax因为Pmax本身比爬坡速率大一个量级足够“放松”停机时段又不至于带来数值问题。读者如果遇到求解器报错“infeasible problem”可以检查大M项是否写反了方向。5.2 约束建模的高频错误第一个高频错误启动事件变量定义不完整。如果只写了su(t) u(t)-u(t-1)而不补充su(t) u(t)和su(t) 1-u(t-1)那么su(t)可能被松弛成一个较大的值只有下限约束。虽然目标函数里启动成本会压低su(t)但在某些情况下尤其是启动成本为0时su(t)的取值可能变得任意导致最小启停时间约束失去意义。第二个高频错误爬坡约束里忘记乘u(t)或u(t-1)。这个错误会导致机组在停机状态下也被分配一个出力变化量逻辑上说不通。我一开始在写备用约束时也犯过类似的错误结果求解出的备用容量出现在停机的机组上后来把r(t,i) RU(u)加上之后才修正过来。第三个高频错误热备用需求方向写反。有的同学会把Σr(t) R写成或者导致系统“故意”少留备用这在优化里属于典型的约束方向错误。检查的时候把value(r)打印出来逐时段看是否满足备用需求一眼就能发现问题。第四个高频错误直流潮流中节点注入向量和支路关联关系搞混。潮流的正方向定义了线路“从哪头流向哪头”虽然从优化求解的角度看潮流正负只影响约束的方向但如果不统一支路潮流的上限约束就可能形同虚设。每建完一个模型我都会随机取一个时段用手动计算的支路潮流验证一下Pflow的值。5.3 结果合不合理用这几条自检经过多次调试我总结了一套机组组合结果的自检清单每次跑出新方案都会逐条检查这里分享给读者检查项目判断标准常见错误表现功率平衡每时段各机组出力之和等于负荷总出力不等于负荷模型无解或漏写约束出力范围运行机组出力在Pmin和Pmax之间停机机组出力为0停机机组有正出力热备用每时段可用备用不低于系统需求备用为负、备用集中在停机机组爬坡相邻时段出力差不超过爬坡速率出力在启停瞬间出现不合理跳变最小启停时间机组开机时长不小于规定值机组一天内频繁启停线路潮流所有支路潮流在容量限值内某条线路潮流越限被忽略启动成本启动事件次数与机组状态变化一致su(t)1但u前后没有变化这套清单我用了很久可以说救了我无数次。很多时候模型“跑通”了但结果不用看也知道有问题比如机组在半夜启停——这种方案上报的话是没用的。最后再分享一点实际操作的体会机组组合这个方向代码实现从来不是最难的环节真正难的是把每一个约束的物理含义搞清楚再转换成正确的数学表达式。我在跑通这个基础版SCUC模型之后又做了几个方向的扩展增加N-1安全约束预想故障集、把热备用需求改成动态变化的、在目标函数中加入新能源出力的不确定性惩罚项。每扩展一个功能都要回到最基础的模型逻辑上去审视看新增约束会不会和原有约束冲突、会不会导致求解困难。如果你想在这个代码基础上继续深入建议先尝试把网络约束从直流潮流升级成线性化的交流潮流再引入储能设备的多时段耦合约束。这些扩展本质上还是在机组组合这个基本框架上叠加更多的复杂约束层核心的建模思路、求解框架和我上面讲的完全一致。先把这篇文章里的基础模型吃透后面再去研究那些进阶版本就会轻松很多。
返回列表