WPP
Gazebo の SDF を分割する

なんとか RGBD_Camara の使い方がわかったGazebo ですが、だんだんファイルが長くなってきて見通しが悪くなってきました。もう少し実践的なシミュレーションにしようとするとオブジェクト(モデル)の数がどんどん増えていくのでちょっと厳しそうです。

SDFromat の仕様をみてみると <include> というタグがありどうやら別ファイルの内容をロードすることができそうなので試してみました。結果からいうと <include><uri> のなかで file:// を使うとファイルシステムからファイルを読み込んでくれました。

分割前のファイル

ファイル名を camera.sdf としているのはいまいち分かりづらいかもしれませんがこれを分割してみます。

地面に相当する面と光源、そしてカメラと球体が配置されています。

<?xml version="1.0" ?>
<sdf version="1.10">
  <world name="rgb_world">
    <plugin
        filename="gz-sim-physics-system"
        name="gz::sim::systems::Physics">
    </plugin>

    <physics name="1ms" type="ignored">
      <max_step_size>0.001</max_step_size>
      <real_time_factor>1.0</real_time_factor>
    </physics>
    <gravity>0 0 -9.8</gravity>

    <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='sphere1'>
      <pose>1.5 1.5 0.5  0 0 0</pose>
      <link name='link'>
        <collision name='collision'>
          <geometry>
            <sphere>
              <radius>0.5</radius>
            </sphere>
          </geometry>
        </collision>
        <visual name='visual'>
          <geometry>
            <sphere>
              <radius>0.5</radius>
            </sphere>
          </geometry>
        </visual>
      </link>
    </model>

    <!-- RGB camera-->
    <model name='rgb_camera'>
      <pose>0 0 0.5  0 0 0.52</pose>
      <link name='link'>
        <inertial>
          <mass>0.1</mass>
          <inertia>
            <ixx>0.000166667</ixx>
            <iyy>0.000166667</iyy>
            <izz>0.000166667</izz>
          </inertia>
        </inertial>

        <collision name='collision'>
          <geometry>
            <box>
              <size>0.1 0.3 0.2</size>
            </box>
          </geometry>
        </collision>

        <visual name='visual'>
          <geometry>
            <box>
              <size>0.1 0.3 0.2</size>
            </box>
          </geometry>
          <material>
            <ambient>0.1 0.1 0.1 1</ambient>
            <diffuse>0.1 0.1 0.1 1</diffuse>
            <specular>0.01 0.01 0.01 1</specular>
          </material>
        </visual>

        <sensor name='camera' type='camera'>
          <always_on>1</always_on>
          <update_rate>2</update_rate>
          <visualize>true</visualize>
          <camera>
            <horizontal_fov>1.047</horizontal_fov>
            <image>
              <width>640</width>
              <height>480</height>
              <format>R8G8B8</format>
            </image>
            <clip>
              <near>0.1</near>
              <far>100</far>
            </clip>
            <save enabled='true'>
              <path>images/</path>
            </save>
          </camera>
          <topic>rgb</topic>
        </sensor>
      </link>
    </model>
    <!-- RGB camea end -->

    <!-- load sensors plugin  -->
    <plugin filename='gz-sim-sensors-system'
      name='gz::sim::systems::Sensors'>
    </plugin>
  </world>

</sdf>

分割後のファイル構成

  • camera-world.sdf ワールド、地面と光源を含む。
  • camera-model.sdf カメラ
  • sphere-model.sdf 球体

こんなふうに分割して、camera-model.sdf と sphere-model.sdf をそれぞれ camera-world.sdf に読み込みます。

camera-world.sdf

まずは、camera-world.sdf です

<?xml version="1.0" ?>
<sdf version="1.10">
  <world name="rgb_world">
    <plugin
        filename="gz-sim-physics-system"
        name="gz::sim::systems::Physics">
    </plugin>

    <physics name="1ms" type="ignored">
      <max_step_size>0.001</max_step_size>
      <real_time_factor>1.0</real_time_factor>
    </physics>
    <gravity>0 0 -9.8</gravity>

    <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='sphere1'>
      <pose>1.5 1.5 0.5  0 0 0</pose>
      <link name='link'>
        <collision name='collision'>
          <geometry>
            <sphere>
              <radius>0.5</radius>
            </sphere>
          </geometry>
        </collision>
        <visual name='visual'>
          <geometry>
            <sphere>
              <radius>0.5</radius>
            </sphere>
          </geometry>
        </visual>
      </link>
    </model>

    <!-- Sphere -->
    <include>
      <uri>file://./sphere-model.sdf</uri>
    </include>

    <!-- RGB camera -->
    <include>
      <uri>file://./camera-model.sdf</uri>
    </include>

    <!-- load sensors plugin  -->
    <plugin filename='gz-sim-sensors-system'
      name='gz::sim::systems::Sensors'>
    </plugin>
  </world>

</sdf>

試していませんが <plugin> も camera-model に追い出せるかもしれません。

camera-model.sdf

<sdf> タグのあと <world> を飛ばして <model> タグを書いて大丈夫でした。これが仕様的に正しいのかちょっと自信はないのですが、問題あったらなおします。

<?xml version="1.0" ?>
<sdf version="1.10">
  <!-- RGB camera-->
  <model name='rgb_camera'>
    <pose>0 0 0.5  0 0 0.52</pose>
    <link name='link'>
      <inertial>
        <mass>0.1</mass>
        <inertia>
          <ixx>0.000166667</ixx>
          <iyy>0.000166667</iyy>
          <izz>0.000166667</izz>
        </inertia>
      </inertial>

      <collision name='collision'>
        <geometry>
          <box>
            <size>0.1 0.3 0.2</size>
          </box>
        </geometry>
      </collision>

      <visual name='visual'>
        <geometry>
          <box>
            <size>0.1 0.3 0.2</size>
          </box>
        </geometry>
        <material>
          <ambient>0.1 0.1 0.1 1</ambient>
          <diffuse>0.1 0.1 0.1 1</diffuse>
          <specular>0.01 0.01 0.01 1</specular>
        </material>
      </visual>

      <sensor name='camera' type='camera'>
        <always_on>1</always_on>
        <update_rate>2</update_rate>
        <visualize>true</visualize>
        <camera>
          <horizontal_fov>1.047</horizontal_fov>
          <image>
            <width>640</width>
            <height>480</height>
            <format>R8G8B8</format>
          </image>
          <clip>
            <near>0.1</near>
            <far>100</far>
          </clip>
          <save enabled='true'>
            <path>images/</path>
          </save>
        </camera>
        <topic>rgb</topic>
      </sensor>
    </link>
  </model>
  <!-- RGB camea end -->
</sdf>

camera-model.sdf

sphere も同じようにします。

<?xml version="1.0" ?>
<sdf version="1.10">
  <model name='sphere1'>
    <pose>1.5 1.5 0.5  0 0 0</pose>
    <link name='link'>
      <collision name='collision'>
        <geometry>
          <sphere>
            <radius>0.5</radius>
          </sphere>
        </geometry>
      </collision>
      <visual name='visual'>
        <geometry>
          <sphere>
            <radius>0.5</radius>
          </sphere>
        </geometry>
      </visual>
    </link>
  </model>
</sdf>

gz sim camera-world.sdf で無事読み込めました。

参考

【ROS】作成したロボット部品を表示してみる【 Gazebo,SDF,XML】 – ふるお〜と!

SDFormat Specification

Gazebo のセンサーが publish しているトピックを調べる方法

前回の記事では camera_sensor と depth_camera を取り上げましたが、その際 rgbd_camera が上手く扱えなかったのですが、どうやら rgbd_camera は / では何も広告していないことが sensors_demo.sdf をみてわかりました。

センサーがどんなトピックを広告するかを調べる方法を調べました。 センサーが広告するトピックがドキュメントで見つけられなかったのですが、その理由の一旦がわかりました。

トピックを確認する方法

まずは、何でもいいので正しく構成された .sdf を読み込んでおきます。

別の端末から以下のコマンドを実行します。ここでは /rgbd が rgbd_camera が publish しているトピックですが、
その下に、camera_info、depth_image などがあることがわかります。

$ gz topic -l
/clock
/gazebo/resource_paths
/gui/camera/pose
/gui/currently_tracked
/gui/track
/rgbd/camera_info
/rgbd/depth_image
/rgbd/image
/rgbd/points
/sensors/marker
/stats
/world/rgbd_world/clock
/world/rgbd_world/dynamic_pose/info
/world/rgbd_world/pose/info
/world/rgbd_world/scene/deletion
/world/rgbd_world/scene/info
/world/rgbd_world/state
/world/rgbd_world/stats
/world/rgbd_world/light_config
/world/rgbd_world/material_color

上記は、この .sdf を読み込んだ結果です

<?xml version="1.0" ?>
<sdf version="1.10">
  <world name="rgbd_world">
    <plugin
        filename="gz-sim-physics-system"
        name="gz::sim::systems::Physics">
    </plugin>

    <physics name="1ms" type="ignored">
      <max_step_size>0.001</max_step_size>
      <real_time_factor>1.0</real_time_factor>
    </physics>
    <gravity>0 0 -9.8</gravity>

    <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='sphere1'>
      <pose>1.5 1.5 0.5  0 0 0</pose>
      <link name='link'>
        <collision name='collision'>
          <geometry>
            <sphere>
              <radius>0.5</radius>
            </sphere>
          </geometry>
        </collision>
        <visual name='visual'>
          <geometry>
            <sphere>
              <radius>0.5</radius>
            </sphere>
          </geometry>
        </visual>
      </link>
    </model>

    <!-- RGB-D camera-->
    <model name='rgbd_camera'>
      <pose>0 0 0.5  0 0 0</pose>
      <link name='link'>
        <inertial>
          <mass>0.1</mass>
          <inertia>
            <ixx>0.000166667</ixx>
            <iyy>0.000166667</iyy>
            <izz>0.000166667</izz>
          </inertia>
        </inertial>

        <collision name='collision'>
          <geometry>
            <box>
              <size>0.1 0.1 0.1</size>
            </box>
          </geometry>
        </collision>

        <visual name='visual'>
          <geometry>
            <box>
              <size>0.1 0.1 0.1</size>
            </box>
          </geometry>
          <material>
            <ambient>0.1 0.1 0.1 1</ambient>
            <diffuse>0.1 0.1 0.1 1</diffuse>
            <specular>0.01 0.01 0.01 1</specular>
          </material>
        </visual>
        
        <sensor name='rgbd_camera' type='rgbd_camera'>
          <always_on>1</always_on>
          <update_rate>1</update_rate>
          <visualize>true</visualize>
          <camera>
            <horizontal_fov>1.047</horizontal_fov>
            <image>
              <width>640</width>
              <height>480</height>
              <!-- <format>R8G8B8</format> -->
            </image>
            <clip>
              <near>0.1</near>
              <far>100</far>
            </clip>
            <save enabled='true'>
              <path>images</path>
            </save>
          </camera>
          <topic>rgbd</topic>
        </sensor>
      </link>
    </model>
    <!-- RGB-D camea end -->

    <!-- load sensors plugin  -->
    <plugin filename='gz-sim-sensors-system'
      name='gz::sim::systems::Sensors'>
    </plugin>
  </world>

</sdf>

ROS / ROS2 を使っている人にとっては、ノードが発信している情報を M2M 的なアプローチで外部から確認すれば OK っていう方式になじみがあるのだろうが、gazebo のドキュメントにはあまり書いていない気がするのはちょっとなーって文句の 1 つもいいたい。

Gazebo ionic で SDF にカメラを追加した

少し前から続いている gazebo ネタですが、シミュレーションの中にカメラを置きたくて試行錯誤しました。結果からいうと github の gz-sim に example がありそこを参考にしました。

カメラを Gazabo のワールドにいれてある特定のことをしたいのですが、自分には、 ROS / ROS2 の経験はほぼない状態なので URDF ではなくひとまず SDF で書くことにしています。そうでないと 2 重 3 重の障害となりなかなか全体の理解がすすまないだろうという判断です。

Gazebo 公式のチュートリアルにもセンサーのサンプルはありますが、あれはシステムプラグインなので <plugin> タグの中の filename をどう書いていいのかイマイチわかりませんでした。

公式ドキュメントを漁っているといつのまに Github の example にたどり着き world フォルダに色々なサンプルがあることに気づきました。

CameraSensor の例

最低限のモデルしかおいていないです。このコードのままだとどうもカメラの向きが悪いようで ワールドの中にある球体をカメラの画角に捉えられていません。が、座標適当に調整すればカメラで捉えるはずです。

<?xml version="1.0" ?>
<sdf version="1.10">
  <world name="camera_world">
    <plugin
        filename="gz-sim-physics-system"
        name="gz::sim::systems::Physics">
    </plugin>

    <physics name="1ms" type="ignored">
      <max_step_size>0.001</max_step_size>
      <real_time_factor>1.0</real_time_factor>
    </physics>
    <gravity>0 0 -9.8</gravity>

    <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='sphere1'>
      <pose>1.5 1.5 0.5  0 0 0</pose>
      <link name='link'>
        <collision name='collision'>
          <geometry>
            <sphere>
              <radius>0.5</radius>
            </sphere>
          </geometry>
        </collision>
        <visual name='visual'>
          <geometry>
            <sphere>
              <radius>0.5</radius>
            </sphere>
          </geometry>
        </visual>
      </link>
    </model>

    <!-- RGB camera-->
    <model name='rgb_camera'>
      <pose>0 0 0.5  0 0 0</pose>
      <link name='link'>
        <inertial>
          <mass>0.1</mass>
          <inertia>
            <ixx>0.000166667</ixx>
            <iyy>0.000166667</iyy>
            <izz>0.000166667</izz>
          </inertia>
        </inertial>

        <collision name='collision'>
          <geometry>
            <box>
              <size>0.1 0.1 0.1</size>
            </box>
          </geometry>
        </collision>

        <visual name='visual'>
          <geometry>
            <box>
              <size>0.1 0.1 0.1</size>
            </box>
          </geometry>
          <material>
            <ambient>0.1 0.1 0.1 1</ambient>
            <diffuse>0.1 0.1 0.1 1</diffuse>
            <specular>0.01 0.01 0.01 1</specular>
          </material>
        </visual>
        
        <sensor name='camera' type='camera'>
          <always_on>1</always_on>
          <update_rate>2</update_rate>
          <visualize>true</visualize>
          <camera>
            <horizontal_fov>1.047</horizontal_fov>
            <image>
              <width>640</width>
              <height>480</height>
              <format>R8G8B8</format>
            </image>
            <clip>
              <near>0.1</near>
              <far>100</far>
            </clip>
            <save enabled='true'>
              <path>images/</path>
            </save>
          </camera>
          <topic>rgb</topic>
        </sensor>
      </link>
    </model>
    <!-- RGB camea end -->

    <!-- load sensors plugin  -->
    <plugin filename='gz-sim-sensors-system'
      name='gz::sim::systems::Sensors'>
    </plugin>
  </world>

</sdf>

カメラの画像は images フォルダに保存されます。もしかすると予めフォルダを作成する必要があるかもしれません。

gazebo と一緒に組み込まれる sensors は、どうやら gz-sim-sensors-system というファイル名で参照できるようです。

動作を確認するには、ターミナルを 2 つ立ち上げ、一方の端末でシミュレータを起動します。

$ gz sim camera.sdf

もう一方の端末でトピックをリッスンします。

 gz topic -e -t /rgb

シミュレータの左下の開始?再生?ボタンをクリックすると、トピックを聴いている端末なにやらデータが流れ出し images フォルダにカメラが捉えた映像が保存されます。

depth_camera の例

基本的に、通常のカメラと同じなのですが、まずは sdf を提示します。

<?xml version="1.0" ?>
<sdf version="1.10">
  <world name="depth_world">
    <plugin
        filename="gz-sim-physics-system"
        name="gz::sim::systems::Physics">
    </plugin>

    <physics name="1ms" type="ignored">
      <max_step_size>0.001</max_step_size>
      <real_time_factor>1.0</real_time_factor>
    </physics>
    <gravity>0 0 -9.8</gravity>

    <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='sphere1'>
      <pose>1.5 1.5 0.5  0 0 0</pose>
      <link name='link'>
        <collision name='collision'>
          <geometry>
            <sphere>
              <radius>0.5</radius>
            </sphere>
          </geometry>
        </collision>
        <visual name='visual'>
          <geometry>
            <sphere>
              <radius>0.5</radius>
            </sphere>
          </geometry>
        </visual>
      </link>
    </model>

    <!-- depth camera-->
    <model name='depth_camera'>
      <pose>0 0 0.5  0 0 0</pose>
      <link name='link'>
        <inertial>
          <mass>0.1</mass>
          <inertia>
            <ixx>0.000166667</ixx>
            <iyy>0.000166667</iyy>
            <izz>0.000166667</izz>
          </inertia>
        </inertial>

        <collision name='collision'>
          <geometry>
            <box>
              <size>0.1 0.1 0.1</size>
            </box>
          </geometry>
        </collision>

        <visual name='visual'>
          <geometry>
            <box>
              <size>0.1 0.1 0.1</size>
            </box>
          </geometry>
          <material>
            <ambient>0.1 0.1 0.1 1</ambient>
            <diffuse>0.1 0.1 0.1 1</diffuse>
            <specular>0.01 0.01 0.01 1</specular>
          </material>
        </visual>
        
        <sensor name='depth_camera' type='depth_camera'>
          <always_on>1</always_on>
          <update_rate>1</update_rate>
          <visualize>true</visualize>
          <camera>
            <horizontal_fov>1.047</horizontal_fov>
            <image>
              <width>640</width>
              <height>480</height>
              <format>L8</format>
            </image>
            <clip>
              <near>0.1</near>
              <far>100</far>
            </clip>
            <save enabled='true'>
              <path>images</path>
            </save>
          </camera>

          <topic>depth</topic>
        </sensor>
      </link>
    </model>
    <!-- depth camea end -->

    <!-- load sensors plugin  -->
    <plugin filename='gz-sim-sensors-system'
      name='gz::sim::systems::Sensors'>
    </plugin>
  </world>

</sdf>

先程の camera_sensor のときとほぼ同じですが、 <sensor> の type 属性がdepth_camera になっています。更に距離情報はカラーだと困るので、SDFormat の仕様を見て適当に L8 としたがこれが正しいのかは全くわかっていません。

一応つぶやく トピック depth に変えていますが、まあ変えなくてもいいです。

動作確認はcamera_sensor のときと同様におこないますが、 リッスンするトピックと SDF 内で指定した depth にしてやる必要があります。

ようやく、カメラデータの吐き出しまでできました。

とはいえ、rgbd_camera は同じようにやってもダンマリなのでなかなか先に進めない。同じフォルダにある sensors_demo.sdf には rgbd_camera の例があるのでこれを参考にやってみるか。

参考

gz-sim/examples/worlds at gz-sim9 · gazebosim/gz-sim

SDFormat Specification

g

Gazebo のチュートリアルをかじった

仕事でロボット系のシミュレーションが必要になるかもしれないってことで Gazebo のチュートリアルをちょっとだけやってみた。

とりあえず Moving the Robot まで進めた。

環境

  • Linux Mint 22.1 (ubuntu 22.04)
  • gazeob ionic

Gazebo 自身は、公式の手順で apt install gz-ionic でインストールした。

Gazebo のチュートリアルを Moving the Robot まで進めると building_robot.sdf はこうなった。
各章の冒頭に完成形があるのでそれをみればいい。

<?xml version="1.0" ?>
<sdf version="1.10">
    <world name="Moving_robot">
        <physics name="1ms" type="ignored">
            <max_step_size>0.001</max_step_size>
            <real_time_factor>1.0</real_time_factor>
        </physics>
        <plugin
            filename="gz-sim-physics-system"
            name="gz::sim::systems::Physics">
        </plugin>
        <plugin
            filename="gz-sim-user-commands-system"
            name="gz::sim::systems:aze:UserCommands">
        </plugin>
        <plugin
            filename="gz-sim-scene-broadcaster-system"
            name="gz::sim::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-->

            <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.126164</ixx>
                        <ixy>0</ixy>
                        <ixz>0</ixz>
                        <iyy>0.416519</iyy>
                        <iyz>0</iyz>
                        <izz>0.481014</izz>
                    </inertia>
                </inertial>
                <visual name='visual'>
                    <geometry>
                        <box>
                            <size>2.0 1.0 0.5</size> <!--question: this size is in meter-->
                        </box>
                    </geometry>
                    <!--let's add color to our 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'> <!--todo: describe why we need the collision-->
                    <geometry>
                        <box>
                            <size>2.0 1.0 0.5</size>
                        </box>
                    </geometry>
                </collision>
            </link>

            <!--let's build the 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>2</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>

            <!--copy and paste for right wheel but change 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>

            <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>


            <!--connecting these links together using joints-->
            <joint name='left_wheel_joint' type='revolute'> <!--continous joint is not supported yet-->
                <pose relative_to='left_wheel'/>
                <parent>chassis</parent>
                <child>left_wheel</child>
                <axis>
                    <xyz expressed_in='__model__'>0 1 0</xyz> <!--can be defined as any frame or even arbitrary frames-->
                    <limit>
                        <lower>-1.79769e+308</lower>    <!--negative infinity-->
                        <upper>1.79769e+308</upper>     <!--positive infinity-->
                    </limit>
                </axis>
            </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>

            <!--different type of joints ball joint--> <!--defult value is the child-->
            <joint name='caster_wheel' type='ball'>
                <parent>chassis</parent>
                <child>caster</child>
            </joint>

            <!--diff drive plugin-->
            <plugin
                filename="gz-sim-diff-drive-system"
                name="gz::sim::systems::DiffDrive">
                <left_joint>left_wheel_joint</left_joint>
                <right_joint>right_wheel_joint</right_joint>
                <wheel_separation>1.2</wheel_separation>
                <wheel_radius>0.4</wheel_radius>
                <odom_publish_frequency>1</odom_publish_frequency>
                <topic>cmd_vel</topic>
            </plugin>
        </model>

        <!-- Moving Left-->
        <plugin filename="gz-sim-triggered-publisher-system"
                name="gz::sim::systems::TriggeredPublisher">
            <input type="gz.msgs.Int32" topic="/keyboard/keypress">
                <match field="data">16777234</match>
            </input>
            <output type="gz.msgs.Twist" topic="/cmd_vel">
                linear: {x: 0.0}, angular: {z: 0.5}
            </output>
        </plugin>
        <!-- Moving Forward-->
        <plugin filename="gz-sim-triggered-publisher-system"
                name="gz::sim::systems::TriggeredPublisher">
            <input type="gz.msgs.Int32" topic="/keyboard/keypress">
                <match field="data">16777235</match>
            </input>
            <output type="gz.msgs.Twist" topic="/cmd_vel">
                linear: {x: 0.5}, angular: {z: 0.0}
            </output>
        </plugin>
        <!-- Moving Right-->
        <plugin filename="gz-sim-triggered-publisher-system"
                name="gz::sim::systems::TriggeredPublisher">
            <input type="gz.msgs.Int32" topic="/keyboard/keypress">
                <match field="data">16777236</match>
            </input>
            <output type="gz.msgs.Twist" topic="/cmd_vel">
                linear: {x: 0.0}, angular: {z: -0.5}
            </output>
        </plugin>
        <!-- Moving Backward-->
        <plugin filename="gz-sim-triggered-publisher-system"
                name="gz::sim::systems::TriggeredPublisher">
            <input type="gz.msgs.Int32" topic="/keyboard/keypress">
                <match field="data">16777237</match>
            </input>
            <output type="gz.msgs.Twist" topic="/cmd_vel">
                linear: {x: -0.5}, angular: {z: 0.0}
            </output>
        </plugin>
    </world>
</sdf>

起動するには、ターミナルを2つ起動する必要がある。

1つめは、sdf ファイルを読み込んで画面を表示する

$ gz sim building_robot.sdf

もう一つは、入力されたキーをターミナルにダンプするものなので本当は動かさなくてもいい。

$ gz topic -e -t /keyboard/keypress

そして GUI で Keyboard Publisher を起動する必要がある。
GUI でプラグインを起動するには、右上のメニューアイコンで 『Key Publisher』 を選択する。
これを読み飛ばしてしばらく考え込んでしまった。

そして左下の 再生ボタン? 開始ボタンをクリックする。

するとキーボードの操作で車?が動作するようになる。

参考

Moving the robot — Gazebo ionic documentation

gazeboとROS2を連携してロボットを操作する #Gazebo – Qiita