1. 项目到底在做什么:从“三个微网”说起
先说结论:这个项目研究的是三个彼此独立、又能通过联络线互相交换功率的微电网,在同时追求“运行成本最低”和“碳排放最少”这两个目标时,如何安排各自内部的分布式电源出力、储能充放电计划以及与相邻微网之间的交换功率。
微网这个东西,本质上是一个小型的发配电系统。里面通常有光伏、风机这类可再生能源,也有微型燃气轮机、燃料电池这类可控机组,还配了储能电池。单微网调度这些年已经被研究得很透了,但现实中很多微网是地理上相邻的,比如同一个园区里的三个厂区、同一个社区里的三个楼宇群。它们各自为政的时候,光伏大发时段自己消纳不完只能弃掉,晚高峰缺电了又得高价从大电网买。如果允许它们之间通过联络线互相支援,就能把“弃掉的电”送给“缺电的邻居”,从整体上看既省钱又减碳。
这个项目做的,就是把三微网之间的互联功率也纳入优化变量,在一个统一的优化框架里同时决定每个微网内部的机组出力和微网之间的交换功率。有人可能会问,那直接三个微网各自独立优化不就完了吗?实测下来差别很大。独立优化时系统总成本可能偏高,因为每个微网都要自求平衡,储能备用量冗余;互联优化后,整体成本能降一截,光伏弃光率也明显降下来。这背后就是“互联互助替代孤岛自保”的核心逻辑。
适合读这个项目的人,我建议是以下几类:正在做微网、综合能源系统方向毕业设计或课程项目的研究生;刚入门优化调度、想找一个完整Matlab算例练手的人;以及做园区级能源管理、想了解多主体协调调度怎么落地的工程师。代码本身并不复杂,但里面涉及的目标函数建模、约束处理、多目标加权、粒子群求解,每一步都有值得琢磨的细节。
2. 数学模型完整拆解:目标函数、约束条件与变量设计
做优化调度,第一步永远是把问题“翻译”成数学语言。这个翻译做得好不好,直接决定后面代码能不能跑出合理结果。
2.1 目标函数怎么定:经济成本与碳排放的博弈
这个项目的目标函数是两个:总运行成本和总碳排放量。先说经济部分。
经济成本通常包含四块:
- 燃料成本:微型燃气轮机、燃料电池消耗天然气或氢气的费用,一般表示成输出功率的二次函数,也就是
C_fuel = a * P^2 + b * P + c这种形式。实际代码里很多人图省事用线性近似,精度也够用,但二次函数更贴近真实机组特性。 - 运行维护成本:机组每发一度电,按照单位运维成本累加,光伏、风机、储能也有运维成本,但一般比可控机组低一个数量级。
- 向大电网购电成本:从主网买电的价格,有时候分时电价,峰谷电价差很大。这个对调度结果影响很大。
- 储能退化成本:电池充放电循环是有寿命损耗的,严格来说应该算进去。很多入门级代码会忽略这一项,但从工程实践看,如果储能一天充放两三个完整循环,退化成本不容忽视。
再说碳排放。碳排放的来源主要是微型燃气轮机和从大电网购电。燃气轮机烧天然气,单位发电量对应的碳排放系数可以从文献查到,比如每千瓦时0.2千克左右,具体数值各文献略有差异。从大电网购电的碳排放系数则取决于主网的电源结构,火电占比高的区域,系数就高,比如每千瓦时0.5到0.8千克。
这两个目标合在一起怎么算?常见做法是加权求和,把碳排放量乘上一个碳价系数,折算成经济成本,然后加到总成本里。碳价的设置很有讲究,设置太高,系统会不惜一切代价减少碳排放,储能和可控机组的运行方式都会很激进;设置太低,碳排放目标形同虚设。一般文献里碳价取每吨几十到几百元,具体看研究对象。
2.2 约束条件逐条讲透
约束条件是调度模型里最容易出错的地方。少了约束,优化结果会跑到物理上根本不可能实现的点;约束写得太紧,又可能导致无解。
需要纳入的约束主要有以下几类:
- 功率平衡约束:每个微网内部,所有电源出力加储能放电功率加互联受入功率,必须等于负荷加储能充电功率加互联送出功率。这条约束在每个时段都要满足,是硬约束中的硬约束。
- 机组出力上下限约束:每个可控机组都有最小稳定出力和最大出力,不能超出。光伏和风机则受预测功率限制,实际出力不能超过预测值,相当于把预测误差的余量留出来。
- 储能约束:包括荷电状态(SOC)上下限、充放电功率上下限、以及充放电不能同时进行的逻辑约束。SOC的动态方程是
SOC(t+1) = SOC(t) + P_charge * η_charge * Δt / E_cap - P_discharge * Δt / (E_cap * η_discharge),这个式子每个时段都要更新。 - 联络线功率约束:两个微网之间交换的功率不能超过联络线的传输容量极限,比如5兆瓦或10兆瓦,视具体场景而定。而且交换功率通常设置成双向的,功率值为正表示从A送到B,负值表示反方向。
- 碳排总量约束(可选):如果不想用加权法,也可以把碳排放设成硬约束,比如“系统24小时碳排放不得超过某某吨”,这样目标就退化成单纯的经济优化加一个排放上限。
2.3 互联功率变量与共享机制的建模
多微网互联调度的核心建模难点在于联络线功率的处理。三微网之间两两互联,可以组成三条联络线,但实际工程里很少这样全连接,常见的是环形连接或单链形连接。比如微网A和B相连、B和C相连、A和C不直接相连,这种拓扑结构就够用了。
在代码里,联络线功率通常表示为有符号的变量,比如P_tie(i,j,t)表示时段t从微网i流向微网j的功率。为了避免重复计数,一般约定只定义i < j方向的变量,功率为负时表示倒流。另外还要注意,实际物理系统中两条微网之间的功率流动方向是唯一的,不能既从A流向B又从B流向A,所以约束里要限制P_tie(i,j,t) = -P_tie(j,i,t),在编码时定义一个变量就够了。
3. Matlab代码整体架构与实现思路
这篇代码整体上不复杂,但如果一上来就逐行读,容易被绕晕。我建议先看模块划分,理解数据流动的方向,再往细处看。
3.1 代码模块划分与数据流
整个程序可以拆成五个模块:
- 数据初始化模块:定义各微网的负荷曲线、光伏风机预测出力曲线、机组参数、储能参数、联络线参数、电价曲线、碳价等。这部分通常是Excel或Matlab结构体读取,方便改参数复用。
- 决策变量生成模块:把优化问题中的自变量统一编码成一个向量。比如24个时段的机组出力、储能充放电功率、联络线功率,全部按顺序拼接成一维数组。
- 目标函数模块:输入决策变量向量,计算出总成本和总碳排放,再按加权系数合成单目标函数值。这个模块是粒子群每次迭代都要调用的核心部分。
- 约束处理模块:把等式约束和不等式约束写成罚函数项,或直接在粒子群速度更新过程中做边界限制。后一种方式更简单,但对等式约束无能为力,所以实际代码里通常用罚函数。
- 主循环模块:粒子群算法的主体,包括初始化粒子群、迭代更新、收敛判断、结果输出与画图。
数据流很清晰:数据初始化后,生成一组初始终群,每个粒子代表一个完整的24小时调度方案,目标函数模块对它评估优劣,然后粒子群按速度和位置更新公式搜索更好的解,循环直到迭代次数用尽。
3.2 决策变量的编码设计
多微网联合调度的决策变量数量不少。以三微网、每网一台燃气轮机、一台储能、一条联络线为例,24个时段里,每个微网的燃气轮机出力就是24个变量,储能充放电是48个变量(充、放分开计),联络线功率是24个变量。三微网合计就有288个变量,如果再加光伏弃光变量、购售电变量,变量数量更多。
编码顺序要固定,且全程序保持一致。比如统一按“微网内可控机组出力,然后储能充电功率,然后储能放电功率,然后联络线功率,最后是购售电功率”的顺序排下去。这样代码虽然啰嗦一点,但排错的时候很方便,能直接定位到某个变量在向量里的位置区间。
3.3 求解器选择:YALMIP加CPLEX还是启发式算法
这里存在两条技术路线,各有适用场景。
一个是精确求解路线:用YALMIP工具包建模,调用CPLEX或Gurobi求解混合整数线性规划。只要把目标函数和约束线性化处理,求解速度快,解的质量有保证,尤其适合约束多、规模大的问题。
另一个是启发式路线:用粒子群或遗传算法直接搜索。好处是不需要线性化,非线性约束照单全收,代码相对简单,也不依赖商业求解器授权。缺点是结果不保证全局最优,每次运行结果可能有差异,参数调不好就发散或早熟收敛。
这个项目选择了粒子群路线,我推测原因是考虑代码的可读性和可移植性,毕竟很多同学手里没有CPLEX授权。粒子群加罚函数法虽然不是最优解,但对教学和研究探索来说足够用了。后面我会专门讲粒子群调参的坑。
4. 求解算法的选择与参数调优
粒子群算法本身不复杂,三五十行就能实现。但同样一份代码,有人跑出漂亮的调度曲线,有人跑出来完全不符合物理规律,差别基本都在参数设置和约束处理上。
4.1 粒子群核心逻辑与实现要点
粒子群的核心就是两个公式。
速度更新:V_new = w * V_old + c1 * r1 * (Pbest - X) + c2 * r2 * (Gbest - X)
位置更新:X_new = X_old + V_new
这里w是惯性权重,控制粒子保持原有速度的能力;c1是认知系数,把粒子拉向自己历史最优位置;c2是社会系数,把粒子拉向群体最优位置。r1和r2是0到1之间的随机数。
在Matlab里实现要注意几个细节:初始化粒子群的取值范围要贴合变量边界,比如储能充电功率不能超过额定值,如果随机初始化越界太多,后面罚函数会很重,算法会花大量迭代先把粒子拉回可行域。实际做法是初始化时直接用边界范围内的均匀分布随机数,让粒子群一开始就散落在可行域附近。
速度初值也不宜设得太大,否则前几次迭代粒子乱跳,容易跑飞。通常速度上限设为变量上限的20%到30%就够了。
4.2 实战参数设置经验
我调过不少粒子群调度代码,一套比较稳妥的基准参数是:种群规模100,迭代次数500,惯性权重从0.9线性递减到0.4,c1和c2都取1.5。这几个参数不是拍脑袋定的,有文献支持,实际跑下来也稳定。
有一点值得单独说:惯性权重的线性递减很关键。迭代早期权重高,粒子探索范围大,不容易陷入局部最优;迭代后期权重低,粒子收敛到局部精细搜索,提高静态精度。如果你把权重固定成一个常数,比如0.6,多跑几次就会发现结果波动大,有时候能收敛到好解,有时候就差很多。
多目标加权系数怎么设?我建议先单独跑两个极端:只优化经济目标,记录总成本和碳排放;只优化碳排放目标,记录同样的指标。然后把其中一个目标归一化,再用加权法合并。这样至少能保证两个目标的量级匹配,不至于因为碳排放数值小而被完全淹没在成本项里。
4.3 约束处理:罚函数还是修复机制
约束处理是粒子群调度代码里最容易出问题的部分。
纯罚函数法简单粗暴:目标函数里加一项,违反约束越严重罚得越狠。但罚函数系数的选择很微妙,太小则约束形同虚设,太大则目标函数尺度失衡,粒子群被罚项主导,经济目标反而优化不好。
我的做法是混合方案:对不等式约束,比如机组出力上下限和SOC上下限,直接在粒子位置更新时截断到边界内,这叫“边界吸收”;对等式约束,比如功率平衡,没办法简单截断,只能用罚函数。这样大多数变量都被边界吸收处理掉,罚函数只处理功率平衡偏差,系数调起来就轻松很多。
另一个常用技巧是“修复机制”:每次粒子更新位置后,检查各微网的功率平衡,如果某个微网在某时段功率不平衡,把不平衡量按比例分配给附近的可控机组或联络线功率。这本质上是一种启发式修复,能大大提升解的可行率,但修复逻辑写起来要小心,别修复完这个约束又破坏了另一个约束。
5. 算例设计与结果解读
代码跑起来之后,怎么判断结果对不对?怎么从一堆曲线里读出有意义的信息?这是很多人拿到代码后最困惑的部分。
5.1 典型算例场景设置
我建议先设置一个基础场景:三微网互联,拓扑结构为链式连接,微网A和B相连,B和C相连,A和C不直接相连。每个微网都配置光伏300千瓦、储能容量500千瓦时、微型燃气轮机200千瓦。典型日负荷曲线取三条错峰曲线,体现不同微网负荷特性差异——比如A微网白天负荷重(类似商业负荷),B微网晚间负荷重(类似居民负荷),C微网比较平稳。这样设置的目的是让互联功率有充分的交换动机。
光伏出力曲线用典型的单峰曲线,正午时分达到峰值300千瓦,早晚为零。分时电价采用峰谷电价,峰时段每千瓦时1.2元,谷时段0.4元,平时0.8元,这样可以检验储能“低充高放”的逻辑是否合理。
碳价先取每吨50元,这是理解目标函数权重的关键。碳价如果设成0,碳排放项完全失效;设成200元以上,结果会明显偏向低碳方案。实际上从0到200逐步扫描碳价,观察系统碳排放与总成本的变化趋势,就能得到一条很有意义的帕累托趋势线,这个后面会说。
5.2 结果曲线的读法:从数值验证到趋势分析
跑出结果后,最重要的验证是看功率平衡是否成立。取任意一个时段,把该时段所有出力加起来,减掉负荷和储能充电,结果应该等于联络线净交换功率。这个验证代码里应该写一个独立的检查函数,我建议放在程序末尾,输出最大的平衡偏差,只要偏差小于某一阈值(比如1e-4),说明结果基本可行。
然后看储能SOC曲线。合理的SOC曲线应该是围绕某个基准水平波动,峰时段放电下降、谷时段充电上升、24小时后回到起始值附近。如果你看到SOC曲线快速冲到100%然后一直顶着上限不动,很可能是SOC约束或充放电功率约束没写对,或者光伏出力数据有误。
再看联络线功率曲线,这是多微网互联的精华所在。合理的趋势是:白天光伏大发时,有余电的微网向缺电的微网送电;晚间负荷高峰时,方向反过来。如果联络线功率几乎一直为零,说明互联机制没起作用,大概率是目标函数权重或拓扑结构设置有问题,或者粒子群收敛不够,没有充分发现互联的价值。
5.3 低碳与经济目标的张力:扫描碳价看趋势
一个很有价值的实验是:保持其他参数不变,把碳价从0元扫描到300元,每次运行记录总成本和总碳排放,然后画成曲线。你会发现两者呈典型的反比关系:一开始碳价低时,系统倾向于多购电、多用燃气轮机,成本低但排放大;随着碳价升高,系统减少购电、减少燃气轮机出力、增加储能利用和光伏消纳,成本上升但排放下降。这个趋势曲线就是多目标优化的帕累托前沿的离散近似。
这里有一个经验之谈:碳价扫描步长取10元或者20元比较合适,太小的话相邻结果差异不明显,太大则丢失细节。扫描时每个碳价点建议运行3次取平均,因为粒子群有随机性,单次结果波动可能掩盖真实趋势。
6. 实操中踩过的坑与排查方法
这部分我写几个亲测过的典型问题。每一个都对应一批代码报错或离谱结果,排查思路比问题本身更有价值。
6.1 算出来的调度结果为负功率
常见现象:燃气轮机出力出现负值,储能放电功率为负,甚至负荷出现负值。这通常是变量边界没有加约束,粒子群在搜索过程中把变量推到了物理不合理的区间,罚函数又没能有效纠正。排查时先检查参数初始化范围,再检查边界吸收逻辑是否对所有变量都生效。一个变量一个变量地打印出来核对,基本几分钟就能定位。
6.2 罚函数系数导致的“假收敛”
我用过一个罚函数系数,刚开始数据都正常,但仔细检查发现功率平衡偏差虽然小于容忍阈值,却并不严格为零。原因是罚函数系数不够大,粒子群找到了一个“偏差不大但成本更低”的方案,宁可顶着局部小偏差也不回归严格可行域。这是罚函数法的先天不足。
解决方案有两个:一是提高罚函数系数,让任何偏差都严重影响目标值;二是改用修复机制,在每次评估目标函数前先把不可行的解修复到可行域内。我后来基本都用第二种方案,效果更可控。
6.3 粒子群结果波动大、不复现
同一份代码同一组参数,连续跑三次,结果总成本差别达到5%甚至更大。这种波动来源通常是:变量维度高、搜索空间大,而种群规模和迭代次数不足;或者惯性权重固定不变,早期和后期搜索策略无法兼顾。
调整思路是:先加大种群到150或200,迭代次数加到800,如果波动减小则说明原参数确实不足;如果波动还在,就需要检查是不是约束处理有随机性成分。另外,粒子群初值的随机种子最好保存下来,方便复现结果,做法很简单,在程序开头固定rng(某个固定值)即可。
6.4 常见参数速查表
| 参数 | 推荐范围 | 说明 |
|---|---|---|
| 种群规模 | 80~200 | 变量维度高时取上限 |
| 迭代次数 | 300~1000 | 视问题规模与时间预算决定 |
| 惯性权重初始值 | 0.8~0.9 | 早期探索 |
| 惯性权重终值 | 0.3~0.5 | 后期局部搜索 |
| 认知系数c1 | 1.0~2.0 | 通常1.5附近 |
| 社会系数c2 | 1.0~2.0 | 通常1.5附近 |
| 碳价 | 0~300元/吨 | 按研究场景扫描 |
| 罚函数系数 | 10~100倍目标量级 | 使违约束方案显著劣势 |
7. 基于经验的几个建议
最后分享几点我在实际使用中的体会,都是踩过坑之后总结出来的。
第一,永远先跑通“单微网独立调度”再扩展“多微网互联”。如果你直接上三微网联合优化,出了问题很难分清到底是求解器的问题、建模的问题还是数据的问题。先让每个微网在无互联的情况下独立优化,确认目标函数和各约束都正确,再引入联络线变量和互联约束,一步步递进,调试成本最低。
第二,调度结果画图的时候,建议把每个微网的功率平衡图画成堆叠柱状图,出力和负荷一目了然。另外单独画一张联络线功率的曲线图,把三根联络线的功率都画在一张图上,这样能快速验证互联逻辑是否合理。数据量不大,但画图风暴排查的效果比看数字强得多。
第三,参数文件用Excel或独立脚本统一管理,不要在代码里硬编码。改一个机组容量、换一组电价、调整储能容量,只改数据文件就能完成新场景测试,这对后续做敏感性分析特别重要。我个人的习惯是参数集中在一个初始化函数头部,并且每组参数都写上注释来源。
第四,真正理解结果背后的物理规律比调出一组漂亮数据更重要。如果某微网在夜间向大电网购电,不是因为算法bug,而是因为夜间谷电便宜且碳排放系数低;如果某微网在白天把光伏电力送给邻居而自己同时买市电,大概率是因为它的负荷曲线与邻居错峰明显,这样整体成本反而更低。读结果的时候多问几个为什么,这套代码才算真正被消化吸收。