从头开始用URDF构建视觉机器人模型 [待校准@8996]
Goal目标: Learn了解如何构建可以在Rviz中查看的机器人视觉模型 [待校准@8997]
教程级别: 初学者 [Alyssa@7088]
Note注意: This本教程假设您知道如何编写格式良好的XML代码 [待校准@8998]
在本教程中,我们将构建一个看起来有点像R2D2的机器人的视觉模型。在后面的教程中,您将学习如何 articulate the model, add in some physical properties, and generate neater code with xacro ,但是现在,我们将重点关注使视觉几何正确。 [待校准@8999]
在继续之前,确保你有 joint_state_publisher package installed. If you installed urdf_tutorial 二进制文件,应该已经是这样了。如果没有,请更新您的安装以包含该包 (使用 rosdep
检查)。 [待校准@9000]
本教程中提到的所有机器人模型 (以及源文件) 都可以在 urdf_tutorial 包中找到。 [待校准@9001]
为了确保正确解析urdf文件中定义的形状,需要将Linux环境变量 LC_NUMERIC
设置为 en_US.UTF-8
。每次打开终端时都必须这样做: [待校准@9002]
LC_NUMERIC="en_US.UTF-8"
要永久设置此环境变量,请在终端中执行以下命令: [待校准@9003]
echo 'export LC_NUMERIC="en_US.UTF-8"' >> ~/.bashrc
一个形状 [待校准@9004]
首先,我们将探索一个简单的形状。这是尽可能简单的urdf。 [Source: 01-myfirst.urdf] [待校准@9005]
<?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” 类型的例子来说,这看起来像是很多封闭的标签,但是它会变得更加复杂,相信我。 [待校准@9006]
检查模型,launch的 display.launch.py
文件: [待校准@9007]
ros2 launch urdf_tutorial display.launch.py model:=urdf/01-myfirst.urdf
这做了三件事: [待校准@9008]
加载指定模型并将其保存为参数 [待校准@9009]
运行节点以发布 sensor_msgs/msg/JointState 和转换 (稍后会详细介绍) [待校准@9010]
使用配置文件启动Rviz [待校准@9011]
请注意,上面的launch命令假定您正在从 urdf_tutorial 包目录执行它 (即: urdf
目录是当前工作目录的直接子目录)。如果不是这种情况,到 01-myfirst.urdf
的相对路径将无效,一旦launcher尝试将urdf作为参数加载,您将收到一个错误。 [待校准@9012]
稍微修改的参数允许它工作,而不管当前的工作目录如何: [待校准@9013]
ros2 launch urdf_tutorial display.launch.py model:=`ros2 pkg prefix --share urdf_tutorial`/urdf/01-myfirst.urdf
你将把所有示例launch命令给出这些教程如果不运行从 urdf_tutorial
包位置。 [待校准@9014]
在launch display.launch.py
之后,你应该以RViz告终,向你展示以下内容: [待校准@9015]
- 需要注意的事项: [待校准@9017]
固定帧是网格中心所在的变换帧。这里,它是由我们的一个链接base_link定义的帧。 [待校准@9018]
默认情况下,视觉元素 (圆柱体) 的原点位于其几何图形的中心。因此,一半的圆柱体在网格下。 [待校准@9019]
多种形状 [待校准@9020]
现在让我们看看如何添加多个形状/链接。如果我们只是向urdf添加更多的链接元素,解析器将不知道将它们放在哪里。所以,我们必须增加关节。接缝元件可以指灵活和不灵活的接缝。我们将从不灵活或固定的关节开始。 [Source: 02-multipleshapes.urdf] [待校准@9021]
<?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.6米 × 0.1米 × 0.2米的盒子 [待校准@9022]
关节是根据父母和孩子来定义的。URDF最终是一个具有一个根链接的树结构。这意味着腿的位置取决于base_link的位置。 [待校准@9023]
ros2 launch urdf_tutorial display.launch.py model:=urdf/02-multipleshapes.urdf
两个形状相互重叠,因为它们共享相同的原点。如果我们希望它们不重叠,我们必须定义更多的起源。 [待校准@9024]
起源 [待校准@9025]
R2D2的腿附着在他躯干的上半部,在侧面。这就是我们指定关节的原点的地方。此外,它不会附着在腿的中间,而是附着在上部,所以我们也必须偏移腿的原点。我们也旋转腿,使它是直立的。 [Source: 03-origins.urdf] [待校准@9026]
<?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 (滚距偏航) 属性,因此子帧将默认具有与父帧相同的方向。 [待校准@9027]
现在,看看腿的视觉来源,它有xyz和rpy偏移。这定义了视觉元素相对于其原点的中心应该在哪里。由于我们希望支腿附着在顶部,我们通过将z偏移设置为-0.3米来向下偏移原点。并且由于我们希望腿的长部分与z轴平行,因此我们将视觉部分形式发票/2绕y轴旋转。 [待校准@9028]
ros2 launch urdf_tutorial display.launch.py model:=urdf/03-origins.urdf
[需手动修复的语法]launch文件运行包,该包将根据您的URDF为模型中的每个链接创建TF帧。Rviz使用此信息来确定在哪里显示每个形状。 [待校准@9030]
如果给定的URDF链接不存在TF帧,则它将以白色 (参考 related question ) 放置在原点。 [待校准@9031]
物质女孩 [待校准@9032]
“好吧,” 我听到你说。“那很可爱,但不是每个人都拥有b21。我的机器人和R2D2不是红色的!”这是一个很好的观点。让我们看看材料标签。 [Source: 04-materials.urdf] [待校准@9033]
<?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>
尸体现在是蓝色的。我们定义新材料调用了 “蓝色”,红色,绿色,蓝色和alpha通道定义为0,0,0.8和1。所有值都可以在 [0,1] 范围内。然后,此材质由base_link的视觉元素引用。白色材料的定义类似。 [待校准@9034]
您还可以从视觉元素中定义材质标签,甚至在其他链接中引用它。如果你重新定义它,甚至没有人会抱怨。 [待校准@9035]
您还可以使用纹理指定用于为对象着色的图像文件 [待校准@9036]
ros2 launch urdf_tutorial display.launch.py model:=urdf/04-materials.urdf
完成模型 [待校准@9038]
现在我们用更多的形状来完成模型: 脚、轮子和头。最值得注意的是,我们添加了一个球体和一些网格。我们还将添加一些其他的作品,我们将在以后使用。 [Source: 05-visual.urdf] [待校准@9039]
<?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
如何添加球体应该是相当不言自明的: [待校准@9041]
<link name="head">
<visual>
<geometry>
<sphere radius="0.2"/>
</geometry>
<material name="white"/>
</visual>
</link>
这里的网格是从pr2借来的。它们是单独的文件,您必须为其指定路径。您应该使用 “package:// NAME_OF_PACKAGE/path” 表示法。本教程的网格位于 urdf_tutorial
包中调用网格的文件夹中。 [待校准@9042]
<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,它可以有自己的颜色数据,这意味着您不必指定颜色/材质。这些通常在单独的文件中。这些网格引用了网格文件夹中的
.tif
文件。 [待校准@9043]网格也可以使用相对缩放参数或边界框大小来确定大小。 [待校准@9044]
我们也可以在一个完全不同的包中提到网格。 [待校准@9045]
给你。一个R2D2-like的URDF模型。现在你可以继续下一步了, making it move 。 [待校准@9046]