|
以下内容【何宾老师及其团队】将整理发布在待发售新书中,敬请期待。
手写识别计算器 · 实现流程
Ai8051U 单片机端侧 AI · 触摸采集 → 字符识别 → 表达式计算(第17章)
1. 项目概览
| 硬件平台 | 显示屏 | 触摸屏 | 推理框架 | 单字符推理 | 模型规模 | | Ai8051U 开发板 | ILI9341 TFT-LCD | XPT2046 电阻触摸(SPI) | TinyMaix + INT8 量化 CNN | 约 58ms | 参数 1954 / MAC 31484 |
数据流:手写轨迹采集 → 降采样 28×28 二值画布 → 垂直投影字符切分 → INT8 CNN 识别 → 表达式拼接/求值 → 局部刷新显示
2. 模型训练(PC 端)
2.1 网络结构:输入 28×28 灰度图;Block1:5×5 卷积 stride=2(用大步长卷积替代池化层降采样)→ BatchNorm → ReLU → Dropout;Block2:更大步长进一步压缩;Flatten → Dense → Softmax。轻量化设计以适配 8051 资源。
2.2 数据集:数字用 QMNIST,运算符用额外手写符号集并统一灰度反转使分布一致。训练后准确率 0.95;再用设备端真实书写样本微调(冻结特征提取层、仅训练分类层)解决数据分布偏移。
2.3 FP32 → INT8 量化效果:
| 模型格式 | 存储容量 | 测试准确率 | | FP32 原始模型 | 76.55 KB | 99.97% | | INT8 量化模型 | 7.09 KB | 98.81% |
2.4 环境与运行:安装 Miniconda3(清华镜像)→ 配置国内镜像 → 创建虚拟环境并安装依赖:
- conda create --name ai8051u python=3.9
- conda activate ai8051u
- cd \ai8051u\_example\example_17_1\Ai-Arithmetic-master\train
- pip install -r requirements.txt
复制代码
VS Code 安装 Python / C++ / Jupyter 插件并激活 Profile ai8051u;打开 good-mnist.ipynb → 选择内核 ai8051u(Python 3.9.25) → 点 Run All(约 20 分钟 ~ 1 小时),完成数据集构建 → 训练 → 微调 → 量化 → 导出,模型输出到 mnist_fine_tuning。
3. 单片机工程部署(Keil μVision C251)
打开工程 \ai8051u\_example\example_17_1\lcd_display\lcd_pro.uvproj,模块划分:
| 文件 | 功能 | | lcd.c | ILI9341 显示驱动(像素/区域填充/字符显示/局部刷新) | | touch.c | XPT2046 触摸采样与校准(软件模拟 SPI) | | canvas.c | 画布管理、字符切分与归一化 | | ai.c | AI 推理接口(封装 TinyMaix) | | tm_layers.c | TinyMaix 算子实现(卷积/全连接/Softmax 等) | | tm_model.c | 模型加载与输入预处理 |
3.1 触摸采集:软件模拟 SPI 时序,命令 0xD0 读 X、0x90 读 Y(12 位 ADC);单次坐标做 15 次采样 → 排序去首尾 → 中间均值(截断均值滤波);双重校验两轮结果在误差范围内才采信;首次运行四角标定,线性映射 x_lcd = (x_ad − x_offset) × 1000 / v_x,横竖屏另做轴交换/镜像。
3.2 数据预处理:触摸坐标按比例映射到 28×28 二值画布(相邻点重叠天然加粗、抗噪);垂直投影法按"列投影 0→非0 进入字符、非0→0 结束字符"切分连续笔迹;取最小外接矩形缩放居中。核心函数 canvas_process_character():
- // 判断干扰点:宽度 ≤3 列且有效像素 < 10 则跳过
- if (end_col - start_col <= 3) {
- if (count < 10) return false;
- }
- // 水平居中偏移
- hor_offset = (CHAR_IMG_SIZE - (end_col - start_col + 1)) / 2;
- // 求有效行范围(top_row/bottom_row),垂直居中偏移
- ver_offset = (CHAR_IMG_SIZE - effective_height) / 2;
- // 字符区域映射进 28×28 的 input_image
复制代码
3.3 模型推理:INT8 量化模型以 C 数组嵌入 Flash,推理全程整数域运算(缩放因子+零点映射),仅输出层反量化,预分配缓冲区逐层前向,入口 tm_run():
- uint8_t Ai_run() {
- tm_preprocess(&mdl, TMPP_UINT2INT, &in_uint8, &in);
- tm_run(&mdl, &in, outs);
- return parse_output(outs); // argmax → 字符标签
- }
复制代码
3.4 业务逻辑:识别结果按书写顺序拼接(过滤"连续运算符/以运算符开头");逐字符顺序扫描 + 静态缓冲区状态机求值(无递归、无动态内存),支持 + − × ÷;结果区局部刷新避免整屏重绘,新串短于旧串时尾部补空白覆盖残留,串口发送前检测链路避免阻塞。
4. 编译 → 下载 → 验证
4.1 安装 Keil 中断扩展插件:C251 编译器不支持中断号 >31,先到 https://www.stcai.com/gjrj 下载"Keil 中断拓展插件",指定 Keil 安装目录执行,提示 C51.exe / C251.exe 修改成功。
- Keil:Project → Open Project 打开 lcd_pro.uvproj → Project → Build Target 编译
- AiCube-ISP-v6.96V-plus:芯片选 AI8051U-34K64,打开 lcd_pro.hex,IRC 频率选 40.000(40MHz)
- 同时按住 Power_SW + P32/INT0 约 5 秒进入下载模式(先松 Power_SW 再松 P32),串口出现 (HID1) USB-Writer 后点"下载/编程"
- 首次运行依次点击四角校准点;上半屏手写表达式(如 6+1×2),点击下半屏显示识别结果与计算值;单字符推理约 58ms
example.zip
(168.46 MB, 下载次数: 33)
|