《ROS机器人编程》
第4章 ROS重要概念这章内容真的又多又重要,全部是基本概念,还有功能包节点的构建方法。我看了大概两个星期(当然中间有将近一个星期补惯导报告去了),才把这一整章整理完。
本章主要介绍了ROS的基本概念(节点、功能包、消息等等),然后介绍了ROS的文件系统,最后构建了一个最简单最基础的系统实例。
第4章 ROS的重要概念
1 常用的ROS术语
- 主节点(master)
- 负责节点到节点的连接和消息通信,类似于名称服务器(NameServer)。
- 主节点使用XML远程过程调用(XMLRPC,XML-Remote Procedure Call)与节点进行通信。
- 节点(node)
- 节点是指在ROS中运行的最小处理器单元。
- 节点使用XMLRPC与主站进行通信。
- 节点之间的连接请求和响应使用XMLRPC,消息通信使用TCPROS,因为它是节点和节点之间的直接通信,与主节点无关。
- 功能包(package)
- ROS应用程序以功能包为单位开发.
- 功能包包括至少一个以上的节点或拥有用于运行其他功能包的节点的配置文件。
- 元功能包(metapackage)—— 具有共同目的的功能包的集合。
- 服务(service)
- 服务客户端(service client)与服务服务器(service server)之间的同步双向消息通信
- 服务服务器,以请求作为输入,以响应作为输出
- 服务客户端,以请求作为输出,以响应作为输入
- 请求和响应都是消息
- 动作(action)
- 动作客户端(action client)和动作服务器(action server)之间进行异步双向消息通信
- 动作服务器,从目标作为输入,以结果和反馈值作为输出
- 动作客户端,以目标作为输出,以结果和反馈值作为输入
- 目标(goal)和结果(result)对应于请求和响应
命令 | 功能 | URI地址与端口 |
---|---|---|
roscore | 运行ROS主节点 | 使用ROS_MASTER_URI变量中列出的URI地址和端口(默认为本地IP+端口11311) |
rosrun | 基本运行命令,用于在功能包中运行一个节点 | 将存储在当前运行节点的ROS_HOSTNAME环境变量作为URI地址,端口为任意的固有值 |
roslaunch | 运行多个节点 | 使用*.launch文件来设置可执行节点,基于可扩展标记语言(XML) |
2 消息通信
节点之间有3中消息通信方式:
- 单向消息发送/接收方式的话题(topic)
- 双向消息请求/响应方式的服务(service)
- 双向消息目标(goal)/结果(result)/反馈(feedback)方式的动作(action)
2.1 话题(topic)
- 话题是单向的,适用于需要连续发送消息的传感器数据,因为它们通过一次的连接连续发送和接收消息。
- 单个发布者可以与多个订阅者进行通信。
- 一个订阅者可以在单个话题上与多个发布者进行通信。
- 话题通信方法是一种异步方法
2.2 服务(service)
- 服务是一次性消息通信,当服务的请求和响应完成时,两个连接的节点将被断开。
- 服务通信方法是一种消息同步方法。
2.3 动作(action)
- 动作消息通信,服务器收到请求后直到响应所需的时间较长,且需要中途反馈值。
- 目标(goal)、结果(result)、反馈(feedback)。
节点同时向主节点注册自己的信息,并从主节点获取其他节点希望通过主节点访问的节点的信息。
然后,节点和节点直接连接进行消息通信。
2.4 参数(parameter)
- 参数可以认为是节点中使用的全局变量。
- 可以指定设置值,有需要时也可以从外部读取或写入参数。
3 消息
消息由两种类型组成:
- 字段类型(fieldtype),填入ROS数据类型
- 字段名称(fieldname),填入指示数据的名称
消息文件分为三类:
- msg文件 ——> 用于话题
- srv文件 ——> 用于服务
- action文件 ——> 用于动作
msg文件只包含一个字段类型和一个字段名称。
Vector3 linear
Vector3 angular
srv文件使用三个连字符(—)作为分隔符,上层消息是服务请求消息,下层消息是服务响应消息。
sensor_msgs/CameraInfo camera_info
---
bool success
string status_message
action文件在两个地方使用三个连字符(—)作为分隔符,第一部分是goal消息,第二部分是result消息,第三部分是feedback消息。
geometry_msgs/PoseStamped start_pose
geometry_msgs/PoseStamped goal_pose
---
geometry_msgs/PoseStamped result_pose
---
float32 percent_complete
4 名称(name)
有一说一,名称这一节的代码我确实没怎么看懂,特别是pdf里下划线部分的排版也很不清晰,因此参考了下面这个教程:
ROS复习笔记之——名称
ROS服务中使用的节点、话题、消息以及ROS中使用的参数都具有唯一的名称(name)话题名称分为相对的方法、全局方法和私有方法,如下所示。
int main(int argc, char **argv) // 节点主函数
{
ros::init(argc, argv, "node1"); // 初始化节点
ros::NodeHandle nh; // 声明节点句柄
// 声明发布者,话题名 = bar
ros::Publisher node1_pub = nh.advertise<std_msg::Int32>("bar", 10);
此处,节点的名称是/node1。如果用一个没有任何字符的相对形式的bar来声明一个发布者,这个话题将和/bar具有相同的名字。如果以如下所示使用斜杠(/)字符用作全局形式,话题名也是/bar。
所以下面两句是等价的:
ros::Publisher node1_pub = nh.advertise<std_msg::Int32>("bar", 10);
ros::Publisher node1_pub = nh.advertise<std_msg::Int32>(“/bar”, 10);
如果使用波浪号(~)字符将其声明为私有,则话题名称将变为/node1/bar。
ros::Publisher node1_pub = nh.advertise<std_msg::Int32>(“~bar”, 10);
/wg 意味着命名空间的修改,但我也不知道 foo 是什么意思(苦涩),留待考证。
若存在两个摄像头,简单地两次执行相关节点将导致之前执行的节点因ROS的性质而终止,因为ROS必须具有唯一的名称。用户可以在运行时更改节点的名称,而不需要运行额外的程序或更改源代码。方法包括命名空间(namespace)和重新映射(remapping)。
例如,当有前、左、右,三个摄像头,且当多次执行同名的节点时,由于节点名重复,该节点将被重复执行,因此节点会被停止。为了避免这种情况,可以采取用同一个名称运行多个不同的节点的方法。下面的命令示例中,name选项使用了两个下划线(__)。
rosrun camera_package camera_node __name:=front _device:=/dev/video0
rosrun camera_package camera_node __name:=left _device:=/dev/video1
rosrun camera_package camera_node __name:=right _device:=/dev/video2
rosrun rqt_imgae_view rqt_imgae_view
附加地说明,选项__ns、__name、__log、__ip、__hostname和__master是运行节点时使用的特殊选项。
我们在话题名称选项中使用了一个下划线(_),如果它是private名称,则在现有名称前加上一个下划线。
5 坐标变换(TF)
在机器人编程中,经过坐标变换的机器人的关节(或带有旋转轴的车轮)和物体的位置是非常重要的,在ROS中将它以坐标变换(Transform)来表达。
位置(position)和方向(direction),统称为姿态(pose)。
- 位置由x、y、z这3个矢量表示
- 方向是用四元数(quaternion)x、y、z、w表示
TF格式:
Header header
string child_frame_id
Transform transform
Header用于记录转换的时间
消息child_frame_id来表示下位的坐标
表达坐标的转换值:
- 位置:transform.translation.x / transform.translation.y / transform.translation.z
- 方向:transform.rotation.x / transform.rotation.y / transform.rotation.z / transform.rotation.w
6 客户端库
由于ROS需要支持具有多重目的特点的机器人,因此ROS提供面向多种语言的便利接口。各个节点可以用各自的语言来编写,而节点间通过消息通信交换信息。
其中,允许用各自的语言编写的软件模块就是客户端库(client library)。
常用且具有代表性的库有支持C++的roscpp、支持Python的rospy、支持LISP的roslisp、支持Java的rosjava 等等。
7 异构设备间的通信
ROS支持异构设备间的通信,节点间的通信不受各节点所在的ROS底层的操作系统的种类的影响,也不受编程语言的影响。
8 文件系统
ROS的文件系统分为安装目录和用户工作目录。
8.1 安装目录
ROS 安装目录路径:/opt/ros/kinetic
文件组织结构:
- /bin 可执行的二进制文件
- /etc 与ROS和catkin相关的配置文件
- /include 头文件
- /lib 库文件
- /share ROS功能包
- env.* 配置文件
- setup.* 配置文件
8.2 工作目录
文件组织结构:
- /build 构建相关的文件
- /devel msg、srv头文件、用户包库、可执行文件
- /src 用户功能包
9 构建系统
- ROS的构建系统默认使用CMake(Cross Platform Make)
- 在ROS中,CMake被修改为适合于ROS的“catkin”构建系统
- 使用CMake的是为了在多个平台上构建 ROS功能包
9.1 创建功能包
创建ROS功能包命令
$ catkin_create_pkg [功能包名称] [依赖功能包1] [依赖功能包n]
例子:
创建一个名称为“my_first_ros_pkg”的功能包,用“std_msgs”和“roscpp”作为前面命令格式中的依赖功能包的选项。
$ catkin_create_pkg my_first_ros_pkg std_msgs roscpp
9.2 修改功能包配置文件
package.xml 是一个包含功能包信息的XML文件,包括功能包名称、作者、许可证和依赖功能包。
package.xml 语句说明:
9.3 修改构建配置文件
功能包目录中的 CMakeLists.txt 文件描述构建环境,在这个文件中设置可执行文件的创建、依赖包优先构建、连接器(linker)的创建等等。
CMakeLists.txt 文件中的指令选项就太多了,参见《ROS机器人编程》P84–P88,这里就不逐一贴上来了。
9.4 运行一个节点实例
首先利用 gedit 编写一个节点源代码文件 hello_world_node.cpp,在CMakelists.txt文件的可执行文件创建部分(add_executable)中,进行以下设置:
引用功能包的src目录中的hello_world_node.cpp源代码来生成hello_world_node可执行文件。
add_executable(hello_world_node src/hello_world_node.cpp)
然后,更新ROS功能包打配置文件(不是必选操作),移动到工作目录下进行catkin构建。
$ rospack profile
$ cd ~/catkin_ws && catkin_make
接着运行节点,打开一个终端窗口运行roscore,运行roscore后,ROS中的所有节点都可用。
$ roscore
最后,打开一个新的终端窗口,运行命令节点。
$ rosrun my_first_ros_pkg hello_world_node
运行打过程中遇到一个问题,找不到构建的功能包:
我参考了下面这个博客,先运行setup.bash 文件,问题迎刃而解。
ROS常见问题1——找不到包
至此,第四章就学习完成啦!