先日のROS 2ノードのライフサイクルを理解する (1)の続きです。

本日は、以下に従って、実際にROS 2のライフサイクルの実装方法を見てきます。

Publisher実装

簡単なPublisherのサンプルプログラムでは、main関数にwhileループを直に書いてPublishすることが多かったと思います。

しかし、ライフサイクルを実現するROS 2ノードは、状態遷移後に呼び出されるコールバックメソッドを実装する必要があります。 そのインタフェースを提供しているクラスが、rclcpp_lifecycle::LifecycleNodeです。

https://github.com/ros2/demos/blob/master/lifecycle/src/lifecycle_talker.cpp

を見ると、確かにon_configure()のように、on_状態名のメソッドが並んでいます。 クラスのpublicメソッド宣言だけ抜き出しました。

class LifecycleTalker : public rclcpp_lifecycle::LifecycleNode
{
public:
  explicit LifecycleTalker(const std::string & node_name,
                           bool intra_process_comms = false);

  void publish();

  rcl_lifecycle_ret_t on_configure(const rclcpp_lifecycle::State &);
  rcl_lifecycle_ret_t on_activate(const rclcpp_lifecycle::State &);
  rcl_lifecycle_ret_t on_deactivate(const rclcpp_lifecycle::State &);
  rcl_lifecycle_ret_t on_cleanup(const rclcpp_lifecycle::State &);
}

どのコールバックメソッドも引数に一つ前の状態を引数に、状態遷移の成功・失敗を返り値に取るようです。 rcl_lifecycle_ret_tは以下の3値を取ります。

RCL_LIFECYCLE_RET_OK  # 成功
RCL_LIFECYCLE_RET_FAILURE  # 失敗
RCL_LIFECYCLE_RET_ERROR  # 捕捉されない例外発生

publish()メソッドはタイマー呼び出しで実行されるPublishのためのメソッドです。

Subscriber実装

https://github.com/ros2/demos/blob/master/lifecycle/src/lifecycle_listener.cpp

を見ると、Subscriberはrclcpp::node::Nodeを使って実装しています。 これはROS 2ノードの基本クラスで、前回のデモのmain関数では、

auto node = rclcpp::node::Node::make_shared("listener");

という形で、このクラスをそのまま使っていましたが、後述する便利機能を使うため、継承して使用します。 こちらも、クラスのpublicメソッド宣言だけ抜き出しました。

class LifecycleListener : public rclcpp::node::Node
{
public:
  explicit LifecycleListener(const std::string & node_name);
  void data_callback(const std_msgs::msg::String::SharedPtr msg)
  void notification_callback(const lifecycle_msgs::msg::TransitionEvent::SharedPtr msg)
};

data_callback()メソッドは、トピック受信時に呼び出されるコールバックメソッドです。

もう一つのnotification_callback()メソッドが、ROS 2特有のコールバックメソッドで、ノードのライフサイクル遷移をトピックとして扱い、Subscribeした場合に呼び出されるメソッドです。

トピックのメッセージの型はlifecycle_msgs::msg::TransitionEventです。 これをコールバックメソッドで監視することで、例えば、ノードAがActive状態に遷移した後、ノードBはUnconfigured状態からActive状態に遷移する、といったノード起動の順序関係を制御、プログラミングすることができるようになります。

ROS 1では、このノードのライフサイクルという考え方がなかったため、ノードAが起動し終わるまで、ノードBの起動は少しsleep()を挟んで待って…、というような場当たり的な制御しかできませんでした。

実行

このデモの実行方法は

https://github.com/ros2/ros2/wiki/Managed-Nodes

に書いてますが、その方法ではPub/Subが始まりませんでした。おそらく情報が古いのでしょう。 深く原因は探っていませんが、

https://github.com/ros2/demos/blob/master/lifecycle/launch/lifecycle_demo_launch.py

というものがあり、これを実行すると同じように実行されました。

その過程で、launchモジュールを知ったのですが、これがroslaunchの代わりになっていくんでしょうか? 後で掘り下げて調べてみます。