Necesita manejar dos problemas:
- desbordamiento aritmético
- enrollador integrador
El desbordamiento aritmético es bastante sencillo: siempre que esté haciendo matemática entera, asegúrese de usar valores intermedios de mayor ancho: por ejemplo, si a
y b
son 16 bits, y los suma / resta, use un intermedio de 32 bits valor y limítelo al rango de un valor de 16 bits (0 a 65535 para sin signo, -32768 a 32767 para firmado) antes de volver a reducir a 16 bits. Si está absolutamente seguro de que nunca puede obtener un desbordamiento, porque está absolutamente seguro de los rangos de las variables de entrada, puede omitir este paso, pero tenga cuidado.
El problema del integrador windup es más sutil. Si tiene un gran error durante un período prolongado de tiempo, de modo que alcance el límite de saturación de la salida del controlador, pero el error sigue siendo distinto de cero, entonces el integrador seguirá acumulando el error, posiblemente haciéndose mucho más grande de lo que debería para lograr estado estable. Una vez que el controlador sale de la saturación, el integrador tiene que volver a bajar, causando demoras innecesarias y posiblemente inestabilidad en la respuesta de su controlador.
En otra nota:
Recomiendo encarecidamente (sí, sé que esta pregunta tiene 18 meses de edad, por lo que probablemente haya terminado su tarea, pero para beneficio de los lectores, supongamos que no lo es) que calcule el término integral de manera diferente: en lugar de Ki * (error integrado), calcule la integral de (Ki * error).
Hay varias razones para hacerlo; puede leerlos en una publicación de blog que escribí sobre cómo implementar los controladores PI correctamente .
<stdint.h>
foruint8_t
yuint16_t
, en lugar deunsigned int
yunsigned char
.unsigned
variables para un controlador PI? Esto agrega mucha complejidad a su código; losif/else
casos separados son innecesarios (a menos que use ganancias diferentes dependiendo del signo de error) También está usando el valor absoluto de la derivada, que es incorrecto.PID_derivative
asignación; obtienes el mismo valor si cambiasPID_error
yPID_lastError
. Y para el caso que ya ha perdidoPID_error
signo 's: si la última vezsetMotorSpeed =8
ycurrentMotorSpeed = 15
, y esta vezsetMotorSpeed = 15
ycurrentMotorSpeed = 8
, a continuación, obtendrá unPID_derivative
valor de 0, lo cual es incorrecto.unsigned char
es un tipo de 8 bits yunsigned int
es de 16 bits: siPID_kd = 8
yPID_derivative = 32
, entonces su producto será(unsigned char)256 == 0
, porque en C, el producto de dos enteros del mismo tipo T también es de ese tipo mismo tipo T. Si desea hacer una multiplicación 8x8 -> 16, debe convertir uno de los términos en un número de 16 bits sin signo antes de la multiplicación, o utilizar un compilador intrínseco (MCHP los llama "incorporados") diseñados para darte una multiplicación de 8x8 -> 16.