☰
微电网鲁棒优化调度:两阶段CCG求解与Matlab实现
2026/10/20 8:25:32 网站建设 项目流程

做微电网调度的同行应该都有这种体会:预测曲线画得再漂亮,一到现场,风电和光伏的出力照样跟你玩心跳。这篇博文要讲的是一个我自己反复验证过的方案——用鲁棒优化做区域微电网的最优运行,把风光出力和负荷预测的不确定性直接框进模型里,配合储能进行可调整调度,同时保证方案是非预测性的,也就是不依赖未来随机信息的实现值。本文将给出完整的模型推导、CCG求解思路和Matlab实现框架,适合正在做微电网能量管理的研究生、工程师,以及刚接触鲁棒优化的同行参考。

我先说结论:这类问题本质上不是“怎么把代码写出来”,而是“怎么把你的保守度定下来”。鲁棒优化最怕的不是计算量大,而是你把不确定集框得太大,导致储能整天满充满放、柴发一直高负荷,运行成本吓人;框得太小,又等于没做。这个度的把握,是需要靠算例、靠对模型结构的理解一点点磨出来的。

1. 先搞清楚一个本质问题:微电网调度到底难在哪

1.1 确定性优化的标准套路和它的天花板

典型的区域微电网,一般包含风电、光伏、储能、柴油发电机,还可能和外网有购售电交互。传统的确定性优化是这样做的:先对风电、光伏、负荷做24小时预测,然后把预测值当成真实值,求解一个以运行成本最小为目标的线性规划或混合整数规划。

目标函数通常是火电/柴发燃料成本加启停成本,再加向电网购电的费用,减去售电收益;约束则是功率平衡、储能SOC递推、充放电功率上下限、柴发出力上下限和爬坡约束,以及线路或变压器容量限制。这种方式的好处是模型简单、求解快,一个24时段的MILP交给商业求解器几秒钟就出结果,所以现在大量工程系统还在用。

但它的天花板也很明显:方案质量完全取决于预测精度。你算出来的“最优”,是在假想场景下的最优,真实现场偏差一大,这个解可能根本不满足功率平衡。我见过一个实际案例,某微网项目中午光伏实际出力比预测低了23%,储能按原计划在充电,柴发又处于最低技术出力附近,结果系统只能紧急从电网高价购电,一个午间时段就把一天的备用裕度吃掉了大半。

1.2 预测误差怎么变成真金白银的损失

预测误差不是简单“多买一点电”就完事的事。它的传导链条是这样的:光伏/风电出力偏差 → 母线功率不平衡 → 储能被迫改变充放电 → SOC轨迹偏离计划 → 晚高峰可调容量不足 → 柴发出力上调或高价购电。如果偏差发生在极端天气下,还可能触发切负荷或弃风弃光,这部分损失是没法用成本模型衡量的。

我常打一个比方:确定性优化相当于你出门前看了一眼天气预报,觉得“今天不会下雨”,于是没带伞。而实际情况是,天气预报说20%概率下雨,你走到半路真的被淋了,全身湿透还耽误了正事。鲁棒优化的思路是:不管天气预报怎么说,我包里都放一把伞,但我不一定全程打着它——根据当时实际是否下雨,我可以决定要不要把伞撑开。这就是“可调整”的含义。

1.3 鲁棒优化要解决的三个核心问题

在进入建模之前,先把鲁棒优化要回答的三个问题摆出来,后面所有代码和公式都是围绕这三件事展开的:

第一,不确定性怎么描述。不能只给一个预测值,要给出一个“不确定集”,也就是风光和负荷可能出现的偏差范围。

第二,最坏情况下方案是否仍然可行。所谓鲁棒,就是不确定集里任何一个偏差场景出现,系统都能通过储能、柴发、购电等调节手段维持功率平衡,不切负荷、不出界。

第三,决策能否根据实际观测进行调整。这是标题里“非预测性”的核心含义:我不能依赖未来的随机信息,但我可以在看到当前偏差后做出响应。这个逻辑要在模型结构里体现出来。

2. 不确定性建模:选对“参数框子”等于成功一半

2.1 哪些参数该进不确定集

微电网模型里参数很多,但不是所有参数都要往不确定集里塞。我的经验是遵循一个原则:只有那些“在决策时点真的不知道、事后才能看到”的量,才需要建模为不确定参数。

通常需要进不确定集的有三类:风电出力预测值、光伏出力预测值、负荷预测值。如果微电网参与电力市场交易,现货电价波动很大,那购售电价也需要放进不确定集。而储能效率、柴发爬坡率、设备容量这类物理参数,基本由设备铭牌决定,实测数据不会出现数量级的跳变,直接用确定值就好。

这里有一个新手常犯的错误:把负荷和光伏同时放进不确定集,却忽略了两者之间可能存在的相关性。比如夏季午后,光伏出力高的时候空调负荷也高,这两个偏差方向在某些时段是相关的。如果不考虑,你的不确定集就会框入一些物理上根本不会同时出现的组合,结果就是白白增加保守度。

2.2 三种常用不确定集对比

不确定集的选择直接决定模型的计算复杂度和保守程度。工程上最常用的有三种,我放在一起对比一下:

不确定集类型数学形式特点计算复杂度适用场景
盒式区间集|u - u_pred| ≤ Δ每个参数独立波动,最保守低初步验证、参数敏感性分析
椭球集(u - u_pred)ᵀΣ⁻¹(u - u_pred) ≤ Ω体现参数相关性,形状更“贴合”数据中(需二阶锥约束)数据充足、相关性明显的场景
预算多面体集盒式约束 + Σ|u - u_pred|/Δ ≤ Γ限制同时偏离的参数个数低(线性)工程最推荐,兼顾保守度和求解效率

我做实际项目时基本都用预算多面体集。原因很简单:它只在线性约束里加一个绝对值求和不等式,对偶之后仍然是线性规划,CCG算法迭代起来非常顺畅。椭球集虽然理论上更精致,但子问题对偶后会出现二阶锥约束,调试难度上一个台阶,而且对工程结果的实际提升往往不如你多花点功夫校正预测偏差。

2.3 预算参数Γ到底怎么定

预算参数Γ是整套模型里最需要“人味”的一个参数,它决定了你愿意为不确定性付出多少额外成本。Γ = 0时模型退化为确定性优化,Γ = T(T为时段数)是最保守情况,假设每个时段所有参数同时达到最大偏差。

我的做法是分两步走。第一步,把手头历史预测数据和实际出力数据拿出来,逐时段算绝对误差,取90%~98%分位数作为单个参数的偏差幅值Δ。这个值不能取最大误差,否则默认设备每个时刻都以最恶劣状态波动,没有任何经济性。第二步,Γ从较小的值开始试,比如5或8,观察成本和可行性怎么变化,一直加到不再显著改善最坏场景表现为止。

有一个小技巧:你可以用确定性模型的“备用容量缺口”来反推Γ。比如确定性方案在历史最坏场景下缺了40kW功率,而你储能和购电容量合计能补的范围是有限的,于是就可以估算这40kW对应的偏差组合大概需要多大的Γ才能覆盖。这比拍脑袋给Γ靠谱得多。

3. 两阶段鲁棒优化模型:min-max-min怎么理解和求解

3.1 第一阶段和第二阶段分别定什么

两阶段鲁棒优化的结构,用一句话概括就是“先决策、后实现、可调整”。第一阶段决策是“事前决策”,必须在不确定参数实现之前就定下来,对应于现场调度里的机组启停、柴发基准出力和储能基准充放电计划。第二阶段决策是“事后调整”,在不确定参数的实际值暴露之后,通过储能、柴发微调、购售电等手段去弥补功率不平衡。

换到生活场景里更好理解:你出差前决定带两套正式衣服和两套运动服(第一阶段决策),到了现场看到真实天气(不确定参数实现),再决定今天穿哪一件、明天穿哪一件(第二阶段决策)。但约束是你不管遇到降温还是下雨,都不能出现没衣服可穿的尴尬场面,这就是“在任何场景下可行”。

数学上,两阶段鲁棒优化的标准形式写成:

min_x cᵀx + max_{u∈U} min_{y∈Ω(x,u)} dᵀy

这个结构看起来吓人,其实拆开就清楚:外层min是在选第一阶段决策x,内层的max-min是在已知x的前提下,寻找最坏的不确定参数u,并计算该场景下最经济的事后调整成本y。这个“内层先求最坏场景”的设计,就是鲁棒优化和传统随机优化的根本区别——它不需要知道不确定参数的概率分布,只需要知道可能出现的范围。

3.2 目标函数与约束的建模细节

我在实际建模时,目标函数取三段成本之和:柴发的燃料和启停成本、向电网购电的峰谷费用、储能的退化成本。储能退化成本在国内不少项目里会被忽略,但如果你把储能当成主力调节手段,没有退化成本,模型会让储能一天内频繁充放,实际运行中电芯寿命下降非常快。我在自己项目里按放电电量线性计提,单位成本约0.05~0.1元/kWh,省心很多。

约束方面,功率平衡是最核心的等式约束,再加上各设备的容量限值、爬坡约束、储能SOC递推和购售电限制。储能SOC递推等式是一个跨时段耦合约束,这是两阶段模型里的一个坑:如果直接把它放进第二阶段,子问题会通过调整SOC“事后修正”整个日内的充放策略,这等于让模型作弊——真实运行中你不可能知道未来所有偏差后再重排全天计划。

我的处理办法是,把储能SOC拆成两部分:一部分是第一阶段确定的计划轨迹,另一部分是第二阶段看到偏差后的增量调整。增量调整的大小必须限制在较小范围内,比如不超过容量的10%,这样既保留了储能的调节能力,又不会让模型“预知未来”。这个细节很多论文里不讲,但工程落地时非常关键。

3.3 为什么max-min不能直接求解

内层max-min结构之所以麻烦,是因为外层在找最坏场景,内层在找最经济调整,两者对抗,本质是个非凸问题,没法直接交给线性规划求解器。处理它的办法主要有两条路。

第一条是强对偶转换。由于第二阶段是线性规划,决策变量连续,满足强对偶条件,可以把内层的min问题转成对偶max问题,从而把内层变成一个纯max问题,这样整个子问题就变成“先对偶、再求max”,可以直接求解。这条路径是CCG算法的理论基础。

第二条是仿射决策规则。它的思路更直接:假设第二阶段调整量y是偏差u的线性函数,即y = a + Bu,把内层min消掉,把问题变成只关于a和B的规划问题。好处是可以处理带有整数变量的场景,坏处是线性策略可能丢失最优性。我在储能参与调节的场景里经常用仿射规则做快速初步分析,精度已经够用。

3.4 CCG算法迭代逻辑

CCG(列与约束生成)是目前求解两阶段鲁棒优化最主流的算法,思路简单说就是“不断把最坏场景拉出来,让主问题被场景逼着改进”。主问题(MP)在已知一批最坏场景的前提下,求最优的第一阶段决策和目标下界;子问题(SP)在给定第一阶段决策后,搜索新的最坏场景,返回目标上界。上下界之差收敛到阈值,迭代结束。

初始化:场景集合S = {一个初始场景(通常取预测值场景)},UB = +inf,LB = -inf 循环 while (UB - LB) / LB > 0.01: 求解MP:给定S,得到x*和目标值LB' 更新LB = max(LB, LB') 求解SP:固定x*,通过强对偶或仿射规则求最坏场景u*和目标值f* 更新UB = min(UB, cᵀx* + f*) 把u*加入场景集合S 结束循环

我实际调试的经验是,CCG一般迭代10~20步左右就会收敛到1%的间隙,但如果子问题的对偶转换写错,可能出现“上界永远小于下界”的荒诞情况。遇到这种问题,先拿一个单场景回代验证,别急着调求解器参数。

4. “非预测性”到底指什么:与MPC滚动时域的组合

4.1 非预测性决策的真实含义

“非预测性”这个词翻译自Non-anticipativity,意思是第一阶段决策不能使用未来随机参数的实现值。换句话说,决定早上8点柴发该出多少功率的时候,你不能等到下午2点看清光照情况再拍板。这个性质在数学上体现为第一阶段决策变量与不确定参数无关,它是两阶段鲁棒优化的隐含假设。

这个性质为什么值得单独讲?因为很多从随机规划转过来的同学,习惯用场景法或者蒙特卡洛模拟,把大量未来场景当作已知信息来优化。这在单阶段随机优化里没问题,但在实时调度里是站不住脚的——真正的控制器每一时刻只掌握当前和过去的信息,未来是未知的。鲁棒优化天然满足非预测性,分布式控制、能量管理系统才能放心地把方案执行下去。

4.2 开环鲁棒和滚动时域的差别

如果你把两阶段鲁棒模型一次求解24小时,然后把这24小时的指令全盘执行,这套方案是开环的。开环的优点是全局最优性有保证,缺点是如果实际偏差超出了你预设的不确定集,比如突然来了一场大风导致光伏骤降,那么后续时段的最优性就无从谈起了。

把MPC滚动时域的思想加进来以后,情况就不一样了。每个控制周期t,只用当前实测状态做初值,预测未来N个时段,求解一个未来有限时域的两阶段鲁棒模型,但只执行第一个时段的指令。等下一时段到来,用新的实测状态重新求解。这就让不确定集每步都在“收缩”,因为离当前时刻越近,预测偏差越小,你框出来的U就越紧凑,运行经济性也会更好。

4.3 鲁棒MPC实操框架

具体操作上,我一般把滚动窗口设为6~12个小时,太长计算量大、太短会失去前瞻性。Γ在滚动窗口内取2~4,意思是在未来6小时里,允许最多2~4个时段的参数同时达到最大偏差。储能初值用当前实测SOC,第一个时段的指令下发执行,第二时段重新更新。

组合滚动时域之后,“非预测性”依然是满足的:每个时刻的决策只依赖当前实测状态、预设不确定集和窗内预测曲线,不依赖未来随机变量的实现值。这就是标题里“鲁棒性和非预测性解决方案”的落地含义——方案既有对抗不确定性的鲁棒自卫能力,又能在时间轴上一步一个脚印地滚动修正,非常适合部署在实际的能量管理系统中。

5. Matlab实现框架与核心代码逻辑

5.1 程序整体构架

我写这类程序一般分六个模块:数据模块、参数模块、不确定集模块、主问题模块、子问题模块、CCG迭代与结果输出模块。数据模块读入预测曲线和历史误差统计数据,参数模块配置成本系数、储能容量、柴发爬坡等常数,不确定集模块按2.3节的方法生成偏差幅值和预算Γ。

主问题模块用Yalmip建模,调用商业求解器CPLEX或Gurobi;子问题模块先对第二阶段LP做对偶转换,再用Yalmip建模求解对偶后的max问题。CCG迭代模块负责维护场景集合、更新上下界、控制循环终止条件。整个程序结构清晰之后,后续换数据、换参数、加约束都很方便。

5.2 不确定集的Matlab构建

在Matlab中用Yalmip构建预算多面体不确定集,代码非常简洁:

% 预测值和偏差幅值 P_W_forecast = ...; % 风电预测 delta_W = 0.2 * P_W_forecast; % 偏差,取历史误差分位数 Gamma = 5; % 预算参数 % 不确定变量 P_W = sdpvar(1, T); % 盒式约束 Constraints = [P_W >= P_W_forecast - delta_W, ... P_W <= P_W_forecast + delta_W]; % 预算约束:偏差归一化和 <= Gamma Constraints = [Constraints, norm(P_W - P_W_forecast, 1) / norm(delta_W, 1) <= Gamma];

注意最后一行的写法,直接把归一化后的偏差求和放进一个范数约束里,Yalmip会自动把它展开成绝对值不等式,比手写循环加绝对值变量要干净很多,也更容易排错。

5.3 子问题对偶与CCG迭代框架

子问题的核心是把“max-min”变成“max”。以简单的功率平衡场景为例,内层min是一个只含连续变量的线性规划,对偶后目标函数变成对偶变量与右端项的乘积,右端项里就带有不确定变量u。此时目标里出现u和λ的双线性项,需要通过强对偶定理或枚举顶点法处理。

CCG迭代框架在Matlab里的大致结构如下:

% 场景集合初始 Scenarios = {forecast_scenario}; LB = -inf; UB = inf; for iter = 1:30 % 主问题 [x_star, LB_new] = solve_MP(Scenarios); LB = max(LB, LB_new); % 子问题 [u_star, obj_second] = solve_SP(x_star); UB = min(UB, obj_second); if (UB - LB) / abs(LB) < 1e-3 break; end % 将新场景加入集合 Scenarios{end+1} = u_star; end

需要注意,每个场景不只是把u放进主问题就行,主问题要为每个场景单独复制一套第二阶段变量和约束,这就是“列与约束生成”里“列”的由来。

5.4 求解器配置与数值技巧

主问题和子问题都建议用Yalmip建模后调用商业求解器,Gurobi对线性规划和大规模MILP的支持比开源求解器好一个量级。求解容差设置上,主问题MILP用默认1e-4到1e-6即可,子问题线性规划直接求到默认精度。

有一个数值问题是必须警惕的:Big-M法处理双线性项时,M值不能给太大。我见过有人图省事设成1e6,结果子问题求解器直接数值病态,返回一些莫名其妙的解。经验值M不超过1e4,能覆盖变量量级就够,如果需要更大的M,建议考虑把相关约束分段线性化或引入指示变量,别硬刚。

6. 算例验证与常见问题

6.1 模拟项目X算例设计

为了说清楚对比效果,我设计了一个中等规模的模拟微电网算例,参数如下:

设备参数数值
风电装机容量200 kW
光伏装机容量150 kW
储能容量/功率300 kWh / 100 kW
柴发装机容量100 kW,最低出力30 kW
电网交互购/售电上限各100 kW

全天24时段,步长1小时。对比四种方案:确定性优化、鲁棒优化(Γ=0、5、10、24)。统计指标取总运行成本、最坏场景下的功率不平衡余量、储能SOC越界次数。

结果规律非常明显:确定性方案的名义成本最低,但在最坏场景下缺电风险最大;Γ从0增加到10的过程中,最坏场景成本下降明显,而名义成本上升平缓;Γ从10增加到24,最坏场景成本几乎不再改善,名义成本却仍在上涨。这说明对于该算例,Γ=10附近是一个经济性和鲁棒性都比较合理的平衡点。

6.2 调试CCG过程中踩过的坑

我把自己调试这段代码时遇到最多的问题整理成一个速查表,给各位参考:

现象可能原因解决方案
上界小于下界,迭代卡死子问题对偶转换符号错误用单场景回代验证对偶目标是否等于原目标
Yalmip报错无法求解不确定集非线性函数使用不当检查是否有abs/norm之外的二次项进入约束
储能SOC频繁越界第二阶段调整量范围限制过松给SOC增量加一个有界范围,比如±10%容量
迭代次数超过30次仍未收敛主问题初始场景太单一或子问题求解精度不够初始加入预测场景和两个极端场景,提高求解精度
Big-M耗尽数值范围M值设置过度M不超过1e4,必要时改分段线性处理

6.3 实操心得与参数建议

跑CCG之前,我强烈建议先把整个两阶段模型简化成单时段、两变量的玩具算例,手工验一遍对偶推导和CCG迭代。这个准备工作做扎实了,放大到24小时就是顺水推舟的事。

参数上,Γ从0开始逐步往上加,每次记录成本增量和最坏场景改善幅度,两者交叉点附近通常就是工程上可接受的保守水平。不确定集偏差幅值用历史数据的90%分位数起步,如果计算量允许再考虑95%乃至更高。储能的退化成本一定要计,否则模型会给出一个储能疯狂充放的最优解,现场根本执行不下去。

最后再分享一个小习惯:我把每次CCG迭代的上下界日志都存下来,跑完一版自动画一条收敛曲线。这个曲线不仅能帮你判断迭代是否收敛,还能在论文或项目汇报里直观呈现算法的有效性。调试过程里看这根曲线,比盯着命令行数字输出高效得多。

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

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

立即咨询