51小车程序
- 格式:doc
- 大小:66.50 KB
- 文档页数:8
51小车作为一种入门小车,其基本原理与飞思卡尔比赛的小车相似,共分为硬件和软件两个部分。其中软件部分可分为传感器数据采集,舵机控制,速度控制等,这里现在对51小车的软件进行一个详细的介绍
1.程序思路概述:
首先51单片机需要通过红外传感器监测道路信息,然后将传感器得到的数据进行转换。最后将转换后的数据通过某种控制算法,计算出舵机和电机的控制量,使小车能够沿着跑道行驶。主体控制流程可以表示为如下图形:
2.程序算法
2.1数据采集
小车传感器为8个红外对管,经过电压比较器后,每个红外对管都有0和1两种状态。因此,8个红外对管产生的数据刚好为1个字节。因此程序有两种方式将这8位数据读入:1、可以将I/O口数据当做一个字节,直接赋给一个字符变量;2、将8位数据按位读取,分别赋给字符变量的每一位。我们的示例程序中采用的就是第二种方法。
2.2数据处理
由于读入的数据并不方便直接参与控制计算,因此先将该数据集分成16类,分别对应于小车不同的位置信息,由-7~+7表示,其中+7表示引导线位于小车最左侧,0表示引导线位于小车中部,-7表示引导线位于小车最右侧,8表示未检测到引导线或其他错误情况。
2.3控制算法
上面的转换后的数据可以作为控制计算的输入,即小车与赛道的偏差信息。方向的控制算法可以采用位置型PD算法。
其中P为比例环节,简单的说就是根据当前小车与赛道的偏差直接进行控制,偏离多少就转多少,偏离越多,舵机转向越多。可以通过调节比例系数改变转向的幅度。系数越大,转向幅度越大,转向越快。
D称为微分环节,这里的微分信息指上面偏差信息的微分,可以简单理解为小车偏离赛道的速率。可以简单的将本次的偏差信息与上一次偏差的差值算作微分项。微分项的控制方法是说,小车偏离跑道越快,方向就要转的越多,反之则转向越小。因此当小车逐渐靠近跑道时,微分项可以使小车偏转量减小,使小车不至于偏向另一边。可以通过改变微分项的系数来改变这种调节作用的程度。一般来讲,微分系数越大,小车对于弯道的响应就越灵敏,行驶时也更稳定。
由于小车在不同的位置时,可能需要不同的比例系数或微分系数,因此可以考虑在不同的情况下,采用不同的系数进行运算。示例程序中采用的就是这种方法。
速度控制也可以采用PD算法进行控制。为了使程序简单化,示例程序中采用了匀速的控制方式。想要提高成绩的同学可以自行加入速度控制程序
2.4生成PWM
由于51单片机没有PWM模块,因此需要通过通用I/O口进行模拟来输出舵机和电机所需的PWM波。
可以分别使用一个定时器来作为一路PWM波的计时器。先将I/O口置位,通过高电平时间确定好定时器的初值,当定时器产生中断时,再将I/O口清零,并设定低电平时间,由此循环即可产生PWM波。其中,高电平时间由上面的控制计算得出,低电平时间由PWM周期减去高电平时间得到。
鲫鱼电子工作室开发的51智能车是一种入门型智能小车,基本原理和参加飞思卡尔比赛的小车相似,但是其制作要比正式比赛的小车简单。目的是为了让大家更快地进入飞思卡尔小车的制作,对智能小车有个更好的理解。
51小车分为硬件和软件两个部分,其中硬件又分为三个主要模块,51核心板模块,传感器模块,扩展板模块。软件部分分为传感器数据采集,舵机控制,速度控制等。
51核心板
51核心板包括几个小的部分,下载部分,单片机最小系统部分,led用户灯,通用IO接口。这次开发的51核心板的主控芯片为STC,选择这块芯片的一个重要原因是他的下载调试方便,可以通过串口直接下载,同时又有51单片机的所有资源。
下载部分主要使用了一片PL2303,它是一片USB转串口功能的芯片,由于现在大部分计算机都不包括串口,但每台计算机都有USB口,于是我们想是否可以通过USB下载,最后在网上找到了一片USB转串口的芯片,这样我们下载程序将更简单,不需要再单独购买51下载器,极大地方便了同学们的调试。
下载部分具体原理图如下:
#include //包括一个52 标准内核的头文件
#include
/*****************定义数据类型(方便移植)*****************/
typedef unsigned char UINT8;
typedef char SINT8;
typedef unsigned int UINT16;
typedef int SINT16;
/**********************************************************/
/*****************定义布尔值(增强程序可读性)*************/
#define HIGH_LEVEL 1
#define LOW_LEVEL 0
/***********************************************************/
/**********定义相关常数(移植于不同的小车时,需根据实际情况改变该常数***********/
#define SERVOMID 1270 //舵机中值
#define SERVOMOSTLEFT 1680 //舵机左值
#define SERVOMOSTRIGHT 870 //舵机右值 #define SERVO_PERIOD 20000 //舵机PWM 周期:20ms,晶振12M 公式:计数值=定时时间*晶振
//频率/12,如20ms: 计数值=0.02 s * 12 000 000 Hz/12 = 20000
#define MOTOR_PERIOD 10000 //电机PWM 周期:10ms,晶振12M
/**************************************************************/
/*****************输出口定义*************************/
sbit SevorPort = P0^1;
sbit MotorPort = P0^6;
sbit P20 = P2^0;
sbit P21 = P2^1;
sbit P22 = P2^2;
sbit P23 = P2^3;
sbit P24 = P2^4;
sbit P25 = P2^5;
sbit P26 = P2^6;
sbit P27 = P2^7;
/*******************************************************/
//*****************定义公共变量**************************/
UINT8 KServoD=10; //舵机D 参数
UINT8 KServoP[5]={65,40,10,40,65}; //舵机分段P 参数
UINT8 SampleData=0; //采样数据
SINT8 Offset=0; //当前赛道位置
SINT8 LastOffset[2]={0}; //上一次赛道位置
UINT16 SevorPWM=0; //舵机PWM 高电平时间
UINT16 MotorPWM=0; //电机PWM 高电平时间
/*******************************************************/
/********************初始化函数*************************/
void Init(void)
{
TMOD=0x11; //定时器0,16 位工作方式;定时器1,16 位工作方式
TR0=1; //启动定时器0
TR1=1; //启动定时器1
ET0=1; //打开定时器0 中断
ET1=1; //打开定时器0 中断
EA=1; //打开总中断
} //开启中断和定时器
/**************************************************************/
/********************数据采样函数************************/
void Sample(void)
{
SampleData=0;
if(P20==0)
SampleData|=0x01;
if(P22==0)
SampleData|=0x02;
if(P21==0)
SampleData|=0x04;
if(P23==0)
SampleData|=0x08;
if(P24==0)
SampleData|=0x10;
if(P26==0)
SampleData|=0x20;
if(P25==0)
SampleData|=0x40;
if(P27==0)
SampleData|=0x80;
}
/***********************************************************/
/********************确定赛道位置*************************/
void ConfirmLocation(void)
{
switch(SampleData) //根据采集到的值进行判断
{
case 0x80:Offset=7;break; //1000 0000 最左边1 个光电传感器检测到黑线
case 0xc0:Offset=6;break; //1100 0000 最左边2 个光电传感器检测到黑线
case 0x40:Offset=5;break; //0100 0000 依次类推
case 0x60:Offset=4;break; //0110 0000
case 0x20:Offset=3;break; //0010 0000
case 0x30:Offset=2;break; //0011 0000
case 0x10:Offset=1;break; //0001 0000
case 0x18:Offset=0;break; //0001 1000
case 0x08:Offset=-1;break; //0000 1000
case 0x0c:Offset=-2;break; //0000 1100
case 0x04:Offset=-3;break; //0000 0100
case 0x06:Offset=-4;break; //0000 0110
case 0x02:Offset=-5;break; //0000 0010
case 0x03:Offset=-6;break; //0000 0011
case 0x01:Offset=-7;break; //0000 0001
default: Offset=8;break; //其余情况包括跑道丢失于检测错误