零基础入门ros之话题通信基本操作篇
1.话题通信的简单介绍
- 话题通信是ROS中使用频率最高的一种通信模式,话题通信是基于发布订阅模式的,也即:发布方发布消息,订阅方接收并处理消息。发布方通过特定的话题来发布消息,订阅者选择性订阅自己感兴趣的主题来接受相关的消息。
通俗来说就像是你在B站上点了追番或追剧,等这部番剧(电视剧)更新了会给你推送消息一样,你不点追番他那边的消息照常发,只是你这边没有接收(订阅)而已,发布者和订阅者之间并没有直接的依赖关系。
1.1作用
- 用于不断更新的、少逻辑处理的数据传输场景。
2.话题通信的理论模型
- 就是由rosmaster通过话题建立发布者和订阅者之间的连接
- 先放图吓一下
- 这小图还有小英文(图中的词汇)根本看不懂->指博文作者本人
下面我来系统的讲解一下这个小图:
0.talker先在rosmaster进行注册自己的话题“bar”,foo(远程调用地址)
1.listener在rosmaster注册自己关注的话题
2.rosmaster把发布者和订阅者的话题进行比对,一样的就会把发布者的地址发送给listener
3.listener通过rosmaster给的地址远程访问talker
4.talker响应listener其中包含了talker的TCP地址
5.listener就可以用这个TCP地址去访问talker
6.talker就可以发布消息给listener
- **不懂?**没关系再看下面这个
master——主人这里可以理解为中介,起到桥梁的作用
图中个人信息相当于话题名,手机号相当于PRC(远程调用地址)
微信号相当于TCP地址
- 以上就是理论模型的介绍
3.话题通信基本操作
在模型实现中我们只需要关注三个点就好(很多已经被封装好了):
1.发布方
2.接收方
3.数据(此处为普通文本)
大致流程为:
- 编辑发布方
- 编辑订阅方
- 编辑配置文件(格外小心)
- 编译执行
3.1发布方实现
3.1.1编辑前准备
注:以下代码均以c++为例
- 在工作空间中创建功能包并添加依赖
- 添加依赖
- 创建cpp文件
这里名字自己想
3.1.2编辑发布方代码
- 包含头文件
// 1.包含头文件
#include "ros/ros.h"
#include "std_msgs/String.h" //普通文本类型的消息
#include <sstream>
- 编写框架
int main(int argc, char *argv[])
{
return 0;
}
- 设置编码(目的是解决中文乱码)
setlocale(LC_ALL,"");
- 初始化节点
//ROS 节点:命名(唯一)
// 参数1和参数2 后期为节点传值会使用
// 参数3 是节点名称,是一个标识符,需要保证运行后,在 ROS 网络拓扑中唯一
ros::init(argc,argv,"xiaoshuai");
- 创建节点句柄
ros::NodeHandle nh;//该类封装了 ROS 中的一些常用功能
- 创建发布者对象
//泛型: 发布的消息类型
//参数1: 要发布到的话题
//参数2: 队列中最大保存的消息数,超出此阀值时,先进的先销毁(时间早的先销毁)
ros::Publisher pub = nh.advertise<std_msgs::String>("fang",10);
队列其实就是一个缓冲区,当满了后先来的最先被销毁为后面的提供空间
advertise是一个创建发布者对象的一个函数
std_msgs::String这个就是泛型类型
- 编写发布逻辑并发布数据(以10hz为例)
//创建被发布的消息
std_msgs::String msg;
//发布频率
ros::Rate rate(10);
//设置编号
int count = 0;
//编写循环,循环中发布数据
while (ros::ok())
{
//使用 stringstream 拼接字符串与编号
std::stringstream ss;
ss << "hello ---> "<< count;
msg.data = ss.str();
//发布消息
pub.publish(msg);
//加入调试,打印发送的消息
ROS_INFO("发送的消息:%s",msg.data.c_str());
rate.sleep();
count++;//循环结束前,让 count 自增
}
3.1.3配置cmakelists文件
- 这里元素1是映射节点名称建议跟创建的文件名一样,元素2是cpp文件名
-
把上面的映射名称写在这就行了
-
编译(ctrl+shift+B)成功了就是配置的没什么问题
3.1.4测试一下代码执行
- 打开终端(Ctrl+Alt+T)输入命令
- 以上就是基本的发布方实现
3.2订阅方实现
- 前面的步骤和发布者一样我们直接从编辑代码开始
3.2.1编辑订阅者代码
- 框架和发布者也是一样的,我们来看创建订阅者对象
ros::Subscriber sub = nh.subscribe<std_msgs::String>("fang",10,doMsg);
//doMsg是一个回调函数用来处理订阅到的数据
- 处理订阅到的消息(编辑回调函数doMsg)
void doMsg(const std_msgs::String::ConstPtr &msg)
{
//通过msg获取并操作订阅到的数据
ROS_INFO("小美订阅的数据:%s",msg->data.c_str());
}
- 设置循环调用回调函数
ros::spin();//循环读取接收的数据,并调用回调函数处理
3.2.2编辑cmakelists
编译—看看是否通过
4.执行
- 打开终端输入
roscore
- 直接分割终端或者再打开两个,到工作空间下命令(cd+你自己的工作空间/)
- 配置环境
source ./devel/setup.bash
- 执行命令分别输入
rosrun + 你的功能包+你的发布者或订阅者文件
- 下面是结果:
先运行哪个都没有关系,运行成功后master就算关闭也没有什么影响
成功后可以用
rqt_graph
查看节点之间的关系
- 下面是cpp完整版代码
发布者
#include "ros/ros.h"
#include "std_msgs/String.h" //普通文本类型的消息
int main(int argc, char *argv[])
{
setlocale(LC_ALL,"");
//ROS 节点:命名(唯一)
// 参数1和参数2 后期为节点传值会使用
// 参数3 是节点名称,是一个标识符,需要保证运行后,在 ROS 网络拓扑中唯一
ros::init(argc,argv,"xiaoshuai");
ros::NodeHandle nh;//该类封装了 ROS 中的一些常用功能
//泛型: 发布的消息类型
//参数1: 要发布到的话题
//参数2: 队列中最大保存的消息数,超出此阀值时,先进的先销毁(时间早的先销毁)
ros::Publisher pub = nh.advertise<std_msgs::String>("fang",10);
//创建被发布的消息
std_msgs::String msg;
//发布频率
ros::Rate rate(10);
//设置编号
int count = 0;
//编写循环,循环中发布数据
while (ros::ok())
{
//使用 stringstream 拼接字符串与编号
std::stringstream ss;
ss << "hello ---> "<< count;
msg.data = ss.str();
//发布消息
pub.publish(msg);
//加入调试,打印发送的消息
ROS_INFO("发送的消息:%s",msg.data.c_str());
rate.sleep();
count++;//循环结束前,让 count 自增
}
return 0;
}
订阅者
#include "ros/ros.h"
#include "std_msgs/String.h" //普通文本类型的消息
#include <sstream>
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,"");
ros::init(argc,argv,"xiaomei");
ros::NodeHandle nh;
ros::Subscriber sub = nh.subscribe<std_msgs::String>("fang",10,doMsg);
//doMsg是一个回调函数用来处理订阅到的数据
ros::spin();
return 0;
}
- 主笔:@famgke
修正:dym踏雪寻梅,Kyuno_wh