这是一种平移/倾斜伺服设备,可帮助相机使用视觉自动跟踪彩色物体。
现在我们将使用我们的设备帮助相机自动跟踪彩色对象,如下所示:
这是我第一次使用 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 步:结论
最后,我希望这个项目可以帮助其他人找到进入令人兴奋的电子世界的方式!
评论
查看更多