简介面向微电网规划与电力系统优化研究者压缩包内提供围绕风电、光伏、储能及燃气轮机容量配置的两阶段鲁棒优化程序实现。模型以一阶段决策储能、风电、光伏的安装容量二阶段规划风光燃储的实际出力并采用列与约束生成算法CCG求解可处理源荷不确定性适合研究生、科研人员及工程师参考。程序主体由主问题、子问题及CCG迭代脚本构成结构清晰便于调试与二次开发。包内共19个文件大小约3.76MB主要包含MATLAB脚本、Word/PDF模型说明、典型日数据Excel、矩阵推导图片及参考文献CAJ覆盖从模型构建、算法推导到仿真验证的完整资料链。已有1331人学习内容涉及两阶段鲁棒构建过程、CCG求解步骤、整体模型文档及实现效果截图结合参考文档与推导图片可降低复现门槛辅助快速搭建微网鲁棒容量配置方案。1. 两阶段鲁棒优化算法做微网电源容量配置先定容量、再对抗最坏场景做微网规划时最头疼的不是装机上限而是风光出力到底按哪个数来设计容量。按典型日均值算储能永远不够按历史最极端情况算投资成本又高到立项过不了。两阶段鲁棒优化算法把这个问题拆成“一阶段定容量、二阶段算最坏场景下的运行代价”通过min-max-min模型一次性给出风电、光伏和储能的配置方案。这套资源正是围绕这个思路展开里面有完整的MATLAB主程序、MP/SP子问题、CCG迭代代码、模型推导文档和四个典型日数据适合正在做微网规划、写电力系统方向论文或刚接触鲁棒优化的从业者。按下文流程跑通之后你就能自己改数据、调不确定预算复现出一套能写进论文的抗风险容量配置结果。2. 微网容量配置的两阶段鲁棒模型决策变量、不确定集与min-max-min结构2.1 一阶段容量决策与二阶段运行调度先定装多少再算最坏怎么开微网里要规划的电源主要有四类风电、光伏、储能、燃气轮机。风机台数、光伏板面积、储能电池容量这类决策一旦落地短期内不会改必须放在一阶段也就是“容量规划”。而每个时段风机发多少、光伏要不要弃掉、储能什么时候充什么时候放、燃气轮机带多少负荷属于运行层面到一天里根据实际天气随机应变属于二阶段“运行调度”。这两个阶段不是独立解出来的运行层的可行性会反过来限制容量层。比如风电容配得很大但二阶段在最坏场景下夜间无风无光、负荷又要保供储能和燃气轮机顶不住那这组容量就是不可行的。两阶段鲁棒优化就是让一阶段决策必须对所有可能的二阶段实现都可行同时把最坏运行成本也打进总成本里。因此程序里一阶段变量主要落在风电、光伏和储能容量上二阶段变量则是各时段出力和储能SOC。注意燃气轮机容量在这个资源里大概率是给定的一阶段不优化它因为它更多是作为备用调节资源存在这也符合“风电、光伏、储能容量配置”的主标题定位。我在拿到程序后第一件事就是把readme里关于燃气轮机容量是常数还是变量确认清楚否则后续调整任何参数都可能改变问题的可行域。更直白地说如果你的场景里燃气轮机也是待规划对象需要在一阶段变量里补上它的投资成本项同时修改MP.m中的容量上限约束。2.2 盒式不确定集与不确定预算Γ鲁棒性和经济性的调节旋钮两阶段鲁棒优化的核心是不确定性集合。这套程序里最常用的建模方式是盒式不确定集对每个时段的风电光伏出力在预测值附近划一个波动区间。例如风电在t时段的实际值可以落在 [p_w_hat(t) - Δw(t), p_w_hat(t) Δw(t)] 内光伏同理。如果每个时段都独立取边界最坏场景会变成“所有时段全部最低”配置结果会极度保守。所以程序里引入了不确定预算Γ限制整个调度周期内风电光伏总偏差的累积量不超过一个阈值。例如∑(|p_w(t)-p_w_hat(t)|/Δw(t)) ≤ Γ这样最坏场景不会同时发生在每个时段而是在有限数量的时段里取极端值。Γ越大越保守越小越接近确定性优化。通常在仿真里从Γ4开始调每增加2观察一次储能容量和总成本的变化直到曲线变平就能找到一个性价比最高的鲁棒水平。main.m里一般会直接定义Gamma变量如果没定义就自己在参数区加上并传给SP.m用于构造不确定集约束。构造不确定集的代码思路通常是这样的% 构造盒式不确定集示意 u_w_hat data.wind; % 风电预测值 Delta_w 0.2 * u_w_hat; % 波动范围可以按比例取 u_pv_hat data.pv; % 光伏预测值 Delta_pv 0.2 * u_pv_hat; % 不确定变量上下界 U struct(lb, [u_w_hat - Delta_w; u_pv_hat - Delta_pv], ... ub, [u_w_hat Delta_w; u_pv_hat Delta_pv]); U.budget Gamma; % 不确定预算用于控制所有时段偏差总和这里的波动范围Delta是最容易拍脑袋的参数。我一般先用历史预测误差的95%分位数算而不是直接取20%固定比例。如果数据表里没有误差信息再退而求其次用预测值的20%、30%试。波动范围取太大容量配置会过度保守储能投资高得离谱。2.3 模型的标准数学形式与典型日加权完整模型可以写成min c_inv*x max_{u∈U} min_{y∈F(x,u)} c_ope*y。外层min是投资成本加最坏运行成本内层max-min是求在给定容量x的前提下最恶劣的不确定场景能造成的运行代价。x是一阶段容量变量u是风电光伏出力等不确定量y是二阶段运行变量。约束里包含功率平衡、储能容量与充放电约束、燃气轮机上/下限、爬坡约束以及典型日内时段之间的SOC耦合。这个资源用的是四个典型日数据意味着不是全年8760小时逐时计算而是用四个代表性日来近似全年运行成本。这里有一个容易忽略的细节每个典型日必须有一个权重比如“春代表60天夏代表80天……”否则运行成本量级会错。程序中如果没显式给权重通常会在readme或整体.docx里说明。我一般会在main.m里搜索“dayWeight”或“weight”找不到就直接在初始化区手动加一个4×1向量% 典型日权重示例加总等于365天 dayWeight [60, 90, 95, 120]; % 四个典型日分别代表的天数注意权重不是必须四天加起来等于365因为这四个日代表的不是完整四分之一年而是需要结合数据生成时的聚类占比所以这里要看清readme里的说明再改。权重不对最直接的表现是运行成本在目标函数里的占比明显异常。如果运行成本比投资成本低两个数量级大概率是权重没乘上。3. 用CCG算法求解主问题MP、子问题SP与割迭代的MATLAB框架3.1 为什么这个场景选CCG而不是Benders分解解决两阶段鲁棒问题有两个主流路径Benders分解和列与约束生成CCG。Benders的思路是不断把第二阶段的极值场景通过对偶割加到主问题里割是线性的主问题保持小规模但收敛速度对子问题里的整数变量不友好。微网容量配置如果二阶段还要考虑燃气轮机启停等0-1变量Benders的对偶信息会变弱迭代次数可能非常长。CCG不一样它在每一轮迭代中不仅加一条约束还把识别出的最坏场景对应的二阶段变量整体作为新的一列加入主问题。主问题规模变大但每一轮都提供更强的下界对含0-1变量的子问题兼容性更好。这个资源里的MP.m和SP.m就是按这个思路写的SP负责找最坏场景MP负责把场景“接住”addC.m则把对应列和约束加入模型。对微网这种中小规模优化问题CCG通常二三十轮就能收敛。3.2 主问题MP的变量结构和迭代信息传递主问题的输入是当前已经识别出的K个最坏场景集合U_k。每轮迭代主问题要做的就是在容量决策x不变的前提下同时让老的场景和新的场景都能可行并计算当前总成本下界。具体实现时主问题会为每个已发现的场景引入一组独立的二阶段变量y_k和运行成本η_k然后求解。这样x是全局共享的y_k则属于各场景局部变量。每次迭代后把新的u_{K1}加进去新增一组y_{K1}和η_{K1}。这是CCG的核心特征割约束是新场景约束本身而不是Benders式的一条线性不等式。在MATLAB里如果用了YALMIP主问题的框架大致是% MP.m 主问题框架示意YALMIP风格 % x一阶段容量变量Y{k}第k个场景的二阶段变量eta最坏运行成本代理 x sdpvar(3, 1); % 风电、光伏、储能容量 eta sdpvar(1, 1); Y cell(K, 1); for k 1:K Y{k} sdpvar(nY, 1); % nY是二阶段变量个数 end % 目标投资成本 最坏运行成本代理值 obj cost_inv * x eta; % 约束每个已识别场景都要可行并且eta大于等于该场景运行成本 Constraints []; for k 1:K Constraints [Constraints, eta cost_ope * Y{k}]; Constraints [Constraints, B * x C * Y{k} d - A_extreme{k}]; end optimize(Constraints, obj);这里的B、C、d、A_extreme需要根据具体模型展开。A_extreme{k}是第k个最坏场景下不确定量对约束的贡献通常是把预测值换成u_star后形成的常数项。如果你没有YALMIP用linprog也可以做但要把K组Y{k}按列拼成一个大向量用blkdiag构造约束矩阵。常见的错误是忘记每组Y{k}之间互相独立导致约束矩阵写成共享变量模型就乱了。3.3 子问题SP的强对偶转换与最坏场景识别子问题要解决的问题是给定一阶段容量x找出让运行成本最大的场景。它的内层是一个常规调度优化问题因此整体是max-min双层结构SP: max_{u∈U} min_{y∈F(x,u)} c_ope*y。直接求这个双层很难通常做法是先把内层调度问题写成对偶问题。由于内层问题是线性规划强对偶成立内层的min可以等价替换成它的对偶max。这样整个子问题就变成外层max和内层max的联合极大化问题。展开后会出现u与对偶变量λ的乘积项这是双线性项。盒式不确定集里u是有限区间加预算约束可以用线性化处理如果u是连续变量且目标里是线性项标准做法是把u放到对偶问题的约束里利用强对偶的互补松弛或者直接枚举不确定集极值点。对于只有1-2类不确定源的微网也可以把u离散成若干典型极端组合跑目标最小的“场景枚举”但理论上不如盒式CCG严谨。SP.m里你会看到它返回两个东西最坏场景u_star和对应的运行成本函数值。如果纯linprog则需要按照对偶推导手动搭矩阵。资源包里的“矩阵推导1.jpg、矩阵推导2.jpg、矩阵推导3.jpg”就是这个对偶过程的纸质推导建议跑程序前先把这三张图对着模型看一遍否则SP写错很难查。3.4 迭代收敛判定与最大迭代轮数CCG的收敛判据是主问题目标值和子问题目标值之间的间隙。一轮迭代里子问题最坏场景下的运行成本加上容量投资成本构成一个可行解的上界UB主问题得到的统筹成本是下界LB。实际工程上常用间隙gap (UB-LB)/UB通常设到1e-2或1e-3就够用。这里有一个经验微网容量配置场景少、维度低gap收敛到1e-3以内通常不超过20轮。如果你跑完发现迭代次数超过50还在震荡优先检查是不是主问题没有为新场景加新的y_k或者子问题返回的u_star根本没被加进去。资源里的main.m会输出每一轮UB、LB和gap打印出来观察曲线比直接看最终结果更能定位问题。调试时可以加一行输出fprintf(Iter %d: UB%.4f, LB%.4f, gap%.4f\n, iter, UB, LB, gap);如果gap在某几轮之间反复横跳大概率是线性化后的大M参数取得不合适或者子问题求出的u_star落在了不确定集之外。这一步可以打断点检查u_star是否还在上下界范围内。4. 在MATLAB里把程序跑起来文件分工、数据准备与参数调优4.1 资源包里的文件各干什么从readme到MP2.m解压后你会看到一个写着“18-基于两阶段鲁棒优化算法的微网多电源容量配置”的文件夹里面核心文件如下文件作用main.m主程序设置参数、初始化不确定集、循环调用MP和SP输出结果MP.m主问题模型求解当前最坏场景集合下的容量配置和成本下界MP2.m主问题第二版可能用于不同鲁棒水平或二阶段二次求解SP.m子问题模型给定一阶段容量后求最坏场景和上界addC.m将子问题返回的新场景对应约束加入主问题生成割四个典型日数据.xlsx风电、光伏、负荷等典型日数据按列组织整体.docx、模型.docx模型说明和目标函数定义文档两阶段鲁棒构建过程.docx从确定性模型到两阶段鲁棒的推导过程矩阵推导1.jpg/2.jpg/3.jpg子问题对偶和KKT条件的手写推导实现效果截图.docx终端运行结果和结果图方便对照我的建议是先读readme.txt再打开整体.docx了解目标函数然后按顺序看矩阵推导图最后再打开MATLAB跑main.m。不要一上来就点运行不然MP.m和SP.m里变量名含义不清楚一旦报错你都不知道是哪一步的逻辑问题。4.2 四个典型日数据加载与典型日权重设置四个典型日数据存放在一个xlsx里通常每一列是一个变量或一个时段的测量值。常见列有hours、wind_predict、pv_predict、load_predict以及可能的燃气轮机燃料价格。你可以用MATLAB的readtable快速读取并画图验证% 读取典型日数据 data readtable(四个典型日数据.xlsx); % 查看前几行确认列名 disp(data(1:5,:)) % 假设列名wind, pv, load figure; plot(data.wind, LineWidth, 1.2); hold on; plot(data.pv, LineWidth, 1.2); plot(data.load, LineWidth, 1.2); legend(风电预测,光伏预测,负荷); grid on;这段代码先读取Exceldisp打印前五行确认列名再用plot把三条曲线画出来目的是确认数据范围是否合理。如果数据是归一化到0-1的标幺值而你没注意就直接乘成本系数容量结果会对不上。读取之后要把单位归一风电光伏预测值如果是在某个基准值下的标幺值投资成本系数也要按同一个基准换算。典型日权重放在main.m的开头例如dayWeight [90, 90, 95, 90]; % 四个典型日分别代表的天数这个向量的含义是每个典型日在一年里代表多少天。权重不对最直接的表现是运行成本在目标函数里的占比明显异常。如果运行成本比投资成本低两个数量级大概率是权重没乘上。4.3 运行主程序从参数设置到查看结果主程序的核心流程是先定义系统参数然后初始化CCG循环在循环里依次调用SP、addC、MP直到gap满足要求。参数设置一般长这样% 主参数设置main.m 中常见写法 T 24; % 每个典型日的调度时段数 NumDays 4; % 典型日个数 Gamma 8; % 不确定预算越大越保守 MaxIter 30; % CCG最大迭代轮数 TolGap 1e-3; % 收敛间隙 windCapMax 200; % 风电容量上限kW pvCapMax 150; % 光伏容量上限kW essCapMax 100; % 储能容量上限kWh这里的T是优化周期四个典型日各24小时所以二阶段变量要按日展开。Gamma是不确定预算初始可以取6到8之后再敏感性分析。windCapMax和pvCapMax是规划层给的装机上限来自电网接入条件或土地面积如果你改了典型日数据记得同步改这些上限否则最优解会被上限卡住。设置好后在MATLAB里打开main.m直接运行。如果环境干净第一次跑通可能需要几分钟因为每个典型日都是一个24时段优化四天就是96个时段的规模对多数电脑来说没有问题。4.4 结果怎么解读容量、运行成本和可行性程序最终会输出一阶段容量变量例如风电装机、光伏装机和储能容量。同时输出最坏运行场景下的总成本和投资成本。看结果时重点看三个东西第一容量配置是否落在上限以内第二最坏场景下有没有失负荷或弃风弃光越限的约束乘子信息第三储能SOC曲线在四个典型日里是否都能在一天内完成充放循环。如果储能容量很大但SOC曲线长期满格说明目标函数里储能投资权重不够或者不确定性太紧促使它过度预留容量。另外程序里的MP2.m很可能是一个用于二阶段或不同鲁棒水平的二次优化版本。你可以在main.m里切换MP.m和MP2.m对比同一组数据下两次求解的容量和成本判断是不是某个版本里把燃气轮机容量也当成了变量。跑之前先确认这一点免得你改完数据后燃气轮机容量被程序自己优化到边界结果和预期差很远。5. 避坑与排查CCG复现中的五个典型问题5.1 子问题对偶无界最坏场景识别失败现象运行SP.m时MATLAB返回“Problem is unbounded”或求解器警告“dual infeasible”然后主循环卡住gap不更新。原因最常见的是子问题在强对偶转换过程中丢了变量的非负约束。比如储能SOC约束在写对偶时如果没有给对应的对偶变量加符号约束或者原始问题里SOC下界写成等号就会让对偶问题出现自由方向目标函数可以无限大。解决把矩阵推导1到3与SP.m逐项对照重点检查对偶变量的上下界。我一般会在SP内层调度问题里先随机固定一组u把内层问题单独用linprog跑一遍确认它是有界且可行的再进行对偶转换。如果单独跑没问题对偶后却无界那就是对偶转换或线性化那一步的问题。5.2 主问题规模膨胀内存占用过高现象迭代到20轮以后MATLAB明显变卡内存占用飙升主问题求解时间一次比一次长。原因CCG每轮都会给新场景增加一组二阶段变量主问题变量数随迭代次数线性增长。微网虽然规模不大但如果你把每个典型日都展开成96个时段并且每个时段的储能SOC、机组出力、爬坡变量都复制一份变量数量会很快过万。解决第一用稀疏矩阵构造约束不要让YALMIP生成全稠密矩阵第二将MaxIter限制到25或30同时把TolGap放松到5e-3很多工程场景不需要1e-4的精度第三如果必须跑很多轮可以考虑加一个“冗余场景清洗”把那些对应运行成本很低、约束从未作用的早期场景变量从主问题里移除只保留必要的割约束。5.3 容量结果过度保守鲁棒性调不回来现象风电和光伏容量总是被压到下限附近储能被配得很大总成本比确定性优化高出一大截怎么看都像病态方案。原因不确定预算Γ设得太大或者每个时段的风光波动区间Δ取的是最大历史偏差。盒式不确定集的极端场景在多个时段同时取边界Γ一旦超过时段数的一半产出的容量几乎是在为“全年每天都是最坏情况”买单。解决先把Γ设为0跑一遍确定性容量配置作为基线然后Γ从2逐步增加到10画一条“总成本-Γ”曲线找到拐点。通常拐点出现在Γ4~6之间。如果你希望重点研究夜间无风场景可以把不确定集只加在风电上光伏仍用预测值避免两个不确定源叠加造成的双倍保守。5.4 整数容量变量与linprog的矛盾现象把储能柜数量或风机台数定义成整数变量后SP或MP报错“Integer variables are not supported”或“No feasible solution found”。原因MP.m里如果用linprog求解变量默认都是连续的。而容量配置中储能柜、风机台数严格来说是离散的但两阶段鲁棒里很多时候为了可解性会先把它们松弛成连续变量。结果你硬改成整数linprog就罢工了。解决如果想保留整数约束把linprog改成intlinprog并给每个整数变量指定索引和上下界。不过要注意CCG每轮SP求解时需要的是固定的一阶段变量整数x在MP里求解后传给SP没有问题但SP内层仍然是连续优化。另一种更简单的做法是保持连续得到结果后向上取整再把取整后的值回代到SP验证可行性。大部分工程容量配置允许这种近似。5.5 单位量级不一致导致收敛判据失效现象主循环虽然能跑但UB和LB总是差几百gap永远降不到1e-3或者在两个值之间震荡。原因目标函数里投资成本单位是万元运行成本单位是元或者数据是标幺值而成本按实际值算导致两项量级相差几个数量级。CCG的gap是用相对值算的量级差大了数值误差会掩盖真实收敛。解决先把所有成本和容量统一到一个基准值。我惯用做法是设置一个基准功率比如100kW和基准成本比如1万元所有目标函数系数除以基准值再求解。这样gap的数值更有意义后续敏感性分析也更清晰。修改后在main.m开头打印一次总目标函数的量级确保UB和LB都在同一指数范围。6. 进阶从复现到验证以及几个值得做的扩展跑通程序只是第一步关键是验证结果是否可信。我每次都会把一阶段容量结果固定下来然后用二阶段子问题单独跑一次极端场景校验把风电和光伏设到预测值的0.6倍负荷设到预测值的1.1倍再观察储能是否还能维持功率平衡。这个校验不依赖MP和CCG只要SP里的调度模型本身正确它能直接告诉你配置方案在更恶劣条件下会不会失负荷。% 校验代码思路固定容量后的极端场景调度 x_fixed [capacity_wind, capacity_pv, capacity_ess]; % 把风电光伏出力下调负荷上调 u_extreme 0.6 * u_predict; % 示意 results run_SP(x_fixed, u_extreme); if results.power_balance_violation 0 disp(配置方案在极端场景下不可行需要增加容量或调整不确定集); end这里的u_predict是程序里已经读入的预测值u_extreme是人为构造的极端场景。run_SP是SP.m封装后的函数你需要传入固定容量和场景数据返回调度结果。如果功率不平衡量的绝对值大于某个小阈值说明容量方案不够硬需要回过去加大储能或减小不确定集。再往下走有几个值得扩展的方向。第一把固定盒式不确定集改成数据驱动的多面体不确定集利用历史风光的协方差信息可以在鲁棒性相同的前提下显著降低投资成本第二在二阶段加入燃气轮机启停的0-1状态变量虽然会让SP变成混合整数规划但强对偶不再成立需要用迭代CCG或L-shaped的拓展方法第三把“只求容量”扩展到“容量运行策略联合优化”比如主问题里同时决策储能日前充放电计划这样能进一步挖掘储能在调峰和备用里的价值代价是主问题规模又上一个台阶。这套包里从矩阵推导到MP/SP代码都齐了按上面的流程跑一遍两阶段鲁棒容量配置基本能落地。我从第一次跑这个程序起就养成一个习惯任何改动数据或参数都先跑一遍Γ0的确定性基线再逐步加大不确定集合最后做极端场景回代验证。这样出了问题能快速定位是数据问题、模型问题还是算法问题而不是把错都怪到鲁棒优化头上。希望帮到你。本文还有配套的精品资源点击获取