自作ロボットのCADファイルを読み込んでgazeboに表示する

# 概要
### gazeboとは
　gazeboとは物理シミュレーションを行うための(今では)ROS2と独立したツールです。しかし、元々ROS2付属のシミュレーターだった経緯があり、ROS2との連携が比較的簡単に行えるようになっています。
gazeboを用いれば本番用のROS2コードで、制御プログラムの挙動をシミュレーション上で確認できます。これには以下のメリットがあります。
・機体が手元になくてもある程度の開発・デバッグができる
・バグまみれのプログラムを実行してしまって実機を壊すリスクを抑えられる
・実際に作成するのは難しいロボットでもシミュレーションして楽しめる
### この記事の内容
　この記事ではgazeboはじめましての人を想定して、gazeboのインストール、開発環境の構築から実機のCADデータを読み込んでgazebo上に表示するまでを説明します。ただし、[公式ドキュメント](https://gazebosim.org/docs/fortress/getstarted)と重複する内容は基本的に書きませんーその都度公式ドキュメントへのリンクを貼ります。また、本記事を完了して実際に自分自身の開発を始める前に、**公式ドキュメントに目を通しておくことー少なくとも目次を見て、何が書いてあるのかを何となく把握しておくこと、を強く推奨します。**
この記事で扱った内容を実装したコードは[私のgithub](https://github.com/OsawaKousei/2DRoboPrac_ws)に公開しています。今回の内容に相当する部分はリンク先のレポジトリの`gz_ws/MakeYourRobot`に収められています。
また、記事の内容に誤りや不備等ございましたら、ご指摘いただけると幸いです。
### 大まかな手順
　gazeboをインストールし、起動を確認します。その後、簡単なsdfファイルを書いてワールドを表示してみます。それが終わったら、自作ロボットのCADファイルを生成し、シミュレーションで用いるdaeファイルを作成します。
最後に、それらのファイルをgazeboで読み込んで正常に表示されることを確認して終了です。
### 注意事項
　gazeboには少々厄介なバージョンの問題が存在します。冒頭でも触れましたが、gazeboは元々ROS付属のシミュレーターです。しかし、ROS２の開発が始まった段階でgazeboもROSから独立したシミュレーターとして開発されるようになりました。前者をclassic gazebo、後者をignition gazeboと呼んで区別することもあります。この区別は便宜的なものではなく、classicとignitionの間には(少なくとも公式には)互換性がありません。そのため、ネットでgazeboの記事を漁る場合はgazeboのバージョンに気をつける必要があります。ただし、classicは開発が終了していますが、ROS2と連携させて使うこともできます。実際ROS2への移行に比べて、ignitionへの移行は進んでいない印象を受けます。
　もう一つの問題点は、ignition同士のバージョンの問題です。ignitionでは、古いバージョンと新しいバージョンでパッケージの名前が異なっています。古いバージョンではメッセージ型の指定の際に、`ignition.msgs.Twist`のように指定するのですが、新しいバージョンでは`gz.msgs.Twist`と指定するようです。他にもパッケージの呼び出しなども異なっています。しかし、これら違いは形式的なもので、呼び出す際の名前に気をつければ基本的にどのバージョンでも同じように使用することができるはずです。注意すべきは、少なからず公式のドキュメントもこの区別を混同している箇所が見られるということです。旧バージョンのチュートリアルに、新バージョンの記法を用いて書かれたコードへのリンクが貼ってあることがあります。少なくとも私の環境では、コピペしたコードは適切な名称変換を行うまで動作ないことがありました。(2023/12.2)。

# 動作環境・バージョン
### 動作環境
Ubuntu22.04　：デュアルブートした実機PC
VScode　：コーディングとターミナルの操作は基本的にVScodeで行った
### バージョン
ROS2 : humble　（この記事ではROS2は使いません）
gazebo : ignition fortless　：ROS2公式がhumbleとの連携相手として推奨するバージョン

# 前提条件
以下のような読者を想定して記事を執筆しました
・上記の動作環境またはそれと同等の機能を持つ環境を有すること
・ 基本的なubuntuの操作が理解できること
・ROS2についての基本的な知識があること(必要ではないが推奨)

# コンテンツ
## gazeboの環境構築
　先ず、gazeboをインストールしましょう。機械研ではgazeboはROS2と連携させて使うことが殆どですから、ROS2との相性に気を使う必要があります。[ROS2 humble公式ドキュメント](https://docs.ros.org/en/humble/Tutorials/Advanced/Simulators/Gazebo/Gazebo.html)では、[こちらのリンク](https://github.com/gazebosim/ros_gz)を参照するように書かれており、gazeboのバージョンとしてfortlessとgardenが選択可能なように見えます。しかし、[gazebo公式ドキュメント](https://gazebosim.org/docs/fortress/ros_installation)を見るとgardenは非推奨となっているので、fortless一択です。
インストールは[公式ドキュメント](https://gazebosim.org/docs/fortress/install_ubuntu)に従ってください。
インストール手順を完了したら、`ign gazebo --version`でインストールを確認してください。
私の環境では`Gazebo Sim, version 6.15.0`と表示されます。
　インストールが確認できたら、開発環境の準備に取り掛かりましょう。どんなエディターを使っても問題ないはずですが、こだわりのない人にはVScodeをオススメします。[VScodeでROS2の開発をする](url)(工事中)を参考にVScodeを準備してください。
　それができたら適当なワークスペースを作成してください。ROS2のワークスペースが既に存在する人はその中に新しいパッケージを作成するのもいいと思いますーどの道いずれそうすることになります。そこに`building_robot.sdf`というファイルを作成して、以下の内容をコピペしてください。
<details><summary>参考コード</summary><div>

```building_robot.sdf
<?xml version="1.0" ?>
<sdf version="1.8">
    <world name="car_world">
        <physics name="1ms" type="ignored">
            <max_step_size>0.001</max_step_size>
            <real_time_factor>1.0</real_time_factor>
        </physics>
        <plugin
            filename="ignition-gazebo-physics-system"
            name="ignition::gazebo::systems::Physics">
        </plugin>
        <plugin
            filename="ignition-gazebo-user-commands-system"
            name="ignition::gazebo::systems::UserCommands">
        </plugin>
        <plugin
            filename="ignition-gazebo-scene-broadcaster-system"
            name="ignition::gazebo::systems::SceneBroadcaster">
        </plugin>

        <light type="directional" name="sun">
            <cast_shadows>true</cast_shadows>
            <pose>0 0 10 0 0 0</pose>
            <diffuse>0.8 0.8 0.8 1</diffuse>
            <specular>0.2 0.2 0.2 1</specular>
            <attenuation>
                <range>1000</range>
                <constant>0.9</constant>
                <linear>0.01</linear>
                <quadratic>0.001</quadratic>
            </attenuation>
            <direction>-0.5 0.1 -0.9</direction>
        </light>

        <model name="ground_plane">
            <static>true</static>
            <link name="link">
                <collision name="collision">
                <geometry>
                    <plane>
                    <normal>0 0 1</normal>
                    </plane>
                </geometry>
                </collision>
                <visual name="visual">
                <geometry>
                    <plane>
                    <normal>0 0 1</normal>
                    <size>100 100</size>
                    </plane>
                </geometry>
                <material>
                    <ambient>0.8 0.8 0.8 1</ambient>
                    <diffuse>0.8 0.8 0.8 1</diffuse>
                    <specular>0.8 0.8 0.8 1</specular>
                </material>
                </visual>
            </link>
        </model>

        <model name='vehicle_blue' canonical_link='chassis'>
            <pose relative_to='world'>0 0 0 0 0 0</pose>   <!--the pose is relative to the world by default-->

            <!--chassis-->
            <link name='chassis'>
                <pose relative_to='__model__'>0.5 0 0.4 0 0 0</pose>
                <inertial> <!--inertial properties of the link mass, inertia matix-->
                    <mass>1.14395</mass>
                    <inertia>
                        <ixx>0.095329</ixx>
                        <ixy>0</ixy>
                        <ixz>0</ixz>
                        <iyy>0.381317</iyy>
                        <iyz>0</iyz>
                        <izz>0.476646</izz>
                    </inertia>
                </inertial>
                <visual name='visual'>
                    <geometry>
                        <box>
                            <size>2.0 1.0 0.5</size>
                        </box>
                    </geometry>
                    <!--Color of the link-->
                    <material>
                        <ambient>0.0 0.0 1.0 1</ambient>
                        <diffuse>0.0 0.0 1.0 1</diffuse>
                        <specular>0.0 0.0 1.0 1</specular>
                    </material>
                </visual>
                <collision name='collision'>
                    <geometry>
                        <box>
                            <size>2.0 1.0 0.5</size>
                        </box>
                    </geometry>
                </collision>
            </link>

            <!--Left wheel-->
            <link name='left_wheel'>
                <pose relative_to="chassis">-0.5 0.6 0 -1.5707 0 0</pose> <!--angles are in radian-->
                <inertial>
                    <mass>1</mass>
                    <inertia>
                        <ixx>0.043333</ixx>
                        <ixy>0</ixy>
                        <ixz>0</ixz>
                        <iyy>0.043333</iyy>
                        <iyz>0</iyz>
                        <izz>0.08</izz>
                    </inertia>
                </inertial>
                <visual name='visual'>
                    <geometry>
                        <cylinder>
                            <radius>0.4</radius>
                            <length>0.2</length>
                        </cylinder>
                    </geometry>
                    <material>
                        <ambient>1.0 0.0 0.0 1</ambient>
                        <diffuse>1.0 0.0 0.0 1</diffuse>
                        <specular>1.0 0.0 0.0 1</specular>
                    </material>
                </visual>
                <collision name='collision'>
                    <geometry>
                        <cylinder>
                            <radius>0.4</radius>
                            <length>0.2</length>
                        </cylinder>
                    </geometry>
                </collision>
            </link>

            <!--The same as left wheel but with different position-->
            <link name='right_wheel'>
                <pose relative_to="chassis">-0.5 -0.6 0 -1.5707 0 0</pose> <!--angles are in radian-->
                <inertial>
                    <mass>1</mass>
                    <inertia>
                        <ixx>0.043333</ixx>
                        <ixy>0</ixy>
                        <ixz>0</ixz>
                        <iyy>0.043333</iyy>
                        <iyz>0</iyz>
                        <izz>0.08</izz>
                    </inertia>
                </inertial>
                <visual name='visual'>
                    <geometry>
                        <cylinder>
                            <radius>0.4</radius>
                            <length>0.2</length>
                        </cylinder>
                    </geometry>
                    <material>
                        <ambient>1.0 0.0 0.0 1</ambient>
                        <diffuse>1.0 0.0 0.0 1</diffuse>
                        <specular>1.0 0.0 0.0 1</specular>
                    </material>
                </visual>
                <collision name='collision'>
                    <geometry>
                        <cylinder>
                            <radius>0.4</radius>
                            <length>0.2</length>
                        </cylinder>
                    </geometry>
                </collision>
            </link>

            <!--arbitrary frame-->
            <frame name="caster_frame" attached_to='chassis'>
                <pose>0.8 0 -0.2 0 0 0</pose>
            </frame>

            <!--caster wheel-->
            <link name='caster'>
                <pose relative_to='caster_frame'/>
                <inertial>
                    <mass>1</mass>
                    <inertia>
                        <ixx>0.016</ixx>
                        <ixy>0</ixy>
                        <ixz>0</ixz>
                        <iyy>0.016</iyy>
                        <iyz>0</iyz>
                        <izz>0.016</izz>
                    </inertia>
                </inertial>
                <visual name='visual'>
                    <geometry>
                        <sphere>
                            <radius>0.2</radius>
                        </sphere>
                    </geometry>
                    <material>
                        <ambient>0.0 1 0.0 1</ambient>
                        <diffuse>0.0 1 0.0 1</diffuse>
                        <specular>0.0 1 0.0 1</specular>
                    </material>
                </visual>
                <collision name='collision'>
                    <geometry>
                        <sphere>
                            <radius>0.2</radius>
                        </sphere>
                    </geometry>
                </collision>
            </link>

            <!--left wheel joint-->
            <joint name='left_wheel_joint' type='revolute'>
                <pose relative_to='left_wheel'/>
                <parent>chassis</parent>
                <child>left_wheel</child>
                <axis>
                    <xyz expressed_in='__model__'>0 1 0</xyz> <!--can be descired to any frame or even arbitrary frames-->
                    <limit>
                        <lower>-1.79769e+308</lower>    <!--negative infinity-->
                        <upper>1.79769e+308</upper>     <!--positive infinity-->
                    </limit>
                </axis>
            </joint>

            <!--right wheel joint-->
            <joint name='right_wheel_joint' type='revolute'>
                <pose relative_to='right_wheel'/>
                <parent>chassis</parent>
                <child>right_wheel</child>
                <axis>
                    <xyz expressed_in='__model__'>0 1 0</xyz>
                    <limit>
                        <lower>-1.79769e+308</lower>    <!--negative infinity-->
                        <upper>1.79769e+308</upper>     <!--positive infinity-->
                    </limit>
                </axis>
            </joint>

            <!--caster wheel joint--> <!--pose defult value is the child-->
            <joint name='caster_wheel' type='ball'>
                <parent>chassis</parent>
                <child>caster</child>
            </joint>
        </model>
    </world>
</sdf>
```

</div></details>

それができたら、ターミナルでワークスペースに移動してーVScodeを使用していれば無駄な`cd`コマンドを打たなくてよいです。`ign gazebo building_robot.sdf`を実行してください。
gazeboが立ち上がり、以下のように表示されれば成功です。
<img width="1920" alt="Screenshot from 2023-12-02 19-46-51.png (281.8 kB)" src="https://img.esa.io/uploads/production/attachments/19744/2023/12/02/148658/1218c76e-d9a4-47d9-810f-4aa4dad132fd.png">

## sdfファイルを書く
### sdfについて
　さて、gazeboの環境構築が済んだら、今度は実際に自らの手でsdfファイルを書いてシミュレーションを行いましょう。ただ、この記事の読者はsdf is 何？の人も多いと思うので、その辺を軽く解説します。sdfファイルとはgazeboでシミュレーションするワールド(世界)の情報をXML形式で記述したファイルです。XML形式については調べれば幾らでも資料が出てくるのでここでは割愛します。gazeboは基本的にこのsdfファイルを読み込んで、シミュレーションを実行していきます。VScodeではXMLの拡張機能が存在するので、入れておくと良いでしょうーフォーマットの誤りなどを指摘してくれます。ただし、拡張子が`.sdf`である関係で、そのままではXMLとしてVScodeが認識してくれないので、`.vscode/setting.json`に以下の文言を追記しておく必要があります。
```
"files.associations": {
        "*.sdf":"xml"
    },
```
### 補足：urdf
　ロボットのモデルを記述するためによく使われるもう一つのファイル形式にurdfというものがあります。これはsdf同様XMLフォーマットで記述され、主にrvizという可視化ツールを用いる際に要求されます。SLAMやnavigationをROS2で簡単に行うにはrvizは必須ですので、ある程度はurdfについての知見もあった方がよいです([urdfの資料](https://docs.ros.org/en/humble/Tutorials/Intermediate/URDF/Building-a-Visual-Robot-Model-with-URDF-from-Scratch.html))。しかし、gazeboはsdfをトレースしてurdfにしてくれる機能ー完全ではなく、ballタイプのjointやsensorタグなどは無視されますが、を有しており、差し当たりurdfを知らなくても問題はありません。
### sdfの書き方
　sdfの記法については[gazeboの公式チュートリアル](https://gazebosim.org/docs/fortress/tutorials)がかなり詳しいのでそちらを読んでください。ただ、少々全体像が見えづらいのと、冗長なので、全体像と最低限見るべき箇所を説明します。
    XMLは`<tag></tag>`で囲んだブロックを組み合わせてーネストすることも可能です、書かれます。第原則として、sdfでは以下の大枠が存在します。
```
<?xml version="1.0" ?>
<sdf version="1.8">
    <world name="hoge">
       
    </world>
</sdf>
```
そして、worldタグの内部にモデルを記述していきます。gazeboが読み込むsdfファイルは必ずworldタグを持つ必要がありますが、既に読み込んだワールドにモデルを出現させるといった応用を行う際にはワールドタグを持たないsdfファイルが用いられる場合もあります。
　取り敢えず、[公式チュートリアルの一番最初](https://gazebosim.org/docs/fortress/building_robot)は必ずやってみてください。とても丁寧にsdfフォーマットの書き方が説明されています。
基本的には順番にチュートリアルを見ていけばいいのですが、優先して見るべき記事のリストを上げます。
但し、本記事の内容は最初のチュートリアルを理解していれば十分です。次回以降の記事でも必要な部分はその都度リンクを貼ります。

- [Building Your Own Robot](https://gazebosim.org/docs/fortress/building_robot)：最初のチュートリアル、上のリンクと同じ
- [Moving The Robot](https://gazebosim.org/docs/fortress/moving_robot)：動力の追加
- [Sdf Worlds](https://gazebosim.org/docs/fortress/sdf_worlds)：ワールドタグの仕様、光源の調整、GUIの調整
- [Sensors](https://gazebosim.org/docs/fortress/sdf_worlds)：センサーの追加
- [Ros 2 Integration Via Bridge](https://gazebosim.org/docs/fortress/ros2_integration)：ROS2との連携
- [Ros 2 Interoperability](https://gazebosim.org/docs/fortress/ros2_interop)：ROS2との高度な連携

## CADデータをgazeboで読み込む
　さて、公式チュートリアルの最初を読んだあなたは、余りにもモデリングが煩雑なことに絶望感を覚えたかもしれません。大会で使用する複雑なロボットをgazeboでモデリングすることは不可能に感じたことでしょう。まあ、シミュレーションでは現実のロボットを忠実に再現する必要はないので、その心配の半分は杞憂なのですが、さらに朗報があります。gazeboではCADデータをインポートして、面倒なモデリングをすることなくロボットを出現させることが可能なのです。今からそのやり方を説明します。
### stlファイル
　先ずは、シミュレーションしたいロボットの`.stl`ファイルを入手する必要があります。ハードウェアを別で担当している人がいれば、その人に頼んで送ってもらって下さい。このとき、注意すべきことが一つあります。それは**メートル(m)単位でファイルを出力する**ということです。ロボットの設計はmm単位で行うことが多いので、出力時のオプションで忘れずに変更しましょう。一応、sdf側で強引にスケーリングすることもできますが、非推奨です。
今回はinventorで設計した次のようなロボットのstlファイルを出力します。このstlファイルは[github](https://github.com/OsawaKousei/2DRoboPrac_ws)の`CAD/data/2DRoboPrac.stl`にあります。
<img width="960" alt="スクリーンショット 2023-12-02 222000.png (1.4 MB)" src="https://img.esa.io/uploads/production/attachments/19744/2023/12/02/148658/e4c227a0-dcbe-4ddc-855a-a43f8fad1784.png">
画面左上のファイル→書き出し→CAD形式→stlファイル形式を選択し、
<img width="960" alt="スクリーンショット 2023-12-02 222116.png (944.5 kB)" src="https://img.esa.io/uploads/production/attachments/19744/2023/12/02/148658/763e2b82-d197-47fb-9148-d1d857245abe.png">
忘れずにオプションでメートル基準に直します
<img width="960" alt="スクリーンショット 2023-12-02 222243.png (726.0 kB)" src="https://img.esa.io/uploads/production/attachments/19744/2023/12/02/148658/26d8e0bc-fc1b-44e6-a977-3c4d7f78e9f8.png">
CADソフトが違えばUIの操作は異なるでしょうが、基本的に以上の手順でstlファイルを作成できるはずです。
### daeファイル
　次に、取得したstlファイルを元に、`.dae`ファイルを作成していきます。stlファイルとは何が違うかと言うと、stlファイルはロボットの当たり判定を作成するのに使いますが、daeファイルはロボットの見た目を作成するのに使います。daeファイルの作成にはBlenderというソフトを使いますーBlenderでなければいけない理由はありませんが。これはwindows、unbuntuどちらでも使えます。ロボットを設計するためのCADソフトでは、機体の表面に装飾を施したりすることは苦手ですが、Blenderはそのためのソフトですので、その気になれば芸術的な要素を追求したロボットモデルを作成することも可能です。但し、今回はdaeファイルを出力することだけが目的なので、デザインの話はしません。
　では、実際にBlenderを使っていきましょう。私はwindowsでinventorを用いてロボットを設計しており、windows側にCADソフトを統一したかったので、windowsに入れました。
[公式サイト](https://www.blender.org/download/)からダウンロードして下さい。インストローラーを起動して、インストールします。

<details><summary>ubuntuへのインストール</summary><div>

ubuntuでは

```
sudo add-apt-repository ppa:irie/blender
sudo apt-get update
sudo apt-get install blender
```

とした後に`blender`で起動できるようです（動作確認はしていません）

</div></details>


インストールが完了したらBlenderを起動してください。先ず、元からある立方体は邪魔なので消してしまいましょう。右クリックして、

<img width="960" alt="スクリーンショット 2023-12-02 222904.png (899.8 kB)" src="https://img.esa.io/uploads/production/attachments/19744/2023/12/02/148658/39309c3d-aba1-4c81-a062-4141fd1f5c83.png">

然る後、stlファイルをインポートします。

<img width="960" alt="スクリーンショット 2023-12-02 223100.png (828.8 kB)" src="https://img.esa.io/uploads/production/attachments/19744/2023/12/02/148658/ab9da7e6-6fcd-4d88-8bac-8cdec5760ec3.png">

上手く表示されているか確認してください。

<img width="960" alt="スクリーンショット 2023-12-02 223203.png (594.0 kB)" src="https://img.esa.io/uploads/production/attachments/19744/2023/12/02/148658/99a8a58a-2883-44fd-adce-7a0b6c504beb.png">

メートル単位系が指定されていることを確認しましょう。

<img width="244" alt="スクリーンショット 2023-12-02 223259.png (137.7 kB)" src="https://img.esa.io/uploads/production/attachments/19744/2023/12/02/148658/011b6f24-ec9d-473d-b708-9144ef4c01a2.png">

インポートが済んだら、daeファイルをエクスポートします。

<img width="960" alt="スクリーンショット 2023-12-02 223438.png (588.1 kB)" src="https://img.esa.io/uploads/production/attachments/19744/2023/12/02/148658/e90e3642-6a23-4b30-912c-44103a0c077f.png">


これで準備は完了です。
### gazeboで表示
　最後に、gazeboでこれらのファイルを読み込んで表示してみましょう。windows側でCADファイルを作成した場合は、何らかの方法でこれをgazeboを実行するubuntu機に持ってきます。そして、以下のようなディレクトリ構成になるようにファイルを配置して下さい。CADファイルの名前は好きに変えて大丈夫です。
```
WorkSpace/
    models/
        meshes/
            2DRoboPrac.stl
            2DRoboPrac.dae
```
次に、gazeboがモデルの保存場所を特定できるようにするために、環境変数を設定します。
`export IGN_GAZEBO_RESOURCE_PATH="Paht/to/your/models/directry"`
これができたら、いよいよsdfファイルを書いていきます。以下のような空っぽのsdfファイルをmodels以下に作成してください。簡単にコメントで説明も付けてあります。
```world.sdf
<?xml version="1.0" ?>
<sdf version="1.8"> <!--xmlとsdfのバージョン定義-->
    <world name="WorldDemo"> <!--ワールドの内容は全てこのタグの中に-->
        <physics name="1ms" type="ignored">
            <max_step_size>0.001</max_step_size> <!--最大何秒ごとにシミュレートするか（小さいほど正確なシミュレーションになる）-->
            <real_time_factor>1.0</real_time_factor> <!--現実時間との比率-->
        </physics>
        <plugin
            filename="libignition-gazebo-physics-system.so"
            name="ignition::gazebo::systems::Physics">
        </plugin> <!--動力学のシミュレーションに必要-->
        <plugin
            filename="libignition-gazebo-user-commands-system.so"
            name="ignition::gazebo::systems::UserCommands">
        </plugin> <!--ユーザーコマンドを利用可能に-->
        <plugin
            filename="libignition-gazebo-scene-broadcaster-system.so"
            name="ignition::gazebo::systems::SceneBroadcaster">
        </plugin> <!--ワールドシーンの描画-->
        
        <!--光に関する設定-->
        <light type="directional" name="sun"> <!--pointかdirectional、spot.-->
            <cast_shadows>true</cast_shadows> <!--trueなら影ができる-->
            <pose>0 0 10 0 0 0</pose> <!--位置 relative_to attributeを追加できる、しなければworldに関連付けられる-->
            <diffuse>0.8 0.8 0.8 1</diffuse>
            <specular>0.2 0.2 0.2 1</specular>
            <attenuation>
                <range>1000</range><!--光の範囲-->
                <constant>0.9</constant><!--減衰係数0-1、1は減衰しない-->
                <linear>0.01</linear><!--線形減衰係数0-1、1は距離について均等に減衰-->
                <quadratic>0.001</quadratic><!--光の方向性、pointには適用されない-->
            </attenuation>
            <direction>-0.5 0.1 -0.9</direction>
        </light>
    </world>
</sdf>
```

　さて、このワールドには現在何のモデルも存在しません。ですので、モデルを追加しましょう。以下はその一例です。
```
<model name="demoRobot">
<!--モデルが地面にめり込まないように少し浮かせています-->
<pose>0 0 0.2 0 0 0</pose>
    <link name="base_link">
      <inertial>
        <pose>0 0 0 0 0 0</pose>
        <inertia>
          <ixx>4.2111447e-02</ixx>
          <ixy>0</ixy>
          <ixz>0</ixz>
          <iyy>4.2111447e-02</iyy>
          <iyz>0</iyz>
          <izz>7.5254874e-02</izz>
        </inertia>
        <mass>1.3729096e+00</mass>
      </inertial>
      <collision name="base_collision">
        <!--重心の位置を調整しています、詳しくは後述します-->
        <pose>-0.064 0.08 0.06 0 0 -1.57</pose>
        <geometry>
         
        </geometry>
      </collision>
      <visual name="base_visual">
        <!--重心の位置を調整しています、詳しくは後述します-->
        <pose>-0.064 0.08 0.06 0 0 -1.57</pose>
        <geometry>
          
        </geometry>
        <material>
          <diffuse> 0.921568627 0.925490196 0.941176471 1</diffuse>
          <ambient> 0.921568627 0.925490196 0.941176471 1</ambient>
          <specular>0.921568627 0.925490196 0.941176471 1</specular>
        </material>
      </visual>
    </link>
</model>
```

　これ以上はないと言う位シンプルなモデルです。ここで重要なことは、collisionとvisualのgeometryタグが空になっていることです。ここには次のように記述しますーそしてこれが、今回の記事の核心部分です。CADファイルの名前を変更している場合はこちらも適宜書き換えてください。
colliderの方には
```
<mesh>
    <uri>model://meshes/2DRoboPrac.stl</uri>
</mesh>
```
visualの方には
```
<mesh>
    <uri>model://meshes/2DRoboPrac.dae</uri>
</mesh>
```
これで、全ての工程が完了しました。後はmodelディレクトリでターミナルを起動し、`ign gazebo world.sdf`でgazeboを起動すれば、以下のようにモデルが表示されているはずです。
今回の内容を実装したsdfファイルは[github](https://github.com/OsawaKousei/2DRoboPrac_ws)の`gaz_ws/MakeYourRobot`にあります。

<img width="1920" alt="Screenshot from 2023-12-02 21-53-48.png (194.8 kB)" src="https://img.esa.io/uploads/production/attachments/19744/2023/12/02/148658/b9be7483-98a6-4833-a48b-27ceef93c6c3.png">

### 上記の方法でCADファイルを読み込んだ際の注意点
上記の方法でCADファイルを読み込んだ場合、基本的には何ら問題なくgazeboを使用できます。しかし、幾つか注意すべきポイントが存在します。
- 先ず、gazebo標準のsdfトレース機能では、CADファイルの読み込みをサポートしていません。これにより何が引き起こされるかというと、rvizと連携したときにロボットの見た目をrviz上で見ることができなくなります。しかし、これは殆ど問題になりません。tfというロボットの状態を把握するための別の機能がありーこれは正常に動作します、rvizでは専らそちらを用いるからです。

次に、今後の無用なトラブルをさけるために調整すべきことがいくつかあります。

- gazeboの画面左上の十字矢印を選択し、モデルをクリックしてください。そうするとモデルの座標系が表示されます。何も設定を弄っていなければ、この座標系を示す矢印の原点が、モデルの重心になります。恐らく、正しくない所に表示されているはずなので、重心の位置を変更する必要があります。

<details><summary>重心の位置の変更</summary><div>
CADファイルを読み込んでいるリンク直下のposeタグを削除してください。これは、リンクに属する各タグの位置を一括で指定していますが、個別に設定することも可能です。但し、そうした場合、他のリンクからこのリンクをrelative to =""で指定したときの挙動が不安定になる可能性があることに留意してください。colliderとvisualタグには削除したものと同じposeの値を指定し、inertialタグには、重心の座標をposeで指定してください。
poseは(x,y,z,Ｒ,P,Y)で指定します。後ろの３つは回転角を表す「ロール・ピッチ・ヨ―」です

</div></details>

- gazeboの画面左上の十字矢印を選択し、モデルをクリックしてください。そうするとモデルの座標系が表示されます。このとき、想定するモデルの進行方向に赤い矢印が、進行方向向かって左側に緑の矢印が、上方向に青い矢印が表示されているか確認してください。

<details><summary>そうなっていなかった場合</summary><div>
重心の位置の変更と同様にposeを個別に指定することで対処します。
colliderとvisualのposeに適切な回転角を指定してください。回転角はradで指定することに注意してください。90度の指定には[1.57]で十分です。
モデルの作成例ではcolliderとvisualを動かすことで、重心を調整していますが、同じことです。
</div></details>

# トラブルシューティング
### 一般論
・とりあえず`sudo apt update`する
・gazeboのゾンビ化を疑う
gazeboは乱暴な閉じ方をしたときーそして丁寧に行った場合でもそれなりの頻度で、正常に終了せずプロセスがゾンビ化します
先ず、`ps aux|grep ign`で検索をかけます。正しく終了できていれば２つのプロセスが表示されます。
３つ以上ならプロセスのゾンビ化が発生しています。`kill -9 <プロセス番号>`で最初と最後のプロセス以外を強制終了します。

<details><summary>プロセスがゾンビ化しているときの代表的な挙動</summary><div>

・モデルやワールドの記述を変更したのに反映されていない
・何故かロボットが２つ表示される
・シミュレーションの開始ボタンのオンオフが高速で切り替わる

</div></details>

### 個別の対策
<details><summary>Wayland がどうとか言う警告</summary><div>

結論から言うと、この警告は問題ありません。少なくとも現時点で私の手元にあるgazeboは元気に動いています。
ネットではこの警告への対処法が何通りも見つかります。しかし、不用意にそれを実行することは推奨できません。実際に警告が消えることもありますが、それ以上の問題が発生する可能性があります。ひどい場合では、gazeboが立ち上がらなくなりました。

</div></details>


<details><summary>仮想マシンを使っているあなたへ</summary><div>

   もしあなたが、VMwareをはじめとする仮想マシンを用いて開発を行っていて、たちの悪い不具合に直面している場合、そもそもその仮想化アプリケーションがignition gazeboに対応していない可能性があります。少なくとも、VMwareは(2023/12/2現在)非対応です。
これはopenGLのバージョン互換性に起因する問題です。VMwareは新しいバージョンのopenGLに対応しておらず、ignition gazeboで使用されるOgre2での描画を実行できません。
Ogre1を使用するようにオプションを設定することは可能ですが、予期しない不具合が発生する可能性があります。

</div></details>

<details><summary>モデルが見つからない或いは読み込めないと言われる</summary><div>

環境変数が正しく設定できているか確認しましょう。
`printenv IGN_GAZEBO_RESOURCE_PATH`で環境変数を確認できます。
sdfファイルでモデルを読み込む際のパスが正しいかも確認しましょう
↑のprintenvで出力されたパスに続くような形で書かれていますか？

</div></details>


# 終わりに
　この記事ではgazeboのほんの触りだけを解説しました。今のままでは、単に自分の作ったモデルを眺める用途にしか使えませんが、これから機能を追加していくことで、とても頼れるツールへと変貌するはずです。
この記事の内容がもの足りなければ是非次回の記事をご覧になってください。
　次回は[gazeboとROS2を連携してロボットを操作する](https://qiita.com/N622/items/9d89f77d85d9da0af29e)です。ロボットに動きが付けば一気にシミュレーションらしくなりますよ。
　これらの記事の全体像が知りたければ、[ignition gazebo+Rviz2の開発環境構築](https://qiita.com/N622/items/6ca879f8b1f9d667e561)も覗いてみてください。ここにはgazeboとrvizに関する私の書いた記事がまとめられています。
また、この記事で扱った内容を実装したコードは[私のgithub](https://github.com/OsawaKousei/2DRoboPrac_ws)に公開しています。今回の内容に相当する部分はリンク先のレポジトリの`gz_ws/MakeYourRobot`に収められています。

# 参考文献
[Gazebo - Docs](https://gazebosim.org/docs/fortress/getstarted)
[ROS 2 Documenttion](https://docs.ros.org/en/humble/index.html)
[Gazebo + ROS で自分だけのロボットをつくる 3.COLLADAファイルをつくる](https://qiita.com/RyodoTanaka/items/b1ebd48da81669db7409)