在微网群协同优化调度这个方向摸爬滚打了一段时间的同行,应该都体会过一种拧巴:多个微网明明可以通过公共联络线互相借力,却因为各自主体的利益边界和信息隐私,没法用一个全局大模型把整个区域全部管起来。集中式建模在学术上很漂亮,落地时却处处碰壁;完全放任各微网自治,又白白损失了协同带来的经济性和可靠性红利。这两年我在某高校课题组做模拟项目X时,正好用目标级联法(Analytical Target Cascading,ATC)把这个问题完整走了一遍:多微网各自保留内部模型,只交换联络线功率这类边界信息,通过一个轻量级协调器迭代逼近全局最优解,整套调度策略用Matlab代码实现并跑通了多组算例。收敛性、最优性间隙、计算耗时都在工程可接受范围内。这篇文章就把当时的思路、模型、代码框架和调试中踩过的坑一次说透,适合正在做分布式优化调度、微网群能量管理方向的研究生,以及刚接触能源互联网协调控制的工程师参考。
1. 微网群调度问题为什么难,目标级联法为什么能解
1.1 微网群多主体协同的本质矛盾
先说清楚微网群是个什么场景。所谓微网群,通常指若干个地理位置相近、通过公共联络线或共用变压器接入配电网的微网单元,每个微网内部都有分布式电源(光伏、风电、微型燃气轮机)、储能、可控负荷和本地负荷。这些微网在物理上通过联络线强耦合:一个微网从联络线多取的功率,必然来自另一个微网少用的功率,或者来自上级电网的注入。可问题出在管理上,每个微网的产权主体往往不同,可能是不同园区、不同运营公司,甚至同一公司内部不同事业部都有独立考核指标。这种“物理强耦合、管理弱协同”的矛盾,正是微网群调度的核心难点。
集中式优化调度在教科书里很完美:把所有微网合并成一个大优化问题,统一建模、统一求解,得到全局最优解。但在真实项目中推进时,我遇到的阻力非常现实。第一是信息隐私。各主体不愿意交出各自内部的负荷曲线、成本参数和设备约束,这些数据一旦集中到某个调度中心,等于把经营底牌全亮出去了。第二是单点故障和责任边界。一个中央调度中心,模型维护、数据更新、结果下发全压在一条链路上,链路一断整个区域失管,出了问题各主体还容易扯皮。第三是计算规模。微网数量上去了、调度时段拉长到96点甚至更多,集中式问题的决策变量和约束规模会膨胀得非常快,模型调整一次牵一发动全身。
所以分布式优化的诉求就很清晰了:每个微网保留自主决策权,独自维护内部模型和数据,只和协调层交换少量边界变量,通过迭代协商让整体运行效果逼近全局最优。目标级联法恰恰提供了一套成熟的“目标分解—反馈修正”框架,工程实现起来逻辑直白,不需要复杂的通信协议和共识机制,非常适合微网群的层级调度架构。
1.2 目标级联法的核心思想与数学框架
目标级联法最早源于复杂工程系统的多层级设计优化。它的工作方式非常像一个公司总部和分部之间的目标管理:总部先给每个分部下达一个利润指标,分部根据自身资源和条件优化自己的生产计划,回传“预计完成情况”;总部对比目标与完成情况,调整指标再下发,如此往复,直到完成情况与目标接近一致。微网群里的“总部”就是协调器,每个微网就是下面的分部,双方交换的“指标”就是联络线功率和某些共享状态量。
从数学上看,原始的全局问题可以写成:
min Σ f_i(x_i)
s.t. g_i(x_i) ≤ 0
h_i(x_i) = 0
c_i(x_i, x_j) = 0
其中f_i是第i个微网的运行成本,g_i和h_i是本地约束,c_i是相邻微网间的耦合约束(比如联络线功率镜像一致性)。ATC的核心思路是把耦合约束从“硬约束”变成“软目标”,在每个微网的局部优化问题中加入带权重的偏差惩罚项:
min f_i(x_i) + λ_i^T (x_i - t_i) + ||w_i · (x_i - t_i)||²
这里的t_i是协调器下发的目标值,λ_i是拉格朗日乘子向量,w_i是罚权重。微网求解自己的问题后,把响应值x_i返回给协调器;协调器按下式更新目标值和乘子:
t_i^{k+1} = α · x_i^k + (1 - α) · t_i^k
λ_i^{k+1} = λ_i^k + w_i · (x_i^k - t_i^k)
w_i → β · w_i
整个迭代一直持续到所有微网的响应值与目标值的最大偏差小于设定阈值。从KKT条件的角度理解,ATC相当于是把原问题的互补松弛条件拆成两侧分别满足,再通过乘子和罚项把两个方向的差距逐步压缩到零。从工程角度看,这种框架最让人舒服的一点是:协调器完全不关心每个微网内部怎么建模、用哪种求解器,它只盯住边界变量。这意味着换设备、调参数、甚至替换某个微网的整体模型,都不会影响协调层逻辑,这对长期维护一个多主体系统来说是巨大的隐性红利。
1.3 为什么选ATC而不是ADMM或一致性算法
许多同行拿到“分布式优化”需求时,第一反应往往是交替方向乘子法(ADMM),因为它有漂亮的收敛性证明和广泛的开源实现。我也专门做过对比测试,这里分享一些真实感受。ADMM在强凸问题上表现确实好,迭代步数少、收敛稳定,但对罚参数的敏感性比ATC高,遇到光伏出力强波动这类非线性场景,如果罚参数没调好,响应值容易在前后两次迭代之间来回振荡。一致性算法强调各智能体只和邻居通信,无需中心节点,听起来很灵活,但实际工程中收敛速度受通信拓扑影响极大,而且对初始化和网络同步的要求比想象中更苛刻,调试成本不低。
三种主流分布式优化方法的特征对比如下:
| 方法 | 协调结构 | 罚参数敏感性 | 对非凸问题的适配 | 实现难度 | 隐私保护程度 |
|---|---|---|---|---|---|
| 目标级联法 | 轻量级协调器 | 中等 | 较好 | 低-中 | 高,仅交换边界量 |
| 交替方向乘子法 | 可选中心或全分布式 | 高 | 依赖凸性 | 中高 | 高 |
| 一致性/共识算法 | 无中心或弱中心 | 中等 | 一般 | 中 | 中高 |
在三个微网的测试算例中,ATC在50到80次迭代内稳定收敛到可接受间隙;而ADMM在相同罚参数下,出现了明显的振荡段,需要重新整定参数。当然这不能说ADMM不行,只能说明在不同场景下各方法各有优势。微网群本身有一个天然的层级结构——区域调度中心对下辖微网下发指令,这和ATC的“父—子”级联模型高度匹配。既然组织结构天然如此,用ATC就是顺着体系走,实现成本最低。
2. 优化调度模型的构建:从物理约束到数学表达
2.1 微网内部设备建模与变量初始化
跳进代码之前,先把每个微网内部的“物理世界”翻译成数学约束。这部分我在项目里用的是最常见的调度模型,兼顾精度和求解效率。
光伏和风电在日前调度中通常作为不可控电源,按预测出力曲线直接作为输入序列。微型燃气轮机的出力上下限、二次成本函数和爬坡约束按下式写:
C_MT(t) = a · P_MT(t)^2 + b · P_MT(t) + c
|P_MT(t) - P_MT(t-1)| ≤ R_MT
储能系统用荷电状态SOC递推描述,充放电效率和容量约束都要显式建模:
SOC(t) = SOC(t-1) + η_ch · P_ch(t)/E_bat - P_dis(t)/(η_dis · E_bat)
0 ≤ SOC(t) ≤ 1,0 ≤ P_ch(t) ≤ P_ch_max,0 ≤ P_dis(t) ≤ P_dis_max
联络线功率P_tie(t)有双向传输上限,购售电P_buy、P_sell按分时电价结算。下面这段Matlab代码是典型的基础参数初始化模块,我在模拟项目X里直接复用了一份,换成实际算例时只需要改这组数:
% 基础参数初始化 T = 24; % 调度周期,单位小时 P_pv = [0 0 0 ... 2.5 3.1]; % 光伏预测曲线,1x24,单位kW P_wt = [1.8 2.0 ... 0.8]; % 风电预测曲线,1x24 P_load= [45 42 ... 58]; % 负荷功率曲线,1x24,单位kW % 微型燃气轮机参数 P_mt_max = 120; P_mt_min = 10; a = 0.02; b = 0.5; c = 0; % 二次成本系数 R_mt = 20; % 爬坡上限,单位kW/h % 储能参数 E_bat = 300; % 额定容量,单位kWh P_ch_max = 80; P_dis_max = 80; eta_ch = 0.95; eta_dis = 0.9; SOC_0 = 0.2; SOC_min = 0.1; SOC_max = 0.9; % 联络线功率上限 tie_max = 100; % 分时购售电价,单位元/kWh price_buy = [0.45*ones(1,7) 0.85*ones(1,5) 1.2*ones(1,5) 0.85*ones(1,4) 0.45*ones(1,3)]; price_sell = 0.35 * ones(1, T);这套参数虽然简化了不少,但已经足够反映调度问题的核心特征。需要注意的是,SOC初始值不要设在上下限边缘,否则前几个时段很容易无解;我会习惯把SOC_0设为0.2左右,给第一个时段的充放电留出裕量。
2.2 耦合约束与信息交互机制
微网之间真正的物理联系是联络线功率。以两个相邻微网A和B之间的连线为例,A侧流出的功率必然等于B侧流入的功率,严格写就是P_AB(t) + P_BA(t) = 0,方向一个人取正、一个人取负。集中式模型里直接把这个等式放进约束集,但分布式模型里不能这么做,因为A和B各自都不愿意把内部完整模型摊开给对面看。
ATC处理耦合约束的方式非常优雅:协调器向A和B同时下发一组目标值,约定A用P_AB_target、B用P_BA_target,且P_BA_target = -P_AB_target。A在自己子问题中加入惩罚项,把P_AB拉向目标;B同样把自己侧的值拉向相反数。两者独立求解后,把各自的响应值P_AB_resp和P_BA_resp返回协调器,协调器用这两个值的加权结果更新下一轮目标。这样一轮轮转下去,双方就会在某个中间值上咬合。
整个信息交互过程只涉及一列功率序列,不包含任何内部设备参数、成本系数和负荷数据。这在多利益主体场景中的价值怎么强调都不过分。你可以想象两个不同公司运营的微网:他们愿意共享“我打算从联络线取多少电”,但绝不会把自己储能成本曲线或者燃气轮机爬坡速率交给对方。ATC的信息交互机制正好卡在这个边界上,这也是它在一众分布式算法中落地阻力较小的原因。
我在代码里把这种交互做成了一个基础函数:每个微网在求解自己的子问题时,只接收一个P_target向量、惩罚系数w和乘子lambda,其余全部从本地struct读取。这样任何一侧的模型调整都不会污染另一侧,调试时定位问题也很快。
2.3 目标函数设计:经济性为主、兼顾环保
优化调度最终要落到“最小化谁”的问题上。在每个微网的局部目标中,我通常组合四块成本:燃气轮机燃料成本、储能充放电损耗等效成本、与上级电网购售电成本、碳排放成本。
C_local = Σ_t [ C_MT(t) + a_bat · (P_ch(t) + P_dis(t)) + price_buy(t)·P_buy(t) - price_sell(t)·P_sell(t) + C_carbon(t) ]
其中储能损耗用充放电功率的线性组合近似,碳成本按出力的碳排放系数折算成货币成本。这里有一个实用经验:多目标不要试图在每个子问题里做复杂的Pareto寻优,直接把环境成本折算进统一货币口径,求解器负担小,也符合电力市场机制里“碳排放有价”的底层逻辑。折算系数怎么给?可以参考碳市场交易价格或者上级的碳排放考核红线,取一个可接受的红线成本即可。
全局目标函数是所有微网局部目标的简单加和。ATC之所以能在这个结构下运转,是因为全局目标和局部目标之间是天然可分离的,协调层只需要处理耦合变量的偏差,而不用重新定义全局偏好。这一点在设计时要格外注意:如果某个主体的目标函数出现跨主体的非分离项(比如希望共享总成本按比例分摊),那就要把这种关系提炼成新的边界变量,让ATC重新协调,而不是硬塞进局部目标里。
3. Matlab代码实现的完整流程
3.1 程序整体架构与数据组织
实际写代码时,我把整个项目拆成了五个层次的模块。最上层是主循环,负责调度ATC迭代;中间层是协调器更新模块;底层是单微网求解函数;数据层集中管理所有输入;后处理层负责画图和结果导出。每个模块单独一个文件,命名规则如下:
- main_atc.m:主程序,定义迭代次数、收敛阈值,串联整个流程
- data_input.m:录入微网群拓扑、各微网设备参数、预测曲线和电价
- solve_microgrid.m:单个微网的局部优化求解入口
- update_coordinator.m:协调层的目标值和乘子更新
- plot_results.m:联络线功率、SOC、出力的可视化
数据和参数统一用struct传递。这个设计在微网数量变化时特别省心——新增一个微网,只需要在data_input里增加一个元素,然后在主循环里多调用一次solve_microgrid,协调层逻辑完全不动。我自己吃过“全局变量满天飞”的亏,后来强制所有函数都走参数传递,虽然代码看起来“啰嗦”了一点,但排错时间至少省了一半。
主程序框架如下:
%% 主程序:基于目标级联法的微网群分布式优化调度 clc; clear; close all; addpath('./data'); addpath('./functions'); % 数据导入 data = data_input(); % ATC参数 MAX_ITER = 150; eps = 1e-3; alpha = 0.5; % 目标值松弛系数 beta = 1.2; % 罚权重递增率 w0 = 1e-3; % 初始罚权重 % 初始化协调变量 T = data.T; nMG = data.nMG; P_target = zeros(nMG, T); lambda = zeros(nMG, T); w = w0 * ones(nMG, T); % ATC迭代主循环 for iter = 1 : MAX_ITER P_resp = zeros(nMG, T); for k = 1 : nMG P_resp(k, :) = solve_microgrid(data.mg(k), P_target(k,:), lambda(k,:), w(k,:)); end dev = max(abs(P_resp - P_target), [], 'all'); fprintf('iter=%3d, 最大偏差=%.4e\n', iter, dev); if dev < eps fprintf('收敛于第 %d 次迭代\n', iter); break; end [P_target, lambda, w] = update_coordinator(P_target, P_resp, lambda, w, alpha, beta); end3.2 单微网局部优化子问题求解
solve_microgrid是每个微网的“黑盒”,协调层不关心它内部怎么实现,只要它接收目标、返回响应。这里最关键的是把ATC惩罚项正确嵌入到局部目标函数里。
局部优化问题的决策变量是燃气轮机出力、储能充放电功率、购售电功率和联络线功率构成的序列。我习惯把决策变量排列成一个向量,这样fmincon、quadprog都能直接处理。目标函数在本地成本之上增加偏差惩罚:
Obj_local = C_local + Σ_t [ lambda(t) · (P_tie(t) - P_target(t)) + w(t) · (P_tie(t) - P_target(t))^2 ]
注意这里的乘子项带的是P_tie与P_target的差,平方项让它变成一个强凸惩罚。平方项保证迭代初期稳定性,乘子项保证收敛后尽量精确定位到协调目标上。两者配合是标准增广拉格朗日结构的简化版,ATC的收敛性在这个结构下能有保障。
子问题约束包括功率平衡等式、设备出力上下限、SOC递推、联络线容量。功率平衡等式写起来最直观:发电机出力加购电加放电,减去光伏风电、充电和本地负荷,最后流入联络线的功率要等于P_tie。这个等式必须严格满足,否则算出来的调度方案物理上不成立。
fmincon求解的示例代码如下:
function [P_tie_opt, obj] = solve_microgrid(mg_data, P_target, lambda, w) T = mg_data.T; nVar = 5 * T; % P_mt, P_ch, P_dis, P_tie, P_curtail % 决策变量下界和上界 lb = [mg_data.P_mt_min * ones(1,T), zeros(1,T), zeros(1,T), -mg_data.tie_max*ones(1,T), zeros(1,T)]; ub = [mg_data.P_mt_max * ones(1,T), mg_data.P_ch_max*ones(1,T), mg_data.P_dis_max*ones(1,T), ... mg_data.tie_max*ones(1,T), mg_data.P_pv*ones(1,T)]; % 线性等式约束:功率平衡,Aeq*x = beq % 这里 x = [P_mt(1:T), P_ch(1:T), P_dis(1:T), P_gap(1:T), P_curtail(1:T)] % 功率平衡:P_mt + P_pv + P_dis - P_ch - P_curtail = P_load + P_gap Aeq = [eye(T), eye(T), -eye(T), -eye(T), eye(T)]; % 注意符号方向按变量排列 beq = mg_data.P_load'; options = optimoptions('fmincon', 'Algorithm', 'sqp', 'Display', 'off', ... 'MaxIterations', 300, 'OptimalityTolerance', 1e-6, 'ConstraintTolerance', 1e-6); obj_fun = @(x) local_objective(x, mg_data, P_target, lambda, w); nonlcon = @(x) local_constraints(x, mg_data); [x_opt, obj] = fmincon(obj_fun, x0, [], [], Aeq, beq, lb, ub, nonlcon, options); P_tie_opt = x_opt(4*T+1:5*T)'; % 根据变量顺序提取联络线功率 end这里有一点值得提醒:决策变量顺序一改,Aeq和lb/ub都要同步改,否则程序报错不算最难受的,最难受的是报出的结果看起来合理但物理上违反约束。我在第一次搭框架时把P_tie放在第三组位置,结果Aeq写错两行,花了整整一个下午才通过SOC曲线发现问题。后来我再也不手写这种大块矩阵,全部用循环生成,既清晰又不容易错。
3.3 协调层主循环与参数更新
协调器更新在ATC里是相对简单的部分,但它决定了整个迭代过程稳不稳。目标值更新使用的是带松弛的固定点迭代:
P_target_new = alpha · P_resp + (1 - alpha) · P_target
这里的alpha是松弛因子。如果alpha取得太接近1,目标值追着响应跑,前后两轮可能剧烈交替;如果取得太小,收敛会慢很多。实践经验里0.4到0.6都算合理区间,我大多数算例用0.5起步,振荡明显就往0.3调。
乘子和罚权重的更新是标准的增广拉格朗日方式。每轮迭代先按残差更新乘子,然后让罚权重按beta倍递增。这个递增非常重要,因为ATC只有在罚权重足够大时,才能把“偏差项”压到收敛阈值以下;但beta不能太大,否则子问题会变得病态,fmincon数值稳定性下降。我会让beta在1.1到1.5之间选,1.2是一个不怎么需要动脑筋的稳妥值。
收敛判据我用的是所有微网、所有时段里响应值与目标值的最大偏差的无穷范数,也就是max(abs(P_resp - P_target))。这个指标比平均偏差要严格得多,因为只要任何一个微网在任何一个时段偏移大,就会显式暴露出来。曾经我试过用平均偏差做判据,迭代过程中平均偏差早早降到阈值以下,但个别时段还差着几十千瓦,最终结果根本没法用。后来换回无穷范数,问题当场暴露。
4. 案例测试与结果分析
4.1 测试算例与运行环境
我用来验证的算例是一个三微网互联系统。MG1配置光伏和储能,负荷曲线有明显早晚高峰,同时带一台微型燃气轮机作为灵活调节电源;MG2以风电为主,负荷水平中等;MG3没有分布式电源,完全依赖上级电网购电和与相邻微网的功率交互,负荷波动最大。三个微网通过两条联络线两两相连,形成一个三角形拓扑。微网群的上级接入点设在MG1,MG2和MG3通过MG1的中转实现间接交互。
分时电价采用三时段结构:深夜低谷段0.45元/kWh,白天平段0.85元/kWh,晚高峰段1.2元/kWh。这个电价结构虽然简单,但足够激发出储能的套利行为和微网间的功率互济行为。全部算例在某个实验室工作站上运行,Matlab版本为R2020a,子问题用fmincon的sqp算法,未使用商业求解器。
4.2 迭代收敛过程观察
运行主程序后,前几轮迭代的偏差下降非常快,这是平方惩罚项的“强吸引”在起作用。前10次迭代偏差从初始的几十千瓦迅速压到5千瓦以内;之后进入缓慢调整期,每次迭代的偏差降幅明显变小;到70次左右,偏差已经降到1千瓦以下;第85次迭代时最大偏差首次跌破1e-3千瓦的量级,程序判敛退出。我保留了某次完整运行的过程数据,摘出关键迭代记录如下:
| 迭代次数 | 联络线最大偏差(kW) | 全局目标值(元) |
|---|---|---|
| 1 | 28.40 | 25320 |
| 10 | 4.80 | 18640 |
| 30 | 0.72 | 17760 |
| 50 | 0.16 | 17690 |
| 70 | 0.035 | 17640 |
| 85 | 0.0008 | 17618 |
这个收敛曲线呈典型的两阶段特征:前期快速收缩偏差,中期稳步提升目标值精度,后期微调收敛退出。从联络线功率曲线看,前几次迭代中MG3刚刚开始向MG1送电时方向都拧着来,两个微网在联络线上一个想流入、一个想流出,协调器不断修正目标,到第40次迭代时两侧已经完全镜像对齐,后半个调度周期再没有出现方向矛盾。储能SOC曲线则呈现出非常标准的“低谷充电、高峰放电”走势,这说明目标函数的电价信号传导正常,没有因为分布式分解而失效。
4.3 与集中式最优解的对比验证
衡量分布式算法做得好不好,最优性间隙是最有说服力的一项指标。我在同一套数据上搭建了集中式优化模型,不设任何信息隐私限制,直接把三个微网的所有变量和约束合并,让fmincon一次性求解全局最优,然后与ATC的分布式结果对比。结果如下:
| 方案 | 总运行成本(元) | 联络线总电量(kWh) | 求解耗时(s) |
|---|---|---|---|
| 集中式 | 17550 | 326 | 3.2 |
| ATC(分布式) | 17618 | 324 | 46.8 |
ATC得到的全局目标值与集中式最优解的差距约为0.39%,在工程上完全可接受。这个间隙主要来自两方面:其一,ATC是对KKT一阶条件的近似,不像集中式直接解原问题,偏差残差虽然被压到阈值以下,但不会完全消失;其二,罚权重在迭代后期变得很大,会让局部解产生微小的“拖拽效应”,使联络线功率偏离理论最优值几个百分点。想进一步压缩间隙,可以调大最后一次迭代的罚权重,或者修正松弛因子做一到两次精修正。
耗时46.8秒和三微网3.2秒对比起来显得很夸张,但这是分布式协调的代价。需要说明的是,MATLAB中的子问题求解并未做并行化;如果换用parfor让三个微网同时求解,每个微网单独耗时才4秒多,总耗时能压到10秒上下。真实工程的可用性取决于另一个维度的优势:集中式需要收集所有主体的私有数据并建模,这个过程可能要谈几周;分布式方案里各主体只需要维护自己的子模块,协调器只处理边界变量,新增微网也只需在数据文件里加记录,后续扩容的边际成本非常低。
5. 常见问题与调试技巧实录
5.1 迭代不收敛或反复振荡的排查
ATC调试中遇到过最典型的问题就是偏差曲线不降反升,或者在某个区间来回震荡。出现这种情况不要急着改算法,先逐层排查。
第一步检查单个微网的子问题在固定目标下是否能稳定得到最优解。方法很简单:固定一组P_target,连续调用两次solve_microgrid,如果两次输出存在明显差异,说明子问题本身不稳定,大概率是初值设置问题或者约束写成了病态结构。第二步检查松弛因子alpha。alpha过大时协调器目标追着响应跑,迭代极易过冲,表现就是偏差在几个值之间来回跳。我实际遇到过alpha设成0.9后,偏差从0.5跳到6再跳回3的振荡;降到0.4后立刻恢复单调收敛。第三步检查罚权重递增速度。beta设成2.0这套路,前十轮看着还行,后面罚项的主导作用会把子问题推向可行域边界,让fmincon硬怼出数值不可行。稳妥做法是beta取1.1到1.2,宁可多迭代二十轮,也不换来数值崩溃。
5.2 罚系数与松弛因子的整定经验
这部分参数选择直接决定程序跑不跑得过,我整理了一张常用的调参清单,大家可以照着试:
| 参数 | 建议范围 | 初始尝试值 | 调整方向 |
|---|---|---|---|
| 初始罚权重 w0 | 1e-4 ~ 1e-1 | 1e-3 | 子问题数值太大时减小,偏小收敛慢时增大 |
| 罚权重递增率 beta | 1.1 ~ 1.5 | 1.2 | 振荡时降低,收敛太慢时可适度增大 |
| 目标松弛因子 alpha | 0.3 ~ 0.7 | 0.5 | 振荡明显时降到0.3,收敛太慢时提高 |
| 收敛阈值 eps | 1e-4 ~ 1e-2 | 1e-3 | 精度要求高时调小,注意迭代次数同步增加 |
还有一个容易被忽略的技巧:w0的初始大小应该和局部目标函数梯度的量级匹配。如果目标函数里的成本动辄是几万元量级,而初始罚权重只有1e-5,前几十轮迭代偏差根本不会被惩罚项拉动,白白浪费时间。可以先跑一轮完全不带惩罚项的子问题,观察目标函数值量级,再按目标量级的千分之一左右设置w0。
5.3 代码实现容易踩的坑
维度错位是Matlab代码里最隐蔽的敌人。P_target、P_resp、P_load的方向不一致,表面上看不出问题,跑起来后联络线功率的数值却面目全非。我的经验是:所有时序变量统一按行向量组织,也就是1×T,在进入fmincon的约束矩阵时再转置。不要在函数内部玩转置游戏,转来转去迟早出错。
fmincon的非线性约束建议写成独立的local_constraints函数,不要塞进目标函数一起算。否则每次求目标函数都要顺带计算一遍约束,数值梯度计算时既慢又容易报错。SOC递推约束写成非线性的形式没问题,但要用状态变量逐时段迭代,别把SOC直接放进决策变量,那样会让问题规模膨胀,求解难度陡增。
最后提一个并行化的细节。Matlab的parfor在ATC中很实用,三个微网并行求解,整体耗时能压缩近三倍。但parfor循环里不能把结果写入工作区普通变量,必须用切片变量或者输出变量收集。另外如果子问题里调用了费时的工具箱函数,最好先本地测试一遍,再上并行,不然出错了很难定位是哪个worker的问题。
这套代码在我后续扩展多时间尺度调度时也派上了用场,只需要把状态变量从24小时扩展成96点,再在协调层增加不同时间尺度的目标更新即可。如果你正在做微网群调度,建议先把ATC跑通一版24小时算例,把协调器、子问题、惩罚项这三大块理解透,再去追求更花哨的不确定性和滚动优化扩展。扎实的基础框架,比任何技巧都值得多花时间。