
做楼宇微网优化调度这个方向前前后后也折腾了几年从最初的“光伏储能电池”联动削峰填谷到后来把“需求侧虚拟储能系统”的概念真正揉进调度模型用Matlab从零写完整套代码实现算是我近期做得比较满意的一个项目。今天把这套方案的建模思路、核心公式、代码架构和踩坑过程都整理出来分享给正在做智能用电、建筑节能、微电网优化调度的朋友。先说一下这套系统到底能干什么。你面前是一栋办公楼或者商业综合体屋顶铺了光伏配电房里有一组储能电池楼里的中央空调是变频可调的。传统调度思路是“光伏出力多少、电池充放多少、不够了从电网买电”但这里的空调负荷往往被当成一个刚性的、必须满足的需求。虚拟储能的思路完全不一样它把建筑物本身的热惯性当作一种隐形的储能资源利用室内温度的允许波动范围在不影响人体舒适度的前提下提前把冷量或热量“存”进楼体结构里等电价高的时段再释放出来。再加上电池储能两条灵活性资源叠加可调容量更大、调度更灵活购电成本能实打实地降下来。这套实现整体建立在Matlab环境下用YALMIP工具箱建模调用Gurobi求解器求解混合整数线性规划问题。模型规模不大但结构很典型涵盖了光伏、储能电池、电网交互、温控负荷等多类元素非常适合作为楼宇级能量管理系统的算法原型。如果你是电力系统方向的研究生或者正在做建筑能源管理的工程师这篇内容可以直接作为你项目起步的参考框架。1. 项目背景与整体设计思路拆解1.1 为什么楼宇微网需要虚拟储能楼宇微网和传统微网最大的区别在于负荷特性。工业微网的负荷以电机、电炉这类刚性设备为主可调性差但楼宇里面大量负荷是跟环境舒适度相关的比如空调、新风、热水。空调负荷在夏季高峰期往往能占到楼宇总用电的40%到50%而这部分负荷的设定温度是可以上下浮动的这就给了调度系统巨大的操作空间。举个最简单的例子。一栋办公楼下午两点到六点是电价高峰室外35摄氏度室内设定24摄氏度。如果你提前从中午就开始把空调功率调高把室温预冷到22摄氏度那么下午高峰期空调就可以降功率运行让室内温度缓慢回升到26摄氏度。这个过程里人体几乎感知不到明显差异但空调压缩机的电耗在高峰时段被大幅压缩了。楼宇的墙体、地板、家具和室内空气都在参与能量的“存取”这就是虚拟储能最朴素的物理机制。我实测下来中等热惯性的混凝土结构办公楼如果允许室内温度在2到3摄氏度的范围内波动可转移的空调负荷功率大约能达到空调总额定功率的15%到25%。这个数字在资源稀缺的电力需求响应场景里已经是相当可观的灵活性了。关键是它不需要新增任何硬件投资只需要一套能预测、能优化、能下发指令的调度算法这就是我把虚拟储能纳入楼宇微网优化调度的根本原因。1.2 方案选型模型驱动而不是规则驱动做楼宇能量管理业内常见的做法有两种。一种基于规则比如“电价超过某个阈值就切空调”“光伏大于负荷就充电池”实现简单、逻辑透明但很难处理多种设备、多种约束同时作用下的全局最优问题。另一种就是本项目采用的模型预测控制思路即对楼宇热动态、储能电池、光伏出力和电价进行数学建模然后在一个滚动优化窗口内求解最优功率分配。我最终选择模型驱动的方式主要有三个考量。第一楼宇微网的各个组成部分之间存在强耦合。空调功率会影响室温和舒适度室温的波动又决定了空调后续可调空间电池的SOC状态会影响充放电能力电网的购售电功率则受变压器容量约束。这种耦合关系靠几条静态规则根本描述不清楚只有放进同一个优化模型里同时求解才能真正找到全局最优解。第二虚拟储能的核心价值是“时间转移”也就是把能量从低价值时段搬到高价值时段。这是一个跨时段优化问题天然适合用数学规划来描述。你在建模时看到的是设备功率变量但在目标函数层面它真正权衡的是“今天多买一度电存起来”和“明天少买一度电”之间的经济账。第三数学规划模型的可解释性和可扩展性都很好。模型建好之后换一套电价、换一组负荷曲线、加一台新设备只需要改参数或者增加约束不用推翻重来。相比之下规则库写死了之后业务逻辑一变就要跟着大改。2. 楼宇微网与虚拟储能的建模核心2.1 楼宇微网的系统构成与设备建模先定义我这里的标准算例结构。一栋综合办公楼接入配电网屋顶光伏装机200千瓦配电房配置一组100千瓦/200千瓦时的磷酸铁锂电池储能系统楼内有一台变频中央空调额定功率100千瓦其他常规用电设备照明、插座、电梯等作为固定负荷处理。微网内部的功率平衡关系很简单每个时刻光伏出力加上电池放电功率、电网购电功率必须等于空调功率加上固定负荷功率再加上电池充电功率。这里我把电池的充放电分别建模而不是用一个带正负号的功率变量主要是为了后续写约束的时候方便区分效率系数也避免出现“同时充电又放电”这种物理上不合理的解。光伏模型的输入是预测出力曲线这些数据在实际项目中可以来自数值天气预报加光伏功率预测算法。我这里先采用典型日的实测曲线午间最大出力接近120千瓦早晚两端趋近于零。电池模型需要描述SOC的递推关系、充放电功率上限、效率系数以及SOC的运行边界。固定负荷我按工作日的典型办公负荷曲线来设早八点开始爬升十点到下午五点是高峰晚上走低。整条曲线乘以一个基准值然后叠加上随机波动用来模拟实际运行中的不确定性。注意这里所有的单位我都统一换算成千瓦和千瓦时调度周期取24小时、步长1小时这样后续写方程的时候不会乱。2.2 虚拟储能的等效热动态模型整个项目里最关键、也最容易写错的就是空调负荷和室内温度的耦合关系。很多新手一开始把空调当成普通的可调负荷只约束功率范围温度模型完全缺失结果优化出来的空调功率曲线忽高忽低毫无规律房间里要么热得受不了要么冷得离谱。原因很简单空调的“能量吞吐”是作用在建筑热质量上的你必须用热动态方程把这个过程约束住。我采用的是一阶等效热参数模型也叫RC模型。室内空气和建筑围护结构被等效成一个热容节点室外温度通过围护结构热阻与室内进行热交换空调的制冷功率作为注入的冷量直接作用在热容节点上。离散化之后室内温度递推关系写成T_in(t1) T_in(t) Δt / (R·C) × (T_out(t) - T_in(t)) - Δt / C × Q_ac(t)其中T_in和T_out分别是室内外温度R是等效热阻C是等效热容Q_ac是空调的等效制冷功率。这个方程就是虚拟储能的“能量状态转移方程”它把空调功率和建筑热状态严格绑定在一起。Q_ac越大室内温度降得越快相当于给虚拟储能“充电”Q_ac变小甚至停机室内温度在室外热负荷作用下缓慢攀升相当于虚拟储能在“放电”。关键点在于这个模型里的温度T_in不是固定的而是被约束在一个舒适度区间内比如22到26摄氏度。这个区间就是虚拟储能的SOC边界。空调功率的上下限就是虚拟储能的充放电功率边界。有了这个映射关系虚拟储能本质上就变成了一个“储能容量”和“运行功率”都受温度约束的特殊储能单元可以直接放进优化调度模型里和电池储能一起联合优化。2.3 关键热工参数的估计方法等效热阻R和等效热容C这两个参数直接决定虚拟储能模型的准确程度但这两个参数不像电池容量那样印在铭牌上需要通过数据辨识来获取。我最初用的是文献里的典型值混凝土框架结构的办公楼等效热容大致在每平方米100到200千焦每摄氏度换算到整栋楼大约是几百千瓦时每摄氏度等效热阻根据围护结构保温性能取0.5到1.0摄氏度每千瓦。后来发现这种粗估值在优化里的表现不够稳我改用了一个更务实的办法。楼宇自控系统里一般都有历史运行数据你挑一个夏季空调连续运行的夜晚把室外温度、室内温度、空调功率都采集下来然后用最小二乘法去拟合RC模型的参数。具体步骤不复杂把递推方程整理成线性回归形式用过去几天的数据做拟合得到的R和C值比文献值靠谱得多。实测下来我用的那个算例楼宇拟合得到的R大约0.8摄氏度每千瓦C大约3.2千瓦时每摄氏度时间常数RC约2.56小时。这个参数意味着空调停掉之后室内温度在外界热扰动下每小时大约变化0.3到0.5摄氏度。你把这个时间常数记在心里对后面理解虚拟储能的调度边界非常有帮助时间常数太小的轻钢结构建筑虚拟储能的可用容量就很有限调度价值不大反之重型混凝土结构的楼宇就是天然的“储能宝库”。3. 优化调度模型的数学表达与求解策略3.1 目标函数与分时电价机制优化调度的目标是让楼宇一整天的总运行成本最小包括从电网购电的费用、电池储能的运行成本、空调运行对舒适度的潜在影响以及可能存在的需求响应收益。这里先把问题简化成单目标优化经济性优先舒适度通过硬约束保证不越界后续再谈怎么扩展成多目标。以国内一般的商业分时电价为例峰时段10点到15点、18点到21点电价1.2元每千瓦时平时段7点到10点、15点到18点、21点到23点电价0.7元每千瓦时谷时段23点到次日7点电价0.4元每千瓦时。电池储能和虚拟储能的价值来源本质上都是利用这个价差把谷时段的低价电挪到峰时段去用。目标函数可以写成所有时段购电费用的累加再减去光伏富余时上网的售电收益加上电池充放电的退化成本项。后者虽然数值不大但能有效防止优化结果中出现过于频繁的电池充放电切换对延长电池寿命有意义。空调在硬约束下已经保证了舒适度不越界所以目标函数里没有加额外的舒适度惩罚项这样模型更简洁求解速度也更快。3.2 约束条件与决策变量优化模型的决策变量包括每个时段的电网购电功率、电池充放电功率、储能SOC、空调制冷功率和室内温度。所有变量都按小时离散一共24个时段模型的规模在几十到上百个变量的量级属于小规模优化问题。约束条件分成四组。第一组是每个时段的功率平衡约束光伏出力、电池放电、电网购电之和等于空调、固定负荷和电池充电之和。第二组是电池储能约束包括SOC递推关系、SOC上下限、充放电功率上限以及防止同时充放电的逻辑约束。第三组是虚拟储能相关的约束空调功率在0到100千瓦之间室内温度在22到26摄氏度之间温度递推方程与空调功率严格绑定。第四组是电网交互约束购电功率不能超过变压器容量上限光伏上网功率不能超过逆变器容量。这里最值得留意的是虚拟储能并没有被当成一个“黑箱储能”来处理而是通过“空调功率变量温度递推方程温度边界”这一组约束显式建模。这样做的好处是模型的物理意义非常清晰空调功率对温度的影响一目了然后续要改舒适度区间或者增加新风负荷改动起来都很方便。3.3 为什么用混合整数线性规划我最终选用了混合整数线性规划MILP而不是非线性规划或者启发式算法主要看中三点。第一模型的本质是线性的。热动态递推方程、功率平衡约束、SOC递推关系都是线性方程目标函数也是线性的。唯一需要二进制变量的地方是防止电池同时充放电以及处理光伏上网的0-1状态这类问题天然适合用MILP求解。第二MILP求解器能保证全局最优解。粒子群、遗传算法这类启发式算法在楼宇调度场景下经常被使用但它们的解是近似最优每次运行结果还可能不一样。在需要做对比实验、写报告、向决策层展示结果的时候MILP得到的可复现全局最优解更有说服力也更利于后续做灵敏度分析。第三大规模扩展性强。现在的算例只有24个时段、一栋楼规模还比较小。但如果你要把模型扩展到多栋楼宇集群或者把时间步长细化到15分钟MILP模型仍然可以通过商用求解器高效求解。换成启发式算法参数调优成本和计算时间都会显著上升。4. Matlab代码实现与关键环节详解4.1 代码总体架构与模块划分这个项目的Matlab实现我把代码分成了三层。最底层是参数定义模块所有的物理参数、电价表、负荷曲线、光伏出力序列都在这里集中定义方便统一修改。中间层是模型构建模块用YALMIP的符号变量定义决策变量然后逐条添加约束和目标函数。最上层是求解与后处理模块调用求解器求解提取结果并绘制曲线。我强烈建议你在写代码时也保持这个分层结构。很多初学者喜欢把参数、建模、求解全部堆在一个脚本里前期调试确实方便但一旦模型规模变大或者参数要反复调整这种“面条代码”会让你改到怀疑人生。参数集中定义还有一个好处你要做参数灵敏度分析的时候只需要写一个循环去修改参数定义模块输出的结构体模型构建和后处理代码完全不用动。我用一个脚本文件main.m作为程序入口运行时按顺序调用三个函数load_parameters()加载参数build_model(para)构建优化模型并求解plot_results(result)绘制结果。三个函数的输入输出接口是清晰的结构体这样无论是后续扩展到实时滚动优化还是把内核封装成函数给其他程序调用都很方便。4.2 核心约束的YALMIP实现YALMIP是一个Matlab的建模工具箱它最大的价值是让你用接近数学表达式的语法写出优化模型而不用手动构造约束矩阵。以温度递推方程为例在YALMIP里写起来非常直观%% 定义决策变量 T_in sdpvar(1, 24); % 室内温度 Q_ac sdpvar(1, 24); % 空调制冷功率 SOC sdpvar(1, 24); % 电池SOC P_ch sdpvar(1, 24); % 电池充电功率 P_dis sdpvar(1, 24); % 电池放电功率 P_grid sdpvar(1, 24); % 电网购电功率 u_ch binvar(1, 24); % 充电状态标志 u_dis binvar(1, 24); % 放电状态标志 %% 虚拟储能热动态约束 Constraints []; for t 1:23 Constraints [Constraints, ... T_in(t1) T_in(t) dt/(R*C)*(T_out(t)-T_in(t)) - dt/C*Q_ac(t)]; end Constraints [Constraints, 22 T_in 26]; Constraints [Constraints, 0 Q_ac 100];这一段代码就是虚拟储能建模的核心。我第一版实现的时候把温度递推方程全部写成矩阵形式结果光是把系数填入一个大矩阵就费了半天功夫而且一旦参数修改矩阵就要重新推导。换成YALMIP之后建模速度提升了不止一个量级可读性也好很多几乎可以在代码注释里直接对照数学模型。电池储能的建模需要注意防止同时充放电。不用二进制变量的话优化器很可能为了“占住功率平衡但又不实际耗电”让充电和放电同时为正两边效率损失互相抵消结果得到一个物理上没有意义却“成本最优”的解。我加了两个二进制变量通过“功率大于0则状态标志为1”这样的逻辑约束来互斥。%% 电池储能约束 Constraints [Constraints, SOC(1) 0.5]; % 初始SOC for t 2:24 Constraints [Constraints, ... SOC(t) SOC(t-1) dt*eta_ch/P_bat_cap*P_ch(t) - dt/eta_dis/P_bat_cap*P_dis(t)]; end Constraints [Constraints, 0.1 SOC 0.9]; Constraints [Constraints, 0 P_ch 100*u_ch(t)]; Constraints [Constraints, 0 P_dis 100*u_dis(t)]; Constraints [Constraints, u_ch(t) u_dis(t) 1];注意这里充电效率影响的是充电时“存入” SOC 的比例放电效率影响的是“取出”功率对应的SOC消耗。如果你把效率算反了电池的充放电损耗会被错误地算成收益结果就是优化器疯狂充放电看起来在套利实际上在凭空“创造”能量这个问题我一开始也踩过后来是按功率平衡等式两边逐一核对能量单位才发现。4.3 目标函数与求解器配置目标函数的YALMIP实现同样非常简洁。每个时段的购电费用是电网购电功率乘以该时段电价光伏上网收益是上网功率乘以上网电价电池退化成本我按充放电电量乘以一个很小的惩罚系数来处理%% 目标函数 Objective sum(price_buy .* P_grid) - sum(price_sell .* P_sell) ... penalty_bat * sum(P_ch P_dis);求解的时候我优先使用Gurobi求解器。Gurobi在当前这个规模的问题上基本上秒解24个时段加几十个变量求解时间通常在0.1秒以下。如果你没有商业求解器用Matlab自带的intlinprog也可以完成求解速度会慢一些但规模不大问题不大。用YALMIP的话只需要设置一下solver参数就行了%% 求解 ops sdpsettings(solver, gurobi, verbose, 2, mipgap, 0.001); diagnose optimize(Constraints, Objective, ops);求解完成后记得检查diagnose.problem是否为0。如果返回值不是0YALMIP会给出问题类型是“不可行”还是“数值问题”等这对后面排查模型非常有帮助。我每次跑完结果首先就是看这一行确认模型求解状态健康再去做曲线分析要不然拿一个“不可行但被求解器强行松弛”的结果去分析一切都是白搭。4.4 典型日仿真结果与效果对比我用一个夏季典型日做了仿真验证。室外温度曲线最高约34摄氏度出现在下午两点左右光伏出力午间峰值约120千瓦固定负荷最高约150千瓦。电池储能初始SOC设为0.5虚拟储能通过空调功率调节和室内温度浮动来提供灵活性。先看不加虚拟储能的基准场景空调直接按照固定温度控制策略运行室内全天恒定在24摄氏度。此时空调负荷完全跟随室外温度变化下午高峰期整栋楼从电网购电的功率曲线与负荷曲线几乎同形日购电成本大约1200元。然后启用虚拟储能联合优化室内温度允许在22到26摄氏度之间浮动。优化器自动选择在中午光伏出力高峰时段、电价相对较低且室外温度尚可的时候提前把空调功率拉高将室温预冷到22到23摄氏度。到了下午电价峰时段空调功率显著降低室温缓慢爬升到25到26摄氏度高峰购电功率下降了约15%日购电成本下降到980元左右降幅约18%。这里面还有一层隐蔽的收益固定负荷本身也有午间高峰叠加空调的预冷功率听起来像是“双倍高峰”但实际上光伏在午间出力最大这部分多出来的制冷电力主要由光伏承担并不会显著增加购电成本。虚拟储能本质上是把太阳能的廉价电能转化成了建筑冷量等光伏出力衰减后、电价升高的时候再释放出来。这就是虚拟储能和光伏天然互补的逻辑也是整个项目最有价值的地方。5. 常见问题与排查技巧实录5.1 模型无解时的系统排查思路我在调试这个模型的过程中遇到最多的就是求解器返回“不可行”的情况。刚开始很懵明明约束看起来都是合理的为什么解不出来后来总结出一套排查套路按顺序走基本能定位到问题。首先检查功率平衡约束是否可能成立。把光伏出力、固定负荷、电价曲线画在同一张图上在脑海里大致估算一下变压器容量有没有限制得太死电池容量和功率是不是小到连价格套利都做不了空调功率上限是不是无法满足温度约束这些都属于“模型本身太紧”的问题往往把某个容量参数放宽一点模型马上就可行了。其次是检查初始SOC和SOC终值设置。如果模型要求调度结束时SOC必须回到0.5但初始SOC很低、电池功率又受限可能根本没有可行路径在24小时内把它充回来。我在第一版里要求SOC(24)0.5结果模型无解后来改成SOC(24)0.3问题就消失了。第三是检查温度递推方程的初始温度。如果T_in(1)设为26摄氏度同时又要求全天温度不超过24摄氏度那第一时段无论空调功率多大都降不到24度以下模型必死。初始温度必须落在温度边界内并且要与空调额定功率匹配否则没有可行域。5.2 求解慢与二进制变量爆炸问题MILP的求解时间对二进制变量数量非常敏感。我这个模型只用了48个0-1变量Gurobi秒解。但如果你把调度周期从24小时扩展到一周甚至一个月二进制变量数量线性增长求解时间就不是线性增长了而是指数级变差这是整数规划的本质。解决思路有两个方向。一个是从模型层面去掉不必要的整数变量。如果电池同时充放电的问题可以通过设定充电和放电不同时段的电价逻辑来规避也就是购售电价区间天然分开那u_ch和u_dis就可以简化甚至去掉。另一个是给求解器设置合适的MIPGap不需要追求特别高的精度时设一个0.5%的gap求解时间能缩短到原来的十分之一。另外大M值的选择也会影响求解速度。M值取得太大数值稳定性变差求解器容易在分支定界过程中反复试探M值取得太小又会把可行域切掉一部分。我一般会把M设成比实际物理上限稍微大一点比如电池充放电功率上限是100千瓦M就设120而不是拍脑袋设个100000。5.3 参数校核与结果合理性检查优化结果出来之后不要急着去汇报先做几个常识性检查。第一个是能量守恒检查。把光伏发电量加上购电量减去售电量和电池充电量再对比固定负荷和空调负荷的用电量两边误差应该在很小的范围内。如果误差很大八成是效率参数写反了或者某条约束漏写了。第二个是温度曲线合理性检查。把室内温度曲线和室外温度曲线画在一起看如果室内温度出现比较剧烈的“锯齿”波动说明热容参数C可能设置得太小了导致虚拟储能对空调功率的变化过于敏感优化器可以利用这个“瞬态响应”去做一些物理上不现实的套利。这种情况下适当调大C值或者增加温度变化率的约束会让结果更可信。第三个是电池SOC连续性检查。检查SOC曲线是否始终在上下限之内初始和末端SOC是否符合预期是否存在某个时段SOC跳变过大导致效率计算失真。我见过不少代码在递推SOC时忘了把功率乘上时间步长1小时结果SOC单位对不上整个调度结果失真但曲线看起来还挺漂亮。所以在做结果分析之前先跑一个“所有位移为常量”的基例看看SOC变化是否符合物理直觉是很值得的。6. 一个值得尝试的扩展方向多目标调度优化这个项目目前的版本是单目标优化只追求经济成本最小。但实际楼宇管理中用户的舒适度不是可以无限牺牲的单纯给温度设一个上下界有时候不够精细。比如你可以在目标函数里加入一个舒适度偏移惩罚项室内温度每偏离设定温度1摄氏度就产生一个相当于高电价电费的惩罚成本。这样优化器会在“省钱”和“保舒适”之间自动做权衡而不是每次都把温度往边界上顶。多目标处理上我建议先别急着用NSGA-II这类多目标遗传算法因为楼宇调度模型的决策变量多、约束复杂启发式算法的收敛性和解的稳定性都很难保证。更务实的做法是采用加权和法或者epsilon约束法把舒适度作为一个目标函数把经济性作为另一个目标函数然后取不同的权重分别求解MILP得到帕累托前沿。这样既保留了MILP的全局最优性和求解速度又能给决策者提供一套“多花多少钱能提升多少舒适度”的决策参考。我还尝试过把碳排放作为一个目标加进去在电价之外再设置一个碳排放因子相当于给每度电加一个“碳成本”。这样做的好处是优化器会自动倾向于在光伏出力充足时段多用绿电、少从电网买火电即便电价差异不大碳排放也能降下来。在当前不少企业有绿电考核需求的环境下这个扩展方向的实际落地价值很高。如果你想在这个项目基础上继续深入多目标调度优化是一个非常自然且应用前景明确的切入点。最后再分享一个个人体会。做这套系统之前我一直觉得“负荷”在电力系统里就是个被动消耗的角色做调度就是把发电侧的资源调配好去满足它。但虚拟储能的思路把整个逻辑反过来楼宇本身就是一个巨大的、未被充分调用的储能资源只要你愿意给它一点温度的“宽容度”它就能回报给你实实在在的电费下降和电网压力缓解。我实际跑完这套仿真之后最大的感触是优化调度算法的价值不在于模型多复杂、公式多漂亮而在于能不能把一个看似不可调动的资源变成一个可量化、可优化、可调度的“虚拟设备”。这套Matlab实现做到了这一点也希望你看完这篇文章之后能顺着这个思路在自己的项目里做出更好的效果。