ПИД-регулятор для управления скоростью двигателя

Я пытался применить ПИ-регулятор к двигателю с помощью Arduino. Я в некоторой степени справился с задачей, но проблема в том, что она слишком сильно колеблется. Также я не мог понять, что делать, если ШИМ-сигнал считается отрицательным

  int counter = 0;
  double end_time=0;
  double ini_time=0;
  double rpm=0;
  double my_time=0;
  double temp = 0;
  int pid_speed=0;
  float kp=2.25;
  float ki=.01;
  float kd=0.005;
  int prev_error=0;
  double pwm_sig=0;
  int integrator  = 0;
  int motor_sig_pin= 5;
  int error=0;
  int absolute = 0;
  float desired_rpm=1000; //мое желаемое значение числа оборотов в минуту
  double zero_time=0;
  double i_time=0;
  double f_time=0;
  int rpm_tacho=0;
  int previous_error=0;
  int derivative=0;

  void setup() {
    pinMode(2,INPUT);
    digitalWrite(2,HIGH);
    pinMode(motor_sig_pin,OUTPUT);
    pinMode(9,OUTPUT);
    pinMode(10,OUTPUT);
    digitalWrite(9,HIGH);
    digitalWrite(10,LOW );
    attachInterrupt (0,Count,RISING);
    ini_time = micros();
    i_time=micros();
    Serial.begin(115200); 
  }

   void Count(){ //вычисление времени для одного раунда с помощью кодировщика

      counter++;
      if (counter>=100){
        end_time=micros()-ini_time;

        ini_time=micros();
        counter=0;
      }
   }
  void loop() { 
      rpm = ((1000000*60)/(end_time));
      rpm = abs(rpm);
      rpm_tacho = ((1000000*60)/(zero_time));
      if (rpm <10 || rpm>5000){
        rpm=0;
      }
      error=desired_rpm-rpm;

      integrator += error;
      pid_speed = kp*error; + ki*integrator;

      pid_speed=(int)pid_speed;

      Serial.print(pid_speed);
      Serial.print("       ");

      if (pid_speed> 0){
        if (pid_speed>255)
          analogWrite(motor_sig_pin,255);
        else
          analogWrite(motor_sig_pin,pid_speed);
      }
      if(pid_speed<0){
        if(pid_speed<-255)
        {
            analogWrite(motor_sig_pin,0); 
        }
        else
       {
          pid_speed=abs(pid_speed);
          analogWrite(motor_sig_pin,255-pid_speed);
       }
      }
      Serial.print ("      ");
      Serial.println(rpm);
  }

, 👍0

Обсуждение

Всего несколько комментариев: 1) end_time используется как в обычном контексте, так и в контексте прерывания, поэтому его следует квалифицировать как "летучий". 2) В цикле, поскольку end_time занимает больше одного байта, вам следует скопировать его в локальную переменную, блокируя прерывания. 3) Вы не используете kd. 4) Перед + ki*integrator стоит ;, что означает, что вычисление интегрального члена не является операцией., @Edgar Bonet

@EdgarBonet, что вы подразумеваете под блокировкой прерывания., @Adnan

noInterrupts(); двойной end_time_copy = end_time; прерывания();, затем используйте копию вместо оригинала. В противном случае Count() может обновить end_time, пока вы его читаете., @Edgar Bonet

5) ini_time и end_time должны быть unsigned long или uint32_t, иначе тахометр вернет мусор, когда micros() обнулится. 6) Тогда нет смысла в abs(rpm), поскольку rpm не может быть отрицательным. 7) rpm_tacho не используется и вычисляется путем деления на ноль. 8) Перед публикацией кода необходимо правильно сделать отступ., @Edgar Bonet

9) Вам также следует удалить все переменные, которые вы никогда не используете. 10) Нет смысла приводить int к int. 11) Ваш набор if...else выглядит слишком сложным, почему бы просто не constrain(pid_speed, 0, 255)? 12) Не следует выполнять Serial.print() на каждой итерации цикла, поскольку его слишком много для чтения, и это замедляет loop(). 13) Но тогда вам следует контролировать частоту обновления или свой PID., @Edgar Bonet

Расчеты pid также выдают отрицательные значения, что мне для этого делать?? @ЭдгарБонет, @Adnan

[constrain()](http://www.arduino.cc/en/Reference/Constrain). Или, еще лучше, используйте готовую [библиотеку PID](http://playground.arduino.cc/Code/PIDLibrary)., @Edgar Bonet

float kp=2,25;; Я не думаю, что пропорциональная часть должна быть больше «1,0», но здесь я могу ошибаться., @Gerben

@Gerben: kp может быть больше 1. Оно должно быть любым, необходимым для преобразования измеренной входной ошибки в выходные данные процесса. Если бы входными данными были радианы в секунду, а не об/мин, kp должно быть в 2*pi*60=376 раз больше, чем kP было бы для датчика, измеряющего обороты в минуту., @Dave X


2 ответа


1

Вместо того, чтобы думать о выходе ПИД-регулятора как о pid_speed, думайте о нем как о мощности двигателя, и то, что делает ПИ-регулятор, — это переводит ошибки в оборотах в настройку мощности двигателя.

Вероятно, вы получаете колебания из-за интегрального сбоя. Если ваш цикл быстрый по сравнению с вашей системой, ошибка каждого цикла добавляется к интегратору быстрее/быстрее, чем система может отрегулировать. Например, если цикл занимает 1 мс, ошибка в 100 об/мин может переполнить «интегратор int» за 32768 всего за 0,327 секунды и за порог, при котором интегратор может привязать выход 25500 (= 255/Ki) всего за 0,255 секунды. интегратор быстрее системы, он легко может вызвать перерегулирование. Вы можете рассмотреть возможность замедления цикла до 10 мс, 100 мс или 1000 с, чтобы соответствовать физическому процессу и сделать член kI разумным преобразованием rpmErrorсекунд/100 интегратора, rpmErrorсекунд/10 или rpmError*. секунд на выходную мощность.

Чтобы лучше обрабатывать числовые значения и ограничения, такие как переполнение интеграторов и отрицательные выходные значения, вероятно, лучше всего использовать библиотеку PID Arduino из http://playground.arduino.cc/Code/PIDLibrary как в комментарии Эдгара Боне.

,

0

Я не знаю, существует ли этот вопрос еще или вам удалось его решить. Тем не менее, я работал (до сих пор работаю) над самобалансирующимся роботом, где я также столкнулся с такой проблемой и Я пришел на этот форум. Я решил эту проблему, установив выходы ПИД-регулятора (при условии, что вы используете библиотеку ПИД-регуляторов Arduino) от 0 до 255 вместо -255 до 255. Теперь для моего проекта мне нужно было изменить направление вращения двигателя в зависимости от угла наклона двигателя. робот. Итак, каждый раз, когда мне нужно изменить направление вращения двигателя, я делаю следующее

  • Сначала я сбрасываю Iterm (или обнуляю его).
  • Измените направление вращения.
  • Снова начните вычисление PID.

Раньше я тоже пытался добиться этого от -255 до 255, но это было очень рывками, и я не был уверен, что это правильный способ сделать это. С моим нынешним подходом мне удалось решить мою проблему.

,