配电网节点电价DLMP这个话题最近来找我讨论的人明显多了起来。大家的困惑往往很一致输电网的LMP概念都懂但一落到配电网发现既有辐射状拓扑、又有分布式电源、还要处理电压越限套用输电网那套方法出来的电价怎么看都不合理。我最近刚好把基于lindistflow潮流模型和SOCP二阶锥松弛的MATLAB配网DLMP计算框架完整搭了一遍从建模、求解到拉格朗日乘子还原都有可复现的代码。这篇就把整个实现思路、关键代码片段和调试中踩过的坑都写出来给正在做配网节点电价、分布式能源市场机制或主动配电网研究的同学做个参考。1. 为什么配电网不能照搬输电网LMPDLMP到底在算什么1.1 输电网LMP的三个分量及其适用前提输电网的节点边际电价LMP一般拆成能量分量、阻塞分量和网损分量三个部分。能量分量是全系统统一的边际机组报价阻塞分量反映输电断面受限时不同节点之间的替代困难程度网损分量则是对潮流损耗的空间归因。这套理论在输电网层面非常成熟因为它建立在几个关键前提之上网络呈环网结构、线路R/X比很低、电压问题靠无功调节基本能解耦处理、阻塞断面数量有限且可枚举。但配电网完全不满足这些前提。配网的R/X比往往在1到3之间甚至更高电阻损耗占比大电压问题从线路首端到末端逐级累积拓扑是辐射状或弱环网功率流动方向单一但DG接入后可能反转阻塞不再是少数几个断面而是大量分支线路的容量约束叠加。更麻烦的是输电网LMP对网损的处理通常采用网损灵敏度近似这套近似在配网里误差会大到不可接受。1.2 配网潮流主导下的价格信号差异拿一个简单的算例就能说清楚问题。同样一个10kV馈线从变电站出口到末端负荷电压降可能超过5%有功网损在高峰时段占系统总负荷的比例也远高于输电网。在这样的物理场景里节点价格信号的核心矛盾已经不是哪个机组最贵而是末端节点增加一单位负荷系统要多付出多少代价——这里的代价包括额外的网损、可能触发的电压越限风险、以及本已接近满载的支路容量占用。这就是DLMPDistribution Locational Marginal Price要回答的问题。配电网节点电价需要在配网自身的最优潮流模型里定义通过求解带网络约束的经济调度问题用节点功率平衡约束的拉格朗日乘子来表征该节点负荷的边际社会成本。这个概念在2014年左右开始被集中讨论这几年随着分布式光伏、储能和车网互动负荷大量接入实际需求变得非常迫切。1.3 DLMP的四个分量构成在配网场景里DLMP的分析口径通常比输电网多一个电压分量。常规分解方式是能量分量系统边际购电成本或边际发电成本对所有节点基本一致网损分量节点注入功率变化引起的网络损耗变化末端节点通常更高阻塞分量支路容量约束越限时受限线路上下游节点出现价格差电压分量电压约束参与调度时对节点边际价格的附加影响这四个分量加在一起才是完整的DLMP。单纯读功率平衡约束的乘子可以得到总和但要解释为什么末端节点电价比首端贵两毛钱必须把分量拆开。后面第3节会讲具体怎么用MATLAB把这四分量还原出来。2. 模型选型lindistflow加SOCP为什么是当前最优组合2.1 DistFlow潮流方程与线性化处理配网最优潮流的建模国内文献用得最多的就是DistFlow方程。对辐射状配网每一条支路k从节点i流向节点j满足P_ij表示支路有功潮流Q_ij表示无功潮流。精确的DistFlow方程包含三项有功平衡、无功平衡、电压方程。精确电压方程是Vj² Vi² - 2(rP xQ) - (r² x²)(P² Q²)/Vi²最后一项对应支路电流引起的损耗。这个方程本身是线性方程但最后一项的分母带Vi²成了非凸项。lindistflow的做法是直接丢弃这个高阶小项令Vj² ≈ Vi² - 2(rP xQ)。在配网中这个近似的合理性在于低压配网支路电流不大损耗项相对2(rP xQ)通常小一个数量级以上而且节点电压本身在0.95到1.05 pu范围内分母项的影响有限。丢掉这一项之后整个潮流模型变成纯线性约束配合支路容量约束就可以形成SOCP。2.2 二阶锥松弛把非凸约束鼓成凸锥支路容量约束通常写成Pij² Qij² ≤ Smax²。从几何上看这个约束在(P, Q)平面上本来是个圆盘凸集本身是凸的。真正麻烦的是精确DistFlow里那些包含Vi²、Iij²的项。在lindistflow框架下如果我们还希望保留电流损耗的间接影响或者处理更精确的线路热极限就经常碰到的形式是Pij² Qij² ≤ Vi²·Iijmax²。这个约束里Vi²是变量把乘积项展开后可行域不再是凸集。二阶锥松弛解决的就是这个问题。我们把Vi²单独作为一个变量记作u_i于是约束变成Pij² Qij² ≤ u_i·Iijmax²。这个不等式在数学上等价于(Pij, Qij, (u_i - Iijmax²)/2)这个点落在旋转二阶锥里YALMIP里可以直接用cone函数表达。松弛带来的问题是原本等号成立的约束变成了不等式可能让最优解偷懒抬高电流。但配网辐射状拓扑加上电压幅值作为变量文献里已经有大量理论证明这种情况下二阶锥松弛是精确的——只要满足正常运行条件和一定的网架条件松弛的间隙为零。我在实际求解中也基本没有遇到过显著非零的松弛间隙。2.3 与其他建模方案的对比这里把几种常见方案放在一起对比方便你根据自己的场景选型。建模方案潮流精度凸性求解速度适用场景直流潮流DC-PF很低忽略无功电压线性规划极快输电网或输配近似lindistflowSOCP中高保留无功电压和网损近似二阶锥快配网DLMP、配网调度精确DistFlowSOCP高保留损耗项二阶锥中等高精度配网分析全交流AC-OPF最高非凸需要NLP求解器慢且易陷入局部解离线分析不适合批量计算做DLMP计算我的建议非常明确用lindistflow加SOCP。原因有三。第一DLMP的本质是边际价格信号依赖拉格朗日乘子这要求模型是凸的KKT条件才给出全局最优下的对偶变量第二配网调度需要反复求解不同负荷场景求解速度直接决定方案能不能工程化第三在0.95到1.05 pu范围内lindistflow的精度足够支撑价格信号的归因分析distflow搞复杂了反而让乘子解读变得困难。2.4 对偶视角拉格朗日乘子如何变成节点电价这里讲一个关键的理论问题为什么节点功率平衡约束的拉格朗日乘子就是节点电价。考虑一个简化的配网经济调度问题目标函数是购电成本和DG发电成本最小化约束包括功率平衡、支路潮流、电压上下限和容量约束。构造拉格朗日函数对节点j的有功平衡约束引入乘子λ_j。最优解处如果所有不等式约束都不越限那么λ_j恰好等于该节点负荷增量的边际成本——因为目标函数对负荷求偏导时拉格朗日函数中所有包含该节点功率的项都会通过链式法则汇聚到λ_j上。直观理解节点j多出1MW负荷系统最优调度需要重新安排发电和潮流分布这个再调度成本的变化量就是λ_j。但是注意这个λ_j已经是包含网损、阻塞、电压影响的综合信号。如果想把四个分量拆开一般用一个近似分解的做法在最优解处固定网络状态分别计算网损灵敏度、阻塞乘子贡献和电压约束贡献然后用总乘子倒推能量分量。这个方法不完美但在工程实践中足够稳定第3.4节会给出具体代码思路。3. MATLAB工程实现从数据表到DLMP数值的完整链路3.1 输入数据组织与拓扑表构建我用的是IEEE 33节点标准配网算例改造了几个关键参数根节点连上级电网购电电价设为0.5元/kWh节点18和33接入分布式光伏发电成本0.35元/kWh容量各1.5MW和0.8MW。这样构造的目的是为了同时考察末端节点高电价和DG接入对价格的空间平滑作用两个现象。数据组织上我习惯用两个结构体bus和branch。bus字段包含节点编号、有功负荷、无功负荷、电压上下限基准值branch字段包含首端节点、末端节点、支路电阻、支路电抗、容量上限。拓扑关联矩阵在读取branch后自动生成。这里有个小建议初版代码不要直接用matpower的case文件格式自己用表格列出来更透明调试时查错也容易。3.2 YALMIP变量定义与约束组装变量定义是整个DLMP实现的骨架。核心变量包括支路有功潮流、支路无功潮流、节点电压平方、根节点受电功率、各DG有功无功出力、根节点无功受电。用YALMIP的sdpvar定义如下。% 支路变量 P_br sdpvar(n_branch, 1, full); % 支路有功潮流 Q_br sdpvar(n_branch, 1, full); % 支路无功潮流 u sdpvar(n_bus, 1, full); % 节点电压幅值平方 % 电源变量 P_sub sdpvar(1, 1, full); % 根节点购电有功 Q_sub sdpvar(1, 1, full); % 根节点购入无功 P_dg sdpvar(n_dg, 1, full); % DG有功出力 Q_dg sdpvar(n_dg, 1, full); % DG无功出力节点功率平衡用关联矩阵搭。关联矩阵B的维度是n_bus乘n_branchB(i,k)在支路k从节点i流出时为-1流入时为1其余为0。这样一个等式约束就同时覆盖了全部节点constraints []; % 节点有功平衡B * P_br 注入电源 节点负荷 % 注入电源向量根节点购电 DG出力 P_inj zeros(n_bus, 1); P_inj(sub_idx) P_sub; for k 1:n_dg P_inj(dg_bus(k)) P_inj(dg_bus(k)) P_dg(k); end constraints [constraints, B * P_br P_inj bus_load];电压方程的约束对应每个支路首末端电压平方差% 线性化DistFlow电压方程 for k 1:n_branch i branch(k, 1); % from节点 j branch(k, 2); % to节点 r branch(k, 3); x branch(k, 4); constraints [constraints, u(j) u(i) - 2*(r*P_br(k) x*Q_br(k))]; end % 电压上下限 constraints [constraints, u (0.95)^2, u (1.05)^2];支路容量约束用YALMIP的cone函数写成二阶锥% 支路容量二阶锥约束 for k 1:n_branch constraints [constraints, cone([P_br(k); Q_br(k)], S_max(k))]; endcone(x, y)的语义是norm(x, 2) y正好表达支路视在功率不超过S_max。这里S_max的单位需要和P、Q保持一致通常填MVA。目标函数为购电成本加DG发电成本objective price_sub * P_sub sum(price_dg .* P_dg);3.3 SOCP求解与拉格朗日乘子提取求解配置我用Gurobi作为SDPT3求解SOCP时的对偶问题。注意YALMIP内部会自动把二阶锥问题分派给求解器不需要额外声明变量类型。求解代码如下% 求解SOCP ops sdpsettings(solver, gurobi, verbose, 2); result optimize(constraints, objective, ops);乘子提取是DLMP实现中最关键也最容易出错的一步。功率平衡等式约束的对偶乘子就是该节点的边际价格信号。在YALMIP里dual函数可以直接取% 提取节点有功平衡约束的拉格朗日乘子 lambda_p dual(constraints(功率平衡约束句柄));但直接这样写有个坑如果constraints是一个数组YALMIP的dual返回的是整个约束向量对应的乘子向量顺序必须和约束定义顺序完全一致。更稳妥的做法是单独保存约束句柄% 推荐写法分离保存关键约束句柄 node_power_constraints [B * P_br P_inj bus_load]; constraints [constraints, node_power_constraints]; % 求解后提取 lambda_p dual(node_power_constraints);这里lambda_p向量中的每一个元素就对应一个节点的实时电价单位为元/MWh。需要强调YALMIP返回的乘子遵循其对偶形式约定正负号可能与KKT教材里的写法相差一个负号后面第5节会讲如何校验符号方向。3.4 DLMP四分量还原的实用算法完整的四分量分解在理论上要构造多层灵敏度分析工程上我推荐一个三步逼近法每一步都有明确意义代码也容易写。第一步求总DLMP也就是直接读lambda_p。这个量一定是对的因为它源自原始问题的KKT条件。第二步把支路容量约束全部拿掉重新求解一次相同优化问题再读一次功率平衡乘子记为lambda_base。这个量对应没有阻塞时的节点边际价格它包含了能量分量和大部分网损分量。第三步节点j的阻塞分量约等于lambda_p(j) - lambda_base(j)。电压分量的归因可以用对称的做法把电压上下限约束从1.05/0.95放宽到1.10/0.90求解后读取lambda_volt_freeDLMP与它的差值可以近似归因于电压约束。% 三阶段求解函数封装示例 [lambda_total, ~] solve_dlmp(constraints_full); [lambda_nocap, ~] solve_dlmp(constraints_without_capacity); [lambda_voltfree, ~] solve_dlmp(constraints_voltage_loose); dlmp_cong lambda_total - lambda_nocap; dlmp_volt lambda_nocap - lambda_voltfree; dlmp_energy_loss lambda_voltfree;这种方法的好处是不需要解析推导网损灵敏度纯靠重新求解就能分离出分量对配网的实际规模完全可以接受的。实测33节点算例整个三阶段求解耗时不到2秒完全能够支撑多场景批量计算。4. 算例结果与物理解读同一馈线上节点电价为何差出一大截4.1 一个低谷时段的基准DLMP结果先用一个无DG、负荷为基准值的低谷时段做测试购电电价0.5元/kWh所有节点电压和线路容量都未越限。三阶段分解后典型节点的DLMP如下。节点总DLMP(元/kWh)能量网损分量阻塞分量电压分量1(根节点)0.5010.501005(中段)0.5130.5130018(末端)0.5290.5290033(分支末端)0.5340.53400这个结果非常直观——没有阻塞、没有电压越限时所有节点的DLMP差异完全来自网损分量。末端节点18比根节点贵约0.028元/kWh折合成度电成本约每度贵近3分钱这个量级在真实配电网中完全合理。注意并不是末端节点本身用电更贵而是末端增加负荷需要额外的网损成本DLMP把这个成本精确地归因到了位置信号上。4.2 阻塞场景下的价格跳变把节点17到18之间支路的容量上限从5MVA压到2.5MVA重新求解。结果出现了非常明显的价格解列支路容量越限上游节点和下游节点的DLMP之间出现跳变。节点总DLMP(元/kWh)阻塞分量17(阻塞上游)0.514018(阻塞下游)0.6520.11319(下游继续)0.6580.11920(下游末端)0.6670.121阻塞分量在下游所有节点几乎一致出现幅度约0.11元/kWh这说明受限支路成了价格大门门内门外分别是两套边际成本体系。这个现象在配网实际运行中完全可以观察到——当一个馈线段满载时下游分布式电源即使成本略高也会因为替代了昂贵的网损和阻塞成本而变得有竞争力。4.3 DG接入后的空间平滑效应同样的阻塞场景投入节点18的1.5MW光伏按0.35元/kWh的成本参与调度。DG出力压低了下游负荷需求支路17-18的潮流下降阻塞解除。节点18的DLMP从0.652元/kWh回落到0.527元/kWh。这个案例很好地展示了DG的价格平滑机制分布式电源在合适的位置接入不仅缓解了物理阻塞还直接把末端节点的电价拉回接近根节点水平。从这些结果能看出DLMP不是抽象的数学概念它就是对物理稀缺性的价格化表达。哪个节点缺调节资源、哪条线路接近极限、哪个DG在关键时刻顶上都会一字不差地写进节点电价里。4.4 结果有效性校验方法拿到一组DLMP数值之后建议做两个校验。第一个是方向性校验在最优解处把某个节点的负荷增加0.1MW重新求解SOCP记录目标函数增量。增量除以0.1应该非常接近该节点的DLMP。这是最直接的经济学验证误差一般应小于1%。第二个是KKT残差校验YALMIP的求解结果里可以读出求解器返回的对偶信息检查原约束残差和对偶残差是否在容差范围内。Gurobi求解SOCP时默认收敛容差是1e-6一般不需要手动调整。如果发现KKT残差偏大优先怀疑变量数值尺度——有功负荷是兆瓦量级电压平方接近1两者相差过大时建议把负荷单位统一成千瓦或把目标函数乘以缩放系数。5. 调试实录那些让DLMP数值瞬间崩掉的细节5.1 一次电价符号全反的排查过程我第一次跑通模型时输出节点18的电价是负数-0.15元/kWh。这明显不合理——没有阻塞时末端节点电价怎么都不可能低于根节点购电价。当时第一反应是模型约束错了但重新检查了功率平衡和电压方程都没发现问题。后来用了最原始的办法逐节点手动算灵敏度。在节点18注入0.1MW目标函数从原来的值增加了0.0529这个数值除以0.1正好等于0.529——和理论总DLMP一致。也就是说求解器给出的最优解和目标函数增量方向完全正确问题出在dual函数提取的乘子符号上。排查到这一步就清晰了。YALMIP对等式约束dual返回的乘子其符号约定是目标函数中约束项加还是减的形式有关。我当时的等式约束写成了B * P_br P_inj bus_loadYALMIP默认的对偶形式里返回的乘子对应-lambda * (f(x) - d)这个约定所以读出来的数值天然就差一个负号。解决办法是用灵敏度校验做一个自动校正求解完后随机选几个节点测试目标函数对负荷的偏导确认符号方向后再批量提取。5.2 dual函数与约束句柄的顺序陷阱YALMIP的dual函数返回的是约束数组整体对应的向量。如果你用constraints [constraints, ...]不断拼接约束之后想提取某一条特定的乘子YALMIP是按约束在最终拼接对象中的出现顺序排列乘子。这个顺序在调试时极易出错。我踩过的一个具体问题是先拼接了电压约束再拼接功率平衡约束运行代码时想当然地认为lambda_p是第二个约束的乘子结果所有价格全都对不上号。更稳的做法是像3.3节那样为每一类关键约束单独设句柄变量不混在一起拼接。记住一个原则乘子提取永远不要依赖顺序要依赖句柄。5.3 零负荷节点的边际价格处理配网算例里常常有零负荷节点比如联络开关附近的节点或纯粹为拓扑连接而存在的中间节点。这些节点的功率平衡等式约束右端是0拉格朗日乘子依然有定义但数值往往对求解器数值误差更敏感偶尔会出现轻微抖动。处理方法有二。一是在数据预处理时给孤立零负荷节点加一个极小的基荷比如1e-4 MW让它物理上成为一个正常负荷点二是提取乘子后做中值滤波处理把明显偏离邻域节点的异常值去掉。工程上第一种方法更干净不影响价格信号的解释。5.4 SOCP松弛不精确的识别方法虽然辐射状配网大多数情况下SOCP松弛是精确的但求解器数值问题或特殊参数设置有可能让松弛出现非零间隙。判断方法很简单求解完查看每个支路的二阶锥约束残差如果norm([P_br(k); Q_br(k)])与S_max(k)之差大于0.1%说明该支路的锥约束被放松了结果可能不是严格物理可行的。为什么会发生常见原因是S_max填写太大远超过整条馈线实际可能出现的最大潮流此时锥约束在最优解处根本不紧对偶乘子为0DLMP的阻塞分量自然消失。如果你期望看到阻塞分离却一直全是零先检查容量约束是否在可行域边界上起作用。5.5 一套工程化调试清单最后整理一份可以直接对照检查的清单这些都是我在多轮调试中总结出来的高频问题。功率平衡等式约束的右端负荷向量方向是否正确——母线注入为正还是负电压方程中r、x的单位是否与支路潮流变量一致电阻电抗是每千米值还是全长值YALMIP dual提取的乘子是否为求解器约定下的负号形式DG出力上限约束的乘子会被YALMIP算进总乘子注意区分节点电价与机组边际价格两个概念求解器容差在配网小数值场景下是否需要从默认1e-6调整到1e-8多时段扩展时储能充放电的跨时段耦合约束会引入时间乘子与空间DLMP并存后要分开存储和打印调试DLMP代码的过程本质上就是把最优潮流能算出来升级成每一个乘子都能解释通的过程。我个人的经验是先用灵敏度校验把基准场景的符号和量级对齐再放开复杂约束这样每一步出错都能快速定位是建模问题还是求解问题。这套框架你照着搭完跑通基准场景再换成自己的配网数据应该就不难了。