ARTICLE DETAIL

资讯详情

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

电力系统多时间尺度优化调度与模型预测控制(MPC)实战解析

电力系统多时间尺度优化调度与模型预测控制(MPC)实战解析 做电力系统优化调度的朋友应该都碰到过这种局面日前计划做得再细第二天新能源一波动全盘作废。计划跟实际经常能差出一大截储能该充的时候没充该放的时候没放可调负荷也没顶上最后只能靠昂贵的备用容量硬扛。根本原因只有一个——用单一时间尺度去应对多时间尺度的不确定性。多时间尺度的灵活性资源优化配置正是为解决这个问题来的。它的核心是把日前调度、日内滚动优化、实时调整按不同时间尺度分层协调模型预测控制MPC则承担了日内滚动优化里最关键的角色。这篇文章我会从整体思路、模型搭建、程序实现到常见坑点把一套可落地、能跑通的做法讲透。无论你是刚接触综合能源调度的研究生还是已经在做优化调度的工程师都可以直接参考这套方案。1. 为什么要做多时间尺度优化配置1.1 单一时间尺度解决不了什么问题先讲个我之前踩过的场景。某个园区微网光伏装机1.2MW配了一套500kWh储能还有200kW可调负荷。我们最开始只做了日前调度按24小时、1小时一个点去优化目标函数是运行成本最小。结果问题特别典型中午光伏突然被云遮了出力比预测低了30%左右但储能还按原计划在11:30开始放电。等到下午电价高峰储能SOC已经见底只能从配网购高价电。整个运行成本反而比不做优化还差。这个案例说明什么日前调度用的预测精度再高也不可能完全预知实际功率。光伏、风电、负荷都有分钟级甚至秒级波动而日前调度只有一个小时级分辨率。在这种粗粒度下储能和可调负荷这类响应快的灵活性资源根本没有机会在实时波动发生时及时调整。你给了它一个基准轨迹但实际运行早就偏离这个轨迹了。单一时间尺度还有另一个问题实时控制又过于短视。如果只看当前五分钟的数据储能可能会因为短时波动频繁充放既伤设备又增加寿命损耗。所以我们需要一个能在“看得远”和“跟得住”之间平衡的机制。1.2 多时间尺度如何分工多时间尺度优化配置的思路就是让不同时间尺度的调度器各管一段日前的管大局日内的管修正实时的管细节。日前尺度Day-ahead提前一天做24小时调度时间间隔通常是1小时。主要决定机组启停、储能SOC参考轨迹、与上级电网的购电计划以及可调负荷的预约安排。因为它面对的是最不确定的预测所以重点不是精细到每个五分钟而是把“大框架”定下来。日内尺度Intraday在当天运行中按15分钟或5分钟滚动执行。每隔一个周期用最新的预测数据和实测状态重新求解一个有限时域优化问题只执行第一个控制指令然后滚动到下一个周期。这一层主要负责对付光伏波动、负荷偏差等短时不确定性是灵活性资源利用最充分的一层。实时尺度Real-time秒级或分钟级自动响应。比如储能参与一次调频、二次调频或者本地控制策略根据频率/电压信号自动调整出力。这一层通常不需要大规模优化求解而是快速闭环。这三层之间的关系不是各管各的而是层层约束、逐级递进。日前的计划给日内提供边界条件比如储能SOC的上下界、机组爬坡空间日内的决策结果又作为实时控制的参考基点。如果你只做日内滚动但没跟日前计划衔接就可能出现日内优化把储能耗尽导致晚高峰没电可用的尴尬。1.3 为什么选择MPC作为日内核心算法说到日内滚动优化很多人会问直接做全局优化不就行了理论上可以但实际问题里预测是滚动的未来信息在不断变化。你不可能在早上十点就确定下午三点的准确出力。模型预测控制MPC恰好适合这种场景。MPC本质上是一种基于模型的有限时域滚动优化策略三个特征非常契合日内调度预测模型、滚动优化、反馈校正。每个控制周期开始它都会基于当前实测状态和最新的未来预测求解从当前时刻到未来Np步的优化问题得到一组最优控制序列但只把第一个控制量下发执行。下一个周期到来时再用新的实测状态和预测重新求解。这个过程很像开车时根据路况不断调整方向盘而不是出发前在地图上画一条直线就闭眼开。因为MPC自带反馈所以即使预测模型有偏差只要每个周期都把实测状态拿回来重新校正就不会让误差一路累积下去。2. 核心方案模型预测控制与日内滚动优化2.1 MPC的数学模型怎么搭要落地MPC第一步是把被控对象写成离散状态空间方程。这里我用储能可调负荷电网交互的简单系统举例。状态量x一般包括储能SOC、设备启停状态如果有机组。控制量u包括储能充放电功率、可调负荷调节量、机组出力、购电功率。扰动量d包括光伏/风电预测出力、负荷预测值。状态方程可以写成x(k1)A x(k)B_u u(k)B_d d(k)其中A对应SOC的自保持系数比如SOC对SOC的传递关系B_u对应储能充放电功率对SOC的影响B_d对应预测负荷和新能源对系统状态的扰动。这里要注意如果你把功率平衡约束当作等式约束放在优化模型里状态方程通常只描述SOC动态。不要把功率平衡也塞进状态方程里那样会把问题搞乱。MPC在每个时刻k求解的优化问题可以抽象为min J Σ_{i0}^{Np-1} [运行成本 控制变化惩罚 松弛惩罚]s.t. x(ki1)A x(ki)B_u u(ki)B_d d(ki) x_min ≤ x(ki1) ≤ x_max u_min ≤ u(ki) ≤ u_max 功率平衡等式约束 爬坡等动态约束这里Np是预测时域也就是“往后看多少步”。每步对应一个控制周期比如15分钟Np8就是看未来2小时。2.2 目标函数与约束条件的设计取舍目标函数不能只写“购电成本最小”一项否则滚动优化结果会非常激进。我的经验是把这几项都放进去购电成本从上级电网购电的电费分时电价场景下按实时电价折算。燃料成本分布式燃气轮机或柴油机发电的燃料消耗。储能退化成本储能充放电循环对寿命的损耗可以用等效成本折算到每次放电电量。可调负荷补偿调用可调负荷时给用户的补偿费用。弃光弃风惩罚如果系统允许弃新能源要在目标函数里加一个较大的惩罚系数避免优化器为了省成本随意弃电。控制增量惩罚对储能出力、购电功率的变化量加二次项惩罚抑制抖振。约束方面功率平衡是必须满足的硬约束。对储能SOC要加上下限约束而且上下限不要用0和1这种极端值建议留5%的缓冲。可调负荷要设置单次最大调节量和总调节次数限制。机组爬坡约束要按时间步长折算成每步最大出力变化量。有一个非常实用的技巧把容易出问题的约束设成软约束引入松弛变量。比如功率平衡的松弛变量s1SOC越限的松弛变量s2目标函数里加一个上千倍的大M惩罚。这样求解器哪怕遇到极端预测数据也不会直接报不可行而是会尽量找到一个可用解方便你做后续诊断。2.3 预测模型和误差反馈校正MPC的效果很大程度取决于预测。但工程上我们不需要非常高质量的预测因为滚动反馈本身已经能纠偏。最简单的方式是用历史均值或ARIMA模型做超短期预测。如果系统有数值天气预报接口也可以把预测数据直接导入。无论哪种方式都要注意预测序列和当前时刻对好索引预测的第i个值必须对应未来第i个控制周期。反馈校正是MPC区别于开环优化的关键。具体做法是每个滚动周期开始用上一周期模型的预测值和实际测量的偏差e(k)y_meas(k)-y_pred(k)去修正未来所有预测点。一种简单修正是d_corrected(ki)d_forecast(ki)α·e(k)其中α是反馈增益通常取0.3~0.8。如果实际值比预测值低就把未来预测往低修。另外状态量一定要用实测值比如储能SOC要用实际监测值初始化而不是用上一周期模型预测的SOC。这样误差不会滚动累积。3. 程序实现的整体思路与关键模块3.1 工具选型与代码架构做日内滚动优化我常用MATLABYALMIPCPLEX的组合。YALMIP建模非常直观CPLEX求解线性规划和二次规划速度快。如果之后要部署到生产系统可以换成PythonPyomoGurobi逻辑完全一致。程序整体建议分四层千万不要所有逻辑堆在一个脚本里数据层管理负荷、新能源、电价、储能参数统一格式和时间戳。预测层根据当前时刻生成未来一段时间的预测序列。模型层根据预测和参数构建MPC模型包括变量、约束和目标函数。执行层主循环调度求解、下发指令、更新状态、记录结果。目录结构大概是这样/ data load_day.csv pv_day.csv price.csv / models build_mpc_model.m forecast_update.m / solver solve_mpc.m / main run_rolling_opt.m3.2 数据准备与场景生成数据是一切的基础。首先要统一时间分辨率和时区日内滚动是15分钟那么全链路数据都对齐到15分钟。别把一个5分钟的功率数据插值到15分钟再用插值会平滑波动预测误差反而被低估。如果只有典型日数据可以叠加高斯噪声生成多个场景用来测试MPC的鲁棒性。测试时先跑确定性场景再跑随机扰动场景这样能快速分辨是模型问题还是数据问题。数据单位也要统一。我习惯把所有功率都用标幺值基准值选系统最大负荷或变压器容量。SOC用0-1表示电价用元/kWh。如果不做标幺储能SOC小数和电价大数值混在目标函数里求解器很容易出现数值病态收敛特别慢。3.3 关键模块的代码实现思路用YALMIP构造MPC模型非常紧凑。核心代码如下function [u_opt, x_pred] mpc_step(x0, d_forecast, params) Np params.Np; u sdpvar(params.nu, Np); x sdpvar(params.nx, Np1); constraints []; constraints [constraints, x(:,1) x0]; for k 1:Np % 状态递推 constraints [constraints, ... x(:,k1) params.A * x(:,k) ... params.Bu * u(:,k) ... params.Bd * d_forecast(:,k)]; % 状态约束 constraints [constraints, ... params.x_min x(:,k1) params.x_max]; % 控制约束 constraints [constraints, ... params.u_min u(:,k) params.u_max]; % 功率平衡以简单形式示意 constraints [constraints, ... params.Cp * u(:,k) d_load(:,k) - d_pv(:,k)]; end % 目标函数 objective 0; for k 1:Np objective objective params.cost_u * u(:,k) * params.dt ... params.lambda * (u(:,k) - u_last(:,k)) * (u(:,k) - u_last(:,k)); end options sdpsettings(solver, gurobi, verbose, 0); optimize(constraints, objective, options); u_opt value(u); x_pred value(x); end注意目标函数里的控制增量项u_last是上一时刻实际下发的控制量。加这一项能显著减少抖振。3.4 滚动优化的执行流程主循环是整个程序的心脏逻辑非常清晰for t 1:total_steps % 1. 更新预测序列 d_forecast forecast_update(t, historical_data); % 2. 获取当前实测状态 x_real get_meas_state(t); % 3. 求解MPC [u_opt, x_pred] mpc_step(x_real, d_forecast, params); % 4. 只执行第一个控制指令 u_apply u_opt(:,1); apply_control(u_apply); % 5. 记录结果 record(t, x_real, u_apply, x_pred); % 6. 等待下一个控制周期 wait_until(t1); end这里最容易被忽略的是第4步。MPC算出了从k到kNp-1的完整控制序列但只能执行第一个。如果贪心把整条序列都下发一旦预测误差变大后续几步就会出现较大偏差滚动优化的意义就没了。4. 核心参数与代码细节4.1 时间尺度与控制周期的选择具体怎么选时间尺度取决于系统里灵活性资源的响应速度。时间尺度周期预测时域主要控制对象日前调度1小时24小时机组启停、储能SOC基准、购电计划日内滚动15分钟4-8步1-2小时储能充放电、可调负荷、机组出力修正实时控制1秒-5秒若干控制周期储能自动调整、AGC、备用响应如果园区有快速波动负荷比如充电桩日内周期建议压缩到5分钟。如果侧重热负荷热惯性大可以放宽到30分钟。我一般先按系统最快灵活性资源的响应时间除以3左右来定控制周期比如储能响应2分钟日内周期取5分钟没有快速资源则取15分钟。4.2 预测时域与控制时域预测时域Np决定MPC的“眼光”有多长。太短看不到未来约束突破容易短视太长计算量大而且远期的预测意义不大。经验值取Np4~8。比如控制周期15分钟Np8就是未来2小时能覆盖大部分云层遮挡和负荷突变周期。控制时域Nc可以比Np短表示控制量在未来多少步内允许变化之后保持恒定。Nc取1~3比较合适。如果Nc太大MATLAB或Python端的求解时间会明显上升而且储能等设备会被允许剧烈调整实际运行时反而不稳定。调参方法很简单先用Np6、Nc2作为初值观察SOC曲线有没有频繁触边如果触边就把Np调大如果控制序列抖动严重就把Nc调小或增大控制增量惩罚系数。4.3 灵活性资源的建模要点不同类型的灵活性资源建模细节差异很大直接影响程序复杂度。储能电池SOC递推为SoC(k1)SoC(k)η_ch·P_ch·Δt/E_rated-(1/η_dis)·P_dis·Δt/E_rated。这里η_ch和η_dis是充放电效率通常各取0.9左右。充放电功率要分两个非负变量建模并加互斥约束如果不想引入整数变量可以不加靠经济性自然选择。可调负荷定义削减量ΔP_ls约束0≤ΔP_ls≤ΔP_ls_max。注意削减后要满足用户温度等舒适度限制例如连续削减次数不超过N次。分布式机组需要限制出力上下限和爬坡速率P_min≤P≤P_max|P(k1)-P(k)|≤ΔP_ramp·Δt。如果机组启停频繁启停则需要引入二进制变量。上级电网交互购电功率上限P_buy_max如果要考虑需量电价目标函数里要加一个高峰期最大需量成本项这个会显著增加模型复杂度。建模一条原则能用线性约束表达就别用非线性。比如储能充放电互斥可以用二进制变量但如果你不想混合整数规划可以忽略互斥因为目标函数通常不会让它同时充放。这样可以大幅提升求解速度。5. 实操过程与踩坑实录5.1 求解器报不可行怎么办MPC程序最常见的问题就是某个周期优化无解。我调试时一般按这个顺序排查检查功率平衡约束是否本身无解。比如预测负荷2000kW但光伏购电上限只有1800kW那必然不可行。这种情况要允许削减可调负荷或引入松弛变量。检查SOC约束是否过紧。储能SOC若被限制在0.3~0.9但预测连续两个时段都要求充电可能越限。可以临时把SOC范围放宽到0.1~0.95看是否变可行。检查数据单位是否统一。功率用kW但储能容量用kWh时间步长如果按小时还是分钟会差60倍这种错误很隐蔽。检查变量索引。YALMIP里for循环构造约束时如果索引从1开始但预测序列从0开始会导致约束错位。如果实在查不出就在功率平衡约束上加一个松弛变量s目标函数加惩罚s sdpvar(1, Np); constraints [constraints, power_balance s 0]; constraints [constraints, s 0]; objective objective 1e6 * sum(s);这样做能让优化器在极端情况下给出一个“软解”帮你分析是哪个约束被违背。5.2 滚动优化结果抖振/震荡运行MPC后如果储能出力每隔一个周期就大幅反向变化目标函数波动很大大概率是缺了控制增量惩罚。你需要在目标函数中加入objective objective lambda_delta * sum(sum((u(:,2:Np) - u(:,1:Np-1)).^2));lambda_delta我一般取成本系数的0.1~0.5倍。太小没效果太大又会把控制量锁死不跟随预测变化。还有一种可能是预测噪声太大。可以对预测序列先做一次低通滤波或者用移动平均窗口。但注意不要过度平滑否则日内滚动就失去了快速响应能力。5.3 计算时间超标怎么办在实时性要求高的场景每个控制周期留给求解的时间可能只有几十秒如果模型复杂CPLEX求解时间可能超标。我常用的几个办法缩短Np从8降到4计算量差不多能减半。把非关键约束从硬约束变软约束减少求解器在可行域边界的反复搜索。提供热启动。YALMIP可以用assign和optimize的init参数把上一周期的最优解作为本次的初始解能显著加速。最后的大招是离线计算显式MPCexplicit MPC把状态空间分区每个区域对应一个线性控制律。在线时只需要查表不需要调求解器。但实现复杂度高一般到第三代优化时才考虑。5.4 排错速查表现象可能原因处理方式SOC长期偏低或偏高反馈校正系数太小调大α或检查SOC实测值是否更新目标函数值异常数据量纲不一致统一标幺值或单位时间步长联络线功率越限约束遗漏或索引错位检查约束构造循环范围和边界求解时间暴涨预测时域太长或有整数变量缩短Np取消不必要的二进制变量解抖动缺少控制增量惩罚加Δu二次项Nc调小日内结果偏离日前太多日前与日内约束未衔接日内模型加上日前SOC轨迹参考约束我自己在调这套程序时最头疼的其实不是MPC本身而是把数据、模型、求解器三者的时间戳对齐。预测序列第几个点对应未来哪个时刻、状态变量用实测值还是预测值这些细节一乱程序就跑出各种奇怪的结果。建议第一次做多时间尺度灵活性资源配置的朋友先从最简单的单时间尺度MPC开始只包含储能和一个固定风光预测把闭环跑通再加上日前层和日内层的嵌套。还有一个很实用的小技巧先用确定性数据测试再叠加随机扰动这样能快速定位是模型问题还是数据问题。等这套流程稳定了你再往里面加可调负荷、机组启停、实时控制层就不会被一堆问题同时卡住。
返回列表