Estendendo o poder de controle do “servo motor de passo” + arduino = Aula 3



Agora que o temos um servo funcional, que foi produzido nos dois primeiros vídeos desta série Motor de Passo, que tal aumentar o arsenal de controle introduzindo um controle via serial do arduino? Continuando a série, vamos implementar funções de comando para o servo que permitirão não só posicioná-lo, mas também criar ajustes e até mesmo medir sua posição verdadeira.


Quer ver os dois primeiros vídeos da série? Acesse: Parte 1 e Parte 2.


Características principais

Permite comandos através da comunicação serial.
Flexibilidade nas configurações do software, permitindo formas de controle variadas.
Flexibilidade na montagem de hardware, permitindo variações de motores, drivers e sensores.
Retorno da informação de posição real através da leitura do sensor.

Montagem

Continuaremos utilizando a mesma montagem anterior. Mas, deixaremos somente o potenciômetro de leitura do eixo.


O potenciômetro continuará funcionando como um sensor da posição atual do eixo. Para isso vamos prender o eixo do motor ao manípulo do potenciômetro.


Conectaremos o potenciômetro a entrada analógica A0.


    ·         O EIXO conectaremos ao pino A0 (fio roxo).
    ·         A alimentação de 5V (fio verde).
    ·         A referência GND (fio preto).




ATENÇÃO!!!

Antes de prender o potenciômetro sensor ao eixo, teste a montagem para verificar se a rotação está ocorrendo no sentido correto, ou seja, ao comandar um aumento de posição o motor deve girar no sentido de aumentar o potenciômetro sensor.
Se a rotação estiver ocorrendo ao contrário, simplesmente inverta a polarização do potenciômetro.
Como o torque do motor de passo costuma ser alto, ele pode danificar o potenciômetro sensor tentando levá-lo para uma posição que não pode ser alcançada.


Montagem do circuito


Entendendo o programa (Declarações)

Código – Fonte do Arduino

Declarações Globais:

(constantes)

Começamos definindo constantes que representarão os pinos D2, D3, D4 e D5 do Arduino. Estes pinos serão os responsáveis pela transmissão da sequencia de acionamento para o driver.
A constante EIXO refere-se ao pino A0 utilizado pelo potenciômetro sensor.



// Declaração Global de constantes que representarão os pinos do Arduino

const byte INPUT_1 = 2; //Pinos de controle do L298N
const byte INPUT_2 = 3;
const byte INPUT_3 = 4;
const byte INPUT_4 = 5;
const byte EIXO = A7; // Potenciômetro do EIXO.


Declarações Globais:

(Variáveis)
Agora veremos que temos novas variáveis para o controle do Servo Motor de Passo.
inc_dec – Armazena o incremento/decremento da posição.
retardo_entre_os_passos – define o retardo para mudança de passo.
tolerancia – define a precisão do ajuste da posição.
leitura_comando – armazena o valor da posição alvo recebida pelo comando.
leitura_eixo – armazena o valor da posição atual do eixo.
passo – armazena o passo atual da sequencia de passos.
limite_inferior – armazena o valor usado para definir a posição mínima de ajuste.
limite_superior – armazena o valor usado para definir a posição máxima de ajuste.
comando_recebido – armazena o comando recebido pela serial.
recepcao_completa – sinaliza se a recepção do dado foi completada.

//Variáveis globais

int inc_dec = 1; //Valor usado como incremento e decremento da posição (Comandos + e -)
int retardo_entre_os_passos = 3; //constante que determinará a retardo entre os passos do ajuste do motor
int tolerancia = 1; //constante que determinará uma tolerância mínima para o ajuste
int leitura_comando = 510; // Variável que armazena o valor da leitura do COMANDO.
int leitura_eixo = 0; // Variável que armazena o valor da leitura do SENSOR DO EIXO.
int passo = 0; //Variável que armazena o passo atual.
int limite_inferior = 420; //limite inferior para o posicionamento
int limite_superior = 620; //limite superior para o posicionamento
String comando_recebido = ""; //String que armazena os dados recebido
boolean recepcao_completa = false; //sinalizador de dados aguardando leitura

Entendendo o programa (Setup)


Setup()

Na função setup(), ajustamos os pinos digitais como saídas, o pino analógico como entrada. Então iniciamos a função Serial. Para a recepção reservamos 200 bytes para o comando recebido (mais que suficiente).
Lemos a posição inicial do eixo e executamos uma função que envia pela serial uma mensagem de configurações atuais e lista de comandos para o usuário.

void setup() {
  // Ajustando os pinos do Arduino para operarem como saídas digitais
  pinMode(INPUT_1, OUTPUT);
  pinMode(INPUT_2, OUTPUT);
  pinMode(INPUT_3, OUTPUT);
  pinMode(INPUT_4, OUTPUT);

  // Ajustando os pinos do Arduino resposáveis pela leitura do potenciômetro
  pinMode(EIXO, INPUT);

  //Inicia a comunicação serial
  Serial.begin(9600);
  // reserva 200 bytes de buffer para dados recebidos
  comando_recebido.reserve(200);
  // Determina sua posição inicial
  leitura_eixo = analogRead(EIXO);
  //envia uma mensagem de inicio pela serial
  mensagem();
}

Entendendo o programa (mensagem)


A função mensagem é uma série de “prints”, enviados pela serial para tornar mais amigável a manipulação do programa. Servindo de orientação.
Ela ainda faz uma chamada à função interpretadora de comando solicitando uma leitura dos valores das variáveis. Veremos essa função mais a frente.

Observação:
A função mensagem é opcional e pode ser removida do programa, uma vez que o usuário já esteja acostumado com a lista de comandos.

void mensagem()
{
  /*
  Envia pela serial uma mensagem inicial com algumas
  informações como ajustes do servo e comandos disponíveis.
  Esta função é opcional. Pode ser retirada do programa sem 
  consequencias
  */
  
  Serial.println("Servo com Motor de Passo e Controle Serial\n");
  Serial.println("Ajustes atuais:");
  interpretador("L");
  Serial.println("");
  Serial.println("Lista de comandos\n");
  Serial.println("L - Ler as configuracoes atuais");
  Serial.println("A# - Ajusta a posicao para #");
  Serial.println("M# - Ajusta para a posicao MEDIA ");
  Serial.println("R# - Ajusta a rapidez com que os passos sao dados");
  Serial.println("S# - Ajusta o valor do limite superior");
  Serial.println("I# - Ajusta o valor do limite inferior");
  Serial.println("T# - Ajusta o valor de desvio toleravel");
  Serial.println("+# - Incrementa o passo em  #");
  Serial.println("-# - Decrementa o passo em  #");
}


Exemplo da mensagem enviada - Informações e Comandos


Entendendo o programa (Loop)


Loop():

Na função loop(), lemos novamente a posição atual do eixo e em seguida verificamos se existe alguma recepção completa de dados, usando a variável sinalizadora recepcao_completa. Se esta variável for verdadeira, passamos o controle do programa para a função interpretadora do comando. Se não houver repetimos o mesmo processo de ajuste que fizemos antes utilizando o valor contido na variável leitura_comando  como alvo de ajuste e tolerancia como determinante da precisão do ajuste.

void loop() {
  //leitura do potenciômetro de posição do eixo
  leitura_eixo = analogRead(EIXO);

  // verifica se há dados disponíveis
  if (recepcao_completa == true)
  {
    //Envia o comando recebido para a função que interpretará  o comando
    interpretador(comando_recebido);
    //reinicia as variáveis de recepção
    comando_recebido = "";
    recepcao_completa = false;
  }

  // Avaliação da direção do movimento
  if (leitura_eixo < (leitura_comando - tolerancia))
  {
    girar(1); //Girar o EIXO no sentido de AUMENTAR a leitura do sensor
  }
  if (leitura_eixo > (leitura_comando + tolerancia))
  {
    girar(-1); //Girar o EIXO no sentido de REDUZIR a leitura do sensor
  }

  // Aguarde para repetir
  delay(retardo_entre_os_passos);
}


Relembrando, para o cálculo da posição alvo incluímos um valor que indica a tolerância do posicionamento. Isso nos permitirá lidar com ruídos na leitura simplesmente aumentando a faixa alvo.


Entendendo o programa (girar)

A função girar funciona da mesma forma que vimos anteriormente.


girar (int direcao)

A função girar receberá um parâmetro que indicará para qual lado o motor deverá girar.
Este parâmetro é enviado pela avaliação dos valores que ocorre no loop, como vimos a pouco.
O valor do parâmetro “direcao” determinará se o passo deve ser incrementado ou decrementado.
Criamos essa função separadamente somente para ilustrar melhor o funcionamento do programa. Poderíamos ter incluído este código diretamente na avaliação que ocorre no loop.

//Função para girar o motor na direcao avaliada
void girar(int direcao)
{
  // Girar INCREMENTANDO o PASSO
  if (direcao > 0)
  {
    passo++;
    if (passo > 3)
    {
      passo = 0;
    }
  }
  //Girar DECREMENTANDO o passo
  else {
    passo--;
    if (passo < 0 )
    {
      passo = 3;
    }
  }
  //Atualiza o passo
  ajustar_passo(passo);
}


Entendendo o programa (ajustar_passo)

A função ajustar_passo ativa as bobinas na sequência correta.



//Função para atualização do passo
void ajustar_passo (int bobina)
{
  switch (bobina) {
    //PASSO 1
    case 0:
      digitalWrite(INPUT_1, HIGH);
      digitalWrite(INPUT_2, LOW);
      digitalWrite(INPUT_3, LOW);
      digitalWrite(INPUT_4, LOW);
    break;
    ///PASSO 2
    case 1:
      digitalWrite(INPUT_1, LOW);
      digitalWrite(INPUT_2, HIGH);
      digitalWrite(INPUT_3, LOW);
      digitalWrite(INPUT_4, LOW);
    break;
    //PASSO 3
    case 2:
      digitalWrite(INPUT_1, LOW);
      digitalWrite(INPUT_2, LOW);
      digitalWrite(INPUT_3, HIGH);
      digitalWrite(INPUT_4, LOW);
    break;
    //PASSO 4
    case 3:
      digitalWrite(INPUT_1, LOW);
      digitalWrite(INPUT_2, LOW);
      digitalWrite(INPUT_3, LOW);
      digitalWrite(INPUT_4, HIGH);
    break;
  }
}


Entendendo o programa (serialEvent)


serialEvent

A função serialEvent() é uma função do Arduino que dispara automaticamente sempre que um dado é recebido pela serial.
Note que não precisamos explicitar nenhuma chamada para esta função pois, uma vez que um dado é recebido, o fluxo do programa é interrompido e esta função é chamada para manipular os dados recebidos.
Fazemos isso verificando se há bytes no buffer de recepção serial do Arduino usando a função Serial.available(). Enquanto houver, armazenamos cada byte na variável caracter e adicionamos à variável comando_recebido até que um caractere de nova linha seja recebido, sinalizando o fim do comando ou até que se esgotem os bytes do buffer de recepção.

void serialEvent()
{
/*
  A função SerialEvent ocorre sempre que um novo byte
  chegar na entrada RX, interrompendo a execução normal
  do programa e retornando depois de executada
*/
  while (Serial.available())
  {
    // captura o novo byte recebido e armazena como caracter
    char caracter = (char)Serial.read();
    // adiciona o novo caracter a variável comando_recebido:
    comando_recebido += caracter;
    // se o novo caracter recebido é um caracter de nova linha (\n),
    //então muda o valor do sinalizador de recepção completa para que o 
    //possamos utilizar o comando recebido
    if (caracter == '\n')
    {
      recepcao_completa = true;
    }
  }
}

Entendendo o programa (Interpretador de comandos)

Cascata de ‘if’...


Interpretador de comandos – Comando “L”

A função interpretadora de comandos é uma coleção de verificações da primeira letra do comando recebido. Cada letra representa um comando e os dados seguintes servirão de argumentos para este comando, isso se houver.
O primeiro comando é o comando “L” que executa uma leitura de todas as variáveis de controle e as envia para o usuários através da serial.

//Esta função é responsável pela interpretação de cada comando
void interpretador(String comando)
{
  if (comando.startsWith("L")) //LEITURA do estado atual
  {
    Serial.println("___________________________");
    Serial.print("Posicao (und.): ");
    Serial.println(leitura_eixo);
    Serial.print("Retardo entre os passos (ms): ");
    Serial.println(retardo_entre_os_passos);
    Serial.print("Tolerancia (und.): ");
    Serial.println(tolerancia);
    Serial.print("Limite Inferior (und.): ");
    Serial.println(limite_inferior);
    Serial.print("Limite Superior (und.): ");
    Serial.println(limite_superior);
    Serial.println("___________________________");
    Serial.flush();
  }


Interpretador de comandos – Comando “T”

O comando “T” lê o restante dos bytes da variável comando, após a primeira letra e usa estes valores para ajustar o valor da variável tolerancia.
Note que usamos o método .substring para cortar da segunda posição (a contagem das posições das strings começam em zero), até o comprimento total da variável comando. Para obter o comprimento usamos o método .length().
Por fim, usamos ainda o método .toInt() para converter este valor em um inteiro.

//determina o valor da TOLERÂNCIA do ajuste
  if (comando.startsWith("T"))
  {
    //obtém somente o valor do comando recebido e o converte para inteiro
    tolerancia = comando.substring(1, comando.length()).toInt();
  }

Interpretador de comandos – Comando “R”

O comando “R” é responsável pelo ajuste da variável retardo_entre_os_passos. Este retardo é importante para controlar a rapidez do movimento. Ela substituiu a variável velocidade que utilizamos antes (somente mudamos o nome).
O argumento para esse comando é capturado da mesma forma que o comando anterior.
Depois de recebido o valor, ele é verificado. O valor deste retardo não pode ser muito pequeno porque faria o motor pular passos. No nosso caso, o limite é 3 milissegundos.

 // Define o RETARDO entre cada mudança de passo 
  if (comando.startsWith("R"))
  {
    //obtém somente o valor do comando recebido e o converte para inteiro
    retardo_entre_os_passos = comando.substring(1, comando.length()).toInt();
    /*
    Verifica se este valor é menor que 3ms. Se a mudança de passos for muito rápida
    o motor pode não ser capaz de girar e pode perder passos. Se uma valor menor que 3
    for recebido, este valor será ignorado e o valor 3 será atribuido.
    */
    if(retardo_entre_os_passos < 3){retardo_entre_os_passos =3;}
  }

Interpretador de comandos – Comandos “I” e “S”

Os comando “I” e “S” servem para ajustar o valor das variáveis que controlam os limites do movimento do servo.
Sendo “I” paro limite inferior e “S” para o limite superior.
A recepção dos valores ocorre praticamente da mesma forma que os casos anteriores.

//Define o limite INFERIOR para posicionamento.  
  if (comando.startsWith("I"))
  {
    //obtém somente o valor do comando recebido e o converte para inteiro
    limite_inferior = comando.substring(1, comando.length()).toInt();
    /*
    Verifica se este valor é menor que 0. Como estamos utilizando diretamente a 
    leitura da porta analógica A0 (0 a 1023), este valor não pode ser negativo.
    */
    if(limite_inferior < 0){limite_inferior = 0;}
  }

  //Define o limite SUPERIOR para posicionamento
  if (comando.startsWith("S"))
  {
    //obtém somente o valor do comando recebido e o converte para inteiro
    limite_superior = comando.substring(1, comando.length()).toInt();
    /*
    Verifica se este valor é maior que 1023. Como estamos utilizando diretamente a 
    leitura da porta analógica A0 (0 a 1023), este valor não pode ser maior que 1023.
    */
    if(limite_superior > 1023){limite_superior = 1023;}
  }


Interpretador de comandos – Comando “M”

O comando “M” calcula o valor da posição MÉDIA entre os limites SUPERIOR e INFERIOR e atribui este valor a variável leitura_comando, tornando-se assim o alvo do ajuste.
Após o comando “M” o servo sempre se posicionará na posição intermediária, entre a posição máxima e mínima ajustadas.

//Ajusta o servo para uma posição MÉDIA entre os LIMITES
  if (comando.startsWith("M"))
  {
    //Calcula a posição MÉDIA
    leitura_comando = (limite_superior + limite_inferior) / 2;
  }


Interpretador de comandos – Comandos “+” e “-”

Os comandos “+” e “-” incrementam e decrementam, respectivamente, a posição do servo, em passos definidos pelo valor da variável inc_dec.
O valor desta variável pode ser ajustado como um argumento destes dois comandos.
Uma vez ajustada, não é necessário que se envie novamente o valor do incremento ou decremento, a menos que o intuito seja realmente altera-la.

//INCREMENTA a posição um ajuste além da tolerância
  if (comando.startsWith("+"))
  {
    //obtém somente o valor do comando recebido e o converte para inteiro
    int temp = comando.substring(1, comando.length()).toInt();
    // Verifica se o valor recebido é maior que 0
    if(temp > 0){inc_dec = temp;}
    //Calcula a nova posição
    leitura_comando = leitura_eixo + inc_dec;
    /*
    Verifica se a posição solicitada está dentro do limite SUPERIOR.
    Se for superado, o valor será ignorado e o valor limite será 
    tomado como novo valor.
    */
    if (leitura_comando > limite_superior)
    {
      leitura_comando = limite_superior;
    }
  }
  
  //DECREMENTA a posição um ajuste além da tolerância
  if (comando.startsWith("-"))
  {
    //obtém somente o valor do comando recebido e o converte para inteiro
    int temp = comando.substring(1, comando.length()).toInt();
    // Verifica se o valor recebido é maior que 0
    if(temp > 0){inc_dec = temp;}
    //Calcula a nova posição
    leitura_comando = leitura_eixo - inc_dec;
    /*
    Verifica se a posição solicitada está dentro do limite INFERIOR.
    Se for superado, o valor será ignorado e o valor limite será 
    tomado como novo valor.
    */
    if (leitura_comando < limite_inferior)
    {
      leitura_comando = limite_inferior;
    }
  }

Interpretador de comandos – Comando “A”

O comando “A” é responsável pelo o AJUSTE da posição do servo. A nova posição é o argumento deste comando. Se o argumento for omitido, será considerado zero e o servo moverá para a posição mínima.

//AJUSTA para a posição contida no comando
  if (comando.startsWith("A"))
  {
    
    //obtém somente o valor do comando recebido e o converte para inteiro
    leitura_comando = comando.substring(1, comando.length()).toInt();
    /*
    Verifica se a posição solicitada está entre os limites SUPERIOR e INFERIOR.
    Se algum deles for superado, o valor será ignorado e o valor limite será 
    tomado como novo valor.
    */
    if (leitura_comando > limite_superior) {
      leitura_comando = limite_superior;
    }
    if (leitura_comando < limite_inferior) {
      leitura_comando = limite_inferior;
    }
  }
}


Novo suporte para NEMA 17




Arquivo STL para impressora 3D



Arquivos para download:


Nenhum comentário:

Tecnologia do Blogger.