ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

多能源微网双层调度模型复现:从双层结构到滚动优化与Pyomo实现

多能源微网双层调度模型复现:从双层结构到滚动优化与Pyomo实现 1. 先搞清楚这个模型到底在调度什么去年年底我接手了一个复现任务把一篇多能源微网双层调度模型的论文跑通。我原以为两天就能完事结果前前后后折腾了三周其中大部分时间不是在建模而是在理顺双层结构、滚动窗口、预测误差这三者之间的关系。等我把这套东西完整落成 Pyomo Gurobi 的工程之后才意识到绝大多数复现卡壳根本不是求解器的问题而是没有先把模型的调度逻辑想清楚。这篇是把我的完整复现思路、数学骨架、代码路径和踩坑记录全部摊开讲一遍。内容适配所有做微网调度、综合能源系统优化的同学尤其是拿着论文但缺少代码、需要从零复现的人。1.1 双层结构谁在决策谁在追随双层调度里最容易被忽略的一点是它不是两个独立的优化问题而是有主从关系的博弈。上层一般代表微网运营商或日前调度中心它做的是长时间尺度的资源配置比如决定燃气轮机明天几点启停、跟主网购多少电、储能什么时候充电。下层则代表日内运行层它在上层给出的计划框架内根据最新的光伏、风电、负荷预测数据做短时间尺度修正决定每15分钟或5分钟各设备实际出力调整多少。我习惯用一个生活化的类比上层是公司年度预算下层是部门在季度末根据实际情况调整采购计划。预算不能随便改但具体怎么花下面有自主权。上下层之间既不是完全集中也不是完全分散而是“计划先行、滚动修正”。在建模型之前我强烈建议先把主从关系画成一张决策层次表明确每个阶段谁的变量是已知量、谁的变量是待求量。否则后面写KKT条件时很容易把上下层变量搞混模型尺寸越堆越大但实际可能在求解一个错得离谱的问题。1.2 多时间尺度为什么一个调度计划不够很多刚接触微网优化的人会问我直接建立一个24小时优化模型不就完了吗为什么还要搞日内滚动、实时修正原因在于预测误差。光伏出力、风电出力、负荷需求都不可能24小时前预测得准。尤其是中午云层遮挡或者傍晚负荷快速爬升的场景日前计划可能与实际相差15%以上。如果完全按照日前计划执行要么造成弃风弃光要么被迫高价购电严重时还会导致功率失衡。多时间尺度滚动优化的核心思想就是“走一步看一步、再看几步”。比如当前时刻是09:00优化窗口是未来4小时我们基于最新预测算出未来16个15分钟调度间隔的最优出力但只执行第一个间隔的实际指令。等到09:15重新获取预测窗口继续向前滚动再次优化。这种方式让决策始终跟随最新信息更新同时还能兼顾未来一段时间的趋势不会变成完全短视。我复现时采用的典型时间粒度是日前调度为1小时一个时段共24个时段日内滚动优化为15分钟一个时段窗口长度4小时即16个时段实时修正为5分钟一个时段窗口30分钟。这样既保证了日前计划的经济性又让日内响应具备足够的灵活性。1.3 复现前先定义能源集线器边界多能源微网之所以比单一微网复杂在于它引入了电、热、冷、气多能耦合。同一个燃气轮机发出来的电属于电力平衡余热属于热力平衡余热还能驱动吸收式制冷机提供冷量。如果不先把能源集线器的输入-转换-存储-输出边界梳理清楚后面写约束时会反复出现“变量未定义”和“量纲不一致”的问题。我复现时定义的微网结构包含了以下设备类型设备输出能源说明输入主网购电电可双向或单向本文按单向购电处理输入天然气气供燃气轮机和燃气锅炉可再生能源光伏、风电电波动性电源可弃热电联产燃气轮机 余热锅炉电、热电热比固定或可调制热燃气锅炉、电锅炉、热泵热不同能效和成本制冷电制冷、吸收式制冷冷吸收式利用余热存储蓄电池、储热罐电、热充放电/充放热效率与SOC约束这套结构是大多数多能源微网双层调度论文的标准配置。你把边界定义清楚后才能把上层和下层决策变量对应到具体设备避免之后每个时段有几十个含义不明的变量索引。2. 双层调度模型的数学骨架与关键约束整个模型我建议按三个层次写上层日前规划、下层日内滚动、以及把上下层捆在一起的耦合约束。不要一上来就写几千行代码先把数学骨架搭出来代码只是骨架的翻译。2.1 上层日前决策变量和目标上层模型的时间尺度是24小时步长1小时所以下标 t 1, 2, ..., 24。决策变量包括日前购电功率 P_grid(t)、购气量 V_gas(t)、燃气轮机启停状态 u_gt(t)、各设备日前计划出力 P_i(t)以及储能的充放电计划 P_ch(t)、P_dis(t)。上层目标函数是让全天总运行成本最小典型表达式为F_up Σ_t [ c_grid(t) * P_grid(t) c_gas * V_gas(t) C_om(t) C_co2(t) ]其中c_grid(t)是分时电价c_gas是天然气价格C_om是设备运行维护成本一般按出力的比例系数折算。C_co2是碳排放成本如果原论文考虑低碳调度就加上不考虑可以去掉。上层还必须满足每个时段的电、热、冷平衡。我这里给出电平衡约束的典型写法P_grid(t) P_pv(t) P_wt(t) P_gt_e(t) P_dis(t) P_load(t) P_eb(t) P_hp(t) P_ec(t) P_ch(t)这个式子看着简单实际很容易漏项。电锅炉、热泵、电制冷机都是耗电设备必须放在用电侧蓄电池充电是负荷放电是电源。如果光记得发电机和光伏忘了电制冷的耗电热季的平衡会算得完全不对。同理热平衡需要同时考虑燃气轮机余热、燃气锅炉供热、电锅炉供热的供给侧以及热负荷、吸收式制冷耗热、储热罐充放热的需求侧。冷平衡则相对简单主要由电制冷和吸收式制冷供给冷负荷需求。2.2 下层日内滚动目标与修正机制下层模型的时间步长是15分钟滚动窗口长度是16个时段。在每个滚动优化触发时刻 k下层需要求解未来16个时段的修正计划但只执行第一个时段。下层目标函数在日前计划的基础上增加了修正惩罚项我采用的写法是F_down Σ_{n0}^{H-1} [ c_grid(kn) * P_grid_down(kn) c_gas * V_gas_down(kn) λ_pen * (偏差量) λ_cur * 弃风弃光量 ]其中H是滚动窗口长度λ_pen是设备出力相对日前计划的偏离惩罚λ_cur是弃风弃光惩罚。偏离惩罚非常重要它让日内修正不会完全推翻日前计划否则日前计划就形同虚设。下层决策变量是各设备在日内时段的实际出力修正量。比如燃气轮机日内出力 P_gt_down(k) 等于日前基准 P_gt_up(t) 加修正项 ΔP_gt(k)。这样写的好处是修正量可以设置上下限避免设备在一个滚动周期内出现剧烈波动。下层还需考虑储能SOC的动态递推SOC(k1) SOC(k) η_ch * P_ch(k) - P_dis(k) / η_disη_ch和η_dis分别是充电、放电效率。滚动优化的每个窗口开始时SOC是当前实际值不是日前计划值。这一点是很多复现版本出错的重灾区因为他们直接把日前SOC终值当作日内初始值导致日内储能曲线完全失真。2.3 上下层如何耦合关键约束的衔接方式双层模型如果上下层完全独立求解那就退化成两个无关的优化问题。必须通过耦合约束把上层计划“锁”进下层模型。我在复现中使用三类耦合约束。第一类是机组启停状态耦合。上层决定的燃气轮机启停状态 u_gt(t)在日内滚动窗口内保持不变日内只能调节出力水平不能改变启停。这样既符合实际也避免了双层整数变量带来的求解困难。第二类是计划功率基准耦合。日内设备出力 P_down(k) 以对应时段上层计划出力 P_up(t) 为基准加上一个可调偏差−δ_i ≤ P_down(k) − P_up(t) ≤ δ_iδ_i是设备允许的最大调整范围。燃气轮机、燃气锅炉的δ可以取额定出力的10%~20%储能和可中断负荷可以设大一些。这个约束把上层计划的权威性和下层修正的灵活性统一了起来。第三类是储能SOC耦合。日前模型规划的SOC曲线不代表日内必须精确复现但日内滚动结束时SOC不能偏离日前计划终值太大否则下一个周期的可调空间会失真。我通常加一个软约束把SOC偏差惩罚放进下层目标而不是硬性限制。3. 滚动优化的核心机制与预测误差处理滚动优化本质上就是模型预测控制在微网调度里的应用。它的核心价值在于用最新的预测更新控制决策同时通过窗口滚动保持对未来趋势的感知。3.1 MPC式执行流程的实际代码逻辑一个完整的滚动优化循环可以拆成六步读取昨日的日前调度结果得到该时段的上层计划基准。获取当前时刻实际的光伏、风电、负荷数据以及未来窗口内的预测数据。将预测数据、日前基准、当前SOC/储热状态传入日内滚动优化模型。调用求解器求解窗口内所有时段的最优出力。只保存并执行窗口第一个时段的结果下发到设备。等待下一个触发时刻滚动到下一周期重复步骤2到5。我在代码里用了一个循环来控制触发时刻每个触发时刻之间间隔15分钟。运行一天一共96个调度点。窗口长度16个时段意味着每次求解要同时优化未来4小时计算量不算大纯Python调用Gurobi单次求解大约在几百毫秒到几秒之间。3.2 预测误差场景的构造方法既然是滚动优化就绕不开预测数据怎么来。完全复现时如果没有实测预测系统可以使用“真实曲线 误差扰动”的方法构造。具体做法是设定光伏、风电和负荷的真实出力曲线然后为每个滚动触发时刻生成一组预测数据P_pred(kn) P_real(kn) * (1 ε(kn))ε(kn)是一个零均值正态分布随机变量标准差根据预测时间尺度设定。日前预测误差大日内预测误差小。比如日前光伏预测标准差取15%日内4小时预测标准差取6%实时30分钟预测取2%。这样构造预测误差场景既接近论文的数值实验方式又能方便地做灵敏度分析。如果你想研究更极端情况还可以把正态扰动换成盒式不确定集或者加入“云层遮挡”事件模拟看看模型在不同鲁棒性要求下的表现。3.3 多尺度衔接的工程细节多尺度真正难的不是每个尺度单独求解而是尺度之间怎么衔接。我复现时采用的衔接原则是长尺度只定框架短尺度只做修正。日前调度给日内提供的是启停状态、购电计划基准和SOC日轨迹。日内滚动给实时修正提供的是当前设备工作点。实时修正则只调整储能出力和小型快速响应设备比如电锅炉、蓄电池尽量不动燃气轮机这类惯性大的设备。这里有一个非常容易踩的坑有些复现版本让日内滚动完全重新优化所有变量结果日内结果和日前计划差异非常大甚至出现燃气轮机日内启停与日前不一致的情况。这种模型虽然求解没问题但已经失去了双层调度的意义。解决办法就是我前面提到的偏差上限约束和偏离惩罚两者缺一不可。4. 完全复现从零搭建可运行代码的完整路径下面这部分是操作重点。我默认你已经了解基本优化建模但可能没写过完整的双层滚动调度程序。我尽量把每一步都讲清楚保证照着搭能跑通。4.1 复现环境与求解器选型我使用的环境是Python 3.10 Pyomo 6.x Gurobi 10.x。选Pyomo而不是直接写Gurobi API是因为Pyomo建模更接近数学表达式后期修改约束和变量非常方便。Gurobi求解MILP和MIQP性能突出对双层模型线性化后的大规模问题尤其合适。如果你没有Gurobi授权也可以换成COPT或MindOpt两者对线性规划和大规模混合整数问题支持都很好。开源方案可以选择SCIP但求解速度会明显慢一些尤其在处理大M线性化产生的数千个二进制变量时SCIP会比较吃力。模型文件结构我推荐这样组织project/ ├── data/ │ ├── load_profile.csv │ ├── pv_profile.csv │ ├── wt_profile.csv │ ├── price_elec.csv │ └── device_params.csv ├── models/ │ ├── upper_model.py │ ├── lower_model.py │ └── coupling.py ├── solver/ │ ├── solve_upper.py │ ├── solve_lower_rolling.py │ └── kkt_linearization.py ├── results/ │ └── outputs/ └── main.py这样分层的好处是后续你想换设备类型、改窗口长度或者加新约束不会牵连到其他模块。4.2 输入数据与典型参数表我在复现时使用了一个典型园区微网系统设备容量和成本参数如下你可以直接用于测试参数数值说明光伏容量200 kW峰值出力风电容量100 kW峰值出力燃气轮机容量300 kW电功率电热比 0.8燃气锅炉容量500 kW热功率电锅炉容量200 kW热功率电制冷容量300 kWCOP3.0吸收式制冷容量200 kWCOP0.8利用余热蓄电池容量500 kWh最大充放功率 100 kW效率 0.95储热罐容量200 kWh最大充放热功率 50 kW购电上限500 kW与主网联络线功率上限天然气价格3.2 元/m³折算成单位热值成本上网分时电价峰1.2/平0.8/谷0.4 元/kWh按峰谷平统计这些参数不需要完全照抄但量级要合理。很多复现结果跑出来设备不出力、储能乱充放问题往往出在成本参数失衡比如天然气价格过低导致燃气轮机全天满发储能完全没有存在价值。4.3 双层模型求解KKT条件与大M线性化双层模型如果直接用启发式算法嵌套求解速度慢且不稳定。我建议把下层LP的KKT条件写出来把双层问题转化为带互补约束的单层问题。对于下层连续线性规划KKT条件包含三部分下层拉格朗日函数对各变量的偏导等于0原始可行性约束对偶可行性以及互补松弛条件互补松弛条件的标准形式是λ_i * g_i(x) 0其中 λ_i 是约束对应的对偶变量g_i(x) 是下层约束的不等式左侧。这种乘积等于0的非线性约束可以用大M法线性化0 ≤ λ_i ≤ M * z_i-M * (1 - z_i) ≤ g_i(x) ≤ M * z_iz_i是额外引入的0-1变量M是一个足够大的正数。注意M不能取得过大否则Gurobi数值稳定性会变差。我一般的做法是根据每个约束的量级分别设置M比如功率平衡相关约束M1000SOC约束M500避免全模型共用一个10^6的M。如果下层模型包含0-1变量KKT条件就不能直接用了此时可以改用强对偶条件把下层的目标函数等价替换或者用Benders分解。我在复现中为保持精度让下层保持为纯线性规划把启停等整数决策全部放到上层。线性化后整个问题变成一个混合整数线性规划虽然规模变大了但Gurobi求解稳定性远好于原来的非线性双层问题。实际求解时我使用的是Gurobi MIP Gap设为1e-4单日滚动求解总耗时不超过两分钟。4.4 滚动求解器结构与伪代码下面这段伪代码是main.py的核心逻辑基本描述了滚动优化的完整流程# 固定的日前计划数据 up_schedule solve_upper_model(data) # 滚动优化循环 for k in range(0, 96, step): # 获取当前时段和未来窗口内的预测 pred_pv, pred_wt, pred_load get_forecast(k, horizon16) # 获取当前储能实际SOC soc_now get_current_soc(k) # 重新构建日内下层模型 lower_model build_lower_model( data, up_schedule, pred_pv, pred_wt, pred_load, soc_now ) # 求解 result solve_milp(lower_model) # 只执行第一个控制步 apply_control(result, time_index0) update_state(result, time_index0)这里有一个细节每个滚动窗口中日前基准值需要做时间对齐。因为日前步长是1小时日内步长是15分钟所以一个日前时段对应4个日内时段。代码里我专门写了一个函数把日前计划插值或重复映射到日内时间轴避免索引错位。5. 复现过程中最容易踩的坑这部分是我真正花时间最多的地方。模型能不能“复现成功”和“复现结果可信”是两件完全不同的事下面几个坑就是我亲身踩过的。5.1 双层KKT线性化后求解器报数值问题第一次跑通线性化后的模型时Gurobi报了numerical trouble一度让我怀疑KKT条件写错了。后来排查发现问题出在大M取值上。我当时图省事所有互补约束都用M1e6结果模型中同时存在数值很小的对偶变量和数值较大的功率变量矩阵条件数变得很差。解决办法就是把每个互补松弛约束的M单独设置。比如储能SOC约束涉及的功率量级是100 kWM取500就足够而联络线功率量级是500 kWM取1000。设置原则是M约为约束左侧量级上限的2~3倍。另外Gurobi的NumericalFocus参数建议从0改成1或2虽然会稍微增加求解时间但能明显减少伪解。还有尽量对所有连续变量设置合理上下界不要依赖自身约束来限界。5.2 日前计划在日内被“架空”第二个坑比第一个更隐蔽。我去掉日内下层目标里的偏离惩罚只保留耦合偏差约束时发现日内优化结果几乎每次都顶在偏差边界上。表面看约束满足实际运行成本可能比日前计划高出很多储能行为也变得很奇怪。原因是偏差约束只是限制偏差范围但没有让日内结果朝日前基准靠近。结果就是每个滚动窗口都会尽量选择边界值误差在每个窗口内持续累积。加上了偏离惩罚并且λ_pen设置成购电价格的0.5倍左右后日内结果才会稳定地贴着日前基准小幅调整。这个教训是对于滚动优化目标函数里的软惩罚有时比硬约束更重要。硬约束定义可行域软惩罚决定决策行为。5.3 冷热电联产的耦合约束遗漏我一开始只写了电、热、冷各自的独立平衡结果夏季场景下吸收式制冷利用率很低燃气轮机余热全都散掉了。后来才发现燃气轮机的余热产出和吸收式制冷的输入必须通过耦合约束连接起来。正确的写法是燃气轮机在产生电功率P_gt的同时产生热功率Q_gt且Q_gt与P_gt满足电热比关系Q_gt k_heat_ratio * P_gt。这部分余热可以被供热或者驱动吸收式制冷。吸收式制冷的消耗热功率 Q_abs 进入热平衡的负荷侧同时它产生的冷功率 C_abs COP_abs * Q_abs 进入冷平衡供给侧。没有这条耦合燃气轮机发电的余热价值就被白白丢掉了优化自然会偏向用电锅炉和电制冷。这种错误在纯电气微网背景的同学身上特别常见复现时一定要对照能源集线器结构逐一核对每条能流。5.4 结果不合理时如何排查我总结了一个高效的排查流程遇到结果明显不合理时按顺序检查先看设备出力是否在容量范围内如果长期顶上限检查成本参数和负荷数据是否有量级错误。再看储能SOC曲线是否连续有无突然跳变。如果有基本是SOC递推约束的时段索引写错了。看日前与日内结果的偏差如果偏差始终处于边界检查偏离惩罚和耦合约束。看冷热电平衡是否同时满足。把三个平衡约束的残差分别输出能快速定位哪个能流漏项。这套流程救了我好几次。很多时候模型求出的目标值很漂亮但平衡约束有微小缺口只是求解器容差把它们掩盖了。建议在求解完成后一定要单独做一次约束残差校验。6. 优化策略对比与典型结果复现模型跑通之后就可以在上面做优化策略研究。我会从三个维度展开滚动窗口长度、预测误差水平、双层与单层模型对比。6.1 滚动窗口长度的影响我用同样的日前计划分别测试了滚动窗口长度为1小时、2小时、4小时和8小时的情况。总运行成本和功率不平衡量的统计如下滚动窗口长度日内运行成本降低比例功率不平衡量1小时4.2%较高2小时7.8%中等4小时9.6%较低8小时9.8%最低这里我的结论是窗口超过4小时后成本下降幅度很小但求解时间明显增加。原因是更远的预测准确率下降对当前决策的优化价值减弱。所以4小时窗口对这套微网系统来说是性价比最优的选择。6.2 预测误差灵敏度分析保持滚动窗口为4小时不变我把日前预测误差标准差从5%逐渐增加到30%得到日内滚动优化的成本优势逐步缩小。预测误差为5%时日内滚动相比完全固化日前计划能降低约10%成本误差到30%时优势降到3%左右。这说明多时间尺度滚动优化的价值高度依赖于预测精度。如果你的预测系统很差先把预测做好远比优化调度模型更有价值。很多复现实验只报告理想预测下的收益这在工程上是偏乐观的。6.3 双层与单层模型的差异作为对照我另外搭建了一个不考虑上下层主从关系、以全天总成本为单一目标的集中式模型。结果并不意外集中式模型的理想成本比双层模型低5%~8%但一旦遇到预测误差集中式计划的执行偏差非常大实际落地产出可能比双层模型还差。双层模型的本质是用一部分经济最优性换取执行可靠性和稳定性。这个差异必须在论文或者技术报告里解释清楚否则评审或领导会质疑“为什么你的模型成本比别人的高”。6.4 求解器调试参数汇总最后分享一组我在反复测试后觉得比较稳的求解器设置参数推荐值原因MIPGap1e-4保证经济性比较的准确性NumericFocus1抑制大M线性化带来的数值噪声TimeLimit300秒防止个别极端场景求解超时Threads8充分利用多核CPUPreDual1改善对偶可行性另外在比较不同策略时一定要固定随机种子。误差场景用同样的随机种子生成否则实验结果的差异会被预测误差的随机性淹没无法归因到调度策略本身。7. 下一步我会怎么改这版模型完全复现只是起点。如果你要在真实系统中落地我个人认为最重要的改进方向是引入不确定性鲁棒优化或分布鲁棒优化把预测误差的正态假设改成更保守的模糊集。其次把可中断负荷、电动汽车充电桩这类柔性资源作为下层跟随者纳入模型可以让滚动优化的调节空间更大。如果算力足够还可以把目前的单目标成本最小化扩展为成本碳排放用户舒适度的多目标优化用帕累托前沿分析不同目标之间的权衡。不过所有扩展都必须建立在当前这套可靠的双层骨架上。骨架不乱扩展才有意义。我在这次复现里真正学到的一点是调度模型的难点永远不在数学有多深而在于是否能用工程手段把假设、约束和数据误差老老实实管理好。
返回列表