资讯详情

资讯详情

蚁群算法(ACO)从TSP到三维避障:四类路径规划问题的建模与Python实现

简介这套ACO蚁群算法仿真资源围绕TSP、二维路径规划、三维路径规划与栅格地图避障四类典型场景提供完整的MATLAB实现方案。资源面向本硕博及科研人员适合算法学习、课程实验与毕业设计参考重点解决蚁群算法在组合优化与不同维度路径规划中的编程落地问题。压缩包共17个文件包含12个M脚本、3个TXT说明、1个MAT数据文件及1个AVI操作录像资源总大小仅1.16MB轻量易下载。其中M脚本按模块划分如TSP求解、二维/三维路径规划、栅格避障主程序及配套子函数MAT数据文件提供三维地形高程数据操作录像演示运行步骤与工程配置要点方便跟着视频快速复现结果。目前已有1531人学习浏览适合希望系统掌握ACO蚁群算法及路径规划仿真的读者下载使用。1. ACO蚁群算法能统一解决的四类路径问题从TSP到三维避障的建模视角把蚁群算法只当成 TSP 求解器的印象该刷新了。只要能把路径问题写成在带权图上找一条满足约束的最短通路ACO 就能套用——区别只在于节点怎么定义、代价怎么算、信息素怎么落。二维栅格路径规划把每个网格单元当节点三维路径规划把搜索空间离散成三维网格或球坐标采样点栅格地图避障规划则在每轮迭代后把障碍物膨胀区写入可通行性矩阵。本文沿着 TSP → 二维路径规划 → 三维路径规划 → 栅格地图避障这条线把状态转移、信息素更新、启发式函数如何改、参数怎么调拆开讲最后给出仿真可视化和操作视频的配套思路。适合做机器人路径规划、无人机路径规划算法仿真以及准备用 ACO 做毕业设计或竞赛的工程师。2. ACO算法核心框架与TSP基准实现2.1 状态转移与信息素更新公式ACO 的第一步是建立问题图。对 TSP 来说城市是节点边是两两之间的欧氏距离对路径规划来说栅格地图中的可通行网格是节点相邻网格之间的移动代价是边。所有变体的核心都围绕下面两个公式。蚂蚁从节点 i 转移到节点 j 的概率由状态转移规则决定P(i,j) [τ(i,j)^α] * [η(i,j)^β] / Σ([τ(i,k)^α] * [η(i,k)^β])其中 τ 是信息素浓度η 是启发式信息。TSP 中 η 1/d(i,j)路径规划中 η 1/(1 cost(i,j))cost 可以是欧氏距离、安全距离惩罚或爬坡代价。α 和 β 分别控制信息素与启发式的相对权重。每次迭代结束后全局或局部更新信息素τ(i,j) (1 - ρ) * τ(i,j) Σ(Δτ_k(i,j))其中 ρ 是挥发率Δτ_k 是第 k 只蚂蚁在本次迭代中走过这条边时留下的信息素增量。TSP 一般用蚁周模型Δτ Q / L_kL_k 是蚂蚁 k 走过的总长度路径规划中也可以沿用这个思想只是 L_k 变成路径总代价。信息素下界通常设为一个很小的正数避免算法停滞。2.2 用ACO求解TSP的最小Python实现下面是一个可运行的 ACO-TSP 实现不依赖额外库只用了 NumPy。我用它作为后面路径规划版本的基础骨架。import numpy as np def aco_tsp(cities, n_ants50, alpha1.0, beta3.0, rho0.1, q100, max_iter200): n len(cities) dist np.zeros((n, n)) for i in range(n): for j in range(n): dist[i, j] np.linalg.norm(cities[i] - cities[j]) tau np.ones((n, n)) / n # 初始信息素 best_path None best_len np.inf for it in range(max_iter): paths [] for _ in range(n_ants): visited [np.random.randint(n)] for _step in range(n - 1): i visited[-1] allowed [j for j in range(n) if j not in visited] probs [] for j in allowed: probs.append((tau[i, j] ** alpha) * ((1.0 / dist[i, j]) ** beta)) probs np.array(probs) probs probs / probs.sum() j np.random.choice(allowed, pprobs) visited.append(j) paths.append(visited) # 计算路径长度并更新信息素 new_tau (1 - rho) * tau for path in paths: length sum(dist[path[k], path[(k 1) % n]] for k in range(n)) if length best_len: best_len length best_path path.copy() for k in range(n): new_tau[path[k], path[(k 1) % n]] q / length tau new_tau return best_path, best_len这段代码的逻辑是先算城市间距离矩阵初始化均匀信息素。每只蚂蚁随机选起点按状态转移概率逐步选下一个城市。一轮结束后用本轮所有蚂蚁的路径长度做增量更新并且保留全局最优路径。参数上n_ants太少容易早熟太多单轮耗时高alpha过大会让信息素主导路径多样性下降beta过大则退化成贪心算法。常见的起步组合是 alpha1、beta3、rho0.1、q100。2.3 参数速查表参数作用TSP 常用范围路径规划注意点n_ants每轮蚂蚁数量20~100栅格地图大时建议 30~60过大内存占用高alpha信息素权重0.5~2.0动态障碍场景可调低到 0.5增强探索beta启发式权重2~5三维规划建议 2~3过高会导致路径贴障碍物rho信息素挥发率0.05~0.3动态避障需要 0.2~0.4 快速遗忘过期信息q信息素强度50~200与路径长度尺度相关需先测路径量级max_iter最大迭代数100~500栅格地图 200 轮基本收敛三维需 300这些参数不是固定值后面各章会针对具体问题给出调整理由。3. 从TSP到二维路径规划栅格地图建模与信息素设计3.1 栅格地图如何转化为图二维路径规划最常见的场景是机器人从起点到终点的避障路径。栅格地图由 0 和 1 组成的矩阵表示0 是可通行区域1 是障碍物。要把 ACO 放上去不能直接把每个像素当节点因为地图可能很大比如 100x100 就是一万个节点信息素矩阵会占用 10000x10000 的浮点数组内存爆炸。常见做法是只把可通行的栅格作为节点同时限制蚂蚁只能在相邻栅格移动即四邻域或八邻域。四邻域只允许上下左右路径平滑度略差但转角简单八邻域允许斜向移动路径更短但需要额外检查斜向穿过的两个相邻栅格是否都是可通行避免穿墙。栅格地图到图的转换就是把每个可通行栅格与其邻居建立边边的代价是欧氏距离如果是八邻域斜边则乘以 sqrt(2)。代码上我一般用一个字典来存邻接关系而不是构建完整邻接矩阵。信息素也可以只存边信息即用一个 dict 记录每条边的 tau 值。这样地图大一些也能跑。3.2 二维路径规划的ACO实现二维路径规划的 ACO 与 TSP 有三点不同第一蚂蚁从起点出发走到终点即结束不需要回到原点第二路径长度是实际走过的栅格之间的欧氏距离累加不再是一个闭环第三启发式信息要结合终点距离即 η 1 / (当前栅格到终点的欧氏距离 0.01)这样蚂蚁倾向朝终点方向移动。下面是一个在栅格地图上使用 ACO 找最短路径的 Python 示例import numpy as np import heapq def neighbors(pos, grid): 八邻域排除障碍物和越界 x, y pos dirs [(1,0),(-1,0),(0,1),(0,-1),(1,1),(1,-1),(-1,1),(-1,-1)] res [] for dx, dy in dirs: nx, ny xdx, ydy if 0 nx grid.shape[0] and 0 ny grid.shape[1] and grid[nx, ny] 0: # 斜穿检测确保相邻两格也通行 if dx ! 0 and dy ! 0: if grid[xdx, y] 1 or grid[x, ydy] 1: continue res.append((nx, ny)) return res def aco_grid(grid, start, goal, n_ants30, alpha1.2, beta2.5, rho0.2, q1.0, max_iter200): rows, cols grid.shape # 只记录访问过边的信息素用字典避免大矩阵 tau {} def get_tau(a, b): key (a, b) if a b else (b, a) return tau.get(key, 0.2) def set_tau(a, b, val): key (a, b) if a b else (b, a) tau[key] val best_path None best_cost np.inf for it in range(max_iter): for _ in range(n_ants): current start path [current] visited set() while current ! goal and len(path) rows * cols: visited.add(current) cand neighbors(current, grid) cand [c for c in cand if c not in visited] if not cand: break # 计算转移概率 probs [] for nxt in cand: d np.linalg.norm(np.array(nxt) - np.array(goal)) inv 1.0 / (d 0.5) # 启发式 t get_tau(current, nxt) probs.append((t ** alpha) * (inv ** beta)) probs np.array(probs) probs probs / probs.sum() nxt cand[np.random.choice(len(cand), pprobs)] path.append(nxt) current nxt if current ! goal: continue # 计算路径代价 cost 0.0 for i in range(len(path)-1): dx path[i1][0] - path[i][0] dy path[i1][1] - path[i][1] cost 1.0 if (dx 0 or dy 0) else np.sqrt(2) if cost best_cost: best_cost cost best_path path[:] # 信息素全局更新只对本次迭代产生的路径更新 for _ in range(n_ants): # 这里为了简化用上一轮的历史路径更新实际可保留每只蚂蚁的路径 pass # 简化更新每轮只对全局最优路径增强信息素 if best_path: delta q / best_cost for i in range(len(best_path)-1): a, b best_path[i], best_path[i1] set_tau(a, b, (1-rho)*get_tau(a, b) delta) return best_path, best_cost这个实现的逻辑是每只蚂蚁从起点出发每一步在可通行的邻居中按照信息素和启发式信息采样直到终点。为了避免蚂蚁绕圈子用visited集合禁止重复访问同一栅格。信息素采用全局最优更新策略类似精英蚂蚁系统收敛更快但要注意多样性所以rho用了 0.2。参数上需要注意q在二维栅格中通常设为 1.0因为路径成本在几十到几百之间如果q太大信息素浓度会迅速饱和后期蚂蚁全部走同一条路beta设为 2.5 可以让蚂蚁偏向终点方向但太高会忽略信息素导致路径呈现明显Z形。实际调参时先固定alpha1、beta2看路径是否绕远再逐步加大beta。3.3 二维路径规划与TSP的关键差异TSP 中蚂蚁必须遍历所有城市而路径规划中蚂蚁一旦到达终点就停止所以路径长度的计算是开放式的。这带来一个问题如果起点和终点之间被障碍物完全隔断蚂蚁可能永远到不了终点。因此在地图构建阶段必须先用广度优先搜索或 A* 算法检测连通性。我在项目中会在 ACO 前先跑一次 BFS如果起点和终点不连通直接返回失败不再浪费迭代。另一个差异是信息素的初始化。TSP 中初始化均匀即可但路径规划中信息素可以初始化得更有方向感把起点到终点的直线路径通过的栅格上的信息素设高一点。这样第一轮蚂蚁就有一个好的引导收敛速度快很多。操作上我一般先用 Bresenham 直线算法生成一条直线如果直线经过障碍物就把直线附近的栅格信息素设为 0.3否则保持默认 0.2。这个技巧在动态避障场景中尤其有效能大幅减少前期无效搜索。4. 三维路径规划与栅格地图避障的扩展4.1 三维空间节点生成与代价函数三维路径规划比二维难的不是算法而是节点生成方式。如果直接使用三维体素网格比如 50x50x50可通行节点最多 125000 个邻接边和信息素都会变得难以处理。工程上常用的两种方案方案一是将三维空间在高度方向分层每一层当作二维栅格层间只允许垂直或斜向移动到相邻层。这种做法的优点是能复用二维代码缺点是路径高度变化不够灵活。方案二是采用球坐标采样。以起点为原点将方位角、俯仰角、距离分别离散生成一组候选点。蚂蚁从当前点按角度步长选择下一个点避开禁飞区或障碍物区域。这个方案适合无人机路径规划算法因为无人机的航向角和俯仰角有物理限制。代价函数在三维中除了距离还要加入高度惩罚。比如cost(i,j) distance(i,j) w_h * |height_j - cruising_altitude|其中w_h是高度偏离权重。如果做巡线任务让路径尽量保持巡航高度如果需要地形遮挡则相反。三维路径规划的信息素更新与二维相同但启发式信息要额外考虑当前点到终点的三维欧氏距离。4.2 动态避障栅格地图的实时更新策略栅格地图避障规划发生在动态环境中障碍物位置会在运行中变化。ACO 天然适合边规划边更新因为信息素是逐步挥发的过期路径会慢慢变淡。但需要注意策略不能等所有蚂蚁走完一轮再更新。常见的做法是在每次迭代开始前重新读取一次障碍物栅格地图。如果障碍物移动速度不快可以直接用新地图重新判断邻居节点。更精细的做法是引入临时信息素惩罚当障碍物出现在某栅格时把与该栅格相连的边的信息素立即置零并且额外施加一个短期禁止信号。我用一个二维数组forbidden记录障碍物出现后的剩余迭代数每次更新时递减避免蚂蚁立刻重新探索刚被占用的区域。下面是一段动态避障过程中的关键更新逻辑obstacle_map get_latest_map() # 外部输入比如激光雷达转出的栅格 for pos in obstacle_map.changed_cells: if obstacle_map.cell_state[pos] 1: forbidden[pos] dynamic_forbid_iter # 例如 5 轮内不可选 # 同时降低周边信息素 for nb in neighbors(pos, grid): set_tau(pos, nb, 0.05) else: # 障碍物清除后恢复可通行信息素逐步由蚂蚁重建 for nb in neighbors(pos, grid): set_tau(pos, nb, initial_tau)这段代码的核心是forbidden计数器和信息素重置。注意动态避障时rho要调大否则信息素残留会让蚂蚁反复尝试旧路径。我一般设rho0.3把旧信息素快速挥发掉同时降低alpha到 0.8让启发式信息占主导这样蚂蚁能更快响应环境变化。4.3 三维与避障共用的参数调整参数二维静态三维静态二维动态避障n_ants305040alpha1.21.00.8beta2.52.03.0rho0.20.150.3max_iter200300150三维空间的搜索空间大蚂蚁数量需要增加同时三维启发式信息中的距离是三维欧氏距离数值更大beta不用太高否则会过于向终点直线倾斜而无法避开山体。动态避障场景中max_iter可以减半因为每轮迭代都要重新感知环境迭代次数再多也会受障碍物变化限制。建议在动态环境里把 ACO 当作局部路径规划器每隔 2~3 秒重新规划一次每次只执行前几步路径类似模型预测控制的做法。5. 仿真验证与代码操作视频的配套技巧5.1 收敛性判断与算法终止条件不要固定最大迭代次数就算完。我通常保存每一轮的全局最优代价绘制收敛曲线。当连续 20 轮最优值不再变化时就提前终止节省计算时间。在三维规划中收敛轮数会明显多于二维波动也大可以用滑动窗口平均来判断。如果最优值在 100 轮内上下跳动超过 10%说明beta太小或者rho太大需要调整。5.2 可视化把路径画在栅格地图和三维坐标上二维栅格地图用 matplotlib 的imshow画把障碍物标记为黑色路径用红色折线叠加。三维场景用mplot3d的plot画同时把障碍物用voxels显示。注意在操作视频中最好把每轮的信息素浓度也动态显示出来——用imshow的颜色深浅表示信息素强度这样观众能直观看到信息素如何从均匀分布逐渐聚合到最优路径上。动态避障场景则可以显示实时更新的障碍物地图并标注当前蚂蚁位置。5.3 如何给代码操作视频做分章节演示代码操作视频的作用是让人能照着跑通所以章节要和博文一一对应。我建议视频拆成四段第一段演示 TSP 运行结果只改城市数量和数据文件第二段演示二维栅格地图显示如何读取矩阵文件、设置起点终点第三段演示三维规划重点展示节点生成代码和代价函数修改处第四段演示动态避障把地图更新函数单独摘出来。每段 5~8 分钟配合屏幕上的代码高亮和参数讲解。视频里不需要把每一行都念出来把修改参数的入口和运行结果的变化说清楚即可。最后提醒一句动态避障场景中如果障碍物移动太快ACO 的规划频率跟不上就换成基于采样的 RRT*或者把 ACO 结果作为全局路径再叠加 DWA 局部避障这在 ROS2 路径规划里是更稳妥的组合。本文还有配套的精品资源点击获取
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →