在ROS当中,原作者是不推荐用多线程的,他建议用多进程,变成一个个节点的形式进行通信。多线程分为两种模式:同步和异步。
同步:
MultiThreadSpinner s(4)
,一共5个线程。包括了主线程。异步:
AsyncSpinner s(4)
, 一共5个线程。包括了主线程。
回调方法 | 阻塞 | 线程 |
---|---|---|
ros::spin() | 阻塞 | 单线程 |
ros::spinOnce | 非阻塞 | 单线程 |
ros::MultiThreadedSpinner | 阻塞 | 多线程 |
ros::AsyncSpinner | 非阻塞 | 多线程 |
对于多话题的订阅,我们先看传统的方法:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22void cb1(const geometry_msgs::PoseStamped::ConstPtr& msg)
{
ROS_INFO("uwb_pose x: %f", msg->pose.position.x);
}
void cb2(const geometry_msgs::PoseStamped::ConstPtr& msg)
{
sleep(2);
ROS_INFO("yolo_pose x: %f", msg->pose.position.x);
}
int main(int argc, char **argv)
{
ros::init(argc, argv, "node");
ros::NodeHandle nh;
setlocale(LC_ALL, "");
ros::Subscriber uwbSub = nh.subscribe<geometry_msgs::PoseStamped>("uwb_pose", 1, cb1);
ros::Subscriber yoloSub = nh.subscribe<geometry_msgs::PoseStamped>("yolo_pose", 1, cb2);
ros::spin();
return 1;
}
在回调函数cb2
里,可能先执行一大堆耗时的命令,这里用sleep(2)代替,这样cb1
的 ROS_INFO
获得的消息就会缺失,这明显就是多线程的问题了。
把代码加上ros::MultiThreadedSpinner s(2);
( 无需加入头文件 ), ros::spin();
改为ros::spin(s);
, 再运行会发现cb1
里没有缺少一个消息。
这里多线程的目的是保证线程cb1
不丢失消息,而不是cb2
,它丢失消息是必然的。
对于ros::AsyncSpinner
,代码在ros::Subscriber
定义之后这样写:1
2
3ros::AsyncSpinner spinner(2);
spinner.start();
ros::waitForShutdown();
当程序当中有数据处理线程的时候,建议开辟异步多线程订阅,算法写在订阅函数里面。 当然,目前的处理当中,我更倾向于重新开辟一个线程,然后通过循环数组来进行数据交互。
参考:ROS多线程订阅消息