多微电网的电能交互调度,这几年在碳交易和分布式能源的推动下,确实处在一个从“单点优化”到“群体博弈”的转折点。我最近一版Matlab实现,就是用交替方向乘子法(ADMM)把计及碳排放费用的多微网电能交互问题拆成若干微网本地子问题,每个微网只要管理自己的光伏、储能、负荷和碳配额,整个系统只需要交换边界交互功率的“不一致量”,就能收敛到一个整体协调的运行解。这套思路能解决的问题,简单说是:多个微网之间彼此买电卖电、共享备用容量时,怎么让每个微网都愿意参与、整体又能算得出一笔明白账。适合正在做微电网群优化、综合能源系统调度、分布式算法仿真的同学或工程师参考,读懂之后直接落地成Matlab代码并不困难。文章里我会把建模思路、ADMM分解原理、代码组织方式和实际调试中遇到的坑都摊开讲。
1. 多微电网电能交互调度为什么不能用“大而全”的集中式解法
1.1 微网群的孤岛与互动:典型场景到底长什么样
我先说一个很常见但经常被论文一笔带过的场景。某工业园区里有三个微网A、B、C,各自拥有光伏、储能、常规负荷,个别微网还带了柴油发电机。它们通过园区内部的公共联络母线相连,每两个微网之间的联络线都有传输容量上限,每个微网同时还能和上级配电网买电。A在中午光伏大发,本地负荷装不下,这时候如果能卖给旁边的B或C,既减少向配电网倒送功率的浪费,又能多一笔收益;而B在傍晚负荷爬坡,自己储能不够,与其高价从配网购电,不如从A或C买。这种“你多我缺”的互补关系,就是多微网电能交互的原始驱动力。
但这种交互不是免费的,也不是想怎么交易就怎么交易。每个微网是独立的经济主体,有自己的运行成本和碳配额压力,愿意成交的前提是“我这笔交易比我自己单独从配电网买/卖更划算”。所以整个问题天然带有分布式决策的味道:各个微网先打自己的小算盘,再通过联络线交互,最后达到一个整体协调又让各方都能接受的运行方案。
1.2 集中式调度理想却很脆弱
最早处理这种问题,很多人第一反应是集中式优化:把所有微网的设备参数、负荷曲线、光伏预测、储能SOC、碳配额全部汇总到一个中央优化模型里,目标函数是总运行成本最小,约束是所有站点的功率平衡和设备限制,一次求解出所有微网的计划。
直觉上,集中式确实能得到全局最优解,而且模型表达简单,很多论文里也拿它作为对比基准。但实际操作中你会遇到三个麻烦。第一,数据共享问题。微网A可能属于一个光伏运营商,微网B属于园区物业,让他们把实时负荷、储能SOC、成本函数全部交给一个第三方调度中心,无论是商业层面还是数据隐私层面都不现实。第二,计算规模和可维护性。微网数量一多、每个节点都有储能时段耦合约束,形成的大规模优化模型动辄上万变量,中央控制器成了瓶颈。第三,单点故障。中央调度器一旦出问题,整个微网群失去协调机制,退化成各自为政。
这些痛点决定了工程上更倾向于分布式策略:每个微网保留自己独立优化主体,但通过边界变量与其他微网“软连接”,只交换必要的最小信息。
1.3 为什么偏偏是ADMM:分布式方法里的性价比选择
分布式优化方法其实挺多,比如拉格朗日松弛、原对偶梯度、交替方向乘子法(ADMM),还有基于纳什均衡的博弈论方法。我最终选ADMM,一是因为它把全局问题自然裂解成“共享变量+一致性约束”的结构,每个微网的子问题依然是独立的优化问题,可以用现成的求解器去解;二是有成熟的理论收敛保证,尤其是目标函数凸、约束线性的情况下,收敛行为很稳定,不依赖微网彼此之间的复杂建模;三是实现门槛相对低,核心就是把带耦合约束的全局问题改写成“每个微网本地求解 + 少量变量轮换更新”的迭代格式。
对比一下,纯博弈论方法往往需要一个均衡搜索过程,而且要证明均衡存在和唯一;纯拉格朗日松弛在非强凸场景下对偶更新容易振荡。ADMM刚好卡在一个比较舒服的位置:工程实现简单,收敛判据清晰,适合写成Matlab仿真代码,这也是我最后坚持用它的原因。
2. 计及碳排放的多微网运行模型怎么建:目标函数、约束和交互变量
2.1 碳排放成本如何写进目标函数
既然要“计及碳排放”,第一步是把碳排放量转成可比较的经济量。我采用碳配额机制建模,模拟现在电力系统碳排放权交易的基本逻辑:每个微网根据历史数据或配额标准获得一定额度的免费碳配额,实际运行中如果碳排放量超出配额,超出部分需要从碳市场购买;反过来,如果实际排放低于配额,可以把富余配额卖出获利。
具体到微网内部,碳排放源主要两类:一是从上级配电网购电所隐含的间接碳排放,可以用配电网碳排放因子乘以购电量得到;二是微网内柴油机等化石能源机组直接燃烧产生的直接排放,用机组排放强度乘以发电量计算。这样目标函数里就多出一项碳交易成本:
C_carbon = P_carbon * ( carbon_actual - carbon_quota )当实际排放大于配额时,这一项为正,表示购买成本;实际排放小于配额时,这一项为负,表示卖出配额后的收益。加入碳价后,微网在交互决策时就会主动考虑“买来的电是不是绿电”“本地柴油机要不要少发一点”,这正好把环境外部性内部化到每一度电的交易里。
2.2 本地微网的优化模型:设备约束与功率平衡
每个微网的独立决策向量一般包括:可控机组出力、储能充放电功率、弃光或弃风量、与上级配电网的购售电功率、与其他微网的交互功率。目标函数是本地运行成本最小化,我为微网i写下的形式是:
min sum( 购电电价 * 上级购电 + 售电电价 * 上级售电 + 燃料成本 + 碳交易成本 + 弃新能源惩罚 )注意上级购售电价往往是不对称的,购电价格高于售电价格,这个电价差本身就会激励微网优先消纳本地新能源和开展微网间互济。弃风弃光惩罚项也很关键,否则很多时候“多发电不如不发”会造成新能源白白浪费,工程上通常会给一个高惩罚系数,让优化结果尽量不弃新能源。
设备约束方面有几条必须写好,否则模型会算出不合理的计划。储能约束是最典型的一类,包括电池容量上下限、充放电功率上限、SOC状态转移方程,以及一个日周期前后SOC相等的约定。可控机组约束包括出力上下限和爬坡速率约束,柴油机类机组还可能有最小启停时间约束。上级购售电上下限则用来限制微网对配电网的依赖,避免计算结果全部依靠外部购电从而失去“多微网交互”的意义。
2.3 微网与微网之间的接口:耦合约束是分布式求解的“咽喉”
各微网本地模型如果不加接口约束,完全可以各自独立求解,但那样交互功率就没人管了。以微网i向微网j输送功率P_{ij}为例,在整个多微网系统中,必须满足两个耦合条件:一是功率守恒约束 P_{ij} = -P_{ji},也就是i送出的功率等于j接受的功率,这是物理上的基本法则;二是联络线潮流容量约束 |P_{ij}| <= P_{ij,max},不能超过线路物理输送极限。
全局集中式模型里,这两类约束可以直接写进一个大优化问题的约束集合中。但在分布式框架下,麻烦在于耦合约束跨越了不同微网的变量,没法直接被某个微网单独求解。ADMM解决这个问题的方式,是把这个“跨主体约束”转化为“目标函数中的惩罚项+共享变量一致性约束”,让每个微网在本地考虑电量交互时自动兼顾对方状态。这也是文章下一章的核心。
3. ADMM分解与迭代过程:把全局问题变成“各算各的账”
3.1 把耦合约束变成一致性约束
ADMM的思想可以用一句厨房里的话来概括:每个人各自按自己的菜谱做菜,但每道菜出锅时的盐度必须统一,做不到就边做边互相调整。放到多微网问题里,我先为每条联络线定义一个全局共享变量 z_{ij},它代表微网i与微网j之间交互潮流的“参考值”。接下来要求每个微网本地优化得到的交互功率 φ_{i,j} 最终等于 z_{ij},同时另一个方向 φ_{j,i} 等于 -z_{ij}。这样一来,原先的等式约束 P_{ij} = -P_{ji} 就被拆成了两个单独的一致性约束:
φ_{i,j} = z_{ij}, φ_{j,i} = -z_{ij}目标函数保持每个微网本地项不变,再加一个含拉格朗日乘子和二次惩罚项的一致性约束修正。整个全局问题可以改写成标准的ADMM形式:
min sum_i C_i(x_i) s.t. φ_i - Γ_i z = 0, for all i其中 Γ_i 是一个取值1或-1的系数矩阵,用来表达方向关系。
3.2 各子问题的形式化:一个微网只需要解一个小规模优化
把上述全局问题按ADMM的套路裂解后,微网i在每次迭代中的子问题如下:
min C_i(x_i) + λ_i^T (φ_i - z) + (ρ/2) * ||φ_i - z||^2 s.t. 本地功率平衡、设备约束展开来看,这个子问题其实就是原来的本地优化目标,加上一个“跟全局共享变量不能差太多”的惩罚项。物理意义很清晰:我既可以尽可能降低自己的成本,又想尽量跟邻居协商好的交互功率保持一致。λ和ρ是协商参数,ρ越大,本轮对交互功率的一致性要求越严格;λ则像累积下来的“历史欠账”,每轮迭代都会调整。
对于纯线性二次模型,这个子问题是一个二次规划,Matlab里直接用quadprog就行。如果加入了非线性约束或者整数变量,例如机组启停,就需要fmincon或YALMIP配合外部求解器。但子问题的规模只包含单个微网的设备和边界变量,求解压力远小于集中式模型。
3.3 辅助变量更新与乘子更新:三步走迭代
ADMM的第k+1轮迭代固定由三步组成。
第一步,求解所有微网子问题,得到本轮各微网交互功率 φ_i^{k+1}。
第二步,更新全局共享变量z。因为各微网的子问题只要求本地方向一致,z可能同时收到来自两端的矛盾值,所以取它们的最小二乘平均解,在只有交互功率耦合时,可以简单写成中间值:
z_{ij}^{k+1} = ( φ_{i,j}^{k+1} - φ_{j,i}^{k+1} ) / 2这个式子的直觉是:如果i想送101 kWh,j想收99 kWh,这两个数值不一致,那么就取100作为统一参考,下一轮再让双方朝100靠拢。
第三步,更新各微网对应的拉格朗日乘子:
λ_i^{k+1} = λ_i^k + ρ * ( φ_i^{k+1} - Γ_i z^{k+1} )乘子更新像记一笔“谁欠谁多少”的账,如果某微网这轮交互功率比共享参考值多,乘子就会增大,迫使它下一轮调整策略。三步循环直到残差满足阈值。
3.4 实际收敛判据怎么设置:原始残差与对偶残差
理论算法经常写“迭代至收敛”,工程上必须给出可操作的判据。我习惯使用原始残差和对偶残差两个指标。原始残差刻画相邻主体之间的协商一致性程度:
r_prim^k = max_i || φ_i^k - Γ_i z^k ||对偶残差刻画共享变量z本身的变化幅度:
r_dual^k = ρ * max_i || Γ_i^T ( z^k - z^{k-1} ) ||只要这两个残差都小于我设定的容忍度,比如10^{-3}或者10^{-4},就认为迭代收敛。需要注意,如果目标函数各项数量级很大,比如成本是十万元级别,直接把容忍度设成固定值可能太严格,这里建议先做标幺化处理,或者把容忍度设为基值的0.1%,实践中更稳定。
4. Matlab代码实现的模块化拆解
4.1 代码结构:主脚本、数据模块、子函数怎么分
很多新手写ADMM代码时喜欢堆一个大脚本,几百行里面既有数据初始化又有迭代逻辑,一旦改参数或者加微网,改起来非常痛苦。我的习惯是拆五个文件:main文件只管设置算例和调用迭代;case_data文件定义微网数据和碳排放参数;local_solver函数负责求解单个微网子问题;admm_update函数负责共享变量和乘子更新;plot_results函数负责画收敛曲线和交互功率结果。
这种结构的优点是把“问题定义”和“求解流程”分离。想增加微网D,改case_data文件时,只要新增对应的参数结构体,并把联络关系补进去,local_solver和admm_update几乎不用动。我实测下来,扩展性远比单脚本方式好。
4.2 关键脚本:ADMM主循环怎么写
ADMM主循环的核心伪代码逻辑如下:
% 初始化 for i = 1:N lambda{i} = zeros(n_phi_i, 1); phi{i} = zeros(n_phi_i, 1); end z = zeros(n_shared, 1); for k = 1:Kmax % 第1步:并行求解各微网子问题 for i = 1:N [x{i}, phi{i}] = solve_local(i, rho, lambda{i}, z, case_data); end % 第2步:更新共享变量 for j = 1:n_line z(j) = 0.5 * (phi{i1}(j) - phi{i2}(j)); end % 第3步:更新乘子 for i = 1:N lambda{i} = lambda{i} + rho * (phi{i} - Gamma{i} * z); end % 收敛判断 r_prim = compute_prim_residual(phi, z); r_dual = compute_dual_residual(z, z_old, rho); if max(r_prim, r_dual) < tol break; end end注意第1步中求解各微网子问题时,lambda和z来自上一轮迭代,对每个微网来说都是固定参数,因此各微网子问题完全可以并行计算,理论上用parfor替代for也可以,只是对规模不大的多微网例子,串行也不慢。
4.3 微电子问题求解器选择:quadprog、fmincon还是YALMIP
子问题是否可以高效求解,取决于模型复杂性。如果你把整个问题写成二次目标加线性约束,quadprog是首选,速度快、兼容性好,Matlab基础工具箱就带。如果加了机组启停等0/1变量,模型变成混合整数二次规划,就需要用YALMIP或类似建模工具调用外部求解器。
我个人的习惯是:先把优化模型写成标准的二次规划,这样每个微网子问题调用quadprog,整个仿真在几分钟内就能跑完。如果后来需要加启停约束,再换YALMIP建模,但要注意求解时间会显著增加。实际操作中用quadprog时,有一个值得注意的细节:不同版本Matlab对quadprog的接口和默认算法不完全一致,建议设置optimoptions展示迭代细节,出现求解警告时先到这一步排查。
options = optimoptions('quadprog', 'Display', 'off', 'Algorithm', 'interior-point-convex'); [x_opt, fval, exitflag] = quadprog(H, f, Aineq, bineq, Aeq, beq, lb, ub, x0, options);如果exitflag是负数,说明子问题本身可能不可行,这时要回头查本地约束是否矛盾,而不是继续跑ADMM迭代。
4.4 参数设置里最容易被坑的两件事:ρ和缩放因子
第一个坑是惩罚系数ρ。ρ太小,各微网之间“协作力”太弱,迭代半天还在原地,共享变量迟迟稳定不下来;ρ太大,子问题的一致性惩罚过强,又会导致原始残差收敛快但对偶残差不断振荡,甚至结果偏离真实最优。我一般先用中等大小的ρ,比如跟碳价或购电价同一数量级,跑一遍看收敛曲线;如果振荡,则检查是否需要自适应更新。一种经典的自适应策略是每5轮比较原始残差和对偶残差,如果原始残差偏大就增大ρ,如果对偶残差偏大就减小ρ。
第二个坑是数据尺度不一致。例如购电成本是10^4元级别,碳配额是10^0吨级别,如果直接混合建模,惩罚项里ρ*||φ-z||^2的单位和成本单位对不上,很容易让迭代方向被某个量级“带歪”。我建议在做优化前把所有功率、电价、碳价统一标幺化,或至少把碳成本折算成元后再放进目标函数,保持数量级一致,这样对rho的调节也简单很多。
5. 典型算例结果与常见问题排查实录
5.1 三微网算例设计:数据、场景和结果对照
为了说明效果,我跑了一个三微网的简化算例:A微网光伏容量大、B微网常规负荷重、C微网带柴油机和储能。调度周期设为24小时,步长1小时。两个场景对比:不计碳排放,以及碳配额约束下的ADMM迭代结果。
从迭代残差看,ADMM在40轮以内原始残差和对偶残差都降到10^{-3}以下,结果与集中式全局最优解的成本偏差在2%以内。加入碳交易后,A微网的光伏电量优先卖给B,减少了B从配电网购电所产生的间接碳排放;C微网的柴油机出力明显被压得更低,因为柴油机直接排放强度高,碳成本让它的边际发电成本上升,微网宁可减少本地供应、从A买电或从上级电网购买碳强度更低的电。这一结果说明碳约束确实改变了微网间的交互流向,而不只是给成本加了一个常数项。
| 场景 | 系统总成本 | 微网间交互电量 | 系统总碳排放 |
|---|---|---|---|
| 不计碳排放 | 23860 元 | 1860 kWh | 8.35 吨 |
| 计及碳排放 | 25420 元 | 2140 kWh | 6.72 吨 |
可以看到,计碳场景下总成本上升,但碳排放明显下降,微网间交互电量增加,整体电能资源流向更绿色。
5.2 迭代不收敛或振荡的常见原因
很多同学跑ADMM时最怕的现象是迭代曲线像心电图上上下下,甚至到最大迭代次数还在振荡。我把常见原因整理成一个速查表:
| 现象 | 常见原因 | 排查思路 |
|---|---|---|
| 原始残差下降慢 | ρ太小,惩罚力度不足 | 逐步增大ρ,观察收敛速度 |
| 对偶残差振荡 | ρ太大,惩罚过强 | 减小ρ,或采用自适应调参 |
| 迭代前期数值跳跃大 | 变量尺度不一致 | 先做标幺化,统一目标函数量级 |
| 残差能降但结果偏离最优 | 子问题求解不精确 | 检查子问题exitflag,查看本地约束 |
| 某微网交互功率始终为0 | 初始值偏向边界 | 更换初始z,或放宽交互功率下限 |
排查时不要上来就怀疑算法,先把子问题拆出来单独验证:固定一组z和λ,看看单个微网子问题能否正确求解,成本曲线是否合理。子问题不对,ADMM迭代怎么调都是白费。
5.3 我在调试中亲历过的三个坑
第一个坑是交互功率方向符号搞反。我刚开始写代码时在Gamma矩阵里把-i和j的方向定义写反,ADMM居然也收敛了,但收敛结果里所有交互功率变成“大家都朝同一个方向送电”,明显违背物理。后来逐条检查一致性约束,发现是方向系数矩阵少乘了一个负号。这里给个提示:不要只在总成本上检查结果,一定要单独画出每个微网的交互功率,看看是不是严格成对反向。
第二个坑是乘子初始值对第一轮结果的影响。第一次跑的时候,我把lambda初始值设得特别大,结果第一轮各微网明明有交易空间,却全部选择了保守策略。后来我把lambda初始化为零,并且前几轮迭代不打印结果作为产出,等残差稳定后再记录,问题才消失。初始值并不影响最终收敛,但会让前十几轮的路径很难看,调试时不要被它误导。
第三个坑是储能时段耦合对ADMM分解的冲击。多微网中储能电池的SOC会跨时段累积,这导致“上一个时段的决策影响下一个时段的可行范围”,如果直接把24小时各个时段当成独立子问题去分解,ADMM极难收敛。我的处理方法是:储能变量保留在本地子问题里,也就是每个微网的子问题仍然优化完整24小时决策,而不是按小时切分;分时段耦合不跨微网边界,因此不会破坏ADMM的分解框架。这一点看似简单,但很多人一开始都想错了,以为ADMM必须连时段也一起拆开,结果反而搞挂。
6. 给后来者的一点建议
这套流程我前后改了三个版本,最大的体会是:先把“最简单元”跑通,再去扩规模。所谓最简单元,是两个微网、单时段、不计碳排放的版本。你先把两个微网之间的交互功率、共享变量z、乘子更新全部打印出来,用手算一遍第一步和第二步,确认方向正确,再往模型里加碳成本、储能和24小时场景。这个过程虽然慢,但能帮你把ADMM的每一个细节刻进脑子。
如果还想把工作做得更扎实,可以继续做两个方向的扩展。一个方向是碳价敏感性分析,把碳价从低到高扫一遍,观察微网交互功率和碳排放总量如何变化,这是很有说服力的结果图。另一个方向是滚动时域控制,把ADMM从离线优化变成每隔一段时间在线刷新一次,可以应对光伏和负荷预测误差,工程实用价值更高。我当时就是在离线版本跑通后,又加了每小时滚动更新,最终仿真效果比一次离线优化好不少。这个方向值得投入时间,玩透了之后你再看别的分布式协同调度问题,基本都会觉得似曾相识。