ROS系列学习(二)通信机制
在学会如何创建工作空间和节点后,需要掌握的是如何建立这些节点之间的通信。ROS的基本通信机制主要有三种实现策略:
1.话题通信(发布订阅模式)
2.服务通信(请求响应模式)
3.参数服务器(参数共享模式)
文章目录
一、话题通信
话题通信是ROS中使用频率最高的一种通信模式,话题通信是基于发布订阅模式的,也即:一个节点发布消息,另一个节点订阅该消息。
1.概念:以发布订阅的方式实现不同节点之间数据交互的通信模式。
2.作用:用于不断更新的、少逻辑处理的数据传输场景。
3.实例:
1).实现最基本的发布订阅模型,发布方以固定频率发送一段文本,订阅方接收文本并输出。
2).实现对自定义消息的发布与订阅。
理论模型
话题通信的理论模型主要包括三个对象:
1.管理者(ROS Master)
2.发布者(Publisher)
3.订阅者(Subscriber)
ROS Master负责保管Publisher和Subscriber注册的信息,并匹配话题相同的Publisher与Subscriber,帮助二者建立连接,连接建立后,Publisher可以发布消息,且发布的消息会被Subscriber订阅。
整个通信过程的流程如下:
1.Publisher注册
publisher通过RPC向ROS Master发送注册信息,包括发布信息以及话题名称,ROS Master将publisher的注册信息注册到注册表中。
2.Subscriber注册
3.ROS Master匹配注册信息
ROS Master根据注册表内的信息匹配Publisher和Subscriber,并通过RPC向Subscriber发送Publisher的地址信息。
4.Subscriber向Publisher发送请求
Subscriber和Publisher成功匹配,Subscriber收到Publisher的地址后,向Publisher发送连接请求,传输订阅的话题名称,消息类型和通信协议(TCP/UDP)。
5.Publisher确定请求
Subscriber收到Subscriber的请求之后,通过RPC向Subscriber确认连接信息,并发送自身的TCP地址信息。
6.Subscriber和Publisher建立连接
Subscriber和Publisher根据TCP协议建立连接。
7.Publisher向Subscriber发布消息
注意1:上述实现流程中,前五步使用的RPC协议,最后两步使用的是TCP协议。
注意2:Publisher与 Subscriber的启动无先后顺序要求。
注意3:Publisher与 Subscriber都可以有多个。
注意4:Publisher与Subscriber连接建立后,不再需要ROS Master。也即,即便关闭ROS Master,Publisher与Subscriber照常通信。
话题通信的代码实现
1.Publisher(C++)
*/
// 1.包含头文件
#include "ros/ros.h"
#include "std_msgs/String.h" //普通文本类型的消息
#include
int main(int argc, char *argv[])
{
//设置编码
setlocale(LC_ALL,"");
//2.初始化 ROS 节点:命名(唯一)
// 参数1和参数2 后期为节点传值会使用
// 参数3 是节点名称,是一个标识符,需要保证运行后,在 ROS 网络拓扑中唯一
ros::init(argc,argv,"talker");
//3.实例化 ROS 句柄
ros::NodeHandle nh;//该类封装了 ROS 中的一些常用功能
//4.实例化 发布者 对象
//泛型: 发布的消息类型
//参数1: 要发布到的话题
//参数2: 队列中最大保存的消息数,超出此阀值时,先进的先销毁(时间早的先销毁)
ros::Publisher pub = nh.advertise("chatter",10);
//5.组织被发布的数据,并编写逻辑发布数据
//数据(动态组织)
std_msgs::String msg;
// msg.data = "你好啊!!!";
std::string msg_front = "Hello 你好!"; //消息前缀
int count = 0; //消息计数器
//逻辑(一秒10次)
ros::Rate r(1);
//节点不死
while (ros::ok())
{
//使用 stringstream 拼接字符串与编号
std::stringstream ss;
ss << msg_front << count;
msg.data = ss.str();
//发布消息
pub.publish(msg);
//加入调试,打印发送的消息
ROS_INFO("发送的消息:%s",msg.data.c_str());
//根据前面制定的发送贫频率自动休眠 休眠时间 = 1/频率;
r.sleep();
count++;//循环结束前,让 count 自增
//暂无应用
ros::spinOnce();
}
return 0;
}
2.Subscriber(C++)
/*
需求: 实现基本的话题通信,一方发布数据,一方接收数据,
实现的关键点:
1.发送方
2.接收方
3.数据(此处为普通文本)
消息订阅方:
订阅话题并打印接收到的消息
实现流程:
1.包含头文件
2.初始化 ROS 节点:命名(唯一)
3.实例化 ROS 句柄
4.实例化 订阅者 对象
5.处理订阅的消息(回调函数)
6.设置循环调用回调函数
*/
// 1.包含头文件
#include "ros/ros.h"
#include "std_msgs/String.h"
void doMsg(const std_msgs::String::ConstPtr& msg_p){
ROS_INFO("我听见:%s",msg_p->data.c_str());
// ROS_INFO("我听见:%s",(*msg_p).data.c_str());
}
int main(int argc, char *argv[])
{
setlocale(LC_ALL,"");
//2.初始化 ROS 节点:命名(唯一)
ros::init(argc,argv,"listener");
//3.实例化 ROS 句柄
ros::NodeHandle nh;
//4.实例化 订阅者 对象
ros::Subscriber sub = nh.subscribe("chatter",10,doMsg);
//5.处理订阅的消息(回调函数)
// 6.设置循环调用回调函数
ros::spin();//循环读取接收的数据,并调用回调函数处理
return 0;
}
3.配置 CMakeLists.txt
add_executable(Hello_pub src/Hello_pub.cpp)
add_executable(Hello_sub src/Hello_sub.cpp)
target_link_libraries(Hello_pub ${catkin_LIBRARIES}s)
target_link_libraries(Hello_sub ${catkin_LIBRARIES}