这本书是比较经典的学习ROS的书,但是感觉对于初学者来说并不能作为第一本书籍来阅读,像我是个技术小白,一开始拿出这本书来读总是感觉云里雾里,包括现在读v2版本也有同样的感觉。
1. 此书的开篇回答了一个关键性的问题:学习ROS从哪里开始?
阶段1:学习基本概念和编程技巧
其中,在阶段1,可以参考ROS Wiki来查阅安装说明和初学者教程
同时,还需要掌握TF坐标变换以理解ROS如何处理不同的框架
使用ROS Answers提问
阶段2:使用ROS来控制机器人
在阶段2:参考本书,通过ROS使机器人完成一些特定任务。书中的代码可以应用在真实世界的机器人、旋转云台、用于检测人脸的摄像头。结至本书结尾,你控制的机器人将能够实现自主导航到你的家或办公室,响应你的口头命令,并结合视觉和运动控制来跟踪人脸或跟随房子周围的人。
2. 操作系统和ROS版本的选择
Ubuntu Linux是开源机器人基金会官方推荐的ROS的操作系统;并且Ubuntu免费、易于安装。
在Windows操作系统上安装Ubuntu,后续使用时进行操作系统的切换。
最好在一台实际的电脑上安装Ubuntu,这样运行起来更快速。
在VMware等虚拟机中安装Ubuntu并不是非常推荐,虽然这是个好方法,但因为在运行密集型程序如Rviz时,虚拟机可能会崩溃。
3. 使用Linux
搜索“Ubuntu教程”、学会使用Ubuntu终端。
4. ROS概念回顾
ROS中的核心实体称为节点。节点通常是用 Python 或 C++ 编写的小程序,执行一些相对简单的任务或流程。 节点可以相互独立地启动和停止,它们通过传递消息进行通信。 一个节点可以发布特定主题的消息或向其他节点提供服务。
例如,发布者节点可能会报告来自连接到机器人微控制器的传感器的数据。 /head_sonar 主题上的值为 0.5 的消息意味着传感器当前正在检测 0.5 米外的物体。(ROS 使用米来测量距离,使用弧度测量角度。)任何节点想要知道这个传感器的数值只需要订阅 /head_sonar 主题。为了使用这些值,订阅者节点定义了一个回调函数,每当有新消息到达订阅的主题时就会执行该回调函数。这个多久发生取决于发布者节点更新其消息的速率。
一个节点也可以定义一个或多个服务。当从另一个节点发送请求时,ROS 服务会产生一些行为或发回回复。一个简单的示例是打开或关闭 LED 的服务。一个更复杂的例子是一个服务,它在给定目标位置和机器人的起始姿势时返回移动机器人的导航计划。
更高级别的 ROS 节点将订阅许多主题和服务,以有用的方式组合结果,并可能发布消息或提供自己的服务。例如,我们将在本书后面开发的对象跟踪器节点订阅一组视频主题的摄像头消息,并发布另一个主题的移动命令,机器人的基本控制器读取这些移动命令以将机器人移动到适当的方向。
5. ROS的发布/订阅架构
使用 ROS 时,第一步是将所需的行为划分为独立的功能,这些功能可以由单独的节点处理。 例如,如果您的机器人使用网络摄像头或 Kinect 或 Xtion Pro 等深度摄像头,则一个节点将连接到摄像头并简单地发布图像和/或深度数据,以便其他节点可以使用它。 如果您的机器人使用移动底座,底座控制器节点将监听某个主题的运动命令并控制机器人的电机以相应地移动机器人。 这些只要所需的行为需要视觉和/或运动控制,节点就可以在许多不同的应用程序中使用而无需修改。
6. launch启动文件
为了运行应用程序,我们使用 ROS launch文件将整个节点集合作为一个组启动。 请记住,launch文件还可以包含其他launch文件,以便在新应用程序中更轻松地重用现有代码。
7. 单位和坐标系
在我们可以向我们的机器人发送移动命令之前,我们需要查看 ROS 中使用的测量单位和坐标系约定。
使用参考框架时,请记住 ROS 使用右手约定来定向坐标轴,如左图所示。 食指和中指指向 x 轴和 y 轴正方向,拇指指向 z 轴正方向。 绕轴旋转的方向由右侧所示的右手定则定义:如果您将拇指指向任何轴的正方向,则您的手指会向正旋转方向弯曲。 对于使用 ROS 的移动机器人,x 轴指向前方,y 轴指向左侧,z 轴指向上方。 根据右手定则,机器人绕 z 轴的正旋转是逆时针方向,而负旋转是顺时针方向。
ROS 使用公制,因此线速度总是以米每秒 (m/s) 为单位指定,角速度以弧度每秒 (rad/s) 为单位。 对于室内机器人(约 1.1 mph)而言,0.5 m/s 的线速度实际上是相当快的,而 1.0 rad/s 的角速度相当于 6 秒或 10 RPM 大约旋转一圈。 当有疑问时,慢慢开始并逐渐提高速度。 对于室内机器人,我倾向于将最大线速度保持在 0.2 m/s 或以下。
8. 运动控制级别
大多数运行 ROS 的差速驱动机器人在驱动电机或车轮上使用编码器。编码器记录相应车轮每转一圈一定数量的滴答声(通常是数百甚至数千个)。知道了轮子的直径和它们之间的距离,编码器刻度可以转换为以米为单位的行驶距离或以弧度为单位的旋转角度。为了计算速度,这些值简单地除以测量之间的时间间隔。
这种内部运动数据统称为里程计odometry,ROS 大量的使用它。如果您的机器人具有准确可靠的编码器,它会有所帮助,但车轮数据可以使用其他来源进行扩充。例如,最初的TurtleBot 使用单轴陀螺仪来提供对机器人旋转运动的额外测量,因为 iRobot Create 的编码器在旋转期间明显不准确。
无论使用多少个里程计数据源,机器人在世界上的实际位置和速度都可能(并且可能会)与里程计报告的值不同。差异程度将根据环境条件和里程计来源的可靠性而有所不同。
9. 使用ROS扭动和转动
ROS 使用 Twist 消息类型发布基本控制器使用的运动命令。 主题为 /cmd_vel,它是“命令速度”的缩写。 基本控制器节点订阅 /cmd_vel 主题并将 Twist 消息转换为实际转动车轮的电机信号。
要查看 Twist 消息的组成部分,请运行以下命令:
$ rosmsg show geometry_msgs/Twist
这将产生以下输出:
geometry_msgs/Vector3 linearfloat64 xfloat64 yfloat64 zgeometry_msgs/Vector3 angularfloat64 xfloat64 yfloat64 z
如您所见,Twist 消息由两个 Vector3 类型的子消息组成,一个用于 x、y 和 z 线速度分量,另一个用于 x、y 和 z 角速度分量。 线速度以米每秒为单位,角速度以弧度每秒为单位。 (1 弧度大约等于 57 度。)
对于在二维平面(例如地板)中运行的差动驱动机器人,我们只需要线性 x 分量和角度 z 分量。 这是因为这种类型的机器人只能沿其纵轴向前/向后移动,并且只能绕其垂直轴旋转。 换句话说,线性 y 和 z 分量始终为零(机器人不能横向或垂直移动),而角度 x 和 y 分量始终为零,因为机器人不能绕这些轴旋转。 全向机器人也将使用线性 y 分量,而飞行或水下机器人将使用使用所有六个组件。
Twist消息示例
假设我们希望机器人以每秒 0.1 米的速度直线前进。 这将需要具有线性值 x=0.1、y=0 和 z=0 以及角度值 x=0、y=0 和 z=0 的 Twist 消息。 如果您要在命令行中指定此 Twist 消息,则消息部分将采用以下形式:
'{linear: {x: 0.1, y: 0, z: 0}, angular: {x: 0, y: 0, z: 0}}'
请注意我们如何使用大括号来描述子消息,并使用冒号和空格(空格是必需的!)将组件名称与其值分隔开来。虽然这看起来像是很多打字,但我们很少会以这种方式控制机器人。 Twist 消息将使用其他 ROS 节点发送给机器人。
要以每秒 1.0 弧度的角速度逆时针旋转,所需的 Twist 消息将是:
'{linear: {x: 0, y: 0, z: 0}, angular: {x: 0, y: 0, z: 1.0}}'
如果我们将这两条信息结合起来,机器人会在向左转的同时向前移动。 生成的 Twist 消息将是:
'{linear: {x: 0.1, y: 0, z: 0}, angular: {x: 0, y: 0, z: 1.0}}'
与线性 x 值相比,角度 z 值越大,转弯越紧。
10. 使用Odometry
当我们要求机器人以一定的速度移动或旋转时,我们怎么知道它实际上是按照我们的要求做的? 例如,如果我们发布一条 Twist 消息以使机器人以 0.2 m/s 的速度向前移动,我们怎么知道机器人实际上不是以 0.18 m/s 的速度前进?我们怎么知道两个轮子甚至以相同的速度行驶?
机器人的基本控制器节点使用里程计和 PID 控制将运动请求转化为真实世界的速度。此过程的准确性和可靠性取决于机器人的内部传感器、校准程序的准确性和环境条件。 (例如,某些表面可能允许车轮轻微打滑,这会打乱编码器计数和行进距离之间的映射。)
机器人的内部里程计可以补充机器人位置和/或方向的外部测量。例如,可以使用壁挂式视觉标记,例如作为基准与 ROS 包 ar_pose、ar_kinect 或 ar_track_alvar 一起提供机器人在房间内的相当准确的定位。
类似的技术使用视觉特征匹配而不需要人工标记(ccny_rgbd_tools、rgbdslam、RTABMap),而另一个包(laser_scan_matcher)使用激光扫描匹配。除了其他形式的里程计,户外机器人通常使用 GPS 来估计位置。
出于本书的目的,我们将使用术语“Odometry”来表示内部位置数据。然而,无论人们如何测量里程计,ROS 都提供了一种消息类型来存储信息;即 nav_msgs/里程计。缩写的
Odometry 消息类型的定义如下所示:
Header headerstring child_frame_idgeometry_msgs/PoseWithCovariance posegeometry_msgs/TwistWithCovariance twist
在这里,我们看到 Odometry 消息由一个 Header、一个标识 child_frame_id 的字符串和两个子消息组成,一个用于 PoseWithCovariance,一个用于 TwistWithCovariance。
要查看定义的扩展版本,请运行以下命令:
$ rosmsg show nav_msgs/Odometry
这应该产生以下输出:
Header headeruint32 seqtime stampstring frame_idstring child_frame_idgeometry_msgs/PoseWithCovariance posegeometry_msgs/Pose posegeometry_msgs/Point positionfloat64 xfloat64 yfloat64 zgeometry_msgs/Quaternion orientationfloat64 xfloat64 yfloat64 zfloat64 wfloat64[36] covariancegeometry_msgs/TwistWithCovariance twistgeometry_msgs/Twist twistgeometry_msgs/Vector3 linearfloat64 xfloat64 yfloat64 zgeometry_msgs/Vector3 angularfloat64 xfloat64 yfloat64 zfloat64[36] covariance
PoseWithCovariance 子消息记录了机器人的位置和方向,而 TwistWithCovariance 组件为我们提供了我们已经看到的线速度和角速度。姿势和扭曲都可以用协方差矩阵来补充,该矩阵测量各种测量中的不确定性。
Header 和 child_frame_id 定义了我们用来测量距离和角度的参考框架。它还为每条消息提供时间戳,这样不仅知道我们所在地点,而且知道所在时间。按照惯例,ROS 中的里程计测量使用 /odom 作为父框架 ID,使用 /base_link(或 /base_footprint)作为子框架 ID。 /base_link 框架对应于机器人的真实物理部分,而 /odom 框架由封装在里程计数据中的平移和旋转定义。这些变换相对于 /odom 框架移动机器人。如果我们在 RViz 中显示机器人模型并将固定框架设置为 /odom 框架,机器人的位置将反映机器人“认为”它相对于其起始位置的位置。