🤖 第12课:URDF机器人建模

工具与仿真 ✅ Docker验证通过

📋 课程目标

🧠 URDF是什么?

URDF(Unified Robot Description Format)是ROS中描述机器人结构的XML格式。它定义了机器人的链接(Link)关节(Joint),构成一棵运动学树。

URDF结构 = Link(链接) + Joint(关节) Link: 物理实体(有质量、惯性、外观) Joint: 连接两个Link的约束 ┌─────────┐ fixed ┌──────────┐ revolute ┌──────────┐ │ base_link│────────►│ shoulder │─────────►│ upper │ └─────────┘ └──────────┘ │ arm │ │ └──────────┘ │ fixed │ ▼ revolute │ revolute ┌──────────┐ ┌──────────┐ │ caster │ │ lower │ └──────────┘ │ arm │ └──────────┘ 关节类型: - fixed: 固定(无自由度) - revolute: 旋转(有角度限制) - continuous: 旋转(无角度限制,如轮子) - prismatic: 滑动(有位置限制)

📄 完整差速驱动机器人URDF

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

  
  <material name="blue">
    <color rgba="0.0 0.0 0.8 1.0"/>
  </material>
  <material name="black">
    <color rgba="0.1 0.1 0.1 1.0"/>
  </material>
  <material name="white">
    <color rgba="1.0 1.0 1.0 1.0"/>
  </material>
  <material name="gray">
    <color rgba="0.5 0.5 0.5 1.0"/>
  </material>

  
  <link name="base_footprint"/>

  <link name="base_link">
    <visual>
      <origin xyz="0 0 0" rpy="0 0 0"/>
      <geometry>
        <box size="0.4 0.3 0.12"/>
      </geometry>
      <material name="blue"/>
    </visual>
    <collision>
      <origin xyz="0 0 0" rpy="0 0 0"/>
      <geometry>
        <box size="0.4 0.3 0.12"/>
      </geometry>
    </collision>
    <inertial>
      <origin xyz="0 0 0" rpy="0 0 0"/>
      <mass value="4.0"/>
      <inertia ixx="0.036" ixy="0" ixz="0"
               iyy="0.056" iyz="0" izz="0.070"/>
    </inertial>
  </link>

  <joint name="base_joint" type="fixed">
    <parent link="base_footprint"/>
    <child link="base_link"/>
    <origin xyz="0 0 0.095" rpy="0 0 0"/>
  </joint>

  
  <link name="left_wheel_link">
    <visual>
      <origin xyz="0 0 0" rpy="1.5707 0 0"/>
      <geometry>
        <cylinder radius="0.065" length="0.04"/>
      </geometry>
      <material name="black"/>
    </visual>
    <collision>
      <origin xyz="0 0 0" rpy="1.5707 0 0"/>
      <geometry>
        <cylinder radius="0.065" length="0.04"/>
      </geometry>
    </collision>
    <inertial>
      <origin xyz="0 0 0" rpy="1.5707 0 0"/>
      <mass value="0.3"/>
      <inertia ixx="0.0004" ixy="0" ixz="0"
               iyy="0.0004" iyz="0" izz="0.0004"/>
    </inertial>
  </link>

  <joint name="left_wheel_joint" type="continuous">
    <parent link="base_link"/>
    <child link="left_wheel_link"/>
    <origin xyz="0 0.17 0" rpy="0 0 0"/>
    <axis xyz="0 1 0"/>
  </joint>

  
  <link name="right_wheel_link">
    <visual>
      <origin xyz="0 0 0" rpy="1.5707 0 0"/>
      <geometry>
        <cylinder radius="0.065" length="0.04"/>
      </geometry>
      <material name="black"/>
    </visual>
    <collision>
      <origin xyz="0 0 0" rpy="1.5707 0 0"/>
      <geometry>
        <cylinder radius="0.065" length="0.04"/>
      </geometry>
    </collision>
    <inertial>
      <origin xyz="0 0 0" rpy="1.5707 0 0"/>
      <mass value="0.3"/>
      <inertia ixx="0.0004" ixy="0" ixz="0"
               iyy="0.0004" iyz="0" izz="0.0004"/>
    </inertial>
  </link>

  <joint name="right_wheel_joint" type="continuous">
    <parent link="base_link"/>
    <child link="right_wheel_link"/>
    <origin xyz="0 -0.17 0" rpy="0 0 0"/>
    <axis xyz="0 1 0"/>
  </joint>

  
  <link name="caster_link">
    <visual>
      <origin xyz="0 0 0" rpy="0 0 0"/>
      <geometry>
        <sphere radius="0.03"/>
      </geometry>
      <material name="gray"/>
    </visual>
    <collision>
      <origin xyz="0 0 0" rpy="0 0 0"/>
      <geometry>
        <sphere radius="0.03"/>
      </geometry>
    </collision>
    <inertial>
      <mass value="0.1"/>
      <inertia ixx="0.00004" ixy="0" ixz="0"
               iyy="0.00004" iyz="0" izz="0.00004"/>
    </inertial>
  </link>

  <joint name="caster_joint" type="fixed">
    <parent link="base_link"/>
    <child link="caster_link"/>
    <origin xyz="-0.15 0 -0.065" rpy="0 0 0"/>
  </joint>

  
  <link name="laser_link">
    <visual>
      <origin xyz="0 0 0" rpy="0 0 0"/>
      <geometry>
        <cylinder radius="0.04" length="0.03"/>
      </geometry>
      <material name="black"/>
    </visual>
  </link>

  <joint name="laser_joint" type="fixed">
    <parent link="base_link"/>
    <child link="laser_link"/>
    <origin xyz="0.2 0 0.09" rpy="0 0 0"/>
  </joint>

  
  <gazebo reference="base_link">
    <material>Gazebo/Blue</material>
  </gazebo>
  <gazebo reference="left_wheel_link">
    <material>Gazebo/Black</material>
  </gazebo>
  <gazebo reference="right_wheel_link">
    <material>Gazebo/Black</material>
  </gazebo>

  
  <gazebo>
    <plugin name="diff_drive" filename="libgazebo_ros_diff_drive.so">
      <update_rate>50</update_rate>
      <left_joint>left_wheel_joint</left_joint>
      <right_joint>right_wheel_joint</right_joint>
      <wheel_separation>0.34</wheel_separation>
      <wheel_diameter>0.13</wheel_diameter>
      <command_topic>cmd_vel</command_topic>
      <odometry_topic>odom</odometry_topic>
      <odometry_frame>odom</odometry_frame>
      <base_frame_id>base_footprint</base_frame_id>
      <publish_odom>true</publish_odom>
      <publish_odom_tf>true</publish_odom_tf>
    </plugin>
  </gazebo>
</robot>

📐 Xacro宏简化

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

  <!-- 参数化属性 -->
  <xacro:property name="wheel_radius" value="0.065"/>
  <xacro:property name="wheel_length" value="0.04"/>
  <xacro:property name="wheel_separation" value="0.34"/>
  <xacro:property name="base_length" value="0.4"/>
  <xacro:property name="base_width" value="0.3"/>

  <!-- 轮子宏 -->
  <xacro:macro name="wheel" params="prefix y_offset">
    <link name="${prefix}_wheel_link">
      <visual>
        <origin xyz="0 0 0" rpy="1.5707 0 0"/>
        <geometry>
          <cylinder radius="${wheel_radius}" length="${wheel_length}"/>
        </geometry>
        <material name="black"/>
      </visual>
      <collision>
        <origin xyz="0 0 0" rpy="1.5707 0 0"/>
        <geometry>
          <cylinder radius="${wheel_radius}" length="${wheel_length}"/>
        </geometry>
      </collision>
      <inertial>
        <mass value="0.3"/>
        <inertia ixx="0.0004" ixy="0" ixz="0"
                 iyy="0.0004" iyz="0" izz="0.0004"/>
      </inertial>
    </link>

    <joint name="${prefix}_wheel_joint" type="continuous">
      <parent link="base_link"/>
      <child link="${prefix}_wheel_link"/>
      <origin xyz="0 ${y_offset} 0" rpy="0 0 0"/>
      <axis xyz="0 1 0"/>
    </joint>
  </xacro:macro>

  <!-- 使用宏 -->
  <xacro:wheel prefix="left" y_offset="${wheel_separation/2}"/>
  <xacro:wheel prefix="right" y_offset="${-wheel_separation/2}"/>

</robot>

🔍 URDF验证

# 检查URDF语法
check_urdf my_robot.urdf

# 查看URDF树结构
urdf_to_graphiz my_robot.urdf

# Xacro转URDF
xacro my_robot.urdf.xacro > my_robot.urdf

# 在rviz2中预览
ros2 run robot_state_publisher robot_state_publisher \
  --ros-args -p robot_description:="$(cat my_robot.urdf)"

# 另开终端
rviz2 -d /path/to/config.rviz

🎯 练习题

📝 练习1:扩展机器人模型

在差速驱动机器人上添加IMU传感器链接和摄像头链接。

📝 练习2:Xacro重构

将完整URDF重构为Xacro格式,使用宏和参数化属性。

📝 练习3:多机器人模型

创建一个带机械臂的移动机器人URDF,包含移动底盘和5自由度机械臂。

🏆 成就解锁

🏅 机器人建模师

经验值:+200 XP