В этом руководстве по Arduino рассказывается, как использовать Arduino TinyML для распознавания жестов с помощью Tensorflow lite micro. Мы будем использовать внешний датчик MPU6050, который является шестиосевым (гироскоп и акселерометр). Этот проект интересен тем, что мы будем разрабатывать микромодель Tensorflow lite с нуля. Идея использования Arduino TinyML для распознавания жестов начинается с официального примера Arduino Tensorflow (дополнительную информацию и исходный код можно найти здесь). Идея этого поста - показать, как мы можем использовать другие датчики, кроме встроенных в Arduino Nano 33 BLE.

Шаги по созданию приложения Arduino TinyML

Вот шаги:

  1. Создайте приложение Arduino для создания набора данных с помощью акселерометра MPU6050.
  2. Создайте модель микромашинного обучения Tensorflow lite, изменив пример в репозитории Tensorflow.
  3. Обучите модель машинного обучения, оцените ее количественно и, наконец, экспортируйте окончательную модель.
  4. Разработайте приложение 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 = &micro_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 г.