
本项目的功能
self-driving-car 是一个使用原生 JavaScript 编写的紧凑型自动驾驶模拟器。一个小型神经网络通过进化学习,在弯曲的三车道高速公路上行驶,保持车道、为交通减速、打转向灯、超越较慢车辆,并在适当时返回右侧车道。
没有示例驾驶员展示正确行为。相反,每一代都会将一组神经网络送上同一条道路。行驶距离最远的网络会获得更高的适应度分数,并成为下一代的父代。
项目包含约 1,300 行 ES 模块代码,涵盖道路、车辆物理、感知、交通、神经网络、遗传算法、规划器、渲染和用户界面。项目没有运行时依赖,同一组模拟模块可以在浏览器和 Node.js 中运行。
你可以打开在线演示,观看预训练驾驶员、在浏览器中进化种群,或亲自控制车辆。
驾驶系统的工作方式
模拟器以每秒 60 步的固定速率运行。在每一步中,车辆都会扫描附近车道,将观测结果转换为 19 个神经网络输入,并生成两个输出:踏板指令和请求车道。
感知与神经网络
网络不会处理摄像头像素或原始距离射线,而是接收类似感知系统提供给驾驶规划器的结构化信息:
- 左侧、当前和右侧车道是否存在。
- 每条相关车道中前方和后方最近的车辆。
- 每个间距缩小的速度。
- 学习车辆的当前速度。
- 未来 100 米内最急的弯道。
- 车道变更规划器的当前状态。
网络架构为 19 · 12 · 8 · 2,使用 tanh 激活函数,包含 362 个权重。一个输出控制踏板,另一个选择车道:高于 +0.5 的值请求向左侧车道变更,低于 -0.5 的值请求向右侧车道变更。
规划器与车辆模型
神经网络决定何时以及向哪里移动,但不会直接控制转向。手写规划器会将车道请求转换为安全、平顺的操作。车辆会打转向灯 0.7 秒,检查目标车道,沿五次 S 曲线行驶 2.6 秒,并使用纯追踪转向来跟随规划路径。
如果另一辆车与本车并行,或即将进入本车盲区,规划器会继续打转向灯并等待间距扩大。在打灯阶段释放请求会取消该操作。
车辆本身使用国际单位制下的运动学自行车模型。其参数接近家用两厢车:车长 4.4 米,0 到 100 km/h 加速时间为 9.8 秒,最高速度为 115 km/h,最大制动能力为 8 m/s²。可用的弯道抓地力会限制车辆通过弯道的速度。
交通、适应度与进化
背景交通遵循智能驾驶员模型。车辆的期望速度在 61 至 94 km/h 之间,会从左侧超车,超车后返回右侧,变道前打转向灯,并对学习车辆做出反应。
适应度主要取决于行驶的米数。如果车辆发生碰撞,或被沿道路追赶而来的慢车队赶上,则会扣除 300 分。如果车辆处于左侧车道且右侧车道明显空闲,每停留一秒还会扣除四米。超车没有直接奖励;超车之所以有用,是因为它能让车辆行驶更远。
进化使用四项主要技术:
- 精英保留:在命令行训练中,最优秀的四个网络会原样保留。
- 锦标赛选择:更强的网络更有可能成为父代。
- 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。
无需安装任何软件包。项目可以由任意静态 Web 服务器托管,但直接通过 file:// 打开页面将无法运行,因为浏览器会限制该环境中的 ES 模块。
测试和命令行脚本需要 Node.js 22 或更高版本。使用以下命令运行测试套件:
npm test
使用浏览器界面
观看预训练驾驶员
使用观看模式,观察随附的网络在交通中驾驶。页面会显示其输入、输出、规划路线、车道变更状态和车辆统计数据。蓝色带状线表示规划器生成的路径。
手动驾驶
在驾驶模式中,你可以替代神经网络,同时保留相同的规划器。车辆开始时的巡航控制设定为 70 km/h。
- 按住
↑提高设定速度。 - 按下
↓制动并降低设定速度。 - 按下
←请求向左变道。 - 按下
→请求向右变道。
网络卡片会继续显示预训练网络原本会采取的操作,因此这种模式适合将你的决策与它的决策进行比较。
在浏览器中训练
训练模式会用随机网络权重启动 60 辆汽车。在一个世代内,所有汽车共用同一条道路,交通会在前 15 个世代逐渐增加。早期轮次从 45 秒开始。当汽车坚持到终点时,后续轮次的时长会增加一半,直到达到五分钟的最长时长。
浏览器训练很快就能发现保持车道和跟车行为,但超车要困难得多。项目测试表明,随机初始化的种群通常在第 15 代之前每公里只能完成 0.1 到 0.5 次变道。原始预训练驾驶器需要约 170 个世代、每代 200 个个体,变道才开始产生收益。
如果你想基于随附大脑进行温和变异,而不是从随机权重开始,请选择基于预训练。在页面中训练的大脑可以导出为 JSON,之后再通过“导入”控件加载。
从命令行训练
命令行训练会以无头模式跨工作线程运行。以下示例使用 120 个网络的种群训练 10 个世代,并在三条带种子的道路上评估每个网络,每次运行 60 秒:
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 中。流程从全交通环境下的两分钟运行开始,逐步扩展到更多道路上的五分钟运行,最后以靠右行驶微调阶段结束。根据项目文档,该课程在一台 14 核笔记本电脑上耗时约一小时。
评估训练后的大脑
评估会在训练期间未使用过的确定性道路上测试大脑。使用道路种子列表运行评估器:
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 个留出种子进行了五分钟测试。它达到了 6.90 km 的平均距离、7.23 km 的中位距离和 87 km/h 的平均速度。30 次运行中有 28 次在五分钟后仍在进行,汽车平均每公里变道 5.1 次。
项目将该系统与早期直接根据 14 个射线传感器进行转向的版本进行了比较。旧网络的平均距离为 4.05 km,平均速度为 56 km/h,30 次运行中有 20 次在五分钟后仍在进行。由于交通模型也发生了变化,这并不是受控基准测试,但它说明了为什么将学习到的决策与规划转向分离后,更容易发现超车行为。
网络选择踏板和目标车道;规划器负责信号灯、间距检查和平顺转向。
高级技巧
使用多个种子减少过拟合
在一条道路上表现良好的大脑,可能会在另一条道路上失败。增加训练种子的数量,并为验证和最终评估预留单独的种子。无头训练器会让每个大脑单独在其道路上评估,因为交通会对学习中的汽车作出反应;这样可以防止某个候选者的行为改变另一个候选者的得分。
先恢复训练,再从头开始
如果训练后的大脑已经能够保持车道并避开交通,请使用 --resume 或浏览器中的预训练选项。从随机权重开始适合观察从零开始的进化过程,但复杂的超车行为可能需要更多世代和更大的种群。
逐步提高难度
遵循代码库中的课程方法:从易于管理的运行时长开始,然后逐步增加时长、道路多样性和评估深度。更长的运行能更充分地证明驾驶的稳健性,但会让每个世代的成本更高。
保持确定性
该模拟精确到固定时间步长,具有相同种子和大脑的运行会产生相同的轨迹。速度控制会让每个渲染帧运行更多个 1/60 秒的模拟步长,而不是改变物理步长。这样就能加速训练,同时不改变结果。
了解项目中的修改位置
src/sim/包含道路、车辆、感知、规划器、交通、世界、神经网络和进化代码,且不依赖 DOM。src/render/包含画布舞台、网络可视化、解剖结构显示和图表。src/ui/包含观察、训练和驾驶会话、存储以及键盘处理。src/brains/包含预训练网络。scripts/包含训练、课程、评估和结果文件。test/包含 Node.js 测试,包括留出回放检查。
结论
本项目展示了一个非常小的神经网络如何学习高层驾驶决策,同时由传统规划器处理精确转向和变道执行。请先运行浏览器演示,比较手动决策与神经网络决策,然后使用 Node.js 脚本训练和评估你自己的神经网络。其无依赖实现和确定性模拟也使其成为研究进化学习、车辆模型、交通行为和规划的实用代码库。
