1.前言
一直有想要寫些東西的想法正好最近比較閑以及大創項目和電賽備賽需要用到OpenMV所以就記錄一下學習過程。因為小白第一次寫文章有什么錯誤希望大家包含在評論區指正。
2.硬件連接
2.1、Openmv端
XDY~JN544R(L5VONC`HZQ~I.png (207.97 KB, 下載次數: 59)
下載附件
2022-4-6 00:02 上傳
這里OpenMV端僅作為數據的發送端,所以只需要共地,以及OpenMV的TX(P4)與開發板的RX端連接即可。
2.2、STM32端
51hei圖片20220406000313.png (170.4 KB, 下載次數: 62)
下載附件
2022-4-6 00:03 上傳
將開發板連接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=() 即可。
51hei圖片20220406000457.png (137.89 KB, 下載次數: 60)
下載附件
2022-4-6 00:05 上傳
獲取中心坐標:
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.查看結果
51hei圖片20220406000730.png (173.84 KB, 下載次數: 60)
下載附件
2022-4-6 00:07 上傳
左邊是OpenMV IDE串行終端的數據,右邊是調試助手中的數據。可以發現雖然延時導致部分數據沒有被打印出來但是調試助手中的數據全部來自串行終端,所以代碼正確。
## 5.總結
代碼部分參考了[這篇文章]寫的很好很詳細。完整的工程代碼可以去[Gitee下載]。
|