第13章-循迹功能 循迹小车讲解 原理分析 STM32智能小车循迹教程 红外对管使用 PID循迹算法分析

讲解一下我们小车里面的循迹部分,包括红外基础使用,无PID循迹和有PID循迹。

第13章-循迹功能

13.1-非PID循迹功能完成

先红外对管调试

我们这里学习一下,如何实现循迹功能

如何才能让小车沿着黑线运动、要让小车感知到黑线的位置,使用这种传感器就可以反馈黑线是否存在
在这里插入图片描述
根据传感器特性,我们检测红外对管DO引脚的电压就可以知道,下面有没有黑线

DO 高电平->有黑线 小灯灭

DO低电平->没有黑线 小灯亮

这是好多地方对这个产品的说明

在这里插入图片描述
然后我们组合上面的红外对管,安装到小车上,就可以知道小车是否偏离了黑线,
在这里插入图片描述
下面我们通过单片机读取红外对管DO口的电压,就知道黑线在小车下面的位置了
STM32初始化

先看原理图需要初始化那些引脚
在这里插入图片描述

OUT_1-PA5、OUT_2-PA7、OUT_3-PB0、OUT_4-PB1初始化为输入模式
在这里插入图片描述
重新生成

然后我们在gpio.h 添加读取GPIO的宏,使得程序更简洁
在这里插入图片描述

#define READ_HW_OUT_1   HAL_GPIO_ReadPin(HW_OUT_1_GPIO_Port,HW_OUT_1_Pin) //读取红外对管连接的GPIO电平
#define READ_HW_OUT_2   HAL_GPIO_ReadPin(HW_OUT_2_GPIO_Port,HW_OUT_2_Pin)
#define READ_HW_OUT_3   HAL_GPIO_ReadPin(HW_OUT_3_GPIO_Port,HW_OUT_3_Pin)
#define READ_HW_OUT_4   HAL_GPIO_ReadPin(HW_OUT_4_GPIO_Port,HW_OUT_4_Pin)

根据红外对管状态控制电机速度

注意:整个主函数不要加入延时,这样实时性更高,可以根据红外对管状态做出及时控制
在这里插入图片描述

	if(READ_HW_OUT_1 == 0&&READ_HW_OUT_2 == 0&&READ_HW_OUT_3 == 0&&READ_HW_OUT_4 == 0 )
	{
		printf("应该前进\r\n");
		motorPidSetSpeed(1,1);//前运动
	}
	if(READ_HW_OUT_1 == 0&&READ_HW_OUT_2 == 1&&READ_HW_OUT_3 == 0&&READ_HW_OUT_4 == 0 )
	{
		printf("应该右转\r\n");
		motorPidSetSpeed(0.5,2);//右边运动
	}
	if(READ_HW_OUT_1 == 1&&READ_HW_OUT_2 == 0&&READ_HW_OUT_3 == 0&&READ_HW_OUT_4 == 0 )
	{
		printf("快速右转\r\n");
		motorPidSetSpeed(0.5,2.5);//快速右转
	}
	if(READ_HW_OUT_1 == 0&&READ_HW_OUT_2 == 0&&READ_HW_OUT_3 == 1&&READ_HW_OUT_4 == 0 )
	{
		printf("应该左转\r\n");
		motorPidSetSpeed(2,0.5);//左边运动
	}
	if(READ_HW_OUT_1 == 0&&READ_HW_OUT_2 == 0&&READ_HW_OUT_3 == 0&&READ_HW_OUT_4 == 1 )
	{
		printf("快速左转\r\n");
		motorPidSetSpeed(2.5,0.5);//快速左转
	}

然后测试

  1. 测试红外对管灵敏度,放在有黑线的地上或者纸上,然后把小车黑线比如放到最右边 及第一个红外对管,观察红外对管小灯变化情况和串口输出情况,如果小灯没有灭,就调节红外对管灵敏度和室内灯光,直到每个红外对管都可以感应到小灯。
  2. 然后在黑线上让小车循迹
    在这里插入图片描述

然后循迹功能完成

然后放到地上
在这里插入图片描述

13.2-加入循迹PID

前面的代码我们对循迹是判断的几个状态,然后PID控制电机不同速度,但是我们可以使用红外对管状态作为PID控制的输入然后再控制电机。

PID的输入是红外对管状态,我们设计 PID输入是红外对管的状态、然后输出一个速度值,然后左右电机去加或者减这个值,就可以完成根据红外对管输入对电机的差速控制

主函数添加的
在这里插入图片描述

extern tPid pidHW_Tracking;//红外循迹的PID
uint8_t g_ucaHW_Read[4] = {0};//保存红外对管电平的数组
int8_t g_cThisState = 0;//这次状态
int8_t g_cLastState = 0; //上次状态
float g_fHW_PID_Out;//红外对管PID计算输出速度
float g_fHW_PID_Out1;//电机1的最后循迹PID控制速度
float g_fHW_PID_Out2;//电机2的最后循迹PID控制速度

然后实现PID循迹控制、注意为了更加快,要减少没有必要的程序和优化判断、将没有必要的输出都注释掉
在这里插入图片描述

	g_ucaHW_Read[0] = READ_HW_OUT_1;//读取红外对管状态、这样相比于写在if里面更高效
	g_ucaHW_Read[1] = READ_HW_OUT_2;
	g_ucaHW_Read[2] = READ_HW_OUT_3;
	g_ucaHW_Read[3] = READ_HW_OUT_4;

	if(g_ucaHW_Read[0] == 0&&g_ucaHW_Read[1] == 0&&g_ucaHW_Read[2] == 0&&g_ucaHW_Read[3] == 0 )
	{
//		printf("应该前进\r\n");//注释掉更加高效,减少无必要程序执行
		g_cThisState = 0;//前进
	}
	else if(g_ucaHW_Read[0] == 0&&g_ucaHW_Read[1] == 1&&g_ucaHW_Read[2] == 0&&g_ucaHW_Read[3] == 0 )//使用else if更加合理高效
	{
//		printf("应该右转\r\n");
		g_cThisState = -1;//应该右转
	}
	else if(g_ucaHW_Read[0] == 1&&g_ucaHW_Read[1] == 0&&g_ucaHW_Read[2] == 0&&g_ucaHW_Read[3] == 0 )
	{
//		printf("快速右转\r\n");
		g_cThisState = -2;//快速右转
	}
	else if(g_ucaHW_Read[0] == 1&&g_ucaHW_Read[1] == 1&&g_ucaHW_Read[2] == 0&&g_ucaHW_Read[3] == 0)
	{
//		printf("快速右转\r\n");
		g_cThisState = -3;//快速右转
	}
	else if(g_ucaHW_Read[0] == 0&&g_ucaHW_Read[1] == 0&&g_ucaHW_Read[2] == 1&&g_ucaHW_Read[3] == 0 )
	{
//		printf("应该左转\r\n");
		g_cThisState = 1;//应该左转	
	}
	else if(g_ucaHW_Read[0] == 0&&g_ucaHW_Read[1] == 0&&g_ucaHW_Read[2] == 0&&g_ucaHW_Read[3] == 1 )
	{
//		printf("快速左转\r\n");
		g_cThisState = 2;//快速左转
	}
	else if(g_ucaHW_Read[0] == 0&&g_ucaHW_Read[1] == 0&&g_ucaHW_Read[2] == 1&&g_ucaHW_Read[3] == 1)
	{
//	    printf("快速左转\r\n");
		g_cThisState = 3;//快速左转
	}
	g_fHW_PID_Out = PID_realize(&pidHW_Tracking,g_cThisState);//PID计算输出目标速度 这个速度,会和基础速度加减

	g_fHW_PID_Out1 = 3 + g_fHW_PID_Out;//电机1速度=基础速度+循迹PID输出速度
	g_fHW_PID_Out2 = 3 - g_fHW_PID_Out;//电机1速度=基础速度-循迹PID输出速度
	if(g_fHW_PID_Out1 >5) g_fHW_PID_Out1 =5;//进行限幅 限幅速度在0-5之间
	if(g_fHW_PID_Out1 <0) g_fHW_PID_Out1 =0;
	if(g_fHW_PID_Out2 >5) g_fHW_PID_Out2 =5;
	if(g_fHW_PID_Out2 <0) g_fHW_PID_Out2 =0;
	if(g_cThisState != g_cLastState)//如何这次状态不等于上次状态、就进行改变目标速度和控制电机、在定时器中依旧定时控制电机
	{
		motorPidSetSpeed(g_fHW_PID_Out1,g_fHW_PID_Out2);//通过计算的速度控制电机
	}
	
	g_cLastState = g_cThisState;//保存上次红外对管状态

在pid.中
在这里插入图片描述

tPid pidHW_Tracking;//红外循迹的PID

在这里插入图片描述


	pidHW_Tracking.actual_val=0.0;
	pidHW_Tracking.target_val=0.00;//红外循迹PID 的目标值为0
	pidHW_Tracking.err=0.0;
	pidHW_Tracking.err_last=0.0;
	pidHW_Tracking.err_sum=0.0;
	pidHW_Tracking.Kp=-1.50;
	pidHW_Tracking.Ki=0;
	pidHW_Tracking.Kd=0.80;

然后就可以跑一下试试了。

可以改进的地方

  1. 红外对管影响差速转向,也影响基础直行的速度 ,会有更好控制效果,所以可以加入每种红外对管状态下对基础速度的影响。
  2. 红外对管的数量越多,效果会越好。

第15章我们会讲解手机遥控的功能

1. 小车开源笔记资料:
我用夸克网盘分享了「V3小车笔记网盘」,点击链接即可保存。打开「夸克APP」,无需下载在线播放视频,畅享原画5倍速,支持电视投屏。
链接:https://pan.quark.cn/s/959b889e058b
2.小车实物或者更多最新资料:
https://item.taobao.com/item.htm?id=811175824266&pisk=frNHu_4FKJkIvsTuuCcCEX7mzo69AHGSPud-2bnPQco69uhKwVlZk4dE8DRe7R4bqDQ5dXeiqlriYBF-9zrgok2pv6ipru4SXud8J726Zra79wGRRXaINbSADshuRyG7W7pYYWxa74awT0JeiYyTPbSAD_bhCncja8hTullQsYb2pHeVxW6nemxKmuePZQnZ0yoUJyFwHvJQ-cDxh5u3M0UFPQRzs2D3we3f5wHLUrMb9SCk1XU0Qfu34sSE_rygkvVhmBnSE5q3EkQJFV2uzSGI6U5U7Xmn35FFVTmZ_yHzZvjVQlHoxRPEIURz7fiKe8qhxpiUplDj7A8lGVExbyNUIata8onnTVkOgZ4ZURFIhWIwtjybJXezb1A30gJ2QKPbmQgNy5JWFvusSm3U67YDehXMzNbM3-ME525ASNvWFvusSmQGSK7sLVgFM&spm=a21xtw.29178619.product_shelf.3.6043fc79LdVbgq
3.小车最新视频:
https://www.bilibili.com/video/BV16x4y1M7EN/?spm_id_from=333.337.search-card.all.click

#include #include "qudong.h" #include "duankou.h" #include "lcd1602.h" #include "celv.h" #define uchar unsigned char #define uint unsigned int #define CPU_F ((double)8000000) #define delay_us(x) __delay_cycles((long)(CPU_F*(double)x/1000000.0)) #define delay_ms(x) __delay_cycles((long)(CPU_F*(double)x/1000.0)) //****************************************************************************** //全局变量 //****************************************************************************** float voltage=6.0; //实际电压值 int RS1=100; int LS1=100; int RS0=100; int LS0=100; char heixian=0x03; //不等于0x03表示检测到信号,等于0x03表示没检测到信号 char ji_shu=0; //表示第几次检测到黑线 char youxinhao=0; //=1表示检测到信号,=0表示没有检测到信号 uint total_time_count=0;//时间相关变量 uint start_low_speed=0; uint end_low_speed=0; uint end_all=0; uint start_all=0; uint LM_cap_new=0; //测速相关变量 uint LM_cap_old=0; uint LM_cap_count=0; uint LM_mai_kuai=0; float real_LM_speed=0.0; float top_speed=0; float average_speed=0; uint RM_cap_new=0; uint RM_cap_old=0; uint RM_cap_count=0; uint RM_mai_kuai=0; float real_RM_speed=0.0; float total_distance=0; //测量值转化为实际值相关变量 float dis1=0; float dis0=0; uint low_speed_time=0; uint total_time=0; //****************************************************************************** //系统时钟初始化,aclk=32768k,mclk=XT2,SMCLK = XT2。 //****************************************************************************** void Init_clock(void) { DCOCTL = DCO0 + DCO1 + DCO2; // Max DCO数字晶振最高频率 BCSCTL1 = RSEL0 + RSEL1 + RSEL2; // XT2on, max RSEL选择高速晶振最高频率 BCSCTL2 |=SELM_2+SELS; //MCLK=8M,SMCLK = XT2,系统主时钟选择高速晶振 do //清除振荡器失效标志,等振荡器稳定。 { IFG1&=~OFIFG; for(unsigned int i=0XFF;i>0;i--); } while((IFG1&OFIFG)!=0); } #pragma vector=ADC_VECTOR //ADC12 interrupt service routine __interrupt void ADC12_ISR (void) { if(ADC12MEM0<2816)P6OUT&=~BIT3; //欠压指示灯亮 else P6OUT|=BIT3; } //*************************
### STM32 红外 PID 循迹实现方法 #### 一、硬件准备 为了实现红外循迹功能,通常会采用多个红外传感器来检测地面的颜色变化。这些传感器通过特定的GPIO引脚连接到STM32微控制器上。根据给定的信息,红外对管依次连接至PB5、PB4、PB3、PA15[^1]。 对于涉及JTAG/SWD调试接口复用的功能引脚(PB3, PB4),应当注意查阅《STM32F103x8B_DS_CH_V10》中的引脚定义部分了解其默认配置;同时,《STM32中文参考手册_V10》第8.3.5节提供了有关如何重新映射这些引脚作为普通I/O端口的方法描述。 #### 二、软件设计思路 PID算法是一种广泛应用的比例积分微分控制策略,能够有效提高系统的响应速度和平稳度。针对本项目而言: - **比例项 (P)** :直接反映当前误差大小; - **积分项 (I)** :累积过去的偏差值以消除静态误差; - **微分项 (D)** :预测未来的趋势并提前调整输出量。 当车辆偏离预定轨迹时,通过对各个方向上传感器采集的数据计算得出偏移角度θ,并以此为基础构建PID调节模型,从而精确调控电机转速使车子回到正轨之上。 #### 三、具体实施步骤概述 由于禁止使用诸如“首先”之类的引导词,在此仅提供各环节的关键操作提示而不做顺序安排: - 定义全局变量用于存储KP,KI,KD参数以及历史误差记录。 - 编写ADC模块初始化函数完成模拟信号读取准备工作。 - 设计PWM波形发生子程序以便后续调用改变马达功率。 - 构建核心PID运算逻辑处理单元负责决策转向指令。 - 开发定时中断服务例程周期性触发上述流程执行。 #### 四、C语言源码片段展示 以下是简化版的PID控制循环体结构示意代码: ```c // 初始化PID系数和其他必要设置... float kp = 1.2f; float ki = 0.005f; float kd = 0.5f; int lastError = 0; void pidControl(void){ int error = getTrackPosition(); // 获取当前位置相对于中心线的位置差 static int integralSum = 0; integralSum += error * ki; int derivativeTerm = (error - lastError) * kd; lastError = error; int output = kp*error + integralSum + derivativeTerm; adjustMotorSpeed(output); // 根据计算结果调整左右轮的速度差异 } ```
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

好家伙VCC

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值