天天看點

OpenCV + 樹莓派制作智能車【1】

前言

以前制作智能車都是在STM32上制作的,學習了一點OpenCV想要實踐一下,一下就想到了買來一直在吃灰的樹莓派,做一個智能小車吧!

黑線識别

我們先在Windows上寫出基本能跑的檢測代碼:

首先我先用畫圖繪制了一張理想的跑道圖檔

OpenCV + 樹莓派制作智能車【1】

在讀取并且進行二值化後:

img = cv2.imread('sd0.png')#讀取圖檔
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)#轉為灰階圖
retval, dst = cv2.threshold(gray, 0, 255, cv2.THRESH_OTSU)# 大津法二值化
           
OpenCV + 樹莓派制作智能車【1】

理想情況真的很理想~(笑哭哈哈)

我們先檢測一條線上的情況,為了小車能夠更早的檢測到偏移并且做出反應,我們選擇靠近圖像頂部1/5處的直線作為檢測對象(如圖黑線):

OpenCV + 樹莓派制作智能車【1】

我們将中心初始化為圖像的中心

rows,cols,channels = img.shape#擷取圖像尺寸
center = int(cols/2)#初始位置中心點處
           

然後我們在中心的附近進行黑線的尋找,在找到中心黑線後中心黑線的中心點即為新的中心位置!

總體代碼如下:

import cv2
import numpy as np

#圖像預處理
img = cv2.imread('sd0.png')#讀取圖檔
rows,cols,channels = img.shape#擷取圖像尺寸
center = int(cols/2)#初始位置中心點處
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)#轉為灰階圖
retval, dst = cv2.threshold(gray, 0, 255, cv2.THRESH_OTSU)# 大津法二值化
#确定位置
h1 = dst[int(rows*4/5)]# 找到4/5處
cv2.line(img,(0,int(rows*1/5)),(cols,int(rows*1/5)),(0,0,0),1,cv2.LINE_AA)#畫輔助線
l_post = r_post = center#從上次中心點開始尋找邊界
if h1[center] == 0:#未偏離黑線
    #尋找黑線邊界
    while h1[l_post] == 0:
        l_post = l_post - 1
    while h1[r_post] == 0:
        r_post = r_post + 1
else:#偏離黑線
    #尋找黑線
    while h1[l_post] == 255:
        l_post = l_post - 1
    while h1[r_post] == 255:
        r_post = r_post + 1
    if (center - l_post) < (r_post - center):#黑線在左邊
        r_post = l_post
        while h1[l_post] == 0:
            l_post = l_post - 1
    else:#黑線在右
        rl_post = r_post
        while h1[r_post] == 0:
            r_post = r_post + 1 
center = (r_post + l_post)/2#确定位置
#确定中心點标記
cv2.circle(img,(int(center),int(rows*1/5)), 5, (0,0,255), -1)
#顯示效果
cv2.imshow("img",img)
cv2.waitKey(0)
cv2.destroyAllWindows()
           
OpenCV + 樹莓派制作智能車【1】

但是我在換了一張圖檔後識别失敗了!

OpenCV + 樹莓派制作智能車【1】

分析了一波原因,感覺是二值化後左邊的深藍色區域也變為黑色,并且距離中心位置更近,導緻錯誤檢測。但是如果在開始對正,連續行駛的情況下應該問題不大(其實是不知道咋辦了~)!

import cv2
import RPi.GPIO as gpio
import time
import numpy as np

# 定義引腳
sg = 12
moto1 = 13
moto2 = 15
moto3 = 16
moto4 = 18
# 設定GPIO口為BOARD編号規範
gpio.setmode(gpio.BOARD)
# 設定GPIO口為輸出
gpio.setup(sg, gpio.OUT)
gpio.setup(moto1, gpio.OUT)
gpio.setup(moto2, gpio.OUT)
gpio.setup(moto3, gpio.OUT)
gpio.setup(moto4, gpio.OUT)
# 設定PWM波,頻率為500Hz
sg_pwm = gpio.PWM(sg, 50)
moto1_PWM = gpio.PWM(moto1, 50)
moto2_PWM = gpio.PWM(moto2, 50)
moto3_PWM = gpio.PWM(moto3, 50)
moto4_PWM = gpio.PWM(moto4, 50)
# pwm波控制初始化
#0.9-1.5-2.1
sg_pwm.start(1.5*100/20)
moto1_PWM.start(0)
moto2_PWM.start(0)
moto3_PWM.start(30)
moto4_PWM.start(30)

cap = cv2.VideoCapture(0)
print(cap.isOpened())
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)


center = center_temp = 320 #初始位置中心點處
while(True):
    ret, img = cap.read()#擷取圖像
    #img = cv2.blur(img,(5,5))
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)#轉為灰階圖 
    retval, dst = cv2.threshold(gray, 0, 255, cv2.THRESH_OTSU)# 大津法二值化
    kernel = np.ones((5,5),np.uint8)#closing
    closing = cv2.morphologyEx(dst,cv2.MORPH_CLOSE,kernel)
    #确定位置
    h1 = closing[int(480/2)]# 找到1/2處
    # 找到黑色的像素點個數
    black_count = np.sum(h1 == 0)
    # 找到黑色的像素點索引
    black_index = np.where(h1 == 0)
    if black_count != 0:
        center_temp = (black_index[0][black_count - 1] + black_index[0][0]) / 2
        
    if (abs(center_temp - center) > 150):
        pass
    else:
        center = center_temp
    #change duty
    duty = (center - 320)*0.015 + 1.5*100/20
    if duty > 2.1*100/20:
        duty = 2.1*100/20
    elif duty < 0.9*100/20:
        duty = 0.9*100/20
        
    sg_pwm.ChangeDutyCycle(duty)
    
    #cv2.line(img,(0,int(480/2)),(640,int(480/2)),(0,0,0),1,cv2.LINE_AA)#畫輔助線
    #cv2.circle(img,(int(center),int(480/2)), 5, (0,0,255), -1)
    #顯示效果
    #cv2.imshow("img",closing)
    if cv2.waitKey(1) & 0xFF == ord('q'):#監測到鍵盤輸入q關閉
        break
cap.release()#釋放攝像頭
cv2.destroyAllWindows()#關閉視窗
pwm1.stop()
moto3_PWM.ChangeDutyCycle(0)
moto4_PWM.ChangeDutyCycle(0)
moto3_PWM.stop()
moto4_PWM.stop()
gpio.cleanup()



           

硬體

機械上面直接在網上買來了一個成品的車模~

OpenCV + 樹莓派制作智能車【1】

就這樣的,電池盒是後面放上去的,想要用三節18650達到12V左右的電壓。

電路上先用子產品搭一下,電源出來12V電壓使用 LM2596S-5.0V 的穩壓到5v給樹莓派供電(之前手上有L7805,兩塊并聯都不行,發燙導緻樹莓派不斷重新開機),再用 LM2596S-adj 可調版本的給舵機供電,和電源連接配接上一個顯示電壓的數位管,友善檢測電壓及時充電。

電機驅動買了一個現成的子產品,如圖小紅闆。

OpenCV + 樹莓派制作智能車【1】

買了一個超大的12V風扇放在電路頂端,給樹莓派、穩壓、電機驅動等的散熱。

攝像頭直接用的樹莓派攝像頭。

OpenCV + 樹莓派制作智能車【1】