系列文章目录:
ROS开发(ubuntu)笔记·1_嘻·嘻的博客-CSDN博客
ROS通信机制~服务通信(server&client)·笔记3_嘻·嘻的博客-CSDN博客
话题通信·理论模型:(图源:2.1.1 话题通信理论模型 · Autolabor-ROS机器人入门课程《ROS理论与实践》零基础教程
基本操作:
首先,写发布者Talker要发布的信息的代码:
1. 在右击src创建新的功能包
2.然后导入依赖包
写完后Ctrl+Shift+B,正常的话,不会出现ERROR,最后有100%
topic就是要发布的信息的话题,红色横线处是队列的长度(发布信息的条数),这里直接用queue(队列)的概念去理解,即我设置是10,当我们输入了12条信息时,他会输出后面10条信息。
修改完编译一下,没有问题就不会有Error输出,并且输出100%
5.检查此时发布者Talker要发布的消息能否正常运行 具体步骤:
1.终端中输入roscore(执行ros的命令都要进行此操作)
2.打开新的终端,输入 cd 当前工作空间
3.source一下环境,然后输入rosrun 功能包 映射名称(正常会没有东西输出)
4.输入rostopic echo 话题名称 (我写的是"fang")结果就是一直打印hello hello
最后输入 Ctrl+C 就停止该程序运行
6. 在原来的代码上添加发布逻辑
#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::NodeHandle nh;
//4.创建发布者对象;
//泛型: 发布的消息类型
//参数1: 要发布到的话题
ros::Publisher pub =nh.advertise("fang",10);//就是队列的长度,例如有12条信息的话就输出后面10条
//5.编写发布逻辑并发布数据
//要求以10HZ的频率发布数据,并且文本后添加编号
//先创建被发布的消息
std_msgs::String msg;
//发布频率
ros::Rate rate(10);
//设置编号
int count =0;
//编写循环,循环中发布数据
ros::Duration(3.0).sleep(); //休眠函数,延迟第一条数据的发送
//原因:发送第一条数据时,publisher 还未在 roscore 注册完毕
//若没有,就算先启动订阅者代码,也可能导致前几条信息丢失
while(ros::ok())//ok()指只要该节点还活着,就循环
{ count++;
//实现字符串拼接数字
std::stringstream ss;
ss<<"hello --->"<
ros::Duration(3.0).sleep(); //休眠函数,延迟第一条数据的发送
原因:发送第一条数据时,publisher 还未在 roscore 注册完毕
若没有,就算先启动订阅者代码,也可能导致前几条信息丢失
接下来是订阅者listener的代码:
#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("listener订阅的数据:%s",msg->data.c_str());
}
int main(int argc,char *argv[])
{
setlocale(LC_ALL,"");
//2.初始化ROS节点
// 参数1和参数2 后期为节点传值会使用
// 参数3 是节点名称,是一个标识符,需要保证运行后,在 ROS 网络拓扑中唯一
ros::init(argc,argv,"listener");
//3.创建节点句柄
ros::NodeHandle nh;//该类封装了 ROS 中的一些常用功能
//4.创建订阅者对象;
ros::Subscriber sub =nh.subscribe("fang",10,doMsg);
//5.处理订阅到的数量
ros::spin();//循环读取接收的数据,并调用回调函数(doMsg)处理
return 0;
}
在第4点创建订阅者对象中,分析一下代码
ros::Subscriber sub=nh.subscribe("fang",10,doMsg);
首先是调用ros库中的订阅器Subscriber (类型)创建了一个sub,后面调用句柄nh的subscribe函数(创建订阅者对象)
括号中,"fang"是订阅者的话题,10是消息队列长度(最多缓存10条信息),当发布者发布了该话题时,就会调用doMsg函数(回调函数)
关于回调函数:一般函数是当程序进行到该函数处时,它就立刻实现,可以说是受我们自己控制的 而回调函数,当程序进行到该处时,它不会立刻执行,需要外界给予它一个信号才能运行,就像一个地雷,只有别人踩了才会爆,这就不受我们自己控制,而是受外界控制了。
我们传入的消息是 std_msgs::String类型的,而参数的类型是常量指针的引用,即ConstPtr &msg
ros::spin() 重复执行回调函数,即循环接收发布者发布的信息(与回调函数相对应)
还有一步:(记得修改CMakeLists.txt)与之前步骤相似
之后的操作也类似,先ctrl+shift+B看看有没有出问题,再打开终端,rosrun发布者和订阅者 运行如图:
在发布者的终端上输入Ctrl+C就可以停止发布,此时订阅者也停止接受信息了。
此时,我们可以通过计算图来看看两个节点之间的关系(新开一个终端输入rqt_graph )
进阶(自定义message):发送的信息可以包含各种数据类型
比如:int8, int16, int32, int64 (或者无符号类型: uint*),float32, float64,string,time,duration,
variable-length array[] and fixed-length array[C] ......
【1】定义msg文件(在功能包下创建msg目录,再创建一个后缀为msg的文件)
【2】配置编辑文件
1.在package.xml中添加 编译 依赖 与 执行 依赖
2.在CMakeLists.txt编辑 msg 相关配置
下面这步配置msg源文件
修改完后,一定要编译一下ctrl+shift+B,没出现ERROR后,就会在devel目录下的include文件夹下出现一个Person.h (Person 就是之前创建的Person.msg中的Person)
下一步:为了方便代码提示以及避免误抛异常,需要先配置 vscode,
1.将前面生成的 head 文件路径(在下图红色框中右击生成终端,输入pwd,复制路径)
2.配置进 c_cpp_properties.json 的 includepath属性(粘贴路径,可将后面具体功能包名改为**):
**意味着直接include目录下的所有内容
下面的操作就是实现了(代码与之前类似,注意头文件"plumbing_pub_sub/Person.h") 这里插一步,如果之前创建Person.msg时没有编译,而此时头文件已经用到了Person.h,则会发生逻辑上的错误,为了避免这种错误,需要在CMakeLists进行一步修改操作
(orag03_pub 和orag02_sub 是下面修改CMakeLists时,我设的映射名称 )
【发布者】
#include "ros/ros.h"
#include"plumbing_pub_sub/Person.h"
int main(int argc,char *argv[])
{
setlocale(LC_ALL,"");
ROS_INFO("这是消息发布方");
ros::init(argc,argv,"talker_person");
ros::NodeHandle nh;
ros::Publisher pub =nh.advertise("chatter_person",10);
//创建被发布的数据
plumbing_pub_sub::Person person;
person.name ="张三";
person.age=1;
person.height=1.73;
ros::Rate rate(1);
ros::Duration(3).sleep();
while(ros::ok())
{
// 修改数据
person.age += 1;
//数据发布
pub.publish(person);
ROS_INFO("发布的消息:%s,%d,%.2f", person.name.c_str(), person.age, person.height);
//休眠
rate.sleep();
ros::spinOnce();
}
}
【订阅者】
#include"ros/ros.h"
#include"plumbing_pub_sub/Person.h"
void doPerson(const plumbing_pub_sub::Person::ConstPtr &person)
{
ROS_INFO("订阅的人的消息:%s,%d,%.2f",person->name.c_str(),person->age,person->height);
}
int main(int argc,char*argv[])
{
setlocale(LC_ALL,"");
ros::init(argc,argv,"listener_person");
ros::NodeHandle nh;
ros::Subscriber sub=nh.subscribe("chatter_person",10,doPerson);
ros::spin();
return 0;
}
【修改CMakeLists】
这次笔记好长QAQ,能全部看完的真的很牛逼了^-^