🦆

NVIDIA自動運転シミュレーターAlpaSimで自社開発VLAモデル「DriveHeron」を走らせてみた

に公開

はじめに

Turing foundation-AIチームに所属している東京科学大学の川村です。

NVIDIAからAlpamayoとあわせて、自動運転向けのシミュレーション環境である AlpaSim が公開されました。AlpaSimは、実走行ログから3D Gaussian Splatting (3DGS)を用いて再構成されたシーン上で、自動運転モデルをクローズドループ(closed-loop)で評価できるシミュレーターです。

本記事では、Alpamayoなどの公式で用意されているモデルだけでなく、自分たちで開発した独自モデルをAlpaSim上で動かすために必要な手順を紹介します。独自モデルをデプロイして評価をする際の参考になれば嬉しいです。

AlpaSimとは


AlpaSimは、NVIDIAが公開した自動運転向けのシミュレーション環境です。従来のオープンループ評価とは異なり、実走行ログから再構成された3Dシーン上で、運転モデルをクローズドループで評価できる点が大きな特徴です。

AlpaSimでは、NuRec(neural reconstruction / neural rendering)由来のデータを使って3Dシーンが構成されており、レンダリング、物理、交通流、コントローラなどのコンポーネントをランタイムが統合して動作させます。ユーザーはVaVAM / Alpamayo / Transfuser / 手動操縦など、複数のポリシーを切り替えて実行できます。

また、AlpaSimでは評価用に910シーンが公開されています。本記事では、執筆時点で正常に実行できた909シーンを評価対象としました。

AlpaSimの概要については、以下の記事も参考になります。本記事では、独自モデルをdriverとして追加し、実際に動かすための流れに焦点を当てます。
https://zenn.dev/hakuturu583/articles/alpasim-introduction

独自モデルをAlpaSimで動かす

AlpaSimの全体像


AlpaSimは、責務ごとに分かれたサービスをgRPCで接続し、runtimeがそれらを呼び出しながらrolloutを進める構成になっています。主な要素は以下です。

  • runtime
    • 各ステップで必要なイベントを順番に実行する
  • sensorsim
    • 現在の自車位置や他車位置をもとに、自車視点のカメラ画像を生成する
  • driver
    • sensorsimから得られた画像、自車状態、routeなどを入力として受け取る
    • 運転モデルを実行し、将来軌跡を出力する
  • controller
    • driverが出力した将来軌跡を、車両モデルで実際の自車挙動に変換する
  • physics
    • 自車や他車の姿勢に対して、地面との接地拘束などを適用する
  • trafficsim
    • 自車の動きに応じて、周囲の挙動を更新する
  • eval
    • rollout後のログをもとに、衝突率などの指標を計算する

AlpaSimではdriverが運転モデルのラッパとして動作するため、独自モデルをAlpaSim上で評価したい場合は、driver内にモデルを追加し、runtimeから呼び出せるように設定します。

driverの構造

driverは、runtimeから送られてくる画像・自車状態・ルート情報などを受け取り、運転モデルを実行して将来軌跡を返す役割を持っています。driverサービスの本体は main.pyEgoDriverService です。ここでは主に以下の処理を行います。

  • gRPC経由で画像、自車状態、ルート情報を受け取る
  • 受け取った情報を PredictionInput にまとめる
  • 設定で指定されたモデルの predict_batch() を呼び出す
  • モデルが返した軌跡をAlpaSimのtrajectory形式に変換してruntimeへ返す

EgoDriverService はモデルに依存しない共通部分で、実際の推論ロジックは外から差し込む形になっています。

driverから呼び出されるモデルは、BaseTrajectoryModel を継承して実装します。

独自モデル実装に必要なメソッド・属性・入出力

以下のような情報を定義します。

  • from_config(): 設定ファイルからモデルを初期化する
  • predict(): 1サンプル分の入力から将来軌跡を予測する
  • predict_batch(): 複数サンプルをまとめて推論する
  • _encode_command(): AlpaSim側のナビコマンドをモデル用の形式に変換する
  • camera_ids: モデルが使うカメラID
  • context_length: 何フレーム分の履歴を使うか
  • output_frequency_hz: 出力軌跡の時間間隔

PredictionInput には、推論に必要な情報がまとめられています。すべてのモデルが全フィールドを使う必要はなく、必要なものだけを読めばよいです。

フィールド 説明
camera_images カメラIDごとの画像列。各カメラについて context_length 分のフレームが入る
command ナビゲーションコマンド。LEFT, STRAIGHT, RIGHT, UNKNOWN のいずれか
speed 現在の自車速度
acceleration 現在の自車加速度
ego_pose_history 過去の自車姿勢

モデルは推論結果として ModelPrediction を返します。

フィールド 説明
trajectory_xy 将来軌跡
headings 各waypointでの向き
reasoning_text テキスト出力 (任意)

AlpaSimで独自モデルを動かすためには、driver内に BaseTrajectoryModel を継承したモデルを追加し、entry pointと設定ファイルから呼び出せるようにするのが基本方針です。

独自モデルのデプロイ

やることは大きく分けると次の3つです。

  1. BaseTrajectoryModel を継承したモデルクラスを実装する
  2. driverから呼び出せるように登録し、設定ファイルを用意する
  3. Wizardから driver=... を指定して実行する
モデル実装からWizard実行までの流れ

AlpaSimの基本的な実行方法は、公式の docs/TUTORIAL.md のコマンドも参考になります。本記事では、既存driverではなく独自モデルを追加する場合に必要な差分に絞って説明します。

Step 1. モデルクラスを実装する

BaseTrajectoryModel を継承して実装します。置き場所は既存モデルと同じく、たとえば以下のようになります。

src/driver/src/alpasim_driver/models/my_model.py

最小構成は次のようなイメージです。

from .base import BaseTrajectoryModel, DriveCommand, ModelPrediction, PredictionInput
class MyCustomModel(BaseTrajectoryModel):
    @classmethod
    def from_config(cls, model_cfg, device, camera_ids, context_length, output_frequency_hz):
        return cls(
            checkpoint_path=model_cfg.checkpoint_path,
            device=device,
            camera_ids=camera_ids,
            context_length=context_length,
            output_frequency_hz=output_frequency_hz,
        )

    def predict(self, x: PredictionInput) -> ModelPrediction:
        self._validate_cameras(x.camera_images)
        # ここで前処理 → モデル推論 → 軌跡への変換を行う
        # trajectory_xy: np.ndarray, shape = (T, 2)
        # headings: np.ndarray, shape = (T,)
        return ModelPrediction(
            trajectory_xy=trajectory_xy,
            headings=headings,
            reasoning_text=None,
        )
    def _encode_command(self, command: DriveCommand):
        # AlpaSim側の command を、自分のモデルが期待する表現に変換する
        ...

    @property
    def camera_ids(self):
        return self._camera_ids

    @property
    def context_length(self):
        return self._context_length

    @property
    def output_frequency_hz(self):
        return self._output_frequency_hz

既存実装を読む場合は、 vam_model.py が参考になります。

なお、predict_batch() はデフォルトでは predict() を順番に呼ぶ実装になっています。GPUでまとめて推論したい場合は、必要に応じて predict_batch() をoverrideします。最初は predict() だけで動かし、速度が必要になってからbatch化するのでも十分です。

Step 2. モデルを登録し、driver設定を用意する

次に、作成したモデルをAlpaSimのdriverから見つけられるようにします。

driverパッケージの pyproject.toml に、entry pointを1行追加します。

[project.entry-points."alpasim.models"]
my_custom = "alpasim_driver.models.my_model:MyCustomModel"

ここで指定した my_custom が、設定ファイル内の model_type になります。

model:
  model_type: my_custom

entry pointを追加したあとは、環境に変更を反映する必要があります。

次に、Wizardから選べるdriver設定を追加します。AlpaSimでは、driverごとに設定ファイルを用意します。

src/wizard/configs/driver/my_custom.yaml

設定ファイルには、使用するモデル名、checkpoint、カメラ、context lengthなどを書きます。

model:
  model_type: my_custom
  checkpoint_path: "/mnt/models/my_custom"
  device: "cuda"

inference:
  use_cameras:
    - camera_front_wide_120fov
  context_length: 1
  max_batch_size: 1
  output_frequency_hz: 2

実際には、モデルの重みをcontainer内から読めるようにするためのvolume mountや、runtime側で使うカメラ設定も必要になります。既存driverでは、driver本体の設定とruntime側の設定を分けて管理しています。たとえば、既存のdriver configや docs/TUTORIAL.md のDriver節を見ると、どのような設定が必要になるか把握しやすいです。

Step 3. Wizardで実行する

設定ができたら、Wizardから driver=my_custom を指定して実行します。

公式tutorialの実行例と同じく、公開テストシナリオでまとめて評価する場合は、以下のようなコマンドになります

uv run alpasim_wizard \\
  deploy=local \\
  topology=1gpu \\
  driver=my_custom \\
  scenes.test_suite_id=public_2507 \\
  wizard.log_dir=$PWD/results/my_custom_public_2507

TuringのVLAモデル「DriveHeron」を評価してみる

DriveHeronは、Turingが自社開発した、VLMを自動運転向けに拡張したVision-Language-Action (VLA) モデルです。
https://x.com/ymg_aq/status/2036987361595437277
上記の手順で、DriveHeronをdriverとして追加し、AlpaSim上で評価してみました。

DriveHeronに関しては以下のTechTalkでも解説しているので、興味がある方はこちらもあわせてご覧ください。

https://turipo.tur.ing/techtalk39/

定量評価

ここでは、論文で主要指標として扱われている以下の3つのメトリクスに絞って、Alpamayo 論文値・Alpamayo 公開モデル・DriveHeron(2B)の結果を並べます。

  • close encounter rate (at-fault) ↓:自車責任の close encounter が起きたシナリオの割合
  • offroad rate ↓:自車が走行可能エリア外に出たシナリオの割合
  • AlpaSim score (at-fault) ↑:incidents(offroad or close encounter)間の平均走行距離(km)。走行距離 / incident 回数

    Alpamayo-R1 論文より


DriveHeronのAlpaSim Score

論文値との比較にはいくつか注意点があります。論文では closed-loop 評価を920シナリオで行っていますが、本記事の手元評価では実行可能だった909シーンを対象にしています。また、DriveHeron や Alpamayo 公開モデルの評価は1回のみの実行結果であり、乱数や実行条件を変えた複数回評価による平均・分散は確認できていません。

今回の条件ではDriveHeronは AlpaSim score が 0.75 と最も高く、Alpamayo-R1 10B の論文値である 0.72 と比べても高い値になりました。AlpaSim score は incident 間の平均走行距離を表すため、全体として長く安定して走れていたと見ることができます。
DriveHeronは offroad rate が低く、走行可能領域から大きく外れにくい傾向が見られました。一方で、close encounter rate (at-fault) は Alpamayo モデルと比べると高めで、周囲の交通参加者との距離の取り方や、インタラクションにはまだ課題がありそうです。

この差には、学習データと評価環境の違いも影響していると考えています。DriveHeronは社内で収集した日本での走行データをもとに学習しているため、左側通行と右側通行の違いを含め、車線構造や信号、周囲の交通環境が異なります。こうしたドメインギャップは、特に他車との距離感や合流・交差点付近の判断に影響しやすいはずです。

定性評価

DriveHeronの実際のシミュレーション動画をいくつか紹介します。緑色の線がGround Truth軌跡を表します。

直進・レーンキープ

車線から外れることなく安定して走行できています。他の直進シナリオでも同様に、道路形状に沿って自然に走れているケースが多く見られます。

交差点で車両を待つ

交差点のシーンでは、路上駐車車両によって見通しが悪い中でも、横方向から交差点を通過する車両を待ってから進むことができています。

右折

右折シーンでは、右折自体はできていた一方で、その後に左側の車線を走ってしまう挙動が見られました。これは先ほど説明したような学習データとのドメインギャップが影響している可能性があります。

最後に

今回は、NVIDIAが公開している自動運転シミュレーター「AlpaSim」に独自モデルを追加し、クローズドループで評価する方法を紹介しました。

実際にDriveHeronを動かしてみると、定量指標だけでなく、定性評価を通してモデルの得意不得意を確認できることが分かりました。

特に、直進やレーンキープのように安定して走れる場面がある一方で、他車とのインタラクションや、学習データと評価環境の違いが影響していそうな場面も見られました。

普段は、日本で収集した走行データを3DGSで再構成した環境上でもモデル評価を行っていますが、AlpaSimを使うことで、日本とは異なる交通環境を含むシーンでもモデルを評価でき、DriveHeronがどの程度ロバストに走行できるかを見ることができました。

独自モデルをAlpaSimにデプロイし、クローズドループで評価してみたい方の参考になれば嬉しいです。


筆者の所属するTuringは完全自動運転の開発を目指すスタートアップで、AI開発から、ソフトウェア・ハードウェアを横断的なエンジニアリングに取り組んでいます。

もし興味のある方はCTO山口さんのX/TwitterのDMにてご連絡いただくか、HPからカジュアル面談をぜひお願いします!

https://jobs.tur.ing/

Tech Blog - Turing

Discussion