配电网光伏储能选址定容:基于粒子群双层优化与Matlab实现 📅 发布时间:2026/9/11 2:50:20 👁 浏览次数: 1. 项目概述配电网光伏储能选址定容到底在解决什么问题1.1 分布式光伏接入带来的三个典型问题最近在整理一个做过的项目基于粒子群优化算法的配电网光伏储能双层优化配置模型测试系统用的是IEEE33节点落脚点是选址定容全部用Matlab代码实现。这个题目乍一看有点长其实内核非常集中在一条标准的33节点配电网里决定光伏和储能分别装在哪几个节点、各装多大容量让整个系统的经济性和电压质量达到最优。先说说这个问题的现实来源。分布式光伏大规模接入馈线之后配电网并不是总能“吃得下”这些绿色电力。我在这类项目里最常遇到的三个问题第一个是电压越限光伏大发时段通常是中午馈线末端电压被抬高严重的直接超过1.05 p.u.第二个是网损增大光伏出力与负荷分布不匹配功率在馈线上来回流动线损不降反升第三个是倒送功率当光伏出力远大于本地负荷时功率向上一级电网倒送影响继电保护和电压调节策略。这三个问题靠“多装光伏”解决不了反而会恶化。所以工程上早就形成了共识光伏要配储能通过储能在时间轴上搬移电量削峰填谷平抑光伏波动同时兼顾电压支撑。但问题紧接着就来了——储能设备不便宜到底装在哪里、装多大才能用最少的钱办最多的事这就是“选址定容”要回答的问题。1.2 选址定容的本质一个混合整数非线性规划问题选址定容在数学上是一个典型的混合整数非线性规划问题MINLP。选址是离散决策装在哪几个节点用整数变量或0-1变量表示定容是连续决策每个站点装多少kW光伏、多少kWh储能映射到实数空间。再加上配电网潮流方程是非线性的目标函数又涉及投资、运行、网损等多个维度问题一下子变得非常难解。用穷举法不现实。IEEE33节点系统虽然有33个节点但如果允许光伏和储能各有若干个候选安装点组合规模会迅速膨胀。举个简单例子假设从32个可接入节点里选3个位置装光伏就有C(32,3)4960种组合再叠加连续容量变量搜索空间接近无穷大。传统数学规划方法比如分支定界、内点法面对这种混合整数非线性问题要么求解时间太长要么容易陷入局部最优。而群体智能算法天然适合这种问题粒子群优化算法PSO是其中最实用的一类它不要求目标函数可导不需要梯度信息对离散和连续混合变量的编码也灵活而且实现简单、收敛速度快。这就是为什么在这个项目里选择PSO作为上层搜索算法。1.3 为什么选IEEE33节点作为测试系统IEEE33节点系统是配电网研究领域事实上的“标准算例”几乎所有做主动配电网、分布式电源规划、无功优化的论文都会用它做验证。原因很简单它规模适中既有配电网辐射状结构的基本特征又不会因为节点太多导致潮流计算拖慢算法。这个系统的关键参数我记得很清楚33个节点、32条分段支路、5个联络开关基准电压12.66kV基准容量10MVA总负荷3715kWj2300kvar。节点1是平衡节点代表上级变电站出口其余节点都是PQ节点。负荷分布并不均匀末端节点比如18、22、33附近负荷较重这正好用来检验“光伏和储能该不该往末端放”这个核心问题。IEEE33节点的数据文件在Matlab里很好组织每个节点的有功负荷、无功负荷、每条支路的阻抗参数都是现成的网上也能轻松找到标准数据表。对我这种习惯用Matlab做仿真的来说这套系统数据干净、结果可对比跑出来的配置方案和论文里的经典结论可以直接对照验证算法有没有写错。1.4 为什么要用“双层优化”而不是一个PSO全搞定这是刚接触这个课题的人最容易问的问题既然PSO能处理混合变量为什么还要搞双层为什么不把所有决策变量扔进一个PSO里直接搜关键在于“容量配置”和“运行调度”的时间尺度完全不同。光伏和储能装多大属于规划层面的决策周期是年储能每天怎么充放电属于运行层面的决策周期是小时。如果把它们放进同一个优化模型变量维度暴增——不仅要搜位置和容量还要同步搜24小时的储能出力曲线而且约束条件跨越多个时间断面PSO在这种高维空间里收敛极慢解的质量也不稳定。更合理的做法是把问题分层上层只决策“装在哪、装多大”下层在给定配置方案的基础上优化储能的日运行策略算出这个配置方案对应的最优运行成本再把运行成本反馈给上层作为评价方案优劣的一部分。上层收敛到最优配置下层给出该配置下的最佳运行方式两者互相嵌套、反复迭代。这就是双层优化的核心思想也是这个模型能和单层方法拉开差距的原因。2. 双层优化模型的具体设计与数学表达2.1 上层模型以年综合成本最小为目标的定容优化上层模型解决的是规划问题目标函数是年综合成本最小化我把它的构成拆成四块F C_inv C_om C_loss C_net其中C_inv是光伏和储能设备的投资等年值C_om是年运行维护费用C_loss是年网损费用C_net是年购电费用。这四个分量对应的工程含义分别是设备买回来要折旧分摊到每一年运营期间需要维护功率在线路上流动会产生能量损耗损耗折算成电费负荷需求减去光伏出力和储能放电之后剩下那部分要从上一级电网购买。投资等年值需要特别解释一下。光伏和储能的使用寿命不同光伏系统通常是20到25年储能电池可能是10到15年不能直接把总投资的现值加起来否则会高估规划期的成本。标准的做法是用等年值系数CRF把初始投资折算成年值CRF r(1r)^T / [(1r)^T - 1]其中r是贴现率T是设备寿命。这样光伏和储能虽然寿命不同但都能统一到“每年平均花多少钱”这个尺度上目标函数才有可比性。上层模型的决策变量就是光伏和储能的接入位置与容量。如果预设最多装3个光伏站点和2个储能站点那么变量组就是光伏位置数组离散、光伏容量数组连续、储能位置数组离散、储能容量数组连续。约束条件包括节点安装容量上限、光伏总容量不超过系统最大可接纳容量、储能总量受投资预算约束以及最核心的潮流约束和节点电压约束通常在0.95到1.05 p.u.范围内。2.2 下层模型储能日运行调度优化下层模型本质上是给定一套光伏和储能配置方案之后求解储能系统最优的24小时充放电计划。目标函数可以设置为日运行费用最小也就是说在满足负荷需求的前提下尽可能让储能“低充高放”在电价低谷时充电、在电价高峰时放电从而降低从电网购电的费用。下层模型的决策变量是储能每小时的充放电功率一共24个连续变量。约束条件包括储能SOC荷电状态的递推关系、SOC上下限、充放电功率上限、充放电效率以及每个时间断面的功率平衡约束。SOC递推关系是最容易写错的地方实际公式是SOC(t1) SOC(t) η_ch·P_ch(t)·Δt / E_es - P_dis(t)·Δt / (η_dis·E_es)其中η_ch是充电效率η_dis是放电效率P_ch是充电功率P_dis是放电功率E_es是储能额定容量Δt是时间步长默认1小时。这里有一个容易踩的坑充放电效率通常取0.95左右但“不能充电和放电同时按0.95来算”有些初学者直接在SOC递推里只乘一个效率导致能量不守恒运行成本算出来偏低上层配置结果自然失真。下层模型是最优潮流或线性规划问题在Matlab里可以直接调用linprog求解。因为目标函数和约束都是线性的如果忽略网损随功率的非线性变化线性规划求解速度极快这比在下层再嵌套一层PSO要高效得多。我实测过下层用linprog和下层用PSO跑同样的算例前者计算时间可能只有后者的几十分之一而且结果更稳定。2.3 上下层的数据传递与迭代收敛逻辑双层模型能跑通的关键在于上下层数据接口要清晰。我整理的逻辑是这样的上层PSO生成一套配置方案位置容量把光伏出力时间序列、储能参数、负荷数据传给下层下层收到方案后进行24小时调度优化得到最优运行成本和SOC变化曲线把日运行成本乘以365折算成年值连同网损成本一起返回上层上层把这个返回值代入年综合成本函数作为粒子的适应度更新pbest和gbest进入下一代。这个过程嵌套循环外层迭代一次内层要跑很多次调度优化。所以代码设计时要特别注意数据传递效率尽量避免在循环里重复加载基础数据。我的做法是将IEEE33节点的线路参数、负荷数据、电价曲线全部定义为全局变量或结构体在内层循环里直接读取不在每次迭代时重新初始化。对这个双层结构我建议先跑通一次完整流程确认数据传递正确再去做收敛精度和参数调优。很多同学一上来就追求结果最优结果程序跑了一个小时才发现潮流计算里负荷符号写反了整个坐标系全是错的。3. 粒子群算法的工程化改造与关键参数详解3.1 粒子编码混合编码是如何设计的PSO最初是为连续优化设计的但选址定容问题既有离散变量又有连续变量直接套标准PSO肯定不行。这时候就要做编码改造。我采用的是“实数位置取整离散”的混合编码方式。具体来说一个粒子就是一个一维数组假设配置上限是3个光伏站点和2个储能站点那么这个数组可以设计为12维前3维是光伏位置实数编码取值在2到33之间的连续数取整后映射到节点编号第4到6维是光伏容量连续实数单位是kW第7到8维是储能位置第9到10维是储能容量最后2维可以预留为储能额定功率与容量的比值比如定义储能功率容量比始终为0.2表示1kWh配0.2kW。这个编码的意义在于位置和容量在粒子迭代过程中各自保持自己的更新方式位置变量取整后落到离散节点空间容量变量保持连续。取整操作在Matlab里就是round或者ceil要注意节点编号不能取到1号平衡节点否则潮流计算会出问题。我在代码里加了一道处理如果取整后的节点编号等于1就强制改为2。3.2 关键参数惯性权重、学习因子、速度限幅怎么设PSO的性能很大程度上取决于参数设置我在这个项目里踩过不少坑总结出了一套比较可靠的参数方案直接列成表格供参考。参数推荐值说明种群规模30~50IEEE33规模下40就够太大反而拖慢收敛最大迭代次数100~200通常150代以内能收敛再多意义不大惯性权重w0.9线性递减到0.4前期全局搜索后期局部精修学习因子c12.0自我认知项不宜过小学习因子c22.0社会认知项与c1取相同值较稳妥速度上限Vmax变量范围的10%~20%防止粒子飞出可行域惯性权重线性递减是我在这个项目里验证最有效的手段。迭代初期w大粒子速度快能大面积探索搜索空间迭代后期w小粒子速度慢能围绕当前最优解精细搜索。如果全程用固定w要么前期收敛慢要么后期在最优解附近震荡结果不稳定。学习因子c1和c2可以取固定值2.0这是标准PSO论文里的经典配置。如果想追求更快的收敛速度可以把c1和c2设为1.49445这是带收缩因子的PSO常用参数效果也不错但我自己在测试中发现对这个具体问题的改善有限。速度限幅一定要加否则粒子可能一步跳出可行域然后罚函数值巨大算法半天回不来。3.3 约束处理罚函数法在选址定容里的实战用法选址定容问题的约束很多直接用约束硬性过滤不可行解会导致种群快速退化可用粒子越来越少。我采用的是罚函数法把约束违反量转化成惩罚项叠加到目标函数上让不可行解虽然能参与迭代但适应度很差自然会被淘汰。罚函数的具体形式是F_fitness F_original λ1·U_voltage λ2·U_power λ3·U_socU_voltage表示电压越限量的平方和U_power表示功率不平衡量U_soc表示储能SOC越限程度λ1、λ2、λ3是惩罚系数。实际调试中我一般从10开始逐步增大惩罚系数直到最优解不再出现电压越限。这里有一个非常重要的经验惩罚系数不能设得太大也不能太小。太大会导致粒子一碰到边界就被巨大的惩罚值“弹开”种群多样性下降搜索不充分太小则会让大量越限解混进迭代最优解可能落在不可行域里看着成本很低实际上根本不满足网络安全约束。我做过一组对比测试惩罚系数设为10、100、1000三种情况发现λ100时结果最合理电压约束基本都能满足又没有过度压制搜索。当然这个值和目标函数量级有关建议读者在跑自己模型时先观察一下目标函数的数量级再确定惩罚系数的基准值。4. Matlab代码实现与实操记录4.1 IEEE33节点系统的数据准备Matlab代码实现的第一个步骤是准备IEEE33节点系统的数据。我把线路参数和负荷数据统一存放在一个结构体里方便在PSO迭代过程中直接调用。node_load是这个系统最重要的输入之一。每个节点的有功负荷在几十到两百多千瓦不等总负荷3715kW无功负荷总量2300kvar。支路阻抗数据则是标准的33×4矩阵每行包含起始节点、终止节点、电阻和电抗。这些数据在代码里用固定的数组初始化建议写成独立的函数文件比如init_ieee33.m返回结构体bus和branch。我建的数据结构大概是这样的function [bus, branch] init_ieee33() % bus: 33x6矩阵列分别为节点编号、有功负荷、无功负荷、电压幅值初值、相角、节点类型 % branch: 32x5矩阵列分别为起始节点、终止节点、电阻(ohm)、电抗(ohm)、支路长度倍数 % 节点1为平衡节点bus(1,4) 12.66kV基础数据准备好之后我建议先用Matpower或者自己写的前推回代法跑一遍初始潮流确认基准状态下的节点电压在合理范围内。这个验证步骤很重要能排除数据录入错误否则后续PSO迭代过程中出现的“异常结果”会让你分不清是算法问题还是数据问题。4.2 潮流计算前推回代法的Matlab实现要点IEEE33节点是辐射状网络最适合的潮流计算方法是前推回代法。这个方法相比牛顿-拉夫逊法实现简单、计算速度快、占内存小而且对辐射状配电网收敛性极好几乎不需要调整初值。前推回代法的原理可以这样理解先假设所有节点电压都是额定值从末端节点开始向前推算各条支路的功率分布这叫“回代”然后从根节点开始向后推算各节点的电压降落这叫“前推”。反复迭代这两个步骤直到两次迭代的电压差值小于允许误差。在Matlab实现里回代的核心语句是把每个节点的注入功率算出来然后从末梢支路往根节点累加功率。前推的核心语句是根据支路功率和线路阻抗算电压降落从根节点逐步更新到末端。两种迭代交替进行一般几十次迭代就能收敛误差控制在1e-6以内。接入光伏和储能之后潮流计算里要把分布式电源当作负的负荷来处理节点注入功率等于负荷功率减去光伏出力加上储能充电功率减去储能放电功率。这个符号逻辑一定要理清楚我debug时发现的最多的错误就是储能“充电时功率方向反了”导致系统仿佛多了一个发电机电压全面偏高配置结果完全失真。4.3 PSO主循环与双层嵌套的代码框架Matlab实现的核心是主程序的循环结构。我整理了一个典型的代码框架读者完全可以照着搭%% 初始化PSO参数 Np 40; % 种群规模 maxIter 150; % 最大迭代次数 c1 2.0; c2 2.0; % 学习因子 w_start 0.9; w_end 0.4; %% 初始化粒子位置和速度 pos zeros(Np, dim); % dim是粒子维度 vel zeros(Np, dim); for k 1:Np pos(k, :) init_particle(); end %% PSO主循环 for iter 1:maxIter w w_start - (w_start - w_end) * iter / maxIter; for k 1:Np % 解码粒子得到选址定容方案 [pv_pos, pv_cap, es_pos, es_cap] decode_particle(pos(k, :)); % 调用下层优化计算日运行成本和网损 [cost_oper, loss] lower_level(pv_pos, pv_cap, es_pos, es_cap); % 计算上层年综合成本 fitness(k) upper_cost(pv_cap, es_cap, cost_oper, loss); % 加入罚函数处理约束 fitness(k) fitness(k) penalty(pos(k, :)); end % 更新pbest和gbest [~, idx] min(fitness); if fitness(idx) Gbest_fitness Gbest_fitness fitness(idx); Gbest_pos pos(idx, :); end % 更新速度和位置 for k 1:Np vel(k, :) w * vel(k, :) ... c1 * rand(1, dim) .* (Pbest(k, :) - pos(k, :)) ... c2 * rand(1, dim) .* (Gbest_pos - pos(k, :)); vel(k, vel(k, :) Vmax) Vmax; vel(k, vel(k, :) -Vmax) -Vmax; pos(k, :) pos(k, :) vel(k, :); end end这里最关键的是lower_level函数它内部完成储能24小时调度优化。我用的是linprog求解变量是24维的储能充放电功率约束写成矩阵形式传入linprog。实测下来在一次PSO迭代里跑40个粒子的下层优化大概需要几秒钟整个程序运行大约十分钟左右属于可接受范围。我在调试代码时还发现一个细节在PSO位置更新后粒子某些维度的值可能越界比如位置取整后是负数或者容量超过上限必须在每次迭代后做一个越界恢复处理。常用的方法有两种一种是直接把越界值截断到边界另一种是随机重置到边界附近。我推荐截断法实现简单且结果稳定。5. 实测结果与调参经验分享5.1 收敛曲线分析为什么设置迭代100次而不是更多程序跑通之后我第一件做的事就是画出收敛曲线。典型情况下前30代适应度下降非常快找到的配置方案从完全随机逐步变为相对合理50代之后下降速度明显放缓开始进入局部精修阶段100代之后曲线基本走平变化幅度小于千分之一。这说明对于IEEE33节点这个规模的问题150代已经足够。继续增大迭代次数只会增加计算时间对结果精度的提升非常有限。我甚至尝试过跑300代最终结果和150代几乎一样但耗时翻倍。如果读者发现自己的收敛曲线在100代还在明显下降那说明初始种群质量太差或者惯性权重衰减太快建议调整参数而不是单纯加迭代次数。5.2 配置结果解读光伏和储能的最优选址有什么规律通过这个模型得到的最优配置方案背后有明显的工程规律。光伏倾向于配置在馈线末端和重负荷节点附近比如节点18、节点33这些末端节点因为在这些位置接入光伏能起到电压支撑作用减少线路上的功率传输距离网损下降明显。储能则倾向于配置在光伏附近或负荷中心。储能和光伏放在同一位置或邻近位置可以就地平抑光伏出力的波动减少功率远距离输送带来的损耗。如果储能位置离光伏太远光伏大发时段发出的功率还要经过长线路输送到储能点中间损耗白白浪费。容量配置上光伏容量一般不会超过接入节点负荷需要的上限储能容量则和光伏容量存在一个匹配关系。我在多次实验中发现储能容量与光伏容量的比值在0.3到0.5之间时综合经济性最好。这个比值太小储能削峰填谷能力不足比值太大储能投资利用率不高经济性下降。这个结果从工程角度也很好理解储能本质上是“辅助角色”配得够用就行配太多就是浪费。5.3 避坑指南7个我在代码调试中踩过的坑第一个坑是初始化粒子的位置取整后越界。Matlab的randi和round在处理浮点数时可能会生成超出节点范围的编号我在代码里必须加边界判断把小于2的强制改为2把大于33的强制改为33否则后续潮流计算会因为找不到对应节点数据而直接报错。第二个坑是罚函数系数设置不当导致结果失真。这个前面提到过惩罚系数太小约束不满足太大搜索被压制。我的经验是先跑一次不带罚函数的模型观察目标函数的量级再把惩罚系数设为目标函数量级的0.1到1倍然后逐步调整。第三个坑是储能SOC初始化不合理。有些粒子在迭代初期生成的储能容量非常小导致SOC上下限区间被压缩到几乎没有可行解。这时候linprog会报“无可行解”程序直接中断。我的解决办法是在init_particle函数里就限制储能容量的下限比如最小为100kWh同时在下层优化里加入SOC初始值设为0.5给储能留出充放电空间。第四个坑是电价曲线的时间颗粒度不一致。我最早用的电价数据是峰谷两段式而负荷数据是24点连续序列结果储能调度在电价切换点前后出现诡异的充放电来回切换。后来我把电价数据细化成24点序列问题就消失了。储能调度对电价信号极其敏感电价数据的颗粒度至少要跟负荷数据一致。第五个坑是用前推回代法计算潮流时没有考虑光伏接入后的电压修正。前推回代法本身迭代收敛是稳定的但如果光伏接入容量很大末端节点电压可能超过1.1 p.u.需要加一个简单的电压越界检查把越界节点记录下来并在罚函数里体现。第六个坑是Linprog默认显示太多中间输出。每次下层优化linprog都会打印一堆迭代信息导致PSO整个循环非常卡顿。我在linprog参数里设置了Display为off程序运行速度快了不少代码看起来也清爽。优化算法嵌套里这种“隐藏输出”的小细节往往能带来意想不到的效率提升。第七个坑是没有固定随机种子。PSO是随机算法同一个程序在不同时间跑出来的结果会有波动多峰问题尤其明显。如果读者要复现实验结果、画收敛曲线、对比算法性能建议在程序开头加上rng(0)把所有随机数序列固定下来这样每次运行结果完全一致排除了随机性干扰。5.4 双层模型和经典单层模型的差异有多大我特意做过一组对比实验同一个IEEE33节点系统用单层PSO把所有变量放一起优化和用双层模型分别求解结果差异非常明显。单层模型跑了300代得到的配置方案在年综合成本上比双层模型高出8%左右而且光伏和储能的配置位置比较散乱缺乏工程合理性。这个差距的根本原因是双层结构把运行层约束从上层解耦了。单层模型在上层搜索时很难在每一步都精确评估“这个配置方案在最优调度下的运行成本”而双层模型下层专门跑了一次优化调度给出的运行成本是精确的。搜索方向更准确最终解自然更优。从计算效率角度单层模型虽然变量空间“看起来”小了但每个粒子适应度评估时如果不做优化调度就只能用简单规则比如固定储能每个时刻以额定功率充放电评价出来的方案非常粗糙需要更多迭代才能修正。双层模型每一步适应度评估都更精确反而总迭代次数更省。这也是我强烈建议做规划类问题优先考虑双层结构的原因。最后再说几句个人体会这个项目做下来我最大的感受是选址定容这类规划问题的难点从来不在某个数学技巧上而在于把工程问题抽象成优化模型时每个环节都要和实际物理过程对得上。光伏出力曲线用什么样的数据、储能SOC怎么递推、电压约束加不加、电价怎么设置这些决定模型是否真实反映现场。模型建错了算法再高级跑出来的结果也是漂亮但不实用的废纸。就实现而言Matlab做这个项目确实很顺手前推回代法几十行代码就能写出来linprog处理储能调度又快又稳PSO主循环逻辑也很直观。相比PythonMatlab在电力系统仿真这种矩阵密集计算场景下的开发效率更高调试工具也更直接特别是画收敛曲线、电压分布图这些分析图表几乎是信手拈来。如果读者想在这个模型基础上做扩展比较有价值的方向是加入光伏出力和负荷的不确定性场景比如用多场景法描述晴天、多云、雨天以及考虑电动汽车充电负荷的动态影响。模型框架不用大改主要是把下层的时间序列从单一天扩展到多个典型日计算量会增加一些但结果会更接近工程实际。我后续自己也打算往这个方向继续迭代把更多配电网运行约束加进去再做一版。