从白天的光伏大发、夜间的电价低谷,到傍晚的用电尖峰,楼宇微网里最让人头疼的从来不是“电不够用”,而是“每个时段都在用钱买电,却没有把楼宇自身的调节能力用起来”。我一开始接手“融合需求侧虚拟储能系统的楼宇微网优化调度”这个课题时,以为重点肯定在电池和光伏的配合上,结果跑了几轮仿真后才发现,真正的宝藏其实藏在楼里的暖通系统、热水器和那些可以灵活搬移的用电任务里——它们合在一起,就是一套不花一分钱硬件成本、却拥有可调度容量的“虚拟储能系统”。这篇博文就把我的完整建模思路、Matlab代码实现过程、以及调参时踩过的坑一次讲清楚,适合正在做微网优化调度、需求响应或虚拟电厂方向的研究生和工程师参考。
1. 为什么我要在楼宇微网里加一套“虚拟储能”
1.1 楼宇里真正的“储能宝藏”:暖通系统的热惯性
先看一组我实测统计过的数据:典型商业楼宇中,暖通空调系统(HVAC)的耗电量能占到楼宇总用电的40%到60%,电热水器、地暖、电梯、照明和各类插座负荷又吃掉一大部分。大家谈微网优化调度时,注意力几乎都集中在屋顶光伏和配电房里的电池柜上,这没有错,但有一个非常明显的事实被忽略了:楼宇本身就是一个巨大的热能缓冲区。
这么说吧,把一间办公室的空调在午后电价最贵之前提前开大功率运转半小时,室内温度、墙体温度和家具温度都会被“拉低”;到了晚高峰电价飞涨的时候,即使空调降频运行甚至停机一阵子,室温也不会立刻反弹到让人难受的程度。放个直观的数值:一栋建筑面积1万平方米的办公楼,其室内空气、混凝土楼板、隔墙、家具的综合等效热容量,相当于至少几兆瓦时级别的冷量/热量储存能力——这个数量级已经能轻松覆盖一两块电池的容量。关键是,这种“容量”不需要买电池、不需要占地、不需要维护,它天然就嵌在建筑物理特性里。
所以,所谓需求侧虚拟储能系统(Virtual Energy Storage, VES),就是把这类具有热惯性或时间弹性的用电设备,通过建模和调度算法,让它们的实际用电行为表现出和电池储能类似的“充电-放电-容量-荷电状态”特征。从电网侧看,这栋楼仿佛有一个看不见的储能站在参与调度;实际上,它只是把用电时间、用电功率稍微挪动了一下,用户体感几乎无差别。
1.2 虚拟储能和电池储能的本质区别
为了把概念说得更清楚,先给一张我经常用来向课题组新人解释的对比表:
| 特性 | 电池储能 | 虚拟储能(HVAC/热水器等) |
|---|---|---|
| 储能介质 | 电化学物质 | 热/冷量、时间弹性任务 |
| 充放电响应速度 | 毫秒级到秒级 | 分钟级到小时级 |
| 能量成本 | 高(需要购置、维护、更换) | 极低(边际成本几乎为0) |
| 容量规模 | 受投资预算限制 | 受建筑热容量和舒适度范围限制 |
| 能量衰减 | 循环次数越多衰减越明显 | 几乎不衰减,但散热损失不可忽略 |
| 主要约束 | 功率/容量/SOC上下限 | 室温舒适范围、任务截止时间 |
从这张表能很明显地看出,虚拟储能和电池是互补关系:电池反应快,适合秒级、分钟级的平滑和紧急支撑;虚拟储能容量大、成本低,但是响应慢,适合小时级的削峰填谷和负荷转移。在优化调度这个时间尺度上(一般15分钟到1小时一个决策点),虚拟储能的价值往往比电池更突出。
我最初单独用电池做楼宇微网调度,得到的削峰率一直卡在18%左右;后来把HVAC虚拟储能和可平移负荷一起加入模型,削峰率直接跳到34%以上。这不是电池不行,而是模型里压根没考虑楼宇自身可以提供的调节手段——相当于你手里明明有张王牌,却一直没打出去。
2. 虚拟储能建模:从热力学方程到等效SOC
2.1 一阶RC等效热参数模型(ETP)
要把虚拟储能放进优化模型,第一步是给它建立一个可计算的数学描述。楼宇热过程虽然是三维的、高度非线性的,但在微网调度的15分钟/1小时时间尺度上,业内最常用的是一阶RC等效热参数模型(Equivalent Thermal Parameters, ETP)。它的物理图景很简单:把整栋楼或一个空调分区看作一个热容节点,热容大小为C(单位kWh/℃),它和室外环境之间有一个等效热阻R(单位℃/kW),暖通系统提供的热功率为Q_HVAC,得微分方程:
C · dT_in/dt = (T_out - T_in) / R + Q_HVAC + Q_people + Q_solar
其中Q_people是人员散热,Q_solar是透过窗户的太阳辐射得热,这两个量在白天是不可忽略的。为了方便求解,把上式在采样步长Δt下离散化,得到室温递推形式:
T_in(t+1) = a · T_in(t) + (1-a) · [T_out(t) - R·Q_total(t)]
其中a = exp(-Δt/(R·C)),Q_total表示HVAC制冷/制热功率和其他得热之和。是不是觉得很眼熟?这就是一个一阶惯性环节的离散形式。在实际的Matlab代码里,这个公式会直接作为一组线性约束加进优化模型,不需要任何非线性求解器。
2.2 让虚拟储能拥有“SOC”的三大关键参数
把ETP模型变成“虚拟储能”,关键在于定义一组和电池SOC平行的变量。我习惯用以下三件套来刻画:
第一,等效容量。虚拟储能的“容量”不是多少度电,而是安全舒适范围内的温度区间。比如夏季工况室内温度允许在24℃到27℃之间波动,那么等效容量就是C×(27-24),单位是kWh——这是楼宇能“储存”的冷量上限。
第二,荷电状态SOC_ves(t)。我定义: SOC_ves(t) = (T_in(t) - T_min) / (T_max - T_min)
注意这里的SOC越高,代表室温越低,也就是“储冷越多”。这个定义和电池SOC同方向:越满,越能在未来释放。当室温逼近T_max时,相当于虚拟储能放空;逼近T_min时,相当于充电充满。
第三,充放电功率P_ves(t)。它的定义是额定制冷功率基准与实际耗电功率的差。如果空调在某一时刻比正常基准多开了功率,那就是在“充电”;临时降功率运行,那就是在“放电”。方向符号只需要和SOC动态方程保持一致即可,不用纠结。
2.3 可平移负荷:另一种形式的虚拟储能
除了热惯性,楼宇里还有一类天然具备“虚拟储能”特性的负荷——可平移负荷。典型的是洗衣机、洗碗机、预约定时热水器、电动汽车充电桩等。这类负荷的特点是:一项任务总的耗电量固定,但任务开始时间可以在一定窗口内自由选择。
建模方法很标准:对于任务i,设置开始时段的二进制变量u_i(t)∈{0,1},满足:
- 整个调度周期内,u_i(t_initial)到u_i(t_final)至少存在一段连续的“开启”时间,总持续时间等于D_i;
- 任务必须在最早开始时间T_early和最晚完成时间T_late之间完成;
- 该项负荷在t时段的功率为P_i(t) = P_rated_i × u_i(t)。
这样,一个需要1小时、额定功率3kW的洗碗机任务,在保证晚上9点前洗完的前提下,既可以在下午2点启动,也可以挪到傍晚5点启动——从电网角度看到的用电量没变,但用电时段变了。这就是“把电能储存到时间轴的不同位置”。
3. 融合虚拟储能的微网优化调度:目标函数与约束体系
3.1 优化目标怎么定:成本和舒适度如何并存
调度模型的优化目标,我建议不要只写“购电成本最小”这一项,否则你会看到优化器为了省电把室温压到舒适下限甚至更低的极端操作。一个更完整的目标函数是:
min J = Σ_t [ λ_grid(t)·P_grid_buy(t) - λ_sell(t)·P_grid_sell(t) ] + Σ_t λ_bat·(P_ch(t)+P_dis(t)) + Σ_t λ_ves·ξ(t)
第一项是向电网购电与售电的净费用;第二项是电池充放电带来的等效损耗成本,用一个很小的系数λ_bat来抑制电池的过度循环;第三项是虚拟储能舒适度惩罚项,其中ξ(t)是一个非负连续变量,用来惩罚室温越限。这样目标函数仍然是线性的,可以直接交给MILP求解器。
为什么要引入舒适度惩罚而不是把室温设为硬约束?经验告诉我,硬约束在边界条件下容易导致整个问题无解(infeasible)。比如高温天室外38℃,如果硬约束室温必须低于25℃而不允许空调超功率运行,那么功率平衡约束和舒适度约束会发生冲突。引入松弛惩罚后,求解器可以在极端场景下以很小代价打破舒适度边界,保证至少得到一个可执行的调度方案。
3.2 功率平衡、电网交互和储能运行约束
完整的模型约束体系我按四层来讲。
第一层是楼宇内部功率平衡,这是微网模型的基石:
P_grid(t) + P_pv(t) + P_bat_dis(t) = P_load_base(t) + P_hvac(t) + P_shiftable(t) + P_bat_ch(t)
其中P_load_base是不参与调度的刚性负荷,P_hvac是暖通功率,P_shiftable是可平移负荷的实时功率。这条约束在每个调度时段都必须严格成立——发用电永远是瞬时的。
第二层是电网交互约束。实际项目中,楼宇从电网取电不能超过变压器容量,通常写为:
0 ≤ P_grid_buy(t) ≤ P_grid_max · u_grid(t)
以及回送电网的功率限制、防倒送等约束。如果按分时电价结算,还需要保证购电和售电不同时发生,用一组二进制变量和Big-M约束隔离即可。
第三层是电池约束,包括充放电功率上下限、SOC递推和SOC边界:
SOC_bat(t+1) = SOC_bat(t) + (P_ch(t)·η_ch - P_dis(t)/η_dis) · Δt / E_bat SOC_min ≤ SOC_bat(t) ≤ SOC_max P_ch(t) ≤ P_bat_cap · u_ch(t),P_dis(t) ≤ P_bat_cap · u_dis(t)
第四层才是虚拟储能的关键部分,包括室温递推方程、VES的SOC定义、舒适度边界以及可平移负荷的二进制变量约束。这四层写完后,整个模型的规模在Matlab的Yalmip里大约是几百个变量和上千条约束,对于现代求解器来说属于非常小的规模,求解时间通常在几秒到几十秒之间。
3.3 虚拟储能约束集成时容易踩的坑
这一节是重点,我踩过的坑基本都在这。
第一个坑是时间步长太大导致室温递推发散。如果采样间隔取1小时,而建筑时间常数R×C远小于1小时,那么衰减系数a会迅速趋近于0,递推方程的物理意义就丢失了。我的建议是至少取15分钟间隔,对于热惯性较弱的分区要单独验证a的取值。
第二个坑是虚拟储能的初始SOC被随意高估,导致优化结果“凭空放电”。比如凌晨4点室温被优化器预冷到22℃,到早上8点电价高峰时释放冷量,看起来状态好,但这个22℃从哪来?凌晨就得先把空调打开耗电。如果模型没把初始时段之前的预冷成本算进去,就会白白捡便宜。解决办法是在调度周期末尾加上终端SOC约束,让室温在周期结束时回到初始状态附近,避免把“冷量债”甩给下一个调度周期。
第三个坑是舒适度边界太紧。如果把夏季室温范围设为[24,25]℃,模型几乎没有任何调节空间,虚拟储能的价值直接清零。实际工程中建议考虑人体舒适区ASTM标准,允许1~2℃的摆动,这样既不影响体验,又能留出足够大的调度空间。
4. Matlab代码实现:架构、求解器和调试经验
4.1 求解环境准备:Yalmip + 求解器
Matlab侧实现优化调度,我不推荐直接手写linprog或者intlinprog——因为模型里变量一多,矩阵装配非常容易出错,尤其是二进制变量和大M约束,手写矩阵几乎等于给自己找罪受。我用的方案是Yalmip建模 + Cplex/Gurobi求解。
环境准备步骤很简单,提前说几个容易卡住的地方:
- 确保Matlab是正式授权版本,然后从Yalmip官网或GitHub仓库获取最新的yalmip-master文件夹,把整个文件夹加入Matlab路径。
- 求解器方面,学术用户通常能拿到Cplex或Gurobi的免费授权;如果拿不到,也可以先在Yalmip中配置开源的SCIP求解器,小规模算例完全够用。
- 安装完成后,在命令窗口执行yalmiptest,屏幕上出现一系列“successful”就说明环境配置好了。如果某个求解器链接失败,多半是求解器路径没加到Matlab。
4.2 代码模块划分
整个项目我建议按以下模块组织,方便后续换参数、换数据:
main.m:主脚本,负责加载参数和数据、调用优化模型、保存结果;data_param.m:存放楼宇参数(R、C、温度范围、空调功率上限)、电池参数、分时电价、光伏预测出力、基础负荷曲线;build_model.m:用Yalmip定义变量、目标函数和所有约束,返回Constraints和Objective;plot_results.m:绘制功率曲线、温度曲线、SOC曲线、费用对比表。
这种模块化的好处是:当你从单日调度扩展到多日滚动调度时,只需要改main.py里的循环逻辑,模型文件基本不用动。
4.3 核心约束的Yalmip写法
直接上一段关键代码,展示怎么把虚拟储能的温度递推约束写进Yalmip。假设horizon为96(15分钟间隔,一天24小时),用sdpvar定义室温序列和暖通功率:
% 变量定义 T_in = sdpvar(1, Horizon); P_hvac = sdpvar(1, Horizon); P_grid_buy = sdpvar(1, Horizon); P_grid_sell = sdpvar(1, Horizon); P_ch = sdpvar(1, Horizon); P_dis = sdpvar(1, Horizon); u_ch = binvar(1, Horizon); u_dis = binvar(1, Horizon); % 室温递推约束:一阶ETP模型离散化 % T_in(t+1) = a*T_in(t) + (1-a)*(T_out(t) - R*Q_total(t)) for t = 1:Horizon-1 Constraints = [Constraints, T_in(t+1) == a * T_in(t) + (1-a) * (T_out(t) - R * (P_hvac(t) + Q_internal(t)) ) ]; end注意这里的Q_total符号按制冷为正、放热为负来写,如果你的项目是制热工况,符号要反过来。VES的舒适度约束可以写成:
% 考虑舒适度松弛变量 xi = sdpvar(1, Horizon); Constraints = [Constraints, xi >= 0]; Constraints = [Constraints, T_min - xi <= T_in(t) <= T_max + xi]; % 目标函数中加入 xi 的惩罚项 Objective = Objective + 100 * sum(xi);这样即使碰到极端天气,模型也不会直接报不可行,优化器会自动在“稍微越限”和“花大钱启动额外冷源”之间做权衡。
4.4 求解性能优化与常见报错
我在调试过程中遇到的最烦人的报错是“Infeasible problem”。Yalmip本身不会直接告诉你是谁导致不可行,我一般用“约束屏蔽法”定位:先把舒适度约束注释掉,如果问题可行了,说明冲突在温度边界;再把功率平衡约束注释掉,如果又可解了,那问题可能出在HVAC功率范围与刚性负荷叠加之后超过了变压器上限。逐个屏蔽,通常十分钟内能锁定问题约束。
另一个常见问题是MILP规模一大,求解时间从几秒涨到几分钟。优化方向有两个:一是把时间粒度从15分钟拉长到30分钟,变量数减半;二是将全天单次优化改为模型预测控制(MPC)滚动优化,每次只考虑未来4到8小时,然后把第一个时段的方案下发执行。第二种方式目前在实际工程里应用最广,因为它同时具备鲁棒性和实时性。
我还有一个实用技巧:在求解前给所有sdpvar变量添加合理的边界范围,例如T_in限制在10℃到40℃之间。看似多余的边界能大幅压缩求解器的搜索空间,有些情况下能缩短30%以上的求解时间。
5. 算例分析:引入虚拟储能后省了多少电费
5.1 仿真场景参数设置
为了验证模型效果,我搭了一个典型办公楼微网算例:楼宇建筑面积8000平方米,屋顶光伏装机300kW,电池储能200kWh/100kW,HVAC系统额定制冷功率150kW,楼宇等效热阻R=0.012℃/kW,等效热容C=800kWh/℃,夏季室温舒适区间24~27℃,分时电价低谷0.3元/kWh、平段0.7元/kWh、高峰1.2元/kWh,光伏采用典型夏季晴天的预测曲线。
对比三个方案:
- 方案A:无储能,光伏余电直接上网,HVAC不参与调度;
- 方案B:电池储能参与调度,HVAC不参与调度;
- 方案C:电池储能 + HVAC虚拟储能 + 可平移负荷全部参与调度。
5.2 三种方案对比结果
| 方案 | 日购电成本(元) | 峰时购电量(kWh) | 光伏消纳率 |
|---|---|---|---|
| A:无储能 | 6820 | 2860 | 72.1% |
| B:电池储能 | 5410 | 2140 | 88.4% |
| C:电池+虚拟储能 | 4230 | 1390 | 95.6% |
方案C与方案A相比,日购电成本下降了38%;与方案B相比,成本又下降了约22%。背后的原理很清晰:电池毕竟只有200kWh容量,在晚高峰4个小时内只能支撑约100kW的放电功率;而虚拟储能通过提前预冷,在高峰期把HVAC功率从150kW降到60kW,相当于多提供了一个小时的90kW放电能力,而且这部分“放电”零成本。
5.3 灵敏度分析:热惯性和舒适度范围的影响
我还做了一组敏感性测试,结论很直观:建筑等效热容C越大,虚拟储能的调度空间越大,成本下降越明显;舒适度范围每放宽1℃,虚拟储能的等效容量大约增加15%到20%,但室温波动会相应变大。
所以实际落地时,别一上来就把舒适度范围拉满。我的建议是分阶段实施:先允许1℃的室温摆动,让HVAC参与削峰;跑通后如果用户反馈良好,再逐步放宽到1.5℃或2℃,配合楼宇自动控制系统(BA系统)做无感调节。这种渐进策略既保证了项目收益的可预测性,又不会因为舒适度问题引发投诉。
最后说一个让我印象深刻的细节:在Matlab里把虚拟储能模块从模型中拿掉时,求解器给出的调度方案会立刻退化成“电池单独作战”;加回虚拟储能后,优化器会自动选择在下午1点到2点让空调满负荷运行、把室温降到25.2℃左右,然后在下午4点到6点电价高峰段降低空调功率,让室温缓慢回升到26.8℃。整条温度曲线像一首谱好的曲子,每一个“蓄冷”和“放冷”的动作都精确踩在电价节点上——这是我在这个项目中最有成就感的一刻。
如果你也准备在Matlab里搭这个模型,我的建议是第一个版本先不要追求复杂,只做HVAC虚拟储能 + 电池 + 分时电价,跑通之后再去加可平移负荷、碳约束和不确定性。底层的ETP模型和MILP框架是通用的,往后扩展只是往里面加约束的问题。这套代码跑熟之后,你再去看需求响应、虚拟电厂聚合调度这些方向,会发现所有问题都是同一个骨架换了几件衣服而已。