简介:这是一套面向微电网运行场景的Matlab仿真代码,聚焦光伏、风机、柴油发电机与储能电池的协同调度,在满足负荷需求及电压/频率、设备容量等安全约束的同时,兼顾购电成本、燃料消耗和储能损耗等经济目标。代码将负荷与新能源出力预测、多目标优化建模、功率分配指令生成及结果可视化串联成完整流程,主程序main.m封装调度核心逻辑,用户可替换本地数据、调整权重系数或接入自定义优化器,适用于高校教学演示、科研方案验证及微电网初期的技术可行性分析。资源包共5个文件,压缩包大小仅6KB,以Matlab主程序m文件、Python辅助脚本及环境依赖配置txt/inscode等类型为主,结构精简,便于快速迁移或二次开发。目前已有25人学习下载,适合对微电网经济调度、Matlab建模或储能协同策略感兴趣的电力专业师生及工程师快速上手。 搞微电网的人迟早会碰上一个绕不开的问题:光伏出力飘忽不定,负荷一天三变,储能、柴油机、配电网接口叠在一起,到底每台设备在每个时刻出多少功率,才能既保供电又省钱?这就是微电网功率协同调度与经济运行问题,也是我这套MATLAB仿真代码解决的核心。具体来说,代码以24小时为调度周期,以购电、燃料、储能损耗与切负荷惩罚之和最小为目标,在功率平衡、设备出力上下限、储能荷电状态等约束下,滚动给出各分布式电源、储能和电网交互功率的调度曲线。适合正在做微电网能量管理、储能优化调度,或者刚入门想搞懂经济调度建模全流程的同学直接当模板改。接下来我从模型、求解、代码、排错四个层面把整套方案完整拆开。
1. 先把问题本质看清楚:协同调度到底在调度什么
1.1 微电网为什么必须做多设备联合决策
微电网的核心特征是小——电源容量小、系统惯量小、调节裕度小。正因为小,任何一台设备的出力变化都会直接影响系统能不能平衡。午间负荷高峰、夜间光伏为零、凌晨风电可能大发,如果靠人工经验或者各设备各自为政,很容易出现储能过放、柴油机长期低效运行、向配电网取电功率产生尖峰等问题。
功率协同调度本质上是一个多设备、多时间断面的联合决策问题:在每个调度时段,同时决定光伏、风电、柴油机、储能、电网交互口各自承担多少功率,并保证这些决策在整段时间序列上协调一致。比如储能白天充、晚上放,这种把能量进行“时间搬移”的能力,只有放在多时段整体优化模型里才能体现出来,拆成单时段独立求解是实现不了的。这也是“协同”二字最容易被新手忽略的地方。
还有一个容易踩的坑:很多人把微电网调度理解成一个纯功率分配问题,每台设备本地控制、互不通信,结果充其量是“各扫门前雪”,谈不上最优。真正的协同调度一定要有一个统一的上层优化器,拿到全系统的预测数据和设备状态,一次性算出全局最优计划,再下发到各设备执行。
1.2 经济运行问题的数学本质
从数学上看,这是一个典型的有约束优化问题,目标函数是所有设备的运行成本之和,决策变量是各设备在每个时段的出力,约束条件是功率平衡、容量限制、爬坡限制、荷电状态动态方程等。
我直接给一个最常用的目标函数形式,各位对照自己的项目看:
总成本 = 购电成本 + 柴油机燃料成本 + 储能运行损耗成本 + 切负荷惩罚成本
这里多说一句切负荷惩罚。实际工程中,极端低温或极端高温天气下,负荷可能超出分布式电源加储能加电网交互口的最大供给能力。如果模型里只写硬性功率平衡,优化问题直接报不可行;加入切负荷变量并配一个足够大的惩罚系数,优化永远有解,同时惩罚项会把切负荷压到最末端。这是我做工程化模型后最推荐的一个建模习惯,纯学术模型往往忽略这一点,一到真实数据上叠加就暴露问题。
至于约束条件,核心的就五类:功率平衡约束、各设备出力上下限约束、柴油机爬坡约束、储能荷电状态递推与上下限约束、电网交互功率上限约束。把这五类写清楚,一个可求解的日前调度模型就基本成型了。
2. 核心模型构建:从物理设备到数学约束
2.1 分布式电源建模的两个关键细节
光伏和风电在日前调度中通常作为不可调度的负负荷,直接采用预测值。这里有一个常见的坑:把光伏出力直接当固定值放进平衡方程,会在预测误差大的时候导致实际执行阶段功率失衡。更稳妥的做法是引入弃光/弃风变量,让极端天气下可以主动削减可再生能源出力。虽然目标函数里会增加一点成本,但模型灵活性和鲁棒性都提升一截,尤其在仿真里验证极端场景时特别好用。
柴油机建模要注意两点。第一,燃料成本通常用二次函数表示,但二次项会让目标函数非线性,用MILP求解时要做分段线性化。实际工程里我会直接把成本曲线做三段线性逼近,误差完全在可接受范围内。第二,爬坡约束不能漏,否则求解器给出的调度计划在实际执行时根本跟不上——上一小时还是20 kW,下一小时直接跳到满功率,物理上做不到。
2.2 储能系统的核心约束与“防作弊”机制
储能建模最重要的是SOC递推方程:
SOC(t+1) = SOC(t) − Pb_dis(t)·η_dis·Δt/E + Pb_ch(t)·Δt/(η_ch·E)
其中Pb_dis是放电功率,Pb_ch是充电功率,E是储能额定容量。光有这个递推还不够,因为同一个时刻电池既能充又能放,优化器可能玩出“既充电又放电”的骚操作来刷成本、糊弄约束。解决办法是加一个二进制变量,让充放电状态互斥:
Pb_ch(t) ≤ z(t)·Pb_ch_max
Pb_dis(t) ≤ (1−z(t))·Pb_dis_max
z(t)是0/1变量,等于1表示该时段处于充电状态。这个约束是储能建模里最经典的“防作弊”机制,代码里少了它,结果一出来SOC曲线经常出现诡异的锯齿。
另外SOC的初值和终值约束也很重要。想做单日调度,一般会要求SOC(24)回到SOC(1),保证储能日循环完整,否则优化器会把电池能量在一天内全部用光,第二天从零接着算,这在实际运行中根本不可持续。
2.3 电网交互口和分时电价的影响
与电网的交互功率一般设置一个上限,避免微电网从配电网取电的功率超出变压器容量。分时电价场景下,电网交互口的建模直接决定了经济调度的收益空间——谷时多买、峰时少买甚至反送,完全是电价信号驱动的。这里要注意:如果允许向电网反送电,售电价通常低于购电价,模型里要区分两个方向的电价,不能简单用一个价格。实际算例里,峰谷价差越大,储能套利空间就越明显,调度结果里储能“谷充峰放”的特征也会越清晰。
3. 求解方案选型:为什么我推荐YALMIP+MILP
3.1 别再一上来就写粒子群了
很多同学一看“优化”两个字,第一反应就是粒子群算法。我的实际经验是:在微电网经济调度这种线性约束为主的场景里,YALMIP建模加Gurobi、CPLEX这类商业求解器,比手写启发式算法快一个量级,而且全局最优性有保证。粒子群不是不能用,而是它更适合目标函数非线性、约束不光滑、常规求解器难以处理的场合;一旦问题规模变大,粒子群的收敛性和参数调优又会变成新的麻烦。
如果手头没有商业求解器,MATLAB自带的intlinprog完全够用。对24时段、5台左右设备的日前调度来说,intlinprog几秒钟就能出结果,教学和小规模算例根本不需要额外装求解器。YALMIP的好处是建模语言贴近数学表达,改目标函数和约束都很直观,底层求解器可以随时切换,调试阶段尤其省心。
3.2 目标函数和约束的代码化写法
下面这段是我整理出来的核心建模骨架,去掉了业务噪音,只保留最主干的部分:
T = 24; Pg = sdpvar(1, T); % 电网购电功率 Pd = sdpvar(1, T); % 柴油机出力 Pb_ch = sdpvar(1, T); % 储能充电功率 Pb_dis = sdpvar(1, T); % 储能放电功率 Pcur = sdpvar(1, T); % 切负荷量 SOC = sdpvar(1, T + 1); % 荷电状态 z = binvar(1, T); % 充放电互斥标志位 % 目标函数:购电 + 柴油燃料 + 储能损耗 + 切负荷惩罚 objective = sum(Pprice .* Pg) + sum(dg_k .* Pd) ... + sum(bat_cost * (Pb_ch + Pb_dis)) + sum(penalty * Pcur); constraints = []; % 功率平衡 constraints = [constraints, Pg + Ppv + Pwt + Pd + Pb_dis - Pb_ch == Pload - Pcur]; % 出力上下限 constraints = [constraints, 0 <= Pg <= grid_max]; constraints = [constraints, 0 <= Pd <= dg_max]; constraints = [constraints, 0 <= Pb_ch <= z .* pb_ch_max]; constraints = [constraints, 0 <= Pb_dis <= (1 - z) .* pb_dis_max]; constraints = [constraints, 0 <= Pcur <= 0.05 * Pload]; % 储能SOC递推 constraints = [constraints, SOC(2:T+1) == SOC(1:T) ... - Pb_dis * eta_dis * dt / E + Pb_ch * dt / (eta_ch * E)]; constraints = [constraints, SOC_min <= SOC <= SOC_max]; constraints = [constraints, SOC(1) == soc_init, SOC(T+1) == soc_init]; % 求解 optimize(constraints, objective, sdpsettings('solver', 'gurobi'));几个值得注意的细节:功率平衡方程里Pcur放在等式右侧,表示切负荷等效于降低需求;储能损耗成本按充放电功率累加,能反映电池循环寿命的近似损耗;SOC的初值和终值都锁在初始值上,保证日循环完整性。这套骨架改起来非常方便,想增加爬坡约束就在Pd上补两个不等式,想改成滚动优化就把T和预测数据换掉。
3.3 24小时前瞻调度的完整流程
一个标准的日前调度流程是:先获取次日光伏、风电、负荷的预测曲线,再读取储能当前SOC和设备检修状态,然后构建优化模型并求解,最后把24小时的调度计划下发给执行层。这里我强烈建议在代码里把“预测—优化—执行—更新”拆成四个函数,不要都堆在主脚本里。
实际工程中更常用的是滚动优化:每15分钟或1小时重新优化一次,只执行第一步的决策,后续时段作为参考。这样做能对冲预测误差,价格不高但鲁棒性提升非常明显。仿真代码里做滚动优化时,只需要把预测数据换成长度为T的滑动窗口,其他模型部分完全复用。
4. 仿真实操:从测试数据到结果分析
4.1 输入数据准备:先把参数表列清楚
入门阶段别急着上复杂算例,先用一组典型日数据跑通流程再逐步扩展。我常用的一组测试参数如下:
| 参数 | 数值 | 说明 |
|---|---|---|
| 光伏额定功率 | 200 kW | 峰值出力 |
| 风电额定功率 | 100 kW | 峰值出力 |
| 柴油机额定功率 | 150 kW | 燃料成本系数0.6元/kWh |
| 储能容量 | 200 kWh | 充放电效率0.95 |
| 储能最大充放功率 | 50 kW | 双向一致 |
| 电网交互上限 | 100 kW | 变压器容量限制 |
| 负荷峰值 | 180 kW | 典型日负荷曲线 |
| 购电价 | 峰0.95/平0.55/谷0.25元/kWh | 分时电价 |
光伏和负荷曲线建议用实际数据生成,没有数据就按正态分布叠加随机噪声模拟一条。要提醒的是,光伏曲线的形状一定要符合午间高、早晚低的特征,否则后面分析储能行为时会出现不符合物理直觉的结果。
4.2 核心代码框架:主脚本如何组织
整个仿真代码我习惯按四段组织:参数定义、数据生成、模型构建与求解、结果画图。主脚本结构大致如下:
%% 1. 参数定义 T = 24; dt = 1; load('dispatch_data.mat'); % 包含Ppv, Pwt, Pload, Pprice %% 2. 构建模型(复用3.2节代码) [objective, constraints] = build_dispatch_model(T, data); %% 3. 求解 ops = sdpsettings('solver', 'gurobi', 'verbose', 0); sol = optimize(constraints, objective, ops); if sol.problem == 0 disp('最优解找到'); else disp(['求解失败: ', sol.info]); end %% 4. 结果整理与画图 Pg_opt = value(Pg); Pb_ch_opt = value(Pb_ch); Pb_dis_opt = value(Pb_dis);这里最容易被忽视的是求解状态检查。很多人在跑仿真时完全不看sol.problem的状态码,直接拿value()取结果,一旦模型不可行或者求解超时,拿到的就是一组乱数据,后面的分析全白做。所以记住:在任何结果分析之前,一定要检查求解状态。
4.3 结果可视化与关键指标提取
调度结果通常画三张图:各电源出力堆积图(看功率分配是否合理)、储能SOC曲线(看充放电时序是否正确)、购电功率与电价对比图(看峰谷套利行为是否被触发)。具体画图用MATLAB的area和plot就能完成,比如:
figure; area([Pg_opt', Pd_opt', Pb_dis_opt', Pcur_opt']); hold on; plot(Pload, 'k--', 'LineWidth', 1.5); legend('购电', '柴油机', '储能放电', '切负荷', '负荷');除了画图,建议顺便计算三个核心指标:日运行总成本、可再生能源利用率、储能等效循环次数。这三个指标分别对应经济性、清洁性和设备寿命,是做方案对比时的硬通货。算等效循环次数的方法很简单,把全天放电量加起来除以储能额定容量即可。
5. 常见问题与排查技巧实录
5.1 求解器报“不可行”的九成原因
遇到infeasible先别慌,按这个顺序排查:第一,功率平衡方程里符号是否写反,尤其是储能充放电项的正负号,这是最频繁的错误;第二,检查SOC的初值和终值约束是否和SOC上下限矛盾,比如初始SOC是30%、但终值要求90%,而储能容量和功率上限在24个时段内根本爬不上去;第三,看切负荷变量有没有设置,如果没有,极端负荷场景下约束无解是必然的。
排查不可行问题有一个利器:YALMIP的diagnostics = optimize(...)返回信息里自带冲突约束提示,再配合check(constraints)逐个查看残差,基本几分钟就能定位是哪一条约束出问题。千万别对着代码一行行肉眼找,效率太低。
5.2 储能SOC曲线出现锯齿,先查“防作弊”约束
如果你的SOC曲线在一个充放电状态反复切换、或者出现既充电又放电的异常结果,不用怀疑,几乎都是少了充放电互斥的二进制变量。补上z(t)的两个不等式后,这类问题立刻消失。另外还有一种情况:互斥约束加了但求解成非整数解,多半是求解器设置里允许了MIP gap过大,把sdpsettings('gurobi', struct('MIPGap', 1e-4))收严一些就好。
5.3 求解慢和结果振荡的工程化处理
在MATLAB里跑24时段的模型根本不该慢,如果出现几十分钟跑不完的情况,大概率有这三类原因:一是用了非线性成本函数而没有做分段线性化,MIP求解器对非线性项处理非常吃力;二是Big-M值取得过大,导致数值病态,切负荷惩罚系数也同理,取负荷最大值的10到100倍就够,别动不动写10的6次方;三是binvar变量数量失控,比如给每个时段额外加了大量无关状态变量。
结果振荡的问题则多见于光伏预测误差大但模型没有弃光变量时。系统只能刚性跟踪预测曲线,遇到偏差就大幅调整柴油机出力。加了弃光变量后,优化器有了缓冲余地,出力曲线会平滑很多。
调试过程中我把常见现象整理成了一张速查表,分享给各位:
| 现象 | 最常见原因 | 处理办法 |
|---|---|---|
| 报infeasible | 功率平衡符号错误 | 用check()定位冲突约束 |
| SOC异常锯齿 | 缺充放电互斥约束 | 加二进制变量z |
| 求解非常慢 | 非线性成本未线性化 | 分段线性化成本曲线 |
| 柴油机频繁启停 | 缺爬坡约束 | 补充爬坡上下限 |
| 结果全是0 | 没检查求解状态 | 查看sol.problem信息 |
最后再分享一个我自己反复用的小经验:无论模型改到第几版,始终保留一个“纯购电+纯柴油”的简化对照算例。拿它做基准,能快速验证新加的设备或约束到底给经济运行带来了多少收益。我这套代码之所以能一直用下去,靠的就是这个对照习惯——每次改动都有参照物,心里有底,排查问题时也不至于一头扎进细节里出不来。
本文还有配套的精品资源,点击获取