前言
本文为零基础小白记录K210视觉模块学习笔记,前面多为手写笔记,自用为主,各位朋友、老师如果认可这样的记录也可参考与交流学习。
学习笔记、学习笔记、学习笔记,不是经验分享,各位大佬轻喷(T▽T)
硬件准备
本次学习的K210为野火科技的K210AI视觉相机,野火官网上有详实的资料,本文的本质为个人对于这些资料的整理与总结,获取资料链接见下。侵权即删。
基本外设学习
图像识别
本部分开始不再为手写笔记
以21年电赛送药小车需要的数字识别功能为例进行学习
图像识别的定性流程描述
- 打开摄像头,实时拍摄画面;
- 把画面处理成适合识别的格式(比如转成黑白、调整大小);
- 用训练好的模型 “认出” 画面中的手写数字(0-9);
- 在屏幕上显示拍摄的画面和识别出的数字。
应用模块介绍
import sensor, image, time, lcd # 摄像头、图像处理、时间、屏幕控制工具
from maix import KPU # 硬件加速器(专门快速处理识别任务的“小芯片”)
import gc # 内存清理工具(防止内存不够用)
各模块应用方法及一般操作流程
摄像头与屏幕初始化
lcd.init(freq=15000000) # 初始化屏幕(设置屏幕刷新率,让显示更稳定)
sensor.reset(dual_buff=True) # 初始化摄像头(双缓冲模式让画面更流畅)
sensor.set_pixformat(sensor.RGB565) # 摄像头画面设为彩色(后续会转黑白)
sensor.set_framesize(sensor.QVGA) # 画面大小设为320x240像素
sensor.set_windowing((224, 224)) # 只取画面中间224x224的区域(聚焦中间,减少干扰)
sensor.skip_frames(time = 1000) # 摄像头启动后等1秒(让画面稳定)
clock = time.clock() # 创建“计时器”(用来计算每秒处理多少帧画面)
加载模型
kpu = KPU() # 启动硬件加速器(K210的专用识别芯片,速度更快)
# 从SD卡加载“手写数字识别模型”(模型文件存在SD卡的/sd/KPU/mnist/路径下)
kpu.load_kmodel("/sd/KPU/mnist/uint8_mnist_cnn_model.kmodel")
这里的.kmodel
文件是提前训练好的 “模型”,相当于给开发板植入了 “认数字的经验”—— 它是通过大量手写数字图片训练出来的,能看懂我们写的 0-9。具体模型如何训练后续内容介绍。
主循环:实时识别数字
while True: # 无限循环(一直工作)
gc.collect() # 清理内存(防止卡壳)
img = sensor.snapshot() # 从摄像头拍一张画面(就像按快门拍照)
# 第一步:处理画面,让模型更容易识别
img_mnist1 = img.to_grayscale(1) # 转成黑白画面(手写数字识别只需要黑白)
img_mnist2 = img_mnist1.resize(112, 112) # 把画面缩放到112x112像素(模型要求的大小)
img_mnist2.invert() # 黑白反转(因为模型训练时用的是“黑底白字”,这里统一格式)
img_mnist2.strech_char(1) # 增强对比度(让数字更清晰,减少阴影干扰)
img_mnist2.pix_to_ai() # 转换格式(让KPU硬件加速器能处理)
# 第二步:用模型识别数字
out = kpu.run_with_output(img_mnist2, getlist=True) # 让模型分析处理后的画面
# out是一个列表,包含模型对0-9每个数字的“置信度”(比如out[5]是认为是5的可能性)
# 第三步:找到最可能的数字
max_mnist = max(out) # 找置信度最高的值(哪个数字可能性最大)
index_mnist = out.index(max_mnist) # 找到这个最高值对应的数字(0-9中的一个)
# 第四步:显示识别结果
display_str = "num: %d" % index_mnist # 生成要显示的文字(比如“num: 5”)
print(display_str) # 在终端打印结果(方便调试)
# 在画面上画文字,显示识别出的数字(位置在(4,3),黑色,大小2倍)
img.draw_string(4, 3, display_str, color=(0,0,0), scale=2)
lcd.display(img) # 把带结果的画面显示在屏幕上
# 程序结束时释放资源(实际中因为while True循环,这里不会执行)
kpu.deinit()
自学习功能
自学习定性流程描述
- 学习阶段:按步骤给它拍几种东西的照片(比如先拍 3 张苹果,再拍 3 张香蕉,再拍 3 张橙子),它会 “记住” 每种东西的特点。
- 识别阶段:学完后,拿任何一种之前教过的东西在摄像头前,它能自动说出 “这是第 1 类(苹果)”“这是第 2 类(香蕉)”。
- 操作阶段:全程用
boot键
(开发板上的一个按键)控制,短按切换步骤,长按重新开始。
操作说明
程序有 5 种 “状态”(可以理解为不同的工作模式),按boot键
切换,具体操作如下:
模式(状态) | 作用 | 如何进入 / 操作 |
---|---|---|
初始化(INIT) | 准备阶段,显示程序说明 | 上电后自动进入,短按boot键 进入训练 1 |
训练 1(TRAIN_CLASS_1) | 学习第 1 类东西(比如苹果) | 短按boot键 拍照片(拍 5 张),拍完自动进入训练 2 |
训练 2(TRAIN_CLASS_2) | 学习第 2 类东西(比如香蕉) | 同上,拍 5 张后进入训练 3 |
训练 3(TRAIN_CLASS_3) | 学习第 3 类东西(比如橙子) | 同上,拍 5 张后进入分类模式 |
分类(CLASSIFY) | 自动识别当前东西属于哪一类 | 摄像头对着东西,屏幕会显示 “class:1”“class:2” 等结果 |
特殊操作:任何时候长按boot键
(约 3 秒),会重新开始整个流程。
应用模块
import gc, lcd, sensor, time # 内存清理、屏幕、摄像头、时间工具
from maix import GPIO, KPU # 按键控制、KPU(“学习大脑”)
from board import board_info # 开发板引脚信息
from fpioa_manager import fm # 引脚映射工具
from image import Image # 图片处理工具
各模块应用方法及一般操作流程
工作模式定义
class STATE(object):
IDLE = 0 # 空闲状态(初始准备)
INIT = 1 # 初始化模式(显示操作说明)
TRAIN_CLASS_1 = 2 # 训练第1类(比如学习“苹果”)
TRAIN_CLASS_2 = 3 # 训练第2类(比如学习“香蕉”)
TRAIN_CLASS_3 = 4 # 训练第3类(比如学习“橙子”)
STATE_MAX = 6 # 状态总数(用于限制范围)
事件发生(出发切换信号)定义
class EVENT(object):
POWER_ON = 0 # 上电事件(程序启动)
BOOT_KEY = 1 # 短按boot键(切换模式)
BOOT_KEY_LONG_PRESS = 2 # 长按boot键(重新开始)
EVENT_NEXT_MODE = 3 # 切换到下一模式(内部信号)
EVENT_MAX = 4 # 事件总数
状态机
class StateMachine(object):
def __init__(self, state_handlers, event_handlers, transitions):
self.previous_state = STATE.IDLE # 上一个状态
self.current_state = STATE.IDLE # 当前状态
self.state_handlers = state_handlers # 状态对应的操作
self.event_handlers = event_handlers # 事件对应的操作
self.transitions = transitions # 状态切换规则表
# 核心功能:根据当前状态和事件,切换到下一状态
def emit_event(self, event):
next_state = self.get_next_state(self.current_state, event) # 查规则表
if next_state: # 如果有对应的下一状态
self.previous_state = self.current_state # 记录上一状态
self.current_state = next_state # 更新当前状态
# 执行“离开上一状态”“进入下一状态”的操作
self.exit_state_action(self.previous_state, event)
self.enter_state_action(self.current_state, event)
“状态机” 是整个程序的 “大脑”,它有一张 “规则表”(transitions
),规定:
“在 A 状态下,收到 B 事件,就切换到 C 状态”。
比如规则表中写了 [STATE.INIT, STATE.TRAIN_CLASS_1, EVENT.EVENT_NEXT_MODE]
,意思是:“在初始化状态(INIT)下,收到‘切换下一模式’事件,就切换到训练第 1 类(TRAIN_CLASS_1)”。
定义每个状态的具体操作
初始化模式
def enter_state_init(self, state, event):
print("enter state: init")
global img_init
img_init = Image(size=(lcd.width(), lcd.height())) # 创建初始化画面
def state_init(self, state, event):
# 短按boot键:进入下一模式(训练1);长按:重新开始
if event == EVENT.BOOT_KEY:
self.emit_event(EVENT.EVENT_NEXT_MODE) # 切换到训练1
elif event == EVENT.BOOT_KEY_LONG_PRESS:
restart(self) # 重新开始
def exit_state_init(self, state, event):
print("exit state: init")
del img_init # 清理初始化画面
训练模式
以训练1为例
def enter_state_train_class_1(self, state, event):
print("enter state: train class 1")
global train_pic_cnt, central_msg, bottom_msg
train_pic_cnt = 0 # 重置拍照计数(每个类别拍5张)
central_msg = "Train class 1" # 屏幕中间显示“训练第1类”
bottom_msg = "Take pictures of 1st class" # 底部提示“拍第1类的照片”
def state_train_class_1(self, state, event):
global train_pic_cnt, features
if event == EVENT.BOOT_KEY: # 短按boot键
if train_pic_cnt < max_train_pic: # 没拍够5张
img = sensor.snapshot() # 拍一张照片
feature = kpu.run_with_output(img, get_feature=True) # 提取特征(物体的“特点”)
features[0].append(feature) # 存到第1类的“特征库”(相当于“教材”)
train_pic_cnt += 1 # 计数+1
bottom_msg = "Class 1: #P{}".format(train_pic_cnt) # 显示“已拍第x张”
else: # 拍够5张
self.emit_event(EVENT.EVENT_NEXT_MODE) # 自动进入训练第2类
分类模式
def enter_state_classify(self, state, event):
global central_msg, bottom_msg
central_msg = "Classification" # 屏幕显示“开始分类”
bottom_msg = "Training complete! Start classification" # 提示“训练完成,开始识别”
def state_classify(self, state, event):
global bottom_msg
img = sensor.snapshot() # 实时拍画面
feature = kpu.run_with_output(img, get_feature=True) # 提取当前物体的特征
# 和特征库对比,找最像的类别
high_score = 0 # 最高相似度
best_class = 0 # 最像的类别
for i in range(len(features)): # 遍历每个类别的特征库
for f in features[i]: # 遍历该类别的每张照片特征
score = kpu.feature_compare(f, feature) # 计算相似度(0-100,越高越像)
if score > high_score:
high_score = score
best_class = i # 更新最像的类别
# 显示结果(如果相似度高于阈值,认为匹配成功)
if high_score > THRESHOLD:
bottom_msg = "class:{},score:{:2.1f}".format(best_class + 1, high_score)
else:
bottom_msg = "Unknown" # 不认识的物体
作用:实时拍摄画面,提取当前物体的特征,和 “特征库”(训练时存的 5 张 / 类)对比,找到最像的类别,显示 “class:1”(第 1 类)及相似度。
按键处理(长短按区分)
class Button(object):
DEBOUNCE_THRESHOLD = 30 # 消抖阈值(忽略30ms内的抖动)
LONG_PRESS_THRESHOLD = 1000 # 长按阈值(按1秒以上算长按)
def key_down(self, delta): # 按键被按下时
self._key_ticks += delta # 累计按下时间
if self._key_ticks > LONG_PRESS_THRESHOLD: # 按了1秒以上
self.st.emit_event(EVENT.BOOT_KEY_LONG_PRESS) # 发送“长按事件”
# 否则按“短按”处理
def key_up(self, delta): # 按键松开时
if 30 < self._key_ticks < 1000: # 按下时间在30ms-1秒之间
self.st.emit_event(EVENT.BOOT_KEY) # 发送“短按事件”
主循环
状态机创建完毕,主程序如下
# 初始化全局变量
features = [] # 特征库(存3类物体的特征,每类5张)
THRESHOLD = 98.5 # 相似度阈值(高于这个值才认为匹配)
max_train_pic = 5 # 每个类别拍5张照片
central_msg = None # 屏幕中间显示的文字
bottom_msg = None # 屏幕底部显示的文字
# 配置硬件
fm.register(board_info.BOOT_KEY, fm.fpioa.GPIOHS0) # 绑定boot键到引脚
boot_gpio = GPIO(GPIO.GPIOHS0, GPIO.IN) # 设置boot键为输入(检测按下)
lcd.init() # 初始化屏幕
sensor.reset() # 初始化摄像头
sensor.set_pixformat(sensor.RGB565) # 摄像头拍彩色画面
sensor.set_framesize(sensor.QVGA) # 画面大小320x240
sensor.set_windowing((224, 224)) # 聚焦中间224x224区域(减少干扰)
sensor.set_vflip(1) # 画面上下翻转(方便拍摄)
sensor.skip_frames(time=500) # 摄像头预热0.5秒
# 加载“学习模型”(KPU的基础大脑)
kpu = KPU()
kpu.load_kmodel("/sd/KPU/self_learn_classifier/mb-0.25.kmodel")
# 启动状态机
state_machine = StateMachine(state_handlers, event_handlers, transitions)
state_machine.emit_event(EVENT.POWER_ON) # 上电事件:从空闲进入初始化模式
# 主循环(程序一直运行的部分)
btn_ticks_prev = time.ticks_ms()
boot_btn = Button(state_machine)
while True:
gc.collect() # 清理内存
# 检测按键状态(短按/长按)
btn_ticks_cur = time.ticks_ms()
delta = time.ticks_diff(btn_ticks_cur, btn_ticks_prev) # 计算时间差
btn_ticks_prev = btn_ticks_cur
if boot_gpio.value() == 0: # boot键被按下(低电平)
boot_btn.key_down(delta) # 处理按下事件
else: # 按键松开
boot_btn.key_up(delta) # 处理松开事件
# 根据当前状态执行对应功能
if state_machine.current_state == STATE.INIT:
loop_init() # 初始化模式:显示操作说明
elif state_machine.current_state == STATE.CLASSIFY:
loop_classify() # 分类模式:实时识别并显示结果
elif state_machine.current_state in [STATE.TRAIN_CLASS_1, STATE.TRAIN_CLASS_2, STATE.TRAIN_CLASS_3]:
loop_capture() # 训练模式:显示提示并拍照
执行流程
初始化硬件(屏幕、摄像头、按键)和变量(特征库、阈值等)。
加载 “学习模型”(mb-0.25.kmodel
):这是 KPU 的 “基础大脑”,负责提取物体的特征(比如形状、颜色分布)。
启动状态机,从 “初始化模式” 开始。
进入无限循环:不断检测按键(短按 / 长按),根据当前状态执行对应功能(显示说明 / 拍照训练 / 实时识别)。
完整代码可到野火资料官网自取
使用建议
本部分内容不必完全记忆,建议直接使用并在原程序基础上修改
模型训练
本部分请见 @欠羽 大佬的https://blog.csdn.net/adas323/article/details/126534658?spm=1001.2014.3001.5502的整理与总结。