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; //其余情况包括跑道丢失于检测错误