Перейти к содержанию
    

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

 

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

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

 

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

 

После внимательного изучения всей учёной части я таки впал в искушение и не стал кодить сам, а скачал атмеловскую аппноту 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. Когда её вызывать?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

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

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

передаваемые в 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 :)

 

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

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

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

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

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

 

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

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

 

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

 

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

 

вот нашел

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

вызывать pid_Reset_Integrator() при error == 0 или когда???

Изменено пользователем raf329

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

Не значит.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Не значит.
Может значить, а может и нет (я утрировал).

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...