
先说明一下这个项目的背景。楼宇微网这几年在双碳目标和电价市场化改革的双重推动下出镜率越来越高。但真正动手做优化调度的人都知道楼宇微网有个很尴尬的痛点——物理储能太贵了一块锂电池从采购到安装再算上运维和衰减成本占比能把整个项目的经济性拖垮。于是“虚拟储能”的概念就火起来了与其花钱买电池不如把楼宇里本来就有的柔性负荷——空凋、热水器、电动汽车充电桩——通过优化调度变成一支“看不见的电池”。这正好是Matlab能发挥长处的领域。这篇博文我来拆解一个完整的实战项目融合需求侧虚拟储能系统的楼宇微网优化调度Matlab代码实现)从模型构建、数学原理到代码落地、参数调试全程走一遍把关键环节和踩过的坑都交代清楚。如果你想做微网优化调度、虚拟电厂、需求响应方向的课题或工程落地这篇文章可以直接拿来当参考框架。1. 项目思路拆解虚拟储能到底是什么以及为什么必须“融合”1.1 虚拟储能的本质把“用电”变“储能”虚拟储能不是一种物理设备而是一种能力。楼宇里的空调、热水器、电梯、电动汽车充电桩都有一定的“时间弹性”——早半小时开空调和晚半小时开空调对用户体感影响不大但对电网来说这就是几百千瓦甚至兆瓦级的调节空间。举个例子。一栋商业楼宇的中央空调房间热容量本身就是一个巨大的“热能仓库”。在电价低谷时段把室内温度预冷到下限相当于把冷量以热能的形式“存”进了墙体在电价高峰时段减少制冷功率让温度自然回升相当于把“存的冷”释放出来。整个过程没有增加任何电池设备却实现了和储能几乎一样的削峰填谷效果——这就是虚拟储能的核心逻辑。1.2 需求侧响应与调度的关系被动参与和主动优化是两回事需求侧响应这个词大家不陌生但很多初学者的误区在于把需求响应理解成“收到信号然后砍负荷”。那是被动的、片面的。真正有价值的做法是把需求侧资源纳入调度模型作为决策变量让它们参与最优潮流计算提前一天规划好每一个时段各柔性设备应该怎么运行——这是主动优化。这个项目之所以强调“融合需求侧虚拟储能系统”就是因为它把两类资源放在同一个优化框架里物理储能传统的蓄电池充放电效率高、响应快但容量有限、成本高。虚拟储能楼宇内的温控负荷、可平移负荷、可中断负荷容量大、成本低但受用户舒适度和生产约束限制。两类储能特性互补物理储能解决瞬时功率平衡和紧急支撑问题虚拟储能解决长时间尺度能量搬移问题。把两者联合建模就是“融合”二字的含义。1.3 为什么选Matlab而不选Python这估计是很多人在评论区想问的。我的观点是Matlab在优化调度领域的工程惯性非常强尤其在学术圈和电力行业几乎成了隐形的“行规”。原因有三个Yalmip工具箱的抽象能力用Yalmip建优化模型约束条件直接按数学公式写逻辑清晰排错容易。Python里虽然也有Pyomo、CVXPY但在混合整数线性规划的建模直觉上Yalmip更顺手。与Simulink的联动如果后续要做仿真验证比如把调度结果接到具体的设备模型上跑动态响应Matlab Simulink是一条龙。论文和课题的复现性大量微网调度方向的文献附带的都是Matlab代码用Matlab复现、对比、改进效率最高。当然Python在数据预处理和大规模并行上更灵活但就单机、中规模几十到几百个节点的楼宇微网优化调度来说Matlab完全够用而且更稳。2. 系统模型构建楼宇微网里都有什么怎么抽象成数学表达2.1 楼宇微网的基本物理结构先画一个逻辑结构图心里有数就行电网上级配电网——变压器——交流母线—— 下面挂着的负荷和电源设备光伏阵列分布式电源储能电池物理储能中央空调系统虚拟储能的主要载体常规刚性负荷照明、电梯、办公设备电动汽车充电桩可作为可平移负荷也可视为V2G虚拟储能优化调度的作用就是明天每小时告诉你电池充还是放、充多少、空调制冷功率设为多少、充电桩要不要延后、从电网买多少电使得一整天的总运行成本最低。2.2 目标函数不只是电费最小化目标函数是优化调度的“指挥棒”。最常见的设定是全天运行成本最小化但运行成本不是只有“电费”一项$$ \min \sum_{t1}^{T} \left( C_{buy,t}P_{buy,t} - C_{sell,t}P_{sell,t} C_{om}^{bat}|P_{bat,t}| C_{comf} \Delta T_{indoor,t} \right) $$逐项拆开看购电成本与售电收益(C_{buy,t}) 是 t 时段从电网购电的分时电价(P_{buy,t}) 是购电功率如果有余电上网条件还要算 (C_{sell,t}P_{sell,t}) 的售电收益。电池损耗成本(C_{om}^{bat}|P_{bat,t}|) 表示充放电功率带来的寿命折损和运维成本。电池不是免费的频繁充放会让循环寿命下降这个成本必须进目标函数否则优化器会“疯狂”充放电。热舒适度惩罚(C_{comf}\Delta T_{indoor,t}) 是虚拟储能引入的关键——空调不能为了省钱无限调高温差用户舒适度是底线约束。引入这个惩罚项可以让模型在节能和舒适之间自动取折中。这套目标函数最精妙的地方在于虚拟储能装置的“成本”不是折旧而是热舒适度损失。它把舒适度货币化了从而可以和电费直接比较纳入同一个优化框架。2.3 约束条件没有约束的优化是耍流氓楼宇微网的约束条件我习惯归成四类。2.3.1 功率平衡约束电网侧每一时刻微网内部的发电 购电 负荷 充电 售电。这是必须满足的硬约束$$ P_{PV,t} P_{bat,t}^{dis} P_{buy,t} P_{load,t}^{fixed} P_{AC,t} P_{EV,t} P_{bat,t}^{ch} P_{sell,t} $$其中 (P_{AC,t}) 是空调消耗的电功率(P_{EV,t}) 是充电桩消耗的电功率。注意这里的 (P_{load,t}^{fixed}) 是刚性负荷不可调节。2.3.2 储能电池约束电池有三类约束缺一不可SOC动态方程(SOC_{t1} SOC_t (\eta_{ch}P_{ch,t} - P_{dis,t}/\eta_{dis})\Delta t / E_{rated})SOC上下限(SOC_{min} \le SOC_t \le SOC_{max})这里提醒一句不要把上下限设成 0 和 1实际工程中建议 0.1~0.9保护电池寿命。充放电功率限制(0 \le P_{ch,t} \le P_{ch}^{max}x_t)(0 \le P_{dis,t} \le P_{dis}^{max}(1-x_t))其中 (x_t) 是 0-1 变量防止同时充放电。2.3.3 虚拟储能空调约束这是整个项目的灵魂部分。空调系统的“虚拟储能”建模最常用的是等效热参数模型ETPEquivalent Thermal Parameter$$ T_{in,t1} T_{in,t} \frac{\Delta t}{RC}\left(T_{out,t} - T_{in,t}\right) - \frac{\Delta t \cdot COP \cdot P_{AC,t}}{C_{air}} $$这个公式的物理解释室内温度的变化 外界通过墙体传入的热量减缓或增加 - 空调制冷带走的热量。其中 R 是墙体的热阻C 是室内空气的热容COP 是空调能效比。关键约束是热舒适度范围 $$ T_{min} \le T_{in,t} \le T_{max} $$这个上下限就是虚拟储能的“储能容量”。允许的温差范围越大虚拟储能的调节空间就越大。实际项目中商用楼宇一般允许 22°C ~ 26°C即舒适度区间4°C住宅可以稍微放宽。2.3.4 充电桩约束充电桩作为可平移负荷核心约束是全天总充电量不低于用户设定的目标电量但充电时段可以灵活安排 $$ \sum_{tt_{arr}}^{t_{dep}} P_{EV,t}\Delta t \ge E_{need} $$加上充电功率上限 (0 \le P_{EV,t} \le P_{EV}^{max})。这里有个工程细节如果你把用户目标电量设置得特别高比如从 20% 充到 100%那优化器基本没有平移空间充电桩就退化成刚性负荷。所以要让虚拟储能发挥价值充电目标设定要留有余量。3. 数据准备与环境配置跑起来之前的三个关键步骤3.1 Matlab工具箱选型Yalmip 求解器这个项目不需要自己写求解算法核心工作在于建模。我用的是Yalmip Cplex的组合。Yalmip一个建模层不是求解器。它把优化问题描述转换成求解器能理解的格式。免费去GitHub下最新版加入Matlab路径即可。CplexIBM的求解器处理混合整数线性规划非常强。学术版免费但需要申请License。如果没有Cplex也可以用Gurobi或开源的CBC、GLPK对小规模的楼宇模型完全够用。安装完两个包后在Matlab里验证一下% 验证Yalmip和求解器是否安装成功 yalmiptest % 如果输出结果中 cplex 一项显示 OK 或 Found说明能正常调用我自己的习惯是模型规模小、求解时间敏感的话优先Cplex如果只是教学演示用Gurobi或CBC也足够后面调试也方便。3.2 基础数据的生成与导入没有数据优化模型就是一纸空文。楼宇微网调度需要的基础数据有五类分时电价曲线典型工商业电价曲线峰段8-11点、18-21点1.2元/kWh平时段11-18点0.8元/kWh谷段23-次日7点0.4元/kWh。光伏出力曲线用典型夏季晴天的日辐射数据转成功率输出。如果手头没有实测数据可以用 Matlab 的 OpenIAP 工具箱生成或者直接构造一组正态分布形状的数据。室外温度曲线影响空调负荷的关键输入夏季典型日大约 24°C~35°C。刚性负荷曲线办公设备、照明等按工作日的峰谷特性手动构造。电池、空调、充电桩的物理参数容量、功率上下限、效率、COP等。这些数据在代码里用Excel读入或者直接写成结构体变量struct放脚本里。做科研项目建议用Excel方便调参不用每次改代码。3.3 场景参数设置一次典型的基础配置我这次的仿真场景参数如下参数含义数值备注调度周期 T24小时时间间隔 1h电池额定容量200 kWh电池最大充/放电功率50 kW电池初始SOC0.5结束时也设为0.5保证日循环空调额定功率80 kW空调COP3.5室温上/下限26/22 °C舒适度区间充电桩功率上限40 kW3台充电桩每台13.3 kW光伏装机容量150 kWp这个场景是典型的中型商业楼宇配置体量不大但麻雀虽小五脏俱全跑一遍能看清所有优化调度逻辑。4. Yalmip建模与Matlab代码实现一步步从公式到可跑代码4.1 变量的定义用Yalmip定义优化变量非常直观sdpvar定义连续变量binvar定义0-1变量% 调度时段 T 24; dt 1; % 单位小时 % 连续变量定义 P_buy sdpvar(1, T); % 从电网购电功率 kW P_sell sdpvar(1, T); % 向电网售电功率 kW P_bat sdpvar(1, T); % 电池功率正充负放kW SOC sdpvar(1, T); % 电池荷电状态 P_ac sdpvar(1, T); % 空调电功率 kW T_in sdpvar(1, T); % 室内温度 °C P_ev sdpvar(1, T); % 充电桩功率 kW % 二进制变量定义 u_buy binvar(1, T); % 购电状态 u_sell binvar(1, T); % 售电状态 u_ch binvar(1, T); % 电池充电状态 u_dis binvar(1, T); % 电池放电状态这里有个细节购电和售电、充电和放电的0-1变量是用来防止物理上矛盾的工况出现。加上它们模型就从线性规划升级成混合整数线性规划求解时间会变长但结果才符合实际。4.2 约束条件的代码写法把前面数学模型的约束条件翻译成Yalmip代码。核心约束写成constraints列表Constraints []; % 1. 功率平衡约束 for t 1:T Constraints [Constraints, P_pv(t) P_bat(t) P_buy(t) ... P_load_fixed(t) P_ac(t) P_ev(t) P_sell(t)]; end % 2. 电池SOC动态方程 SOC0 0.5; E_bat 200; % kWh eta_ch 0.95; % 充电效率 eta_dis 0.95; % 放电效率 for t 1:T-1 Constraints [Constraints, SOC(t1) SOC(t) ... (eta_ch * max(P_bat(t), 0) - max(-P_bat(t), 0) / eta_dis) * dt / E_bat]; end Constraints [Constraints, SOC(1) SOC0 ... (eta_ch * max(P_bat(1), 0) - max(-P_bat(1), 0) / eta_dis) * dt / E_bat]; Constraints [Constraints, SOC(T) SOC0]; % 日循环约束 % 3. 电池充放电功率及状态互斥约束 P_ch_max 50; P_dis_max 50; for t 1:T Constraints [Constraints, 0 P_bat(t) P_ch_max * u_ch(t)]; Constraints [Constraints, -P_dis_max * u_dis(t) P_bat(t) 0]; Constraints [Constraints, u_ch(t) u_dis(t) 1]; end % 4. SOC上下限 SOC_min 0.1; SOC_max 0.9; for t 1:T Constraints [Constraints, SOC_min SOC(t) SOC_max]; end这里要注意max(P_bat(t), 0)这种写法在Yalmip中虽然支持但会引入非光滑函数影响求解效率。更推荐的做法是把充放电拆成两个变量P_ch sdpvar(1, T); % 充电功率正数 P_dis sdpvar(1, T); % 放电功率正数 % 电池功率 P_bat P_ch - P_disSOC方程就变线性了这个改动看起来小但对求解器非常友好Cplex处理线性约束比处理带max函数的约束快一个数量级。4.3 虚拟储能约束的代码实现空调系统是虚拟储能的核心载体它的建模要依托ETP模型。Yalmip里线性约束直接写就行% 空调参数 R 0.12; % 墙体热阻 °C/kW C_air 0.525; % 室内热容 kWh/°C COP 3.5; P_ac_max 80; % 空调最大电功率 kW % ETP模型离散状态方程 for t 1:T-1 Constraints [Constraints, T_in(t1) T_in(t) ... dt/(R*C_air) * (T_out(t) - T_in(t)) - ... (dt * COP / C_air) * P_ac(t)]; end % 初始室内温度为24°C T_in0 24; Constraints [Constraints, T_in(1) T_in0]; % 功率上下限 for t 1:T Constraints [Constraints, 0 P_ac(t) P_ac_max]; end % 舒适度约束 T_min 22; T_max 26; for t 1:T Constraints [Constraints, T_min T_in(t) T_max]; end为什么空调功率只约束在 0 到 (P_{ac}^{max}) 之间不需要0-1变量因为空调是连续调节的变频空调不是只能开/关。如果需要建模定频空调只有开关两种状态就得引入二进制变量 (u_{ac,t})此时空调功率变成 (P_{ac,t} P_{ac}^{rated}u_{ac,t})模型复杂度会上升不少。我的经验是优先建模变频空调因为楼宇中央空调绝大多数是变频的而且线性连续模型求解快得多。4.4 目标函数与求解目标函数按前面的定义逐项写% 电价参数分时电价 price_buy [0.4*ones(1,7), 0.8*ones(1,3), 1.2*ones(1,3), ... 0.8*ones(1,3), 1.2*ones(1,4), 0.8*ones(1,2), 0.4*ones(1,2)]; price_sell 0.65 * price_buy; % 上网电价一般按购电价打折 % 成本系数 c_bat 0.02; % 电池运维损耗 元/kWh c_comf 10; % 舒适度惩罚系数 元/°C % 目标函数 Objective 0; for t 1:T Objective Objective ... price_buy(t) * P_buy(t) - price_sell(t) * P_sell(t) ... c_bat * (P_ch(t) P_dis(t)) ... c_comf * max(0, T_in(t) - T_set_high) ... % 高温惩罚 c_comf * max(0, T_set_low - T_in(t)); % 低温惩罚 end % 求解 ops sdpsettings(solver, cplex, verbose, 2, showprogress, 1); optimize(Constraints, Objective, ops); % 提取结果 P_buy_opt value(P_buy); P_ac_opt value(P_ac); T_in_opt value(T_in); SOC_opt value(SOC);至此一个完整的楼宇微网优化调度模型就跑通了。5. 结果分析与场景对比没有对比就没有说服力5.1 单日调度结果的解读跑完代码后把结果画出来你会看到几条非常有信息量的曲线。电池SOC曲线理想情况下SOC呈现“谷段充电、峰段放电”的形态。凌晨2-5点电价低谷电池从初始0.5充到0.9上限上午8-11点电价高峰电池放电到0.5左右下午再找机会补充晚上峰段再次放电最终回到0.5。空调功率曲线虚拟储能的核心价值在这里体现。上午10点电价尖峰时空调功率明显压低室内温度从24°C缓慢爬升到26°C上限下午电价回落到平段空调加大功率把室温“打”回24°C。这个过程就是典型的“虚拟储能放电——释放室内蓄存的冷量”与物理储能在峰段放电的形态如出一辙。购电功率曲线叠加了电池放电和空调减载的双重作用后峰值时段的购电功率大幅下降。跟无优化场景对比峰值负荷削减率通常能达到 15%~25%。5.2 三种调度模式的经济性对比为了验证模型的有效性我会跑三种模式做对比模式说明全天运行成本不加调度空调恒温24°C电池不动作购电满足全部负荷基准仅物理储能电池优化调度空调恒温成本下降约8%~12%虚拟物理储能融合电池空调充电桩全部参与优化成本下降约15%~20%虚拟储能的边际效益如此显著原因不难理解电池容量200kWh在峰谷电价差 0.8 元/kWh 的情况下一天最多搬移 200×0.8160元 的电费而空调虚拟储能在4°C舒适度区间内一整天的等效调节能量可达500~800kWh峰谷套利空间是物理储能的3~5倍。两者融合收益自然翻倍。5.3 灵敏度分析舒适度区间和电池容量怎么取舍调参时可以做两个灵敏度分析结论非常直观舒适度区间大小把 (T_{min}) 和 (T_{max}) 的差值从2°C拉大到6°C虚拟储能等效容量近似线性增长热容模型运行成本持续下降但到±4°C以后边际收益递减。4°C是个性价比很合适的中间值。电池容量把电池从100kWh逐步增到500kWh成本先降后平。原因是峰谷套利空间有限电池太大多出来的容量根本没有能量可搬移纯属浪费投资。这从定量层面解释了为什么虚拟储能可以替代一部分物理储能投资。6. 常见问题与排错实战Yalmip建模最容易踩的五个坑6.1 求解报“Infeasible problem”怎么办这是初学者最崩溃的时刻。问题不可行的常见原因约束过强比如室内温度限制过严21°C~23°C空调能力不足根本无法满足。排查方法先把舒适度约束放宽到18°C~30°C看模型是否可行可行后再逐步收紧找到可行边界。日循环约束冲突SOC(T) SOC0这个约束有时会和充放电功率上限冲突。比如末端时段电价虽高但电池容量和功率上限注定了充不到初始SOC。排查方法把终值约束改成SOC(T) 0.45这类不等式或者加一个小的惩罚项而非硬约束。单位不统一功率用kW能量用kWh时间用小时三个单位必须自洽。最容易错的地方是功率 × 时间 能量这一步。6.2 Cplex求解很慢或卡死楼宇微网模型一般规模不大卡死通常是自己写复杂了。一个常见病根是把SOC方程里写了max(P_bat, 0)这种非线性项虽然Yalmip能处理但求解器的性能会断崖式下降。解决办法拆变量——充电和放电各用一个非负连续变量模型保持线性秒解。另一个坑是二进制变量太多。如果电池模型里用了u_ch和u_dis且约束写成u_ch u_dis 124时段就是48个二进制变量很小但如果把每个充电桩都加上启动状态停车时段逐小时变量再加上购售电互斥可能会膨胀到200个二进制变量这时求解时间会明显拉长。优化手段是把购电和售电互斥去掉因为只要购电价高于售电价优化器天然不会同时购售电实在需要保留可以加一个很松的惩罚项代替二进制约束。6.3 Yalmip报“Could not find solver”这纯粹是安装问题。Yalmip只是个建模语言不包含求解器。需要单独安装Cplex/Gurobi/CBC并把对应文件夹加进Matlab路径。用yalmiptest命令能看到当前可用的求解器列表。常见的坑是装了Cplex但Matlab没找到cplexlicens.dat需要设置环境变量ILOG_LICENSE_FILE。6.4 结果出现极端值比如空调功率一直在最大或最小这通常是目标函数里缺少惩罚项。如果你没把热舒适度写进目标函数只写了T_min T_in T_max优化器会“钻空子”只要能保证温度在区间内就会让空调功率最小最省钱或最大如果夏天电价负值不管实际体感。解决办法在目标函数里加入温度偏离设定值的罚项前面代码里的c_comf * max(0, T_in - T_set_high)就是干这个的但注意罚项的系数不能太大否则优化器会为了省0.1°C不惜一切代价充放电电池扭曲了虚拟储能的行为。6.5 结果曲线有跳变、不平滑这是正常现象因为分时电价本身就是阶跃的且模型是小时级离散的优化器会在电价切换的边界瞬间改变运行策略。如果希望曲线更平滑可以缩小时间粒度从1小时改成15分钟Δt0.25但模型规模会变成原来的4倍求解时间也显著上升。工程上1小时粒度足够支撑中长期调度决策不需要强行追求平滑。7. 项目扩展思路从单楼宇到规模化应用一个楼宇的优化调度模型跑通后扩展路径非常清晰。扩展方向一多楼宇协同。把多个楼宇的虚拟储能聚合成一个虚拟电厂统一参与需求响应或电力现货市场。模型上只需要把约束维度从“楼宇”扩展到“楼宇集合”在公共母线上增加联络线功率约束。这部分我后续单独写一篇关键点在于楼宇间的利益分配问题。扩展方向二加入不确定性。光伏出力和负荷预测不可能100%准可以在模型里引入场景法或鲁棒优化。常用做法是生成N个场景把确定性模型扩展成两阶段随机规划——第一阶段做日前调度决策第二阶段根据光伏/负荷实际值做实时修正。Yalmip也能处理这类问题但求解规模会大很多。扩展方向三把空调模型换成更精细的RC网络模型。ETP模型是单节点模型精度有限。实际楼宇有多个房间、多层结构墙体传热不是一个RC能完全描述的。可以用二阶或三阶RC模型模型精度提升但参数辨识难度也上来了。扩展方向四做实时滚动优化。日前调度是开环决策实际运行时会发现执行结果和计划有偏差。更工程化的做法是模型预测控制——每15分钟滚动一次用当前实测状态刷新预测重新求解未来4小时的调度方案。这也是工业界最接近实际落地的方案。我个人的体会是虚拟储能要想在工程上落地最关键的从来不是算法多聪明而是你能不能在可接受的舒适度损失下把楼宇里的“储能潜力”精确地量化和调度起来。这需要电力系统、暖通空调HVAC、数学优化三个领域的知识交叉。Matlab和Yalmip只是把这些知识拧在一起的工具但有了这套工具和模型框架你可以非常高效地验证不同场景、不同参数下的调度策略效果为实际工程决策提供可靠的定量依据。最后再分享一个调试小技巧建完模型先别急着跑完整24小时先用T4比如只取0-4点测试跑通确认目标函数和约束都正常再扩展到24小时。这个小习惯能在调试阶段帮你节省大量时间。