【电赛学习笔记】K210视觉模块(自用为主)

前言

本文为零基础小白记录K210视觉模块学习笔记,前面多为手写笔记,自用为主,各位朋友、老师如果认可这样的记录也可参考与交流学习。

学习笔记、学习笔记、学习笔记,不是经验分享,各位大佬轻喷(T▽T)

硬件准备

本次学习的K210为野火科技的K210AI视觉相机,野火官网上有详实的资料,本文的本质为个人对于这些资料的整理与总结,获取资料链接见下。侵权即删。

野火论坛 — 野火产品资料下载中心 文档

基本外设学习

图像识别

本部分开始不再为手写笔记

以21年电赛送药小车需要的数字识别功能为例进行学习

图像识别的定性流程描述

  1. 打开摄像头,实时拍摄画面;
  2. 把画面处理成适合识别的格式(比如转成黑白、调整大小);
  3. 用训练好的模型 “认出” 画面中的手写数字(0-9);
  4. 在屏幕上显示拍摄的画面和识别出的数字。

应用模块介绍

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()

自学习功能

自学习定性流程描述

  1. 学习阶段:按步骤给它拍几种东西的照片(比如先拍 3 张苹果,再拍 3 张香蕉,再拍 3 张橙子),它会 “记住” 每种东西的特点。
  2. 识别阶段:学完后,拿任何一种之前教过的东西在摄像头前,它能自动说出 “这是第 1 类(苹果)”“这是第 2 类(香蕉)”。
  3. 操作阶段:全程用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的整理与总结。

### 关于K210视觉模块编程的相关信息 #### 配置串口通信 对于初次接触K210视觉识别模块的学习者来说,了解如何配置并使用其串口显得尤为重要。这不仅有助于理解设备的基础操作方式,也为后续更复杂的项目打下坚实基础[^1]。 ```python import utime as time from machine import UART uart = UART(UART.UART1, baudrate=115200, bits=8, parity=None, stop=1) def send_data(data): uart.write(data) def receive_data(): if uart.any() > 0: return uart.read() else: return None send_data(b'Hello K210') time.sleep(0.1) # 等待接收响应 response = receive_data() if response is not None: print('Received:', str(response)) else: print('No data received.') ``` 这段Python代码展示了如何初始化UART对象用于设置波特率和其他参数,并定义了发送和接收函数以便与外部设备交换信息。 #### 实现人脸检测功能 为了快速入门K210的人脸检测能力,可以利用预训练模型简化开发流程。通过打包特定目录下的`.kmodel`文件与其他必要资源形成`.kfpkg`格式的固件包,便于部署至目标硬件平台执行相应的图像处理任务[^3]。 ```bash # 假定当前工作目录位于face_model_at_0x300000/ zip face_detection.kfpkg kmodel flash-list.json ``` 上述命令行脚本说明了创建一个名为`face_detection.kfpkg`的压缩包的过程,其中包含了运行人脸识别所需的关键组件——机器学习模型及其对应的闪存映射描述符。 #### 多线程多模型并发处理 当涉及到更为复杂的应用场景时,比如同时加载多个神经网络模型来进行不同类型的物体分类或特征提取,则可能需要用到多线程技术提高效率。值得注意的是,在实际应用过程中可能会遇到诸如性能波动等问题,此时应当考虑优化算法结构或是增加样本数量以改善最终的效果稳定性[^4]。 ```c #include <pthread.h> // ...其他必要的头文件... void *thread_function(void *arg){ // 各自独立的任务逻辑... } int main(){ pthread_t thread_id; int ret; /* 创建新线程 */ ret = pthread_create(&thread_id, NULL, thread_function, (void *)NULL); if(ret != 0){ printf("Create pthread error!\n"); exit(-1); } /* 主线程继续做自己的事情 */ /* 等待子线程结束 */ pthread_join(thread_id, NULL); return 0; } ``` 此C语言片段提供了一个简单的例子来展示怎样在一个应用程序内部开启新的线程去异步完成某些耗时较长的操作,从而避免阻塞主线程的工作流。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值