ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

MATLAB实现微电网两阶段鲁棒优化:CCG算法与YALMIP/CPLEX实战

MATLAB实现微电网两阶段鲁棒优化:CCG算法与YALMIP/CPLEX实战 简介本资源是一套面向电力系统优化方向研究生与科研人员的原创MATLAB代码完整复现《中国电机工程学报》中微电网两阶段鲁棒经济调度模型聚焦分布式电源与负荷不确定性下的最恶劣场景成本最小化决策问题。压缩包共8个文件6个核心m脚本、1个说明文档docx、1个文本说明txt总大小272KB涵盖主问题建模、子问题求解、KKT条件转化、场景生成、结果可视化等关键模块结构清晰、注释详尽所有函数分工明确、可读性强。已有7736人学习下载代码经作者与原文通讯作者邮件确认修正了文献中的笔误确保模型严格可解并支持任意随机初始化光伏与负荷场景下的稳定收敛。使用者可直接运行获得调度方案、成本曲线与鲁棒边界图示同时掌握CCG算法在两阶段鲁棒优化中的工程实现逻辑与YALMIPCPLEX协同建模技巧。1. 项目概述微电网两阶段鲁棒优化的核心价值在能源系统领域微电网的优化调度一直是个经典且充满挑战的课题。传统的确定性优化模型往往基于对未来风光出力、负荷需求的精确预测。然而现实世界充满了不确定性——一阵突如其来的云层、一场计划外的设备检修都可能让“完美”的调度方案瞬间失效。这正是鲁棒优化Robust Optimization大显身手的地方。它不追求在“最理想”场景下的最优解而是致力于寻找一个能够抵御最坏情况Worst-case Scenario的“免疫”方案确保系统在不确定性扰动下依然安全、经济地运行。我这次分享的正是一套基于MATLAB平台结合YALMIP建模工具和CPLEX求解器完整实现微电网两阶段鲁棒优化Two-Stage Robust Optimization的原创代码。这个项目不是简单的模型调用而是从底层原理出发完整复现了“主问题-子问题”的列与约束生成Column-and-Constraint Generation, CCG算法框架。对于从事电力系统优化、能源管理、运筹学研究的同行或者希望深入理解鲁棒优化如何落地的工程师来说这套代码和实现思路提供了一个清晰、可操作、可直接复现的范本。它能帮你透彻理解两阶段决策“这里-现在”的调度决策 vs. “那里-那时”的实时调整在面对不确定性时的协同机制并亲手搭建起从数学模型到可执行代码的桥梁。2. 核心思路与算法框架拆解2.1 两阶段鲁棒优化模型的内涵要理解代码必须先吃透模型。两阶段鲁棒优化将决策变量分为两类第一阶段决策Here-and-Now Decisions在不确定性揭示之前必须做出的决策。在微电网中这通常对应着日前调度计划比如各分布式发电机柴油发电机、微型燃气轮机的启停状态和计划出力、与主网的联络线计划交换功率、储能系统的计划充放电功率等。这些决策一旦做出在调度周期内通常是刚性或调整成本很高的。第二阶段决策Wait-and-See Decisions在不确定性风光出力、负荷的实际值被观测到之后可以进行的调整决策。这对应着实时调度或平衡措施比如柴油发电机和微型燃气轮机的出力微调、储能的实时充放电、切负荷如果允许等。第二阶段决策是适应性的目标是弥补第一阶段决策因不确定性造成的偏差并最小化实时运行成本。模型的目标是最小化第一阶段成本与最坏不确定性场景下的第二阶段成本之和。这就引出了“鲁棒”的核心——我们不是针对某一个预测场景优化而是针对所有可能的不确定性场景通常描述在一个有界的集合内即“不确定集”中的那个“最坏”场景进行优化以确保任何情况下方案都可行且成本可控。2.2 列与约束生成CCG算法流程面对这种min-max-min结构的三层优化问题外层最小化总成本中层最大化不确定性以寻找最坏场景内层最小化第二阶段成本直接求解非常困难。CCG算法通过主问题Master Problem, MP和子问题Subproblem, SP的迭代巧妙地将其分解。算法流程如下初始化设定一个空的“场景”集合设定收敛容差ε设定迭代次数k0。求解主问题MP主问题是一个混合整数线性规划MILP。它基于当前已知的有限个最坏场景初始为空求出一个第一阶段决策方案并给出当前方案下的目标值下界LB。核心思想主问题假设不确定性只存在于我们已发现的这几个“坏”场景里因此它求出的解是相对乐观的成本更低这个成本值就是下界。求解子问题SP在固定主问题求出的第一阶段决策后子问题是一个双层优化外层在不确定集中寻找一个最坏的不确定性场景内层在该场景下优化第二阶段决策以使总成本第一阶段成本该场景下第二阶段成本最大化。核心思想子问题检验当前的第一阶段决策看是否存在某个我们尚未考虑的不确定性场景能让总成本变得更高更坏。这个最大化后的总成本值就是当前第一阶段决策在实际上面临所有不确定性时的真实成本上界UB。关键技巧通过强对偶理论Strong Duality或KKT条件可以将这个双层max-min问题转化为一个单层的最大化问题通常是MILP或线性规划LP从而可用CPLEX高效求解。收敛性判断计算gap (UB - LB) / UB。如果gap ≤ ε算法收敛当前第一阶段决策即为鲁棒最优解。否则进入下一步。添加约束与场景将子问题求解得到的最坏不确定性场景作为一个新的“场景”添加到主问题的约束集中。这相当于告诉主问题“你刚才的方案在这个新场景下会很糟糕请重新找一个能应对这个场景的方案。” 然后令kk1返回步骤2。这个过程不断迭代主问题的解下界LB因为要应对越来越多的坏场景而不断上升子问题找到的针对当前解的最坏场景成本上界UB则不断下降直至两者足够接近。注意子问题的求解是算法效率和精度的关键。对于线性模型利用对偶转化是标准做法。如果模型中包含整数第二阶段变量如启停子问题会变成MILP max问题求解难度增加可能需要采用其他分解策略。3. 开发环境搭建与工具链详解3.1 MATLAB算法原型与矩阵运算核心MATLAB是本项目的“大脑”和“调度中心”。它的核心优势在于其强大的矩阵运算能力和丰富的内置数学函数使得算法原型的实现和验证变得非常高效。我们主要利用它来组织算法流程编写CCG算法的主循环控制MP和SP的迭代调用。处理输入输出数据读取网络参数、设备成本、风光负荷预测区间等。进行中间计算如形成约束矩阵、目标函数系数向量等。可视化结果绘制收敛曲线、调度计划图、成本分析图等直观验证算法效果。版本选择建议R2019b及以上版本均可。高版本对大型稀疏矩阵的处理和内存管理更优。特别注意需要安装Optimization Toolbox因为YALMIP的某些内部函数会依赖它。3.2 YALMIP从数学建模到求解器调用的“翻译官”YALMIP是一个基于MATLAB的建模语言它的价值在于让你像写数学公式一样描述优化问题而无需关心底层求解器复杂的API调用。在本项目中它至关重要统一建模无论是主问题的MILP还是子问题转化后的MILP/LP都可以用近乎数学符号的方式sdpvar定义变量,,定义约束objective定义目标来构建。求解器抽象写好模型后只需一行optimize命令YALMIP会自动根据问题类型LP, MILP, QP等调用你配置的求解器如CPLEX并将结果返回到你定义的变量中。处理对偶变量在子问题转化时我们需要获取第二阶段问题的对偶变量。YALMIP可以很方便地在求解后提取这些对偶变量这是实现CCG算法的关键一步。安装从YALMIP官网下载最新版本将文件夹添加到MATLAB路径并在命令行执行yalmiptest来验证安装和检测可用求解器。3.3 IBM ILOG CPLEX高性能数学规划求解引擎CPLEX是业界顶尖的商业数学规划求解器尤其擅长求解大规模、复杂的线性规划LP、混合整数线性规划MILP和二次规划QP问题。我们的主问题和转化后的子问题最终都会交给CPLEX求解。为什么是CPLEX相比于MATLAB自带的intlinprogCPLEX在处理包含大量整数变量和约束的MILP问题时其预设的切割平面Cutting Planes策略、启发式算法和并行计算能力要强大得多能显著缩短求解时间提高找到最优解的可靠性。对于鲁棒优化这种需要迭代求解数十甚至上百个MILP的问题求解器效率直接决定了项目的可行性。版本与安装需要安装CPLEX Optimization Studio并获得有效许可证。安装后在MATLAB中运行addpath(genpath(你的CPLEX安装路径/cplex/matlab))来添加CPLEX的MATLAB接口。之后在YALMIP中可以通过sdpsettings(solver, cplex)指定使用CPLEX。环境配置检查清单MATLAB含Optimization Toolbox安装成功。YALMIP文件夹已添加至MATLAB路径yalmiptest能识别出CPLEX。CPLEX的MATLAB接口路径已添加YALMIP能成功调用cplex作为求解器。可选但推荐安装一个代码编辑器如VSCode配合MATLAB扩展进行代码编写体验更佳。4. 微电网系统建模与不确定集定义4.1 微电网物理模型与约束我们的微电网模型包含以下典型单元每个单元都需要用数学约束来描述其物理特性和运行限制分布式发电机如柴油机、微型燃气轮机约束出力上下限、爬坡率约束相邻时段出力变化限制。成本通常为二次或分段线性函数这里为简化可采用线性成本系数/kWh。整数变量启停状态0/1变量是构成MILP的关键。可再生能源光伏、风机模型其出力被视为不确定性源是鲁棒优化的核心扰动项。我们不对其进行确定性预测而是给定一个预测区间[P_min, P_max]。约束在子问题中其实际出力可在不确定集内自由变化以制造最坏场景。储能系统电池约束充放电功率上下限、储能状态SOC上下限、SOC动态方程SOC(t1) SOC(t) (η_charge * P_charge - P_discharge/η_discharge) * Δt、充放电互斥约束同一时刻不能既充又放可通过整数变量或互补约束实现。成本通常考虑折旧成本或简化为充放电效率损耗。可中断负荷模型允许在极端情况下以高昂代价切除部分负荷作为维持系统平衡的最后手段。这是一个第二阶段决策变量。约束切负荷量不超过该时段总负荷的一定比例。成本切负荷惩罚成本通常远高于发电成本以体现供电可靠性价值。与主网的联络线约束交换功率上下限。购电成本通常高于自发电售电价格可能低于成本。作用为微电网提供重要的功率平衡和灵活性支撑。系统平衡约束这是最核心的等式约束在每一个调度时段t如1小时必须满足∑发电机出力 ∑可再生能源实际出力 联络线购电 储能放电 总负荷 - 切负荷 联络线售电 储能充电这个等式约束将第一阶段和第二阶段变量、确定性和不确定性变量耦合在一起。4.2 不确定集的数学描述不确定集的定义直接决定了鲁棒优化的“保守度”。太大会导致方案过于保守、成本高昂太小则可能无法覆盖真实的不确定性失去鲁棒性。本项目采用最经典且可处理的盒式不确定集Box Uncertainty Set或多面体不确定集Polyhedral Uncertainty Set。盒式不确定集最简单假设每个时段的风光出力不确定性是独立的且在其区间内任意取值。U_box { P_wind(t), P_pv(t) | P_wind_min(t) P_wind(t) P_wind_max(t), for all t }这种集合下的最坏场景通常发生在所有可再生能源同时取最小值对于缺电系统或最大值对于弃电严重的系统。多面体不确定集预算不确定集更符合实际它通过一个“预算参数”Γ来限制所有时段的总不确定性偏差允许某些时段偏差大某些时段偏差小。U_budget { P_wind(t), P_pv(t) | P_wind(t) P_wind_nom(t) ΔP_wind(t) * z_wind(t), |z_wind(t)| 1, ∑_t |z_wind(t)| Γ_wind }其中z是标准化后的不确定性变量Γ是保守度参数。Γ0退化为确定性Γ等于总时段数则退化为盒式集合。这种集合下的最坏场景需要通过优化子问题来寻找通常不是简单的边界值。实操选择在代码实现中我们采用了预算不确定集因为它能通过Γ灵活调节鲁棒性并且其对应的子问题经过对偶转化后仍然是一个易于求解的MILP当第二阶段问题为线性时。5. 代码结构解析与核心模块实现5.1 项目文件结构与数据流一个清晰的项目结构是代码可复现、可维护的基础。我的项目目录如下Microgrid_TwoStage_Robust_Optimization/ ├── data/ │ ├── load_profile.csv # 基础负荷数据 │ ├── pv_profile.csv # 光伏预测基准值与偏差范围 │ ├── wind_profile.csv # 风电预测基准值与偏差范围 │ └── system_parameters.m # 发电机、储能、成本等参数脚本 ├── src/ │ ├── main_robust_optimization.m # 主程序入口CCG算法主循环 │ ├── build_master_problem.m # 构建主问题MILP的函数 │ ├── solve_master_problem.m # 调用YALMIP/CPLEX求解主问题 │ ├── build_subproblem.m # 构建子问题双层的函数 │ ├── solve_subproblem.m # 求解子问题转化并求解的函数 │ ├── duality_transformation.m # 子问题对偶转化核心函数 │ ├── update_master_with_scenario.m # 向主问题添加新场景约束 │ └── plot_results.m # 结果可视化函数 ├── results/ │ ├── iteration_log.txt # 算法迭代日志LB, UB, Gap, Time │ └── figures/ # 保存生成的图片 └── README.md # 项目说明文档数据流main脚本首先从data/文件夹加载所有参数和预测数据。然后进入CCG循环在每次迭代中调用build_master_problem构建或更新主问题模型通过solve_master_problem求解得到第一阶段决策和下界LB接着固定该决策调用build_subproblem构建子问题模型通过solve_subproblem其内部会调用duality_transformation求解得到最坏场景和上界UB判断收敛若不收敛则调用update_master_with_scenario将新场景添加到主问题模型继续迭代。最后调用plot_results进行可视化。5.2 主问题Master Problem构建详解主问题是一个包含已知“坏场景”集合的MILP。随着迭代约束会越来越多。决策变量定义YALMIP实现:% 第一阶段变量 x_binary binvar(N_gen, T, full); % 发电机启停状态N_gen台发电机T个时段 x_power sdpvar(N_gen, T, full); % 发电机计划出力 x_ess sdpvar(2, T, full); % 储能计划充/放电功率 [充电; 放电] x_grid sdpvar(1, T, full); % 联络线计划交换功率正为购电 % 辅助变量用于线性化最坏情况成本在CCG中 eta sdpvar(1, 1); % 表示最坏场景下的第二阶段成本 % 第二阶段变量对于每个已添加的场景s y_power_adj sdpvar(N_gen, T, S, full); % 发电机出力调整量 y_curtail sdpvar(1, T, S, full); % 弃风弃光量可选 y_load_shed sdpvar(1, T, S, full); % 切负荷量约束构建:第一阶段物理约束发电机出力上下限与启停状态关联、爬坡约束、储能SOC动态约束、联络线功率限值等。这些约束只涉及第一阶段变量。constraints []; % 示例发电机出力约束 for g 1:N_gen for t 1:T constraints [constraints, ... gen_min(g) * x_binary(g, t) x_power(g, t) gen_max(g) * x_binary(g, t)]; end end耦合约束对于每个场景s这是CCG算法的精髓。对于每一个已经发现的最坏场景s其可再生能源出力P_wind_s(t),P_pv_s(t)是已知常数我们需要添加约束使得存在对应的第二阶段调整变量满足系统平衡和所有第二阶段约束并且其成本不大于eta。for s 1:length(scenario_set) % 针对场景s的第二阶段变量约束 % 功率平衡约束包含第一阶段决策和场景s下的实际风光出力 for t 1:T constraints [constraints, ... sum(x_power(:, t)) x_grid(t) P_wind_s(t, s) P_pv_s(t, s) ... y_power_adj_sum(t, s) y_ess_discharge(t) ... % 第二阶段调整和储能放电 load(t) - y_load_shed(t, s) y_ess_charge(t)]; % 负荷和储能充电 end % 第二阶段调整量约束如发电机调整限值、切负荷上限等 constraints [constraints, -ramp_limit y_power_adj(:, :, s) ramp_limit]; constraints [constraints, 0 y_load_shed(:, :, s) load_max_shed]; % 关键约束该场景下的总成本第一阶段成本第二阶段成本 eta cost_stage1 ... % 计算基于x的第一阶段成本 cost_stage2_s ... % 计算场景s下的第二阶段成本基于y_xxx(:,:,s) constraints [constraints, cost_stage1 cost_stage2_s eta]; end目标函数minimize( eta )。主问题的目标就是最小化这个代表所有考虑场景中最坏情况成本的eta。5.3 子问题Subproblem构建与对偶转化子问题是整个算法中最具技巧性的部分。给定第一阶段决策x*从主问题获得子问题为SP(x*): maximize_{u in U} minimize_{y} (第二阶段成本)其中u是不确定性变量风光出力y是第二阶段变量。步骤1构建内层最小化问题。固定x*和一组具体的u内层问题是一个线性规划LP求解除x*和u外使第二阶段成本最小的y。% 内层问题对于给定的u y sdpvar(...); % 定义第二阶段变量 constraints_inner []; % 添加第二阶段约束功率平衡含固定的x*和u、调整量限制、切负荷限制等。 objective_inner ... % 第二阶段成本函数 inner_problem optimizer(constraints_inner, objective_inner, sdpsettings(solver, cplex), {u}, {y, objective_inner});但这样直接求解双层问题效率低下。步骤2利用强对偶理论转化。由于内层问题是一个线性规划且对于固定的u是可行的假设原问题设计合理那么它的对偶问题的最优值等于原问题的最优值。而最大化一个最小化问题可以转化为最大化其对偶问题的目标函数并且对偶变量和原变量u的约束可以合并到一个单层最大化问题中。设内层LP的标准形式为min_y c^T y, s.t. A y b - B u, y 0.其中B u项体现了不确定性u对约束右端项的影响。其对偶问题为max_π π^T (b - B u), s.t. A^T π c, π 0.那么原双层子问题max_u min_y c^T y等价于max_{u, π} π^T (b - B u) 约束为u in U,A^T π c,π 0。步骤3在YALMIP中实现转化后的问题。转化后子问题变成了一个关于u和π的双线性规划目标函数中π和u相乘。幸运的是对于盒式或预算不确定集u通常有线性约束且B u是线性的。目标函数π^T B u是双线性的。如果u是连续变量这仍然是非凸的。但当不确定集U是多面体如预算不确定集并且我们利用其标准形式u u_nom Δu * z, |z|1, sum|z|Γ时可以通过引入额外的辅助变量和约束将双线性项线性化最终将子问题转化为一个混合整数线性规划MILP。这是CCG算法能有效求解的关键。在我的代码duality_transformation.m中详细实现了这一线性化过程。核心是引入新的变量w π * z其中z是标准化不确定性变量并利用大M法或特殊有序集SOS约束来线性化这个乘积关系最终得到一个完全由线性约束和线性/整数变量构成的MILP模型可以直接用CPLEX高效求解。步骤4求解与信息提取。求解转化后的MILP子问题得到最优解u*即当前迭代的最坏场景和最优目标值obj_sub。这个obj_sub加上固定的第一阶段成本就是当前解的上界UB。u*将被传递给主问题用于生成新的场景约束。6. 算法迭代、收敛与结果分析6.1 主程序循环与收敛控制主程序main_robust_optimization.m的结构是一个清晰的while循环%% 初始化 LB -inf; % 下界初始为负无穷 UB inf; % 上界初始为正无穷 gap inf; tolerance 1e-4; % 收敛容差例如0.01% iteration 0; scenario_set []; % 存储已发现的最坏场景 %% CCG 主循环 while gap tolerance iteration iteration 1; fprintf(--- 迭代 %d ---\n, iteration); % 步骤1求解主问题 [x_opt, eta_opt, LB_new, solve_status_mp] solve_master_problem(scenario_set, data); if ~strcmp(solve_status_mp, Successfully solved) error(主问题求解失败); end LB max(LB, LB_new); % 下界是非递减的 % 步骤2固定x_opt求解子问题 [worst_scenario, obj_sub, UB_new, solve_status_sp] solve_subproblem(x_opt, data); if ~strcmp(solve_status_sp, Successfully solved) error(子问题求解失败); end UB min(UB, UB_new); % 上界是非递增的 % 步骤3计算间隙 gap abs(UB - LB) / UB; fprintf(LB%.2f, UB%.2f, Gap%.4f%%\n, LB, UB, gap*100); % 步骤4收敛判断 if gap tolerance fprintf(算法收敛于迭代%d。\n, iteration); break; end % 步骤5添加新场景到集合 scenario_set [scenario_set, worst_scenario]; % 可选防止无限循环 if iteration max_iterations warning(达到最大迭代次数强制终止。); break; end end关键细节下界LB更新取max(LB, LB_new)因为主问题随着约束增加目标值eta只会增大或不变越来越保守。上界UB更新取min(UB, UB_new)因为子问题找到的是当前解对应的最坏成本而算法寻找的是所有第一阶段决策中最小的最坏成本所以上界是下降或不变的。收敛判断通常采用相对间隙(UB-LB)/UB。当它小于一个很小的正数如1e-4时认为找到了最优解。6.2 结果可视化与方案解读算法收敛后我们得到了鲁棒最优的第一阶段调度方案x_opt发电机启停计划、计划出力、储能计划、联络线计划以及一个收敛的UB即鲁棒最优总成本。可视化脚本plot_results.m通常包含收敛曲线图绘制LB和UB随迭代次数的变化曲线。可以清晰看到LB上升、UB下降并最终交汇的过程这是验证算法正确运行的最直观证据。日前调度计划甘特图用堆叠面积图展示各发电机、可再生能源、储能、联络线在每个时段的功率贡献。用阶梯图或散点图展示发电机组的启停状态。用曲线展示储能SOC的变化。成本构成分析饼图分析总成本中第一阶段燃料成本、启停成本、联络线购电成本、第二阶段调整成本、切负荷惩罚成本等各自的占比。与确定性优化对比作为效果验证可以用同样的模型但将风光出力设为预测期望值中点运行一次确定性优化。然后将确定性方案代入鲁棒优化的子问题中计算其在最坏场景下的成本。通常会发现确定性方案在最坏场景下的成本远高于鲁棒方案的成本甚至可能无法找到可行的第二阶段调整即子问题不可行这凸显了鲁棒优化的价值——用稍高的日前计划成本换取了应对极端情况的“保险”。方案解读要点鲁棒性体现观察鲁棒调度方案与确定性方案的差异。鲁棒方案通常会增加传统机组或联络线的备用以应对可再生能源可能的最低出力。更保守的储能调度可能在预期可再生能源不足的时段前储备更多能量。启停计划更稳定减少机组的频繁启停因为启停状态是刚性的在面对不确定性时缺乏灵活性。经济性权衡鲁棒最优成本UB一定会高于确定性最优成本。这个成本差就是为“不确定性”支付的保险费。决策者可以通过调整不确定集的大小如预算参数Γ在经济性和鲁棒性之间进行权衡。7. 性能调优、常见问题与避坑指南7.1 求解效率优化技巧CCG算法的计算时间主要消耗在反复求解MILP上。以下技巧可以显著提升效率提供初始可行解在第一次求解主问题前可以向YALMIP的求解器提供一个初始解例如用确定性优化得到的解。这能帮助CPLEX的启发式算法更快找到优质解加速根节点松弛。% 假设x0是一个初始解结构体 assign(x_binary, x0.binary); % 将初始值赋给YALMIP变量 assign(x_power, x0.power); options sdpsettings(solver, cplex, usex0, 1); optimize(constraints, objective, options);求解器参数调优强调可行性在早期迭代主问题可能较难求解。可以设置cplex.emphasis.mip 1(平衡最优性与可行性) 或cplex.emphasis.feasibility 1。设置时间/间隙限制对于大规模问题可以为每次MP/SP求解设置一个合理的时间限制或相对MIP间隙容忍度如cplex.timelimit300,cplex.mip.tolerances.mipgap0.01不一定每次都要找到绝对最优解只要迭代能稳步推进即可。并行计算启用CPLEX的并行模式cplex.parallel 1或cplex.threads设置为物理核心数。模型简化减少整数变量仔细检查是否所有整数变量都是必需的。例如储能的充放电互斥约束有时可以用线性互补约束近似避免引入0-1变量。收紧变量边界根据物理意义尽可能给变量设定紧的上下界这能极大缩小搜索空间。添加有效不等式根据问题结构添加一些能帮助收紧线性松弛的约束可以加速分支定界过程。例如对于机组组合可以添加最小开/停机时间约束的加强形式。算法层面“快-慢”迭代策略前几次迭代使用较宽松的求解器容忍度快速获取一些坏场景接近收敛时再提高求解精度以获得精确解。场景管理迭代中产生的场景可能很多。可以定期检查并移除一些非活跃的或非常相似的场景以控制主问题规模。7.2 典型错误与排查方法问题不可行Infeasible主问题首次迭代就不可行检查第一阶段约束是否自相矛盾如机组最小出力大于负荷。检查不确定集定义是否合理风光出力下限是否过低导致任何第一阶段决策都无法满足平衡。添加新场景后主问题不可行这理论上不应发生因为新场景是从一个可行的子问题中产生的。如果出现极有可能是子问题对偶转化或线性化过程有误导致找到的“最坏场景”u*不真实。需要仔细调试duality_transformation.m用一个小例子验证对偶转化前后问题的等价性。排查方法当问题不可行时使用optimize(constraints, objective, options)后用yalmiperror(errorcode)查看错误代码。更有效的是使用求解器的不可行性分析工具如CPLEX的conflict refiner找出导致不可行的最小约束集。算法不收敛或震荡间隙始终很大检查不确定集是否过大导致问题过于保守上下界本身差距就很大。尝试减小预算参数Γ。LB和UB在某个值附近震荡这可能是求解器精度问题或者子问题线性化不精确导致的。尝试收紧求解器的整数容忍度cplex.mip.tolerances.integrality和最优容差。检查对偶转化确保子问题转化后的MILP是正确的。可以手动构造一个固定的x*和几个极端场景u分别用原始双层问题和转化后的单层问题求解对比目标值是否一致。求解速度极慢检查问题规模时段数T、机组数N_gen、场景数S都会影响问题规模。对于研究性质的问题可以先从缩小规模的算例如T24, N_gen3开始调试。分析求解日志关注CPLEX的输出日志看时间主要消耗在“根节点松弛”还是“分支定界”上。如果根节点松弛就很慢可能是模型本身线性松弛质量差如果分支节点很多可能是整数变量多或结构复杂。使用YALMIP的调试功能optimize之前用debug函数查看约束数量、变量数量等信息。结果不符合预期如储能行为反常检查成本系数确保充放电成本、效率惩罚设置正确。如果放电“收益”设置过高模型可能会让储能无意义地循环充放电来套利。检查SOC约束确保SOC的初始值、上下限、动态更新方程正确无误。一个常见的错误是符号弄反。检查功率平衡约束这是最容易出错的地方。务必确认每一项的符号发电为正负荷为正充电为正/负并确保所有时段、所有场景的平衡约束都已正确添加。实操心得调试鲁棒优化代码是一个需要耐心的过程。建议采用“分而治之”的策略首先在一个确定性的场景下固定风光出力为期望值验证你的主问题模型不含CCG循环是否能给出合理的调度方案。然后手动构造一个极端场景如风光全为最小值验证你的子问题在给定一个简单第一阶段决策后能否正确找到这个极端场景并计算出高成本。最后再运行完整的CCG算法并从最简单的参数开始如Γ0它应该退化为确定性优化。每一步都做好结果的可视化和合理性检查能帮你快速定位问题所在。本文还有配套的精品资源点击获取
返回列表