对 ROS 中 callback 机制进行一点探索
前言
之前在使用 YOLO 时,因为我们使用的是 GPU 算力很低(或者说无)的 nuc 运行的,最大速度是 0.1s 一帧,也就是 10Hz,但是相机一般都有 30Hz,当时出现了很奇怪的延时,就是 YOLO 运行的结果总是比相机话题中的画面明显慢的多(秒为单位),当时错误的理解为队列长度设置为 1,就可以一直保证是最新的消息,所以一直没有很好的解决。当时是把相机的帧率转到只有 10Hz,再让 YOLO 跑,就没有延时了,当时没管,最新想起来了,就配合 ChatGPT 写了点代码测试了一下。
实验过程
先用python写了个发布者和订阅者,发布者一秒一发,1 2 3 4 … ,这样一个一个发下去
接受者使用 rospy.spin() 一直运行 callback 函数,callback 函数中延时 5s,模拟回调函数花费时间很长
1 | #!/usr/bin/env python |
1 | #!/usr/bin/env python |
结果如下:
1 | [INFO] [1686216384.496250]: 1 |
回调函数的运行机制是,当有一个消息过来时,就执行回调函数,没有则不执行,这里依照此原则一个一个执行,于是从 1 2 3 4 … 一个一个进行接受。显然,这样无法达到我们希望消息实时更新的想法。
这时候加上队列长度为会变成如下结果,令 queue_size=1
1 | [INFO] [1686216536.836731]: 1 |
从结果来看,队列长度里面储存的内容是在上一次 callback 执行时更新的,如果callback花费很长时间,这个队列为一的消息并不是最新的,而是上一次 callback 运行时候的值,而不写队列长度则会把所有的数据全部储存,一个一个依次读取
显然,两种结果都不是想要的结果
由于 python 没有 spinOnce 机制,于是我测试了 c ++ 的 ros::spinOnce() 函数,其效果与队列长度设置为1更新机制一样,即使用的消息是上一次执行callback保存的值
1 | #include <ros/ros.h> |
1 | [INFO] [1686236228.724176]: 1 |
解决方法
- 使用
while not rospy.is_shutdown():
,实测会来了一个消息就会开始执行callback
直接上代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15#!/usr/bin/env python
import rospy
from std_msgs.msg import String
import time
a = ' '
def callback(data):
global a
a = data.data
rospy.init_node('subscriber_node')
rospy.Subscriber('my_topic', String, callback, queue_size=1)
while not rospy.is_shutdown():
time.sleep(5)
rospy.loginfo('python %s', a)输出
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25[INFO] [1686239068.123173]: 1
[INFO] [1686239069.124314]: 2
[INFO] [1686239070.124508]: 3
[INFO] [1686239071.124797]: 4
[INFO] [1686239072.124445]: 5
[INFO] [1686239073.124328]: 6
[INFO] [1686239073.138271]: python 6
[INFO] [1686239074.124528]: 7
[INFO] [1686239075.124556]: 8
[INFO] [1686239076.124529]: 9
[INFO] [1686239077.124631]: 10
[INFO] [1686239078.124316]: 11
[INFO] [1686239078.146387]: python 11
[INFO] [1686239079.124611]: 12
[INFO] [1686239080.124351]: 13
[INFO] [1686239081.124512]: 14
[INFO] [1686239082.124566]: 15
[INFO] [1686239083.124489]: 16
[INFO] [1686239083.154269]: python 16
[INFO] [1686239084.124850]: 17
[INFO] [1686239085.124540]: 18
[INFO] [1686239086.123901]: 19
[INFO] [1686239087.124629]: 20
[INFO] [1686239088.124192]: 21
[INFO] [1686239088.159253]: python 21把费时间的程序放在while中即可,spin 负责一直更新参数
- 使用
2.双线程
https://blog.csdn.net/lizhiyuanbest/article/details/104710653
结论
这主要对于视觉代码提出了要求,如果使用重型网络,只有10hz的处理速度,则需要使用while not rospy.is_shutdown()
、或者修改图像消息的帧率、或者使用多线程更新图像
如果是传统视觉,一般处理速度较快,30hz 到100hz 左右,这时候基本保证消息是实时的。当消息处理能力大于话题消息频率时,十分推荐使用spin函数