刚接触微电网调度优化时,我踩过最大的坑就是把“调度”理解成简单的峰谷套利规则:电价高峰放电、低谷充电、光伏优先消纳。这套规则在晴天好用,可一旦光伏被云遮、负荷突变,规则就失灵了。后来我在一个园区级微电网项目里把方案改成模型预测控制(MPC),情况立刻不一样。所谓MPC,就是每个调度时刻都基于当前实测状态和未来一段时间的预测数据,在线求解一个有限时域优化问题,只执行第一个控制动作,下一时刻再重新滚动。换成微电网调度优化的话说,储能充放、电网购售电的决策不再是一张“死计划”,而是跟着实时状态和预测误差不断修正的动态策略。本文把这套方案的模型原理、Python实现和实战中踩过的坑完整梳理一遍,适合正在做电力系统优化的研究生、搞储能EMS的工程师,以及想入门预测控制的新人参考。
1. 微电网调度为什么需要MPC
1.1 传统调度的痛点与MPC的引入
传统微电网调度通常分两类:一类是纯规则调度,比如“光伏出力优先给负荷,多余给储能充电,缺电时从电网购电”,另一类是日前优化调度,提前一天根据光伏、负荷预测曲线和分时电价,求解未来24小时的最优储能功率序列。
规则调度的优点是简单、可解释性强,缺点是完全不考虑电价动态和储能的综合效益。举个实际例子:中午光伏大发时,如果电价正处于平价段,而傍晚峰段电价很高,规则调度可能会把过多的能量塞进储能,导致傍晚之前储能已接近满充,失去了利用峰谷价差的机会。规则调度看起来每时每刻都“符合逻辑”,但放在全局时间轴上,往往不是最优的。
日前优化调度的思路先进一些,它把未来24小时的钱和约束放在一个目标函数里求全局最优。但这个方案有一个致命弱点:预测结果和实际运行状态不一致。光伏预测误差在晴天能控制在10%以内,多云天经常飙到20%以上,一旦实际出力偏离计划值,后续时段的调度计划全盘失效。更麻烦的是,如果调度员或EMS按计划执行,而实际SOC因效率损耗发生偏移,几天之后计划值和真实值之间的偏差会越滚越大。
MPC解决的就是这个痛点。它不追求一次性把24小时的最优解算干净,而是把优化窗口压缩成未来一小段,比如未来6小时、15分钟一个步长,共24步,只执行第一步,到下一个调度时刻重新读取实测状态和最新预测,再算一遍。相当于开车导航不是从出发时一口气规划到终点,而是每隔几分钟根据实时路况重新计算未来半小时的路线。这个“滚动优化、逐步修正”的思路,恰恰是微电网这种强不确定场景最需要的。
1.2 总体架构:预测、优化、执行、滚动的闭环
我在项目中落地MPC微电网调度时,整体架构分成了四个模块:预测模块、状态测量模块、优化求解模块和执行模块。
预测模块负责给出未来N个采样步长的光伏出力和负荷功率,来源可以是数值天气预报换算、历史相似日检索,也可以是最简单的指数平滑外推。实测下来,预测数据不需要非常精确,但对趋势的把握要准,否则MPC的优化结果会跟着错误预测走,反而比简单规则更离谱。状态测量模块读取储能SOC、当前实际功率、并网点功率等实时数据,这是MPC闭环的锚点。优化求解模块接收预测和实时状态,在当前时刻t构建一个从t到t+N-1的有限时域优化问题,输出未来N步的最优控制序列。执行模块只取控制序列的第一组动作,下发到储能变流器和并网开关,让系统实际执行。到t+1时刻,再读取新状态、新预测,重复上述过程。
这个闭环结构里,最容易被忽视的是“只执行第一步”这件事。很多新手第一次写MPC,习惯把整个序列全部执行完,这就退化成了预测控制的反面教材:开环控制。一旦开环执行,预测误差和模型误差会直接累积,MPC的抗扰优势完全浪费。我一般会在主循环里严格控制,只把u[0]传入仿真或实际设备,剩余序列直接丢弃。
1.3 什么场景下MPC最值得用
并不是所有微电网都需要上MPC。如果是纯离网、负荷固定、光伏装机很小的系统,规则调度甚至固定出力就能满足需求,上MPC属于杀鸡用牛刀。我自己判断要不要用MPC,主要看三个特征。
第一,系统里存在可控的储能或者可调负荷。MPC输出的是控制量,如果系统里压根没有可控设备,比如光伏直接带负载、没有储能也没有柴发,那优化无从谈起。第二,电价或运行成本具有明显的时序差异。分时电价、现货电价、需求响应激励,这些时间维度上的价格波动是MPC收益的主要来源,MPC能自动把成本考虑进预测时域内统筹分配储能行为。第三,光伏或负荷预测存在显著不确定性。如果一个系统环境稳定、光伏变化平缓,一次日前优化就能解决大部分问题;反之,如果经常出现云层快速移动、负荷大范围波动,MPC的滚动修正能力会显著优于固定计划。
满足这几点的典型场景,包括园区光储微电网、商业楼宇光储充系统、含多个发电单元的独立微电网,以及未来参与现货市场交易的用户侧储能。在这些场景里,MPC不是花哨的学术玩具,而是真正能产生经济效益的工程手段。
2. MPC核心原理与微电网模型搭建
2.1 微电网典型拓扑与基础模型
微电网调度优化里,建模的尺度通常在母线级,不需要考虑线路潮流和电压分布,而是把整条母线看作一个功率平衡节点。系统中每个设备以功率注入或消耗的形式出现在平衡方程里。
以最常见的并网型光储微电网为例,系统包含光伏阵列、储能电池、本地负荷以及与大电网的交换点,功率平衡方程为:
P_pv(t) + P_grid(t) + P_bat(t) = P_load(t)
这里P_pv和P_load作为预测给定的外部输入,是“扰动量”;P_bat是储能的充放电功率,定义为正表示放电、负表示充电;P_grid是并网点功率,正表示从电网购电、负表示向电网售电。
光伏模型在调度时间尺度下不需要精确的I-V曲线,直接采用预测序列即可。如果本地有辐照度数据,可以用简化效率模型折算:P_pv(t) = eta_pv * S(t) * A_pv,其中S是辐照度,A_pv是光伏板面积,eta_pv是综合转换效率。工程上我更喜欢直接用逆变器上报的预测功率序列,省去建模误差。
负荷模型同样简化,按预测序列给定。如果系统含有可平移负荷或可削减负荷,需要额外引入0-1变量或者连续功率调节变量,这会增加模型的复杂度,入门版本可以先不纳入,先把储能和电网交互这条主线打通。
2.2 储能模型与约束的数学表达
储能是MPC微电网调度中最核心的建模对象,因为它的“状态”SOC会跨时段累积,直接决定了MPC动态优化的意义。SOC的状态转移方程是:
SOC(k+1) = SOC(k) - eta_c * Tc * P_bat_c(k) / C_rated + (1/eta_d) * Tc * P_bat_d(k) / C_rated
这个公式里有两个变量P_bat_c和P_bat_d,分别表示充电功率和放电功率,都是非负的。eta_c是充电效率,eta_d是放电效率,C_rated是电池额定容量,Tc是采样步长对应的小时数。之所以要把充放电拆成两个变量,是因为充放电效率不同,如果只用一个变量表达,无法同时体现两段效率差异。当然,如果不强制约束P_bat_c和P_bat_d不能同时非零,优化器可能会为了利用效率不对称性而出现“既充电又放电”的荒谬解。解决办法有两个:一是引入二进制变量约束二者互斥,二是通过设定成本项让同时充放电经济上不划算。在入门代码里,可以先用线性成本让充放电互补,后面再讨论MIP方案。
SOC的约束条件包括上下限和功率限幅:
SOC_min <= SOC(k) <= SOC_max
0 <= P_bat_c(k) <= P_bat_c_max
0 <= P_bat_d(k) <= P_bat_d_max
这里需要注意SOC_min和SOC_max的设置。很多项目为了让电池多存电,会把SOC下限放到20%甚至10%,但我建议综合考虑电池循环寿命和调度鲁棒性,设计在30%到90%比较稳妥。SOC下限太低,遇到连续阴雨天可能面临容量不足,MPC即便能够提前规划,也会因为预测误差被迫频繁深度充放,这对电池健康影响很大。
2.3 MPC优化问题的数学框架
MPC在每个时刻t求解的核心问题可以写成如下形式:
min sum_{k=0}^{N-1} [ C_price(t+k) * P_grid(t+k) * Tc + lambda * (P_bat_c(t+k) + P_bat_d(t+k)) ]
约束条件包括功率平衡、储能SOC转移方程、储能功率上下限、SOC范围,以及电网交互功率的允许区间。N是预测时域长度,C_price是分时电价或实时电价,第二项是储能使用成本,用充放电功率之和乘以一个很小的系数lambda表示电池损耗的罚项,这是避免电池频繁动作的重要技巧。
目标函数里还应该根据实际需要增加光伏弃光的惩罚项、负荷切除的惩罚项等。比如允许光伏逆变器限功率,那么P_pv_max和实际发电量之间会出现差额,差额乘以一个较高的惩罚系数纳入目标函数,MPC就会自动权衡“弃光多一些”还是“储能多充一些”。
为什么MPC在数学上适合微电网?因为它本质上是一个带约束的有限时域最优控制问题,而这个问题的结构恰好符合凸优化或二次规划的标准形,求解速度快、稳定、易于工程实现。当预测时域N较短时,变量规模只有几十到几百个,主流的嵌入式处理器都能跑起来。我在树莓派上用cvxpy加OSQP,15分钟采样步长、24步预测时域,单次求解耗时大概几十毫秒,完全满足微电网秒级甚至分钟级的调度需求。
2.4 采样步长与预测时域的选型经验
MPC性能好坏,很大程度取决于采样步长和预测时域两个参数。采样步长决定控制频率,常见的有5分钟、15分钟、30分钟、1小时。步长越短,对快速波动的响应越好,但预测数据的可用性、求解频率、通信开销都会增加。光伏和负荷预测在分钟级上的可信度并不高,做调度优化时我一般倾向15分钟步长,既能捕捉光伏云层遮挡的快速变化,又不会因为过于频繁的优化而引入高噪声的预测数据。
预测时域N的选择更关键。如果步长是15分钟,N=24对应的就是未来6小时。这个长度通常能覆盖一个完整的电价峰段或谷段,也让光伏日内的主要变化趋势进入优化视野。N再加大到48甚至96,覆盖到全天,求解规模变大,但优化效果提升有限,因为超过6到12小时的预测误差显著增大,远程时段的“最优解”参考价值开始打折扣。实际项目中我会做一组敏感性对比,在预测误差可控的前提下找到效果和时长的平衡点。有一回我把N从24加到48,成本只下降了2.3%,求解耗时却从42毫秒涨到了130毫秒,没有意外的话,24步是性价比最高的起点。
3. Python代码实现与关键细节
3.1 工具选型:cvxpy、casadi还是scipy
Python做MPC可选用的优化工具不少,我实际对比过cvxpy、casadi和scipy.optimize,各有优劣。
cvxpy是面向凸优化的建模工具,DSL风格清晰,适合线性规划、二次规划、锥规划,底层可以接OSQP、ECOS、SCS等多种求解器。微电网调度优化大部分约束是线性的、目标函数是线性的或二次的,cvxpy几乎是天然适配,代码简洁易读,是入门首选。casadi是更偏控制领域的最优控制框架,支持非线性优化、多级动态规划,适合系统模型更复杂、需要做风电机组动力学或详细发电机模型的场景,但学习曲线更陡、建模方式也更底层。scipy.optimize灵活性最高,但要自己处理稀疏矩阵和约束表达,写大规模MPC时容易遇到性能问题。
依赖安装方面,我建议直接用pip安装cvxpy和osqp:
pip install cvxpy osqp numpy matplotlib如果网络源慢,可以换用国内镜像,比如清华源。这里顺便提醒一句,务必确认numpy、scipy和cvxpy的版本兼容,cvxpy依赖的manylinux包有时会和较老版本的numpy冲突,装完最好跑一段简单QP验证一下。
3.2 数据准备与预测序列的工程处理
MPC的输入预测序列直接来自光伏和负荷预测模块。在写优化代码之前,第一步是数据清洗和序列对齐。时间戳必须是连续的,采样间隔一致;如果是外部预测给出的序列,需要重采样到与调度步长相同的时间网格上。异常值处理我习惯用滑动中位数滤波,把突变毛刺平滑掉,否则MPC会根据一个异常点做出剧烈的储能动作。
实际实现中,可以准备一个类来管理预测序列和状态量:
import numpy as np class MicrogridPredictor: def __init__(self, pv_series, load_series, dt): self.pv = pv_series self.load = load_series self.dt = dt def predict(self, t, horizon): end = min(t + horizon, len(self.pv)) pv = np.zeros(horizon) load = np.zeros(horizon) pv[:end - t] = self.pv[t:end] load[:end - t] = self.load[t:end] return pv, load这段代码简单地把历史或预设序列截取成预测窗口。实际工程中,预测模块一般独立部署,这里留出接口即可。需要特别提醒的是:预测序列的长度必须大于等于MPC预测时域N,否则循环后段会出现全零填充,优化器会误以为未来没有光伏出力,给出错误的充电策略。
3.3 储能SOC与功率约束的代码实现
用cvxpy实现MPC的优化模型时,核心是把上一节的数学模型翻译成代码。这里给出一段带充放电效率差异的储能建模示例:
import cvxpy as cp def build_mpc_problem(pv, load, price, C_rated, eta_c, eta_d, soc_init, soc_min, soc_max, p_bat_max, dt): N = len(pv) P_bat_c = cp.Variable(N, nonneg=True) P_bat_d = cp.Variable(N, nonneg=True) P_grid = cp.Variable(N) SOC = cp.Variable(N + 1) constraints = [SOC[0] == soc_init] for k in range(N): constraints += [SOC[k + 1] == SOC[k] - eta_c * dt * P_bat_c[k] / C_rated + P_bat_d[k] * dt / (eta_d * C_rated)] constraints += [soc_min <= SOC[k + 1] <= soc_max] constraints += [P_bat_c[k] <= p_bat_max, P_bat_d[k] <= p_bat_max] constraints += [P_bat_c[k] >= 0, P_bat_d[k] >= 0] constraints += [pv[k] - load[k] + P_grid[k] + P_bat_d[k] - P_bat_c[k] == 0] cost = sum(price[k] * P_grid[k] * dt + 0.001 * (P_bat_c[k] + P_bat_d[k]) for k in range(N)) prob = cp.Problem(cp.Minimize(cost), constraints) return prob, P_bat_c, P_bat_d, P_grid, SOC这个写法比用一个P_bat变量更清晰,每一条约束都能对着原始公式检查。需要注意的是,充放电互斥的问题在这里没有被严格约束,因为目标函数中的0.001成本项不足以阻止同时充放电。如果发现优化结果出现同时充放电,可以把罚系数适当调大,比如0.01到0.05,或者显式加入MIP约束。我在项目里试过给成本的罚项用二次形式,效果也不错,还能保持连续规划、求解速度更快。
3.4 主循环:滚动优化与闭环执行
MPC和普通优化的本质区别在滚动执行,即只应用控制序列的第一步,然后推进到下个时刻。主循环的完整代码如下:
def mpc_loop(data, params, total_steps): hist = {"SOC": [], "P_bat": [], "P_grid": [], "cost": []} soc_real = params["soc_init"] for t in range(total_steps): pv_pred, load_pred = data.predict(t, params["N"]) price = params["price_series"][t: t + params["N"]] prob, P_bat_c, P_bat_d, P_grid, SOC = build_mpc_problem( pv_pred, load_pred, price, params["C_rated"], params["eta_c"], params["eta_d"], soc_real, params["soc_min"], params["soc_max"], params["p_bat_max"], params["dt"]) prob.solve(solver=cp.OSQP, verbose=False) if prob.status == "optimal": p_bat_c_now = P_bat_c.value[0] p_bat_d_now = P_bat_d.value[0] p_grid_now = P_grid.value[0] else: p_bat_c_now = 0.0 p_bat_d_now = 0.0 p_grid_now = (load_pred[0] - pv_pred[0]) soc_real = soc_real - params["eta_c"] * params["dt"] * p_bat_c_now / params["C_rated"] \ + p_bat_d_now * params["dt"] / (params["eta_d"] * params["C_rated"]) hist["SOC"].append(soc_real) hist["P_bat"].append(p_bat_d_now - p_bat_c_now) hist["P_grid"].append(p_grid_now) hist["cost"].append(price[0] * p_grid_now * params["dt"]) return hist这段代码有几个细节值得注意。一是soc_real必须用实际执行后的SOC来更新,而不是用优化模型预测的SOC序列第一段,因为模型参数和实际系统总有偏差,用实测值重置状态是闭环的关键。二是求解失败时要有个兜底策略,我的兜底逻辑很简单:储能不动,电网按差值填补功率平衡。虽然不经济,但能保证系统不崩溃,实际调试价值很高。三是每个调度周期只取P_bat_c.value[0]和P_bat_d.value[0]执行,其余序列全部丢弃。
3.5 cvxpy建模提速与结果输出
MPC主循环最大的性能瓶颈在模型构建。cvxpy每轮循环都要重新构建问题对象、生成数值矩阵,如果预测时域较大或者控制频率很高,这个开销不容忽视。我试过两个优化手段:一是把变量和约束结构固定在循环外,只更新参数数值(cvxpy的Parameter机制);二是用求解器的warm start,让OSQP在上一轮解的基础上继续迭代。
用Parameter机制改写的代码稍微复杂,但收益明显。把SOC初值、预测序列、电价序列都定义成Parameter,循环里只做soc_param.value = soc_real这类赋值,然后调用prob.solve()。我实测在24步预测时域下,重构式写法每轮耗时80毫秒左右,参数化写法可以压到30毫秒以下。如果只是一个演示项目,重构式写法够用;如果要做实时EMS或者长时间仿真,强烈推荐参数化写法,代码虽绕一点但值得。
结果输出方面,可以把每组车道的数据存成numpy数组,最后用matplotlib一次性绘制储能功率、SOC、电网交互功率和成本曲线。分析时我习惯把MPC的结果和规则调度放在同一张图里,放在同一坐标下对比,能直观看到MPC在哪段时间决策更聪明、省了多少成本。
4. 算例验证与效果分析
4.1 测试场景设计与参数配置清单
为了让代码能跑出直观的效果,我设计了一个典型的园区光储微电网算例。光伏装机容量60kW,负荷峰值50kW,储能配置100kWh/50kW,购电价格按一般工商业分时电价,峰段1.2元/kWh、平段0.8元/kWh、谷段0.4元/kWh,售电价格统一按0.35元/kWh。调度步长15分钟,MPC预测时域24步,SOC初始值为50%,允许运行范围30%到90%。
下面这张表可以帮助你快速搭建自己的算例参数:
| 参数 | 数值 | 说明 |
|---|---|---|
| 光伏装机 | 60 kW | 预测峰值出力约60 kW |
| 负荷峰值 | 50 kW | 日最大负荷 |
| 储能容量 | 100 kWh | 可用容量100 kWh |
| 储能功率限幅 | 50 kW | 最大充放电功率 |
| 充/放电效率 | 0.95 / 0.95 | 锂电池典型值 |
| SOC范围 | [0.3, 0.9] | 保护电池寿命 |
| 调度步长 | 15 min | 采样间隔 |
| 预测时域 | 24步 | 未来6小时 |
光伏出力曲线用一个带云层遮挡的波动序列,早上爬坡、中午高发但有一段骤降、傍晚归零;负荷曲线设置早晚两个高峰。这样的场景能让MPC充分展现处理预测波动的能力。
4.2 典型调度结果解读
跑完代码后,最关心的自然是MPC给出的储能行为是否符合预期。
白天光伏充足的时段,MPC会让储能保持充电状态,把多余的光伏能量吸收,SOC缓慢爬升。中午光伏出现骤降时,MPC因为预测到了这个下降趋势,会在骤降前主动降低充电功率甚至转为小功率放电,保证下午负荷高峰时有足够容量可以用。傍晚电价进入峰段、光伏出力接近零,MPC在高价时段释放储能,SOC从高位下降,同时从电网购电的量显著减少。夜间电价进入谷段,系统又从电网补电,把SOC抬回来一部分,为第二天做准备。
与简单的“固定峰谷时段充放”规则对比,MPC最大的优势体现在中午云层遮挡和傍晚峰段的衔接上。规则调度按时间表机械充放,在光伏骤降时没有提前预防,只能依靠电网硬补,实际购电成本明显更高。我这一组算例里,MPC方案比固定规则整体购电成本降低了约12.6%,而且储能动作次数更少、深度更浅,对电池更友好。
4.3 预测时域影响与敏感性分析
预测时域对MPC性能的影响值得专门做一组实验。把N从6逐步增加到48,其他条件不变。N=6时MPC只能看到未来1.5小时,而傍晚峰段往往持续2到3小时,等于“近视眼”看不到完整峰段,优化效果有限。N=24时MPC能看到完整峰谷结构,储能可以在谷段充满、峰段释放,成本下降明显。继续增大到N=48,效果提升不大,因为超过12小时后的预测可信度下降,远端电价信息虽然被纳入了优化,但对应的预测误差也更大,优化结果参考价值打折扣。
调度步长的敏感性测试也值得做。把15分钟改成1小时,问题规模缩小了四倍,但系统对光伏骤降的响应明显迟钝,局部时段出现较大的功率缺额。改成5分钟之后,求解频率升高,对预测数据的噪声也更敏感,偶尔会出现储能动作抖动的现象。综合来看,15分钟对大多数园区微电网是一个均衡的选择。
5. 常见问题与排查技巧实录
5.1 求解报错infeasible怎么办
MPC初学者最常遇到的错误是problem infeasible,即问题不可行。原因大多是约束条件之间互相矛盾。最典型的是SOC初始值偏低,而当前步长的最大充电功率又不足以把SOC在下一个时刻提升到下限以上。比如SOC初始值28%,SOC下限30%,步长0.25小时,最大充电功率50kW,容量100kWh,一小时内最多充满约12.5%,理论上能补到30%以上,但如果充电效率也计入损耗,边界条件一紧就可能导致首步不可行。
排查思路分三步。先检查SOC初值和下限是否自洽,可以把soc_init设成soc_min来排除边界问题。其次检查功率平衡方程是否可能无解,比如负荷预测值过大而光伏和储能容量都不足,此时需要对外购电功率放开上限。最后建议把SOC下限做成软约束,引入一个非负松弛变量乘以惩罚系数加入目标函数,既能保证问题永远有解,也能在优化中自然权衡过下限的代价。
5.2 实际SOC和预测SOC漂移
滚动MPC如果没有用实测SOC重置模型初值,预测SOC和实际SOC会出现明显漂移,原因就是模型效率、容量参数与实际不一致。我曾经做过一次没有重置初值的仿真,到第9个小时预测SOC还有65%,实际SOC只剩41%,后续所有决策全面失真。
解决办法很简单:每个调度周期把soc_real作为下一轮优化模型的初值,而不是用上一轮预测的SOC[1]往下传。这段代码在主循环里我已经体现。更进一步的方案是引入状态估计,用卡尔曼滤波把SOC和充放电效率作为一个整体去估计,能显著提高模型精度,但入门阶段不必急着上,先把状态重置做到位,问题就能解决大半。
5.3 二进制变量与购售电互斥约束
很多微电网调度模型为了符合物理规则,会要求系统不能同时购电和售电,于是引入0-1变量:
z_grid = cp.Variable(N, boolean=True) constraints += [P_grid[k] <= z_grid[k] * P_grid_max] constraints += [P_grid[k] >= -(1 - z_grid[k]) * P_grid_max]加入二进制变量之后,问题变成了混合整数规划,cvxpy默认求解器可能无法处理,需要换成ECOS_BB,或者调用商业求解器。求解时间会从几十毫秒涨到几百毫秒甚至秒级,在实时EMS里这是不能接受的。实际经验是:电价曲线本身对购售电行为有足够的约束作用,只要售电价格低于购电价格,优化器天然不会同时购售电,不需要显式加强制互斥约束。只有当出现“倒挂”电价(售电高于购电)的极端场景才需要考虑这个约束。还是那句话:先让模型跑起来、跑得快,再根据物理需求加复杂度。
5.4 预测输入突变与调度抖动
MPC对预测误差有一定的容错性,但预测序列出现大幅突跳时,优化结果会产生剧烈波动。比如光伏预测在某一时刻从50kW突然跌到5kW,MPC会立刻让储能从充电切到大功率放电,这种控制“抖动”对设备和电池都不好。
我的处理方法是两层。第一层是数据预处理,对预测序列做时间尺度上的平滑,比如中位数滤波、移动平均,消除孤立毛刺。第二层是在目标函数中增加控制增量惩罚,也就是惩罚P_bat(k+1)和P_bat(k)之间的差值,让储能功率变化速率受限:
if k >= 1: constraints += [cp.abs(P_bat_d[k] - P_bat_d[k-1]) <= ramp_rate * p_bat_max]加上爬坡约束后,MPC的动作会变得平滑许多。代价是响应速度稍有下降,但考虑到实际储能逆变器的功率变化率本来就有物理限制,这个约束反而更符合工程实际。这个坑我在第一个项目里没注意,储能变流器频繁报过流,后来加了爬坡约束就稳定了。
写在最后:一点实操体会
如果只让我分享一条经验,那就是MPC在微电网调度中真正值钱的部分不是求解器选得多高级、预测算法多花哨,而是“状态重置”和“约束鲁棒”这两个工程细节。代码层面几十行就能搭出完整的滚动优化框架,但能不能在仿真里跑一整年不崩,取决于你是否认真处理了SOC初值更新、求解失败兜底、功率爬坡约束这些冷门角落。我见过不少人对MPC的数学原理倒背如流,一到实际数据就频频翻车,问题都不在模型本身,而在工程接口上。建议你把文中的主循环代码跑通后,先尝试改一改预测时域和SOC下限,观察它们对成本和电池动作量的影响,再逐步加入爬坡约束、软约束和参数化提速。这些改动虽然不起眼,但正是把纸面算法变成能落地调度的关键一步。后续如果想继续扩展,方向也很多:把固定电价换成现货市场出清价、把储能换成氢储能、甚至在预测误差场景集上做随机MPC,每一步都能让这套基础框架往更复杂的工程场景推进。