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

news2024/11/26 19:52:36

边缘计算是一种分布式计算范例,指在更靠近设备的地方进行数据存储和计算。边缘人工智能(边缘 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.coloradmin.cn/o/627333.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

通信算法之167: (低空无人机)机载视频通信传输系统基带算法设计

一.物理层基带仿真 通信系统的链路级仿真主要可以分成5个部分。 1.系统参数 2.发送机算法 3.信道模型 4.接收机算法 5.统计性能 其中主要组成部分很明显是中间三部分&#xff0c;即发送&#xff0c;信道&#xff0c;接收。但系统参数和统计性能这两部分的适当设计会大大…

linux基础命令系列之10 分钟掌握 ln 命令:创建链接,软链接,硬链接,递归链接,打印详细输出

文章目录 前言一. ln命令介绍二. 语法格式及常用选项三. 参考案例3.1 ln命令创建硬链接3.1.1 创建硬链接3.1.2 源文件被删除&#xff0c;不影响链接文件的正常使用3.1.3 硬链接不能跨分区创建 3.2 为什么目录刚刚创建的时候&#xff0c;链接数为23.3 ln -s 软链接的创建3.3.1 l…

【漏洞修复】node-exporter被检测出来pprof调试信息泄露漏洞

node-exporter被检测出来pprof调试信息泄露漏洞 说在前面解决方法结语 说在前面 惯例开篇吐槽&#xff0c;有些二五仔习惯搞点自研的安全扫描工具&#xff0c;然后加点DIY元素&#xff0c;他也不管扫的准不准&#xff0c;就要给你报个高中危的漏洞&#xff0c;然后就要去修复&…

C++元模板技术与traits解析:根据类型的特性来调整代码的行为,解决没有重载运算符的情况

C元模板技术与traits解析 第一章、C元模板技术简介 (C Meta-template Introduction)1.1 元模板的定义与概念 (Definition and Concepts)1.2 元模板技术的发展历程 (Evolution of Meta-templates)1.3 元模板应用场景举例 (Examples of Meta-template Applications) 第二章、 tra…

[数据结构初阶]顺序表

目录 静态顺序表 动态顺序表 初始化 销毁 尾插 ​编辑 尾删 头插 头删 Insert erase find查找 顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构&#xff0c;一般情况下采用数组存储。在数组上完成数据的增删查改。 静态顺序表 定义结构体&#xff1…

Talk | 北卡罗来纳州立大学唐圣坤浙江大学张磊: 数据为中心的高效视觉语言学习—动态退出与数据蒸馏

本期为TechBeat人工智能社区第504期线上Talk&#xff01; 北京时间6月8日(周四)20:00&#xff0c;北卡罗来纳州立大学在读博士生—唐圣坤与浙江大学硕士生—张磊的Talk将准时在TechBeat人工智能社区开播&#xff01; 他们与大家分享的主题是: “数据为中心的高效视觉语言学习…

基于jsp+mysql+mybatis的SpringBoot美容院后台管理系统

运行环境: 最好是java jdk 1.8&#xff0c;我在这个平台上运行的。其他版本理论上也可以。 IDE环境&#xff1a; Eclipse,Myeclipse,IDEA或者Spring Tool Suite都可以&#xff0c;如果编译器的版本太低&#xff0c;需要升级下编译器&#xff0c;不要弄太低的版本 tomcat服务器环…

【嵌入式环境下linux内核及驱动学习笔记-(15)linux总线、设备、驱动模型之I2C总线】

目录 1、 I2C总线机制1.1 导入1.2 时序1.3 地址格式 2、华清fs4412上I2C的实现2.1 寄存器2.2 寄存器位具体含义2.3 fs4412上针对具本设备的I2C工作逻辑2.3.1 主机读写工作流程**2.3.1.1 主机发送时序及操作流程2.3.1.2 主机接收的时序及流程 2.3.2 从机读写工作流程 3、LINUX内…

Redis-认识NoSQl和Redis常见的通用命令

1. 认识NoSQL 非关系型数据库 NoSQL是指一类非关系型数据库&#xff0c;它们采用的数据模型不同于传统的关系模型&#xff0c;它通常使用键值对、文档、图形等非传统的数据结构进行数据存储&#xff0c;不遵循预定义的模式和模型。NoSQL数据库通常分布式、高可扩展性&#xff0…

【项目一】GCC(gcc,g++)、静态库、动态库、MakeFile、GDB调试

GCC、静态库 1.2 GCC(1&#xff09;gcc&#xff08;1&#xff09;常用命令&#xff08;2&#xff09; C程序编译过程&#xff08;3&#xff09;GCC工作流程 1.3 GCC(2&#xff09;g1.3静态库的制作1.5静态库的使用1.6动态库的制作1.7动态库加载失败的原因1.8解决动态库加载失败…

秋招必看,Java后端高频面试题1000题、拒绝简单背诵,深入浅出近30个技术栈

Java 面试随着时间的改变而改变。在过去的日子里&#xff0c;当你知道 String 和 StringBuilder 的区别就能让你直接进入第二轮面试&#xff0c;但是现在问题变得越来越高级&#xff0c;面试官问的问题也更深入。 在我初入职场的时候&#xff0c;类似于 Vector 与 Array 的区别…

面试专题:计算机网络常见面试点总结

socket、tcp、udp、http 的认识及区别 socket、tcp、udp、http 的认识及区别​ 一、先来一个讲TCP、UDP和HTTP关系的 1、TCP/IP是个协议组&#xff0c;可分为三个层次&#xff1a;网络层、传输层和应用层。 在网络层有IP协议、ICMP协议、ARP协议、RARP协议和BOOTP协议。 在传…

10分钟让你彻底了解Loadrunner性能测试工具

目录 Loadrunner简介 Loadrunner原理 Loadrunner工具组件 1、VUGen&#xff08;虚拟用户生成器&#xff09; 2、Controller&#xff08;控制器&#xff09; 3、Load Generator&#xff08;负载生成器&#xff09; 4、Analysis分析器 性能测试工具&#xff0c;从广义上讲…

Shell脚本攻略:Linux防火墙

目录 一、理论 1.安全技术 2.防火墙 3.通信五元素和四元素 二、实验 1.iptables基本操作 2.扩展匹配 一、理论 1.安全技术 &#xff08;1&#xff09;安全技术 ①入侵检测系统&#xff08;Intrusion Detection Systems&#xff09;&#xff1a;特点是不阻断任何网络访…

游戏外包开发技术难点分析

游戏开发涉及多个领域的技术&#xff0c;因此在开发过程中可能会遇到很多技术难点。今天和大家分享一些常见的游戏开发技术难点&#xff0c;希望对大家开发游戏有一定帮助。北京木奇移动技术有限公司&#xff0c;专业的软件外包开发公司&#xff0c;欢迎交流合作。 1. 图形渲染…

「料见」vol25.回顾 | PKU-Beaver开源项目团队:一起来聊首个可复现的RLHF基准

为了解决复现RLHF技术和基于RLHF技术的大预言模型的不安全问题&#xff0c;北京大学团队开源了名为PKU-Beaver&#xff08;海狸&#xff09;开源项目。 第25期料见闭门分享会&#xff0c;我“门”非常开心邀请到PKU-Beaver开源项目团队成员——北京大学人工智能研究院助理教授…

欧美同学会第三届“双创”大赛——空天装备产业赛区(浙江诸暨)正式启动,开启报名通道

6月8日&#xff0c;欧美同学会第三届“双创”大赛——空天装备产业赛区&#xff08;浙江诸暨&#xff09;启动仪式暨北京推介会圆满举行。活动由欧美同学会&#xff08;中国留学人员联谊会&#xff09;主办&#xff0c;中共浙江省委统战部支持&#xff0c;浙江省欧美同学会、中…

国内比较火的报表工具测评——Smartbi电子表格软件和Finereport

最近在学习BI软件&#xff0c;因为最近工作中需要开发报表&#xff0c;因此选用了国内市场比较热门的报表工具——Finereport和Spreadsheet进行学习。 BI软件经常会定期发布新的版本&#xff0c;增加新的功能模块&#xff0c;或者对现有功能进行增强&#xff0c;提升运行效率。…

解决pip install -r requirements.txt 超时

解决方案&#xff1a; pip install -r requirements.txt -i http://pypi.douban.com/simple/ --trusted-host pypi.douban.com测试验证

让你的AndroidUI更亮眼:Jetpack Compose中的可视状态

让你的Android UI更亮眼&#xff1a;Jetpack Compose中的可视状态 任何设计系统的重要责任是清晰地表明哪些组件可以与之交互&#xff0c;哪些不行&#xff0c;并让用户知道交互已发生。本博客文章将解释如何监听Jetpack Compose中的用户交互&#xff0c;并创建可重用的视觉指…