设置 URDF
在本指南中,我们将为简单的差速驱动机器人创建统一机器人描述格式 (URDF) 文件,让您亲身体验如何使用 URDF。我们还将设置机器人状态发布者并在 RVIZ 中可视化我们的模型。最后,我们将向我们的机器人 URDF 添加一些运动学属性,以准备进行模拟。这些步骤对于表示您的机器人的所有传感器、硬件和机器人变换以用于导航是必不可少的。
See also
本教程中的完整源代码可以在 navigation2_tutorials 存储库中的 sam_bot_description
包中找到。请注意,完成本指南中的所有教程后,存储库包含完整的代码。
URDF 和机器人状态发布器
如上一教程所述,Navigation2 的要求之一是从 base_link
到各种传感器和参考系的转换。此转换树可以是只有一个从 base_link
到 laser_link
的链接的简单树,也可以是由位于不同位置的多个传感器组成的树,每个传感器都有自己的坐标系。创建多个发布器来处理所有这些坐标系转换可能会变得很繁琐。因此,我们将利用机器人状态发布器包来发布我们的转换。
机器人状态发布器是 ROS 2 的一个包,它与 tf2 包交互以发布可以从机器人的几何形状和结构直接推断出的所有必要转换。我们需要为其提供正确的 URDF,它将自动处理发布转换。这对于复杂的转换非常有用,但对于更简单的转换树,我们仍然建议使用它。
统一机器人描述格式 (URDF) 是一个表示机器人模型的 XML 文件。在本教程中,它将主要用于构建与机器人几何相关的转换树,但它也有其他用途。一个例子是如何通过定义材料和网格等可视组件,在 RVIZ(一种 ROS 的 3D 可视化工具)中可视化您的机器人模型。另一个例子是如何用 URDF 来定义机器人的物理属性。然后,这些属性用于 Gazebo 等物理模拟器,以模拟您的机器人在环境中的交互方式。
URDF 的另一个主要特性是它还支持 Xacro(XML 宏),以帮助您创建更短、更易读的 XML,以帮助定义复杂的机器人。我们可以使用这些宏来消除在 URDF 中重复使用 XML 块的需要。Xacro 还可用于定义可在整个 URDF 中重复使用的配置常量。
See also
如果您想了解有关 URDF 和机器人状态发布器的更多信息,我们建议您查看官方的 URDF 文档 和 Robot State Publisher 文档
设置环境
在本指南中,我们假设您已经熟悉 ROS 2 以及如何设置您的开发环境,因此我们将轻松完成本节中的步骤。
让我们从安装一些我们将在本教程中使用的其他 ROS 2 软件包开始。
sudo apt install ros-<ros2-distro>-joint-state-publisher-gui
sudo apt install ros-<ros2-distro>-xacro
接下来,为您的项目创建一个目录,初始化 ROS 2 工作区并为您的机器人命名。对于我们来说,我们将其称为 sam_bot
。
ros2 pkg create --build-type ament_cmake sam_bot_description
编写 URDF
See also
本节旨在为您提供一个适合初学者的介绍,帮助您为您的机器人构建 URDF。如果您想了解有关 URDF 和 XAcro 的更多信息,我们建议您查看官方的 URDF 文档
现在我们已经设置了项目工作区,让我们直接开始编写 URDF。下面是我们将要尝试构建的机器人的图像。


首先,在
src/description
下创建一个名为sam_bot_description.urdf
的文件,并输入以下内容作为文件的初始内容。
1<?xml version="1.0"?>
2<robot name="sam_bot" xmlns:xacro="http://ros.org/wiki/xacro">
3
4
5
6</robot>
Note
以下代码片段应放在“<robot>”标签内。我们建议按照本教程中介绍的顺序添加它们。我们还包含了一些行号,让您大致了解在哪里输入代码。这可能与您编写的实际文件不同,具体取决于您对空格的使用。另请注意,行号假定您正在输入本指南中出现的代码。
接下来,让我们使用 XAcro 属性定义一些常量,这些常量将在整个 URDF 中重复使用。
4 <!-- Define robot constants -->
5 <xacro:property name="base_width" value="0.31"/>
6 <xacro:property name="base_length" value="0.42"/>
7 <xacro:property name="base_height" value="0.18"/>
8
9 <xacro:property name="wheel_radius" value="0.10"/>
10 <xacro:property name="wheel_width" value="0.04"/>
11 <xacro:property name="wheel_ygap" value="0.025"/>
12 <xacro:property name="wheel_zoff" value="0.05"/>
13 <xacro:property name="wheel_xoff" value="0.12"/>
14
15 <xacro:property name="caster_xoff" value="0.14"/>
下面简要讨论一下这些属性在我们的 urdf 中代表什么。 base_*
属性都定义了机器人主底盘的尺寸。wheel_radius
和 wheel_width
定义了机器人两个后轮的形状。wheel_ygap
沿 y 轴调整车轮与底盘之间的间隙,而 wheel_zoff
和 wheel_xoff
沿 z 轴和 x 轴适当定位后轮。最后, caster_xoff
沿 x 轴定位前脚轮。
然后让我们定义我们的 base_link
- 这个链接将是一个大盒子,并将充当我们机器人的主底盘。在 URDF 中, link
元素描述了我们机器人的刚性部分或组件。然后,机器人状态发布者利用这些定义来确定每个链接的坐标系并发布它们之间的变换。
我们还将定义一些链接的视觉属性,这些属性可被 Gazebo 和 Rviz 等工具用来向我们展示机器人的 3D 模型。这些属性包括描述链接形状的 <geometry>
和描述其颜色的 <material>
。
对于下面的代码块,我们使用 ${property}
语法从之前定义的机器人常量部分访问 base
属性。此外,我们还将主机架的材质颜色设置为 Cyan
。请注意,我们在 <visual>
标签下设置这些参数,因此它们将仅用作不影响任何碰撞或物理属性的视觉参数。
17 <!-- Robot Base -->
18 <link name="base_link">
19 <visual>
20 <geometry>
21 <box size="${base_length} ${base_width} ${base_height}"/>
22 </geometry>
23 <material name="Cyan">
24 <color rgba="0 1.0 1.0 1.0"/>
25 </material>
26 </visual>
27 </link>
接下来,让我们定义一个 base_footprint
链接。 base_footprint
链接是一个虚拟(非物理)链接,没有尺寸或碰撞区域。其主要目的是使各种包能够确定投射到地面的机器人中心。例如,Navigation2 使用此链接来确定其避障算法中使用的圆形足迹的中心。同样,我们设置此链接没有尺寸,以及机器人中心投射到地平面时的位置。
定义 base_link 后,我们添加一个关节将其连接到 base_link
。在 URDF 中, joint
元素描述坐标系之间的运动学和动态特性。对于这种情况,我们将根据上述描述定义一个具有适当偏移量的 fixed
关节,以将我们的 base_footprint
链接放置在适当的位置。请记住,我们希望将 base_footprint 设置为从主底盘中心投影到地面,因此我们得到 wheel_radius
和 wheel_zoff
的总和,以获得沿 z 轴的适当位置。
29 <!-- Robot Footprint --> 30 <link name="base_footprint"/> 31 32 <joint name="base_joint" type="fixed"> 33 <parent link="base_link"/> 34 <child link="base_footprint"/> 35 <origin xyz="0.0 0.0 ${-(wheel_radius+wheel_zoff)}" rpy="0 0 0"/> 36 </joint>现在,我们将为机器人添加两个大驱动轮。为了使我们的代码更简洁并避免重复,我们将使用宏来定义将使用不同参数重复的代码块。我们的宏将有 3 个参数:
prefix
只是为我们的链接和关节名称添加前缀,x_reflect
和y_reflect
允许我们分别相对于 x 轴和 y 轴翻转车轮的位置。在这个宏中,我们还可以定义单个车轮的视觉属性。最后,我们还将定义一个continuous
关节,以允许我们的车轮绕轴自由旋转。这个关节还将我们的车轮连接到适当位置的base_link
。在此代码块的末尾,我们将使用刚刚通过
xacro:wheel
标签创建的宏实例化两个车轮。请注意,我们还定义了参数,以便在机器人背面的两侧各有一个车轮。38 <!-- Wheels --> 39 <xacro:macro name="wheel" params="prefix x_reflect y_reflect"> 40 <link name="${prefix}_link"> 41 <visual> 42 <origin xyz="0 0 0" rpy="${pi/2} 0 0"/> 43 <geometry> 44 <cylinder radius="${wheel_radius}" length="${wheel_width}"/> 45 </geometry> 46 <material name="Gray"> 47 <color rgba="0.5 0.5 0.5 1.0"/> 48 </material> 49 </visual> 50 </link> 51 52 <joint name="${prefix}_joint" type="continuous"> 53 <parent link="base_link"/> 54 <child link="${prefix}_link"/> 55 <origin xyz="${x_reflect*wheel_xoff} ${y_reflect*(base_width/2+wheel_ygap)} ${-wheel_zoff}" rpy="0 0 0"/> 56 <axis xyz="0 1 0"/> 57 </joint> 58 </xacro:macro> 59 60 <xacro:wheel prefix="drivewhl_l" x_reflect="-1" y_reflect="1" /> 61 <xacro:wheel prefix="drivewhl_r" x_reflect="-1" y_reflect="-1" />接下来,我们将在机器人的前面添加一个脚轮。我们将把这个轮子建模为一个球体,以保持简单。同样,我们定义轮子的几何形状、材质和关节,以将其连接到适当位置的
base_link
。
63 <!-- Caster Wheel -->
64 <link name="front_caster">
65 <visual>
66 <geometry>
67 <sphere radius="${(wheel_radius+wheel_zoff-(base_height/2))}"/>
68 </geometry>
69 <material name="Cyan">
70 <color rgba="0 1.0 1.0 1.0"/>
71 </material>
72 </visual>
73 </link>
74
75 <joint name="caster_joint" type="fixed">
76 <parent link="base_link"/>
77 <child link="front_caster"/>
78 <origin xyz="${caster_xoff} 0.0 ${-(base_height/2)}" rpy="0 0 0"/>
79 </joint>
就这样!我们已经为一个简单的差速驱动机器人构建了一个 URDF。在下一节中,我们将重点介绍构建包含我们的 URDF 的 ROS 包、启动机器人状态发布器以及在 RVIz 中可视化机器人。
构建和启动
See also
本教程中的启动文件改编自官方的 URDF Tutorials for ROS 2
让我们从添加构建此项目后所需的一些依赖项开始此部分。打开项目目录的根目录,并将以下行添加到 package.xml
中(最好在 <buildtool_depend>
标记之后)
<exec_depend>joint_state_publisher</exec_depend>
<exec_depend>joint_state_publisher_gui</exec_depend>
<exec_depend>robot_state_publisher</exec_depend>
<exec_depend>rviz</exec_depend>
<exec_depend>xacro</exec_depend>
接下来,让我们创建启动文件。ROS 2 使用启动文件来启动我们包所需的节点。从项目的根目录中,创建一个名为 launch
的目录并在其中创建 display.launch.py
文件。下面的启动文件在 ROS 2 中启动一个机器人发布者节点,该节点使用我们的 URDF 发布我们机器人的变换。此外,启动文件还会自动启动 RVIZ,这样我们就可以按照 URDF 的定义来可视化我们的机器人。将下面的代码片段复制并粘贴到您的 display.launch.py
文件中。
import launch
from launch.substitutions import Command, LaunchConfiguration
import launch_ros
import os
def generate_launch_description():
pkg_share = launch_ros.substitutions.FindPackageShare(package='sam_bot_description').find('sam_bot_description')
default_model_path = os.path.join(pkg_share, 'src/description/sam_bot_description.urdf')
default_rviz_config_path = os.path.join(pkg_share, 'rviz/urdf_config.rviz')
robot_state_publisher_node = launch_ros.actions.Node(
package='robot_state_publisher',
executable='robot_state_publisher',
parameters=[{'robot_description': Command(['xacro ', LaunchConfiguration('model')])}]
)
joint_state_publisher_node = launch_ros.actions.Node(
package='joint_state_publisher',
executable='joint_state_publisher',
name='joint_state_publisher',
parameters=[{'robot_description': Command(['xacro ', default_model_path])}],
condition=launch.conditions.UnlessCondition(LaunchConfiguration('gui'))
)
joint_state_publisher_gui_node = launch_ros.actions.Node(
package='joint_state_publisher_gui',
executable='joint_state_publisher_gui',
name='joint_state_publisher_gui',
condition=launch.conditions.IfCondition(LaunchConfiguration('gui'))
)
rviz_node = launch_ros.actions.Node(
package='rviz2',
executable='rviz2',
name='rviz2',
output='screen',
arguments=['-d', LaunchConfiguration('rvizconfig')],
)
return launch.LaunchDescription([
launch.actions.DeclareLaunchArgument(name='gui', default_value='True',
description='Flag to enable joint_state_publisher_gui'),
launch.actions.DeclareLaunchArgument(name='model', default_value=default_model_path,
description='Absolute path to robot urdf file'),
launch.actions.DeclareLaunchArgument(name='rvizconfig', default_value=default_rviz_config_path,
description='Absolute path to rviz config file'),
joint_state_publisher_node,
joint_state_publisher_gui_node,
robot_state_publisher_node,
rviz_node
])
See also
有关 ROS 2 中启动系统的更多信息,您可以查看官方的 ROS 2 Launch System Documentation
为了在进行可视化时使事情变得更简单,我们提供了一个 RVIz 配置文件,该文件将在启动包时加载。此配置文件使用适当的设置初始化 RVIz,以便您在机器人启动后立即查看它。在项目的根目录中创建一个名为 rviz
的目录,并在其下创建一个名为 urdf_config.rviz
的文件。将以下内容作为 urdf_config.rviz
的内容
Panels:
- Class: rviz_common/Displays
Help Height: 78
Name: Displays
Property Tree Widget:
Expanded:
- /Global Options1
- /Status1
- /RobotModel1/Links1
- /TF1
Splitter Ratio: 0.5
Tree Height: 557
Visualization Manager:
Class: ""
Displays:
- Alpha: 0.5
Cell Size: 1
Class: rviz_default_plugins/Grid
Color: 160; 160; 164
Enabled: true
Name: Grid
- Alpha: 0.6
Class: rviz_default_plugins/RobotModel
Description Topic:
Depth: 5
Durability Policy: Volatile
History Policy: Keep Last
Reliability Policy: Reliable
Value: /robot_description
Enabled: true
Name: RobotModel
Visual Enabled: true
- Class: rviz_default_plugins/TF
Enabled: true
Name: TF
Marker Scale: 0.3
Show Arrows: true
Show Axes: true
Show Names: true
Enabled: true
Global Options:
Background Color: 48; 48; 48
Fixed Frame: base_link
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
Transformation:
Current:
Class: rviz_default_plugins/TF
Value: true
Views:
Current:
Class: rviz_default_plugins/Orbit
Name: Current View
Target Frame: <Fixed Frame>
Value: Orbit (rviz)
Saved: ~
最后,让我们修改项目根目录中的 CMakeLists.txt
文件,以包含我们在软件包安装过程中刚刚创建的文件。将以下代码片段添加到 CMakeLists.txt
文件中,最好位于 if(BUILD_TESTING)
行上方:
install(
DIRECTORY src launch rviz
DESTINATION share/${PROJECT_NAME}
)
我们现在准备使用 colcon 构建我们的项目。导航到项目根目录并执行以下命令。
colcon build
. install/setup.bash
构建成功后,执行以下命令安装 ROS 2 软件包并启动我们的项目。
ros2 launch sam_bot_description display.launch.py
ROS 2 现在应该启动一个机器人发布者节点并使用我们的 URDF 启动 RVIZ。我们将在下一节中使用 RVIZ 查看我们的机器人。
使用 RVIZ 进行可视化
RVIZ 是一个机器人可视化工具,它允许我们使用机器人的 URDF 查看机器人的 3D 模型。使用上一节中的命令成功启动后,RVIZ 现在应该在您的屏幕上可见,并且应该看起来像下面的图像。您可能需要四处移动并操纵视图才能很好地查看您的机器人。

如您所见,我们已经成功创建了一个简单的差速驱动机器人并在 RVIz 中对其进行了可视化。无需在 RVIz 中可视化您的机器人,但这是查看您是否正确定义了 URDF 的一个很好的步骤。这可以帮助您确保机器人状态发布器正在发布正确的转换。
您可能已经注意到启动了另一个窗口 - 这是关节状态发布器的 GUI。关节状态发布器是另一个 ROS 2 包,它发布我们非固定关节的状态。您可以通过小型 GUI 操纵此发布器,关节的新姿势将反映在 RVIz 中。滑动两个轮子中任何一个的杆都会旋转这些关节。您可以通过在关节状态发布器 GUI 中扫描滑块来查看 RVIZ 来看到这一点。

Note
We won’t be interacting much with this package for Nav2, but if you would like to know more about the joint state publisher, feel free to have a look at the official Joint State Publisher Documentation.
此时,您可能已经决定停止本教程,因为我们已经实现了为简单差速驱动机器人创建 URDF 的目标。机器人状态发布器现在正在发布从 URDF 派生的变换。这些变换现在可以被其他包(例如 Nav2)用来获取有关机器人形状和结构的信息。但是,要在模拟中正确使用此 URDF,我们需要物理属性,以便机器人像真实机器人一样对物理环境做出反应。可视化字段仅用于可视化,而不是碰撞,因此您的机器人将直接穿过障碍物。我们将在下一节中介绍在 URDF 中添加这些属性。
添加物理属性
作为本指南的附加部分,我们将修改当前的 URDF,以包含我们机器人的一些运动学特性。这些信息可能被 Gazebo 等物理模拟器用来建模和模拟我们的机器人在虚拟环境中的行为。
让我们首先定义包含我们在项目中使用的几何图元的惯性特性的宏。将下面的代码片段放在 URDF 中的常量部分之后:
17 <!-- Define inertial property macros -->
18 <xacro:macro name="box_inertia" params="m w h d">
19 <inertial>
20 <origin xyz="0 0 0" rpy="${pi/2} 0 ${pi/2}"/>
21 <mass value="${m}"/>
22 <inertia ixx="${(m/12) * (h*h + d*d)}" ixy="0.0" ixz="0.0" iyy="${(m/12) * (w*w + d*d)}" iyz="0.0" izz="${(m/12) * (w*w + h*h)}"/>
23 </inertial>
24 </xacro:macro>
25
26 <xacro:macro name="cylinder_inertia" params="m r h">
27 <inertial>
28 <origin xyz="0 0 0" rpy="${pi/2} 0 0" />
29 <mass value="${m}"/>
30 <inertia ixx="${(m/12) * (3*r*r + h*h)}" ixy = "0" ixz = "0" iyy="${(m/12) * (3*r*r + h*h)}" iyz = "0" izz="${(m/2) * (r*r)}"/>
31 </inertial>
32 </xacro:macro>
33
34 <xacro:macro name="sphere_inertia" params="m r">
35 <inertial>
36 <mass value="${m}"/>
37 <inertia ixx="${(2/5) * m * (r*r)}" ixy="0.0" ixz="0.0" iyy="${(2/5) * m * (r*r)}" iyz="0.0" izz="${(2/5) * m * (r*r)}"/>
38 </inertial>
39 </xacro:macro>
首先,使用 <collision>
标签将碰撞区域添加到我们的 base_link
。我们还将使用之前定义的 box_inertia 宏为我们的 base_link
添加一些惯性属性。在我们的 URDF 中的 base_link 的 <link name="base_link">
标签内包含以下代码片段。
52 <collision>
53 <geometry>
54 <box size="${base_length} ${base_width} ${base_height}"/>
55 </geometry>
56 </collision>
57
58 <xacro:box_inertia m="15" w="${base_width}" d="${base_length}" h="${base_height}"/>
接下来,让我们对我们的轮子宏执行相同的操作。在我们的 URDF 中,将以下代码片段包含在我们的轮子宏的 <link name="${prefix}_link">
标签内。
83 <collision>
84 <origin xyz="0 0 0" rpy="${pi/2} 0 0"/>
85 <geometry>
86 <cylinder radius="${wheel_radius}" length="${wheel_width}"/>
87 </geometry>
88 </collision>
89
90 <xacro:cylinder_inertia m="0.5" r="${wheel_radius}" h="${wheel_width}"/>
最后,让我们为我们的球形脚轮添加类似的属性。在 URDF 中,将以下内容添加到我们的脚轮的 <link name="front_caster">
标签中。
114 <collision>
115 <origin xyz="0 0 0" rpy="0 0 0"/>
116 <geometry>
117 <sphere radius="${(wheel_radius+wheel_zoff-(base_height/2))}"/>
118 </geometry>
119 </collision>
120
121 <xacro:sphere_inertia m="0.5" r="${(wheel_radius+wheel_zoff-(base_height/2))}"/>
Note
我们没有为我们的 base_footprint
链接添加任何惯性或碰撞属性,因为这是一个虚拟的、非物理的链接。
构建您的项目,然后使用上一节中的相同命令启动 RViz。
colcon build
. install/setup.bash
ros2 launch sam_bot_description display.launch.py
您可以通过在左侧窗格的 RobotModel
下启用 Collision Enabled
来验证是否已正确设置碰撞区域(如果您还关闭 Visual Enabled
,可能会更容易看到)。在本教程中,我们定义了一个与我们的视觉属性类似的碰撞区域。请注意,情况可能并非总是如此,因为您可能会根据机器人的外观选择更简单的碰撞区域。

现在,我们必须在这里停下来,因为我们需要设置更多组件才能真正开始在 Gazebo 中模拟我们的机器人。我们将在学习这些设置指南的过程中回到这个项目,一旦进入模拟部分,我们最终会看到我们的机器人在虚拟环境中移动。这项工作缺少的主要组件是模拟机器人控制器所需的模拟插件。我们将在适当的部分介绍这些插件并将它们添加到此 URDF 中。
结论
就是这样。在本教程中,您已成功为简单的差速驱动机器人创建了一个 URDF。您还设置了一个 ROS 2 项目,该项目启动了一个机器人发布者节点,然后使用您的 URDF 发布机器人的变换。我们还使用 RViz 可视化我们的机器人以验证我们的 URDF 是否正确。最后,我们为我们的 URDF 添加了一些物理属性,以便为模拟做好准备。
请随意使用本教程作为您自己的机器人的模板。请记住,您的主要目标是发布从 base_link 到 sensor_frames 的正确变换。一旦设置完成,您就可以继续执行我们的其他设置指南。