尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

ROS多机器人仿真从搭建到编队:Gazebo环境、导航避碰与领航跟随实践

ROS多机器人仿真从搭建到编队:Gazebo环境、导航避碰与领航跟随实践 简介一套基于ROS的多机器人仿真项目聚焦导航与编队两大任务覆盖xacro/URDF机器人建模、Gazebo环境搭建、Rviz可视化以及编队控制适合已有ROS基础、希望实践多机器人协同的中高级开发者。压缩包共68个文件约1.17MB包含18个launch启动脚本、7个yaml参数配置、5个world仿真场景、机器人模型与网格文件、C/Python节点源码以及说明文档目录按功能模块划分便于按需取用与二次开发。该项目已有3558人学习下载配套古月居四篇笔记从原理到配置逐步拆解可对照工程代码深入理解导航栈配置、编队算法与多机器人协同细节。通过这套工程可快速复现多机器人导航编队仿真效果节省从零搭建环境的时间也适合作为毕业设计或机器人竞赛的基础框架。 做多机器人仿真这个项目起因其实挺直接的。我之前一直在跑单台差速小车的SLAM和自主导航单机功能确实能跑通但当我把场景放到仓库盘点、园区巡检这类需要多车协同的真实任务里单机的方案一下子就撑不住了。多个机器人要不要共享一张地图它们在同一个空间里怎么避碰队形怎么保持这些单机导航里根本不会遇到的问题全都冒了出来。所以我就用ROS配合Gazebo搭了一套多机器人仿真环境把导航和编队两件事揉在一起做了完整验证。这篇文章把我从环境搭建到算法调通的过程、踩过的坑、换过的方案都整理出来给打算上手ROS多机器人仿真的朋友一条可以照着走的路线。需要说明的是这篇文章面向的是已经跑通过单机导航、但对多机器人系统还不熟悉的读者。如果你连第一次Gazebo启动都还没完成建议先去把基础的环境搭建和单机导航流程过一遍再回头来看多机的内容会顺畅很多。1. 多机器人仿真项目到底在解决什么问题1.1 为什么仿真验证比直接上实车更合适做多机器人系统仿真不是一个可选项而是一个必选项。原因很简单多机器人系统的失效模式在真实环境中代价太高。两台机器人如果在仓库里撞在一起轻则任务中断重则设备损坏如果编队控制算法有bug实车上根本来不及调试。而在Gazebo仿真里我可以在几分钟内反复用不同数量的机器人、不同障碍布局去测试同一种算法出了问题直接把仿真世界重置就好。另外一个容易忽略的点是回放和可视化。多机器人系统的行为是涌现式的单看某一台机器人的日志很难看出问题你需要在同一时间轴上看到所有机器人的位置、速度、轨迹、队形误差。Gazebo加上RViz的组合能非常直观地把这些信息同时呈现出来。这种上帝视角在实车测试里很难获得。从技术验证角度看多机器人系统真正需要验证的核心问题有三个共享环境下的相互避碰、编队控制律的稳定性、以及机器人之间通信的可靠性。这三个问题在Gazebo中都能比较真实地模拟出来尤其是避碰因为每个机器人都可以通过仿真传感器感知到其他机器人。1.2 单机导航到多机编队的技术演进单机导航解决的是我从A点到B点怎么走的问题核心是定位、全局路径规划和局部避障。多机器人系统在这个基础上增加了两个维度空间维度上多个机器人共享同一张地图必须考虑彼此之间的避碰时间维度上多机器人需要协同任务这就引出了编队控制的问题。编队控制本质上是一个分布式控制问题。每个机器人既要完成自己的导航任务又要维持与相邻机器人的相对几何关系。从单机到多机的跨越看起来只是数量变多了实际上整个软件架构都要调整。我在做这个项目时最大的感触就是命名空间的重要性。单机导航里话题和TF树都是全局唯一的比如/cmd_vel、/map、/odom不会冲突。但到了多机环境三台机器人都叫base_link都发布/cmd_vel整个系统就乱了。所以在架构设计阶段必须给每个机器人分配独立的命名空间所有话题、TF、参数都带上前缀。这一步做不好后面全是坑。2. 仿真环境搭建与机器人模型准备2.1 ROS版本选型与环境安装我用的方案是Ubuntu 20.04 ROS Noetic Gazebo 11。选这个组合主要考虑的是稳定性。ROS 2 的 Nav2 功能包虽然很强大但多机器人相关的配置相对复杂而 ROS 1 的 move_base 加上namespace的支持非常成熟社区资料也多遇到问题更容易查。如果你是从零开始学我更推荐先用 ROS 1 把整体流程跑通理解了多机协同的核心逻辑之后再迁移到 ROS 2 也不迟。环境安装这一块我是用鱼香ROS的一键安装脚本完成的。这个工具确实帮了大忙它会自动处理 Ubuntu 版本检测、ROS 软件源配置、rosdep 初始化等一系列麻烦步骤。运行完脚本之后Gazebo 和 RViz 都可以直接启动中间没有遇到什么版本冲突的问题。当然如果你之前已经装过 ROS那就直接复用现有环境不必重新装。装好之后我会习惯性地跑一遍完整验证source /opt/ros/noetic/setup.bash roscore rosrun gazebo_ros gazebo能正常打开 Gazebo 空世界并且看到 ROS 通信建立成功就说明基础环境没问题了。2.2 多机器人模型的生成与批量启动这个项目里我使用的是差速驱动机器人模型结构很简单一个底座、两个驱动轮、一个万向轮、一个二维激光雷达。在 Gazebo 里创建多个机器人的方式通常有两种思路。第一种是把多个机器人模型写进同一个 world 文件中Gazebo 启动时一次性加载。这种方式的优点是启动快、不需要额外脚本但缺点是机器人的 URDF 模型是静态写死的后续如果想改机器人数量就要重新编辑 world 文件非常不灵活。第二种方式是在空的 Gazebo 世界里通过 ROS 的spawn_model服务动态生成机器人。我用的是这种方式因为可以通过一个 Python 脚本灵活控制机器人的数量、初始位置和命名空间。核心逻辑如下import os import yaml from launch import LaunchDescription from launch.actions import ExecuteProcess from launch_ros.actions import Node def generate_launch_description(): ld LaunchDescription() robots [ {name: robot1, x: 0.0, y: 0.0, yaw: 0.0}, {name: robot2, x: -2.0, y: 0.0, yaw: 0.0}, {name: robot3, x: 2.0, y: 0.0, yaw: 0.0}, ] for robot in robots: ld.add_action(ExecuteProcess( cmd[rosrun, gazebo_ros, spawn_model, -urdf, -param, robot_description, -model, robot[name], -x, robot[x], -y, robot[y], -Y, robot[yaw]], outputscreen )) return ld这里有一个很关键的点-param robot_description指的是在参数服务器上的 URDF 名称。如果三台机器人要从同一个 xacro 文件生成就需要把它们分别加载到不同的参数键下比如/robot1/robot_description、/robot2/robot_description然后在 spawn 的时候指定对应参数。如果三台机器人都从同一个robot_description读取最后 spawn 出来的会是三台一模一样的名字冲突模型Gazebo 会直接报错。2.3 仿真环境地图的构建与精度考量多机器人导航实验需要一个能让三台机器人同时跑起来不失真的地图。我不建议一开始就在复杂迷宫里测试先用一个室内场景大概 20 米×15 米中间放几个方形的障碍物就够了。这样既能让编队有用武之地又不会因为地图太复杂导致定位不稳定干扰对编队算法的判断。地图的获取方式有三种用 Gazebo 自带的 Building Editor 手动画墙、加载别人做好的模型、或者用 SLAM 建图工具实时构建。我的经验是仿真场景里的地图最好手动建因为你要保证静态障碍的坐标精确对齐。如果用 SLAM 建图激光雷达的误差会导致地图和模型之间存在偏移看起来是小问题但在多机器人共享地图的时候这个偏移会被放大出现机器人明明绕开了墙另两台却说它撞了墙的诡异现象。如果导航方案里需要用到 3D 环境感知可以考虑用八叉树地图OctoMap作为补充地图格式。体素网格能更精细地描述三维障碍物分布但计算开销也大。对于最初的多机导航验证2D 栅格地图完全够用等跑通了再升级也不迟。3. 多机器人导航系统的关键实现3.1 导航框架选型move_base 的命名空间配置在我用的 ROS Noetic 环境下导航框架选择move_base是顺理成章的。但多机器人场景下每个机器人需要启动一套独立的 move_base 实例并且每个实例都要在独立的命名空间下运行。这个namespace不是可选项而是必须项。以三台机器人robot1、robot2、robot3为例合理的节点和话题结构应该是/robot1/map /robot1/odom /robot1/scan /robot1/move_base/goal /robot1/move_base/cancel /robot2/map /robot2/odom /robot2/scan /robot2/move_base/goal /robot2/move_base/cancel ...在 launch 文件里我会用 group 标签把每个机器人的所有节点包裹进去然后统一加命名空间group nsrobot1 node pkgmove_base typemove_base namemove_base param namebase_frame valuerobot1/base_link/ remap frommap tomap/ remap fromodom toodom/ remap fromscan toscan/ /node /group这里特别要注意的是成本地图的配置。在单机导航里代价地图的观测来源只有一个激光雷达。但在多机环境下如果每个机器人的局部代价地图只订阅自己的/scan它就无法感知到其他机器人的存在两条机器人在窄通道里相遇就会互相看不到对方直接走向碰撞。这个问题的解决办法我在 3.3 节展开说。3.2 TF 树与坐标变换的隔离策略多机器人系统里最容易翻车的就是TF树的混乱。单机导航的TF树是一条清晰的链路map - odom - base_link - laser。但如果多个机器人的base_link都挂在同一个map坐标系下RViz 里所有机器人的模型会挤在同一位置然后疯狂抖动导航节点也会因为无法正确区分机器人的位姿而出错。正确的做法是让每个机器人的 TF 链路都带上命名空间前缀。也就是说robot1 的 TF 链路是robot1/map - robot1/odom - robot1/base_link - robot1/laserrobot2 是robot2/map - robot2/odom - robot2/base_link - robot2/laser以此类推。在实现层面每个机器人的robot_state_publisher发布的是 URDF 中 link 之间的固定变换这些 frame id 在模型文件里已经写好了所以要让它们都带上前缀。我习惯在 xacro 文件里直接用参数传入命名空间或者在启动时用xacro的--symbol参数把前缀传进去。验证 TF 是否正确最直接的方法是启动后运行rosrun tf view_frames或者直接在 RViz 里查看 TF 树。如果能在 TF 树里看到三套独立的链路说明隔离做对了。这一步如果不出问题后面的导航基本就成功了一半。3.3 多机器人相互感知与避碰多机器人避碰是导航系统里最需要细心处理的部分。前面说过如果每个机器人的代价地图只感知自己的激光雷达机器人之间就是盲人摸象。要解决这个问题我采用了两个层面的方案。第一个层面是共享位置信息。每个机器人把自己的位姿发布出去其他机器人在做代价地图更新时把这些位姿当作动态障碍物叠加到代价地图里。具体实现路径是robot1 的 move_base 在局部代价地图中订阅一个话题/other_robots这个话题由中央节点汇总 robot2 和 robot3 的实时位姿。收到消息后在obstacle_layer之外额外维护一个footprint_layer根据接收到的位置信息更新障碍物栅格。第二个层面是规划时的速度限制。仿真的激光雷达感知范围是有限的当两台机器人接近到一定距离时仅仅靠代价地图的膨胀半径可能来不及减速。我在 move_base 的局部规划器参数里把max_vel_x和min_vel_x做了动态调整当检测到目标机器人距离小于 1 米时把最大线速度从 0.5 m/s 降到 0.2 m/s。这个逻辑不复杂但对避免碰撞非常有效。下表是我在项目里用的关键参数可以作为参考参数值说明robot_radius0.20 m机器人底盘半径inflation_radius0.30 m代价地图膨胀半径obstacle_range3.0 m激光障碍物探测距离raytrace_range3.5 m激光射线追踪距离max_vel_x0.5 m/s最大线速度min_vel_x-0.1 m/s最小线速度max_vel_theta1.0 rad/s最大角速度注意多机器人避碰不能完全依赖规划器的代价地图那是被动防御。更可靠的做法是同时使用速度限制和代价地图叠加这样在规划层面和运动控制层面都有了保障。4. 编队控制算法设计与实践4.1 编队控制策略的对比与选型编队控制是这类项目的高级玩法也是最有意思的部分。常见的方法大致有三种领航跟随法、虚拟结构法和基于行为法。我在做方案选型时把它们的优缺点列了一张表策略核心思想优点缺点领航跟随法指定一台机器人作为领航者其他机器人与领航者保持相对位姿关系实现简单稳定性好易于理解领航者故障会导致编队崩溃虚拟结构法把整个编队看成一个刚体每台机器人在刚体中占据固定位置队形保持精度高适合直线队形转弯时机器人容易产生大角度偏差基于行为法为每台机器人设计避碰、队形保持、目标追踪等行为加权输出灵活性高适应性强参数调试难度大行为冲突时难以预测我最终选择了领航跟随法原因是这个项目的核心验证目标是多导航协同和队形保持的稳定性领航跟随的模型最简单控制律的物理意义也最清晰出问题时容易定位到具体环节。其他方法后续可以在这个框架上扩展。4.2 领航跟随法的控制律实现领航跟随法的基本思路是领航机器人走正常的导航路径跟随机器人接收领航者的实时位姿再计算出自己期望的位置并生成速度指令去跟踪这个期望位置。假设领航者在全局坐标系下的位姿是 ( (x_l, y_l, θ_l) )跟随者与领航者的期望相对位置是 ( (dx, dy) )那么跟随者的期望位姿就是x_f_des x_l dx * cos(θ_l) - dy * sin(θ_l) y_f_des y_l dx * sin(θ_l) dy * cos(θ_l)得到期望位姿之后我用一个比例控制器来计算跟随者的速度。角度误差通过atan2(sin(error), cos(error))归一化到 [-π, π] 区间线速度和角速度分别计算dx x_des - x_actual dy y_des - y_actual angle_error normalize_angle(target_yaw - yaw_actual) dist_error math.sqrt(dx*dx dy*dy) vx kp_linear * dist_error wz kp_angular * angle_error这里注意ki和kd可以根据实际效果加上但多数情况下比例控制就够了。我给每个跟随者都加上了一个最大线速度限制设为 0.4 m/s防止领航者急转弯时跟随者冲得过猛。把控制律在仿真中跑起来之后最明显的效果是三台机器人在直线和缓转弯路段能保持不错的三角形编队队形误差可以控制在 10 厘米以内。这个精度对于仿真验证来说已经足够了。4.3 通信频率与系统实时性对编队的影响编队控制的稳定性不仅取决于控制律本身还取决于机器人之间的通信质量。在Gazebo仿真里这个话题不太容易被注意到因为所有节点都在同一台机器上跑话题通信没有网络延迟。但在真实系统中通信延迟是编队控制的最大敌人。我在仿真里做了一个模拟延迟的测试在话题传输路径上人为加延迟从 50ms 逐渐增加到 500ms。结果在 200ms 以下跟随者还能保持队形只是误差略有增大到了 300ms 以上在转弯路段跟随者开始出现明显的摆动队形基本保持不住了。这说明了两个问题。第一编队控制律要对位姿更新频率有适应性不能指望始终拿到最新数据。第二在设计通信拓扑时应该避免所有机器人之间都建立双向通信那样通信量太大。更合理的方案是跟随者只订阅领航者的位姿不订阅其他跟随者的位姿也就是星型拓扑。这样通信链路最少延迟影响也最小。5. 常见问题与排查技巧实录5.1 仿真卡顿多机器人系统的性能瓶颈三台机器人同时跑导航和编队Gazebo的CPU占用会迅速飙升。我在第一次全系统联调的时候仿真帧率掉到了 10fps 以下机器人移动起来跟幻灯片一样。这个问题的根源在于每台机器人都有独立的激光雷达传感器各有 360 个光束在做射线检测三台就是 1080 条射线每一个仿真步都要计算压力极大。我的优化手段依次是降低激光雷达的 beam 数量。把 angle_increment 从 1 度改为 3 度360 根减为 120 根对导航精度影响很小CPU 占用明显下降。更新物理频率。Gazebo 默认的 physics update rate 是 1000Hz对多机器人仿真来说太高了我调到了 200Hz。关闭不必要的渲染。用headless模式运行 Gazebo不使用 3D 渲染窗口。经过这三步优化仿真帧率恢复到了接近实时 30fps三台机器人正常编队导航不卡顿。5.2 TF 树错乱导致的多机器人位置漂移这个问题的症状非常典型RViz 里三台机器人的模型忽隐忽现位置严重跳变导航轨迹乱成一团。排查下来根因是 robot_state_publisher 和 move_base 中的base_frame配置不一致部分节点用了base_link另一部分用了robot1/base_link导致 TF tree 出现了多个分支坐标变换链断裂。排查方法是在线查看 TF 树rosrun tf view_frames evince frames.pdf如果你能在生成的图中看到三套完整的、彼此独立的 TF 链路说明隔离正确如果看到某些base_link没有后缀出现在地图下说明某些节点在发布 TF 时没有加命名空间前缀。修复方法是检查所有节点的frame_id配置统一加上机器人前缀。5.3 编队振荡跟随者对领航者转弯的过度响应编队跑起来之后最常见的坑是跟随者在领航者转弯时出现振荡。我的 robot2 在跟随时每当 robot1 转弯它就开始左右摆动幅度越来越大甚至会甩出队形。这个问题的核心原因是速度指令更新频率不够导致跟随者始终在追一个自己刚算出来的目标点。经过排查问题出在比例控制器的角速度增益太大。领航者转弯时跟随者的角度误差瞬间变大控制器输出了一个很大的角速度指令但由于机器人模型的惯性和指令更新延迟实际转角超出了期望值误差符号翻转又触发反向的大角速度输出形成振荡。解决方案有两步一是给角速度控制加上 D 项增加阻尼二是限制角速度指令的更新率或者说对角度误差做低通滤波。我在代码里加了angle_error_smoothed 0.7 * angle_error 0.3 * previous_angle_error让转角响应变得柔和振荡立刻消失。这个问题的教训是编队控制的比例增益不能一味求快要根据实际系统的响应速度来匹配。仿真环境里机器人模型的惯性参数未必准确调参时宁可保守一些也要优先保证稳定性。最后说点实在话整个项目跑下来最大的体会是多机器人仿真的技术难点并不在于某一台机器人的导航或某个编队算法而是整个系统的集成和协调。你需要同时保证命名空间隔离清晰、TF 树不打架、避碰策略生效、编队控制律稳定任何一个环节出问题都会让整个系统表现出一种说不清哪里错了但就是不对劲的状态。如果你正在做类似的项目我建议先别急着追求复杂的编队算法。先把两台机器人的独立导航跑通再让它们共享地图相互避碰最后才加编队控制。一步一步来每一步的可信度都确认了再往前走这样调试的难度会降低很多。我自己当时就是先验证了双机避碰再扩展到三机编队整个过程的思路会清晰很多。本文还有配套的精品资源点击获取
返回列表