微网电源容量配置:两阶段鲁棒优化模型与MATLAB实现
做微网规划咨询这几年被问得最多的一个问题就是手头有历史负荷和风光数据怎么确定风机、光伏、储能该装多少容量才最稳妥常规做法是拿典型日做确定性优化但现实里风光出力一个波动算出来的方案立刻就不够了。这篇分享的是一套在MATLAB环境下用两阶段鲁棒优化算法做微网电源容量优化配置的完整方案包含模型推导、CCG求解流程、代码骨架和算例分析适合正在做微网规划、储能配置或者鲁棒优化方向研究的朋友参考。1. 微网电源容量配置为什么要做成两阶段鲁棒优化1.1 确定性优化的问题算出来的方案一遇波动就崩先说我见过最多的一种翻车场景。不少工程师做容量配置时用的是确定性优化把光伏、负荷取一个典型日的曲线建立目标函数规划电源容量然后用求解器一跑得到一组最优解。这组解在数据上很漂亮但问题在于——典型日只是历史数据的一个平均切片。光伏午间的出力峰值、晚高峰的负荷叠加、阴雨天连续多日的出力低落这些非典型的情况一旦出现配置方案就原形毕露。典型日的风光出力假设偏高储能配置就会偏小负荷假设偏低柴油机的容量就会不足。结果就是系统实际运行时频繁切负荷、靠外部电网支撑甚至直接失稳。确定性优化的本质是用一个确定的场景替代所有可能的未来这在不确定性环境里是站不住脚的。做微网规划必须回答的问题是未来可能出现的最恶劣情况是什么我的配置方案在这种最恶劣情况下花多少钱能撑住。两阶段鲁棒优化解决的就是这个问题。1.2 鲁棒优化的核心思想按最坏情况做决定鲁棒优化的思路其实很朴素与其赌一个最可能的场景不如在最坏情况下找到一个成本可控且兼顾经济性的方案。最坏情况不是凭空想象而是靠不确定集约束出来的——风光出力、负荷在某个波动范围内变化所有可能的取值组合构成一个集合鲁棒优化的目标是在这个集合里找到一个最不想面对的场景针对这个场景做决策。这个思路放在微网规划里非常合适因为微网的不确定性来源是结构性的光伏出力受云层遮挡和辐照影响风电出力受风速随机性影响负荷预测存在偏差。这些不确定因素很难全部精确建模但它们的波动范围是可以用历史数据框定出来的。用鲁棒优化帮你把未来可能有多差想清楚再去决定现在怎么配置。1.3 两阶段到底分的是什么阶段两阶段鲁棒优化里的两阶段指的是决策变量的时序分工。第一阶段做容量配置决策装多少风机、光伏、储能容量多大这一阶段在不确定性还没揭开时就要完成第二阶段做运行调度决策各电源出力多少、储能充放电多少这一阶段是在不确定参数已经呈现之后针对某个具体的风光出力场景做出的适应性调整。这里有个关键逻辑容量一旦确定在规划期内是固定不变的这是现在就必须拍板的决策而运行推理是随场景变化的面对不同的实时出力情况可以灵活调整。两阶段鲁棒模型就是把这两个不同性质的决策放进同一个优化框架里让第一阶段决策在考虑所有未来可能场景时达到全局最优。2. 两阶段鲁棒优化模型目标函数、不确定集与约束体系2.1 第一阶段决策容量变量与投资成本模型第一阶段是投资决策层决策变量是各类分布式电源的配置容量。一个典型的微网系统里通常包括风机WT、光伏PV、储能ESS和柴油发电机DG容量优化配置就是要决定这四类电源各装多少容量。第一阶段的目标函数是投资成本叠加运行成本的最坏情况期望也就是最小化[ \min_{x} \left( C_{inv}^T x \max_{u \in U} \min_{y} , C_{op}^T y \right) ]其中投资成本 ( C_{inv} [c_{WT}, c_{PV}, c_{ESS_P}, c_{ESS_E}, c_{DG}] )对应的容量变量 ( x [P_{WT}, P_{PV}, P_{ESS}, E_{ESS}, P_{DG}] )。注意储能要区分功率容量和能量容量两个维度的成本不同这个在第一阶段建模中必须拆开处理。第一阶段还需要满足约束各电源配置容量不超过站址可开发上限储能能量与功率比例在合理范围内。这些约束形式简单但直接影响第二阶段的可行域大小建模时别忽略。2.2 第二阶段决策运行调度变量与运行成本模型第二阶段是运行层给定第一阶段确定的容量在具体的风光出力场景下安排运行策略。第二阶段的决策变量包括各时刻的电源出力、储能充放电功率、柴油机出力、向外部电网购售电功率、切负荷功率等。运行成本包含柴油机燃料成本、购电成本、售电收益以及切负荷惩罚成本。第二阶段问题要用最坏场景下的最小运行成本来描述逻辑是先看不确定性参数取哪个值时运行最困难再在这个最困难场景下做最优调度。风光出力差、负荷高的场景运行成本必然高第二阶段优化的目标是在不确定集里找到那个让运行成本最高的场景然后把这个场景下的成本做到最小。运行约束包括功率平衡约束、各电源出力上下限约束、储能SOC递推约束、购售电功率限制、切负荷上限等。需要留意的是储能SOC约束是跨时段耦合的这会给子问题的对偶推导带来不小的复杂度后面第三节细说。2.3 不确定参数与盒式不确定集的构造两阶段鲁棒优化的核心之一是构造不确定集 ( U )。微网里最常见的不确定参数是风机出力、光伏出力和负荷。工程上做容量配置最实用的不确定集就是盒式Box不确定集加预算约束Budget Constraint[ U \left{ u \mid u_t^{min} \le u_t \le u_t^{max},\ \sum_t |u_t - \bar{u}_t| / \Delta u_t \le \Gamma \right} ]盒式约束把每个时段的波动限制在上下界内预算约束则限制了整个调度周期内总偏离程度。预算参数 ( \Gamma ) 直接控制鲁棒保守程度( \Gamma ) 越大允许偏离均值的时段越多方案越保守、抗风险能力越强但经济性越差( \Gamma 0 ) 时退化为确定性优化。实测下来微网容量配置里不确定集参数设置的原则是( \Gamma ) 取总时段数的20%-40%比较稳既能覆盖大部分实际波动又不至于让结果保守到无法接受。波动幅度上下界可以用历史数据的百分之十分位数和九十分位数来标定比拍脑袋强得多。2.4 完整模型与矩阵形式的统一写法上面各项合起来两阶段鲁棒优化模型的完整结构就是min-max-min三层外层min是第一阶段投资决策中层max是不确定集里最坏场景选择内层min是第二阶段运行调度决策。写成统一的矩阵形式就是[ \min_{x} \left( c^T x \max_{u \in U} \min_{y \in \Omega(x,u)} d^T y \right) ] [ s.t.\ Ax \ge b,\ x \in X ]其中 ( \Omega(x, u) { y \mid Ey \le f Fx Gu } ) 是给定容量 ( x ) 和不确定参数 ( u ) 后的运行可行域。矩阵形式的优点是方便写代码时用YALMIP批量构建约束整个模型水到渠成。3. CCG求解流程把min-max-min拆成可迭代的两个问题3.1 主问题MP带割平面约束的容量优化CCGColumn and Constraint Generation列与约束生成算法是目前求解两阶段鲁棒优化问题最主流的算法。核心思想是迭代逼近用一个主问题和一个子问题交替求解主问题逐步把最坏场景对应的约束割平面加进来子问题则负责寻找最坏场景。主问题形式如下[ \min_{x, y, \eta} \ c^T x \eta ] [ s.t.\ Ax \ge b,\ x \in X ] [ \eta \ge d^T y_k, \quad Ey_k \le f Fx Gu_k, \quad \forall k \le K ]其中 ( k ) 是迭代次数( u_k ) 是已经发现的“坏场景”集合每一个坏场景对应一组运行变量 ( y_k ) 和一条割平面约束 ( \eta \ge d^T y_k )。主问题的含义是在已知的所有最坏场景下选择容量配置 ( x )使投资成本和最坏运行成本之和最小。每迭代一次主问题就会多一组坏场景约束方案也就会更稳健一分。3.2 子问题SP最坏场景下的运行优化子问题是在给定第一阶段容量解 ( x^* ) 的条件下寻找不确定集里让运行成本最大的场景[ Q(x^) \max_{u \in U} \min_{y \in \Omega(x^, u)} d^T y ]这是一个max-min双层结构直接求解在MATLAB里是跑不动的。常规做法是对内层min问题取强对偶把min-max结构转化为单个max问题[ Q(x^) \max_{u \in U, \lambda \in \Lambda} \ \lambda^T (f Fx^ Gu) ] [ s.t.\ E^T \lambda \le d,\ \lambda \ge 0 ]对偶转换成立的前提是内层问题是线性规划所以第二阶段的运行模型必须尽量保持线性比如用分段线性成本函数替代非线性燃料成本曲线。碰上非线性成本函数就得在建模阶段预处理线性化这是跑代码前必须做的一道功课。3.3 大M法处理双线性项让子问题变为MILP强对偶之后的子问题中( \lambda^T Gu ) 是一个双线性项对偶变量与不确定变量相乘问题变为非凸。工程上最常见的处理办法是对不确定变量进行离散化或引入大M法线性化。以大M法为例引入辅助变量 ( z_{t} |u_t - \bar{u}_t|/\Delta u_t ) 标识不确定参数是否偏离均值配合约束[ \lambda^T G u \sum_t \left( \lambda^T G_t \bar{u}_t \lambda^T G_t \Delta u_t \cdot z_t \right) ]同时对 ( \lambda ) 和 ( z ) 的乘积项引入辅助变量 ( v )用Big-M约束强制替换。这一步会引入大量0-1变量子问题变成MILP求解时间会明显上升。实测时M的取值很影响求解速度——M太大会造成数值病态M太小会错误截断可行域。一般建议M取对偶变量理论上界的5-10倍。3.4 迭代终止条件与割平面生成逻辑CCG的完整迭代流程如下初始化令 ( LB -\infty )( UB \infty )设置最大迭代次数和收敛阈值 ( \epsilon )。求解主问题MP得到最优解 ( x^, \eta^)更新 ( LB c^T x^* \eta^* )。固定 ( x x^* )求解子问题SP得到最坏场景 ( u^* ) 和目标值 ( Q(x^) )更新 ( UB c^T x^ Q(x^*) )。如果 ( (UB - LB)/LB \le \epsilon )终止迭代输出 ( x^* ) 为最优容量配置。否则把当前找到的最坏场景 ( u^* ) 加入主问题新增一组运行变量 ( y_{k1} ) 和割平面约束回到步骤2。CCG的优势是收敛速度快多数情况下10轮以内就能收敛到1%以内的间隙。每一步实际跑起来并不复杂关键是主问题和子问题之间的数据接口要清晰我习惯把两类问题的约束分别封装成函数主程序只负责调度它们。4. MATLAB实现要点YALMIP建模、求解器配置与代码骨架4.1 环境准备YALMIP、求解器的安装与路径配置MATLAB里搭建两阶段鲁棒优化模型最顺手的组合是YALMIP加商业求解器。YALMIP是一个免费的MATLAB建模工具箱支持用接近数学表达式的语法构建优化模型可以省掉大量矩阵维度拼接的烦恼。求解器推荐Gurobi或MOSEK两者都支持MILP学术许可免费申请。安装YALMIP只需要把下载的文件夹加入MATLAB路径再把求解器的可执行文件路径配置好然后在代码里使用yalmip(clear)清除旧缓存。实测中一个常见的坑是MATLAB版本与求解器接口版本不匹配报错大多集中在Invalid MPS file这类处理方式就是去Gurobi官网下载对应MATLAB版本的接口包而不是用旧版本的通用接口。目前YALMIP更新到最新版本后对MATLAB 2020b-2026b的兼容性都不错。4.2 数据准备典型日生成与不确定集参数初始化进入建模之前先把数据准备做好。容量配置通常用典型日或典型季节日作为计算基础不要直接用365天原始数据否则计算量会爆炸。先用K-means把全年的风光和负荷曲线聚成3-6个典型场景每个场景带权重再对每个典型场景构造不确定集。典型场景数目通常取3-4个太多会成倍放大主问题规模。不确定集参数初始化代码里建议封装成独立的函数比如buildUncertaintySet(histData, Gamma)输入历史数据和预算参数输出每个时段波动的上下界和平均场景。4.3 主程序框架CCG主循环的伪代码CCG主循环骨架如下YALMIP语法% 主问题定义 x sdpvar(sizeX, 1); % 容量决策变量 eta sdpvar(1, 1); % 辅助变量 Constraints [Aineq * x bineq]; obj_MP c_inv * x eta; % 子问题定义初始场景 u_0 均值场景 for k 1:maxIter % 1. 求解主问题 optimize(Constraints, obj_MP, ops); x_opt value(x); eta_opt value(eta); LB value(obj_MP); % 2. 固定 x_opt求解子问题获得最坏场景 u_opt 和 Q(x_opt) [Q_val, u_opt] solveSubproblem(x_opt); UB c_inv * x_opt Q_val; % 3. 收敛判断 if (UB - LB) / LB epsilon break; end % 4. 新增割平面约束 y_k sdpvar(sizeY, 1); Constraints [Constraints, eta d * y_k, ... E_y * y_k f F_x * x G_u * u_opt]; obj_MP c_inv * x eta; end每一步迭代新增一组变量和约束本质上是逐步扩充主问题的“最坏场景库”。规模上来之后主问题的约束数量会不断膨胀所以建议约束按矩阵形式批量加入不要逐条add否则运行效率骤降。4.4 关键建模细节对偶变量、大M法与互补松弛的YALMIP写法子问题求解是整套代码最容易出bug的部分。用YALMIP直接写出子问题的原始max-min形式是行不通的必须先手动做对偶转换。实操中是用dual函数解析原始约束的对偶变量表达式但更推荐自己先在草稿纸上把对偶形式推导清楚再直接写到代码里省去调试过程中的反复试错。强对偶转换后含有双线性项和0-1变量用binvar定义预算约束对应的0-1指示变量用大M法展开乘积项。YALMIP对大面积MILP支持得很好但不建议把M值设置成1e6这种夸张数字数学上没有问题数值上几乎是灾难。我一般先用小规模测试M的灵敏度M取10倍上界左右。5. 案例配置与结果分析风光储微网算例的容量优化结果5.1 算例参数与场景设置用一个典型的风光储微网来验证整套模型和代码。系统包含风机、光伏、储能、柴油发电机负荷峰值1MW并与外部电网连接允许购售电。设备参数设置如下设备类型投资成本运维成本容量上限风机5.5元/W0.03元/kWh2MW光伏3.8元/W0.02元/kWh1.5MW储能功率1200元/kW0.01元/kWh500kW储能能量900元/kWh-2000kWh柴油机1500元/kW燃料运维1.2元/kWh800kW风光和负荷使用某地区实际测量数据的归一化处理结果将全年数据聚类为夏季、冬季、过渡季3个典型场景。风光出力波动幅度设为均值的正负20%负荷波动为正负10%预算参数 ( \Gamma ) 取所有时段数的30%。5.2 两阶段鲁棒优化结果与确定性方案对比在相同参数下分别跑确定性模型和两阶段鲁棒模型结果差异非常明显指标确定性优化两阶段鲁棒优化风机容量850kW1150kW光伏容量600kW750kW储能功率/容量200kW/650kWh380kW/1200kWh柴油机容量400kW650kW总投资成本约430万元约590万元最坏场景运行成本约52万元/年约31万元/年鲁棒方案投资成本高了约37%但最坏场景下的年运行成本降低了约40%。把两者放在一起看鲁棒方案在极端天气、负荷尖峰叠加时依然能保证供电可靠而确定性方案在这种场景下已经失稳必然伴随大量切负荷和高额购电。用增量投资换极端场景下的可靠性这正是鲁棒优化的价值所在。5.3 不确定预算对配置结果的影响规律将预算参数 ( \Gamma ) 从0逐步调整到50%观察容量配置的变化规律会发现一个明显拐点( \Gamma ) 从0增大到20%的过程中储能和柴油机的容量配置快速上升光伏和风机也小幅提高但超过40%之后各类电源容量几乎不再变化。原因很直接不确定集对最坏场景的覆盖已经趋于饱和继续增大预算只是让方案“更保险”但实际不会遇到比这更极端的情况了。这个拐点在实际工程中可以直接换算成“确定了多少比例的极端时段”用来跟决策者沟通鲁棒保守程度的取舍。实测中碰到预算参数取过大导致无解或求解时间翻倍的情况记得回到这个规律来。6. 调试经验与常见坑从能跑到算得准6.1 子问题不可行的处理CCG迭代过程中最容易遇到的故障是固定某个 ( x^* ) 后子问题原始模型不可行。原因通常是第一阶段容量配置过小导致在某些波动场景下功率平衡无法满足比如储能容量太小无法平抑晚高峰。处理思路有两条。第一在第一阶段约束中加入“最小可行容量”限制确保任何可行解下第二阶段都存在可行调度第二在子问题中引入切负荷变量并设置高惩罚成本把严格约束软化为可惩罚约束这样即使极端情况下切负荷也只是成本升高而不会导致模型直接崩溃。6.2 大M参数的选择与数值稳定性大M参数影响MILP的数值稳定性。M取太小会错误地排除真实可行解M取太大求解器内部的对偶尺度会恶化导致报错或收敛极其缓慢。按我的经验大M取值应基于对偶变量的物理含义估算上界柴油机发电成本在1.2元/kWh左右购电成本约1元/kWh对偶变量的上界基本就在这个量级M取10倍即10左右即可而不是拍脑袋取个1e5。如果发现自己写的模型在YALMIP中求解时频繁卡住先把所有约束的系数矩阵scale一下再看有没有数量级相差悬殊的常数项混在一起。6.3 求解时间太长怎么办两阶段鲁棒模型的计算量主要来自子问题的MILP求解和大规模割平面的主问题迭代。我的经验是先缩小不确定集的离散粒度比如把负荷和出力的波动档位从连续值离散为3档能显著降低MILP复杂度且对结果影响很小。另外一个实用技巧是先用确定性模型得到一组良好初始解把它作为CCG主问题的warm start迭代次数能减少三分之一左右。如果微网规模较大节点多、设备类型复杂建议将典型日segmentation从24小时小时级降为4小时间隔先验证算法正确性再逐步恢复分辨率做最终计算。整个流程走完调试一次的成本会低很多。6.4 个人实操体会这套模型从推导到代码落地前后迭代了两周。最大的体验其实有两处一是别试图在YALMIP里用黑盒方式写max-min手动推导强对偶虽然麻烦但推完一次之后整个问题结构会变得非常清晰后续改约束、加设备都是在清晰的地基上做扩展。二是预算参数 ( \Gamma ) 和波动幅度的标定要和实际场景的历史数据强绑定不要为了鲁棒而鲁棒——配置方案是要真金白银投下去的。如果手头已经收集了微网所在区域的历史气象和负荷数据建议先把不确定集的参数标定做扎实这套方法跑出来的配置结果才能既抗风险又不至于过度浪费。