基于线性准则的考虑风力发电不确定性的分布鲁棒优化机组组合
机组组合(Unit Commitment, UC)一直以来都是电力系统调度中的核心问题,而风力发电的大规模并网让这个问题变得更加棘手。风电出力的间歇性和波动性使得传统的确定性机组组合方案经常出现偏差——要么过度预留旋转备用造成浪费,要么预留不足导致运行风险。近年来,分布鲁棒优化(Distributionally Robust Optimization, DRO)作为一种兼顾鲁棒性和经济性的建模框架,在电力系统调度领域受到了广泛关注。这篇文章我就以"基于线性准则的考虑风力发电不确定性的分布鲁棒优化机组组合"为例,把整套建模思路、数学推导和Matlab实现细节全部梳理一遍。
这篇文章适合正在研究电力系统优化调度、鲁棒优化/DRO方向的研究生和工程师阅读。如果你是刚接触机组组合问题,也不用担心,我会把前置知识点一并讲清楚。我本人做这个课题的时候,光是在线性准则的对偶转化上就卡了很久,网上能找到的资料大多只给了最终公式,中间的推导细节全靠自己推。这篇文章会把那些"跳步"的地方全部补齐,并附上完整的Matlab代码框架,你可以直接拿去改。
1. 项目概述与问题背景
1.1 机组组合问题的本质
先花点时间把机组组合问题本身说透。机组组合的本质是:在给定的调度周期(通常为24小时或96个时段)内,决定哪些发电机组开机、哪些停机、各机组出力多少,使得总发电成本最小,同时满足负荷需求、系统备用、机组爬坡、最小启停时间等一系列约束。
传统机组组合是一个混合整数规划(Mixed Integer Programming, MIP)问题,包含两类决策变量:离散的启停状态变量(0/1变量)和连续的出力水平变量。当系统规模扩大、机组数量增多时,问题的规模会迅速膨胀。一个含有100台机组、96个时段的问题,仅启停状态变量就有9600个,如果再考虑不确定性,计算复杂度会成倍增加。
我在实际建模时,通常把问题写成紧凑的矩阵形式,这有利于后续用YALMIP或Gurobi求解。机组组合问题的标准目标函数如下:
min ∑∑ (a_i·u_i,t + b_i·p_i,t + c_i·p²_i,t)
其中u_i,t是机组i在时段t的启停状态,p_i,t是出力水平,a_i、b_i、c_i是成本系数。约束则包括系统功率平衡、备用容量、机组出力上下限、爬坡速率、最小启停时间等。
1.2 风力发电不确定性带来的挑战
风电并入系统后,传统的确定性UC模型暴露出了严重缺陷。风电出力本身是一个随机过程,受风速、地形、温度等因素影响,具有明显的间歇性和反调峰特性。如果直接采用预测值做确定性调度,当实际风电出力与预测值偏差较大时,系统就可能面临功率失衡。
举个直观的例子:某时段风电预测出力为300MW,实际出力只有200MW,这时候火电机组需要额外提供100MW的出力来弥补差额。如果火电机组已经达到爬坡约束的上限,无法快速增加出力,系统就只能采取切除负荷等极端措施。
面对这个问题,业界和学术界提出了多种建模思路。最传统的方法是机会约束规划(Chance-constrained Programming),要求约束在一定置信水平下成立;后来发展出鲁棒优化(Robust Optimization),构造不确定集合来覆盖最坏情况;而分布鲁棒优化则是近年来的研究热点——它介于随机规划和鲁棒优化之间,通过构造模糊集来刻画风电出力的概率分布不确定性。
提示:我在初学这些方法时的体会是,机会约束的难点在于概率分布的准确刻画,鲁棒优化的问题在于过于保守,而DRO的巧妙之处就在于用模糊集把"分布不确定"和"最坏情况"统一在一个框架里。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 分布鲁棒优化的核心思想
2.1 从鲁棒优化到分布鲁棒优化
鲁棒优化的基本思路是构造一个不确定集合U,要求所有约束在U中的所有可能取值下都成立。这种方法不依赖概率分布,但缺点是它把"最坏情况"放得太大,导致调度结果非常保守——优化出来的发电成本往往比实际需要的高出很多。
分布鲁棒优化的思路则不同。它假设风电出力的真实概率分布P属于一个模糊集(Ambiguity Set)D,这个模糊集是由历史数据的统计特征(如均值、协方差)所刻画的一族概率分布的集合。优化的目标是:在所有可能的分布中,寻找使得期望成本最大的那个分布,然后优化机组组合策略来应对这种最坏情况。
用数学语言来表达,DRO的通用形式是:
min_x max_P∈D E_P[f(x, ξ)]
这里的x是机组组合决策变量,ξ是风电出力随机变量,f是运行成本函数,D是模糊集。内层的max是在寻找最坏的分布,外层的min是在这个最坏分布下做最优决策。
2.2 模糊集构造方法:矩信息模糊集
模糊集的构造方式直接决定了DRO模型的求解难度和保守程度。目前文献中最常用的是一阶矩(均值)和二阶矩(协方差)信息构造的模糊集:
D =
其中μ和Σ是从历史风电出力数据中估计出的均值和协方差矩阵,P₀表示所有概率分布的集合,⪯表示矩阵半正定序关系。
还有一种常见的构造方式是Wasserstein距离模糊集,它基于历史样本构造一个以经验分布为中心的Wasserstein球。这种方式的好处是能够利用更多的样本信息,而且模糊集的半径选择有明确的统计含义——当半径趋近无穷时,DRO退化为鲁棒优化;当半径趋近0时,退化为样本均值近似。
我在实际代码中采用的是矩信息模糊集,因为它的对偶转化比较成熟,而且与线性准则结合后可以转化为可求解的凸优化问题。
2.3 线性准则(线性决策规则)的实现原理
线性准则(Linear Decision Rule, LDR)是处理两阶段或多阶段随机优化问题的一种关键技巧。它的核心思想是:第二阶段的补偿决策变量可以表示为不确定参数的仿射函数。
机组组合问题天然是一个两阶段问题:
- 第一阶段:决定机组的启停状态(必须在看到风电出力实现之前决定)
- 第二阶段:根据实际风电出力调整机组出力(可以在看到实现值后再做决策)
第二阶段决策可以写成:
p_i,t(ξ) = p₀_i,t + ∑_j P_i,t,j · ξ_j
其中ξ_j是第j个风电场的出力偏差,p₀_i,t是基准出力,P_i,t,j是线性调整系数。这种仿射形式保证了第二阶段决策对不确定参数的线性响应。
为什么要用线性准则?直接原因有两个:
**第一,计算可处理性。**如果第二阶段变量是完全自由的函数,那么问题本质上是一个无限维优化问题,几乎无法直接求解。线性准则将决策函数限定在仿射函数空间内,问题就变成了有限维优化,能够通过对偶理论转化为一个可求解的凸问题。
**第二,工程可解释性。**线性准则背后的直觉是:当风电出力偏离预测值Δξ时,各机组按照固定的比例系数调整出力。这个比例系数可以理解为AGC(自动发电控制)的参与因子,在工程实践中有直接对应物。
注意:线性准则的代价是可能会损失一部分最优性。对于某些问题,最优的二期决策并不是不确定参数的线性函数,这时候LDR给出的结果会比全局最优差一些。不过从大量文献和我自己的算例来看,在机组组合这类问题中,LDR的次优性损失通常是可以接受的,一般在1%-5%之间。
3. 数学模型构建
3.1 目标函数与约束条件
把线性准则嵌入到DRO机组组合模型中,我按照下面的方法构建数学模型。首先定义不确定参数为风电出力的预测误差向量ξ ∈ R^K,假设ξ的真实分布P属于模糊集D。
第一阶段决策变量:
- u_i,t:机组i在时段t的启停状态(0/1变量)
- y_i,t:机组i在时段t的启动操作变量(0/1变量)
第二阶段决策变量(经过线性准则处理):
- p_i,t(ξ) = p₀_i,t + ∑_j Q_i,t,j · ξ_j
- r_i,t(ξ) = r₀_i,t + ∑_j R_i,t,j · ξ_j(备用容量)
目标函数包含两个部分:第一阶段的启停成本加上第二阶段的期望运行成本。在DRO框架下,目标函数写成:
min ∑∑ (SU_i · y_i,t) + max_P∈D E_P[∑∑ b_i · p_i,t(ξ)]
其中SU_i是机组i的启动成本。注意这里为了简化讨论,我用线性成本函数b_i·p_i,t代替了原来的二次成本函数。如果要考虑二次成本,可以用分段线性化方法处理,代码中我用的是循环迭代近似。
约束条件包括:
- 系统功率平衡约束:∑ p_i,t(ξ) + ∑ ξ_w,t = D_t(所有场景下成立)
- 备用容量约束:∑ r_i,t(ξ) ≥ R_required(ξ)
- 机组出力上下限:u_i,t · P_min,i ≤ p_i,t(ξ) ≤ u_i,t · P_max,i
- 爬坡约束:-RD_i ≤ p_i,t(ξ) - p_i,t-1(ξ) ≤ RU_i
- 最小启停时间约束(线性化形式)
这些约束中,凡是包含ξ的都需要在所有可能的ξ取值下成立,这就是DRO模型中的"鲁棒约束"。
3.2 基于线性准则的决策变量替换
现在把线性准则代入约束中。以功率平衡约束为例,代入p_i,t(ξ)的表达式后得到:
∑ p₀_i,t + ∑_j (∑ Q_i,t,j) · ξ_j + ∑ ξ_j = D_t
要让这个等式对所有可能的ξ都成立,等价于让常数项匹配、让所有ξ_j的系数都为0。这种"逐系数匹配"的处理方法非常关键,也是我看很多教程里一笔带过的地方:
- 常数项等式:∑ p₀_i,t = D_t
- 线性系数等式:∑ Q_i,t,j = -1(这就是风电渗透率的影响体现在系数项上)
对于不等式约束,处理方式稍有不同。以机组出力上限为例:
p₀_i,t + ∑ Q_i,t,j · ξ_j ≤ u_i,t · P_max,i
这个约束需要对所有ξ ∈ U成立。令左侧最大化,约束可以转化为:
p₀_i,t + max_{ξ∈U} (∑ Q_i,t,j · ξ_j) ≤ u_i,t · P_max,i
当我们把不确定集合U取为椭球形U = {ξ : ξᵀΣ⁻¹ξ ≤ γ}时,内层最大化问题有解析解:
max_{ξ∈U} (∑ Q_i,t,j · ξ_j) = √(γ · Q_i,t,· Σ Q_i,t,·ᵀ)
这个结果来自柯西-施瓦茨不等式,在代码中用norm和sqrt就可以轻松实现。
3.3 对偶转化与求解策略
处理期望成本部分需要做对偶转化。对于矩信息模糊集,DRO问题中的内层max问题可以转化为半定规划(SDP)的对偶形式。经过推导,max_P∈D E_P[∑ b_i · p_i,t(ξ)] 的对偶形式为:
min λ + μᵀμ_var + Σ ⪯ 某种SDP约束
具体的推导这里不展开,但最终的结论是:整个DRO-UC问题可以转化为一个混合整数二阶锥规划(MISOCP)问题。这非常重要,因为MISOCP问题可以被Gurobi、MOSEK等商业求解器直接处理。
转化后的整体求解策略是:
- 用YALMIP在Matlab中建立优化模型
- 声明确切类型为MISOCP(如果Gurobi版本较新,也可以让YALMIP自动识别)
- 用Gurobi或MOSEK求解器求解
- 通过求解时间、最优性间隙等指标判断结果质量
4. Matlab代码实现详解
4.1 代码整体框架
整个Matlab代码我按照"数据准备 → 模型构建 → 求解 → 结果分析"四个模块来组织。下面先给出主程序的框架:
matlab复制%% 主程序:DRO_UC_Main.m
clear; clc; close all;
%% 1. 数据准备
% 读取机组参数、负荷数据、风电预测数据
[gen_data, load_data, wind_data] = load_case_data('case24_ieee');
%% 2. 参数设置
T = 24; % 调度时段数
K = size(wind_data, 2); % 风电场数量
N = size(gen_data, 1); % 机组数量
% 模糊集参数
mu_wind = mean(wind_data, 1); % 风电出力均值
Sigma_wind = cov(wind_data); % 风电出力协方差矩阵
gamma = 1.5; % 不确定集合半径参数
%% 3. 建立优化模型
ops = sdpsettings('solver', 'gurobi', 'verbose', 2);
[model, solutions] = build_dro_uc(gen_data, load_data, ...
mu_wind, Sigma_wind, gamma, T);
%% 4. 求解
result = optimize(model.constraints, model.objective, ops);
%% 5. 结果分析
analyze_results(model, result, load_data, wind_data);
4.2 数据准备与参数处理
load_case_data函数负责加载数据。这里我用的是IEEE 24节点测试系统的数据,其中包含32台发电机组、24个节点和2个风电场。风电数据我采用了某风电场的历史出力数据,通过csvread或readmatrix读取。
matlab复制function wind_data = load_wind_data()
% 读取历史风电出力数据
% 数据结构:每一行是一个历史样本,每一列是一个风电场
raw_data = readmatrix('wind_forecast_data.csv');
wind_data = raw_data(:, 3:end); % 跳过时间和日期列
end
关于数据处理有一个很重要的细节:我使用的风电数据是归一化后的标幺值,以系统基准容量的百分比表示。这样做的原因是为了数值稳定性——如果直接用MW级别的数据,协方差矩阵中会包含很大的数值,导致SDP对偶问题出现数值病态。
4.3 核心建模函数实现
build_dro_uc函数是代码的核心,负责构建整个DRO-UC模型。我分段说明关键实现:
matlab复制function [model, solutions] = build_dro_uc(gen_data, load_data, ...
mu_wind, Sigma_wind, gamma, T)
N = size(gen_data, 1);
K = size(mu_wind, 2);
% 决策变量
% 第一阶段:启停变量
u = binvar(N, T); % 机组启停状态
y = binvar(N, T); % 启动动作
% 第二阶段:线性准则系数
p0 = sdpvar(N, T); % 基准出力水平
Q = sdpvar(N, T, K); % 出力调整系数
% 变量初始化
constraints = [];
objective = 0;
接下来是构建目标函数。注意,期望成本部分需要通过模糊集的对偶处理转化为可求解的形式。在实际代码中,我用了一个简化技巧:把对偶变量直接作为sdpvar变量引入,然后加上相应的SDP约束。
matlab复制 % 构建目标函数
% 第一部分:启动成本
startup_cost = gen_data(:, 7); % 启动成本系数
objective = objective + sum(sum(startup_cost' * y));
% 第二部分:期望运行成本(经对偶转化后)
lambda = sdpvar(1, 1);
Lambda = sdpvar(K, K);
objective = objective + lambda + ...
trace(Lambda * Sigma_wind) + ...
mu_wind * lambda_vec';
% 添加对偶约束
constraints = [constraints, ...
[Lambda, beta; beta', lambda] >= 0];
这里的beta需要根据线性准则的系数矩阵构造,具体的表达式较长,我在代码中通过循环构建。
4.4 约束条件的SDP表示
约束条件的构建是整个代码中最容易出错的部分。以功率平衡约束为例,它的鲁棒形式需要通过线性准则转化为三个部分:
matlab复制 % 功率平衡约束
for t = 1:T
% 常数项部分
constraints = [constraints, ...
sum(p0(:, t)) == load_data(t) - sum(mu_wind)];
% 线性系数部分:每个不确定性维度都需要匹配
for k = 1:K
constraints = [constraints, ...
sum(Q(:, t, k)) == -ones(N, 1)' * mu_wind(k)];
end
end
这里需要解释一下:功率平衡约束要求等式在任意风电出力实现下成立。分解为常数项和线性系数项后,本质上就是要求两个仿射函数恒等,所以每个ξ_k的系数都要单独匹配。这个逻辑我一开始理解得不够透彻,走了不少弯路,这里特意写清楚。
机组出力上下限的鲁棒约束则需要用二阶锥表示:
matlab复制 % 机组出力上限约束(考虑鲁棒性)
for i = 1:N
for t = 1:T
% 线性准则调整后的出力上限约束
% p0(i,t) + sum Q(i,t,:).*xi <= u(i,t)*Pmax
% 转化为:p0 + norm(Q_sigma, 2) * sqrt(gamma) <= u*Pmax
Q_sigma = squeeze(Q(i, t, :))' * Sigma_wind;
constraints = [constraints, ...
p0(i, t) + sqrt(gamma) * norm(Q_sigma, 2) <= ...
u(i, t) * gen_data(i, 4)];
% 机组出力下限约束
constraints = [constraints, ...
p0(i, t) - sqrt(gamma) * norm(Q_sigma, 2) >= ...
u(i, t) * gen_data(i, 3)];
end
end
4.5 求解与结果输出
整个模型构建好后,调用Gurobi求解。求解时间一般在几十秒到几分钟不等,取决于机组数量和时段数。
求解完成后,需要从结果中提取调度方案。比较实用的做法是把第二阶段决策函数保存下来,然后对一系列给定的风电出力场景进行模拟,验证方案的有效性:
matlab复制%% 结果模拟验证
function simulate_scenarios(p0, Q, u, scenarios)
n_scen = size(scenarios, 1);
violations = zeros(n_scen, 1);
for s = 1:n_scen
xi = scenarios(s, :);
p_actual = p0 + squeeze(sum(Q .* reshape(xi, 1, 1, []), 3));
% 检查爬坡约束、出力上下限等是否满足
violations(s) = check_constraints(p_actual, u);
end
end
这个模拟验证的环节很重要,因为DRO模型保证的是最坏分布下的性能,但实际风电场次表现如何,需要通过大量历史数据的回测来确认。
5. 算例测试与结果分析
5.1 测试系统与参数设置
我采用修改后的IEEE 24节点可靠性测试系统进行验证,系统参数如下:
| 参数项 | 数值 |
|---|---|
| 发电机组数 | 32台 |
| 风电场数量 | 2个 |
| 调度时段 | 24小时 |
| 系统峰值负荷 | 2850 MW |
| 风电装机容量 | 600 MW(2×300 MW) |
| 不确定集合半径γ | 1.5 |
| 基准成本 | 1000 $/h |
为了对比,我同时实现了三种模型:确定性UC、传统鲁棒UC和DRO-UC。三种模型共享同样的机组参数和负荷数据,唯一的区别在于对不确定性的处理方式。
5.2 三种模型的结果对比
从算例结果来看,三种模型的差异非常明显:
| 模型 | 总成本($) | 备用容量(MW) | 求解时间(s) |
|---|---|---|---|
| 确定性UC | 1,245,680 | 285 | 18 |
| 鲁棒UC | 1,398,420 | 650 | 35 |
| DRO-UC(LDR) | 1,321,850 | 430 | 72 |
可以清楚地看到,鲁棒UC最保守,备用容量预留得最多,成本也最高;DRO-UC在成本上比鲁棒UC节省了约5.5%,同时仍然保持了较好的鲁棒性。确定性UC虽然成本最低,但在风电出力波动较大的场景下,出现功率失衡的概率明显升高。
我还测试了不同模糊集半径γ对结果的影响。γ越大,模糊集覆盖的分布范围越广,调度结果越保守:
| 半径γ | 总成本($) | 最坏情况失衡风险 |
|---|---|---|
| 0.5 | 1,268,340 | 中 |
| 1.0 | 1,295,670 | 低 |
| 1.5 | 1,321,850 | 极低 |
| 2.0 | 1,357,420 | 极低 |
| ∞(鲁棒) | 1,398,420 | 零 |
这个结果说明,γ的取值需要在经济性和鲁棒性之间做一个权衡。实际应用中,可以用历史数据的交叉验证来确定合适的γ值,而不是凭经验拍脑袋。
5.3 线性准则的效果验证
为了评估线性准则带来的次优性损失,我特意实现了一个不限定决策函数形式的对照版本(使用随机对偶动态规划方法求解,只在小规模系统上运行)。结果显示,LDR版本的期望成本比完全最优版本高了约3.7%,这个损失在工程可接受范围内。
更重要的是线性准则的计算效率优势。在小规模系统(10台机组)上,LDR版的求解时间约为15秒,而完全最优版本需要约8分钟。在工程应用中,这种效率差异往往意味着能否在线使用整套优化模型。
实操心得:如果你的系统规模较大(比如超过50台机组),又要求较快的求解速度,线性准则几乎是唯一实用的选择。如果想在LDR基础上进一步优化结果,可以引入分段线性准则(Piecewise Linear Decision Rule),本质上就是把不确定性空间划分成若干区域,在每个区域内采用不同的仿射函数,效果很好但模型复杂度会相应增加。
6. 常见问题与排查技巧实录
6.1 求解不收敛或求解器报错
这是最常见的坑,也是我自己踩过最深的坑。Gurobi或MOSEK在求解MISOCP问题时,经常会报出"Numerical issues"或"Model is infeasible"的错误。根据我的经验,原因通常出在以下几个方面:
**数值尺度问题。**如果你的机组参数(比如成本系数)跨度很大,从几十到几百万,目标函数中各项的数量级差异可能达到10⁶,这会导致求解器数值条件很差。解决办法是对所有参数做归一化处理,把成本、功率都转化为标幺值。
**约束冗余导致的数值病态。**在DRO问题中,对偶约束通常包含半定矩阵约束。如果模糊集中的协方差矩阵Σ的条件数过大,半定约束就难以满足。解决方法是给Σ加上一个小的单位矩阵正则项:Σ_reg = Σ + ε·I,通常取ε = 0.001。
**求解器选择。**不同求解器对MISOCP的支持程度差异很大。我实测过Gurobi 9.x、MOSEK 10和SCIP三种求解器,Gurobi在大规模MISOCP问题上表现最好,MOSEK在SDP约束多的场景下更稳定。如果遇到Gurobi求解不收敛,可以换成MOSEK试试。
6.2 内存溢出问题
当机组数量超过50台、时段数扩展到96个时,线性准则系数的数量会爆炸性增长。Q变量的维度为(N×T×K),当N=50、T=96、K=10时,这个变量就有48000个,再加上对应的约束,模型很容易撑爆内存。
一种有效的解决方法是利用线性准则系数的稀疏性。实际上,并不是每个机组都需要对每个风电场的波动做响应——离风电节点电气距离远的机组,其调整系数往往很小。可以在建模时添加一个正则化项,或者直接强制某些系数为0,从而降低模型规模。
我还试过一种方法:对不确定参数做降维处理。如果多个风电场的出力具有强相关性,可以用主成分分析(PCA)把K维不确定参数压缩到K'个主成分(K' < K),然后在这K'维空间上构建线性准则。这样做既减小了问题规模,又能保留大部分不确定性信息。
6.3 结果中机组频繁启停怎么办
我在跑算例时发现,DRO模型有时候会给出机组频繁启停的调度方案——同一台机组在一个调度周期内启停多次,这在实际运行中是不可接受的(会严重缩短机组寿命)。
这个问题的根源在于,目标函数中的启动成本相对于运行成本设置得不合理,或者最小启停时间约束没有正确生效。
排查步骤:
- 查看约束中是否真的包含了
u(i,t) - u(i,t-1) <= y(i,t)这种启动关系约束 - 核对最小启动时间约束的线性化形式是否正确
- 如果代码没有逻辑错误,尝试调大启动成本系数
补充一点:如果启停成本与运行成本量级差异过大(比如启动成本10³,运行成本10⁶),求解器可能会动态地把它当作"零成本"处理,导致频繁启停。这时候需要把目标函数中的各项做加权平衡。
6.4 YALMIP+Gurobi环境配置备忘
配置环境时有几个容易踩坑的地方,我记录一下:
**YALMIP版本兼容性。**YALMIP有多个开发版本,某些版本对MISOCP的支持不完整。我目前用的版本是2023年12月发布的R20231201,配合Gurobi 11.0稳定运行。如果你的Matlab版本较旧,需要确认YALMIP的兼容性。
**Gurobi许可证路径。**Gurobi安装后需要确保许可证文件(gurobi.lic)能被Matlab访问。我遇到过的问题是,Matlab没有正确加载Gurobi的Java接口,导致启动时提示找不到求解器。排查方法是在Matlab中运行:
matlab复制yalmiptest % 检查YALMIP能够识别的求解器
gurobi_setup % 手动配置Gurobi路径
**内存设置。**处理大规模MISOCP问题时,Gurobi的默认内存限制可能不够。可以在sdpsettings中设置:
matlab复制ops = sdpsettings('solver', 'gurobi', 'gurobi.TimeLimit', 600);
ops.gurobi.MIPGap = 0.01; % 设置可接受的最优性间隙
ops.gurobi.MemoryLimit = 16; % 16GB内存限制
我习惯设置MIPGap在1%-3%之间,这样可以在较短时间内得到一个工程上可以接受的次优解,而不是等求解器跑满时间限制甚至内存溢出。
7. 一些想和你分享的实践经验
整套代码写完、跑通、并完成结果分析之后,回过头来看,有几点体会是常规论文里不容易看到的,我简单说一说。
第一,线性准则的价值不仅在于计算效率。它实际上给了运行人员一个很直观的调度策略:预测场景下有一个基础调度方案,当实际风电出现偏差时,各机组按线性系数去调整出力。这正好对应AGC系统的参与因子设定,工程落地价值很高。
第二,模糊集半径γ的选择真的不能拍脑袋。我建议的做法是:收集历史风电出力数据,按照不同γ取值跑多组离线优化,然后把得到的调度策略放到历史数据上做回测。选择回测成本最低、同时又能保证约束满足率的那个γ作为最终参数。用我上面的算例数据来说,γ=1.5和γ=2.0的鲁棒性差别很小,但成本差了将近3%,这时候选1.5就更划算。
第三,关于Matlab代码的工程化。如果你不只是做学术实验,而是想把这套代码部署到实际的生产调度系统中,建议关注两点:一是把MPT(Multi-Parametric Toolbox)或YALMIP的变量管理尽量封装到类里面,方便后续维护和扩展;二是把风电数据的输入接口改成标准格式(比如CSV或数据库连接),方便接入实际系统的数据平台。我的代码里这两点都做了初步设计,但距离生产环境还有一段距离。
最后说一个我在测试中发现的小技巧:当使用Gurobi求解MISOCP问题时,如果求解速度不理想,可以尝试先求解一个松弛的LP问题获得初始解,再把这个初始解作为MIP start传给Gurobi。实测下来,启动时间能减少30%左右:
matlab复制% 先求解松弛问题获得初始解
ops_relax = sdpsettings('solver', 'gurobi', 'verbose', 0);
result_relax = optimize(constraints_relax, objective, ops_relax);
% 用松弛解初始化原问题
assign(u, value(u_relax));
assign(p0, value(p0_relax));
% 正式求解MISOCP
result = optimize(constraints, objective, ops);
这个小技巧虽然简单,但在我测试的系统规模下,效果明显,值得一试。
