虚拟储能融合电池储能的楼宇微网优化调度Matlab实现 这两年做微网优化调度的同行应该对“虚拟储能”这个词不陌生。把楼宇里最灵活的冷热负荷——空调、通风、热水——打包成一座看不见的“虚拟电池”跟真实的电池储能一起参与楼宇微网优化调度既能省成本又能提升光伏消纳。围绕这个思想做一套Matlab代码实现几乎是微网方向研究生和一线技术人员的必修课。今天我把这个课题完整拆一遍思路、建模、代码、算例、踩坑。文章偏工程理论推导点到为止重点放在能直接跑起来、能讲清楚结果的细节上。这个题目适合几类人看论文复现需求的研究生、做楼宇能源管理系统开发的工程师、刚接触优化调度想找个能跑通例子的自学者。我不绕弯子上来讲思路中间给公式和代码最后集中讲排查。1. 需求侧虚拟储能系统先把研究对象彻底讲透1.1 从“热惯性”到“虚拟电池”VESS的核心原理虚拟储能系统Virtual Energy Storage System简称VESS它本身不新增任何储能设备而是把楼宇里已经存在的柔性负荷通过控制策略改造成一座“可以充放电的电池”。最典型的对象是中央空调系统。楼宇能耗里暖通空调通常占40%到50%而且是天然有弹性的负荷。人体对温度的感知不是一条线而是一个区间。夏季工况一般24到26摄氏度都算舒适这意味着楼宇在一定时间内是允许“少用点冷”或者“多用点冷”的。这里有个生活常识冬天暖气关了房间不会立刻冷下来夏天压缩机停了室温短时间内也不会明显上升。这就是楼宇的热惯性。墙体、地板、家具、空气本身都在储存热量它们构成了一座天然的“热仓库”。所以VESS的调度逻辑是这样的电价低谷或光伏大发时段把空调功率调高、提前把室内温度降到舒适区间下限相当于给虚拟电池“充电”——把冷量储存在建筑结构里。电价高峰时段把空调功率压下来甚至停机让室温慢慢回升到舒适上限相当于虚拟电池“放电”——释放之前储存的冷量同时减少从电网购电。整个过程对用户体验几乎无感但楼宇的电费曲线和功率曲线被重塑了。这就是VESS这个名字的由来设备是虚拟的但削峰填谷的效果是实在的。1.2 楼宇柔性负荷的可调节潜力评估动手算算你有多少“虚拟电量”搞清楚原理之后第一件事是评估眼前这栋楼到底能“挤出”多少虚拟储能容量。我见过不少论文直接把VESS上下限设成一个拍脑袋的数这会导致调度结果脱离物理实际。你至少应该做一次估算。以一个5000平方米的办公楼为例峰值负荷大约500kW空调总装机150kW。空调功率的可调节幅度一般取20%到35%这就是VESS的功率上限大约±45kW。VESS的能量容量跟楼宇的等效热容直接相关。空气部分的显热计算很简单Q ρ · V · c · ΔT空气密度ρ约1.2kg/m³比热容c约1.005kJ/(kg·K)。假设楼宇有20000立方米空间允许温度浮动2℃单靠空气可以储存约48MJ折算下来大约13.4kWh。但注意墙体、地板、家具的热容比空气大一个量级所以楼宇整体的等效热容量通常是几十到几百kWh。综合估算下来一个中型办公楼能提供的VESS能量容量大约在60到120kWh功率容量40到60kW这个量级已经相当于一套小型锂电池储能了。再结合BESS容量来看如果电池配了100kW/200kWhVESS大约能提供电池容量的一半左右。这就是“融合”的价值基础不买电池也能拿到可观的调节能力。1.3 为什么要把VESS和BESS“融合”起来很多人问既然VESS这么香是不是可以完全替代电池答案是不能至少现阶段不能。看一组对比就清楚了特性物理储能BESS虚拟储能VESS响应速度毫秒级分钟级受热惯性限制可控精度高功率双向精确中受制于舒适度边界初始投资高需消防/空间/审批极低几乎零硬件成本能量上限固定取决于电芯容量浮动取决于温度允许范围恢复周期可长期保持SOC必须周期回位一天内归零预测难度容易困难受天气/人行为影响BESS是“短平快”的功率型选手VESS是“量大管饱”的能量型选手。融合之后BESS负责快速响应和功率平衡VESS负责峰谷套利和光伏消纳二者互补性非常强。还有一层现实原因不少楼宇根本没有条件安装大型电池储能消防审批、空间、投资回报都是坎。VESS几乎不需要新增硬件只需要在楼宇控制系统中添加调度策略是现阶段最容易落地的灵活性资源。2. 楼宇微网优化调度的数学建模约束、目标与变量2.1 VESS的通用数学模型要把虚拟储能放进优化问题里得先把它的“电池特性”数学化。抛开复杂的等效热参数模型调度层面推荐用一个通用的能量状态方程E_vess(t1) E_vess(t) P_vess(t) · Δt这里约定P_vess(t) 0表示虚拟储能“充电”即空调功率高于基线值相当于额外的电能转化为冷量/热量存入楼宇P_vess(t) 0表示“放电”即释放储存的能量电网购电随之降低。约束条件有三个层次功率边界-P_vess_max ≤ P_vess(t) ≤ P_vess_max由空调调节能力决定。能量边界0 ≤ E_vess(t) ≤ E_vess_max由楼宇等效热容和允许温度浮动范围决定。终端状态约束E_vess(1) E_vess(T1)即一天调度结束虚拟储能必须回到初始状态。这一点特别关键否则模型会“作弊”——持续透支冷量把今天的成本转嫁给明天。VESS在建模里最妙的点是“效率”。电池充放电效率通常是95%VESS可以近似设为1因为电能先变成冷量、再以冷量的形式释放没有经过二次转换损耗。如果你想要更保守可以在充放电两侧各乘0.95的衰减系数用来近似冷量在围护结构中的散失。2.2 BESS、光伏与电网交互建模物理储能的建模是微网调度里的标准模块。电池的荷电状态动态方程SOC(t1) SOC(t) (η_ch · P_ch(t) - P_dis(t) / η_dis) · Δt / E_cap其中P_ch和P_dis是非负变量η_ch和η_dis分别是充电、放电效率。由于充放电效率不同需要一个二进制变量确保同一时刻只能充电或放电Constraints [Constraints, 0 P_ch(t) P_bess_max * u_bess(t)]; Constraints [Constraints, 0 P_dis(t) P_bess_max * (1 - u_bess(t))];SOC还需要满足上下限一般为了保护电池寿命设在0.1到0.9之间。调度周期末端的SOC可以要求回到初始值也可以设一个下限这取决于你是做日前调度还是日内滚动。光伏出力按照预测值直接给定必要时引入弃光变量表示削减。弃光的本质是光伏发出的电既不能被负荷消纳、也不能被储能吸收只能白扔。加了VESS之后午间多余的光伏电力可以用来预冷弃光率自然就降下来了。电网交互这块购电功率和售电功率各有一个上限用二进制变量保证不同时买卖电也是一个常见做法。如果你的问题是纯成本最小化且售电价恒低于购电价模型天然不会同时买卖那就不需要额外加整数变量。2.3 目标函数与完整约束清单这个调度问题的目标函数非常直接最小化日内运行总成本。min Σ ( price_buy(t) · P_buy(t) - price_sell(t) · P_sell(t) ) 惩罚项惩罚项看你的研究目标。我建议至少考虑两类一类是弃光惩罚用很小的系数让模型优先消纳光伏避免出现“高价买电弃光”同时发生的矛盾解另一类是功率波动惩罚限制电池和空调的频繁切换防止结果过于抖动。完整的约束体系整理如下编号约束名称表达式要点1功率平衡P_pv P_buy P_dis P_vess_dis P_load P_sell P_ch P_vess_ch2BESS充放电状态互斥P_ch / P_dis 与二进制变量u_bess耦合3BESS功率边界P_ch, P_dis ≤ P_bess_max4BESS的SOC边界0.1 ≤ SOC(t) ≤ 0.95BESS的SOC动态SOC(t1) SOC(t) (η_ch·P_ch - P_dis/η_dis)·Δt/E_cap6VESS功率边界-P_vess_max ≤ P_vess(t) ≤ P_vess_max7VESS能量边界0 ≤ E_vess(t) ≤ E_vess_max8VESS能量动态E_vess(t1) E_vess(t) P_vess(t)·Δt9VESS终端状态E_vess(1) E_vess(T1)10购售电网交互0 ≤ P_buy ≤ P_buy_max, 0 ≤ P_sell ≤ P_sell_max这里面的难点是功率平衡约束里P_vess的正负号。我习惯把它写成统一的净功率变量在平衡方程里按充放电方向分开写。如果写成 P_vess放在负荷侧符号容易混乱。建议先定一个“正方向表”所有约束和代码都严格对着表写。3. Matlab实现要点从数据准备到求解器配置3.1 建模工具选型YALMIP Gurobi/CBCMatlab里求解优化问题最怕自己手写约束矩阵。项目规模和约束一多intlinprog的系数矩阵容易把人绕疯。我强烈建议用YALMIP这个建模工具箱它天然允许你把约束写成接近数学表达式的形式维护成本低一个量级。求解器方面Gurobi是最稳的选择学术用户申请license免费求解MILP的速度和稳定性都远超开源求解器。如果拿不到GurobiCBC作为开源替代也能跑只是遇到带几百个二进制变量的整数规划时会慢不少。小规模问题24时段、单楼宇用Matlab自带的intlinprog也完全够用。安装上有几个坑值得提前说YALMIP不需要安装把文件夹加进Matlab路径就行然后运行yalmiptest确认求解器被识别。Gurobi需要单独安装并配置环境变量Matlab版本和Gurobi版本有兼容矩阵建议用Gurobi官方文档里验证过的组合。网上有大量“Gurobi装完但YALMIP识别不到”的求助帖基本都出在环境变量没生效或者Java路径冲突。重新启动Matlab之前先把系统环境变量改好。3.2 数据准备与典型日场景构造做仿真实验时最常卡住的问题没有真实楼宇数据怎么办我的建议是分两步走。先用合成数据把模型逻辑验证通再替换真实数据。合成数据可以这样构造光伏曲线用一个钟形函数P_pv P_pv_max * exp(-(t - t_peak)^2 / (2*σ^2))夏季典型日中午12点到14点达到峰值。负荷曲线用两段高斯叠加出早晚双峰早上8到10点一个峰晚上18到21点一个峰夏季中间再叠加一段空调制冷负荷。分时电价设置谷段22点到次日7点0.36元/kWh平段7点到10点、15点到18点0.72元/kWh峰段10点到15点、18点到22点1.2元/kWh。这套数据虽然来自构造但形态和量级符合中国典型商业楼宇的实际情况。把模型调通之后再去找真实楼宇的负荷数据替换逻辑不会变。真实数据可以去OpenEI、楼宇能耗公开数据集找也可以直接摘自己项目里的历史负荷曲线。3.3 核心代码解读从变量声明到求解调用下面给一套可运行的YALMIP代码骨架覆盖了前面模型的所有核心逻辑%% 参数设置 T 24; dt 1; E_cap 200; P_bess_max 100; SOC_init 0.2; E_vess_max 100; P_vess_max 50; eta_ch 0.95; eta_dis 0.95; %% 决策变量 P_buy sdpvar(1, T); P_sell sdpvar(1, T); P_ch sdpvar(1, T); P_dis sdpvar(1, T); SOC sdpvar(1, T1); P_vess sdpvar(1, T); E_vess sdpvar(1, T1); u_bess binvar(1, T); %% 目标函数 obj sum(price_buy .* P_buy) - sum(price_sell .* P_sell) ... 0.1 * sum(max(0, P_pv - (P_pv_actual))); % 弃光惩罚占位 %% 约束 Constraints []; % 功率平衡正方向按耗电计 Constraints [Constraints, P_pv P_buy P_dis ... P_load P_sell P_ch P_vess]; % BESS约束 Constraints [Constraints, 0 P_ch P_bess_max * u_bess]; Constraints [Constraints, 0 P_dis P_bess_max * (1 - u_bess)]; Constraints [Constraints, SOC(1) SOC_init]; Constraints [Constraints, 0.1 SOC 0.9]; for t 1:T Constraints [Constraints, ... SOC(t1) SOC(t) (eta_ch * P_ch(t) - P_dis(t) / eta_dis) * dt / E_cap]; end % VESS约束 Constraints [Constraints, E_vess(1) E_vess(T1)]; Constraints [Constraints, -P_vess_max P_vess P_vess_max]; Constraints [Constraints, 0 E_vess E_vess_max]; for t 1:T Constraints [Constraints, E_vess(t1) E_vess(t) P_vess(t) * dt]; end %% 求解 optimize(Constraints, obj, sdpsettings(solver,gurobi,verbose,1)); %% 结果提取 P_buy_opt value(P_buy); P_vess_opt value(P_vess); E_vess_opt value(E_vess); SOC_opt value(SOC);这套代码里我把功率平衡里的P_vess直接放在用电侧正为充电负荷增加、负为放电负荷减少。与2.1的模型定义一致运行时不会出现符号打架的问题。有个细节值得注意弃光惩罚占位里我用了max(0, ...)这在YALMIP里不能直接用需要引入辅助变量线性化。实际项目里更稳妥的做法是定义弃光变量P_curt sdpvar(1,T)并加约束0 ≤ P_curt ≤ P_pv_max然后在功率平衡中减掉它。上面代码里这个位置留给你按自己的数据改。3.4 结果可视化三张图判断调度是否合理结果画图这一块大多数教程只扔一句“画图看效果”但实战里画图才是验证模型对错的第一手段。我的经验是至少画三张图。第一张功率平衡堆叠图。横轴是时间画出光伏、购电、电池放电、VESS放电的堆叠和负荷曲线放在一起比较。如果堆叠总高度不等于负荷曲线那功率平衡约束一定写错了。第二张SOC与E_vess曲线图。电池SOC曲线应当是平滑的在电价谷段充电、峰段放电。VESS能量状态曲线应该像“一个周期内的冲激响”——先上升后下降首尾相等。如果E_vess全天单调下降说明终端状态约束没起作用。第三张分时电价与购电量对比图。理想结果是购电量集中分布在电价谷段和平段峰段购电量大幅削减。如果峰段还在大量购电看是不是BESS容量太小或VESS功率上限太小需要调参。4. 仿真实验设计与算例分析对比方案与结果讨论4.1 三种方案的设计与运行成本对比设计实验方案时建议用“控制变量法”把VESS的价值单独拎出来。一套标准配置如下方案A无储能纯购电光伏。方案B仅BESS100kW/200kWh初始SOC 20%。方案CBESSVESSVESS功率上限50kW能量容量上限100kWh。用3.2的合成数据跑一天24小时结果量级大概是这样的这是典型工况下的示意结果具体数值随数据不同会变指标方案A方案B方案C日运行成本元141812481089购电量kWh183016101432光伏消纳率91.2%94.5%97.6%峰段购电占比31%19%11%方案A到方案B成本下降12%方案B到方案C再降12.7%。VESS带来的边际收益一点都不比电池差这是“融合”最有说服力的点。更值得注意的是峰段购电占比从19%降到11%这说明VESS确实在削峰。光伏消纳率提升到97.6%说明午间多余的光伏电力被VESS“吃掉”了而不是被白白弃掉。4.2 VESS对系统运行的实质性影响只看总成本还不够要把调度策略落到运行层面讲清楚。看典型日的VESS功率曲线你会发现逻辑完全符合预期中午12点到14点光伏大发、电价处于平段VESS开始“充电”空调功率从基线150kW升到195kW左右多出来的45kW直接由光伏供给。室内温度从25℃被预冷到23.5℃左右。傍晚18点到22点电价进入峰段VESS开始“放电”空调功率被压低到90kW左右释放出60kW的冷量储备室内温度慢慢回升到舒适区间上限。与此同时电池在峰段也集中放电两者的放电功率叠加使得峰段购电功率大幅下降。这里有个容易被忽视的点VESS的加入减轻了BESS的负担。方案B里电池一天可能需要满充满放1.2次以上SOC波动剧烈方案C里电池充放电次数显著减少对延长电池寿命很有帮助。这个结论在很多论文里都有提及你在自己的结果对比里也可以加上“电池循环次数”这个指标。4.3 灵敏度分析舒适度边界、峰谷价差与VESS容量论文或项目汇报里灵敏度分析是证明方案稳健性的关键。这块做三个方向的扫描就够了。第一个方向温度舒适度范围。舒适区间从±1℃放宽到±2℃VESS能量容量从60kWh增大到120kWh成本下降幅度从8%扩大到15%。但注意超过2℃用户投诉风险显著增加工程上不能为了省钱无限放宽。第二个方向峰谷价差。峰谷价差从0.6元/kWh扩大到0.9元/kWhVESS的收益跟着涨这是因为价差越大“谷充峰放”套利空间越大。第三个方向VESS容量上限。扫描VESS功率从0kW到100kW成本下降曲线呈现明显的边际递减趋势容量从0增到50kW时收益最明显继续翻倍到100kW额外收益就很小了。这个规律可以帮助确定VESS的“经济最优”配置不用把容量配得过大。灵敏度分析的表格做个简单示例舒适度范围VESS能量上限日成本元成本降幅±1℃60kWh11567.4%±1.5℃90kWh108912.7%±2℃120kWh103217.3%5. 常见问题与排查技巧实录求解、结果与工程落地5.1 求解失败的三个典型原因跑这个模型最常见的就是求解器直接报Infeasible problem。我踩过的坑基本集中在三处。第一功率平衡约束的正负号搞反。P_vess在电平衡里放在哪一侧决定了它的正方向语义。建议写一个“符号速查表”写代码前先把所有变量的正方向定义清楚。第二SOC和E_vess的初值、终值互相矛盾。比如SOC初值设了20%却要求终端SOC等于80%而电池容量又不够一天充满直接无解。VESS的终端约束E_vess(1) E_vess(T1)如果和初始值设置不一致同样会报无解。顺序排查先看初值终端值是否合理再看功率平衡最后看上下界是否冲突。第三求解器没配好。报错里出现No solver found或者License error基本是YALMIP没识别到Gurobi或者license没激活。跑一次yalmiptest能快速定位问题。5.2 结果不合理的常见原因与修正方法结果能跑出来但一眼看去是错的这种问题更让人崩溃。整理了我遇到最多的三种情况现象可能原因解决办法同一时段既购电又售电售电价高于购电价模型在“套利”设置售电价低于购电价或加二进制变量互斥SOC锯齿状剧烈震荡充放电切换没有惩罚项目标函数增加切换惩罚或加功率变化率约束E_vess全天单调下降VESS终端状态约束缺失或初始值设错确认E_vess(1) E_vess(T1)已加入且初值0.5倍容量附近空调功率频繁大幅波动电价波动导致VESS功率出现台阶跳变对P_vess添加爬坡约束比如每时段变化不超过±20kWVESS功率频繁波动这个坑最隐蔽。从优化结果看VESS“完美”地跟随了电价变化但工程上空调没法这样剧烈调节室内温度也会跟着抖。加爬坡约束是最直接的解法代价是成本会比理论最优值高一点点但真实可执行。5.3 工程化落地中的几个提醒仿真跑通只是第一步真正落到楼宇控制系统里还有几件事值得记在心里。调度周期要合理。日前调度建议用1小时步长跑24点够快也够用。日内修正建议用15分钟步长滚动优化每次滚动都把最新的光伏预测和室温反馈更新进去这就是工程上常说的模型预测控制思路。VESS的容量不是常数。温度、风速、太阳辐射都会改变楼宇的等效热容真实系统中E_vess_max应该根据实时室温动态更新。模型里设固定值是简化落地时最好加一层参数辨识。预测误差对VESS的影响比BESS更大。BESS的实际充放电功率可以跟踪指令到很高精度VESS则受制于人行为、门窗开关等随机因素。工程上要给VESS的执行偏差留裕量调度结果里把VESS的放电功率打折到计算值的80%再用实测下来更稳。从我个人复现这个课题的经验来说最花时间的地方往往不在模型本身而在数据对齐和符号统一。建议你从一开始就严格规定功率正方向、单位、时基这样所有模块对接起来会顺畅很多。上面给的代码骨架可以直接改数据跑跑通后再一步一步换成你自己的场景会比从零开始少走很多弯路。