☰
源荷不确定性下的电力系统调度:Matlab+Yalmip建模与优化实战
2026/10/24 4:44:53 网站建设 项目流程

做电力系统调度这块的,这几年应该都有个很深的感触:以前调度的核心矛盾在“怎么分”,现在变成了“怎么扛”。分布式光伏、风电的比例一上来,源侧和荷侧的不确定性被无限放大,传统确定性调度模型做出来的计划,在实际执行的时候往往对不上号——不是高峰时段缺出力,就是低谷时段弃风弃光。这个“电力系统调度之源荷不确定性”的题目,本质上就是在解决一个问题:当你知道明天的负荷和新能源出力都不是一个确定值时,你该怎么提前把机组出力计划排得既经济又安全。

这篇文章我打算拆开揉碎地聊一聊,基于Matlab和Yalmip工具箱,从建模思路到代码实现,再到我在实际调试里踩过的坑,完整梳理一遍源荷不确定性调度问题的技术脉络。适合正在做电力系统优化调度方向研究的学生、刚入行做调度策略的工程师,以及想把不确定性问题落地到代码里的从业者。

1. 源荷不确定性到底在“不确定”什么

1.1 不确定性的两个源头:出力侧与负荷侧

电力系统的实时平衡等式,左边是发电出力,右边是负荷需求加上网损。传统火电时代,发电机组的出力可控可调,负荷预测精度虽然有限,但误差在可控范围内,所以调度计划基本是“开环预测+闭环校正”的思路。但新能源大规模接入以后,情况变了。

风电机组的出力取决于风速,光伏出力取决于光照强度,两者都是强随机变量。一个风电场的出力波动可能在几分钟内达到装机容量的百分之二三十,这已经不是传统调频备用能轻易消化的量级。我在之前一个模拟项目里测过,某风电场在夏季午后的5分钟内,出力从180MW跌到120MW,这样的速度如果不提前在日前计划里留足余地,实时运行基本只能靠紧急切负荷来扛。

同样地,负荷侧的确定性也在下降。电动汽车无序充电、空调负荷占比提升、用户侧储能的双向充放电,都在让负荷曲线变得更“毛”。传统负荷预测基于历史同期数据加温度修正,现在这套办法的误差显著放大,尤其是在节假日和极端天气前后,负荷预测误差超过5%是常有的事。

这两类不确定性叠加起来,就带来一个问题:调度计划如果只按预测值做单个场景的优化,那么一旦实际值偏离预测,要么出现功率不平衡,要么出现备用容量被大量占用,要么出现机组频繁调节、煤耗上升。源荷不确定性研究的本质,就是把“未来可能发生的多种情况”纳入优化模型,让决策结果对不确定性具有“免疫能力”。

1.2 为什么确定性调度会失效

确定性调度模型,也就是常说的单场景优化,目标函数是总运行成本最小,约束里把负荷预测值和新能源预测值当作已知常数。这样做的好处是模型简单、求解快,但代价是结果对预测误差高度敏感。

我用一个简化的两机组系统说明一下。假设系统有火电A机组(最小出力100MW,最大出力300MW,煤耗成本系数较高)和火电B机组(最小出力50MW,最大出力200MW,煤耗成本系数较低)。预测负荷是350MW,预测风电出力是100MW,那么净负荷是250MW。优化结果大概率是让A机组出150MW、B机组出100MW,总成本最低。

但如果实际风电出力只有60MW,净负荷变成290MW,A机组需要上调到190MW才能满足平衡。如果A机组的爬坡速率是2MW/min,上调40MW需要20分钟,这个时间窗口内系统可能已经出现频率偏差。更麻烦的是,如果B机组在优化结果里已经被压到最小出力50MW,而实际净负荷低于预测值,B机组已经没有下调空间,只能弃风。

这个例子说明,确定性调度的问题不在于优化算法本身,而在于它对“未来只有一个可能”这个假设过于乐观。实际运行中,预测误差是必然事件,不是偶然事件,所以调度模型必须考虑不确定性的概率分布或其取值区间。

1.3 不确定性建模的三条主流路线

学术和工程实践中,处理源荷不确定性主要有三条路线:随机规划、鲁棒优化、机会约束规划。三条路线各有侧重,适合不同的场景需求。

随机规划的思路是:为不确定参数构建多个典型场景,每个场景有对应的概率,目标函数变成所有场景下运行成本的期望值最小化。它的优势是经济性好,因为它在概率意义上做了加权;劣势是需要知道不确定参数的概率分布,而且场景数量多了以后计算量会大幅上升。

鲁棒优化的思路是:只关心不确定参数的取值范围(区间),或者构建一个不确定集,保证在最坏情况下约束仍然满足。它的优势是不需要概率分布,鲁棒性强,非常适合安全要求极高的场合;劣势是过于保守,过度牺牲经济性,尤其是不确定区间设置得比较大的时候。

机会约束规划的思路是:允许约束在一定的置信水平下被违反,比如“系统功率平衡约束被满足的概率必须大于等于95%”。它介于随机规划和鲁棒优化之间,在可靠性和经济性之间做了权衡;劣势是求解时需要把机会约束转化为确定性等价形式,转化过程往往涉及分布假设,复杂度较高。

实际项目里,我做源荷不确定性调度时,经常用的是随机规划的场景法加鲁棒优化的区间法做对比,两者在Yalmip里都能方便地建模,方便从不同角度审视调度结果。

2. Matlab和Yalmip在调度建模中的角色定位

2.1 为什么选Matlab做电力系统调度建模

电力系统调度优化,本质上是一个大规模混合整数线性规划(MILP)或二次规划(QP)问题。Matlab在矩阵运算和科学计算上有天然优势,电力系统的节点导纳矩阵、机组组合状态变量、功率平衡约束,这些用矩阵表达非常自然。

和Python相比,Matlab的Optimization Toolbox提供了fmincon、intlinprog等求解接口,而且和Yalmip、Matpower这些工具箱配合得很成熟。做电力系统研究的学术圈子使用Matlab非常普遍,遇到问题找人讨论、参考现有代码都方便。另一个现实原因是,很多高校和研究机构的电力系统仿真平台还是基于Matlab的,比如Simulink里的电力系统模型可以直接和调度优化代码对接,形成仿真-优化闭环。

我个人的体验是,Matlab写调度模型的代码可读性比较好,建模语言接近数学表达,逻辑清晰不容易出bug。而且Matlab的调试工具(断点、变量查看)比命令行友好很多,对于动辄几百个变量、上千条约束的调度模型来说,这一点非常实用。

2.2 Yalmip是什么:建模语言与求解器的桥梁

Yalmip是Matlab环境下的一个免费优化建模工具箱。它本身不求解任何问题,而是把用户用符号变量写的约束和目标函数,自动转化为特定求解器能够识别的标准形式。你可以把它理解为建模语言和求解器之间的翻译器。

举个例子,你在Yalmip里定义一个决策变量x和约束x >= 0,再定义目标函数x^2,然后调用sdpvar定义变量,用optimize求解。Yalmip会自动识别这是一个二次规划问题,然后调用你指定的求解器(比如Cplex、Gurobi、Mosek或Matlab自带的quadprog)去解。

Yalmip的核心价值有两个。一是符号建模。你写的代码和论文里的数学公式几乎一一对应,比如constraints = [P >= 0, sum(P) == demand];这种写法,直观且不易出错。二是求解器自适应。Yalmip支持多种求解器,同样的模型代码,只需要改一行指定求解器的参数,就能在Gurobi和Cplex之间切换,方便对比不同求解器的性能。

在调度领域,Yalmip的另一个好处是支持二进制变量和混合整数模型。机组启停状态这种0-1决策变量,配合连续变量一起建模,Yalmip都能处理,最终交给MILP求解器去求解。

2.3 调度模型的标准数学形式

用Yalmip建模之前,先把调度优化问题写成标准数学形式,这一步非常关键。一个典型的日前调度模型包含以下几个要素:

目标函数是所有机组运行成本之和,包括煤耗成本、启停成本,以及新能源弃电惩罚成本。煤耗成本一般用二次函数表示,但在MILP中常做分段线性化处理。如果是纯线性模型,也可以用线性成本系数近似。

约束条件分为几类:

  • 系统功率平衡约束:所有机组出力加新能源出力等于负荷需求。
  • 机组出力上下限约束:每台机组的出力在最小技术出力和最大技术出力之间。
  • 机组爬坡约束:相邻时段之间,机组出力变化量不能超过爬坡速率限制。
  • 最小启停时间约束:机组一旦启动或停机,必须保持该状态至少若干小时。
  • 备用容量约束:系统旋转备用容量必须满足一定比例要求。

这些约束里,机组最小启停时间和爬坡约束是整数变量和连续变量耦合最紧的地方,也是模型求解难度最大的地方。在Yalmip里,把机组启停状态定义为binvar变量,机组出力定义为sdpvar变量,然后用线性不等式把它们关联起来。

3. 源荷不确定性调度的核心建模方法

3.1 场景法:用多个“如果”代替一个“预测”

场景法处理不确定性的思路很直白:既然未来的负荷和新能源出力有好多种可能,那就把这多种可能都用场景表示出来,每个场景对应一组预测值,带一个概率权重。目标函数变成所有场景下运行成本的期望值:

[ \min \sum_{s \in S} \pi_s \cdot C_s(x, y_s) ]

其中,(\pi_s)是场景s的概率,(C_s)是场景s下的运行成本,x是一阶段决策变量(机组启停状态等),(y_s)是场景s下才能决定的二阶段变量(具体出力、备用等)。

在Matlab中实现场景法的步骤如下:

先要生成场景。假设负荷预测误差服从正态分布,新能源出力预测误差可以用Beta分布或正态分布近似。用拉丁超立方采样从分布中抽取样本,生成比如200个初始场景。

接着做场景缩减。200个场景直接带入优化模型求MILP,计算量可能是天文数字。实际工程中通常用同步回代消除法(fast forward selection)把场景缩减到10~20个,保证代表性同时控制计算量。场景缩减的原理是,把距离最近的场景合并,用概率加权代替原来的多个场景,使得缩减前后的概率分布差异尽量小。

最后把缩减后的场景带入Yalmip模型求解。场景法求解出来的机组组合方案,不是针对单一预测值的最优方案,而是在概率意义上让期望成本最小化的方案。实际运行中无论哪个场景发生,系统都有对应的调度策略可用。

我在一个包含10台火电机组和2个风电场的测试系统上做过对比:单场景模型求解时间为2秒,期望运行成本是105万元;用15个场景的随机规划模型求解时间为40秒,期望运行成本是112万元。看起来随机规划的成本更高,但这多出来的7万元实际上是“保险费”——它换来了在不确定性场景下约束不被违反的可靠性。

3.2 鲁棒优化:不依赖概率分布的安全边界

鲁棒优化的思路完全不同于场景法。它不求所有场景的期望最优,而是保证在不确定参数取值范围内的任何情况下,系统约束都得到满足,同时优化目标函数(一般是成本或最坏情况下的成本)。

鲁棒优化的标准形式是:

[ \min_x \max_{u\in U} f(x, u) ]

其中u是不确定参数,U是不确定集。在调度问题里,通常把负荷需求和新能源出力作为不确定参数,U定义为区间 ([ \hat{u} - \Delta, \hat{u} + \Delta ]),或者更精细的盒式/椭球式不确定集。

用Yalmip求解鲁棒优化调度模型,需要把“对所有u都成立”的约束转化为等价的可求解形式。对于区间不确定集,功率平衡约束可以转化为在最坏情况下的等效约束。最坏情况一般是:负荷取区间上限同时新能源出力取下限,即净负荷最大的情况。这样鲁棒模型就退化为一个确定性的“最坏场景”模型,求解难度不大。

但纯鲁棒优化的保守性让我吃过苦头。在一个案例里,不确定区间设置得比较宽(负荷±10%,风电±30%),鲁棒优化给出的计划成本比确定性模型高了20%以上,而且在很多时段强制开机了额外的机组,以保证极端情况下的容量充裕。这在学术上很好看,但在实际经济调度里很难被接受。

所以现在主流的做法是两阶段鲁棒优化,也叫自适应鲁棒优化。第一阶段决定机组启停状态,第二阶段在最坏场景下决定机组出力,通过列与约束生成算法(C&CG)迭代求解主问题和子问题。在Yalmip里,两阶段鲁棒优化比单阶段复杂,但Yalmip提供了bilevel和robust的扩展支持,可以借助Yalmip内置的鲁棒建模机制来实现。

3.3 机会约束规划:在可靠性和经济性之间找平衡

机会约束规划的思路是允许约束以小概率违反,这个概率也就是置信水平。比如要求系统功率平衡约束以95%的概率满足,那么允许5%的极端场景下功率不平衡(可以通过切负荷或弃风处理)。

在数学上,机会约束可以写成:

[ Pr{Ax \le b} \ge 1-\epsilon ]

其中(\epsilon)是允许的违反概率。在Matlab中处理机会约束,最常用的做法是把不确定参数假设为服从某种分布,然后计算出约束的确定性等价形式。

比如,假设负荷需求(d)服从均值为(\mu)、标准差为(\sigma)的正态分布,那么约束 (P_{gen} \ge d) 以 (1-\epsilon) 的概率成立,等价于:

[ P_{gen} \ge \mu + \Phi^{-1}(1-\epsilon) \cdot \sigma ]

其中(\Phi^{-1})是标准正态分布的分位数函数。这样就把带概率的约束转化成了带有安全裕量的确定性约束。安全裕量是分位数乘以标准差,置信水平越高,安全裕量越大,成本越高。

我在实际中更喜欢用场景法近似机会约束,就是把每个场景下的约束都写成约束条件,然后保持约束数量不变,只调整约束的右端项。这一方法实现简单,和Yalmip配合得好,不用求分位数,对非正态分布也适用。

4. Matlab+Yalmip实操:从零搭建不确定性调度模型

4.1 基础数据与参数初始化

开始写代码之前,先把系统参数准备好。我一般用一个结构体或者Excel表格来管理所有数据,避免在代码里硬编码。以下是一个典型测试系统的参数清单:

  • 机组数量N:10台火电机组
  • 调度时段T:24小时(也可以取96个15分钟时段,但计算量会大不少)
  • 每台机组的参数:最大出力Pmax、最小出力Pmin、爬坡速率、启停成本、煤耗成本系数
  • 负荷预测曲线:24个时段的预测负荷值,以及误差标准差
  • 风电预测曲线:24个时段的预测出力值,以及误差标准差
% 机组参数示例:第i台机组的成本系数和出力范围 % a(i), b(i), c(i) 为煤耗二次函数的系数 % Pmin(i), Pmax(i) 为出力上下限 % Ru(i), Rd(i) 为向上/向下爬坡速率 % Cstart(i), Cstop(i) 为启停成本 N = 10; % 机组数 T = 24; % 时段数 % 成本系数(仅示意,实际数据根据机组型号设定) a = [0.012 0.014 0.017 0.019 0.021 0.023 0.025 0.027 0.029 0.032]; b = [14.5 15.2 15.8 16.3 17.1 17.5 18.2 18.9 19.5 20.1]; c = [650 680 720 750 790 820 860 900 940 980]; Pmax = [320 300 280 260 240 220 200 180 160 150]; Pmin = [80 75 70 65 60 55 50 45 40 35]; Ru = [60 60 55 50 50 45 45 40 40 35]; Rd = [60 60 55 50 50 45 45 40 40 35]; Cstart = [3000 3000 2800 2600 2400 2200 2000 1800 1600 1400];

把数据单独放一个脚本里,后续建模脚本直接引用,这样参数调整和实验对比会很方便。我习惯用load_system_data.m来统一加载,每次跑不同算例只需要改数据文件,不用动主程序。

4.2 确定性调度模型:先立一个Baseline

在引入不确定性之前,先写一个确定性调度模型作为基准。这样做的好处是,后续所有不确定模型的代码都是在确定模型的框架上叠加,出问题时容易定位。

确定性调度模型在Yalmip里的写法如下:

% 定义决策变量 % u(i,t) = 1 表示机组i在时段t处于开机状态,0 表示停机 % P(i,t) 为机组i在时段t的出力 u = binvar(N, T, 'full'); P = sdpvar(N, T, 'full'); % 目标函数:运行成本最小 % 煤耗成本采用线性化近似:c(i) + b(i)*P,忽略二次项,或使用分段线性化 objective = 0; for i = 1:N for t = 1:T objective = objective + b(i) * P(i,t) + c(i) * u(i,t); % 启停成本单独加,需要比较u(i,t)和u(i,t-1)的变化 end end % 约束条件 constraints = []; % 出力上下限约束 for i = 1:N for t = 1:T constraints = [constraints, Pmin(i) * u(i,t) <= P(i,t) <= Pmax(i) * u(i,t)]; end end % 功率平衡约束 load_demand = load_forecast; % 24x1列向量,负荷预测值 for t = 1:T constraints = [constraints, sum(P(:,t)) == load_demand(t)]; end % 爬坡约束 for i = 1:N for t = 2:T constraints = [constraints, P(i,t) - P(i,t-1) <= Ru(i)]; constraints = [constraints, P(i,t-1) - P(i,t) <= Rd(i)]; end end % 求解 options = sdpsettings('solver', 'gurobi', 'verbose', 2); optimize(constraints, objective, options); % 提取结果 P_opt = value(P); u_opt = value(u);

功率平衡约束这里,sum(P(:,t)) == load_demand(t)表示所有机组出力之和等于该时段负荷,是一个等式约束。在Yalmip中,等式约束用==表示,不等式约束用<=或>=表示,非常直观。

运行完这段代码,可以得到确定性调度的机组组合方案和总成本。这个结果就是后续所有不确定性模型对比的Baseline。

4.3 场景法随机规划模型的具体实现

接下来在确定性模型的基础上,改造为场景法的随机规划模型。核心改动有两个方面:一是把负荷和风电用多个场景表示,二是目标函数从“单场景成本”变成“所有场景期望成本”。

代码实现思路如下:

% 生成场景 % 假设负荷预测误差服从正态分布,标准差取预测值的3% % 假设风电预测误差服从正态分布,标准差取预测值的10% num_scenarios = 15; % 场景缩减后的数量 % load_forecast 为24x1的预测负荷 % wind_forecast 为24x1的预测风电出力 % 用拉丁超立方采样生成初始场景,再用场景缩减得到15个典型场景 % 场景抽样与缩减函数(简化示例) [scenario_load, scenario_wind, prob] = generate_scenarios(load_forecast, wind_forecast, num_scenarios); % 决策变量:第一阶段变量(机组启停状态) u = binvar(N, T, 'full'); % 第二阶段变量:每个场景下的机组出力 P = sdpvar(N, T, num_scenarios, 'full'); % 目标函数:期望运行成本 objective = 0; for s = 1:num_scenarios for i = 1:N for t = 1:T objective = objective + prob(s) * (b(i) * P(i,t,s) + c(i) * u(i,t)); end end end % 约束条件 constraints = []; % 第一阶段约束:启停变量的一致性(此处简化为无约束) % 第二阶段约束:各场景下的出力约束和功率平衡约束 for s = 1:num_scenarios for i = 1:N for t = 1:T constraints = [constraints, Pmin(i) * u(i,t) <= P(i,t,s) <= Pmax(i) * u(i,t)]; end end for t = 1:T % 功率平衡:该场景下所有机组出力 + 该场景风电出力 = 该场景负荷 constraints = [constraints, sum(P(:,t,s)) == scenario_load(t,s) - scenario_wind(t,s)]; end end % 爬坡约束:每个场景内成立 for s = 1:num_scenarios for i = 1:N for t = 2:T constraints = [constraints, P(i,t,s) - P(i,t-1,s) <= Ru(i)]; constraints = [constraints, P(i,t-1,s) - P(i,t,s) <= Rd(i)]; end end end % 求解 options = sdpsettings('solver', 'gurobi', 'verbose', 2); optimize(constraints, objective, options);

这里的关键在于,P被定义成了三维变量(N,T,num_scenarios)。在Yalmip中,多维sdpvar是支持的,使用下标时注意维度对应关系就行。如果场景数量较多、机组数量较大,这个模型会变得非常大,求解时间可能从几秒涨到几分钟。我在实际中一般把场景控制在10到30个,再多的话就需要专门的求解策略或者简化模型。

场景生成和缩减是这个环节里最容易出问题的地方。拉丁超立方采样的优点是样本覆盖性好,用较少的样本就能比较好地反映分布特征。场景缩减我推荐用MATLAB自带的kmeans做近似缩减,或者写一个简单的同步回代消除算法。如果不想在这个环节花费太多时间,可以直接用15~20个按分位数选取的场景,效果也不会太差。

4.4 两阶段鲁棒优化的代码框架

两阶段鲁棒优化比随机规划复杂,但用Yalmip仍然可以实现。代码框架的核心是C&CG算法:主问题是鲁棒模型的一阶段问题,子问题是在给定一阶段解后寻找最坏场景下的运行调整量。

主问题的标准形式如下:

% 主问题:决定机组启停状态u % 目标函数:最小化基础运行成本,加上子问题反馈的调整成本 % 子问题的变量:给定u后,在最坏场景下调整出力幅度 P_adjust = sdpvar(N, T, 'full'); % 切负荷变量(松弛变量,用于保证子问题在极端情况有可行解) load_shed = sdpvar(T, 1, 'full'); % 最坏场景下的负荷需求和风电出力 % 实际负荷需求 = 预测值 + 最大偏差 worst_load = load_forecast + delta_load_max; % 实际风电出力 = 预测值 - 最大偏差 worst_wind = wind_forecast - delta_wind_max; % 子问题约束:给定u,调整出力以满足功率平衡 constraints_sub = []; for i = 1:N for t = 1:T % 机组出力的调整量,受限于启停状态和上下限 constraints_sub = [constraints_sub, Pmin(i) * u_opt(i,t) <= P_adjust(i,t) <= Pmax(i) * u_opt(i,t)]; end end for t = 1:T constraints_sub = [constraints_sub, sum(P_adjust(:,t)) + load_shed(t) == worst_load(t) - worst_wind(t)]; end % 子问题目标:最小化调整成本,加上切负荷惩罚成本(惩罚系数设大) sub_objective = sum(sum(b .* P_adjust)) + 10000 * sum(load_shed);

C&CG的迭代过程是:先求解主问题得到机组启停状态,然后把启停状态代入子问题求最坏场景下的调整方案,如果子问题对当前启停状态不可行或不满足要求,就把对应的最坏场景作为新的约束加入主问题,重新求解主问题,直到收敛。

在Yalmip里实现C&CG需要注意,每一轮迭代都要重新构建并求解子问题,而主问题随着迭代轮次增加而不断变大,所以求解时间会逐轮增加。我在一个20台机组的系统上跑过,迭代6轮左右收敛,总求解时间大约是随机规划模型的1.5倍。

4.5 机会约束模型的代码实现

机会约束模型在Yalmip中最简单的实现方式是“场景近似法”,也就是把N个蒙特卡洛样本作为约束条件,要求其中至少((1-\epsilon) \times N)个样本满足约束。

Matlab代码框架如下:

% 生成N个样本,代入约束 num_samples = 500; % 样本总数 samples_load = load_forecast + randn(T, num_samples) * sigma_load; samples_wind = wind_forecast + randn(T, num_samples) * sigma_wind; % 构建约束:对每个样本,要求功率平衡等式或不等式 constraints = []; for k = 1:num_samples % 引入辅助变量表示功率不平衡量 slack_pos = sdpvar(T, 1); slack_neg = sdpvar(T, 1); for t = 1:T constraints = [constraints, sum(P(:,t)) - samples_load(t,k) + samples_wind(t,k) <= slack_pos(t)]; constraints = [constraints, samples_load(t,k) - samples_wind(t,k) - sum(P(:,t)) <= slack_neg(t)]; end % 统计每个样本下的不平衡量,用于机会约束表述 end

这种方式的缺点是模型规模会随着样本数线性增长,500个样本可能让模型大到无法在合理时间内求解。所以我建议先用少量样本(比如50个)试跑,确认模型可解后再增加样本量。

5. 不确定性调度的场景应用与案例对比

5.1 案例一:风电高渗透率下的日前调度

我用一个风电装机占比40%的测试系统做了对比实验。系统共有10台火电机组,总装机容量2200MW,风电装机容量1500MW,典型日的负荷曲线在1200MW到1800MW之间波动。

确定性模型的结果是总运行成本158万元,但检查各时段可以发现,有两个时段的备用容量低于安全阈值,也就是说一旦风电出力低于预测值,系统将面临备用不足的风险。随机规划模型(15个场景)的结果是总运行成本173万元,备用容量在所有场景下都满足要求。鲁棒优化模型的结果是总运行成本192万元,备用容量裕度最大,但机组启停次数也最多,因为需要频繁调整机组组合来应对最坏场景。

这个对比直观展示了三种模型的不同取向:确定性模型最省钱但风险最大,随机规划在经济性和安全性之间取得了平衡,鲁棒优化最安全但代价最高。在实际应用中,可以根据系统对可靠性的要求程度来选择合适的模型。

5.2 案例二:极值场景下的系统应对能力

另一个值得关注的问题是,不确定性模型在极值场景下的表现。我构造了一个极端场景:晚间负荷高峰时段,风电出力几乎为零,同时负荷比预测值高出8%。在这个场景下,确定性模型由于没有预留足够的向上备用,出现约120MW的功率缺口,必须通过切负荷来维持平衡。随机规划模型由于在场景集中包含了类似的高净负荷场景(虽然概率较低),提前启用了额外的机组,功率缺口很小。鲁棒优化模型则完全没有功率缺口。

这个案例说明,随机规划和鲁棒优化本质上都是在用“事前成本”换取“事后可靠性”。具体选哪个模型,取决于系统调度人员对风险的容忍程度以及市场环境下的电价激励机制。

5.3 结果分析与评价指标

评价不确定性调度模型的效果,我习惯看以下几个指标:

  • 期望运行成本:所有场景下的加权平均成本,反映经济性。
  • 最坏场景运行成本:反映极端情况下的成本风险。
  • 备用容量充足率:各时段可用备用容量与需求备用的比值,反映安全性。
  • 切负荷期望值:所有场景下切负荷量的概率加权值,反映可靠性。
  • 求解时间:模型的实用性约束,超过可接受时间则需要简化。

这些指标用Matlab计算非常方便,从优化结果中提取P和u的值,再对每个场景做一次功率平衡校验,统计失衡时段和失衡量即可。

6. 常见问题与调试经验实录

6.1 Yalmip模型求解慢或者内存不足怎么办

这是我在实际项目里遇到最多的问题。第一次跑一个10台机组、24时段、15个场景的随机规划模型时,Gurobi直接显示内存不足,进程被杀。排查后发现,问题出在场景生成阶段:200个原始场景全部代入模型,变量数量爆炸。

解决办法有两个方向。一是场景缩减一定要做到位,把原始场景从200缩减到15~20个,模型规模会缩小到原来的10%左右。二是约束稀疏化,检查约束矩阵中是否存在大量重复或冗余的约束,用Yalmip的numel和nnz命令检查变量和约束数量,发现异常就及时调整。

如果缩减之后仍然太慢,可以考虑把24个时段拆分成多个子问题,或者采用滚动时域优化的思路,把日前调度拆成几个8小时的子问题分别求解。

6.2 模型不可行怎么定位

模型不可行是调度建模里最折磨人的问题之一。约束条件过多或者互相矛盾时,求解器会报“infeasible”,但不会告诉你是哪条约束出了问题。

我的调试方法很粗暴但有效:逐组约束排查法。先把所有约束注释掉,只保留功率平衡约束和一个机组的出力上下限约束,确认模型可解;然后逐步加入其余机组、加入爬坡约束、加入启停约束,每一步都重新求解。这样能快速定位是哪一类约束导致不可行。

另外要注意,功率平衡约束是等式约束,而爬坡约束限制了机组出力的变化速度,如果负荷曲线在相邻时段波动过大,而爬坡速率设置得不够,就会出现不可行。这时候给等式约束加一个小的松弛变量(比如允许1MW的偏差),能帮助你判断是不是平衡约束过紧。

6.3 场景缩减对结果的影响有多大

我在不同系统上测过,场景缩减对结果的影響不可忽略。用200个原始场景求解的期望成本作为基准,缩减到50个场景时,期望成本偏差在1%以内;缩减到15个场景时,偏差在3%左右;缩减到5个场景时,偏差可能超过10%。

所以场景数量的选择要看精度要求。如果只是做方案预研,15个场景足够;如果要拿结果做经济核算或者对照论文数据,建议至少保留30个场景。Yalmip本身不限制场景数量,限制的是你的内存和等待时间。

6.4 Yalmip与商用求解器的坑

Yalmip支持Gurobi、Cplex、Mosek等商用求解器,但我遇到过一个很隐蔽的坑:不同求解器对模型的处理方式不同,同样的模型在Gurobi上解出来的最优解目标值,和Cplex解出来的可能会在小数点后第三位有差异。这个差异本身正常,但如果目标函数值数量级很大(比如上千万),小数点后第三位的差异对应到成本可能就是几千块,需要警惕。

另一个坑是求解器许可证的问题。Gurobi和Cplex都有免费的学术许可证,但计算容量有限制。如果你要跑的场景数量很多、机组数量很大,可能超出免费许可的规模限制。这时候可以换用开源求解器SCIP或HiGHS,Yalmip也支持,不需要改模型代码,只需改solver选项。

7. 扩展方向与我的实操体会

7.1 从日前到日内:滚动修正的力量

日前调度模型解决的是“明天怎么运行”的问题,但实际运行中需要不断根据最新的预测数据修正计划。我做过的滚动调度方案是:每15分钟重新求解一次未来4小时的调度计划,把最新获取的负荷和风电预测值作为输入,更新机组出力设定点。

这个方案和本文的日前模型代码大部分兼容。只需要把时段数从24改成16(4小时×15分钟),然后每次滚动时把当前时刻的实际运行状态作为初始条件即可。效果上,滚动调度比单纯日前调度的运行成本低5%左右,而且对预测误差的适应能力明显更强。

7.2 把统计特征嵌入调度模型

另一个我比较推荐的扩展方向是:不止考虑预测误差的均值,把预测误差的时程相关性、空间相关性也纳入模型。比如相邻时段的负荷预测误差往往有正相关性,如果忽略这个相关性,模型会低估风险。在场景生成阶段,用历史误差数据训练一个向量自回归模型来生成场景,比简单地独立正态抽样更贴近实际。

这个方向在Matlab里可以用Econometrics Toolbox实现,生成的场景再代入Yalmip的调度模型,效果比独立抽样好不少。

7.3 给后来者的几点建议

从确定性模型到不确定性模型,不是简单的代码改动,而是一次思维方式的转变。确定性模型里,你只需要关心“预测值是多少”;不确定性模型里,你要时刻问自己“如果不是这个值会怎么样”。

我在做这个课题的过程中,最大的体会是:不要一开始就追求复杂的模型。先把确定性的调度模型写对、跑通、调好,再逐步加入不确定性要素。每一步都验证前一步的结果,这样即使出了问题,也能快速定位是模型问题还是参数问题。

另外,场景法、鲁棒优化、机会约束这几条路线没有绝对的优劣之分,关键是你所处的应用场景需要什么样的保证。如果你做的是区域电网的实时调度,可能机会约束更合适;如果你做的是微电网的日前计划,场景法可能更实用;如果你做的是黑启动或者极端天气应对方案,鲁棒优化是更稳妥的选择。

最后再分享一个小技巧:多跑几个对比实验,把不同模型的结果摆在同一个坐标图里看。我一般用Matlab的plot函数,把负荷曲线、风电出力曲线、各机组的出力计划画在同一张图上,很容易发现模型的异常行为,比如机组频繁启停、出力曲线振荡、备用容量不足等。图上的直觉判断,往往比数值指标更先暴露问题。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询