以下内容来自我的博客 http://blog.csdn.net/a158337/article/details/38925757,点击可以查看文
章原文,有些人可能是先看到我上传的代码的,没有看到文章,可能效果不好,为了使下载
代码资源的人进步更快所以写了下自己的经验,做车中遇到问题也欢迎来找我,我尽力帮忙。
代码的下载地址是:http://download.csdn.net/detail/a158337/7735547。
这篇文章不能帮助每个人解决做车遇到的问题,有些问题是想不到的,你不知道会出现在那
里,或者不能知道会出现什么样的问题,最好的办法就是培养自己分析问题解决问题的能力,
遇到问题解决问题。代码已经上传,经验已经分享,没有免费的午餐,因为我也是弄了整整
一学期,经历了非常非常痛苦的过程,没有人可以体会,在做车过程中我第一次体会到什么
叫做压力,什么叫做欲哭无泪,遇到困难的时候叫天天不应,叫地地不灵。老师,同学都是
那么无助,白天上课,准备考试,晚上调车,守着龟速的车弄了一个多月没有任何进展,说
多了都是泪,希望看到这篇文章的人做车顺利,看不懂我代码的可以问我,做车遇到问题的
也可以问我,我会尽力帮助的,QQ824955445.我只要 7 分的 CSDN 下载积分,我的程序对
大神没有用,但是对于大部分刚刚入门的人还是有帮助的。下面是博客内容。
本人参加了第九届飞思卡尔智能车比赛,光电组。现在分享下自己的心得和体会,希望能够
给后来人带来点帮助。为什么不在智能车论坛发,因为那个论坛我现在不经常上了,如果有
人回复我怕不能及时回复,下面这些内容适合新手,当然也可能有错误,希望根据自己的情
况甄别。好了废话不多说开始吧,分几点来说。
我的程序代码下载地址: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.二值化
这种方式有很多人用,代码大概如下
[cpp] view plaincopy
int i,j;
for(int j=0;j<128;j++)
{
1. void binaryzation(unsigned char *pixel)
2. {
3.
4.
5.
6.
7.
8.
9.
10.
11.
else
if(chen[j]>=PixelAverageValue)//比较是否大于临界值,黑线电平低
chen[j]=1;
chen[j]=0;
}
for(int i=0;i<128;i++)//根据赛道黑白都是连续的特性进行滤波处理
if((Pixel[i-1]==1)&&(Pixel[i+1]==1))
{
Pixel[i]=1;
if((Pixel[i-1]==0)&&(Pixel[i+1]==0))
{
Pixel[i]=0;
}
}
{
}
}
}
}
if(Pixel[i]==0)
{
}
if(Pixel[i]==1)
{
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30. }
31. void calculate_line(void)//计算中线
32. {
33.
34.
35.
36.
37.
38.
39.
40.
41. for(int j=(int)g_centre_line_new;j<=127;j++)
42. {
43.
44.
45.
46.
47.
48. }
49. old_line= new_line;
50. new_line=(left+right)/2;
51. }
right=j+1;//记录右线的坐标
break;
left=i-1;//记录左线的坐标
break;
}
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)
{
if(Pixel[j]==1&&Pixel[j+1]==0&&Pixel[j+2]==0&&Pixel[j+3]==0)
{
这种方法我感觉不是很好,但是有些人用的非常不错,想不到什么好处,感觉坏处:首先这
个值设置显的非常重要,相对于下面将要提到的检测跳变沿还重要,尤其是余弦效应比较大,
或者采集到的图像信息不太好的时候,这一个值确定很难,如果设置成静态,对环境依赖很
强,动态根据采集到像素点平均值在乘以系数得到,很坑爹,下面会提到调参数问题,一个
值决定生死,里面判断有可能会出现很多点是误判了,如果误判的点是在接近左右线的位置,
把黑线看成了白线,或者判断的是白线其实是黑线,当然如果误判都对称还好说,如果不对
称的,比如单侧丢线的时候呢,未丢线的黑线和白色赛道没有什么变化,因为丢线的位置原
来是黑色电压低,现在突然变成了白色,按照平均值来计算临界值肯定不准确,对确定为丢
失黑线的位置有干扰。而且这个要多次对数组进行访问,有点浪费时间。还有就是当 ccd
视野比较广的时候,某些边界点的值实际是赛道外面的,就我个人来说我的 ccd 信息的数
组,坐标小于 40,或者大于 90 的点都是看的赛道外面,上面方法确定中线的时候显然利用
了这些点,是不应该的。如果 ccd 镜头视野不广阔,又非常容易丢线,这就矛盾了。
2.检测跳变沿:
这种方法是用的最多的,不是我瞎说,以前看过别人统计。代码大概如下:
[cpp] view plaincopy
int i,j;
int lines=0;
b_left_flag=0;
b_right_flag=0;//左右线检测标志位
for(i=lines;i-interval-1>=L_B;i--)
{
1. void CCD_P2(unsigned char *s,int interval,int yuzhi)
2. {
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26. }
}
for(j=lines;j+interval+1<=R_B;j++)
{
b_right=j+interval;
b_right_flag=1;
break;
}
}
}
if(s[i]-s[i-interval]>yuzhi&&s[i-1]-s[i-interval-1]>yuzhi)
{
b_left=i-interval;
b_left_flag=1;
break;
if(s[j]-s[j+interval]>yuzhi&&s[j+1]-s[j+interval+1]>yuzhi)
{
虽然这种方法也用到比较,也是通过找临界值,来确定中线位置。但是,通过隔几个点比较
这些值是否大于临界值来确定黑线这种方法要比那个小,而且可以也比较容易修复误差,比
如我检测到第 i 个点符合条件,i 是不是黑线呢,我可以继续检测到,直道不符合条件,就
可以准确的知道那个一定是黑线了,当然这只是一种思想。我用的就是这种方法。
当两条线都检测到就可以正常处理,所以关键的是如何处理丢线的情况,这种策略不是唯一
的,不能说对与错,这个同样可以自己寻找方法,关键同样是,你要自己琢磨自己这样操作
符不符合实际,比如丢了右线,你给多少,你是给右线确定的数值,如果这样,右线给多少。
还是根据自己左线的位置的坐标加上一个数值直接得到中值,这个值给多少
如何确定,怎么才算合理。我的方法是补上一次的赛道宽度的一半。代码后面会提供。
3.寻黑不寻白。这个方法不是我的。虽然简单,但是人家可以用,我用不了,不知道为什么。
也许这个适合直立平衡车,代码大概如下:
[cpp] view plaincopy
count++;
line+=i;
1. void ccd_p(char *s)
2. {
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20. }
}
if(count!=0)
{
}
else
{
}
}
int i,line=0,count=0;
for(i=0;i<128;i++)
{
if(s[i]>yuzhi)
{
line=line/count;
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 语言数据类型重新认识,最
后结尾的例子。
最后一句感悟,智能车最大的特点不是保证每个时刻不出现错误,而是每个时刻都能对前次
出现的误差进行正确的,快速的调节。希望看到这篇博客的人做车顺利,如果有人向参考最
终代码,请前往:http://download.csdn.net/detail/a158337/7735547 下载,表达一下对我
的支持。虽然不做车了,但是还是愿意帮一些像我一样的小白的
比赛视频地址:智能车视频,其他的都在我的专辑里面
有问题请留言。