基于Python的配电网分布式电源双目标优化规划与可靠性评估实践
发布时间:2026/10/2 3:20:53 作者:尧图编辑部 阅读量:1,286

做配电网规划的人尤其是刚接触分布式电源规划的大概率都遇到过这样一个场景一块区域要接入光伏、风电、储能领导问装在哪、装多大、什么时候装还得说清楚“花多少钱”和“停电概率能降多少”。这两个问题放在一起就是典型的“经济与可靠性双目标”混合配电系统规划。我这次想聊的就是用Python完整做一遍这类研究的思路和代码实现包括目标函数怎么建模、可靠性评估怎么做、双目标怎么迭代寻优以及实操中容易踩的坑。这个项目解决的核心问题是在投资有限的前提下给分布式电源和储能找合适的安装位置和容量同时兼顾“今天少花钱”和“未来少停电”两个互相矛盾的目标。适合两类人参考一类是电力系统方向的研究生拿去改指标做论文另一类是规划工程师想用代码支撑可研报告里的选址定容论证。代码层面不需要太多工程经验能把IEEE 33节点这类标准算例跑起来再往自己的网络数据上迁移就行。1. 项目整体设计思路双目标、混合系统与Python的定位1.1 经济性与可靠性之间的拉扯才是规划的核心矛盾单看“经济目标”和“可靠性目标”各自都是好理解的经济目标是投资、运维、网损和停电损失的总成本尽可能低可靠性目标则是系统停电指标尽可能好。麻烦在于这两个目标几乎总是打架。你多装一台储能可靠性指标确实能变好但初期投资多出来几十万上百万单位停电时间可能只降了那么几分钟反过来你为了省钱只在架空线末端加一台光伏结果电压越限、单点故障时照样一片片区停电可靠性评估分数反而更难看。这种矛盾决定了不能用“先算一笔账再定方案”的单目标思路去处理。必须把两个目标都放到优化框架里让算法去找一组“非劣解”——也就是所谓的帕累托前沿。每一个解代表一种折中多花一点钱换来更高的可靠性或者省一点投资接受可靠性下降。这个思路在论文里很常见但落到代码层面很多人的做法是把两个目标直接加权成一个数再用遗传算法跑。这种加权法不是不行但有两个很明显的毛病一是权重很难拍脑袋定经济性和可靠性单位不同、尺度不同加权前还要做归一化归一化方式换个写法就影响最优方案二是加权单目标跑一次只能得到一个解想要全套折中方案得反复改权重重跑非常浪费时间。所以我在这套代码里直接用多目标进化算法一次运行就输出一整条帕累托前沿让规划人员自己根据资金和可靠性要求去挑选最终方案。这个选型是项目的核心定调后续所有的建模、评估、数据结构设计都围绕它展开。1.2 混合配电系统规划的技术内核“混合配电系统”这个提法听起来抽象落到实际规划里就是指传统配电网变电站、馈线、开关、变压器和主动元素光伏、风电、储能的混合。规划要做的事情其实可以分成三个维度第一个维度是选址。分布式光伏、储能、联络开关这些设备装在哪个节点对电压分布和供电可靠性的影响差别非常大。末端节点装光伏往往能改善电压但末端短路容量小光伏逆变器故障时也可能带来更复杂的暂态问题储能放得离负荷中心近避峰填谷效果好但地价和消防要求也会推高成本。这些都是模型里要反映的现实约束。第二个维度是定容。容量太小了起不到作用容量太大了又会引起反送电、过电压。确定容量的方式有多目标优化的进化迭代、也有基于灵敏度分析的经验估算项目里采用的是前者因为可以和选址问题天然地放在同一套决策变量里编码。第三个维度是运行策略。规划不能只看设备装上之后的样子还要模拟它在一年8760个小时里怎么运行。光伏出力早晚低中午高、负荷有峰谷差、储能按什么规则充放电都会影响电网运行费和损耗。所以规划模型里内嵌了一层运行模拟用典型日时序数据或者全年小时级数据来做成本核算和可靠性分析。选址、定容、运行模拟三层耦合在一起再加上可靠性的蒙特卡洛评估整个问题就变成一个高维、非线性、带大量约束的多目标组合优化问题。手算是不可能了商用软件也不一定给你这么细的控制权所以用Python做这件事本质上是把那三层逻辑“串”成一套可复现的算法流水线。1.3 为什么这套研究更适合用Python而不是MATLAB或GAMS这个项目最早也有人用MATLAB做过类似工作但我个人体验下来Python在三个环节有明显的优势。首先是拓扑处理。配电网本质上是一张图节点和支路的关系用NetworkX这类库表达会非常顺手。构造IEEE 33节点的邻居矩阵、找供电路径、判断故障隔离后的复电路径都是图算法的问题Python写起来很直白代码可读性也比MATLAB的矩阵思维好。你可以想想如果在MATLAB里靠0-1矩阵去绕一个最小生成树或判断潮流路径得多费劲。其次是时序数据的组织。规划仿真涉及8760小时的光伏出力、负荷曲线、燃料成本和故障率参数这些数据用Pandas的DataFrame来管理合并、切片、分组统计都非常简单。特别是最后做可靠性指标汇总的时候不同故障场景下的失负荷量要按用户数和时间加权Pandas一句groupby就搞定了。第三是生态完整度。NSGA-II这种多目标进化算法Deap、Pymoo这些库都有现成实现蒙特卡洛抽样可以用Numpy的并行向量化计算大幅提速帕累托前沿散点图用Matplotlib画出来也专业。如果还希望把规划结果接进后期的配电网仿真软件写个CSV接口也比MATLAB打包分发方便不少。当然GAMS在求解确定性数学规划问题上确实更严谨但双目标优化和可靠性蒙特卡洛评估这两块本质上已经不是“给定公式求最优”的经典数学规划的活而是更像启发式搜索加随机模拟的工程作业Python的灵活性明显更合适。2. 核心模型与数学表达从物理问题到代数问题2.1 经济性目标函数怎么建模才不“失真”有的文章把经济性目标简化成“设备投资最小”这做出来的方案看起来省钱但实际运行之后会发现网损成本高得离谱或者停电损失远超节省的初投资。所以我在这套模型里把经济目标分成四块拼起来第一块是投资等年值。光伏、风电、储能的单位造价乘以装机容量再加上固定安装成本和配套并网设备费用然后用资金回收系数折算到每年。公式是 ( C_{invest} \sum_{i} (c_i \cdot S_i c_{fix,i}) \cdot \frac{r(1r)^n}{(1r)^n-1} )其中 ( r ) 是贴现率( n ) 是设备寿命。注意寿命取值的坑光伏和储能的寿命不同一定是分开折不能图省事统一套25年。第二块是运行维护成本。光伏逆变器和储能电池每年都有检修和更换费用通常按装机容量的比例取储能电池还可以加一个循环寿命衰减的修正系数。这部分金额不大但在方案对比里往往能拉开差距。第三块是网损成本。传统馈线在重载时线损很高分布式电源接入后如果布局合理能显著降低网络损耗。计算需要全年时序潮流将每个时段的线路损耗乘以电价再累加。这块用的潮流算法后面会讲先记住一条基本原则一年8760个小时逐点算潮流是标准操作如果只拿一个最大负荷断面算会严重低估或高估分布式电源的价值。第四块是停电损失成本。系统不是100%可靠切负荷意味着用户停电停电对应的损失就是初投资之外的一笔隐性账。工程上常用的做法是按各类用户的单位停电损失元/千瓦时乘以失负荷电量ENS这套代码也采用这个口径。把四个成本加在一起就是我要的经济性目标函数 ( \min C_{total} C_{invest} C_{om} C_{loss} C_{ENS} )。2.2 可靠性评估的指标选型与计算口径可靠性评估这块很多人上来就想算SAIFI和SAIDI但其实规划阶段最常用的不止这两个。SAIFI系统平均停电频率关注“多久停一次”SAIDI系统平均停电持续时间关注“每次停多久”对失负荷更敏感的ENS期望失供电量则直接反映电量损失。还有一个容易被忽略的指标是系统最小供电能力也就是极端条件下还能带多少负荷。在这个项目的可靠性评估模块里我采用序贯蒙特卡洛模拟作为主力方法。思路是把每个元件馈线段、变压器、断路器的故障率和修复时间建模成随机过程然后按时间轴逐小时模拟系统的运行状态统计数据得出可靠性指标。公式层面元件 ( j ) 在模拟时长 ( T ) 内发生故障的次数 ( k_j ) 服从泊松过程故障率 ( \lambda_j ) 单位是次/年每次故障后的停电时间从修复时间分布里抽样负荷损失大小由潮流计算结果决定。最后把所有故障事件对用户和电量的影响累加就得到SAIFI、SAIDI和ENS的估计值。这套方法说起来不算复杂真正麻烦的是样本量。元件数多、故障率低的时候需要模拟很长的年份才能让指标收敛否则方差大得没法看。后面讲代码实现时我会给出一个效率上的应对办法。2.3 为什么要选NSGA-II做双目标寻优备选的多目标算法不少MOPSO、NSGA-III、SPEA2都有各自优势但NSGA-II是这个场景下最稳的选择。它的核心机制是非支配排序加拥挤度距离前者保证种群逐步收敛到帕累托前沿后者保证前沿上的解分布尽量均匀。我在代码里直接用了Pymoo框架里的NSGA-II里面已经处理好了选择、交叉、变异三大算子我需要做的只是定义好决策变量、目标函数和约束。选择NSGA-II而不是加权平均法还有一层实际考虑规划方案最终是要人拍板的。帕累托前沿上一排点每个点对应一个选址定容方案资金充足的单位可以选高可靠性方案预算紧张的可以选低造价方案而不是被某一个指定权重绑死。这也算是多目标优化的“决策透明性”——到底是多花钱换可靠性还是省下钱接受风险决定权回到规划人员手里。如果读者手上没有Pymoo自己写NSGA-II也不难核心是快速非支配排序和拥挤度计算两个函数加起来一百多行代码很多新手教程都有现成实现拿过来改数据结构就能用。只不过我自己用过几次Pymoo的接口之后确实回不去手写了毕竟算法本身不是研究创新点没必要重复造轮子。3. Python代码实现与实操细节3.1 网络建模以IEEE 33节点系统作为标准试验场规划算法的研究对象不能太复杂也不能太简单IEEE 33节点径向配电网是公开文献里最常用的算例之一节点和支路参数在论文、教材、甚至Pymoo的例子里都有现成数据。用这个系统的好处是拓扑规模中等潮流计算快故障场景可枚举而且电网规划领域的横向对比基本都以它为基准后续写作论文或者对接平台都方便。网络建模我是这样组织的用一个 ( N \times 2 ) 的二维数组存支路首端节点、末端节点。用一个 ( N \times 3 ) 的二维数组存线路参数电阻、电抗、允许载流量。用一个数组存各节点有功/无功负荷基准值。用NetworkX的from_edgelist函数直接生成DiGraph对象方便后续遍历潮流路径和故障隔离边界。代码里最核心的、也是最容易写错的地方是邻接矩阵的编号对位。不同数据源给出的节点编号从0开始还是从1开始的都有统一在读取数据后就地转换后面所有函数都只用内部编号避免混用。我在第一版代码里吃过这个亏算潮流时节点错位结果出来的电压分布图像抽象画排查了很久才发现编号差了一位。3.2 潮流计算用DistFlow方程不要上来就调库配电网规划仿真如果要调用通用潮流工具每算一个方案就要重复建模型、求解循环里叠加几千次性能会很要命。更合理的做法是直接用配电网领域常用的DistFlow方程利用网络径向拓扑推导出节点电压和潮流的递推关系。对一条从节点 ( i ) 到节点 ( j ) 的支路DistFlow方程的核心形式是[ P_{ij} P_{j} \sum_{k \in \text{children}(j)} P_{jk} r_{ij} \cdot \frac{P_{ij}^2 Q_{ij}^2}{V_i^2} ][ Q_{ij} Q_{j} \sum_{k \in \text{children}(j)} Q_{jk} x_{ij} \cdot \frac{P_{ij}^2 Q_{ij}^2}{V_i^2} ][ V_j^2 V_i^2 - 2(r_{ij}P_{ij} x_{ij}Q_{ij}) ]前向回推功率、后向更新电压迭代几次就能收敛。这个近似在径向配电网里精度足够而且因为完全基于向量化Numpy操作性能比调库快得多。代码实现上对根节点变电站母线赋 ( V1.0 ) 的标幺值然后从末梢节点往回累加功率再从根节点出发一层层往下更新电压直到最大电压偏差小于 ( 10^{-6} )。3.3 经济性评估与序贯蒙特卡洛可靠性评估实现经济性计算相对直接把2.1节那四个成本封装成一个函数calculate_cost(individual, load_curve, solar_curve)输入是优化算法给的DG位置和容量向量输出是总成本标量。内部流程是把DG容量写入节点负荷对典型日或全年时序负荷跑DistFlow潮流累计全网网损 ( \sum P_{ij}^2 \cdot r_{ij} )乘电价和时长叠加投资等年值和运维成本可靠性模块单独调一次把ENS乘以单位停电损失后并入总成本如果可靠性是独立目标就不并入这里而是作为第二个目标单独输出。可靠性模块实现要花点功夫。我用的序贯蒙特卡洛模拟核心步骤是这样定义元件故障率 ( \lambda )次/年和平均修复时间 ( r )小时每一步按指数分布抽样模拟下一个故障发生时间故障发生后根据网络拓扑判断哪些负荷节点失电再判断是否有备用电源路径可以恢复部分负荷对每个故障事件记录频率、持续时间、损失负荷和损失电量模拟结束后用统计公式汇总指标( SAIFI \frac{\sum \lambda_i N_i}{\sum N_i} )( SAIDI \frac{\sum \lambda_i U_i N_i}{\sum N_i} )( ENS \sum \lambda_i U_i P_{load,i} )其中 ( U_i ) 是节点 ( i ) 的不可用率( N_i ) 是用户数。代码层面故障抽样和失负荷计算是整个项目最耗时的部分。我做了两个加速处理一是把一年8760小时压缩成故障时段加恢复时段的事件序列一个模拟周期里省掉大量无故障小时的空转二是用Numpy的随机数生成批量生成故障序列把“逐个事件模拟”改成“并行向量抽样”实测下来速度能提升一个数量级。另外对于有备用电源接入的节点失负荷计算还需要做一个简单的“是否满足容量约束”的判定比如联络线能带的负荷上限是多少超过部分仍视为失电。3.4 双目标寻优的代码架构与参数配置整个优化模块我用Pymoo的NSGA-II框架搭建决策变量设计成变长数组前 ( N_{DG} ) 个是光伏候选节点的编号后 ( N_{DG} ) 个是对应的装机容量。容量可以做成离散档位比如0不装、100kW、200kW、500kW、1000kW这样更接近工程实际不会出现类似187.325kW这种没有供应商能做的容量值。核心代码如下from pymoo.algorithms.moo.nsga2 import NSGA2 from pymoo.core.problem import Problem from pymoo.optimize import minimize import numpy as np class DistributionPlanningProblem(Problem): def __init__(self, n_dg5): super().__init__( n_var2 * n_dg, # 位置 容量 n_obj2, # 经济性 可靠性 n_constr1, # 电压约束 xlnp.array([1] * n_dg [0] * n_dg), xunp.array([33] * n_dg [3] * n_dg), ) self.n_dg n_dg def _evaluate(self, X, out, *args, **kwargs): costs [] reliabilities [] violations [] for x in X: locations x[:self.n_dg].astype(int) capacities self.capacity_levels[x[self.n_dg:].astype(int)] cost calculate_cost(locations, capacities) rel calculate_reliability(locations, capacities) costs.append(cost) reliabilities.append(rel) violations.append(max(voltage_violation(locations, capacities), 0)) out[F] np.column_stack([costs, reliabilities]) out[G] np.column_stack([violations]) algorithm NSGA2(pop_size50, samplingrandom_sampling, crossoversbx_crossover, mutationpolynomial_mutation) res minimize(DistributionPlanningProblem(n_dg5), algorithm, (n_gen, 100), seed42, save_historyTrue)容量用离散整数编码再映射回物理容量等于是“整数优化”和“连续优化”的折中。这种方式在工程上最推荐因为优化速度和实际可落地性都更好。种群规模和代数根据算例规模调整IEEE 33节点配5个DG候选点时种群50、迭代100代以内基本够用再多耗时收益就边际递减了。最后画图的时候把res.F里的两个目标列分别作为横纵轴就是帕累托前沿散点图。前沿左端是经济优先的最便宜方案右端是可靠性优先的最可靠方案中间每一点都代表一个有意义的折中。可以用TOPSIS或者归一化距离再选一个“综合最优解”作为推荐方案但我的建议是把这个选择权留给人。4. 常见问题与排查技巧实录4.1 序贯蒙特卡洛评估慢到让人头大这是我在项目里碰到的最影响效率的坑。第一次跑一个50个体的种群每个个体都要做全年蒙特卡洛可靠性模拟单次模拟大概要模拟几万小时的事件序列结果一整轮迭代要跑半小时以上。后来优化了三个地方一是把“全年遍历”改成“事件驱动”。正常运行时没有故障的时段不需要逐小时记录只在发生故障的时刻才触发潮流和失负荷计算这样无故障时间长的时候计算量急剧下降。二是向量化抽样。一次生成成千上万个故障事件的下一次发生时间合并成一个大数组统一处理比在Python循环里一个个调用np.random.exponential快得多。三是最重要的规划循环和可靠性评估解耦。优化算法迭代前期不需要特别精确的可靠性指标误差大一点不影响方向判断先在少量样本比如模拟10年下跑粗选筛掉明显不可能落在帕累托前沿上的个体最后精确评估可以只针对保留的候选解做长样本比如模拟5000年的蒙特卡洛。这个“粗筛再精估”的思路是工程上解决计算瓶颈的通用招数。4.2 帕累托前沿聚集在一侧看不到完整的权衡关系如果画出的前沿点全挤在图像左上角或者右下角说明算法搜索方向偏离了。常见原因有三个第一个是种群在初始化时被约束条件卡死大量无效个体被惩罚剩下能活的个体特征同质化严重第二个是容量编码阶距太大可行方案数量太少前沿自然稀疏第三个是目标函数尺度差太大可靠性指标波动范围是几十到几百经济指标是几百万到几千万算法会不自觉地偏向尺度大的目标。解决办法把经济性和可靠性分别做线性归一化后再进优化函数让两个目标的量级大致相当容量档位从5档扩到9档甚至直接连续编码再取整初始化种群时用启发式方法优先在所有节点里均匀撒点而不是纯随机抽。还有一个调试技巧先随机生成1000个个体只算目标函数不做进化把目标空间画出来看看可行域的大致形状如果可行方案本身就分布很差那问题就出在建模而非算法参数上。4.3 潮流不收敛和电压越限的隐性原因潮流计算报“迭代不收敛”是高频问题尤其是在把DG接到末端节点、出力又很大的时候。我排查这一类问题的顺序是固定的先查网络参数有没有写错电阻电抗单位是标幺还是欧姆再查负荷功率方向有没有符号反了最后查DistFlow的迭代更新公式有没有把 ( V_j^2 ) 和 ( V_j ) 弄混。最让人困惑的一次是明明潮流收敛了但电压结果一直偏高查下来发现是我在计算分布式电源出力时段时把光伏出力的峰值时长没有对齐当地的负荷峰值曲线导致中午光伏大发但负荷很小电压被推得很高。这属于数据时序错位问题程序本身没有锅。电压越限不只是可靠性问题它还直接决定方案能不能过评审。在代码里加约束最简单的方式是把每个节点的最大电压偏差比如超过1.05标幺取绝对值后加起来作为惩罚项加进目标或者用Pymoo的约束函数传进去。我通常用后者因为多目标优化里加惩罚容易干扰帕累托前沿的形态。4.4 数据口径不统一导致的经济性失真规划研究里经济性失真最常见的来源不是模型复杂而是参数之间各说各话。贴现率取了8%设备寿命又按10年结果等年值算出来高得离谱可靠性评估里的停电影响没有区分居民、商业和工业用户动不动按一个笼统的单位电价一乘停电损失就和网损成本一样“凭感觉”。我的处理习惯是把所有参数放进一个params.json或者一个专门的config函数里标明来源和适用条件。另外要强调一点可靠性参数本身的置信度远比优化算法精确度重要。故障率参数差一个数量级优化结果可能完全不同。所以项目里我专门做了一轮敏感性分析把故障率上下浮动50%看帕累托前沿上推荐方案的变化范围。这一步能清晰地向决策者传达“规划结果对参数依赖的程度”比单纯报一个优化值有说服力得多。最后分享一个实际项目里帮了我大忙的小技巧。整套代码里可靠性评估函数写完之后不要急着嵌进优化循环先拿单独的测试网络、已知可靠性的文献数据把SAIFI和SAIDI跑出来对比。指标能对上了再考虑效率优化和接入NSGA-II。原因是可靠性评估是整个项目里最容易改错但最难查错的部分如果直接从优化结果反推指标正确性在全套算法互相耦合的条件下出了问题根本分不清是优化器的问题还是评估器的问题。模块之间先各自验证再组装这个顺序能省下大量排查时间。