资讯动态

基于Matlab的楼宇微网虚拟储能优化调度实现与案例分析

发布时间:2026/9/10 3:17:23 来源:尧图企业网站定制
大概在2024年底接了一个商业楼宇光储微网的项目对方提了一个很实际的需求光伏和电池都已经装上了但调度策略还是“光伏满发、电池定时充放”的粗糙玩法夏天空调一开峰段购电还是压不下来电池容量又不够基本只能撑一个小时。后来我们把楼里的空调、热水、充电桩这些用能侧资源全部纳进去做“需求侧虚拟储能”跟物理电池一起参与优化调度效果比单纯扩电池容量明显好得多。这篇就用Matlab实现的全过程来复盘一下从建模、代码到结果分析尽量把每一步的取舍都讲清楚适合正在做微网优化调度、虚拟电厂、或者毕业论文需要代码支撑的同学参考。核心是把“用能侧柔性资源”抽象成储能模型放进同一个优化框架里。1. 楼宇微网调度为什么盯上“虚拟储能”1.1 传统储能在楼宇场景的尴尬楼宇微网的物理储能电池大家第一反应就是“容量不够就加电池”。但实际做完经济测算就发现了问题锂电池按6000次循环寿命、系统效率90%估算把电池全生命周期摊到每度电上度电成本大概在0.5到0.8元之间这还没算换新和运维。在峰谷价差只有0.8元左右的商业电价体系下靠套利回收投资周期经常超过8年很多业主根本等不起。还有一个更隐蔽的问题电池的充放电功率是固定的楼宇负荷高峰往往集中在傍晚5到8点但光伏出力高峰期在中午。电池如果中午充满傍晚放空一天只能做一次完整循环。而空调负荷高峰期却是全天跟着气温走的电池要么在午后提前放电导致傍晚无电可用要么傍晚猛放寿命衰减加速。这种“负荷曲线和电池容量曲线错配”的问题靠物理储能很难单方面解决。虚拟储能的逻辑是把楼宇里本来就有能量存储特性的用电设备通过网络化控制组织起来在电网看来楼宇像一个“储能装置”在运行——负荷低谷时吸收电能等价于充电负荷高峰时少用电等价于放电。1.2 需求侧虚拟储能的核心思路楼宇里到底有哪些“天然电池”首当其冲是建筑的围护结构和室内空气。一栋办公楼的混凝土楼板、墙体、家具、室内空气在空调关掉的半小时内室温可能只回升2到3摄氏度这部分热容量换算成电量以一栋1万平米的办公楼来算大概相当于200到400度电的储能容量。其次是热水箱、冰蓄冷系统再有是电动汽车充电桩、洗衣机、洗碗机这类可时移负荷。把这些柔性负荷集中起来做调度效果相当于一个“虚拟电池”它的特点非常鲜明容量大一个中型办公楼的热容量等效电度数量级是物理电池的数倍、投资成本为零设备本来就要用、没有寿命衰减问题。代价就是调度它有“隐性成本”——比如室温偏差造成的人员舒适度下降、热水温度不足、充电未完成等。这就是虚拟储能参与楼宇微网优化调度的核心思想把舒适度的微小让步换算成经济成本放进目标函数里和购电成本、电池退化成本一起做统一优化最终得到一个“怎么用电最省钱”的全局策略。2. 把楼宇柔性负荷变成“电池”虚拟储能建模2.1 空调类热惯性负荷的ETP等效模型空调负荷是楼宇里最值得做虚拟储能的设备原因就是热惯性大、可调控范围宽。工程上最常用的是一阶等效热参数模型ETP模型C_a * dT_in/dt (T_out - T_in) / R_eq Q_gain P_ac * eta_ac这个公式说白了就是室内温度的变化由室外向室内的传热、室内设备人员产热、空调的制冷/制热功率三方面决定。其中C_a是室内空气等效热容单位kWh/℃R_eq是墙体等效热阻℃/kWP_ac是空调电功率eta_ac是能效比COP。实际做Matlab实现时需要把微分方程离散化。设置调度周期为24小时、时间分辨率为1小时也可以取15分钟效果更精细那么第t个时段的室内温度递推式可以写为T_in(t1) T_in(t) * exp(-dt / (R_eq * C_a)) (T_out(t) R_eq * Q_gain(t) R_eq * P_ac(t) * eta_ac) * (1 - exp(-dt / (R_eq * C_a)))这个式子放进优化模型里之后虚拟储能的概念就具象化了当电价高时优化器会让P_ac(t)适度降低T_in(t)升高相当于“虚拟电池在放电”当电价低时让P_ac(t)提高室内温度预冷到区间下限相当于“虚拟电池在充电蓄冷”。2.2 热水负荷和可时移负荷怎么等效除了空调楼宇里的电热水器和中央热水系统是另一个理想的虚拟储能载体。热水箱本身就是非常标准的蓄能设备模型比空调还简单SOC_water(t1) SOC_water(t) (P_water(t) * eta_water - Q_demand(t)) / C_tank其中SOC_water是热水箱的等效储热比例P_water是电加热功率Q_demand是生活热水消耗功率C_tank是水箱等效热容量。水箱的SOC可以像电池一样有上下限。可时移负荷比如充电桩、洗衣机、消毒柜用二值变量建模设定一个“必须完成的工作量窗口”优化器决定在窗口内哪个时段开启。比如20台电动车充电桩每台7kW总共需要充200度电充电窗口是晚上6点到早上6点优化器就会自动把大部分充电功率挪到夜间谷段——这个过程本质上也是“虚拟储能”。2.3 用户舒适度约束怎么折算虚拟储能不能随便“放”。空调预冷和回调都有一个舒适度边界工程上通常用可接受的温度区间来约束T_min T_in(t) T_max这里T_min、T_max是按实际管理要求设定的可以用带宽2到3摄氏度的线性区间。在代码里我采用的是“硬约束惩罚项”的组合方案温度必须保持在上下限内但是在目标函数中额外加一个人体舒适度PMV的线性化惩罚温度偏离设定值越远PMV偏移越大惩罚越高。这样做的好处是求解器不会为了省钱把室温推到边界上调度结果更符合实际可接受度。实际运行中还有一点要特别注意舒适度约束是对“预测室温”而言的实际室温可能因为门窗开闭、人员密度变化等原因偏移。所以调度策略必须留温度余量比如约束上限25度设定值不能真放在25度建议模型内部按照24到25度优化实际执行留1度余量否则第二天行政部门的投诉电话会把你打爆。3. 优化调度模型目标函数与约束条件搭建3.1 目标函数购电成本 电池退化 舒适度惩罚整个优化调度问题可以做成一个混合整数线性规划MILP目标函数分三块min F sum(P_buy(t) * price(t)) sum(c_battery * (P_ch(t) P_dis(t))) sum(c_comfort * (T_in(t) - T_set(t))^2)第一项是向配电网购电的成本P_buy(t)是时段t从电网买的功率price(t)是分时电价第二项是电池充放电损耗折算成本P_ch和P_dis分别是充放电功率c_battery是每度电过流的退化成本折价第三项是舒适度偏离成本这个惩罚能把室内温度尽量拉回设定值。有些模型还会加上碳排放成本、需量电费惩罚等楼宇场景可以暂不考虑否则模型规模翻倍求解时间拉长第一版做太复杂反而容易出错。3.2 约束体系一览表约束是整个模型里最容易写错的地方我会在代码注释里逐条列清楚。核心约束如下表所示约束类型表达式说明功率平衡P_buy(t) P_PV(t) P_dis(t) P_load(t) P_ac(t) P_water(t) P_ch(t) P_EV(t)电网购电光伏电池放电 固定负荷空调热水电池充电充电桩光伏出力0 P_PV(t) P_PV_forecast(t)可弃光上限为预测值电池SOCSOC_min SOC_bat(t) SOC_max防过充过放电池充放电功率0 P_ch(t) P_ch_max, 0 P_dis(t) P_dis_max功率上限电池SOC递推SOC_bat(t1) SOC_bat(t) (eta_ch*P_ch(t) - P_dis(t)/eta_dis) / C_bat考虑充放电效率室内温度递推见2.1节公式空调虚拟储能核心温度舒适度T_min T_in(t) T_max允许的温度波动范围热水SOC递推SOC_water(t1) SOC_water(t) (P_water(t)*eta_water - Q_demand(t)) / C_tank热水虚拟储能核心充电桩状态sum(P_EV(t)) E_EV_total, P_EV(t) P_EV_max * y_EV(t)总充电量固定y_EV为二值变量电池充放电不要同时进行的约束也要加上工程上通常用一组二值变量y_ch和y_dis来互斥y_ch(t) y_dis(t) 1, 0 P_ch(t) P_ch_max * y_ch(t), 0 P_dis(t) P_dis_max * y_dis(t)求解器层面以上约束全部是线性的二值变量只有充电桩启停和电池充放电互斥因此是个标准的MILP问题规模不大Matlab加YALMIP配任何主流求解器都很轻松。4. Matlab代码实现环境准备与核心逻辑拆解4.1 环境准备代码依赖三样东西Matlab2021a及以上版本都行、YALMIP工具箱、一个MILP求解器。我这边用的是Gurobi如果你手头没有商业求解器用Matlab自带的intlinprog也可以只是求解速度慢一些对于24小时、时间分辨率1小时、单日调度这种规模intlinprog完全够用不会出现跑不动的情况。YALMIP安装非常简单去GitHub下载整个文件夹添加路径保存一下即可核心命令是% 添加YALMIP路径 addpath(genpath(D:\toolbox\yalmip-master)); savepath; % 定义优化变量 x sdpvar(24,1); % 连续变量 y binvar(24,1); % 二值变量 % 设置约束和目标 Constraints [x 0, x 100]; Objective sum(x) sum(y); % 调用求解器 optimize(Constraints, Objective, sdpsettings(solver,gurobi));这里要特别提醒一下YALMIP里面的sdpvar是连续变量binvar是0-1二值变量不要混用类型否则约束写进去求解器直接报错而且错误信息往往不太直观排查起来很耗时。4.2 主函数结构第一版代码不要写成一个巨型脚本建议按功能拆成函数和脚本两个层级主脚本负责数据初始化、调用优化函数、画图函数负责建模和求解输入参数是各类预测数据和设备参数输出结果是各时段功率指令。我的目录结构是这样组织的VES_Scheduling/ ├── main_schedule.m % 主脚本 ├── build_model.m % 建模求解函数YALMIP核心 ├── load_parameters.m % 所有设备参数表格式集中管理 ├── data/ │ ├── price.xlsx % 分时电价 │ ├── pv_forecast.xlsx % 光伏预测 │ └── load_baseline.xlsx % 基础负荷 └── results/ └── schedule_result.mat % 结果保存4.3 核心代码片段解析主脚本的框架大概是这样的%% main_schedule.m clear; clc; close all; % 1. 读取数据 price xlsread(data/price.xlsx); pv_forecast xlsread(data/pv_forecast.xlsx); load_baseline xlsread(data/load_baseline.xlsx); % 2. 加载参数 params load_parameters(); % 3. 调用建模求解函数 solution build_model(price, pv_forecast, load_baseline, params); % 4. 结果可视化 plot_schedule(solution, price, pv_forecast, load_baseline, params);这里最核心的build_model函数其实就是把第3节的数学模型逐条翻译一遍我用YALMIP实现的片段如下%% build_model.m function solution build_model(price, pv_forecast, load_baseline, params) % 时间轴 H 24; % 决策变量 P_buy sdpvar(H, 1); % 购电功率 P_ch sdpvar(H, 1); % 电池充电功率 P_dis sdpvar(H, 1); % 电池放电功率 SOC_bat sdpvar(H1, 1); % 电池SOC T_in sdpvar(H1, 1); % 室内温度 P_ac sdpvar(H, 1); % 空调电功率 SOC_water sdpvar(H1, 1); % 热水SOC P_water sdpvar(H, 1); % 热水加热功率 P_EV sdpvar(H, 1); % 充电桩功率 y_ch binvar(H, 1); % 电池充电互斥变量 y_dis binvar(H, 1); % 电池放电互斥变量 y_EV binvar(H, 1); % 充电桩状态变量 % 目标函数 Objective sum(P_buy .* price) ... sum(params.c_bat * (P_ch P_dis)) ... sum(params.c_comfort * (T_in(1:H) - params.T_set).^2); % 约束 Constraints []; % 功率平衡 Constraints [Constraints, P_buy pv_forecast P_dis ... load_baseline P_ac P_water P_ch P_EV]; ... % 求解 options sdpsettings(solver, gurobi, verbose, 1); diagnostics optimize(Constraints, Objective, options); if diagnostics.problem ~ 0 warning(求解失败: %s, diagnostics.info); end % 输出结果 solution.P_buy value(P_buy); ... end特别提醒两点一是所有功率单位统一用kW能量单位用kWh电价单位用元/kWh这样目标函数算出来就是元不会出现单位错乱二是SOC_bat和T_in这类“有记忆”的状态变量在YALMIP里长度是H1第1个值是初始状态后面的值才是每个时段结束时的状态写约束循环时下标千万别串了这是初学者最容易犯的错。5. 调度结果解读数据说了什么5.1 案例参数设置为了验证代码效果我设计了一个典型商业楼宇案例参数如下参数数值参数数值空调额定功率240kW电池容量100kWh空调能效比COP3.2电池最大充放功率50kW室内初始温度24°C电池充放电效率95%温度设定值24°C温度允许范围22~26°C热水箱等效容量300kWh热水最大加热功率100kW充电桩总需求200kWh充电窗口18:00~次日6:00光伏装机300kW峰平谷电价1.2/0.8/0.4元/kWh实测下来加了虚拟储能之后调度结果和传统“电池定时充放”相比峰段购电功率下降了约18%全天购电成本下降了约14.5%而且这是在室内温度不超过26度、热水SOC始终高于30%的前提下做到的。纯电池方案在同等约束下最多只能压10%的峰段购电差距就是虚拟储能贡献的。5.2 等效SOC曲线怎么画、怎么看验证虚拟储能是否真正“工作”了最有说服力的是看两条等效SOC曲线一条是电池的SOC一条是室内温度转换出来的“空调虚拟储能SOC”。空调虚拟储能SOC的定义可以和电池对应起来SOC_ac(t) (T_max - T_in(t)) / (T_max - T_min)这个公式的含义是室温越低说明蓄的“冷”越多SOC_ac越接近1室温越高冷量耗散越多SOC_ac越低。画出来之后你就能清楚看到在电价高的下午时段SOC_ac是持续下降的说明空调在“放电”——降低制冷功率让室内温度缓慢上升电价降到谷段后SOC_ac又被拉回来空调满负荷制冷把温度压到下限相当于“充电”。这一步其实很有用它用数据回答了很多人质疑的问题“虚拟储能到底有没有在干活”看到SOC_ac曲线跟着电价曲线反向变化结论就很直观了。5.3 温度曲线与舒适度代价当然要交代的还有舒适度代价。仿真结果里在下午4点到6点这个峰段室内温度确实会从24度爬升到25.4度左右之后谷段又会回落到22.5度附近。有物业管理人员看到温度波动会担心员工不适所以我会把目标函数里的舒适度惩罚权重c_comfort调高一些这样温度波动收窄到24到25度之间对应的购电成本只比原来增加2%左右。这个“成本-舒适度Pareto权衡”是虚拟储能调度里特别关键的一个环节建议你在做代码时就把c_comfort写成一个可调参数然后跑几个权重对比一下成本曲线给业主看图时一目了然。6. 复现过程中的几个坑我替你踩过了6.1 求解器导致的奇怪报错第一次跑通模型的时候YALMIP报了一个非常奇怪的错误提示某个约束存在非凸二次项但我的模型明明是线性约束。查了半天发现是Gurobi的许可证没配置好YALMIP悄悄退化用了内置的bnb求解器而bnb对二次约束支持很差。遇到这类“莫名其妙”的报错先检查求解器有没有真正启用在sdpsettings里强制指定求解器名称再用yalmiptest命令验证配置。6.2 SOC递推约束写错下标导致不可行还有一个最坑的BugSOC_bat初值约束写成了SOC_bat(1) 50但模型里的循环是从第1个时段开始递推实际约束写成了SOC_bat(2) SOC_bat(1) ...导致初值没用上第1个时段的充放电直接不受SOC范围约束结果调度出来的策略把电池在第一个小时从空电猛充到满电物理上根本不可能实现。这类问题在Matlab里不报错因为表达式合法但结果完全错误。排查方法是在求解后专门打印SOC_bat(1:H1)的值看一眼初值和终值是否合理。6.3 虚拟储能容量边界设太宽求解结果“假大空”如果把空调温度范围设成18到30度求解器确实能算出极低的运行成本因为它在峰段把空调全关了但这个结果没有实用价值。后来我总结了一个经验法则虚拟储能参数要“拿实际能接受的极限来设”温度偏差不超过2度热水SOC不低于30%充电桩必须完成全部充电需求。这样跑出来的策略才能直接落地执行而不是只能在论文里好看的理想曲线。6.4 时间分辨率对结果的影响我第一版用的是1小时分辨率结果空调虚拟储能的切换动作非常生硬峰段开50%功率谷段直接拉满功率实际设备很难跟上。后来改成15分钟分辨率模型规模变成原来的4倍但Gurobi求解时间也只要6到8秒完全可接受。而且15分钟分辨率下的调度曲线更平滑空调功率变化率也符合变频设备的实际调节能力。建议你如果设备侧有实际响应速度约束再额外加一条功率爬坡约束比如-20 kW P_ac(t1) - P_ac(t) 20 kW这样得到的调度指令送到楼宇自控系统执行时基本不会跳变。7. 后续扩展与个人体会这个项目做完后代码还能往几个方向扩展。一个是把单日优化改成模型预测控制MPC滚动调度每15分钟重新预测一次负荷和光伏并求解未来4小时的策略抗扰动能力会强很多另一个是加入需求响应指令把电网的削峰信号作为硬约束放进模型楼宇可以直接参与需求响应并获取补贴。还可以考虑多栋楼宇协同调度把每栋楼当成一个“虚拟储能单元”去和上级微网互动。最后说一点个人体会。虚拟储能调度在Matlab里实现难点从来不是优化算法本身而是把楼宇物理过程的“约束”和“参数”摸清楚。空调功率、房间热容、热水箱容量这些参数如果拍脑袋乱填模型再精巧输出也是废纸。我的做法是找了楼宇物业拿了一个月的空调功率和室内温度历史数据用最小二乘辨识出了ETP模型的R_eq和C_a效果比经验值好很多。你在复现代码时如果数据条件允许也建议按这个路数来这样跑出来的调度结果才能真正指导实际运行。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价