开头
在配电网规划这个方向折腾了几年,一个特别明显的趋势是:现在的“配电系统”早就不是传统意义上那种单电源、辐射状、只负责传电的末端网络了。大量分布式光伏、风电、储能、EV充电桩甚至柴油应急电源接入后,系统变成了一种“混合电源+多形态负荷”的复杂网络。规划这类系统时,如果只盯着经济性,很可能算出一个投资最低的方案,但一到极端天气或者设备故障场景,可靠性惨不忍睹;反过来如果只追求高可靠性,成本又会成倍往上翻,项目根本批不下来。
所以这几年圈子里最常用的做法,就是做基于经济与可靠性双目标的混合配电系统规划。两个目标一起优化,通过Python可以完整落地。Python生态里的pandapower负责潮流计算、numpy/pandas处理数据、pymoo跑多目标进化算法,基本能把“规划+可靠性评估+双目标优化”整条链路打通。
这篇文章我会从一个可复现的角度拆开讲:混合配电系统规划到底在优化什么、可靠性怎么嵌入规划模型、Python代码结构怎么搭,以及我在实际跑数时踩过的坑。适合正在做配电网规划方向的学生、刚接触综合能源系统规划的工程师,以及想用Python替代传统商业仿真软件做研究的朋友。
1. 问题拆解:混合配电系统规划到底在优化什么
1.1 什么是混合配电系统
先明确边界。混合配电系统的“混合”通常指两个层面:
一是电源侧的混合。系统里不只有上级变电站提供的传统电力,还有分布式光伏、分布式风电、燃气轮机、储能装置等。不同类型电源的出力特性差异很大:光伏中午大发、晚上归零;风机随风速波动;储能既能充电又能放电,相当于多了个“时间搬移”的工具。规划时要在什么节点接入什么类型的电源、接入多大容量、储能配在哪,这些都是决策变量。
二是网络结构层面的混合。传统配电网多为单电源辐射状,一根线路从头串到尾,某个开关跳闸,下游全黑。现在规划常常要引入“多分段多联络”的结构,甚至形成弱环网,让故障时部分负荷能从其他馈线转供。不同拓扑下,同样一套设备的供电可靠性差异非常大。
把这两层叠加起来,规划问题的复杂程度就上来了:既要做电源选址定容,又要做网架结构优化,还要考虑系统运行方式的约束。这种多变量耦合的问题,靠手工试算是完全不现实的,必须靠建模加算法求解。
1.2 为什么是“双目标”,而不是直接加一个权重
这是很多初学者最困惑的地方:为什么不能把可靠性折算成钱,和目标成本合并成一个单目标?
理论上可以,工程上也确实有人这么干——把用户停电损失定义为“缺供电量×单位停电损失费用”,然后加到总成本里。但这么做有很实际的问题:
- 单位停电损失费用很难定准。居民、商业、工业用户的停电损失差异可能相差一个数量级,而且不同地区、不同场景取值争议很大。
- 把目标合并成单目标后,其实是在人为指定两个目标之间的“汇率”,但规划者往往不确定这个汇率应该定多少。
- 单目标优化只能给一个“最优解”,但实际项目评审中,决策者更需要的是一组“方案套餐”:哪个投资最低但可靠性一般、哪个可靠性高但贵多少、有没有中间选项。
双目标优化的输出是一条Pareto前沿。前沿上的每个方案都不被另一个方案完全支配——想要更好的可靠性,就必须付出更高的成本;想压缩成本,就得牺牲一部分可靠性。最终选谁,取决于决策者的偏好和项目预算。这个框架,比强行合并成单目标要合理得多,也是论文和实际工程中都比较认可的思路。
1.3 规划中的决策变量与约束条件
在模型层面,规划问题需要明确决策变量和约束。我整理一个自己常用的模型框架:
| 模块 | 决策变量 | 说明 |
|---|---|---|
| 网架规划 | 线路新建/升级0-1变量、联络开关状态 | 决定系统拓扑结构 |
| 分布式电源 | 各节点光伏/风机投建容量(连续或离散) | 影响潮流分布与可靠性 |
| 储能系统 | 储能额定功率与容量、安装节点 | 削峰填谷和应急供电作用明显 |
| 无功补偿 | 电容器或SVG配置容量 | 约束电压质量,影响潮流收敛性 |
对应的约束条件包括:
- 潮流方程约束(节点功率平衡)
- 节点电压上下限(比如0.93 p.u.到1.07 p.u.)
- 支路容量约束(线路不能过载)
- 储能充放电功率与SOC约束
- 分布式电源渗透率约束(避免反向潮流过大)
- 可靠性最低限值(如SAIDI不能超过某个阈值)
这些约束在优化算法里,有的可以通过潮流计算直接检验,比如电压和过载;有的则需要单独建模,比如储能SOC的动态变化。Python实现的时候,比较简便的做法是把不满足约束的个体直接通过罚函数淘汰,或者用修复策略强制拉回可行域。
2. 可靠性评估:核心指标与嵌入规划的方法
2.1 可靠性指标选哪些
配电系统可靠性评估的经典指标主要有三个:
- SAIFI(系统平均停电频率指标):单位用户年停电次数的平均值,反映“多久停一次”。
- SAIDI(系统平均停电持续时间指标):单位用户年停电小时数的平均值,反映“每次停电要停多久”。
- ENS / EENS(失电量 / 期望失电量):系统因故障而损失的电量,反映停电对供电量的实际影响。
在双目标规划里,我个人更倾向直接用EENS作为可靠性目标,而不是SAIFI或SAIDI。原因很简单:EENS和“停电损失”挂钩最紧密,而且和成本目标不是同一个量纲,做Pareto前沿更直观。SAIFI、SAIDI则可以作为约束或辅助校验指标。比如某个候选方案虽然EENS很低,但如果单个停电事件时间过长,可能违反用户对最长停电时长的要求,这种情况就需要用SAIDI卡一下。
2.2 可靠性评估的主流方法
配电网可靠性评估的方法可以分三类:
故障模式与后果分析法(FMEA)。这是最经典也是工程上最常用的方法。思路是枚举系统中每一个元件(线路、变压器、开关)的故障,分析它对下游负荷点造成的影响,然后累加所有故障事件的影响,算出系统级指标。优点是原理透明、速度极快,适合规划阶段的成百上千次方案评估;缺点是需要人工/程序生成故障事件表,对含分布式电源和储能的系统,还要额外分析孤岛运行状态,逻辑会复杂不少。
最小割集法。本质上是把FMEA自动化:找出系统失效的最小割集组合,再通过故障概率计算系统的不可用率。适合拓扑比较复杂的网状网络,但建模门槛高,代码量也大。
序贯蒙特卡洛模拟。按时间序列模拟元件运行和故障状态,比如模拟8760小时或10000年的运行过程,统计停电事件。好处是能精细刻画储能充放电策略、光伏随机出力、负荷时序变化对可靠性的影响;坏处是计算量非常大。内层跑潮流、外层跑模拟,容易让整个规划算法慢到无法接受。
在双目标规划的大框架里,我的建议是:外层优化迭代用FMEA或解析法,对最终选出的几个Pareto方案再做一次序贯蒙特卡洛精细校验。这种“粗筛+精验”的组合,速度和精度都能兼顾。
2.3 怎样把可靠性评估嵌入规划循环
这里有一个新手特别容易忽略的点:可靠性评估不能放在规划优化结束后再补一次,必须嵌入规划循环内部。因为网架结构、电源位置、储能容量都会直接决定故障后的转供能力和孤岛运行范围,如果最后才算可靠性,优化过程就完全没有“可靠性视角”,算出来的最优方案很可能在可靠性维度上非常差。
我实际采用的流程是:
- 种群个体对应一组决策变量(线路开关状态、DG容量、储能配置)。
- 根据个体方案修改电网模型,调用潮流计算。
- 如果潮流收敛且电压/容量约束通过,再执行可靠性评估。
- 输出成本目标和EENS目标,返回给优化算法。
这样整个优化过程里,每个个体都经过“潮流+可靠性”两层校验,Pareto前沿上的每个解都是通过潮流可行的。代价是单次评估时间增加,所以外层算法要选得对、并行化要做得好。
3. Python实现:从数学模型到可运行代码
3.1 为什么要选Python生态
做配电网规划,传统工具是商业软件如DIgSILENT、CYME,或者自己写MATLAB程序。但Python这几年在科研和工程中的应用越来越广,主要原因有三:
一是pandapower这个库实在太方便了。它建立在pandapower内部的电力系统元件模型之上,可以很方便地搭建配电网拓扑、修改元件参数、跑潮流计算,不需要自己写导纳矩阵和牛顿-拉夫逊迭代。而且它对IEEE标准算例系统有内置支持,比如IEEE 33节点、123节点等,省掉了手工输入数据的大麻烦。
二是多目标优化算法库成熟。pymoo实现了NSGA-II、NSGA-III等主流多目标进化算法,接口规范,自定义问题和遗传算子都很灵活。如果自己从头写NSGA-II,至少几百行,还容易出bug。
三是数据处理和可视化的配套太全。pandas整理结果、matplotlib画Pareto前沿、numpy做矩阵运算,几乎不需要额外开发工具。
环境配置其实非常简单,三行命令的事:
pip install numpy pandas matplotlib pip install pandapower pip install pymoo建议用Python 3.8以上的版本,我用的3.9和3.11都能稳定跑通。如果你之前没装过Python,记得安装时勾选“Add Python to PATH”,不然后面命令行无法直接识别python,这是个很低级但频率极高的坑。
3.2 双目标优化算法选型
在Python里实现双目标规划,最常用的算法是NSGA-II。它的核心思路是:通过非支配排序把种群分成不同Pareto层级,同时用拥挤度距离保持解的多样性,再配合选择、交叉、变异生成新一代种群。
NSGA-II能够一次运行输出尽可能多的Pareto前沿点,很适合分析“成本-可靠性”的权衡关系。还有一个替代方案是epsilon约束法:先单独优化经济性目标,然后把可靠性作为约束逐步收紧,跑多个单目标问题后合成Pareto前沿。这个方法在解空间较小或约束复杂时更稳定,但需要反复求解,计算量也很大。
在pymoo中,我一般用NSGA-II配合SBX交叉和多项式变异。种群大小可以设置在50到100,进化代数200到300代。下面是一个自定义问题的骨架:
from pymoo.core.problem import Problem import numpy as np import pandapower as pp import pandapower.networks as pn class DistributionPlanningProblem(Problem): def __init__(self): # 决策变量说明: # x[0]: 联络开关状态(0-1整数) # x[1:3]: 光伏接入容量(连续) # x[3:5]: 储能功率(连续) # 实际项目中按自己的决策变量维度改这里 super().__init__( n_var=5, n_obj=2, xl=np.array([0, 0, 0, 0, 0]), xu=np.array([1, 2, 2, 1, 1]), ) def _evaluate(self, X, out): costs = np.zeros(X.shape[0]) eens = np.zeros(X.shape[0]) for i, x in enumerate(X): costs[i], eens[i] = evaluate_solution(x) out["F"] = np.column_stack([costs, eens])3.3 目标函数怎么算:成本与EENS
经济性目标的计算公式通常是:
C_total = C_inv + C_om + C_loss + C_purchase
其中C_inv是设备投资年化费用(线路建设、DG设备、储能设备等,按使用寿命折算到每年);C_om是运行维护费用;C_loss是网损折价;C_purchase是向上级电网购电的费用。这些每一项在代码里都比较直接——单位造价乘以容量再加固废,网损由潮流结果乘以电价得出。
可靠性目标的计算逻辑是:在某个故障场景下,识别停电负荷点集合,统计停电影响户数和失电量。对于每个故障事件k,如果它导致负荷点j停电,且停电持续时间为t_k,那么EENS的累加式是:
EENS = Σ_k λ_k × P_j × t_k
其中λ_k是元件k的故障率(次/年),P_j是该负荷点的平均负荷功率。这个公式本质上就是FMEA的数学表达,代码实现起来很省力。
3.4 可靠性评估代码的实用写法
我用pandapower实现FMEA可靠性评估,核心代码如下:
def evaluate_reliability(net, fault_rate_dict, load_p_mw_dict): # 遍历所有可选故障元件 total_eens = 0.0 total_saidi = 0.0 total_users = 0.0 for element, rate in fault_rate_dict.items(): # 断开故障元件 pp.run_temporary_disable(net, element) # 潮流计算,识别失电节点 try: pp.runpp(net) # 找出电压越限的负荷节点 lost_loads = identify_lost_loads(net) except: # 潮流不收敛通常意味着大面积失电 lost_loads = all_load_buses(net) # 累加EENS for bus in lost_loads: total_eens += rate * load_p_mw_dict[bus] * outage_duration_h # 恢复网络 pp.run_temporary_restore(net) return total_eens, total_saidi有几处细节必须提示:
- pandapower里
run_temporary_disable/run_temporary_restore是成套使用的,别只disable忘了restore,否则网络状态污染,后面所有元件评估都会错。 - 潮流不收敛很多时候不是网络真“死”了,而是初值不好或并联电容器控制策略导致迭代发散。要区分“潮流计算失败”和“系统失电”两种情况,否则EENS会被高估。
- 负荷点的平均功率建议用年负荷曲线折算,而不是用峰值负荷。用峰值算出来的EENS会严重偏高,这个误差在目标函数里会被放大。
4. 算例演示:用IEEE 33节点系统跑双目标规划
4.1 算例系统构建
这里我以IEEE 33节点系统为例,它是配电网规划领域最常见的标准算例:33个节点、32条支路、1个根节点,基准电压12.66 kV,总负荷约3.7 MW。pandapower里有现成数据:
net = pn.create_ieee33_bus()在基础网络上,我加了几处规划候选位置:
- 节点8、节点24、节点30可接入分布式光伏,单点容量上限1 MW。
- 节点14和节点29可配置储能,单点功率上限0.5 MW、容量上限2 MWh。
- 增加两个联络线候选:8-21、9-15,用于故障转供。
这样就构成了一个带有DG、储能、可调节网架的小型混合配电系统。决策变量一共是:2个联络开关0-1变量 + 3个光伏容量 + 2个储能功率 = 7维向量。
4.2 优化参数设置与运行结果
我用pymoo的NSGA-II做双目标优化,种群大小80,迭代300代,目标函数就是总成本(万元/年)和EENS(MWh/年)。跑完后把Pareto前沿画出来:
from pymoo.algorithms.moo.nsga2 import NSGA2 from pymoo.operators.crossover.sbx import SBX from pymoo.operators.mutation.pm import PM from pymoo.optimize import minimize algorithm = NSGA2( pop_size=80, crossover=SBX(eta=15, prob=0.9), mutation=PM(eta=20), ) res = minimize(problem, algorithm, ('n_gen', 300), seed=1, verbose=True)结果典型的形态是一条“L型”曲线:左边是最小成本方案,EENS较高;随着成本增加,EENS快速下降;但在曲线的右下段,再增加很多投资,可靠性提升已经很小。这就是经济学上说的“边际递减效应”,在配电网规划里表现得极其明显。
对项目实际决策来说,这条曲线最有用的信息是拐点:拐点之前的投入能“花小钱办大事”,拐点之后继续砸钱换可靠性就不划算了。如果有预算上限,也可以直接把预算约束画在图上,找交点处的方案。
4.3 从Pareto前沿到底选哪个方案
很多同学跑完双目标优化,拿到一整个前沿就不知道怎么汇报了。这里分享一个很实用的偏好选点法——TOPSIS:
- 对Pareto前沿上的所有解,按成本和EENS分别做归一化。
- 定义理想点(两个目标都最小)和负理想点(两个目标都最大)。
- 计算每个解到理想点和负理想点的欧氏距离,取贴近度最大的解作为推荐方案。
还有一种更简单的“拐点法”:在Pareto前沿上取每个点,计算它到两个端点连线的距离,距离最大的那个点通常就是性价比最高的折衷解。
我自己在论文和项目报告中,一般会把“纯经济最优”“纯可靠最优”和“折衷推荐”三个方案并列展示,分别列出投资、年运行成本、EENS、SAIDI这几个核心指标。这样评审人一眼就能看懂权衡关系,不会觉得你在给他一个“黑箱答案”。
5. 常见问题与排查技巧实录
5.1 潮流总是不收敛,怎么排查
这是最大概率会遇到的问题,而且原因多种多样。
第一步看元件参数。pandapower中等值线路的r_ohm_per_km、x_ohm_per_km、c_nf_per_km这几个参数是否合理。参数填错(比如数量级搞混)会导致整个网络阻抗异常。
第二步看电压初值。含大量DG接入的弱网络,用牛顿-拉夫逊很容易因为初值不佳发散。这时候可以先用pp.runpp(net, algorithm='iwamoto_nr')试试,或者在pandapower里把电压初值设为0.98~1.02 p.u.再算。
第三步,如果加了储能的充放电控制,注意储能元件在潮流模型里是PQ节点还是PV节点。储能按恒功率充电/放电时是PQ节点,如果按电压控制模式则要考虑无功支撑能力,初值不对也容易发散。
一个排查技巧:把DG容量逐步从0加到目标值,每一步都跑一次潮流。哪个节点加到一定数值发散,问题就出在哪。
5.2 可靠性评估慢得让人崩溃怎么办
如果用的是序贯蒙特卡洛,在规划算法里循环几千次,时间开销几乎是灾难性的。我的解决思路是分层评估:
- 在优化循环内部用FMEA解析法,计算速度毫秒级,精度对方案筛选足够;
- 对最终选出的3到5个Pareto方案,再做一次序贯蒙特卡洛精细验证,模拟年限取2000到5000年,算出更精确的SAIFI、SAIDI和EENS。
另一个加速办法是故障事件预生成。对同一个网络结构,故障元件集合是固定的,只把负荷水平和DG出力作为变量。可以提前枚举好所有故障场景,在优化循环内只查表累加,不重复跑潮流。我实测下来,这个优化能提速20倍以上。
5.3 NSGA-II跑出来的Pareto前沿质量差
现象一般是两类:前沿点太少,或者前沿点分布不均匀。
前沿点太少,大概率是种群数量不够。建议按决策变量维度的5到10倍设置种群大小。7维决策变量,pop_size至少70,我通常用100。
分布不均匀,常见原因是交叉变异参数没调好。SBX的eta参数控制子代离父代的远近,eta越小子代分布越散,越大越聚集。如果发现前沿只集中在一个小块,试着把eta从15调小到10;如果太散导致收敛差,就调大到20。多项式变异的eta也是同理。
还有一个容易被忽略的问题:如果两个目标函数的数值量纲差距巨大(比如成本是百万级别,EENS只有几十),需要对目标做归一化处理。pymoo里可以通过ref_point或标准化函数处理,否则NSGA-II的选择压力会一边倒。
5.4 Python环境配置相关的避坑
这部分给刚入门的读者。网上热搜里关于Python安装、numpy库安装的提问特别多,实际就三件事:
第一,Python环境变量。Windows安装时勾选“Add Python to PATH”,否则python命令找不到。如果已经装完发现不能用,手动把C:\Users\你的用户名\AppData\Local\Programs\Python\Python39\和同目录的Scripts\加进系统PATH,重新打开终端就好。
第二,包安装失败。pip install pandapower如果速度极慢或报错,换成国内镜像源:
pip install pandapower -i https://pypi.tuna.tsinghua.edu.cn/simple第三,numpy版本冲突。pandapower对numpy的版本有一定要求,如果pip install把numpy升到过新版本导致pandapower报错,可以用pip install "numpy<2.0"回退版本。我自己的环境是numpy 1.26.4,非常稳。
结尾
最后分享一点个人体会。双目标规划这种工作,代码本身不是最难的,难的是把电网物理逻辑“翻译”成优化模型时别偷懒。比如可靠性评估里每个故障场景是否真的识别对了停电范围,储能孤岛运行策略和故障时序是否一致,这些细节如果没捋清楚,不管算法多高级,出来的结果都是错的。
我一开始跑这个课题的时候吃过一次大亏:FMEA评估里把“故障元件上游”和“下游”的失电范围搞反了,导致EENS被严重高估,整个Pareto前沿直接往错误方向偏移。后来耐着性子把所有故障场景手算核对了一遍,才发现是转供逻辑写反了。
所以给想做类似项目的朋友一个建议:拿到任何一个新算例系统,先用一个小网络(比如3节点或5节点)手算验证你的代码结果,确认无误后再上标准33节点、123节点系统。这个“先小后大”的习惯,能帮你省下大量调试时间。多目标规划是个越做越有意思的领域,把成本和可靠的权衡讲清楚,无论在学术还是工程上都是特别值钱的能力。