ARTICLE DETAIL

资讯详情

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

微电网MPC储能调度优化:Python实现模型预测控制滚动优化方案

微电网MPC储能调度优化:Python实现模型预测控制滚动优化方案 做微电网调度优化这几年我越来越觉得模型预测控制MPC是一个绕不开的解法。只要你的系统里有储能、光伏和不确定的负荷又想以最低成本维持功率平衡MPC基本上就是那个“既要又要”的答案。这篇文章不整虚的我直接把一套可以跑的微电网MPC调度优化方案拆开讲核心代码用Python实现从为什么选MPC到预测模型怎么建、目标函数怎么设再到用cvxpy做滚动优化的完整代码最后把实操里最容易踩的坑都列出来。适合刚接触电池储能调度的研究生也适合在项目里试过规则策略或PID、想升级控制方式的工程师。1. 为什么微电网调度偏偏选中模型预测控制1.1 微电网调度到底在优化什么先理清楚我们面对的问题。一个典型的微电网场景光伏发电、储能电池、负荷再加上和外部电网的并网点。调度任务往往以15分钟为间隔下发需要在这96个时间点上决定储能是充电还是放电、充多少、并网点是买电还是卖电。核心指标是运行成本最低硬性要求是功率平衡同时还得照顾储能寿命——不能让电池每天都在过充过放边缘疯狂试探。这其实有点像管家安排一家人的用电光伏是“自己家发电”电池是“蓄水池”电网是“外部供应商和回收站”。最理想的状态是光伏高峰时把多余的电存起来电价高峰时放出来用电价低谷时再买一点补充。问题是光伏出力会波动负荷也有峰谷电价还分时变化三个变量叠加在一起凭经验很难每次都做出最优决策。所以微电网调度本质上是一个带约束的序列决策问题当前时刻的充放电决策会影响下一时刻的SOC而下一时刻又会面临新的不确定性和新的电价环境。只看当前时刻必然短视看完整天又过于理想化。模型预测控制之所以能成为主流方向就是因为它把“预测未来”和“闭环反馈”结合了起来用一种有限时域滚动优化的方式逼近全局最优。1.2 为什么不能只靠规则策略或单次全局优化很多项目最开始用的是规则策略典型逻辑是“光伏大于负荷就充电光伏小于负荷就放电”。这套方案的优点是简单但缺点非常明显它不看电价。我见过实际项目里出现这种情况傍晚负荷高峰时电价已经很贵但电池因为傍晚前被“有光就充”的策略提前充满了结果到了最该放电的时段反而放不出多少电。还有更尴尬的场景规则策略在中午光伏大发时给电池充电等到下午电价高峰才放电效果还行可一旦遇到连续阴天或负荷突变规则就完全失灵。也有些人会尝试做全局优化比如用一整天的光伏、负荷曲线做一个开环最优计划。这种方法如果预测完美结果确实很漂亮但实际中根本做不到。光伏预测误差、负荷波动、设备故障都会让“计划”在几个小时后变成一张废纸。更关键的是开环优化没有反馈一旦执行偏离预期没有机制去修正后续决策。说到这里MPC的优势就很清楚了。它在每个采样时刻只信任最近一段时间内的预测数据求解一个有限时域的优化问题只实施第一步然后等到下一个时刻拿到新的测量值再重新滚动求解。整个过程是“预测—优化—执行—反馈”的闭环既保留了优化的前瞻性又通过反馈抑制了模型误差和预测误差的影响。1.3 MPC的数学描述没有想象中那么吓人用一个简单离散时间模型来描述储能系统SOC(k1) SOC(k) - [P_dis(k)/η_dis - η_ch·P_ch(k)] / E_cap其中SOC(k)是第k步的荷电状态P_dis是放电功率P_ch是充电功率η_dis和η_ch分别是放电和充电效率E_cap是储能额定容量。控制量是充放电功率和并网点功率扰动输入是光伏出力和负荷功率状态量就是SOC。MPC需要做的就是在每个时刻求解一个有限时域优化问题让未来N步内的总成本最小同时满足功率平衡、SOC上下限、功率限值等约束。这里的“N步”就是预测时域它决定了控制器往后看多远。如果预测时域太短控制器看不到晚高峰的高电价可能一早把电池的电放光了如果预测时域太长一方面预测误差会累积另一方面求解规模变大计算变慢。所以后续所有的参数设计和代码实现几乎都是围绕这个“有限时域滚动”来展开的。2. MPC微电网调度优化的核心设计拆解2.1 储能建模是首先要做的事储能是微电网调度的灵魂所以在MPC设计里电池模型必须准确且线性化。很多论文喜欢用二进制变量表示“充电状态”和“放电状态”让两个状态互斥。但实际做工程时我更喜欢把充电功率P_ch和放电功率P_dis都定义成非负变量并在目标函数里加上充放电功率对应的损耗惩罚项。只要惩罚系数为正优化器自己就会避免“一边充电一边放电”的浪费行为不需要额外引入整数变量。这样可以把问题保持为线性规划或二次规划求解速度会快很多。SOC的动态方程写成上面那种形式。注意充放电效率不对称所以P_ch项乘以η_ch、P_dis项除以η_dis这种不对称对结果影响很大。有些简化模型把效率统一成一个值短期看差别不大但如果需要评估全天电量损耗还是分开写更靠谱。2.2 功率平衡约束必须写对方向整个MPC里最容易出错的就是并网点功率的正负号定义。我习惯把P_grid定义为“从电网流入微电网为正”售电时P_grid为负。功率平衡约束写成P_grid(k) P_pv(k) P_dis(k) P_load(k) P_ch(k)左边是“流入”微电网的功率右边是“流出”的功率。光伏功率P_pv在光照充足时为正负荷P_load为正充电功率P_ch也作为负荷消耗放电功率P_dis作为电源供给。这个方向只要写反一个符号优化结果就完全乱套。排查问题时第一步永远是检查这个等式。另外目标函数里会用到购电量和售电量所以还需要把P_grid拆成两个非负变量P_grid_pos和P_grid_neg分别代表购电和售电并且满足P_grid P_grid_pos - P_grid_neg。在目标函数中给购电加权电价、给售电减售电电价优化器自然会在电价高时多卖电、电价低时多买电。2.3 目标函数与约束条件的完整写法目标函数我通常写成三项之和。第一项是购电成本第二项是售电收益第三项是充放电功率惩罚项用来保护电池、减少不必要的循环。如果希望全天结束时SOC维持在一个理想水平还可以加一个末端偏离惩罚项。minimize Σ [buy_price(k)·P_grid_pos(k) - sell_price(k)·P_grid_neg(k) λ·(P_ch(k) P_dis(k))] λ_soc·(SOC(N) - SOC_target)²约束条件包括功率平衡P_grid(k) P_pv(k) P_dis(k) P_load(k) P_ch(k)SOC上下限SOC_min ≤ SOC(k) ≤ SOC_maxSOC递推SOC(k1) SOC(k) - (P_dis(k)/η_dis - η_ch·P_ch(k))/E_cap充放电功率限制0 ≤ P_ch(k) ≤ P_bat_max0 ≤ P_dis(k) ≤ P_bat_max并网功率限制0 ≤ P_grid_pos(k) ≤ P_grid_max0 ≤ P_grid_neg(k) ≤ P_grid_max有了完整的问题描述后续写到Python里就是把这些公式逐行翻译成代码而已。2.4 关键参数怎么定才靠谱参数设计直接影响控制器表现。采样周期15分钟是最常见的配置既不会太频繁导致通信压力大又能跟上负荷和光伏的快速变化。预测时域N一般取24步也就是6小时这个长度可以覆盖一个典型的分时电价周期又不至于让预测误差累积太多。如果项目里晚高峰持续时间特别长可以适当增加到36步或48步。SOC的上下限要根据电池类型决定磷酸铁锂储能通常设0.1到0.9之间但为了保证寿命项目里我一般设0.2到0.9。末端SOC惩罚系数λ_soc要设得大一些否则控制器会在预测窗口最后把电池放空导致下一个滚动窗口起不到调度作用。经验值是让末端惩罚在数值上比单步电费成本高一个数量级。3. Python实现完整流程3.1 环境准备装好Python和优化库代码实现依赖几个库numpy负责数值计算scipy可以处理额外数据matplotlib负责画图cvxpy负责求解优化问题。如果你是从零开始装环境先安装Python 3.8或更高版本然后在终端执行pip install numpy scipy matplotlib cvxpy这里特别注意cvxpy的求解器。LP问题默认用自带的求解器就能跑但如果加了整数变量建议额外安装一个开源的mixed-integer求解器pip install glpk我实际开发时往往会先秒跑一个简单的LP验证模型方向确认无误后再考虑是否加入更复杂的约束。否则一上来就背上一大堆求解器依赖出了问题很难定位。3.2 数据准备与预测曲线生成为了演示我直接生成一组模拟的光伏和负荷数据。光伏曲线用白天峰值的正弦型曲线模拟负荷曲线设置早晚两个高峰电价按照峰谷平三段设置。这些数据在实际项目中会来自短期预测模型但MPC只关心“未来N步内的数据”所以哪怕预测数据不完全准确滚动优化也能持续修正。3.3 核心MPC滚动优化代码下面这段代码是我在实际项目里的简化版本去掉了交流损耗、变压器模型等非核心环节保留了MPC的全部关键流程。import numpy as np import cvxpy as cp import matplotlib.pyplot as plt # 基础参数 T_steps 96 # 一天96个点每15分钟一个 N 24 # 预测时域6小时 E_cap 100.0 # 储能容量 kWh soc0 0.5 # 初始SOC soc_min, soc_max 0.2, 0.9 eta_ch, eta_dis 0.95, 0.95 P_bat_max 30.0 # 最大充放电功率 kW P_grid_max 100.0 # 最大并网功率 kW lam 0.01 # 电池使用惩罚系数 lam_soc 1000.0 # 末端SOC惩罚 soc_target 0.5 # 模拟光伏、负荷、电价数据 np.random.seed(42) t np.linspace(0, 24, T_steps) P_pv np.clip(60 * np.sin(np.pi * (t - 6) / 10), 0, 60) np.random.normal(0, 1, T_steps) P_load 25 15*np.exp(-((t-8)/2)**2) 15*np.exp(-((t-19)/2)**2) np.random.normal(0, 0.5, T_steps) P_pv np.clip(P_pv, 0, None) price_buy np.where((t 10) (t 12) | (t 19) (t 21), 1.1, np.where((t 7) (t 22), 0.6, 0.3)) price_sell price_buy * 0.8 # 记录结果 soc_list [soc0] grid_list [] ch_list [] dis_list [] soc_current soc0 for k in range(T_steps): horizon min(N, T_steps - k) pv P_pv[k:khorizon] load P_load[k:khorizon] price_b price_buy[k:khorizon] price_s price_sell[k:khorizon] P_ch cp.Variable(horizon, nonnegTrue) P_dis cp.Variable(horizon, nonnegTrue) P_grid_pos cp.Variable(horizon, nonnegTrue) P_grid_neg cp.Variable(horizon, nonnegTrue) SOC cp.Variable(horizon 1) constraints [SOC[0] soc_current] for i in range(horizon): constraints [ P_grid_pos[i] - P_grid_neg[i] pv[i] P_dis[i] load[i] P_ch[i], 0 P_grid_pos[i], P_grid_pos[i] P_grid_max, 0 P_grid_neg[i], P_grid_neg[i] P_grid_max, 0 P_ch[i], P_ch[i] P_bat_max, 0 P_dis[i], P_dis[i] P_bat_max, P_ch[i] P_dis[i] P_bat_max, soc_min SOC[i1], SOC[i1] soc_max, SOC[i1] SOC[i] - (P_dis[i]/eta_dis - eta_ch*P_ch[i]) / E_cap ] cost cp.sum(cp.multiply(price_b, P_grid_pos) - cp.multiply(price_s, P_grid_neg) lam * (P_ch P_dis)) \ lam_soc * (SOC[-1] - soc_target) ** 2 prob cp.Problem(cp.Minimize(cost), constraints) prob.solve(solvercp.OSQP) if prob.status ! cp.OPTIMAL: print(fStep {k}: optimization failed, status{prob.status}) break P_ch_opt P_ch.value[0] P_dis_opt P_dis.value[0] P_grid_opt P_grid_pos.value[0] - P_grid_neg.value[0] soc_current SOC.value[1] soc_list.append(soc_current) ch_list.append(P_ch_opt) dis_list.append(P_dis_opt) grid_list.append(P_grid_opt) print(Total buy cost:, np.sum(price_buy * np.maximum(grid_list, 0))) print(Total sell income:, np.sum(price_sell * np.maximum(-grid_list, 0)))这段代码有几点要注意。P_ch P_dis P_bat_max 这条约束替代了二进制互斥变量加上目标函数里的lam惩罚优化器不会出现同时充放电的无效解而且求解速度明显更快。SOC末端用软惩罚让SOC自动回归目标值比强制约束“必须恰好到某个SOC”更稳定尤其是在预测不准时。3.4 结果可视化与指标评估跑完上面的代码后最好把SOC曲线、充放电功率、并网购售电功率和电价画在同一张图里能直观看出MPC是否在“低谷充电、高峰放电”。我习惯用三个子图叠加上面是光伏和负荷曲线中间是充放电功率和SOC下面是分时电价和并网功率。看结果时重点关注三个指标购电成本、售电收益、电池循环量。购电成本和售电收益直接决定经济性电池循环量则是储能寿命的关键指标。如果发现SOC频繁撞到上限或下限说明SOC约束范围或功率限制参数设得太紧如果SOC长期停留在0.9附近可能是末端惩罚没有起作用或电价峰谷差不够。4. 实操中的常见问题与排错技巧4.1 优化求解报无解怎么排查MPC无解是最常见的坑。第一次跑模型时大概率会遇到statusinfeasible。先别急着怀疑求解器从最简单的地方查起功率平衡约束方向有没有写反SOC递推公式里效率符号有没有搞错。还有一个经常被忽略的点初始SOC不满足上下限约束。如果soc00.15但SOC下限是0.2第一步就无解了。如果模型本身没问题但某些极端场景下无解最好改用软约束。把SOC上下限约束改成惩罚项让优化器在“稍微越界”和“大幅改变充放电策略”之间做个权衡。软约束会让问题几乎永远有解这也是工程上比论文更实用的处理方式。4.2 预测不准时MPC还能不能打很多初学MPC的人以为预测必须很准其实不然。MPC最大的底气不是精确预测而是滚动反馈。光伏突然被云遮住当前时刻实际功率和预测差了很多那也没关系下一个采样周期会用新的测量值刷新SOC重新优化未来N步。这就像开车导航走错一个路口后重新规划路线而不是要求导航预知一切。但如果预测偏差持续且系统化仅靠滚动还不够。可以加一个简单的在线修正把“上一时刻预测误差”作为修正项叠加到未来PV预测上。这种做法在很多项目里比单纯换模型更有效。更严谨的做法是用场景随机优化或鲁棒MPC把预测不确定性显式放进约束里代价是求解复杂度成倍增加。4.3 计算太慢怎么办如果预测时域一长求解就变慢先检查是否用了整数变量。二进制变量是求解速度的杀手能避免就避免。我用P_ch P_dis P_bat_max替代互斥变量就是出于这个考虑。其次可以缩短预测时域很多时候24步降到16步效果损失不大但速度提升明显。还有一招是保留同一个cvxpy Problem对象在滚动循环中只更新参数不重新构建模型能省掉一大部分建模开销。4.4 末端效应导致SOC漂移怎么处理MPC只关心有限时域所以会“短视”地在预测窗口末尾用光电池。解决末端效应主要有三种办法一是加末端成本函数也就是代码里的lam_soc惩罚项二是给最后一步SOC加一个范围约束比如允许偏离目标值不超过0.1三是直接把问题拉长到全天变成开环全局优化。在真实运营里终端SOC漂移会直接影响第二天调度所以末端惩罚系数宁可大一点也不能让SOC不管不顾地冲到边界。5. 实测案例一次24小时滚动调度全程记录5.1 场景数据与参数设置我构造一个典型的工业微电网场景储能容量100kWh最大充放电功率30kW初始SOC为0.5SOC允许范围0.2~0.9光伏峰值60kWp负荷峰值约55kW。分时电价按峰平谷设置高峰1.1元/kWh在10点-12点和19点-21点平时0.6元/kWh在7点-22点低谷0.3元/kWh在夜间和23点。采样周期15分钟预测时域6小时。光伏曲线模拟晴天特征早上6点开始出力中午达到峰值后逐渐降低。负荷曲线设置了早高峰和晚高峰特别是晚高峰正好落在19点-21点的高电价区间这是考验MPC削峰填谷能力的关键时刻。5.2 MPC调度结果与规则策略对比我拿同一个场景跑了两套策略。一套是MPC另一套是我在前面提过的规则策略光伏大于负荷就充电光伏小于负荷就放电完全忽略电价信号。一个典型的24小时结果对比如下指标MPC方案规则策略购电成本约628元约735元售电收益约52元约21元净电费约576元约714元电池充放电循环深度约0.65约0.82MPC能省下近20%的电费主要省在三个方面第一低谷电价时段主动从电网买电把电池充满而不是只等光伏充电第二晚高峰来临前SOC已经预留了足够电量不会出现规则策略“光伏不够就早早放光”的尴尬第三在中午光伏大发时把多余电量卖给电网赚取收益而不是让光伏白白弃掉。规则策略虽然也能在晚高峰放电但它缺少对电价峰谷的主动响应白白浪费了很多低价时段。5.3 调参心得与后续扩展这套MPC调参有几个个人体会。第一末端SOC惩罚系数不能只看数值大小要结合储能容量和电价量级一起看。我之前把lam_soc设为100结果末端SOC还是会被拉到0.9以上后来改成1000才稳定。第二预测时域至少要覆盖一个完整的电价高峰段否则控制器永远“看不见”晚高峰。第三电池使用惩罚系数lam不能设太大否则储能几乎不动作MPC退化成纯电网购电策略。后续扩展可以考虑把光伏预测换成真实预测模型把确定性MPC换成带场景树的随机MPC或者把储能老化模型加入目标函数。每加一层模型控制器都会变得“更聪明”但对应的求解复杂度和参数标定工作量也上来了。从工程落地的角度来说先把基础MPC调到稳定再逐步叠加复杂度才是最稳妥的路径。最后分享一个小技巧在目标函数里同时保留末端SOC软惩罚和SOC上下限硬约束是防止控制器“短视”和“病态”最有效的组合拳。
返回列表