智能车之PID

放假前答应学长写一篇关于pid控制电机的文章,耽搁了几天着手开始写。首先感谢学长带我走上飞思卡尔智能车比赛的路,万分感谢。
我们要了解pid是什么,就得知道pid是干什么的。因为没有通过专业的学过pid的用法,一切都是新鲜的,我只能用我的理解简略的介绍一下pid的用处。我们在做智能车比赛,做车干什么的,让他跑啊,不光得会跑,还得跑的快,不光跑的快,还得跑的稳。这个pid就是让我们的小车跑的又快又稳的利器,当然调的好是利器,调不好····· 我们希望我们的车子以一个恒定的速度去跑,但是我们的车子总归会过弯,上坡,也会遇到种种问题,我们给车子一个目标速度,当车子低于这个目标速度的时候,我们就让车子加速,目标速度与实际速度的差值越大,车子的加速度就会越大,反之越小。这就是简单的pid控制的应用,我们也可以使用pid控制舵机,让两个电机产生差值即让两个后轮子产生差速,辅助舵机过弯。
我们再系统的讲一下pid什么,下面的原理是从网络上找的的,也可以查看这几篇文章形象解释pidpid算法详解
强烈推荐看一下这几个文章,而且也要多在网上找一些文章自己琢磨,这是不可缺少的一步
首先我们知道PID是一个控制系统,pid的应用有很多,当然这里我们只是对与pid对于电机的控制。比例、积分和微分的线性组合,构成控制量u(t),称为:比例(Proportional)、积分(Integrating)和微分(Differentiation)控制,简称PID控制。比例作用P只与偏差成正比,积分作用I是偏差对时间的累积,而微分作用D是偏差的变化率。用一句形象的比喻,比例P代表着现在,积分I代表着过去,而微分D则代表着未来。
pid的数学模型
我们来看pid的控制系统图
pid控制系统
关于P、I、D三个环节的选择,一般只会用到PI控制或者PD控制,例如速度控制要求稳态无误差,那么就需要积分环节,所以使用PI控制;而使用方向控制的时候,由于不需要无稳态误差,所以使用PD控制即可,D的作用是消除P环节所带来震荡,比如舵机的控制就可以使用pd控制。
我们接触过pid的人就知道pid控制存在增量式和位置式,对于两者的区别一直搞不清楚,可以阅读pid算法中位置型和增量型有什么区别,分析两者优缺点查阅。

typedef struct PID
{
intSetPoint; //设定目标 DesiredValue
longSumError; //误差累计
doubleProportion; //比例常数Proportional Const
doubleIntegral; //积分常数 IntegralConst
doubleDerivative; //微分常数Derivative Const
intLastError; //Error[-1]
intPrevError; //Error[-2]
} PID;
static PID sPID;
static PID *sptr = &sPID;
/
*=============================================================
=======
InitializePID Structure PID 参数初始化
=============================================================
======*/
void IncPIDInit(void)
{
sptr->SumError= 0;
sptr->LastError= 0; //Error[-1]
sptr->PrevError= 0; //Error[-2]
sptr->Proportion= 0; //比例常数Proportional Const
sptr->Integral= 0; //积分常数IntegralConst
sptr->Derivative= 0; //微分常数Derivative Const
sptr->SetPoint= 0;
}

/
*=============================================================
======= 增量式PID 计算部分
=============================================================
=======*/
int IncPIDCalc(int NextPoint)
{
registerint iError, iIncpid; //当前误差
iError= sptr->SetPoint - NextPoint; //增量计算
iIncpid= sptr->Proportion * iError //E[k]项
        -sptr->Integral * sptr->LastError //E[k-1]项                     
        +sptr->Derivative * sptr->PrevError; //E[k-2]项

//存储误差,用于下次计算
sptr->PrevError= sptr->LastError;
sptr->LastError= iError;
//返回增量值
return(iIncpid);
}

int16 RightDuty = 0,LeftDuty = 0;						//占空比承载参量
int error_0_r=0,d_error_r=0,dd_error_r=0;              	//电机误差过渡参量
int vi_e_r=0,vi_d_r=0; 
int error_0_l=0,d_error_l=0,dd_error_l=0;              
int vi_e_l=0,vi_d_l=0;

float SpeedKp=30;                                      	//电机PID参数35
float SpeedKi=0; //                                    	//10
float SpeedKd=250;
** 函数名称: void MotorControl(void)
** 功能描述: 电机速度控制函数
** 输    入:
** 输    出: 
** 说明:正交解码获取编码器读数后 pid进行调节
***************************************************/
void MotorControl(void)
{
	volatile int16 right_pulseval = 0,left_pulseval = 0;
	
    right_pulseval = FTM_QUAD_get(FTM2);                    	//获取FTM 正交解码 的脉冲数(负数表示反方向)
	FTM_QUAD_clean(FTM2);                              			//清空正交解码计数寄存器
	
    left_pulseval = lptmr_pulse_get();          				//获取脉冲计数值	
	lptmr_pulse_clean();                   						//清空脉冲计数器计算值
	
	
	if(right_pulseval < 0)										//脉冲计数这一路依靠另一路的正交解码获取方向
	{
		left_pulseval = -left_pulseval;
	} 
	if(STOP_flag||Endline_flag)                         		//遥控器或者终点线停车
	{
		R_prespeed_speed=0;  
		L_prespeed_speed=0;
	}	
	
    error_0_r = R_prespeed_speed - right_pulseval ;             //期望脉冲数-实际脉冲数
    d_error_r = error_0_r - vi_e_r;                           	//本次误差-上次误差
    dd_error_r = d_error_r - vi_d_r;                          	//本次误差-上次误差-(上次本次误差-上次上次误差)
    vi_e_r = error_0_r;                                     	//保存上次误差的值
    vi_d_r = d_error_r;                                     	//保存上次本次误差-上次误差的值
	
    error_0_l = L_prespeed_speed - left_pulseval ;              //期望脉冲数-实际脉冲数
    d_error_l = error_0_l - vi_e_l;                           	//本次误差-上次误差
    dd_error_l = d_error_l - vi_d_l;                          	//本次误差-上次误差-(上次本次误差-上次上次误差)
    vi_e_l = error_0_l;                                     	//保存上次误差的值
    vi_d_l = d_error_l;                                     	//保存上次本次误差-上次误差的值
	
    RightDuty += (int)(SpeedKp*error_0_r
					   + SpeedKi*d_error_r
						   + SpeedKd*dd_error_r);//增量式pid  
	
	LeftDuty  += (int)(SpeedKp*error_0_l 
					   + SpeedKi*d_error_l 
						   + SpeedKd*dd_error_l);
	
	if(R_prespeed_speed == 0)  //&& L_prespeed_speed == 0                               	//制动轮胎抱死 正反转交替
	{
		if(RightDuty < 0)
        {  
            if(RightDuty<-4000)
            {
                RightDuty = -4000;
            }
            FTM_PWM_Duty(FTM0 ,FTM_CH0 ,-RightDuty);
            FTM_PWM_Duty(FTM0 ,FTM_CH1 , 0);
        }
        else 
        {
            if(RightDuty>9000)
            {
                RightDuty = 9000;
            }
            
            FTM_PWM_Duty(FTM0 ,FTM_CH0 ,0);
            FTM_PWM_Duty(FTM0 ,FTM_CH1 ,RightDuty);
        }
		
		if(LeftDuty < 0)
        {  
            if(LeftDuty<-4000)
            {
                LeftDuty = -4000;
            }
            FTM_PWM_Duty(FTM0 ,FTM_CH3 ,-LeftDuty);
            FTM_PWM_Duty(FTM0 ,FTM_CH2 , 0);
        }
        else 
        {
            if(LeftDuty>9000)
            {
                LeftDuty = 9000;
            }
			
            FTM_PWM_Duty(FTM0 ,FTM_CH3 ,0);
            FTM_PWM_Duty(FTM0 ,FTM_CH2 ,LeftDuty);
            
        }
	}
	else
	{
		if( error_0_r < -10)                                 	//实际脉冲数远大于期望脉冲时电机反转减速
		{     
			FTM_PWM_Duty(FTM0,FTM_CH0,500);                 		//反转
			FTM_PWM_Duty(FTM0,FTM_CH1,0);
		}
		else
		{ 
			if(RightDuty > 9000)                               	//电机占空比限幅
			{
				RightDuty = 9000;
			}     
			else if(RightDuty < 0)
			{
				RightDuty = 0;
			}
			FTM_PWM_Duty(FTM0,FTM_CH0,0);             	//电机正转
			FTM_PWM_Duty(FTM0,FTM_CH1,RightDuty);
		}
		
		if( error_0_l < -10)                                 	//实际脉冲数远大于期望脉冲时电机反转减速
		{     
			FTM_PWM_Duty(FTM0,FTM_CH3,500);                 		//反转
			FTM_PWM_Duty(FTM0,FTM_CH2,0);
		}
		else
		{ 
			if(LeftDuty > 9000)                               	//电机占空比限幅
			{
				LeftDuty = 9000;
			}     
			else if(LeftDuty < 0)
			{
				LeftDuty = 0;
			}
			FTM_PWM_Duty(FTM0,FTM_CH3,0);             	//电机正转
			FTM_PWM_Duty(FTM0,FTM_CH2,LeftDuty);
		}
	}

上边是位置式和增量式的实例代码,仅以作为借鉴,其实pid最难的不是把算法写出来,最难的是调参。调参数这个事情只能是一点一点试出来的,这里有个口诀。

参数整定找最佳, 从小到大顺序查。
​ 先是比例后积分, 最后再把微分加。
​ 曲线振荡很频繁, 比例度盘要放大。
​ 曲线漂浮绕大弯, 比例度盘往小扳。
​ 曲线偏离回复慢, 积分时间往下降。
​ 曲线波动周期长, 积分时间再加长。
​ 曲线振荡频率快, 先把微分降下来。
​ 动差大来波动慢, 微分时间应加长。
​ 理想曲线两个波, 前高后低四比一。
​ 一看二调多分析, 调节质量不会低。

至于是怎么看的,我也看不懂,还得一点一点摸索。

有关于pid的优化调整

我写这篇文章主要是关于pid的优化调整的一些经验。前面提过pid不难写,难在调参,难在优化上。其实增量式和位置式我拿到数学模型后不到半小时就写出来了,但是调参调了一个多星期还是存在问题,还存在pid不够精细的问题。
由于我做的车是f车型,用的是位置式pid,所以就以位置式为例。

为了直观的显示pid的控制情况,我们用python进行一下图像化显示。主要是放假回家没有把车子带回来。
在这里插入图片描述
来自这篇文章
有关于python做的pid的图像化展示看我这篇文章

我用的一开始使用的是增量式,由于一开始没写好程序,小车在走的时候产生咔哧咔哧的声音,一开始猜测是中断函数给的间断时间太长导致的。但其实并不是,后来发现是误差值由本来的 期望-时间 写成 实际-期望 了。
在学长的建议下我使用了位置式pid,这次很成功。但是在调节差速过弯时,存在一些问题,也就是现在遇到的问题。F车型没有舵机,只能使用差速过弯,而且F车有得天独厚的优势,就是车速快,而且过弯时无需减速,这样对于pid的要求更加高了。
现在就对于我这个实际情况进行研究。
1)中断函数的时间
原来的中断函数的时间是20ms,现在改成了5ms,我们借助我们写的python的pid进行展示结果。下面这张图显示的事时间更长的pid效果,上边那个是时间间隔稍微短一些的效果图。
在这里插入图片描述
与上面的那个图片展示的对比,可以看到时间越精细,pid控制的效果越好。
2)在这里插入图片描述
我们再来看一下这个数学模型。Kp是比例系数,e(t)是误差值,和Ki,Kd一样,kp是一个给定的参数,但是e(t)是一个动态的量。如果我们把这个动态的量乘以三次方,使这个量变大,会不会让整个pid算法变得更敏感一些。(这里不能乘以偶数次方,因为这个差值可能是一个负数,若是一个偶数次方,负数变正数,模拟数据发生突变,整个pid就会产生大的误差)我们也是使用写好的python程序看一下。
在这里插入图片描述
这个现象就非常有意思了,在底端刚开始的时候产生了波动,但是在接近目标值的时候曲线趋于平滑且波动较少,因为没有实体车子试一试,只能这样借助一个模拟的情况来看一下,具体还是以实际测试为准。但是放大误差值的方法的结果超出了我预想的情况,还需要继续分析,但是又情况来看,这种方法也能稍稍提高pid的稳定性。
3)我猜测可能还与设定的范围有关,也就是设置的windup_gurad。我们的pid设置的精细一点是为了让我们的车子能够丝滑的过弯。差速的区间一定设置好,这也是个模糊的值,得一点一点的试出来,我的车子从组装好到过弯用了接近一个月的时间,速度仅在不到1.5,确实跑的很慢。当然第一次做车我也欣然接受了,但是我想让他跑的再快点,过弯就得再精细点车速才能上去。经过很多次的测试,两个轮子的差值与设置的挡风板也就是区间有很大的关系。类比的推理,pid是否与设定的范围有关系?当然,我们设定的时间间隔是其一,输出的pwm还有pid各个位置的调控的占比是不是应该有个范围。等到回校后会一点一点的测试的,现在只是一个推理。
4)与硬件有关。
这一点应该是一定会遇到的问题,比如有一次我在调车,跑着跑着突然失灵,捣鼓半天才发现是电池没电了。轮胎与地面的摩擦力也应该算入其中。轮子齿轮的咬合程度,不能太紧也不能太松。

就这么多了,毕竟在家里也没把车子带回来,做的这些都是口头空谈。写着一篇文章仅仅是做个学习笔记,里面有不对的多多包涵,也希望看到有错误的地方能帮我指出来,小弟不胜感激。
希望实验室里的各位大佬都能取得很好的成绩,也祝各位新年快乐,万事胜意。

发布了7 篇原创文章 · 获赞 1 · 访问量 156

猜你喜欢

转载自blog.csdn.net/qq_43303805/article/details/104055705
今日推荐