把工业质检模型送上产线:RT-Thread 边缘 AI 的落地方法

2026-09-03 48 预计阅读时间: 1 分钟
来源: oschina.net AI 摘要 Original link

Disclaimer: This article is an AI-assisted summary. Read it together with the original source when precision matters. The summary may omit context, version differences, or edge cases and is not official documentation.

预计阅读时间:10 分钟

工业质检是 AI 在制造业中较成熟的应用之一。划痕、裂纹、漏装和装配偏差,机器视觉可以持续工作,也能用一致的标准替代疲劳的人眼检查。

真正的难点却不在于“训练一个模型”。不同工件、不同光照、不同工艺阶段对应着不同缺陷,企业往往需要重新采集数据、标注样本、训练模型,再将模型部署到产线设备。这个过程短则几个月,长则超过一年。

RT-Thread 公布工业质检 AI 命题的价值,正在于把问题拉回工程现场:模型如何运行在资源受限的边缘设备上?摄像头、推理任务和产线信号如何协同?开发者怎样用更短的周期做出可以验证的原型?

工业质检的瓶颈不是单点算法

一套可用的质检系统,至少包含四个环节:

  1. 稳定采集:固定相机、镜头、光源和工件位置,减少环境变化对模型的影响。
  2. 缺陷定义:明确什么算缺陷、缺陷等级如何划分,以及误检和漏检哪个代价更高。
  3. 边缘推理:把模型放到靠近产线的位置,降低网络依赖和响应延迟。
  4. 结果联动:推理结果需要触发蜂鸣器、指示灯、继电器、机械臂或上位机,而不是停留在屏幕上的分类标签。

这也是工业质检区别于普通图片分类 demo 的地方。模型准确率只是指标之一,现场还要关注单帧延迟、连续运行稳定性、内存占用、异常恢复和数据追溯。

RT-Thread 适合承载哪些工作

在边缘设备上,RT-Thread 可以负责组织实时任务和硬件资源。例如:

  • 摄像头采集任务负责获取图像帧;
  • 图像预处理任务负责裁剪、缩放和归一化;
  • AI 推理任务负责调用已经转换好的轻量模型;
  • 产线控制任务负责根据结果驱动 GPIO、继电器或通信接口;
  • 日志任务负责记录置信度、耗时和设备状态。

任务之间可以通过消息队列或内存池传递数据。这样做的好处是,采集、推理和控制不会全部堆在一个循环里,后续替换模型或调整摄像头驱动时,影响范围也更清晰。

需要明确的是,RT-Thread 本身不会自动解决数据质量和模型泛化问题。开发者仍然需要针对具体工件采集样本,并根据设备的 CPU、内存和加速能力选择合适的模型规模。

一个可改造的边缘质检任务

下面的 C 代码是一个最小化的 RT-Thread 风格示例。它假设项目已经提供 camera_captureai_inferline_set_reject 三个硬件或推理适配函数。将这些函数替换为实际 BSP、摄像头驱动和推理库接口后,可以作为任务编排的起点。

#include <rtthread.h>
#include <stdint.h>

#define FRAME_QUEUE_SIZE  2
#define DEFECT_THRESHOLD  0.80f

typedef struct {
    uint8_t *data;
    uint32_t size;
    uint32_t frame_id;
} frame_t;

typedef struct {
    float defect_score;
    uint32_t inference_ms;
} infer_result_t;

static rt_mq_t frame_mq;

/* Replace these adapters with the camera, AI runtime, and line-control APIs. */
extern int camera_capture(uint8_t **data, uint32_t *size);
extern int ai_infer(const uint8_t *data, uint32_t size, infer_result_t *result);
extern void line_set_reject(int rejected);
extern void release_frame(uint8_t *data);

static void vision_thread_entry(void *parameter)
{
    frame_t frame;
    infer_result_t result;

    while (1) {
        if (rt_mq_recv(frame_mq, &frame, sizeof(frame), RT_WAITING_FOREVER) == RT_EOK) {
            if (ai_infer(frame.data, frame.size, &result) == 0) {
                line_set_reject(result.defect_score >= DEFECT_THRESHOLD);
                rt_kprintf("frame=%u score=%.3f latency=%ums\\n",
                           frame.frame_id,
                           result.defect_score,
                           result.inference_ms);
            } else {
                /* A failed inference should enter a known production state. */
                line_set_reject(1);
            }
            release_frame(frame.data);
        }
    }
}

static void camera_thread_entry(void *parameter)
{
    static uint32_t frame_id;
    frame_t frame;

    while (1) {
        frame.data = RT_NULL;
        frame.size = 0;

        if (camera_capture(&frame.data, &frame.size) == 0) {
            frame.frame_id = frame_id++;
            if (rt_mq_send_wait(frame_mq, &frame, sizeof(frame), 20) != RT_EOK) {
                /* Drop the frame when the queue is full to protect real-time behavior. */
                release_frame(frame.data);
            }
        }
        rt_thread_mdelay(10);
    }
}

int inspection_app_init(void)
{
    rt_thread_t camera_thread;
    rt_thread_t vision_thread;

    frame_mq = rt_mq_create("frames", sizeof(frame_t),
                            FRAME_QUEUE_SIZE, RT_IPC_FLAG_FIFO);
    if (frame_mq == RT_NULL) {
        return -1;
    }

    camera_thread = rt_thread_create("camera", camera_thread_entry, RT_NULL,
                                     2048, 15, 10);
    vision_thread = rt_thread_create("vision", vision_thread_entry, RT_NULL,
                                     4096, 10, 10);
    if (camera_thread == RT_NULL || vision_thread == RT_NULL) {
        return -1;
    }

    rt_thread_startup(camera_thread);
    rt_thread_startup(vision_thread);
    return 0;
}
INIT_APP_EXPORT(inspection_app_init);

这个示例有几个值得保留的工程决策:队列长度很小,避免图像在内存中无限堆积;推理失败时进入明确的拒检状态;每帧记录置信度和耗时,便于定位现场问题。实际项目还需要增加超时、看门狗、传感器触发、模型版本和结果上传机制。

从数据到产线的最小闭环

可以把一次命题实践拆成一条可验证的流水线:

# 1. 准备按类别组织的样本目录
mkdir -p dataset/{ok,scratch,crack,assembly_error}

# 2. 训练阶段导出适合边缘设备的模型
python train.py \
  --data dataset \
  --input-size 224 \
  --epochs 50 \
  --export model/inspection.tflite

# 3. 根据目标芯片和推理框架做量化或转换
python convert_model.py \
  --input model/inspection.tflite \
  --output model/inspection_int8.bin \
  --quantize int8

# 4. 将模型和固件资源写入开发板,再查看推理日志
python flash_assets.py --model model/inspection_int8.bin --port /dev/ttyUSB0
python monitor.py --port /dev/ttyUSB0 --baudrate 115200

这里的 train.pyconvert_model.pyflash_assets.py 是项目适配脚本,不是 RT-Thread 的固定命令。它们分别对应训练、模型转换和资源烧录三个阶段。真实使用时,应根据所选的芯片、AI 推理库和工具链替换命令。

验证时不要只拿一张“效果最好”的图片。至少要记录以下数据:

  • 正常件和各类缺陷件的召回率;
  • 连续运行时的平均延迟和 P95 延迟;
  • 光照、角度、速度变化下的误检率;
  • 模型、固件和样本集的版本;
  • 断电、重启、相机掉线和推理失败后的恢复行为。

适合开发者的切入方式

这类命题不要求一开始就复刻完整工厂系统。更稳妥的路径是先固定一个缺陷类型和一个采集条件,完成“图像输入到控制输出”的闭环,再逐步扩大数据范围。

建议按下面的顺序推进:

  1. 先用少量真实样本跑通离线推理,确认标签定义没有歧义。
  2. 将模型压缩或量化,测量目标板上的内存、延迟和温度。
  3. 接入相机和触发信号,验证连续采集不会丢帧或阻塞控制任务。
  4. 加入异常状态处理,让相机断开、模型失败和队列满都有可观察结果。
  5. 最后才调节阈值,并用现场误检与漏检样本持续迭代。

工业质检 AI 的门槛,正在从“能不能训练模型”转向“能不能把模型稳定地放进设备和流程”。RT-Thread 提供了一个适合组织实时任务、驱动和边缘应用的基础环境,而真正决定项目价值的,仍然是数据闭环、现场约束和可维护的部署方式。

落地检查清单

  • [ ] 缺陷类别和判定标准已经明确
  • [ ] 采集环境和样本覆盖了主要现场变化
  • [ ] 模型已在目标硬件上完成延迟和内存测试
  • [ ] 推理失败、相机掉线和设备重启都有处理策略
  • [ ] 检测结果能够驱动实际产线动作
  • [ ] 日志包含模型版本、帧号、置信度和耗时
  • [ ] 阈值调整有真实样本和误检漏检数据支撑

相关推荐