实验九 小车花样秀—大胆秀出爱
这次实验我们来做一下有意思的东西,我们不需要任何其他的工具,只需要用到我们的小车然后配合我们的程序走出我们想要走出的形状,当然已经有很多人走8字了,我们在这里就不再走一遍了,那么现在我们就一步一步用平衡小车走出 “我爱你”( ps I ❤ U),当然了,我做的还不是很好,大家如果做的比较好了,完全可以以一个工科生的方式给妹子表白嘛!
接下来我详细的说一下每一个步骤,相信大家看完都能明白其中原理并做出自己的图形。
我们的基本代码用的是卡尔曼滤波标准版本,我们都知道小车编码器的值和小车的速度成比例关系,假如在STM32 的5MS中断函数中,将编码器的结果乘以0.005,一个和速度成比例关系的值乘以时间就是一个和距离有比例关系的值了,然后经过实地矫正,就可得到小车的距离。下面这段代码就是这段话的解释:
if(Encoder_Left>0) distance = distance + (float)Encoder_Left*0.005; else distance = distance - (float)Encoder_Left*0.005;
disSHow = distance*2.176542;
这里很明显我们可以看到左轮编码值被我们用来记录小车的行驶距离了,2.176542在这里就是实地矫正系数,而disSHow就是矫正后的真正小车位移了。我在多说一句,至于为什么Encoder_Left>0时是加否则是减呢,那是因为我们写代码是,需要小车不管前进还是后退,都需要位移值增大,这样我们才能方便的让小车走出我们想要的图形了。
转弯角度是同样的道理:
if((Encoder_Left-Encoder_Right)>0) turning = turning + (float)(Encoder_Left-Encoder_Right)*0.005; else turning = turning + (float)(Encoder_Right-Encoder_Left)*0.005;
turSHow = turning;
只不过转弯角度用上午是左右轮编码器差值罢了。
其实在I ❤ U这三个图形当中,最难的就是心形了,我做的不好,你们做好了,就可以找妹子表白了,哈哈,当然这是句玩笑话,回归正传,那么我是怎么设计这个心形的呢?
相信大家看完这图也一目了然了吧,虽然我画的有点丑!
那么说道现在,其实我们的核心思想已经出来了,就是当小车达到某个位移或者某个转弯角度时,我们让小车执行相应的动作就好了。
好吧,原理思想和部分代码我讲完了,具体的代码我就不一一分析了,如果有不懂得等下在下面我会将完整程序和视频打包给大家,给大家留点空间,还是希望大家能够通过自己的理解来完成了。
主要的改进在show.c和 minibalance.c里面,当然一些没有定义的全局变量也要定义一下,比如要在main.c中加入int turning; int distance;在main.h中加入extern int turning; extern int distance; 在MiniBalance.h中放入extern int ThreeSecFlag;
声明:这次实验是我在xxjian大哥的帖子的基础上想到的,经他本人同意后所以写了出来, xxjian大哥也给了我一些指导,再次表示感谢,这里附上xxjian大哥的帖子的传送门:http://forum.eepw.com.cn/thread/273082/4#31 大家可以看看。
最后,老规矩,我将本人拍的视频拿出来献丑了,大家看看就行,欢迎吐槽,如果做成这样的话去表白成功几率不大,革命尚未成功,同志还需努力啊。有什么可以交流的或者不太明白的地方欢迎大家跟帖交流或者私聊,也希望大家早日完成自己的diy秀。小车循线下期见!
视频地址:http://player.youku.com/player.php/sid/XMTM0MzM0OTM4OA==/v.swf
视频奉上,欢迎吐槽!
这里看效果不好,还有优酷地址,点击即可观看:
因为版主可能最近比较忙,可能不能及时审批文件,大家如果下载不了的话我就发一下百度云的链接吧!
百度云链接:http://pan.baidu.com/s/1mg1qhKk实验十 CCD前瞻—DemokTool上位机上手
好久不见,今天我们来上手看看DemokTool这款由岱默科技出品的专用摄像头调试上位机软件。
首先,软件一打开就是岱默大片的广告,看着不太好,但没办法,谁让这是人家开发的呢!
要想测试这款软件,我们必须先给小车装上CCD,大家可能都见过飞思卡尔光电组的车模吧,上面的摄像头都架的很高,因为只有架的足够高,才能让摄像头有足够的前瞻和进光量,而我们的小车相对于来说又非常的矮,所以我们就必须给小车装上一个支架将摄像头支起来,就像这样:
我是这样做的,首先在小车中下方打孔(ps用烙铁秒秒钟就好了),然后找个塑料棒或者木条,同样用烙铁烫个洞,然后用螺丝拧紧,在接口处涂上502,事实证明这样摄像头很稳固,不会随着小车运动而晃动。还有一个问题很坑爹的是官网给我们推荐的岱默科技提供的CCD摄像头的接线和我们的小车接口根本插不进去,还好我机智的换了杜邦线,但是由于亚克力板没有开口,所以我们还需要将亚克力板开个小口,这样基本上小车部分就连接好了。
接下来稍微改动一下程序:
首先将顶配版main.c中的第45行“ccd_Init();”和第51行“CCD();”取消注释(程序部分修改完毕,是不是so easy)。
然后转到adc.c中可以看到这样的引脚定义:
#define TSL_SI PAout(12) //SI
#define TSL_CLK PBout(4) //CLK
岱默的CCD摄像头共有6个引脚: 分别是SI,SO,AO,AOO,VCC,GND
所以很明显, VCC和GND就不多说了;
CCD摄像头的SI连小车上的A12口;
CCD摄像头的CLK连小车上的B4口;
CCD摄像头的AO连小车上的A7口;(AO和A00是一样的,只是放大倍数不同,所以AOO不连)
现在只需要把改好的程序烧录到小车,打开小车让其平衡,然后通过串口线连上电脑,打开DemokTool软件,将波特率调到最大(115200),选择对应的com口,然后打开串口,点击“黑白数据”,然后选中又上方的“根据界面尺寸放大像素”,然后连续采集,根据上位机图像效果,用梅花起子拧CCD摄像头背部的螺丝(调节放大倍数适应当前光线环境),直至识别到清晰的黑线即可。
下面放上我已经调节好了的视频:(注意看好电脑屏幕哦)
视频地址:http://player.youku.com/player.php/sid/XMTM1NjgzNjI4OA==/v.swf
下一个实验:正式CCD循迹(正在优化,近期更新)!
实验十一 CCD循迹小车
今天终于忙完了这段时间手头上的琐事,可以继续折腾我的小车了,上次实验我们已经将摄像头架好了,基本的上位机操作大家也学会了,那么今天我们就来讲讲软件部分是如何让小车做到循迹的。
本次实验实在“大渣渣”(ps车友的ID,并非冒犯)同学的基础上完成,在这里感谢“大渣渣”同学,他已经讲解的很好了,能写出这样的代码还是很牛b的,所以我就从另一个角度来给大家分析,毕竟每个人都有自己的想法嘛!当然最后我仍然会将代码打包供大家下载来学习分析!
首先先聊聊线性ccd的原理对吧,TSL1401 线性 CCD 传感器包含 128 个线性排列的光电二极管。每个光电二极管都有各自的积分电路,以下我们将此电路统称为像素。每个像素所采集的图像灰度值与它所感知的光强和积分时间成正比。所以我们的ccd摄像头能否提取清晰好看的黑线跟光强和积分时间有着很大的关系,那么这 128 个像素是怎么进行采集并输出的呢,这就用到了 SI 和 CLK 信号。可以简单的了解它们的功能。在 128 个像素之外,还有一个开关逻辑控制和移位寄存器电路。SI 通过该电路,控制每一个像素的积分和复位操作;CLK 通过该电路控制每一个像素电压的值依次输出,每个像素的值可以在 CLK 的下降沿时从 AO 采集。
下面来看看采集的代码:
void RD_TSL(void) //CCD采样函数 { u8 i=0,tslp=0; TSL_CLK=1; TSL_SI=0; Dly_us(); TSL_SI=1; TSL_CLK=0; Dly_us(); TSL_CLK=1; TSL_SI=0; Dly_us(); for(i=0;i<128;i++) { TSL_CLK=0; //将CLK置低电平 Dly_us(); ADV[tslp]=(Get_Adc(7)>>4);//将获取到的电压值存储在一个128元素的数组中 ++tslp; TSL_CLK=1; //再将CLK拉高,形成上升沿 Dly_us(); } } }
很明显,这段代码是CCD采样函数的一的流程,简要说明下,CLK是一个上升沿触发的信号,采样一次数据的时候,首先把CLK拉高,这时候AO上已经出现了第1个(这里数数的方式是正常人数法不是程序员数法)像素的模拟电压,然后把SI拉低否则积分复位电路工作可能不正常,连续输入128个CLK(指的是上升沿)以后,128个模拟电压已经传输完毕,
此时注意:必须再次输入至少一个CLK脉冲(第129个脉冲),否则采样电容的开关无法回到正常采样位置,下次的数据将出现问题。
如果还不是很清楚的请看看下面的时序图:
至于曝光时间我们这里就不做深入研究了,应为我们只是做一个简单的循迹,我想大家也已经明白的线性ccd摄像头的工作原理和采样原理了吧!那么接下来我们就来探讨一下如何得到循迹算法呢!
接下来我们就进入这段代码:
void CCD(void) { int i,j,Find_Left = 0; static u8 Mid_Cnt = 0,Find_Cnt = 0; extern u8 Flag_Mid; extern int MID; usart1_send(0xff); for(i=0; i<10;i++) { RD_TSL(); Flag_Mid = 0; Mid_Cnt=0; Find_Left = 0; j=0; for(j=0;j<128;j++)//利用串口发送数据到上位机 { if(ADV[j] ==0XFF) ADV[j]--; usart1_send(ADV[j]); } for(j=1;j<127;j++) { if((ADV[j] < Value) && Flag_Mid) { if((ADV[j+1] < Value) && (ADV[j] < Value) && (ADV[j-1] >= Value)) //找出黑线左边缘 { Find_Left = j; Find_Cnt = 1; } if(Find_Cnt) { Find_Cnt++; if(Find_Cnt >=4) break; } } else if(Mid_Cnt <= 10 && ADV[j] > Value)//识别白色区域 { Mid_Cnt++; Flag_Mid = 1; } else if(Mid_Cnt <= 10) { Mid_Cnt = 0; Flag_Mid = 0; } } if(Find_Left != 0) { MID = Find_Left+2;//找到黑线的中点 BufMid[i] = MID; } else { BufMid[i] = BufMid[i-1]; MID = BufMid[i]; } if(MID >=127) MID =127; //限制中线范围 else if(MID <=0) MID = 0; if(MID > (Expect +15))//右转判断条件 { Flag_Qian=0;Flag_Hou=0;Flag_Left=0;Flag_Right=1; } else if(MID < (Expect -15))//左转判断条件 { Flag_Qian=0;Flag_Hou=0;Flag_Left=1;Flag_Right=0; } else { Flag_Qian=0;Flag_Hou=0;Flag_Left=0;Flag_Right=0; } } }
第一次看完这段代码时我的脑袋懵了,完全不知从何下手,看了好长时间总算明白怎么回事,为了大家更好的理解,容我先介绍几个变量:
Value 我们所期望的阀值,就是比阀值大的肯定识别为白色,因此我们设置的值是100,这里的值是电压值(ps为了方便调试,所以经过了放大),定义在了sys.h里面;
Expect 我们所期望的线性ccd摄像头中心线所在的值,也就是128个像素列的中线,就是128/2左右,因此我们设置的值是60;这里的值是计数值,并非电压值,用来记录线性摄像头的像素列中心值,定义在了sys.h里面;
MID 表示实际上的黑线中心的值在线性ccd摄像头眼中的位置值(范围在0-128之间的任意值),这里的值是计数值,并非电压值;
Find_Left 我们的黑线左边缘在线性ccd摄像头眼中的计数值;
接下来我们用分几个点来解释这段代码吧:
(这里面其实最最关键的是怎样找出黑线的左边缘位置)
1.第一个子for 循环
for(j=0;j<128;j++)
{
if(ADV[j] ==0XFF) ADV[j]--;
usart1_send(ADV[j]);
}
这段代码是通过串口将每个像素点传到上位机上面去显示
2.第二个子for循环的第一个if语句
if((ADV[j] < Value) && Flag_Mid)
第一个判断条件ADV[j] < Value是判断是否是黑色区域,Flag_Mid是一个标志位,而Flag_Mid初始化为0,所以不会进入循环,而会进入else判断,
else if(Mid_Cnt <= 10 && ADV[j] > Value)
else判断是识别白色区域
3.上一个if的嵌套if循环
if((ADV[j+1] < Value) && (ADV[j] < Value) && (ADV[j-1] >= Value))
{
Find_Left = j;
Find_Cnt = 1;
}
这个if语句意思为第j个和第j+1个像素点的电压值小于阀值Value,识别为黑色,第j-1个像素点大于阀值Value,识别为白色,因此第j个就是黑线的左临界点,Find_Left = j;,用Find_Left记录下来。
4. MID = Find_Left+2;
因为一般一条黑线大概占用128个像素列中的5到6个像素点,因此Find_Left+2就可以近似看成实际的中线(MID)所在的位置;
5. if(MID > (Expect +15))
{
Flag_Qian=0;Flag_Hou=0;Flag_Left=0;Flag_Right=1;
}
else if(MID < (Expect -15))
{
Flag_Qian=0;Flag_Hou=0;Flag_Left=1;Flag_Right=0;
}
else
{
Flag_Qian=0;Flag_Hou=0;Flag_Left=0;Flag_Right=0;
}
这段是用来判断左转右转的,假设我们现在需要右转,说明右边有黑线,那么实际黑线的中心必然远大于我们所期待的像素点中心,为了方便我们选择用MID < (Expect+15)作为判断条件;同理,假设我们现在需要左转,说明左边有黑线,那么实际黑线的中心必然远小于我们所期待的像素点中心,为了方便我们选择用MID < (Expect-15)作为判断条件。
看完这个,大家可能纳闷,那么控制向前的在哪里呢,大家看完这个就明白了:
原代码为:
Encoder_Least =(Encoder_Left+Encoder_Right)+0;
//===获取最新速度偏差==测量速度(左右编码器之和)-目标速度(此处为零)
我们将目标速度改为60,就像这样:
Encoder_Least =(Encoder_Left+Encoder_Right)+60;
这句代码在control.c中,这样小车就有了一个初始的向前的速度了。
讲完了这些,相信大家对线性ccd循迹有了一个比较清楚的认识了吧,那么大家就开始动手做吧,不需要太长时间,加油哦;
最后老规矩奉上代码打包和视频演示:
视频演示:
视频地址:http://player.youku.com/player.php/sid/XMTM1NjgyNTk4MA==/v.swf
代码打包下载:循迹平衡小车.zip
回复
有奖活动 | |
---|---|
【有奖活动】分享技术经验,兑换京东卡 | |
话不多说,快进群! | |
请大声喊出:我要开发板! | |
【有奖活动】EEPW网站征稿正在进行时,欢迎踊跃投稿啦 | |
奖!发布技术笔记,技术评测贴换取您心仪的礼品 | |
打赏了!打赏了!打赏了! |
打赏帖 | |
---|---|
与电子爱好者谈读图二被打赏50分 | |
【FRDM-MCXN947评测】Core1适配运行FreeRtos被打赏50分 | |
【FRDM-MCXN947评测】双核调试被打赏50分 | |
【CPKCORRA8D1B评测】---移植CoreMark被打赏50分 | |
【CPKCORRA8D1B评测】---打开硬件定时器被打赏50分 | |
【FRDM-MCXA156评测】4、CAN loopback模式测试被打赏50分 | |
【CPKcorRA8D1评测】--搭建初始环境被打赏50分 | |
【FRDM-MCXA156评测】3、使用FlexIO模拟UART被打赏50分 | |
【FRDM-MCXA156评测】2、rt-thread MCXA156 BSP制作被打赏50分 | |
【FRDM-MCXN947评测】核间通信MUTEX被打赏50分 |