
このプロジェクトの概要
self-driving-carは、プレーンなJavaScriptで書かれたコンパクトな自動運転シミュレーションです。小規模なニューラルネットワークが進化によって学習し、カーブのある3車線の高速道路を走行し、車線を維持し、交通車両のためにブレーキをかけ、方向指示器を出し、遅い車両を追い越し、適切なタイミングで右側に戻ります。
正しい動作を示す模範ドライバーは存在しません。その代わり、各世代でニューラルネットワークの集団を同じ道路に送り出します。最も遠くまで走行したネットワークほど高い適応度スコアを獲得し、次世代の親になります。
このプロジェクトは、道路、車両物理、認識、交通、ニューラルネットワーク、遺伝的アルゴリズム、プランナー、レンダリング、ユーザーインターフェースを扱う、およそ1,300行のESモジュールで構成されています。実行時の依存関係はなく、同じシミュレーションモジュールがブラウザーとNode.jsの両方で動作します。
ライブデモを開くと、事前学習済みのドライバーを観察したり、ブラウザーで集団を進化させたり、自分で車を操作したりできます。
運転システムの仕組み
シミュレーションは毎秒60ステップの固定レートで進みます。各ステップで車は周囲の車線をスキャンし、観測結果を19個のニューラルネットワーク入力に変換して、ペダル操作と要求車線という2つの出力を生成します。
認識とニューラルネットワーク
このネットワークはカメラ画像の画素や生の距離光線を処理しません。運転プランナーに認識システムが提供する情報に近い、構造化された情報を受け取ります。
- 左、現在、右の各車線が存在するかどうか。
- 該当する各車線で最も近い前方車両と後方車両。
- 各車間距離がどの程度の速さで縮まっているか。
- 学習中の車の現在速度。
- 今後100メートルにある最も急なカーブ。
- 車線変更プランナーの現在の状態。
アーキテクチャは19 · 12 · 8 · 2で、tanh活性化関数を使用し、362個の重みを含みます。一方の出力はペダルを制御します。もう一方は車線を選択し、+0.5を上回る値で左車線を、-0.5を下回る値で右車線を要求します。
プランナーと車両モデル
ニューラルネットワークは移動するタイミングと場所を決めますが、直接ステアリングを操作するわけではありません。手書きのプランナーが車線要求を安全で滑らかな動作に変換します。0.7秒間方向指示器を出し、移動先の車線を確認し、2.6秒間の5次Sカーブに沿って走行し、ピュアパースートステアリングで計画経路を追跡します。
別の車両が自車と並んでいる場合、または自車の死角に入りそうな場合、プランナーは方向指示を続け、車間が開くまで待機します。方向指示中に要求を解除すると、操作はキャンセルされます。
車両自体にはSI単位系の運動学的自転車モデルを使用しています。パラメーターはファミリーハッチバックに近く、全長4.4メートル、0から100 km/hまで9.8秒、最高速度115 km/h、最大制動力8 m/s²です。利用可能なコーナリンググリップによって、カーブを曲がれる速さが制限されます。
交通、適応度、進化
背景交通にはIntelligent Driver Modelを使用しています。車両の希望速度は61~94 km/hで、左側から追い越し、追い越し後は右側に戻り、車線変更前に方向指示器を出し、学習中の車にも反応します。
適応度は主に走行したメートル数です。衝突した場合、または道路を進む遅い列に追いつかれた場合、車は300ポイントを失います。また、右側の車線が明らかに空いているのに左車線に留まった1秒ごとに4メートル分を失います。追い越しに対する直接的な報酬はありません。追い越しが有効になるのは、より長い距離を走行できるようになるためです。
進化には主に4つの手法を使用します。
- エリート保存: コマンドライン学習では、最良の4つのネットワークが変更されずに引き継がれます。
- トーナメント選択: 強いネットワークほど親になる可能性が高くなります。
- BLX-0.25交叉: 親の重みを混ぜ合わせて子を作成します。
- ガウス変異: 変異率と変異量は40世代の半減期で減少します。
インストールとローカルセットアップ
リポジトリをクローンし、ローカル静的サーバーを起動します。
git clone https://github.com/Stxqq/self-driving-car.git
cd self-driving-car
npm run serve
serveコマンドはポート5101でPython HTTPサーバーを起動します。ブラウザーでhttp://localhost:5101を開いてください。
インストールが必要なパッケージはありません。任意の静的ウェブサーバーでプロジェクトをホストできますが、ブラウザーではそのコンテキストでESモジュールが制限されるため、file://でページを直接開くことはできません。
テストとコマンドラインスクリプトにはNode.js 22以降が必要です。次のコマンドでテストスイートを実行します。
npm test
ブラウザーインターフェースの使用
事前学習済みドライバーを見る
視聴モードでは、提供されているネットワークが交通の中を運転する様子を観察できます。ページには入力、出力、計画経路、車線変更状態、車両統計が表示されます。青いリボンはプランナーが生成した経路を表します。
手動で運転する
運転モードでは、同じプランナーを維持したままニューラルネットワークを置き換えます。車両はクルーズコントロールが70 km/hに設定された状態で開始します。
↑を押し続けると設定速度が上がります。↓を押すとブレーキがかかり、設定速度が下がります。←を押すと左への車線変更を要求します。→を押すと右への車線変更を要求します。
ネットワークカードには、事前学習済みの「脳」ならどう操作したかが引き続き表示されるため、自分の判断とその判断を比較するのに役立ちます。
ブラウザーで学習する
Trainモードでは、ランダムなネットワーク重みを持つ60台の車から開始します。1世代の間、すべての車は同じ道路を共有し、交通量は最初の15世代で徐々に増加します。初期ラウンドは45秒から始まります。車が最後まで生き残ると、後続ラウンドの時間は最大5分に達するまで、それまでの半分ずつ増加します。
ブラウザー上の学習では、車線維持や追従行動がすぐに見つかりますが、追い越しははるかに困難です。プロジェクトで説明されているテストでは、ランダムに初期化された集団が第15世代までに行った車線変更は、通常1キロメートルあたりわずか0.1~0.5回でした。元の事前学習済みドライバーでは、車線変更が有益になるまでに、200個の個体を使って約170世代が必要でした。
ランダムな重みから始めるのではなく、同梱された脳の穏やかな変異を進化させたい場合は、From pretrainedを選択してください。ページ上で学習した脳はJSONとしてエクスポートでき、後からImportコントロールで読み込めます。
コマンドラインからの学習
コマンドラインでの学習は、ワーカースレッドを使ってヘッドレスで実行されます。次の例では、120個のネットワークからなる集団を10世代学習させ、60秒のエピソードで、それぞれ異なるシードを持つ3本の道路上で各ネットワークを評価します。
node scripts/train.mjs --generations 10 --population 120 --seeds 3 --seconds 60
デフォルトでは、結果の脳はruns/brain.jsonに書き込まれます。これにより、同梱ネットワークであるsrc/brains/pretrained.jsonとは別に実験を管理できます。
トレーナーは、最良距離と平均距離、生き残った車の数、変異率、経過時間などの統計を報告します。プランナーが車線の中央を維持するため、第0世代でも短いエピソード全体を生き残る車が存在する場合があります。そのため、進歩は単一の最良結果よりも、集団平均の上昇に表れやすい傾向があります。
便利な学習オプション
--densityは交通量を変更します。--resumeは--outで指定したパスの脳から開始し、より穏やかな変異を使用します。--generationsは進化ラウンドの数を制御します。--populationは1世代で評価する脳の数を制御します。--seedsは使用する道路シードの数を制御します。--secondsは各エピソードの時間を設定します。
同梱された脳の学習に使用した手順はscripts/curriculum.shに記録されています。これは、全交通量の中で2分間のエピソードから始め、より多くの道路で5分間のエピソードへと進み、最後に右側通行を維持する微調整フェーズで終了します。プロジェクトのドキュメントによると、このカリキュラムは14コアのノートパソコンで約1時間かかりました。
学習済みの脳を評価する
評価では、学習時に使用していない決定論的な道路上で脳の性能を測定します。道路シードのリストを指定して評価ツールを実行します。
node scripts/evaluate.mjs --seeds 9001,9002,9003,9004,9005
出力には、距離、経過時間、平均速度、車線変更回数、および各実行が時間切れまたは車のクラッシュのどちらで終了したかが表示されます。平均距離や中央値などの集計値も計算されます。
同梱された脳ではなく、エクスポートしたネットワークまたは新たに学習したネットワークを評価するには、--brainでJSONファイルを指定します。
node scripts/evaluate.mjs --brain runs/brain.json --seeds 9001,9002,9003
公式結果は次のコマンドで再生成できます。
npm run evaluate -- --write
このコマンドはscripts/results.jsonを書き込みます。このファイルはブラウザーインターフェースからも読み込まれます。
公開結果を理解する
同梱された362重みのネットワークは、全交通量の中で、学習時に使用していない30個のシードを使って5分間テストされました。平均距離は6.90 km、中央値は7.23 km、平均速度は87 km/hでした。30回の実行のうち28回では、5分経過後も走行が継続しており、車線変更は平均で1キロメートルあたり5.1回でした。
プロジェクトでは、このシステムを14個のレイセンサーから直接操舵していた以前のバージョンと比較しています。旧ネットワークの平均距離は4.05 km、平均速度は56 km/hで、30回の実行のうち20回が5分経過後も継続していました。交通モデルも変更されているため、この比較は管理されたベンチマークではありません。しかし、学習した意思決定と計画された操舵を分離することで、追い越しを発見しやすくなった理由を示しています。
ネットワークはペダルと目標車線を選択し、プランナーは方向指示、車間確認、滑らかな操舵を処理します。
高度なヒント
複数のシードを使って過学習を減らす
ある道路で高い性能を発揮する脳が、別の道路では失敗することがあります。学習シードの数を増やし、検証と最終評価には別のシードを割り当ててください。ヘッドレストレーナーは、交通が学習中の車に反応するため、各脳を道路上で単独に評価します。これにより、ある候補の行動が別の候補のスコアを変えることを防ぎます。
最初からやり直す前に再開する
学習済みの脳がすでに車線を維持し、交通を避けられる場合は、--resumeまたはブラウザーの事前学習済みオプションを使用してください。ランダムな重みから始めると、ゼロからの進化を観察できますが、高度な追い越し行動には、より多くの世代と大規模な集団が必要になる場合があります。
難易度を段階的に上げる
リポジトリのカリキュラム方式に従い、扱いやすいエピソード時間から始めて、時間、道路の多様性、評価の深さを徐々に増やしてください。長いエピソードは堅牢な運転のより確かな証拠になりますが、各世代のコストは高くなります。
決定性を維持する
シミュレーションは固定タイムステップによって完全に決定論的です。同じシードと脳を使えば、同じ軌跡が生成されます。速度制御は物理ステップを変更するのではなく、描画フレームごとにより多くの1/60秒のシミュレーションステップを実行します。これにより、結果を変えずに学習を高速化できます。
プロジェクトの変更箇所を把握する
src/sim/には、DOMに依存しない道路、車両、認識、プランナー、交通、ワールド、ブレイン、進化のコードが含まれています。src/render/には、キャンバスステージ、ネットワーク可視化、構造表示、グラフが含まれています。src/ui/には、監視、訓練、運転の各セッション、ストレージ、キーボード処理が含まれています。src/brains/には、事前訓練済みのネットワークが含まれています。scripts/には、訓練、カリキュラム、評価、結果のファイルが含まれています。test/には、ホールドアウト再生チェックを含むNode.jsテストが含まれています。
まとめ
このプロジェクトは、従来のプランナーが正確なステアリングと車線変更の実行を担うことで、非常に小さなニューラルネットワークでも高度な運転判断を学習できることを示しています。まずブラウザのデモを実行し、手動による判断とニューラルネットワークによる判断を比較してから、Node.jsのスクリプトを使って独自のブレインを訓練・評価してください。依存関係のない実装と決定論的なシミュレーションを備えているため、進化的学習、車両モデル、交通挙動、プランニングを研究するための実用的なコードベースにもなっています。
