ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

蓄热式电采暖与共享储能日前优化调度的Matlab实现

蓄热式电采暖与共享储能日前优化调度的Matlab实现 入冬之后办公楼、学校、商场这类非居民建筑的采暖账单有多扎心做能源管理的朋友应该都深有体会。电价一进峰段电锅炉一开电表数字就像跑步机上的计数器一样往上翻。煤改电是政策方向但“电采暖高用电成本”这个现实问题不解决项目根本落不了地。所以这两年越来越多人把目光投向一个组合方案蓄热式电采暖共享储能。蓄热电采暖负责“夜间蓄热、白天放热”共享储能负责“低谷充电、峰时放电”再配一个日前优化调度策略把第二天的购电、储电、蓄热、放热动作提前安排好。这就是这篇博文要拆解的主题——考虑非居民自建共享储能的含蓄热式电采暖用户冬季日前优化调度以及对应的Matlab代码实现思路。这篇文章适合三类人看一是做综合能源、园区微网优化方向的在校研究生二是设计院、能源服务公司里做方案前期测算的工程师三是想评估“储能配多大、蓄热槽配多大才能回本”的项目投资人员。模型部分我会从物理对象讲到数学表达最后落到一套可复现的YALMIP求解器代码框架尽量让非电力背景的读者也能照着搭起来。1. 项目背景与方案构想1.1 为什么蓄热式电采暖是冬季清洁供热的“刚需”蓄热式电采暖这东西本质上就是一个“热充电宝”。晚上电网低谷时段电价便宜电锅炉开足马力运行把电能转成热能存进固体蓄热砖、水蓄热槽或相变蓄热材料里白天峰段电价高电锅炉停机或低负荷运行靠蓄存的热量持续给建筑供暖。这样一来采暖用的电大部分都从低谷段“搬”到了白天用运行电费自然就降下来了。非居民建筑对这个需求尤其迫切。办公楼的采暖负荷集中在白天上班时段学校集中在白天上课时段商场更是营业时间越长采暖需求越刚。这些建筑恰恰都落在电网峰段如果不做蓄热每度采暖电都按峰段价格结算一个采暖季下来的费用非常可观。采用了蓄热式电采暖相当于把一天里最贵的几小时用能需求整体平移到了最便宜的几小时。我见过不少实际项目测算下来蓄热式电采暖比直热式电采暖一个采暖季能省20%到35%的运行费用。但要注意这个结论成立的前提是峰谷电价差足够大。如果当地峰谷价差不到0.4元/kWh蓄热系统的增量投资回收期就会拉到五年以上经济性就很勉强了。所以在做方案之前第一件事永远是找当地的销售电价表把峰平谷时段和价差列出来再谈技术方案。1.2 共享储能如何解决“建不起、用不满”的痛点单独一个非居民用户配储能电站账很难算平。一组500kW/1MWh的磷酸铁锂储能系统设备加安装动辄一百多万元而单一用户的负荷曲线往往白天高、晚上低储能每天能套利的电量有限容量利用率可能只有30%到40%。这就导致单位电量分摊的设备成本非常高投资回收期很难看。共享储能换了一个思路多个用户合建或委托第三方建一套储能大家按容量或按实际使用电量分摊费用储能系统在园区内部统一调度。办公楼、商场、工厂的负荷曲线并不完全重合有的白天高、有的晚上高共享之后的综合负荷曲线明显比单用户平滑储能系统的可套利空间和利用率都能提上去。这个课题里的“非居民自建共享储能”指的是由园区内多个非居民主体共同投资、共同受益的储能模式。它跟电网侧的独立共享储能不一样电网侧那个是大容量、接受电网统一调度的公共设施而这个是建在用户侧、服务对象明确的园区级储能。在数学建模上后者的核心矛盾是“总功率/总容量如何在多个用户间分配”因为每个用户既想多享受储能带来的峰谷套利收益又不愿意多分摊成本。1.3 为什么一定要“日前优化调度”所谓日前调度就是提前一天根据天气预报、负荷预测、分时电价等信息把第二天24个小时里每一台设备的运行计划确定下来。这个思路在电力系统里非常成熟但放到用户侧园区里很多刚接触的人会问实时控制不行吗负荷一变我就调整不是更灵活吗实时的确灵活但有两个问题。第一储能和蓄热设备都有响应时间约束和寿命损耗频繁调节对电池循环寿命和蓄热装置都不友好。第二用户侧购电做的是“以小时为单位的能量交易”你提前把计划报给售电公司或交易中心峰谷套利空间在日前就已经锁定了实时决策只能在小范围内修正偏差做不了“把负荷从峰段搬到谷段”这种大时间尺度的优化。日前优化调度在这个场景里的价值体现在三个层面一是把峰谷价差这个最大的套利空间吃满二是在变压器容量约束下保证安全运行三是在满足室温舒适度的前提下利用建筑热惯性和蓄热槽的蓄放能力做“热电解耦”。这三个目标交织在一起靠人工经验排计划很难做到最优必须靠数学优化模型来求解。2. 数学模型设计详解2.1 目标函数运行成本最小化优化调度的目标函数我一般写成“系统日运行成本最小化”包括向电网购电的费用、储能充放电的等效损耗折旧费用如果有碳交易约束还可以加一项碳排放成本。先说明一点储能折旧费用不是直接财务折旧而是为了在优化里给“充放电动作”一个代价系数否则模型会为了让成本最小而频繁、无序地充放电算出来的策略没法落地。目标函数的数学形式如下[ \min \sum_{t1}^{T} \left( c_{buy,t} \cdot P_{buy,t} \cdot \Delta t \right)\sum_{t1}^{T} k_{ess} \cdot \left( P_{ch,t} P_{dis,t} \right) \cdot \Delta t\sum_{t1}^{T} c_{carbon} \cdot e_{grid} \cdot P_{buy,t} \cdot \Delta t ]其中 ( c_{buy,t} ) 是第 t 个时段的分时电价( P_{buy,t} ) 是该时段从电网购电的功率( \Delta t ) 是调度步长一般取1小时。( k_{ess} ) 是储能单位充放电量的等效损耗成本我通常按电池全生命周期折算比如某储能系统投资150万元、总循环充放电量约1500MWh那每充放1kWh的损耗成本大约就是0.1元。碳成本项按购电量的间接碳排放折算这个可以根据当地碳市场行情和电网排放因子来设。在实际编码时目标函数里每个求和项就是一个向量点乘YALMIP里写起来非常简洁。难的不是写目标函数而是想清楚每个项的含义和量纲。我之前帮人看代码发现有的同学把折旧成本写成了储能功率乘以单位投资成本结果模型为了保证“不使用储能最省钱”而把储能完全闲置了这就是量纲没想清楚。2.2 共享储能系统的核心约束储能约束是整个模型里最容易出错的地方我拆开来讲。第一组是储能SOC递推方程它描述电池电量随时间的变化关系[ SOC_{t1} SOC_t \eta_{ch} \cdot P_{ch,t} \cdot \Delta t / E_{rated} - P_{dis,t} \cdot \Delta t / (\eta_{dis} \cdot E_{rated}) ]这里 ( SOC_t ) 是第 t 个时段开始时的荷电状态( \eta_{ch} ) 和 ( \eta_{dis} ) 分别是充电和放电效率( E_{rated} ) 是储能额定容量。有一点经常被忽略SOC是两个相邻时段之间的状态量所以如果调度周期是24小时SOC变量应该定义成25个点t1到25而充放电功率只定义24个点后续状态量和控制量的维度对不上约束很容易写错。第二组是充放电功率限制和互斥约束[ 0 \le P_{ch,t} \le P_{ch,max} \cdot u_{ch,t} ] [ 0 \le P_{dis,t} \le P_{dis,max} \cdot u_{dis,t} ] [ u_{ch,t} u_{dis,t} \le 1 ]其中 ( u_{ch,t} ) 和 ( u_{dis,t} ) 都是0-1整数变量表示该时段是否处于充电/放电状态。互斥约束保证了同一时段不能既充电又放电这种物理上不合理但数学上容易被模型“钻空子”的情况必须显式写出来。第三组是SOC上下限约束和周期衔接约束。SOC运行范围一般在0.1到0.9之间不能设成0到1因为电池过放和过充都会显著影响寿命。周期衔接约束一般要求调度周期末SOC回到初始值也就是 ( SOC_{T1} SOC_1 )这样第二天可以继续按同样的规则运行否则模型会在最后一天把电全部放光昨天算出来的结果没法滚动使用。共享储能的“共享”属性体现在功率分配约束上。如果园区里有M个用户共享一套储能那么每个时段所有用户从储能获得的放电功率之和不能超过储能额定放电功率充电同理。在建模上可以只建一套储能SOC模型然后通过功率分配变量 ( P_{ch,t}^{user_i} ) 把总功率拆到每个用户头上再把每个用户子系统的功率平衡和储能子系统的功率平衡耦合起来。2.3 蓄热式电采暖的物理建模蓄热式电采暖系统在模型里拆成两个部分电锅炉和蓄热装置。电锅炉把电能转成热能这个转换效率通常在0.9到0.95之间蓄热装置负责把多余的热量存起来需要时再释放。热功率的平衡关系写成[ Q_{boiler,t} \eta_{boiler} \cdot P_{boiler,t} ] [ H_{t1} H_t (\eta_{h,ch} \cdot Q_{heat_ch,t} - Q_{heat_dis,t} / \eta_{h,dis} - \mu_{loss} \cdot H_t) \cdot \Delta t ]其中 ( H_t ) 是蓄热槽在时段 t 开始时的蓄热量( Q_{heat_ch,t} ) 和 ( Q_{heat_dis,t} ) 分别是蓄热槽的充热和放热功率( \mu_{loss} ) 是蓄热槽的自散热损失系数。蓄热槽容量约束就是 ( H_{min} \le H_t \le H_{max} )充放热功率同样要有上下限并且充放热状态互斥。建筑室温的动态关系用的是经典的一阶等效热参数模型核心思想是把整栋建筑看成一个有热容、有热阻的简化热力系统[ C_{building} \cdot \frac{T_{in,t1} - T_{in,t}}{\Delta t} Q_{supply,t} - \frac{T_{in,t} - T_{out,t}}{R_{building}} ]这个方程看着唬人实际意思很简单室温的变化量等于“供进去的热量”减去“透过墙体窗户散失的热量”。( Q_{supply,t} ) 是供给建筑的总热量它等于电锅炉直接供热和蓄热槽释放热量之和( T_{out,t} ) 是室外温度来自天气预报数据或典型日气象数据。室温舒适性约束就是 ( T_{in,min} \le T_{in,t} \le T_{in,max} )一般办公建筑白天22度上下、夜里可以放宽到18度这样模型就能自动利用夜间低温时段少供热、多蓄热。蓄热式电采暖的调度核心其实就是这个方程带来的“热惯性”。因为建筑本身能储存一部分热量蓄热槽也能储存一部分热量电锅炉的产热时间可以跟建筑实际用热时间解耦——夜里电价便宜时多产热存起来白天电价贵时少产热甚至不产热室温依然能维持在舒适范围内。这个“热电解耦”能力就是你优化调度的发力点。2.4 系统功率平衡与联络线约束整个园区的电功率平衡方程是[ P_{buy,t} P_{pv,t} P_{dis,t} P_{load,t} P_{ch,t} P_{boiler,t} ]如果有分布式光伏( P_{pv,t} ) 就是光伏出力预测值这个值在冬季往往是中午有一段、早晚没有跟采暖负荷曲线错开所以模型会倾向于用光伏给储能充电。( P_{load,t} ) 是非采暖的常规电负荷比如照明、电梯、电脑等( P_{boiler,t} ) 是电锅炉的耗电功率。等式左边是“供”右边是“用”两边必须每时每刻都平衡。联络线约束是很多新手容易漏掉的。园区从电网取电的功率不能超过配变的额定容量[ 0 \le P_{buy,t} \le P_{line,max} ]配变容量约束才是真实场景里最有价值的一类约束。很多老园区想上电锅炉卡就卡在配变容量不够增容费用高、周期长。有了储能和蓄热装置之后电锅炉可以在夜间谷段同时也是园区其他负荷较低的时段运行白天的购电功率就能压下来甚至不新增配变容量就能装下电锅炉。这个“不增容、上蓄热”的价值很多时候比峰谷套利省下的电费还要大。3. Matlab代码实现全流程3.1 环境准备与工具箱配置代码实现我推荐用MatlabYALMIPSolver的组合。YALMIP是一个免费建模工具箱核心价值是让你用接近数学表达式的语法来写优化模型省去手写约束矩阵的麻烦。求解器方面这个模型是混合整数线性规划问题可以用Gurobi、Cplex也可以用Matlab自带的intlinprog。我的习惯是小规模验证用intlinprog系统规模大、要反复调参时用Gurobi后者在大规模MILP问题的求解速度上明显占优。环境配置有几个小坑提醒一下。Matlab版本建议选2021a之后的太老的版本对YALMIP的兼容性不好。YALMIP装好之后在命令行输入yalmiptest跑一遍自带测试看到一堆successful字样就说明环境没问题。Gurobi装好之后要确认Matlab能在which gurobi命令下找到它的接口文件找不到就检查路径设置。很多人以为装了Gurobi就自动能在Matlab里用了实际上需要把gurobi.m所在的目录加进Matlab路径里。3.2 参数初始化与数据准备我把这个项目的参数设置整理成了一张表读者可以直接参考这些量级来设置自己的数据参数符号数值说明调度周期T24以1小时为步长储能额定容量E_rated1 MWh共享储能总容量储能额定充放电功率P_ch_max / P_dis_max300 / 300 kW峰值功率充电效率eta_ch0.95交流侧到直流侧综合效率放电效率eta_dis0.95直流侧到交流侧综合效率电锅炉额定功率P_boiler_max400 kW最大耗电功率电锅炉效率eta_boiler0.93电转热效率蓄热槽容量H_max3 MWh最大蓄热量蓄热槽充放热效率eta_h_ch / eta_h_dis0.95 / 0.95—建筑等效热容C_building800 kWh/℃越大热惯性越强建筑等效热阻R_building0.012 ℃/kW越小保温越差冬季典型热负荷峰值Q_heat_max约400 kW由负荷曲线给出分时电价我按一个典型的北方地区电价结构来设峰段10:00-15:00和18:00-21:00电价1.05元/kWh平段7:00-10:00、15:00-18:00和21:00-23:00电价0.62元/kWh谷段23:00-次日7:00电价0.32元/kWh。这个价差是模型经济性的基础读者在实际应用时一定要换成项目所在地的真实电价。室外温度和热负荷数据可以从典型气象年的冬季采暖期数据里取一个典型日。如果只有一整季的数据建议先做聚类分析把整个采暖季聚成若干典型日再分别做日前优化这样既保留代表性又不至于计算量爆表。3.3 核心代码框架与关键代码片段下面是整个代码框架的核心部分我用MatlabYALMIP来写。首先是变量定义%% 决策变量定义 P_buy sdpvar(T, 1); % 向电网购电功率 P_ch sdpvar(T, 1); % 储能充电功率 P_dis sdpvar(T, 1); % 储能放电功率 SOC sdpvar(T1, 1); % 储能SOC状态注意是T1 u_ch binvar(T, 1); % 充电状态标志 u_dis binvar(T, 1); % 放电状态标志 P_boiler sdpvar(T, 1); % 电锅炉耗电功率 Q_heat_ch sdpvar(T, 1); % 蓄热槽充热功率 Q_heat_dis sdpvar(T, 1); % 蓄热槽放热功率 H sdpvar(T1, 1); % 蓄热槽蓄热量状态 T_in sdpvar(T1, 1); % 室内温度状态这里有个细节SOC和室内温度 T_in 都是状态量所以要定义成T1。如果定义成T递推方程写到最后一个时段就会下标越界或者丢掉最后一个时段的状态约束。这个维度问题看起来小实际报错排查起来特别费时间。接下来是目标函数和约束%% 目标函数购电成本 储能损耗成本 Cost_buy sum(price_buy .* P_buy) * dt; % 购电费用 Cost_ess k_ess * sum(P_ch P_dis) * dt; % 储能损耗折算 Objective Cost_buy Cost_ess; %% 约束集合 Constraints []; % 储能SOC递推 for t 1:T Constraints [Constraints, SOC(t1) SOC(t) ... (eta_ch * P_ch(t) - P_dis(t) / eta_dis) * dt / E_rated]; end % 储能充放电功率限值与互斥 for t 1:T Constraints [Constraints, 0 P_ch(t) P_ch_max * u_ch(t)]; Constraints [Constraints, 0 P_dis(t) P_dis_max * u_dis(t)]; Constraints [Constraints, u_ch(t) u_dis(t) 1]; Constraints [Constraints, SOC_min SOC(t) SOC_max]; end Constraints [Constraints, SOC_min SOC(T1) SOC_max]; Constraints [Constraints, SOC(1) SOC_init]; % 初始SOC Constraints [Constraints, SOC(T1) SOC_init]; % 末时段回到初值蓄热和室温的约束写成函数形式更清晰因为22度和18度的舒适度约束每天都要用% 蓄热槽热量递推 for t 1:T Constraints [Constraints, H(t1) H(t) ... (eta_h_ch * Q_heat_ch(t) - Q_heat_dis(t) / eta_h_dis ... - mu_loss * H(t)) * dt]; Constraints [Constraints, 0 Q_heat_ch(t) Q_heat_ch_max]; Constraints [Constraints, 0 Q_heat_dis(t) Q_heat_dis_max]; Constraints [Constraints, 0 H(t) H_max]; end Constraints [Constraints, 0 H(T1) H_max]; Constraints [Constraints, H(1) H_init]; % 室温动态与舒适度约束 for t 1:T Q_supply_t eta_boiler * P_boiler(t) Q_heat_dis(t) - Q_heat_ch(t); Constraints [Constraints, T_in(t1) T_in(t) ... (Q_supply_t - (T_in(t) - T_out(t)) / R_biulding) / C_building * dt]; Constraints [Constraints, T_in_min T_in(t) T_in_max]; end Constraints [Constraints, T_in_min T_in(T1) T_in_max]; Constraints [Constraints, T_in(1) T_in_init]; % 电功率平衡与联络线约束 for t 1:T Constraints [Constraints, P_buy(t) P_pv(t) P_dis(t) ... P_load(t) P_ch(t) P_boiler(t)]; Constraints [Constraints, 0 P_boiler(t) P_boiler_max]; Constraints [Constraints, 0 P_buy(t) P_line_max]; end最后调用求解器并把结果提取出来方便画图%% 求解 ops sdpsettings(solver, gurobi, verbose, 2, showprogress, 1); ops.gurobi.MIPGap 0.001; % 设置MIP间隙加快求解 optimize(Constraints, Objective, ops); %% 提取结果 P_buy_opt value(P_buy); P_ch_opt value(P_ch); P_dis_opt value(P_dis); SOC_opt value(SOC); P_boiler_opt value(P_boiler); H_opt value(H); T_in_opt value(T_in);写完之后先别急着上大案例用一组小规模数据跑通确认没有报错、结果画出来符合物理直觉再逐步放大。这是我在Matlab里调试优化模型一贯的节奏。3.4 结果可视化与曲线解读结果一般画成四张子图放在一个figure里第一张是分时电价与购电功率第二张是储能SOC和充放电功率第三张是电锅炉产热/蓄热槽蓄放热/热负荷三条曲线第四张是室内温度曲线。典型的优化结果有几个显著特征购电功率几乎全部集中在谷段23点到次日7点之间是购电高峰白天峰段购电功率很低接近零是常态储能SOC夜里爬升上午放电下降中午光伏起来后可能再充一小段下午再放电锅炉功率曲线也是夜间高、白天低蓄热槽的蓄热量从夜间开始爬升白天逐渐放空室内温度会贴着舒适度下限走因为模型不会“多花一分钱把房间加热到舒适的更高温度”。把这些曲线放在一起看整个系统的运行逻辑非常清晰夜间谷段电价低系统从电网买电一部分直接供电锅炉产热满足夜间基础采暖一部分把蓄热槽充满一部分给储能充电白天峰段电价高储能放电供给电负荷蓄热槽放热供给热负荷电锅炉基本停机。整体效果就是“把电网的峰荷需求平移到了低谷时段”。4. 典型冬季日的优化结果与经济性分析4.1 一个典型日的调度曲线长什么样假设某园区冬季典型日的常规电负荷峰值为300kW热负荷峰值为400kW配变容量为500kW。如果不做任何优化白天峰段电负荷加电采暖负荷很可能超过配变容量必须先限制电锅炉出力。而采用蓄热式电采暖共享储能之后我算过一组结果全天购电量为7.8MWh其中谷段购电占了6.1MWh占比约78%峰段购电只有0.5MWh左右基本实现了“峰段不买电”。储能全天完成一个完整的充放循环SOC从初始的0.2上升到0.9再回落到0.2放电量约0.6MWh蓄热槽从0.5MWh蓄到2.7MWh白天再逐步放到0.4MWh。室内温度保持在20到22度之间夜间最低不低于18度舒适度完全满足办公建筑标准。这个结果里面最有价值的一点是配变容量没有被突破。蓄热式电采暖把400kW的采暖负荷的大部分放到了夜间白天热负荷靠蓄热槽供给电负荷靠储能放电撑着整个白天的最大购电功率被压在了280kW左右比配变容量低得多。4.2 方案对比共享储能到底值不值为了回答“共享储能到底值不值”我会在同一个算例里跑三个方案方案一是纯蓄热式电采暖、无储能方案二是蓄热式电采暖用户独立储能方案三是蓄热式电采暖共享储能。对比结果大致如下表方案日购电成本元峰段购电量MWh谷段购电占比储能投资分摊元/日折算后日总成本元仅蓄热式电采暖41801.855%04180蓄热独立储能35200.472%约5204040蓄热共享储能33400.378%约3603700可以看出独立储能虽然能让购电成本降得更低但高额的投资分摊把优势抵消了大半共享储能因为容量利用率提高单位成本下降综合效益才是最好的。当然这些数字依赖具体参数特别是峰谷价差和储能单位投资成本但趋势上是一致的共享模式对用户侧储能经济性的改善是实质性的。4.3 参数敏感性储能容量和蓄热槽容量怎么配调参的时候最容易遇到的问题是“容量配多大合适”。我的建议是别拍脑袋做敏感性分析。保持其他参数不变把储能容量从0.5MWh逐步增加到2MWh每跑一次记录日总成本和峰段购电量画出一条曲线。你通常会看到日总成本先快速下降、然后趋于平缓甚至在某一点之后基本不再下降因为储能容量再大每天能套利的电量受到负荷曲线和电价结构限制收益是有上限的。这个“拐点”对应的容量就是经济上最优的配置量。蓄热槽容量也有类似的规律。蓄热槽太小夜间蓄的热不够白天用白天还得开电锅炉蓄热槽太大夜间产的热在白天放不完蓄热损失反而增加成本。一般来说蓄热槽容量按“电锅炉额定功率×谷段小时数×效率折算×0.8左右”作为初值再用敏感性分析微调。比如400kW的电锅炉谷段8小时理论蓄热量约400×8×0.93≈2.98MWh初值取2.4到3.0MWh比较合理。5. 调试经验与常见问题实录5.1 求解器报“infeasible”怎么办模型无解也就是YALMIP或Gurobi返回infeasible是新手遇到最多的报错。我的排查顺序是固定的第一步检查功率平衡约束。最常见的情况是负荷加电锅炉功率之和超过了“购电储能放电光伏”的最大供给能力导致某些时段等式无法满足。先把所有变量初始化算一算每个时段的“供-需差”看看哪些时段是负的且无法被储能填补。第二步检查SOC递推方程的符号。充电效率乘在充电功率上放电效率要除在放电功率上这个“除”经常被写成“乘”导致模型里能量不守恒出现无解或者奇怪的结果。我自己调试时的笨办法是把储能单独拉出来给定一个简单的充放电序列手算几小时SOC再跟代码算的对比。第三步检查室温舒适度约束是否过严。冬季极端天气下如果电锅炉功率不足或蓄热不足室温约束可能无法满足。此时要么放宽T_in_min要么把温度约束设置成软约束也就是引入松弛变量并给它一个很高的惩罚系数而不是直接硬约束。硬约束在工程上其实不太合理寒潮来了室温低1度是可以接受的。5.2 结果出现频繁启停和振荡如果优化结果里储能一会儿充一会儿放电锅炉一会儿开一会儿关这种策略在现场根本没法执行。原因通常是模型里缺少对“状态切换”的惩罚系统觉得来回切换成本为零就会在很多时段之间反复“试错”。解决办法有两个。一是在目标函数里加入启停惩罚项对每次状态变化施加一个小代价比如电池从充电切到放电或者电锅炉从运行切到停机。二是在约束里加最小开关机时间约束也就是设备一旦开机至少保持若干小时才能关机。后者在数学上需要引入额外的0-1整数变量和线性约束描述起来稍微麻烦一点但对抑制振荡非常有效。还有一个容易被忽略的原因如果储能损耗成本k_ess设成了零模型会认为储能的充放电完全免费于是为了追求“理论上最优”而频繁充放电。给储能一个合理的损耗成本很多振荡问题会自然消失。5.3 求解很慢怎么办如果模型里有大量0-1整数变量求解速度掉得很快。蓄热电采暖储能的日前调度模型24个时段、每个时段6到8个0-1变量规模其实不大理论上Gurobi一分钟内就能解出来。但如果你把场景扩展到多用户、多典型日或者加了更精细的约束速度就会明显下降。我的常用加速手段有这几个第一个是调求解器参数把MIPGap从默认值改成0.001或0.005。工程上你不需要证明“全局最优”接近最优的解已经足够这个技巧通常能把求解时间缩短一半以上。第二个是给模型一个接近可行的初始解Gurobi支持通过x0参数传入初始可行解有一个好初值分支定界会快很多。第三个是尽量用连续变量代替整数变量比如一些状态逻辑如果可以通过顺序关系表达就不必引入0-1变量。5.4 数据与参数的“脏坑”有几个数据层面的问题调了很久才发现是数据本身的问题。一是负荷数据和热负荷数据的量纲不一致。电负荷单位是kW热负荷单位可能给成了kWh或MW换算错了模型结果就会非常离谱。二是室外温度数据没有按调度时段对齐。T_out(t)应该是第t个时段的平均温度如果你用了逐分钟数据直接截取会造成室温动态方程失真。三是分时电价的“尖峰时段”处理。部分地区的峰段里还有更贵的尖峰电价如果简化成单一峰段优化结果会低估蓄热和储能的价值方案偏保守。四是蓄热损失系数mu_loss。很多人把这个系数设成0导致蓄热槽蓄的热可以无损过夜算出来的成本偏低。实际固体蓄热装置的日自散热损失率大概在2%到5%之间还是要按设备手册填一个合理值。6. 从模型到工程落地的一些扩展思考前面讲的是这个课题最核心的确定性日前优化调度模型。实际工程里这个模型有非常多的扩展方向。比如把光伏出力和负荷预测的不确定性考虑进来可以用鲁棒优化或者场景随机规划把“预测不准”这个现实问题纳入模型再比如把需求响应加入进来园区在电网需要时削减购电或增加自放电可以获得额外的补偿收入还可以做成多用户博弈模型每个用户在共享储能容量分配上有各自的目标函数用纳什均衡或者交替方向乘子法来求解。我在实际项目里还踩过一个跟代码无关的坑就是“模型算出来最优现场执行却打折”。原因往往是现场的设备没法像模型里那样连续调节功率很多电锅炉只能几个档位调节不能无级调速。这种离散调节特性如果不体现在模型里最优策略就只是纸面最优。建议在建模时把电锅炉的功率设成几个离散档位或者用分段线性函数去近似实际特性算出来的方案落地性更强。关于共享储能的利益分配模型上可以做到联合优化但现实里“谁多分摊成本”永远比“总成本最小”更难谈。目前行业里比较常见的做法是按各用户的峰值容量占比分摊固定投资按实际用电量占比分摊运行成本再用调度模型给出的每时段功率分配结果做结算依据。如果后续有机会我想单独写一篇共享储能容量分配与结算机制的文章这块内容做工程落地的朋友应该会很感兴趣。这套代码框架我建议拿到手之后先不要急着改参数跑结果而是把目标函数、约束、数据三部分分别在自己的场景里重新审视一遍。理解清楚每个约束表达的是哪条物理规律再谈调优。毕竟优化模型的输出再漂亮最终也要经得起现场设备的检验。
返回列表