拓冰建站拓冰建站
首页 / 资讯中心 / 正文

ROS 2 Gazebo从安装到自建世界:版本选型与高频坑解析

做ROS 2机器人开发几乎绕不开Gazebo这个话题。无论是调导航、测SLAM还是验证机械臂运动规划先在仿真环境里跑通逻辑再上真机能省下大量调试时间。但很多初学者第一次接触Gazebo时卡住的往往不是机器人模型本身而是最基础的安装和世界构建。这篇文章围绕ROS 2中Gazebo的版本选型、安装步骤、自定义世界文件编写、模型与传感器接入这几个核心环节展开把我实际踩过的高频坑也一并整理出来。内容相当于教材6.4.1节的扩展版适合刚装好ROS 2还没跑通第一个仿真场景的读者也适合那些已经在用Gazebo但想彻底搞懂.world文件结构的开发者。1. 版本选择是第一个坎Classic和Modern两条线怎么选1.1 Gazebo 11和Gazebo Harmonic到底差在哪很多人搞不清楚为什么有两个Gazebo。这得从Gazebo的改名历史说起。老的Gazebo 7、9、11系列现在被官方称作Gazebo Classic采用的是ROS 1时代延续下来的gazebo_ros_pkgs集成方式。这条线非常成熟网上的教程、论文复现、开源的仿真案例绝大多数都基于它。Ubuntu 22.04搭配ROS 2 Humble时默认源里的Gazebo就是Classic 11这也是目前资料最多、最不容易卡住的环境组合。另一条线是新一代仿真器曾经叫Ignition Gazebo后来改名成Gazebo Sim再后来直接叫Gz Sim。它从架构上做了大量重构渲染引擎、物理引擎、插件系统都变了模型格式依然是SDF但启动命令从gazebo变成了gz sim集成ROS 2的依赖包也从gazebo_ros_pkgs换成了ros_gz。Gazebo Harmonic是当前长期支持版对应Ubuntu 24.04和ROS 2 Jazzy是最常见的组合。从功能上讲新旧两代都在持续维护Gazebo Classic短期内也不会消失。但你要明白一件事新封装和老封装的话题接口、launch文件写法、环境变量名都不一样混着用容易出各种诡异问题。1.2 ROS 2发行版和Gazebo版本的匹配对照装机前先对着自己的环境确定版本组合这是最重要的一步。我曾经见过有人用ROS 2 Humble却强行去跑只支持gz sim的launch文件折腾一晚上没跑起来最后发现是版本配错了。ROS 2发行版Ubuntu版本推荐仿真器集成包Humble22.04Gazebo Classic 11ros-humble-gazebo-ros-pkgsIron22.04Gazebo Classic 11或Gazebo Fortressros-iron-gazebo-ros-pkgsJazzy24.04Gazebo Harmonicros-jazzy-ros-gz这里有个关键区别Humble的官方源里默认装的是Gazebo Classic对应包名是gazebo_ros_pkgs启动launch文件用的是ros2 launch gazebo_ros gazebo.launch.py。而Jazzy的源里默认没有Gazebo Classic只有新架构的gz-harmonic对应的ROS 2集成包是ros_gz_sim启动方式完全不同。如果你在Ubuntu 22.04上非要跑Gazebo Harmonic也不是不行需要额外添加OSRF软件源同时ROS 2侧的ros_gz包得从源码编译指定harmonic版本这部分操作比较折腾新手不推荐一上来就这么干。先用系统默认的组合把基础流程跑通后面再研究进阶切换会从容得多。1.3 按照你的实际场景做选择怎么判断自己应该用哪条线我给自己定了几条选择标准可以供你参考如果你要复现学术代码、跑开源SLAM/导航项目优先选Gazebo Classic 11。开源生态里大部分仓库都在这条线上验证过。如果你是Ubuntu 24.04新装系统别想办法去装老版Gazebo直接顺应默认用Gazebo Harmonic。Jazzy集成它已经是官方推荐路径硬件加速和渲染效果也更好。如果你要做视觉或雷达仿真新架构的传感器插件更规范Humble环境里也建议提前尝试Harmonic。基于这些判断后面我会把HumbleClassic和JazzyHarmonic两套安装流程都讲清楚。2. 安装实操Ubuntu 22.04与24.04两条完整路径2.1 Humble搭配Gazebo Classic 11的安装步骤Ubuntu 22.04上安装这一套非常省心ROS 2 Humble装好之后一行命令就能把Gazebo本体和ROS 2集成包一起搞定。sudo apt update sudo apt install ros-humble-gazebo-ros-pkgs这个包会把gazebo、libgazebo11、gazebo_ros_pkgs一起拉进来。装完确认版本gazebo --version如果终端能打印出类似Gazebo multi-robot simulator, version 11.x.x的信息说明本体没问题。接着确认ROS 2集成是否正常ros2 pkg list | grep gazebo正常情况下能看到gazebo_ros、gazebo_ros_pkgs、gazebo_plugins等包。这里有个容易忽略的点gazebo_ros_pkgs是个元包真正负责ROS 2节点的是gazebo_ros负责话题桥接和gazebo插件的是gazebo_plugins。如果只装了元包而不确认子包后面编译自己的模型插件时会报找不到头文件的错。然后是环境变量。虽然gazebo_ros_pkgs安装时会设置一部分路径但你的自定义模型目录一定要手动加进GAZEBO_MODEL_PATH。这个我建议写进~/.bashrc里echo export GAZEBO_MODEL_PATH$GAZEBO_MODEL_PATH:$HOME/gazebo_models ~/.bashrc source ~/.bashrc如果没有这个变量后续用model://引用自己下载或导出的模型时Gazebo会一直提示找不到模型。2.2 Jazzy搭配Gazebo Harmonic的安装步骤Ubuntu 24.04上的路径完全不同。先安装Gazebo Harmonic本体sudo apt install gz-harmonic然后安装ROS 2 Jazzy侧的集成包sudo apt install ros-jazzy-ros-gz这个元包会拉入ros_gz_sim、ros_gz_bridge等关键组件。ros_gz_sim负责在仿真里生成实体、管理世界ros_gz_bridge负责在ROS 2话题和gz传输话题之间搭桥。验证安装时注意启动命令不是gazebo了gz sim --version正常会打印Gazebo Sim, version 8.x.x之类的信息这里的版本号对应的是Harmonic的Sim核心版本不是Gazebo Classic的11。环境变量也和Classic不一样新架构用的是GZ_SIM_RESOURCE_PATH设置自定义模型目录时别写错了echo export GZ_SIM_RESOURCE_PATH$GZ_SIM_RESOURCE_PATH:$HOME/gz_models ~/.bashrc source ~/.bashrc2.3 装完怎么判断能不能用每次装完工具我喜欢用最小化验证来确认配置是真的通的而不是等到后面写launch文件时才发现环境有问题。先做GUI启动测试。Humble环境执行ros2 launch gazebo_ros gazebo.launch.pyJazzy环境执行ros2 launch ros_gz_sim gz_sim.launch.py gz_args:empty.sdf正常会弹出一个空世界窗口里面有地面网格和天空背景。看到这个窗口还不算完再开一个终端确认ROS 2侧能看到仿真器节点ros2 node listHumble下能看到/gazeboJazzy下能看到/gzserver或/gz_sim这类节点名。如果节点列表是空的说明ROS 2和Gazebo之间的桥没建立起来后面spawn实体肯定失败需要先排查这一步。3. 写world文件前必须整明白的SDF骨架3.1 world文件到底是个什么格式Gazebo描述仿真世界用的是SDF格式全称Simulation Description Format。你可能听过URDF那是描述单个机器人的SDF则更宏观不仅描述机器人本身还描述整个世界的组成地面、光照、物理参数、静态物体、动态物体。一个.world文件的最外层是sdf标签里面通常包含一个world标签。看一个最小世界的骨架?xml version1.0 ? sdf version1.6 world namemy_first_world include urimodel://sun/uri /include include urimodel://ground_plane/uri /include /world /sdfmodel://sun和model://ground_plane是Gazebo内置的模型引用sun提供全局光照ground_plane提供地面。这个文件虽然简单但已经是能跑的合法世界了。SDF特别讲究版本号。sdf version1.6对应Gazebo 9的基本语法到了Harmonic版本号推荐用1.10或1.11因为新版本里部分标签结构有调整。跨版本解析时老版本写法的文件基本都能被新版本读取但反过来不行。我见过有人把新版world文件拿到Gazebo Classic 11里跑报出一堆XML解析错误多半就是这个原因。3.2 scene、light、physics三个全局配置除了include模型world里最常用的是三个配置块scene控制画面表现light控制光照physics控制物理仿真参数。scene background0.8 0.8 0.9 1.0/background ambient0.4 0.4 0.4 1.0/ambient sky clouds speed5/speed /clouds /sky /scenebackground是RGB加透明度的背景色ambient是环境光强度。这两个值调不好世界会看起来一片漆黑或者惨白刺眼。新架构里sky的云层速度用了speed旧架构里则是wind加humidity这些不同字段这也是跨版本兼容要注意的地方。光照配置以方向光为例模拟太阳光最合适light typedirectional namesun cast_shadowstrue/cast_shadows pose0 0 10 0 0 0/pose diffuse1 1 1 1/diffuse specular0.5 0.5 0.5 1/specular direction-0.5 0.1 -0.9/direction /lightdirection的取值很多人调整不好。光线的方向是光源照射的方向向量-0.5 0.1 -0.9表示从右上方向后方照射阴影会投到地面偏后侧。如果你发现阴影方向诡异第一件事不是去调pose而是调direction。物理参数是仿真重力的关键physics typeode max_step_size0.001/max_step_size real_time_factor1/real_time_factor gravity0 0 -9.8/gravity /physicsmax_step_size是每一步物理求解的时间步长默认0.001秒精度高但计算量大。如果仿真卡顿严重可以放宽到0.002或0.005但机械臂这类高精度场景不建议放宽太多。real_time_factor控制仿真速度和真实时间的比例保持1就代表和真实时间一样快跑仿真机器人视觉感知时这个值很重要真机部署前就是靠这个参数确认算力够不够。3.3 用include还是自己建model构建世界时摆在面前的有两条路引用现成模型或者在world文件里直接写死一个model。引用现成模型用include加uriinclude urimodel://cafe_table/uri pose2.0 -1.5 0 0 0 0.8/pose /include这种方式的好处是代码简洁Gazebo会自动从模型数据库加载。但缺点也很明显首次加载需要联网下载下载不成功就卡住。在离线环境或网络不稳的场合大量model://引用会导致世界加载时间长得让人崩溃。直接在world里写model则完全依赖本地SDF描述不涉及网络更适合那些需要精确定制的墙、柱子、坡道。写法举例如下model namewall_1 statictrue/static pose0 2.0 0.5 0 0 0/pose link namelink collision namecollision geometry box size3.0 0.2 1.0/size /box /geometry /collision visual namevisual geometry box size3.0 0.2 1.0/size /box /geometry material ambient0.8 0.2 0.2 1/ambient diffuse0.8 0.2 0.2 1/diffuse /material /visual /link /model没有写static默认是false也就是动态物体会有重力作用往下掉。做环境墙体时一定要显式写statictrue否则启动后会看到墙体轰然倒下的名场面。4. 零基础搭出一个能跑的世界文件4.1 完整案例带围栏和障碍物的导航测试场纸上谈兵没有意义直接给一个能跑的world文件这个场景我经常用来测试导航算法包含地面、四周围墙、两个障碍物和一盏方向光。?xml version1.0 ? sdf version1.6 world namenav_test_world include urimodel://sun/uri /include include urimodel://ground_plane/uri /include physics typeode max_step_size0.001/max_step_size real_time_factor1/real_time_factor gravity0 0 -9.8/gravity /physics model namewall_left statictrue/static pose-5.0 0 0.5 0 0 0/pose link namelink collision namecollision geometry boxsize0.2 10.0 1.0/size/box /geometry /collision visual namevisual geometry boxsize0.2 10.0 1.0/size/box /geometry material ambient0.5 0.5 0.5 1/ambient diffuse0.5 0.5 0.5 1/diffuse /material /visual /link /model model namewall_right statictrue/static pose5.0 0 0.5 0 0 0/pose link namelink collision namecollision geometry boxsize0.2 10.0 1.0/size/box /geometry /collision visual namevisual geometry boxsize0.2 10.0 1.0/size/box /geometry material ambient0.5 0.5 0.5 1/ambient diffuse0.5 0.5 0.5 1/diffuse /material /visual /link /model model namewall_back statictrue/static pose0 -5.0 0.5 0 0 0/pose link namelink collision namecollision geometry boxsize10.0 0.2 1.0/size/box /geometry /collision visual namevisual geometry boxsize10.0 0.2 1.0/size/box /geometry material ambient0.5 0.5 0.5 1/ambient diffuse0.5 0.5 0.5 1/diffuse /material /visual /link /model model nameobstacle_box statictrue/static pose1.5 0 0.3 0 0 0.4/pose link namelink collision namecollision geometry boxsize0.6 0.6 0.6/size/box /geometry /collision visual namevisual geometry boxsize0.6 0.6 0.6/size/box /geometry material ambient0.1 0.6 0.1 1/ambient diffuse0.1 0.6 0.1 1/diffuse /material /visual /link /model model nameobstacle_cylinder statictrue/static pose-1.8 1.8 0.5 0 0 0/pose link namelink collision namecollision geometry cylinderradius0.4/radiuslength1.0/length/cylinder /geometry /collision visual namevisual geometry cylinderradius0.4/radiuslength1.0/length/cylinder /geometry material ambient0.9 0.6 0.1 1/ambient diffuse0.9 0.6 0.1 1/diffuse /material /visual /link /model /world /sdf保存为nav_test.world。启动之前注意一点pose里的四个墙和障碍物的z坐标都是半个高度偏移量比如墙体高度1米所以z设置了0.5让底部刚好贴地。这个偏移算错了看起来会像悬浮或者穿模。4.2 启动你自己的世界Humble环境下启动自定义world文件ros2 launch gazebo_ros gazebo.launch.py world:/path/to/nav_test.world注意world:后面要用绝对路径用相对路径偶尔会因为launch文件的工作目录不同而找不到文件。如果你不想每次敲这么长的路径可以把world文件统一放在一个目录里然后在launch文件里用pkg-relative方式引用。但对初学者我建议先直接用绝对路径先把流程跑通。如果你在Jazzy环境启动方式有变化而且支持直接在gz_args里传世界文件路径ros2 launch ros_gz_sim gz_sim.launch.py gz_args:/path/to/nav_test.world启动后应该能看到一个带灰色地面、四面围墙、两个障碍物的三维场景。视角操作上Shift鼠标中键拖动是平移鼠标左键拖动是旋转视角鼠标滚轮是缩放。很多人第一次跑起来后不知道怎么调整视角这里先记一下。4.3 启动后必做的三个自检世界加载成功不等于一切正常我通常会在刚打开场景后立刻做三个自检。第一物理引擎的静置检查。视线盯着障碍物和墙面确认没有任何模型在下落或滑动。如果出现墙体倾倒检查static是否为true。第二光照方向检查。看地面上的阴影是否自然如果整个场景惨白或者完全漆黑检查sun模型是否加载成功。sun加载失败时场景会有光源缺失感此时在Gazebo左侧模型面板里手动拖一个sun进去也能临时解决。第三用户界面里的模型列表。在左侧插入面板可以看到当前世界的树结构里面应有wall_left、obstacle_box等模型节点。如果某个模型没有出现多半是XML里有标签拼写错误终端也会同步打印SDF解析报错。终端里没有错误输出、模型却没显示的情况极少一旦出现基本是图形渲染层面的显示问题可以试着关闭重开或调整渲染引擎。5. ROS 2与Gazebo的交接生成实体与传感器接入5.1 spawn实体命令在不同版本间的差异世界文件准备好了机器人怎么放进去两种方式一是在world文件里直接通过include引入机器人模型二是在启动仿真后动态生成实体。动态生成在实际开发中更常用因为可以在launch文件里灵活控制位置、参数和数量。Humble环境经典做法是用gazebo_ros包里的spawn_entity.py脚本ros2 run gazebo_ros spawn_entity.py \ -entity my_robot \ -file /path/to/my_robot.sdf \ -x 0.0 -y 0.0 -z 0.1实体名-entity必须唯一重复生成同名实体会报错。-file参数指向URDF或SDF文件URDF也能用但推荐转成SDFSDF对Gazebo的支持更完整。Jazzy环境命令换成了ros_gz_sim的create命令ros2 run ros_gz_sim create \ -name my_robot \ -file /path/to/my_robot.sdf \ -x 0.0 -y 0.0 -z 0.1注意选项从-entity变成了-name其他坐标参数保持一致。如果你在Humble里强行用gz sim --spawn或ros_gz_sim create会因为版本不对打出奇怪的eigen3库报错当年我排查了一晚上后发现只是命令用混了。5.2 差速驱动机器人接入仿真假设你已经有一个差速驱动机器人的URDF模型想接入Gazebo进行导航测试。URDF直接给Gazebo用会缺少一些关键信息建议先用gazebo_ros2_control插件包把URDF转成SDF或者在URDF里附带Gazebo插件标签。一个最简的差速驱动Gazebo插件配置长这样gazebo plugin namediff_drive filenamelibgazebo_ros_diff_drive.so ros namespace/robot/namespace /ros left_jointleft_wheel_joint/left_joint right_jointright_wheel_joint/right_joint wheel_separation0.35/wheel_separation wheel_diameter0.2/wheel_diameter command_topiccmd_vel/command_topic odom_topicodom/odom_topic /plugin /gazebo这些配置写入URDF文件的末尾再用spawn命令把它加载进仿真。Gazebo会解析插件创建cmd_vel和odom话题这时你可以从终端发一个速度指令验证ros2 topic pub -1 /robot/cmd_vel geometry_msgs/msg/Twist {linear: {x: 0.5}, angular: {z: 0.0}}同时监听里程计话题ros2 topic echo /robot/odom如果机器人动了、里程计也在持续更新说明闭环建立成功。这一步没反应时优先检查插件是否真的被加载在启动仿真时的终端里搜索diff_drive相关日志找不到基本就是插件路径或命名空间配置问题。5.3 激光雷达和相机传感器怎么放进来Gazebo里的激光雷达一般用gpu_ray插件模拟网络上有大量现成配置。以Humble为例一个gpu_lidar插件的URDF片段长这样gazebo referencelidar_link sensor typegpu_ray namelidar_sensor pose0 0 0.1 0 0 0/pose always_ontrue/always_on update_rate10/update_rate visualizetrue/visualize plugin namelidar_plugin filenamelibgazebo_ros_ray_sensor.so ros namespace/robot/namespace remappingscan:scan/remapping /ros output_typesensor_msgs/LaserScan/output_type min_range0.1/min_range max_range10.0/max_range horizontal_samples360/horizontal_samples horizontal_resolution1.0/horizontal_resolution /plugin /sensor /gazebo相机传感器同样用插件方式接入常用libgazebo_ros_camera.so。相机接入后能产生image_raw和camera_info话题供视觉SLAM或者目标检测做测试。这类传感器在Harmonic上有对应替代配置接口字段和插件名字会有差别。这里提醒一句雷达的update_rate和horizontal_samples两个参数非常影响CPU占用。把水平采样设为720、更新率设为20时一颗高仿雷达就能烧掉不少算力做多机器人仿真时要特别注意别让一颗虚拟雷达拖垮整台电脑。6. 高频异常排查闪烁、白屏、加载失败的现场还原6.1 界面一直在闪图形渲染问题排查网上关于为什么gazebo界面一直在闪的提问特别多。我在虚拟机里遇到过一次真机上也遇到过。先判断环境。如果运行在VMware或者VirtualBox虚拟机里闪烁大概率是OpenGL渲染不兼容导致的。虚拟机默认的3D加速能力和Gazebo要求的OpenGL版本经常对不上。简单的解决办法是强制软件渲染启动前设置环境变量export LIBGL_ALWAYS_SOFTWARE1设置后Gazebo界面闪烁会显著减少代价是渲染帧率下降稍微复杂的场景旋转视角时会感觉卡顿。如果是真机出现闪烁优先排查显卡驱动关闭快捷键切换导致的渲染异常再尝试升级mesa驱动。第二个常见原因是窗口聚焦问题Gazebo的渲染循环在高DPI缩放或多显示器环境下会异常刷新。可以尝试把外部GUI渲染选项关掉用headless模式跑服务器端仿真界面只用来做可视化调试export GAZEBO_HEADLESS16.2 模型加载不出来或显示白模资源路径设置Gazebo模型加载的问题分两种。第一种是model://引用找不到终端会打印Unable to find uri[model://xxx]。这时候检查GAZEBO_MODEL_PATH或GZ_SIM_RESOURCE_PATH是否包含了目标模型所在目录。注意环境变量要在启动Gazebo的同一个终端里生效改了.bashrc记得source或者重开终端。第二种是模型能加载但显示全白大概率是模型文件里没有定义material或者材质贴图路径缺失。Gazebo White Model是一个常见现象特别是用Blender导出的模型最容易出现。因为Blender导出时纹理贴图的相对路径没有正确带到模型目录里Gazebo找不到贴图文件只能显示白色材质。处理方法是把贴图文件和模型文件放在同一目录确保SDF里的uri是相对路径不要写成file:///home/user/...这样的绝对路径。跨机器拷贝模型包时绝对路径几乎必坏这是很多人踩过的坑。6.3 用Blender导出的模型怎么正确进Gazebo热词里有人搜blender导出gazebo模型说明这条路确实很多人都走。Blender默认导出格式里没有直接对应Gazebo的选项通常路径是Blender导出.dae再通过gz sdf工具转换成.sdf。Humble环境下的转换命令gz sdf -p model.dae model.sdf转换完成后需要在SDF里补上材质信息Blender导出的材质在转换后经常丢失或者变成默认白色。转换出来的SDF文件里往往是一堆visual和collision块你需要确认碰撞体积是不是和视觉模型一致。很多人在这一步偷懒写一个简化的长方体碰撞盒导致仿真里机器人明明看起来离障碍物还远却已经撞上去了。如果只是想在world里放一个装饰性物体可以把整个模型包放到GAZEBO_MODEL_PATH目录中目录结构应该是model_name/model.config加model_name/model.sdf。Gazebo通过model.config识别模型名称和描述信息缺少这个文件时模型面板里不会显示你的模型。6.4 版本混杂导致的链接错误与话题缺失最后一种高频异常是Humble环境里装了一堆gz sim相关包导致gazebo_ros_pkgs插件加载时报找不到符号或版本不匹配。一个典型场景你先安装了ros-humble-ros-gz又在同一环境里用gazebo_ros_pkgs启动Classic仿真可能会出现libignition-common相关的so文件冲突。原因是两个版本的集成包依赖了不同版本的底层库动态链接时优先加载了不对应的库文件。遇到这种问题检查两件事。第一用ldd确认实际加载的库路径ldd /opt/ros/humble/lib/libgazebo_ros_diff_drive.so | grep ignition看到多个不同版本的ignition库时考虑在启动前用LD_LIBRARY_PATH指定优先路径或者干脆清理掉不需要的gz sim相关包。第二检查当前环境变量里是不是混入了多个AMENT_PREFIX_PATH。如果养成每装一个包就手动往环境里加路径的习惯很容易导致ROS 2包解析错乱这时用printenv | grep ROS检查一下把重复路径清理干净。从我自己折腾Gazebo这些年的经验看绝大多数启动失败、界面异常、模型加载问题根因都不是复杂的技术难题而是环境不纯、版本混用、路径错误这三件事。所以每次搭建新环境时我都会写个备忘明确记录当前ROS 2版本、Gazebo版本、模型资源路径避免几天后回过头来忘了自己配过什么。遇到奇怪问题先降级处理把自定义的东西全部停掉跑官方示例跑通了再逐步加回自己的模型这个排查思路虽然笨但效率往往最高。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门