
楼宇调度这个方向最近我把一套“日前日内非滚动日内滚动实时修正”的多时间尺度调度框架完整跑了下来用Matlab实现实测下来很有参考价值。这套东西说白了就是给智慧楼宇装一个“会看明天、盯当下、随时纠偏”的能量大脑把需求侧响应揉进四个时间尺度里让空调、储能、光伏、充电桩这些资源在不同时间粒度上各司其职。你可能会问为什么要分这么多层直接用最优算法一把梭不行吗做工程的同学应该都懂真实的楼宇运行有太多“计划赶不上变化”——光伏预测会偏、空调负荷会被天气打脸、电价信号还时不时给你惊喜。一套调度策略如果只看24小时那今天下午突然来的一朵云就能让实际运行全线崩盘如果只看当下又没法提前把储能的电备好、把空调预冷做好。所以多时间尺度不是炫技是楼宇能量管理的刚需。这篇文章我会把这套框架从场景拆解、设备建模、四层策略的Matlab实现到求解器配置和踩坑记录都过一遍。适合做园区能源管理、楼宇自动化、虚拟电厂聚合或者课题方向刚好卡在需求侧响应和优化调度上的同学。1. 先拆明白多时间尺度调度到底在解决什么问题1.1 一个楼宇调度员的一天设想你是一个园区能源管理工程师每天早上八点要跟电网做一次“约会”——报告今天的用电计划。你的楼宇里有空调、储能、屋顶光伏、几个充电桩还有一群到点就开电脑的上班族。为了少花钱你昨晚用光伏预测和负荷预测做了一份24小时的储能充放电计划中午光伏多储能充电下午电价贵储能放电扛峰值。结果呢早上十点天气预报说要下雨但天还晴着光伏出力比预测高了20%下午两点会议室临时加开三个小时的评审会空调负荷直接起飞下午四点储能SOC比计划低了15%。你要是还抱着那份“昨日计划”不放妥妥的翻车。这就是为什么调度不能只做一次。真实运行的楼宇本质上是一个“预测永远不准、但还得一直优化”的动态系统。所以业界把调度拆成多个时间尺度各管一段互相兜底。这个思路跟开长途车很像出发前规划全程路线途中根据实时路况调整快到路口再判断具体走哪条车道——每层盯的颗粒度不一样做的事情也不一样。1.2 四个尺度各管一段先看一张表把这四个层级拉齐调度层级时间尺度核心任务典型粒度日前调度未来24小时定基准储能充放电计划、空调预冷计划、购电计划1小时日内非滚动未来数小时修正在日前计划基础上根据最新预测重新优化15分钟日内滚动未来短时域逼近滚动优化每一步只执行当前时段5-15分钟实时修正分钟级兜底把执行偏差拉回来1-5分钟这里有两个容易混的概念日内“非滚动”和“滚动”。我的理解是——非滚动优化每次求解固定覆盖未来一段时域比如4小时解完了执行这4小时到点了再重新求解4小时窗口是一整段一整段往前挪的有点像“分段规划”。而滚动优化更像模型预测控制MPC每次只优化未来一个窗口比如15步但只执行第一步到了下个时刻窗口整体平移一步再重新求解每时每刻都在“刷新计划”。打个比方非滚动是“你看了四集连续剧再决定怎么追”滚动是“每看完一集就根据下一集的预告重新调整追剧计划”。滚动更贴近真实运行但计算量也更大这个后面会详细讲。实时修正是最容易被忽略的一层。日内滚动即使每5分钟做一次用的还是“预测值”而实际负荷、光伏和设备的真实状态总会有偏差。实时修正不搞复杂优化通常用规则判断或快速调节比如储能SOC偏离计划超过3%就调整充电功率电压越限就切掉部分可中断负荷。它不追求“最优”只追求“别出事”。1.3 需求侧响应在框架里的角色需求侧响应DR本质就是让楼宇从“被动用电”变成“主动参与调节”。在楼宇场景里最常用的是价格型DR电网发布分时电价或实时电价楼宇的优化器看到电价高就把负荷挪开、把储能放出来电价低就提前充电、预冷用价格信号引导用电行为。另外还有激励型DR比如电网或聚合商发来“今天下午削减50kW”的信号楼宇响应了拿补偿。激励型DR必须建约束比如削减次数上限、单次削减时长、两次削减之间的最小间隔这些在日前计划和日内修正里面都要体现不然策略就是“理论能跑现场不敢用”。为什么要把DR放进多时间尺度框架因为DR不是一个“瞬时动作”它是一串“提前准备、到点执行、事后恢复”的联动。你要在日前就知道明天有几个小时电价会很高提前把空调预冷、给储能充满日内再根据最新信号微调真正到电价高峰那半小时才谈得上“响应”。没有多时间尺度DR就是无源之水。2. 楼宇侧资源建模调度之前先把“家底”盘清楚2.1 储能系统储能几乎永远是楼宇调度模型里最核心的资源。最简单的模型是能量状态SOC递推SOC(t1) SOC(t) [η_ch * P_ch(t) * Δt - P_dis(t) * Δt / η_dis] / E其中P_ch、P_dis是充放电功率η是效率E是容量Δt是调度步长。约束必须包括充放电功率上限0 ≤ P_ch ≤ P_ch_max0 ≤ P_dis ≤ P_dis_maxSOC上下限SOC_min ≤ SOC ≤ SOC_max充放电不能同时进行引入二进制变量P_ch * P_dis 0或用一个0/1变量加两组大M约束这里有个很容易踩的坑很多同学图省事不加“不能同时充放”的约束结果优化结果出来又充电又放电目标函数倒是挺好看但真设备根本跑不了。所以储能建模一定要用混合整数线性规划MILP而不是纯线性规划LP。Matlab代码片段% 储能充放电不能同时的约束YALMIP E_init 0.5*E_cap; % 初始SOC delta binvar(T,1); % 0放电,1充电 % 充电时放电为0 P_dis P_dis_max*(1-delta); % 放电时充电为0 P_ch P_ch_max*delta; % SOC递推 E(1) E_init; for t1:T-1 E(t1) E(t) (P_ch(t)*eta_ch - P_dis(t)/eta_dis)*dt; end2.2 空调负荷空调是楼宇里最大的可调负荷也是需求侧响应的主力。我常用一阶等效热参数模型ETP来近似房间热动态T_in(t1) T_in(t) (T_out(t) - T_in(t)) / (R*C) * dt Q_hvac(t) / C * dt其中T_in是室内温度T_out是室外温度R是等效热阻C是等效热容Q_hvac是空调制冷功率。室温不能随便放飞要约束在舒适范围内比如24-26度。而且这个区间就是天然的需求侧响应资源电价贵的时候允许室温往26度靠空调少出力电价便宜的时候提前把室温预冷到24度靠建筑热惯性“扛”过电价高峰。我把“预冷”写进模型的方式是在日前优化里加一个“设定温度偏移量”的决策变量让优化器自行决定在哪些时段预冷、在哪些时段放松约束。这个变量一般是连续量线性化不难。2.3 光伏与常规负荷光伏出力用预测曲线一般按1小时或15分钟粒度生成。如果你手里没有真实历史数据可以先按典型日曲线加扰动来构造重点是验证调度策略不用在数据精度上纠结。常规负荷照明、插座、电梯在楼宇里占比不小但基本不可调所以在模型里当成“刚性负荷”处理直接加在功率平衡方程里就行。可调负荷比如充电桩、部分暖通设备单独建。关于预测不确定性的处理这里我说一下套路日前调度用“期望预测”考虑一定的备用容量比如留出10%的储能裕度日内滚动里预测误差会被实时修正兜底。如果课题要求更高可以上鲁棒优化或随机优化但说实话工程上先把确定性框架跑通再谈随机性性价比更高。2.4 可调负荷与EV充电桩EV充电桩是楼宇需求侧响应的另一个重点。充电桩负荷可以“转移”——上午充还是下午充、1C还是0.5C都是可调空间。在模型里EV负荷通常建为一个“可平移负荷”或“可调功率负荷”。可平移负荷的典型约束是总充电量固定一天要充够某电量但充电流量分布在哪个时段由优化器决定。用公式说就是 sum(P_ev(t)*dt) E_ev_need同时每时段功率不超上限。DR削减约束我单独说。激励型DR不是你想响应就响应电网侧有硬性要求单次最大削减时长一天内最大削减次数两次削减之间的最小间隔削减指令响应延迟这些都得建成约束不然优化器可能为了让成本最低天天“拉闸”现场早炸了。我之前跑仿真的时候吃过这个亏结果出来削减次数太多被项目评审一句话问住了——“你这策略敢投运吗”从那以后DR约束我都是老老实实建全。3. 四层调度策略的Matlab实现思路3.1 总体框架与代码结构我用Matlab YALMIP写了一套示例工程代码目录大概这样smart_building_scheduling/ ├── data/ % 负荷、光伏、电价、温度等输入数据 │ ├── load_profile.csv │ ├── pv_profile.csv │ └── price_profile.csv ├── model/ % 设备模型 │ ├── battery_model.m │ ├── hvac_model.m │ └── ev_model.m ├── optimizer/ % 四层调度优化 │ ├── day_ahead_scheduler.m │ ├── intraday_non_rolling.m │ ├── intraday_rolling.m │ └── real_time_correction.m ├── utils/ % 数据读取、画图、结果统计 │ ├── load_data.m │ └── plot_results.m └── run_main.m % 主脚本如果只是复现一个Demo可以不用这么规整函数化就行。但如果是做交付项目按这个结构拆后面改模型、换数据、出图表都方便得多。主脚本的逻辑大概是% 读取数据 [load_data, pv_data, price_data] load_data(); % 日前调度 da_result day_ahead_scheduler(load_data, pv_data, price_data); % 日内非滚动修正 idn_result intraday_non_rolling(load_data, pv_data, price_data, da_result); % 日内滚动优化 idr_result intraday_rolling(load_data, pv_data, price_data, idn_result); % 实时修正 rt_result real_time_correction(load_data, pv_data, price_data, idr_result); % 绘图 plot_results(da_result, idr_result, rt_result);3.2 日前调度基准计划怎么定日前调度是整个框架的“总指挥”目标一般是最小化全天运行成本目标函数min 购电费用 储能折旧费用 DR费用购电费用 sum(电价(t) * 购电功率(t) * dt)储能折旧 sum(充放电功率 * 折算系数)防止SOC乱飞DR费用 参与激励型DR的补偿费用约束条件主要是四类功率平衡、储能SOC与充放电、室温舒适区间、DR动作限制。功率平衡是核心约束光伏 购电 储能放电 削减负荷 刚性负荷 空调 EV 储能充电。YALMIP写出来大致是P_buy sdpvar(T,1); % 购电功率 P_ch sdpvar(T,1); P_dis sdpvar(T,1); SOC sdpvar(T,1); delta_dr binvar(T,1); % DR动作状态 % 目标函数 obj sum(price.*P_buy*dt) sum(beta*(P_chP_dis)*dt) sum(price_dr.*delta_dr); % 约束 Constraints []; for t1:T Constraints [Constraints, P_pv(t)P_buy(t)P_dis(t)P_dr(t) P_load(t)P_hvac(t)P_ch(t)]; Constraints [Constraints, 0 P_ch(t) P_ch_max*delta_ch(t)]; % ... 其余约束 end ops sdpsettings(solver,cplex); optimize(Constraints, obj, ops);这里有个经验储能折旧成本一定别省。如果不加折旧折算优化器会为了让购电费用最低拼命充放SOC一天来回十几次电池几个月就报废算出来的“最优成本”毫无工程意义。3.3 日内非滚动调度在日前计划上做修正日内非滚动解决的场景是早上10点发现光伏预测变了但未来4小时的基准计划还在执行。此时不能等到明天重新做日前计划得“局部重算”。实现思路是把未来4小时16个15分钟点作为优化区间重新求解一个和日前类似的小规模优化但目标函数里加一项“与日前计划的偏差惩罚”让结果不要偏离基准太远同时留出足够的调整余地。为什么要加偏差惩罚因为没有惩罚的话日内重算会完全推翻日前计划储能可能从“下午放电”变成“下午充电”虽然单看这4小时是最优的但全天来看可能打乱后续安排。加上偏差惩罚之后日内就是“小修小补”而不是“推倒重来”。其实这正是“非滚动”和“滚动”的重要区别非滚动是分段修正目标和上层紧密绑定。3.4 日内滚动调度MPC思想的工程落地滚动优化的核心循环是设置一个优化窗口比如未来1小时15分钟粒度就是4步求解这个窗口的优化问题然后只执行第一步到了下个时刻窗口整体往前推一步。核心代码大概是for k 1:K_total % 从k开始取窗口内的预测数据 idx k:kN_horizon-1; % 构建并求解未来N_horizon步的优化问题 [u_opt, x_opt] solve_window_problem(idx, x_current); % 只执行第一步 u_exec u_opt(:,1); % 更新系统状态用真实或模拟数据 x_current update_state(x_current, u_exec, actual_data(k)); % 记录结果 results(:,k) [u_exec; x_current]; end这里最容易出错的是“状态初值传递”。每轮滚动窗口求解储能的初始SOC、室温的初始值必须是上个时刻执行后的真实状态而不是上一轮窗口优化算出来的“预测状态”。很多同学做MPC时结果持续越界、振荡十有八九是这一步搞错了。滚动优化求解时最好不要用重型求解器逐次启动太慢。小规模窗口问题我用YALMIP自带求解器或者直接用intlinprog就够了。后面第4节详细说计算效率。3.5 实时修正简单规则比复杂优化更可靠实时修正层我建议用规则而不是再套一层优化。原因很简单这一层要的是毫秒级反应优化求解在延迟上不占优势而且规则逻辑的透明度高、便于现场调试。我自己常用的修正规则储能SOC偏差超过±3% → 调整当前时段充放电功率做成一个简单的比例控制。实时负荷超过预测10%以上 → 先调空调设定温度向上偏移0.5度再看EV充电桩是否需要延时。房间温度越限 → 启动预冷或切回舒适模式。光伏突然大幅下跌 → 优先用储能顶上而不是动空调舒适度优先。if SOC_now SOC_plan - 0.03 P_dis_exec P_dis_exec * 0.8; % 降低放电功率 end if P_load_now 1.1 * P_load_forecast T_set min(T_set 0.5, T_max); % 上调设定温度 end这一层的参数比如3%阈值、10%超限要基于实际运行数据调没有标准答案。我一般先把阈值放到宽松区间跑仿真再逐步收紧观察成本和舒适度的变化找到一个自己项目最合适的点。4. 求解器配置与计算效率的实战经验4.1 用YALMIP还是手写linprog很多人纠结要不要用YALMIP。我的建议是直接上YALMIP。原因有三。第一建模速度快尤其是指标、约束拼接、sum求和这些YALMIP比手写矩阵快太多第二换求解器非常方便只需要改sdpsettings里的solver参数不需要动模型代码第三滚动优化里每次求解的问题结构比较相似用YALMIP方便做热启动。当然YALMIP也有痛点如果你要嵌入到大型程序里反复调用建模和求解的开销比较大。这时候可以退一步把优化模型手动写成标准形式直接调intlinprog。但日常工作里90%的场景YALMIP 商业求解器就够了。关于求解器选择问题类型推荐求解器备注纯LPlinprogMatlab自带速度快MILP小规模intlinprog自带几十到几百个变量可应付MILP大规模CPLEX / Gurobi效率优势明显QPquadprog一般用于二次惩罚项4.2 滚动优化算不完怎么办日内滚动优化的计算量是很多人翻车的重灾区。一天96个15分钟点每个点都做一次窗口优化如果模型复杂、窗口长很容易算到半夜。我的几条降计算量经验窗口别贪长。未来1-2小时的窗口足够不是未来24小时都优化。滚动要的是“新鲜度”不是“全局视野”。把空调这类非线性模型线性化。能不用非线性就不用求解器遇到非线性会慢很多。约束松弛技巧。某些约束比如舒适区间可以做成软约束加惩罚项不仅计算快还更贴近实际情况——真的会有人为了省电让室温超过设定1度吗会。复用上一步的解。把上轮窗口求得的解作为这轮迭代的初始可行解可以显著减少搜索空间。减少每次求解的决策变量。储能的充放电功率和SOC可以用线性等式绑定EV负荷可以聚合建模不用给每台车单独建变量。5. 我踩过的坑常见问题与排查方法5.1 问题速查表现象可能原因排查方法YALMIP报“No suitable solver found”没装商业求解器或没配置路径检查sdpsettings(solver,...)配置或改用intlinprog滚动优化结果长时间不收敛模型非线性强或约束过紧先跑纯LP松弛版本逐步加约束SOC经常越界滚动窗口状态初值传错或SOC边界留的裕量不足检查每个窗口的E_init是否来自实际执行结果日前计划跟日内结果严重不一致偏差惩罚项没加或权重太小在日内目标里加上与日前计划的偏差项模拟结果里储能反复充放目标函数缺少折旧成本加储能使用成本惩罚DR削减信号被优化器无视DR约束建模遗漏恢复时间/次数限制补全削减间隔和次数约束5.2 三个容易忽略的细节第一个细节温度舒适区间的约束要留“人情味”。把22-28度写成硬约束优化器会为了“不越界”疯狂运行空调成本很难看。我一般的做法是设一个硬边界比如23-27度再设一个软边界比如24-26度软边界越界在目标函数里惩罚让优化自己去权衡“是省钱重要还是舒适重要”。第二个细节日前和日内衔接要留余量。日前计划把储能SOC排到98%日内稍微一扰动就会顶到100%上限导致放电能力直接归零。我习惯在日前优化里把SOC上限设为95%留出5%的调节空间效果立竿见影。第三个细节数据量纲和权重。目标函数里购电费用是几十万级别SOC是0-1偏差惩罚是百分之几如果不做归一化优化器会被量纲最大的项“带跑”。我一般在写目标函数之前先把所有成本项统一到“元”这个单位把偏差项做成“元/kWh”的形式保证数量级一致。这套框架跑下来我最大的体会是多时间尺度调度不是四个优化器叠在一起就完事难点在“层与层的衔接”——日前定的基准日内做出来的修正到底怎么传、怎么惩罚、怎么保持一致性才是真正值得花时间的地方。另外我也想提醒一句Matlab里的代码跑通只是第一步现场工程的坑往往在数据质量和执行偏差上别指望模型能覆盖所有不确定性给实时修正留点权力比把模型做得无限复杂更靠谱。