概述 此系统采用IAP15F2K60S2单片机作为主控芯片,控制直流电机的转动,带动小车移动,使用超声波传感器来测量小车位移,实现小车行进指定位移的功能。显示部分使用LCD1602液晶屏。
主要由单片机控制器、直流电机驱动电路、超声波传感器测距电路、显示电路、锁定装置等部分组成。
核心在于如何能够驱动电机稳定又准确转动,以及如何构建适当的反馈系统来确保小车位移的准确性。
反馈控制系统如图2.1所示;系统通过按键模块将目标位移值输入反馈控制链中,当前位移值与目标值作差;控制系统对差值控制,在某一阈值之前,系统保持高速前进,到达阈值之后,使得小车能够逐渐减速并且最终能够在目标值处停下。其中使用超声波测距作为负反馈,在密闭的空间内能够拥有很好地精度,也能确定小车当前的位移,是良好的反馈系统。
图2.1 反馈控制系统框图
图2.2 系统连接框图
以模型小车为载体,各个模块都需要安装在小车上。系统的连接如图2.2所示,按键模块对应位移控制系统的输入,CUP对应着系统的程序部分,超声波测距对应着反馈部分。驱动部分接收来自MCU的命令,通过ENA的PWM的占空比以调节小车的行进。直流电机控制着传动系统,驱动小车的位移。具体的连线图参考图2.3.
按键模块接收输入信息和控制命令,系统通过测距模块采集距离信息,经过反馈算法之后,将通过ENA、IN1和IN2的状态自动控制直流电机转动等,最终使得小车的行进距离与输入值相等。
图2.3 电路连线图
一、 电机驱动
1、 模块简介该模块主控芯片为L298N,一种双H桥电机驱动芯片,其中每个H桥可以提供2A的电流,功率部分的供电电压范围是2.5-48v,逻辑部分5v供电,接受5vTTL电平。其实质类似于一个单刀双掷开关,不过能够提供较大的电流。 该模块共有13个接口,此次项目将会使用到的共有8个:两个使能接口IN1、IN2,12V供电接口,GND接口,5V电源接口,驱动接口C、D,PWM控制接口ENA。本系统中12V供电接口接入的是9V电池,作为整个系统的供电;5V接口作为电源输出为单片机及其它外设供电;C、D接口与直流电机相连,当IN1、IN2接口接入的数值是10时,电机正转,01电机反转,00及11电机不转。 表3.1 L298N控制 2、功能实现控制小车行进速度的方法就是,通过调节控制接口ENA的PWM波占空比,来控制电机转动与否以及时长,如此控制小车的匀速减速等工作状态。 这里使用定时器来实现PWM波的输出:每当定时时间来临,计数单位加一,达到一百就置零;而当计数单位小于占空比时,PWM输出口置1,否则清零。代码参考附录L298N.c。
二、 超声波测距
1、模块简介选用常见的HC-SR04超声波模块,该模块使用直流5.0V供电,可探测距离为2cm-450cm,精度能够达到0.2cm,各参数基本符合此次项目的设计。该模块共有四个接口,其中VCC、GND为电源接口,TRIG控制接口、Echo为数据接口。 使用步骤有三: (1) 触发测距:IO口TRIG触发,至少给10us的高电平信号; (2) 模块内部自动发送8个40KHz的方波,自动检测是否有信号返回; (3) 有信号返回,Echo输出一个高电平,高电平持续的时间就是超声波从发射到接收的时间。 可以根据超声波传输的时间计算出测量的距离: 2 t即为高电平持续的时间,c为声速340m/s。 2、功能实现程序里,利用外部中断来接受来自Echo的高电平,上升沿定时器1开始计数,下降沿定时器计数结束。将计数时间乘以音速,再除以2便可得到测距的结果。 值得注意的是:此超声波测距模块测得的距离并不是那么准确,导致前后数据的差值较大。鉴于此,在获得距离之前,先进行五次测距,取它们的平均值作为当前测得的距离。另外这部分的重要性是毋庸置疑的,在实验过程之初,没有进行均值滤波之前,小车在到达目标1cm左右的位置时,小车会来回的抖动。一方面是由于当时控制的频率太高,而另一方面正是由于测得的距离有着十足的误差。代码请参考附录wave.c。
三、 控制算法为了实现速度的闭环控制,这里使用了业内常常使用到了PID算法来控制PWM波的占空比,通过对偏差的比例、积分、微分进行控制,使偏差趋于零的过程。 系统每隔1ms进行一次采样,之后进行一次PID算法。 PID控制一般有两种写法,一个是位置式,另一个是增量式。位置式需要进行累加的操作,而此系统的主控芯片为IAP15F2K61S2,其存储与计算能力一般,于是这里采用增量式。 增量式程序: //PID控制 //move 与 deta的关系,最终使得 move = objective if(caiyang_FLAG_) { deta_ID[2]=deta;//采样 PWM_deta=Kp*(deta_ID[2]-deta_ID[1])+Ki*deta_ID[2]+Kd*(deta_ID[2]-2*deta_ID[1]+deta_ID[0]); PWM_ID[1]= PWM_ID[0]+PWM_deta; PWM=(uint)PWM_ID[1]; //准备下一轮控制 PWM_ID[0]=PWM_ID[1]; deta_ID[0]=deta_ID[1]; deta_ID[1]=deta_ID[2]; } if(deta>0) Z_F= 1; else Z_F= 2; mada(); 此外,为了解决系统到达位置之后的原地抖动问题,以及消除到达之后受到人为遮挡会引起小车强烈反应的情况。又引入了消抖算法:小车位于指定位置±0.5cm的距离1秒钟以上时,视为调节结束,小车停止运动。这里使用定时器来确定一秒的时间。 代码如下: START是能直接控制小车是否运动的标志,STOP是记录小车能否保持±0.5cm的标志。 if(deta<= 0.5 && deta >= -0.5) sanmiao = 1; //一旦deta的绝对值小于0.5,电机暂停控制 if(sanmiao == 1) { if(deta > 0.5 ||deta < -0.5) {//跳出暂停 STOP = 0; sanmiao_flag =0; sanmiao = 0; } else { STOP = 1; } } 定时器处理函数里面: if(sanmiao== 1 ) { if(sanmiao_flag++ == 10000) { sanmiao_flag = 0; sanmiao = 0; if(STOP == 1) START = 0; } } 四、 其他功能模块1、4*4按键模块:4*4矩阵功能如下: 1 2 3 A 4 5 6 B 7 8 9 C * 0 # D, 其中A按键控制读入初始距离,B按键一键开始,C按键停止运动,D按键切换前进后退,*按键重新输入,#按键确认输入,各数字按键进行数字读入。
2、 电源模块由于小车需要移动,系统必须使用移动电源。而普通电池电量不够充足,而且质量太大,影响电机的运转。故系统使用一节9V可充电电池供电,利用L298N的5V电源接口给单片机及其外设供电,电机驱动模块使用9V供电。
3、 显示模块显示部分使用LCD1602液晶显示屏,也叫1602字符型液晶,它是一种专门用来显示字母、数字、符号等的点阵型液晶模块。它由若干个5X7或者5X11等点阵字符位组成。 共有16个接口,其中VSS、VDD、VO、A、K是电源接口,VSS、K接单片机的GND,VDD、A接单片机的VCC,VO接上变阻器;RS、RW、EN作为使能接口;其余8个引脚作为数据接口,以便显示不同的ASCII或者自定义的字符。 实验中显示初始距离、设定位移、当前距离以及实时误差。
单片机源程序如下:
- /*******
- 位移控制系统设计:小车
- 设计编写:汪佳锌
- 开始时间:2019/6/4
- 结束时间:2019/6/8
- 最终时间:2019/6/8
- 功 能:按键输入指定位移,小于4米;
- 小车行进到指定距离(位置);
- 使用模块:矩阵按键、超声波、电机驱动、显示模块;
- KBD_key.c\wave.c\L298N.c\LCD1602.c
- 按键功能:按键A:位置初始化;
- 数字按键 1,2,3,4,5,6,7,8,9,0:输入数字,百十个CM
- 按键#:确认输入;
- 按键*:重新输入;
- 按键A:位置初始化;
- 按键B:一键开始;
- 按键C:手动停止;
- 按键D: '+'/'-';
- 1、2、3、A
- 4、5、6、B
- 7、8、9、C
- *、0、#、D
- 显示区域:1:
- 2:
- *******/
- #include <STC15F2K60S2.h>
- #include <intrins.h>
- #include "timer0.h"
- #include "L298N.h"
- #include "wave.h"
- #include "LCD1602.h"
- #include "KBD_keys.h"
- #define MAX 400
- //#ifndef uchar
- //typedef unsigned char uchar;
- //#endif
- //#ifndef uchar
- //typedef unsigned int uint;
- //#endif
- enum keyState {state0,state1,state2};
- bit key_FLAG_=1,mada_FLAG_=0,caiyang_FLAG_,wave_FLAG_,dsp_FLAG_,//定时器FLAG
- QJ_HT=1,//前进、后退
- sanmiao=1,//一秒消抖
- STOP=0, //一秒消抖就停止运动
- START=0; //开始、停止
- uint key_flag=0,countB=0,caiyang_flag,wave_flag,dsp_flag,
- sanmiao_flag,
- T; //超声波测距返回的时间
- uchar Z_F, //马达的状态:0代表停,1正转,2反转;
- key_value=44, //指按下了哪个按键;
- add=0, //按键的指针,三位数 150、010、007
- PWM=0; //0~100表示占空比
- uchar aim[]={0,0,0}; //按键的输入
- float distance_caiyang=0,
- deta=20, // -------------------------- 墙壁
- //deta = 当前位置-目标位置,也是程序的控制对象 ---------------- d 终点 目标位置distance_aim
- distance=0, //当前位置 o d i
- distance_init=0, //初始位置 b eta s
- distance_aim=0, //目标位置=初始位置-目标位移 j ^--^ tance 现在 当前位置
- objective=0; //目的位移CM e |
- // ctive |
- //使用PID算法:P比例,I微分,D积分; -------------------------- 起点 初始位置distance_init
- float deta_ID[3]; //用于微分和积分
- float PWM_deta,Kp,Ki,Kd,zhongjian;
- uchar PWM_ID[2];
- void display(void);
- void control();
- void main()
- {
- uchar p=0;
-
- Kp = 0.06;
- Ki = 0.25;
- Kd = 0.01;
- deta_ID[0]=deta_ID[1]=0;
- PWM_ID[0]=0;
-
- Timer0_init();
- LCD_init();
- while(1)
- {
- //按键扫描,
- if(key_FLAG_)
- {
- key_FLAG_ = 0;
- key_value = key_scan();
- if(key_value != 44)
- {
- key_hand(key_value);
- }
- }
- //测距,六次取平均值
- if(wave_FLAG_)
- {
- wave_FLAG_ = 0;
- send_wave();
- zhongjian += distance_caiyang;
- if(p++ == 5)
- {
- distance=zhongjian/6;
- zhongjian = 0;
- p=0;
- }
- }
- //显示
- if(dsp_FLAG_)
- {
- dsp_FLAG_ = 0;
- display();
- }
- //电机控制
- control();
- // PWM = 50;
- // Z_F = 1;
- mada();
- }
- }
- void control()
- {
- distance_aim = distance_init - objective;
- deta = distance-distance_aim;
- //deta = distance-distance_init+objective;
- if(START == 1 && objective <= MAX && objective >= -MAX)
- {
- //控制部分
- if(deta <= 0.5 && deta >= -0.5)
- sanmiao = 1;
- //一旦deta的绝对值小于0.5,电机暂停控制
- if(sanmiao == 1)
- {
- if(deta > 0.5 || deta < -0.5)
- {//跳出暂停
- STOP = 0;
- sanmiao_flag = 0;
- sanmiao = 0;
- }
- else
- {
- STOP = 1;
- }
- }
- else//
- {
- //分段控制
- if(deta >= 100)
- { PWM = 70;Z_F = 1;}
- else if(deta >= 50)
- { PWM = 50;Z_F = 1;}
- else if(deta >= 10)
- { PWM = 30;Z_F = 1;}
- else if(deta > 5)
- { PWM = 10;Z_F = 1;}
- else if(deta > 0.5)
- { PWM = 5;Z_F = 1;}
- else if(deta >= -0.5 && deta <= 0.5)
- { //START = 0;
- PWM = 0;Z_F = 0;}
- else if(deta >= -5)
- { PWM = 5 ;Z_F = 2;}
- else if(deta >= -10 && deta < -5)
- { PWM = 10;Z_F = 2;}
- else if(deta >= -50)
- { PWM = 30;Z_F = 2;}
- else if(deta >= -100)
- { PWM = 50;Z_F = 2;}
- else
- { PWM = 70;Z_F = 2;}
- //PID控制
- //move 与 deta的关系,最终使得 move = objective
- // if(caiyang_FLAG_)
- // {
- // deta_ID[2]=deta;//采样
- //
- // PWM_deta=Kp*(deta_ID[2]-deta_ID[1])+Ki*deta_ID[2]+Kd*(deta_ID[2]-2*deta_ID[1]+deta_ID[0]);
- //
- // PWM_ID[1] = PWM_ID[0]+PWM_deta;
- // PWM=(uint)PWM_ID[1];
- // //准备下一轮控制
- // PWM_ID[0]=PWM_ID[1];
- // deta_ID[0]=deta_ID[1];
- // deta_ID[1]=deta_ID[2];
- // }
- // if(deta>0)
- // Z_F = 1;
- // else
- // Z_F = 2;
-
- }
- }
- else
- {
- Z_F = 0;
- ENB = 0;
- ENA = 0;
- }
- //一秒无变化,停止运动
-
- }
- void key_hand(uchar key_value)
- {
- switch(key_value)
- {
- case 1:
- case 2:
- case 3:aim[add]=key_value;add++;break;
- case 4:distance_init = distance;break;
-
- case 5:
- case 6:
- case 7:aim[add]=key_value-1;add++;break;
- case 8:START = 1;
- sanmiao = 0; //开始、停止
- objective = aim[0]*100+aim[1]*10+aim[2]; //确认输入
- if(QJ_HT == 0)//后退
- {
- objective = -objective;}
- distance_init = distance; //位置初始化
- break;//
-
- case 9:
- case 10:
- case 11:aim[add]=key_value-2;add++;break;
- case 12:START = 0;
- break;
-
- case 13:aim[0]=aim[1]=aim[2]=0;add=0;QJ_HT = 1;break;//重新输入
- case 14:aim[add]=0;add++;break;
- case 15:objective = aim[0]*100+aim[1]*10+aim[2];//确认输入
- if(QJ_HT == 0)//后退
- { objective = -objective;}
- break;
- case 16:QJ_HT = ~QJ_HT;break; //输入前进或后退
- default:break;
- }
- if(add == 3)
- add = 0;
- }
- void display()
- {
- LCD_write_com(0x80);
- if(START == 1)
- LCD_write_data('T');
- else
- LCD_write_data('P');
- LCD_write_data(':');
- display_32(distance_init);
- LCD_write_data(' ');
- if(QJ_HT == 1)
- LCD_write_data('+');
- else
- LCD_write_data('-');
- LCD_write_data('0'+aim[0]);
- LCD_write_data('0'+aim[1]);
- LCD_write_data('0'+aim[2]);
- LCD_write_data('C');
- LCD_write_data('M');
- Delayms(1);
-
- LCD_write_com(0x80+0x40);
- if(objective >MAX || objective < -MAX)
- {
- LCD_write_string(2,1,"INPUT ERROR! ");
- }
- else
- {
- display_32(distance);
- LCD_write_data(' ');
- display_32(deta);
- }
- Delayms(1);
- }
- void Timer0_handle(void) interrupt 1
- {
- if(sanmiao == 1 )
- {
- if(sanmiao_flag++ == 10000)
- {
- sanmiao_flag = 0;
- sanmiao = 0;
- if(STOP == 1)
- START = 0;
- else
- START = 1;
- }
- }
- if(dsp_flag++ == 30) //LCD显示3ms
- {
- dsp_FLAG_=1;
- dsp_flag =0;
- ……………………
- …………限于本文篇幅 余下代码请从51黑下载附件…………
复制代码
所有资料51hei提供下载:
工程程序.rar
(65.19 KB, 下载次数: 26)
|