ПИД-управление двигателем разгона

У меня есть PID, управляющий двигателем постоянного тока. Я пытаюсь очень точно контролировать скорость двигателя. Мой контроллер позволяет мне изменять направление вращения двигателя и задавать ему скорость. Поэтому у меня есть PID, у которого есть плюс и минус максимум и минимум. Для того, чтобы ускорить устройство и замедлить устройство достаточно быстро. Выход PID предназначен для pwm и, следовательно, является абсолютным значением PID, просто изменяя вывод направления, когда PID < 0. Я использую противоположное направление двигателя только в качестве тормозной системы. Таким образом, двигатель всегда должен двигаться в одном направлении, но должен быстрее замедляться, применяя обратный крутящий момент.

Я пишу прошивку C в MCUXpresso. Графики получаются при отправке данных через UART на Arduino, чтобы легко отображать данные.

Моя проблема заключается в том, что иногда, когда переменная процесса достигает 0 или близко к нему, ПИД-регулятор инвертируется и должен стать отрицательным, и, таким образом, двигатель вращается на полной скорости в противоположном направлении. На двух рисунках ниже показаны определенные случаи, когда это произошло. Красная линия – это уставка, а синяя линия – переменная процесса.

Код, управляющий устройством и PID, приведен ниже.

Мне трудно понять, почему PID убегает вот так. Любая помощь была бы потрясающей. Спасибо!

введите описание изображения здесь

введите описание изображения здесь

Основной контроль

int dir = FORWARD; //Controls direction of motor

motorPID.setpoint = vehicleSpeed;

motorPID.input = SM_GetRPM();

motorPID.input = motorPID.input * speedConversion;

UART_SendPID((uint8_t)motorPID.input, (uint8_t)motorPID.setpoint);

PID_Compute(&motorPID);

if(motorPID.output < 0){
    dir = BACKWARD;
}

if(motorPID.setpoint == 0){
    motorPID.output = 0;
}

if(motorPID.input > 60){
    MC_SetMotorSpeed(0, dir);
    int test = 0;
}

MC_SetMotorSpeed(abs(motorPID.output),dir);

PID-код

//Find all error variables
self->lastError = self->error;
double input = self->input;                         //Only so input can't change during compute
self->error = self->setpoint - input;
self->integral += self->error;
double derivative = self->error - self->lastError;

//Anti-integral Windup
if(self->integral > self->Outmax)
    self->integral = self->Outmax;
else if(self->integral < self->Outmin)
    self->integral = self->Outmin;

//Calculate PID
self->output = (self->Kp*self->error) + (self->Ki * self->integral) + (self->Kd * derivative);

//Set limits
if(self->output > self->Outmax)
    self->output = self->Outmax;
else if(self->output < self->Outmin)
    self->output = self->Outmin;

РЕДАКТИРОВАТЬ: оказалось, что это была комбинированная ошибка описанной проблемы и проблемы с оборудованием.

Пожалуйста, публикуйте код в виде текста, а не необрезанного снимка экрана с кодом. Таким образом, он будет разборчивым, и мы сможем скопировать и вставить его для редактирования в ответы. Убедитесь, что вы используете кнопку тега кода и правильно форматируете / форматируете его. Вы также можете упомянуть, что такое среда разработки и язык программирования.
Подобные внезапные огромные скачки сигнала часто возникают из-за проблем с целочисленным переполнением. Если на пути прохождения сигнала есть какие-либо целые числа (например, MC_SetMotorSpeed?), проверьте их или покажите.
Ваш PID-код выглядит нормально, за исключением того, что вы должны увеличивать self->integral с ошибкой * self->Ki (и не выполнять это умножение в строке self->output), или вы должны сравнивать self->integral * self- >Ки с селф->Аутмин и селф->Аутмакс.
@TimWescott Ограничение интеграла после его умножения на Ki выглядит так, как будто это могло быть проблемой. Я ценю вашу помощь, и я должен был понять это раньше. Спасибо. Если бы вы могли поместить свой комментарий в ответ, это было бы решением на данный момент.

Ответы (2)

Попробуйте изменить строку, которая читает

self->integral += self->error;

к

self->integral += self-> Ki * self->error;

и сопоставьте это, изменив строку, которая читает

self->output = (self->Kp*self->error) + (self->Ki * self->integral) + (self->Kd * derivative);

к

self->output = (self->Kp*self->error) + self->integral + (self->Kd * derivative);

Это позволит правильно масштабировать интегральный член для вашего ограничивающего шага интегратора.

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

В идеале у вас должно быть математическое описание вашей системы, чтобы у вас была передаточная функция, после чего вы могли бы просто считать пропускную способность из передаточной функции. Эта полоса пропускания также является частотой Найквиста, которая является минимальной «приемлемой» (используя это слово в широком смысле) частотой выборки сигнала. Обычно вы хотите сэмплировать примерно в 30-40 раз больше частоты Найквиста. Если это невозможно, я предлагаю заменить датчики.

Я столкнулся с подобными проблемами в прошлом семестре в университете в своем проекте, надеюсь, это поможет!

Графики показывают только скорость, с которой данные отправляются в Arduino. Я не беспокоюсь о пропускной способности в этом решении, так как шаг расчета PID можно настроить.
Итак, если я правильно понял график и себя, номер выборки находится по оси абсцисс, а значение ШИМ — по оси у?
Мои извинения. Я должен был ясно это объяснить. Ось х — это время, а ось у — скорость двигателя.