1. 项目概述与研究动机
1.1 冷热电多微网系统优化为什么值得做
我最初接触这个项目,是因为手头有好几个微网优化调度的活儿——传统做法是把每个微网单独建模、单独优化,算出来结果也“挺合理”,但放到实际园区里一看,问题就暴露了:燃气轮机闲置率高、余热浪费严重、电制冷机挤在峰时和高电价撞车。说白了,单一微网就像单打独斗的小作坊,资源利用率天花板很低。
后来看到“共享储能电站”这个思路,我一下子觉得路子对了。它的核心逻辑很朴素:与其每个微网各自买一块电池、各自承担高额投资和运维成本,不如由一个集中的储能电站统一提供充放电服务,多个微网按需租用容量、按实际电量结算费用。这个概念放到冷热电多微网系统里,就形成了本项目要研究的问题——储能电站服务模式下的多微网双层优化配置。
双层优化配置,拆开来看是两个层面的决策问题。上层是储能电站的规划者,决定储能系统的额定容量、额定功率,以及向微网收取的容量租赁费用和电量服务费用;下层是各微网的运行者,在给定储能服务价格和自身设备参数的前提下,优化各自的冷热电联供系统出力与购电策略。上下层之间存在博弈关系——上层的配置方案会影响下层的运行决策,而下层的运行结果反过来又决定上层储能服务的收益。这种“配置—运行”耦合的问题,用单层模型根本描述不了,必须上双层框架。
1.2 这个坑我替你们踩过了
坦白说,这类项目在算法和建模上坑不少。我最初就拿自己的微网数据试过直接套用传统的单层容量优化模型,把储能投资成本和微网运行成本加权成一个目标函数,结果算出来的储能不能说完全没用,但总是“看起来大而空”——额定容量虚高、实际调用率很低,原因是模型根本没刻画储能电站与微网之间的服务交易关系,一旦计入容量租赁费用,微网立刻会调整用电行为,储能利用率就掉下来了。
所以后来做这个题目,我给自己定的路线是:先吃透双层规划的理论框架(KKT条件转化、强对偶、Big-M线性化),再把它落到MATLAB代码上,用YALMIP调用求解器解混合整数线性规划。整个项目做下来,涉及储能容量配置、共享服务定价、多微网冷热电协同调度、双层模型重构、求解器调试等一系列环节。这篇文章就把我从建模到代码实现、再到结果分析的完整过程摊开来写,该踩的坑、该避的雷都给它交代清楚,希望能给正在做类似题目的人省点时间。
2. 整体建模思路与双层框架设计
2.1 为什么必须是双层不是单层
这个问题的答案,其实一句话就能说透——决策主体的利益不一致。储能电站想多收服务费、想让自己的投资尽快回收;微网想压低用能成本、尽量减少对外部的依赖。这两个目标互相拉扯,单层模型里的目标函数只能把两者按权重捏在一起,捏出来的结果既不代表储能电站的真实收益最大化,也不代表微网的真实用能成本最小化,更谈不上博弈均衡。
举个具体例子。假设某个微网中午光伏大发、电用不完,如果储能服务费定得低,它可能愿意把多余的电存起来晚上用或者卖给别的微网;如果服务费定得高,它宁可让光伏弃掉也不愿“替储能电站打工”。这个行为转变,单层模型是捕捉不到的,因为单层模型里面微网没有独立的“决策意志”,只是目标函数里的一堆变量而已。双层模型把微网作为下层优化问题单独刻画,储能电站的价格信号会真实影响微网的策略,微网的响应又会回到上层约束里参与储能收益计算——这才是现实中“服务定价—用户响应”的完整闭环。
从数学形式上看,本项目采用的标准双层结构是这样的:
- 上层(储能电站)决策变量:储能额定容量 (E_{ess})、额定功率 (P_{ess})、容量租赁单价 (c_{cap})、电量服务单价 (c_{ser});
- 上层目标函数:储能电站年净收益最大化,即服务收入扣除投资与运维成本;
- 下层(各微网)决策变量:各时刻燃气轮机出力、余热回收量、电制冷/吸收式制冷分配、与储能电站的交互功率、与外网购售电功率;
- 下层目标函数:微网年运行成本最小化,包含燃料费、购电费、储能服务费、设备运维费,扣除售电收益。
上层的容量配置变量进入下层模型时是参数;下层的购电量和储能交互量回到上层模型时也是参数。上下层之间通过“服务价格 + 交互功率”这条纽带紧密耦合。
2.2 储能电站服务模式的经济机制
储能电站在这里不是“卖设备”,而是“卖服务”,这一点决定了整个模型的交易结构。结合目前工程上常见的共享储能运营模式,本项目设计了两种服务费用:
第一种是容量租赁费。每个微网根据自己的高峰负荷特性和调峰需求,向储能电站租赁一定容量,按年或按月支付租金。这笔费用对应储能电站的容量投资成本回收,所以建模里把它设计成与储能额定容量线性相关,同时也和各微网的“最大占用容量”相关。
第二种是电量服务费。微网每次向储能电站充电或从储能电站放电,按实际吞吐电量付费。这笔费用对应储能电站的运行维护成本和充放电损耗补偿,建模时按充放电功率积分折算成年度费用。
这个机制的好处是,储能电站的收益不完全取决于自己装了多少电池,而取决于微网真正“用了多少服务”。下层微网自然会在“多用储能 vs 自建机组 vs 外网购电”三者之间做经济比较,价格定高了用户就少用,定低了储能电站就亏本。这个博弈关系不建模,后续容量配置就没有经济意义上的最优解。
2.3 冷热电多微网的设备模型概览
既然是冷热电三联供系统,微网内部的能量流就比纯电微网复杂得多。我用的标准结构是:燃气轮机发电,高温烟气经余热锅炉回收,一部分供给吸收式制冷机做集中供冷,一部分通过换热器供热,同时配备电制冷机和燃气锅炉做冷热缺口补充,电负荷可由燃气轮机、储能电站与外网购电联合满足。多微网之间通过公共联络线相连,实现电量互济。
为了不让模型膨胀到不可解,我在设备建模上做了几个饥工程化近似——这些近似也是业界普遍做法:
- 燃气轮机的发电效率和热电比按固定值处理,不建非线性效率曲线;
- 余热锅炉、换热器的换热效率取常数;
- 吸收式制冷机和电制冷机的能效比(COP)按季节取典型值;
- 储能电站的充放电效率分别取常数,忽略SOC对效率的影响。
这套近似下来,整个模型的约束基本都能保持线性,为后面的双层转化和MILP求解打下基础。如果你想把模型做得更精细,比如引入燃气轮机部分负荷效率曲线,那就要处理非凸非线性项,求解难度会呈指数级上升,不推荐作为第一篇论文的起点。
3. 上层配置模型的核心逻辑与目标函数
3.1 上层决策变量和目标函数拆解
上层模型的变量设置是第一步,也是最关键的一步。我这里用了四个决策变量:储能额定容量 (E_{ess})、额定功率 (P_{ess})、容量租赁单价 (c_{cap})、电量服务单价 (c_{ser})。
上层目标函数写成年净收益最大化:
[ \max ; F = \sum_{m \in M} (c_{cap} \cdot Q_{m}^{cap} + c_{ser} \cdot E_{m}^{ser}) - (c_{inv}^{E} \cdot E_{ess} + c_{inv}^{P} \cdot P_{ess}) - C_{om} ]
其中 (Q_{m}^{cap}) 是微网m实际占用的储能容量,(E_{m}^{ser}) 是微网m全年通过储能电站消纳/补充的电量,(c_{inv}^{E}) 是储能容量单位投资成本折算到每年的值,(c_{inv}^{P}) 是功率单位投资成本折算值,(C_{om}) 是年运行维护成本。
这里有个容易忽略的点:容量租赁费是根据“实际占用容量”算的,而不是直接按储能总额定容量算。如果所有微网都在同一时刻满负荷用储能,那实际占用容量就等于总额定容量;但现实是微网的用能行为有差异、有错峰,实际占用容量往往低于额定容量。这就是共享储能的“容量红利”——多个用户的峰值需求错开,储能电站可以用较小的额定容量服务较多的用户,多出来的容量收益就是共享模式的利润空间。
为了算出这个空间,我在代码里专门加了一个逻辑:下层模型返回每个微网逐时段的储能交互功率 (p_{m,t}^{ch}, p_{m,t}^{dis}),上层再统计逐时总占用容量 (\sum_m (p_{m,t}^{dis})) 的最大值作为实际需要的额定容量参考值,同时统计年度吞吐电量。这两个统计量直接决定了储能服务收入,也决定了上层投资是否划算。
3.2 上层约束条件的设计细节
上层约束主要有三类。
第一类是储能电站自身的物理约束。额定容量必须大于等于任意时刻所有微网放电功率之和,也就是:
[ \sum_{m \in M} p_{m,t}^{dis} \le P_{ess}, \quad \forall t ]
同时充电功率也有对称约束 (\sum_m p_{m,t}^{ch} \le P_{ess})。容量约束类似,任意时刻储能SOC不能超过额定容量:
[ SOC_t \cdot E_{ess} \le E_{ess} ]
这里的 (SOC_t) 是0到1之间的状态变量,由储能电站统一调度。我实际建模时,把储能电站当成一个集中式设备处理,微网只提“充放电需求”,储能电站内部再决定每个时刻的实际充放电量和SOC轨迹。
第二类是价格约束。为了防止储能电站报出离谱价格,我加了一个价格上限约束:(c_{cap} \le c_{cap}^{max}), (c_{ser} \le c_{ser}^{max})。这个上限通常参考微网自建储能的全生命周期成本来设定,逻辑是“你的服务费不能比用户自己建电池还贵,否则用户凭什么租你的”。
第三类是投资回收约束。储能电站的期望投资回收期给定为 (N) 年,那么年净收益必须大于等于总投资除以回收期:
[ \sum_{m} (c_{cap} \cdot Q_{m}^{cap} + c_{ser} \cdot E_{m}^{ser}) - C_{om} \ge \frac{C_{inv}}{N} ]
这个约束在优化里非常重要。没有它,上层目标函数会把价格压到极低来诱导微网多用电量,结果总收入可能连投资都覆盖不了,算出来的配置方案在工程上毫无意义。
3.3 为什么目标函数不能等单层加权
这块我说点个人体会。早期我图省事,把上层收益和下层成本放在同一个目标函数里,做成典型的多目标加权问题,结果出现了一个非常尴尬的现象:储能容量总是往大里配,但储能利用率(实际吞吐量/额定容量年化值)很低。因为权重一旦偏向投资方,模型为了让上层收益“显得高”,更倾向于多装电池收租赁费,而不管微网实际用不用——用户侧的真实响应被吃掉了。
后来我改成严格的双层博弈框架,这个现象自然消失了。因为下层的微网运行优化会“对抗”上层的定价策略,畸高的价格直接导致微网减少储能交互量,上层收益反而下降。最终解出来的价格和容量,都是博弈均衡点附近的方案,比单层加权靠谱得多。
4. 下层运行优化模型与冷热电平衡约束
4.1 微网目标函数与运行变量
下层模型是三个微网(也可以扩展成更多个)各自独立的运行优化问题。每个微网的目标函数是年运行成本最小化,细化到典型日再乘以天数折算成年值。我这里用四季典型日,每季取一个代表日,共四个典型日,每日24个时段。
微网m的成本项包括:
- 燃气轮机燃料费:由天然气耗量按热值折算,和发电功率 (p_{gt,t}) 线性相关;
- 外购电费用:从主网购电功率 (p_{buy,t}) 乘以分时电价;
- 储能服务费:充电电量付服务费 (c_{ser} \cdot p_{ch,t}),放电电量付服务费 (c_{ser} \cdot p_{dis,t}),容量租用费 (c_{cap} \cdot Q_m^{cap});
- 设备运维费:各设备出力乘以单位运维成本;
- 售电收益:向主网或相邻微网售电的收益,作为负成本。
决策变量包括逐时段燃气轮机发电功率、余热分配比例(供热 vs 供冷)、吸收式制冷机出力、电制冷机出力、燃气锅炉出力、储能交互功率、购售电功率。如果一个微网里还自带分布式光伏,再加一个光伏出力变量,按预测曲线给定即可。
4.2 冷热电三种能量平衡约束
能量平衡是下层模型的核心约束,写错一个符号整个模型就跑飞。我分三条来写:
电平衡:
[ p_{gt,t} + p_{pv,t} + p_{dis,t} + p_{buy,t} = p_{eload,t} + p_{ch,t} + p_{ec,t} + p_{sell,t} ]
左边是电来源(燃气轮机 + 光伏 + 储能放电 + 购电),右边是电去向(电负荷 + 储能充电 + 电制冷机耗电 + 售电)。
热平衡:
[ \eta_{he} \cdot H_{gt,t} + Q_{gb,t} = Q_{hload,t} ]
其中 (H_{gt,t}) 是燃气轮机余热产出,(\eta_{he}) 是换热器效率,(Q_{gb,t}) 是燃气锅炉补热。这里我把“供热”和“供冷”分开了,余热可以分流到换热器供暖或吸收式制冷机供冷,所以还需要一个分配系数约束 (H_{gt,t} = H_{gt,t}^{heat} + H_{gt,t}^{cool})。
冷平衡:
[ COP_{ac} \cdot H_{gt,t}^{cool} + COP_{ec} \cdot p_{ec,t} = Q_{cload,t} ]
左边第一项是吸收式制冷机产冷量(由余热驱动),第二项是电制冷机产冷量,右边是冷负荷。这个约束必须写对单位,热量的单位统一用kW,COP是纯系数,电制冷机的输入是电功率,乘上COP才是冷量,别写反了。
4.3 设备运行约束的Bound处理和逻辑
除了平衡约束,设备还有出力上下限和爬坡约束。我通常这样处理:
燃气轮机的出力区间设为额定功率的30%到100%,下限30%是为了避免低负荷工况下燃烧效率恶化。爬坡约束按每分钟2%额定功率处理,折算成每小时的爬坡量。这个约束在冷热联供系统里特别重要——如果不加爬坡约束,优化器会让燃气轮机在相邻两个时段剧烈跳变,结果虽然是“数学最优”,但工程上完全不可行。
储能交互功率有上下限,同时充放电不能同时进行。这个逻辑约束在MILP里用二元变量实现:引入 (\delta_t \in {0,1}),充电时 (\delta_t = 1),放电时 (\delta_t = 0),然后写两个约束:
[ 0 \le p_{ch,t} \le \delta_t \cdot P_{ess}^{max} ]
[ 0 \le p_{dis,t} \le (1-\delta_t) \cdot P_{ess}^{max} ]
这样就能保证同一时刻只有一种状态。类似地,购售电也不能同时发生,用一个二元变量控制购电和售电的互斥关系。
燃气轮机的余热分配也引入了类似的逻辑——余热用于供热或供冷,可以同时进行,所以不需要互斥变量,只需要分配系数在0到1之间连续变化即可。
5. 双层模型求解方法与MATLAB代码实现
5.1 KKT条件转化的完整流程
双层模型不能直接用求解器算,必须先把它改写成单层。业界最常用的方法是:把下层问题的KKT条件作为约束加入上层问题,再用强对偶定理处理目标函数里的双线性项。
具体到本项目,下层问题是一个线性规划(变量连续,约束线性),所以KKT条件可以精确写成线性等式和不等式组。我分这几步操作:
第一步,写出下层问题的拉格朗日函数,把所有等式约束和不等式约束都乘上对偶变量。注意这里要对偶变量设置规则:等式约束对应自由对偶变量,不等式约束对应非负对偶变量。
第二步,写KKT条件:
- 平稳性条件:拉格朗日函数对各决策变量的偏导数为0;
- 原始可行性:所有决策变量满足原始约束;
- 对偶可行性:不等式对应的对偶变量大于等于0;
- 互补松弛条件:对偶变量与相应不等式约束的松弛量乘积为0。
第三步,处理互补松弛条件。这个条件是典型的非线性项——对偶变量乘以松弛量等于0,没法直接丢给MILP求解器。标准做法是用Big-M法线性化。引入二元变量,把每对互补条件拆成两个带大M的不等式。这里的M取值要小心,太小会把最优解切掉,太大又会影响求解数值稳定性。我一般根据变量的物理边界来确定——比如功率变量最大不超过额定功率的1.2倍,那么M取这个边界值的10倍就差不多了。
第四步,处理上层目标函数里的双线性项。上层目标是 (c_{ser} \cdot E_{m}^{ser}),而 (E_{m}^{ser}) 本身由下层变量决定,两个变量相乘就是非线性项。用强对偶定理,下层最优目标值等于其对偶问题最优目标值,可以把下层运行成本以对偶变量的形式表达出来,从而消去乘积项。具体推导有点长,这里不展开,核心操作是在目标函数里用“原始问题目标值 = 对偶问题目标值”这个等式替换,把含有上下层变量乘积的项消除。
第五步,把改写后的单层MILP交给求解器。我用的MATLAB + YALMIP + Gurobi组合,YALMIP提供建模接口,Gurobi负责求解。整个转化过程在代码里其实就是几个循环:先写上层目标和约束,再写下层KKT条件,然后逐条线性化,最后拼成一个大优化问题。
5.2 MATLAB代码架构设计
代码我按功能模块拆成了五个脚本,这样调试的时候不用从头跑到尾,哪块出问题就单跑哪块。
第一个是data_input.m,负责读入所有基础数据:微网电热冷负荷曲线、光伏出力曲线、分时电价、天然气价格、设备参数、储能技术经济参数。数据全部用Excel存放,MATLAB里用readtable读进来,后续所有模块共用同一个数据接口。
第二个是build_lower_model.m,构建下层微网运行优化问题。输入是上层给定的储能价格和容量参数,输出是一个YALMIP优化对象。这里我用的是YALMIP的optimize接口,逐条写约束,所有变量先统一用sdpvar声明。
第三个是build_upper_model.m,构建上层优化问题。这个脚本做了大部分脏活累活——把下层KKT条件写成约束、做Big-M线性化、调用强对偶替换目标函数项。写这个脚本的时候我吃了不少苦头,尤其是Big-M的选取,调了很多轮才找到稳定的参数区间。
第四个是solve_bilevel.m,组装上下层模型并调用Gurobi求解。求解结束后,把储能容量、价格、各微网逐时段机组出力和储能交互量全部导出到一个结构体里,方便后续画图和统计。
第五个是plot_results.m,可视化。负责画储能SOC曲线、微网电热冷平衡图、成本累积柱状图、价格灵敏度曲线等。
代码里我用了一个关键技巧:把下层KKT条件里的互补约束统一收集到一个cell数组里,循环生成Big-M约束。这样做的好处是微网数量变化时,只需要在数据文件里增加一行,模型程序不用动。
5.3 YALMIP建模与求解器配置注意事项
YALMIP建模这块有几个容易踩的坑。
第一个坑是变量类型。储能SOC、功率这些是连续变量没问题,但互斥充放电的二元变量一定要显式声明binvar。如果你偷懒用sdpvar再加0-1约束,求解器可能识别不了,解出来的结果充放电同时非零,看着就离谱。
第二个坑是求解器设置。Gurobi的MIPGap参数我习惯设为0.01(即1%的gap),因为这类工程问题不需要精确到小数后四位。MIPGap设太小会让求解时间暴涨,尤其下层模型有几百个二元变量的时候,差距非常大。我实测过同一个模型,MIPGap从1e-4放宽到1e-2,求解时间从40多分钟降到3分钟左右,目标值只差了不到0.3%。
第三个坑是数值尺度。储能容量动辄几十MWh,而服务单价可能是几分钱每千瓦时,两个量放在同一个目标函数里,差了好几个数量级,求解器数值性容易出问题。我的处理办法是统一单位:容量用MWh,功率用MW,价格用元/kWh但电量统一折算成MWh,这样目标函数各项的量级就相对接近了。如果某一项的量级和其他项差超过1000倍,就要考虑换单位或者加缩放系数。
第四个坑是Big-M参数。M太大会导致求解器数值误差变大,M太小又可能剪掉真最优解。我的经验是先让模型不带互补条件跑一遍下层问题,看每个约束松弛量的实际最大值,再用这个最大值乘以安全系数来定M。调试时我写了个脚本专门输出每个互补对的松弛量范围,这样定M就有依据了。
5.4 求解时间与计算效率的经验数据
我用三个微网、四个典型日、每典型日24个时段的标准算例跑了一下,最终的单层MILP大约有3000多个连续变量、400多个二元变量、9000多条约束。在Gurobi默认参数下,MIPGap设为1%时求解时间在2分钟到5分钟之间,换成MIPGap=0.01%则要半小时以上。如果你的算例规模更大,建议优先检查能不能通过调整典型日数量来降维——四个典型日已经能抓住季节差异,用12个月份代表日会让变量数翻三倍,求解时间经常超过1小时,精度提升却很有限。
6. 案例设计、结果分析与灵敏度讨论
6.1 基础算例参数设置
我设计了一个包含三个微网的园区级算例。微网A偏工业,电负荷大、冷负荷集中在白天;微网B偏商业,冷热负荷峰出现在午后到傍晚;微网C偏居民,早晚双峰特征明显。三个微网的负荷曲线错峰,正好能体现共享储能“容量复用”的价值。
储能电站的技术经济参数:锂电池单位容量投资成本取1200元/kWh,单位功率投资成本取800元/kW,按10年折旧、年利率5%折算成等年值;运行维护成本取初始投资的2%;充放电效率分别取0.95和0.95。分时电价按当地一般工商业峰谷电价设置,峰段1.1元/kWh、平段0.65元/kWh、谷段0.35元/kWh。
6.2 典型日运行结果解读
算出来之后,我先看微网A在夏季典型日的逐时出力图。燃气轮机基本跟着电负荷走,白天负荷高时满发,余热一部分驱动吸收式制冷机供冷、一部分换热供热;傍晚负荷降下来之后,电制冷机开始补冷缺口。储能电站的充放电曲线显示,它在夜间谷时段充电、白天峰时段放电,基本做到了“谷充峰放”——这就是储能服务的核心价值。
容量复用方面,三个微网的最大储能占用时刻明显错开:微网A在上午10点到下午2点占用高,微网B在下午4点到晚上8点占用高,微网C在早高峰7点到9点占用高。三个微网虽然都对储能“有需求”,但峰值错开了,所以总额定容量只需要达到单个微网最大需求的大约1.6倍,而不是直接相加的3倍。这个数字直接体现了共享储能的经济性来源。如果你算出来的容量复用系数特别差(接近各微网容量之和),说明定价机制不对或者负荷曲线设计不合理,要回头检查下层约束有没有写错。
6.3 共享储能 vs 各微网独立配置储能的对比
为了验证模型价值,我跑了一个对照组——三个微网各自单独配置储能,不共享,不搞服务费,各自投资各自运行。结果显示:独立配置下总储能容量需求是共享模式下的1.87倍,年化总成本高出约21%。成本差异主要来自两个地方:一是独立配置需要各自承担容量冗余,二是独立储能的利用率低,很多时段电池闲着但折旧照算。共享模式下储能电站通过错峰复用提高了设备利用率,服务费收入足以覆盖投资还能产生利润,微网支付的费用也低于自建储能的年均成本——这是一个双赢的结果。
6.4 价格灵敏度分析的重要发现
我做了两个灵敏度分析:一是固定容量不变,看电量服务费从低到高变化时微网储能利用量和储能电站收益的变化;二是固定价格,看储能容量从1MWh扫描到20MWh时各方成本的变化。
第一个分析有个非常有意思的现象:电量服务费提高初期,储能电站收益反而下降。原因是价格一涨,微网立刻减少充放电量,虽然单次服务费高了,但总交易电量掉得更多,收益反而受损失。价格再高到一定程度,微网干脆基本不碰储能了,此时储能只剩下容量租赁收入。这个转折点就是价格上限的工程参考值。
第二个分析显示,储能容量从5MWh增加到8MWh的过程中,微网总成本下降明显,因为更多的谷时电量可以被搬到峰时用;但超过10MWh之后,成本下降曲线变得平缓,储能电站投资回收压力反而增大。这个“容量-成本拐点”就是上层优化最后收敛到的范围,也说明双层模型确实能跑出比较合理的经济边界。
7. 常见问题与代码调试经验
7.1 求解器报“Infeasible”的排查思路
双层模型改成单层MILP以后,最常遇到的就是Gurobi或YALMIP直接告诉你“问题不可行”。第一次遇到这个问题我差点把代码推倒重写,后来总结了一套排查套路,按这个顺序查,基本半小时内能定位。
第一步检查KKT条件有没有漏写或写错对偶变量的符号。尤其是等式约束对应的对偶变量,必须是自由变量(即没有非负限制),我当初就是在这一步把等式约束的对偶变量写成了非负,导致收敛不到可行解。
第二步检查Big-M约束的M取值。互补条件线性化这里最容易出数值问题。建议写个脚本,单独打印每个互补对对应的原始约束松弛量范围,然后检查M是否覆盖了所有可能取值。M不足的话,互补条件会被错误激活,导致可行性丢失。
第三步检查设备出力上下限和平衡约束之间是否有矛盾。常见错误是冷负荷大于所有制冷设备最大出力之和,或者热负荷大于燃气轮机余热与锅炉最大出力之和。这种问题数据文件里很容易埋着,查约束时先把每个平衡节点的最大供给能力算出来,和负荷对比一眼就能看出来。
第四步把二元变量全部暂时改成连续变量,跑一遍LP松弛。如果LP松弛都不可行,说明约束本身冲突;如果LP松弛可行但MILP不可行,问题大概率出在互斥逻辑或者Big-M上。
7.2 双层模型收敛到非最优解的排查方法
有时候求解器不报错,但解出来的结果明显不合理——比如储能电站的收益是负的但容量还很大,或者微网在电价高峰时疯狂购电而不是用储能。这种时候不要急着调参数,先检查强对偶替换是否用对了。
我吃过最大的亏是强对偶条件里有一项符号写反了。下层目标函数里有购电成本正项、售电收益负项,强对偶等式必须把每一项都对上,一个符号错了,整个上层收益计算就全偏了。检查技巧是取一个已知的小规模测试案例,把下层模型单独用求解器跑一遍,得到最优目标值,然后代入强对偶等式两边,看是不是严格相等。如果不等,逐项对符号。
另一个常见问题是互补松弛条件里的二元变量在求解过程中被赋值随意,导致收敛到不可行的“伪解”。Gurobi的NumericsFocus参数可以设成1或2,提高数值稳定性。如果还不行,检查所有变量的量纲,确保尺度在1e-3到1e6之间,尽量避免极小值项。
7.3 典型报错信息速查表
我整理了一张速查表,基本覆盖了这个项目调试过程中常见的报错情况:
| 报错/异常现象 | 可能原因 | 排查方向 |
|---|---|---|
| YALMIP报“No suitable solver” | 没装Gurobi或没有配置路径 | 检查yalmiptest输出,确认Gurobi被识别 |
| 提示“Index exceeds array bounds” | 典型日数或时段数与数据矩阵维度不匹配 | 检查循环变量维度,统一用size()获取 |
| 求解结果充放电同时为正 | 二元互斥变量没写或Big-M太大 | 检查binvar声明和互斥约束 |
| 目标函数值异常大 | 单位不统一,某项量级差太多 | 全部统一到MWh、MW、元/kWh量纲体系 |
| LP松弛可行但MILP无解 | 互斥逻辑约束冲突或M取值不当 | 单独检查每个二元变量对应的约束对 |
| 储能SOC始终为0 | SOC递推约束写错或初始SOC没设置 | 检查SOC(t) = SOC(t-1)更新式及初值 |
| Gurobi求解时间过长 | MIPGap设太紧或二元变量过多 | 放宽MIPGap到1%,考虑合并对称约束 |
7.4 调参心得与实操技巧
最后分享几个代码层面提升效率的小技巧。
第一个是给YALMIP的变量加上注释标签。用sdpvar声明变量时直接给变量命名成有意义的标识符,比如Pgt(1, 24),后面调试时用value()提取结果,一眼就知道是哪个设备的出力,不用去翻建模时写的注释。我曾经用x1 x2 x3这种变量名调试过一次,差点把自己绕晕。
第二个是分段保存中间结果。双层模型的求解不是一蹴而就的,我会先跑一个小规模测试(两个微网、两个典型日),确认模型结构没问题后再放大到完整算例。中途把每个阶段的储能价格、微网成本、KTT对偶变量都存成.mat文件,后续做灵敏度分析时可以直接复用,不用重新求解。
第三个是价格初始值的选择。双层模型本质上是迭代寻优,如果给上层价格一个离谱的初始值,微网的响应也会离谱,求解器要花大量时间在无意义的区域搜索。我用的是“微网自建储能年均成本”作为价格初始值的锚点,让上层在这个锚点附近搜索,收敛快很多,而且结果也接近工程实际。
第四个是尽量用连续变量的线性表达替代二元变量。能不用二元变量就不用,因为二元变量才是MILP求解时间爆炸的根源。比如购电和售电互斥,我用的是分段线性约束加一个二元变量,但这可以用“购电功率大于等于0、售电功率大于等于0、两者之和不超过联络线容量”这种线性约束来近似,省掉一个二元变量。代价是模型精度略微下降,换来的却是求解速度成倍提升,这个trade-off在工程场景下非常划算。
8. 扩展方向与个人操作体会
这项目做完,我自己的几个体会特别想分享。
第一个体会是双层规划这种框架,吃掉了我以前很多“拍脑袋定参数”的时间。以前做微网优化,储能容量基本靠全年最大负荷乘一个同时率系数定出来,再用仿真验证一下,运气好坏成分很大。现在改成双层模型后,容量的经济最优是博弈出来的,微网的真实响应被纳入了决策过程,最后结果自己会说“为什么这个容量是最优的”——这比任何验证仿真都有说服力。
第二个体会是共享储能的服务定价和容量配置永远要放在一起算。单独算容量不考虑定价,算出来没有经济闭环;单独算定价不考虑容量,算出来的价格没有物理约束支撑。这就是为什么这个题目必须用双层框架——上层决策变量覆盖了价格和容量,下层响应又反过来修正上层决策,两者缺一不可。
第三个体会是模型可以继续扩展的方向很多。比如把多微网之间的电量交易做成完全内部化市场,储能电站只是做辅助服务,模型会更贴近未来电力市场的形态。也可以引入需求响应,让微网的负荷不再刚性固定,而是随价格弹性变化,这样下层模型的决策空间会更大,博弈结果也更丰富。但扩展之前,务必先把当前的双层框架吃透、把KKT转化弄熟练了再动手,否则模型复杂度一上来,调试难度是指数增长。
如果你现在正在跑这个题目,我的建议是:先在小规模数据上把模型跑通、理清每一行约束的含义,再逐步放大算例。中途遇到不可行、不收敛的情况,别急着怀疑算法,回到约束本身一条条查——这个项目里90%的问题都是约束写错,不是求解器的问题。