最近在做一个关于无线充电车辆调度的项目:一批电动公交车/园区接驳车,跑在一个铺设了动态无线充电线圈的路网上,既能正常行驶,又能在经过充电路段时边跑边充。单看每辆车没问题,但整个车队的路线怎么走、每条路线上每一段跑多快,直接决定了充了多少电、耗了多少能、能不能按时到站。我最初试过把路线和速度拆开做两阶段优化,效果不理想,后来改成用随机搜索优化方法做路线与速度的联合分配,在Matlab里实现并做了一轮比较完整的实验。
这篇文章主要记录这个项目的建模思路、算法选型、代码实现和实测过程中踩过的坑,内容围绕“无线充电车辆路线和速度预测”展开。适合刚接触动态无线充电系统调度、想把优化算法落到Matlab代码里的读者参考;即使你是做传统车辆路径问题(VRP)的,看到“速度也作为决策变量”这个维度后,也会有一些新的启发。
1. 问题建模:无线充电车辆的路线与速度联合优化
很多网上流传的代码示例都把问题简化成“最短路径”,但无线充电车辆真正难的地方在于:路线会改变充电机会,速度会改变充电时长和耗能速率。二者必须同时放进模型里,否则算出来的方案很可能在实际中根本不可行。
1.1 为什么路线和速度必须一起考虑
先解释一个直觉:动态无线充电系统(Dynamic Wireless Power Transfer, DWPT)一般是在某些路段的路面下埋设发射线圈,车辆经过时通过车载接收线圈获取电能。它的核心特征是“边跑边充”,所以车辆在一段充电路面上停留多久,直接决定了能充进多少电。
- 车速越快,经过充电路段的时间越短,充电量越少;
- 车速越慢,充电量越大,但行驶时长增加,可能超出时间窗;
- 不同路线的充电路段长度、数量、分布完全不同,所以路线选择决定了“充电补给机会”的上限。
这还没算上能耗:车速本身也会影响单位里程能耗,很多电动汽车的能耗曲线在中低速区间有一个经济车速点。综合来看,路线和速度通过“能耗+充电”双向耦合在一起,不是简单的加法关系,必须做联合优化。
我从实际测试里获得的一个深刻体会是:如果固定路线只优化速度,结果往往是在一条充电能力很弱的路线上拼命降速,导致总运营时间暴增,成本反而更高。这就像你选了一条没有服务区的高速,然后靠开慢车省油,既危险又不划算——核心问题在于路线本身提供的“补给机会”不够。
1.2 模型变量与目标函数的构建
这个模型可以抽象成一个有向图G=(N, A),N是节点集合,A是路段集合。一部分路段带有无线充电功能,参数为充电功率P_c(kW)和线圈长度L_c(m),没有充电功能的路段P_c = 0。
我设定了三类决策变量:
- 路线变量:每辆车选择哪一条路径,可以用路径索引,也可以用0-1变量表示是否经过某条弧;
- 速度变量:每辆车在每条路段上的平均行驶速度v_{i,j};
- 辅助变量:各节点到达时间、离开节点时的电池剩余电量(SOC)。
目标函数我采用的是“总运营成本最小化”,大致由四部分构成:
- 能耗成本:根据速度-能耗模型计算各路段的能量消耗,再乘电价;
- 充电成本:动态无线充电的计量计费通常按电量计算,这部分与充电量成正比;
- 时间成本:车辆早到或晚到会产生惩罚,尤其是晚到,会直接影响乘客或货物交接;
- 电池退化成本(可选):如果充放电频繁,电池寿命缩短,这个成本也不能忽视。
写成公式大概是:
min Z = Σ_c(C_e·E_cons + C_c·E_charge + C_t·T_total + C_bat·ΔSOC)
其中E_cons是总能耗,E_charge是总充电量,T_total是总运行时间,ΔSOC是电池SOC波动幅度。
节能能耗模型我用的是一个二次函数拟合,基础是道路行驶阻力公式,再结合电机效率:
E_cons(v) = a·v² + b·v + c
a、b、c是通过实际车辆数据拟合的系数。这个模型虽然简单,但用来做路线和速度联合优化的规划层决策,精度完全够用。
1.3 关键约束:充电、时间窗与SOC边界
模型里最容易出问题的约束有三个:
第一个是SOC动态约束。车辆进入路段时的电量加上充电量、减去行驶能耗,必须等于到达下一个节点时的电量。写成迭代式就是:
SOC_{k+1} = SOC_k + (P_c·L_c/v - E_cons(v))/B_cap
注意这里的L_c/v就是车辆在充电线圈上的通行时间。如果路段没有充电功能,P_c = 0,那么SOC只会下降。加上这层约束后,车辆在整个路网中的电量轨迹就是一条“下降—回升—再下降”的曲线,这条曲线必须始终高于SOC_min,且不超过SOC_max。
第二个是时间窗约束。每辆车从起点出发,必须在约定时间窗[d_i, u_i]内到达终点或途经的关键节点。时间计算比较简单:
t_{k+1} = t_k + L_k / v_k
但把时间约束和速度变量放在一起后,可行域会因为“充电路段不能跑太快(否则充不够电)”而缩小,这些耦合关系是求解时的主要难点。
第三个是同路段多车冲突约束。如果多辆车同时经过同一条充电路段,受供电侧容量限制,可能无法同时满载充电。这个问题严格说已经带有车队协同调度的色彩。我在项目中先用一个简化处理:设定每条充电路段同一时刻最多服务K辆车,超过则其中一部分车辆只能以较低功率充电。这个约束用时间窗重叠判断来近似,虽然不够精细,但能有效防止出现“所有车都在同一条充电路排队”的极端解。
2. 随机搜索优化方法选型思路
模型建完之后,下一步就是求解。老实说,这个问题的精确解模型不是不能建,但在中等规模路网下,求解时间完全不可接受,所以我一开始就锁定了元启发式方向,最终选用了随机搜索优化方法作为主算法框架。
2.1 为什么不用传统精确算法
从问题性质看,这本质上是带时间窗的车辆路径问题(VRPTW)的变种,再加上连续速度变量,是一个典型的混合整数非线性规划(MINLP)。当车辆数和节点数增加到一定规模后,精确算法的分支定界树会迅速膨胀。
我测试过一个去掉无线充电约束的简化版——纯最短路径+速度优化——用线性化方式求解,即使这样,在30个节点、5辆车的规模下,单次求解也要几十秒到几分钟。真实场景里车辆调度需要分钟级甚至秒级响应,等不起。
随机搜索这类元启发式方法虽不能保证全局最优,但好处是:
- 不依赖问题解析性质,非凸、非线性、离散连续混合都能处理;
- 容易把各类复杂约束(充电功率、SOC、时间窗)以惩罚函数形式加进去;
- 代码量可控,改造成本低;
- 适合和仿真模型联动,因为每次迭代只需正向评价目标函数。
2.2 随机搜索的核心逻辑与参数设计
标题里说的“随机搜索优化方法”,在代码实现上我采用的是多起点随机搜索(Multi-start Random Search)增强版本。它的核心思路很简单:在一个可行域内反复随机生成初始解,再通过局部邻域扰动去逼近更优解。
具体步骤:
- 随机生成一组满足基本约束的初始解(路线序列+速度向量);
- 计算目标函数值,保存当前最优;
- 对当前最优解做邻域扰动:随机交换路线中的两个节点,或随机调整某一路段的速度;
- 用Metropolis准则(来自模拟退火的思想)判断是否接受新解;
- 迭代一定次数后重新随机生成初始解,开始新一轮搜索;
- 达到最大评价次数后输出历史最优解。
核心参数有三个:最大迭代次数MaxIter、扰动强度NeiScale、温度衰减系数Alpha。这三个参数直接决定搜索的“广度”和“深度”。
2.3 与遗传算法、粒子群算法的对比
在项目中期,我同时实现了简单遗传算法(GA)和粒子群算法(PSO)作为对照。三者的对比结果让我对随机搜索有了更务实的认知:
| 方法 | 实现复杂度 | 全局搜索能力 | 收敛速度 | 参数敏感性 | 最终解质量(测试算例) |
|---|---|---|---|---|---|
| 随机搜索(多起点) | 低 | 中 | 中 | 低 | 基准 |
| 遗传算法 | 中高 | 强 | 较慢 | 高(交叉率、变异率) | 比随机搜索高3%~8% |
| 粒子群 | 中 | 较强 | 快 | 中(惯性权重) | 比随机搜索高2%~5% |
随机搜索的差距没有想象中那么大,尤其是考虑到它的调试成本低、几乎不依赖专家经验。工程上有一个很关键的逻辑:如果你的约束是扭曲的、目标函数是粗糙拟合的,那么解的精度提升3%实际意义并不大。先把模型跑通、跑可信,比一味追求最优解更重要。
3. Matlab代码实现:核心模块拆解
Matlab之所以适合这类项目,是因为它处理矩阵运算、数组索引、绘图和调试都很方便。整个项目代码量不大,核心部分约500行,分成四个模块:主程序(流程控制)、随机搜索核心、目标评价、结果可视化。
3.1 整体程序框架
主程序的工作流很清晰,打开Matlab后直接运行main.m即可看到完整结果:
- 加载路网数据,包括节点坐标、路段信息、充电线圈参数;
- 设置车辆参数:电池容量、初始电量、起终点、时间窗;
- 初始化随机搜索参数;
- 调用随机搜索函数优化路线和速度;
- 输出最优路线、最优速度序列、各车辆SOC曲线;
- 绘制路网、充电位置、SOC变化曲线和收敛曲线。
加载路网数据这块,我建议直接把路网定义成结构体数组,这样比用cell更高效,也方便后续索引:
% 路网定义示例(局部) links(1).from = 1; links(1).to = 2; links(1).length = 1200; % 路段长度,单位m links(1).charge = 1; % 是否有无线充电 links(1).P_charge = 30; % 充电功率 kW links(1).L_coil = 200; % 线圈总长度 m links(1).v_min = 20; % 最低限速 km/h links(1).v_max = 60; % 最高限速 km/h用结构体数组的另一个好处是,之后如果要用表(table)做统计分析,一行table(links)就能转过去。
3.2 随机搜索算法实现要点
核心的随机搜索函数我用了一个“双层循环+多样本重启”结构。内层循环对当前最优解做邻域扰动,外层循环负责重启。接受准则采用模拟退火的Metropolis准则,这个细节很关键:
function [best_sol, best_cost] = randomSearch(prob, params) % prob: 问题结构体,包含路网、车辆、参数配置 % params: 算法参数,包括MaxIter, NeiScale, Alpha等 best_cost = inf; best_sol = []; for restart = 1:params.numRestart % 1. 生成随机初始解 cur_sol = initRandomSolution(prob); cur_cost = evaluateFitness(cur_sol, prob); if cur_cost < best_cost best_cost = cur_cost; best_sol = cur_sol; end T0 = 100; % 初始温度 T = T0; for iter = 1:params.MaxIter % 2. 邻域扰动:随机扰动路线或速度 new_sol = neighborPerturb(cur_sol, prob, params.NeiScale); % 3. 可行性修正 new_sol = feasibilityRepair(new_sol, prob); new_cost = evaluateFitness(new_sol, prob); % 4. Metropolis接受准则 delta = new_cost - cur_cost; if delta < 0 || rand < exp(-delta / T) cur_sol = new_sol; cur_cost = new_cost; end % 5. 更新全局最优 if cur_cost < best_cost best_cost = cur_cost; best_sol = cur_sol; end % 6. 温度衰减 T = params.Alpha * T; end end end这个框架的可扩展性很好。如果后面想升级成遗传算法,只需要把initRandomSolution改成种群初始化,把neighborPerturb改成交叉和变异,外层框架完全不用动。
这里有个细节经验:温度T0的初始值和目标函数数量级要匹配。我一开始把T0设成1000,但目标函数值在10^5量级,导致算法几乎变成纯随机游走。后来把T0设为目标函数初始值的10%~20%,收敛速度明显改善。
3.3 速度分配与充电约束的处理
速度分配是这个问题中最“非线性”的部分,也是很多Matlab代码最糊弄的部分。很多网上的代码直接把速度设成常数,完全没利用速度的调节能力,模型就废掉了。
我用的速度编码方式是:每条路段一个速度值,存放在向量v中,v(i)对应车辆在第i条路段上的平均速度。邻域扰动时,选择一个随机路段,在[v_min, v_max]范围内做小幅增减:
function new_v = velocityPerturb(v, links, idx, scale) % 对第idx条路段的速度做一个高斯扰动 v_min = links(idx).v_min; v_max = links(idx).v_max; delta = scale * (v_max - v_min) * randn(); new_v = v; new_v(idx) = max(v_min, min(v_max, v(idx) + delta)); end对每一辆车的速度序列,评价函数中会同步计算SOC轨迹。SOC的计算需要注意单位统一。我一开始在“车速km/h、路段长度m、充电功率kW”之间来回混用,结果算出来的SOC一会儿是负的,一会儿爆表,调试了很久。后来强制统一成国际单位制:速度用m/s,距离用m,功率用kW,能耗用kWh,问题立刻清晰了。
SOC计算和约束判断的核心逻辑:
function [soc_traj, feasible] = calSOC(sol, prob) % 解算SOC轨迹,并检查是否越界 soc = prob.vehicle.SOC0; soc_traj = zeros(length(sol.route), 1); feasible = true; for k = 1:length(sol.route) link_id = sol.route(k); v = sol.v(k); % 单位 m/s L = prob.links(link_id).length; % 行驶能耗 (kWh),基于速度二次能耗模型 E_cons = (0.008 * v^2 - 0.3 * v + 8) * (L / 1000) / 3.6; % 充电量 (kWh) if prob.links(link_id).charge == 1 t_pass = prob.links(link_id).L_coil / v; E_charge = prob.links(link_id).P_charge * t_pass / 3600; else E_charge = 0; end soc = soc + (E_charge - E_cons) / prob.vehicle.B_cap; if soc < prob.vehicle.SOC_min || soc > prob.vehicle.SOC_max feasible = false; break; end soc_traj(k) = soc; end end运行一次全局评价是矢量化的,不需要循环,直接在矩阵层面算能耗和充电量,速度能提升几十倍。这在小规模路网里体感不明显,但车辆数到20辆以上时就非常重要了。
3.4 关键参数与调参经验
随机搜索对参数不敏感是相对的,以下是几组我实测下来的经验值,在大多数路网规模下都能稳定工作:
- v_min = 20 km/h, v_max = 60 km/h(园区接驳车场景);
- 充电功率P_charge = 25~40 kW(取决于地面线圈和车载接收端规格);
- 电池容量B_cap = 40~80 kWh;
- SOC运行区间:20%~95%,低于20%强制进入惩罚;
- 时间窗惩罚系数:每超时1分钟,在总成本中加50~100元;
- 重启次数numRestart = 10,每次内层迭代MaxIter = 3000;
- 邻域扰动尺度NeiScale = 0.2(速度扰动约为速度范围的20%);
- 温度衰减系数Alpha = 0.95。
这套参数在30个节点、5辆车的算例下,单次完整优化耗时约40秒左右,结果稳定。如果发现收敛曲线跳得太厉害,可以把Alpha调到0.98,增加低温段精细搜索。
4. 实验设计与结果对比分析
模型和代码都跑通后,真正有价值的工作是验证它的效果。我设计了一组对照实验,用一个小型园区的路网数据,重点回答三个问题:随机搜索能不能稳定收敛?联合优化到底比分开优化好多少?和遗传算法比差距多大?
4.1 算例设置与数据准备
路网我设计成环形加支路的拓扑,共25个节点、32条路段,其中8条路段布置了无线充电线圈。每辆车的起点、终点、时间窗都做了一定差异化,避免出现“对称解”干扰对比。
车辆参数设定为:电池容量50 kWh,初始电量80%(40 kWh),SOC下限20%(10 kWh),平均每百公里能耗约25 kWh,充电路段功率30 kW,线圈长度150 m(以30 km/h通过,耗时约18秒,可充电约0.15 kWh,相当于续航增加约0.6 km)。
看得出来,单次充电量并不大,但多辆车的路线如果穿过多个充电段,累计效果就很可观了。这个细节也是动态无线充电和静态充电站的本质区别:充电是“碎片化”的,需要路线整体配合。
4.2 收敛性与可行性验证
随机搜索的收敛曲线呈典型的阶梯状:前期下降快,后期趋于平稳。使用默认参数时,大约在1万次目标评价后收敛到稳定区间。多起点重启的关键作用在于,单次搜索很容易陷入局部极小,重启能显著提高最终解质量。
下表是在同一算例上运行5次随机搜索的结果:
| 运行次数 | 最优总成本(元) | 平均迭代次数 | 是否满足SOC约束 | SOC最低值 |
|---|---|---|---|---|
| 1 | 8346 | 9200 | 是 | 26.3% |
| 2 | 8510 | 8600 | 是 | 24.8% |
| 3 | 8265 | 10500 | 是 | 28.1% |
| 4 | 8398 | 9400 | 是 | 25.4% |
| 5 | 8472 | 8800 | 是 | 27.0% |
5次运行之间的极差约3%,在可接受范围内。这说明随机搜索的稳定性虽不如GA,但对于方案评估、可行性验证这类用途完全够用。
我还在调试阶段专门构造过极端算例:把所有充电路段放到同一条路线上,其余路线完全没有充电。结果模型给出的最优解明显会倾向把所有车辆都“挤”到那一条充电线路上,虽然SOC可行,但时间惩罚很大。这说明目标函数中的时间成本权重需要与充电收益平衡,否则会诱导“充电绕行”行为。
4.3 联合优化与分步优化的差距
衡量这个模型价值最直接的方式,是做一组消融对比:
- 方式1:联合优化(路线+速度同时优化);
- 方式2:固定速度(所有路段统一25 km/h),只优化路线;
- 方式3:最短路径优先,再优化速度。
实验结果清楚说明问题:
| 方式 | 总成本(元) | 总充电量(kWh) | 总能耗(kWh) | SOC低于30%的车辆数 |
|---|---|---|---|---|
| 联合优化 | 8265 | 15.2 | 146.8 | 0 |
| 固定速度+路线优化 | 9210 | 11.6 | 152.4 | 2 |
| 最短路径+速度优化 | 10480 | 8.9 | 158.2 | 4 |
联合优化比固定速度方案节约约10%的总成本,比“只选最短路径”的贪心方案节约约21%。这说明在动态无线充电场景下,路线选择不能只看距离,还要看充电资源分布和速度带来的能耗差异。
有意思的是,速度优化单独做时,效果并不明显(成本只降了4%),但放在联合优化里,速度变量的自由度能显著增加解的可行空间。打个比方:单纯优化路线是在一条固定轨道的“过山车”上调整乘坐舒适度,联合优化等于同时修改了“轨道”和“速度曲线”,很多原本无解的场景因此变得可行。
4.4 与遗传算法的质量对比
作为控制组,我还实现了标准遗传算法(二进制编码路线、实数编码速度),运行相同次数的目标评价。遗传算法的确能找到略好的解(约高6%),但有一个实际问题:参数多、调试时间成倍增加。而且遗传算法在处理“时间窗+SOC”这类强约束时,初始种群不好生成,大量个体在交叉后根本不可行,废掉的比例很高。
最终我的建议是:
- 如果你的首要目标是论文数据好看一点,用GA或PSO去磨细节;
- 如果你是做工程评估、方案预研,想快速得到一个可信结果并理清约束关系,随机搜索完全够用,它还能作为其他算法的“下界分”参考。
另外,随机搜索代码结构简单,非常适合作为后续算法升级的骨架——我在这个框架上换成粒子群核心只花了半天时间。
5. 常见问题与调试实录
这个项目我断断续续调试了两周,踩坑记录比想象中多。整理几个最具代表性的问题和排查思路,希望能帮你少走弯路。
5.1 随机搜索“不收敛”或“结果反复横跳”
如果收敛曲线波动很大,通常不是算法本身的问题,而是目标函数里有大量惩罚项在和真实目标打架。我之前把时间窗惩罚系数设得极高,结果求解器宁可让SOC掉到阈值以下,也要优先满足时间窗,这就导致SOC约束经常越界,惩罚值剧烈波动。
排查方法:把目标函数拆开,分别输出能耗成本、充电费用、时间惩罚、SOC惩罚,看哪一项波动最大,再针对性调整对应权重。还有一种常见原因是最优温度T0设置与目标函数值尺度不匹配,参照前面提到的“温度匹配原则”重设即可。
5.2 车辆SOC始终低于下限,怎么办
这类问题要分两类看。第一类是模型问题:你的路网充电资源从物理上就不足以支撑车辆跑完全程,这时候再厉害的算法也救不了,需要先放宽电池容量或增加充电路段。第二类是算法问题:随机生成的初始路线绕开了所有充电路段,并且邻域扰动太温和,始终没跳到有充电资源的区域。
解决办法是调整初始解生成策略。我把初始解设计成三类混合:纯随机、偏向充电密集路段的随机、以及基于静态最短路径的随机。这样能确保一定比例的初始解天然具备充电能力,搜索起点覆盖得更广。
5.3 时间窗和速度上限冲突
当车辆在充电路段上为了多充电而降速时,容易超过时间窗上限。这个冲突是模型本身固有的,不是bug。关键是搞清楚时间惩罚的“单位成本”设定,这取决于实际业务:如果晚到1分钟损失100元,那这个速度降得值不值,模型自己会算。
我在实验中发现,把时间窗的刚性硬约束改成软惩罚后,总成本反而下降。原因在于软约束给了搜索算法更大的可行域,让它能尝试一些“看似晚到但其实更省”的方案。工程上这就是“柔性时间窗”的概念,和很多外卖、物流调度里的做法一致。
5.4 Matlab运行速度太慢
如果路网规模变大,Matlab的for循环会变成瓶颈。我的经验是三个优化方向:
- 把路网参数、能耗系数、充电参数全部预计算成矩阵,评价函数用向量化实现;
- 随机搜索的重启之间没有依赖,可以用parfor做并行,我测试过4核并行,总耗时约等于原来的35%;
- 不要每次都评估整个目标函数的所有项,先算SOC约束检查,如果不可行直接跳过能耗成本计算,减少无效计算量。
5.5 如何验证结果的合理性
这个容易被忽略。我建议至少做三个“退化实验”:第一,把所有充电功率设为0,模型退化成一个纯电动汽车路径规划问题,结果应和VEV(纯电耗路径)模型一致;第二,把速度上限和下限设成同一个值,模型退化为纯路线优化问题;第三,把所有时间窗设成无限大,时间惩罚项应为0。
这三个测试能帮你快速定位模型写错了哪里。我在第一次做退化测试时发现SOC计算偏差了十几个百分点,就是因为能耗模型里少除了一个3.6——单位换算问题,不测试根本发现不了。
6. 项目扩展方向与个人体会
关于这个项目的后续,我个人觉得有几个方向值得探索。一个是把静态随机搜索升级成自适应随机搜索,让邻域扰动幅度在搜索过程中自动调整,前期大步探索、后期小步精修,和模拟退火的温度衰减呼应会更强。另一个是考虑充电负荷对电网的影响,如果大规模车队同时经过同一片充电区域,供电侧可能面临峰值负荷压力,这时候问题就变成了车辆调度与电网负荷的联合优化,模型的价值更大。
最后分享一段我在做这个项目时的感受。这套路线和速度联合优化框架,核心难点其实不在算法,而在建模时能不能把“充电能力受速度影响”这一点想透。一旦想透了,随机搜索、遗传算法、粒子群这些方法都只是工具箱里的不同螺丝刀而已。Matlab在这个项目里最让我满意的,是它把路网、SOC轨迹和收敛曲线的可视化做得非常直观——好几次方案优化前后的差距,不是数值对比出来的,而是直接看图一眼就看出来的。
如果这篇文章能帮你少走一点弯路,或者帮你把Matlab代码跑通,那我的目的就达到了。