简易超声波雷达
任务:
一、 摘要:
超声波测距广泛运用于各类电子产品精确距离测量,如汽车尾部的倒车雷达对倒车时的障碍物进行精确判断,反馈到驾驶员,使驾驶员能做出准确的判断和操作;各类航模中当即将触地传感器的精确判断;超声波也可广泛用于超声探伤、测厚、测距、遥控等。本系统是由STC8952单片机位核心的简易超声波雷达定位装置。该系统由一下几部分组成:5V+9V线性电源模块、MCU模块、L298N驱动模块、12864液晶显示模块以及多个HC-SR04超声波模块和SG90舵机模块组成。电源模块负责给各个模块供电;MCU模块负责各个模块的通信和处理数据。系统开启时,第一个舵机带动超声波旋转,检测到物体后,产生外部中断,进入外部中断立即开启定时器0测量高电平时间,高电平时间测量完成后立即算出该段距离并保存,连续扫描两次并保存过程中的所有数据,送入最后的处理函数,得出最后的有效数据并显示。第一个完成后第二个超声波开始旋转,检测物体位置,以此类推,当4个超声波检测完成后,液晶屏上显示出物体的象限以及X轴和Y轴的值。该系统可对一个1*1米或1*1.5米的正方形区域内的物体进行快速定位。
二 系统原理
本系统是由STC8952单片机位核心的简易超声波雷达定位装置。该系统由一下几部分组成:5V+9V线性电源模块、MCU模块、L298N驱动模块、12864液晶显示模块以及多个HC-SR04超声波模块和SG90舵机模块组成。电源模块负责给各个模块供电;MCU模块负责各个模块的通信和处理数据。系统开启时,第一个舵机带动超声波旋转,检测到物体后,产生外部中断,进入外部中断立即开启定时器0测量高电平时间,高电平时间测量完成后立即算出该段距离并保存,连续扫描两次并保存过程中的所有数据,送入最后的处理函数,得出最后的有效数据并显示。第一个完成后第二个超声波开始转动,检测物体位置,以此类推,当4个超声波检测完成后,将四个舵机收集到的值传入坐标计算函数,算出最后的坐标值,液晶屏上显示出物体的象限以及X轴和Y轴的值。
(1)测距原理示意图
三、系统方案
系统框图:
(1)系统控制
使用定时器0和1来控制超声波发送频率和超出距离判定,定时器2控制时间,精确输出PWM波,再使用74HC138做数据分配器,当哪路舵机需要PWM波时,就选通哪路输出。外部中断0用作检测回波的时间,由于外部中断有限,所以使用74HC153作数据选择器,需要哪路的回波时间时,就选择哪路作为输入。
(2)HC-SR04距离计算
采用高精度的超声波模块采集。该超声波模块盲区仅为2cm,且测量误差可小于等于3mm,,并且该模块价格低廉,可以使成本控制的很好。只需单片机一个IO口发一个10us以上的高电平,就可以在接收口等待高电平输出.一有输出就可以开定时器计时,当此口变为低电平时就可以读定时器的值,此时就为此次测距的时间, 根据公式:测试距离=(高电平时间*声速(340M/S))/2,方可算出距离。
(3)L298N驱动
L298N驱动上使用了8个二极管,由于我们使用的点击是线圈式的,从运行状态转换到突然停止的状态和从顺时针状态突然转到逆时针状态时会形成很大的反向电流,在电路中加入二极管作用就是在产生反向电流时泄流,保护芯片安全。另外,驱动使用了光耦隔离,使所使用不同工作电平之间的电路间没有电流通路的直接连接,不会引起干扰。由于L298N驱动电流很大(可以达到2A),所以完全可以满足4路舵机的驱动问题。
四、各模块实物图
(1)MCU及显示模块
主控芯片采用STC89C52芯片,片内包含3个定时器,2个外部中断,8KBROM和256BRAM
(2)电源模块
变压器输入交流电,经过整流、滤波、稳压得到+5V电压和+9V电压,5V电源给超声波及MCU模块及液晶屏供电,9V电源给L298N驱动模块供电
(3)舵机驱动模块
利用L298N驱动,用来给SG90舵机供电,每一路的舵机的VCC和GND都可以由单片机独立控制
(4)舵机
SG90舵机,最大转角为180度,工作电压仅需4.8伏即可驱动,稳定性好,扭矩大
(5)超声波模块
HC-SR04超声波模块性能稳定,测量精度可达到3mm,测量角度为15度,并且便于控制,成本低廉。该模块工作原理:首先给Trig一个10us以上的脉冲,模块自动发送8个40KHZ的方波,自动检测是否有信号返回,如果有信号返回,则在Echo输出高电平,高电平的时间为从发送到接收到信号的时间。距离计算公式:(高电平时间*340(m/s))/2
五、程序的设计
(1)程序功能描述与设计思路
①程序功能描述:根据题目要求软件部分主要实现对超声波数据的处理和显示。
②程序设计思路:通过单片机输出PWM波控制第一个舵机,带动超声波模块旋转,当检测到物体后(即超声波检测到回波,产生中断),保存数据,将收集到的所有数送入最后的处理函数,然后第二个舵机开始旋转检测,同理测量三次,根据余弦定理算出物体的X轴和Y轴,物体的象限也随之得出,最后一组超声波探头检验前面三次测量是否准确,如果准确,便在液晶屏上面显示出来。
(2)程序流程图
六、源程序
Main.c文件程序:
*/*1m所需周期:1000mm/(1微秒的声波距离mm 1周期的us时间) => 1000/0.17;
计算得到:最小探测距离2CM时间:0.02*1000/0.17=117微秒
探测距离1m时间:1000/0.17*1= 5882.35294微秒 5.882ms
最大探测距离4.5m时间:1000/0.17*4.5= 26470微秒 26.470ms*/**
include”reg52.h”
include”interrupt.h”
include”lcd_init.h”
include”Deal.h”
uint temp1,temp2,temp3,temp4;
uchar j;
uchar code table[]=”0123456789 “; //显示字符
uchar code table1[]=”The Distance: “; //提示字符
void main()
{
inct();
lcd_pos(0,0);
for(j=0;j<=14;j++)
write_data(table1[j]); //写提示语
while(1)
{
P1=0xf1; //pw波选择第一路输出
P0=0xf0; //选择第一路超声波中断输入
EX0=1; //打开外部中断
temp1=Conut_and_Scan(); //扫描并计算距离
lcd_pos(1,0); //第一个距离值显示位置
disply(temp1); //显示第一个超声波测得距离
P1=0xf9; //pw波选择第二路输出
P0=0xf1; //选择第二路超声波中断输入
EX0=1; //打开外部中断
temp2=Conut_and_Scan(); //扫描并计算距离
lcd_pos(1,5); //第二个距离值显示位置
disply(temp2); //显示第二个超声波测得距离
P1=0xf5; //pw波选择第三路输出
P0=0xf2; //选择第三路超声波中断输入
EX0=1; //打开外部中断
temp3=Conut_and_Scan(); //扫描并计算距离
lcd_pos(2,0); //第三个距
disply(temp3); //显示第三个超声波测得距离
P1=0xfd; //pw波选择第四路输出
P0=0xf3; //选择第四路超声波中断输入
EX0=1; //打开外部中断
temp4=Conut_and_Scan(); //扫描并计算距离
lcd_pos(2,5); //第四个距离值显示位置
disply(temp4);
if((temp1!=0)&&(temp2!=0)&&(temp3!=0)&&(temp4!=0))
data_deal(temp1,temp2,temp3,temp4);
else if((temp1=0)&&(temp2!=0)&&(temp3!=0)&&(temp4!=0))
data_deal(temp2,temp3,temp4,(1000-((temp3*temp3+100000000-temp4*temp4)/(2*temp3*1000))*temp3));
else if((temp1!=0)&&(temp2=0)&&(temp3!=0)&&(temp4!=0))
data_deal(temp3,temp4,temp1,(1000-((temp4*temp4+100000000-temp3*temp3)/(2*temp4*1000))*temp1));
else if((temp1!=0)&&(temp2!=0)&&(temp3=0)&&(temp4!=0))
data_deal(temp4,temp1,temp2,(1000-((temp1*temp1+100000000temp2*temp2)/(2*temp1*1000))*temp1));
else if((temp1!=0)&&(temp2!=0)&&(temp3!=0)&&(temp4=0))
data_deal(temp1,temp2,temp3,(1000-((temp2*temp2+100000000-temp3*temp3)/(2*temp2*1000))*temp2));
}
}
Interrupt.c文件:
/
*********************************************
本C文件主要放中断服务函数
超声波触发端Trig=P0^2 高电平返回端Echo=RX=P3^2; 即外部中断0输入口
舵机 PWM=P1^0
************************************************/
include”reg52.h”
include”interrupt.h”
include”intrins.h”
include”lcd_init.h”
include”Deal.h”
/
*******************
定时器中断0对超声波探头得到回波后返回的高电平时间进行计时
*******************/
extern uchar flag,count,jd,k;
uint x=0,S1;
extern uint time;
uint data_S[20]=0; //定义收集数据数组
sbit TX=P0^2; //接Trig端,触发输入
sbit RX=P3^2; //高点平接收端,接Echo
sbit pwm=P1^0; //PWM输出端
void delay(uint x) //延时函数
{
uint p,o;
for(p=x;p>0;p–)
for(o=100;o>0;o–);
}
/
*******************
定时器中断0用于测量返回高电平的时间
*******************/
void T_0time()interrupt 1 //中断一次超声波测得的距离是5.57056m
{
TH0=0;
TL0=0;
}
/
*******************
定时器中断1对输出的PWM波进行调制,中断一次的时间是0.1ms,200次为一个周期,总共20ms提供给舵机
调制占空比使得舵机调速
*******************/
void T_1time()interrupt 3
{
TL1= 0x9c; //设置定时重载值
TH1= 0xff; //装入初值,使得中断一次的时间是0.1ms
if(count
ifndef __INTERRUPT_H
define __INTERRUPT_H
include”reg52.h”
define uchar unsigned char
define uint unsigned int
void delay(uint x);
endif
lcd_init.c文件:
/
*********************************************
本C文件主要放12864驱动函数
12864数据接口P2 PSB=P3^4 EN=P3^5 RW=P3^6 RS=P3^7
************************************************/
include”reg52.h”
include”interrupt.h”
sbit psb=P3^4;
sbit lcden=P3^5;
sbit rw=P3^6;
sbit rs=P3^7;
extern uchar count,jd;
void write_com(uchar com) //设置液晶参数
{
P2=com;
rs=0; //写命令操作
rw=0; //读写控制端选择写
lcden=0;
lcden=0;
delay(1);
lcden=1; //脉冲使能
delay(1);
lcden=0;
}
void write_data(uchar date) //写入数据
{
P2=date;
rs=1;
rw=0;
lcden=0;
lcden=0;
delay(1);
lcden=1; //脉冲使能
delay(1);
lcden=0;
}
void lcd_pos(uchar X,uchar Y) //写显示地址函数
{
uchar pos;
if(X==0)
X=0x80;
else if(X==1)
X=0x90;
else if(X==2)
X=0x88;
else if(X==3)
X=0x98;
pos=X+Y;
write_com(pos);
}
void inct() //液晶及中断初始化
{
psb=1;
TMOD=0x11; //设置T0为方式1,16位定时/计数器 用于测量回波时间,即距离;
T2CON=0x00; //定时器2控制寄存器,内部控制
TH0=0x00;
TL0=0x00;
TL1= 0xb0; //设置定时重载值
TH1= 0xff; //装入初值,使得中断一次的时间是0.1ms
TL2= 0xff; //设置定时重载值 b0
TH2= 0x3c; //设置定时重载值 3c
RCAP2L = 0xff; //设置定时重载值
RCAP2H = 0x3c; //设置定时重载值
ET0=1;
ET1=1;
ET2=1; //定时器1、2、3使能
TR1=1; //开定时器1,pwm输出
IT0=1; //外部中断下降沿触发
EA=1; //全局中断使能
write_com(0x30); //设置12864*2,显示5*7点阵,8位数据接口
delay(1);
write_com(0x0c); //设置开显示,不显示光标 开光标闪烁位0x0f
delay(1);
write_com(0x01); //写一个字符后光标自动加1
delay(1);
jd=1;
count=0; //角度置0,开始位置
}
lcd_init.h文件:
ifndef __LCD_INIT_H
define __LCD_INIT_H
void write_com(uchar com);
void write_data(uchar date);
void lcd_pos(uchar X,uchar Y);
void inct();
endif
deal.c文件:
include”interrupt.h”
include”lcd_init.h”
include”math.h”
include”reg52.h”
include”Deal.h”
uchar code table4[]=”0123456789-+一二三四 “; //坐标值显示数组
uint time,S;
uchar jd,count,i,k;
extern uint data_S[20];
uint Conut_and_Scan()
{
TR2=1; //开TX触发端
k=0; //数组计数清零
for(i=5;i<=11;i++) //开始扫描,正反扫描
{
jd=i;
count=0;
delay(200);
}
for(i=11;i>=5;i–)
{
jd=i;
count=0;
delay(200);
}
EX0=0; //关外部中断
TR2=0; //关触发输入
TH0=0;
TL0=0; //定时器清零
time=0;
S=bijiao(data_S); //数据处理
return S; //返回测量距离
}
void disply(uint x) //显示函数
{
uchar a;
a=x/1000;
write_data(table4[a]);
a=x/100%10;
write_data(table4[a]);
a=x/10%10;
write_data(table4[a]);
a=x%10;
write_data(table4[a]);
}
void data_deal(float data1,float data2,float data3,float data4) //计算显示坐标函数
{
float data_z1,data_z2,data_jy3;
uint DZ1,DZ2,JY1;
data_z1=((pow(data2,2)+pow(1000,2)-pow(data1,2))/(2*data2*1000))*data2; //竖值计算
data_z2=((pow(data2,2)+pow(1000,2)-pow(data3,2))/(2*data2*1000))*data2; //横值计算
data_jy3=((pow(data4,2)+pow(1000,2)-pow(data1,2))/(2*data4*1000))*data4; //校验值计算
DZ1=(uint)data_z1; //强制转换
DZ2=(uint)data_z2;
JY1=(uint)data_jy3;
if((JY1-1000+(uint)data_z2)<=500&&(1000-(uint)data_z2-JY1)<=500) //误差分析 5cm
{
if(DZ2>=500) //横坐标计算
{
lcd_pos(3,0); /
*坐标/
write_data(table4[20]);
write_data(table4[11]);
lcd_pos(3,1);
disply(DZ2-500);
}
else
{
lcd_pos(3,0); /
*坐标/
write_data(table4[20]);
write_data(table4[10]);
lcd_pos(3,1);
disply(500-DZ2);
}
if(DZ1>=500) //纵坐标计算
{
lcd_pos(3,3); /
*坐标/
write_data(table4[20]);
write_data(table4[11]);
lcd_pos(3,4);
disply(DZ1-500);
}
else
{
lcd_pos(3,3); /
*坐标/
write_data(table4[20]);
write_data(table4[10]);
lcd_pos(3,4);
disply(500-DZ1);
}
if((DZ2>=500)&&(DZ1>=500)) //一象限
{
lcd_pos(3,6); /***坐标**/
write_data(table4[12]);
write_data(table4[13]);
}
else if((DZ2>=500)&&(DZ1<=500)) // 四象限
{
lcd_pos(3,6); /***坐标**/
write_data(table4[18]);
write_data(table4[19]);
}
else if((DZ2<=500)&&(DZ1>=500)) // 二象限
{
lcd_pos(3,6); /***坐标**/
write_data(table4[14]);
write_data(table4[15]);
}
else if((DZ2<=500)&&(DZ1<=500)) // 三象限
{
lcd_pos(3,6); /***坐标**/
write_data(table4[16]);
write_data(table4[17]);
}
}
}
uint bijiao(uint *p)
{
uint sum;
uchar i,j;
for(i=0;i
ifndef __DEAL_H
define __DEAL_H
void data_deal(uint data1,uint data2,uint data3,uint data4);
uint Conut_and_Scan();
void disply(uint x);
uint bijiao(uint *p);
endif
七、心得:
本次设计基于51单片机的简单应用,对于检测方面的设计传感器的精度显得尤为重要,本次设计的超声波雷达定位装置设计起初未能考虑到一个重要的问题,HC-SR04静态参数较好,当测量静态物体时、一米的误差为3-5毫米,随着测距的增加误差成非线性增加,有误差累积的效果。本次设计中最大的难点也是最容易忽略的地方,就是动态测量的回波误差,当超声波转动一个很小的步距后发送一次超声波,再等待回波的反射回来,此等待时间固定,再转到下一个小角度,每次转的角度大下由布进电机决定。但是,引起数据的误差是由于当转到下一次的时候上一次发送的超声波经过多个障碍物后返回的高电平距本次的触发脉冲时间是不确定的随机的,故测得的数据也是杂乱的,毫无规律。发现这个问题我也是大费周折,起先是怀疑线的接触电阻引起的电平干扰,于是利用焊点固定接触点的方法和检查返回的高电平信号的波形以此检查干扰信号的影响。经过反复的验证,发现数据的误差不是由于接触电阻造成的,经过几番假设和控制变量的推理、发现了问题的所在。解决:本次的问题是由于传感器的精度所造成的影响,最后选定用软件解决的方法解决了,但还是偶尔会出现该问题。具体是每当舵机扫过一圈记录所有的数据,利用冒泡法排序进行升序排列。舍去最下的三个值后依次向上比较,当连续相邻3个数之间的差值小于设定的范围,如0.5cm则认为这三次值是所测障碍物的反射值距离,即最佳有效距离。取其平均值为最终的值。最后经过反复测试效果明显提高。基本达到功能所需。