Gazeboで自分で作った広い空間でNavigation2を実行する方法

# はじめに
　本記事のシチュエーションは、
- 実機のロボットを動作させる環境（実環境）の図面や大きさ寸法が予め既知
- 実環境で実機を頻繁に動かせない（展示会場、学会会場、人通りの多い校舎 .etc）
- 実環境で将来的にデモンストレーションや発表を行うにあたり、予め実環境でのシミュレーションを行う必要あり

を想定しています。ユースケースとしては稀かもしれませんが、どなたかのお役に立てれば幸いです。

# 実環境のモデリングからGazeboでの召喚
　筆者は以下の記事の手順を上からなぞることで、問題なく、自作の3DデータをGazeboでSpawnさせることができました。

https://zenn.dev/katsuitoh/articles/1ce402abed32bd

　重複するので詳しく説明はしませんが、モデリングしたデータを`.fbx`形式にして[Blender](https://www.blender.jp/)で読み込んでから、`.sdf`形式と`.dae`形式に出力します。
　そのうえで、適切なファイル構成や設定ファイルの編集を行います。

　今回は[Fusion360](https://www.autodesk.com/jp/products/fusion-360/overview?mktvar002=5022415|SEM|11655048452|162674806217|kwd-52461241111&term=1-YEAR&tab=subscription&utm_source=GGL&utm_medium=SEM&utm_campaign=GGL_D-M_Fusion-360_JAPAN_JP_eComm_SEM_BR_Mix_EX_0064_5022415_MainBrand&utm_id=5022415&utm_term=kwd-52461241111&gad_source=1&gclid=Cj0KCQjwrp-3BhDgARIsAEWJ6SxnKoGMxJS0mirGomD6kVOtOdhM5yK1jJA88GN9o82bqhkiHPCHd3YaAg_EEALw_wcB&plc=FSN)で以下のような壁と柱によって構成される実環境をモデリングしました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2035642/57c5a9fd-cd88-d4a1-2bb2-559e7211c91a.png)

　以下のlaunchのコードをベースにファイルの指定や初期位置の指定を行います。
　新しくワークスペースを建てるなり、ビルドして使っている方はファイルを同じ階層でコピペして編集するなりしましょう。

https://github.com/ROBOTIS-GIT/turtlebot3_simulations/blob/humble-devel/turtlebot3_gazebo/launch/turtlebot3_world.launch.py

　以下は変更箇所の一部抜粋です。

```custom_world.launch.py
use_sim_time = LaunchConfiguration('use_sim_time', default='true')
    x_pose = LaunchConfiguration('x_pose', default='8.0') #X座標の初期位置
    y_pose = LaunchConfiguration('y_pose', default='4.0') #Y座標の初期位置

    world = os.path.join(
        get_package_share_directory('turtlebot3_gazebo'),
        'worlds',
        'custom_world.world' #ワールドの指定
    )
```

　Turtlebot3のシミュレーション関連のLaunchで用意したモデルを召喚したのが以下の図です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2035642/d5d4e809-f1c8-083b-5038-502d58dad406.png)

# 自律移動のナビゲーション
　基本的な手順は[ROBOTIS E-manual](https://emanual.robotis.com/docs/en/platform/turtlebot3/slam_simulation/)に準拠しています。

## 実環境の地図生成
　E-manualと同じくCartographerを使用して地図を作っていきます。

### 問題点
　今回対象とした実空間はそこそこ広い空間であり、柱や壁と壁の距離が最大7mあり、全体は一辺20m近くあるスペースを使用しました。
そこで、発生したのが、**Cartographerで地図がうまく生成されない**、という問題です。

　具体的には、以下の画像のように広い空間で自己位置がロストしてしまっているのか、すでに推定した実環境の輪郭が徐々に乱れるという問題です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2035642/ae8d2821-f3b3-89e6-94b6-9f9ed3cdd68e.png)

　いくつかの懸念点を精査した結果、

1. **シミュレーション上のLidarの設定**
2. **Cartographerの設定**

を適切に調整することで地図を適切に処理することができました。

#### シミュレーション上のLidarの設定
　Gazeboに召喚されるTurtlebot3の設定については、turtlebot3_gazeboパッケージ内の[models](https://github.com/ROBOTIS-GIT/turtlebot3_simulations/tree/humble-devel/turtlebot3_gazebo/models)に集約されています。

　使用しているロボットモデルの`.sdf`ファイルを開き、以下の部分を編集します。
具体的にはLidarで取得する最大レンジと最小レンジです。
```model.sdf
<sensor name="hls_lfcd_lds" type="ray">
        <always_on>true</always_on>
        <visualize>true</visualize>
        <pose>-0.064 0 0.121 0 0 0</pose>
        <update_rate>5</update_rate>
        <ray>
          <scan>
            <horizontal>
              <samples>360</samples>
              <resolution>1.000000</resolution>
              <min_angle>0.000000</min_angle>
              <max_angle>6.280000</max_angle>
            </horizontal>
          </scan>
          <range>
            <min>0.20000</min> <!-- 元の値: 0.12000 -->
            <max>15.0</max> <!-- 元の値: 3.5 -->
            <resolution>0.015000</resolution>
          </range>
          <noise>
            <type>gaussian</type>
            <mean>0.0</mean>
            <stddev>0.01</stddev>
          </noise>
        </ray>
        <plugin name="turtlebot3_laserscan" filename="libgazebo_ros_ray_sensor.so">
          <ros>
            <!-- <namespace>/tb3</namespace> -->
            <remapping>~/out:=scan</remapping>
          </ros>
          <output_type>sensor_msgs/LaserScan</output_type>
          <frame_name>base_scan</frame_name>
        </plugin>
      </sensor>

```
　最小レンジを修正する必要はこの文脈では無いように思われますが、筆者が試したところ元の値のままだと、角度の一部分が測定できたりできなかったり、ノイズが走るような挙動が見られたりしました。
　これに対して、最小レンジを大きくすることで解決しました。

#### Cartographerの設定
　E-manualで使用している`cartographer.launch.py`では設定ファイルとして`turtlebot3_lds_2d.lua`がデフォルトで設定されているので、これの中身を編集します。

　具体的には、先ほどのTurtlebot3のLidarの設定と同様、採用するレンジの最大最小の設定です。
　以下はその一部抜粋です。

```turtlebot3_lds_2d.lua
TRAJECTORY_BUILDER_2D.min_range = 0.12
TRAJECTORY_BUILDER_2D.max_range = 15.0
TRAJECTORY_BUILDER_2D.missing_data_ray_length = 3.
TRAJECTORY_BUILDER_2D.use_imu_data = false
TRAJECTORY_BUILDER_2D.use_online_correlative_scan_matching = true 
TRAJECTORY_BUILDER_2D.motion_filter.max_angle_radians = math.rad(0.1)
```
　この中の`TRAJECTORY_BUILDER_2D.min_range`と`TRAJECTORY_BUILDER_2D.max_range`の編集を行いました。

　ここまで出来たら、Turtlebot3パッケージのビルドを忘れずに行いましょう。
```
$ cd (PATH_TO_TB3_WORKSPACE)
$ colcon build --symlink-install
```

　無事、うまくいけば、Gazebo上で可視化されているLidarの光線が指定した分延長されているのが確認できるはずです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2035642/67fe2e82-8c1a-e72e-3a12-e698210d9e01.png)


### 完成したもの
　いくつかの設定を適切にしたうえで、Cartographerを実行しました。

```
ros2 launch turtlebot3_cartographer cartographer.launch.py use_sim_time:=True
```

　Rviz2上の推定範囲が設定に伴って大きくなっていることが確認できるはずです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2035642/c6272b1b-cb3e-9eef-01a8-44a7b3315bd6.png)

　地図の保存は以下のノードから実行します。
```
ros2 run nav2_map_server map_saver_cli -f ~/map
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2035642/d12aad19-334a-9dca-11fd-c002dfcdabaf.png)


### ナビゲーション
　地図まで作成できれば、あとはNavigation2のチュートリアルと同様です。

```
$ ros2 launch nav2_bringup bringup_launch.py use_sim_time:=True map:=$HOME/map.yaml
```

```
$ ros2 run rviz2 rviz2 -d $(ros2 pkg prefix nav2_bringup)/share/nav2_bringup/rviz/nav2_default_view.rviz
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2035642/a8deafc5-7dd3-411d-a8fa-16e8e81fdffe.png)


# 補足
　今回は実環境を模した広めの空間をシミュレーション上でマッピングするにあたり、Lidarの能力を底上げするかたちで無理やり解決したかたちになっています。
　デフォルトのLidarの設定は最大半径3.5mに設定されており、筆者の所持しているLidarは最大半径8mなので、地図が取れ次第、実機のパラメータに調整する必要があると思っています。

https://emanual.robotis.com/docs/en/platform/turtlebot3/appendix_lds_02/

# 参考
https://zenn.dev/katsuitoh/articles/1ce402abed32bd

https://qiita.com/srs/items/18291a49cbb278525b47

https://docs.nav2.org/