rclcpp code reading 1 (Nodeの起動とSingleThreadedExecutor)
はじめに
rclcppのコードリーディングをしています。
基本的にはardent branchのものを読んでいます。(コード量が少ないのと、把握してから変更点を追いかける方針)
なんちゃってロボットエンジニアなので間違っていれば教えてください。
コードは下記から引用しております。
Nodeの起動について
ros tutorialのlistener.cppを見てみる。
- init
- nodeをつくって
- spin(node)
という流れ。
initについて
utilities.hpp/cppにて実装
詳しくは、そのうち書きたい。。。
とりあえず
init => rcl_initでrclを初期化して、シグナルハンドラ類を設定
spinについて
executors.hpp/cppに実装があります。SingleThreadExecutorにnodeを渡して、executorのspinに処理を渡します。executorはNodeBaseInterface::SharedPtrを保持しますが、Node::SharedPtrを受け取れるよう、ラッパが書いてありますね。
Executor
SingleThreadedExecutor
Executorを継承していて、rclcpp::okかつspinningがtrueの間、
実行可能なコールバックを取ってきて、実行するというもの。
注1
spinningはstd::atomic<bool>なので,exchange/store/loadとか書いているが
一旦はただのboolだと思ってもらってもいいです。(怒られそう)
if(spinning.exchange(true)) は
prev_spinning = spinning
spinning = true
if(prev_spinning)
要するに、spinning.exchenge(true)はspinningをtrueに変更する。返り値は変更する前の値。
spinning.load は spinning
spinning.store(false) は spinning = false
注2
面白いのが、RCLCPP_SCOPE_EXITですね。スコープを抜けたとき(上の場合spin関数を抜けるとき)にthis->spinning.store(false) が実行されます。
get_next_executable()
細かいところは省略しますが、
get_next_executable => get_next_ready_executableが呼ばれます。
要するに何かしら処理するものがあったらptr、なければnullptrを返します。
AnyExecutable型は↓のようになっており、
複数持ってるptrどれか一つのみが有効になるようなクラスのようです。
そして、execute_any_executableで実際に実行されますが、
AnyExecutable型の中で有効なptrに対し、関数を呼び分けているだけですね。
timerの実行
TimerBaseのコールバックを呼んでます。
subscriptionの実行
rcl側からメッセージを取得して。SubscriptionBaseのhandle_messageを呼んでます。
そして、any_call_back_.dispatchしており、ここではこれ以上深堀しませんが、
create_subscription時に登録した、cbが呼ばれます。
serviceの実行
rcl側からrequestを取り出して、サービスを実行します。
rcl側からresponseを取り出して、コールバックを起動
ここは、少し複雑なので、別に調べます。
ざっくりと
executorにnodeを渡せば、実行できそうなもの取ってきて、いい感じに実行してくれる。
コメント
排他実行の仕組みや、どうやってrcl側から、readyが伝わるのかなど、
もっと深堀が必要な部分がたくさんありますが。
なんとなくnodeの中身の動きが分かってきたような気がします。
次は、インターフェースクラス周りを読み進めてみようと思います。
Discussion