First-Time Robot Setup Guide
这个部分是一系列指南的集合,旨在为读者提供设置 Nav2 的良好资源。该部分的目标如下:
- 帮助新用户设置使用新机器人的 Navigation2
- 帮助自定义机器人的用户正确设置他们的机器人,以在 ROS/Navigation2 中使用
- 作为有经验读者的检查清单、模板或参考样例
- 提供示例,可以在诸如 Gazebo 或 RViz 这样的仿真器/工具上运行,引导读者进行 Nav2 设置过程,即使没有实体机器人也能进行学习。
涵盖了一般的要点、技巧以及配置机器人平台不同组件(传感器、里程计等)的方法。
为了引导您完成机器人的首次设置,我们将涉及以下主题:
- 介绍 TF2 并设置您的机器人 URDF
- 设置机器人里程计的传感器来源
- 设置机器人感知的传感器来源
- 为您的机器人配置圆形或任意形状的足迹
- 选择并设置用于机器人导航任务的规划器和控制器导航插件
- 生命周期节点管理,以便轻松启动其他相关传感器或节点
note: 这些教程并不旨在提供完整的调整和配置指南,因为它们仅旨在帮助您使用基本配置使您的机器人运行起来。要获取有关如何为您的机器人自定义和调整 Nav2 的更详细讨论和指南,请前往“配置指南”部分。 |
Setting Up Transformations
在本指南中,我们将查看 Nav2 所需的必要变换。这些变换允许 Nav2 将来自各种来源(如传感器和里程计)的信息进行解释,将其转换为可用的坐标框架。以下是一个机器人的完整变换树示例,但我们将从更简单的内容开始。
对于本教程,我们首先会简要介绍ROS中的变换。其次,我们将通过一个简单的命令行演示TF2静态发布器的示例来展示它的运行方式。最后,我们将概述Nav2运行所需的必要变换。
Transforms Introduction变换介绍
.0
这部分指南内容是从 ROS(1)导航文档中的“使用 tf 设置机器人”教程中改编而来。 |
这个例子描述了一个简单的机器人,它有一个移动底座,底座顶部安装有一个单一的激光传感器。机器人有两个定义好的坐标系:一个对应于机器人移动底座的中心点,另一个对应于安装在底座顶部的激光的中心点。我们将附着在移动底座上的坐标系称为 base_link,将附着在激光上的坐标系称为 base_laser。需要注意的是,在接下来的部分中我们将更多地讨论这些坐标系的命名和约定。
现在假设我们有来自激光传感器的一些数据,这些数据是从激光中心点获取的距离测量值。换句话说,我们在 base_laser 坐标系中有一些数据。
现在,假设我们想要利用这些数据帮助移动底座避开世界中的障碍物。为了成功做到这一点,我们需要一种将从 base_laser 坐标系接收到的激光扫描数据转换到 base_link 坐标系的方法。本质上,我们需要定义 base_laser 和 base_link 坐标系之间的关系。
在定义这种关系时,让我们假设我们唯一拥有的数据是激光安装在移动底座中心点前方10厘米、上方20厘米。这为我们提供了一个将 base_link 坐标系与 base_laser 坐标系相关联的平移偏移量。具体来说,我们知道要从 base_link 坐标系转换到 base_laser 坐标系,我们必须应用一个平移量为(x: 0.1米,y: 0.0米,z: 0.2米),而反过来,要从 base_laser 坐标系转换到 base_link 坐标系,我们必须应用相反的平移(x: -0.1米,y: 0.0米,z: -0.20米)。
我们可以选择自己管理这种关系,也就是在必要时存储和应用两个坐标系之间的适当平移,但随着坐标系数量的增加,这变得非常麻烦。幸运的是,我们不必自己来完成这项工作。相反,我们将使用 TF2 一次性定义 base_link 和 base_laser 之间的关系,并让它为我们管理这两个坐标系之间的转换。在处理非静态转换(例如相对移动的一组坐标系,比如地图坐标系中的机器人基底坐标系)时(举例来说,在一个机器人移动并在地图中定位自身位置时,机器人的坐标系相对于地图坐标系是在变化的。机器人基底坐标系的位置和方向可能随着机器人在环境中移动而不断变化),这是特别有用的(TF2(Transform Library for ROS)在处理相对运动的坐标系时非常有用,因为它能够动态地管理这些坐标系之间的转换关系。它可以追踪和更新随着时间或者机器人运动而变化的坐标系之间的转换关系。这样做的目的是确保在机器人移动或者坐标系之间发生变化时,能够正确地进行坐标转换。这种能力对机器人非常重要,因为它需要在移动过程中准确地理解自身在地图中的位置和姿态,以便能够有效地导航、避障或者执行其他任务。TF2能够帮助机器人根据不断变化的环境,保持对自身位置和方向的准确认知)。
要使用 TF2 定义和存储 base_link 和 base_laser 之间的关系,我们需要将它们添加到一个变换树(transform tree)中。在概念上,变换树中的每个节点对应一个坐标系,每条边对应从当前节点到其子节点需要应用的变换。TF2 使用树结构来保证任意两个坐标系之间只有一条单一的遍历路径,假设树中的所有边都是从父节点指向子节点的。
(
每个坐标系都是树中的一个节点,节点之间的边表示从一个坐标系到另一个坐标系进行转换所需的变换。这种结构保证了任意两个坐标系之间只有一条路径,因此在进行坐标转换时,不会出现歧义或多个可能的转换路径,确保了转换的唯一性和准确性。
)
为了创建我们简单示例的变换树,我们将创建两个节点:一个用于 base_link 坐标系,另一个用于 base_laser 坐标系。为了创建它们之间的边,我们首先需要决定哪个节点将成为父节点,哪个将成为子节点。记住 —— 这个区别很重要,因为 TF2 假设所有的变换都是从父节点指向子节点的。
让我们选择 base_link 坐标系作为父节点,因为当机器人添加其他部件/传感器时,它们与 base_laser 坐标系的关系最合理的方式是通过 base_link 坐标系进行关联。这意味着连接 base_link 和 base_laser 的边上关联的变换应为(x: 0.1米,y: 0.0米,z: 0.2米)。
有了这个变换树设置,将在 base_laser 坐标系中接收到的激光扫描转换到 base_link 坐标系,就像调用 TF2 库一样简单。我们的机器人现在可以使用这些信息,在 base_link 坐标系中推理激光扫描数据,并安全地规划绕过环境中的障碍物。
Static Transform Publisher Demo静态变换发布器演示
如果你对 ROS 2 还不熟悉,或者尚未配置好工作环境,请花些时间使用官方 ROS 2 安装文档中的资源来正确设置你的机器。 |
现在让我们尝试使用 TF2 提供的 static_transform_publisher 工具发布一个非常简单的变换。我们将发布从链接 base_link 到链接 base_laser 的转换,其平移为(x: 0.1m,y: 0.0m,z: 0.2m)。请注意,我们将根据本教程前面的图示构建这个变换。
打开命令行并执行以下命令:
ros2 run tf2_ros static_transform_publisher 0.1 0 0.2 0 0 0 base_link base_laser
有了这个设置,我们现在成功地在 TF2 中发布了从 base_link 到 base_laser 的变换。现在让我们通过 tf2_echo 来检查它是否正常工作。打开一个单独的命令行窗口,并执行以下命令:
ros2 run tf2_ros tf2_echo base_link base_laser
你应该能够观察到类似以下内容的重复输出。
At time 0.0
- Translation: [0.100, 0.000, 0.200]
- Rotation: in Quaternion [0.000, 0.000, 0.000, 1.000]
到此为止就是这个简短的演示了 - 我们成功地使用 TF2 库发布了从 base_link 到 base_laser 的变换。请注意,我们不建议在实际的机器人项目中使用上面的演示,这只是一个快速演示 TF2 的方法。对于真正的机器人系统,我们会创建一个 URDF 文件,其中包含有关机器人的这些信息,用于 robot_state_publisher 而不是 static_transform_publisher。在“设置 URDF”教程中将讨论更适合和实用的方法。
如果你想了解更多关于 TF2 及如何创建自己的变换发布器,可以前往官方的 TF2 文档。 TF2 Documentation |
Transforms in Navigation2导航2中的变换
有两个重要的 ROS REP(ROS Enhancement Proposals),我们强烈建议你查看一下。这些文档详细说明了 ROS 社区设定的一些标准,以确保在不同包之间正确运行。Nav2 也遵循这些标准和约定。
REP 105 - 移动平台的坐标系
REP 103 - 测量单位和坐标系的标准约定
简要总结 REP 105,这份文档规定了 ROS 中使用的不同坐标系的命名约定和语义含义。本教程感兴趣的是 base_link、odom 和 map 坐标系。base_link 是连接到机器人固定位置的坐标系,通常位于其主要底盘和旋转中心。odom 坐标系是相对于机器人起始位置的固定坐标系,主要用于局部一致的距离表示。最后,map 坐标系是一个世界固定坐标系,用于全局一致的距离表示。
另一方面,REP 103 讨论了一些标准的测量单位和其他相关约定,以尽量减少不同 ROS 包之间的集成问题。基本概述是使用右手规则定义坐标系,Z轴指向上方,X轴指向前方,并且单位应该是标准的国际单位制单位。
现在让我们针对 Navigation2 包的正常运行,谈谈一些具体的要求。Nav2 需要在 ROS 中发布以下转换关系:
map => odom
odom => base_link
base_link => base_laser(传感器基准坐标系)
REP 105标准中并没有包含base_laser坐标系。在本指南中,我们将使用这个名称来指代机器人平台上激光传感器的坐标系。如果存在多个传感器基准坐标系(例如 camera_link、base_laser2、lidar_link 等),则需要为每一个坐标系进行到base_link的转换。 |
第一个变换 map => odom 通常由处理定位和地图等工作的不同 ROS 包(如 AMCL)提供。这个变换在使用中会实时更新,因此我们不会在机器人的 TF 树中设置静态值。关于如何设置这一点的详细信息可能会相当复杂,因此我们强烈建议查看你所使用的平台的地图或定位包的文档。所有符合 ROS 标准的 SLAM 和定位包都会在启动时自动为你提供这个变换。
而 odom => base_link 通常由我们的里程计系统使用轮子编码器等传感器发布。这通常是通过里程计传感器(IMU、轮子编码器、VIO 等)的传感器融合计算出来的,使用了 robot_localization 包。
在本指南的其余部分,我们将讨论所有其他静态定义的变换(例如 base_link => base_laser,base_link => wheels,wheels => IMU 等)。Nav2 使用这个变换树来正确关联来自传感器或其他感兴趣的坐标系的信息和机器人的其余部分。这两个坐标系之间的变换通常通过机器人状态发布器(Robot State Publisher)和通用机器人描述文件(URDF)提供给 Nav2。如果你的平台上有更多的传感器坐标系,则需要从 base_link 到每个传感器坐标系发布一个变换树。
另请参阅
对于更深入讨论变换的使用以及它们如何用于估计机器人的当前状态,我们强烈推荐查看导航概念中的状态估计主题。Navigation Concepts |
Conclusion
在本教程中,我们讨论了变换的概念以及它们在 Nav2 中的使用。
在最后一节中,我们还探讨了如何使用 TF2 的 static_transform_publisher 发布我们的变换。你可以使用这个方法来设置 Nav2 的变换,但这通常不是最佳方式。在大多数机器人项目中,我们使用机器人状态发布器(Robot State Publisher),因为它更容易使用,并且随着机器人变得更加复杂,它的扩展性更好。我们将在下一个关于“设置 URDF”的教程中讨论机器人状态发布器、URDF 以及如何进行设置。
最后,我们还讨论了 Nav2 的三个发布变换的要求以及在设置它们时需要牢记的相关 ROS Enhancement Proposals(REP)。
对于本指南,我们将创建一个简单的差动驱动机器人的统一机器人描述格式(Unified Robot Description Format,URDF)文件,让你亲身体验使用URDF的操作。我们还将设置机器人状态发布器(robot state publisher),并在RVIZ中可视化我们的模型。最后,我们将向我们的机器人URDF文件添加一些运动学属性,以便为仿真目的做好准备。这些步骤对于表示机器人的所有传感器、硬件和机器人变换以供导航使用是必要的。
本教程中的完整源代码可以在 navigation2_tutorials 存储库的 sam_bot_description 软件包中找到。请注意,该存储库包含在完成本指南中所有教程后的全部代码。 |
URDF and the Robot State Publisher
正如前面的教程中所讨论的那样,Navigation2的要求之一是从base_link到各种传感器和参考坐标系的变换。这个变换树可以是一个简单的树,只有一个从base_link到laser_link的链接,也可以是由位于不同位置的多个传感器组成的树,每个传感器都有自己的坐标系。为处理所有这些坐标系的变换,创建多个发布器可能会变得繁琐。因此,我们将利用机器人状态发布器(Robot State Publisher)软件包来发布我们的变换。
机器人状态发布器是ROS 2的一个软件包,与tf2软件包交互以发布可以直接从机器人的几何结构推断出的所有必要变换。我们需要向它提供正确的URDF,它将自动处理变换的发布。对于复杂的变换非常有用,但对于简单的变换树仍然建议使用。
(
这段话指的是机器人状态发布器(Robot State Publisher)是一个ROS 2的软件包,它与tf2软件包互动,以便发布能够直接从机器人的几何结构推断出的所有必要变换。你只需要提供正确的URDF文件描述机器人的几何结构和连接关系,机器人状态发布器会根据这个描述自动处理变换的发布。
这个工具对于复杂的机器人模型非常有用,因为它能够自动处理从URDF中推断出的变换关系,并将其发布为TF树中的各种变换。但是对于简单的变换树,手动创建和发布这些变换可能更加直观和简单。所以,对于简单的情况,你可能会选择手动创建和发布变换,而对于复杂的机器人模型,机器人状态发布器会更为方便。
)
统一机器人描述格式(URDF)是表示机器人模型的XML文件。在本教程中,它主要用于构建与机器人几何相关的变换树,但它还具有其他用途。
- 一个例子是如何在RVIZ中可视化机器人模型,RVIZ是ROS的一个三维可视化工具,可以通过定义诸如材质和网格等视觉组件来使用URDF。
- 另一个例子是URDF可以用来定义机器人的物理特性。这些特性然后被物理仿真器(如Gazebo)用来模拟机器人在环境中的互动。
URDF的另一个重要特性是它还支持Xacro(XML宏),可以帮助你创建更短、更可读的XML,以便定义复杂的机器人。我们可以使用这些宏来消除URDF中重复的XML块的需求。Xacro还有助于定义配置常量,可以在整个URDF中重复使用。
如果你想了解更多关于URDF和机器人状态发布器(Robot State Publisher)的信息,我们鼓励你查阅官方的URDF文档 URDF Documentation 和机器人状态发布器文档 Robot State Publisher Documentation 。 |
URDF代表统一机器人描述格式(Unified Robot Description Format),是ROS中用于描述机器人模型的XML文件格式。它定义了机器人的几何结构、连接关系和传感器信息等,可用于模拟、可视化和控制机器人。
例如,对于一个机器人模型,URDF文件可以描述其各个连接部件(例如轮子、关节、传感器等)的位置、方向和关系,如车轮的尺寸、连接位置,以及传感器的安装位置等信息。
机器人状态发布器(Robot State Publisher)是ROS中的一个工具,它利用URDF描述的机器人模型自动发布tf2的变换树。通过读取URDF,机器人状态发布器可以自动生成机器人各个部件之间的坐标变换关系,然后发布这些变换关系到ROS系统中,使得机器人的不同部件的位置和关系能够在tf2中得到正确表示。
举个例子,如果你有一个机器人模型的URDF文件描述了机器人的基本结构,包括车轮、臂和传感器的连接关系,机器人状态发布器可以根据这个URDF自动发布tf2所需的所有变换,如车轮到基座的变换、臂到基座的变换以及传感器到基座的变换等。
Setting Up the Environment
在本指南中,我们假设你已经熟悉了ROS 2以及如何设置你的开发环境,所以我们将在这一部分快速介绍这些步骤。
让我们开始安装一些额外的ROS 2软件包,在本教程中我们将会用到它们。
sudo apt install ros-<ros2-distro>-joint-state-publisher-gui
sudo apt install ros-<ros2-distro>-xacro
//将 <ros2-distro> 替换为你正在使用的 ROS 2 版本名称(如 foxy、galactic 等)。
接下来,为你的项目创建一个目录,初始化一个ROS 2工作空间,并给你的机器人起一个名字。
创建项目工作空间和URDF
创建项目目录和工作空间:
mkdir -p ~/sam_bot_ws/src # 创建项目目录
cd ~/sam_bot_ws # 进入项目目录
colcon build # 构建工作空间(如果尚未安装,请使用 'sudo apt install python3-colcon-common-extensions' 安装 'colcon')
对于我们的示例,我们将称其为sam_bot。
cd ~/sam_bot_ws/src # 进入src目录
ros2 pkg create --build-type ament_cmake sam_bot_description
ros2 pkg create
: 这个命令用于创建一个新的ROS 2软件包。--build-type ament_cmake
: 这个参数指定了构建类型为ament_cmake,表示该软件包将使用ament构建系统和CMake作为构建工具。sam_bot_description
: 这是软件包的名称。在这个示例中,该命令将创建一个名为sam_bot_description
的软件包,用于描述机器人的模型和相关信息。
Writing the URDF
这一部分旨在为初学者介绍如何为你的机器人构建URDF。如果你想了解更多关于URDF和XAcro的信息,我们建议你查阅官方的URDF文档 URDF Documentation 。 |
现在我们已经设置好了项目工作空间,让我们直接开始编写URDF。以下是我们将尝试构建的机器人的图片。
要开始,请在src/sam_bot_description下创建一个名为sam_bot_description.urdf的文件,并将以下内容作为文件的初始内容输入。
<?xml version="1.0"?>
<robot name="sam_bot" xmlns:xacro="http://ros.org/wiki/xacro">
</robot>
以下代码片段应放置在<robot>标签内。我们建议按照本教程介绍的顺序添加它们。我们还包括了一些行号,以便大致指导您在何处输入代码。实际写入文件时,由于空格使用方式的不同,行号可能会有所不同。请注意,行号假设您按照本指南中的代码顺序进行输入。 |
接下来,让我们使用XAcro属性定义一些常量,这些常量将在整个URDF中被重复使用。
<!-- Define robot constants -->
<xacro:property name="base_width" value="0.31"/>
<xacro:property name="base_length" value="0.42"/>
<xacro:property name="base_height" value="0.18"/>
<xacro:property name="wheel_radius" value="0.10"/>
<xacro:property name="wheel_width" value="0.04"/>
<xacro:property name="wheel_ygap" value="0.025"/>
<xacro:property name="wheel_zoff" value="0.05"/>
<xacro:property name="wheel_xoff" value="0.12"/>
<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中,链接元素描述了机器人的一个刚性部件或组件。然后,机器人状态发布器利用这些定义来确定每个链接的坐标框架,并发布它们之间的变换。
我们还将定义一些链接的视觉属性,这些属性可以被诸如Gazebo和Rviz之类的工具使用,来展示我们机器人的三维模型。其中包括<geometry>描述链接的形状和<material>描述其颜色。
在下面的代码块中,我们使用了之前定义的机器人常量部分中的base属性,采用了${property}的语法。此外,我们还将主底盘的材质颜色设置为Cyan。请注意,我们将这些参数设置在<visual>标签下,因此它们只会作为视觉参数应用,不会影响任何碰撞或物理特性。
<!-- Robot Base -->
<link name="base_link">
<visual>
<geometry>
<box size="${base_length} ${base_width} ${base_height}"/>
</geometry>
<material name="Cyan">
<color rgba="0 1.0 1.0 1.0"/>
</material>
</visual>
</link>
接下来,让我们定义一个base_footprint链接。base_footprint链接是一个虚拟(非物理)链接,它没有尺寸或碰撞区域。它的主要目的是使各种软件包能够确定机器人在地面上的投影中心。例如,Navigation2使用此链接来确定其避障算法中使用的圆形足迹的中心。同样,我们设置此链接没有尺寸,并确定机器人在投影到地面时的中心位置。
在定义了我们的base_link之后,我们添加一个关节来将其连接到base_footprint。在URDF中,关节元素描述了坐标框架之间的运动学和动力学特性。对于这种情况,我们将定义一个固定关节,并使用适当的偏移量将我们的base_footprint链接放置在基于上述描述的适当位置。请记住,我们希望将我们的base_footprint设置在地面平面上,从主底盘的中心投影出来,因此我们将轮子半径和轮子z轴偏移量的和用于获得沿z轴的适当位置。
<!-- Robot Footprint -->
<link name="base_footprint"/>
<joint name="base_joint" type="fixed">
<parent link="base_link"/>
<child link="base_footprint"/>
<origin xyz="0.0 0.0 ${-(wheel_radius+wheel_zoff)}" rpy="0 0 0"/>
</joint>
这段描述讨论了如何添加两个大的驱动轮到机器人上,并且使用宏来避免代码重复。他们将利用宏来定义一段代码块,这段代码块会在不同的参数下重复使用。宏将有三个参数:prefix,它简单地给我们的链接和关节名称添加前缀;x_reflect和y_reflect,它们允许我们分别相对于x轴和y轴翻转我们轮子的位置。在这个宏中,我们还可以定义单个轮子的视觉属性。最后,我们还将定义一个连续关节,允许我们的轮子在一个轴周围自由旋转。这个关节还将我们的轮子连接到base_link,在适当的位置。
在这段代码块的最后,他们将通过xacro:wheel标签实例化两个轮子,这是通过之前定义的宏完成的。请注意,我们还定义了参数,让我们的机器人在后面的两侧各有一个轮子。
定义了车轮的宏,然后根据这个宏创建了左右两个车轮的节点。
<!-- Wheels -->
<xacro:macro name="wheel" params="prefix x_reflect y_reflect">
<link name="${prefix}_link">
<visual>
<origin xyz="0 0 0" rpy="${pi/2} 0 0"/>
<geometry>
<cylinder radius="${wheel_radius}" length="${wheel_width}"/>
</geometry>
<material name="Gray">
<color rgba="0.5 0.5 0.5 1.0"/>
</material>
</visual>
</link>
<joint name="${prefix}_joint" type="continuous">
<parent link="base_link"/>
<child link="${prefix}_link"/>
<origin xyz="${x_reflect*wheel_xoff} ${y_reflect*(base_width/2+wheel_ygap)} ${-wheel_zoff}" rpy="0 0 0"/>
<axis xyz="0 1 0"/>
</joint>
</xacro:macro>
<xacro:wheel prefix="drivewhl_l" x_reflect="-1" y_reflect="1" />
<xacro:wheel prefix="drivewhl_r" x_reflect="-1" y_reflect="-1" />
用户,接下来我们将在机器人前部添加一个转向轮。为了简化,我们将这个轮子建模为一个球体。同样地,我们会定义轮子的几何形状、材质以及将其连接到 base_link 的关节,确保它在适当位置。
<!-- Caster Wheel -->
<link name="front_caster">
<visual>
<geometry>
<sphere radius="${(wheel_radius+wheel_zoff-(base_height/2))}"/>
</geometry>
<material name="Cyan">
<color rgba="0 1.0 1.0 1.0"/>
</material>
</visual>
</link>
<joint name="caster_joint" type="fixed">
<parent link="base_link"/>
<child link="front_caster"/>
<origin xyz="${caster_xoff} 0.0 ${-(base_height/2)}" rpy="0 0 0"/>
</joint>
完成了!我们已经构建了一个简单差分驱动机器人的 URDF。在接下来的部分,我们将专注于构建包含我们 URDF 的 ROS 软件包,启动机器人状态发布器,并在 RViz 中可视化机器人。
(
-
自动发布变换:机器人状态发布器可以基于定义的 URDF 自动发布节点之间的变换关系。这使得在机器人移动或其关节发生变化时,发布器能够自动更新坐标系之间的变换,使得机器人的状态保持最新。
-
可视化支持:它为 RViz 等工具提供了机器人模型的实时可视化。通过发布坐标系之间的变换,可以在 RViz 中看到机器人模型,从而更直观地了解机器人的状态和姿态。
-
协助导航系统:在导航系统中,机器人状态发布器可以提供实时的机器人位置和姿态信息,帮助导航系统进行路径规划和导航。
)
Build and Launch
这个教程中使用的启动文件是改编自 ROS 2 官方 URDF 教程的版本 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',
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
])
有关 ROS 2 中启动系统的更多信息,请查阅官方 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 查看我们的机器人。
Visualization using RVIZ
RVIZ 是一个机器人可视化工具,它可以使用 URDF 查看我们机器人的三维模型。在前面部分成功启动后,RVIZ 现在应该在屏幕上可见,并且看起来应该像下面的图像。您可能需要移动和调整视图,以便更好地观察您的机器人。
正如您所见,我们成功创建了一个简单的差分驱动机器人,并在 RVIz 中进行了可视化。并不一定需要在 RVIz 中可视化您的机器人,但这是一个很好的步骤,可以查看是否正确定义了您的 URDF。这有助于确保机器人状态发布器发布了正确的转换。
您可能已经注意到另一个窗口被启动了 - 这是关节状态发布器的图形用户界面。关节状态发布器是另一个 ROS 2 软件包,用于发布我们非固定关节的状态。您可以通过小 GUI 操作此发布器,并且关节的新位置将在 RVIz 中反映出来。滑动任意两个车轮的滑块将旋转这些关节。在关节状态发布器 GUI 中滑动滑块时,您可以在 RVIz 中看到这一操作。
我们在 Nav2 中不会与这个软件包互动太多,但如果你想了解更多关于关节状态发布器的信息,可以查看官方的关节状态发布器文档。 |
此时,你可能已经决定停止这个教程,因为我们已经实现了创建一个简单差分驱动机器人的目标。机器人状态发布器现在正在发布从URDF派生出的变换。这些变换现在可以被其他软件包(例如Nav2)用来获取有关你的机器人形状和结构的信息。然而,为了在仿真中正确使用这个URDF,我们需要物理属性,这样机器人才能像真实机器人一样对物理环境做出反应。可视化字段只用于可视化,不包括碰撞,所以你的机器人会直接穿过障碍物。我们将在下一节中介绍如何在我们的URDF中添加这些属性。
Adding Physical Properties
作为这个指南的一个附加部分,我们将修改当前的URDF,以包含我们机器人的某些运动学属性。这些信息可以被物理仿真器(如Gazebo)使用,来对我们的机器人在虚拟环境中的行为进行建模和模拟。
让我们首先定义包含我们项目中使用的几何基元的惯性属性的宏。将以下代码段放在URDF中的常量部分之后:
<!-- Define intertial property macros -->
<xacro:macro name="box_inertia" params="m w h d">
<inertial>
<origin xyz="0 0 0" rpy="${pi/2} 0 ${pi/2}"/>
<mass value="${m}"/>
<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)}"/>
</inertial>
</xacro:macro>
<xacro:macro name="cylinder_inertia" params="m r h">
<inertial>
<origin xyz="0 0 0" rpy="${pi/2} 0 0" />
<mass value="${m}"/>
<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)}"/>
</inertial>
</xacro:macro>
<xacro:macro name="sphere_inertia" params="m r">
<inertial>
<mass value="${m}"/>
<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)}"/>
</inertial>
</xacro:macro>
让我们从在base_link中使用 <collision>
标签添加碰撞区域开始。我们还将使用之前定义的 box_inertia
宏为base_link添加一些惯性属性。将以下代码段包含在URDF中base_link的 <link name="base_link">
标签内。
<collision>
<geometry>
<box size="${base_length} ${base_width} ${base_height}"/>
</geometry>
</collision>
<xacro:box_inertia m="15" w="${base_width}" d="${base_length}" h="${base_height}"/>
接下来,让我们对我们的轮子宏做同样的操作。将以下代码片段包含在我们URDF中轮子宏的 <link name="${prefix}_link">
标签内。
<collision>
<origin xyz="0 0 0" rpy="${pi/2} 0 0"/>
<geometry>
<cylinder radius="${wheel_radius}" length="${wheel_width}"/>
</geometry>
</collision>
<xacro:cylinder_inertia m="0.5" r="${wheel_radius}" h="${wheel_width}"/>
最后,让我们为球形转向轮添加类似的属性。在我们URDF中转向轮的 <link name="front_caster">
标签内包含以下内容。
<collision>
<origin xyz="0 0 0" rpy="0 0 0"/>
<geometry>
<sphere radius="${(wheel_radius+wheel_zoff-(base_height/2))}"/>
</geometry>
</collision>
<xacro:sphere_inertia m="0.5" r="${(wheel_radius+wheel_zoff-(base_height/2))}"/>
我们没有为 base_footprint 链接添加惯性或碰撞属性,因为这是一个虚拟且非物理的链接。 |
构建你的项目,然后使用前一节中的相同命令启动 RViz。
colcon build
. install/setup.bash
ros2 launch sam_bot_description display.launch.py
你可以通过在左侧面板的“RobotModel”下启用“Collision Enabled”来验证是否正确设置了碰撞区域(如果关闭“Visual Enabled”,可能会更容易看到)。在本教程中,我们定义了一个与我们的可视属性类似的碰撞区域。请注意,这并不总是适用,因为你可能会根据机器人的外观选择更简单的碰撞区域。
目前,我们必须到此为止,因为我们需要设置更多的组件才能开始在 Gazebo 中进行模拟我们的机器人。在这些设置指南的过程中,我们将回到这个项目,并最终在模拟部分看到我们的机器人在虚拟环境中移动。这项工作中缺失的主要组件是模拟你的机器人控制器所需的插件。我们将在适当的部分介绍它们并将其添加到此 URDF 中。
Conclusion
这就是全部。在本教程中,你成功地为一个简单的差分驱动机器人创建了一个 URDF。你还设置了一个 ROS 2 项目,启动了一个机器人发布节点,该节点使用你的 URDF 来发布机器人的变换。我们还使用 RViz 来可视化我们的机器人,以验证我们的 URDF 是否正确。最后,我们在 URDF 中添加了一些物理属性,以便为模拟做好准备。
随时可以将本教程用作你自己机器人的模板。记住,你的主要目标是从 base_link 到 sensor_frames 发布正确的变换。一旦这些设置好了,你就可以继续阅读我们的其他设置指南。
(
sensor_frames 是指机器人上的各种传感器框架或坐标系,用来描述不同传感器的位置、方向和相对关系。例如,激光传感器、相机、雷达等传感器都会有各自的坐标系(sensor frames),它们的相对位置和方向关系需要在整个机器人的坐标系中进行描述和转换。在导航和感知方面,这些传感器框架非常重要,因为它们提供了机器人周围环境的数据。
)
最终的sam_bot_description.urdf文件:
<?xml version="1.0"?>
<robot name="sam_bot" xmlns:xacro="http://ros.org/wiki/xacro">
<!-- Define robot constants -->
<xacro:property name="base_width" value="0.31"/>
<xacro:property name="base_length" value="0.42"/>
<xacro:property name="base_height" value="0.18"/>
<xacro:property name="wheel_radius" value="0.10"/>
<xacro:property name="wheel_width" value="0.04"/>
<xacro:property name="wheel_ygap" value="0.025"/>
<xacro:property name="wheel_zoff" value="0.05"/>
<xacro:property name="wheel_xoff" value="0.12"/>
<xacro:property name="caster_xoff" value="0.14"/>
<!-- Define intertial property macros -->
<xacro:macro name="box_inertia" params="m w h d">
<inertial>
<origin xyz="0 0 0" rpy="${pi/2} 0 ${pi/2}"/>
<mass value="${m}"/>
<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)}"/>
</inertial>
</xacro:macro>
<xacro:macro name="cylinder_inertia" params="m r h">
<inertial>
<origin xyz="0 0 0" rpy="${pi/2} 0 0" />
<mass value="${m}"/>
<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)}"/>
</inertial>
</xacro:macro>
<xacro:macro name="sphere_inertia" params="m r">
<inertial>
<mass value="${m}"/>
<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)}"/>
</inertial>
</xacro:macro>
<!-- Robot Base -->
<link name="base_link">
<visual>
<geometry>
<box size="${base_length} ${base_width} ${base_height}"/>
</geometry>
<material name="Cyan">
<color rgba="0 1.0 1.0 1.0"/>
</material>
</visual>
<collision>
<geometry>
<box size="${base_length} ${base_width} ${base_height}"/>
</geometry>
</collision>
<xacro:box_inertia m="15" w="${base_width}" d="${base_length}" h="${base_height}"/>
</link>
<!-- Robot Footprint -->
<link name="base_footprint"/>
<joint name="base_joint" type="fixed">
<parent link="base_link"/>
<child link="base_footprint"/>
<origin xyz="0.0 0.0 ${-(wheel_radius+wheel_zoff)}" rpy="0 0 0"/>
</joint>
<!-- Wheels -->
<xacro:macro name="wheel" params="prefix x_reflect y_reflect">
<link name="${prefix}_link">
<visual>
<origin xyz="0 0 0" rpy="${pi/2} 0 0"/>
<geometry>
<cylinder radius="${wheel_radius}" length="${wheel_width}"/>
</geometry>
<material name="Gray">
<color rgba="0.5 0.5 0.5 1.0"/>
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="${pi/2} 0 0"/>
<geometry>
<cylinder radius="${wheel_radius}" length="${wheel_width}"/>
</geometry>
</collision>
<xacro:cylinder_inertia m="0.5" r="${wheel_radius}" h="${wheel_width}"/>
</link>
<joint name="${prefix}_joint" type="continuous">
<parent link="base_link"/>
<child link="${prefix}_link"/>
<origin xyz="${x_reflect*wheel_xoff} ${y_reflect*(base_width/2+wheel_ygap)} ${-wheel_zoff}" rpy="0 0 0"/>
<axis xyz="0 1 0"/>
</joint>
</xacro:macro>
<xacro:wheel prefix="drivewhl_l" x_reflect="-1" y_reflect="1" />
<xacro:wheel prefix="drivewhl_r" x_reflect="-1" y_reflect="-1" />
<!-- Caster Wheel -->
<link name="front_caster">
<visual>
<geometry>
<sphere radius="${(wheel_radius+wheel_zoff-(base_height/2))}"/>
</geometry>
<material name="Cyan">
<color rgba="0 1.0 1.0 1.0"/>
</material>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0"/>
<geometry>
<sphere radius="${(wheel_radius+wheel_zoff-(base_height/2))}"/>
</geometry>
</collision>
<xacro:sphere_inertia m="0.5" r="${(wheel_radius+wheel_zoff-(base_height/2))}"/>
</link>
<joint name="caster_joint" type="fixed">
<parent link="base_link"/>
<child link="front_caster"/>
<origin xyz="${caster_xoff} 0.0 ${-(base_height/2)}" rpy="0 0 0"/>
</joint>
</robot>
在下一篇文章
在下一篇文章
在下一篇文章
在下一篇文章