第十六届全国大学生智能汽车比赛是我大学参加的第一个有意义的比赛,让我在学校实验室SCA打开了进入大学的大门,无数次的包夜和学习让我静下了心,很荣幸参加过这样一次的竞赛,收获颇丰。简单的做一个总结,算是画上大二一学年的句号,希望对你们有帮助。

本文仅自己看法,并不是权威结论。若有错误或不足之处,还请多多包涵。

摄像头算法控制我分为二类:图像处理和车体控制

第一类:图像处理

1.1 摄像头如何使用(小白篇) 

我比赛用的总钻风130°摄像头,拿到摄像头后,使用例程可以在IPS屏幕上看到一个灰度图像(原始图像)。这个原始图像我们一般都要进行处理,也可以不处理。我来介绍一下常用的三种处理方法.(1)图像二值化。(2)图像灰度。(3)边缘检测算法。

我用的是简单好用的图像二值化,所以我拿二值化进行讲解。我先简单介绍一下二值化图像,在原始图像的基础上,把他分为黑和白(即赛道是白,其它是黑)。

灰度图像有256个像素点 即0-255。0-255是什么意思,个人理解即颜色的深浅。0是黑,255是白。0-255是介于黑至白之间。对于一个灰度图像中,图像中每一个点都是在0-255之间,每一个点都有一个值.受光照影响,不同位置,不同时间,不同关照,每一个点的值都会不同。所以我们要对这个值进行区分黑白,要进行处理。处理的方法就是:大津法(还有其他方法不唯一,大津法用的人比较多)

大津法就是对你的灰度图像进行处理后得到一个阈值。输入进去一个二位数组图像,然后会返回给你一个阈值,然后你就用这个阈值进行二值化(即大于阈值为白(黑),小于阈值为黑(白)。即你就可以得到一个二值化图像。

1.2 扫线

  关于摄像头循迹,一般都是按照获取赛道中线,根据赛道中线来进行循迹的。如何获得赛道中线,我来简单介绍一下。在2.1中我们知道中线是按照左右边线获得的,而左右边线我们则需要在图像中进行处理获得,这个过程就是扫线。

  在二值化图像的基础上进行扫线处理,以图像60行120列为例,从上到下为0-60行,我们取第60行中点(图像下面相比上面较稳定,所以我们从下面开始),也就第60行第60列为起始点往两边进行扫线。

一般来说,我们的车是放在赛道中间,即起始点为白点,然后我们往两边判断一直到黑点,我们记录下黑点坐标放进左右边线的数组里,若没有扫到黑点则记录图像数组边界值放进左右边线数组里。这样每一行都这样重复就可以获取左右边线数组。简单的扫线就这么结束了。(后面还会补充扫线进阶篇,你也可以根据简单的扫线,自己想一想代码如何升级)

1.3 中线

我们是根据赛道中线进行循迹,所以中线的处理也非常重要。

中线 = (左边线+右边线)/2 

中线 = 左边线+X

中线 = 右边线-X

 X的值,根据实际情况来定。

1.4 图像的直道和弯道

所有元素处理过后都是直道和弯道,这部分的处理也决定了你车体速度的上限。不仅是你PID的调参还有你图像的处理。

先说简单的,根据中线的值来确定直道和弯道,在不同情况下,调用不同的PID参数。(等元素处理都写完,回来在写这个进阶篇)

1.5 元素处理

1.5.1 十字、环岛、三岔路口

  这三个元素我是同一个处理方法。三个问题:

(1)拐点问题(左右看看,上下看看)

所有拐点我都分为下拐点和上拐点。

下拐点:十字下面两个拐点,环岛入环和出环拐点,三叉下面两个拐点

下拐点都是在边线上,在一定范围内遍历数组寻找最大值或最小值。

上拐点:十字上面两个拐点,三叉路口上拐点。

上拐点(抽象讲解)图像数组 范围内 从左到右,从下往上扫点(白变黑的跳变点),扫到把行坐标放入一维数组内,然后范围内搜索较大(小)值(一次就好)。

拐点详细讲解

(2)补线问题

两点确定一条直线,确定斜率,就确定两点中的每一个点,然后让这条直线上所有白点变成黑点,在重新扫线,就可以了。(说着其实都很简单,其实也不难,自己多动动手0.0)

(3)状态问题

   1)十字

这是即将进入十字的布线状态,在看到两边丢线且中间大量空白时,进入十字状态,然后开始算出上下四个拐点,上拐点连接下拐点就可以如图所示了。不管你是即将进入十字还是十字中,还是即将出十字,都需要这么一个补线状态,所以,我们用简单的if来控制十字状态。

当下拐点消失时,我们固定下拐点值,直接连接上拐点。我简单来说一下我的代码怎么写的。
if( 左边线丢线&右边线丢线)   (条件判断的有点粗糙,后面详细说一下)     
{    
 if(中间大量白点)     
  {    
   进入十字标志   
  }       
}
 
 
 
if( 进入十字标志)                                                                       
{   
   搜索左上拐点(没搜到给定值)       搜索左下拐点  (没搜到给定值)   连接左上和左下两拐点    
 
   搜索右上拐点(没搜到给定值)       搜索右下拐点  (没搜到给定值)   连接右上和右下两拐点     
 
   if(两边不丢线,结束十字标志)
   {  
 
   }         
}      

2)环岛 

  环岛的补线差不多就4个状态(还有一个出环切内的补线)      但是我分为7个状态。用的switch语句来控制个个状态,一但进入环岛第一个状态后面,在环岛结束前都不会误判,非常好用。

if(左边丢线 || 右边丢线)  //  进入元素识别状态
{
 
 
  左环岛判断
  if(左边丢线||右边不丢线)  // 可能是环岛或者弯道或者车库
  {
    if(右边边线是条直线)  // 排除弯道
    {
 
      给予左环岛标志;
      进入左环岛状态1;
    }
  }
 
roadabout_dispose();  //环岛处理函数
}
 
 
roadabout_dispose()
{
 if(左环岛标志位)
 {
  switch(左环岛状态)
   {
     case 1:
        if(左边即将不丢线)       
         {           
           进入左环岛状态2;
         }
     break;
 
     case 2:
        if(左下边为圆环)
         {
           进入左环岛状态3;
         }
     break;
 
     case 3:
        if(左边第二次丢线,找拐点补线进入环岛)
         {
           进入左环岛状态4;
         }
     break;
 
     case 4:
        if(进入环岛,环岛内)
         {
           进入左环岛状态5;
         }
     break;
 
     case 5:
        if(即将出环岛,找拐点补线出环岛)
         {
           进入左环岛状态6;
         }
     break;
 
     case 6:
        if(右边即将不丢线,车身摆正)
         {
           进入左环岛状态7;
         }
     break;
 
     case 7:
        if(左边即将不丢线)
         {
           进入左环岛状态8;
         }
     break;
 
     case 8:
        if(完全出环岛)
         {
           清除左环岛状态;
           清除左环岛标志位;
         }
     break;
 
   }
    
  if(左环岛状态1||左环岛状态2)
  {
   找到左下拐点;
   找到左上拐点;
   补线;
  }
 
 
  if(左环岛状态4)
  {
   找到左上拐点;
   定点补线;
  }
 
  if(左环岛状态6)
  {
   找到右下拐点;
   定点补线;
  }
 
  if(左环岛状态7)
  {
   定点补线切内环;
  }
 
  if(左环岛状态8)
  {
   左边定点补线,防抖;
  }
 
 }
}
 
 
  3)三岔路口

 作为十六届的新元素,起初觉得很简单,拉一条线就能过,后来错了,其实很有讲究和难度,并未有想象出来那么简单,衍生出来很多难搞的问题,要仔细对待。三岔路口最好的特征点,就是两个Y拐点和一个V拐点。

在你看到三岔路口时,并没有丢线状态,所以你在直道上就要判断Y拐点是否存在。有时三岔接弯道时,车体无法“完全”正 ,肉眼可能觉得车体正的(但是我们称这种为斜入)只能看到一个Y拐点。所以三岔路口要分为正入,左斜,右斜,三种判断。简单说一下怎么写的三岔,因为三岔路口我写的不是很好,2.8以上会误判不是很好,
if(中间上面出现黑点)
{
 判断Y拐点是否存在;
 2个Y拐点为正入;
 左边存在为左斜;
 右边存在为右斜;
 判断V拐点是否存在;
 V拐点存在或Y拐点存在进入三岔路口标志位1;
 进入三岔路口状态1;
}
然后根据3种情况和状态 进入下一阶段和拐点补线操作。
可以模仿环岛伪代码来写。
仅供参考。

借鉴一下思路即可。

1.5.2 坡道

坡道个人建议用红外测距,还是比较准的。即简单也准确。

1.5.3 车库

车库识别和处理详解

第二类:车体控制

车体控制主要是舵机和电机以及配合图像处理进行循迹。俗话说的好:图像处理的好,开环也能跑。说到控制那不得不说几个环的事,简单说一下最基本的两个环(1)转向环 (2)速度环

2.1 转向环

我用的是C车模,S3010舵机。转向要能进行左转和右转,所以我们要在图像上区分左和右。

例如:我的图像是 60×120( 60行,120列)我利用(左边线+右边线)/2=中线 这时候你就取一行或多行 赛道中线-图像中线=error。得到error来区分左右,正常来说你把车体放在赛道中间,error会是0。车体往左边偏为正,车体往右边偏为负。(反之也可以)这样我们就区分了左和右,车体就可以进行转弯了。

但我们发现转弯可能过大或者过小,没法理想的转弯,这时候我们要用PID算法进行调节,新手建议位置式PID(非常够用)。个人建议起步用位置式PID中的纯P调节。调到2米时用PD调节,跳到2米8时换模糊PID。

这时候你把error放入位置式PID中,Ki和Kd给0,调节Kp,输出Steer。然后用舵机中值加减Steer得到的控制值来控制舵机灵敏的转向。这样舵机就可以根据在赛道的不同位置来进行灵活的转向了,这就是一个简单的转向环。
十六届全国大学生智能汽车代码总结

最近考试和课设有点多,所以更新比较慢,个人也比较懒哈,有问题留言。