多微网电能互补、需求响应、双层优化,这三个词组合在一起,基本就是一个典型的微网群优化调度问题。第一次看到这个标题的时候,我脑子里浮现的场景是:几个相邻的微网各自有光伏、风电、储能和负荷,单独运行的时候要么弃电、要么买高价电,但通过一条联络线互相借一借功率,再配合用户侧把一部分负荷挪到电价低的时段,整体运行成本能明显降下来。而双层优化解决的是“谁说了算”的问题——上层协调机构制定电价和交换计划,下层每个微网自己做经济调度,这比把所有微网强行合并成一个系统求解更贴近真实工程。
这篇文章我会把整个模型的建模思路、数学表达、MATLAB代码框架、实测案例和调试经验完整讲一遍。如果你正准备做多微网方向的课题,或者正在复现类似的双层优化模型,这篇文章应该能帮你节省不少时间。
1. 项目整体思路与模型设计出发点
1.1 多微网为什么要做电能互补
单微网运行时,系统的功率平衡完全靠自身机组、储能和主网交互来维持。光伏大发的中午,如果本地负荷用不完,多余的电要么卖给主网(电价通常很低),要么直接弃掉;晚高峰时本地机组出力不够,又得从主网高价购电。这种情况并不少见,尤其是当光伏渗透率超过30%以后,午间弃电几乎是常态。
电能互补的做法是在多个微网之间铺设联络线,让A微网多余的电量输送给B微网,C微网晚高峰不够的电量由D微网的储能来补。这样做的好处有两点:一是减少向主网的购售电量,降低交易成本;二是从系统层面提高新能源消纳率,因为某个微网弃掉的光伏电量,很可能正是旁边微网需要的。
在仿真建模中,电能互补需要额外定义两个东西:联络线最大传输功率和微网间交易电价。前者直接约束交换功率上限,后者决定各微网参与互补的意愿。内部交易电价如果定得太低,没有微网愿意卖电;定得太高,买方不如直接从主网购电,系统就退化成独立运行模式。所以在模型里,内部电价往往是上层决策变量,这就是双层优化的第一个触发点。
1.2 需求响应如何进入调度模型
需求响应在微网模型里主要有两种表达方式:价格型和激励型。
价格型需求响应通过分时电价引导用户调整用电行为,用户响应曲线通常用弹性系数来描述。这类模型更贴近经济学视角,但和调度优化结合时,需要额外构建电价和负荷量的函数关系,非线性程度较高。
激励型需求响应更工程化,直接对各种可调负荷建模。典型的有两类:可转移负荷和可中断负荷。
可转移负荷的特点是总用电量不变,但用电时段可以平移。比如电动汽车充电,用户可能接受从晚高峰挪到凌晨,总充电量不变,只是时间窗口变了。建模时需要用0-1变量标记负荷在各时段的启停位置,并约束总转移次数不超过设定上限,避免模型把用户负荷切得七零八落。
可中断负荷则是在系统功率紧张时直接削减一部分负荷,用户获得补偿。建模时把削减量设为一个连续变量,上限是各类可中断负荷的容量,补偿费用按削减功率乘补偿单价计算。需要注意的是,削减负荷不能突破用户忍受限度,一般还要加一个最大削减比例约束,比如不超过总负荷的15%。
需求响应加入模型后,下层微网的调度空间更大——缺电时不再只能买高价电或启动昂贵机组,还可以通过削峰填谷获得更优的调度方案。
1.3 为什么一定要用双层优化
如果只用单层优化,把所有微网的设备、负荷、储能全放进一个目标函数里求最优,计算上没有任何问题,但模型的经济含义会变味。因为在实际中,每个微网是独立运营商,有权决定自己的调度方案,不会无条件服从一个全局协调者的“一体化指令”。上层调度中心可以制定电价和联络线计划,但真正执行时,各微网是在这个电价约束下做自身利益最大化的决策。
双层优化描述的就是这种主从决策关系。上层问题(领导者)确定内部交易电价、微网间的交换功率计划;下层问题(跟随者)根据电价和计划,优化自己的机组出力、储能充放电、需求响应量。上层的决策会影响下层的可行域和目标,下层的最优响应又反过来决定上层目标的实现程度。
这种结构和实际的多微网运营机制非常吻合。在电力市场环境下,聚合商或者区域调度机构没有权力直接控制每一个微网内部的机组,只能通过价格信号和功率计划间接引导。所以用双层模型求解出来的方案,比单层模型更有落地价值,也更符合论文评审或项目验收时对“机制设计”的要求。
2. 上层与下层的数学模型拆解
2.1 上层模型:协调与定价决策
上层模型扮演的角色是区域协调者,目标一般是让整个微网群的系统运行成本最小,或者让新能源消纳率最高。实际建模中,我更倾向把多目标处理成加权单目标,例如系统总成本最小化同时通过弃电惩罚项来兼顾消纳。
上层决策变量包括:
- 微网间交换功率 $$P_{ij,t}$$ ,表示微网 i 向微网 j 传输的功率;
- 内部交易电价 $$\lambda_{ij,t}$$ ,由协调者制定;
- 各微网与主网的交互功率 $$P_{grid,i,t}$$ 。
目标函数:
$$ \min \sum_{t=1}^{T} \left( \sum_{i=1}^{N} c_{grid,t} \cdot P_{grid,i,t} + \sum_{i=1}^{N}\sum_{j=1}^{N} c_{loss} \cdot P_{ij,t} + \sum_{i=1}^{N} \rho_{curtail} \cdot P_{curtail,i,t} \right) $$
约束包括:
- 系统功率平衡:所有微网的净功率加总后与主网交互功率匹配;
- 联络线传输功率限制:$$0 \le P_{ij,t} \le P_{ij}^{\max}$$ ;
- 内部交易电价范围限制:$$\lambda_{ij,t} \in [\lambda_{\min}, \lambda_{\max}]$$ ,防止电价过高或过低导致市场失灵;
- 各微网与主网交互功率限制。
上层模型的核心意义不在于直接调度设备,而是创造一个合理的交易环境。电价定得越高,卖电方越有动力多发电;电价定得越低,买方越愿意从其他微网购电。这个价格信号直接传入下层优化模型,最终影响每个微网的调度决策。
2.2 下层模型:微网自治运行与需求响应细节
每个下层微网都是独立的经济调度问题,目标函数是在给定内部交易电价下,最小化自身运行成本。成本项包括:
- 微型燃气轮机或柴油发电机的燃料成本;
- 储能充放电的损耗成本;
- 从其他微网购电的费用(内部交易);
- 从主网购电的费用;
- 需求响应补偿费用;
- 自身弃电惩罚成本。
以微网 i 为例,目标函数可以写为:
$$ \min \sum_{t=1}^{T} \left[ F_{gen}(P_{g,i,t}) + c_{es,i} \cdot (P_{ch,i,t} + P_{dis,i,t}) + \lambda_{ij,t} \cdot P_{ji,t} + c_{grid,t} \cdot P_{grid,i,t} + c_{dr} \cdot (P_{cut,i,t} + \alpha \cdot P_{shift,i,t}) \right] $$
下层约束相对复杂,基本分六类:
- 有功平衡约束:机组出力、光伏风电出力、储能充放电、购售电、需求响应调整量相加等于负荷;
- 机组出力上下限与爬坡约束;
- 储能SOC动态方程与充放电功率限制;
- 可中断负荷削减量上限;
- 可转移负荷的总能量守恒与最大转移次数约束;
- 与主网交换功率限制。
这里最容易遗漏的是需求响应与功率平衡的耦合。可转移负荷的建模方式决定了它进入平衡方程的形态。如果采用“负荷平移”模型,原时段的负荷减少、目标时段的负荷增加,两边要同时反映在平衡约束里;如果只把总负荷剥离出来再重新分配,就需要额外保证分配后的负荷曲线与原曲线总能量一致。
2.3 层间耦合与KKT条件转换
双层模型不能直接交给求解器求解,必须先做转换。最常用的方法是利用KKT条件把下层问题转成上层问题的约束,形成单层MPEC(Mathematical Program with Equilibrium Constraints)问题。
转换逻辑是这样:下层优化是一个凸规划(线性目标加线性约束,或者凸二次目标),在凸性成立的前提下,它的最优解一定满足KKT条件。KKT条件包括四部分:
- 下层目标函数的梯度条件(拉格朗日函数对各变量求导为零);
- 原始可行性条件(下层所有约束都满足);
- 对偶可行性条件(不等式约束对应的拉格朗日乘子非负);
- 互补松弛条件(乘子与对应不等式约束的乘积为零)。
把KKT条件完整写进上层约束,下层优化就变成上层模型的一组约束,整个问题从“主从博弈”变成单层优化问题。
转换后的模型仍然有两处非线性需要处理:
第一,互补松弛条件中有“乘子乘以约束”的乘积项,需要引入0-1变量和大M法线性化。设某个下层不等式约束为 $$g(x) \le 0$$ ,对应乘子为 μ ,互补条件 μ · g(x)=0 等价于:
$$ g(x) \le M_1 \cdot z, \quad \mu \le M_2 \cdot (1-z), \quad z \in {0,1} $$
大M的取值很关键,太小会错误地削减可行域,太大会导致数值病态。一般建议取该约束原始量级的50到100倍,然后根据求解结果做调整。
第二,上层目标中的电价和下层交换功率相乘项也是双线性项,同样需要通过Big-M或分段线性化处理。如果把内部交易电价当作定值迭代求解,可以避免这个双线性项,但牺牲了优化全局性。我的建议是优先做KKT+大M线性化,形成MILP问题,用商业求解器求解,稳定性和全局性都有保证。
3. MATLAB代码实现思路与关键片段
3.1 建模工具与求解器选型
MATLAB环境下做优化模型,我的首选组合是YALMIP加商业求解器。YALMIP封装了变量定义、约束描述和目标函数表达,代码风格接近数学公式,写起来顺手,可读性也强。求解器可以选择常用的商业MILP求解器,学术许可对高校用户也比较友好。
如果没有商业求解器,也可以用开源的CBC或SCIP,但对大规模双层模型来说性能差距比较明显。我的建议是至少准备一个可用的MILP求解释器,因为双层转单层后的问题规模会膨胀,单纯靠MATLAB内置求解器很难扛住。
YALMIP还有一个很实用的功能是kkt命令,可以直接对下层约束和目标生成KKT系统。这个命令我在实际项目中反复用过,写代码时不需要手动推导几十个乘子和互补条件,减少了很多笔误。需要注意,kkt命令生成的系统默认包含双线性互补约束,必须手动线性化之后才能交给MILP求解器。
3.2 下层模型的标准YALMIP写法
先定义基础参数和变量。下面这个片段是某微网的下层经济调度模型框架:
%% 下层模型:单微网经济调度(YALMIP框架) % 基础参数 T = 24; % 调度时段数 P_g_max = 500; % 微型燃气轮机最大出力(kW) P_g_min = 50; % 最小出力(kW) SOC_max = 400; SOC_min = 40; % 储能SOC上下限(kWh) eff_ch = 0.95; eff_dis = 0.95; % 决策变量 P_g = sdpvar(T,1); % 燃机出力 SOC = sdpvar(T+1,1); % 储能SOC P_ch = sdpvar(T,1); % 储能充电功率 P_dis = sdpvar(T,1); % 储能放电功率 P_cut = sdpvar(T,1); % 可中断负荷削减量 u_ch = binvar(T,1); % 充电状态0-1变量 u_dis = binvar(T,1); % 放电状态0-1变量 % 约束定义 cons = []; cons = [cons, P_g_min <= P_g <= P_g_max]; % 机组出力约束 cons = [cons, P_ch <= u_ch * 80, P_dis <= u_dis * 80]; % 充放电功率上限 cons = [cons, u_ch + u_dis <= 1]; % 不能同时充放电 cons = [cons, SOC(2:T+1) == SOC(1:T) + eff_ch*P_ch - P_dis/eff_dis]; % SOC动态 cons = [cons, SOC_min <= SOC <= SOC_max, SOC(1) == 200, SOC(T+1) == 200]; cons = [cons, 0 <= P_cut <= 60]; % 可中断负荷上限 % 目标函数:运行成本 lambda_p2p = 0.8; % 内部交易电价,上层传下来 c_gas = 0.6; % 燃气成本系数 c_es = 0.05; % 储能损耗成本 c_cut = 1.2; % 需求响应补偿单价 objective = sum(c_gas * P_g + c_es * (P_ch + P_dis) + ... c_cut * P_cut + lambda_p2p * P_buy);这里P_buy表示从其他微网购电功率,在实际代码中由联络线交换变量推导。储能约束里用了两个0-1变量防止同时充放电,这是常规做法,但也会让MILP问题的变量数翻倍。如果对求解速度敏感,可以改成单个0-1变量,充电时置1、放电时置0,配合功率上下界约束也能达到同等效果。
3.3 双层问题整体求解与结果输出
用KKT转换整个双层模型时,YALMIP的kkt命令能省很多事情:
%% 双层模型转单层(框架代码) % 定义下层变量集合 lower_vars = [P_g, SOC, P_ch, P_dis, P_cut, u_ch, u_dis]; % 定义下层约束和目标 lower_cons = cons; lower_obj = objective; % 调用YALMIP生成KKT条件 [KKT_system, details] = kkt(lower_cons, lower_obj, lower_vars); % 把KKT系统并入上层约束 upper_cons = [upper_cons, KKT_system]; % 双线性项线性化(需手动处理,通过大M法将互补条件转为整数线性约束) upper_cons = [upper_cons, linearized_comp_cons]; % 求解 ops = sdpsettings('solver', 'cplex', 'verbose', 2, 'mip.tolerance.mipgap', 0.01); result = optimize(upper_cons, upper_obj, ops); % 结果输出 if result.problem == 0 P_g_opt = value(P_g); P_ex_opt = value(P_ex); ... else disp('求解失败,请检查约束'); end这里必须提醒一下,kkt命令自动生成的互补条件中有双线性项,不要直接丢给求解器,否则问题变成非凸MINLP,大概率求解失败。手动线性化的标准做法是:遍历每条不等式约束的互补条件,引入0-1变量并加两条大M约束。代码写起来有点繁琐,但这是一劳永逸的做法。
最后结果输出建议增加几个关键指标的计算:总运行成本、弃电率、各微网与主网交互电量、需求响应贡献度。这些指标不做特殊处理直接输出原始变量时,可能不够直观,建议单独写一个统计脚本。
4. 典型算例与仿真效果分析
4.1 测试系统结构与参数
我在实际复现这个模型时,搭建了一个三微网测试系统。三个微网的配置有明显差异化,方便体现电能互补的效果:
| 微网 | 分布式电源 | 储能容量(kWh) | 峰值负荷(kW) | 可调负荷 |
|---|---|---|---|---|
| 微网A | 光伏800kW | 400 | 600 | 可中断负荷80kW |
| 微网B | 风电600kW | 300 | 500 | 可转移负荷100kW |
| 微网C | 燃气轮机500kW | 200 | 650 | 可中断+可转移120kW |
联络线最大交换功率设为200kW,内部交易价格区间0.3~1.0元/kWh,主网购电价格采用典型峰谷分时电价,晚高峰时段1.2元/kWh。调度周期取24小时,步长1小时。
这样的配置在参数上有几个小心思:微网A光伏装机远大于负荷,午间必然出现大量盈余;微网B风电出力集中在夜间,晚高峰反而出力不足;微网C有燃气轮机作为支撑,但燃料成本高,尽量少用。三个微网互补性很强,怎么把盈余电量通过联络线送到缺电微网,正是模型要解决的问题。
4.2 三种模式对比与结果分析
我对比了三种运行模式:完全独立运行、只做电能互补、电能互补加需求响应。仿真结果整理如下:
| 运行模式 | 总运行成本(元) | 弃电率(%) | 峰值购电功率(kW) | 联络线利用率(%) |
|---|---|---|---|---|
| 独立运行 | 12840 | 12.6 | 520 | 0 |
| 仅电能互补 | 11360 | 7.3 | 410 | 78.5 |
| 互补+需求响应 | 10520 | 5.8 | 350 | 91.2 |
可以看到,仅电能互补就把总成本压低了约11.5%,弃电率从12.6%降到7.3%。加入需求响应后成本进一步降到10520元,相比独立运行下降约18%。这个结果符合预期,因为可中断负荷削减了晚高峰的高价购电,可转移负荷把部分用电量挪到午间,既消纳了本地光伏,又避免了高峰时段依赖主网。
值得关注的是联络线利用率从78.5%上升到91.2%。说明需求响应不仅改变了负荷曲线,还让微网间的功率交换更加频繁,通道资源得到了更充分的利用。
4.3 结果可视化与论文呈现方式
仿真做完,结果可视化是少不了的。我的建议是至少画四张图:
- 各微网24小时功率平衡图,用堆叠柱状图展示光伏、风电、燃气轮机、储能、购电、需求响应削减等各电源出力,能一眼看出每个时段的电力来源构成;
- 联络线交换功率曲线,标注功率方向和幅值,体现互补过程;
- 储能SOC曲线,检查储能是否出现越限或者频繁充放;
- 需求响应前后负荷曲线对比,展示削峰填谷的实际效果。
论文里呈现时,这三张图加一张结果对比表基本就够了。文字描述部分要强调的结论是:电能互补解决的是“空间上的不平衡”,需求响应解决的是“时间上的不平衡”,两者叠加效果最优。
5. 调试实录:常见问题与避坑指南
5.1 无解与收敛异常
双层转单层后最常遇到的情况就是infeasible problem,模型直接报无解。排查优先级我是这样排的:
先检查所有变量的上下界是否合理。比较典型的错误是储能终点SOC约束写得过紧——傍晚时段没有足够充电功率,导致终点SOC无法回到初始值。我习惯把储能SOC终点约束改成区间约束而不是等式,比如允许在200~240kWh之间,既满足运行要求,又给求解器留出可行空间。
再检查大M取值。大M太小会让互补条件把约束砍掉,变成不可行;太大会让求解器出现数值精度问题,表现为同一个模型不同初始点结果差异巨大。大M的经验值是对应约束最大可能量级的50~100倍,比如某条功率平衡约束最大偏差约500kW,大M取25000到50000之间比较合适。
最后检查电价边界和联络线功率是否匹配。如果内部交易电价上限设置成0.8元/kWh,但主网购电价格低谷只有0.4元/kWh,买方会倾向从主网购电,内部交易就不活跃,某些微网可能因此被迫开启高成本机组,导致系统整体无解或成本异常。
5.2 求解器的选择与稳定性
MILP求解器稳定性差异在实际工程中体会很深。同样一个模型,换求解器之后结果可能不一致,尤其是当MIP gap设置不当时。我的建议是设置合理的MIP gap,比如1%或2%,不要追求绝对最优。多微网调度问题本质上是小时级决策,1%的最优性偏差对运行成本的影响几乎可以忽略,但求解时间可能缩短一个数量级。
如果模型规模在三个微网左右,变量数通常在几千到一万的量级,这个问题商业求解器基本在分钟级完成。但如果微网数量扩展到十个以上,MILP规模会急剧膨胀。此时建议将模型拆成滚动优化,每个窗口只优化未来4~6小时,或者把下层问题进一步分解成多时段独立问题并行求解。
5.3 我的几点实操心得
这套模型我前后调试过至少几个版本,最深的三点体会写在这里:
第一,变量单位和量纲一定要统一。计算成本时电量用kWh还是MWh,电价用元/kWh还是元/MWh,单位不统一是最坑的错误,往往导致计算结果偏差巨大但约束和数值本身看起来正常。我习惯全模型统一用kW和kWh,成本用元,所有系数一并换算。
第二,0-1变量的初始值会影响求解速度。对于储能充放电状态这类0-1变量,可以先用连续松弛模型跑一遍,把结果中明显闲置的时段固定下来,再求解完整MILP,速度能提升30%以上。这个方法在模型规模大、求解时间紧张时特别实用。
第三,不要过度建模。可转移负荷如果每个时段都允许任意平移,模型会出现“把所有负荷堆到最便宜时段”的极端解,这在实际中不可行。加一个最大转移次数约束和单次转移小时数约束很有必要,比如限制可转移负荷最多只允许连续转移3小时,这样结果才符合真实用户行为。
我在实际运行这套模型时,最深的一点体会是:双层优化模型的难点往往不在数学推导,而在“让问题落到求解器里”。多微网调度的核心是把三个问题先想清楚——单位统一、交换功率方向定义、电价更新机制。顺序弄对了,后面就是体力活了。这套模型后续还可以在三个方向上自然扩展:把确定性模型升级为随机优化,考虑新能源出力的不确定性;加入碳交易机制,让碳价进入目标函数;或者把上层问题改成多目标优化,同时兼顾经济性和低碳性。这几个方向都是基于现有代码框架的增量修改,做起来并不困难。