一、项目简介

依据机器视觉模块OpenMV收集车道、红绿灯、交通标志等模仿路况信息,完成一辆能车道保持、红绿灯辨认、交通标志辨认、安全避障以及长途WiFi操控的多功能无人驾驶小车。

赛道标准:

基于OpenMV的自动驾驶智能小车模拟系统

1、编程所需软件:

OpenMV:运用OpenMV官方的OpenMV IDE

基于OpenMV的自动驾驶智能小车模拟系统

ESP8266:运用Arduino官方的Arduino IDE

基于OpenMV的自动驾驶智能小车模拟系统

STM32:运用ARM官方的Keil uVision5(ARM版)

基于OpenMV的自动驾驶智能小车模拟系统

2、功能介绍:

OpenMV:首要是使用OpenMV进行路况信息(红绿灯、交通标志、车道)的采纳,以及和STM32的通讯。

ESP8266:首要是使用ESP8266与手机端进行长途的指令接纳和数据交互,以及和STM32的通讯。

STM32:首要是经过ESP8266接纳长途操控指令和处理路况信息,并依据这些指令数据进行实时的PID操控小车运动。

二、硬件体系

首要依托机器视觉模块OpenMV经过图画处理的方式获取实时的路况信息,以及超声波传感器获取障碍物间隔信息,得到的路况数据再经过串口传输到主控器STM32上面,STM32会将实时的路况信息处理成智能小车的运动操控指令,让智能小车完成红绿灯辨认、交通标志辨认以及车道实时保持的功能,还有STM32也会经过WiFi模块ESP8266与手机端进行路况数据和操控指令的长途交互。硬件体系框图如下:

基于OpenMV的自动驾驶智能小车模拟系统

下面简略介绍一下,整个体系用到的硬件模块。

基于OpenMV的自动驾驶智能小车模拟系统

详细的硬件电路连接框图如下:

基于OpenMV的自动驾驶智能小车模拟系统

三、软件体系

1、OpenMV中的路况辨认算法完成

本项目的首要路况数据信息都是依据OpenMV摄像头获取的图画进行图画处理得到的。要完成智能小车的自动驾驶行为,最起码要让小车辨认到红绿灯、交通标志以及车道,后续主控器才干依据这些路况数据信息操控小车的运动。关于机器视觉模块OpenMV的介绍,能够阅读《初探机器视觉模块OpenMV》这篇文章。

①红绿灯辨认

首要是对摄像头每帧拍摄到的图画进行图画进行阈值处理,再进行判别出现的究竟是哪种红绿灯(红灯、绿灯、黄灯),然后再将这个断定成果和其他两个数据一起打包经过串口发送出去。

【程序流程图】

基于OpenMV的自动驾驶智能小车模拟系统

【首要程序】

###################################开端####################################
...
sensor.set_pixformat(sensor.RGB565) # 图片格式设为 RGB565五颜六色图
light_threshold = [(59, 100, 26, 127, -128, 127),(59, 100, -128, -40, -128, 127)]; #设置红绿灯阈值,默认为0无红绿灯 1红灯 2绿灯 4黄灯
...
#界说寻找色块面积最大的函数
def find_max(blobs):    
    max_size=0
    for blob in blobs:
        if blob.pixels() > max_size:
            max_blob=blob
            max_size = blob.pixels()
return max_blob
#主循环
while(True):
    clock.tick() #追寻两个snapshots()之间经过的毫秒数
    img = sensor.snapshot() #拍一张相片,回来图画
    blobs = img.find_blobs(light_threshold,area_threshold=150); #找到红绿灯
    cx=0;cy=0;LED_color=0; #变量界说
    if blobs:
        max_b = find_max(blobs); #假如找到了方针色彩
        img.draw_rectangle(max_b[0:4]) #在Blob周围绘制一个矩形
        #用矩形符号出方针色彩区域
        img.draw_cross(max_b[5], max_b[6]) # cx, cy
        img.draw_cross(160, 120) # 在中心点画符号
        #在方针色彩区域的中心画十字形符号
        cx=max_b[5];
        cy=max_b[8];
        img.draw_line((160,120,cx,cy), color=(127));
        img.draw_string(cx, cy, "(%d, %d)"%(cx,cy), color=(127));
LED_color=cy; #红绿灯的阈值是数组里的cy(二进制)个
print(LED_color); #串行终端打印出 红绿灯序号数据
###################################完毕####################################

②交通标志辨认

首要是使用NCC(Normalized Cross Correlation)归一化互相关算法来进行交通标志的图画辨认与匹配。

【NCC算法】

NCC算法的根本完成原理:首要是经过求两幅巨细相近的图画的相关系数矩阵来判别两幅图画是否相关。假定需求辨认的初始图片$g$的巨细为$mn$,摄像头拍摄到的图片S的巨细为$MN$,其间的以$(x,y)$为左上角点与$g$巨细相同的子图画为$S_{(x,y)}$​​​​​​​​。详细的使用NCC算法完成核算图画类似度的方法如下:

基于OpenMV的自动驾驶智能小车模拟系统

【首要程序】

###################################开端####################################
...
sensor.set_pixformat(sensor.GRAYSCALE) #设置图片格式为灰度图
#导入图片模板
template1 = image.Image("/1.pgm") #直行
template2 = image.Image("/2.pgm") #向右转弯
template3 = image.Image("/3.pgm") #向左转弯
template4 = image.Image("/4.pgm") #泊车让行
template5 = image.Image("/5.pgm") #鸣喇叭
#主循环
while (True):
    clock.tick()
    img = sensor.snapshot()
    flag=0
ratio=0
#匹配1.pgm(直行)串行终端打印go,flag=1
    r1 = img.find_template(template1, 0.70, step=4, search=SEARCH_EX)
    if r1:
        img.draw_rectangle(r1,color=(255,0,0))
        print("go")
        flag=1
        img.draw_string(10, 10, "%.d"%flag)
#匹配2.pgm(向右转弯)串行终端打印right,flag=2
    r2 = img.find_template(template2, 0.70, step=4, search=SEARCH_EX) 
    if r2:
        img.draw_rectangle(r2,color=(0,255,0))
        print("right")
        flag=2
        img.draw_string(10, 10, "%.d"%flag)
#匹配3.pgm(向左转弯)串行终端打印left,flag=3
    r3 = img.find_template(template3, 0.70, step=4, search=SEARCH_EX)
    if r3:
        img.draw_rectangle(r3,color=(255,0,0))
        print("left")
        flag=3
        img.draw_string(10, 10, "%.d"%flag)
#匹配4.pgm(泊车让行)串行终端打印stop,flag=4
    r4 = img.find_template(template4, 0.70, step=4, search=SEARCH_EX) 
    if r4:
        img.draw_rectangle(r4,color=(255,255,0))
        print("stop")
        flag=4
        img.draw_string(10, 10, "%.d"%flag)
#匹配5.pgm(鸣喇叭)串行终端打印beep,flag=5
    r5 = img.find_template(template5, 0.70, step=4, search=SEARCH_EX)
    if r5:
        img.draw_rectangle(r5,color=(255,255,0))
        print("beep")
        flag=5
        img.draw_string(10, 10, "%.d"%flag)
###################################完毕####################################

③车道辨认

首要经过OpenMV模块,辨认并跟踪车道阈值,经过几许运算出小车与车道中线的视点(偏左为正、偏右为负),反应出小车与车道的真实违背情况(可量化),后续用于PID操控。

【首要程序】

###################################开端####################################
...
sensor.set_pixformat(sensor.RGB565) # 图片格式设为 RGB565五颜六色图
road_threshold = [(23, 0, -45, 19, -31, 28)]; #黑线道路
ROI = (0, 100, 320, 40)
...
#省略了辨认车道边框函数
#偏移视点核算算法
def get_direction(left_blob, right_blob):
    # 依据图画中的三块左中右的白色部分,核算出摄像头偏转视点
    # ratio < 0 左拐,小车在车道偏右方位
    # ratio > 0 右拐,小车在车道偏左方位
    MAX_WIDTH = 320
    # 调理theta来设置中间宽度的比重, theta越高ratio越靠近0
    # 需求依据赛道宽度与摄像头高度从头设定到合适巨细
    theta = 0.01
    # 这儿的b是为了防止除数是0的情况产生, 设定一个小一点的值
    b = 3
    x1 = left_blob.x() - int(0.5 * left_blob.w()) #左面黑线中心x值
    x2 = right_blob.x() + int(0.5 * right_blob.w()) #右边黑线中心x值
#车道信息核算
    w_left = x1 #左面车道外宽度
    w_center = math.fabs(x2 - x1) #车道中心x值
    w_right = math.fabs(MAX_WIDTH - x2) #右边车道外宽度
#核算摄像头偏移视点
    direct_ratio = (w_left + b + theta * w_center) / (w_left + w_right + 2 * b + 2 * theta * w_center) - 0.5
#回来摄像头偏移视点
return direct_ratio
#省略了可视化绘图函数
...
while(True): #主循环
clock.tick() #追寻两个snapshots()之间经过的毫秒数
    img = sensor.snapshot() #拍一张相片,回来图画
    blobs = img.find_blobs(road_threshold, roi=ROI, merge=True);
    a=0;ratio=0;
    if blobs:
        left_blob, right_blob = get_top2_blobs(blobs)
        if(left_blob == None or right_blob == None):
            print("Out Of Range")
            continue
        else:
#画出车道左面线
            img.draw_rectangle(left_blob.rect())
            img.draw_cross(left_blob.cx(), left_blob.cy())
#画出车道右边线
            img.draw_rectangle(right_blob.rect())
            img.draw_cross(right_blob.cx(), right_blob.cy())
#可视化显示偏转视点
            direct_ratio = get_direction(left_blob, right_blob)
            draw_direct(img,direct_ratio)
            ratio=int(math.degrees(direct_ratio)) #偏转视点转成弧度值
            img.draw_string(10, 10, "%.d"%ratio) #帧缓冲区实时画出偏转视点
            print(ratio) #串行终端打印偏转视点
    img.draw_rectangle(ROI,color=(255, 0, 0)) #画出感兴趣区域
###################################完毕####################################

2、依据ESP8266的长途操控渠道完成

首要是使用点灯科技-Blinker物联网渠道建立操控APP的UI界面,以及调用Blinker的操控代码,完成智能小车操控指令的下发与路况数据的上传。

【长途操控渠道UI界面】

基于OpenMV的自动驾驶智能小车模拟系统

【UI装备代码】

直接运用 点灯.blinker APP导入装备代码即可取得和我相同的UI布局。

{config{headerColortransparentheaderStyledarkbackground{imgassets/img/headerbg.jpgisFull}}dashboard|{typebtnicofad fa-arrow-alt-upmodet0前进t1文本2bgcolsrowskeybtn-goxyspeech|clr#076EEF}{ABCfad fa-arrow-alt-downEF后退HIJKLMbtn-backxyCO|PQlstyle}{ABCfad fa-arrow-alt-rightEF右转HIJKLMbtn-rightxyO|PQU}{ABCfad fa-arrow-alt-leftEF左转HIJKLMbtn-leftxyO|PQU}{ABCfad fa-power-offEF泊车HIJKLMbtn-stopingxyO|PQU}{AtexF小车长途监控体系HJCKLMxyO|PQU}{AnumF障碍物间隔Cfad fa-routePQminmax1cunicmJKLMnum-distancexyEO|U}{AgF小车偏移视点Cfad fa-tachometer-alt-fastPQjk0lJKLMnum-anglexyEO|U}{AgF红绿灯(红1绿2)Cfad fa-siren-onPQjklJKLMnum-ledxyO|U}{AdebEJKLMdebugxyO|U}{AgFWIFI信号Cfad fa-signal-4P#389BEEjk0ldbmJKLMnum-wifixyO|U}{ABCfad fa-repeat-altEF自动驾驶模式HIJKLMbtn-autoxyO|PQU}actions|cmdswitch‡text‡on翻开?nameoff关闭?name—triggers|{source16source_zh开关状况state|on19state_zh|翻开关闭}}

【操控指令与监控数据】

基于OpenMV的自动驾驶智能小车模拟系统

【首要程序】

/***********************************开端***********************************/
...
int  flag= 0;                        //按键标志位
int  l= 0;                          //红绿灯数据
int  a= 0;                         //视点数据
int  d= 0;                        //间隔数据
int  z= 0;                        //json解析出来的数据
BlinkerNumber Number0("num-wifi");//WIFI信号
BlinkerNumber Number1("num-led");//红绿灯信号
BlinkerNumber Number2("num-angle");//视点信号
BlinkerNumber Number3("num-distance");//间隔信号
BlinkerButton Button0("btn-stoping");//停止状况按键
BlinkerButton Button1("btn-go");//前进状况按键
BlinkerButton Button2("btn-right");//右转状况按键
BlinkerButton Button3("btn-left");//左转状况按键
BlinkerButton Button4("btn-back");//后退状况按键
BlinkerButton Button5("btn-auto");//自动驾驶状况按键
...
/*主循环*/
void loop()
{
    Blinker.run();
    Number0.print(WiFi.RSSI());  //发送信号强度
    usartEvent();//串口中止
    l=int(z/10000);          //解析红绿灯数据
    a=int((z-10000*l)/100);  //解析偏移视点数据
d=int(z-10000*l-100*a); //解析间隔数据
    Number1.print(l);  //发送红绿灯信号
    Number2.print(a);  //发送视点信号
    Number3.print(d);  //发送间隔信号
//发送操控指令,灯的亮灭,首要是查看WiFi模块是否接纳到数据
    if(oState == false && digitalRead(LED_BUILTIN)== LOW)//灯灭
    {
      digitalWrite(LED_BUILTIN,HIGH);//灯灭
      Serial.print(flag);                //发送指令
    }
     else if(oState == true && digitalRead(LED_BUILTIN)== HIGH)//灯亮
     {
       digitalWrite(LED_BUILTIN,LOW);//灯亮
       Serial.print(flag);                 //发送指令
     }
}
//Blinker初始化
//WiFi连接信号检测略
//STM32数据上传解析略
...
/***********************************完毕***********************************/

3、智能小车的无人操控计划完成

智能小车在接纳到ESP8266的操控指令和OpenMV路况数据,会依据这些指令数据进行小车运动的操控。

【PID操控算法】

关于直流电机的PID调理,首要用来完成车道保持功能。经过OpenMV回来的偏转视点,进行实时调理电机PWM输出,使得偏转视点$Y=50$​(也便是小车与中线的偏转角为0,由于之前为了传输便利整体加上了50)。故将设定值定为50,经过实时回来的$Y$值与50做差值运算,得到PID的输入误差,经过方位式PID回来实时的PWM值。关于PID操控算法,之前也有介绍到,这儿不再深入赘述。

$$PWM=K_P\theta(t)+K_i\sum_{t=0}\theta(t)+K_d[\theta(t)-\theta(t-1)]$$其间为$\theta(t)$​​是本次OpenMV回来的偏移视点数据$Y$​​与50的差值,$\theta(t-1)$​​为上一个$Y$​​​与50的差值。

【模仿环境

基于OpenMV的自动驾驶智能小车模拟系统

项目百度网盘地址:

链接:pan.baidu.com/s/16iSkrEZp…

提取码:n9sm