电机PI调节器结构代码

时间:2017-12-08 13:18:45

标签: c embedded nucleo pid-controller

我实际上是在一个基于freeRTOS的机器人项目上工作。我遇到了控制机器人车轮速度的PI控制器的一些问题

#define To 100 // 100 ms     
   #define Te 5  // 5 ms
   #define Kp 0.01
   #define Ti 10  //0.1*To
   #define Ki 0.05/10   //Te/ti

   void corrNum(int consigneVitesse) // takes speed as parameter 
   {
        int eL,eR = 0; errors left and right
         float cdeL = 0 ;
         float UpL = 0 ;
         int dutyL = 0 ;
         float cdeR = 0 ;
    float UpR = 0 ;
    int dutyR = 0 ;
    float UiL= 0 ;
    float UiR = 0 ;
    if(consigneVitesse)
    {
            tickL = quadEncoder_GetSpeedL();
            tickR = quadEncoder_GetSpeedR();
            eL = (int)(consigneVitesse - tickL);
            eR = (int)(consigneVitesse - tickR);
            UpL = Kp*eL ;
            UpR = Kp*eR ;
            UiL= UiL + Kp*Ki*eL ;
            UiR= UiR + Kp*Ki*eR ;
            UiL_prev = UiL ;
            UiR_prev = UiR ;
            cdeL = UpL + UiL ;
            cdeR = UpR + UiR ;
            dutyL = cdeL < 100 && cdeL > -100 ? (int)cdeL +100 : 200 ;
            dutyR = cdeR < 100 && cdeR > -100 ? (int)cdeR +100 : 200 ;
            motorLeft_SetDuty(dutyL);
            motorRight_SetDuty(dutyR);
            HAL_Delay(5); // sampling period 5 ms
            term_printf("MOTOR LEFT ::::::> CMD : %d SPEED : %d Err : %d    DUTY : %d\n\r",consigneVitesse,tickL,eL,dutyL`enter code here`);
            term_printf("MOTOR RIGHT ::::::> CMD : %d SPEED : %d Err : %d  DUTY : %d \n\r",consigneVitesse,tickR,eR,dutyR);;
            tickL = 0 ;
            tickR = 0;
    }
    else
    {
        motorLeft_SetDuty(100);
        motorRight_SetDuty(100);
    }


}`

问题在于,当误差达到0.0而不是稳定时,开始振荡。

1 个答案:

答案 0 :(得分:-1)

目前尚不清楚如何在此解决方案中使用FreeRTOS - 片段中没有FreeRTOS调用,非RTOS HAL_delay()和时间关键控制循环中的终端输出无济于事 - 您在5ms内只能输出57个字符 - 你没有时间输出文字。

您的错误信号计算错误,编码器提供的相对位置不是速度;你必须根据时间的变化计算速度。

您应该将任务专用于控制回路并使用RTOS定时来确保高效和准确的调度。该任务应该相对于其他任务以足够高的优先级运行,以确保无抖动时序。

以下内容应视为伪代码;我不熟悉FreeRTOS API,所以操作系统调用不是真的,我没有处理实例化任务本身,也没有处理你没有提供足够信息的扩展和其他细微之处 - 这是建议的您应该从中获取的控制器结构 - 而不是逐字的实际代码:

typedef enum
{
  LEFT = 0,
  RIGHT = 1
} tMotorId

volatile static struct
{
  float integral ;
  int speed ;
  int previous_position ;
} motor_ctrl_data[2] = {{0,0},{0,0}} ;

void setSpeed( tMotorId id, int speed )
{
  motor_ctrl_data[id] = speed ;
}

void motor_ctrl_task()
{
  // Configure RTOS time for 5ms update period
  OS_TIMER timer = creatTimer( 5 ) ;
  startTimer( timer ) ;

  // Get initial encoder counts
  motor_ctrl_data[LEFT].previous_position = getEncoder(LEFT) ;
  motor_ctrl_data[RIGHT].previous_position = getEncoder(RIGHT) ;

  // PI control loop
  for(;;)
  {
    // Loop update timer wait
    waitTimer( timer ) ;

    // For each motor
    for( int m = 0; m < 2; m++ )
    {
      // Calculate speed
      int speed = getEncoder[m] - motor_ctrl_data[m].previous_position ;

      // Calculate speed error
      int error = motor_ctrl_data[m].speed - speed[m] ;

      // Calculate error integral
      motor_ctrl_data[m].integral += error[m] ;

      motorDuty( LEFT, Kp * error +  Ki * motor_ctrl_data[m].integral ) ;
    }
  }
}

这当然不能解决振荡,但至少是一个能够在给定适当系数的情况下进行控制的控制器结构。调整环路设置Ki = 0并增加Kp,直到电机尽可能快地移动但没有振荡。这不会让你达到目标速度,因为在目标上错误将为零,因此Kp单独无法工作。然后开始增加Ki直到达到目标速度。即使是小的Ki,最终也会达到目标速度,但动态响应会很迟钝。太多,你会得到过冲或振荡。

根据电机的动态响应,您可能需要进行其他调整,例如积分限制或死区补偿。您甚至可以从差异术语中受益,但对于简单的速度控制器,这通常是不必要的并且通常难以调整。

如果需要输出调试数据,请将循环写入调试数据写入共享变量(例如,如果使用上面建议的结构,则在motor_ctrl_data中),并以较低优先级任务异步打印它们,速率为终端输出可以维持。

文章&#34; PID Without a PhD&#34;作者Tim Wescott可能是一本有用的读物​​。