0 前言
🔥 这两年开始毕业设计和毕业答辩的要求和难度不断提升,传统的毕设题目缺少创新和亮点,往往达不到毕业答辩的要求,这两年不断有学弟学妹告诉学长自己做的项目系统达不到老师的要求。
为了大家能够顺利以及最少的精力通过毕设,学长分享优质毕业设计项目,今天要分享的是
🚩 基于stm32与openmv的目标跟踪系统
🥇学长这里给一个题目综合评分(每项满分5分)
- 难度系数:3分
- 工作量:3分
- 创新点:5分
🧿 选题指导, 项目分享:
🧿 项目分享:
https://gitee.com/sinonfin/sharing
课题简介
随着智能化技术的迅猛发展,网络智能视频监控系统也得到了广泛的应用。视频监控系统经历了从模拟化向数字化、网络化、智能化的发展过程,目标检测和跟踪技术是计算机视觉的重要组成部分,也是实现监控系统智能化的基础,因其广泛的应用前景而成为近年来的研究热点。本文通过OpenMV作为是识别模块去识别被测物体(以红色小球为例),当其识别到红色小球后,判断小球中心点所在的区域信息,并将其区域标志位通过串口发送给STM32,当STM32接收到位置信息后对x轴、y轴的两个舵机参数进行操作,最后通过定时器输出合适的PWM波,控制舵机旋转相应的角度,使OpenMV摄像头对准被测物体,以实现物体追踪功能。
设计框架
主要分为两个方面:一个是OpenMV识别部分,另一个是STM32控制部分。通过OpenMV对被测物体进行捕捉,所用的被测物体为红色小球,OpenMV编程时,先对红色的色域进行调整,在识别到红色后,对红色进行判断,判断出识别区域中红色区域最大的部分,此举在过滤掉背景中存在小部分红色区域,造成对识别的干扰,识别到红色小球后,用矩形框将其框住,并显示矩形框的中心位置,判断中心位置所在区域,并将区域的标志信息会通过串口传递给STM32,STM32接收到数据后进行相应动作,使定时器输出PWM波,控制舵机旋转相应的角度,使OpenMV摄像头对准被测物体,以实现物体追踪功能。主要研究内容如下:
- 1.通过OpenMV识别出被测物体(以红色小球为例)。
- 2.在OpenMV识别出目标物体后,判断物体所在区域,将区域信息通过串口传输给STM32。
- 3.利用STM32的串口部分的应用及原理知识,对接收到的数据进行处理。
- 4.利用STM32的应用及原理知识,对处理好的数据进行判断后,对舵机进行控制。
- 5.利用STM32的定时器部分和PWM控制的应用及原理知识,控制舵机,使其转动适当角度,使OpenMV对准目标物体。主控制图如图所示。
舵机控制逻辑
3 硬件设计
硬件连接部分使用杜邦线连接,连接如下:3S锂电池接稳压板输入端以及直接给LED补光板供电,稳压板输出端接OpenMV的VIN和GND引脚、STM32的5V和GND引脚以及两个舵机的正(红色)负(棕色)极。OpenMV的P4引脚(串口3的TX)接STM32的PA10引脚(串口1的RX),OpenMV的P5引脚(串口3的RX)接STM32的PA9引脚(串口1的TX),STM32的PC7引脚(定时器3通道2)接x轴的舵机的信号线(橙黄色),STM32的PC7引脚(定时器3通道1)接y轴的舵机的信号线(橙黄色)。
4 软件设计
软件部分的功能主要分为两部分,一个是OpenMV部分,另一是STM32部分,OpenMV主要实现功能:完成被测物体的识别、寻找最大色块区域、判断被测物体所在区域、通过串口发送被测物体的位置信息。STM32部分主要实现功能:使用串口接收OpenMV发来的数据、通过定时器输出PWM波、以及实现控制舵机旋转追踪的目的。
整体逻辑
对被测物体的识别
本设计中被测物体为一个红色小球,因此对于物体的识别主要为颜色识别,在编程中首先需要对OpenMV的红色的阈值进行调整,意在告诉OpenMV“什么是红色”,打开阈值编辑器,对LAB的阈值进行调整,使二进制图像中只有红色区域的映像
import sensor, image, time, pyb
ensor.reset() # 初始化摄像头传感器.
sensor.set_pixformat(sensor.RGB565) # 使用RGB565.
sensor.set_framesize(sensor.QVGA) # 分辨率:320*240
sensor.skip_frames(10) # 让新设置生效.
sensor.set_auto_whitebal(False) # 关闭自动白平衡.
clock = time.clock() # Tracks FPS.
red_threshold = (14, 68, 11, 70, 9, 56) #红色阈值设定
while(True):
img = sensor.snapshot() # 拍照并返回图像.
blobs = img.find_blobs([red_threshold])
img.draw_rectangle(blobs)
img.draw_cross(blobs.cx(), blobs.cy())
判断被测物体所在区域
import sensor, image, time, pyb
from pyb import UART
red_threshold = (14, 68, 11, 70, 9, 56) #红色阈值设定
sensor.reset() # 初始化摄像头传感器.
sensor.set_pixformat(sensor.RGB565) # 使用RGB565.
sensor.set_framesize(sensor.QVGA) # 使用QVGA.
sensor.skip_frames(10) # 让新设置生效.
sensor.set_auto_whitebal(False) # 关闭自动白平衡.
clock = time.clock() # Tracks FPS.
uart = UART(3, 115200)
def find_max(blobs):
max_size=0
for blob in blobs:
if blob.pixels() > max_size:
max_blob=blob
max_size = blob.pixels()
return max_blob
led = pyb.LED(1)
while(True):
img = sensor.snapshot() # 拍照并返回图像.
blobs = img.find_blobs([red_threshold])
x_max = 320
x_min = 0
x_1 = 115
x_2 = 185
y_max = 240
y_min = 0
y_1 = 95
y_2 = 145
if blobs:
max_blob=find_max(blobs)
print('sum :', len(blobs))
img.draw_rectangle(max_blob.rect())
img.draw_cross(max_blob.cx(), max_blob.cy())
if max_blob.cx()>= x_min and max_blob.cx() <= x_1 and\
max_blob.cy() >= y_2 and max_blob.cy() <= y_max :
flag = 1
if max_blob.cx()>= x_2 and max_blob.cx() <= x_max and\
max_blob.cy() >= y_2 and max_blob.cy() <= y_max :
flag = 2
if max_blob.cx()>= x_min and max_blob.cx() <= x_1 and\
max_blob.cy() >= y_min and max_blob.cy() <= y_1 :
flag = 3
if max_blob.cx()>= x_2 and max_blob.cx() <= x_max and \
max_blob.cy() >= y_min and max_blob.cy() <= y_1 :
flag = 4
if max_blob.cx()> x_1 and max_blob.cx() < x_2 and\
max_blob.cy() > y_1 and max_blob.cy() < y_2 :
flag = 5
output_str="%d" %flag
led.on()
print('you send:',output_str)
uart.write(output_str+'\r\n')
else:
print('not found!')
led.off()
STM32程序:
main.c:
#include "STM32f10x.h"
#include "control.h"
#include "Systick.h"
#include "usart.h"
#include "pwm.h"
int main()
{
Systick_init(72);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //中断优先级分组
USART1_Init(115200);
TIM3_CH2_PWM_Init(20000-1,72-1); //20ms
TIM3_CH1_PWM_Init(20000-1,72-1);
//舵机角度参数:19500-17500(前者为0°,后者为180°)
TIM_SetCompare2(TIM3,19500);//x轴角度初始化,下面的舵机。
TIM_SetCompare1(TIM3,18700);//y轴角度初始化,上面的舵机。
Delay(2500);
while(1);
}
pwm.c:
#include "pwm.h"
void TIM3_CH2_PWM_Init(u16 per,u16 psc)//控制x轴舵机(引脚:PC7)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
/* 开启时钟 */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
/* GPIO配置 */
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_7;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;//复用推挽输出
GPIO_Init(GPIOC,&GPIO_InitStructure);
/* GPIO重映射 */
GPIO_PinRemapConfig(GPIO_FullRemap_TIM3,ENABLE);//改变指定管脚的映射
TIM_TimeBaseInitStructure.TIM_Period=per; //自动装载值
TIM_TimeBaseInitStructure.TIM_Prescaler=psc; //分频系数
TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStructure);
/* PWM参数配置 */
TIM_OCInitStructure.TIM_OCMode=TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OCPolarity=TIM_OCPolarity_Low;
TIM_OCInitStructure.TIM_OutputState=TIM_OutputState_Enable;
TIM_OC2Init(TIM3,&TIM_OCInitStructure); //输出比较通道2初始化
TIM_OC2PreloadConfig(TIM3,TIM_OCPreload_Enable);
TIM_ARRPreloadConfig(TIM3,ENABLE);//使能预装载寄存器
TIM_Cmd(TIM3,ENABLE); //使能定时器
}