【ROS2 速成 - Day23】Gazebo 机器人仿真入门(虚拟环境调试)

文章目录


前言:为什么机器人开发离不开仿真?

在嵌入式转型 ROS2 的过程中,很多工程师都会遇到一个痛点:硬件成本高、调试周期长、风险大

  • 每次修改代码都要烧录到硬件,重启机器人,效率极低
  • 算法测试时可能会撞坏机器人或周围环境
  • 多人协作开发时,硬件资源有限
  • 极端场景(如高速运动、复杂地形)难以在现实中复现

Gazebo 正是为解决这些问题而生的。它是 ROS2 官方推荐的物理仿真引擎,能够:

  • 精确模拟刚体物理、碰撞检测、重力、摩擦力等物理特性
  • 提供丰富的传感器模型(激光雷达、摄像头、IMU、里程计等)
  • 支持自定义机器人模型和环境场景
  • 与 ROS2 无缝集成,使用相同的话题、服务和动作接口

核心优势:你在仿真环境中编写的 ROS2 代码,几乎不需要修改就能直接运行在真实机器人上。这也是为什么所有专业机器人团队都会先在仿真中完成 90% 的算法开发,再进行硬件验证。


一、环境准备:ROS2 与 Gazebo 安装配置

1.1 版本对应关系

ROS2 与 Gazebo 有严格的版本对应关系,错误的版本组合会导致各种奇怪的问题:

ROS2 版本推荐 Gazebo 版本安装包名称
HumbleGazebo 11gazebo11
IronGazebo 11gazebo11
JazzyGazebo Harmonicgazebo-harmonic

本文以ROS2 Humble + Gazebo 11为例,这是目前工业界最稳定、使用最广泛的组合。

1.2 安装 Gazebo 与 ROS2 集成包

# 更新软件源
sudo apt update && sudo apt upgrade -y

# 安装Gazebo 11
sudo apt install gazebo11 libgazebo11-dev -y

# 安装ROS2与Gazebo的集成包
sudo apt install ros-humble-gazebo-ros-pkgs ros-humble-gazebo-ros2-control -y

# 安装机器人模型和控制器依赖
sudo apt install ros-humble-robot-state-publisher ros-humble-joint-state-publisher-gui -y
sudo apt install ros-humble-xacro ros-humble-urdf -y

1.3 验证安装

# 测试Gazebo是否能正常启动
gazebo --version
gazebo

# 测试ROS2-Gazebo集成
source /opt/ros/humble/setup.bash
ros2 launch gazebo_ros gazebo.launch.py

如果能成功启动 Gazebo 界面,说明环境配置正确。


二、Gazebo 基础:启动与界面详解

2.1 三种启动方式

Gazebo 有三种常用的启动方式,适用于不同场景:

方式 1:直接启动空世界

gazebo

这是最基础的启动方式,会打开一个只有地面和天空的空仿真环境。

方式 2:通过 ROS2 launch 启动

ros2 launch gazebo_ros gazebo.launch.py

这种方式会自动启动gazebo_ros节点,实现 ROS2 与 Gazebo 的通信。

方式 3:启动带预定义世界的仿真

# 启动官方示例世界
ros2 launch gazebo_ros gazebo.launch.py world:=/usr/share/gazebo-11/worlds/empty.world
ros2 launch gazebo_ros gazebo.launch.py world:=/usr/share/gazebo-11/worlds/warehouse.world

2.2 Gazebo 界面详解

Gazebo 界面主要分为以下几个区域:

  1. 左侧面板:模型库、世界树、属性编辑器

    • 模型库:包含各种预定义的物体(盒子、球体、圆柱体、机器人等)
    • 世界树:显示当前仿真环境中的所有物体及其层级关系
    • 属性编辑器:编辑选中物体的位置、旋转、质量、摩擦系数等属性
  2. 中央 3D 视图:仿真环境的可视化窗口

    • 鼠标左键:旋转视角
    • 鼠标右键:平移视角
    • 鼠标滚轮:缩放视角
    • Shift + 左键:拖动选中的物体
  3. 顶部工具栏:仿真控制按钮

    • 播放 / 暂停:控制仿真的运行和暂停
    • 步进:单步执行仿真
    • 重置:重置仿真到初始状态
    • 时间显示:显示仿真时间和实时时间的比例
  4. 底部状态栏:显示仿真状态、帧率、内存使用等信息


三、机器人模型加载:URDF 与 SDF 格式

Gazebo 支持两种机器人模型格式:URDFSDF

  • URDF:ROS 统一机器人描述格式,是 ROS 生态的标准格式,适合描述机器人的连杆和关节结构
  • SDF:仿真描述格式,是 Gazebo 原生格式,支持更丰富的物理属性和环境描述

在实际开发中,我们通常使用URDF + Gazebo 扩展标签的方式来定义机器人模型。

3.1 加载官方示例机器人

我们先从加载官方提供的 TurtleBot3 机器人开始,这是学习 ROS2 仿真最常用的测试平台。

# 安装TurtleBot3仿真包
sudo apt install ros-humble-turtlebot3-gazebo -y

# 设置TurtleBot3模型
export TURTLEBOT3_MODEL=burger

# 启动TurtleBot3仿真
ros2 launch turtlebot3_gazebo turtlebot3_empty_world.launch.py

启动成功后,你会看到一个 TurtleBot3 机器人出现在空世界中。

3.2 加载自定义 URDF 模型

下面我们来学习如何加载自己编写的 URDF 机器人模型。

步骤 1:创建一个简单的差分驱动机器人 URDF

创建文件my_robot.urdf

<?xml version="1.0"?>
<robot name="my_diffbot">
  <!-- 底盘连杆 -->
  <link name="base_link">
    <visual>
      <geometry>
        <box size="0.5 0.3 0.1"/>
      </geometry>
      <material name="blue">
        <color rgba="0 0 1 1"/>
      </material>
    </visual>
    <collision>
      <geometry>
        <box size="0.5 0.3 0.1"/>
      </geometry>
    </collision>
    <inertial>
      <mass value="1.0"/>
      <inertia ixx="0.01" ixy="0" ixz="0" iyy="0.01" iyz="0" izz="0.01"/>
    </inertial>
  </link>

  <!-- 左轮 -->
  <link name="left_wheel">
    <visual>
      <geometry>
        <cylinder radius="0.05" length="0.02"/>
      </geometry>
      <material name="black">
        <color rgba="0 0 0 1"/>
      </material>
    </visual>
    <collision>
      <geometry>
        <cylinder radius="0.05" length="0.02"/>
      </geometry>
    </collision>
    <inertial>
      <mass value="0.1"/>
      <inertia ixx="0.0001" ixy="0" ixz="0" iyy="0.0001" iyz="0" izz="0.0001"/>
    </inertial>
  </link>

  <!-- 右轮 -->
  <link name="right_wheel">
    <visual>
      <geometry>
        <cylinder radius="0.05" length="0.02"/>
      </geometry>
      <material name="black">
        <color rgba="0 0 0 1"/>
      </material>
    </visual>
    <collision>
      <geometry>
        <cylinder radius="0.05" length="0.02"/>
      </geometry>
    </collision>
    <inertial>
      <mass value="0.1"/>
      <inertia ixx="0.0001" ixy="0" ixz="0" iyy="0.0001" iyz="0" izz="0.0001"/>
    </inertial>
  </link>

  <!-- 左轮关节 -->
  <joint name="left_wheel_joint" type="continuous">
    <parent link="base_link"/>
    <child link="left_wheel"/>
    <origin xyz="0 0.15 0" rpy="1.5708 0 0"/>
    <axis xyz="0 0 1"/>
  </joint>

  <!-- 右轮关节 -->
  <joint name="right_wheel_joint" type="continuous">
    <parent link="base_link"/>
    <child link="right_wheel"/>
    <origin xyz="0 -0.15 0" rpy="1.5708 0 0"/>
    <axis xyz="0 0 1"/>
  </joint>

  <!-- Gazebo插件:差分驱动控制器 -->
  <gazebo>
    <plugin name="diff_drive" filename="libgazebo_ros_diff_drive.so">
      <robotNamespace>/</robotNamespace>
      <leftJoint>left_wheel_joint</leftJoint>
      <rightJoint>right_wheel_joint</rightJoint>
      <wheelSeparation>0.3</wheelSeparation>
      <wheelDiameter>0.1</wheelDiameter>
      <publishTf>true</publishTf>
      <publishOdom>true</publishOdom>
      <odometryTopic>odom</odometryTopic>
      <commandTopic>cmd_vel</commandTopic>
    </plugin>
  </gazebo>
</robot>

步骤 2:创建 launch 文件加载模型

创建文件spawn_my_robot.launch.py

import os
from launch import LaunchDescription
from launch.actions import ExecuteProcess
from launch_ros.actions import Node
from ament_index_python.packages import get_package_share_directory

def generate_launch_description():
    # 获取URDF文件路径
    urdf_path = os.path.join(os.getcwd(), 'my_robot.urdf')
    with open(urdf_path, 'r') as f:
        robot_desc = f.read()

    return LaunchDescription([
        # 启动Gazebo空世界
        ExecuteProcess(
            cmd=['gazebo', '--verbose', '-s', 'libgazebo_ros_factory.so'],
            output='screen'
        ),

        # 启动机器人状态发布者
        Node(
            package='robot_state_publisher',
            executable='robot_state_publisher',
            name='robot_state_publisher',
            output='screen',
            parameters=[{'robot_description': robot_desc}]
        ),

        # 生成机器人模型
        Node(
            package='gazebo_ros',
            executable='spawn_entity.py',
            arguments=['-topic', 'robot_description', '-entity', 'my_diffbot'],
            output='screen'
        )
    ])

步骤 3:运行 launch 文件

source /opt/ros/humble/setup.bash
python3 spawn_my_robot.launch.py

如果一切正常,你会看到一个简单的蓝色差分驱动机器人出现在 Gazebo 中。


四、虚拟底盘运动控制:ROS2 话题通信

现在我们已经成功加载了机器人模型,接下来学习如何通过 ROS2 话题控制机器人运动。

4.1 查看可用话题

打开一个新终端,运行:

source /opt/ros/humble/setup.bash
ros2 topic list

你应该能看到以下关键话题:

  • /cmd_vel:速度命令话题,用于控制机器人运动
  • /odom:里程计话题,发布机器人的位置和速度信息
  • /tf:坐标变换话题,发布机器人各连杆之间的变换关系
  • /joint_states:关节状态话题,发布机器人各关节的角度和速度

4.2 使用键盘控制机器人运动

ROS2 提供了一个非常方便的键盘控制节点teleop_twist_keyboard

# 安装键盘控制包
sudo apt install ros-humble-teleop-twist-keyboard -y

# 运行键盘控制节点
ros2 run teleop_twist_keyboard teleop_twist_keyboard

按照终端提示,使用以下按键控制机器人:

  • i:前进
  • ,:后退
  • j:左转
  • l:右转
  • k:停止
  • q/z:增加 / 减少线速度
  • w/x:增加 / 减少角速度

你会看到 Gazebo 中的机器人随着你的按键运动,这说明 ROS2 与 Gazebo 的通信已经正常工作。

4.3 编写 C++ 代码控制机器人

下面我们来编写一个简单的 C++ 节点,实现机器人的自动运动控制。

创建文件robot_controller.cpp

#include "rclcpp/rclcpp.hpp"
#include "geometry_msgs/msg/twist.hpp"

class RobotController : public rclcpp::Node
{
public:
    RobotController() : Node("robot_controller")
    {
        // 创建速度命令发布者
        publisher_ = this->create_publisher<geometry_msgs::msg::Twist>("/cmd_vel", 10);
        
        // 创建定时器,每100ms发布一次速度命令
        timer_ = this->create_wall_timer(
            std::chrono::milliseconds(100),
            std::bind(&RobotController::timer_callback, this)
        );
        
        RCLCPP_INFO(this->get_logger(), "机器人控制器已启动");
    }

private:
    void timer_callback()
    {
        auto msg = geometry_msgs::msg::Twist();
        
        // 设置线速度和角速度
        msg.linear.x = 0.2;  // 前进速度0.2m/s
        msg.angular.z = 0.1; // 左转角速度0.1rad/s
        
        publisher_->publish(msg);
    }

    rclcpp::Publisher<geometry_msgs::msg::Twist>::SharedPtr publisher_;
    rclcpp::TimerBase::SharedPtr timer_;
};

int main(int argc, char * argv[])
{
    rclcpp::init(argc, argv);
    rclcpp::spin(std::make_shared<RobotController>());
    rclcpp::shutdown();
    return 0;
}

编译并运行这个节点,你会看到机器人开始做圆周运动。这就是我们在仿真环境中编写的第一个机器人控制程序!


五、虚拟传感器数据获取:激光雷达与摄像头

除了控制机器人运动,Gazebo 还能模拟各种传感器的数据,这是仿真环境最强大的功能之一。

5.1 添加激光雷达传感器

我们在之前的 URDF 模型中添加一个激光雷达传感器:

my_robot.urdf</robot>标签前添加以下内容:

  <!-- 激光雷达连杆 -->
  <link name="laser_link">
    <visual>
      <geometry>
        <cylinder radius="0.03" length="0.05"/>
      </geometry>
      <material name="red">
        <color rgba="1 0 0 1"/>
      </material>
    </visual>
    <collision>
      <geometry>
        <cylinder radius="0.03" length="0.05"/>
      </geometry>
    </collision>
    <inertial>
      <mass value="0.05"/>
      <inertia ixx="0.00001" ixy="0" ixz="0" iyy="0.00001" iyz="0" izz="0.00001"/>
    </inertial>
  </link>

  <!-- 激光雷达关节 -->
  <joint name="laser_joint" type="fixed">
    <parent link="base_link"/>
    <child link="laser_link"/>
    <origin xyz="0.2 0 0.1" rpy="0 0 0"/>
  </joint>

  <!-- Gazebo激光雷达插件 -->
  <gazebo reference="laser_link">
    <sensor type="ray" name="laser_sensor">
      <update_rate>10</update_rate>
      <ray>
        <scan>
          <horizontal>
            <samples>360</samples>
            <resolution>1</resolution>
            <min_angle>-3.14159</min_angle>
            <max_angle>3.14159</max_angle>
          </horizontal>
        </scan>
        <range>
          <min>0.1</min>
          <max>10.0</max>
          <resolution>0.01</resolution>
        </range>
      </ray>
      <plugin name="gazebo_ros_laser" filename="libgazebo_ros_ray_sensor.so">
        <topic_name>scan</topic_name>
        <frame_name>laser_link</frame_name>
      </plugin>
    </sensor>
  </gazebo>

重新启动仿真,然后查看激光雷达数据:

bash

运行

# 查看激光雷达话题
ros2 topic list | grep scan

# 打印激光雷达数据
ros2 topic echo /scan

5.2 添加摄像头传感器

同样,我们可以添加一个摄像头传感器:

在 URDF 中添加以下内容:

  <!-- 摄像头连杆 -->
  <link name="camera_link">
    <visual>
      <geometry>
        <box size="0.05 0.03 0.03"/>
      </geometry>
      <material name="green">
        <color rgba="0 1 0 1"/>
      </material>
    </visual>
    <collision>
      <geometry>
        <box size="0.05 0.03 0.03"/>
      </geometry>
    </collision>
    <inertial>
      <mass value="0.02"/>
      <inertia ixx="0.000001" ixy="0" ixz="0" iyy="0.000001" iyz="0" izz="0.000001"/>
    </inertial>
  </link>

  <!-- 摄像头关节 -->
  <joint name="camera_joint" type="fixed">
    <parent link="base_link"/>
    <child link="camera_link"/>
    <origin xyz="0.25 0 0.15" rpy="0 0 0"/>
  </joint>

  <!-- Gazebo摄像头插件 -->
  <gazebo reference="camera_link">
    <sensor type="camera" name="camera_sensor">
      <update_rate>30</update_rate>
      <camera>
        <horizontal_fov>1.047</horizontal_fov>
        <image>
          <width>640</width>
          <height>480</height>
          <format>R8G8B8</format>
        </image>
        <clip>
          <near>0.1</near>
          <far>100</far>
        </clip>
      </camera>
      <plugin name="gazebo_ros_camera" filename="libgazebo_ros_camera.so">
        <topic_name>camera/image_raw</topic_name>
        <frame_name>camera_link</frame_name>
      </plugin>
    </sensor>
  </gazebo>

重新启动仿真,然后使用rqt_image_view查看摄像头图像:

bash

运行

# 安装rqt_image_view
sudo apt install ros-humble-rqt-image-view -y

# 运行rqt_image_view
rqt_image_view

在弹出的窗口中选择/camera/image_raw话题,你就能看到虚拟摄像头拍摄的画面了。


六、仿真调试技巧与常见问题

6.1 常用调试命令

# 查看Gazebo日志
gazebo --verbose

# 查看ROS2节点信息
ros2 node list
ros2 node info /gazebo

# 查看话题信息
ros2 topic info /cmd_vel
ros2 topic hz /scan  # 查看话题发布频率

# 查看TF变换
ros2 run tf2_tools view_frames

6.2 常见问题及解决方法

问题 1:Gazebo 启动时卡在加载界面

原因:模型下载速度慢或模型文件损坏解决方法

# 清理Gazebo模型缓存
rm -rf ~/.gazebo/models/*

# 手动下载常用模型
git clone https://github.com/osrf/gazebo_models.git ~/.gazebo/models

问题 2:机器人模型加载后消失或穿透地面

原因:惯性参数设置错误解决方法:检查 URDF 中所有<inertial>标签的质量和惯性矩阵值,确保它们是合理的正数。

问题 3:机器人运动不平稳或抖动

原因:物理参数设置不当或仿真步长太大解决方法

  • 调整 Gazebo 的物理参数:在 Gazebo 界面中点击Edit -> World Properties -> Physics
  • 减小仿真步长(max_step_size
  • 增加迭代次数(iters

问题 4:传感器数据不发布

原因:插件配置错误或话题名称不匹配解决方法

  • 检查 URDF 中插件的topic_nameframe_name参数
  • 查看 Gazebo 日志,寻找插件加载错误信息

七、总结与下期预告

今天我们系统学习了 Gazebo 机器人仿真的基础知识,包括:

  • Gazebo 的安装配置和启动方法
  • URDF 机器人模型的编写和加载
  • 通过 ROS2 话题控制虚拟底盘运动
  • 激光雷达和摄像头等虚拟传感器的使用
  • 仿真调试技巧和常见问题解决

核心收获:仿真环境是机器人开发的 "虚拟实验室",它能让你在没有硬件的情况下快速验证算法,大大提高开发效率。你今天编写的控制代码,未来可以直接移植到真实的嵌入式小车上。


📢 下期连载预告【ROS2 速成 - Day24~Day28】完整 ROS2 实战项目落地|嵌入式遥控小车全项目

接下来的 5 天,我们将把前面所学的所有知识整合起来,从零开始完成一个完整的嵌入式遥控小车项目:

  • Day24:项目整体架构设计与硬件选型
  • Day25:STM32 底层驱动开发与 ROS2 通信
  • Day26:小车底盘控制与里程计计算
  • Day27:激光雷达 SLAM 建图与导航
  • Day28:项目部署与实战演示

这将是本系列最有价值的部分,我会分享完整的工程代码和硬件对接细节,帮助你真正实现从嵌入式到 ROS2 的转型。


码字不易,欢迎点赞 + 收藏 + 关注,后续按【ROS2 速成 - DayX】持续连载更新 ROS2 嵌入式实战代码、硬件对接源码、项目工程模板,嵌入式转型 ROS2 不踩坑!

如果有任何问题,欢迎在评论区留言交流,我会一一回复。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值