智能小车综合项目(超详细!)—蓝牙、PWM调速、差速转弯等智能小车问题

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

智能小车综合项目


前言

一篇文章解决所有智能小车的问题!


提示:以下是本篇文章正文内容,下面案例可供参考

一、先让小车能动起来

1.1 硬件准备

单片机、智能小车套装(电机、电机驱动、电源、车轮、杜邦线)

1.11 L9110s—电机驱动模块

接通VCC,GND 模块电源指示灯亮,以下资料来源官方,官方给的数据有误。(实际要通过自己代码来调试电机正转反转)

IA1输入高电平,IA1输入低电平,【OA1 OB1】电机正转;
IA1输入低电平,IA1输入高电平,【OA1 OB1】电机反转;
IA2输入高电平,IA2输入低电平,【OA2 OB2】电机正转;
IA2输入低电平,IA2输入高电平,【OA2 OB2】电机反转;
L9110s—电机驱动模块

1.12 L9110驱动模块与单片机、电源、轮子的接线图

在这里插入图片描述

1.2 代码

① motor.c —— 封装小车向前后左右的功能代码

#include "reg52.h"
sbit RightCon1A = P3^2;  //右轮
sbit RightCon1B = P3^3;
sbit LeftCon1A = P3^4;   //左轮
sbit LeftCon1B = P3^5;

//前进
void goForward()
{
	LeftCon1A = 0;   //左轮向前
	LeftCon1B = 1;
	
	RightCon1A = 0;  //右轮向前
	RightCon1B = 1;
}
//后退
void goBack()
{
	LeftCon1A = 1;
	LeftCon1B = 0;
	
	RightCon1A = 1;
	RightCon1B = 0;
}
//左转
void goLeft()
{
	LeftCon1A = 0;  //左轮不动
	LeftCon1B = 0;
	
	RightCon1A = 0;  //右轮向前
	RightCon1B = 1;
}
//右转
void goRight()
{
	LeftCon1A = 0;  //左轮向前
	LeftCon1B = 1;
	
	RightCon1A = 0;  //右轮不动
	RightCon1B = 0;
}

② main.c —— 主函数

#include "reg52.h"
#include "intrins.h"
#include "motor.h"

void Delay1000ms()		//@11.0592MHz
{	unsigned char i, j, k;
	_nop_();
	i = 8;
	j = 1;
	k = 243;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}

void main()
{
	while(1){
		goForward();
		Delay1000ms();
		Delay1000ms();
		goBack();
		Delay1000ms();
		Delay1000ms();
		goLeft();
		Delay1000ms();
		Delay1000ms();
		goRight();
		Delay1000ms();
		Delay1000ms();
	}
}

二、蓝牙小车

2.1 先通过串口控制小车

原理

配置串口中断模式,串口不断的接收数据,如果串口接收到我们自己设置【标记】,让小车进行相对应的操作。

步骤

直接将自己之前做的串口接收数据的代码复制粘贴过来 (下面也有附代码),串口初始化和串口发送和接收的代码基本通用的,不用改。

1.配置串口通信中断模式(避免与主函数main执行的程序发生冲突);
2.每次接收到数据都会进行串口中断。如果接收到"M"开头的数据,把数组下标"i"设置为0,让数组从下标0开始保存数据;
3.判断数组存的数据进行相对应的操作;
4.串口处于一直接收数据的状态,如果一直没有接收到我们需要的M开头的数据,存数据的数组慢了(越界了),要记得把数组清0;
5.使用STC-ISP软件的串口助手调试(如下图)。
在这里插入图片描述

重点
①要记得配置串口工作方式1,REN位要使能可以接收数据
②打开串口中断,用串口中断来接收数据
③下面有附上串口通信UART的代码
在这里插入图片描述

2.2 代码

①uart.c —— 串口通信的功能代码

#include "reg52.h"
#include "motor.h"
#include "string.h"

#define SIZE 12
sfr AUXR = 0x8E;
char buffer[SIZE];

void UartInit(void)		//9600bps@11.0592MHz
{
	AUXR = 0x01;
	SCON = 0x50; //配置串口工作方式1,REN使能接收
	TMOD &= 0x0F;
	TMOD |= 0x20;//定时器1工作方式位8位自动重装
	
	TH1 = 0xFD;
	TL1 = 0xFD;//9600波特率的初值
	TR1 = 1;//启动定时器
	
	EA = 1;//开启总中断
	ES = 1;//开启串口中断
}

//M1前  M2后 M3左  M4右
void Uart_Handler() interrupt 4
{
	static int i = 0;//静态变量,被初始化一次
	char tmp;

	if(RI)//中断处理函数中,对于接收中断的响应
	{
			RI = 0; //清除接收中断标志位
			
			tmp = SBUF;  //①获取SBUF寄存器接收到的数据
			if(tmp == 'M'){   //②如果接收数据里有字母M,就让buffer数组从0开始把
				i = 0;
			}
			//③然后buffer[0]把M存起来, 
			//i加加, buffer[1]存接下来的数据
			buffer[i++] = tmp;   //buffer[i]=tmp; i++;
		
			//④buffer[0]=M的情况下,buffer[1]为1,2,3,4做不同的操作
			if(buffer[0] == 'M'){
				switch(buffer[1]){
					case '1':
						goForward();
						break;
					case '2':
						goBack();
						break;
					case '3':
						goLeft();
						break;
					case '4':
						goRight();
						break;
					default:
						stop();
						break;
				}
			}
			
			//如果接收数据一直没有遇到M,数组满了,把数组清空
			if(i == 12) {
				memset(buffer, '\0', SIZE);
				i = 0;
			}
	}
		
}

②motor.c —— 封装小车向前后左右的功能代码

③main.c —— 主函数

2.3 把蓝牙和小车结合起来

1.把蓝牙RX、TX、GND、VCC插上小车的单片机,
2.然后手机微信扫描蓝牙模块自带的蓝牙助手(或者自己开发的手机APP有蓝牙连接功能),
3.选择蓝牙的名字连接上,发送文本数据即可。

【蓝牙的知识点】:
1.因为蓝牙是透明传输,不对数据进行任何的解析和处理,只负责将数据从源地址传原封不动输到目的地址。
2.所以蓝牙
需要接收数据就直接插上RX口
需要发送数据就直接插上TX口即可。
3.AT指令
可以通过AT指令来改蓝牙名字,重启模块等等操作。具体的指令,模块的官网有。

三、点动效果——小车的改良

把小车的效果做的跟遥控小车一样,比如不按它就不动,一直按着前进的按键它才会向前。

3.1 思路

【怎么做?】

1.在主函数循环执行stop()函数,让小车一直保持停止的状态
2.那么当有人按下前进按键,串口接收到数据后中断主函数来执行串口中断中的前进函数,一直按着前进的按键就一直中断,一直往前。如果松手,就会停止。
在这里插入图片描述

【重点!!遇到的问题】

虽然我们一直按的前进,单片机串口也不断地接收到了前进的指令,并执行前进的程序,但是这过程太耗时了。
每一次的单片机接收到的前进信号是我们手机按键按下前进按钮,然后手机的蓝牙把数据发送到单片机的蓝牙,单片机的蓝牙再传输给单片机的串口,串口接收到数据后传给单片机的cpu,单片机才执行串口中断并进行前进的操作。
所以单片机接收到前进信号的间隔太久,虽然我们一直按着前进,但是等到单片机接收到下一次的前进信号这中间的间隔时间,由于单片机执行的速度非常的快,例如51单片机的12MHZ,执行一条指令的机器周期只需要1us起步,所以单片机等接收到你下一次漫长的前进命令时,单片机早就又执行了几千上万次的主函数中的stop()函数。
所以虽然我们一直按的前进,但是小车根本不会动,你一直按的前进,单片机串口刚接收到小车前进的指令并执行,还看不出啥反应,单片机主函数马上又来了几千上万条停止的指令。

【如何解决】

在串口中断的小车执行前后左右的操作后面延时一会(这里延时10ms),让它先看到一点前后左右操作。具体延时多久,这个时间自己慢慢调试,找到一个平衡点。
在这里插入图片描述

3.2 修改后的代码

①uart.c —— 串口通信的功能代码

#include "reg52.h"
#include "motor.h"
#include "string.h"
#include "delay.h"
sbit D5 = P3^7;
#define SIZE 12

sfr AUXR = 0x8E;
char buffer[SIZE];

void UartInit(void)		//9600bps@11.0592MHz
{
	AUXR = 0x01;
	SCON = 0x50; //配置串口工作方式1,REN使能接收
	TMOD &= 0x0F;
	TMOD |= 0x20;//定时器1工作方式位8位自动重装
	
	TH1 = 0xFD;
	TL1 = 0xFD;//9600波特率的初值
	TR1 = 1;//启动定时器
	
	EA = 1;//开启总中断
	ES = 1;//开启串口中断
}

//M1qian  M2 hou M3 zuo  M4 you
void Uart_Handler() interrupt 4
{
	static int i = 0;//静态变量,被初始化一次
	char tmp;

	if(RI)//中断处理函数中,对于接收中断的响应
	{
			RI = 0;//清除接收中断标志位
			tmp = SBUF;
			if(tmp == 'M'){
				i = 0;
			}
			buffer[i++] = tmp;
		
			//小车前后左右指令
			if(buffer[0] == 'M'){
				switch(buffer[1]){
					case '1':
						goForward();
						Delay10ms();
						break;
					case '2':
						goBack();
						Delay10ms();
						break;
					case '3':
						goLeft();
						Delay10ms();
						break;
					case '4':
						goRight();
						Delay10ms();
						break;
					default:
						stop();
						break;
				}
			}
		
			if(i == 12) {
				memset(buffer, '\0', SIZE);
				i = 0;
			}
	}
}

②main.c —— 主函数

#include "motor.h"
#include "delay.h"
#include "uart.h"

void main()
{
	UartInit();
	
	while(1){
		stop();
	}
}

四、PWM调速小车

pwm是什么?

 PWM,英文名Pulse Width Modulation,是脉冲宽度调制缩写,它是通过对一系列脉冲的宽度进行调
 制,等效出所需要的波形 (包含形状以及幅值),对模拟信号电平进行数字编码,也就是说通过调节占
 空比的变化来调节信号、能量等的变化,占空比就是指在一个周期内,信号处于高电平的时间占据整个
 信号周期的百分比,例如方波的占空比就是50%.

①脉冲宽度调制
②通过占空比编码模拟信号
③占空比:一个周期内,高电平占据时长的百分比(通俗点就是有效的数据占空的比例)

【自己的理解】
如下图,
绿色部分一个周期是20ms,在一个周期内,高电平占0.5ms,低电平占19.5ms。占空比是:0.5ms/20ms =2.5%。
红色部分一个周期是20ms,在一个周期内,高电平占2ms,低电平占18ms。占空比是:2ms/20ms=10%。
这个高、低电平在每个周期里的持续时间我们可以自己调制,这就是脉冲宽度调制,这就是PWM。
在这里插入图片描述

用定时器来模拟PWM波

按照上面的波形,我们可以用定时器来设置20ms为一个周期,设置0.5ms为一个单位(也可以设置1ms、2ms为一个单位,看个人),
按照这个设置,我们定时器计数0.5ms就进入定时器中断函数一次,每次进去统计次数+1,进去定时器中断函数40次的时候就是一个周期也就是20ms
ok,我们用定时器模拟出了一个20ms的周期,接下来写代码模拟红色的波形图,在20ms内,我们给io口 2ms 的高电平,剩下18ms为低电平,然后以此为周期
在这里插入图片描述

用PWM给小车调速

那用PWM给小车调速也是一样的原理,由于直接给电机电平是全速的状态,那我们可以通过pwm在一个周期内,一段时间给小车电平,一段时间不给电平,这样就可以很好的控制小车的速度。在不给电平的状态下,其实电机是有惯性的,也不会完全停止。
在这里插入图片描述

其它用途:

除了可以调速,通过PWM调速这样更有利于小车的转弯。因为小车在转弯的过程中,需要一个轮子动,一个轮子不动。如果在小车直行的过程中,突然让一个轮子不动,让小车转弯,这样会显得转弯不够丝滑,通过pwm调速,可以让轮子的速度降下来,而不是完全的不动,这样转弯会丝滑很多。(看下一章,pwm差速转弯)

代码

①time.c——定时器模拟PWM

#include "motor.h"
#include "reg52.h"

char speed;
char cnt = 0;

void Time0Init()
{
	//1. 配置定时器0工作模式位16位计时
	TMOD = 0x01;
	//2. 给初值,定一个0.5出来
	TL0=0x33;
	TH0=0xFE;
	//3. 开始计时
	TR0 = 1;
	TF0 = 0;
	//4. 打开定时器0中断
	ET0 = 1;
	//5. 打开总中断EA
	EA = 1;
}

void Time0Handler() interrupt 1
{
	cnt++;  //统计爆表的次数. cnt=1的时候,报表了1
	//重新给初值
	TL0=0x33;
	TH0=0xFE;
	
	//控制PWM波
	if(cnt < speed){
		//前进
		goForward();
	}else{
		//停止
		stop();
	}
	
	if(cnt == 40){//爆表40次,经过了20ms
		cnt = 0;  //当40次表示20ms,重新让cnt从0开始,计算下一次的20ms
	}	
}

②main.c

#include "motor.h"
#include "delay.h"
#include "uart.h"
#include "time.h"

extern char speed;

void main()
{
	Time0Init();
	//UartInit();
	
	while(1){
		speed = 10; //10份单位时间0.5ms全速运行,30份停止,所以慢
		Delay1000ms();
		Delay1000ms();
		speed = 20;   //20份单位时间0.5ms的全速运行,20份的0.5ms停止
		Delay1000ms();
		Delay1000ms();
		speed = 40;   //全速前进
		Delay1000ms();
		Delay1000ms();
	}
}

③motor.c —— 封装小车向前后左右的功能代码

④delay.c ——自己去软件生成

⑤uart.c —— 串口通信的功能代码(这里其实没用到它)

五、PWM小车差速转弯(丝滑转弯)

用途:

通过PWM调速这样更有利于小车的转弯。因为小车在转弯的过程中,需要一个轮子动,一个轮子不动。如果在小车直行的过程中,突然让一个轮子不动,让小车转弯,这样会显得转弯不够丝滑,通过pwm调速,可以让轮子的速度降下来,而不是让轮子完全的不动,这样转弯会丝滑很多。

如何实现:

原理还是跟第四章的PWM调速小车一样,由于需要把小车左右两边的轮子分开控制,所以我们需要两个定时器分别模拟PWM控制左右两边的电机。所以配置两个定时器写两个中断函数即可,直接看代码。

代码:

①time.c——两个定时器分别模拟PWM来控制左右轮(与前面不同)

#include "motor.h"
#include "reg52.h"

char speedLeft;
char cntLeft = 0;

char speedRight;
char cntRight = 0;

void Time0Init()
{
	//1. 配置定时器0工作模式位16位计时
	TMOD = 0x01;
	//2. 给初值,定一个0.5出来
	TL0=0x33;
	TH0=0xFE;
	//3. 开始计时
	TR0 = 1;
	TF0 = 0;
	//4. 打开定时器0中断
	ET0 = 1;
	//5. 打开总中断EA
	EA = 1;
}

void Time1Init()
{
	//1. 配置定时器1工作模式位16位计时
	TMOD &= 0x0F;     //保留低四位,定时器0的配置不变,把高四位清0
	TMOD |= 0x01 << 4;  //0x01左移4位0x10,再或TMOD。
	//2. 给初值,定一个0.5出来
	TL1=0x33;
	TH1=0xFE;
	//3. 开始计时
	TR1 = 1;
	TF1 = 0;
	//4. 打开定时器1中断
	ET1 = 1;
	//5. 打开总中断EA
	EA = 1;
}


void Time1Handler() interrupt 3
{
	cntRight++;  //统计爆表的次数. cnt=1的时候,报表了1
	//重新给初值
	TL1=0x33;
	TH1=0xFE;
	
	//控制PWM波
	if(cntRight < speedRight){
		//右轮前进
		goForwardRight();
	}else{
		//右轮停止
		stopRight();
	}
	
	if(cntRight == 40){//爆表40次,经过了20ms
		cntRight = 0;  //当40次表示20ms,重新让cnt从0开始,计算下一次的20ms
	}
		
}

void Time0Handler() interrupt 1
{
	cntLeft++;  //统计爆表的次数. cnt=1的时候,报表了1
	//重新给初值
	TL0=0x33;
	TH0=0xFE;
	
	//控制PWM波
	if(cntLeft < speedLeft){
		//左轮前进
		goForwardLeft();
	}else{
		//左轮停止
		stopLeft();
	}
	
	if(cntLeft == 40){//爆表40次,经过了20ms
		cntLeft = 0;  //当40次表示20ms,重新让cnt从0开始,计算下一次的20ms
	}
}

②main.c —— 主函数(与前面不同)

#include "motor.h"
#include "delay.h"
#include "uart.h"
#include "time.h"

extern char speedLeft;
extern char speedRight;

void main()
{
	Time0Init(); //先初始化定时器0,因为定时器0我们配置的比较暴力
	Time1Init();  //再初始化定时器1
	//UartInit();
	
	while(1){
		//向左转弯:右边轮子快,左边轮子慢
		speedLeft = 10;//10份单位时间全速运行,30份停止,所以慢,20ms是40份的500us
		speedRight = 40;
		Delay1000ms();
		Delay1000ms();
	
		//向右转弯:左边轮子快,右边轮子慢
		speedLeft = 40;
		speedRight = 10;
		Delay1000ms();
		Delay1000ms();
	}
}

③motor.c —— 封装小车向前后左右的功能代码(与前面不同)

#include "reg52.h"

sbit RightCon1A = P3^2;
sbit RightCon1B = P3^3;

sbit LeftCon1A = P3^4;
sbit LeftCon1B = P3^5;


//①通过PWM来丝滑的左右转弯
//左轮前进
void goForwardLeft()
{
	LeftCon1A = 0;
	LeftCon1B = 1;
}
//左轮停止
void stopLeft()
{
	LeftCon1A = 0;
	LeftCon1B = 0;
}
//右轮前进
void goForwardRight()
{
	RightCon1A = 0;
	RightCon1B = 1;
}
//右轮停止
void stopRight()
{
	RightCon1A = 0;
	RightCon1B = 0;
}

//————————————————————————————————
//②全速的前进后退左转右转
void goForward()
{
	LeftCon1A = 0;
	LeftCon1B = 1;
	
	RightCon1A = 0;
	RightCon1B = 1;
}

void goRight()
{
	LeftCon1A = 0;
	LeftCon1B = 1;
	
	RightCon1A = 0;
	RightCon1B = 0;
}


void goLeft()
{
	LeftCon1A = 0;
	LeftCon1B = 0;
	
	RightCon1A = 0;
	RightCon1B = 1;
}

void goBack()
{
	LeftCon1A = 1;
	LeftCon1B = 0;
	
	RightCon1A = 1;
	RightCon1B = 0;
}

void stop()
{
	LeftCon1A = 0;
	LeftCon1B = 0;
	
	RightCon1A = 0;
	RightCon1B = 0;
}

④delay.c ——自己去软件生成

⑤uart.c —— 串口通信的功能代码(这里其实没用到它)

六、循迹小车、跟随小车

没什么好讲的,后面直接贴代码
①在车头左右两侧安装红外模块
②…未完待续…

七、未完待续…

未完待续…

  • 45
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
智能小车是一种基于51单片机的智能控制系统,能够自主进行行驶和避障。在这篇文章中,我们将介绍智能小车的构成和原理,并讨论其在实际应用中的优势和局限性。 首先,让我们来看看智能小车的构成。智能小车包括三个主要部分:控制模块、传感器模块和执行模块。控制模块是智能小车的大脑,它负责接收传感器模块采集到的数据并做出相应的决策,控制执行模块进行行动。传感器模块主要由声波传感器和红外线传感器组成,用于探测环境中的障碍物和其他物体。执行模块包括电机和轮子,用于控制小车的移动方向和速度。 智能小车的工作原理是基于控制模块和传感器模块之间的信息交互。传感器模块不断采集周围环境的数据,并将数据传输到控制模块。控制模块接收到数据后,会根据预设的算法进行计算和判断,决定小车的行动方向和速度,控制执行模块进行相应操作。 在实际应用中,智能小车有许多优势。首先,它能够自主避障,可以在遇到障碍物时自动停止或绕过障碍物,避免碰撞或意外。其次,智能小车可以通过添加不同的传感器模块,实现不同的功能,如声音识别、图像识别等。再次,智能小车具有灵活性和可扩展性,可以随时更改程序和添加功能,适应不同的应用场景。 然而,智能小车也存在一些局限性。首先,它的运行速度和最大负载受到限制,不能像一些机器人一样完成更复杂的任务。其次,智能小车的可靠性和稳定性也需要不断提高,尤其是在复杂的环境中,如户外场景和不良天气条件下。 总的来说,智能小车是一种基于51单片机的智能控制系统,具有自主避障、灵活性和可扩展性等优势。在未来的发展中,智能小车将会在各个领域得到广泛应用,如智能家居、物流配送、环境监测等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值