首页 / 资讯中心 / 文章详情

数据中心微网两阶段鲁棒规划中的灵活性建模与Matlab求解

数据中心微网两阶段鲁棒规划中的灵活性建模与Matlab求解 ★ FEATURED ARTICLE
数据中心微网的规划问题,这两年在EI期刊上出现的频率非常高,尤其是考虑灵活性和两阶段鲁棒两个关键词组合在一起的时候,基本就是冲着解决实际工程痛点去的。我自己在复现这类文章的过程中,最大的感受是:模型本身并不算复杂,真正的门槛在于把鲁棒优化的嵌套结构、灵活性约束的数学表达,以及Matlab这边的求解框架三者对齐。这篇文章就围绕考虑灵活性的数据中心微网两阶段鲁棒规划方法这个题目,把我从读论文到写出可运行Matlab代码的完整思路、建模过程、求解工具和踩过的坑一次性讲清楚。适合正在做微网规划方向的研究生、刚接触鲁棒优化的工程师,以及想用现成代码跑通一个完整案例的读者参考。1. 数据中心微网的规划痛点——为什么常规方法不够用1.1 数据中心的电气结构与用能画像先说数据中心微网到底长什么样。一个典型的数据中心配电结构是这样的:10kV或35kV母线从电网引入主供电源,往下经过变压器、UPS、PDD,最后给机柜里的IT设备供电;制冷系统(精密空调、冷冻水机组、冷却塔)单独一路,但同样挂在微网内部。为了应对电网波动和断电风险,现在很多数据中心会加装屋顶光伏、电化学储能,甚至配置燃气轮机或大功率柴油发电机作为备用电源。数据中心的负荷画像跟普通园区差别很大。IT设备负荷占比通常在45%到60%之间,精密空调和制冷系统占30%到40%,剩下的是电源损耗和照明。最关键的一点是,IT负载具有一定的可调节性——批处理任务、离线训练任务、虚拟机迁移,都是可以错峰执行甚至降功率运行的。也就是说,数据中心并不是一个刚性负荷,它本身就能提供一部分向上和向下的调节空间。这一点正是灵活性能写进规划模型的基础。另一个特征是负荷密度高、可靠性要求极高。大型互联网数据中心的单机柜功率密度已经从2kW涨到10kW甚至20kW,一个中型园区的峰值负荷动辄三四十兆瓦。电网供电协议里往往明确提出双路电源不间断供电的要求,一旦停电,损失不是少卖几度电,而是整片业务的宕机,每小时损失可能以百万计。这就是为什么数据中心微网规划不能照搬普通园区微网那套年费用最小化的套路。1.2 常规规划方法在数据中心场景下的三大局限场景法覆盖不到极端组合。常规微网规划喜欢用典型日场景来近似全年运行状态,比如一个晴天一个阴天一个冬夏典型日。但数据中心最怕的恰恰是低概率高风险事件,比如光伏出力骤降的同时恰好碰上IT负载高峰。场景法很难保证这种组合被覆盖到。机会约束需要精确分布信息。有些方法把不确定性建模成概率约束,比如供电不足概率小于0.5%,但实际工程中光伏出力和数据中心负荷的联合概率分布很难准确估计,误差一大,概率约束就是摆设。只做能量平衡,没做调节能力平衡。传统规划模型里只有功率平衡约束和电量平衡约束,也就是发多少、用多少、配多少储能,但没有回答一个关键问题:当一个1分钟内出现7MW的净负荷跃升时,你这个微网跟不跟得上?这就是灵活性分析要解决的问题。举个直观的例子:某数据中心峰值负荷40MW,光伏装机12MW,某天午后光伏出力从10MW骤降到3MW,净负荷在很短时间内抬升7MW。此时储能如果已经放空,燃气轮机爬坡率只有4MW/h,那剩下的3MW缺口就只能靠切负荷来补——对数据中心来说,这就是宕机事件。这样的场景在常规规划模型里是显示不出来的,只有把灵活性约束显式写进去,才能让规划方案真正满足运行需求。2. 两阶段鲁棒模型怎么建——min-max-min结构的工程解读2.1 从先投资后运行的视角理解模型结构两阶段鲁棒优化的核心思想,我用一句大白话概括:你现在花多少钱买设备,决定了未来在最恶劣情况下要花多少钱运行。设备买多了,投资高但运行从容;设备买少了,投资省了,但极端场景下运行成本(包括切负荷惩罚)可能高到离谱。规划的任务就是在这两头之间找到最优平衡点。数学上,这个问题的标准形式是:min c_inv * x max_{u∈U} min_{y∈Ω(x,u)} c_ope * y第一阶段变量x:光伏容量、储能容量、燃机台数、变压器容量等投资决策;第二阶段变量y:储能充放电功率、燃机出力、IT负载调节量等运行决策;不确定性变量u:光伏实际出力、数据中心负荷的实际波动。内层max-min结构的工程含义是:不确定性u被对手控制,它会专门挑对你最不利的组合;而你只能在看到u之后,在运行约束允许的范围内做出最省钱的反应。整个规划的目标就是让投资成本最坏情况下的最低运行成本总和最小。这种结构特别适合数据中心微网还有一个原因:数据中心负荷的to干扰源是自身的业务调度,光伏出力则完全不受控,两者叠加后的净负荷波动往往带有厚尾特征。鲁棒优化不关心具体概率分布,只需要给出波动范围,这一点在实际工程中非常实用。2.2 不确定性集合怎么选——别一上来就取最保守的不确定性集合是整个鲁棒模型里最需要拿捏的地方。常见的有三种:集合类型表达形式保守程度求解复杂度箱式集合u∈[u_min, u_max]很高低多面体集合预算Σu_i-u_bar_i≤Γ混合范数集合结合1-范数和∞-范数可调中高箱式集合最直观,但它假设所有时段同时取最坏值,后果是规划结果极度保守,储能和燃机可能会被配到满上限,投资成本虚高。实际工程中,我推荐用带预算参数Γ的多面体集合,意思是允许波动,但总波动量不超过某条预算线,这样可以在模型里直接调节保守度,做灵敏度分析时很方便。在Matlab里定义这个集合,可以写成:% 不确定性变量: 光伏出力偏差 pv_dev, 负荷偏差 load_dev % 每个时刻允许在 [extreme_low, extreme_high] 内波动 % 预算约束: 总加权偏差 Gamma Gamma 8; % 可调预算参数, 4小时级别 cons_unc []; % 光伏偏差范围 cons_unc [cons_unc, pv_dev(:) -pv_error_max(:), pv_dev(:) pv_error_max(:)]; % 负荷偏差范围 cons_unc [cons_unc, load_dev(:) -load_error_max(:), load_dev(:) load_error_max(:)]; % 预算约束, 用归一化权重 cons_unc [cons_unc, sum(abs(pv_dev(:))./pv_error_max(:)) sum(abs(load_dev(:))./load_error_max(:)) Gamma];这里abs()在YALMIP里会被自动线性化,不需要手动引入额外布尔变量,算是一个很方便的处理技巧。2.3 强对偶与CCG求解的两条路两阶段鲁棒模型没法直接丢给求解器,必须先做转化。主流方法有两条:Benders分解:把内层问题对偶化,通过割平面迭代逼近,但Benders在处理离散变量时收敛会比较慢;列与约束生成算法:每轮迭代在主问题里显式增加一组新的运行变量和约束,逼近速度通常比Benders快一个数量级,是目前两阶段鲁棒规划的主流选择。CCG的思路很简单:主问题最初只包含一部分最坏场景,然后每轮通过求解子问题把最坏场景对应的运行约束当成新割集加入主问题,循环更新直到上下界收敛。用流程图表达的话,就是主问题给下界,子问题给上界,循环割集。在具体写代码之前,先把子问题的转化讲清楚。给定第一阶段决策x̄,子问题是:max_{u∈U} min_y c_ope * y s.t. F*y f B*u G*y g D*x̄ y 0内层的min是一个线性规划,可以取其对偶问题。假设内层约束的等式对偶变量为λ,不等式对偶变量为μ,那么内层对偶是:max_{λ,μ} -(f B*u) * μ - (g D*x̄) * λ s.t. F*μ G*λ c_ope μ 0把内层对偶代回原问题,整个子问题就变成一个单层的max问题(目标里含有u和μ的双线性项,后面用大M线性化处理)。这一步一旦搞定,整个CCG循环就只是求解主问题→固定x̄→求解子问题→新增割集的重播。3. 灵活性指标如何嵌入规划模型——这是题目里的核心难点3.1 从能量平衡到调节能力平衡很多人在复现时会把考虑灵活性简单理解成目标函数里加一个惩罚项,这是不对的。灵活性不是成本项,而是一组动态调节能力约束。它衡量的不是某个时刻发了多少电,而是某个时刻系统能不能按要求把出力往上调或往下调。以数据中心微网为例,调节能力来源主要有:储能系统的放电裕度(向上)和充电裕度(向下);燃气轮机的上调富余和下调富余,取决于当前出力与上下限的距离;燃气轮机的爬坡速率;IT负载的可削减量(向上调节)和可恢复量(向下调节);制冷系统利用蓄冷能力形成的虚拟储能。需求侧则由净负荷波动定义:净负荷在某一时段突然增大,就需要向上灵活性;净负荷突然减小,则需要向下灵活性。3.2 灵活性供给与需求的数学化表达定义t时刻净负荷为:P_net(t) P_load(t) - P_pv(t)那么从t-1到t的净负荷变化量:ΔP_net(t) P_net(t) - P_net(t-1)如果ΔP_net(t) 0,说明净负荷增加,系统需要向上灵活性;如果ΔP_net(t) 0,则需要向下灵活性。向上灵活性的供给能力可以写成:FLX_up(t) min(P_es_disch_max - P_es_disch(t), P_es_disch_ramp) % 储能放电裕度 min(P_gt_max - P_gt(t), P_gt_ramp_up) % 燃机上调裕度 P_IT_cut(t) % IT负载可削减量 P_cooling_release(t) % 蓄冷释放能力约束条件就是:FLX_up(t) max(ΔP_net(t), 0) flexibility_reserve_up(t)向下灵活性同理:FLX_down(t) max(-ΔP_net(t), 0) flexibility_reserve_down(t)这里的flexibility_reserve是一个可选的安全余量,可以取净负荷波动方差的倍数,也可以直接设为一个固定比例,比如2%峰值负荷。我个人建议在复现时一定要保留这个变量,它在灵敏度分析中非常好用。3.3 灵活性约束在鲁棒模型中的嵌入位置灵活性约束不是加在第一阶段就完了,而是要在第二阶段的每个运行时段都施加。也就是说,在CCG的子问题里,当不确定性变量u固定后,净负荷已经确定,此时灵活性供给能力必须覆盖净负荷波动需求。这里有一个容易被忽略的细节:灵活性约束里边包含储能SOC、燃机出力等状态变量,而这些状态变量在第二阶段必须连续耦合,不能把每个时段独立建模。否则你会计算出储能同时放电和充电这种荒谬结果。具体到MatlabYALMIP实现,我一般是这么处理的:for t 1:T % 净负荷 P_net(t) P_load_bar(t) load_dev(t) - (P_pv_bar(t) pv_dev(t)); if t 1 delta_net P_net(t) - P_net(t-1); % 向上灵活性供给 flx_up_supply(t) min(P_es_cap - P_es_dis(t), P_es_ramp) ... 这个min在YALMIP里不友好 end endYALMIP对min/max函数的处理能力有限,更稳妥的做法是把灵活性供给拆成多个约束。比如储能放电裕度那部分,引入辅助变量flx_es_up(t),然后写四个约束:cons [cons, flx_es_up(t) P_es_cap - P_es_dis(t)]; cons [cons, flx_es_up(t) P_es_ramp_max]; cons [cons, flx_es_up(t) 0]; % 燃机同理 cons [cons, flx_gt_up(t) P_gt_max - P_gt(t)]; cons [cons, flx_gt_up(t) P_gt_ramp_up]; % 总灵活性 ≥ 需求 cons [cons, flx_es_up(t) flx_gt_up(t) P_IT_flex_cut(t) flx_cool_up(t) max(delta_net, 0) reserve_up(t)];注意max(delta_net, 0)这个形式在YALMIP里需要用额外二元变量做线性化,或者直接把约束拆成两条:当delta_net 0时FLX_up≥delta_net;当delta_net 0时只需要FLX_up≥0。后者在实现上更省事,我通常用两条约束替代:cons [cons, flx_up_total(t) flx_up_slack(t) delta_net(t)]; cons [cons, flx_up_total(t) flx_up_slack(t) 0]; % 外加 flx_up_slack(t) 0, 这是一个松弛变量, 目标函数中配大惩罚系数这里引入非负松弛变量slack是在灵活性不足时给模型一个逃生门,否则很多情况下问题会直接无解。目标函数里给这个松弛变量配一个足够大的惩罚系数(比如10000元/MW),就既能保持问题可行性,又能在结果里直观看到每个时段的灵活性不足量。4. Matlab环境下CCG算法的完整实现4.1 求解环境配置与工具箱选择跑这套模型,我建议的环境组合是:Matlab R2021a或更新版本 YALMIP Gurobi(或CPLEX)。YALMIP负责建模,Gurobi负责求解线性规划和混合整数线性规划。Gurobi学术版是免费的,申请license的时候用学校邮箱,同一天基本就能下来。安装的时候有几个坑要提前避开:YALMIP压缩包解压后要把整个文件夹加入Matlab路径,不是只加入子文件夹;Gurobi安装后要运行gurobi_setup.m脚本,把求解器接口加到Matlab路径;sdpsettings(solver,gurobi)之前,先跑一次yalmiptest验证接口是否正常;如果发现license not found这类报错,大概率是Gurobi的license文件路径没配置好,检查环境变量GRB_LICENSE_FILE。对于不确定集合规模特别大的模型,我还试过用Mosek替代Gurobi,性能也不错,但混合整数部分稍弱,所以主力还是Gurobi。4.2 CCG算法主循环的Matlab骨架下面给出一个可以直接套用的CCG主循环框架,注释里标明了每一步在干什么。这个框架不依赖具体的模型规模,可以灵活修改。%% CCG 主循环 % 参数定义 T 24; % 调度时段数 Gamma 8; % 不确定性预算 max_iter 30; % 最大迭代次数 tol_gap 0.01; % 收敛间隙 % 主问题初始变量 x_pv sdpvar(1,1); % 光伏装机容量 x_es sdpvar(1,1); % 储能容量 alpha sdpvar(1,1); % 运行成本代理变量 % 初始主问题约束(不含割集) cons_master [0 x_pv 50, 0 x_es 40]; % 存储割集的cell数组 cuts_u {}; % 保存每次迭代生成的u_star cuts_y {}; % 保存对应的第二阶段变量组 LB -inf; UB inf; iter 0; while (UB - LB) / abs(UB) tol_gap iter max_iter iter iter 1; % ---------- 1. 求解主问题 ---------- % 注意这里要遍历已有割集, 每个割集对应一组y变量 cons_master_iter cons_master; for k 1:length(cuts_u) u_k cuts_u{k}; y_k sdpvar(size_running_vars,1); cuts_y{k} y_k; % 添加第二阶段运行约束(与u_k耦合) cons_master_iter [cons_master_iter, running_constraints(x_pv, x_es, y_k, u_k)]; % 添加运行成本约束, 让alpha至少达到该割集下的运行成本 cons_master_iter [cons_master_iter, alpha c_ope * y_k]; end % 目标函数: 投资成本 alpha obj_master c_inv * [x_pv; x_es] alpha; optimize(cons_master_iter, obj_master, sdpsettings(solver,gurobi,verbose,0)); x_current value([x_pv; x_es]); LB value(obj_master); % ---------- 2. 求解子问题(固定x_current) ---------- [u_star, obj_sub, constraint_violation] solve_subproblem(x_current, Gamma); % 子问题返回的是最坏场景u_star下的运行成本 candidate_UB c_inv * x_current obj_sub; UB min(UB, candidate_UB); fprintf(Iter %d: LB%.4f, UB%.4f, gap%.4f%%\n, ... iter, LB, UB, abs(UB-LB)/abs(UB)*100); % ---------- 3. 收敛判断 ---------- if (UB - LB) / abs(UB) tol_gap break; end % ---------- 4. 新增最优割集 ---------- cuts_u{end1} u_star; end这个骨架有几个要点需要注意:每次迭代新增的y_k是独立的一组变量,不能复用上一轮的y变量,否则会导致主问题维度混乱;主问题的alpha会随着割集增多而增大,这是正确的——加进来的场景越极端,运行成本代理就越接近最坏情况;子问题里固定x_current时,要注意把x_pv和x_es替换成数值,而不是变量,否则没有算清楚固定住的是什么。4.3 子问题求解的具体实现子问题需要求解的是:max_{u∈U} min_y c_ope * y我采用的做法是先写出内层min的KKT条件或对偶形式,再把外层max合并成单层MILP。对于分支规模不大的情况,也可以直接在Matlab里把内层min写成YALMIP优化问题,然后用export接口导出对偶形式——但这个做法在变量维度大的时候极易出错,不建议新手尝试。更稳妥的做法是手写对偶。假设内层问题是:min c_ope * y s.t. A_eq * y b_eq B_eq_u * u A_ineq * y b_ineq B_ineq_u * u lb y ub引入对偶变量λ(等式约束)和μ(不等式约束),对偶问题的目标与约束如下:function [obj_val, u_star, sub_info] solve_subproblem(x_current, Gamma) % 不确定性变量 u sdpvar(T,1); % 光伏偏差 v sdpvar(T,1); % 负荷偏差 % 对偶变量 lambda sdpvar(N_eq,1); mu sdpvar(N_ineq,1); % 不等式约束对偶 mu_lb sdpvar(T,1); % y下界对偶 mu_ub sdpvar(T,1); % y上界对偶 % 对偶可行性约束 cons [mu 0, mu_lb 0, mu_ub 0]; cons [cons, A_eq*lambda A_ineq*mu mu_ub - mu_lb c_ope]; % 不确定性集合 cons [cons, -pv_err_max u pv_err_max, -load_err_max v load_err_max]; cons [cons, sum(abs(u)./pv_err_max) sum(abs(v)./load_err_max) Gamma]; % 目标(这里用了对偶形式) obj -b_eq*lambda - b_ineq*mu x_current(1)*ones(T,1)*... % 与x_current耦合的项 ... 省略细节 optimize(cons, -obj, sdpsettings(solver,gurobi,verbose,0)); % 因为我们要max obj obj_val value(obj); u_star value([u; v]); end这段代码里最令人头疼的是目标函数中u*lambda这类双线性项。Gurobi不支持直接处理双线性目标中的连续变量乘积,所以需要用大M法做线性化。具体做法是引入辅助变量w(t) u(t) * lambda(t),然后通过M界限把非线性约束替换成线性约束组。线性化的标准形式是:% 假设 u(t) ∈ [u_min_u, u_max_u], lambda(t) ∈ [lam_min, lam_max] w sdpvar(T,1); M 1e4; % 大M取值, 需要根据问题量级调整 cons [cons, w u_max_u * lambda u * lam_min - u_max_u * lam_min]; cons [cons, w u * lam_max u_min_u * lambda - u_min_u * lam_max]; cons [cons, w u * lam_min u_min_u * lambda - u_min_u * lam_min]; cons [cons, w u * lam_max u_max_u * lambda - u_max_u * lam_max];这样处理之后,整个子问题就变成一个线性规划(MILP如果含整数变量),可以直接用Gurobi求解。5. 复现过程中的实测大坑——每一个都让我浪费过一天5.1 max-min结构处理错,结果居然是负无穷我第一次复现时,子问题直接用optimize(cons_sub, c_ope*y)求解,然后外层再用场景枚举去max——这是完全错误的理解。内层的min结果本身不是一个数,而是关于u的函数,必须经过对偶或KKT转化后才能和外层max合并。否则你得到的是某个固定u下的最小运行成本,而不是最坏u下的最小运行成本。这两个在数值上相差很大,典型的表现是UB一直偏低,主问题割集加不进去,最终结果看起来太乐观。判断标准:如果CCG迭代中LB很快就超过UB,或者UB波动很大,先检查子问题是不是真的在优化max-min,而不是简单的给定u求min。5.2 大M取值,一个看似小实则致命的问题大M线性化里M的选取直接影响求解器数值稳定性。我试过M1e4跑10MW节点系统,结果Gurobi报病态,很多约束违反距离达到1e-2级别;把M调到200,反而一切正常。原因很简单:大M太大会让预求解器丢掉有效约束,太紧又可能把可行域切掉。更合理的方式是不用M,改用implies或者if-then-else逻辑,但这在YALMIP里也需要额外工具。我的建议是:先跑一个确定性问题,观察u和lambda的实际取值范围,再把M设置成该范围的3到5倍,不要凭空拍脑袋。5.3 灵活性子问题的符号方向错误灵活性约束引入后,子问题的对偶变量数量发生变化,很多人在这里把不等式方向搞反。我的经验是:给每个灵活性约束配一个非负松弛变量,并在目标函数里加上惩罚项。这样即使约束写错方向,至少模型不会直接无解,而是会在结果中显示出异常大的松弛值——这个信号能帮你快速定位出错位置,比直接面对infeasible problem高效得多。5.4 先跑确定性模型,再上鲁棒,最后加灵活性这条经验值得多说两句。我自己刚上手时,直接按论文里的完整模型一步到位写代码,结果出错后根本分不清是鲁棒循环的问题、对偶转化的问题还是灵活性约束的问题。后来我改成三步走:第一步:把所有不确定变量设为预测值,去掉max-min结构,只求一个确定性规划,确保设备投资和运行策略合理;第二步:加回不确定性和CCG循环,先不要灵活性约束,用最基础的功率平衡把鲁棒循环跑通;第三步:再加灵活性供给约束、松弛变量和惩罚项。每步之间都能看到明确的中间结果,定位bug的时间至少缩短一半。5.5 求解器报license error的快速排查顺序搜索热词里Matlab和license相关的词出现频率很高,这确实是很多人的拦路虎。遇到报错,我的排查顺序是:在命令行输入gurobi_setup,确认Gurobi路径已加入Matlab;检查环境变量GRB_LICENSE_FILE是否指向了正确的license文件;在Matlab里运行gurobi命令,如果返回版本信息说明接口正常;如果Gurobi正常而YALMIP仍然报错,重新运行yalmiptest并查看哪个求解器接口是红色的;还不行就重启Matlab——这招听着蠢,但真的能解决不少路径缓存问题。6. 结果分析与灵敏度测试——哪些参数值得多调几轮6.1 正常拿到收敛解之后的第一个判断标准跑通之后,第一件事不是画图,而是看鲁棒解相对确定性解的成本增量是否合理。以我复现的案例为例:确定性模型下总成本约1850万元,预算Γ8的两阶段鲁棒模型总成本约2140万元,增加约15.7%。这个增量如果太小(比如小于2%),说明不确定性集合设置得太松,鲁棒约束没有真正起作用;如果太大(超过40%),说明Γ或者波动范围取值过激,方案在经济性上没有竞争力。你可以通过调整Gamma做一条成本曲线。Γ从0增加到峰值时,成本上升速率最快的区间就是鲁棒模型最有价值的区间。在实际论文里,通常取上升速率由陡转缓的拐点作为推荐值。6.2 灵活性指标对投资决策的影响对比考虑灵活性和不考虑灵活性两组实验,最明显的变化是储能容量配置:场景光伏配置(MW)储能配置(MWh)灵活性不足惩罚成本不考虑灵活性12.018无法统计考虑灵活性(储备率2%)11.2243.2万元考虑灵活性(储备率5%)10.5311.8万元储备率从2%加到5%,储能容量增加了29%,但灵活性不足惩罚成本下降了44%。这说明在你这个算例里,边际储能投资用于降低灵活性缺口的效率是相当高的。如果相反——储能容量加了很多但灵活性不足率几乎不变,那就要检查是不是灵活性供给约束写错了,或者IT可削减负荷的潜力没有合理建模。6.3 数据中心特有指标:把PUE和可用性也算进去既然做的是数据中心微网,结果分析里最好带两个行业特有指标:PUE:数据中心总能耗与IT设备能耗之比。在微网规划模型中,PUE可以简化为制冷负荷电源损耗与IT负荷的比例。引入灵活性约束后,系统可以通过下调IT负载来应对波动,但下调IT负载会影响业务可用性,所以PUE大概率会略微升高。这个trade-off在论文里是个很好的分析切入点。可用性:在鲁棒最坏场景下,切负荷量和灵活性不足量之和与总负荷的比值,可以用来估算极端场景下的供电缺口。我算出来大概是0.3%,也就是全年约26小时存在潜在降载风险。这个数字可以直接和行业标准做对比。6.4 三招把仿真时间压到30分钟以内最后聊聊性能。两阶段鲁棒规划最大的痛点就是求解时间,尤其是T24、每时段都有灵活性约束时。我实测的优化技巧有三个:预设好初始场景:用k-means对历史光伏和负荷数据做聚类,选出3到5个簇中心作为CCG主问题的初始割集,而不是从空割集开始,能明显减少迭代次数;控制主问题规模:每轮迭代只加入最恶劣的u-star,不要图方便一次加多个候选场景,否则主问题变量会爆炸式增长;求解器参数调优:sdpsettings(gurobi.MIPGap, 1e-3),gurobi.TimeLimit, 300,给求解器留够预算但别让它无限跑。我现在的典型运行时间是25到35分钟(24时段,Gamma从0到12,储能和光伏高度耦合),在可接受范围内。如果你的是小时级别,优先检查二次运行成本函数是否写成了非线性导致YALMIP调用了内点法而不是线性求解。最后分享一个我自己的习惯:跑任何鲁棒规划代码之前,先花十分钟把目标函数里所有成本项的量纲统一写清楚——投资成本按年折摊、运行成本按调度周期、惩罚成本按单位功率计费,三者单位不统一是结果分析时最容易犯的低级错误。把这一步做扎实,后面所有灵敏度分析和论文配图都会顺利很多。
阅读完成 · 觉得有帮助?
咨询建站