2024寒假练 - 基于CrowPanel ESP32 Display 4.3英寸HMI开发板实现手写识别
该项目使用了CrowPanel ESP32 Display 4.3英寸HMI开发板,实现了手写识别的设计,它的主要功能为:手写识别显示到屏幕。
标签
ESP32
lvgl
冲向天空的猪
更新2025-03-13
41

板卡介绍

Elecrow ESP32 4.3英寸显示屏是一款功能强大的HMI触摸屏,具有480*272分辨率的LCD显示屏。它使用ESP32-S3-WROOM-1-N4R2模组作为主控处理器,具有双核32位 LX7处理器,集成WiFi和蓝牙无线功能,主频高达240MHz,提供强大的性能和多功能的应用,适用于物联网应用设备等场景。

1.项目要求

  • 使用LVGL编程
  • 在LCD屏幕上设定一个正方形的写字区域
  • 在写字区域里书写0-9的数字
  • 对书写的数字进行识别,并将识别的数字传递给灯板,在灯板上进行显示(灯板信息

2.处理思路

使用LVGL创建我们的画布,在画布上手写数字,通过识别按钮实现通过神经网络换算发送到我们的屏幕和灯板上,显示出我们识别出来的数字。

3.流程图

4.实现步骤

4.1开发环境

我们选择板卡厂家提供的程序基于修改,PlatformIO上进行开发,在使用的过程中,需要下载厂商的json的板卡配置文件才能正常下载程序。

https://www.elecrow.com/wiki/4.3_inch_ESP32_Display_PlatformIO_Tutorial.html#get-started-with-platformio

4.2 选择训练数据

在手写识别上,网上有很多的训练模型数据,这里我选择的是
MNIST(modified national institute of standard and technology)数据集是由Yann LeCun及其同事于1994年创建一个大型手写数字数据库(包含0~9十个数字)。MNIST数据集的原始数据来源于美国国家标准和技术研究院(national institute of standard and technology)的两个数据集:special database 1和special database 3。它们分别由NIST的员工和美国高中生手写的0-9的数字组成。原始的这两个数据集由128×128像素的黑白图像组成。LeCun等人将其进行归一化和尺寸调整后得到的是28×28的灰度图像。

这里我们需要将画布中的图像最后整体压缩为28*28的图片数据上传到我们的神经网络进行识别。

这里我创建了一个python程序,可以将网上下载的minst程序,进行下载并生成神经网络模型,并转化为我们识别需要的二维数组。可以方便我们更快的完成训练模型的情况。
在第一次使用时需要下载对应的pip的包。

import tensorflow as tf
import numpy as np

# 加载MNIST数据集
mnist = tf.keras.datasets.mnist
(x_train, y_train), (x_test, y_test) = mnist.load_data()

# 数据预处理
x_train = x_train / 255.0
x_test = x_test / 255.0

# 创建模型
model = tf.keras.models.Sequential([
  tf.keras.layers.Flatten(input_shape=(28, 28)),
  tf.keras.layers.Dense(128, activation='relu'),
  tf.keras.layers.Dropout(0.2),
  tf.keras.layers.Dense(10, activation='softmax')
])

# 编译模型
model.compile(optimizer='adam',
            loss='sparse_categorical_crossentropy',
            metrics=['accuracy'])

# 训练模型
model.fit(x_train, y_train, epochs=5)

# 保存模型
model.save('mnist_model.h5')

# 转换为TFLite格式
converter = tf.lite.TFLiteConverter.from_keras_model(model)
converter.optimizations = [tf.lite.Optimize.DEFAULT]
converter.target_spec.supported_types = [tf.float32]
tflite_model = converter.convert()

# 保存TFLite模型
with open('model.tflite', 'wb') as f:
  f.write(tflite_model)

# 转换为C数组
def convert_to_c_array(data):
  c_array = []
  for byte in data:
      c_array.append(f"0x{byte:02x}")
  return ",".join(c_array)

c_array = convert_to_c_array(tflite_model)

# 生成头文件
with open('src/mnist_model.h', 'w') as f:
  f.write("#ifndef MNIST_MODEL_H\n")
  f.write("#define MNIST_MODEL_H\n\n")
  f.write("const unsigned char mnist_model[] = {")
  f.write(c_array)
  f.write("};\n\n")
  f.write("const unsigned int mnist_model_len = sizeof(mnist_model);\n\n")
  f.write("#endif")


4.3 TensorFlow_LiteESP32

TensorFlow 是一个端到端开源机器学习平台。它拥有一个包含各种工具、库和社区资源的全面灵活生态系统,可以让研究人员推动机器学习领域的先进技术的发展,并让开发者轻松地构建和部署由机器学习提供支持的应用。

TensorFlow Lite 是一组工具,可帮助开发人员在移动、嵌入式和 IoT 设备上部署/运行 TensorFlow 模型。 TensorFlow Lite 使设备上的机器学习推理具有低延迟的特性,并使可执行文件更小。
我们从GitHub下载下来版本作为模型部署平台,在ESP32中也能灵活的使用。

使用模型来推理我们的手写数字

int predict() {
  preprocess_canvas();
   
  TfLiteStatus invoke_status = interpreter->Invoke();
  if(invoke_status != kTfLiteOk) {
      return -1;
  }
   
  float max_prob = 0;
  int predicted_digit = -1;
   
  for(int i = 0; i < 10; i++) {
      if(output->data.f[i] > max_prob) {
          max_prob = output->data.f[i];
          predicted_digit = i;
      }
  }
   
  return predicted_digit;
}


4.4 LVGL画布按钮的实现

通过LVGL创建我们的按钮方便我们操作。

  // 添加清除按钮
lv_obj_t * clear_btn = lv_btn_create(lv_scr_act());
lv_obj_set_size(clear_btn, 80, 40);
lv_obj_align(clear_btn, LV_ALIGN_TOP_RIGHT, -10, 10);
lv_obj_add_event_cb(clear_btn, clear_btn_handler, LV_EVENT_CLICKED, NULL);
 
lv_obj_t * clear_label = lv_label_create(clear_btn);
lv_label_set_text(clear_label, "Clear"); // 清除
lv_obj_center(clear_label);
 
// 添加识别按钮
lv_obj_t * predict_btn = lv_btn_create(lv_scr_act());
lv_obj_set_size(predict_btn, 80, 40);
lv_obj_align(predict_btn, LV_ALIGN_TOP_RIGHT, -10, 60); // y坐标从110改为60
lv_obj_add_event_cb(predict_btn, predict_btn_handler, LV_EVENT_CLICKED, NULL);
 
lv_obj_t * predict_label = lv_label_create(predict_btn);
lv_label_set_text(predict_label, "Predict"); // 识别
lv_obj_center(predict_label);


4.5 灯板显示数字实现

通过板上的锁存器,提取字模实现我们的数字显示

#define DIN_PIN  37/* DIN 引脚 J1 阴极 */
#define CLOCK_PIN 17/* 时钟引脚 SRCLK*/
#define LATCH_PIN 18/* 锁存引脚 RCLK*/


const uint8_t datax[10] = { // 阳极数据
0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80
};
const uint8_t datay[11][8] = { // 阴极数据
{0x00, 0x3C, 0x24, 0x24, 0x24, 0x24, 0x3c, 0x00}, // 数字0
{0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00}, // 数字1
{0x00, 0x3C, 0x20, 0x20, 0x3C, 0x04, 0x04, 0x3C}, // 数字2
{0x00, 0x3C, 0x20, 0x20, 0x3C, 0x20, 0x20, 0x3C}, // 数字3
{0x00, 0x24, 0x24, 0x24, 0x3C, 0x20, 0x20, 0x20}, // 数字4
{0x00, 0x3C, 0x04, 0x04, 0x3C, 0x20, 0x20, 0x3C}, // 数字5
{0x00, 0x3C, 0x04, 0x04, 0x3C, 0x24, 0x24, 0x3C}, // 数字6
{0x00, 0x3C, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20}, // 数字7
{0x00, 0x3C, 0x24, 0x24, 0x3C, 0x24, 0x24, 0x3C}, // 数字8
{0x00, 0x3C, 0x24, 0x24, 0x3C, 0x20, 0x20, 0x3C} // 数字9
};
void LED_gpio_init(void) {
// 初始化GPIO引脚
pinMode(DIN_PIN, OUTPUT); // DIN 引脚设置为输出模式
pinMode(CLOCK_PIN, OUTPUT); // 时钟引脚设置为输出模式
pinMode(LATCH_PIN, OUTPUT); // 锁存引脚设置为输出模式
}
void sendData(uint8_t data, uint8_t gpio) {
for (int i = 0; i < 8; i++) {
    // 发送每一位
    if ((data & (0x80 >> i)) != 0) {
        // 设置gpio高
        digitalWrite(gpio, HIGH);
    } else {
        // 设置gpio低
        digitalWrite(gpio, LOW);
    }

    // 脉冲时钟引脚
    digitalWrite(CLOCK_PIN, HIGH);
    digitalWrite(CLOCK_PIN, LOW);
}
}
void latchData() {
// 锁存数据到输出引脚
digitalWrite(LATCH_PIN, HIGH);
digitalWrite(LATCH_PIN, LOW);
}
void displayNumber(uint8_t number, uint8_t frame) {
if (number > 9) {
    return; // 不支持的数字
}

sendData(datax[frame], DIN_PIN); // 发送阳极数据
sendData(datay[number][frame], DIN_PIN); // 发送阴极数据

// 锁存数据以更新输出
latchData();
}
void LED_displayNumber(uint8_t number) {
static uint8_t frame = 0;
 
displayNumber(number, frame); // 显示当前帧
 
frame = (frame + 1) & 0x07; // 循环0-7
}


效果展示

数字识别结果

灯板数字显示

总结

这次活动让我第一次接触到神经网络,开始尝试于训练模型,如何分析思路,将我们的模型在内存运行空间小的芯片去运行,在此中也遇到许多内存崩溃的情况,也在不断调整整个内存结果,将模型能够成功在ESP32中执行,这次的实现也让自己对神经网络也有一定认识,也希望接下来也能将其他的模型也能成功部署到ESP32中,也让自己尝试其他的模型识别。

项目连接:

链接: https://pan.baidu.com/s/1ry-iXkd364eGrXehZhGhVw?pwd=w37n 提取码: w37n

团队介绍
热爱电子学生
评论
0 / 100
查看更多
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号