В этом руководстве по Arduino рассказывается, как использовать Arduino TinyML для распознавания жестов с помощью Tensorflow lite micro. Мы будем использовать внешний датчик MPU6050, который является шестиосевым (гироскоп и акселерометр). Этот проект интересен тем, что мы будем разрабатывать микромодель Tensorflow lite с нуля. Идея использования Arduino TinyML для распознавания жестов начинается с официального примера Arduino Tensorflow (дополнительную информацию и исходный код можно найти здесь). Идея этого поста - показать, как мы можем использовать другие датчики, кроме встроенных в Arduino Nano 33 BLE.
Шаги по созданию приложения Arduino TinyML
Вот шаги:
- Создайте приложение Arduino для создания набора данных с помощью акселерометра MPU6050.
- Создайте модель микромашинного обучения Tensorflow lite, изменив пример в репозитории Tensorflow.
- Обучите модель машинного обучения, оцените ее количественно и, наконец, экспортируйте окончательную модель.
- Разработайте приложение Arduino TinyML, которое использует модель для распознавания жестов.
Шаг 1. Создайте приложение Arduino для создания набора данных с помощью внешнего акселерометра.
Как и в других проектах машинного обучения, первым шагом является создание набора данных, в котором мы будем обучать Tensorflow lite micro для получения окончательной модели. Для этого мы будем использовать Arduino Nano 33 BLE с MPU6050. Перед запуском необходимо установить библиотеку MPU6050. Это код:
#include <Adafruit_MPU6050.h> #include <Adafruit_Sensor.h> #include <Wire.h> #define NUM_SAMPLES 119 #define THRESHOLD 3 Adafruit_MPU6050 mpu; int samples = 0; float ax; float ay; float az; float baseAx; float baseAy; float baseAz; void setup(void) { Serial.begin(9600); while (!Serial) delay(10); Serial.println("Adafruit MPU6050 test!"); // Try to initialize! if (!mpu.begin()) { Serial.println("Failed to find MPU6050 chip"); while (1) { delay(10); } } Serial.println("MPU6050 Found!"); // Set Accelaration Range mpu.setAccelerometerRange(MPU6050_RANGE_4_G); calibrate_sensor(); Serial.println(""); Serial.println("aX,aY,az"); delay(100); } boolean detectMovement() { float totAcc = fabs(ax) + fabs(ay) + fabs(az); return totAcc > THRESHOLD; } void calibrate_sensor() { float totX, totY, totZ; sensors_event_t a, g, temp; for (int i = 0; i < 10; i++) { mpu.getEvent(&a, &g, &temp); totX = totX + a.acceleration.x; totY = totY + a.acceleration.y; totZ = totZ + a.acceleration.z; } baseAx = totX / 10; baseAy = totY / 10; baseAz = totZ / 10; //Serial.print("BaseX:"); //Serial.println(baseAx); //Serial.print("BaseY:"); //Serial.println(baseAy); //Serial.print("BaseZ:"); //Serial.println(baseAz); } void loop() { /* Get new sensor events with the readings */ sensors_event_t a, g, temp; mpu.getEvent(&a, &g, &temp); samples = 0; ax = a.acceleration.x - baseAx; ay = a.acceleration.y - baseAy; az = a.acceleration.z - baseAz; if (!detectMovement()) { delay(10); return; } while (samples < NUM_SAMPLES) { Serial.print(ax, 3); Serial.print(","); Serial.print(ay, 3); Serial.print(","); Serial.println(az, 3); samples++; if (samples == NUM_SAMPLES) Serial.println(); } }
Код действительно прост. Он получает данные от акселерометра и выводит эти значения на последовательный порт. Перед тем, как начать получение значений, необходимо выполнить этап калибровки. Калибровка сенсора действительно проста и быстра. В любом случае, мы можем использовать выходной результат для нормализации значений.
Дополнительные полезные ресурсы:
Как распознавать ключевые слова с помощью машинного обучения с Arduino
Arduino с TinyML с набором данных ключевых слов Google
Классификация изображений ESP32-CAM с использованием EdgeImpulse
Сбор данных жестов
В этом простом руководстве мы хотим распознавать только два типа жестов:
Следовательно, необходимо начинать сбор данных, повторяя несколько раз одни и те же движения, используя два разных сеанса сбора данных. Следуй этим шагам:
- Запустите приложение Arduino на вашем Arduino Nano 33 BLE.
- Начните сбор данных последовательного порта. Если вы используете OSX или Linux, вы можете получить данные последовательного порта с помощью этой команды:
Теперь у вас должно быть два имени файла punch.csv и up.csv:
Построение микромодели Tensorflow lite и ее обучение
Теперь откройте этот colab-файл в вашем браузере. И давайте изменим его в соответствии с нашими потребностями:
2. Перейдите в Parse, подготовьте данные и измените их с помощью ярлыков punch и up. Затем измените код, как показано ниже:
Мы не используем данные гироскопа.
3. Обучите модель и экспортируйте ее.
Распознавание жестов с помощью Arduino и Tensorflow lite
Наконец последний шаг. Нам нужно создать код, запускающий Tensorflow lite micro на Arduino Nano 33 BLE. Код показан ниже и основан на этом примере:
#include <TensorFlowLite.h> #include "tensorflow/lite/micro/micro_error_reporter.h" #include "tensorflow/lite/micro/micro_interpreter.h" #include "tensorflow/lite/micro/micro_mutable_op_resolver.h" #include "tensorflow/lite/schema/schema_generated.h" #include "tensorflow/lite/version.h" #include "tensorflow/lite/micro/all_ops_resolver.h" #include "model.h" #include <Adafruit_MPU6050.h> #include <Adafruit_Sensor.h> #include <Wire.h> #define NUM_SAMPLES 119 #define THRESHOLD 3 Adafruit_MPU6050 mpu; int samples = 0; float ax; float ay; float az; float baseAx; float baseAy; float baseAz; tflite::ErrorReporter* tflErrorReporter; tflite::AllOpsResolver tflOpsResolver; const tflite::Model* tflModel = nullptr; tflite::MicroInterpreter* tflInterpreter = nullptr; TfLiteTensor* tflInputTensor = nullptr; TfLiteTensor* tflOutputTensor = nullptr; constexpr int tensorArenaSize = 100 * 1024; byte tensorArena[tensorArenaSize]; const char* GESTURES[] = { "punch", "up" }; #define NUM_GESTURES 2 void init_tensorflow() { Serial.println("Initialing Tensorflow Lite.."); // get the TFL representation of the model byte array tflModel = tflite::GetModel(model); if (tflModel->version() != TFLITE_SCHEMA_VERSION) { Serial.println("Model schema mismatch!"); while (1); } static tflite::MicroErrorReporter micro_error_reporter; tflErrorReporter = µ_error_reporter; static tflite::MicroInterpreter static_interpreter( tflModel, tflOpsResolver, tensorArena, tensorArenaSize, tflErrorReporter); tflInterpreter = &static_interpreter; // Allocate memory for the model's input and output tensors TfLiteStatus allocate_status = tflInterpreter->AllocateTensors(); if (allocate_status != kTfLiteOk) { TF_LITE_REPORT_ERROR(tflErrorReporter, "AllocateTensors() failed"); return; } // Get pointers for the model's input and output tensors tflInputTensor = tflInterpreter->input(0); tflOutputTensor = tflInterpreter->output(0); Serial.println("Tensorflow initialized"); } void setup(void) { Serial.begin(9600); while (!Serial) delay(10); // will pause Zero, Leonardo, etc until serial console opens Serial.println("Adafruit MPU6050 test!"); // Try to initialize! if (!mpu.begin()) { Serial.println("Failed to find MPU6050 chip"); while (1) { delay(10); } } Serial.println("MPU6050 Found!"); // Set Accelaration Range mpu.setAccelerometerRange(MPU6050_RANGE_4_G); calibrate_sensor(); Serial.println(""); init_tensorflow(); delay(100); } boolean detectMovement() { float totAcc = fabs(ax) + fabs(ay) + fabs(az); return totAcc > THRESHOLD; } void calibrate_sensor() { float totX, totY, totZ; sensors_event_t a, g, temp; for (int i = 0; i < 10; i++) { mpu.getEvent(&a, &g, &temp); totX = totX + a.acceleration.x; totY = totY + a.acceleration.y; totZ = totZ + a.acceleration.z; } baseAx = totX / 10; baseAy = totY / 10; baseAz = totZ / 10; } void loop() { sensors_event_t a, g, temp; mpu.getEvent(&a, &g, &temp); samples = 0; ax = a.acceleration.x - baseAx; ay = a.acceleration.y - baseAy; az = a.acceleration.z - baseAz; if (!detectMovement()) { delay(10); return; } while (samples < NUM_SAMPLES) { // Read samples mpu.getEvent(&a, &g, &temp); ax = a.acceleration.x - baseAx; ay = a.acceleration.y - baseAy; az = a.acceleration.z - baseAz; tflInputTensor->data.f[samples * 3 + 0] = (ax + 4.0) / 8.0; tflInputTensor->data.f[samples * 3 + 1] = (ay + 4.0) / 8.0; tflInputTensor->data.f[samples * 3 + 2] = (az + 4.0) / 8.0; samples++; } if (samples == NUM_SAMPLES) { Serial.println("Invoking inferences..."); TfLiteStatus invokeStatus = tflInterpreter->Invoke(); if (invokeStatus != kTfLiteOk) { Serial.println("Invoke failed!"); return; } for (int i = 0; i < NUM_GESTURES; i++) { Serial.print(GESTURES[i]); Serial.print(": "); Serial.println(tflOutputTensor->data.f[i], 6); } Serial.println("-----------------------"); return; } }
Запускает код и начинает распознавать жесты с помощью Arduino.
Подведение итогов
Мы узнали, как создать микромодель Tensorflow lite для Arduino и как мы можем использовать машинное обучение для распознавания жестов. Используя приведенный выше код, вы можете распознавать другие жесты, создавая собственный набор данных.
Первоначально опубликовано на https://www.survivingwithandroid.com 10 апреля 2021 г.