目录
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)避障思路
三、小车运行完整视频
四、完整工程
红-------------------------VCC
棕色----------------------GND
橙色----------------------信号线
舵机的控制信号为周期是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
舵机又分为数字舵机和模拟舵机。
数字舵机和模拟舵机(RC伺服系统)的机械结构是完全相同的,其最大的区别体现在控制电路上,数字舵机的伺服控制器采用了数字电路(拥有MCU和晶振),而模拟舵机的控制器采用的是模拟电路。
模拟舵机需要给它不停的发送PWM信号,才能让它保持在规定的位置或者让它按照某个速度转动,数字舵机则只需要发送一次PWM信号就能保持在规定的某个位置。
数字舵机以高得多的频率向马达发送动力脉冲,相对与传统的50脉冲/秒(50Hz),数字舵机的频率是300脉冲/秒(300Hz),因此反应速度更快。
结论:数字舵机系统总体优于模拟舵机
看舵机有无机械限位,有机械限位那么在上电是不能手动旋转舵机,反之
//初始化舵机的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);//中转
L298N,是一款接受高电压的电机驱动器,直流电机和步进电机都可以驱动。一片驱动芯片可同时控制两个直流减速电机做不同动作,在6V到46V的电压范围内,提供2安培的电流,并且具有过热自断和反馈检测功能,可对电机进行直接控制,通过主控芯片的I/O输入对其控制电平进行设定,就可为电机进行正转反转驱动,操作简单、稳定性好,可以满足直流电机的大电流驱动条件。
12V供电----------------------------------电池的正极
输出A(out1,out2)----------------------接电机的正负极(正负极的改变可以改变轮子极性)
5V供电-----------------------------------可以给单片机供电,但一定要共地
输出A使能-------------------------------电机会以全速的状态运行,如果不接,则外接PWM控制速度
逻辑输入---------------------------------连接单片机引脚输入高低电平控制电机的转向
循迹原理非常简单,模块上配有一个输出指示灯,部分模块还有电源指示灯,我们主要关注输出指示灯。红外发射器一直发射红外线,红外线经发射后被接收,此时输出低电平,输出指示灯点亮。
黑色是不反射红外线的,也就是说循迹模块遇到黑线,模块输出高电平,输出指示灯熄灭。
当然除了遇到黑线熄灭,当距离太远红外线反射后检测不到,此时指示灯也会熄灭。那么如果要循迹,模块离地面要近,在没有遇到黑线时确保指示灯长亮,一旦指示灯熄灭就说明遇到黑线了
接口说明:
(1) VCC 外接3.3V-5V电压(可以直接与5v单片机和3.3v单片机相连)
(2) GND 外接GND
(3) OUT 小板数字量输出接口(0和1)
PS: 对于循迹来说,四个引脚一般就只用三个引脚即可(VCC,GND,DO)
当循迹模块距离地面太高时,会出现与循迹模块遇到黑线的一样情况,因此循迹模块距离地面不要太高。
超声波传感器发出的啁啾声通常在23 kHz到40 kHz之间,远高于人类听觉在20 kHz时的典型可听范围,因此称为超声波。
超声波测距模块是用来测量距离的一种产品,通过发送和收超声波,利用时间差和声音传播速度, 计算出模块到前方障碍物的距离。
超声波有四个引脚口,即Vcc(5V),Gnd,Trig(控制端),Echo(接受端)。
(1)STM32给超声波的Trig管脚一个10微秒的高电平,此时将触发超声波工作。
(2)超声波发射端会发送8个40KHz的方波,方波发射后遇障碍物返回到超声波接收端。
(3)模块将记录超声波来回的时间,并从Echo管脚输出一个与该时间等长的高电平。
PS:
使用方法:一个控制口发一个10US以上的高电平,就可以在接收口等待高电平输出。一有输出就可以开定时器计时,当此口变为低电平时就可以读定时器的值,此时就为此次测距的时间,方可算出距离。如此不断的周期测,即可以达到你移动测量的值;
测试距离=(高电平时间*声速(340M/S))/2 =uS/58(厘米) 注意:1/58=0.017
2.1 M法测速(周期测量法)
简单地说就是根据单位时间一共有多少个脉冲来计算转速。
例如:统计时间 T0 为3s,在3s内测得的脉冲数M0 为60,而编码器的单圈脉冲数C为20,则转速n=60/(20*3)=1圈每秒
当M0很大,即转速快时,这个方法测得精度和平稳性都很好,但当M0很小,速度改变带来的M0变化很小,即转速慢时算出的误差就很大。所以M法测速适用于高转速场景
当转速较低时,每个统计时间 T0 内的计数值较小,由于统计时间的起始位置与编码器脉冲的上升沿不一定对应,当统计时间的起始位置不同时,会有一个脉冲的误差(只统计上升沿时,最多会有1个脉冲误差,统计上升沿和下降沿时,最多会有2个脉冲的误差)。
2.2 T法测速(频率测量法)
T法测速是这样操作的:是指先建立一个频率已知且固定的高频脉冲,当编码器读到一个信号,开始对高频脉冲进行计数,编码器第二个信号到来后,停止计数。根据对高频脉冲计数的次数、高频脉冲频率和电机转一圈编码器产生的脉冲数进行速度计算。
2.3 M/T法测速
4.1 分辨率
指编码器能够分辨的最小单位。
码盘上透光线槽的数目其实就等于分辨率,也叫多少线,较为常见的有5-6000 线。
4.2 精度
首先明确一点,精度与分辨率是两个不同的概念。
精度是指编码器每个读数与转轴实际位置间的最大误差,通常用角度、角分或角秒来表示。
例如有些绝对式编码器参数表里会写±20′′,这个就表示编码器输出的读数与转轴实际位置之间存在正负20 角秒的误差。
精度由码盘刻线加工精度、转轴同心度、材料的温度特性、电路的响应时间等各方面因素共同决定。
4.3 最大响应频率
指编码器每秒输出的脉冲数,单位是Hz。计算公式为:
最大响应频率= 分辨率* 轴转速/60
例如某电机的编码器的分辨率为100(即光电码盘一圈有100条栅格),轴转速为120转每分钟(即每秒转2圈),则响应频率为100*120/60=200Hz,即该转速下,编码器每秒输出200个脉冲(电机带动编码器转了2圈嘛)。
4.4 信号输出形式
比如某光栅编码器一圈有N个栅格,理论上电机带动编码器转一圈,只能输出N个信号,通过倍频技术,可以实现转一圈,却能输出N*n个信号,这里的n为倍频数。
增量式编码器输出的脉冲波形一般为占空比50% 的方波,通道A 和B 相位差为90°。
增量式编码器是将设备运动时的位移信息变成连续的脉冲信号,脉冲个数表示位移量的大 小。其特点如下:
如下图,通道A和通道B的信号的周期相同,且相位相差1/4个周期,结合两相的信号值:
如下图,传感器转一圈后Z 轴信号才会输出一个脉冲,在Z轴输出时,可以通过将AB通道的计数清零,实现对码盘绝对位置的计算。
编码器计数原理与电机测速原理——多图解析 - 知乎 (zhihu.com)
ATK_ESP8266 模块支持 STA/AP/STA+AP 三种工作模式:
STA 模式:ESP8266 模块通过路由器连接互联网,手机或电脑通过互联网实现对设备的远程控制。
AP 模式:默认模式 ATK_ESP8266 模块作为热点,实现手机或电脑直接与模块通信,实现局域网无线控制。
STA+AP 模式:两种模式的共存模式,(STA 模式)即可以通过路由器连接到互联网,并通过互联网控制设备;
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);
}
1.如果要发送AT指令,则先发送+++退出透传模式,然后通过串口发送数据给esp8266
2.如果要发数据,则先进入透传模式,然后通过串口发送数据给esp8266
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);
}
推挽输出和开漏输出的比较,见下面文章
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);
}
//采用浮空输入的原因在于循迹模块不知道默认的电平是多少,所以采用浮空输入
//定时器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档驱动能力太弱,导致电机不能驱动轮子转动
//初始化舵机的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);
}
//获取定时器时间
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的延时
//先测量前方距离,如果距离小于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