前言
无
一、编写回调函数
发布和订阅消息的一个重要的区别是:
订阅者节点无法知道消息什么时候到达。为了应对这一事实,我们必须把响应收到消息事件的代码放到回调函数里,ROS 每接收到一个新的消息将调用一次这个函数。订阅者的回调函数类似于:
void function_name(const package_name::type_name &msg)
{
. . .
}
其中参数 package_name 和 type_name 和发布消息时的相同,它们指明了我们想订阅的话题的消息类。回调函数的主体有权限访问接收到消息的所有域,并以它认为合适的方式存储、使用或丢弃接收到的数据。与往常一样,我们必须包含定义该类的头文件。
注意订阅者的回调函数的返回值类型为 void。其实这样安排是合理的,因为调用此函数是 ROS 的工作,返回值也要交给 ROS,所以我们的程序无法获得返回值,当然非 void 的返回值类型也就没有意义了。
二、创建订阅者对象
为了订阅一个话题,我们需要创建一个ros::Subscriber对象 :
ros::Subscriber sub = node_handle.subscribe
(topic_name,queue_size, pointer_to_callback_function);
这个构造函数有三个形参,其中大部分与 ros::Publisher 声明中的类似:
1.node_handle 与我们之前多次见到的节点句柄对象是相同的。
2.topic_name 是我们想要订阅的话题的名称,以字符串的形式表示。本例程中是"turtle1/pose"。再次强调,我们忽略了前斜线使其成为相对名称。
3.queue_size 是本订阅者接收消息的队列大小,是一个整数。通常,你可以使用一个较大的整数,例如 1000,而不用太多关心队列处理过程。
4.最后一个参数是指向回调函数的指针,当有消息到达时要通过这个指针找到回调函数。在 C++中,你可以通过对函数名使用符号运算符(&,“取址”)来获得函数的指针。
创建 ros::Subscriber 对象时,我们没有在任何地方显式地提到消息类型。实际上,subscribe 方法是模板化的,C++编译器会根据我们提供的函数指针中的数据类型判断出正确的消息类型。
三.给ROS控制权
最后的复杂之处在于只有当我们明确给ROS许可时,它才会执行我们的回调函数 。实际上有两个略微不同的方式来做到这一点,其中一个版本如下所示:
ros::spinOnce();
这个代码要求 ROS 去执行所有挂起的回调函数,然后将控制权限返回给我们。
另一个方法如下所示:
ros::spin();
这个方法要求 ROS 等待并且执行回调函数,直到这个节点关机。换句话说,ros::spin()大体等于这样一个循环:
while(ros::ok( ))
{
ros::spinOnce();
}
使用 ros::spinOnce()还是使用 ros::spin()的建议如下:
你的程序除了响应回调函数,还有其他重复性工作要做吗?如果答案是“否”,那么使用 ros::spin();否则,合理的选择是写一个循环,做其他需要做的事情,并且周期性地调用 ros::spinOnce()来处理回调。
订阅者程序中常见的一个错误是不小心忽略了调用ros::spinOnce 和 ros::spin。在这种情况下,ROS 永远没有机会去执行你的回调函数。忽略 ros::spin 会导致你的程序在开始运行后不久就退出。忽略 ros::spinOnce 使程序表现的好像没有接收到任何消息。
示例代码如下:
* 该例程将订阅/person_info话题,自定义消息类型learning_topic::Person
*/
#include <ros/ros.h>
#include "learning_topic/Person.h"
// 接收到订阅的消息后,会进入消息回调函数
void personInfoCallback(const learning_topic::Person::ConstPtr& msg)
{
// 将接收到的消息打印出来
ROS_INFO("Subcribe Person Info: name:%s age:%d sex:%d",
msg->name.c_str(), msg->age, msg->sex);
}
int main(int argc, char **argv)
{
// 初始化ROS节点
ros::init(argc, argv, "person_subscriber");
// 创建节点句柄
ros::NodeHandle n;
// 创建一个Subscriber,订阅名为/person_info的topic,注册回调函数personInfoCallback
ros::Subscriber person_info_sub = n.subscribe("/person_info", 10, personInfoCallback);
// 循环等待回调函数
ros::spin();
return 0;
}
总结
无