2024年寒假练 - 基于Seeed XIAO ESP32S3 Sense制作识别+局域网摄像头
该项目使用了Seeed XIAO ESP32S3 Sense,实现了识别+局域网摄像头的设计,它的主要功能为:查看本机网络信息,运行训练出的识别模型,局域网监控摄像头,手动重置本机网络。 该项目使用了oled12864,实现了用户交互界面的设计,它的主要功能为:输出系统界面。 该项目使用了微动按键,实现了交互按键的设计,它的主要功能为:给用户提供交互功能。
标签
嵌入式系统
测试
数字逻辑
显示
xfp23
更新2024-04-02
重庆电力高等专科学校
564

hackster.io

项目名称:智能物体检测与监控系统

项目介绍:
智能物体检测与监控系统是基于ESP32开发板和Edge Impulse SDK开发的一款智能监控设备。该系统具有实时物体检测、网络连接、时间同步、OLED显示、按键控制等功能。用户可以通过该系统监控特定区域内的物体,并实时获取检测结果。


本次识别的物体是橙子和一个星星玩具。首先利用Seeed XIAO ESP32S3 Sense对橙子和星星拍照采样,拍了大概100张左右的照片,然后把这些图片上传至edge impulse 平台进行模型训练,之后将导出的Arduino项目代码上传至开发板,与自己的代码整合后烧录进开发板


主要功能和特点:

  1. 物体检测:利用Edge Impulse SDK进行实时物体检测,通过相机捕获图像并用Edge Impulse训练的模型进行处理,最终在OLED显示屏上显示检测结果。
  2. 网络连接:通过WiFi连接到无线网络,实现远程监控和数据传输。
  3. 时间同步:利用NTPClient库同步设备时间,确保系统具有准确的时间信息。
  4. 显示功能:通过OLED显示屏显示网络连接状态、电池状态、菜单选项等信息,提供用户友好的操作界面。
  5. 按键控制:通过按键控制菜单的导航和选择,实现用户与系统的交互。
  6. 摄像头服务器:启动摄像头服务器,允许用户通过网络流式传输摄像头的视频,实现远程监控功能。
  7. 其他功能:包括电池状态显示、信号强度显示等。

项目背景:
随着物联网技术的发展,智能监控设备在家庭安全、工业生产等领域得到了广泛应用。本项目旨在利用低成本的硬件平台和开源的软件工具,构建一款功能丰富、易于部署的智能监控系统,满足用户对安全监控的需求。

未来展望:
在未来,可以进一步优化系统的性能和稳定性,增加更多的监控功能和智能算法,如行人识别、车辆识别等,提高系统的智能化水平。同时,可以考虑将系统与云平台集成,实现远程管理和数据存储,拓展其应用场景和商业化价值。

image.png

简单的硬件介绍:

Seeed XIAO ESP32S3 Sense开发板:作为系统的主控制器,负责控制各个外设的操作和数据传输。

Seeed Studio XIAO ESP32S3 Sense 是一款功能强大、易于使用的开发板,专为智能语音和视觉 AI 应用而设计。它集成了摄像头传感器、数字麦克风和 SD 卡支持,并具有强大的嵌入式 ML 计算能力。

主要特点:

  • 搭载 Espressif ESP32-S3R8 芯片,支持 2.4GHz Wi-Fi 和低功耗蓝牙 BLE 5.0 双模
  • 内置 8MB PSRAM 和 8MB Flash
  • 拥有 1600 x 1200 分辨率的 OV2640 摄像头传感器
  • 支持数字麦克风阵列
  • 支持 microSD 卡扩展
  • 内置锂电池充电管理功能

应用领域:

  • 智能家居
  • 可穿戴设备
  • 工业控制
  • 环境监测
  • 机器视觉

示例项目:

  • 智能家居摄像头
  • 语音控制机器人
  • 人脸识别门禁系统
  • 环境监测系统
  • 机器学习图像分类

优势:

  • 小巧玲珑,尺寸仅有拇指大小
  • 功能强大,支持多种功能
  • 易于使用,支持 Arduino 和 MicroPython 开发环境

总结:

Seeed Studio XIAO ESP32S3 Sense 是一款功能强大、易于使用的开发板,是您开始学习智能语音和视觉 AI 的绝佳工具


OLED12864 是一种常见的单色OLED显示屏,具有以下特点:

  • 分辨率为128 x 64像素
  • 显示效果清晰
  • 功耗低
  • 体积小巧

OLED12864由一个OLED显示屏和一个驱动芯片组成。OLED显示屏由像素矩阵组成,每个像素由一个有机发光二极管组成。驱动芯片负责控制OLED显示屏的显示内容。

OLED12864可以显示文本、图形和图像。它可以用于各种应用,包括:

  • 电子设备的显示屏
  • 仪表仪表的显示屏
  • 工业控制设备的显示屏


OLED12864的接口类型包括:

I2C

SPI

8080并行接口

微动按键:

造型小巧,适合用于交互设计的场景充当按键


实现功能:

开机界面:

1.png


获取实时时间:

2.png


主功能菜单:

3.png


查看实时网络信息:


4.png

物体识别界面:

5.png


局域网摄像头功能:

6.png

7.png


重置本机网络界面:

8.png


主要代码说明:

这里做了一个状态机

利用中断里改变全局变量enter的值来改变要在屏幕上输出的内容,是哪个界面。

 switch (enter) {
    case 0:
      if (clc == 1) {
        display.clearDisplay();
        display_signal();
        display_battery();
        String currentTime = timeClient.getFormattedTime();
        currentTime.remove(5, 3);
        display.setTextSize(2);
        display.setCursor(30, 30);
        display.print(currentTime);
        display.display();
        clc = false;
      }
      //使用更新时间
      if (timeClient.update()) {
        display.clearDisplay();
        display_signal();
        display_battery();
        displayout("Online", 46, 4);
        String currentTime = timeClient.getFormattedTime();
        currentTime.remove(5, 3);
        if (currentTime != previousTime) {
          display.clearDisplay();
          display.setTextSize(2);
          display.setTextColor(SSD1306_WHITE);
          display.setCursor(30, 30);
          display.print(currentTime);
          display_signal();
          displayout("Online", 46, 4);
          display_battery();
          display.display();
          previousTime = currentTime;
        }
      }
      break;
    case 1:
    //stopCameraServer();
    WiFi.setSleep(true);
     display.clearDisplay();
     //display.display();
     displayout("Menu", 46, 4);
     display_signal();
     display_battery();
      interface_menu(&menu_1);
      select_icon(x);
      display.display();
      break;
    case 2:
      display_signal();
      display_battery();
      //select_icon(x);
      display.display();

下面利用全局变量x来筛选选择了哪一个功能

      switch (x) {
        case 13:
          display.clearDisplay();
          //display.display();
          displayout("Network", 46, 4);
          display_signal();
          display_battery();
          interface_menu(&menu_1_1);
          Internet();
          display.display();
          break;
        case 23:
    display.clearDisplay();
    display_signal();
    display_battery();
    interface_menu(&menu_1_2);
    display.display();

// 检查是否需要进行检测
if (Dection_status) {
   if (ei_sleep(5) != EI_IMPULSE_OK) {
        return;
    }

    snapshot_buf = (uint8_t*)malloc(EI_CAMERA_RAW_FRAME_BUFFER_COLS * EI_CAMERA_RAW_FRAME_BUFFER_ROWS * EI_CAMERA_FRAME_BYTE_SIZE);

    // check if allocation was successful
    if(snapshot_buf == nullptr) {
       menu_1_2.fourth_text="buffer error";
        return;
    }

    ei::signal_t signal;
    signal.total_length = EI_CLASSIFIER_INPUT_WIDTH * EI_CLASSIFIER_INPUT_HEIGHT;
    signal.get_data = &ei_camera_get_data;

    if (ei_camera_capture((size_t)EI_CLASSIFIER_INPUT_WIDTH, (size_t)EI_CLASSIFIER_INPUT_HEIGHT, snapshot_buf) == false) {
        free(snapshot_buf);
        return;
    }

    // Run the classifier
    ei_impulse_result_t result = { 0 };

    EI_IMPULSE_ERROR err = run_classifier(&signal, &result, debug_nn);
    if (err != EI_IMPULSE_OK) {
        return;
    }

#if EI_CLASSIFIER_OBJECT_DETECTION == 1
    bool bb_found = result.bounding_boxes[0].value > 0;
    for (size_t ix = 0; ix < result.bounding_boxes_count; ix++) {
        auto bb = result.bounding_boxes[ix];
        if (bb.value == 0) {
            continue;
        }
        // Display only the label on the screen
        displayout_s(bb.label, 10, 10); // Adjust position as needed
    }
    if (!bb_found) {
        menu_1_2.fourth_text="no object";
       
    }
#else
    for (size_t ix = 0; ix < EI_CLASSIFIER_LABEL_COUNT; ix++) {
        // Display only the label on the screen
        displayout_s(result.classification[ix].label, 10, 10); // Adjust position as needed
        menu_1_2.fourth.text=result.classification[ix].label;
    }
#endif

    free(snapshot_buf);
    display.display();
}
    break;
        case 33:
          display.clearDisplay();
          //display.display();
          display_signal();
          display_battery();
          interface_menu(&menu_1_3);
          display.display();
            WiFi.setSleep(false);
            startCameraServer();
          while(Web_status)
          {
            delay(10000);
          }
          break;
        case 43:
          display.clearDisplay();
          //display.display();
          display_signal();
          display_battery();
          interface_menu(&menu_1_4);
          display.display();
          WiFi.begin(ssid, password);
          //Get_net(&ESP32_NET);
          break;
      }

这里利用状态机筛选功能把Edge impulse 的训练出来的模型用于物体识别

        case 23:
    display.clearDisplay();
    display_signal();
    display_battery();
    interface_menu(&menu_1_2);
    display.display();

// 检查是否需要进行检测
if (Dection_status) {
   if (ei_sleep(5) != EI_IMPULSE_OK) {
        return;
    }

    snapshot_buf = (uint8_t*)malloc(EI_CAMERA_RAW_FRAME_BUFFER_COLS * EI_CAMERA_RAW_FRAME_BUFFER_ROWS * EI_CAMERA_FRAME_BYTE_SIZE);

    // check if allocation was successful
    if(snapshot_buf == nullptr) {
       menu_1_2.fourth_text="buffer error";
        return;
    }

    ei::signal_t signal;
    signal.total_length = EI_CLASSIFIER_INPUT_WIDTH * EI_CLASSIFIER_INPUT_HEIGHT;
    signal.get_data = &ei_camera_get_data;

    if (ei_camera_capture((size_t)EI_CLASSIFIER_INPUT_WIDTH, (size_t)EI_CLASSIFIER_INPUT_HEIGHT, snapshot_buf) == false) {
        free(snapshot_buf);
        return;
    }

    // Run the classifier
    ei_impulse_result_t result = { 0 };

    EI_IMPULSE_ERROR err = run_classifier(&signal, &result, debug_nn);
    if (err != EI_IMPULSE_OK) {
        return;
    }

#if EI_CLASSIFIER_OBJECT_DETECTION == 1
    bool bb_found = result.bounding_boxes[0].value > 0;
    for (size_t ix = 0; ix < result.bounding_boxes_count; ix++) {
        auto bb = result.bounding_boxes[ix];
        if (bb.value == 0) {
            continue;
        }
        // Display only the label on the screen
        displayout_s(bb.label, 10, 10); // Adjust position as needed
    }
    if (!bb_found) {
        menu_1_2.fourth_text="no object";
       
    }
#else
    for (size_t ix = 0; ix < EI_CLASSIFIER_LABEL_COUNT; ix++) {
        // Display only the label on the screen
        displayout_s(result.classification[ix].label, 10, 10); // Adjust position as needed
        menu_1_2.fourth.text=result.classification[ix].label;
    }
#endif

    free(snapshot_buf);
    display.display();
}
    break;

这里的结构体声明并定义了每个界面输出的内容

/* 输出函数 */
extern void displayout(char *pt, int x, int y);
extern void displayout_s(const String pt, int x, int y);

extern const char *ssid;
extern const char *password;

/* 定义屏幕显示内容结构体 */
struct interface {
    unsigned int first_text_x;
    unsigned int first_text_y;
    char *first_text;
    unsigned int second_text_x;
    unsigned int second_text_y;
    char *second_text;
    unsigned int third_text_x;
    unsigned int third_text_y;
    char *third_text;
    unsigned int fourth_text_x;
    unsigned int fourth_text_y;
    char *fourth_text;
};

/* 定义显示菜单的函数 */
void interface_menu(struct interface *menu) {
    displayout(menu->first_text, menu->first_text_x, menu->first_text_y);
    displayout(menu->second_text, menu->second_text_x, menu->second_text_y);
    displayout(menu->third_text, menu->third_text_x, menu->third_text_y);
    displayout(menu->fourth_text, menu->fourth_text_x, menu->fourth_text_y);
}

/*输出网络信息*/
void Internet() {
    displayout_s(WiFi.localIP().toString().c_str(), 50, 17);
    displayout_s(WiFi.dnsIP().toString().c_str(), 50, 27);
    displayout_s(WiFi.gatewayIP().toString().c_str(), 50, 37);
    displayout_s("xfp23", 50, 47);
}


/* 定义菜单项结构体 */
struct interface menu_1 = {
    .first_text_x = TEXT_X,
    .first_text_y = 17,
    .first_text = "NetWork",
    .second_text_x = TEXT_X,
    .second_text_y = 27,
    .second_text = "Face dection",
    .third_text_x = TEXT_X,
    .third_text_y = 37,
    .third_text = "LAN camera",
    .fourth_text_x = TEXT_X,
    .fourth_text_y = 47,
    .fourth_text = "Restart network",
};

struct interface menu_1_1 = {
    .first_text_x = 5,
    .first_text_y = 17,
    .first_text = "IP: ",
    .second_text_x = 5,
    .second_text_y = 27,
    .second_text = "Gateway: ",
    .third_text_x = 5,
    .third_text_y = 37,
    .third_text = "DNS: ",
    .fourth_text_x = 5,
    .fourth_text_y = 47,
    .fourth_text = "SSID: ",
};

struct interface menu_1_2 = {
    .first_text_x = 46,
    .first_text_y = 4,
    .first_text = "Dection",
    .second_text_x = TEXT_X,
    .second_text_y = 17,
    .second_text = "Object dection",
    .third_text_x = TEXT_X,
    .third_text_y = 27,
    .third_text = "Dection resultS:",
    .fourth_text_x = TEXT_X,
    .fourth_text_y = 47,
    .fourth_text = " "
};

struct interface menu_1_3 = {
    .first_text_x = 46,
    .first_text_y = 4,
    .first_text = "IPC",
    .second_text_x = TEXT_X,
    .second_text_y = 17,
    .second_text = "Enter the IP address view",
    .third_text_x = TEXT_X,
    .third_text_y = 27,
    .third_text = " ",
    .fourth_text_x = TEXT_X,
    .fourth_text_y = 47,
    .fourth_text = " "
};

struct interface menu_1_4 = {
    .first_text_x = 46,
    .first_text_y = 4,
    .first_text = "REBOOT",
    .second_text_x = TEXT_X,
    .second_text_y = 17,
    .second_text = "Network reconnecting   ......",
    .third_text_x = TEXT_X,
    .third_text_y = 27,
    .third_text = " ",
    .fourth_text_x = TEXT_X,
    .fourth_text_y = 47,
    .fourth_text = " "
};


按键的消抖:

const unsigned int JITTERTIME=200;//消抖时间
volatile unsigned long int Enter_key_LASTTIME=0;
volatile unsigned long int Return_key_LASTTIME=0;
volatile unsigned long int Menu_selection_key_LASTTIME=0;
volatile unsigned long int Current_time=0;
Current_time=millis();
  if(Current_time-Enter_key_LASTTIME>=JITTERTIME)
  {
    Enter_key_LASTTIME=Current_time;
      if (enter >= 0 && enter <= 2) {
    enter += 1;
  }
Current_time=millis();
  if(Current_time-Return_key_LASTTIME>=JITTERTIME){
    Return_key_LASTTIME=Current_time;
 Current_time=millis();
  if(Current_time-Menu_selection_key_LASTTIME>=JITTERTIME){
    Menu_selection_key_LASTTIME=Current_time;

这里是返回键的服务函数:

extern volatile unsigned short int enter;
extern volatile unsigned short int  x;
extern volatile unsigned long int Return_key_LASTTIME;
extern const unsigned int JITTERTIME;
extern volatile unsigned long int Current_time;
extern volatile bool Web_status;
extern volatile bool Dection_status;
extern bool clc;
extern void return_down() {
  Current_time=millis();
  if(Current_time-Return_key_LASTTIME>=JITTERTIME){
    Return_key_LASTTIME=Current_time;
    if (enter > 0 && enter <= 2) {
    enter -= 1;
  } else {
    enter = 0;
    clc=true;
    x = 13;
  }
  if(x==33&&Web_status==true)
  {
    Web_status=false;
    }
   if(x==22&&Dection_status==true)
    {
      Dection_status=false;
      }
  }
 
}

这里是确认键的服务函数:

extern volatile unsigned short int enter;
extern volatile unsigned short int x;
extern volatile unsigned long int Enter_key_LASTTIME;
extern volatile unsigned long int Current_time;
extern const unsigned int JITTERTIME;
extern volatile bool Dection_status;
/*确认键设置*/
extern void enter_down() {
  Current_time=millis();
  if(Current_time-Enter_key_LASTTIME>=JITTERTIME)
  {
    Enter_key_LASTTIME=Current_time;
      if (enter >= 0 && enter <= 2) {
    enter += 1;
  }
   if(x==22&&Dection_status==false)
   {
    Dection_status=true;
    }
  }
}

这里是选择键的服务函数:

extern void displayout(char *pt,int x,int y);
/*绘制选择图标*/
extern void select_icon(int cur) {
  int sur = cur + 5;
  int a = cur, b = sur;
  for (int x = 0; x < 5; x++) {
    for (int y = cur; y < sur; y++) {
      if (cur <= (a + b) / 2 && sur >= (a + b) / 2) {
        displayout(".", x, y);
      }
    }
    sur -= 1;
    cur += 1;
  }
}

这里是等待连接WiFi的函数:

#include <WiFi.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

extern Adafruit_SSD1306 display;

extern void display_signal();
extern void display_battery();
extern void displayout(char *pt, int x, int y);

extern void wait_to_wifi() {
  static unsigned int point_x = 20, point_y = 30; // 声明为静态变量
  display_battery();
  display_signal();
  while (WiFi.status() != WL_CONNECTED) {
    display_battery();
    displayout("Connecting to WiFi", 11, 20);
    point_x += 5;
    displayout(".", point_x, point_y);
    display.display();
    delay(500);
    if(point_x>=60)
    {
      display.clearDisplay();
      display.display();
      point_x=20;
      }
  }
}

遇到的问题和解决方法:

1.按键消抖问题:在项目进行的过程中,由于不能在中断函数里不能出现返回值和使用延时,思考了很久怎么消抖的问题。

解决方法:利用了单片机的定时器消抖。

2.物体识别率过低问题:在edge impulse平台训练的AI模型识别率过低

解决方法:重新利用开发板拍照,在纯白的背景下拍照。

3.单片机运行问题:在项目进行过程中,把编译成功的程序烧录进开发板,开发板无法正常运行

解决方法:重构了代码,修复了部分函数返回空指针的bug,开启了ardiuno 的OPI PSRAM

未来的计划:

未来计划是进一步优化系统性能和稳定性,增加更多的监控功能和智能算法,如行人识别、车辆识别等,提高系统的智能化水平。同时,可以考虑将系统与云平台集成,实现远程管理和数据存储,拓展其应用场景和商业化价值。建议对系统的用户界面进行优化,提供更友好、直观的操作界面,提升用户体验。


附件下载
esp32_camera.zip
代码文件数量大于10,故上传压缩包
团队介绍
团队热情积极,热爱嵌入式
团队成员
彭小风
热爱嵌入式
评论
0 / 100
查看更多
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号