1. 先说清楚:冷热电联供微网到底为什么要配冰蓄冷
最近好几个研究生和工程师都在问含冰蓄冷空调的冷热电联供型微网优化调度怎么做,Matlab代码实现到什么程度才算"能用"。这不算个新方向,但确实是综合能源系统里最有工程落地价值的一类问题,因为它同时踩中了两个痛点:联供系统的"以热定电"刚性和空调冷负荷的削峰填谷潜力。
先花点篇幅把系统组成和痛点讲透。冷热电联供型微网(CCHP微网)的核心设备一般包括燃气轮机或内燃机、余热锅炉、吸收式制冷机、电制冷机,再加上光伏、储能等。燃气轮机发电会产生大量余热,余热通过溴化锂吸收式制冷机转化成冷量,就实现了"发一次电,同时拿到电、冷、热三种能量"的效果。听起来很美,但实际调度时有个绕不开的矛盾:用户的电负荷和冷负荷曲线不是同步变化的,而燃气轮机的发电量一旦定下来,余热量基本就跟着定了。白天冷负荷峰值时段,如果为了让吸收式制冷机多出冷而去抬高燃气轮机出力,电就会过剩,只能上网或者弃掉;如果为了匹配电负荷压低燃气轮机出力,冷又不够用,只能让电制冷机顶上,系统经济性会打折扣。
这时候冰蓄冷空调的价值就出来了。它的核心思路是在冷负荷低谷(通常是夜间)用电驱动双工况制冷主机造冰,把冷量以冰的形式储存起来,白天冷负荷高峰时融冰供冷,从而把"制冰"这个动作从峰时挪到谷时。引入冰蓄冷之后,微网调度多了一个可控的"冷储能",等于把原来刚性的冷平衡关系柔化了——燃气轮机的出力可以在更大范围内自由选择,系统不再被迫跟着某一条负荷曲线走。
多时间尺度优化调度则解决另一个问题:日前预测和实际运行总有偏差。光伏出力预测误差、负荷预测误差、设备实际响应延迟,这些不确定性在前一天定的计划在当天跑起来很可能已经失真。所以工程上普遍采用"日前调度(小时级)+日内滚动优化(15分钟或1小时级)+实时反馈修正(分钟级)"的分层架构,让计划尺度、滚动尺度、执行尺度各司其职。这也是这类项目写到论文里、写到评审答辩里最受关注的部分。
我用Matlab搭了一套完整的代码框架,把冷热电联供、冰蓄冷空调、多时间尺度这三个核心要素全部揉进一个可运行的优化调度模型里。这篇文章把建模思路、代码结构、参数设置和实际调试中踩过的坑都梳理一遍,适合正在做综合能源优化调度方向的研究生、刚接触微网调度的工程师参考。文中的模型用Yalmip建模,求解器用Cplex或Gurobi都行,代码整体逻辑是通用的。
2. 系统建模:把CCHP微网里的每个设备数学化
这个部分是整个项目的地基。模型建得对不对、约束写得全不全,直接决定调度结果靠不靠谱。
2.1 微网系统拓扑与能量流关系
我采用的系统拓扑是典型的CCHP微网结构:电网通过联络线向微网供电;燃气轮机燃烧天然气发电,余热进入余热锅炉,一部分蒸汽驱动吸收式制冷机供冷,一部分直接供热;电制冷机和冰蓄冷空调承担剩余冷负荷;光伏作为可再生电源优先消纳;储能电池负责电功率的平移。天然气锅炉作为备用热源,保证极端工况下热负荷不缺。
从能量流角度看,微网内部有电、冷、热三种能量耦合,核心耦合点在于燃气轮机的热电比。燃气轮机的余热回收量不是独立变量,而是取决于发电出力,这就形成了电-冷-热之间的强耦合约束。冰蓄冷的引入让冷负荷不再必须实时满足,冷量可以在时间维度上搬运,相当于给燃气轮机松了绑——这是模型里最有价值的自由度。
2.2 燃气轮机、余热锅炉、溴化锂机组的数学模型
燃气轮机的建模不搞复杂的非线性特性曲线,工程上普遍采用线性化的效率模型:
- 发电功率范围:( P_{gt,min} \le P_{gt}(t) \le P_{gt,max} )
- 爬坡约束:( -\Delta P_{gt,down} \le P_{gt}(t) - P_{gt}(t-1) \le \Delta P_{gt,up} )
- 燃料成本:( C_{fuel}(t) = \frac{P_{gt}(t) \cdot \Delta t}{\eta_e(t) \cdot LHV_{gas}} \cdot c_{gas} ),其中 ( \eta_e ) 是发电效率,( LHV_{gas} ) 是天然气低位热值,( c_{gas} ) 是气价
- 余热回收:( Q_{rec}(t) = P_{gt}(t) \cdot \frac{1-\eta_e(t)-\eta_{loss}}{\eta_e(t)} ),其中 ( \eta_{loss} ) 是散热损失系数
发电效率如果要做精细化处理,可以用分段线性函数拟合,把非线性问题转化为混合整数线性规划(MILP)。我自己在代码里用的是固定效率加爬坡限制的简化做法,对日前调度完全够用,求解速度快很多。
余热锅炉的模型相对简单:( Q_{hrb}(t) = \eta_{hrb} \cdot Q_{rec}(t) ),即回收到的余热乘以一个换热效率。吸收式制冷机的出力约束为 ( Q_{ac}(t) \le Q_{ac,max} ),其制冷的 COP 通常在 0.7~1.3 之间,我这里取 1.0 左右。注意吸收式制冷机的 COP 和电制冷机不在一个量级,电制冷机 COP 一般有 3~5,所以什么时候用哪种制冷方式,是优化模型里很有意思的经济权衡。
2.3 冰蓄冷空调的两种运行工况与约束写法
冰蓄冷空调是本文模型的特色环节,重点展开。它的物理构成是双工况制冷主机加蓄冰槽。双工况主机有两种运行模式:
- 制冰工况(夜间):主机满负荷制冷,但制冷量不直接送给用户冷负荷,而是通过乙二醇溶液循环把蓄冰槽里的水冻成冰。制冰工况下 COP 较低,大约 2.9~3.1。
- 制冷工况(白天):主机像普通电制冷机一样直接制取低温冷水供冷,同时可以根据策略选择是否融冰补充冷量。制冷工况 COP 较高,约 3.5~5.0。
在优化模型里,我引入两个二元变量 ( u_{ice}(t) ) 和 ( u_{cool}(t) ) 分别表示制冰工况和制冷工况的启停状态,并且强制两者互斥:
[ u_{ice}(t) + u_{cool}(t) \le 1 ]
制冰工况下主机出力:
[ Q_{ice}(t) = COP_{ice} \cdot P_{ice}(t) ]
其中 ( P_{ice}(t) ) 是制冰工况下主机的耗电功率,( COP_{ice} ) 取 3.0。
制冷工况下主机出力:
[ Q_{cool}(t) = COP_{cool} \cdot P_{cool}(t) ]
其中 ( COP_{cool} ) 取 4.2。
蓄冰槽的蓄冰量用状态量 ( Q_{store}(t) ) 表示,动态方程是:
[ Q_{store}(t) = Q_{store}(t-1) \cdot (1 - \eta_{loss}) + \eta_{ice} \cdot Q_{ice}(t) - Q_{melt}(t) ]
这里 ( \eta_{loss} ) 是蓄冰槽的自损系数,通常很小,取 0.02 左右;( \eta_{ice} ) 是制冰充冷效率,取 0.95;( Q_{melt}(t) ) 是 t 时段融冰供冷量,受最大融冰速率限制:
[ 0 \le Q_{melt}(t) \le Q_{melt,max} ]
同时蓄冰量要满足上下限约束:
[ 0 \le Q_{store}(t) \le Q_{store,max} ]
为了让调度结果有"日循环"的可重复性,通常要求调度周期末蓄冰量回到初始值附近,即 ( Q_{store}(T) \ge Q_{store}(0) ),这个约束在做法上可以根据运行需要灵活调整。
这里有个关键设计点:融冰供冷量 ( Q_{melt}(t) ) 到底是"决策变量"还是"跟谁匹配的量"?我采用了决策变量的写法,让优化器在满足蓄冰量动态平衡和融冰速率上限的前提下,自由选择每个时段放多少冰。这样经济性最优,代码实现也简洁。如果要更严谨,可以考虑融冰速率随剩余冰量下降而衰减的非线性特性,但日前调度层面没必要,模型复杂化对结果改善很有限。
2.4 电、冷、热功率平衡约束
三种负荷都要有平衡方程,这是调度模型的骨架。
电功率平衡:
[ P_{grid}(t) + P_{gt}(t) + P_{pv}(t) + P_{dis}(t) = P_{load}(t) + P_{ch}(t) + P_{ice}(t) + P_{cool}(t) ]
其中 ( P_{grid}(t) ) 为电网交互功率,( P_{pv}(t) ) 为光伏出力,( P_{dis}/P_{ch} ) 是储能电池放电/充电功率。注意等式右侧的 ( P_{ice}(t) + P_{cool}(t) ) 就是冰蓄冷主机的耗电。
冷功率平衡:
[ Q_{ac}(t) + Q_{cool}(t) + Q_{melt}(t) = Q_{load}(t) ]
也就是吸收式制冷出力加上电制冷出力加上融冰供冷量之和等于冷负荷。这个方程形式上简单,实际是模型里最容易写错的地方——冷负荷和热负荷的单位都是 kW,但来源不同能量品位,不能只把数值加起来就完事。
热功率平衡:
[ Q_{hrb}(t) + Q_{gb}(t) = Q_{heat_load}(t) ]
余热锅炉供热加上燃气锅炉补燃等于热负荷。燃气锅炉的模型是:( Q_{gb}(t) = \eta_{gb} \cdot P_{gb,fuel}(t) ),燃料成本按天然气计算。
设备模型和平衡约束都清楚之后,目标函数就顺理成章了。
3. 多时间尺度调度的框架设计和目标函数
多时间尺度调度听起来高大上,实际落地就是一个"层层递进、逐步精化"的思想:每天开始前一天做一次24小时规模的计划,决定机组启停、蓄冰放冰的大方案;当天运行过程中每隔一段时间重新优化一次,把最新预测数据带进来修正计划;实时层面再把偏差找平。
3.1 日前调度层:以24小时为单位确定冰蓄冷的大策略
日前调度的决策变量是24小时,步长1小时,预测数据是日前预测的冷热电负荷和光伏出力。这一层要解决的问题是:明天燃气轮机每个时段发多少电、冰蓄冷主机夜间哪几个小时制冰、白天哪几个小时融冰、吸收式制冷机和电制冷机怎么配合、储能电池怎么充放、跟电网买多少电。
我设定的目标函数是全天运行成本最小:
[ \min \sum_{t=1}^{24} \left[ C_{grid}(t) + C_{fuel}(t) + C_{om}(t) \right] ]
其中 ( C_{grid}(t) ) 是购电成本,( C_{fuel}(t) ) 是天然气燃料成本(包括燃气轮机和燃气锅炉),( C_{om}(t) ) 是设备运行维护成本。电价采用分时电价,峰、平、谷三段。典型数据我设置的是:
| 时段 | 电价(元/kWh) |
|---|---|
| 峰时(10:00-15:00, 18:00-21:00) | 1.20 |
| 平时(07:00-10:00, 15:00-18:00, 21:00-23:00) | 0.80 |
| 谷时(23:00-次日07:00) | 0.35 |
天然气价格取2.5元/m³,燃气轮机发电效率0.35,余热回收效率0.45,散热损失0.20。
日前调度的结果会给出冰蓄冷的"策略骨架":谷时电价时段制冰,峰时电价时段融冰。这是直觉上就该成立的结论,优化器会找到这个解。
3.2 日内滚动层:修正预测误差的15分钟级重优化
日内滚动优化的逻辑是:每15分钟为周期,跑一次未来4小时(16个时段)的优化,只执行当前时段的决策,下一个周期再滚动刷新。这样做的好处是每一步都是基于最新实测负荷和最新光伏预测做的决策,比死板执行日前计划抗扰动能力强得多。
日内滚动层的时间步长我设为15分钟,但在模型里复用日前调度的设备约束,只是把数据换成分辨率更高的预测值。这里有技术细节:日内滚动优化的开机状态直接沿用日前调度的结果,不做启停优化,只优化出力大小和冰蓄冷放冰量。原因是启停变量涉及机组最小开停机时间约束,滚动时频繁变动不现实。
3.3 实时层:功率偏差的反馈校正
实时层的功能是让调度计划在极端情况下不越界。现实中光伏云层遮挡、冷负荷骤变等扰动,可能导致设备实际出力偏离计划值。实时层以分钟级采样,计算实测功率与计划功率的偏差,然后通过储能电池的充放电微调来平衡。这一层我尽量写得轻量,因为它在Matlab代码里属于"流程控制"层面的东西,核心逻辑简单,没必要建复杂模型。
关于三层之间的衔接,有个实践建议:日前调度结果中蓄冰槽在每个时段的蓄冰量,要作为日内滚动的初始状态和边界参考。日内滚动不能脱离日前的大框架去重新规划蓄冰量,否则会出现白天把冰全放光、傍晚冷负荷上来没冰可用的尴尬局面。我在代码里通过传递 ( Q_{store}(t) ) 的上下限区间来约束日内滚动,效果很好——既保留日前制定的蓄冰放冰节奏,又给日内留了调整余地。
4. Matlab代码实现的核心逻辑与调试经验
这一部分是我最想写的,因为很多同学卡在"模型会推、代码不会写"或者"代码能跑、结果不对"这两类问题上。我把代码框架和关键调试经验梳理一遍。
4.1 代码模块划分与变量定义
整个Matlab代码工程我按以下模块组织:
data_input.m:基础数据录入,包括24小时冷热电负荷、光伏出力、分时电价、设备参数、天然气价格build_model.m:用Yalmip定义决策变量、目标函数、约束条件solve_day_ahead.m:日前调度主程序,返回各设备出力计划和蓄冰槽状态rolling_optimization.m:日内滚动优化主程序,循环调用优化函数plot_results.m:结果可视化,输出电平衡图、冷平衡图、蓄冰量曲线、成本统计
变量命名要形成习惯。我自己常用的命名规则是:电功率用P前缀,冷功率用Q前缀,热功率用H前缀,二元状态变量用u前缀。比如P_gt代表燃气轮机发电功率,Q_ac代表吸收式制冷出力,Q_melt代表融冰供冷量,u_ice代表制冰工况状态。这样写约束的时候一眼能看出物理量含义,排查错误很方便。
4.2 Yalmip建模的核心代码框架
以日前调度为例,核心代码框架大致如下:
%% 决策变量定义 P_gt = sdpvar(1, 24); % 燃气轮机发电功率 P_grid = sdpvar(1, 24); % 电网交互功率 P_ice = sdpvar(1, 24); % 制冰工况耗电功率 P_cool = sdpvar(1, 24); % 制冷工况耗电功率 Q_ac = sdpvar(1, 24); % 吸收式制冷出力 Q_cool = sdpvar(1, 24); % 电制冷出力 Q_melt = sdpvar(1, 24); % 融冰供冷量 Q_store = sdpvar(1, 25); % 蓄冰槽蓄冰量,25个点 u_ice = binvar(1, 24); % 制冰工况状态 u_cool = binvar(1, 24); % 制冷工况状态 u_gt = binvar(1, 24); % 燃气轮机启停状态 %% 约束条件 Constraints = []; % 电功率平衡 Constraints = [Constraints, P_grid + P_gt + P_pv + P_dis - P_ch ... == P_load + P_ice + P_cool]; % 冷功率平衡 Constraints = [Constraints, Q_ac + Q_cool + Q_melt == Q_load]; % 热功率平衡 Constraints = [Constraints, H_hrb + H_gb == H_load]; % 燃气轮机范围约束 Constraints = [Constraints, P_gt_min * u_gt <= P_gt <= P_gt_max * u_gt]; Constraints = [Constraints, -P_gt_ramp <= P_gt(2:24) - P_gt(1:23) <= P_gt_ramp]; % 冰蓄冷工况互斥 Constraints = [Constraints, u_ice + u_cool <= 1]; % 制冰工况功率范围 Constraints = [Constraints, 0 <= P_ice <= P_ice_max * u_ice]; Constraints = [Constraints, Q_ice == COP_ice * P_ice]; % 制冷工况功率范围 Constraints = [Constraints, 0 <= P_cool <= P_cool_max * u_cool]; Constraints = [Constraints, Q_cool == COP_cool * P_cool]; % 蓄冰量动态平衡 Constraints = [Constraints, Q_store(2:25) == Q_store(1:24) * (1 - eta_loss) ... + eta_ice * Q_ice - Q_melt]; % 蓄冰量与融冰速率约束 Constraints = [Constraints, 0 <= Q_store <= Q_store_max]; Constraints = [Constraints, 0 <= Q_melt <= Q_melt_max]; %% 目标函数 Cost_grid = sum(P_grid .* Price_electricity); Cost_fuel = sum(P_gt / (eta_e * LHV_gas) * Price_gas) ... + sum(H_gb / (eta_gb * LHV_gas) * Price_gas); Cost_om = sum(P_gt) * c_om_gt + sum(P_ice + P_cool) * c_om_ec ... + sum(Q_ac) * c_om_ac; Objective = Cost_grid + Cost_fuel + Cost_om; %% 求解 optimize(Constraints, Objective, sdpsettings('solver', 'cplex'));这段代码看着不长,但每个约束都有实际意义。最让我在调试时头疼的是Q_store的索引对齐问题:蓄冰量状态是从0到24共25个点,而其他变量是1到24共24个时段。动态方程对应的是Q_store(2:25)等于上一时刻乘以自损系数再加充冷减融冰,写的时候稍不留神索引就对不齐,优化结果会出现蓄冰量莫名其妙的跳变。调试方法是在求解前把约束表达式输出来检查维度,Yalmip可以直接在命令行打印约束的左端和右端,一眼就能发现索引错误。
4.3 从论文模型到可运行代码的四个关键坑
论文里的模型和能跑出合理结果的代码之间,隔着几个隐性坑。
第一个坑:蓄冰槽初始状态的设置方式。很多文献假设初始蓄冰量为0,但实际为了日周期可重复运行,初始蓄冰量应该等于前一天结束时的剩余量。更合理的做法是设定调度周期末蓄冰量不小于初始值,形成一个循环运行的"软闭环"。我在代码里直接设定 ( Q_{store}(0) = 0.2 \cdot Q_{store,max} ),然后加约束 ( Q_{store}(24) \ge Q_{store}(0) )。这个技巧能避免优化器把蓄冰槽当成"一次性"资源,第一天把所有冰用完,后面几天没法持续运行。
第二个坑:冷负荷的单位和数量级。冷负荷本质上是用电制冷机、吸收式制冷机、融冰供冷三路供给的,但三种制冷方式的能效不同,在混合整数规划里很容易因为约束写得过于紧凑而让问题无解。我建议先跑一个不含冰蓄冷的纯CCHP模型,验证电平衡和冷平衡能同时满足且总成本合理,再引入冰蓄冷,定位问题会容易得多。
第三个坑:二元变量的分支定界效率。制冰工况、制冷工况、燃气轮机启停三个二元变量让模型变成MILP,求解时间随着约束复杂度上升明显变慢。我踩过的坑是:在日内滚动优化中仍然让所有二元变量参与优化,导致单次滚动要跑几十秒,实时性无法接受。解决方法前面提过——日内滚动固定启停状态、冻结冰蓄冷工况模式、只优化连续变量,求解时间降到不到1秒。
第四个坑:Cplex和Gurobi对同一个模型给出的结果可能有细微差异。主要是因为MILP的整数解不是唯一最优,边界上有多个最优解时,不同求解器选解的策略不同。这不算bug,但如果你需要严格的"代码可复现",建议在论文里注明求解器和版本号。我实际测试下来Gurobi的MILP根节点松弛质量略好,Cplex在大规模约束下的求解稳定性更有口碑,两者都能用。
4.4 滚动优化的循环实现方式
日内滚动优化在Matlab里实现起来核心是一个for循环加一个结构体存储结果。伪代码如下:
for k = 1 : N_horizon % 更新当前时刻的负荷、光伏预测数据 P_load_pred = load_profile(k : k + horizon - 1); P_pv_pred = pv_profile(k : k + horizon - 1); % 读取日前调度确定的蓄冰量参考值 Q_store_ref = day_ahead_Q_store(k : k + horizon - 1); % 构建并求解滚动优化模型 [x_opt(k), Q_melt_opt(k)] = solve_rolling(P_load_pred, P_pv_pred, Q_store_ref); % 只下发当前时刻的决策 setpoint_power(k) = x_opt(1); end注意这里有个小技巧:虽然每次滚动优化求解了未来horizon时段的决策,但只有第一个时段的决策真正下发执行,下一轮重新用新的实测数据算一遍。这就是"滚动时域控制"(receding horizon control)的工程实现方式。
5. 算例分析:冰蓄冷如何改变调度结果
模型建好、代码调通之后,最有意思的部分就是看结果了。我跑了一个典型夏日的算例,把冰蓄冷的调节优势量化出来。这里把关键结果和背后逻辑分享一下。
5.1 典型日负荷与设备参数设置
算例设定在夏季,冷负荷峰值约500kW,电负荷峰值约380kW,光伏装机150kW。微网内燃气轮机额定功率300kW,吸收式制冷机额定制冷量250kW,电制冷机额定制冷量200kW,冰蓄冷主机额定制冷/制冰功率均为100kW,蓄冰槽容量300kWh,最大融冰速率80kW/h。储能电池容量100kWh,最大充放电功率30kW。
分时电价的设置见表1,峰谷价差达到0.85元/kWh。天然气价格为2.5元/m³,燃气轮机发电效率0.35,热回收效率0.45。
5.2 蓄冰槽的"谷充峰放"模式与冷平衡分析
优化结果显示,蓄冰槽的蓄冰量曲线呈现出清晰的"谷充峰放"特征:凌晨1点到6点,电价处于谷时,蓄冰量从初始值稳步上升,到早上6点达到峰值约260kWh;白天10点到14点冷负荷高峰时段,融冰供冷量持续输出,蓄冰量逐步下降;到傍晚18点左右蓄冰量回到初始值附近,正好满足日循环约束。
从冷功率平衡角度看,融冰供冷量在峰时段的贡献率大约占冷负荷的20%~30%。别小看这20%多,它直接削减了白天电制冷机的耗电量。冷负荷峰值靠吸收式制冷加上融冰供冷就能覆盖大部分,电制冷机只需要在极端时段补充出力,显著降低了峰时购电需求。
5.3 经济性优化与分时电价的联动机理
从经济性角度,我对比了有/无冰蓄冷的两种运行结果:
| 方案 | 购电成本(元) | 燃气成本(元) | 运行维护成本(元) | 总运行成本(元) |
|---|---|---|---|---|
| 无冰蓄冷 | 1850.6 | 2420.3 | 186.2 | 4457.1 |
| 含冰蓄冷 | 1572.8 | 2385.7 | 193.5 | 4152.0 |
含冰蓄冷方案总成本降低约6.8%。成本降低的主要来源是购电成本下降了约15%,因为夜间谷时电价购电制冰、白天峰时电价少买电,价差收益直接反映在购电成本里。燃气成本变化不大,因为燃气轮机整体发电量变化不大,只是出力曲线变得更平缓。运行维护成本略增,因为冰蓄冷设备增加了运行小时数,但增量很小。
需要强调一点:这个6.8%并不是一个固定的结论,它高度依赖于设备容量配比和峰谷电价差。电价差越小,冰蓄冷的收益空间越小;蓄冰槽容量相对冷负荷峰值越小,转移冷量的能力越弱。做参数敏感性分析时可以考虑扫描峰谷价差和蓄冰槽容量两个参数,画出经济性热力图,写论文时很有说服力。
5.4 日前与日内结果的偏差为什么存在
对比日前调度结果和日内滚动优化结果,会发现两者不完全一致。前天预测的光伏出力在当天10点到12点出现了约15%的偏差——实际光照比预测强,光伏出力更高。日内滚动优化检测到光伏出力增加后,自动减少了燃气轮机的出力和电网购电量,保证电功率平衡不被破坏。
与此同时,冷负荷的实际值比预测值高了一点,日内滚动通过加大融冰供冷量来弥补,不需要额外启动电制冷机。这个动作的合理性在于,融冰供冷只受蓄冰量余量限制,成本近乎为零,而电制冷要消耗高价电能。如果没有日内滚动,死板执行日前计划,就会在光伏出力波动时段出现功率失衡,产生不必要的成本。
这个案例很好地说明了多时间尺度调度的价值:日前调度负责"方向",日内滚动负责"校准",实时反馈负责"托底",三者配合才能在真实运行中兼顾经济性和可靠性。
6. 代码调试中我踩过的几个坑和解决办法
最后这部分全是实操教训,每个坑都是我拿时间换来的,写出来给大家省点排查时间。
6.1 蓄冰量状态变量的索引对齐问题
前面提过Q_store有25个点而其他变量有24个时段,索引对不齐会直接导致约束维度错误。我在调试时用Yalmip的plot()命令看变量维度,最快捷的定位方式是在命令行输入size(Q_store)和size(Q_melt),确认维度后再检查每个约束。模拟优化结果里蓄冰量如果出现"阶跃跳变",大概率就是动态方程某个系数写错了,最常见的是漏了自损系数eta_loss或者充冷效率eta_ice没乘上。
6.2 二元变量格式化导致求解时间爆炸的规避手段
MILP求解时间爆炸的根因基本是二元变量太多。我采用的规避手段是"分层决策":日前调度做完整的MILP,日内滚动固定启停状态变成LP(只有连续变量),实时层直接用比例控制器。这是工程上的妥协,但效果很好。如果你在论文里必须强调日内滚动也是MILP,可以只在某些特殊时段(如冷负荷从平段转峰段)允许调整开停,其他时段冻结。
6.3 目标函数量纲不一致的坑
燃气成本的单位是元,购电成本的单位也是元,但如果设备维护成本按元/MWh算、而负荷数据是kW,不换算就会出现数量级差异,优化器会优先优化数字大的部分,结果看起来"正确"实则偏离真实最优。我在代码里所有数据统一转换为kW和元后再进入优化器,成本系数统一为元/kWh。这个看似不起眼的问题,是新手最容易犯且最难排查的错误。
6.4 参数敏感性不好用:配置文件的集中管理
整套代码里有几十个设备参数和价格参数,如果散落在各个脚本里,改参数时要翻好几个文件。我建议把所有参数集中到一个data_input.m脚本里,每个参数写清楚单位和含义,用结构体组织。代码工程化之后,做场景对比分析(比如换一组电价、换一组负荷数据)就只需要动一个文件,不仅效率高,还不容易出错。
有个参数值得单独提醒:蓄冰槽自损系数 ( \eta_{loss} ) 我设的是0.02,但不同文献取值差异很大,从0.01到0.1都有。过高的自损会显著降低夜间制冰的积极性,因为存到白天的冷量"损耗太多不划算"。做敏感性分析时建议优先扫这个参数,它对冰蓄冷运行策略的影响非常敏感。
7. 后续可以怎么扩展这套代码
这套框架本身的扩展性很强,我自己的下一步想法是基于蓄冰槽双工况主机的精细化模型做改进——当前模型里制冰工况和制冷工况的切换是理想化的,实际切换需要吹扫、阀门切换等过渡时间,在分钟内会造成冷量供应中断。把这个过渡过程建模进去,日内滚动优化的鲁棒性会更好。另外,在不确定性处理上,目前的滚动优化还是确定等价方法,没用到场景法或鲁棒优化。如果换到置信区间比较宽的预测场景,可以考虑在约束里加入鲁棒调节参数,对冷负荷偏差形成更强的抗扰动能力。
回到最初的问题:含冰蓄冷空调的冷热电联供型微网多时间尺度优化调度,核心价值不在于模型有多复杂,而在于你是否真正理解了三种能量在时间维度上的耦合逻辑。冰蓄冷让冷量有了"库存",多时间尺度让计划有了"弹性",这两点组合起来,才是这套系统应对真实运行不确定性的底气。希望这篇文章能帮你把模型写对、代码跑通。