超声波跟随原理
下面介绍的超声波跟随小车,是可以进行直线跟随,可以转弯,可以避障等等的一款小车,接下来主要介绍小车的自动跟随部分。
基本原理:在小车的两端各安装一个超声波(非收发一体),人手持一个超声波模块。这样左侧与右侧离人的距离就构成了一个三角形。
当小车正对着人时,装在小车上的2个超声波模块,距离A=B。
当人左拐时,A必定小于B,同理当人右拐时,A大于B。
当人向前走时,A和B的距离必定大于设定距离。我们只需要控制好这几个距离即可。
代码
话不多说,直接上代码。
接下来以STM32为例,下面是小车电机部分的代码:
代码片
1.电机部分
#include "motor.h"
void Motor_Init(void) //电机引脚的初始化
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //使能GPIOB端口时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12|GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15; //端口配置
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //50MHZ
GPIO_Init(GPIOB, &GPIO_InitStructure); //根据设定参数初始化GPIOB
}
/**************************************************************************
函数功能:赋值给PWM寄存器
入口参数:左轮PWM、右轮PWM
返回 值:无
**************************************************************************/
void Set_Pwm(int moto1,int moto2)
{
if(moto1<0)
{
AIN2=0;
AIN1=1;
}
else
{
AIN2=1;
AIN1=0;
}
PWMA=myabs(moto1);
if(moto2<0)
{
BIN1=1;
BIN2=0;
}
else
{
BIN1=0;
BIN2=1;
}
PWMB=myabs(moto2);
}
/**************************************************************************
函数功能:绝对值函数
入口参数:int
返回 值:unsigned int
目 的:经过直立环和速度环以及转向环计算出来的PWM有可能为负值
而只能赋给定时器PWM寄存器只能是正值。故需要对PWM进行绝对值处理
**************************************************************************/
int myabs(int a)
{
int temp;
if(a<0)
temp =- a;
else
temp = a;
return temp;
}
2.PWM定义部分
#include "pwm.h"
//PWM输出初始化
//arr:自动重装值
//psc:时钟预分频数
//TIM1_PWM_Init(7199,0);//PWM频率=72000/(7199+1)=10Khz
void TIM1_PWM_Init(u16 arr,u16 psc)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);//
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE); //使能GPIO外设时钟使能
//设置该引脚为复用输出功能,输出TIM1 CH1 CH4的PWM脉冲波形
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8|GPIO_Pin_11; //TIM_CH1 //TIM_CH4
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值
TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值 不分频
TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //选择定时器模式:TIM脉冲宽度调制模式1
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
TIM_OCInitStructure.TIM_Pulse = 0; //设置待装入捕获比较寄存器的脉冲值
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM输出比较极性高
TIM_OC1Init(TIM1, &TIM_OCInitStructure); //根据TIM_OCInitStruct中指定的参数初始化外设TIMx
TIM_OC4Init(TIM1, &TIM_OCInitStructure); //根据TIM_OCInitStruct中指定的参数初始化外设TIMx
TIM_CtrlPWMOutputs(TIM1,ENABLE); //MOE 主输出使能
TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable); //CH1预装载使能
TIM_OC4PreloadConfig(TIM1, TIM_OCPreload_Enable); //CH4预装载使能
TIM_ARRPreloadConfig(TIM1, ENABLE); //使能TIMx在ARR上的预装载寄存器
TIM_Cmd(TIM1, ENABLE); //使能TIM1
}
3.串口部分
void uart2_init(u32 bound)
{
//GPIO端口设置
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE); //使能UGPIOA时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE); //使能USART2时钟
//USART2_TX
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; //PA2
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_Init(GPIOA, &GPIO_InitStructure);
//USART2_RX
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;//PA3
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
GPIO_Init(GPIOA, &GPIO_InitStructure);
//USART 初始化设置
USART_InitStructure.USART_BaudRate = bound;//串口波特率
USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位
USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式
USART_Init(USART2, &USART_InitStructure); //初始化串口2
USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);//开启串口接受中断
USART_Cmd(USART2, ENABLE); //使能串口2
}
/**************************************************************************
函数功能:串口3初始化
入口参数: bound:波特率
返回 值:无
**************************************************************************/
void uart3_init(u32 bound)
{
//GPIO端口设置
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //使能GPIOB时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE); //使能USART3时钟
//USART3_TX
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; //PB.10
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_Init(GPIOB, &GPIO_InitStructure);
//USART3_RX
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;//PB11
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
GPIO_Init(GPIOB, &GPIO_InitStructure);
//USART 初始化设置
USART_InitStructure.USART_BaudRate = bound;//串口波特率
USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位
USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式
USART_Init(USART3, &USART_InitStructure); //初始化串口3
USART_ITConfig(USART3, USART_IT_RXNE, ENABLE);//开启串口接受中断
USART_Cmd(USART3, ENABLE); //使能串口3
}
/**************************************************************************
函数功能:串口2接收中断
入口参数:无
返回 值:无
**************************************************************************/
void USART2_IRQHandler(void)
{
if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET) //接收到数据
{
dat_left[num_left]=USART_ReceiveData(USART2);
if(dat_left[0] != 0xA5)
num_left = 0;
else
num_left++;//判断超声波串口接收的数据,如果是0XA5就接收,否则不予接收
if(num_left==3) //当接收到3个字节数之后,开始对数据进行解算并处理。得出最终的距离。
{
num_left = 0;
distance_left = dat_left[1]<<8 | dat_left[2];
}
}
}
/**************************************************************************
函数功能:串口3接收中断
入口参数:无
返回 值:无
**************************************************************************/
void USART3_IRQHandler(void)
{
if(USART_GetITStatus(USART3, USART_IT_RXNE) != RESET) //接收到数据
{
dat_right[num_right]=USART_ReceiveData(USART3);
if(dat_right[0] != 0xA5)
num_right = 0;
else
num_right++;//判断超声波串口接收的数据,如果是0XA5就接收,否则不予接收
if(num_right==3) //当接收到3个字节数之后,开始对数据进行解算并处理。得出最终的距离。
{
num_right = 0;
distance_right = dat_right[1]<<8 | dat_right[2];
}
}
}
4.控制部分
控制部分主要包括定时器中断、直线跟随、转向PD控制,很简单接下来的部分就自己写啦。
#include "control.h"
.......
主函数
#include "sys.h"
int main(void)
{
delay_init(); //=====延时函数初始化
uart1_init(128000); //=====串口1初始化
uart3_init(115200); //=====通信超声波串口
uart2_init(115200); //=====通信超声波串口
NVIC_Configuration(); //=====中断优先级分组,其中包含了所有的中断优先级的配置,方便管理和一次性修改。
TIM3_Int_Init(99,7199); //=====控制周期10ms
TIM1_PWM_Init(7199,0); //=====10Khz PWM
Motor_Init(); //=====初始化与电机连接的硬件IO接口
while(1)
{
}
}
具体的内容也就这些了,如果还有什么技术问题想咨询可以留言,鉴于这是我的第一篇博客,因此有问必回,欢迎留言交流!想要快速回复的话也可以加我的个人qq3284421573咨询: