资讯动态

RIOT 中的 uTensor MNIST 手写数字识别示例:从 TensorFlow 模型训练到 MCU 端推理

发布时间:2026/9/20 23:32:56 来源:尧图企业网站定制
物联网嵌入式操作系统实时系统【免费下载链接】RIOTRIOT - The friendly OS for IoT项目地址https://gitcode.com/GitHub_Trending/riot/RIOT点击查看免费下载导读本文围绕 RIOT 仓库 tests/pkg/utensor/README.md 展开系统讲解如何在一个嵌入式 IoT 操作系统 RIOT 上运行 uTensor 深度学习推理引擎用 TensorFlow 训练一个 MLP多层感知机模型在 MNIST 数据集上完成手写数字识别并通过utensor-cli工具把模型与权重转换为 C 代码静态烧写进固件后在 MCU 上执行推理。读完本文你将掌握该示例的构建运行方法、如何更换待识别数字、以及如何重新训练模型并重新生成 C 模型文件的完整工作流。示例概览在 MCU 上跑一个神经网络uTensor 是面向 MCU 的轻量级深度学习推理框架。本示例应用展示了一条完整的端到端链路使用 TensorFlow 在 MNIST 数据集上训练一个 MLPMulti-Layer Perceptron模型用于识别图像中的手写数字0–9通过utensor-cli将训练好的模型Protocol Buffer 格式的.pb文件转换为 C 源码模型结构 权重将 C 模型文件作为 RIOT 的外部模块external module编译进固件把一张 MNIST 测试集图片以二进制 blob 形式嵌入固件在设备上运行推理并打印预测的标签。该应用的代码源自一个公开的博客示例Hackster.io 上的 Simple Neural Network on MCUs由 RIOT 团队Inria适配进仓库版权信息见 main.cpp 头部注释。应用运行时输出如下Simple MNIST end-to-end uTensor cli example (device) Predicted label: 7仓库自带的自动化测试 tests/01-run.py 正是通过child.expect_exact(Predicted label: 7)来校验该默认输出确保端到端推理链路可用。快速构建与运行在具备 RIOT 工具链make 对应架构的交叉编译器的机器上进入示例目录并指定目标板卡即可编译并打开串口终端make BOARDboard of your choice all term例如使用native板卡或任意 RAM/Flash 充足的开发板。term目标会编译完成后直接打开终端连接设备的串口便于观察推理输出。内存约束并非所有板卡都能跑神经网络的权重文件 deep_mlp_weight.hpp 约 650 KB包含所有层权重加上 uTensor 运行时对板卡的 RAM/Flash 有一定要求。仓库在 Makefile.ci 中显式声明了BOARD_INSUFFICIENT_MEMORY列表如bluepill-stm32f103c8、nucleo-f103rb、microbit、samd10-xmini、stm32f0discovery等数十块小内存板卡CI 会跳过这些内存不足的板卡。选择板卡时建议优先选用内存充足的型号例如nucleo-f746zg、esp32-wroom-32等否则可能链接失败。同时Makefile 中通过CFLAGS -DTHREAD_STACKSIZE_MAINTHREAD_STACKSIZE_LARGE为main线程分配更大的栈空间以满足 uTensor 推理时的栈需求。输入数字从哪来blob 机制待识别的手写数字图片存放在应用目录下的digit二进制文件中28×28 像素、每像素以 float32 存储共 28×28×4 3136 字节。构建时它通过 RIOT 构建系统的blob 机制Makefile 中的BLOBS digit被自动转换为一个 C 数组digit以静态数据的方式嵌入固件镜像。在 main.cpp 中可以看到完整的推理主流程alignas(float) #include blob/digit.h // 嵌入的 MNIST 测试图片float32 数组 #include deep_mlp.hpp // utensor-cli 生成的模型文件 #include tensor.hpp // uTensor 张量类 int main() { puts(Simple MNIST end-to-end uTensor cli example (device)\n); // 创建上下文推理在该上下文中执行 Context ctx; // blob 以字节存储借助 alignas(float) 保证对齐后转为 float 指针 float *digit_as_float (float *)(uintptr_t)digit; // 把输入图片包装成张量{1, digit_len 2}digit_len 为字节数2 得到 float 个数784 auto input_x new WrappedRamTensorfloat({1, digit_len 2}, digit_as_float); // 将输入张量的所有权移交给 context并注册模型计算图 get_deep_mlp_ctx(ctx, input_x); // 取输出张量引用并触发推理 S_TENSOR pred_tensor ctx.get(y_pred:0); ctx.eval(); // 读取预测标签one-hot 向量中的最大索引并打印 uint8_t pred_label *(pred_tensor-readint(0, 0)); printf(Predicted label: %d\r\n, pred_label); return 0; }关键点解析get_deep_mlp_ctx(ctx, input_x)由utensor-cli生成声明见 deep_mlp.hpp其实现 deep_mlp.cpp 内部通过ctx.add(...)、ctx.push(new MatMulOp()/new QuantizedMatMulOp()...)等调用把整个计算图占位符、reshape、量化、矩阵乘法、Softmax 等算子注册进Context输出张量名为y_pred:0对应 README 中utensor-cli convert命令的--output-nodesy_predctx.eval()触发整张计算图的执行。更换待识别数字默认的digit文件包含 MNIST 测试集的第一张图片——一个手写的7。你可以用示例自带的 Python 脚本 generate_digit.py 从 MNIST 测试集中选取其他图片重新生成digit文件./generate_digit.py --index 1脚本参数说明参数默认值含义-i, --index0在 MNIST 测试集中选取的图片索引-o, --outputdigit输出二进制文件名--no-plotFalse禁用 matplotlib 弹出图片窗口脚本会调用tf.keras.datasets.mnist.load_data()加载 MNIST 测试集取第index张图片将其astype(float32)后以二进制原样写入digit文件与 blob 机制的 float32 布局一致。每个选中的数字在脚本结束时都会以图像形式显示出来方便与固件预测出的标签做目测对比。需要注意每次生成新的 digit 后固件必须重新编译——图片是作为 blob 静态嵌入固件镜像的不重新烧写固件就无法生效make BOARDboard of your choice all flash term重新训练模型并更新 C 模型文件应用默认包含一个预训练模型存放在external_modules/models外部模块中除 Makefile 外所有 C 文件——模型结构 权重——均由utensor-cli从 TensorFlow 训练的模型自动生成。如果希望训练新模型并同步更新应用中的 C 文件可按以下步骤操作。1. 安装 Python3 依赖pip3 install --user utensor_cgen graphviz pip3 install --user tensorflow -U注意utensor_cgen目前仅兼容 TensorFlow 1.x请勿使用 TensorFlow 2.x 执行下述转换流程。2. 克隆 utensor-mnist-demo 仓库该仓库包含在 MNIST 数据集上训练 MLP 模型的 Python 脚本cd /tmp git clone uTensor/utensor-mnist-demo 仓库地址3. 训练 MLP 模型cd /tmp/utensor-mnist-demo python3 /tmp/utensor-mnist-demo/tensorflow-models/deep_mlp.py训练完成后模型以 Protocol Buffer 格式存放在/tmp/utensor-mnist-demo/mnist_model/deep_mlp.pb4. 生成 C 模型文件并纳入 RIOT 构建cd $RIOTBASE/tests/pkg/utensor utensor-cli convert /tmp/utensor-mnist-demo/mnist_model/deep_mlp.pb --target utensor --output-nodesy_pred该命令会在当前目录应用目录生成模型与权重 C 文件deep_mlp.cpp/deep_mlp.hpp/deep_mlp_weight.hpp随后即可重新编译固件。生成结果需覆盖或替换 external_modules/models 目录下的同名文件。外部模块如何接入 RIOT 构建生成的模型 C 文件通过 RIOT 的外部模块机制接入构建系统。相关配置集中在应用的 Makefileinclude ../Makefile.pkg_common USEPKG utensor # 引入 uTensor 包pkg/utensor # Include input data as blob BLOBS digit # 将 digit 二进制文件作为 blob 嵌入固件 # Include C model and training weights code as an external module USEMODULE models EXTERNAL_MODULE_DIRS external_modulesUSEPKG utensor声明依赖 pkg/utensor 外部包uTensor 推理引擎的源码由此获得EXTERNAL_MODULE_DIRS external_modules把external_modules目录加入外部模块搜索路径其中的models子模块含Makefile与Makefile.include被USEMODULE models拉入构建models/Makefile仅包含include $(RIOTBASE)/Makefile.base声明这是一个标准模块models/Makefile.include 则负责把头文件目录加入USEMODULE_INCLUDES并追加CXXEXFLAGS -Wno-unused-parameter抑制生成代码中未使用参数的告警生成的模型代码使用了 C如new、模板类因此该示例依赖 C 工具链支持RIOT 的 C 支持。小结一条可复用的 MCU 端 ML 流水线该示例完整演示了在 RIOT 上落地一个端到端 ML 应用的标准路径训练侧TensorFlow1.x训练 MLP → 导出.pb模型转换侧utensor-cli convert --target utensor --output-nodesy_pred生成 C 模型与权重集成侧USEPKGUSEMODULEEXTERNAL_MODULE_DIRS接入 RIOT 构建BLOBS机制静态嵌入输入数据推理侧ContextWrappedRamTensorctx.eval()在 MCU 上完成前向计算输出预测标签。对于希望把更大模型CNN、量化模型等搬到 RIOT uTensor 上的开发者本文的每一步都可直接复用更换训练脚本产出新的.pb重新执行utensor-cli convert再调整main.cpp中的输入输出张量名即可。赞分享物联网嵌入式操作系统实时系统【免费下载链接】RIOTRIOT - The friendly OS for IoT项目地址https://gitcode.com/GitHub_Trending/riot/RIOT点击查看免费下载相关推荐Caffe MNIST手写数字识别实战从数据准备到模型训练完整流程Caffe MNIST手写数字识别实战从数据准备到模型训练完整流程 想要快速入门深度学习实战Caffe框架结合MNIST手写数字识别项目是完美的起点深度学习计算机视觉机器学习使用GGML实现MNIST手写数字识别从训练到推理全流程解析使用GGML实现MNIST手写数字识别从训练到推理全流程解析 项目概述 GGML是一个专注于机器学习模型推理的轻量级库特别适合在资源受限的环境中运行。本文将人工智能机器学习深度学习算子库本地部署模型量化Caffe 实战在 MNIST 上训练 LeNet 手写数字识别模型从数据准备到训练部署全流程Caffe 实战在 MNIST 上训练 LeNet 手写数字识别模型从数据准备到训练部署全流程 本文以 Caffe 官方 MNIST 示例为线索完整讲解深度学习计算机视觉上一篇Envoy Google VRP 安全赏金计划实战双 Envoy 边界/源站架构、威胁模型与 Docker 执行环境下一篇Elasticsearch Node.js客户端错误处理与重试机制构建稳定应用的终极指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价