基于C51单片机和TB6600步进电机驱动器驱动的三路超声波避障移动机器人平台设计与Proteus仿真

前言

这是我在一个大学生机械创新比赛所做的项目,主要学科是机械,本人也是一位机自专业的大三学生,对高端的电控知识并不是很了解,只是在学习之余自学了相关硬件和控制的知识。因本人才疏学浅,故该文章只是记录我的此次设计过程,并将其分享出来,希望能够和与我有类似经验的同学讨论,或者受到专业人士的批评指正。

为了记录的这次比赛的具体设计思路,以供以后有参与相关比赛意愿的同学参考,我将会从最基本的各模块的使用开始说起,相信我,即使你只是了解过一点点和51单片机有关的知识,懂得并行I/O口和定时器的相关使用,那么这边文章就不会难懂。如果连并行I/O口和定时器也不了解,那我还是建议你先学习这些知识。

例程分析

作为此次比赛的作品的某一个功能模块,我们的想实现的具体功能为:该移动平台和机器人的其他模块保持独立,该平台只具有移动的功能,底部两个橡胶轮通过差速实现平台转弯,另外加一个万向轮用来保存平台的稳定,橡胶轮接有悬架,可以保证平台移动平稳,而在底盘的底部还有橡胶垫,当平台上受到的力达到某个阈值时,平台下降,橡胶垫接触地面,保证在平台受力时不会乱晃。底盘三维模型如下图。
底盘三维模型

模块介绍

该项目涉及到的模块有:超声波模块(SRF04)、TB6600步进电机驱动器和两相四线步进电机。如果能力允许,我将在后期更新红外遥控功能。这样,这个机器人底盘就可以实现红外遥控功能和自主避障功能。

由于身边没有相关硬件设备,所以我目前的设计工作只是停留在软件仿真阶段,使用的是Proteus 8进行模拟的。下面介绍相关模块的使用

超声波模块(SRF04)

超声波测距模块
上图是Proteus 8 提供的超声波模块(关键字:SRF04),可以发现它有5个引脚,其中GND接地、VCC接高电平、NC(Not Connected)可不接,因此只剩下了两个引脚。TR用作激发信号的输入,当超声波模块在TR引脚上检测到了连续的10us以上的高电平时,超声波模块才开始工作。“ECHO”用作反馈信号输出,当超声波检测到有障碍物时,从该引脚输出相应信号。具体的信号时序如下图所示
超声波时序图
值得注意的是,在超声波模块获得触发信号的时候,模块内部发出了8个40KHz的脉冲,如果只是要使用这个模块,则可以不理会其中的含意。从超声波模块发出超声波开始,输出回回响信号置高电平,直到模块接收到信号为止,回响信号才变为低电平,由此,我们可以得到输出回响信号高电平持续的时间与障碍物之间的距离的关系:

X ( t ) = t × 340 / 2 X(t) = t×340/2
其中 t 的单位为秒,X的单位为米。340是常温下声音在空气中的传播速度。

以上公式需要注意,超声波在开始发出的时候,回响信号就置1,到接收到超声波的时候置0,此时超声波走过了两倍的障碍物的距离,所以应该除以2。我们可以通过单片机自带的定时器来获得回响信号高电平的精确时间,再通过软件自动计算出距离。当我们不需要精确知道距离的时候,也可以通过延迟的方式来查询高电平在某一时刻是否还存在,进而确定障碍物的距离是否达到阈值。

此外,我们还要注意,为了防止信号紊乱,即第一次检测的回声还没有被接收到,第二次检测的触发就开始了,所以我们通常设定两次触发信号的时间间隔在60ms以上。

在Proteus仿真中,我们可以通过改变模块上自带的数字来模拟改变障碍物的距离,上图显示的数字的含意是距离模块50cm的位置有障碍物。

TB6600步进电机驱动器和42步进电机

在Proteus 8 上没有这一型号的驱动器,但是该驱动器在做比赛的时候会经常用到,为此,我按照实际情况在Proteus上建立了该元件的模型。如下图所示(由于本人太菜,只是画出来了这个模型,方便连线用的,实际上内部电路也是一片空白,无法用于模拟……)
TB6600驱动器
这个驱动器有12个接口,其中GND和VCC接通12V电源,因为仅靠51单片机输出的微弱电流还不足以带动42步进电机。

另外,A+、A-、B+、B-连接四线步进电机,proteus里有两相四线步进电机的模型(Motor-Bistep),但是我还没有查到具体的连线方式,不过也没有关系,我将在后面介绍如何使用示波器来判断电机的旋转方式。

此外还有六个引脚,分别为EN±、DIR±、CP±。这六个引脚有两种连线方式,分为共阴和共阳两种。共阴是将这三组引脚的负极接地,阳极接IO口,共阳即将这三组引脚的正极接电源,阴极接IO口,对于初学者来说,这选用这两种方式皆可。需要我们关注的是这三组接口的含义。其中,EN称为使能端,当使能端置1时,步进电机可在电机不受控时自由旋转调节,使能端置0时锁定电机,只有驱动器可以控制电机,人为施力无法转动电机。DIR为方向控制端,当该端口检测到变化的电平信号时(从0置1或从1置0)电机旋转变向。CP端为脉冲输入端,步进电机通过输入脉冲来旋转,在特定的情况下,当输入一定数量的脉冲时,步进电机即旋转对应的角度,这种对应关系可在TB6600驱动器的外壳上或驱动器用户手册上查询到,关系如下图所示
步进电机频率细分表
举个例子,如果一个步进电机的固有步距角是1.8°,在4细分下驱动下,即接收到4个脉冲后旋转1.8°,在接受到800个脉冲后,电机旋转一圈。因此,当接收到的脉冲频率越高,电机的转速越快。

到此为止,TB6600驱动器的各引脚的功能已经说明完毕,总的来说,即EN控制电机自锁,DIR控制电机旋转方向,CP控制电机旋转速度,AB连接电机的四线,GND和VCC连接外置电源。

原理图讲解

原理图
上图是我绘制的底盘控制原理图,其中P0组并口连接电机驱动器,分为高4位和低4位,每4位控制一个电机,4位分别接VCC(驱动器共阳接法)、CP、DIR、EN。

P2组并口连接超声波模块,他们在图中的位置即表示了超声波模块在实际机器人上的物理位置(我开始设想是在机器人前后左右四个方向各安装一个超声波模块,实现距离的比较和智能拐弯,但是后来发现对后的超声波模块在避障中似乎没有用到,不过可以用这个扩展来做机器人的室内定位)。其中每两个引脚控制一个超声波模块,一个连接TR用于触发,一个连接ECHO用于接收。

再者就是P3.2和P1.6,P3.2用于红外接收,P1.6用于指示模式。当P1.6上的LED灯熄灭时,无法使用遥控控制机器人平台,只能由机器人自主移动,当按下遥控上的待机键时,LED灯亮,此时机器人平台可以遥控。该功能还在研究中,如果有小伙伴想了解的,我们可以一起讨论。

运用示波器模拟

示波器可以辅助我们调试程序和仿真,关于在Proteus里调用示波器以及示波器的使用,我不做介绍,其他很多资料都能查到,我在这里只介绍示波器在本例程的运用。

我在这个例子中运用了3个示波器,一个用于检测四个超声波模块的TR脚信号(没错我用了4个超声波模块……),一个用于检测四个超声波模块的输出信号反映距离的变化(这个示波器也可以不用设定),还有一个示波器用于检测两个TB6600驱动器的输入信号(只检测DIR端和CP端),毕竟我们只用知道驱动器的输入信号,就可以了解电机的运动情况。

当烧录进编写好的程序时,设定障碍物的距离均为30时,示波器的表示如下图,其中左中右依次表示为SHOWTR(显示超声波触发信号)、SHOWMOTOR(显示电机运动状态)、SHOWDIS(显示距离)。而对于中间的示波器,最上和最下两路信号表示两个电机输入的脉冲情况,第二和第三两路信号表示两个电机的方向。
初始状态示波器
从左边示波器中可以看出单片机每180ms向超声波模块发起一次触发信号,不会引起信号紊乱。从中间示波器可以看出驱动器CP端收到的脉冲信号周期为5ms,在不分频的情况下,电机转速为60r/min,轮胎直径为100mm的时候,机器人移动速度大约为314mm/s。最右边的示波器用于核对四个超声波模块探测到的距离。

当前方超声波模块探测障碍物的距离小于阈值(设定为18)时,可以发现显示电机运动状态示波器显示的信号发生了变化,如下图
当检测到前方有障碍物时
示波器表明左电机在以一定频率改变方向,致使整个移动平台在不断向左转以调整姿态,负信号持续的时间由轮距计算得出,在该情形下,移动平台每次旋转10.8°直到前后左右四个超声波模块检测到障碍物距离在阈值之外才恢复正常行驶。调整好姿态后的示波器显示如下图
调整好后的示波器显示
同理,当检测到左边或右边有障碍物时,该移动平台会相应地向右或向左调整姿态。而这种要求是只用一个超声波传感器无法做到的。

代码部分

#include <reg51.h>
#include "intrins.h"
typedef unsigned int uint;

/*电机部分引脚定义*/
sbit left_pulse=P0^1;//左电机脉冲信号
sbit left_dir=P0^2;//左电机方向信号
sbit right_pulse=P0^5;//右电机脉冲信号
sbit right_dir=P0^6;//右电机方向信号
sbit P0_0=P0^0;
sbit P0_3=P0^3;
sbit P0_4=P0^4;
sbit P0_7=P0^7;

/*超声波部分引脚定义*/
sbit left=P2^7;         //表示距离
sbit right=P2^3;
sbit front=P2^1;
sbit back=P2^5;
sbit P2_0=P2^0;
sbit P2_2=P2^2;
sbit P2_4=P2^4;
sbit P2_6=P2^6;

/*红外和LED引脚定义*/
sbit ir=P3^2;             //红外接收引脚
sbit led=P1^6;           //待机指示灯,亮灯表示可遥控,灭灯表示不可遥控

/*初始化部分******************************************************************************************/

void motor_init(void)//初始化电机部分引脚
{
	P0=0xff;
}

void srf_init(void)//初始化超声波部分引脚
{
	P2=0x00;
}

void ir_init(void)//初始化红外遥控部分引脚
{
	ir=1;
	led=1;
}

void timer0_init(void)//初始化定时器
{
	EA=1;
	ET0=1;
	TMOD|=0x01;
	TH0=(65536-2500)/256;
	TL0=(65536-2500)%256;      //2.5ms定时,即5ms一个脉冲,不分频时,1000ms旋转1圈
	TR0=1;
}

/*方向运动函数******************************************************************************************/

void go_forward()
{
	left_dir=1;
	right_dir=1;
}

void turn_right()
{
	left_dir=1;
	right_dir=0;
}

void turn_left()
{
	left_dir=0;
	right_dir=1;
}

void go_backward()
{
	left_dir=0;
	right_dir=0;
}

/*其他必要函数******************************************************************************************/

void delay(uint xms)  //延时xms函数
{
	uint i,j;
	for(i=xms;i>0;i--)
		for(j=124;j>0;j--);
}

void delay100us()   //延时100us
{
	uint i;
	for(i=11;i>0;i--);
}

void srf_open(void)//超声波测距并调整姿态
{
    P2_0=1;           //超声波触发信号
    P2_2=1;
    P2_4=1;
    P2_6=1;
	_nop_();          //空语句,1us延时,当延时大于10us时,超声波模块被触发
	_nop_();
	_nop_();
	_nop_();
	_nop_();
	_nop_();
	_nop_();
	_nop_();
	_nop_();
	_nop_();
	_nop_();
	_nop_();
	_nop_();
	_nop_();
	_nop_();
	_nop_();
	_nop_();
	P2_0=0;
	P2_2=0;
	P2_4=0;
	P2_6=0;
	//delay100us();            //添加函数以调整阈值
	while(front)
	{
		delay(1);                 //改变此值以调整阈值
		if(front==0||right==0)   //说明前方或右侧17cm以内有障碍物
		{
			turn_left();
			delay(30);              //向左旋转10.8°以调整姿态
			go_forward();           //避免调整角度过量,适度向前
			delay(70);              //保证周期一致
		}
		else if(front==0||left==0)
		{
			turn_right();
			delay(30);               //向右旋转10.8°以调整姿态
			go_forward();             //避免调整角度过量,适度向前
			delay(70);               //保证周期一致
		}
		else 
		{
			go_forward();
			delay(100);
		}
		delay(80);
	}
}

void timer0(void) interrupt 1  //定时器精确控制电机脉冲
{
	TH0=(65536-2500)/256;
	TL0=(65536-2500)%256;      //2.5ms定时,即5ms一个脉冲,不分频时,1000ms旋转1圈
    left_pulse=~left_pulse;
	right_pulse=~right_pulse;
}

void main(void)
{
	timer0_init();
	motor_init();
	srf_init();
	ir_init();
	while(1)
	{
		srf_open();
	}
}

由于本人经验不足,大三才开设的微机原理课程,具体时间经验过少,所以此代码并不是最完美的,欢迎大佬指正。

如果有想意愿参加类似比赛或者对这方面感兴趣的同学,也欢迎联系我,可以共同探讨学习。

项目后期

因为比赛还在进行中,所以不遍透露该作品的具体的其他相关信息,只是开源其中的底盘部分,后期可能会开源红外控制,欢迎大家一起探讨学习。

发布了1 篇原创文章 · 获赞 2 · 访问量 161

猜你喜欢

转载自blog.csdn.net/yudandan_caesar/article/details/105115225