最近实际跑了一组二维避障路径规划,把粒子群优化(PSO)和多起点 fminsearch 放在同一个目标函数下进行求解。
结果有点出乎预期:全局搜索的 PSO 最终沿地图边缘绕行,而基于 Nelder-Mead 的多起点 fminsearch 反而找到了一条更短、更平滑的避障路径。
下面结合路径结果、核心代码和收敛过程,看看两种方法为什么会出现这么大的差异。
问题设定
场景是一张 10×10 的二维地图,从起点 (0,0) 到终点 (10,10),中间布置4个圆形障碍物,并使用5个中间路径点描述最终路线,相当于优化10个坐标变量。
评价一条路径时,同时考虑三个因素:
目标值 = 1.0 × 路径长度 + 0.6 × 平滑性 + 50 × 避障惩罚
也就是说,一条好的路径不仅要避开障碍物,还需要尽量做到:
距离短、路径变化平滑、与障碍物保持安全距离。
先直接看两种算法最终规划出的路径。
图中:
- 灰色虚线:初始直线路径
- 蓝色实线:PSO路径
- 橙色实线:fminsearch路径
- 红色圆:障碍区域
- 左下角:起点
- 右上角:终点
两种结果的差别非常明显。
PSO最终选择了一条比较保守的路径:先沿地图左侧向上,再沿顶部移动到终点,整体形成比较明显的直角绕行。
而多起点 fminsearch 则从几个障碍物之间穿过,最终形成了一条更接近起终点直线、转折也更平缓的路径。
两条路径最终都没有发生碰撞,但路径质量差异很大。
实验配置
本次对比采用完全相同的地图、障碍物和目标函数。
- 地图范围:10×10
- 中间路径点:5个
- 优化变量:10维
- 障碍物:4个圆形障碍物
- PSO:60个粒子、90代
- 随机种子:20260512
- fminsearch:3个初始路径,多起点取优
- 每个fminsearch起点最多600次迭代
运行环境:
- Syslab:26.6.1.8422
- Julia:1.10.10
- TyGlobalOptimization:1.0.5
- TyOptimization:1.0.6
- TyPlot:1.0.65
后文中的运行时间仅代表本次测试环境,不作为跨平台性能结论。
目标函数是怎么实现的?
路径长度的计算比较直接,就是对相邻路径点之间的欧氏距离进行累加:
function path_length(path::Matrix{Float64})
total = 0.0
for i in 1:(size(path, 1) - 1)
total += norm(path[i + 1, :] .- path[i, :])
end
return total
end
平滑性则计算相邻路径段向量变化的平方和:
function path_smoothness(path::Matrix{Float64})
total = 0.0
for i in 2:(size(path, 1) - 1)
v1 = path[i, :] .- path[i - 1, :]
v2 = path[i + 1, :] .- path[i, :]
total += sum((v2 .- v1) .^ 2)
end
return total
end
这里并不是单纯计算转角,而是比较相邻路径段向量的变化。
因此,路径方向或者相邻段长度变化越剧烈,这一项通常就越大。像图1中PSO得到的直角绕行路径,就会产生比较大的平滑性代价。
避障部分则对每一条路径段进行采样。
当采样点与障碍物之间的净距离小于安全距离 0.35 时,会逐步增加惩罚;如果距离小于 0.05、已经接近实际碰撞,则进一步叠加较大的碰撞惩罚。
最终三项组合为:
objective = weights[:length] * len +
weights[:smoothness] * smooth +
weights[:obstacle] * obs_penalty
其中:
weights[:length] = 1.0weights[:smoothness] = 0.6weights[:obstacle] = 50
所以这个优化问题本质上是在三个目标之间寻找平衡:
路径不能太长,也不能转得太急,同时还必须避开障碍物。
为什么PSO是152,而fminsearch只有16.66?
本次运行结果如下:
| 算法 | 目标值 | 路径长度 | 平滑性 | 避障惩罚 | 碰撞 | 用时 | 评估次数 |
|---|---|---|---|---|---|---|---|
| PSO | 152.0 | 20.0 | 220.0 | 0 | 否 | 2.79 s | 5400 |
| fminsearch | 16.66 | 15.35 | 2.18 | 0.0001 | 否 | 1.62 s | 2636 |
先把PSO结果代回目标函数:
20 + 0.6 × 220 + 50 × 0 = 152
正好对应最终目标值:
152.0
其中单独平滑性一项就贡献了:
0.6 × 220 = 132
所以PSO目标值较高的主要原因,并不是它发生了碰撞,而是图1中的边界绕行既增加了路径长度,又产生了非常大的路径段变化。
再看fminsearch:
15.35 + 0.6 × 2.18 + 50 × 0.0001 ≈ 16.66
同样与最终结果基本一致。
两条路径都实现了无碰撞避障,真正拉开差距的是:
路径长度和平滑性。
PSO找到的是一条“安全但绕远、转折明显”的可行路径;而fminsearch得到的路径在障碍物之间穿行,更接近起终点直线,同时路径变化更加平缓。
PSO是怎么调用的?
PSO直接在路径点的有界空间中进行搜索。
lb = repeat(config[:bounds_min], config[:num_waypoints])
ub = repeat(config[:bounds_max], config[:num_waypoints])
options = pso_options(
iterations = config[:pso_iterations],
N = config[:pso_population],
seed = config[:pso_seed],
store_convergence = true,
draw_picture = false,
)
x, _, output = particleswarm(
objective,
lb,
ub,
(),
(),
options
)
这里主要参数为:
pso_iterations = 90pso_population = 60- 固定随机种子:20260512
因此一次完整运行大约进行:
60 × 90 = 5400
次目标函数评估。
PSO的优势在于不需要提前给出一条非常接近最优解的路径,粒子可以在整个搜索空间内进行探索。
但在这个10维路径点空间中,它既要找到无碰撞区域,又要继续同时优化长度和平滑性,搜索空间并不算简单。
fminsearch为什么叫“多起点”?
fminsearch本身使用的是 Nelder-Mead 无导数优化方法。
这个案例并没有只从一个初始路径开始搜索,而是提前构造了3组候选路径:
seed_paths = [
encode_straight_line(config),
flatten_waypoints([
0.8 2.0;
1.8 5.4;
3.8 8.8;
6.7 9.6;
8.9 9.8
]),
flatten_waypoints([
2.0 0.6;
4.4 1.2;
7.1 1.6;
8.9 4.6;
9.4 7.6
]),
]
其中一条是起点到终点的直线路径,另外两条则分别从障碍物不同方向绕行。
随后分别对3个初始路径执行fminsearch:
for seed in seed_paths
x0 = to_unconstrained(seed, config)
y, _, exitflag, output =
fminsearch(objective, x0, options)
# 记录当前结果,并与已有最优结果比较
if best_result === nothing ||
ranking_value < best_ranking
best_result = candidate
end
end
最终选择3次搜索中目标值最低的结果。
所以这里使用的实际上不是:
单次局部优化
而是:
多起点 + Nelder-Mead局部优化 + 结果取优
这种策略显著降低了单一起点落入较差搜索区域的风险。
收敛过程:PSO前期下降很快,但后续改善有限
再看两种算法的收敛过程。
图2采用对数纵轴。
原因是PSO初始阶段部分随机路径对应的目标值达到百万量级,如果使用普通线性坐标,后期几十到几百范围内的变化几乎都会被压缩到横轴附近,很难看清。
从收敛过程可以看到,PSO在前几百次目标函数评估中下降非常快。
这说明它很快完成了第一件重要的事情:
从大量质量较差甚至发生碰撞的随机路径中,找到一条能够成功避开障碍物的可行路径。
但是在目标值下降到大约152以后,后续改善已经非常有限。
在当前种群规模、迭代次数和初始化条件下,PSO较早出现搜索停滞,最终停留在图1中那条沿地图边缘绕行的解附近。
多起点fminsearch的初始路径本身质量就明显更高,后续通过Nelder-Mead继续调整路径点,目标值逐渐降低,最终稳定在约:
16.66
附近。
因此,两种算法的差异不仅体现在最终路径形状上,也能从收敛过程看到。
从综合指标来看:
- 两种算法最终都没有发生碰撞;
- fminsearch路径长度约为15.35;
- PSO路径长度为20;
- fminsearch路径相比PSO缩短约23%;
- fminsearch的平滑性明显更好;
- 本次测试中fminsearch运行时间也更短。
PSO最终避障惩罚为0,而fminsearch约为0.0001。
这个数值已经非常小,最终碰撞判断同样为“否”。
之所以还有极小的避障惩罚,是因为fminsearch的路线更加靠近部分障碍物边界。
这也体现出路径规划中的一个典型权衡:
距离障碍物更远通常更安全,但同时可能意味着更长的绕行距离;想进一步缩短路径,就可能需要更加接近障碍边界。
为什么多起点fminsearch这次表现更好?
看到这个结果,很容易产生一个疑问:
PSO属于全局智能优化方法,为什么反而明显输给了局部优化?
关键并不在“全局”或者“局部”这两个标签,而在于具体的问题和算法配置。
1. 这个问题只有10维
5个中间路径点,每个路径点包含x、y两个坐标,因此优化变量一共10维。
这并不是一个特别高维的问题。
除避障惩罚区域外,路径长度和平滑性也具有比较明确的局部结构,所以只要初始路径质量较好,Nelder-Mead可以继续高效调整路径点。
2. fminsearch获得了3个有针对性的初始路径
本次fminsearch并不是从一个完全随机的初始解开始。
除了直线路径,还提前给出了两条从不同方向绕开障碍物的候选路径。
因此,多起点策略实际上已经承担了一部分全局探索工作。
换句话说,本次比较并不是:
一次局部搜索 vs 一次全局搜索
而更接近:
多起点局部搜索 vs 固定搜索预算下的PSO
3. PSO的搜索预算有限
本次PSO只有:
60个粒子 × 90代
对于10维连续路径点空间来说,这个搜索量并不算特别大。
障碍物又让搜索空间中存在大量高惩罚区域。
PSO首先需要解决:
找到一条不会撞障碍的路径
然后才有机会进一步优化:
路径长度和路径平滑性
从本次结果看,它很好地完成了第一件事,但没有继续找到质量更高的短路径。
fminsearch如何处理路径点边界?
还有一个实现细节值得注意。
路径点坐标必须限制在:
[0,10]
范围内。
但fminsearch本身属于无约束优化算法,因此案例通过sigmoid/logit变量变换,将有界路径坐标映射到无约束空间。
有界变量转换为无约束变量:
ratio =
(bounded_vars[i] - lb[i]) /
(ub[i] - lb[i])
y[i] = logit(ratio)
优化完成后再转换回有界空间:
x[i] =
lb[i] +
(ub[i] - lb[i]) *
sigmoid(unbounded_vars[i])
这样Nelder-Mead仍然可以在无约束空间中正常搜索,而最终得到的路径点会始终落在地图允许的范围内。
这是处理:
无约束优化算法 + 箱约束变量
时一个比较实用的思路。
这个结果能说明PSO不如fminsearch吗?
不能。
需要特别说明的是,本实验并不是严格意义上的 PSO与Nelder-Mead算法能力基准测试。
本次fminsearch使用了3个设计好的初始路径进行多起点搜索,而PSO采用的是60粒子、90代的固定搜索预算。
因此,这里真正比较的是:
当前配置下的PSO
和:
多起点fminsearch策略
在这个具体二维路径规划问题上的实际效果。
如果进一步:
- 增加PSO粒子数量;
- 增加迭代次数;
- 修改惯性权重和学习参数;
- 优化PSO初始种群;
最终结果完全可能发生变化。
同样,fminsearch本身也明显依赖初始路径。
如果没有两条针对障碍分布设计的候选路径,仅从一条质量较差的初始路径开始,结果也可能发生变化。
所以这个实验更适合说明:
算法效果必须结合具体问题、初始化方式、搜索预算、目标函数和约束处理方式来看,而不能只凭“全局算法”或者“局部算法”的标签判断优劣。
还能怎么继续改?
这个案例还可以继续进行几组比较。
首先,可以增加PSO的粒子数量和迭代次数,观察它是否能够摆脱当前的边界绕行解。
其次,可以使用多个不同随机种子重复运行PSO,比较最终目标值的均值、最好结果和波动范围,而不是只观察一次随机实验。
还可以修改目标函数中的三项权重:
路径长度 / 平滑性 / 避障惩罚
观察路径形状如何变化。
如果希望进一步接近真实无人车或移动机器人,还可以加入:
- 最小转弯半径;
- 运动学约束;
- 动态障碍;
- 速度和能耗限制。
这样可以逐渐从二维几何避障过渡到更接近实际无人系统的路径规划问题。
小结
在本次10维二维避障问题中,PSO能够比较快地找到无碰撞可行路径,但在当前60粒子、90代的搜索预算下,最终停留在一条沿地图边缘绕行的路径附近。
多起点fminsearch则利用3个初始路径分别进行Nelder-Mead优化,最终在障碍物之间找到了一条距离更短、变化更加平滑的路线。
两种方法最终都实现了避障,真正拉开目标函数差距的主要是:
路径长度和平滑性,而不是碰撞惩罚。
这个结果也说明,在实际优化问题中,算法最终表现不仅取决于“全局”还是“局部”,还与初始解、搜索预算、目标函数设计和约束处理方式密切相关。
完整代码、案例说明和运行结果可以在这里获取:

2668

被折叠的 条评论
为什么被折叠?



