ros::spin()和ros::spinOnce()的作用
ROS monitor socket负责接受所有订阅的消息。当一个新消息到来,ROS将其对应的回调函数放到队列中。ROS不会立即调用这些回调函数,而是等到ros::spinOnce()到来时才会处理。
很少有应用场景适合直接采用ros::spinOnce(),下面的特殊情况需要特殊处理。
以一定间隔处理回调
一般而言,你很少会希望ROS回调函数只在固定的时间间隔被调用。比如一个消息的频率为100HZ,而ros::spinOnce()仅仅5HZ,而同时queue_size为1,那么你将丢失95/100的消息。
这种情况下,使用ROS::Timer是一个更好的选择。
存在ROS以外的其他回调函数
比如使用另一个需要处理异步消息的库,你就需要将这个库中类似于spinOnce()的函数同spinOnce()一起放到主循环函数中。
1
2
3
4
5
6
7ros::Rate r(100);
while (ros::ok())
{
libusb_handle_events_timeout(...); // Handle USB events
ros::spinOnce(); // Handle ROS events
r.sleep();
}将ROS集成到其他框架中
例如在使用OpenGL和GLUT时,一种集成ROS的方法是告诉GLUT的主循环,每10ms就调用以下ros::spinOnce():
1
2
3
4void timerCb(int value) { ros::spinOnce(); }
glutTimerFunc(10, timerCb, 0);
glutMainLoop(); // Never returns
ros::spin()和ros::spinOnce()的区别
ros::spin()再调用之后不会返回,因此主程序不会继续执行,所以循环没有意义。而ros::spinOnce()在调用之后会返回,继续执行后面的程序。
ros::spin()函数一般不会出现在循环中,因为程序执行到spin()后就不调用其他语句了,也就是说该循环没有任何意义,还有就是spin()函数后面一定不能有其他语句(return 0 除外),有也是白搭,不会执行的。ros::spinOnce()的用法相对来说很灵活,但往往需要考虑调用消息的时机,调用频率,以及消息池的大小,这些都要根据现实情况协调好,不然会造成数据丢包或者延迟的错误。
参考文献
https://answers.ros.org/question/11887/significance-of-rosspinonce/
[http://wiki.ros.org/roscpp/Overview/Callbacks%20and%20Spinning](http://wiki.ros.org/roscpp/Overview/Callbacks and Spinning)