IoT-For-Beginners 实战作业:让设备对水果分类结果做出响应(IoT Hub 遥测与执行器控制)
【免费下载链接】IoT-For-Beginners12 Weeks, 24 Lessons, IoT for All!项目地址: https://gitcode.com/GitHub_Trending/io/IoT-For-Beginners
本文是 Microsoft IoT-For-Beginners 课程《从物联网设备检查水果质量》(4-manufacturing/lessons/2-check-fruit-from-device)配套作业《响应分类结果》的深度技术指南。设备通过 Custom Vision 完成图像分类后,会拿到每个标签(如ripe/unripe)的概率值——本作业的核心是让设备利用这些预测结果"做点什么":把数据上送到 IoT Hub 供云端系统处理、直接驱动执行器(如检测到未熟水果时点亮 LED),或将两者结合形成闭环。读完本文,你将掌握在 Python(Raspberry Pi / 虚拟设备)与 C++(Wio Terminal)两套代码栈中实现"预测结果 → 业务动作"的完整实战方案。
作业背景:预测值就是设备的决策输入
在本课正文 README.md 中,你已经完成了三件事:给设备接上相机传感器采集图像、在 Custom Vision 门户发布分类器迭代、然后从设备端调用预测 API 拿到结果。以仓库中的示例代码为例,Python 端的分类输出形如:
ripe: 56.84% unripe: 43.16%Wio Terminal 端通过classifyImage函数在串口监视器输出同样的内容。这些概率就是本作业的"原材料"——作业要求你为设备新增响应逻辑,让分类结果不再只是终端里的一行打印,而是真正驱动业务行为。
从实现层面看,设备在调用分类器之后已经握有完整的预测数据。以 code-classify/pi/fruit-quality-detector/app.py 为例,results.predictions中每个prediction都携带tag_name(标签名)与probability(0~1 的浮点概率);Wio Terminal 的 code-classify/wio-terminal/fruit-quality-detector/src/main.cpp 则通过 ArduinoJson 解析 REST 响应的predictions数组,同样拿到tagName与probability。你的响应逻辑就建立在解析这两个字段的基础之上。
三种响应方案:作业的技术路径
作业给出了三条明确的技术路径,可以单选,也可以组合:
- 向 IoT Hub 发送数据:把预测结果作为遥测消息上送到云端,供其他系统继续处理;
- 控制执行器:例如当预测为
unripe时点亮 LED,直接在现场给出物理反馈; - 组合方案(闭环):设备把预测数据发送到 IoT Hub,由一段无服务器代码(如 Azure Functions)判定水果是否成熟,再把控制命令回传给设备驱动执行器。
方案一:将预测结果作为遥测数据发送到 IoT Hub
这是最直接的响应方式——不要求设备做任何本地判断,只需把预测值序列化后通过 IoT Hub 设备 SDK 上送。仓库的农场专题课程中已有完整可参考的实现模式:2-farm/lessons/4-migrate-your-plant-to-the-cloud/code/virtual-device/soil-moisture-sensor/app.py 展示了标准流程:
import json from azure.iot.device import IoTHubDeviceClient, Message, MethodResponse connection_string = '<connection_string>' device_client = IoTHubDeviceClient.create_from_connection_string(connection_string) print('Connecting') device_client.connect() print('Connected') # ... 读取传感器 / 获取分类结果 ... message = Message(json.dumps({ 'soil_moisture': soil_moisture })) device_client.send_message(message)套用到本作业,只需把soil_moisture换成分类结果即可,例如构造如下消息负载:
message = Message(json.dumps({ 'tag': prediction.tag_name, 'probability': prediction.probability })) device_client.send_message(message)Wio Terminal 端则使用IoTHubDeviceClient的 C++ 版本(azure-iot-sdk-c),实现send_message调用,将 JSON 序列化的预测结果发送到 IoT Hub。云端既可以是简单的数据收集存储,也可以是后续课程中介绍的 Azure Functions 事件处理。
💁 需要说明:连接 IoT Hub 所需的
connection_string来自你在 Azure 门户中创建的 IoT Hub 设备注册,不同硬件平台(Raspberry Pi / 虚拟设备 / Wio Terminal)的 SDK 包与初始化方式不同,但"序列化预测结果 → 调用 send_message"这一响应逻辑是通用的。
方案二:直接驱动执行器
如果设备端希望在本地即时反应,可以在解析出预测结果后直接控制执行器。作业给出的典型示例是:当水果被判定为未熟(unripe)时点亮 LED。
逻辑骨架如下(Python 伪代码,可嵌入 app.py 的预测打印循环之后):
for prediction in results.predictions: print(f'{prediction.tag_name}:\t{prediction.probability * 100:.2f}%') if prediction.tag_name == 'unripe' and prediction.probability > 0.5: led.on() # 点亮 LED,提示该果实未熟 else: led.off()这里有两个值得注意的设计要点:
- 阈值判定:分类器会为所有标签返回概率,且所有概率之和为 1。因此"该水果是否为未熟"的判定通常取概率最高(或超过 0.5)的标签,或为每个标签单独设置业务阈值;
- 响应的一致性:评分标准中"卓越(Exemplary)"级别明确要求响应必须"对相同取值的预测稳定生效(consistently works with predictions of the same value)",也就是说同样的预测输入应始终触发同样的执行器动作,不能出现偶发的不响应或误动作。这提示你在实现时要避免竞态条件,例如在 Wio Terminal 的
loop()中按下按钮后调用classifyImage,应确保预测循环结束后再统一驱动执行器,而不是在每个标签概率更新时各自触发动作。
方案三:IoT Hub + 无服务器代码 + 命令回传(闭环)
组合方案把前两者串成完整链路,作业描述为:设备发送预测数据到 IoT Hub → 无服务器代码(serverless code)判定水果是否成熟 → 向设备回传命令 → 设备收到命令后控制执行器。
这条路径在仓库中有成熟的参考骨架。云端侧可以复用农场专题中"IoT Hub 直连方法(direct method)"的通信模式——设备端通过on_method_request_received注册处理方法,云端调用直连方法时设备执行对应动作并回执状态码:
def handle_method_request(request): print("Direct method received - ", request.name) if request.name == "relay_on": relay.on() elif request.name == "relay_off": relay.off() method_response = MethodResponse.create_from_method_request(request, 200) device_client.send_method_response(method_response) device_client.on_method_request_received = handle_method_request(完整示例见 2-farm/lessons/4-migrate-your-plant-to-the-cloud/code/virtual-device/soil-moisture-sensor/app.py。)
在本作业场景中,你可以把relay_on/relay_off换成"点亮/熄灭 LED"之类的设备动作:设备上送{ "tag": "unripe", "probability": 0.87 }后,Azure Functions 等无服务器代码解析消息并判断成熟度,若判定为未熟则调用 IoT Hub 直连方法下发命令,设备收到命令后点亮 LED。这样做的好处是判断逻辑集中在云端,设备端保持"只采集、只执行"的简单职责,业务规则变更时无需重新烧录设备固件。
评分标准逐项拆解:从"达到要求"到"卓越"
作业自带的评分表(Rubric)界定了三个层级,是检验实现质量的直接标尺:
| 标准 | 卓越(Exemplary) | 合格(Adequate) | 需改进(Needs Improvement) |
|---|---|---|---|
| 对预测结果做出响应 | 成功实现了一种响应,且对相同取值的预测能稳定生效 | 实现了响应,但不依赖预测结果,例如仅向 IoT Hub 发送原始数据 | 无法编写程序让设备对预测结果做出响应 |
三档之间的本质差异在于响应是否真正由预测值驱动:
- 合格档:代码确实调用了 IoT Hub 或执行器,但动作与预测值无关——例如无条件把图像原始字节上送 IoT Hub,或无论预测结果如何都点亮 LED。这类实现完成了"通信",但没有完成"响应";
- 卓越档:动作严格依赖预测值。例如只有
unripe概率超过阈值才点亮 LED;或上送的消息负载中包含标签与概率字段,云端据此做出差异化处理。同时,相同预测值反复出现时,设备每次都应稳定触发相同动作——这正是实际产线场景对一致性的最低要求; - 需改进档:预测循环只打印不动作,或代码存在编译/运行错误导致无法执行。
对照 code-classify/wio-terminal/fruit-quality-detector/src/main.cpp 可以看到,预测循环内部已经在逐个标签读取tagName与probability——卓越档的响应逻辑天然应该挂在这个循环里,而不是循环之外。
落地与验证建议
- 先确认分类链路通畅:按 single-board-computer-classify-image.md 或 wio-terminal-classify-image.md 跑通图像采集与预测打印,再叠加响应逻辑,避免一次排查两个问题域;
- 响应代码紧贴预测循环:Python 端写在
for prediction in results.predictions循环内,C++ 端写在classifyImage的预测解析之后、httpClient.end()之前,确保缓冲区释放前完成动作决策; - 验证稳定性:连续用同一张水果图像(或同一场景)触发多次预测,确认每次触发相同的执行器动作 / 发送相同的遥测负载,满足"卓越"档的一致性要求;
- 组合方案注意消息协议一致性:设备发送的 JSON 字段名(如
tag、probability)需与云端无服务器代码解析的字段名严格一致,直连方法名(如led_on/led_off)也需在设备端handle_method_request与云端调用方之间统一。
完成本作业后,你的设备将不再是一个"只会打印概率的相机",而是一个具备现场决策能力的 IoT 节点——无论是向云端上报、本地驱动执行器,还是接受云端命令形成闭环,这套"分类结果 → 业务响应"的模式正是智能分拣、质量检测等制造业 IoT 应用的通用骨架,可直接迁移到 4-manufacturing 后续课程的真实检测场景中。
【免费下载链接】IoT-For-Beginners12 Weeks, 24 Lessons, IoT for All!项目地址: https://gitcode.com/GitHub_Trending/io/IoT-For-Beginners
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考