目录

1. 舵机

(1) 接线方法

  (2)   工作原理

  (3)  舵机种类

(4) 代码书写

2.L298N驱动

(1) 简介

(2) 电源引脚

3.红外循迹

(1)简介

(2)引脚说明

4.超声波测距

(1)简介

(2)超声波测距原理 

(3) 使用方法

5.编码器测速

(1)引脚接法

(3)测速方法

(3)编码器接口简介

(4)编码器参数

(5)编码器倍频

(6)编码器分类:增量式编码器

(7)此文章写的很好,可以仔细阅读

4. 高级定时器和低级定时器的区别

5.esp8266

(1)工作模式

(2)服务端AT指令

(3)通信步骤

二、代码详解

(1)电机的初始化

(2) 循迹初始化

(3)PWM控制小车轮子速度

(4)初始化舵机

(5)超声波测距

(6)避障思路

三、小车运行完整视频

四、完整工程


1. 舵机

(1) 接线方法


                        红-------------------------VCC
                        棕色----------------------GND
                        橙色----------------------信号线

  (2)   工作原理

        舵机的控制信号为周期是20ms 的脉宽调制(PWM)信号,其中脉冲宽度从0.5ms-2.5ms,相对应舵盘的位置为0—180度,呈线性变化。也就是说,给它提供一定的脉宽,它的输出轴就会保持在一个相对应的角度上,无论外界转矩怎样改变,直到给它提供一个另外宽度的脉冲信号,它才会改变输出角度到新的对应的位置上。舵机内部有一个基准电路,产生周期20ms,宽度1.5ms的基准信号,有一个比较器,将外加信号与基准信号相比较,判断出方向和大小,从而产生电机的转动信号。

        控制电路板接受来自信号线相应的PWM控制信号,进而控制电机转动,电机带动一系列齿轮组,减速后传动至输出舵盘。舵机的输出轴和位置反馈电位计是相连的,舵盘转动的同时,带动位置反馈电位计,电位计将输出一个电压信号到控制电路板,进行反馈,然后控制电路板根据所在位置决定电机的转动方向和速度,从而达到目标停止。

        舵机的控制需要MCU产生一个周期为20ms的脉冲信号,以0.5ms到2.5ms的高电平来控制舵机转动的角度。

	TIM_TimeBaseInitStructure.TIM_Period = (200-1);			//ARR
	TIM_TimeBaseInitStructure.TIM_Prescaler =  (7200-1);	//PSC
    //此时周期就为20ms,如果传入TIM_Period=5,则输出信号脉冲宽度为0.5ms

             

  (3)  舵机种类

  1. 180度舵机可以控制旋转角度,有角度定位,上电后舵机会自动恢复到之前设置的角度位置
  2. 360度舵机不可以控制角度,只能顺时针旋转、逆时针旋转、停止、调节转速、无角度定位,上电之后不会角度不会复位
  3. 舵机又分为数字舵机和模拟舵机。

    数字舵机和模拟舵机(RC伺服系统)的机械结构是完全相同的,其最大的区别体现在控制电路上,数字舵机的伺服控制器采用了数字电路(拥有MCU和晶振),而模拟舵机的控制器采用的是模拟电路。
    模拟舵机需要给它不停的发送PWM信号,才能让它保持在规定的位置或者让它按照某个速度转动,数字舵机则只需要发送一次PWM信号就能保持在规定的某个位置。
    数字舵机以高得多的频率向马达发送动力脉冲,相对与传统的50脉冲/秒(50Hz),数字舵机的频率是300脉冲/秒(300Hz),因此反应速度更快。

    结论:数字舵机系统总体优于模拟舵机

  4. 看舵机有无机械限位,有机械限位那么在上电是不能手动旋转舵机,反之

(4) 代码书写

//初始化舵机的PWM,通道3
void steer_Init(void)
{
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	TIM_InternalClockConfig(TIM2);
	
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
	TIM_TimeBaseInitStructure.TIM_Period = (200-1);			//ARR
	TIM_TimeBaseInitStructure.TIM_Prescaler =  (7200-1);	//PSC
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
	
	TIM_OCInitTypeDef  TIM_OCInitStructure;
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;						 //配置为PWM模式1
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;//输出使能
	TIM_OCInitStructure.TIM_Pulse = 0;													 //设置占空比大小,在主函数compare中又设置一遍
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;		 //输出通道电平极性配置
	TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set;	 //输出通道空闲电平极性配置
	TIM_OC3Init(TIM2, &TIM_OCInitStructure);			   						 //初始化通道3
	TIM_OC3PreloadConfig(TIM3, TIM_OCPreload_Enable);						 //使能通道3输出
	
	TIM_Cmd(TIM2, ENABLE);
}


TIM_SetCompare3(TIM2,15);//中转

2.L298N驱动

(1) 简介

        L298N,是一款接受高电压的电机驱动器,直流电机和步进电机都可以驱动。一片驱动芯片可同时控制两个直流减速电机做不同动作,在6V到46V的电压范围内,提供2安培的电流,并且具有过热自断和反馈检测功能,可对电机进行直接控制,通过主控芯片的I/O输入对其控制电平进行设定,就可为电机进行正转反转驱动,操作简单、稳定性好,可以满足直流电机的大电流驱动条件。

(2) 电源引脚

  • 5V 驱动芯片内部逻辑供电引脚,如果安装了5V跳帽,则此引脚可输出5V电压,为微控板或其他电路提供电力供给,如果拔掉5V跳帽,则需要独立外接5V电源

12V供电----------------------------------电池的正极
输出A(out1,out2)----------------------接电机的正负极(正负极的改变可以改变轮子极性)
5V供电-----------------------------------可以给单片机供电,但一定要共地
输出A使能-------------------------------电机会以全速的状态运行,如果不接,则外接PWM控制速度
逻辑输入---------------------------------连接单片机引脚输入高低电平控制电机的转向

3.红外循迹

(1)简介

        循迹原理非常简单,模块上配有一个输出指示灯,部分模块还有电源指示灯,我们主要关注输出指示灯。红外发射器一直发射红外线,红外线经发射后被接收,此时输出低电平,输出指示灯点亮。
        黑色是不反射红外线的,也就是说循迹模块遇到黑线,模块输出高电平,输出指示灯熄灭。
当然除了遇到黑线熄灭,当距离太远红外线反射后检测不到,此时指示灯也会熄灭。那么如果要循迹,模块离地面要近,在没有遇到黑线时确保指示灯长亮,一旦指示灯熄灭就说明遇到黑线了

(2)引脚说明

接口说明:

(1) VCC 外接3.3V-5V电压(可以直接与5v单片机和3.3v单片机相连)

(2) GND 外接GND

(3) OUT 小板数字量输出接口(0和1)

PS: 对于循迹来说,四个引脚一般就只用三个引脚即可(VCC,GND,DO)

        当循迹模块距离地面太高时,会出现与循迹模块遇到黑线的一样情况,因此循迹模块距离地面不要太高。

4.超声波测距

(1)简介

超声波传感器发出的啁啾声通常在23 kHz到40 kHz之间,远高于人类听觉在20 kHz时的典型可听范围,因此称为超声波。

超声波测距模块是用来测量距离的一种产品,通过发送和收超声波,利用时间差和声音传播速度, 计算出模块到前方障碍物的距离。

超声波有四个引脚口,即Vcc(5V),Gnd,Trig(控制端),Echo(接受端)。

(2)超声波测距原理 

(1)STM32给超声波的Trig管脚一个10微秒的高电平,此时将触发超声波工作。

(2)超声波发射端会发送8个40KHz的方波,方波发射后遇障碍物返回到超声波接收端。

(3)模块将记录超声波来回的时间,并从Echo管脚输出一个与该时间等长的高电平。

PS:

  1. 建议测量周期为60ms以上,以防止发射信号对回响信号的影响。
  2. 此模块不宜带电连接,若要带电连接,则先让模块的GND端先连接,否则会影响模块的正常工作。
  3. 测距时,被测物体的面积不少于05平方米且平面尽量要求平整,否则影响测量的结果。
  4. 由于声速取决于温度和湿度,环境条件可能会改变测量的准确性。

(3) 使用方法

使用方法:一个控制口发一个10US以上的高电平,就可以在接收口等待高电平输出。一有输出就可以开定时器计时,当此口变为低电平时就可以读定时器的值,此时就为此次测距的时间,方可算出距离。如此不断的周期测,即可以达到你移动测量的值;

测试距离=(高电平时间*声速(340M/S))/2 =uS/58(厘米)   注意:1/58=0.017

5.编码器测速

(1)引脚接法

(3)测速方法

        2.1 M法测速(周期测量法)

        简单地说就是根据单位时间一共有多少个脉冲来计算转速。

  • 编码器单圈总脉冲数为C(常数)
  • 统计时间为T0 (固定值,单位秒)
  • 该时间内统计到的编码器脉冲数为M0 (测量值)

        例如:统计时间 T0 为3s,在3s内测得的脉冲数M0 为60,而编码器的单圈脉冲数C为20,则转速n=60/(20*3)=1圈每秒        ​

        当M0​很大,即转速快时,这个方法测得精度和平稳性都很好,但当M0​很小,速度改变带来的M0​变化很小,即转速慢时算出的误差就很大。所以M法测速适用于高转速场景

        当转速较低时,每个统计时间 T0 内的计数值较小,由于统计时间的起始位置与编码器脉冲的上升沿不一定对应,当统计时间的起始位置不同时,会有一个脉冲的误差(只统计上升沿时,最多会有1个脉冲误差,统计上升沿和下降沿时,最多会有2个脉冲的误差)。

        2.2 T法测速(频率测量法)

        ​ T法测速是这样操作的:是指先建立一个频率已知且固定的高频脉冲,当编码器读到一个信号,开始对高频脉冲进行计数,编码器第二个信号到来后,停止计数。根据对高频脉冲计数的次数、高频脉冲频率和电机转一圈编码器产生的脉冲数进行速度计算。

  • 编码器单圈总脉冲数为C(常数)
  • 高频脉冲的频率为 F0 (固定值,单位Hz)
  • 捕获到编码器相邻两个脉冲的间隔时间为TE ,其间的计数值为 M1 (测量值)

        2.3 M/T法测速

(3)编码器接口简介

  • Encoder Interface 编码器接口
  • 编码器接口可接收增量(正交)编码器的信号,根据编码器旋转产生的正交信号脉冲,自动控制CNT自增或自减,从而指示编码器的位置、旋转方向和旋转速度
  • 每个高级定时器和通用定时器都拥有1个编码器接口
  • 定时接入了编码器接口,此时定时器基本不能干其他事情,因此硬件资源减少,但是可以用软件资源外部中断弥补,即硬件资源和软件资源是互补的。
  • PS:有硬件资源的情况下,优先使用硬件资源,节约下来的软件资源去做其他更重要的事情。比如PWM,直接利用定时中断,然后在中断里面手动计数,手动翻转电平。输入捕获,利用外部中断或者定时中断,然后在终端里面手动把cnt的值取出来,当一个条件满足时,开启定时器功能,再将cnt里面的值置0,然后在中断里面进行计数,等到条件不满足了,在关闭定时器功能,把中断溢出次数加上cnt的值就可以获得次数。
  • 两个输入引脚借用了输入捕获的通道1和通道2

(4)编码器参数

4.1 分辨率

指编码器能够分辨的最小单位。

  • 对于增量式编码器,其分辨率表示为编码器转轴旋转一圈所产生的脉冲数,即脉冲数/转(Pulse Per Revolution 或PPR)

码盘上透光线槽的数目其实就等于分辨率,也叫多少线,较为常见的有5-6000 线。

  • 对于绝对式编码器,内部码盘所用的位数就是它的分辨率,单位是位(bit),具体还分单圈分辨率和多圈分辨率。

4.2 精度

首先明确一点,精度与分辨率是两个不同的概念。

精度是指编码器每个读数与转轴实际位置间的最大误差,通常用角度、角分或角秒来表示。

例如有些绝对式编码器参数表里会写±20′′,这个就表示编码器输出的读数与转轴实际位置之间存在正负20 角秒的误差。

精度由码盘刻线加工精度、转轴同心度、材料的温度特性、电路的响应时间等各方面因素共同决定。

4.3 最大响应频率

指编码器每秒输出的脉冲数,单位是Hz。计算公式为:

最大响应频率= 分辨率* 轴转速/60

例如某电机的编码器的分辨率为100(即光电码盘一圈有100条栅格),轴转速为120转每分钟(即每秒转2圈),则响应频率为100*120/60=200Hz,即该转速下,编码器每秒输出200个脉冲(电机带动编码器转了2圈嘛)。

4.4 信号输出形式

  • 对于增量式编码器,每个通道的信号独立输出,输出电路形式通常有集电极开路输出、推挽输出、差分输出等。
  • 对于绝对式编码器,由于是直接输出几十位的二进制数,为了确保传输速率和信号质量,一般采用串行输出或总线型输出,例如同步串行接口(SSI)、RS485、CANopen 或EtherCAT 等,也有一部分是并行输出,输出电路形式与增量式编码器相同。

(5)编码器倍频

        比如某光栅编码器一圈有N个栅格,理论上电机带动编码器转一圈,只能输出N个信号,通过倍频技术,可以实现转一圈,却能输出N*n个信号,这里的n为倍频数。

增量式编码器输出的脉冲波形一般为占空比50% 的方波,通道A 和B 相位差为90°。

  • 如果只使用通道A计数,并且只捕获通道A的上升沿,则一圈的计数值=码盘的栅格数,即为1倍频(没有倍频)
  • 如果只使用通道A计数,并且捕获了通道A的上升沿和下降沿,则编码器转一圈的计数值翻倍,实现2倍频
  • 如果既使用通道A计数,又使用通道B计数,且都捕获了上升沿和下降沿,则实现了4倍频

(6)编码器分类:增量式编码器

增量式编码器是将设备运动时的位移信息变成连续的脉冲信号,脉冲个数表示位移量的大 小。其特点如下:

  • 只有当设备运动时才会输出信号。
  • 一般会输出通道A和通道B 两组信号,并且有90° 的相位差(1/4个周期),同时采集这两组信号就可以计算设备的运动速度和方向。

如下图,通道A和通道B的信号的周期相同,且相位相差1/4个周期,结合两相的信号值:

  • 当B相和A相先是都读到高电平(1 1),再B读到高电平,A读到低电平(1 0),则为顺时针
  • 当B相和A相先是都读到低电平(0 0),再B读到高电平,A读到低电平(1 0),则为逆时针
  • 除通道A、通道B 以外,还会设置一个额外的通道Z 信号,表示编码器特定的参考位置

如下图,传感器转一圈后Z 轴信号才会输出一个脉冲,在Z轴输出时,可以通过将AB通道的计数清零,实现对码盘绝对位置的计算。

  • 增量式编码器只输出设备的位置变化和运动方向,不会输出设备的绝对位置。

(7)此文章写的很好,可以仔细阅读

编码器计数原理与电机测速原理——多图解析 - 知乎 (zhihu.com)

4. 高级定时器和低级定时器的区别

5.esp8266

(1)工作模式

ATK_ESP8266 模块支持 STA/AP/STA+AP 三种工作模式:

STA 模式:ESP8266 模块通过路由器连接互联网,手机或电脑通过互联网实现对设备的远程控制。

AP 模式:默认模式 ATK_ESP8266 模块作为热点,实现手机或电脑直接与模块通信,实现局域网无线控制。

STA+AP 模式:两种模式的共存模式,(STA 模式)即可以通过路由器连接到互联网,并通过互联网控制设备;

(2)服务端AT指令

AT+CWMODE=1 配置STA模式
AT+RST 重启生效
AT+CWJAP=“wifi名称”,“WiFi密码” 连接WIFI
AT+CIPMUX=0 设置单路连接模式,=1为多路连接
AT+CIPSTART=“TCP”,“服务器ip地址”,端口号 连接服务器
AT+CIPMODE=1 开启透传模式
AT+CIPSEND 开始透传



void esp8266_start_trans(void)
{
	//设置工作模式 1:station模式   2:AP模式  3:兼容 AP+station模式
	esp8266_send_cmd("AT+CWMODE=1","OK",50);
	
	//让Wifi模块重启的命令
	esp8266_send_cmd("AT+RST","ready",20);
	
	delay_ms(1000);         //延时3S等待重启成功
	delay_ms(1000);
	delay_ms(1000);
	delay_ms(1000);
	
	//让模块连接上自己的路由
	while(esp8266_send_cmd("AT+CWJAP=\"TP_226\",\"226226226\"","WIFI GOT IP",600));
	
	//=0:单路连接模式     =1:多路连接模式
	esp8266_send_cmd("AT+CIPMUX=0","OK",20);
	
	//建立TCP连接  这四项分别代表了 要连接的ID号0~4   连接类型  远程服务器IP地址   远程服务器端口号
	while(esp8266_send_cmd("AT+CIPSTART=\"TCP\",\"192.168.1.100\",8080","CONNECT",200));
	
	//是否开启透传模式  0:表示关闭 1:表示开启透传
	esp8266_send_cmd("AT+CIPMODE=1","OK",200);
	
	//透传模式下 开始发送数据的指令 这个指令之后就可以直接发数据了
	esp8266_send_cmd("AT+CIPSEND","OK",50);
}

(3)通信步骤

1.如果要发送AT指令,则先发送+++退出透传模式,然后通过串口发送数据给esp8266

2.如果要发数据,则先进入透传模式,然后通过串口发送数据给esp8266

二、代码详解

(1)电机的初始化

void motor_gpio()
{
  GPIO_InitTypeDef  GPIO_InitStructure;
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);	  
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5|GPIO_Pin_8|GPIO_Pin_4|GPIO_Pin_3;		
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 	 
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;	 
  GPIO_Init(GPIOA, &GPIO_InitStructure);			     
}

推挽输出和开漏输出的比较,见下面文章

(2) 循迹初始化

void xunji_gpio()
{
  GPIO_InitTypeDef  GPIO_InitStructure;
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);	  
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4|GPIO_Pin_6|GPIO_Pin_7|GPIO_Pin_8|GPIO_Pin_5;		
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
  GPIO_Init(GPIOB, &GPIO_InitStructure);		
}
//采用浮空输入的原因在于循迹模块不知道默认的电平是多少,所以采用浮空输入

(3)PWM控制小车轮子速度

//定时器2的2个PWM通道驱动小车的四个轮子
void PWM_Init(u16 arr,u16 psc)
{
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	TIM_InternalClockConfig(TIM2);
	
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
	TIM_TimeBaseInitStructure.TIM_Period = arr;		//ARR
	TIM_TimeBaseInitStructure.TIM_Prescaler = psc;		//PSC
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
	
	TIM_OCInitTypeDef TIM_OCInitStructure;
	TIM_OCStructInit(&TIM_OCInitStructure);
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
	TIM_OCInitStructure.TIM_Pulse = 0;		//CCR
	TIM_OC1Init(TIM2, &TIM_OCInitStructure);
	TIM_OC2Init(TIM2, &TIM_OCInitStructure);
	
	TIM_Cmd(TIM2, ENABLE);
}

//PWM_Init(100,7199);则小车的速度可以为1--100档,低于10档驱动能力太弱,导致电机不能驱动轮子转动

(4)初始化舵机

//初始化舵机的PWM,通道3
void steer_Init(void)
{
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	TIM_InternalClockConfig(TIM2);
	
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
	TIM_TimeBaseInitStructure.TIM_Period = (200-1);			//ARR
	TIM_TimeBaseInitStructure.TIM_Prescaler =  (7200-1);	//PSC
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
	
	TIM_OCInitTypeDef  TIM_OCInitStructure;
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;						 //配置为PWM模式1
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;//输出使能
	TIM_OCInitStructure.TIM_Pulse = 0;													 //设置占空比大小,在主函数compare中又设置一遍
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;		 //输出通道电平极性配置
	TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set;	 //输出通道空闲电平极性配置
	TIM_OC3Init(TIM2, &TIM_OCInitStructure);			   						 //初始化通道3
	TIM_OC3PreloadConfig(TIM3, TIM_OCPreload_Enable);						 //使能通道3输出
	
	TIM_Cmd(TIM2, ENABLE);
}

(5)超声波测距

//获取定时器时间
u32 GetEchoTimer(void)
{
	u32 t = 0;
	t = msHcCount*1000;    
	t += TIM_GetCounter(TIM4);    
	TIM4->CNT = 0;     //将TIM2计数寄存器的计数值清零
	delay_ms(50);
	return t;
}

//一次获取超声波测距数据 两次测距之间需要相隔一段时间,隔断回响信号
//为了消除余震的影响,取五次数据的平均值进行加权滤波。
float Hcsr04GetLength(void )
{
	u32 t = 0;
	int i = 0;
	float lengthTemp = 0;
	float sum = 0;
	while(i!=5)
	{
		
	TRIG_Send = 1;      //发送口高电平输出
	delay_us(20);
	TRIG_Send = 0;
		
	while(ECHO_Reci == 0);      //等待接收口高电平输出
		
	OpenTimerForHc();        //打开定时器
	i = i + 1;
	while(ECHO_Reci == 1);
	CloseTimerForHc();        //关闭定时器
		
	t = GetEchoTimer();        //获取时间,分辨率为1US
		
	lengthTemp = ((float)t/58.0);
	sum = lengthTemp + sum ;
	
	}
	lengthTemp = sum/5.0;
	return lengthTemp;
}

//PS:每一次测距之间必须要有大于50Ms的延时

(6)避障思路

//先测量前方距离,如果距离小于40cm,则后退一些,测量左边和右边的距离,如果左边离障碍物的距离远,则左转,反之,
此程序有bug:
1.如果左右两边都不存在障碍物或者障碍物德距离一样,他可能出现先左转,在右转,一直循环到一个地方的情况
2.超声波检测不到很低范围的障碍物,导致当车子已经停止了,因为没有检测到障碍物,从而卡死在某个地方,所以就要采用编码器测速,当速度低于某一个值得时候,要先后退一些,要重新检测
3.总之,自己多想就会发现很多漏洞,需要一步一步完善,作者此处仅仅列出自己想到得,希望看到得你可以完善得更加完美
void avoidObstacle(void)
{
	forward(25);
	measureSpeed();
	float len=Hcsr04GetLength();
	showDistance(len);
	while(1)
	{
		len=Hcsr04GetLength();
		showDistance(len);
		
		measureSpeed();
		if(cnt<1)
		{
			len=0;
		}
		
		if(len<40)
		{
			back(30);
			delay_ms(500);
			
			TIM_SetCompare3(TIM2,10);//右转
			delay_ms(200);
			float len1=Hcsr04GetLength();
			showDistance(len1);
			
			TIM_SetCompare3(TIM2,20);//左转
			delay_ms(200);
			float len2=Hcsr04GetLength();
			showDistance(len2);
			
			TIM_SetCompare3(TIM2,15);//中转
			back(30);
			delay_ms(300);
			
			if(len1>len2)
			{
				right(50);
				delay_ms(500);
				forward(35);
			}
			else
			{
				left(50);
				delay_ms(500);
				forward(35);
			}
		}
	}
}

三、小车运行完整视频

 

小车完整视频

四、完整工程

链接:https://pan.baidu.com/s/1UK1skGeU-ubAGAOLYmdHZg?pwd=8888 
提取码:8888

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐