智能车巡线方案(双线方案,大残次品)

文章目录

  • 前言
  • 一、核心思路
  • 总结


前言

参加智能车比赛,一周复制粘贴出来的代码。没有写函数,全放大循环里了,因为python是真的不是很会,真的很像一坨屎

一、核心思路

先将图像二值化(黑白地图),然后分别划分摄像头两侧的roi,分别计算两侧roi所看到的白色像素点总和,然后进行判断是否转弯。

话不多说,直接上代码

import time
import cv2
import numpy as np
import serial
from socket import *
#引入模板
img_leftcenter = cv2.imread('./left_26_2.png',0)
# 127.0.0.1表示本机的IP,用于测试,使用时需要改为服务端的ip
addr = ('192.168.106.54', 8080)
s = socket(AF_INET, SOCK_DGRAM) # 创建UDP套接字   //UDP视频流图传,有需要的或有想法的可以自行搜索
s.bind(('', 8081))
b=0
num=0
data='0'
cap = cv2.VideoCapture(0)
kernel = np.ones((3,3), np.uint8)
#模板函数     //模板函数是我队友添加上去的,为了识别路牌,但是时间太急没来得及调试,就只是写在了里面,没有用上,如果有兴趣的可以自行探索,这个识别率挺低的
def muban():
    ret,frame_mask = cap.read()
    frame_mask = cv2.cvtColor(frame_mask, cv2.COLOR_BGR2GRAY)  # 灰度
    # retval, line = cv2.threshold(frame, 180, 255, cv2.THRESH_BINARY)  # 二值
    frame_mask = cv2.GaussianBlur(frame_mask, (3,3), 1, 2)
    frame_mask = np.zeros_like(frame_mask)  # 获得一个与原矩阵相同的矩阵,画布
    frame_mask = cv2.fillPoly(frame_mask, np.array([[[390, 480], [390, 0], [640, 0], [640, 480]]]), color=255)  # 填充多边形的四个顶点
    frame_mask = cv2.bitwise_and(frame_mask, mask)
    result_left = cv2.matchTemplate(frame_mask,img_leftcenter,5)
    minVal,maxVal,minLoc,maxLoc = cv2.minMaxLoc(result_left)
     #"""左转"""
    print("左转最小:",minVal,"""左转最大:""",maxVal)
    if minVal <-0.55and minVal >-0.59and maxVal>0.61and maxVal<0.7 :
        print("left")
    elif minVal <-0.55and minVal >-0.59and maxVal>0.64and maxVal<0.9 :
        print("left")
        
#定义回调函数    //这是定义阈值按钮的回调函数,不需要特别关注,(因为我没看懂有啥作用)
def returnnum(x):
    pass 

cv2.namedWindow('two_fram')         #定义一个名叫 “two_fram”的窗口,为后面定义滑动条做一个前提准备
cv2.createTrackbar('two1','two_fram',190,255,returnnum)         #二值化的滑动条阈值一,第一个参数:滑动条名称,第二个参数:滑动条依附于哪个窗口,第三个参数:初始值,第四个参数:阈值滑动条最大值,第五个参数:回调函数
cv2.createTrackbar('two2','two_fram',255,255,returnnum)   
serial_port = serial.Serial(
    port="/dev/ttyUSB0",
    baudrate=115200,
    bytesize=serial.EIGHTBITS,
    parity=serial.PARITY_NONE,
    stopbits=serial.STOPBITS_ONE,
)       #我们用的是nano来作为视觉判断向下位机(stm32)发送数据的端口   这里是打开并设置串口
serial_arduino= serial.Serial(
    port="/dev/ttyTHS1",
    baudrate=115200,
    bytesize=serial.EIGHTBITS,
    parity=serial.PARITY_NONE,
    stopbits=serial.STOPBITS_ONE,
)          #arduino串口设置,下位机向jetson nano发送数据
cx=0.0
cy=0.0
cxr=0.0
cyr=0.0  #这里都是用来初始化数据的
lo_blue = np.array([100,43,46])   #hsv颜色识别阈值
up_blue = np.array([124,255,255])  
green_up = np.array([35 ,43,46])
green_d= np.array([77,255,255])


global tinghs    #决定循环启动位置           
tinghs=0           #有限启动视频流图传      
serial_port.write('s\r\n'.encode())   #发送停止指令,默认让小车停止

while tinghs==0:        #     当tinghs=0时,先执行这个循环,将jetson nano 获得的视频传输到电脑上                     
    _, img = cap.read()
    img = cv2.flip(img, 1)
    _, send_data = cv2.imencode('.jpg', img, [cv2.IMWRITE_JPEG_QUALITY, 50])
    s.setsockopt(SOL_SOCKET, SO_REUSEADDR,1)
    s.sendto(send_data, addr)
    recv_data,addr = s.recvfrom(1024)
    recv_data = str(recv_data,'UTF-8')
    if recv_data == "green" :
        print("green")
        serial_port.write('g\r\n'.encode())  #识别到绿色后,程序退出前让车动起来
        tinghs=1   
        break

while tinghs==1:
    ret, frame = cap.read()
    two1=cv2.getTrackbarPos('two1','two_fram')
    two2=cv2.getTrackbarPos('two2','two_fram')
    if ret is True:
        #VideoCapture set
        gray_fram=cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY)        #图像灰度
        retval,two_fram=cv2.threshold(gray_fram, two1, two2, cv2.THRESH_BINARY)  #二值化
        two_fram = cv2.morphologyEx(two_fram, op=cv2.MORPH_OPEN,kernel=kernel, iterations=1)         #开运算,腐蚀膨胀消除噪声
        #rio set
        roib=frame[430:480,0:640]       #蓝色识别(弃用)
        roil=two_fram[310:480,20:300]  #左侧roi划分
        roir=two_fram[310:480,340:640]  #右侧roi划分
        #findContours and draw it
        contourl, hierarchyl= cv2.findContours(roil,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)     #寻找白线并且定位白线中心位置,确定小车在大拐弯处的状态
        cv2.drawContours(frame,contourl,-1,(0,0,255),3)    #测试用,是否寻找出白线轮廓
        contourr, hierarchyr = cv2.findContours(roir,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)  
        cv2.drawContours(frame,contourr,-1,(0,0,255),3) 
        #if else:
        if serial_arduino.inWaiting()>0:       #如果arduino串口有数据,执行避障(避障是stm32写死的)
            data=serial_arduino.readline()
            if data==b'6\r\n':
                serial_port.write('s\r\n'.encode())
                things=2
                break
            else:
                pass
        else:
            if len(contourl) & (len(contourr))==0:       #防止找不到轮廓报错
                pass
            else:            
                M= cv2.moments(contourl[-1])        #寻找轮廓并且画出来,标出中心坐标
                cx = int(M['m10']/(M['m00']+0.01) )
                cy = int(M['m01']/(M['m00']+0.1) )
                cv2.circle(frame ,(cx,cy),2,(0,0,255),4) 
                print('cx=',cx,'cy=',cy,type(cy))
                Mr= cv2.moments(contourr[-1]) 
                cxr = int(Mr['m10']/(Mr['m00']+0.01) )
                cyr = int(Mr['m01']/(Mr['m00']+0.01) )
                cv2.circle(frame ,(cxr,cyr),2,(0,0,255),4) 
                print('cxr=',cxr,'cyr=',cyr)
            #hsv
            # hsv = cv2.cvtColor(roib,cv2.COLOR_BGR2HSV)
            # blue = cv2.inRange(hsv,lo_blue,up_blue)
            # blue=cv2.morphologyEx(blue, op=cv2.MORPH_OPEN,kernel=kernel, iterations=2)

            # hsvg= cv2.cvtColor(frame,cv2.COLOR_BGR2HSV)
            # green = cv2.inRange(hsvg,green_d,green_up)
            # green=cv2.morphologyEx(blue, op=cv2.MORPH_OPEN,kernel=kernel, iterations=2)
            # print('green',green)
            #data 
            white_l = np.sum(roil== 255)/5500    #找到白色点,并且计算总和数量,/5500是为了计算比例范围。这个根据实际场地调试,可以先输出白色点数量,寻找最大值后,再除以一定的数即可   。  
            white_r = np.sum(roir ==255)/5500
            print(white_l,white_r)
            #下面就是一堆判断了,思路可能会很乱,当时直接是上场地,遇到问题就硬调的。所以判断思路有些混乱
            if serial_arduino.inWaiting() ==0:
                if (0.0<=white_l<=0.23) &(0.0<=white_r<=0.23):
                    print('run')
                    serial_port.write('z\r\n'.encode())
                elif (0.18<white_l<0.45)&(white_r==0):
                    print('run')
                    serial_port.write('z\r\n'.encode())
                elif (white_l==0)&(white_r==0):
                    printf('left')
                    serial_port.write('l\r\n'.encode())  #以上就是在直走的情况下判断是否在大拐弯处转弯过晚,导致误判,(核心思想就是哪边白色像素点越多,就说明越靠近那一边,如:右侧白色像素点多于左侧,说明小车靠近右侧道路,所以应该左转,特别提醒!!我们的小车底盘是阿克曼结构,所以底层需要修改一下,转弯范围也需要修改)
                else:
                    if(white_l>white_r):
                        if (cy<cyr) &(cy>0.02):
                            print('big left')
                            serial_port.write('l\r\n'.encode())
                        else:
                            print('right')
                            serial_port.write('r\r\n'.encode())
                    else:
                        if (cy>cyr) & (cyr>0.02):
                            print('big right')
                            serial_port.write('r\r\n'.encode())
                        else:
                            print('left')
                            serial_port.write('l\r\n'.encode())
            
                """      # elif white_l>white_r:   #这一块是以前弃用的方案,判断的方法不一样,核心思路是一样的

                #     print('right')
                #     serial_port.write('-2\r\n'.encode())
                # elif white_r>white_r:
                #     print('left')
                #     serial_port.write('+2\r\n'.encode())
                # elif (white_r<0.2)&(white_l>0.2):
                #     if white_r<0.12:
                #         print('right')
                #         serial_port.write('r\r\n'.encode())
                #     else:
                #         print('run')
                #         serial_port.write('z\r\n'.encode())

                # elif (white_l<0.2)&(white_r>0.3):
                #     print('left')
                #     serial_port.write('l\r\n'.encode())
                # elif (white_r>0.6) :
                #     if (white_l>0.55) & (white_r>white_l):
                #         print('right')
                #         serial_port.write('r\r\n'.encode())
                #     elif (white_l>white_r):

                #         print('left')
                #         serial_port.write('l\r\n'.encode())
                #     elif(white_l<0.2):
                #         rint('right')
                #         serial_port.write('r\r\n'.encode())
                # elif  (white_r>0.5) :
                #     if white_l>0.2:
                #         print('left')
                #         serial_port.write('l\r\n'.encode())
                #     else:
                #         print('right')
                #         serial_port.write('r\r\n'.encode())

                # elif (white_r<0.09) &(white_l<0.09):
                #     print('run')
                #     serial_port.write('z\r\n'.encode())

                        """
				#这一块都是显示输出
            	#cv2.imshow("line_fram",frame)
                cv2.imshow("roil", roil)
                cv2.imshow('roir',roir)
                cv2.imshow('two_fram',two_fram) 
                #cv2.imshow('roib',roib)
            else:
                serial_port.write('s\r\n'.encode())  #这句似乎没用,不知道为啥小车停不下来,可能是我的流程有问题

        if cv2.waitKey(10) ==27:
            break
cap.release()
cv2.destroyAllWindows()


while things==2:         #弃用方案,想法是:小车遇到障碍物后会一直输出停止的指令,直到没有数据后会结束此循环跳到主循环里去
            if serial_arduino.inWaiting()>0:
                data=serial_arduino.readline()
                if  data==b'6\r\n':
                    serial_port.write('s\r\n'.encode())
                    print(data)

            else:
                things=1
                serial_port.write('g\r\n'.encode())
                break
        


总结

此代码是大大的大残次品(本人python也是略知皮毛,就不会使用函数啥的,opencv也才没学多久几乎是用到才会去学,而且也就只知道调用函数,代码几乎是复制粘贴的,没有涉及算法,全是判断,所以会有误判的情况),发出来是为了给自己留个记录的同时也给正在对双线巡线毫无头绪的同学提供一点点小启发…

你可能感兴趣的:(python,计算机视觉)