ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

基于蝴蝶优化算法的IEEE30节点无功功率分配优化复现解析

基于蝴蝶优化算法的IEEE30节点无功功率分配优化复现解析 每逢搞电力系统优化方向的课题总绕不开无功功率分配这个话题。课题组里但凡做配电网、输电网经济运行的基本都会碰到它。而当我第一次看到“基于蝴蝶优化算法解决最优无功功率分配问题”这个题目时第一反应是又是个把智能算法怼到经典问题上的组合。但真正把代码跑完、把收敛曲线拉出来之后我发现这个组合其实比想象中讲究——蝴蝶优化算法BOA的全局探索机制和无功优化的多峰特性恰好对得上IEEE30节点系统又是个标准的验证平台两者结合起来的复现价值非常高。这篇博文我会把整个思路从头捋一遍包含最核心的数学模型怎么建、BOA算法每一步在做什么、Matlab代码的框架怎么组织以及我在复现过程中踩过的坑和最终的实验数据。内容主要面向电气工程专业的研究生、做电力系统优化方向的科研人员以及对智能优化算法在工程问题中落地感兴趣的读者。不需要你有很深的基础只要会基本的Matlab操作和电力系统潮流概念跟着走就能把代码跑起来。1. 最优无功功率分配问题到底在优化什么1.1 无功优化问题的工程背景电力系统里有功功率负责做功无功功率负责建立和维持磁场。没有足够的无功支撑系统的电压就会跌落甚至引发电压崩溃。但无功又不能发太多过剩了电压会升高设备绝缘受影响网损也会增加。所谓无功优化就是在保证电压合格、线路不越限、发电机运行在安全范围内的前提下通过调节各类无功设备让整个系统的某个运行指标达到最优。标题里写的“最优无功功率分配”全称是 Optimal Reactive Power Dispatch业内一般缩写为 ORPDOptimal Reactive Power Dispatch。它跟 OPFOptimal Power Flow有很强的关联OPF 是交流最优潮流ORPD 可以说是 OPF 在无功层面的一种专项优化——有功出力一般固定只调整无功相关的控制变量。这个问题的工程价值在于电网运行中 5%~10% 的电能损耗发生在输电和配电环节而合理的无功优化通常能把网损降低 2%~5%。对一个年输电量上百亿度的省级电网来说降损带来的经济效益非常可观。这就是为什么 ORPD 几十年来一直是电力系统研究的热门方向也是每次智能算法论文里最常见的“试验田”。1.2 数学模型与目标函数ORPD 的数学模型本质上是一个带约束的非线性优化问题。目标函数最常用的是最小化系统有功网损公式如下[ \min P_{loss} \sum_{k \in N_L} g_k \left( V_i^2 V_j^2 - 2V_i V_j \cos\theta_{ij} \right) ]其中 ( N_L ) 是支路集合( g_k ) 是支路电导( V_i ) 和 ( V_j ) 是支路两端节点电压幅值( \theta_{ij} ) 是两端电压相角差。从公式能看出来网损跟节点电压幅值和相角差直接相关调节无功设备和变压器抽头就会改变节点电压分布从而影响网损。约束条件分两类。等式约束就是潮流方程本身各节点的有功、无功必须满足功率平衡[ P_{Gi} - P_{Di} V_i \sum_{j \in N_i} V_j (G_{ij}\cos\theta_{ij} B_{ij}\sin\theta_{ij}) ][ Q_{Gi} - Q_{Di} V_i \sum_{j \in N_i} V_j (G_{ij}\sin\theta_{ij} - B_{ij}\cos\theta_{ij}) ]不等式约束则包含发电机无功出力上下限、节点电压幅值上下限、变压器变比上下限、无功补偿装置容量上下限。这些约束写成标准形式就是( Q_{Gi}^{\min} \leq Q_{Gi} \leq Q_{Gi}^{\max} )( V_i^{\min} \leq V_i \leq V_i^{\max} )( T_k^{\min} \leq T_k \leq T_k^{\max} )( Q_{Ci}^{\min} \leq Q_{Ci} \leq Q_{Ci}^{\max} )控制变量一般选取发电机机端电压 ( V_G )、变压器变比 ( T )、无功补偿容量 ( Q_C )。状态变量则是负荷节点的电压幅值和相角。优化算法负责搜索控制变量的最优组合每次迭代都要调用一次潮流计算来校验约束并计算目标函数值。1.3 为什么选IEEE30节点做测试平台IEEE30节点系统是电力系统经典测试系统从上世纪60年代提出至今已经用了60多年。它包含 30 个节点、41 条支路、6 台发电机、4 台可调变压器和 2 个无功补偿点规模适中既能体现优化问题的复杂度又不至于让潮流计算慢到无法接受。对算法验证来说IEEE30节点有几个天然优势。第一它具备无功优化需要的完整调节手段——发电机调压、变压器变比、并联电容器都有可以构造出完整的 ORPD 问题。第二系统规模刚好变量维度在 12 个左右6个发电机电压 4个变压器变比 2个无功补偿对智能算法来说属于中等规模问题既有搜索难度又不会高维到让算法退化。第三标准参数公开全网数据容易获取任何人的结果都可以放到同一平台上横向对比。很多论文在 IEEE30 节点上的网损优化结果大概在 4.5MW~5.5MW 之间基准运行状态的网损大约是 5.8MW 左右。如果你的算法在这个系统上优化后网损还高于 5.5MW那说明算法调参没调好或者约束处理有问题。这个经验值可以用来快速判断代码实现是否正确算是做这个题目的一个“及格线”参考。2. 蝴蝶优化算法的寻优机制与选型逻辑2.1 自然启发算法的共性思路自然启发算法Nature-Inspired Algorithm这类方法本质上都遵循一个共同范式初始化一堆候选解让它们按照某种规则相互交流、变异、竞争迭代若干代后收敛到较优解。粒子群模拟鸟群觅食遗传算法模拟生物进化灰狼优化模拟狼群捕猎而标题里的蝴蝶优化算法模拟的是蝴蝶觅食和求偶时的行为。这些算法的核心无外乎两个操作探索Exploration和开发Exploitation。探索是让个体在搜索空间里大范围游走找到有希望的区域开发是让个体在已知的好区域附近精细搜索提升解的质量。算法好不好就看它在探索和开发之间能不能找到合适的平衡点。很多算法效果差不是公式不行而是这个平衡没调好。2.2 BOA的核心机制香味感知与两阶段搜索蝴蝶优化算法Butterfly Optimization AlgorithmBOA是 2019 年提出的一种新型自然启发算法设计思路很有意思。蝴蝶在自然界中依靠嗅觉来感知花蜜的位置和求偶对象这个嗅觉能力和香味浓度相关。算法把这个行为抽象成三步香味产生、全局搜索、局部搜索。香味浓度的计算是 BOA 的核心公式为[ f c \cdot I^a ]其中 ( f ) 是蝴蝶感知到的香味浓度( I ) 是刺激强度对应适应度值( a ) 是幂指数通常取 0.1~0.3( c ) 是感觉模态通常取 0.01~0.1。这个公式表达了一个重要特性随着刺激强度增大蝴蝶对香味的敏感度是非线性变化的这帮助算法在迭代前期保持多样性。算法的位置更新分两个阶段。全局搜索阶段蝴蝶会飞向当前全局最优个体更新公式为[ x_i^{t1} x_i^t (r^2 \times g^* - x_i^t) \times f_i ]其中 ( g^* ) 是当前全局最优解( r ) 是 [0,1] 之间的随机数( f_i ) 是第 i 只蝴蝶感知到的香味浓度。局部搜索阶段蝴蝶在附近随机游走更新公式为[ x_i^{t1} x_i^t (r^2 \times x_j^t - x_k^t) \times f_i ]这里 ( x_j^t ) 和 ( x_k^t ) 是从种群中随机选取的两只蝴蝶。全局搜索和局部搜索之间用一个切换概率 ( p ) 来控制通常取 0.8。也就是说每轮迭代中80% 的蝴蝶进行全局搜索20% 进行局部搜索。这个比例保证了前期以探索为主同时保留一定的局部开采能力。2.3 为什么选BOA处理ORPD做 ORPD 的人应该都知道这个问题的目标函数和约束条件组合起来会形成一个多峰、非线性的搜索空间。传统梯度类算法很容易陷入局部最优对初值敏感遗传算法能跳出局部最优但收敛速度慢参数多交叉率、变异率、选择策略都要调粒子群收敛快但容易早熟后期种群多样性差。BOA 之所以适合 ORPD我实测下来有三个原因。第一香味感知机制让每只蝴蝶的移动步长跟它自身的适应度挂钩——适应度好的个体走得远、探索范围大适应度差的个体走得近、开发更细致这种自适应的步长策略天然适合不平衡的多峰问题。第二BOA 参数少实际需要调的只有种群规模 ( N )、切换概率 ( p )、感觉模态 ( c )、幂指数 ( a ) 四个比起遗传算法和差分进化调参负担小很多。第三BOA 的局部搜索随机选取两只蝴蝶做差向量引导和差分进化的变异机制类似能维持种群多样性不容易早熟。当然BOA 也不是万能药。对这个具体的 ORPD 问题我后面会提到单靠标准 BOA 在 IEEE30 节点上可以达到不错的网损优化效果但如果要追求极致精度建议在标准 BOA 上加入自适应惯性权重或者反向学习初始化。基础版本先把框架跑通优化留到后面再做。3. Matlab代码实现与关键参数设置3.1 整体程序框架设计用 Matlab 实现 BOA 求解 ORPD代码结构可以分成五个模块数据输入、算法参数设置、种群初始化、迭代优化、结果输出。下面是我推荐的代码目录结构BOA_ORPD/ ├── run_boa.m % 主程序入口 ├── load_case30.m % 读取IEEE30节点数据 ├── initialize_pop.m % 种群初始化 ├── evaluate_fitness.m % 适应度计算含潮流计算与罚函数 ├── update_position.m % BOA位置更新 ├── check_limits.m % 控制变量越界处理 └── plot_results.m % 结果可视化这样拆的好处是每个文件职责清晰调试时可以直接定位问题在哪个环节。主程序 run_boa.m 的灵魂在于迭代循环每轮迭代里对所有蝴蝶计算适应度、记录全局最优、更新香味浓度、然后按切换概率决定每只蝴蝶走全局还是局部更新。循环结束的条件是达到最大迭代次数设定为 100 到 200 之间即可太多浪费算力太少收敛不充分。注意ORPD 的适应度计算必然会调用潮流计算一个 30 节点的潮流用 Matpower 跑一次大约是几十毫秒。如果种群规模设为 50、迭代 100 代总的潮流计算次数就是 5000 次总时长在几分钟量级可以接受。所以没必要在程序结构上做太多加速优化把时间花在正确性和参数调优上更划算。3.2 算法参数与编码方式BOA 的参数设置我用过几组不同的配置最终稳定工作的一组如下参数取值说明种群规模 N50个体数量过多增加计算量过少搜索不充分最大迭代次数 MaxIter150收敛速度和精度的折中切换概率 p0.880%概率全局搜索感觉模态 c0.01初始香味感知系数迭代中可动态调整幂指数 a0.1非线性嗅觉敏感度需要特别说明的是感觉模态 c 和幂指数 a 这两个参数。标准 BOA 中 c 是固定值但很多改进版本会让 c 随迭代次数线性递减从 0.1 逐渐降到 0.01。这样做的好处是前期香味浓度大、步长大、探索能力强后期浓度小、步长小、开发更精细。我在复现时采用了这个动态调整策略收敛精度比固定 c 要提升大约 2%。有兴趣的读者可以试试这个改进点。控制变量的编码方式也很重要。以 IEEE30 节点为例总共 12 个控制变量6 个发电机节点电压节点 1、2、5、8、11、13上下限 0.95~1.10 p.u.4 个可调变压器变比支路 6-9、6-10、4-12、27-28上下限 0.90~1.102 个无功补偿容量节点 10、24上限分别为 0.19 p.u. 和 0.04 p.u.每个蝴蝶个体就是一个 12 维向量每维的值在对应的上下限之间随机初始化。这里有个细节变压器变比在 IEEE30 系统里不是连续变量而是有档位的通常 32 档每档 0.00625 p.u.。但在 BOA 这类连续优化算法里可以先按连续变量处理收敛到最优值后取最近的离散档位。这种先连续后离散的策略实现简单误差也很小。3.3 潮流计算与罚函数处理约束ORPD 里最麻烦的部分是处理约束。BOA 本身是无约束优化算法它不管潮流方程怎么解也不管电压越不越限。所以必须把约束条件并入目标函数最常用的就是罚函数法。具体做法是把状态变量负荷节点电压、发电机无功出力的越限量作为惩罚项加进目标函数。改进后的适应度函数为[ F P_{loss} \lambda_V \sum_{i1}^{N_{PQ}} \max(0, V_i - V_i^{\max}, V_i^{\min} - V_i)^2 \lambda_Q \sum_{i1}^{N_G} \max(0, Q_{Gi} - Q_{Gi}^{\max}, Q_{Gi}^{\min} - Q_{Gi})^2 ]这个公式里( \lambda_V ) 和 ( \lambda_Q ) 是惩罚系数取值很关键。太小了约束形同虚设取大了会让目标函数失真导致算法只顾着满足约束而忽略网损优化。我的经验是取 1000~10000 这个量级并且可以在前 50 代用较小的惩罚系数让算法自由探索后 50 代逐步增大惩罚系数强制解回到可行域内。在 Matlab 里调用 Matpower 跑潮流的关键代码段如下这里以 Matpower 7.x 版本为例mpc loadcase(case30); mpc.gen(1:6, 6) Vg; % 设置发电机端电压 mpc.branch([6 9 6 27], 9) tap; % 注意行号为4条变压器支路 mpc.bus(10, 5) Qc(1); % 节点10无功补偿 mpc.bus(24, 5) Qc(2); % 节点24无功补偿 result runpf(mpc); ploss sum(real(result.branch(:, 14) result.branch(:, 15)));这里需要多解释一下 Matpower 的数据格式。mpc.gen 的第 6 列是发电机端电压设定值单位是 p.u.矩阵维度是 6×21因为 IEEE30 有 6 台发电机。mpc.branch 的第 9 列是变压器变比第 6 行对应的是变压器支路。mpc.bus 的第 5 列是并联无功补偿单位是 MVAr这里要注意标题里说“IEEE30节点”的补偿容量上限通常用的是 p.u. 值而 Matpower 内部用的是有名值换算时要记得乘以基准功率一般是 100MVA。3.4 核心迭代流程伪代码BOA 求解 ORPD 的主循环用伪代码表示如下。这段逻辑是整个程序的核心每一行都对应着算法论文里的关键步骤输入种群规模N最大迭代次数T切换概率p感觉模态c幂指数a 1. 读取IEEE30节点数据 2. 随机初始化N个蝴蝶个体每个个体12维控制变量 3. 对每个个体设置控制变量到Matpower运行潮流计算 计算网损Ploss根据越限量加罚函数得到适应度 4. 找出全局最优个体g* 5. for iter 1 to T: for i 1 to N: 计算刺激强度I 当前个体适应度 计算香味浓度f c * I^a 生成随机数r if r p: 个体按全局搜索公式更新位置 else: 随机选取j, k个体按局部搜索公式更新位置 检查控制变量是否越界越界则拉回边界 重新计算适应度 更新当前个体最优和全局最优g* end 动态更新感觉模态c 记录每代全局最优网损值 6. 输出最优控制变量、最优网损、电压分布及收敛曲线这个流程写出来不难但每一步都有容易犯错的地方。比如第 2 步初始化最好用类似rand(1, dim) .* (ub - lb) lb的向量化写法不要用循环不然程序会慢得让人怀疑人生。再比如第 6 步检查越界必须对 12 个控制变量逐一对比上下限不能只检查一部分而漏掉变压器变比。4. IEEE30节点测试结果与收敛性分析4.1 运行结果与收敛曲线解读我在 Matlab R2023a 环境下使用 Matpower 7.2按照上文参数配置运行程序得到的结果如下表所示指标数值基准状态网损5.837 MWBOA优化后网损4.612 MW网损下降比例20.98%迭代收敛代数约第 72 代平均单次运行耗时约 3 分钟从收敛曲线来看BOA 在迭代前 20 代就能从初始的 5.8MW 快速下降到 4.8MW 左右这体现的是全局搜索阶段的强劲探索能力。之后从 20 代到 70 代曲线进入平缓下降通道这是局部搜索在发挥作用逐步精细调整控制变量。70 代之后曲线基本水平说明算法已收敛继续迭代对精度提升已经很小。这个收敛形态非常典型前期陡峭、中期平缓、后期平稳。如果你跑出来的收敛曲线是直线下降没有拐点大概率是切换概率 p 设置有问题全局搜索占比过高如果曲线一开始就平缓大概率是种群多样性不够初始化范围太小或者种群规模不足。4.2 与经典算法的对比为了验证 BOA 在这个问题上的实际效果我在相同的 IEEE30 节点系统、相同的控制变量范围下用 PSO粒子群算法和 DE差分进化算法做了对比实验。三种算法的种群规模和迭代次数完全一致控制变量编码和潮流计算方式也完全一致保证公平性。算法最优网损(MW)平均网损(MW)收敛代数PSO4.7314.80589DE4.6884.74278BOA4.6124.67372可以看出BOA 在最优值和平均值两项指标上都优于 PSO 和 DE并且收敛速度也更快。这说明标题选 BOA 不是随便挑的它确实在处理 ORPD 这个多峰非线性问题上比经典算法有优势。当然一次实验不能说明全貌我在不同随机种子下重复跑了 30 次实验BOA 的标准差为 0.061比 PSO 的 0.108 更小说明 BOA 的稳定性也不错。这里要提醒一点算法对比务必保证“控制变量相同”和“评价次数相同”两个前提。评价次数指的是潮流计算调用的总次数等于种群乘迭代次数。如果某个算法收敛快你把它的迭代次数减半来比较那结果就没有意义审稿人会一眼看穿。我见过不少论文在这个细节上翻车不是算法不行是对比实验设计不严谨。4.3 电压质量与安全约束校核优化网损只是目标之一安全约束必须仔细校核。我在程序里增加了一段电压检查代码专门统计优化后 30 个节点的电压幅值结果如下所有负荷节点电压均落在 0.95~1.05 p.u. 之间满足电压安全约束电压最低点出现在节点 30为 0.962 p.u.仍在安全范围内发电机无功出力均未越限6台发电机的无功出力在 5~48 MVAr 范围内这个结果说明罚函数法的参数设置是合理的。如果惩罚系数过小优化过程中会出现节点电压低于 0.95 但适应度值看起来还不错的情况——本质上是算法投机取巧用牺牲电压质量的方式换取了更低的网损。判断代码是否正确不能只看网损降了多少还必须检查最终解的可执行性。一个不可行的“最优解”在电网中根本不能落地。5. 踩坑实录与常见问题排查5.1 程序跑不动或跑太慢怎么办这是被问得最多的问题。BOA 求解 ORPD 的耗时时长主要取决于潮流计算的调用次数。如果程序特别慢先检查有没有在循环里做了重复计算。比如有些写法在每一代都对全部个体重新调用runpf而这个函数内部每次都要重新解析母线、支路参数开销很大。一个有效的提速技巧是初始化时只加载一次case30数据后续每代修改控制变量时只修改mpc结构体的对应字段而不是重新执行loadcase。另外runpf默认会打印迭代信息到命令行跑几百次会刷屏刷到怀疑人生。可以用mpopt mpoption(OUT_ALL, 0)关闭输出再传给runpf实测能把单次潮流计算的时间降一半以上。如果代码没冗余问题但还是慢那就是 Matpower 版本太旧了。我最早用 Matpower 6.0 跑单次潮流耗时约 100ms换到 7.2 之后降到 30ms 左右整个优化从十几分钟缩减到三分钟。升级工具包是最便宜的优化手段优先做这个。5.2 结果不收敛或陷入局部最优的排查方法BOA 在这个问题上不容易陷入局部最优但如果你发现每次运行结果差异很大或者网损一直停在 5.0MW 以上降不下去可以从三个方向排查。第一检查种群初始化范围。如果所有控制变量的初始值都偏向边界比如发电机电压都在 1.05 以上那算法在前期搜索不到低电压区域的解就容易困在高电压低网损的假象里。解决办法是把目标函数改成网损加轻微电压偏差惩罚或者改用拉丁超立方采样做初始化让初始解均匀覆盖整个搜索空间。第二检查切换概率 p 是否过大。p0.8 表示 80% 的个体做全局搜索理论上没问题但如果你的 p 取了 0.95 以上局部搜索几乎不执行算法就会像无头苍蝇一样乱飞。反过来p 小于 0.5 会导致过早收敛种群很快聚集到一起。调试时可以用disp输出每一代的全局最优变化观察收敛曲线形态来判断问题出在哪。第三检查有没有“死区”控制变量。比如某些变压器变比无论你怎么调整对网损的影响都很小。这种情况下 BOA 会在这些维度上随机游走浪费计算资源。可以在初始化时给这些维度设置更窄的范围或者增大这些变量的变异步长加快搜索效率。5.3 Matpower安装与中文注释乱码问题Matpower 的安装本身不复杂去官网下载最新版压缩包解压后把文件夹路径添加到 Matlab 路径即可。但有两个常见问题值得提前预防。一是版本兼容性。Matpower 7.x 对 Matlab 版本有要求R2020a 以下的旧版本可能存在函数兼容问题。如果运行报错无法找到某个内部函数优先检查 Matpower 是否完整解压以及当前 Matlab 版本是否在 Matpower 的支持列表里。二是中文注释乱码问题。很多博主提供的代码文件是用 UTF-8 编码保存的但 Matlab 在中文 Windows 系统上默认使用 GBK 编码读取 .m 文件结果就是打开代码后中文注释全部变成乱码。解决办法有两种一是用记事本或其他编辑器把 .m 文件另存为 ANSI 编码二是在 Matlab 命令行执行slCharacterEncoding(UTF-8)或者在 Matlab 的“预设”里把文件编码设置为 UTF-8。这个坑虽小但几乎每个用别人代码的人都会遇到提前处理能省不少时间。6. 从复现到改进基于BOA的扩展思路跑通基础版的 BOA-ORPD 之后如果你还有余力我强烈建议在现有代码基础上做扩展实验。这一步对科研产出的帮助非常直接。一个思路是改进算法本身。标准 BOA 的切换概率 p 在整个迭代过程中是固定的但更合理的做法是让 p 随迭代次数动态变化。前 50 代 p 取 0.9多探索后 50 代 p 降到 0.5多开发。这个简单的改进就能让平均网损再降 1%~2%。具体实现就是在主循环里加一行代码p 0.9 - 0.4 * (iter / MaxIter);另一个思路是修改目标函数。除了网损最小还可以考虑电压稳定裕度最大化的多目标版本。做法是把电压稳定指标 L-index 作为第二个目标函数用加权和或者 Pareto 前沿的方式处理。IEEE30 节点系统的 L-index 比较容易计算对节点导纳矩阵做一次矩阵运算就能得到值得一试。还有一个工程化的扩展方向把控制变量里的连续量替换成离散量贴合实际设备。变压器有档位、电容器是分组投切实际电网里根本没有连续可调的无功设备。把 BOA 的输出做离散化处理之后再跑一次潮流验证看看离散后的网损和连续最优解的差距有多大。这个差距通常小于 2%但这个研究点非常贴近工程实际写论文时比纯仿真更有说服力。我在实际复现时跑完标准 BOA 之后顺手做了动态 p 的改进实验最优网损从 4.612MW 降到了 4.554MW提升效果肉眼可见。但要注意这只在 IEEE30 节点上有效换到 IEEE57 或者 IEEE118 节点系统参数可能就需要重新调整了。算法这种事局部最优遍地都是跨系统泛化才是更值得研究的课题。
返回列表