微电网容量配置双层优化建模与遗传算法求解实战
2026/9/16 2:25:53 网站建设 项目流程

干这行的人都有一个共同的感受:综合能源系统规划里最磨人的不是设备选型,也不是潮流计算,而是“到底配多少光伏、配多少储能、配多大的柴油机”这种容量配置问题。拍脑袋定容量,运行起来不是浪费就是缺电;想认真算,又要同时面对投资成本、运行调度、分时电价、不确定性这一堆互相牵扯的因素。最近我在做微电网多电源容量配置时,把双层优化建模重新撸了一遍,才发现这东西看着抽象,一旦把上层决策和下层运行的关系想清楚,代码落地反而比单层模型更自然。这篇东西就把我踩过的坑、试通的思路、能直接跑的代码骨架全部摊开讲。

这篇文章适合正在做微电网规划、综合能源系统设计、或者被“容量配置”这种投资决策问题折磨的研究生和工程师。你不需要提前精通优化理论,我会把双层优化的核心逻辑、为什么上下层要这么拆、Yalmip怎么搭、遗传算法怎么和下层调度耦合这些事讲透。看完你至少能动手改出一版自己的容量配置程序。

1. 项目背景与双层模型思路拆解

1.1 微电网容量配置为什么必须用双层优化

先说说单层模型为什么不够用。常见的单层做法是把投资成本和运行成本揉成一个目标函数,比如总成本等于设备等年值投资加上典型日运行费用,约束里面同时写设备容量上下限、储能SOC、功率平衡,一次求解直接得到容量方案。听起来没问题,但实际操作时你会发现一个致命缺陷:容量和运行是强耦合的,而且运行策略本身不是一次求解能确定的。比如你配了2MWh储能,不同调度策略下,储能的年运行成本、购电费用、弃光率差异巨大;单层模型里如果预先固定了运行方式,等于默认调度员是个傻子,永远按固定规则走,容量结果必然失真。如果你用复杂的运行约束去逼近真实调度,模型规模又爆炸,求解器直接卡死。

双层优化天然适合这种“投资决策在前、运行调度在后”的层级结构。上层决定装多少风机、光伏、储能、柴油机,算出投资成本;把容量参数传给下层。下层拿到这些设备参数后,在满足负荷和电网交互约束的前提下,以日运行成本最小为目标,算出最优调度策略,再把运行成本反馈给上层。两层之间反复迭代,直到上层找到一组容量配置,使得“投资成本+运行成本”的总账最优。这就像你先定一个预算买设备,再在运营中精打细算看怎么用设备最省钱;如果设备买少了,下层的运行成本会很难看,上层自然会在迭代中被淘汰。

1.2 总体方案:上层投资决策、下层运行调度

我的整体设计并不复杂,上层用遗传算法做离散容量寻优,下层用Yalmip调Cplex/Gurobi做线性规划调度。为什么会这样分工?因为容量配置本质是整数规划问题(台数、功率等级都是离散的),而连续变量的运行调度很适合成熟的线性规划求解器。用商用求解器直接解混合整数双层问题很容易撞上非凸、非线性这座墙,而启发式算法在工程界的稳定性和灵活度反而更高。

上层的每个个体就是一组“设备装机方案”,比如光伏500kW、风机300kW、储能600kW/1200kWh、柴油机200kW。这个方案传给下层后,下层会基于典型日数据(负荷曲线、光伏出力曲线、分时电价)求出一个最优运行计划,再把最小运行成本返回给上层。上层把投资成本和下层反馈的运行成本相加,作为这个个体的适应度值。经过多代进化,最终收敛出一组年综合成本最低的容量配置。这套框架最大的好处是解耦:你不用把复杂的运行策略强行塞进一个庞大的优化模型里,下层的运行模型可以做得非常精细,哪怕加上机组启停、SOC约束、购售电策略,都不会拖垮上层寻优。

2. 数学建模与约束设计

2.1 上层模型:等年值投资成本与设备选型

上层的目标函数不是简单把设备价格乘以容量,而是要把投资成本折算成等年值,才能和运行成本放在同一个时间尺度上比较。这里用到了资金回收系数(CRF)。如果项目生命周期是20年,折现率取6%,那么CRF = r(1+r)^n / ((1+r)^n - 1) ≈ 0.0872。也就是说,你现在花100万买设备,平摊到每年大约是8.72万。这个系数很多人第一次算会错,我建议直接用Excel的PMT函数验证一遍。

等年值投资成本可以写成:

C_inv = CRF × (C_pv × N_pv × P_pv_unit + C_wt × N_wt × P_wt_unit + C_bat_p × E_bat + C_bat_e × P_bat + C_diesel × P_diesel)

其中N_pv、N_wt是光伏和风机的安装组数,P_pv_unit是单组光伏的额定功率,E_bat是储能容量,P_bat是储能功率,P_diesel是柴油机容量。注意储能一般按容量(kWh)和功率(kW)分开计价,因为电池电芯和PCS逆变器是两个成本体系。在做设备选型时,我还加了建设面积约束、最大装机比例约束,比如“光伏装机不能超过屋顶可用面积的极限”这类现实条件,这些是实际项目中甲方最关心的硬约束。

2.2 下层模型:分时电价下的运行调度优化

下层运行调度的目标函数是典型日运行成本最小,包含向电网购电费用、柴油机燃料费用,减去售电收益。分时电价是让这个模型变得有意思的关键。我用的典型日数据里,峰时段电价1.2元/kWh,平时段0.7元/kWh,谷时段0.35元/kWh。下层模型会自动在谷时段充电、峰时段放电,体现“低买高卖”的套利逻辑。目标函数写出来:

C_op = Σ_t ( price_buy(t) × P_buy(t) - price_sell(t) × P_sell(t) + fuel_cost × P_diesel(t) )

注意P_buy和P_sell不能同时为正,不然模型会利用买卖价差无限套利。这个问题在Yalmip里通过加一个互斥约束或者设置合理的price_sell小于price_buy来解决。如果售电价和购电价一样,就必须显式加P_buy(t) × P_sell(t) = 0这种非凸约束,不太好处理。我的做法是直接把售电价设为购电价的80%,既符合实际,又天然避开了同时买卖的问题。

下层约束包括功率平衡(光伏+风电+柴油机+储能放电+购电 = 负荷+储能充电+售电)、储能SOC递推方程、SOC上下限、充放电功率限制、柴油机出力上下限和爬坡约束。SOC递推要特别注意效率:充电时 SOC(t+1) = SOC(t) + η_ch × P_ch(t) / E_bat,放电时 SOC(t+1) = SOC(t) - P_dis(t) / (η_dis × E_bat)。如果不把充放电效率写对,储能的套利收益会被高估,配置出来的储能容量会偏大。我实际测试过,效率从90%改成95%,最优储能容量能差出15%左右。

2.3 双层耦合机制:传递变量与可行性判定

上下层交换的数据包括:设备容量(上层传下层)、日运行成本(下层传上层)。但仅传递这些还不够,还需要检查下层模型是否可行。比如上层配了一个很小的柴油机,但极端天气下光伏不出力、储能又放完电,导致某些时段无法满足负荷,下层模型就无解。这种情况下不能简单给个无穷大惩罚值,我习惯返回一个很大的适应度值(比如1e8),同时记录这个个体是“不可行方案”,在遗传算法里直接淘汰。还有一种做法是给下层模型加虚拟电源,以极高成本提供缺电功率,这样即使设备容量不足,下层也能解出来,但运行成本会异常高,上层自然会避开这些方案。这种方法比直接删掉个体更优雅,因为它提供了梯度信息,引导种群往容量足够的方向进化。

下层返回的运行成本是单典型日的,如果只算一天,会以偏概全。工程上更稳妥的是选几个典型日——比如夏季大负荷日、冬季小负荷日、过渡季节日——分别求解,再按每个典型日出现的天数加权汇总成年运行成本。我用的是三个典型日,权重分别取0.3、0.4、0.3。这样虽然计算量翻了三倍,但配置结果在全年运行中不会出现大的偏差。

3. 求解策略与代码实现(手把手)

3.1 遗传算法为什么适合外层、线性规划为什么适合内层

先聊一下求解器选型的逻辑。双层优化经典解法之一是KKT条件转换,把下层问题用KKT条件替换,变成单层带互补松弛约束的问题。这个方法理论上很漂亮,但对下层模型的要求很苛刻:必须凸、必须可导、互补约束处理不好数值上很容易翻车。我的下层模型里有储能SOC递推、柴油机爬坡这些整数或非平滑项,KKT转换后模型复杂度不减反增,实际调试成本很高。所以我的选择是:上层用遗传算法(GA)做离散寻优,下层用Yalmip调Cplex做线性规划求解。GA的每一代可能要调用下层求解器几十次,但每个下层LP问题求解速度快(毫秒级),整体算下来五六分钟能收敛,工程上完全能接受。

3.2 整体代码逻辑架构

代码整体分四层:主程序(遗传算法)、个体容量解析函数、下层运行优化函数、数据输入模块。主程序负责生成种群、计算适应度、选择交叉变异;容量解析函数把一个个体的染色体解码成具体的设备容量;下层运行优化函数接收设备容量和典型日数据,调用Yalmip建模并求解;数据输入模块统一管理典型日负荷曲线、光伏归一化出力、分时电价和成本参数。这样拆的好处是,你想换下层的调度模型,只需要改“下层运行优化函数”,上层完全不用动;想换成别的启发式算法,主程序替换起来也很方便。

我用MATLAB+Yalmip是因为这个组合在学术和工程圈最通用,调试环境也友好。Python的同行可以用Pygmo或者Geatpy做外层,内层用PuLP或者Pyomo调Cbc,逻辑完全一样。这里给出来的是MATLAB版本,注释写得很细,方便你改成自己的数据。

3.3 可运行的MATLAB代码骨架

下面这套代码是简化但仍完整可跑的版本。我在关键位置加了注释,你把它保存成几个m文件就能跑通。

首先是主程序,跑遗传算法:

%% 遗传算法主程序 clear; clc; rng(42); addpath('./data'); % 数据目录 % 加载典型日数据 load('typical_days.mat'); % 包含 load_curve, pv_curve, wt_curve, price_buy, price_sell, day_weight % 遗传算法参数 pop_size = 20; max_gen = 30; crossover_prob = 0.85; mutation_prob = 0.1; % 决策变量编码: [N_pv, N_wt, E_bat, P_bat, P_diesel] % N_pv范围0~10,N_wt范围0~6,E_bat范围0~2000kWh,P_bat范围0~500kW,P_diesel范围0~400kW lb = [0, 0, 0, 0, 0]; ub = [10, 6, 2000, 500, 400]; % 初始化种群 pop = zeros(pop_size, 5); for i = 1:pop_size pop(i,1) = randi([lb(1), ub(1)]); pop(i,2) = randi([lb(2), ub(2)]); pop(i,3) = lb(3) + rand * (ub(3) - lb(3)); pop(i,4) = lb(4) + rand * (ub(4) - lb(4)); pop(i,5) = lb(5) + rand * (ub(5) - lb(5)); end best_fitness = zeros(max_gen, 1); for gen = 1:max_gen fitness = zeros(pop_size, 1); for i = 1:pop_size % 关键一步:调用下层运行优化,取得总成本 [total_cost, op_cost] = evaluate_individual(pop(i,:)); fitness(i) = total_cost; end % 记录最优 [best_fitness(gen), best_idx] = min(fitness); fprintf('第%d代,最优总成本: %.2f 元\n', gen, best_fitness(gen)); % 选择(锦标赛选择) new_pop = zeros(size(pop)); for i = 1:pop_size idx1 = randi(pop_size); idx2 = randi(pop_size); if fitness(idx1) < fitness(idx2) new_pop(i,:) = pop(idx1,:); else new_pop(i,:) = pop(idx2,:); end end % 交叉(单点交叉) for i = 1:2:pop_size-1 if rand < crossover_prob cp = randi(4); % 交叉点 tmp = new_pop(i, cp+1:end); new_pop(i, cp+1:end) = new_pop(i+1, cp+1:end); new_pop(i+1, cp+1:end) = tmp; end end % 变异(随机重置) for i = 1:pop_size for j = 1:5 if rand < mutation_prob if j <= 2 new_pop(i,j) = randi([lb(j), ub(j)]); else new_pop(i,j) = lb(j) + rand * (ub(j) - lb(j)); end end end end % 强制边界 new_pop = max(new_pop, repmat(lb, pop_size, 1)); new_pop = min(new_pop, repmat(ub, pop_size, 1)); pop = new_pop; end % 输出最优解 [best_cost, best_idx] = min(best_fitness); fprintf('最优容量配置: 光伏组数=%d, 风机台数=%d, 储能容量=%.1fkWh, 储能功率=%.1fkW, 柴油机=%.1fkW\n', ... pop(best_idx,1), pop(best_idx,2), pop(best_idx,3), pop(best_idx,4), pop(best_idx,5)); fprintf('最低年综合成本: %.2f 元\n', best_cost);

然后是适应度评估函数,就是容量解析和调用下层:

function [total_cost, op_cost] = evaluate_individual(x) % 解码个体 N_pv = round(x(1)); N_wt = round(x(2)); E_bat = x(3); P_bat = x(4); P_diesel = x(5); % 设备单位容量参数 P_pv_unit = 100; % kW, 每组光伏容量 P_wt_unit = 100; % kW, 每台风机容量 c_pv = 5000; % 元/kW c_wt = 8000; % 元/kW c_bat_e = 1500; % 元/kWh c_bat_p = 1000; % 元/kW c_diesel = 2000; % 元/kW CRF = 0.0872; % 等年值折算系数 % 上层投资成本(等年值) inv_cost = CRF * (c_pv * N_pv * P_pv_unit + ... c_wt * N_wt * P_wt_unit + ... c_bat_e * E_bat + ... c_bat_p * P_bat + ... c_diesel * P_diesel); % 调用下层运行优化 op_cost = run_lower_level(N_pv * P_pv_unit, N_wt * P_wt_unit, ... E_bat, P_bat, P_diesel); total_cost = inv_cost + op_cost; end

下层运行优化是核心,我用Yalmip建模:

function op_cost = run_lower_level(P_pv_total, P_wt_total, E_bat, P_bat, P_diesel) % 加载典型日数据 load('typical_days.mat'); % Yalmip建模变量 n_day = length(day_weight); op_cost_total = 0; for d = 1:n_day T = length(load_curve{d}); % 决策变量 P_buy = sdpvar(1, T); P_sell = sdpvar(1, T); P_ch = sdpvar(1, T); P_dis = sdpvar(1, T); P_diesel_out = sdpvar(1, T); SOC = sdpvar(1, T+1); % 约束条件 Constraints = []; % 功率平衡 for t = 1:T Constraints = [Constraints, ... P_pv_total * pv_curve{d}(t) + P_wt_total * wt_curve{d}(t) + ... P_dis(t) + P_diesel_out(t) + P_buy(t) == ... load_curve{d}(t) + P_ch(t) + P_sell(t)]; end % 购售电互斥(通过价格机制避免同时买卖,这里加最大功率约束) Constraints = [Constraints, 0 <= P_buy <= 500, 0 <= P_sell <= 500]; % 储能约束 Constraints = [Constraints, SOC(1) == 0.5 * E_bat]; % 初始SOC eta_ch = 0.95; eta_dis = 0.95; for t = 1:T Constraints = [Constraints, ... SOC(t+1) == SOC(t) + eta_ch * P_ch(t) - P_dis(t) / eta_dis, ... 0.1 * E_bat <= SOC(t+1) <= 0.9 * E_bat, ... 0 <= P_ch(t) <= P_bat, ... 0 <= P_dis(t) <= P_bat, ... 0 <= P_diesel_out(t) <= P_diesel]; end % 柴油机燃料成本系数 fuel_cost = 1.2; % 元/kWh % 目标函数:购电费用 + 燃料费用 - 售电收益 Objective = sum(price_buy{d} .* P_buy) + sum(fuel_cost * P_diesel_out) - sum(price_sell{d} .* P_sell); % 求解 ops = sdpsettings('solver', 'cplex', 'verbose', 0); optimize(Constraints, Objective, ops); op_cost_total = op_cost_total + day_weight(d) * value(Objective); end op_cost = op_cost_total * 365; % 折合成年运行成本 end

注意最后一个函数里我用day_weight(d) * value(Objective)然后乘以365,这个逻辑要小心:如果day_weight已经代表了每个典型日占全年天数的比例,比如0.3/0.4/0.3,那么三个典型日加权求和后直接乘以365就是年运行成本。如果你的典型日权重是“每年出现30天”这种绝对天数,那就直接加权求和不用再乘365。我在这上面栽过一次跟头,算出来的年成本高得离谱,查了两天才发现是权重口径搞混了。

3.4 数据准备与典型日生成

下层模型的数据准备是整个工程里最容易被低估的部分。负荷曲线、光伏归一化出力、分时电价这些数据如果拍脑袋乱填,算出来的配置方案没有任何参考价值。我用的典型日生成方法是:拿项目地实测的全年8760小时数据,按“聚类”的思路挑出三个最具有代表性的日子——夏大负荷日、冬大负荷日、春秋平均日。每个典型日包含24小时的负荷功率、光伏归一化出力(实际出力/额定容量)、风电归一化出力、分时购电价和售电价。

这里有一个很重要的细节:光伏归一化曲线应该是“单位千瓦出力”,也就是你装1kW光伏,每个时刻实际能发多少电。这个值跟地理位置、天气、倾角都有关系,不能随便拿发电小时数去反推。同样,风电的归一化曲线也要小心,风机有切入风速、切出风速,不是风速达标就满发。我用的是PVsyst和WAsP导出的数据,如果你手头没有专业软件,至少要用气象NASA数据加转换效率估算一下,别从论文里抄一组曲线就完事。我实测下来,光伏曲线偏差10%,容量配置结果能差20%以上。

分时电价的设置要结合当地电网的峰谷时段。我用的分时电价是:峰时段10:00-15:00、18:00-21:00,电价1.2元/kWh;平时段7:00-10:00、15:00-18:00、21:00-23:00,电价0.7元/kWh;谷时段23:00-次日7:00,电价0.35元/kWh。售电价统一按购电价的80%计算。这个设定一旦变了,储能的最优容量会立刻跟着变——如果峰谷价差小于储能的充放电损耗成本,储能就完全没有配置价值。这个盈亏平衡点你可以在跑模型之前先估算一下:假设充放电效率95%,电池每度电循环一次损耗约0.1元,加上PCS损耗,如果峰谷价差低于0.3元/kWh,储能套利基本不赚钱。

4. 实验场景设计(微网与外部电网的调控策略)

4.1 微网与外部电网互动逻辑

很多初学者跑微电网容量配置时,把并网点功率当成完全自由的变量,想买多少买多少。实际项目里,电网对微网的联络线功率是有约束的,常见的有两种:一种是最大交换功率限制,比如不能超过变压器容量;另一种是功率波动限制,比如每15分钟功率变化不能超过多少kW。这些约束在容量配置阶段如果不考虑,等设备装完了再发现联络线不够用,就很被动了。

我在下层模型里加了一个联络线功率上限,比如P_line_max = 300kW,也就是从电网买电或者向电网卖电的功率都不能超过300kW。这个约束对容量配置的影响很大:如果联络线功率限制很紧,微电网就必须自己配更大的储能或者柴油机来应对负荷高峰,总成本会明显上升。你可以把P_line_max设成300、500、1000分别跑一遍,看看容量配置结果怎么变,这个敏感性分析对实际项目非常有价值,也是和甲方谈方案时最有说服力的材料。

电网互动还有一层意思是“怎么卖电”。我见过很多模型允许微网向电网卖电,但设了很高的售电价,导致模型倾向于“使劲装光伏,发了电全卖掉”,这种结果偏离实际。真实情况下,分布式光伏上网电价通常低于目录电价,部分地区甚至没有余电上网资格。所以我在模型里把售电价设为购电价的80%,且允许向电网卖电。如果你想模拟“自发自用、余电上网”的保守模式,把售电价设成0.35元/kWh左右,再跑一遍,你会发现最优的光伏装机量会降很多。这个参数的敏感性就是商业模式的敏感性。

4.2 储能调度策略与分时电价套利分析

下层模型跑完之后,你可以把储能调度曲线画出来分析。正常情况下,谷时段(23:00-7:00)储能会以最大功率充电,SOC从10%一直充到90%左右;上午平时段和中午光伏大发时段,储能基本不动作,因为光伏直接供给负荷就够了;下午峰时段(18:00-21:00),储能开始放电,支撑晚高峰负荷,直到SOC掉到10%左右。这个行为模式验证了模型在正确实现“低充高放”逻辑。如果你发现储能SOC曲线反着走,比如峰时段充电、谷时段放电,那一定是电价数据或者目标函数符号搞错了。

还有一个细节:很多模型会给储能加“日始日终SOC相等”的约束,也就是一天下来储能电量回到初始值,这是为了避免模型把前一天的能量“偷”到当天用,造成日运行成本虚低。我建议加上这个约束,尤其在做多典型日加权时。但是注意,加了日始日终SOC相等后,如果光伏大发日储能被迫在光伏多的时段充电、在晚上全放完,可能造成日内无法完成整体循环。我的处理方式是让SOC初始值在0.2到0.8之间可调,让优化器自己决定。

储能功率和容量也不是随便分开配的。储能容量E_bat决定它能存多少电,功率P_bat决定它能以多快速度充放。如果E/P比选得太小,就像一个“大水管小水池”,一小时就充满了,剩下时间闲着;如果E/P比太大,就是“小水管大水池”,每天根本用不完。实际项目中,电化学储能的E/P比一般选2~4小时,我的模型跑出来的最优配置如果偏离这个范围,我会检查是不是价格参数或调度约束出了问题。

4.3 柴油机在容量配置中的定位

柴油机虽然不环保,但在离网型微电网里往往是兜底电源。在并网型微电网里,柴油机的作用就变成了“极端工况备胎”——当电网故障或者光伏风电全无出力时,柴油机保证重要负荷不断电。我的模型里如果允许柴油机正常参与调度,因为它的燃料成本(1.2元/kWh)高于谷时购电成本,优化器会在大部分时间让它停机,只在峰时段电价高于燃料成本时启动。这个结果符合预期。你可以加上“最小开机时间”“最小停机时间”约束让柴油机更接近实际工况,但这会让下层模型从纯LP变成混合整数规划,求解时间会翻好几倍。容量配置阶段我建议先用简化的连续出力模型,等确定了大致容量,再在详细调度阶段加机组组合约束精算。

这里有一个容易忽略的点:柴油机的容量配置和它的年利用小时数密切相关。如果模型算出来柴油机年运行时间特别短,比如只有50小时,那这200kW的柴油机投资几乎就是纯沉没成本。这时候你要么考虑减少柴油机容量,要么考虑换成租赁移动电源车的方案。这种“设备买了没用”的信息,正是双层模型能提供给决策者的独特价值——它不仅仅给出一个数字,还能告诉你每个设备到底在什么场景下被用到。

5. 参数校准、常见问题与避坑经验

5.1 收敛性差、结果不稳定怎么办

跑遗传算法最烦的就是每次结果不一样,或者很多代后成本还在大幅波动。我遇到过的原因主要有三个。第一个是种群太小,多样性不足,算法陷入局部最优。我的经验是种群规模至少20以上,迭代次数至少30代以上,如果你发现最优个体连续5代没有变化,可以适当增加变异概率。第二个是上下层迭代时,下层模型的求解容差太大。Cplex这类求解器的默认容差是1e-6,但我跑大规模问题时它会自动放宽。你可以在sdpsettings里显式设置'cplex.optimalitytarget', 1e-9或者调低容差。第三个是投资成本和运行成本的数量级差异太大,导致适应度函数里投资成本主导,运行成本成了“噪声”。解决办法是对两个成本做归一化,或者调整遗传算子的选择压力。

还有一个我自己经常用的技巧:把上一代最优个体直接保留到下一代(精英保留策略)。我给的代码里没有做这一步,实际使用建议加上,不然最优解很可能在交叉变异中被破坏掉。一个简单的做法是每代记录最优个体,下一代强制放回种群,替代最差个体。这能显著加快收敛。

5.2 下层模型无解时的处理

容量配置中,下层模型无解是非常常见的情况,尤其是初始种群随机生成时,大概率会出现“光伏+储能+柴油机完全撑不住负荷”的个体。我的处理方式前面提过,可以在下层模型加一个“虚拟电厂”变量,单位购电成本设置成正常购电价的10倍甚至50倍。这样做的好处是下层模型永远不会无解,但一旦调用了虚拟电源,运行成本会飞涨,上层适应度函数自然淘汰这个个体。

还有一种更工程化的做法:在下层模型求解前先做可行性预判,比如算一下“最坏情况下(无光伏、无风电、储能SOC最低时),柴油机和联络线最大出力能不够满足负荷”。如果不够,直接返回一个惩罚值,不进入Yalmip求解流程。这样能省下大量无效求解时间,种群越大优势越明显。我在写高校项目时常用这种方法,特别是学生用的电脑性能一般,对求解次数的控制很关键。

5.3 代码调试与结果校核的几个土办法

很多初学者跑通代码后,面对一堆输出数字不知道对不对。我分享几个土办法,虽然简单,但救命。第一,把储能容量设成0,跑一次纯购电方案,算出来的成本应该等于“所有时段负荷乘以购电价”的全年累计,这个值可以手算验证。第二,把光伏容量设成0但保留储能,跑出来的结果应该比纯购电方案成本更高或持平,如果变低了,说明模型里储能能凭空造电,一定有bug。第三,把分时电价全设成同一个值,储能的套利价值消失,最优储能容量应该会显著变小甚至趋近于0,如果没有,说明储能约束写拧了。

另外,Yalmip的check(Constraints)函数是你的好朋友。每次求解完,用check检查每个约束的残差,如果某个约束残差大于1e-5,说明这个约束在数值上没满足,可能是量纲问题或者求解器精度问题。我刚做这个项目时吃过亏,储能SOC约束单位用的是MWh,功率平衡约束用的是kW,导致能量和功率直接相加,残差巨大,结果永远不对劲。统一单位是建模的基本功,但在代码里特别容易犯。

5.4 代码扩展方向:从双层到多时间尺度

这套双层框架最让我满意的地方是它的扩展性。容量配置确定之后,你往往还要做“日前调度+日内滚动”的多时间尺度能量管理。这时上层模型可以变成日前机组组合,下层模型可以变成基于模型预测控制的日内滚动优化。热搜里经常看到的“基于模型预测控制的微电网多时间尺度能量管理”,本质上就是在容量配置之后,把下层的静态调度换成带反馈校正的动态优化。代码层面,你只需要改run_lower_level这个函数,上层完全不用动。

如果你想加入不确定性分析,比如光照、负荷预测误差,可以在下层模型里改成随机规划或者鲁棒优化。Yalmip对这两种问题都有比较好的支持,鲁棒优化还可以用鲁棒对等转换写成线性规划。但我要提醒一句:不确定性模型会让计算量成倍增长,在容量配置阶段先跑确定性模型,确定大致方案后,再针对候选方案做不确定性验证,这样性价比最高。

写在最后的一点个人体会

这套代码前前后后我改了好几版,最大的体会是:双层优化能不能在工程中真正发挥作用,关键不在算法多高级,而在上下层模型的分工是否符合真实的决策流程。上层管“买什么设备”,下层管“设备怎么用”,这是每个项目投资方和运营方都要面对的关系,模型只是把这种关系数学化了。你如果还在纠结理论推导和求解器选型,我的建议是先把手头的单层模型改成双层框架,哪怕初始用的都是最简单的遗传算法和线性规划,跑通之后再逐步加约束、换算法。另一个建议就是多跑敏感性分析——容量配置的结果对电价、负荷曲线、设备单价特别敏感,把几个关键参数的敏感性曲线做出来,比单次优化结果更能说服人。这些曲线也是你以后写报告、做答辩、向甲方汇报时最有分量的材料。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询