ros::spin()とros::spinOnce()の違い

ros::spin()とros::spinOnce()の違いに関して、
簡潔に説明している日本語のウェブサイトが少なかったので説明します。

ros::spinOnce()

ros::spinOnceの場合、その行が実行されたタイミングで1回コールバック関数にアクセスする処理になります。その行を実行しなければ、メッセージがパブリッシュされてもコールバックしないので、定期的にサブスクライブしたい場合は、下のソースコードのようにwhile文等のループの中に書く必要があります。
ちなみに、ros::Rateのsleepは、設定したレート(今回は10Hz)でループするように、while文の中で時間をためる役割を担っています。

#include <ros/ros.h>
#include <std_msgs/String.h>

void Callback(const std_msgs::StringConstPtr& msg)
{
	std::cout << "msg = " << msg->data.c_str() << std::endl;
}

int main(void)
{
	ros::init(argc, argv, "spinOnce_ver");
	ros::NodeHandle nh;
	ros::Subscriber sub = nh.subscribe("message", 1, Callback);

	ros::Rate loop_rate(10);
	while(ros::ok()){
		std::cout << "loop" << std::endl;
		ros::spinOnce();
		loop_rate.sleep();
	}
}

Ad.

ros::spin()

一方ros::spin()では、メッセージがパブリッシュされるたびにコールバック関数にアクセスする処理になります。よって、下のソースコードのように、ループの中に書く必要がありません。
ただし、メッセージがパブリッシュされても、他の処理をしている途中である場合は、処理が終わってからのアクセスになりますので、”処理が間に合う範囲で”全メッセージを拾うシステムになります。
ただし、ros::spin()を実行すると、そのスレッドが終了するまで、他のスレッドには入れないので、Callbackの関数しか呼ばれなくなります。つまり、Callbackの中に全ての処理を書く必要があります。ちなみに、ros::spin()以降に書かれている行は、Ctrl+Cでプロセスを中断した後に実行されます。

#include <ros/ros.h>
#include <std_msgs/String.h>

void Callback(const std_msgs::StringConstPtr& msg)
{
	std::cout << "msg = " << msg->data.c_str() << std::endl;
}

int main(void)
{
	ros::init(argc, argv, "spin_ver");
	ros::NodeHandle nh;
	ros::Subscriber sub = nh.subscribe("message", 1, Callback);

	std::cout << "start" << std::endl;
	ros::spin();
	std::cout << "This will be printed after Ctrl+C" << std::endl;
}

※厳密には、宣言時のバッファの数の設定によって挙動が変わるので注意してください。

ros::Subscriber sub = nh.subscribe("message", 1, Callback);	//宣言 バッファ=1

まとめ

  • ros::spinOnce()
    その行が実行されるタイミングで1度だけcallback関数にアクセス
  • ros::spin()
    その行が実行された後は、メッセージがパブリッシュされるたびにcallback関数にアクセス

さいごに

用途に応じて使い分けましょう。
少しでも役に立てば幸いです。

以上です。

Ad.