ESP32-S3 边缘人工智能|使用加速度计数据和 ESP-DL 识别人体活动

news/2024/11/8 17:46:34/

边缘计算是一种分布式计算范例,指在更靠近设备的地方进行数据存储和计算。边缘人工智能(边缘 AI)是边缘计算中一项振奋人心的成果,可以令传统技术更高效地运行,在降低功耗的同时又有更好的性能。训练好的神经网络可以在小型设备上进行推理。边缘 AI 的潜在应用领域包括制造业、医疗保健、零售业、监控、智能家居和金融银行业。 

乐鑫提供的 ESP-DL 框架可用于在 ESP32-S3 上部署高性能深度学习模型。

本文将介绍如何读取传感器数据,并使用 ESP-DL 在  ESP32-S3 上部署深度学习模型。 

本文分为以下四个部分 :
1. 部署模型 
2. 定义模型 
3. 运行模型 
4. 结论


ESP-DL 的使用前提】在深入了解 ESP-DL 之前,读者需要: 

  • 构建和训练神经网络的相关知识(查看深度学习的基础知识
  • ESP-IDF release/v4.4 环境(更多信息,请参考设置 ESP-IDF 环境或 ESP-IDF 工具链) 
  • 基础的 C 和 C++ 语言应用知识 
  • 转换成 ESP-DL 格式的模型 

1. 部署模型

使用加速度计数据设计卷积神经网络,识别人类活动。 
*本文不会重点介绍神经网络的开发和 ESP-DL 格式转换。 

1.1 ESP-IDF 项目结构

部署模型的步骤如下: 

  1. 首先,根据 ESP-IDF 标准在 VS Code 中创建一个新项目。有关如何在 VS Code 中创建 ESP32 项目,请参考 ESP-IDF 快速入门。 
  2. 模型转换成 ESP-DL 格式时生成的 .cpp 和 .hpp 文件需放置到当前工作目录中。 
  3. 将所有依赖组件添加到工作目录的 components 文件夹中。 
  4. 添加 ESP-WHO 示例的默认配置 sdkconfig 文件。sdkconfig 文件也可在 GitHub 找到。 


项目目录应如下所示: 

├── CMakeLists.txt 
├── components 
│   ├── bus 
│   ├── mpu6050 
│   └── esp-dl 
├── dependencies.lock 
├── main 
│   ├── app_main.cpp 
│   └── CMakeLists.txt 
├── model 
│   ├── Activity_coefficient.cpp 
│   ├── Activity_coefficient.hpp 
│   └── model_define.hpp 
├── partitions.csv 
├── sdkconfig 
├── sdkconfig.defaults 
├── sdkconfig.defaults.esp32 
├── sdkconfig.defaults.esp32s2 
└── sdkconfig.defaults.esp32s3 


2. 定义模型

按下列步骤和步骤说明在 ‘model_define.hpp’ 中定义模型。在 Netron 中打开模型,会出现图 1 所示内容。 

可视化优化模型 

2.1 导入库

导入所有相关库。请点击此处查看ESP-DL 当前支持的库。 

#pragma once 
#include "dl_layer_model.hpp" 
#include "dl_layer_base.hpp" 
#include "dl_layer_max_pool2d.hpp" 
#include "dl_layer_conv2d.hpp" 
#include "dl_layer_concat.hpp" 
#include "Activity_coefficient.hpp" 
#include "dl_layer_reshape.hpp" 
#include "dl_layer_softmax.hpp" 
#include <stdint.h> using namespace dl; 
using namespace layer; 
using namespace Activity_coefficient; 

2.2 声明层

下一步是声明每个层。 

  • 输入不算是层,因此不在此处定义。 
  • 除了输出层之外,其他所有层都声明为私有层。 
class ACTIVITY : public Model<int16_t>  
{ 
private: Conv2D<int16_t> l1; Conv2D<int16_t> l2; Reshape<int16_t> l3; Conv2D<int16_t> l4; Conv2D<int16_t> l5; public: Softmax<int16_t> l6; 

2.3 初始化层

声明层之后,初始化每个层的权重、偏差、激活函数和形状。 

ACTIVITY () :  l1(Conv2D<int16_t>(-13, get_statefulpartitionedcall_sequential_1_conv2d_2_biasadd_filter(), get_statefulpartitionedcall_sequential_1_conv2d_2_biasadd_bias(), get_statefulpartitionedcall_sequential_1_conv2d_2_biasadd_activation(), PADDING_VALID, {}, 1,1, "l1")), l2(Conv2D<int16_t>(-13, get_statefulpartitionedcall_sequential_1_conv2d_3_biasadd_filter(), get_statefulpartitionedcall_sequential_1_conv2d_3_biasadd_bias(), get_statefulpartitionedcall_sequential_1_conv2d_3_biasadd_activation(), PADDING_VALID, {}, 1,1, "l2")),                        l3(Reshape<int16_t>({1,1,2496},"l2_reshape")),  l4(Conv2D<int16_t>(-11, get_fused_gemm_0_filter(), get_fused_gemm_0_bias(), get_fused_gemm_0_activation(), PADDING_VALID, {}, 1, 1, "l3")), l5(Conv2D<int16_t>(-9, get_fused_gemm_1_filter(), get_fused_gemm_1_bias(), NULL, PADDING_VALID,{}, 1,1, "l4")), l6(Softmax<int16_t>(-14,"l5")){} 

2.4 构建层

下一步是构建每个层。有关构建层的更多信息,请查看每个层的构建函数。 

void build(Tensor<int16_t> &input) { this->l1.build(input); this->l2.build(this->l1.get_output()); this->l3.build(this->l2.get_output()); this->l4.build(this->l3.get_output()); this->l5.build(this->l4.get_output()); this->l6.build(this->l5.get_output()); } 

2.5 调用层

最后,将层连接起来,通过调用函数一一调用。有关调用层的更多信息,请查看每个层调用函数。 

void call(Tensor<int16_t> &input) { this->l1.call(input); input.free_element(); this->l2.call(this->l1.get_output()); this->l1.get_output().free_element(); this->l3.call(this->l2.get_output()); this->l2.get_output().free_element(); this->l4.call(this->l3.get_output()); this->l3.get_output().free_element(); this->l5.call(this->l4.get_output()); this->l4.get_output().free_element(); this->l6.call(this->l5.get_output()); this->l5.get_output().free_element(); } 
}; 

3. 运行模型

构建好模型后,在 ‘app_main.cpp’ 文件中声明模型输入,并在 ESP32-S3 上运行模型。 

3.1 导入库

#include <stdio.h> 
#include <stdlib.h> 
#include "esp_system.h" 
#include "freertos/FreeRTOS.h" 
#include "freertos/task.h" 
#include "dl_tool.hpp" 
#include "model_define.hpp" 
#include "i2c_bus.h" 
#include "mpu6050.h" 
#include "driver/i2c.h" 
#include "esp_log.h" 

3.2 声明输入

神经网络的输入来自 MPU6050 加速度传感器。读取实时的传感器数据需使用乐鑫的  MPU6050 驱动。每四秒钟,传感器数据便会存储在一个数组中,输入到神经网络中进行预测。 

int input_height = 80; 
int input_width = 3; 
int input_channel = 1; 
int input_exponent = -13; 
float acc_xyz[240] = {0}; 
int index_acc=0; 
#define I2C_MASTER_SCL_IO 16      /*!< gpio number for I2C master clock */ 
#define I2C_MASTER_SDA_IO 17      /*!< gpio number for I2C master data  */ 
#define I2C_MASTER_NUM I2C_NUM_0  /*!< I2C port number for master dev */ 
#define I2C_MASTER_FREQ_HZ 400000 /*!< I2C master clock frequency */ 
static i2c_bus_handle_t i2c_bus = NULL; 
static mpu6050_handle_t mpu6050 = NULL; extern "C" void app_main(void) 
{ i2c_config_t conf = { .mode = I2C_MODE_MASTER, .sda_io_num = I2C_MASTER_SDA_IO, .scl_io_num = I2C_MASTER_SCL_IO, .sda_pullup_en = GPIO_PULLUP_ENABLE, .scl_pullup_en = GPIO_PULLUP_ENABLE, .clk_flags = 0, }; conf.master.clk_speed = I2C_MASTER_FREQ_HZ; i2c_bus = i2c_bus_create(I2C_MASTER_NUM, &conf); mpu6050 = mpu6050_create(i2c_bus, MPU6050_I2C_ADDRESS); uint8_t mpu6050_deviceid; mpu6050_acce_value_t acce; mpu6050_get_deviceid(mpu6050, &mpu6050_deviceid); printf("mpu6050 device ID is: 0x%02x\n", mpu6050_deviceid); mpu6050_set_acce_fs(mpu6050, ACCE_FS_4G); 
while(1){ 
for (int i=0 ;i<80; i++) 
{ mpu6050_get_acce(mpu6050, &acce); acc_xyz[index_acc]=acce.acce_x; index_acc=index_acc+1; acc_xyz[index_acc]=acce.acce_y; index_acc=index_acc+1; acc_xyz[index_acc]=acce.acce_z; index_acc=index_acc+1; vTaskDelay(50 / portTICK_RATE_MS); 
} 
index_acc=0; 
int16_t *model_input = (int16_t *)dl::tool::malloc_aligned_prefer(input_height*input_width*input_channel, sizeof(int16_t *)); for(int i=0 ;i<input_height*input_width*input_channel; i++){ float normalized_input = acc_xyz[i] / 1.0; //normalization model_input[i] = (int16_t)DL_CLIP(normalized_input * (1 << -input_exponent), -32768, 32767); } 

3.3 设置输入形状

设置张量中的数据,输入到神经网络。 

Tensor<int16_t> input; input.set_element((int16_t *) model_input).set_exponent(input_exponent).set_shape({input_height,input_width,input_channel}).set_auto_free(false); 

3.4 调用模型

通过调用 forward 方法、传递输入调用模型。使用延迟来计算ESP32-S3 运行神经网络所需的时间。 

ACTIVITY model; dl::tool::Latency latency; latency.start(); model.forward(input); latency.end(); latency.print("\nActivity model", "forward");3. Future Directions 

3.5 监控输出

输出来自公共层,即 l6。结果可以在终端中打印出来。 

float *score = model.l6.get_output().get_element_ptr(); float max_score = score[0]; int max_index = 0; for (size_t i = 0; i < 6; i++) { printf("%f, ", score[i]*100); if (score[i] > max_score) { max_score = score[i]; max_index = i; } } printf("\n"); switch (max_index) { case 0: printf("0: Downstairs"); break; case 1: printf("1: Jogging"); break; case 2: printf("2: Sitting"); break; case 3: printf("3: Standing"); break; case 4: printf("4: Upstairs"); break; case 5: printf("5: Walking"); break; default: printf("No result"); } printf("\n"); 
} 
} 

4. 结论

总之,这个项目可以给各种应用带更多可能,比如在工业领域开展预测性维护,在运动领域使用加速度计识别拳击中的出拳,在医疗保健领域进行跌倒检测。这只是一部分可以进一步探索的领域。如果想查看源代码,可前往 GitHub 仓库。


http://www.ppmy.cn/news/292622.html

相关文章

c# 从零到精通-ArrayList-Hashtable的操作

c# 从零到精通-ArrayList-Hashtable的操作 1、ArrayList的操作 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Collections; namespace Test11 { class Program { static void Main(string[] args) { ArrayList list …

joplin的安装和使用

本文主要介绍joplin的安装和坚果云的配合使用 坚果云 先到坚果云的官网注册登录坚果云 官网&#xff1a;https://www.jianguoyun.com/ a、点击右上角昵称 b、点击账户信息 c、安全选项 d、第三方应用管理 e、点击添加应用&#xff0c;输入应用名称Joplin(这里可以自定义) f、…

华硕怎么安装linux系统教程,华硕笔记本系统如何安装win10和linux 双系统

稍微了整理了一下win10和linux双系统的安装教程,第一个选项是进入U盘linux live,等等) 第一点设置boot挂载点。 设置BIOS。 然后点击试用用UBUNTU,但是windows上面的数据又删除不得,使用Ultraiso把LINUX UBUNTU14.X这个iso文件【写入硬盘影像】到U盘。 第三个自己看,去正规…

华硕ac68u最佳设置_【华硕RT-AC68U路由器使用总结】频段|设置|信号_摘要频道_什么值得买...

华硕RT-AC68U路由器使用总结(频段|设置|信号) 假如不组建Mesh网络的话,使用第二台AC68U为第一台AC68U做无线桥接,同样可以实现这套211平米房间的信号全覆盖,但是信号经过第二台AC68U桥接之后速度会下降,而且无法实现统一的SSID和无缝漫游。例如第一台AC68U有2.4G和5G两个频…

联想开启物联网救赎

配图来自Canva可画 凭借Pro14、小新15、Air15等多款高性价比的电脑产品&#xff0c;联想在2020年以24%的市场份额&#xff0c;超过惠普、戴尔等厂商&#xff0c;再度蝉联PC市场桂冠。 不过&#xff0c;对联想而言&#xff0c;这样的成绩并不能令人满意。为缓解PC市场面临的压…

联想计算机BIOS开启Intel-vx,各品牌电脑BIOS开启Intel VT-X虚拟化技术方法(华硕,联想,HP,技嘉等)...

我们在电脑上运行虚拟机、模拟器是需要CPU虚拟化技术支持的,如果你的电脑没有开启,那么将无法运行虚拟机或模拟器,那么下面小编整理了华硕,联想,HP,技嘉等品牌电脑BIOS下开启Intel VT-X虚拟化技术方法,有需要的朋友可以学习一下哦。 华硕UEFI BIOS 华硕主板电脑,开机反复按…

华硕电脑连接不上wifi_四招解决华硕笔记本不能连接WiFi!

在笔记本电脑中&#xff0c;我们一般选择通过连接WiFi来上网&#xff0c;因为现在的几乎所有的笔记本都有内置无线网卡&#xff0c;具有支持连接无线网络的功能。如果笔记本电脑出现无法开启WiFi或者连接不了WiFi的情况&#xff0c;应该如何解决呢?下面分析几种可能的原因并为…

华硕电脑计算机名字联想,华硕与联想哪个电脑好?请详细阐述一下。

联想 U350-SSE(白皮革) 处理器型号 Intel 酷睿2双核SU7300 主板芯片组 Intel GS45 标配内存容量 2GB 内存类型 DDRIII 硬盘容量 250GB 显卡芯片 Intel GMA X4500 屏幕尺寸 13.3英寸 屏幕分辨率 1366768 背光技术 LED背光 笔记本重量 1600g 外形尺寸 32822817-24.9mm 外壳描述 蜥…