电赛备赛日记(一):K210与STM32串口通信

news2024/12/27 6:57:18

   拖更了n久的备赛日记终于来啦,最近实现了关于K210图像识别并将所需数据(即目标类别,目标在图像中的加权坐标)其中,加权坐标指K210识别到的目标并框出的框的宽和高与框左上顶点的坐标加权,希望以此来判断目标所处的位置并方便后续进行诸如寻迹,目标跟随等任务。其中涉及包括YOLO网络的训练,上位机K210进行目标检测并利用串口对数据进行发出。下位机STM32则要接收到K210传出的数据,对数据进行解码,存入对应数组便于后续引用,在此次实验中再利用串口二将接收数据传给PC端利用串口助手进行数据显示。

   我们一步一步来,从K210的YOLO网络训练开始讲起,K210中常用的YOLO网络训练包括利用spieed公司的线上训练平台,本地训练中除了深度学习大佬依旧可以利用之前常用的训练方法进行训练外,身为初学者,没有那么多的经历也没有能力完成深度学习环境的搭建与代码的编写,可以选择大佬开发好的MX-YOLO进行训练。本次实验就是采用MX-YOLO进行本地训练(跑例程的mask检测),开发的模型,在本次日记中不做过多赘述,后续会和相关训练方法同期更新出。在模型运行中遇到了诸如内存不足的问题(感觉是运行内存不足),在重刷了spieed的最小固件后得以解决。也建议大家在使用K210跑深度学习模型的时候刷最小固件来执行程序,这样能容纳稍微大一点的模型。一般能支持2兆到3兆左右(模型存至sd卡)。

   模型导入进行目标识别后,我们需要启动K210的串口将数据发出,要注意K210的串口不能单独发出数字,所以此处选择定义了一个一个元组参数来统一发送数据。为了让STM32更好的接收数据,我们定义了一个简单的通信协议。长这样

帧头1:0xfe
帧头2: 0xfd
数据1:classid
数据2:cx
数据3:cy
帧尾:0xff

其中之所以选择0xfe 0xfd 0xff作为帧头,是因为我们的捕捉的图像大小为224*224,皆小于0xfd,不会出现加权后数据大于0xff从而影响通信的情况。

下面就是我们K210端的全部代码啦,烧即用,只需要修改对应模型以及对应的anchor文件和lable文件即可。(注意我选用的spieed的固件包,选用canmv或者其它固件包大概率会无法使用喔)

#此代码中0为未带口罩,1为戴口罩
import sensor
import image
import lcd
import KPU as kpu
from machine import UART
from fpioa_manager import fm
import ustruct
lcd.init()
sensor.reset()
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QVGA)
sensor.set_windowing((224, 224))
sensor.set_vflip(1)#设置摄像头后置即所见即所得
sensor.run(1)
#初始化串口,pin6为串口1RX口,pin7为串口1TX口
fm.register(6,fm.fpioa.UART1_RX)
fm.register(7,fm.fpioa.UART1_TX)
def sending_data(cc,cx,cy):
    global uart;
    data = ustruct.pack("<bbhhhb",              #格式为俩个字符俩个短整型(2字节)
                   0xfe,#帧头1
                   0xfd,#帧头2
                   int(cc), #数据1为传入类型 #四字节
                   int(cx), #数据2为框与顶点坐标的加权的长#四字节
                   int(cy), #数据3为框与顶点坐标的加权的高#四字节
                   0xff)
    uart = UART(UART.UART1, 115200, 8, 0, 0, timeout=1000, read_buf_len=4096)
    uart.write(data);   #必须要传入一个字节数组
task = kpu.load("/sd/mask.kmodel")
info_list = kpu.netinfo(task)
f=open("mask.anchors.txt","r")
anchor_txt=f.read()
L=[]
for i in anchor_txt.split(","):
    L.append(float(i))
anchor=tuple(L)
f.close()
a = kpu.init_yolo2(task, 0.6, 0.3, 5, anchor)
f=open("mask.lable.txt","r")
labels_txt=f.read()
labels = labels_txt.split(",")
f.close()
while(True):
    img = sensor.snapshot()
    code = kpu.run_yolo2(task, img)
    if code:#识别到对应目标
        for i in code:
            #print(i.w())
            obj_x=(i.x()+i.w())/2
            obj_y=(i.y()+i.h())/2#数据加权平均计算x,y
            sending_data(i.classid(),obj_x,obj_y)#发送数据
            if i.classid():#检测目标为mask,仅适用于二分类问题运用此行
               a=img.draw_rectangle(i.rect(),(0,255,0),2)
               a = lcd.display(img)
               for i in code:
                 lcd.draw_string(i.x()+45, i.y()-20, labels[i.classid()]+" "+'%.2f'%i.value(), lcd.WHITE,lcd.GREEN)
            else:#检测目标为un_mask
               a=img.draw_rectangle(i.rect(),(255,0,0),2)
               a = lcd.display(img)
               for i in code:
                 lcd.draw_string(i.x()+45, i.y()-20, labels[i.classid()]+" "+'%.2f'%i.value(), lcd.WHITE,lcd.RED)
    else:#没有要检测的目标出现
        a = lcd.display(img)
a = kpu.deinit(task)

  K210的数据会按ASCII形式发出(其实无所谓,对应都有十六进制形式),这里要留意,一个整形是四字节,也就是比如我classid为01,但实际发出的数据是 00 01

   然后就是stm32的部分了,为了方便移植和开发,本程序中选用hal库开发,对于较底层的解读后续更新。

   目前用到的所有初始化都是最基础的cubemx初始化串口的步骤,并无特殊操作,要注意需要使能串口一的中断用于接收中断。同时将串口模式设置为asynchronous(异步)。下面简单介绍一下具体配置过程。

    

 

首先我们需要配置系统的RCC,将高速时钟配置成Crystal/Ceramic Resonator:外部无源晶振(陶瓷晶振

  编辑时钟树  配置对应串口

 

这里只展示了配置串口1,串口2同串口1一致,注意串口1要打开中断,即NVIC那块enable

 

中断优先级可以不动,让系统自动按中断号跑就行。

 

初始化就是这样,我们就可以生成代码啦。

将我们这里需要的一些定义代码写在此处,注意一定一定要把代码写在user code里,这样重新配置文件的时候不会被覆盖掉。这里有如此多的hello world主要是为了测试程序是否正常执行。可以进行删除。

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "stdio.h"
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
uint8_t aRxBuffer;			//接收中断缓冲
uint8_t Uart1_RxBuffer[9] = {0};		//接收缓冲
uint8_t Uart1_Rx_Cnt = 0;		//接收缓冲计数
uint8_t Uart1_RxFlag = 0;
uint8_t hello[]={"hello,world\r\n"};
uint8_t hello1[]={"hello1,world\r\n"};
uint8_t hello2[]={"hello2,world\r\n"};
uint8_t hello3[]={"hello3,world\r\n"};
uint8_t hello4[]={"hello4,world\r\n"};
uint8_t hello5[]={"hello5,world\r\n"};
uint8_t hello6[]={"hello6,world\r\n"};
uint8_t error3[]={"please check the end of the frame\r\n"};
uint8_t error2[]={"please check the end of the header1\r\n"};
uint8_t error1[]={"please check the end of the header1\r\n"};
uint8_t classid;
uint8_t obj_x;
uint8_t obj_y;
uint8_t jieguo[3]={0};
/* USER CODE END PTD */

下面是main函数中的部分,要记得在main函数中除了生成的初始化函数,我们还要开启对应的接收中断。

  HAL_UART_Transmit(&huart2, hello,sizeof(hello) , 1000);
  HAL_UART_Receive_IT(&huart1,Uart1_RxBuffer, 9);

这里将串口1接收的数据直接存储进数组Uart1_RxBuffer中要注意这里其实需要的是一个地址,但是我们知道数组的第一位可以代表他整个数组的地址,所以这里只需要这么写,9代表接收数据的长度为9个字节(参考上述的通信协议,总共是九个字节)

串口接收满9个字节的数据后会引起中断,本程序目前while循环中无任何代码,如果有需要也可以将数据解析任务放置在main函数的while循环中。

引起串口中断后会执行串口中断服务函数,在服务函数中调用串口中断回调函数

此处串口中断回调函数代码如下:

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
  /* Prevent unused argument(s) compilation warning */
  /* NOTE: This function Should not be modified, when the callback is needed,
           the HAL_UART_TxCpltCallback could be implemented in the user file
   */

	//HAL_UART_Transmit(&huart2, hello, sizeof(hello),0xFFFF);
		HAL_UART_Transmit(&huart2, hello1, sizeof(hello1),0xFFFF);
		if(Uart1_RxBuffer[0]==0XFE)//判断帧头1
		 {
			 HAL_UART_Transmit(&huart2, hello2, sizeof(hello2),0xFFFF);
			 if(Uart1_RxBuffer[1]==0XFD)//判断帧头2
			 {
				HAL_UART_Transmit(&huart2, hello3, sizeof(hello2),0xFFFF);
				 jieguo[0]=Uart1_RxBuffer[3];
				 jieguo[1]=Uart1_RxBuffer[5];
				 jieguo[2]=Uart1_RxBuffer[7];
				 if(Uart1_RxBuffer[8]==0xFF) //判断帧尾
					{
						HAL_UART_Transmit(&huart2, hello4, sizeof(hello4),0xFFFF);
					}
				 else
				 {
						HAL_UART_Transmit(&huart2, error3, sizeof(error3), 1000);
				 }
			 }
			 else
			  {
				 HAL_UART_Transmit(&huart2, error2, sizeof(error2), 1000);
			  }
		 }
	    else
	     {
	       HAL_UART_Transmit(&huart2, error1, sizeof(error1), 1000);
		 }
    HAL_UART_Transmit(&huart2, jieguo, sizeof(jieguo), 1000);
    memset(jieguo,0x00,sizeof(jieguo)); //清空数组
	memset(Uart1_RxBuffer,0x00,sizeof(Uart1_RxBuffer)); //清空数组
    HAL_UART_Transmit(&huart2, Uart1_RxBuffer, sizeof(Uart1_RxBuffer), 1000);
	HAL_UART_Receive_IT(&huart1,Uart1_RxBuffer, 9);   //再开启接收中断
	HAL_UART_Transmit(&huart2, hello5, sizeof(hello5), 1000);
}

此处要注意必须用memset清除数组,如果不用memset清除数组,则串口接收数据无法更新(我也不懂,明明进中断了但是不会覆盖第一次的数据结果。有懂的大佬麻烦给我讲一下),同时一定要在中断回调函数中再次使能接收中断。

进行完上述所有,就可正常接收K210传输的数据并将接收数据通过串口2利用USB转TTL传输回PC机串口助手显示。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/613972.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

海云捷讯杯 赛后总结 目标检测——缺陷检测(模型训练部分)

在这次比赛中&#xff0c;本人在队伍中主要负责模型训练部分&#xff0c;所以本文主要讲述如何使用PaddleDetection代码自定义数据集进行目标检测&#xff0c;欢迎大家纠错讨论哦&#xff0c;不胜荣幸~ 参考项目&#xff1a; SSDquexianjiance - 飞桨AI Studio (baidu.com) 感…

云上高校导航

2023042719 - 云上高校导航 中国大学生计算机设计大赛 广西赛区 软件应用与开发 - 移动应用开发&#xff08;非游戏类&#xff09; 三等奖 “云上高校导航”是一套基于小程序云开发的校园导航类系统开发方案。 该开发方案可供开发者进行二次开发&#xff0c;用于解决师生和访客…

MySQL数据库理论基础

数据库-理论基础 1.什么是数据库2.数据库管理系统(DBMS)3.数据库与文件系统的区别4.数据库的发展史5. 常见数据库5.1 关系型数据库5.2 非关系型数据库 6.MySQL简介7. MySQL的特性8.MySQL获取9.MySQL在企业中应用10.MySQL体系结构 1.什么是数据库 数据&#xff1a; 描述事物的符…

Linux 高级篇-定制自己的Linux 系统

Linux 高级篇-定制自己的Linux 系统 基本介绍 通过裁剪现有Linux 系统(CentOS7.6)&#xff0c;创建属于自己的min Linux 小系统&#xff0c;可以加深我们对linux 的理解。利用centos7.6&#xff0c;搭建一个小小linux 系统, 很有趣。 基本原理 启动流程介绍&#xff1a; 制…

LeetCode25. K 个一组翻转链表

给你链表的头节点 head &#xff0c;每 k 个节点一组进行翻转&#xff0c;请你返回修改后的链表。 k 是一个正整数&#xff0c;它的值小于或等于链表的长度。如果节点总数不是 k 的整数倍&#xff0c;那么请将最后剩余的节点保持原有顺序。 你不能只是单纯的改变节点内部的值…

操作系统之IO管理

目录 IO设备的概念和分类 IO控制器 IO控制方式 IO软件层次结构 IO应用程序接口和驱动程序接口 IO核心子系统 假脱机技术 IO设备的分配和回收 缓冲区管理 磁盘的结构 磁盘调度算法 磁盘的管理 固态硬盘 本文内容摘自&#xff1a;5.1_1_I-O设备的概念和分类_哔哩哔哩…

cookie 和 session 的恩恩怨怨

目录 cookie 1. Cookie 从哪里来? 2. Cookie 到哪里去? 3. Cookie 有啥用? Session Session 中的常用方法 模拟实现一个登录页面: session 和 cookie 的最官方的恩恩怨怨 cookie Cookie 是浏览器在本地存储数据的一种机制 1. Cookie 从哪里来? Cookie 从服务器…

chatgpt赋能python:Python如何将两张图片横向拼在一起

Python如何将两张图片横向拼在一起 在网页设计中&#xff0c;有时候需要将两张图片横向拼在一起来达到更好的展示效果。本文将介绍如何使用Python实现这一功能。 前置知识 在使用Python进行图像处理之前&#xff0c;先了解以下几个库&#xff1a; Pillow&#xff1a;Python…

chatgpt赋能python:Python如何取非

Python如何取非 Python是一种强大的编程语言&#xff0c;可以用于许多不同的任务&#xff0c;包括搜索引擎优化&#xff08;SEO&#xff09;。在这篇文章中&#xff0c;我们将重点介绍Python如何取非&#xff0c;这对于SEO优化非常重要。 什么是取非&#xff1f; 在SEO中&am…

chatgpt赋能python:Python如何手动安装包:技术向SEO文章

Python如何手动安装包&#xff1a;技术向SEO文章 虽然大多数Python用户都习惯使用pip来安装和管理包&#xff0c;但手动安装包是必要的技能之一。这篇文章将介绍如何使用Python的标准方法手动安装包&#xff0c;并提供实际的指导。 为什么要手动安装包&#xff1f; 有时候&a…

达梦数据库读写分离集群搭建

目录 说明... 3 前期准备... 4 开始搭建读写分离... 5 一、主库200. 5 1、卸载原实例... 5 2、创建新的实例... 6 3、配置主库200. 6 4、启动主库... 8 5、设置OGUID.. 8 二、配置2台备库... 9 1、创建新的实例... 9 2、备份恢复&#xff08;初始化实例后应该先备份…

VS2012编译VTK7.1.1库,使用VTK加载显示STL图像

文章目录 cmake配置项目编译VTK库代码测试报错:no override found for vtkpolydata下载VTK库下载地址,下载VTK源码 https://vtk.org/download/ 需要工具 1.VS2012 2.CMAKE 官网 https://cmake.org/ cmake配置项目 下载后的vtk7.1.1库解压 安装好cmake,打开cmake-gui,在whe…

chatgpt赋能python:Python循环语句详解:如何循环执行一个语句

Python循环语句详解&#xff1a;如何循环执行一个语句 Python是一种简单易学、优雅高效的编程语言&#xff0c;在很多领域都有广泛应用。其中&#xff0c;循环语句特别重要&#xff0c;可以让我们轻松实现重复执行某个语句的功能。本文将介绍Python的循环语句及其应用场景&…

01:快速入门爬虫

1.引导 1.Robots协议 Robots协议&#xff08;爬虫协议&#xff09;的全称是“网络爬虫排除标准”&#xff08;Robots Exclusion Protocol&#xff09;&#xff0c;网站通过Robots协议告诉搜索引擎哪些页面可以抓取&#xff0c;哪些页面不能抓取。该协议是国际互联网界通行的道…

Pytorch1.12.1+cu113安装记录

因为torch1.7.0对于SiLU算子导出不支持,需要1.7.1才支持.于是索性准备更新一下自己的算法版本库,查询到CUDA11.3支持的最高Pytorch版本为1.12.1,于是统一做一下更新.这里采用离线下载的方式,因为在线下载实在是太蛋疼了 1. Pytorch离线包下载 这是官方提供的版本,我这里不直接…

Python-web开发学习笔记(4):CSS基础

&#x1f680; Python-web开发学习笔记系列往期文章&#xff1a; &#x1f343; Python-web开发学习笔记&#xff08;1&#xff09;--- HTML基础 &#x1f343; Python-web开发学习笔记&#xff08;2&#xff09;--- HTML基础 &#x1f343; Python-web开发学习笔记&#xff08…

面试常考算法(1):反转链表、局部反转链表(包含误区分析)

BM1 反转链表 给定一个单链表的头结点pHead(该头节点是有值的&#xff0c;&#xff0c;$ 长度为n&#xff0c;反转该链表后&#xff0c;返回新链表的表头。   数据范围: 0 ≤ n ≤ 1000 0 \leq n \leq 1000 0≤n≤1000   要求: 空间复杂度 O ( 1 ) O(1) O(1) &#xff0c…

QT QHorizontalSpacer弹簧控件

本文详细的介绍了QHorizontalSpacer控件的各种操作&#xff0c;例如&#xff1a;新建界面、控件布局、隐藏控件、设置宽高、添加布局、其它参数、.h源文件、cpp源文件、其它文章等等操作。 实际开发中&#xff0c;一个界面上可能包含十几个控件&#xff0c;手动调整它们的位置既…

最优化简明版(上)

引言 本文简单地介绍一些凸优化(Convex Optimization)的基础知识&#xff0c;可能不会有很多证明推导&#xff0c;目的是能快速应用到机器学习问题上。 凸集 直线与线段 设 x 1 ≠ x 2 x_1 \neq x_2 x1​x2​为 R n \Bbb R^n Rn空间中的两个点&#xff0c;那么具有下列形…

基于 log4j2 插件实现统一日志脱敏,性能远超正则替换

前言 金融用户敏感数据如何优雅地实现脱敏&#xff1f; 日志脱敏之后&#xff0c;无法根据信息快速定位怎么办&#xff1f; 经过了这两篇文章之后&#xff0c;我们对日志脱敏应该有了一定的理解。 但是实际项目中&#xff0c;我们遇到的情况往往更加复杂&#xff1a; 1&am…