小车循迹原理2009-07-11 12:401.小车控制及驱动单元的选择此部分是整个小车的大脑,是整个小车运行的核心部件,起着控制小车所有运行状态的作用。
通常选用单片机作为小车的核心控制单元,本文以台湾凌阳公司的SPCE061A单片机为例予以介绍。
SPCE061是一款拥有2K RAM、32KFlash、32 个I/O 口,并集成了AD/DA功能强大的16位微处理器,它还拥有丰富的语音处理功能,为小车的功能扩展提供了相当大的空间。
只要按照该单片机的要求对其编制程序就可以实现很多不同的功能。
小车驱动电机一般利用现成的玩具小车上的配套直流电机。
考虑到小车必须能够前进、倒退、停止,并能灵活转向,在左右两轮各装一个电机分别进行驱动。
当左轮电机转速高于右轮电机转速时小车向右转,反之则向左转。
为了能控制车轮的转速,可以采取PWM调速法,即由单片机的IOB8、IOB9输出一系列频率固定的方波,再通过功率放大来驱动电机,在单片机中编程改变输出方波的占空比就可以改变加到电机上的平均电压,从而可以改变电机的转速。
左右轮两个电机转速的配合就可以实现小车的前进、倒退、转弯等功能。
2.小车循迹的原理这里的循迹是指小车在白色地板上循黑线行走,通常采取的方法是红外探测法。
红外探测法,即利用红外线在不同颜色的物体表面具有不同的反射性质的特点,在小车行驶过程中不断地向地面发射红外光,当红外光遇到白色纸质地板时发生漫反射,反射光被装在小车上的接收管接收;如果遇到黑线则红外光被吸收,小车上的接收管接收不到红外光。
单片机就是否收到反射回来的红外光为依据来确定黑线的位置和小车的行走路线。
红外探测器探测距离有限,一般最大不应超过15cm。
对于发射和接收红外线的红外探头,可以自己制作或直接采用集成式红外探头。
(1)自制红外探头电路如图1所示,红外光的发送接收选用型号为ST168的对管。
当小车在白色地面行驶时,装在车下的红外发射管发射红外线信号,经白色反射后,被接收管接收,一旦接收管接收到信号,那么图中光敏三极管将导通,比较器输出为低电平;当小车行驶到黑色引导线时,红外线信号被黑色吸收后,光敏三极管截止,比较器输出高电平,从而实现了通过红外线检测信号的功能。
将检测到的信号送到单片机I/O口,当I/O口检测到的信号为高电平时,表明红外光被地上的黑色引导线吸收了,表明小车处在黑色的引导线上;同理,当I/O口检测到的信号为低电平时,表明小车行驶在白色地面上。
此种方法简单,价格便宜,灵敏度可调,但是容易受到周围环境的影响,特别是在图1较强的日光灯下,对检测到的信号有一定的影响。
(2)集成式红外探头可以采用型号为E3F-DS10C4集成断续式光电开关探测器,它具有简单、可靠的工作性能,只要调节探头上的一个旋钮就可以控制探头的灵敏度。
该探头输出端只有三根线(电源线、地线、信号线),只要将信号线接在单片机的I/O 口,然后不停地对该I/O口进行扫描检测,当其为高电平时则检测到白纸,当为低电平时则检测到黑线。
此种探头还能有效地防止普通光源(如日光灯等)的干扰。
其缺点则是体积比较大,占用了小车有限的空间。
3.红外探头的安装在小车具体的循迹行走过程中,为了能精确测定黑线位置并确定小车行走的方向,需要同时在底盘装设4个红外探测头,进行两级方向纠正控制,提高其循迹的可靠性。
这4个红外探头的具体位置如图2所示。
图中循迹传感器共安装4个,全部在一条直线上。
其中InfraredMR与InfraredML 为第一级方向控制传感器,InfraredSR 与InfraredSL 为第二级方向控制传感器。
小车行走时,始终保持黑线(如图2 中所示的行走轨迹黑线)在InfraredMR和InfraredML这两个第一级传感器之间,当小车偏离黑线时,第一级探测器一旦探测到有黑线,单片机就会按照预先编定的程序发送指令给小车的控制系统,控制系统再对小车路径予以纠正。
若小车回到了轨道上,即4个探测器都只检测到白纸,则小车会继续行走;若小车由于惯性过大依旧偏离轨道,越出了第一级两个探测器的探测范围,这时第二级动作,再次对小车的运动进行纠正,使之回到正确轨道上去。
可以看出,第二级方向探测器实际是第一级的后备保护,从而提高了小车循迹的可靠性。
4.软件控制其程序控制框图如图3。
小车进入循迹模式后,即开始不停地扫描与探测器连接的单片机I/O口,一旦检测到某个I/O或者InfraredSL(左面第二级传感器)探测到黑线,即小车左半部分压到黑线,车身向右偏出,此时应使小车向左转;如果是InfraredMR(右面第一级传感器)或InfraredSR(右面第二级传感器)探测到了黑线,即车身右半部压住黑线,小车向左偏出了轨迹,则应使小车向右转。
在经过了方向调整后,小车再继续向前行走,并继续探测黑线重复上述动作。
由于第二级方向控制为第一级的后备,则两个等级间的转向力度必须相互配合。
第二级通常是在超出第一级的控制范围的情况下发生作用,它也是最后一层保护,所以它必须要保证小车回到正确轨迹上来,则通常使第二级转向力度大于第一级,即level2>level1(level1、level2为小车转向力度,其大小通过改变单片机输出的占空比的大小来改变),具体数值在实地实验中得到。
专家点评:根据本文所讲述的方法,我们可以较容易地做出按照一定轨迹行走的智能电动小车。
但是按照该方法行走的小车如果是走直线,有可能会是蛇形前进。
为了使小车能够按轨迹行走的更流畅,可以在软件编程时运用一些简单的算法。
例如,在对小车进行纠偏时,适当提前停止纠偏,而不要等到小车完全不偏时再停止,以防止小车的过冲。
电源电路:由于本系统需要电池供电,我们考虑了如下几种方案为系统供电。
方案1:采用10节1.5V干电池供电,电压达到15V,经7812稳压后给支流电机供电,然后将12V电压再次降压、稳压后给单片机系统和其他芯片供电。
但干电池电量有限,使用大量的干电池给系统调试带来很大的不便,因此,我们放弃了这种方案。
方案2:采用3节4.2V可充电式锂电池串联共12.6V给直流电机供电,经过7812的电压变换后给支流电机供电,然后将12V 电压再次降压、稳压后给单片机系统和其他芯片供电。
锂电池的电量比较足,并且可以充电,重复利用,因此,这种方案比较可行。
但锂电池的价格过于昂贵,使用锂电池会大大超出我们的预算,因此,我们放弃了这种方案。
方案3:采用12V蓄电池为直流电机供电,将12V电压降压、稳压后给单片机系统和其他芯片供电。
蓄电池具有较强的电流驱动能力以及稳定的电压输出性能。
虽然蓄电池的体积过于庞大,在小型电动车上使用极为不方便,但由于我们的车体设计时留出了足够的空间,并且蓄电池的价格比较低。
因此我们选择了此方案。
综上考虑,我们选择了方案3。
这个黑呼呼的东西,让我们可爱的小车变得很难看。
稳压模块:方案1:采用两片7812将电压稳压至12V后给直流电机供电,然后采用一片7809将电压稳定至9V,最后经7805将电压稳至5V,给单片机系统和其他芯片供电,但7809和7805压降过大,使7809和7805消耗的功率过大,导致7809和7805发热量过大,因此,我们放弃了这种方案。
方案2:采用两片7812将电压稳压至12V后给直流电机供电,然后采用2576将电压稳至5V。
2576的输出电流最大可至3A,完全满足系统要求。
综上考虑,我们选择了方案2。
稳压模块如下图:(左图:稳压模块)循迹传感器模块方案1:用光敏电阻组成光敏探测器。
光敏电阻的阻值可以跟随周围环境光线的变化而变化。
当光线照射到白线上面时,光线发射强烈,光线照射到黑线上面时,光线发射较弱。
因此光敏电阻在白线和黑线上方时,阻值会发生明显的变化。
将阻值的变化值经过比较器就可以输出高低电平。
但是这种方案受光照影响很大,不能够稳定的工作。
因此我们考虑其他更加稳定的方案。
方案2:用红外发射管和接收管自己制作光电对管寻迹传感器。
红外发射管发出红外线,当发出的红外线照射到白色的平面后反射,若红外接收管能接收到反射回的光线则检测出白线继而输出低电平,若接收不到发射管发出的光线则检测出黑线继而输出高电平。
这样自己制作组装的寻迹传感器基本能够满足要求,但是工作不够稳定,且容易受外界光线的影响,因此我们放弃了这个方案。
方案3:用RPR220型光电对管。
RPR220是一种一体化反射型光电探测器,其发射器是一个砷化镓红外发光二极管,而接收器是一个高灵敏度,硅平面光电三极管。
RPR220采用DIP4封装,其具有如下特点:•塑料透镜可以提高灵敏度。
•内置可见光过滤器能减小离散光的影响。
•体积小,结构紧凑。
•当发光二极管发出的光反射回来时,三极管导通输出低电平。
此光电对管调理电路简单,工作性能稳定。
此我们选择了方案3。
安装后效果如下图:下图是电压比较器:电设小车循迹模块2007-10-14 14:06//包含所需头文件#include <ioM16v.h>#include <macros.h>#include"time1_init.h"#include"motor.h"#define ahead 1#define backwards 0#define compare(x,y) (x<y?1:0)#define mid 0X17/*****************************初始化函数********************************///端口初始化void port_init(void){PORTA = 0x00;DDRA = 0x00;PORTB = 0x00;DDRB = 0x08;PORTC = 0x00;DDRC = 0x00;PORTD = 0x00;DDRD = 0x00;}void timer0_init(void)TCCR0 = 0x00;//停止定时器TCNT0 = 0x00;//初始值OCR0 = 0x17;//匹配值TIMSK |= 0x00;//中断允许TCCR0 = 0x7D;//启动定时器}void adc_init(void){//adc转换初始化ADCSRA = 0x00; //禁止AD转换ADCSRA|=BIT(ADIF);ADMUX=0X46;SFIOR |= 0x00;ACSR = 0x80; //禁止模拟比较器ADCSRA = 0xE7;}void init_devices(void){CLI(); //禁止所有中断MCUCR = 0x00;MCUCSR = 0x80;//禁止JTAGGICR = 0x00;port_init();timer0_init();timer1_init();adc_init();SEI();//开全局中断}/**********************************************************************//*****选择前端传感器用uchar start_head_sensor(void)*****************************选择后端传感器用uchar start_back_sensor(void)/*****使用角度传感器用uint cord_sensor(void)********************************************/uintsensor_head[3],sensor_back[3],cord; //存储6个传感器AD转换的值uchar offset ; //黑线偏移小车中心轴的距离uint sensor_compare_head[3]={300,300,300},sensor_compare_back[3]={300,300,300}; //判断黑线是否位于传感器下的阈值uchar start_head_sensor(void){uchar i,j=0,sum=0;ADMUX=0X40;ADCSRA=0xC7;while(ADCSRA&BIT(ADSC));for(i=0;i<3;i++){ADMUX=0X40+i; //启用前端传感器0,1,2通道ADCSRA=0xC7;while(ADCSRA&BIT(ADSC));sensor_head[i]=ADC;}for(i=3;i;i--){if(compare(sensor_head[i-1],sensor_compare_head[i-1])){sum+=i-1;j++;}if(j)offset=sum*2/j;ADMUX=0X46;ADCSRA=0xE7;return offset;}uchar start_back_sensor(void){uchar i,j=0,sum=0;ADMUX=0X43;ADCSRA=0xC7;while(ADCSRA&BIT(ADSC));for(i=0;i<3;i++){ADMUX=0X43+i; //启用前端传感器0,1,2通道ADCSRA=0xC7;while(ADCSRA&BIT(ADSC));sensor_back[i]=ADC;}for(i=3;i;i--){if(compare(sensor_back[i-1],sensor_compare_back[i-1])){sum+=i-1;j++;}}if(j)offset=sum*2/j;ADMUX=0X46;ADCSRA=0XE7;return offset;}//角度传感器滤波函数uint cord_sensor(void){uchar i;uint max=0,min=1023,sum=0;for(i=0;i<5;i++){ADCSRA|=BIT(ADIF);while(!(ADCSRA&BIT(ADIF)));cord=ADC;sum+=cord;max=(max>cord)?max:cord;min=(min<cord)?min:cord;}return (sum-max-min)/3;}void direc_ctrl(uchar x,uchar y){if(y){if(x==0)OCR0=mid+3;if(x==4)OCR0=mid-3;if(x==2) OCR0=mid;}else OCR0=mid+x-2;}void menmber_path(void){uint max_head[3]={0,0,0},min_head[3]={1023,1023,1023},max_back[3]={0,0,0},min_back[3]={1023,1023,1023};for(i=4000;i;i--){start_head_sensor();for(j=0;j<3;j++){max_head[j]=(max_head[j]>sensor_head[j])?max_head[j]:sensor_head[j];min_head[j]= (min_head[j]<sensor_head[j])?min_head[j]:sensor_head[j];}start_back_sensor();for(j=0;j<3;j++){max_back[j]=(max_back[j]>sensor_back[j])?max_back[j]:sensor_back[j];min_back[j]= (min_back[j]<sensor_back[j])?min_back[j]:sensor_back[j];}}for(j=0;j<3;j++){sensor_compare_head[j]=(max_head[j]+min_head[j])/2;sensor_compare_back[j]=(max_back[j]+min_back[j])/2;}}/***********************前端同时检测到黑线判断函数****************************************/uchar head_sensor_all(void){start_head_sensor();if( compare(sensor_head[0], sensor_compare_head[0]) && compare(sensor_head[1], sensor_compare_head[1]) && compare(sensor_head[2], sensor_compare_head[2]))return 1;elsereturn 0;}uchar back_sensor_all(void){start_back_sensor();if( compare(sensor_back[0], sensor_compare_back[0]-30) && compare(sensor_back[1],sensor_compare_back[1]-30) && compare(sensor_back[2], sensor_compare_back[2]-30))return 1;elsereturn 0;}/**********************前端循迹函数**************************************************/void search_path_ahead(uchar speed){motor_autorun(ahead,speed);while(1){if(head_sensor_all()){motor_stop();return;}else{direc_ctrl(offset,1);}}}/**********************后端循迹函数*******************************************/void search_path_backward(uchar speed)motor_autorun(0,speed);while(1){if(back_sensor_all()){motor_stop();return;}elsedirec_ctrl(offset,0);}}#include<reg51.h>#define TH0_TL0 (65536-1000)//设定中断的间隔时长unsigned char count0 = 0;unsigned char count1 = 0;bit Flag = 1;//电机正反转标志位,1正转,0反转sbit Key_add=P1 ^ 4; //电机减速sbit Key_dec=P1 ^ 5; //电机加速sbit Key_turn=P1 ^ 6; //电机换向sbit PWM1=P3^6;//PWM 通道 1sbit PWM2=P3^7;//PWM 通道 2unsigned char Time_delay;//函数声明void Delay(unsigned char x);void Motor_speed_high(void);void Motor_speed_low(void);void Motor_turn(void);void Timer0_init(void);/******************************************************************************/ void Delay(unsigned char x)//延时处理{Time_delay = x;while(Time_delay != 0);}/******************************************************************************/ void Timer0_int(void) interrupt 1 using 1//定时0中断处理{TR0 = 0;TL0 += (TH0_TL0 + 9) % 256;TH0 += (TH0_TL0 + 9) / 256 + (char)CY;TR0 = 1;if(Time_delay != 0)//延时函数用{Time_delay--;}if(Flag == 1)//电机正转{PWM1 = 0;if(++count1 < count0){PWM2 = 1;}elsePWM2 = 0;if(count1 >= 100){count1=0;}}else //电机反转{PWM2 = 0;if(++count1 < count0){PWM1 = 1;}elsePWM1 = 0;if(count1 >= 100){count1=0;}}//反转}/******************************************************************************/{if(Key_add==0){Delay(10);if(Key_add==0){count0 += 5;if(count0 >= 100){count0 = 100;}}while(Key_add == 0);//等待键松开}}/******************************************************************************/ void Motor_speed_low(void)//按键处理减pwm占空比,电机减速{if(Key_dec==0){Delay(10);if(Key_dec==0){count0 -= 5;if(count0 <= 0){count0 = 0;}}while(Key_dec == 0);}}/******************************************************************************/ void Motor_turn(void)//电机正反向控制{if(Key_turn == 0){Delay(10);if(Key_turn == 0){Flag = ~Flag;}while(Key_turn == 0);}}/******************************************************************************/ void Timer0_init(void)//定时器0初始化{TMOD=0x01;TH0=TH0_TL0 / 256;TL0=TH0_TL0 % 256;TR0=1;ET0=1;EA=1;}/******************************************************************************/ void main(void)//主函数{Timer0_init();while(1){Motor_turn();Motor_speed_high();Motor_speed_low();}}上面的太复杂,我来一个://PWM控制LED的亮度,渐灭到渐亮#include<at89x51.h>#define uchar unsigned char;/*************公阴数码管显示数据***************************//*********a_.0 b_.1 c_.2 d_.4 e_.5 f_.6 h_.5 *********/unsigned char leddata[]={0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90,};void mdelay(unsigned int m); ///m*1ms的延时子程序void disp( unsigned char i ,num); ///显示子函数,在第i个数码管显示内容是num void xianshi(void);////显示主函数。