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