混合配电系统双目标规划实战:NSGA-II与Python实现

发布时间:2026/10/3 9:24:48
混合配电系统双目标规划实战:NSGA-II与Python实现 我刚接触这个课题时其实有点不以为然配电网规划做了这么多年线径选粗点、变电站多布几个点经济性和可靠性不就都上去了直到把光伏、储能、直流充电桩这些设备真正接入模型才发现问题远没有那么简单——交流系统里塞直流设备换流器怎么选、直流馈线怎么铺、故障怎么隔离每一个决策都在同时拉扯投资金额和供电可靠性这两本账。这篇文章就来复盘我完成的一个完整项目用Python实现混合配电系统的经济性与可靠性双目标规划以及配套的可靠性评估模块。这个项目适合两类人参考一是正在做配电网规划、微电网或交直流混合系统方向课题的学生和工程师二是想把多目标优化算法和电力系统可靠性分析串起来落地的算法工程师。整体思路是从为什么要做双目标建模讲起然后是NSGA-II的求解框架接着是可靠性评估的程序化实现最后是算例和真实的坑。全文以我个人的工程视角展开尽量把每一步的取舍逻辑说清楚。1. 混合配电系统为什么混合值得单独规划1.1 传统交流规划方法在交直流混联场景下的失效点很多初学者会问混合配电系统规划是不是在传统交流配电网规划基础上多加几个换流器和直流馈线就行我最初也是这么想的直到被一个园区项目打脸。当时我按传统方法做网架扩容优化把光伏电站、直流充电桩全部折算成交流节点的等效负荷用线性规划找最小投资方案。优化结果出来之后我在夏季午间场景下做校核发现换流器接入点附近的电压已经跌到0.92 p.u.以下直流馈线末端更是越限严重。问题根源在于直流设备接入后系统潮流分布和电压支撑机制彻底变了。交流规划里靠变压器分接头和无功补偿就能解决的电压问题在直流侧要通过VSC的有功/无功解耦控制来调节而VSC的容量、损耗、接入位置每一步都和系统可靠性直接挂钩。纯交流规划方法失效的核心原因有三个。第一辐射状配电网络的潮流模型假设被打破——直流馈线和换流器形成电源-负荷高度耦合的新通道功率可能从直流侧反灌交流侧双向潮流成为常态。第二电力电子设备的故障特性明显区别于传统变压器和线路它们故障率高、修复快、控制逻辑复杂可靠性建模需要单独处理。第三直流系统的电压等级、保护配合方式和交流系统完全不同不能简单地折算进交流模型。1.2 混合网架模型的基本构成与决策变量在做正式建模前我给系统画了一个清晰的边界。典型混合配电系统包含三个部分交流子网保留传统辐射状馈线、配电变压器负责向常规交流负荷供电。直流子网由直流母线、直流馈线、DC/DC变换器组成汇集光伏、储能、直流充电桩等设备。VSC换流器交流与直流子网的耦合桥梁实现功率双向流动和电压支撑。规划层面的决策变量我分成了三类决策类别具体变量说明网架类交流线路扩容/新建、直流馈线走径0-1整数变量决定拓扑设备类VSC安装位置与容量、储能配置功率/容量混合整数变量运行类储能充放电策略、换流器控制模式连续变量归到运行模拟中这三类变量在优化中交织在一起构成一个典型的混合整数非线性规划MINLP问题。直接用数学规划求解器很难收敛这也是我选择启发式算法NSGA-II的根本原因——这个问题规模不大但维度复杂不追求全局最优的数学证明只要能在有限时间内给出一组工程上可信的Pareto解即可。1.3 为什么一定要做双目标而不是单目标从工程实际看经济性和可靠性在配电系统里是一对天然矛盾。投资更多线路和换流器可靠性确实会上升但成本也会同步上涨反过来过度压缩投资某些薄弱环节就会变成停电风险点。如果只把可靠性作为约束条件比如EENS不能超过50 MWh/年或满足N-1准则那么优化结果会有一个隐患满足约束即可不再有继续改善可靠性的动力也没有暴露出为了降低这最后1 MWh的缺供电量需要付出多少额外投资这个工程师最关心的信息。双目标优化的价值就在这里它把经济性与可靠性的权衡关系以Pareto前沿的形式完整展现出来。决策者可以看到当前方案处在曲线的哪个位置再往下压停电损失需要额外投入多少。Pareto前沿本身就是一个关于置换成本的工程决策工具。2. 双目标模型怎么建经济账与可靠性账的算法化2.1 经济性目标把每一笔钱算细我采用等年值法处理整个规划期的经济性指标将一次性投资折算成每年等额支出方便与运行费用直接相加。经济性目标函数为C_inv等年值投资成本包括交流线路扩容、直流馈线新建、VSC设备、储能设备的投资乘上等年值系数 converting 总现值到年值。C_om年运行维护成本按设备容量的比例系数估算VSC、储能这类电力电子设备的运维费率明显高于传统变压器。C_loss年网络损耗成本交流损耗和直流损耗、换流器损耗都要算。注意VSC损耗不是常数我按额定容量的空载损耗加负载损耗的二次函数近似。C_EENS停电损失成本前面算出的EENS乘以单位停电损失费用比如按不同负荷类型的产电比赋值。经济性目标里最容易出错的是等年值系数的选取。我习惯用公式等年值系数 (i * (1i)^n) / ((1i)^n - 1)其中 i 为折现率n 为设备寿命。不同设备寿命不同——线路通常按20-30年储能按8-10年电力电子设备按10-15年——所以不能图省事用同一个系数否则储能投资会被明显低估。2.2 可靠性目标为什么选EENS而不是SAIDI可靠性指标有很多SAIFI系统平均停电频率、SAIDI系统平均停电持续时间是供电企业最常用的。但在这个优化模型里我选择EENS期望缺供电量作为第二目标原因很简单EENS是一个连续、非线性但平滑度较好的系统级指标它能同时反映停电深度和停电时长而且天然与经济损失挂钩。SAIDI和SAIFI更适合做考核指标不适合做优化目标——它们会把所有负荷等权看待导致算法倾向于保护重负荷节点而忽略末端轻负荷节点这在规划层面是有失偏颇的。EENS则按负荷量加权算法会自然地把最贵的电和最重要的节点优先保护起来这与工程直觉是一致的。2.3 约束条件电压、容量、辐射状拓扑一个都不能少模型的约束条件我在代码里分成了三组潮流约束交流节点功率平衡方程直流节点功率平衡方程VSC两侧功率耦合关系。安全约束交流节点电压在0.95~1.05 p.u.范围直流节点电压在±5%范围内线路和换流器容量不超过限值。结构约束交流侧必须保持辐射状运行不得形成环网这是配电系统继电保护的基本要求直流馈线必须有明确的功率流向控制防止多换流器并联时出现环流。结构约束在实际编码中很容易被忽略。我曾经在某次迭代中放任算法尝试接近环网运行的拓扑结果可靠性评估结果异常偏低因为环网能把失电区域互相转供但这个状态在实际系统中受保护配合限制根本不可能长期运行。后来我在解码阶段就加入拓扑检测只要发现环路就判为该个体不可行直接淘汰。2.4 双目标处理的常见误区把双目标变成单目标的最常见操作就是线性加权把经济性指标和EENS乘以权重系数相加。这个做法的坑在于两个目标的量纲和数值量级差异巨大——投资动辄百万级别EENS可能只有几十MWh权重系数稍有变化结果就完全被经济性主导。更隐蔽的问题是经济性与可靠性的Pareto前沿往往是非凸的线性加权法只能找到凸包上的解对于非凸区域内的折中方案根本搜索不到。这意味着你费劲调出来的权重得到的可能只是整个权衡曲线上的一小段。NSGA-II这类多目标演化算法不依赖目标函数的凸性假设能更完整地覆盖权衡空间。3. NSGA-II求解框架Pareto前沿是怎么被搜出来的3.1 决策变量编码从拓扑到基因串在用NSGA-II之前首先要解决编码问题。一个规划方案的基因由三部分组成# 示意一个个体的染色体结构 # 前N1位0-1编码的交流线路是否扩容/新建 # 中间N2位0-1编码的直流馈线是否建设 # 后N3位整数/连续编码表示VSC容量等级、储能容量 individual { ac_line: [0, 1, 0, 1, ...], # 二进制 dc_line: [1, 0, 0, 1, ...], # 二进制 vsc_cap: [0.5, 1.0, 0.0, ...], # 连续值MW storage: [0.2, 0.4, 0.0, ...] # 连续值MWh }这里有个很重要的小技巧VSC容量和储能容量虽然是连续变量但实际工程中设备容量都是离散档位。我在解码阶段会做一个整档化处理把连续基因值映射到最接近的标称容量档位这样方案才具备工程可实施性。连续变量在算法内部保持连续是为了维持种群多样性解码时再离散化两不耽误。3.2 NSGA-II的核心机制非支配排序与拥挤度NSGA-II之所以在工程多目标优化里长盛不衰是因为它用三个机制解决了怎么搜索和怎么保持多样性的问题非支配排序把种群分成从优到劣的若干层。一个解如果两个目标都不弱于另一个解并且至少在一个目标上严格更优那么它就是支配者。不被任何其他解支配的个体组成第一层去掉它们后再次排序得到第二层以此类推。优化过程只保留层级靠前的个体这就保证了每一代种群都向Pareto前沿逼近。拥挤度距离用于同层个体之间的取舍。Pareto前沿上如果个体扎堆在某一段另一段空荡荡说明多样性差。拥挤度距离衡量某个个体两侧相邻个体的距离距离越大说明该区域解越稀疏应该优先保留。这个机制让算法尽量均匀地覆盖整个前沿。精英保留策略把父代和子代合并后统一筛选确保优秀个体不会在交叉变异中被淘汰。这三个机制配合起来算法无需手动设置权重也不需要任何梯度信息只要给定评价函数就能迭代搜索这是它能应对MINLP问题的关键。3.3 用pymoo实现NSGA-II的实验框架Python生态里做多目标优化我用过的库里有pymoo、DEAP、Platypus最顺手的是pymoo。它的API干净算子内置齐全而且支持自定义评价函数非常适合和电力系统的潮流计算、可靠性评估模块对接。核心框架如下from pymoo.algorithms.moo.nsga2 import NSGA2 from pymoo.operators.crossover.sbx import SBX from pymoo.operators.mutation.pm import PM from pymoo.operators.sampling.rnd import IntegerRandomSampling from pymoo.core.problem import Problem from pymoo.optimize import minimize class DistributionPlanningProblem(Problem): def __init__(self, n_var, ...): super().__init__( n_varn_var, n_obj2, xl0, # 变量下界 xu1, # 变量上界实数归一化 vtypefloat ) self.data load_system_data() def _evaluate(self, X, out, *args, **kwargs): # X是一个种群每次调用需要批量评价多个个体 costs [] eens_list [] for individual in X: network decode_individual(individual) cost economic_evaluation(network) eens reliability_evaluation(network) costs.append(cost) eens_list.append(eens) out[F] np.column_stack([costs, eens_list]) algorithm NSGA2( pop_size100, samplingIntegerRandomSampling(), crossoverSBX(prob0.9, eta20), mutationPM(prob0.1, eta20), ) result minimize( problem, algorithm, (n_gen, 200), seed42, verboseTrue )这里我特别提醒一点**评价函数一定要用向量化/批量方式写不要在_evaluate里逐个跑潮流。**一个种群100个个体每个个体要跑一次时序潮流加一年或多年的可靠性模拟如果串行评估一轮迭代可能就要几分钟。后面第五部分我会专门讲性能优化的经验。3.4 调参经验种群大小与迭代次数的平衡NSGA-II参数我用过几轮记录一下在实际系统中的表现参数常用范围我的最终配置备注种群大小50-200100过小收敛不稳过慢且计算量爆炸迭代代数100-500200在这个算例上200代已经基本稳定交叉概率0.8-1.00.9靠高交叉率保留优秀骨干变异概率1/n_var左右0.05太大容易退化成随机搜索SBX分布指数eta15-3020控制交叉后子代偏离父代的程度多项式变异eta15-3020控制变异步长判断是否收敛不要单看最优解变化要观察整个Pareto前沿的覆盖度和超体积指标。我每次迭代结束都记录当前的Pareto前沿如果连续30代前沿几乎不再向外扩展就认为收敛了。在写论文或出报告时建议用不同随机种子跑至少5次以免一次运气不好得到异常结果。4. 可靠性评估的程序化实现从状态枚举到序贯模拟4.1 解析法和模拟法的边界选择可靠性评估有两类经典方法解析法枚举故障状态和模拟法蒙特卡洛。我在这个项目里做过详细对比解析法背后的思路是把所有可能故障状态枚举出来算概率和后果适合元件数量少、拓扑简单的系统。模拟法通过对元件状态时序抽样模拟系统长期运行统计停电指标。混合配电系统里元件类型多VSC、DC/DC变换器、储能控制逻辑各有各的故障模式而且运行状态切换频繁。解析法枚举组合数会爆炸而且很难模拟储能充放电的时序逻辑。所以我选择序贯蒙特卡洛模拟它天然支持时序性储能对策、换流器状态转换都能自然表达唯一的问题是计算量大需要在代码层面优化。4.2 序贯蒙特卡洛的Python实现思路序贯蒙特卡洛本质上是构造系统一整年的运行时间线逐年累加停电信息。核心流程分四步第一步元件状态抽样。每个元件用两状态模型正常运行/故障。正常状态的持续时间TTFtime to failure按元件故障率lambda用指数分布抽样故障状态的持续时间TTRtime to repair按平均修复时间用指数分布抽样import numpy as np def sample_component_state(lam, mu, rng): lam: 故障率次/年 mu: 修复率次/小时mu 1 / MTTR 返回 (正常持续时间, 修复持续时间)单位小时 ttf rng.exponential(1.0 / lam) # 单位从年转小时要乘8760 ttr rng.exponential(1.0 / mu) return ttf * 8760, ttr这里有个单位换算的坑故障率通常以次/年为单位修复时间以小时为单位不统一好单位会让EENS结果差好几个数量级。我在代码里统一把所有时间折算成小时。第二步叠加所有元件事件生成系统事件序列。把所有元件的状态切换事件按时间排序形成一条完整的事件流。第三步逐事件评估后果。从初始正常运行状态开始按事件流推进。当某个元件状态切换为故障时重新跑潮流判断是否出现失负荷如果失负荷通过最优削负荷模型算出该时段的缺供电量。第四步统计指标。模拟足够多年后对每年的EENS取平均和标准差计算收敛判据。4.3 收敛判据与耗时优化模拟几年才够蒙特卡洛的误差按1/√N下降其中N为模拟年数。我采用变异系数作为收敛判据方差系数 标准差 / (均值 * sqrt(N))当EENS的变异系数小于5%时停止模拟。在这个算例中一般需要模拟1500到3000年才能稳定收敛。听起来吓人但对应的只是几百次事件处理的循环纯Python实现其实可以接受。更关键的性能优化手段是提前终止如果某个故障状态下失负荷量极小比如小于系统总负荷的0.1%可以直接忽略它的后果只记录事件不跑潮流。这样能把总模拟耗时压缩40%左右。对于追求速度的场景我还会用拉丁超立方抽样替代简单的伪随机抽样在不损失精度的前提下把需要的模拟年数减少20%~30%。不过要注意用这类方差缩减技术后收敛判据的表达要相应调整否则容易误判。5. 算例测试与结果解读Pareto前沿长什么样5.1 测试系统配置用IEEE 33节点改造的混合算例我没有用现成的混合配电标准算例而是对经典的IEEE 33节点系统做了扩展改造保持原有交流馈线结构在其中一段馈线上接入一个直流子网直流子网包含光伏电源、储能和直流负荷通过一个VSC与交流母线相连。参数项数值交流系统基准电压12.66 kV交流节点数33直流节点数5光伏装机候选容量0~0.8 MW储能候选容量档位0 / 0.2 / 0.4 / 0.6 MWhVSC候选容量档位0 / 0.3 / 0.5 / 1.0 MVA线路扩容候选13条可选改造后的系统既有传统交流负荷又有光伏和充电类直流负荷能较好模拟真实的混合配电场景。5.2 Pareto前沿的三个特征区域运行NSGA-II 200代后得到的Pareto前沿见图1我是用matplotlib散点图直接画的。前沿从左下方到右上方分布典型解有三个端点A最小投资解投资仅100万元左右EENS高达72 MWh/年。这个方案几乎不做网架扩容不装储能只装一台小容量VSC勉强联通直流子网。它暴露出来的问题是直流光伏大发时换流器容易饱和导致弃光和局部停电并存。端点B最大可靠性解投资约360万元EENS降到19 MWh/年。这种方案加粗了主要馈线建设了全额储能VSC容量也选到最大但边际改善已经明显减弱——从方案C到方案B多花了100万EENS只降低了16 MWh。折中解C投资约210万元EENS约38 MWh/年位于前沿的膝盖位置。方案等年值投资万元/年EENSMWh/年储能配置VSC容量A10072无0.3 MVAC210380.2 MWh0.5 MVAB360190.6 MWh1.0 MVA三个特征区域的价值在于前沿左侧陡峭部分说明低成本提升可靠性空间巨大前沿右侧平缓部分说明继续堆投资已不划算。决策者如果预算在200万级别最优选择就是折中解C附近不必追求端点B。5.3 折中解的自动选择实际项目中决策者往往希望算法直接给出一个推荐方案而不是一整条曲线。我会用TOPSIS方法辅助选解先对两个目标做归一化然后计算每个Pareto解与理想点投资最小且EENS最小的相对距离选距离最近的解作为推荐方案。在我的算例中TOPSIS选出的推荐解与人工判断的膝盖点基本一致。这个方法不复杂几十行代码就能实现但能让项目报告里如何从多个方案中选一个这页PPT好写很多。唯一的注意点是权重设置——如果公司更看重投资预算适当加大经济性目标的权重即可。6. 代码工程里的坑我一直想有人提前告诉我的事6.1 潮流计算和优化器之间的数据孤岛问题第一个大坑是潮流计算模块和优化算法之间的数据接口。刚开始我把网络构建逻辑写死在评价函数里每个个体都重新创建一套networkx图对象再转成潮流计算需要的数据结构。结果30代之后程序慢到无法忍受后来发现90%的时间花在网络对象重复构建上。改进办法是把网络结构定义抽离成数据驱动预先把系统拓扑存成DataFrame每个个体只记录哪条线路扩容了、哪里加了设备这些改动标志评价时直接对基态数据打补丁而不是全部重建。这一步优化让单次评价耗时从0.8秒降到0.15秒整个优化流程提速了5倍。6.2 随机数种子的工程教训蒙特卡洛模拟里随机数种子处理不当会直接影响优化结果的可靠性。我踩过的坑是在NSGA-II的每个个体评价过程中循环调用全局随机数生成器导致同一方案两次评价的结果都不一样算法无法准确比较两个个体收敛曲线看起来像过山车。正确做法是每个个体分配独立的子随机数生成器从同一个主种子派生。这样同一个方案无论何时评价结果都一样优化过程更加稳定。同时整个优化程序跑前设置全局种子保证结果可复现——写论文的时候这一点尤其关键审稿人问你给出随机种子你得真的能复现。6.3 可靠性指标的一个隐蔽数值错误还有一个典型的Bug我需要提醒在累加EENS时我最初忽略了失负荷事件的发生时间直接把故障期间的所有负荷缺口都算进去了。看起来没问题但实际上故障修复时间的单位是小时而故障率抽样的时间单位是年两处时间基准不一致会导致EENS偏大到离谱。排查了一整天最后打印中间变量才抓出来。经验就是在可靠性代码的入口和出口各打印一次单位基准确认并且把所有时间量统一折算到小时后再做乘除否则这类看起来原理对、数值全错的问题极难发现。6.4 向量化还是不向量化算力与复杂度的权衡对于这类仿真与优化耦合的问题我建议先做性能剖析确认瓶颈在哪再动手优化。如果每个个体评价都要跑几千小时蒙特卡洛那再怎么向量化收益也有限不如先砍模拟年数或用方差缩减技术。另外一个实用技巧第一轮优化用低精度评价模拟500年选出有希望的备选方案后第二轮用高精度评价模拟3000年对备选方案精算。这种方法类似粗筛加精筛能省一半以上的总优化时间而最终结果与全程高精度优化几乎没有差距。7. 写在最后做完这个项目我个人最大的体会是混合配电系统的规划难点不在算法多么高深而在于把电力系统的物理约束、经济账本中的细节和可靠性计算的时序逻辑真正耦合在一起。任何一部分单独拿出来都不算难但凑在一起每一步细节错误都会被放大到整个优化链路里。如果要从头再做一个类似项目我建议先花三四天时间把可靠性评估模块单独搞定用已知的简单系统验证它的正确性再花两三天把潮流计算跑通最后才把NSGA-II套上去。顺序反了的话你会陷入优化结果不对到底是算法问题、模型问题还是可靠性模块问题的三重困惑里。最后再分享一个小技巧项目里所有参数包括折现率、设备寿命、故障率、修复时间我都录入到一个单独的YAML配置文件里代码不写死任何数字。后来换了套系统数据再跑只需要改配置文件不必动一行代码。这个好习惯让我在改算例、应对导师临时换参数时从容很多。希望这篇复盘能给你节省几天的踩坑时间。