1. 问题拆解:为什么充电站和分布式电源必须"联合"配置
做配电网规划的朋友应该都有体会,前些年分布式电源(DG)和电动汽车充电站(EVCS)基本是各做各的:DG选址定容归新能源口管,充电桩布局归营销口管,两边拿着同一张网架图,却很少坐在一起算账。
但实际运行中,这两样东西的耦合远比想象中紧。充电负荷是配电网里增长最快、波动最凶的一块负荷,一台120kW直流快充桩满功率运行时,相当于几十户居民同时用电。而DG接入后,配电网的潮流方向、电压分布、网损特性全部改变——原来辐射状单向潮流的网架,变成了双向潮流的主动配电网。如果规划阶段不联动,很可能出现这种情况:充电站选在DG出力旺盛的馈线末端,午间光伏大发时线路反向潮流过载,晚间充电高峰时又电压越限。
这个项目的核心思路,是把充电负荷的空间可调度特性引入规划模型,与DG的选址定容放到同一个优化框架里求解。所谓空间可调度,简单说就是:电动汽车用户并不是绑定某一个充电站的,A站排队久、电价高、位置偏,用户就可能去B站。那么规划者在布局充电站时,就不只是"选点+定容",还可以通过充电服务价格、容量分配等手段引导用户在空间上分散充电需求,让充电负荷主动去匹配DG出力和网架承载能力。
一句话概括这个项目在做什么:用Matlab搭建一个联合规划模型,输入网架参数、DG候选位置、充电站候选位置、历史负荷曲线,输出DG的安装位置和容量、充电站的选址和容量,并计算考虑充电负荷空间调度前后的成本差异。对做配电网规划课题的研究生来说,这是一个很典型的"论文模型+代码复现"组合;对刚入门的同学,它也提供了一个理解主动配电网规划问题的最小可行框架。
我在这篇文章里会按自己的复现思路,把这套方法从建模到求解、再到踩坑点完整拆开讲,所有内容基于常见实践补充。文章偏工程实现向,不会堆推导公式,重点讲清楚每一块在代码里是怎么落地的。
2. 空间可调度特性到底是什么:数学模型里的"弹性"从哪里来
2.1 先理解"不可调度"的充电负荷长什么样
很多早期研究在规划充电站时,把充电负荷当成刚性需求:每个区域有多少车、每天需要多少电量,是固定不变的,规划者只需要保证充电站容量覆盖这个需求即可。这种假设的好处是模型简单,坏处是——它浪费了EV用户天然具备的选择弹性。
举个实际例子。一个城市中心区域,白天有大量通勤车辆需要充电,如果只有一处充电站且容量紧张,排队时间可能超过40分钟。与此同时,城郊一处充电站因为知道的人少、电价略高,利用率只有30%。在刚性负荷模型里,这个局面无解,只能继续在市中心扩容。但在真实场景里,只要城市郊区的充电站电价每度便宜两毛钱、App显示空闲桩位,一部分用户自然会被引导过去。
所谓空间可调度特性,就是把这个"引导"变成规划模型里的一个决策变量——每个充电需求点的负荷,不一定全部由本区域充电站承接,而是按一定规则分配给区域内所有候选站,分配比例本身可以优化。
2.2 用Logit模型和"效用"概念量化用户选择
要把这个弹性写进数学规划,最常用的做法是采用Logit模型来模拟用户的选择行为。这个模型在交通领域用得非常多,大家如果做过出行方式选择研究,应该不陌生。
基本逻辑是:用户在选择充电站时,会综合比较三个因素——距离、价格、等待时间。每个候选站i对需求点j的用户来说,有一个"效用值":
[ U_{ij} = -\alpha \cdot d_{ij} - \beta \cdot p_i - \gamma \cdot t_i ]
其中(d_{ij})是需求点到充电站的距离,(p_i)是站i的充电价格,(t_i)是估算等待时间,(\alpha, \beta, \gamma)是用户对这三个因素的敏感系数。效用值越高(实际上是负值越小),用户选择这个站的概率越大。
用Logit公式把效用转化为选择概率:
[ P_{ij} = \frac{e^{U_{ij}}}{\sum_{k \in \Omega_j} e^{U_{ik}}} ]
这个公式的逻辑和softmax一模一样,做过机器学习的同学看着应该很眼熟。它保证所有候选站的选择概率之和为1,而且不需要人为指定"哪个站服务哪片区域",边界是自然形成的。
在Matlab实现里,这就是一个矩阵运算的问题:算距离矩阵→算效用矩阵→算概率矩阵,全部可以向量化,不需要循环,速度很快。
2.3 从选择概率到规划决策:负荷分配如何进入优化
选择概率本身是用户行为的描述,但规划模型关心的是"如果我调整充电站容量配置和电价策略,负荷会发生什么变化"。所以要把概率嵌入优化模型,有两条路:
路径A(简化版):把选择概率作为固定参数。先用历史数据算出需求点和候选站之间的选择概率,标定好之后不再变化,充电负荷按这个比例刚性分配到各站。这个方案实现简单,适合入门,但缺点是没有体现"规划决策反过来影响用户行为"的耦合关系。
路径B(完整版):把选择概率公式直接嵌入优化模型,价格、容量成为优化变量,负荷分配比例随决策变量变化。这个方案真正实现了"空间可调度",但模型变成非线性,求解难度上一个台阶。
我的建议是:入门选路径A,论文投稿和深入研究选路径B。这篇文章后续讲解默认走路径B,因为那才是标题里"空间可调度特性"的核心价值所在——规划方案本身就能重塑负荷的空间分布,而不是被动接受一个固定分布。
2.4 这一特性给规划模型带来的"化学反应"
引入这个特性之后,联合配置模型会产生几个有意思的联动效应:
- 充电站选址不再是简单"跟着负荷密度走",而是要考虑DG出力分布。如果某个DG候选点附近的网架承载能力好,充电站可以稍微偏离负荷中心,用价格引导用户多跑一段路,换取更高的DG就地消纳率。
- 网架扩容成本有机会下降。原来负荷集中区域需要重载改造,现在通过空间调度把部分负荷引流到其他馈线,峰值电流被削掉一块,变压器和线路的投资可以推迟甚至取消。
- DG容量配置会更激进。因为有空间调度做缓冲区,DG装机可以适当超过本地负荷需求,多余电量通过充电负荷就地消纳,减少上网压力。
这些联动都是传统"刚性负荷+独立规划"模型捕捉不到的,也是投稿时讲"创新点"最容易出彩的地方。
3. 联合配置模型搭建:目标函数、约束条件和双层结构
3.1 我们先明确规划决策变量
模型要决策的东西分两大类:
- DG规划变量:每个DG候选节点的安装容量(连续变量或离散档位)、是否安装(0-1变量)。
- 充电站规划变量:每个充电站候选点的建设等级或桩数(整数变量)、是否建设(0-1变量)、服务价格(连续变量,路径B才有)。
另外还有运行层面的变量,典型的是各时段各节点的注入功率、充电站各时段的实际负荷、节点电压幅值等。
整个问题的最佳数学形态是混合整数二阶锥规划(MISOCP)——配电网潮流用Distflow方程线性化或二阶锥松弛,规划变量用整数,运行变量用连续量。但MISOCP需要CPLEX/Gurobi等商业求解器的二阶锥模块,门槛高一些。如果只是想跑通框架,完全可以先用**线性化Distflow+混合整数线性规划(MILP)**处理,后面再升级。
3.2 目标函数:全生命周期成本怎么算才完整
联合配置的目标函数,业内最常见的写法是"最小化年化综合成本",主要包含四大块:
- DG投资年化成本:单位容量投资成本 × DG容量 × 年化系数。年化系数用等额分付资本回收公式计算:(CRF = \frac{r(1+r)^n}{(1+r)^n - 1}),其中r是折现率,n是设备寿命。这个公式不理解没关系,Matlab里写一行就能算出来。
- 充电站投资年化成本:单位桩建设成本 × 桩数 + 征地/场地成本。注意充电站的固定成本(场地、变压器、配电设施)和可变成本(充电桩本身)要分开计,因为固定成本不随桩数线性增长,用0-1变量+固定费用项描述更准确。
- 运行维护成本:DG的运维成本(按发电量计)+ 充电站的运维成本(按充电量计),再加配电网网购电成本。
- 网损成本:各时段线路损耗 × 电价。这一项虽然占比不大,但它是DG选址的重要风向标——DG位置选得好,网损能降不少。
设计目标函数时有一个容易踩的坑:网损成本必须用运行模拟算出来,不能拍脑袋估一个百分比。同一个DG容量,放在馈线首端和末端,网损差异可能差出好几倍,直接影响配置结果。
3.3 约束条件逐条理清
我按自己在代码里的实现顺序,把约束分成五组来讲:
| 约束类别 | 包含内容 | 典型数学表达 |
|---|---|---|
| 配电网潮流 | Distflow有功/无功平衡、节点电压限幅、支路电流限幅 | (P_{ij}=p_j+r_{ij}l_{ij}),(V_j^2=V_i^2-2(r_{ij}P_{ij}+x_{ij}Q_{ij})+\dots) |
| 充电负荷分配 | 每个需求点的充电需求全部分配、各站承接负荷不超过其容量 | (\sum_i P_{ij}^{EV}=D_j),(P_i^{EV}\le S_i^{EVCS}) |
| DG运行约束 | 各时段DG出力不超过装机容量、爬坡约束(如适用) | (0\le P_{i,t}^{DG}\le P_i^{DG,cap}) |
| 充电站容量约束 | 各站承接的峰值充电负荷不能超过配置容量 | (P_{i,t}^{EVCS}\le N_i \cdot P^{per_pile}) |
| 投资预算约束 | DG+充电站总投资不超过上限 | (\sum C_{i}^{DG}+\sum C_{i}^{EVCS}\le B_{max}) |
其中充电负荷分配约束是核心,它把"空间可调度"落到模型里。每个需求点的总充电需求必须被满足(这是刚性底线),但具体由哪些站承担、各自承担多少,是优化出来的结果。
3.4 双层结构:为什么我更推荐单层化处理
这类问题很多论文喜欢写成双层:上层做DG和充电站规划决策,下层做运行模拟(各时段最优潮流)。双层的优点是物理意义清晰——先做规划,再看规划方案在运行层面的表现。但缺点是求解困难,下层问题嵌套在上层目标里,直接套用常规求解器会卡死。
我个人的经验是:尽量把双层改写成单层MISOCP或MILP,通过KKT条件或强对偶理论把下层问题"吸"进上层。如果嫌推导麻烦,还有一个折中方案:采样几个典型日(比如夏季典型日、冬季典型日、过渡季典型日),把运行模拟改成有限场景下的约束集,这样下层就变成了若干个平行场景约束,整个模型还是单层混合整数规划,规模可控,Matlab+YALMIP就能解决。
顺带说一句,单层化之后的模型规模如下:以IEEE 33节点系统为例,33个节点,15个DG候选点,8个充电站候选点,24时段,3个典型日,变量数大约在8000~12000个量级,整数变量约100个。这个规模对商业求解器来说完全是"小菜一碟",几秒到几十秒就能收敛。
4. Matlab实现框架:从YALMIP建模到求解器调用
4.1 环境准备:这一套工具链怎么搭配
工欲善其事,必先利其器。做这个项目,我推荐的Matlab环境配置如下:
- Matlab R2020b及以上(低版本也能跑,但YALMIP对高版本适配更好)
- YALMIP工具箱——Matlab里的建模语言,把优化模型从"写矩阵"解放成"写表达式",强烈推荐
- 求解器:首选Gurobi 9.x或CPLEX 12.x(学术许可免费);退而求其次可以用MATLAB自带的intlinprog,但求解速度和稳定性差不少
- Matpower——不是必须,但用它的IEEE算例数据和潮流计算函数能省很多事
安装YALMIP和求解器的过程不展开讲,网上教程很多。只说一个最容易踩的坑:Matlab的路径设置。装完YALMIP和Gurobi之后,必须把他们的根目录通过addpath(genpath(...))加到搜索路径里,而且每次重开Matlab都要重新加——除非你把路径保存在savepath里。我见过不少人"安装成功但一运行就报未定义函数",几乎都是这个原因。
4.2 数据准备:Matpower算例怎么转成规划模型输入
这一步很多人不重视,但恰恰是最耗时间的环节。Matpower的case33bw.m给出的是静态网架参数,而规划模型需要的是:节点负荷曲线(24时段)、DG候选信息、充电站候选信息、需求点分布。
我的做法是写一个data_prep.m脚本,统一完成以下工作:
- 读取Matpower算例,提取支路参数矩阵(首末端节点、电阻、电抗、容量上限)和节点参数(有功/无功负荷基准值、电压限值)。
- 把基准负荷乘以各时段的负荷系数曲线(典型商用/居民混合曲线),生成24时段的节点注入功率矩阵。
- 定义DG候选节点集合(通常选光照/风资源好、且靠近馈线末端的节点)和候选容量档位。
- 定义充电站候选节点集合和需求点集合。需求点怎么划分直接决定模型质量——我习惯把配电网按馈线供区切成3~5个需求块,每个块的中心作为需求点,而不是每个节点都当需求点,否则模型规模会爆炸。
- 预计算需求点到候选站的最短电气距离矩阵。用电气距离而不是地理距离,在规划里更有意义——充电用户感受到的是"方不方便",但电网感受到的是"网损和电压影响"。
4.3 YALMIP核心建模代码:不做黑盒,骨干逻辑全公开
下面给一段核心建模骨架,基本覆盖了模型的主干。实际运行还需要补齐数据文件,但结构是可以直接借鉴的。
% 变量定义 X_dg = binvar(n_dg_candidate, 1); % DG是否安装 Cap_dg = sdpvar(n_dg_candidate, 1); % DG安装容量(连续) X_evcs = binvar(n_evcs_candidate, 1); % 充电站是否建设 N_pile = intvar(n_evcs_candidate, 1); % 充电桩数量(整数) P_evcs = sdpvar(n_evcs_candidate, n_demand_point, n_time, n_scene); % 各站各时段承接各需求点负荷 P_dg = sdpvar(n_dg_candidate, n_time, n_scene); % DG各时段出力 P_buy = sdpvar(n_sub, n_time, n_scene); % 变电站购电功率 V_square = sdpvar(n_bus, n_time, n_scene); % 节点电压平方 I_square = sdpvar(n_branch, n_time, n_scene); % 支路电流平方 % 目标函数:年化综合成本 objective = dg_annual_cost(Cap_dg, X_dg) + evcs_annual_cost(N_pile, X_evcs) ... + sum(sum(sum(om_cost .* P_dg))) ... + sum(sum(sum(elec_price .* P_buy))) ... + network_loss_cost; % 约束组:充电负荷空间分配 Constraints = []; for s = 1:n_scene for t = 1:n_time for d = 1:n_demand_point % 需求点总充电需求必须被满足 Constraints = [Constraints, ... sum(P_evcs(:, d, t, s)) == D_demand(d, t, s)]; end for k = 1:n_evcs_candidate % 各充电站承接负荷不超过容量 Constraints = [Constraints, ... sum(P_evcs(k, :, t, s)) <= N_pile(k) * P_per_pile]; end end end % ... 其余潮流约束、DG出力约束等按Distflow逐条写入 ...注意:如果走路径B(完整空间调度),
P_evcs和选择概率之间有非线性耦合关系,不能直接像上面这样线性写。常用的处理办法是把Logit公式线性化——取场景集合,预先枚举价格离散档位,把选择概率做成"价格档位×0-1变量"的组合。这相当于把非线性函数用分段线性近似,工程上完全够用。
4.4 求解流程与收敛性判断
求解器调用在YALMIP里就是三行:
ops = sdpsettings('solver', 'gurobi', 'verbose', 2, 'showprogress', 1); optimize(Constraints, objective, ops);但日常跑模型,很少一次成功。我基本按这个顺序排查:
- 先去掉整数约束跑一遍,看连续松弛版能不能求解。如果连续版就报不可行,说明约束之间存在矛盾——最常见的是充电需求总量大于预算下的最大充电容量,这时要么放宽预算,要么削减需求。
- 再放开整数约束跑,这时大概率会出现求解时间暴增。优先检查是不是某个
binvar变量的维度写错了,导致YALMIP生成了几万个额外整数变量。 - 检查目标函数量纲一致性。投资成本是万元/年,网损成本是元/天,如果忘记统一,优化器会疯狂压低网损而忽略投资,产出完全不合理的规划方案。这个错误报了无数次警,建议一开始就把所有成本统一成万元/年。
5. 算例设计与结果验证:没有对比的实验等于白做
5.1 基准场景怎么设置才有说服力
一个完整的算例,必须包含至少三个场景的对比,否则无法体现"空间可调度特性"的增量价值:
- 场景1(基准刚性模型):充电负荷按固定比例分配,不考虑空间调度。每个需求点的负荷只分配给距离最近的候选站,这是传统的"就近服务"假设。
- 场景2(完全空间调度模型):用本篇模型的路径B——通过价格、容量联合优化引导负荷空间分布。
- 场景3(敏感性分析场景):改变用户距离敏感系数、价格敏感系数,考察这些参数对规划结果的影响程度。
这三个场景跑下来,预期的结果方向是:
| 场景 | 充电站总投资 | DG总容量 | 年网损 | 总年化成本 |
|---|---|---|---|---|
| 场景1(刚性) | 偏高 | 偏低 | 偏高 | 基准 |
| 场景2(可调度) | 略低 | 略高 | 明显下降 | 比基准下降8%~15% |
注意结果方向不是绝对的,和具体网架参数强相关。但如果场景2的总成本反而比场景1高,那要先检查模型——大概率是约束写错了,比如充电负荷分配约束里没有保证"总需求=Σ分配量",导致模型偷偷丢掉了一部分需求来省钱。
5.2 从结果反推模型合理性:三个必查指标
算完结果不能直接出图写结论,我习惯先做三件事:
- 查充电站容量利用率:如果某个站容量利用率接近100%,而另一个站利用率不到20%,说明空间调度几乎没有起作用——问题可能出在价格变量范围设置太窄,用户没有动力换站。解决方法是扩大价格上下限范围。
- 查电压越限节点:把24时段电压最低节点的曲线画出来,如果最极端情况恰好卡在0.95(国标下限),说明DG和充电站配置刚好压着约束边界,方案其实没有安全裕度。投稿审稿人经常拿这一点挑刺,建议主动留3%~5%的裕量。
- 查DG消纳率:DG总出力中自用/就地消纳的比例。空间调度模型一个重要的隐含收益就是提升DG消纳率——如果场景2的消纳率没有比场景1高,那这个"联合配置"和"独立配置"就没拉开差距,创新点立不住。
5.3 收敛性、计算时间与模型规模的扩展路
IEEE 33节点系统规模下,场景2的求解时间通常在30秒~5分钟之间(取决于整数变量数量和求解器),这个量级完全可以接受。
如果你的研究方向需要扩展,最常见的两个方向是:
- 扩大到IEEE 123节点或实际馈线:模型结构不变,但约束矩阵维度上涨4倍左右,求解时间可能涨10倍。这时需要把24时段改成典型时段聚合(比如分峰谷平三段),把场景数从3压缩到2,牺牲精度换可解性。
- 加入光伏出力不确定性:用鲁棒优化或场景随机规划替代确定性模型。这部分改动较大,建议在确定性问题完全跑通之后再做。
6. 复现过程的常见坑与处理经验
6.1 充电负荷总量和配网容量不匹配引发的"诡异不可行"
这是我在实际调试中反复遇到的一类问题。场景2模型跑出来一直报"Infeasible problem",而且不给你任何有效提示。我花了大量时间逐条检查约束才发现:问题根本不在约束逻辑,而在输入数据——某个需求点的24小时充电需求总量为3600kWh,但这个点周围所有候选充电站的最大总容量加起来只有3200kWh。
解决方案也分两个思路:一是调参,把需求点拆分、增加候选站数量,让容量覆盖需求;二是承认现实,把这个约束从"必须满足全部需求"改成"满足95%需求,剩余5%由移动充电车或邻近区域补充"。做研究时不必太纠结于100%满足——实际工程里也没有100%覆盖率。
6.2 Logit模型里的"效用值"量纲问题
空间调度模型里有一个细节容易被忽略:距离、价格、等待时间三个因素的单位完全不一样(km、元/kWh、分钟),如果直接代入(U_{ij})计算,量级大的因素会完全主导选择概率,量级小的因素形同虚设。
比如距离是0.5~10km,价格是0.8~1.2元/kWh,等待时间是5~30分钟,如果不做归一化,距离项在效用值里占绝对主导——这等于用户只看距离,不看价格,空间调度完全失效。
正确的处理方式是把三个因素各自归一化到[0,1]区间,或者通过灵敏度系数的相对大小来平衡(比如标定α=0.5, β=1.2, γ=0.3,让价格权重最大)。参数标定是空间调度模型能不能出好结果的关键,建议至少做一组敏感性分析,展示结果对参数不过分敏感,审稿人最怕的就是调参调出来的结果。
6.3 YALMIP和Gurobi版本兼容性:一个容易被忽视的坑
Matlab 2023b + YALMIP的最新版(2023年之后) + Gurobi 10.x,这个组合我实际跑下来很稳。但如果你是老版本Matlab(R2019a及更早),YALMIP里ganz的optimize传到Gurobi的接口可能会有问题,报错信息五花八门:有报"Unknown solver"的,有报"Gurobi not found"的,还有报"License error"的。
遇到这类问题,最省事的办法是检查两个版本号:在Matlab里运行yalmiptest,看Gurobi是否被正常识别;再运行gurobi_setup(Gurobi自带的Matlab接口脚本),看是不是没把Gurobi的Matlab目录加进来。这两个检查能排除90%的兼容性问题。
6.4 结果可视化和论文出图的一点心得
最后分享一个出图层面的细节。这类规划问题,审稿人最想看到的三张图是:
- 规划方案示意图:在配电网拓扑图上标出DG安装位置/容量、充电站位置/容量,用不同颜色区分新建和扩容。
- 24小时充电负荷分布曲线:三个场景的对比,展示空间调度如何削峰填谷。这张图最有说服力,因为一眼能看出调度前后的负荷曲线变化。
- 成本构成堆积柱状图:投资成本、运维成本、网损成本各自占多少,三个场景并排对比,直观展示成本下降来自哪里。
画图用Matlab自带的plot、bar、scatter就够。别把精力花在花哨的配色上——审稿人看的是趋势和差异,不是配色。
7. 写在最后:这套代码还能怎么扩展
我自己在做这个项目时最大的感受是:空间可调度特性听起来是一个很"虚"的概念,但落到数学模型里,其实就是改了一个约束、加了一个变量、换了一套参数标定。真正的难点从来不在数学本身,而在于你想清楚这个模型要回答什么问题——是回答"充电站建在哪",还是"充电站建多大",还是"怎么定价才能引导负荷分布"。
如果你打算在这个方向上继续深入,我建议按这个顺序扩展:
- 先在IEEE 33节点上跑通刚性模型和空间调度模型,对比结果;
- 再从路径A升级到路径B(把选择概率参数变成决策变量);
- 再加分布式光伏的时序出力和不确定性;
- 最后考虑接入交通路网数据,做真正意义上的"车-路-网"协同规划。
每一步之间都有清晰的递增关系,不会出现推翻重来的情况。这也是我推荐这个题目作为论文方向的原因——它刚好踩在配电网规划从"被动适应"向"主动引导"转型的节点上,方法成熟、工具齐全、扩展空间大,足够支撑一篇高质量的期刊论文。
如果复现过程中遇到具体报错——不管是YALMIP建模、Gurobi求解还是结果解读,欢迎带着你的调试信息来交流。这类问题往往一句话就能点破,但自己闷头查可能要花上一整天。