주요 콘텐츠로 건너뛰기
AI 튜토리얼

JavaScript로 진화형 자율주행 자동차를 만들고 학습시키며 평가하기

종속성 없는 JavaScript 프로젝트를 실행하고, 신경망과 주행 플래너를 살펴보며, 브라우저나 Node.js에서 새로운 두뇌를 학습시키고, 아직 보지 못한 도로에서 성능을 평가하는 방법을 알아보세요.

JavaScript로 진화형 자율주행 자동차를 만들고 학습시키며 평가하기

이 프로젝트가 하는 일

self-driving-car는 순수 JavaScript로 작성된 소형 자율주행 시뮬레이션입니다. 작은 신경망이 진화를 통해 학습하며, 굽이치는 3차선 고속도로에서 차선을 유지하고, 교통 상황에 맞춰 제동하고, 방향지시등을 켜고, 느린 차량을 추월한 뒤 적절한 경우 오른쪽으로 돌아옵니다.

올바른 주행을 보여 주는 예제 운전자는 없습니다. 대신 각 세대마다 신경망 개체군을 같은 도로에 투입합니다. 가장 멀리 주행한 네트워크가 더 높은 적합도 점수를 받고 다음 세대의 부모가 됩니다.

이 프로젝트는 도로, 차량 물리, 인지, 교통, 신경망, 유전 알고리즘, 플래너, 렌더링, 사용자 인터페이스를 다루는 약 1,300줄의 ES 모듈로 구성되어 있습니다. 런타임 의존성은 없으며, 동일한 시뮬레이션 모듈이 브라우저와 Node.js에서 모두 실행됩니다.

라이브 데모를 열어 사전 학습된 운전자를 지켜보거나, 브라우저에서 개체군을 진화시키거나, 직접 차량을 조작할 수 있습니다.

주행 시스템의 작동 방식

시뮬레이션은 초당 60회의 고정된 속도로 진행됩니다. 매 단계마다 차량은 주변 차선을 탐색하고, 관측 결과를 19개의 신경망 입력으로 변환한 다음 두 가지 출력을 생성합니다. 하나는 페달 명령이고, 다른 하나는 요청 차선입니다.

인지와 신경망

이 네트워크는 카메라 픽셀이나 원시 거리 레이를 처리하지 않습니다. 주행 플래너에 인지 스택이 제공할 수 있는 정보와 유사한 구조화된 정보를 입력으로 받습니다.

  • 왼쪽, 현재, 오른쪽 차선의 존재 여부
  • 각 관련 차선에서 앞뒤로 가장 가까운 차량
  • 각 간격이 좁혀지는 속도
  • 학습 차량의 현재 속도
  • 앞으로 100미터 구간에서 가장 급격한 커브
  • 차선 변경 플래너의 현재 상태

아키텍처는 19 · 12 · 8 · 2이며 tanh 활성화 함수를 사용하고 362개의 가중치를 포함합니다. 한 출력은 페달을 제어합니다. 다른 출력은 차선을 선택합니다. +0.5보다 큰 값은 왼쪽 차선을, -0.5보다 작은 값은 오른쪽 차선을 요청합니다.

플래너와 차량 모델

신경망은 언제, 어디로 이동할지를 결정하지만 직접 조향하지는 않습니다. 직접 작성한 플래너가 차선 요청을 안전하고 부드러운 조작으로 변환합니다. 플래너는 0.7초 동안 방향지시등을 켜고, 목표 차선을 확인한 뒤, 2.6초 동안 5차 다항식 S자 곡선을 따라 이동하며, 퓨어 퍼슈트 조향으로 계획된 경로를 추적합니다.

다른 차량이 차량과 나란히 있거나 사각지대에 진입하려는 경우, 플래너는 계속 방향지시등을 켜고 간격이 벌어질 때까지 기다립니다. 신호를 보내는 단계에서 요청을 해제하면 조작이 취소됩니다.

차량 자체는 SI 단위의 운동학적 자전거 모델을 사용합니다. 매개변수는 소형 해치백을 참고했습니다. 차체 길이는 4.4미터, 0~100km/h 가속 시간은 9.8초, 최고 속도는 115km/h이며 최대 8m/s²로 제동합니다. 사용 가능한 코너링 접지력이 커브를 통과할 수 있는 속도를 제한합니다.

교통, 적합도, 진화

배경 교통은 지능형 운전자 모델을 따릅니다. 차량의 목표 속도는 61~94km/h이며, 왼쪽으로 추월하고, 추월 후 오른쪽으로 돌아오며, 차선 변경 전에 방향지시등을 켜고, 학습 차량에도 반응합니다.

적합도는 주로 주행한 미터 수로 결정됩니다. 충돌하거나 도로를 따라 올라오는 느린 차량 행렬에 붙잡히면 300점을 잃습니다. 또한 오른쪽 차선이 명확히 비어 있는데 왼쪽 차선에 머문 매 1초마다 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

브라우저 인터페이스 사용

사전 학습된 운전자 보기

보기 환경에서 제공된 네트워크가 교통 속에서 주행하는 모습을 관찰할 수 있습니다. 페이지에는 네트워크의 입력, 출력, 계획된 경로, 차선 변경 상태, 차량 통계가 표시됩니다. 파란 리본은 플래너가 생성한 경로를 나타냅니다.

수동으로 운전하기

Drive 모드에서는 동일한 플래너를 유지한 채 신경망을 직접 조작합니다. 차량은 정속 주행 속도 70km/h로 시작합니다.

  • ↑를 누르고 있으면 설정 속도가 올라갑니다.
  • ↓를 누르면 제동하고 설정 속도가 내려갑니다.
  • ←를 누르면 왼쪽 차선 변경을 요청합니다.
  • →를 누르면 오른쪽 차선 변경을 요청합니다.

네트워크 카드는 사전 학습된 두뇌가 어떤 결정을 내렸을지도 계속 보여 줍니다. 따라서 이 모드는 자신의 판단과 네트워크의 판단을 비교하는 데 유용합니다.

브라우저에서 학습하기

학습 모드는 무작위 네트워크 가중치로 차량 60대를 시작합니다. 한 세대 동안 모든 차량은 같은 도로를 공유하며, 교통량은 처음 15세대에 걸쳐 점진적으로 나타납니다. 초기 라운드는 45초에 시작합니다. 차량이 끝까지 생존하면 이후 라운드의 시간이 절반씩 늘어나 최대 5분에 도달합니다.

브라우저 학습은 차선 유지와 앞차 추종 행동을 빠르게 발견하지만, 추월은 훨씬 어렵습니다. 프로젝트에서 설명한 테스트에 따르면 무작위로 초기화된 개체군은 일반적으로 15세대까지 킬로미터당 0.1~0.5회의 차선 변경만 수행했습니다. 기존의 사전 학습 운전자는 차선 변경이 유익해지기까지 200개 개체군으로 약 170세대가 필요했습니다.

무작위 가중치로 시작하는 대신 제공된 두뇌의 완만한 변이를 진화시키려면 사전 학습 모델에서를 선택하세요. 페이지에서 학습한 두뇌는 JSON으로 내보낸 후 가져오기 컨트롤로 나중에 불러올 수 있습니다.

명령줄에서 학습하기

명령줄 학습은 작업자 스레드에서 헤드리스 방식으로 실행됩니다. 다음 예시는 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은 세대마다 평가할 두뇌 수를 제어합니다.
  • --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.90km, 중앙값은 7.23km, 평균 속도는 87km/h였습니다. 30회 실행 중 28회는 5분 후에도 계속 진행 중이었으며, 차량은 킬로미터당 평균 5.1회의 차선 변경을 수행했습니다.

프로젝트는 이 시스템을 14개의 레이 센서에서 직접 조향하던 이전 버전과 비교합니다. 이전 네트워크는 평균 4.05km와 56km/h를 기록했고, 30회 실행 중 20회가 5분 후에도 계속 진행 중이었습니다. 교통 모델도 변경되었으므로 이 비교는 통제된 벤치마크가 아니지만, 학습된 의사 결정과 계획된 조향을 분리했을 때 추월을 더 쉽게 발견할 수 있었던 이유를 보여줍니다.

네트워크는 페달과 목표 차선을 선택하고, 플래너는 방향 지시, 간격 확인, 부드러운 조향을 처리합니다.

고급 팁

과적합을 줄이려면 여러 시드 사용하기

한 도로에서 잘 작동하는 두뇌가 다른 도로에서는 실패할 수 있습니다. 학습 시드 수를 늘리고 검증 및 최종 평가를 위해 별도의 시드를 확보하세요. 헤드리스 학습기는 교통이 학습 차량에 반응하기 때문에 각 두뇌를 해당 도로에서 단독으로 평가합니다. 이를 통해 한 후보의 행동이 다른 후보의 점수를 바꾸는 일을 방지합니다.

처음부터 다시 시작하기 전에 이어서 학습하기

학습된 두뇌가 이미 차선을 유지하고 교통을 피한다면 --resume 또는 브라우저의 사전 학습 옵션을 사용하세요. 무작위 가중치로 시작하면 처음부터 진화하는 과정을 관찰할 수 있지만, 정교한 추월 행동에는 훨씬 더 많은 세대와 더 큰 개체군이 필요할 수 있습니다.

난이도를 점진적으로 높이기

저장소의 커리큘럼 접근 방식을 따르세요. 관리하기 쉬운 에피소드 길이로 시작한 다음 시간, 도로의 다양성, 평가 깊이를 점차 늘립니다. 긴 에피소드는 견고한 주행을 더 잘 입증하지만 각 세대의 비용을 높입니다.

결정론 유지하기

시뮬레이션은 고정된 시간 간격에 이르기까지 결정론적입니다. 동일한 시드와 두뇌는 동일한 궤적을 생성합니다. 속도 조절은 물리 시간 간격을 변경하는 대신 렌더링 프레임마다 더 많은 1/60초 시뮬레이션 단계를 실행합니다. 따라서 결과를 변경하지 않고 학습을 가속할 수 있습니다.

프로젝트에서 수정할 위치 파악하기

  • src/sim/에는 DOM 종속성 없이 도로, 자동차, 인지, 플래너, 교통, 월드, 브레인, 진화 코드가 들어 있습니다.
  • src/render/에는 캔버스 스테이지, 네트워크 시각화, 해부학적 표시, 차트가 들어 있습니다.
  • src/ui/에는 관찰, 학습, 주행 세션과 저장소, 키보드 처리가 들어 있습니다.
  • src/brains/에는 사전 학습된 네트워크가 들어 있습니다.
  • scripts/에는 학습, 커리큘럼, 평가, 결과 파일이 들어 있습니다.
  • test/에는 홀드아웃 리플레이 검사를 포함한 Node.js 테스트가 들어 있습니다.

결론

이 프로젝트는 정밀한 조향과 차선 변경 실행을 기존 플래너가 처리할 때, 매우 작은 신경망이 어떻게 높은 수준의 주행 결정을 학습할 수 있는지 보여 줍니다. 먼저 브라우저 데모를 실행하고 수동 결정과 신경망 결정을 비교한 다음, Node.js 스크립트를 사용해 직접 브레인을 학습하고 평가해 보세요. 종속성이 없고 결정론적으로 동작하는 시뮬레이션 덕분에 진화 학습, 차량 모델, 교통 행동, 계획을 연구하기 위한 실용적인 코드베이스로도 활용할 수 있습니다.