はじめに
本記事はWSL2を介してGenesisのロボットアームの学習(manipulation)を試す時に発生した「Cannot find matching vulkan UUID for GPU」というエラーとその対処を紹介する記事になります。
Genesisとは、ロボットの機械学習に特化した物理シミュレータになります。
このgenesis-worldのexamplesの中にある学習をいくつか試していたのですが、ロボットアームの学習をしようとした時にエラーが出て学習ができませんでした。
結果としてはWSL2+Genesisという環境によって起こった、WSL2定番のエラーだったため、本記事では私が行った対処を共有しようと思います。
環境
| 項目 | 内容 |
|---|---|
| OS(ホスト) | Windows11 + WSL2(2.7.10.0) |
| Ubuntu | 24.04 LTS |
| CUDAバージョン | 12.6(PyTorchのcu126ビルドに対応) |
| Python | 3.11.0 |
| Genesis (genesis-world) | v1.2.0 |
| GPU | NVIDIA GeForce RTX 3060 |
また、想定するディレクトリ構造については、以前書いた以下の記事の通りです。
出たエラー
まずはターミナルを開き、以下のコマンドでUbuntuと仮想環境を立ち上げます。
# Ubuntu24.04にログイン
wsl -d Ubuntu-24.04
# プロジェクト直下に移動
cd ~/projects/genesis-rl-lab
# 仮想環境起動
source .venv/bin/activate
その後、genesis-worldのロボットアーム学習を開始してみます。
python third_party/genesis-world/examples/manipulation/grasp_train.py -e arm-grasp-v1 -B 8192 --max_iterations 301
すると学習が始まらず以下のエラーが出ました。
# (前略)
Error at /project/src/render/vk/backend.cpp:553 in
madrona::render::vk::Backend::findPhysicalDevice(...)
Cannot find matching vulkan UUID for GPU
Aborted (core dumped)
重要なのは中2行の、「madronaパッケージ内のfindPhysicalDeviceメソッドを呼び出した際、GPUに対応するvulkanのUUIDが見つからなかった」という文言です。
エラーメッセージの意味
madronaとは
このmadronaというのは、GenesisのBatchRendererの中身です(パッケージ名gs-madrona)。
Genesisでの学習時、数千環境ぶんのカメラ画像を一括でGPUレンダリングするための専用バックエンドとなります。
Cannot find matching vulkan UUID for GPUとは
madronaは動作上、同じ1枚のGPUをCUDAとVulkanの両方から掴む必要があります。
物理計算・テンソル処理はCUDA側、描画はVulkan側でやって、その間でメモリを共有するためです。
この時、CUDA側で認識しているGPUとVulkan側で認識しているGPUが同一デバイスであることを保証するために、デバイスUUIDで突合せをします。findPhysicalDeviceメソッドがこの処理にあたります。
Cannot find matching vulkan UUID for GPUは一致するVulkanデバイスが1つも見つからなかったという意味です。つまり、CUDAで設定されているGPUが、Vulkan側で認識されていないから起こったエラーとなります。
原因の診断
nvidia-smiではGPUがちゃんと認識されていました。
$ nvidia-smi
+-----------------------------------------------------------------------------------------+
| NVIDIA-SMI 610.47 KMD Version: 610.47 CUDA UMD Version: 13.3 |
+-----------------------------------------+------------------------+----------------------+
| GPU Name Driver-Model | Bus-Id Disp.A | Volatile Uncorr. ECC |
しかし、vulkaninfo --summaryでは
deviceType = PHYSICAL_DEVICE_TYPE_CPUdeviceName = llvmpipe
となっており、ソフトウェア(CPU)レンダラとなっていました。
$ vulkaninfo --summary
# (前略)
Devices:
========
GPU0:
...
deviceType = PHYSICAL_DEVICE_TYPE_CPU
deviceName = llvmpipe (LLVM 20.1.2, 256 bits)
driverID = DRIVER_ID_MESA_LLVMPIPE
driverName = llvmpipe
...
よって、Vulkanから見えているのはCPUソフトレンダラだけという事がわかりました。
次に、ls /usr/share/vulkan/icd.d/とls /usr/lib/wsl/lib/ | grep -i nvidiaも調べた所、nvidia_icd.jsonもlibGLX_nvidia.so.0も存在しないことがわかり、WSLのドライバスタックがそもそもVulkan描画ライブラリを積んでいないことがわかりました。
$ ls /usr/share/vulkan/icd.d/
asahi_icd.json intel_hasvk_icd.json lvp_icd.json radeon_icd.json
gfxstream_vk_icd.json intel_icd.json nouveau_icd.json virtio_icd.json
$ ls /usr/lib/wsl/lib/ | grep -i nvidia
libnvidia-encode.so
libnvidia-encode.so.1
libnvidia-gpucomp.so
libnvidia-gpucomp.so.610.43.02
libnvidia-ml.so.1
libnvidia-ngx.so.1
libnvidia-opticalflow.so
libnvidia-opticalflow.so.1
nvidia-ngx-updater
nvidia-smi
対処方法
Vulkanが対象のGPUを認識できない場合の定番の対処は、/usr/share/vulkan/icd.d/にNVIDIA用のICDマニフェスト(nvidia_icd.json)を自作し、library_pathにlibGLX_nvidia.so.0を指定することだそうです。参考1 参考2
しかし、/usr/lib/wsl/lib/にlibGLX_nvidia.so.0が存在しなかったため、この方法が使えませんでした。
そもそもネイティブLinux用のNVIDIA Vulkan ICD(libGLX_nvidia.so.0)は/dev/nvidia*デバイスを前提としており、/dev/dxg経由でGPUにアクセスするWSL2では動作しないため、WSL2にはこのライブラリが同梱されていないようです。
今回のケースで考えられる解決策は、以下のとおりです。
- Vulkan ICD/描画ライブラリが同梱されていた版のWindows用NVIDIAドライバに戻す
- madrona(BatchRenderer)を使わないようスクリプト側を修正する
Genesisのスクリプトを読んでみると、今回試そうと思っていたロボットアームの強化学習(RL)は状態ベースで画像は要らない、つまりmadronaの起動が必要ないことがわかりました。
よって、今回はスクリプトから不要にmadronaを起動させないようスクリプトを小規模修正することにしました。
スクリプトの修正
①grasp_env.py:__init__ の冒頭付近(self.rgb_image_shapeの直後あたり、49行目の後)にフラグを追加
# カメラ(ステレオsensor)が必要かを一元判定する
# - visualize_camera: デバッグ表示
# - record_video: 動画記録
# - use_cameras: BC/評価で明示的にONにするための拡張フラグ
self.use_cameras = bool(
env_cfg.get("use_cameras", False)
or env_cfg.get("visualize_camera", False)
or env_cfg.get("record_video", {})
)
② ステレオsensor生成ブロック(113〜138行)をif self.use_cameras:で囲む
if self.use_cameras:
if _ENABLE_MADRONA and gs.backend == gs.cuda:
CameraOptions = BatchRendererCameraOptions
cam_kwargs = dict(use_rasterizer=True)
else:
CameraOptions = RasterizerCameraOptions
cam_kwargs = {}
self.left_cam = self.scene.add_sensor(
CameraOptions(
res=(self.image_width, self.image_height),
pos=(1.25, 0.3, 0.3),
lookat=(0.0, 0.0, 0.0),
fov=60,
**cam_kwargs,
)
)
self.right_cam = self.scene.add_sensor(
CameraOptions(
res=(self.image_width, self.image_height),
pos=(1.25, -0.3, 0.3),
lookat=(0.0, 0.0, 0.0),
fov=60,
**cam_kwargs,
)
)
③ _reset_idxのキャッシュ無効化(241〜242行)もガードする
# Invalidate camera caches after state change
if self.use_cameras:
self.left_cam._stale = True
self.right_cam._stale = True
④ grasp_train.py:教師あり学習(BC)でカメラが必要なので、そこだけ明示ON(1行)
BC(--stage=bc)はステレオ画像を使うので、フラグを立てておきます。
env_cfg["num_envs"] = ... の行の近く(グラフ環境生成の直前)に:
env_cfg["use_cameras"] = args.stage == "bc"
⑤env_cfg["visualize_camera"] = True を、ステージと--recordに応じて決める形に置き換える
env_cfg["num_envs"] = 10
env_cfg["box_fixed"] = False
# ステレオsensorはCUDA上でBatchRenderer(madrona/Vulkan)を通る。
# 画像を消費するBC評価か、ステレオ動画を記録するときだけ必要。
# 状態ベースのRL評価ではOFFにし、madronaを初期化させない(WSL2のVulkanクラッシュ回避)。
needs_stereo = (args.stage == "bc") or args.record
env_cfg["use_cameras"] = needs_stereo
env_cfg["visualize_camera"] = needs_stereo
これでスクリプトの修正は完了です。
結果
修正後、先ほどのコマンドを使ってロボットアーム学習を開始します。
python third_party/genesis-world/examples/manipulation/grasp_train.py -e arm-grasp-v1 -B 8192 --max_iterations 301
しばらくすると学習が完了します。
学習が完了したら、以下のコマンドで評価シミュレータを開始してみましょう。
python third_party/genesis-world/examples/manipulation/grasp_eval.py -e arm-grasp-v1 --stage rl
しばらくすると、ロボットアームがターゲットの赤い箱にアームを伸ばすことが確認できます。
残った課題
今回はロボットアームの強化学習のみを行う予定だったので、強化学習(RL)時にmadronaを通らせない方法を選びました。
もし、強化学習で生成したモデルを元にした教師あり学習(BC)までやるにはVulkanのGPU認識問題を解決する必要があります。WSL2でVulkanにGPUを認識させる方法として以下の記事が参考になります。
ただし、それでmadrona(BatchRenderer)が要求するVulkan拡張まで揃うかは別途検証が必要です。
まとめ
本記事ではWSL2でGenesisのロボットアームの学習(manipulation)を試す時に発生したVulkanがGPUを認識しない問題と、Genesisのスクリプトを修正することで強化学習のみ実行する手順を紹介しました。
サンプルスクリプトを動かす段階で、カメラ画像を使用するにあたってのエラーを知ることができたのは幸運だったかもしれません。またGenesisを使ったPythonスクリプトを読むいい機会になりました。
