ロボット開発記録

ロボット開発の進捗や記録をするブログ

続・ソフトウェア構成とコードの整理Ver2 [ヒューマノイド動歩行]

前回の振り返り

 前回の記事では、ソフトウェアアーキテクチャを再考案しました。今回も引き続き、ソフトウェアアーキテクチャに取り組んでいきます。

odome.hatenablog.com

今回の概要

 今回は前回に引き続き、ソフトウェアアーキテクチャを考えていきます。前回は旧アーキテクチャの問題点と、それを改善できるであろう新アーキテクチャを示しました。今回は追加で発生した問題とそれに応じた新アーキテクチャを示します。

新たに発生した問題

ソフトウェアアーキテクチャVer2

 前回考案した新しいアーキテクチャ(Ver2)への移行をする途中で、一つの問題が発生しました。それは、同期Service通信の中で、もう一つ別の動機Service通信を行うと、処理が完全に止まってしまう問題です。具体的には、上図での、Webots Robot Handler , Robot Manager間の同期Service通信の中で Robot Manager , Walking Pattern Generator や Robot Manager, Walking Stabilization Controller 間の同期Service通信を実行すると、Requestは送れてもResponseが全く帰ってこない状態になります。
 この問題は、アーキテクチャVer2ではService通信のみを用いてシーケンシャルな構成をとっていることから、非常によろしくありません。早急に解決するべき問題です。

問題とその解決方法の調査

 この問題について、先行事例がないか少し調べてみたところ、やはりいくつかissueや質問が確認できました。以下に、参考になるものをいくつか挙げます。

Calling a Service from an other Service leading to a std::runtime_error · Issue #312 · ros2/rclcpp · GitHub
Exception when using parameterclient while having node spinning in other thread · Issue #206 · ros2/rclcpp · GitHub
How to call spin_some or spin_until_future_complete in member functions - ROS Answers: Open Source Q&A Forum
[ROS2] How to implement a sync service client in a node? - ROS Answers: Open Source Q&A Forum
rclcpp (ROS Client Library for C++) を読み解く (2) | Yutaka Kondo
Using Callback Groups — ROS 2 Documentation: Humble documentation

以上の記事などから、よわよわ英語力と推測マシマシで考えると、

  • "spin"は、1つのスレッドに1つしか存在できない。
  • 複数の"spin"を1つのnodeで使いたいときには、MultiThreadExecutorでnodeをマルチスレッドで動かす。
  • 複数の"spin"を持つ状態で同期Service通信を行う場合は、spin_until_future_complete を使わずに、future型のや.wait_for()などで擬似的に同期を取る。
  • 複数のCallback を用いるときには、Callback Groupを分ける必要がある。デフォルトだと同一の相互排他的Callback GroupでCallbackを回そうとしてしまう。
  • Callback Groupは、3つのTypeが存在する。デフォルト(nullptr)と、上でも挙げたこちらの記事で挙げられている2つ。
  • main関数においてexecutorやspinに渡す引数(nodeを定義したclass)は、一旦別変数にmake_sharedに渡して、それを用いる必要がある。


まとめると、

  • 複数のspinを持つnodeは、spin_until_future_completeではなくwait_for()などで同期をとり、MultiThreadExecutorを用いてnodeをマルチスレッドで動かし、すべてのCallbackのCallback Groupを分ける必要がある。


と解釈しました。
 この解決方法を実際に試してみました。ただ今回はService内でのServiceではなく、上でも挙げた公式のドキュメントを参考に、create_wall_timerで周期的に実行される関数内で同期Service通信を行いました。なお、Callback GroupのTypeはservice, create_wall_timerどちらも Reentrant としました。
 結果、成功しました。他の先行事例を見ると、大体同様の方法でService内Serviceを行っているので、恐らくこの方法で問題は解決できると考えられます。参考に、成功したcommitを以下に示します。robot_manager/src/ と walking_stabilization_controller/src/ 内のプログラムを用いて行いました。

github.com

問題の解決方法の試用と新アーキテクチャVer4

 発生した問題の解決方法とアーキテクチャの改善を考えて、ソフトウェアアーキテクチャVer4を考案しました。Ver4への過程で生まれたVer3も含めて、以下に示します。

ソフトウェアアーキテクチャVer3

ソフトウェアアーキテクチャVer4

 Ver2と大きく変わった点としては、歩行パターンを生成するnodeが制御周期の外に出た点が挙げられます。制御周期の外に出した理由としては、歩行パターンの生成にはFeedbackを用いないので、毎回わざわざService通信で歩行パターンを生成するよりも、数秒分の歩行パターンを一気に生成してまとめてマネージャに送り、そこから歩行パターンを参照してロボットに適用したほうがオーバーヘッドが少なく済むと考えたためです。こうすることで、制御周期内で行う処理が減り、処理を10[ms]以内に収めやすくなりました。
 また、コントローラとマネージャとの通信をTopic通信に変更し、制御周期をマネージャ内で指定するようにしました。こうすることで、マネージャとコントローラの関係をより疎にし、コントローラを変更しやすくしました。
 

 現在はこのVer4を実装する方向で考えています。ただ、Ver2のほうがシーケンシャルで考えやすいアーキテクチャをしているので、状況に応じて臨機応変に対応しようと考えています。

 ソフトウェアアーキテクチャの移行作業は、以下の tidy-up branch で行っています。

github.com

まとめ

 今回は前回に引き続き、ソフトウェアアーキテクチャの改善を行いました。同期Service通信内で同期Service通信ができない問題が発生しましたが、その解決策となり得る情報を得、実際にそれが解決できるであろう方法を実装できることを確認しました。この解決方法やさらなる改善を考え、ソフトウェアアーキテクチャVer3とVer4を示しました。
 今後はソフトウェアアーキテクチャVer4を実装する方向で進めていきます。ただ最近、理論の方をほったらかしにしていましたので、特に6月からは動歩行の実装のほうに取り組んでいきます。