基于TI板MSP430 玩转PID


前言

具体啥是PID,我这里不做介绍,网上有大把的资料可查,以下我推荐几篇优秀文章以供大家参考:


一、整体框架

该篇以电机控速为例展开分析,假如你手上有一个编码器电机,其余配件若干,请你运用PID算法相关知识设计出一套完整的反馈路线。

相信大家最常用的设计路线是用定时器,那么这里我就以定时器来构思反馈路线。

  1. 编码器电机的变化源于所在ABZ三相的变化(一般编码器电机只有A相和B相,这里也以该俩相为例),所以先对该两项进行设计,可知定时器有输入捕获功能刚好可以用于捕获AB相的变化 【定时器一】。
  2. 每隔一定时间对电机速度进行一次检测,以及时响应未知变化 【定时器二】。
  3. 需对电机进行速度控制,也就是PWM设计 【定时器三】。
  4. 你需给定电机一个初速度,那么该怎么给呢,我的方法是你可以根据你检测电机的时间,也就是定时器二的时间所捕获到的AB相脉冲数做为初速度,PWM周期可根据实际脉冲数而设定。

简易框图如下:
在这里插入图片描述

二、PID算法

1. 位置式PID

在这里插入图片描述
位置式Pid就是位置闭环控制,位置闭环控制就是根据编码器的脉冲累加,测量电机的位置信息,并与目标值进行比较得到一个控制偏差,然后我们对偏差进行比例积分、微分的控制,使偏差趋近于0的一个过程。

  • 优点:
    位置式PID是一种非递推式算法,可直接控制执行机构(如平衡小车),u(k)的值和执行机构的实际位置(如小车当前角度)是一一对应的,因此在执行机构不带积分部件的对象中可以很好应用
  • 缺点:
    每次输出均与过去的状态有关,计算时要对e(k)进行累加,运算工作量大。
PID_VAR_TYPE Position_PID_Cal(PID * s_PID,PID_VAR_TYPE now_point)
{
    
    
    s_PID->LastResult = s_PID->Result;                 // 简单赋值运算
    //误差计算
    s_PID->Error = s_PID->SetPoint - now_point;
    s_PID->SumError += s_PID->Error;                            //积分误差累加
    //积分限幅
    PID_VAR_TYPE IOutValue = s_PID->SumError * s_PID->Integral;
    if(IOutValue > s_PID->IntegralMax)IOutValue = s_PID->IntegralMax;
    else if(IOutValue < s_PID->IntegralMin)IOutValue = s_PID->IntegralMin;
    //PID计算
    s_PID->Result =  s_PID->Proportion  *  s_PID->Error                          // 比例项
                   + IOutValue                                                     // 积分项
                   + s_PID->Derivative  * (s_PID->Error - s_PID->LastError);     // 微分项

    s_PID->PrevError = s_PID->LastError;                               // 简单赋值运算
    s_PID->LastError = s_PID->Error;                       // 简单赋值运算

    //输出限幅
    if(s_PID->Result > s_PID->OutMax)s_PID->Result = s_PID->OutMax;
    else if(s_PID->Result < s_PID->OutMin)s_PID->Result = s_PID->OutMin;

    return s_PID->Result;
}

2. 增量式PID

在这里插入图片描述
增量式pid就是速度闭环控制,速度闭环控制是根据单位时间获取的脉冲数,测量电机的速度信息,并于目标值进行比较,得到了偏差。然后同样通过对偏差的比例、积分、微分进行控制,使偏差趋近于0的一个过程。

  • 优点:
    ①误动作时影响小,必要时可用逻辑判断的方法去掉出错数据。
    ②手动/自动切换时冲击小,便于实现无扰动切换。当计算机故障时,仍能保持原值。
    ③算式中不需要累加。控制增量Δu(k)的确定仅与最近3次的采样值有关。
  • 缺点:
    ①积分截断效应大,有稳态误差;
    ②溢出的影响大。有的被控对象用增量式则不太好;
PID_VAR_TYPE Increment_PID_Cal(PID * s_PID,PID_VAR_TYPE now_point)
{
    
    
    s_PID->LastResult = s_PID->Result;                         // 简单赋值运算
    //误差计算
    s_PID->Error = s_PID->SetPoint - now_point;
    //PID计算
    s_PID->Result = s_PID->LastResult
                  + s_PID->Proportion  * (s_PID->Error - s_PID->LastError)                          // 比例项
                  + s_PID->Integral    *  s_PID->Error                                         // 积分项
                  + s_PID->Derivative  * (s_PID->Error - 2*(s_PID->LastError) + s_PID->PrevError);  // 微分项

    s_PID->PrevError = s_PID->LastError;                       // 简单赋值运算
    s_PID->LastError = s_PID->Error;                       // 简单赋值运算
    //输出限幅
    if(s_PID->Result > s_PID->OutMax)s_PID->Result = s_PID->OutMax;
    else if(s_PID->Result < s_PID->OutMin)s_PID->Result = s_PID->OutMin;

    return s_PID->Result;
}

3. 比例外置式PID

此类运用相对较少,请自行查阅

PID_VAR_TYPE PID_Cal(PID * s_PID,PID_VAR_TYPE now_point)
{
    
    
    s_PID->LastResult = s_PID->Result;                 // 简单赋值运算
    //误差计算
    s_PID->Error = s_PID->SetPoint - now_point;
    s_PID->SumError += s_PID->Error;                            //积分误差累加
    //积分限幅
    PID_VAR_TYPE IOutValue = s_PID->SumError * s_PID->Integral;
    if(IOutValue > s_PID->IntegralMax)IOutValue = s_PID->IntegralMax;
    else if(IOutValue < s_PID->IntegralMin)IOutValue = s_PID->IntegralMin;
    //PID计算
    s_PID->Result = s_PID->Proportion *
       (s_PID->Error + IOutValue + s_PID->Derivative * (s_PID->Error - s_PID->LastError) );

    s_PID->PrevError = s_PID->LastError;                               // 简单赋值运算
    s_PID->LastError = s_PID->Error;                       // 简单赋值运算
    //输出限幅
    if(s_PID->Result > s_PID->OutMax)s_PID->Result = s_PID->OutMax;
    else if(s_PID->Result < s_PID->OutMin)s_PID->Result = s_PID->OutMin;

    return s_PID->Result;
}

4. 积分限幅、输出限幅和PID参数整定

//设置PID输出范围
void  PID_SetOutRange  (PID * s_PID, PID_VAR_TYPE outMax,PID_VAR_TYPE outMin)
{
    
    
  s_PID->OutMax = outMax;
  s_PID->OutMin = outMin;
}
//设置PID积分范围
void  PID_SetIntegralOutRange(PID * s_PID, PID_VAR_TYPE outMax,PID_VAR_TYPE outMin)
{
    
    
  s_PID->IntegralMax = outMax;
  s_PID->IntegralMin = outMin;
}
				参数整定找最佳, 从小到大顺序查。
                先是比例后积分, 最后再把微分加。
                曲线振荡很频繁, 比例度盘要放大。
                曲线漂浮绕大弯, 比例度盘往小扳。
                曲线偏离回复慢, 积分时间往下降。
                曲线波动周期长, 积分时间再加长。
                曲线振荡频率快, 先把微分降下来。
                动差大来波动慢, 微分时间应加长。
                理想曲线两个波, 前高后低四比一。
                一看二调多分析, 调节质量不会低。

5. 位置式PID和增量式PID的区别及抉择

  • (1)区别
  1. 增量式算法不需要做累加,控制量增量的确定仅与最近几次偏差采样值有关,计算误差对控制 量计算的影响较小。而位置式算法要用到过去偏差的累加值,容易产生较大的累加误差。

  2. 增量式算法得出的是控制量的增量,例如在阀门控制中,只输出阀门开度的变化部分,误动作 影响小,必要时还可通过逻辑判断限制或禁止本次输出,不会严重影响系统的工作。 而位置式的输出直接对应对象的输出,因此对系统影响较大。

  3. 增量式PID控制输出的是控制量增量,并无积分作用,因此该方法适用于执行机构带积分部件的对象,如步进电机等,而位置式PID适用于执行机构不带积分部件的对象,如电液伺服阀。

  4. 在进行PID控制时,位置式PID需要有积分限幅和输出限幅,而增量式PID只需输出限幅

  • (2)抉择
  1. 位置式PID控制的输出与整个过去的状态有关,用到了误差的累加值;而增量式PID的输出只与当前拍和前两拍的误差有关,因此位置式PID控制的累积误差相对更大;
  2. 增量式PID控制输出的是控制量增量,并无积分作用,因此该方法适用于执行机构带积分部件的对象,如步进电机等,而位置式PID适用于执行机构不带积分部件的对象,如电液伺服阀。
  3. 由于增量式PID输出的是控制量增量,如果计算机出现故障,误动作影响较小,而执行机构本身有记忆功能,可仍保持原位,不会严重影响系统的工作,而位置式的输出直接对应对象的输出,因此对系统影响较大。

三、初值获取

关于初值的设定可以根据以下3种方法获取。

1. 定时器输入捕获

定时器A0和A1开捕获模式(双边沿),分别接俩个电机的A/B相,每发生一个脉冲变化进一次捕获中断,中断里做计数操作。

void TIME()//配置编码电机接口
{
    
    
    TA0CCTL1 = CM_3 | CCIS_0 | SCS | CAP | CCIE; // 捕获比较器1:双边沿捕获模式,捕获 TA0CLK 信号,启用捕获中断
    TA0CTL = TASSEL_2 | ID_0 | MC_2 | TACLR; // P1OUT=BIT2;时钟源选择 SMCLK,分频系数为 1,计数器模式为连续计数,启用计数器清零

    P1DIR &=~ BIT2;               //初始化捕获IO口
    P1SEL |=  BIT2;

    // 启用 Timer_A 中断和总中断
    TA0CTL |= TAIE; // 启用 Timer_A 中断
    __enable_interrupt(); // 启用总中断
}

#pragma vector=TIMER0_A1_VECTOR //定时器A中断处理
__interrupt void timer_A0(void)
{
    
    
      switch(TA0IV) //向量查询,读取的话无需手动清零标志位
      {
    
    
      case 2:             //捕获中断
        count0++;
        break;

      case 10:            //溢出中断
          break;
      default:
        break;
      }
}

2. 外部中断

将A/B相接外部中断引脚,设置触发模式,每发生一次跳变便进一次中断,中断内做计数操作。

void init_encoders()
{
    
    
    P1DIR &= ~BIT4;          //设置P1.4口为输入
    P1IN |= BIT4;
    P1REN |= BIT4;

    P1OUT |= BIT4;          //配置P1.4上拉电阻
    P1IE |= BIT4;           //P1.4中断使能
    P1IES |= BIT4;          //P1.4下降沿触发中断
    P1IFG &= ~BIT4;        //清除中断标志位
    
    _EINT();
}

#pragma vector = PORT1_VECTOR
__interrupt void P1_ISR(void)
{
    
    
    _DINT();  //关总中断

    if (P1IFG & BIT4)
    {
    
    
        count0++;
    }

    P1IFG &= ~ BIT4 ;
    _EINT();  //开总中断
}

3. ADC采样

对电机的输出电压进行采样,可知 电机输出电压范围是 0~12V,所以是不能直接采集的,这时你需串联一个电阻进行分压,然后再将采集到的模拟信号转换为数字信号再根据PWM进行再次转化,最后将处理后的值当作初值运算。


void ADC_Init(void)
{
    
    
    P6SEL |= BIT6;
    ADC12CTL0 = ADC12ON + ADC12SHT0_8 + ADC12MSC;
    ADC12CTL1 = ADC12SHP + ADC12CONSEQ_2;
    ADC12MCTL0 = ADC12SREF_0 + ADC12INCH_6;
    ADC12CTL0 |= ADC12ENC;
 
    ADC12CTL0 |= ADC12SC;
    _EINT();
}

//采样数据转换
float ADC_date()
{
    
    
    float date;
    date = ADC12MEM0*3.3/4065;
    return date;
}


//采样中断服务函数
#pragma vector = ADC12_VECTOR
__interrupt void ADC12ISR(void)
{
    
    
	......
}

猜你喜欢

转载自blog.csdn.net/Dustinthewine/article/details/130466421