调度台前最怕的不是风电突然来一阵大波动,而是我们根本不知道误差到底服从什么分布。第二天风电出力预测值是350兆瓦,实际可能落在180到420兆瓦之间,这种偏差的“分布形状”往往只有几十个历史样本,谁也说不准。所以当“基于线性准则的考虑风力发电不确定性的分布鲁棒优化机组组合”这个思路进入视野时,我最关心的是:分布鲁棒优化能不能从论文变成能跑的代码。传统机组组合把风电当确定数字用,随机优化则硬给误差套一个概率分布,分布鲁棒优化走的是中间路线——只要真实分布藏在我用历史数据圈出的模糊集里,调度方案就保证可行。本文就用Matlab把这个流程完整跑了一遍,把建模思路、线性准则的作用、求解时踩过的坑都摆出来。
1. 为什么机组组合必须正视风电误差的“分布未知”
1.1 确定性调度:误差不是噪声而是系统性风险
很多教材里的机组组合模型,输入侧只有一条风电预测曲线,启停计划、出力基点、备用容量都围着这条单一曲线转。这种做法在风电占比小、误差不算离谱的时候还能接受,但风电一多,问题就藏不住了:预测误差会在某些时段集中爆发,比如夜间预测偏高而实际出力很低,系统不得不用高价机组紧急顶上。
更关键的是,确定性模型的“备用设置”通常是拍脑袋的,比如固定加10%备用或按最大单机容量留备用。它没有把“误差有多大、误差分布长什么样”和成本关联起来,导致两种极端:要么备用给少了,负荷平衡被打破;要么备用给多了,经济性被白白牺牲。
我见过一个很典型的案例:某地区风电渗透率到30%之后,固定比例备用策略在强风过程天气下连续三天出现备用不足,最后是靠临时切负荷才兜住。问题根源不是预测算法太差,而是调度模型根本没有量化“预测偏差的分布”对可行性的影响。
1.2 随机优化与经典鲁棒优化的两难
既然把风电当确定值不行,自然会想到随机优化。随机优化要求先给出风电预测误差的确切概率分布,比如已知误差服从均值为0、方差为σ的正态分布,然后对大量抽样场景求期望最优。但实际中“分布是谁”本身就是未知数。
文献里经常说“用一个正态分布拟合误差”就够了,可你会发现同一风电场在不同季节、不同天气过程下的误差形态差别很大,轻尾、重尾、偏态都可能出现。样本量不够时,用指定分布做出来的解在样本外测试里频繁违约,原因很简单:你假设错了分布。
反过来,经典鲁棒优化把误差限定在一个确定的“盒式不确定集合”里,只要误差落在这个盒子内,约束必须全部满足。这个思路很稳,但它等价于假设误差的每一个极端值同时发生。实际中不同风电场间误差存在明显抵消效应,全网总偏差很少同时向最坏方向拉满。结果是鲁棒解的成本比确定性方案高出一大截,启停机组频繁,运行人员看了报价只想摇头。
1.3 分布鲁棒优化给出的第三条路
分布鲁棒优化的想法很自然:别去精确指定分布,也别用纯集合最坏情况,而是在历史数据附近构造一个“模糊集”,让真实分布以大概率落在这个集合里。调度方案只需要对该集合内所有可能的分布都可行或足够优。
这个思路同时避开了两个坑:第一,不需要知道真实分布的具体形式,只用样本;第二,通过模糊集的半径控制了保守程度,半径取0退化成随机优化,半径取无穷大退化成经典鲁棒优化。
所以标题里“考虑风力发电不确定性”的关键,不是把某个分布参数写得天花乱坠,而是把“分布未知”这件事本身作为建模对象。我从一开始就确定,这篇实现的核心难点在模糊集的构造、以及后续怎么把分布鲁棒问题转成Matlab能求解的混合整数线性规划。
2. 模糊集与线性准则:模型里真正值钱的数学设计
2.1 把“分布未知”翻译成约束:两阶段机组组合的一般形式
机组组合天然是两阶段决策。第一阶段是“日前”决策:决定每台机组在哪些时段开机、关机,给出出力基点;第二阶段是“实时/再调度”决策:看到风电实际出力偏差之后,通过调整部分机组出力、调用备用等手段保证负荷平衡和网络安全。
用变量来写大致是这个结构:
- 第一阶段变量:机组开停状态 (u_{i,t})、启动/停机变量、基点出力 (p^0_{i,t})。
- 不确定参数:各风电场在时段的出力偏差 (\xi_{w,t}),可以按时段堆成一个向量 (\xi)。
- 第二阶段变量:实际调整量 (p_{i,t}(\xi)),以及弃风、切负荷、备用调用等。
问题的一般形式是:
[ \min_{u,p^0} \left{ c^T u + d^T p^0 + \sup_{\mathbb{P}\in \mathcal{F}} \mathbb{E}_{\mathbb{P}}\left[ Q(p^0,\xi) \right] \right} ]
其中 (Q(p^0,\xi)) 是给定第一阶段方案和误差 (\xi) 后的再调度成本函数。难点就在 (\sup_{\mathbb{P}\in \mathcal{F}} \mathbb{E}_{\mathbb{P}}[\cdot]) 这一项,它要求我们枚举模糊集内所有可能分布下的最坏期望成本,直接算几乎不可能。
2.2 用Wasserstein球圈出真实分布
模糊集有很多种构造方式,比如矩约束集、统计距离球、机器学习里的对抗样本集。本文采用了一个在工程上接受度很高的选择:以历史经验分布为球心、以Wasserstein距离为半径构造模糊球。
Wasserstein距离衡量的是“把一个分布搬运成另一个分布所需的最小成本”,因为考虑到误差向量之间的坐标尺度问题,实际计算时常选用1-范数或无穷范数等。它的好处是:模糊集里不仅包含与经验分布接近的点分布,还允许支撑集发生平移,不会把某些真实可能出现的极端误差值从根上排除掉。
设历史偏差样本为 (\hat{\xi}_1,\dots,\hat{\xi}_N),经验分布为 (\hat{\mathbb{P}}_N),则模糊集写为:
[ \mathcal{F}_\varepsilon={\mathbb{P}: W(\mathbb{P},\hat{\mathbb{P}}_N)\le \varepsilon} ]
这里的 (\varepsilon) 是模糊集半径,集中体现了模型对“分布不确定”的容忍程度。半径太小,模型把历史样本当成金科玉律;半径太大,模型又宁可信最坏情况,不信任任何数据。
在这个框架下,最坏期望问题可以通过对偶理论转变为一个增广的有限维优化问题。对于线性准则配合下的目标函数和约束,最后得到的是混合整数线性规划,这为Matlab下的求解扫清了最大障碍。
2.3 线性决策规则:把“看风下单”写成线性函数
第二阶段最理想的调整策略是 (\xi) 的任意函数 (p_{i,t}(\xi)),因为再调度完全跟随误差走。但任意函数是不可求解的,工程中通行做法是限制函数形式,其中最常用的是仿射决策规则,也就是标题里说的“线性准则”。
线性准则的含义非常直接:机组出力对风电偏差的反应是线性响应,写成:
[ p_{i,t}(\xi)=p^0_{i,t}+\sum_{w\in \mathcal{W}} \alpha_{i,t,w}\xi_w ]
其中系数 (\alpha_{i,t,w}) 表示机组i在时段t对风电场w偏差的响应斜率。正值表示风电出力不足时多带出力,负值表示风电出力过剩时少发或弃风。
为什么敢用线性准则?一方面是因为机组爬坡约束、成本函数在线性化后,整个问题的最优调整策略在一定条件下本来就接近分段线性,线性近似已经能捕获大部分经济性收益;另一方面是只有把第二阶段策略设成线性函数,对偶后的模型才能保持线性结构,否则就要引入非线性规划,求解难度会指数级上升。
我当时做完第一版非线性场景测试后,又用线性决策规则对比,发现两者的期望成本差距在1%到3%之间,但求解时间从几小时降到几分钟。做工程调度,这个代价完全可以接受。
3. Matlab实现的完整链路:从历史样本到MILP落地
3.1 数据准备:从历史出力记录到偏差样本
实现的第一步是整理风电预测偏差数据。每个风电场需要同时具备历史预测值和实际值,确保数据在时区上对齐。时间颗粒度常见的是15分钟或1小时,我建议用1小时起步,先把模型跑通再细化。
偏差样本的计算很简单:
% 假设 pred 是预测值矩阵(nHistorical x nWind) % actual 是实际出力矩阵(nHistorical x nWind) xi = actual - pred; % 舍入到保留两位小数,减少求解器数值压力 xi = round(xi, 2);每个历史时刻对应一个偏差向量 (\xi_k),N个历史时刻就得到N个样本点。如果风电场数量很多,建议先做相关性分析和主成分降维,避免偏差向量维度过高导致对偶后的辅助变量爆炸式增长。
我在实验中用了两个风电场、6个节点的小系统,偏差样本取了最近90天的数据。注意:样本量不必贪多,但必须覆盖不同的天气过程和季节形态,否则模糊集球心本身就有偏。
3.2 上下层变量组织与目标函数构造
模型变量主要包括三层:
第一层是整数变量,描述机组启停状态,例如6台机组、24个时段就有144个0-1变量。Matlab中可以用optimvar定义二进制变量。
第二层是连续变量,包括各机组各时段的基点出力、启动/停机成本相关辅助变量,以及第二阶段调整策略的仿射系数 (\alpha_{i,t,w})。
第三层是分布鲁棒对偶后的辅助变量,这一步是模型能否落入MILP的关键,通常包括对偶变量、范数约束里的辅助标量等。粗算下来,6节点小系统大约有几千个连续变量和两百多个整数变量,规模不算大。
目标函数我分三块写:
% 燃料成本:二次成本线性化后的分段系数 % 启停成本:启动成本+停机成本 % 分布鲁棒最坏期望再调度成本 objective = sum(sum(bidcost .* p0)) ... + sum(sum(startcost .* u_start)) ... + dro_expected_adjust_cost;dro_expected_adjust_cost由对偶问题中的辅助变量构成,求解器看不见“最坏分布”这些概念,只能看见一堆线性变量和约束。
3.3 对偶转换、线性化与intlinprog求解
Wasserstein分布鲁棒问题对偶后,指数项会被一组有限的变量和约束替换。对线性准则情形,核心是把:
[ \sup_{\mathbb{P}\in \mathcal{F}\varepsilon} \mathbb{E}{\mathbb{P}}[Q(p^0,\xi)] ]
转化为关于辅助变量的线性目标,并在约束中加入关于每个历史样本 (\xi_k) 的不等式。
在实际建模时,我使用了Matlab的problem-based优化工具箱,把约束一行行读进去,再调用内置的混合整数线性规划求解器。关键代码示意如下:
prob = optimproblem; u = optimvar('u', nGen, T, 'Type', 'integer', 'LowerBound', 0, 'UpperBound', 1); p0 = optimvar('p0', nGen, T, 'LowerBound', 0); alpha = optimvar('alpha', nGen, T, nWind, 'LowerBound', -1, 'UpperBound', 1); s = optimvar('s', nSample, 1, 'LowerBound', 0); % 对偶辅助变量 q = optimvar('q', 1, 1); % 对偶标量变量 prob.Constraints.load_balance = ...; prob.Constraints.dro_dual = ...; prob.Objective = ...;这里最容易出错的是范数约束的线性化。如果历史偏差按2-范数计算Wasserstein距离,对偶后会出现二阶锥约束,MATLAB自带intlinprog并不能直接处理。解决方式有两种:要么把范数改成1-范数或无穷范数,得到纯线性约束;要么引入额外变量做锥规划近似。我建议第一版代码直接选1-范数,干净、快速,和2-范数在最坏成本上的差异很小。
3.4 用一个小系统算例验证流程
我用包含6台机组、24时段、2个风电场和90个历史偏差样本的模拟系统做验证。机组参数来自常见算例的公开形式,风电预测曲线和实际出力由历史数据驱动。
跑通之后,第一步检查的是负荷平衡约束是否满足。因为分布鲁棒模型允许误差在一定范围内变化,所以不能只查预测场景下的平衡,还要查所有历史样本对应的平衡是否满足。把每个样本代入线性决策规则后,逐点检查节点注入功率,发现有一半时段因为风电功率离散化精度问题出现微小越限,把偏差样本保留两位小数之后恢复正常。
最终得到的启停计划相比确定性方案多启动了半台到一台机组,但切负荷概率从确定性方案的10%以上降到3%以内,总成本增加大约6%。在调度场景里,这个代价换来的可靠性提升是值得的。
4. 参数敏感性、求解性能与那些容易踩的坑
4.1 模糊集半径ε怎么取:过小等于自欺,过大等于躺平
分布鲁棒优化最难以解释也最需要调参的就是模糊集半径 ε。它本质上是“样本容量和真实置信度之间的扳手”。
理论上,可以通过统计方法给出ε关于样本数量N、置信度β的估计公式,但工程上我更推荐直接用样本外测试定半径。做法是留出一部分历史数据不参与建模,把不同ε下求得的调度方案代入样本外数据,统计约束违反率和总成本。
我实测的一组数据大致如下,注意这里不是某个系统的标准答案,只用于说明趋势:
| ε取值 | 总成本(万元) | 求解时间(秒) | 样本外约束违反率 |
|---|---|---|---|
| 0.01 | 1025 | 42 | 18.7% |
| 0.05 | 1089 | 59 | 6.5% |
| 0.10 | 1153 | 87 | 2.1% |
| 0.30 | 1248 | 124 | 0.3% |
从表里能清楚看到,ε=0.01时模型约等于把历史样本当成精确分布,样本外违反率飙升到接近两成,这个方案根本不敢用;ε=0.30时样本外表现很稳,但成本涨了20%多,调度员也会抱怨太保守。
我的选择习惯是:先根据安全标准确定可接受违反率上限,比如切负荷概率低于5%,然后选能满足该约束的最小ε。这比纠结统计置信度公式更直观,也更贴近调度要求。
4.2 大M与辅助变量带来的数值陷阱
分布鲁棒对偶转换后会引入很多形如 (\eta_k \ge \ldots) 的线性约束,其中经常出现表示“样本k最坏成本”的辅助变量和二进制选择逻辑。为了把逻辑关系写进整数规划,我用了大M法。
大M法本身不复杂,但M取多大会直接影响求解质量。M太小会把可行域错误压缩,导致明明应该有解的时节电计划直接无解;M太大则会让lp松弛很差,整数求解时间暴涨。
一个真实的排查经历:我把启动成本的线性化M值设成1e6,结果求解器在连续节点上反复探索无用区域,跑2000秒还收敛不了。把M值缩小到和该时段最大可能成本同量级后,问题在几十秒内解决。经验是:M值不是越大越好,最好通过历史数据推算出成本上界,再放大1.5到2倍,留一点余量即可。
数值上还要注意向量维度假象。Matlab的problem-based模型会把每个约束自动向量化,当你写sum(p, 1) >= demand时,求解器内部生成的约束数量可能远大于你预期,导致内存暴涨。我建议把24时段按循环逐条写清楚,或者用索引生成约束矩阵,不要依赖过度花哨的向量化技巧。
4.3 求解时间与精度权衡的实测明细
分布鲁棒机组组合的求解时间主要由整数变量规模和对偶辅助变量规模共同决定。我实测发现,同等规模下,把再调度策略从线性准则放松成普通连续变量再做场景枚举,求解时间会呈指数级增长;而线性准则将每个机组的调整策略压缩成少量仿射系数,整数变量数量基本不变,瓶颈从“如何枚举分布”转移到“如何切分整数节点”,对商用求解器比较友好。
另一个容易忽略的性能因素是风电偏差向量的维度。两个风电场时,仿射系数矩阵是 机组数×时段数×风电场数,还算可控;如果系统变成20个风电场,系数数量立刻膨胀到原来的10倍,单纯增加风电场数量对模型负担非常大。
针对这个情况,我采用了两种缓解手段:一是对高度相关的风电场进行聚类,合并成若干等效风电场;二是对偏差样本做稀疏编码,只保留对负荷平衡影响最大的分量。这样可以在损失极小精度的情况下显著压减变量规模。
5. 往储能与滚动调度扩展时,我踩出的几条经验
模型跑通之后,我自然想把它往含储能的联合调度方向推。第一个直观做法是把储能充放电功率也放进第二阶段线性策略里,让储能随风电偏差自动调整。试算后发现,虽然储能提升了灵活性,但储能自身的充放电效率、容量衰减等参数也存在不确定性,如果只把风电偏差放进模糊集,储能参数误差带来的风险会被低估。
一个可行的改进是:把储能效率波动也放入随机向量,模糊集半径相应增大。代价是求解规模进一步变大,但结果明显更扎实。
另一个经验来自滚动调度场景。如果每15分钟就重新估计一次模糊集,ε会随着最新样本进进出出发生抖动,导致相邻两个时段的启停方案互相冲突。我最终选择了一个折中:短期内不调整ε,只在每天重新滚动时更新一次历史样本并重新标定半径。
如果你也想复现这套流程,别急着把模糊集半径、范数类型、辅助变量全部调成“理论上最优”。先把一个最小系统的完整链路跑通,把负荷平衡、爬坡约束、直流潮流约束都检查一遍,再逐步增加风电场数和样本量。数据驱动的调度模型,最关键的不是公式有多漂亮,而是每一个约束在Matlab里都真实、可复现、敢拿到明天的调度台上运行。