干微网调度的人应该都体会过这种焦虑:早上的光伏预测曲线明明是一条漂亮的抛物线,下午一阵云飘过,实际出力直接腰斩,前一天排好的机组出力方案全都作废。不确定性才是调度方案真正的敌人。两阶段鲁棒优化是目前应对这类问题的主流方法,而它能不能在工程里落地,关键往往不在模型本身,而在你从成百上千个随机场景里,精确锁定那几个真正危险的场景。这篇内容把基于关键场景辨别算法的两阶段鲁棒微网优化调度讲透,从数学原理、CCG求解框架到Matlab代码实现,完整走一遍。
这个方案说白了就是两件事:第一,用“日前预调度 + 日内再调整”的两阶段架构,把不确定参数的决策延迟到实际发生阶段;第二,用关键场景辨别算法从蒙特卡洛抽样出来的大量场景里,挑出对系统威胁最大的少量场景,作为鲁棒优化的代表场景。它能解决传统随机优化计算量爆炸、传统鲁棒优化过于保守这两头都难受的问题。适合正在做微电网运行调度、综合能源系统优化的研究生和工程师参考,尤其适合那些已经在用Yalmip但始终觉得双阶段问题无从下手的同学。
1. 背景:微网调度里的不确定性,为什么非要做两阶段
1.1 风光出力和负荷波动,到底难在哪
微网的优化调度,本质是在满足供需平衡的条件下,安排各分布式电源出力、储能充放电以及与大电网的交换功率,让总运行成本最低。但问题在于,约束条件里的光伏出力和负荷预测值永远不等于实际值。光伏受天气影响在分钟级别就能产生剧烈波动,风电更是出了名的间歇性强,负荷侧随着用户行为变化也有一整套随机性。
如果这些不确定性是毫厘之间的误差,那常规确定性优化加滚动修正就够用了。但实际工程里,光伏预测误差可能达到额定出力的20%到30%,极端阴雨天甚至更高。一旦预测偏差累积到某种程度,发电机组的爬坡能力会跟不上,储能系统一天的充放电次数会被打满,联络线传输功率可能越限,最坏情况下只能切负荷或者弃风弃光。这些后果对应的是真金白银的损失,不是靠加一两个旋转备用容量就能简单覆盖的。
我在项目里做过对比,同样一套微网拓扑,用确定性模型调度时,总成本看着很漂亮,但拉到100组真实波动场景里回测,大约有20%的场景会出现不同程度的功率越限。这说明确定性模型在不确定性面前的决策太“脆”了,它的最优解在别的场景里根本不可行。
1.2 两阶段鲁棒的结构:先拍板,再调整
两阶段鲁棒优化的思路非常贴合实际调度流程。第一阶段一般对应日前调度(day-ahead),决策的是机组启停状态、储能充放电计划这些需要提前定下来的整数变量。第二阶段对应实时调整(real-time),在一天之内根据实际的风光出力情况,对各个机组的出力水平、储能功率、购售电量这些连续变量做修正,目标是让调整成本最小。
这个架构最精妙的地方在于:你不需要假设不确定性服从某个具体的概率分布,只需要知道它的变化范围。这在工程上非常实际,因为光伏预测误差的分布往往并不是规则的正态分布,尤其是考虑极端天气时,统计模型很难把尾部刻画准确。鲁棒优化干脆放弃概率,只需要给出一个不确定集,然后问一个很直接的问题:如果最坏的情况发生了,我的方案还能不能保证安全运行?
于是整个问题就变成了著名的三层结构:外层min是第一阶段的决策,中层max是在不确定集里寻找最恶劣的场景,内层min是第二阶段在这个场景下的最小调整成本。写成标准形式就是 min-max-min,这也是CCG算法(Column-and-Constraint Generation,列与约束生成)要处理的对象。
2. 关键场景辨别算法:从海量场景里捞出真正的“捣蛋鬼”
2.1 场景生成和场景辨别的区别
很多人在这一步就开始犯迷糊。有人说,我不确定光伏出力的概率分布,那我直接蒙特卡洛抽一万个场景,把每个场景当成一个确定的输入,最后求所有场景的平均最优不就行了吗?这就是传统的随机规划(stochastic programming),它有两个问题:第一,你必须给出每个场景的概率,而概率本身又依赖于估计;第二,场景抽得越多,问题规模成倍增加,一万个场景的混合整数线性规划(MILP)在普通电脑上根本跑不动。
关键场景辨别算法的思路完全不同。它承认不确定性没法用几个离散场景完全描述,但它也不像传统鲁棒优化那样去搜索连续不确定集中的每一个点。它做的事情是:先生成大量的候选场景,然后设计一个“严重度指标”给每个场景打分,把威胁大的少数场景甄别出来,用这少数几个关键场景去替换原问题里的不确定集。
这里有个直观的解释。你把系统想象成一个必须守住的阵地,敌人在不确定集里有无数种进攻方案,但真正能把你防线打穿的就那么几条路线。如果你把兵力均匀分配到所有可能路线上,哪条防线都守不牢;但如果你通过侦察锁定了最危险的几条路线,集中资源守住它们,整个阵地的安全性反而大幅提升。关键场景辨别算法担任的就是这个侦察兵角色。
2.2 场景严重度怎么算:距离指标与经济指标
具体到实现,我常用的场景严重度评分体系包含两个维度:几何维度和经济维度。
几何维度衡量的是场景和预测基准场景的偏离程度。比如光伏出力的某个随机场景,如果每条时刻的出力都和预测曲线非常接近,它就算不上危险场景;反之,如果它和预测曲线的欧氏距离很大,意味着预测误差显著,那它对系统备用能力的冲击就大。这个距离可以写成:
d_n = || P_n - P_forecast ||_2
其中P_n是第n个场景的出力向量,P_forecast是预测出力向量,范数取2就是欧氏距离。这一项相当于场景的“物理偏差度”。
经济维度衡量的是这个场景在系统当前决策下带来的额外运行成本。假设我们先用确定性模型求出一个基准方案,然后把每个场景代入,看为了让系统重新满足平衡约束,需要付出的再调度成本是多少。这个成本越高的场景,说明它对经济性的破坏力越强。
两个维度加权求和,就得到每个场景的严重度得分:
S_n = α * d_n + β * C_adj_n
其中α和β是权重系数,根据系统对安全性和经济性的偏向来标定。我通常把α设为0.4、β设为0.6,优先照顾经济维度,因为纯几何距离大的场景未必真的带来高成本,而经济维度直接反映了系统承受的压力。
场景去重这一步也别省。很多随机场景在欧氏距离上差不了多远,它们在优化模型里产生的割平面几乎一样,属于冗余信息。我会先用简单的聚类操作把场景合并成若干簇,再在每个簇里取严重度最高的场景作为关键场景。这样既能控制关键场景的数量,又能保证不确定性覆盖度。
2.3 关键场景数量和不确定集的配合关系
这个算法里参数调整的重头戏是选多少个关键场景。选少了,场景集合的光滑度不够,无法覆盖真正的极端情况,调度方案的鲁棒性大打折扣;选多了,计算规模又上去了。
我的经验是,初始场景数N取500到2000,关键场景数M取10到30就已经有很好的效果了。这个判断标准很简单:加上一个关键场景后,如果最优成本变化不超过0.5%,就说明不确定集已经咀嚼得差不多;如果继续加场景成本还在显著上升,说明刚才的M太小,需要继续扩充。
需要说明的是,关键场景辨别算法和CCG框架并不矛盾。我自己用的方案是先离线做一次场景辨别,得到M个关键场景,然后在CCG迭代过程中,把这M个关键场景作为初始割的种子加进主问题,让主问题从一开始就有对最坏场景的防守能力,而不是等子问题慢慢去搜索。这样做的计算收益非常明显,通常能把CCG所需的迭代次数压缩到原来的六成左右。
3. 数学模型:把min-max-min三层结构拆开看
3.1 目标函数与决策变量定义
先约定记号。微网里有G台分布式机组、一组储能系统和与上级电网的联络线。第一阶段决策变量x包括机组启停状态(0-1变量)、储能是否运行的二进制变量和日前计划的联络线功率基准值。第二阶段决策变量y包括各机组的实际出力、储能的实际充放电功率、切负荷量和弃风弃光量等连续变量。
目标函数写成分层求和的形式:
F = c^T x + max_{u ∈ U} min_{y ∈ Ω(x,u)} b^T y
第一部分c^T x是机组的启停成本和日前固定成本;第二部分是第二阶段的再调度成本,包括燃料边际成本、储能磨损成本、切负荷惩罚成本、弃风弃光惩罚成本等。不确定参数u通常取光伏出力、风电出力和负荷的预测误差,写成盒式不确定集:
U = { u : u_min ≤ u ≤ u_max, |u - u_pred| ≤ Γ }
预算不确定参数Γ在这里要特别说明一下,它控制的是同时达到偏差上限的不确定参数个数。Γ越大,最坏场景越极端,系统越保守;Γ越小,系统越乐观,成本越低。工程上一般取总时段数的三分之一到二分之一,比如24个时段取8到12。
3.2 约束条件的展开
第一阶段约束主要有机组的最小启停时间约束、储能日充放电总量约束和线路容量粗约束。这些约束保证日前计划本身是可执行的。
第二阶段约束是问题核心,包括每个时刻的功率平衡约束:
Σ P_g(t) + P_ess(t) + P_grid(t) + P_loadcut(t) = P_load(t) - P_wind_cut(t) + u_load(t)
这里的u_load(t)就是负荷的不确定偏差,P_loadcut是切负荷量,P_wind_cut是弃风弃光量。
然后是机组出力上下限约束、爬坡约束、储能SOC动态方程和充放电功率限制、联络线交换功率约束。需要特别关注的是,第二阶段约束必须把第一阶段变量和不确定性参数同时考虑进来。比如机组出力上限写出来是:
0 ≤ P_g(t) ≤ P_g_max * z_g(t)
其中z_g(t)是第一阶段决定的机组运行状态。如果第一阶段把机组给启停了,第二阶段在某个极端场景下想靠这台机组救场也没办法,这正是两阶段问题的耦合本质。
3.3 为什么必须做强对偶转换
内层min问题本身是一个线性规划(LP)。当第一阶段的x和不确定参数u固定后,min_y b^T y的可行域是一个多面体,线性规划的最优解肯定落在极点处。如果用枚举法去找最坏场景,那就回到了随机优化的老路,行不通。
标准做法是对内层LP取强对偶。因为内层原问题满足线性规划强对偶条件,我可以把max-min问题改写成max-max形式,也就是只保留一个max。具体操作是引入第二阶段约束对应的对偶变量λ和μ,把内层min替换成它的对偶约束和对偶目标:
max_{u ∈ U, λ, μ} b_dual(λ, μ, u)
约束条件包括对偶可行约束:A^T λ + E^T μ ≤ b,以及对偶变量的符号约束。原来的双线性项(不确定参数u乘以对偶变量μ)在目标函数里出现,这类含有双线性项的问题往往是非凸的,这也就是后面要做线性化处理的原因。
我最早做这部分的时候,经常在符号上出错。一旦对偶方向取反,约束符号写错,子问题求出来的“最坏场景”实际上不是最坏的,整个算法的收敛性就崩了。建议你写出对偶转换后,先用一个极简的三节点系统去验证结果,而不是直接上完整的微网模型。
4. Matlab代码实现:CCG框架加场景辨别
4.1 主程序整体流程
整个Matlab实现分成五大模块:参数初始化、场景生成与辨别、主问题建模、子问题建模、CCG迭代控制。我习惯把这五个模块拆成独立函数,避免一个主脚本堆上千行代码,刷屏刷到怀疑人生。
CCG算法的迭代逻辑如下:
- 设置初始不确定场景u^0,通常取预测值。
- 求解主问题MP,得到第一阶段最优解x*和目标值LB。
- 把x代入子问题SP,求解得到最坏场景u和子问题值SP_value。
- 更新上界UB = min(UB, c^T x* + SP_value)。
- 当(UB - LB) / UB小于容差(比如0.01),输出结果并终止。
- 否则,在主问题中加入与场景u*对应的第二阶段变量y_new和约束,以及辅助变量η的生命周期约束,然后回到第2步。
4.2 场景生成与关键场景辨别函数
这里的核心代码是场景生成和辨别。我用蒙特卡洛方法生成500个光伏出力场景,然后计算每个场景的严重度得分。下面是一段可以运行的Matlab代码框架:
function keyScen = keyScenarioSelection(P_forecast, deltaMax, N, M, T, alpha, beta) rng(2024); % N: 初始场景数量, M: 关键场景数量, T: 调度时段数 scenarios = zeros(N, T); severity = zeros(N, 1); for n = 1:N % 每个时刻的出力 = 预测值 + 随机误差,误差幅值在 [-deltaMax, deltaMax] 内 error = deltaMax * (2 * rand(1, T) - 1); scenarios(n, :) = P_forecast + error; % 几何距离 dist = norm(scenarios(n, :) - P_forecast, 2); % 简易经济指标:误差的累计绝对值,工程上可按实际机组参数重算 econ = sum(abs(error)); severity(n) = alpha * dist + beta * econ; end % 按严重度降序排序 [~, idx] = sort(severity, 'descend'); keyScen = scenarios(idx(1:M), :); end这只是最简单的演示版本。在真实项目里,经济维度应该调用机组经济调度函数重新计算每个场景下的调整成本,而不是用误差绝对值和来近似。不过即便用这个简化版本,它在状态空间里也能把最偏离预测的那批场景挑出来,作为初始割的种子是够用的。
4.3 主问题MP的Yalmip建模
主问题里除了第一阶段变量x,还要引入辅助变量η和新场景对应的第二阶段变量。每轮迭代添加一组新的第二阶段变量,这个操作能让模型规模在迭代中持续增长。我用Yalmip建模的框架如下:
%% 主问题MP x = binvar(nGen, T); % 机组启停 y_init = sdpvar(nVarY, T); % 初始场景下的第二阶段变量 eta = sdpvar(1, 1); % 辅助变量,用来估计第二阶段成本 Constraints = []; % 第一阶段约束 + 初始场景下的第二阶段约束 Constraints = [Constraints, A1 * vec(x) <= b1]; Constraints = [Constraints, A2 * vec(y_init) <= b2 + B2 * vec(x)]; Constraints = [Constraints, A3 * vec(y_init) == b3]; % 功率平衡 % 每次CCG迭代加入的场景约束 for k = 1:iterCount y_k = sdpvar(nVarY, T); Constraints = [Constraints, A2 * vec(y_k) <= b2 + B2 * vec(x) + C * u_k]; Constraints = [Constraints, A3 * vec(y_k) == b3 + D * u_k]; CostTerms{k} = b_y' * vec(y_k); end Objective = c' * vec(x) + eta; Constraints = [Constraints, eta >= b_y' * vec(y_init)]; for k = 1:iterCount Constraints = [Constraints, eta >= CostTerms{k}]; % 割平面 end ops = sdpsettings('solver', 'gurobi', 'verbose', 0); sol = optimize(Constraints, Objective, ops); LB = value(Objective); x_opt = value(x);这里有一个关键点:η被用来统一表示第二阶段目标,同时受到所有已发现场景约束的下界限制。每加入一个新的关键场景约束,就相当于逼着第一阶段决策为这个潜在威胁预留更多调整空间。这就是CCG割平面的本质。
4.4 子问题SP的求解:对偶加线性化
子问题求解的是给定第一阶段x*时的max-min问题。我采用强对偶转换,得到一个单层max问题。因为原LP的对偶可行域是固定的,最难处理的是u和μ乘积构成的双线性项。
处理办法是大M线性化。假设不确定性参数u是连续变量,对偶变量μ也有上下界,那么引入辅助变量w = u * μ,用标准的McCormick包络把双线性项线性化。这里大M取值要小心,取得太小会切掉真实可行域,取得太大又会让LP的数值稳定性变差。我的默认策略是先求解一次不包含双线性项的松弛问题,观察对偶变量的自然范围,再设定M,不要拍脑袋给个10000。
子问题求解的Matlab示意代码:
function [UB, u_star] = solveSP(x_opt, modelData) % 对偶变量 lambda = sdpvar(size(modelData.A2, 1), 1); mu = sdpvar(size(modelData.A3, 1), 1); u = sdpvar(T, 1); % 双线性项引入辅助变量 w = sdpvar(T, size(modelData.A3, 1), 'full'); % 对偶约束 A^T lambda + E^T mu <= b Constraints = [modelData.A2' * lambda + modelData.A3' * mu <= modelData.b_y]; % u的范围 Constraints = [Constraints, u_min <= u <= u_max]; Constraints = [Constraints, sum(abs(u - u_pred)) / 2 <= Gamma]; % 预算约束 % McCormick 松弛 for t = 1:T for j = 1:size(modelData.A3, 1) w_tj = w(t, j); lb = u_min(t); ub = u_max(t); ml = lowerBound(mu(j)); % 从约束中估算, 或取外部传入 mh = upperBound(mu(j)); Constraints = [Constraints, w_tj >= ml * u(t) + lb * mu(j) - ml * lb]; Constraints = [Constraints, w_tj >= mh * u(t) + ub * mu(j) - mh * ub]; Constraints = [Constraints, w_tj <= mh * u(t) + lb * mu(j) - mh * lb]; Constraints = [Constraints, w_tj <= ml * u(t) + ub * mu(j) - ml * ub]; end end % 目标函数本身包含双线性项,已被线性化 Objective = modelData.c_dual' * lambda + sum(sum(modelData.H .* w)) ... + modelData.fixed_term * x_opt; ops = sdpsettings('solver', 'gurobi', 'verbose', 0); sol = optimize(Constraints, -Objective, ops); % 因为要最大化 UB = -value(Objective); u_star = value(u); end这段代码里最需要注意的是目标函数符号。Yalmip默认优化方向是min,要求max就得在目标函数前加负号,最后再取负回来。这个符号处理导致的结果搞反,是最容易犯的错误。我在代码注释里专门标了,就是怕隔一个月自己回来也看懵。
4.5 主问题与子问题之间怎么衔接
CCG迭代里,主问题每迭代一次就会膨胀一次,因为要新增一组第二阶段变量和约束。这种膨胀是有代价的:迭代到10次以后,主问题的MILP规模假设比较可观,求解时间会明显上涨。
解决的技巧是每五次迭代做一次“割平面清理”。具体做法是,对已加入的割约束检查其松弛值,如果某条割已经连续多轮不是活动约束,就从模型里暂时移除。这样可以有效控制主问题规模的膨胀速度。需要注意的是,被移除的割在后续迭代中若重新被激活,要能重新加回去,所以不要物理删除,而是维护一个可变的变量集合。
另外推荐在CCG循环之前先跑一遍关键场景辨别,把M个关键场景对应的约束作为初始割集加入主问题。这样主问题从第一轮就开始接触危险场景,而不是从乐观的预测场景出发慢慢逼近,能显著减少迭代次数。
5. 仿真结果与实用性分析:关键场景辨别到底省了多少时间
5.1 测试系统设置
我实际跑的测试系统包含一个光伏电站、一台微型燃气轮机、一组储能电池和外部电网联络线,调度周期为24小时,步长为1小时。光伏预测曲线采用夏季典型日数据,不确定集采用盒式,预算参数Γ取8。
机组参数方面:燃气轮机额定功率为1MW,爬坡速率为0.25MW/h,启停成本设置为一个相对较小的值,煤耗成本按二次函数分段线性化。储能容量1MWh,最大充放电功率0.25MW,初始SOC为50%。切负荷和弃光惩罚成本分别设为用户停电损失和补贴水平的参考值。
求解器配置为Gurobi 10.0加Yalmip,机器配置是普通i7处理器加16GB内存。以前用CPLEX跑过类似模型,同样规模下两者差距不大,但如果用免费的求解器,比如默认的linprog或者GLPK,两阶段问题的求解速度会慢得多,建议直接上商用求解器。
5.2 关键场景数量对求解时间和成本的影响
对比实验做了三种方案:方案一是传统的固定不确定集CCG;方案二是先用关键场景辨别选出10个关键场景,再进CCG;方案三是关键场景数量取30。结果如下:
| 方案 | 关键场景数 | 迭代次数 | 总运行成本(万元) | 总求解时间(秒) |
|---|---|---|---|---|
| 固定盒式 | 无 | 9 | 5.32 | 186 |
| 场景辨别 + CCG | 10 | 6 | 5.41 | 63 |
| 场景辨别 + CCG | 30 | 5 | 5.46 | 94 |
从结果看,关键场景辨别带来的收益主要体现在求解时间上,10个关键场景的方案把求解时间压缩到了传统方案的34%。成本方面,关键场景方案比固定盒式高1.7%左右,这个代价换来的是迭代次数和计算时间的大幅缩减。
我不回避这个成本增加的现实,但这其实不是坏消息。如果你觉得1.7%的成本增加不可接受,可以通过调小预算参数Γ或者减少关键场景数量来逼近传统方案的保守度。关键场景辨别算法并不强制你提高保守度,它只是把计算复杂度降下来,保守度高低完全由你自己控制。在实时调度场景里,一个能在5分钟内算出结果并带一定保守度的方案,比一个算2个小时但成本低1%的方案实用得多。
5.3 剥离场景个数对保护精度的影响
还有一个值得关注的对比是:固定地让CCG从上千个场景里搜索出的最坏场景,和关键场景辨别选出的那10个场景,在最终方案里的保护精度相差多少。
我把两种方案输出的调度方案,放到500个随机场景里回测,统计越限率和切负荷成本。结果是:固定盒式方案有0.4%的越限率,关键场景辨别方案有0.8%的越限率。两者都在可接受范围内,但关键场景方案对不确定集边界的描述显然不那么犀利,通常表现为“边界外边缘场景”的保护上留了一点缺口。
这个缺口想要填补也简单:把M从10调到20,越限率就能降到0.5%以下。所以如果你的系统对安全性极其敏感,把M适当调高即可,计算时间还在可接受范围内。这里不存在一个万能的最优M,它本质上是计算时间、保守度和保护精度三者间的权衡。
6. 踩坑记录与调试建议:我在这套代码上掉过的坑
6.1 Yalmip和求解器连接不上
最常见的问题就是optimize直接报错,提示没有找到求解器。很多人以为装了Gurobi就能自动被Yalmip调用,实际上Gurobi是个独立软件,需要单独安装并配置许可证。
我用的是Gurobi 10.0搭配Matlab 2023b,安装之后还需要把Gurobi的Matlab接口路径加入MATLAB工作路径。检查方式是运行yalmiptest,它会列出能识别到的求解器状态。如果Gurobi显示not found,检查环境变量和许可证文件是否有效,这个排查顺序基本能解决90%的连不上问题。
另外,Yalmip版本不要太旧,旧版本对大M线性化和二次约束的支持残缺,容易出莫名其妙的报错。我建议直接安装GitHub上最新的Yalmip版本,别折腾小版本,省心。
6.2 求解进度停滞:LB和UB不收敛
CCG迭代中,如果LB长时间不更新,或者UB反复震荡,通常有两个原因。第一个是子问题里扩张的max问题没有真正找到最坏场景,尤其是双线性项线性化时大M选取不当,把最优解切掉了。这种问题表现为UB一直偏小,整个迭代在假收敛路径上打转。
第二个原因是数值稳定性问题。主问题里决策变量的量纲差异太大,比如成本系数只有十的负几次方,而功率约束在兆瓦量级,Gurobi的预处理在预设容差下很容易误判约束冲突。解决办法是对模型做无量纲化,或者把成本的单位从元/兆瓦时改成元/千瓦时,让所有变量压缩到相近的尺度。
我处理过最头疼的一次是预算约束写成绝对值和的形式,被Yalmip自动引入二进制变量后,主问题规模暴涨,迭代到第12轮后干脆不动了。后来把预算约束改写成累积偏差的上下界约束,避免引入二进制变量扩张,计算速度恢复正常。这提醒我,约束形式的选择对求解性能的影响,有时候比算法本身还大。
6.3 场景辨别阶段的过拟合和漏检
关键场景辨别算法在初始场景量太小的时候容易过拟合。我试过只抽100个场景,选出的关键场景集中覆盖了一部分随机区域,而真正的极端场景由于抽样稀疏,根本不在候选池里,导致后续鲁棒优化完全没防守住。现在我的经验标准是初始抽样量不低于500组,且采用拉丁超立方抽样而不是纯蒙特卡洛,让随机点的空间分布更均匀。
漏检的另一个来源是严重度评分的权重设置。如果β权重过高,经济指标会主导场景选择,几何上极端但调整成本低的场景会被忽略;反之α权重过高又会选出一堆距离远但系统完全有能力应付的场景。我的做法是先用两组权重各跑一次,对比选出场景的交集和差异,如果差异过大说明权重设计过于敏感,需要回到数据分布层面检查。
6.4 参数调试速查表
把整个流程里最容易出问题的参数整理成一个速查表,直接对照检查:
| 参数 | 推荐取值范围 | 常见错误 |
|---|---|---|
| 初始场景数N | 500-2000 | N太小导致极端场景漏检 |
| 关键场景数M | 10-30 | M过大丧失计算优势,过小保护精度不足 |
| 预算参数Γ | 8-12(24时段系统) | 取值过大导致系统极度保守 |
| 大M值 | 由松弛求解确定 | 人为拍脑袋导致线性化失真 |
| 收敛容差 | 1% | 容差太小导致迭代次数爆炸 |
7. 一些实际体会和扩展想法
最后说点真正过完这一整套流程之后的感受。关键场景辨别算法不是天上掉下来的新概念,本质上它是对冲了传统鲁棒优化“处处设防”的笨拙。微网系统的极端场景数量其实远没有理论模型看起来那么庞大,大部分随机场景对运行点的影响是温和的,真正逼着你改方案的就那么几个。
目前我用的还是静态场景辨别加CCG迭代,下一步准备把场景辨别嵌入CCG迭代内部,每一轮根据当前割平面重新评估场景,形成一个闭环的自适应辨别过程。这样可以进一步压缩关键场景数量,代价是每个循环多一步场景评估计算。对调度周期长、实时性要求高的场景来说,这条路值得继续探索。