半年前の記事ですが、ROS 2のクライアントサイドの仕組みを理解するためにC++用クライアントrclcppに着目していました。

ROS 2が正式リリースされる際にこのソースコードにもかなり変更が入り、実装がより疎結合になるように改善されているため、本日はその部分を見ていきます。

rclcpp::node_interfaces::Node***Interface

ROSノードを表現する一番重要なクラスNodeの実装は、デザインパターン用語でいうDelegateパターンで実装されています。

  • 名前空間関連
  • トピック関連
  • サービス関連
  • パラメータ関連
  • タイマー関連

というような単位でインタフェースのみを純粋仮想関数で提供するクラスを定義し、それを実際に実装するインスタンスをコンストラクタで与えることで、実行時にノードの特性を変更することができるようになっています。

Node::create_wall_timer

一つだけ、タイマー関連のインタフェースであるNodeTimersInterfaceを取り上げます。

ROS 1からROS 2で大きく実装パターンが変更された部分にタイマー実行処理があります。 これまではトピックのパブリッシャノードはmain関数などのstatic関数内でwhile文を作って独自に無限ループを回すことが常套手段だったと思います。

しかし、ROS 2ではその実装方法は推奨されない方法になりました。 その代わり、Nodeクラスに周期実行のためのタイマー機能が内蔵され、タイマーのコールバック関数にトピックの発行処理を書くスタイルが推奨されます。

そのためのメソッドがNode::create_wall_timer()です。 callback引数に周期実行したいコールバック関数を渡し、Nodeインスタンスがスピン状態になった後、period引数の周期に従って周期実行されます。

11行目でNodeTimersInterface::add_timer()が呼ばれています。 add_timerの実装自体はrclcppの関数ではないrclライブラリ関数(rcl_timer_***()など)が呼び出されます。 これにより、タイマー実行の実装をその他のクライアントライブラリ(例えばPython用のrclpy)との間で再利用しているようです。

ちなみに、タイマーのクロックには標準のWallTimerではstd::chrono::steady_clockが用いられています。

https://cpprefjp.github.io/reference/chrono/steady_clock.html

steady_clockは、物理的な時間と同様、決して逆行することがない時間を表現するためのクロックである。

とのことです。処理系依存ですが、クロックの精度が非常に気になるところです。