目录:
- 单线程
- 多线程
- 订阅多个Topic,多个Spinner threads
- 订阅一个Topic,多个Spinner threads
- 订阅多个Topic,每个Subscriber一个Callback queue
ROS里面每个节点都是用默认的单线程来处理任务,以前很多时候都没有注意使用多线程的方式来处理回调函数,今天在这里总结一下。实际上ROS中提供了多线程接口,因为单线程在很多时候不能保证时效性,所以我们往往还会使用到多线程。为了文章的完整性,下面将讨论单线程和多线程下的情况。
单线程
在只有一个Spinner thread的情况下,callback queue只能顺序执行。假设场景:一个发布节点 publisher
向两个话题(/topic/A
和 topic/b
)以1Hz速度发布消息,另外一个节点 subscriber
接收两个话题上的消息,以回调函数的方式处理消息内容,其中 CallbackA
在打印接收到的消息以外还会sleep 2s,CallbackB
只打印接收到的消息。代码如下
publisher.cpp
#include "ros/ros.h"
#include "std_msgs/String.h"
#include <sstream>
int main(int argc, char **argv)
{
ros::init(argc, argv, "publisher");
ros::NodeHandle n;
ros::Publisher pub_a = n.advertise<std_msgs::String>("/topic/A", 1000);
ros::Publisher pub_b = n.advertise<std_msgs::String>("/topic/B", 1000);
ros::Rate loop_rate(1);
std_msgs::String msg;
int count = 0;
while (ros::ok())
{
std::stringstream msg_a;
msg_a << "msg_a " << count;
msg.data = msg_a.str();
ROS_INFO("%s", msg.data.c_str());
pub_a.publish(msg);
std::stringstream msg_b;
msg_b << "msg_b " << count;
msg.data = msg_b.str();
ROS_INFO("%s", msg.data.c_str());
pub_b.publish(msg);
ros::spinOnce();
loop_rate.sleep();
++count;
}
return 0;
}
subscriber.cpp
#include "ros/ros.h"
#include "std_msgs/String.h"
#include <thread>
void CallbackA(const std_msgs::String::ConstPtr& msg)
{
ROS_INFO("A heard: [%s]", msg->data.c_str());
std::this_thread::sleep_for(std::chrono::seconds(2));
}
void CallbackB(const std_msgs::String::ConstPtr& msg)
{
ROS_INFO("B heard: [%s]", msg->data.c_str());
}
int main(int argc, char **argv)
{
ros::init(argc, argv, "subscriber");
ros::NodeHandle n;
ros::Subscriber sub_a = n.subscribe("/topic/A", 1000, CallbackA);
ros::Subscriber sub_b = n.subscribe("/topic/B", 1000, CallbackB);
ros::spin();
return 0;
}
输出结果
从执行效果中可以看到,不管有多少个Subscriber,节点都只能顺序执行回调,在对实时性要求比较高的场景中,这种方式会使得系统性能大打折扣,因此需要使用多线程的方式
多线程
订阅多个Topic,多个Spinner threads
在刚刚的 subscriber.cpp
代码中增加如下两行即可
输出结果
可以看到,A和B两个回调函数现在分别在两个不同的线程中进行处理,B回调不会因为A线程的sleep而被阻塞了。
订阅一个Topic,多个Spinner threads
假设只有一个Topic, 发布端的频率比较高,但我们希望尽可能多地响应消息,这时可以把回调任务分发给多个线程处理
修改 subscriber.cpp
输出结果如下
可以看到,回调A的处理频率和发布频率一致,都是1Hz。如果是单线程处理回调,那么回调的频率应该是0.5Hz,因为CallbackA()
中sleep 2s
订阅多个Topic,每个Subscriber一个Callback queue
上面提到的两种情况都是只有一个回调队列,这种情况会导致在处理回调B的时候无法处理回调A。ROS里面还提供了给每个回调创建一个回调消息队列,这样可以实现真正的多线程回调
修改 subscriber.cpp
如下:
输出结果:
给每一个subscriber创建一个单独的callback queue,这样就解决了即使用了MultiThreadedSpinner但所有的callback依然在同一个queue执行的问题,此方法用来解决subscriber的优先级问题。
参考:https://zhuanlan.zhihu.com/p/375418691
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)