去年年底我接到一个多能源微电网能量管理系统的仿真任务,最开始我写的是单层调度——24小时预测、一次性优化、给出各机组出力曲线。跑了两天数据,光伏正午大发时弃电率飙升,傍晚预测偏差一大,储能SOC直接越过安全下限。说实话,这种单层调度连自圆其说都难。后来我把框架改成两阶段控制:日前计划层负责24小时经济预调度,日内滚动层负责15分钟级别的修正控制,微网之间再叠加协同自主迭代机制,这才跑出一个能稳定收敛的调度系统。
这个项目的关键词是两阶段控制框架、多能源微网、协同自主优化、调度系统和MATLAB实现。它解决的问题很直接:风光出力不确定、负荷波动、多微网之间的功率交互怎么协调,以及如何在计算代价可控的前提下把优化结果真正落到可执行的控制指令上。适合正在做微电网能量管理、分布式能源调度、或者用MATLAB搭仿真平台的同行参考。
1. 为什么单层调度必然翻车:两阶段框架的工程逻辑
1.1 日前计划为什么是"近视眼"决策
先把话说透:很多人第一次做微网调度,想到的就是拿预测数据丢进优化器,一次性求出24小时机组出力曲线。听起来没问题,但实际跑起来处处碰壁。
核心原因在于预测本身有误差。光伏出力预测误差在晴天可能是5%,多云天直接飙到20%以上,负荷预测误差同样存在。当日前调度把预测值当成真实值来处理,任何一个时间断面出现偏差,后续的设备运行状态就会连锁偏移。尤其是储能,单层调度把储能的SOC轨迹规划得非常紧凑,实际运行中只要一两个时段的充放电量发生偏差,SOC就会偏离参考轨迹,甚至触发保护停机。
还有个容易被忽略的问题:单层调度不考虑控制的可执行性。优化出一组解,但控制器能不能跟踪这组解是另一回事。燃气轮机有爬坡约束,储能有充放电切换延时,联络线功率有协议上限,这些约束在纯能量管理层面可能只是约束条件,在控制执行层面就变成了动态性能问题。日前计划的本质是"静态决策",它回答的是"如果预测完全准确,明天该怎么运行";而现实中预测不可能完全准确,所以必须有一层机制在运行过程中不断修正。
1.2 日内滚动修正:把计划变成闭环
日内滚动控制做的事情,本质上是把一个开环计划变成一个闭环反馈。我采用的是15分钟一个控制周期,每次滚动优化未来1小时的调度指令,只执行第一个时段的动作,下一个周期再把窗口往前推。
这么设计有几个理由。第一,15分钟正好匹配大多数微电网的能量管理系统数据刷新频率,也匹配超短期预测的精度水平。第二,滚动优化的计算窗口短,决策变量少,求解速度能压到秒级以内,满足在线控制要求。第三,滚动执行机制天然具备容错能力,上一周期模型误差再大,下一周期会根据最新测量数据重新优化,偏差不会累积。
这里有个工程细节值得展开:滚动窗口并不是越小越好。窗口太短,储能就没法"看到"未来的负荷与光伏变化趋势,容易做出只顾眼前、杀鸡取卵的决策;窗口太长,预测精度下降,计算量也跟着涨。我用1小时预测窗口配合15分钟控制步长,等于兼顾了趋势感知和在线性,这是工程中比较稳妥的折中方案。
1.3 协同与自主的一体两面
"协同自主优化"这个说法听起来玄,拆开看就清楚了。多能源微网系统里,多个微网通过联络线互联,每个微网有自己的负荷、分布式电源和储能。协同是指全局层面:微网之间的功率交互要整体经济,不能出现一个微网大量弃风的同事另一个微网在烧高价柴油。
自主是指局部层面:每个微网保留本地的调度自治权,不把全部决策权上交中央调度中心。
这两种诉求天然存在张力。完全集权式的全局优化,计算复杂度随微网数量指数增长,而且通信依赖强,某个微网掉线就可能导致整个系统瘫痪。完全去中心化,每个微网各自为政,联络线功率就没有协同逻辑,系统整体经济性一定亏。
我的做法是把两阶段框架再叠一层分布式协同机制:日前阶段各微网独立求解经济调度,然后把联络线功率计划作为边界条件交互几轮,达成初步一致;日内阶段每15分钟根据实测数据刷新,只有联络线偏差超过阈值才触发微网之间的协商修正。这样既保证了局部自主性,又未牺牲协同经济性。
2. 建模的七大模块:从设备约束到边界传递
2.1 目标函数:经济性、环保与弃电惩罚如何加权
多能源微网的优化目标不是把运行成本压到最低就完事,还要兼顾碳排放和可再生能源消纳。我的目标函数是三项费用的加权和。
第一项是运行成本。柴油发电机考虑燃料成本,储能按充放电损耗折算成本,向外部电网购电/售电按分时电价结算。第二项是碳排放成本。柴油机每发一度电对应固定的碳排放因子,按碳价折算成费用。第三项是弃风弃光惩罚。光伏和风电没有燃料成本,但在实际运行中因为功率平衡、线路容量等原因可能被迫拖出,这部分损失要在目标函数里体现为高权重的惩罚项。
三个目标的权重系数不是拍脑袋定的。我在仿真里把弃电惩罚系数设成柴油发电成本的1.5倍,这样系统的自然倾向是优先消纳可再生,而不是为了省小钱去弃电。碳价按当前主流碳市场水平设定,不影响系统倾向性,但能让碳排放差异体现在优化结果里。
2.2 储能与机组的运行约束怎么落到式子
储能建模是这类系统里最容易出错的地方。我用的是一阶离散状态方程:
[ SOC_{t+1} = SOC_t + \eta_c P_{chg,t} \Delta t / E_{rated} - P_{dis,t} \Delta t / (\eta_d E_{rated}) ]
其中SOC是荷电状态,(\eta_c)和(\eta_d)是充放电效率,(E_{rated})是额定容量,(\Delta t)是步长。这里有个单位坑:(\Delta t)必须统一换算成小时,否则SOC变化量会差一个量级。功率单位用kW,容量单位用kWh,一小时步长就是直接相乘。
充放电功率不能同时为正,这是非线性约束。工程上常用的处理方式是引入二进制变量,(P_{chg,t} \leq M \cdot u_t),(P_{dis,t} \leq M \cdot (1-u_t)),把模型变成MILP。很多新手偷懒省略这个约束,结果就是最优解里储能一边充电一边放电,白白损耗效率。虽然目标函数会让这种情况自我惩罚,但松弛解和整数解还是有差异,我建议严格建模。
柴油发电机的约束包括出力上下限和爬坡约束。爬坡约束在日前阶段用小时级步长限制相邻时段的出力变化,在日内阶段用15分钟步长重新限制。还要加上最小运行时间和最小停机时间约束,避免机组频繁启停。这些约束用二进制变量表达,会让模型变成混合整数规划,计算量会涨,但更接近真实机组特性。
2.3 两阶段之间的边界传递机制
两阶段框架不是两套独立的模型,它们之间有明确的边界传递关系。日前调度算完后,把三组信号交给日内控制层:第一组是机组启停状态,日内阶段固定这个状态不再重新决策,否则频繁启停机毁电池又伤;第二组是储能的SOC参考轨迹,日内优化的目标之一就是跟踪这条轨迹;第三组是微网间联络线的协议交换功率计划,日内阶段只在极端情况下允许偏离。
这个传递机制有效地避免了两个阶段打架。如果不固定机组启停状态,日内优化可能每隔15分钟就启停一次机组,既违反机械约束又增加成本。SOC参考轨迹更是关键,它告诉日内层"全局经济性最优的蓄能策略"是什么样,日内层只需要在这个大方向下做局部修正。
边界传递还有一个数据层面的细节:日前阶段用的预测数据是小时级的,日内阶段拿到的是15分钟刷新超短期预测。两者数据库不同,但模型参数必须一致。我写了个统一参数管理脚本,所有设备参数都从同一个结构体数组读取,避免两个阶段各自维护一套数据导致的不一致问题。
3. MATLAB求解链路:YALMIP建模与滚动窗口的实现细节
3.1 工具箱选型:YALMIP还是CVX
在MATLAB里做优化调度,逃不开YALMIP和CVX两个建模范式的选择。我的结论很明确:这套系统用YALMIP,不用CVX。
原因有三点。第一,YALMIP对混合整数规划的支持原生且完整,不管是二进制变量定义还是MILP约束书写,都顺畅很多;CVX的强项是凸优化,处理SOCP和SDP非常漂亮,但混合整数模型的表达绕得太多。第二,YALMIP的后端求解器切换非常灵活,同一个模型可以轻松切换Cplex、Gurobi和MATLAB自带的intlinprog,这在仿真调优阶段很重要——先用免费求解器验证逻辑,再换商用求解器拼性能。第三,YALMIP学习成本低,建模语言直接对应数学表达式,回头维护的时候看着代码就能想起公式,不需要额外翻译层。
当然CVX也有它的场景:如果你做的是鲁棒优化里面的线性矩阵不等式、或者做SOCP鲁棒调度,CVX会更顺手。但就两阶段控制框架这种以MILP为主体的调度问题,YALMIP是更合适的选择。
3.2 求解器配置与代码骨架
我在仿真中同时测试过intlinprog和Cplex,两者结果一致,但性能差距明显。日内滚动优化需要在秒级内出解,intlinprog在模型稍微复杂一点的情况下就会拖到十几秒,Cplex则稳定在2到3秒。最终我选了Cplex,设置MIP间隙为0.5%——这个间隙设置很关键,完全求解到最优可能要几百秒,0.5%的间隙通常几十秒内就能收敛,而代价仅仅是每天多花几十块钱的次优运行成本,完全可接受。
代码骨架长这样:
%% 日前调度:YALMIP建模核心 T = 24; P_dg = sdpvar(1, T); % 柴油机出力 u_dg = binvar(1, T); % 柴油机启停 P_import = sdpvar(1, T); % 从外部电网购电 P_export = sdpvar(1, T); % 向外部电网售电 SOC = sdpvar(1, T + 1); % 储能SOC P_chg = sdpvar(1, T); % 充电功率 P_dis = sdpvar(1, T); % 放电功率 constraints = []; constraints = [constraints, SOC(1) == 0.2]; % 初始SOC constraints = [constraints, SOC >= 0.1, SOC <= 0.9]; % SOC上限 constraints = [constraints, P_chg >= 0, P_chg <= 500]; constraints = [constraints, P_dis >= 0, P_dis <= 500]; % 功率平衡 constraints = [constraints, P_dg + P_pv + P_wt + P_dis - P_chg ... + P_import - P_export == P_load]; % 联络线交换上下限 constraints = [constraints, P_import + P_export <= 800]; objective = sum(c_dg .* P_dg) + sum(c_fuel ...) + ... sum(c_buy .* P_import) - sum(c_sell .* P_export) + ... penalty * sum(P_abandon); optimize(constraints, objective, sdpsettings('solver','cplex',... 'verbose',0,'mip.relGap',0.005));功率平衡表达式里有个细节要注意:P_dg + P_pv + P_wt + P_dis - P_chg + P_import - P_export == P_load这种等式约束,在MATLAB里P_pv和P_wt是已知参数序列,可以直接和sdpvar混合运算,YALMIP能正确处理这种参数和变量的线性组合。
3.3 滚动窗口的三个关键参数
日内滚动优化的参数配置直接影响控制效果。我总结出来三个关键参数,调试的时候优先调它们。
第一个是控制步长。15分钟是我的选择,但如果你算力紧张,可以放宽到30分钟;如果微网里含有响应速度快的储能变流器,5分钟步长能显著提升抑制波动的效果。控制步长直接影响模型里(\Delta t)的取值,代码里所有储能状态方程、爬坡约束都要跟着改,必须统一。
第二个是预测时域长度。前面说了我用1小时,也就是4步预测。这个长度在夏季光伏大发场景下特别敏感:预测时域太长,光伏预测误差导致储能始终在修正;太短,储能看不见傍晚负荷高峰,下午提前把电放光了。
第三个是滚动频率与数据刷新频率的匹配。我的做法是每个控制周期开始时拉一次超短期预测数据,包括光伏、负荷和风速,然后重新优化。这里有个性价比很高的技巧:预测数据不是每个时刻都更新,只有偏差超过阈值的时刻才触发重新优化,其它时刻沿用上一周期的解。实测下来能节省三成以上的计算时间,控制效果几乎无损。
4. 微网间协同自主迭代:从数学原理到代码落地
4.1 一致性算法与ADMM的分工
多微网协同优化到底怎么实现"自主"和"协同"的统一?我调研了一圈,主流的做法无非两种:一致性算法和分布式交替方向乘子法。
一致性算法的思路很直观:每个微网都有一个本地决策变量和一个全局共识变量,迭代过程中各微网交换边界功率增量信息,逐步让所有微网的联络线功率达成一致。这种方法的优点是通信量小、实现简单,缺点是收敛速度对网络拓扑敏感,迭代次数多的时候计算延迟不可控。
ADMM则是把全局优化问题分解成子问题,每个微网求解局部子问题,再通过拉格朗日乘子协调耦合约束。ADMM的收敛性是线性到超线性的,工程实现上更稳健。缺点是每个子问题都是一个MILP,迭代多轮的计算开销不容忽视。
我的方案是两层配合:日前阶段用ADMM求解多微网的全局经济调度问题,每个微网独立求解带耦合联络线约束的局部MILP,通过对偶变量迭代修正联络线功率计划;日内阶段则简化处理——联络线功率偏差在阈值内的微网不再迭代,只有偏差超限的微网才触发一轮增量协商。
这套方案的实际效果是:日前调度3到5轮ADMM迭代就能让全局目标收敛到集中式的95%以上,日内阶段绝大多数时段零迭代,系统整体计算负担很小。
4.2 增量协商的迭代流程与MATLAB框架
给一段ADMM迭代的伪代码式说明,方便你照搬思路:
- 初始化:每个微网设置联络线功率初始值(P_{tie,i}^0),对偶变量(\lambda_i^0=0)。
- 局部优化:每个微网独立求解自己的MILP,目标函数里加一项((\rho/2)|P_{tie,i}-P_{tie,i}^{ref}|^2)。
- 交换相邻微网的最新联络线功率值。
- 更新对偶变量:(\lambda_i^{k+1} = \lambda_i^k + \rho(P_{tie,i}^{k+1}-P_{tie,i}^{ref}))。
- 检查原始残差和对偶残差是否同时低于容差。
- 不满足就回到第2步,满足就输出联络线计划。
在MATLAB里实现时,我用了parfor并行每个微网的局部优化——因为各微网模型互相独立,并行天然契合。注意parfor循环里不能嵌套YALMIP变量共享,必须保证每个迭代内部的模型完全独立。实测4个微网并行比串行快2.8倍左右,迭代轮数少的时候优势更明显。
这里有个我踩过的坑:YALMIP的sdpvar如果定义在公共函数里,parfor并行时会报错或者结果不确定。解决办法是把模型构建代码写进每个微网自己的子函数,输入只有本地数据和对偶变量,输出是本地决策变量。
4.3 收敛判据与计算代价的平衡
ADMM的收敛判据设置是有讲究的。太严导致迭代轮数飙升,太松又让联络线功率计划偏离全局最优。我采用的判据是:原始残差(|P_{tie}^{k+1}-P_{tie}^{ref}|_\infty)低于0.1MW,同时对偶残差低于0.05MW。这个阈值配合(\rho=1)的惩罚参数,在大多数场景下3到5轮就能通过。
惩罚参数(\rho)的选择直接影响收敛速度。(\rho)太小,对偶变量更新慢,收敛慢;(\rho)太大,局部目标函数里联络线功率偏离的代价过高,等效于牺牲局部经济性。我跑过一组参数扫描,(\rho)从0.1到10,结论是1附近最优,1到2之间的差异不大,超过5就会出现明显的振荡。
计算代价方面,我统计过典型工况:3微网系统,日前ADMM迭代4轮,每轮每个微网求解一个含96个二进制变量的MILP,总耗时约40秒,可以接受。日内滚动优化单次2到3秒,满足15分钟控制周期绰绰有余。
5. 仿真案例:三微网互联系统的调度结果与边界校验
5.1 典型日场景:夏季光伏大发
为了验证两阶段框架的实际效果,我搭了一个三微网互联的仿真算例。三个微网分别模拟农村型、城镇型和工业型负荷特征,每个微网都配置光伏、储能和一台柴油发电机,微网之间有两条联络线可以双向交换功率。
选取的典型日是夏季晴天:光伏出力从早上六点开始爬升,中午十一点到下午三点达到峰值,此时负荷却处于低谷,工业型微网负荷相对稳定,而农村型微网负荷全天波动极大。这种场景最能暴露单层调度的软肋——光伏大发时弃电严重,傍晚光伏退出后柴油机需要快速爬坡补上缺口,储能在下午的充放电策略稍有失误就会导致晚间电力供应紧张。
预测数据在日前调度时使用理想预测(就当系统内生成了完美的预测曲线),日内滚动阶段在预测基础上叠加一个随机扰动序列,模拟真实预测误差。这样就能剥离开"预测误差影响"和"框架性能差异"两个因素。
5.2 两阶段与单阶段调度的量化对比
我分别跑了单阶段日前调度、两阶段框架带理想预测、两阶段框架带预测误差三个场景,统计整天的运行成本、弃光率和联络线峰值波动。
| 指标 | 单阶段日前调度 | 两阶段框架(理想预测) | 两阶段框架(含预测误差) |
|---|---|---|---|
| 运行成本(元/天) | 12480 | 11400 | 11560 |
| 弃光率(%) | 23.4 | 4.1 | 5.8 |
| 联络线功率最大越限(MW) | 1.2 | 0 | 0.3 |
| SOC越限次数 | 3 | 0 | 0 |
| 柴油机启动次数 | 6 | 3 | 3 |
单阶段调度之所以成本高、弃光率高,本质原因是它把预测当成确定值,正午光伏大发时储能被功率上限卡住,只能弃光;傍晚负荷上升时储能SOC因为中午充得过多而提早触顶,无法在晚间高峰释放电量,成本就上去了。
两阶段框架在理想预测下成本下降8.6%,这是框架本身的价值;加入预测误差后成本只增加1.4%,说明日内滚动修正有效吸收了预测偏差。SOC全程没有越限,柴油机启动次数从6次降到3次,机组磨损也轻得多。
5.3 边界条件校验:功率平衡与SOC轨迹
仿真跑完不能只看总指标,逐时段的边界校验才是判断调度合理性最紧迫的环节。我写了两个校验脚本:功率平衡校验和SOC轨迹校验。
功率平衡校验把每个时段的发电机出力、光伏出力、储能功率和联络线功率全部求和,再减去负荷,得到残差。正常情况下残差应该在机器精度级别,如果出现几十kW级别的残差,一定是约束写漏了或者变量赋值重复。我第一次跑平衡校验的时候就发现了问题:联络线功率在己方微网建模里是正值、在对方建模里也是正值,两个微网加起来功率不平衡直接翻倍。后来统一约定联络线功率的正方向并加符号约定说明,问题才消失。
SOC轨迹校验关注的是两阶段传下来的参考轨迹和日内实际SOC之间的偏差。偏差如果在每一个控制周期内被拉回参考轨迹附近,说明滚动修正没有失控;如果SOC一直偏离参考往一个方向走,那是边界传递机制出了问题。我观察到上午光伏大发时段,日内SOC会明显低于参考值——因为实际光伏比预测更强,日内层主动少充了一点,把负荷供电压力转给了光伏直供,这是正确的行为。
6. 实测中踩过的坑与参数调校经验
6.1 数值病态与缩放问题
第一个让我头疼的问题是求解器动不动报数值错误。排查到最后,根源是模型里的量纲不一致:光伏出力动辄几百kW,而碳成本计算出来的目标值只有个位数,目标函数里量级差了几百倍,病态矩阵直接让求解器解不出来。
解决办法是给模型做整体缩放。我把所有功率变量统一除以基准功率100kW,SOC保持0到1的比例,价格系数统一除以100再进目标函数。调整之后,求解器数值状态从红色警告变成绿色稳定。
这里有个经验:如果模型中存在乘积项,比如(P_{dg,u} \cdot u),先用大M法线性化,不要指望求解器能处理变量乘积。大M取值也不能拍脑袋,太大会导致数值病态,取设备出力上限的1.2倍就够。
6.2 联络线功率振荡怎么压住
多微网协同迭代的过程中,一个高频出现的问题是联络线功率在迭代轮次之间来回振荡——这轮协议买电,下轮协议卖电,振幅还有放大的趋势。
这个问题的本质是惩罚参数(\rho)取值不当或对偶变量初始位置离最优解太远。我的解决方案是给联络线功率加斜坡约束,限制相邻迭代轮次之间的调整幅度;同时把(\rho)从0.5开始小步递增,每轮迭代后自动调整,收敛后稳定在1附近。
实际调参过程中,我发现振荡还有一个隐藏来源:各微网更新联络线功率时用的参考值不一致。ADMM要求所有节点在每轮迭代开始时拿到同样的参考值,否则算法在数学上就不收敛。我加了边界条件广播脚本,确保每个微网在局部优化之前都同步一份最新的联络线计划。
6.3 热启动策略与扩展方向
最后分享一个很实用的工程细节:日内滚动优化不要每次从零初始化求解器,而是把上一轮的整数解设为热启动。
Cplex和Gurobi都支持热启动。我把日前调度求出的机组启停状态、上一轮滚动优化的储能充放电状态作为MIP start传给本轮求解器,MIP求解时间直接减半,从2秒多降到1秒左右。这个优化太值了,代码只加了十几行。
我跑完这个项目之后的体会是:两阶段控制框架真正的价值不在"模型比谁的更复杂",而在于把不确定性的应对机制工程化——预测不准是常态,控制框架要用滚动机制去接住这些不确定性,而不是指望预测模型能修到完美。
这套系统后续能扩展的方向也很多。可以把热负荷、冷负荷联动进来做成综合能源系统,也可以把碳流追踪算法叠加到调度结果上,实现设备级的碳排放核算。再往后,日内滚动层的优化任务可以试着换成强化学习代理模型,让柴油机和储能的控制策略直接学习风光出力的不确定性模式,我看了一圈文献,这个方向已经有不少可复现的MATLAB案例了。但那是后话了,这套两阶段框架搭好,后续扩展就都有了下层堵漏的地基。