ROS2 和 Gazebo 的组合,算是目前机器人仿真领域最主流的一套开源方案了。但很多刚接触的朋友,第一次搭环境时往往会被各种报错、黑屏、模型加载失败折腾得够呛。这篇内容就是把我自己反复装过十几遍 ROS2 + Gazebo 的经验整理出来,从版本选择、安装命令、环境变量配置,到几个高频坑点的排查思路,全部讲清楚。不管你是刚学 ROS2 的小白,还是从 ROS1 迁移过来的老手,跟着走一遍,基本能在几分钟内跑通一个可用的仿真环境,并且知道出问题时该往哪个方向查。
1. 先把版本这件事定下来,别急着敲命令
1.1 ROS2 发行版和 Gazebo 版本的对应关系
新手最容易犯的错,就是随便找个教程就开始装,结果 ROS2 和 Gazebo 版本对不上,后面各种依赖冲突。ROS2 每个发行版都对 Gazebo 有明确的版本要求,这个必须提前确认。
目前主流的搭配是这样的:
| ROS2 发行版 | 推荐 Gazebo 版本 | 推荐 Ubuntu 版本 |
|---|---|---|
| Humble | Gazebo Classic 11 | Ubuntu 22.04 |
| Jazzy | Gazebo Harmonic (gz-sim 8) | Ubuntu 24.04 |
| Iron | Gazebo Classic 11 / Harmonic | Ubuntu 22.04 |
这里有个概念要区分清楚:Gazebo Classic和Gazebo(新版,也叫 Ignition/Gazebo Sim)是两个不同的东西。Gazebo Classic 就是老的那个gazebo命令,版本号到 11 就停止更新了;新版 Gazebo 改名叫gz sim,命令是gz。ROS2 Humble 时代两者都能用,但到了 Jazzy,官方主推的是新版 Gazebo Harmonic。
我的建议很直接:如果你是新装环境,Ubuntu 22.04 就上 Humble + Gazebo Classic 11,Ubuntu 24.04 就上 Jazzy + Gazebo Harmonic。别去折腾混搭,除非你有明确的理由。
1.2 为什么版本匹配这么重要
ROS2 和 Gazebo 之间靠ros_gz(新版)或gazebo_ros(Classic)这个桥接包通信。这个桥接包是针对特定版本编译的,版本不匹配时,轻则话题(topic)发不出去,重则直接段错误崩溃。
我踩过的一个典型坑:在 Ubuntu 22.04 上装了 Humble,然后手贱去 apt 装了最新版 Gazebo Harmonic,结果gz sim能单独启动,但一挂 ROS2 桥接就报undefined symbol。查了半天才发现是桥接包版本和 Gazebo 版本对不上。后来老老实实卸掉重装 Classic 11,一次就通了。
所以第一步,先确认你的系统版本,再决定装哪套。命令很简单:
lsb_release -a看清楚是 22.04 还是 24.04,后面的选择就清晰了。
1.3 安装前的系统准备
在正式装之前,有几件事先做掉,能省掉后面一堆麻烦。
第一,更新系统包索引,并且把universe仓库打开(ROS2 的包在这个仓库里):
sudo apt update sudo apt install software-properties-common sudo add-apt-repository universe第二,确认 locale 设置正确,否则 ROS2 的某些工具会因为编码问题报错:
locale # 检查输出里是否有 UTF-8如果LANG不是en_US.UTF-8或zh_CN.UTF-8,用下面命令设置:
sudo apt update && sudo apt install locales sudo locale-gen en_US en_US.UTF-8 sudo update-locale LC_ALL=en_US.UTF-8 LANG=en_US.UTF-8 export LANG=en_US.UTF-8第三,如果你之前装过 ROS1 或者别的 ROS2 版本,先把环境变量清理干净。检查~/.bashrc里有没有旧的source /opt/ros/xxx/setup.bash,有的话注释掉,避免多个版本互相干扰。这个坑很隐蔽,有时候你明明装的是 Humble,结果终端里ros2 --version显示的是别的版本,就是bashrc里残留的 source 语句在作怪。
2. 安装 ROS2 和 Gazebo 的完整流程
2.1 添加 ROS2 软件源并安装
以 Ubuntu 22.04 + Humble 为例,先添加 ROS2 的 apt 源:
sudo apt install curl gnupg lsb-release sudo curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.key -o /usr/share/keyrings/ros-archive-keyring.gpg echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/ros-archive-keyring.gpg] http://packages.ros.org/ros2/ubuntu $(source /etc/os-release && echo $UBUNTU_CODENAME) main" | sudo tee /etc/apt/sources.list.d/ros2.list > /dev/null然后更新并安装桌面版(包含 RViz2、demo 等工具):
sudo apt update sudo apt install ros-humble-desktop这里有个选择:ros-humble-desktop是完整版,包含 GUI 工具;ros-humble-ros-base是精简版,只有核心库。新手直接上 desktop 版,省得后面缺工具再一个个补。
安装完成后,把环境变量加到bashrc:
echo "source /opt/ros/humble/setup.bash" >> ~/.bashrc source ~/.bashrc验证一下:
ros2 --version能打印出版本号就说明 ROS2 本体装好了。
2.2 安装 Gazebo 和 ROS2 桥接包
Humble 对应的 Gazebo Classic 11,安装命令:
sudo apt install ros-humble-gazebo-ros-pkgs这个包会自动把gazebo本体和gazebo_ros桥接一起装上。装完后验证:
gazebo --version应该显示Gazebo multi-robot simulator, version 11.x.x。
如果你用的是 Jazzy + Harmonic,命令换成:
sudo apt install ros-jazzy-ros-gz验证用gz sim --version。
2.3 环境变量里那几个容易漏的配置
装完之后,有几个环境变量建议显式设置,能避免很多奇怪问题。
第一个是GAZEBO_MODEL_PATH,告诉 Gazebo 去哪里找模型。如果你后面要加载自定义模型或者网上下载的模型,这个必须配:
export GAZEBO_MODEL_PATH=$GAZEBO_MODEL_PATH:~/gazebo_models第二个是GAZEBO_RESOURCE_PATH,资源文件路径。一般不用改,但如果你把 Gazebo 装到了非标准位置,需要指一下。
第三个是LIBGL_ALWAYS_SOFTWARE,这个跟显卡驱动有关。如果你在虚拟机里跑,或者显卡驱动有问题导致 Gazebo 黑屏、闪退,可以临时设成 1 强制用软件渲染:
export LIBGL_ALWAYS_SOFTWARE=1注意:软件渲染性能很差,只适合排查问题用,正常跑仿真还是要把显卡驱动装好。
这些变量建议都写进~/.bashrc,省得每次开终端都要手动设。
3. 跑通第一个仿真:从空世界到带机器人的场景
3.1 启动一个空世界验证基础环境
环境装好后,先别急着上机器人模型,用一个空世界验证一下 Gazebo 能不能正常启动:
gazebo如果弹出一个带网格地面的窗口,说明 Gazebo 本体没问题。这时候你可以试着在左侧面板插入一个简单物体,比如一个立方体,看看能不能正常显示和交互。
如果这一步就黑屏或者闪退,先别往下走,问题出在图形渲染上。常见原因和排查方向:
- 显卡驱动没装好:
glxinfo | grep "OpenGL renderer"看看是不是走了软件渲染 - 虚拟机 3D 加速没开:在虚拟机设置里把 3D 加速勾上
- 远程桌面连接:X11 转发对 OpenGL 支持不好,尽量本地跑
3.2 用 launch 文件启动 Gazebo 并加载 ROS2 桥接
单独启动gazebo命令,ROS2 是感知不到的。要让 ROS2 和 Gazebo 通信,得通过gazebo_ros提供的 launch 文件启动:
ros2 launch gazebo_ros gazebo.launch.py这个命令会启动 Gazebo,同时把 ROS2 和 Gazebo 的桥接节点拉起来。启动后,另开一个终端,用下面命令看看话题列表:
ros2 topic list你应该能看到/clock、/gazebo/...之类的话题。看到这些,说明桥接通了。
3.3 加载一个带差速驱动的机器人模型
空世界跑通后,下一步是加载一个真实的机器人模型。这里我用一个常见的两轮差速小车举例,说明整个流程。
首先创建一个工作空间和功能包:
mkdir -p ~/ros2_ws/src cd ~/ros2_ws/src ros2 pkg create --build-type ament_cmake my_robot_description然后在功能包里放机器人的 URDF/Xacro 文件、Gazebo 的 world 文件,以及一个 launch 文件。launch 文件的核心内容大概是这样:
from launch import LaunchDescription from launch.actions import IncludeLaunchDescription from launch.launch_description_sources import PythonLaunchDescriptionSource from launch_ros.actions import Node from ament_index_python.packages import get_package_share_directory import os def generate_launch_description(): pkg_share = get_package_share_directory('my_robot_description') gazebo_launch = IncludeLaunchDescription( PythonLaunchDescriptionSource( os.path.join(get_package_share_directory('gazebo_ros'), 'launch', 'gazebo.launch.py') ) ) spawn_entity = Node( package='gazebo_ros', executable='spawn_entity.py', arguments=['-topic', 'robot_description', '-entity', 'my_robot'], output='screen' ) return LaunchDescription([gazebo_launch, spawn_entity])这里的关键是spawn_entity.py这个节点,它负责把 URDF 描述的机器人模型生成到 Gazebo 世界里。-topic robot_description表示从 ROS2 的robot_description话题读取模型描述。
编译并运行:
cd ~/ros2_ws colcon build source install/setup.bash ros2 launch my_robot_description my_robot.launch.py如果一切正常,你会在 Gazebo 里看到小车模型,并且可以用ros2 topic pub往/cmd_vel发速度指令让它动起来。
3.4 验证机器人是否真的"活"了
模型加载出来不代表就能用。要验证机器人是否真的和 ROS2 打通了,做两件事:
第一,检查robot_description话题有没有数据:
ros2 topic echo /robot_description --once第二,发一个速度指令,看 Gazebo 里的小车是否移动:
ros2 topic pub /cmd_vel geometry_msgs/msg/Twist "{linear: {x: 0.5}, angular: {z: 0.0}}"如果小车动了,恭喜你,整个链路是通的。如果没动,问题可能出在 URDF 里的gazebo_ros_diff_drive插件配置上,检查插件里的<leftJoint>和<rightJoint>名字是否和 URDF 里的关节名一致。
4. 那些年我踩过的坑:高频问题排查手册
4.1 Gazebo 界面一直闪或者黑屏
这是被问得最多的问题,没有之一。现象是 Gazebo 窗口打开后不停闪烁,或者干脆一片黑。
根本原因通常是 OpenGL 渲染问题。排查顺序:
第一步,确认是不是虚拟机。如果是,检查虚拟机设置里的 3D 加速有没有开。VMware 和 VirtualBox 都需要在设置里手动开启 3D 加速,否则 Gazebo 的 OpenGL 渲染会失败。
第二步,检查显卡驱动。在物理机上跑:
glxinfo | grep "OpenGL renderer"如果显示的是llvmpipe或者softpipe,说明在用软件渲染,性能极差且容易闪。需要装正确的显卡驱动。NVIDIA 显卡用ubuntu-drivers devices看看推荐驱动,然后sudo apt install nvidia-driver-xxx。
第三步,如果是 NVIDIA 双显卡笔记本,可能是用了核显跑 Gazebo。用prime-select query看看当前用的是哪个,切到独显:
sudo prime-select nvidia然后重启。
第四步,临时应急可以用软件渲染绕过:
export LIBGL_ALWAYS_SOFTWARE=1 gazebo能跑起来说明确实是渲染问题,但这不是长久之计。
4.2 模型加载失败:一直显示 "Loading model"
Gazebo 启动后,如果模型一直卡在加载状态,或者报Unable to find model,基本是模型路径或者网络问题。
Gazebo 默认会从在线模型库拉取模型,如果网络不通,就会一直卡着。解决办法是配置本地模型路径,或者提前把模型下载到本地。
设置本地模型路径:
export GAZEBO_MODEL_PATH=$GAZEBO_MODEL_PATH:~/.gazebo/models然后把需要的模型放到~/.gazebo/models目录下。每个模型是一个文件夹,里面包含model.config和model.sdf。
如果你在 launch 文件里指定了 world 文件,检查 world 文件里引用的模型 URI 是否正确。常见格式是model://model_name,这个model_name必须能在GAZEBO_MODEL_PATH里找到。
4.3 ROS2 话题发不出去,Gazebo 里没反应
这个问题的排查链路比较长,我一般按下面的顺序查:
先确认桥接节点是否在跑:
ros2 node list应该能看到/gazebo相关的节点。如果没有,说明gazebo_ros没启动成功,检查 launch 文件里有没有包含gazebo.launch.py。
再确认话题是否对得上:
ros2 topic list ros2 topic info /cmd_vel看看/cmd_vel的发布者和订阅者数量。如果订阅者是 0,说明 Gazebo 那边的插件没订阅这个话题,检查 URDF 里插件的<ros><remapping>配置。
最后确认消息类型是否匹配。ROS2 里cmd_vel的类型是geometry_msgs/msg/Twist,如果你发的是别的类型,Gazebo 收不到。
4.4 spawn_entity 报 "entity already exists"
这个错误通常是因为你重复启动了 launch 文件,上一次的模型还没被清理掉。解决办法有两个:
一是重启 Gazebo,最简单粗暴。
二是在 launch 文件里加一个删除实体的步骤,或者用-entity参数换个名字。
我一般习惯在调试时给实体名加个时间戳,避免冲突:
import time entity_name = f'my_robot_{int(time.time())}'4.5 编译时报 "package not found"
colcon build时如果报找不到某个包,先确认依赖有没有装全:
rosdep install --from-paths src --ignore-src -r -yrosdep会自动解析package.xml里的依赖并安装。如果rosdep本身没初始化,先跑:
sudo rosdep init rosdep update还有一个常见原因是source顺序不对。工作空间的setup.bash必须在 ROS2 的setup.bash之后 source,否则找不到 ROS2 的基础包。
5. 让仿真更接近真实:几个进阶配置
5.1 用 Xacro 管理复杂的机器人模型
URDF 写复杂机器人时会非常冗长,Xacro 是 URDF 的宏语言,支持变量、条件、复用。强烈建议机器人模型都用 Xacro 写,然后在 launch 文件里转成 URDF。
转换命令:
xacro robot.urdf.xacro > robot.urdf在 launch 文件里可以用Command动作动态转换:
from launch.substitutions import Command robot_description = Command(['xacro ', os.path.join(pkg_share, 'urdf', 'robot.urdf.xacro')])这样改模型不用手动转换,launch 时自动处理。
5.2 配置物理引擎参数
Gazebo 默认的物理引擎参数对某些场景不够真实,比如摩擦力、关节阻尼。可以在 world 文件里调整:
<physics type="ode"> <max_step_size>0.001</max_step_size> <real_time_factor>1.0</real_time_factor> <real_time_update_rate>1000</real_time_update_rate> </physics>max_step_size越小,仿真越精确但越慢。一般 0.001 秒够用。real_time_factor是仿真时间和真实时间的比例,1.0 表示实时。
如果仿真跑起来很卡,先把max_step_size调大试试,比如 0.002 或 0.005。
5.3 传感器插件的配置要点
Gazebo 里常用的传感器插件有激光雷达、摄像头、IMU。配置时最容易出问题的是话题名和坐标系。
以激光雷达为例,插件配置里要指定<frameName>,这个必须和 URDF 里的 link 名字一致,否则 RViz2 里显示会错位。另外<topicName>建议用相对话题名,比如scan,这样会自动加上命名空间。
摄像头插件要注意<imageTopicName>和<cameraInfoTopicName>两个话题都要配,否则 RViz2 里只能看到图像但拿不到相机内参。
5.4 在虚拟机里跑仿真的性能优化
很多人是在虚拟机里学 ROS2 的,虚拟机跑 Gazebo 性能确实是个问题。几个优化方向:
- 给虚拟机多分点 CPU 核心和内存,至少 4 核 8G
- 开启 3D 加速
- 降低 Gazebo 的渲染质量,在 Gazebo 设置里把阴影、抗锯齿关掉
- 用
gzserver单独跑物理仿真,gzclient单独跑界面,调试时可以只开 server
gzserver world_file.world gzclient这样界面卡的时候,物理仿真不受影响。
6. 从仿真到实机的衔接思路
仿真跑通之后,下一步通常是把仿真里验证过的算法往实机上迁。这里有几个经验点值得提前注意。
第一,仿真里的传感器是理想的,实机有噪声。所以算法在仿真里跑通不代表实机能用,得留出调参的余地。比如激光雷达的噪声模型,仿真里可以加高斯噪声模拟真实情况。
第二,仿真里的时间系统和实机不一样。Gazebo 用/clock话题发布仿真时间,实机用的是系统时间。如果你的代码里用了ros2::Time,要确保use_sim_time参数设置正确。仿真时设为true,实机设为false。
第三,坐标系标定。仿真里机器人的各个 link 位置是精确的,实机上需要做手眼标定、IMU 标定。这部分工作仿真帮不上忙,但可以在仿真里先把标定流程跑一遍,熟悉操作。
第四,控制频率。仿真里可以跑很高的控制频率,实机上受限于通信和计算,频率要降下来。建议在仿真里就把控制频率设成和实机一致,避免迁移时出现控制不稳定。
我自己的习惯是,仿真环境里就把参数配置和实机对齐,包括话题名、坐标系、控制频率。这样从仿真切到实机时,只需要改use_sim_time和硬件接口,算法部分基本不用动。
Gazebo 的仿真环境搭建,说到底就是版本匹配、依赖装全、环境变量配对这三件事。把这三件事做扎实,后面 90% 的报错都能避免。剩下的 10%,靠的是遇到问题时知道往哪个方向查——是渲染问题、路径问题,还是话题通信问题。这篇内容里列的排查链路,都是我在实际项目里一条条试出来的,希望能帮你少走点弯路。