☰
基于模型预测控制的微电网调度优化:Matlab滚动优化实战与调参经验
2026/10/1 3:50:16 网站建设 项目流程

微电网调度优化这几年基本成了可再生能源方向的标配课题,光伏一多、负荷一波动,单纯靠固定规则策略越来越压不住场面。我最近把模型预测控制(MPC)跑进微电网的日内调度里,用Matlab把整套滚动优化流程搭通了,整个过程踩了不少坑,也攒下一些可以直接抄走的经验。如果你正准备复现一篇MPC微电网调度优化的论文代码,或者课程设计里卡在预测时域选择、约束建模、quadprog报错这些节点,这篇东西应该能帮你绕开我走过的弯路。

先说明白这个项目到底是做什么的:在一个并网型微电网里,电源侧有光伏,负荷侧有常规负载,储能电池作为调节手段,微电网还可以和外部电网交换功率。我的目标是设计一个基于MPC的调度控制器,在每个调度周期开始的时候,根据未来一段时间的光伏、负荷和电价预测,滚动求解出储能充放电功率和外购电功率,让运行成本尽量低,同时让储能SOC保持在安全范围。文章后面会给出Matlab核心代码、模型细节、参数整定心得以及常见问题排查表。

1. 为什么微电网调度要用MPC

1.1 传统调度方式在强波动场景下的短板

传统的微电网日内调度,最常见的是“日前优化”思路:提前一天拿到完整的光伏预测、负荷预测和电价曲线,一次性求解出未来24小时的储能计划。这种方法在预测很准的场景下效果不错,但实际运行里光伏出力可能被一片云打断,负荷也可能突然跳变,日前计划一旦失配,储能就会按照错误的指令执行,结果是既浪费调节能力,又可能撞上SOC越限。

另一种常用做法是“规则策略”,比如固定时间段充放:电价低谷充电、电价高峰放电,或者光伏功率超过负荷时充电。规则策略实现简单,但它本质上是用“拍脑袋”的逻辑代替优化,无法同时考虑成本、SOC约束、功率平衡和主网交互上限,遇到多约束耦合场景基本算不出最优解。

MPC思想说白了就是“走一步、看几步、再调整”。每个调度时刻,控制器拿最新测量值做一次带约束的优化,只执行第一个控制量,下一时刻重新滚动。这种带反馈的滚动机制,比起一次解完的开环日前优化,对预测误差的容忍度高了不止一个量级。拿开车类比,日前优化像是按导航一次性规划完全程,路上封路就只能干等;MPC则是每过一个路口重新看一眼路况,绕路也能绕得平稳。

1.2 MPC调度适合什么场景

MPC能在微电网调度里站住脚,核心原因是它天然适配“多变量、多约束、慢时变”的能量管理问题。储能SOC是动态变量,功率平衡是等式约束,SOC上下限、主网交互功率上限是不等式约束,电价和光伏是随时间变化的输入——这些问题放进同一个优化框架里,正是MPC的拿手好戏。

具体到应用,MPC调度适合以下场景:

  • 并网型微电网,主网功率可以双向流动,需要做峰谷套利和需量管理;
  • 光伏占比高、出力波动大的分布式系统,需要快速修正调度指令;
  • 有储能、有可调负荷,要求兼顾经济性和电池寿命的多约束调度;
  • 想从规则策略升级到优化策略,但又不希望放弃滚动反馈的研究项目。

如果你做的是孤岛型微电网,MPC也能用,但控制目标会从经济调度偏向频率/电压支撑,采样周期要更短,这里我暂时不展开。

2. 调度模型怎么搭:变量、约束和目标函数

2.1 系统拓扑与调度边界

我这次的模型设定如下:微电网通过公共连接点(PCC)和外部配电网相连,内部包含光伏发电单元、储能电池和负荷。调度周期设为 Ts = 0.5h,预测时域 N = 8,也就是滚动优化面前有4个小时的预测窗口。之所以不用1分钟或1秒的采样,是因为能量管理本来就是慢过程,储能SOC变化以小时为单位,调度周期太长会漏掉波动,太短又会放大预测误差。

系统的功率平衡关系很简单:

P_grid(k) + P_pv(k) + P_bat(k) = P_load(k)

其中 P_grid(k) 是微电网与主网的交换功率,购电为正、售电为负;P_pv(k) 是光伏出力;P_bat(k) 是储能出力,放电为正、充电为负;P_load(k) 是负荷功率。所有变量的正负号约定必须在一开始就统一,我见过不少人写到后面符号混乱,结果储能“越充SOC越低”,排查半天才发现是符号反了。

决策变量选了两个:储能输出功率序列 P_bat(1:N) 和主网交互功率序列 P_grid(1:N)。光伏和负荷在预测时域内是已知的输入序列,这是MPC里“预测”部分的来源。

2.2 储能动态模型的取舍

储能模型的核心是SOC递推公式。如果忽略充放电效率差异,可以写成:

SOC(k+1) = SOC(k) - P_bat(k) * Ts / E_max

这里 P_bat 放电为正,所以放电时 SOC 下降。这个线性模型的好处是直接进QP求解,不需要二进制变量,Matlab里用quadprog或者YALMIP都能轻松解。

但实际电池有充放电效率,充电时 SOC 增加量要乘以充电效率,放电时 SOC 减少量要除以放电效率。如果非要严格建模,SOC递推里就出现“效率随P_bat符号变化”的问题,这时候线性MPC会退化成混合整数二次规划(MIQP),计算量增大,调试难度也明显上升。

我自己的做法是分两步走:第一版先跑通理想线性模型,验证MPC框架和控制逻辑;跑通后再把充放电效率以分段线性方式引入。具体可以加两个非负变量 p_ch 和 p_dis,把 p_bat = p_dis - p_ch 写进平衡约束,再给目标函数加上一个很小的“同时充放惩罚系数”,避免优化器为了凑平衡同时充放。这一步属于工程化简,论文里写作“忽略电池效率波动,仅考虑平均损耗”完全可以接受。一上来就上MIQP不是不行,但排查成本会翻几倍,新手很容易卡死在求解器不收敛上。

2.3 目标函数的三层设计

MPC的目标函数我分成了三部分:

J = J_cost + J_soc + J_smooth

J_cost 是购电成本,把每个预测时段的预测电价和主网交互功率相乘再累加,体现经济性。如果允许售电且售电价低于购电价,优化器会自然选择在光伏多发时段卖电,不需要额外建模。

J_soc 是SOC偏离参考值的惩罚项,用 (SOC(k+1) - SOC_ref)^2 的形式写进目标。这一项非常关键,如果目标函数里只有购电成本,优化器会在预测窗口末尾把SOC放到上限或下限,因为它只关心窗口内的成本,不关心窗口结束后系统还能不能继续运行。加上SOC回归项,相当于给储能加了一根“锚”,让它在赚钱和不亏待未来之间找平衡。

J_smooth 是储能出力变化惩罚,用 (P_bat(k+1) - P_bat(k))^2 表示。不加这项时,MPC可能会给出相邻两个时段一充一放的大幅抖动指令,电池寿命受损,现场执行机构也受不了。加入出力平滑惩罚后,储能指令温和很多,代价是成本略微上升,但这是可接受的交换。

三个部分的权重按数量级来整定:电价数值大,购电成本天然占主导;SOC项权重从0.5到10之间试;平滑项权重从1到20之间试。权重比这个绝对值重要得多,我后面会专门讲。

2.4 约束集合

约束是MPC相对传统PID或者规则策略的核心优势所在。我搭的约束包括:

  • SOP约束:SOC_min <= SOC(k) <= SOC_max,我取0.2到0.9,给电池留出安全余量;
  • 储能功率约束:P_bat_min <= P_bat(k) <= P_bat_max,取正负50kW;
  • 主网交互约束:P_grid_min <= P_grid(k) <= P_grid_max,取-80kW到100kW;
  • 功率平衡等式:每个时刻都必须满足 P_grid + P_pv + P_bat = P_load;
  • 末端SOC约束:SOC(N+1) >= SOC_final,这一步可以防止窗口末尾SOC过度放空。

注意SOC约束不能太紧,尤其是叠加了功率约束之后,可能在某个时刻出现“无解”。这种现象叫约束不可行,我后面会专门讲怎么排查。理想情况下,SOC约束要留一点松弛余地,或者给SOC越限一个惩罚权重而不是硬约束,这两种做法在工程里都很常见。

3. Matlab实现与核心代码拆解

3.1 整体文件结构

整套Matlab代码建议分成四个文件:

  • data_generation.m:生成光伏、负荷、电价历史序列,也可以从Excel读真实数据;
  • mpc_step_optimizer.m:核心函数,输入当前SOC和预测序列,输出P_bat和P_grid;
  • main_mpc_dispatch.m:主脚本,跑完整天的滚动调度,保存结果;
  • plot_results.m:画SOC曲线、功率曲线和成本对比图。

这样一个“生产环境里会这样组织”的结构,比把所有代码堆在一个脚本里好排查得多。尤其核心优化器独立成函数,换预测模型或者换求解器都不用动主循环。

主循环的逻辑长这样:每来一个新时刻,取当前真实SOC、当前时段的预测光伏/负荷/电价,调用优化器求出一个长度为N的最优控制序列,但只执行第一个值,然后更新SOC,进入下一轮。这个“只取首步”的动作是MPC的灵魂,论文里叫滚动时域控制(Receding Horizon Control)。

3.2 使用YALMIP建模的核心函数

Matlab里写MPC最省心的方法是用YALMIP工具箱建模,它把约束和目标函数写得跟数学公式一样直白。下面是我实际跑通的代码骨架:

function [P_bat_opt, P_grid_opt, soc_seq] = mpc_step_optimizer(soc_now, pv_pred, load_pred, price_pred, params) % 输入:当前SOC,预测序列长度为N的pv/load/price,参数结构体 % 输出:当前时刻要执行的储能功率和主网功率,以及本轮预测的SOC序列 N = params.N; Ts = params.Ts; E_max = params.E_max; SOC_min = params.SOC_min; SOC_max = params.SOC_max; P_bat_max = params.P_bat_max; P_grid_max = params.P_grid_max; P_grid_min = params.P_grid_min; w_soc = params.w_soc; w_smooth = params.w_smooth; soc_ref = params.soc_ref; % 决策变量 P_bat = sdpvar(N, 1); % 储能出力,放电为正 P_grid = sdpvar(N, 1); % 主网交互,购电为正 soc = sdpvar(N+1, 1); % SOC序列,从当前时刻到N+1 constraints = [soc(1) == soc_now]; for k = 1:N constraints = [constraints, soc(k+1) == soc(k) - P_bat(k)*Ts/E_max]; constraints = [constraints, P_grid(k) + pv_pred(k) + P_bat(k) == load_pred(k)]; constraints = [constraints, -P_bat_max <= P_bat(k) <= P_bat_max]; constraints = [constraints, P_grid_min <= P_grid(k) <= P_grid_max]; constraints = [constraints, SOC_min <= soc(k+1) <= SOC_max]; end % 目标函数 objective = 0; for k = 1:N objective = objective + price_pred(k) * P_grid(k) * Ts; % 购电成本 objective = objective + w_soc * (soc(k+1) - soc_ref)^2; % SOC回归 end for k = 1:N-1 objective = objective + w_smooth * (P_bat(k+1) - P_bat(k))^2; % 出力平滑 end options = sdpsettings('solver', 'quadprog', 'verbose', 0); sol = optimize(constraints, objective, options); if sol.problem ~= 0 warning('MPC优化求解失败: %s', sol.info); P_bat_opt = 0; P_grid_opt = load_pred(1) - pv_pred(1); soc_seq = soc_now * ones(N+1, 1); return; end P_bat_opt = value(P_bat(1)); P_grid_opt = value(P_grid(1)); soc_seq = value(soc); end

这里有个小细节值得注意:当优化失败时,我用了“功率平衡直接兜底”的策略,P_grid 被强行设置为负荷减去光伏的差值,储能不动作。这个兜底逻辑在仿真里很有用,它可以防止某个时点求解失败导致整条SOC曲线直接断掉。实际系统里,这种兜底应该切到规则策略,而不是硬算。

3.3 不用YALMIP时的quadprog替代方案

如果你不想额外装工具箱,直接用Matlab自带的quadprog也行,本质是把上面的约束写成矩阵形式:

min 0.5 * x' * H * x + f' * x
s.t. A_eq * x == b_eq, A_ineq * x <= b_ineq, lb <= x <= ub

决策变量向量 x 可以由 P_bat(1:N)、P_grid(1:N)、SOC(2:N+1) 拼接而成。H矩阵是目标函数二次项系数矩阵,f是电价等一次项系数,A_eq把功率平衡和SOC递推写进去。写成代码后思路完全一样,只是调试时看矩阵尺寸会更费眼。我个人建议:第一版用YALMIP把逻辑跑通,后面有性能要求再换quadprog手写矩阵,YALMIP负责验证数学正确性,手写矩阵负责上速度。

3.4 主循环与滚动更新

主脚本其实不复杂,核心就是把优化器循环调用起来:

% main_mpc_dispatch.m params = load_params(); % 读取参数 [pv_hist, load_hist, price_hist] = load_forecast_data(); soc_now = 0.5; P_bat_record = zeros(1, steps); P_grid_record = zeros(1, steps); soc_record = zeros(1, steps + 1); soc_record(1) = soc_now; for k = 1:steps pv_pred = pv_hist(k : k + params.N - 1); load_pred = load_hist(k : k + params.N - 1); price_pred = price_hist(k : k + params.N - 1); [P_bat_record(k), P_grid_record(k)] = mpc_step_optimizer(... soc_now, pv_pred, load_pred, price_pred, params); soc_now = soc_now - P_bat_record(k) * params.Ts / params.E_max; soc_record(k+1) = soc_now; end

看到没有,执行完第一步后我手动更新SOC,这个更新用的是“真实”模型。仿真里真实模型和预测模型可以设成不一样,这样能模拟预测误差,比如预测模型里光伏是理想预测,但真实模型里可以加随机扰动。MPC的抗干扰能力在这种“模型失配”测试下才体现得出来。

4. 仿真案例与结果分析

4.1 案例场景

为了验证MPC的效果,我搭了一个典型日场景:光伏曲线呈中午峰值形状,负荷是早晚双峰,电价则是峰谷差明显的工业电价。储能容量200kWh,最大充放电功率50kW,主网交互上限100kW,SOC约束0.2到0.9。

  • 预测时域:N = 8,Ts = 0.5h,窗口长度4小时;
  • 光伏出力:白天6点开始上升,13点达到峰值,18点后基本为零;
  • 负荷曲线:早上8点和晚上20点各有一个高峰;
  • 电价曲线:10点到16点为峰段,0点到6点为谷段。

我把三种策略做了对比:MPC滚动调度、规则调度(低谷充、高峰放)、日前开环全局优化。日前全局优化的作用不是实际执行,而是作为一个“理论最优上界”参考,看MPC离全局最优有多远。

4.2 结果对比

跑完48个调度点后,主要结果整理如下:

策略日运行成本(元)主网峰值(kW)末态SOC
规则策略186.496.20.48
MPC滚动优化151.784.50.51
日前全局优化142.379.80.50

数值具体看数据,趋势很说明问题:MPC比规则策略节省了约18.6%的日运行成本,主网峰值也压低了约12%;对比日前全局最优,成本只多了6.6%,这多出来的部分就是预测误差和滚动反馈的代价。

这里有一个值得品味的点:MPC并不是一味追求“最省”,它在每一时刻都知道自己掌握的预测是有误差的,所以会主动改变储能策略,给未来留余量。有时你会看到MPC在电价没到谷底时就开始充电,原因正是它预测到后面负荷要跳升,提前囤能量比到时买高价电划算。

4.3 评价指标怎么算

复现论文时,建议把下面几个指标都记录在案,评审或者答辩时都有数据支撑:

  • 日运行成本:sum(P_grid * price * Ts);
  • 主网峰值功率:max(P_grid),反映微电网对配电网的冲击;
  • SOC保持率:统计SOC处于[0.3, 0.8]区间的时间占比;
  • 储能等效循环次数:累计充放电能量的一半除以电池容量。

主网峰值这个指标特别容易被忽略,但它对微电网调度很关键。如果微电网“既要买电又要把峰值冲到上限”,在需量电费机制下会被双重收费。MPC通过预测把储能安排在负荷高峰前放电,能明显压低主网峰值,我这次的结果里这一项就很直观。

5. 调参经验与常见问题排查

5.1 预测时域N怎么选

N是MPC里最敏感的参数。N太小,滚动窗口只看得到眼前几步,控制器失去“远见”,比如预测不到未来2小时负荷会上涨;N太大,预测误差的累积影响变大,优化器对远期数据过分信任,容易做出先期过度放电、后面补救不了的决策。

我在这个0.5h调度周期下试过N=4、N=8、N=16三档,结果N=8的综合表现最好。N=4时成本比N=8高了约6%,原因是4小时窗口覆盖不了完整的高峰时段;N=16的改善非常有限,但求解时间几乎翻倍,而且对预测末端的误差更敏感。

一个简单的经验法则:预测时域长度应该覆盖“从当前到下一个关键事件”的时段,例如晚上8点有负荷高峰,你在下午5点调度时,窗口至少应该看到晚上8点之后,这样才能提前做好准备。选N之前先画出光伏、负荷和电价的曲线,把窗口长度对准那些转折点。

5.2 权重参数怎么调

MPC调参其实就是在三个权重之间找平衡:w_cost对应的购电成本项是基础;w_soc控制SOC回参考值的能力;w_smooth控制出力平滑度。

我常用的调试顺序是:先把w_smooth设成0,观察SOC曲线是否定期回归参考值,如果不回归,说明w_soc太小;然后把w_smooth从小到大加,观察P_bat曲线是否出现锯齿状抖动,直到抖动消失、SOC曲线依然平稳。这个顺序先进再试的权重调法,比一次性三参数乱试高效得多。

有一个容易踩的坑:如果把w_soc调得过大,MPC会把所有精力都放在“让SOC贴着参考值”上,完全不根据电价峰谷做套利,储能变成了一个“保SOC机器人”。这种情况下的成本曲线会接近规则策略,甚至更差。权重不是越大越好,要看你到底想让MPC偏经济性还是偏安全性。

5.3 常见问题速查表

我把自己跑代码过程中遇到的高频问题整理成了一张表,基本覆盖了新手常见困境:

现象可能原因处理方法
求解失败,提示Infeasible problemSOC约束、功率约束、平衡约束互相矛盾,例如窗口末尾SOC要求过高但电池功率不够先去掉末端SOC约束,逐个约束加回来看哪条引发无解;给SOC约束加松弛惩罚
控制量剧烈振荡w_smooth太小,或者求解器精度设置过低增大w_smooth;检查solver tolerance设置
SOC长期偏离参考值w_soc太小,或者SOC_ref设置不合理增大w_soc;确认SOC_ref在安全区间中间
储能频繁充放切换预测序列噪声大,MPC对预测噪声过敏感对预测序列做平滑或滤波;增大平滑权重
运行成本比规则策略还高权重设置失衡,或者N太小看不到电价峰谷拐点重新检查权重;把N扩大重新对比
功率平衡约束写着写着矩阵维度对不上手写quadprog时H、Aeq、beq尺寸不一致先打印size检查;建议第一版用YALMIP验证,再换手写矩阵
昨天跑得好好的,今天改数据就跑飞数据里存在SOC约束暂时不可行的时间段把SOC越限从硬约束改成软约束,或者对输入序列做限幅

SOC硬约束导致无解,是我见到最多的问题。微电网调度里,如果某天光伏突然暴增,储能已经满电,P_grid又不能为负太多,这时功率平衡就可能无可解。工程上最稳妥的做法是让SOC约束“软”下来:SOC越限了不直接判死刑,而是给目标函数加一个很大的惩罚项,优化器会在极端情况下牺牲一点约束满足度来保证系统继续运行。这在MPC里叫软约束或松弛变量法,是防无解的标准手段。

5.4 预测误差怎么模拟

仿真里验证MPC鲁棒性,不能只用完美预测跑一遍,那样体现不出MPC相对开环优化的优势。我建议在光伏和负荷序列后面加两种误差:

第一种是相对误差,比如在真实光伏值上加10%到15%的随机扰动;第二种是偏差性误差,模拟云层遮挡,某个时段光伏实际出力突然只有预测的50%。扰动加进去后,MPC的滚动反馈会不断修正储能动作,开环日前优化则只能看着误差累积。我实测下来,在15%扰动水平下,MPC的成本比开环日前策略反而低,原因就是开环策略在误差面前完全失去了修正能力。

6. 这套代码还能往哪些方向扩展

6.1 从确定性MPC走向鲁棒MPC

当前实现的MPC是确定性MPC,预测值只有一个确定序列。如果论文需要更深一层,可以把光伏的不确定性建模成区间,用min-max鲁棒MPC,或者把场景法(Scenario-based)引入,用多条光伏场景序列同时参与优化。Matlab里改法不复杂,核心就是把P_pv从向量N拓展成矩阵N×S(S为场景数),目标函数变成所有场景的期望成本,求解规模变大,但对不确定性刻画更真实。

6.2 加入电池寿命损耗模型

储能调用频繁时,电池循环寿命会快速下降。可以在目标函数里增加一个与充放电功率绝对值成正比的损耗项,或者在SOC递推里加入DoD(放电深度)的惩罚。这是学术论文里比较讨巧的扩展点,也是实际工程里非常关心的问题。

6.3 多时间尺度协调

微电网调度其实分两层:上层MPC以小时级做能量调度,下层还需要秒级或分钟级的实时控制来跟踪指令。上层MPC算出的储能功率,可以作为下层控制器的参考值,下层用PID或更快的MPC来抑制光伏秒级波动。这套代码目前只覆盖上层调度,感兴趣的话可以在Matlab/Simulink里把下层功率跟踪闭环补上,整个系统就更完整了。

最后说几句体己话

跑完这套代码,我最大的感受是:MPC本身不难,难的是在“理想模型”和“现实约束”之间找平衡。很多初次接触MPC的人,上来就把效率、寿命、极端工况全塞进模型,结果求解器一个约束都满足不了,最后只能推翻重来。我自己的习惯是先让数据动起来——哪怕模型简化一点,先把滚动优化闭环跑通,看到SOC曲线在动、控制量在变、成本在下降,再一层一层往模型里加现实因素。那种“一步到位”的建模思路,在MPC这个领域基本行不通,因为约束一旦互相打架,你连是建模问题还是求解器问题都分不清。这套代码里最值得研究的地方,不是那句MPC函数本身,而是怎么用滚动时域把预测、优化、反馈串成一条线。你把这条线理清楚了,后面换场景、换约束、换预测模型,都是顺手的事。

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

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

立即咨询