Você precisa lidar com dois problemas:
- estouro aritmético
- conclusão do integrador
O estouro aritmético é bastante direto - sempre que você estiver fazendo cálculos inteiros, use valores intermediários de largura maior: por exemplo, se a
e b
são de 16 bits, e você os adiciona / subtrai, usa um intermediário de 32 bits e limite-o ao intervalo de um valor de 16 bits (0 a 65535 para não assinado, -32768 a 32767 para assinado) antes de converter novamente para 16 bits. Se você tiver certeza absoluta de que nunca poderá ficar sobrecarregado, porque tem certeza absoluta dos intervalos das variáveis de entrada, poderá pular esta etapa, mas tenha cuidado.
O problema de conclusão do integrador é mais sutil. Se você tiver um erro grande por um longo período de tempo, para atingir o limite de saturação da saída do controlador, mas o erro ainda for nulo, o integrador continuará acumulando erros, possivelmente ficando muito maior do que deveria alcançar curso estável. Depois que o controlador sai da saturação, o integrador precisa voltar a funcionar, causando atraso desnecessário e possivelmente instabilidade na resposta do controlador.
Em outra nota:
Eu recomendaria fortemente (sim, eu sei que essa pergunta tem 18 meses, então você provavelmente já concluiu sua tarefa, mas para o benefício dos leitores, vamos fingir que não é) que você calcule o termo integral de maneira diferente: Em vez de Ki * (erro integrado), calcule a integral de (erro Ki *).
Existem várias razões para fazê-lo; você pode lê-los em um post que escrevi sobre como implementar os controladores PI corretamente .
<stdint.h>
foruint8_t
euint16_t
, em vez deunsigned int
eunsigned char
.unsigned
variáveis para um controlador PI? Isso adiciona muita complexidade ao seu código; osif/else
casos separados são desnecessários (a menos que você use ganhos diferentes dependendo do sinal de erro) Você também está usando o valor absoluto da derivada, que está incorreto.PID_derivative
tarefas; você obtém o mesmo valor se você alternarPID_error
ePID_lastError
. E, por falar nisso, você já perdeuPID_error
o sinal de s: se da última vezsetMotorSpeed =8
ecurrentMotorSpeed = 15
, e dessa vezsetMotorSpeed = 15
ecurrentMotorSpeed = 8
, então você obterá oPID_derivative
valor 0, que está errado.unsigned char
for do tipo de 8 bits e do tipo deunsigned int
16 bits: ifPID_kd = 8
ePID_derivative = 32
, então, o produto será(unsigned char)256 == 0
, porque em C, o produto de dois números inteiros do mesmo tipo T também é daquele mesmo tipo T. Se você deseja fazer uma multiplicação 8x8 -> 16, é necessário converter um dos termos em um número de 16 bits não assinado antes da multiplicação ou usar um compilador intrínseco (o MCHP os chama de "integrados") projetados para dê a você 8x8 -> 16 multiplicado.