# initialize GPIO and variablesredLed = 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))fori inrange(5):GPIO.output(redLed, GPIO.LOW)time.sleep(freq)GPIO.output(redLed, GPIO.HIGH)time.sleep(freq)# do a bit of cleanupprint("n [INFO] Exiting Program and cleanup stuff n")GPIO.cleanup
上边的代码需要一个GPIO端口号和一个LED闪烁频率作为参数。LED闪烁5次后程序结束。结束之前记得释放GPIO。
也就是说,运行脚本时要给出两个参数:“LED GPIO”和frequency。举个例子:
pythonLED_simple_test.py21 1
上边的指令意味着使用“GPIO 21”上连接的LED灯,并且每1秒闪烁一次,总共闪烁五次。
同样,上边这段代码也可以在GitHub下载:GPIO_LED_test.py
上边的图片显示了我的程序结果。至于LED灯亮不亮,就要各位自己去检验啦。
好,下面让我们把OpenCV和基本GPIO操作一起耍起来~
7. 识别颜色和GPIO(General-purpose input/output:通用型输入输出)交互
让我们开始集成 OpenCV 代码和 GPIO 进行交互。我们会从 最后的OpenCV 代码开始,并且我们将会把 GPIO_RPI 库集成到代码中,其目的是在摄像头检测到我们的着色物体时,能使红色LED常亮。这一步骤使用的代码是基于 Adrian 写得非常不错的教程OpenCV, RPi.GPIO, and GPIO Zero on the Raspberry Pi
第一件需要做的事情是:”创建“我们的LED对象,目的是为了连接上指定的GPIO。
importRPi.GPIO asGPIOredLed = 21GPIO.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
运行代码使用到的命令行:
pythonobject_detection_LED.py
下面的图片就是实现的效果。提示:当物体被检测到时,在图片左下方的LED灯就会亮着。
试试不同颜色,不同形式的物体,你会发现一旦颜色和掩码范围内匹配的话,LED灯就会亮起来。
下面的视频显示了一些经验。要注意的是,只有在色值一定范围内的黄色物体才会被检测到,LED等会亮起来。而其他不同颜色的物体则会被略过。
正如最后一步解释的那样,我们只是用到了LED灯。但是在视频中,摄像头却集成了云台(Pan Tilt:指摄像头可全方位左右/上下移动),所以不妨先忽略它。我们会下一步骤中实现云台机制。
8. 云台机制
现在我们已经用上了基本的 OpenCV 和 GPIO,那么接下来我们升级一下云台机制。
获取更多细节,请查看我的教程:Pan-Tilt-Multi-Servo-Control
伺服(servo:一种微型电子与机械产品的合体转置)需要连接额外的 5V 电力供应模块,并且这些伺服使用它们的数据插口(在我这边,它们是黄色的布线)连接草莓派的 GPIO,连接方式如下:
GPIO 17 ==> 倾斜伺服
GPIO 27 ==> 水平伺服
不要忘记了将 GND(GND:ground,接地端)引脚 也连到一起 ==> 草莓派——伺服——额外电力供应模块
你有个可选项:在草莓派 GPIO 和 服务端的数据输入引脚之间串联一个 1K 欧姆的电阻。这个举措可以在伺服发生问题时保护你的草莓派。
让我们一起用这个机会在 虚拟 Python 环境中测试一下我们的伺服。
我们执行 Python 脚本来测试一下驱动器。
fromtime importsleepimportRPi.GPIO asGPIO)GPIO.setmode(GPIO.BCM)GPIO.setwarnings(False)defsetServoAngle(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__':importsysservo = int(sys.argv[1])GPIO.setup(servo, GPIO.OUT)setServoAngle(servo, int(sys.argv[2]))GPIO.cleanup
上面代码的核心是 setServoAngle(servo, angle)方法。这个方法会接收的参数有:一个 GPIO 数字,一个伺服被定位的角度值。一旦把角度值输入到这个方法中,我们必须将其转换到等效的工作周期中(duty cycle:指伺服进入角度变化的时间段)。
执行脚本时,你要输入两个参数值:GPIO 伺服对应的端口以及角度值。
例如:
pythonangleServoCtrl.py17 45
上面的命令行会将在 连接在GPIO 17端口的伺服(倾斜伺服)定位到45度的”海拔“上。
angleServoCtrl.py 文件可以在我的GitHub上下载到。
9. 实时地获取到物体位置
将物体定位到屏幕中央的想法会使用到云台机制。实现这个想法坏消息是 我们必须实时地定位到物体的位置,但好消息是 如果我们已经知道了物体中心坐标点,这将会很容易。
首先,我们使用之前用过的”object_detect_LED“代码,以及修改该代码,以打印出检测物体的 x,y坐标点。
代码可以从我的GitHub中下载到:objectDetectCoord.py
代码核心逻辑是:在检测到的物体区域画出一个圆,并且在圆的中心画一个红点。
# only proceed if the radius meets a minimum sizeifradius > 10:# draw the circle and centroid on the frame,# then update the list of tracked pointscv2.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 coordinatesmapObjectPosition(int(x), int(y))
# if the led is not already on, turn the LED onifnotledOn:GPIO.output(redLed, GPIO.HIGH)ledOn = True
我们输出中心点坐标到 mapObjectPosition(int(x), int(y)) 方法中,目的是打印这些坐标点。方法如下:
defmapObjectPosition(x, y):print("[INFO] Object Center coordinates atX0 = {0} and Y0 = {1}".format(x, y))
在跑这个程序时,我们会看到在命令行终端上输出的 (x,y)坐标点,如下图所示:
太好了!我们可以使用这些坐标点作为云台追踪系统的开始点。
10. 物体位置追踪系统
我们想要目标始终在屏幕的中央,我们来定义一下,例如:假如满足下方条件,我们就认为物体在中央:
220 < x < 280
160 < y < 210
而在这个界限之外的话,我们就需要通过移动云台装置来修正偏差。基于这个逻辑,我们可以构建如下方法mapServoPosition(x, y)。需要注意的是,该方法中使用到的”x“和”y“是和我们之前打印出来的中心位置一样的。
# position servos to present object at center of the framedefmapServoPosition (x, y):globalpanAngleglobaltiltAngleif(x < 220):panAngle+= 10ifpanAngle > 140:panAngle= 140positionServo(panServo, panAngle)if(x > 280):panAngle-= 10ifpanAngle < 40:panAngle= 40positionServo(panServo, panAngle)if(y < 160):tiltAngle+= 10iftiltAngle > 140:tiltAngle= 140positionServo(tiltServo, tiltAngle)if(y > 210):tiltAngle-= 10iftiltAngle < 40:tiltAngle= 40positionServo(tiltServo, tiltAngle)
基于这些(x,y)坐标点,并使用方法positionServo(servo, angle),伺服位置命令已经产生了。举个例子:假如”y“的位置是”50“,这就意味着我们的物体几乎在屏幕的顶部,也就是说 摄像头的视野是往下的(比如说倾斜装置处于120°上),所以要调低倾斜装置的角度(比如说调到100°),如此一来,摄像头的视野将会抬高,进而使得物体在屏幕上就会往下方移动(比如 y坐标提高到190的位置)。
上面的图例在几何上解释了举的例子。
思考一下水平装置上的摄像头如何移动的。要注意的是 屏幕并不是镜像映射的,也就是说,当你面对着摄像头时,如果你将物体移动到”你的左边“,但在屏幕上看,物体却会在”你的右边“移动。
positionServo(servo, angle)方法可以写成这样:
defpositionServo(servo, angle):os.system("python angleServoCtrl.py "+ str(servo) + " "+str(angle))print("[INFO] Positioning servo at GPIO {0} to {1}degreesn".format(servo, angle))
上面的代码中,我们将会调用之前展示的伺服移动脚本。
注意:angleServoCtrl.py一定要和 objectDetectTrac.py 在同一个目录下。
完整的代码可以从我的GitHub上下载:objectDetectTrack.py
下面的gif 展示了我们的项目运行的效果:
11.结论
我一如既往地希望这个项目能帮助其他人找到进入激动人心的电子世界的入口。
想要获取项目细节以及最终的代码,可以浏览我的GitHub仓库:OpenCV-Object-Face-Tracking 。
而想了解更多的项目,请浏览我博客:MJRoBot.org
下面是我下一篇教程的预告,我们将会探索”人脸追踪和检测“。
向你们发出来自世界南半球的问候!
我们下篇文章见!
相关参考内容与链接内容,请点击阅读原文访问查看。via https://towardsdatascience.com/automatic-vision-object-tracking-347af1cc8a3b
欢迎加入公众号读者群一起和同行交流,目前有SLAM、算法竞赛、图像检测分割、人脸人体、医学影像、计算摄影、自动驾驶、综合等微信群(以后会逐渐细分),请扫描下面微信号加群,备注:”昵称+学校/公司+研究方向“,例如:”张三 + 上海交大 + 视觉SLAM“。请按照格式备注,否则不予通过。添加成功后会根据研究方向邀请进入相关微信群。请勿在群内发送广告,否则会请出群,谢谢理解~返回搜狐,查看更多