目录
前言
一、电磁车的电感排布方案
二、如何调整电感的ADC数值+滤波方法
三、电磁四轮车的一些基础控制算法
1.差比和算法+方向环
2.电机的速度环控制算法
小结
2022年由于疫情的缘故,导致制作智能车的周期变得很短,线下赛也变成了线上赛,期间遇到了许多的困难,所幸的是得到了实验室小伙伴的帮助,在两个月内完成了比赛,取得了省级二等奖。比赛的那短短十几分钟涵盖了不知多少个日日夜夜,做智能车的这段时间收获颇丰,这也将是我大学生涯最难忘的一段日子。本文在此对我这次比赛做一个总结,本文内容仅自己看法,如有错误还请多多包涵。
受限于stc16f的ADC管脚的数目,当时采取的是两横,两竖,两内八的方案,这里详细说明一下各种电感的作用。
横电感:这种电感利于在直道上面循迹,但是对弯道的变化感知低于斜电感和竖直电感
竖电感:在直道上面的数值理论上来说几乎为0,但对于不同的车来说情况可能不太一样,有点可能为0,有的不为0,甚至偏差很大。这种电感同样对弯道有较强的感知,但比斜电感弱一点。
斜电感:这种电感对于弯道感知非常强烈,利于判断环岛,弯道等元素,还可以用来补线,但不利于单独用它来循迹。
根据我的比赛经验,电感的排布方案以及电感的位置需要根据赛道情况,电感类型,控制算法等因素不断调整,如果照搬照抄,后期提速环节可能会非常受限制。
在小车硬件搭好以后,可以把电感垂直放在电磁线上,通过对运放的调节改变数值,注意,该数值得到的是电感的最大值。
一般来说,最佳的调节状态就是把小车来回摆动,这时通过上位机观察一对电感的变化波形,如果一对电感的上下浮动相对称,则说明调节的很好。
要想得到上面的那种波形,给电感采集到的值进行滤波必不可少,常见的方法有:滑动均值滤波,中位均值滤波,一阶互补滤波等,下面给出中位均值滤波的方法以供读者参考:
//冒泡排列
for(j = 0; j < 10 - 1; j++)
{
for(i = 0; i < 10 - 1 - j; i++)
{
if(Left[i] > Left[i + 1])
{
filter_temp = Left[i];
Left[i] = Left[i + 1];
Left[i + 1] = filter_temp;
}
}
}
//去掉最大以及最小值
for(i = 1; i < 10 - 1; i++)
{
adc[0] += Left[i];
}
//取平均值
adc[0]=adc[0]/8;
void Direction_out(void) //方向环控制器
{
direct.now_err=(20*(0.8*(adc[3]-adc[1])+0.2*(adc[5]-adc[2])))/(0.8*
(adc[3]+adc[1])+0.2*myabs(adc[5]-adc[2]));
//限幅
if(direct.now_err>40) direct.now_err=40;
else if(direct.now_err<-40) direct.now_err=-40;
direct.sum_err=direct.kp*direct.now_err+direct.kd*(direct.now_err-direct.last_err);
price_pwm=direct.sum_err/2;
if(price_pwm>180) price_pwm=180;
if(price_pwm<-180) price_pwm=-180;
}
direct.now_err为差比和算法得出的特征值,该值为距离中线的偏差,下面简单讲述一下该差比和算法:
其中A,B,C,P,均为可调系数,可根据自己小车的实际情况来一个一个调节。以此来获得小车对赛道情况的良好感知。
price_pwm为方向环PD控制得出的参量,通过放缩来传给舵机驱动函数,其中P为比例系数,D为微分系数,由于是提供给舵机控制的参量,所以不太需要积分系数。
/*舵机参数*/
int steer_mid_value=490; //舵机中值
void duoji_run(int angl) //驱动舵机函数
{
uint16 PWM1_duty;
PWM1_duty = steer_mid_value + angl; //duty占空比为490时,舵机居中
if(PWM1_duty > 595)PWM1_duty = 595; //限幅
if(PWM1_duty < 380)PWM1_duty = 380;
pwm_duty(PWMB_CH1_P74,PWM1_duty); //PWMB设置占空比
}
至于PID如何调节,可以参考一下方法:
第一步是先把微分的参数设置成 0,把比例项按照最大的占空比除以最大的
误差系数在乘上 0.1 作为调试的起点;第二步根据系统的状态增大比例项,直到
系统能转过急弯;第三步增加微分项,系统出现比较稳定时候开始下一步;第四
步是同时增减比例项和微分项,直到智能车能平稳地在直道上运行且顺滑地转过
弯道;第五步把参数稍微调小到刚好振荡,并将车速缓慢提升,完成控制参数调
试。
由于是双电机,后续会采用差速控制,所以必须用两个速度环控制两个电机,注意,这里的两个速度环指的是变量分离,所使用的PID控制参数是一致的,如果不一样,那参数整定起来会非常麻烦。下面以左电机为例:
int16 PID_IncreaseL(PID *SPID,int16 NextPoint,int16 NowData)
{
static int iErrorL,iIncpidL,iIncpidL_last;
SPID->PrevErrorL = SPID->LastErrorL; //存储误差,用于下次计算
SPID->LastErrorL = iErrorL; //当前误差
iErrorL = NextPoint - NowData; //增量计算
iIncpidL = (int16)((SPID->Integral) * iErrorL //E[k]项
+(SPID->Proportion ) * (SPID->LastErrorL-SPID->PrevErrorL) //E[k-1]项
+(SPID->Derivative) * (iErrorL-2*(SPID->PrevErrorL+SPID->LastErrorL))); //E[k-2]项
return(iIncpidL); //返回增量值
}
------------------------------------------------------------------------------
void PID_InitL(PID *SPID,float Ep,float Ei,float Ed)
{
SPID->SumErrorL = 0;
SPID->LastErrorL = 0; //Error[-1]
SPID->PrevErrorL = 0; //Error[-2]
SPID->Proportion = Ep; //比例常数 Proportional Const
SPID->Integral = Ei; //积分常数 Integral Const
SPID->Derivative = Ed; //微分常数 Derivative Const
}
int Speed_outL(int EncoderL,int TargetL) //左轮速度环控制器
{
PID_InitL(&QPID,speed.kp,speed.ki,speed.kd);
speed_pwmL+=PID_IncreaseL(&QPID,TargetL,EncoderL);
if( speed_pwmL > 8000 ) //增量限幅
{
speed_pwmL = 8000;
}
if( speed_pwmL < -8000 )
{
speed_pwmL = -8000;
}
speed_pwmL_last = speed_pwmL;
return speed_pwmL; //增量输出
}
这里的速度环采用的是增量式PID控制,增量式PID的好处是它所得到的反馈仅和过去那一时刻有关,得到的是累加量,非常适合电机这种被控制器,值得注意的是增量式中的P等同于位置式中的I,这一点可以对比两者的公式发现.下面给出位置式PID和增量式PID的公式,读者可以自行对比.
位置式PID
增量式PID
以下是一些电机驱动函数的代码,控制逻辑基于8701的电机驱动.
void dianji_run(int example1,int example2) //驱动电机函数
{
if(super.start==1)
{
dutyL=Speed_outL(num_left,example1);//num_left为编码器采集的读数,example1为期望速度
dutyR=Speed_outR(num_right,example2);
if(dutyL >= 0) //正转
{
DIR_1 = 0;
pwm_duty(PWMA_CH2P_P62,dutyL);
}
else //反转
{
DIR_1 = 1;
pwm_duty(PWMA_CH2P_P62,-dutyL);
}
if(dutyR >= 0) //正转
{
DIR_2 = 0;
pwm_duty(PWMA_CH4P_P66,dutyR);
}
else //反转
{
DIR_2 = 1;
pwm_duty(PWMA_CH4P_P66,-dutyR);
}
}
else
{
dutyL=Speed_outL(num_left,0);
dutyR=Speed_outR(num_right,0);
if(dutyL >= 0) //正转
{
DIR_1 = 0;
pwm_duty(PWMA_CH2P_P62,dutyL);
}
else //反转
{
DIR_1 = 1;
pwm_duty(PWMA_CH2P_P62,-dutyL);
}
if(dutyR >= 0) //正转
{
DIR_2 = 0;
pwm_duty(PWMA_CH4P_P66,dutyR);
}
else //反转
{
DIR_2 = 1;
pwm_duty(PWMA_CH4P_P66,-dutyR);
}
}
}
本文对电磁四轮车的一些基础控制算法以及制作思路作出了说明以供读者参考,其中许多算法的细节还可以再优化,考虑到简洁性就不再过多阐述.后续会更新到闭环控制的原理以及电磁的识别算法等等.