飞思卡尔智能车经验

本人参加了第九届飞思卡尔智能车比赛,光电组。现在分享下自己的心得和体会,希望能够给后来人带来点帮助。为什么不在智能车论坛发,因为那个论坛我现在不经常上了,如果有人回复我怕不能及时回复,下面这些内容适合新手,当然也可能有错误,希望根据自己的情况甄别。好了废话不多说开始吧,分几点来说。

我的程序代码下载地址:http://download.csdn.net/detail/a158337/7735547

1.做车心态和赛前准备

这个放在第一点是非常重要的,做车一定必须有个好心态,中间可能遇到各种各样的问题,比如车本来好好的,突然不能跑了,或者龟速了,突然某个器件烧了,电机不转动了,尤其是光电组和摄像头组的对环境依赖很大,电磁组虽然信号较稳定,但是车因为速度快,往往会撞坏采集模块,同样很坑。不论什么时候都要保持一个积极向上的心态,小车虐我千百遍,我待小车如初恋。不乏很多人开始做车之前士气高昂,遇到困难也会尝试解决,但是如果一个问题长时间不能解决,就渐渐放弃了,长时间是多次时间,也许一周,一个月都是有可能的,或者别人的车都跑的很快了,自己的车还是龟速,甚至连龟速都是不能跑,在雪上加霜的是,你还要上课,课程难,但是你上课又没有心思听,人在教室,心里却想着车,一学期快结束了,也快比赛了,大家都准备考试,你还在调车,担心各种挂科,白天上课,晚上调车调到深夜........各种情况也要有心里准备,这些都是极有可能经历的,在比赛中也有不少大神的车6次机会都没有跑下来......这些都要去思考都是很有可能的,所以我们做车心里准备一定要做好,同时要保持一个好的心态,在自己遇到困难的时候,要时刻相信困难的解决就在下一秒,也要不断总结,思考问题可能出现在什么地方。另一点不得不说的就是要选择好队友了,一定要相互合作,队友不一定要多么多么牛,但是一定要是那种乐意付出,能吃苦的人,有困难大家一起扛才可以,遇到问题可以一起商量解决才可以,希望做车时一定要严以律己,绝对不能坑队友,以负责的态度去对自己的队伍,同时为了自己不被坑,选择自己信任的人。上面这些绝对是重要的,所以放在第一点,你可以不相信,但是别后悔。

2.车的机械结构

智能车有个大神说的不错:车的机械结构决定了车的最大速度,车的软件决定了车可以达到的速度。这点我非常认可,很多参赛的都不是机械专业的,基本都是根据以往的车来确定车的机械结构,重心高低,电池位置,前轮内倾还是外倾等等。其实我感觉这些在车2m/s一下的时候都不是很关键的,至少说影响不是那么明显。说一下我当初一个愚蠢的错误,认为舵机的中值在pwm周期为20K的时候,1500是中值,就给舵机1500的pwm值然后把这个位置当作中间位置,放车上去了,后来很长时间发现左右转不一样,发现舵机1500根本不是中值。舵机的打角度范围是0-180度,车的前轮用不了那么多,我们只需要选择一个范围,在这个范围里面选择一个值作为中值,给这个值的时候要保证舵机打角度可以使车的前轮左右打角度都能打到极限,最好选取90度时候作为中值。这样是最好的,个人这样办的。车的机械调整也不是一成不变的,不要过于盲目的听从别人,有些时候别人会无意误导了你,比如第八届飞思卡尔的B车模是倒着跑的,轮子的差速应该是紧一些效果好,第九届变了,车正着跑,如果你还是把车的差速调到很紧,车根本不行,弯道必然出去,所以遇到问题还是多尝试多分析好,在前期不要过于在意车的结构,除非你比较明白,假如在第九届你按照第八届参赛的人的意见把车差速调紧,以后也不怀疑这个问题,你的车估计永远跑不了,但是你只要随意点,前期车不太快的情况下还是可以跑的,以后自己调节找找规律也很好啊。

3.采用的传感器

这一点也是重要的,车的控制是根据采集来的信息来处理的,如果采集得到的信息本身都是错误的,那么无论你的算法有多牛,估计车也不能跑。电磁的我没有做过,我对硬件了解很少,但是在设计或者制作的时候最好保证采集性要好,让在距离一个相对较远的范围还是能够采集到信号。光电组的只有一行数据,经常会遇到丢线的问题,尤其是车入弯道后必丢内圈的线,最令人蛋疼的就是线性ccd的余弦效应。选择传感器的时候一定要选择余弦效应比较小的,视野比较大的。比如我们反复换,最后选择了广角无畸变镜头,效果不错,这一步也是决定车能否运行的关键一环。如果这一环选择的好,程序按照最简单的写都可以使车运行,也许速度不尽人意,但是选择不好,不论你程序怎么改,参数怎么调节,车都不能跑完全程一次。

4.硬件电路

这一点具体我不能说什么,因为我不懂,参赛的时候发现原来一个很不起眼的问题,显的很重要,拨码开关,当时我们用了四个,因为自己的车起跑线检测是利用程序检测跳变沿的个数实现的,怕误检,不得已只能牺牲一个开关设置检测与不检测,只要有一次误检,我就把开关拨回来。后来听别人说,有些赛区试车的时候灯光和比赛灯光不一样,导致很多车没有跑下来,又牺牲了一开关设置两个不同的阈值,,现在就剩下两个开关设置速度了。四个速度有点少。能够在硬件上解决的最好在硬件上解决,比如起跑线检测,我的很有可能出现漏检,但是用硬件的红外检测就不会,比程序要好很多。而比赛的时候尤其是名次靠前些的队伍,一秒的名次差距是很恐怖的。

5.算法

上面完了后,就应该说是程序了,很多人把这个称为算法,其实我感觉这算不上算法,顶多算策略,至少大多数人的都不能成为算法,我的是不能的。根据采集到的信息进行处理让车在赛道上行驶就是一种策略,其实不必过于在策略,尤其是非要找大神的代码,别人一半不给,给了也不全,或者看不懂,看懂的时间估计自己思考的都差不多了。这种控制策略绝对不是一条,但是,自己要去根据赛道元素对比自己的算法去思考,看看有没有什么漏洞。怎么去修改,有没有少考虑了什么情况。比如,拿ccd来说,无法就是如何根据采集到的一行数据,提取中线位置,然后车根据这个位置求出误差,然后得到舵机打的角度,丢线的时候无非就是要求在如何只有一条线的情况下,或者两条线都丢的情况下正确的得到中线位置,保证舵机打相应的角度,确切的说打的角度在合理范围之内。不同的思考方式会有不同的算法,但是都是为了计算中线位置,但是,计算过程可能不一样,比如有些人用二值化,有些人用根据左右线计算中值。下面我说一下自己对几种方法的看法。首先你得到一个数组,里面包含了采集到的值,你是怎么提取,从左向右,从右向左,从中间向两边,不论怎么样,你都要避免利用赛道外的信息,或者防止赛道外的信息对你处理这些数据造成影响,有时候利用了也无所谓因为比赛时候赛道环境比较好,但是建议最好还是不用好。

1.二值化

这种方式有很多人用,代码大概如下


void binaryzation(unsigned char *pixel)
{
    int i,j;
    for(int j=0;j<128;j++)
    {
        if(chen[j]>=PixelAverageValue)//比较是否大于临界值,黑线电平低
            chen[j]=1;
        else
            chen[j]=0;
    }
    for(int i=0;i<128;i++)//根据赛道黑白都是连续的特性进行滤波处理
    {
        if(Pixel[i]==0)
        {
            if((Pixel[i-1]==1)&&(Pixel[i+1]==1))
            {
                Pixel[i]=1;
            }
        }
        if(Pixel[i]==1)
        {
            if((Pixel[i-1]==0)&&(Pixel[i+1]==0))
            {
                Pixel[i]=0;
            } 

        }
    }

}
void calculate_line(void)//计算中线
{
    for(int i=(int)g_centre_line_new;i>=0;i--)
    {
        if(Pixel[i]==1&&Pixel[i-1]==0&&Pixel[i-2]==0&&Pixel[i-3]==0)
        {
            left=i-1;//记录左线的坐标
            break;
        }
    }
for(int j=(int)g_centre_line_new;j<=127;j++)
{
    if(Pixel[j]==1&&Pixel[j+1]==0&&Pixel[j+2]==0&&Pixel[j+3]==0)
    {
        right=j+1;//记录右线的坐标
        break;
    }   
}
old_line= new_line;
new_line=(left+right)/2;
}

这种方法我感觉不是很好,但是有些人用的非常不错,想不到什么好处,感觉坏处:首先这个值设置显的非常重要,相对于下面将要提到的检测跳变沿还重要,尤其是余弦效应比较大,或者采集到的图像信息不太好的时候,这一个值确定很难,如果设置成静态,对环境依赖很强,动态根据采集到像素点平均值在乘以系数得到,很坑爹,下面会提到调参数问题,一个值决定生死,里面判断有可能会出现很多点是误判了,如果误判的点是在接近左右线的位置,把黑线看成了白线,或者判断的是白线其实是黑线,当然如果误判都对称还好说,如果不对称的,比如单侧丢线的时候呢,未丢线的黑线和白色赛道没有什么变化,因为丢线的位置原来是黑色电压低,现在突然变成了白色,按照平均值来计算临界值肯定不准确,对确定为丢失黑线的位置有干扰。而且这个要多次对数组进行访问,有点浪费时间。还有就是当ccd视野比较广的时候,某些边界点的值实际是赛道外面的,就我个人来说我的ccd信息的数组,坐标小于40,或者大于90的点都是看的赛道外面,上面方法确定中线的时候显然利用了这些点,是不应该的。如果ccd镜头视野不广阔,又非常容易丢线,这就矛盾了。

2.检测跳变沿:

这种方法是用的最多的,不是我瞎说,以前看过别人统计。代码大概如下:
void CCD_P2(unsigned char *s,int interval,int yuzhi)
{
  int i,j;
  int lines=0;
  b_left_flag=0;
  b_right_flag=0;//左右线检测标志位
  for(i=lines;i-interval-1>=L_B;i--)
  {
    if(s[i]-s[i-interval]>yuzhi&&s[i-1]-s[i-interval-1]>yuzhi)
    {
			b_left=i-interval;
			b_left_flag=1;
			break;
    }
  }
  for(j=lines;j+interval+1<=R_B;j++)
   {
		if(s[j]-s[j+interval]>yuzhi&&s[j+1]-s[j+interval+1]>yuzhi)
		{
			b_right=j+interval;
			b_right_flag=1;
			break;
		}
	}
 
}
虽然这种方法也用到比较,也是通过找临界值,来确定中线位置。但是,通过隔几个点比较这些值是否大于临界值来确定黑线这种方法要比那个小,而且可以也比较容易修复误差,比如我检测到第i个点符合条件,i是不是黑线呢,我可以继续检测到,直道不符合条件,就可以准确的知道那个一定是黑线了,当然这只是一种思想。我用的就是这种方法。

当两条线都检测到就可以正常处理,所以关键的是如何处理丢线的情况,这种策略不是唯一的,不能说对与错,这个同样可以自己寻找方法,关键同样是,你要自己琢磨自己这样操作符不符合实际,比如丢了右线,你给多少,你是给右线确定的数值,如果这样,右线给多少。还是根据自己左线的位置的坐标加上一个数值直接得到中值,这个值给多少
如何确定,怎么才算合理。我的方法是补上一次的赛道宽度的一半。代码后面会提供。
3.寻黑不寻白。这个方法不是我的。虽然简单,但是人家可以用,我用不了,不知道为什么。也许这个适合直立平衡车,代码大概如下:
void ccd_p(char *s)
{
    int i,line=0,count=0;
    for(i=0;i<128;i++)
    {
       if(s[i]>yuzhi)
       {
           count++;
           line+=i;
       } 
    }
    if(count!=0)
    {
        line=line/count;
    }
    else
    {
        line=64;
    }
}
这种方法,虽然也利用了赛道外信息,但是省去了丢线的处理,因为检测的是白色部分。临界值的确定也要是动态的,根据自己尝试,比如本次采集到的最大值和最小值的差之类的再乘一个系数。如果赛道背景是蓝色,这种方法也许可以,但是如果是白色,或者浅色,估计有点玄乎。这种方法就不用纠结于丢线。效果如何还是自己尝试吧。

6.参数调试和细节

代码完成了就是参数的调试了

舵机的PD调节

不知道别人是怎么样的,我是这样的,首先根据舵机中值是1900,距离左右极限是600.,然后移动车体,用上位机软件用手左右移动车在直道或者弯道上车的中线距离64的差的最大值大概是多少,比如我的在30左右,600除以30就是20多,这还不确定,但是确定的是,他是一个两位数。先在20上下范围调,这时候把D设置为0.然后给车一个慢速,1.5m/s左右,一直调到车可以非常稳定的适应各种赛道的时候,然后,慢慢加D,一直到车不能跑了,可以先给个大的D,往下减。然后试着提速,一直到车不能稳定跑完全程,这个时候速度,记下来,然后,稍微降速,然后降低P值,自己要慢慢调节每次降低多少合适。D值不要动。

速度PID调节

说实话,这个我不知道怎么调节,但是调节的效果如果好的话应该是弯道减速不太明显。几乎全程都是一个速度,自己设置的除外。但是实际发现,比赛的时候很多队伍设置了直道加速弯道减速,效果不好,车反应不过来。当然也有好的。我直接给电机一个pwm值发现车根据自身可以很好的适应赛道,弯道减速,直道加速。但是最好还是调节下,因为决赛的坡道,如果速度开环下坡非常不稳定,冲出去是必然。

C语言数据类型统一

编程的时候C语言的数据类型尽量做到统一,尤其是函数声明的参数的数据类型和传递进去的数据的数据类型,不然会有很多隐患,比如,设置形参是unsigned char类型,实际传递的是char ,如果计算中线出现了负数,很坑。具体分析见 C语言数据类型重新认识,最后结尾的例子。

最后一句感悟,智能车最大的特点不是保证每个时刻不出现错误,而是每个时刻都能对前次出现的误差进行正确的,快速的调节。希望看到这篇博客的人做车顺利,如果有人向参考最终代码,请前往:https://github.com/xiaobai1993/FreeScaleSmartCar下载,。虽然不做车了,但是还是愿意帮一些像我一样的小白的
比赛视频: 智能车视频,其他的都在我的专辑里面



你可能感兴趣的:(麻烦和收获)