這是一種平移/傾斜伺服設(shè)備,可幫助相機(jī)使用視覺(jué)自動(dòng)跟蹤彩色物體。
現(xiàn)在我們將使用我們的設(shè)備幫助相機(jī)自動(dòng)跟蹤彩色對(duì)象,如下所示:
這是我第一次使用 OpenCV,我必須承認(rèn),我對(duì)這個(gè)奇妙的“開(kāi)源計(jì)算機(jī)視覺(jué)庫(kù)”挺感興趣的。
OpenCV可免費(fèi)用于學(xué)術(shù)和商業(yè)用途。它具有 C++、C、Python 和 Java 接口,并支持 Windows、Linux、Mac OS、iOS 和 Android。在我的一系列 OpenCV 教程中,我們將專(zhuān)注于 Raspberry Pi(因此,Raspbian as OS)和 Python。OpenCV 旨在提高計(jì)算效率,并且非常注重實(shí)時(shí)應(yīng)用程序。因此,它非常適合物理計(jì)算項(xiàng)目!
第 1 步:BOM - 物料清單
主要部分:
樹(shù)莓派 V3
5 兆像素 1080p 傳感器 OV5647 迷你攝像頭視頻模塊
TowerPro SG90 9G 180 度微型伺服(2 X)
帶 2 個(gè)伺服系統(tǒng)的迷你平移/傾斜攝像機(jī)平臺(tái)防震攝像機(jī)支架(*)
LED 紅色
電阻 220 歐姆
電阻 1K ohm (2X) - 可選
雜項(xiàng):金屬部件、帶子等(以防您構(gòu)建平移/傾斜機(jī)構(gòu))
第 2 步:安裝 OpenCV 3
我正在使用更新到最新版本的 Raspbian (Stretch) 的 Raspberry Pi V3,因此安裝 OpenCV 的最佳方法是遵循 Adrian Rosebrock 開(kāi)發(fā)的優(yōu)秀教程:Raspbian Stretch: Install OpenCV 3 + Python on your Raspberry Pi 。
完成后您應(yīng)該準(zhǔn)備好 OpenCV 虛擬環(huán)境,以便在您的 Pi 上運(yùn)行我們的實(shí)驗(yàn)。
讓我們進(jìn)入我們的虛擬環(huán)境并確認(rèn) OpenCV 3 已正確安裝。
建議您每次打開(kāi)新終端時(shí)都運(yùn)行命令“source”,以確保您的系統(tǒng)變量已正確設(shè)置。
source ~/.profile
接下來(lái),讓我們進(jìn)入我們的虛擬環(huán)境:
workon cv
如果您在提示符之前看到文本 (cv),則您處于cv 虛擬環(huán)境中:
(cv) pi@raspberry:~$
注意cv Python 虛擬環(huán)境是完全獨(dú)立的,并且與 Raspbian Stretch 下載中包含的默認(rèn) Python 版本隔離開(kāi)來(lái)。因此,全局 site-packages 目錄中的任何 Python 包都對(duì) cv 虛擬環(huán)境不可用。同樣,安裝在 cv 的 site-packages 中的任何 Python 包都不能用于 Python 的全局安裝。
現(xiàn)在,輸入你的 Python 解釋器:
python
并確認(rèn)您運(yùn)行的是 3.5(或更高)版本
在解釋器中(將出現(xiàn)“〉〉〉”),導(dǎo)入 OpenCV 庫(kù):
import cv2
如果沒(méi)有出現(xiàn)錯(cuò)誤消息,則 OpenCV 已正確安裝在您的 PYTHON 虛擬環(huán)境中。
您還可以檢查安裝的 OpenCV 版本:
cv2.__version__
3.3.0 應(yīng)該會(huì)出現(xiàn)(或?qū)?lái)可以發(fā)布的更高版本)。上面的終端 PrintScreen 顯示了前面的步驟。
第 3 步:測(cè)試您的相機(jī)
在您的 RPi 中安裝 OpenCV 后,讓我們測(cè)試您的相機(jī)是否正常工作。
我假設(shè)您的 Raspberry Pi 上已經(jīng)安裝了 PiCam。
在您的 IDE 中輸入以下 Python 代碼:
import numpy as np
import cv2
cap = cv2.VideoCapture(0)
while(True):
ret, frame = cap.read()
frame = cv2.flip(frame, -1) # Flip camera vertically
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
cv2.imshow(‘frame’, frame)
cv2.imshow(‘gray’, gray)
if cv2.waitKey(1) & 0xFF == ord(‘q’):
break
cap.release()
cv2.destroyAllWindows()
上面的代碼將捕獲將由您的 PiCam 生成的視頻流,并以 BGR 顏色和灰色模式顯示。
請(qǐng)注意,由于它的組裝方式,我垂直旋轉(zhuǎn)了我的相機(jī)。
您也可以從本文下方下載代碼。
要執(zhí)行,請(qǐng)輸入命令:
要完成程序,您必須按鍵盤(pán)上的 [q] 或 [Ctrl] + [C] 鍵
圖片顯示了結(jié)果:
第 4 步:使用 OpenCV 在 Python 中進(jìn)行顏色檢測(cè)
我們將嘗試完成的一件事是檢測(cè)和跟蹤某種顏色的對(duì)象。為此,我們必須更多地了解 OpenCV 如何解釋顏色。
Henri Dang用 OpenCV 寫(xiě)了一篇關(guān)于 Python 中顏色檢測(cè)的精彩教程。
通常,我們的相機(jī)將使用 RGB 顏色模式,可以將其理解為可以由紅色、綠色和藍(lán)色三種顏色的光組成的所有可能的顏色。我們將在這里使用 BGR(藍(lán)色、綠色、紅色)代替。
如上所述,在 BGR 中,一個(gè)像素由藍(lán)色、綠色和紅色 3 個(gè)參數(shù)表示。每個(gè)參數(shù)通常有一個(gè)從 0 到 255 的值(或十六進(jìn)制的 O 到 FF)。例如,計(jì)算機(jī)屏幕上的純藍(lán)色像素的 B 值為 255,G 值為 0,R 值為 0。
OpenCV 與 HSV(色相、飽和度、值)顏色模型一起使用,它是 RGB 顏色模型的替代表示,由計(jì)算機(jī)圖形研究人員在 1970 年代設(shè)計(jì),以更接近人類(lèi)視覺(jué)感知顏色生成屬性的方式:
因此,如果您想使用 OpenCV 跟蹤某種顏色,則必須使用 HSV 模型對(duì)其進(jìn)行定義。
例子
假設(shè)我必須跟蹤一個(gè)黃色物體,如上圖所示的塑料盒。容易的部分是找到它的 BGR 元素。您可以使用任何設(shè)計(jì)程序來(lái)找到它(我使用了 PowerPoint)。
就我而言,我發(fā)現(xiàn):
藍(lán)色:71
綠色:234
紅色:213
接下來(lái),我們必須將 BGR (71, 234, 213) 模型轉(zhuǎn)換為 HSV 模型,該模型將定義上限和下限范圍。為此,讓我們運(yùn)行以下代碼:
import sys
import numpy as np
import cv2
blue = sys.argv[1]
green = sys.argv[2]
red = sys.argv[3]
color = np.uint8([[[blue, green, red]]])
hsv_color = cv2.cvtColor(color, cv2.COLOR_BGR2HSV)
hue = hsv_color[0][0][0]
print(“Lower bound is :”),
print(“[” + str(hue-10) + “, 100, 100]\n”)
print(“Upper bound is :”),
print(“[” + str(hue + 10) + “, 255, 255]”)
要執(zhí)行,請(qǐng)輸入以下命令,將之前找到的 BGR 值作為參數(shù):
python bgr_hsv_converter.py 71 234 213
程序?qū)⒋蛴∥覀儗?duì)象顏色的上下邊界。
在這種情況下:
lower bound: [24, 100, 100]
和
upper bound: [44, 255, 255]
終端打印屏幕顯示結(jié)果:
最后但同樣重要的是,讓我們看看一旦我們確定了對(duì)象的顏色,OpenCV 如何“屏蔽”我們的對(duì)象:
import cv2
import numpy as np
# Read the picure - The 1 means we want the image in BGR
img = cv2.imread(‘yellow_object.JPG’, 1)
# resize imag to 20% in each axis
img = cv2.resize(img, (0,0), fx=0.2, fy=0.2)
# convert BGR image to a HSV image
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
# NumPy to create arrays to hold lower and upper range
# The “dtype = np.uint8” means that data type is an 8 bit integer
lower_range = np.array([24, 100, 100], dtype=np.uint8)
upper_range = np.array([44, 255, 255], dtype=np.uint8)
# create a mask for image
mask = cv2.inRange(hsv, lower_range, upper_range)
# display both the mask and the image side-by-side
cv2.imshow(‘mask’,mask)
cv2.imshow(‘image’, img)
# wait to user to press [ ESC ]
while(1):
k = cv2.waitKey(0)
if(k == 27):
break
cv2.destroyAllWindows()
要執(zhí)行,請(qǐng)輸入以下命令,在您的目錄中包含目標(biāo)對(duì)象的照片(在我的情況下:yellow_object.JPG):
python colorDetection.py
上圖將顯示原始圖像(“圖像”)以及應(yīng)用蒙版后對(duì)象將如何出現(xiàn)(“蒙版”)。
第 5 步:對(duì)象移動(dòng)跟蹤
現(xiàn)在我們知道如何使用蒙版“選擇”我們的對(duì)象,讓我們使用相機(jī)實(shí)時(shí)跟蹤它的移動(dòng)。為此,我的代碼基于Adrian Rosebrock 的 Ball Tracking with OpenCV 教程l。
首先,確認(rèn)您是否安裝了imutils 庫(kù)。它是 Adrian 的 OpenCV 便利函數(shù)集合,可以使一些基本任務(wù)(如調(diào)整大小或翻轉(zhuǎn)屏幕)變得更加容易。如果沒(méi)有,請(qǐng)輸入以下命令以在您的虛擬 Python 環(huán)境中安裝該庫(kù):
pip install imutils
接下來(lái),從我的 GitHub 下載代碼ball_tracking.py ,并使用以下命令執(zhí)行它:
python ball_traking.py
基本上,它與阿德里安的代碼相同,除非“視頻垂直翻轉(zhuǎn)”,我得到了這條線:
frame = imutils.rotate(frame, angle=180)
另外,請(qǐng)注意,使用的掩碼邊界是我們?cè)谏弦徊街蝎@得的。
第 6 步:測(cè)試 GPIO
現(xiàn)在我們已經(jīng)了解了 OpenCV 的基礎(chǔ)知識(shí),讓我們?cè)?RPi 上安裝一個(gè) LED 并開(kāi)始與我們的 GPIO 交互。
按照上面的電氣圖:LED 的陰極將通過(guò)一個(gè) 220 歐姆的電阻連接到 GPIO 21,其陽(yáng)極連接到 GND。
讓我們?cè)谔摂M Python 環(huán)境中測(cè)試我們的 LED。
請(qǐng)記住,您的 Python 虛擬環(huán)境中可能未安裝 RPi.GPIO!要解決這個(gè)問(wèn)題,你需要使用 pip 將它安裝到你的虛擬環(huán)境中:
pip install RPi.GPIO
讓我們使用 python 腳本來(lái)執(zhí)行一個(gè)簡(jiǎn)單的測(cè)試:
import sys
import time
import RPi.GPIO as GPIO
# initialize GPIO and variables
redLed = int(sys.argv[1])
freq = int(sys.argv[2])
GPIO.setmode(GPIO.BCM)
GPIO.setup(redLed, GPIO.OUT)
GPIO.setwarnings(False)
print(“\n [INFO] Blinking LED (5 times) connected at GPIO {0} at every {1} second(s)”.format(redLed, freq))
for i in range(5):
GPIO.output(redLed, GPIO.LOW)
time.sleep(freq)
GPIO.output(redLed, GPIO.HIGH)
time.sleep(freq)
# do a bit of cleanup
print(“\n [INFO] Exiting Program and cleanup stuff \n”)
GPIO.cleanup()
此代碼將接收 GPIO 編號(hào)和 LED 閃爍頻率(以秒為單位)作為參數(shù)。LED 將閃爍 5 次,程序?qū)⒔K止。請(qǐng)注意,在終止之前,我們將釋放 GPIO。
因此,要執(zhí)行腳本,您必須輸入LED GPIO和頻率作為參數(shù)。
例如:
python LED_simple_test.py 21 1
上述命令將每“1”秒將連接到“GPIO 21”的紅色 LED 閃爍 5 次。
上面的終端打印屏幕顯示了結(jié)果(當(dāng)然您應(yīng)該確認(rèn) LED 正在閃爍。
現(xiàn)在,讓我們使用 OpenCV 和一些基本的 GPIO 東西。
第 7 步:識(shí)別顏色和 GPIO 交互
讓我們開(kāi)始將我們的 OpenCV 代碼與 GPIO 交互集成。我們將從最后一個(gè) OpenCV 代碼開(kāi)始,我們將在其上集成 GPIO-RPI 庫(kù),因此我們將在相機(jī)發(fā)現(xiàn)我們的彩色對(duì)象時(shí)打開(kāi)紅色 LED。此步驟中使用的代碼基于 Adrian 的出色教程O(píng)penCV、RPi.GPIO 和 Raspberry Pi 上的 GPIO Zero :
首先要做的是“創(chuàng)建”我們的 LED,將其連接到特定的 GPIO:
import RPi.GPIO as GPIO
redLed = 21
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
GPIO.setup(redLed, GPIO.OUT)
其次,我們必須初始化我們的 LED(關(guān)閉):
GPIO.output(redLed, GPIO.LOW)
ledOn = False
現(xiàn)在,在循環(huán)內(nèi)部,當(dāng)找到對(duì)象時(shí)創(chuàng)建“圓圈”,我們將打開(kāi) LED:
GPIO.output(redLed, GPIO.HIGH)
ledOn = True
讓我們從我的 GitHub 下載完整的代碼:object_detection_LED.py
使用以下命令運(yùn)行代碼:
python object_detection_LED.py
結(jié)果在這里。請(qǐng)注意,每次檢測(cè)到對(duì)象時(shí),LED(左下角)都會(huì)亮起:
嘗試使用不同的對(duì)象(顏色和格式)。您會(huì)看到,一旦遮罩邊界內(nèi)的顏色匹配,LED 就會(huì)打開(kāi)。
如上一步所述,我們僅在此處使用 LED。我拍視頻的時(shí)候已經(jīng)組裝好了云臺(tái),所以忽略它。我們將在下一步處理 PAN/TILT 機(jī)制。
第 8 步:云臺(tái)
現(xiàn)在我們已經(jīng)了解了 OpenCV 和 GPIO 的基礎(chǔ)知識(shí),讓我們安裝我們的平移/傾斜機(jī)制。
伺服器應(yīng)連接到外部 5V 電源,其數(shù)據(jù)引腳(在我的情況下,它們的黃色接線)連接到 Raspberry Pi GPIO,如下所示:
GPIO 17 ==〉 傾斜伺服
GPIO 27 ==〉 平移伺服
不要忘記將 GND 連接在一起 ==〉 Raspberry Pi - 伺服系統(tǒng) - 外部電源)
您可以選擇在 Raspberry Pi GPIO 和服務(wù)器數(shù)據(jù)輸入引腳之間串聯(lián)一個(gè) 1K 歐姆的電阻。如果出現(xiàn)伺服問(wèn)題,這將保護(hù)您的 RPi。
讓我們也利用這個(gè)機(jī)會(huì)在我們的虛擬 Python 環(huán)境中測(cè)試我們的伺服系統(tǒng)。
讓我們使用 Python 腳本對(duì)我們的驅(qū)動(dòng)程序執(zhí)行一些測(cè)試:
from time import sleep
import RPi.GPIO as GPIO
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
def setServoAngle(servo, angle):
pwm = GPIO.PWM(servo, 50)
pwm.start(8)
dutyCycle = angle / 18. + 3.
pwm.ChangeDutyCycle(dutyCycle)
sleep(0.3)
pwm.stop()
if __name__ == ‘__main__’:
import sys
servo = int(sys.argv[1])
GPIO.setup(servo, GPIO.OUT)
setServoAngle(servo, int(sys.argv[2]))
GPIO.cleanup()
上述代碼的核心是函數(shù) setServoAngle(servo, angle)。此函數(shù)接收作為參數(shù)、伺服 GPIO 編號(hào)和伺服必須定位的角度值。一旦這個(gè)函數(shù)的輸入是“角度”,我們必須將其轉(zhuǎn)換為等效的占空比。
要執(zhí)行腳本,您必須輸入伺服 GPIO和角度作為參數(shù)。
例如:
python angleServoCtrl.py 17 45
上述命令將連接在 GPIO 17(“傾斜”)上的伺服定位在“仰角”45 度。
第 9 步:查找對(duì)象實(shí)時(shí)位置
這里的想法是使用平移/傾斜機(jī)制將對(duì)象定位在屏幕中間。壞消息是,我們必須實(shí)時(shí)知道對(duì)象的位置。但好消息是,一旦我們已經(jīng)有了對(duì)象中心的坐標(biāo),這很容易。
首先,讓我們使用之前使用的“object_detect_LED”代碼并對(duì)其進(jìn)行修改以打印創(chuàng)建對(duì)象的 x,y 坐標(biāo)。
代碼的“核心”是我們找到對(duì)象并在其上畫(huà)一個(gè)圓的部分,圓的中心有一個(gè)紅點(diǎn)。
# only proceed if the radius meets a minimum size
if radius 〉 10:
# draw the circle and centroid on the frame,
# then update the list of tracked points
cv2.circle(frame, (int(x), int(y)), int(radius),
(0, 255, 255), 2)
cv2.circle(frame, center, 5, (0, 0, 255), -1)
# print center of circle coordinates
mapObjectPosition(int(x), int(y))
# if the led is not already on, turn the LED on
if not ledOn:
GPIO.output(redLed, GPIO.HIGH)
ledOn = True
讓我們將中心坐標(biāo)“導(dǎo)出”到mapObjectPosition(int(x), int(y))函數(shù)以打印其坐標(biāo)。函數(shù)下方:
def mapObjectPosition (x, y):
print (“[INFO] Object Center coordenates at X0 = {0} and Y0 = {1}”.format(x, y))
運(yùn)行程序,我們會(huì)在終端看到(x,y)位置坐標(biāo),如上圖所示。移動(dòng)對(duì)象并觀察坐標(biāo)。我們將意識(shí)到 x 從 0 到 500(從左到右)而 y 從 o 到 350(從上到下)。
現(xiàn)在我們必須使用這些坐標(biāo)作為我們的 Pan/Tilt 跟蹤系統(tǒng)的起點(diǎn)
第 10 步:對(duì)象位置跟蹤系統(tǒng)
我們希望我們的對(duì)象始終保持在屏幕的中心。因此,例如,讓我們定義,如果我們將考慮我們的對(duì)象“居中”:
220 《 x 《 280
160 《 y 《 210
在這些邊界之外,我們必須移動(dòng)我們的平移/傾斜機(jī)制來(lái)補(bǔ)償偏差?;诖?,我們可以構(gòu)建函數(shù)mapServoPosition(x, y)如下。請(qǐng)注意,此函數(shù)中用作參數(shù)的“x”和“y”與我們之前用于打印中心位置的參數(shù)相同:
# position servos to present object at center of the frame
def mapServoPosition (x, y):
global panAngle
global tiltAngle
if (x 《 220):
panAngle += 10
if panAngle 〉 140:
panAngle = 140
positionServo (panServo, panAngle)
if (x 〉 280):
panAngle -= 10
if panAngle 《 40:
panAngle = 40
positionServo (panServo, panAngle)
if (y 《 160):
tiltAngle += 10
if tiltAngle 〉 140:
tiltAngle = 140
positionServo (tiltServo, tiltAngle)
if (y 〉 210):
tiltAngle -= 10
if tiltAngle 《 40:
tiltAngle = 40
positionServo (tiltServo, tiltAngle)
根據(jù) (x, y) 坐標(biāo),使用函數(shù)positionServo(servo, angle) 生成伺服位置命令。例如,假設(shè) y 位置為“50”,這意味著我們的對(duì)象幾乎在屏幕頂部,可以翻譯為“相機(jī)視線”為“低”(假設(shè)傾斜角度為 120 度)所以我們必須“減小”傾斜角(假設(shè)為 100 度),因此相機(jī)瞄準(zhǔn)器將“向上”,而對(duì)象將在屏幕上“向下”(y 將增加到假設(shè) 190)。
上圖顯示了幾何方面的示例。想想 Pan 相機(jī)將如何工作。請(qǐng)注意屏幕沒(méi)有鏡像,這意味著如果您將對(duì)象移動(dòng)到“您的左側(cè)”,一旦您與相機(jī)相對(duì),它將在屏幕上移動(dòng)到“您的右側(cè)”。
函數(shù) positionServo(servo, angle) 可以寫(xiě)成:
def positionServo (servo, angle):
os.system(“python angleServoCtrl.py ” + str(servo) + “ ” + str(angle))
print(“[INFO] Positioning servo at GPIO {0} to {1} degrees\n”.format(servo, angle))
我們將調(diào)用前面顯示的腳本進(jìn)行伺服定位。
請(qǐng)注意,angleServoCtrl.py 必須與 objectDetectTrac.py 位于同一目錄中
第 11 步:結(jié)論
最后,我希望這個(gè)項(xiàng)目可以幫助其他人找到進(jìn)入令人興奮的電子世界的方式!
評(píng)論
查看更多