乐于分享
好东西不私藏

机器人系统——ROS2文档(进阶)—URDF-使用 Xacro 清理代码

机器人系统——ROS2文档(进阶)—URDF-使用 Xacro 清理代码

目标: 学习使用 Xacro 减少 URDF 文件中代码量的一些技巧。

教程级别: 中级
时间: 20 分钟

目录
使用 Xacro
常量
数学运算
宏
o简单宏
o参数化宏
实际应用
o腿的宏

到目前为止,如果您在自己的机器人设计中遵循了所有这些步骤,您可能已经厌倦了为了正确解析非常简单的机器人描述而做各种数学计算。幸运的是,您可以使用 xacro 包来让您的工作更简单。它做了三件非常有帮助的事情:

  • 常量
  • 简单数学运算

在本教程中,我们将了解所有这些快捷方式,以帮助减少 URDF 文件的整体大小,使其更易于阅读和维护。

使用 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():
return LaunchDescription([
IncludeLaunchDescription(
PathJoinSubstitution([FindPackageShare('urdf_launch'), 'launch', 'display.launch.py']),
launch_arguments={
'urdf_package': 'turtlebot3_description',
'urdf_package_path': PathJoinSubstitution(['urdf', 'turtlebot3_burger.urdf'])
}.items()
)
])

在 URDF 文件的顶部,您必须指定一个命名空间以便文件能够正确解析。例如,这是一个有效的 xacro 文件的前两行:

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

常量

让我们快速看一下 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),在任何级别,在使用之前或之后。通常它们放在顶部。
  • 我们不在 geometry 元素中指定实际半径,而是使用美元符号和花括号来表示该值。
  • 这段代码将生成上面所示的相同代码。

${} 构造的内容的值然后被用来替换 ${}。这意味着您可以将其与属性中的其他文本组合。

<xacro:property name="robotname" value="marvin" />
<link name="${robotname}s_leg" />

这将生成:

<link name="marvins_leg" />

然而,${} 中的内容不仅限于属性,这引出了我们的下一个要点……

数学运算

您可以使用四种基本运算(+、-、*、/)、一元减号和括号在 ${} 构造中构建任意复杂的表达式。例如:

<cylinder radius="${wheeldiam/2}" length="0.1"/>
<origin xyz="${reflect*(width+.02)} 0 0.25" />

您还可以使用基本的数学运算之外的功能,如 sin 和 cos

这是 xacro 包中最大且最有用的组件。

简单宏

让我们看一个简单的无用宏。

<xacro:macro name="default_origin">
<origin xyz="0 0 0" rpy="0 0 0"/>
</xacro:macro>

<xacro:default_origin />

(这没用,因为如果没有指定 origin,它具有与此相同的值。)这段代码将生成以下内容:

<origin rpy="0 0 0" xyz="0 0 0"/>
  • name 在技术上不是必需的元素,但您需要指定它才能使用它。
  • 每个 <xacro:$NAME /> 的实例都被 xacro:macro 标签的内容替换。
  • 注意,即使不完全相同(两个属性交换了顺序),生成的 XML 也是等价的。
  • 如果找不到指定名称的 xacro,它将不会被展开并且不会生成错误。

参数化宏

您还可以参数化宏,以便它们不会每次都生成完全相同的文本。当与数学功能结合时,这更加强大。

首先,让我们看一个在 R2D2 中使用的简单宏示例。

<xacro:macro name="default_inertial" params="mass">
<inertial>
<mass value="${mass}" />
<inertia ixx="${(1e-3) * mass}" ixy="0.0" ixz="0.0" iyy="${(1e-3) * mass}" iyz="0.0" izz="${(1e-3) * mass}" />
</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 命令插入块。
  • 根据您的需要插入任意次数的块。

实际应用

xacro 语言在其允许您做的事情方面相当灵活。除了上面显示的默认惯性宏之外,以下是 R2D2 模型中使用 xacro 的一些有用方式。

要查看 xacro 文件生成的模型,请运行与之前教程相同的命令:

ros2 launch urdf_tutorial display.launch.py model:=urdf/08-macroed.urdf.xacro

(启动文件一直在运行 xacro 命令,但由于没有要展开的宏,所以没有影响)

腿的宏

通常您希望在多个不同位置创建多个相似外观的对象。您可以使用宏和一些简单的数学运算来减少您必须编写的代码量,就像我们对 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:使用 reflect 参数,并将其设置为 1 或 -1。看看我们如何在 base_to_${prefix}_leg 原点中使用 reflect 参数将腿放置在身体的两侧。
本文档完整翻译自 ROS 2 Lyrical 官方文档,仅用于学习交流。