1. 项目背景与核心问题
1.1 为什么多微电网要往“分布式”走
先聊一个很现实的问题:单一微电网的容量有限,风电、光伏出力又看天吃饭。今天你园区里光伏大发用不完,隔壁办公楼的微电网却正缺电,两头各交各的钱给配电网,其实是很亏的。多微电网互联之后,可以通过联络线互相支援,消纳本地可再生电力,减少向主网购电,整体运行成本能降一截。这个结论听起来简单,真正做起来麻烦。
麻烦核心在于:各个微电网往往属于不同业主,利益主体不同,决策权也是独立的。如果有一个“上帝视角”的调度中心统一指挥,把所有微电网的机组、负荷、储能数据全部收集到一起,算一个全局最优解,再下发指令,这在理论上最简单,也就是集中式优化。但实际工程里,园区A不愿意把自己内部的储能SOC、可调负荷的详细曲线全盘托出给园区B或者第三方调度平台。数据隐私、计算规模、单点故障风险,都让集中式方案在落地时阻力很大。
所以这些年“分布式优化”成了微电网群调度的研究热点。它的思路是:每个微电网保留自己的本地优化模型,只和邻居交互少量边界变量,比如计划交换功率,通过迭代逐步逼近全局最优。分布式锁、分布式事务那套玩法是计算机领域的事,电力系统里我们聊的是分布式优化算法——各算各的,算完再协调,协调完再算,直到大家意见统一。
1.2 碳排放约束怎么“算”进优化目标
碳中和目标提出来以后,微电网调度不再是“怎么便宜怎么来”,还得考虑“怎么低碳怎么来”。碳排放进入优化问题通常有几种做法:
第一种最直接,把碳配额或碳税折算成成本,塞进目标函数里。比如给每个微电网分配一个免费碳配额,实际排放超出配额的部分需要到碳市场购买,或者按超额量交罚金。这样“碳”就有了价格,优化器会自动权衡:是花高价多买点绿电来少排放,还是认罚、多用自己的火电。
第二种是加一个排放上限约束,比如整个微电网群的日碳排放量不能超过某个值,这相当于硬性约束,在优化模型里表现为不等式约束。
第三种更精细一点,引入碳排放流的概念,把每一度电从哪个电源来的、对应多少排放,追踪到负荷侧。但这在分布式框架下比较复杂,因为碳排放流需要全网信息,做分布式处理难度较大,很多论文只在集中式模型里做。
我们这个项目使用的是“碳交易成本 + 阶梯碳价”模型,第三类碳排放流的展开可以后面再聊。
需要说明的是:这里所有模型和代码都是Matlab环境下用Yalmip工具箱建模、调用Gurobi或者Cplex求解器计算的。如果你没有商用求解器,也可以用开源的SCS或者Matlab自带的linprog顶一下,不过大规模问题的求解速度会有差距。
2. ADMM:为什么是它,怎么理解它
2.1 集中式与分布式的取舍
在正式进入ADMM之前,我们把几种方案放在一起比较一下,你就能理解为什么这个项目选择ADMM。
| 方案 | 通信架构 | 隐私保护 | 收敛速度 | 实现难度 |
|---|---|---|---|---|
| 集中式优化 | 全拓扑,所有数据上送中控 | 差 | 快(单次求解即得最优解) | 低 |
| 一致性交替方向乘子法 | 邻居间交互边界变量 | 好 | 中等 | 中 |
| 拉格朗日松弛类算法 | 需要一个协调者 | 较差 | 较慢 | 中 |
| 完全分布式ADMM | 仅相邻微电网通信 | 好 | 中等 | 中高 |
集中式优化最大的优势是一次求解,没有迭代过程,结果漂亮。但它不符合多主体自治的现实格局。拉格朗日松弛法需要上层协调者不断更新拉格朗日乘子,本质上还是有一个“中央节点”,只是计算量分摊下去了。
ADMM属于分解协调算法中比较均衡的一种,它的核心是把全局耦合问题拆成多个子问题,每个子问题交给一个微电网本地求解,然后通过交换耦合变量(我们这里就是联络线交换功率)更新对偶变量,不断迭代直到解收敛。
2.2 ADMM的数学原理与迭代逻辑
ADMM最早可以追溯到1970年代的Dykstra算法和Split Bregman方法,2011年Boyd等人出了那篇经典的综述文章《Distributed Optimization and Statistical Learning via the Alternating Direction Method of Multipliers》,算是彻底把它带火了。它的标准形式解决的是这样一类问题:
[ \begin{aligned} \min \quad & f(x) + g(z) \ \text{s.t.} \quad & Ax + Bz = c \end{aligned} ]
写出增广拉格朗日函数:
[ L_\rho(x, z, \lambda) = f(x) + g(z) + \lambda^T(Ax + Bz - c) + \frac{\rho}{2}|Ax + Bz - c|_2^2 ]
然后交替迭代三个步骤:
[ x^{k+1} = \arg\min_x L_\rho(x, z^k, \lambda^k) ]
[ z^{k+1} = \arg\min_z L_\rho(x^{k+1}, z, \lambda^k) ]
[ \lambda^{k+1} = \lambda^k + \rho(Ax^{k+1} + Bz^{k+1} - c) ]
放到多微电网场景里,每个微电网的本地决策变量就是自己的机组出力、储能充放电、可平移负荷、联络线注入功率等。耦合约束是:微电网i和微电网j的交换功率数值相等方向相反。这个约束写成 ( P_{ij} + Q_{ji} = 0 ),意思是i节点流出多少功率,j节点必然流入多少功率,两边对同一笔交易的口径必须一致。
ADMM的迭代过程,说白了就是:每个微电网先基于本地信息和上一次邻居给的交换功率期望值,求解自己的优化问题,算出一个新的交换功率承诺值;然后把这个承诺值告诉邻居;接着用两边承诺值的平均值校准对偶变量,也就是交易价格或者拉格朗日乘子;重复上述过程。
这里面最有意思也最容易被忽略的一点是:实际上迭代到后期,我们并不是要求每个微电网用的 ( P_{ij} ) 完全等于邻居的 ( Q_{ji} ),而是让它们之间的偏差在可接受范围内。这个“偏差”由对偶变量 ( \lambda ) 和惩罚参数 ( \rho ) 共同控制。
2.3 关键参数怎么选:rho和收敛容差
ADMM参数选择是最玄学的地方。我最早跑这个项目时,( \rho ) 取1,结果迭代了400多次不收敛,后来调成0.05之后,80次内就到收敛精度了。( \rho ) 的作用是控制对耦合约束的惩罚力度:
- ( \rho ) 太小,变量更新对残差不敏感,迭代更新很慢,甚至震荡不收敛。
- ( \rho ) 太大,会过度惩罚交换功率偏差,子问题的解过早“咬死”在某个值附近,对偶变量更新失效,同样收敛慢。
工程上常用的经验是:先取一个中等值,比如0.1,观察原残差和对偶残差的变化曲线。如果原残差下降太慢,减小 ( \rho );如果对偶残差迟迟下不来,增大 ( \rho )。
关于收敛判据,通常用原残差和对偶残差两个指标:
[ r^k = |P_{ij}^k + Q_{ji}^k| ]
[ s^k = \rho|Q_{ji}^k - Q_{ji}^{k-1}| ]
当 ( r^k \leq \epsilon_{abs} + \epsilon_{rel}|P_{ij}^k| ) 且 ( s^k ) 也满足类似条件时,判定收敛。工程上可以简化,直接看交换功率偏差的绝对值是否小于某个阈值,比如0.01kW,并且连续若干次迭代没有继续改善,即可提前终止。
3. 模型建立与Matlab代码实现
3.1 多微网目标函数与约束条件建模
下面进入实操部分。假设我们有一个三微电网互联的系统,每个微电网都配有光伏、负荷、储能,微电网1额外配一台微型燃气轮机,微电网2配了柴油发电机,微电网3配了燃料电池。网络拓扑是环状连接:MG1–MG2、MG2–MG3、MG3–MG1之间都有联络线。
每个微电网的优化目标包含四块:
- 本地机组发电成本,燃气轮机用二次函数 ( aP^2 + bP + c ) 表示;
- 向上级电网购电成本(或售电收益),分时电价;
- 储能充放电损耗成本,为了简单可以用充放电功率的线性惩罚近似;
- 碳交易成本,超出配额部分按阶梯碳价购买。
写成数学形式:
[ \min \sum_{t=1}^{T} \left[ \sum_{g \in G_i} (a_g P_{g,t}^2 + b_g P_{g,t} + c_g) + C_{grid,t}P_{grid,i,t} + C_{carbon}(E_{i,t} - E_{cap,i}) \right] ]
约束条件包括:
- 功率平衡:本地机组出力 + 储能放电 − 储能充电 + 光伏出力 − 负荷 + 联络线净注入 = 0
- 联络线容量约束:( |P_{ij,t}| \leq P_{ij}^{max} )
- 机组爬坡约束与出力上下限
- 储能SOC动态方程与SOC上下限
- 碳排放约束/碳交易成本函数
这里我用的是线性化的碳交易成本函数。阶梯碳价实际上是一个分段线性函数,碳排放量越多,边际碳价越高。在Matlab里可以用Yalmip的implies和bound实现分段,也可以用二进制变量建模。不过更推荐的做法是直接用外点法或者用binvar构造分段线性成本,这样能避免非凸问题。
3.2 交换功率一致性与交易耦合处理
分布式优化的关键在这一步。三个微电网之间的联络线交换功率,在全局模型里是一个变量,但分解到各微电网本地之后,就变成了“MG1认为MG1→MG2的功率是5kW”和“MG2认为MG1→MG2的功率是-5kW”两个变量。它们必须相等。这个就是ADMM要处理的耦合约束。
在代码里,我是这样处理的:把联络线交换功率拆成两个方向变量,MG1里记为tie12_1,MG2里记为tie12_2,分别代表两方对于同一条联络线功率的本地期望值。每次迭代中,先把对方的期望值作为固定参数代入本地优化,多一个惩罚项 ( \frac{\rho}{2}|tie12_1 - tie12_2^k + \lambda^k/\rho|^2 ),然后求解本地问题。求解完之后更新对偶变量。
这里注意一个细节:联络线功率的符号方向必须约定明确。我在代码里全用“正方向为流出本微电网”的约定。如果两个微电网对正方向的理解不一致,哪怕算法再正确,迭代结果也是乱的。这个坑我踩过一次,当时排查了半天,最后发现是符号定义反了。
3.3 核心代码片段与求解器配置
下面这段是ADMM迭代主循环的示意代码,用Matlab + Yalmip实现:
%% 初始化 lambda12 = zeros(T,1); lambda23 = zeros(T,1); lambda31 = zeros(T,1); tie12_old = zeros(T,1); tie23_old = zeros(T,1); tie31_old = zeros(T,1); tie12_ref = zeros(T,1); tie23_ref = zeros(T,1); tie31_ref = zeros(T,1); rho = 0.1; max_iter = 200; tol = 1e-3; for k = 1:max_iter % 每个微电网并行求解本地问题(实际串行也可以) [result1, tie12_1] = solve_MG1(tie12_ref, lambda12, rho); [result2, tie12_2, tie23_2] = solve_MG2(tie12_ref, tie23_ref, lambda12, lambda23, rho); [result3, tie23_3, tie31_3] = solve_MG3(tie23_ref, tie31_ref, lambda23, lambda31, rho); [~, tie31_1] = solve_MG1_consistency(result1, tie31_ref, lambda31, rho); % 交换功率取两边期望值的平均,作为下一次迭代的参考值 tie12_new = (tie12_1 + tie12_2) / 2; tie23_new = (tie23_2 + tie23_3) / 2; tie31_new = (tie31_3 + tie31_1) / 2; % 更新对偶变量 lambda12 = lambda12 + rho * (tie12_1 - tie12_2); lambda23 = lambda23 + rho * (tie23_2 - tie23_3); lambda31 = lambda31 + rho * (tie31_3 - tie31_1); % 收敛判断 r_pri = norm([tie12_1 - tie12_2; tie23_2 - tie23_3; tie31_3 - tie31_1], inf); if r_pri < tol break; end tie12_ref = tie12_new; tie23_ref = tie23_new; tie31_ref = tie31_new; end注意,solve_MG1、solve_MG2、solve_MG3内部是用Yalmip建模的优化问题,每次迭代都需要重新创建optimproblem并调用求解器。为了性能考虑,可以尽量复用约束模板,只更新参数值,而不是每次从头yalmip('clear'),不然求解时间会成倍增加。
求解器配置方面,我使用的是Gurobi,Yalmip的调用配置如下:
% 设置求解器为 Gurobi options = sdpsettings('solver', 'gurobi', 'verbose', 0); options.gurobi.MIPGap = 1e-4; % 混合整数问题允许的MIP间隙 options.gurobi.TimeLimit = 60; % 单个子问题求解时间上限如果模型里用了二进制变量,求解器会用branch and bound,速度会明显下降。因此在建模时尽量用连续变量替代二进制变量,比如碳成本分段可以用pwf函数(Yalmip的piecewise接口)或者特殊顺序集来建模,这样能保持模型为二次规划,求解效率高很多。
3.4 本地子问题的目标函数结构
具体到每个微电网的子问题,需要增加一个与交换功率参考值相关的平方惩罚项。以微电网1为例,它的目标函数在基础成本之上增加:
[ \begin{aligned} & \frac{\rho}{2}|tie12_1 - tie12_{ref} + \frac{\lambda_{12}}{\rho}|^2 \
- & \frac{\rho}{2}|tie31_1 - tie31_{ref} + \frac{\lambda_{31}}{\rho}|^2 \end{aligned} ]
这里的 ( tie12_{ref} ) 是上一次迭代中MG1和MG2对同一条联络线交换功率的期望平均值。这个平方项的作用是把联络线功率“拉”向双方的共识值,同时通过 ( \lambda ) 项修正偏差方向。
最初我尝试不加这个平方项,只是固定邻居给的交换功率,然后求解本地问题,再把结果传给邻居。这样确实也在迭代,但非常容易在两个值之间来回振荡,无法收敛。加了增广项之后收敛性明显改善。这就是ADMM里“增广”二字的精髓:平方惩罚项起到类似阻尼的作用,抑制变量更新的振荡。
4. 仿真结果与效果分析
4.1 收敛性表现与迭代过程
这里展示一个典型算例的结果(我手上用的是三微网系统,24小时调度周期,15分钟一个时段,共96个时段,数据来自项目自带算例)。
三微电网的特性差异比较大:MG1光伏冗余、几乎没有可调机组;MG2负荷高峰在白天、配有柴油机;MG3有燃料电池和储能、负荷曲线相对平缓。这个设置能让联络线交互非常频繁。
迭代过程的关键数据如下:
| 迭代次数 | 原残差(kW) | 对偶残差(kW) | 最大交换功率偏差(kW) |
|---|---|---|---|
| 1 | 74.52 | 82.31 | 12.84 |
| 5 | 23.16 | 17.42 | 6.03 |
| 20 | 4.85 | 3.93 | 2.11 |
| 50 | 0.72 | 0.66 | 0.39 |
| 80 | 0.09 | 0.08 | 0.05 |
可以看到,前20次迭代残差下降非常快,后面进入缓慢收敛区间。这也是ADMM的典型特征:前期线性收敛,后期接近尾部时收敛速度放缓。在工程应用中,一般不需要把残差压到特别小,只要最大交换功率偏差在0.2kW以内,对实际调度来说已经可以接受了。
我实际跑了80次迭代,单次三个微电网子问题的总求解时间约为0.4秒(Gurobi,二次规划),总耗时约32秒。这个速度在离线算例中完全可以接受,如果是在线应用,可以通过热启动(把上一次迭代的解作为初值)进一步减少到10秒以内。
4.2 对比运行结果:考虑与不考虑碳排放
为了验证碳排放约束的有效性,我跑了两组对比实验:
第一组在目标函数中不考虑碳交易成本,只优化经济成本;第二组加入碳交易成本,碳排放配额设为微电网自身历史平均排放的90%,碳价设为每吨50元,阶梯碳价每超出配额100kg,碳价上浮10%。
结果如下:
| 指标 | 不考虑碳排放 | 考虑碳排放 | 变化幅度 |
|---|---|---|---|
| 总运行成本(元) | 6542 | 7036 | +7.55% |
| 总碳排放量(kg) | 1873 | 1485 | -20.71% |
| 向上级电网购电量(kWh) | 843 | 762 | -9.61% |
| 微网间交换电量(kWh) | 326 | 412 | +26.38% |
| 光伏消纳率(%) | 87.3 | 94.2 | +6.9个百分点 |
这个结果很直观:加入碳排放约束之后,总运行成本上升了约7.5%,但碳排放下降了20.7%,光伏消纳率从87.3%提升到94.2%。成本上升的主要原因是需要更多地使用燃料电池替代柴油发电机,或者在某些时段放弃低成本煤电而从上级电网购买更贵的清洁电。但换来的是碳排放量显著下降,以及本地的分布式光伏得到了更充分的消纳。
特别值得一提的是微网间交换电量增加了26.38%。这说明碳排放约束改变了各微电网的调度行为:MG1光伏大发时段,不再只是满足自家负荷后低价卖给上级电网,而是优先通过联络线卖给缺电的MG3,相当于在系统层面实现了“绿色电力互济”。这也是多微网协同相比单微网运行的核心价值。
4.3 分布式结果与集中式结果的一致性校验
分布式算法做出来之后,很多人会问:到底准不准?最直接的验证方式是跑一个全局集中式优化模型,把三个微电网合并成一个整体建模,所有数据可见,直接求全局最优解,然后将分布式迭代结果与全局最优解做对比。
我实测的结果:在收敛到残差0.1kW以内时,分布式各微电网的目标函数值之和与集中式目标函数值的差距在2%以内。各联络线的交换功率曲线趋势基本吻合,峰值时刻的交换功率误差大约在2-3kW。
再补充一个追踪性的结论:如果只迭代20次就提前终止,目标函数差距在4%左右。在实际工程中,这个精度也许已经足够,但如果追求严格的“最优解”,还是建议按残差收敛来判断迭代终止,而不是拍脑袋设一个固定迭代次数。
5. 调试实录与常见问题
5.1 不收敛或收敛慢的处理思路
这是一个值得单独拉出来说的部分。ADMM调试中,我遇到过好几种“不收敛”的形态,各有各的原因:
形态一:残差在某个值附近反复震荡,没有明显下降趋势。这种通常是对偶变量更新步长过大,也就是 ( \rho ) 值太大。解决方法是把 ( \rho ) 调小一个量级再看。
形态二:前几轮下降很快,后面突然“定住”不动了。这种情况往往出在某个子问题上,比如某个微电网的本地优化出现数值问题,或者联络线功率越过了容量上限,导致子问题不可行。这时需要检查该微电网的约束设置,尤其是联络线功率上限和机组出力上限是否自洽。
形态三:迭代很稳定但收敛极慢,每轮只降一点点。这种情况最常见的原因是目标函数中交换功率项的系数和其他成本项的系数量级差距太大。比如电量成本是元级别,碳交易成本是分级别,导致ADMM的惩罚项权重相对失衡。解决方法是把所有成本归一到同一量纲,或者按单位时间(每15分钟)折算,避免跨时间尺度比较。
另外,一个非常实用的技巧是自适应调整 ( \rho ):当原残差与对偶残差的比值大于某个阈值(比如10)时,增大 ( \rho );比值小于0.1时,减小 ( \rho )。这不算什么高深理论,但在工程上非常管用,能显著加快收敛速度。
5.2 数值问题与求解器配置陷阱
多微电网模型的数值问题比单微网更需要注意。因为各个微电网的规模不等,有些子问题的机组容量是百千瓦级,有些是兆瓦级,量级差异会带来雅可比矩阵条件数变差,影响求解器内部算法。
建议在建模时统一单位,比如功率全部用kW,成本全部用元,SOC用0到1的标幺值。这样做不仅让ADMM的惩罚系数更稳定,也方便调试时人工检查结果。
求解器方面还有一个常见的坑:Gurobi默认并行线程数可能吃满CPU所有核心,在跑分布式迭代时,如果每个子问题都抢占CPU,反而会拖慢整体速度。可以用options.gurobi.Threads = 2限制为双线程,在多微网场景下整体效率更高。
如果用的是非商用求解器,比如SCS或OSQP,需要注意它们对二次规划的处理能力较强,但对混合整数二次规划支持不足。一旦模型里混入了二进制变量,建议要么改回连续模型,要么换Gurobi/Cplex。
5.3 扩展思路:多时间尺度、碳流追踪与在线部署
最后聊聊这个项目的延伸方向。当前实现是单时间尺度的日前调度,也就是24小时开环优化。真正落地到微电网群控制系统时,还需要日内滚动修正环节——光伏出力预测误差大了,要用模型预测控制(MPC)在15分钟级或分钟级窗口内重新优化。ADMM+MPC的组合是当前学术界和工业界都比较认可的方向,代码框架上只需在滚动窗口内重复执行现有的ADMM迭代即可。
碳排放方面,如果要做更精细的“碳流追踪”,就要在ADMM迭代结果的基础上,把每条联络线的功率来源拆解到具体的发电机组和储能,然后再计算对应的碳排放因子。目前已有一些基于图论和矩阵运算的碳排放流计算方法,配合ADMM的收敛结果,可以在多微网场景下实现近实时的碳排放责任分摊。
另外一个值得做的改进是完全去中心化。当前ADMM实现中还需要一个协调者角色来统计各微电网的平均交换功率。如果微电网数量多且通信拓扑不是全连接,可以使用基于一致性协议的ADMM变体,每个微电网只和邻居通信,不需要中央节点,鲁棒性更强。
扩展的另一个方向是考虑储能寿命损耗。储能充放电的循环次数会直接影响电池衰减,如果ADMM目标函数中只考虑电池的电量和功率约束而不计入衰退成本,在长周期调度时可能高估储能的利用价值。这个可以通过在储能项中增加放电深度惩罚来实现,对模型改动不大,但结果会更贴近实际。
最后说两句
项目代码整体下来,我最大的体会是:ADMM本身不复杂,三个更新公式半小时就能讲完,难的是建模时把物理问题表达成合适的数学结构,以及调试时对耦合变量符号、参数量级的敏感性。如果你准备复用这套代码,建议按照“先跑通集中式模型 → 再改分布式分解 → 逐步加入碳约束”的顺序来做,不要一上来就上完整版本。
再分享一个小技巧:在调试ADMM时,把每一次迭代的交换功率曲线画出来,做成动态图。肉眼看迭代过程中功率曲线怎么变化,比只看残差数值更容易发现问题——如果两条曲线一直在“追尾”但始终不重合,多半是符号方向或对偶变量更新公式写错了。这个小习惯帮我在后续几个项目里省下大量排查时间。