【K230 CanMV】图像识别-AI Demo开发框架-人脸识别

引言:在当今人工智能技术迅速发展的背景下,计算机视觉已成为嵌入式系统应用的重要组成部分。K230 CanMV提供了一个强大的AI Demo开发框架,专注于实现低功耗、高性能的边缘设备图像处理与AI推理任务。本篇文档以人脸检测任务为核心,详解框架的工作原理、开发流程和应用方法,为开发者提供快速上手和深入理解的指导。

目录

一、AI Demo开发框架

1.工作原理流程讲解

1.1.Sensor 到 Frame

Sensor 数据采集

Frame 构建

1.2. Frame 到 AI

ConfigPreprocess

PreProcess

Inference(推理)

PostProcess

DrawResultK

1.3. AI 到 OSD

叠加信息(OSD)

1.4. OSD 到 Display

图像编码

显示输出

二、应用方法和示例

三、单模型任务详解

四、人脸检测详解

1. 类定义:FaceDetectionApp

1.1. 构造函数 (__init__)

1.2. config_preprocess

1.3. postprocess

1.4. draw_result

1.5. get_padding_param

2. 初始化部分

3. 主循环

异常处理

4. 核心逻辑流程

5. 关键库和模块


一、AI Demo开发框架

为了帮助用户简化 AI 部分的开发,基于 K230_CanMV 提供的 API 接口,我们搭建了配套的 AI 开发框架。框架结构如下图所示:

Camera 默认输出两路图像:一路格式为 YUV420SP (Sensor.YUV420SP),直接提供给 Display 显示;另一路格式为 RGBP888 (Sensor.RGBP888),则用于 AI 部分进行处理。AI 主要负责任务的前处理、推理和后处理流程。处理完成后,结果将绘制在 OSD 图像实例上,并发送给 Display 进行叠加显示。
接下来就是重点针对上述流程进行讲解了,ConfigPreprocess、PreProcess、Inference、PostProcess、DrawResultK。

1.工作原理流程讲解

在 K230 的 AI Demo 过程中,数据从 Sensor(传感器) 传递到 Display(显示设备) 是一个复杂的流水线。

1.1.Sensor 到 Frame

Sensor 数据采集
  • 输入源:通常是摄像头传感器,如 OV5640、IMX415 等,用来捕获实时视频流或图片。
  • 采样频率:根据应用场景设置帧率(如 30FPS 或 60FPS)。
  • 输出数据:传感器会输出 RAW 图像数据或经过 ISP(图像信号处理器)处理后的 RGB/YUV 数据。
Frame 构建
  • 传输接口:通过 MIPI 或其他接口将图像数据传递到处理器。
  • 缓存帧:将传感器数据存储到 Frame Buffer(帧缓存)中,供后续模块使用。

1.2. Frame 到 AI

ConfigPreprocess
  • 配置参数:加载和设置模型相关的预处理参数,如输入图像的尺寸、数据格式(RGB/BGR/YUV)、归一化范围(如 [0,1] 或 [-1,1])。
  • 模型加载:初始化 AI 模型的配置,包括加载推理引擎(如 K230 支持的 NPU 加速)。
PreProcess
  • 图像格式转换
    • 如果输入图像是 RGB 格式,但模型需要 BGR 格式,则需要进行通道交换。
    • 如果模型需要固定大小的输入(如 224x224),则需要裁剪或缩放图像。
  • 归一化
    • 将像素值从 [0,255] 映射到模型要求的范围。
    • 例如,减去均值并除以标准差:(pixel - mean) / std
  • 数据排布
    • 将图像从 HWC(Height, Width, Channel)转换为 CHW(Channel, Height, Width),以适应模型输入。
Inference(推理)
  • 输入数据:将预处理后的图像传入神经网络。
  • 执行推理:利用 K230 的 NPU(神经网络处理单元)加速推理过程,输出模型结果。
    • 检测任务:输出框坐标、置信度等。
    • 分类任务:输出类别索引及其概率。
    • 分割任务:输出像素级的分类图。
  • 推理引擎:K230 的 NPU 支持主流框架转换的模型(如 TensorFlow、PyTorch 通过工具链转换)。

    在 K230 的 AI Demo 过程中,数据从 Sensor(传感器) 传递到 Display(显示设备) 是一个复杂的流水线。以下是整个流程的详细分解

PostProcess
  • 解析模型输出
    • 检测任务:使用非极大值抑制(NMS)去掉冗余框。
    • 分类任务:根据概率排序,取最高的类别。
    • 分割任务:对输出 mask 映射到原图像尺寸。
  • 结果修正:对推理结果进行置信度筛选或几何校正。
DrawResultK
  • 结果绘制
    • 在检测任务中,绘制检测框和类别标签。
    • 在分割任务中,将 mask 叠加到原图。
    • 在分类任务中,显示类别及概率。
  • 显示格式:将结果转为适配屏幕分辨率和显示格式的图像。

1.3. AI 到 OSD

叠加信息(OSD)
  • 将推理结果和辅助信息(如帧率、时间戳、设备状态等)叠加到图像上。
  • 使用 K230 的硬件加速功能优化 OSD 的绘制效率。
  • 典型信息:
    • 检测框和标签:物体边界框及类别。
    • 时间戳:帧捕获时间。
    • 置信度:预测结果的可靠性。

1.4. OSD 到 Display

图像编码
  • 将带有 OSD 信息的帧编码为显示设备支持的格式(如 RGB888 或 YUV420)。
  • 如果是视频流输出,则可以选择编码为 H.264 或 H.265 格式。
显示输出
  • 接口:通过 HDMI、MIPI DSI 或其他显示接口输出图像到屏幕。
  • 显示刷新:将图像送入显示缓冲区,并根据显示器刷新率(如 60Hz)进行更新。

二、应用方法和示例

用户可根据具体的AI场景自写任务类继承AIBase,可以将任务分为如下四类:单模型任务、多模型任务,自定义预处理任务、无预处理任务。不同任务需要编写不同的代码实现,具体如下图所示:

 关于不同任务的介绍:

任务类型

任务描述

代码说明

单模型任务

该任务只有一个模型,只需要关注该模型的前处理、推理、后处理过程,此类任务的前处理使用Ai2d实现,可能使用一个Ai2d实例,也可能使用多个Ai2d实例,后处理基于场景自定义。

编写自定义任务类,主要关注任务类的config_preprocess、postprocess、以及该任务需要的其他方法如:draw_result等。
如果该任务包含多个Ai2d实例,则需要重写preprocess,按照预处理的顺序设置预处理阶段的计算过程。

自定义预处理任务

该任务只有一个模型,只需要关注该模型的前处理、推理、后处理过程,此类任务的前处理不使用Ai2d实现,可以使用ulab.numpy自定义,后处理基于场景自定义。

编写自定义任务类,主要关注任务类的preprocess、postprocess、以及该任务需要的其他方法如:draw_result等

无预处理任务

该任务只有一个模型且不需要预处理,只需要关注该模型的推理和后处理过程,此类任务一般作为多模型任务的一部分,直接对前一个模型的输出做为输入推理,后处理基于需求自定义。

编写自定义任务类,主要关注任务类的run(模型推理的整个过程,包括preprocess、inference、postprocess中的全部或某一些步骤)、postprocess、以及该任务需要的其他方法如:draw_results等

多模型任务

该任务包含多个模型,可能是串联,也可能是其他组合方式。对于每个模型基本上属于前三种模型中的一种,最后通过一个完整的任务类将上述模型子任务统一起来。

编写多个子模型任务类,不同子模型任务参照前三种任务定义。不同任务关注不同的方法。
编写多模型任务类,将子模型任务类统一起来实现整个场景。

三、单模型任务详解

单模型任务的伪代码结构如下:

  • 导入模块
    • PipeLine:用于图像处理流程,负责从传感器获取图像帧、显示图像等操作。
    • ScopedTiming:用于性能分析,测量代码块的执行时间,帮助调试和优化。
    • AIBase:AI 任务的基类,封装了模型加载、预处理、推理和后处理的通用逻辑。
    • Ai2d:K230 的图像预处理库,支持裁剪、缩放、平移等操作。
    • nncase_runtime:推理引擎,负责加载和执行 kmodel 模型。
    • ulab.numpy:用于数组操作的轻量级库,适合嵌入式环境。
    • image:可能用于图像操作(代码中未直接使用)。
    • gc:垃圾回收,释放内存。
    • sys:处理异常、退出程序等功能。
  • 自定义 AI 任务类
    • class MyAIApp(AIBase):
      

      MyAIApp 类继承自 AIBase,是一个定制化的 AI 应用。

      • 初始化方法

        • def __init__(self, kmodel_path, model_input_size, rgb888p_size=[224,224], display_size=[1920,1080], debug_mode=0):
          
        • 传入参数
          • kmodel_path:模型文件路径。
          • model_input_size:模型的输入分辨率(如 [320, 320])。
          • rgb888p_size:传感器传递给 AI 的图像分辨率。
          • display_size:显示设备的分辨率。
          • debug_mode:调试模式开关。
        • 分辨率对齐
          • 宽度按 16 字节对齐,满足硬件 DMA 的要求。
          • 使用 ALIGN_UP 函数完成对齐操作。
        • Ai2d 初始化
          • 创建 Ai2d 实例,设置输入/输出格式和数据类型。
      • 配置预处理
        • def config_preprocess(self, input_image_size=None):
          
        • 检查是否传入自定义的输入图像尺寸(input_image_size),否则使用默认的 rgb888p_size
        • 配置 resize 操作,使用 TensorFlow 的双线性插值方法(tf_bilinear)和半像素模式(half_pixel)。
        • 构建预处理流水线,将图像从 ai2d_input_size 转换为模型输入大小 model_input_size
      • 后处理
        • def postprocess(self, results):
              pass
          

          逻辑:此方法需要用户根据具体任务重写。

        • 例如:
          • 对检测任务进行非极大值抑制(NMS)。
          • 解析分类任务的概率结果。
          • 将分割任务的 mask 映射到图像上。
      • 绘制结果
        • def draw_result(self, pl, dets):
              pass
          

          逻辑:此方法也需要用户根据具体任务实现。

        • 使用 PipeLine 绘制检测框、类别标签等。
      • 主程序入口
        • if __name__ == "__main__":
          
        • 显示模式配置:根据目标设备选择 hdmilcd 显示模式,并设置对应分辨率。
        • 初始化 PipeLine
          • 创建 PipeLine 实例,负责从传感器读取图像帧并显示结果。
        • 初始化 AI 任务
          • 加载自定义的 AI 应用 MyAIApp
          • 调用 config_preprocess() 配置模型的预处理参数。
      • 主循环
        • while True:
              os.exitpoint()                  # 检查是否有退出信号
              with ScopedTiming("total", 1):  # 性能分析,记录单帧处理时间
                  img = pl.get_frame()        # 获取当前帧
                  res = my_ai.run(img)        # 推理当前帧
                  my_ai.draw_result(pl, res)  # 绘制推理结果
                  pl.show_image()             # 显示结果
                  gc.collect()                # 垃圾回收
          

          详细流程:

        • 退出检查
          • 检查是否收到退出信号。
        • 性能分析
          • 使用 ScopedTiming 记录总耗时,便于调试性能。
        • 帧处理
          • 调用 pl.get_frame() 从传感器获取当前帧。
          • 将图像传入 MyAIApp.run() 进行推理。
          • 绘制推理结果,叠加到图像上。
        • 显示图像
          • 使用 pl.show_image() 显示当前帧。
        • 垃圾回收
          • 使用 gc.collect() 清理未使用的内存。
      • 异常处理与清理
        • except Exception as e:
              sys.print_exception(e)         # 打印异常信息
          finally:
              my_ai.deinit()                 # 释放 AI 相关资源
              pl.destroy()                   # 销毁 PipeLine
          
        • 捕获异常并打印堆栈信息。
        • 在程序结束时,释放 AI 应用和 PipeLine 占用的资源。

四、人脸检测详解

下面以人脸检测为例给出示例代码:

from libs.PipeLine import PipeLine, ScopedTiming
from libs.AIBase import AIBase
from libs.AI2D import Ai2d
import os
import ujson
from media.media import *
from time import *
import nncase_runtime as nn
import ulab.numpy as np
import time
import utime
import image
import random
import gc
import sys
import aidemo

# 自定义人脸检测类,继承自AIBase基类
class FaceDetectionApp(AIBase):
    def __init__(self, kmodel_path, model_input_size, anchors, confidence_threshold=0.5, nms_threshold=0.2, rgb888p_size=[224,224], display_size=[1920,1080], debug_mode=0):
        super().__init__(kmodel_path, model_input_size, rgb888p_size, debug_mode)  # 调用基类的构造函数
        self.kmodel_path = kmodel_path  # 模型文件路径
        self.model_input_size = model_input_size  # 模型输入分辨率
        self.confidence_threshold = confidence_threshold  # 置信度阈值
        self.nms_threshold = nms_threshold  # NMS(非极大值抑制)阈值
        self.anchors = anchors  # 锚点数据,用于目标检测
        self.rgb888p_size = [ALIGN_UP(rgb888p_size[0], 16), rgb888p_size[1]]  # sensor给到AI的图像分辨率,并对宽度进行16的对齐
        self.display_size = [ALIGN_UP(display_size[0], 16), display_size[1]]  # 显示分辨率,并对宽度进行16的对齐
        self.debug_mode = debug_mode  # 是否开启调试模式
        self.ai2d = Ai2d(debug_mode)  # 实例化Ai2d,用于实现模型预处理
        self.ai2d.set_ai2d_dtype(nn.ai2d_format.NCHW_FMT, nn.ai2d_format.NCHW_FMT, np.uint8, np.uint8)  # 设置Ai2d的输入输出格式和类型

    # 配置预处理操作,这里使用了pad和resize,Ai2d支持crop/shift/pad/resize/affine,具体代码请打开/sdcard/app/libs/AI2D.py查看
    def config_preprocess(self, input_image_size=None):
        with ScopedTiming("set preprocess config", self.debug_mode > 0):  # 计时器,如果debug_mode大于0则开启
            ai2d_input_size = input_image_size if input_image_size else self.rgb888p_size  # 初始化ai2d预处理配置,默认为sensor给到AI的尺寸,可以通过设置input_image_size自行修改输入尺寸
            top, bottom, left, right = self.get_padding_param()  # 获取padding参数
            self.ai2d.pad([0, 0, 0, 0, top, bottom, left, right], 0, [104, 117, 123])  # 填充边缘
            self.ai2d.resize(nn.interp_method.tf_bilinear, nn.interp_mode.half_pixel)  # 缩放图像
            self.ai2d.build([1,3,ai2d_input_size[1],ai2d_input_size[0]],[1,3,self.model_input_size[1],self.model_input_size[0]])  # 构建预处理流程

    # 自定义当前任务的后处理,results是模型输出array列表,这里使用了aidemo库的face_det_post_process接口
    def postprocess(self, results):
        with ScopedTiming("postprocess", self.debug_mode > 0):
            post_ret = aidemo.face_det_post_process(self.confidence_threshold, self.nms_threshold, self.model_input_size[1], self.anchors, self.rgb888p_size, results)
            if len(post_ret) == 0:
                return post_ret
            else:
                return post_ret[0]

    # 绘制检测结果到画面上
    def draw_result(self, pl, dets):
        with ScopedTiming("display_draw", self.debug_mode > 0):
            if dets:
                pl.osd_img.clear()  # 清除OSD图像
                for det in dets:
                    # 将检测框的坐标转换为显示分辨率下的坐标
                    x, y, w, h = map(lambda x: int(round(x, 0)), det[:4])
                    x = x * self.display_size[0] // self.rgb888p_size[0]
                    y = y * self.display_size[1] // self.rgb888p_size[1]
                    w = w * self.display_size[0] // self.rgb888p_size[0]
                    h = h * self.display_size[1] // self.rgb888p_size[1]
                    pl.osd_img.draw_rectangle(x, y, w, h, color=(255, 255, 0, 255), thickness=2)  # 绘制矩形框
            else:
                pl.osd_img.clear()

    # 获取padding参数
    def get_padding_param(self):
        dst_w = self.model_input_size[0]  # 模型输入宽度
        dst_h = self.model_input_size[1]  # 模型输入高度
        ratio_w = dst_w / self.rgb888p_size[0]  # 宽度缩放比例
        ratio_h = dst_h / self.rgb888p_size[1]  # 高度缩放比例
        ratio = min(ratio_w, ratio_h)  # 取较小的缩放比例
        new_w = int(ratio * self.rgb888p_size[0])  # 新宽度
        new_h = int(ratio * self.rgb888p_size[1])  # 新高度
        dw = (dst_w - new_w) / 2  # 宽度差
        dh = (dst_h - new_h) / 2  # 高度差
        top = int(round(0))
        bottom = int(round(dh * 2 + 0.1))
        left = int(round(0))
        right = int(round(dw * 2 - 0.1))
        return top, bottom, left, right

if __name__ == "__main__":
    # 显示模式,默认"hdmi",可以选择"hdmi"和"lcd"
    display_mode="hdmi"
    # k230保持不变,k230d可调整为[640,360]
    rgb888p_size = [1920, 1080]

    if display_mode=="hdmi":
        display_size=[1920,1080]
    else:
        display_size=[800,480]
    # 设置模型路径和其他参数
    kmodel_path = "/sdcard/examples/kmodel/face_detection_320.kmodel"
    # 其它参数
    confidence_threshold = 0.5
    nms_threshold = 0.2
    anchor_len = 4200
    det_dim = 4
    anchors_path = "/sdcard/examples/utils/prior_data_320.bin"
    anchors = np.fromfile(anchors_path, dtype=np.float)
    anchors = anchors.reshape((anchor_len, det_dim))

    # 初始化PipeLine,用于图像处理流程
    pl = PipeLine(rgb888p_size=rgb888p_size, display_size=display_size, display_mode=display_mode)
    pl.create()  # 创建PipeLine实例
    # 初始化自定义人脸检测实例
    face_det = FaceDetectionApp(kmodel_path, model_input_size=[320, 320], anchors=anchors, confidence_threshold=confidence_threshold, nms_threshold=nms_threshold, rgb888p_size=rgb888p_size, display_size=display_size, debug_mode=0)
    face_det.config_preprocess()  # 配置预处理

    try:
        while True:
            os.exitpoint()                      # 检查是否有退出信号
            with ScopedTiming("total",1):
                img = pl.get_frame()            # 获取当前帧数据
                res = face_det.run(img)         # 推理当前帧
                face_det.draw_result(pl, res)   # 绘制结果
                pl.show_image()                 # 显示结果
                gc.collect()                    # 垃圾回收
    except Exception as e:
        sys.print_exception(e)                  # 打印异常信息
    finally:
        face_det.deinit()                       # 反初始化
        pl.destroy()                            # 销毁PipeLine实例

上述代码实现了一个基于人脸检测模型的应用,主要用于实时捕获视频帧进行人脸检测,并在画面上绘制检测结果。代码的核心分为几个部分:类定义、初始化和主循环

1. 类定义:FaceDetectionApp

FaceDetectionApp继承自基类 AIBase,实现了模型加载、前处理、后处理以及检测结果的绘制。

1.1. 构造函数 (__init__)
  • 初始化模型路径、输入大小、置信度阈值、NMS阈值、锚点数据和其他参数。
  • 配置 AI 数据预处理工具 Ai2d,用于裁剪、填充和调整输入图像尺寸。
  • 对分辨率进行 16 对齐操作(宽度必须为 16 的倍数)。
1.2. config_preprocess
  • 配置图像预处理的流程,包括:
    • 填充:通过 get_padding_param 获取填充值,并调用 self.ai2d.pad 添加边缘填充。
    • 缩放:使用双线性插值将图像调整为模型输入尺寸。
    • 构建流程:将输入尺寸转换为模型适配的形状。
1.3. postprocess
  • 使用 aidemo.face_det_post_process 进行后处理,返回人脸检测结果。
    • 后处理包括置信度筛选和非极大值抑制(NMS)以消除重叠框。
1.4. draw_result
  • 在检测结果上绘制矩形框:
    • 根据检测框的坐标,计算映射到显示分辨率的实际位置。
    • 调用 draw_rectangle 方法绘制矩形框。
1.5. get_padding_param
  • 计算图像在调整到模型输入尺寸时需要的填充量。
    • 确保图像比例一致,避免拉伸失真。

2. 初始化部分

if __name__ == "__main__": 中:

  1. 设置显示模式为 HDMILCD,选择对应的显示分辨率。
  2. 加载人脸检测模型(.kmodel 格式)和锚点数据(.bin 文件)。
  3. 初始化 PipeLine 管道,用于处理图像流(包括帧获取、显示等功能)。
  4. 创建 FaceDetectionApp 实例,并配置前处理流程。

3. 主循环

  • 不断从 PipeLine 中获取视频帧。
  • 调用模型进行推理,返回检测结果。
  • 绘制检测框并更新显示画面。
  • 定期调用 gc.collect() 进行垃圾回收以释放内存。
异常处理
  • 使用 try...except 捕获可能的运行时错误,并在异常发生时打印信息。
  • finally 块中释放资源,包括反初始化模型和销毁管道。

4. 核心逻辑流程

  1. 模型加载:加载 .kmodel 文件和锚点。
  2. 图像预处理:通过裁剪、填充和缩放,将帧调整为模型输入格式。
  3. 模型推理:使用神经网络对帧进行推理,输出检测框和置信度。
  4. 结果后处理:通过置信度和 NMS 筛选最终检测框。
  5. 结果显示:在视频帧上绘制检测框并输出。

5. 关键库和模块

  • nncase_runtime: 用于加载和运行神经网络模型。
  • Ai2d: 图像预处理模块,支持裁剪、填充、缩放等操作。
  • PipeLine: 管道处理视频流,封装了帧获取、显示等功能。
  • aidemo: 提供后处理函数(如 face_det_post_process)。
<think>嗯,用户想用CanMV K210训练识别物体,然后通过STM32F103VET6控制小车推动物体。首先,我需要确认整个流程。CanMV K210主要负责图像识别,STM32负责控制电机。两者之间需要通信,可能用串口。那分步骤的话,应该是先训练模型,然后部署到K210,接着处理通信和控制部分。 首先,训练模型。用户可能需要收集自己物体的图片,标注后训练YOLO或者分类模型。CanMV支持TensorFlow Lite吗?或者他们有自己的训练工具?可能需要用Edge Impulse或者其他在线平台,或者本地用PyTorch训练后转换。然后保存为K210支持的格式,比如kmodel。 然后是K210的代码,要初始化摄像头,加载模型,进行推理。检测到目标后,通过串口发送信号给STM32。比如发送字符指令,当检测到物体时发送特定的字符串,比如'forward'。 接着是STM32的部分。需要配置串口接收中断,解析K210发来的指令,根据指令控制电机驱动,比如PWM输出,驱动H桥电路,让小车移动。可能需要电机驱动模块,比如L298N或者TB6612。 硬件连接方面,K210和STM32之间需要连接串口的TX和RX,注意电平匹配,可能都是3.3V,所以可以直接连接。电源方面要确保两者供电稳定,尤其是电机运行时避免电压波动影响控制板。 用户可能遇到的难点包括模型训练的数据不足,导致识别不准;串口通信的稳定性,比如波特率设置是否正确,数据解析是否正常;还有小车的机械结构是否适合推动物体,比如电机的扭矩是否足够。 测试步骤可能需要分阶段,先测试K210是否能正确识别并发送信号,再单独测试STM32能否根据指令控制电机,最后整合整个系统。调试时可能需要用逻辑分析仪或者串口调试助手查看通信数据。 另外,还要考虑实时性的问题,K210的处理速度是否足够快,避免延迟导致小车反应不及时。如果检测到物体后需要计算位置,可能需要发送坐标信息,STM32根据位置调整方向。但用户的需求是推动物体,可能只需要检测到存在物体就让小车前进,直到物体被推出某个区域。 可能还需要在小车上安装适当的推板结构,确保能有效推动物体而不损坏小车。同时,电源管理也很重要,尤其是使用电池供电时,需要足够的容量支持K210、STM32和电机同时工作。 总结下来,步骤大概是:1. 准备数据集,训练模型;2. 部署模型到K210并测试识别;3. 实现K210与STM32的串口通信;4. STM32控制电机响应指令;5. 整合系统,调试优化。</think>好的,我将分步骤为您说明如何实现CanMV K210物体识别与STM32F103VET6小车的联动控制: --- ### **一、系统架构** ``` CanMV K210(视觉处理) --> 串口通信 --> STM32F103(运动控制) --> 电机驱动 --> 机械执行 ``` --- ### **二、CanMV K210端实现** #### 1. 模型训练 - **推荐工具**:使用YOLOv2/YOLOv3-Tiny轻量化模型 + MaixHub在线训练平台 - **步骤**: 1. 拍摄200+张目标物体的多角度图片 2. 在MaixHub标注数据集(支持自动标注) 3. 选择"目标检测"任务类型训练 4. 导出`.kmodel`格式模型文件 #### 2. 代码部署 ```python import sensor, image, lcd, time from maix import KPU # 初始化硬件 sensor.reset() sensor.set_pixformat(sensor.RGB565) sensor.set_framesize(sensor.QVGA) lcd.init() # 加载训练好的模型 kpu = KPU() kpu.load(0x300000) # 模型烧录地址 # 串口配置 from board import board_info from fpioa_manager import fm fm.register(35, fm.fpioa.UART1_TX, force=True) fm.register(34, fm.fpioa.UART1_RX, force=True) uart = UART(UART.UART1, 115200, 8, None, 1, timeout=1000) while True: img = sensor.snapshot() dets = kpu.run(img, threshold=0.5) # 执行推理 if dets: uart.write("Object Detected\n") # 检测到物体时发送信号 # 可添加坐标信息:uart.write(f"X:{dets[0]['x']},Y:{dets[0]['y']}\n") lcd.display(img) ``` --- ### **三、STM32端实现** #### 1. 硬件连接 | K210引脚 | STM32引脚 | |---------|----------| | UART1_TX | PA10(RX) | | UART1_RX | PA9(TX) | | GND | GND | #### 2. 关键代码(HAL库) ```c // 串口中断回调 void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { if(huart->Instance == USART1) { if(strstr(rx_buffer, "Object Detected")) { Motor_Forward(100); // 以100%占空比前进 HAL_Delay(1000); // 持续推动1秒 Motor_Stop(); } HAL_UART_Receive_IT(&huart1, rx_buffer, 16); // 重新使能接收 } } // 电机控制函数示例 void Motor_Forward(uint8_t speed) { __HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1, speed); // 右电机 __HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_2, speed); // 左电机 } ``` --- ### **四、调试要点** 1. **通信验证**: - 使用USB-TTL模块配合串口助手测试指令格式 - 确保双方波特率(推荐115200)、校验位一致 2. **抗干扰设计**: - 在STM32端添加软件滤波: ```c if(连续3次收到相同指令)才执行动作 ``` - 电机电源与控制系统电源隔离 3. **机械调优**: - 推板安装角度建议30°-45° - 实测电机扭矩是否满足推力需求 --- ### **五、扩展建议** 1. 可增加距离传感器(如TOF)辅助定位 2. 使用PID算法实现精准位置控制 3. 添加OLED显示实时状态信息 实际部署时建议分阶段测试: 1. 单独验证K210识别准确率 2. 测试STM32基础运动控制 3. 联调通信协议 4. 整体场景测试
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

7yewh

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值