meta data for this page
差分
このページの2つのバージョン間の差分を表示します。
— |
lab:ros:add-arguments-to-callback [2020/06/02 20:48] (現在) strv 作成 |
||
---|---|---|---|
行 1: | 行 1: | ||
+ | # ROSのコールバック関数、まとめたくない? | ||
+ | |||
+ | ROS は情報を受け取ろうと思うと基本的には Topic をコールバック関数で受け取ることになります。このコールバック関数はもともと定義されている型しか使えないから、ほとんど似たような処理でも Topic ごとに関数が必要になって煩雑!とか思ってませんか? | ||
+ | 実はそれ、おまとめ出来るんですよ。そう、```boost:: | ||
+ | |||
+ | ※ 相変わらず筆者はkineticを使っているので、以降の文章はすべてkineticでのみ確認しています。 | ||
+ | |||
+ | # boost::bind | ||
+ | |||
+ | ```boost:: | ||
+ | この機能により、指定された形しか許されていないコールバック関数を別の形の関数で定義して、呼び出せるようになります。 | ||
+ | |||
+ | 詳しくは[公式ドキュメント](https:// | ||
+ | なお、C++11以降ではC++の標準ライブラリにほぼ同等の機能が移植されており、```std:: | ||
+ | |||
+ | ROSで使うためには、パッケージの ```CMakeLists.txt``` に ```find_package(Boost REQUIRED COMPONENTS system)``` を追加し、プログラム中で ```boost/ | ||
+ | |||
+ | # Topic のコールバック | ||
+ | |||
+ | ## サンプル | ||
+ | |||
+ | 早速例を示しましょう。 | ||
+ | |||
+ | ```c | ||
+ | #include < | ||
+ | #include < | ||
+ | #include < | ||
+ | |||
+ | #include < | ||
+ | |||
+ | void commonCb(const std_msgs:: | ||
+ | { | ||
+ | ROS_INFO(" | ||
+ | } | ||
+ | |||
+ | class hoge | ||
+ | { | ||
+ | public: | ||
+ | hoge() | ||
+ | { | ||
+ | sub_m1_ = nh.subscribe< | ||
+ | 1, | ||
+ | boost:: | ||
+ | sub_m2_ = nh.subscribe< | ||
+ | 1, | ||
+ | boost:: | ||
+ | } | ||
+ | |||
+ | void methodCb(const std_msgs:: | ||
+ | { | ||
+ | ROS_INFO(" | ||
+ | } | ||
+ | |||
+ | private: | ||
+ | ros:: | ||
+ | ros:: | ||
+ | ros:: | ||
+ | }; | ||
+ | |||
+ | int main(int argc, char** argv) | ||
+ | { | ||
+ | ros:: | ||
+ | ros:: | ||
+ | ros:: | ||
+ | 1, | ||
+ | boost:: | ||
+ | ros:: | ||
+ | 1, | ||
+ | boost:: | ||
+ | hoge h; | ||
+ | ros:: | ||
+ | 1, | ||
+ | boost:: | ||
+ | |||
+ | ros:: | ||
+ | return 0; | ||
+ | } | ||
+ | ``` | ||
+ | |||
+ | このコードは、文字列を扱う Topic を受け取ってコンソールに表示するというだけの例です。 | ||
+ | コールバック関数がクラス内のメソッドとして定義されている場合と、そうでない場合とで若干呼び出し方が異なるので、それぞれの場合を実装してあります。 | ||
+ | Boostのライブラリは心の目で見ると読める、などと偉大な先輩はおっしゃっておられましたが、これもなんとなく見ればわかるんじゃないかと思います。 | ||
+ | 普段、subscribe関数のコールバック関数を指定する引数ところにboost:: | ||
+ | boost:: | ||
+ | それ以降の引数が、関数に渡される引数となっており、```_1``` のように```_```から始まるものがPlaceholderと呼ばれる特殊な値であり、「もともとの呼び出し元の引数のN個目」が```_N```として表現されます。 | ||
+ | |||
+ | 簡単な例にすると | ||
+ | |||
+ | ```c | ||
+ | int f(int a, int b, int c) | ||
+ | { | ||
+ | return a + b + c; | ||
+ | } | ||
+ | ``` | ||
+ | |||
+ | という引数三個の関数を引数二個として呼びたい、与えた二個の引数はそのままに、引数の3番目には常に0を与えたい、という場合には | ||
+ | |||
+ | ```c | ||
+ | boost:: | ||
+ | ``` | ||
+ | |||
+ | とします。バリエーションで、同じく引数二個の関数だけど、与えた引数1番目を実行関数の3番目に、2番目を1番目に入れ替えて、2番目には常に10を渡したい、という場合には | ||
+ | |||
+ | ```c | ||
+ | boost:: | ||
+ | ``` | ||
+ | |||
+ | となります。このように、Placeholderは元の引数の順番や位置に依存せずに使うことができます。 | ||
+ | |||
+ | と言っても無意味に順番を入れ替えても混乱するだけなので、今回のROSのサンプルでは通常のコールバック関数で受け取る引数のあとに追加のint引数を作り、subscriberを作るときに定数を与えています。 | ||
+ | |||
+ | 注意が必要なのは、boost:: | ||
+ | |||
+ | サンプルを実行して、トピックを与えてやると、こんな感じの結果になります。 | ||
+ | |||
+ | ```bash | ||
+ | rosrun cb_bind_example cb_bind_example | ||
+ | [ INFO] [1576593517.940507359]: | ||
+ | [ INFO] [1576593529.864747527]: | ||
+ | [ INFO] [1576593548.404552068]: | ||
+ | ``` | ||
+ | |||
+ | ```bash | ||
+ | $ rostopic list | ||
+ | /rosout | ||
+ | /rosout_agg | ||
+ | /sub1 | ||
+ | /sub2 | ||
+ | /sub3 | ||
+ | /sub_m1 | ||
+ | /sub_m2 | ||
+ | ``` | ||
+ | |||
+ | ```bash | ||
+ | $ rostopic pub /sub1 std_msgs/ | ||
+ | publishing and latching message. Press ctrl-C to terminate | ||
+ | $ rostopic pub /sub2 std_msgs/ | ||
+ | publishing and latching message. Press ctrl-C to terminate | ||
+ | $ rostopic pub /sub_m1 std_msgs/ | ||
+ | publishing and latching message. Press ctrl-C to terminate | ||
+ | ``` | ||
+ | |||
+ | sub1とsub2でコールバック関数は一つなのに異なる処理ができていることがわかると思います。また、メソッドの場合も正しく呼ぶことができています。 | ||
+ | |||
+ | # Service のコールバック | ||
+ | |||
+ | 当然ながらこの手法は Topic のコールバックだけに限りません。サービスでも利用可能です。 | ||
+ | |||
+ | 筆者が作ってる中華IMUのROSドライバを例にします。 | ||
+ | https:// | ||
+ | |||
+ | このIMUは固定のバイナリ配列を送信することで各種キャリブレーションをトリガすることができます。それをROSのServiceとして実装したのですが、実際に必要なのはシリアルポートにデータを送るということが共通で、送る中身が異なるだけ、しかも送る内容は固定であるため、Topicの例のように関数はひとつにして、送る中身のデータを予め与えるようにしてみました。 | ||
+ | |||
+ | 送信に使うServiceのコールバック関数は下記のように定義しています。 | ||
+ | |||
+ | ```c | ||
+ | bool cbSrvTrgWriteCommand(std_srvs:: | ||
+ | , std_srvs:: | ||
+ | , const std:: | ||
+ | { | ||
+ | bool ret = sendBytes(bytes); | ||
+ | if (ret) | ||
+ | { | ||
+ | res.message = " | ||
+ | res.success = true; | ||
+ | } | ||
+ | else | ||
+ | { | ||
+ | res.message = " | ||
+ | res.success = false; | ||
+ | } | ||
+ | return true; | ||
+ | } | ||
+ | ``` | ||
+ | |||
+ | これを登録している部分が下記のようになります。 | ||
+ | |||
+ | ```c | ||
+ | srv_trg_yaw_clr_ = pnh_.advertiseService< | ||
+ | " | ||
+ | boost:: | ||
+ | & | ||
+ | this, | ||
+ | _1, | ||
+ | _2, | ||
+ | ptr_imu_-> | ||
+ | srv_trg_acc_cal_ = pnh_.advertiseService< | ||
+ | " | ||
+ | boost:: | ||
+ | & | ||
+ | this, | ||
+ | _1, | ||
+ | _2, | ||
+ | ptr_imu_-> | ||
+ | ``` | ||
+ | |||
+ | この他にも何種類か同じ関数を利用しており、関数をまとめるメリットが大きいのです。 | ||
+ | |||
+ | # まとめ | ||
+ | |||
+ | boost::bind 超便利。けど、「C++完全に理解した」じゃないとちゃんとは理解できない。 | ||
+ | 別にboost:: | ||
+ | |||
+ | # 参考文献 | ||
+ | |||
+ | この記事を書き始めてから気がついたのですが、fuRoの原さんがこの辺も含めて超丁寧に説明されているスライドを公表してくださっておりました。P.68以降、数ページにわたって解説されているので、まずはそっちを読みましょう!! | ||
+ | https:// | ||