搞调度优化这些年,我最大的感受是:高比例可再生能源并网之后,"确定性调度"越来越像在走钢丝。风电、光伏的预测曲线看着挺光滑,真到运行日,实际出力可能和预测差出一大截,而且偏差的"形状"还不稳定——有时候集中在午间,有时候压在晚高峰。换句话说,我们面对的不仅是波动,更是"分布本身都不确定"。这种场景下,传统随机规划里"已知概率分布"的假设变得很尴尬,而纯鲁棒优化又容易把计划憋得过于保守。
这篇想分享的是一套我最近反复调试的框架:基于Min-Max-Max-Min四层优化架构的多能源系统日前-实时两阶段鲁棒调度模型,核心是把Wasserstein分布鲁棒优化(DRO)和CVaR风险管理塞进同一个优化框架里。它解决的问题可以浓缩成一句:在不清楚风光出力真实分布、又不想为个别极端场景过度买单的前提下,怎么把日前计划和实时调整决策一起定下来。如果你做综合能源系统、电力系统优化或者鲁棒优化方向,不管是刚入门还是已经写了几个月代码,下面这些模型拆解、求解器实现和参数整定经验,应该能帮你省下不少弯路。
1. 高比例可再生能源把调度模型的"确定性幻觉"打破了
1.1 风光出力不确定性的本质:不是方差大,而是分布未知
很多人一提到新能源不确定性,第一反应是"波动大"。波动大确实是个问题,但更麻烦的是我们很难知道波动的真实概率分布。风电功率的预测误差会受到天气系统、季节、地形甚至风机老化状态的影响,不同时间窗下的误差分布形态差别很大。早上可能近似正态,台风天就成了重尾分布,光伏在云层快速移动时甚至会出现双峰特征。
你如果用历史数据硬拟合一个分布,得到的只能是一个"平均意义"上的近似。而调度决策恰恰对分布尾部很敏感——某些低概率场景一旦发生,代价可能是切负荷、弃风、设备越限。传统随机规划(Stochastic Programming)假定分布已知,然后用场景树去离散化,这在分布估计准确时尚可一战,但在高比例新能源场景下,分布估计本身就成了最大的误差源。
1.2 从确定性模型到随机规划、传统鲁棒优化的演进:各自的代价
我最早做调度模型时用的是确定性方法:把风光出力固定在预测值上,再留一定比例的旋转备用。可再生能源占比不高时够用,但比例上来后,预测误差造成的备用需求激增,确定性方法要么经济性很差,要么安全性兜不住。
随机规划的想法是"用场景代替单点预测",理论上更精细,但计算量大,而且对场景生成质量要求很高。传统鲁棒优化则走向另一个极端:用盒式(box)或者椭球式不确定集把所有可能出力都包进去,然后要求所有场景下都可行。这个方法思路清晰,但结果往往过度保守——尤其是多能源系统里电气热耦合后,单一环节的保守性会被层层放大,最后算出来的运行成本高到没法用。
1.3 为什么最终选择"分布鲁棒+两阶段+CVaR"的组合
在实际项目里,我们既拿不到精确分布,又不想完全放弃概率信息,于是目光落到**分布鲁棒优化(DRO)**上。DRO的核心思想是:我承认真实分布未知,但我可以用历史数据构造一个包含真实分布的"模糊集",然后在这个模糊集内寻求最坏情况下的最优决策。它比随机规划更稳健,又比传统鲁棒优化更贴近数据,正好卡在两者的中间位置。
至于两阶段,是因为调度决策天然分成"日前"和"实时"两个时间尺度:日前定启停、备用,实时根据实际出力做再调度。CVaR的引入则是因为调度员关心的不只是平均成本,更关心尾部损失——也就是那些很少发生但一发生就代价巨大的场景。把CVaR放进目标函数,相当于给"极端风险"设置了一个明确预算,而不是笼统地要求所有场景都绝对安全。
这三个工具组合起来,就形成了标题里那套Min-Max-Max-Min四层优化架构。下面我从两阶段模型的运行逻辑开始拆。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 日前-实时两阶段框架的运行逻辑和数学结构
2.1 两阶段决策在物理上意味着什么
两阶段框架不是数学家的玩具,它对应的是实际调度流程。日前阶段,你要在风光出力还没实现之前,决定机组的启停状态、备用容量、联络线交换计划、储能是否充电等"慢决策"。这些决策通常要提前几个小时甚至一天确定,因为机组启停有最小时间约束,市场交易也要提前申报。
实时阶段,等到风光出力大致明朗了,再根据实际偏差做"快决策":调整常规机组出力、调用备用、储能充放、必要时切负荷或弃风。这个阶段的时间窗口只有几分钟到几十分钟,变量基本是连续量。
可以类比成出差:日前是订航班和酒店(必须提前定,决定了你的基本行动范围),实时是到了当地之后,根据天气和交通临时调整出行路线(在酒店和航班给定的前提下尽量省时间)。
2.2 第一阶段与第二阶段变量的划分与目标函数写法
两阶段分布鲁棒模型的紧凑形式可以写成:
[
\min_{x} \left{ c^T x + \sup_{P \in \mathcal{B}_\varepsilon(\hat{P}_N)} E_P \left[ V(x, u) \right] \right}
]
其中 (V(x, u)) 是第二阶段的最优再调度成本:
[
V(x, u) = \min_{y} ; d^T y
]
[
\text{s.t.} \quad G y \ge g - B x - C u,\quad y \in \mathcal{Y}
]
这里:
- (x) 表示第一阶段变量,包括机组启停 0-1 变量、日前出力计划、备用容量、储能初始状态等;它的可行域里要体现出力上下限、最小启停时间约束。
- (y) 表示第二阶段变量,包括实时出力调整量、储能充放功率、切负荷量等。
- (u) 是风光出力等不确定量,通过矩阵 (C) 进入第二阶段约束右侧。
- (P) 是风光出力场景的概率分布,(\hat{P}N) 是由历史场景得到的经验分布,(\mathcal{B}\varepsilon) 是以 (\hat{P}_N) 为球心、半径 (\varepsilon) 的Wasserstein模糊集。
这个写法里,最外层的 (\min) 是日前决策,(\sup) 是"最坏分布",内层 (V(x,u)) 又包含一个 (\min y) 的实时再调度。为什么这样分?因为第二阶段约束矩阵 (G)、系数 (d) 都只和第二阶段决策有关,第一阶段决策 (x) 通过 (B x) 项影响第二阶段的可行空间——比如你没开某台机组,实时阶段就不能调用它的出力。
2.3 为什么两阶段比分阶段独立优化更优
两阶段模型最关键的一点,是把"实时再调度成本"放进了日前决策的目标里。如果你先单独优化日前计划,不理会实时可能发生的调整,算出来的成本只是表面好看。等到了实时阶段,偏差一出现,燃料费、备用费、切负荷惩罚全冒出来,总成本可能比两阶段联合优化高出不少。
数学上这句话对应的是"后悔值"(regret)的概念:确定性日前计划在某个场景 (u) 下的后悔值,等于"给定日前计划后实时最优调整成本"减去"如果提前知道 (u) 再优化出的理想成本"。两阶段模型的最小化对象是"日前成本+最坏分布下的期望调整成本",本质上是在为后悔值买单,而单阶段模型根本不考虑这笔账。
另外,两阶段模型还隐式保证了"可调性":如果某个场景下第二阶段可行域为空,说明日前计划在该场景下无法通过实时调整来满足安全约束,这是调度上绝对不能接受的。这也是后面C&CG算法里要生成"可行割"的原因。
3. 四层Min-Max-Max-Min架构拆解:每一层都在对抗什么问题
3.1 从经典两阶段鲁棒 min-max-min 出发
经典两阶段鲁棒优化是三层结构:
[
\min_{x} \max_{u \in \mathcal{U}} \min_{y \in \mathcal{F}(x,u)} d^T y
]
第一层 (\min x) 寻找最优日前决策,第二层 (\max u) 在不确定集 (\mathcal{U}) 中寻找最坏场景,第三层 (\min y) 在给定场景下做最优再调度。这个结构直观、好懂,也是很多论文的起点。但它的不确定集 (\mathcal{U}) 通常是盒式或椭球式,里面每个场景等权看待,没有用到历史数据的分布信息。
3.2 分布鲁棒引入后的双重最坏:分布与场景叠加
当你把Wasserstein分布鲁棒优化加进来之后,情况就变了。你不光要面对"哪个场景最坏",还要面对"哪个分布最坏"。因为真实分布不确定,调度计划必须对模糊集内所有分布都说得过去,而最坏的那个分布,往往会把概率质量尽可能堆到最糟糕的几个场景上。
于是原来的一层 (\max u) 变成了两层 (\max):一层是 (\max_{P \in \mathcal{B}}) 找最坏分布,另一层是 (\max_{u \in \mathcal{U}}) 找最坏场景。加上最外层的日前 (\min) 和最内层的实时 (\min y),整体就是:
[
\min_{x} \max_{P \in \mathcal{B}\varepsilon(\hat{P}N)} \max{u \in \mathcal{U}} \min{y \in \mathcal{F}(x,u)} d^T y
]
这就是标题里"Min-Max-Max-Min"四层结构的来源。实际求解时,由于 (\max_P) 和 (\max_u) 都是"寻找最坏",在固定 (x) 后可以将两层 (\max) 合并为一个联合 (\max)((\max) of (\max) 等于联合 (\max),前提是 (u) 的支撑集不受 (P) 的选择影响,或者你可以把支撑集合并进模糊集定义里),四层从计算角度看又回到min-max-min的可分解形式。但从建模角度看,保留两层 (\max) 能更清楚地表达"分布不确定"和"场景不确定"是两个不同来源的风险。
3.3 四层结构的整体表达式与物理含义
把CVaR也加进来后,四层结构可以更一般地写成:
[
\min_{x} \left{ c^T x + \sup_{P \in \mathcal{B}\varepsilon(\hat{P}N)} \mathrm{CVaR}\beta^P \left[ \max{u \in \mathcal{U}P} \min{y \in \mathcal{F}(x,u)} d^T y \right] \right}
]
这里的 (\mathrm{CVaR}_\beta^P) 表示在分布 (P) 下、置信水平 (\beta) 的条件风险值。物理含义很直白:调度员在最坏分布下,关注的是再调度成本的尾部平均值,而不是简单的期望。这样设计有两个好处:一是模糊集里那些"看似概率很小但损失巨大"的场景不会被平均成本稀释掉;二是通过调节 (\beta) 和模糊集半径 (\varepsilon),你可以连续地控制保守程度,而不是非黑即白。
我第一次跑通这个四层结构的算例时,最直观的感受是:它把"不确定性"这件事拆成了可以分别调参的两个风险源。如果这周天气预报可信度高,就把 (\varepsilon) 调小;如果调度员对极端天气特别敏感,就把 (\beta) 调大。相比传统鲁棒优化一棍子打死所有场景的做法,灵活性高多了。
4. Wasserstein模糊集:把"分布不确定"转化为可计算的约束
4.1 Wasserstein距离的定义与搬土直觉
Wasserstein距离也叫最优传输距离(earth mover's distance)。直觉上,把概率分布 (P) 想象成堆在地面上的土,把分布 (Q) 想象成目标土堆,Wasserstein距离就是"把 (P) 这堆土搬到 (Q) 这个形状所需要的最小运输成本",其中单位土从位置 (u) 搬到位置 (v) 的成本由地面距离 (c(u,v)) 决定。
数学定义是:
[
W(P,Q) = \inf_{\pi \in \Pi(P,Q)} \int c(u,v) , d\pi(u,v)
]
其中 (\Pi(P,Q)) 是所有边缘分布分别为 (P) 和 (Q) 的联合分布。相比KL散度这类基于密度比值的度量,Wasserstein距离对"支撑集位置不同"的两个分布也能给出合理度量——这在实际风电出力场景里太重要了:预测偏差不仅体现在概率权重上,还体现在出力数值偏移上。
4.2 模糊集构造与半径选择
在分布鲁棒优化里,模糊集通常以经验分布 (\hat{P}_N) 为中心:
[
\mathcal{B}_\varepsilon(\hat{P}_N) = \left{ P ;|; W(P, \hat{P}_N) \le \varepsilon \right}
]
这个球半径 (\varepsilon) 的选择,直接决定模型的保守程度。从理论上讲,在一定的矩条件下,真实分布 (P^*) 与经验分布 (\hat{P}_N) 的Wasserstein距离可以控制在 (O(1/\sqrt{N})) 量级,所以随着历史样本数 (N) 增多,(\varepsilon) 应该逐步减小。实际中我一般这样处理:
- 先把历史场景做场景削减,得到 (N) 个典型场景及其权重,构成经验分布 (\hat{P}_N);
- 用交叉验证的方法,对一组候选 (\varepsilon)(比如0.01、0.02、0.05、0.1倍场景平均范数)分别求解模型,统计样本外成本;
- 选样本外成本开始明显抬升的那个 (\varepsilon) 作为最终半径。
如果 (\varepsilon) 取0,模型退化成纯随机规划;如果 (\varepsilon) 取得非常大,模糊集几乎包含所有分布,模型又退化回传统鲁棒优化。所以实际调参时,我习惯画一条"(\varepsilon)-总成本"曲线,选曲线拐点,而不是拍脑袋定一个数。
4.3 对偶变换:如何在求解器中处理无限维的max问题
分布鲁棒优化在理论上漂亮,但真正让它能落地的,是对偶变换。(\max_{P \in \mathcal{B}_\varepsilon} E_P[V(x,u)]) 这个问题的变量 (P) 是一个概率分布,无穷维,没法直接丢给求解器。好在Wasserstein模糊集的对偶形式很成熟,通过拉格朗日对偶可以把它转化成有限维问题,引入对偶变量 (\lambda \ge 0) 和 (s_i),最终得到一个包含所有历史场景、有限个不等式约束的凸优化问题。
这里有一个关键前提:内层函数 (V(x,u)) 对 (u) 要满足一定的凸性/连续性条件。在我的模型里,第二阶段是个线性规划,其值函数 (V(x,u)) 是 (u) 的凸函数,恰好满足要求。对偶变换之后,原问题变成了可以交给Gurobi、CPLEX这类求解器直接处理的形式。这也是为什么后面我会在上层套C&CG算法,而不是自己去写什么"分布优化求解器"。
5. CVaR风险管理:给尾部损失一个明确的"预算"
5.1 VaR和CVaR:为什么VaR解决不了调度问题
在风险管理里,(VaR_\beta) 是置信水平 (\beta) 下的分位数损失,意思是"有 (\beta) 的概率损失不会超过这个数"。听起来挺合理,但它有个致命缺陷:它只告诉你分界点在哪,完全不关心超过分界点之后损失有多大。两个分布可能具有相同的VaR,但一个尾部很薄,一个尾部极厚——调度员显然更怕后者。
CVaR则弥补了这个缺陷:它定义的是"损失超过VaR之后的平均损失"。用保险来类比,VaR相当于免赔额,CVaR相当于免赔额之上的平均赔付金额。对于调度决策来说,你要的不是"大概率不出事",而是"出事之后代价可控",这正是CVaR的用武之地。
5.2 把CVaR嵌入目标函数并线性化
在样本场景离散的情况下,CVaR有一个非常方便的线性化写法。设再调度成本为 (Z_k)(对应第 (k) 个场景),分布权重为 (p_k),则:
[
\mathrm{CVaR}\beta(Z) = \min{\alpha} \left{ \alpha + \frac{1}{1-\beta} \sum_{k} p_k (Z_k - \alpha)^+ \right}
]
引入辅助变量 (t_k \ge 0) 和约束 (t_k \ge Z_k - \alpha),就能把 ((Z_k - \alpha)^+) 线性化。把这项加进目标函数时,我用权重 (\lambda \in [0,1]) 把期望成本风险和尾部风险整合起来:
[
\min_x ; c^T x + \lambda \cdot \mathrm{CVaR}_\beta(Z) + (1-\lambda) \cdot E[Z]
]
(\lambda) 越大,决策者越厌恶尾部风险。(\beta=0) 时CVaR退化成期望,(\beta \to 1) 时CVaR趋近最坏场景损失,所以CVaR天然地连接了随机规划和鲁棒优化这两个极端。
5.3 CVaR与Wasserstein DRO如何协同
Wasserstein DRO和CVaR不是二选一的关系,它们解决的是两个维度的问题:DRO管的是"分布不知道",CVaR管的是"尾部损失要控制"。
如果只用DRO不加CVaR,模型对分布偏差有鲁棒性,但最坏分布下的期望成本可能被大量普通场景稀释,尾部极端事件依然没被突出。如果只用CVaR不加DRO,又回到了"先假设一个分布,再算尾部"的老路,分布估计一旦有偏,CVaR再精细也是空中楼阁。
两者叠加的效果是:先在Wasserstein球里找到最不利的分布,再在这个最不利分布下把尾部损失压到可接受范围。实际算例里,我见过 (\varepsilon) 和 (\beta) 联动时目标成本从"过保守"到"不过保守"的平滑过渡,这种可调节性是单一风险工具很难做到的。
6. 多能源系统调度中的关键约束与耦合关系
6.1 电-热-气网络平衡与设备模型
多能源系统的调度比纯电力系统复杂,因为能量流不再只有一条路。典型的园区级综合能源系统里有电锅炉、燃气锅炉、CHP热电联产机组、P2G电转气设备、电储能和热储能。CHP机组同时产生电和热,电出力与热出力之间有一个可行运行域(通常用多边形描述);P2G则把多余风电转化为天然气,把电力和天然气网络耦合在一起。
每个时刻,系统都要满足:
- 电功率平衡:机组出力、风光出力、储能放电之和 = 电负荷 + 电锅炉耗电 + P2G耗电 + 储能充电;
- 热功率平衡:CHP余热、燃气锅炉、电锅炉供热之和 = 热负荷 + 热储能充放热;
- 天然气平衡:气源供气 + P2G产气 + 管存释放 = 燃气机组/锅炉耗气 + 气负荷。
这些约束在模型里就是一组线性等式/不等式。但要注意,电、热、气的单位不一致,量纲差异会造成目标函数里各项数值相差悬殊,求解器收敛变慢。我通常会把热功率和天然气流量都折算成等效电功率,或者对变量做归一化处理。
6.2 不确定性参数如何进入约束:场景支撑与传播
风光出力作为不确定性参数,直接进电功率平衡约束。如果系统里有P2G,风电不确定性还会通过P2G进一步传导到天然气网络;而热负荷的不确定性则影响热平衡。在两步阶段模型里,不确定参数 (u) 进入所有相关约束的右侧,实时阶段通过调整机组出力、储能、切负荷来平衡。
如果同时考虑风、光、热三类不确定参数,场景维度会非常高。我一般先对历史数据做相关性分析,剔除强相关的分量,再用K-means或后向削减法生成典型场景集。经验上,50到200个场景已经足够支撑Wasserstein模糊集,再多场景只会增加计算负担,不会显著改善解的质量。
6.3 实际建模时容易被忽略的时间耦合约束
多能源系统里最容易翻车的地方,是时间耦合约束。储能SOC递推、机组爬坡率、最小启停时间、热网蓄热惯性,这些约束把相邻时段连在一起,不能在两阶段模型里被忽略。
特别是,第一阶段决策 (x) 一旦固定,第二阶段 (y) 必须遵守这些时间耦合约束的"底层框架"。比如,如果日前已经决定某台CHP机组在时刻 (t) 关停,实时阶段就不能再让它开机发电——这正是第二阶段可行域 (\mathcal{F}(x,u)) 依赖 (x) 的原因之一。我一开始偷懒,只建了各时段独立的平衡约束,算出来的"最优解"在实时校验时发现部分场景下根本无法执行,后来把启停状态作为0-1变量放进第一阶段、把出力调整量放进第二阶段,结果才合理。
7. 模型求解实战:C&CG算法的主-子问题框架与代码逻辑
7.1 为什么要用C&CG而不是直接调用求解器
四层Min-Max-Max-Min结构里含0-1变量,又有 (\max \min) 的子结构,直接整体丢给Gurobi通常是解不动的——就算解一个很小的算例,也要耗上几个小时。实际工程里,主流做法是**列与约束生成(C&CG)**算法:把原问题拆成主问题(MP)和子问题(SP),通过迭代不断把最坏场景对应的约束加入主问题,直到上下界收敛。
C&CG适合这类问题的原因在于:它不需要枚举所有不确定场景,每次迭代只从子问题里找"当前最容易击穿约束"的场景。就像一个质检员,每次只盯着最可能出问题的那批产品,修完再看下一批。
