前言
关于发布方用到的几个零碎的知识点
1、初始化ROS节点
//2.初始化 ROS 节点
ros::init(argc,argv,"talker");//其定义如下:name必须唯一
/** @brief ROS initialization function.
*
* This function will parse any ROS arguments (e.g., topic name
* remappings), and will consume them (i.e., argc and argv may be modified
* as a result of this call).
*
* Use this version if you are using the NodeHandle API
*
* \param argc
* \param argv
* \param name Name of this node. The name must be a base name, ie. it cannot contain namespaces.
* \param options [optional] Options to start the node with (a set of bit flags from \ref ros::init_options)
* \throws InvalidNodeNameException If the name passed in is not a valid "base" name
*
*/
ROSCPP_DECL void init(int &argc, char **argv, const std::string& name, uint32_t options = 0);
2、 句柄:句柄的意义和作用以及句柄和指针的区别 - 菜鸟开车 - 博客园 (cnblogs.com)
相当于为他取了个简单的名字,或者专业一点:句柄就是像一个指向指针的指针
3、创建发布对象的参数含义
//4.创建发布者对象
ros::Publisher pub = nh.advertise<std_msgs::String>("chatter",10);
改名 发布者 发布的消息类型 话题 消息最多看到几个
4、消息发布这里涉及到了C++里的重载,重载函数就是多个函数具有相同的函数标识名,而参数类型或参数个数不同,函数调用时,编译器根据参数的类型以及参数的个数来区分调用的哪一个函数。下面我们用到的消息发布,他的定义里有要求发布的消息是字符串型。
消息发布:
pub.publish(msg);//用这个发布者对象发布消息,看定义,也和我们之前的创建发布者那边有关,他只能发布string的
下面是他的定义
void ros::Publisher::publish<std_msgs::String>(const std_msgs::String &message) const
还有 3 个重载
Publish a message on the topic associated with this Publisher.
二、订阅方实现
1、创建订阅方C++文件
2、编程
流程如下(与发布方区别不大)
2.1 订阅方实现
1、包含头文件
//ros里的文本类型——————>std_msgs/String.h
//1.包含头文件;
#include"ros/ros.h" //包含了标准ROS类的声明
#include"std_msgs/String.h" //文本被封装成单独的数据类型了std_msgs功能包里的string.h
#include<sstream> //支持字符串的流输入输出
消息是从发布方来的,所以这边的头文件=发布方+订阅方处理数据所要用到的
2.初始化 ROS 节点
setlocale(LC_ALL,"");//防止乱码
//2.初始化 ROS 节点
ros::init(argc,argv,"Listener");//了解定义
3.创建节点句柄
//3.创建节点句柄
ros::NodeHandle nh;//相当于一个重命名 NodeHandle = nh
4.创建订阅者对象 和 5.处理订阅到的数据
//4.创建订阅者对象
ros::Subscriber sub = nh.subscribe("chatter",10,doMsg);
关于doMsg的定义: 这是一个回调函数,所谓回调函数:你到一个商店买东西,刚好你要的东西没有货,于是你在店员那里留下了你的电话,过了几天店里有货了,店员就打了你的电话,然后你接到电话后就到店里去取了货。在这个例子里,你的电话号码就叫回调函数,你把电话留给店员就叫登记回调函数,店里后来有货了叫做触发了回调关联的事件,店员给你打电话叫做调用回调函数,你到店里去取货叫做响应回调事件。
PS:怎么区别回调函数和普通函数?回调函数是自己定义,系统调用(系统传实参),普通函数自己定义,自己调用(自己传实参)怎么看出来是自己调用还是系统调用,回调函数的时候往往可以定义形参,系统会给该形参传递实参,例如单击响应函数的,系统调用时会传入实参,所以,普通函数调用是自己传入实参,回调函数是系统传入实参。这个domsg的参数是由发布者传过来的,所以也是系统传参。
这其实算是消息的处理了,但是只处理了一次,执行到“创建订阅者对象”这边的程序时,就跳到doMsg那边,那边的相关操作就是打印结果,随着话题“chatter”里不断的有东西进来,就会不停的跳到doMsg(类似于中断,有信号触发,就跳转。)。但是不会有任何结果输出,想要有结果输出需要假如下面第6步的代码。
void doMsg(const std_msgs::String::ConstPtr &msg){
//通过msg获取并操作订阅到的数据
ROS_INFO("订阅的数据:%s",msg->data.c_str());
}
6.spin()函数
ros::spin();
这个函数很重要 !!!
根据上面的实例,我们来捋一捋处理流程:
- 消息发布器(发布方)在一个while里面一直循环发送“hello ——>数字”到话题(topic)chatter上。
- 消息订阅器(订阅方)一旦知道chatter上面有data,就会将这data作为参数传入回调函数doMsg中,但是此时还没有执行domsg函数,而是把domsg函数放到了一个回调函数队列中。
- 所以当发布器不断发送data到chatter上面时,就会有相应的domsg函数进入队列中,它们函数名一样,只是实参不一样。
那什么时候处理回调函数队列中的回调函数了?这就是 ros::spin() 需要做的工作了。
- 对于spin函数,一旦进入spin函数,它就不会返回了,也不继续往后执行了,相当于它在自己的函数里面死循环了(直到ctrl+c 或者程序终止的时候才退出)。
- 主要的工作,就是不断的检查回调函数队列里面是否有domsg函数存在,如果有的话,它就会马上去执行domsg函数。如果没有的话,它就会阻塞,不会占用CPU。
综上所述,订阅方的函数也就结束了。
三、改写 CMakeLists.txt
我们在 ROS学习笔记(一)http://配置 CMakeLists.txt里有些这部分的内容
add_executable(demo01_pub src/demo01_pub.cpp)
add_executable(demo02_sub src/demo02_sub.cpp)
target_link_libraries(demo01_pub
${catkin_LIBRARIES}
)
target_link_libraries(demo02_sub
${catkin_LIBRARIES}
)
四、运行
执行七、八步骤即可