二阶锥规划求解配电网分布式电源选址定容的建模与工程实践

发布时间:2026/10/17 8:58:09
二阶锥规划求解配电网分布式电源选址定容的建模与工程实践 最近在做一个分布式电源优化配置的咨询项目甲方想把光伏、小风机和储能塞进一个老旧的辐射型配电网里问的最多的就是“往哪装、装多大、什么时候该出力”。这类问题标准的学术叫法是“分布式电源选址定容”落到工程上就是一个混合整数优化问题。而解决它的关键路径就是用二阶锥规划SOCP松弛交流潮流方程再通过MATLABYALMIP建模交给CPLEX或Gurobi去解。这篇文章我专门聊二阶锥编程方法不是把公式往墙上一贴就完事而是把我从“看懂文献”到“能跑出可靠结果”这段过程的全部细节写出来。适合正在做配电网优化、被非线性潮流折磨得不轻、或者刚接触YALMIP但不知道二阶锥约束怎么写的同行。你能得到的东西包括完整的数学模型怎么设、为什么松弛后还能保证解有意义、YALMIP里二阶锥约束的标准写法、以及一套可以直接套用的调试方法。1. 分布式电源选址定容的数学模型目标、变量与约束框架配电网的分布式电源优化配置说白了就是找一个组合在哪些节点装DG、装多大的容量、运行时段内怎么分配出力使得总成本最小同时电压不越限、线路不过载、逆流还能被接受。模型拆开来看其实很朴素难点从来不在目标函数的写法而在“潮流约束怎么表达”。1.1 决策变量与目标函数怎么定决策变量分三层。第一层是二进制变量表示节点i是否配置DG记为 x_i ∈ {0,1}第二层是连续的容量变量表示节点i安装的DG额定容量 P_i^cap第三层是运行变量包括各节点DG出力 p_i^{DG}、无功出力 q_i^{DG}、节点电压幅值平方 U_i V_i^2以及各支路的有功P_ij、无功Q_ij和电流平方项L_ij。用 V_i^2 而不是直接用 V_i是建模里非常关键的一步。DistFlow方程里的电压降项在展开后会同时出现平方项和乘积项直接对V建模会引入额外的非线性定义 U_i V_i^2 就能把大部分约束变成线性的只有电流平方和功率平方的耦合关系保留为二阶锥形式。这是YALMIP建模中“让模型更线性”的一个经典技法。目标函数我习惯采用“投资成本年运行成本”的组合。投资成本可以折算成年值CA Σ (α_i · C_ins · P_i^cap)其中 C_ins 是单位容量安装成本α_i 是年折算系数把安装成本平摊到寿命年限。运行成本主要是从主网购电费用和网络损耗费用CO 8760 · (C_elec · P_loss C_elec · P_grid)。当然如果做的是多时段优化就把8760换成典型日小时数乘以天数权重。1.2 DistFlow潮流方程辐射配电网的建模基础辐射状配电网的潮流计算很少直接用传统的牛拉法极坐标方程因为那个方程对优化问题非常不友好。业内普遍采用DistFlow模型。假设以根节点为参考方向支路k从节点i流向节点j负荷为 P_j^L jQ_j^L注入为 p_j^{DG} jq_j^{DG}则支路功率平衡和电压递推可以写成P_ij Σ_{c∈child(j)} P_jc P_j^L - p_j^DGQ_ij Σ_{c∈child(j)} Q_jc Q_j^L - q_j^DGU_j U_i - 2(r_ij P_ij x_ij Q_ij) - (r_ij² x_ij²) L_ij其中 L_ij (P_ij² Q_ij²) / U_i表示支路电流幅值平方的等效值。这里 child(j) 是以节点j为首端的下游支路集合也就是把从根节点开始的“父子关系”显式表达出来。这三个方程描述的物理含义很直观流入节点j的功率一部分流向所有下游支路一部分被节点j的负荷消耗另一部分被节点j的DG供给首端电压等于末端电压加上线路上的压降。对于树状拓扑的配电网来说这个模型比求解节点导纳矩阵的潮流方程要合适得多因为拓扑本身就是天然的递推结构。1.3 二阶锥松弛把非凸约束变凸的核心操作精确的DistFlow方程中(P_ij² Q_ij²) / U_i 这一项是非凸的等式约束它让整个模型变成非凸非线性规划NLP全局最优性完全没有保障。二阶锥松弛做的事情非常直接——把这个等式改成不等式L_ij ≥ (P_ij² Q_ij²) / U_i这个不等式等价于二阶锥约束‖ [2·P_ij; 2·Q_ij; U_i - L_ij] ‖₂ ≤ U_i L_ij从几何上理解左边是一个二维功率向量被“放大装订”到三维空间右边是一个线性表达式。满足这个不等式的点集是一个凸锥而原来的等式约束要求点落在锥的边界上。松弛的巧妙之处在于绝大多数情况下最优解会自动落在锥边界上也就是不等式自动取等号此时松弛是“紧”的结果对原问题完全有效。为什么通常能取等号因为目标函数里包含网损和购电成本的减小这些项都会推动L_ij尽量变小而电压下限约束也会限制U_i不至于被压得过低从而让锥约束倾向于收紧。更严格的条件在文献里称为“松弛精确性条件”工程上一般通过求解后的间隙检验来确认后面会专门讲怎么做。2. 为什么非要用二阶锥与经典方法的对比和选型逻辑很多刚接触这个问题的人会问直接用非线性求解器去解原始NLP不行吗或者干脆用遗传算法、粒子群算法去搜索DG的位置和容量不是更简单吗这个问题我在一开始也纠结过所以这里把三种路线的利弊说透。2.1 直接求解非线性规划有何问题原始DistFlow方程带整数变量本质上是个MINLP问题。用MATLAB自带的fmincon这类工具去解有几个绕不开的坎。首先fmincon基于梯度下降遇到非凸目标函数和强非线性的等式潮流约束极容易陷入局部最优而且初值的选取对结果影响极大换一组初值可能就得到完全不同的安装方案。其次整数变量和连续变量混在一起fmincon无法直接处理只能用罚函数把二进制变量变成连续的然后强制阈值这样得出来的“0/1”解本身就不可靠。我做过对比试验同样一个33节点算例用直接NLP方法跑十组不同初值能收敛到5、6种不同的局部解而且完全无法判断哪一个更接近全局最优。对于做决策支持的人来说这基本是不可接受的。2.2 启发式算法为何只适合粗略估算遗传算法和粒子群算法在学术界很受欢迎因为实现简单、不用求导、对模型形式不挑剔。但在真实的优化配置场景中它们有一个非常实际的硬伤每次评估适应度都需要计算一次潮流一次潮流对应一次非线性求解。随着种群规模和迭代代数增长计算量会呈线性甚至指数膨胀一个多时段的33节点模型跑个几千次潮流就是好几个小时。更麻烦的是启发式算法的解没有任何最优性下界你得到了一个“看起来不错”的解但永远不知道它离最优差多少。这跟买彩票差不多运气好可能很好运气不好就是坑。如果项目里需要写研究报告评审专家问一句“你的解离最优解差距有多少”启发式算法直接没法回答。2.3 二阶锥的工程价值精确、全局最优、求解快SOCP松弛后的模型在固定0/1变量以后是一个凸优化问题凸问题就保证了任何局部最优都是全局最优。即使0/1变量还在模型的整体结构也是一个凸MINLP的特例——混合整数二阶锥规划MI-SOCP商用求解器CPLEX、Gurobi可以对它做分支定界并在求解过程中给出当前可行解与最优解之间的间隙MIP Gap这意味着我们随时都知道自己的解“离最优有多远”。这带来的工程价值是切切实实的第一结果可复现大家用同一套数据和同一份模型得到同一个优化解第二计算效率高典型33节点单时段模型在Gurobi中一般零点几秒就出解多时段和带容量等级扩展后也通常在以分钟计的时间尺度内完成第三全局最优可以用来做大量敏感性分析和“what-if”推演比如把负荷往上调10%再看最优方案变没变这类分析在传统方法里几乎做不了。我把三条路线整理成了一张对比表方便做决策的时候直接看方法解的质量计算速度全局最优保证工程适用性直接NLPfmincon等依赖初值多为局部最优较快但反复试初值无低遗传/粒子群等启发式不确定无上界评估慢需大量潮流计算无中低SOCP MI-SOCP求解器全局最优/带间隙上界快毫秒到分钟级有凸松弛精确时高单是“解有可验证的最优性”这一条就足以让我在公司内部的技术评审里说服所有人用二阶锥路线。这也是为什么我在这个项目里毫不犹豫选择了MATLABYALMIPCPLEX/Gurobi的组合。3. MATLABYALMIP编程实现从变量定义到求解器调用的完整流程模型和求解器都定了接下来是让代码落地。这一节我直接按实际写代码的顺序讲包括变量定义、约束构建、目标函数和求解器配置重点展示二阶锥约束的YALMIP标准写法。3.1 参数准备与变量定义我在写这类代码时建议所有电气量都采用标幺制。电压基准取12.66 kV配电网常见电压等级功率基准取10 MVA或100 MVA。这样可以避免变量数量级跨度过大比如电压标幺值在1附近功率在0.001到0.1之间差两三个数量级不至于让求解器产生数值病态。变量定义的代码一般长这样% 假设已经有拓扑数据: branch(nbranch,4) [首端 末端 r x] % load(nbus,2) [PL QL] 单位标幺 nbus 33; nbranch 32; baseMVA 10; % 优化变量 x_site binvar(nbus, 1); % 0/1是否安装DG Pcap sdpvar(nbus, 1); % DG额定容量标幺 p_DG sdpvar(nbus, 1); % DG有功出力 q_DG sdpvar(nbus, 1); % DG无功出力 U sdpvar(nbus, 1); % 电压幅值平方 Pij sdpvar(nbranch, 1); % 支路有功 Qij sdpvar(nbranch, 1); % 支路无功 Lij sdpvar(nbranch, 1); % 支路电流平方等效量这里用 sdpvar 定义连续变量binvar 定义二进制变量。YALMIP会自动识别模型中的整数变量并向求解器声明这是一个混合整数问题。我自己习惯把容量上限也作为连续变量然后把“只能安装在选定节点”用一个大M约束来体现这样比直接把容量做成离散候选集合更容易收敛也方便后续做灵敏度分析。3.2 DistFlow约束与二阶锥约束的代码写法YALMIP支持很多种约束写法但二阶锥约束最稳妥的是用 cone() 函数它接收两个参数第一个是锥内部的向量第二个是锥的线性上界。对应上面松弛后的约束Constraints []; % 电压上下限 U_min 0.95^2; U_max 1.05^2; Constraints [Constraints, U_min U U_max]; % 根节点电压固定 Constraints [Constraints, U(1) 1.0]; % 遍历所有支路构建DistFlow与二阶锥约束 for k 1:nbranch i branch(k,1); % 首端节点 j branch(k,2); % 末端节点 r branch(k,3); % 电阻标幺 x branch(k,4); % 电抗标幺 % 支路功率平衡首端流出的功率 汇入所有下游支路 负荷 - DG注入 % 为简化示例这里用方式先列出所有以j为首端的子支路索引 child_idx find(branch(:,1) j); sum_P_down sum(Pij(child_idx)); sum_Q_down sum(Qij(child_idx)); Constraints [Constraints, Pij(k) sum_P_down load(j,1) - p_DG(j)]; Constraints [Constraints, Qij(k) sum_Q_down load(j,2) - q_DG(j)]; % 电压递推方程 Constraints [Constraints, U(j) U(i) - 2*(r*Pij(k) x*Qij(k)) - (r^2 x^2)*Lij(k)]; % 二阶锥松弛约束 Constraints [Constraints, cone([2*Pij(k); 2*Qij(k); U(i) - Lij(k)], U(i) Lij(k))]; end这里用 find(branch(:,1) j) 找到所有以节点j为首端的支路把它们当成子支路然后 sum_P_down 就是这些子支路的有功之和。对于一个标准的33节点系统节点编号如果按从根节点逐层向外编号每个节点的子支路都有清晰定义。如果网络结构更复杂或者节点编号不是按层级顺序建议先做拓扑预处理生成 parent-child 关系表再把它带入约束矩阵。我不建议在YALMIP里写二次不等式 V(i)*Lij(k) Pij(k)^2 Qij(k)^2 这种方式虽然数学上正确、YALMIP一般也能识别但在某些版本里这类约束会被YALMIP误判为一般非线性约束导致求解器选了不该选的算法或者模型卡在预处理阶段。直接用 cone() 最保险它能100%生成SOCP问题。3.3 容量约束、目标函数与求解器配置DG安装容量和出力之间的关系用几个约束锁住。首先是“只有安装的节点才有容量”Pcap(i) ≤ M·x_site(i)M取一个足够大的数比如系统总负荷的若干倍。其次是“出力不能超过额定容量”0 ≤ p_DG(i) ≤ Pcap(i)无功输出可以控制在功率因数范围内例如 -0.3·Pcap(i) ≤ q_DG(i) ≤ 0.3·Pcap(i)这对应逆变器在0.95功率因数下可吸收或发出的无功能力。目标函数我建议同时包含投资年值和运行成本这样模型能够自动权衡“多装DG减少购电”和“DG投资成本”之间的关系% 投资年值C_ins是单位容量安装成本alpha是年折算系数 annual_inv sum(alpha * C_ins * Pcap); % 运行成本网损成本 上级电网购电成本 % P_loss_total可以通过根节点注入和全网DG出力、负荷求差得到 P_loss_total Pij(1) sum(p_DG) - sum(load(:,1)); cost_loss C_elec * P_loss_total * hours_year; Objective annual_inv cost_loss;这里 Pij(1) 是根节点流出的有功其实就是系统从主网购得的功率。用这种方式算运行成本不需要额外引入变量非常简洁。最后配置求解器并求解ops sdpsettings(solver,gurobi,verbose,2); ops.gurobi.MIPGap 1e-3; % 可以接受的MIP间隙比例 ops.gurobi.TimeLimit 300; % 单位秒防止个别极端情况卡死 sol optimize(Constraints, Objective, ops); if sol.problem 0 disp(求解成功); else disp([求解失败 sol.info]); end这里把MIPGap设置成1e-3意味着当求解器找到的可行解与当前最优下界的相对差距小于千分之一时就认为收敛结束。这个值在工程上已经足够精确同时能避开分支定界在末端疯狂的枚举把求解时间从几分钟压到几十秒。我自己实测中单纯追求1e-6间隙和1e-3间隙最优解偏差几乎可以忽略但计算时间可能差一个数量级。4. 典型算例与结果分析33节点系统的复现过程数学公式和代码框架都有了还是要跑一个完整的例子才能真正讲清楚。我用的算例是经典的33节点辐射型配电网测试系统这套数据在很多文献里都能找到额定电压12.66 kV总负荷约3.7 MW 2.3 Mvar有32条支路。为了演示我把DG候选安装节点设置为13、20、25、30号这些节点位于馈线中后段电压偏低、线路负载较重从工程直觉来说最容易产生效益。4.1 算例参数与场景设置负荷取峰值负荷时段不展开多时段先把单时段模型跑通。单位容量安装成本按年折算后大约是0.32元/kWh的等效值但更直观的做法是每kVA容量年投资约500元/kVA·年购电价取0.6元/kWhDG运维成本取0.05元/kWh。不过成本参数每个地区差异很大大家替换成自己手上项目的实际数据即可。优化前先做一个不装DG的基准潮流计算此时系统网损大约在0.2 MW左右末节点电压接近0.92已经越下了0.95的下限。这就说明系统确实需要无功支撑和就地电源不是随便在馈线首端装个发电源就完事。% 候选安装节点 cand_node [13; 20; 25; 30]; % 只允许候选节点安装DG for i 1:nbus if ~ismember(i, cand_node) Constraints [Constraints, x_site(i) 0]; Constraints [Constraints, Pcap(i) 0]; end end这样相当于对二进制变量做了一个硬过滤大幅缩小整数搜索空间比在目标函数里加惩罚项靠谱得多。4.2 结果校验松弛间隙与潮流校核用Gurobi求解完成后先看解是否满足原始潮流方程。这一步很多人会忽略而它恰恰是最重要的一环。SOCP松弛得到的最优解虽然数学上可行但只有在松弛“紧”的情况下原分布式电源配置才真正成立。校验方法很简单取最优解中的 U, Pij, Qij, Lij 带入原始等式的二阶锥表达计算每个支路的松弛残差gap_k (U_i · L_ij - (P_ij² Q_ij²)) / (U_i · L_ij)如果 gap_k 都接近于 0比如小于1e-5说明锥约束全部取等号原问题松弛精确。如果某些支路的 gap_k 明显大于0说明该支路的锥约束不紧这时候解虽然在松弛问题里可行但在原问题里并不对应一个真实的潮流感。实际优化中只要目标函数里包含网损或电压偏差项松弛不紧的情况非常少见但如果目标函数变成纯粹的DG投资成本最低必要时需要检查并处理不紧的情况。我跑出来的结果最优方案是13号节点安装约0.45 MW25号节点安装约0.6 MW20号节点0.3 MW30号节点不装。末端电压从0.92抬升到0.98网损从0.2 MW降到0.12 MW年综合成本相比不装DG的基准方案下降了大约12%。这几个候选节点分布在馈线末端和中部说明从损耗和电压两个维度看就地平衡确实是有效的。4.3 常见报错与调试经验跑YALMIPCPLEX/Gurobi的过程中最容易踩的坑我整理成四类一是模型不可行。求解器弹出infeasible后第一反应不要乱改约束先查Power平衡方程里的 child_idx 有没有漏掉某些支路特别是末端节点没有子支路时sum() 会得到一个空矩阵而YALMIP里空矩阵相加有时不报错但会静默生成错误约束。我的调试办法是用 check(Constraints) 查看每一条约束的残差定位是哪条约束出了问题或者把求解器换成无整数约束的连续SOCP松弛先跑一遍看连续可行域本身是否非空。二是缩放问题。如果不做标幺处理电压用 kV、功率用 kW、电阻用 Ω数值从几千到零点几都有这会导致KKT系统条件数恶化Gurobi在迭代过程中反复调整预加重心最终解虽然出来了但精度差。解决方式很简单所有电气量统一用标幺电压平方、支路功率、电阻电抗都要同比例缩小。三是二阶锥约束写法。如果用了 Pij^2 Qij^2 U*Lij 这种平方形式YALMIP在某些旧版本里不会自动将其归类为二阶锥而是把它们当成一般非线性不等式交给非线性求解器看起来能算但速度慢几个数量级。建议强制用 cone() 并检查 sdpsettings 的求解器日志里是否显示问题类型为“SOCP/MI-SOCP”。四是解精度。即使求解报告显示“Optimal”也要用潮流计算验证端到端的电压和功率值。我曾经遇到一次求解器显示MIP Gap 0%但松弛间隙在一条支路上达到0.08这说明DG容量和电压约束在边界上太紧需要重新审视负荷数据和容量离散粒度。5. 进阶扩展多时段运行、储能协同与不确定性单时段的选址定容模型能帮我们把“装在哪、装多大”定下来但真实系统里光伏和负荷都是随时间变化的储能设备的加入又引入了充放电状态这一维度。这一部分讲三个最常见的扩展方向以及它们各自对建模和求解的影响。5.1 多时段SOCP模型的规模控制把单时段模型扩展成24小时或8760小时变量数量会线性增加。24时段版本有大约2400个连续变量加上用于出力的容量约束Gurobi依然能从容应对典型求解时间在十几秒到两分钟之间。但8760时段版本就没那么轻松了尤其是完整建模我在实践中一般会用年负荷持续曲线按场景削减选出4到8个代表性时段并配上权重既能保持年量的准确性又显著压缩模型规模。多时段版本中需要加入储能模型的话就要增加一个描述充电量在时间轴上递推的状态变量SOC(t)并且同时处理充电和放电两个方向避免求解器出现既充电又放电的循环损耗套利。一个实用技巧是引入二进制变量 yu 来表示充放电状态或者在目标函数中对充放电叠加惩罚项把“同时充放”当作不可行情况排除。5.2 从确定性到鲁棒优化光伏和风力出力的随机性让很多项目需要分析“最坏情况”。确定性SOCP模型在处理这个需求时天然有点乏力因为不确定变量引入了无穷多个点必须离散化或者采用鲁棒对等转换。对配电网优化配置这个场景我推荐的做法是采用“盒式椭球式”的混合不确定集合建模把DG出力的波动范围描述成一个线性约束集合再把潮流方程中该出力出现的项做对等转换最终模型通常还能保持成SOCP结构但约束中会出现额外的不确定变量需要用鲁棒线性化的技巧处理。理论上的细节比较长但工程上落地的时候大多数人最终都会用一个更务实的办法——敏感性分析。分别对DG出力从0.6倍到1.2倍做五组确定性SOCP看最优配置方案是否稳定在同一组合如果候选方案的排名没有跨组合反转基本上就可以认为当前解对不确定性是能够接受的。5.3 与配电网重构、无功优化的联合建模分布式电源接入后原来的配电网馈线拓扑在故障转供或优化运行时可能会变化DG的选址和配电网重构之间存在耦合。把重构变量分段开关和联络开关状态也写成二进制变量加进去模型就升格成带拓扑约束的MI-SOCP复杂度明显上升。有一个技巧比较好用重构约束通常可以写成“所有节点的度数约束”和“连通性约束”在配电网场景里辐射状网络要求所有边数 节点数 - 1且网络连通这两组约束可以线性化。求解器层面这一问题的整数变量规模一般在50到100个左右现代商业化求解器配合强割平面依然可以在几分钟内给出较高精度的解。在做联合优化时我一般先跑一个固定拓扑的单目标模型再逐步放开开关变量这样既可以调试模型又可以获得一个较优的初始可行解大幅加速分支定界收敛。算到这里这个项目最核心的部分已经走完。从最开始的数学建模到YALMIP代码落地再到求解器参数微调和结果校验整个过程其实没有哪个环节是“魔法”都是可以逐层拆解、逐层验证的。最后再分享一个很小的个人习惯每次跑完一个新算例我会把松弛间隙、MIP Gap、电压最低节点、网损这四个数记在Excel表里连同工况参数一起存档。几次做下来你就会发现这些数字本身就是最直观的模型健康度报告任何一次参数修改导致的不正常波动都能从这四个数的变化里快速定位到原因。