1. 项目整体设计与模型构建
混合配电系统规划,说白了就是在一个既要交流负荷、又要带直流负荷的配电网里,回答三个问题:在哪里装设备、装多大容量、怎么接线最划算。这三个问题背后其实牵着一个更深的需求——电网公司不能只顾省钱,还要保证不停电,这两个目标放在一起就变成了非线性的多目标优化问题。这个项目用Python把整套流程跑通,从网络结构、设备模型、可靠性指标到优化算法全部编程实现,我拿到这个课题第一反应就是:真正的难点不在某个单点算法上,而是把这几个模块串起来时的接口设计和计算效率。
1.1 混合配电系统的“混合”到底指什么
传统配电网是纯交流的,从10kV母线下来,一路变压器到380V,所有负荷都挂在交流支路上。但随着光伏直流发电、储能电池、电动车充电桩这些直流设备大规模接入,交流—直流—交流来回变换导致效率下降,于是出现了交直流混合配电系统——既保留交流馈线,又新增直流母线,换流器(VSC)和DC/DC变换器作为接口设备连接两个区域。
这个项目里的“混合配电系统规划”,规划对象就是这些接口设备的选址和定容,以及网架拓扑的调整方案。规划变量包括:换流器装在哪条馈线上、容量多大;直流母线覆盖哪些负荷;储能和光伏的接入位置;是否需要新增联络线。约束条件则包含节点电压偏差、线路载流量、变压器容量、可靠性指标上限等。
我在实际建模中认为,最需要想清楚的是设备替换成本。比如一台VSC换流器,它的成本不只是设备购置费,还包括安装调试、占地、维护、损耗,以及寿命周期内的故障维修费用。不考虑这些,规划结果在工程上基本没法落地。
1.2 经济性目标函数:从账单角度拆解规划成本
经济性目标函数通常写成年度综合费用最小化,包含四大部分。
第一部分是投资等年值。设备投资不是一次性投入当年算完,而是要折算到每年。公式是:
[ C_{inv} = \sum_{i \in \Omega_{VSC}} c_{VSC} \cdot S_{VSC,i} \cdot \frac{r(1+r)^n}{(1+r)^n - 1} ]
其中 ( r ) 是折现率,一般取0.08,( n ) 是设备寿命,VSC取20年,储能取10年。这个等年值系数就是把一次性投资变成每年的还款额,类似房贷等额本息的概念。
第二部分是运行维护成本,通常按投资额的一定比例估算,取2%~3%。第三部分是网损成本,这是最考验计算精度的部分,需要先做潮流计算求出各支路损耗,再乘以电价和年利用小时数。第四部分是购电成本,从上级电网买电的费用。
很多初学的人会把投资成本和网损成本直接相加,忽略了等年值折现,这样会导致规划结果偏向选择大容量设备,因为大设备能显著降低网损,但一次性投资被低估了。这个细节我建议在建模时严查,否则结果偏差很大。
1.3 可靠性目标:用停电损失和缺供电量说话
可靠性目标我推荐用缺供电量期望值(EENS,Expected Energy Not Supplied)来量化,单位是MWh/年。它衡量的是系统在一年内因为故障、检修、容量不足等原因导致用户少用的电量。EENS越小,可靠性越高。
计算EENS的工程思路是:模拟系统中各个设备的状态(正常运行或故障停运),对每个状态做潮流计算,找出不能正常供电的负荷量,乘以其持续时间和概率,累加得到期望值。
除了EENS,还常用的指标包括系统平均停电频率指标(SAIFI)和系统平均停电持续时间指标(SAIDI),这两个指标在可靠性评估领域用得非常多,但在规划优化里作为约束条件更合适,目标函数一般还是用EENS或者停电损失费用。
做双目标优化的关键点在于:经济性和可靠性是互相冲突的。要提升可靠性,就得增加设备冗余、加大导线截面、多装联络开关,投资成本上升;反过来只省钱,可靠性指标就会恶化。于是问题变成了在Pareto前沿上寻找一组非劣解,而不是找一个唯一最优解,这正是为什么选择多目标进化算法来处理。
2. 求解框架:为什么用NSGA-II而不是简单加权
面对双目标问题,新手最容易想到的就是加权求和,把两个目标变成单目标。但这个方法有一个致命问题:权重系数非常难定,而且不同量纲之间需要归一化,归一化因子选不好,优化方向就会被某个目标主导。实际跑下来你会发现,权重法一次只能得到一个解,要得到Pareto前沿需要反复调整权重跑几十次,效率极低。
2.1 三种多目标处理方式的对比
权重法、约束法和Pareto支配法是目前最常见的三种思路。
权重法实现最简单,但结果对权重极其敏感,而且无法处理非凸的Pareto前沿。约束法是把一个目标转为约束,比如设定EENS不超过某个值,然后优化经济性,这样也能得到一系列解,但需要合理设置约束边界,边界的取值会影响解的分布。
Pareto支配法则是通过“支配”关系来比较解的好坏:一个解A支配解B,当且仅当A在所有目标上都不比B差,且至少在一个目标上严格优于B。所有不被其他解支配的解构成第一层Pareto前沿。这种方法不需要人为定权重,一次运行能生成整个前沿面,所以我选了NSGA-II来实现。
2.2 NSGA-II的三大核心机制
NSGA-II是带精英策略的非支配排序遗传算法,它的成功靠三个机制。
第一是快速非支配排序。对种群中的每个个体,计算它被多少个其他个体支配(n_p)以及它支配哪些个体(S_p),然后分层归类。时间复杂度是 ( O(MN^2) ),M是目标数量,N是种群规模。
第二是拥挤度距离。同一Pareto层内的解之间需要保持多样性,否则所有解都挤在一个区域。拥挤度距离的计算方法是:对每个目标排序,计算相邻两个解之间的目标差值,归一化后累加。距离越大,代表这个解周围越空旷,越值得保留。
第三是精英保留策略。将父代种群和子代种群合并,从中选出前N个最优个体作为下一代。这样可以防止优秀解在交叉变异过程中丢失。
我用Python实现时,数据结构上用了dataclass来定义个体,每个个体包含决策变量数组、两个目标函数值、支配层级和拥挤度距离。代码结构清晰,后面扩展第三目标也非常方便。
2.3 编码方案与约束处理技巧
决策变量怎么编码,直接决定算法的搜索效率。我把规划变量分成两类:离散型和连续型。换流器安装位置用整数编码,索引对应节点编号;换流器容量用连续编码,范围根据负荷总量设定;储能容量也是连续变量。
约束处理上,我采用了罚函数法加可行性优先策略。具体做法是:电压越限和潮流不收敛的解,赋予极大的目标值,使其在非支配排序中必然处于较差层级。但要注意罚函数值不能设成无穷大,否则会影响拥挤度计算时的归一化。
一个非常实用的经验是:将决策变量的取值范围在种群初始化时对齐物理约束。比如换流器容量范围根据所在馈线的峰值负荷来动态调整,而不是整个系统统一范围。这样能大幅减少无效搜索过程,收敛速度明显提升。
3. 可靠性评估的工程化实现
3.1 序贯蒙特卡洛法:模拟一整年的系统运行
可靠性评估精度直接决定优化结果的可靠性,而仿真方法的选取影响计算时间,这中间需要平衡。
我采用的是序贯蒙特卡洛法(Sequential Monte Carlo Simulation),它的核心思想是:对每个可修复元件建立“正常运行—故障停运—修复—再运行”的两状态模型,用随机抽样确定每个状态持续时间,然后按时间顺序推进,模拟系统一年的运行状态。
元件状态持续时间抽样公式是:
[ T_{up} = -\frac{1}{\lambda} \ln U_1, \quad T_{down} = -\frac{1}{\mu} \ln U_2 ]
其中 ( \lambda ) 是故障率,( \mu ) 是修复率,( U_1 )、( U_2 ) 是 [0,1] 区间均匀分布的随机数。这套抽样依据的是指数分布的无记忆性。
模拟一年的流程是:初始化所有元件状态,记录每个元件的下一次状态切换时刻,找到最早发生切换的元件,更新系统状态,然后对该状态下的系统进行潮流计算,检查是否有负荷失电。如果有,累积失电负荷量和持续时间。这个循环反复进行,直到模拟时长达到8760小时。
3.2 潮流计算:前推回代法在交直流混合网里的推广
可靠性评估对潮流计算有一个硬性要求:快。一次蒙特卡洛模拟一年数据,要跑8760个状态,每次状态变化都要重算潮流,而且这个潮流计算被嵌在进化算法的适应度函数里,一跑就是几万次。所以潮流计算速度决定整个项目能不能跑完。
我采用的方法是前推回代法,它非常适用于辐射状配电网。算法分成两步:第一步前推,从末端节点向根节点推进,根据节点注入功率和电压,求支路功率;第二步回代,从根节点向末端推进,更新各节点电压。重复这两步直到收敛。
对于交直流混合系统,处理思路是把换流器看作一个特殊的功率注入节点。交流侧和直流侧分开计算潮流,通过换流器的功率平衡方程进行耦合迭代。直流潮流计算相对简单,因为直流网络没有无功和相角,只需要求解线性方程组。这里给出一个前推回代法的核心代码骨架,方便你直接复现:
def backward_forward_sweep(nodes, branches, load_power, v0=1.0, tol=1e-4, max_iter=50): """ 前推回代法潮流计算 nodes: 节点列表, branches: 支路列表(包含首端、末端、阻抗) load_power: 各节点负荷复功率, v0: 根节点电压标幺值 """ v = {n: v0 for n in nodes} # 电压初始化 s = {n: load_power.get(n, 0j) for n in nodes} # 节点注入功率 for _ in range(max_iter): # 前推:从末端累加支路功率 branch_power = {} for b in sorted(branches, key=lambda x: x['depth'], reverse=True): # 按深度降序 back = b['end'] s_accum = s.get(back, 0j) branch_power[b['id']] = s_accum # 回代:从根节点更新电压 v_new = dict(v) for b in sorted(branches, key=lambda x: x['depth']): # 按深度升序 start, end = b['start'], b['end'] z = b['impedance'] i_br = (branch_power[b['id']].conjugate() / (v[start].conjugate() * v0)) if abs(v[start]) > 1e-6 else 0j v_new[end] = v[start] - z * i_br # 检查收敛 delta = max(abs(v_new[n] - v[n]) for n in nodes) v = v_new if delta < tol: break return v这段代码简化了实际工程中的处理,但对理解算法核心非常有效。实际我在项目中加了节点深度排序的预处理,大幅降低每次迭代的计算复杂度。
3.3 年时序负荷与光伏出力曲线建模
可靠性评估不能只用峰值负荷来做,因为峰荷只在极少数时段出现,用它评估会严重高估缺电风险,导致过渡投资。正确做法是构建小时级时序数据。
负荷曲线我用了典型日+随机波动的方式生成。每组规划方案共用同一个负荷序列,确保不同方案之间的比较公平。光伏出力曲线则基于光照强度的Beta分布抽样,再换算成功率输出。风速、光照、负荷之间实际上存在一定的相关性,但这个项目为了简化,假设它们相互独立,同时通过划分季节典型场景来兼顾时序耦合关系。
在实现中,我将时序数据预先计算好存成数组,可靠性评估循环里直接索引读取,避免每次重复计算。这种预处理看似简单,但对性能提升非常明显,整个仿真速度能快30%以上。
4. 算例分析与避坑指南
4.1 IEEE 33节点系统参数与场景设置
测试算例我选用经典的IEEE 33节点配电系统,基准电压12.66kV,基准功率10MVA,系统总峰值负荷3715kW+2300kvar。在原始拓扑基础上,预设三个待选节点作为光伏接入候选点,两个待选节点作为储能接入候选点,三个待选位置作为VSC换流器接入点,连接构建直流网络。
种群规模设为50,进化代数为100,交叉概率0.9,变异概率0.1,模拟年数为2000年(序贯蒙特卡洛的抽样年数)。这些参数我在多次试验后发现比较平衡,既不会让计算时间爆炸,也能保证EENS的方差收敛。
4.2 关键代码:目标函数内部的可靠性评估调用
目标函数必须把“一个规划方案”映射为“两个目标值”。核心代码逻辑如下:
def evaluate(individual): # 解码规划变量,生成网络拓扑和设备参数 vsc_pos = int(individual[0]) vsc_cap = individual[1] ess_cap = individual[2] pv_cap = individual[3] # 更新网络参数 network = build_network(vsc_pos, vsc_cap, ess_cap, pv_cap) # 经济性目标:计算综合费用(投资等年值 + 运维 + 购电 + 网损) cost_inv = calc_invest_annuity(vsc_cap, ess_cap, pv_cap) cost_om = 0.03 * cost_inv cost_loss, cost_purchase = calc_operation_cost(network) f1 = cost_inv + cost_om + cost_loss + cost_purchase # 可靠性目标:序贯蒙特卡洛模拟计算EENS eens = sequential_mc(network, n_samples=2000) f2 = eens return f1, f2运行时你会发现这个函数非常耗时,所以后面的性能优化显得尤为重要。
4.3 常见问题与调试技巧实录
问题一:目标函数出现None或NaN。原因通常是潮流计算不收敛时没有处理返回结果。解决方案是:在目标函数开头就做潮流预检,如果潮流不收敛,直接返回一个大罚函数值,而不是让异常传递到上层。
问题二:Pareto前沿分布不均匀,解全部集中在经济性好的区域。这通常是拥挤度距离计算时的归一化出了问题。当两个目标量纲差异极大时(比如一个成本是10^6量级,EENS是10^2量级),必须对目标值做归一化后再计算拥挤度,否则EENS维度几乎不起作用。
问题三:仿真时间爆炸。2000年序贯仿真加上100代进化,每代50个个体,总共要评估10000个方案,每个方案2000次年仿真,计算量巨大。我的解决方案分三层:第一层,把潮流计算从Python循环改成NumPy向量化操作;第二层,采用时间并行——把2000年拆成多个片段并行计算;第三层,在进化早期使用低精度评估(比如500年),后期才使用完整2000年评估。
三层优化合起来,整个项目的运行时间从原来的三小时压缩到了约二十分钟,工程意义非常明显。
4.4 快速验收的实用指标
跑完之后怎么判断结果靠不靠谱?我一般看三个信号。
一看Pareto前沿形状。正常结果应该是一条从左上到右下平滑下降的曲线。如果出现阶梯形或Z字形,多半是离散变量编码粒度太粗,或者约束罚函数设置不当。
二看经济极端解的设备配置。最便宜方案大概率是所有设备容量都取下界,如果出现容量反而很大的现象,说明目标函数里有漏算的成本项。
三看可靠性极端解的电源配置。最高可靠性方案的储能和换流器容量应该接近上界,且分布式电源会分散接入多个节点而不是集中在一处。如果集中在一处,说明可靠性评估代码可能存在孤岛效应误判。
这三招可以帮助你快速定位代码缺陷,而不是对着完全不合理的曲线发呆。
我个人做下来的体会是,这个项目最有价值的不是某个算法本身,而是把一个真实的工程规划问题完整拆成了可运行的代码逻辑。对新手而言,能坚持把经济性模型、可靠性仿真和进化算法这三块拼接整合,比单点调参的收获大得多。最后再分享一个小技巧:所有随机数生成器,无论是蒙特卡洛仿真还是遗传算法的种群初始化和交叉变异,都固定随机种子。这样每次运行的对比才有意义,调试定位问题也会轻松很多。我因为在调试时忽略了这件事,曾花了整整一个晚上排查一个根本不存在的“随机Bug”。