尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

基于ROS与Gazebo的AGV仿真系统搭建与自主导航实战

发布时间:2026/9/27 1:01:38

资讯中心
01
ARTICLE

基于ROS与Gazebo的AGV仿真系统搭建与自主导航实战

基于ROS与Gazebo的AGV仿真系统搭建与自主导航实战
最近在一家做工厂物流的朋友那边蹭了个AGV项目他问我能不能在真车进场之前先在仿真里把车能跑、能停准、能多车调度这套逻辑验证明白。正好他们那边现场环境复杂直接拿真车试错成本太高于是我就用ROS加Gazebo搭了一套AGV工业运输系统的仿真环境。做完之后我们发现这套东西不只是给新人练手用的哪怕你是要搞真车部署、要验证调度算法也值得先在仿真里把坑都踩一遍。这篇文章就围绕从模型构建到自主导航这条线把整个项目里涉及的思路、模型、传感器、SLAM、move_base导航、多车A*调度以及我在实际调试中踩过的坑全部按步骤写清楚。适合刚入门ROS和Gazebo的读者当作一个完整的实战参考也适合在工业物流场景里做AGV预研的工程师拿来对比选型。1. 项目整体设计思路做AGV仿真最忌讳一上来就抓着一个点猛调比如只调雷达参数或者只跑导航结果整个系统串起来之后到处打架。我更习惯先想清楚我要在仿真里解决什么问题技术路线是什么每一层之间怎么通信。1.1 为什么选择ROS加Gazebo这套组合市面上机器人仿真工具不少Webots、CoppeliaSim、Unity加MLAgents都能做但AGV领域我最推荐ROS加Gazebo。原因其实很简单ROS的机器人生态已经事实上成了行业通用语言从传感器驱动到导航规划再到多机管理都有成熟的功能包你不需要从零造轮子。Gazebo作为仿真器和ROS的集成是原生级别的URDF模型加载、传感器数据发布、物理引擎交互这些最核心的需求它都帮你做好了。对于AGV这种场景我们要验证的无非是这四件事底盘运动学对不对、传感器能不能可信地感知环境、导航算法能不能规划出安全路径、多车之间会不会互相堵路。这四件事在Gazebo里都能闭环。尤其是物理引擎这块ODE、Bullet这些引擎可以模拟轮子打滑、负重状态下的加减速变化比单纯的路径规划器画线要真实得多。还有一个现实因素AGV相关的中文资料和开源代码绝大多数都跑在ROS1的Noetic版本上。就算项目最终要迁到ROS2先用ROS1把逻辑验证清楚再迁移效率往往更高。所以我这个项目选择了ROS Noetic配Ubuntu 20.04搭配Gazebo Classic 11。这个组合在稳定性上经过了大量用户验证踩坑成本最低。1.2 系统架构与技术选型我习惯把整个系统拆成四层每一层各管一摊调试定位问题的时候非常清晰。第一层是物理模型层负责描述AGV长什么样、轮子怎么分布、身体各部分的重量和惯量。这部分在URDF/XACRO文件里定义。第二层是仿真层由Gazebo负责它读取URDF模型并放在一个虚拟世界里同时通过插件模拟激光雷达、IMU、轮式里程计这些传感器的数据输出。第三层是功能层负责跑SLAM建图、AMCL定位、move_base导航这些核心算法。第四层是任务层这一层更偏应用比如一台AGV从A点取货到B点卸货多台AGV怎么排队都是在这一层实现。底盘选择上工业AGV最常见的是差速驱动两个主动轮加若干从动轮或万向轮模型简单、控制成熟、转弯半径灵活。Gazebo里有现成的差速驱动插件直接用就行。雷达方面选了2D激光雷达因为室内工业场景用单线雷达做定位和避障是主流方案数据量小、实时性高、算法生态成熟建模和导航都够用。3D视觉方案不是不能做但对仿真机的性能要求高了不少前期预研阶段没必要。导航方案采用navigation栈里的move_base全局规划器用A*或Dijkstra局部规划器用DWA。后续要扩展成多AGV系统时在调度层加一张路径占用时间表每台车规划完路径之后去查表冲突就让低优先级车辆等待或改道。整个架构做到后面其实非常接近真实的AGV调度系统。2. AGV底盘与传感器模型构建仿真的第一步是造车。很多新手在URDF上栽跟头一启动模型就乱飞或者轮子陷进地里其实都是模型描述有问题。URDF的核心思想不复杂把机器人拆成若干刚体每个刚体叫一个linklink和link之间用joint连接joint决定它们之间的相对运动方式。2.1 URDF/XACRO建模核心一个最简AGV底盘至少要有这几部分底盘本体base_link、两个主动轮wheel_left_link、wheel_right_link、一到两个万向支撑轮、还有放雷达和IMU的传感器安装座。每个link都要写清楚视觉visual、碰撞collision和惯性inertial三个属性。很多人只写视觉不写碰撞结果Gazebo里车和货架穿模或者雷达扫描不到东西还有人不写惯性模型加载后直接翻车。我习惯用XACRO而不是直接写URDF因为XACRO支持宏定义和数学表达式。两台轮子参数几乎一样写一个wheel宏传不同参数就能生成左右轮改轮距的时候只改一个变量就行。下面是一个核心片段可以作为参考xacro:macro namewheel paramsprefix x_reflect link name${prefix}_wheel visual geometry cylinder radius0.11 length0.08/ /geometry origin rpy0 0 0 xyz0 0 0/ /visual collision geometry cylinder radius0.11 length0.08/ /geometry /collision inertial mass value2.0/ inertia ixx0.02 ixy0 ixz0 iyy0.03 iyz0 izz0.02/ /inertial /link joint name${prefix}_wheel_joint typecontinuous parent linkbase_link/ child link${prefix}_wheel/ origin xyz${0.28 * x_reflect} 0 -0.11/ axis xyz0 0 1/ /joint /xacro:macro特别注意joint的origin坐标它定义了轮子相对底盘的位置。轮子半径0.11米那么轮子关节的z坐标就得是-0.11让轮子底部正好和底盘底面对齐这样模型放在地面上才是正常姿态。还有一个坑是惯性张量。对于圆柱体轮子转动惯量可以直接按圆柱公式估算ixx和izz通常比iyy大因为轮子绕直径方向的转动惯量相对小绕轴的转动惯量要看轮辐分布。如果随便填一组很小的值Gazebo物理引擎解算时就会出现高频抖振车像得了帕金森一样。解决方法是先把质量填对再用常见几何体的惯量公式算一遍。2.2 传感器插件配置模型建好了之后要给AGV装上眼睛和感知器官。Gazebo里所有传感器都是通过插件实现的。2D激光雷达用的是ray sensor系列插件IMU有imu传感器插件轮式里程计可以直接通过差速驱动插件发布odom话题不需要额外建模。雷达配置里最关键是那几个参数更新频率、激光线束数量、角度范围和最大测距。我常用的是10Hz、360度范围、0.25度分辨率对应1440个采样点最大测距30米。室内仓库场景需要注意如果雷达安装高度太低扫描会打到地面凸起太高又可能漏掉低矮障碍物。工业AGV的雷达一般装在0.2到0.4米高度这个高度正好能扫到货架腿和托盘边缘。IMU的作用主要是提供角速度反馈帮助定位算法在机器人转弯时修正朝向。很多人觉得摄像头对AGV导航没必要但如果你后面要加二维码识别、托盘对接那仿真里就得提前加好相机link和插件。我在这个项目里预留了一个朝下安装的相机位置方便后面做二维码定位扩展。传感器数据发布到ROS之后一定要养成用rqt_graph看话题流向的习惯。我调试时见过不少次模型加载了、Gazebo也没报错但rostopic list里根本没有scan话题八成是插件参数里namespace或者frame_id拼错了。2.3 从Blender到Gazebo模型导出避坑很多项目为了演示效果好会先用Blender建一个精细的外观模型再导入Gazebo。这个流程确实能出效果但坑也多。第一个坑是单位。Blender默认米制但导出ColladaDAE格式时有的版本会默认厘米导致导入之后模型尺寸放大了100倍。所有内容瞬间变成哥斯拉。建议导出前先检查Blender场景单位导出后到Gazebo里量一下模型包围盒再继续用。第二个坑是碰撞体不能太精细。URDF里碰撞模型应该用尽量简单的几何体比如方盒、圆柱体这样物理引擎跑得快、也不容易卡顿。外观模型可以精细但碰撞体尽量用原始几何体代替。如果你把几万面片的精美房车模型直接当碰撞体用Gazebo的碰撞检测计算量会直接爆炸仿真帧率掉到惨不忍睹。第三个坑是坐标轴朝向。Blender里默认z轴向上但有些三维软件是y轴向上。导出的模型位置和朝向不对车会躺在地面上。解决办法是在Blender里先把模型摆正再在URDF的visual和collision标签里补一个origin偏移来微调。我的经验是工业仿真里外观没有想象中重要。轮子、底盘、雷达安装位置对了功能就能验证一张干净的白色底盘子配上黑色轮子完全够用。外观留给最后汇报演示时再加优先把逻辑跑通。3. 仿真世界与传感器细节模型本身没问题了接下来的重点就是让Gazebo环境尽量贴近真实车间。这里包括地面的物理属性和传感器的噪声很多人忽略了后者的重要性导致仿真里一切完美上真车就完蛋。3.1 Gazebo世界文件与物理参数Gazebo世界文件.world定义了仿真环境里有什么、物理引擎怎么工作。一个典型的仓库场景至少要有地面、墙面、货架、通道标志线。开源的仓库模型很多也可以自己用Gazebo提供的建模工具搭建。比起环境外观我更关注物理参数设置。物理引擎我一般选ODE稳定且速度可接受。关键参数是max_step_size和real_time_update_rate。max_step_size代表物理仿真的步长一般设0.001秒到0.005秒之间。步长越小越精确但CPU消耗成倍增长步长太大会出现穿透、抖动看起来很假。real_time_update_rate设1000左右表示每秒种更新1000次物理计算两个参数配合不对就会出现仿真时间比真实时间慢的现象。地面摩擦也很关键。货车行驶在环氧树脂地面和水泥地面打滑程度完全不一样。在模型插件里通过mu1、mu2、kp、kd这些参数控制滑动摩擦、滚动摩擦和接触刚度值设小了车会漂移设大了车转弯会显得过度抓地。我一般先把mu1和mu2设为1.0左右跑起来再根据转弯和刹车表现微调。还有一个细节是负重。工业AGV基本都是带货跑的在车尾加装一个load参数或者直接在Gazebo里放一个货架模型用固定关节绑到车体上。这样可以验证载重之后加速变慢、停车距离变长这个真实物理行为对后面调度算法的时间窗计算非常有用。3.2 给传感器增加真实感噪声与频率默认的Gazebo传感器是理想传感器数据干净得像纸面参数但真机不是这样。雷达会跳点IMU会漂移里程计会打滑。不处理这些噪声你在仿真里辛苦调好的参数部署到真车时往往会失灵。给激光雷达加噪声有几个常用手段。第一个是加高斯噪声在传感器插件里通过noise元素设置均值和标准差。第二个是模拟测距丢失让雷达在某些角度偶尔返回极大值相当于真机遇到透明或高反光物体时的丢点现象。第三个是限幅超过最大量程的数值直接截断。这些设置能让costmap更真实地出现误障碍物或者间隙逼着导航算法去应对这些意外。更关键的是里程计。Gazebo的差速驱动插件默认发布的是理想里程计轮子转了多少位置就变化多少。但实际上轮胎会磨损、地面会打滑。我一般会在里程计发布链路里叠加一个小的线性漂移量或者直接写一个简单节点订阅理想odom再叠加高斯噪声后转发到真正的导航节点使用。这样后面调试AMCL时才会发现单纯靠轮速积分根本定位不准必须融合激光和粒子滤波这才是真车上的常态。4. 从建图到自主导航核心算法怎么落地模型和环境都有了现在就到了这个项目最核心的部分让AGV知道自己在哪、要去哪、怎么去。这一章我会从头到尾拆解2D激光SLAM、move_base导航框架以及多台AGV基于A*的调度思路。4.1 2D激光SLAM选型与实操AGV定位建图方案最常见的是gmapping、slam_toolbox和cartographer三选一。gmapping是老牌方案代码简单但对计算资源要求随地图增大而快速上升适合小场景练手。Cartographer精度高、支持闭环但配置复杂上手成本太高。slam_toolbox是折中方案基于图优化支持2D激光有显式回环检测保存地图也方便仓库这种典型结构化环境里效率很高。所以我的建议是先试slam_toolbox。建图的前置条件是把AGV控制起来。最简单的方式是写一个teleop键盘控制脚本让车子以稳定速度在仓库里跑一圈。这里有个注意点建图时车速不能过快雷达更新频率10Hz时车速建议控制在0.3m/s以内。走太快会导致帧间匹配漂移建出来的地图会重影或扭曲。转弯时更要慢一次转角不要超过30度这样激光匹配才有足够的重叠区域。地图建好之后slam_toolbox会保存出一个PGM图片和对应的YAML配置。这个地图文件非常关键以后每次启动导航都要加载它。如果场景变了比如货架位置移动了地图需要重新构建否则AMCL粒子会全部飘在墙上。4.2 move_base导航框架与代价地图导航我用的是ROS经典navigation栈里的move_base节点。很多人嫌它老但它其实是一个清晰的框架先有一个全局代价地图再由全局规划器在这张地图上找出一条从当前位置到目标点的路径然后有一个局部代价地图由局部规划器在跟随全局路径的同时实时躲避动态障碍物。全局规划器默认算法是Dijkstra但工业AGV场景里我更推荐A*。Dijkstra会均匀地向四周探索路径不见得差但搜索范围大、耗时更长。A多了启发式函数目标方向明确搜索效率更高尤其在地图大、节点多的仓库里优势明显这也是很多人提到三条AGV基本A算法时最看重的点。修改方式是在move_base参数里选择算法类型并设置相关权重让路径尽量贴墙但不擦墙。代价地图是导航效果好坏的关键。inflaion_radius设大了AGV会离障碍物太远在窄通道里可能直接认为无路可走设小了路径会擦着货架走车体稍有偏差就会撞上。我的经验是先测量车体最大外接圆半径作为robot_radius再在这个基础上加10厘米作为inflation_radius既能保证安全又不会把通道堵死。AMCL定位参数同样需要调。粒子数量是个权衡太少定位不稳定太多CPU占用高。1000到2000个粒子对仓库场景一般够了。update_min_d和update_min_a这两个参数决定粒子更新的阈值频繁更新会浪费资源更新太慢则定位滞后。我一般设0.2米和0.1弧度。4.3 多台AGV的A*路径规划与调度单台AGV能导航还不够工业场景下必然涉及多台车协同。这里最基础的问题是如何避免两台车在通道里正面相遇。解决这个问题不能只靠局部规划器的DWA因为DWA的视角太短只有几十厘米等它发现对面来车时很可能已经刹不住了。我的做法是在move_base外面加一个调度节点把地图栅格化之后每台车规划出一条路径时就把这条路径上每个栅格的占用时间窗登记到一张共享表里。当另一台车也规划路径时先查这张表如果它准备占用的栅格在某个时间段内已经被其他车占用就根据优先级决定等待或者换一条路。底层全局规划器仍然用A*但加上了时间维度的冲突检测之后就变成了类似路径预留的交通管制机制。在实际Gazebo仿真里我放了3台同样的AGV。调度节点给它们分配了三个任务点让它们在十字路口附近交叉行驶。第一次调试时两台车在路口僵住了谁都不让谁因为优先级判断逻辑写反了。后来改成了严格的主从优先级加超时重新规划策略才算顺利跑通。这一步让我意识到多机调度的难点其实不在路径算法本身的数学复杂度而在各种边界情况同时到达路口、任务取消、车体故障阻塞通道等。5. 整个实操流程从零到完整跑通这一章我把从环境安装到navigation全流程的关键步骤列出来你照着操作可以很稳地把AGV模型开起来并完成一次自主导航循环。5.1 环境准备与安装我的组合是Ubuntu 20.04加ROS Noetic加Gazebo Classic 11。ROS安装方法很多如果不想折腾依赖关系直接用社区比较流行的一键安装脚本比如鱼香ROS一键安装会省很多时间。它会把ROS本体、依赖和常用工具一次性配好对新人来说非常友好。手动安装也可以核心命令是sudo sh -c echo deb http://packages.ros.org/ros/ubuntu $(lsb_release -sc) main /etc/apt/sources.list.d/ros-latest.list sudo apt install ros-noetic-desktop-full sudo apt install ros-noetic-gazebo-ros-pkgs ros-noetic-gazebo-ros-control装完之后记得初始化rosdep并配置环境变量sudo rosdep init rosdep update echo source /opt/ros/noetic/setup.bash ~/.bashrc source ~/.bashrc这里有个容易踩坑的地方rosdep init如果提示已经存在可以不管它直接跳到下一步。还有如果你的显卡比较老Gazebo启动黑屏或者闪烁大概率不是安装问题而是渲染环境变量没设置好。5.2 工作空间与launch文件组织项目代码我建议全部放在catkin工作空间里mkdir -p ~/agv_ws/src cd ~/agv_ws catkin_make source devel/setup.bashsrc下面分几个包agv_description放URDF模型和meshesagv_gazebo放world文件和launchagv_navigation放move_base和AMCL的配置文件agv_scheduler放多车调度节点。包与包之间职责分明后续维护也不用在一堆launch文件里翻来翻去。launch文件是整个系统的总开关。我的主启动文件大致包含三块加载AGV模型并生成到Gazebo世界、启动传感器和差速控制器、启动rviz可视化。后面建图和导航阶段再单独启动各自的算法节点。一个很实用的做法是把所有可调参数单独放到yaml文件里不要在launch里硬编码。这样后续做参数扫描、对比实验时只需要改yaml不用动代码。Gazebo加载模型时URDF最好通过xacro命令动态解析不要在launch里写死因为模型文件改完之后不需要手动生成URDF启动时它自己会更新。5.3 跑通建图与导航全流程的关键命令启动仿真环境roslaunch agv_gazebo agv_empty_world.launch启动键盘遥控节点让AGV缓慢在仓库里跑几圈rosrun teleop_twist_keyboard teleop_twist_keyboard.py启动slam_toolbox开始建图roslaunch agv_navigation slam_toolbox.launch建图完成后保存地图rosrun map_server map_saver -f ~/maps/warehouse_map之后启动AMCL定位和move_base导航roslaunch agv_navigation amcl_move_base.launch map:/home/yourname/maps/warehouse_map.yaml在rviz里通过2D Nav Goal按钮设置目标点AGV就开始规划路径并移动了。整个流程跑通之后建议用rqt_graph截图保存方便以后排查话题连接问题。6. 常见问题排查与避坑速查最后一部分我把这一路踩过的高频问题汇总一下这些问题在各类社区里天天有人问我整理成表格你可以直接当速查手册用。现象可能原因处理方式Gazebo界面一直在闪显卡驱动问题或渲染后端冲突先安装显卡驱动再用LIBGL_ALWAYS_SOFTWARE1 gazebo强行软件渲染模型乱飞或者翻车URDF惯性参数缺失或joint方向错误检查每个link的inertial设置确认joint轴方向正确轮子陷进地里碰撞模型缺失或地面参数异常给每个link添加collision检查世界文件地面高度雷达话题无数据插件配置错误或frame_id不匹配用rostopic list确认话题存在检查雷达插件里的topic和frame设置建图重影、扭曲车速太快或转弯过急放慢车速控制每次转角小于30度导航路径穿墙代价地图膨胀半径太小增大inflation_radius同时检查地图文件是否正确AMCL粒子散落不收敛定位初始位姿错误或地图陈旧在rviz中重新设置初始位姿确认地图与当前场景一致多车路口死锁调度优先级逻辑不完善增加主从优先级配置超时后重新规划路径CPU占用过高、Gazebo卡顿物理步长太小或碰撞体过于精细适当增大max_step_size简化碰撞体几何结构6.1 Gazebo界面闪烁或黑屏的排查Gazebo界面闪是老问题了尤其是集成显卡或者N卡Optimus双显卡笔记本上。最直接的排查顺序是先确认显卡驱动装好再尝试启动时添加环境变量强制用软件渲染比如LIBGL_ALWAYS_SOFTWARE1。如果只是想跑仿真不想看画面也可以加headless参数把GUI关掉完全用命令行访问话题数据这样性能反而更高。6.2 模型跑飞和里程计漂移问题模型一启动就爆炸基本就是物理参数没写对。先检查URDF里有没有漏掉collision再看每个link的惯性张量数值是否合理。如果是启动后缓慢漂移那是里程计没有噪声补偿或者AMCL没有启用。可以对比odom话题和amcl输出的位姿差异差异越来越大说明轮式里程计在仿真里的表现已经和真实产生了偏差这时候就看粒子滤波能不能把它拉回来。6.3 雷达跳点和导航规划失败雷达偶尔跳几个点本来就不影响大局但如果跳点太多会导致代价地图上出现幽灵障碍物车走到那附近就停下来。解决思路是把雷达噪声压低一点点同时在costmap配置里增大obstacle_range和raytrace_range的差值让代价地图能区分真实障碍和噪声跳点。规划失败还有一部分原因在地图精度建图时走过的路径一定要覆盖机器人日后要走的全部区域否则AMCL很容易定位失效。最后说一点个人体会这一套AGV仿真项目做下来我最深的感受是仿真并不能替代真机测试但它真的能把低级错误全部挡在真车调试之前。URDF没建好真车上就是电机堵转导航参数没调好真车上就是撞货架。而这些东西在Gazebo里花几分钟就能暴露出来。如果你正准备搞AGV相关项目我建议先别急着买硬件把ROS和Gazebo这套仿真吃透再上真车你会感谢自己的。
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。