ROS通信机制
机器人是一种高度复杂的系统性实现,在机器人上可能集成各种传感器(雷达、摄像头、GPS...)以及运动控制实现,为了解耦合,在ROS中每一个功能点都是一个单独的进程,每一个进程都是独立运行的。更确切的讲,ROS是进程(也称为Nodes)的分布式框架。
因为这些进程甚至还可分布于不同主机,不同主机协同工作,从而分散计算压力。不过随之也有一个问题: 不同的进程是如何通信的?也即不同进程间如何实现数据交换的?在此我们就需要介绍一下ROS中的通信机制了。
ROS 中的基本通信机制主要有如下三种实现策略:
- 话题通信(发布订阅模式)
- 服务通信(请求响应模式)
- 参数服务器(参数共享模式)
话题通信
话题通信是ROS中使用频率最高的一种通信模式,话题通信是基于发布订阅模式的,也即:一个节点发布消息,另一个节点订阅该消息。话题通信的应用场景也极其广泛,比如下面一个常见场景:
机器人在执行导航功能,使用的传感器是激光雷达,机器人会采集激光雷达感知到的信息并计算,然后生成运动控制信息驱动机器人底盘运动。
在上述场景中,就不止一次使用到了话题通信。
- 以激光雷达信息的采集处理为例,在 ROS 中有一个节点需要时时的发布当前雷达采集到的数据,导航模块中也有节点会订阅并解析雷达数据。
- 再以运动消息的发布为例,导航模块会根据传感器采集的数据时时的计算出运动控制信息并发布给底盘,底盘也可以有一个节点订阅运动信息并最终转换成控制电机的脉冲信号。
以此类推,像雷达、摄像头、GPS.... 等等一些传感器数据的采集,也都是使用了话题通信,换言之,话题通信适用于不断更新的数据传输相关的应用场景。
概念
以发布订阅的方式实现不同节点之间数据交互的通信模式
作用
用于不断更新的、少逻辑处理的数据传输场景。
理论模型
话题通信实现模型是比较复杂的,该模型如下图所示,该模型中涉及到三个角色:
- ROS Master (管理者)
- Talker (发布者)
- Listener (订阅者)
ROS Master 负责保管 Talker 和 Listener 注册的信息,并匹配话题相同的 Talker 与 Listener,帮助 Talker 与 Listener 建立连接,连接建立后,Talker 可以发布消息,且发布的消息会被 Listener 订阅。
举一个有趣的例子以帮助理解,翠花和狗子相亲为例,如下图所示:
整个流程由以下步骤实现:
0.Talker注册
Talker启动后,会通过RPC在 ROS Master 中注册自身信息,其中包含所发布消息的话题名称。ROS Master 会将节点的注册信息加入到注册表中。相当于狗子提交自身信息话题(房子)给媒婆
1.Listener注册
Listener启动后,也会通过RPC在 ROS Master 中注册自身信息,包含需要订阅消息的话题名。ROS Master 会将节点的注册信息加入到注册表中。相当于翠花提交自身信息需求(房子)
2.ROS Master实现信息匹配
ROS Master 会根据注册表中的信息匹配Talker 和 Listener,并通过 RPC 向 Listener 发送 Talker 的 RPC 地址信息。相当于二者房子话题达成一致,RPC相当于狗子的手机号
3.Listener向Talker发送请求
Listener 根据接收到的 RPC 地址,通过 RPC 向 Talker 发送连接请求,传输订阅的话题名称、消息类型以及通信协议(TCP/UDP)。相当于翠花打电话给狗子
4.Talker确认请求
Talker 接收到 Listener 的请求后,也是通过 RPC 向 Listener 确认连接信息,并发送自身的 TCP 地址信息。TCP相当于微信号,也即狗子通过电话把自己微信号给翠花
5.Listener与Talker件里连接
Listener 根据步骤4 返回的消息使用 TCP 与 Talker 建立网络连接。翠花加狗子微信
6.Talker向Listener发送消息
连接建立后,Talker 开始向 Listener 发布消息。狗子通过微信给翠花发消息。
注意
注意1:上述实现流程中,前五步使用的 RPC协议(电话),最后两步使用的是 TCP 协议(微信)
注意2: Talker 与 Listener 的启动无先后顺序要求
注意3: Talker 与 Listener 都可以有多个(可以和多个人相亲)
注意4: Talker 与 Listener 连接建立后,不再需要 ROS Master。也即,即便关闭ROS Master,Talker 与 Listern 照常通信。
话题通信基本操作A(C++)
需求:
编写发布订阅实现,要求发布方以10HZ(每秒10次)的频率发布文本消息,订阅方订阅消息并将消息内容打印输出。
分析:
在模型实现中,ROS master 不需要实现,而连接的建立也已经被封装了,需要关注的关键点有三个:
- 发布方
- 接收方
- 数据(此处为普通文本)
流程:
- 编写发布方实现
- 编写订阅方实现
- 编辑配置文件
- 编译并执行
0、创建功能包 会生成include src CmakeLists.txt package.xml四个文件
cd ~/catkin_ws/src
catkin_create_pkg plumbing_pub_sub std_msgs rospy roscpp
然后在plumbing_pub_sub/src路径下新建demo01_pub.cpp、demo02_sub.cpp的.txt文件
终端中输入
touch demo01_pub.cpp
touch demo02_sub.cpp
1、发布方代码:
#include "ros/ros.h"
#include "std_msgs/String.h" //普通文本类型的消息
#include <sstream>
/*
发布方实现:
1、包含头文件;
ROS中文本类型 ---> std_msgs/String.h
2、初始化ROS节点 命名(唯一)
3、创建节点句柄
4、创建发布者对象
5、编写发布逻辑并发布数据
*/
int main(int argc,char *argv[])
{
//解决乱码问题
setlocale(LC_ALL,"");
//2、初始化ROS节点
ros::init(argc,argv,"erGouZi");
//3、创建节点句柄
ros::NodeHandle nh;
// 4、创建发布者对象
//参数1: 要发布到的话题 此处是fang
//参数2: 队列中最大保存的消息数,超出此阀值时,先进的先销毁(时间早的先销毁) 此处队列长度10
ros::Publisher pub = nh.advertise<std_msgs::String>("fang",10);
// 5、编写发布逻辑并发布数据
//要求以10HZ的频率发布数据,并且文本后添加编号
//先创建被发布的消息
std_msgs::String msg;
//发布频率
ros::Rate rate(10);
//设置编号
int count = 0;
//编写循环,循环中发布数据
while (ros::ok())
{
count++;
//实现字符串拼接数字 stringstream
std::stringstream ss;
ss <<"hello ---> " << count;
//msg.data = "hello";
msg.data = ss.str();
pub.publish(msg);
//添加日志:
ROS_INFO("send data:%s",ss.str().c_str());
//根据前面制定的发送贫频率自动休眠 休眠时间 = 1/频率;
rate.sleep();
}
}
2、订阅方
#include "ros/ros.h"
#include "std_msgs/String.h"
/*
订阅方实现:
1、包含头文件;
ROS中文本类型 ---> std_msgs/String.h
2、初始化ROS节点
3、创建节点句柄
4、创建订阅者对象
5、处理订阅到的数据
6、spin()函数 循环调用回调函数
*/
void doMsg(const std_msgs::String::ConstPtr &msg)
{
//通过msg获取并操作订阅到的数据
ROS_INFO("翠花订阅的数据:%s",msg->data.c_str());
}
int main(int argc, char *argv[])
{
setlocale(LC_ALL,"");
//2、初始化ROS节点
ros::init(argc,argv,"cuiHua");
// 3、创建节点句柄
ros::NodeHandle nh;
//4、创建订阅者对象 注意Subscriber首字母大写
ros::Subscriber sub = nh.subscribe("fang",10,doMsg);
//5、处理订阅到的数据
//6.设置循环调用回调函数
ros::spin();//循环读取接收的数据,并调用回调函数处理
return 0;
}
3、配置 CMakeLists.txt
//在build下面附近添加如下代码
add_executable(demo01_pub src/demo01_pub.cpp)
add_executable(demo02_sub src/demo02_sub.cpp)
//尽量将demo01_pub与demo01_pub.cpp命名保持一致
//添加依赖库
target_link_libraries(demo01_pub
${catkin_LIBRARIES}
)
target_link_libraries(demo02_sub
${catkin_LIBRARIES}
)
4、执行
1.启动 roscore; 必须先启动
2.启动发布节点;
3.启动订阅节点。
5、演示效果图
6、注意
每次编写及更改代码后必须先catkin_make后再roscore,而后运行其他节点
7、rqt_graph 查看节点信息