Kubricで利用可能な3Dモデルを自作する
※本記事は 2022年07月執筆時の情報です。
はじめに
Kubric は機械学習用の合成データ生成パイプラインです。前回の投稿では、物理シミュレーションなどのサンプルを実行しました。
今回は Kubric で物理シミュレーション可能な 3D モデルの作成に挑戦します。
主に以下のような流れになります。
- フォトグラメトリ
- OBJ フォーマットに変換
- 物理特性を記述
- Kubric に読み込み
フォトグラメトリ
iPhone をお持ちなら Metascan が手軽だと思います。LiDAR が付いていない iPhone SE などの機種でも利用可能です。ただし、無料版では毎月のスキャン回数が5回までに制限されているので注意してください。
ターンテーブルがあると撮影時に歩き回らなくて便利です。かなり適当なセッティングですがそれなりに綺麗に 3D モデルを作ることができました。
obj フォーマットに変換
有料版の Metascan であれば直接 OBJ 形式でエクスポートできますが、無料版は USDZ のみとなっています。
Kubric は 2022 年 7 月時点で内部に Blender 2.93 を利用しています。Blender 3 は USD 形式のインポートに対応していますが、2 では読み込むことができません。Kubric 側で Blender 3 に切り替える動きがあるようですが、当面は別途 Blender 3 をインストールして OBJ 形式に変換する必要がありそうです。
具体的な変換手順については MetascanのUSDファイルをBlenderでOBJファイルに変換する を参照してください。
エクスポートの際にスケールを調整して 3D モデルの大きさを実物に合わせます(単位は m)。また、Z 軸が上向きになるようにします。
物理特性を記述
作成した 3D モデルを物理シミュレーションで動かすためには、質量や慣性モーメントなどの物理特性を記述する必要があります。Kubric は物理エンジンに PyBullet を使用しているため、PyBullet と同様に URDF ファイルを作成します。
なお、URDF (Universal Robot Description Format) は本来 ROS (Robot Operating System) でロボットを記述するために使われているもので、URDF 自体の仕様は http://wiki.ros.org/urdf/XML にあります。XML 形式になっており、剛体を表す <link>
タグ、重心と慣性モーメントを定義する <inertia>
タグ、コリジョン判定に用いる <collision>
タグなどがあります。
今回 Kubric で動かす分には以下のスクリプトを参考に作成しました。
3D モデルの長さは実物に合わせて 0.075 (単位は m) としたので、重心座標を真ん中あたりの (0, 0, 0.04) にしています。慣性モーメントは本当はきちんと計算するべきですが、とりあえず動けばいいということで適当な値にしています。
<robot name="object">
<link name="base">
<visual>
<origin xyz="0 0 0" rpy="0 0 0"/>
<geometry>
<mesh filename="/kubric/meshes/untitled.obj"/>
</geometry>
</visual>
<collision>
<origin xyz="0 0 0" rpy="0 0 0"/>
<geometry>
<mesh filename="/kubric/meshes/untitled.obj"/>
</geometry>
</collision>
<inertial>
<origin xyz="0 0 0.04" rpy="0 0 0"/>
<mass value="0.065"/>
<inertia ixx="1e-3" ixy="0" ixz="0" iyy="1e-3" iyz="0" izz="1e-3"/>
</inertial>
<restitution value="1.0"/>
</link>
</robot>
Kubric に読み込み
シミュレーション用のスクリプトは、前回のサンプルを少し書き換えて以下のようにしました。kb.FileBasedObject()
で 表示用の OBJ ファイルと物理特性を記述した URDF ファイルを読み込んでいます。
import logging
import numpy as np
import kubric as kb
from kubric.renderer.blender import Blender as KubricRenderer
from kubric.simulator.pybullet import PyBullet as KubricSimulator
logging.basicConfig(level="INFO") # < CRITICAL, ERROR, WARNING, INFO, DEBUG
# --- create scene and attach a renderer and simulator
scene = kb.Scene(resolution=(256, 256))
scene.frame_end = 48 # number of frames to render
scene.frame_rate = 24 # rendering framerate
scene.step_rate = 240 # simulation framerate
renderer = KubricRenderer(scene)
simulator = KubricSimulator(scene)
# --- populate the scene with objects, lights, cameras
scene += kb.Cube(scale=(0.5, 0.5, 0.1), position=(0, 0, -0.1), static=True)
scene += kb.DirectionalLight(position=(-1, -0.5, 3), look_at=(0,0,0), intensity=1.5)
scene.camera = kb.PerspectiveCamera(position=(1, -0.25, 2), look_at=(0,0,0))
# --- generates objects randomly within a spawn region
spawn_region = [[-0.5, -0.5, 0], [0.5, 0.5, 0.5]]
rng = np.random.default_rng() # generator
for i in range(16):
position = rng.uniform(*spawn_region)
velocity = rng.uniform([-1, -1, 0], [1, 1, 0])
sphere = kb.FileBasedObject(
asset_id="custom",
render_filename="meshes/untitled.obj",
scale=1.0,
position=position,
velocity=velocity,
simulation_filename="meshes/untitled.urdf")
scene += sphere
kb.move_until_no_overlap(sphere, simulator, spawn_region=spawn_region)
# --- executes the simulation (and store keyframes)
simulator.run()
# --- renders the output
renderer.save_state("output/simulator.blend")
frames_dict = renderer.render()
kb.write_image_dict(frames_dict, "output")
出力された連番画像を ImageMagick で動画にすると次のようになります。
セグメンテーション画像も同様に出力可能です。
おわりに
フォトグラメトリで作成した 3D モデルを Kubric に取り込んで合成データを生成することができました。Blender も PyBullet もほとんど経験がなかったので調べながら取り組みましたが、なんとか動かすところまでできて安心しました。ですがどちらもまだまだ奥が深そうなので今後もっと身につけていきたいです。
Discussion