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

PID-регулятор - не могу добиться плавности.

Друзья, помогите с натройкой PID-регулятора.  Хотя тут наверное проблема не в настройке коэффициентов, а в самом коде. Работает он в печке, которая горячо обсуждалась в соседней теме. Забегая вперед, скажу, что термопрофиль она держит прекрасно. Но есть одно но.

 

Мощность нагревателя  у меня регулируется от 1 до 100.  С шагом в 1.   А регулятор выдает значения кратные 10-ти, то есть при его настройках он способен выдать всего 10 значений

Стал настраивать, начал, как полагается с коэффициента P.

Температура смогла "угнаться" за графиком только тогда, когда я поставил коэффициент P=10. При этом никогда не было необходимости во включении печки на полную мощность - максимум 50%.

Если ставить скажем 3, то картина такая 

YfQeNH80.png

 

Если же ставить P=10, то, повторюсь, все нормально, однако регулятор выдает значения в диапазоне от 0 до 100 с шагом 10.

То есть 0,10,20,30,40...

И когда идеальным значением, к примеру было бы 5 - регулятор попеременно выдает 10 и 0.  В виду температурной инерции это на конечный результат не влияет, но лампы мигают.

Что не так?

 

 

Код (не мой)

/************************************************************************/
/* AVR PID Library source file                                          */
/* @author Gazizov A.T. [email protected]                                  */
/************************************************************************/
#include "stdint.h"
#include "pid.h"

/** Structure for storing PID data between calculations */
typedef struct PID_DATA {
  int16_t lastProcessValue;
  int32_t integralTerm;
  double kp;
  double ki;
  double kd;
  int16_t MAX_OUT;
  int16_t MIN_OUT;
} pidData_t;

struct PID_DATA pidData;

/* PID controller initialization. Should be called only once.
 *  @param kp - proportional coefficient.
 *  @param ki - integral coefficient.
 *  @param kd - differential coefficient. 
 */
void pid_Init(double kp, double ki, double kd)
{
  pidData.integralTerm     = INITIAL_INTEGRAL_TERM;
  pidData.lastProcessValue = INITIAL_PROCESS_VALUE;
  pid_setParams(kp, ki, kd);
  pid_setOutputLimits(DEFAULT_MIN_OUT, DEFAULT_MAX_OUT);
}

/* Main PID controller function. It should be called regularly, each SAMPLE_TIME sec.
 * @param setPoint - level desired at output of the control object.
 * @param processValue - level obtained from the control object.
 * @returns input to control object.
 */
double pid_Controller(int16_t setPoint, int16_t processValue)
{
  double error, p_term, d_term;
  double out;

  error = setPoint - processValue;

  pidData.integralTerm += pidData.ki * error;
  if (pidData.integralTerm > pidData.MAX_OUT) {
    pidData.integralTerm = pidData.MAX_OUT;
	out = pidData.MAX_OUT;
  } else {
	d_term = pidData.kd * (processValue - pidData.lastProcessValue);
	p_term = pidData.kp * error;
	out = (p_term + pidData.integralTerm - d_term);
	if (out > pidData.MAX_OUT) {
		out = pidData.MAX_OUT;
		} else if (out < pidData.MIN_OUT){
		out = pidData.MIN_OUT;
	}
  }
  pidData.lastProcessValue = processValue;
  
  return out;
}

/** Restricts PID controller output.
 *  @param Min - minimal allowed output value.
 *  @param Min - maximal allowed output value.
 */
void pid_setOutputLimits(int16_t Min, int16_t Max)
{
	if(Min > Max) return;
	pidData.MIN_OUT = Min;
	pidData.MAX_OUT = Max;
}

/** Set PID controller params with consideration of sampling time.
 *  @param Kp - proportional coefficient.
 *  @param Ki - integral coefficient.
 *  @param Kd - differential coefficient.
 */
void pid_setParams(double Kp, double Ki, double Kd)
{
   if (Kp<0 || Ki<0 || Kd<0) return;
   
   pidData.kp = Kp;
   
   pidData.ki = Ki * SAMPLE_TIME;
   pidData.kd = Kd / SAMPLE_TIME;
}

 

Заголовок

/************************************************************************/
/* AVR PID Library header file                                          */
/* @author Gazizov A.T. [email protected]                                  */
/************************************************************************/

/** Range of allowed PID output range. Needed to avoid sign/overflow problems. */
#define DEFAULT_MIN_OUT  0
#define DEFAULT_MAX_OUT  100


/** Sample time in seconds. 
 *  How often PID control function (pid_Controller) will be called. 
 *  Important setting dependent on timer implementation in your program.
 */
#define SAMPLE_TIME 0.1

/** Parameters for initializing */
#define INITIAL_INTEGRAL_TERM 0
#define INITIAL_PROCESS_VALUE 0

/** Library functions declaration */
void pid_Init(double p_factor, double i_factor, double d_factor);
void pid_setParams(double Kp, double Ki, double Kd);
void pid_setOutputLimits(int16_t Min, int16_t Max);
double pid_Controller(int16_t setPoint, int16_t processValue);

 

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


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

 

8 hours ago, -=Женек=- said:

Что не так?

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

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


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

20 minutes ago, rkit said:

 

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

Под методикой вы понимаете алгоритм расчета выходного сигнала или настройку коэффициентов?

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


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

вродь нормальный пид на первый взгляд.

отлаживать надо.

попробуйте SAMPLE_TIME поменять - может оно вносить эту кратность. 

но полюбому надо отладчиком смотреть что с вашими числами происходит, есть ли где кривое округление

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


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

Да.... если вдруг кто-то неправильно понял меня - график, что я привел, может навести кого либо (например форумца rkit) на мысль о том, что не настроен D коэффициент и имеет место статическая ошибка. Это не так. При коэффициенте p=3 -это лучшее, чего удается добиться за счет настройки остальных коэффициентов.

И при p=3 проблема все равно остается - ПИД не выдает выходных значений кроме как кратных трем. Меня именно это беспокоит.

8 minutes ago, AlexRayne said:

попробуйте SAMPLE_TIME поменять - может оно вносить эту кратность

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

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


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

А если "I" увеличить? По идее интеграл должен компенсировать расхождение с течением времени, пусть даже будут небольшие колебания вокруг уставки. Код не смотрел.

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


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

12 minutes ago, Alexashka said:

А если "I" увеличить? По идее интеграл должен компенсировать расхождение с течением времени, пусть даже будут небольшие колебания вокруг уставки. Код не смотрел.

 Зря я наверное график выложил. Проблема не в нем. Манипуляции с I , если мне не изменяет память, вносили задержку в реакции системы на отклонение температуры от заданной.

Что-то мне подсказывает, что умножение прмежуточных значений на коэффициент p происходит поздновато. То есть система рассчитала некое значение с точностью до единицы, а потом тупо умножила его на 10. 

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

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


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

Хм... если вглядеться в формулу, то получается, что на каждый 1 градус ошибки при коэффициенте р=10 система увеличивает мощность на 10 %. То есть нужно просто точнее измерять температуру. Если этому коду скармливать значения ошибки с шагом в 0.1 градус, то тогда и шаг температуры будет 1%. В принципе у меня разрешение измерения -0.25 градуса. А у меня регулятору подается значение типа int.

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


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

14 hours ago, -=Женек=- said:

Если же ставить P=10, то, повторюсь, все нормально, однако регулятор выдает значения в диапазоне от 0 до 100 с шагом 10.

То есть 0,10,20,30,40...

И когда идеальным значением, к примеру было бы 5 - регулятор попеременно выдает 10 и 0.  В виду температурной инерции это на конечный результат не влияет, но лампы мигают.

Что не так?

Вы и сами поняли уже похоже, нужно разрешение увеличить, не 100 градусов, а 1000 или 10000 значений. Тогда если статическая ошибка и будет, то будет она 0.1 градусов и вы ее наверное не увидите глазами. Но шаг будет маленький, нужно чаще вызывать.

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


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

44 minutes ago, Дима said:

Вы и сами поняли уже похоже, нужно разрешение увеличить, не 100 градусов, а 1000 или 10000 значений. Тогда если статическая ошибка и будет, то будет она 0.1 градусов и вы ее наверное не увидите глазами. Но шаг будет маленький, нужно чаще вызывать.

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

Не надо было бояться этого ПИДа), вот и все.  Если будет шаг в 2.5% мощности, то я думаю, это существенно улучшит ситуацию. Самое смешное, чтобы совсем не было мерцания от 0 до 2.5%, нужно увеличить величину тепловых потерь)))

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


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

Да.... вот что значит чужой код. Настроил разрешение измерения температуры - pid_out  стал дробным, но все равно стал меняться с шагом 10.

 

А цуцик вот где был зарыт

double pid_Controller(int16_t setPoint, int16_t processValue)

измеренная и целевая температуры выражались в целых числах.

 

За всеми проверять нужно...

15 hours ago, rkit said:

 

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

Так все-таки, поясните Ваши слова?

Какую методику я проигнорировал, в ней ли было дело?

Или так - прочитали по диагонали и решили выпендриться?

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


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

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

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

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

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

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

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

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

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

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