Вам нужно решить две проблемы:
- арифметическое переполнение
- интегратор windup
Арифметическое переполнение довольно просто - всякий раз, когда вы выполняете целочисленную математику, убедитесь, что вы используете промежуточные значения большей ширины: например, если a
и b
16-битные, и вы добавляете / вычитаете их, используйте 32-битное промежуточное значение значение, и ограничьте его диапазоном 16-битного значения (от 0 до 65535 для без знака, от -32768 до 32767 для со знаком) перед приведением к 16 битам. Если вы абсолютно уверены, что вы никогда не получите переполнение, потому что вы абсолютно уверены в диапазонах входных переменных, тогда вы можете пропустить этот шаг, но будьте осторожны.
Проблема с запуском интегратора более тонкая. Если у вас есть большая ошибка в течение длительного периода времени, так что вы достигнете предела насыщения выходного сигнала вашего контроллера, но ошибка все еще не равна нулю, то интегратор будет продолжать накапливать ошибку, возможно, становясь намного больше, чем он должен достичь устойчивое состояние. Как только контроллер выходит из насыщения, интегратору приходится возвращаться вниз, вызывая ненужную задержку и, возможно, нестабильность реакции вашего контроллера.
На другой ноте:
Я настоятельно рекомендую (да, я знаю, что этому вопросу исполнилось 18 месяцев, так что вы, вероятно, выполнили свою задачу, но для удобства читателей притворимся, что это не так), чтобы вы вычислили интегральный термин по-другому: вместо Ки * (интегрированная ошибка), вычислить интеграл от (Ki * error).
Есть несколько причин для этого; Вы можете прочитать их в блоге, который я написал о том, как правильно реализовать PI-контроллеры .
<stdint.h>
дляuint8_t
иuint16_t
, а неunsigned int
иunsigned char
.unsigned
какого чёрта вы используете переменные для ПИ-контроллера? Это добавляет сложности вашему коду; отдельныеif/else
случаи не нужны (если вы не используете разные коэффициенты усиления в зависимости от знака ошибки). Вы также используете абсолютное значение производной, что неверно.PID_derivative
назначение; вы получите то же значение, если вы переключитесьPID_error
иPID_lastError
. И в этом отношении вы уже потерялиPID_error
знак: если в прошлый разsetMotorSpeed =8
иcurrentMotorSpeed = 15
, а в этот разsetMotorSpeed = 15
иcurrentMotorSpeed = 8
тогда вы получитеPID_derivative
значение 0, что неверно.unsigned char
он является 8-битным типом иunsigned int
16-битным типом: еслиPID_kd = 8
иPID_derivative = 32
, то их продукт будет(unsigned char)256 == 0
, потому что в C произведение двух целых чисел одного типа T также того же типа T. Если вы хотите сделать умножение 8x8 -> 16, вам нужно привести одно из слагаемых к 16-разрядному числу без знака перед умножением или использовать встроенный компилятор (MCHP называет их «встроенными»), предназначенные для дать вам 8x8 -> 16 умножить.