毕业设计 stm32车牌识别 - 单片机 嵌入式 物联网 机器视觉

269 篇文章 42 订阅
269 篇文章 9 订阅


0 前言

🔥 这两年开始毕业设计和毕业答辩的要求和难度不断提升,传统的毕设题目缺少创新和亮点,往往达不到毕业答辩的要求,这两年不断有学弟学妹告诉学长自己做的项目系统达不到老师的要求。

为了大家能够顺利以及最少的精力通过毕设,学长分享优质毕业设计项目,今天要分享的是

🚩 基于stm32的车牌识别

🥇学长这里给一个题目综合评分(每项满分5分)

  • 难度系数:4分
  • 工作量:4分
  • 创新点:3分

1 简介

使用STM32F103RCT6作为主控,摄像头使用OV7670(带FIFO)。STM32进行了16倍频。识别过程分别为:图像采集,二值化,识别车牌区域,字符分割,字符匹配。

2 主要器件

  • STM32F103RCT6主控芯片
  • OV7670摄像头

3 实现效果

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

4 设计原理

识别过程如下

图像采集
通过OV7670摄像头进行图像采集,采集的图像大小为320*240像素,像素格式为RGB565。每个像素由两字节组成,第一字节的高五位是Red,第一字节的低三位和第二字节的高三位组成Green,第二字节的低五位是Blue。

二值化
二值化就是让图像的像素点矩阵中的每个像素点的灰度值为0(黑色)或者255(白色),让整个图片呈现出只有黑色和白色的效果。二值化后的图像中灰度值范围是0或者255。这时需要设定一个阈值来对像素点进行设置。

常用二值化方法:

  1. 取中值:设置阈值为127,灰度值小于127的为0,大于127的为255。这样设置计算量小,计算快。缺点也严重:在不同的图像中,颜色分布差别大,处理效果也不会很好。程序开始之前设置R,G,B的阈值,通过阈值判断将像素设置为全黑(0x0000)或者全白(0xFFFF).同时根据色彩的变化记录每一行的颜色跳变点,由此识别出车牌区域。
  2. 取平均值:像素点平均值 = (像素点1灰度值 + 像素点2灰度值 + …… + 像素点n灰度值) / n
  3. 双峰法:此方法适用于具有明显双峰直方图的图像,不适合直方图中双峰差别很大或双峰间的谷比较宽广而平坦的图像。该方法认为图像由前景和背景组成,在灰度直方图上,前景和背景会形成高峰,在双峰之间的最低谷处就是阈值。

识别车牌区域
根据上一步的二值化,由于车牌区域跳变点多,由此可以得出车牌区域。分别记录车牌区域的上下高度。然后通过RGB-HSV颜色转换,识别出车牌区域的左右边界。

字符分割
我国常见车牌以及排列顺序大部分都是按照如下设计的:汉字、英文字母、点、英文字母、阿拉伯数字、阿拉伯数字、阿拉伯数字、阿拉伯数字。基于这个规律,以及图像采集高度一致,设计了如下的分割方法:

  1. 在内存中开辟七个长为车牌长的七分之一和宽为车牌宽的区域
  2. 从车牌图像长边的巾问向下开始扫描车牌图像,并把扫描到的所有的点灰度值复制到0区域的第四个区域对应位置上。然后再从上向下扫描刚扫描过这一努的左边或右边,直到所扫描的这一峰上的所有点的灰度都是0时为止,并把这一竖认为是字符的分离处。
  3. 切割第五到第七个字符。方法就是,切割完了第四个字符之后,再依次扫描剩下的空间,直到所扫描的这一竖上的所有点的灰度值不全为0时,认为是字符的开始并依次扫描直到所扫描的这一竖上的所有点的灰度值全为0时认为是字符的结束。
  4. 切割第三到第四个字符。这两个字符的切割方式与第五到第七个字符一样。
  5. 切割第一到第二个字符。当第三个字符切割完之后,我们将遇到一个点,我们也把它看作一个字符,只不过这个点扫描之后就不要了。扫描完这个点之后,我们来切割第二个字符,它的切割方式与前面一样。切割完了第二个字符之后,再向左扫描,直到所扫描的这一竖上的所有点的灰度值不全为0时,认为是字符的开始,并依次扫描直到所扫描所有剩下的,并填到相应的位置,直到剩下的空间填满。经过粗分割后,可以得到一些单个字符区域和多余的空间。下一步我们将把这些多余的空间去掉。这将更有利于下一步字符的识别。
  6. 去除图像上多余空间:车牌上的字符经过了粗切割所得到的是一些单的字符,但在分配空间时是按照车牌的宽和长的七分之一来分配的;所以这个空间可能大于字符应该占的空问。所以,要将多余空间去除。对于第一个字符从第一行开始向下扫描,把那些一行中所有的点的灰度值全为0的点去掉,直到扫描到有一行不全为0时为止。然后再从第一列开始向右扫描把那些一列中所有的点的灰度值全为0的点去掉,直到扫描到有一列不全为0时为止。接下来从最后一行开始向上扫描,把那些一行中所有的点的灰度值全为0的点去掉,直到扫描到有一行不全为0时为止。最后从最后一列开始向左扫描把那些一列中所有的点的灰度值全为0的点去掉,直到扫描到有一列不全为0时为止。重复上面的步骤完成剩下字符的切割。
  7. 根据二值化的结果,以及记录的跳变点位置,对字符进行分割,同时记录字符的左右边界。

字符匹配
对分割出来的字符进行归一化处理,这里用到图片的扩大算法,扩大之后逐一的去进行字符匹配。字符模板事前通过字模软件转换成二进制数据保存在数组中。最后根据匹配结果相似度最大的做为输出结果。
归一化图像就是要把原来各不相同的字符统一到同一尺寸。因为扫描进来的图像中字符大小存在较大的差异,而相对来说,统一尺寸的字符识别的标准性更强,准确率自然也更高。具体算法如下:先得到原来字符的高度和宽度,与系统已存字模的数据作比较,得出要变换的系数,然后根据得到的系数按照插值的方法映射到原图像中。

5 部分核心代码

摄像头部分

#include "stm32f10x.h"
#include "board.h"
#include "ov7670.h" 

#define OV7670_REG_NUM  184

#define DELAYTIME 2//9倍频时延时“1”,16倍频时延时“2”

void OV7670_GPIO_Init(void)
{
  GPIO_InitTypeDef gpio_init_struct;//结构体
		
	RCC_APB2PeriphClockCmd(FIFO_WR_RCC, ENABLE);//初始化时钟
	RCC_APB2PeriphClockCmd(FIFO_RRST_RCC, ENABLE);//初始化时钟
	RCC_APB2PeriphClockCmd(FIFO_OE_RCC, ENABLE);//初始化时钟
	RCC_APB2PeriphClockCmd(FIFO_RCLK_RCC, ENABLE);//初始化时钟
	RCC_APB2PeriphClockCmd(FIFO_WRST_RCC, ENABLE);//初始化时钟
	RCC_APB2PeriphClockCmd(SCCB_SIC_RCC, ENABLE);//初始化时钟
	RCC_APB2PeriphClockCmd(SCCB_SID_RCC, ENABLE);//初始化时钟
	RCC_APB2PeriphClockCmd(OV7670_RRST_RCC, ENABLE);//初始化时钟
	
	gpio_init_struct.GPIO_Mode = GPIO_Mode_Out_PP;
	gpio_init_struct.GPIO_Speed = GPIO_Speed_50MHz;

	gpio_init_struct.GPIO_Pin = FIFO_WR_PIN;
	GPIO_Init(FIFO_WR_PORT, &gpio_init_struct);

	gpio_init_struct.GPIO_Pin = FIFO_RRST_PIN;
	GPIO_Init(FIFO_RRST_PORT, &gpio_init_struct);

	gpio_init_struct.GPIO_Pin = FIFO_OE_PIN;
	GPIO_Init(FIFO_OE_PORT, &gpio_init_struct);

	gpio_init_struct.GPIO_Pin = FIFO_RCLK_PIN;
	GPIO_Init(FIFO_RCLK_PORT, &gpio_init_struct);

	gpio_init_struct.GPIO_Pin = FIFO_WRST_PIN;
	GPIO_Init(FIFO_WRST_PORT, &gpio_init_struct);

	gpio_init_struct.GPIO_Pin = SCCB_SIC_PIN;
	GPIO_Init(SCCB_SIC_PORT, &gpio_init_struct);

	gpio_init_struct.GPIO_Pin = SCCB_SID_PIN;
	GPIO_Init(SCCB_SID_PORT, &gpio_init_struct);

	gpio_init_struct.GPIO_Pin = OV7670_RRST_PIN;
	GPIO_Init(OV7670_RRST_PORT, &gpio_init_struct);
	
	RCC_APB2PeriphClockCmd(OV7670_DATA_RCC, ENABLE);//初始化时钟
	
	gpio_init_struct.GPIO_Mode = GPIO_Mode_IPU;//输入上拉
	
	gpio_init_struct.GPIO_Pin = OV7670_DATA_PIN;//OV7670数据口引脚初始化
	GPIO_Init(OV7670_DATA_PORT, &gpio_init_struct);
	
	GPIO_WriteBit(FIFO_OE_PORT, FIFO_OE_PIN, 0);
}
static void Sccb_Sid_Change_In(void)//引脚切换为输入
{
	GPIO_InitTypeDef gpio_init_struct;//结构体

	gpio_init_struct.GPIO_Mode = GPIO_Mode_IPU;//输入上拉
	gpio_init_struct.GPIO_Speed = GPIO_Speed_50MHz;

	gpio_init_struct.GPIO_Pin = SCCB_SID_PIN;
	GPIO_Init(SCCB_SID_PORT, &gpio_init_struct);
}
static void Sccb_Sid_Change_Out(void)//引脚切换为输出
{
	GPIO_InitTypeDef gpio_init_struct;//结构体
	
	gpio_init_struct.GPIO_Mode = GPIO_Mode_Out_PP;//推挽输出
	gpio_init_struct.GPIO_Speed = GPIO_Speed_50MHz;
	gpio_init_struct.GPIO_Pin = SCCB_SID_PIN;
	GPIO_Init(SCCB_SID_PORT, &gpio_init_struct);
}
void FIFO_Reset_Read_Addr(void)//FIFO 读数据复位,通过直接操作寄存器来提高速度
{				
	GPIOC->BRR =1<<2;	//	RRST=0
	GPIOC->BRR =1<<4;	//	RCLK= 0
	GPIOC->BSRR =1<<4;	//  RCLK=1
	GPIOC->BRR =1<<4;	//	RCLK=0
	GPIOC->BSRR =1<<2;	//  RRST=1
	GPIOC->BSRR =1<<4;	//	RCLK=1
}
static void Start_Sccb(void)//SCCB设置,类似I2C
{
	GPIO_WriteBit(SCCB_SID_PORT, SCCB_SID_PIN, 1);
	delay(DELAYTIME);
	GPIO_WriteBit(SCCB_SIC_PORT, SCCB_SIC_PIN, 1);
	delay(DELAYTIME);
	GPIO_WriteBit(SCCB_SID_PORT, SCCB_SID_PIN, 0);
	delay(DELAYTIME);
	GPIO_WriteBit(SCCB_SIC_PORT, SCCB_SIC_PIN, 0);
	delay(DELAYTIME);
}

static void Stop_Sccb(void)//stop命令,SCCB的停止信号
{
	GPIO_WriteBit(SCCB_SID_PORT, SCCB_SID_PIN, 0);
	delay(DELAYTIME);
	GPIO_WriteBit(SCCB_SIC_PORT, SCCB_SIC_PIN, 1);
	delay(DELAYTIME);
	GPIO_WriteBit(SCCB_SID_PORT, SCCB_SID_PIN, 1);
	delay(DELAYTIME);  
}
static void No_Ack(void)//noAck,用于连续读取中的最后一个结束周期
{
	GPIO_WriteBit(SCCB_SID_PORT, SCCB_SID_PIN, 1);
	delay(DELAYTIME);
	GPIO_WriteBit(SCCB_SIC_PORT, SCCB_SIC_PIN, 1);
	delay(DELAYTIME);
	GPIO_WriteBit(SCCB_SIC_PORT, SCCB_SIC_PIN, 0);
	delay(DELAYTIME);
	GPIO_WriteBit(SCCB_SID_PORT, SCCB_SID_PIN, 0);
	delay(DELAYTIME);
}
static u8 Get_Ack(void) 
{
	u8 Error;

	Sccb_Sid_Change_In();																			
	GPIO_WriteBit(SCCB_SID_PORT, SCCB_SID_PIN, 1);
	delay(DELAYTIME);

	GPIO_WriteBit(SCCB_SIC_PORT, SCCB_SIC_PIN, 1);
	delay(DELAYTIME);

	Error= GPIO_ReadInputDataBit(SCCB_SID_PORT, SCCB_SID_PIN);
	delay(DELAYTIME);

	GPIO_WriteBit(SCCB_SIC_PORT, SCCB_SIC_PIN, 0);
	delay(DELAYTIME);

	Sccb_Sid_Change_Out();//输出
	GPIO_WriteBit(SCCB_SID_PORT, SCCB_SID_PIN, 0);

	return !Error;
}

static u8 Sccb_Write_Byte(u8 dat)//写入一个字节的数据到SCCB
{
	u8 i;
	for(i=0;i<8;i++)
	{
		GPIO_WriteBit(SCCB_SID_PORT, SCCB_SID_PIN, (((dat<<i)&0x80))>>7);
		delay(DELAYTIME);

		GPIO_WriteBit(SCCB_SIC_PORT, SCCB_SIC_PIN, 1);
		delay(DELAYTIME);

		GPIO_WriteBit(SCCB_SIC_PORT, SCCB_SIC_PIN, 0);	 
		delay(DELAYTIME);
	}
	GPIO_WriteBit(SCCB_SID_PORT, SCCB_SID_PIN, 0);

	return Get_Ack();
}
static u8 Sccb_Read_Byte(void)//一个字节数据读取并且返回
{
	u8 i,rbyte=0;

	Sccb_Sid_Change_In();
	for(i=0;i<8;i++)
	{
		GPIO_WriteBit(SCCB_SIC_PORT, SCCB_SIC_PIN, 1);
		delay(DELAYTIME);

		if(GPIO_ReadInputDataBit(SCCB_SID_PORT, SCCB_SID_PIN)) rbyte|=(0x80>>i);
		delay(DELAYTIME);

		GPIO_WriteBit(SCCB_SIC_PORT, SCCB_SIC_PIN, 0);	 
		delay(DELAYTIME);
	} 

	Sccb_Sid_Change_Out();//输出
	GPIO_WriteBit(SCCB_SID_PORT, SCCB_SID_PIN, 0);
	return rbyte;
}

主函数部分

#include "stm32f10x.h"
#include "stm32f10x_it.h" 

#include "key.h"
#include "usart.h"
#include "delay.h"
#include "lcd.h"
#include "ov7670.h"
#include "rcc.h"
#include "ShowChar.h"
#include "discern.h"

extern vu8 Red_Vlaue, Green_Value ,Blue_Value;//��ֵ


int main(void)
{  
	unsigned int scan_time = 0;
 
	STM32_Clock_Init(16);                                    //��ʼ��ʱ��

	LCD_Init();	
	Key_Init();	                                             //��ʼ�� KEY1 PA8
	OV7670_GPIO_Init();                                      //OV7670���ų�ʼ�������ڴ��ڳ�ʼ��ǰ��
	//USART1_init();                                         //��ʼ������	

	TIM3_Configuration();                                    //10Khz�ļ���Ƶ�ʣ�������5000Ϊ500ms  
	LCD_Fill(0x6666);		
	while(!Sensor_Init());
	
	LCD_Fill(0xF800);
	delayms(100);

	scan_time = 2;
	
  //��ֵ����ֵ	
	Red_Vlaue = 24;
	Green_Value = 53;
	Blue_Value = 24;

	while(1)
	{
		if(scan_time <= 1) {
			CameraDiscern();                                    //����ʶ��
		}
		if(scan_time > 1) {
			CameraScan();                                       //����ͷɨ�����
			LCD_ShowNum(30,220,21 - scan_time, 2); 
			while(GPIO_ReadInputDataBit(KEY1_PORT,KEY1_PIN)==0) //��������
			{
				LCD_Fill(0x00);                                   //����
				Show_Title();                                     //��ʾ����
				Show_Card(0);                                     //��ʾ�ڼ��鳵��
				Show_Card(1);
				Show_Card(2);
				Show_Card(3);
				Show_Card(4);
				delay_ms(5000);
			}
		}
		if(scan_time == 20) {
			 scan_time = 0;
		}
		scan_time++;
	} 
}



5 最后

  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 基于STM32的二维码识别OneNet物联网云仓库系统是我毕业设计的主题。 本系统使用STM32开发板和相应的模块,结合OneNet物联网平台,实现对仓库中商品的二维码识别和管理。系统的设计包括硬件部分和软件部分。 硬件方面,我选择使用STM32开发板作为主控制器,配合摄像头模块进行图像采集。通过串口或者USB接口连接OneNet物联网平台,实现数据的传输。此外,我还会加入一些传感器模块,如温湿度传感器,用于监测仓库环境。 软件方面,我将使用C语言编程,基于HAL库来驱动STM32开发板和相应的模块。通过摄像头模块获取仓库中商品的二维码信息,然后通过串口或者USB将数据发送给OneNet物联网平台进行识别和存储。同时,我还会设计一个用户界面,方便用户查看仓库中商品的信息和状态。 系统的工作流程如下:摄像头模块不断扫描仓库中的二维码,将识别到的信息发送给STM32开发板;STM32开发板通过串口或者USB将数据发送给OneNet物联网平台;OneNet物联网平台接收到数据后,进行二维码识别和存储;用户可以通过用户界面访问OneNet物联网平台,查看仓库中商品的信息和状态。 这个系统的应用前景广阔。以二维码为识别标识,可以提高仓库商品的管理效率。同时,结合物联网平台,可以实现远程监控和管理仓库。 ### 回答2: 基于STM32的二维码识别OneNet物联网云仓库系统是一种利用STM32单片机实现的毕业设计项目。该系统旨在通过扫描二维码进行物联网设备的识别和管理,并将实时数据上传至OneNet物联网云平台。 在该系统中,通过STM32单片机与二维码识别模块的结合,能够快速高效地读取二维码上的信息。通过扫描二维码后,STM32将获取到的数据通过串口或网络模块上传给OneNet物联网云平台。 物联网云仓库系统的核心功能包括设备识别和管理。在设备识别方面,系统能够通过扫描二维码获取设备的唯一标识符,并通过OneNet平台获取该设备的详细信息。同时,系统还能够实现设备的状态监测和报警功能,提高仓库管理的效率和安全性。 在设备管理方面,系统能够实时监测设备的工作状态和数据变化,并将数据上传至OneNet平台。通过OneNet平台的数据分析功能,可以对设备进行远程控制和管理。同时,系统还支持对设备的扩展和管理,方便后续的仓库扩大或设备更换。 基于STM32的二维码识别OneNet物联网云仓库系统具有简洁的硬件结构和高效的数据传输能力。它可以实现仓库设备的智能管理和监控,提高仓库管理的效率和质量。 ### 回答3: 基于STM32的二维码识别OneNet物联网云仓库系统将利用STM32微控制器和二维码技术来实现物联网云仓库管理系统。这个系统的核心目标是实现仓库中商品的自动记录、管理和查询,并通过OneNet平台实现数据的实时监控和远程控制。 系统的硬件部分主要由STM32微控制器、二维码扫描模块、电机驱动模块和传感器模块等组成。首先,二维码扫描模块将负责将商品的二维码进行扫描并解析出商品的相关信息。接着,STM32微控制器将通过串口将解析出的信息传输到OneNet平台上。 软件部分主要由STM32嵌入式程序、OneNet平台的云服务以及PC端的管理软件构成。STM32嵌入式程序将负责二维码扫描模块的控制、数据的处理和上传。一旦商品被扫描,系统会将商品的信息发送到OneNet平台上,并根据实时数据更新仓库的商品存储情况。同时,OneNet平台会提供实时的仓库数据监控和查询功能,以便用户能够在PC端进行即时的掌控和管理。 通过基于STM32的二维码识别OneNet物联网云仓库系统,用户可以实现对仓库中商品的远程监控和管理,无需直接接触商品,提高了仓库的管理效率和准确性。此外,通过实时的云数据传输,仓库的存储和发货信息可以实时同步,降低了人工差错的风险。同时,系统还具备可扩展性,可以根据需求进行各种功能的扩展,如温湿度传感器的接入、库存预警等。整体上,这个系统极大地简化了仓库管理的流程,提高了效率,为用户提供了更好的使用体验。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值