☰
A*与DWA融合:轮式机器人路径规划实战解析
2026/10/22 10:52:27 网站建设 项目流程

简介:这是一份基于Python实现A算法与DWA动态窗口法融合的轮式机器人路径规划项目,面向毕业设计、课程设计及机器人导航方向的开发者与学习者。压缩包共51个文件、约137KB,内容涵盖Python源码、项目文档、算法示意图,以及Gazebo/ROS仿真相关的launch、rviz、world等配置文件;Python源码用于算法核心实现,示意图便于理解算法流程,仿真配置支持在虚拟环境中调试验证,可帮助读者贯通全局路径规划与局部动态避障的完整链路。核心代码分别实现A全局路径搜索与DWA动态窗口法,运行时可结合鼠标交互设定起始点、目标点与障碍物,在静态与动态环境中完成路径规划与实时避障演示。项目另附有清晰的控制指令说明和模块文件划分,便于二次开发与功能扩展。已有281人参与学习浏览,适合希望借鉴完整工程结构、深入掌握路径规划算法协同机制的Python开发者参考。

1. A*与DWA组合方案:轮式机器人路径规划为什么需要两条路径

一个轮式机器人从起点到终点,单靠A算法会给你一条在栅格地图上“看起来最短”的折线,但机器人真按这条线走,既没考虑转弯半径,也躲不开行进中冒出来的障碍物;单靠DWA算法倒是能实时避障,但它只盯着眼前几秒的窗口,复杂环境里容易原地兜圈。所以工程上最常见的做法,是把A的全局路径规划和DWA的局部路径规划串成一条链路,让全局路径只管大方向,DWA管每一步怎么走。这套基于Python的轮式机器人路径规划毕业项目,涵盖了栅格地图构建、A*全局寻路、DWA动态窗口采样、轨迹评价和仿真可视化,源码、文档、示意图齐全,能直接跑出机器人绕障到终点的完整过程。适合正在做导航类课设或毕设的学生,也适合想快速上手“全局规划+局部规划”这套经典架构的开发者。

2. A*全局路径规划:栅格地图上的最短路到底怎么算出来

2.1 全局规划为什么选A*而不是无脑BFS或Dijkstra

先把结论放在前面:在栅格地图这种离散状态空间里,A找到的不是“一条可行路”,而是在当前代价定义下的最优路。它和Dijkstra的差别在引入启发函数h(n),搜索方向会被引导到终点一侧,open集规模明显更小;和RRT这类采样算法的差别则在于RRT擅长高维连续空间,在二维栅格地图上反而要处理碰撞检测和路径平滑,效率并不占优。对轮式机器人这个场景,A的输出恰好是连续栅格序列,后续转全局路径点非常直接。

A的决策核心是f(n)=g(n)+h(n)。g(n)是从起点到当前栅格n的实际代价,h(n)是从n到终点的估计代价。只要h(n)不大于真实代价,A就保证能搜到最优解,这也是搜索最优性的前提。我一般会把g(n)的代价分成直线代价和对角代价,相邻栅格直走代价1.0,对角走代价约1.414,和栅格距离保持一致,这样路径长度统计也更接近真实里程。

如果一上来就追求复杂变体,比如JPS跳点搜索或者带权重A*,调参成本会陡然上升。这个毕业项目方案里先用最朴素的A把链路打通,后面要优化再替换搜索核心也不迟。A跑出来的路径是一串栅格索引,直接传给后续模块前还要经过坐标转换和抽稀,这两步放在2.2和4.2里展开。

2.2 地图准备:栅格表示、障碍物膨胀与坐标换算

所有路径规划都建立在地图上。仿真里地图不需要建模成图像,一张二维数组就够了。0表示可通行,1表示障碍物。但直接拿原始障碍物格去搜索有个问题:机器人有半径,路径点离障碍物太近,DWA在那附近几乎找不到不撞车的轨迹。所以建图之后第一件事是膨胀。

class MapGrid: def __init__(self, width, height, resolution=0.1): self.width = width # 栅格列数 self.height = height # 栅格行数 self.resolution = resolution # 单个栅格代表的真实边长,单位米 self.grid = [[0] * width for _ in range(height)] def set_obstacle(self, x, y): if 0 <= x < self.width and 0 <= y < self.height: self.grid[y][x] = 1 def inflate(self, radius_cell): inflated = [[0] * self.width for _ in range(self.height)] for y in range(self.height): for x in range(self.width): if self.grid[y][x] == 1: for dy in range(-radius_cell, radius_cell + 1): for dx in range(-radius_cell, radius_cell + 1): nx, ny = x + dx, y + dy if 0 <= nx < self.width and 0 <= ny < self.height: inflated[ny][nx] = 1 self.grid = inflated def world_to_grid(self, wx, wy): return int(wx / self.resolution), int(wy / self.resolution) def grid_to_world(self, gx, gy): return (gx + 0.5) * self.resolution, (gy + 0.5) * self.resolution

inflate实现用的是最朴素的遍历,对小地图没问题,地图大了可以改用形态学膨胀,但效果完全一致。radius_cell是膨胀半径的栅格数,常见取法是ceil(机器人半径 / 分辨率)再加1格安全余量。比如机器人半径0.25米,分辨率0.1米,那就膨胀3格左右。grid_to_world取格子中心,是为了让机器人坐标和栅格坐标的换算误差控制在半格以内,A*搜出来的路径点转成世界坐标后,DWA跟踪时不会因为换算偏差对着障碍物冲。

用类封装地图还有一个好处:后续要在仿真里动态添加障碍物,只需要调用set_obstacle并重新膨胀,A*和DWA的接口都不需要改。地图这块是整个项目的地基,地基歪了,后面所有模块的表现都会跟着歪,但看起来像算法的问题。

2.3 A*搜索主流程:优先队列、邻域展开与路径回溯

核心搜索部分我用heapq实现,逻辑上分成open堆、closed集合、g_cost字典和came_from回溯表四块。

import heapq import math def neighbors(x, y, grid_map, mode='8'): h, w = grid_map.height, grid_map.width result = [] if mode == '4': dirs = [(1,0,1.0),(-1,0,1.0),(0,1,1.0),(0,-1,1.0)] else: dirs = [(1,0,1.0),(-1,0,1.0),(0,1,1.0),(0,-1,1.0), (1,1,math.sqrt(2)),(1,-1,math.sqrt(2)), (-1,1,math.sqrt(2)),(-1,-1,math.sqrt(2))] for dx, dy, cost in dirs: nx, ny = x + dx, y + dy if 0 <= nx < w and 0 <= ny < h and grid_map.grid[ny][nx] == 0: result.append((nx, ny, cost)) return result def heuristic(a, b, htype='euclidean'): dx, dy = abs(a[0]-b[0]), abs(a[1]-b[1]) if htype == 'manhattan': return dx + dy return math.hypot(dx, dy) def astar_plan(grid_map, start, goal, mode='8', htype='euclidean'): open_heap = [(0.0, 0.0, start[0], start[1])] came_from = {start: None} g_cost = {start: 0.0} closed = set() while open_heap: f, g, x, y = heapq.heappop(open_heap) node = (x, y) if node in closed: continue if node == goal: path = [] cur = goal while cur is not None: path.append(cur) cur = came_from[cur] return path[::-1] closed.add(node) for nx, ny, ncost in neighbors(x, y, grid_map, mode): child = (nx, ny) if child in closed: continue new_g = g + ncost if child not in g_cost or new_g < g_cost[child]: g_cost[child] = new_g h = heuristic(child, goal, htype) heapq.heappush(open_heap, (new_g + h, new_g, nx, ny)) came_from[child] = node return None

每次从堆里弹出的是f最小的节点,相当于自动按总代价排序。同一节点可能被多次压入堆,所以代码里加了node in closed的判断,避免重复扩展。回溯路径时从goal一路取came_from到start,最后反转得到从起点到终点的栅格序列。

有个容易忽略的点:障碍判断其实在neighbors里已经做了,主循环里的child in closed判断也够用,甚至不需要再查一次障碍。但我习惯保留这个判断,因为neighbors可能被别的模块复用,比如后续做重规划时要在地图上临时加禁行区。多一次判断比多一次返工便宜。

2.4 邻域与启发函数怎么配:四邻域还是八邻域

很多翻车案例都出在邻域和启发函数不匹配。四邻域就是上下左右,路径只能走横竖,曼哈顿距离自然是最佳启发;八邻域引入了对角移动,曼哈顿距离会高估真实代价,一旦高估,A*就退化成不完全搜索,搜出来的路径可能不是最短,我就见过路径明明能再短两格却绕了个小弯的情况。

匹配关系整理成一张表,方便毕设文档里直接引用:

邻域模式相邻代价推荐启发函数说明
四邻域1.0曼哈顿距离适用只能横竖移动的简单模型
八邻域直线1.0,对角1.414欧氏距离差速轮最常用,路径更短
八邻域直线1.0,对角1.0切比雪夫距离特殊情况才用,不推荐

调参时的判断依据是:如果A出来的路径有明显锯齿,先看邻域和对角代价是否设置一致;如果路径贴着膨胀边界走,那多数是膨胀半径偏小;如果搜出来的路径长度和直线距离差距过大,把启发函数改成可采纳的一致型,也就是欧氏距离配八邻域。这一整套做完,全局路径就到手了,但真正的机器人在这种路径上直接跑是跑不稳的,因为A压根不知道机器人的速度变化,接下来就该DWA接管。

3. DWA局部路径规划:动态窗口采样与三条评价规则

3.1 动态窗口到底动态在哪

DWA的核心不是预测轨迹本身,而是“动态窗口”这个速度搜索区间。差速轮机器人在当前时刻有一个实际速度(v0, w0),受电机加速度限制,下一时刻能达到的速度不是任意组合,而被限制在一个有限集合里:v只能在[v0-avdt, v0+avdt]内,w只能在[w0-awdt, w0+awdt]内,同时还要满足机器人自身的最大、最小速度限制。这个集合就是动态窗口。每个控制周期这个窗口都随当前速度变化,所以叫动态。

实际实现里我还会加一个安全约束:候选速度(v,w)对应的刹车距离不能超过前方最近障碍物距离,否则即使轨迹碰撞检测通过了,真跑起来也可能因为减速不及时撞上去。刹车距离用匀减速公式估算,和汽车安全车距是同一个思路。这个约束单独拎出来的好处是,评价函数里的距离项不用背负“保命”职责,可以更专注于路径质量。

3.2 轨迹预测:用圆弧模型推算候选速度的走向

对每一个候选(v,w),假设机器人在预测时间T内保持这个速度不变,用离散差分模型推演轨迹:

def simulate_trajectory(state, v, w, dt=0.1, predict_time=3.0): """state = [x, y, theta],v为线速度(m/s),w为角速度(rad/s)""" traj = [] x, y, theta = state steps = int(predict_time / dt) for _ in range(steps): x = x + v * math.cos(theta) * dt y = y + v * math.sin(theta) * dt theta = theta + w * dt traj.append((x, y, theta)) return traj

这个模型假设机器人在预测周期内速度恒定,实际控制器自然是每周期重新决策,所以预测只是评估候选速度“会走向哪里”,不是真让对方跑完这条轨迹。差速轮没有严格意义上的最小转弯半径,圆弧半径r=v/w,w接近0时近似直线,v接近0时近似原地旋转。dt越小轨迹越接近连续运动,但计算量也越大,仿真里0.1秒就够用。

3.3 评价函数三个子项:朝向、间距、速度怎么平衡

每条候选轨迹算完后,用三个子指标打分。第一个是朝向代价,衡量轨迹末端航向与目标点方向的夹角,夹角越小分越高;第二个是障碍物距离代价,取整条轨迹上每个点到最近障碍物距离的最小值,距离越远越安全;第三个是速度代价,鼓励机器人别在原地磨蹭。

def normalize_angle(angle): while angle > math.pi: angle -= 2 * math.pi while angle < -math.pi: angle += 2 * math.pi return angle def evaluate_trajectory(traj, local_goal, obstacles, safe_dist=0.5): end_x, end_y, end_theta = traj[-1] dx = local_goal[0] - end_x dy = local_goal[1] - end_y goal_angle = math.atan2(dy, dx) heading_diff = abs(normalize_angle(goal_angle - end_theta)) heading_score = math.pi - heading_diff min_dist = float('inf') for px, py, _ in traj: for ox, oy in obstacles: d = math.hypot(px - ox, py - oy) if d < min_dist: min_dist = d dist_score = min(min_dist, safe_dist) return heading_score, dist_score, 0.0

障碍物距离计算如果直接双重循环,在地图障碍物几百上千个点时就会卡顿,这一点后面专门讲,这里先记住:真正跑仿真时要把障碍物列表换成空间索引。safe_dist是一个截断阈值,距离超过它就不再继续加分,避免“离障碍物越远越好”导致机器人绕大圈。

三个子项量纲完全不同,朝向是弧度,距离是米,速度是米每秒。必须分别归一化到[0,1]再加权,否则距离项数值大,会把朝向项完全压死。归一化的做法是记录本周期所有候选轨迹的子项最大值,用Score = alphaheading/max_h + betadist/max_d + gamma*vel/max_v。速度项我在evaluate里占位0.0,实际使用时把abs(v)填进vel_score再参与归一化就好。

3.4 采样分辨率与评价权重的调参方向

采样窗口内的离散化也直接影响效果:

参数典型值影响
Δv(线速度分辨率)0.05 m/s过小则采样密但慢,过大则轨迹间差异明显
Δw(角速度分辨率)0.1 rad/s同上,调太粗会漏掉刚好能避障的速度
预测时间2~4 s太短只看眼前,太长机器人反应迟钝
控制周期dt0.1~0.2 s越小轨迹越精确,计算量线性增加

权重alpha、beta、gamma常见初始值在0.05、0.2、0.1附近。注意这是归一化之后的权重,不是原始值的权重。调参的血泪经验是:beta太大机器人会停着不动,alpha太大机器人会顶着障碍物转向,gamma太大机器人会拿安全换速度。每改一次权重,至少要跑同一张地图做对比,别凭肉眼感觉调。DWA本身参数不多,但每个参数都和环境尺寸强相关,换地图后参数基本要重调一轮。

4. A*与DWA融合:全局路径点如何驱动局部规划器

4.1 为什么要融合:全局规划看不见动态障碍,局部规划看不见全局

A规划的路径基于静态栅格地图,一旦真实环境里出现地图之外的新障碍,或者机器人因为打滑偏离轨迹,A不会自动修正;DWA虽然能实时避障,但它的有效感知范围就那么几米,目标点一旦设成最终终点,机器人可能被一个U形障碍困住,反复左右试探出不来。融合的本质是让A*做慢决策、DWA做快决策,前者决定大方向,后者决定小动作。

常见架构是:主循环里A只在需要时执行,DWA每个控制周期都执行。A的结果不直接当作轨迹,而是抽成一系列关键路径点,DWA以当前关键点为局部目标做评价。这种分层的做法在真实机器人导航里也是一样的思路,从仿真迁移到真实平台时不用推到重来。

4.2 局部目标点切换:从全局路径抽稀出关键点

A*出来的路径往往包含大量连续栅格点,全部喂给DWA会导致目标点频繁切换,机器人在每个栅格点附近都要重新调整朝向,跑起来一顿一顿。我一般会把路径抽稀,只保留方向发生明显变化的点:

def extract_key_points(path, angle_thresh=30.0): """抽稀全局路径,只保留夹角变化超过阈值的路径点""" if path is None or len(path) < 3: return path key_points = [path[0]] for i in range(1, len(path) - 1): v1 = (path[i][0] - path[i-1][0], path[i][1] - path[i-1][1]) v2 = (path[i+1][0] - path[i][0], path[i+1][1] - path[i][1]) angle = abs(normalize_angle(math.atan2( v1[0]*v2[1] - v1[1]*v2[0], v1[0]*v2[0] + v1[1]*v2[1]))) if angle > math.radians(angle_thresh): key_points.append(path[i]) key_points.append(path[-1]) return key_points

angle_thresh不固定,走廊环境我一般用20到30度,这样弯道会被留下来而直道上的冗余点被过滤。过滤后起点到终点之间通常只剩几个到十几个关键点,DWA的切换频率大幅下降。抽稀还有一个隐藏作用:全局路径上的小锯齿会被顺带抹平,DWA跟踪时机器人摆头次数明显减少,仿真动画看起来流畅很多。

4.3 主循环:一次控制周期内要做的五件事

把A*、抽稀、DWA串起来的主循环是整套代码的骨架。每个周期按顺序执行:检查是否到达终点、判断是否需要全局重规划、从关键点列表取当前局部目标、DWA采样评价得到最优速度、用最优速度更新机器人状态。

def run_simulation(map_grid, start, goal, dwa, max_step=20000): state = [start[0], start[1], 0.0, 0.0, 0.0] # x, y, theta, v, w global_path = astar_plan(map_grid, start, goal, mode='8') key_points = extract_key_points(global_path, angle_thresh=25.0) goal_idx = 1 fail_count = 0 for step in range(max_step): if goal_idx >= len(key_points): break # 1. 判断是否需要整条重规划 if need_replan(global_path, state, map_grid): current_grid = map_grid.world_to_grid(state[0], state[1]) global_path = astar_plan(map_grid, current_grid, goal, mode='8') key_points = extract_key_points(global_path, angle_thresh=25.0) goal_idx = 1 # 2. 取当前局部目标 local_goal = key_points[goal_idx] # 3. DWA采样、评价、选最优速度 best_v, best_w, best_traj = dwa.plan(state, local_goal, obstacle_points) # 4. 没有可行速度时的兜底 if best_v is None: fail_count += 1 state[3], state[4] = 0.0, 0.8 # 原地旋转尝试摆脱 if fail_count > 20: return False, global_path # 触发重规划由外层接管 else: fail_count = 0 state = step_motion(state, best_v, best_w, dwa.dt) # 5. 到达当前关键点就切换到下一个 if math.hypot(state[0]-local_goal[0], state[1]-local_goal[1]) < 0.2: goal_idx += 1 return True, global_path

step_motion里用的是和simulate_trajectory一样的差分模型,只不过只推进一步。判断到达阈值0.2米是我在0.1米分辨率的栅格地图上常用值,如果控制周期更长或者地图分辨率更粗,这个阈值要相应调大,否则机器人会在目标点附近反复横跳。

need_replan是融合里容易被遗漏的一环。我维护一份“已知障碍物快照”,当DWA连续多次找不到可行速度,或者机器人当前位置与全局路径的垂距超过一定阈值,就认为环境可能发生了变化,触发一次新的A规划。否则A只在开始跑一次,让整个系统响应及时、计算量也小。

4.4 工程文件组织:源码、文档、示意图、仿真脚本的分工

这个方案配套的材料通常按四个部分组织,我也按这个习惯维护代码库:

目录/文件内容对应文档章节
src/map.py栅格地图、膨胀、坐标换算地图建模
src/astar.pyA*搜索、路径抽稀全局路径规划
src/dwa.py动态窗口采样、轨迹评价局部路径规划
src/sim.py主循环、可视化、碰撞检测系统集成与仿真
docs/需求分析、设计说明、测试报告全部
figures/算法流程图、栅格地图效果图、仿真截图图示

文档里算法示意图不需要画得花哨,用matplotlib把地图、全局路径、DWA候选轨迹簇画在同一张图里,比任何手绘框图都直观。A和DWA两个算法核心模块保持解耦,A只依赖map.py,DWA只接收目标和障碍物点,这样后续把某个模块替换成真实传感器数据时,接口改动很小。

5. 路径规划仿真的5个高频坑:现象、原因与排查

5.1 机器人轨迹穿墙而过:碰撞检测的采样间距比地图分辨率还大

现象:仿真里机器人轨迹明显压着障碍物走,甚至从障碍物栅格上斜切过去,但程序没报碰撞,最终路径统计显示的“安全距离”也不足。

原因:碰撞检测直接复用了simulate_trajectory输出的轨迹点,但轨迹点间隔是v*dt。v=1.0m/s、dt=0.1s时,相邻轨迹点间隔0.1米,而栅格分辨率是0.05米,轨迹点恰好落在了障碍物两侧,检测自然漏了。

解决:碰撞检测不能只看离散轨迹点,要对相邻两点做线性插值,按半个栅格步长密集取点再查障碍。我一般插值步长取resolution/2,这样任何轨迹都不会跳过障碍物栅格。另外把dt从0.1改到0.05也能缓解,但计算量翻倍,不如插值来得划算。

5.2 机器人停在障碍物前“思考人生”:动态窗口内没有可行速度

现象:机器人接近障碍物时突然停下来,DWA返回None,主循环陷入原地旋转,反复几次后整体卡死。把仿真速度调慢看逐帧,机器人明明还有空间可以绕过去。

原因:动态窗口本身受加速度限制,当前速度较高时,下一周期能降到的速度范围很小,所有候选(v,w)生成的轨迹都撞上了障碍物。另一个隐蔽原因是beta权重过大,距离项在归一化后仍然主导,评价函数宁可选择低速轨迹也不选绕障轨迹,机器人被“吓停”。

解决:排查顺序是先看动态窗口的v_min,允许很小的倒车速度能立刻扩大搜索空间;再看评价函数权重,把beta往小调;最后检查安全刹车约束,刹车距离算法过于保守时也会把大量可行速度滤掉。这三个都调完仍然频繁失败,就触发全局重规划,大概率是地图膨胀或A*路径本身出了问题。

5.3 全局路径绕远路还带锯齿:启发函数与邻域不匹配

现象:A*跑出来的路径明显不是最短,直线上反而走出阶梯状锯齿,机器人跟踪时不停左右摆头。

原因:最典型的是八邻域搜索配了曼哈顿启发函数。曼哈顿距离在对角移动下高估了h值,A*为保证f的单调性会刻意先走横竖格,路径就变成了阶梯。另一种可能是对角代价写成了1.0而不是1.414,让对角移动过于“便宜”,路径会尽量斜着走,看起来也怪。

解决:把邻域和启发函数按匹配表对齐,八邻域一律用欧氏距离计算h。调完之后重新统计路径长度,正常情况下应当比原来短一截。这里没有玄学,纯粹是代价定义不一致造成的次优。

5.4 仿真越跑越卡:轨迹采样与障碍物距离计算的复杂度失控

现象:地图20x20米,分辨率0.05米,障碍物上千个点,DWA每周期采样20x20条轨迹、每条30个点,每点都遍历所有障碍物求距离,一帧仿真要算几十万次hypot,帧率掉到几帧每秒。

原因:evaluate_trajectory里的双重循环是罪魁祸首。障碍物距离计算复杂度是轨迹点数×障碍物点数,采样数再多乘一层,指数爆炸。问题不在DWA算法本身,而在工程实现。

解决:先把障碍物点全部塞进scipy.spatial.cKDTree,每周期构建一次树,然后对轨迹上的每个点用tree.query(px, py)[0]取最近距离,复杂度从O(点数)降到O(logN)。这一步做完,同样的参数帧率能提升一个数量级。如果不想引入scipy,可以先把障碍物按栅格分辨率下采样,只保留每个格子中心点,也能大幅减少循环次数。

5.5 起点或终点被“埋”在障碍物里:膨胀后没检查可达性

现象:程序跑起来A*直接返回None,或者机器人一开始就判定与障碍物碰撞,仿真秒退。检查地图才看到,起点或终点离膨胀后的障碍区只有一两格。

原因:地图膨胀后,原先标记的起点终点可能已经落在禁行区内,而主循环没有做可达性检查。A*搜不到目标返回None,后续代码没有针对None的分支,直接解引用崩了。

解决:规划前先检查起点和终点的格子值,若起点不可达,在周围按半径递增搜索最近空闲格当作替身起点;终点不可达则直接报错提示重新选点。这看起来是个小细节,但功能测试一定会覆盖,做不做会直接影响答辩效果。

6. 从能跑到能答辩:可复现的验证方法与三个进阶方向

6.1 设计三张地图做定量对比

毕设文档里最怕只有效果截图,没有量化数据。我固定用三张地图:空旷地图测速度、走廊地图测跟踪偏差、密集障碍地图测成功率。每个场景各跑20次,统计下表:

指标A*单独跑DWA单独跑A*+DWA融合
到达率高,但忽略动态障碍低,复杂场景易卡死高
平均路径长度接近最优明显绕路接近最优
平均规划耗时毫秒级每周期毫秒级毫秒级

这个对比表写进测试章节,比任何描述都有说服力。注意每个场景要固定随机种子,否则数据抖动说不清楚。

6.2 三个值得做的进阶方向

第一是给DWA增加“贴近全局路径”的第四个子代价,把机器人和全局路径的垂距算进去,能明显减少绕路,代价是调参项多一个。第二是改用真实地图图片导入,比如把一个PNG二值图转成栅格,整套代码的输入输出都不用改,普适性立刻上一个台阶。第三是记录机器人在每个周期的轨迹并导出gif,答辩现场放动画比放截图直观得多。

6.3 最后说一点我的习惯

我第一版调DWA时,把距离权重从0.2加到0.4,结果机器人连空旷地方都慢慢吞吞,远远看见障碍物就刹停,看起来像在障碍物面前思考人生。后来才发现问题不在权重,在归一化——距离项数值天然比朝向项大,权重细微变化就被放大了。那次之后我所有多目标评价都先归一化再看权重。

如果你照着这套方案复现,建议先把A*跑通、把可视化打出来,确认全局路径正常后再接DWA;接上后先拿空旷地图,再慢慢加障碍物。每一步都留一个能跑的版本,改坏了随时回到上一版,这是毕设项目里最值得养成的习惯。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询