(一)电磁处理
在此次智能车竞赛中,我选择了摄像头为主,电磁简单辅助路径规划,帮助车身在出弯后能快速回正,只不过后来因为赛场电磁线原因,关闭了电磁辅助,采用了纯摄像头循迹。
关于电磁循迹,我并没有使用归一化,只是简单的差比和处理
Diangan dg;
void Read_ADC()
{
int16 i,j,temp;
static int16 value_temp[5][5]={0}; //行:五个电感 列:每个电感自己五组均值
int16 value_sum[5]={0};
int16 value_ave[5]={0};
for(j=0;j<5;j++)
{
value_temp[0][j] = adc_mean_filter(ADC_1,ADC1_CH10_B21,5); //5次采样,取平均值
value_temp[1][j] = adc_mean_filter(ADC_1,ADC1_CH7_B18,5);
value_temp[2][j] = adc_mean_filter(ADC_1,ADC1_CH8_B19,5);
value_temp[3][j] = adc_mean_filter(ADC_1,ADC1_CH5_B16,5);
value_temp[4][j] = adc_mean_filter(ADC_1,ADC1_CH6_B17,5);
}
/*=======================冒泡排序=================================*/
for(i=0;i<5;i++)
{
for (j=0;j<4;j++)
{
if(value_temp[i][j]>value_temp[i][j+1])
{
temp = value_temp[i][j];
value_temp[i][j] = value_temp[i][j+1];
value_temp[i][j+1] = temp;
}
}
}
/*============中值滤波==========================================*/
//求中间三项的和 舍去每个电感采集的五组值中最大最小值,剩余求平均
for(i=0;i<5;i++)
{
value_sum[i] = value_temp[i][1] + value_temp[i][2] + value_temp[i][3];
value_ave[i] = (int)(value_sum[i]/3);
}
dg.Middle_vlaue = value_ave[4];
for(i=0;i<9;i++)
{
value_ave[i] = value_ave[i]/10*10;
}
dg.Left_heng = value_ave[0]; //B21
dg.Left_shu = value_ave[1]; //B18
dg.Right_heng = value_ave[3]; //B16
dg.Right_shu = value_ave[2]; //B19
dg.Middle_heng = value_ave[4]-1000; //B17
}
void ADC_deal()
{
static float LevelTemp[5]; // 最近5次 差比和保存
static float shuzhiTemp[5];
// static float ad_M_max=3400;
// static float ad_M_min =2800;
dg.shuiping_error = (int)((float)( dg.Left_heng - dg.Right_heng )/( dg.Left_heng +
dg.Right_heng )* 100);//水平电感差比和
dg.shuzhi_error = (int)((float)( dg.Left_shu - dg.Right_shu )/( dg.Left_shu +
dg.Right_shu )* 100);//内八电感差比和
LevelTemp[4] = LevelTemp[3];
LevelTemp[3] = LevelTemp[2];
LevelTemp[2] = LevelTemp[1];
LevelTemp[1] = LevelTemp[0];
LevelTemp[0] = dg.shuiping_error;
dg.shuiping_error_d =(int)(LevelTemp[0]-LevelTemp[3]);//水平电感偏差微分
shuzhiTemp[4] = shuzhiTemp[3];
shuzhiTemp[3] = shuzhiTemp[2];
shuzhiTemp[2] = shuzhiTemp[1];
shuzhiTemp[1] = shuzhiTemp[0];
shuzhiTemp[0] = dg.shuzhi_error;
dg.shuzhi_error_d = (int)(shuzhiTemp[0]-shuzhiTemp[3]);//竖直电感偏差微分
//
// if(dg.Middle_vlaue<ad_M_min) ad_M_min = dg.Middle_vlaue;
// if(dg.Middle_vlaue>ad_M_max) ad_M_max = dg.Middle_vlaue;
// dg.Middle_heng=(float)(dg.Middle_vlaue-ad_M_min)/(ad_M_max-ad_M_min);
// dg.mid_normalized =(int)(100*dg.Middle_heng);
}
关于我的误差值,0.7*(摄像头误差)+0.4(电感误差):
car.Error=(int)((SteerSum)*0.7+dg.shuiping_error *0.4);
(二)陀螺仪使用
对于陀螺仪的使用,在我做比赛前,以为陀螺仪除双车组别,在其他组别上,也许只有在坡道上能用到,其实不然,陀螺仪在比赛中,可以辅助通过三个轴传回来的值,计算偏差的角度,在过弯后通过Z轴数值辅助车身快速回正,在出入圆环以及进出车库时,也可以通过对陀螺仪进行积分判断条件。
//简单的mpu6050转换
gyro_x = ((float)mpu_gyro_x)*0.5f)/(16.384f);
gyro_y = ((float)mpu_gyro_y)*0.5f)/(16.384f);
gyro_z = ((float)mpu_gyro_z)*0.5f)/(16.384f);
(三)编码器闭环
曾看到一位大神说,电机闭环随便调两天,就会比开环跑的快。可见闭环控制的重要性,对于电机的闭环控制,主要通过设定的速度于编码器返回的速度进行做差,再进行pid计算,关于pid的讲解,网上无论是开源还是讲解都有很多优秀的文章,我就不过多讲解。这里我贴出我比赛的增量式pid代码,正对不同的赛道元素,我们可以设定不同的速度。
void Get_speed()
{
//读取编码器计数值
Speed.left_encoder = -qtimer_quad_get(QTIMER_1,QTIMER1_TIMER2_C2);
Speed.right_encoder = qtimer_quad_get(QTIMER_1,QTIMER1_TIMER0_C0);
qtimer_quad_clear(QTIMER_1,QTIMER1_TIMER0_C0);
qtimer_quad_clear(QTIMER_1,QTIMER1_TIMER2_C2);
Speed.encoder_l=(int32)(Speed.left_encoder+0.00001);
Speed.encoder_r=(int32)(Speed.right_encoder+0.00001);
Speed.encoder = (int32)((Speed.left_encoder + Speed.right_encoder + 0.00001)/2);
}
int16 error0,error1,error2;
void motor_pid()
{
Get_speed();
float P,I,D;
//左电机
error0 = error1 = error2 = 0;
error0 = targrt_speed - Speed.encoder_l;//I
error1 = error0 - pidl.lastError;//P
error2 = error0 - 2*pidl.lastError + pidl.preError;//D
pidl.preError = pidl.lastError;
pidl.lastError = error0;
P=speed_P; I=speed_I; D=speed_D;
pidl.PWM += (int16)(P*error1 + I*error0 + D*error2);
if(pidl.PWM > 30000) pidl.PWM = 30000;
if(pidl.PWM < -9999) pidl.PWM = -9999;
//右电机
error0 = error1 = error2 = 0;
error0 = targrt_speed - Speed.encoder_r;//I
error1 = error0 - pidr.lastError;//P
error2 = error0 - 2*pidr.lastError + pidr.preError;//D
pidr.preError = pidr.lastError;
pidr.lastError = error0;
P=speed_P; I=speed_I; D=speed_D;
pidr.PWM +=(int16)(P*error1 + I*error0 + D*error2);
if(pidr.PWM > 30000) pidr.PWM = 30000;
if(pidr.PWM < -9999) pidr.PWM = -9999;
pidl.PWM += (int16)(P*error1 + I*error0 + D*error2);
pidr.PWM +=(int16)(P*error1 + I*error0 + D*error2);
Motor_pwm_out(pidl.PWM,pidr.PWM );
}
对于电机pid如何调节,我们可以通过山外的上位机,通过串口发送波形,更改三个值的大小,直至波形完美为止
//串口发送至上位机
void display_sendware()
{
int8 a[6]={0};
a[0]=targrt_speed;
a[1]=targrt_speed>>8;
a[2]=(int16)Speed.left_encoder;
a[3]=(int16)Speed.left_encoder>>8;
a[4]=(int16)pidl.PWM;
a[5]=(int16)pidl.PWM>>8;
vcan_sendware((uint8*)a,6);
}
大概的波形调至到这种形态就可以了
因为手上没有调试的波形,所以只能大概画出来,红色为你设定的速度,蓝色为编码器返回的值,当打开电机时,编码器值能快速反应并迅速在设定好的速度周围很小的范围震荡。
(四)检测斑马线
对于摄像头采集图像,可以通过代码判断,但如果是纯电磁判断,可以使用干簧管以及红外对管判断,但是由于硬件原因,似乎只能倒车入库,且准确性不高,在本次比赛中,我看到有的学校使用激光模块识别,可以实现判断车库,不需要倒车入库,对于电磁的同学可以尝试。
(五)总结
如果有人已经看到这里,当你决定做智能车比赛时,我希望你能懂得,智能车竞赛与其他学科竞赛不同,它所需投入的时间要很多,且你最后取得的比赛成绩可能也与你投入的时间不成正比,除去时间,一个好的队友也及其重要,他可以不是那个硬件和软件最厉害的人,但一定是那个可以一直和你坚持下去的那个,我很荣幸我在这次比赛中有一个很好的队友。
最后,当有一个你投入了几个月时间的比赛却没有达到你想要的成绩时,心中会产生巨大的落差感。但如果有人问我你是否会后悔参加这个比赛时,我还是会说,不后悔。你所有的比赛经历都会转变成自己的成果沉淀,像荷花一样,也许有人已经于水面上盛放结果,或许属于你的还没有浮出水面,但深埋在淤泥之下的也是你积累的果实,相信它总有收获的那一天。加油,智能车人!
评论(0)
您还未登录,请登录后发表或查看评论