Controle PID

Boa noite galera,

Sou novo no fórum e se estiver criando errado o tópico me desculpe, me avisem que corrigirei.

Estou querendo fazer um projeto pra faculdade sobre uma elevatória de água, queria controlar a bomba através de um sensor ultrassônico hc-sr04 utilizando PID. Porém como ainda não domino muito bem a linguagem C do arduino, estou tendo umas dificuldade para adaptar a lógica. Realizei testes isolados com a bomba e com o sensor ultrassônico para aprender sobre cada um e montar a lógica PID com os 2, porém estou cometendo erros que não sei consertar.
Minhas dúvidas:

  1. Não sei mexer em bibliotecas portanto estou realizando a logica de acordo com o que vejo de exemplos. A lógica básica de PID do arduino define os pinos 0 analógico e 3 digital PWN como entrada e saída respectivamente, e o sensor ultrassônico utiliza pinos digitais PWM (utilizei 4 e 5 para trigger e echo). Não sei como relacionar os dados vindos do ultrassônico para colocar no PID. Tentei algumas conversões para jogar os valores do Echo para a entrada analógica 0 porém sem sucesso.

  2. Além de não conseguir realizar a tarefa anterior, ainda possuo o seguinte problema: a minha bomba possui tensão de operação de 7,5 V a 12 V. Estou controlando ela com um driver L298N Ponte H. Caso consiga a criação da lógica, ainda terá uma parte do range de saída do PID que não surtirá efeito na bomba. Quando o nível estiver próximo do setpoint, a tensão seria muito baixa para controlar a bomba e afetaria o processo.

Segue um exemplo da lógica que eu estava mexendo:

/********************************************************
 * PID Basic Example for 12V Pump - By Edes
 * Reading analog input 0 to control analog PWM output 3
 * Esse exemplo foi adaptado para funcionar com uma bomba 12V,
 * um driver L298N e um ultrassonico
 ********************************************************/

#include <PID_v1.h>           //Carrega a biblioteca PID

#include <Ultrasonic.h>       //Carrega a biblioteca do sensor ultrassonico

#define PIN_INPUT 0           //Define os pinos para Input e Output do PID
#define PIN_OUTPUT 3

#define pino_trigger 4        //Define os pinos para o trigger e echo do ultrasom
#define pino_echo 5

int IN1 = 7;                  //Saídas digitais 3 e 4 ligadas à entrada da Ponte H
int IN2 = 8;
//int variacV_PonteH = 6;     //Saída PWM ligada à  entrada da Ponte H p/ variação de tensão do motor

int cmMsec, inMsec;           //variáveis de conversão e visualização no monitor serial

//Define Variables we'll be connecting to
double Setpoint, Input, Output;

//Specify the links and initial tuning parameters
double Kp=2, Ki=5, Kd=1;
PID myPID(&Input, &Output, &Setpoint, Kp, Ki, Kd, DIRECT);

//Inicializa o sensor nos pinos definidos
Ultrasonic ultrasonic(pino_trigger, pino_echo);

void setup()
{
  int val = analogRead(pino_echo);
  val = map(val, 0, 255, 0, 1023);
  analogWrite(PIN_INPUT, val);
    
  //initialize the variables we're linked to
  Input = analogRead(PIN_INPUT);
  Setpoint = 512;

  //turn the PID on
  myPID.SetMode(AUTOMATIC);

  //Inicializa Pinos da bomba/motor e Ponte H
  pinMode(IN1, OUTPUT);
  pinMode(IN2, OUTPUT);
  //pinMode(variacV_PonteH, OUTPUT);

  //Inicia a serial com um baud rate de 9600
  Serial.begin(9600);
}

void loop()
{
  Input = analogRead(PIN_INPUT);
  myPID.Compute();
  analogWrite(PIN_OUTPUT, Output);


  //Motor
  //Sentido Horario:
  digitalWrite(IN1, HIGH);
  digitalWrite(IN2, LOW);

  //Sensor Ultrassonico
  //Le as informaçoes do sensor, em cm e pol
  int cmMsec, inMsec;
  long microsec = ultrasonic.timing();
  cmMsec = ultrasonic.convert(microsec, Ultrasonic::CM);
  inMsec = ultrasonic.convert(microsec, Ultrasonic::IN);

  //Teste da saida do PID para a saida da bomba
  //Output = variacV_PonteH;

  //imprimi os valores no monitor serial:
  Serial.print("Distancia em cm: ");
  Serial.print(cmMsec);
  //  Serial.print(" - Distancia em polegadas: ");
  //  Serial.print(inMsec);
  Serial.print(" - ");
  Serial.print(Input);
  Serial.print(" - ");
  Serial.println(Output);
  delay(500);
}

Como dito, eu não possuo muito conhecimento de Arduino, comecei a estuda-lo a pouco tempo e acabei me arriscando em algo um pouco mais complexo haha. Agradeço quem puder me ajudar. Até mais

Antes de mais... deverias ver qual o alcance do sensor de nível.

Depois a biblioteca tem exemplos para explicar como a usar. Isto normalmente é mais que suficiente para entender a funcionalidade da biblioteca.

Começa por praticar com este exemplo antes de pensares no PID:

Isto deve ser tudo o que precisas para usar o sensor.

Isto não faz sentido...

  int val = analogRead(pino_echo);
  val = map(val, 0, 255, 0, 1023);
  analogWrite(PIN_INPUT, val);

Isto

  Input = analogRead(PIN_INPUT);

Não faz sentido... a tua variável de entrada para o PID é o nível... não é nada vindo duma variável analógica.
A tua saída do PID é sim uma saída de PWM.

O teu código não manda PWM para a bomba... aparentemente, liga e desliga. Isso não é o que pretendes. Também não precisas duma ponte H... a bomba não é reversível. Logo podes ligar a bomba de forma directa com uma saída de PWM.

Depois tens de relacionar as saídas com números que façam sentido... por exemplo percentagem para a saída de PWM que controla a bomba e talvez litros para o nível do tanque.

Podes limitar os valores de saída com o método void SetOutputLimits(double, double);, experimenta com vários valores de PWM qual é o mínimo que faz a bomba mexer e coloca isso como o minimo.

Obrigado pela ajuda amigo.

O alcance do sensor é de 2 a 400 cm, isso pelo menos é o que consta no datasheet do aparelho, mas nem precisarei disso tudo, meu tanque de água irá possuir pelo menos 50 cm.
Eu fiquei na dúvida da declaração de variáveis do PID por definir o ) analógico, não sabia que poderia alterar para uma entrada digital, farei o teste, mas esqueci de perguntar: as variáveis declaradas como #define, posso simplesmente declarar como "int PIN_INPUT = ?" referente ao pino digital que quero?
A bomba de água eu só estou usando o driver por ser de tensão 12 V, e para não mexer direto com água, estou realizando os testes iniciais com um motor 12 V, assim que conseguir controlar o motor como quero, irei trocar pela bomba. Você disse que posso controla-lá direto com o PWM, como eu faria isso sendo ela de 12 V?

Obrigado novamente e assim que realizar as alterações posto aqui.

O #define não define variáveis. Apenas define expressões que podes usar no código para o tornar mais legível.

Por exemplo:

#define TRUE  1
#define SENSOR  3

if (digitalRead(SENSOR) == TRUE) ... 

//A alternativa seria: 

if (digitalRead(3) == 1) ...

Muito menos legível do que com os defines, correcto??

Para definir pinos de entrada e saída, podes usar um define ou usar uma variável com a indicação "const"

const unsigned char SensorDistancia = 3;

O PID por definição não funciona com entradas digitais... No entanto, o bloco de código do PID não sabe que sensor estás a utilizar, logo tu é que tens de calcular as variáveis e colocar na entrada do bloco de PID.
Da mesma maneira, depois tens de fazer sentido da variável de saída do PID. Daí eu ter dito que devias para já pegar no sensor e medir o nível em litros/cm/m^3, etc... depois podes com a bomba ver qual o caudal que obtens com diferentes níveis de PWM. Com estes valores, será mais simples fazeres as contas dos parâmetros a utilizar no PID.

Estou controlando ela com um driver L298N Ponte H.

Tu próprio disseste que estás a controlar a bomba com uma ponte H e um L298... se leres um pouco acerca das capacidades do L298, vais ver que consegues controlar a velocidade da bomba com PWM.

Eu fico com a sensação que tu basicamente não entendes nenhum dos conceitos necessários para realizar o projecto. Não entendes o tipo de sensor, não pareces entender como funciona um PID ou mesmo controlo de motores. Que curso é que estás a tirar na Universidade e em que ano estás??

O processo todo eu entendo amigo, o que não domino é o Arduino e sua linguagem que não tenho prática nem muito costumo. Estou com o Arduino a relativamente pouco tempo e não sei de toda a sua capacidade, e nem de seus equipamentos. Meu curso é de engenharia elétrica mas estou querendo me especializar em automação. O projeto é algo ainda em início e terei tempo suficiente para estudar e compreender tudo que usarei. Estou aqui para pedir ajuda com certas coisas que não consegui achar com facilidade em outros meios e aprender cada vez mais. Sou bem iniciante ainda mesmo como você pode ver.

Eu já realizei as medições com o sensor de nível e já possuo os dados dele em cm e polegadas. Pelo que pude entender, o Trigger é o responsável por enviar o sinal e o Echo é responsável por receber o sinal rebatido do Trigger, assim calculando através do tempo a distancia do objeto (corrija-me se estiver errado). Eu fiz algumas tentativas porém nenhuma delas funcionaram, não sei se errei também haha. Tentei converter os dados obtidos do sensor em centímetros para a variação de 0 a 1023 com o comando "map", e a nova variável acompanhava certinho a variação, mas quando eu tentava jogar os dados da conversão a variável da entrada ela não respondia corretamente. Como posso atribuir esse novo valor convertido a entrada analógica? Tentei fazendo um novo "map" e tentei igualando as variáveis, mas sem sucesso.

Agradeço mais uma vez pela ajuda. Tentarei as modificações de acordo com o que você me aconselhou e depois posto o resultado. Valeu!

Vamos por partes:

  • Sensor, se tu dizes que já tens o valor em cm e polegadas ,porque é que vais transformar numa variável de 0 a 1023? O nível em cm (por exemplo) é o que tu vais usar como variável Input no PID.

  • PID, a tua variável de entrada no PID vai ser o valor do sensor em cm... e da mesma forma o teu setpoint para o PID vai ser também o nível que pretendes em cm. Nota no entanto que tens de limitar os valores de alguma forma porque o PID não vai poder esvaziar o tanque. Apenas encher... Mas isso é um pequeno pormenor.

  • A variável de saída do PID vai ser um número. Por defeito esse número é entre 0 a 255 para poder ser utilizado directamente numa saída de PWM. Logo a variável Output é copiada para uma saída de PWM.

Se estás em engenharia electrotécnica, já deves saber o que é um PWM e como controlar a velocidade dum motor DC com isso... logo não me vou estender muito nesse aspecto.

Só para confirmar, o sensor já lê o nível em cm, correcto???

Se quiseres ler um pouco mais sobre essa aplicação tens aqui alguns links:

http://blog.opticontrols.com/archives/697

http://www.wavetechllc.com/educational/LevelTuningPID_sec.pdf

Olá,

Depois de ver os detalhes todos e ter estudado um pouco mais, eu vi que o problema era simplesmente a ordem em que as funções estavam declaradas, após a inversão, ficou tudo normal. Obrigado a ajuda amigo

Agora estou com um outro problema, estou indo além no projeto e querendo realizar a comunicação com um supervisório, pelo protocolo OPC. Pelo que acompanhei no site http://plcescada.com/automacao/opc-server-para-arduino/ , e pelo site st4makers, eles ensinam a utilizar o protocolo e realizar testes. Após os testes, tentei realizar o mesmo com a minha lógica, porém com muitos problemas

O servidor quando conecta não transfere as variáveis, somente quando quer. Quando transfere, a leitura das variáveis fica instável demais, poucos momentos tenho visualização dos dados. Acredito que seja algum detalhe na minha lógica, ou declaração errada ou algum detalhe que passou despercebido. Gostaria de ajuda. Segue a minha lógica.

/****************************************************************************************
   Lógica TCC By Edes - Automação de Elevatória de Água/Esgoto com Arduino
   Essa lógica foi criada com o propósito de adaptar o funcionamento de uma elevatória de
   água para uso com microcontrolador Arduino Mega. Utilizou-se uma bomba d'água de 12 V, 
   um driver L298N, um sensor ultrasônico HC-SR04, um sensor de vazão YF-S201 e uma fonte 
   chaveada 12 V - 5 A. O projeto consiste em realizar a medição de nível de um tanque a 
   montante da bomba e através dele, realizar um controle PID para a velocidade da bomba,  
   para assim deslocar uma coluna de água para um tanque a jusante da bomba. O Setpoint 
   (valor desejado) é referente ao nível do tanque a montante. Também será realizado
   medição para monitorar a vazão da bomba.
 ****************************************************************************************/
//Bibliotecas
#include <PID_v1.h>
#include <Ultrasonic.h>
#include <OPC.h>
#include <Bridge.h>
#include <SPI.h>

OPCSerial aOPCSerial;                                   //Declara o OPC object

//Variáveis e Pinos utilizados
#define PIN_OUTPUT 3                                    //Define os pinos para Input e Output do PID
//#define PIN_INPUT A0

#define pino_trigger 5                                  //Define os pinos para o trigger e echo do ultrasom
#define pino_echo 6

int IN1 = 4;                                            //Define os pinos como saídas digitais ligadas à entrada da Ponte H
//int IN2 = 7;

float cmMsec;                                           //Variáveis para conversão do sensor ultrassônico e visualização no monitor serial

float vazao;                                            //Variável para armazenar o valor em L/min
float media = 0;                                        //Variável para tirar a média a cada 1 minuto
int contaPulso;                                         //Variável para a quantidade de pulsos
int i = 0;                                              //Variável para contagem
int flow = 2;                                           //Define o pino como entrada do valor de vazão

int SetpointPorc;                                       //Variáveis para Visualização no monitor serial (% e cm)
float SetpointCM;

double Setpoint, Input, Output;                         //Define as variáveis que conectaremos ao PID

double Kp = 2, Ki = 5, Kd = 1;                          //Especifica as variáveis linkadas e os parametros iniciais do PID

//Teste OPC
float Output2;
float Setpoint2;

//Cria uma função callback para o OPCItem
float level_cm(const char *itemID, const opcOperation opcOP, const float value){
  return cmMsec;
}
float vazao_lm(const char *itemID, const opcOperation opcOP, const float value){
  return vazao;
}
int out(const char *itemID, const opcOperation opcOP, const int value){
  return Output2;
}
int show_set(const char *itemID, const opcOperation opcOP, const int value){
  return Setpoint2;
}

PID myPID(&Input, &Output, &Setpoint, Kp, Ki, Kd, DIRECT);

Ultrasonic ultrasonic(pino_trigger, pino_echo);         //Inicializa o sensor nos pinos definidos

void incpulso ()
{
  contaPulso++;                                         //Incrementa a variável de contagem dos pulsos
}


void setup()
{
//Controle PID
  //Defone os valores iniciais das variáveis PID
  Input = 0;                                        
  Setpoint = 0;

  //Liga a funçõo PID
  myPID.SetMode(AUTOMATIC);
  
//Driver L298N
  //Inicializa os pinos da Ponte H para acionamento do motor/bomba
  pinMode(IN1, OUTPUT);
  //pinMode(IN2, OUTPUT);

//Inicia a serial
  Serial.begin(9600);
  
//Sensor de Vazão YF-S201
  //Inicializa o pino e informações do sensor de vazão
  pinMode(flow, INPUT);
  attachInterrupt(0, incpulso, RISING);                  //Configurado (Interrupção 0) para trabalhar como interrupção
//  Serial.println("\n\nInicio\n\n");                      //Imprime Inicio na serial
//  Serial.println("Lendo dados do sensor...");

//OPC
  //Inicializa o OPC object
  aOPCSerial.setup();
  //Declara os OPCItem's
  aOPCSerial.addItem("Nivel Tanque",opc_read, opc_float, level_cm);
  aOPCSerial.addItem("Vazao_L/m",opc_read, opc_float, vazao_lm);
  aOPCSerial.addItem("Saida_Bomba",opc_read, opc_int, out);
  aOPCSerial.addItem("Setpoint",opc_readwrite, opc_int, show_set);

}

void loop()
{
//Teste
  Output2 = Output;
  Setpoint2 = Setpoint;
  
//Sensor Ultrassonico HC-SR04
  //Le as informações do sensor, em centímetros
  long microsec = ultrasonic.timing();
  cmMsec = ultrasonic.convert(microsec, Ultrasonic::CM);
  
//Sensor de Vazão YF-S201
  contaPulso = 0;                                        //Zera a variável para contar os giros por segundos
  sei();                                                 //Habilita interrupção
  delay (500);                                           //Aguarda 0,5 segundo
  cli();                                                 //Desabilita interrupção
  vazao = contaPulso / 7.5;                              //Converte para L/min

//Controle PID
  Input = map(cmMsec, 2, 25, 0, 1023);
  Setpoint = analogRead(A1);
  myPID.Compute();

  //Define o range de saída para a tensão de operação do motor/bomba
  if (Output >= 95) {
    analogWrite(PIN_OUTPUT, Output);
  }
  else {
    analogWrite(PIN_OUTPUT, 0);
  }

//OPC
  //Processa os comandos OPC
  aOPCSerial.processOPCCommands();

  //Motor
  //Define o sentido de rotação no caso de um motor ou habilita a Ponte H para acionamento da bomba:
  digitalWrite(IN1, HIGH);
  //digitalWrite(IN2, LOW);

  SetpointPorc = map(Setpoint, 0, 1023, 0, 100);
  SetpointCM = map(Setpoint, 0, 1023, 2, 25);

/*  //Imprimi os valores no monitor serial:
  Serial.print("Distancia cm: ");
  Serial.print(cmMsec);
  Serial.print(" Input: ");
  Serial.print(Input);
  Serial.print(" Output: ");
  Serial.print(Output);
  Serial.print(" Vazão: ");
  Serial.print(vazao);
  Serial.print(" L/min  ");
  Serial.print(" Setpoint: ");
  Serial.print(SetpointPorc);
  Serial.print("% - ");
  Serial.print(SetpointCM);
  Serial.print(" cm - ");
  Serial.print(Setpoint2);
  Serial.print(" ");
  Serial.println(Output2);
  delay(1000);
*/
}

Detalhe final: acredito que não seja relevante mas posso estar enganado. Eu estou usando um Macbook para o projeto, como os softwares são para windows, criei uma máquina virtual para usa-los, uso o VMware

Agradeço desde já