基于stm32的巡线(白线)小车(依据光敏板传感器测线)比赛,直角转弯等赛道分析与局部到整体代码实现

本文介绍了使用STM32单片机控制的巡线小车,通过光敏传感器检测赛道,结合L298N驱动电机,实现直线行驶、左转、右转及S弯行驶。代码详细展示了如何配置定时器3产生精确PWM控制电机,以及关键的逻辑判断和子函数设计。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言:我巡线小车代码是分割赛道的思想写的(从标志位上得知已经跑到那里了)

以下是材料选择与代码展示

1:光敏板(协会自己做的),原理是和红外检测差不多,是当其中一个光敏LED灯碰到白线会给GPIO的引脚输出低电平,此处低电平用0表示,高电平用1表示。相信有一定单片机知识的小伙伴会get到我的点。这里传感器可以用红外传感器,这个需要对应场地找对应效果最好的传感器,这里选择如果不好,可以在学校问一问做这方面比赛的学长,这里不做过多解释。 

2:L298N的直流电机驱动

     这里注意需要12V供电,自身在供电可以产生5V电压,用于某一类单片机或一些模块供电。左右分别有OUT1,OUT2,OUT3,OUT4,四路输出,其对应着四路输入,这里这个模块简单来说就是将单片机产生的对应引脚的PWM做放大处理输出在电机上,要不靠单片机GPIO产生的电压怎么驱动小车前进呢。这里是IN1=1,IN2=0,左轮正传,如果颠倒IN1与IN2的值,就会电机反转,如果IN1与IN2都为0或者都为1,电机会瞬间停止。IN3与IN4亦然。这里我们玩过51的固有的思维是用时钟中断来调节PWM(也就是速度),但是STM32却可以硬件产生精准的PWM,但只针对对应自己设定的引脚,这里用STM32就要换一种思维了,要知道PWM=0,其实就是给这个输入引脚置0了,如果PWM为最大值其实就是对应输入引脚置1了。要转变一下思维。

  3.小车的车身和所选用的轮子 

对于车身,由于我比较喜欢合金的觉得更结实,这个就看个人想法了。但是重要的一点是最好买双层的车身,因为可以把驱动等放在最底上一层,把STM32放在最上层,这样显得条理清晰,还可以使小车更稳定。轮子建议选择麦克纳姆轮,这样可以保证小车跑起来不会那么容易失控。这里可以去网上搜巡线小车套件。此处要注意,我是l298n一路控制一边的两个马达,还要注意马达接线,不要误以为是同向的两根线接在一起,不要忘了,轮子安装可是反方向安装的呦,这里很容易出错。

                        

4.电池

电池的话采用18650电池,最好买三节或四节11V到12V左右的电池组,这里我当时就犯了错误,我买的就是单个电池最后充电还要一节一节冲,十分麻烦,而且有很多时候不仅充电效率低而且有很多时候单个充电会充不上,所以这边建议买可充电的电池组。然后别忘了买搭配使用的充电器

 5.以下是自己的代码段

其中使用STM32的定时器3产生PWM的

PA.6和PA.7分别对应l298n的IN1和IN2

PB.0与PB.1分别对应l298n的IN3和IN4

                                                      

                                                                子函数 .h文件

#ifndef   __Timer3_H__
#define   __Timer3_H__

#include <stm32f10x.h>

void TIM3_PWM_Init(u16 arr,u16 psc);
void Led_Init(void);
void up(void);
void  left(void);
void  right(void);
void  surrounding(void);

#endif

                                                                子函数.c文件

#include "stm32f10x.h" // Device header
#include "Delay.h"

 #define    L1   GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_3)  
 #define    L2   GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_4) 
 #define    L3   GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_5)  
 #define    L4   GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_6)  
 #define    L5   GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_7)   
 #define    L6   GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_8) 
 #define    L7   GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_9) 
 #define    L8   GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_10)
	

extern  int  arr1,arr2,arr3,arr4;
extern  int  flag;
extern  int  flag1;
extern  int  counter;
extern  int  flag2;
extern  int i;

void left(void);


void TIM3_PWM_Init(u16 arr,u16 psc)
{  
	
	
	GPIO_InitTypeDef GPIO_InitStructure;
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
	TIM_OCInitTypeDef  TIM_OCInitStructure;
	
 
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);	//使能定时器3时钟
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB |RCC_APB2Periph_GPIOA  |  RCC_APB2Periph_AFIO, ENABLE);  //使能GPIO
 	 
   //设置引脚为复用输出功能,输出TIM3 CH的PWM脉冲波形	GPIOA.6  	GPIOA.7  	GPIOB.0  	GPIOB.1
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1; //通道3 TIM_CH3   通道4 TIM_CH4
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //复用推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIOB
 
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7; //通道1 TIM_CH1   通道2 TIM_CH2
	GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA
	
   //初始化TIM3定时器,我的4个pwm是用的定时器3的四个pwm通道
	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(TIM3, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
	
	//下边是四个通道的初始化,基本都是一样的
		//初始化TIM3 Channel 1 PWM模式	 
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //选择定时器模式:TIM脉冲宽度调制模式2
 	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //输出极性:TIM输出比较极性高
	TIM_OC1Init(TIM3, &TIM_OCInitStructure);  //根据T指定的参数初始化外设TIM3 OC1
	TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable);  //使能TIM3在CCR1上的预装载寄存器
	
		//初始化TIM3 Channel 2 PWM模式	 
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //选择定时器模式:TIM脉冲宽度调制模式2
 	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //输出极性:TIM输出比较极性高	
	TIM_OC2Init(TIM3, &TIM_OCInitStructure);  //根据T指定的参数初始化外设TIM3 OC2
	TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable);  //使能TIM3在CCR2上的预装载寄存器
	
		//初始化TIM3 Channel 3 PWM模式	 
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //选择定时器模式:TIM脉冲宽度调制模式2
 	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //输出极性:TIM输出比较极性高
	TIM_OC3Init(TIM3, &TIM_OCInitStructure);  //根据T指定的参数初始化外设TIM3 OC3
	TIM_OC3PreloadConfig(TIM3, TIM_OCPreload_Enable);  //使能TIM3在CCR3上的预装载寄存器
	
		//初始化TIM3 Channel 4 PWM模式	 
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //选择定时器模式:TIM脉冲宽度调制模式2
 	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //输出极性:TIM输出比较极性高
	TIM_OC4Init(TIM3, &TIM_OCInitStructure);  //根据T指定的参数初始化外设TIM3 OC4
	TIM_OC4PreloadConfig(TIM3, TIM_OCPreload_Enable);  //使能TIM3在CCR4上的预装载寄存器
 
	TIM_Cmd(TIM3, ENABLE);  //使能TIM3
	
 
}



void Led_Init()
{
	
GPIO_InitTypeDef    GPIO_InitStruct1;
	

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);	
	
	
	
GPIO_InitStruct1.GPIO_Mode= GPIO_Mode_IPD;
GPIO_InitStruct1.GPIO_Pin=GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7|GPIO_Pin_8|GPIO_Pin_9|GPIO_Pin_10;
GPIO_InitStruct1.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStruct1);

	

}


void up()
{

while(1)
{

	
if(L3==1&&L4==0&&L5==0&&L6==1)//4,5中间两灯遇见白线
{
arr1=800;
arr2=0;
arr3=810;
arr4=0;
flag2=0;	

}


else if((flag1==4&&L3==0&&L4==0&&L5==0&&L6==0)&&flag2==0)//遇见一条横白线,且标志位flag2为0

{

	
counter++;//经过的横白线次数累计
flag2=1;//立即将标志位flag2变为1,防止下回在此进入此判断条件下由于stm32运算速度较快而造成多次counter累加

	
if(counter==3)//经过第三次横白线
{

            TIM_SetCompare1(TIM3,0);	
			TIM_SetCompare2(TIM3,800);	
			TIM_SetCompare3(TIM3,0);;	
			TIM_SetCompare4(TIM3,800);	//运用反冲速度减速1.89秒

	
	Delay(1890);
	
	        TIM_SetCompare1(TIM3,0);	
			TIM_SetCompare2(TIM3,899);	
			TIM_SetCompare3(TIM3,899);;	
			TIM_SetCompare4(TIM3,0);	//辅助左转,此处可将左轮PWM变小,此处由于我硬件问题左轮反转有问题,所以改高PWM
	
	Delay(2000);//左转2秒
	
flag1=5;//改变标志位的值,使主函数完成调用左转子函数操作
break;


}


}

	


else if(((L1==0&&L2==0&&L3==0&&L7==1)||(L1==0&&L3==0&&L7==0))&&flag==1)//遇见需要左转的情况,改为左转标志位
{

            flag1=1;
            break;            


}


else if((L8==0&&L7==0&&L2==1)&&flag1==2)//遇见需要右转的情况,改为右转标志位
{


flag1=3;
	
break;




}

else if(L3==1&&L4==0&&L5==1&&L6==1)//4灯遇白线
{
arr1=770;
arr2=0;
arr3=800;
arr4=0;

flag2=0;

}

else if(L3==1&&L4==1&&L5==0&&L6==1)//5灯遇白线
{

arr1=800;
arr2=0;
arr3=780;
arr4=0;
flag2=0;


}



else if(L3==0&&L4==0&&L5==1&&L6==1)//3,4灯遇白线
{

arr1=760;
arr2=0;
arr3=800;
arr4=0;





}

else if(L3==1&&L4==1&&L5==0&&L6==0)//5,6灯遇白线
{

arr1=800;
arr2=0;
arr3=770;
arr4=0;

flag2=0;



}


else if(L3==0&&L4==1&&L5==1&&L6==1)//3灯遇白线
{

arr1=750;
arr2=0;
arr3=800;
arr4=0;

flag2=0;



}




else if(L3==1&&L4==1&&L5==1&&L6==0)//6灯遇白线
{

arr1=800;
arr2=0;
arr3=760;
arr4=0;





}


else if(L2==0&&L5==1)//2灯遇白线,加5灯为了增加判断准确性
{

arr1=740;
arr2=0;
arr3=800;
arr4=0;
flag2=0;


}


else if(L7==0&&L3==1)//7灯遇白线,加3灯为了增加判断准确性
{

arr1=800;
arr2=0;
arr3=750;
arr4=0;
flag2=0;

}




            TIM_SetCompare1(TIM3,arr1);	
			TIM_SetCompare2(TIM3,arr2);	
			TIM_SetCompare3(TIM3,arr3);	
			TIM_SetCompare4(TIM3,arr4);	//赋pwm值的,以下同理






}


}





void  left()
{
	
	        TIM_SetCompare1(TIM3,0);	
			TIM_SetCompare2(TIM3,0);	
			TIM_SetCompare3(TIM3,0);	
			TIM_SetCompare4(TIM3,0);	             

	Delay(1500);
	              
	                
	
	

	
while(1)
{
         
            TIM_SetCompare1(TIM3,0);	
			TIM_SetCompare2(TIM3,750);	
			TIM_SetCompare3(TIM3,490);	//左转
			TIM_SetCompare4(TIM3,0);	             


                        
if(flag1==1)
{
if(L2==0||L3==0||(L4==0&&L5==1)||(L4==0&&L5==0)||(L4==1&&L5==0))//左转停止的判断
{
	        TIM_SetCompare1(TIM3,0);	
			TIM_SetCompare2(TIM3,0);	
			TIM_SetCompare3(TIM3,0);	
			TIM_SetCompare4(TIM3,0); 	 
Delay(500);	
	flag1=2;
	break;

}
}




else if(flag1==5)//为了增加执行准确性
{

if(L5==0||(L5==0&&L6==1))//s弯左转准备开始标志
{
	flag1=6;
	break;

}




}



}

}




void  right()//右转
{

	        TIM_SetCompare1(TIM3,0);	
			TIM_SetCompare2(TIM3,0);	
			TIM_SetCompare3(TIM3,0);	
			TIM_SetCompare4(TIM3,0);	             

	
	
Delay(1500);
	
	
	
	               
while(1)
{
                        
	        TIM_SetCompare1(TIM3,520);	
			TIM_SetCompare2(TIM3,0);	
			TIM_SetCompare3(TIM3,0);	
			TIM_SetCompare4(TIM3,500);	
if(flag1==3)
{

if(L6==0||L5==0||(L4==0&&L5==1)||L4==0)//右转结束标志
{  
	        TIM_SetCompare1(TIM3,0);	
			TIM_SetCompare2(TIM3,0);	
			TIM_SetCompare3(TIM3,0);	
			TIM_SetCompare4(TIM3,0);	     
	                 
	Delay(500);
	flag1=4;
	break;

}




}
}

	
                        

}



void  surrounding()//转S弯的函数
{

while(1)
{

if(L3==1&&L4==0&&L5==0&&L6==1)
{

arr1=720;
arr2=0;
arr3=750;
arr4=0;



}



else   if(L2==0&&L3==0&&L4==0&&L5==0)//最后遇见横白线,意味着跑到终点,直行7秒左右停止
{

	
	
while(1)
{
	if(i==0)
	{
	
	        TIM_SetCompare1(TIM3,500);	
			TIM_SetCompare2(TIM3,0);	
			TIM_SetCompare3(TIM3,500);;	
			TIM_SetCompare4(TIM3,0);		
	Delay(7000);
		i=1;
	
	}
	
            TIM_SetCompare1(TIM3,0);	
			TIM_SetCompare2(TIM3,0);	
			TIM_SetCompare3(TIM3,0);;	
			TIM_SetCompare4(TIM3,0);		
	
	
	
	

}

}

else if(L4==0)
{
arr1=510;
arr2=0;
arr3=780;
arr4=0;
	


}


else if(L5==0)
{
arr1=899;
arr2=0;
arr3=0;
arr4=450;



}


else if(L3==0)
{

arr1=440;
arr2=0;
arr3=780;
arr4=0;



}


else if(L6==0)
{

arr1=800;
arr2=0;
arr3=0;
arr4=430;



}


else if(L2==0)
{

arr1=430;
arr2=0;
arr3=780;
arr4=0;




}

else if(L7==0)
{
arr1=840;
arr2=0;
arr3=380;
arr4=0;




}

else if(L1==0)
{

arr1=420;
arr2=0;
arr3=780;
arr4=0;



}


else if(L8==0)
{

arr1=840;
arr2=0;
arr3=380;
arr4=0;



}



            TIM_SetCompare1(TIM3,arr1);	
			TIM_SetCompare2(TIM3,arr2);	
			TIM_SetCompare3(TIM3,arr3);	
			TIM_SetCompare4(TIM3,arr4);	



}







}





                                                                     主函数

#include "stm32f10x.h"  // Device header
#include "Timer3.h"
#include "Delay.h"

 #define    L1   GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_3)  
 #define    L2   GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_4) 
 #define    L3   GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_5)  
 #define    L4   GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_6)  
 #define    L5   GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_7)   
 #define    L6   GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_8) 
 #define    L7   GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_9) 
 #define    L8   GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_10)
	


int arr1,arr2,arr3,arr4;//对左右两轮输出占空比,我是左两轮共接一路输出
int counter;//记横白线数量
int flag;
int flag1;//记转弯等的标志位
int flag2;//十字路口标志位,将要过S弯之前的横白线与直线当作十字路口
int i;//最后停止时用的变量



int  main()
{

TIM3_PWM_Init(899,0);	
Led_Init();	
	
	if(L1==0&&L2==0&&L3==0&&L4==0&&L5==0&&L6==0&&L7==0&&L8==0)//开始小车在起跑线准备开跑
{


            TIM_SetCompare1(TIM3,899);	
			TIM_SetCompare2(TIM3,0);	
			TIM_SetCompare3(TIM3,899);	
			TIM_SetCompare4(TIM3,0);	

Delay(2500);
	
	flag=1;
}

while(1)
{

	              
	
	              
if(flag==1)//开始直行遇左转停止
{
up();
flag=0;//标志位为0,下次不进此if判断

}


else if(flag1==1)//遇见第一个需要左转的标志位
{

	
            TIM_SetCompare1(TIM3,0);	
			TIM_SetCompare2(TIM3,0);	
			TIM_SetCompare3(TIM3,0);	
			TIM_SetCompare4(TIM3,0);//由于速度过快此处相当于减速	
	Delay(600);
	
left();//调用左转子函数



}

else if(flag1==2)//遇见直走标志位
{

up();//调用直走子函数


}


else if(flag1==3)//遇见右转标志位
{

            TIM_SetCompare1(TIM3,0);	
			TIM_SetCompare2(TIM3,0);	
			TIM_SetCompare3(TIM3,0);	
			TIM_SetCompare4(TIM3,0);//此处各轮停止转动相当于减速	

	Delay(800);
right();//调用右转子函数



}

else if(flag1==4)//遇到第二个直行标志位
{


	
up();//调用直行子函数


}

else if(flag1==5)//遇到预备过S弯的标志位
{

	
left();//调用特定左转子函数




}


else if(flag1==6)//遇到开始过S弯的标志位
{





	
surrounding();	//调用过S弯的子函数
	
}


                      
}




}

                                                             延迟函数.h文件 

#ifndef __DELAY_H__
#define __DELAY_H__

void Delay(unsigned int xms);

#endif

                                                              延迟函数.c文件

void Delay(unsigned int xms)
{
	unsigned char i, j;
	while(xms--)
	{
		i = 2;
		j = 239;
		do
		{
			while (--j);
		} while (--i);
	}
}

如果代码有问题欢迎各位大佬批评指正,后续也会分享一些其他学习的东西,如果大家觉得对你有帮助欢迎关注我呦~
 

### 四路红外线寻迹小车 锐角转弯 Arduino代码 示例 对于四路红外循迹小车,在遇到路径上的锐角时,需要精确控制电机的速度和方向来完成顺利转弯。下面提供一段Arduino代码示例用于实现这一功能: ```cpp const int pinLB = 9; // 左侧后轮刹车 const int pinLF = 8; // 左侧前轮前进 const int MotorLPWM = 10; const int pinRB = 7; // 右侧后轮刹车 const int pinRF = 6; // 右侧前轮前进 const int MotorRPWM = 5; // 定义传感器引脚 int sensorPins[] = {A0, A1, A2, A3}; // 假设四个传感器连接到模拟口A0-A3 int threshold = 500; // 阈值设定,可根据实际情况调整 void setup() { pinMode(pinLB, OUTPUT); pinMode(pinLF, OUTPUT); pinMode(MotorLPWM, OUTPUT); pinMode(pinRB, OUTPUT); pinMode(pinRF, OUTPUT); pinMode(MotorRPWM, OUTPUT); Serial.begin(9600); } void loop() { int values[4]; for (int i = 0; i < 4; ++i) { values[i] = analogRead(sensorPins[i]); if(values[i] > threshold){ // 黑线逻辑 turnSharpLeft(); // 如果左侧两个传感器都感应到了黑线,则执行左转操作 delay(500); // 转弯延时 break; } } goStraight(); } void turnSharpLeft(){ digitalWrite(pinRB, LOW); digitalWrite(pinRF, HIGH); analogWrite(MotorRPWM, 150); // 设置右侧电机速度[^1] digitalWrite(pinLB, HIGH); digitalWrite(pinLF, LOW); analogWrite(MotorLPWM, 0); // 关闭左侧电机以便于快速转向 } void goStraight(){ // 控制两侧行走轮向前运动 digitalWrite(pinRB, LOW); digitalWrite(pinRF, HIGH); analogWrite(MotorRPWM, 150); digitalWrite(pinLB, LOW); digitalWrite(pinLF, HIGH); analogWrite(MotorLPWM, 150); } ``` 此段程序通过读取安装在底盘下方的四个红外发射接收管的状态判断当前车辆所处位置,并据此做出相应动作。当最左边的一个或多个传感器到黑色轨迹时,即认为遇到了需要向左急转的情况。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值