简介基于MATLAB与Yalmip工具箱调用Cplex求解器构建微电网两阶段鲁棒优化完整模型面向电力系统研究生与科研人员用于处理风光出力和负荷的不确定性经济调度问题。目标函数涵盖储能等年值投资成本、配电网购售电交互成本、各单元运维成本及微型燃气轮机燃料成本并计及储能、微型燃气轮机、功率平衡与配电网交互等约束参考刘一欣《微电网两阶段鲁棒优化经济调度方法》建模适合学习盒式不确定集与列约束生成CCG算法在微电网中的落地实现。资源共14个文件其中5个m源程序为核心求解代码辅以docx模型推导说明、pdf算法原理文献、caj参考论文、3张矩阵推导图片、Excel典型日数据与readme说明压缩包仅1.77MB结构精简、便于快速对照学习。已有14119人学习下载程序保证稳定收敛且注释清晰随包附带推导过程和图片读者可基于模型自行修改不确定度比例等参数用于复现论文结果或扩展至多层鲁棒场景。1. 两阶段鲁棒微电网为什么不能只跑一次确定性调度微电网的日前调度通常把光伏、负荷当作已知数但实际运行中预测误差是常态。确定性模型一旦遇到极端天气要么备用不足导致切负荷要么被迫高价购电两天就把全月的优化收益赔回去。两阶段鲁棒微电网调度two_stage robust optimization把不确定性放进一个集合里先做第一阶段日前决策第二阶段在不确定性实现后按最坏情况进行校核用min-max-min结构保证任何场景下成本都有上界。配合MATLAB的YALMIP建模和CPLEX求解这是目前研究和工程预研里最常用的一类实现路线。这个方法适合两类人一是做微电网能量管理研究的开发者需要和随机规划、确定性优化做对比二是正在搭仿真平台的工程师想评估自己的调度策略在最恶劣输入下到底还稳不稳。它不是银弹也不能替代随机规划但能给你一条清晰、可复现的鲁棒性边界。下面我会从建模开始一步步讲到能跑的代码、参数和坑。2. 两阶段鲁棒微电网的建模逻辑从min到min-max-min2.1 确定性微电网调度模型为什么不够用微电网日前优化调度的目标函数是运行成本最小化。以最常见的燃气轮机储能联络线结构为例目标函数为min Σ ( c_f * Pg_t c_start * u_t c_b * Pb_t c_ess * |P_ess_t| )约束包括功率平衡Pg_t Pb_t P_ess_t P_w_t P_L_t燃气轮机上下限和爬坡约束储能SOC递推联络线容量约束。这里P_w_t和P_L_t是预测值。确定性模型把P_w_t和P_L_t固定在预测值然后通过备用约束来兜底。常见的做法是要求总可控出力能覆盖预测负荷加上一个固定百分比偏差。这种做法的毛病不在于“有备用”而在于“备用怎么给”给多了成本高给少了到了实际运行日负荷和光伏一偏传输线和储能就会逼近极限调度员只能临时调度或者切负荷。我见过某公司预研的血泪经验用确定性模型做的日前计划在连续阴天后光伏实际出力只有预测的50%备用全部花光最后只能从联络线高价买电当日结算成本是预测成本的两倍多。问题不在优化器在模型没有“最坏情况”的概念。两阶段鲁棒优化把这个概念显式化第一阶段变量机组启停、联络线购电协议等必须在不确定性实现前定下来因为机组启停需要提前通知、购电合约需要提前签第二阶段变量实际出力、储能充放电、切负荷量可以在不确定性实现后调整。这个时间线就是“两阶段”的物理含义。目标函数变成最小化“第一阶段成本 第二阶段最坏成本”min_x { c^T x max_{ξ∈U} Q(x, ξ) }其中Q(x, ξ) min_y { d^T y | A y ≥ b - T x - C ξ, y ≥ 0 }代表给定第一阶段决策x和不确定量ξ时第二阶段运行优化问题的最优成本。最外层min找第一阶段决策中间的max在不确定集U里找最坏场景最内层min是运行优化。对工程人员来说这个嵌套结构不需要一口气理解。你要记住的三件事是第一第一阶段决策是“硬承诺”第二不确定集是“多坏都算坏”的边界第三求解方法不是一步到位而是通过主问题-子问题迭代逼近。这三个点对应了后面所有代码和参数的来历。2.2 不确定集的工程选型盒式、预算式与椭球式两阶段鲁棒的第一个坑往往不是算法而是不确定集选太大或太小。不确定集U是第二阶段max做搜索的范围。工程上常用三种集合第一种是盒式集ξ ∈ [ξ_min, ξ_max]逐时段独立。它的求解最简单但把所有时段同时推到极端看起来是在做最坏情况实际上过度保守。24个时段如果每个时段都取最大偏差算出来的成本可能比真实极端场景高20%——因为真实中风向、光照不会所有时段同步崩。第二种是预算式Budgeted Uncertainty集合在盒式基础上加一个总偏差约束Σ_t |ξ_t - ξ_pred_t| / Δξ_t ≤ Γ其中Γ是预算参数。Γ0退化为确定性模型ΓT所有时段都可偏离到边界退化为盒式。这个集合在工程里是最常用的因为它能控制保守度且保持线性结构CPLEX可以直接处理。我一般建议先用Γ 0.5T到0.8T扫一条成本-风险曲线让运维方自己选点。有的运营方愿意接受15%的成本抬升来换更高的供电可靠性有的则更在意经济性会选小Γ。这不是算法问题是决策偏好问题。第三种是椭球式集合ξ^T M ξ ≤ 1数学上很漂亮能考虑时段间相关性但会让主问题变成二次约束优化。CPLEX虽然支持二次约束但在两阶段迭代中子问题还要对偶椭球会让子问题变成SOCP求解器来回切换时间会明显变长。除非你要发表带相关性结论的论文否则初版不建议上椭球。我见过一个翻车案例某项目把负荷相关性建模成椭球集主问题还能解子问题对偶后出现锥约束CPLEX被反复调用单次迭代时间从秒级跳到了分钟级最后只能退回预算式。工程里“先线性后复杂”永远是第一原则。2.3 CCG 的求解结构主问题子问题两阶段鲁棒不能直接丢给求解器标准解法是列与约束生成Column-and-Constraint Generation, CCG。它和Benders分解长得很像但区别在于CCG每轮迭代会把新场景连同对应的第二阶段变量一起加入主问题而Benders只加一条割平面。CCG的主问题规模增长快但迭代次数远少于Benders对微电网这种数十到数百个约束的问题更实用。主问题MP在第k轮的形式是min_{x, z, y_j} c^T x zs.t. x ∈ Xz ≥ d^T y_j, ∀ j ≤ kA y_j ≥ b - T x - C ξ_j, ∀ j ≤ ky_j ≥ 0其中ξ_j是前面若干轮子问题找出来的最坏场景y_j是对应场景下的第二阶段变量。注意主问题里有k组第二级变量但每组之间除了共享x和z没有其他耦合所以主问题本质上是带场景索引的扩展问题。子问题SP在给定了主问题解x*之后求解Q(x*) max_{ξ∈U} min_y { d^T y | A y ≥ b - T x* - C ξ, y ≥ 0 }它是个max-min问题。如果内层是线性规划可以通过强对偶把内层min转成max变成单层max问题。但转出来的目标里会有对偶变量λ和不确定量ξ相乘的项也就是双线性项。这是两阶段鲁棒实现里最麻烦的数学点后面代码部分我会给出big-M线性化的具体写法。CCG的主循环是这样初始化设下界LB -∞上界UB ∞迭代计数k 0。求解主问题得到x*, z*。更新LB c^T x* z*。把x代入子问题求解最坏场景ξ_{k1}和对应第二阶段成本Q(x)。更新UB min(UB, c^T x* Q(x*))。如果UB - LB ≤ ε或相对gap小于阈值停下来。否则把ξ_{k1}作为新场景加入主问题创建对应的y变量和约束k自增回到第2步。迭代中UB是单调不增的LB是单调不减的所以gap一定收敛。但注意如果子问题没严格求解比如枚举场景漏了真正最坏点UB会偏小gap会失真看起来收敛了其实没有。这是第5章的常见坑之一。对微电网调度来说第一阶段变量通常包括机组启停这类整数变量所以主问题是MILP第二阶段如果全是连续变量子问题对偶后是MILP如果第二阶段也有整数变量比如储能放电状态对偶失效处理方式只有两种把放电状态挪到第一阶段或者用大M把第二阶段整数线性化。初学者容易在这里卡一整天后面避坑章节会展开。3. MATLABYALMIPCPLEX 实现两阶段鲁棒的代码骨架3.1 环境与建模习惯用什么版本、怎么组织文件常见的做法是用YALMIP描述优化问题用CPLEX作为底层求解器。YALMIP的好处是用sdpvar/binvar定义变量约束写起来和数学表达式几乎一一对应不需要手动转换成求解器API。两阶段鲁棒需要反复改约束、加场景这种声明式建模能让你把注意力放在模型结构上而不是坐标变换。我一般会建四个文件main.m主循环、build_MP.m主问题生成、solve_SP.m子问题求解、config.m所有参数集中定义。参数集中放一个文件很重要因为两阶段鲁棒要打的参数很多分散在代码里就是灾难。一个影响求解速度的习惯YALMIP在多次optimize调用中会重复解析模型最好把不变的基座约束放在循环外循环内只更新割平面相关的约束。具体实现可以用元胞数组MP_cons{end1} ...而不是每次拼接整个约束集。3.2 主问题MP变量定义与割平面累积下面是主循环主问题部分的最小骨架。假设我们已经有了历史场景集合xi_cell和对应的辅助变量Y_cell。% 参数 T 24; % 调度时段数 Gamma 14; % 不确定预算取时段数的60%按你的场景调整 C_fuel 0.6; % 燃料成本系数单位元/kWh C_buy 0.8; % 购电成本系数 P_buy_max 500; % 联络线购电上限kW load_pred zeros(T,1); % 负荷预测值由外部数据填充 w_pred zeros(T,1); % 光伏/风电预测值 w_dev ones(T,1) * 0.3 * w_pred; % 预测偏差上限 % 第一阶段变量机组启停与日前购电 u binvar(T,1); % 燃气轮机启停状态 Pb sdpvar(T,1); % 联络线日前购电计划kW % 第二段成本下界变量 z sdpvar(1,1); % 基础约束购电上限 base_cons [Pb 0, Pb P_buy_max]; % 燃气轮机启停约束可以后面补这里先给空集 MP_cons base_cons; % 每个历史场景对应的第二阶段变量储能出力/燃气轮机实际出力等 % 这里用元胞数组保存每次迭代新增一个场景 Y_cell {}; xi_cell {}; % 主循环开始 for k 1:max_iter % 求解当前MP ops sdpsettings(solver,cplex,verbose,0); objective fuel_cost(u) P_buy_cost(Pb) z; sol optimize(MP_cons, objective, ops); if sol.problem ~ 0 error(MP求解失败: %s, sol.info); end LB value(objective); x_u value(u); x_Pb value(Pb); % ... 后面调用子问题见3.3 end这里的fuel_cost(u)和P_buy_cost(Pb)建议用函数表达式在脚本里展开别用循环。YALMIP内部是符号计算循环里逐时段相加会让模型结构缓慢。直接用向量化表达式比如fuel_cost_expr C_fuel * x_RT; % 见子问题上面代码中u是binvarCPLEX会识别为整数变量。z是无约束连续变量它的作用是被割平面约束托底最终等于第二段最坏成本的下界。注意主问题不是“用z代替第二阶段成本”而是“第二阶段成本必须不小于所有已发现场景对应的成本”这正是CCG的语义。一个容易错的地方sol.problem可能返回1为不可行。两阶段鲁棒的主问题如果加了太多割平面约束可能因为早期场景太过乐观而互相矛盾导致MP不可行。这时候需要检查子问题返回的场景是否真的可行。常见做法是在加入割平面时同时加入可行割这个第5章会讲。3.3 子问题SPmax-min转max并处理双线性项子问题是最难写对的一步。固定第一阶段u*和Pb*后第二阶段运行优化是一个LP假设储能连续。我们先把内层LP写成通用形式Q(x*) max_ξ min_y { d^T y | A y ≥ b - T x* - C ξ, y ≥ 0 }内层LP对偶后变成Q(x*) max_{λ, ξ} { (b - T x* - C ξ)^T λ | A^T λ ≤ d, λ ≥ 0, ξ ∈ U }现在目标里出现了λ^T C ξ是双线性项。CPLEX不能直接处理双线性目标需要线性化。对于盒式预算不确定集ξ的取值可以表达为ξ_t ξ_pred_t ξ_dev_t * (2*b_t - 1)其中b_t ∈ {0,1}。如果b_t1ξ取上界b_t0取下界。预算约束写为Σ b_t ≤ Γ这里是说偏离的时段数不超过Γ如果你用连续偏差幅度也可以把偏差幅度放在ξ_dev里。把上式代入目标中λ_t * ξ_t项得到ξ_dev_t * (2 * λ_t * b_t - λ_t)。这里只有λ_t * b_t一项是双线性。引入辅助变量r_t λ_t * b_t用big-M线性化% lambda 是从内层LP对偶得到的连续变量假设已在变量中定义 lambda_t sdpvar(T,1); % 这里仅示意lambda应当在对偶问题中定义 b binvar(T,1); % 不确定量方向选择 r sdpvar(T,1); % r lambda .* b M 1e3; % big-M要大于lambda可能取到的绝对值的上界 cons_lin []; for t 1:T % r 的两个方向的上界 cons_lin [cons_lin, r(t) -M * b(t)]; cons_lin [cons_lin, r(t) M * b(t)]; % r 与 lambda 的耦合 cons_lin [cons_lin, r(t) lambda(t) - M * (1 - b(t))]; cons_lin [cons_lin, r(t) lambda(t) M * (1 - b(t))]; end % 目标函数中的双线性项替换为 % xi_dev_t * (2*r_t - lambda_t)上面这个线性化需要说明当b0时一组约束强制r0当b1时一组约束强制rlambda。四个约束是标准big-M写法M只要大于lambda的绝对上界即可。M设太大会让LP的数值变差出现“求解器报告最优但结果明显不对”的玄学现象。实际调试中我会先解一次不考虑双线性的松弛对偶打印lambda的最大绝对值再据此把M收紧到它的2倍左右。另外预算约束不是非要二进制。如果ξ_t的偏差幅度也是决策变量比如|ξ_t - ξ_pred_t| ≤ ξ_dev_t, 且Σ ≤ Γ那么ξ本身就是连续变量目标中λ*ξ用McCormick包络也可以线性化。但McCormick的紧度依赖于lambda和xi的界不好调。我建议初版先用二值方向模式把偏差幅度固定为预测误差最大值用Γ控制数量。这样模型简单割平面质量也足够。子问题本身是个MILP因为包含b变量。CPLEX求解MILP没问题。但注意每轮迭代子问题都要完整求解一次如果M取值引起数值问题会导致求解时间从几秒跳到几十秒。所以M的调试是两阶段鲁棒落地里值得花时间的环节。3.4 CCG主循环上下界更新与收敛主循环把这几个函数串起来UB inf; LB -inf; max_iter 30; eps_gap 1e-3; for k 1:max_iter % 求解MP [sol_mp, obj_mp] solve_MP(MP_cons, mp_obj, ops); LB value(obj_mp); x_u value(u); x_Pb value(Pb); % 求解SP [Qk, xi_k, lambda_k] solve_SP(x_u, x_Pb, params); candidate_UB first_stage_cost(x_u, x_Pb) Qk; UB min(UB, candidate_UB); if UB - LB eps_gap * abs(LB) break; end % 新增场景变量和割平面 Y_new sdpvar(T,1); % 第二阶段变量按你的模型定义 xi_cell{end1} xi_k; Y_cell{end1} Y_new; % 为新场景添加约束和割 MP_cons [MP_cons, ... z second_stage_cost_expr(Y_new, xi_k), ... coupling_cons(u, Pb, Y_new, xi_k)]; end这里UB的更新要特别小心value(obj_mp) - value(z) Qk其实是c^T x Qk。因为obj_mp c^T x z而z是MP中托底的成本不是真实第二段成本。用c^T x的当前值加子问题Qk才是候选上界。很多人在这一步写错导致gap永远不收敛。更稳妥的办法是单独计算first_stage_cost(x_u, x_Pb)不依赖MP目标里的z。收敛判据我用UB - LB eps_gap * abs(LB)相对gap比绝对gap更实用。微电网成本量级可能从几万到几百万绝对阈值不好定。迭代上限max_iter设30通常够用但如果你发现每轮MP规模都在膨胀且20轮后gap还在2%以上多半是子问题或割平面写错了而不是单纯迭代不够。4. 让求解不再玄学关键参数与场景集设置4.1 CPLEX求解器参数mipgap、容差与内存YALMIP调用CPLEX时求解器参数通过sdpsettings(cplex.xxx, value)传递。我常用的几个参数推荐值说明cplex.mip.tolerances.mipgap1e-4整数规划相对gap太大会让子问题最优性打折cplex.mip.tolerances.absmipgap1e-6绝对gap一般不用动cplex.mip.tolerances.integrality1e-5整数容差过大会把0.9999当1用cplex.tune.timelimit300单个MP或SP的超时秒数防止死循环cplex.parallel.maxthreads8多核并行但注意和子问题内层并行叠加会爆内存一个血泪经验不要把mipgap设成1e-6尤其是子问题还要被迭代调用几十次。子问题稍微偏离最优解会导致返回的ξ不是严格最坏场景UB被低估主问题割平面切不准最后整体gap卡在0.5%降不下去。与其把每个子问题解到极致不如把mipgap设成1e-4然后允许主循环多迭代两轮。这是整体和局部的权衡。另外YALMIP的verbose参数建议设1能打印求解器日志。设0虽然干净但一旦不收敛你什么都看不出来。4.2 收敛判据与迭代上限什么才算真正收敛主循环里默认用相对gap公式是(UB-LB)/|LB|。设1e-3时对应成本误差约0.1%对微电网调度已足够。但要注意LB来自MP的松弛因为MP里的z是被割平面限制的下界UB来自真实场景成本所以gap天然是正的。如果出现UB LB说明MP目标里z小于子问题返回的Qk也就是割平面没生效。常见原因是添加割时用了方向反了或者子问题返回的是可行割而非最优割。迭代上限一般30次。CCG在微电网问题上如果不确定集只有负荷和光伏两个源的预算约束通常10次内gap就能降到1%以下。如果你发现30次还在振荡建议先跳过gap限制打印每轮的xi_k变化。如果场景一直在几个固定值之间跳说明主问题割平面存在冗余导致LB和UB交替靠近但不交叉。这时候要给主问题加“第k个场景的变量只在对应场景约束中出现”的索引正确性检查。4.3 不确定集参数Γ的影响成本-鲁棒性权衡Γ的选择直接决定结果的经济性。实际操作中我会对Γ做一次参数扫描Γ从0到T按步长2取点每个点都跑完整两阶段鲁棒记录总成本和最坏场景下的实际成本。最后画一条曲线交给决策人。如果这条曲线在某段非常陡说明系统对不确定性非常敏感需要加固如果平缓说明当前方案余量已经够大没必要上更复杂的鲁棒模型。注意Γ的物理含义它表示允许同时偏离预测值的时段数量。比如Γ8意味着最坏场景下24h中至多8个小时的不确定量同时取到偏差上界或下界。很多论文直接写Γ8没有解释实际工程里应该结合气象预报置信度来确定。比如风速预测偏差在午后时段相关性高那么Γ可以适当调小因为相关性高时真实极端场景集中在一段连续时间盒式会高估。这些需要靠实际数据的统计特征来定早期可以先用5折交叉验证用历史场景回测不同Γ下的成本和失负荷率。还有一个实用技巧主问题第一轮往往没有任何割平面z没有下界MP目标会倾向于让z取一个很负的值导致LB非常低。常用的做法是预先加入确定性预测场景作为第0个场景让z至少等于预测场景下第二段成本。这样第一轮LB合理也能减少迭代。代码中就是在循环前先设置xi_cell{1} xi_pred; Y_cell{1} Y_0;并把对应的割平面约束加进去。5. 两阶段鲁棒微电网避坑指南六个常见现象与对策5.1 子问题报二次约束或不可行现象固定第一步后子问题求解报“QP不是凸”或“二次约束无可行解”。原因对偶变换时把等式约束的λ设为自由变量后与binvar相乘的big-M约束写错导致λ无界或约束矛盾。解决先固定λ的边界用M作为辅助在子问题前先加λ 0或明确λ的上下界如果仍不可行将M从1000调成10看是否数值问题。另外检查内层LP本身是否可行可在相同ξ下直接解一次原LP若不可行就是第二阶段可行域写错了。5.2 主问题LB不更新现象多轮迭代LB一直等于第一轮的值。原因z没有被割平面约束限制住或者割平面约束方向写反了。常见错误是z ...而不是z ...。另外如果MP目标里漏加了zz对目标没作用割平面再怎么加也不会影响LB。解决打印每一轮MP求解后的value(z)和value(obj_mp)确认割平面加入后MP中z至少等于该轮子问题成本。如果z始终是负值说明没有切割它优先检查约束方向。5.3 求解时间随迭代增速过快现象第1轮1秒第3轮10秒第5轮100秒。原因主问题每轮新增一组第二阶段变量和约束CPLEX需要重新求解整个MILP。如果新增场景没有让整数变量u重新优化大部分算力浪费在重复搜索。解决当u的整数解连续两轮不变时可以固定u为当前值只求解LP子结构或者使用热启动。YALMIP支持assign和求解器热启动但CPLEX热启动需要保留求解器对象比较麻烦。工程里更简单的是减少不必要的割平面如果子问题返回的场景和已有场景相同就严格收敛了若连续两轮场景相同但gap没达标说明子问题被错解或上下界更新有误而不是再加割。5.4 不确定量方向选择时big-M数值问题现象子问题返回的最坏场景所有时段全部取上界且成本异常大。原因big-M过大导致的数值扰动λ_t的冗余让r_t出现假值。比如M1e6CPLEX在整数预求解中容忍误差r可能被错误赋了一个比λ大几个量级的值。解决把M收紧到max(abs(lambda)) * 1.2或根据物理边界用M max_dev * max_dual_bound。同时把CPLEX整数容差从默认1e-5收紧到1e-6并检查子问题目标是否包含未线性化的残余双线性项。5.5 第一阶段启停变量在子问题中被“锁定”导致UB虚低现象UB在迭代中有时低于LB或者最终gap为负。原因子问题固定x后第二阶段约束本来应该包含所有耦合约束但如果第二阶段模型里漏掉了爬坡或SOC的跨时段约束那x表面上可行实际不可行。子问题只搜索了“可行子空间”的一部分Qk偏小UB偏低。解决把第二阶段所有与第一阶段耦合的约束都在子问题里显式列出对每个新场景在主问题中添加耦合约束时也要保证与子问题完全一致。一个验证技巧拿第1轮主问题解出的x*代入一个纯仿真程序跑一遍看第二阶段是否可行。这个“对拍”能揪出很多模型不一致的地方。5.6 割平面收敛但最终结果不可执行现象gap已经小于1e-4但把最优决策放到仿真软件里出现储能SOC越限或联络线功率越限。原因主问题里新增场景约束是“在特定场景ξ_j下可行”但CCG收敛只保证了这些已发现场景的可行性没有覆盖所有可能的ξ。如果预算Γ取得太小未覆盖到的极端场景依然会越限。这不是算法错误而是鲁棒集合选取宽度不足。解决回代验证时用一组独立的历史极端场景测试如果越限增大Γ或改用盒式集重新求解。另外把第二阶段变量的边界设置成比物理限值稍微内缩5%给数值误差留出余量这是工程落地里常见的习惯不算作弊。6. 进阶让两阶段鲁棒不那么保守——仿射可调节策略与场景耦合两阶段鲁棒最常被人吐槽的就是最坏场景太极端成本偏高。有一种工程上实用的升级把第二阶段变量从“完全自由调整”改成“按预测误差的线性仿射函数调整”。比如储能出力P_ess_t P_ess_t^0 α_t * (ξ_t - ξ_pred_t)其中α_t是可调系数也放在第一阶段决策里。这样第二阶段变量不再是独立的y而是和不确定量ξ明确挂钩避免最优解依赖“看到ξ后再完全重算”的过度灵活假设。在YALMIP里实现这个思路很简单把原来的第二阶段变量y直接替换成y y0 Alpha * (xi - xi_pred)其中y0和Alpha都是sdpvar变量。主问题里所有场景的约束对xi xi_j分别写出目标函数里的d^T y变成d^T y0 d^T Alpha * (xi_j - xi_pred)。这样双线性项消失了因为Alpha乘以具体的场景值xi_j是一个常数矩阵与变量相乘还是线性的。代价是变量维度多了T * 不确定量个数个α但主问题仍是MILP。这个技巧常见于可调整鲁棒优化Adjustable Robust Optimization适合微电网里储能和燃气轮机有快速响应能力的场景。它比标准两阶段鲁棒的保守度低又比随机规划更快。需要提醒的是仿射策略限制了第二段决策的灵活性如果系统本身允许完全重优化它的结果不会比CCG更优但它产出的计划更“可执行”调度员看得到“误差来了怎么调”。我自己的习惯是先用标准CCG跑出上界再用仿射策略跑一个解两个结果做对比。如果差距小于5%说明系统对不确定性不敏感仿射策略足够如果差距大说明需要保留完全两阶段最优性。这个对比能帮你跟业务方讲清楚“为什么鲁棒优化多出来的成本是必要成本”。另外针对第二阶段确实需要整数决策的场景比如储能充放电状态、可中断负荷可以把状态变量留在第一阶段或者用外近似把整数决策的凸包加入子问题。我踩过的坑是直接尝试用YALMIP对含整数的第二阶段做对偶得到的结果在SOC递推约束上完全失效。后来换成“储能状态由功率符号决定符号变量进第一阶段”问题就顺了。如果你只是在做预研先把标准CCG跑通再往仿射策略、多场景耦合、时变Γ扩展。不要一开始就上可调节鲁棒因为它的调试点比CCG更多尤其α的边界和稳定约束之间很容易冲突。希望这些经验能让你少走几周弯路希望帮到你。本文还有配套的精品资源点击获取