天天看點

ROS學習(十四):ROS Spinning

ROS 單線程與多線程 Spinning

roscpp 不為應用程式指定具體的線程模型。

允許回調函數調用任意數量的線程

必須要調用線程,否則訂閱、服務等回調将永遠不會被調用

常見的解決方案是,在主函數開頭用 ros::spin()

注意:回調序列對内部的網絡通訊沒有影響,隻能影響回調發生的時刻。它們将影響訂閱序列,取決于處理回調函數的速度、消息到達的速度、消息是否丢棄等。

1、單線程

最簡單最常用的單線程為:

ros::spin()

ros::init(argc, argv, "my_node");
ros::NodeHandle nh;
ros::Subscriber sub = nh.subscribe(...);
...
ros::spin();
           

所有使用者的調用程式将從

ros::spin()

開始調用。隻到節點關閉(ros::shutdown() or a Ctrl-C),ros::spin()才有傳回值。

另一個常用的模式是周期性的

ros::spinOnce()

ros::Rate r(); //  hz
while (should_continue)
{
  ... do some work, publish some messages, etc. ...
  ros::spinOnce();
  r.sleep();
}
           

ros::spinOnce()

将在那一刻調用所有等待調用的函數。

執行一個自己的spin()函數很簡單:

#include <ros/callback_queue.h>
ros::NodeHandle n;
while (ros::ok())
{
  ros::getGlobalCallbackQueue()->callAvailable(ros::WallDuration());
}

           

spinOnce() 類似:

#include <ros/callback_queue.h>

ros::getGlobalCallbackQueue()->callAvailable(ros::WallDuration());
           

注:spin()和spinonce()真的意味着單線程應用程式,而不是從多個線程中調用的一次優化。

2、多線程

ros::MultiThreadedSpinner

阻塞式線程,類似于

ros::spin()

,你可以在它的構造器上指定一定數量的線程數,但如果不指定或設定為0,則會在每個CPU上執行一個線程。

ros::MultiThreadedSpinner spinner(); // Use  threads
spinner.spin(); // spin() will not return until the node has been shutdown
           

ros::AsyncSpinner (since 0.10)

一個更有用的線程。它不用

spin()

回調,而用

start()

stop()

,當它銷毀時候就會自動關閉

ros::AsyncSpinner spinner(); // Use  threads
spinner.start();
ros::waitForShutdown();
           

3、回調序列

建立回調序列:

#include <ros/callback_queue.h>
...
ros::CallbackQueue my_queue;
           

CallbackQueue 有兩種方法調用回調函數: callAvailable() 和callOne()。

callavailable()調用隊列裡所有的。callone()隻會調用回調在隊列最舊的。

callAvailable()和callOne()都會有一個逾時選項,在傳回前,它會在逾時時間内等待回調有效。

如果是0,同時隊列沒有回調,則直接傳回。

4、進階回調序列

上面spin()執行的語句,有調用到 ros::getGlobalCallbackQueue(),預設所有的回調都會放到全局隊列,由ros::spin() 處理。

自定義回調序列:
1、subscribe(), advertise(), advertiseService()
2、NodeHandle
ros::AsyncSpinner spinner(0, &my_callback_queue);
spinner.start();
           

這個可使用所有訂閱,服務,定時器等,回調通過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();
spinner.spin(&my_callback_queue);
           

參考

http://wiki.ros.org/roscpp/Overview/Callbacks%20and%20Spinning