一、必须明确的PID前提
在电赛中,我们常常通过传感器的状态值pid控制电机运动状态以实现循迹。听着有点抽象,举个例子,就是右边的传感器检测到了,说明小车在黑线偏左面,让小车向右偏一点就要让左轮加速,右轮减速。
现实情况往往更复杂,跑道不同,传感器位置不同,实现循迹的算法也不同。
pid的本质是通过当前状态以及理想状态之间的计算输出一个控制量。首先要明确你的理想状态与当前状态是什么,如何数学的表示数来,下面举一个简单的例子:
二、pid的具体应用
1.单环循迹pid
循一条线,使用的为7路传感,且由于传感器位置与跑道粗细的原因,任何时刻只有一个传感器识别到黑线(高电平):
思路如下:
1.目标状态为:中间灰度输出高电平
2.当前状态为:某一个传感器输出高电平。
3.控制量:将计算结果加在右轮速度 减在左轮速度,实现差速循迹。当不同位置的传感器识别到,差速值也不同。(很好理解,最外侧检测到黑线,差速大一点快速实现回正)
展现在代码上该如何实现呢,我们对传感器从右往左,通过一个数组记录每个传感器状态,1为黑线,0为其它;为了能体现出不同位置出检测到黑线,差速值不同,再为其进行赋权重:
-50/ -25/ -10/ 0/ 10/ 25/ 50
即当对应位检测到黑线时,用对应的权值记为当前值进行pid计算。理想状态是最中间检测到黑线其权值是0;
对于控制量,可根据需要选择;在这里,可以直接选择在PWM控制水平的差值,即pid结果直接体现在输出捕获寄存器上。(也可以如果期望速度是0.5m/s,控制量+/-在0.5水平上体现,将权重缩小一点(比如小数点左移),这个比较麻烦一点,本文后面会讲到;)
循迹部分
float g_Track_ThisState = 0; //当前小车权重
PID g_Track_Pid; //红外pid参数
uint8_t g_TrackState_array[7];
float g_TrackStateWeight_array[7] = { -50, -25, -10, 0, 10, 25, 50};
uint8_t g_TrackStateOrder_array[7] = {99};
float pwma,pwmb;
void Check_Track_State()
{
g_TrackState_array[0] = Right1;
g_TrackState_array[1] = Right2;
g_TrackState_array[2] = Right3;
g_TrackState_array[3] = Middle0;
g_TrackState_array[4] = Left3;
g_TrackState_array[5] = Left2;
g_TrackState_array[6] = Left1;
int i = 0, j = 0;
for (i = 0; i < 8; i++)
{
if (g_TrackState_array[i] == BLACK_LINE)
{
g_TrackStateOrder_array[j] = i;
j++;
}
}
}
void Track()
{
Check_Track_State();
g_Track_ThisState = g_TrackStateWeight_array[g_TrackStateOrder_array[0]];
PID_Calc(&g_Track_Pid, g_Track_ThisState, 0);
pwma = g_Inital_Speed + g_Track_Pid.output;
pwmb = g_Inital_Speed - g_Track_Pid.output;
Set_Pwm(pwma,pwmb);
}
#include "main.h"
float g_Inital_Speed = 2000; // 小车初始速度
int timer10ms_flag=0;
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //硬件部分初始化
Motor_gpio_Init() ;
Motor_pwm_Init(7199,0);
Encoder_Init_Tim2();
Encoder_Init_Tim3();
huidu_init();
key_init();
int key_mode=OPEN; //控制代码运行状态
int key_state=0;
PID_Init(&g_Track_Pid, 10 , 0.0000, 0, 1000, 1000, 0);//pid参数
while(1)
{
if(key_mode==OPEN)
{
key_state=key_getnum();
while(key_state==1)
{
Timer1_Init(7199,99);
if(timer10ms_flag==1)
{
Track();
timer10ms_flag=0;
}
}
}
}
}
int TIM1_UP_IRQHandler(void)
{
if(TIM_GetFlagStatus(TIM1,TIM_FLAG_Update)==SET)//5ms定时中断
{
timer10ms_flag=1;
TIM_ClearITPendingBit(TIM1,TIM_IT_Update);
}
return 0;
}
不过要说明一点,当前状态的权重由是7个传感器中从右往左第一个识别到黑线的位置权重决定;所以展示视频中可以看到小车面对岔路口时选择内圈;可是如果我改成由从左往右第一个识别黑线的传感器权重为当前权重,结果依然是走内圈,经过排错发现,这跟灰度的灵敏度有很大关系以及小车的结构与外界环境有关,左面的灰度灵敏度大于右面的灰度灵敏度,且右面灰度差速值比较弱。
直线循迹展示