1. Gazebo与ROS2双雄会:环境搭建与核心概念
说实话,每次带新人入门机器人仿真,我最怕听到的问题就是:「老师,我装了一整天,Gazebo和ROS2就是连不上。」
嗯,这确实是个坎。Gazebo Garden + ROS2 Humble这套组合,就像两个高手要联手打天下,但前提是你得先把它们的「通信协议」对好。今天我们就从零开始,把这件事彻底讲透。
1.1 为什么是Garden + Humble?
你可能要问:Gazebo版本那么多,为什么偏偏选Garden?
我个人习惯是追新不追旧。Gazebo Garden是Ignition系列的最新稳定版,底层架构大改过,性能比老版Gazebo Classic强了不止一个量级。而ROS2 Humble是长期支持版,2024年之前都不会过时。
说白了,这对组合就是「新引擎 + 稳底盘」。我在去年一个仓储机器人项目里用过这对组合,跑了三个月仿真,一次都没崩过。
gz-sim 包,不再兼容旧版 gazebo_ros_pkgs。你需要用 ros_gz_bridge 来搭桥。
1.2 从零搭建开发环境
别急着复制粘贴命令。先理清思路:我们要装三样东西——ROS2 Humble、Gazebo Garden、以及它们之间的桥梁。
第一步:安装ROS2 Humble
这个网上教程很多,我只说几个坑:
- 系统版本:必须用Ubuntu 22.04,别想着在20.04上硬装Humble,我试过,依赖冲突能让你怀疑人生。
- 语言环境:记得设
LC_ALL=en_US.UTF-8,否则编译时中文乱码会把你逼疯。 - 验证安装:跑一下
ros2 run demo_nodes_cpp talker,能看到「Hello World」就对了。
第二步:安装Gazebo Garden
这里有个小技巧:别用apt直接装,用二进制包更干净。
# 添加Gazebo官方源
sudo wget https://packages.osrfoundation.org/gazebo.gpg -O /usr/share/keyrings/gazebo-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/gazebo-archive-keyring.gpg] http://packages.osrfoundation.org/gazebo/ubuntu-stable $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/gazebo-stable.list
# 安装Garden
sudo apt update
sudo apt install gz-garden
装完后,终端输入 gz sim,如果能看到一个空荡荡的仿真世界,恭喜你,环境成了。
第三步:安装ros_gz_bridge
这才是重头戏。ROS2和Gazebo Garden之间通信,全靠这个桥。
# 从源码编译
mkdir -p ~/ros2_humble_ws/src
cd ~/ros2_humble_ws/src
git clone https://github.com/gazebosim/ros_gz.git -b humble
# 安装依赖
cd ~/ros2_humble_ws
rosdep install --from-paths src --ignore-src -r -y
# 编译
colcon build --packages-select ros_gz_bridge
编译时间大概5-10分钟,趁这个时间喝杯咖啡。我第一次编译时忘了装 rosdep,结果报了一堆错,后来才发现是依赖没装全。
1.3 理解仿真中的坐标系
环境搭好了,但别急着跑。先搞清楚一个关键问题:坐标系怎么对齐?
Gazebo Garden用的是ENU坐标系(东-北-天),而ROS2默认是FLU坐标系(前-左-上)。你想想看,如果两个系统各说各话,机器人往「前」走,在仿真里可能就变成往「东」飘了。
| 坐标系 | X轴 | Y轴 | Z轴 | 典型应用 |
|---|---|---|---|---|
| ENU (Gazebo) | 东 | 北 | 天 | 全局地图、GPS |
| FLU (ROS2) | 前 | 左 | 上 | 机器人本体、传感器 |
| NED (无人机) | 北 | 东 | 地 | 飞行控制、IMU |
我在做AGV项目时,就吃过这个亏。仿真里机器人定位明明是对的,但一接到真实里程计数据,轨迹就歪了。后来才发现是坐标系没转换。
ros_gz_bridge 的配置文件中,显式指定坐标系转换。比如:frame_id: "base_link" 对应ROS2的FLU,child_frame_id: "world" 对应Gazebo的ENU。
1.4 时间流:仿真时间 vs 真实时间
另一个容易懵的概念是时间。Gazebo里有自己的仿真时钟,和你的系统时钟是两码事。
为什么会这样?因为仿真可以加速、减速、甚至暂停。你想想看,如果仿真时间直接绑定真实时间,那你想让机器人跑100倍速做强化学习训练,就完全不可能了。
ROS2里通过 /clock 话题来同步时间。Gazebo Garden默认会发布 /clock,ROS2节点订阅它,就能感知到仿真时间的流逝。
# 查看仿真时间
ros2 topic echo /clock
# 输出示例
---
clock:
sec: 1234
nanosec: 567890000
注意:如果你在仿真里用了 ros::Time::now(),它拿到的就是仿真时间,不是你的电脑时间。我有个同事没注意这点,在仿真里做延时控制,结果真实世界过了10秒,仿真里才过了2秒,整个逻辑全乱了。
1.5 通信机制:话题、服务、动作
ROS2和Gazebo之间的通信,说白了就是三个套路:
- 话题(Topic): 单向数据流,适合传感器数据、状态发布。比如激光雷达数据从Gazebo发到ROS2。
- 服务(Service): 请求-响应模式,适合一次性操作。比如「请加载这个模型」。
- 动作(Action): 带反馈的长时间任务。比如「让机器人走到A点」,过程中不断汇报进度。
实际项目中,90%的场景用话题就够了。我一般这样配置桥接:
# 在 launch 文件中配置桥接
from launch import LaunchDescription
from launch_ros.actions import Node
def generate_launch_description():
bridge = Node(
package='ros_gz_bridge',
executable='parameter_bridge',
arguments=[
'/lidar/points@sensor_msgs/msg/PointCloud2@gz.msgs.PointCloudWithIntensity',
'/cmd_vel@geometry_msgs/msg/Twist@gz.msgs.Twist',
'/odom@nav_msgs/msg/Odometry@gz.msgs.Odometry'
],
output='screen'
)
return LaunchDescription([bridge])
格式是固定的:话题名@ROS2消息类型@Gazebo消息类型。注意方向箭头:@ 表示双向,[ 表示Gazebo到ROS2,] 表示ROS2到Gazebo。
1.6 知识体系总览
说了这么多,其实核心就一张图。我画了个SVG,帮你把整个体系串起来:
这张图我建议你保存下来。每次遇到通信问题,先看看是哪个环节出了岔子——是ROS2内部的问题,还是Gazebo的问题,还是桥接没配好。定位清楚了,解决起来就快。
1.7 验证你的环境
最后,跑一个最简单的测试,确保一切正常:
# 终端1:启动Gazebo空世界
gz sim empty.sdf
# 终端2:启动桥接
ros2 run ros_gz_bridge parameter_bridge /clock@rosgraph_msgs/msg/Clock@gz.msgs.Clock
# 终端3:查看时间同步
ros2 topic echo /clock
如果你能看到 /clock 话题在不断更新,说明ROS2和Gazebo已经成功「握手」了。这时候,你可以自豪地说:环境搭建完成!
/clock 没数据,先检查Gazebo是否在运行。有时候你关了仿真窗口,但后台进程没退出,用 ps aux | grep gz 查一下,手动kill掉再重试。
嗯,到这里,第一章的核心内容就差不多了。环境搭好了,坐标系、时间流、通信机制也理清了,接下来就可以真正开始写你的第一个仿真机器人了。
可可精品资料库