ROS2 - URDF
系列文章目录
前言
URDF(统一机器人描述格式)是一种文件格式,用于在 ROS 中指定机器人的几何形状和组织结构。本教程假定您知道如何编写格式规范的 XML 代码
一、从零开始构建可视机器人模型
????????在本教程中,我们将建立一个机器人的可视化模型,这个模型看起来有点像 R2D2。在稍后的教程中,你将学习如何衔接模型、添加一些物理属性以及使用 xacro 生成更简洁的代码,但现在,我们将专注于获得正确的视觉几何形状。
????????在继续之前,请确保已安装 joint_state_publisher 软件包。如果你安装了 urdf_tutorial 二进制文件,应该已经安装了。如果没有,请更新您的安装以包含该软件包(使用 rosdep 检查)。
本教程中提到的所有机器人模型(以及源文件)都可以在 urdf_tutorial 软件包中找到。
1.1 一个形状
首先,我们只探讨一个简单的形状。下面是你能做出的最简单的 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 的机器人,只包含一个 link(又称部件),其视觉组件只是一个长 0.6 米、半径 0.2 米的圆柱体。对于一个简单的 "hello world"(你好世界)类型的示例来说,这可能看起来像很多外层标签,但它会变得更加复杂,相信我。
要检查模型,请启动 display.launch.py 文件:
ros2 launch urdf_tutorial display.launch.py model:=urdf/01-myfirst.urdf
?它有三个功能
- 加载指定模型,并将其保存为机器人状态发布(robot_state_publisher)节点的参数。
- 运行节点,以发布 sensor_msgs/msg/JointState 和变换(稍后将详细介绍)。
- 使用配置文件启动 Rviz
启动 display.launch.py 之后,RViz 将显示如下内容:
1.2 多个形状
现在我们来看看如何添加多个形状/部件。如果我们只是在 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.6 米 x 0.1 米 x 0.2 米的方框的
- 接合点是以父节点和子节点的形式定义的。URDF 最终是一个只有一个 root link 的树形结构。这意味着腿的位置取决于 base_link 的位置。
ros2 launch urdf_tutorial display.launch.py model:=urdf/02-multipleshapes.urdf
1.3 起源
R2D2 的腿连接到躯干上半部分的侧面。因此,我们要在这里指定 JOINT 的原点。此外,它并不是连接到腿的中间,而是连接到上半部分,因此我们也必须偏移腿的原点。我们还可以旋转腿部,使其直立。
<?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(滚动俯仰偏航)属性,子坐标系将默认与父坐标系具有相同的方向。
现在,再看腿部的视觉原点,它有 xyz 和 rpy 偏移量。这定义了视觉元素相对于其原点的中心位置。由于我们希望支腿连接在顶部,因此我们将 Z 偏移量设置为-0.3 米,从而将原点向下偏移。由于我们希望腿的长部分与 Z 轴平行,因此我们将视觉部分绕 Y 轴旋转 PI/2。
ros2 launch urdf_tutorial display.launch.py model:=urdf/03-origins.urdf
?启动文件运行的软件包将根据 URDF 为模型中的每个部件创建 TF 坐标系。Rviz 会使用这些信息来确定每个形状的显示位置。
如果给定的 URDF 部件不存在 TF 坐标系,那么它将以白色显示在原点处
1.4 Material Girl?
"好吧,"我听到你说。"这很可爱,但不是每个人都拥有 B21。我的机器人和 R2D2 都不是红色的!" 说得好。让我们来看看材料标签。
<?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 的视觉元素就会引用该材质。白色材质的定义与此类似。
- 你也可以在可视化元素中定义材质标签,甚至在其他部件中引用它。不过,如果你重新定义了它,也不会有人抱怨。
- 您还可以使用纹理来指定用于为对象着色的图像文件
ros2 launch urdf_tutorial display.launch.py model:=urdf/04-materials.urdf
1.5 完成模型
现在,我们再为模型添加一些形状:脚、轮子和头。最值得注意的是,我们添加了一个球体和一些网格。我们还将添加一些稍后会用到的其他部件。?
<?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,它可以有自己的颜色数据,这意味着你不必指定颜色/材料。通常情况下,这些都是单独的文件。这些网格参考网格文件夹中的 .tif 文件。
- 网格也可以使用相对缩放参数或边界框尺寸来确定大小。
- 我们也可以参考完全不同软件包中的网格。
就是这样。一个类似 R2D2 的 URDF 模型。现在你可以继续下一步,让它移动起来。
?二、建立可移动的机器人模型
????????在本教程中,我们将修改上一教程中制作的 R2D2 模型,使其具有可移动的关节。在之前的模型中,所有的关节都是固定的。现在我们将探索另外三种重要的关节类型:连续关节、旋转关节和棱柱关节。
????????在继续之前,请确保您已经安装了所有先决条件。有关所需信息,请参阅前面的教程。
????????同样,本教程中提到的所有机器人模型都可以在 urdf_tutorial 包中找到。
????????下面是带有柔性关节的新版 urdf。您可以将其与之前的版本进行比较,以了解所有变化,但我们将只关注三个关节示例。
????????要可视化并控制该模型,请运行与上一教程相同的命令:
ros2 launch urdf_tutorial display.launch.py model:=urdf/06-flexible.urdf
不过,现在这也会弹出一个图形用户界面,允许你控制所有非固定关节的值。玩一下模型,看看它是如何移动的。然后,我们可以看看我们是如何做到这一点的。
2.1?头部
<joint name="head_swivel" type="continuous">
<parent link="base_link"/>
<child link="head"/>
<axis xyz="0 0 1"/>
<origin xyz="0 0 0.3"/>
</joint>
车身和车头之间的连接是一个连续的关节,这意味着它可以从负无穷大到正无穷大任意角度旋转。车轮也是这样建模的,因此它们可以永远向两个方向滚动。
我们唯一需要添加的额外信息是旋转轴,这里由 xyz 三元组指定,它指定了头部将围绕其旋转的矢量。由于我们希望它围绕 Z 轴旋转,所以我们指定了 "0 0 1 "这个向量。
?2.2?夹具
<joint name="left_gripper_joint" type="revolute">
<axis xyz="0 0 1"/>
<limit effort="1000.0" lower="0.0" upper="0.548" velocity="0.5"/>
<origin rpy="0 0 0" xyz="0.2 0.01 0"/>
<parent link="gripper_pole"/>
<child link="left_gripper"/>
</joint>
?左右两个抓手关节都被建模为旋转关节。这意味着它们的旋转方式与连续关节相同,但有严格的限制。因此,我们必须在极限标签中指定关节的上限和下限(以弧度为单位)。我们还必须指定该关节的最大速度和力度,但实际值在这里并不重要。
2.3?夹持臂
<joint name="gripper_extension" type="prismatic">
<parent link="base_link"/>
<child link="gripper_pole"/>
<limit effort="1000.0" lower="-0.38" upper="0" velocity="0.5"/>
<origin rpy="0 0 0" xyz="0.19 0 0.2"/>
</joint>
夹臂是一种不同的关节,即棱柱关节。这意味着它是沿轴线运动,而不是绕轴线运动。这种平移运动使我们的机器人模型能够伸出和缩回抓臂。
除了单位是米而不是弧度外,棱柱臂的极限指定方式与旋转接头相同。
2.4 其他类型的关节
还有两种关节可以在空间中移动。棱形关节只能沿一个维度移动,而平面关节可以在一个平面或两个维度内移动。此外,浮动关节不受约束,可以在三个维度中的任意维度移动。这些关节不能只用一个数字指定,因此不包括在本教程中。
2.5 指定姿势
当您在图形用户界面中移动滑块时,模型也会在 Rviz 中移动。 这是如何实现的?首先,图形用户界面会解析 URDF,找到所有非固定关节及其限制。然后,它使用滑块的值来发布 sensor_msgs/msg/JointState 消息。然后,机器人状态发布器会使用这些信息来计算不同部件之间的所有变换。然后,生成的变换树将用于在 Rviz 中显示所有形状。
三、添加物理和碰撞属性?
?在本教程中,我们将了解如何为 URDF 模型添加一些基本的物理属性,以及如何指定其碰撞属性。
3.1 碰撞
到目前为止,我们只用一个子元素视觉(visual)指定了我们的部件,它定义了(毫不奇怪)机器人的外观。但是,为了让碰撞检测发挥作用或模拟机器人,我们还需要定义一个碰撞元素。下面是带有碰撞和物理属性的新 urdf。
下面是新基础部件的代码。
<link name="base_link">
<visual>
<geometry>
<cylinder length="0.6" radius="0.2"/>
</geometry>
<material name="blue">
<color rgba="0 0 .8 1"/>
</material>
</visual>
<collision>
<geometry>
<cylinder length="0.6" radius="0.2"/>
</geometry>
</collision>
</link>
- 碰撞元素是链接对象的直接子元素,与视觉标记处于同一级别。
- 碰撞元素定义形状的方式与视觉元素相同,都使用几何体标签。几何标记的格式与视觉标记完全相同。
- 您还可以以与视觉元素相同的方式,将原点作为碰撞标记的子元素来指定。
在很多情况下,您都希望碰撞的几何体和原点与视觉的几何体和原点完全相同。不过,有两种主要情况是不需要的:
- 更快的处理速度。对两个网格进行碰撞检测要比对两个简单几何体进行碰撞检测复杂得多。因此,您可能希望在碰撞元素中用更简单的几何图形替换网格。
- 安全区。您可能希望限制靠近敏感设备的移动。例如,如果我们不想让任何东西与 R2D2 的头部发生碰撞,我们可以将碰撞几何体定义为一个包裹着他头部的圆柱体,以防止任何东西太靠近他的头部。
3.2 物理特性
为了让模型正常模拟,您需要定义机器人的几个物理属性,即 Gazebo 等物理引擎需要的属性。
3.2.1 惯性
每个被模拟的部件都需要一个惯性标签。下面是一个简单的例子。
-
<link name="base_link"> <visual> <geometry> <cylinder length="0.6" radius="0.2"/> </geometry> <material name="blue"> <color rgba="0 0 .8 1"/> </material> </visual> <collision> <geometry> <cylinder length="0.6" radius="0.2"/> </geometry> </collision> <inertial> <mass value="10"/> <inertia ixx="1e-3" ixy="0.0" ixz="0.0" iyy="1e-3" iyz="0.0" izz="1e-3"/> </inertial> </link>
- ?该部件也是 link 对象的子部件。
- 质量以千克为单位。
- 惯性元素指定了 3x3 旋转惯性矩阵。由于该矩阵是对称的,因此只需 6 个元素即可表示。
ixx | ixy | ixz |
ixy | iyy | iyz |
ixz | iyz | izz |
- 建模程序(如 MeshLab)可以提供这些信息。几何基元(圆柱体、方盒子、球体)的惯性可以通过维基百科上的惯性力矩张量列表计算出来(上例中使用的就是惯性力矩张量)。
- 惯性张量取决于物体的质量和质量分布。第一种好的近似方法是假设物体体积内的质量分布相等,然后根据物体的形状计算惯性张量,如上所述。
- 如果不确定放什么,对于一个中等大小的部件来说,ixx/iyy/izz=1e-3 或更小的矩阵通常是一个合理的默认值(它相当于一个边长为 0.1 米、质量为 0.6 千克的盒子)。单位矩阵是一个特别糟糕的选择,因为它往往过高(相当于边长 0.1 米、质量 600 千克的盒子!)。
- 您还可以指定一个原点标签来指定重心和惯性参考系(相对于部件的参考系)。
- 在使用实时控制器时,惯性部件为零(或几乎为零)会导致机器人模型在没有警告的情况下坍塌,所有链接的原点都将与世界原点重合。
3.3 接触系数
您还可以定义部件相互接触时的行为方式。这可以通过碰撞标记的子元素 contact_coefficients 来实现。有三个属性可以指定:
- mu - 摩擦系数
- kp - 刚度系数
- kd - 阻尼系数
3.4 关节动力学
关节的运动方式由关节的动力学标签定义。这里有两个属性:
- friction - 物理静摩擦力。对于棱柱关节,单位是牛顿。对于旋转接头,单位为牛顿米。
- damping - 物理阻尼值。对于棱柱关节,单位为牛顿秒/米。对于旋转接头,单位为牛顿米秒/弧度。
如果未指定,这些系数默认为零。
3.5 其他标签
在纯 URDF 领域(即不包括 Gazebo 特有的标签),还有两个标签可以帮助定义关节:校准和安全控制器。请查看规范,因为本教程不包含这两个标签。
四、使用 Xacro 清理代码
现在,如果你正在家里按照这些步骤设计自己的机器人,你可能已经厌倦了为了让非常简单的机器人描述正确解析而做各种数学运算。幸运的是,您可以使用 xacro 软件包来简化您的生活。它有三个非常有用的功能。
- 常量
- 简单数学
- 宏
在本教程中,我们将介绍所有这些快捷方式,以帮助减小 URDF 文件的整体大小,并使其更易于阅读和维护。
4.1 使用 Xacro
顾名思义,xacro 是一种 XML 宏语言。xacro 程序运行所有宏并输出结果。典型的用法如下
xacro model.xacro > model.urdf
您还可以在启动文件中自动生成 urdf。这很方便,因为它可以保持更新,而且不会占用硬盘空间。不过,生成需要一定时间,因此请注意启动文件可能需要更长时间才能启动。
要在启动文件中运行 xacro,需要将 Command 替换作为 robot_state_publisher 的参数。
path_to_urdf = get_package_share_path('turtlebot3_description') / 'urdf' / 'turtlebot3_burger.urdf'
robot_state_publisher_node = launch_ros.actions.Node(
package='robot_state_publisher',
executable='robot_state_publisher',
parameters=[{
'robot_description': ParameterValue(
Command(['xacro ', str(path_to_urdf)]), value_type=str
)
}]
)
加载机器人模型的更简便方法是使用 urdf_launch 软件包自动加载 xacro/urdf。
from launch import LaunchDescription
from launch.actions import IncludeLaunchDescription
from launch.substitutions import PathJoinSubstitution
from launch_ros.substitutions import FindPackageShare
def generate_launch_description():
ld = LaunchDescription()
ld.add_action(IncludeLaunchDescription(
PathJoinSubstitution([FindPackageShare('urdf_launch'), 'launch', 'display.launch.py']),
launch_arguments={
'urdf_package': 'turtlebot3_description',
'urdf_package_path': PathJoinSubstitution(['urdf', 'turtlebot3_burger.urdf'])}.items()
))
return ld
在 URDF 文件的顶部,您必须指定一个命名空间,以便正确解析文件。例如,这是一个有效 xacro 文件的前两行:
<?xml version="1.0"?>
<robot xmlns:xacro="http://www.ros.org/wiki/xacro" name="firefighter">
4.2 常量
让我们快速浏览一下 R2D2 中的 base_link。?
<link name="base_link">
<visual>
<geometry>
<cylinder length="0.6" radius="0.2"/>
</geometry>
<material name="blue"/>
</visual>
<collision>
<geometry>
<cylinder length="0.6" radius="0.2"/>
</geometry>
</collision>
</link>
这里的信息有点多余。我们两次指定了圆柱体的长度和半径。更糟糕的是,如果我们想要更改,需要在两个不同的地方进行更改。
幸运的是,xacro 允许您指定作为常量的属性。我们可以写这样的代码来代替上述代码。
<xacro:property name="width" value="0.2" />
<xacro:property name="bodylen" value="0.6" />
<link name="base_link">
<visual>
<geometry>
<cylinder radius="${width}" length="${bodylen}"/>
</geometry>
<material name="blue"/>
</visual>
<collision>
<geometry>
<cylinder radius="${width}" length="${bodylen}"/>
</geometry>
</collision>
</link>
- ?前两行指定了两个值。它们可以定义在任何地方(假设 XML 有效)、任何层级、使用之前或之后。通常它们放在最上面。
- 我们没有在几何元素中指定实际半径,而是使用美元符号和大括号来表示该值。
- 这段代码将生成与上图相同的代码。
然后,${} 结构的内容值将用于替换 ${}。这意味着您可以将其与属性中的其他文本结合起来。
<xacro:property name=”robotname” value=”marvin” />
<link name=”${robotname}s_leg” />
?这将生成
<link name=”marvins_leg” />
然而,${}中的内容不一定只能是一个属性,这就引出了我们的下一个问题...
4.3 数学
在 ${} 结构中,你可以使用四种基本运算(+,-,*,/)、一元减号和括号,建立任意复杂的表达式。例如
<cylinder radius="${wheeldiam/2}" length="0.1"/>
<origin xyz="${reflect*(width+.02)} 0 0.25" />
除 sin 和 cos 等基本数学运算外,您还可以使用更多运算。
4.4 宏
这是 xacro 软件包中最大、最有用的组件。
4.4.1 简单宏
让我们来看看一个简单无用的宏。
<xacro:macro name="default_origin">
<origin xyz="0 0 0" rpy="0 0 0"/>
</xacro:macro>
<xacro:default_origin />
(这是无用的,因为如果不指定原点,它的值与此相同)。这段代码将生成以下内容。
<origin rpy="0 0 0" xyz="0 0 0"/>
- 从技术上讲,名称并非必备元素,但您需要指定名称才能使用它。
- <xacro:$NAME /> 的每个实例都会被替换为 xacro:macro 标记的内容。
- 请注意,尽管内容不完全相同(两个属性的顺序互换了),但生成的 XML 是等价的。
- 如果未找到指定名称的 xacro,它将不会被展开,也不会产生错误。
4.4.2 参数化宏
您还可以对宏进行参数化处理,这样它们就不会每次都生成完全相同的文本。如果与数学功能相结合,功能会更加强大。
首先,让我们以 R2D2 中使用的一个简单宏为例。
<xacro:macro name="default_inertial" params="mass">
<inertial>
<mass value="${mass}" />
<inertia ixx="1e-3" ixy="0.0" ixz="0.0"
iyy="1e-3" iyz="0.0"
izz="1e-3" />
</inertial>
</xacro:macro>
可与代码一起使用
<xacro:default_inertial mass="10"/>
参数的作用与属性相同,可以在表达式中使用。
您也可以将整个程序块用作参数。
<xacro:macro name="blue_shape" params="name *shape">
<link name="${name}">
<visual>
<geometry>
<xacro:insert_block name="shape" />
</geometry>
<material name="blue"/>
</visual>
<collision>
<geometry>
<xacro:insert_block name="shape" />
</geometry>
</collision>
</link>
</xacro:macro>
<xacro:blue_shape name="base_link">
<cylinder radius=".42" length=".01" />
</xacro:blue_shape>
- ?要指定程序块参数,请在参数名称前加上星号。
- 可以使用 insert_block 命令插入程序块
- 插入程序块的次数不限。
4.4.3 实际使用
xacro 语言的使用非常灵活。除了上面显示的默认惯性宏之外,下面是 xacro 在 R2D2 模型中的几种实用方法。
要查看 xacro 文件生成的模型,请运行与之前教程相同的命令:
ros2 launch urdf_tutorial display.launch.py model:=urdf/08-macroed.urdf.xacro
(启动文件一直在运行 xacro 命令,但由于没有宏要展开,所以这并不重要)。
4.4.4 腿部宏
您经常需要在不同位置创建多个外观相似的对象。您可以使用宏和一些简单的数学计算来减少您需要编写的代码量,就像我们在 R2 的两条腿上所做的那样。
<xacro:macro name="leg" params="prefix reflect">
<link name="${prefix}_leg">
<visual>
<geometry>
<box size="${leglen} 0.1 0.2"/>
</geometry>
<origin xyz="0 0 -${leglen/2}" rpy="0 ${pi/2} 0"/>
<material name="white"/>
</visual>
<collision>
<geometry>
<box size="${leglen} 0.1 0.2"/>
</geometry>
<origin xyz="0 0 -${leglen/2}" rpy="0 ${pi/2} 0"/>
</collision>
<xacro:default_inertial mass="10"/>
</link>
<joint name="base_to_${prefix}_leg" type="fixed">
<parent link="base_link"/>
<child link="${prefix}_leg"/>
<origin xyz="0 ${reflect*(width+.02)} 0.25" />
</joint>
<!-- A bunch of stuff cut -->
</xacro:macro>
<xacro:leg prefix="right" reflect="1" />
<xacro:leg prefix="left" reflect="-1" />
- 常用窍门 1:使用名称前缀得到两个名称相似的物体。
- 常用窍门 2:使用数学计算关节原点。在改变机器人尺寸的情况下,使用数学计算来计算关节偏移量,可以省去很多麻烦。
- 常用技巧 3: 使用反射参数,并将其设置为 1 或-1。请看我们是如何使用反射参数,将身体两侧的腿置于 base_to_${prefix}_leg 原点的。
五、将 URDF 与机器人状态发布器结合使用?
5.1 背景
本教程将向您展示如何为行走机器人建模、将状态发布为 tf2 消息并在 Rviz 中查看模拟结果。 首先,我们创建描述机器人装配的 URDF 模型。接下来,我们编写一个节点,模拟运动并发布 JointState 和变换。然后,我们使用 robot_state_publisher 将整个机器人状态发布到 /tf2。
5.2?任务
5.2.1 创建软件包
创建目录:
mkdir -p second_ros2_ws/src
然后创建软件包:
cd second_ros2_ws/src
ros2 pkg create --build-type ament_python --license Apache-2.0 urdf_tutorial_r2d2 --dependencies rclpy
cd urdf_tutorial_r2d2
现在你应该看到一个 urdf_tutorial_r2d2 文件夹。接下来,您将对其进行一些更改。
5.2.2 创建 URDF 文件
创建一个目录,我们将在其中存储一些资产:
mkdir -p urdf
下载 URDF 文件并将其保存为 second_ros2_ws/src/urdf_tutorial_r2d2/urdf/r2d2.urdf.xml。下载 Rviz 配置文件并将其保存为 second_ros2_ws/src/urdf_tutorial_r2d2/urdf/r2d2.rviz。
<robot name="r2d2">
<link name="axis">
<visual>
<origin xyz="0 0 0" rpy="1.57 0 0" />
<geometry>
<cylinder radius="0.01" length=".5" />
</geometry>
<material name="gray">
<color rgba=".2 .2 .2 1" />
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="1.57 0 0" />
<geometry>
<cylinder radius="0.01" length=".5" />
</geometry>
<contact_coefficients mu="0" kp="1000.0" kd="1.0"/>
</collision>
</link>
<link name="leg1">
<inertial>
<mass value="1"/>
<inertia ixx="1e-3" ixy="0" ixz="0" iyy="1e-3" iyz="0" izz="1e-3" />
<origin/>
</inertial>
<visual>
<origin xyz="0 0 -.3" />
<geometry>
<box size=".20 .10 .8" />
</geometry>
<material name="white">
<color rgba="1 1 1 1"/>
</material>
</visual>
<collision>
<origin xyz="0 0 -.3" />
<geometry>
<box size=".20 .10 .8" />
</geometry>
<contact_coefficients mu="0" kp="1000.0" kd="1.0"/>
</collision>
</link>
<joint name="leg1connect" type="fixed">
<origin xyz="0 .30 0" />
<parent link="axis"/>
<child link="leg1"/>
</joint>
<link name="leg2">
<inertial>
<mass value="1"/>
<inertia ixx="1e-3" ixy="0" ixz="0" iyy="1e-3" iyz="0" izz="1e-3" />
<origin/>
</inertial>
<visual>
<origin xyz="0 0 -.3" />
<geometry>
<box size=".20 .10 .8" />
</geometry>
<material name="white">
<color rgba="1 1 1 1"/>
</material>
</visual>
<collision>
<origin xyz="0 0 -.3" />
<geometry>
<box size=".20 .10 .8" />
</geometry>
<contact_coefficients mu="0" kp="1000.0" kd="1.0"/>
</collision>
</link>
<joint name="leg2connect" type="fixed">
<origin xyz="0 -.30 0" />
<parent link="axis"/>
<child link="leg2"/>
</joint>
<link name="body">
<inertial>
<mass value="1"/>
<inertia ixx="1e-3" ixy="0" ixz="0" iyy="1e-3" iyz="0" izz="1e-3" />
<origin/>
</inertial>
<visual>
<origin xyz="0 0 -0.2" />
<geometry>
<cylinder radius=".20" length=".6"/>
</geometry>
<material name="white"/>
</visual>
<collision>
<origin xyz="0 0 0.2" />
<geometry>
<cylinder radius=".20" length=".6"/>
</geometry>
<contact_coefficients mu="0" kp="1000.0" kd="1.0"/>
</collision>
</link>
<joint name="tilt" type="revolute">
<parent link="axis"/>
<child link="body"/>
<origin xyz="0 0 0" rpy="0 0 0" />
<axis xyz="0 1 0" />
<limit upper="0" lower="-.5" effort="10" velocity="10" />
</joint>
<link name="head">
<inertial>
<mass value="1"/>
<inertia ixx="1e-3" ixy="0" ixz="0" iyy="1e-3" iyz="0" izz="1e-3" />
<origin/>
</inertial>
<visual>
<geometry>
<sphere radius=".4" />
</geometry>
<material name="white" />
</visual>
<collision>
<origin/>
<geometry>
<sphere radius=".4" />
</geometry>
<contact_coefficients mu="0" kp="1000.0" kd="1.0"/>
</collision>
</link>
<joint name="swivel" type="continuous">
<origin xyz="0 0 0.1" />
<axis xyz="0 0 1" />
<parent link="body"/>
<child link="head"/>
</joint>
<link name="rod">
<inertial>
<mass value="1"/>
<inertia ixx="1e-3" ixy="0" ixz="0" iyy="1e-3" iyz="0" izz="1e-3" />
<origin/>
</inertial>
<visual>
<origin xyz="0 0 -.1" />
<geometry>
<cylinder radius=".02" length=".2" />
</geometry>
<material name="gray" />
</visual>
<collision>
<origin/>
<geometry>
<cylinder radius=".02" length=".2" />
</geometry>
<contact_coefficients mu="0" kp="1000.0" kd="1.0"/>
</collision>
</link>
<joint name="periscope" type="prismatic">
<origin xyz=".12 0 .15" />
<axis xyz="0 0 1" />
<limit upper="0" lower="-.5" effort="10" velocity="10" />
<parent link="head"/>
<child link="rod"/>
</joint>
<link name="box">
<inertial>
<mass value="1"/>
<inertia ixx="1e-3" ixy="0" ixz="0" iyy="1e-3" iyz="0" izz="1e-3" />
<origin/>
</inertial>
<visual>
<geometry>
<box size=".05 .05 .05" />
</geometry>
<material name="blue" >
<color rgba="0 0 1 1" />
</material>
</visual>
<collision>
<origin/>
<geometry>
<box size=".05 .05 .05" />
</geometry>
<contact_coefficients mu="0" kp="1000.0" kd="1.0"/>
</collision>
</link>
<joint name="boxconnect" type="fixed">
<origin xyz="0 0 0" />
<parent link="rod"/>
<child link="box"/>
</joint>
</robot>
Panels:
- Class: rviz_common/Displays
Help Height: 78
Name: Displays
Property Tree Widget:
Expanded:
- /Global Options1
- /Status1
- /TF1
- /RobotModel1
- /RobotModel1/Description Topic1
Splitter Ratio: 0.5
Tree Height: 617
- Class: rviz_common/Selection
Name: Selection
- Class: rviz_common/Tool Properties
Expanded:
- /2D Goal Pose1
- /Publish Point1
Name: Tool Properties
Splitter Ratio: 0.5886790156364441
- Class: rviz_common/Views
Expanded:
- /Current View1
Name: Views
Splitter Ratio: 0.5
Visualization Manager:
Class: ""
Displays:
- Alpha: 0.5
Cell Size: 1
Class: rviz_default_plugins/Grid
Color: 160; 160; 164
Enabled: true
Line Style:
Line Width: 0.029999999329447746
Value: Lines
Name: Grid
Normal Cell Count: 0
Offset:
X: 0
Y: 0
Z: 0
Plane: XY
Plane Cell Count: 10
Reference Frame: <Fixed Frame>
Value: true
- Class: rviz_default_plugins/TF
Enabled: true
Frame Timeout: 15
Frames:
All Enabled: true
axis:
Value: true
body:
Value: true
box:
Value: true
head:
Value: true
leg1:
Value: true
leg2:
Value: true
odom:
Value: true
rod:
Value: true
Marker Scale: 1
Name: TF
Show Arrows: true
Show Axes: true
Show Names: false
Tree:
odom:
axis:
body:
head:
rod:
box:
{}
leg1:
{}
leg2:
{}
Update Interval: 0
Value: true
- Alpha: 1
Class: rviz_default_plugins/RobotModel
Collision Enabled: false
Description File: ""
Description Source: Topic
Description Topic:
Depth: 5
Durability Policy: Volatile
History Policy: Keep Last
Reliability Policy: Reliable
Value: /robot_description
Enabled: true
Links:
All Links Enabled: true
Expand Joint Details: false
Expand Link Details: false
Expand Tree: false
Link Tree Style: Links in Alphabetic Order
axis:
Alpha: 1
Show Axes: false
Show Trail: false
Value: true
body:
Alpha: 1
Show Axes: false
Show Trail: false
Value: true
box:
Alpha: 1
Show Axes: false
Show Trail: false
Value: true
head:
Alpha: 1
Show Axes: false
Show Trail: false
Value: true
leg1:
Alpha: 1
Show Axes: false
Show Trail: false
Value: true
leg2:
Alpha: 1
Show Axes: false
Show Trail: false
Value: true
rod:
Alpha: 1
Show Axes: false
Show Trail: false
Value: true
Name: RobotModel
TF Prefix: ""
Update Interval: 0
Value: true
Visual Enabled: true
Enabled: true
Global Options:
Background Color: 48; 48; 48
Fixed Frame: odom
Frame Rate: 30
Name: root
Tools:
- Class: rviz_default_plugins/Interact
Hide Inactive Objects: true
- Class: rviz_default_plugins/MoveCamera
- Class: rviz_default_plugins/Select
- Class: rviz_default_plugins/FocusCamera
- Class: rviz_default_plugins/Measure
Line color: 128; 128; 0
- Class: rviz_default_plugins/SetInitialPose
Topic:
Depth: 5
Durability Policy: Volatile
History Policy: Keep Last
Reliability Policy: Reliable
Value: /initialpose
- Class: rviz_default_plugins/SetGoal
Topic:
Depth: 5
Durability Policy: Volatile
History Policy: Keep Last
Reliability Policy: Reliable
Value: /goal_pose
- Class: rviz_default_plugins/PublishPoint
Single click: true
Topic:
Depth: 5
Durability Policy: Volatile
History Policy: Keep Last
Reliability Policy: Reliable
Value: /clicked_point
Transformation:
Current:
Class: rviz_default_plugins/TF
Value: true
Views:
Current:
Class: rviz_default_plugins/Orbit
Distance: 10
Enable Stereo Rendering:
Stereo Eye Separation: 0.05999999865889549
Stereo Focal Distance: 1
Swap Stereo Eyes: false
Value: false
Focal Point:
X: 0
Y: 0
Z: 0
Focal Shape Fixed Size: true
Focal Shape Size: 0.05000000074505806
Invert Z Axis: false
Name: Current View
Near Clip Distance: 0.009999999776482582
Pitch: 0.459797203540802
Target Frame: <Fixed Frame>
Value: Orbit (rviz)
Yaw: 4.618575572967529
Saved: ~
Window Geometry:
Displays:
collapsed: false
Height: 846
Hide Left Dock: false
Hide Right Dock: false
QMainWindow State: 000000ff00000000fd000000040000000000000156000002f4fc0200000008fb0000001200530065006c0065006300740069006f006e00000001e10000009b0000005c00fffffffb0000001e0054006f006f006c002000500072006f007000650072007400690065007302000001ed000001df00000185000000a3fb000000120056006900650077007300200054006f006f02000001df000002110000018500000122fb000000200054006f006f006c002000500072006f0070006500720074006900650073003203000002880000011d000002210000017afb000000100044006900730070006c006100790073010000003d000002f4000000c900fffffffb0000002000730065006c0065006300740069006f006e00200062007500660066006500720200000138000000aa0000023a00000294fb00000014005700690064006500530074006500720065006f02000000e6000000d2000003ee0000030bfb0000000c004b0069006e0065006300740200000186000001060000030c00000261000000010000010f000002f4fc0200000003fb0000001e0054006f006f006c002000500072006f00700065007200740069006500730100000041000000780000000000000000fb0000000a00560069006500770073010000003d000002f4000000a400fffffffb0000001200530065006c0065006300740069006f006e010000025a000000b200000000000000000000000200000490000000a9fc0100000001fb0000000a00560069006500770073030000004e00000080000002e10000019700000003000004420000003efc0100000002fb0000000800540069006d00650100000000000004420000000000000000fb0000000800540069006d00650100000000000004500000000000000000000002cb000002f400000004000000040000000800000008fc0000000100000002000000010000000a0054006f006f006c00730100000000ffffffff0000000000000000
Selection:
collapsed: false
Tool Properties:
collapsed: false
Views:
collapsed: false
Width: 1340
X: 72
Y: 60
5.2.3 发布状态
现在,我们需要一种方法来指定机器人所处的状态。为此,我们必须指定所有三个关节和整体运动轨迹。
启动您最喜欢的编辑器,将以下代码粘贴到 second_ros2_ws/src/urdf_tutorial_r2d2/urdf_tutorial_r2d2/state_publisher.py 中
from math import sin, cos, pi
import rclpy
from rclpy.node import Node
from rclpy.qos import QoSProfile
from geometry_msgs.msg import Quaternion
from sensor_msgs.msg import JointState
from tf2_ros import TransformBroadcaster, TransformStamped
class StatePublisher(Node):
def __init__(self):
rclpy.init()
super().__init__('state_publisher')
qos_profile = QoSProfile(depth=10)
self.joint_pub = self.create_publisher(JointState, 'joint_states', qos_profile)
self.broadcaster = TransformBroadcaster(self, qos=qos_profile)
self.nodeName = self.get_name()
self.get_logger().info("{0} started".format(self.nodeName))
degree = pi / 180.0
loop_rate = self.create_rate(30)
# robot state
tilt = 0.
tinc = degree
swivel = 0.
angle = 0.
height = 0.
hinc = 0.005
# message declarations
odom_trans = TransformStamped()
odom_trans.header.frame_id = 'odom'
odom_trans.child_frame_id = 'axis'
joint_state = JointState()
try:
while rclpy.ok():
rclpy.spin_once(self)
# update joint_state
now = self.get_clock().now()
joint_state.header.stamp = now.to_msg()
joint_state.name = ['swivel', 'tilt', 'periscope']
joint_state.position = [swivel, tilt, height]
# update transform
# (moving in a circle with radius=2)
odom_trans.header.stamp = now.to_msg()
odom_trans.transform.translation.x = cos(angle)*2
odom_trans.transform.translation.y = sin(angle)*2
odom_trans.transform.translation.z = 0.7
odom_trans.transform.rotation = \
euler_to_quaternion(0, 0, angle + pi/2) # roll,pitch,yaw
# send the joint state and transform
self.joint_pub.publish(joint_state)
self.broadcaster.sendTransform(odom_trans)
# Create new robot state
tilt += tinc
if tilt < -0.5 or tilt > 0.0:
tinc *= -1
height += hinc
if height > 0.2 or height < 0.0:
hinc *= -1
swivel += degree
angle += degree/4
# This will adjust as needed per iteration
loop_rate.sleep()
except KeyboardInterrupt:
pass
def euler_to_quaternion(roll, pitch, yaw):
qx = sin(roll/2) * cos(pitch/2) * cos(yaw/2) - cos(roll/2) * sin(pitch/2) * sin(yaw/2)
qy = cos(roll/2) * sin(pitch/2) * cos(yaw/2) + sin(roll/2) * cos(pitch/2) * sin(yaw/2)
qz = cos(roll/2) * cos(pitch/2) * sin(yaw/2) - sin(roll/2) * sin(pitch/2) * cos(yaw/2)
qw = cos(roll/2) * cos(pitch/2) * cos(yaw/2) + sin(roll/2) * sin(pitch/2) * sin(yaw/2)
return Quaternion(x=qx, y=qy, z=qz, w=qw)
def main():
node = StatePublisher()
if __name__ == '__main__':
main()
?5.2.4 创建启动文件
新建一个 second_ros2_ws/src/urdf_tutorial_r2d2/launch 文件夹。打开编辑器并粘贴以下代码,保存为 second_ros2_ws/src/urdf_tutorial_r2d2/launch/demo_launch.py
import os
from ament_index_python.packages import get_package_share_directory
from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument
from launch.substitutions import LaunchConfiguration
from launch_ros.actions import Node
def generate_launch_description():
use_sim_time = LaunchConfiguration('use_sim_time', default='false')
urdf_file_name = 'r2d2.urdf.xml'
urdf = os.path.join(
get_package_share_directory('urdf_tutorial_r2d2'),
urdf_file_name)
with open(urdf, 'r') as infp:
robot_desc = infp.read()
return LaunchDescription([
DeclareLaunchArgument(
'use_sim_time',
default_value='false',
description='Use simulation (Gazebo) clock if true'),
Node(
package='robot_state_publisher',
executable='robot_state_publisher',
name='robot_state_publisher',
output='screen',
parameters=[{'use_sim_time': use_sim_time, 'robot_description': robot_desc}],
arguments=[urdf]),
Node(
package='urdf_tutorial_r2d2',
executable='state_publisher',
name='state_publisher',
output='screen'),
])
5.2.5 编辑 setup.py 文件
您必须告诉 colcon 编译工具如何安装您的 Python 软件包。编辑 second_ros2_ws/src/urdf_tutorial_r2d2/setup.py 文件如下:
- 包含这些导入语句
import os
from glob import glob
from setuptools import setup
from setuptools import find_packages
- ?将这两行添加到 data_files 中
data_files=[
...
(os.path.join('share', package_name, 'launch'), glob(os.path.join('launch', '*launch.[pxy][yma]*'))),
(os.path.join('share', package_name), glob('urdf/*')),
],
?修改 entry_points 表,以便以后从控制台运行 "state_publisher
'console_scripts': [
'state_publisher = urdf_tutorial_r2d2.state_publisher:main'
],
保存更改后的 setup.py 文件。
5.2.6 安装软件包
cd second_ros2_ws
colcon build --symlink-install --packages-select urdf_tutorial_r2d2
?源设置文件:
source install/setup.bash
5.2.7 查看结果
启动软件包
ros2 launch urdf_tutorial_r2d2 demo_launch.py
打开一个新终端,使用以下命令运行 Rviz
rviz2 -d second_ros2_ws/install/urdf_tutorial_r2d2/share/urdf_tutorial_r2d2/r2d2.rviz
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!