
这几年做配电网规划跟“电动汽车充电设施如何布置”打了不少交道。很多项目前期拍脑袋定站址、按经验定容量等实际运行起来才发现要么忙闲不均要么分布式电源发了电送不出去。真正要把规划做扎实得把充电负荷的空间可调度特性和分布式电源的时序出力一起放进模型里做联合优化。这篇就完整梳理一下“考虑充电负荷空间可调度特性的分布式电源与电动汽车充电站联合配置方法”重点把建模思路、双层求解架构和Matlab实现拆开讲透适合正在做微电网规划、配电网优化、新能源接入方向研究或做科研复现的同学参考。1. 这个课题到底在解决什么问题为什么联合配置空间可调度又是什么先聊清楚背景不然直接上公式很容易懵。做分布式电源与充电站配置的传统思路有两种一种是单独做充电站选址定容按区域最大负荷需求来定容量完全不理会分布式电源的位置另一种是给定充电站方案后再去优化光伏、风电的安装位置。这两种做法的共同特点是假设充电负荷是“死的”——每个充电站服务范围内的车固定去那个站充电。但实际上充电负荷有比较强的空间可调度性。什么意思呢一个区域内分布着多个充电站用户选择去哪个站充电会受到距离、等待时间、电价、站点容量、道路拥堵程度等因素影响。也就是说某片区域的充电需求并不是天生绑定在某个充电站上它可以在空间上重新分配。如果在配置时把这个可调特性用起来就可以主动引导一部分车去距离更近、配套资源更好的站或者避开分布式电源出力不稳定、电压偏低的节点从而提升整个系统的利用率。那为什么一定要和分布式电源联合配置而不是各做各的因为分布式电源尤其光伏的位置和出力曲线直接影响节点电压、线路潮流和网损。充电站的接入同样影响这几点。假如光伏集中在馈线末端充电站也放在末端低谷期光伏出力大、充电负荷小末端电压可能越上限反过来高峰期充电负荷大、光伏没有出力末端电压又会越下限。分开规划和联合规划的结果差异非常大联合配置本质上就是在同一个优化框架下把这两类决策变量的相互制约和相互补偿关系显式建模出来。这个课题的现实价值也很明确一是投资侧联合配置能共用线路走廊和土地资源降低整体建设成本二是运行侧空间可调度给了系统一个额外的调节自由度相当于用软手段换投资。把一个区域的充电负荷在不同站点之间动态分配给合理可能比盲目多建一个充电站效果好得多。2. 数学建模把“空间可调度特性”变成能在优化模型里计算的东西整个方法的核心是建模。建模的层次决定了代码能做什么、做到什么精度。这里我把模型拆成四块空间可调度特性模型、分布式电源出力模型、规划目标函数、约束体系。2.1 充电负荷空间可调度特性的数学抽象学术上最常用的做法是用重力模型加Logit选择模型来刻画用户对充电站的选择行为。基础假设是一个用户在某时刻产生了充电需求位于某个出行终点附近他面前有若干个候选充电站j每个站对他有一个“效用值”效用越高被选中的概率越大。效用函数可以建得很简单也可以很复杂。我们项目里用的标准形式是[ U_{ij} \alpha \cdot \frac{1}{d_{ij}} - \beta \cdot p_j - \gamma \cdot W_j ]其中d_ij是用户i与站点j的距离p_j是该电站当前电价W_j是排队等待时间可用站点实时负载率近似alpha、beta、gamma是敏感性系数。然后通过Logit模型计算选择概率[ Prob_{ij} \frac{e^{\theta U_{ij}}}{\sum_{k} e^{\theta U_{ik}}} ]这个prob就是一个“空间调度因子”。当我们改变某个站点的容量或者电价水平时概率分布会变化负荷在空间上就发生了转移。在规划模型中这个概率直接用于把区域总充电需求分摊到各候选站点节点上[ P_{load,j} \sum_{i} \lambda_{ij} \cdot P_{req,i} ]实际工程中把研究区域划分成若干交通小区每个小区的充电需求作为集中负荷点通过路网距离计算d_ij。如果不想把交通模型做太重也可以用直线距离并加一个道路曲折系数修正。2.2 分布式电源出力模型时序性是关键分布式电源的配置决策是容量和位置但评估这个决策好不好要看它一年8760小时里实际能发多少电、什么时候发。光伏和风电都有强时变特性所以必须引入时序出力模型。光伏出力可以用典型日的日照强度曲线折算或者用Beta分布随机抽样。工程上我推荐的做法是从当地气象数据获取全年逐小时太阳辐照度和温度然后用效率模型折算[ P_{pv}(t) P_{rated} \cdot \eta_{pv} \cdot \frac{G(t)}{G_{STC}} \cdot [1 k(T_{cell}(t) - 25)] ]风电出力则根据风速的Weibull分布和单台风机的功率曲线拟合获得。如果研究数据来源受限用典型日曲线替代全年时序数据也可以接受但要在结果分析里说明误差来源。最麻烦的是分布式电源的容量决策和时序出力之间的耦合。比如光伏容量配多大直接决定每小时的出力上限而电网的消纳能力又约束了这个容量。所以时序出力的计算不是一次性的必须嵌在规划评估里每次调用。2.3 目标函数年综合费用而不是静态投资配置方案好不好最终要看钱。但只看初投资远远不够因为不同方案在运行中的网损、运维费用、购电成本差距非常大。我们的目标函数用年综合费用最小表示[ \min F C_{inv,new} C_{om} C_{loss} C_{buy} C_{env} C_{serve} ]各项分别代表C_inv_new充电站和分布式电源的年化投资建设成本把总投入按设备寿命和折现率换算成年度值C_om全系统运行维护成本光伏和充电设施按容量计接线部分按长度计C_loss线路网损费用这个要对每个时段做潮流计算之后累加C_buy从上级电网购电的费用分布式电源发电量不足以支撑负荷时需要买电C_env碳排放费用按化石能源替代量折算C_serve充电服务惩罚项如果某站点容量不足导致充电排队过长产生社会成本。目标函数能不能算出来依赖给定的配置方案和运行模拟结果。这也是为什么需要双层求解框架。2.4 约束体系决策变量不是随便取的规划模型的约束分为投资决策约束和运行约束两类。投资决策约束很好理解每个候选节点安装分布式电源不超过可安装面积/接入容量上限充电站容量在规划级别上有个最小值和最大值且站间总覆盖容量需要大于高峰小时总充电需求投资总预算约束。运行约束更关键包括潮流平衡约束各节点有功无功必须满足KCL/KVL节点电压约束一般取0.95~1.05p.u.有分布式电源接入的节点要特别关注上限越界线路载流量约束避免充电站集中接入导致馈线过载充电站服务半径约束确保每个交通小区在合理距离内至少能到达一个充电站不能为了成本把有的区域“放荒”分布式电源渗透率约束防止在消纳能力不足的节点过度安装。整套约束用Matlab实现时最核心的是在评估函数里对每一个“配置方案个体”做完整的时序潮流校验任何一个约束越界都要在适应度函数里以惩罚项形式体现。3. 双层优化求解架构上层做决策、下层做运行模拟两边怎么闭环接着上面说联合配置问题天然适合用双层优化框架来解。原因在于两类决策的时间尺度完全不同充电站和分布式电源的选址定容是长期决策一年甚至几年内基本不变而空间可调度的负荷分配、分布式电源的出力控制、潮流分布是短时间尺度的运行决策每小时内都在变。如果把它们压进同一个单层模型里约束矩阵会大得离谱而且非线性耦合会让求解器很难收敛。3.1 上层规划决策层上层的决策变量是各个候选节点是否建设充电站、建多大容量以及分布式电源的接入位置和容量。这些变量组合成一个个体用粒子群算法或遗传算法做全局寻优很顺手因为它们的编码天然适合这类0-1混合整数问题。每个个体的编码结构大致长这样% [站点1容量, 站点2容量, ..., DG1容量, DG2容量, ...] % 容量为0表示不建设 individual [station_capacity_vector, dg_capacity_vector];上层的适应度值就是目标函数F。但因为F的计算依赖运行模拟结果所以每个个体都必须传递给下层去做评估再返回成本值。粒子群迭代一次就要对所有粒子都跑一遍下层时序模拟。这也是求解耗时的主要来源。3.2 下层运行模拟层下层要做的事情是把一个规划方案放到“真实环境”里跑一年。具体步骤包括生成全年的电动汽车充电需求时序曲线。根据每小时的出行概率、车辆剩余电量等因素计算各交通小区的充电需求按空间可调度模型把充电需求分配到各个候选站点节点上生成分布式电源的全年出力时序光伏按辐照度、风电按风速曲线对每个时段调用潮流计算记录节点电压、线路潮流、网损、购电量等运行指标最后汇总各项成本加上约束越界的惩罚项返回给上层。这层计算量非常大。如果按8760小时跑每个粒子一次评估就要调用8760次潮流。为了控制计算量实际项目中普遍的做法是采用“典型日聚类法”——用k-means把全年数据聚成春、夏、秋、冬四个季节的典型工作日和周末总共8个典型日每个典型日是24小时的时序也就是每个体只需跑8×24192次潮流计算。这样精度损失在接受范围内而计算量能减少一个数量级以上。3.3 反馈通道与收敛逻辑上层的智能算法靠适应度值驱动搜索下层必须把可行性和成本信息完整反馈回去。特别要强调的是千万不能把约束越界直接判死否则搜索空间会被切得零零碎碎算法会在可行域边界挣扎很难找到好解。我们的习惯是给越界设置一个逐步增大的惩罚系数以电压偏差为例[ Penalty M_v \cdot \sum_{t} \max(0, |V_{min} - V_{node}(t)|, |V_{node}(t) - V_{max}|) ]M_v在前面迭代取较小值让不可行个体也有机会参与搜索后期再逐步增大强制方案收敛到可行域。这种“动态罚函数”策略在工程实现中效果明显好于静态惩罚。4. Matlab代码实现项目结构、核心函数拆解与关键片段建模和算法聊完了下面直接说代码。这块是大家最关心也最容易卡住的地方。我建议的Matlab工程目录结构如下为后续维护和做敏感性分析留足空间project_root/ ├── main.m # 主程序入口调度全部流程 ├── config/ │ ├── sys_params.m # 电网拓扑参数、基准功率、电压等级 │ └── ev_params.m # EV数量、电池容量、充电功率、出行特征 ├── data/ │ ├── load_profile.mat # 全年负荷时序 │ ├── solar_irrad.mat # 辐照度数据 │ └── wind_speed.mat # 风速数据 ├── model/ │ ├── spatial_dispatch.m # 空间可调度负荷分配函数 │ ├── dg_output.m # 分布式电源时序出力 │ ├── powerflow.m # 基于潮流计算的节点状态分析 │ └── cost_estimate.m # 成本汇总 ├── planner/ │ ├── pso_planner.m # 粒子群主循环 │ ├── ga_planner.m # 遗传算法主循环备选 │ └── evaluate_individual.m # 单个配置方案的完整评估 └── result/ ├── plot_results.m # 结果可视化 └── output_summary.m # 指标表输出每个模块的职责要单一不要把所有逻辑写进一个大脚本里。尤其是evaluate_individual它是整个系统的“心脏”结构稍乱一点调试起来就是灾难。4.1 主循环与评价函数的交互主程序入口的核心逻辑其实很短%% main.m 核心流程 addpath(genpath(pwd)); % 读取配置 sys sys_params(); ev ev_params(); % 生成典型日 [typical_days, weights] cluster_days(data/load_profile.mat, sys); % 初始化粒子群 nvars sys.N_station sys.N_dg; lb zeros(1, nvars); ub [sys.station_max_capacity * ones(1, sys.N_station), ... sys.dg_max_capacity * ones(1, sys.N_dg)]; options optimoptions(particleswarm, ... SwarmSize, 40, ... MaxIterations, 120, ... FunctionTolerance, 1e-6, ... Display, iter); [x_best, f_best] particleswarm((x) evaluate_individual(x, sys, ev, typical_days, weights), ... nvars, lb, ub, options); % 后处理与结果保存 output_summary(x_best, f_best, sys); plot_results(x_best, sys);粒子群在Matlab里有现成工具箱直接接自定义目标函数即可千万不要自己手写PSO循环调参和稳定性都远不如内置实现。4.2 空间可调度负荷分配函数怎么写这是整个课题最有特色的模块值得单独讲。它的输入是各个交通小区的充电需求量、候选站点状态位置、容量、当前负载率输出是分配到每个站点节点上的充电负荷时序。function P_node spatial_dispatch(ev_demand_per_zone, station_pos, station_capacity, lambda) % ev_demand_per_zone: [n_zone * 24] 各交通小区每小时充电需求 % station_pos: [n_station * 2] 各候选站的坐标 % station_capacity: [n_station * 1] 各站容量0表示未建设 % lambda: 用户灵敏度参数lambda越大距离和等待时间占效用权重越高 n_zone size(ev_demand_per_zone, 1); n_station length(station_capacity); P_node zeros(n_station, 24); zone_center compute_zone_center(); % 各小区质心坐标 for t 1:24 for i 1:n_zone demand ev_demand_per_zone(i, t); if demand 0, continue; end % 候选站点容量大于0且未满载 candidate_idx find(station_capacity 0); % 计算各站效用值 utility zeros(length(candidate_idx), 1); for k 1:length(candidate_idx) j candidate_idx(k); dist norm(zone_center(i,:) - station_pos(j,:)); % 当前负载率作为等待时间近似 load_rate P_node(j,t) / station_capacity(j); utility(k) exp(-lambda(1) * dist - lambda(2) * max(load_rate - 0.8, 0)); end % Logit 概率分配 prob utility / sum(utility); allocated demand * prob; P_node(candidate_idx, t) P_node(candidate_idx, t) allocated; end end end这里的lambda参数非常关键。lambda(1)越大用户越不愿跑远路lambda(2)越大用户对拥堵越敏感。工程上这两个参数需要结合区域调研数据标定。如果没有实测数据建议从lambda(1)0.05, lambda(2)0.1起步做灵敏度分析观察它对结果的影响方向。4.3 分布式电源出力模块光伏出力和风电出力虽然物理机理不同但在代码结构上可以统一成一个函数只是输入数据不同function P_dg dg_output(dg_type, capacity, weather_data, sys) % dg_type: pv 或 wind % capacity: 装机容量 [kW] % weather_data: 辐照度或风速的时序矩阵 [T * 1] % sys: 系统参数效率、温度系数等 T size(weather_data, 1); P_dg zeros(T, 1); switch dg_type case pv G weather_data(:, 1); Tc weather_data(:, 2); % 电池板温度 P_dg capacity * sys.pv_eff .* (G / 1000) .* (1 sys.pv_temp_coeff .* (Tc - 25)); case wind v weather_data(:, 1); % 简化的风机功率曲线 v_in 3; v_rated 12; v_out 25; P_dg zeros(T, 1); idx_1 v v_in v v_rated; idx_2 v v_rated v v_out; P_dg(idx_1) capacity .* (v(idx_1) - v_in) ./ (v_rated - v_in); P_dg(idx_2) capacity; end P_dg max(P_dg, 0); end有一点要提醒很多新手会把容量单位弄混风力发电机的铭牌容量是额定风速下的出力实际年平均出力可能只有容量的20%~30%光伏则要学会区分直流容量和交流容量。配置结果汇报时如果单位不统一一个简单的对比表都会错十万八千里。4.4 潮流评估与成本汇总潮流计算这块如果研究重点是算法和配置逻辑强烈建议直接调开源的Matpower不要重复造轮子。需要做的工作只是把distributed source和charging station处理成PQ节点或PV节点然后对每个典型日时段调用runpf。关键的处理逻辑function [opf_result] evaluate_individual(x, sys, ev, typical_days, weights) % 解码个体前N_station个是充电站容量后N_dg个是DG容量 station_capacity x(1:sys.N_station); dg_capacity x(sys.N_station1:end); % 如果不建设任何设施直接给很差的适应度 if sum(station_capacity) ev.total_demand * 0.2 opf_result.fitness 1e10; return; end % 遍历典型日 total_cost 0; for d 1:length(typical_days) day_data typical_days(d); % 充电负荷空间分配 P_ev_node spatial_dispatch(day_data.ev_demand, sys.station_pos, station_capacity, sys.lambda); % DG时序出力 P_dg_node dg_output(pv, dg_capacity, day_data.weather, sys); % 每个时段潮流计算并累积网损、购电成本 for t 1:24 bus_load day_data.base_load(:, t) P_ev_node(:, t) - P_dg_node(:, t); mpopt mpoption(verbose, 0); results runpf(modified_case, mpopt); total_cost total_cost weights(d) * 365/24 * ... (results.loss_cost results.buy_cost); end end % 加上投资成本、运维成本、碳排放成本 inv_cost annualized_investment_cost(station_capacity, dg_capacity, sys); om_cost annualized_om_cost(station_capacity, dg_capacity, sys); env_cost carbon_cost(total_energy_bought, sys); opf_result.fitness total_cost inv_cost om_cost env_cost; end这个评价函数的灵魂是“投资成本年化”的处理。很多复现代码会直接把设备单价乘容量当作目标这是不合理的。充电站的充电桩寿命一般在10年光伏组件25年逆变器15年如果不年化处理就会导致算法偏向少建充电桩、多建光伏的扭曲解。年化因子用标准的资金回收系数计算[ AF \frac{r(1r)^n}{(1r)^n - 1} ]其中r是折现率n是设备寿命。在sys_params里把每种设备的寿命单独配置evaluate_individual里分别折算。5. 复现案例与结果解读典型的配置方案变化规律写完代码做案例验证时一定要设计对照组否则很难证明“空间可调度”真的起了作用。我们当时用IEEE 33节点配电系统作为基础拓扑设置5个交通小区、7个候选充电站点、6个分布式电源接入点做了三组对照方案A不考虑空间可调度的独立配置各区固定负荷绑定最近站点方案B考虑空间可调度但不联合配置DG方案C同时考虑空间可调度和DG联合配置。结果很有意思。方案C相比方案A年综合费用降低了约13%其中网损费用下降最明显因为调度因子把充电负荷引导到了离分布式电源出力更近的节点减少了潮流穿越。还有一个显著变化是充电站容量配比——不考虑空间调度时算法会给每个小区硬配一个足够大的站“停车坐等充电”的成本很高考虑可调度性之后算法会把容量适度集中到几个枢纽节点因为只要引导得当小站过载的部分可以分流到附近大站不必处处留冗余。另外还有一个很容易被忽略的结论分布式电源的渗透率上限也在发生变化。在方案A里光伏渗透率稍微一高中午过电压问题就会限制DG容量继续增加方案C里充电负荷被引导到光伏出力大的节点附近就地消纳中午时段的净负荷曲线被“削峰”了一部分过电压约束松绑DG装机容量反而可以提升约12%。这个案例也给了我们一个重要启示在做汇报材料时单独列“DG装机多少”“充电站总容量多少”其实说明不了问题更好用的是列出不同方案下的“网损电量”“峰谷差改善率”“平均充电等待时间”“DG利用率”这些运行指标。这几个指标一摆出来联合配置和空间可调度的价值就很直观评审老师和工程业主也都能看懂。代码输出的结果表我习惯用下面的格式组织方案充电站总容量(kW)DG总容量(kW)年网损电量(MWh)年化总费用(万元)DG利用率(%)A2450180086231861.2B22100934296—C2040202071227774.5表格一出来就看得出来方案C用更少的充电站容量支撑了相同的充电需求把省下来的投资转移到了分布式电源建设上网损也降下来了。这个“容量转移”的逻辑就是空间可调度性带来的规划红利。6. 实操与复现中的几个关键坑以及我的处理经验代码写对了算法跑通了并不代表方案就靠谱。以下几件事是我实际项目中反复踩过的拿出来分享一下。6.1 初值敏感性是默认问题别指望一次收敛粒子群对初值的敏感性比遗传算法要低一些但仍然存在。特别是充电站容量这个决策变量如果初始种群集中在某个局部区域很容易收敛到局部最优解。我的处理办法是两阶段策略先用较大的惯性权重跑一个短的初筛找到几个有潜力的区域再在候选解附近重新布点跑精细搜索。Matlab的particleswarm支持设置InitialSwarmMatrix可以把第一阶段的好解作为第二阶段初始种群的一部分。另外把决策变量做归一化也很有必要不同量纲的变量直接放在一起会拉偏粒子速度更新方向。6.2 罚函数设计比约束本身更容易出错前面提过动态罚函数这里具体说一个失败的案例。最初我在evaluate_individual里一旦发现电压越限就直接返回inf结果PSO寻优效果非常差——大量个体因为微小的越界被判死搜索过程几乎是在可行域边缘随机游走。后来改成动态罚函数前期允许轻微越界迭代后期加大惩罚系数收敛速度立刻改善。具体实现上可以给惩罚系数乘一个随迭代次数线性增长的因子penalty_multiplier 100 iter * 10;前期100倍的惩罚让不可行解不被忽略后期增大到1000以上强制压回可行域。注意电压偏差、线路过载、容量不足这几类约束的罚函数量级要统一到同一个成本量纲否则小的约束会完全被大的约束淹没。6.3 空间可调度参数不要直接套文献值很多论文里的lambda参数直接给一组常数但那是针对特定仿真心场的。真实项目中lambda的标定需要结合区域问卷调研或者手机信令数据来看用户跨站充电的意愿。实在没有数据一定要做灵敏度分析至少画一个lambda取值从0.01到0.2变化时总费用和充电站容量的曲线。如果总费用对lambda不敏感说明这个区域内空间调度玩不出花来赶紧把精力放到别的地方如果敏感那这个参数无论如何要标准。6.4 计算耗时是绕不开的现实问题最后说一下耗电问题。8个典型日、192次潮流计算、40个粒子、120次迭代这套组合跑下来大概要2~6个小时取决于潮流计算的规模和机器性能。如果要做多场景蒙特色采样时间直接爆炸。我常用的加速方式有几种先对潮流计算函数做代码剖析把重复计算的节点导纳矩阵、因子分解结果缓存起来不要每次调用都重新算把粒子群并行化Matlab的UseParallel选项可以直接把适应度评估分配到多个worker上这个提升是实打实的线性加速比在首批实验时用聚类日模型的24点降到12点跑通流程后再恢复完整精度。12点和24点的误差通常在1%以内但速度翻倍。6.5 结果可视化的重点不是3D图而是调度轨迹很多复现代码喜欢画容量对比柱状图、收敛曲线图这些当然要有但最有用的是展示“空间可调度前后各充电站的24小时负荷分配变化”。这个图建议用堆叠面积图横轴是时间纵轴是每个站的充电功率颜色区分站点。对比方案A固定分配和方案C可调度分配两张图直接看哪些时段的负荷被从A站转移到了B站配合分布式电源出力曲线一起看解释力非常强。我自己的体验是刚开始复现这个课题时把大量时间花在了调潮流模型和编码上后来慢慢意识到整个方法的核心是“空间可调度特性如何改变规划问题的可行域结构”。把这个逻辑在模型里表达清楚代码自然就顺了。后面如果大家想继续深入可以沿着两个方向做扩展一是在空间调度模型里加入实时电价引导机制让充电负荷的转移跟着分时电价走二是把储能设备引入联合配置框架给空间可调度加上时间维度的储能缓冲系统调节能力会更完整。