Немного слов обо мне: мое хобби это робототехника. На данный момент экспериментирую с шагающим роботом на базе SunFounder PiCrawler.
Последнее время тема искусственного интеллекта (ИИ) приобретает все большую популярность. Причиной этому служит в том числе совершенствование мобильных устройств и компьютеров - они становятся мощнее и компактнее.
В данной статье я постараюсь простыми словами объяснить, как можно применить ИИ для управления роботом, используя готовую библиотеку TensorFlow.

Что такое ИИ ?
Краткими словами - это инструмент для обработки данных, построенный на базе нейросети. Для создания собственного ИИ мы решаем, какие входные параметры обрабатываем и какие ожидаем на выходе. Потом собираем огромный объем этих данных, будь то изображения или что-то другое и "тренируем" нейросеть на их основе. В итоге получаем модель, которая будет обрабатывать наши значения.
Немного о роботе PiCrawler
Это девайс, приобретенный на AliExpress. Для его управления необходим мини компьютер Raspberry Pi 4. Робот имеет четыре ноги, плату расширения, подключаемую по i2c интерфейсу, камеру, ультразвуковой датчик и разные выходы.
Я подключил к этому роботу мини-гироскоп WT901 (можно любой другой), также приобретенный на китайском маркетплейсе и подключаемый по интерфейсу i2c. Имеется ПО для управления ногами.
Постановка задачи
В качестве примера я возьму следующую задачу управления роботом - придерживаться горизонтального положения в зависимости от угла наклона опорной поверхности.
Так как внутри робота имеется гироскоп, с него мы и будем считывать углы наклона x, y и уже на основе этих данных определять положение ног y1, y2, y3, y4,
где x, y, y1, y2, y3, y4 - числа.
API модели в качестве входных переменных использует Tensor-ы - массивы с числовыми значениями. На выходе аналогичная ситуация.
В нашем случае формат входных данных будет [x, y], а выходных [y1, y2, y3, y4]
В виде картинки эту модель можно представить следующим образом:

Пишем код модели TensorFlow
И так, что мы имеем:
Входные данные в формате
[x, y]Выходные данные в формате
[y1, y2, y3, y4]
На основе этого можно уже построить простую layers-модель в Tensorflow - в качестве входных параметров массив двух элементов - это inputShape: [2], выходные данные - units: 4 что соответствует массиву из четырех элементов.
const tf = require("@tensorflow/tfjs-node"); const model = tf.sequential(); model.add(tf.layers.dense({ inputShape: [2], units: 32, activation: "relu" })); model.add(tf.layers.dense({ units: 32, activation: "relu6" })); model.add(tf.layers.dense({ units: 32, activation: "relu6" })); model.add(tf.layers.dense({ units: 4, activation: "relu" })); model.summary(); model.compile({ optimizer: tf.train.sgd(0.0001), loss: "meanSquaredError", });
И так, наша модель готова! Не просто ли?
Сбор тренировочных данных. Обучение модели.
Что такое обучение? Это процесс обработки различных переменных и их дальнейший анализ для выявления каких-либо закономерностей. Звучит немного запутанно, но давайте разберемся на нашем примере.
Какие данные мы будем собирать и обрабатывать? Это все те же параметры модели - углы наклона и координаты У для ног робота. Но при сборе данных мы действуем наоборот - устанавливаем соответствующие координаты ног робота и записываем углы наклона. Другими словами - наклоняем робот, изменяя его положения ног и запоминаем углы наклона. Тут важно проанализировать, как и в какую сторону его наклонять.
Давайте обусловимся, что мы имеем объекты:
gyroScope- для получения данных с гироскопа. Пусть будет иметь методgetData()который возвращает объект{ X, Y }с углами наклона.leftArm, rightArm, leftArmBack, rightArmBack- объекты с методомsetCoordsдля установки координат ног робота. Для нас интересна координата по оси Y
Запишем наши первые данные!
Тут все просто - это нейтрально положение.
Y - координаты ног [-80, -80, -80, -80] - Данные гироскопа [0, 0].
Далее, меняя координаты передних ног, наклоняем робота, например с шагом 10-20 мм.
Координаты ног - [-50, -50, -80, -80], данные гироскопа [8, 0].
Координаты ног - [-30, -30, -80, -80], данные гироскопа [14, 0].
Теперь наклоняем в другую сторону.
Для этого изменяем координаты противоположных ног.
Координаты ног - [-80, -80, -50, -50], данные гироскопа [-7, 0].
Координаты ног - [-80, -80, -30, -30], данные гироскопа [-12, 0].
И так далее, меняя координаты ног, записываем получившиеся при этом углы наклона.
Сохраняем эти данные в отдельный файл в виде массива
const x_train = [ [0, 0], [8, 0], [14, 0], [-7, 0], [-12, 0] //...... и множество остальных данных ]; const y_train = [ [-80, -80, -80, -80], [-50, -50, -80, -80], [-30, -30, -80, -80], [-80, -80, -50, -50], [-80, -80, -30, -30], //...... и множество остальных данных ];
Таким мы имеем набор тренировочных данных.
Приступаем к тренировке модели.
Чтобы выполнить тренировку модели, необходимо записать следующий код:
model .fit(tf.tensor2d(x_train), tf.tensor2d(y_train), { epochs: 300, batchSize: 4, callbacks: { onEpochEnd(e, l) { console.log(e, l); }, }, });
Здесь я добавил колбэк
onEpochEnd(e, l) { console.log(e, l); },
чтобы можно было видеть в логах текущую потерю точности. Это пригодится дальше.
И так, запускаем наш скрипт node model.js
В консоли можно наблюдать следующие логи:
Epoch 298 / 300 eta=0.0 ===========================================================> 22ms 1380us/step - loss=245.84 297 { loss: 245.83786010742188 } Epoch 299 / 300 eta=0.0 ===========================================================> 23ms 1466us/step - loss=245.56 298 { loss: 245.56163024902344 } Epoch 300 / 300 eta=0.0 ===========================================================> 23ms 1457us/step - loss=245.76 299 { loss: 245.76048278808594 }
Как видно, loss параметр слишком большой. Почему это происходит? Ответ прост - наших данных слишком мало. В данном случае я просто расширю их, добавив те же самые значения несколько раз:
const x_train = [ [0, 0], //....те же самые строчки х10 [0, 0], [8, 0], //....те же самые строчки х10 [8, 0], [14, 0], //....те же самые строчки х10 [14, 0], [-7, 0], //....те же самые строчки х10 [-7, 0], [-12, 0] //....те же самые строчки х10 [-12, 0] //...... и множество остальных данных ]; const y_train = [ [-80, -80, -80, -80], //....те же самые строчки х10 [-80, -80, -80, -80], [-50, -50, -80, -80], //....те же самые строчки х10 [-50, -50, -80, -80], [-30, -30, -80, -80], //....те же самые строчки х10 [-30, -30, -80, -80], [-80, -80, -50, -50], //....те же самые строчки х10 [-80, -80, -50, -50], [-80, -80, -30, -30], //....те же самые строчки х10 [-80, -80, -30, -30], //...... и множество остальных данных ];
Запустим скрипт повторно. Последние логи в консоли выглядят следующим образом:
296 { loss: 0.3628617525100708 } Epoch 298 / 300 eta=0.0 ===========================================================> 61ms 358us/step - loss=0.363 297 { loss: 0.3628309965133667 } Epoch 299 / 300 eta=0.0 ===========================================================> 57ms 338us/step - loss=0.364 298 { loss: 0.36354658007621765 } Epoch 300 / 300 eta=0.0 ===========================================================> 58ms 341us/step - loss=0.363 299 { loss: 0.3632044792175293 }
Отлично! Теперь добавим код для сохранения модели:
model .fit(tf.tensor2d(x_train), tf.tensor2d(y_train), { epochs: 300, batchSize: 4, callbacks: { onEpochEnd(e, l) { console.log(e, l); }, }, }).then(() => model.save('file://model-js'));
После запуска скрипта наша модель будет сохранена в данной папке:

Используем созданную модель для управления роботом
Настала практическая часть. Мы можем загрузить нашу модель и использовать ее для определения положения ног в зависимости от углов. Для загрузки модели напишем следующий код:
const tf = require("@tensorflow/tfjs-node"); async function initModel() { const model = await tf.loadLayersModel("file://model-js/model.json"); return model; } initModel().then(model => { /////// После загрузки модели можно передать в нее данные с гироскопа X и Y const result = model.predict(tf.tensor2d([[X, Y]])).dataSync(); });
Метод model.predict() используется для получения координат ног.
Как работает алгоритм - гироскоп считывает углы несколько раз в секунду и передает их в model.predict(). Полученный результат, в виде массива из четырех элементов, используем для установки координат ног робота.
Абстрактный код может выглядеть так -
const tf = require("@tensorflow/tfjs-node"); async function initModel() { const model = await tf.loadLayersModel("file://model-js/model.json"); return model; } initModel().then(model => { setInterval(async () => { const { X, Y } = await gyroScope.getData(); const result = await model.predict(tf.tensor2d([X, Y])).dataSync(); leftArm.setCoords(defaultX, result[0], defaultZ); rightArm.setCoords(defaultX, result[1], defaultZ); leftArmBack.setCoords(defaultX, result[2], defaultZ); rightArmBack.setCoords(defaultX, result[3], defaultZ); }, 30); });
(Здесь я не привожу реализацию объектов gyroScope и leftArm, rightArm, leftArmBack, rightArmBack)
Вот и все! Теперь, наклоняя робота в разные стороны он будет автоматически двигать свои ноги.
Заключение
Как видно из примера, использование ИИ не выглядит каким-то сложным. Тут важнее понимать, какую задачу он должен решать.
В моих планах продолжить экспериментирование с обработкой данных гироскопа и различных моделях поведения робота.
