這是一種平移/傾斜伺服設備,可幫助相機使用視覺自動跟蹤彩色物體。
現在我們將使用我們的設備幫助相機自動跟蹤彩色對象,如下所示:
這是我第一次使用 OpenCV,我必須承認,我對這個奇妙的“開源計算機視覺庫”挺感興趣的。
OpenCV可免費用于學術和商業用途。它具有 C++、C、Python 和 Java 接口,并支持 Windows、Linux、Mac OS、iOS 和 Android。在我的一系列 OpenCV 教程中,我們將專注于 Raspberry Pi(因此,Raspbian as OS)和 Python。OpenCV 旨在提高計算效率,并且非常注重實時應用程序。因此,它非常適合物理計算項目!
第 1 步:BOM - 物料清單
主要部分:
樹莓派 V3
5 兆像素 1080p 傳感器 OV5647 迷你攝像頭視頻模塊
TowerPro SG90 9G 180 度微型伺服(2 X)
帶 2 個伺服系統的迷你平移/傾斜攝像機平臺防震攝像機支架(*)
LED 紅色
電阻 220 歐姆
電阻 1K ohm (2X) - 可選
雜項:金屬部件、帶子等(以防您構建平移/傾斜機構)
第 2 步:安裝 OpenCV 3
我正在使用更新到最新版本的 Raspbian (Stretch) 的 Raspberry Pi V3,因此安裝 OpenCV 的最佳方法是遵循 Adrian Rosebrock 開發的優秀教程:Raspbian Stretch: Install OpenCV 3 + Python on your Raspberry Pi 。
完成后您應該準備好 OpenCV 虛擬環境,以便在您的 Pi 上運行我們的實驗。
讓我們進入我們的虛擬環境并確認 OpenCV 3 已正確安裝。
建議您每次打開新終端時都運行命令“source”,以確保您的系統變量已正確設置。
source ~/.profile
接下來,讓我們進入我們的虛擬環境:
workon cv
如果您在提示符之前看到文本 (cv),則您處于cv 虛擬環境中:
(cv) pi@raspberry:~$
注意cv Python 虛擬環境是完全獨立的,并且與 Raspbian Stretch 下載中包含的默認 Python 版本隔離開來。因此,全局 site-packages 目錄中的任何 Python 包都對 cv 虛擬環境不可用。同樣,安裝在 cv 的 site-packages 中的任何 Python 包都不能用于 Python 的全局安裝。
現在,輸入你的 Python 解釋器:
python
并確認您運行的是 3.5(或更高)版本
在解釋器中(將出現“〉〉〉”),導入 OpenCV 庫:
import cv2
如果沒有出現錯誤消息,則 OpenCV 已正確安裝在您的 PYTHON 虛擬環境中。
您還可以檢查安裝的 OpenCV 版本:
cv2.__version__
3.3.0 應該會出現(或將來可以發布的更高版本)。上面的終端 PrintScreen 顯示了前面的步驟。
第 3 步:測試您的相機
在您的 RPi 中安裝 OpenCV 后,讓我們測試您的相機是否正常工作。
我假設您的 Raspberry Pi 上已經安裝了 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] 或 [Ctrl] + [C] 鍵
圖片顯示了結果:
第 4 步:使用 OpenCV 在 Python 中進行顏色檢測
我們將嘗試完成的一件事是檢測和跟蹤某種顏色的對象。為此,我們必須更多地了解 OpenCV 如何解釋顏色。
Henri Dang用 OpenCV 寫了一篇關于 Python 中顏色檢測的精彩教程。
通常,我們的相機將使用 RGB 顏色模式,可以將其理解為可以由紅色、綠色和藍色三種顏色的光組成的所有可能的顏色。我們將在這里使用 BGR(藍色、綠色、紅色)代替。
如上所述,在 BGR 中,一個像素由藍色、綠色和紅色 3 個參數表示。每個參數通常有一個從 0 到 255 的值(或十六進制的 O 到 FF)。例如,計算機屏幕上的純藍色像素的 B 值為 255,G 值為 0,R 值為 0。
OpenCV 與 HSV(色相、飽和度、值)顏色模型一起使用,它是 RGB 顏色模型的替代表示,由計算機圖形研究人員在 1970 年代設計,以更接近人類視覺感知顏色生成屬性的方式:
因此,如果您想使用 OpenCV 跟蹤某種顏色,則必須使用 HSV 模型對其進行定義。
例子
假設我必須跟蹤一個黃色物體,如上圖所示的塑料盒。容易的部分是找到它的 BGR 元素。您可以使用任何設計程序來找到它(我使用了 PowerPoint)。
就我而言,我發現:
藍色:71
綠色:234
紅色:213
接下來,我們必須將 BGR (71, 234, 213) 模型轉換為 HSV 模型,該模型將定義上限和下限范圍。為此,讓我們運行以下代碼:
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]”)
要執行,請輸入以下命令,將之前找到的 BGR 值作為參數:
python bgr_hsv_converter.py 71 234 213
程序將打印我們對象顏色的上下邊界。
在這種情況下:
lower bound: [24, 100, 100]
和
upper bound: [44, 255, 255]
終端打印屏幕顯示結果:
最后但同樣重要的是,讓我們看看一旦我們確定了對象的顏色,OpenCV 如何“屏蔽”我們的對象:
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()
要執行,請輸入以下命令,在您的目錄中包含目標對象的照片(在我的情況下:yellow_object.JPG):
python colorDetection.py
上圖將顯示原始圖像(“圖像”)以及應用蒙版后對象將如何出現(“蒙版”)。
第 5 步:對象移動跟蹤
現在我們知道如何使用蒙版“選擇”我們的對象,讓我們使用相機實時跟蹤它的移動。為此,我的代碼基于Adrian Rosebrock 的 Ball Tracking with OpenCV 教程l。
首先,確認您是否安裝了imutils 庫。它是 Adrian 的 OpenCV 便利函數集合,可以使一些基本任務(如調整大小或翻轉屏幕)變得更加容易。如果沒有,請輸入以下命令以在您的虛擬 Python 環境中安裝該庫:
pip install imutils
接下來,從我的 GitHub 下載代碼ball_tracking.py ,并使用以下命令執行它:
python ball_traking.py
基本上,它與阿德里安的代碼相同,除非“視頻垂直翻轉”,我得到了這條線:
frame = imutils.rotate(frame, angle=180)
另外,請注意,使用的掩碼邊界是我們在上一步中獲得的。
第 6 步:測試 GPIO
現在我們已經了解了 OpenCV 的基礎知識,讓我們在 RPi 上安裝一個 LED 并開始與我們的 GPIO 交互。
按照上面的電氣圖:LED 的陰極將通過一個 220 歐姆的電阻連接到 GPIO 21,其陽極連接到 GND。
讓我們在虛擬 Python 環境中測試我們的 LED。
請記住,您的 Python 虛擬環境中可能未安裝 RPi.GPIO!要解決這個問題,你需要使用 pip 將它安裝到你的虛擬環境中:
pip install RPi.GPIO
讓我們使用 python 腳本來執行一個簡單的測試:
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 編號和 LED 閃爍頻率(以秒為單位)作為參數。LED 將閃爍 5 次,程序將終止。請注意,在終止之前,我們將釋放 GPIO。
因此,要執行腳本,您必須輸入LED GPIO和頻率作為參數。
例如:
python LED_simple_test.py 21 1
上述命令將每“1”秒將連接到“GPIO 21”的紅色 LED 閃爍 5 次。
上面的終端打印屏幕顯示了結果(當然您應該確認 LED 正在閃爍。
現在,讓我們使用 OpenCV 和一些基本的 GPIO 東西。
第 7 步:識別顏色和 GPIO 交互
讓我們開始將我們的 OpenCV 代碼與 GPIO 交互集成。我們將從最后一個 OpenCV 代碼開始,我們將在其上集成 GPIO-RPI 庫,因此我們將在相機發現我們的彩色對象時打開紅色 LED。此步驟中使用的代碼基于 Adrian 的出色教程OpenCV、RPi.GPIO 和 Raspberry Pi 上的 GPIO Zero :
首先要做的是“創建”我們的 LED,將其連接到特定的 GPIO:
import RPi.GPIO as GPIO
redLed = 21
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
GPIO.setup(redLed, GPIO.OUT)
其次,我們必須初始化我們的 LED(關閉):
GPIO.output(redLed, GPIO.LOW)
ledOn = False
現在,在循環內部,當找到對象時創建“圓圈”,我們將打開 LED:
GPIO.output(redLed, GPIO.HIGH)
ledOn = True
讓我們從我的 GitHub 下載完整的代碼:object_detection_LED.py
使用以下命令運行代碼:
python object_detection_LED.py
結果在這里。請注意,每次檢測到對象時,LED(左下角)都會亮起:
嘗試使用不同的對象(顏色和格式)。您會看到,一旦遮罩邊界內的顏色匹配,LED 就會打開。
如上一步所述,我們僅在此處使用 LED。我拍視頻的時候已經組裝好了云臺,所以忽略它。我們將在下一步處理 PAN/TILT 機制。
第 8 步:云臺
現在我們已經了解了 OpenCV 和 GPIO 的基礎知識,讓我們安裝我們的平移/傾斜機制。
伺服器應連接到外部 5V 電源,其數據引腳(在我的情況下,它們的黃色接線)連接到 Raspberry Pi GPIO,如下所示:
GPIO 17 ==〉 傾斜伺服
GPIO 27 ==〉 平移伺服
不要忘記將 GND 連接在一起 ==〉 Raspberry Pi - 伺服系統 - 外部電源)
您可以選擇在 Raspberry Pi GPIO 和服務器數據輸入引腳之間串聯一個 1K 歐姆的電阻。如果出現伺服問題,這將保護您的 RPi。
讓我們也利用這個機會在我們的虛擬 Python 環境中測試我們的伺服系統。
讓我們使用 Python 腳本對我們的驅動程序執行一些測試:
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()
上述代碼的核心是函數 setServoAngle(servo, angle)。此函數接收作為參數、伺服 GPIO 編號和伺服必須定位的角度值。一旦這個函數的輸入是“角度”,我們必須將其轉換為等效的占空比。
要執行腳本,您必須輸入伺服 GPIO和角度作為參數。
例如:
python angleServoCtrl.py 17 45
上述命令將連接在 GPIO 17(“傾斜”)上的伺服定位在“仰角”45 度。
第 9 步:查找對象實時位置
這里的想法是使用平移/傾斜機制將對象定位在屏幕中間。壞消息是,我們必須實時知道對象的位置。但好消息是,一旦我們已經有了對象中心的坐標,這很容易。
首先,讓我們使用之前使用的“object_detect_LED”代碼并對其進行修改以打印創建對象的 x,y 坐標。
代碼的“核心”是我們找到對象并在其上畫一個圓的部分,圓的中心有一個紅點。
# 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
讓我們將中心坐標“導出”到mapObjectPosition(int(x), int(y))函數以打印其坐標。函數下方:
def mapObjectPosition (x, y):
print (“[INFO] Object Center coordenates at X0 = {0} and Y0 = {1}”.format(x, y))
運行程序,我們會在終端看到(x,y)位置坐標,如上圖所示。移動對象并觀察坐標。我們將意識到 x 從 0 到 500(從左到右)而 y 從 o 到 350(從上到下)。
現在我們必須使用這些坐標作為我們的 Pan/Tilt 跟蹤系統的起點
第 10 步:對象位置跟蹤系統
我們希望我們的對象始終保持在屏幕的中心。因此,例如,讓我們定義,如果我們將考慮我們的對象“居中”:
220 《 x 《 280
160 《 y 《 210
在這些邊界之外,我們必須移動我們的平移/傾斜機制來補償偏差。基于此,我們可以構建函數mapServoPosition(x, y)如下。請注意,此函數中用作參數的“x”和“y”與我們之前用于打印中心位置的參數相同:
# 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)
根據 (x, y) 坐標,使用函數positionServo(servo, angle) 生成伺服位置命令。例如,假設 y 位置為“50”,這意味著我們的對象幾乎在屏幕頂部,可以翻譯為“相機視線”為“低”(假設傾斜角度為 120 度)所以我們必須“減小”傾斜角(假設為 100 度),因此相機瞄準器將“向上”,而對象將在屏幕上“向下”(y 將增加到假設 190)。
上圖顯示了幾何方面的示例。想想 Pan 相機將如何工作。請注意屏幕沒有鏡像,這意味著如果您將對象移動到“您的左側”,一旦您與相機相對,它將在屏幕上移動到“您的右側”。
函數 positionServo(servo, angle) 可以寫成:
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))
我們將調用前面顯示的腳本進行伺服定位。
請注意,angleServoCtrl.py 必須與 objectDetectTrac.py 位于同一目錄中
第 11 步:結論
最后,我希望這個項目可以幫助其他人找到進入令人興奮的電子世界的方式!
評論
查看更多