第13章-循迹功能 STM32智能小车循迹教程 PID循迹算法分析

电子说

1.3w人已加入

描述

功能介绍放开头, 使用便捷无需愁

这是全网最详细、性价比最高的STM32实战项目入门教程,通过合理的硬件设计和详细的视频笔记介绍,硬件使用STM32F103主控资料多方便学习,通过3万字笔记、12多个小时视频、20多章节代码手把手教会你如何开发和调试。让你更快掌握嵌入式系统开发。

**V3.3.0-STM32智能小车 **

**视频: **[https://www.bilibili.com/video/BV16x4y1M7EN/?spm_id_from=333.337.search-card.all.click]

V3:HAL库开发、功能:PID速度控制、PID循迹、PID跟随、遥控、避障、PID角度控制、视觉控制、电磁循迹、RTOS等功能。

讲解一下我们小车里面的循迹部分,包括红外基础使用,无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("应该前进rn");
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("应该右转rn");
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("快速右转rn");
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("应该左转rn");
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("快速左转rn");
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("应该前进rn");//注释掉更加高效,减少无必要程序执行
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("应该右转rn");
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("快速右转rn");
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("快速右转rn");
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("应该左转rn");
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("快速左转rn");
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("快速左转rn");
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章我们会讲解手机遥控的功能

审核编辑 黄宇

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分