ROS 2 Lyrical 第四章 URDF机器人建模与Gazebo Garden仿真

前言

本章完整迁移原版ROS Kinetic URDF/Gazebo内容,全量适配ROS 2 Lyrical + Ubuntu 26.04 + Gazebo Garden,修正ROS1专属rospack、roslaunch XML、gazebo_ros旧插件,替换为colcon、Python Launch、gazebo_ros2、xacro ROS2标准接口,统一DDS分布式通信逻辑。
内容严格遵循ROS 2官方机器人建模规范,覆盖URDF基础语法、Xacro宏简化建模、RViz2可视化、Gazebo物理仿真、传感器插件、差速底盘驱动、自定义仿真场景全流程,所有代码、命令可直接在Lyrical环境复现,兼顾理论严谨性与实操可落地性。

本章核心学习目标

  1. 掌握URDF标准格式:link视觉/碰撞/惯性、五类joint关节语法
  2. 使用check_urdfurdf_to_graphiz校验机器人模型
  3. RViz2加载URDF/Xacro,关节滑块调试运动模型
  4. Xacro常量、数学运算、复用宏,消除重复建模代码
  5. C++关节状态发布节点,驱动模型循环运动
  6. 外部3D网格(STL/DAE)导入机器人模型
  7. Gazebo Garden物理仿真配置:碰撞、惯性、材质渲染
  8. 激光雷达、相机Gazebo ROS2传感器插件配置
  9. 四轮差速滑移转向驱动插件,通过/cmd_vel遥控小车
  10. 自定义仿真世界、室内办公室场景加载与多传感器可视化

4.1 机器人描述文件URDF基础

4.1.1 URDF定义

URDF(Unified Robot Description Format,统一机器人描述格式)是基于XML的机器人标准化描述语言,用于定义机器人连杆几何、关节运动约束、惯性、碰撞外形、传感器挂载,是RViz2、Gazebo仿真的底层模型标准,所有ROS2机器人均依赖URDF/Xacro文件。

核心两大基础标签:

  • <link>:机器人独立刚体(底盘、轮子、机械臂杆件、传感器外壳),包含视觉渲染、碰撞检测、惯性物理参数;
  • <joint>:两个连杆之间的运动连接,定义父子坐标系、运动自由度、运动限位。

4.1.2 最简四轮小车URDF示例(chapter4_tutorials/robot1_description/urdf/robot1.urdf)

<?xml version="1.0"?>
<robot name="Robot1">
  <!-- 底盘主体连杆 -->
  <link name="base_link">
    <visual>
      <geometry>
        <box size="0.2 0.3 0.1" />
      </geometry>
      <origin rpy="0 0 0" xyz="0 0 0.05" />
      <material name="white">
        <color rgba="1 1 1 1" />
      </material>
    </visual>
  </link>

  <!-- 四个车轮连杆 -->
  <link name="wheel_1">
    <visual>
      <geometry>
        <cylinder length="0.05" radius="0.05" />
      </geometry>
      <origin rpy="0 1.57 0" xyz="0.1 0.1 0" />
      <material name="black"><color rgba="0 0 0 1" /></material>
    </visual>
  </link>
  <link name="wheel_2">
    <visual>
      <geometry><cylinder length="0.05" radius="0.05" /></geometry>
      <origin rpy="0 1.57 0" xyz="-0.1 0.1 0" />
      <material name="black" />
    </visual>
  </link>
  <link name="wheel_3">
    <visual>
      <geometry><cylinder length="0.05" radius="0.05" /></geometry>
      <origin rpy="0 1.57 0" xyz="0.1 -0.1 0" />
      <material name="black" />
    </visual>
  </link>
  <link name="wheel_4">
    <visual>
      <geometry><cylinder length="0.05" radius="0.05" /></geometry>
      <origin rpy="0 1.57 0" xyz="-0.1 -0.1 0" />
      <material name="black" />
    </visual>
  </link>

  <!-- 固定关节连接底盘与车轮 -->
  <joint name="base_to_wheel1" type="fixed">
    <parent link="base_link"/>
    <child link="wheel_1"/>
    <origin xyz="0 0 0"/>
  </joint>
  <joint name="base_to_wheel2" type="fixed">
    <parent link="base_link"/>
    <child link="wheel_2"/>
    <origin xyz="0 0 0"/>
  </joint>
  <joint name="base_to_wheel3" type="fixed">
    <parent link="base_link"/>
    <child link="wheel_3"/>
    <origin xyz="0 0 0"/>
  </joint>
  <joint name="base_to_wheel4" type="fixed">
    <parent link="base_link"/>
    <child link="wheel_4"/>
    <origin xyz="0 0 0"/>
  </joint>
</robot>

4.1.3 Link完整字段说明

单个link包含三层可选配置,Gazebo仿真必须填写collision、inertial,仅RViz可视化只需visual:

  1. <visual>:RViz/Gazebo渲染外观,几何(box/cylinder/sphere/mesh)、坐标偏移origin、材质color/texture;
  2. <collision>:物理碰撞外形,建议使用简化几何体降低仿真算力;
  3. <inertial>:刚体质量、惯性矩阵,Gazebo物理引擎动力学计算必需。

4.1.4 Joint五种标准类型

joint类型 运动特性 适用场景
fixed 完全锁死无相对运动 车身固定传感器、固定底座
continuous 无角度限位旋转 驱动车轮、无限制旋转云台
revolute 带角度上下限位旋转 机械臂关节、舵机
prismatic 直线平移运动 伸缩推杆、升降机构
floating 6自由度全自由 空中无人机、悬浮物体

revolute关节完整示例(机械臂关节):

<joint name="arm_1_to_arm_base" type="revolute">
  <parent link="arm_base" />
  <child link="arm_1" />
  <axis xyz="1 0 0" /> <!-- 旋转轴 -->
  <origin xyz="0 0 0.15" />
  <!-- 限位:力矩上限、最小/最大转角、最大转速 -->
  <limit effort="1000.0" lower="-1.0" upper="1.0" velocity="0.5" />
</joint>

4.2 URDF模型校验工具(ROS2 Lyrical适配)

在这里插入图片描述

4.2.1 语法校验 check_urdf

# 进入urdf目录执行
check_urdf robot1.urdf

输出示例:

robot name is: Robot1
---------- Successfully Parsed XML -------------
root Link: base_link has 4 child(ren)
child(1): wheel_1
child(2): wheel_2
child(3): wheel_3
child(4): wheel_4

校验失败会直接提示标签缺失、父子link不存在、关节参数非法。

在这里插入图片描述

4.2.2 生成连杆关节拓扑图 urdf_to_graphiz

urdf_to_graphiz robot1.urdf
evince origins.pdf

生成PDF拓扑图,直观查看父子连杆层级关系,快速排查关节连接错误。

在这里插入图片描述

4.3 RViz2加载URDF可视化模型

4.3.1 Python Launch启动文件(ROS2标准,替代XML launch)

robot1_description/launch/display.launch.py

from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument
from launch.substitutions import LaunchConfiguration
from launch_ros.actions import Node

def generate_launch_description():
    # 声明模型路径参数
    model_arg = DeclareLaunchArgument(name="model")
    gui_arg = DeclareLaunchArgument(name="gui", default_value="False")
    model_path = LaunchConfiguration("model")
    use_gui = LaunchConfiguration("gui")

    return LaunchDescription([
        model_arg,
        gui_arg,
        # 将URDF载入参数服务器robot_description
        Node(
            package="robot_state_publisher",
            executable="robot_state_publisher",
            parameters=[{"robot_description": model_path}]
        ),
        # 关节状态发布器,gui=True弹出滑块面板
        Node(
            package="joint_state_publisher_gui",
            executable="joint_state_publisher_gui",
            condition=LaunchConfigurationEquals(use_gui, "True")
        ),
        Node(
            package="joint_state_publisher",
            executable="joint_state_publisher",
            condition=LaunchConfigurationEquals(use_gui, "False")
        ),
        # 启动RViz2
        Node(package="rviz2", executable="rviz2")
    ])

在这里插入图片描述

4.3.2 启动命令(ROS2替换rospack为ament索引)

ros2 launch robot1_description display.launch.py \
model:=$(ament_index_get_resource robot1_description urdf/robot1.urdf) gui:=true
  • gui:=true 弹出Joint State Publisher滑块面板,拖拽实时调节机械臂关节角度;
  • RViz2左侧Add添加RobotModel、TF插件,Fixed Frame设为base_link即可显示小车3D模型。

扩展机械臂夹持器完整模型效果:

在这里插入图片描述

4.4 Xacro简化建模(ROS2推荐标准)

原生URDF重复几何、参数会造成文件冗余,Xacro(XML宏)提供常量、数学运算、自定义宏、文件包含四大核心能力,大幅精简代码,所有ROS2官方机器人均使用Xacro。

4.4.1 Xacro文件基础头(必须命名空间)

<?xml version="1.0"?>
<robot xmlns:xacro="http://www.ros.org/wiki/xacro" name="robot1_xacro">

4.4.2 常量定义与数学运算

<!-- 车轮参数常量 -->
<xacro:property name="wheel_radius" value="0.05"/>
<xacro:property name="wheel_length" value="0.05"/>
<!-- 引用常量 + 四则运算 -->
<cylinder radius="${wheel_radius}" length="${wheel_length}"/>
<origin xyz="${0.2/2} 0 0"/>

4.4.3 复用宏:统一惯性参数模板

<!-- 通用惯性宏,输入质量自动填充惯性矩阵 -->
<xacro:macro name="default_inertial" params="mass">
  <inertial>
    <mass value="${mass}"/>
    <inertia ixx="1e-3" ixy="0" ixz="0" iyy="1e-3" iyz="0" izz="1e-3"/>
  </inertial>
</xacro:macro>

<!-- 任意连杆直接调用 -->
<xacro:default_inertial mass="10"/>

4.4.4 Xacro转URDF编译

终端手动转换

xacro $(ament_index_get_resource robot1_description urdf/robot1.xacro) > robot1_proc.urdf

Launch自动转换(推荐,无需手动生成文件)

在robot_state_publisher参数中直接执行xacro解析命令,launch文件内写法:

parameters=[{
    "robot_description": Command([
        "xacro ", LaunchConfiguration("model")
    ])
}]

4.5 外部3D网格模型导入(STL/DAE)

URDF支持加载三维建模软件(SolidWorks、SketchUp、Blender)导出的网格文件,提升模型精细度,语法:

<link name="gripper">
  <visual>
    <geometry>
      <!-- package:// 定位功能包内mesh资源 -->
      <mesh filename="package://pr2_description/meshes/gripper_v0/l_finger.dae" scale="1 1 1"/>
    </geometry>
  </visual>
</link>

SketchUp导出DAE网格小车RViz渲染效果:

在这里插入图片描述

4.6 C++节点驱动机器人自动运动

通过发布sensor_msgs/msg/JointState话题更新关节角度,广播TF坐标变换,实现小车原地圆周运动+机械臂往复摆动。
在这里插入图片描述

4.6.1 核心逻辑说明

  1. JointState消息:存储所有关节名称、实时角度;
  2. tf2_ros::TransformBroadcaster发布odom → base_link世界坐标变换;
  3. 循环累加关节角度,设置边界反向实现往复运动。

4.6.2 适配ROS 2 Lyrical完整代码

#include <rclcpp/rclcpp.hpp>
#include <sensor_msgs/msg/joint_state.hpp>
#include <tf2_ros/transform_broadcaster.h>
#include <geometry_msgs/msg/transform_stamped.hpp>
#include <cmath>

using namespace std::chrono_literals;

class StatePublisher : public rclcpp::Node
{
public:
  StatePublisher() : Node("state_publisher")
  {
    joint_pub_ = this->create_publisher<sensor_msgs::msg::JointState>("joint_states", 10);
    tf_broadcaster_ = std::make_shared<tf2_ros::TransformBroadcaster>(this);
    timer_ = this->create_wall_timer(33ms, std::bind(&StatePublisher::timer_cb, this));
    angle_ = 0.0;
    arm_inc_ = 0.005;
  }

private:
  void timer_cb()
  {
    sensor_msgs::msg::JointState js;
    js.header.stamp = this->get_clock()->now();
    // 7个运动关节
    js.name = {
      "base_to_arm_base", "arm_1_to_arm_base", "arm_2_to_arm_1",
      "left_gripper_joint", "left_tip_joint", "right_gripper_joint", "right_tip_joint"
    };
    js.position.resize(7);
    js.position[0] = sin(angle_ * 0.5);
    js.position[1] = sin(angle_ * 0.8);
    js.position[2] = sin(angle_);
    js.position[3] = fabs(sin(angle_ * 2));
    js.position[4] = fabs(sin(angle_ * 2));
    js.position[5] = fabs(sin(angle_ * 2));
    js.position[6] = fabs(sin(angle_ * 2));
    joint_pub_->publish(js);

    // 发布odom到base_link变换,小车圆周运动
    geometry_msgs::msg::TransformStamped odom_tf;
    odom_tf.header.stamp = this->get_clock()->now();
    odom_tf.header.frame_id = "odom";
    odom_tf.child_frame_id = "base_link";
    odom_tf.transform.translation.x = cos(angle_);
    odom_tf.transform.translation.y = sin(angle_);
    odom_tf.transform.rotation.z = sin(angle_/2);
    odom_tf.transform.rotation.w = cos(angle_/2);
    tf_broadcaster_->sendTransform(odom_tf);

    angle_ += 0.005;
  }

  rclcpp::Publisher<sensor_msgs::msg::JointState>::SharedPtr joint_pub_;
  std::shared_ptr<tf2_ros::TransformBroadcaster> tf2_broadcaster_;
  rclcpp::TimerBase::SharedPtr timer_;
  double angle_;
  double arm_inc_;
};

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

编译配置添加ament依赖rclcpp sensor_msgs tf2_ros geometry_msgs,启动后RViz2可见小车绕圈、机械臂自动摆动:
在这里插入图片描述

4.7 Gazebo Garden 物理仿真完整部署

ROS2 Lyrical配套仿真器为Gazebo Garden,替代ROS1 Gazebo 11,使用gazebo_ros2插件实现ROS-DDS双向通信,支持动力学、碰撞、传感器物理噪声。

4.7.1 前置安装

sudo apt install ros-lyrical-gazebo-ros2 gazebo-garden ros-lyrical-xacro

4.7.2 Xacro仿真必备配置(collision+inertial+gazebo标签)

<link name="base_link">
  <visual>
    <box size="0.2 0.3 0.1"/>
    <origin xyz="0 0 0.05"/>
    <material name="orange"/>
  </visual>
  <!-- 碰撞外形 -->
  <collision>
    <box size="0.2 0.3 0.1"/>
  </collision>
  <!-- 惯性参数 -->
  <xacro:default_inertial mass="10"/>
</link>
<!-- Gazebo材质渲染 -->
<gazebo reference="base_link">
  <material>Gazebo/Orange</material>
</gazebo>
<gazebo reference="wheel_1">
  <material>Gazebo/Black</material>
</gazebo>

无gazebo材质标签时模型为纯白:

在这里插入图片描述

4.7.3 Gazebo启动Python Launch文件

from launch import LaunchDescription
from launch.actions import IncludeLaunchDescription, DeclareLaunchArgument
from launch.launch_description_sources import PythonLaunchDescriptionSource
from launch.substitutions import LaunchConfiguration
from launch_ros.actions import Node
from ament_index_python.packages import get_package_share_directory
import os

def generate_launch_description():
    model_arg = DeclareLaunchArgument("model")
    model_path = LaunchConfiguration("model")
    pkg_gazebo = get_package_share_directory("gazebo_ros2")
    empty_world = IncludeLaunchDescription(
        PythonLaunchDescriptionSource(
            os.path.join(pkg_gazebo, "launch", "empty_world.launch.py")
        )
    )
    # 加载机器人模型到参数服务器
    robot_state_pub = Node(
        package="robot_state_publisher",
        executable="robot_state_publisher",
        parameters=[{"robot_description": Command(["xacro ", model_path])}]
    )
    # Gazebo生成机器人spawn节点
    spawn_robot = Node(
        package="gazebo_ros2",
        executable="spawn_entity.py",
        arguments=[
            "-topic", "robot_description",
            "-entity", "robot1",
            "-z", "0.05"
        ],
        output="screen"
    )
    return LaunchDescription([model_arg, empty_world, robot_state_pub, spawn_robot])

启动命令:

roslaunch robot1_gazebo gazebo.launch.py model:=$(ament_index_get_resource robot1_description urdf/robot1_base_02.xacro)

在这里插入图片描述

4.8 Gazebo传感器插件:激光雷达+相机

4.8.1 Hokuyo激光雷达Xacro配置

  1. 定义雷达link几何网格;
  2. <gazebo>内加载libgazebo_ros2_laser.so射线传感器插件,设置测距范围、角度、高斯噪声、发布话题。
<link name="hokuyo_link">
  <collision><box size="0.1 0.1 0.1"/></collision>
  <visual><mesh filename="package://robot1_description/meshes/hokuyo.dae"/></visual>
  <xacro:default_inertial mass="0.00001"/>
</link>
<joint name="base_to_laser" type="fixed">
  <parent link="base_link"/><child link="hokuyo_link"/><origin xyz="0.1 0 0.1"/>
</joint>

<gazebo reference="hokuyo_link">
  <sensor type="ray" name="laser_scan">
    <update_rate>40</update_rate>
    <scan>
      <horizontal samples="720" min_angle="-1.57" max_angle="1.57"/>
    </scan>
    <range min="0.1" max="30" resolution="0.01"/>
    <noise type="gaussian" mean="0" stddev="0.01"/>
    <plugin filename="libgazebo_ros2_laser.so" name="laser_controller">
      <topicName>/robot/scan</topicName>
      <frameName>hokuyo_link</frameName>
    </plugin>
  </sensor>
</gazebo>

相机插件配置逻辑与激光雷达一致,发布/robot/camera/image_raw图像话题。
带雷达、相机小车Gazebo界面:

在这里插入图片描述

4.9 四轮差速滑移转向驱动插件

添加Gazebo差速驱动插件,订阅标准geometry_msgs/msg/Twist话题/cmd_vel,实现键盘遥控小车:

<gazebo>
  <plugin name="skid_steer" filename="libgazebo_ros2_skid_steer_drive.so">
    <updateRate>100</updateRate>
    <leftFrontJoint>base_to_wheel1</leftFrontJoint>
    <rightFrontJoint>base_to_wheel3</rightFrontJoint>
    <leftRearJoint>base_to_wheel2</leftRearJoint>
    <rightRearJoint>base_to_wheel4</rightRearJoint>
    <wheelSeparation>0.2</wheelSeparation>
    <wheelDiameter>0.1</wheelDiameter>
    <topicName>/cmd_vel</topicName>
  </plugin>
</gazebo>

键盘遥控工具:

sudo apt install ros-lyrical-teleop-twist-keyboard
ros2 run teleop_twist_keyboard teleop_twist_keyboard

遥控操控界面效果:需完善

4.10 自定义仿真室内场景

Gazebo支持加载预设办公室世界(Willow Garage车库室内环境),在launch中包含官方世界文件,将机器人生成在室内地图中,可做SLAM、导航仿真:

world_launch = IncludeLaunchDescription(
    PythonLaunchDescriptionSource(
        os.path.join(get_package_share_directory("gazebo_ros2"), "launch/willowgarage_world.launch.py")
    )
)

室内仿真地图展示:需完善

在这里插入图片描述

本章小结

  1. URDF是ROS2机器人标准描述语言,link负责刚体几何物理,joint定义运动约束,仿真必须配置collision与inertial标签;
  2. Xacro通过常量、宏、数学运算大幅简化重复建模,是工业机器人开发首选格式;
  3. RViz2用于纯可视化调试,可拖拽滑块手动调节关节;Gazebo Garden提供完整物理动力学、传感器噪声仿真;
  4. 支持外部STL/DAE三维网格导入,快速复用三维建模软件成品模型;
  5. gazebo_ros2系列插件实现激光、相机、差速驱动仿真,统一ROS2 DDS话题通信接口;
  6. teleop_twist_keyboard通过标准/cmd_vel话题通用控制所有移动机器人,代码兼容性极强;
  7. 可加载官方室内仿真世界,直接开展导航、SLAM、避障算法离线仿真,无需实体硬件。

下一章将基于本章小车模型,讲解ROS2导航2框架、代价地图、全局/局部路径规划完整仿真实操。

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐