Post

複数ノードのシングルスレッド実行

ROS Japan UG #18 関西勉強会

先日、@takasehidekiさん企画で初の関西ROS勉強会が開催されました。 発表資料を共有していただいているので、その場の雰囲気を少し味わうことができます。

特に「ROSのリアルタイムツールの紹介」は熱そうです。

ROS勉強会の地方開催は、関西以外でももっと積極的に拡大していきたいです。 求む、地方開催企画推進者。

複数ノードのシングルスレッド実行

ROS 1のroscppを使う場合、複数のノード(実行最小単位)を実行するときにNodeletという仕組みを使うことで、メッセージ通信を介さずメモリコピーも発生させずに実行することができました。

これにより、それぞれのノードは再利用しやすい単純な機能のみを提供させながらも、実行時にそれらを組み合わせて単一プロセスで動かすことで、ほとんど計算・通信オーバヘッドを発生させず、複雑なノードグラフを持つプログラムも構成できるようになりました。 高い開発効率と実行効率を両立する素晴らしい仕組みです。

ただし、Nodeletの作法に則ったプログラムの追記とlaunch設定の記述が必要でした。 ROS 2のrclcppではこの煩雑さが解決され、Nodeletという特殊な仕組みを再設計し、標準機能として取り込んでしまいました。

https://github.com/ros2/examples/tree/master/rclcpp/minimal_composition

が単純なPublisher、Subscriberの例で非常にわかりやすいです。 PublisherのPublisherNodeクラスとSubscriberのSuscriberクラスは、それぞれノードの基底クラスrclcpp::Nodeを継承したクラスです。

これらを別々のプロセスで動かすには、rclcpp::spin()を別々のmain関数で呼びます。

int main(int argc, char * argv[])
{
  rclcpp::init(argc, argv);
  rclcpp::spin(std::make_shared<publishernode>());
  rclcpp::shutdown();
  return 0;
}

int main(int argc, char * argv[])
{
  rclcpp::init(argc, argv);
  rclcpp::spin(std::make_shared<subscribernode>());
  rclcpp::shutdown();
  return 0;
}

一方、同じプロセスの同じスレッドで動かすには間にrclcpp::executors::SingleThreadedExecutorを挟むだけです。 ノードの実装方法自体に変更は全く必要ありません。

int main(int argc, char * argv[])
{
  rclcpp::init(argc, argv);
  rclcpp::executors::SingleThreadedExecutor exec;
  auto publisher_node = std::make_shared<publishernode>();
  auto subscriber_node = std::make_shared<subscribernode>();
  exec.add_node(publisher_node);
  exec.add_node(subscriber_node);
  exec.spin();
  rclcpp::shutdown();
  return 0;
}

裏側ではC++11のスマートポインタが上手く働き、メモリのゼロコピーでノード間のデータのやり取りが行われます。

同じプロセスのマルチスレッドで動かすには、rclcpp::executors::MultiThreadedExecutorに置き換えるだけです。 C++11で導入されたstd::threadのおかげで、OS非依存なマルチスレッドプログラミングができるようになった賜物です。

This post is licensed under CC BY 4.0 by the author.

Comments powered by Disqus.