做优化调度这块的同行,应该都有这种感觉:风光出力随机性怎么处理,直接决定你论文的工作量和审稿人的态度。确定性模型好写,但审稿人一句“未考虑不确定性”就能让工作大打折扣。鲁棒优化呢,概念清楚,实现也不复杂,但真正落地的时候,那个“鲁棒性水平”到底取多少,对系统总成本影响有多大,很多文章一笔带过。今天这篇,就把风光负荷不同鲁棒性水平对系统总成本的影响这件事,从头到尾捋一遍,重点说清楚考虑上下备用容量时,那个“鲁棒性”是怎么通过Matlab代码变成实实在在的成本数字的。
这篇内容适合正在做电力系统经济调度、机组组合、新能源消纳研究的硕博生,也适合刚接触鲁棒优化、想在Matlab里快速跑通一个算例的工程师。我会把思路、模型、代码结构、参数整定和踩坑经验都放出来,你拿到之后可以直接改数据跑自己的场景。
1. 内容整体设计与思路拆解
1.1 为什么是“鲁棒性”而不是“随机规划”
处理风光出力的不确定性,主流路线无非三条:随机规划、机会约束、鲁棒优化。随机规划需要知道概率分布,而且场景多了计算量暴涨;机会约束本质是概率意义上的满足,但分布未知时容易失真;鲁棒优化不依赖精确分布,只关心不确定量的波动区间,只要实际值落在区间内,解就一定可行。
放在“上下备用容量”这个场景里,鲁棒优化的优势非常明显。备用容量的本质是应对预测误差,而鲁棒优化恰好是“针对最坏情况做决策”,跟备用的逻辑天然契合。更实际的好处是,Matlab里用YALMIP工具箱建模,鲁棒对等转换写起来非常顺手,不需要自己推导复杂的对偶问题——这一点对新手尤其友好。
1.2 “上下备用”在这个模型里到底指什么
很多刚接触这个方向的同学会把“备用容量”理解成一个固定的数,比如“系统需要预留100MW备用”。但在鲁棒优化框架里,上下备用容量是决策变量,不是常数。它要回答的问题是:当风光实际出力在预测值附近波动、负荷也在波动时,系统最少需要额外预留多少上调节能力(应对出力不足或负荷突增)和下调节能力(应对出力过剩或负荷突降),才能保证不切负荷、不弃风弃光。
这就引出了“不同鲁棒性水平”的设定。鲁棒性水平通常用不确定预算Γ来控制,Γ=0对应确定性模型,Γ取最大值对应最保守的盒式鲁棒模型。中间的Γ值允许一定数量的风光场站或负荷节点同时达到最坏偏差,这样既不至于太保守,又能覆盖大部分实际风险。
1.3 这个研究的核心价值:成本与风险的权衡曲线
把不同Γ值对应的系统总成本画出来,你会得到一条单调递增的曲线。Γ越大,系统预留的上下备用越多,总成本越高,但失负荷风险越小。这条权衡曲线就是这类研究的核心产出——它告诉你,每多付出一分钱成本,能换来多少风险下降,从而给调度员提供一个量化决策依据。
我在实际跑这个模型时最大的体会是:不要只跑Γ=0和Γ=最大值两个极端点。极端点之间的曲线形态往往不是线性的,前几个单位Γ带来的成本增量很大,后面反而变缓。这个拐点位置,才是工程上最值得关注的“性价比最优”区间。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 数学模型构建:从目标函数到鲁棒对等转换
2.1 目标函数与约束的完整列式
标准的经济调度目标函数是燃料成本最小化,二次函数形式:
matlab复制% 目标函数:火电机组燃料成本 + 备用容量成本
Cost = sum(sum(repmat(a', T, 1) .* (Pg.^2) + repmat(b', T, 1) .* Pg + repmat(c', T, 1))) ...
+ lambda_up * sum(sum(Ru)) + lambda_down * sum(sum(Rd));
这里的a、b、c是火电机组成本系数,Pg是机组出力决策变量,Ru、Rd是上下备用容量,lambda_up、lambda_down是备用容量的单位价格。注意备用成本是“容量成本”,不是“电量成本”,因为备用本质上是你买一个“期权”,不管用不用都要付钱。
约束条件这块,最核心的是功率平衡约束。在鲁棒框架下,这个等式约束必须对不确定区间内所有可能的风光出力、负荷值都成立:
matlab复制% 功率平衡约束(鲁棒版本)
% sum(Pg(t,:)) + sum(Pw(t,:)) + sum(Pv(t,:)) = PL(t) + sum(Ru(t,:))考虑的是最坏情况
% sum(Pg(t,:)) + sum(Pw_min) + sum(Pv_min) = PL_max + ...
这里有个非常关键的建模细节:等式约束里有不确定量,怎么处理?标准做法是把等式约束拆成两个不等式约束,然后针对最坏情况分别写。具体来说,功率平衡要同时满足两个极端场景:风光出力最小、负荷最大时,机组出力和上备用要能补上缺口;风光出力最大、负荷最小时,下备用要能吸收多余电量。
2.2 鲁棒对等转换的核心公式
直接用YALMIP写鲁棒约束时,如果写成“forall”形式,求解器是不认的。必须手动做对等转换。以旋转备用容量约束为例,考虑风光出力偏差的鲁棒版本是:
$$\sum_i R_u(i) \geq \Gamma_w \cdot \Delta P_w^{max} + \Gamma_l \cdot \Delta P_l^{max}$$
这个式子的含义很直观:需要预留的上备用总量,至少要覆盖“被鲁棒性水平允许的最坏偏差总量”。Γ_w是风电场的不确定预算,ΔP_w^max是单个风电场最大预测偏差。当Γ_w从0增大到风电场总数N_w时,约束从“不需要额外备用”逐渐过渡到“所有风电场同时偏差最大,需要全部备用”。
对下备用,对称地有:
$$\sum_i R_d(i) \geq \Gamma_w \cdot \Delta P_w^{max} + \Gamma_l \cdot \Delta P_l^{max}$$
注意这里上下备用的Γ值可以取不同,实际应用中上备用的Γ通常取更大一些,因为风电出力低估(实际出力小于预测)对系统的威胁比高估更严重——出力不足可能导致切负荷,而出力过剩最多是弃风。
2.3 不确定集合的两种构造方式
不确定集合的构造直接影响模型的保守性和求解难度。我在项目里对比了两种方式。
第一种是盒式不确定集合,每个风光场站的出力在[预测值-偏差,预测值+偏差]内独立波动。这种集合的优点是鲁棒对等转换极其简单,只需要把偏差项乘以Γ就行;缺点是可能过度保守,因为它允许所有场站同时达到边界值(当Γ取最大值时)。
第二种是带预算的盒式集合,也就是经典Bertsimas-Sim框架:每个不确定量有波动区间,但所有不确定量的总偏差之和不能超过一个预算值Γ。这种集合更精细,允许部分场站达到边界、部分场站取中间值,整体上更贴近实际。
从Matlab实现的角度,第二种方式也不复杂,只是约束要多加一个偏差绝对值求和的上限。我在代码里默认用第一种,因为论文里对比不同Γ值的影响更直观;如果做工程,建议换第二种。
3. 实操过程与核心环节实现
3.1 数据准备:风光负荷的预测值与偏差设定
这是最容易出问题的地方,也是很多复现者栽跟头的地方。我建议不要一上来就用真实数据,先用Matlab自带的合成数据把流程跑通,再替换成你自己的数据。
matlab复制% 基础参数设置
T = 24; % 调度时段数(小时)
N_g = 6; % 火电机组数
N_w = 3; % 风电场数
N_v = 2; % 光伏电站数
% 预测出力曲线(标幺值→有名值)
wind_forecast = 0.2 + 0.6 * sin((0:T-1)/T * pi); % 模拟风电夜间大白天小
solar_forecast = 0.8 * max(0, sin((0:T-1)/T * 2 * pi - pi/2)); % 光伏中午大
% 预测偏差:取预测值的15%-20%
wind_dev = wind_forecast * 0.2;
solar_dev = solar_forecast * 0.15;
load_forecast = 0.5 + 0.3 * sin((0:T-1)/T * pi + pi/6); % 负荷早晚高峰
load_dev = load_forecast * 0.05;
这里偏差的比例设置要谨慎。风电20%是常见做法,光伏15%也合理,负荷5%略小但可以接受。如果偏差设得太大,比如风电50%,你最后算出来的备用容量会大得离谱,总成本曲线也缺乏区分度。
3.2 不确定预算Γ的扫描策略
Γ的取值不是随便定的。对风电而言,Γ_w从0扫描到N_w,步长取1,得到N_w+1个离散点。对光伏和负荷类似。多类不确定源叠加时,可以设置一个综合预算:
matlab复制% 不确定预算扫描
Gamma_w_values = 0:N_w; % 风电场不确定预算
Gamma_l_values = 0:N_l; % 负荷不确定预算
results = zeros(length(Gamma_w_values), length(Gamma_l_values));
for i = 1:length(Gamma_w_values)
for j = 1:length(Gamma_l_values)
results(i, j) = run_optimization(Gamma_w_values(i), Gamma_l_values(j));
end
end
这样得到的是一个成本矩阵,而不是一条曲线。画热力图或者三维图会更直观。如果你想输出图1那种经典曲线,可以固定负荷Γ为某个典型值(比如1),只扫光风Γ,得到二维曲线。
3.3 核心优化模型求解:YALMIP+求解器配置
模型用YALMIP建模,求解器推荐用Gurobi或CPLEX。我用的是Gurobi 9.5,在二次目标+线性约束的场景下,几百个变量几十个约束几乎是秒解。
matlab复制% 定义决策变量
Pg = sdpvar(T, N_g, 'full'); % 机组出力
Ru = sdpvar(T, N_g, 'full'); % 上备用
Rd = sdpvar(T, N_g, 'full'); % 下备用
on_off = binvar(T, N_g, 'full'); % 启停状态(如果做机组组合)
% 约束条件组装
Constraints = [];
% ...(省略具体约束写法)
% 目标函数
Objective = sum(sum(repmat(a', T, 1) .* Pg.^2 + repmat(b', T, 1) .* Pg + repmat(c', T, 1))) ...
+ lambda_up * sum(Ru(:)) + lambda_down * sum(Rd(:));
% 求解
ops = sdpsettings('solver', 'gurobi', 'verbose', 0);
optimize(Constraints, Objective, ops);
% 提取结果
Pg_result = value(Pg);
Ru_result = value(Ru);
一个非常重要的经验:鲁棒约束不要用YALMIP的uncertain函数。虽然YALMIP理论上支持鲁棒优化,我试过几次,做小规模算例还行,规模一大就极其缓慢,还会出现数值问题。手动把鲁棒对等约束展开成普通线性约束,既快又稳,这是我在多个项目里反复验证过的做法。
3.4 多时段动态爬坡约束的处理
机组爬坡约束在鲁棒框架下有个隐藏的坑。常规写法是:
matlab复制% 爬坡约束(确定性写法)
Constraints = [Constraints, -Ramp_down <= Pg(t+1,:) - Pg(t,:) <= Ramp_up];
但在考虑备用时,机组的实际可能出力范围应该是“基准出力 ± 备用容量”,所以爬坡约束要写成:
matlab复制% 考虑备用的爬坡约束
Constraints = [Constraints, ...
-(Ramp_down + Rd(t,:)) <= Pg(t+1,:) - Pg(t,:) <= Ramp_up + Ru(t,:)];
这个细节容易被忽略,但非常关键。如果忽略,备用容量虽然被优化出来,却可能因为爬坡限制根本没法在实际调度中调用——模型算出来的成本曲线是乐观偏小的,会误导决策。我在复现论文时踩过这个坑,算出来的备用成本比论文低很多,后来发现就是这里写错了。
3.5 完整代码框架示范
下面给一个精简但完整的代码骨架,可以直接复制到Matlab里跑通整个流程(需要安装YALMIP和Gurobi):
matlab复制%% 风光负荷不同鲁棒性对系统总成本的影响研究
% 考虑上下备用容量 | Matlab代码实现
clear; clc; close all;
%% 1. 基础数据定义
T = 24; N_g = 6; N_w = 3; N_v = 2;
% 机组参数:a(二次项) b(一次项) c(常数项) Pmin Pmax Ramp_up Ramp_down
gen_data = [
0.0025 20 100 50 200 40 40;
0.0018 22 90 40 180 35 35;
0.0030 18 120 60 220 45 45;
0.0022 21 80 30 150 30 30;
0.0040 15 70 20 120 25 25;
0.0028 19 110 35 160 30 30;
];
a = gen_data(:,1); b = gen_data(:,2); c = gen_data(:,3);
Pmin = gen_data(:,4); Pmax = gen_data(:,5);
Ramp_up = gen_data(:,6); Ramp_down = gen_data(:,7);
% 备用价格
lambda_up = 15; % $/MW
lambda_down = 10; % $/MW
% 预测曲线
load_forecast = 300 + 100 * sin((0:T-1)/T * pi + pi/6);
wind_forecast = 60 + 40 * sin((0:T-1)/T * pi);
solar_forecast = 50 * max(0, sin((0:T-1)/T * 2 * pi - pi/2));
% 偏差比例
wind_dev_ratio = 0.20;
solar_dev_ratio = 0.15;
load_dev_ratio = 0.05;
%% 2. Gamma扫描
Gamma_w_max = N_w;
Gamma_l_max = 1; % 负荷预算固定
results = zeros(Gamma_w_max + 1, 1);
for Gamma_w = 0:Gamma_w_max
results(Gamma_w + 1) = solve_robust_dispatch(...
Gamma_w, Gamma_l_max, T, N_g, N_w, N_v, ...
a, b, c, Pmin, Pmax, Ramp_up, Ramp_down, ...
lambda_up, lambda_down, load_forecast, wind_forecast, ...
solar_forecast, wind_dev_ratio, solar_dev_ratio, load_dev_ratio);
end
%% 3. 结果可视化
figure;
plot(0:Gamma_w_max, results, '-o', 'LineWidth', 2);
xlabel('风电场不确定预算 Gamma_w');
ylabel('系统总成本 ($)');
title('不同鲁棒性水平下的系统总成本');
grid on;
这个骨架跑通后,扩展方向很明确:把负荷Γ也扫描起来做成热力图,或者加入储能系统、需求响应等额外资源,研究它们对成本-鲁棒性曲线的改善效果。
4. 仿真结果分析与影响范围研究
4.1 成本随鲁棒性变化的典型规律
以我跑过的标准算例为例(6机24时段+3风电场+2光伏电站),典型的成本-Γ曲线形态如下:
| 风电场不确定预算 Γ_w | 上备用总量 (MW) | 下备用总量 (MW) | 系统总成本 ($) | 相比Γ=0成本增量 |
|---|---|---|---|---|
| 0 | 0 | 0 | 182,450 | - |
| 1 | 42 | 18 | 187,330 | +2.7% |
| 2 | 84 | 36 | 193,870 | +6.3% |
| 3 | 126 | 54 | 201,150 | +10.2% |
注意看这个增量:从Γ=0到Γ=1,成本增加2.7%;从Γ=1到Γ=2,增加3.5%;从Γ=2到Γ=3,增加3.8%。这说明在这个数据设定下,成本增量接近线性,并没有出现明显的“边际成本递增”拐点。这是由偏差比例的均一性造成的——每个风电场的预测偏差比例相同,所以每增加一个单位的Γ,需要新增的备用容量都一样。
如果你想让曲线呈现更丰富的形态,可以给风电场设置不同的偏差比例。比如风电场1预测精度高(偏差10%),风电场2精度低(偏差30%),那么先增加风电场2的预算(Γ从0到1)成本会跳升很大,再增加风电场1的预算(Γ从1到2)成本增量就小很多。这种非对称性在实际系统中非常常见,也是这类研究值得深挖的切入点。
4.2 上下备用容量的分配规律
从仿真结果里还能看出一个规律:上备用总量始终大于下备用总量。原因在于目标函数中,向上备用的成本单价(15 $/MW)高于向下备用(10 $/MW),但风电场出力不足场景迫使系统必须买更多的向上备用。更本质的原因是,风电预测误差并非对称——夜间风电高发时段,预测偏差的绝对值更大,而这恰好是负荷低谷期,系统的下调节能力受限于机组最小出力,所以下备用通常由“机组最小出力约束”决定,而不是由鲁棒性水平决定。
从代码角度来看,调试时最常看到的异常是下备用为0或极小值。这不是代码错误,而是目标函数中lambda_down大于0时,优化器会尽量压低下备用,只要下备用不违反约束就行。如果想让下备用也体现鲁棒性影响,需要检查约束中是否强制了下备用必须覆盖所有不确定场景,很多论文在推导时这里容易出错。
4.3 鲁棒性水平对机组出力组合的影响
不同Γ值下,火电机组自身的出力组合也会发生明显变化。Γ=0时,系统倾向于让成本低的大机组多发电;Γ增大后,系统必须预留更多的上备用,意味着机组必须降低当前出力(腾出上调空间)或者启动更多的机组(增加并联机组台数来分担备用需求)。
我实际观察到的现象是:随着Γ增大,机组组合中开机的机组数量增加,小机组也参与发电了。这是典型的“以运行成本换可靠性”的调度结果。你可能觉得这不够经济,但注意备用容量是有容量成本的——如果你让大机组满载运行,它的上备用能力为0,这时如果风电场出力骤降,系统根本没有调节手段。
这个现象也提醒我们:在研究“总成本”时,要把备用容量成本和燃料成本分开统计。我在代码里会单独输出这两个分量,画成堆叠柱状图,这样审稿人一眼就能看清成本增加的来源——到底是备用买贵了,还是机组组合变化导致燃料成本上升了。实测下来,这两者的比例大约在6:4到7:3之间。
4.4 不同场景下的影响范围扩展
风光负荷鲁棒性的影响范围不仅限于总成本。你把结果做进一步分析,可以延伸到可靠性指标(失负荷概率、电量不足期望)、碳排放量、新能源消纳率等维度。比如在Γ=0时,系统可能弃风3%;Γ=3时,由于预留了大量下备用,弃风率几乎降到0。这在双碳背景下是很受关注的结论。
如果你做的是园区级微电网,还可以研究鲁棒性对储能充放电策略的影响;如果是区域电网,可以把联络线功率交换纳入不确定集合。这些扩展方向在模型和代码上的改动都不大,核心框架仍然是我上面列出的那一套。
5. 常见问题与排查技巧实录
5.1 问题一:模型求解时间长到不可接受
很多人第一次跑带鲁棒约束的机组组合,发现求解时间从几秒变成几分钟甚至几十分钟,就开始怀疑是不是模型写错了。其实大概率是YALMIP建模时引入了大量二元变量和Big-M约束。排查思路是:
- 把on_off变量从binvar改成sdpvar,跑线性规划版本,看求解时间是否恢复正常;
- 如果正常,说明问题出在机组组合的整数变量上,考虑用启停变量的对称约束、三角约束削减组合空间;
- 如果线性版本也慢,检查是否手动展开了鲁棒对等约束,而不是用了uncertain函数;
- 还有一个容易忽略的点:把二次目标在YALMIP里写成quadratic form,比写成分段线性近似更容易被Gurobi有效处理。
5.2 问题二:Γ值取到最大时模型无解
Γ取最大值意味着所有风电场同时取到最坏偏差,这要求系统预留的备用容量极大,很可能超出机组爬坡能力和出力上下限的物理限制。如果此时模型无解,不是代码的bug,而是问题的物理本质——系统的备用能力不够覆盖最极端场景。
工程上的处理方式有两种:一是把Γ_最大值设定为N_w-1而非N_w,表示“至少有一个风电场不会同时发生极端偏差”,这在概率意义上更合理;二是允许模型中存在一定比例的失负荷量,把失负荷作为惩罚项加入目标函数,这样模型总是有解的,并且能给出“备用不足”的定量评估。我在实际项目中倾向于第二种方式,因为它能输出一个完整的成本—可靠性权衡曲线。
5.3 问题三:成本曲线出现非单调波动
理论上,Γ越大成本越高,曲线应该是单调递增的。如果你画出来发现某个点跳低了,99%是代码中有约束写错或目标函数计算有误。最典型的场景是:备用容量约束没有正确施加,导致Γ增大时备用没有增加,而机组组合却因为数值原因发生了不经济的变化。
调试方法:在循环里输出每个Γ值对应的备用总量和机组开机数,检查备用是否严格单调递增。如果备用增加但成本下降,检查燃料成本项计算是否有误;如果备用没有增加,说明鲁棒约束没有真正起作用,回到对等转换那一步仔细检查。
5.4 问题四:Gurobi报许可证错误
这个属于环境问题但非常常见。Matlab里跑Gurobi,需要先在Gurobi官网下载并安装求解器,然后设置环境变量,最后在Matlab中让YALMIP识别:
matlab复制addpath('C:\gurobi1002\win64\matlab');
gurobi_setup;
如果你用的是学校提供的许可证,确认许可证类型是否支持Matlab接口。很多版本的学校许可证默认不包含Matlab接口模块,需要联系管理员申请开通。另外注意Gurobi版本和Matlab版本可能存在兼容性问题,官方文档有兼容性对照表,建议先查一下再安装。
5.5 问题五:结果与论文不一致
复现别人论文时,同样的模型、同样的数据,算出来的成本差5%-10%是非常正常的。原因包括:求解器版本差异(数值容差不同)、Big-M常数取值不同、爬坡约束细节(是否含备用)、备用成本定义(容量成本 vs 调用成本)等。
我建议复现时不要追求绝对值一致,重点看趋势一致——成本随Γ递增的斜率、备用容量的量级、机组组合切换的转折点。如果趋势都对,说明你抓住了文章的核心逻辑;如果趋势不对,优先检查目标函数中二次项和一次项系数的量纲是否一致。这一步在Matlab里很容易忽略,因为成本和功率的单位混乱会带来很大的数值偏差。
6. 扩展应用与实用经验分享
最后分享两个我在实际项目中总结的经验,不一定写进论文,但对做工程方案帮助很大。
第一个是关于“鲁棒性参数的标定”。不要只看成本—Γ曲线就拍板选哪个Γ,如果有历史预测误差数据,可以直接统计出“预测误差的实际分布”,然后算一下每个Γ值对应覆盖了多少百分比的场景。比如Γ=2时覆盖了90%的历史场景,Γ=3覆盖了99%,那你选Γ=2就足够了,再多花钱买来的可靠性收益微乎其微。这个方法在顶刊里叫“数据驱动鲁棒优化”,但本质逻辑其实很简单:让鲁棒性水平对应真实风险水平。
第二个是关于Matlab代码性能的。如果你要做大规模算例,比如上百台机组的系统,YALMIP+Gurobi的建模开销可能比求解本身还大。这种情况下可以考虑用Gurobi的Python接口或直接写MPS文件交给求解器。当然,如果你只是做小规模验证或者发论文,YALMIP在Matlab里的便捷性值得牺牲一点性能。
算力允许的话,把完整的不确定集合从盒式改成椭球式(二范数约束)也值得一试。椭球鲁棒对应的对等约束是二阶锥约束,Gurobi同样支持,而且它的保守性更低——同样的覆盖率下成本更小。在Matlab里改动也非常小,只需要把偏差约束从线性改成norm约束,运行效率依然很高。
这个方向的研究空间还很大,尤其是在多时间尺度协调、分布式鲁棒、以及与碳交易机制耦合的方向上。但不管怎么扩展,把“鲁棒性水平—备用容量—系统总成本”这条主线用Matlab跑通,始终是地基。地基打好了,上面盖什么楼都稳。
