使用YOLOv8-Pose构建模拟仪表读数器
我构建了一个四关键点的YOLOv8n-Pose模型,用于读取NE301 AI摄像头上的模拟仪表。该模型可定位指针支点、刻度两端以及指针尖端;随后通过一个小的后处理模块将这些角度转换为0-100范围内的读数。
浮点数处理流程产生了空间上变化较大的输出结果,而首次进行的INT8部署则未出现此问题。导出成功,量化完成,STM32N6神经处理单元(NPU)软件包编译通过,但相关坐标却塌缩为相同值,置信度分数重复,且一个量表被检测为两个检测点。
仅在导出时对解码后的坐标进行归一化,即可恢复有用的差异性。在三张图像中,归一化的TFLite模型最大成对关键点差异达到0.3127,而修复前这一数值约为0.000。设备随后返回一个姿态,并完成仪表计算,报告读数为46.99。
该数值并非准确性的保证:所记录的帧没有独立标注的真实地面真值。这表明输出已停止崩溃,且完整的姿态到读取路径已在设备上运行。
你将构建的内容
通过完成本项目,你将组装出可复现的相同流程组件:
- 使用四个命名的姿态关键点解释模拟仪表;
- 识别 INT8 崩溃的输出模式;
- 修复 YOLOv8-Pose 导出路径,使坐标和置信度值使用可比较的范围;
- 对修复后的模型进行量化,并构建 STM32N6 NPU 包;
- 分别验证数值一致性、空间响应、NPU 可部署性以及设备输出结果。
附件包含导出补丁、量化配置、模型打包配置、C 和 Python 仪表读取代码、可编辑的图表、测试用例以及一次记录的设备运行结果。
开始前的准备
你需要一个训练好的四关键点 YOLOv8n-Pose 模型、代表性的校准图像、Ultralytics v8.4.60、ST 的 tflite_quant.py 脚本、stedgeai 工具链,以及 NE301 源码树。测试模型输入尺寸为 256x256,输入为无符号 INT8,输出为有符号 INT8。
训练好的权重、校准数据集、ST 脚本及专有工具链均不得重新分发。使用您自己训练好的模型和校准图像,然后从本项目末尾链接的源文件中下载ST工具。
步骤1:构建四点关键点测量仪
该应用程序使用自定义的YOLOv8n-Pose模型来定位四个地标:
•中心:指针的枢轴点
•最小端点:最小尺度端点
•最大端点:最大尺度端点
•指尖:指针末端
仅靠一个边界框无法生成读数。后处理模块需要这四个点之间的角度关系。
对于每个点,系统计算其相对于中心的角度;
然后测量指针指尖沿刻度弧线的距离;
实现代码会检查逆时针和顺时针方向的补角弧线,将比例值限制在0-1范围内,并排除退化几何结构。经过测试的固件会将结果映射到0-100百分比范围。
预期结果:给定四个有效的关键点,后处理模块应返回一个弧度比率以及在配置值范围内的读数。
步骤2:识别INT8崩溃
将模型导出为INT8 TFLite并部署到NE301后,输出同时出现了四个失败签名。
该简化的设备JSON展示了两个几乎重复姿势中的第一个。为了便于阅读,第二个姿势已省略:
有用的诊断模式是:
•center.x == center.y 且 max.x == max.y。
•四个关键点的置信度值均重复为0.99930495。
•一个物理量测仪生成了pose_count = 2。
•不同输入产生几乎相同的空间输出。
未出现崩溃、NaN或转换器错误,因此这种故障容易被误判为校准不良或模型较弱。
预期结果:在更改模型前,应确认该故障属于重复输出模式,而非转换器崩溃、操作缺失或后处理错误。
步骤3:对混合尺度输出进行归一化
对于这个四关键点模型,YOLOv8-Pose导出的张量形状为[1, 17, 1344]:
•通道0-3:边界框坐标,范围在0-256之间。
•通道4:类别置信度,范围在0-1之间。
•通道 5-16:四个关键点的坐标范围在 0-256 范围内,置信度值范围在 0-1 范围内。
量化配置在图中支持时采用逐通道量化,但最终导出的输出张量仍保留一个缩放因子和零点。单一由 0-256 坐标主导的输出缩放因子,使得 0-1 置信度通道的分辨率极低。
以一个简化的对称示例说明:
精确的缩放因子取决于校准和转换器的行为。重要的是观察到的范围不匹配问题,而非图示中的 2.01 值。
本项目所采用的修复方案仅在导出时将解码后的坐标归一化为 0-1 范围,训练和验证阶段则保持其原始坐标行为。
三个相关更改如下:
附件提供了 head-normalization-v8.4.60.patch 文件,该文件是针对干净的 Ultralytics v8.4.60 head.py 进行的干运行测试。
预期结果:导出的边界框和关键点坐标已归一化为 0-1 范围,而训练和验证阶段则保持原有行为。
步骤 4:导出、量化并构建 NPU 包
导出已打补丁的模型
在将附件补丁应用于 Ultralytics v8.4.60 后,以记录的输入尺寸导出训练好的权重:
使用记录的 ST 量化配置
项目配置为 user_config_pose_gauge_norm.yaml:
替换占位符路径,然后运行 ST 的官方脚本:
从生成的模型中读取量化参数
请勿从旧的 JSON 文件中复制比例和零点值,应从每个生成的 TFLite 良品中读取:
生成的固定成品中记录的比例为 0.00613071,零点为 -116。这些是证据值,而非未来构建时的常量。
配置并构建 NE301 包
将 TFLite 和 JSON 文件放置于 ne301/Model/weights/ 目录下,然后设置模型变量:
从 NE301 仓库根目录开始:
测试流程如下:
预期结果:生成一个 TFLite 模型,其比例和零点值与模型 JSON 中一致,并使用 yolov8_mpe 重定位配置生成 NE301 模型包。
步骤 5:验证数值行为与设备输出
验证采用了四种不同的检查方法,它们回答了不同问题,不应视为可互换。
检查 1:相同图像上的 INT8 与 FP32
项目采用最大绝对差值小于 0.05 作为接受标准。这是项目特定的阈值,而非通用的 YOLO 基准。
•test-meter-3:最大绝对差值 0.030(通过)。
•test-meter-4:最大绝对差值 0.017(通过)。
•meter-test:最大绝对差值 0.048(通过)。
比较两张不同的图像并不能替代此项检查。相似的度量工具确实可能产生相似的结果。
检查2:TFLite空间响应
•压缩导出结果:三张图像中最大点对差异约为0.000。
•归一化导出结果:三张图像中最大点对差异为0.3127。
此项检查确认固定模型对输入几何结构作出响应,而非返回几乎恒定的坐标。
检查3:NPU部署性
stedgeai编译确认该图谱可部署在STM32N6目标上。但仅凭此无法证明数值正确性。
•C代码生成耗时:23.6秒。
•network_rel.bin大小:3.18 MB。
•npuRAM5占用率:98.21%(440 KB / 448 KB)。
•纯硬件训练轮次:118 / 133。
•混合训练轮次:14 / 133。
•软件训练轮次:1 / 133(Softmax)。
检查4:设备运行记录
设备端输出不再被压缩:
运行包含的Python参考程序与该JSON文件对比后得出:
包含的参考测试套件共通过14个测试用例,涵盖记录样本、范围映射、顺时针/逆时针选择、低置信度、缺失关键点、退化弧线以及关键点排序等场景。
该设备框架没有独立标注的地面真实读数。它证明了坐标和置信度的变化已恢复,且后处理器已完成;但它并非精度基准。
预期结果:相同图像的对比通过项目阈值,不同输入产生不同的关键点几何结构,图谱可为NPU编译,设备返回一个未塌陷的姿态,供量规后处理器使用。
故障排查与限制
•坐标或置信度重复:检查导出的输出张量范围,并确认导出时已启用归一化补丁。
•TFLite输出看似有效但设备输出不一致:确认缩放因子、零点、原始输出缩放因子和原始输出零点与生成的TFLite文件匹配。
•量规读数缺失:确认所有四个命名的关键点均存在,且未与中心重合。
•旋转后的量规表现更差:记录的测试显示存在方向敏感性问题。训练数据不平衡可能是原因之一,但在本次实验中并未被单独确认。
•范围:此补丁针对观察到的混合尺度输出崩溃问题进行了处理。它并未消除与之无关的校准、操作支持或模型准确度问题。
结论
重要的教训不仅仅是 INT8 量化会降低精度。该模型经过编译并运行,但其最终输出张量已无法保留足够有用的变异性以满足应用需求。
通过以下三个检查可使诊断结果具有可重复性:
1. 检查输出模式,而不仅仅关注转换器和编译器的状态;
2. 使用相同的图像比较 FP32 和 INT8 的输出,并测试不同输入是否产生不同的空间输出;
3. 将数值一致性、NPU 编译以及设备端结果视为独立的判断门控条件。
对于此模型,导出时的坐标归一化在不改变训练行为的前提下,对坐标和置信度范围进行了对齐。最终模型通过了项目中针对相同图像的三项检查,能够响应输入几何变化,成功为 STM32N6 编译,并在设备上完成了四点测距路径的读取任务。
下一步有实际意义的实验是使用标注数据集,涵盖不同测距方向和指针位置。这将用于评估读取准确率;目前的证据表明的是从观察到的崩溃中恢复的能力,而非一个精确的准确性基准。
本文编译自hackster.io





