1. 从零开始用URDF构建视觉机器人模型

目标:学习如何构建可以在Rviz中查看的机器人视觉模型

教程级别:入门

说明:本教程假设您知道如何编写格式良好的XML代码

在本教程中将会构建一个看起来像R2D2的机器人视觉模型。在后续教程中,您将学习如何表达模型、添加一些物理属性以及使用xacro生成更简洁的代码,但现在将专注于获得正确的视觉几何。

在继续之前,请确保安装了joint_state_publisher软件包。如果您安装了urdf_tutorial二进制包文件,则应该已经有了该软件包。如果没有,请更新您的安装以包含该软件包(使用rosdep来进行检查)。

本教程中提到的所有机器人模型(和源代码文件)都可以在urdf_tutorial软件包中找到。

1.1 单一形状

首先仅仅来探索单个简单的形状。这是您可以制作的最简单urdf。[源代码文件:01-myfirst.urdf]

<?xml version="1.0"?>
<robot name="myfirst">
  <link name="base_link">
    <visual>
      <geometry>
        <cylinder length="0.6" radius="0.2"/>
      </geometry>
    </visual>
  </link>
</robot>

若将上面这个XML翻译成英文,意思就是:这是一个名为myfirst的机器人,该机器人只包含一个链接(也称为部件),其视觉组件只是一个长0.6米、半径为0.2米的圆柱体。这可能看起来像是很多用于简单的“hello world”类型示例的封闭标签,但请相信我,那会更加复杂。

要检查该机器人模型,请启动display.launch.py文件:

ros2 launch urdf_tutorial display.launch.py model:=urdf/01-myfirst.urdf

这条命令做了以下三件事情:

● 加载了该特定模型并将其保存为一个参数

● 运行了节点以发布sensor_msgs/msg/JointState消息并进行坐标变换(稍后会详细介绍)

● 用一个配置文件启动了Rviz

请注意,上面这条启动命令假定您是从urdf_tutorial软件包目录执行它(即:urdf目录是当前工作目录的直接子目录)。如果不是在urdf_tutorial软件包目录执行该命令,则01-myfirst.urdf的相对路径将会无效,且一旦启动程序尝试将urdf加载为一个参数,就会收到一条错误消息。

对上条命令的参数稍加修改就会让其正常工作,无论当前工作目录是否在urdf_tutorial软件包目录:

ros2 launch urdf_tutorial display.launch.py model:=`ros2 pkg prefix --share urdf_tutorial`/urdf/01-myfirst.urdf

如果您不是从urdf_tutorial软件包目录位置运行命令,则必须更改教程中给出的所有示例启动命令。

启动display.launch.py后,就应该会看到RViz显示下图所示内容:

注意事项:

 固定坐标系是网格中心所在的变换坐标系。在这里,固定坐标系是由机器人的一个链接base_link定义的坐标系

● 默认情况下,视觉元素(圆柱体)的原点位于其几何图形的中心。因此,圆柱体的一半位于网格下方。

1.2 多个形状

现在让我们来看看如何添加多个形状/链接。如果我们只是向urdf添加更多链接元素,解析器不知道应该将它们放在哪里。所以必须添加关节。关节元件可以是柔性关节和非柔性关节。这里将会从非柔性关节或固定关节开始。[源代码文件:02-multipleshapes.urdf]

<?xml version="1.0"?>
<robot name="multipleshapes">
  <link name="base_link">
    <visual>
      <geometry>
        <cylinder length="0.6" radius="0.2"/>
      </geometry>
    </visual>
  </link>

  <link name="right_leg">
    <visual>
      <geometry>
        <box size="0.6 0.1 0.2"/>
      </geometry>
    </visual>
  </link>

  <joint name="base_to_right_leg" type="fixed">
    <parent link="base_link"/>
    <child link="right_leg"/>
  </joint>

</robot>

● 注意我们是如何定义一个尺寸为0.6m×0.1m×0.2m的方盒的

● 关节是以一个父级链接和一个子级链接来定义的。URDF归根结底是一种具有一个根链接的树状结构。这意味着腿的位置取决于base_link的位置。

ros2 launch urdf_tutorial display.launch.py model:=urdf/02-multipleshapes.urdf

这两个形状彼此相互重叠,因为它们共享了同一个原点。如果想要让它们不相互重叠,则必须定义更多的原点。

1.3 原点

R2D2的腿连接到机器人躯干的上半身的侧面。所以那就是我们指定JOINT原点的地方。此外,该关节也不是连接在腿的中间,而是连接到腿的上部,所以也必须将腿的原点进行偏移。也要对腿进行旋转以使其直立起来。[源代码文件:03-origins.urdf]

<?xml version="1.0"?>
<robot name="origins">
  <link name="base_link">
    <visual>
      <geometry>
        <cylinder length="0.6" radius="0.2"/>
      </geometry>
    </visual>
  </link>

  <link name="right_leg">
    <visual>
      <geometry>
        <box size="0.6 0.1 0.2"/>
      </geometry>
      <origin rpy="0 1.57075 0" xyz="0 0 -0.3"/>
    </visual>
  </link>

  <joint name="base_to_right_leg" type="fixed">
    <parent link="base_link"/>
    <child link="right_leg"/>
    <origin xyz="0 -0.22 0.25"/>
  </joint>

</robot>

● 先来看下该关节的原点。该原点是以其父级参考坐标系定义的。 因此,该原点在y方向为-0.22米(朝左,但在坐标轴的右边),且在z方向为0.25米(朝上)。这意味着其子链接的原点将会是向上和向右的,无论该子链接的视觉原点标签如何。由于这里没有指定rpy (横滚角/俯仰角/偏航角,roll/pitch/yaw)属性,子级坐标系将默认与父级坐标系具有相同的方位。

● 现在来查看腿的视觉原点,该原点同时具有xyz和rpy偏移量。 这可以定义视觉元素的中心相对于其原点的位置。由于想要让腿连接到顶部,因此这里通过将z轴偏移量设置为-0.3米来向下偏移该原点。 由于想要让腿的更长部分方向平行于z轴,所以要绕Y轴旋转视觉元素π/2即90°(对应1.57075弧度)。

ros2 launch urdf_tutorial display.launch.py model:=urdf/03-origins.urdf

● 启动文件运行的软件包会根据URDF为模型中的每个链接创建TF坐标系。Rviz使用此信息来确定在何处显示每个形状。

● 如果某个给定的URDF链接的TF坐标系不存在,则会将该连接放置在白色材质的原点处(参考相关问题)。

1.4 材质

“好吧,”我听到你说。“那很可爱,但不是每个人都拥有B21。我的机器人和R2D2都不是红色的!”这是个好主意。那么现在就来看看材质标签吧。[源代码文件:04-materials.urdf]

<?xml version="1.0"?>
<robot name="materials">

  <material name="blue">
    <color rgba="0 0 0.8 1"/>
  </material>

  <material name="white">
    <color rgba="1 1 1 1"/>
  </material>

  <link name="base_link">
    <visual>
      <geometry>
        <cylinder length="0.6" radius="0.2"/>
      </geometry>
      <material name="blue"/>
    </visual>
  </link>

  <link name="right_leg">
    <visual>
      <geometry>
        <box size="0.6 0.1 0.2"/>
      </geometry>
      <origin rpy="0 1.57075 0" xyz="0 0 -0.3"/>
      <material name="white"/>
    </visual>
  </link>

  <joint name="base_to_right_leg" type="fixed">
    <parent link="base_link"/>
    <child link="right_leg"/>
    <origin xyz="0 -0.22 0.25"/>
  </joint>

  <link name="left_leg">
    <visual>
      <geometry>
        <box size="0.6 0.1 0.2"/>
      </geometry>
      <origin rpy="0 1.57075 0" xyz="0 0 -0.3"/>
      <material name="white"/>
    </visual>
  </link>

  <joint name="base_to_left_leg" type="fixed">
    <parent link="base_link"/>
    <child link="left_leg"/>
    <origin xyz="0 0.22 0.25"/>
  </joint>

</robot>

● 机器人身体现在是蓝色的。我们定义了一种名为“blue”的新材质,其红、绿、蓝和alpha通道分别定义为0、0、0.8和1。所有这4个通道的值都介于[0,1]范围内。然后此材质被base_link的视觉元素引用。白色材质的定义相类似。

● 也可以在视觉元素中定义材质标签,甚至可以在其他链接中引用该材质。如果重新定义该材质也没有任何问题。

● 还可以使用纹理来指定某个图像文件用于对物体着色。

ros2 launch urdf_tutorial display.launch.py model:=urdf/04-materials.urdf

1.5 完成模型

现在我们用下面这些更多的形状来完成该机器人模型:脚、轮子和头。最值得注意的是,我们添加了一个球体和一些网格。我们还会添加一些稍后要用到的其他部分。[源代码文件:05-visual.urdf]

<?xml version="1.0"?>
<robot name="visual">

  <material name="blue">
    <color rgba="0 0 0.8 1"/>
  </material>
  <material name="black">
    <color rgba="0 0 0 1"/>
  </material>
  <material name="white">
    <color rgba="1 1 1 1"/>
  </material>

  <link name="base_link">
    <visual>
      <geometry>
        <cylinder length="0.6" radius="0.2"/>
      </geometry>
      <material name="blue"/>
    </visual>
  </link>

  <link name="right_leg">
    <visual>
      <geometry>
        <box size="0.6 0.1 0.2"/>
      </geometry>
      <origin rpy="0 1.57075 0" xyz="0 0 -0.3"/>
      <material name="white"/>
    </visual>
  </link>

  <joint name="base_to_right_leg" type="fixed">
    <parent link="base_link"/>
    <child link="right_leg"/>
    <origin xyz="0 -0.22 0.25"/>
  </joint>

  <link name="right_base">
    <visual>
      <geometry>
        <box size="0.4 0.1 0.1"/>
      </geometry>
      <material name="white"/>
    </visual>
  </link>

  <joint name="right_base_joint" type="fixed">
    <parent link="right_leg"/>
    <child link="right_base"/>
    <origin xyz="0 0 -0.6"/>
  </joint>

  <link name="right_front_wheel">
    <visual>
      <origin rpy="1.57075 0 0" xyz="0 0 0"/>
      <geometry>
        <cylinder length="0.1" radius="0.035"/>
      </geometry>
      <material name="black"/>
    </visual>
  </link>
  <joint name="right_front_wheel_joint" type="fixed">
    <parent link="right_base"/>
    <child link="right_front_wheel"/>
    <origin rpy="0 0 0" xyz="0.133333333333 0 -0.085"/>
  </joint>

  <link name="right_back_wheel">
    <visual>
      <origin rpy="1.57075 0 0" xyz="0 0 0"/>
      <geometry>
        <cylinder length="0.1" radius="0.035"/>
      </geometry>
      <material name="black"/>
    </visual>
  </link>
  <joint name="right_back_wheel_joint" type="fixed">
    <parent link="right_base"/>
    <child link="right_back_wheel"/>
    <origin rpy="0 0 0" xyz="-0.133333333333 0 -0.085"/>
  </joint>

  <link name="left_leg">
    <visual>
      <geometry>
        <box size="0.6 0.1 0.2"/>
      </geometry>
      <origin rpy="0 1.57075 0" xyz="0 0 -0.3"/>
      <material name="white"/>
    </visual>
  </link>

  <joint name="base_to_left_leg" type="fixed">
    <parent link="base_link"/>
    <child link="left_leg"/>
    <origin xyz="0 0.22 0.25"/>
  </joint>

  <link name="left_base">
    <visual>
      <geometry>
        <box size="0.4 0.1 0.1"/>
      </geometry>
      <material name="white"/>
    </visual>
  </link>

  <joint name="left_base_joint" type="fixed">
    <parent link="left_leg"/>
    <child link="left_base"/>
    <origin xyz="0 0 -0.6"/>
  </joint>

  <link name="left_front_wheel">
    <visual>
      <origin rpy="1.57075 0 0" xyz="0 0 0"/>
      <geometry>
        <cylinder length="0.1" radius="0.035"/>
      </geometry>
      <material name="black"/>
    </visual>
  </link>
  <joint name="left_front_wheel_joint" type="fixed">
    <parent link="left_base"/>
    <child link="left_front_wheel"/>
    <origin rpy="0 0 0" xyz="0.133333333333 0 -0.085"/>
  </joint>

  <link name="left_back_wheel">
    <visual>
      <origin rpy="1.57075 0 0" xyz="0 0 0"/>
      <geometry>
        <cylinder length="0.1" radius="0.035"/>
      </geometry>
      <material name="black"/>
    </visual>
  </link>
  <joint name="left_back_wheel_joint" type="fixed">
    <parent link="left_base"/>
    <child link="left_back_wheel"/>
    <origin rpy="0 0 0" xyz="-0.133333333333 0 -0.085"/>
  </joint>

  <joint name="gripper_extension" type="fixed">
    <parent link="base_link"/>
    <child link="gripper_pole"/>
    <origin rpy="0 0 0" xyz="0.19 0 0.2"/>
  </joint>

  <link name="gripper_pole">
    <visual>
      <geometry>
        <cylinder length="0.2" radius="0.01"/>
      </geometry>
      <origin rpy="0 1.57075 0 " xyz="0.1 0 0"/>
    </visual>
  </link>

  <joint name="left_gripper_joint" type="fixed">
    <origin rpy="0 0 0" xyz="0.2 0.01 0"/>
    <parent link="gripper_pole"/>
    <child link="left_gripper"/>
  </joint>

  <link name="left_gripper">
    <visual>
      <origin rpy="0.0 0 0" xyz="0 0 0"/>
      <geometry>
        <mesh filename="package://urdf_tutorial/meshes/l_finger.dae"/>
      </geometry>
    </visual>
  </link>

  <joint name="left_tip_joint" type="fixed">
    <parent link="left_gripper"/>
    <child link="left_tip"/>
  </joint>

  <link name="left_tip">
    <visual>
      <origin rpy="0.0 0 0" xyz="0.09137 0.00495 0"/>
      <geometry>
        <mesh filename="package://urdf_tutorial/meshes/l_finger_tip.dae"/>
      </geometry>
    </visual>
  </link>
  <joint name="right_gripper_joint" type="fixed">
    <origin rpy="0 0 0" xyz="0.2 -0.01 0"/>
    <parent link="gripper_pole"/>
    <child link="right_gripper"/>
  </joint>

  <link name="right_gripper">
    <visual>
      <origin rpy="-3.1415 0 0" xyz="0 0 0"/>
      <geometry>
        <mesh filename="package://urdf_tutorial/meshes/l_finger.dae"/>
      </geometry>
    </visual>
  </link>

  <joint name="right_tip_joint" type="fixed">
    <parent link="right_gripper"/>
    <child link="right_tip"/>
  </joint>

  <link name="right_tip">
    <visual>
      <origin rpy="-3.1415 0 0" xyz="0.09137 0.00495 0"/>
      <geometry>
        <mesh filename="package://urdf_tutorial/meshes/l_finger_tip.dae"/>
      </geometry>
    </visual>
  </link>

  <link name="head">
    <visual>
      <geometry>
        <sphere radius="0.2"/>
      </geometry>
      <material name="white"/>
    </visual>
  </link>
  <joint name="head_swivel" type="fixed">
    <parent link="base_link"/>
    <child link="head"/>
    <origin xyz="0 0 0.3"/>
  </joint>

  <link name="box">
    <visual>
      <geometry>
        <box size="0.08 0.08 0.08"/>
      </geometry>
      <material name="blue"/>
    </visual>
  </link>

  <joint name="tobox" type="fixed">
    <parent link="head"/>
    <child link="box"/>
    <origin xyz="0.1814 0 0.1414"/>
  </joint>
</robot>

ros2 launch urdf_tutorial display.launch.py model:=urdf/05-visual.urdf

如何添加球体应该是不言自明的:

<link name="head">
  <visual>
    <geometry>
      <sphere radius="0.2"/>
    </geometry>
    <material name="white"/>
  </visual>
</link>

这里的网格是借用于PR2机器人的。这些网格是单独的文件,必须为其指定路径。您应该使用package://NAME_OF_PACKAGE/path 表示法。本教程的网格位于urdf_tutorial软件包中名为meshes的文件夹中。

<link name="left_gripper">
  <visual>
    <origin rpy="0.0 0 0" xyz="0 0 0"/>
    <geometry>
      <mesh filename="package://urdf_tutorial/meshes/l_finger.dae"/>
    </geometry>
  </visual>
</link>

● 网格可以以多种不同的格式导入进来。STL格式相当普遍,但引擎也支持DAE格式,该格式可以拥有自己的颜色数据,这意味着您不必指定颜色/材质。通常这些网格均位于单独的文件中。这些网格也会引用meshes文件夹中的.tif文件。

● 也可以使用相对缩放参数或边界框大小来调整网格的大小。

● 还可以引用一个完全不同的软件包中的网格。

这样你就完成了一个类似于R2D2的URDF机器人模型。现在你可以继续下一个教程“让机器人模型移动”。

英语原文网址:docs.ros.org/en/galacti