☰
微电网多阶段鲁棒调度模型:MATLAB实现与不确定性优化
2026/10/14 7:06:17 网站建设 项目流程

微电网调度这行做久了,最磨人的不是优化算法本身,而是"预测不准"这四个字。风功率和光伏的短期预测误差动辄百分之二三十,调度计划又得提前定,实际出力一偏,不是高价补电就是被迫切负荷。后来我把方案从确定性优化换成了考虑不确定性的多阶段鲁棒调度模型,用 MATLAB 把含可再生能源和储能的最优运行问题完整复现了一遍,今天把建模思路、数学推导和实现细节一次性整理出来,给同样在做微电网调度或者刚接触鲁棒优化的朋友做个参考。

1. 为什么确定性调度在微电网里越来越不靠谱

1.1 预测偏差的真实冲击:从一次不愉快经历说起

我做这套模型的起因很实际。某个测试微电网场景里,光伏中午的预测出力是 1000kW,结果一坨云飘过来,实际出力直接掉到 200kW。而按照确定性优化给出的计划,午间恰恰是购电最少的时段,储能也在上午就放得差不多了。结果就是,缺口只能靠电网高价补,那个时段的实时电价又不便宜,一天下来多花了小两千块钱。

这不是偶然。如果你做过负荷侧和电源侧的预测就会发现,微电网里的风光预测误差分布相当宽,短期预测在好天气下能到 10% 以内,天气不稳定时 20% 甚至 30% 的偏差都是家常便饭。对于确定性模型来说,优化目标再漂亮,结果也只是一条"预测曲线下的最优轨迹",跟实际运行时的物理约束一碰撞,脆弱得很。后来我意识到,调度要解决的核心问题不是"平均值怎么最优",而是"最坏情况下怎么不崩"——这正是我转向鲁棒优化的直接原因。

1.2 从"容错"到"抗最坏":建模思路的转变

确定性模型长这样:

min f(x) s.t. A x ≤ b

其中 x 是购电计划、储能充放电、可中断负荷安排等。所有约束都基于"预测值恰好等于实际值"的假设。

鲁棒优化模型的思路则是另一个态度:不确定参数 u 落在一个事先定义好的集合 U 里,要求所有约束对 u ∈ U 都成立,目标函数则按最坏场景评估。写成最朴素的形式就是:

min_x max_u f(x, u) s.t. g(x, u) ≤ 0, ∀u ∈ U

拿生活类比一下。确定性调度是"听天气预报说中午有雨,就带一把伞";鲁棒调度是"不管天气预报怎么说,都按最坏暴雨天带伞"——额外成本买的是安全边界。对于有储能的微电网来说,这个"带伞"的灵活性来自储能和可中断负荷,怎么把这部分资源用足,是模型设计的核心。

1.3 本文模型与经典两阶段鲁棒的区别

很多朋友一听到鲁棒优化就想到两阶段模型:第一阶段做日前决策,第二阶段根据不确定实现做实时调整。这确实是主流框架。但标题里的"多阶段鲁棒调度模型"在这个语境下,我更愿意把它理解为:以两阶段鲁棒为骨架、包含跨时段递推决策的调度模型。

也就是说,这里的"多阶段"不是简单地化为"日前+实时"两个点,而是把储能 SOC 递推、可中断负荷分时调用这类跨时段耦合约束完整纳入不确定优化框架。每一时段的决策都会影响后续时段的状态,这比单时段静态鲁棒复杂,但也更贴合微电网实际运行。

提示:不同文献对"多阶段鲁棒"的定义有差异。有些论文里多阶段指滚动时域决策,有些指多周期 min-max-min 问题。复现或引用时,要先明确自己模型的定位,我这里的定位是"多时段耦合的两阶段鲁棒经济调度"。

2. 多阶段鲁棒调度模型的数学架构

2.1 不确定集设计:盒式加预算的经典组合

既然要让模型"抗最坏",第一步得先定义清楚"最坏往哪个方向坏"。对光伏和风电的预测偏差,我用的不确定集是盒式集合加预算约束:

U = { ΔP_pv, ΔP_w : ΔP_min ≤ ΔP ≤ ΔP_max, ∑_t ( |ΔP_pv,t| / ΔP_pv,max + |ΔP_w,t| / ΔP_w,max ) ≤ Γ }

其中 ΔP_pv,t 和 ΔP_w,t 分别表示 t 时段光伏和风电出力相对预测值的偏差,ΔP_max 是偏差上限,Γ 是预算参数。

为什么要加预算约束?如果不加,只用一个纯盒式集合,等于允许每个时段的风光偏差同时达到最大值。这在数学上没问题,但实际几乎不可能发生——风不会连续 24 小时都偏到极限,光伏也不会。结果就是模型极度保守,运行成本被人为抬高一大截。预算参数 Γ 相当于"允许同时达到最大偏差的时段总数",旋钮拧到 0,模型退化为确定性调度;拧到 24,就是全空间盒式最保守版本。

2.2 三层结构:min-max-min 问题怎么理解

有了不确定集,模型自然长成 min-max-min 三层嵌套结构:

min_x max_u min_y c^T x + d^T y s.t. A x ≤ b F y ≤ h - E x - G u, ∀u ∈ U x ∈ X, y ∈ Y

三个层级各司其职:

  • 外层 min_x :确定第一阶段决策,比如日前购电计划、储能基准充放电计划、可中断负荷的调用安排。这类决策在不确定实现前就要定下来,无法根据实际场景修正。
  • 中间层 max_u :在不确定集 U 中寻找让运行成本最大的最坏场景。这是鲁棒优化的"恶意仲裁者",它在所有可能的出力偏差里挑一个最伤人的组合。
  • 内层 min_y :在最坏场景 u 下,通过第二阶段的实时调整变量 y(包括储能修正充放电、临时购电、可中断负荷的实际中断量等)把损失降到最低。

一句话概括就是:做计划时要考虑到最坏场景,但在最坏场景到来时,我还有一系列可调节的手段兜底。三层结构反映的是"决策-对抗-响应"的博弈关系。

2.3 关键约束逐一拆解

模型里有几条约束是必须写对的,我列一下重点。

第一,功率平衡约束:

P_grid,t + P_pv,t + P_w,t + P_dis,t = P_load,t + P_ch,t + P_int,t

注意 P_pv,t 和 P_w,t 用的是"预测值 + 偏差",也就是 P_pv,t = P_pv_fore,t + ΔP_pv,t。功率平衡是等式约束,在鲁棒框架里它属于"对变量 u 取最坏情况后仍要满足"的约束,CCG 算法正是靠这个约束在迭代中识别最坏场景。

第二,储能约束。这部分最容易出问题:

SOC_t+1 = SOC_t + η_ch · P_ch,t - P_dis,t / η_dis 0 ≤ SOC_t ≤ C_ess 0 ≤ P_ch,t ≤ u_ch,t · P_ch,max 0 ≤ P_dis,t ≤ (1 - u_ch,t) · P_dis,max SOC_0 = SOC_T = 0.5 · C_ess

其中 P_ch,t 和 P_dis,t 必须互斥,需要引入二进制变量 u_ch,t 或者用 big-M 方式建模。初末 SOC 相等是为了保证调度方案在日内是可持续的——不能让储能一天下来从 50% 放到 10%,第二天就没得用了。

第三,联络线功率约束。微电网和主网的交互不能无限,通常有购电上限和售电上限:

0 ≤ P_buy,t ≤ P_line,max 0 ≤ P_sell,t ≤ P_line,sell,max

第四,可中断负荷约束。这是鲁棒问题里很重要的"软手段":

0 ≤ P_int,t ≤ P_int,max,t P_int,t 带一个较高的成本系数

为什么一定要放进可中断负荷?因为纯鲁棒问题一旦最坏场景下功率平衡无解,整个模型就不可行。可中断负荷相当于给问题一个弹性出口:在极端场景下宁可切除一部分负荷,也要保证系统不发散。它的成本设得比正常购电高,但比失负荷惩罚低,这样优化器不会滥用它。

提示:P_int 的最优值在确定性场景下一般是 0,只有在最坏场景逼近时才会被激活。这个设计让鲁棒模型在数值上稳定得多。

3. MATLAB + YALMIP 实现:从模型到可运行代码

3.1 环境配置与变量定义技巧

我用的组合是 MATLAB + YALMIP + Cplex。YALMIP 的建模语法对状态变量和约束的声明非常友好,省去手动转标准型的痛苦。装好 Cplex 后,在 MATLAB 里通过 yalmip('clear')、sdpvar 和 optimize 三件套就能完成整套流程。

先定义三组变量:

% 第一阶段决策:日前购电量、储能基准充放电、可中断负荷安排 P_buy = sdpvar(24, 1); P_ch0 = sdpvar(24, 1); P_dis0 = sdpvar(24, 1); P_int0 = sdpvar(24, 1); % 不确定变量:光伏和风电的出力偏差 delta_pv = sdpvar(1, 24); delta_w = sdpvar(1, 24);

注意 YALMIP 里 sdpvar 默认是连续变量,主问题里储能充放电互斥需要二进制变量,要用 binvar。Cplex 会自动识别并求解 MILP。

3.2 主问题 MP 与子问题 SP 的交替迭代逻辑

说清楚 CCG 的整体逻辑之前,先明确一下主问题长什么样。第 k 次迭代时,主问题已经积累了若干组"特定最坏场景",形式如下:

min c^T x + θ s.t. A x ≤ b d^T y_j ≤ θ, j = 1..k F y_j ≤ h - E x - G u_j, j = 1..k x ∈ X, y_j ∈ Y

也就是说,每轮迭代发现一个新的关键场景 u_j,就把对应的第二段变量 y_j 和约束切进主问题,同时用一个公共的 θ 逼近那个最坏成本。迭代流程我用伪代码整理一下:

% 初始化:从预测场景(偏差为0)开始 u{1} = zeros(2, 24); LB = -inf; UB = inf; k = 1; while true % 1. 求解主问题MP optimize([cons_MP, cuts], obj_MP, options); LB = value(obj_MP); % 2. 取出第一阶段决策,固定后求解子问题SP x_star = value([P_buy; P_ch0; P_dis0; P_int0]); [sp_obj, u_worst] = solve_SP(x_star); % 3. 更新上界 UB = min(UB, c' * x_star + sp_obj); % 4. 收敛判断 gap = (UB - LB) / abs(UB); if gap < tol break; end % 5. 生成新割平面,加入MP k = k + 1; u{k} = u_worst; add_cut_to_MP(u_worst); end

这个流程跟 Benders 分解有点像,但 CCG 的关键区别在于:每次迭代不是只加一条约束,而是同时加一组新变量 y_k 和相关约束。因为第二段变量是随着场景一起来的,CCG 的收敛速度通常比 Benders 快很多。我实际跑的时候,24 时段模型一般 5-8 轮迭代就能把 gap 降到 1% 以内。

3.3 子问题的最坏场景求解:对偶转换与二元变量处理

子问题是整个模型的"发动机",它负责回答一个问题:给定第一阶段的购电计划和储能基准方案,什么样的风光偏差组合会让运行成本最高?

子问题的标准形式是:

max_u min_y d^T y s.t. F y ≤ h - E x* - G u, u ∈ U

内层 min_y 是一个线性规划,可以用对偶转换成 max 问题,然后和外层 max 合并。对偶转换这一步要特别小心,推导时容易丢项。转换后的子问题里会出现"不确定变量 × 对偶变量"的双线性项,这里我用大 M 法和辅助二元变量把它线性化。

线性化的思路大致是这样:假设出现了一个双线性项 u · λ,其中 u 在一个区间 [u_min, u_max] 里,λ 也有物理边界。引入二元变量 z,把问题拆成分段线性形式。Cplex 处理这类 MILP 很在行,只要 M 取得到位,求解效率还可以。

3.4 收敛判据与割平面构造的关键细节

我一开始踩过一个坑:上界 UB 的更新方式不对。按照模型定义,原始问题等价于:

min_x [ c^T x + max_u min_y d^T y ]

所以每次迭代里,固定 x* 后子问题的最优值 sp_obj 要加上 c^T x*,这才是一个完整的可行上界。如果只拿 sp_obj 当 UB,数值会明显偏低,导致 gap 收敛不到,甚至出现 UB < LB 的荒谬情况。

另外一个细节是割平面要不要把"场景索引"带上。CCG 中每轮添加的约束对应一个具体场景 u_j,这些场景是历史迭代中产生的。如果忘了把对应场景的下标约束写进主问题,等于把一个场景的切面错误地当成所有场景通用,解出来的方案会严重偏保守。我在代码里专门维护了一个 cell 数组来存历史场景,每次构造主问题约束时用循环展开。

4. 算例设计:验证模型有效性的完整流程

4.1 测试系统与基础参数

为了验证模型,我搭了一个 24 时段的小型微电网测试场景。参数如下表:

项目数值
负荷范围1000-2000 kW
光伏预测容量峰值约 800 kW
风电预测容量200-600 kW
储能容量500 kWh
储能最大充/放功率100 kW
储能效率95%
购电上限800 kW
售电上限300 kW
可中断负荷上限100 kW
峰时电价(10-14, 18-22)1.1 元/kWh
平时电价(8-10, 14-18, 22-24)0.65 元/kWh
谷时电价(0-8)0.35 元/kWh
可中断负荷补偿价格2.0 元/kWh
风光偏差上限预测值的 ±20%
预算参数 Γ4(基准)

负荷按典型日设置,早高峰和晚高峰分明,光伏正午最高,风电夜间较强。这个配置比较贴近实际区域微电网的典型特征。

4.2 鲁棒调度结果与确定性方案对比

跑完 CCG 后,我做了三组方案对比:

方案日运行成本最坏场景切负荷量求解时间
确定性调度(Γ=0)约 8500 元约 180 kWh8 s
鲁棒调度(Γ=4)约 9050 元约 20 kWh46 s
鲁棒调度(Γ=8)约 9450 元0 kWh72 s

结果符合预期:确定性方案成本最低,但在最坏场景下要切 180 kWh 的负荷,这在实际运行里可能触发失负荷惩罚,算总账未必便宜。鲁棒方案多花 500 到 1000 元,换来的是最坏场景基本不切负荷,系统抗风险能力显著提升。这个"花钱买确定性"的权衡,我觉得恰恰是建模时要向运行人员解释清楚的核心价值。

4.3 Γ 的灵敏度分析:保守度的旋钮怎么拧

我还专门扫了 Γ 从 0 到 12 的成本曲线。趋势很直观:Γ 增大,运行成本单调上升,但上升速率不同。Γ 在 0-4 区间内成本增长明显,说明最坏场景压力增大后,储能和联络线资源被快速占满;Γ 到了 6 以上,成本增长趋缓,因为系统的灵活性资源已经接近饱和,再多增加不确定空间也只是让约束更紧,但实际可调手段已经耗尽。

这个分析对实际调度特别有价值。如果你所在地区风光波动不算极端,Γ 取 4-6 就是成本和保守度的甜点区;如果历史出现过极端天气,再往上调。盲目把 Γ 拉满,成本爆涨还不一定带来实际运行收益。

5. 复现过程中踩过的坑与调试经验

5.1 对偶转换里"藏着"的常数项

前面提到对偶转换容易丢项,我再细说一下。子问题的内层 min_y 包含右端项 h - E x* - G u,其中 x* 已经固定。对偶转换后,x* 不为零的项会留在目标函数里,u 会留在对偶约束里。我在第一次实现时,把所有常数项都当成了"已知量"丢在外层,结果子问题目标值系统性偏低,CCG 迭代 20 轮 gap 还在 5% 左右下不去。后来逐个检查项,才发现是对偶问题中的常数项没有乘上对应的对偶变量。建议推导完先手算一个 2 时段小例子核对数值,再上完整模型。

5.2 储能 SOC 跨时段耦合带来的求解慢

储能约束是跨时段递推的,这让子问题不像传统两阶段模型那样按时段独立。刚开始我直接在 MP 里对每一轮历史场景都展开 24 个时段的 SOC 递推,场景一多,变量规模暴涨,Cplex 求解时间从几十秒飙到几分钟。后来我做了两步优化:一是把储能充放电互斥约束用 big-M 而不是二进制展开,减少整数变量数量;二是 MP 中预置一组"虚拟场景变量",新场景加入时直接复用,不重复创建同一结构的约束块。实测下来,24 时段模型总求解时间稳定在 1 分钟左右,完全可接受。

5.3 大 M 数值选取的教训

大 M 值不是拍脑袋定的。我第一次偷懒,把大 M 写成 1e6,结果 Cplex 求解器在预求解阶段就把部分约束判成冗余,某些时段的最坏场景识别不准确。后来我根据物理边界推导 M:比如购电功率上限是 800kW,电价上限 1.1 元/kWh,那么电量费用项的合理上界就是 800×1.1×24,约 2 万元。M 取这个量级的两倍,既保证线性化等价,又不至于让数值尺度过于悬殊。Cplex 的参数也可以调优,把 MIP emphasis 从默认改成平衡模式,整数问题收敛得更稳。

提示:如果求解器预处理后提示警告,先检查大 M 是否远超问题本身的物理尺度。把目标函数和约束的数值单位统一(比如用 k 元、MW 而不是元、W)再做一遍,很多数值问题会自然消失。

5.4 结果合理性自检的四板斧

模型跑通不等于结果正确,我每轮仿真完都会做下面四步自检:

  1. 检查每个时段的功率平衡是否严格满足,尤其在最坏场景下。这一步能快速暴露约束漏写或变量引用错误。
  2. 检查最坏场景是否落在不确定集的边界上。理论上,最坏场景应该满足预算约束取等,如果某些迭代里最坏场景的偏差和明显小于 Γ,说明子问题求解可能卡在了局部。
  3. 检查储能 SOC 曲线是否平滑。如果出现锯齿状跳变,十有八九是充放电互斥约束没写对或者效率参数方向搞反了。
  4. 检查收敛后的 UB 和 LB 是否接近。正常情况下 CCG 在 10 轮以内 gap 降到 1% 以下,如果 gap 顽固不降,优先怀疑对偶转换和常数项。

个人经验收尾

最后分享一个我自己的习惯:拿到任何鲁棒优化题目,别急着上大系统,先搭一个 2-3 时段的迷你算例,把所有约束和推导手算一遍,再用 MATLAB 跑同一个算例。这一步能帮你节省至少一整天的调试时间。我在复现这套多阶段鲁棒调度模型时,光是子问题对偶和 CCG 割平面的衔接就返工过两轮,都是靠这个小算例快速定位到问题。

另外想说一点体会:鲁棒优化的本质不是追求一个"绝对最优"的数字,而是帮助你理解系统的脆弱点在哪儿。当你看到 Γ 从 4 调到 6 时成本曲线的斜率变化,你会比只跑确定性模型的人更清楚储能容量的边际价值究竟有多大。这也是做这类复现最有趣的地方。

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

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

立即咨询