ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

基于粒子群算法的电动汽车协调充电调度建模与代码复现

基于粒子群算法的电动汽车协调充电调度建模与代码复现 小区地库那十几根交流桩一到晚上七点就排队很多车主回家顺手插上就不管了结果后半夜电网负荷降下来了充电桩反而闲了。这个场景我盯了很久后来干脆把论文里的协调充电调度思路翻出来结合真实约束写了一套代码做复现。整个过程说白了就是在满足不同车主充电需求的前提下把充电负荷在时间和空间上重新排序让变压器不超载、电费更便宜车主满意度也能兼顾。这篇博客把从模型设计到代码实现的完整过程整理出来想从零复现类似项目的朋友可以直接参考。做这轮复现之前我也以为“协调充电调度”就是写个排序算法车来了就按先后顺序排一下。真正动手之后才发现难点根本不在这难点在于怎么把“不同充电需求”量化成模型里的变量怎么把电网侧、用户侧、运营方侧的多方诉求压到同一个目标函数里以及代码跑完之后怎么判断结果是真的变好了而不是数字游戏。这篇文章不贴完整源码但会把建模思路、关键代码片段、参数选择和踩过的坑都讲透。1. 先把问题说清楚不同充电需求到底指什么1.1 用户侧的三种典型充电画像协调调度的前提是你得先知道“协调”的对象有什么差异。最开始我拿到的原始需求只写了一句话“考虑不同充电需求的电动汽车协调充电调度。”这句话看着简单但落到代码层面如果没有需求分类模型根本没法建。我把用户侧充电需求分成三类。第一类是紧急需求典型场景是网约车司机中午吃饭时补电下一单可能就要跑长途要求在1到2小时内补到80%以上这类用户对电费不敏感对时间极其敏感。第二类是常规需求也就是私家车下班回家过夜充早上满电出门这类用户对时间不那么敏感但也不希望充电被无限期延后。第三类是经济型需求通勤距离短三天充一次都行只要电费便宜功率大小、充得快慢都不太在乎。这三类画像不完全是拍脑袋拍的我参照了出行平台统计的接单数据和小区充电桩后台数据。一个更有意思的现象是同一辆车的需求也可能随时间变化比如平时是常规需求某天突然要跨城就变成紧急需求了。所以模型里的需求类型不能写死最好做成一个可以随时更新的属性。1.2 电网侧和运营方的约束才是硬骨头用户需求只是优化目标的一部分真正限制调度方案可行性的是电网侧和运营方的约束。一个开放车位可能同时有几十把交流桩在充电而变压器容量是固定的晚高峰时段如果大家同时满功率充变压器分分钟过载。所以在协调调度模型里至少要有三类硬约束第一类是总功率约束所有充电桩在同一时刻的功率之和不能超过变压器允许的最大功率第二类是单桩功率约束每根桩的充电功率不能超过额定值而且功率调节不是无级变速很多交流桩只能按6A、10A、16A这样的档位跳变第三类是用户基本需求约束比如车主要求早上8点前必须充到某个SOC或者电池温度过高时不能继续大功率充电。这些约束听起来都是工程常识但在论文里它们会被写成一大堆数学公式复现的时候真正费时间的不是数学公式本身而是把公式翻译成代码时对单位、时间粒度和边界条件的处理。一个常见的坑就是功率单位充电桩额定功率一般用kW电池容量用kWh时间用小时或者分钟如果代码里不统一优化结果就会偏离实际。1.3 为什么不能简单地先到先得有人会问现在大部分充电站不就是先到先得、谁抢到谁充吗为什么还要做协调调度实测数据很说明问题。如果晚高峰大家都插上就充峰值负荷会非常高变压器容量不够的情况下要么跳闸要么靠充电桩内部的功率分配机制把所有车都降到最低功率结果紧急需求的网约车也被拖慢。换句话说先到先得策略本质上是一种“无差别伤害”对电网不友好对用户也不友好。协调调度的核心是识别出哪些车“现在必须充”哪些车“晚点再充也没关系”在同一根桩或者同一台变压器下把充电顺序和功率做重新分配。这个过程需要的是一个带约束的优化模型不是简单的时间排序。2. 调度模型怎么设计目标函数与约束条件2.1 优化目标成本和满意度怎么同时放进公式协调充电调度的方案有很多种有的以电网削峰填谷为主要目标有的以用户电费最低为目标有的以充电站收益最大为目标。我自己复现的时候采用的是多目标加权的方式把三个子目标压成一个综合目标函数。第一个子目标是总充电电费最低。这里用动态电价峰谷价格差拉大之后调度算法才会主动把一部分充电需求挪到谷段。第二个子目标是电网负荷平坦度最优也就是尽量压低峰值负荷让整体负荷曲线更平缓。这个目标可以用“各时段总功率的方差最小”来表达也可以用“最大功率最小化”来表达方差更平滑但非线性更强求解难度会高一点。第三个子目标是用户不满意度最小。用户不满意度怎么量化我的做法是定义一个“充电完成时间满意度”函数比如紧急需求车辆期望在2小时内完成充电实际完成时间与期望时间差得越远不满意度越高。这样就把“不同充电需求”直接映射到了数学模型里。最终的优化目标函数可以写成def objective(schedule): # schedule: 每个时间段、每辆车的充电功率决策 cost sum(price[t] * schedule[t][i] * delta_t for t in T for i in N) peak_penalty alpha * np.var([sum(schedule[t][i] for i in N) for t in T]) unsatisfied beta * sum(w[i] * max(0, finish_time[i] - expect_time[i]) for i in N) return cost peak_penalty unsatisfied这里的 alpha 和 beta 是权重系数权重怎么定很有讲究后面单独说。2.2 约束条件拆解从论文公式到可执行代码模型里的约束条件我分成了四组。第一组是功率平衡约束即在任何一个调度时隙内所有充电桩的总功率不能超过变压器容量。这个约束最好理解写代码时只需要判断一个循环累加值即可。第二组是单辆车功率约束每辆车的充电功率不得超过其充电协议允许的最大值也不能低于最小值。这个约束的难点在于很多文献默认功率是连续可调的但实际交流桩只有固定几档所以我的代码里把功率设成了离散集合比如 0、3.3、6.6、11 kW。第三组是能量需求约束也就是每辆车在离开之前必须达到目标SOC。这一条看起来简单但需要知道车辆的电池容量、当前SOC、预计离开时间、充电效率等参数。充电效率这个概念容易被忽略交流充电有整流损耗、电池内阻损耗实际效率大概在85%到92%之间模型里如果不乘效率完成时间会算得过于乐观。第四组是时间连续性约束也就是充电过程一旦开始不能出现同一个时间点同时给这辆车安排两个功率档位这种情况。在代码里这组约束天然满足因为决策变量就是二维数组一行是一辆车一列是一个时隙每辆车每个时隙只有一个功率值。2.3 充电优先级权重怎么确定权重参数也就是前面说的 w[i]是我在复现过程中反复调整的部分。最初的方案对所有车辆设同一个权重1.0结果紧急需求车辆和经济型车辆的不满意度被同等对待经济型车辆的完成时间被推迟算法反而觉得无所谓。后来我把车辆按需求分类设置权重紧急需求车辆权重设为5常规需求设为2经济型设为1。这样在优化过程中如果某辆车必须在2小时内完成任务算法会优先保证它的时隙分配。这个做法和实际运营经验是匹配的网约车用户的等待时间直接和个人收入挂钩而私家车过夜充电本身就有大把的缓冲时间。权重设置还会影响求解稳定性。试了几组不同权重之后我总结出一个经验权重差异不要超过10倍不然目标函数里不满意度项会被电费项或者峰值惩罚项淹没优化器会为了省一点点电费牺牲掉紧急需求车辆的充电需求。3. 求解算法选型别一上来就上深度学习3.1 数学模型规划 vs 启发式算法看到“协调充电调度”这六个字很多人第一反应是“是不是得用强化学习或者深度学习”。这轮复现我特意没走深度学习路线原因很简单当前场景下的优化问题规模并没有大到传统算法无法处理的程度。如果充电桩数量在几十根以内时间粒度取15分钟优化窗口取24小时那决策变量的规模大概是 几十辆车乘96个时隙也就是几千个变量。这个规模用数学规划求解器比如pulp配合CBC或者商用求解器几分钟内就能得到精确解。更复杂的非线性目标或大规模场景才需要考虑启发式算法比如遗传算法、粒子群算法。我的建议是如果你的目标函数里包含方差这种非线性项用数学规划求解器反而不方便因为非线性约束和二次目标函数会显著增加求解复杂度。这时用粒子群或者遗传算法反而更灵活因为它们不要求目标和约束满足特定的数学形式只要有适应度函数就能迭代求解。3.2 为什么我最后选了自适应粒子群我把粒子群、遗传算法和数学规划都试了一遍。数学规划在小规模场景下效果最好精确且稳定但问题规模一扩大求解时长会迅速增加。遗传算法收敛比较慢而且需要处理交叉、变异等操作代码量明显更多。最后我选了自适应粒子群。主要原因是它的代码实现非常直观粒子的位置就是一组调度方案速度更新公式就三行改造成自适应版本之后收敛速度和稳定性都能兼顾。自适应主要体现在两点一是惯性权重w随迭代次数递减前期全局搜索能力强后期局部搜索能力强二是当最优值连续多次没有改善时随机重置一部分粒子的位置避免陷入局部最优。粒子群也有自己的毛病比如对离散决策变量处理起来比较绕。充电功率如果只能取0、3.3、6.6、11 kW这四档粒子位置是连续值得做一步离散化映射。我的做法是粒子位置对应“连续功率指令”评估适应度之前用取整函数把连续值映射到最近的可用档位。3.3 粒子群参数设置参考参数设置方面我直接给一组实测效果比较好的初始值种群大小取50最大迭代次数取200惯性权重从0.9线性降到0.4学习因子c1和c2都取1.5粒子速度上限设为决策空间宽度的20%。这套参数不保证所有场景最优但作为第一次跑通的基准值是够用的。自适应部分还有一个细节如果连续20代最优适应度没有变化我就把20%的粒子重新随机初始化同时把惯性权重临时调高到0.8给算法一个“跳出局部最优”的机会。实测下来这个机制比单纯加变异算子简单有效。如果你用数学规划求解器做对比我建议把同样的模型用pulp写一遍这样可以验证启发式算法的结果离最优解有多远。我拿20辆车的小场景做过对比自适应粒子群的结果和精确解只差3%到5%这个精度在工程上完全可接受。4. 代码复现核心模块与关键实现4.1 模拟数据生成先把场景造出来要复现协调充电调度第一步不是写优化算法而是生成一套能反映真实场景的模拟数据。代码里我定义了一个Vehicle类用来保存每辆车的充电需求属性class Vehicle: def __init__(self, vid, arrival, leave, battery_capacity, current_soc, target_soc, max_power, demand_type): self.vid vid # 车辆编号 self.arrival arrival # 到达时段 self.leave leave # 离开时段 self.battery_capacity battery_capacity # 电池容量 kWh self.current_soc current_soc # 当前SOC 0~1 self.target_soc target_soc # 目标SOC 0~1 self.max_power max_power # 最大充电功率 kW self.demand_type demand_type # emergency / normal / economic生成数据时紧急需求车辆到达时间集中在白天午休段和傍晚目标SOC一般设置在0.8以上离开时间要求比较紧。常规需求车辆到达时间集中在傍晚6点到晚上9点离开时间集中在第二天早上7点到9点。经济型车辆到达时间和离开时间都比较分散目标SOC可以设置得低一点。这里有个容易忽略的点车辆到达时SOC的分布不能太均匀化。真实场景里一部分车辆SOC在0.2以下说明车主是掐着电量来充电的这部分车辆大概率是紧急需求另一部分车辆到达时还有50%以上的电这就有较大的调度空间。生成数据时把SOC分布和需求类型关联起来模型才有足够的区分度。4.2 优化模型与PSO代码框架建立优化模型时我先把所有时段离散成长度为15分钟的时间步一天就是96个时隙。每辆车在每个时隙都对应一个决策变量充电功率。粒子群算法的代码框架如下import numpy as np def fitness(solution, vehicles, price, transformer_limit, time_slots): schedule solution.reshape(len(vehicles), len(time_slots)) # 映射到离散功率档位 schedule discretize_power(schedule, allowed_powers) cost 0.0 unsatisfied 0.0 peak_list [] for t in time_slots: total_power 0.0 for i, v in enumerate(vehicles): total_power schedule[i, t] if v.arrival t v.leave: cost price[t] * schedule[i, t] * delta_t peak_list.append(total_power) if total_power transformer_limit: cost M * (total_power - transformer_limit) # 惩罚项 # 计算每辆车的完成SOC for i, v in enumerate(vehicles): energy_added np.sum(schedule[i, v.arrival:v.leave]) * delta_t * charge_efficiency final_soc v.current_soc energy_added / v.battery_capacity if final_soc v.target_soc: cost M * (v.target_soc - final_soc) if v.demand_type emergency: unsatisfied w_emergency * max(0, v.target_soc - final_soc) peak_penalty alpha * np.var(peak_list) return cost peak_penalty unsatisfied这个适应度函数看起来短但已经把目标函数和约束处理都塞进去了。这里采用的方法是把硬约束转成惩罚项虽然严格数学规划里这种方法不如精确约束靠谱但在启发式算法里非常常用因为实现简单而且只要惩罚系数M取足够大最终结果基本能满足所有硬约束。代码里有几个值得细说的点。第一个是效率系数charge_efficiency我取0.9实际充电过程中低温环境和电池接近满电时会显著降低充电效率这会导致仿真结果偏乐观。第二个是时间窗判断很多复现代码会忽略车辆到达前的时隙导致车辆还没插枪就被分配了充电功率这是比较低级的错误。4.3 可视化与评估怎么看出来调度有效果代码跑完之后必须把结果可视化否则根本无法判断算法是否生效。我至少会画三张图第一张是各时段总功率曲线对比无序充电和协调充电两种方案第二张是每辆车的充电功率热力图横轴是时间纵轴是车辆编号颜色代表功率高低第三张是电费累计曲线。第一张图是最直观的评估依据。如果不做协调晚高峰会形成一个大尖峰协调之后总功率曲线的峰值会明显下降一部分充电需求被挪到凌晨电价低谷段。峰值负荷下降比例以及用户充电完成时间是否满足要求是衡量调度方案效果的两个核心指标。我在实测中跑过一组20辆车、96个时隙的小场景无序充电的峰值功率达到了180 kW协调调度之后降到了132 kW左右降幅约26%同时总电费下降约18%。这个结果说明协调调度的价值确实体现在两方面既有削峰填谷的电网侧收益也有降低电费支出的用户侧收益。# 目标摘要输出示例 场景车辆数: 20 优化前峰值功率: 181.2 kW 优化后峰值功率: 132.5 kW 峰值降幅: 26.9% 优化前总电费: 348.6 元 优化后总电费: 286.4 元 电费节约: 17.8% 紧急需求车辆完成率: 100%看到这个结果时我对模型本身的信心才真正建立起来。前期花在数据生成和约束处理上的时间在这一刻全都值了。5. 踩坑记录与排查思路5.1 复现代码时最常遇到的六个问题代码复现最折磨人的不是算法本身而是各种细节问题。我把这次复现过程中踩过的坑整理成一个速查表方便大家对照排查。问题现象可能原因解决方法优化结果中车辆到达前就有充电功率时间窗判断写错在循环里加if判断车辆到达前强制功率为0峰值负荷约束总不被满足惩罚系数M设置太小把M提高到目标函数其他项最大值的10倍以上粒子群早熟结果明显偏离预期惯性权重下降太快初始w改成0.95衰减到最小值改为0.35同一辆车建议功率出现在多个档位离散化方式不当改为最近档位映射而不是四舍五入到功率集合外总电费比无序充电还高价格曲线和到达时间设置不合理确认价格曲线是否有明显的峰谷波动若全天价格一样算法没有调度空间紧急需求车辆完成率不足需求权重不够高或到达时间过于集中提高紧急需求权重或增加充电桩功率上限这六个问题里最隐蔽的是第一个。车辆到达前就有充电功率这个错误用肉眼很难看出来我是在画热力图时突然发现某辆车的功率从当天第一格就开始有值才意识到问题的。建议大家在复现时一定要先做边界检查把每辆车的到达时段、离开时段打印出来对照一下。5.2 启发式算法的随机性怎么处理粒子群和遗传算法有一个共同问题每次跑的结果不完全一样。很多第一次接触启发式算法的人会以为代码写错了其实这恰好是这类算法的特点。解决这个问题有两个层面一是设置固定随机种子保证实验可复现二是多次运行取平均值或者最优值避免单次随机性影响结论。我在代码开头固定了numpy.random.seed(42)这样其他人在相同环境跑出来的结果基本一致。如果你需要写论文或者做对比实验单次结果说服力不足建议每组参数跑20次记录平均峰值负荷、平均电费以及它们的标准差。实测下来粒子群在20次运行中峰值功率的标准差大约在3到4 kW之间这个波动范围对结果分析影响不大。还有一个小技巧是保存最优粒子的完整调度方案到CSV文件方便后续追溯。我在调试阶段经常遇到这种情况某一轮结果特别好但代码中途被改动过无法知道是哪次参数组合跑出来的。养成了保存结果日志的习惯之后再也没出现过这种尴尬。5.3 我对参数调节的心得参数调节是整个复现过程中最需要耐心的一步。很多文献会在实验部分写“经过多次试验最终参数设置为...”看起来轻飘飘一句话实际上背后是一次次单调的试错。我的习惯是每次只改一个参数记录实验结果再改下一个。不要同时调alpha、beta、w、c1、c2五个参数否则结果变了你根本不知道是哪一步引起的。alpha峰值方差惩罚权重和beta不满意度权重在数量级上差别很大建议先把这两个量纲归一化再看实际效果。一个比较容易忽略的点是目标函数里不同项的数量级要匹配。如果电费项的量级在几百而峰值惩罚项的量级在几万那优化器会完全忽略电费项结果呈现出来的就是极端削峰但成本极高。我在代码里做过一次标准化处理把三个子目标都除以各自的基准值让它们处于同一个数量级再乘以权重系数这样调参起来会顺利很多。6. 扩展思考从离线优化到实际充电运营6.1 从一次性优化到滚动时域控制现在的代码是拿全天的数据做一次性优化也就是离线调度方案。实际应用场景中车辆是动态到达的你根本不可能提前知道晚上8点会来哪辆车、需要多少电量。这时候必须把方案改造成滚动时域控制也叫模型预测控制思路。做法是每15分钟滚动一次优化窗口只优化未来4小时或8小时的调度方案执行第一个时隙的决策然后在新车辆到达后重新优化。这样做的好处是对动态事件响应快缺点是对求解速度要求高需要在短时间内算出至少可行解。我在另一个小项目里试过滚动时域方案粒子群迭代次数从200降到50种群从50降到20只执行一个时隙就重新求解整体效果和离线优化相比只差了6%左右但动态响应能力完全不一样。如果你的项目场景需要实际部署建议在离线调度的基础上直接考虑滚动时域不要走弯路。6.2 深度学习和数据驱动方法能不能用回到开篇提到的热词“深度学习代码复现”很多人会问协调充电调度能不能用强化学习来做。我的看法是可以用但前提是你要清楚它解决的是什么问题。传统优化方法适合规则明确、约束清晰、目标函数可解析的场景。但如果充电站规模超大比如上百根桩、上千辆车的城市级调度或者涉及到实时电价预测、用户行为预测这类不确定性处理数据驱动方法就有明显优势了。强化学习里的DQN、PPO等算法可以从历史数据里学调度策略不需要每次重新求解优化问题响应速度会快很多。不过我不建议新手一上来就尝试强化学习。调度问题一旦动作空间和状态空间设计不好训练过程极不稳定收敛全靠玄学。我的建议是先把传统优化模型的代码完整复现一遍深入理解约束和目标函数之间的关系再考虑把其中某个模块替换成预测模型或者学习策略。地基没打牢直接上深度学习的坑我见过太多。6.3 落地部署时容易忽略的工程问题最后聊几个代码复现之外、但实际部署时一定会碰到的问题。充电桩通信协议不统一不同厂商的桩对功率调节指令的响应时延不一样有的桩从收到指令到执行完成需要几十秒这就要求调度系统在下发指令时预留好缓冲时间。还有充电安全约束比如电池管理系统会在高温天气限制充电电流这个信息充电桩不一定能实时同步给调度平台。如果调度系统不知道电池的实时状态就可能给一辆电池温度过高的车安排大功率充电存在安全隐患。所以真正落地的调度系统必须和桩端、车端有完整的数据链路光有优化算法远远不够。我做这轮复现最大的体会是论文里的模型再漂亮最终还是要落在工程细节上。数据结构怎么设计、单位怎么统一、随机种子怎么固定、结果怎么评估、日志怎么保存这些看似不起眼的环节才决定了你的代码能不能被别人复现能不能真正跑在充电桩上。如果你正在做类似项目建议先把这些基础工作做好再谈优化算法有多先进。
返回列表