0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

この記事でできること

学習用シミュレータから別のシミュレータへ移す前に、出力の単位・車の応答・センサー・相手の動き・CPU負荷を順に点検できます。各比較のモデル、台数、試行回数を併記し、1本の成績を一般化しないようにしています。

自動運転AIチャレンジ2026 の End to End AI 部門で、私(高専生1人のチーム KSK)は 2D LiDAR だけで舵を決めるモデルを自作の GPU シミュレータで学習させ、決勝戦で2位(準優勝)になりました。この記事は、その途中でいちばん時間を取られた「自作シミュレータと本番シミュレータの差」の記録です。

この記事の要点

  • 自作 GPU シムで当時最高の成績(検証コースで 45 秒間ぶつからなかった割合 90.2%)を出した中間版モデルが、本番シミュレータ AWSIM の4台6周では4台とも完走できませんでした(4台とも3〜4周で制限 300 秒の時間切れ、罰 23 件・計 203.1 秒、1本)。自作シムで 83.6% だった別の版は、同じ条件の1本で 3/4 完走でした。
  • 差の出どころは5つありました。
    • 舵の倍率:同じ重みで1周 93 秒 → 43 秒(中間版・AWSIM 単独2周)
    • 車両モデル:同じ舵でも 8.75 → 6.56 m/s(初期版が AWSIM で出した舵を、自作シムの車両モデルに流したもの)
    • 壁の形:同じ姿勢から見たスキャンが平均 1.28 m ずれる(AWSIM の本番コースを走った記録の姿勢で比較)
    • 相手の動き:他車に入れた「時間の 25% は 5 km/h」は、AWSIM で前を走る遅い車の実測(2.4% と 0.0%)の 10 倍以上(混走1本の記録)
    • CPU の取り合い:スレッド数を1にすると完走 8/12 → 12/12(初期版・4台×3本)
  • 最後は「自作シムはデータ集めと候補の絞り込みに使い、採否は AWSIM で複数本走らせて決める」という運用に落ち着きました。

自作シムと AWSIM で順位が逆になった2つの中間版モデル
自作シムの検証で上だったモデルが、AWSIM の4台6周(各1本)では1台も完走しませんでした。どちらも中間版で、自作シムの値は 45 秒間ぶつからなかった割合です(詳しくは「罠4」)

30秒でわかる自動運転AIチャレンジ

自動運転AIチャレンジ2026(自動車技術会主催)は、レーシングカートを自動運転ソフトで走らせて競う大会です。End to End AI 部門は、センサーの入力から操舵までを学習モデルでつなぐ部門で、決勝は公式シミュレータ AWSIM の上で4台が同時に走るレースでした。この部門で私は、選抜戦は組で1位、選抜戦と発表の総合2位で決勝戦に進み、決勝戦で2位(準優勝)でした。もう1つの Sim to Real 部門は、予選が学生クラス2位、SIM 選抜戦で敗退です。

関連する記事と動画です。

前提:何を作って、どこで測ったか

モデルは公式サンプルの TinyLidarNet(1次元の畳み込みネットワーク、約15万パラメータ)で、ネットワークの形は変えていません。入力は 2D LiDAR の距離 750 個だけで、出力のうち舵だけを使います。アクセルは一定で、ブレーキと壁からの後退はモデルの外のルールです。

学習は自作の GPU シミュレータで行いました。乱数で作ったコースの上で、Pure Pursuit(中心線上の少し先の点へ向けて舵を切る、古典的な追従のしかた)を先生役にして、その舵を生徒のモデルにまねさせます。まねのさせ方は BC(Behavior Cloning、先生の舵をそのまま回帰で写す)と DAgger(生徒に運転させ、ずれた場面に「先生ならこう切る」を足して学び直す)です。

最終モデルと、本番コースを除外する仕組みを入れた後に学習した中間版は、本番コースの走行データを学習に入れていません(コース名で読み込み時に弾いています)。最初の試作だけはその仕組みを入れる前に学習していて、学習に使ったコースの置き場に本番コースが入っていたので、ほぼ確実に学習に混ざっています。また、本番コースは選ぶことと調整には使いました。自作シムに置いた本番コースでの完走率をモデルや先生の設定を選ぶ指標にしましたし、後で出てくる舵の倍率 0.80 などは AWSIM の本番コースを走らせて決めています。

この記事に出てくるモデルの版は3種類です。数字ごとにどの版かを書きます。

呼び名 中身
初期版 AWSIM の本番コースで、SW 部門の制御器を先生にして収録・学習した版
中間版 自作シムで学習した途中の版。いくつもあるので、本文では「最初の試作」「揺らぐ相手の版」のように書き分けます。最初の試作だけは本番コースの除外を入れる前に学習
最終モデル 本番コースの走行データは学習に不使用。決勝で使用

2つの「完走」と、この記事の用語

自作シムの「完走率」と AWSIM の「完走」は、ものさしが違います。

  • 自作シムの完走率:学習に使っていない検証用のコースに置いた車のうち、45 秒(900 ステップ)のあいだ壁や他の車にぶつからなかった割合です。1周を走り切ったかどうかは見ていません。
  • AWSIM の完走:制限時間内に6周を走り切ることです。

この記事で自作シムの%と AWSIM の「○/4 完走」を並べるのは、同じものさしで比べるためではなく、順位が入れ替わったことを示すためです。

ほかに、次の言葉を使います。

  • 罰:壁や他の車にぶつかったときなどに科されるペナルティです。公式の結果ファイルに件数と秒数が出ます。罰の間は車速が約 5 km/h に抑えられます(走行記録で見た値)。
  • 減速ガード:前方の最短距離を車速で割って衝突までの時間(TTC、Time To Collision)を出し、0.45 秒より短いと減速する、モデルの外のルールです。最終的に採用しました。
  • 壁ガード:斜め前の壁が近いと、反対側へ舵を足すルールです。試しましたが、最終的には採用していません。

2つのシミュレータ

シミュレータで作ったものが実機で動かない問題(sim-to-real)と同じことが、シミュレータどうしでも起きます。ここでは sim-to-sim と呼びます。

自作 GPU シム AWSIM(本番シミュレータ)
中身 PyTorch のテンソルで N 台ぶんのカートを一括で計算 Unity 製の公式シミュレータ。Autoware(ROS 2)とつないで走らせる
同時に走らせる数 256 環境 最大4台。実時間で進む(CPU が足りないとさらに遅い)
処理量 256 環境の合計で 13.1k ステップ/秒 20 ステップ/秒(LiDAR 20 Hz)
他の車 半径 0.85 m の円が中心線に沿って等速で動く 実際に運転している車(手元の検証では自チームのモデル、本番は他チームのソフト)
使い道 データ集め・学習・候補のふるい 採否の判定

処理量の比は約 655 倍ですが、これは全環境を合計した処理量の比で、学習が 655 倍速く終わるという意味ではありません(1環境あたりでは約 51 ステップ/秒です)。

自作 GPU シムの俯瞰
自作 GPU シムの俯瞰(試走、開始 8〜20 秒)。乱数で作ったコースの上の 12 台は互いにぶつからない別々の環境で、1枚に重ねて描いています。上の「生存」はまだ壁にぶつかっていない台数、黄色の車の LiDAR はオレンジで表示。走らせた重みがどの版かは記録が残っていません

AWSIM の4台混走
AWSIM の4台混走(1周目)。4台とも自チームのモデル(最終モデル ×2、中間版 ×2)で、互いの画面に他の車がはっきり写ります

最初の転移は 21 km/h

自作シムで学習した最初の試作(自作シムのランダムコースで検証平均 84.5%)を、はじめて AWSIM に載せました。走りはしましたが、画面の表示で車速 21 km/h(2周目の時点で計時は 158 秒)で、制限 600 秒で6周に届きませんでした。当時 AWSIM で学習していた初期版は 38 km/h、1周 40.6〜41.2 秒です。逆向き、つまり初期版の重みを自作シムの本番コースに載せる実験もその前にしていて、こちらは 64 台すべてが途中でぶつかりました(完走率 0.0%)。

ここから2日間で、差を1つずつ見つけて潰していきました。

罠1:舵の倍率を一度も AWSIM で確かめていなかった

自作シムでは、モデルの出力 $u \in [-1, 1]$ に最大舵角 0.64 rad を掛け、さらに「舵の効き」の係数 0.70(SW 部門で AWSIM の走行記録から合わせた値)を掛けたものが実際に効く舵角でした。

$$
\delta_{\mathrm{eff}} = 0.70 \times 0.64,u = 0.448,u
$$

一方、AWSIM 側の推論ノードは出力をそのまま舵角 [rad] として送っていました。自作シムの定義どおりに計算すると、学習した重みをそのまま載せれば舵が約 2.2 倍(1/0.448)効くはずでした。最初の転移(21 km/h の回)は、倍率を掛けずにそのまま載せた状態です。

そこで倍率を掛ける口を推論ノードに足し、まず走らせずに確かめました。AWSIM の本番コースを初期版が走った記録から、実スキャン 5,315 枚に最初の試作を通して、そのとき実際に出ていた指令と比べます。差は RMSE(差の二乗を平均して平方根をとった値)で見ます。

条件 舵のばらつき(標準偏差) 実際の指令との差(RMSE)
実際に出ていた指令 0.2200 -
最初の試作をそのまま 0.4270 14.23°
最初の試作 × 0.448 0.1913 5.66°

最小二乗で求めた最適な倍率は 0.467 で、計算値の 0.448 とほぼ一致しました。私はここで「理論と合っている」と判断し、以後、自作シムで学習した重みの AWSIM の試験は、すべて倍率 0.448 で走らせました。

その日の夜、別の中間版で AWSIM を単独2周ずつ走らせ、倍率を振ってみたのが次の図です。この版は MPC(モデル予測制御。車の動きを少し先まで予測して、最も良い操作を選ぶ制御)を先生にした系統です。

舵の倍率と AWSIM の最速ラップ
同じ重みで倍率だけを変えた比較。0.448 では1周 93.04 秒、0.80 では 43.02 秒(AWSIM・単独2周・中間版・倍率ごとに1本)

同じ重みで、ラップが 2 倍以上動きました。0.95 では当てすぎて壁に触れ、71.06 秒の周が出ています。オフラインの検算は「別のモデルが出した指令に形を合わせる」ことしか見ておらず、AWSIM の中で閉じたループで走ったときの最適とは一致しませんでした。自作シムの定義から出した「2.2 倍効く」という計算の前提自体が、AWSIM の実際と合っていなかったことになります。0.448 で出していたそれまでの AWSIM の判定は、測り直す必要が出ました。

試した2つの版では、どちらも 0.80 が良い側でしたが、理由は先生によって逆でした。舵の小さい MPC の先生(平均 5.2°)の版は舵が足りずに膨らんでいたと見ていて、倍率を上げると大きく速くなりました。舵の大きい Pure Pursuit の先生(平均 12.5°)の版は、0.95 に上げるとむしろ遅くなりました(この版で試したのは 0.80 と 0.95 の2点だけで、0.95 は単独3周で最速 46.14 秒。条件の揃っていない参考値です)。最終モデルの AWSIM での試験も 0.80 です。

推論ノードの該当部分です(コメントは短く要約しています)。

tiny_lidar_net_controller_node.py
# AWSIM の実走で最も良かったのが 0.80
self.declare_parameter('steer_scale',
                       float(os.environ.get('E2E_STEER_SCALE', '0.80')))
...
# 学習した場所に合わせて舵を実舵角[rad]に直す
steer *= self.steer_scale

埋め方は単純で、自作シムから持ってくる定数は AWSIM で数点振ってから使う、新しい重みでは毎回振る、の2つです。のちに、この倍率を含む 45 項目を走行中に ros2 param set で変えられるようにしました。

罠2:車両モデルが AWSIM より「重かった」

自作シムの Pure Pursuit の先生は、どう設定を振っても平均の速さが 4〜5 m/s で頭打ちでした。自作シムの縦の運動は次の式で、舵を切るほど減速する項が入っています。係数は 0.70 と同じく、SW 部門で AWSIM の走行記録に合わせたものをそのまま使っていました。

$$
\dot v = a_{\mathrm{cmd}} - 0.10,v - 1.3,\lvert \delta_{\mathrm{eff}} \rvert,v
$$

先生が走ったときの加速度を平均すると、加速の指令 +1.294 m/s² のうち、転がり抵抗で 0.516、舵による減速で 0.594 が消え、残りは +0.184 m/s² でした。86% が抵抗で相殺されています。

どこまでが先生のせいで、どこからが車両モデルのせいかを分けるため、AWSIM で初期版が単独で走ったときの実際の舵を、自作シムの縦の運動モデルに流しました。

AWSIM と自作シムの速度の差
左:同じ舵でも自作シムでは 8.75 → 6.56 m/s。Pure Pursuit の先生は舵を約 1.9 倍当てるので、さらに 4.34 m/s まで落ちます。右:加速の内訳

AWSIM では平均 8.75 m/s で走れた舵が、自作シムでは 6.56 m/s にしかなりません。自作シムの縦の運動は AWSIM より約 25% 重く、そのうえ Pure Pursuit は舵を AWSIM の初期版の約 1.9 倍(12.5° 対 6.7°)当てるので、4.34 m/s まで落ちます。係数を軽くすれば速くなりそうですが、SW 部門で係数を軽くした版は AWSIM で成績が落ちた記録があったので、触りませんでした。

この差から生まれた2つの期待は、どちらも外れました(どちらも AWSIM の4台6周・制限 400 秒、各1本)。

  1. 速い先生を混ぜれば速くなる。自作シムで平均 9.82 m/s 出る MPC の先生の走りを学習データの 30% に混ぜると、自作シムの本番コース(学習には不使用)で車速が 4.15 → 4.61 m/s(+11%)に上がりましたが、AWSIM では最速ラップが約 0.7 秒縮んだだけで、罰は 5 件(29.7 秒)から 7 件(59.9 秒)に増えました。
  2. 自作シムが重いぶん、AWSIM では余裕が出る。横加速度の上限を上げた先生の版(自作シムで完走率 73.4%)は、AWSIM では最速ラップ 46.20〜47.48 秒と遅く、罰も 16 件(115.9 秒)でした。

罠3:壁の形が合っていなかった

倍率を 0.448 にしても(後で分かるとおり、これ自体が小さすぎたのですが)最初の試作は AWSIM で完走しませんでした。次に疑ったのは入力そのものです。AWSIM で記録した姿勢に自作シムの LiDAR を置き、同じ場所から見たスキャンを並べました。

同じ姿勢でのスキャンの比較
同じ姿勢から見たスキャンでも、自作シムのほうが壁が近く、遠くまで抜ける点が少なくなっています。距離どうしの相関は +0.750

記録した姿勢には誤差があるので、フレームごとに位置 ±0.6 m・向き ±0.1 rad の範囲で最もよく合う姿勢を総当たりで探しました。それでも差は平均 1.28 m、中央値 0.97 m 残りました。AWSIM の実スキャンの平均距離(7.95 m)の約 16% にあたり、形の違いとして小さくありません。

原因の候補として疑って、否定できたものは次のとおりです。表の距離の差は、どれも姿勢を探索する前の比較で出した値です。ただし図の「記録した姿勢のまま」の 2.43 m とは別に集計した値なので、数字は一致しません。表で見るのは、条件を変えても差が変わらなかったことです。

疑ったこと 確かめ方 結果(姿勢を探索する前の差)
路面の傾きで地面を拾っている 標高あり・なしで比較 どちらも平均 2.47 m で変わらず。無関係
LiDAR の取り付け位置 前後位置を 0〜2.0 m で振る 差は 2.46〜2.63 m でほぼ変わらず
コースの周長が違う 自作シム 358.8 m と AWSIM の実走 348〜370 m 一致。最初に「周長が怪しい」と書いたメモは誤り

壁の形を AWSIM に合わせ込む作業は、最後までしていません。代わりに、学習は乱数で作ったコース(最終モデルは 80 本)で行って特定の壁を覚えさせないようにし、走らせる前の足切りには AWSIM の記録済みスキャンを使いました。重みを通して舵を比べるだけなら数秒で済み、AWSIM を1回走らせる 5〜10 分よりずっと安く済みます。ただし罠1のとおり、これは足切り専用で、採否には使いません。

最終モデルは、壁の形がずれたままの自作シムで学習しましたが、AWSIM の単独6周を罰0(合計 278.9 秒)で走り切っています。乱数のコースで「壁一般」を学ばせたことが形の差を吸収した、というのが当時の見立てですが、確かめてはいません。コースの本数を変えた比較も、測り直すとばらつきが大きく、結論を撤回しています。同じ40本で学習した別モデルでも結果が81〜98%にばらつき、本数だけの効果は分離できませんでした。

罠4:「現実っぽい相手」が AWSIM の実測と違った

冒頭の図の話です。自作シムの他車は「中心線に沿って等速で動く円」です。自車の 4〜25 m 先に、自車の最高速の 0.25〜0.60 倍の速さで置きます。一方 AWSIM の相手は実際に運転している車で、ぶつかれば罰で減速し(走行記録で見ると約 5 km/h に抑えられます)、避けるときは横にも動きます。

「この差が4台走行の弱さの原因だろう」と考えて、自作シムの他車に揺らぎを入れて学習し直しました。該当部分です(コメントは短く要約しています)。

e2e_sim.py
opp_stall_prob: float = 0.0       # 1周期のうち「罰で落ちている」割合
opp_stall_period: float = 12.0    # [s] その周期
opp_stall_speed: float = 1.389    # [m/s] 5km/h
opp_weave_m: float = 0.0          # [m] 横の振れ幅
opp_weave_period: float = 7.0     # [s]
...
ph = (self._opp_t / cfg.opp_stall_period + self._opp_phase) % 1.0
v = torch.where(ph < cfg.opp_stall_prob,
                torch.full_like(v, cfg.opp_stall_speed), v)

このときは opp_stall_prob=0.25(12 秒のうち 3 秒は 5 km/h)、opp_weave_m=0.5(7 秒周期の正弦波で横に 0.5 m)にしました。揺れの位相は車ごとにずらしています。

自作シムでは、揺らぐ他車3台を入れた検証で完走率が平均 90.2%(最悪のコースで 77.7%)と、それまでの最高が出ました。同じ条件で、等速の他車で学習した中間版(後で AWSIM の混走の相手役にも使う版。以下「相手役の中間版」)は 83.6% です。ところが AWSIM の4台6周(4台とも同じ重み、倍率 0.80、減速ガードなし、制限 300 秒)では結果が逆になりました。

この比較は各1本で、後で書く「3本以上ずつ測る」の基準を満たしていません。ただ、差は完走 0/4 対 3/4、罰 23 件対 3 件と大きく、揺らぐ相手の版はこの後使っていません。

中間版 自作シム(揺らぐ他車、45 秒間ぶつからない割合) AWSIM 完走(1本) AWSIM 罰 AWSIM 最速ラップ
揺らぐ相手で学習 90.2% 0/4 23 件・203.1 秒 55.95〜61.54 秒
等速の相手で学習(相手役の中間版) 83.6% 3/4 3 件・20.1 秒 44.68〜44.90 秒

未完走はどれも制限 300 秒の時間切れで、揺らぐ相手の版は4台とも3〜4周まで、相手役の中間版の1台は5周まで進んでいました。

後から、AWSIM の混走(速い初期版2台と、遅い相手役の中間版2台)を1本記録して、実際の車がどれくらい遅くなっているかを数えました。

AWSIM の混走での車速
設定した「時間の 25% は 5 km/h」に対し、前を走る遅い車が 2 m/s 未満だったのは 2.4% と 0.0%。2.4% のほうもスタート直後の 7 秒だけでした(AWSIM 混走1本の記録)

自作シムの他車がまねしたかったのは「前を走る遅い車」です。その役の2台は 2.4% と 0.0% でした。後ろから追いつく速い2台は、詰まりや罰で 11.5% と 5.9% ありましたが、それでも 25% には届きません。入れた揺らぎは前を走る車の実測の 10 倍以上で、少なくとも手元の AWSIM の混走に対しては、等速の円のほうが近かったことになります。

自作シムでだけ成績が上がった理由は確かめきれていませんが、規則的な揺らぎはそれ自体が学べる模様になるので、その周期に合わせた避け方を覚えたのだと見ています。既定は 0(等速)に戻し、「変えるなら AWSIM で A/B」とコードに書き残しました。相手の動きを本当に入れたいなら、AWSIM で記録した実際の軌跡を再生するべきでした(未実施)。

罠5:シミュレータではなく、測っている PC が詰まっていた

丸一日、AWSIM の4台走行で完走できない問題を「学習の問題」として追っていました。先生を MPC にする、ラベルの食い違いを直す、DAgger の配合を変える。自作シムの成績は上がっても、AWSIM では 0/4 のままでした。

その夜に気づいたのは、24 コアの PC で Linuxのload average(実行待ちや割り込み不能な待ち状態も含むタスク数の平均)が 84 だったことです。推論ノードは多いもので1つ 2.8 コア、4つ合わせて 9.4 コアを使っていました。750 入力の小さなネットワークなのに、です。推論は NumPy で書いていて、行列積を回す BLAS(数値計算ライブラリ)のスレッド数が既定でコア数(24)になっていました。4 ノードで 96 本のスレッドが CPU を取り合っていたのです。

直したのは推論ノードの先頭です(コメントは短く要約しています)。

tiny_lidar_net_controller_node.py
# numpy を import する前でないと効かない。setdefault ではなく必ず代入する
_TH = os.environ.get('E2E_NUM_THREADS') or '1'
for _v in ('OMP_NUM_THREADS', 'OPENBLAS_NUM_THREADS', 'MKL_NUM_THREADS',
           'NUMEXPR_NUM_THREADS', 'VECLIB_MAXIMUM_THREADS'):
    os.environ[_v] = _TH

import numpy as np

スレッド数を1にした前後
初期版4台×3本、減速ガード・壁ガードとも ON。モデルの計算の中身は同じで、スレッド数だけを変えています

CPU の合計は 943% から 99.5% に、load は 84 から 13 に下がり、完走は 8/12 から 12/12 に、車どうしの接触による罰(crash)は 4 件から 0 件になりました。その日に「AWSIM で駄目」と判定していた相手役の中間版を同じ条件(倍率 0.80・ガードなし)で測り直すと、0/4(罰 22 件・191.4 秒)から 3/4(罰 3 件・20.1 秒)に変わりました(どちらも1本)。それまでの判定の多くは、モデルより PC の混み具合に左右されていた可能性があります。

ほかに、手元の AWSIM がシーンを進めないまま止まる回もありました。自作の起動スクリプトはレース開始の合図しか見ておらず、止まったまま時間切れまで待っていました。推論ノードの CPU が 25% 前後から 0.1% に落ちるので、今はそれで見分けています。スレッド数の設定を setdefault にしない理由など、詳しくはスレッド数の記事に書きました。

採否は AWSIM で決める:落ち着いた運用

コース 80 本で学習した版(これがそのまま最終モデルになります)ができたあとも、自作シムでこれを上回る版は作れましたが、AWSIM では勝てませんでした。

自作シムの順位と AWSIM の混走の成績
自作シムで最も良かった「DAgger のコースを増やした版」は、AWSIM の混走で罰が最終モデルの約 2.8 倍(前者は 6 台、最終モデルは採用を決めた時点までの 10 台での比較)。40 本の中間版は学習コースの組がほかの2つと違い、自作シムの検証コースの組も違う可能性があります

AWSIM の混走は、評価したいモデル2台と、遅い相手役の中間版2台を同時に走らせ、6周・制限 400 秒・減速ガード ON・倍率 0.80 で測りました。図の台数は、DAgger のコースを増やした版が3本で6台、最終モデルが5本で10台、40 本の中間版が4本で8台です。

  • 最終モデルの台数:図の 10 台に後で2本(4台)を足した 14 台では、罰 11 件(0.79 件/台、うち接触4件)でした。このうち1周目だけが 104〜108 秒に延びた1本(2台)を除くと、12 台で罰 4 件(0.33 件/台)です。
  • 除いた回とその理由:その1本は、同じ日の夕方に1周目だけが延びる回が続いていたので除きました。当時は、手元で 10 時間以上続けて動かしていた AWSIM の状態によるものと見ていました(原因は確かめていません)。DAgger のコースを増やした版の 6 台も、別の評価コンテナと CPU を取り合った回(6.75 件/台)を除いた値です。どちらも後で書く「測る前に PC を見る」の基準によるもので、走行が最後まで終わらず結果が残らなかった回は、どの数にも入っていません。
  • 言えること:台数が少ないので、差の大きさは目安です。自作シムで上の版が AWSIM で勝てなかっただけでなく、罠2の 73.4% の版のように自作シムで悪かったものが AWSIM で良くなることもありませんでした。自作シムの数字は、どちら向きにも AWSIM の予測に使えませんでした。

最終的に、次のように役割を分けました。

AWSIM で測るときに守ったことをまとめます。

  1. A/B は1変数だけ変え、3 本以上ずつ測ります。単発では壁ガードで罰が 34.7 → 5.3 秒に減ったように見えたものが、3 本ずつでは 221.5 → 169.3 秒でした(どちらも罠5に気づく前、CPU の取り合いが起きていた時期の値です)。
  2. 速さの違う相手を混ぜます。4 台とも同じ重みだと前の車に追いつく場面が起きず、追突に関わる仕組みを評価できません(下の図)。
  3. 制限時間と周回数を添えます。相手役の中間版は制限 300 秒で 9/12 完走(3本。未完走はすべて5周まで到達)、400 秒で 4/4(1本)でした。「完走しない」と「間に合わない」は対策が違います。
  4. 測る前に PC を見ます。load、推論ノードの CPU、別の評価コンテナとの競合(競合した回は 6.75 件/台)、手元の AWSIM を長く動かし続けたときの1周目の延びを確かめ、怪しい回は捨てます。

前方の減速ガードの A/B
速度差のある4台混走×3本(初期版2台+相手役の中間版2台)。ガードの有無で完走 12/12 対 8/12。罠5の「8/12 → 12/12」とは別の実験で、数字が偶然同じです

この方針は、開発に使っていた AI エージェント向けの作業指示ファイルにも「GPUシミュレータの値だけで採用を決めず、最終判断は公式AWSIM条件で行う」という一文で書き込みました。

5つの罠と埋め方(早見表)

罠 症状 気づいたきっかけ 埋め方
舵の倍率 1周 93 秒(0.448) 倍率を AWSIM で振った AWSIM で数点振る。重みを替えたら毎回振る
車両モデル 先生が 4〜5 m/s で頭打ち AWSIM の舵を自作シムに流した 自作シムのタイムを AWSIM の予測に使わない
壁の形 同じ姿勢で平均 1.28 m ずれる 同じ姿勢のスキャンを並べた 乱数のコースで学ばせる。実スキャンで足切り
相手の動き 自作シム 90.2%(45 秒ぶつからない率)→ AWSIM 0/4(1本) AWSIM の車速を数えた 等速に戻す。変えるなら AWSIM で A/B
CPU の取り合い 4 台だと完走しない load 84 に気づいた BLAS を1スレッドに固定。測る前に load を見る

その後

決勝に持ち込んだのは、最終モデルと、減速ガードや壁からの後退などモデルの外の安全装置です。AWSIM では単独6周で罰0・合計 278.9 秒、4台混走6周の1本(相手2台は自チームの中間版、制限 400 秒)で4台とも完走・車どうしの接触0件です。決勝戦は2位(準優勝)でした。一方で、単独の最速ラップ 45.33 秒は本番コースで学習した初期版の 39.77 秒より約 5.6 秒遅く、Pure Pursuit の先生の遅さが残っています。

まとめ

  • 自作シムの数字は自作シムの中の比較にだけ使えます。自作シムで 90.2%(45 秒ぶつからない率)のモデルが AWSIM の1本で 0/4、自作シムで1位の版が最終モデルに負ける、が2日間で起きました。
  • 差の出どころは舵の倍率・車両モデル・壁の形・相手の動き・測る PC の5つ。前の3つはシミュレータの定義の違い、4つ目は現実に寄せたつもりの推測、5つ目はシミュレータの外でした。
  • 埋め方は、定数は AWSIM で振る、乱数のコースで形を覚えさせない、相手は実測を見てから変える、測る前に PC を見る、採否は AWSIM の複数本で決める、です。

来年出る人へ一言

自作シミュレータは本当に役に立ちます。256 環境を同時に走らせられなければ、コースの本数を振る実験も、それを後で測り直して結論を撤回することもできませんでした。ただ、自作シムの成績が上がった日ほど、AWSIM で1回走らせてから喜んでください。私は「自作シムで当時最高」の日に、AWSIM で4台とも完走できませんでした。

参考文献・出典

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?