在 CanMV K210 入门和综合项目中,YOLO 模型物体识别实验是一个很适合拆解的 AI 视觉案例。它不是只验证某一个模块能否工作,而是把摄像头采集、模型加载、图像预处理、KPU 推理、YOLO 后处理、检测框绘制和 LCD 显示串成一条完整流程。通过这个实验,可以理解 K210 如何在边缘端完成基础目标检测任务。
本实验使用 CanMV K210 运行 YOLO 目标检测程序。代码加载 /sd/voc20_detect.kmodel 模型文件,使用 KPU 执行神经网络推理,并在摄像头画面中标出目标类别、位置和运行帧率。实验重点是理解类别标签、模型文件路径、anchor 参数、置信度阈值、NMS 后处理和检测框绘制之间的关系。
可以在这里放置实验运行效果截图。重点观察 LCD 画面中是否出现摄像头实时画面、绿色目标框、类别名称和左上角 FPS 信息。
| 学习目标 | 说明 |
|---|---|
| 理解目标检测流程 | 掌握摄像头采集、模型推理、后处理和显示标注之间的关系 |
| 掌握 KPU 模型加载 | 使用 KPU 加载 kmodel 文件,并执行 YOLO 目标检测 |
| 理解 YOLO 参数 | 认识类别标签、anchor、阈值、NMS 和检测框的作用 |
| 完成多类别标注 | 在图像中绘制检测框和类别文字,显示识别结果 |
| 建立 AI 视觉调试思路 | 能从模型路径、输入尺寸、光照、目标大小和阈值等方向排查问题 |
文章目录
理论基础
YOLO 目标检测和普通图像分类不同。图像分类只需要回答"这张图里主要是什么",而目标检测需要进一步回答"图中有哪些目标、每个目标在哪里、属于哪个类别、置信度大约是多少"。因此,YOLO 识别结果通常包含检测框坐标、宽高、类别编号和置信度等信息。
在 K210 上运行 YOLO 模型时,摄像头负责采集图像,KPU 负责执行神经网络推理,YOLO 后处理负责从模型输出中筛选目标框,LCD 负责把结果显示出来。程序中的 obj_name 保存 20 个类别名称,anchor 保存目标框先验参数,threshold=0.5 用于过滤低置信度结果,nms_value=0.2 用于去掉重叠度较高的重复检测框。
本实验使用的模型输入尺寸为 320x256,摄像头采集尺寸为 QVGA,也就是 320x240。代码中创建了一个 od_img = image.Image(size=(320,256)),再把摄像头图像绘制到 od_img 中,之后调用 pix_to_ai() 转换为 KPU 适合处理的数据格式。这个步骤可以理解为把普通摄像头图像转换成模型推理需要的输入图像。
摄像头采集
sensor.snapshot()
图像准备
draw_image 到 320x256
AI 格式转换
pix_to_ai()
KPU 模型推理
run_with_output()
YOLO 后处理
regionlayer_yolo2()
结果筛选
threshold + NMS
LCD 标注显示
矩形框 / 类别名 / FPS
模型文件
/sd/voc20_detect.kmodel
类别标签
VOC 20 类
anchor 参数
5 组先验框
这张流程图展示的是 AI 视觉实验中的数据流。摄像头图像不是直接显示后结束,而是先进入 KPU 进行模型推理,再经过 YOLO 后处理得到检测框。程序拿到检测结果后,再把检测框和类别名称画回原始图像,最后送到 LCD 显示。这个流程是后续 AI 分类、目标跟踪、物体计数和视觉联动控制的重要基础。
硬件设施
本实验围绕 CanMV K210、摄像头、LCD、SD 卡模型文件和 KPU 推理展开。代码没有使用按键、蜂鸣器、电机、传感器或其他 GPIO 外设,因此硬件重点应放在摄像头连接、LCD 显示、模型文件路径和电源稳定性上。
可以在这里放置 CanMV K210、摄像头、LCD 和 SD 卡的整体连接照片。检查时重点关注摄像头排线方向、LCD 是否正常安装、SD 卡是否插入,以及模型文件是否放在代码指定路径。
| 硬件 / 软件 | 作用 | 说明 |
|---|---|---|
| CanMV K210 开发板 | 实验运行平台 | 执行 MicroPython 程序,完成图像采集、KPU 推理和结果显示 |
| 摄像头模块 | 图像采集 | 通过 sensor.snapshot() 获取实时画面 |
| LCD 显示屏 | 结果显示 | 显示摄像头画面、检测框、类别名称和 FPS 信息 |
| KPU 神经网络加速器 | AI 推理 | 加载 kmodel 模型并执行边缘端神经网络运算 |
| SD 卡 | 模型文件存储 | 存放 /sd/voc20_detect.kmodel 模型文件 |
sensor |
摄像头控制模块 | 初始化摄像头,设置图像格式和分辨率 |
image |
图像处理模块 | 创建输入图像、绘制检测框和类别文字 |
lcd |
LCD 显示模块 | 初始化屏幕并显示图像 |
maix.KPU |
KPU 推理模块 | 加载模型、初始化 YOLO2 参数并运行推理 |
gc |
内存回收模块 | 在循环中释放临时对象,降低内存压力 |
根据代码可以确认,摄像头由 sensor.reset() 初始化,LCD 由 lcd.init() 初始化,模型文件通过 kpu.load_kmodel("/sd/voc20_detect.kmodel") 从 SD 卡加载。模型路径是本实验中非常关键的资源配置,如果文件名、目录或 SD 卡状态不正确,程序会在模型加载阶段报错。
| 资源 / 接口 | 代码对象 | 对应硬件与说明 |
|---|---|---|
| 摄像头接口 | sensor.reset() / sensor.snapshot() |
初始化摄像头并持续采集图像帧 |
| LCD 接口 | lcd.init() / lcd.display(img) |
显示摄像头画面、检测框和文字标注 |
| 模型文件 | /sd/voc20_detect.kmodel |
YOLO 目标检测模型,需要放在 SD 卡指定路径 |
| KPU 对象 | kpu = KPU() |
创建神经网络推理对象 |
| 输入图像 | od_img = image.Image(size=(320,256)) |
准备模型推理使用的输入图像 |
| 类别标签 | obj_name |
保存 20 个可检测类别名称 |
| anchor 参数 | anchor |
YOLO2 后处理使用的先验框参数 |
摄像头采集到的画面尺寸为 320x240,KPU 模型推理使用的输入图像尺寸为 320x256。代码通过 od_img.draw_image(img, 0, 0) 把摄像头画面写入推理图像,再通过 od_img.pix_to_ai() 生成 AI 运算需要的数据格式。LCD 显示的仍然是原始摄像头图像 img,检测框和类别文字也绘制在 img 上,因此屏幕上看到的是带有标注结果的实时画面。
| 实验现象 | 正常表现 | 异常提示 |
|---|---|---|
| 程序启动 | 串口打印 ready load model |
如果停在模型加载处,检查 SD 卡和模型文件路径 |
| 摄像头采集 | LCD 显示实时画面 | 如果画面黑屏或卡住,检查摄像头排线和初始化参数 |
| 检测到目标 | 画面中出现绿色检测框和类别文字 | 如果没有检测框,检查目标类别、光照、距离和阈值 |
| FPS 显示 | 左上角显示当前帧率 | 帧率过低时减少无关打印或绘制内容 |
| 串口输出 | 检测到目标时打印 dect 结果 |
如果串口没有任何输出,先确认程序是否进入主循环 |
| 长时间运行 | 画面持续刷新并周期性回收内存 | 如果运行一段时间后卡顿,关注内存和模型推理耗时 |
软件代码
本实验的软件部分以 YOLO 目标检测程序为核心。程序先初始化 LCD 和摄像头,再创建 KPU 对象并加载模型文件,随后初始化 YOLO2 参数。主循环中不断采集图像、准备 KPU 输入、执行推理、获取检测结果、绘制检测框和类别名称,最后将图像显示到 LCD。
| 软件环境 | 作用 | 检查重点 |
|---|---|---|
| CanMV IDE | 编辑、运行和调试 K210 程序 | 能识别开发板串口,并能正常运行基础示例 |
| CanMV / MaixPy 固件 | 提供 sensor、image、lcd、KPU 等模块 |
固件需要支持 KPU 模型加载和 YOLO2 后处理 |
| SD 卡 | 存放 kmodel 模型文件 | /sd/voc20_detect.kmodel 路径需要与代码一致 |
| 摄像头驱动 | 提供实时图像输入 | sensor.snapshot() 能正常返回图像 |
| LCD 显示 | 显示识别结果 | lcd.display(img) 能正常刷新画面 |
| 串口终端 | 查看调试信息 | 能看到模型加载、计数、检测结果等输出 |
python
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# ----湖南创乐博智能科技有限公司----
# 文件名:4.6_Object_Detect.py
# 版本:V2.0
# author: zhulin
# 说明:CanMV K210 YOLO 模型物体识别实验
#####################################################
import sensor, image, time, lcd
from maix import KPU
import gc
# =========================
# LCD 与摄像头初始化
# =========================
lcd.init() # 初始化 LCD 显示屏
sensor.reset() # 复位并初始化摄像头
sensor.set_pixformat(sensor.RGB565) # 设置摄像头输出格式为 RGB565
sensor.set_framesize(sensor.QVGA) # 设置摄像头输出大小为 QVGA,也就是 320x240
sensor.skip_frames(time=1000) # 等待摄像头稳定
clock = time.clock() # 创建 clock 对象,用来计算帧率
# =========================
# 模型输入图像准备
# =========================
# 检测模型需要 320x256 图像输入,这里初始化一个 image 对象
od_img = image.Image(size=(320, 256))
# =========================
# 类别标签与 YOLO anchor
# =========================
obj_name = (
"aeroplane", "bicycle", "bird", "boat", "bottle",
"bus", "car", "cat", "chair", "cow",
"diningtable", "dog", "horse", "motorbike", "person",
"pottedplant", "sheep", "sofa", "train", "tvmonitor"
)
anchor = (
1.3221, 1.73145,
3.19275, 4.00944,
5.05587, 8.09892,
9.47112, 4.84053,
11.2364, 10.0071
)
# =========================
# KPU 模型加载与 YOLO2 初始化
# =========================
kpu = KPU()
print("ready load model")
# 加载 SD 卡中的 kmodel 模型文件
# 如果模型烧录到 Flash,也可以使用 kpu.load_kmodel(0x300000, 1536936)
kpu.load_kmodel("/sd/voc20_detect.kmodel")
# YOLO2 初始化
kpu.init_yolo2(
anchor,
anchor_num=5,
img_w=320,
img_h=240,
net_w=320,
net_h=256,
layer_w=10,
layer_h=8,
threshold=0.5,
nms_value=0.2,
classes=20
)
# =========================
# 主循环
# =========================
i = 0
try:
while True:
i += 1
print("cnt :", i)
clock.tick() # 更新帧率统计
img = sensor.snapshot() # 获取一帧摄像头图像
# 将摄像头图像写入模型输入图像
od_img.draw_image(img, 0, 0)
# 转换为 KPU AI 运算需要的数据格式
od_img.pix_to_ai()
# 运行 KPU 推理
kpu.run_with_output(od_img)
# YOLO2 后处理,得到检测结果
dect = kpu.regionlayer_yolo2()
fps = clock.fps()
# 如果检测到目标,绘制检测框和类别名称
if len(dect) > 0:
print("dect:", dect)
for l in dect:
img.draw_rectangle(l[0], l[1], l[2], l[3], color=(0, 255, 0))
img.draw_string(
l[0],
l[1],
obj_name[l[4]],
color=(0, 255, 0),
scale=1.5
)
# 显示 FPS
img.draw_string(
0,
0,
"%2.1ffps" % (fps),
color=(0, 60, 128),
scale=1.0
)
# LCD 显示最终画面
lcd.display(img)
# 回收内存
gc.collect()
except KeyboardInterrupt:
print("object detect stopped")
finally:
# 释放 KPU 模型资源
kpu.deinit()
gc.collect()
这段代码可以分成摄像头初始化、模型输入准备、类别与 anchor 配置、KPU 模型加载、YOLO2 初始化、循环推理和结果显示几个部分。sensor.set_pixformat(sensor.RGB565) 设置摄像头图像格式,sensor.set_framesize(sensor.QVGA) 设置采集尺寸为 320x240,sensor.skip_frames(time=1000) 用于等待摄像头曝光和画面稳定。
obj_name 保存的是 VOC 20 类目标名称,检测结果中的类别编号会用它转换成文字标签。anchor 保存 5 组先验框参数,配合 anchor_num=5 用于 YOLO2 后处理。threshold=0.5 表示置信度低于 0.5 的结果会被过滤,nms_value=0.2 用于抑制重叠检测框,避免同一个目标被多次标注。
| 函数 / 语句 | 功能 | 对应现象 |
|---|---|---|
lcd.init() |
初始化 LCD 显示屏 | 屏幕准备显示摄像头画面 |
sensor.reset() |
初始化摄像头 | 摄像头进入可采集状态 |
sensor.set_pixformat(sensor.RGB565) |
设置图像格式 | 获取彩色图像 |
sensor.set_framesize(sensor.QVGA) |
设置图像大小 | 摄像头输出 320x240 图像 |
image.Image(size=(320,256)) |
创建模型输入图像 | 为 KPU 推理准备输入缓冲 |
kpu.load_kmodel() |
加载 kmodel 模型文件 | 模型进入 KPU,可执行推理 |
kpu.init_yolo2() |
初始化 YOLO2 后处理参数 | 设置 anchor、类别数、阈值和 NMS |
sensor.snapshot() |
获取摄像头画面 | 得到当前帧图像 |
od_img.pix_to_ai() |
转换为 AI 运算格式 | 输入图像适配 KPU 推理 |
kpu.run_with_output() |
执行模型推理 | KPU 输出检测相关结果 |
kpu.regionlayer_yolo2() |
执行 YOLO2 后处理 | 得到检测框、类别和置信度信息 |
img.draw_rectangle() |
绘制检测框 | LCD 中目标区域出现绿色矩形 |
img.draw_string() |
绘制类别和 FPS | 显示类别名称和帧率 |
lcd.display(img) |
刷新 LCD 画面 | 屏幕显示带标注的图像 |
gc.collect() |
回收内存 | 降低循环运行时的内存压力 |
kpu.deinit() |
释放 KPU 模型资源 | 程序退出时释放模型占用 |
主循环中的逻辑比较直接。每一轮先获取摄像头画面,再把图像写入 od_img,随后进行 AI 格式转换和 KPU 推理。regionlayer_yolo2() 返回检测结果后,程序判断结果数量是否大于 0,如果检测到目标,就在原始图像 img 上绘制矩形框和类别名称。最后,程序把 FPS 画到左上角,并通过 lcd.display(img) 显示到屏幕上。
代码中使用 try...finally 包住主循环,是为了保证程序停止时能够执行 kpu.deinit()。目标检测模型会占用 KPU 资源,长时间调试或多次运行实验时,退出前释放资源可以让后续实验更加稳定。
扩展应用
YOLO 物体识别实验常见问题集中在模型文件、摄像头画面、目标条件、阈值参数和运行性能几个方面。排查时应先确认模型能否加载,再确认摄像头画面是否正常,最后再调整识别阈值、目标距离和光照环境。
| 问题现象 | 可能原因 | 处理思路 |
|---|---|---|
| 程序无法运行 | 文件没有传到开发板、固件不支持相关模块、库文件缺失 | 确认主程序、模型文件和固件环境正确,查看具体报错行号 |
| 模型加载失败 | /sd/voc20_detect.kmodel 不存在,或 SD 卡未识别 |
将模型文件放到 SD 卡根目录,并确认文件名与代码完全一致 |
| LCD 没有画面 | LCD 未初始化成功、摄像头采集失败、程序卡在模型加载处 | 先运行简单摄像头显示程序,确认 lcd.display(sensor.snapshot()) 正常 |
| 画面偏暗或颜色异常 | 光照不足、摄像头未稳定、图像格式设置不合适 | 增强环境光,等待摄像头稳定,确认使用 RGB565 格式 |
| 检测不到目标 | 目标不在 VOC 20 类中、目标太小、角度不合适、阈值过高 | 选择模型支持的类别,将目标放大并保持完整,必要时降低 threshold |
| 检测框重复较多 | NMS 参数不合适,同一目标被多次保留 | 适当调整 nms_value,观察重复框数量变化 |
| 类别显示错误 | 模型类别顺序与 obj_name 顺序不一致 |
确认模型训练类别顺序与代码中的类别列表一致 |
| 识别结果不稳定 | 光照变化、目标遮挡、距离过远或画面抖动 | 固定摄像头位置,保持目标清晰完整,减少背景干扰 |
| 运行帧率较低 | KPU 推理、图像绘制、串口打印和 LCD 刷新都有开销 | 减少无关打印,减少复杂绘制,保持输入尺寸与模型匹配 |
| 长时间运行后卡顿 | 内存碎片或临时对象积累 | 保留 gc.collect(),减少循环中不必要的对象创建 |
YOLO 物体识别实验的价值不只是复现检测框,而是建立"视觉输入、模型判断、结果标注、外设联动"的项目思路。当前代码把识别结果显示在 LCD 上,后续可以把检测到的类别继续传递给电机、蜂鸣器、舵机、串口通信或网络上传模块。
可以在这里放置扩展示意图,例如"摄像头识别目标后控制舵机、蜂鸣器或小车动作"的项目结构图。
| 应用场景 | 实现思路 | 可扩展能力 |
|---|---|---|
| 多类别物体识别 | 使用 VOC 20 类模型识别常见物体 | 可统计类别出现次数,显示到 LCD 或串口 |
| 智能分拣 | 根据识别类别控制舵机或电机分流 | 可与步进电机、舵机、传送带实验联动 |
| 人体检测提醒 | 当类别为 person 时触发提示 |
可扩展蜂鸣器、RGB 灯或消息上传 |
| 机器人视觉感知 | 根据目标位置判断前方物体 | 可与小车运动控制结合,实现避障或跟随 |
| 摄像头云台跟踪 | 根据检测框中心点偏移控制云台转向 | 可结合步进电机或舵机实验实现目标跟随 |
| 安防检测 | 检测到指定类别后保存日志或发出提示 | 可扩展 OLED 状态显示、蜂鸣器报警或联网通知 |
| AI 教学示例 | 展示模型加载、推理、后处理和结果绘制 | 可用于讲解 KPU、YOLO、anchor、NMS 和置信度 |
| 边缘视觉部署 | 在本地完成图像识别,不依赖电脑端推理 | 可扩展为离线识别、现场检测或嵌入式交互项目 |
从工程角度看,建议保持"采集、推理、判断、显示、执行"的分层结构。摄像头只负责图像输入,KPU 只负责模型推理,YOLO 后处理只负责生成目标结果,显示函数负责可视化,业务逻辑再根据类别或位置决定后续动作。这样在后续扩展时,可以单独更换模型、调整阈值、修改显示样式,或者把识别结果交给其他硬件模块,而不需要重写整份程序。
总结
本实验通过 CanMV K210 完成了 YOLO 模型物体识别的基础验证,核心内容包括摄像头初始化、LCD 显示、kmodel 模型加载、KPU 推理、YOLO2 参数配置、检测结果后处理、检测框绘制、类别标注和 FPS 显示。它展示了 K210 在嵌入式 AI 视觉项目中的典型使用方式,也为后续综合项目留下了清晰入口。
如果实验现象与预期不一致,应优先检查模型文件路径、SD 卡状态、摄像头画面和 LCD 显示,再结合串口输出定位模型加载、推理结果和检测参数问题。掌握这套调试顺序后,后续的目标检测、人体识别、视觉跟随、智能分拣和边缘 AI 交互项目都可以围绕同一条主线继续扩展。