拓冰建站拓冰建站
首页 / 资讯中心 / 正文

NSGA-III求解微电网多目标优化调度:建模、Matlab实现与Pareto前沿分析

最开始做这个课题的时候我以为微电网调度无非就是把几个目标写成函数、丢给优化算法去算跑出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(Mp-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(Mp-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的取值会影响参考点数量。当M3、p12时是91个点M3、p13时是105个点。种群规模建议与参考点数量接近或略大比如种群取104会比91效果好一点因为NSGA-III在关联阶段需要每个参考点附近有足够个体供选择。p值太小参考点太疏Pareto面覆盖差p太大参考点太密某些无实际解的区域会空置选择压力变得不均匀。3目标问题我一般取p10到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_c20、eta_m20这是Deb在早期论文里的推荐值配合同样的多项式变异分布指数效果稳定。如果你发现收敛太慢可以适当减小eta_c到10如果多样性差可以增大到30。多数文献也都在这个范围内取。变异概率pm一般取1/nn是决策变量个数。我这里的调度问题有96个决策变量pm1/96约等于0.0104也就是说每个个体平均变异1个基因。变异步长由多项式分布决定分布指数eta_m20保证了小步长变异为主偶尔有大幅度跳跃这样有利于局部精细搜索。这里要提一个容易忽略的点Matlab里的rand和randi在不同版本间对随机数生成器有差异为了实验可复现运行前记得设rng种子。我在主循环开始前加一句rng(42)同一组参数下每次跑出来的Pareto前沿完全一致复现论文结果和调试代码都很方便。4. 完整代码架构与关键函数实现4.1 工程文件组织结构整个Matlab项目我拆成几个文件每个文件职责单一调试起来清晰很多main.m主入口定义参数、调用优化流程、输出结果图和表格init_params.m设置微电网系统参数机组限值、电价、负荷曲线、光伏风电预测数据nsga3_main.mNSGA-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越界。我的修法是在评估目标函数之前按顺序从t1到t24逐时段检查SOC如果预测下一时段SOC会低于下限就把当前时段充电功率调大如果会高于上限就调小充放电。这个操作本质上是用启发性规则修复不增加目标函数复杂度效果很直接。第二处理爬坡约束。MT和FC相邻时段出力差超过限值时后一时段的出力值被修正到限值边界。注意这里修正后可能还需要重新检查功率平衡所以循环多跑几遍直到所有约束都满足或达到最大迭代次数。第三对联络线交换功率越限的处理。如果用功率平衡反推P_grid那P_grid的上下限可能被突破。遇到这种情况我把超过限值的那部分功率差值加到罚函数里。这意味着个别极端不可行解仍然存在但它们的适应度会被严重惩罚自然会被淘汰掉。这是一种软处理比强行裁剪更符合遗传算法逻辑因为强行裁剪等于人为改变了决策变量会让种群的搜索方向偏差。5. 算例运行Pareto前沿、最优折中解与调度结果分析5.1 测试算例的参数设置我按典型微电网算例设置参数MT额定功率100kW爬坡限值30kW/hFC额定功率60kW爬坡限值20kW/h储能容量500kWhSOC运行范围0.1~0.9最大充放电功率80kW联络线功率限值200kW。负荷曲线取某园区冬季典型日数据光伏出力中午时段峰值明显风电在夜间出力较高。电价采用分时电价谷时段23:00-07:000.25元/kWh平时段07:00-10:00、15:00-18:00、21:00-23:000.53元/kWh峰时段10:00-15:00、18:00-21:000.82元/kWh。这个设置比较常规能较好地测试算法在不同电价区间内对储能和联络线功率的调度能力。NSGA-III参数种群200进化300代3个目标参考点每维划分数p12共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向量化优化。如果你正在做类似课题建议先把问题模型用数学公式写清楚再动手写代码否则会来回重构很多次。代码跑通之后再回头优化性能、加后处理功能就都很顺了。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门