前两年我接手一个配电网规划项目,AC/DC混合配电系统是绕不开的方向。光伏、储能、充电桩越接越多,直流负荷占比持续上升,要是全部走交流配电,变换环节的损耗和调节灵活性都在拖后腿。可真要回答“哪些馈线改成直流、换流器装在哪、容量选多大”,就得同时算清投资运行成本和供电可靠性这两本账,缺一不可。这篇文章就是把我做的一个完整Python实现拆开讲,核心是“基于经济与可靠性双目标的混合配电系统规划及可靠性评估”,从建模思路、指标口径、代码结构到算例结果,能直接参考复现。无论你是在做配电网规划课题的研究生,还是做分布式电源接入方案设计的工程人员,这套代码框架都值得拿过去改一改。
代码涉及三个核心点:混合配电系统的网络建模与潮流分析、考虑故障后果的可靠性评估、以及用多目标进化算法求解经济-可靠性双目标规划问题。下面的内容按“模型—指标—代码—算例—排错”的顺序展开,工程上关心的那些细节我会尽量点透。
1. 项目问题定义与总体设计思路
1.1 从“要不要混”到“怎么混”:规划问题的本质
交流配电是成熟了一百多年的技术,但直流设备比例上来之后,每接入一台光伏逆变器、一个充电桩,就多一套AC/DC变换环节。把这些设备直接接到直流馈线或直流母线上,能省掉一级变换,效率更高,控制也更直接,所以中低压直流配电网的工程试点和示范项目越来越多,方向本身已经不是争议点。
规划层面真正要回答的是“怎么混”:哪几条馈线从交流改成直流、在哪个节点装VSC换流器、装多大容量、联络和转供怎么安排。这些决策直接决定两类结果——投资和运行成本是一本账,故障时的供电可靠性是另一本账,而且两者经常打架。多加一台换流器、多改造一条直流馈线,可靠性指标确实会好看,但账面上多出来的投资也得有人认;反过来,为了省钱少装设备,一旦某个元件故障,转供路径不够用,停电范围和停电时间马上变大。
所以这个项目一开始就没打算做单目标优化,而是把问题定义成标准的双目标规划:让优化算法在“经济成本”和“可靠性水平”之间找一组非支配解,把权衡关系摆出来,交给规划人员按实际偏好去选。
1.2 双目标函数的数学表达
两个目标分别定义。第一个目标是年综合费用最小,包括线路和VSC的投资等年值、年运行维护费用、年网损费用三部分;第二个目标是系统年缺供电量最小,也就是EENS。写成公式就是:
- f1 = C_inv + C_om + C_loss
- f2 = EENS
有人会问,为什么不把EENS乘上一个单位停电损失,加进f1变成单目标?那样做其实没问题,而且求解更简单,但有一个前提:你得先给定单位停电损失,而不同地区、不同用户类型对停电的容忍度和损失评价差别巨大。与其拍脑袋定一个数,不如把EENS作为独立的第二目标,先算出一整条帕累托前沿,等明确了停电损失水平之后,再在前沿上挑点或换算,这样决策弹性大得多。
1.3 代码模块划分与执行流程
整个代码按四个模块组织,界线尽量清晰:
- 数据模块:管理网络拓扑、节点负荷、设备参数、可靠性参数。
- 评估模块:完成潮流与可靠性评估,输出网损、EENS、SAIFI、SAIDI等指标。
- 优化模块:NSGA-II主循环,负责种群进化、目标评价和帕累托前沿输出。
- 结果模块:整理方案对比表,导出规划结果。
优化主流程是:初始化种群 -> 解码每个个体得到网络方案 -> 对每个方案做可靠性和成本计算 -> 非支配排序 -> 锦标赛选择 -> 交叉变异 -> 循环迭代 -> 输出帕累托前沿。这个流程后面各章会展开讲。先记住一点:评估模块是核心,优化算法只是外壳,只要评估算得准、跑得快,换任何进化算法都能用。
2. 可靠性评估:指标体系与解析法实现
2.1 可靠性指标怎么选
配电网可靠性评估最后总要落到几个指标上。这个项目用了最常见的四个:SAIFI、SAIDI、EENS、AENS。表2-1把指标、单位、计算口径列出来,方便对照。
| 指标 | 含义 | 常用单位 | 计算口径 |
|---|---|---|---|
| SAIFI | 系统平均停电频率 | 次/户·年 | Σ(λi×Ni)/N |
| SAIDI | 系统平均停电持续时间 | 小时/户·年 | Σ(λi×ri×Ni)/N |
| EENS | 期望缺供电量 | MWh/年 | Σ(Pi×λi×ri) |
| AENS | 平均系统缺供电量 | MWh/户·年 | EENS/N |
其中N是用户总数,Ni是负荷点i的用户数,λi是元件故障率,ri是平均修复时间。SAIFI关心“停几次”,SAIDI关心“停多久”,EENS把功率和时间耦合在一起,直接对应停电损失,适合和经济性衔接。四个指标相互校验,能发现评估逻辑里不少隐蔽错误。
2.2 FMEA解析法的评估流程
规划阶段系统规模不大,元件数量上百,解析法完全够用,不需要一上来就上序贯蒙特卡洛。这个项目用的是故障模式后果分析FMEA,逻辑用一句大白话说就是:把每个可能坏的元件都坏一次,看看停电范围多大、停多久。
具体步骤分六步:对每个元件假设永久性故障;断开故障元件;判断系统是否被分割成孤岛;检查失电孤岛内是否有备用转供路径;有转供路径则停电时间为开关切换时间,比如1小时,没有则为故障修复时间;最后统计失电负荷功率和用户数,把所有故障事件的后果按概率加权累加得到指标。
这个流程里最容易踩坑的地方是转供判断。孤岛内如果只有分布式电源没有联络线,算不算有备用?答案取决于你的运行策略。我的代码里默认只考虑联络线转供,分布式电源按孤岛运行单独处理,避免结果过于乐观。如果你系统里有储能或者黑启动能力,要在这里单独扩展逻辑。
2.3 设备可靠性参数与数据来源
线路、变压器、VSC的可靠性参数来自统计分析或厂家数据。代码里统一放在一个reliability_param字典里方便修改。下面是一组常规量级的数据,供参考:
- 交流线路:故障率0.05次/(km·年),修复时间4小时
- 直流线路:故障率略高,取0.075次/(km·年),修复时间4小时
- VSC换流器:故障率0.2次/年,修复时间8小时
- 配电变压器:故障率0.01次/年,修复时间6小时
VSC是电力电子设备,器件多,故障率其实不低,修复时间也比常规线路长得多。把VSC故障率设成0.2次/年并不夸张,实测下来,VSC往往是整个系统里最薄弱的环节之一。可靠性评估做完之后,把每个元件的失负荷贡献排个序,VSC通常稳居前几名。
3. 经济性建模与约束条件
3.1 投资费用等年值计算
规划是长期决策,不能直接用初始投资比大小,因为设备寿命不同、资金有时间价值。这个项目把所有投资都折算成等年值,用到资金回收系数:
- CRF = r×(1+r)^n / ((1+r)^n−1)
- 年投资费用 = 初始投资 × CRF
示例里贴现率r取6%,线路寿命30年,VSC寿命20年。设备单价按常规工程经验估算,交流馈线改造单价、直流馈线造价、VSC容量单价都做成可配置参数,放在prices字典里,改一个数就能适配不同地区的造价水平。等年值折算看起来是个小细节,但算账方式不统一,方案对比就会失真。
3.2 运行成本与网损计算
运行费用主要是网损和运维。网损依赖潮流计算,规划阶段可以用简化直流法按典型负荷时段估算,也可以直接调用pandapower做精确潮流。这个项目用的是后者,pandapower的潮流结果稳定,交流、直流综合网络建模也方便。年网损费用=网损功率×年等效负荷小时数×购电价,其中年等效负荷小时数根据负荷曲线取经验值,通常用最大负荷损耗时间τ,大致在2500到4000小时之间。
运维费用采用比例系数法,取等年值投资的一定比例,比如线路和VSC的年度运维费率取1.5%到2%。这个系数各地口径不同,但作为规划阶段估算已经够用。
3.3 规划约束与可行性判断
规划方案必须满足运行约束:各节点电压在允许范围、线路和VSC不过载、系统不出现解列失电等。代码里用潮流是否收敛、电压是否越限、VSC容量是否足够来判断方案是否可行。这里有个处理细节:不可行方案不直接淘汰,而是在目标函数里给一个大的惩罚值。这样做是为了保持种群多样性,让算法自己慢慢排除坏的探索方向,而不是过早把所有不可行解一刀切掉,否则容易陷入局部最优。
4. Python核心代码实现
4.1 配电网拓扑数据结构怎么建
我建议用NetworkX管理拓扑,用dataclass管理设备参数。NetworkX的好处是连通性分析、路径查找、孤岛识别这些图算法都是现成的,省了重复造轮子的时间。节点和线路的定义大致是这样:
from dataclasses import dataclass import networkx as nx @dataclass class NodeData: name: str bus_type: str # 'pcc' 电源节点, 'load' 负荷节点, 'ac','dc' 母线 active_power: float = 0.0 voltage_base: float = 10.0 @dataclass class LineData: name: str node_from: str node_to: str line_type: str # 'ac' 或 'dc' length_km: float r: float = 0.0 x: float = 0.0 capacity_mva: float = 0.0 is_candidate: bool = False failure_rate: float = 0.03 repair_time: float = 4.0创建图就是把节点和边塞进NetworkX的Graph对象:
def build_graph(nodes, lines): G = nx.Graph() for n in nodes: G.add_node(n.name, **n.__dict__) for l in lines: G.add_edge(l.node_from, l.node_to, **l.__dict__) return G图建好之后,判断孤岛和转供就是调用networkx连通分量的问题,性能也不错。规划候选线路和VSC要单独标记出来,解码个体时只在这些候选位置上做改动,不要动固定线路。
4.2 FMEA可靠性评估函数的关键实现
可靠性评估函数输入一个网络图和所有设备参数,输出SAIFI、SAIDI、EENS。核心逻辑是遍历元件列表,对每个元件模拟故障。下面给出一个简化但逻辑完整的版本,其中has_pcc和can_transfer是两个辅助函数,前者判断孤岛内是否有电源节点,后者判断故障元件所在供电区域是否有备用转供路径,实际项目里要根据你的网络结构补全这两个函数:
def fmea_assess(G, lines, nodes, user_counts, switch_time=1.0): total_user = sum(user_counts.values()) saifi = saidi = eens = 0.0 for line in lines: lam = line.failure_rate # 次/年 repair_time = line.repair_time # 小时 G.remove_edge(line.node_from, line.node_to) islands = list(nx.connected_components(G)) affected_power = 0.0 affected_user = 0.0 outage_time = repair_time # 默认停电时间等于修复时间 for comp in islands: if not has_pcc(comp, nodes): for n in comp: if nodes[n].bus_type == 'load': affected_power += nodes[n].active_power affected_user += user_counts.get(n, 0) # 转供成功时,停电时间按开关切换时间计算 if can_transfer(G, line): outage_time = switch_time saifi += lam * affected_user / total_user saidi += lam * outage_time * affected_user / total_user eens += lam * outage_time * affected_power G.add_edge(line.node_from, line.node_to) return {'SAIFI': saifi, 'SAIDI': saidi, 'EENS': eens}这段代码的工程量不算大,但要注意几个细节:遍历完元件要恢复边;每个孤岛单独判断;user_counts要对上节点名。SAIFI只与频率有关,所以公式里没有repair_time,而SAIDI和EENS都要乘时间,这些口径错了,指标结果会对不上。
4.3 经济性目标函数与惩罚处理
经济目标函数是在可靠性评估结果之上叠加投资和运行成本。核心逻辑是先解码个体得到网络方案,然后分别计算等年值投资、年网损费用和运维费用:
def economic_cost(individual, prices): G = decode_individual(individual) inv_cost_yearly = annualized_investment(individual, prices) loss_cost = annual_energy_loss(G, prices['elec_price']) om_cost = inv_cost_yearly * prices['om_rate'] f1 = inv_cost_yearly + loss_cost + om_cost f1 += penalty(G) # 不可行方案加惩罚 return f1这里有个设计选择值得说:如果想把缺电成本并入经济目标,就在f1里加上EENS×unit_outage_cost;如果保持双目标形态,就不加,让EENS作为第二目标返回。代码里留了一个开关,两种思路可以随时切换。个人建议默认走真正的双目标,因为单位缺电成本这个参数主观性太强,先不掺进目标函数,让算法自己跑出权衡关系,后面怎么折算都好说。
4.4 NSGA-II双目标求解流程
优化部分用DEAP框架。先注册两个最小化目标,再定义个体。个体编码采用整数列表,长度等于候选线路数加候选VSC数,候选线路位置用0和1表示交流或直流,候选VSC位置存接入容量等级,0表示不装,1、2表示不同容量等级。这样编码天然离散,符合线路选型的实际约束。
from deap import base, creator, tools creator.create("FitnessMin", base.Fitness, weights=(-1.0, -1.0)) creator.create("Individual", list, fitness=creator.FitnessMin) def evaluate(individual): G = decode_individual(individual) f1 = economic_cost(individual, prices) f2 = fmea_assess(G, lines, nodes, user_counts)['EENS'] return f1, f2 toolbox.register("evaluate", evaluate) toolbox.register("mate", tools.cxTwoPoint) toolbox.register("select", tools.selNSGA2)关键在于变异函数。线路选型是离散逻辑,不能用高斯变异,要用翻转逻辑,以一定概率把某个位置的编码翻到另一个值:
import random def mutate_individual(individual, indpb): for i in range(len(individual)): if random.random() < indpb: individual[i] = 1 - individual[i] return individual,种群规模建议100,迭代200代以上。NSGA-II跑完之后输出非支配解集。如果帕累托前沿太稀疏,优先调大种群规模和变异率,而不是急着改算法结构。
5. 算例测试与结果分析
5.1 算例系统与参数设置
测试算例用的是改造后的33节点系统。原始33节点系统是12.66kV辐射状配电网,总负荷大约3.7MW。我在其中选了5条联络馈线作为候选直流化线路,3个节点作为VSC候选接入点。可靠性参数按第二章的默认量级设置,单位电价0.5元/kWh,贴现率6%,线路寿命30年,VSC寿命20年。
需要说明,算例的主要目的是验证代码逻辑和跑通流程,数值大小本身不是重点,要迁移到实际系统时,把数据模块里的节点负荷、线路长度、故障率、造价全部换成真实数据即可。
5.2 帕累托前沿与方案权衡
跑完NSGA-II,把所有非支配解画在同一张图上,横轴是年综合费用,纵轴是EENS。前沿上的解呈现很明显的权衡关系:随着投资增加,EENS下降,但下降幅度逐渐变缓。这说明增量投资的边际可靠性收益是递减的——直流化程度越高,换流器和直流馈线带来的冗余度越强,可靠性收益却越不明显。
表5-1给了三个典型解,分别对应经济优先、折中和可靠优先:
| 方案 | 直流馈线条数 | VSC容量 | 年综合费用(万元) | EENS(MWh/年) | SAIFI(次/户·年) | SAIDI(小时/户·年) |
|---|---|---|---|---|---|---|
| 方案A(经济优先) | 1 | 0.5MVA | 92.1 | 16.8 | 0.89 | 4.9 |
| 方案B(折中) | 3 | 1.2MVA | 105.6 | 11.2 | 0.63 | 3.8 |
| 方案C(可靠优先) | 5 | 2.8MVA | 126.4 | 7.5 | 0.44 | 3.1 |
趋势很清楚:直流化程度越高,转供能力和电源灵活性越强,EENS显著下降,但年综合费用也在上升。选择哪个方案取决于工程实际对停电损失的容忍度。这个项目最后选了方案B,因为从方案B到方案C多花约20万,EENS只降低3.7MWh,性价比已经不高了。
5.3 可靠性指标校验与合理性检查
评估结果出来后不能直接信,要做合理性检查。对照故障率量级,SAIFI和SAIDI是否在合理范围,比如方案B的SAIFI是0.63次/户·年,考虑到33节点系统本身设置有联络线和自动化开关,这个数值是合理的。如果算出来SAIDI超过十几小时,就要回头查转供逻辑是不是判断错了。
另一个重要检查是单重故障扫描。把每个元件故障时的失负荷功率排序,找最薄弱环节,通常落在没有联络线、线路特别长的末端馈线附近。这份薄弱环节清单可以直接交给规划人员做投资优先级参考,比单纯看总指标直观得多。
6. 调试经验与性能优化
6.1 高频报错与排查
实际操作中遇到的问题主要集中在可靠性逻辑、潮流收敛和算法参数三块。把高频问题和对应处理方案整理成表6-1,读者照着排查效率会高很多:
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| 潮流不收敛 | VSC参数设置不合理或网络不连通 | 检查解码后的网络是否含孤岛,把不可行惩罚调大 |
| SAIFI/SAIDI为零 | 故障枚举列表没覆盖全部线路 | 检查lines列表是否包含所有候选线路 |
| EENS异常大 | 转供判断失效,负荷被误判为全停 | 在FMEA里逐条打印故障事件的失负荷功率,核对每个孤岛 |
| 帕累托前沿太稀疏 | 种群太小或变异率太低 | 种群增大到150~200,变异率提高到0.4~0.8 |
| 程序运行太慢 | FMEA重复计算全图连通性 | 先做快速可行性判断,再做完整评估,见6.2节 |
6.2 性能优化经验
FMEA评估是性能瓶颈。单次FMEA要对每个元件做连通性分析,如果种群里有100个个体,每个个体都要评估,200代跑下来就是两万次评估,累计耗时相当可观。我的优化手段按效果排序如下:
- 先做快速可行性判断。检查网络是否辐射状、是否连通、VSC容量是否满足,明显不可行的个体不进完整FMEA,直接返回惩罚目标值。
- 转供判断里只对含负荷且不含PCC的孤岛做详细分析,纯输电通道孤岛直接跳过。
- 统计用户数和失负荷功率时尽量用numpy向量化。
- 多进程并行。用multiprocessing池把种群个体分到多核评估,8核机器上实测能省超过一半时间。
另一个经验是:规划阶段不要一上来就上时序蒙特卡洛。解析法速度快、稳定、毛病少,足够支撑上万次评估。等方案基本敲定之后,再对选出的少数几个方案用序贯蒙特卡洛做精细验证,这样整体时间成本低很多。
6.3 代码的扩展方向
这套框架的扩展性不错,后续加东西不需要推翻重来。可以考虑的方向包括:加储能设备和分布式光伏后,可靠性评估要考虑孤岛运行和黑启动能力,FMEA的场景复杂度会增加;把8760小时负荷曲线加进经济性评估,网损和停电损失按小时细分,精度更高;加入更精细的VSC损耗模型,换流器在不同负载率下损耗不是线性的;约束里加入碳排放目标,就变成三目标规划。
这些扩展的改动基本都发生在评估模块内部,优化框架和数据结构不用动。
最后分享一个个人感受。这个项目真正有价值的地方,不是某个算法多高级,而是把规划、评估、优化三个环节串成了一个闭环——每个候选方案都能同时给出经济账和可靠性账。实际项目里用这套框架做过几轮方案比选,评估结果比较可靠,业主在投资决策上少了很多扯皮。
给后来者的建议很简单:拿到代码先不急着加功能,先把33节点算例跑通,把每个指标和预期值对照一遍,再往自己的系统上套。只要数据格式保持一致,主要改动集中在reliability_param和prices两个字典里。这个习惯能省下大量本来会花在排查“到底是模型问题还是代码问题”上面的时间。