根据题目要求我们制定了两套方案,第一套是用树莓派+opencv+openmv+arduino作为主方案。树莓派作为图像处理的芯片,openmv作为辅助摄像头,arduino 作为控制舵机以及速度控制的芯片。为什么要这么做呢?主要目的是为了提高帧率以及去掉无效路径。
具体方案:
首先树莓派一个上系统后安装opencv库,采用python编程用hsv/灰度的方式找到色块并将位置信息发送给arduino。
我们将比赛地图作为绝对坐标,车自身分为6个象限,四个摄像头,前后摄像头是采用120帧的CMOS高速摄像头结合树莓派作为第1和第4象限,而左,右摄像头以x的中点分为5,6,2,3象限。这样可以时时无死角的确定信标灯的位置,再根据象限确定车的运动轨迹以取消无效路径。
# -*- coding: utf-8 -*-
"""
Created on Mon Jun 4 09:41:46 2018
@author: Hong
"""
import cv2
#from interfere import Servo #90 center
from interfere.data import switch
#from interfere import data
#from interfere.pid import PID
import RPi.GPIO as GPIO
import time
KP= 0.05
KI= 0
KD= 0
sum_error = 0
derror = 0
last_error = 0
Delay_1=0
Delay_2=0
lower_red = [150,50,60] #hsv色彩空间低值
upper_red = [180,255,255] #hsv色彩空间的高值
hightbigain = 100 #roi 的起点
hight = 140 #roi 的高
graylow = 130 #阈值分割的阈值
pixw = 320 #图像的像素宽度
pixh = 240 #图像得像素高
length_y = 200 #减速标志位
camera0 = cv2.VideoCapture(0) #前摄像头
camera1 = cv2.VideoCapture(1) #前摄像头
camera0.set(3,pixw)
camera0.set(4,pixh)
camera1.set(3,pixw)
camera1.set(4,pixh)
#============================Initializer_GPIO=======================
GPIO.setmode(GPIO.BCM)
GPIO.setup(16,GPIO.IN) # input (close the camere)
GPIO.setup(2,GPIO.OUT) #1 (main camera)
GPIO.setup(3,GPIO.OUT) #2 (behind camera)
GPIO.setup(20,GPIO.OUT)
#========================set_high===================================
GPIO.output(2,GPIO.HIGH)
GPIO.output(3,GPIO.HIGH)
def onMouse(event, x, y, flags, prams):
global xs,ys,ws,hs,selectObject,xo,yo,trackObject
if selectObject == True:
xs = min(x, xo)
ys = min(y, yo)
ws = abs(x-xo)
hs = abs(y-yo)
if event == cv2.EVENT_LBUTTONDOWN:
xo,yo = x, y
xs,ys,ws,hs= x, y, 0, 0
selectObject = True
elif event == cv2.EVENT_LBUTTONUP:
selectObject = False
trackObject = -1
#++++++++++++++++++++++Initializer_camera+++++++++++++++++++++++++++
infor0 = camera0.isOpened()
infor1 = camera1.isOpened()
print(infor0,infor1)
if (infor0 & infor1):
while(1):
t1 = time.time()
#def Read(camera0,camera1,graylow,med,hig,hiw,Delay_1,Delay_2): #find light
inputvalue0 = GPIO.input(16)
#print(inputvalue0)
x0 = x1 = y0 =y1 = 0
ret0,img0 = camera0.read(0)
ret1,img1 = camera1.read(0)
gray0 = cv2.cvtColor(img0, cv2.COLOR_BGR2GRAY)
gray1 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY)
cv2.threshold(gray0,graylow, 255, 0, gray0)
cv2.threshold(gray1,graylow, 255, 0, gray1)
roigray0 = gray0[hightbigain:hightbigain+hight,:]
roigray1 = gray1[hightbigain:hightbigain+hight,:]
aa0, contours0, hierarchy0 = cv2.findContours(roigray0, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
aa1, contours1, hierarchy1 = cv2.findContours(roigray1, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
rests0 = []
rests1 = []
#=================1摄像头图像===================================================
for contour0 in contours0:
approx0 = cv2.convexHull(contour0)
area0=cv2.contourArea(approx0)
rests0.append(area0)
#================2摄像头图像====================================================
for contour1 in contours1:
approx1 = cv2.convexHull(contour1)
area1=cv2.contourArea(approx1)
rests1.append(area1)
#==============================================================================
if inputvalue0==0:# go ahead and find light
if rests0: # go ahead
mx0 = max(rests0)
local0 = rests0.index(mx0)
x0,y0,w0,h0 = cv2.boundingRect(contours0[local0])
print("x0",x0,y0)
if y0 > length_y:
GPIO.output(2,GPIO.LOW) #give drive signal
GPIO.output(3,GPIO.HIGH)
GPIO.output(20,GPIO.LOW)
data.x_to_angle(x0)
else:
GPIO.output(2,GPIO.LOW) #give drive signal
GPIO.output(3,GPIO.HIGH)
GPIO.output(20,GPIO.HIGH)
data.x_to_angle(x0)
elif rests1 : # go back
mx1 = max(rests1)
local1 = rests1.index(mx1)
x1,y1,w1,h1 = cv2.boundingRect(contours1[local1])
if y1 > length_y:
GPIO.output(3,GPIO.LOW)
GPIO.output(2,GPIO.HIGH)
GPIO.output(20,GPIO.LOW)
print("x1",x1,y1)
data.x_to_angle(x1)
else:
GPIO.output(3,GPIO.LOW)
GPIO.output(2,GPIO.HIGH)
GPIO.output(20,GPIO.HIGH)
print("x1",x1,y1)
data.x_to_angle(x1)
else:
print("error!!!")
else: # direct back
print("zhitui")
data.x_to_angle(160)
#===================================================================================================
cv2.imshow("my0",img0)
cv2.imshow("my1",img1)
cv2.imshow("gray0",roigray0)
cv2.imshow("gray1",roigray1)
t2 = time.time()
print(t2-t1)
if (cv2.waitKey(1) & 0xff == ord('q')):
break
else:
continue
camera0.release()
camera1.release()
cv2.destroyAllWindows()
else:
print("Try again!\n")
由于树莓派串口发送数据只有少部分数据可以用因此我们采用映射的方式将数据压缩后发送给Arduino
data源码:
import serial
import time
ser = serial.Serial('/dev/ttyUSB0',9600,timeout = 1)
class switch(object):
def __init__(self,value):
self.value = value
self.fall = False
def __iter__(self):
yield self.match
raise StopIteration
def match(self,*args):
if self.fall or not args:
return True
elif self.value in args:
self.fall = True
return True
else:
return False
def x_to_angle(x):
if((x >= 130)and(x <= 190)):
x = (int)(x/2)
elif(x <130):
x = 0
else:
x = 31
# 75 83____98____113 121 servo
# 0 130___160___190 320 camera_x
# 0 65____80____95 320 camera_x / 2
# 4b 53____62____71 79 hex
for case in switch(x):
if case(0):
ser.write('\x4b'.encode())
break
if case(65):
ser.write('\x53'.encode())
break
if case(66):
ser.write('\x54'.encode())
break
if case(67):
ser.write('\x55'.encode())
break
if case(68):
ser.write('\x56'.encode())
break
if case(69):
ser.write('\x57'.encode())
break
if case(70):
ser.write('\x58'.encode())
break
if case(71):
ser.write('\x59'.encode())
break
if case(72):
ser.write('\x5a'.encode())
break
if case(73):
ser.write('\x5b'.encode())
break
if case(74):
ser.write('\x5c'.encode())
break
if case(75):
ser.write('\x5d'.encode())
break
if case(76):
ser.write('\x5e'.encode())
break
if case(77):
ser.write('\x5f'.encode())
break
if case(78):
ser.write('\x60'.encode())
break
if case(79):
ser.write('\x61'.encode())
break
if case(80):
ser.write('\x62'.encode())
break
if case(81):
ser.write('\x63'.encode())
break
if case(82):
ser.write('\x64'.encode())
break
if case(83):
ser.write('\x65'.encode())
break
if case(84):
ser.write('\x66'.encode())
break
if case(85):
ser.write('\x67'.encode())
break
if case(86):
ser.write('\x68'.encode())
break
if case(87):
ser.write('\x69'.encode())
break
if case(88):
ser.write('\x6a'.encode())
break
if case(89):
ser.write('\x6b'.encode())
break
if case(90):
ser.write('\x6c'.encode())
break
if case(91):
ser.write('\x6d'.encode())
break
if case(92):
ser.write('\x6e'.encode())
break
if case(93):
ser.write('\x6f'.encode())
break
if case(94):
ser.write('\x70'.encode())
break
if case(95):
ser.write('\x71'.encode())
break
if case(31):
ser.write('\x79'.encode())
break
if case():
break
以上完成了树莓派上面处理的全部内容。
速度控制以及舵机控制待续。