拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

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

基于粒子群算法的多微网优化调度MATLAB仿真与工程实现

基于粒子群算法的多微网优化调度MATLAB仿真与工程实现

前阵子一直在做多微网优化调度的仿真项目,核心工作是用MATLAB搭建两个互联微网的优化模型,再用粒子群算法求解每个时段的调度方案,最终输出蓄电池充放电、柴油发电机出力以及微网之间的交互功率。这个项目听起来不算复杂,但真正动手做下来,坑还是挺多的——从数学模型怎么简化,到粒子群算法怎么编码,再到惩罚系数怎么调,每一步都可能让结果偏离物理常识。这篇文章就把整个项目的建模思路、PSO实现过程以及我在调试中踩过的坑完整整理出来,给正在做微电网优化调度、或者想用粒子群算法做电力系统方向毕业设计的同学一个可以直接参考的算例。

我尽量用从业者交流的口吻,把“为什么要这样处理”讲清楚,而不是只丢出一堆代码。毕竟优化调度的核心不是粒子群算法本身有多神秘,而是如何把工程约束翻译成算法能理解的语言。

1. 项目整体架构与思路拆解

1.1 两个微网之间为什么要“互济”

先交代一下我搭建的这个仿真系统。通常单微网优化调度只需要管好本微网内部的电源和负荷,问题相对清晰。但一旦变成“多微网”,事情就变得有意思了:两个微网之间通过联络线连接,功率可以双向流动,微网1在光伏大发或者负荷低谷时可以把多余的电送往微网2,反过来微网2在自身缺电时也能从微网1得到支撑。

这个互联结构在物理上很直观——相当于多个微网组成一个小型电力系统,互相作为备用容量,也提高了整体系统的经济运行空间。比如微网1的柴油发电机容量较大,发电成本相对低,那么由微网1多出力、向微网2送电,可能比微网2自己启动高成本机组更划算。这就是多微网协调调度最核心的价值所在。

在我这个模型里,两个微网各自的设备构成不完全相同。微网1配备了一台100kW的柴油发电机、一组200kWh的蓄电池和一个峰值约60kW的光伏系统;微网2则是一台80kW的柴油发电机、一组150kWh的蓄电池以及一条相对较重的负荷曲线。联络线的传输功率上限设定为30kW,这个值不能取得太大,否则联络线的经济性会掩盖微网内部调节的作用,也不能太小,否则互济的意义就不明显。

1.2 为什么选粒子群算法而不是直接写线性规划

多微网优化调度本质上是一个带约束的非线性优化问题。柴油发电机的燃料成本是出力的二次函数,蓄电池的充放电效率在充和放两个方向上还不一样,再加上功率平衡的等式约束、SOC的递推约束、联络线容量约束,整个问题呈现出明显的非凸性。

如果使用传统的数学规划方法,比如混合整数线性规划或动态规划,理论上可以求出更严格的解。但在做研究和课程设计时,粒子群算法有几个明显的吸引力:实现代码量小、不需要求梯度、对目标函数的连续可导性没有要求,而且它对约束的“容忍度”很高——即使某个粒子暂时违反约束,也能通过惩罚项把它拉回来。这在探索阶段非常有用。

当然,我也得坦白讲一句大实话:如果这是工业级应用、对解的精度有硬性要求,我建议还是用Gurobi或者MATLAB的Optimization Toolbox去求解数学规划模型。粒子群算法在严格意义上得到的是满意解而非最优解,但对于“分析两个微网的优化调度规律”这个目标来说,PSO输出的方案足够说明问题了,而且收敛曲线、调度曲线的物理规律都非常清晰。

1.3 整体求解流程先搭出来

动手写代码之前,我把流程拆成了六个步骤,后面所有工作都围绕这个流程展开:

  1. 定义系统参数:发电机成本系数、容量限制、蓄电池容量、充放电效率、联络线容量、负荷与光伏曲线。
  2. 定义决策变量:每个时段各设备的出力或状态变量,展开成一维粒子向量。
  3. 编写目标函数:总成本最小,包含发电成本、购电成本、蓄电池损耗成本。
  4. 编写约束处理:用惩罚函数处理功率平衡等式约束,用边界裁剪处理上下限约束。
  5. 跑粒子群主循环:初始化种群、迭代更新速度位置、更新个体最优和全局最优。
  6. 输出并可视化:绘制收敛曲线、SOC曲线、机组出力曲线、微网交互功率曲线。

这个流程看起来简单,但每一步展开都有值得注意的细节。尤其是第4步,约束处理方式直接决定了最终结果是否符合物理规律。

2. 核心数学模型搭建

2.1 目标函数怎么定才合理

这个案例的目标是让两个微网在24小时调度周期内的总运行成本最小。运行成本由三块构成。

第一块是柴油发电机的燃料成本。工程上一般用二次函数近似:

C_i(P) = a_i * P^2 + b_i * P + c_i

其中P是发电机出力,a、b、c是成本系数。a项体现了机组在接近满负荷时效率下降带来的边际成本上升,c项是空载成本,表示即使发电机不输出功率也要消耗的燃料费用。微网1的机组我取 a = 0.6,b = 1.2,c = 10;微网2的机组则因为容量更小、效率略低,取 a = 0.8,b = 1.5,c = 8。这个参数组合决定了系统在调度时会优先安排经济性更好的机组多出力。

第二块是从上级电网购电的成本。这里我设置了分时电价:峰时段(10点-15点、18点-21点)购电价格1.2元/kWh,谷时段(23点-次日6点)价格0.4元/kWh,平时段0.8元/kWh。之所以要引入分时电价,是因为只有电价存在波动时,蓄电池的“低充高放”才有经济意义,调度结果也会更有看头。

第三块是蓄电池的损耗成本。这里做了一点简化,没有采用复杂的循环老化模型,而是用一个线性折旧估计:每充放1kWh计设备折旧费0.05元。学术仿真中这种简化是完全可以接受的,因为蓄电池的老化机理,比如循环次数、放电深度、温度等因素,都需要更细的实验数据支撑,在调度模型里强行加入反而会喧宾夺主。

2.2 决策变量、约束条件与物理意义

决策变量是整个模型里最需要弄清楚的部分。因为我做的是24时段日前调度,所以每个变量都要按时段展开。这个案例的粒子结构我设计成:

  • 微网1柴油发电机出力,1×24
  • 微网1蓄电池充电功率,1×24
  • 微网1蓄电池放电功率,1×24
  • 微网2柴油发电机出力,1×24
  • 微网2蓄电池充电功率,1×24
  • 微网2蓄电池放电功率,1×24
  • 微网1向微网2输送的交互功率,1×24

这里还有一个容易忽视的点:蓄电池的充电功率和放电功率必须分开定义,因为充放电效率不一样。如果只定义一个可正可负的变量,那就无法同时表达两种方向上的效率差异。物理意义上,充电时电从系统流入蓄电池,放电时电从蓄电池流回系统,模型上必须用两个非负变量来描述。

约束条件从工程角度可以分为四组:

功率平衡约束:每个微网在每个时段都必须满足能量守恒。对于微网1: Pdg1(t) + Ppv1(t) + Pdis1(t) + Pgrid1(t) = PL1(t) + Pch1(t) + P12(t)

其中Pgrid1(t)表示从上级电网购电,P12(t)是微网1流向微网2的功率。注意P12在微网1侧是“负荷”属性,在微网2侧是“电源”属性,符号处理必须统一,否则整个系统就会凭空生产或消耗功率。

设备出力约束:发电机出力在最小技术出力和额定容量之间,蓄电池充放电功率有上下限,联络线功率受线路容量限制。这些约束在粒子群算法里用边界裁剪处理,也就是粒子越界时直接拉回到边界值。

SOC递推约束:蓄电池的荷电状态满足递推公式: SOC(t) = SOC(t-1) + (eta_ch * Pch(t) - Pdis(t) / eta_dis) / Cap * dt

初始SOC设为0.5,并要求调度周期结束时SOC回到0.5附近。这样终端SOC约束让整个调度方案是可持续的,不会出现把所有电量都耗尽的情况。

2.3 微网间协调的关键约束处理

微网间交互功率P12(t)是整个模型里最体现“多微网协调”精髓的变量。我在代码里规定P12(t)大于等于0,表示功率从微网1流向微网2,并设定上限30kW。

为什么不把P12设计成可正可负的双向变量?原因很简单:如果允许双向自由流动,粒子群算法在迭代初期可能会频繁出现“微网1送电给微网2、同时微网2又送电给微网1”的荒唐方案,虽然目标函数最终会把这种行为惩罚掉,但收敛速度会明显变慢。更稳妥的做法是直接限定单向变量,然后用一个常数偏移处理反向传输,或者在算法收敛后更换P12的符号方向再跑一轮验证。对于这个算例,我直接限定方向为微网1流向微网2,同时也单独测试过反向场景。

这个处理方式背后有一个值得记住的原则:智能优化算法的变量设计越贴近物理实际,求解效率越高。不要为了模型“形式上的对称性”而增加不必要的维度,粒子维数越高,搜索空间越稀疏,收敛效果越难保障。

3. 粒子群算法原理与MATLAB实现

3.1 参数设置与粒子编码方式

标准粒子群算法的核心就是两个更新公式。速度更新:

v(i, :) = w * v(i, :) + c1 * rand * (pbest(i, :) - x(i, :)) + c2 * rand * (gbest - x(i, :))

位置更新: x(i, :) = x(i, :) + v(i, :)

这里w是惯性权重,我用的是线性递减策略,从1.2递减到0.4。这个策略的重要意义在于:迭代初期需要较大的w保持全局搜索能力,让粒子有足够的移动速度去探索整个解空间;迭代后期w变小,粒子更加依赖pbest和gbest的引导,在最优解附近精细搜索。学习因子c1和c2都取2.0,这是经典配置,大多数实际问题下表现稳定。

粒子编码是整个实现里最先踩坑的地方。我的粒子是一个一维的行向量,长度等于决策变量总数乘以24。以这个案例为例:微网1发电机24个值 + 微网1充/放电各24个值 + 微网2发电机24个值 + 微网2充/放电各24个值 + 交互功率24个值。注意充电和放电分开定义后是独立变量,不能合并。

我强烈建议在代码注释里写清楚每个维度的索引范围。比如定义变量:idxPdg1 = 1:24; idxCh1 = 25:48; 这样拆解粒子时会少犯很多低级错误。我第一次写的时候就因为忘了调整索引偏移,导致微网2的发电出力读取了微网1蓄电池充电的值,算出来的成本曲线完全对不上号。

3.2 粒子群主循环核心代码

主循环本身不复杂,核心代码可以精简为:

for iter = 1:maxIter w = wMax - (wMax - wMin) * iter / maxIter; for i = 1:nPop v(i, :) = w * v(i, :) ... + c1 * rand(1, nDim) .* (pbest(i, :) - x(i, :)) ... + c2 * rand(1, nDim) .* (gbest - x(i, :)); x(i, :) = x(i, :) + v(i, :); % 边界裁剪:越界的变量直接拉回边界 x(i, :) = max(x(i, :), lb); x(i, :) = min(x(i, :), ub); % 计算适应度 cost(i) = fitness(x(i, :), data); % 更新个体最优和全局最优 if cost(i) < pbestCost(i) pbest(i, :) = x(i, :); pbestCost(i) = cost(i); end end [~, idx] = min(pbestCost); if pbestCost(idx) < gbestCost gbest = pbest(idx, :); gbestCost = pbestCost(idx); end costHis(iter) = gbestCost; end

这里有一个细节想提醒:v(i, :)的初始化不应该全为0,应该用随机数初始化,否则迭代初期所有粒子的位置更新完全依赖pbest和gbest的引导,搜索多样性不足。随机初始化速度可以让粒子在初期就有足够的随机探索能力。

3.3 适应度函数与罚函数的设计陷阱

适应度函数是整个PSO的灵魂。我的设计思路是先算目标函数值(三类成本之和),再检查等式约束的违反程度,最后用一个较大惩罚系数叠加到适应度上。

核心逻辑如下:

function f = fitness(x, data) % 拆解粒子 T = data.T; Pdg1 = x(1:T); Pch1 = x(T+1 : 2*T); Pdis1 = x(2*T+1 : 3*T); P12 = x(3*T+1 : 4*T); Pdg2 = x(4*T+1 : 5*T); Pch2 = x(5*T+1 : 6*T); Pdis2 = x(6*T+1 : 7*T); % 目标:总成本 fuel1 = data.a1 * Pdg1.^2 + data.b1 * Pdg1 + data.c1; fuel2 = data.a2 * Pdg2.^2 + data.b2 * Pdg2 + data.c2; battCost = 0.05 * sum(Pch1 + Pdis1 + Pch2 + Pdis2); gridCost = data.price .* (data.Pgrid1 + data.Pgrid2); totalCost = sum(fuel1) + sum(fuel2) + sum(battCost) + sum(gridCost); % 功率平衡惩罚 bal1 = Pdg1 + data.Ppv1 + Pdis1 - Pch1 - data.PL1 - P12; bal2 = Pdg2 + data.Ppv2 + Pdis2 - Pch2 - data.PL2 + P12; penalty = 5000 * (sum(bal1.^2) + sum(bal2.^2)); f = totalCost + penalty; end

罚函数系数5000是怎么定的?我是从1000开始试,发现1000太小时粒子经常停在约束违反较大的区域,适应度值虽然不高但对应的方案工程上完全不可行;试到5000之后,功率平衡误差基本被压在0.01kW量级,可以接受了。再往大调到20000,虽然约束更严格,但目标函数和惩罚项之间的数值差距太悬殊,会让算法陷入“只要约束满足就行”的状态,反而弱化了对成本的优化。这个平衡点每个算例都不同,建议用参数扫描的方式快速找,也就是跑50次迭代看约束违反量是否收敛。

还有一个常见陷阱:SOC递推约束不能靠简单的边界裁剪处理,因为SOC是一个跨时段累积的变量。我在代码里没有把SOC直接作为决策变量,而是把Pch和Pdis作为决策变量,然后通过递推公式计算SOC序列,最后判断SOC是否在0.2到0.9之间、终值是否接近初始值0.5。这两个约束同样纳入惩罚函数。这样做的好处是保证蓄电池的充放电行为在时间上是连贯的,不会出现前后时段脱离物理规律的跳变。

4. 仿真参数设定与运行结果分析

4.1 算例参数与负荷曲线准备

为了让结果有说服力,我把具体参数整理成了一张表。微网1的负荷曲线以工业负荷为主导,日中有一个明显高峰,峰值约120kW,出现在10点和15点前后;夜间负荷低谷约40kW。光伏出力从6点开始爬升,12点到14点达到峰值60kW,16点后快速下降。微网2的负荷相对平稳,峰值只有90kW,但没有光伏,完全依靠柴油发电机、蓄电池和与微网1的交互功率来维持平衡。

蓄电池参数方面,微网1配置200kWh容量,充电效率0.95,放电效率0.95,最大充/放功率50kW,SOC初始值0.5,运行范围设定为0.2到0.9。微网2配置150kWh容量,其他参数相同。联络线容量30kW,方向为微网1向微网2送电。

这里补充一个关于“终端SOC约束”的经验细节。如果只要求SOC在0.2到0.9范围内,而不限制终端值,那么粒子群算法通常会倾向于把蓄电池在最后时段全部放空,因为这样可以减少成本,即使调度的物理含义最终会让系统无法进入“第二天”。为了得到可持续的调度策略,我在罚函数中增加了终端SOC偏差惩罚项:|SOC(24) - 0.5| * 10000。这样优化出的结果就不会出现“半夜疯狂放电”这种纯数学上省钱、工程上离谱的调度。

4.2 收敛曲线与调度结果解读

粒子群参数我设置为:种群规模80,最大迭代200次。运行结束后,收敛曲线显示总成本从初始的约12000元快速下降,前50代下降幅度最大,到第120代左右接近稳定,最终收敛值约为7350元。这里提一下为什么要看收敛曲线:如果曲线在100代之后还在明显下降,说明迭代次数不够;如果前20代就完全平稳,可能是种群规模太小或者惯性权重衰减太快,导致过早收敛。理想的收敛曲线应该是前期陡降、后期平缓,像一条指数下降曲线。

再看调度结果,规律非常清晰。蓄电池的SOC整体呈现“夜间充电、白天放电”的形态:在凌晨电价低谷期,两个微网都会尽量买电给蓄电池充电;到了白天负荷高峰,蓄电池放电支撑负荷,减少高价购电量和柴油机出力。微网1因为光伏在午间大发,会有明显的功率外送时段——中午12点到14点间,P12稳定在25kW左右,把多余的光伏电送给微网2,替微网2省下了柴油燃料成本。

柴油发电机方面,微网1的机组出力在晚高峰时段上升到接近上限90kW,微网2的机组反而不需要满发,因为相当一部分负荷由微网1输送过来的功率支撑。这就直观体现了多微网协调的经济价值:让成本更低的机组多发电,在整个系统范围内实现发电资源的优化配置。

4.3 结果可视化:怎么从曲线中读信息

我最后用subplot画了四张子图。第一张是收敛曲线,确认迭代过程正常。第二张是两个微网蓄电池的SOC曲线,可以看到SOC的24小时时序变化没有出现剧烈跳变,也没有超限,说明约束处理是有效的。第三张是两个微网柴油发电机的出力曲线,重点看是否满足上下限约束。第四张是P12交互功率曲线,可以直观看到微网间功率流动的时间分布。

画图其实也是排查结果的重要手段。有一次我发现SOC曲线在某几个时段出现从0.9直接掉到0.5的陡降,当时第一反应是放电功率没有限幅,结果排查下来发现是SOC递推公式里充放电效率用反了,导致同样功率下SOC变化量失真。所以我的建议是:写完模型之后先把SOC曲线画出来,看一眼波形是否平滑、是否符合常识,这种可视化检查比单纯看适应度函数值可靠得多。

5. 常见问题与调试心得

5.1 结果不合理时的七个排查方向

如果跑完发现调度结果不符合物理常识,我最常用的排查顺序是:先检查维度拆解是否正确,再看单位是否统一,接着检查功率平衡约束的方向符号,然后排查罚函数系数是否合适,随后检查SOC递推效率和方向,再检查边界裁剪是否覆盖所有变量,最后检查惯性权重参数是否合理。

这里给一张我自己常用的问题速查表:

现象可能原因处理方式
收敛曲线震荡剧烈不下降惯性权重过大设置wMax为1.2,wMin为0.4,线性递减
发电机出力在某时段长期贴在上限罚函数系数过小增大功率平衡惩罚系数
SOC曲线全程贴上限或下限终端SOC惩罚缺失增加SOC终值偏差惩罚项
微网交互功率忽大忽小P12缺少平滑约束可加相邻时段变化量惩罚,或降低联络线容量
目标函数值很小但方案不可行罚函数过小观察功率平衡误差,调整惩罚系数
几次运行结果差异很大随机初始化导致局部最优固定随机种子,或增大种群规模
三相功率曲线出现负值变量维度错位检查索引范围与变量结构对应关系

5.2 我特别想强调的调试经验

做这个项目的过程中,最让我意识到的一件事是:粒子群算法的调参并不是一步到位的。我一开始把wMax和wMin设置为0.9和0.4,结果收敛到后期粒子几乎停住,gbest还在一个明显不是最优的位置。后来改成1.2到0.4的递减配置,加上随机初始速度,收敛效果明显改善。这个参数确实需要根据问题维度调整——维度越高,初期探索速度越重要。

另一个经验是关于“先单微网、后多微网”的调试策略。不要一上来就调两个微网的完整案例,那样问题出在任何一环都不容易定位。我是先在单微网结构下把目标函数、SOC模型和PSO主循环调试通过,确认曲线合理之后,再复制出一套微网2的参数,把交互功率变量加进去,重新跑。这种方式排查问题时工作量最小,因为你知道单微网那部分已经没有原则性错误,出了问题大概率出在交互功率变量或两个微网之间的符号约定上。

5.3 后续还能怎么扩展

这个模型本身的扩展空间很大。比如把2个微网扩展到5个或者更多,决策变量的维度会线性增长,PSO的搜索压力也相应变大,这时可以考虑用分阶段优化或者引入分布式求解框架。或者把日前的单目标优化改成考虑碳排放的多目标优化,用多目标粒子群算法得到Pareto前沿。另外,如果追求更贴近工程实际,可以把蓄电池的循环寿命老化模型做得更细,把柴油发电机的爬坡约束也加入罚函数。

根据我个人经验,做这类仿真项目最核心的能力不在于会调用现成的算法函数,而在于能把一个复杂的工程问题用数学语言清晰地描述出来,再通过数值实验反推验证模型逻辑是否正确。多微网优化调度这个题目恰好涵盖了新能源出力建模、储能运行约束、多主体协调机制和智能优化算法应用,是一个非常值得认真做一遍的综合性算例。最后分享一个小技巧:哪怕你的目标只是完成课程设计,也建议把成本曲线、SOC曲线、交互功率曲线都仔细画出来看一遍——看懂这些曲线的物理含义,比调通代码本身收获大得多。

返回列表