0

我实际上正在开发一个基于 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而不是稳定时开始振荡。

4

1 回答 1

-1

目前尚不清楚您在此解决方案中如何使用 FreeRTOS - 片段中没有 FreeRTOS 调用,时间关键控制循环中的非 RTOSHAL_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例如,如果使用上面建议的结构)并以终端输出可以维持的速度从较低优先级任务异步打印它们。

Tim Wescott的文章“没有博士学位的 PID”可能是有用的读物​​。

于 2017-12-09T00:12:04.597 回答