реклама на сайте
подробности

 
 
> PID регулятор от Atmel, как-то не радует он меня. А должен.
sigmaN
сообщение May 4 2010, 06:00
Сообщение #1


I WANT TO BELIEVE
******

Группа: Свой
Сообщений: 2 617
Регистрация: 9-03-08
Пользователь №: 35 751



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

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

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

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


--------------------
The truth is out there...
Go to the top of the page
 
+Quote Post
 
Start new topic
Ответов
Jenyok2
сообщение Oct 9 2011, 16:40
Сообщение #2


Участник
*

Группа: Участник
Сообщений: 41
Регистрация: 25-02-09
Из: Russia, Moscow
Пользователь №: 45 341



Вот реализация ПИД алгоритма.
В процедуре DoChart 4-е цикла с разными реализациями ПИД алгоритма.
Язык программирования Delphi 7.0 .
Функция для регулироания fPID внутри процедуры DoChart.
Для тестирования и применения в ваших программах.
.
Код
  function TForm1.ChangeValPoint(s : String) : String;
    var
      i : Integer;
    begin
       Result:=s;
       i:=Length(s);
       while (i > 0) and (Result[i] <> '.') do Dec(i);
       if (i > 0) then Result[i]:=',';
    end;

  procedure TForm1.DoChart;
    var
      f    : array[1..4000] of Real;
      f1   : array[1..4000] of Real;
      f2   : array[1..4000] of Real;
      f3   : array[1..4000] of Real;

      A, B : Real;
      i    : Integer;
      n    : integer;

      min_integ_error,
      max_integ_error,
      integral : Real;
      diapazon : Real;

      T0   : Real; { Ïåðèîä êâàíòîâàíèÿ (äèñêðåòèçàöèè) }
      Ti   : Real; { Ïîñòîÿííàÿ èíòåãðèðîâàíèÿ }
      Td   : Real; { Ïîñòîÿííàÿ äèôåðåíöèðîâàíèÿ }

      q0   : Real;
      q1   : Real;
      q2   : Real;

      Uk   : Real; { Òåêóùåå çíà÷åíèå U(k) }
      Uk1  : Real; { Ïðåäûäóùåå çíà÷åíèå U(k-1) }
      ek   : Real; { Òåêóùåå çíà÷åíèå îøèáêè e(k) }
      ek1  : Real; { Ïðåäûäóùåå çíà÷åíèå îøèáêè e(k-1) }
      ek2  : Real; { Ïðåäûäóùåå çíà÷åíèå îøèáêè íà äâà øàãà íàçàä e(k-2) }

      Uust : Real; { Çíà÷åíèå óñòàâêè (âõîäíîé âåëå÷èíû) }

      Kp   : Real;
      Ki   : Real;
      Kd   : Real;
      Ku   : Real;

    function fPID(i : Integer; Uk : Real) : Real;
      begin
        B:=0.2;
        A:=i / 10.0;
//        Result:=Uk * ((1.0 / A) / (B + 1.0 / A));

//        t:=(i - 1) / 250000.0;
//        f[i]:=k * (1.0 - Exp(-1.0 * t / Tau));

//        Result:=10.0 * sin(((i - 1) - Uk) / 40.0);
        Result:=sin(((i - 1) - Uk) / 40.0);
      end;

    begin

      { Series1 - Ôóíêöèÿ äëÿ ðåãóëèðîâàíèÿ }
      { Series2 - PID }
      { Series3 - PID + Ôóíêöèÿ ðåãóëèðîâàíèÿ }
      { Series4 - Îøèáêà }

      Form1.LabeledEdit1.Text:=ChangeValPoint(Form1.LabeledEdit1.Text);
      Form1.LabeledEdit2.Text:=ChangeValPoint(Form1.LabeledEdit2.Text);
      Form1.LabeledEdit3.Text:=ChangeValPoint(Form1.LabeledEdit3.Text);
      Form1.LabeledEdit4.Text:=ChangeValPoint(Form1.LabeledEdit4.Text);
      Form1.LabeledEdit5.Text:=ChangeValPoint(Form1.LabeledEdit5.Text);
      Form1.LabeledEdit6.Text:=ChangeValPoint(Form1.LabeledEdit6.Text);
      Form1.LabeledEdit7.Text:=ChangeValPoint(Form1.LabeledEdit7.Text);
      Form1.LabeledEdit8.Text:=ChangeValPoint(Form1.LabeledEdit8.Text);
      Form1.LabeledEdit9.Text:=ChangeValPoint(Form1.LabeledEdit9.Text);

      Uust:=StrToFloat(Form1.LabeledEdit1.Text);
      Kp:=StrToFloat(Form1.LabeledEdit2.Text);
      Ki:=StrToFloat(Form1.LabeledEdit3.Text);
      Kd:=StrToFloat(Form1.LabeledEdit4.Text);
      T0:=StrToFloat(Form1.LabeledEdit5.Text);
      n:=StrToInt(Form1.LabeledEdit6.Text);
      Ku:=StrToFloat(Form1.LabeledEdit7.Text);
      min_integ_error:=StrToFloat(Form1.LabeledEdit8.Text);
      max_integ_error:=StrToFloat(Form1.LabeledEdit9.Text);

//      n:=200;


      for i:=1 to n do
        begin

          f[i]:=Ku * fPID(i, 0.0);
          Series1.AddXY(i, f[i]);

        end;


      diapazon:=Uust;

      Uk:=0.0;
      Uk1:=0.0;

      ek:=0.0;
      ek1:=0.0;
      ek2:=0.0;

      integral:=0.0;

      if (CheckBox1.State = cbChecked) then begin
      for i:=1 to n do
        begin

          ek:=Uust - Uk;

          f1[i]:=ek;
          Series4.AddXY(i, f1[i]);

//          integral:=integral + ek;

          if ((min_integ_error < ek) and (ek < max_integ_error))
            then integral:=integral + ek
            else integral:=0.0;

          Uk:=Kp * ek + Ki * integral * T0 + (Kd * (ek - 2.0 * ek1 + ek2)) / T0;
//          Uk:=Kp * ek + Ki * integral + (Kd * (ek - 2.0 * ek1 + ek2));

          if (-1.0 * diapazon > Uk) then Uk:=-1.0 * diapazon;
          if (Uk > diapazon) then Uk:=diapazon;

          f2[i]:=Uk;
          Series2.AddXY(i, f2[i]);

          f[i]:=Ku * fPID(i, Uk);
          Series3.AddXY(i, f[i]);

          ek2:=ek1;
          ek1:=ek;

          Uk:=f[i];


{ http://kazus.ru/forums/showthread.php?t=11558 }
{ http://kazus.ru/forums/showthread.php?t=11558&page=4 }

        end;
      end
      else begin
        Series2.Clear;
        Series3.Clear;
        Series4.Clear;
      end;


      Uk:=0.0;
      Uk1:=0.0;

      ek:=0.0;
      ek1:=0.0;
      ek2:=0.0;

      integral:=0.0;

      if (CheckBox2.State = cbChecked) then begin
      for i:=1 to n do
        begin

          ek:=Uust - Uk;

          f1[i]:=ek;
          Series7.AddXY(i, f1[i]);

          integral:=ek;

          if ((min_integ_error < integral) and (integral < max_integ_error))
            then integral:=integral
            else integral:=0.0;

          Uk:=Uk1 + Kp * (ek - ek1) + Kp * Ki * integral + Kp * Kd * (ek - 2.0 * ek1 + ek2);

          if ((-1.0 * diapazon) > Uk) then Uk:=-1.0 * diapazon;
          if (Uk > diapazon) then Uk:=diapazon;

          f2[i]:=Uk;
          Series5.AddXY(i, f2[i]);

          f[i]:=Ku * fPID(i, Uk);
          Series6.AddXY(i, f[i]);

          ek2:=ek1;
          ek1:=ek;

          Uk:=f[i];
          Uk1:=Uk;

{ http://ru.wikipedia.org/wiki/%D0%9F%D0%98%D0%94-%D1%80%D0%B5%D0%B3%D1%83%D0%BB%D1%8F%D1%82%D0%BE%D1%80 }

        end;
      end
      else begin
        Series5.Clear;
        Series6.Clear;
        Series7.Clear;
      end;


      { Ñêîðîñòíîé ìåòîä }
      q0:=Kp * (1.0 + Kd);
      q1:=Kp * (-1.0 - 2.0 * Kd + Ki);
      q2:=Kp * Kd;

      Uk:=0.0;
      Uk1:=0.0;

      ek:=0.0;
      ek1:=0.0;
      ek2:=0.0;

      if (CheckBox3.State = cbChecked) then begin
      for i:=1 to n do
        begin

          ek:=Uust - Uk;

          f1[i]:=ek;
          Series10.AddXY(i, f1[i]);

          Uk:=Uk1 + q0 * ek + q1 * ek1 + q2 * ek2;

          if (-1.0 * diapazon > Uk) then Uk:=-1.0 * diapazon;
          if (Uk > diapazon) then Uk:=diapazon;

          f2[i]:=Uk;
          Series8.AddXY(i, f2[i]);

          f[i]:=Ku * fPID(i, Uk);
          Series9.AddXY(i, f[i]);

          ek2:=ek1;
          ek1:=ek;

          Uk:=f[i];
          Uk1:=Uk;

{ http://www.rlda.ru/PID_Control_Tutor.htm }

        end;
      end
      else begin
        Series8.Clear;
        Series9.Clear;
        Series10.Clear;
      end;


      { Ìåòîä òðàïåöèè }
      q0:=Kp * (1.0 + 0.5 * Ki + Kd);
      q1:=Kp * (-1.0 - 2.0 * Kd + 0.5 * Ki);
      q2:=Kp * Kd;

      Uk:=0.0;
      Uk1:=0.0;

      ek:=0.0;
      ek1:=0.0;
      ek2:=0.0;

      if (CheckBox4.State = cbChecked) then begin
      for i:=1 to n do
        begin

          ek:=Uust - Uk;

          f1[i]:=ek;
          Series13.AddXY(i, f1[i]);

          Uk:=Uk1 + q0 * ek + q1 * ek1 + q2 * ek2;

          if (-1.0 * diapazon > Uk) then Uk:=-1.0 * diapazon;
          if (Uk > diapazon) then Uk:=diapazon;

          f2[i]:=Uk;
          Series11.AddXY(i, f2[i]);

          f[i]:=Ku * fPID(i, Uk);
          Series12.AddXY(i, f[i]);

          ek2:=ek1;
          ek1:=ek;

          Uk:=f[i];
          Uk1:=Uk;

{ http://www.rlda.ru/PID_Control_Tutor.htm }

        end;
      end
      else begin
        Series11.Clear;
        Series12.Clear;
        Series13.Clear;
      end;


    end;


Сообщение отредактировал Jenyok2 - Oct 9 2011, 16:43
Go to the top of the page
 
+Quote Post

Сообщений в этой теме
- sigmaN   PID регулятор от Atmel   May 4 2010, 06:00
- - Dog Pawlowa   Цитата(sigmaN @ May 4 2010, 09:00) В обще...   May 4 2010, 06:54
- - MSprut   Делал регулятор для бензинового двигателя на атмел...   May 4 2010, 07:21
- - AHTOXA   Цитата(sigmaN @ May 4 2010, 12:00) Регуля...   May 4 2010, 07:56
- - sigmaN   ЦитатаДля успешной работы регулятора нужно иметь д...   May 4 2010, 17:54
- - kamil yaminov   Может еще посмотреть в сторону всяких апериодическ...   May 5 2010, 03:50
- - DL36   Заготовка с плавающей точкой   May 5 2010, 04:58
- - sigmaN   А почему в атмеловском примере нигде не учитываетс...   May 5 2010, 11:39
|- - SSerge   Цитата(sigmaN @ May 5 2010, 18:39) А поче...   May 5 2010, 12:27
- - sigmaN   ЦитатаУ них эта зависимость засунута в коэффициент...   May 5 2010, 12:36
|- - raf329   вызывать pid_Reset_Integrator() при error == 0 или...   May 24 2010, 10:32
|- - demiurg_spb   Цитата(raf329 @ May 24 2010, 14:32) вызыв...   May 24 2010, 11:14
|- - Tanya   Цитата(demiurg_spb @ May 24 2010, 15:14) ...   May 24 2010, 11:17
|- - demiurg_spb   Цитата(Tanya @ May 24 2010, 15:17) Не зна...   May 24 2010, 11:22
|- - raf329   c ПИД регулятором работаю впервые, так когда необх...   May 24 2010, 11:41
||- - demiurg_spb   Цитата(raf329 @ May 24 2010, 15:41) c ПИД...   May 24 2010, 12:08
|- - Tanya   Цитата(demiurg_spb @ May 24 2010, 15:22) ...   May 24 2010, 12:33
|- - demiurg_spb   Цитата(Tanya @ May 24 2010, 16:33) Предуп...   May 24 2010, 13:11


Reply to this topicStart new topic
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0

 


RSS Текстовая версия Сейчас: 22nd July 2025 - 09:04
Рейтинг@Mail.ru


Страница сгенерированна за 0.0147 секунд с 7
ELECTRONIX ©2004-2016