红外循迹外观:
红外循迹原理:
红外循迹模块原理还是很简单的,和许多光电传感器原理一样,当发射器发射出去的光被接收器接收到后,模块上对应的LED灯点亮,此时相应的输出引脚输出低电平;如果发射器发射的光没有被接收器接收到的话,那么LED灯不点亮,此时相应的输出引脚输出高电平。
PWM调速:
这里必须要有PWM来控制小车的速度,不然的话太快容易脱离轨道,具体多少速度还是需要看硬件和软件的配合,需要多次实验试错。PWM可以参考我前面写的关于舵机的代码,原理是一样的,只需要加些变量即可使用。
电机驱动模块:
这里就不再多说,一般都是拿L298N驱动模块来做小车,可以一个驱动模块控制四个轮子,也可以两个控制四个,具体还是需要看硬件是否被满足。
程序设计:
在程序设计过程中,最难的还是走直线的情况下,因为小车如果速度太快的话,会直接脱离轨道,太慢的话,玩的时候感觉又有点不得劲,所以要尽力写出最高效的程序还是有点挑战的。如果循迹模块检测到低电平,就直走,检测到高电平,因为我刚开始是在前面装了两个,但是后来觉得走直线的时候太快,冲出了轨迹,所以后来又加了一个,放在了前一排的后面。
这里注意一下,此代码只是往右旋转的,不支持左转,它会一直右转右转。
代码:
代码有两个,前一个是走椭圆的,后一个是走直角的,有点差别,但差别不大,注意区分。
前一个;:
#include <reg52.h> //此文件中定义了单片机的一些特殊功能寄存器
typedef unsigned int u16; //对数据类型进行声明定义
typedef unsigned char u8;
sbit leftA=P2^1;
sbit leftB=P2^2;
sbit leftC=P2^3;
sbit leftD=P2^4;
sbit rightA = P1^4;
sbit rightB = P1^3;
sbit rightC = P1^2;
sbit rightD = P1^1;
sbit Left_moto_pwmA=P2^0 ;
sbit Left_moto_pwmB=P2^5 ;
sbit Right_moto_pwmA=P1^0;
sbit Right_moto_pwmB=P1^6;
bit Left_moto_stop =1;
bit Right_moto_stop =1;
extern unsigned char pwm_val_left =0; //脉冲变量
unsigned char push_val_left =0;
extern unsigned char pwm_val_right =0; //脉冲变量
unsigned char push_val_right=0;
//调节速度
unsigned char Left_Speed_Ratio;
unsigned char Right_Speed_Ratio;
#define Right_1_led P3^2 //四路寻迹模块接口第一路
#define Left_2_led P3^3 //四路寻迹模块接口第二路
void delay(u16 n);
void delay(u16 n) //1ms (误差 -0.651041666667us)
{
u16 i ;
unsigned char a,b;
for(i=1;i<=n;i++)
{
for(b=102;b>0;b--)
for(a=3;a>0;a--);
}
}
void pwm_out_left_moto(void) //左电机调速
{
if(pwm_val_left>=10) pwm_val_left=0;
if(Left_moto_stop)
{
if(pwm_val_left<=push_val_left)
{
Left_moto_pwmA=1;
Left_moto_pwmB=1;
}
else
{
Left_moto_pwmA=0;
Left_moto_pwmB=0;
}
}
else
{
Left_moto_pwmA=0;
Left_moto_pwmA=0;
}
}
void pwm_out_right_moto(void) //右电机调速
{
if(pwm_val_right>=10) pwm_val_right=0;
if(Right_moto_stop)
{
if(pwm_val_right<=push_val_right)
{
Right_moto_pwmA=1;
Right_moto_pwmB=1;
}
else
{
Right_moto_pwmA=0;
Right_moto_pwmB=0;
}
}
else
{
Right_moto_pwmA=0;
Right_moto_pwmB=0;
}
}
//pwm中断
void Timer0Init() //定时器初始化函数
{
TMOD|=0X01;//选择为定时器0模式,工作方式1,仅用TR0打开启动。
TH0=0XFC; //给定时器赋初值,定时1ms
TL0=0X18;
ET0=1;//打开定时器0中断允许
EA=1;//打开总中断
TR0=1;//打开定时器
}
void timer0()interrupt 1 //定时器中断函数,此处配置为1ms产生一次中断,对PWM的输出进行控制
{
TH0=0XFC; //给定时器赋初值,定时1ms
TL0=0X18;
//time++;
pwm_val_left++;
pwm_val_right++;
pwm_out_left_moto();
pwm_out_right_moto();
}
void stop()
{
leftA=0;
leftB=0;
leftC=0;
leftD=0;
rightA=0;
rightB=0;
rightC=0;
rightD=0;
}
void forward() //前进
{
push_val_left =Left_Speed_Ratio;
push_val_right =Right_Speed_Ratio;
leftA=1;
leftB=0;
leftC=1;
leftD=0;
rightA=1;
rightB=0;
rightC=1;
rightD=0;
}
void back() //后退
{
push_val_left =Left_Speed_Ratio;
push_val_right =Right_Speed_Ratio;
leftA=0;
leftB=1;
leftC=0;
leftD=1;
rightA=0;
rightB=1;
rightC=0;
rightD=1;
}
void left() //左转
{
push_val_left =Left_Speed_Ratio;
push_val_right =Right_Speed_Ratio;
leftA=0;
leftB=1;
leftC=0;
leftD=1;
rightA=0;
rightB=0;
rightC=0;
rightD=0;
}
void right() //右转
{
push_val_left =Left_Speed_Ratio;
push_val_right =Right_Speed_Ratio;
leftA=0;
leftB=0;
leftC=0;
leftD=0;
rightA=0;
rightB=1;
rightC=0;
rightD=1;
}
void main()
{
Timer0Init();
while(1)
{
switch(P3&0x0f)
{
//说明一下,速度很关键,开始的时候尽量慢一点。
//我写的时候,右转把左边的轮子停住了,左边的轮子是不动的,而右边的轮子是加速的。
//注意两个传感器检测到都是往右转的。
case 0x03: Left_Speed_Ratio=2;Right_Speed_Ratio=2;forward();break;//
case 0x07: Left_Speed_Ratio=0; Right_Speed_Ratio=3;right();delay(10);break;
case 0x0b: Left_Speed_Ratio=0; Right_Speed_Ratio=3;right();delay(7);break;
case 0x0f: Left_Speed_Ratio=0; Right_Speed_Ratio=3;right();delay(8);break;
default : break;
}
}
}
后一个:
#include <reg52.h> //此文件中定义了单片机的一些特殊功能寄存器
typedef unsigned int u16; //对数据类型进行声明定义
typedef unsigned char u8;
sbit leftA=P2^1;
sbit leftB=P2^2;
sbit leftC=P2^3;
sbit leftD=P2^4;
sbit Left_moto_pwmA=P2^0 ;
sbit Left_moto_pwmB=P2^5 ;
sbit Right_moto_pwmA=P1^0;
sbit Right_moto_pwmB=P1^6;
sbit rightA = P1^4;
sbit rightB = P1^3;
sbit rightC = P1^2;
sbit rightD = P1^1;
bit Left_moto_stop =1;
bit Right_moto_stop =1;
extern unsigned char pwm_val_left =0; //脉冲变量
unsigned char push_val_left =0;
extern unsigned char pwm_val_right =0; //脉冲变量
unsigned char push_val_right=0;
//调节速度
unsigned char Left_Speed_Ratio;
unsigned char Right_Speed_Ratio;
#define Right_1_led P3^2 //四路寻迹模块接口第一路
#define Left_1_led P3^3 //四路寻迹模块接口第二路
#define Right_2_led P3^4 //四路寻迹模块接口第一路
void delay(u16 n);
void delay(u16 n) //1ms (误差 -0.651041666667us)
{
u16 i ;
unsigned char a,b;
for(i=1;i<=n;i++)
{
for(b=102;b>0;b--)
for(a=3;a>0;a--);
}
}
void pwm_out_left_moto(void) //左电机调速
{
if(pwm_val_left>=10) pwm_val_left=0;
if(Left_moto_stop)
{
if(pwm_val_left<=push_val_left)
{
Left_moto_pwmA=1;
Left_moto_pwmB=1;
}
else
{
Left_moto_pwmA=0;
Left_moto_pwmB=0;
}
}
else
{
Left_moto_pwmA=0;
Left_moto_pwmA=0;
}
}
void pwm_out_right_moto(void) //右电机调速
{
if(pwm_val_right>=10) pwm_val_right=0;
if(Right_moto_stop)
{
if(pwm_val_right<=push_val_right)
{
Right_moto_pwmA=1;
Right_moto_pwmB=1;
}
else
{
Right_moto_pwmA=0;
Right_moto_pwmB=0;
}
}
else
{
Right_moto_pwmA=0;
Right_moto_pwmB=0;
}
}
//pwm中断
void Timer0Init() //定时器初始化函数
{
TMOD|=0X01;//选择为定时器0模式,工作方式1,仅用TR0打开启动。
TH0=0XFC; //给定时器赋初值,定时1ms
TL0=0X18;
ET0=1;//打开定时器0中断允许
EA=1;//打开总中断
TR0=1;//打开定时器
}
void timer0()interrupt 1 //定时器中断函数,此处配置为1ms产生一次中断,对PWM的输出进行控制
{
TH0=0XFC; //给定时器赋初值,定时1ms
TL0=0X18;
//time++;
pwm_val_left++;
pwm_val_right++;
pwm_out_left_moto();
pwm_out_right_moto();
}
void stop()
{
leftA=0;
leftB=0;
leftC=0;
leftD=0;
rightA=0;
rightB=0;
rightC=0;
rightD=0;
}
void forward() //前进
{
push_val_left =Left_Speed_Ratio;
push_val_right =Right_Speed_Ratio;
leftA=1;
leftB=0;
leftC=1;
leftD=0;
rightA=1;
rightB=0;
rightC=1;
rightD=0;
}
void back() //后退
{
push_val_left =Left_Speed_Ratio;
push_val_right =Right_Speed_Ratio;
leftA=0;
leftB=1;
leftC=0;
leftD=1;
rightA=0;
rightB=1;
rightC=0;
rightD=1;
}
void left() //左转
{
push_val_left =Left_Speed_Ratio;
push_val_right =Right_Speed_Ratio;
leftA=0;
leftB=1;
leftC=0;
leftD=1;
rightA=0;
rightB=0;
rightC=0;
rightD=0;
}
void right() //右转
{
push_val_left =Left_Speed_Ratio;
push_val_right =Right_Speed_Ratio;
leftA=1;
leftB=0;
leftC=1;
leftD=0;
rightA=0;
rightB=1;
rightC=0;
rightD=1;
}
void main()
{
Timer0Init();
while(1)
{
switch(P3&0xff)
{
case 0xe3:Left_Speed_Ratio=2;Right_Speed_Ratio=2;forward();break;
case 0xe7:back();delay(8);Left_Speed_Ratio=0;Right_Speed_Ratio=3;right();delay(10);break; case 0xeb:back();delay(8);Left_Speed_Ratio=0;Right_Speed_Ratio=3;right();delay(8);break;
case 0xef:back();delay(8);Left_Speed_Ratio=0;Right_Speed_Ratio=3;right();delay(7);break;
case 0xf3:back();delay(8);Left_Speed_Ratio=2;Right_Speed_Ratio=2;forward();break;//部没
case 0xf7:back();delay(8);Left_Speed_Ratio=0;Right_Speed_Ratio=3;right();delay(10);break;
case 0xfb:back();delay(8);Left_Speed_Ratio=0;Right_Speed_Ratio=3;right();delay(8);break;
case 0xff:back();delay(8);Left_Speed_Ratio=0;Right_Speed_Ratio=3;right();delay(7);break;
case 0xdb:back();delay(350);Left_Speed_Ratio=0;Right_Speed_Ratio=4;right();delay(10);break;
case 0xdf:back();delay(450);Left_Speed_Ratio=0;Right_Speed_Ratio=4;right();delay(8);break
case 0xd7:back();delay(450);Left_Speed_Ratio=0;Right_Speed_Ratio=4;right();delay(7);break;
}
}
}
总结:
写程序的过程和调试代码的过程是很辛苦的,需要坚持住,坚持就是胜利。