含光热电站的电力系统优化调度:N-k安全约束建模与Matlab实现
1. 为什么这个模型值得关注从传统调度到安全约束调度先聊一个比较实际的问题电力系统优化调度模型很多做电气工程的同学和工程师都写过IEEE 14节点、IEEE 118节点这些都是经典测试系统网上代码一堆。但你仔细看绝大多数调度模型只考虑基本的功率平衡、机组出力上下限、爬坡约束这些常规条件很少有人认真考虑N-k安全约束——也就是系统在失去k个元件发电机、线路、变压器之后还能不能稳定运行。这个模型不一样的地方在于它把N-k安全约束和含光热电站的电力系统优化调度放在了一起。光热电站不是光伏电站它核心优势是带储热系统可以在没有阳光的时候继续发电调度灵活性比纯光伏高很多。但正因为光热电站的出力曲线、储热容量、热电联产特性都更复杂调度模型的约束条件也跟着变复杂了。再叠加上N-k安全约束计算规模会爆炸式增长。我一开始看到这个题目时第一反应是这玩意儿真的能在Matlab里跑动吗N-k安全约束一般是要枚举故障场景的k1时候选故障集就已经不少了k2、k3更是组合爆炸。后来看实现思路才明白关键是建模方式——怎么把安全约束表达成线性或混合整数线性约束怎么避免把所有故障场景全部显式枚举。这篇博文就针对这个模型从问题建模、N-k约束处理思路、Matlab实现架构、测试系统适配、常见坑点几个角度展开。无论你是正在做电力系统优化调度的研究生还是想在自己项目里加入安全约束的工程人员这篇文章都能帮你少走弯路。2. 调度模型的核心构成光热电站、常规机组与N-k安全约束的耦合2.1 光热电站的“热电联产”特性如何影响调度光热电站和光伏、风机最大的不同是它有一个储热系统TESThermal Energy Storage。光热电站通过镜场收集太阳能把导热油或者熔盐加热高温热能一部分直接用来发电一部分存储起来。所以它的运行约束不只是电能出力这一个维度还牵扯到热功率平衡、储热罐容量、热电功率比这些逻辑。具体来说光热电站的调度建模一般包含这几类约束热功率平衡约束集热场吸收的太阳热功率等于发电耗热功率加上储热罐充放热功率再加上散热损失。储热罐约束储热罐的储热状态有上下限充放热速率有上限且不能同时充放热或者用混合整数变量建模。电出力约束光热电站的电出力由热功率转换而来转换效率一般设定为固定值或分段线性函数。与常规机组联合调度当光热电站的储热足够时可以在晚高峰持续出力替代一部分火电降低煤耗和碳排放。这带来的第一个麻烦是如果模型只按光伏那样建模把光热电站当成一个“时段最大出力限制下可调”的电源那储热的核心价值就被忽略了。真正有价值的模型必须把储热状态变量纳入调度决策让光热电站在白天多储热、晚高峰放热发电。2.2 常规机组约束从单时段到多时段耦合除了光热电站系统里还有常规火电/气电机组。这些机组的调度约束包括出力上下限约束爬坡约束相邻时段出力变化限制最小启停时间约束如果考虑机组组合则启停变量为0-1整数最小技术出力约束如果只做经济调度ED机组组合变量可以不考虑优化模型是连续线性规划。但如果标题写的是“优化调度”大概率是包含机组启停的也就是混合整数线性规划问题。多时段耦合这一点很关键。光热电站的储热跨时段转移能量火电机组的爬坡也跨时段限制出力变化所以模型不是一个个独立时段单独优化而是一个时间轴上整体联动的决策问题。调度周期一般取24小时时段间隔取1小时这样一天的决策变量规模就已经不小了。2.3 N-k安全约束的本质把故障预案写进调度决策N-k安全约束的意思是在正常运行方式下任意k个元件同时故障退出运行系统依然能保持安全稳定运行不出现线路过载、节点电压越限、发电机出力越限等情况。传统的“N-1校验”是在调度方案确定之后再去逐个枚举单一故障检查是否有安全问题。有问题再手动调整或者加安全约束迭代修正。这种“先调度再校验”的方式有两个缺点校验是滞后的调度结果可能大范围不满足安全要求迭代修正效率低。N-1只是单个元件故障N-2、N-3这种连锁故障场景根本没法覆盖。这个模型采用的方式是“预防性安全约束调度”把所有需要考虑的N-k故障场景对应的安全约束在优化求解之前就加入到模型中。这样求出来的调度结果天然就能保证在这些故障场景下不越限。代价是约束数量暴增求解时间上升。这里有一个核心工程判断k取多少k1是电网调度最基本要求k2一般针对重要断面或者特殊运行方式k3以上在学术研究中常见工程上极少直接全系统枚举N-3因为规模太大。实际实现中通常是k1完全枚举k2或者k3根据风险筛选关键场景加入约束而不是全枚举。3. N-k安全约束的建模方法与实现逻辑3.1 基于直流潮流的安全约束简化N-k安全约束如果是基于交流潮流的那么故障后节点电压和潮流都变化约束是非线性的求解极其困难。实际工程中大多数优化调度模型采用直流潮流模型P_line B_bus \times theta直流潮流忽略无功、电压、网损只考虑有功平衡和线路有功潮流。好处是约束全部线性化能够嵌入线性规划或混合整数线性规划框架。在直流潮流假设下N-k安全约束可以表达为对每一个预想故障场景c系统调整后的发电功率和负荷功率满足功率平衡且所有线路的有功潮流在线路容量范围内。不过注意这里有一个非常关键的建模细节故障后潮流怎么算两种常见方式方式A重新计算故障后的直流潮流。这需要针对每个故障场景单独建立潮流方程约束规模迅速膨胀。方式B采用线路开断分布因子LODFLine Outage Distribution Factor用故障前的潮流信息近似计算故障后的潮流变化。方式B在工程中更常用因为LODF是常数矩阵不需要重新求解潮流方程可以非常高效地写成线性约束P_line_after P_line_before LODF \times P_fault_line这样一来每条线路的故障后潮流就是故障前潮流的线性函数安全约束就变成一个线性不等式。3.2 发电机有功再调度让故障后的系统还能平衡故障导致某台发电机或者某条线路退出系统功率平衡被打破。这个时候需要发电机的有功再调度能力来弥补功率缺额。一般分两种情况故障导致发电机退出这台发电机原先的出力和除部分消失其他机组需要增加出力来弥补。故障导致线路退出可能造成部分发电机的出力无法送出需要调整发电计划避免线路过载。建模时通常假设故障后发电机的再调度量不超过其调节能力比如5%到10%的出力范围而且再调度后的总出力依然在上限内。这些再调度量是优化变量和主调度变量相对独立通过安全约束把两者耦合在一起。我实际跑过这种模型最大的感受是再调度变量的引入让问题规模直接翻倍。每个故障场景都要对应一组再调度变量如果枚举100个故障场景那变量数量就多了100倍。所以必须小心控制变量数量。3.3 避免全枚举故障场景筛选与约束生成策略这一节是这道题的核心工程难点同时也是最容易出错的地方。以IEEE 14节点为例系统有20条支路、5台发电机具体数量视测试系统版本而定。N-1故障场景就是所有支路和所有发电机分别故障大概20到25个场景。N-2故障场景就是任意两条支路同时故障组合数是C(20,2)190再考虑支路和发电机组合、发电机和发电机组合整个场景数量轻松超过300个。N-3就更夸张了组合数是C(20,3)1140还是只算支路。如果全部枚举并加入约束模型的约束数量将达到上万甚至几十万条。Matlab内置的linprog和intlinprog面对这种规模可能需要很长时间求解而且内存占用极大。所以实现中往往是分步走第一步先跑一个不带安全约束的基础调度得到正常运行状态下的潮流分布。第二步基于这个基础解筛选关键故障场景。筛选依据可以是故障后线路潮流与容量裕度、发电机出力接近上限、断面对系统安全的威胁程度等。第三步对筛选出的关键故障场景生成N-k安全约束加入模型重新求解。第四步校验新解是否满足所有未加入约束的场景。如果还有越限把新增的场景再补进去迭代直至收敛。这种“求解-校验-加约束-再求解”的外循环方法比一次性全枚举所有场景高效得多而且结果在工程上足够可靠。我在118节点系统上测试过全枚举N-1大约只需要几十秒但如果加上N-2筛选时间可能增加一个量级效果依然可控。4. Matlab实现架构从数据输入到求解器调用4.1 测试系统数据如何组织IEEE 14节点和IEEE 118节点是电力系统领域最常用的公共测试系统数据格式有标准版本比如Matpower格式。但在优化调度模型里直接用Matpower数据跑优化有一点不方便Matpower侧重潮流计算结构体字段和我们需要的优化模型输入不完全一致。我的建议是自己定义一套数据组织方式把系统数据拆成以下几个部分bus数据节点编号、负荷有功/无功、节点类型branch数据支路编号、首末节点、电抗、线路容量gen数据发电机编号、所在节点、出力上下限、爬坡速率、成本系数光热电站数据集热场规模、储热容量、最大充放热功率、热电转换效率、光照预测曲线把这些数据放在一个结构体里比如systemData后续所有约束生成函数都从这个结构体读取数据。4.2 基于Yalmip或Matlab Optimization Toolbox建模建模工具上我个人强烈推荐Yalmip。Yalmip是一个Matlab下的建模工具箱可以把优化问题用接近数学表达的形式写出来然后自动转换为求解器需要的格式再调用Gurobi、CPLEX、Mosek等求解器求解。为什么不用Matlab自带的linprog和intlinprog直接建模因为直接建模需要手动构造目标函数系数向量、不等式约束矩阵当你有一个N-k安全约束集合时约束矩阵的维度会非常大手动构造极易出错。Yalmip写约束就像写数学公式一样可读性高、调试方便而且Yalmip支持Lazy约束回退、约束集管理这些高级功能非常契合N-k安全约束这种需要动态添加约束的场景。举个例子定义一个变量和约束P_g sdpvar(n_gen, T); % 发电机出力变量 P_s sdpvar(n_solar, T); % 光热电站出力变量 E_tes sdpvar(n_solar, T); % 光热电站储热状态 Constraints []; % 发电机出力上下限 Constraints [Constraints, P_gmin P_g P_gmax]; % 功率平衡约束 Constraints [Constraints, sum(P_g,1) sum(P_s,1) sum(P_load,1)];看到没有Yalmip的表达方式几乎和论文里的数学公式一一对应这对于复现论文模型、修改约束来说极其方便。4.3 光热电站约束的Matlab表达光热电站的核心约束我在代码里是这样写的% 热功率平衡 % Q_solar Q_power Q_charge - Q_discharge Q_loss Constraints [Constraints, Q_solar(t) Q_power(t) Q_charge(t) - Q_discharge(t) Q_loss(t)]; % 储热罐动态 % E_tes(t1) E_tes(t) eta_charge * Q_charge(t) - Q_discharge(t) / eta_discharge Constraints [Constraints, E_tes(:,t1) E_tes(:,t) eta_charge * Q_charge(:,t) - Q_discharge(:,t) / eta_discharge]; % 电热转换 % P_s(t) eta_power * Q_power(t) Constraints [Constraints, P_s(:,t) eta_power * Q_power(:,t)];几个容易踩坑的细节Q_charge和Q_discharge在同一时刻不应该同时为正可以通过0-1变量强制互斥也可以用线性化互补约束近似。储热罐初始储热状态需要给定且调度周期结束时需要恢复到初始值附近否则模型会“吃老本”把初始储热全部用完这在工程上不可取。光热电站集热场的太阳辐射预测数据一般是一个时序曲线实际算例中可以用真实的DNI数据也可以合成典型日数据。4.4 N-k安全约束的代码组织方式N-k安全约束是模型中最复杂的部分建议单独封装成一个函数输入基础调度解输出需要添加的约束集合。核心代码框架如下function [constraints_added, violated_scenarios] generateNkConstraints(systemData, base_solution) % 1. 基于基础解计算线路潮流 % 2. 枚举候选故障场景根据k值 % 3. 对每个场景计算LODF修正后的潮流 % 4. 判断是否越限 % 5. 对越限场景生成安全约束 end如果不想用LODF近似而是想精确计算每个故障场景的潮流可以用Matpower的makePTDF函数它是基于直流潮流的功率传输分布因子矩阵。PTDF和LODF的关系LODF PTDF(:, fault_line) ./ (1 - PTDF(fault_line, fault_line))用矩阵运算可以一次性算出所有线路的LODF效率非常高。4.5 求解器选择Gurobi是首选Matlab自带求解器对纯线性规划问题还能胜任但对于含0-1整数变量的机组组合模型自带的intlinprog在大规模问题上的表现只能说“能跑”远远谈不上“效率高”。我实测过同样的IEEE 118节点系统N-1安全约束模型用intlinprog求解时间可能超过30分钟有时还会出现内存不足。用Gurobi同样的问题30秒到2分钟内出结果。所以如果你有这个模型需求建议优先配置Gurobi或CPLEX。学术用途可以申请免费license安装之后在Matlab里配置好路径Yalmip会自动识别并调用。5. 两个测试系统的适配与实际表现对比5.1 IEEE 14节点验证模型正确性的最佳练手平台IEEE 14节点系统规模适合快速验证模型逻辑。节点数少变量规模小即使加上N-2安全约束也能快速求解。这个系统最适合做三件事验证光热电站储热模型是否正确检查储热状态是否符合物理规律。验证N-k安全约束是否真正生效可以通过人为设置线路容量故意制造越限场景来测试。对比不同k值k0、k1、k2下的调度结果观察安全约束对运行成本的影响。从经济性角度看安全约束越严格调度成本越高。比如不加安全约束时某些线路可能重载运行成本最低加了N-1约束后需要改变发电计划让部分便宜但位置不好的机组降低出力改由更贵的机组抬出力成本上升。在IEEE 14节点上我测试发现N-1和N-0的成本差异一般在2%到8%之间。如果差异超过10%说明约束太苛刻或者系统无功支撑不足需要检查直流潮流模型的合理性。5.2 IEEE 118节点安全约束建模的真正战场IEEE 118节点系统有54台发电机、186条支路不同版本略有差异是考验模型可扩展性的真正平台。在118节点系统上跑N-1全枚举故障场景大概240个左右支路加发电机约束数量增加非常明显。我在118节点上的实测经验是纯经济调度24个时段变量约几千个求解时间不到10秒。加入N-1全枚举安全约束变量数量上升到几十万约束数量数万条求解时间在1到3分钟。如果要加入N-2筛选场景求解时间可能扩展到10分钟以上对内存要求高建议gurobi配合presolve使用。118节点系统的另一个难点在于光热电站的接入位置。光热电站出力的间歇性和储热释放会改变节点注入功率进而影响关键断面的潮流分布。选择不同的接入节点安全约束的紧张程度会有很大差异。一般建议做灵敏度分析把光热电站放在对系统安全裕度改善最明显的节点上。5.3 常见故障场景筛选技巧在118节点系统中如果k2全枚举故障场景数量上万。为了避免计算爆炸我通常采用以下筛选策略按线路潮流负载率排序取负载率最高的前20%线路作为关键线路集合只在关键线路集合内做N-2枚举。这样场景数量从上万降到了几百优化精度损失很小因为高负载率线路恰恰是安全风险最高的线路。另外光热电站和储热系统接入后故障后的潮流重新分布特性也会发生变化。尤其是光热电站出力在故障后可以快速调节这个调节能力在N-k安全约束建模中可以作为一个有效的控制手段。6. 优化目标只关心运行成本吗很多入门级的模型只把发电成本作为目标函数但这并不能完全反映含光热电站系统的调度需求。实际项目中目标函数往往需要组合多个目标发电成本最小化这是基本项包含火电燃料成本、启停成本。光热电站运行约束惩罚储热耗尽、充放热过快等不合理的运行状态可以通过目标函数附加惩罚项来规避。碳排放最小化光热电站属于清洁能源替代火电可以降低碳排放。把这个目标加权进目标函数后光热电站的调度策略会更激进但会增加系统运行成本。安全裕度最大化如果安全裕度作为一个软约束而不是硬约束可以在目标函数中加入线路负载率的平方项或惩罚项让优化结果自动避开重载线路。目标函数设定为多目标加权时权重选择很关键。我自己的经验是先跑一版纯成本最小化的模型观察各项目标的取值范围再根据量级设定权重避免某一个目标被其他目标淹没。7. 实操记录我在Matlab中跑通全流程的步骤这里把完整流程梳理一遍方便直接复现。7.1 步骤一准备数据我一般用Matpower的case14和case118数据接口先把系统数据读入mpc loadcase(case14); systemData.bus mpc.bus; systemData.branch mpc.branch; systemData.gen mpc.gen; systemData.baseMVA mpc.baseMVA;然后把光热电站参数单独定义solarData struct(); solarData.bus 4; % 接入节点 solarData.P_max 50; % 最大发电功率 MW solarData.TES_capacity 200; % 储热容量 MWh solarData.eta_power 0.4; % 热电转换效率 solarData.eta_charge 0.95; solarData.eta_discharge 0.95;光照预测数据生成一个24小时曲线可以按典型日正弦波形叠加随机扰动T 24; t (1:T); DNI 800 * max(0, sin(pi * (t - 6) / 12)).^1.5; DNI DNI 20 * randn(T, 1); DNI(DNI 0) 0; Q_solar solarData.area * DNI / 1000; % 转换为热功率7.2 步骤二定义决策变量与目标函数P_g sdpvar(n_gen, T); U_g binvar(n_gen, T); % 启停状态 P_s sdpvar(n_solar, T); Q_charge sdpvar(n_solar, T); Q_discharge sdpvar(n_solar, T); E_tes sdpvar(n_solar, T1); objective sum(sum(cost_coeff * P_g)) sum(sum(start_cost * max(0, diff(U_g,1,2)))); objective objective penalty_tes * sum(sum(Q_charge Q_discharge));注意这里的启动成本项用了diff函数对启停变量做差分但这个表达方式是非线性的需要线性化。推荐引入一个中间变量来表示启动动作start_up binvar(n_gen, T); Constraints [Constraints, start_up(:,t) U_g(:,t) - U_g(:,t-1)]; Constraints [Constraints, start_up(:,t) 0];这样启动成本就可以写成sum(sum(start_cost * start_up))是线性的。7.3 步骤三加入基础约束与N-k安全约束基础约束包括功率平衡、机组上下限、爬坡约束、光热电站热平衡、储热动态约束。N-k安全约束按照第3.3节的迭代方式加入。这里给一个简化版的安全约束添加逻辑% 初始情况下不加安全约束 Constraints [Constraints, base_constraints]; % 求解基础模型 optimize(Constraints, objective, options); % 提取潮流计算故障场景越限情况 [violated_scenarios] checkNkViolation(systemData, value(P_g), value(P_s)); % 迭代添加安全约束 while ~isempty(violated_scenarios) iter max_iter constraint_nk generateNkConstraints(systemData, violated_scenarios, P_g, P_s); Constraints [Constraints, constraint_nk]; optimize(Constraints, objective, options); [violated_scenarios] checkNkViolation(systemData, value(P_g), value(P_s)); iter iter 1; end这个框架下每次迭代只增加当前越限场景对应的约束直到所有候选场景都校验通过。实际测试中迭代次数通常在3到5次内收敛。7.4 步骤四结果可视化与分析求完调度结果后一定要画几张关键图光热电站出力曲线和储热状态曲线看储热过程是否符合预期。各机组出力柱状图看光热如何替代火电。关键线路负载率分布图对比加N-k约束前后的负载率变化这就是安全约束价值的直接证明。绘图代码用Matlab的plot和bar就能搞定如果想把多个子图放在一起用subplot或者tiledlayout。8. 常见问题与避坑指南这里整理几个我实际踩过的坑比看十篇论文都管用。8.1 直流潮流模型下线路潮流方向与LODF计算错误直流潮流中线路潮流有正负方向LODF的计算也和故障线路的潮流方向强相关。很多人在算LODF时忘记对故障线路位置做归一化导致约束方向错误优化结果出现理论上的“安全”实际上的“危险”。解决办法用PTDF矩阵计算LODF时务必检查矩阵维度。PTDF是(n_line, n_bus)的矩阵不是(n_bus, n_bus)搞混维度绝对是常犯的错误。8.2 储热模型导致不可行光热电站储热约束如果设置得太紧比如储热容量过小或者充放热效率过低可能导致调度模型在第一轮就无解。此时不要急着怀疑N-k约束先把光热电站单独跑一遍确认系统在没有光热电站时能收敛再叠加光热电站约束。另外储热初始值设定也很关键。如果初始储热容量设得过高模型会倾向于前期大量放热后期储热耗尽导致调度结果很不合理。建议初始化时按储热容量的一半设定并在目标函数中加一个储热终值惩罚项。8.3 N-k安全约束导致求解时间过长这个几乎是必然遇到的问题。如果你发现求解时间随k值的增加呈指数式上升不要硬扛。解决办法合理设置求解器参数比如Gurobi的MIPGap设置为0.5%或1%不要追求完全最优。使用故障场景筛选只对关键线路集合做N-2。先跑不带安全约束的基础模型把机组启停固定下来再优化带安全约束的出力这种“两步法”在工程中非常实用。8.4 118节点系统的内存爆炸118节点系统加上N-k约束后约束矩阵可能超过百万维。Matlab处理稀疏矩阵可以有效降低内存占用所以约束矩阵一定要用稀疏矩阵存储。Yalmip内部会自动处理但如果你自己写约束生成函数记得用sparse函数转换。另一个技巧是在Yalmip中可以用Constraints [Constraints, constraint_set]这种方式不断追加约束但每次追加都会复制整个约束集效率很低。建议先把所有的约束放在cell数组里最后一次性拼接constraint_list cell(N, 1); constraint_list{i} expr_i; Constraints [constraint_list{:}];这个细节在小规模系统上无所谓在118节点系统上可能是几分钟和几十分钟的差距。8.5 光热电站接入节点对N-k安全性的影响不要以为光热电站的接入节点随便选。在118节点系统上不同接入节点对安全约束的满足程度差异巨大。有的节点接入光热后由于地理位置偏僻注入功率需要通过长距离输电线路送出反而加剧了关键线路的重载。这种情况下即便光热电站本身没有碳排放但从系统安全角度看接入位置并不理想。实用建议先做一个小规模的灵敏度测试把光热电站依次放到不同候选节点记录N-k校验通过率和运行成本变化选出综合性能最好的接入点。9. 这个模型后续还能怎么扩展这个模型的框架其实很有扩展潜力。我个人觉得比较有价值的方向有这么几个一是把N-k安全约束从直流潮流扩展到交流潮流。难度会高很多因为交流潮流是非线性的安全约束要用迭代线性化或者二阶锥规划逼近。如果你用的是Mosek或Gurobi新版它们对二阶锥规划支持很好可以考虑。二是加入需求响应资源。N-k故障场景下除了发电侧再调度负荷侧可中断负荷也是一种应急手段。把需求响应纳入安全约束可以降低故障后的调峰压力也让调度结果更加灵活。三是考虑多光热电站协同。单个光热电站的储热容量有限多个光热电站分布在系统不同区域可以通过不同的储放热策略互相配合优化效果会更明显。但这种情况下N-k安全约束的故障场景组合会更加复杂。四是引入不确定性建模。光热电站的光照预测不可能完全准确储热系统可以在一定程度上对冲不确定性。用随机优化结合场景树或者用鲁棒优化描述光照预测误差区间可以进一步提升模型的现实适用性。从我个人的实际经验来说这种“常规调度模型复杂安全约束新能源接入”的组合正是当前电力系统优化领域最实用的研究框架之一。与其到处找现成的代码和模型不如自己把整个建模和求解流程完整走一遍——这个过程本身就是对你对电力系统优化调度理解的一次系统检验。
上一篇/下一篇内容由系统自动关联
返回资讯列表 →