对于一些只订阅一个话题的简单节点来说,我们使用ros::spin()进入接收循环,每当有订阅的话题发布时,进入回调函数接收和处理消息数据。但是更多的时候,一个节点往往要接收和处理不同来源的数据,并且这些数据的产生频率也各不相同,当我们在一个回调函数里耗费太多时间时,会导致其他回调函数被阻塞,导致数据丢失。这种场合需要给一个节点开辟多个线程,保证数据流的畅通。
https://blog.csdn.net/wengge987/article/details/50619851
roscpp 的api文档:
https://docs.ros.org/api/roscpp/html/namespaceros.html
多线程:
http://wiki.ros.org/roscpp/Overview/Callbacks%20and%20Spinning
http://blog.chinaunix.net/uid-27875-id-5817906.html
ros::spin()
单线程根据需要只调用一次
ros::spinOnce()
自定义自己的rose::spin,自定义调用频率
ros::getGlobalCallbackQueue()->callAvailable(ros::WallDuration(0.1))
只调用一次
ros::getGlobalCallbackQueue()->callAvailable(ros::WallDuration(0));
ros::MultiThreadedSpinner spinner(4); // Use 4 threads
spinner.spin(); // spin() will not return until the node has been shutdown
ros::AsyncSpinner s(4);
s.start();
ros::Rate r(5);
while (ros::ok())
{
...
r.sleep();
}
#include "ros/ros.h"
#include "ros/callback_queue.h"
#include "std_msgs/String.h"
#include
/**
* tutorial demonstrates the use of custom separate callback queues that can be processed
* independently, whether in different threads or just at different times.
* 演示了自定义独立回调队列的使用,
* 这些回调队列可以在不同的线程中独立处理,也可以在不同的时间进行处理。
*/
void chatterCallbackMainQueue(const std_msgs::String::ConstPtr& msg)
{
ROS_INFO_STREAM("I heard: [ " << msg->data << "] in thread [" << boost::this_thread::get_id() << "] (Main thread)");
}
//主线程中的调用
void chatterCallbackCustomQueue(const std_msgs::String::ConstPtr& msg)
{
ROS_INFO_STREAM("I heard: [ " << msg->data << "] in thread ["
<< boost::this_thread::get_id() << "]");
}
/**
* The custom queue used for one of the subscription callbacks
*/
ros::CallbackQueue g_queue; //第一步:用于订阅回调的自定义队列
void callbackThread()
{
ROS_INFO_STREAM("Callback thread id=" << boost::this_thread::get_id());
ros::NodeHandle n;
while (n.ok())
{
//第四步: 执行自定义队列中的回调函数.
// CallbackQueue类有两种调用内部回调的方法:callAvailable()和callOne()。
// callAvailable()将获取队列中当前的所有内容并调用它们。callOne()将简单地调用队列上最古老的回调。
g_queue.callAvailable(ros::WallDuration(0.01));
}
}
int main(int argc, char **argv)
{
ros::init(argc, argv, "listener_with_custom_callback_processing");
ros::NodeHandle n;
/**
* The SubscribeOptions structure lets you specify a custom queue to use for a specific
* subscription. SubscribeOptions结构允许您指定用于特定订阅的自定义队列
* You can also set a default queue on a NodeHandle using the
* NodeHandle::setCallbackQueue() function.
* 还可以使用NodeHandle::setCallbackQueue()函数在节点句柄上设置默认队列
*
* AdvertiseOptions and AdvertiseServiceOptions offer similar functionality.
* 对于话题发布者, 有 AdvertiseOptions 和 AdvertiseServiceOptions 可以使用
*/
//第二步: 声明订阅或者发布选项, 然后和订阅器/发布器绑定在一起
ros::SubscribeOptions ops = ros::SubscribeOptions::create
("chatter", 1000, chatterCallbackCustomQueue, ros::VoidPtr(), &g_queue);
ros::Subscriber sub = n.subscribe(ops);
ros::Subscriber sub2 = n.subscribe("chatter", 1000, chatterCallbackMainQueue);
//第三步: 声明线程.
boost::thread chatter_thread(callbackThread);
ROS_INFO_STREAM("Main thread id=" << boost::this_thread::get_id());
ros::Rate r(1);
while (n.ok())
{
ros::spinOnce();
r.sleep();
}
chatter_thread.join();
return 0;
}
roscpp还允许分配自定义回调队列并分别为其提供服务。可以使用以下两种之一来完成此操作:
每个节点句柄
(1) is possible using the advanced versions of those calls that take a *Options structure. See the API docs for those calls for more information.
(2) is the more common way:
ros::NodeHandle nh;
nh.setCallbackQueue(&my_callback_queue);
这使所有订阅,服务,计时器等都通过my_callback_queue而不是roscpp的默认队列进行回调。这意味着ros :: spin()和ros :: spinOnce()将不会调用这些回调。相反,您必须单独服务该队列。您可以使用ros :: CallbackQueue :: callAvailable()和ros :: CallbackQueue :: callOne()方法手动进行操作:
my_callback_queue.callAvailable(ros::WallDuration());
// alternatively, .callOne(ros::WallDuration()) to only call a single callback instead of all available
各种* Spinner对象还可以使用指向要使用的回调队列的指针,而不是默认对象:
ros::AsyncSpinner spinner(0, &my_callback_queue);
spinner.start();
---
ros::MultiThreadedSpinner spinner(0);
spinner.spin(&my_callback_queue);
出于多种原因,将回调分为不同的队列可能很有用。一些示例包括:
https://www.boost.org/doc/libs/1_67_0/doc/html/thread/thread_management.html#thread.thread_management.tutorial
多个不同的topic 用一个回调处理
void callback(const ImageConstPtr& image_color_msg,
const ImageConstPtr& image_depth_msg,
const CameraInfoConstPtr& info_color_msg,
const CameraInfoConstPtr& info_depth_msg)
{
....
}
int main(int argc, char** argv)
{
ros::init(argc, argv, "vision_node");
ros::NodeHandle nh;
//不同类型的topic订阅
message_filters::Subscriber image_color_sub(nh,"/camera/rgb/image_raw", 1);
message_filters::Subscriber image_depth_sub(nh,"/camera/depth_registered/image_raw", 1);
message_filters::Subscriber info_color_sub(nh,"/camera/rgb/camera_info", 1);
message_filters::Subscriber info_depth_sub(nh,"/camera/depth_registered/camera_info", 1);
pointcloud_pub = nh.advertise ("mypoints", 1);
//定义规则
typedef sync_policies::ApproximateTime MySyncPolicy;
//规则化实例
Synchronizer sync(MySyncPolicy(10), image_color_sub, image_depth_sub, info_color_sub, info_depth_sub);
//绑定回调
sync.registerCallback(boost::bind(&callback, _1, _2, _3, _4)); // _1 代表实力的第一个对象.
ros::spin();
return 0;
}