OpenMV学习笔记(一):色块识别及与STM通信传输中心坐标

目录

  • 1.前言
  • 2.硬件连接
  • 3.代码
  • 4.查看结果
  • 5.总结
  • 1.前言

    一直有想要写些东西的想法正好最近比较闲以及大创项目和电赛备赛需要用到OpenMV所以就记录一下学习过程。因为小白第一次写文章有什么错误希望大家包含在评论区指正。

    2.硬件连接

    2.1、Openmv端
    这里OpenMV端仅作为数据的发送端,所以只需要共地,以及OpenMV的TX(P4)与开发板的RX端连接即可。
    2.2、STM32端

    将开发板连接STM芯片RX端与转串口TX端的跳帽取下,再将OpenMV的TX端(P4)与STM的RX连接。如果使用USB转TTL则将TTL的RX端与STM的TX端连接,STM的RX端与OpenMV的TX端(P4)连接,然后共地,这样就可以在串口调试助手中查看数据的传输情况了。

    3.代码

    3.1、OpenMV端

    # Blob Detection and uart transport
    import sensor, image, time, math, pyb
    from pyb import UART
    import json
    import ustruct
    # For color tracking to work really well you should ideally be in a very, very,
    # very, controlled enviroment where the lighting is constant...A
    yellow_threshold   = (8, 22, -60, -3, 127, -128)
    # 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()
    sensor.set_pixformat(sensor.RGB565)
    sensor.set_framesize(sensor.QVGA)       #设置图像大小QVGA大小为320*240,所以中心坐标应该是(160,120)
    sensor.skip_frames(time = 2000)
    sensor.set_auto_gain(False)             # must be turned off for color tracking
    sensor.set_auto_whitebal(False)         # must be turned off for color tracking
    red_threshold_01=(66, 31, -58, -24, 127, -128)
    clock = time.clock()
    
    
    uart = UART(3, 115200)
    uart.init(115200, bits=8, parity=None, stop=1) # OpenMV端初始化与STM端配置一样即可。
    
    #**************************传输数据的函数************************************
    def sending_data(cx,cy):
        global uart;
        #frame=[0x2C,18,cx%0xff,int(cx/0xff),cy%0xff,int(cy/0xff),0x5B];
        #data = bytearray(frame)
        data = ustruct.pack("<bbhhb",               #格式为俩个字符俩个短整型(2字节)
                       0x2C,                        #帧头1
                       0x12,                        #帧头2
                       int(cx), # up sample by 4    #数据1
                       int(cy), # up sample by 4    #数据2
                       0x5B)
        uart.write(data);   #必须要传入一字节的数组,这个函数似乎不能发送单个字节,必须得一次发送多个字节
    #**************************************************************************
    
    #**************************找到最大的色块函数*******************************#
    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())
    
            print('you send:',output_str)            #打印色块中心点坐标到串行终端便于数据核验
            sending_data(max_blob.cx(),max_blob.cy())#发送色块框的中心点坐标
            #FH = bytearray([0x2C,0x12,blobs[0].cx(),blobs[0].cy(),0x5B])
    
            #uart.write(FH)
        else:
            print('not found!')
            sending_data(567,789)#如果没有找到符合条件的色块,那么发送一个不可能出现的坐标
            #FH = bytearray([0x2C,0x12,0x77,0x55,0x5B])
    
            #uart.write(FH)
    

    颜色识别:
    这里只需要使用阈值编辑器,将想要识别的颜色调节到纯白色如下图所示,然后将下面的LAB阈值填入 red_threshold_01=() 即可。
    获取中心坐标:
    blob.rect() 返回这个色块的外框——矩形元组(x, y, w, h),可以直接在 image.draw_rectangle中使用。
    blob.x() 返回色块的外框的x坐标(int),也可以通过blob[0]来获取。
    blob.y() 返回色块的外框的y坐标(int),也可以通过blob[1]来获取。
    blob.w() 返回色块的外框的宽度w(int),也可以通过blob[2]来获取。
    blob.h() 返回色块的外框的高度h(int),也可以通过blob[3]来获取。
    blob.pixels() 返回色块的像素数量(int),也可以通过blob[4]来获取。
    blob.cx() 返回色块的外框的中心x坐标(int),也可以通过blob[5]来获取。
    blob.cy() 返回色块的外框的中心y坐标(int),也可以通过blob[6]来获取。

    3.2、STM32端

    void DEBUG_USART_IRQHandler(void)                                
    {
        uint8_t com_data; 
    		uint8_t i;
    		static uint8_t   RxCounter1=0;
    		static uint16_t  RxBuffer1[7]={0};
    		static uint8_t   RxState = 0;	
    //		static uint8_t   RxFlag1 = 0;
    
    		if( USART_GetITStatus(USART1,USART_IT_RXNE)!=RESET)  	   //接收中断  
    		{ 
    				USART_ClearITPendingBit(USART1,USART_IT_RXNE);       //清除中断标志
    				com_data = USART_ReceiveData(DEBUG_USARTx );
    			
    				if(RxState==0&&com_data==0x2C)          //0x2c帧头
    				{
    					RxState=1;
    					RxBuffer1[RxCounter1++]=com_data;     //RxBuffer1[0]==0x2c RxCounter1==1
    				}
    		
    				else if(RxState==1&&com_data==0x12)     //0x12帧头
    				{
    					RxState=2;
    					RxBuffer1[RxCounter1++]=com_data;     //RxBuffer1[0]==0x12 RxCounter1==2
    				}
    		
    				else if(RxState==2)                     //开始接收有效数据
    				{
    					RxBuffer1[RxCounter1++]=com_data;     //全部接收完,RxCounter1==7
    
    					if(RxCounter1>=7||com_data == 0x5B)  //RxBuffer1接受满了,接收数据结束
    					{
    					  RxState=3;
    //						RxFlag1=1;
    						Cx=(RxBuffer1[RxCounter1-4]<<8)+(RxBuffer1[RxCounter1-5]);    //RxBuffer1[2]是openmv发送的第一个数据的低八位,RxBuffer1[3]是openmv发送的第一个数据的高八位
    						Cy=(RxBuffer1[RxCounter1-2]<<8)+(RxBuffer1[RxCounter1-3]);    //RxBuffer1[4]是openmv发送的第二个数据的低八位,RxBuffer1[5]是openmv发送的第二个数据的高八位
    			    	printf("X=%d,Y=%d\n",Cx,Cy);
    		            delay_ms(300);       //延时防止大量数据造成上位机卡顿
    					}
    				}
    		
    				else if(RxState==3)		//检测是否接受到结束标志
    				{
    						if(RxBuffer1[RxCounter1-1] == 0x5B)
    						{
    									USART_ITConfig(DEBUG_USARTx,USART_IT_RXNE,DISABLE);//关闭DTSABLE中断
    //									if(RxFlag1)
    //									{
    //									
    //									OLED_ShowNum(0,0,Cx,3,1,2);
    //									OLED_ShowNum(0,2,Cy,3,1,2);
    //									
    //									}
    //									RxFlag1 = 0;
    									RxCounter1 = 0;
    									RxState = 0;
    									USART_ITConfig(DEBUG_USARTx,USART_IT_RXNE,ENABLE);
    						}
    						else   //接收错误
    						{
    									RxState = 0;
    									RxCounter1=0;
    									for(i=0;i<7;i++)
    									{
    											RxBuffer1[i]=0xff;      //将存放数据数组清零
    									}
    						}
    				} 
    	
    				else   //接收异常
    				{
    						RxState = 0;
    						RxCounter1=0;
    						for(i=0;i<7;i++)
    						{
    								RxBuffer1[i]=0xff;      //将存放数据数组清零
    						}
    				}
    
    		}
    
    } 
    			
    

    看到这里或许会存在疑问,为什么我们发送五个数据却要定义一个七位的数据的接收数组呢?在OpenMV端的发送函数里面有一个"<bbhhb"的数据声明,其中b表示C语言的signed char类型占一个字节八位二进制数,h表示C语言的short类型占两个字节十六位二进制数。而我们的OpenMV一次只能发送一个字节八位二进制数,因此实际传输的数据为:数据帧1->数据帧2->数据1的低八位->数据1的高八位->数据2的低八位->数据2的高八位->数据帧3。至于为什么数据要使用两个字节,那是因为我们选择的是QVGA即320*240大小的图像,因此数据将会超过255即一个字节八位二进制数。

    4.查看结果

    左边是OpenMV IDE串行终端的数据,右边是调试助手中的数据。可以发现虽然延时导致部分数据没有被打印出来但是调试助手中的数据全部来自串行终端,所以代码正确。

    5.总结

    代码部分参考了这篇文章写的很好很详细。完整的工程代码可以去Gitee下载

    物联沃分享整理
    物联沃-IOTWORD物联网 » OpenMV学习笔记(一):色块识别及与STM通信传输中心坐标

    发表评论