1. 项目概述与问题背景
搞过电力系统优化调度的人应该都有同感:风光出力预测数据永远是“看起来很美”,实际运行起来总会被现实打脸。今天要聊的这个问题,针对的就是这个痛点——风光负荷不同鲁棒性对系统总成本的影响,同时把系统向上、向下备用容量约束一起纳入优化模型。这是个非常典型的两阶段鲁棒优化/单层鲁棒优化在电力调度中的应用场景。
先把这个题目拆开看。风光负荷三者的预测值都存在误差,传统确定性调度只按预测值安排出力,一旦实际值与预测值偏差较大,就可能出现功率失衡、切负荷或弃风弃光。鲁棒优化的思路是:不再假设预测值一定准确,而是假设不确定参数(风光出力、负荷)会在一个预先设定的不确定集合内波动,调度方案必须对集合内的所有可能场景都可行。这里的“不同鲁棒性”指的是不确定集合的大小或鲁棒调节参数(比如预算参数、保守度系数)的变化。当鲁棒性增强时,系统预留的备用容量增加,调度方案变得更保守,总成本大概率上升;但代价是系统应对不确定性的能力变强。这个“成本-鲁棒性”的权衡关系,就是这个项目要搞清楚的核心问题。
为什么要把向上、向下备用容量单独考虑进来?因为风光的波动是双向的:实际出力低于预测值时,系统需要向上备用来补功率缺口;实际出力高于预测值时,系统需要向下备用(或调峰能力)来消纳多余电量。只考虑单向备用在现实中是行不通的。把两个方向的备用需求都建模为与鲁棒性参数相关的约束,才能真实反映系统成本随鲁棒性变化的规律。
这个项目适合谁?正在做电力系统优化调度方向的研究生、做新能源并网规划的工程师、以及想学习如何用Matlab+YALMIP+求解器(CPLEX或Gurobi)求解鲁棒优化问题的入门者。看完这篇文章,你能得到一套可以直接跑的Matlab代码框架,以及关于鲁棒性参数选取、备用约束建模、结果分析的一整套实践经验。
2. 鲁棒优化建模思路与关键设计
2.1 不确定集合怎么选:盒式集合与预算约束
鲁棒优化的第一步是定义不确定参数的波动范围。最常见的做法是盒式不确定集合:每个不确定参数在预测值上下浮动一个区间。以光伏出力为例,如果预测值是100 MW,预测误差比例是20%,那么光伏实际出力p_pv就在[80, 120] MW之间任意取值。
只做盒式集合有个问题:它假设所有不确定参数同时达到最坏情况,这太保守了。实际中所有风电、光伏、负荷同时偏差到极值的概率非常低。所以工程上更常用的是带预算约束(budget)的不确定集合,也叫做Bertsimas-Sim方法。核心做法是限制所有不确定参数偏离预测值的总“数量”不能超过一个预算参数Γ。这样做的好处是:可以通过调节Γ连续地控制方案的保守程度,Γ=0退化为确定性调度,Γ取最大值时等价于最保守的盒式鲁棒。
在Matlab代码里,我是这样处理的:把风电、光伏、负荷的预测误差都归一到各自预测值的百分比区间,比如风电误差±20%,光伏误差±15%,负荷误差±5%,然后引入统一的预算参数Γ,它表示所有节点不确定参数偏离预测值之和的上限。实际代码中用0到N(不确定参数总数)之间的整数或连续值来扫描,观察总成本随Γ的变化曲线。
注意:Γ到底取整数还是连续值,取决于你的不确定参数建模方式。如果每个不确定参数的偏差是一个0-1乘上最大偏差的连续变量,那Γ可以是连续值;如果只允许出现/不出现偏差,Γ就是整数。两种方式YALMIP都能处理,但整数形式的求解速度更快。
2.2 目标函数:总成本都包含哪些项
系统总成本不是只算发电成本。在这个模型里,我考虑了以下四部分:
- 常规机组(火电/燃气)的发电成本,用二次函数或分段线性函数表示,工程中常用二次函数: ai * P_i^2 + bi * P_i + ci
- 常规机组的启停成本,单位时间示例中为简化可暂不考虑启停状态变量,但更完整的模型需要加入
- 系统向上备用的成本,由提供向上备用的机组按备用报价结算,可以按各自机组备用容量乘以备用价格系数计算
- 系统向下备用的成本,同理,由参与下调的机组提供
为什么备用要单独计算成本?因为备用容量占用的是机组可调容量,机组需要预留这部分能力而不能全出力发电,这相当于一种机会成本,必须体现在经济信号里。另外,向上备用的价格通常高于向下备用,因为向上备用需要机组在较低出力点运行并具备快速加出力能力,对机组性能要求更高。
对于风光和负荷侧,模型里不需要为它们单独加成本项,但通过弃风弃光约束和切负荷约束来保证不出现不合理的调度结果。如果想更精细,可以在目标函数里加入弃风弃光和切负荷的惩罚项,这样求解器在可行与不可行之间会自动权衡。本项目基础版不加惩罚项,而是严格保证所有不确定场景下都满足功率平衡和备用约束,这样鲁棒性含义更纯粹。
2.3 备用约束怎么转化为数学表达式
这是整个模型最核心也最容易写错的地方。先定义符号:
- P_g(t):常规机组g在时刻t的计划出力(决策变量)
- P_w(t)、P_pv(t)、P_L(t):风电、光伏、负荷的预测值
- ΔP_w、ΔP_pv、ΔP_L:对应的不确定偏差,属于不确定集合U
- R_up(t):系统向上备用需求(决策变量或由不确定集合推导)
- R_down(t):系统向下备用需求
功率平衡约束的原始形式是:
sum(P_g(t)) + P_w(t) + ΔP_w + P_pv(t) + ΔP_pv = P_L(t) + ΔP_L这里ΔP_w、ΔP_pv、ΔP_L是变量,这就导致约束是不确定的。鲁棒优化的核心就是求一个不依赖于Δ取值的调度方案,使上式对所有Δ∈U都成立。
把约束重新整理成最坏情况形式。对向上备用来说,最坏情况是风电、光伏出力最小(ΔP_w、ΔP_pv取下界负值),而负荷最大(ΔP_L取上界正值),此时需要的向上备用容量为:
R_up(t) = max_{Δ∈U} [ ΔP_L - ΔP_w - ΔP_pv ]对向下备用来说,最坏情况是风电、光伏出力最大、负荷最小,此时需要向下备用容量为:
R_down(t) = max_{Δ∈U} [ ΔP_w + ΔP_pv - ΔP_L ]于是机组的调节能力约束变为:
sum( P_g,max - P_g(t) ) ≥ R_up(t) // 机组上调能力 sum( P_g(t) - P_g,min ) ≥ R_down(t) // 机组下调能力这两个约束的物理含义很直观:所有常规机组剩余可加出力之和,必须能覆盖最坏情况下的功率缺口;所有常规机组剩余可减出力之和,必须能消纳最坏情况下的超额功率。
3. 不确定性与备用的耦合处理
3.1 对偶变换:把max问题变成线性约束
上面的R_up(t)和R_down(t)是max问题,不能直接嵌进线性规划里。标准的处理办法是把它转化成对偶问题,或者直接用线性规划的对偶理论。
具体来说,风电预测偏差表示为:
ΔP_w = ξ_w * ε_w * P_w,base其中ξ_w是0到1之间的不确定水平系数,ε_w是最大偏差比例。如果每个不确定参数都可以独立地在其区间内取最大偏差,那么 R_up(t) 的最大值可以通过对偶变换转化为一组线性约束。
以预算约束Γ为例,R_up(t)的max问题可以写成:
max Σ_i ( -ε_i * P_i,base * ξ_i ) // 对同向偏差符号做归一化 s.t. Σ_i ξ_i ≤ Γ 0 ≤ ξ_i ≤ 1其中i遍历风电、光伏、负荷三个不确定源。由于目标函数是线性的、约束是线性的,这个max问题本质上是一个线性规划。把它的对偶问题写出来,对偶变量是α(对应预算约束)和β_i(对应0≤ξ_i≤1),然后让原调度约束里带上这些对偶变量和α、β,就能得到一个等价的可解的线性约束系统。
这样处理的巧妙之处在于:原问题(max)的每个解都对应对偶问题的一个可行解,两者的目标函数值相等(强对偶成立,因为原问题是线性规划),所以对偶问题加进原模型后,不会改变最优解,但把不确定约束变成了确定线性约束。
在YALMPI+求解器环境下,你甚至不需要手动推导对偶形式,因为代码里通常会直接用YALMIP的鲁棒优化模块(uncertain和uncertainty声明)。但如果你想自己手推,上面这个过程就是标准推导。
3.2 为什么向上/向下备用要一起考虑
很多人做鲁棒调度时容易忽略向下备用。实际项目里遇到的情况是:光伏在午间大发时,系统净负荷可能变成负值,如果只考虑向上备用,调度方案会在光伏大发时段大量弃光,或者让常规机组压低出力,但机组有个最小技术出力,往下调的空间有限。如果这个有限的下调空间还不够用,系统就需要额外的向下备用来源,比如储能充电、可中断负荷等。
所以完整的备用约束必须同时考虑两个方向。从模型结构上看,这两个约束会共同制约机组出力区间。举个例子,某台机组容量300 MW,最小出力150 MW,如果系统需要200 MW向上备用和100 MW向下备用,这台机组的出力必须同时满足:
300 - P_g ≥ 200 → P_g ≤ 100 P_g - 150 ≥ 100 → P_g ≥ 250单个约束都成立,但组合起来显示这个机组自身无法同时满足两种备用需求。这就是为什么备用约束必须放到多机组的系统层面来看,而不是单台机组算完就完事。
从数学上看,向上备用约束和向下备用约束叠加在一起,定义了常规机组群的可行出力区间。鲁棒性增强时,两者都变紧,可行域收缩,系统不得不让更多机组在线运行或增加高成本机组出力,总成本随之上升。
3.3 鲁棒性参数的扫描策略
“不同鲁棒性”在代码里通常体现为对Γ或ε做参数扫描。我的经验是分两个维度看:
- 固定不确定区间大小(如风电±20%),扫描预算参数Γ从0到最大值的等间距点,观察总成本和备用需求的变化曲线。这回答的是“系统对同时发生多个偏差的容忍度成本”。
- 固定Γ,扫描不确定区间宽度(如风电误差从±5%到±30%),观察系统总成本的变化。这回答的是“预测精度提高能带来多少经济效益”。
两种扫描都很实用,论文里也常用。实际计算时为节省时间,可以用50个或100个扫描点,线性插值画出平滑曲线。Matlab里用for循环包住求解过程就行,每个扫描点单独调一次求解器,求解完成把结果存到数组里,最后统一画图。
4. Matlab代码实现与核心环节
4.1 系统数据设计
我在做这个项目时,参考了标准IEEE 6节点/30节点系统的数据风格,搭建了一个含6台常规机组、1个风电场、1个光伏电站和一组负荷节点的简化系统。为了让你能快速上手,下面给出一个可直接用于小规模测试的数据设计表:
| 机组编号 | 容量(MW) | 最小出力(MW) | 煤耗系数a(元/MW²h) | 煤耗系数b(元/MWh) | 煤耗系数c(元/h) | 上调备用价格(元/MWh) | 下调备用价格(元/MWh) |
|---|---|---|---|---|---|---|---|
| G1 | 300 | 80 | 0.0015 | 35 | 600 | 50 | 30 |
| G2 | 200 | 60 | 0.0020 | 38 | 450 | 55 | 32 |
| G3 | 200 | 50 | 0.0025 | 42 | 400 | 55 | 32 |
| G4 | 150 | 40 | 0.0030 | 45 | 350 | 60 | 35 |
| G5 | 150 | 30 | 0.0035 | 50 | 300 | 60 | 35 |
| G6 | 100 | 20 | 0.0040 | 55 | 250 | 65 | 38 |
风电场装机200 MW,预测出力按日曲线变化,最大预测误差设为±20%。光伏电站装机150 MW,预测出力按日曲线变化,最大预测误差设为±15%。负荷侧峰值1500 MW,预测误差设为±5%。
这一组数据不是实际电网数据,但用来做机理研究和算法演示足够了。你在用自己的数据时,只需要替换矩阵里的数值,代码框架不需要大改。
4.2 求解器与YALMIP设置
代码实现用的是YALMIP加外部求解器。求解器选CPLEX或Gurobi都可以,我在本机实测两者都能快速求解这个规模的优化问题(6台机组、24时段,几百个变量和约束,秒级求解)。
安装YALMIP很简单,去GitHub Releases下载,把文件夹放到Matlab路径下,然后addpath(genpath('yalmip路径'))。求解器需要单独安装:CPLEX或Gurobi都提供免费学术授权。
YALMIP里声明决策变量的方式如下:
P = sdpvar(6, 24, 'full'); % 6台机组24时段出力 R_up = sdpvar(6, 24, 'full'); % 向上备用 R_down = sdpvar(6, 24, 'full'); % 向下备用目标函数用sum和quad2lin处理二次成本函数,或者直接使用二次目标函数让求解器自行处理如果用的是Gurobi/CPLEX,都能直接求解二次规划。为了线性化方便,也可以把二次成本分段线性化。我的建议是:先用二次函数跑通,再去尝试分段线性化,两个结果应该非常接近。
4.3 核心约束代码对照
下面是一段简化但可运行的核心约束伪代码,展示功率平衡、向上/向下备用约束的实现思路:
% 不确定参数最大值修正 for t = 1:24 % 功率平衡约束(不确定形式,用鲁棒对偶后的确定等价形式) Constraints = [Constraints, sum(P(:,t)) + P_w_base(t) + P_pv_base(t) == P_L_base(t)]; % 向上备用需求不等式(最坏情况:风光低、负荷高) % 这里用R_up_total表示所有机组提供的向上备用总和 Constraints = [Constraints, sum(R_up(:,t)) >= Gamma_scale * (delta_PL_max(t) + delta_Pw_max(t) + delta_Ppv_max(t))]; % 机组出力与备用能力约束 Constraints = [Constraints, P(:,t) + R_up(:,t) <= P_max(:)]; Constraints = [Constraints, P(:,t) - R_down(:,t) >= P_min(:)]; % 向下备用需求不等式(最坏情况:风光高、负荷低) Constraints = [Constraints, sum(R_down(:,t)) >= Gamma_scale * (delta_Pw_max(t) + delta_Ppv_max(t) + delta_PL_min(t))]; end上面代码中Gamma_scale是归一化后的鲁棒性参数,取值0到1之间。delta_Pw_max(t)表示t时刻风电预测偏差的最大值,用预测值乘以误差比例得到。这样写虽然不完全等同于Bertsimas-Sim的标准形式(那个需要引入对偶变量),但在不确定参数只有三个源、且你主要想观察趋势的场景下,这个简化形式已经够用且非常直观。
如果你要严格推导预算约束下的对偶形式,推荐参考Bertsimas-Sim 2004年的论文,代码实现上可以用YALMIP的uncertain接口,但新手容易踩坑,建议先跑通简化版再升级。
4.4 结果展示与成本曲线绘制
求解完成后的标准画图动作包括:
- 常规机组各时段最优出力堆叠图(面积图)
- 系统总成本随鲁棒性参数变化曲线
- 向上/向下备用需求随鲁棒性参数变化曲线
- 不同鲁棒性场景下的功率平衡校验图(验证方案在最坏偏差下是否依然可行)
画图用Matlab基础函数plot和area就行,注意设置FontSize和LineWidth,保证放到论文里清晰可读。以下是一个画成本曲线的示例:
Gamma_range = 0:0.1:1; cost_list = zeros(size(Gamma_range)); for k = 1:length(Gamma_range) % 在这里设置当前Gamma并求解 % ... cost_list(k) = value(Objective); end figure; plot(Gamma_range, cost_list, '-o', 'LineWidth', 1.8); xlabel('鲁棒性参数 \Gamma'); ylabel('系统总成本(元)'); grid on;5. 实验结果与分析要点
以我搭建的6机系统为例,跑出来的典型结果是:总成本随着鲁棒性参数Γ从0到1增大而单调上升,但曲线形态往往是先缓后陡。Γ从0到0.4时成本上升约3%-5%,从0.4到1时成本上升幅度明显加大,可能到15%以上。原因是:低鲁棒性时,备用需求增量主要由效率较高、成本较低的机组承担,边际成本上升不明显;高鲁棒性时,低成本机组的备用能力被用尽,必须调用高成本机组或增加高成本机组出力,边际成本迅速上升。
向上备用和向下备用的变化规律不完全同步。风资源好的夜间时段,向下备用需求增幅更大;光伏大发的中午时段,向下备用需求也显著增加。这说明鲁棒性对两个方向备用需求的影响存在时段差异,在分析结果时要把时段因素考虑进去,不能只给一个全天平均值。
另一个值得注意的现象:随着鲁棒性增强,某些时段的机组组合会发生质变。比如从只开4台机组变成必须开5台甚至6台机组,导致固定成本跳升,总成本曲线出现不连续的跳跃点。这种跳跃在真实系统里很常见,论文呈现时不需要刻意平滑。
如果画功率平衡校验图,你会发现在最坏偏差场景下(风光取最低、负荷取最高),常规机组出力加上备用总量刚好等于负荷上限,说明模型正确保证了鲁棒可行性。这是判断你的模型写对没写对的标志性检验。
6. 常见问题与排查技巧实录
6.1 求解器报“Infeasible Problem”怎么处理
这个项目最容易遇到的坑就是模型不可行。我调试时遇到过的情形和解决办法:
- 电源总容量不足以覆盖最大负荷加上向上备用需求。检查发电机总容量与负荷峰值的关系,至少要留出备用裕度。如果不可行,把某些机组最小出力调低或增加机组数量。
- 向下备用约束过紧导致机组出力区间冲突。典型场景是光伏大发时段,系统要求大量向下备用,但所有机组出力已经压到最低出力附近,没有下调空间了。解决办法是允许弃风弃光或者增加储能装置。
- 鲁棒性参数取到最大值时任何方案都不可行。这是正常现象,说明在给定不确定集合最大值下系统无法保证所有场景可行。分析时把Γ限制在可解区间内即可,不必强求覆盖整个0-1区间。
排查顺序建议是:先把Γ设为0跑确定性调度,确认模型基础没问题;再逐步增大Γ,观察哪个约束最先被触发,把对应的约束松弛掉或用对偶形式替换。
6.2 YALMIP报错“No suitable solver”
出现这个错误通常是因为没装求解器,或者YALMIP没找到求解器路径。检查方法是在Matlab里运行yalmiptest,它会列出所有已安装求解器的状态。我实际用过CPLEX 12.10、Gurobi 9.5和Gurobi 10.0,配合YALMIP R2021版本都能正常用。
如果你的问题里有二次目标函数,确保求解器支持二次规划。Gurobi和CPLEX都支持,但如果用的是免费的linprog(只支持线性规划),就需要先把二次成本分段线性化。具体做法是把机组出力区间切成几段,每段内成本按线性函数近似,再引入0-1变量选择段。切5段左右精度就很高了,不用太贪多。
6.3 结果不随鲁棒性变化或变化异常
大多数情况是因为鲁棒性参数没有真正进入约束。比如代码里用了全局变量但求解循环里忘了更新,或者对偶变换后有简化处理导致约束变成了定值。调试方法很简单:把不同Γ值对应的备用需求值打印出来,如果完全一致,说明模型里鲁棒性参数和备用约束之间的连接断开。
还有一种可能是误差分配方式不对。如果所有误差都归到一个“总偏差”变量上,Γ的变化会被线性稀释,导致结果变化非常平缓。正确的做法是每个不确定源有独立的偏差变量,Γ通过预算约束来限制偏差的总量。
6.4 求解时间过长怎么办
6机24时段这个规模,正常求解时间应该在几秒到几十秒之间。如果求解时间超过几分钟,大概率是二元变量太多,或者约束写得不够紧凑。优化建议:
- 机组组合(启停)变量是主要耗时点,如果只是研究备用与鲁棒性关系,可以先把启停变量固定为确定性调度结果,只优化出力与备用,这能大幅加速。
- 用
sdpvar声明变量时指定维度,不要用动态扩展(比如循环里不停把变量拼接进约束数组),预先分配好约束数组能提速不少。 - 如果用了YALMIP的
uncertain接口,可以手动写出对偶等价形式替代,手工对偶虽然推导繁琐,但求解器处理起来更高效。
7. 扩展方向与实际应用建议
这个基础模型本身就能回答很多工程问题。比如,当风电预测误差从20%降到10%时,系统总成本能节省多少?这个“预测精度的经济价值”是新能源并网评估里很有说服力的数据。再比如,增加储能系统后,相同鲁棒性水平下系统总成本能降低多少?这些扩展只需要调整约束和决策变量,代码框架不用推翻重来。
如果你是做毕业设计或者发小论文,建议在这个框架上叠加更复杂的场景:多时段耦合的机组爬坡约束、储能充放电模型、需求响应资源、市场出清机制。这些扩展会显著增加模型的现实意义。
我个人在实际操作中的一个习惯是:每次调整模型后都保留一个“验证场景”——固定Γ=0.3,把最坏偏差场景的功率平衡情况打印出来,确认所有等式约束在误差范围内成立。这个习惯帮我避免了很多因为约束写错而导致的隐性错误,也能在答辩或汇报时作为模型正确性的有力佐证。
最后再分享一个小技巧:跑完扫描后,把每个Γ值下各时段的备用需求和机组出力存成Excel表格。后面写报告或做敏感性分析时,不需要重新求解,直接用表格数据就能做二次分析,省时省力。