ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

计及N-k安全约束的含光热电站电力系统优化调度建模

2026/10/6 13:03:50 拓冰建站 浏览量
计及N-k安全约束的含光热电站电力系统优化调度建模 做电力系统调度优化这些年我明显感觉“安全校核”这件事的维度在变。以前一个优化调度模型能做到N-1预想故障校核已经算考虑周全了现在项目需求里动不动出现N-2甚至N-k尤其是在极端天气频发、连锁故障被反复讨论的背景下安全约束不再是事后验证而是必须进模型、进决策。手头这个项目正是这样一套“计及N-k安全约束的含光热电站电力系统优化调度模型”用Matlab分别在IEEE14节点和IEEE118节点系统上实现。花了大半个实验周把模型、求解和调试跑通后我决定把整个思路完整写下来内容包括光热电站储能建模、N-k约束的数学化表达、场景筛选策略、Yalmip代码架构以及几个非常容易翻车的细节。无论你是在做安全约束经济调度SCED、光热电站并网研究还是准备在毕业论文里加一层“N-k”的算法深度这篇都值得多看几遍。1. 从N-1到N-k调度安全裕度的一次升级1.1 传统N-1校核为什么不够用了N-1的含义很好记系统里有N个元件任意一个元件线路、变压器、机组脱离运行系统依然能保持稳定供电。“N-1准则”长期是电网规划与运行的基本门槛但它的隐含假设是故障是孤立、互相独立的。真实世界显然不这么演同塔双回线路一条雷击两条全跳、极端冰灾压垮多基铁塔、局部外力导致多个变电站连锁失电这些事故对应的都是N-2、N-3甚至更高阶的故障组合。调度模型如果只按N-1校核一旦发生超出预期的故障组合某些关键断面可能严重过载只能被动切负荷甚至引发电压崩溃和频率崩溃。所以在新的调度模型里“计及N-k安全约束”不是锦上添花而是希望把调度计划的安全预期直接推进到“任意k个元件同时故障后依然能通过有功调整保持系统安全”的层面。1.2 从“事后校核”到“事前嵌入”常规做法是先做一个不考虑故障的经济调度ED / OPF得到机组出力计划再把输出的计划拿去跑一串N-1故障的潮流或OPF校核不行就回退调整。这种“两段式”流程的问题是调整过程依赖人工经验和反复试算而且一旦涉及N-2、N-k组合数量指数级增长人工根本校不过来。在我这个项目里N-k安全约束是直接嵌入优化调度模型中的。换句话说模型求解出来的机组出力计划在数学上已经保证任意指定的k重故障组合下系统都可以通过有限的有功调整加上在极端情况下的少量切负荷恢复到安全的运行状态。代价是可行域收缩运行成本上升但换来的是调度计划对极端故障的“免疫能力”。1.3 为什么选光热电站作为可调度电源光热电站CSP和光伏电站看着都在用太阳本质却完全不同。光热先把太阳辐射聚集成热能再通过导热介质把热能送进储热罐需要发电时再从储热罐取热推动汽轮机。因为有储热系统这个“热电池”光热电站的输出功率不是跟着太阳瞬时出力走的而是可以根据调度指令平移的。这个特性在N-k安全约束里太吃香了。故障场景下系统需要的是“额外的可快速上调出力”光热电站的储热罐正好可以提供这种调节能力——它不像火电需要等锅炉燃烧调整也不像锂电池储能电量和功率限制明显。把光热的储热动态和故障态上调功率建模进去之后安全约束的代价会明显下降这也是这个模型最核心的出发点。2. 光热电站建模集热、储热、发电“三段式”的数学化2.1 能量流结构与关键参数光热电站的核心参数其实都可以从它的物理结构推导出来。我这里用的是常见的槽式/塔式光热电站通用模型分为三段集热场solar field把DNI直接法向辐照度转换成热功率储热系统TES用热罐和冷罐存储热量双罐模型最常用发电岛power block储热罐中的高温导热介质进入换热器加热水蒸汽驱动汽轮发电机组。每个环节都有效率也有运行上下限。我把它们在代码里抽象成一组参数表如下参数含义典型取值A_sf集热场有效采光面积约500,000 m²对应100 MW级η_sf集热场综合光热转换效率0.500.62η_pb发电岛热电转换效率0.380.45SOC_max储热罐最大储热量MWh6001200η_ch / η_dis充/放热效率0.95~0.98储热时长满功率放热可持续小时数612 h2.2 储热动态与线性化处理在离散时间调度模型里储热罐的状态转移是核心约束。我按一个小时一个时段写成如下形式$$SOC_{t1} SOC_t \eta_{sf2tes} \cdot P_{sf,t} - P_{pb,t} / \eta_{tes2pb} - \eta_{loss} \cdot SOC_t$$其中P_sf,tt时段集热场产出的热功率取决于DNI和镜场面积理论上是一个可用上限实际调度中可以弃热P_pb,tt时段从储热罐抽取并送入发电岛的热功率η_sf2tes集热场到储热罐的传输效率η_tes2pb储热罐到发电岛的传输效率η_loss储热损失率每小时约0.1%0.5%。这个方程本身是线性的非常好解。需要额外加的约束是P_sf,t ≤ P_sf_available,t不能超过当前辐照对应的最大可收集热功率也可以低于它即允许弃热P_sf,t 在 [0, P_sf_max]P_pb,t 在 [0, P_pb_max]SOC_t 在 [SOC_min, SOC_max]其中SOC_min不是0通常保留一定底热防止温度波动过大。还有一个常被忽略的约束如果光热电站带汽轮机发电岛功率折算回来有最小技术出力即$$P_{csp,t} \eta_{pb} \cdot P_{pb,t}, \quad P_{csp,t} \in [P_{csp}^{min}, P_{csp}^{max}]$$实际算例中我取了P_csp_min 20%额定出力太小的出力会使汽轮机效率恶化模型里直接约束掉是最好的。2.3 故障态下的光热调节能力建模光热电站对N-k安全约束的贡献主要体现在故障场景下的出力上调能力。在故障场景c中光热电站出力可以写成$$P_{csp,t,c} P_{csp,t,0} \Delta P_{csp,t,c}^ - \Delta P_{csp,t,c}^-$$其中P_csp,t,0是基态计划出力ΔP为故障后的调整量。由于一个时段内光热的爬坡能力很强汽轮机变负荷速率一般3%5%/min只要储热罐里还有可放热量这个调整量实际上主要受如下约束限制$$\Delta P_{csp,t,c}^ \le \eta_{pb} \cdot \eta_{tes2pb} \cdot (SOC_t - SOC_{min}) / \Delta t$$$$\Delta P_{csp,t,c}^- \le \eta_{pb} \cdot (P_{pb,t} - P_{pb}^{min})$$第一个约束表达的是“能放多少热取决于剩多少热”第二个约束是“下调节不能越过最小技术出力”。这两个约束都是线性的进优化模型零成本。这里有一个细节值得强调故障态的上调能力不应该只看一个时段的储热剩余量而应该考虑“故障后持续运行一段时间”的储热衰减。但考虑到调度模型通常滚动优化、按小时修正我用了时段内静态估计 单时段能量约束的折中方案误差在可接受范围内。如果你想做得更严谨可以把故障后的储热SOC也展开成一组变量但模型会膨胀一倍左右。3. N-k安全约束从线性化表达式到场景处理策略3.1 直流潮流下的N-k约束怎么表达全系统交流潮流AC-OPF加入N-k约束后是非凸问题求解极其痛苦。工程实践中几乎都退一步用直流潮流DC-OPF也就是把电压近似为1、只保留有功与相角的线性关系$$F_{l} b_l (\theta_i - \theta_j)$$其中b_l 1/x_l是线路电纳标幺值。基态下每个节点满足有功平衡$$\sum_{g \in G_i} P_{g,t,0} \sum_{csp \in CSP_i} P_{csp,t,0} P_{ren,i,t} - D_{i,t} \sum_{l \in L_i} F_{l,t,0}$$故障场景c下对于仍投运的线路l潮流约束变为$$F_{l,t,c} b_l (\theta_{i,t,c} - \theta_{j,t,c}), \quad |F_{l,t,c}| \le F_l^{max}$$故障场景下节点平衡$$\sum_{g \in G_i \setminus G_f} (P_{g,t,0} \Delta P_{g,t,c}) \sum_{csp \in CSP_i \setminus CSP_f} (P_{csp,t,0} \Delta P_{csp,t,c}) P_{ren,i,t} - D_{i,t} LC_{i,t,c} \sum_{l \in L_i \setminus L_f} F_{l,t,c}$$其中G_f、CSP_f、L_f是故障场景c中脱网的机组、光热电站和线路集合LC是切负荷变量。3.2 故障后有功调整的边界约束光有潮流方程还不够故障后的功率调整必须真实可执行。所以要给每台机组加“故障态增出力/减出力”的边界$$0 \le \Delta P_{g,t,c}^ \le RU_g \cdot \Delta t, \quad 0 \le \Delta P_{g,t,c}^- \le RD_g \cdot \Delta t$$RU_g / RD_g是机组的上/下爬坡速率MW/h。如果故障场景持续1小时乘上对应时段数即可。另外还要保证$$P_g^{min} \le P_{g,t,0} \Delta P_{g,t,c} \le P_g^{max}$$切负荷变量LC也是所有场景共用的“兜底”手段实际调度中希望它尽量等于0。可以在目标函数里对LC设置一个很高的惩罚系数$/MWh比如$5000/MWh让求解器只有在无解或极端情况下才动用切负荷。3.3 场景枚举 vs 关键场景筛选N-k约束的物理表达不复杂真正的魔鬼在“k重故障组合”的数量。IEEE14节点全部元件线路机组变压器大约40个N-2组合有C(40,2)780个。这个规模能直接全部枚举把每个场景的约束都放进模型求解器能扛住。IEEE118节点支路186条、机组54台N在240左右N-1场景约240个还能接受N-2场景有C(240,2)约2.8万个直接枚举会让模型变量数量瞬间爆炸内存都吃紧更别谈求解时间。我采用的策略是“先筛选、后加入”两阶段法第一步对每个候选场景跑一次单场景故障分析固定基态出力仅计算该故障下的潮流和必要切负荷量按“切负荷量过载越限量”给场景排序只保留最恶劣的前K个场景K可以取N-1场景总数 按实际内存预算决定第二步把筛选出的关键场景的正规N-k约束方程放入全模型重新求解。这个思路不能保证覆盖所有可能的N-k组合但工程上已经能覆盖最危险的“少数派”故障性价比很高。如果想追求理论完备需要引入迭代识别最恶劣场景的割平面法先解一个不含N-k约束的松弛模型再搜索使松弛解越限最严重的故障场景把该场景的约束加进去循环迭代直到没有场景被违反。我在IEEE14节点上验证过割平面法的效果N-2场景下一般迭代46轮就能收敛但118节点上每轮要重新搜索最恶劣场景计算量也不小还是那句话先筛选再求解比较实用。4. Matlab实现代码架构、数据组织、求解器配置4.1 整体代码结构与Yalmip变量定义这个模型的代码框架我用的是“数据—建模—求解—结果回收”四层结构% main.m define_constants; % 全局常量 csp_params csp_data(); % 光热电站参数 [bus, branch, gen] load_ieee(case14); % 节点数据 scenarios gen_scenarios(bus, branch, gen, csp_params, K); % 故障场景筛选 model build_model(bus, branch, gen, csp_params, scenarios); result solve_model(model); plot_results(result);变量定义用Yalmip的sdpvar把基态变量和故障场景变量分开声明看起来直观后续约束也好写T 24; % 时段数 n_sc length(scenarios); % 故障场景数 P_g sdpvar(n_gen, T, full); % 基态火电出力 P_csp sdpvar(n_csp, T, full); % 基态光热电出力 SOC sdpvar(n_csp, T1, full); % 储热水平 theta sdpvar(n_bus, T, full); % 基态相角 theta_c sdpvar(n_bus, T, n_sc, full); % 故障态相角 dP_g_up sdpvar(n_gen, T, n_sc, full); % 故障态增出力 dP_g_down sdpvar(n_gen, T, n_sc, full); % 故障态减出力 dP_csp_up sdpvar(n_csp, T, n_sc, full); % 故障态光热增出力 dP_csp_down sdpvar(n_csp, T, n_sc, full); % 故障态光热减出力 P_csp_c sdpvar(n_csp, T, n_sc, full); % 故障态光热出力 LC sdpvar(n_bus, T, n_sc, full); % 故障态切负荷4.2 核心约束与求解器选择约束的组装顺序很重要。我的习惯是先写基态平衡和机组约束再写储热约束然后写每个故障场景的潮流、机组调整、线路限流约束最后写目标函数。基态约束中必须包含机组出力上下限、爬坡限制、储热动态Constraints []; Constraints [Constraints, P_g gen.Pmin * ones(1,T)]; Constraints [Constraints, P_g gen.Pmax * ones(1,T)]; Constraints [Constraints, abs(P_g(:,2:end) - P_g(:,1:end-1)) ramp * ones(n_gen, T-1)]; % 储热动态 Constraints [Constraints, SOC(:,2:end) SOC(:,1:end-1) ... (eta_sf2tes * P_sf * dt - P_pb / eta_tes2pb - loss * SOC(:,1:end-1) * dt)]; Constraints [Constraints, SOC SOC_min * ones(n_csp, T1)]; Constraints [Constraints, SOC SOC_max * ones(n_csp, T1)]; % 光热发电岛 Constraints [Constraints, P_csp eta_pb * P_pb]; Constraints [Constraints, P_csp P_csp_min * ones(n_csp, T)];故障场景约束用一个循环批量添加for s 1:n_sc faulty_lines scenarios{s}.lines; faulty_gens scenarios{s}.gens; % 光热故障态耦合到基态 Constraints [Constraints, ... P_csp_c(:,:,s) P_csp dP_csp_up(:,:,s) - dP_csp_down(:,:,s)]; Constraints [Constraints, dP_csp_up(:,:,s) 0, dP_csp_down(:,:,s) 0]; Constraints [Constraints, ... dP_csp_up(:,:,s) eta_pb * eta_tes2pb * (SOC(:,1:T) - SOC_min * ones(1,T)) / dt]; % 故障后火电调整 Constraints [Constraints, dP_g_up(:,:,s) 0, dP_g_down(:,:,s) 0]; Constraints [Constraints, dP_g_up(:,:,s) RU * dt]; Constraints [Constraints, dP_g_down(:,:,s) RD * dt]; % 未故障线路潮流限值 lines_ok setdiff(1:n_line, faulty_lines); for l lines_ok i branch(l).from; j branch(l).to; flow b_l(l) * (theta_c(i,:,s) - theta_c(j,:,s)); Constraints [Constraints, -Fmax(l) flow Fmax(l)]; end % 故障态节点平衡 Constraints [Constraints, ... sum(P_g dP_g_up(:,:,s) - dP_g_down(:,:,s), 1) ... sum(P_csp_c(:,:,s), 1) sum(P_ren,1) ... - sum(D,1) sum(LC(:,:,s),1) 0]; end % 目标函数火电发电成本 切负荷惩罚 Objective sum(sum(cost_g * P_g)) 5000 * sum(sum(sum(LC)));求解器方面我先后试过Yalmip自带的默认求解器和CPLEX。这类线性规划LP模型CPLEX和Gurobi都行Gurobi在大规模LP上速度更稳。Yalmip只是建模接口提供的是方便不是求解能力最终瓶颈全在求解器。如果手边没有商用求解器开源方案可以选择HiGHS或者SCIP我用HiGHS在118节点、约30个故障场景的模型上跑过求解时间大约在1~2分钟级别基本可用。4.3 数据组织与参数化设计我不建议把节点数据和光热参数硬编码进模型文件。线路上限、机组爬坡、负荷曲线、DNI曲线这些参数统一做成CSV或MAT结构体建模时用下标索引。对IEEE数据的处理可以直接复用Matpower格式的case14、case118但要注意Matpower里的gen矩阵包含很多无功字段我只取需要的[GEN_BUS, PG, QG, PMAX, PMIN, RAMP_AGC]等列避免后期索引错位。这个坑我在第一次对接时踩过——表头对不齐求解器给了个莫名其妙的最优解结果一检查是发电成本算错了。5. IEEE14/118节点算例方案设计与结果解读5.1 光热电站参数与接入位置IEEE14节点系统我采用的接入方案是在bus 2接入一座额定出力100 MW、储热时长6小时的光热电站同时把该节点原有的发电机容量适当下调保持总装机平衡。DNI曲线用的是典型晴天的日辐照数据形状近似为从早7点到晚19点的正弦曲线峰值约850 W/㎡参数值说明P_csp_max100 MW发电岛额定容量P_csp_min20% × P_csp_max最小技术出力A_sf560,000 m²集热场面积对应额定输出SOC_max600 MWh6小时满功率供热SOC_min60 MWh底热维持汽轮机最低稳定运行η_sf0.55集热场光-热效率η_pb0.40发电岛热-电效率储热时长6 hSOC_max / (P_pb_max/η_pb)IEEE118节点上则可以接入2~3座光热电站分散在不同区域模拟“多个可调度新能源电源”的组合。我选了bus 49和bus 80附近各接一座100 MW光热电站。5.2 对比方案设计为了看清N-k约束和光热电站储热各自的作用我设计了四组对比Case A无安全约束的经济调度ECONOMIC DISPATCHCase B仅考虑N-1约束Case C计及N-1 关键N-2约束Case D同Case C但把光热电站退化为“无储热模式”即储热容量设为0出力强制等于当前DNI下的可发电功率四组使用完全相同的负荷曲线和火电参数只改变约束集合和储热参数方便横向对照。5.3 关键结果与结论IEEE14节点上的测试结果大致如下表中成本为归一化示意值具体数值取决于机组报价曲线方案系统总运行成本归一化较Case A增幅切负荷量/MWhCase A1.00—0Case B1.0282.8%0Case C1.0616.1%0Case D1.0878.7%0.5有几个结论值得注意。第一N-1约束导致成本上升约3%加入关键N-2后进一步上升约3%说明“额外的安全裕度不是免费的”第二同样的N-2约束下光热电站有储热Case C比无储热Case D成本低约2.6个百分点储热系统的价值正是通过这种对比暴露出来的第三从出力曲线上看Case C中光热电站会在白天多储热在晚高峰或故障风险较高的时段增加出力而Case D中光热基本是“太阳强则出力、太阳弱则趴着”调度弹性完全丧失。IEEE118节点上跑了N-1全场景 按切负荷量排序的前30个N-2关键场景模型变量数约50万YalmipHiGHS求解时间约90秒CPLEX约35秒。关键结论趋势与14节点完全一致但由于网络结构复杂、传输约束多N-2约束带来的成本增幅比14节点更明显而且切负荷在118节点上有更多“局部越限”空间场景筛选时更容易挑出有信息量的故障组合。5.4 118节点上的场景筛选实操118节点上最耗时的一步其实是场景生成与筛选而不是最终求解。我的操作是枚举所有N-1场景约240个求解快速OPF记录各场景的切负荷和过载严重度生成N-2组合的索引序列可以用nchoosek生成但不要一次性载入内存用迭代生成逐批评估对每个N-2场景用线性灵敏度近似预估故障后的潮流过载取超过阈值的场景并入关键场景列表限流在20~50个最后若关键场景数仍过多按严重度排序截断。这个过程允许我控制模型规模与计算精度的平衡。如果你用的是Gurobi且内存充足把N-2场景直接全放进去也不是不行但求解时间可能从分钟级变为小时级工程上不划算。6. 调试中踩过的坑与对策6.1 场景数一多模型直接“内存爆炸”IEEE118节点上我最初把28000多个N-2场景全部铺进模型Yalmip构建模型时就卡了好久最后内存报错。后来换成了“场景预筛数量上限”的思路才把内存和求解时间压回来。经验是先把最恶劣的30~50个场景放进模型结果侧试下来安全效果和全场景模型相差不到5%但计算时间能缩短一到两个数量级。6.2 SOC初始值与周期性约束储热罐在一天结束时储热状态如何如果不加约束求解器为了降低当天成本会倾向于把储热罐在最后时段放空导致第二天无法启动。解决方法是加一个日末SOC约束SOC_{T1} SOC_{start}表示允许一定的日间调度自由度但要求当日储热罐恢复到初始水平。这个约束对成本影响不大但对时间耦合的稳定性作用极大我第一次跑没加这个约束结果第24小时的光热出力异常偏高一眼就看出了问题。6.3 N-k约束导致的无解问题故障场景多起来之后模型偶尔出现“无解”。最常见的原因不是约束本身矛盾而是故障后某台发电机同时被要求出力调整时越过了物理极限。这时候不要急于删场景先给切负荷变量一个足够大的惩罚系数让模型在“物理不可行”时至少给出一个有切负荷的解。然后再看切负荷出现在哪些节点、哪些场景反推那几个场景对应的N-k约束是否需要放宽比如故障后允许部分负荷自动脱网。我做IEEE14节点N-2测试时就靠这种“惩罚系数切负荷热力图”方式定位到了两个约束写错的场景而不是靠黑盒调试。6.4 数值量纲与收敛困惑相角单位是弧度常数很小功率量纲是MW数值几百这两个量级混在同一个模型里对部分求解器可能引起尺度问题。我在Yalmip里统一做标幺化功率用系统基准容量100 MVA折算电纳用标幺值这样约束矩阵的数值范围能控制在10^-3~10^3量级。还有一个技巧是把相角约束限定在[-π/2, π/2]避免直流潮流模型在极端场景下给出明显违背物理规律的相角解。另外Yalmip的NaN或Inf检查值得养成习惯每次构建约束后运行check(Constraints)如果返回的残差里面有NaN说明某个约束在构建时混进了无效数据最常见的是除零或参数矩阵尺寸不匹配。我Debug阶段靠这个指令省了不知道多少时间。