Jump to content

    
Sign in to follow this  
sigmaN

PID регулятор от Atmel

Recommended Posts

В общем-то ПИД регулятор применяю впервые.

 

Для начала решил стабилизировать обороты самого обычного кулера. Потом, естественно, доберусь и до температуры.

С кулером удобнее и нагляднее экспериментировать.

 

Ну, задача известная: поддерживать заданную частоту вращения ротора безколлекторного двигателя постоянного тока.

 

После внимательного изучения всей учёной части я таки впал в искушение и не стал кодить сам, а скачал атмеловскую аппноту AVR221

 

Код регулятора:

#define SCALING_FACTOR  128

//Needed to avoid sign/overflow problems
// Maximum value of variables
#define MAX_INT         INT16_MAX
#define MAX_LONG        INT32_MAX
#define MAX_I_TERM      (MAX_LONG / 2)

typedef struct PID_DATA{
  //! Last process value, used to find derivative of process value.
  int16_t lastProcessValue;
  //! Summation of errors, used for integrate calculations
  int32_t sumError;
  //! The Proportional tuning constant, multiplied with SCALING_FACTOR
  int16_t P_Factor;
  //! The Integral tuning constant, multiplied with SCALING_FACTOR
  int16_t I_Factor;
  //! The Derivative tuning constant, multiplied with SCALING_FACTOR
  int16_t D_Factor;
  //! Maximum allowed error, avoid overflow
  int16_t maxError;
  //! Maximum allowed sumerror, avoid overflow
  int32_t maxSumError;
} pidData_t;
//===========================================================

void pid_Init(int16_t p_factor, int16_t i_factor, int16_t d_factor, struct PID_DATA *pid)
// Set up PID controller parameters
{
  // Start values for PID controller
  pid->sumError = 0;
  pid->lastProcessValue = 0;
  // Tuning constants for PID loop
  pid->P_Factor = p_factor;
  pid->I_Factor = i_factor;
  pid->D_Factor = d_factor;
  // Limits to avoid overflow
  pid->maxError = MAX_INT / (pid->P_Factor + 1);
  pid->maxSumError = MAX_I_TERM / (pid->I_Factor + 1);
}
//===========================================================

int16_t pid_Controller(int16_t setPoint, int16_t processValue, struct PID_DATA *pid_st)
{
  int16_t error, p_term, d_term;
  int32_t i_term, ret, temp;

  error = setPoint - processValue;

  // Calculate Pterm and limit error overflow
  if (error > pid_st->maxError){
    p_term = MAX_INT;
  }
  else if (error < -pid_st->maxError){
    p_term = -MAX_INT;
  }
  else{
    p_term = pid_st->P_Factor * error;
  }

  // Calculate Iterm and limit integral runaway
  temp = pid_st->sumError + error;
  if(temp > pid_st->maxSumError){
    i_term = MAX_I_TERM;
    pid_st->sumError = pid_st->maxSumError;
  }
  else if(temp < -pid_st->maxSumError){
    i_term = -MAX_I_TERM;
    pid_st->sumError = -pid_st->maxSumError;
  }
  else{
    pid_st->sumError = temp;
    i_term = pid_st->I_Factor * pid_st->sumError;
  }

  // Calculate Dterm
  d_term = pid_st->D_Factor * (pid_st->lastProcessValue - processValue);

  pid_st->lastProcessValue = processValue;

  ret = (p_term + i_term + d_term) / SCALING_FACTOR;
  if(ret > MAX_INT){
    ret = MAX_INT;
  }
  else if(ret < -MAX_INT){
    ret = -MAX_INT;
  }

  return((int16_t)ret);
}

 

ШИМ у меня 8бит.

каждые 250ms делаю так

int16_t regOut;
        cooler = coolers.collection[i];
        regOut = pid_Controller(cooler->rpm_goal, cooler_get_rpm(cooler->id), &cooler->pid ); 
        regOut += cooler->current_pwm_duty;
        if( regOut > 255 )
            regOut = 255;
        if( regOut < 0 )
            regOut = 0;
        cooler->current_pwm_duty = regOut;
        cooler->setPWM( cooler->current_pwm_duty );

Регулятор в целом работает, но скверно. Его невозможно настроить.

передаваемые в pid_Init() p_factor i_factor d_factor имеют слишком большой эффект.

p_factor 1 работает, но реакция медленная. А уже при 2 колеблется достаточно сильно. При 3 - 5 уже полный абзац.

i_factor вообще лучше не трогать. Там даже 1 всё ломает. )

d_factor туда-сюда +/-5 - 10 ещё терпимо, но толку это не даёт

 

Также, заметил очень большой "зазор". Регулятор считает, что всё хорошо, хотя на самом деле обороты от заданных могут отличаться на 200 - 300(при 1100 заданных).

 

Мне не нравится что здесь всё как то уж больно целочисленно делается.

Есть идея переписать это дело под фиксированную точку, чтобы коэффициенты можно было задавать с шагом хотя-бы 0.1 вместо нынешней единицы.

Однако чувствую, что просто чего-то ещё не понял и того-же эффекта можно достичь другими методами.

Может быть мне стоит разделить входные setPoint и processValue на 2(а может и на 4), тем самым уменьшу ошибку и диапазон коэффициентов расширится?

 

Ещё вопрос как быть с частотой регулирования: как выбрать её оптимальной?

 

А ещё есть функция pid_Reset_Integrator(), которая сбрасывает сумму ошибок в 0. Когда её вызывать?

Share this post


Link to post
Share on other sites
В общем-то ПИД регулятор применяю впервые.

Для успешной работы регулятора нужно иметь достаточную частоту отсчетов, иначе никакими коэффициентами невозможно регулировать.

А Вы даже не объяснили, что на входе регулятора, что на выходе.

Share this post


Link to post
Share on other sites

Делал регулятор для бензинового двигателя на атмеловской ПИД-заготовке. Управлял заслонкой карбюратора через ШД и редуктор. Время отклика системы где-то 1-2сек, сделал измерение оборотов 10 раз/сек и одновременно пересчет управляющего воздействия. Работает вполне сносно, только i_factor очень маленький получился иначе время реакции снижается. Фактически не ПИД, а ПД регулятор получился.

Share this post


Link to post
Share on other sites
Регулятор в целом работает, но скверно. Его невозможно настроить.

передаваемые в pid_Init() p_factor i_factor d_factor имеют слишком большой эффект.

p_factor 1 работает, но реакция медленная. А уже при 2 колеблется достаточно сильно. При 3 - 5 уже полный абзац.

Не может быть. Там же всё делится на SCALING_FACTOR, который равен 128. То есть, при ошибке менее 128 и p_factor=1 отклик будет нулевым. То же и при p_factor=2 и ошибке 64.

 

Также, заметил очень большой "зазор". Регулятор считает, что всё хорошо, хотя на самом деле обороты от заданных могут отличаться на 200 - 300(при 1100 заданных).

 

Вот это как раз по указанной мной причине. Увеличивайте коэффициенты, не бойтесь.

 

Мне не нравится что здесь всё как то уж больно целочисленно делается.

Есть идея переписать это дело под фиксированную точку, чтобы коэффициенты можно было задавать с шагом хотя-бы 0.1 вместо нынешней единицы.

 

Там сейчас шаг 1/128 :)

 

ЗЫ. Советую для тренировки сделать всё на плавучке, так гораздо легче почувствовать поведение системы. Оптимизировать всегда успеете.

Share this post


Link to post
Share on other sites
Для успешной работы регулятора нужно иметь достаточную частоту отсчетов, иначе никакими коэффициентами невозможно регулировать.

А Вы даже не объяснили, что на входе регулятора, что на выходе.

На входе заданные обороты(об/мин) и измеренные в данный момент времени.

Измерение происходит каждый оборот кулера.

Вызов функции регулятора - каждые ~250ms

 

Про выход думал будет понятно из кода.

В общем воздействую я на систему путем изменения скважности ШИМ.

 

Не может быть. Там же всё делится на SCALING_FACTOR, который равен 128. То есть, при ошибке менее 128 и p_factor=1 отклик будет нулевым. То же и при p_factor=2 и ошибке 64.
Логично. SCALING_FACTOR я что-то не учел.
Вот это как раз по указанной мной причине. Увеличивайте коэффициенты, не бойтесь.
Ситуация немного прояснилась, спасибо.

Share this post


Link to post
Share on other sites

Может еще посмотреть в сторону всяких апериодических звеньев в качестве регулятора? Гибкость не такая как у ПИД, зато реализация проще некуда. Да и проще они в настройке.

Share this post


Link to post
Share on other sites

А почему в атмеловском примере нигде не учитывается частота вызова функции?

А в приведеннем примере наоборот есть real tauQuant // период квантования (реальный!!! период запуска функции регулятора), размерность - секунды

Share this post


Link to post
Share on other sites
А почему в атмеловском примере нигде не учитывается частота вызова функции?

У них эта зависимость засунута в коэффициенты I_Factor и D_Factor. при изменении частоты дискретизации эти коэффициенты нужно будет пересчитать.

Share this post


Link to post
Share on other sites
У них эта зависимость засунута в коэффициенты I_Factor и D_Factor.
Понятно. В общем один раз выбрал частоту, настроил коэффициенты и забыл :))))

 

Кстати, а что там с частотой? Как её правильно выбрать?

 

вот нашел

Есть простое правило для цифровых управляющих систем, которое гласит, что продолжительность итерации управляющего цикла должна быть между 1/10 и 1/100 желаемого времени стабилизации системы в новом положении.
1/10 и 1/100 - неплохой зазорчик )) Подозреваю, что тут нужно учитывать инерционность системы. Потому что если у меня будет желаемое время стабилизации 0.5с а двигатель здоровенный и тупой, реагирует на команды через 3секунды, то наверное ничего хорошего не выйдет из того, что я буду вызывать регулятор каждые 5ms :)

Share this post


Link to post
Share on other sites
вызывать pid_Reset_Integrator() при error == 0 или когда???
А Вы сами-то что об этом думает?

Когда ошибка равна нулю - это значит что регулятор вышел на установившуюся мощность и зачем его лишать интегральной составляющую в этом момент (вдруг он плохо настроен и только на ней и теплится)?

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Sign in to follow this