2.11 PID控制算法(三)----PID结果与实际值的关联

文章目录

  • 1、讲解
  • 2、举例1
  • 3、举例2
  • 4、举例3
  • 5、调试PID参数

1、讲解

1、AD采样,经过PID计算的值,怎么和PWM对应起来?
2、电机编码采样,经过PID计算的值,怎么与速度对应起来?

这个简单,PID控制原理就是输出一个控制量,然后检测反馈,由反馈得到的数据计算当前实际输出是多少,由这个实际输出和目标输出得到差值,最后由这个差值计算一个新的输出控制量。
举例(数据都是乱拟的,只为好计算):假设你的PWM是用来控制电机速度的,0%占空比对应的电机速度是0r,100%占空比对应的电机速度是100r。你目标电机速度是50r,那么调控步骤如下:

  1. 输出50%占空比的PWM
  2. AD采样并计算当前电机速度,假设计算得到的当前实际电机速度是40r,比目标速度50r差10r,这个10r送到PID计算得到一个增量结果,假设结果是5r
  3. 由5r计算对应的PWM占空比,即5%
  4. 更新PWM输出占空比,就是:50%+5%=55%,返回到第1步
    我个人理解,它们之间实际上就是一个比例关系。

2、举例1

我们来分析一个例子:步进电机PID速度闭环控制,它原理:预先设置好需要达到的速度,然后每隔一段时间读取编码器的值反馈当前速度,经过PID计算慢慢到达设定的速度。

#define FEEDBACK_CONST                        (SPR/ENCODER_SPR)   //编码器和步进电机驱动器的比值

typedef struct 
{
  __IO float SetPoint;    // 目标值  单位:mm/s
  __IO int LastError;     // 前一次误差    
  __IO int PrevError;     // 前两次误差
  __IO long SumError;     // 累计误差
  __IO double Proportion; // Kp系数
  __IO double Integral;   // Ki系数
  __IO double Derivative; // Kd系数
}PID_Typedef;

__IO PID_Typedef vPID;
 Vel_Target = (vPID.SetPoint*P_PERIOD);//每单位采样周期内的脉冲数(频率)
 
 
/**
  * 函数功能:增量式PID速度环计算
  * 输入参数:NextPoint     由编码器得到的速度值 
  *           TargetVal    目标值
  * 返 回 值:经过PID运算得到的增量值
  * 说    明:增量式 PID 速度环控制设计,计算得到的结果仍然是速度值
  */
float IncPIDCalc(__IO float NextPoint,__IO float TargetVal)    //临时变量,期望值 
{
  __IO float iError = 0,iIncpid = 0;                    //当前误差
  iError = TargetVal - NextPoint;                  // 增量计算
//  if((iError<0.5f)&&(iError>-0.5f))
//    iError = 0;                                    // |e| < 0.5,不做调整
  
  iIncpid=(vPID.Proportion * iError)               // E[k]项
              -(vPID.Integral * vPID.LastError)    // E[k-1]项
              +(vPID.Derivative * vPID.PrevError); // E[k-2]项
  
  vPID.PrevError=vPID.LastError;                   // 存储误差,用于下次计算
  vPID.LastError = iError;
  return(iIncpid);                                 // 返回增量值
}

/**
  * 函数功能: 系统滴答定时器中断回调函数
  * 输入参数: 无
  * 返 回 值: 无
  * 说    明: 每发生一次滴答定时器中断进入该回调函数一次
  */
void HAL_SYSTICK_Callback(void)
{
  __IO static uint16_t time_count = 0; // 时间计数,每1ms增加一(与滴答定时器频率有关)
  __IO static int32_t Last_CaptureNumber=0;       // 上一次捕获值
  // 每1ms自动增一
  time_count++; 
  if(Start_Flag == ENABLE)
  {
    if(time_count >= SAMPLING_PERIOD)// 20ms控制周期
    {
      time_count = 0;
      //获得编码器的脉冲值
      CaptureNumber = OverflowCount*65536 + __HAL_TIM_GET_COUNTER(&htimx_Encoder);
      //M法 测速度
      MSF = CaptureNumber - Last_CaptureNumber;
      Last_CaptureNumber = CaptureNumber;
      MSF = abs(MSF);
      
      Exp_Val += IncPIDCalc((float)MSF,Vel_Target);
	  //编码器输出期望值
      Exp_Val = abs(Exp_Val);
      /* 经过PID计算得到的结果是编码器的输出期望值的增量,
         需要转换为步进电机的控制量(频率值),这里乘上一个系数6400/2400
      */
      //乘上一个系数,6400/2400,将PID计算结果转换为步进电机的频率(速度)
      STEPMOTOR_Motion_Ctrl(MotorDir,Exp_Val*FEEDBACK_CONST);
    }
  }
}

/**
  * 函数功能: 步进电机运动控制
  * 输入参数: Dir:步进电机运动方向 0:反转 1正转
  *           Frequency:步进电机频率,0:停止
  * 返 回 值: void
  * 说    明: 无
  */
void STEPMOTOR_Motion_Ctrl(uint8_t Dir , float Frequency)
{
  uint16_t Step_Delay = 0;  //步进延时

  if(Frequency == 0)
    STEPMOTOR_OUTPUT_DISABLE();
  else
  {
    if(Dir==MOTOR_DIR_CCW)
    {
      STEPMOTOR_DIR_REVERSAL();
    }
    else STEPMOTOR_DIR_FORWARD();//方向控制
    /*
      步进电机速度由定时器输出脉冲频率(f)决定,
        f = c/F;c是计数器的计数值,F是定时器频率
      推导过程:(T是定时器输出脉冲周期)
        T=c*t => T=c*F => f = 1/T = F/c;
    
    */
    Step_Delay = (uint16_t)(FREQ_UINT/Frequency);
    
    Toggle_Pulse = Step_Delay>>1;//算出来的结果是周期,这里除以2,半周期翻转一次
  }
}

我们来分析一下它PID是怎么PID输出结果与速度量的关系:
从上面我可以知道,PID算法
输入参数:
1、MSF :单位采样周期内(20mS)编码器的值。
2、Vel_Target:每单位采样周期内编码器的脉冲数(频率);它由SetPoint(标 值 单位:mm/s),经过一定的计算而来。
输出参数:
1、编码器的输出期望值

步进电机的频率(速度)与PID结果的关系:Exp_Val*FEEDBACK_CONST ;从整个公式我们可发现,它们仅仅一个比例系数而已。

3、举例2

再举一例PWM控制温度

int iDataSigma
void PIDCalc(uint32_t Channel,int16_t set_temp,int16_t actualTemp)//温度放大100倍
{
	int  En,out_temp,controlOut; 

	En= set_temp-actualTemp;  //本次偏差
	if(abs(En) > 500)         //积分分离
	{
		index=0;
	}
	else
	{
		index=1;
		iDataSigma += En*iData; //历史偏差总和
	}
//	out_temp = Kp*En+index*Ki*iDataSigma; //P+I
	out_temp = Kp*En+index*Ki*iDataSigma+Kd(En-En_1);//P+I+D
	En_1 = En;
			 
	controlOut=_TIM3_ARR/500*out_temp;
	
	if (controlOut <= 0)
		bsp_SetPWM_TEMP_CCRx(Channel,0); //停止加热
	else
		bsp_SetPWM_TEMP_CCRx(Channel,abs(controlOut));//加热	
}

//占空比
 void bsp_SetPWM_TEMP_CCRx(uint32_t Channel,uint16_t duty)
{
	if(duty <= _TIM3_ARR)
	{
		__HAL_TIM_SET_COMPARE(&htim3, Channel, duty);		
	}
}

//STM32 PWM_1模式 初始化设置PWM周期
bsp_SetPWM_TEMP_ARR(_TIM3_ARR);		                 //周期 T=1000uS  _TIM3_ARR 

4、举例3

举例三:PWM控温

//定时器装载1000,相当于1mS
void PID_Init1(void)
{
    spid1.Uplimit = 999;
    spid1.Downlimit = 0;
    spid1.Kp = 20;
    spid1.Ki = 0.1;
    spid1.Kd = 0;
    spid1.e_0 = 0;
    spid1.e_1 = 0;
    spid1.e_2 = 0;
    spid1.ei = 0;
    spid1.ed = 0;
}
void PID_Init2(void)
{
    spid2.Uplimit = 999;
    spid2.Downlimit = 0;
    spid2.Kp = 20;
    spid2.Ki = 0.1;
    spid2.Kd = 0;
    spid2.e_0 = 0;
    spid2.e_1 = 0;
    spid2.e_2 = 0;
    spid2.ei = 0;
    spid2.ed = 0;
}
//位置式
u32 pid_calc(sPID *pid,float target ,float feedback)  
{     
    pid->target = target;
    pid->feedback = feedback;
    pid->e_0 = target - feedback;
    #if 0 
    if(pid->Ki*pid->ei + pid->Kp*pid->e_0 <= pid->Uplimit)//限制累积值
        pid->ei += pid->e_0;  
    else if(pid->e_0 < 0)
       pid->ei += pid->e_0;
    else;
    #endif
    pid->ei += pid->e_0;
    if(pid->Ki*pid->ei > 500)//限制累积值
    {
        pid->ei = 500/pid->Ki;
    }
    if(pid->ei < (-100))
    {
        pid->ei = (-100);
    }
#if 0
    pid->ed = pid->e_0 - 2*pid->e_1 + pid->e_2;  
 #else 
    pid->ed = pid->e_1 - pid->e_0;  //个人以为这个才是对的 
 #endif   
    pid->Out = pid->Kp*pid->e_0 + pid->Ki*pid->ei + pid->Kd*pid->ed;  

    if(pid->Out > pid->Uplimit)
    {
        pid->Out = pid->Uplimit;
    }
    else if(pid->Out < pid->Downlimit)
    {
        pid->Out = pid->Downlimit;
    }
    else
    {}
    
    pid->e_2 = pid->e_1;  
    pid->e_1 = pid->e_0;  
    
    return (u32)pid->Out;     
}

 pid_calc(&spid1,(float)TempCtrler[PURE_TEMP_LYSIS].target,(float)TempForCal[PURE_TEMP_LYSIS]);
 __HAL_TIM_SET_COMPARE(&htim1,TIM_CHANNEL_2,spid1.Out);

5、调试PID参数

调试PID参数链接
2.11 PID控制算法(三)----PID结果与实际值的关联_第1张图片
Kp相当于一个比例系数
Ki,减小静差
Kd,相当于一个阻尼系数

3.1 PID参数的整定原则

U(t)=P*[e(t)+ 1/Ti∫0te(t)dt+Tdde(t)/dt]
a.在输出不振荡时,增大比例增益P。(即增开Kp)
b.在输出不振荡时,减小积分时间常数Ti。(即增大Ki)
c.在输出不振荡时,增大微分时间常数Td。(即减小Kd)

P环很好理解,我们将误差乘以P值加在执行机构上面,当误差越小时P环所得值也就越小,给到执行机构的值也就越小,执行机构反应也就越慢,很符合这种闭环反馈的思想。但是当有一种情况,误差很小的时候P环得出的值给到执行机构上面不足以驱动执行机构工作,这时系统距离期望值仍有一段距离,该距离便被称为静差。

I环的作用是对过去状态的累加,本质上是对误差的积分,当系统存在静差时,I环会持续累加静差值,直到累加值大到足以驱动执行机构工作。这是I环的优点,可以有效地处理静差。但是I环本质上是对过去状态的处理,所以加上I环以后整个系统会变得有些不可控制,这里可以采用积分分离式的思想,只有当误差小于一定范围才开始进行进行误差的累加。如果是对稳定性要求比较高的系统,比如平衡小车直立环,就尽量不要用I环,单PD控制器控制即可。

D环,这个环比较的神奇,它可以预测将来,本质上是对误差的微分。也可以理解成对系统的阻尼,打个比方,在一个理想的环境下有个单摆一直在摆动,它摆动的波形是个正弦波形。如果不加阻尼那么它便可以一直摆动,但是现实中总会有各种各样的阻尼作用迫使单摆运动停下来。D环起的就是这个作用。D环一般运用在角度环比较多,因为你不可以进入弯道才想到转弯,而是在进入弯道之前就要有这个转弯的意识。做智能车的小伙伴们注意了!!

下面式复制别人图片,讲的挺好的,链接如下:
PID参数整定

你可能感兴趣的:(STM32,PID算法)