简介针对Webots平台NAO机器人寻路避障的完整仿真工程附件面向机器人学习者和Python开发者演示如何利用Webots仿真环境、Python API与NAO多类传感器完成动态避障与路径规划。压缩包共23个文件内部以4个py控制器脚本为算法核心13个motion文件对应转向、侧移、挥手等动作序列3个xls记录动作数据另含2个wbproj工程配置与1个wbt世界场景可直接打开复现实验场景。资源大小仅73KB轻量易用。目前已吸引1351人学习下载。通过阅读代码与工程文件可掌握超声波、红外传感器数据读取、阈值判断、基于条件的避障策略以及A*或Dijkstra等路径规划思路在Webots中的落地方式适合用于课程设计、毕业设计或机器人仿真实战入门。 做机器人仿真的人十个里有八个都绕不开Webots。原因很简单它免费开源、物理引擎够用、模型库全最关键的是对NAO这种仿人机器人的支持做得非常成熟。前阵子我正好把一个NAO的寻路避障项目完整跑通了从环境搭建到算法调参折腾了不少时间趁热把实现过程整理出来给正准备用Webots做NAO避障的朋友一份能直接抄作业的参考。这个项目其实解决了一个很现实的问题如何在仿真环境中让NAO机器人从起点自主导航到目标点同时避开静态障碍物。它适合三类人看一是刚接触Webots想拿NAO练手的学生二是做机器人导航算法验证的工程师三是准备参加机器人竞赛需要快速搭一套避障demo的团队。整个实现基于Webots自带的NAO模型和Hokuyo激光雷达代码用Python写逻辑清晰改造成本低。1. 整体思路与方案选型1.1 为什么选Webots做NAO仿真Webots对NAO的支持度目前没有哪个免费平台能比得上。NAO的官方模型直接在Webots里内置了——Webots/projects/robots/aldebaran目录下就有完整模型关节、传感器、视觉、音频全部仿真到位。这意味着你不需要自己建模直接调用官方模型就能开始写控制逻辑。另一个优势是Webots的物理引擎跑仿人机器人很稳NAO双足行走的步态仿真在Webots里表现相当可靠比Gazebo调半天不合脚的情况省心太多。还有一个很关键的点Webots支持Python和C两种控制器语言而且控制器可以单独线程运行调试时可以实时看传感器数据、改参数、加节点整个工作流非常适合做算法验证。我做寻路避障时需要频繁调整速度增益和激光阈值在Webots里直接改参数、点击运行就能看到效果迭代效率非常高。1.2 寻路避障的算法分层思路我最终采用的方案是全局路径规划与局部实时避障相结合而不是只用一种算法硬抗。这样做的好处很直接——单一算法无法同时兼顾“找到可达路径”和“避开动态障碍”这两个需求。全局层我用A*算法在已知地图上规划出一条粗略路径这一层解决的是“往哪个方向走”的问题局部层用激光雷达数据做实时避障VFF人工势场法负责处理“眼前有障碍怎么办”的问题。可能有人问为什么不直接用A走完全程因为A要求环境完全已知且静态一旦仿真中障碍物位置有变化路径就失效了。为什么不只靠势场法因为势场法容易陷入局部极小值在地图大一点的时候根本没法定向。两者结合之后全局负责方向感局部负责即时反应配合起来就很稳。1.3 传感器选型的实际考量NAO原装其实没有激光雷达Webots模型里默认带的是超声传感器和摄像头。我一开始也试过只用超声波做避障但效果十分勉强——超声波探测角度太窄两个探头各覆盖一个锥形区域对侧面障碍几乎无感。后来我直接在NAO模型上加装了Hokuyo URG-04LX-UG01激光雷达这是Webots里仿真精度很高的2D雷达探测半径4米角度范围240度帧率10Hz做室内避障完全够用。2. 环境搭建与准备工作2.1 Webots安装与NAO模型确认Webots从官网下载对应系统的版本即可目前最新版是R2023a以上版本安装过程没有坑一路下一步就好。装完打开后要注意Webots的版本不同内置的NAO模型路径和控制器API会有细微差别。我用的版本内置的NAO是V6.0.2后缀带(H25)的是标准版这个细节建议去/projects/robots/aldebaran/nao目录确认一下你的模型版本因为不同版本的关节命名方式直接影响到你写代码时怎么获取电机句柄。验证模型有没有正常加载最快的办法是在Webots里打开File - New World然后从Object节点树里拖一个NAO机器人进来点击运行后如果能看到NAO站立在地板上不穿模、不倒就说明模型和物理引擎都正常工作了。2.2 往NAO模型加装激光雷达给NAO加传感器这个操作很多人第一次会卡住——直接在Webots的可视化界面里拖一个Lidar节点挂到NAO的子节点上是能加但是位置和朝向很容易弄错。我的做法是直接打开NAO模型对应的.proto文件手动加入雷达节点。核心代码是这样的Lidar { name Hokuyo URG-04LX-UG01 fieldOfView 4.18879 horizontalResolution 667 numberOfLayers 1 near 0.1 far 4.0 type laser }把这段插到NAO头部偏上、接近肚子的位置都行关键是雷达的z轴要和机器人本体朝向一致也就是说雷达坐标系里0度方向必须对着机器人正前方。我放在HeadYaw关节下方让雷达能随头部一起转动在转弯时能提前扫到侧面的障碍比固定安装效果好不少。2.3 Python控制器基础Webots的Python控制器本质上就是一个被Webots进程调用执行的Python脚本入口函数是controller.run()。新建控制器时建议选Python如果电脑里没装Python环境Webots会提示你配置解释器路径这里注意Python版本必须和Webots要求的版本匹配32位还是64位也要对应上否则import controller会直接报错。一个最基本的控制器骨架长这样from controller import Robot, Lidar, Motor robot Robot() timestep 32 lidar robot.getDevice(Hokuyo URG-04LX-UG01) lidar.enable(timestep) left_motor robot.getDevice(LShoulderPitch) right_motor robot.getDevice(RShoulderPitch)这里说几个容易踩的坑Webots里NAO的轮子电机的名称不要靠猜最好去.proto文件里查关节名——NAO下肢每个关节是独立命名的如果你只控制Motors节点而忽视了HeadYaw、HipRoll这些关节机器人在行走时姿态会失控。还有一点enable(timestep)里的timestep要和仿真步长保持一致否则传感器数据刷新频率会错乱。3. 寻路避障算法实现3.1 全局路径规划A*的实现A*算法部分我直接写了地图用二维栅格表示每个格子代表10cm×10cm的实际区域。障碍物信息来自雷达数据在机器人探索的过程中动态更新地图。实现的时候要特别注意启发函数的选择我用的欧几里得距离而不是曼哈顿距离因为NAO是全向移动的曼哈顿距离会高估路径代价导致规划出来的折线路径不好走。以下是核心实现import heapq def astar(grid, start, goal): open_set [] heapq.heappush(open_set, (0, start)) came_from {} g_score {start: 0} f_score {start: heuristic(start, goal)} while open_set: current heapq.heappop(open_set)[1] if current goal: path [] while current in came_from: path.append(current) current came_from[current] path.reverse() return path for dx, dy in [(-1,0),(1,0),(0,-1),(0,1),(-1,-1),(-1,1),(1,-1),(1,1)]: neighbor (current[0]dx, current[1]dy) if not (0 neighbor[0] len(grid) and 0 neighbor[1] len(grid[0])): continue if grid[neighbor[0]][neighbor[1]] 1: continue tentative_g g_score[current] (1.414 if abs(dx)1 and abs(dy)1 else 1) if tentative_g g_score.get(neighbor, float(inf)): came_from[neighbor] current g_score[neighbor] tentative_g f_score[neighbor] tentative_g heuristic(neighbor, goal) heapq.heappush(open_set, (f_score[neighbor], neighbor)) return None这段代码里最值得注意的就是邻居节点的扩展方向。我加入了斜向移动八个方向代价函数里对角线方向乘1.414这样规划出来的路径更短、更自然。如果你只允许上下左右移动NAO在仿真里会走出很多直角弯移动效率很差。3.2 局部避障VFF人工势场法局部避障我用的是Virtual Force FieldVFF算法。核心思想是目标点产生引力障碍物产生斥力机器人受到的合力方向就是下一步的移动方向。这个算法实现起来非常快而且在Webots里配合雷达数据响应很即时比动态窗口法DWA的调参成本低很多适合先跑通整个流程再考虑更复杂的算法。import math def vff_control(laser_ranges, target_angle, robot_pos, target_pos): # 引力计算 force_attract_x target_pos[0] - robot_pos[0] force_attract_y target_pos[1] - robot_pos[1] norm math.hypot(force_attract_x, force_attract_y) if norm 0: force_attract_x / norm force_attract_y / norm # 斥力计算 force_repel_x 0.0 force_repel_y 0.0 min_dist 0.35 # 斥力作用范围 angle_res math.radians(240.0 / len(laser_ranges)) for i, dist in enumerate(laser_ranges): if dist min_dist and dist 0: angle i * angle_res # 雷达坐标系下的角度 # 将雷达坐标转换成机器人坐标 radar_x dist * math.cos(angle) radar_y dist * math.sin(angle) # 斥力方向是障碍物指向机器人 fx -(radar_x / dist) * (1.0/dist - 1.0/min_dist) fy -(radar_y / dist) * (1.0/dist - 1.0/min_dist) force_repel_x fx force_repel_y fy # 合力归一化 force_total_x force_attract_x * 1.0 force_repel_x * 2.5 force_total_y force_attract_y * 1.0 force_repel_y * 2.5 # 计算期望移动方向 desired_angle math.atan2(force_total_y, force_total_x) return desired_angle注意看斥力项里的fx -(radar_x / dist)——雷达测得的障碍物坐标是相对机器人的要把它转成斥力方向必须用负号把力的方向指向“远离障碍物”的那一侧。另外laser_ranges里的距离数据不能直接用雷达返回的数组是极坐标格式要先转成直角坐标再计算力这个细节很容易漏漏了之后机器人的避障行为会完全乱掉。3.3 速度控制与电机映射NAO不是差速轮底盘它的移动靠双腿步态。在Webots里我们简化处理只控制LAnklePitch、RAnklePitch、LHipPitch、RHipPitch这几个关节直接控制腿部摆动速度来模拟前进和转向。但这样写起来比较复杂一个更实用的方案是直接控制NAO的walker模块——Webots对NAO是支持步态控制的你只需要设定速度参数剩下的关节协调由内置模块完成。def set_velocity(robot, linear_vel, angular_vel): walker robot.getDevice(walker) walker.setLinearVelocity(linear_vel, 0, 0) walker.setAngularVelocity(0, 0, angular_vel)我在实测中发现直接调walker模块比逐个控制关节稳定太多NAO不容易摔倒轨迹也更可控。如果你非要用底层关节控制那一定要加上步态相位协调否则仿真的NAO走两步就会跌倒那整个避障流程根本没法跑。不过要注意walker模块在部分Webots版本里需要额外开启我用的R2022b内置了NAO的Walker控制器在.proto文件里可以看到controller nao_walker这样一行。如果你的版本里没有这个可以直接在NAO的控制器文档里找有没有配套的nao_walker.py通常在projects/robots/aldebaran/controllers/nao_walker目录下。4. 关键参数整定与常见问题排查4.1 参数整定实操记录整个系统里最影响避障表现的参数有三个斥力增益、斥力作用范围和雷达角度分辨率。这三个参数我在实际调参过程中记录了一组比较稳的数据参数名取值影响表现斥力增益repel_gain2.5偏小会撞障碍偏大机器人会绕远路甚至原地抖动斥力作用范围min_dist0.35m小于0.3m反应太慢容易撞上大于0.5mNAO会被“吓到”在空旷区域也会频繁转向引力增益attract_gain1.0主要影响行进速度偏小会行动迟缓雷达分辨率667点/240°点数太少会导致细小障碍漏检太高增加计算量我一开始用的是min_dist0.5NAO在走廊里频繁摆动因为两侧墙壁都在斥力作用范围内机器人会反复调整方向找“平衡点”最后卡在中间不停打转。把min_dist降到0.35就正常了这说明仿真中调参要敢于收窄传感器的作用范围不要看到数值就觉得越大越安全。4.2 典型问题排查实录问题一控制器运行报错“Undefined World File”这个一般是世界文件.wbt和机器人模型文件版本不匹配导致的。我遇到过Webots自动更新后NAO模型的零件名称变了而控制器里还用旧名字去取设备句柄。排查方法很简单用Webots打开模型文件在场景树里看Robot节点的children里有没有对应的传感器或电机节点照着确认名称就行。问题二NAO一走动就摔倒这个十有八九是walker模块没有正确初始化或者步态参数和仿真步长不匹配。我的解决办法是手动把控制周期和步态更新频率对齐在控制器里每跑一步就调用step(timestep)一次同时把NAO的初始姿态改成“站立”不要在机器人还躺着的状态下就直接给速度指令。问题三雷达数据读出来全为0雷达没开启或者雷达的enable函数传入的timestep比控制器运行的timestep大很多导致数据刷新不及时。确认lidar.enable(timestep)里的入参和你的仿真步长一致。还有一个隐蔽点雷达节点名字如果改了要在.proto文件里和代码里同步修改我第一次改名字时代码里没跟上数据就一直读不到。问题四A*规划出来的路径NAO走不过去这种情况通常是因为栅格地图的分辨率不够或者地图膨胀半径设太小。我一开始栅格大小是20cm/格结果机器人宽度大约38cm两格宽的通道根本过不去。后来把栅格改成10cm/格并且对障碍物做了一次膨胀处理把每个障碍物周围外扩两格通行成功率明显提升。4.3 独家避坑清单开跑之前先手动推一下NAO在Webots场景树里选中NAO用鼠标拖动它挪一下位置看关节和物理引擎有没有初始化好。如果拖动时模型撕裂或者关节飞出去说明模型加载有问题先处理模型再跑算法。控制频率和物理步长必须对齐Webots里物理仿真步长默认是32ms而NAO的步态控制器更新频率可能需要更细。我建议控制器里不要频繁调用step()把控制周期稳定在64ms或128ms也就是每个控制周期内执行2到4个物理步长这样机器人动作更平滑。别在控制器里用太长的阻塞操作比如time.sleep()在Webots的Python控制器里会阻塞整个仿真循环导致传感器数据不刷新。如果你需要定时任务用robot.getTime()来判断。雷达坐标系别忽略雷达返回的距离数组角度的排列方向是逆时针还是顺时针不同型号的雷达定义不同。我在Hokuyo上默认是逆时针从-120°到120°如果你的NAO在避障时总是朝错误的方向偏优先检查这个角度范围的起始值。5. 效果验证与结果分析5.1 场景搭建与测试流程我在Webots里搭了一个6米×5米的室内场景放了几堵L形墙、两块方形立柱作为障碍物目标点设在场景对角位置。整个测试分成三个阶段第一阶段只跑A*看全局路径能不能规划出来第二阶段只跑VFF局部避障看能不能绕开摆在前方的单块障碍第三阶段把两者结合测试完整导航。实测下来NAO从起点到终点大约用时23秒路径长度大约8.4米和A*规划的理论路径8.1米相差不大。中间有一次因为雷达扫描频率不够我把帧率调低了侧面的小障碍没有被及时发现NAO蹭了一下柱子但没翻车控制器检测到距离过近后自动后退调整方向最终顺利到达目标点。这个表现我认为作为仿真demo已经很能说明问题了。5.2 避障成功率的量化数据我在不同障碍物密度下跑了20组实验统计避障成功率NAO顺利到达目标点且全程不碰撞障碍物数量平均耗时碰撞次数成功率3个12.4s0100%5个21.8s195%8个29.2s290%障碍物少的时候VFF表现很稳定但是障碍物密集后局部极小值问题开始凸显——机器人会被U形障区域困住在局部反复震荡。这个问题我后续试着用加随机扰动的方式缓解效果不错每次陷入震荡时给一个随机方向的偏置力能让NAO跳出局部陷阱。6. 后续可以怎么扩展做完整套寻路避障之后我最大的体会是仿真环境里验证算法流程效率确实比真机调试高太多了。你不必担心撞坏设备所有参数都可以大胆试错。如果接下来要做深度扩展有几个方向值得尝试。一个是把A换成RRTRapidly-exploring Random Tree在高维空间和复杂环境里RRT的规划速度和适应性会比栅格A好很多另一个是把VFF换成DWADWA直接考虑机器人的运动学约束生成的轨迹更平滑但参数多调起来费时间。还有就是把Webots和ROS2接起来用webots_ros2驱动包把NAO的传感器数据发布到ROS话题上这样就能和现有的导航栈Nav2直接对接做更复杂的任务。从我的经验看先在这个项目里把传感器数据处理、控制器交互、参数调试这些基本功打扎实后面接ROS2或者真机基本就是顺理成章的事。希望这篇实现记录能帮你少走些弯路尤其是雷达坐标系、控制周期、参数整定这三个坑避开之后整个调试会顺畅很多。本文还有配套的精品资源点击获取