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

Google Maps JS API を読み込むと uBlock Origin が maps.googleapis.com/maps/api/mapsjs/gen_204 をブロックしてしまう

ちょっとした調査で Google maps API を動かしているのですが、こんな感じでリクエストをが失敗してしまします。

結果としては uBlock Origin (UBO と略すらしい) の不具合ではなく フィルターリストの不適合だった。

Firefox の Dev Tools の Transferred には “Blocked By uBlock Origin” と表示されています。

そこで uBlock Origin のログ画面を開き再度リクエストしてみると “EasyPrivacy” というフィルターリストにヒットしてブロックされたことがわかりました。

フィルターリストの該当部分だけを無効化や削除できればいいのですが、ちょっとやり方がわからなかったので一旦 EasyPrivacy を無効にすることで回避しました。

ちょうど今日から uBlock Origin が Chrome 系ブラウザで使えなくなったようだがとりあえず Chrome 系はメインでないので様子見する。別のプラグインに移行するか UBO のアップデート待ちすることになるだろう。

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

自ホストの外側の IP アドレスを curl で取得する。

IP アドレスでアクセス制限すfる場合に自ネットワークの外側からみた IP アドレスを調べたい場合がある。外部の API サーバーや Web サーバーが接続元の IP アドレスで制限するような場合だ。

ローカルのルーターに入れば確認することは当然にできるがまあ面倒です。curl でアクセスすると自ネットの IP アドレスを返してくれるサイトがあります。以前から ipinfo.io は知ってましたがこのサイトだと IPv4 しか返してくれません。(恐らく IPv4 でしか接続を受け付けてくれないのでしょう。)

Claude に “curl で自ホストのIPアドレスを確認できるサイトをリストアップして” と聞いてみると以下の回答を返してくれました。

IP アドレスを確認できるウェブサイトに対して、curl コマンドでアクセスできるものをリストアップします。以下のサイトでは外部から見たあなたの IP アドレスを確認できます:

1. `curl ifconfig.me`
2. `curl ipinfo.io/ip`
3. `curl api.ipify.org`
4. `curl checkip.amazonaws.com`
5. `curl icanhazip.com`
6. `curl ipecho.net/plain`
7. `curl ip.tyk.nu`
8. `curl wtfismyip.com/text`
9. `curl ipaddress.sh`
10. `curl myip.dnsomatic.com`

これらのサイトはプレーンテキストで IP アドレスだけを返すため、curl コマンドと相性が良いです。追加情報が必要な場合は、例えば `curl ipinfo.io` のようにすると、IP アドレスだけでなく地理的な位置情報なども取得できます。

ひとつづつ確認してみると、IPv6 と IPv4 を返すサイトが半々くらいでした、10 はアクセスできませんでした。で、自分にはこれらのサイト名はちょっと覚えにくかったので IPv6 と IPv4 を両方とも表示するスクリプトにしました。

# get outside IP address. this script gets IPv6 and IPv4 address both.

#!/bin/bash

# for IPv6
echo -n "IPv6: "

# 出力に改行がないパターン
curl ipecho.net/plain
# curl ipecho.net/plain
# curl ip.tyk.nu
echo ""

# 出力に改行のあるパターン
# curl wtfismyip.com/text
# curl ifconfig.me
# curl icanhazip.com
# curl wtfismyip.com/text

# for IPv4
echo -n "IPv4: "
curl ipinfo.io/ip
#curl api.ipify.org
echo ""

# 出力に改行のあるパターン
# curl checkip.amazonaws.com
# curl ipaddress.sh
RUSTDESK がいい感じ

これまで linux での VNC の設定がいまだによくわからないので、nomachine をリモートデスクトップ接続に使用していました。

数あるリモートデスクトップアプリの中でも nomachine は linux, Mac, Raspberry pi, Windows に対応し都合が良かったからです。

一方で Windows ドメイン (Active Directory) に参加している Windows にリモート PC からアクセスすると度々認証まわりのエラーで接続できないことが多くちょっと面倒だとおもっていました。
具体的には設定等を変更していないのに、あるときは接続に失敗したり、別のときは成功したりといった現象にわずらわされていました。これは推測ですが、Windows ドメイン認証は、社内ネットワークに接続していない状態でPC にログインできるように認証情報がキャッシュされる仕組みがあったはずです。このキャッシュでの認証できる状態だとどうやらログインできている気がする。(反対向きにいうとネットワーク経由でログインしたときは、どうもnomachine で必要な権限が許可されていないようだ。)

話がながくなったが、上に閉口して移転先を探してみた。

候補としては、X2Go、RUSTDESK、Dayon が見つかったが今回は RUSTDESK を使用することにした。

Windows でのインストール

scoop にパッケージがあったので `> scoop install rustdesk` でインストールした。

Linux でのインストール

LM22 では デフォルトのアプリストアに flatpak で掲載されていたので flatpak でインストールした。

2025-3-25 追記:

LM22 で RUSTDESK をインストールしただけの状態では、デスクトップセッション(X11)を開いてからしか使えないようなので、自動ログインするように設定した上で、 [Startup Applications] で RUSTDESK を起動するようにした。

Raspberry pi でのインストール

参考ページの Pi-Apps というのを経由して入れてみた。RUSTDESK 自身は github にあるんですが、Version-Upの度に手動インストールは結構だるいので、Pi-Apps を使ってみることにした。

使用方法

説明を明確にするための用語の定義

リモート PC: リモートからアクセスさせたいPC (画面なしPC や手元以外のPC)

ローカル PC: 自画面に外部PC の画面を表示してコントロールする PC

  1. リモート PC で RUSTDESK を起動する。
    • この PC をヘッドレス運用したい場合は、固定パスワードを指定する。
    • リモートPC の ID とパスワード をメモる (メモらなくてもいいがこの PC の ID でリモートから接続する)
  2. ローカル PC でも同じように RUSTDESK を起動する。
  3. リモート PC の ID で接続開始する。
  4. リモート PC のワンタイムパスワードか固定パスワードで認証要求をかける
  5. リモート PC の画面が表示される。

あくまで体感でしかないが、nomachine や VNC よりもキビキビと動いているきがする。画面表示もクッキリなきがしていいかもしれない。

当面はこれを使っていこう。

参考

Install RustDesk on Raspberry Pi | Pi-Apps

rustdesk/rustdesk: An open-source remote desktop application designed for self-hosting, as an alternative to TeamViewer.

ラズベリーパイに eza をインストール

eza は ls の代替コマンドでいい感じに表示してくれるやつ。LM22 や Ubuntu では気に入って使用しているが RaspberryOS ではデフォルトではインストールできないようだ。

公式の説明では以下の通りだが、RaspberryOS (bullseye) では 若干ディレクトリ構成がことなるので調整する。

$ sudo mkdir -p /etc/apt/keyrings
$ wget -qO- https://raw.githubusercontent.com/eza-community/eza/main/deb.asc | sudo gpg --dearmor -o /etc/apt/keyrings/gierens.gpg
$ echo "deb [signed-by=/etc/apt/keyrings/gierens.gpg] http://deb.gierens.de stable main" | sudo tee /etc/apt/sources.list.d/gierens.list
$ sudo chmod 644 /etc/apt/keyrings/gierens.gpg /etc/apt/sources.list.d/gierens.list
$ sudo apt update
$ sudo apt install -y eza

gpg キー用のフォルダが /etc/apt/trusted.gpg.d となり keyrings とは異なっていたので変更。

gpg キーファイル、ソースストのファイル名も eza.gpg と eza.list にそれぞれに変更した。もとの gierens という名前だと後で見たとき恐らく何ために入れたかわからなくなりそうなので変えた。
これは好みの問題で、そのままで変える必要はない。

$ wget -qO- https://raw.githubusercontent.com/eza-community/eza/main/deb.asc | sudo gpg --dearmor -o /etc/apt/trusted.gpg.d/eza.gpg
$ echo "deb [signed-by=/etc/apt/trusted.gpg.d/eza.gpg] http://deb.gierens.de stable main" | sudo tee /etc/apt/sources.list.d/eza.list
$ sudo chmod 644 /etc/apt/trusted.gpg.d/eza.gpg /etc/apt/sources.list.d/eza.list
$ sudo apt update
$ sudo apt install -y eza

eza という名前を忘れるのでエイリアスを ~/.barshrc や~/ .bash_aliases に書き込む。書き込んだら . (source コマンド) で反映させる。

$ alias ls='eza'
$ . ~/.bashrc

参考

eza/INSTALL.md at main · eza-community/eza

LM22: Mozc の辞書ツールにキーボードショートカットを割り当てて、ユーザ辞書を追加した

最近、日本語変換で積極的に単語登録するようにしている。特別な方法ではなくよくある「よろ」で「よろしくお願いします」に変換するような短縮を登録している。

で、辞書に単語登録するのが面倒な理由の一つが辞書ツールや登録ツールを呼び出すのに IME のアイコンから右クリックしてメニューを選択するというアクションがいちいち面倒なためだと思う。
この面倒さは、Linux のデスクトップでも Windows でもほとんど変わらない。しかの最終的に変換できてしまうものだからなんとなく後回しにしがち。

でだ、辞書ツールのコマンドラインがわからないので Archi Linux の Wiki を調べるとユーザー辞書を追加したほうがいいよ的なことが書いてあるのであわせて実施してみた

辞書ツールにショートカット割り当て

LM22 (Linux Mint) のCinnamon で行っているので Ubuntu とか他のデスクトップ環境では若干異なると思うがほぼ同じようなことはできるはずだ。

辞書ツールのコマンド

Mozc – ArchWiki によると設定ツール、辞書ツール、単語登録というのは mozc_tool という一つのファイルで引数を切り替えることで使い分けているようです。

実際のコマンドはこんな感じらしい。

設定ツール:
/usr/lib/mozc/mozc_tool –mode=config_dialog

辞書ツール
/usr/lib/mozc/mozc_tool –mode=dictionary_tool

単語登録
/usr/lib/mozc/mozc_tool –mode=word_register_dialog

今回は、ラウンチャアイコンは作成せずに、辞書ツールに直接キーボード・ショートカットを割り当てることにした。

  1. 設定アプリからキーボードを開く
  2. ショートカットタブを選択する
  3. カスタムショートカットを選択
  4. [Add custom shortcut] ボタンをクリック
  5. Name: を適当に決める
  6. Command: に /usr/lib/mozc/mozc_tool –mode=dictionary_tool を指定する
  7. 登録して、ダイアログを閉じる。
  8. 下段のキーボードバインディングで unassigned の行をダブルクリックする。
  9. 割り当てたいキーボードショートカットを押す。

自分は、Shift + Ctrl + D をショートカットに割り当てましたが、このへんは好みで変えてほしい。
また、個人的な好みでダブリ登録したくないので辞書ツールを起動しているが、単語登録でもいいと思う。

ユーザー辞書登録

ネット上では Mozc UT ってのを使うっていう記事がたくさん見つかりますがこれ、Mozc そのものをビルドしなきゃいけないんですね。ちょっと面倒なので、他の方法でいくことにした。

Mozc UT Dictionaries

utuhiro78/merge-ut-dictionaries: Merge multiple Mozc UT dictionaries into one and modify the costs.

だいぶ古い記事になるのですが、

Google日本語入力 強化辞書の構成を変えてみた。 – 黒MacBookが好き の辞書をインポートして使っている。

Mozcに辞書を追加した: arcadia’s blog のやり方が参考になるかもしれない。

考えるのも面倒だったので、自分の場合はそのまま辞書ツールでインポートした。

英和と和英辞書はインポートしなくてもよかったかもしれない。

Remmina が mdns で名前解決できるようにした

最近は、いちいち VNC クライアントをインストールがめんどうなので LM22 にはじめから入っている Remmina を 使っている。

がしかし、Remmina だと mdns による名前解決が使えなくて困っていた。

Remmina 自身は flatpak でインストールしていたが、D-Bus のパーミッション (socket=system-bus、socket=session-bus) を許可しても現象に変化がなかった。

Claude に聞いてみると /etc/nsswitch.conf を書き換えろってことでやってみた。

変更前:
hosts: files mdns4_minimal [NOTFOUND=return] dns myhostname mymachines

変更後:
hosts: files mdns4_minimal mdns4 [NOTFOUND=return] dns myhostname mymachines

[NOTFOUND] の前に mdns4 を追加すると Remmina でも名前解決できるようになった。