简单实现物体追踪

    • OpenMV简介
    • 材料准备
    • OpenMV实现脱机运行
      • 1.将OpenMV与电脑连接
      • 2.使用IDE把色块追踪代码烧录到OpenMV
    • 单片机准备好串口通信

OpenMV简介

open_mv是一款很方便的人工智能摄像头,自带很多ai算法,有很多应用的场景,今天我来教大家如何使用open_mv实现物体追踪、色块识别,并且把识别到的物品坐标信息通过串口传输给单片机等处理器。
如果你还没用过OpenMV,建议先简单入门IDE软件,然后进入到我们的主题:

材料准备

  1. OpenMV摄像头一个(不限版本)
  2. sd卡一张(内存不限,实在没有也可以不用)
  3. 单片机一块(能用串口功能的都行)
  4. 安卓线一根(能传输数据的)

OpenMV实现脱机运行

1.将OpenMV与电脑连接


如图所示,将OpenMV插上sd卡后,将usb口与电脑端相连,这时候电脑就会发出“咚咚”的声音,相当于是把U盘插入了电脑,这时候我们就可以打开这个U盘查看sd卡里面有什么文件,这里我建议把其他文件删除,剩下一个空的sd卡。如果是没插sd卡的话,模块自带的也有一个76kb的储存空间。

如图所示,将OpenMV插上sd卡后,将usb口与电脑端相连,这时候电脑就会发出“咚咚”的声音,相当于是把U盘插入了电脑,这时候我们就可以打开这个U盘查看sd卡里面有什么文件,这里我建议把其他文件删除,剩下一个空的sd卡。如果是没插sd卡的话,模块自带的也有一个76kb的储存空间。

在这里插入图片描述

2.使用IDE把色块追踪代码烧录到OpenMV

首先点击左下角的两个按键使电脑成功连接OpenMV,这个时候软件左上角就可以实时显示拍下的画面
在这里插入图片描述

再点击左上角随便打开一个代码

在这里插入图片描述

然后把下面的代码复制过去

# Blob Detection and uart transport
import sensor, image, time
from pyb import UART
import json
# For color tracking to work really well you should ideally be in a very, very,
# very, controlled enviroment where the lighting is constant...
yellow_threshold   = (65, 100, -10, 6, 24, 51)
# You may need to tweak the above settings for tracking green things...
# Select an area in the Framebuffer to copy the color settings.

sensor.reset() # Initialize the camera sensor.
sensor.set_pixformat(sensor.RGB565) # use RGB565.
sensor.set_framesize(sensor.QQVGA) # use QQVGA for speed.
sensor.skip_frames(10) # Let new settings take affect.
sensor.set_auto_whitebal(False) # turn this off.
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

while(True):
    img = sensor.snapshot() # Take a picture and return the image.

    blobs = img.find_blobs([yellow_threshold])
    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())

        output_str="[%d,%d]" % (max_blob.cx(),max_blob.cy()) #方式1
        #output_str=json.dumps([max_blob.cx(),max_blob.cy()]) #方式2
        print('you send:',output_str)
        uart.write(output_str+'\r\n')
    else:
        print('not found!')

我这段代码是从官方找到的最精简的代码,完全没有其他什么乱七八糟的函数,只传输面积最大的色块的x,y中心坐标,方便你使用它来传输物体坐标位置,实现追踪。找到上面yellow_threshold =(,,,,) 赋值的语句,意思是识别黄色色块,你要根据你的物体的颜色对这个语句进行赋值,可以百度一下你所需要的颜色的阈值,然后复制过去就可以了,最后保存一下。

下一步就是烧录程序了
点击左上角工具->save open…->点击弹出窗口的Yes

在这里插入图片描述
这时候,你再把OpenMV的文件盘打开,就会发现里面多了几个文件,其中有一个main.py文件就说烧录好的可执行文件,OpenMV脱机运行的时候会从该文件开始执行代码,并且把色块中心位置的坐标通过串口发送出去啦!

单片机准备好串口通信

OpenMV可以发送数据了,这时候就需要单片机接收数据了。首先编写一个串口功能初始化代码

void uart4_Init(void)
{
	GPIO_InitTypeDef GPIO_InitStructure; //串口端口配置结构体变量
	USART_InitTypeDef USART_InitStructure;//串口参数配置结构体变量
	NVIC_InitTypeDef NVIC_InitStructure;//串口中断配置结构体变量
	//使能 UART4 时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_UART4, ENABLE);	//打开串口复用时钟
   	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);   //打开PC端口时钟
  
	//UART4_TX   GPIOC.10
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; //PC.10
  	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//设定IO口的输出速度为50MHz
  	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;	//复用推挽输出
 	 GPIO_Init(GPIOC, &GPIO_InitStructure);//初始化GPIOC.10
   
  	//UART4_RX	  GPIOC.11初始化
  	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;//PC.11
  	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
  	GPIO_Init(GPIOC, &GPIO_InitStructure);//初始化GPIOC.10  

 	//Usart1 NVIC 配置
  	NVIC_InitStructure.NVIC_IRQChannel = UART4_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2 ;//抢占优先级2
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;		//子优先级3
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;			//IRQ通道使能
	NVIC_Init(&NVIC_InitStructure);	//根据指定的参数初始化VIC寄存器
  
  	//USART 初始化设置
	USART_InitStructure.USART_BaudRate = 115200;//串口波特率为115200
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
	USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位
	USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制
	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;	//收发模式使能
	USART_Init(UART4, &USART_InitStructure); //初始化串口4
	
	USART_ITConfig(UART4, USART_IT_RXNE, ENABLE);//开启串口接受中断
  
	USART_Cmd(UART4, ENABLE);                    //使能串口4 
  
 	 //如下语句解决第1个字节无法正确发送出去的问题
   	USART_ClearFlag(UART4, USART_FLAG_TC);       //清串口4发送标志
}

然后通过编写串口中断实现接收数据

void UART4_IRQHandler(void)			   //串口4全局中断服务函数
{
	u8 com_data;
  	//接收中断
	if( USART_GetITStatus(UART4,USART_IT_RXNE) )
	{
		USART_ClearITPendingBit(UART4,USART_IT_RXNE);//清除中断标志
		com_data = UART4->DR;
		Openmv_Receive_Data(com_data);//openmv数据处理函数
	}
}

void Openmv_Receive_Data(int16_t data)//接收Openmv传过来的数据
{
	static u8 openmv[18];	//存取数据
	static u8 state = 0;	
	static u8 bit_number=0;	
	if(state==0&&data==0x2C)
	{
		state=1;
		openmv[bit_number++]=data;
	}
	else if(state==1&&data==18)
	{
		state=2;
		openmv[bit_number++]=data;
	}
	else if(state==2)
	{
		openmv[bit_number++]=data;
		if(bit_number>=17)
		{
			state=3;
		}
	}
	else if(state==3)		//检测是否接受到结束标志
	{
        	if(data == 0x5B)
        	{
            		state = 0;
            		openmv[bit_number++]=data;
        	}
        	else if(data != 0x5B)
        	{
           		 state = 0;
            		for(i=0;i<18;i++)
            		{
               			 openmv[i]=0x00;
            		}           
        	}
	}    
	else
	{
		state = 0;
		bit_number=0;
            	for(i=0;i<18;i++)
            	{
               		 openmv[i]=0x00;
           	 }
	}
}

这时候就大功告成喇!你就可以在单片机中利用传输过来的数据进行分析利用,可以做一个追小球的车子或者做板球系统调pid等等。
补充一个字符串转数字函数,方便大家串口接收到openmv数据后的使用

if(USART3_RX_STA&0x8000)
		{
             u8 x=0,y=0,z=0;		
			len=USART3_RX_STA&0x3fff;
			for(t =0;t<len;t++)
				{
					USART3->DR=USART3_RX_BUF[t];
					while((USART3->SR&0X40)==0);
					if(USART3_RX_BUF[t]=='[')x=1,y=0;
					if(USART3_RX_BUF[t]==','){x=0,y=1;xx[t-1]='\0';}	
					if(USART3_RX_BUF[t]==']'){yy[z]='\0';x=0,y=0,z=0;};
					if(x==1&&USART3_RX_BUF[t]!='[')xx[t-1] = USART3_RX_BUF[t];
				  	if(y==1&&USART3_RX_BUF[t]!=','){yy[z] = USART3_RX_BUF[t]; z++;};
				}
			for(t=0;t<2;t++)
				{
					USART3->DR=USART3LAST[t];
					while((USART3->SR&0X40)==0);//µÈ´ý·¢ËͽáÊø
				}
			Xt=atoi(&xx[0]);
			Yt=atoi(&yy[0]);
			OLED_ShowNum(0,52,Yt,3,12);
			OLED_ShowNum(64,52,Xt,3,12);
			stop=0;
			USART3_RX_STA=0;
		}