💻

自動運転開発を始めたい人必見!MetaDriveでお手軽シミュレーション!

2024/12/13に公開

はじめに

こんにちは!チューリング株式会社のドライビングソフトウェアチームに所属している堀ノ内です!

今回は弊社で導入を検討している自動運転開発用シミュレータについてお話させて頂きます。以前のブログにて自動運転開発におけるシミュレータの必要性についても言及しておりますので合わせてご覧ください。

これまでチューリングではCARLAというシミュレータを使っていたのですが、レンダリング処理が重く高性能なGPUを搭載したデスクトップPCでないと動かせないという問題がありました。そこで今回は比較的軽量なMetaDriveと呼ばれるシミュレータについて調査してみました。MetaDriveはCARLAと比較すると以下のような特徴があります。

メリット

  • CPUだけでも動作可能。GPU付きのゲーミングノートやデスクトップPCではより軽快に動作する
  • 直線、カーブ、交差点などを組み合わせて任意のコースを簡単に作ることができる

デメリット

  • 動作の軽快さを重視しているため、リアルな風景のCG表現などは苦手
  • 公式ドキュメントの内容が若干少ない。ただし、リポジトリの以下にサンプルやテストコードがあるため、こちらを参考にすることで様々な機能を試すことができます。
metadrive/metadrive/examples
metadrive/metadrive/tests

MetaDrive(公式から引用)
MetaDrive(公式から引用)

今回は現時点での最新リリースである0.4.3を用いました。ここからは環境構築、MetaDriveでできることなどについて記載します。

環境構築

筆者が今回の調査で用いたノートPCのスペックは以下の通りです。

  • CPU: AMD Ryzen 7 5800H
  • NVIDIA GeForce RTX 3050
  • Memory: 8GB
  • OS: Ubuntu 22.04

レンダリングをGPUで行う場合とCPUで行う場合の環境構築方法について、以下にそれぞれ記載します。これらの情報は公式ドキュメントにもある程度記載があるため、問題が発生した際はこちらも合わせてご覧ください。また筆者の環境で発生したトラブルについては本記事の最後にトラブルシューティングとして記載しました。

レンダリングをGPUで行う場合

まず初めに、GPUを使うためにnvidia driverCUDAをインストールする必要があります。これらは使用するPCの環境に大きく依存するため、リンク先のNVIDIA公式ページを参照しながらご自身の環境に合ったソフトウェアをインストールしてください。筆者の環境では今回以下のバージョンをインストールしました。

nvidia-driver-550 550.120-0ubuntu0.22.04.1 amd64 NVIDIA driver metapackage
cuda-toolkit-12-6  12.6.2-1 amd64 CUDA Toolkit 12.6 meta-package

次にMetaDriveのソースコードをcloneします。ここで適宜仮想環境などを構築しても良いでしょう。

$ git clone https://github.com/metadriverse/metadrive.git
$ cd metadrive

サンプルを動かす際の依存パッケージとしてcupy、PyTorch関連をインストールします。今回はcuda12系ですので以下をインストールします。こちらもPC環境に合わせる必要がありますので、適宜PyTorchの公式ドキュメントなどを参照ください。

$ pip install cupy-cuda12x
$ pip install torch torchvision torchaudio

続けて更に依存パッケージをインストールします。


$ pip install -e .[cuda]

ただし、この際、筆者の環境ではmetadrive/setup.pyに以下の修正が必要でした。

 cuda_requirement = [
-    "cuda-python==12.0.0",
+    "cuda-python==12.3.0",
     "PyOpenGL==3.1.6",
-    "PyOpenGL-accelerate==3.1.6",
+    "PyOpenGL-accelerate==3.1.7",
     "pyrr==0.10.3",
     "glfw",
 ]

最後にインストールが正常に完了したか動作確認をしてみましょう。

$ cd metadrive/examples
$ python verify_image_observation.py --cuda --render

以下のようにレンダリング画面が表示され、車が走っていれば成功です!

MetaDriveの環境構築テスト
MetaDriveの環境構築テスト

GPUを使えているかはnvidia-smiコマンドで確認してみましょう。pythonのプログラムがGPUのメモリを1348MiB使っていると表示されていますので、GPUレンダリングにも成功していることが分かります。

(.venv) metadrive $ nvidia-smi
+-----------------------------------------------------------------------------------------+
| Processes:                                                                              |
|  GPU   GI   CI        PID   Type   Process name                              GPU Memory |
|        ID   ID                                                               Usage      |
|=========================================================================================|
|    0   N/A  N/A      2167      G   /usr/lib/xorg/Xorg                            153MiB |
|    0   N/A  N/A      6370    C+G   python                                       1348MiB |
+-----------------------------------------------------------------------------------------+

レンダリングをCPUで行う場合

以下の手順に従ってサンプルが動作するところまで確認できればOKです。

$ pip install -e .
$ python verify_image_observation.py --render

以降はGPUでレンダリングを行うことを前提に進めます。

基本構文

ここではMetaDriveの基本的な構文について説明します。詳細はコード中にコメントで記載しました。

from metadrive.envs.metadrive_env import MetaDriveEnv
from metadrive.component.map.pg_map import MapGenerateMethod

# 直線道路を作るためのdictを生成する
def straight_block(length):
  return {"id": "S", "pre_block_socket_index": 0, "length": length}

# カーブ道路を作るためのdictを生成する
def curve_block(length, angle=45):
  return {"id": "C", "pre_block_socket_index": 0, "length": length, "angle": angle}

# 環境の設定。ここで
config = dict(
    # Trueだと3Dレンダリングが有効になる
    use_render=True,
    # Trueだと車が車線を逸脱した場合にenv.step()が返すterminatedがTrueになる
    out_of_route_done=True,
    # マップの生成(直線 -> 90度カーブ -> 直線)
    map_config=dict(
        type=MapGenerateMethod.PG_MAP_FILE,
        config=[
            None,
            straight_block(60),
            curve_block(60, 90),
            straight_block(60),
        ],
        lane_num=1,  # 車線数
    ),
    traffic_density=0.0, # 自車以外の車の交通量
)

# ↑で作ったconfigを適用してシミュレータ環境を生成
env = MetaDriveEnv(config)
# シミュレータ環境をリセット
obs, _ = env.reset()
for i in range(1000):
  # シミュレーションを1step進める
  # [0, 1]はそれぞれステア角(degree)、アクセル/ブレーキ量(hp)。[-1, 1]の範囲で入力する
  # この量をシミュレータ上の車両に毎ステップ適用することで車両が移動する
  # 詳細は以下参照
  # https://metadrive-simulator.readthedocs.io/en/latest/action.html#action
  # obs: 1step進めた後に得られた観測情報。この中には車両の位置や方向、カメラなどのセンサが
  #      ついていれば画像情報などが格納される
  # terminated: 予め設定したシミュレーションの終了条件があった場合、それを満たすとTrueになる
  #             今回はout_of_route_done=Trueを設定しているため、車両がレーンを
  #             逸脱するとTrueになりプログラムが終了するようにしている
  obs, _, terminated, _, _ = env.step([0, 1])
  # 2Dレンダリング及び、ウインドウや描画範囲の設定など
  env.render(
      mode="topdown",
      screen_size=(1200, 800),
      window=True,
      scaling=3,
      # film_size / scalingの範囲までしか描画できないため、大きいマップを描画する際は要調整
      film_size=(5000, 5000))
  if terminated:
    break
env.close()

上記ファイルをmain.pyとして保存し、以下のように実行します。

$ python main.py

2Dと3Dのシミュレーション画面が表示され、車が真っ直ぐに走り、レーンを逸脱するとプログラムが終了します。

基本的な機能のみを実装したプログラムの動作
基本的な機能のみを実装したプログラムの動作

また、自動運転のシミュレータとして用いるには車両にセンサを取り付けられる必要があります。ここではカメラを取り付けてみましょう。先程のコードを以下のように編集します。

+import cv2
+
 from metadrive.envs.metadrive_env import MetaDriveEnv
 from metadrive.component.map.pg_map import MapGenerateMethod
+from metadrive.component.sensors.rgb_camera import RGBCamera

 # 直線道路を作るためのdictを生成する
 def straight_block(length):
@@ -27,6 +30,9 @@
         lane_num=1,  # 車線数
     ),
     traffic_density=0.0, # 自車以外の車の交通量
+    vehicle_config=dict(image_source="rgb_camera"),
+    sensors={"rgb_camera": (RGBCamera, 320, 240)},
+    image_observation=True,
 )

 # ↑で作ったconfigを適用してシミュレータ環境を生成
@@ -45,6 +51,8 @@
   #             今回はout_of_route_done=Trueを設定しているため、車両がレーンを
   #             逸脱するとTrueになりプログラムが終了するようにしている
   obs, _, terminated, _, _ = env.step([0, 1])
+  cv2.imshow('img', obs["image"][..., -1])
+  cv2.waitKey(1)
   # 2Dレンダリング及び、ウインドウや描画範囲の設定など
   env.render(
       mode="topdown",

車両に取り付けた仮想カメラから見た画像データで取得しOpenCVのウインドウで表示できました。
車両に仮想カメラを付けた状態での動作
車両に仮想カメラを付けた状態での動作

つまり、仮想カメラから毎フレーム得られる画像を入力として車両のステアリング量、アクセル量出力する機械学習モデルを構築することができれば

env.step([0, 1])

の[0, 1]の部分に入れてあげることで自動運転させることができるのです!(実際にそのようなモデルを作るのは難しいのですが、、、)

このようにMetaDriveが自動運転シミュレータに必要な機能を持っていることがお分かり頂けたかと思います。

その他の機能

MetaDriveには様々な機能がありますが、ここからは自動運転開発を行うにあたり有用そうな機能をピックアップしてご紹介します。実装の一部のみを抜粋しますので、これらのキーワードを元にexampleを参照して頂ければと思います。

オリジナルコースの作成

自動運転の開発においては様々なコースで実験を行うことが重要です。CARLAでは独自のコースを作るのに色々と手間が必要なのですが、MetaDriveでは直線、カーブ、交差点などの部品を組み合わせて簡単にコースをカスタマイズすることができます。以下は直線、カーブ、直線、交差点を組み合わせた例です。

def straight_block(length):
  return {"id": "S", "pre_block_socket_index": 0, "length": length}

def curve_block(length, angle=45):
  return {"id": "C", "pre_block_socket_index": 0, "length": length, "angle": angle}

def intersection_block():
  return {"id": "X", "pre_block_socket_index": 0}

# Set the environment config
config = dict(
    map_config=dict(
        type=MapGenerateMethod.PG_MAP_FILE,
        config=[
            None,
            straight_block(30),
            curve_block(60, 90),
            straight_block(30),
            intersection_block()
        ],
        lane_num=1,
    ),
)

オリジナルのコース
オリジナルのコース

表示方式の切り替え

シミュレーションの表示方式として2D(左の図)、3D(右の図)、そして画面無し(ヘッドレス)に対応しています。場合によっては3Dほどの表現は不要であることもありますし、ヘッドレスで動かせば、CI上で自動運転の検証を行うことも可能かもしれません。以下のオプションをつけることで3Dレンダリングを有効 or 無効にすることができます。

config = dict(
    use_render=True, # 3Dレンダリングする場合はTrueにする
    ...
)
env = MetaDriveEnv(config)

# ヘッドレスで行う際はuse_render=Falseとし、env.render()をコールしない。

2D/3Dレンダリング画面
2D/3Dレンダリング画面

オブジェクト設置

自動運転開発においてはコース上に人や車などのオブジェクトを設置して人の前で停止できるか、前方車を追従できるか、といった機能を試したいことがあります。MetaDriveではspawn_object()使うことでオブジェクトを任意の位置に設置できます。設置できるものは車、人、コーン、信号機などです。また設置したオブジェクトを動的に動かすということも可能です。

vehicle = env.engine.spawn_object(DefaultVehicle, vehicle_config=env.config["vehicle_config"], position=[50,0], heading=0)
# 動かす
vehicle.before_step([0, 0.3])

オブジェクト設置
オブジェクト設置

ScenarioNet

自動運転開発では様々なシチュエーションのでのテストが必要です。しかしながら、エンジニアが手動でそのようなシナリオをすべて記述するのは難しく、そもそも想定していないケースは記述することができません。そのような課題を解決するため、MetaDriveでは現実空間で得たデータに対して、物体認識結果などのアノテーションを所定のフォーマットで保存しておくと、その状況をCGで再現してくれるという機能が実装されており、ScenarioNetと呼ばれます。自動運転に関する研究で広く活用されているnuScenesデータセットもScenarioNet形式に変換することができ、nuScenesのシーンをCGで再現し、CG上でclosed loopのテストを行うことができます。

ScenarioNet(公式から引用)
ScenarioNet(公式から引用)

まとめ

いかがだったでしょうか?自動運転を開発するために必要最低限の機能は備わっているシミュレータかと思います。また、簡易的なレンダリングではありますが、ノートPCでも動かすことができるというのは開発する上で非常に大きなメリットだと思います。

現実問題としてはシミュレータで得られた画像と現実世界の画像で大きな乖離があり、現実世界のデータでのみ学習したモデルはシミュレータ上では正しく動作しない可能性があります。今後もシミュレータについては検討を続け、活用できる場面では積極的に活用し、開発効率を上げていく予定です。

チューリングではソフトウェアエンジニアを絶賛募集中ですので、自動運転シミュレーション、自動運転ソフトウェアについてご興味がある方は筆者のメールアドレス(tsukasa.horinouchi@turing-motors.com)に連絡頂くか、チューリング株式会社の採用ページをご覧ください!ご応募お待ちしてます!

トラブルシューティング

AssertionError: You have to install torch to use CUDA

torchがインストールされているにも関わらず、以下のようなエラーがでることがあります。

$ python verify_image_observation.py --cuda --render
Can not find torch
Traceback (most recent call last):
  File "metadriverse/metadrive/metadrive/examples/verify_image_observation.py", line 104, in <module>
    assert torch_available, "You have to install torch to use CUDA"
AssertionError: You have to install torch to use CUDA

筆者の環境ではtorchを再インストールすることで解消できました。

$ pip install --force-reinstall torch

RuntimeError: cudaErrorUnknown(999): unknown error

レンダリングの際にGPUを上手く指定できていないことに起因するようです。筆者の環境では、以下のように環境変数として__NV_PRIME_RENDER_OFFLOADと__GLX_VENDOR_LIBRARY_NAMEを付けて実行することで解消できました。

$ __NV_PRIME_RENDER_OFFLOAD=1 __GLX_VENDOR_LIBRARY_NAME=nvidia python verify_image_observation.py --cuda --render
Tech Blog - Turing

Discussion