手势分类是机器学习可以做的一个简单但同时也是很好的例子。它使用大量“杂乱”的数据对事物进行分类。
在这个项目中,我们将制作一个有 4 个类的分类器,idle、up_down、left_right 和 circle。该项目将使用Esp32 板(8 美元)制作,因为与使用Arduino Nano 33 BLE Sense (35 美元)的“经典”示例相比,它非常便宜。
要将数据上传到 Edge Impulse,我们需要使用 Edge Impulse CLI,按照本教程了解如何安装它。
我们要使用的工具是Data Forwarder ,该工具将串行中可用的数据上传到 Edge Impulse。我们需要使用 Arduino Sketch 以一定的采样频率将数据上传到串行,并用“,”分隔数据,在这种情况下,我们将发送加速度数据。
#include
#include
#include
#define FREQUENCY_HZ 60
#define INTERVAL_MS (1000 / (FREQUENCY_HZ + 1))
// objeto da classe Adafruit_MPU6050
Adafruit_MPU6050 mpu;
static unsigned long last_interval_ms = 0;
void setup() {
Serial.begin(115200);
Serial.println("Classificador de gestos com TinyML");
// Try to initialize!
if (!mpu.begin()) {
Serial.println("Failed to find MPU6050 chip");
while (1) {
delay(10);
}
}
Serial.println("MPU6050 Found!");
mpu.setAccelerometerRange(MPU6050_RANGE_8_G);
Serial.print("Accelerometer range set to: ");
switch (mpu.getAccelerometerRange()) {
case MPU6050_RANGE_2_G:
Serial.println("+-2G");
break;
case MPU6050_RANGE_4_G:
Serial.println("+-4G");
break;
case MPU6050_RANGE_8_G:
Serial.println("+-8G");
break;
case MPU6050_RANGE_16_G:
Serial.println("+-16G");
break;
}
mpu.setGyroRange(MPU6050_RANGE_500_DEG);
Serial.print("Gyro range set to: ");
switch (mpu.getGyroRange()) {
case MPU6050_RANGE_250_DEG:
Serial.println("+- 250 deg/s");
break;
case MPU6050_RANGE_500_DEG:
Serial.println("+- 500 deg/s");
break;
case MPU6050_RANGE_1000_DEG:
Serial.println("+- 1000 deg/s");
break;
case MPU6050_RANGE_2000_DEG:
Serial.println("+- 2000 deg/s");
break;
}
mpu.setFilterBandwidth(MPU6050_BAND_21_HZ);
Serial.print("Filter bandwidth set to: ");
switch (mpu.getFilterBandwidth()) {
case MPU6050_BAND_260_HZ:
Serial.println("260 Hz");
break;
case MPU6050_BAND_184_HZ:
Serial.println("184 Hz");
break;
case MPU6050_BAND_94_HZ:
Serial.println("94 Hz");
break;
case MPU6050_BAND_44_HZ:
Serial.println("44 Hz");
break;
case MPU6050_BAND_21_HZ:
Serial.println("21 Hz");
break;
case MPU6050_BAND_10_HZ:
Serial.println("10 Hz");
break;
case MPU6050_BAND_5_HZ:
Serial.println("5 Hz");
break;
}
Serial.println("");
delay(100);
}
void loop() {
sensors_event_t a, g, temp;
if (millis() > last_interval_ms + INTERVAL_MS) {
last_interval_ms = millis();
mpu.getEvent(&a, &g, &temp);
Serial.print(a.acceleration.x);
Serial.print(",");
Serial.print(a.acceleration.y);
Serial.print(",");
Serial.println(a.acceleration.z);
}
}
在 Esp32 板准备好发送数据后,我们需要将 Edge Impulse 帐户与 CLI 连接起来,它应该是这样的:
之后你可以开始收集一些数据,你可以做很多动作,只要确保它们不太相似(稍后你可以在特征浏览器中决定)。
脉冲是一组采集、处理和推理数据。
频谱分析为 Keras 模型创建了要使用的特征,在这个阶段,信号被过滤并生成两个频谱,一个是频率(使用 FFT),另一个是能量(PSD )
我们可以使用特征资源管理器看到我们的类是如何分离的:
它们看起来非常不同,所以我们应该用模型得到一个很好的结果。
这些数据进入神经网络,因为这是一项简单的任务,神经网络也应该很简单。
在我们训练模型之后,我们可以在混淆矩阵中看到性能:
它看起来不错,但性能可能会随着测试数据而降低。
对模型感到满意后,您可以使用 de Arduino IDE 和 Edge Impulse 中生成的库将其嵌入。
就像 Arduino 中的另一个库一样,我们需要包含它并使用它们的函数进行推理。
#include
#include
#include
#include
#define FREQUENCY_HZ 60
#define INTERVAL_MS (1000 / (FREQUENCY_HZ + 1))
#define RED 16
#define GREEN 17
#define BLUE 18
// objeto da classe Adafruit_MPU6050
Adafruit_MPU6050 mpu;
float features[EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE];
size_t feature_ix = 0;
static unsigned long last_interval_ms = 0;
void setup() {
Serial.begin(115200);
ledcSetup(0, 5000, 8);
/*
ledcAttachPin(RED, 0);
ledcAttachPin(GREEN, 1);
ledcAttachPin(BLUE, 2);
*/
pinMode(RED, OUTPUT);
pinMode(GREEN, OUTPUT);
pinMode(BLUE, OUTPUT);
if (!mpu.begin()) {
Serial.println("Failed to find MPU6050 chip");
while (1) {
delay(10);
}
}
Serial.println("MPU6050 Found!");
mpu.setAccelerometerRange(MPU6050_RANGE_8_G);
Serial.print("Accelerometer range set to: ");
switch (mpu.getAccelerometerRange()) {
case MPU6050_RANGE_2_G:
Serial.println("+-2G");
break;
case MPU6050_RANGE_4_G:
Serial.println("+-4G");
break;
case MPU6050_RANGE_8_G:
Serial.println("+-8G");
break;
case MPU6050_RANGE_16_G:
Serial.println("+-16G");
break;
}
mpu.setGyroRange(MPU6050_RANGE_500_DEG);
Serial.print("Gyro range set to: ");
switch (mpu.getGyroRange()) {
case MPU6050_RANGE_250_DEG:
Serial.println("+- 250 deg/s");
break;
case MPU6050_RANGE_500_DEG:
Serial.println("+- 500 deg/s");
break;
case MPU6050_RANGE_1000_DEG:
Serial.println("+- 1000 deg/s");
break;
case MPU6050_RANGE_2000_DEG:
Serial.println("+- 2000 deg/s");
break;
}
mpu.setFilterBandwidth(MPU6050_BAND_21_HZ);
Serial.print("Filter bandwidth set to: ");
switch (mpu.getFilterBandwidth()) {
case MPU6050_BAND_260_HZ:
Serial.println("260 Hz");
break;
case MPU6050_BAND_184_HZ:
Serial.println("184 Hz");
break;
case MPU6050_BAND_94_HZ:
Serial.println("94 Hz");
break;
case MPU6050_BAND_44_HZ:
Serial.println("44 Hz");
break;
case MPU6050_BAND_21_HZ:
Serial.println("21 Hz");
break;
case MPU6050_BAND_10_HZ:
Serial.println("10 Hz");
break;
case MPU6050_BAND_5_HZ:
Serial.println("5 Hz");
break;
}
Serial.println("");
delay(100);
Serial.print("Features: ");
Serial.println(EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE);
Serial.print("Label count: ");
Serial.println(EI_CLASSIFIER_LABEL_COUNT);
}
void loop() {
sensors_event_t a, g, temp;
if (millis() > last_interval_ms + INTERVAL_MS) {
last_interval_ms = millis();
mpu.getEvent(&a, &g, &temp);
features[feature_ix++] = a.acceleration.x;
features[feature_ix++] = a.acceleration.y;
features[feature_ix++] = a.acceleration.z;
if (feature_ix == EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE) {
Serial.println("Running the inference...");
signal_t signal;
ei_impulse_result_t result;
int err = numpy::signal_from_buffer(features, EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE, &signal);
if (err != 0) {
ei_printf("Failed to create signal from buffer (%d)\n", err);
return;
}
EI_IMPULSE_ERROR res = run_classifier(&signal, &result, true);
if (res != 0) return;
ei_printf("Predictions ");
ei_printf("(DSP: %d ms., Classification: %d ms.)",
result.timing.dsp, result.timing.classification);
ei_printf(": \n");
for (size_t ix = 0; ix < EI_CLASSIFIER_LABEL_COUNT; ix++) {
ei_printf(" %s: %.5f\n", result.classification[ix].label, result.classification[ix].value);
if (result.classification[ix].value > 0.6) {
if (result.classification[ix].label == "up_down")
{
/*// color code #00C9CC (R = 0, G = 201, B = 204)
//analogWrite(RED, 0);
ledcWrite(0, 0);
//analogWrite(GREEN, 201);
ledcWrite(1, 201);
//analogWrite(BLUE, 204);
ledcWrite(2, 204);
Serial.println("up and down");*/
digitalWrite(RED, HIGH);
digitalWrite(GREEN, LOW);
digitalWrite(BLUE, LOW);
} else if (result.classification[ix].label == "left_right")
{
digitalWrite(RED, LOW);
digitalWrite(GREEN, HIGH);
digitalWrite(BLUE, LOW);
} else if (result.classification[ix].label == "circle")
{
digitalWrite(RED, LOW);
digitalWrite(GREEN, LOW);
digitalWrite(BLUE, HIGH);
} else
{
digitalWrite(RED, LOW);
digitalWrite(GREEN, LOW);
digitalWrite(BLUE, LOW);
}
}
}
feature_ix = 0;
}
}
}
void ei_printf(const char *format, ...) {
static char print_buf[1024] = { 0 };
va_list args;
va_start(args, format);
int r = vsnprintf(print_buf, sizeof(print_buf), format, args);
va_end(args);
if (r > 0) {
Serial.write(print_buf);
}
}
在这个应用程序中,我使用 RGB LED 来显示检测到的类,您也可以在串行监视器中看到。
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
全部0条评论
快来发表一下你的评论吧 !