ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

ROS2 Humble AGV自主导航仿真:Gazebo建模与避障实战

ROS2 Humble AGV自主导航仿真:Gazebo建模与避障实战 简介本资源是一个面向ROS2初学者与移动机器人开发者的AGV自主导航仿真教学项目基于ROS2 Humble框架在Gazebo模拟环境中完整实现小车建模、SLAM建图、全局路径规划如Nav2、局部避障DWA/TEB及RVIZ可视化全流程。资源共507个文件涵盖24个STL机械模型、2个URDF/SDF机器人描述文件、2个World仿真场景、7个YAML配置参数、12个Python节点脚本含导航控制与状态发布、9个Shell启动脚本及大量日志与构建辅助文件CMake、make、colcon相关压缩包仅2.38MB轻量易部署。已有176人学习下载适合高校课程实验、毕业设计原型开发或ROS2导航模块专项训练。读者可直接复现从环境搭建、参数调优到动态避障的完整闭环配套结构清晰的agv_bot功能包与本地化setup脚本显著降低ROS2导航系统入门门槛。基于ROS2 Humble的AGV自主导航仿真从Gazebo建模到路径规划避障实战做机器人导航开发这几年最大的感受是算法写得再漂亮没有一套可靠的仿真环境做验证照样不敢往真机上跑。尤其是AGV这类需要长时间运行、反复调试路径规划与避障策略的移动平台每次都在实体车上试错时间和硬件成本都扛不住。所以我自己在做AGV项目时主力调试环境就是基于ROS2 Humble Gazebo的仿真平台。这篇文章要聊的就是这样一个仿真项目的完整落地过程在ROS2 Humble框架下搭建一台差速驱动的自主导航小车模型放进Gazebo模拟环境让它跑起来完成SLAM建图、路径规划、实时避障这一整套导航闭环。无论你是正在做AGV项目选型验证还是刚入门ROS2导航想找一份能直接上手的参考这套流程都能帮你少走不少弯路。1. 项目整体拆解把AGV仿真这件事看透1.1 这个仿真项目的三个核心模块从标题就能看出来这个项目本质上拆开是三件事仿真环境Gazebo、导航框架ROS2 Nav2、机器人本体AGV小车模型。三者靠ROS2 Humble这条主线串起来。小车本体我用的是一辆差速轮底盘的AGV模型两个主动驱动轮加一个万向支撑轮顶部装一枚2D激光雷达车前和车尾各配一颗RGBD深度相机。激光雷达负责SLAM建图和导航避障RGBD相机则主要做视觉辅助比如障碍物识别、深度图避障这些扩展功能。仿真环境Gazebo里布置了一个室内仓库场景有墙壁、货架、立柱还有几个会移动的障碍物用Gazebo的actor或者直接给模型加运动插件。这些动态障碍物才是真正考验避障策略的地方——静态地图里的路径规划只是及格线能实时躲开移动障碍才算真本事。导航闭环小车先在手动遥控下用SLAM Toolbox扫描环境完成建图保存地图之后切换到Nav2自主导航模式。给定一个目标点Nav2负责全局路径规划、局部路径跟踪、以及基于代价地图的实时避障整个流程不需要人为干预。这三个模块的关系一句话总结就是模型是身体Gazebo是舞台Nav2是大脑。1.2 为什么选ROS2 Humble而不是ROS1或更高版本说实话ROS1的Navigation Stackmove_base那套在AGV领域沉淀了很多年网上资料多得看不完。但它的短板也很明显没有原生支持多机通信、没有生命周期节点管理、实时性保障差。AGV调度系统往往涉及多台车、多台调度服务器之间的通信ROS1的roscore单点架构在规模化之后会很痛苦。ROS2 Humble是长期支持版本LTS支持到2027年而且和Ubuntu 22.04的组合是官方认证的最佳搭配。相对更新的版本比如Iron、JazzyHumble的生态更成熟——Nav2、slam_toolbox、gazebo_ros_pkg这些核心组件在Humble上都经历了充分验证踩坑少。做项目不是追新稳定压倒一切这是我在选型时反复权衡后的结论。2. 环境搭建Ubuntu 22.04 ROS2 Humble Gazebo的完整组合2.1 Ubuntu版本选型与ROS2 Humble安装先说结论如果你不是有特殊原因必须用Ubuntu 24.04老老实实装Ubuntu 22.04。网上很多人问Ubuntu 24.04能不能搭Gazebo slam_toolbox Nav2官方支持上确实能装但那些第三方依赖包、二进制预编译版本的兼容性坑能磨掉你整整一天时间。我在虚拟机里实测过最终为了省时间切回了22.04。安装ROS2 Humble官方deb源的三条命令是最靠谱的sudo apt update sudo apt install curl -y curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.key -o /usr/share/keyrings/ros-archive-keyring.gpg sudo apt update sudo apt install ros-humble-desktop -y装完后务必初始化rosdep并配置环境变量这是新手最容易忽略的两步sudo rosdep init rosdep update echo source /opt/ros/humble/setup.bash ~/.bashrc source ~/.bashrc注意如果你用的是虚拟机内存至少给到8GB处理器4核以上硬盘留出40GB空闲空间。Gazebo的物理引擎和渲染吃资源很凶配置不够的话仿真帧率会掉到惨不忍睹导航算法在低帧率下的表现会严重失真。2.2 Gazebo 11与ROS2桥接gazebo_ros_pkg的作用Ubuntu 22.04软件源里默认的Gazebo是11.x版本而这个版本和ROS2打交道全靠gazebo_ros_pkg这个桥接包。它做的事情简单说就是把Gazebo的ROS2接口打通——让仿真世界里的激光雷达数据能发到/scan话题让小车模型能订阅/cmd_vel速度指令动起来。安装桥接包和相关依赖sudo apt install ros-humble-gazebo-ros-pkg ros-humble-gazebo-dev ros-humble-gazebo-plugins -y装完可以快速验证一下Gazebo能否正常启动source /usr/share/gazebo/setup.bash gazebo --verbose如果弹出空的世界窗口且控制台没有红色报错说明环境已经通了。这里有个小坑每次新开终端都要source一遍Gazebo的环境变量否则gazebo_ros相关插件加载时会报找不到共享库的错误。2.3 我的工作空间与package布局整个项目的代码我放在一个agv_sim_ws工作空间里package结构是这样组织agv_sim_ws/src/ ├── agv_description/ # URDF模型、xacro宏、rviz配置 ├── agv_gazebo/ # world文件、launch启动脚本、模型材质 ├── agv_navigation/ # Nav2的参数配置、地图保存与加载 └── agv_slam/ # slam_toolbox建图相关的launch和配置这种分包方式的好处很直接模型、仿真环境、导航算法各管各的改一个模块不影响其他模块的代码。比如要换激光雷达型号只需要改agv_description里的URDF要调整代价地图的膨胀半径进agv_navigation的param文件就行。工作空间编译用colconcd ~/agv_sim_ws colcon build --symlink-install source install/setup.bash--symlink-install这个参数强烈推荐加上它会用符号链接安装Python代码和launch文件改完脚本不用重新编译就能生效迭代效率会高很多。3. 机器人建模从URDF到Gazebo里的完整AGV小车3.1 差速轮底盘links、joints与传动模型URDF是描述机器人运动学模型的核心文件。差速轮AGV的建模思路不复杂就是三个link车身、左轮、右轮加两个continuous类型的joint车轮与车身的旋转关节。车身部分我加了一个1.2米×0.8米×0.3米的矩形底盘两个直径0.25米的驱动轮分布在车身两侧中心。材质上用了带纹理的金属感外观方便在RViz里做视觉辨别。Gazebo里的物理仿真和RViz不一样RViz只需要统一的描述文件而Gazebo需要额外为每个link补充摩擦系数和惯性参数。所以URDF里必须带上gazebo标签给车轮设置mu1和mu2摩擦系数。这个参数我实测下来对AGV的启停影响很大——设得太小车轮会原地打滑走不动设得太大转弯时又容易抖动0.8左右是相对稳妥的起点。为了让Gazebo里的小车能被ROS2控制我使用了gazebo_ros2_control插件再配一套ros2_control的控制器配置controller_manager: ros__parameters: update_rate: 100 joint_state_broadcaster: type: joint_state_broadcaster/JointStateBroadcaster diff_drive_controller: type: diff_drive_controller/DiffDriveController diff_drive_controller: ros__parameters: wheel_separation: 0.6 wheel_radius: 0.125 linear.x.max_velocity: 1.2 angular.z.max_velocity: 1.5这一步做的是把/cmd_vel的Twist速度指令转换成左右轮各自的角速度驱动Gazebo里的小车底盘运动。3.2 传感器配置2D激光雷达与RGBD深度相机这台AGV我配了一个单线激光雷达放在车顶正中心扫描范围360度最大测距20米频率10Hz。激光雷达的安装高度很关键距离地面0.35米——太低会被底盘自身遮挡视野太高则扫描不到低矮障碍物这个高度值是参考了货架AGV应用场景中常见货架高度后选的。RGBD相机用的是类似Intel Realsense D435的模型参数一个放在车头保险杠位置一个放在车尾安装角度略微向下倾斜15度这样可以兼顾近距离地面障碍物和前方中距离的货架。Gazebo里RGBD相机的深度点云数据可以发布到ROS2话题上作为视觉辅助避障的输入源。3.3 在Gazebo中搭建仓库场景并放置动静态障碍物Gazebo的world文件我用XML手写了一个20米×15米的室内仓库四周是墙内部摆放了若干货架和立柱地面铺设了灰色纹理的floor。静态障碍物直接放模型动态障碍物则用Gazebo的actor标签实现——actor可以让一个人形模型沿着预设轨迹来回走动我在主通道上放置了三个巡逻的actor模拟AGV运行环境中的人员走动场景。如果你不想手写world文件也可以在Gazebo图形界面里手动添加模型后用File - Save World As导出XML。不过手动添加的模型坐标往往不够精确我建议在代码里直接定义位置这样每次启动仿真都能保证场景完全一致导航路径的可复现性才有保障。4. SLAM建图让AGV先认识作业环境再谈导航4.1 slam_toolbox与cartographer怎么选ROS2生态下做2D激光SLAM主流方案就是slam_toolbox和cartographer。对于AGV这个场景我的选择是slam_toolbox没有悬念。原因有三点slam_toolbox是纯2D激光SLAM计算开销小Gazebo仿真里跑起来CPU占用很低笔记本上也能流畅运行它支持长期运行时的图优化和闭环检测在仓库这种特征重复较多的环境里表现不错配置远比cartographer简单——后者要调一堆子图、回环、扫描匹配的参数新手上手成本太高slam_toolbox的launch配置核心就一个参数文件其中比较关键的是scan_topic激光话题名、max_laser_range激光最大量程和solve_timeout图优化求解超时时间slam_toolbox: ros__parameters: scan_topic: /scan max_laser_range: 20.0 solve_timeout: 10.0 minimum_travel_distance: 0.5 minimum_travel_heading: 0.54.2 手动遥控建图的完整流程建图这个环节虽然没有太多技术难度但操作细节直接影响地图质量。我的标准做法是启动Gazebo仿真环境和机器人模型启动slam_toolbox节点启动teleop_twist_keyboard键盘遥控节点用键盘控制AGV在整个仓库里慢速巡检一遍ros2 run teleop_twist_keyboard teleop_twist_keyboard --ros-args -r /cmd_vel:/cmd_vel遥控建图有两条经验速度要放慢AGV的线速度控制到0.3m/s以下转角速度0.5rad/s左右。跑太快激光帧匹配会漂地图边缘会出现重影。绕墙闭环路径设计尽量让AGV把仓库外圈完整跑一遍并且回到起点附近这样闭环检测才能发挥作用地图不会出现“开口”的漂移。建图过程中实时观察RViz里的地图变化如果发现局部重影立即停下来原地转360度让激光重新对齐这是最实用的地图修整技巧。4.3 保存地图与后续加载等地图完整呈现后保存地图ros2 run nav2_map_server map_saver_cli -f ~/agv_sim_ws/src/agv_navigation/maps/warehouse这会生成warehouse.pgm和warehouse.yaml两个文件。pgm是栅格图片yaml里记录坐标分辨率、原点位置等元信息。保存后建议用图像工具打开pgm看一眼确认边缘清晰、没有大块黑洞残留未探测区域。如果有零星的黑点噪点可以用图像编辑器抹掉但注意不要动墙壁结构本身否则导航时小车的定位会和对不上的地图发生漂移。5. Nav2路径规划与避障策略这套仿真里最核心的部分5.1 Nav2架构梳理Planner、Controller、Costmap、BTNav2是ROS2原生的导航框架它拆成好几个独立节点协同干活。要用好Nav2先把角色关系搞清楚Planner Server负责全局路径规划基于地图从起点到目标点算出一条最优/次优路径Controller Server负责局部路径跟踪把全局路径切成小段配合实时传感器数据输出速度指令Costmap全局和局部两张代价地图把障碍物栅格化并做膨胀处理导航避障的一切决策都以代价地图为依据BT Navigator行为树导航器管理导航任务的状态流转比如“先算路径、再跟踪路径、遇到障碍重规划”Nav2的一大优势是所有模块的参数都可以在线动态配置。在导航运行中用ros2 param set直接改代价地图的膨胀半径、或者切换局部规划器都能实时生效不用重启系统。这个特性在调参阶段排除了大量重启等待时间非常受益。5.2 全局路径规划算法A*和Dijkstra的选型逻辑全局路径规划器在Nav2里默认用的是NavFn它同时实现了Dijkstra和A*两种算法。从代码层面选择开关就是use_astar这个参数planner_server: ros__parameters: GridBased: plugin: nav2_navfn_planner/NavfnPlanner tolerance: 0.5 use_astar: trueA算法大家应该都熟悉它在Dijkstra的基础上引入了启发式函数h(n)用“当前节点到目标点的预估代价”来引导搜索方向让扩展节点数大幅减少。地图规模越大、场景越空旷A相对Dijkstra的提速效果越明显。我的AGV仓库地图有20米×15米实际测试下来A*路径搜索耗时大约在几十毫秒级别Dijkstra要慢3到5倍。但A并不总是最优选择如果代价地图里的障碍物非常复杂启发函数估计可能失效导致路径看似最短却要频繁绕障。Nav2的SmacPlannerHybrid-A支持阿克曼底盘的运动学约束更接近真实AGV的转弯半径限制——如果你的AGV是麦克纳姆轮或者全向轮用NavFn就够了如果是传统差速或阿克曼SmacPlanner能生成对车更友好的平滑路径。5.3 局部规划与实时避障DWA和TEB哪个更适合AGV全局路径只是“导航地图上的路线”真正决定小车能不能安全走过去的是局部规划器。在两个主流插件之间我做了实测对比DWADynamic Window Approach动态窗口法核心思路是在速度空间里采样多组线速度、角速度组合用运动学模型模拟每条轨迹再通过评价函数给轨迹打分选分最高的执行。评价函数通常包含三个因子朝向目标的夹角、距离最近障碍物的距离、和当前速度的接近程度。TEBTimed Elastic Band时间弹性带法把路径看作一条“弹性带”在满足运动学约束、避开障碍物、时间最短的多目标优化下迭代变形路径它天然考虑时间维度对动态障碍物的预测能力更强。我实测下来的对比结果对比维度DWATEB动态障碍物避让效果偏保守容易急刹平滑提前绕行参数调试难度参数少上手快参数多调起来费时在窄通道内表现容易来回摇摆相对稳定AGV场景适合度适合低速、简单环境适合中高速、动态场景如果你和我一样跑的是带多个动态actor的仓库场景我会倾向于选TEB但注意TEB的参数量较大而且对帧率比较敏感Gazebo仿真帧率掉到30FPS以下时它的优化效果会明显变差需要做硬件匹配。5.4 动态障碍物重规划与代价地图的交互机制AGV在真实场景里遇到最多的就是“地图上没有的障碍物”——箱、叉车、走过的人。Nav2处理这类情况的机制是局部代价地图的实时更新。局部代价地图订阅雷达和深度相机的数据以10Hz的频率把新障碍物描进一张以小车为中心的小范围代价地图里。当全局路径穿过新障碍物时Controller Server的路径跟踪会被阻挡此时机器人会做出判断本地能绕过去就绕过绕不过去就通知BT Navigator触发全局路径重规划。为了让这套机制更符合AGV的安全需求我给局部代价地图设置了三个关键参数local_costmap: local_costmap: ros__parameters: robot_radius: 0.3 inflation_radius: 0.4 observation_sources: laser_scan_sensor rgbd_sensor_front rgbd_sensor_backrobot_radius设为0.3米对应车体实际宽度的一半。这里我特别提醒不要直接把URDF里车体的半宽搬过来要留一点安全余量AGV在动态运动中会有惯性滑移。inflation_radius设置0.4米意味着栅格地图中障碍物周围0.4米内的区域都会被标记为高代价区域全局规划器会自动“绕开”这块区域避免小车贴着障碍物走。多传感器融合激光和RGBD同时做障碍物观测源这样既保证大范围扫描又补足了近处盲区。6. 实操过程从空地图启动到自主导航全流程复盘6.1 启动文件设计launch把六个节点串起来手动一个个启动节点太痛苦我把整个流程做成了三个launch文件分开复用第一个是gazebo_world.launch.py负责启动Gazebo世界并加载URDF到参数服务器再塞进仿真环境from launch import LaunchDescription from launch.actions import ExecuteProcess from launch_ros.actions import Node def generate_launch_description(): return LaunchDescription([ ExecuteProcess( cmd[gazebo, --verbose, os.path.join(pkg_gazebo, worlds, warehouse.world)], outputscreen ), Node( packagerobot_state_publisher, executablerobot_state_publisher, parameters[{robot_description: urdf_xml}] ), Node( packagegazebo_ros, executablespawn_entity.py, arguments[-topic, robot_description, -entity, agv], outputscreen ) ])第二个是online_async_slam.launch.py启动slam_toolbox建图节点同时把激光雷达的话题接好。第三个是navigation.launch.py一次性拉起Nav2的planner、controller、BT navigator、AMCL定位和costmapros2 launch agv_navigation navigation.launch.py map:/path/to/warehouse.yaml用map:参数指定建好的地图文件nav2会先加载地图再用AMCL做定位初始化然后RViz里就能看到小车在地图上的位姿。6.2 发布目标点让AGV自己规划路径导航启动完成后在RViz里点一下“Nav2 Goal”按钮在地图上选一个目标点设定目标朝向Nav2就会开始处理。发布目标点的话题是/goal_pose如果希望在命令行测试也可以直接用CLI方式发布ros2 topic pub -1 /goal_pose geometry_msgs/msg/PoseStamped { header: {frame_id: map}, pose: {position: {x: 6.0, y: 4.0, z: 0.0}, orientation: {w: 1.0}} }这条命令会发布一个到地图坐标(6.0, 4.0)的导航任务导航行为树的运行状态会实时打印在终端里同时你可以订阅/plan和/local_plan两个话题观察全局路径和局部路径的Visualization Marker。6.3 RViz可视化与参数动态调整RViz是观察Nav2内部机制的最佳窗口我建议配置这几个显示项/map话题显示加载的静态地图/global_costmap/costmap话题显示全局代价地图膨胀层用不同颜色标记/local_costmap/costmap话题显示实时更新的局部代价地图能直观看到动态障碍物被描进来/plan话题绿色线条显示全局路径/local_plan话题红色线条显示当前正在执行的局部路径/tf话题显示小车坐标系、雷达坐标系、地图坐标系之间的变换关系如果发现小车在某个路口反复摇摆别急着改代码先看局部代价地图在窄通道里是否把两侧墙壁的膨胀区域连成了一条不可穿越的“墙”。如果是优先调低inflation_radius或者把障碍物监测的传感器范围缩小让局部路径搜索有更多的可行域。7. 常见问题与排查技巧踩坑实录7.1 小车原地抖动、不前进、或Z轴漂移这三个问题我全部在Gazebo仿真里遇到过大感。原因各不相同排查方向也不一样现象可能原因排查手段小车原地抖动diff_drive_controller的线速度/角速度增益太高把linear.x.max_velocity降到0.5angular.z.max_velocity降到1.0试跑小车不前进车轮摩擦系数mu1/mu2设得太小在URDF里为车轮增加gazebo标签mu1和mu2都设为0.8以上Z轴漂移/腾空底座或车轮的惯性参数设置不合理检查所有link的inertial特别是转动惯量数值不要设置成正数极小的值调试这类动力学问题最快捷的办法是打开Gazebo的左下角状态栏实时看到每个link的坐标系和速度数据。如果小车在无指令时都自己往下沉说明惯性参数互相冲突逐项检查mass和inertia数值。7.2 AMCL定位漂移地图和激光对不上建图质量没问题但导航时定位漂移多半是初始位姿偏差过大或者AMCL参数不匹配。启动Nav2后第一件事用RViz的“2D Pose Estimate”按钮手动给小车一个初始位姿让激光点云和地图重叠。如果运行时仍然漂移调整AMCL的粒子数量和更新频率amcl: ros__parameters: min_particles: 1000 max_particles: 3000 update_min_a: 0.05 update_min_d: 0.1 resample_interval: 1粒子数太少在对称环境里容易丢失全局定位太多则消耗CPU。1000到3000是我实测里准确性和性能平衡较好的一个范围。7.3 动态障碍物会导致导航卡死反复重规划Gazebo里的actor沿着固定轨迹移动但Nav2的局部规划器并没有预测它们的运动意图——每当actor挡在路径前面代价地图就会把它标为障碍物规划器绕行后actor又走到新位置从而造成反复重规划甚至卡死。我实测了两个有效的优化方向把TEB的obstacle_proximity_ratio从默认值调小一点让局部路径规划器愿意更晚、更小幅度地绕行减少频繁改道在局部代价地图的观察源里给动态障碍物所在的传感器增加一个较小的obstacle_max_range避免太远距离的物体影响局部路径另外在车库或者仓库的真实场景里动态障碍物和高层货架经常构成“视线盲区”纯2D激光的避障能力确实有限。这时候RGBD深度相机可以作为一个补充的障碍物观测源用深度图像里的近距点云来做盲区障碍的探测并压在Nav2的本地代价地图里。这也是我在模型上放两颗RGBD相机的原因建议导航避障的管线里把它们也加进去作为激光盲区的兜底方案。8. 关于导航参数调试的一些实操心得用Nav2做AGV仿真和真机导航最怕的就是“看似能跑但一换环境就废”。我自己的参数调试流程是先固定场景跑通再逐步增大环境复杂度每换一步只改一个参数保留每次的配置文件对比。调参的正确顺序也很重要我先调全局规划器让路径合理再调局部规划器让路径可跟踪最后调代价地图的膨胀参数让避障行为贴合工况。如果一开始就纠结DWA和TEB的参数细节大概率会在局部绕不出来。Gazebo仿真相对于真机的最大优势是可以随时暂停、录像、回放调试逻辑错误时效率极高。但仿真环境的物理最理想化等这些问题都弄利落了真机上的标定和传感器噪声补偿仍然是一个必不可少的苦力环节。我个人在实际操作中的体会是AGV导航系统里90%的“诡异问题”都不是某个算法写错了而是坐标变换、代价地图数据源、传感器安装位置这些小事在作怪。所以在做这个仿真项目时我会建议你花时间把每一层数据流都弄清楚——激光数据从哪来、costmap如何更新、路径规划器在哪个坐标系下工作——这些基本功扎实了后面换算法、加传感器都只是水到渠成的事。最后再分享一个小技巧给AGV模型和所有测试目标点做一套统一的命名规范写launch和调试时能省下大量无谓的口头沟通成本。本文还有配套的精品资源点击获取
返回列表