NJUPT第二次积分赛小结与视觉部分开源

跟队友连肝一周多积分赛,写了一堆屎山,总算是今天完赛了。结果也还行,80分到手。其实题目是全做完了的,但验收时我nt了没操作好导致丢了不少分,而且整个控制流程也都基于一堆bug和屎山做的,所以其实能做成这样我也很满意了,下次积分赛再战,只要国赛不出bug就行。

先放题目吧,如图:

基础部分:

基础1:小车从1号区域出发,巡线到2号区域(15分)

基础2:在基础1的基础上,小车在2号区域成功转直角弯,并走到3号区域。(15分)

基础3:在基础2的基础上,小车从3号区域成功走到4号位置。(10分)

基础4:在基础3的基础上,小车停车成功,在框内,不压到边界线。(10分)

提高部分:

小车以正方形和圆心区分左右移动,其中正方形指示小车向左移动,圆形指示小车向右移动。在移动时,如果移动侧存在道路,为变道,不存在道路为避障。其中避障从指定方向(识别的指示方向)绕过障碍,然后立即再次回到之前的道路上。即存在道路即继续按照道路行驶,如果不存在则超过障碍需要切回道路。距离越短越好。

例如,小车从3区域走到A障碍物处,发现为方形障碍物,需要往左移动,所以变道到虚线道路上,继续沿着虚线行驶到B位置,看到B为圆形障碍物,则小车往右侧移动,变道到实线道路上,继续沿着实线前进,看到C处为方形障碍物,从障碍物右侧避障再立刻回到实线道路。继续往前走,由于道路上不经过D处障碍物,所以不做处理。(D为隔壁道路的指示牌,与当前道路无关,视为干扰项,随机在小车此刻相邻道路的某一位置)。

注:所有障碍物颜色均为红色,一共有且仅有三个需要做出反应的障碍物。

发挥1:小车每成功路过一个障碍物,并执行相应动作获得10分,共30分。(路过干扰障碍物不额外加分)

发挥2:小车在每次变道/避障时,需要提前进行声光提示,变完道需要关闭声光提示。注:声光提示必须体现处左转和右转区别。(5分)

发挥3:小车成功走到4区域并完成停车。(10分)

发挥部分:

自由发挥出额外特色功能。(5分)

注:禁止使用麦克纳姆轮小车。

当初看到题目的时候,没觉得有多难,但真正入手时,是真的感觉到了题目的恶意。。。

我们队用的是树莓派4B 4GB + STM32401CCU6,一个USB摄像头,一个mpu6050(屁用没有),一个oled,一个TB6612,还有车板(拿平衡车板子改的,机械结构很逆天。)

先说出我们队做题时的几个困难点:

1、线好细,2mm线径,灰度巡线的队伍直接寄。还好我和我做控制的队友都没用过灰度,所以整套传感器基本是纯摄像头(mpu6050只能算是摆设,几套控制全程开环)

2、场地数字干扰。数字和线离得很近,干扰蛮大的,图像部分方案选取不好的话容易寄。

3、场地地形干扰。从1区域到2区域时车子左边噪声干扰较大,因为紧挨桌子,同时圆弧顶部离墙特别近,我们队的车又比较宽,如果不做一些处理的话巡线会直接撞墙,同时墙与地板之间的夹线对图像部分也有一定干扰。

4、障碍物大小不同。障碍物做的太过随便,都是拿胶把红色卡纸贴快递盒子上,高度大小都不太一样,甚至还有卷边。两个圆方识别我也是调了不少时间。

5、控制方案是真不好做。四个动作,两个变道,两个避障。在做变道的时候试了不少方案,一开始是准备mpu转90度走一段然后再转90度就开始寻线,但是后来由于车子形态,pid控制,mpu零漂,代码屎山堆积等多方面因素影响,我们最终选择直接开环。。。然后程序直接少了上百行,可读性大大增强。至于变道怎么个开环法,那还得是车子强大的巡线功能,从一条线上转个角度就能直接巡到另一条线上然后就是避障,还是靠的巡线,转个角度走个圆弧就切进线了

6、通信。我们用的串口,因为最常用。至于其他乱七八糟的通信协议用的都不怎么熟练,所以直接选用串口,但由于第一次进行STM32与树莓派交互通信,bug频出,有一个bug找了整整一天,最后还是一个有经验的学长过来指导的。。。真的很感谢那位学长!红豆泥阿里嘎多!

废话不多说,直接上图像部分代码。这是我封装后的函数,所以要用的话直接复制就能用。

bgr转hsv

def TrackBar_Init():
cv2.namedWindow('hsv_binary')
cv2.createTrackbar('hmin', 'hsv_binary', 0, 179, call_back)
cv2.createTrackbar('hmax', 'hsv_binary', 179, 179, call_back)
cv2.createTrackbar('smin', 'hsv_binary', 0, 255, call_back)
cv2.createTrackbar('smax', 'hsv_binary', 255, 255, call_back)
cv2.createTrackbar('vmin', 'hsv_binary', 130, 255, call_back)
cv2.createTrackbar('vmax', 'hsv_binary', 255, 255, call_back)
def getHSV(image):
# 1 get trackbar's value
hmin = cv2.getTrackbarPos('hmin', 'hsv_binary')
hmax = cv2.getTrackbarPos('hmax', 'hsv_binary')
smin = cv2.getTrackbarPos('smin', 'hsv_binary')
smax = cv2.getTrackbarPos('smax', 'hsv_binary')
vmin = cv2.getTrackbarPos('vmin', 'hsv_binary')
vmax = cv2.getTrackbarPos('vmax', 'hsv_binary')
cv2.imshow("hsv_binary", win)
hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
h, s, v = cv2.split(hsv)
h_binary = cv2.inRange(np.array(h), np.array(hmin), np.array(hmax))
s_binary = cv2.inRange(np.array(s), np.array(smin), np.array(smax))
v_binary = cv2.inRange(np.array(v), np.array(vmin), np.array(vmax))
binary = 255 - cv2.bitwise_and(h_binary, cv2.bitwise_and(s_binary, v_binary))
return binary

逆时针90度旋转摄像头读取的图像:

def rotateAntiClock_90(img):
img = cv2.flip(cv2.transpose(img), 0)
return img

图像处理

def Image_Processing():
global frame, binary, dst, blur
ret, frame = camera.read()
frame = rotateAntiClock_90(frame) # 逆时针旋转90度
binary = getHSV(frame)
blur = cv2.GaussianBlur(binary, (9, 9), 0)
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (10, 10))
dst = cv2.morphologyEx(blur, cv2.MORPH_CLOSE, kernel)
cv2.imshow('Dilate', dst) # dst:处理后的图像

巡线(实线)

def Find_RealLine(src):  # 从左扫线
dst = src
direc1_x = 0
direc1_y = 0
diffCRL = 0
filtRate = 1 / 20
dirLine = int(dst.shape[0]*9/13) # 扫线
dot_x = []
# Canny边缘检测
dst_canny = cv2.Canny(dst, 190, 250)
h, w = dst_canny.shape[0:2]
dst_canny = dst_canny[:, 0 + int(filtRate*w):int(w - w*filtRate)] # 提取ROI
# cv2.imshow('canny', dst_canny)
# 找基准点,利用与运算掩模
dst_mask_line = np.zeros(dst_canny.shape, np.uint8)
dst_mask_line[dirLine, :] = 255 # 图像尺寸:640x480
dotImg = cv2.bitwise_and(dst_mask_line, dst_canny) # 使用按位与找交点
# 从左往右找第一第二个交点,取中点即为基准点
for i in range(0, dotImg.shape[1]):
if dotImg[dirLine, i] == 255:
dot_x.append(i)
try: # 防止没有找到交点导致列表内无数据的情况
x1 = dot_x[0]
x2 = dot_x[1]
# 基准点坐标
direc1_x = int((x1 + x2) / 2)
direc1_y = dirLine
print((direc1_x, direc1_y))
# 计算偏差值
diffCRL = direc1_x - int(1/2*dst_canny.shape[1])
print(diffCRL)
except:
pass
return diffCRL

巡线(虚线)

def Find_DottedLine(img):  # 巡虚线:利用图像矩(好用!)
# img = cv2.Canny(img, 50, 150)
area = 0
difDotLine = 0
h, w = img.shape[0:2]
img = img[int(3/5*h):h, :] # 提取ROI
m = cv2.moments(img)
try:
x = int(m['m10'] / m['m00'])
y = int(m['m01'] / m['m00'])
area = int(m['m00'])
difDotLine = int(x - 1 / 2 * img.shape[1])
# cv2.circle(frame, (x, y + int(2/3*h)), 7, (0, 255, 0), 4)
cv2.imshow('dstline', img)
except:
call_back()
return area, difDotLine

检测近似水平的线

def findHorizonLine(src):  # 检测水平直线
flag = 0
src = src[int(1/2 * src.shape[0]):src.shape[0], :] # 提取ROI
src_canny = cv2.Canny(src, 50, 150)
lines = cv2.HoughLinesP(src_canny, 1, np.pi / 180, 70, minLineLength=40, maxLineGap=10)
try:
for line in lines:
x1, y1, x2, y2 = line[0]
slope = (y2 - y1) / (x2 - x1 + 1e-6)
if abs(slope) > 0.3: # 滤掉斜率大于0.3的线
continue
else:
print("Found Horizon Line")
flag = 1
# cv2.line(frame, (x1, y1+int(1/2 * src.shape[0])), (x2, y2+int(1/2 * src.shape[0])), (0, 0, 255), 2)
except:
call_back()
return flag

检测近似垂直的线

def findVerticalLine(src):
flag = 0
src = src[int(src.shape[0]*2/3):src.shape[0], :]
lines = cv2.HoughLinesP(src, 1, np.pi / 180, 70, minLineLength=150, maxLineGap=5)
try:
for line in lines:
x1, y1, x2, y2 = line[0]
slope = (x2 - x1) / (y2 - y1 + 1e-6)
if abs(slope) > 0.3:
continue
else:
print("Found vertical Line")
flag = 1
# cv2.line(frame, (x1, y1 + int(src.shape[0]*2/3)), (x2, y2 + int(src.shape[0]*2/3)), (0, 0, 255), 2)
except:
call_back()
return flag

串口发送消息

def comPrint(_string):
message = _string
ser.write(message.encode())

串口接收单字节消息(能接收,但有bug)

def getMessage():
if ser.inWaiting():
string = ser.read(1) # 监听消息
return string

识别矩形

def getRect(img):
# global frame
shape = 0
centerPoint = (0, 0)
area = 0
_, contours, hierarchy = cv2.findContours(img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE) # openCV不同版本有区别
for obj in contours:
area = cv2.contourArea(obj) # 计算轮廓内区域的面积
perimeter = cv2.arcLength(obj, True) # 计算轮廓周长
approx = cv2.approxPolyDP(obj, 0.02 * perimeter, True) # 获取轮廓角点坐标
CornerNum = len(approx) # 轮廓角点的数量
x, y, w, h = cv2.boundingRect(approx) # 获取坐标值和宽度、高度
centerPoint = (int(x+(1/2)*w), int(y+(1/2)*w))
if CornerNum == 4:
shape = 4
# cv2.circle(frame, centerPoint, 3, (0,0,255), -1)
return shape, centerPoint, area

识别并提取圆形

def getCircle(src):  # src:处理过的图像
signalCircle = 0
circle = []
edges = cv2.Canny(src, 128, 255)
cv2.imshow('canny', edges)
# 检测图像形状
# edges = edges[int(edges.shape[0] * 1 / 10):edges.shape[0], :]
circles = cv2.HoughCircles(edges, cv2.HOUGH_GRADIENT, 1, 300,
param1=13, param2=26, minRadius=50,
maxRadius=200)
# 确保至少发现一个圆
if circles is not None:
# 进行取整操作
print("Circle here")
edges = np.zeros(edges.shape, np.uint8)
circles = np.round(circles[0, :]).astype("int")
signalCircle = 1
# 循环遍历所有的坐标和半径
# 绘制结果
for (x, y, r) in circles:
cv2.circle(frame, (x, y), r, (255, 0, 0), 4)
cv2.rectangle(frame, (x - 5, y - 5), (x + 5, y + 5), (255, 128, 0), -1)
circle.append((x,y,r))
return signalCircle, circle

提取红色部分

def getRedImg(img):
grid_HSV = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
# H、S、V范围一:
lower1 = np.array([0, 43, 46])
upper1 = np.array([10, 255, 255])
mask1 = cv2.inRange(grid_HSV, lower1, upper1) # mask1 为二值图像
# H、S、V范围二:
lower2 = np.array([156, 43, 46])
upper2 = np.array([180, 255, 255])
mask2 = cv2.inRange(grid_HSV, lower2, upper2)
# 将两个二值图像结果 相加
mask3 = mask1 + mask2
# 去除噪声
kernel = np.ones((5, 5), np.uint8)
dst3 = cv2.morphologyEx(mask3, cv2.MORPH_OPEN, kernel)
dst3 = cv2.medianBlur(dst3, 29)
return dst3

全套工程代码(一堆屎山)

import numpy as np
import cv2
import serial
import time
from numpy import *
import RPi.GPIO as GPIO ser = serial.Serial("/dev/ttyAMA0", 9600)
ser.timeout = 5
if not ser.isOpen:
ser.open() # 打开串口 width, height = 160, 120
camera = cv2.VideoCapture(0)
camera.set(100, width)
camera.set(80, height)
camera.set(cv2.CAP_PROP_FPS, 10)
win = np.zeros((300, 512, 3), np.uint8)
dst = np.zeros((width, height), np.uint8)
blur = np.zeros((width, height), np.uint8)
direc1_x = 0
direc1_y = 0
diffCDL = 0
diffCRL = 0
modeFlag = 0
horizonFlag = 0
verticalFlag = 0
followReaLineFlag = 1
followDottedLineFlag = 1
findDotHorLineFlag = 0
rectFlag = 0
circleFlag = 0
whichLine = 1 # 1:实线,2:虚线,0:中间位置
stopFlag = 0
openFlag = 0
ticks = 0
tickFlag = 0
printFlag = 0
turnFlag = 0
newModeFlag = 0 def call_back(*arg):
pass def TrackBar_Init():
# 1 create windows
cv2.namedWindow('hsl_binary')
# 2 Create Trackbar
cv2.createTrackbar('hmin', 'hsl_binary', 0, 179, call_back)
cv2.createTrackbar('hmax', 'hsl_binary', 179, 179, call_back)
cv2.createTrackbar('smin', 'hsl_binary', 0, 255, call_back)
cv2.createTrackbar('smax', 'hsl_binary', 255, 255, call_back)
cv2.createTrackbar('vmin', 'hsl_binary', 130, 255, call_back)
cv2.createTrackbar('vmax', 'hsl_binary', 255, 255, call_back) def Init():
TrackBar_Init()
GPIO.setmode(GPIO.BCM)
GPIO.setup(5, GPIO.IN)
GPIO.setup(5, GPIO.IN, pull_up_down=GPIO.PUD_UP) # 可以调整上下拉状态 def rotateAntiClock_90(img):
img = cv2.flip(cv2.transpose(img), 0)
return img # 在HSV色彩空间下得到二值图
def Get_HSV(image):
# 1 get trackbar's value
hmin = cv2.getTrackbarPos('hmin', 'hsl_binary')
hmax = cv2.getTrackbarPos('hmax', 'hsl_binary')
smin = cv2.getTrackbarPos('smin', 'hsl_binary')
smax = cv2.getTrackbarPos('smax', 'hsl_binary')
vmin = cv2.getTrackbarPos('vmin', 'hsl_binary') # 128
vmax = cv2.getTrackbarPos('vmax', 'hsl_binary')
cv2.imshow("hsl_binary", win)
# 2 to HSV
hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
# cv2.imshow('hsv', hsv)
h, s, v = cv2.split(hsv) # 3 set threshold (binary image)
# if value in (min, max):white; otherwise:black
h_binary = cv2.inRange(np.array(h), np.array(hmin), np.array(hmax))
s_binary = cv2.inRange(np.array(s), np.array(smin), np.array(smax))
v_binary = cv2.inRange(np.array(v), np.array(vmin), np.array(vmax)) # 4 get binary(对H、S、V三个通道分别与操作)
binary = 255 - cv2.bitwise_and(h_binary, cv2.bitwise_and(s_binary, v_binary)) # 5 Show
# cv2.imshow('binary', binary) return binary # 图像处理
def Image_Processing():
global frame, binary, dst, blur
ret, frame = camera.read()
frame = rotateAntiClock_90(frame) # 逆时针旋转90度
binary = Get_HSV(frame)
blur = cv2.GaussianBlur(binary, (9, 9), 0)
# cv2.imshow('blur', blur)
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (10, 10))
dst = cv2.morphologyEx(blur, cv2.MORPH_CLOSE, kernel)
cv2.imshow('Dilate', dst) # dst:处理后的图像 def Find_RealLine(): # 从左扫线
global dst, direc1_x, direc1_y, diffCRL, frame
dirLine = int(dst.shape[0]*9/13)
dot_x = []
# Canny边缘检测
dst_canny = cv2.Canny(dst, 190, 250)
h, w = dst_canny.shape[0:2]
filtRate = 1/20
dst_canny = dst_canny[:, 0 + int(filtRate*w):int(w - w*filtRate)]
# cv2.imshow('canny', dst_canny)
# 找基准点,利用与运算掩模
dst_mask_line = np.zeros(dst_canny.shape, np.uint8)
dst_mask_line[dirLine, :] = 255 # 图像尺寸:640x480
dotImg = cv2.bitwise_and(dst_mask_line, dst_canny) # 使用按位与找交点 # 从左往右找第一第二个交点,取中点即为基准点
for i in range(0, dotImg.shape[1]):
if dotImg[dirLine, i] == 255:
dot_x.append(i)
try: # 防止没有找到交点导致列表内无数据的情况
x1 = dot_x[0]
x2 = dot_x[1]
# 基准点坐标
direc1_x = int((x1 + x2) / 2)
direc1_y = dirLine # 画出基准点
frame = cv2.circle(frame, (direc1_x + int(filtRate*w), direc1_y), 5, (0, 0, 255), 2)
dst_canny = cv2.line(dst_canny, (dirLine, 0), (dirLine, dst_canny.shape[0]), (255, 255, 255), 2)
dst_canny = cv2.cvtColor(dst_canny, cv2.COLOR_GRAY2BGR)
# cv2.imshow('dst_canny', dst_canny)
print((direc1_x, direc1_y)) # 计算偏差值
diffCRL = direc1_x - int(1/2*dst_canny.shape[1])
print(diffCRL)
except:
pass def findHorizonLine(src): # 检测水平直线
global modeFlag, horizonFlag, frame
flag = 0
src = src[int(1/2 * src.shape[0]):src.shape[0], :]
dst_y = cv2.Canny(src, 50, 150)
# cv2.imshow('dst_y', dst_y)
lines = cv2.HoughLinesP(dst_y, 1, np.pi / 180, 70, minLineLength=40, maxLineGap=10)
# dsty_bgr = cv2.cvtColor(dst_y, cv2.COLOR_GRAY2BGR)
try:
for line in lines:
x1, y1, x2, y2 = line[0]
slope = (y2 - y1) / (x2 - x1 + 1e-6)
if abs(slope) > 0.3:
continue
else:
print("Found Line")
flag = 1
horizonFlag = 1
cv2.line(frame, (x1, y1+int(1/2 * src.shape[0])), (x2, y2+int(1/2 * src.shape[0])), (0, 0, 255), 2)
except:
call_back()
# cv2.imshow('dsty_line', dsty_bgr)
return flag def findVerticalLine(src):
global modeFlag, frame
flag = 0
src = src[int(src.shape[0]*2/3):src.shape[0], :]
lines = cv2.HoughLinesP(src, 1, np.pi / 180, 70, minLineLength=150, maxLineGap=5)
try:
for line in lines:
x1, y1, x2, y2 = line[0]
slope = (x2 - x1) / (y2 - y1 + 1e-6)
if abs(slope) > 0.45:
continue
else:
print("Found vertical Line")
flag = 1
cv2.line(frame, (x1, y1 + int(src.shape[0]*2/3)), (x2, y2 + int(src.shape[0]*2/3)), (0, 0, 255), 2)
except:
call_back()
return flag def findChangeLine(src):
global modeFlag, horizonFlag, frame
# src = cv2.Canny(src, 50, 150)
flag = 0
src = src[int(1 / 3 * src.shape[0]):src.shape[0], 0:int(src.shape[1] * 5/6)]
# cv2.imshow('HorDotLine', src)
lines = cv2.HoughLinesP(src, 1, np.pi / 180, 70, minLineLength=40, maxLineGap=120)
try:
for line in lines:
x1, y1, x2, y2 = line[0]
slope = (y2 - y1) / (x2 - x1 + 1e-6)
if 1/100 <= abs(slope) <= 2:
print("Found Change Line")
flag = 1
# horizonFlag = 1
else:
continue
cv2.line(frame, (x1, y1+int(1 / 2 * src.shape[0])), (x2, y2+int(1 / 2 * src.shape[0])), (0, 0, 255), 2)
except:
call_back()
return flag def findChangeRLine(src):
global modeFlag
# src = cv2.Canny(src, 50, 150)
flag = 0
src = src[int(1 / 4 * src.shape[0]):src.shape[0], :]
# cv2.imshow('HorDotLine', src)
lines = cv2.HoughLinesP(src, 1, np.pi / 180, 70, minLineLength=50, maxLineGap=20)
try:
for line in lines:
x1, y1, x2, y2 = line[0]
slope = (y2 - y1) / (x2 - x1 + 1e-6)
if 1/100 <= abs(slope) <= 2:
print("Found Change Line")
flag = 1
# horizonFlag = 1
else:
continue
# cv2.line(frame, (x1, y1+int(1 / 2 * src.shape[0])), (x2, y2+int(1 / 2 * src.shape[0])), (0, 0, 255), 2)
except:
call_back()
return flag def Find_DottedLine(img): # 巡虚线:利用图像矩(好用!)
global diffCRL, frame
# img = cv2.Canny(img, 50, 150)
area = 0
difDotLine = 0
h, w = img.shape[0:2]
img = img[int(3/5*h):h, :]
m = cv2.moments(img)
try:
x = int(m['m10'] / m['m00'])
y = int(m['m01'] / m['m00'])
area = int(m['m00'])
difDotLine = int(x - 1 / 2 * img.shape[1])
img = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
cv2.circle(frame, (x, y + int(2/3*h)), 7, (0, 255, 0), 4)
cv2.imshow('dstline', img)
except:
call_back()
cv2.imshow("DotLine", img)
return area, difDotLine # cv2.fitLine(points, distType, param, reps, aeps[, line ]) -> Line
def findDottedLine3(img): # 巡虚线方案3:利用cv2.fitLine方法
cnt = [] # 点集
minVal = 1 # 区间下限
maxVal = 255 # 区间上限
# 由于拟合出的直线几乎垂直,故使用 x = m * y + n 型直线
h, w = img.shape[0:2]
img = img[int(2 / 3 * h):h, :] # 提取ROI,滤去边缘噪声
for i in range(img.shape[0]):
for j in range(img.shape[1]):
if minVal <= img[i, j] <= maxVal:
cnt.append([j, i])
cnt = np.array(cnt) # cnt 必须为矩阵形式,且表示[x,y]坐标
[vx, vy, x, y] = cv2.fitLine(cnt, cv2.DIST_L2, 0, 0.01, 0.01)
# 其中:(vx,vy)为直线的方向向量,(x,y)为直线上的一个点
img_bgr = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR) # 用来画线的BGR图
try: # 防止全黑图等情况
m = vx / vy
n = x - m * y
pt1 = (int(m * (y - 1000) + n), int(y - 1000))
pt2 = (int(m * (y + 1000) + n), int(y + 1000))
cv2.line(img_bgr, pt1, pt2, (0, 255, 0), 3)
cv2.imshow('DottedLine', img_bgr)
except:
pass def comPrint(_string):
message = _string
ser.write(message.encode())
# ser.write(b"10%d" % diffCRL) def getRect(img):
global frame
shape = 0
centerPoint = (0, 0)
area = 0
imgContour = img.copy()
_, contours, hierarchy = cv2.findContours(img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
for obj in contours:
area = cv2.contourArea(obj) # 计算轮廓内区域的面积
cv2.drawContours(imgContour, obj, -1, (255, 0, 0), 4) # 绘制轮廓线
perimeter = cv2.arcLength(obj, True) # 计算轮廓周长
approx = cv2.approxPolyDP(obj, 0.02 * perimeter, True) # 获取轮廓角点坐标
CornerNum = len(approx) # 轮廓角点的数量
x, y, w, h = cv2.boundingRect(approx) # 获取坐标值和宽度、高度
centerPoint = (int(x+(1/2)*w), int(y+(1/2)*w))
if CornerNum == 4:
shape = 4
cv2.circle(frame, centerPoint, 3, (0,0,255), -1)
return shape, centerPoint, area, imgContour def findRed():
global frame
signalRect = 0 # 检测矩形标志位
signalCirlcle = 0 # 检测圆形标志位
img = frame
# cv2.imshow('ord', img)
grid_RGB = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
# 从RGB色彩空间转换到HSV色彩空间
grid_HSV = cv2.cvtColor(grid_RGB, cv2.COLOR_RGB2HSV)
# H、S、V范围一:
lower1 = np.array([0, 43, 46])
upper1 = np.array([10, 255, 255])
mask1 = cv2.inRange(grid_HSV, lower1, upper1) # mask1 为二值图像
# H、S、V范围二:
lower2 = np.array([156, 43, 46])
upper2 = np.array([180, 255, 255])
mask2 = cv2.inRange(grid_HSV, lower2, upper2)
# 将两个二值图像结果 相加
mask3 = mask1 + mask2
# 去除噪声
kernel = np.ones((5, 5), np.uint8)
dst3 = cv2.morphologyEx(mask3, cv2.MORPH_OPEN, kernel)
dst3 = cv2.medianBlur(dst3, 29)
# dst3 = dst3[:, int(dst3.shape[1]*1/10):dst3.shape[1]]
# 结果显示
# cv2.imshow("dst", dst3)
edges = cv2.Canny(dst3, 128, 255)
cv2.imshow('canny', edges)
# 检测图像形状
edges = edges[int(edges.shape[0] * 1/10):edges.shape[0], :]
circles = cv2.HoughCircles(edges, cv2.HOUGH_GRADIENT, 1, 300,
param1=13, param2=26, minRadius=50,
maxRadius=200)
# dst3 = cv2.cvtColor(dst3, cv2.COLOR_GRAY2BGR)
# 确保至少发现一个圆
if circles is not None:
# 进行取整操作
print("Circle here")
edges = np.zeros(edges.shape, np.uint8)
circles = np.round(circles[0, :]).astype("int")
signalCirlcle = 1
# 循环遍历所有的坐标和半径
for (x, y, r) in circles:
# 绘制结果
cv2.circle(frame, (x + int(dst3.shape[1]*1/5), y), r, (255, 0, 0), 4)
# cv2.rectangle(frame, (x - 5, y - 5), (x + 5, y + 5), (255, 128, 0), -1)
shape, centerPoint, area, _ = getRect(edges)
# 检测矩形:
if shape >= 2 and area >= 2500:
print("There is a Rectangular")
edges = np.zeros(edges.shape, np.uint8)
cv2.circle(frame, centerPoint, 3, (255,0,0), 3)
signalRect = 1
# cv2.imshow('dst3', dst3)
# edges = np.zeros(edges.shape(), np.uint8)
del edges
return signalRect, signalCirlcle if __name__ == '__main__':
Init()
modeFlag = 2
stopFlag = 2
turnFlag = 0
while True:
Image_Processing()
# Find_RealLine()
# modeFlag = 10
# if findHorizonDotLine(frame):
# print("findHorizon Line")
# _, diffCDL = Find_DottedLine(dst)
# if findChangeLine(dst) == 1:
# print("Chnage!")
# findVerticalLine(dst)
# comPrint("14\r\n")
# if ser.read(1) == b'A':
# print("A")
# comPrint("10%d\r\n" % diffCDL)
# comPrint("10%d\r\n" % diffCRL)
# findRed()
# findHorizonLine(dst)
# comPrint("13\r\n")
# string = ser.read(1)
# print(string)
if modeFlag == 0: # 出发走直线
comPrint("17\r\n") # 开环走直线模式
if findHorizonLine(dst): # 如果看到水平线
modeFlag = 1 # 切换
# 模式
if modeFlag == 1: # 右转90度模式
turnFlag = 1
horizonFlag = 0
comPrint("12\r\n") # 让车子转弯
print("turn RIGHT, STM32!")
# string = ser.read(1) # 读取来自stm32的消息
# print(string)
# if string == b'A': # 如果收到"A"
# modeFlag = 2 # 切换模式
# print(modeFlag)
verticalFlag = findVerticalLine(dst)
if tickFlag == 0:
ticks = int(time.time())
tickFlag = 1
if verticalFlag == 1 and int(time.time()) - ticks >= 5:
print("Find Vertical Line")
followReaLineFlag = 1
modeFlag = 2
horizonFlag = 0
tickFlag = 0
if modeFlag == 2: # 巡实线模式,同时检测障碍物与停车线
whichLine = 1 # 实线
tickFlag = 0
openFlag = 1
if followReaLineFlag == 1:
Find_RealLine()
# _,diffCDL = Find_DottedLine()
comPrint("10%d\r\n" % diffCRL) # 发给stm32偏移量
print("10%d\n" % diffCRL)
rectFlag, circleFlag = findRed()
horizonFlag = findHorizonLine(dst)
if horizonFlag == 1 and stopFlag >= 2: # 经过多少障碍物停车
modeFlag = 8 # 实线停车模式
if rectFlag == 1:
followReaLineFlag = 0
modeFlag = 3 # 向左变道
# stopFlag = stopFlag + 1
if circleFlag == 1:
followReaLineFlag = 0
modeFlag = 6 # 向右避障
# stopFlag = stopFlag + 1
# 给stm32发变道或避障指令
# 如果读到来自stm32的动作完毕指令,就开始巡虚线或实线,并将形状flag置为0
if turnFlag == 0:
if findHorizonLine(dst):
horizonFlag = 1
if horizonFlag == 1:
tickFlag = 0
modeFlag = 1
if modeFlag == 7: # 巡虚线模式
whichLine = 2
tickFlag = 0
openFlag = 1
if followDottedLineFlag == 1:
print("Find Dot Line")
_, diffCDL = Find_DottedLine(dst)
comPrint("10%d\r\n" % diffCDL) # 发给stm32偏移量
rectFlag, circleFlag = findRed()
horizonFlag = findHorizonLine(dst)
if horizonFlag == 1 and stopFlag >= 2:
modeFlag = 9 # 虚线停车模式
if rectFlag == 1:
followDottedLineFlag = 0
modeFlag = 5 # 向左避障
if circleFlag == 1:
followDottedLineFlag = 0
modeFlag = 4 # 向右变道
# 此处再加一个停车位的判断 if modeFlag == 3: # 向左变道模式
whichLine = 0
if tickFlag == 0:
ticks = int(time.time())
tickFlag = 1
openFlag = 1
if int(time.time()) - ticks >= 2 and openFlag == 1:
stopFlag = stopFlag + 1
openFlag = 0
comPrint("13\r\n") # 向左变道指令
if ser.inWaiting():
string = ser.read(1) # 监听消息
if string == b'A': # 收到stm32的动作完毕指令
modeFlag = 7 # 直接进入巡线
print("Following Dot Line")
string = None
followDottedLineFlag= 1
rectFlag = 0
circleFlag = 0
# ser.flushInput() # 清除输入缓冲区数据 if modeFlag == 4: # 向右变道模式
whichLine = 0
if tickFlag == 0:
ticks = int(time.time())
tickFlag = 1
openFlag = 1
if int(time.time()) - ticks >= 2 and openFlag == 1:
stopFlag = stopFlag + 1
openFlag = 0
if printFlag == 0:
comPrint("15\r\n") # 向右变道指令
printFlag = 1
if ser.inWaiting():
string = ser.read(1) # 监听消息
if string == b'B': # 收到stm32的动作完毕指令
print("B")
modeFlag = 2
followReaLineFlag = 1
rectFlag = 0
circleFlag = 0
printFlag = 1
# ser.flushInput() # 清除输入缓冲区数据
if modeFlag == 5: # 向左避障模式
whichLine = 0
if tickFlag == 0:
ticks = int(time.time())
tickFlag = 1
openFlag = 1
if int(time.time()) - ticks >= 2 and openFlag == 1:
stopFlag = stopFlag + 1
openFlag = 0
comPrint("14\r\n") # 向左避障指令
if ser.inWaiting():
string = ser.read(1) # 监听消息
if string == b'C': # 收到stm32的动作完毕指令
print("left avoid")
# if findChangeLine(dst):
# print("142\r\n")
# comPrint("142\r\n")
time.sleep(2)
modeFlag = 7
followDottedLineFlag = 1
rectFlag = 0
circleFlag = 0
# ser.flushInput() # 清除输入缓冲区数据
if modeFlag == 6: # 向右避障模式
if tickFlag == 0:
ticks = int(time.time())
tickFlag = 1
openFlag = 1
if int(time.time()) - ticks >= 2 and openFlag == 1:
stopFlag = stopFlag + 1
openFlag = 0
comPrint("16\r\n") # 向右避障指令
whichLine = 0
if ser.inWaiting():
string = ser.read(1) # 监听消息
if string == b'D': # 收到stm32的动作完毕指令
string = None
print("right avoid")
if findChangeRLine(dst) == 1:
comPrint("163\r\n")
print("Turn right, STM32!")
modeFlag = 2
followReaLineFlag = 1
rectFlag = 0
circleFlag = 0
# ser.flushInput() # 清除输入缓冲区数据
# serial.flushInput() # 清除输入缓冲区数据 if modeFlag == 8: # 实线停车
comPrint("11\r\n")
print("STOP AT REAL LINE")
time.sleep(10)
if findHorizonLine(dst):
comPrint("18\r\n")
print("18")
modeFlag = 10
if modeFlag == 9: # 虚线停车
comPrint("19\r\n")
print("STOP AT DOT LINE")
time.sleep(15)
# if findHorizonLine(dst):
# comPrint("19\r\n")
# print("18")
# modeFlag = 10
modeFlag = 10
cv2.imshow('frame', frame)
if ser.inWaiting():
string = ser.read(1) # 监听消息
if string == b'E':
print("We can stop")
stopFlag = 3
print("stop flag : %d" % stopFlag)
print("mode : %d" % modeFlag)
if modeFlag == 10:
newModeFlag = int(input("please change mode:"))
if newModeFlag == 1:
print("change mode")
stopFlag = 0
modeFlag = 2
# getKeyVal()
# if GPIO.input(3):
# print("HIGH VALUE")
# GPIO.wait_for_edge(3, GPIO.RISING)
# print("HIGH!")
# if GPIO.input(5) == GPIO.HIGH:
# print("HIGH VALUE!") if cv2.waitKey(1) == ord('q'):
cv2.destroyAllWindows()
break

因为自己比较懒,有些函数没怎么测试,反正思路在那,看着改改就好。

NJUPT第二次积分赛小结与视觉部分开源的更多相关文章

  1. 软件工程(GZSD2015)第二次作业小结

    第二次作业,从4月7号开始,陆续开始提交作业.根据同学们提交的作业报告,相比第一次作业,已经有了巨大改变,大家开始有了完整的实践,对那些抽象的名词也开始有了直观的感受,这很好.然后有一些普遍存在的问题 ...

  2. 软件工程(GZSD2015) 第二次作业小结

    第二次作业,从4月7号开始,陆续开始提交作业.根据同学们提交的作业报告,相比第一次作业,已经有了巨大改变,大家开始有了完整的实践,对那些抽象的名词也开始有了直观的感受,这很好.然后有一些普遍存在的问题 ...

  3. OO第二单元作业小结

    前言 转眼已是第九周,第二单元的电梯系列作业已经结束,终于体验了一番多线程电梯之旅. 第一次作业是单电梯的傻瓜调度,虽然是第一次写多线程,但在课程PPT的指引下,写起来还是非常容易:第二次作业是单电梯 ...

  4. 《Linux内核分析》第二周学习小结 操作系统是如何工作的?

    郝智宇   无转载   <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 一.函数调用堆栈: 1.计算机是 ...

  5. 20181117-python第二章学习小结-part2

    浮点型补充: 有限小数与无限循环小数,不包括无理数! 小数点后面的数据运算太复杂,精确度不及整数! 尽量使用科学计数表示小数 列表学习(语法) 创建:[] list = []  #创建空表 list ...

  6. 20181117-python第二章学习小结-part1

    什么是二进制,十进制如何转化成二进制. 在python上可使用简单的函数进行转化,bin() 数据量的基本关系: 1bit  就是0/1的一个单位 1bytes = 8bit    #1个字节,就是一 ...

  7. 软件工程实践助教每周小结 < 福州大学 | 傅明建 >

    第一周助教小结 1. 助教博客链接: http://www.cnblogs.com/sinceway/ 2. 本周点评的作业数量:约22份,有多次交互 3. 本周点评有困难的作业链接: https:/ ...

  8. Ember.js入门教程、博文汇总

    第一章 对象模型 Ember.js 入门指南——类的定义.初始化.继承 Ember.js 入门指南——类的扩展(reopen) Ember.js 入门指南——计算属性(compute properti ...

  9. vimtutor

    ================================================================================ 欢 迎 阅 读 < V I M ...

  10. VIMTUTOR《VIM教程》

    =============================================================================== =      欢     迎     阅 ...

随机推荐

  1. DBGRIDEH 排序 我自己大总结【含DBX,Dac控件】

    1.三个属性让DBGridEH可以点击表头自动排序 只要设置下面三个属性: ColumDefValues->Title->TitleButton设为TRUE  sortlocal   设为 ...

  2. clickhouse导入和导出

    一.连接clickhouse--客户端连接default库clickhouse-client -h localhost --port 9001 -u default --password 123456 ...

  3. AI抠图神器RMBG下载介绍

    RMBG是一款先进的AI抠图工具,和其它同类型软件不同的是,RMBG不需要人工勾勒图形轮廓,可以自动识别图像的前景并去除背景,节省大量时间,效果非常惊艳 最新中文版下载: 百度网盘:https://p ...

  4. CF1878C Vasilije in Cacak 题解

    题目传送门 简化题意 有 \(t\) 组询问,每次询问是否能从 \(1 \sim n\) 中选择 \(k\) 个数使得它们的和为 \(x\). 解法 考虑临界情况,从 \(1 \sim n\) 中选择 ...

  5. Oracle 10gR2新SQL提示——opt_param

    [English] 搜索Internet 搜索 HelloDBABA 首页 English 中文 技术文档 文章 案例 产品及下载 产品 >> FyDB OraTracer FySafe ...

  6. Oracle 表压缩(Table Compression)技术介绍

    Oracle 表压缩(Table Compression)介绍 1.官方文档说法: As your database grows in size, consider using table compr ...

  7. Windows Docker Destop修改默认镜像文件位置

    0.首先关闭docker destop. 1.通过Everything或者资源管理器找到以.vhdx结尾的文件所在的位置,这些就是docker镜像路径 2.我的路径:C:\Users\Administ ...

  8. 【Azure 应用服务】App Service for Windows 环境中为Tomcat自定义4xx/5xx页面

    问题描述 通过设置Java Web项目,实现在App Service For Windows环境中达到自定义4XX/5XX的页面效果 问题解答 第一步:在本地项目文件中打开web.xml文件 (src ...

  9. Nebula Graph 源码解读系列 | Vol.01 Nebula Graph Overview

    上篇序言中我们讲述了源码解读系列的由来,在 Nebula Graph Overview 篇中我们将带你了解下 Nebula Graph 的架构以及代码仓分布.代码结构和模块规划. 1. 架构 Nebu ...

  10. go语言实现扫雷

    源码如下 package main import ( "archive/zip" "bytes" "encoding/base64" &qu ...