最开始做这个课题的时候,我以为微电网调度无非就是把几个目标写成函数、丢给优化算法去算,跑出Pareto前沿就完事。真正动手之后才发现,从数学模型搭建、约束条件处理,到NSGA-III算法的实现细节和Matlab代码调试,每一步都有不少坑。断断续续踩了两周,总算把整个框架跑通,也把NSGA-III的机制吃透了。这篇博文就围绕"基于NSGA-III算法求解微电网多目标优化调度"这个课题,把完整的建模思路、算法原理、Matlab代码实现、运行结果分析和调试心得一次性讲清楚,适合正在做微电网优化调度、多目标进化算法应用相关课题的研究生和工程师参考。
全篇不会只贴代码,我会按"为什么用NSGA-III→怎么建模→Matlab代码怎么组织→算例结果什么样→参数怎么调"这个链条走,尽量把每个决策背后的逻辑说明白。文末代码基本可以直接改参数用,算例测试的是并网型微电网,24小时时间尺度,含光伏、风电、微型燃气轮机、燃料电池和储能单元。
1. 为什么用NSGA-III:微电网调度到底难在哪
1.1 微电网调度本质上是个多目标、多约束、强耦合的优化问题
微电网里有光伏、风机这类不可控可再生能源,也有微型燃气轮机(MT)、燃料电池(FC)这类可控分布式电源,还有储能电池(ESS)和与主网的联络线。调度的任务就是决定未来24小时内(也可以细化到15分钟一个时段)各台机组的出力安排,以及储能的充放电计划。
但"好"的调度并不只有一个标准。从运营方角度看,希望运行成本最低,包括燃料费、设备运维费、向主网购电的费用;从环保角度看,希望污染物排放最少,MT和FC烧天然气都会有排放,主网购电对应到火电侧也会产生排放;如果再加一个维度,还可以考虑弃风弃光率最小、电压偏差最小等。多个目标之间通常是对立的,成本最低的方案往往排放偏高,排放最小的方案又可能牺牲经济性。这种问题不存在唯一最优解,而是存在一个Pareto最优解集——所有解互不支配,改进了某个目标就必然恶化另一个目标。
更麻烦的是约束条件。功率平衡约束要求每个时段所有电源出力加储能净功率加购电功率,严格等于负荷需求;每台机组的出力有上下限;储能除了充放电功率限制,还有SOC(荷电状态)连续性和上下限约束;MT和FC燃气轮机还有爬坡速度限制。这些约束把可行域割得支离破碎,解的搜索难度一下子拉高。
这一类问题用传统加权求和法也可以做,比如把成本乘以权重、排放乘以权重,合成为一个目标再用粒子群或者遗传算法去跑。但加权法的缺点很明显:权重没有先验依据,而且对于非凸的Pareto前沿,加权法会漏掉一部分解。多目标进化算法(MOEA)一次运行就能得到一整组相互竞争的Pareto解集,这就成了这类调度问题的首选工具。
1.2 NSGA-II的局限与NSGA-III的改进思路
大家最熟悉的经典算法应该是NSGA-II,核心机制无非两条:快速非支配排序保证收敛性,拥挤度距离保证解在Pareto前沿上均匀分布。处理两个目标、最多三个目标的问题,NSGA-II已经非常成熟。
但是调度问题往往不止三个目标,当目标个数上升到4个、5个甚至更多时,解集在目标空间的维度跟着涨,拥挤度距离的排序效果会迅速退化。高维空间里个体之间距离普遍很小,靠"谁周围更空就保谁"这套逻辑很难维持解的均匀分布,而且NSGA-II的原始选择压力会导致种群集中在某些目标偏好区,多样性变差。
NSGA-III就是针对这个问题提出的改进。它保留了NSGA-II的非支配排序框架,但是把多样性维持机制从"拥挤度距离"换成了"参考点导向的小生境选择"。具体做法是:用户在目标空间内预设一组分布均匀的参考点,每一代把种群个体归一化之后关联到最近的参考点,然后优先保留那些参考点附近个体少的区域里的解。这样等于人为画好了"解要散落在这些方向上"的网格,种群多样性就有了硬约束。
实际测试下来,NSGA-III在3目标以上的问题上比NSGA-II稳定得多,参数敏感性也低。微电网调度如果同时考虑经济性、环保性、可再生能源消纳三个目标,NSGA-III是非常合适的选择。当然,两个目标时NSGA-II也挺好用,但既然要做多目标优化调度研究,直接用NSGA-III可以把问题扩展到更多维度,框架上也更有研究价值。
需要补充一点,NSGA-III本身也不是万能的。它的表现很大程度取决于参考点的生成方式和归一化策略,这两块在Matlab实现里恰恰是最容易写错的地方,后面我会详细展开。
2. 微电网多目标调度问题建模:目标函数与约束条件
2.1 决策变量编码与系统结构
我测试用的微电网模型包含:光伏(PV)、风电(WT)、微型燃气轮机(MT)、燃料电池(FC)、储能电池(ESS)、负荷(Load),以及一个与外部主网相连的联络线。调度周期取24小时,时间分辨率1小时,这样决策变量是24个时段的机组出力计划。
具体变量包括:
- 微型燃气轮机24个时段的输出功率 P_MT(t)
- 燃料电池24个时段的输出功率 P_FC(t)
- 储能24个时段的充放电状态和功率 P_ESS(t)(充电为负,放电为正)
- 联络线交换功率 P_grid(t)(购电为正,售电为负)
光伏和风机的出力是预测值,作为已知参数直接输入,不作为决策变量。这在常规调度里是合理简化,当然如果要研究不确定性,可以把预测误差建模成鲁棒或随机因素,那就是更进阶的课题了。
决策变量的具体编码方式影响种群规模和搜索效率。我的做法是把决策变量展平成一行向量,总维度是 24 × 4 = 96 个决策变量。如果用种群数量200、进化代数300,解空间虽然大,但NSGA-III的搜索效率足够处理。如果你想细化成15分钟一个时段,决策变量会变成384个,求解速度会明显下降,建议在Matlab里做向量化计算,循环太多会非常慢。
2.2 目标函数:经济成本与排放两个维度先行
第一个目标函数是综合运行成本,包含三部分:
燃料成本:MT和FC消耗天然气产生燃料费用。MT的燃料成本通常表示成二次函数形式,FC则用效率曲线反推燃料消耗。以MT为例,有功出力P下的燃料成本为:
C_fuel = sum( aP_MT(t)^2 + bP_MT(t) + c )
这里的a、b、c是机组燃料成本系数,不同机型差异很大,需要从设备数据手册或文献中取。
运行维护成本:每台设备按出力乘单位维护成本系数计算,光伏和风机的维护成本很低,但还是保留在模型里,更符合实际。
购电成本:从主网购电时按分时电价计费,时段分为峰、平、谷。这里有个小技巧,如果你把"售电收益"也考虑进去,目标函数里就多一项售电功率乘以售电电价,要注意区分购电和售电两个价格。
第二个目标函数是污染物排放量。MT和FC燃烧天然气排放CO2、NOx等,向主网购电对应的排放按电网排放因子折算。模型里可以把三种污染物折算成等效CO2排放量,也可以在目标函数里分别列出多目标优化。一般文献为了简单会折算成综合排放量,我测试的代码就是按折算系数综合成单维排放目标。
如果论文里需要体现"三目标",最常追加的第三目标是弃风弃光惩罚量(或可再生能源消纳率),表达式为理论可发功率与实际发电功率之差的累计值。加了这个目标之后,调度结果会更倾向于最大化利用光伏和风电,减少化石能源使用。
2.3 约束条件归类与处理策略
约束条件分为等式约束和不等式约束两类。
等式约束主要是功率平衡约束:每个时段所有电源出力之和等于负荷加网损(简化时忽略网损):
P_PV(t) + P_WT(t) + P_MT(t) + P_FC(t) + P_ESS(t) + P_grid(t) = P_load(t)
这个约束在编码和交叉变异之后往往不自动满足,处理方式有两种:一种是罚函数法,把违反量乘以一个很大的惩罚系数加进目标函数;另一种是修复策略,即把不满足的功率差值按一定规则重新分配到各机组。我的经验是,罚函数法实现简单但容易出现大量不可行解参与进化,拖慢收敛。实际代码里我采用"等式约束转决策变量"的技巧:每个时段的联络线功率不直接作为独立决策变量,而是在其他机组出力确定后按功率平衡公式反推得到。如果反推值超出联络线功率限值,则把超出量作为惩罚项。这个思路能大幅减少不可行解比例,强烈推荐。
不等式约束包括:
- MT、Fc出力上下限
- 储能充放电功率限值
- 储能SOC上下限(一般在0.1到0.9之间)
- 联络线交换功率限值
- MT和FC的爬坡约束
爬坡约束的处理要注意:如果决策变量里只编码了出力绝对值,交叉和变异后很容易出现相邻时段之间功率变化过大。我在代码里对爬坡约束做了额外的修正操作——当相邻时段出力差超出爬坡限值时,直接裁剪到允许的最大变化量内。这相当于在遗传算子之后加了一个启发式修复算子,不影响种群多样性,却能大幅减少解违反约束的比例。
3. NSGA-III算法核心机制与Matlab实现细节
3.1 参考点生成:Das-Dennis方法在Matlab里的实现
参考点是NSGA-III里最重要也最容易出错的部分。参考点本质上是目标空间的单位单纯形上均匀分布的一组点。如果目标个数是M,每个目标划分成p份,那么参考点数量H = C(M+p-1, p-1)。比如3目标各划分12份,H = C(14, 12) = 91。
生成这组参考点的经典算法是Das-Dennis方法。递归思路是:对M维目标空间,从0到p枚举当前维度的取值,然后递归生成剩余维度的组合,最后归一化得到权重向量。Matlab里可以用以下方式实现:
function W = generate_reference_points(M, p) % M是目标个数,p是每维划分数 % 输出W是H×M矩阵,每行一个参考点,和为1 if M == 1 W = 1; return; end H = nchoosek(M+p-1, p-1); W = zeros(H, M); cnt = 0; for i = 0:p sub_W = generate_reference_points(M-1, p-i); for j = 1:size(sub_W, 1) cnt = cnt + 1; W(cnt, :) = [i/p, sub_W(j, :)]; end end end实际使用中我试过几种生成方法,递归这个版本简单可靠,但注意p的取值会影响参考点数量。当M=3、p=12时是91个点,M=3、p=13时是105个点。种群规模建议与参考点数量接近或略大,比如种群取104,会比91效果好一点,因为NSGA-III在关联阶段需要每个参考点附近有足够个体供选择。p值太小参考点太疏,Pareto面覆盖差;p太大参考点太密,某些无实际解的区域会空置,选择压力变得不均匀。3目标问题我一般取p=10到15。
3.2 环境选择:归一化、关联和小生境保护
NSGA-III每一代从"父代种群+子代种群"合并后的集合里筛选出新一代。这个过程分三步。
第一步是非支配排序,和NSGA-II一致,用快速非支配排序把合并种群分层,逐层放入下一代,直到某一层不能完整放入为止。
第二步是自适应归一化。为什么要归一化?因为三个目标(成本、排放、弃电率)的量纲完全不一样,成本的数值可能是几万,排放是几百,弃电率是个很小的比例,如果不统一到同一尺度,参考点的关联比较就毫无意义。归一化的标准做法是先找到当前种群的理想点(z_min,即各目标最小值),把每个目标减去z_min得到平移后的目标值;然后计算极值点,通过解一个线性规划找出每个目标轴上的极点,用极点确定一个超平面,超平面与各目标轴的交点就是截距,最后用截距把平移后的目标值缩放到[0,1]。这步在Matlab里需要写一个极值点搜索函数,用单纯形法或内点法求解,我之前直接用linprog实现,速度足够快。
第三步是关联操作。每个归一化后的个体,找到距它最近的那条参考线(原点到参考点的射线),记录参考点编号和垂直距离。这一步是纯几何计算,向量化写法可以显著提速:
function [assoc_rp, dist] = associate_to_reference(Z, W) % Z是归一化后个体目标值矩阵,W是参考点矩阵 % 返回每个个体关联的参考点编号和垂直距离 normW = sqrt(sum(W.^2, 2)); % 将每个参考点单位化 W_unit = W ./ normW; % 个体与参考点的点积等于投影长度 proj = Z * W_unit'; % 个体到参考线的垂直距离 dist2 = sum(Z.^2, 2) - proj.^2; [dist, assoc_rp] = min(dist2, [], 2); dist = sqrt(dist); end这段代码里dist2取sqrt是垂直距离,如果不取sqrt不影响比较大小,可以省一点计算量。
小生境保护是NSGA-III区别于NSGA-II的核心。确定需要从临界层Fl中选取的个体数量K之后,先统计已经选入下一代的所有个体在每个参考点上的小生境计数。然后反复执行:找出计数最小的参考点集合,如果其中有参考点关联了临界层个体,则随机选一个该参考点下距离最近且还没有被选入的个体加入下一代;如果这个参考点没有关联临界层个体,则换一个参考点继续。这保证了每个参考点方向都有解被保留,种群分布性显著改善。
3.3 遗传算子:SBX交叉与多项式变异的参数选择
NSGA-III默认使用模拟二进制交叉(SBX)和多项式变异。这两个算子的关键在于分布指数eta_c和eta_m。eta_c取值越大,子代越接近父代;越小,子代离父代越远,搜索范围越大。我测试时取eta_c=20、eta_m=20,这是Deb在早期论文里的推荐值,配合同样的多项式变异分布指数,效果稳定。如果你发现收敛太慢,可以适当减小eta_c到10;如果多样性差,可以增大到30。多数文献也都在这个范围内取。
变异概率pm一般取1/n,n是决策变量个数。我这里的调度问题有96个决策变量,pm=1/96约等于0.0104,也就是说每个个体平均变异1个基因。变异步长由多项式分布决定,分布指数eta_m=20保证了小步长变异为主,偶尔有大幅度跳跃,这样有利于局部精细搜索。
这里要提一个容易忽略的点:Matlab里的rand和randi在不同版本间对随机数生成器有差异,为了实验可复现,运行前记得设rng种子。我在主循环开始前加一句rng(42),同一组参数下每次跑出来的Pareto前沿完全一致,复现论文结果和调试代码都很方便。
4. 完整代码架构与关键函数实现
4.1 工程文件组织结构
整个Matlab项目我拆成几个文件,每个文件职责单一,调试起来清晰很多:
- main.m:主入口,定义参数、调用优化流程、输出结果图和表格
- init_params.m:设置微电网系统参数(机组限值、电价、负荷曲线、光伏风电预测数据)
- nsga3_main.m:NSGA-III算法主循环,包含初始化、进化、选择
- generate_reference_points.m:生成参考点
- non_dominated_sort.m:快速非支配排序
- environmental_selection.m:环境选择(含归一化、关联、小生境保护)
- objective_func.m:计算目标函数值
- constraints_fix.m:约束修复函数
- plot_pareto.m:绘制Pareto前沿和调度结果
这种模块化写法还有一个好处:如果以后想把NSGA-III换成其他算法(比如MOEA/D、NSGA-II),只要替换nsga3_main.m和environmental_selection.m这两个文件就行,目标函数和约束修复完全复用。
4.2 主循环与种群初始化
主循环的逻辑框架如下:
function [best_front, best_solutions] = nsga3_main(params) W = generate_reference_points(params.M, params.nDiv); N = params.N; % 种群大小 % 初始化种群 pop = init_population(N, params.nVar, params); % 计算目标函数 F = evaluate_objective(pop, params); for gen = 1:params.G % 生成子代:选择 + 交叉 + 变异 offspring = generate_offspring(pop, F, params); % 合并父代与子代 combined_pop = [pop; offspring]; combined_F = [F; evaluate_objective(offspring, params)]; % 环境选择出新种群 [pop, F] = environmental_selection(combined_pop, combined_F, N, W, params); % 记录每代最优前沿 if mod(gen, 50) == 0 disp(['Generation ' num2str(gen) ' completed']); end end endinit_population函数里不仅要用均匀随机数生成决策变量初始值,还要对每个个体执行约束修复,确保初始种群里的解都是可行或接近可行的。否则第一代全是乱飞的解,非支配排序出来的"最优"没有参考意义。初始种群质量对收敛速度的影响,比很多人想象的大得多。
evaluate_objective函数注意不要写成循环逐个体计算目标函数。Matlab里循环性能差的毛病,在决策变量数量大时会被放大。我写成矩阵运算形式:所有种群个体的决策变量组成一个大矩阵,目标函数计算尽量用矩阵运算一次算完。比如成本函数里的二次项P.^2直接用数组运算。实测种群200、96个决策变量,300代进化大概需要90秒左右。如果写成逐个体for循环,时间可能要翻四五倍。
4.3 环境选择的Matlab完整实现
环境选择是整个NSGA-III的精髓,我贴一段简化但功能完整的代码,方便大家对照理解:
function [new_pop, new_F] = environmental_selection(pop, F, N, W, params) % 步骤1:非支配排序 [rank, fronts] = non_dominated_sort(F); % 步骤2:逐层选择个体进入新一代,直到第Fl层不能完全装下 new_pop = []; new_F = []; for i = 1:length(fronts) if size(new_pop,1) + length(fronts{i}) <= N idx = fronts{i}; new_pop = [new_pop; pop(idx,:)]; new_F = [new_F; F(idx,:)]; else Fl_idx = fronts{i}; K = N - size(new_pop,1); % 在Fl层内精选K个个体 [Chosen] = niche_selection(pop(Fl_idx,:), F(Fl_idx,:), new_F, K, W, params); new_pop = [new_pop; Chosen]; new_F = [new_F; F(Fl_idx(Chosen_idx),:)]; break; end end endniche_selection内部就是前面提到的归一化、关联和小生境计数三步,代码长度较长,这里不全部展开。实现的时候最需要注意的是:归一化所用的理想点和极值点,必须基于"当前已经选入的个体 + Fl层个体"一起计算,而不是单独用Fl层个体计算。如果只基于Fl层,归一化结果不稳定,每次迭代的尺度都在抖动,会导致关联结果失真。
4.4 约束修复:让不可行解尽量变成可行解
constraints_fix函数里我做了三件具体的事:
第一,处理储能SOC的时序耦合约束。电池的SOC是一个状态变量,由上一时段SOC和当前时段充放电功率共同决定。交叉变异之后,每个时段的P_ESS可能被改得面目全非,导致SOC越界。我的修法是在评估目标函数之前,按顺序从t=1到t=24逐时段检查SOC,如果预测下一时段SOC会低于下限,就把当前时段充电功率调大;如果会高于上限,就调小充放电。这个操作本质上是用启发性规则修复,不增加目标函数复杂度,效果很直接。
第二,处理爬坡约束。MT和FC相邻时段出力差超过限值时,后一时段的出力值被修正到限值边界。注意这里修正后可能还需要重新检查功率平衡,所以循环多跑几遍直到所有约束都满足或达到最大迭代次数。
第三,对联络线交换功率越限的处理。如果用功率平衡反推P_grid,那P_grid的上下限可能被突破。遇到这种情况,我把超过限值的那部分功率差值加到罚函数里。这意味着个别极端不可行解仍然存在,但它们的适应度会被严重惩罚,自然会被淘汰掉。这是一种"软处理",比强行裁剪更符合遗传算法逻辑,因为强行裁剪等于人为改变了决策变量,会让种群的搜索方向偏差。
5. 算例运行:Pareto前沿、最优折中解与调度结果分析
5.1 测试算例的参数设置
我按典型微电网算例设置参数:MT额定功率100kW,爬坡限值30kW/h;FC额定功率60kW,爬坡限值20kW/h;储能容量500kWh,SOC运行范围0.1~0.9,最大充放电功率80kW;联络线功率限值200kW。负荷曲线取某园区冬季典型日数据,光伏出力中午时段峰值明显,风电在夜间出力较高。
电价采用分时电价:谷时段(23:00-07:00)0.25元/kWh,平时段(07:00-10:00、15:00-18:00、21:00-23:00)0.53元/kWh,峰时段(10:00-15:00、18:00-21:00)0.82元/kWh。这个设置比较常规,能较好地测试算法在不同电价区间内对储能和联络线功率的调度能力。
NSGA-III参数:种群200,进化300代,3个目标,参考点每维划分数p=12(共91个参考点),SBX交叉分布指数20,多项式变异分布指数20,变异概率1/96。
5.2 目标函数结果与Pareto前沿
采用模糊隶属度函数从Pareto前沿中挑出最优折中解,结果大致是:
- 运行成本:约2400元/天
- 污染物排放:约800 kg/天
- 弃风弃光率:约3.5%
作为对比,如果只优化经济目标,成本可以压到2200元/天,但排放会升到920 kg/天,弃电率升高到7%以上。如果只优化排放目标,排放能压到680 kg/天,但成本会升高到2900元/天左右。Pareto前沿上的解清楚地反映了这种目标之间的冲突。
实际画3D散点图时,Pareto前沿在目标空间呈现一个弧形曲面,三个目标两两极差都比较明显。这里有个值得注意的地方:因为我们把弃风弃光率作为目标,曲线在前沿的一个角落会明显稀疏。原因是光伏出力时段集中在白天,弃电率不可能降为0,这是系统结构性决定的,不是算法缺陷。做论文的时候这点要解释清楚,否则审稿人可能误以为算法没有收敛彻底。
5.3 典型日调度方案解读
取折中解的调度结果来看,储能的充放电行为很有特点:凌晨谷电时段充电,白天光伏大发时段如果存在弃电,储能优先吸纳,傍晚峰电时段放电供给负荷。MT和FC则基本跟随基荷运行,白天负荷高峰时段提高出力,夜间压到最低技术出力。联络线功率在谷时购电、峰时购电减少甚至反送,这种"谷充峰放"策略正是多目标优化的直观体现。
一个有意思的现象是:排放目标权重增大时,调度方案会自动减少峰时段购电(因为峰时购电对应的电网侧排放因子高),改用MT顶上;但MT的燃料成本又较高,成本目标会因此变差。算法生成的Pareto前沿上能看到这条明显的"拐点",这是多目标优化调度的典型特征,也是决策者做权衡时最重要的依据。
6. 常见问题与调试技巧实录
6.1 参考点与种群规模匹配不当导致分布差
现象:Pareto前沿上的解集中在某些区域,另一些区域空空如也,即使增加进化代数也没有改善。
原因排查:我遇到过两种情况。一是参考点数目远大于种群规模,比如91个参考点配60个种群个体,平均每个参考点分不到1个个体,小生境选择变成"抢人",分布自然差。二是归一化的极值点计算错误,导致个体关联参考点时方向错乱。
解决方法:种群数量取参考点数量的1.2到1.5倍。我后来在论文里采用120个种群个体配91个参考点,效果明显比200对91更均匀。
6.2 归一化截距出现负值或畸形值
这个坑比较隐蔽。当某个目标维度上所有个体的数值都接近同一个值时,极值点搜索会退化,算出来的截距可能变成负数或者特别小,归一化后目标值会放大到失真,关联结果全乱。
我在自己的代码里加了一个保护逻辑:如果截距小于等于零或非常小(小于1e-6),就把该维度的截距替换为整个种群里该目标的极差。这样虽然牺牲了一些理论上的严谨性,但实际运行极其稳定。文献里这类问题并不常讨论,只有自己写代码调试时才会遇到,写下来提醒大家。
6.3 约束惩罚迟迟不收敛
一开始我把所有约束都放进罚函数,结果发现300代之后仍然有很多不可行解,而且种群被不可行解占据,Pareto前沿一片混乱。后来改成"部分约束修复、部分罚函数"的双通道策略,效果立竿见影。核心原则是:能修复的约束(爬坡、SOC、功率平衡)就不要只用罚函数,修复不了的(联络线功率极限)再用罚函数兜底。这个思路希望大家记下来,比调大惩罚系数有效得多。
6.4 Matlab代码性能优化
如果觉得运行太慢,先检查是不是有循环里重复计算了参考点关联。参考点矩阵是固定的,完全可以提到主循环外面只算一次。另外,目标函数里避免使用cell数组,多目标进化算法里数据都是数值矩阵,用cell会拖慢几十倍。还有,evaluate_objective尽量写成向量化形式,我实测从逐个体循环改成矩阵运算,时间从200秒降到80秒左右,差别非常明显。
6.5 结果复现时随机数种子问题
Matlab的随机数生成器在不同版本、不同操作系统下行为不完全一致。论文里的实验结果如果要他人复现,一定要在代码里固定rng种子,并且在论文实验设置里写明种子值和Matlab版本。这一点很多新手容易忽略。
7. 个人经验与扩展方向
在这套代码上继续扩展的话方向不少:如果要研究不确定性,可以把光伏和风电的预测误差用场景法建模,改成两阶段鲁棒优化或随机优化框架,NSGA-III仍然可以作为外层求解器;如果要考虑需求响应,可以把负荷侧的可平移、可中断负荷也建模为决策变量,维度会进一步增加;如果想加入运行风险指标,目标函数可以加到4个甚至更多,这时候NSGA-III参考点数会迅速爆炸,需要配合分层参考点或者其他降维手段。
还有一个很实用的体会:多目标优化算法跑出来的结果,不要直接丢给决策者,最好在后处理阶段用TOPSIS、灰色关联度或者简单的模糊隶属度选出一个或几个推荐方案,再配合可视化的调度Gantt图和Pareto前沿3D图,才算完整的可交付成果。我自己的代码里已经把这些后处理都加上了,输出就是一整套图和表,方便直接用进报告或论文。
搞这套代码前后花了大概三周,最花时间的不是算法本身,反而是微电网模型建模和Matlab向量化优化。如果你正在做类似课题,建议先把问题模型用数学公式写清楚,再动手写代码,否则会来回重构很多次。代码跑通之后,再回头优化性能、加后处理功能就都很顺了。