做配电网规划的人应该都有印象,以前处理电动汽车充电负荷,最省事的办法就是把预测出来的充电需求按节点分摊,然后当成普通负荷直接塞进潮流计算里。这个做法本身没毛病,但它默认了一件事:充电需求在哪个节点出现,就必须在哪个节点消纳。现在的实际情况是,电动车用户的充电行为远没那么固定,通过充电价格、导航推荐、预约排队这些手段,完全可以让用户去一两个街区之外的充电站充电。这意味着充电负荷在空间上是可以被引导、被再分配的。
这就是"充电负荷空间可调度特性"的价值所在。把它和分布式电源选址定容放到同一个优化框架里做联合配置,能让配电网的投资经济性、电压质量和DG消纳能力同时受益。这篇内容围绕这个联合配置方法展开,重点讲清楚模型怎么搭、空间可调度约束怎么表达、Matlab代码如何组织,以及我在实际跑这类算例时踩过的坑。适合正在做配电网规划方向研究的学生,也适合刚开始接触YALMIP+求解器做优化计算、想尽快把代码跑通的工程师参考。
1. 把充电负荷当成"可调度资源"而不是"刚性负荷",是这套方法的起点
1.1 传统规划模型里,充电负荷是怎么被处理的
传统配电网规划里,充电负荷大多数时候被处理成"刚性负荷"。具体来说,规划者先通过充电行为调研或者某些预测模型,得到未来某一年各节点各时段的充电功率曲线,然后把这份曲线当作基础负荷的叠加项。潮流计算怎么算普通负荷,就怎么算充电桩负荷。这样做的好处是建模简单,坏处也很明显:忽略了一个重要事实——充电需求在空间上是可选择的。
一个城市的充电需求总量在某个时段可能是确定的,但具体到节点层面,用户完全可以选择在A站充、B站充还是在C站充。传统方法把这种可选择性强制固定成"本地需求本地消纳",实际上压缩了规划的可行域,导致两个后果。第一,充电站选址可能过于分散,每个站点都按局部峰值需求配置容量,设备利用率上不去。第二,分布式电源的选址失去了和充电负荷在空间上互补的可能性。光伏在某个馈线段出力很高,但如果这一带恰好没什么充电需求,DG的电力就只能往远处送,网损自然上去了。
1.2 空间可调度到底"调度"的是什么
所谓空间可调度特性,本质上是说充电需求在一组空间上不同的候选站点之间可以重新分配。从数学角度看,它给规划模型增加了一类决策变量:需求节点和候选充电站之间的功率分配比例。约束是每个节点的充电需求可以被拆分给多个充电站,而每个充电站承接的总负荷不能超过自身容量。
打个比方,这种关系很像外卖配送:用户点餐的内容必须被满足,但选哪家店配送是可以协调的。只要最终送到用户手里的订单总量正确,具体由哪家店出餐并不重要。充电负荷也一样,某个区域的总充电量必须被满足,但由哪些充电站承接,是规划阶段可以优化的自由度。
需要特别说明的是,空间可调度和时间可调度是两个维度的概念。时间可调度指的是有序充电、削峰填谷,改变的是充电发生的时刻;空间可调度改变的是充电发生的位置。两者可以叠加使用,但联合配置这篇文章里,核心抓的是空间维度。如果后续想把有序充电策略也塞进规划模型,需要在时序运行约束上再做扩展。
1.3 为什么把DG和充电站放在一个模型里联合配置
如果把DG规划和充电站规划分开做,会丢失一个非常重要的协同效应:分布式光伏和充电负荷在时间上天然错峰。光伏出力的高峰在白天,而家庭充电和下班后充电的高峰在傍晚到夜间。表面上看这两者在时间上互补性有限,但空间上的协同依然明显——在光伏渗透率高的馈线附近建设充电站,可以让白天富余的光伏电力就地消纳一部分,而不是全部往主变方向倒送;在远离主变的末端负荷区配置合适容量的DG,又能缓解末端电压偏低的问题。
联合配置的意义就在这里。DG的选址定容和充电站的选址定容不再是两个独立问题,而是共享同一套潮流约束、同一套投资预算的协同优化问题。空间可调度特性则是这个协同过程的润滑剂——它让充电负荷可以"流动"到对系统更有利的位置,从而进一步放大DG和充电站的互补价值。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 联合配置的核心数学模型:目标、约束与空间可调度约束的实现
2.1 目标函数:年综合费用最小
规划模型的目标函数习惯用年综合费用来衡量,把一次性投资成本折算到每年,再加上每年的运行费用,便于和购电成本等经常性费用直接比较。具体构成如下:
- DG投资等年值成本:单位容量投资乘安装容量,再乘资金回收系数。资金回收系数跟贴现率和设备寿命有关。
- 充电站投资等年值成本:包含站点固定建设成本(土地、配电设施、施工)和充电桩的设备成本。站点固定成本和充电桩数量是解耦的,一般用二进制变量表示是否建站,用整数变量表示建多少个桩。
- 运行维护成本:DG和充电桩的年度检修维护费用,通常按投资额的一定比例估算。
- 网络损耗费用:把全年各时段的线损电量折算成费用。
- 向上级电网购电费用:配电网从主网买电的成本。这是运行费用里的大头,规划结果好坏对这项影响最大。
目标函数表达式可以写成总费用最小化,各项量纲统一为万元/年。做代码实现的时候要注意单位换算,容量用kW、电量用kWh、价格用元/kWh的情况下,各项费用系数的数量级一定要核对清楚,这是很多代码跑出来结果不合理的最常见原因。
2.2 决策变量体系
模型里的决策变量分为投资层和运行层。
投资层变量包括:DG候选节点的安装状态(0-1变量)和安装容量(连续变量);充电站候选节点的建站状态(0-1变量)和充电桩数量(整数变量);如果DG有多种可选型号,还需要一组表示选型的0-1变量。
运行层变量包括:各时段DG的实际出力;潮流计算用的节点电压、支路功率、支路电流平方项;各时段充电站的充电功率;以及空间可调度分配变量——每个需求节点到每个候选充电站的分配功率。
空间可调度分配变量是整个模型与刚性负荷模型的本质区别。它把"各节点充电需求"映射为"各充电站承接的充电功率",再通过充电站节点注入到潮流计算中。
2.3 约束条件分层
约束体系可以分成四层。
第一层是潮流约束。配电网三相基本平衡,可以用DistFlow方程描述辐射状网络的有功、无功和电压关系。DistFlow中的平方项让模型变成非线性,标准的处理方法是做二阶锥松弛,把非线性等式松弛成锥约束,从而让整个模型变成混合整数二阶锥规划(MISOCP),能够用商业求解器求全局最优解。
第二层是安全运行约束。节点电压上下限、支路容量上限、主变容量上限,这些在规划层面都是硬约束,直接保证方案在正常运行方式下不过载、不越限。
第三层是设备运行约束。DG出力在零到额定容量之间,爬坡约束视情况加入;充电站的充电功率受充电桩数量和单桩最大功率限制。
第四层是空间可调度约束。每个需求节点的总充电需求必须被完全满足,即所有充电站从该节点承接的功率之和等于该节点的需求;同时,每个充电站承接的总功率不能超过自身容量。如果模型考虑用户选择意愿,还需要给需求节点到充电站的距离设置上限,或者给分配比例设置可行域范围,避免出现跨越整个配电网去充电的极端情况。
2.4 空间可调度约束的Matlab代码表达
用YALMIP表达空间可调度约束非常直观。假设有n_node个节点、n_cs个候选充电站、n_time个时段,定义三维连续变量P_alloc表示第d个需求节点在第t个时段分配给第s个候选充电站的功率:
matlab复制P_alloc = sdpvar(n_node, n_cs, n_time, 'full');
% 每个需求节点的总充电需求必须被完全满足
for t = 1:n_time
for d = 1:n_demand_node
Constraints = [Constraints, sum(P_alloc(d, :, t), 2) == P_demand(d, t)];
end
end
% 每个充电站承接的总负荷不超过站点容量
for t = 1:n_time
for s = 1:n_cs
Constraints = [Constraints, sum(P_alloc(:, s, t), 1) <= N_cs(s) * P_max_per_pile];
end
end
这里P_demand是各节点的时序充电需求矩阵,N_cs是充电桩数量整数变量,P_max_per_pile是单桩额定功率。把P_alloc按候选充电站汇总,就是注入潮流计算的节点等效充电负荷。
这个约束写起来不难,但要注意一点:P_demand是需求节点的总充电需求,它和P_alloc的索引关系要提前设计好。如果需求节点编号和候选充电站节点编号有重叠,需要单独维护从需求节点集合到全部节点集合的映射,避免索引错位。这属于写代码容易踩、但找起来特别费时间的低级错误。
3. 求解框架与Matlab代码的整体组织方式
3.1 为什么用MISOCP作为求解框架
配电网潮流约束是非线性非凸的,直接交给通用非线性求解器,一方面求解速度没有保障,另一方面得到的基本都是局部最优解,做规划方案对比时说服力不足。DistFlow方程经过变量替换和二阶锥松弛后,整个模型变成混合整数二阶锥规划,理论上可以通过分支定界法求全局最优解。MISOCP是当前配电网规划领域最主流的建模框架,原因很简单:模型表达能力够强,能涵盖绝大多数规划约束,同时求解器对这类问题的支持已经非常成熟。
如果不想依赖商业求解器,也可以用粒子群、遗传算法这类元启发式算法处理同一个模型。但智能算法的参数敏感度高,每次运行结果有波动,且约束处理要额外写惩罚函数,对于这种包含大量投资-运行耦合变量的模型,收敛效果很难保证。所以我个人的建议是:优先用YALMIP+Gurobi的组合,只有在做对比算法时才需要写智能算法。
3.2 Matlab代码的模块划分
这套代码建议按四个模块组织,后续调试和扩展都方便。
数据输入模块负责读入电网拓扑参数、负荷曲线、DG出力曲线、充电需求曲线,以及各类经济性参数。推荐用Excel或CSV存原始数据,Matlab里用readtable读取后转成struct。把参数集中放在数据文件里,而不是写死在代码中,这是让代码可复现的第一步。
模型构建模块负责定义决策变量、拼装目标函数和约束。这是YALMIP的核心部分,建议在代码中用注释把约束分成几个block:潮流约束、安全约束、DG约束、充电站约束、空间可调度约束,每个block独立检查。出问题的时候可以单独注释掉某个block,定位速度会快很多。
求解模块负责设置求解器参数、调用optimize、收集求解状态和信息。建议在求解前显式设置求解器的MIP gap、时间限制等参数,避免用默认参数跑大规模算例时失控。
结果处理模块负责从求解结果中提取决策变量值,计算各分项费用,统计DG利用率和电压质量指标,绘制结果图。这一块能直观展示方案效果,做论文时大量图表都从这里来。
3.3 核心求解代码示例
主程序框架大致如下:
matlab复制%% 数据加载
mpc = load_case('case33.m'); % 电网拓扑参数
load('scenario_data.mat'); % 负荷、DG出力、充电需求时序数据
%% 参数设置
r_year = 0.08; % 贴现率
life_dg = 20; % DG寿命,年
life_cs = 15; % 充电站寿命,年
n_time = 24;
n_node = 33;
%% 定义决策变量
x_dg = binvar(n_dg_candidate, 1); % 是否安装DG
P_dg = sdpvar(n_dg_candidate, 1); % DG安装容量
x_cs = binvar(n_cs_candidate, 1); % 是否建设充电站
N_cs = integer_var(n_cs_candidate, 1); % 充电桩数量
P_alloc = sdpvar(n_demand_node, n_cs, n_time, 'full');
% 潮流变量按t循环定义,或定义三维变量后用reshape处理
%% 目标函数
Objective = investment_cost_dg + investment_cost_cs + oandm_cost ...
+ loss_cost + purchase_cost;
%% 约束
Constraints = [];
% 依次添加各层约束...
%% 求解
ops = sdpsettings('solver', 'gurobi', 'verbose', 2, ...
'gurobi.MIPGap', 0.001, ...
'gurobi.TimeLimit', 3600);
result = optimize(Constraints, Objective, ops);
%% 结果提取
if result.problem == 0
P_dg_opt = value(P_dg);
N_cs_opt = value(N_cs);
P_alloc_opt = value(P_alloc);
else
disp('求解失败,请检查约束和求解器配置');
end
这里面sdpvar三维变量的reshape和索引是很多新手容易出问题的地方。YALMIP允许定义三维sdpvar,但涉及矩阵运算时经常需要重新塑造成二维,建议在定义变量的时候就规划好维度和reshape的规则,在代码注释里写清楚每个维度的含义。
4. Matlab环境准备与代码落地时最常见的几个坑
4.1 YALMIP工具箱和求解器的安装配置
跑这套代码之前,得先把环境配好。YALMIP是Matlab下的建模工具箱,本身不包含求解器,需要搭配Gurobi或Cplex使用。安装步骤本身不复杂:把YALMIP根目录添加到Matlab路径,再把Gurobi的Matlab接口添加到路径。但实际装起来,我见过太多人卡在版本匹配上。
Gurobi每年出两个大版本,对应的Matlab接口有最低版本要求。如果你用的是比较旧的Matlab版本,装上最新版Gurobi可能直接报接口不兼容。我建议在下载Gurobi之前先看官方文档里的Matlab版本支持表,或者干脆找一个在自己Matlab版本上验证过稳定的组合。
另外,Gurobi的许可证配置也很容易出问题。很多人装完以后运行报错,说找不到许可证。这时候先检查环境变量GRB_LICENSE_FILE有没有指向正确的licence文件。还有一个隐蔽的问题是:电脑上如果装了多个版本的Gurobi,Matlab的addpath顺序可能会导致加载错版本,排查的时候先在命令行输入gurobi看显示的是哪个路径下的版本。
4.2 数据文件格式和单位不统一的问题
数据准备阶段最常见的坑是单位不统一。线路参数里电阻电抗用标幺值还是欧姆,容量用kVA还是MVA,功率用kW还是MW,必须全篇统一。我习惯的做法是:所有内部计算统一用kW、kVar、kV作为基本单位,读入原始数据后立即转换成这套单位,之后所有公式都不用再操心换算。
节点编号也是一个隐蔽的坑。Matlab数组索引从1开始,而部分数据文件里节点编号从0开始,读进来以后如果没有统一加1,后续计算不会直接报错,但结果会莫名其妙地错。建议在数据加载函数里加一个断言,检查节点编号的范围和连续性,尽早暴露这种问题。
4.3 代码跑不通时的排查顺序
如果代码报错或者求解不可行,不要直接怀疑求解器。我的排查顺序是这样的:
先跑一遍纯潮流计算,不带任何规划变量,确认原始数据和潮流方程本身没问题。再只加DG变量,不加充电站和空间可调度约束,看模型是否可行。第三步单独加充电站变量,把充电负荷当刚性负荷处理,看投资约束是否合理。最后才加入空间可调度约束。每一步都验证,定位问题范围。
如果模型报求解不可行,优先怀疑空间可调度约束的范围限制。检查需求节点的充电总量是否超出了候选充电站总容量的上限;检查距离限制或比例范围约束是否过紧,导致可行域为空。这时候把相关约束的上界放宽一个数量级,看模型是否马上变成可行,基本就能确认是哪条约束的问题。
之前有人在网上问Matlab的license文件在远程桌面环境下打不开怎么处理,这个问题我在多台电脑上遇到过。第一反应先看当前的license manager服务是否启动,以及环境变量是否被远程会话重置。配电网优化计算经常要跑在远程服务器上,这种环境问题比模型本身更磨人,处理完建议把这些环境变量写进启动脚本,一劳永逸。
5. 算例对比:考虑与不考虑空间可调度,配置结果差在哪里
5.1 算例设计
为了把空间可调度特性的作用说清楚,我用一个基于IEEE 33节点配电网的定制算例做对比实验。系统基准电压12.66 kV,总负荷约3.7 MW。在系统里设置了8个DG候选节点、6个充电站候选节点、3个典型充电需求集中区域。分布式电源用光伏建模,按典型日光照曲线生成24时段出力系数。充电需求基于通勤场景生成,晚高峰集中在18点到22点。
对比方案设置两组。方案A把充电负荷当作刚性负荷处理,每个需求区域的充电需求只能由本区域所在节点的候选充电站满足;方案B在相同的候选站集合和容量配置下,引入空间可调度约束,允许充电需求在候选站之间分配。两组方案除了这一点不同,其余条件和参数完全一致。
5.2 关键结果指标
求解完成后,统计得到的核心指标对比如下:
| 指标 | 方案A(刚性负荷) | 方案B(空间可调度) | 变化幅度 |
|---|---|---|---|
| 年综合费用(万元) | 约1237.5 | 约1102.8 | 降低约10.9% |
| DG总装机容量(MW) | 2.4 | 3.0 | 提升25% |
| 充电站总桩数(个) | 46 | 38 | 减少约17.4% |
| 年网损电量(MWh) | 约815.6 | 约672.3 | 降低约17.6% |
| 低谷时段DG利用率 | 78.2% | 84.6% | 提升6.4个百分点 |
年综合费用降低10.9%,其中最直观的原因是网损下降了。DG选址跟着空间可调度的充电负荷走以后,光伏出力大的时段和充电负荷集中的位置更容易在空间上重合,就地消纳的比例上去了,远距离传输造成的损耗自然降下来。
充电站总桩数反而减少了,这说明了空间可调度的另一个作用:刚性模型被迫在多个需求节点各自配置充足的充电桩,容易造成每个站点的利用率都不高。允许空间分配以后,规划方案可以把充电容量集中放在负荷和DG出力匹配度更好的位置上,用更少的设备满足同样的总需求。
5.3 从算例结果读出的工程含义
这个对比结果很容易被误读成"空间可调度特性一定带来这么多收益"。实际上,收益大小高度依赖网络结构和候选站布局。如果候选充电站本身选址都集中在同一个区域,充电需求没有其他选择,空间可调度的作用就很小;反过来,如果配电网覆盖面大、候选站分布分散,这个特性的价值会被放大。
另一个工程层面的点是:模型里的空间可调度约束,在现实运营中对应的是充电导航调度、分时分区充电价格或者预约充电机制。规划模型假设这些调度手段能让用户按期望的比例改变充电地点,这需要合理的运营策略配合。如果实际运营中用户完全没有改变充电地点的意愿,模型里再强的空间可调度自由度也是空的。所以做规划时,最好同时评估一下运营侧的可实现性,至少给出一个调度比例可行域。
6. 工程落地中的差距与后续扩展方向
6.1 模型和实际工程之间的差距
这套联合配置模型在学术层面已经很成熟,但从模型结果到工程方案落地,中间还有几层现实约束需要补充。
交通网络和配电网的耦合是最大的差距之一。模型里用节点间距离或拓扑连接来表达充电站和需求区域的关系,但现实中用户选择充电站不仅看直线距离,还看实际路网距离、道路拥堵情况、充电桩排队时长。这些因素叠加在一起,决定了空间可调度特性实际能发挥多大作用。想要更准确地刻画,需要引入交通网络模型,把充电站选址问题扩展成配电网-交通网耦合规划问题。
另一个差距在不确定性。规划模型里用的是典型日曲线,但真实的光伏出力和充电需求有很强的不确定性。典型的处理方法是场景法或鲁棒优化,把多组典型场景同时纳入约束,让规划方案在多种运行情况下都可满足安全约束。这会显著增加模型规模,需要在求解精度和计算时间上做取舍。
6.2 值得扩展的几个方向
如果把这篇工作继续往后做,我建议优先考虑三个方向。
第一个是双层优化框架:上层做投资规划,下层模拟系统最优运行,上下层通过迭代求解收敛。这种结构可以更精确地刻画充电负荷空间分配和DG出力的时序运行关系,缺点是计算量大很多。
第二个是多目标优化。规划问题天然是多目标的,除了年综合费用最小,电压质量、DG消纳率、碳排放这些指标也值得关注。用正规化边界交叉或者增强epsilon约束方法求Pareto前沿,能给决策者提供一组折衷方案,比单目标结果实用得多。
第三个是时间维度上的扩展。如果进一步引入有序充电策略,把一部分充电负荷从晚高峰转移到凌晨低谷,空间上可调度和时间上可调度叠在一起,模型的协同效果会更强。不过约束非线性会显著增加,需要重新设计求解方案。
6.3 个人调试体会
最后分享几个调试这类MISOCP模型的小技巧。
给求解器设置一个合理的MIP gap。追求gap太小会导致求解时间爆炸性增长,实际做规划方案对比时,gap设为0.1%到1%完全够用,求解时间能控制在可接受范围内。如果模型规模特别大,先跑一次松弛解看看下界,再决定gap的取值。
给二进制变量一个合理的初始值。YALMIP支持通过assign给变量赋初始解,从一组启发式方案出发,分支定界的搜索效率往往会好很多。我习惯先跑一个不考虑充电站投资的简化模型,拿到一组解,再作为完整模型的初始值。
还要注意求解器的热启动问题。如果是做多场景循环计算,每轮求解前清空上一轮的变量赋值,否则YALMIP会把上一轮的初始值带入新一轮模型,可能导致求解方向被带偏。这个问题很隐蔽,但排查一次就能记住一辈子。
我在第一次把空间可调度约束加入模型时,遇到的最大障碍不是数学建模,而是数据映射——需求节点的编号、候选充电站节点的编号、潮流计算节点的编号是三套体系,维护好它们之间的映射关系,比模型本身花的时间还多。建议从项目一开始就写一个清晰的数据字典,把每套编号体系的关系固定下来,后续所有模块都只跟这个字典打交道。做规划模型,数据和模型同样重要,这一点在项目结束时体会尤其深。
