在工业质检领域,边缘计算设备正逐步取代传统人工检测方式。高通跃龙IQ-9100平台凭借其强大的AI加速能力,成为工业缺陷检测的理想选择。本文将带你在该平台上实现从摄像头输入到端侧推理的完整闭环,为后续工程化落地奠定基础。
这个实战项目的核心价值在于:通过最简化的流程验证整个缺陷检测链路的可行性。不同于理论讲解,我们将聚焦具体实现,让你在30分钟内就能看到摄像头画面中的实时检测效果。完成本教程后,你将获得:
我们选用Thundercomm基于IQ-9100开发的边缘计算盒子,主要考虑以下因素:
对于初期验证,建议使用普通的UVC摄像头(如罗技C920)。这种选择基于三点考量:
实际产线部署时,建议更换为工业相机。但验证阶段应优先确保开发速度,避免硬件问题阻塞软件调试。
系统要求Ubuntu 20.04 LTS或更高版本,关键组件版本要求:
bash复制# 验证基础环境
uname -a # 确认内核版本(建议5.4+)
python3 -V # 需要3.8+
gst-launch-1.0 --version # 需要1.18+
创建隔离的Python环境:
bash复制sudo apt update && sudo apt install -y python3-pip python3-venv
mkdir -p ~/defect_demo && cd ~/defect_demo
python3 -m venv venv
source venv/bin/activate
pip install -U pip setuptools wheel
安装核心依赖包时需注意版本兼容性:
bash复制pip install numpy==1.23.5 \ # 避免最新版与ONNXRuntime冲突
opencv-python-headless==4.7.0.72 \ # 无GUI依赖
onnxruntime==1.15.1 # CPU专用版
首先确认设备节点(通常为/dev/video0):
bash复制ls -l /dev/video* # 查看所有视频设备
v4l2-ctl --list-formats -d /dev/video0 # 检查支持的格式
使用GStreamer进行基础测试:
bash复制gst-launch-1.0 v4l2src device=/dev/video0 ! \
videoconvert ! \
fpsdisplaysink video-sink=fakesink \
text-overlay=false sync=false -v
正常情况应持续输出帧率(如30fps)。若出现以下问题:
v4l2src ! video/x-raw,width=640,height=480v4l2src ! video/x-raw,format=YUY2在letterbox函数中,我们实现了三项关键处理:
改进版的预处理流程:
python复制def smart_preprocess(frame, target_size=640):
# 自动选择最优插值方法
interp = cv2.INTER_AREA if frame.shape[0] > target_size else cv2.INTER_LINEAR
# 动态计算填充颜色(取图像边缘均值)
border_color = np.mean(frame[0:5, :], axis=(0,1)).astype(int).tolist()
# 执行letterbox
return letterbox(frame, new_shape=target_size, color=border_color)
初期验证推荐使用YOLOv8n(纳米级),原因在于:
导出ONNX模型时的关键参数:
bash复制yolo export model=yolov8n.pt \
format=onnx \
imgsz=640 \
opset=12 \ # 确保算子兼容性
dynamic=False # 固定批次维度提升性能
创建推理会话时推荐以下配置:
python复制sess_options = ort.SessionOptions()
sess_options.graph_optimization_level = ort.GraphOptimizationLevel.ORT_ENABLE_ALL
sess_options.intra_op_num_threads = 4 # 使用所有CPU核心
sess = ort.InferenceSession(MODEL_PATH,
sess_options,
providers=["CPUExecutionProvider"])
性能监控实现技巧:
python复制# 滑动窗口计算FPS
fps_window = deque(maxlen=30) # 限制队列长度
infer_time = time.time() - start_time
fps_window.append(1.0 / infer_time)
current_fps = np.mean(fps_window)
传统NMS的优化版本:
python复制def fast_nms(boxes, scores, iou_thres):
# 按得分降序排序
order = scores.argsort()[::-1]
boxes = boxes[order]
# 计算各框面积
areas = (boxes[:, 2] - boxes[:, 0]) * (boxes[:, 3] - boxes[:, 1])
keep = []
while order.size > 0:
i = order[0]
keep.append(i)
# 计算当前框与其他框的IoU
xx1 = np.maximum(boxes[i, 0], boxes[order[1:], 0])
yy1 = np.maximum(boxes[i, 1], boxes[order[1:], 1])
xx2 = np.minimum(boxes[i, 2], boxes[order[1:], 2])
yy2 = np.minimum(boxes[i, 3], boxes[order[1:], 3])
w = np.maximum(0.0, xx2 - xx1)
h = np.maximum(0.0, yy2 - yy1)
inter = w * h
iou = inter / (areas[i] + areas[order[1:]] - inter)
# 保留IoU低于阈值的框
inds = np.where(iou <= iou_thres)[0]
order = order[inds + 1]
return keep
可视化环节的三个实用技巧:
改进版可视化代码:
python复制def draw_boxes(img, boxes, scores, cls_ids, class_names):
h, w = img.shape[:2]
for i, (box, score, cls_id) in enumerate(zip(boxes, scores, cls_ids)):
x1, y1, x2, y2 = map(int, box)
# 动态调整线宽
thickness = max(1, int(min(h, w) / 300))
# 颜色编码
color = COLORS[cls_id % len(COLORS)]
# 绘制矩形
cv2.rectangle(img, (x1, y1), (x2, y2), color, thickness)
# 智能文本位置
label = f"{class_names[cls_id]}:{score:.2f}"
(tw, th), _ = cv2.getTextSize(label, cv2.FONT_HERSHEY_SIMPLEX, 0.5, 1)
y1_label = max(y1 - th - 5, 0)
cv2.rectangle(img, (x1, y1), (x1 + tw, y1_label), color, -1)
cv2.putText(img, label, (x1, y1 - 5),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255,255,255), 1)
return img
在控制台输出中添加更多诊断信息:
python复制print(f"[Stats] Frame: {frame_id:06d} | "
f"Infer: {infer_ms:.1f}ms | "
f"FPS: {current_fps:.1f} | "
f"Det: {len(boxes)} | "
f"Mem: {psutil.virtual_memory().percent}%")
通过实验测得各阶段耗时占比(640x640输入):
code复制预处理:15.2ms(28%)
推理:32.4ms(60%)
后处理:6.1ms(11%)
可视化:1.3ms(1%)
优化方向优先级:
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| 无视频信号 | 1. 设备未识别 2. 权限不足 |
1. 检查dmesg输出 2. sudo chmod 666 /dev/video0 |
| 画面卡顿 | 1. USB带宽不足 2. 分辨率过高 |
1. 换USB3.0接口 2. 降低至720P |
| 颜色异常 | 像素格式不匹配 | v4l2-ctl --set-fmt-video=... |
当遇到检测结果异常时,按以下步骤排查:
python复制print("Input:", sess.get_inputs()[0].shape)
print("Outputs:", [out.shape for out in sess.get_outputs()])
完成基础闭环后,建议从以下方向深入:
这个最小闭环的实现,就像搭建好了赛车的基本框架。接下来要做的,就是逐步换上更强大的引擎(QNN加速)、更精准的导航系统(模型优化)和更可靠的传动装置(工程化部署)。