ROS1 -> ROS2のポーティングときにハマった点のまとめ　その2

#はじめに
こんにちは、先日[こんな記事](https://qiita.com/hakuturu583/items/4e559ff72c290b39e4e6)を投稿したものです。
さらに作業を続けていて罠はコレだけじゃなかったっていうのが発覚したので第二報を出したいと思います。

# 罠その8 tf2_rosのインターフェースが変わっている
まず英語、日本語にかかわらずドキュメントがほぼありません。
強いて言うなら[コレ](https://index.ros.org//doc/ros2/Tutorials/tf2/)
[githubのリポジトリ](https://github.com/ros/geometry2)を確認してみてもdashingのブランチがない・・・・
でもnavigation2とかが来ていて座標変換ができてないわけないでしょうと思いつつaptのパッケージを確認してみたらどこから来たかはわからないがたしかにいる・・・・
とりあえずROS1と同じ感覚でtf2_ros::Bufferのインスタンスを作ろうとしたら

```
error: no matching function for call to ‘tf2_ros::Buffer::Buffer()’
```
APIが変わっているようなので、/opt/ros/dashing/以下にあるヘッダを探してコンストラクタを探す。

![Screenshot from 2019-10-16 10-34-45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/160346/7169197f-eccd-b350-661c-ce22d34c7bfe.png)

第一引数にClockオブジェクトが必要になっている！
なので、このようにコンストラクタ関数に初期化を書けば使うことができます

```cpp
    PointsTransformComponent::PointsTransformComponent(const rclcpp::NodeOptions & options)
    : Node("points_transform", options),
        ros_clock_(RCL_ROS_TIME), 
        buffer_(std::make_shared<rclcpp::Clock>(ros_clock_)),
        listener_(buffer_)
```

さらにLookupTransformのAPIも変わっている。

```cpp
    TF2_ROS_PUBLIC
    virtual geometry_msgs::msg::TransformStamped 
    lookupTransform(const std::string& target_frame, const std::string& source_frame,
                    const tf2::TimePoint& time, const tf2::Duration timeout) const override;
```
ros::Timeがrclcppではなくtf2::TimePointという独自型になっています。
ちなみにこの型はheaderのスタンプをいれたら変換してくれるというような甘えた仕様はありません:innocent:
tf2::Durationも同様です。
諦めて以下のようなコードを書いてtransformを取得しましょう。

```cpp
geometry_msgs::msg::TransformStamped transform_stamped = 
  buffer_.lookupTransform("lidar", "map", time_point, tf2::durationFromSec(1.0));
```

# 罠その9 node handler的なものの仕様が変わっている

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">ROS2、ノードハンドラ的なものを複数使うと裏でDDSのノードレジストレーション処理が走るらしく、同じ名前のノードが２つ爆誕するという知見を得た</p>&mdash; 片岡大哉 @ 技術書典 い23 (@hakuturu583) <a href="https://twitter.com/hakuturu583/status/1184142265607839746?ref_src=twsrc%5Etfw">October 15, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

ROS1ではnode handleというオブジェクトを経由してpublisher/subscriberを作成していました、このオブジェクトは何個生成しても生成時にノード作成処理を伴わないため、複数node handleを使ったノードを作ることができました。
しかし、ROS2でそれをやるとノード作成処理が走ってしまうようで同じ名前のノードが2つ発生します。
どんな副作用がでるかは未検証ですが、避けたほうが良いでしょう。

# 罠その10 subscriberのcallbackでコンパイルエラーがでる
publisherはチュートリアルの通りに作ることができたのだが、subscriberを作ろうとしたところ何故かエラーになる。
![Screenshot from 2019-10-16 10-19-36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/160346/45378093-a5dd-91d1-8dfc-f1b468e8b7a0.png)

```bash
libpoints_transform_component.so: `rosidl_message_type_support_t const* 
  rosidl_typesupport_cpp::get_message_type_support_handle<sensor_msgs::msg::PointCloud2_<std::allocator<void> > >()' に対する定義されていない参照です
```

std_msgs::msg::Stringに変更してみたところ、このエラーは出なくなった。
ライブラリリンクの問題のようだ
確認するとCMakeLists.txtに

```cmake:CMakeLists.txt
find_package(sensor_msgs REQUIRED)
```
を入れ忘れていた、どうもメッセージ型のパッケージをfind_packageし忘れていてもヘッダのインクルードはできてしまう仕様らしい:thinking_face:

# 罠その11　rclpy向けメッセージ型でコケる

同じパッケージの中でメッセージ生成+ノード作成をやろうとしたらハマった・・・
原因不明、同様の症状が報告されているものの解決策は書いていない。
rclpy向けのメッセージ生成でコケているようだ
https://answers.ros.org/question/330444/errors-during-messages-compilation-in-ros2/

メッセージ用のパッケージを分離してビルドするとうまく行く。謎
と思ってたら近藤さんから真相を教えてもらった、どうもrclpyでCMakeLists.txtなしでビルドできるようになった副作用らしい。
今後はメッセージは分離しよう

# 罠その12 std::threadでスレッドを作ると死ぬ
rclcpp componentの中でstd::threadを使ってスレッドを切った場合joinしないとスレッドが別れた瞬間にクラッシュ、joinするとjoinsした瞬間にクラッシュした。
一定周期で回す処理はtimerを使うことでなんとかなることが判明
ただ、OpenMPみたく高速化のためスレッドを切る場合とかは大丈夫なんだろうか・・・（未検証です・・・すみません。）

# まとめ

今回も前回に続いてかなり罠が見つかる結果となった（まあ、お世辞にもパッケージが揃っているとは言えない状況なのでしかたないと思うが・・・）
しかし、罠といってもちょっとお約束を守れば解決できそうなものがほとんどで、慣れればROS2のほうがROS1より圧倒的に人権であると感じる。
Let's start ROS2!!