一、全体结构:
(1)规划功用:
①能完结多方向行走以及其他的自定义的动作。(行进,后撤,左右转,避障);
②可主动避障;
③经过手机蓝牙命令他的下一步动作。
(2)功用结构:
(3)运用器件:
①STC89C52单片机、74LS04(反相器);
②蓝牙串口通讯模块;
③超声波测距模块;
④9G舵机18个;
⑤PVC线槽若干(模具);
⑥PCB转印板;
⑦螺丝螺母若干。
⑦keil3软件
二、作业原理:
(1)蓝牙串口通讯模块:
蓝牙串口通讯模块接纳手机蓝牙软件发送字符串信号,单片机经过串口通讯协议处理蓝牙模块接纳到的信息,再依据信息的内容来判别机器人将进行的下一步举动。
(2)超声波测距模块:
超声波模块向某一方向发射超声波,在发射时刻的一起开端计时(传出低电平),超声波在空气中传达,途中碰到障碍物就当即返回来,超声波接纳器收到反射波就当即中止计时(回到高电平),依据低电平的长短来核算丈量间隔。(超声波在空气中的传达速度为340m/s,依据计时器记载的时刻t,就能够核算出发射点距障碍物的间隔(s),即:s=340t/2)
(3)舵机操控:
操控电路板承受来自信号线的操控信号,操控电机滚动,电机带动一系列齿轮组,减速后传动至输出舵盘。舵机的输出轴和方位反应电位计是相连的,舵盘滚动的一起,带动方位反应电位计,电位计将输出一个电压信号到操控电路板,进行反应,然后操控电路板依据所在方位决议电机滚动的方向和速度,然后到达方针中止。舵机的操控信号周期为20MS的脉宽调制(PWM)信号,其间脉冲宽度从0.5-2.5MS,相对应的舵盘方位为0-180度,呈线性改动。也便是说,给他供给必定的脉宽,它的输出轴就会坚持必定对应视点上,不管外界转矩怎样改动,直到给它供给一个别的宽度的脉冲信号,它才会改动输出视点到新的对应方位上。
在咱们的著作中,18路舵机分红2组,分别用一个内部定时器来操控,产生对应舵机的PWM信号(首要定时器1生成第一个舵机的脉宽,再生成第二个舵机的,到第9个舵机停止,然后定时器2以相同方法生成剩下的9个舵机的PWM信号,以此往复)。
三、制造进程:
(1)仿真原理图:
(2)PCB制造:
(3)硬件建立:
《a》肢体制造:
资料:PVC线槽,PVC板
①模型制造:(纯手工割出来的)
②舵机改造:
③全体:
四、调试以及问题解决:
①结构问题:
咱们以为,全体的外形结构是决议著作胜败的要害。经过多种资料的实验,终究咱们挑选了简单裁剪、硬度根本满意的PVC线槽来改装拼接肢体,躯体运用更厚的塑料板。阅历一周的纯手工加工改造后,完结了整个模型的制造。
②供电问题:
因为咱们运用的是9G舵机,功能较差,扭力不行,无法支撑起咱们规划的电源与稳压模块,最终抛弃了内嵌的电源,运用实验室的可调电源箱经过电线来供电,无法独立开来也是咱们的仅有惋惜。
③机器颤动问题
因为89C52只要6个内部中止,远远无法满意18个舵机的操控,而且其他功用模块也要运用到内部中止。所以咱们将18路舵机分红了2组,初始时一个接一个舵机(每个舵机20ms周期)来发送PWM,但这也产生了发送一次18路PWM的总周期长度太大(18*20=360ms),足以产生被人眼所发觉的颤动。经过反复研讨,让当时舵机的PWM信号在上一个PWM信号的低电平处开端产生高电平(在上一个PWM的高电平完毕后)如下图,大大缩短了18路舵机一次动作的总周期长度(经过18路后,总周期长度为一个PWM的周期长度约20ms),使颤动无法被人眼所调查。
代码挺多,给出首要的舵机操控代码,代码看不懂不要紧,后边有解说:
#include《reg52.h》
#include《intrins.h》
#include《dongzuo.h》
#define ucharunsigned char
#define uintunsigned int
//PWM
sbit PWM0 = P1^0;
sbit PWM1 = P1^1;
sbit PWM2 = P1^2;
sbit PWM3 = P1^3;
sbit PWM4 = P1^4;
sbit PWM5 = P1^5;
sbit PWM6 = P3^4;
sbit PWM7 = P3^5;
sbit PWM8 = P3^6;
sbit PWM9 = P3^7;
sbit PWM10 = P2^0;
sbit PWM11 = P2^1;
sbit PWM12 = P2^2;
sbit PWM13 = P2^3;
sbit PWM14 = P2^4;
sbit PWM15 = P2^5;
sbit PWM16 = P2^6;
sbit PWM17 = P2^7;
//超声波测距
sfr T2MOD = 0XC9; //定时器2形式操控寄存器地址
sbit Trig =P3^2;
sbit Echo =P3^3;
unsigned intdistance;
uchar DZCS =0x11; //操控动作
uchar buf;
uchar sd=3;
bit flag=0; //是否发送字符
bit CSB =0; //超声波发动操控位
bit HZ=0; //撤退后左转操控位
uchar PWMscan =0;
uchar PWMscan1 =0;
uchar PWMval[]={//初始姿势
0xf8,0x8f,0xf7,0x05,0xf9,0x8c,/*5*/ 0xfa,0x0d,0xf8,0x0b,0xf9,0x67,/*b*/ 0xfa,0xd4,0xf7,0x94,0xf9,0xcb,/*11*/
0xfa,0xad,0xfc,0xdd,0xfb,0x58,/*17*/ 0xfa,0xe9,0xfc,0xfc,0xfb,0x39,/*1d*/ 0xfc,0x18,0xfc,0xca,0xfb,0x00/*23*/
};
void delay(uint a)
{
uchar j;
for(a;a》0;a–)
for(j=0;j《112;j++)
;
}
void task00()
{
if(PWMscan==1) //第1路PWM。
{
PWM0=1;
TH0=PWMval[0];
TL0=PWMval[1];
}
else if(PWMscan==2) //第2路PWM。
{
PWM0=0;
PWM1=1;
TH0=PWMval[2];
TL0=PWMval[3];
}
else if(PWMscan==3) //第3路PWM。
{
PWM1=0;
PWM2=1;
TH0=PWMval[4];
TL0=PWMval[5];
}
else if(PWMscan==4) //第4路PWM。
{
PWM2=0;
PWM3=1;
TH0=PWMval[6];
TL0=PWMval[7];
}
else if(PWMscan==5) //第5路PWM。
{
PWM3=0;
PWM4=1;
TH0=PWMval[8];
TL0=PWMval[9];
}
else if(PWMscan==6) //第6路PWM。
{
PWM4=0;
PWM5=1;
TH0=PWMval[10];
TL0=PWMval[11];
}
else if(PWMscan==7) //第7路PWM。
{
PWM5=0;
PWM6=1;
TH0=PWMval[12];
TL0=PWMval[13];
}
else if(PWMscan==8) //第8路PWM。
{
PWM6=0;
PWM7=1;
TH0=PWMval[14];
TL0=PWMval[15];
}
else if(PWMscan==9) //第9路PWM。
{
PWM7=0;
PWM8=1;
TH0=PWMval[16];
TL0=PWMval[17];
}
else if(PWMscan==10) //给必定低电平,将周期拉长
{
PWM8=0;
TH0=0xFF;
TL0=0xd2;
PWMscan=0;
TR0 = 0; //关定时器0,开定时器1
TR1 = 1;
}
PWMscan++;
}
void task01()
{
if(PWMscan1==1) //第10路PWM。
{
PWM9=1;
TH1=PWMval[18];
TL1=PWMval[19];
}
else if(PWMscan1==2) //第11路PWM。
{
PWM9=0;
PWM10=1;
TH1=PWMval[20];
TL1=PWMval[21];
}
else if(PWMscan1==3) //第12路PWM。
{
PWM10=0;
PWM11=1;
TH1=PWMval[22];
TL1=PWMval[23];
}
else if(PWMscan1==4) //第13路PWM。
{
PWM11=0;
PWM12=1;
TH1=PWMval[24];
TL1=PWMval[25];
}
else if(PWMscan1==5) //第14路PWM。
{
PWM12=0;
PWM13=1;
TH1=PWMval[26];
TL1=PWMval[27];
}
else if(PWMscan1==6) //第15路PWM。
{
PWM13=0;
PWM14=1;
TH1=PWMval[28];
TL1=PWMval[29];
}
else if(PWMscan1==7) //第16路PWM。
{
PWM14=0;
PWM15=1;
TH1=PWMval[30];
TL1=PWMval[31];
}
else if(PWMscan1==8) //第17路PWM。
{
PWM15=0;
PWM16=1;
TH1=PWMval[32];
TL1=PWMval[33];
}
else if(PWMscan1==9) //第18路PWM。
{
PWM16=0;
PWM17=1;
TH1=PWMval[34];
TL1=PWMval[35];
}
else if(PWMscan1==10) //给必定低电平,将周期拉长
{
PWM17=0;
TH1=0xFf;//b1 //这是一个大约的值,因为每一组的PWMval的总和(PWMval中定时器的间隔的总和便是一个周期)纷歧致,
//所以会导致周期不必定是20ms,但大约能够操控在20ms左右,也是因为周期的不固定,所以才需求
TL1=0xd2;//e0 //调整每一个舵机的实践的占空比。
PWMscan1=0;
TR0 = 1;//开定时器0
TR1 = 0;//关定时器1
}
PWMscan1++;
}
void TImer0()interrupt 1
{
task00();//操控前9路PWM
}
void TImer1()interrupt 3
{
task01();//操控后9路PWM
}
在实践进程中,或许是因为舵机的质量问题,又或者是其他问题,舵机的视点操控总是难以运用原理上的公式来操控视点,都是实践操作,手动调整高电平的宽度,当到达适宜的值的时分,然后再把相应的代码记载下来。
单片机的高电平宽度是经过定时器的两个寄存器操控的,所以操作舵机的滚动就变成操作定时器的寄存器,再详细一点便是要得到TH、TL两个值。(定时器高低位的差值对应高电平的宽度)
在代码上,在操控第几路舵机的时分,TH、TL的值现已定死了为哪一个PWMval[?],比方第18路:
TH1=PWMval[34];
TL1=PWMval[35];
这将决议此刻第18路舵机的滚动视点是多少,那么怎样操控下一次该舵机的滚动视点呢?答案很简单,便是把PWMval[34];PWMval[35];的值修正一下就能够了,其他的舵机相同是这个道理。所以,机器人的一个姿势就能够变为这样:机器人姿势→18路舵机的视点→18个TH、TL的值→一个36个元素的数组PWMval的值。
所以,一个动作姿势就能够用这样一个函数来确认:
void DZ(ucharPWM[])//动作
{
uchar i;
for(i=0;i《36;i++)
PWMval[i]=PWM[i];
}
理解了这个之后,便是对每一个姿势搜集数据了,在制造进程,我是把TH和TL的两个值显现在数码管上,然后记载下来的。
后边又加入了蓝牙操控模块,超声波测距,发现51单片机的定时器不太够用,改成了52系列的单片机,还一个定时器即用蓝牙模块,又用超声波测距,现在想来真敬服自己。给出操控代码,我们自行研讨:
//***************************中止初始化**************************
void Init()
{
TMOD |= 0x11;//定时器0、1
ET0 = 1;//使能定时器0中止
TR0 = 1;//敞开定时器0,定时器1中止在定时器0开端后才翻开
ET1 = 1;//使能定时器1中止
IT1 = 0;//外部中止1,低电平触发 (边缘高变低)
EX1 = 1;//开外部中止1
//定时器2用于波特率的产生
SCON=0x50;
PCON=0x00;
RCAP2H=0xFF;
RCAP2L=0xDC;//设置波特率为9600
T2CON=0x34;//将定时器2设置为波特率产生器(接纳和发送都用TImer2) //此处包含发动T2
ES=1; //串口中止
EA = 1;//开总中止
}
void TImer0()interrupt 1
{
task00();//操控前9路PWM
}
void timer1()interrupt 3
{
task01();//操控后9路PWM
}
void serial() interrupt 4
{
EA=0; //其他中止全停
if(RI)
{
RI=0; //铲除串行承受标志位
flag=1;
buf=SBUF; //从串口缓冲区获得数据 (i-0x30)将ASCLL码转换成数字
switch(buf)
{
case 0x00: DZCS=0x00;break; //向前走
case 0x01: DZCS=0x01;break; //向后走
case 0x02: DZCS=0x02;break; //左转
case 0x03: DZCS=0x03;break; //右转
case 0x04: DZCS=0x04;break; //横着左
case 0x05: DZCS=0x05;break; //横着右
case 0x06: DZCS=0x06;break; //挥爪子
case 0x07: sd++;break; //减速,其实便是每个姿势中的延时纷歧样
case 0x08: sd–;break; //加快
case 0xff: CSB=!CSB;break; //发动封闭超声波壁障
default:
DZCS=0x11;break; //
}
}
EA = 1; //翻开总中止
}
void start()// 超声波测距发动函数
{
uchar i;
Trig=1;
for(i=0;i《20;i++)
{
_nop_();
}
Trig=0;
}
void count()// 超声波测距函数
{
unsigned int time,timeH,timeL;
timeH=TH1;
timeL=TL1;
time=timeH*256+timeL;
distance=time*1.7/100;
}
void Inter()interrupt 2//外部中止1在次完结测距以及相应的后续操作
{
EA =0;
ET0=0; //关定时器中止0
TH1=0;
TL1=0;
TR1 =1; //检测到间隔敞开定时器1
while(!Echo); //当echo为零时等候,中止flag跳出等候
TR1 =0; //封闭定时器1
count(); //核算间隔
if(((10《distance)(distance《30))||HZ) //当间隔小于5cm时,改换动作哦(在中止中改换平面感应
{
DZCS=0x02; //向左
HZ=0;
}
if(distance《10) //当间隔小于10cm时,改换动作哦(在中止中改换曲面感应
{
DZCS=0x01; //撤退
HZ=1; //撤退后左转标志
}
if(distance》30) //当间隔小于40cm时,改换动作哦(在中止中改换
{
DZCS=0x00; //向前
HZ=0;
}
TR1=1;
ET0=1;
EA = 1;
}
void main()
{
Init();
while(1)
{
uchar DZCST;//,i;
if(CSB)
start();
if(DZCST!=DZCS)//动作产生改动,则回到平衡
DZ(PH1);
if(sd==0)
sd=1;
switch(DZCS)
{
case0x00:DZXQ(sd);break;
case0x01:DZXH(sd);break;
case0x02:DZXZ(sd);break;
case0x03:DZXY(sd);break;
case0x04:DZHZZ(sd);break;
case0x05:DZHZY(sd);break;
case0x06:DZZZ(sd);break;
default:
DZ(PH1);
}
DZCST=DZCS;
}
}
『本文转载自网络,版权归原作者一切,如有侵权请联络删去』