前言:最近一段时间在准备智能车考核,其中要求使用TC264单片机实现PID控制的小车摄像头循迹。其中关于PID的部分在之前我已经上传过了,这篇文章主要讲怎么实现循迹与舵机的位置式PID调参和电机的增量式调参的一些心得。
一、摄像头循迹的实现
首先我们要明白英飞凌公司旗下的小钻风摄像头为灰度摄像头,它收集到的图像信号为黑白灰像素点构成的图像,那我们想要小车能识别到赛道边缘从而实现循迹,就需要对采集到的图像进行二值化处理。
那么什么叫二值化处理呢,下面我结合代码进行说明。
int16 i,j;
uint8 mt9v03x_image_2[120][188];
uint8 th=100;//阈值大小
mt9v03x_init();//摄像头初始化
tft180_set_dir(TFT180_CROSSWISE_180); // 需要横屏不然显示不下
tft180_init();//屏幕初始化
if(mt9v03x_finish_flag)//这个if函数内部进行对采集到的每一帧画面进行处理
{
erzhihua();//二值化函数
tft180_displayimage03x((const uint8 *)mt9v03x_image_2, 160, 128);//将处理的图像显示出来
mt9v03x_finish_flag = 0;
}
void erzhihua(void)
{
//说明:摄像头采集的图像会以数组的形式存起来,其中数组的里面的数据为0~255的uint8型的数,其中0为黑色,255为白色
int i,j;
for(i=0;i<MT9V03X_H;i++)
{
for(j=0;j<MT9V03X_W;j++)
{
mt9v03x_image_2[i][j] = mt9v03x_image[i][j];
}
}//为防止二值化对原始数据的破坏导致不能正常输出,我们复制了一个一样的数组,对其进行操作
for(i=0;i<MT9V03X_H;i++)//行遍历
{
for(j=0;j<MT9V03X_W;j++)//列遍历
{
if(mt9v03x_image[i][j]<th)
{
mt9v03x_image[i][j]=0;
mt9v03x_image_2[i][j]=0; //小于阈值变黑
}
else
{
mt9v03x_image[i][j]=255;
mt9v03x_image_2[i][j]=255; //大于阈值变白
}
}
}
}
简单来说二值化就是将黑白灰图像转化为只有黑白的图像,通过阈值的合理设定达到对赛道边缘的黑化(便于检测)的效果。
但是二值化仅仅使得我们知道了赛道边缘在哪里,而我们想要小车保持在赛道中间,就需要控制转向的舵机的配合。具体怎么配合呢?
首先,我们要将得到二值化图像进行处理,将赛道的中线表示出来。此时这个所谓的赛道中线其实本质上就是一串数组对应的宽和高。通过将得到的赛道中线的水平位置与屏幕显示的中间位置(就是数组的宽的一半)进行比较,算出小车与赛道中心的误差值,将这个误差值输入到舵机的PID中进行计算,最后输出PWM给舵机进行转向。
具体代码如下
int16 i,j;
uint8 mt9v03x_image_2[120][188];
uint8 th=100;//阈值大小
uint8 mid[120];
int side_left[120],side_right[120];
void erzhihua(void)
{
int i,j;
for(i=0;i<MT9V03X_H;i++)
{
for(j=0;j<MT9V03X_W;j++)
{
mt9v03x_image_2[i][j] = mt9v03x_image[i][j];
}
}
for(i=0;i<MT9V03X_H;i++)
{
for(j=0;j<MT9V03X_W;j++)
{
if(mt9v03x_image[i][j]<th)
{
mt9v03x_image[i][j]=0;
mt9v03x_image_2[i][j]=0; //小于阈值变黑
}
else
{
mt9v03x_image[i][j]=255;
mt9v03x_image_2[i][j]=255; //大于阈值变白
}
}
}
}
void xunji_Mid(void)
{
erzhihua();
int i,j;
for(i=0;i<MT9V03X_H;i++)
{
for(j=80;j<MT9V03X_W;j++)
{
if( mt9v03x_image_2[i][j]==0)
{ side_right[i]=j;break;}//从屏幕中间开始向两边寻找黑点,找到就记下黑点的横坐标
}
for(j=80;j>=0;j--)
{
if( mt9v03x_image_2[i][j]==0)
{ side_left[i]=j;break;}
}
mid[i]=(side_right[i]+side_left[i])/2;//赛道中线就是左右端点之和除以二
}
for(int i=120;i>70;i--)
{
feedbackpositionleijia+=mid[i];
}
feedbackposition= feedbackpositionleijia/50;//为提高数据可信度,减小误差,将50个中点求和算平均值
feedbackpositionleijia=0;
}
void show_line(void)//将中线与边界线在显示的图像中描黑
{
int i;
for(i = MT9V03X_H-1; i >= 0; i--)
{
//画赛道中线
if(mid[i] < MT9V03X_W && mid[i] >= 0)
{
mt9v03x_image_2[i][side_left[i]] = 0;
mt9v03x_image_2[i][side_right[i]] = 0;
mt9v03x_image_2[i][mid[i]] = 0;
}
}
}
if(mt9v03x_finish_flag)
{
erzhihua();
xunji_Mid();
show_line();
tft180_displayimage03x((const uint8 *)mt9v03x_image_2, 160, 128);
mt9v03x_finish_flag = 0;
}//最后写在主函数循环里面的显示函数
PID_Calc_wz(&mypid_Angle,targetposition,feedbackposition);//位置式pid控制舵机
int angleout=dutyzhong+mypid_Angle.output;
pwm_set_duty(SERVO_MOTOR_PWM,angleout);
int dutyzhong=720;//每个舵机的中点对应的PWM占空比不同,一般在750上下
float feedbackposition;
float feedbackpositionleijia;
float targetposition=80;//这里获取小车目标位置
void PID_Init(PID *pid,float p,float i,float d,int16 maxI,int16 maxOut)
{
pid->kp=p;//调参只要改动这三个值就行p,i,d
pid->ki=i;
pid->kd=d;
pid->maxIntegral=maxI;//最大积分
pid->maxOutput=maxOut;//最大输出值
}
进行一次位置式pid计算
参数为(pid结构体,目标值,反馈值),计算结果放在pid结构体的output成员中
void PID_Calc_wz(PID *pid,float reference,float feedback)
{
//更新数据
pid->lastError=pid->error;//将旧error存起来
pid->error=reference-feedback;//计算新error
//计算微分
int dout=(pid->error-pid->lastError)*pid->kd;//误差的差值乘以Kd
//计算比例
int pout=pid->error*pid->kp;//误差乘以Kp
//计算积分
pid->integral+=pid->error*pid->ki;//误差的累加乘以Ki
//积分限幅
if(pid->integral > pid->maxIntegral) pid->integral=pid->maxIntegral;//积分过大则限幅赋值为max
else if(pid->integral < -pid->maxIntegral) pid->integral=-pid->maxIntegral;//积分过小则赋值为min
//计算输出
pid->output=pout+dout+pid->integral;//输出值为P+I+D
//输出限幅
if(pid->output > pid->maxOutput) pid->output=pid->maxOutput;
else if(pid->output < -pid->maxOutput) pid->output=-pid->maxOutput;
if(pid->output < pid->minOutput) pid->output=pid->minOutput;
}
二、PID调参技巧
位置式PID:先调Kp,使得波形在目标值上下震荡,且幅度会逐渐减小。接下来就加入Kd来对震荡进行抑制,当Kd过大时就没有抑制作用,反而会使得波形不停震荡。最后加入Ki来使得最后趋于稳定的值更趋近于目标值。当你初步调节完成PID参数时,先记录下此时的参数,然后回过去继续微调Kp、Kd、Ki。直到最后波形达到一定的响应与稳定时才算调好了参数。而且每个电机的PID参数不尽相同,需要单独调节。
增量式PID:因为由于公式为增量,Ki对应的量才是error,所以先调Ki。接着再调Kp,最后调Kp。具体调到什么情况合适参考位置式PID。文章来源:https://www.toymoban.com/news/detail-763803.html
可以用vofa这个软件,用电脑这个上位机来实时显示波形,便于我们的调参。文章来源地址https://www.toymoban.com/news/detail-763803.html
到了这里,关于关于TC264单片机与智能车摄像头循迹的一些学习心得的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!