天天看點

ROS中訂閱(Subscribe)最新消息以及對消息隊列的淺談

寫在前面

本文是轉載文章,如有侵權,請聯系删除

本文轉載自 https://blog.csdn.net/handsome_for_kill/article/details/81984428

原文内容

1.問題與目标

機器人應用中難免會遇到運算起來很費時間的操作,比如圖像的特征提取、點雲的比對等等。有時候,不可避免地,我們需要在ROS的Subscriber的Callback回調函數中進行這些費時的操作。

Subscriber所訂閱的消息的釋出頻率可能是很高的,而這些操作的運算速度肯定達不到消息釋出的速度。是以,如果我們要是沒有取舍的對于每個消息都調用一次回調函數,那麼勢必會導緻計算越來越不實時,很有可能當下在處理的還是幾十秒以前的資料。是以,我們希望每次回調函數都處理目前時刻最新的一個消息,這就是我們的目标。

要達到這個目标有三點,第一點是要設定Publisher的queue_size等于1;第二點是要設定Subscriber的queue_size(消息隊列大小)等于1;第三點非常重要,要設定Subscriber的buff_size(緩沖區大小)足夠大,大于一個消息的大小。

像這樣:

# ROS Python
pcdpub = rospy.Publisher("lidardata", PointCloud, queue_size=1)
rospy.Subscriber("lidardata", PointCloud, self.pcd_resolve_callback,queue_size=1,buff_size=52428800)
           

2.解釋:

(1) Subscriber和Publisher的消息隊列起什麼作用,隊列的大小有什麼影響?

  • 簡單描述一下,Publisher的消息隊列是為了緩存釋出節點釋出的消息,一旦隊列中消息的數量超過了queue_size,那麼最先進入隊列的(最老的)消息被舍棄。Subscriber的消息隊列是為了緩存節點接收到的資訊,一旦自己處理的速度過慢,接收到的消息數量超過了queue_size,那麼最先進入隊列的(最老的)消息會被舍棄。是以,我們想隻處理最新的消息,實際上隻需要把兩個queue_size都設定成1,那麼系統不會緩存資料,自然處理的就是最新的消息。

(2) Subscriber有消息隊列緩存消息了,為什麼Publisher還要有消息隊列?

  • 在我看來,Publisher的消息隊列是一定要有的,因為ROS中釋出節點往外發送消息是基于Topic發送,而不是直接向Subscriber訂閱者發送,是以必須要有一個消息隊列來存放釋出的消息,以供訂閱者來擷取。而且這個消息隊列的好處是在網絡差、帶寬小、延時高的時候,保證資料不容易丢失。

(3) 既然Publisher有消息隊列了,為什麼Subscriber還要有消息隊列?

  • 這個問題比較難一點。我的了解是,由于ROS畢竟是分布式系統,Publisher和Subscriber不一定在同一台主機上,是以消息需要通過網絡來交換。但是網絡的性能時好時壞,如果Subscriber沒有消息隊列,那麼每次運作Callback函數前都要先通過網絡取回消息,然後才能處理。當網絡很差時,就會讓系統堵塞。而有消息隊列的話,Subscriber就可以一邊處理隊列中的消息,一邊通過網絡緩存新的消息,而不用每次處理消息前都要臨時去讀一個回來。這樣就增加了系統的可靠性。

(4) 為什麼要設定緩沖區的大小?

  • 這個緩沖區的大小是指消息隊列使用的緩沖區實體記憶體空間大小。如果這個空間小于一個消息所需要的空間,比如消息是一副圖檔或者一幀點雲,資料量超過了緩沖區的大小。這個時候為了保證通信不發生錯誤,就會觸發網絡通信的保護機制,TCP的Buffer會為你緩存消息。這種機制就會導緻每一幀消息都被完整的緩存下來,沒有消息被丢棄,感覺上就像queue_size被設定成了無窮大。

詳細說明請參考:

Ros subscriber not up to date

rospy subscriber queue_size acting strange when buff_size is too small

(5) 消息隊列的運作機制

這裡隻能說是我的了解了,因為沒有看過源代碼。

  • 首先,釋出節點把消息釋出,消息進入Publisher的消息隊列,同時通知訂閱了該話題消息的Subscriber來取消息。
  • 其次,Subscriber來Publisher的消息隊列裡取消息,但取走的也是最老的消息,因為畢竟這是先入先出的隊列。這也是為什麼Publisher的消息隊列的大小也要設定為1。
  • 最後,被取走的消息存放入了Subscriber的消息隊列中,等待被Callback執行。如果Callback執行很慢,消息越堆越多,最老的消息會逐漸被頂替。
  • 當然,這裡究竟是Subscriber來取消息,還是Publisher直接把消息推給Subscriber,我隻是猜測,反正這裡交換的消息肯定不是最新的消息,而是隊列裡最老的消息。

轉載連結:

[1] ROS中訂閱(Subscribe)最新消息以及對消息隊列的淺談 https://blog.csdn.net/handsome_for_kill/article/details/81984428