多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

Python实现混合配电系统双目标规划:经济性与可靠性协同优化

Python实现混合配电系统双目标规划:经济性与可靠性协同优化 1. 项目概述做电力系统规划研究的朋友对“混合配电系统”这个词应该不陌生。所谓混合配电系统简单来说就是传统配电网与分布式电源Distributed Generation, DG、储能系统Energy Storage System, ESS等新型元素融合后的电网形态。这些年分布式光伏、小型风电场、储能电站大规模接入配电网侧纯粹的“被动配电网”正在向“主动配电网”转型随之而来的规划问题也变得越来越复杂。这套Python代码实现的核心任务是在配电系统规划阶段同时兼顾两个目标一个是经济性另一个是可靠性。经济性容易理解——投资成本、运行维护成本、网损费用都要算进去每一项都是真金白银可靠性则关系到用户侧持续供电的能力一个规划方案如果经济指标很漂亮但一到台风天就大面积停电这样的方案显然不具备工程可行性。双目标优化最大的难点在于两个目标往往互相打架。想提高可靠性通常意味着要增加投资比如多装几台变压器、多铺设几回线路、配置冗余的储能容量反过来想省钱就得在设备选型、网架结构上做减法牺牲的往往是供电可靠性。这类问题不存在一个“绝对最优解”而是一个权衡后的解集——学术上叫Pareto前沿帕累托前沿工程上叫多方案比选。我选择用Python来做这项工作主要看中三点第一Python有非常成熟的多目标优化库比如pymoo、DEAP内置了NSGA-II等经典算法不用从头写轮子第二配电网可靠性评估的核心是蒙特卡洛模拟或解析法这两者在Python里都可以用较少的代码实现得很高效第三Python生态下的数据处理和可视化工具完善规划结果可以直接用matplotlib、pandas整理输出汇报和论文插图都省事。这套代码面向的读者比较明确电力系统专业的研究生、从事配电网规划设计的工程师以及对分布式电源接入分析有需求的技术人员。如果你已经了解基本的配电网潮流计算又想在规划层面深入研究经济性与可靠性的协同优化这份代码会是一个很不错的起点。2. 系统建模与双目标数学描述2.1 经济性目标函数怎么建经济性目标考察的是全生命周期成本Life Cycle Cost, LCC也就是从规划年开始到设备退役为止整个系统在建设、运行、维护上要花的钱总和。在配电系统规划中通常把规划周期取为20年然后折算成等年值来比较这样处理可以避免通货膨胀和时间价值带来的干扰。经济性目标函数的核心构成可以拆成四块设备投资等年值、运行维护成本、网损费用、停电损失费用这个后面会讲它同时也是连接可靠性的桥梁。设备投资等年值的计算是第一个关键点。投资发生在规划期初但设备使用是贯穿整个规划周期的所以需要把总投资通过资金回收系数Capital Recovery Factor, CRF折算成等年值。比如某台变压器的成本是100万元使用寿命20年基准折现率取8%那么[ CRF \frac{r(1r)^n}{(1r)^n-1} \frac{0.08 \times 1.08^{20}}{1.08^{20}-1} \approx 0.1019 ]折算后的等年值就是100 × 0.1019 10.19万元/年。这个系数在代码里可以直接用numpy的金融函数计算不用自己写公式但原理要清楚不然报告里写不清楚折现率的来龙去脉。运行维护成本相对简单通常按投资额的一定比例估算变压器取2%~3%线路取1%~2%DG设备取3%~5%。这部分在代码里做成参数表方便调整。网损费用则需要通过潮流计算获得。规划方案确定了网络拓扑和DG接入位置后典型的日负荷曲线和DG出力曲线分别取若干个典型场景对每个场景做潮流计算得到总有功网损再乘以电价和年利用小时数累加就是全年的网损费用。2.2 可靠性评估非序贯蒙特卡洛模拟可靠性评估是这套代码另一个核心模块。配电网可靠性评估的常用方法分两大类解析法和模拟法。解析法基于故障枚举思路清晰、计算快但面对分布式电源接入后的复杂网络尤其是考虑孤岛运行、转供策略时解析法的状态空间会爆炸式增长建模复杂度很高。模拟法特别是非序贯蒙特卡洛模拟处理复杂系统更加灵活原理上也更容易理解。非序贯蒙特卡洛模拟的核心逻辑非常直白把系统里所有可能失效的元件线路、变压器、断路器的故障概率作为随机变量反复抽样生成系统状态然后逐一判断该状态下的失负荷情况。循环很多次之后统计平均就能得到系统的可靠性指标。具体到配电系统可靠性评估的几个常用指标包括指标全称含义SAIFI系统平均停电频率指标每个用户每年平均停电次数SAIDI系统平均停电持续时间指标每个用户每年平均停电小时数ENS系统缺供电量一年内系统总的停电损失电量AENS平均缺供电量ENS除以用户总数在混合配电系统中DG的接入会显著改善可靠性指标。一个重要原因是孤岛运行机制当主网发生故障导致某区域与变电站断开后如果该区域内接入的DG容量足够就可以形成孤岛继续供电避免用户长时间停电。规划代码里对每个支路故障场景都要判断断开故障支路后下游区域是否有DG支撑孤岛内的DG出力与负荷是否平衡这里有个容易踩坑的细节DG出力是波动的。光伏在夜间出力为0风电出力也高度依赖风速。所以孤岛能否持续供电不能只看额定容量要看故障发生时刻DG的实际出力水平。非序贯蒙特卡洛模拟在这一点上并不严谨因为它的抽样结果不包含时间维度信息。严格的做法是采用时序蒙特卡洛模拟把风速、光照、负荷的时序曲线都纳入抽样过程但计算量会大幅上升。我的代码里做了折中处理对DG出力和负荷各取若干典型时段比如分季节、分时段聚类成12个典型场景在每个场景下做非序贯抽样最后按场景时长加权汇总。这样做计算效率高结果也足够贴近工程实际。2.3 双目标如何统一到同一个框架双目标规划的理想输出不是单一方案而是一组Pareto最优方案。在代码实现层面需要把上述两个目标封装成两个独立的函数目标1系统总费用包括投资、运维、网损、停电损失。其中停电损失就是把ENS乘以单位停电损失费用得到这个做法把可靠性指标量化为经济指标是“经济-可靠性”双目标能放进同一个优化框架的关键。目标2系统可靠性指标我这里用的是ENS缺供电量或SAIDI。因为这两个指标的经济意义都很直观也方便决策者理解。需要说明的是不一定非得把停电损失折成钱才算经济性目标。很多时候研究者会把“综合费用最小”和“ENS最小”作为两个独立目标直接进行双目标优化这样的好处是Pareto前沿能直接展示“多花多少钱能换来多少可靠性的提升”决策者可以按预算从方案库里挑。我的代码默认采用这种方式NSGA-II的种群个体同时计算总费用和ENS两个目标不互相加权。3. Python代码体系结构与核心模块解析3.1 整体架构怎么组织代码才不乱我建议把代码组织成四个核心模块职责分离、接口清晰。很多初学者喜欢把所有逻辑写在一个脚本里前200行定义参数中300行算潮流后100行画图文件到了3000行之后自己都找不到变量定义在哪。规划项目远比单次计算复杂模块化是必须的。整个项目的推荐结构如下distribution_planning/ ├── data/ # 输入数据目录 │ ├── bus_data.csv # 节点数据 │ ├── branch_data.csv # 支路数据 │ ├── load_profile.csv # 负荷时序数据 │ └── dg_profile.csv # DG出力时序数据 ├── core/ # 核心计算模块 │ ├── power_flow.py # 潮流计算 │ ├── reliability.py # 可靠性评估 │ ├── cost_model.py # 经济性评估 │ └── optimization.py # NSGA-II优化主流程 ├── utils/ # 工具函数 │ ├── data_loader.py # 数据读取与校验 │ └── visualizer.py # 结果可视化 └── main.py # 主入口脚本模块划分的思路很明确数据层与计算层分开计算层内部再把潮流、可靠性、经济性、优化四件事分别封装互不干扰。这样任何一个模块想替换算法比如把NSGA-II换成SPEA2都不用动其他模块的代码。3.2 潮流计算模块配电网前推回代法配电网潮流计算和输电网不一样输电网用的是牛顿-拉夫逊法配电网因为辐射状结构、支路电阻比较大的特点最常用的是前推回代法Backward/Forward Sweep。这个方法实现简单、内存占用小、收敛性对配电网非常好迭代速度很快。前推回代法的思路可以这么理解先假设所有节点电压都是额定值比如1.0 p.u.从末端节点向首端逐段回推计算每条支路的电流和功率分布然后再从首端向末端逐段前推用已知的首端电压和各支路功率损耗推算各节点电压。交替进行直到两次迭代之间的电压差满足精度要求一般取10⁻⁴ p.u.。核心代码片段大致长这样def backward_forward_sweep(bus_data, branch_data, max_iter100, tol1e-4): 前推回代法潮流计算 bus_data: 节点数据负荷、DG出力等 branch_data: 支路数据阻抗、连接关系、拓扑结构 返回各节点电压幅值与相角 # 初始化所有节点电压设为额定电压 v_mag np.ones(len(bus_data)) v_ang np.zeros(len(bus_data)) for _ in range(max_iter): v_mag_prev v_mag.copy() # 回代从末端向首端计算支路电流 branch_current backward_sweep(v_mag, bus_data, branch_data) # 前推从首端向末端更新节点电压 v_mag, v_ang forward_sweep(v_mag, v_ang, branch_current, bus_data, branch_data) if np.max(np.abs(v_mag - v_prev)) tol: break return v_mag, v_ang这里的backward_sweep和forward_sweep是核心函数。回代时需要注意DG节点的处理DG并网节点在潮流计算中不能简单当作负荷节点处理而应视为负的负荷PQ节点来处理出力为负值。如果采用恒电压控制模式PV节点计算复杂一些但多数规划阶段的DG建模取PQ模式就够了。3.3 可靠性评估模块故障枚举与蒙特卡洛结合可靠性评估模块是整个代码中最需要认真设计的地方。我的实现思路是两层循环外层枚举所有可能发生故障的元件每条支路、每台变压器对每个故障事件计算其发生的概率由元件的历史故障率决定单位是次/年内层则是判断该故障事件发生后系统是否失负荷、失负荷多少。每个故障场景下的失负荷评估流程如下断开故障元件判断系统是否解列。对于辐射状配电网断开一条支路通常会把下游节点从主网中隔离形成失电区。判断失电区是否存在DG。如果没有DG失电区的所有负荷全部丢失失负荷量就是该区域总负荷。如果有DG进一步判断DG容量与失电区总负荷的关系。如果DG总出力大于负荷理论上可以维持孤岛稳定运行失负荷量为零但要考虑DG自身故障率现实中孤岛运行也有失败概率如果DG出力小于负荷则缺多少损失多少。记录本次故障的失负荷量、受影响用户数、停电时长等于故障修复时间。为什么要用蒙特卡洛因为DG出力和负荷大小都是不确定量。非序贯蒙特卡洛模拟正是通过对这些不确定量的大量抽样逐个场景计算上述流程最后对结果求期望。代码中的抽样次数取多少合适也是个问题——我的经验是3000~5000次抽样就能稳定收敛再多对精度提升有限反而白白增加计算时间。3.4 经济性评估模块全生命周期成本核算经济性模块相对简单核心是把投资、运行、维护费用统一折算成等年值。在优化算法迭代过程中这个模块会被频繁调用所以要注意计算效率避免在循环内部重复计算不变的常量比如CRF系数只需算一次。代码函数签名大致这样def evaluate_cost(dg_plan, ess_plan, network, load_data, dg_data, params): 计算规划方案的全生命周期等年值费用 dg_plan: DG安装位置与容量方案 ess_plan: 储能安装位置与容量方案 network: 网络拓扑数据 load_data: 负荷数据 dg_data: DG出力数据 params: 各类经济参数投资单价、运维比例、电价等 annual_cost 0.0 # 1. 设备投资等年值 annual_cost dev_invest_equivalence(dg_plan, ess_plan, params) # 2. 运行维护成本 annual_cost o_and_m_cost(dg_plan, ess_plan, params) # 3. 网损费用 annual_cost loss_cost(network, load_data, dg_plan, params) # 4. 停电损失 annual_cost outage_cost(network, load_data, dg_plan, params) return annual_cost值得注意的一点是经济性评估要与潮流计算和可靠性评估联动。比如发电流、网损费用都要基于规划方案接入DG后的网络运行状态来计算停电损失更是直接依赖可靠性评估结果。代码里我直接用函数调用嵌套来体现这种依赖关系。3.5 优化算法用NSGA-II搜索Pareto前沿双目标优化最经典的选择是NSGA-II非支配排序遗传算法第二代。它从2002年提出至今仍然广泛应用开源的pymoo库直接支持用起来很方便。NSGA-II的核心机制有三板斧非支配排序、拥挤度距离、精英保留策略。非支配排序就是把种群中所有个体按Pareto支配关系划分成多个前沿层级层级越靠前的个体越优秀拥挤度距离用来衡量同一层级内个体的分布密度优先保留分布稀松的个体避免算法早熟收敛到一个局部区域精英保留策略则是在生成下一代种群时同时从父代和子代中选优保证优秀个体不会在进化过程中丢失。我在代码里的实现思路from pymoo.algorithms.moo.nsga2 import NSGA2 from pymoo.core.problem import Problem from pymoo.operators.crossover.sbx import SBX from pymoo.operators.mutation.pm import PM from pymoo.operators.sampling.lhs import LHS class DistributionPlanningProblem(Problem): def __init__(self): # 决策变量维度取决于候选DG接入节点数和候选容量档位数 super().__init__(n_varnum_candidates, n_obj2, xl0, xunum_capacity_levels-1) # 决策变量编码每个候选节点选择哪一档DG容量 def _evaluate(self, x, out): # 对种群中每个个体 # 解码DG配置方案 - 调潮流计算 - 调可靠性评估 # 计算经济目标与ENS目标 out[F] np.column_stack([cost_values, ens_values])决策变量的编码设计直接决定了搜索空间的大小。我采用的是离散编码方式每个候选节点对应一个整数变量取值范围是0到N-10表示不安装DG1~N-1对应N-1个可选容量档位。这种编码方式让搜索空间大幅缩小比直接用连续变量安装容量kW更适合工程实际因为容量档位通常也就是几个规格的产品系列。种群规模设置多大合适我测试下来节点数在30~100的中等规模配电网种群规模取50~80、进化代数取100~200就能得到比较稳定的Pareto前沿。如果发现前沿形状很散优先检查是否种群太小导致搜索不充分其次再考虑增加代数。4. 算例测试与结果分析4.1 测试系统IEEE 33节点配电网我的测试算例选用了经典的IEEE 33节点配电系统。这个系统有33个节点、32条支路基准电压12.66 kV总负荷约3.7 MW2.3 Mvar结构上是典型的辐射状网络。选择这个系统的原因是它是配电网研究的标准算例所有参数都能在公开文献里查到结果便于复现和对比。测试中设定候选DG接入节点为5个候选容量档位为4档0、300kW、600kW、900kWDG类型设定为光伏。光伏出力曲线按典型日光照数据生成负荷曲线取冬夏两季的典型日曲线。经济参数方面光伏单位投资成本设为4000元/kW运维成本按投资的3%计电价取0.6元/kWh单位停电损失取20元/kWh折现率8%规划周期20年。4.2 优化结果与Pareto前沿解读用NSGA-II跑完规划程序后得到的一组Pareto最优解分布在一个双目标平面上。横轴是年综合费用纵轴是系统ENS缺供电量。从结果可以看出两个明显趋势一是费用低端和ENS低端不可兼得。Pareto前沿左端点对应的方案总费用最低但ENS最高右端点ENS最小但费用也最高。中间则是各种不同权衡程度的折中方案。二是DG总容量与ENS的下降呈显著的边际递减效应。DG容量从0增加到1500kW时ENS下降非常明显但继续增加到3000kW、4500kW时ENS的下降幅度越来越小。这说明光伏接入的可靠性红利在达到一定渗透率后就饱和了后续投资更多是为了追平负荷增长或参与其他辅助服务。具体到某个折中方案可以这样解读方案A总费用207万元/年ENS 420MWh/年与方案B总费用226万元/年ENS 280MWh/年相比每年多投入约19万元换来的是140MWh的停电电量削减。如果单位停电损失按20元/kWh算相当于每年减少停电损失约280万元远超额外投入的19万元——这样算下来方案B的经济账更划算。这也是双目标规划的决策价值所在不是简单找一个“最优解”而是把各方案的性价比摆在桌面上让决策者选。4.3 结果可视化建议规划结果的可视化很重要因为汇报对象可能不懂算法细节但一眼能看懂趋势图。我强烈建议输出以下四张图第一张是Pareto前沿散点图横轴总费用、纵轴ENS每个点标注对应的DG配置方案第二张是优化后的网络拓扑图突出标出DG接入位置和容量大小第三张是各方案下节点电压分布对比图直观展示DG接入对电压质量的改善第四张是ENS构成饼图区分主网故障、支路故障、DG不可用分别贡献了多少缺电损失。代码里用matplotlib绘制Pareto前沿的片段import matplotlib.pyplot as plt plt.figure(figsize(8, 6)) plt.scatter(cost_values, ens_values, csteelblue, alpha0.7, edgecolorsk) plt.xlabel(Annual Total Cost (10k CNY)) plt.ylabel(ENS (MWh/year)) plt.title(Pareto Front of DG Planning) plt.grid(True, linestyle--, alpha0.6) plt.savefig(pareto_front.png, dpi300, bbox_inchestight)5. 代码运行与工程化落地的经验总结5.1 运行环境的配置建议跑这套代码对硬件要求不高但软件环境有几个坑值得注意。Python版本建议3.9或3.10太新的版本3.12个别第三方库可能还没跟上。核心依赖库是numpy、pandas、pymoo、matplotlib、networkx这几个都建议用pip安装pip install numpy pandas pymoo matplotlib networkx如果是要跑大规模可靠性仿真节点数超过100、蒙特卡洛抽样次数破万建议装numba做JIT加速。用numba给蒙特卡洛循环加个装饰器性能提升常常是几十倍级别的实测下来非常惊喜。5.2 实操中遇到的典型问题与排查技巧我在开发和调试这套代码的过程中积累了不少排查经验这里挑几个高频问题分享。问题一潮流计算不收敛怎么办不收敛最常见的原因是DG容量设置过大导致局部节点电压越限回代过程发散。排查思路是先去掉所有DG看原始网络是否收敛确认网络本身没问题后再逐个接入DG定位是哪个节点出了问题。另外前推回代法对PV节点处理比较繁琐如果代码里把光伏以PV节点建模要检查无功越限处理逻辑是否正确。问题二NSGA-II种群进化几百代还是找不到好解这种情况十有八九是决策变量的编码范围设计不合理。比如容量档位上限设得过高导致搜索空间里大量解是“装了太多DG”经济性差得离谱算法一直在无效区域搜索。对策是先跑几个随机方案摸清合理的DG容量范围再把编码上限收紧到有效区间。问题三蒙特卡洛模拟的结果方差太大、每次运行指标波动大这是抽样次数不足的典型症状。先把抽样次数提高到1万次看结果是否稳定如果还存在明显波动那么检查一下随机数种子是否固定。代码里应该在主入口固定随机种子否则每次运行结果不一致对比实验没法做。5.3 针对不同应用场景的参数调整建议这套代码框架并不局限于配电网DG规划做以下几种扩展也是顺理成章的储能规划在决策变量里增加储能安装容量和位置目标函数中增加储能设备的投资与运维费用可靠性评估中加入储能的充放电策略故障前充电、孤岛时放电。注意储能荷电状态的时间耦合特性非序贯模拟处理起来比较困难建议对孤岛时段做简化假设取故障时段SOC平均值。网架扩展规划决策变量增加新建线路的位置和型号选择。经济性目标加入线路投资费用可靠性建模要考虑新建线路对网络拓扑的影响。这种场景下网络结构会变化可靠性评估的故障枚举逻辑要做相应修改。主动配电网规划在目标函数中加入分布式电源弃电惩罚、需求响应成本还能考虑电压调节设备有载调压变压器、静止无功补偿器的配置。每加一类设备决策变量维度和经济性评估模型都要对应扩展。我个人在实际操作中最大的体会是这个项目里最花时间的环节不在算法本身而在数据准备和结果解读上。IEEE 33节点算例的数据在网上能找到但负荷数据的时序曲线往往需要自己根据典型负荷特性生成DG出力的随机性建模更是需要仔细校核。建议大家在跑通代码后务必用几组人工构造的极端场景比如DG全停运、负荷尖峰光伏出力低谷来验证可靠性评估逻辑是否正确这一步做好了后面无论做什么变体研究都心里有底。
返回列表