
1. 为什么选择MPC做微电网调度——从一个调度的痛点说起前段时间刚把一套基于模型预测控制MPC的微电网调度优化程序用Matlab完整跑通光伏、风电、储能、柴油机、电网交互全部写进一个可调的Demo里。从预测模型搭建到滚动优化实现整个过程比我预想中要复杂不少但也正是因为这些细节我才真正理解MPC在微电网调度里到底解决的是什么问题。这篇文章会把我的实现思路、Matlab代码骨架、关键参数和踩过的坑全部整理出来给准备做相关研究或者工程落地的同学一份可以直接参考的方案。1.1 微电网调度到底在解决什么问题微电网一般由分布式发电源光伏、风电、储能系统BESS、可控发电机组如柴油发电机和本地负荷组成既可以并网运行也可以离网孤岛运行。调度的核心任务很简单在满足负荷供电的前提下合理分配各个单元的输出功率让系统运行成本最低、储能寿命损耗可控、碳排放尽量少、并网功率不越限。听起来像是一个典型的经济调度问题。但如果只是做一个“日前计划”把一天24小时分成96个时段提前算出每一刻柴油机出多少、储能充多少、电网交互多少实际跑起来就会发现根本不好用。光伏功率受云层影响可能十几分钟就从100千瓦掉到30千瓦负荷也总有突发波动。前一天制定的计划第二天早上就已经严重偏离现实储能可能会过放柴油机爬坡跟不上甚至可能因为功率越限被电网考核。微电网调度真正难的地方是在“预测一定不准”的前提下仍然能保证系统安全和运行经济性。也就是说调度算法必须能滚动更新、不断用新测量数据修正未来决策。MPC天然适合这件事。1.2 为什么传统开环优化不够用传统优化调度通常走“一次建模、一次求解、结果下发”的路线。以日前经济调度为例已知第二天各时段的预测值后构造一个大规模线性规划或混合整数规划求出一整天的开机组合和出力计划。这种方式的问题在于开环计划下发后基本不根据实时状态修正一旦扰动到来只能依赖人工干预或者底层控制器被动响应。而微电网里有几个环节对这种开环方式非常不友好。储能系统有SOC上下限约束过充过放会直接影响电池寿命柴油机有爬坡约束不是你想让它一秒从零加到满负荷就能做到并网功率上下限也会被调度协议卡住。这些本质上都是“动态约束”如果只看未来一个时段很难判断当前该不该多充一点电、多预存一些能量。开环优化相当于一个人在夜晚看了一眼第二天天气预报就决定全天穿多少衣服结果白天降温也只能硬扛。MPC则更像每隔几个小时看一眼实时天气和温度再重新规划接下来几小时的穿搭既考虑了未来趋势又结合了当前实测。1.3 MPC的核心思想与可控优势MPC在每一个控制周期内执行三步根据当前状态量和未来扰动预测计算未来一段有限时域内的系统状态轨迹在满足各种物理约束的前提下求解一个带目标函数的有限时域优化问题只把求解结果中的第一步下发执行等到下一个控制周期到来时用新的实测状态重新滚动求解。这个“滚动优化反馈校正”的机制让MPC同时具备了最优控制和闭环反馈的优点。在微电网调度里我们可以把光伏、风电、负荷预测当作已知的外部扰动量把SOC当作状态变量把柴油机出力、储能充放电功率、电网交互功率当作控制变量。相比传统开环调度MPC能够在每个周期都引入最新的SOC测量值及时纠正模型失配和预测误差造成的偏差。通过有限时域优化提前看到未来几小时的负荷和新能源变化为储能预留充放电决策空间。把储能SOC约束、机组爬坡约束、并网功率约束统一放进优化问题中避免“先决策后校验”带来的冲突。对多目标进行灵活加权比如同时考虑购电成本、柴油油耗、储能损耗平抑。当然MPC也不是没有代价。它依赖模型精度预测时域长度和求解速度直接相关而且离散时域下引入储能充放互斥的二进制变量后问题会变成混合整数规划计算量比常规线性规划高不少。这部分在Matlab实现里体现得非常明显尤其是对时域长度和求解器的选择稍不注意就卡住。2. 系统建模与MPC框架搭建MPC效果好不好模型占了一半。调度建模不追求组分内部的电磁暂态而是关注功率平衡、能量状态转移、成本特性和运行约束。这一节我把常见的微电网组件模型和MPC状态空间框架拆开讲清楚方便大家直接对照自己的算例改。2.1 微电网组件数学模型以一个典型并网型微电网为例包含光伏阵列、风力机组、柴油发电机、锂电池储能、本地负荷和上级电网联络线。所有功率单位设为千瓦时间步长设为15分钟。光伏和风电是“不可控扰动源”。在MPC的预测时域内我们直接把它们的预测功率序列当作已知的输入信息。也就是说光伏和风电不提供优化自由度它们出力多少由环境决定算法只能在当前预测假设下进行资源分配。这个处理方式简单有效后续如果想加入弃光弃风控制也可以把实际并网功率作为控制变量再把上限设为可发功率。储能系统的核心状态是荷电状态SOC。离散状态方程通常写成SOC(k1) SOC(k) - η_b * P_b(k) * Δt / E_b其中P_b(k)是储能与系统的交换功率约定放电为正、充电为负η_b是综合充放效率E_b是储能容量。如果希望细致描述充放电效率不对称可以拆成P_ch和P_dis两个非负变量配合充放互斥二进制变量。代码实现部分我会给出两种做法不过第一次调试建议先用单变量模型等框架跑通再升级成含互斥约束的版本。柴油发电机是可控单元成本一般写成关于出力的线性或二次函数C_dg a * P_dg^2 b * P_dg c实际调度里爬坡约束非常关键相邻两个时段的犯出力变化必须限制在允许范围内否则机组无法响应。这个约束在MPC里容易处理因为预测时域内的每个相邻控制步都可以建立差值约束。电网联络线可以做成功率可正可负的交互变量正表示从上级电网购电负表示向电网售电。为了方便计费更推荐拆成P_buy和P_sell两个非负变量。购电成本按分时电价计算售电收益按上网电价计算两者价格不同这也更贴近实际。2.2 MPC预测模型与状态空间描述从控制角度看系统的状态量可以选为SOC因为这是唯一的动态变量。如果把柴油机爬坡过程也建模成一阶惯性环节那还可以把柴油机当前出力也作为状态但大多数调度级MPC里简化成一阶代数关系只增加爬坡约束。预测模型在离散时间下可以写为x(k1) f(x(k), u(k), d(k))其中xSOCu[P_dg, P_buy, P_sell, P_b]的组合d[P_load, P_pv, P_wt]。在预测时域Np内已知当前测量值SOC(k)以及未来Np步的负荷、光伏和风电预测序列就可以递推得到未来Np1步的SOC轨迹。MPC并不要求所有控制变量在未来每一步都独立变化通常只让前Nc步有变化自由度后面保持恒定也就是控制时域。设置控制时域比预测时域短可以降低问题维度减少计算量。在搭建目标函数之前还需要明确等式约束每个时段的功率平衡关系为P_pv P_wt P_dg P_dis P_buy P_load P_ch P_sell这样写可以避免正负功率符号混乱所有变量都取非负便于线性建模。2.3 目标函数与约束设计MPC目标函数通常采用加权求和的形式。在我的案例里主要包含这几项柴油发电成本可以线性化也可以在YALMIP中直接描述二次型。电网购电成本和售电收益不同时段价格不同用P_buy和P_sell分别写。储能充放电动作惩罚减小频繁切换保护电池。SOC偏差惩罚让SOC尽量回到参考值附近防止优化结果把电池电量“用尽”或“充满”。柴油机出力变化量惩罚让出力曲线更平缓避免机组频繁调整。约束条件必须覆盖柴油机出力上下限和爬坡约束。储能充放电功率上下限。SOC动态方程和上下限约束。电网交互功率上限。系统功率平衡方程。在新能源渗透率过高的时段功率平衡方程可能会导致无解比如光伏大发且储能已满、电网还限制外送。遇到这种情况可以在功率平衡中引入松弛变量并给松弛变量一个很大的惩罚系数保证MPC始终有可行解而不是直接报错。2.4 常用参数的经验取值我从经验出发列一组适合做研究和演示的初始参数方便第一次跑通代码。时域步长取15分钟预测时域Np建议8到12步也就是未来2到3小时控制时域Nc取4到6步。太短容易近视看不到负荷峰谷的移峰需求太长会显著增加求解器压力。储能SOC上限建议设0.2到0.9之间不一定要用0%和100%留一点安全余量。柴油机爬坡限幅取额定功率30%左右。如果系统容量较大可以把所有功率单位改成标幺值避免数值差异过大导致求解精度下降。权重方面目标函数各项数量级差异很大不能直接按“重要性”乱写。建议先把变量归一化到相近量级比如成本单位都用元每小时功率单位都用千瓦SOC用百分比。这样权重调整才有意义。3. Matlab代码实现步骤与关键代码解析这一节直接上干货。我使用的是MatlabYALMIP框架求解器可以用常见商业求解器也可以换成开源求解器。YALMIP的好处是建模速度快约束和目标函数写起来接近数学公式适合快速验证MPC算法。下面按照程序架构、核心主循环、约束构建和结果分析四块逐个讲。3.1 程序整体架构与数据准备我的目录结构一般这样组织data_generation.m生成负荷、光伏、风电预测序列和真实序列。mpc_parameters.m集中定义所有系统参数。build_mpc_problem.m创建YALMIP变量和约束返回预测时域内的控制器结构。main_mpc_loop.m主循环实现滚动优化。plot_results.m绘制调度曲线。数据准备很简单为了模拟“预测不准”先有一条真实功率曲线然后在每个采样点加上一定标准差的高斯噪声作为当前时刻控制器能看到的预测曲线。实际研究中可以用历史实测数据或数值天气预报但Demo里用噪声模拟可以快速验证算法鲁棒性。典型算例参数如下表参数名称数值系统时步间隔0.25小时调度周期总步数96光伏额定功率100千瓦风电额定功率50千瓦峰值负荷120千瓦储能容量100千瓦时储能最大充放电功率40千瓦柴油机额定功率80千瓦并网交互功率上限60千瓦3.2 MPC主循环与变量定义核心逻辑在一个循环里完成。每次迭代先拿到当前SOC实测值生成未来Np步的预测序列再建立优化问题并求解最后只执行第一步结果。主循环代码骨架如下% main_mpc_loop.m 核心循环 T 96; Np 8; Nc 4; soc_record zeros(1, T1); P_dg_record zeros(1, T); P_ch_record zeros(1, T); P_dis_record zeros(1, T); P_buy_record zeros(1, T); P_sell_record zeros(1, T); % 初始SOC soc_record(1) soc_init; for k 1:T % 获取当前真实SOC(模拟传感器测量) soc_now soc_record(k); % 构造预测序列越过末尾时用最后一个可用值填充 idx_range k:min(kNp-1, T); load_pred load_real(idx_range); load_pred(end1:Np) load_real(end); pv_pred pv_real(idx_range); pv_pred(end1:Np) pv_real(end); wt_pred wt_real(idx_range); wt_pred(end1:Np) wt_real(end); % 调用YALMIP建模求解 [u_dg_pre, u_buy_pre, u_sell_pre, u_ch_pre, u_dis_pre, soc_traj, solve_status] ... solve_mpc_step(soc_now, load_pred, pv_pred, wt_pred, Np, Nc); % 只执行第一步 P_dg_record(k) u_dg_pre(1); P_buy_record(k) u_buy_pre(1); P_sell_record(k) u_sell_pre(1); P_ch_record(k) u_ch_pre(1); P_dis_record(k) u_dis_pre(1); % 用真实模型更新SOC引入模拟误差 real_load load_real(k); real_pv pv_real(k); real_wt wt_real(k); soc_record(k1) soc_record(k) ... - (P_dis_record(k)/eta_dis - eta_ch*P_ch_record(k)) * dt / E_b; end特别注意MPC内部的预测模型和“真实对象”要保持不完全一致这样才能看出反馈校正价值。比如在真实对象中充放电效率分别为0.95和0.9而预测模型里用同一个综合效率0.92。等到整个程序跑完再看MPC依然能靠SOC反馈把轨迹拉回来这也是最小实现里最有价值的一点。3.3 YALMIP下的约束构建与求解调用solve_mpc_step函数里的核心建模代码可以写成如下形式function [u_dg_pre, u_buy_pre, u_sell_pre, u_ch_pre, u_dis_pre, soc_traj, status] ... solve_mpc_step(soc_now, load_pred, pv_pred, wt_pred, Np, Nc) P_dg sdpvar(1, Np); P_buy sdpvar(1, Np); P_sell sdpvar(1, Np); P_ch sdpvar(1, Np); P_dis sdpvar(1, Np); SOC sdpvar(1, Np1); constraints []; SOC(1) soc_now; for t 1:Np % 功率平衡 constraints [constraints, ... pv_pred(t) wt_pred(t) P_dg(t) P_buy(t) P_dis(t) ... load_pred(t) P_ch(t) P_sell(t)]; % SOC动态 constraints [constraints, ... SOC(t1) SOC(t) - (P_dis(t)/eta_dis - eta_ch*P_ch(t)) * dt / E_b]; % 柴油机约束 constraints [constraints, P_dg_min P_dg(t) P_dg_max]; if t 1 constraints [constraints, ... -dP_dg_max P_dg(1) - P_dg_prev_meas dP_dg_max]; else constraints [constraints, ... -dP_dg_max P_dg(t) - P_dg(t-1) dP_dg_max]; end % 储能功率与SOC限制 constraints [constraints, 0 P_ch(t) P_ch_max]; constraints [constraints, 0 P_dis(t) P_dis_max]; constraints [constraints, SOC_min SOC(t1) SOC_max]; % 电网交互 constraints [constraints, 0 P_buy(t) P_grid_max]; constraints [constraints, 0 P_sell(t) P_grid_max]; end % 目标函数 objective 0; for t 1:Np objective objective dg_cost_coef * P_dg(t); objective objective buy_price(t) * P_buy(t) - sell_price(t) * P_sell(t); objective objective w_soc * (SOC(t1) - 0.5)^2; objective objective w_fluct * (P_dg(t) - P_dg_prev)^2; end options sdpsettings(solver, gurobi, verbose, 0); optimize(constraints, objective, options); u_dg_pre value(P_dg); u_buy_pre value(P_buy); u_sell_pre value(P_sell); u_ch_pre value(P_ch); u_dis_pre value(P_dis); soc_traj value(SOC); status optimize(constraints, objective, options); end上面这段代码里P_dg_prev在每一轮循环前需要更新成上一次的实际出力。objective里的平抑项(P_dg(t)-P_dg_prev)^2写成对所有t都减去同一个上一步值并不完全严谨更规范的做法是对相邻控制步求差。不过作为演示代码第一轮可以把离散化影响降到最低。真正做论文或工程我会单独写一个差分矩阵来施加爬坡和平抑约束。如果使用综效η且不区分充放更简单的方法是单变量P_bSOC方程写为SOC(t1) SOC(t) - eta_b * P_b(t) * dt / E_b此时P_b正表示放电负表示充电目标函数里面加一项惩罚w_b * P_b^2来抑制不合理的较大电流。这个模型没有混合整数变量求解速度能提升一个量级特别适合在算法验证初期使用。3.4 实测结果怎么看仿真结束后建议画出四张图功率调度曲线、储能SOC曲线、电网交互功率曲线、柴油机出力与爬坡对比。拿我的这个算例来说MPC会在负荷低谷和光伏大发时提前给储能充电在晚高峰时段放电从而明显降低电网购电峰值。和纯规则策略对比SOC很少触到边界。因为MPC在预测时域内“看得到”未来会出现负荷尖峰会提前几天把储能充电。如果单靠规则控制一般要等到SOC过低或者超限才被动响应。我这里跑出来的典型结果是在不考虑分时电价的情况下相比传统的“光伏优先、缺额购电”策略总运行成本大约降低8%到15%。如果叠加峰谷电价收益会更明显。但一定要强调这个结果依赖具体算例核心价值是算法框架和验证方法而不是那个百分比。4. 实操过程中遇到的常见问题与解决技巧MPC看起来是论文里两三页公式实际代码运行时问题一个接一个。这一节把我实际调试中遇到的高频问题整理成速查表对应排查思路和解决方案。4.1 求解速度慢靠时域和模型简化解决第一次跑完整96步预测时域设为20步控制时域设为10步代码跑起来明显卡顿每一步都要等一两秒整个流程几分钟起步。如果还加入了储能充放互斥的二进制变量那每一步都是混合整数规划速度会更慢。解决办法分几层先是缩短预测时域。调度MPC不是越长越好微电网的预测精度随时间快速下降2到3小时已经足够。设定Np8、Nc4求解时间能减一半以上。其次是去掉不必要的整数变量用单一储能功率变量代替充放电分离变量速度提升特别明显。再次是把系数归一化到1附近很多求解器的小数收敛难度主要来自数值尺度差异比如SOC范围0到1功率却是0到1000代价矩阵接近病态。如果项目确实需要充放互斥约束建议用二进制变量时只控制充放状态而不是给每个时段都加两个变量。还可以用逻辑约束写成P_ch(t) P_ch_max * (1 - u_dis(t))减少约束数量。4.2 SOC越界或持续漂移缺少末端校正调试MPC时常发现SOC经常贴着上限或下限跑。原因很简单目标函数如果没有给SOC设置“吸引力”优化器会倾向于把储能用到极限因为这样可以节省成本。但在实际系统中SOC长期保持在边界附近非常危险。有效的做法是在目标函数中加入SOC参考值偏移惩罚项。我把惩罚项设计为w_soc * (SOC(t1) - 0.5)^2系数w_soc一开始取很小比如0.1然后观察SOC轨线。如果SOC还是容易跑偏逐步增大到1到5之间。另一种更“硬”的方式是给末端SOC加约束让预测时域终点处的SOC等于某个期望值或者落在上下限内。不过强约束可能造成无解这时可以引入松弛变量并在目标函数里给松弛量一个大惩罚保证问题始终有界可解。另一个我踩过的坑是每轮更新的SOC测量值没有用作下一次优化初值。MPC反馈机制的前提就是状态反馈如果主循环里SOC不从真实模型更新滚动修正就失效了。调试时可以先打印每一轮的SOC(1)确认它确实等于上一轮执行后的真实值。4.3 预测数据不准导致决策抖动系统里负荷和光伏的预测值都有噪声如果直接使用原始预测MPC给出的控制序列往往跳变明显。尤其是光伏预测在云层快速变化时相邻两轮MPC解出的第一步火力出力可能从30千瓦跳到60千瓦再跳回来。这个现象说明MPC的反馈回路工作正常但也暴露了缺少平抑项。解决方式有三类目标函数中加入控制增量惩罚或者出力变化惩罚。对预测序列做平滑滤波例如采用一次指数平滑。在功率平衡约束中为新能源波动预留备用容量。单纯增加惩罚权重容易让系统响应变慢我在实际调试中更常用“预留备用短期平均”的组合。也就是把光伏预测值打八折作为可调用功率剩余部分作为实时扰动备用让柴油机和储能不至于被一个突变预测牵着走。4.4 混合整数规划无解排查技巧当储能充放互斥和二进制变量加入后容易碰到优化问题不可行。先不要急着改参数而是做以下三步排查关闭二进制约束把模型退化为线性规划确认功率平衡能成立。检查SOC上下限和功率上下限的数值是否与容量一致尤其是P_ch_max * eta * dt / E_b对应的每时段SOC变化量是否远大于SOC范围如果是说明功率上限或时步设置不合理。启用YALMIP的调试模式输出不可行约束集定位具体是哪一个时段、哪一条约束出问题。每周遇到不可行问题九成是参数单位混乱导致的比如时间单位用了小时但容量单位用了千瓦时功率上限却写成兆瓦。我自己的办法是在参数文件里统一所有单位并在每个参数后写注释例如dt 0.25; % 单位:小时。这样能避免最蠢的错误。4.5 权重调节与标幺化建议权重系数是MPC调参的最后一步。常见误区是谁重要就把权重设成100结果导致目标函数量级失衡优化器只去最小化大数项其他目标形同虚设。更稳妥的做法是先跑一次开环统计每部分成本大致量级。比如购电费用一天可能是几百元柴油费用可能是几十元SOC惩罚项可能是0到1之间的小数。那么把SOC偏差乘以100或者把成本除以一个基准值让所有目标项落在0到10之间。然后按照目标优先级从小到大调。先把调度可行和SOC平稳放在第一位其次再优化成本和功率平抑。5. 从单微网MPC到更大范围的扩展与个人体会这一节聊点更实际的方向也算是在这个Demo项目基础上的延伸思考。如果读者只是想先把课题交差本章前几节的代码框架已经够用但如果想继续深入下面几条路是我亲测比较可行的。5.1 多微网协调与分布式MPC单个微网跑MPC不难难在多个微网互联。多微网之间可以通过公共联络线交换功率但每个微网都有各自的决策权和隐私保护需求不能简单汇总成一个集中式模型。这时可以用分布式MPC框架上层做一致性协调下层每个微网独立求解本地MPC。Matlab实现时可以借助每个微网单独跑YALMIP问题再通过迭代交换联络线功率期望值实现协调。难点是协调信息的更新步长和收敛判据我建议先用两个微网连一起的案例验证算法再扩展到更多节点否则调试会很痛苦。5.2 数据驱动预测与MPC的结合单纯的MPC依赖模型而数据驱动可以弥补预测误差。比较简单的做法是把负荷和光伏预测换成LSTM或者神经网络预测结果接入到现有的MPC主循环里。这时注意预测结果往往带有偏置最好在MPC前增加一个在线误差修正环节。更进阶的是“数据驱动MPC”直接用历史数据构造系统动态模型不依赖物理机理。这种方式适合电网模型不公开的场景。不过在我实际运行中数据驱动模型在样本少的时候很容易过拟合建议至少先积累一个完整季节的运行数据再尝试。5.3 我对这套代码的实操反思最后分享一点个人经验。做MPC调度研究最容易陷入的陷阱是“程序能跑就不深究”。我最早跑通时也觉得很顺利后来慢慢改参数才发现同一个代码框架换一套参数可能结果完全不同。因此我在所有MPC项目中都坚持这么几件事先跑确定性MPC再跑预测误差场景。不要一上来就把预测噪声加进去否则不知道是算法问题还是数据问题。每轮都把“预测模型”和“真实对象模型”分开写。这样后续扩展鲁棒MPC、随机MPC就很简单只需要改对象模型这一层。记录每一轮的执行时间和求解状态。如果求解器返回非最优解也要记录下来避免把错误结果当作正常调度输出。调参时一次只改一个变量。先固定Np和Nc再调权重最后才动约束边界否则两个因素搅在一起根本无法定位。做Matlab代码实现这件事真正花时间的往往不是MPC原理本身而是各种工程细节数值尺度、边界填充、求解器选择、结果可视化。把这些细节处理好整套算法的可复现性和说服力会提升一大截。希望这份实现笔记能帮你在自己的微电网调度项目里少走几个弯路。