ARTICLE

Webots中NAO人形机器人自主寻路与避障:A*算法与传感器融合实战

考试通知 · 政策解读 · 开班计划

发布时间:2026/10/12 8:14:24来源:迅启考通分类:考试资讯

文章详情

以下为资讯详情页模板:正文区域由后台内容渲染,图片与正文将自动替换为对应文章内容。

文章配图
Webots中NAO人形机器人自主寻路与避障:A*算法与传感器融合实战简介这份资源面向机器人仿真与类人机器人控制的学习者聚焦在Webots平台用Python实现NAO机器人的寻路避障功能适合具备一定Python基础、想入门机器人传感器融合与路径规划的中级开发者。压缩包共23个文件约73KB包含4个py控制脚本、13个motion动作文件、3个xls数据表、2个wbproj工程配置和1个wbt场景文件分别对应算法逻辑、关节动作序列、传感器数据记录与仿真环境搭建。已有1351人学习下载。资源完整呈现了从超声波与红外传感器读数判断障碍、设定阈值触发避障到调整关节角度执行转向、循环更新路径的实践链路并预留了向A*、Dijkstra等高级规划算法扩展的空间。通过分析其中的场景配置与控制器代码读者可快速复现NAO在虚拟环境中的避障行为理解传感器数据处理与机器人运动控制的衔接方式是机器人学课程实验与自主项目练手的实用参考。1. Webots 里让 NAO 自己找路从仿真到避障的完整落地路径很多做机器人仿真的朋友第一次把 NAO 模型拖进 Webots 场景时都会遇到一个尴尬局面机器人站在原地前方摆几个箱子它要么一动不动要么一头撞上去。这不是模型坏了而是你还没给它一套完整的「感知—决策—执行」链路。NAO 是双足人形机器人和轮式底盘不同它的运动约束更复杂寻路避障不能只靠一个简单的 if-else 判断。Webots 作为一款成熟的机器人仿真平台提供了完整的传感器仿真、物理引擎和控制器接口非常适合用来验证 NAO 的自主导航算法。这篇文章面向的是已经装好 Webots、手里有 NAO 模型、想让它在仿真环境里真正跑起来的开发者。我会从环境搭建讲到控制器代码再到参数调优和踩坑记录每一步都给出可复现的操作。如果你正在做课程设计、毕业课题或者自己的机器人仿真项目这套方案可以直接拿去改。2. 场景搭建与 NAO 模型配置先把舞台搭对2.1 Webots 世界文件的最小可用结构Webots 的世界文件.wbt本质是一个场景描述文本里面定义了地面、光照、机器人节点、障碍物节点以及它们之间的层级关系。很多人习惯直接用 Webots 自带的向导生成一个空世界然后手动拖拽模型但这样做容易漏掉一些关键属性比如物理接触参数和传感器刷新率。我一般会从一个最小结构开始写确保每个节点都有明确的用途。一个典型的 NAO 寻路场景需要包含以下顶层节点WorldInfo 用于设置基本物理参数Viewpoint 定义观察视角TexturedBackground 提供视觉背景RectangleArena 作为地面NAO 机器人节点以及若干 Solid 或 Box 节点作为障碍物。下面是一个精简的世界文件片段你可以直接保存为nao_avoidance.wbt并在 Webots 中打开。#VRML_SIM R2023b utf8 WorldInfo { basicTimeStep 32 physics { damping Damping { linear 0.2 angular 0.2 } } } Viewpoint { orientation -0.3 0.5 0.8 1.2 position 2.5 2.0 2.5 } RectangleArena { floorSize 10 10 floorTileSize 1 1 } NAO { translation 0 0 0.35 rotation 0 0 1 0 controller nao_avoidance_controller customData start } Solid { translation 2 0 0.25 children [ Shape { appearance PBRAppearance { baseColor 0.8 0.2 0.2 } geometry Box { size 0.5 0.5 0.5 } } ] boundingObject Box { size 0.5 0.5 0.5 } physics Physics { density -1 mass 5 } }这段世界文件里basicTimeStep 32表示仿真步长为 32 毫秒这是 Webots 中 NAO 模型比较常用的值再小会增加计算量再大则传感器数据更新不够及时。NAO节点里的controller字段指定了控制器名称必须和后面写的 Python 或 C 文件名一致。障碍物用Solid包裹一个Box同时设置boundingObject和physics否则机器人会直接穿过去这是新手最容易忽略的一点。2.2 NAO 传感器使能与参数确认NAO 模型在 Webots 中默认会带一批传感器但并不是所有都默认开启。寻路避障主要依赖两类传感器一是位于头部的摄像头二是分布在身体前方的红外测距传感器。摄像头可以用来做视觉里程计或目标检测但计算量大红外传感器响应快、数据直接适合做实时避障。我一般会先用红外传感器把避障跑通再考虑加视觉。在控制器代码里你需要通过getDevice获取传感器对象并调用enable方法设置采样周期。下面是一段 Python 控制器初始化代码展示了如何开启 NAO 的前向红外传感器和摄像头。from controller import Robot robot Robot() timestep int(robot.getBasicTimeStep()) # 开启前向红外传感器NAO 通常有 2 个前向和 2 个侧向 ir_names [ds_front_left, ds_front_right, ds_left, ds_right] ir_sensors [] for name in ir_names: sensor robot.getDevice(name) if sensor is not None: sensor.enable(timestep) ir_sensors.append(sensor) else: print(f警告未找到传感器 {name}) # 开启摄像头用于后续视觉扩展 camera robot.getDevice(Camera) camera.enable(timestep) # 获取电机 left_motor robot.getDevice(LAnklePitch) # 实际使用需替换为行走相关电机这里有一个关键点NAO 的传感器命名在不同版本的 Webots 模型中可能略有差异。如果你调用getDevice返回None不要急着改代码先去 Webots 的场景树里展开 NAO 节点逐个查看name字段的实际值。我见过不少案例是因为传感器名字写错导致程序一直读不到数据最后误以为是算法问题。另外enable的参数是采样周期一般设为和basicTimeStep一致即可设得太大会导致避障反应迟钝。2.3 障碍物布局与可复现的测试场景为了让避障效果可对比、可复现我建议固定一套障碍物布局而不是每次随机摆放。一个经典的测试场景是在机器人正前方 2 米处放一个箱子左右两侧各 1.5 米处再放两个形成一个「品」字形通道。这样既能测试直行避障也能测试侧向绕行。你可以在世界文件里复制多个Solid节点只改translation的坐标值。障碍物编号位置 (x, y, z)尺寸 (m)用途O1(2.0, 0, 0.25)0.5×0.5×0.5正前方阻挡O2(1.5, 1.2, 0.25)0.4×0.4×0.4右侧绕行测试O3(1.5, -1.2, 0.25)0.4×0.4×0.4左侧绕行测试O4(3.5, 0, 0.25)0.6×0.6×0.6远距离目标点附近这张表可以直接作为你搭建场景的参考。注意 z 轴坐标NAO 的初始高度大约在 0.35 米左右障碍物高度设为 0.25 米可以确保红外传感器能检测到。如果障碍物太矮传感器可能扫不到太高则可能遮挡摄像头视野。实际调试时你可以先把机器人放在起点手动旋转视角观察传感器射线是否指向障碍物。3. 寻路算法选型为什么用 A* 而不是纯反应式避障3.1 反应式避障的局限与适用边界纯反应式避障的逻辑很简单传感器检测到前方有障碍就转向没有就直行。这种方案在开阔环境里能跑但一旦遇到凹形障碍或死胡同机器人就会在局部来回震荡永远出不去。我在早期测试中用过纯反应式方案机器人面对「品」字形障碍时会在两个箱子之间反复横跳最后卡在角落里。这不是参数没调好而是算法本身缺乏全局信息。反应式避障的另一个问题是无法保证到达目标点。它只能保证「不撞」不能保证「走到」。如果你的任务只是让 NAO 在房间里随机游走那反应式够用但如果是「从 A 点走到 B 点」就必须引入全局路径规划。常见的做法是把环境栅格化用 A* 或 Dijkstra 算出一条全局路径再用局部避障处理动态障碍。3.2 栅格地图构建与 A* 路径搜索在 Webots 中你不需要真的去建一张高精度地图。对于固定障碍物场景可以直接在控制器里维护一个二维栅格数组每个格子标记为可通行或障碍。栅格分辨率一般取 0.1 米到 0.2 米太细会增加搜索时间太粗会导致路径贴墙。下面是一个简化的栅格构建和 A* 搜索代码。import heapq # 10x10 米环境分辨率 0.2 米共 50x50 格 GRID_SIZE 50 RESOLUTION 0.2 grid [[0 for _ in range(GRID_SIZE)] for _ in range(GRID_SIZE)] # 标记障碍物坐标转换世界坐标 (x, y) - 栅格 (row, col) def mark_obstacle(x, y, size0.5): half size / 2.0 min_col int((x - half) / RESOLUTION) max_col int((x half) / RESOLUTION) min_row int((y - half) / RESOLUTION) max_row int((y half) / RESOLUTION) for r in range(min_row, max_row 1): for c in range(min_col, max_col 1): if 0 r GRID_SIZE and 0 c GRID_SIZE: grid[r][c] 1 # 1 表示障碍 # A* 搜索 def astar(start, goal): open_set [(0, start)] came_from {} g_score {start: 0} while open_set: _, current heapq.heappop(open_set) if current goal: path [] while current in came_from: path.append(current) current came_from[current] return path[::-1] for dr, dc in [(-1,0),(1,0),(0,-1),(0,1),(-1,-1),(-1,1),(1,-1),(1,1)]: neighbor (current[0]dr, current[1]dc) if not (0 neighbor[0] GRID_SIZE and 0 neighbor[1] GRID_SIZE): continue if grid[neighbor[0]][neighbor[1]] 1: continue tentative_g g_score[current] (1.414 if dr ! 0 and dc ! 0 else 1.0) if neighbor not in g_score or tentative_g g_score[neighbor]: came_from[neighbor] current g_score[neighbor] tentative_g f tentative_g abs(neighbor[0]-goal[0]) abs(neighbor[1]-goal[1]) heapq.heappush(open_set, (f, neighbor)) return []这段代码里mark_obstacle把世界坐标转成栅格坐标注意 y 轴方向和栅格行号的关系Webots 里 y 轴通常指向屏幕内而栅格行号从上往下增加所以需要根据你的坐标系做一次翻转。A* 的启发函数用了曼哈顿距离在允许对角移动时也可以换成欧几里得距离但曼哈顿距离计算更快路径稍长一点实际用起来差别不大。heapq维护开放集每次取 f 值最小的节点扩展。3.3 路径平滑与 NAO 行走控制对接A* 算出来的路径是一串栅格点直接让 NAO 逐点走会非常生硬因为栅格路径会有很多直角转弯。NAO 是双足机器人频繁急转容易失去平衡。我一般会做两步处理先用视线检测去掉不必要的中间点再把路径点转换成朝向角和步数。视线检测的逻辑是如果从当前点能直接「看到」后面某个点且中间没有障碍就跳过中间点。def line_of_sight(p1, p2): # Bresenham 直线算法检查两点间是否穿过障碍 r0, c0 p1 r1, c1 p2 dr abs(r1 - r0) dc abs(c1 - c0) sr 1 if r0 r1 else -1 sc 1 if c0 c1 else -1 err dr - dc while True: if grid[r0][c0] 1: return False if (r0, c0) (r1, c1): return True e2 2 * err if e2 -dc: err - dc r0 sr if e2 dr: err dr c0 sc def smooth_path(path): if len(path) 2: return path smoothed [path[0]] i 0 while i len(path) - 1: j len(path) - 1 while j i 1: if line_of_sight(path[i], path[j]): break j - 1 smoothed.append(path[j]) i j return smoothed平滑后的路径点数量会明显减少NAO 走起来更自然。接下来要把栅格点转回世界坐标计算每个路径点相对于机器人当前朝向的角度差然后调用 NAO 的行走接口。Webots 中 NAO 的行走通常通过setWalk或直接控制关节电机实现具体取决于你用的模型版本。如果模型自带行走管理器可以直接设置目标速度和方向如果没有就需要自己写步态控制那工作量会大很多。建议优先使用带行走管理器的 NAO 模型。4. 避坑与常见问题排查那些让我熬夜的瞬间4.1 传感器读不到数据先查名字再查使能现象控制器运行后红外传感器返回值一直是 0 或 -1机器人对障碍物毫无反应。原因通常有两个一是传感器名称写错getDevice返回None后续调用直接报错或被跳过二是忘记调用enableWebots 中传感器默认不刷新数据。解决方法是先在场景树里确认每个传感器的name字段然后在代码里加打印语句确认getDevice返回值不为空。如果为空把名字复制过去重新试。4.2 机器人抖动或原地转圈检查时间步和电机限幅现象NAO 在避障时频繁左右摆动甚至原地转圈。原因可能是控制周期和仿真步长不匹配或者电机速度设得太大。Webots 的basicTimeStep是 32 毫秒如果你的控制循环里没有用robot.step(timestep)同步传感器数据会重复读取旧值导致决策震荡。另外NAO 的关节电机有速度限制直接设一个很大的值会被内部限幅实际输出和预期不符。建议在设置速度后读取实际值做校验。4.3 路径贴墙或穿过障碍栅格膨胀没做现象A* 算出的路径紧贴障碍物边缘NAO 走过去时肩膀或手臂碰到箱子。原因是栅格地图只标记了障碍物本身没有考虑机器人半径。解决方法是做栅格膨胀把障碍物周围一圈也标记为不可通行。膨胀半径一般取机器人最大半宽加 0.05 米安全余量。NAO 的肩膀宽度大约 0.2 米所以膨胀 2 到 3 个栅格比较合适。4.4 仿真跑一段时间后变慢物理参数需要调现象仿真刚开始很流畅几分钟后越来越卡。原因可能是物理引擎中接触点过多或者某些物体没有设置合理的damping。Webots 默认的物理参数在复杂场景下可能不够用。可以在WorldInfo里增加damping的线性值和角度值减少不必要的震荡。另外把不参与碰撞的装饰性物体设为Solid但不加physics也能减轻计算负担。4.5 目标点永远到不了检查坐标转换现象机器人一直在目标点附近打转就是停不下来。原因通常是世界坐标和栅格坐标转换时 y 轴方向搞反了导致目标点在栅格地图上的位置和实际位置不一致。解决方法是打印目标点的世界坐标和转换后的栅格坐标手动核对。Webots 中 x 轴向右y 轴向前或向后取决于视角设置而栅格行号通常从上到下增加两者需要一次翻转。5. 进阶技巧让 NAO 走得更稳的几个参数5.1 动态障碍物处理与局部重规划固定障碍物场景跑通后可以加入动态障碍物比如一个来回移动的箱子。这时候全局路径需要定期重规划但重规划频率不能太高否则计算量会拖慢仿真。我一般设一个 1 秒的定时器每秒检查一次当前路径是否被新障碍物阻断如果阻断就重新跑 A*。同时局部避障层继续用红外传感器做紧急转向两层配合能覆盖大多数情况。5.2 用摄像头做视觉辅助的轻量方案如果红外传感器不够用可以加一个轻量的视觉辅助。不需要跑深度学习模型直接用颜色阈值分割就能识别特定颜色的障碍物。比如把障碍物涂成红色摄像头图像里提取红色区域计算质心位置就能得到障碍物的方位。这种方法计算量小在 Webots 里跑实时没问题。下面是一个简单的颜色检测代码片段。import numpy as np def detect_red_obstacle(camera): image camera.getImage() width camera.getWidth() height camera.getHeight() # 将图像转为 numpy 数组Webots 返回的是 BGRA 格式 img np.frombuffer(image, dtypenp.uint8).reshape((height, width, 4)) # 提取红色通道简单阈值 red_mask (img[:, :, 2] 150) (img[:, :, 1] 100) (img[:, :, 0] 100) if np.sum(red_mask) 50: ys, xs np.where(red_mask) cx np.mean(xs) # 根据质心横坐标判断障碍物在左还是右 if cx width / 3: return left elif cx 2 * width / 3: return right else: return front return None这段代码里camera.getImage()返回的是字节串需要转成 numpy 数组。Webots 的图像格式是 BGRA所以红色通道在索引 2。阈值可以根据实际光照调整np.sum(red_mask) 50是为了过滤噪声避免误检。检测到障碍物方位后可以覆盖 A* 的路径决策优先执行避让动作。5.3 参数调优的优先级顺序调参不要眉毛胡子一把抓我一般按这个顺序来先调传感器采样周期确保数据实时性再调栅格分辨率和膨胀半径保证路径安全然后调 A* 的启发函数权重平衡路径长度和搜索速度最后调 NAO 的行走速度和转向角度让运动更平滑。每次只改一个参数改完跑同一套测试场景记录成功率和耗时。这样即使出了问题也能快速定位是哪个参数导致的。参数建议范围影响传感器采样周期3264 ms越小反应越快但计算量增加栅格分辨率0.10.2 m越小路径越精细搜索越慢膨胀半径0.150.25 m越大越安全但可能堵死窄通道A* 启发权重1.01.5越大搜索越快路径可能不是最优行走速度0.20.5 m/s太快容易失衡太慢效率低这张表是我多次调试后总结的经验值具体还要根据你的场景尺寸和 NAO 模型版本微调。比如场景里有窄通道时膨胀半径就要适当减小否则 A* 会直接判定无路可走。5.4 一个让我后悔药的教训早期我为了省事把 A* 搜索和传感器读取放在同一个循环里没有做时间分离。结果仿真步长 32 毫秒A* 搜索一次要 50 毫秒导致控制循环被拖慢机器人动作一顿一顿的。后来我把 A* 放到单独的线程里或者降低重规划频率问题才解决。这个坑让我明白仿真环境里的计算资源也是有限的算法复杂度不能只看理论还要看实际运行开销。希望帮到你。本文还有配套的精品资源点击获取
特别提醒:考试时间、报名批次等安排如有调整,以河南省应急管理厅及官方考点最新通知为准。

最新新闻

看完这篇文章,想了解更多?

报考条件、材料清单、最近批次,顾问一次帮你理清,别自己摸索。