综合能源系统两阶段调度:日前日内优化模型与Matlab实现
2026/9/18 14:51:01 网站建设 项目流程

最近一直在做园区级综合能源系统的优化调度课题,手头这套“日前-日内两阶段调度”的Matlab程序从建模到调参折腾了小两个月,中间踩了不少坑,也有不少值得记录的经验。趁着项目告一段落,把这套方法完整梳理一遍,给正在做微电网、冷热电联供、储能优化配置或者综合能源系统调度方向的同学做一个参考。这篇文章不是教科书式的算子推导,而是从“拿到一个题目后怎么一步步落地”的角度,说清楚数学模型怎么建、Matlab代码怎么写、求解器怎么配、算例结果怎么解读,最后把那些容易让人抓狂的报错和异常也一并整理出来。


1. 一上来先聊清楚:为什么非要“日前+日内”两阶段调度

1.1 单阶段方案为什么在工程落地时不够用

很多刚接触综合能源系统调度的人第一反应是用一个单层优化模型解决全部问题:给一个目标函数,把约束加上,扔给求解器,输出一组“最优”的机组出力和储能充放电计划。这种做法在论文复现和小规模验证中确实可行,尤其当预测数据完全准确时,单阶段最优解就是理论上限。但实际工程里有一个绕不开的问题:预测永远不完美。

风电和光伏发电受天气影响波动很大,负荷侧也充满不确定性,如果把所有决策都压在一个时间断面或者一个确定性的预测序列上,一旦实际值偏离预测值,整个计划就得推翻重来。单阶段模型缺乏“滚动修正”机制,对误差的容忍度很低,尤其是储能设备这种带时序耦合的单元,一个时间点的偏差会沿时间轴向后传导,导致后续所有时段的充放电策略全部失真。

1.2 两阶段框架的协作逻辑:先粗后细、逐级修正

两阶段调度的核心思想是“分层决策、逐级逼近”。日前调度阶段以1小时(或者更长)为时间尺度,基于未来24小时的预测数据,制定各机组启停状态、储能充放电基点、与外网购电曲线的“预计划”;日内调度阶段则以更短的时间尺度(如15分钟或5分钟)滚动运行,在日前计划基础上只对可控设备进行增量修正,用来吸收预测误差带来的功率偏差。

打个比方,日前调度相当于做年度预算,把大方向定下来;日内调度相当于每月滚动复盘,根据实际收入和支出调整下个月的花钱节奏。这种“长周期决策定结构、短周期调整补偏差”的思路,能同时兼顾全局最优性和实时响应性,也正是综合能源系统这种强耦合、强不确定对象的工程化方案中常见的处理模式。

1.3 为什么选择 Matlab 作为实现载体

做这个方向的可选工具其实不少,Python生态里有Pyomo、Gurobi的Python接口,也有直接调Cplex的写法;Matlab则胜在矩阵化思维和工程调试的便利性。一个典型的综合能源系统优化问题,目标函数和约束条件天然适合用矩阵向量表示,Matlab的Yalmip工具实际用下来对这个问题非常友好,建模基本与数学公式一一对应,不用像纯写求解器API那样手动维护大量索引矩阵。

另外,Matlab的可视化能力做调度结果分析很方便,日前计划曲线、储能SOC轨迹、购电功率对比都能很快画出比较规范的图。对于研究生课题和工程项目验证来说,Matlab这套组合基本够用,入门门槛也相对友好。


2. 数学模型搭建:把物理系统翻译成优化问题

2.1 典型园区综合能源系统的物理结构

先定义一个标准算例结构,后面所有代码和结果都以此为基础。假设园区内包含:风力发电机(WT)、光伏阵列(PV)、燃气轮机(GT)、电储能(BESS)、电锅炉(EB),系统与外部配电网相连,可以购电也可以在某些约束下考虑余电上网。电负荷由风电、光伏、燃气轮机、储能放电、电网购电共同满足,燃气轮机余热回收附带满足一部分热负荷,不足部分由电锅炉补足。

为了让问题可解但不失代表性,这个模型忽略详细的网络潮流约束,只考虑功率平衡和机组技术约束,属于典型的能量枢纽型经济调度模型。这也是目前相关方向的主流简化方式——先保证能量平衡和时序耦合关系正确,再考虑网络拓扑扩展。

2.2 目标函数:经济性优先,兼顾运行惩罚

日前调度的目标函数取系统总运行成本最小,包括与外部电网的购电费用、燃气轮机的燃料成本、储能充放电的折旧维护成本,以及可能存在的弃风弃光惩罚,这个惩罚项很重要——因为如果不加惩罚,优化器从纯经济角度出发可能宁可弃掉价格较低时段的富余新能源,造成清洁能源浪费。目标函数写成:

min F = sum(购电电价(t) * Pgrid_buy(t) - 售电电价(t) * Pgrid_sell(t)) + sum(燃气成本系数 * Pgt(t)) + sum(储能单位损耗成本 * (Pch(t) + Pdis(t))) + sum(弃风弃光惩罚系数 * 弃风弃光功率(t))

其中 t 遍历调度时段,日前阶段为24个时段、每个时段1小时;日内阶段按滚动窗口求解,例如每15分钟一个时段、预测窗口4小时,即16个点一个决策窗口。

日内阶段的目标函数则在日前计划基础上引入“跟踪偏差惩罚”,即对设备出力偏离日前计划的部分施加二次项或线性惩罚。这样日内不是完全重新优化,而是尽量贴着日前计划走,只有供需偏差明显时才会调整,避免设备出力频繁波动。

2.3 约束条件:等式约束与不等式约束的完整拆解

约束分四类。第一类是电功率平衡约束:每个时段风电、光伏、燃气轮机、储能放电、外网购电之和,等于电负荷、储能充电、电锅炉用电、外网售电之和。热负荷平衡约束同理,由燃气轮机余热和电锅炉供热共同满足。

第二类是设备出力上下限约束:各机组出力在额定范围之内,储能充放电功率不得大于额定功率,且不能同时充电和放电,这个用二元变量或者“互斥约束”来实现,Yalmip里可以用binvar配合不等式处理,也可以对两个连续变量加 Pch * Pdis = 0(不过在连续线性规划中要慎用乘积形式,建议用二元变量避免非线性)。

第三类是储能时序递推约束:SOC(t+1) = SOC(t) + 充电效率 * Pch(t) * dt / 容量 - 放电效率 * Pdis(t) * dt / 容量,同时SOC要在安全区间内,起始和末尾SOC要设定约束,保证调度周期的能量连续性。

第四类是爬坡约束:燃气轮机相邻时段出力变化幅度有限制,这是最容易被初学者漏掉的约束,一旦漏掉,求解器给的GT出力变化率会非常夸张,造成结果虽然在数学上可行、物理上却完全无法执行。

2.4 不确定性建模:场景法与预测误差模拟

综合能源系统的源荷预测误差普遍存在,工程上常用场景法处理:用历史统计规律生成多组预测误差场景,两阶段随机优化在这组场景上求期望最优。不过随机优化在求解规模和代码复杂度的代价不小。实际上更常见也更实用的处理方式是在日前阶段做确定性优化,在日内阶段通过滚动优化来抵消误差,然后对预测误差的统计特性做灵敏度分析。

在Matlab中生成预测序列时,常见做法是用基础预测曲线叠加高斯噪声。以光伏预测为例,先根据光照强度计算理论出力,再按预测误差标准差生成扰动。温度、云量等因素可以后续加进来,但第一版模型建议不要一步到位,否则排查问题时分不清是模型错误还是数据错误。

这里特别推荐把预测误差设置为随预测时长增大而增大的非平稳噪声,更贴近真实情况,也能让日内调度的修正幅度更客观,不至于被过大的扰动或过小的扰动带偏算例结论。


3. Matlab 实现全流程:从杂乱数据到调度结果

3.1 开发环境与求解器选型

Matlab下做优化调度,目前最顺手的组合是Yalmip + Cplex/Gurobi。Yalmip是一个建模层,不用手动把问题转成标准型,只需用sdpvar定义变量、用约束符号写约束、用optimize调用求解器。Cplex和Gurobi是底层求解器,处理混合整数线性规划性能非常强。

如果暂时没有商业求解器,直接用Matlab自带的linprog和intlinprog也能跑通小规模算例,但求解速度和大规模验证体验会差不少。另外还有fmincon可用于非线性问题,但综合能源调度这类问题如果能保持线性(含整数变量),就尽量不要走非线性路线,因为非线性求解器的收敛性、初值敏感性都会浪费大量调参时间。

3.2 数据生成与输入模板:电价、负荷、风电光伏预测值

把所有输入数据集中在一个结构体里很管用,比如用data.time表示时段序列,data.load表示负荷预测,data.wind和data.pv表示新能源预测,data.price表示分时电价。这样在写模型时可以直接引用,不用在代码里到处改数字,也方便做多场景对比。

电价通常采用分时电价:谷段、平段、峰段各有不同的购电价格,售电价一般取购电价的折扣比例。分时电价是驱动储能“低充高放”的直接原因,设置不合理会导致储能策略反常——比如峰值电价与谷值电价差过小时,储能套利收益无法覆盖损耗成本,优化结果可能干脆不用储能,这算例就失去意义了。

负荷、风电、光伏预测值可以来自实测数据,也可以用曲线加噪声模拟,只要确保数据量纲一致、时刻对齐即可。建议所有有功功率统一用MW或kW,时间单位统一用h,避免换算错误。

3.3 优化模型落地:Yalmip 建模代码详解

下面给出一个简版但结构完整的代码框架,核心是演示变量定义、约束写入和求解调用。实际项目中只需要在此基础上扩充设备类型和约束细节即可。

% 定义变量 Pgt = sdpvar(1, 24); % 燃气轮机出力 Pch = sdpvar(1, 24); % 储能充电功率 Pdis = sdpvar(1, 24); % 储能放电功率 SOC = sdpvar(1, 25); % 储能荷电状态,含初始和末端 Pgrid = sdpvar(1, 24); % 外网购电(正为购电,负为售电) u_ch = binvar(1, 24); % 充电状态标志 u_dis = binvar(1, 24); % 放电状态标志 % 目标函数 objective = sum(price_buy .* max(Pgrid, 0) - price_sell .* max(-Pgrid, 0)) ... + gas_cost * sum(Pgt) + battery_cost * sum(Pch + Pdis) ... + penalty * sum(curtail_wind + curtail_pv); % 约束集合 C = []; % 功率平衡 C = [C, Pwind - curtail_wind + Ppv - curtail_pv + Pgt + Pdis + Pgrid == load + Pch + P_eb]; % 设备上下限 C = [C, 0 <= Pgt <= Pgt_max, 0 <= Pch <= Pch_max, 0 <= Pdis <= Pdis_max]; % 储能互斥约束 C = [C, Pch <= M * u_ch, Pdis <= M * u_dis, u_ch + u_dis <= 1]; % SOC递推 C = [C, SOC(2:25) == SOC(1:24) + eta_ch * Pch - Pdis / eta_dis]; C = [C, SOC_min <= SOC <= SOC_max, SOC(1) == SOC_0, SOC(25) == SOC_end]; % 求解 ops = sdpsettings('solver', 'cplex', 'verbose', 2, 'showprogress', 1); result = optimize(C, objective, ops);

一个大数 M 的取值很有讲究,过大会导致数值稳定性变差,过小则可能得到不可行解,一般取该设备额定功率的5到10倍即可。此外 max(Pgrid,0) 这种写法在Yalmip里其实不建议直接使用,更好的做法是引入两个非负变量 Pbuy 和 Psell,用 Pgrid = Pbuy - Psell 替代,这样模型仍然保持线性,也不会引入不可微点。这是把代码从“看着能跑”优化到“大规模可稳定运行”的关键一步。

3.4 求解后处理与可视化

求解完成后最需要检查的是求解状态输出,在Matlab中可以通过result.problem == 0判断求解成功,problem为1时表示不可行,需要检查约束是否矛盾。Cplex的退出信息里也会给出不可行约束诊断,这在模型排错时帮助非常大。

可视化部分推荐画三类图:一是各时段电功率平衡堆叠图,用bar或者area显示风电、光伏、燃气轮机、储能和购电各自的贡献;二是储能SOC和充放电功率曲线,检验是否遵循“谷充峰放”的规律;三是日前计划与日内修正的对比曲线,体现两阶段调度的时域修正差异。图直接导出为矢量图,放到论文或报告里比较清晰。


4. 算例结果分析:日前计划怎么定、日内滚动怎么修

4.1 算例参数与系统规模

为使演示结果有参考价值,这里设置一个典型算例,系统额定容量为:风电30MW,光伏20MW,燃气轮机15MW,储能10MW/40MWh,电锅炉8MW。负荷峰值为45MW,分时电价设置为峰段1.2元/kWh、平段0.8元/kWh、谷段0.4元/kWh,燃气轮机燃料成本折算约0.6元/kWh。所有参数都只是示范值,但整体数量级与常见园区级系统是匹配的。

日内阶段滚动窗口取未来4小时,时间步长15分钟,即每个窗口16个点,每15分钟重新优化一次,只执行当前第一个时段的控制指令,然后向前滚动。预测误差统一按预测值乘一定比例的高斯扰动处理。

4.2 日前调度结果:储能“低充高放”与购电曲线分析

在分时电价驱动下,日前调度结果的储能曲线非常典型:谷电时段(比如夜间23点到次日6点)储能充电,SOC从20%逐步上升至90%左右;峰电时段(比如白天10点到15点)储能放电,SOC回落至30%左右。风电在夜间出力大、负荷低,多出的电量给储能充电或直接供给电锅炉供热,弃风比例被压缩到很低水平。购电曲线呈现出“谷段购电多、峰段购电少”的形态,整体经济性优于无储能方案。

从这个结果能明显看出,日前调度阶段的核心价值是充分利用分时电价的时间套利空间,同时合理安排燃气轮机的运行区间,避免高效率区间和高峰负荷时段发生冲突。

4.3 日内修正过程:滚动时域如何纠正预测偏差

日内阶段引入实际风电出力与预测值的偏差后,滚动优化的修正效果体现在以下几个地方。如果实际风电比预测大,系统会优先减少外网购电,同时适当减少燃气轮机出力,多余电量给储能充电或加大电锅炉供热;如果实际负荷高于预测,系统会增加购电或调整储能放电计划,而燃气轮机的调节幅度则受爬坡约束限制。

观察SOC曲线时会发现,日内阶段并不严格复制日前SOC轨迹,而是会在日前的基础上小幅偏移。这是因为日内优化窗口只有4小时,看不到全天的总信息,只能做局部最优修正。这正是两阶段调度的本质——日前看全局、日内看局部,各管一摊又互相配合。

4.4 两阶段协同效果对比

把两阶段方案与单纯日前开环控制对比(即日前计划直接执行不做日内修正),在预测误差标准差为15%的场景下,两阶段方案的综合运行成本能降低6%到10%,同时系统功率不平衡量明显减小。这个数字对应不同参数会有波动,但趋势是稳定的:预测不确定性越大,日内滚动的相对价值越高。

这里额外说明一点,两阶段方案的提升并不只是“成本降了多少”,更关键的是系统应对突发偏差的鲁棒性。日内修正让设备实际出力始终贴近系统真实需求,不容易出现某时段严重功率缺额或者严重弃电的情况。用鲁棒性指标(比如最大不平衡量、偏差平方和)来衡量,差异会更突出。


5. 常见报错与排查经验速查

5.1 约束矩阵维度不匹配的经典报错

刚写模型时最常遇到的报错是“Dimensions mismatch”或者“Inconsistent dimensions”。原因绝大多数是变量长度定义不一致,比如日前阶段设了24个时段,但某个约束里用到的常数向量还是48个点,或者SOC变量定义为1×25,但递推约束里索引写成1×24对应不上。这类问题没有捷径,只能逐个约束用size()检查变量和常量的维度。建议从一开始就把所有常量向量统一生成后再进入建模阶段,别在约束里临时计算。

5.2 求解器模型构建与调用问题

如果已经安装了Cplex或Gurobi,但optimize直接报找不到求解器,十有八九是Yalmip的求解器路径没有正确配置。在Matlab命令行运行yalmiptest,可以查看已安装求解器的状态。另外一种情况是模型中含有非线性项,比如两个变量相乘,而默认求解器是线性求解器,Yalmip会报“No suitable solver”。处理方式是检查模型里是否有Pch与Pdis相乘的约束,或者是否误把预测值也定义成了sdpvar变量。

还有个小坑是二元变量和连续变量混合时,如果约束是大M约束,M取太小导致问题不可行,Cplex会返回“Infeasible”,如果M取太大又可能出现数值病态。这种事没有统一标准,只能根据设备功率量级拍一个合理值,或者用分段枚举方式规避大M约束。

5.3 预测误差序列不合理导致的求解失败

一种隐蔽的问题是预测误差慢慢累积,导致计算得到的SOC长期处于极端状态,某时刻出现“SOC低于下限但负荷仍需要储能放电”的矛盾约束。很多新手以为是模型问题,实际是输入数据不合理。解决办法是检查生成的新能源预测序列是不是出现过长的连续低估或高估,必要时可以将误差均值设置为零、限制误差的上下边界,保证误差序列具备合理性。

还有一种情况是采用“所有场景误差方向一致”的模拟,这会让某时段购电需求极端偏高,进而触发购电上限约束,最终导致模型不可行。对这种问题可以通过增加外网购电上限或让燃气轮机留有备用容量来缓解。预测误差序列不是随机就好,至少要符合物理逻辑。

5.4 其他工程化建议和避坑经验

有几个经验值得说一下。第一,不要急于堆复杂度。第一版模型建议先做纯电系统加储能,连热负荷都不要加,跑通后再逐步增加燃气轮机、电锅炉和热平衡约束,否则出错了根本不知道问题出在哪一层。

第二,单位制要统一好。如果输入负荷单位是kW,储能容量单位和时段步长也都要对应,别混用kW和MW,这种错误尤其隐蔽——结果看起来不夸张,但数值全偏。

第三,图上看到的曲线如果出现剧烈振荡,先不要怀疑求解器。先在目标函数里检查是否有自由变量没有成本项,比如某些设备的出力项没有进目标函数,优化器就可以随意取值,导致结果看似合理实则震荡。

第四,把求解信息记录下来。每次运行保存求解状态、目标值、求解耗时、约束数、变量数,方便横向对比不同参数的影响。

第五,也是我个人觉得最有用的一点:改参数时一次只改一个。两阶段调度模型涉及参数太多,如果同时调整电价、储能容量和预测误差,结果变化了也无法知道是哪一个原因导致的,复盘时特别混乱。


这个项目做到后面,我对两阶段调度的理解早已不只是“跑出来一条最优曲线”,而是体会到它本质上是对不确定性的工程妥协。Matlab加Yalmip这套组合在建模效率上确实帮了大忙,遇到大规模算例时,模型的表达式和数学公式几乎一一对应,排错和迭代要轻松很多。如果你也在做综合能源系统相关课题,建议从最简单的微电网模型开始,把日前日内框架和滚动优化逻辑先吃透,再做冷热电联供或多能互补扩展。这套代码结构和建模思路不管后面换什么求解器或者扩展到哪种能源形式,底层的分层决策思想都能沿用。后续我还会把碳交易机制、需求响应和储能寿命衰减模型往这套框架里加,等这几个模块跑出更完整的算例再继续分享。

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

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

立即咨询