如何使用 AI 进行猫监控

使用简单的人工智能自动检测您的猫是否正在进出房屋!

项目介绍

在度假时,能够知道我的猫是否在家可能是一个很好的补充。为此,我们将对猫瓣添加检测,以了解猫是进入还是退出。

我们将用加速度计监测猫拍打产生的振动,并使用简单的人工智能 (AI) 进行分类。在这种情况下使用人工智能的好处是它更容易。我知道,这很疯狂,但在这种情况下,使用我们将使用的工具,使用 AI 比通过设置阈值等手动操作更容易、更快捷。

为了发布此用例,我们将:

  1. 编写一些Arduino代码来使用加速度计
  2. 使用串口记录猫进出的振动数据
  3. 使用 NanoEdge AI Studio 工具获取 AI 库(模型 + 代码使用它)
  4. 将几 lin 中的检测添加到 Arduino 代码中

就是这样!

第 1 步:设置

首先将屏蔽层插入电路板:

在Arduino IDE中:

确保您选择了正确的 COM 端口:工具>端口,然后选择正确的端口。

选择正确的电路板:

  1. Arduino Renesas UNO R4 > 开发板的工具>板 > Arduino UNO R4 WIFI
  2. 如果没有找到它,请单击“Tools > Boards > Boards Manager…”,查找 UNO R4 并安装软件包

选择所需的库:

  1. Sketch > Include Library,我们需要包含以下库:
  1. 线
  2. LIS2DW12传感器

您还可以下载您没有的库:草图>包括库>管理库…

在本教程的后面部分,我们还需要添加 NanoEdge AI 库来自行处理声音,而不是手动处理。

猫拍打设置:

  1. 将 X-NUCLEO-IKS01A3 插入 Arduino R4 WiFI
  2. 将设置放在猫耳光上,以便稍后收集振动

第 2 步:使用加速度计

我们的目标是收集数据来训练人工智能,以了解猫是否正在进出房子。

为了实现这一点,我们需要使用加速度计。

我们需要:

  1. 包括所需的库
  2. 定义传感器的参数
  3. 初始化串行、I2C 和加速度计
  4. 创建缓冲区并收集数据

您将在下面的代码中看到,我们在变量neai_buffer(对于 NanoEdge AI 缓冲区)中收集数据缓冲区,而不仅仅是 3 个值 (x,y,z) 的样本。这是为了稍后创建AI模型。

为什么选择缓冲区?因为它们包含一段时间内发生的事情的信息。使用缓冲区(信号),您可以回答诸如“它是增加还是减少”之类的问题,但仅使用一个值就不能。

我们将使用包含猫进入房屋产生的振动示例的缓冲区和其他包含猫离开房屋产生的振动示例的缓冲区,并使用 AI 模型自动对它们进行分类。

您可以刷写此代码以收集大小为 1024 * 3 轴的缓冲区并通过 Serial 打印它们:

/* Libraries part */        
#include "Wire.h"        
#include <LIS2DW12Sensor.h>        
/* Macros definitions */        
#define SERIAL_BAUD_RATE  115200        
/* Sensor data rates.        
	  You can choose from the following values for both accel & gyro:        
	  12.5f, 25.0f, 50.0f, 100.0f, 200.0f, 400.0f, 800.0f & 1600.0f.        
*/        
#define SENSOR_DATA_RATE	1600.0f        
/* Sensor ranges.        
	  You can choose from:        
	  2, 4, 8 & 16.        
*/        
#define SENSOR_RANGE	4        
#define SENSOR_SAMPLES	1024        
#define AXIS  3        
/* Sensor object declaration using I2C */        
LIS2DW12Sensor Accelero(&Wire);        
/* Global variables definitions */        
static uint8_t drdy = 0;        
static uint16_t neai_ptr = 0;        
static int32_t accelerometer[3];        
static float neai_buffer[SENSOR_SAMPLES * AXIS] = {0.0};        
/* Initialization function: In this function,        
	   code runs only once at boot / reset.        
*/        
void setup() {        
	 /* Init serial at baud rate 115200 */        
	 Serial.begin(SERIAL_BAUD_RATE);        
	 /* I2C workaround: Sometimes, on some boards,        
	    I2C get stuck after software reboot, reset so,        
	    to avoid this, we toggle I2C clock pin at boot.        
	 */        
	 pinMode(SCL, OUTPUT);        
	 for (uint8_t i = 0; i < 20; i++) {        
	   digitalWrite(SCL, !digitalRead(SCL));        
	   delay(1);        
	 }        
	 delay(100);        
	 Wire.begin();        
	 Accelero.begin();        
	 Accelero.Enable_X();        
	 Accelero.Set_X_ODR(SENSOR_DATA_RATE);        
	 Accelero.Set_X_FS(SENSOR_RANGE);        
}        
/* Main function: Code run indefinitely */        
void loop() {        
	 // Those values should be adapted regarding the user setup        
	 // Goal is to complete the buffer only if a cat is there         
	 if (!(rms < 700 || rms > 2000))        
	   return;        
	 /* Get data in the neai buffer */        
	 while (neai_ptr < SENSOR_SAMPLES) {        
	   /* Check if new data if available */        
	   Accelero.ReadReg(LIS2DW12_STATUS, &drdy);        
	   if (drdy & 0x01) {        
	     /* If new data is available we read it ! */        
	     Accelero.Get_X_Axes(accelerometer);        
	     /* Fill neai buffer with new accel data */        
	     neai_buffer[AXIS * neai_ptr] = (float) accelerometer[0];        
	     neai_buffer[(AXIS * neai_ptr) + 1] = (float) accelerometer[1];        
	     neai_buffer[(AXIS * neai_ptr) + 2] = (float) accelerometer[2];        
	     /* Increment neai pointer */        
	     neai_ptr++;        
	   }        
	 }        
	 /* Reset pointer */        
	 neai_ptr = 0;        
	 /* Print the whole buffer to the serial */        
	 for (uint16_t i = 0; i < AXIS * SENSOR_SAMPLES; i++) {        
	   Serial.print((String)neai_buffer[i] + " ");        
	 }        
	 Serial.print("\n");        
	 // }        
	 /* Clean neai buffer */        
	 memset(neai_buffer, 0.0, AXIS * SENSOR_SAMPLES * sizeof(float));        
}        

第 3 步:NanoEdge AI Studio

使用上一步中的代码,我们收集大小为 1024 的缓冲区,数据速率为 1.6kHz。这意味着当我们的振动高于小阈值时,我们收集代表猫通过猫拍打的 64 毫秒的数据。

我们将收集数据集并直接在 NanoEdge 中创建模型(它是免费的):

  1. 安装 NanoEdge AI Studio: https://stm32ai.st.com/download-nanoedgeai/ 美元
  1. 创建N类分类项目
  2. 选择加速度计 3 轴作为传感器
  3. 选择Arduino UNO R4 WIFI作为目标
  4. 收集数据:
  5. 使用Arduino IDE刷写上一步的代码
  6. 在 NanoEdge 的 signals 选项卡中,单击 add Signal,然后单击 serial
  7. 首先收集多个猫进入房子的例子(100个应该足够了)
  8. 然后是猫退出的多个例子

让猫做你想做的事可能很乏味,你可以尝试用手做第一个原型,但如果用真正的猫:)完成它会更好

  1. 启动基准测试并获得良好的准确性

整个基准测试可能需要数小时,但在最初的几分钟内,您的分数将接近最终基准测试。因此,如果几分钟后你有 90+% 的分数,你可以根据需要停止它。

如果您的数据少于 80+%,您可能需要收集更多/更好的数据

  1. 编译库

在编译步骤中,您将获得一个包含Arduino库的.zip。下一步将对此进行更多介绍。

有关如何使用 NanoEdge AI Studio 和 Arduino 的更多详细信息,分步教程: $ https://wiki.st.com/stm32mcu/wiki/AI:How_to_create_Arduino_Rock-Paper-Scissors_game_using_NanoEdge_AI_Studio $

步骤 4:向代码添加分类

现在我们有了分类库,我们需要将其添加到我们的Arduino代码中:

  1. 打开得到的.zip,有一个Arduino文件夹,里面有另一个zip
  2. 在Arduino IDE中导入库:Sketch > Include library > Add .ZIP library…,然后选择Arduino文件夹中的.zip

下面是一个代码示例,用于演示我们需要做什么(代码不编译,仅用于解释目的)

#include "NanoEdgeAI.h"        
#include "knowledge.h"        
#define CLASS_NUMBER 2        
/* NanoEdgeAI variables part */        
uint8_t neai_code = 0; //to check the initialization of the library        
uint16_t id_class = 0; // Point to id class (see argument of neai_classification fct)        
float output_class_buffer[CLASS_NUMBER]; // Buffer of class probabilities        
const char *id2class[CLASS_NUMBER + 1] = { // Buffer for mapping class id to class name        
	 "unknown", //always present        
	 "inside",        
	 "outside",        
};        
static float neai_buffer[SENSOR_SAMPLES * AXIS] = {0.0}; //our neai buffer        
void setup() {        
	 ...        
	 /* initialize NanoEdge Library */        
	 neai_code = neai_classification_init(knowledge);        
	 if (neai_code != NEAI_OK) {        
	   Serial.print("Not supported board.\n");        
	 }        
	 ...        
}        
void loop() {          
	  ...        
	  /* make a classification */        
	  get_microphone_data();        
	  neai_classification(neai_buffer, output_class_buffer, &id_class);        
	  /* then depending on the class in id_class, we play up, down or wathever */        
	  ...        
}        

完整的代码在下一步中。

警告:

您需要检查Arduino代码中的id2class变量是否与我们之前导入的库中的NanoEdgeAI.h文件中的变量相同。

第 5 步:最终代码

您可以找到附上的完整代码。该代码包含前面步骤中介绍的所有内容。

第 6 步:走得更远

现在,您可以对猫是否进入或离开房屋进行分类,由您来定义如何处理它。

以下是一些建议:

  1. 检测到时,使用开发板的 WiFi 发送消息或电子邮件:https://www.youtube.com/watch?v=KwFyCGYuV6Q
  2. 创建托管在服务器上的仪表板,并使用 WiFi: https://www.youtube.com/watch?v=32VcKyI0dio 发送更新

最终代码

/* If you want to use NEAI functions please, include NEAI library
   in your Arduino libraries then, uncomment NEAI parts in the following code
*/

/* Libraries part */
#include "Wire.h"
#include <LIS2DW12Sensor.h>

/* Macros definitions */
#define SERIAL_BAUD_RATE  115200

/* Sensor data rates.
   You can choose from the following values for both accel & gyro:
   12.5f, 25.0f, 50.0f, 100.0f, 200.0f, 400.0f, 800.0f & 1600.0f.
*/
#define SENSOR_DATA_RATE	1600.0f

/* Sensor ranges.
   You can choose from:
   2, 4, 8 & 16.
*/
#define SENSOR_RANGE	4


#define SENSOR_SAMPLES	1024
#define AXIS  3

/* Sensor object declaration using I2C */
LIS2DW12Sensor Accelero(&Wire);

/* Global variables definitions */
static uint8_t drdy = 0;
static uint16_t neai_ptr = 0;
static int32_t accelerometer[3];
static float neai_buffer[SENSOR_SAMPLES * AXIS] = {0.0};


/* Initialization function: In this function,
    code runs only once at boot / reset.
*/
void setup() {
  /* Init serial at baud rate 115200 */
  Serial.begin(SERIAL_BAUD_RATE);

  /* I2C workaround: Sometimes, on some boards,
     I2C get stuck after software reboot, reset so,
     to avoid this, we toggle I2C clock pin at boot.
  */
  pinMode(SCL, OUTPUT);
  for (uint8_t i = 0; i < 20; i++) {
    digitalWrite(SCL, !digitalRead(SCL));
    delay(1);
  }
  delay(100);

  Wire.begin();
  Accelero.begin();
  Accelero.Enable_X();
  Accelero.Set_X_ODR(SENSOR_DATA_RATE);
  Accelero.Set_X_FS(SENSOR_RANGE);
}

/* Main function: Code run indefinitely */
void loop() {
  /* Get data in the neai buffer */
  while (neai_ptr < SENSOR_SAMPLES) {
    /* Check if new data if available */
    Accelero.ReadReg(LIS2DW12_STATUS, &drdy);
    if (drdy & 0x01) {
      /* If new data is available we read it ! */
      Accelero.Get_X_Axes(accelerometer);
      /* Fill neai buffer with new accel data */
      neai_buffer[AXIS * neai_ptr] = (float) accelerometer[0];
      neai_buffer[(AXIS * neai_ptr) + 1] = (float) accelerometer[1];
      neai_buffer[(AXIS * neai_ptr) + 2] = (float) accelerometer[2];
      /* Increment neai pointer */
      neai_ptr++;
    }
  }
  /* Reset pointer */
  neai_ptr = 0;

  /* Print the whole buffer to the serial */
  for (uint16_t i = 0; i < AXIS * SENSOR_SAMPLES; i++) {
    Serial.print((String)neai_buffer[i] + " ");
  }
  Serial.print("\n");
  // }

  /* Clean neai buffer */
  memset(neai_buffer, 0.0, AXIS * SENSOR_SAMPLES * sizeof(float));
}

Similar Posts

Leave a Reply