ROS机器人入门-ROS通信机制(一)
简介
机器人是一种高度复杂的系统性实现,在机器人上可能集成各种传感器(雷达、摄像头、GPS…)以及运动控制实现,为了解耦合,在ROS中每一个功能点都是一个单独的进程,每一个进程都是独立运行的。更确切的讲,ROS是进程(也称为Nodes)的分布式框架。 因为这些进程甚至还可分布于不同主机,不同主机协同工作,从而分散计算压力。我们就需要ROS中的通信机制来实现不同进程间的数据交换。
ROS 中的基本通信机制主要有如下三种实现策略:
- 话题通信(发布订阅模式)
- 服务通信(请求响应模式)
- 参数服务器(参数共享模式)
话题通信
概述
话题通信是ROS中使用最频繁的一种通信机制,它是基于发布-订阅模式的一种通信机制,某个节点向话题发布消息时,其他订阅该话题的节点就会收到这条消息。话题通信一般用于不断更新的、少逻辑处理的数据传输场景,应用场景也十分广泛。
机器人在执行导航功能,使用的传感器是激光雷达,机器人会采集激光雷达感知到的信息并计算,然后生成运动控制信息驱动机器人底盘运动。
在上述场景中,就使用到了话题通信。
- 以激光雷达信息的采集处理为例,在 ROS 中有一个节点需要时时的发布当前雷达采集到的数据,导航模块中也有节点会订阅并解析雷达数据。
- 再以运动消息的发布为例,导航模块会根据传感器采集的数据时时的计算出运动控制信息并发布给底盘,底盘也可以有一个节点订阅运动信息并最终转换成控制电机的脉冲信号。
以此类推,像雷达、摄像头、GPS…. 等等一些传感器数据的采集,也都是使用了话题通信,换言之,话题通信适用于不断更新的数据传输相关的应用场景。
理论模型
角色
- master -> 管理者
- talker -> 发布者
- listener -> 订阅者
流程
master根据话题建立发布者和订阅者之间的连接。
0、talker在master中进行注册,提交话题以及RPC地址。
1、listener在master中注册关注的话题。
2、master将话题的RPC地址发送给listener
3、listener根据地址访问talker
4、talker响应自己的TCP地址
5、listener连接talker的TCP服务
6、talker与listener建立连接,talker发布消息,listener接收消息
注意事项
使用RPC与TCP两种通信方式;
talker注册发布话题与listener关注话题没有先后顺序(步骤0与步骤1);
talker与listener可以存在多个;
talker与listener一旦建立TCP连接,master就可以退出;
该实现模型在ROS已经封装
基本操作
编写发布订阅实现,要求发布方以10HZ(每秒10次)的频率发布文本消息并添加编号,订阅方订阅消息并将消息内容打印输出。
之后的项目学习均采用vscode编写,有关vscode的安装、配置以及使用参考ROS集成开发环境搭建。
C++实现
实现流程
- 发布方实现
- 订阅方实现
- 编辑配置文件
- 编译执行
发布方实现
- 新建cpp文件
- 引入头文件
- 初始化ROS节点
- 创建节点句柄
- 创建发布者对象
- 编写发布逻辑并发布数据
1 | #include "ros/ros.h" |
接下来就是编辑配置文件,编译执行。
可以在新的终端窗口使用rostopic echo 话题名
命令查看话题发布结果。
订阅方实现
- 新建cpp文件
- 引入头文件
- 初始化ROS节点
- 创建节点句柄
- 创建订阅对象
- 消息回调处理
- ros::spin() 跳回处理回调函数
1 | #include "ros/ros.h" |
编辑配置文件,编译执行。
Python实现
实现流程
- 发布方实现
- 订阅方实现
- python添加可执行权限(chmod)
- 编辑配置文件
- 编译执行
发布方实现
- 新建Python文件
- 导入消息类型包
- 初始化ROS节点
- 创建发布者对象
- 编写发布逻辑并发布数据
1 | #! /usr/bin/env python |
添加可执行权限、编辑配置文件,执行。
订阅方实现
新建Python文件
导入消息类型包
初始化ROS节点
创建订阅对象
消息回调处理
rospy.spin()跳回处理回调函数
1 | #! /usr/bin/env python |
添加可执行权限、编辑配置文件,执行。
注意事项
vscode 中的 main 函数 声明 int main(int argc, char const *argv[]){},默认生成 argv 被 const 修饰,需要去除该修饰符
订阅时,第一条数据丢失
原因: 发送第一条数据时, publisher 还未在 roscore 注册完毕
解决: 注册后,加入休眠 延迟第一条数据的发送。
C++:
ros::Duration(3.0).sleep();
Python:
rospy.sleep(3)
自定义消息
之前我们使用了ROS自带的字符串类型消息进行话题通信,除了字符串以外,ROS 中还 封装了一些原生的数据类型,比如:String、Int32、Int64、Char、Bool等,但在实际应用中,数据往往都是多种类型的组合体,因此需要自定义的消息类型来满足实际的应用场景。下面将开始学习自定义消息类型,并使用自定义的消息类型收发数据。
自定义消息具体的实现流程如下:
按照固定格式创建 msg 文件
编辑配置文件
编译生成可以被 Python 或 C++ 调用的中间文件
定义msg文件
在功能包下新建msg
目录,添加Person.msg文件
1 | string name |
编辑配置文件
package.xml
在package.xml中添加以下两条配置
1 | <build_depend>message_generation</build_depend> |
CMakeLists.txt
在find_package中添加message_generation
1 | find_package(catkin REQUIRED COMPONENTS |
在add_message_files中加入Person.msg
1 | add_message_files( |
添加generate_message配置
1 | generate_messages( |
catkin_package中添加依赖
1 | catkin_package( |
编译
编译后会生成消息调用的中间文件
C++:.../工作空间/devel/include/包名/xxx.h
Python:.../工作空间/devel/lib/python3/dist-packages/包名/msg
调用
vscode的配置
为了方便代码提示以及误抛异常,需要先配置 vscode。
C++
在c_cpp_properties.json 的 includepath中加入刚刚编译生成的头文件路径
Python
在settings.json的python.autoComplete.extraPaths中配置刚刚编译生成的包路径
导入头文件/包
C++
1
#include "包名/Person.h"
Python
1
from 包名.msg import Person
导入之后Person就可以作为一种消息类型在通信中使用了。