神经网络?人工智能?这些词应该都看腻歪了吧?想必还没有人不知道吧?
啥?不知道?
那.....别走😭😭😭😭,听我说!

前言
本篇幅主要是通过web,来做一个模拟的"自动驾驶"。
当然,这个只是提供一个大致的框架流程,逐步实现这么一个"自动驾驶",只是会有一些伪代码。
如果有更细致的讲解,呃,看有多少人需要,如果需要,请留一个支持专栏,到时候我给一个更详细的讲解。
预先准备
1、道路场景准备
"自动驾驶"嘛,首先就是道路的绘制。
道路的绘制,核心点点在于,先设定点,然后由点生成线,然后由线扩充道路的宽度。
具体伪代码如下:
js
// 点的类
export default class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
.....其它绘制代码...
}
// 线的类
export default class Segment {
constructor(startPoint, endPoint) {
this.startPoint = startPoint;
this.endPoint = endPoint;
}
.....其它绘制代码...
}
// 由线,生成多边形类以及优化外角为原型
export default class Polygon {
constructor(segments) {
this.segments = segments;
// 点位循环,加入线条中去
for (let i = 1; i <= points.length; i++) {
this.segments.push(new Segment(points[i - 1], points[i % points.length]));
}
}
}
具体效果如下:

另外,小地图的绘制用于缩小整体的地图展示,有了点和线,基本都可以绘制。
PS:提示下哈,这些代码都可以由代码AI生成!
2、小汽车准备:
有了以上的道路,那么就得有像样的车子在道路上。
车子的功能需要满足这些来模拟真实的车子,比如:
-
支持前、后、前左转弯、前右转弯,后左转弯。后右转弯;
-
模拟摩擦力,向前或者向后的动力,如果向前或者前后的动力不再持续,则速度会降至0;
效果如下(以下由键盘的↑↓←→键控制):

然后,我们再加上与道路交互,当碰撞到道路,速度停止,不再移动;

有一说一,我这种手残的控制,基本都能撞墙。😭😭😭😭😭😭😭😭😭😭😭😭。。
上"动力"
1、汽车上"雷达"
好了,有了上面的基本材料,现在需要给汽车的一个感知能力。这边是在汽车的前方,增加一个所谓雷达感知。
这里的"雷达",是指可检测汽车与周边环境的距离,如下图:

其中黑色为安全距离外,黄色为安全距离内。
现实生活中,雷达当然不是这样的,有兴趣的可以自行搜索下。
扯题外话,现实中,让汽车有感知周围环境的能力,目前有两种方式主流模式:一个是雷达成像,还有一种为摄像头直接探测;
2、上"智能"--神经网络算法
这边暂时跳过项目,先了解下神经网络基础算法,如下图示:

神经网络最基础的算法,就是分输入层=》隐藏层=》输出层。
输入层输入最开始定义的数据,然后每一个点都链接隐藏层。其输出的值,就是∑输入值x每个对应点的权重的总和,如果大于或者小于某一个偏置层,则会给隐藏层设置对应的输入值。
经过上述所有层级,最后会有输出数据。
因为我们自动驾驶需要前、后、左、右的方向控制,这里设置的输出就会是四个值。
不懂没有关系,直接上源码。
js
import { lerp } from '../utils/index.js';
export class NeuralNetwork {
constructor(neuronCounts) {
this.levels = [];
// 获取层级
for (let i = 0; i < neuronCounts.length - 1; i++) {
this.levels.push(new Level(neuronCounts[i], neuronCounts[i + 1]));
}
}
// 返回层级输出
static feedForward(givenInputs, network) {
let outputs = Level.feedForward(givenInputs, network.levels[0]);
for (let i = 1; i < network.levels.length; i++) {
outputs = Level.feedForward(outputs, network.levels[i]);
}
return outputs;
}
// 从最佳策略中,变更值
static mutate(network, amount = 1) {
network.levels.forEach((level) => {
for (let i = 0; i < level.biases.length; i++) {
level.biases[i] = lerp(level.biases[i], Math.random() * 2 - 1, amount);
}
for (let i = 0; i < level.weights.length; i++) {
for (let j = 0; j < level.weights[i].length; j++) {
level.weights[i][j] = lerp(
level.weights[i][j],
Math.random() * 2 - 1,
amount,
);
}
}
});
}
}
export class Level {
constructor(inputCount, outputConut) {
this.inputs = new Array(inputCount);
this.outputs = new Array(outputConut);
// 偏差值,每个输出神经元都有一个偏差值
// 高于该值它将触发这个输出
this.biases = new Array(outputConut);
// 权重值
// 每一条输入,都对应很多条输出,每个输出都有对应的权重值
this.weights = [];
for (let i = 0; i < inputCount; i++) {
this.weights[i] = new Array(outputConut);
}
Level.#randomize(this);
}
// 随机数据
static #randomize(level) {
for (let i = 0; i < level.inputs.length; i++) {
for (let j = 0; j < level.outputs.length; j++) {
level.weights[i][j] = Math.random() * 2 - 1;
}
}
for (let i = 0; i < level.biases.length; i++) {
level.biases[i] = Math.random() * 2 - 1;
}
}
// 反馈
static feedForward(givenInputs, level) {
// console.log(level)
for (let i = 0; i < level.inputs.length; i++) {
level.inputs[i] = givenInputs[i];
}
for (let i = 0; i < level.outputs.length; i++) {
let sum = 0;
// 计算每一条线的积分,输入加权重
for (let j = 0; j < level.inputs.length; j++) {
sum += level.inputs[j] * level.weights[j][i];
}
// 判断,如果大于偏差值,则输出结果
if (sum > level.biases[i]) {
level.outputs[i] = 1;
} else {
level.outputs[i] = 0;
}
// level.outputs[i] = Math.tanh(sum + level.biases[i]);
}
return level.outputs;
}
}
3、动起来
好了,算法也有了,细心的朋友看上面的代码,一定知道。上面给与各个权重都是有一个初始的随机值的。
这个,就是为了方便给与每项的权重和偏置值都有一个初始种子,因为我们需要这个随机的值去驱动我们的车。
我随机生成了一个,如下:

很明显。这随机碰运气的话,得到的模型数据并不好。
那么如何将这个汽车训练的越来越好呢?
择优-遗传算法
不知道前端佬有没有看过火影忍者,里面的主人公会一种影分身。而这个影分身用在学习上,非常的变态。假如你有1000个影分身,同时去学习一个东西,而要其中有一个会了,那么基本主人公自己就会了。
基于这种方法,我们可以将汽车随机生成1000个,那基本就有1000个模型。我们让这1000个同时跑起来,选择其中一个最好的就行了。如下图(时间会有点长):

结果
最终,我大致搞了这么一个模型,效果如下:

如果有时间的话,还可以加入各种策略,比如红绿灯,人行道,或者其他车辆。感兴趣的可以自己动手试试!
前端佬们,机器学习之路,还是不错的吧?
哈哈~