OpenMV色塊定位-電賽的半個總結


回顧

今年暑假基本上待在了學校,一方面是因爲準備出去工作了,爭取再學點東西;另一方面便是準備一下17年8月的全國大學生電子競賽。
賽場是我們學院所在的實驗室,比賽期間幾乎都待在實驗室,甚至於睡覺。
我們選的題目是I組-室內可見光定位,有很多成員貢獻出可行的方案,也逐步嘗試突破。


前言

競賽不僅練就的是承受壓力的能力和團隊協作能力,也是對本身所學知識的總結利用。故在這篇博客中我想對我們組針對電賽I題的解決方案做一些彙總。
因爲通過了廣西區的評比之後,將我們的作品進行了封箱處理,所以在此時我記錄這件事時,沒有辦法提供實物圖樣以及演示效果展示,所以我想盡量詳細地去敘述。
首先這次競賽用到的主要材料有:

OpenMV2白光LED燈1w*380cm*80cm木製板*551單片機開發板12864LCD液晶顯示屏


正文

① I組題目以及其要求

題目
基本要求
說明

② 提供方案

  • LED燈放置於頂部三個不同位置,根據不同位置光通信時間差,計算出接收點位置。該方案是首先實行的,但是因爲對硬件性能要求高,比如LED的頻閃,傳感器的延遲都會導致出現很大的誤差,所以不可行。
  • LED燈放於頂部中心,並將底部的接收器做成金字塔樣式,四個面分別貼和放置一個光線強度傳感器,這樣在底部不同位置,根據每個面的不同光照強度,從而判斷底部傳感器所在位置。該方案較爲容易實現,但是受環境影響大,可以判斷傳感器大致所在區域,並不能確定其精確位置。該方案採用了,並作爲一個作品去參與了競賽,拿到了廣西區二等獎。
  • 採用底部放置攝像頭的方式,說實話是挺鋌而走險的方案。該方案是再頂部面板中心放置單點光源LED燈,點亮的LED燈會在上頂部面板中形成亮斑,而攝像頭則至於底部,用於捕捉該白色色斑,根據其亮斑在採集圖像中的相對位置,計算出底部攝像頭(傳感器)的位置。該方案幫助我們拿到了廣西區一等獎。

② 方案實行

我們採用的是底部放置攝像頭的方案,分爲三個模塊,模塊一是題中要求的LED燈,我們將三個LED燈匯聚成一個燈,讓它看起來像是一個亮點,該點放置於頂部面板對角線交點;模塊二是傳感器模塊,我們將傳感器模塊水平放置於底部座標面板上,並且攝像頭的圖像採集照片平面的長寬要和底部面板座標軸平行;模塊三是數據顯示模塊,我們是用51單片機開發板結合12864LCD液晶顯示器,通過串口接收數據,實現在屏幕上實時刷新當前座標位置。

注意點:
1. 實物搭建,木製板80*80cm*5塊形成立方體,內壁貼滿黑色表面磨砂紙,可用於較少外界光以及立方體內LED燈反光干擾。
2. 是捕捉白色色斑,我們設置爲捕捉單點最亮白色色塊,因爲五面立方體,有一面暴露與室外環境,故室外光線太強會導致目標色斑捕捉錯誤。故我們將三個LED燈匯聚成一點,增強光照強度,即使在外界光照較爲強烈的情況下也不會產生干擾。

③ 具體代碼以及詳解

在OpenMV開發板中寫入的代碼

更多教程可以參考@雲江科技的教程:【直達鏈接

import sensor, image, time
from pyb import UART

uart = UART(3, 9600)    #設置爲串口3、波特率爲9600發送數據
thresholds = (245, 255) #設置監測色塊閾值

sensor.reset()  #攝像頭初始化
sensor.set_pixformat(sensor.GRAYSCALE)  #設置爲灰度模式
sensor.set_framesize(sensor.QQVGA)      #畫幅爲QQVGA即分辨率爲160*120
sensor.skip_frames(time = 2000)         #跳過起始畫面,獲取穩定圖像
sensor.set_auto_gain(False) #在色塊檢測模式下關閉自動補光
sensor.set_auto_whitebal(False) #關閉白平衡
clock = time.clock()

xPositionNow = 0    # 初始化各座標值
yPositionNow = 0
xPositionLast = 0
yPositionLast = 0
imageSize = 128     

while(True):
    clock.tick()
    img = sensor.snapshot() #獲取當期所採集到的圖像快照
    # 設置色塊閾值,具體數值情況可以通過OpenMVIDE中的閾值調整功能來得出
    # 工具 → Mechine Vision → Threshold Editor 
    # area_threshold面積閾值設置爲100 ,如果色塊被面積小於100,則會被過濾掉
    # pixels_threshold 像素個數閾值,如果色塊像素數量小於這個值,會被過濾掉
    # merge 設置爲True,合併所有重疊的尋找到的blob爲一個色塊
    for blob in img.find_blobs([thresholds], pixels_threshold=100, area_threshold=100, merge=True):
        # 繪製相應的圖形,方便我們測試的時候使用
        img.draw_rectangle(blob.rect())
        img.draw_cross(blob.cx(), blob.cy())
        x = blob.cx() - (imageSize/2)
        y = (imageSize/2) - blob.cy()
        xPositionLast = xPositionNow
        yPositionLast = yPositionNow
        # 這個0.7的數值不固定,只是在調試的時候爲了使得像素點和座標單位cm匹配所設置的數值
        xPositionNow = x * 0.7
        yPositionNow = y * 0.7
        # 測試時打印出當前座標
        print(xPositionNow, yPositionNow, end = ',')
        # 通過串口將座標數據發送給單片機處理,實際上發送的就是一段文本
        uart.write(',' + str(xPositionNow) + ',' +  str((-1)*yPositionNow) + ',')
        # 判斷當前所在區域(A\B\C\D)
        if abs(xPositionNow) < 20 and abs(yPositionNow) < 20:
            uart.write('A\n')
            print('A')
        elif yPositionNow < -20 and yPositionNow < xPositionNow and (-1)*yPositionNow > xPositionNow:
            uart.write('B\n')
            print('B')
        elif xPositionNow > 20 and (-1)*yPositionNow < xPositionNow and yPositionNow < xPositionNow:
            uart.write('C\n')
            print('C')
        elif yPositionNow > 20 and xPositionNow < yPositionNow and (-1)*xPositionNow < yPositionNow:
            uart.write('D\n')
            print('D')
        else:
            uart.write('E\n')
            print('E')
        # 0.5s更新一下座標數據
        time.sleep(500)

在作爲顯示設備的51單片機中寫入的代碼

在這裏51單片機+LCD12864液晶屏只是作爲一個顯示設備,只需要顯示串口接收到的數據,需要注意的是波特率的一致,串口接收到的數據將會處理成亂碼。之前的原代碼沒有能找到了,找到了之前做GPS定位項目時的代碼,作爲顯示設備可以通用,現在貼上代碼:

#include<reg52.h>
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>

#define uchar unsigned char
#define uint unsigned int
#define Buf_Max 80

#define GPS_Buffer_Length 80
#define gpsRxBufferLength  76 

#define false 0
#define true 1

sbit led1 = P1^1;
sbit led2 = P1^2;

bit flag = 1;

typedef struct SaveData 
{
    char GPS_Buffer[GPS_Buffer_Length];
    char isGetData;     //是否獲取到GPS數據
    char isUsefull;     //定位信息是否有效
} xdata _SaveData;


char idata gpsRxBuffer[gpsRxBufferLength];
uchar RX_Count = 0;
_SaveData Save_Data;

char tempr;

#include "uart.h"
#include "lcd.h"

/*********初始化函數*********/
void init()
{
    TMOD=0x20;    //設定定時器T1工作方式2
    PCON=0x00;    //串口波特率正常9600,不加倍;
    SCON=0x50;    //藍牙串口工作方式爲3
    TH1=0xfd;     //T1定時器裝初值
    TL1=0xfd;     //T1定時器裝初值
    TR1=1;        //啓動T1定時器
    REN=1;        //允許串口接收
    SM0=0;        //設定串口工作方式1
    SM1=1;        //設定串口工作方式1
    EA=1;         //開總中斷
    ES=1;         //開串口中斷
        EX1=1;
}

/***********主函數**********/
void main()
{
    init();                  //初始化
    lcd_init();
    flag = 0;
  while(1)
    {   
        printfGps();
//      write_cmd(0x01);
  }
}

void sint() interrupt 4
{
    ES=0;
    if(RI == 1 && flag == 0)
    {   
        RI=0;
        tempr=SBUF;

        if(tempr == '\n')                                      
        {
            memset(Save_Data.GPS_Buffer, 0, GPS_Buffer_Length);      //清空
            memcpy(Save_Data.GPS_Buffer, gpsRxBuffer, RX_Count);    //保存數據
            Save_Data.isGetData = true;
            RX_Count = 0;
            memset(gpsRxBuffer, 0, gpsRxBufferLength);      //清空
            gpsRxBuffer[RX_Count] = '\0';//添加結束符
        }
        else
        {
            gpsRxBuffer[RX_Count++] = tempr;
        }       
    }
    ES=1;
}

寫在最後

發現寫博客雖然對鞏固知識很有幫助,但是有時候真是一件費時費力的事情,本來計劃詳細講解一下過程,分享一下經驗。又因爲馬上要出學校開始找工作了,所以覺得時間完全不夠用啊,包括寫這篇博文記錄一下,中間也是跨了一個多月的時間才重新總結,所以想想先把分析思路和主要代碼貼出來,關於更多具體的分析,之後有時間的話再進行更新吧。
這樣子算寫了半個總結了吧~~~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章