自动循迹功能现实
一,硬件描述
1、循迹模块五路黑线检测,黑线反馈低电平
2、小车行驶速度通过PWM控制,现实无极调速。因为开发板以及小车自重太重占空比太低的时候小车无法驱动
3、小车方向打太大的时候无法移动。
4、舵机通过周期为20ms不同占空比的方波控制转向
二、循迹思路
传感器各种情况舵机处理方式:
因为车身过大以及不能进行大拐弯,所以转不过去的时候先反向倒车,倒车直到中间传感器能检测到中线,再反向转弯直到2或者4传感器检测到黑线才开始正常巡线。
STM32循迹小车.mp4
三,源代码
1、主程序加了一段外部中断,通过开发板按键来进行模式切换
static u8 mode = 0; //操作模式,0:红外遥控,1:自动循迹,2,app蓝牙控制
void EXTIX_Init(void)
{
EXTI_InitTypeDef EXTI_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
delay_init();
KEY_Init(); // 按键端口初始化
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE); //使能复用功能时钟
GPIO_EXTILineConfig(GPIO_PortSourceGPIOE,GPIO_PinSource3);//GPIOE.3 中断线以及中断初始化配置 下降沿触发 //KEY1
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; //模式 中断
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; //上升沿触发
EXTI_InitStructure.EXTI_LineCmd = ENABLE; //使能
EXTI_InitStructure.EXTI_Line=EXTI_Line3; //中短线 KEY1按键PE3口对应3线
EXTI_Init(&EXTI_InitStructure); //根据EXTI_InitStruct中指定的参数初始化外设EXTI寄存器
NVIC_InitStructure.NVIC_IRQChannel = EXTI3_IRQn; //使能按键KEY1所在的外部中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02; //抢占优先级2
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x01; //子优先级1
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能外部中断通道
NVIC_Init(&NVIC_InitStructure); //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器
}
//外部中断3服务程序
void EXTI3_IRQHandler(void)
{
delay_ms(10);//消抖
if(KEY1==0) //按键KEY1 KEY1用来切换控制模式
{
// LCD_Clear(WHITE); //清空显示屏
LED1=!LED1;
mode++;
if(mode==3)mode=0;
}
EXTI_ClearITPendingBit(EXTI_Line3); //清除LINE3上的中断标志位
}
u8 GetMode(void){
return mode;
}
上面代码是对外部中断进行初始化已经中断服务程序对中断进行处理。因为开发板上的按键使用了STM32的引脚复用功能,所以上面除了对IO常规初始化之外还对引脚进行了部分复用。
在主程序里面通过对GetMode(void)进行调用可以查询到当前模式标志位mode 的值,通过该值来判断当前模式。注意mode设置的是static类型,可以保证mode的值不会随着调用的结束而丢失。
while(mode1 == 0){ //模式1,红外遥控模式
}
while(mode1 == 1){ //模式2,自动循迹模式
}
while(mode1 == 2){ //模式3,App蓝牙模式
Show_Str(30,30,200,16,"循迹小车蓝牙调试界面",16,0);
mode1 = GetMode();
}
2、循迹程序。已经通过调试验证,基本上能完成循迹不会有问题,但是因为没有加速度传感器,不能有效通过PID算法通过速度来控制,所以如果小车速度太快可能会冲出跑道。目前算法里面只能简单PID算法,通过检测到黑线的位置来实现.
pwm_sudu = 10000;
TIM_SetCompare4(TIM3,pwm_sudu);
LCD_Clear(WHITE); //LCD清屏
led0pwmval = Z_qian;
while(mode1 == 1){ //模式2,自动循迹模式
PGout(13) = 1;
PGout(14) = 0;
Show_Str(30,30,200,16,"循迹小车循迹调试界面",16,0);
POINT_COLOR=RED;
Show_Str(30,70,200,16,"传感器反馈值(1-5):",16,0);
Show_Str(30,110,200,16,"当前舵机pwm值:",16,0);
Show_Str(30,130,200,16,"当前速度pwm值:",16,0);
POINT_COLOR=BLUE;
if((LEDKEY5 == 0) && (LEDKEY4 == 1) && (LEDKEY3 == 1) && (LEDKEY2 == 1) && (LEDKEY1 == 1)){
led0pwmval = 1050;
TIM_SetCompare2(TIM3,led0pwmval);
TIM_SetCompare4(TIM3,pwm_sudu-1000);
flagkeyled = 5;
}
else if((LEDKEY5 == 1) && (LEDKEY4 == 0) && (LEDKEY3 == 1) && (LEDKEY2 == 1) && (LEDKEY1 == 1)){
led0pwmval = 1100;
TIM_SetCompare2(TIM3,led0pwmval);
TIM_SetCompare4(TIM3,pwm_sudu);
flagkeyled = 4;
}
else if((LEDKEY5 == 1) && (LEDKEY4 == 1) && (LEDKEY3 == 0) && (LEDKEY2 == 1) && (LEDKEY1 == 1)){
led0pwmval = Z_qian;
TIM_SetCompare2(TIM3,led0pwmval);
TIM_SetCompare4(TIM3,pwm_sudu-2000);
flagkeyled = 3;
}
else if((LEDKEY5 == 1) && (LEDKEY4 == 1) && (LEDKEY3 == 1) && (LEDKEY2 == 0) && (LEDKEY1 == 1)){
led0pwmval = 1300;
TIM_SetCompare2(TIM3,led0pwmval);
TIM_SetCompare4(TIM3,pwm_sudu);
flagkeyled = 2;
}
else if((LEDKEY5 == 1) && (LEDKEY4 == 1) && (LEDKEY3 == 1) && (LEDKEY2 == 1) && (LEDKEY1 == 0)){
led0pwmval = 1350;
TIM_SetCompare2(TIM3,led0pwmval);
TIM_SetCompare4(TIM3,pwm_sudu-1000);
flagkeyled = 1;
}else if((LEDKEY5 == 1) && (LEDKEY4 == 1) && (LEDKEY3 == 1) && (LEDKEY2 == 1) && (LEDKEY1 == 1)){//当所有检测线反馈全为高电平时黑线丢失
if(flagkeyled == 1 || flagkeyled ==5)delay_ms(200);
if((LEDKEY5 == 1) && (LEDKEY4 == 1) && (LEDKEY3 == 1) && (LEDKEY2 == 1) && (LEDKEY1 == 1)){
switch(flagkeyled){
case 5: { //车身较长,如果弯道弧度过大即使打死方向也转不过弯道
//黑线出了所有检测点,打反方向盘倒车4.5秒
flagkeyled = 0;
PGout(13) = 0;
PGout(14) = 0;
led0pwmval = 1200;
TIM_SetCompare4(TIM3,pwm_sudu-2000);
TIM_SetCompare2(TIM3,led0pwmval);
PGout(13) = 0;
PGout(14) = 1;
while((LEDKEY1 == 1) && (LEDKEY2 == 1) && (LEDKEY3 == 1))
{
LCD_ShowNum(30+8*2,90,LEDKEY1,1,16);
LCD_ShowNum(30+8*5,90,LEDKEY2,1,16);
LCD_ShowNum(30+8*8,90,LEDKEY3,1,16);
LCD_ShowNum(30+8*11,90,LEDKEY4,1,16);
LCD_ShowNum(30+8*14,90,LEDKEY5,1,16);
} //每次倒车之后,等待中间检测到黑线再开始运行检测程序
PGout(13) = 1;
PGout(14) = 0;
TIM_SetCompare2(TIM3,1100);
while(LEDKEY2 && LEDKEY4){
LCD_ShowNum(30+8*2,90,LEDKEY1,1,16);
LCD_ShowNum(30+8*5,90,LEDKEY2,1,16);
LCD_ShowNum(30+8*8,90,LEDKEY3,1,16);
LCD_ShowNum(30+8*11,90,LEDKEY4,1,16);
LCD_ShowNum(30+8*14,90,LEDKEY5,1,16);
}
}break;
case 1: {
flagkeyled = 0;
PGout(13) = 0;
PGout(14) = 0;
led0pwmval = 1100;
TIM_SetCompare4(TIM3,pwm_sudu-2000);
TIM_SetCompare2(TIM3,led0pwmval);
PGout(13) = 0;
PGout(14) = 1;
while((LEDKEY4 == 1) && (LEDKEY5 == 1) && (LEDKEY3 == 1))
{
LCD_ShowNum(30+8*2,90,LEDKEY1,1,16);
LCD_ShowNum(30+8*5,90,LEDKEY2,1,16);
LCD_ShowNum(30+8*8,90,LEDKEY3,1,16);
LCD_ShowNum(30+8*11,90,LEDKEY4,1,16);
LCD_ShowNum(30+8*14,90,LEDKEY5,1,16);
}//每次倒车之后,等待中间检测到黑线再开始运行检测程序
PGout(13) = 0;
PGout(14) = 0;
PGout(13) = 1;
PGout(14) = 0;
TIM_SetCompare2(TIM3,1350);
while(LEDKEY4 && LEDKEY2){
LCD_ShowNum(30+8*2,90,LEDKEY1,1,16);
LCD_ShowNum(30+8*5,90,LEDKEY2,1,16);
LCD_ShowNum(30+8*8,90,LEDKEY3,1,16);
LCD_ShowNum(30+8*11,90,LEDKEY4,1,16);
LCD_ShowNum(30+8*14,90,LEDKEY5,1,16);
}
}break;
}}
}else if(((LEDKEY5 == 0) && (LEDKEY4 == 0) && ((LEDKEY3 == 1) || (LEDKEY2 == 1) || (LEDKEY1 == 1))) ||
((LEDKEY4 == 0) && (LEDKEY3 == 0) && ((LEDKEY2 == 1) || (LEDKEY5 == 1) || (LEDKEY1 == 1))) ||
((LEDKEY3 == 0) && (LEDKEY2 == 0) && ((LEDKEY1 == 1) || (LEDKEY5 == 1) || (LEDKEY4 == 1))) ||
((LEDKEY1 == 0) && (LEDKEY2 == 0) && ((LEDKEY3 == 1) || (LEDKEY5 == 1) || (LEDKEY4 == 1)))){
PGout(13) = 0;PGout(14) = 0;TIM_SetCompare4(TIM3,0); //连续三个检测线检测到黑线另外两线检测到白线停车(只检测三条线是为了防止十字路口停车)
}
LCD_ShowNum(30+8*2,90,LEDKEY1,1,16);
LCD_ShowNum(30+8*5,90,LEDKEY2,1,16);
LCD_ShowNum(30+8*8,90,LEDKEY3,1,16);
LCD_ShowNum(30+8*11,90,LEDKEY4,1,16);
LCD_ShowNum(30+8*14,90,LEDKEY5,1,16);
LCD_ShowNum(30+8*16,110,led0pwmval,4,16);
LCD_ShowNum(30+8*16,130,pwm_sudu,4,16);
mode1 = GetMode();
}
STM32循迹小车/Android蓝牙控制小车(一)
STM32循迹小车/Android蓝牙控制小车(二)
STM32循迹小车/Android蓝牙控制小车(三)