news 2026/9/16 20:44:52

在 Wio Terminal 上接入 IoT Edge 图像分类器:将水果质量检测从云端切换到本地边缘服务(IoT-For-Beginners)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
在 Wio Terminal 上接入 IoT Edge 图像分类器:将水果质量检测从云端切换到本地边缘服务(IoT-For-Beginners)

在 Wio Terminal 上接入 IoT Edge 图像分类器:将水果质量检测从云端切换到本地边缘服务(IoT-For-Beginners)

【免费下载链接】IoT-For-Beginners12 Weeks, 24 Lessons, IoT for All!项目地址: https://gitcode.com/GitHub_Trending/io/IoT-For-Beginners

本指南讲解 IoT-For-Beginners 项目第 4 章第 3 课中面向 Wio Terminal 的硬件改造流程:把fruit-quality-detector应用从调用云端 Custom Vision 分类服务,切换为调用部署在本地 IoT Edge 设备上的图像分类器。完成改造后,你的 Wio Terminal 将拍照并通过 HTTP 直接请求本机网络内的边缘分类服务,获得"ripe / unripe"(成熟/未成熟)的实时预测结果,整个过程不经过公网。文章完整覆盖配置修改、代码改造、编译上传与串口输出验证,并结合仓库源码逐行解释改动原理。

改造背景:为什么把分类器搬到边缘

在上一课(第 2 课:从设备端检查水果)中,Wio Terminal 把相机拍摄的 JPEG 图片通过 HTTPS 上传到云端 Custom Vision 服务进行分类。云端调用存在延迟、费用,且图片数据要离开本地网络。

第 3 课 Run your fruit detector on the edge 引入边缘计算思路:将训练好的图像分类模型以 Docker 容器形式部署到本地网络内的 IoT Edge 设备上,设备直接访问局域网内的分类服务。这样做的好处是响应更快、可离线工作、图片不出本地网络(隐私性更强)。本文的关联文档 wio-terminal.md(本仓库同时提供 丹麦语翻译版)就是 Wio Terminal 一侧的完整改造指南。

IoT Edge 分类器的 REST API 端点

部署在 IoT Edge 上的图像分类器对外暴露一个与云端 Custom Vision 相同的 REST API,只是端点从 Azure 云端地址变成了本地地址:

http://<IP地址或主机名>/image

其中<IP地址或主机名>替换为运行 IoT Edge 的那台计算机的 IP 地址或主机名(如果代码运行在 IoT Edge 设备本机,也可以使用localhost)。请求方式是POST,请求体为图片二进制内容,响应为 JSON,其中predictions数组里包含每个标签及其probability置信度。主文档 README.md 中给出了用curl直接验证该端点的示例:

curl --location \ --request POST 'http://<IP地址或主机名>/image' \ --header 'Content-Type: image/png' \ --data-binary '@<文件名>'

由于这是本地网络内的服务而非 Azure 资源,调用时不再需要Prediction-Key预测密钥——安全性改由内部网络安全策略保障,而不是依赖公网端点和 API Key。这一差异正是本次改造的核心动因。

改造步骤总览

整个改造围绕"从 HTTPS + 证书 + 密钥的云端调用"变为"HTTP + 无证书 + 无密钥的本地调用"展开,共涉及两个源文件:config.h(配置)与main.cpp(主逻辑),工程位于 code-classify/wio-terminal 目录下(PlatformIO 工程,结构为fruit-quality-detector/src/)。

1. 打开fruit-quality-detector工程

如果你还没有打开 Wio Terminal 的fruit-quality-detector工程,请先打开它。工程根目录的 platformio.ini 指定了目标平台为seeed_wio_terminal(Atmel SAMD 平台、Arduino 框架),并声明了rpcWiFiArduinoJsonSeeed Arduino FS等依赖库。

2. 清理config.h:删除证书与密钥,更新预测 URL

云端版本中,config.h 除了 WiFi 凭据和预测 URL 外,还包含两项本地调用不再需要的内容:

  • PREDICTION_KEY:云端 Custom Vision 服务的预测密钥;
  • CERTIFICATE:Microsoft Azure DigiCert Global Root G2 根证书(用于 HTTPS 证书校验)。

因为 IoT Edge 分类器运行在HTTP(而非 HTTPS)上,无需证书校验,因此要把CERTIFICATEconfig.h中删除;PREDICTION_KEY同样不再需要,一并删除。改造后的 config.h 只剩三个配置项,最终形态如下:

#pragma once #include <string> using namespace std; // WiFi credentials const char *SSID = "<SSID>"; const char *PASSWORD = "<PASSWORD>"; const char *PREDICTION_URL = "<URL>";

其中<URL>替换为你的边缘分类器地址,例如:

const char *PREDICTION_URL = "http://192.168.1.100/image";

3. 修改main.cpp:切换为 HTTP 版 WiFi 客户端

主逻辑文件 main.cpp 需要做三处改动,全部围绕"从 HTTPS 客户端降级为 HTTP 客户端":

第一处:替换 include 指令。云端版本导入的是安全版 WiFi 客户端(WiFiClientSecure.h),现在改为标准 HTTP 版本:

#include <WiFiClient.h>

第二处:修改客户端对象声明。将全局声明的 WiFi 客户端对象改为 HTTP 版本:

WiFiClient client;

第三处:删除证书设置。connectWiFi函数中删除设置根证书的语句:

// 删除这一行: client.setCACert(CERTIFICATE);

改造后的connectWiFi只负责连接 WiFi,不再做任何 TLS 证书绑定:

void connectWiFi() { while (WiFi.status() != WL_CONNECTED) { Serial.println("Connecting to WiFi.."); WiFi.begin(SSID, PASSWORD); delay(500); } Serial.println("Connected!"); }

第四处:删除预测密钥请求头。classifyImage函数中删除为请求添加Prediction-Key头的语句:

// 删除这一行: httpClient.addHeader("Prediction-Key", PREDICTION_KEY);

源码级解读:边缘调用如何完成分类

完成上述改动后,main.cpp 中的核心调用链路如下:

void classifyImage(byte *buffer, uint32_t length) { HTTPClient httpClient; httpClient.begin(client, PREDICTION_URL); httpClient.addHeader("Content-Type", "application/octet-stream"); int httpResponseCode = httpClient.POST(buffer, length); if (httpResponseCode == 200) { String result = httpClient.getString(); DynamicJsonDocument doc(1024); deserializeJson(doc, result.c_str()); JsonObject obj = doc.as<JsonObject>(); JsonArray predictions = obj["predictions"].as<JsonArray>(); for(JsonVariant prediction : predictions) { String tag = prediction["tagName"].as<String>(); float probability = prediction["probability"].as<float>(); char buff[32]; sprintf(buff, "%s:\t%.2f%%", tag.c_str(), probability * 100.0); Serial.println(buff); } } httpClient.end(); }

可以看到,改造后的分类请求只保留了两个 HTTP 头:Content-Type: application/octet-stream(标识请求体为二进制图片流),以及与边缘服务地址对应的 URL。调用流程为:

  1. httpClient.begin(client, PREDICTION_URL)建立到边缘设备的 HTTP 连接;
  2. httpClient.POST(buffer, length)把相机读入内存的 JPEG 字节流作为请求体直接 POST 到/image端点;
  3. 响应码为200时,用 ArduinoJson(工程依赖 platformio.ini 中的bblanchon/ArduinoJson @ 6.17.3)解析返回的 JSON,遍历predictions数组;
  4. 对每个预测项,取出tagNameprobability,按标签: 百分比格式输出到串口,其中概率值乘以 100 并以两位小数格式化。

图片的采集来自 camera.h 封装的 ArduCAM OV2640 摄像头:startCapture()触发捕获、captureReady()轮询 FIFO 完成标志、readImageToBuffer()通过 SPI 突发读取 JPEG 数据到内存缓冲区(缓冲区大小即图片字节数)。loop()中检测 Wio Terminal 的C 按钮WIO_KEY_C,上拉输入模式),按下后依次执行拍照、读缓冲、分类。

编译、上传与运行验证

完成代码修改后,在 PlatformIO 中编译并上传到 Wio Terminal(对应环境seeed_wio_terminal)。将相机对准水果,按下 C 按钮,即可在串口监视器看到如下输出:

Connecting to WiFi.. Connected! Image captured Image read to buffer with length 8200 ripe: 56.84% unripe: 43.16%

输出含义:

  • Connecting to WiFi..Connected!connectWiFi成功连上config.h中配置的 WiFi;
  • Image captured:C 按钮触发拍照成功;
  • Image read to buffer with length 8200:摄像头读出的 JPEG 数据共 8200 字节;
  • ripe: 56.84% / unripe: 43.16%:边缘分类器判定当前画面中水果为"成熟"的概率约 56.84%,"未成熟"约 43.16%。

与上一课云端调用的输出格式完全一致,说明边缘 REST API 与云端 Custom Vision API 的响应结构(predictionstagName/probability)保持兼容,这正是应用可以平滑切换端点、无需改动解析逻辑的原因。

注意事项与后续优化

  • HTTP 明文传输:本方案使用 HTTP 而非 HTTPS,图片在本地网络内明文传输。正如主文档 README.md 所述,边缘部署不再依赖公网端点与 API Key,安全模型应由内部网络策略负责;如需加密传输,应在边缘侧自行配置 TLS。
  • 模型再训练:切到边缘分类后,图片不再上传到 Custom Vision,因此 Custom Vision 门户的 Predictions 页不会出现这些被分类的图片。若想用新样本改进模型,需要自行设计图片留存与人工再标注流程。
  • 性能对比:可以分别对云端与边缘端点做计时对比,评估不同网络拓扑与硬件算力下的延迟差异,并研究 GPU/加速硬件(如 AI 加速板)对边缘推理的加速效果。
  • 环境清理:本课使用了 Azure IoT Hub、Azure Container Registry 等付费资源,实验完成后请参考 clean-up.md 清理项目资源以节省费用。

完成上述全部改造后,你的水果质量分类程序就成功运行在边缘了——完整代码可对照仓库中的 code-classify/wio-terminal 目录,部署清单可参考 code-deployment/deployment/deployment.json。

【免费下载链接】IoT-For-Beginners12 Weeks, 24 Lessons, IoT for All!项目地址: https://gitcode.com/GitHub_Trending/io/IoT-For-Beginners

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/16 20:44:41

逆向微信小游戏包,还原Cocos工程结构与核心玩法

把一款已经上线的微信小游戏当作学习样本&#xff0c;从包体里反推它的Cocos工程结构&#xff0c;这事听起来有点“灰”&#xff0c;但只要你把边界划清楚&#xff0c;它其实是特别高效的进阶训练。我最近就拿一款“切水果跑酷”玩法的上线小游戏复盘了一轮&#xff0c;玩法说白…

作者头像 李华
网站建设 2026/9/16 20:44:27

OpenCode 的 /models 报 401?TaoToken 的 Base URL 别加 /v1

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/16 20:44:06

蜂鸟芯片:国产离线语音识别的硬核实践指南

1. 项目概述&#xff1a;为什么“蜂鸟”不是一只普通的小鸟&#xff1f;云知声&#xff08;Unisound&#xff09;的蜂鸟系列芯片&#xff0c;名字听着轻巧&#xff0c;但实际是嵌入式AI语音识别领域里少有的、真正把“离线”二字刻进骨子里的硬核方案。我第一次在客户现场看到它…

作者头像 李华
网站建设 2026/9/16 20:43:51

AI工具矩阵如何提升本科开题报告效率

1. 本科开题报告的核心痛点解析本科阶段的开题报告是学术研究的第一个正式里程碑&#xff0c;却让无数学生辗转反侧。根据我指导过200本科生的经验&#xff0c;90%的迷茫集中在三个维度&#xff1a;选题价值论证薄弱&#xff08;42%&#xff09;、文献综述质量低下&#xff08;…

作者头像 李华