Вы когда-нибудь мечтали о беспилотном автомобиле? Теперь это уже не причудливая фантазия. Представьте, что едете по дороге, не держа руки на руле, а строчки кода ведут вас по дороге. Звучит безумно, но еще безумнее то, что этот код заставляет автомобиль не только двигаться, но и уклоняться от препятствий. А если ситуация становится сложной, машина сама нажимает на тормоза.
Присоединяйтесь ко мне, чтобы разгадать магию, с помощью которой простые строки кода превращают автомобиль в умное, самоуправляемое чудо.
JavaScript как проводник
В мире автопилотируемых автомобилей JavaScript играет важнейшую роль проводника, указывающего машине, как двигаться и какие решения принимать. Представьте, что за рулем электронный мозг, использующий простые инструкции для управления, ускорения и принятия решений. Подобно тому, как мы следуем карте во время движения, JavaScript направляет машину по дорогам в потоках других машин, превращая код в действия.
Как проводник, JavaScript взаимодействует с электронным блоком управления (ЭБУ) автомобиля, отвечающим за приводную систему, рулевые механизмы и педали ускорения. С помощью тщательно продуманных алгоритмов он преобразует абстрактные инструкции в реальное движение, позволяя автомобилю автономно перемещаться по дороге. По сути, JavaScript наделяет автомобиль интеллектом для принятия решений в доли секунды, превращая строки кода в динамичное управление автомобилем.
Датчики и камеры
Чтобы самоуправляемый автомобиль “видел” дорогу, ему необходимо использовать сложный набор датчиков и камер, выполняющих роль глаз и ушей. Камеры фиксируют визуальные данные, а датчики, такие как лидар и радар, обеспечивают 360-градусное восприятие окружающей обстановки. Такое сочетание визуального и пространственного восприятия создает комплексное представление об окружении, позволяя автомобилю воспринимать дорожную разметку, определять препятствия и предвидеть потенциальные опасности.
Данные, собранные этими датчиками, поступают в систему, создавая карту окружения автомобиля в реальном времени. Эта карта становится основой для принятия решений, позволяя автомобилю точно ориентироваться в сложных сценариях. Благодаря сенсорным данным, самоуправляемый автомобиль воспроизводит, а в некоторых случаях и превосходит человеческое восприятие, обеспечивая повышенный уровень осведомленности на дороге.
Если посмотреть на синий автомобиль на рисунке, можно заметить пять желтых линий, отходящих от него. Они представляют собой направления, в которых сфокусированы камеры автомобиля. В реальных условиях у автомобилей, оснащенных камерами, также есть эти абстрактные желтые линии, простирающиеся во всех направлениях. Внимательно посмотрите на желтые линии в крайнем левом и крайнем правом углу. Вы заметите, что часть линии окрашена в черный цвет. Эта особенность является важнейшим средством коммуникации для автомобиля. По сути, это предупреждение о том, что по желтой линии ехать безопасно, но за ней находится потенциальное столкновение или препятствие.
Проще говоря, желтые линии действуют как глаза автомобиля, подсказывая ему, где можно безопасно двигаться, а где следует объехать, чтобы избежать нежелательных столкновений. Это своего рода защитный экран, позволяющий автомобилю оставаться в безопасных границах и избегать потенциальных опасностей.
Перейдем к коду
Кода очень много, поэтому рассмотрим только самое важное.
class Car{
constructor(x,y,width,height,controlType,maxSpeed=3){
this.x=x;
this.y=y;
this.width=width;
this.height=height;
this.speed=0;
this.acceleration=0.2;
this.maxSpeed=maxSpeed;
this.friction=0.05;
this.angle=0;
this.damaged=false;
this.useBrain=controlType=="AI";
if(controlType!="DUMMY"){
this.sensor=new Sensor();
this.brain=new NeuralNetwork(
[this.sensor.rayCount,4]
);
}
this.controls=new Controls(controlType);
}
update(roadBorders,traffic){
if(!this.damaged){
this.#move();
this.polygon=this.#createPolygon();
this.damaged=this.#assessDamage(roadBorders,traffic);
}
if(this.sensor){
this.sensor.update(this.x,this.y,this.angle,roadBorders,traffic);
const offsets=this.sensor.readings.map(
s=>s==null?0:1-s.offset
);
const outputs=NeuralNetwork.feedForward(offsets,this.brain);
if(this.useBrain){
this.controls.forward=outputs[0];
this.controls.left=outputs[1];
this.controls.right=outputs[2];
this.controls.reverse=outputs[3];
}
}
}
#assessDamage(roadBorders,traffic){
for(let i=0;i<roadBorders.length;i++){
if(polysIntersect(
[...this.polygon,this.polygon[0]],
roadBorders[i])
){
return true;
}
}
for(let i=0;i<traffic.length;i++){
const poly=traffic[i].polygon;
if(polysIntersect(
[...this.polygon,this.polygon[0]],
[...poly,poly[0]])
){
return true;
}
}
return false;
}
#createPolygon(){
const points=[];
const rad=Math.hypot(this.width,this.height)/2;
const alpha=Math.atan2(this.width,this.height);
points.push({
x:this.x-Math.sin(this.angle-alpha)*rad,
y:this.y-Math.cos(this.angle-alpha)*rad
});
points.push({
x:this.x-Math.sin(this.angle+alpha)*rad,
y:this.y-Math.cos(this.angle+alpha)*rad
});
points.push({
x:this.x-Math.sin(Math.PI+this.angle-alpha)*rad,
y:this.y-Math.cos(Math.PI+this.angle-alpha)*rad
});
points.push({
x:this.x-Math.sin(Math.PI+this.angle+alpha)*rad,
y:this.y-Math.cos(Math.PI+this.angle+alpha)*rad
});
return points;
}
#move(){
if(this.controls.forward){
this.speed+=this.acceleration;
}
if(this.controls.reverse){
this.speed-=this.acceleration;
}
if(this.speed!=0){
const flip=this.speed>0?1:-1;
if(this.controls.left){
this.angle+=0.03*flip;
}
if(this.controls.right){
this.angle-=0.03*flip;
}
}
if(this.speed>this.maxSpeed){
this.speed=this.maxSpeed;
}
if(this.speed<-this.maxSpeed/2){
this.speed=-this.maxSpeed/2;
}
if(this.speed>0){
this.speed-=this.friction;
}
if(this.speed<0){
this.speed+=this.friction;
}
if(Math.abs(this.speed)<this.friction){
this.speed=0;
}
this.x-=Math.sin(this.angle)*this.speed;
this.y-=Math.cos(this.angle)*this.speed;
}
draw(ctx,drawSensor=false){
if(this.damaged){
ctx.fillStyle="gray";
}else{
ctx.fillStyle="black";
}
ctx.beginPath();
ctx.moveTo(this.polygon[0].x,this.polygon[0].y);
for(let i=1;i<this.polygon.length;i++){
ctx.lineTo(this.polygon[i].x,this.polygon[i].y);
}
ctx.fill();
if(this.sensor && drawSensor){
this.sensor.draw(ctx);
}
}
}
Конструктор (Constructor):
- Класс
Car
— в данном проекте как бы макет для создания автономного автомобиля. - Он принимает такие начальные параметры, как положение (
x
,y
), размеры (width
,height
), тип управления (human/AI/dummy — человек/ИИ/манекен) и максимальная скорость (maxSpeed).
Инициализация:
- Инициализируются такие свойства автомобиля, как скорость (speed), ускорение (acceleration), трение (friction) и угол наклона (
angle
). damaged
отслеживает, нет ли повреждений, не столкнулся ли автомобиль с чем-либо.useBrain
— флаг, указывающий, использует ли автомобиль искусственный интеллект для управления.
Настройка датчиков и нейронной сети:
- Если автомобиль не управляется манекеном, то в нем устанавливаются датчик (sensor) и нейронная сеть (neural network).
- Датчик имитирует способность автомобиля “видеть” дорогу, а нейронная сеть выполняет роль мозга автомобиля, принимающего решения.
Управление (Controls):
- Инициализируются элементы управления в зависимости от типа элемента управления (keyboard/AI/dummy — клавиатура/ИИ/манекен).
Метод update:
- Метод
update
— это как бы мозг и мышцы автомобиля вместе взятые. - Он перемещает автомобиль, обновляет его многоугольную форму и проверяет наличие повреждений.
- Если есть датчик, он обновляет показания датчика и обрабатывает их через нейронную сеть.
Оценка повреждений (Access Damage):
- При сравнении многоугольников проверяется, не столкнулся ли автомобиль с краем дороги или другим транспортом.
Создание многоугольника (Create Polygon):
- Вычисляются четыре точки многоугольника, представляющего положение и форму автомобиля.
Метод move:
- Управляет движением автомобиля в зависимости от типа управления, скорости и трения.
- Соответствующим образом регулирует положение и угол наклона автомобиля.
Метод draw:
- Рисует автомобиль на холсте.
- Если есть датчик, который нужно визуализировать, также рисует датчик.
Приведенный выше код написан только для Car.Js (проекта беспилотного автомобиля, управляемого с помощью JavaScript). Однако на Github можно найти много других файлов с еще большим количеством кода.
Обучение
Машина собирает данные каждый раз при запуске кода. Иногда выдаются хорошие результаты, но чаще всего — плохие. Под хорошими результатами подразумевается то, что машина идет по траектории без аварий, а плохие — наоборот. Машина часто врезается, потому что еще учится, но со временем она будет врезаться все реже и реже. Именно так все происходит и в реальной жизни. Самоуправляемые автомобили постоянно тестируются, и машина постоянно получает данные. Чем больше она ездит, тем лучше ее результаты. С каждым разом она становится все лучше и лучше.
При использовании технологии Fleet Learning (обучение автопарка) автомобили работают как единый парк, обмениваясь обезличенными данными и знаниями. Обучение автопарка позволяет каждому автомобилю использовать коллективный опыт всей сети, создавая более интеллектуальную и адаптивную систему. Эта система будет развиваться и принимать еще более гибкие решения, адаптируясь не только к текущей обстановке, но и учитывая долгосрочную перспективу. Под такой перспективой подразумевается стратегическое планирование маршрутов для повышения эффективности и рационального использования энергии.
Нейронная сеть
Представьте нейронную сеть (НС) как цифровой мозг, вдохновленный тем, как работает человеческий мозг. Работает НС как команда взаимосвязанных сотрудников, каждый из которых отвечает за обработку своей информации. Эти работники, или узлы, общаются между собой посредством взвешенных связей, подобно членам команды, обменивающимся информацией.
В процессе обучения НС учится на опыте, подстраивая весовые коэффициенты, чтобы лучше справляться с задачами. В проекте беспилотного автомобиля НС — это мозг, принимающий решения на основе того, что он “видит” с помощью датчиков. Дисплей справа — это как бы окно в этот цифровой мозг, и пока автомобиль движется, НС реагирует на это движение, показывая, как она думает и адаптируется к различным ситуациям, подобно тому, как мы учимся у окружающих.
Читайте также:
- Нужно ли оптимизировать программный код для ИИ: аргументы за и против
- Создаем ИИ с помощью OpenAI
- Движки JavaScript. Часть 2: генерация кода и базовые оптимизации
Читайте нас в Telegram, VK и Дзен
Перевод статьи Krish Bhoopati: Coding the Road Ahead: Self-Driving Cars with JavaScript and AI