前言
前面ROS很多都忘记了 ,现在来重新回顾一下内容
ROS 中的基本通信机制主要有如下三种实现策略:
-
话题通信(发布订阅模式)
-
服务通信(请求响应模式)
-
参数服务器(参数共享模式)
-
Action通信
ROS常用API
话题通信(发布订阅模式)
以发布订阅的方式实现不同节点之间数据交互的通信模式。
话题通信实现模型是比较复杂的,该模型如下图所示,该模型中涉及到三个角色:
- ROS Master (管理者)
- Talker (发布者)
- Listener (订阅者)
ROS Master 负责保管 Talker 和 Listener 注册的信息,并匹配话题相同的 Talker 与 Listener,帮助 Talker 与 Listener 建立连接,连接建立后,Talker 可以发布消息,且发布的消息会被 Listener 订阅
ROS中的通信方式中,topic是常用的一种。对于实时性、周期性的消息,使用topic来传输是最佳的选择。topic是一种点对点的单向通信方式,这里的“点”指的是node,也就是说node之间可以通过topic方式来传递信息。topic要经历下面几步的初始化过程:首先,publisher节点和subscriber节点都要到节点管理器进行注册,然后publisher会发布topic,subscriber在master的指挥下会订阅该topic,从而建立起sub-pub之间的通信。注意整个过程是单向的。
其中在话题通信中值得注意的是
- Subscriber接收消息会进行处理,一般这个过程叫做回调(Callback)。所谓回调就是提前定义好了一个处理函数(写在代码中),当有消息来就会触发这个处理函数,函数会对消息进行处理
- ROS是一种分布式的架构,一个topic可以被多个节点同时发布,也可以同时被多个节点接收。
话题通信的基本操作
代码
//topic_pub1.cpp
#include"ros/ros.h"
#include<std_msgs/String.h>
#include<sstream>
int main(int args ,char *argv[])
{
//设置编码 会在终端输出信息 防止乱码
setlocale(LC_ALL,"");
//初始化 ROS 节点:命名(唯一) 节点命名不能重复
// 参数1和参数2 后期为节点传值会使用
// 参数3 是节点名称,是一个标识符,需要保证运行后,在 ROS 网络拓扑中唯一
ros::init(args,argv,"talker");
//实例化句柄该类封装了 ROS 中的一些常用功能
ros::NodeHandle nh;
//实例化 发布者 对象
//泛型: 发布的消息类型
//参数1: 要发布到的话题
//参数2: 队列中最大保存的消息数,超出此阀值时,先进的先销毁(时间早的先销毁)
//n.advertise通过NodeHandle的对象n告诉ROS系统我要创建一个可以发布信息的对象了。这个信息是什么类型的呢?
//<std_msgs::String>告诉ROS我要发布的是标准信息中的String类型。
//那些信息叫啥名字呢?名字叫chatter。这个chatter就是我们之前提到的topic!
ros::Publisher pub=nh.advertise<std_msgs::String>("chatter",10);
std_msgs::String msg;
std::string msg_fromt="day day up";
int count =0; //计数器
//逻辑(一秒10次) 可以制定循环的频率
//程序如果在不断地发布信息,那么有时候我会想控制发布的信息的快慢,这行表示你希望你发布信息的速度为10Hz。
//这个函数要和ros::Rate r.sleep()配合使用才能达到控制速度目的
ros::Rate r(10);
while(ros::ok())
{
std::stringstream ss;
ss<<msg_fromt<<count;
msg.data=ss.str();
//发布消息
pub.publish(msg);
//输出消息
ROS_INFO("发送的消息:%s",msg.data.c_str());
r.sleep();
count++;
//回调函数
ros::spinOnce();
}
return 0;
}
//topic_sub1.cpp
// 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());
}
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<std_msgs::String>("chatter",10,doMsg);
//5.处理订阅的消息(回调函数)
// 6.设置循环调用回调函数
ros::spin();//循环读取接收的数据,并调用回调函数处理
return 0;
}
话题通信自定义msg
ROS 中通过 std_msgs 封装了一些原生的数据类型,比如:String、Int32、Int64、Char、Bool、Empty.... 但是,这些数据一般只包含一个 data 字段,结构的单一意味着功能上的局限性,当传输一些复杂的数据,比如: 激光雷达的信息... std_msgs 由于描述性较差而显得力不从心,这种场景下可以使用自定义的消息类型
其中 ros::spin() 是进入了循环执行回调函数,而 r