含风光燃储的微网优化调度做优化的人绕不开粒子群算法PSO。这个组合在大学课题、工程预研甚至生产调度系统里都非常常见核心逻辑就一句话把“如何分配光伏、风电、燃气轮机、储能的出力”这个问题转成一个带约束的数学优化问题然后用粒子群算法去找一个足够好的解。很多人会纠结是不是该上深度学习、强化学习但实际工程里粒子群算法简单、上手快、可解释性强在中小规模调度场景下非常能打。这篇东西我就从建模、算法原理、代码实操到踩坑复盘把整套链路讲透适合正在做相关课题的学生、刚接触微网调度的工程师以及想快速验证调度策略的研发人员。1. 调度问题建模从物理场景到数学描述先别急着写代码任何事情第一步应该是把物理场景翻译成数学模型。这一步做不扎实后面算法再花哨也是白搭。1.1 微网结构描述“含风光燃储”四个字点明了微网里的四大类电源/储能设备光伏PV、风电WT、燃气轮机MTMicro Turbine或Gas Turbine、储能系统ESS通常指电池。有时还会加一个“主网交互”的出口因为微网往往并联在配电网中可以从电网买电也可以反送卖电但这一块要看具体项目边界。典型拓扑是这样的所有设备都接在同一个交流母线上光伏和风电是间歇性电源出力取决于天气和风速属于“不可控”或“半可控”电源燃气轮机是可控机组可以设定出力储能电池既能充电又能放电本质上是一个时间耦合的灵活资源。负荷侧则是必须满足的用电需求。调度问题的目标就是在未来一段时间内通常以24小时为周期时间步长1小时或15分钟决定每个可控单元每个时段的出力计划使得整个微网在某一个目标下运行最优。需要注意的是调度是分时间尺度的。我们这里讨论的是“目前调度”或“日内滚动调度”通常假设预测数据已经给出比如光伏功率预测曲线、风电功率预测曲线、负荷预测曲线。这本身就有误差但先把确定性调度做明白再谈不确定性鲁棒优化。1.2 目标函数怎么设目标函数决定了“最优”的定义。最常见的目标是系统总运行成本最小包括燃气轮机的燃料成本通常用二次函数拟合C_MT a * P^2 b * P c其中P是燃气轮机出力kWa、b、c是成本系数。储能充放电的折旧损耗成本可以建模为一个单位电量损耗系数乘以充放电功率。向外部电网购电的成本电价按峰谷分时或实时市场电价。如果需要绿色属性还可以加入碳排放惩罚项把CO2排放量乘以一个碳价系数折进成本。有些项目也会把“弃风弃光惩罚”加入目标函数——光伏和风电出力是免费的如果因为调度原因没消纳掉会产生浪费可以在目标函数里加一个很大的惩罚权重促使算法尽量多利用可再生电力。举一个我常用的目标函数表达式min F sum_t [ C_MT(P_MT(t)) C_ESS(P_ESS(t)) C_grid(P_buy(t)) - C_sell(P_sell(t)) λ_curtail * (P_curtail_PV(t) P_curtail_WT(t)) ]其中C_grid是购电成本C_sell是售电收益λ_curtail是弃电惩罚系数。这里有个细节售电收益在目标函数里是负的成本即收益。如果地方政策允许反送电这个要算进去。也可以做多目标优化比如同时最小化成本和最大化可再生能源利用率然后用权重系数加权或者用多目标粒子群MOPSO。不过我在实际课题里通常先做单目标把约束摸清楚再扩展避免一开始就陷入多目标帕累托前沿的泥潭。1.3 约束条件有哪些约束条件决定了解的可行域任何智能优化算法都必须在可行域内搜索。微网调度常见的约束如下功率平衡约束每个时段光伏出力 风电出力 燃气轮机出力 储能放电功率 购电功率 负荷功率 储能充电功率 弃电功率。这个约束必须严格满足因为电网系统实时平衡是物理铁律。我用公式写一下P_PV(t) P_WT(t) P_MT(t) P_ESS_discharge(t) P_buy(t) P_load(t) P_ESS_charge(t) P_curtail_PV(t) P_curtail_WT(t)。燃气轮机出力上下限约束P_MT_min ≤ P_MT(t) ≤ P_MT_max即每台机组有最小技术出力防止熄火和最大额定功率。另外还有爬坡约束-ramp_down ≤ P_MT(t) - P_MT(t-1) ≤ ramp_up燃气轮机从一个时段到另一个时段的功率变化不能太快否则机械和燃烧系统跟不上。储能约束储能的状态SOC随时间演变SOC(t1) SOC(t) η_charge * P_ESS_charge(t) * Δt / E_capacity - P_ESS_discharge(t) * Δt / (η_discharge * E_capacity)。同时SOC要保持在安全范围比如0.2到0.9之间充放电功率也要有上限0 ≤ P_ESS_charge(t) ≤ P_charge_max0 ≤ P_ESS_discharge(t) ≤ P_discharge_max。注意充放电不能同时进行这个约束可以用二进制变量处理也可以靠算法限制具体后面讲。弃风弃光约束弃电功率必须大于等于0并且小于等于当期最大可消纳的功率。其实弃电功率本身就是松约束只要目标函数里惩罚足够大算法会自动把弃电压到0。但如果惩罚不够就可能出现舍近求远白白弃掉可再生电力的现象。主网交换约束如果允许从电网购电通常有最大购电功率限制P_buy_max和最大售电功率限制P_sell_max取决于并网线路的容量。建模时我常犯一个错误只写功率平衡忘了写SOC的时间耦合。储能是一个“跨时段”的单元它的最优策略天然带有动态规划的味道这也是为什么有些研究者喜欢把调度问题写成动态规划或者模型预测控制MPC。但粒子群算法也能处理只要把SOC守恒关系作为约束塞进适应度函数中即可。2. 粒子群算法原理与选型理由建模完成之后摆在面前的是一个带约束的非线性混合整数优化问题。求解方法很多线性规划、内点法、混合整数规划比如用Gurobi、Cplex都能解为什么还要用粒子群算法这里我得说句实话如果问题规模不大且约束都是线性的直接用商业求解器效果更好、更稳定。但粒子群算法适合三种情况一是目标函数非线性强、甚至不可导比如加入了分段惩罚项、阶梯电价二是想快速验证方案或做算法横向对比三是对手写优化代码、理解算法细节有需求毕竟自己实现一遍能学到很多东西。2.1 算法核心公式粒子群算法的灵感来自鸟群觅食。每一个粒子代表解空间中的一个候选解即一组决策变量的取值。粒子会不断调整自己的位置调整依据是个体历史最优位置pbest和群体历史最优位置gbest。位置更新的标准公式是v_i(k1) ω * v_i(k) c1 * r1 * (pbest_i - x_i(k)) c2 * r2 * (gbest - x_i(k))x_i(k1) x_i(k) v_i(k1)其中v是速度x是位置即解向量ω是惯性权重c1是认知学习因子c2是社会学习因子r1、r2是[0,1]之间的随机数。速度公式的物理意义很好理解第一项是“惯性”代表上一时刻运动状态的延续防止方向突变第二项是“个体经验”把粒子拉向自己曾经找到过的好位置第三项是“群体经验”把粒子拉向整个种群找到过的最优位置。算法迭代若干次之后粒子逐渐汇聚到最优解附近。为了方便理解你可以把位置想象成在一个山谷里找最低点的登山者每个登山者都有GPS记录自己走过的最低点也有一个对讲机随时同步大家的最低点坐标然后不断调整自己的脚步方向和步长。惯性权重相当于“习惯”个体经验相当于“直觉”群体经验相当于“情报”。2.2 为什么选PSO做调度微网调度问题的特点是维度适中比如24小时调度每个时段3个可控变量燃气轮机出力、储能充放电功率或者SOC、购售电功率再算上弃电变量总维度大概在100个左右。这个量级粒子群算法处理绰绰有余。另一个优势是粒子群算法对初值不敏感不需要求导可以直接用数值方法计算适应度。微网调度中经常有设备启停状态这种离散变量手动编码时可以把离散状态作为0/1变量放进粒子位置或者用连续变量加阈值转换的方式处理很灵活。相比遗传算法GA粒子群算法没有交叉变异那些算子实现起来简单得多收敛速度也快一些特别是在中等维度问题中。相比模拟退火粒子群有群体并行搜索能力不容易卡在某个局部区域。当然它的缺点也很明显容易早熟收敛在复杂约束下可能最终解不可行。这些我后面讲处理方法。2.3 关键参数的意义与选择粒子群算法的参数不多但每个参数都很敏感。根据我长期实战的经验参数设置如下种群规模N通常取20到50。维度达到100时我建议取40到60个粒子。太小搜索不充分太大计算量翻倍且收敛慢。最大迭代次数T一般取100到500。观察收敛曲线如果适应度值在80次之后不再下降就说明已经收敛可以提前终止。惯性权重ω线性递减是业界最稳妥的做法从0.9降到0.4。前期ω大粒子“飞”得快探索全局后期ω小局部精细搜索。简单写法ω ω_max - (ω_max - ω_min) * k / T。学习因子c1、c2经典取c1c22。后来有研究者建议c1从2.5降到0.5c2从0.5升到2.5以加强前期的个体探索和后期的群体收敛。我实测下来差别不大固定取2.0就够用。速度上限v_max防止粒子飞出去。一般设为决策变量范围的10%-20%。比如燃气轮机出力范围0-1000kW速度上限就设为100-200kW/次。也有人再乘一个随机扰动但没必要。约束处理策略直接丢弃越界粒子会导致种群多样性迅速下降最好采用“边界吸收重新初始化”混合策略。位置越界就拉到边界如果连续n次越界则在该变量范围内重新随机初始化给粒子一次“重飞”的机会。3. 实操过程编码、适应度与迭代计算理论说完了现在进入“抄作业”环节。我以一个24小时调度问题为例时间步长1小时负荷、光伏、风电数据已经预测好燃气轮机有一台储能有一台可以和电网交互但为了突出微网自平衡能力购电功率作为备用变量也可以开放。下面把编码、适应度函数和迭代过程完整走一遍。3.1 决策变量编码设计把每个时段的决策变量按顺序拼成一个向量这个向量就是粒子的位置。我习惯的编码顺序是先燃气轮机出力24个变量再储能充放电功率24个变量再购电功率24个变量一共72个变量。如果储能充放电是同一个变量用正数代表放电功率负数代表充电功率那么还需要在适应度函数里把“同时充放”的违规消除。这种编码简单但容易产生不现实的解所以我在编码上做了更严谨的改进燃气轮机出力P_MT(1..24)范围[P_MT_min, P_MT_max]储能功率P_ESS(1..24)范围[-P_charge_max, P_discharge_max]正数放电、负数充电购电功率P_buy(1..24)范围[0, P_buy_max]弃电变量不直接编码而是利用功率平衡方程式反解出来这样能天然保证功率平衡。风电和光伏是已知的输入数据不参与编码。每次迭代粒子位置对应上述变量取值然后代入约束校验和适应度计算。这里有个关键设计如果用功率平衡反解弃电功率那么弃电功率必须是正值如果反解出来是负值说明供电缺口需要用购电或储能来弥补但我们已经显式编码了购电变量怎么处理我的做法是在适应度函数中检查平衡关系如果不满足则直接罚一个非常大的值同时尝试把功率平衡约束转成等式约束穿透处理。具体在后面小节解释。3.2 适应度函数封装适应度函数是PSO的“裁判”它接收一个粒子位置返回一个数值数值越小代表方案越优。我把它设计成“总成本 惩罚项”Fit(x) F_total(x) w1 * sum(约束违返量) w2 * sum(约束违返量^2)其中约束违返量指的是每个约束的违反程度比如储能SOC越界多少、燃气轮机爬坡越界多少。使用线性和平方结合的惩罚是为了让算法在接近可行域时获得梯度指引。具体实现为从粒子位置中取出P_MT、P_ESS、P_buy。计算每个时段的功率平衡残差res(t) P_PV(t)P_WT(t)P_MT(t)discharge(t)P_buy(t)-P_load(t)-charge(t)。 注意P_ESS如果为正代表放电为负代表充电所以net_ESS max(0,P_ESS) - max(0,-P_ESS)代入平衡式。 弃电功率P_curt(t) res(t)如果res(t) 0则违反功率平衡增加惩罚。计算燃气轮机成本总和储能损耗成本按充放电功率绝对值乘以单位损耗系数购电成本按分时电价。利用储能SOC递推公式计算每个时段SOC检查是否越界。同时加入最终SOC要回到初始SOC附近的约束比如SOC末值等于初值或者在一个小范围内这是为了满足调度周期可重复性否则电池一直放电到下一天会没电。计算燃气轮机爬坡约束违反量。返回总成本加所有惩罚。这里我踩过一个坑如果惩罚系数设置过小算法会优先降低成本而忽视约束最终给出的“最优解”根本不可行SOC跑到了1.5或者功率平衡残差大到离谱。惩罚系数也不能太大太大则粒子一旦越界就被拽得很远导致搜索空间极度扭曲难以过渡到可行区域。我的经验是惩罚系数随着迭代次数逐步增大初期让粒子“飞”出去探索后期强拉回可行域。或者直接在迭代后期丢弃不可行解用可行解的适应度排序。3.3 完整计算流程与伪代码整个算法流程如下输入基础数据负荷、光伏、风电预测曲线设备参数电价算法参数。初始化种群随机生成N个粒子位置和速度确保位置在上下限内计算每个粒子的适应度初始化pbest和gbest。进入主循环对每一个迭代步更新惯性权重线性递减。对每个粒子产生随机数r1、r2按速度公式更新速度按位置公式更新位置。对位置进行边界处理。计算新适应度更新pbest如果新适应度小于该粒子的pbest则更新。更新gbest选取所有粒子pbest中最小的作为gbest。判断终止条件达到最大迭代次数或者适应度值连续50次变化小于阈值如1e-6。输出gbest解码为调度计划。伪代码如下for k in range(max_iter): w w_max - (w_max - w_min) * k / max_iter for i in range(n_particles): r1 random.random() r2 random.random() velocity[i] w * velocity[i] c1 * r1 * (pbest[i] - position[i]) c2 * r2 * (gbest - position[i]) velocity[i] clip(velocity[i], -v_max, v_max) position[i] velocity[i] position[i] boundary_clip(position[i], lower_bound, upper_bound) fitness[i] calc_fitness(position[i]) if fitness[i] pbest_fitness[i]: pbest_fitness[i] fitness[i] pbest[i] position[i].copy() if min(pbest_fitness) gbest_fitness: gbest_fitness min(pbest_fitness) gbest pbest[argmin(pbest_fitness)].copy()特别提醒粒子位置里包含三组变量它们的物理量纲不同MW、kWh、元/kWh没关系但要归一化处好。如果燃气轮机出力在1000kW量级储能功率在500kW量级购电功率在2000kW量级直接混在一个向量里计算速度会让大数值变量主导搜索。所以我在编码前统一将每个变量归一化到[0,1]迭代完后再反归一化回实际值。这样做还能为不同变量设置不同的v_max。3.4 参数计算示例我以一套具体参数展示计算过程方便你复现时对数量级有感觉。假设微网参数如下燃气轮机最大出力1000kW最小出力100kW爬坡限制200kW/h成本系数a0.0006元/kW^2hb0.4元/kWhc10元/h。储能容量2000kWh最大充/放电功率500kW充放电效率均为0.95SOC初始0.5范围[0.2,0.9]损耗成本0.02元/kWh。电网购电功率上限800kW分时电价峰段9-1217-211.2元/kWh平段8-912-1721-230.8元/kWh谷段23-80.4元/kWh。光伏预测白天0-200kW曲线风电预测0-300kW曲线负荷预测200-800kW曲线。总成本大致量级燃气轮机一天如果发8000kWh成本约4000-5000元加上购电成本能到6000元。粒子群优化后的成本会比“光伏发多少用多少、电网补缺”这种简单策略低10%-20%主要省在利用谷电给储能充电、峰时放电以及用燃气轮机替代峰段高价购电。参数设置种群N60迭代最大300次ω_max0.9ω_min0.4c1c22v_max设为每个变量范围宽度的15%。我用Python写了不到200行在普通笔记本上跑完300次迭代大约10秒左右24小时调度完全够用。你可以用同样的参数跑一下收敛曲线基本在150次之后就平了。4. 常见问题与排查技巧实录这部分是我最想分享的因为学术论文里很少写但实际跑起来全是坑。4.1 过早收敛与局部最优现象迭代到四五十次时gbest不再变化最后的调度计划不理想比如储能完全没有在谷电时段充电或者燃气轮机一直满出力。原因多半是种群多样性丢失。排查方法先看粒子的位置分布。如果大部分粒子都挤在同一个区域说明搜索空间收窄太快。解决办法有三个增大惯性权重上限从0.95开始降。加入扰动机制每隔一定迭代次数随机选择几个粒子重新初始化位置。使用“子群”思想把种群分成几个小组每组独立搜索一定代数后交换gbest比如每次迭代让小组用不同的ω0.7、0.9增加多样性。我实测最直接有效的是第一种ω_max改成0.95同时速度上限提高到变量范围的20%就明显缓解了早熟。4.2 等式约束如何处理功率平衡约束是等式约束不同于不等式约束粒子群算法基于随机搜索很难天然落在等式约束的超平面上。我的处理策略是“变量消去法”将弃电功率作为冗余变量用功率平衡反解这样等式约束严格满足只检查弃电功率是否非负。这是最优雅的办法。但如果弃电功率反解出来为负表示功率不足需要切负荷此时检查该时段购电上限和储能放电上限若仍不够则罚掉。具体到代码计算res P_load(t) - P_PV(t) - P_WT(t) - P_MT(t) - P_ESS_discharge(t) - P_buy(t) P_ESS_charge(t)如果res0则本时段弃电量为res惩罚0如果res0说明等式被破坏惩罚值abs(res)*1e5。这样算法知道哪些粒子更接近可行域。另外还有一类方法叫“约束支配比较”也就是把违反约束的量排在第一优先级让不可行解之间的比较只依据违反量可行解依据目标值。我在处理含启停状态的混合变量问题时用过效果也还行。你可以试一下。4.3 储能SOC越界问题储能SOC是一个积分量调度24小时即使单个时段的功率都在限内叠加起来也可能SOC超上限或跌破下限。如果在适应度函数里直接设罚函数算法可能永远找不到完全可行解尤其在约束严格时。我采用的方法在适应度计算中对SOC越界量施加很大的平方惩罚同时在每次迭代结束前做一个“SOC修正”操作。具体来说若某一时刻SOC预计会低于下限则强制降低该时段的放电功率或增加充电功率到恰好满足下限若会高于上限则强制提高放电或降低充电。这个修正操作会让粒子位置偏离原始编码值但修正后的位置更接近可行域作为下一代粒子的初始位置反而是好事。还有一种稳妥做法不直接编码P_ESS而是编码每个时段的净功率变化量通过累加才能得到SOC。更能控制SOC变化但会让解码复杂化。两利相权我现在更推荐在适应度层做修正简单可维护。4.4 算法参数调参经验汇总我做过的十几个微网调度项目参数敏感性也就那几点种群规模少于30基本不稳建议50以上超过100收益很小。惯性权重线性递减比固定值好几乎无例外。学习因子不需要特别调2.0没问题如果发现震荡厉害c1降到1.5、c2升到2.5。速度上限是最容易出问题的参数设置过小会搜索不到边界外的好解设置过大会越过最优解不断震荡。我的经验是设置为变量范围的10%-20%然后观察收敛曲线如果收敛太快就调高到20%如果一直不收敛就调低到8%。再分享一个实用小技巧每次跑完算法把gbest解码后的调度计划画成一张24小时功率分配堆叠图一眼就能看出“储能是否在谷电充电、峰电放电”“燃气轮机是否频繁爬坡”“购电是否避峰就谷”。如果图形不合理说明约束或者算法还有问题。5. 我的个人体会与扩展建议粒子群算法做微网优化调度这条路说到底是“数学模型优化工具”的组合真正的工作量集中在建模和约束处理上算法本身反而是最不费心的地方。我在实际项目中感受最深的一点是如果约束靠惩罚函数硬凑算法收敛后的解总带着“病态特征”比如某个时段弃电特别多不是因为可再生能源过剩而是因为储能SOC刚好卡在下限燃气轮机又有爬坡限制找不到可行出路——这时候别急着调粒子群参数回到约束模型里看看是不是漏掉了某个柔性的自由度比如允许储能微小的SOC松弛、增加一个辅助调节电源。模型合理了算法自然也就好用了。如果你想在这个基础上继续扩展可以试试三个方向一是把确定性调度升级成鲁棒优化用风电光伏的预测误差区间生成多个场景粒子群可以逐场景计算期望成本二是加入启停变量把燃气轮机的开机、停机决策也放进粒子编码这就变成混合整数问题粒子群用连续变量映射到离散值也能跑三是做多目标粒子群MOPSO把成本最小化和碳排放最小化同时考虑输出帕累托前沿再用模糊隶属度函数挑一个折中解。这些方向我都实际跑通过粒子群算法在微网调度领域并不是过时货它简单、透明、易改作为项目起步和验证工具很值得留在工具箱里。最后再分享一个小经验写调度程序时一定要把数据读取、模型参数、结果输出分开模块化。我最初把所有代码揉在一起后来换了一组设备参数调了整整一下午才理清头绪。把微风预测数据、电价表、设备上下限都放在独立的配置文件里粒子群主程序只负责计算项目的可维护性会好很多。这不算技术亮点但真的是保证你在多轮迭代后还能保持头脑清醒的关键。