“Rex”, um robô que nunca perde a linha!

20 20UTC abril 20UTC 2016 — 2 Comentários

Introdução:

Esta é a primeira parte de um projeto mais complexo, explorando a potencialidade de um robô seguidor de linha. Construiremos um robô com controle PID e também aprenderemos como usar um dispositivo Android na configuração mais dos principais parâmetros (ganhos) de sua malha de controle. Na segunda parte deste projeto Robô explorador de labirintos, utilizando Inteligência Artificial com Arduino, aplicaremos conceitos de inteligência artificial para explorar labirintos,  encontrando o caminho da saída mais curto e rápido.

Abaixo um vídeo que mostra o robô seguindo um circuito básico:

Lista de Materiais:

A lista de materiais necessários é simples e o preço final do robô, muito barato (cerca de US$ 75,00):

  • Corpo (pode ser adaptado para as suas necessidades):
    • 2 quadrados de madeira (80x80mm)
    • 3 Grampos de papel
    • 2 rodas de madeira (diâmetro: 50 mm)
    • 1 roda “solta” (Caster)
    • 9 Elásticos
    • Fita 3M “Command”
    • Articulações plásticas para fixação do sensor
  • Protoboard e fiação
  • 2 conjuntos de baterias (4XNi-metal hidreto) –  5V cada conjunto
  • 2 Servos de Rotação Contínua (SM-S4303R)
  • Arduino Nano
  • Módulo Bluetooth  HC-06
  • 5 sensores x Linha (TCRT5000 4CH Infrared Linha Pista Seguidor Módulo Sensor + 1 sensor de Pista independente)
  • 1 LED
  • 1 Botão

Ajuste dos motores

Como motores, utilizei 2 servos em modo contínuo (SM-S4303R). Eles devem ser “colados”, formando um único e sólido bloco como você pode ver na foto (use a fita 3M Command, cola ou fita dupla face). Esses servos girarão a uma dada velocidade dependendo da largura do pulso que receba em sua entrada de dados. Para este servo específico, a largura do pulso varia de 1.0 ms a 2.0ms (outros tipos de servos podem trabalhar com larguras de pulso diferentes).

Arduino3

Olhando em detalhes:

  • Um pulso de 1.5ms comandará o servo para que fique em sua posição neutra, ou “parado”.
  • Um pulso de 1.0 ms comandará o servo para que funcione em sua velocidade máxima (cerca de 70 RPM) em uma direção e 2.0ms na direção oposta.
  • Pulsos entre 1.0 e 1.5ms ou 1.5ms e 2.0ms, farão os servos girarem em velocidades intermediárias e proporcionais.

Arduino4

 

A primeira coisa que se deve fazer, é enviar um pulso de 1.5ms (1500us) para verificar se os motores ficam realmente”parados”. Caso isto não ocorra, os servosdevem ser ajustados (procure o parafuso amarelo, na lateral do servo). É claro que se o seu servo não possui esse ajuste, tente alterar o valor “1500ms” até obter o ponto final.

Monte o Arduino e os servos como mostrado abaixo:

Motorflb5wsqin3edek6-large

O código abaixo, para o Arduino deve ser usado para o ajuste:

#include Servo // este código linha deve ser mudado, por favor, olhe o código anexado
Servo leftServo;
Servo rightServo;
void setup ()
{
   leftServo.attach (5);
   rightServo.attach (3);
   leftServo.writeMicroseconds (1500);
   rightServo.writeMicroseconds (1500);
}

void loop ()
{
}

Montagem do Corpo e motores para teste de movimento

fk1e3mein3ef60l-medium

 

1. Com a fita 3M Command, fixar os 2 Servos à uma das peças de madeira quadrado.

 

 

fd75kabin3ef63f-small2. Fixar o segundo quadrado de madeira ao anterior, utilizando os grampos de papel. Ajuste o comprimento da plataforma para suas necessidades.

fb16lqlin3ef65n-small

 

 

3. Fixe o caster utilizando-se o grampo de papel.

 

ftmc0s8in3ee81d-medium4. A alimentação para os motores será fornecida por um dos conjuntos de baterias (5V). Este conjunto de baterias será instalado entre o protoboard e a plataforma do corpo.

 

 

fog5h6ein3ee14w-small
5. Conecte a bateria a ser utilizada com os servos: deixe uma linha de alimentação lateral do protoboard exclusivamente para os servos

6. Conecte o Arduino Nano ao protoboard

7. Conecte os terras do Protoboard ao GND do Arduino.

8. Conecte os Servos ao Arduino: LEFT => Pin 5;   RIGHT => Pin 3

9. Ligue o LED ao Arduino Pino 13

10. Ligue o botão para Arduino Pin 9

 

Note que, devido a maneira com que os servos estão montados (em oposição) as faixa de variação de velocidade em função da largura de pulso são diferentes:

  • RIGHT Servo: velocidade de avanço frontal vai de 1500us (parado) a 2000us (velocidade máxima)
  • LEFT Servo: velocidade de avanço frontal vai de 1500us (parado) a 1000us (velocidade máxima)

Um LED externo é adicionar ao pin13, com finalidade de sinalização e testes (você pode usar o LED interno do Arduino se quiser, mas leve em consideração que será difícil de vê-lo no meio dos cabos).

Além disso, um botão é ligado ao pino 9. Este botão será muito útil para testes e para o comando de início, quando o robô estiver posicionado no início do circuito de linha.
Por exemplo:

while (digitalRead (buttonPin)) {}
    motorTurn (LEFT, 500);
    motorTurn (RIGHT, 500);

Note que as 2 linhas que irão comandar o robô para virar à esquerda, esperar 500ms e virar à direita, só serão executadas depois que você pressionar o botão (buttonPin = 0). Antes disso, o programa ficará executando um “loop infinito”.

O código Motor_Test.ino, disponível no final deste tutorial, poderá ser usado como uma base para um teste de motores mais completo (para frente, para trás, parada completa, virar à esquerda, virar à direita). Se necessário você deverá ajustar os atrasos (“delays”) para obter um ângulo de virada correto.. Também, dependendo de seus motores, os valores definidos para a largura de pulso devem ser um pouco diferentes para compensar qualquer falta de alinhamento entre os mesmos.

Instalando o módulo Bluetooth (opcional)

O módulo Bluetooth HC-06 deverá ser instalado no protoboard. A biblioteca SoftSerial do Arduino será utilizada.

Motor&BTf4fe9iqin3eedct-medium

Abaixo as conexões de pino HC-06:

  • Tx Pin para Arduino pino 10 (Rx)
  • RX Pin para Arduino pino 11 (Tx)
  • VCC / GND para Arduino 5V / GND

 

O robô funcionará com ou sem o módulo Bluetooth. O código será construído de uma maneira que caso você não ative o BT, os parâmetros utilizados serão os padrões. Na última parte deste tutorial, vou explorar como usar um aplicativo Android para enviar dados para um melhor ajuste dos parâmetros do robô e mover-lo em modo manual. O uso da App Android é um opcional no caso que alguém deseje explorar mais o uso de um Robô seguidor de linha em competições, por exemplo.

Adicionando os sensores de linha


fqq0sfhin3ees26-large

1. Fixe os 5 sensores em uma barra de plástico, como mostrado nas fotos

 

 

fw9zenoin3eeqyx-large

2. É aconselhável para rotular os sensores para fins de teste. O nome sensores vai de de “0” (mais à esquerda) para “4” (mais à direita)

 

f9d55psin3eer0w-large

3. Passe os cabos sob o quadro, usando os elásticos para corrigi-los (tome cuidado para não misturar-se com as rodas ou Caster.

 

f5c3vfgin3eersy-large

4. Conecte os cabos de pinos do Arduino, como abaixo:

  • Sensor 0 = 12
  • Sensor 1 = 18
  • Sensor 2 = 17
  • Sensor 3 = 16
  • Sensor 4 = 19

fmhzshlin3ecx2e-large

 

5. Fixe o segundo conjunto de baterias 5V e conecte ao pino Vin do Arduino.

Line Follow full circuit

 

 

 

Em meu caso, estou usando um módulo com 4 sensores integrados + 1 extra. Todos são compatíveis. Para simplificar, no diagrama abaixo, I inclui 5 sensores individuais conectados juntos. O resultado final é o mesmo em ambas as configurações.

 

Implementando a Lógica dos Sensores de linha

IRsensorCada sensor é constituído por um LED e um fotodiodo, ambos infravermelhos. A luz emitida pelo LED atinge a superfície e é reflectida de volta para o fotodiodo. O fotodiodo em seguida, gera uma tensão de saída proporcional à reflectância da superfície.

Track SensorsNo caso dos sensores utilizados, um circuito integrado no módulo gera um sinal de saída digital simples (ALTO: escuro; BAIXO: Luz). Um potenciômetro instalado no módulo (ver foto) irá ajustar o nível correto de luz para ser considerada “dark” ou não. Quando a luz reflectida é conLine Follower Sensorsiderada “escura”, a saída fica ALTA ( “1”) e BAIXA ( “0”) para outra cor mais clara. Eu usei aqui um módulo integrado com 4 sensores e e módulo extra com um sensor único (forma diferente, mas mesma lógica). A combinação é uma matriz de 5 sensores que eu considero apropriado para este tipo de controle, como explicado abaixo.

Sensor1_Cover

A matriz de 5 sensores  é montada de forma que, se apenas um sensor está centrado em relação à linha preta, ele irá produzir um “1”.

 

Sensor1_&2_Cover

Por outro lado, o espaço entre os sensores devem ser calculados para permitir que 2 sensores possam cobrir a largura total da linha preta simultaneamente,também produzindo assim um “1” em ambos os sensores (ver os quadros aqui).
As possíveis combinações de saída do conjunto de sensores são:

  • 0 0 0 0 1
  • 0 0 0 1 1
  • 0 0 0 1 0
  • 0 0 1 1 0
  • 0 0 1 0 0
  • 0 1 1 0 0
  • 0 1 0 0 0
  • 1 1 0 0 0
  • 1 0 0 0 0

Trabalhar com 5 sensores, permite a geração de uma “variável de erro” que ajudará a controlar a posição do robô sobre a linha, como mostrado abaixo:
Vamos considerar que a condição ideal é quando o robô está centrado, tendo a linha apenas abaixo do “sensor do meio” (Sensor 2). A saída da matriz será: 0 0 1 0 0  e nesta situação, o “erro” será “zero”.

Se o robô começa a derivar para a esquerda (a linha “parece mover-se” para a direita) o erro deverá aumentar e com um sinal positivo. Se o robô começar a mover-se para a direita (a linha” parece mover-se “para a esquerda), da mesma maneira, o erro tem de aumentar, mas agora com um sinal negativo.
A variável de erro, relacionada com o estado do sensor será:

  • 0 0 0 0 1 ==> erro = 4
  • 0 0 0 1 1 ==> erro = 3
  • 0 0 0 1 0 ==> erro = 2
  • 0 0 1 1 0 ==> erro = 1
  • 0 0 1 0 0 ==> erro = 0
  • 0 1 1 0 0 ==> erro = -1
  • 0 1 0 0 0 ==> erro = -2
  • 1 1 0 0 0 ==> erro = -3
  • 1 0 0 0 0 ==> erro = -4

Olhando o código Arduino, cada um dos sensores será definido com um nome específico (considere que o Sensor mais à esquerda deve ser atribuído com uma etiqueta de “0”):

const int lineFollowSensor0 = 12;
const int lineFollowSensor1 = 18;
const int lineFollowSensor2 = 17;
const int lineFollowSensor3 = 16;
const int lineFollowSensor4 = 19;

A fim de armazenar os valores de cada um dos sensores uma variável tipo Array será criada:

int LFSensor [5] = {0, 0, 0, 0, 0};

Cada posição da matriz irá ser constantemente actualizada com a saída de cada um dos sensores:

LFSensor [0] = digitalRead (lineFollowSensor0);
LFSensor [1] = digitalRead (lineFollowSensor1);
LFSensor [2] = digitalRead (lineFollowSensor2);
LFSensor [3] = digitalRead (lineFollowSensor3);
LFSensor [4] = digitalRead (lineFollowSensor4);

Uma vez armazenado o valor de cada um dos sensores, uma lógica deve ser implementada para gerar a variável de erro:

if ((LFSensor [0] == 0) && (LFSensor [1] == 0) && (LFSensor [2] == 0) && (LFSensor [3] == 0) && (LFSensor [4] == 1 )) erro = 4;
else if ((LFSensor [0] == 0) && (LFSensor [1] == 0) && (LFSensor [2] == 0) && (LFSensor [3] == 1) && (LFSensor [4] == 1)) erro = 3;
else if ((LFSensor [0] == 0) && (LFSensor [1] == 0) && (LFSensor [2] == 0) && (LFSensor [3] == 1) && (LFSensor [4] == 0)) erro = 2;
else if ((LFSensor [0] == 0) && (LFSensor [1] == 0) && (LFSensor [2] == 1) && (LFSensor [3] == 1) && (LFSensor [4] == 0)) erro = 1;
else if ((LFSensor [0] == 0) && (LFSensor [1] == 0) && (LFSensor [2] == 1) && (LFSensor [3] == 0) && (LFSensor [4] == 0)) erro = 0;
else if ((LFSensor [0] == 0) && (LFSensor [1] == 1) && (LFSensor [2] == 1) && (LFSensor [3] == 0) && (LFSensor [4] == 0)) erro = - 1;
else if ((LFSensor [0] == 0) && (LFSensor [1] == 1) && (LFSensor [2] == 0) && (LFSensor [3] == 0) && (LFSensor [4] == 0)) erro = -2;
else if ((LFSensor [0] == 1) && (LFSensor [1] == 1) && (LFSensor [2] == 0) && (LFSensor [3] == 0) && (LFSensor [4] == 0)) erro = -3;
else if ((LFSensor [0] == 1) && (LFSensor [1] == 0) && (LFSensor [2] == 0) && (LFSensor [3] == 0) && (LFSensor [4] == 0)) erro = -4;

Controlando a direção (Proporcional Control – P)

Perfeito! Neste ponto, o robô está montado e operacional. Você deve aproveitar e executar alguns testes básicos com os motores, ler a saída de sensores e testá-los através de uma linha. O que está faltando ainda, é o verdadeiro “cérebro”, ou seja fazer o Robô executar a tarefa de “andar na linha” por seus próprios meios. Vamos conseguir isso, através da implementação de uma lógica de controle que garantirá ao robô seguir uma linha, seja qual for o desenho.

PD Control
Suponha que o robô esteja “andando” sobre uma linha e a saída do Array de Sensores é: “0 0 1 0 0”.  Neste caso, o erro correspondente é “0” e ambos os motores estarão empurrando o robô para a frente com uma velocidade constante. Um código para essa condição seria:

rightServo.writeMicroseconds (1500 + iniMotorPower);
leftServo.writeMicroseconds (1500 - iniMotorPower);

Por exemplo, com iniMotorSpeed = 250,  significa que LEFT Servo receberá pulsos de 1,1250us e o RIGHT Servo 1750us, o que fará com que o robô avance com metade de sua velocidade máxima. Lembre-se que a velocidade de avanço do Servo direito irá variar com larguras de pulso que variam de 1500us (parado) a 2000us (velocidade máxima) e o Servo esquerdo de 1500us (parado) a 1000us (velocidade máxima).

Suponha agora que o robô comece a derivar para a esquerda ( a “linha vai para a direita”), fazendo com que o sensor 3 també, fique sobre a linha. Neste caso, a saída do Array de sensores será: “0 0 1 1 0” e o erro = 1. Nesta situação, o que necessitamos é virar o robô para a direita. Para fazer isso, devemos diminuir a velocidade do Servo direito o que significa diminuir a largura do pulso. Além disso, a velocidade do Servo esquerdo deve aumentar, o que significa diminuir a largura do pulso. Para isso, precisamos alterar a função de controle do motor:

rightServo.writeMicroseconds (1500 + iniMotorPower - erro);  // Erro positivo: velocidade diminue
leftServo.writeMicroseconds (1500 - iniMotorPower - erro);  //Erro positivo: velocidade aumenta

A lógica acima é correcta, mas é facil compreender que a adição ou subtracção de apenas “1” microssegundo na duração dos pulsos não irá gerar a correcção necessária, ao menos em um tempo realista. É intuitivo que o número a se adicionar ou subtrair deve ser maior, por exemplo, 50, 100, etc. Para conseguir isso, o “erro” deve ser multiplicado por uma constante “K”. Uma vez que a influência dessa constante será proporcional ao erro, vamos chamar-la “Constante proporcional: Kp. No meu caso, testei o robô com uma constante Kp=50 e ele funcionou muito bem.

A função final para os motores será:

int Kp = 50;
rightServo.writeMicroseconds (1500 + iniMotorPower - Kp * erro);
leftServo.writeMicroseconds (1500 - iniMotorPower - Kp * erro);

Podemos resumir o que acontecerá com os motores, como mostrado abaixo:

  • Array de Sensores: 0 0 1 0 0
    • erro = 0
    • Servo direito: pulso = 1750us
    • Servo esquerdo: pulso  = 1250us
    • (ambos os motores a mesma velocidade)
  • Array de Sensores: 0 0 1 1 0
    • erro = 1
    • Servo direito: pulso = 1700us (mais lento)
    • Servo esquerdo: pulso = 1200us (mais rápido)

Se a situação é o oposto e o robô vai para a direita, o erro seria “negativo” e a velocidade dos servos deve mudar:

  • Array de Sensores: 0 0 1 0 0
    • erro = 0
    • Servo direito: pulso = 1750us
    • Servo esquerdo: pulso = 1250us
    • (ambos os motores a mesma velocidade)
  • Array de Sensores: 0 1 1 0 0
    • erro = -1
    • Servo direito: pulso = 1800us (mais rápido)
    • Servo esquerdo: pulso = 1300us (mais lento)

Neste ponto fica claro que quanto mais o robô “escorregue” para um lado, maior será o erro (1, 2, 3 ou 4) e mais rápido ele deve retornar ao centro (valores a serem adicionados a largura do pulso: 50, 100, 150, 200). A velocidade com que o robô irá reagir ao erro será proporcional ao mesmo.

Controle PID

Controle PID (Proporcional, Derivativo e Integral) é um dos sistemas de controle mais comuns que existem. A maioria dos sistemas de controle industriais utilizam algum tipo do controle PID. Há muitas maneiras de ajustar uma malha PID, incluindo a técnica “tentativa e erro”, que é a que usaremos nesse projeto.

Pense no PID como uma mola simples. Uma mola tem um comprimento original (setpoint), que quando perturbada, pela expansão ou contração, tende a recuperar o seu comprimento original no menor tempo possível. Controladores PID são utilizados onde quer que haja a necessidade de controlar uma quantidade física e a torná-la igual a um valor pré-especificado. Por exemplo, controlador de velocidade em carros, robôs, reguladores de temperatura, reguladores de tensão, etc.

Como o PID funciona?
O sistema calcula o “ePIDrro” ou “desvio” da quantidade física em relação ao set point, medindo o valor atual dessa quantidade física usando um sensor. Para voltar ao set point, este “erro” deve ser minimizado, idealmente igual a zero. Além disso, esse processo deve ocorrer o mais rapidamente possível (também idealmente esse tempo deveria ser zero).
Mais informações, acesse:  http://en.wikipedia.org/wiki/PID_controller

Implementando PID

  1. erro (e):
    Este valor é igual à diferença entre o ponto de regulação e o valor actual da quantidade a ser controlada.
    error = set_point – CURRENT_VALUE (no nosso caso é a variável de erro começa a partir da posição do robô sobre a linha
  2. Termo Proporcional (P):
    Este termo é proporcional ao erro.
    P = error
    Este valor é responsável pela magnitude da mudança necessária na quantidade física para atingir o ponto de ajuste. O termo proporcional é o que determina o tempo de subida da malha de controle ou o quão rápido ele vai chegar ao set point.
  3. Termo Integral (I):
    Este termo é a soma de todos os valores de erro anterior.
    I = I + error
    Este valor é o responsável pela rapidez de resposta do sistema para a mudança do ponto de ajuste. O termo integral é utilizado para eliminar o erro de estado estacionário exigido pelo termo proporcional. Normalmente, pequenos robôs não usam o termo integral porque não estamos preocupados com erro de estado estacionário e isso pode complicar o ajuste.
  4. Diferencial ou termo derivativo (D):
    Este termo é a diferença entre o erro instantâneo do ponto de ajuste, e o erro a partir do instante anterior.
    D = error – previousError
    Este valor é responsável para diminuir (“slow down”) a taxa de variação da quantidade física quando nos aproximamos do ponto de ajuste. O termo derivativo é utilizado a reduzir o “overshoot” ou o quanto o sistema “super corrige”.

Equação:

PIDvalue = (Kp * P) + (Ki * I) + (Kd * D)

Onde:

  • Kp é a constante usada para fazer variar a magnitude da mudança necessária para atingir o ponto de ajuste.
  • Ki é a constante utilizada para variar a velocidade com que a mudança deve ser apresentado na quantidade física para atingir o ponto de ajuste.
  • Kd é a constante utilizada para variar a estabilidade do sistema.

Uma abordagem para a obtenção das constantes é deixar Ki de fora  e trabalhar com o Controle PD (manter Ki = 0);  definir a variável Kd como 0 e sintonizar o termo Kp sozinho pela primeira vez. Kp de 25 é um bom lugar para começar no nosso caso aqui e ir subindo seu valor. Na última etapa foi utilizado um Kp de 50 que funciona muito bem com meu robô. Se o robô reagir muito lentamente, voce deve aumentar o valor. Se o robô parece reagir muito rapidamente tornando-se instável, diminua o valor. Uma vez que o robô responda razoavelmente, sintonize a parte derivativa da malha de controle. Primeiro defina o valor Kp e Kd cada um para a 1/2 do valor Kp. Por exemplo, se a resposta do robô é razoável com um Kp = 50, definir Kp = 25 e Kd = 25 para iniciar. Aumente o ganho de Kd para diminuir o “overshoot ou o diminua se o robô se tornar instável.

Um outro componente do loop a ser considerado é a taxa real de amostra / loop. Acelerar-la ou para retardar-la pode fazer uma diferença significativa no desempenho do robô. Isso é definido pelas declarações de “Delay” que você tem em seu código. Use o método tentativa-erro para obter o melhor resultado.

Com base na abordagem anterior, implementamos a função abaixo:

void calculatePID () 
{
   P = error;
   I = I + error;
   D = error - previousError;
   PIDvalue = (Kp * P) + (Ki * I) + (Kd * D);
   previousError = error;
}

A constante Kp simples usada na última etapa será substituída agora por PIDvalue, mais completa:

void motorPIDcontrol () 
{
   int leftMotorSpeed = 1500 - iniMotorPower - PIDvalue; 
   int rightMotorSpeed = 1500 + iniMotorPower - PIDvalue;
   leftServo.writeMicroseconds (leftMotorSpeed);
   rightServo.writeMicroseconds (rightMotorSpeed);
}

Mas note que se você tem Kd e Ki = 0, PIDvalue = Kp * error, exatamente como na etapa anterior onde usamos só o controle Proporcional.

Adicionando condições especiais ao código final

 

FullSizeRender 13

Neste ponto do projeto, o robô já pode seguir sem parar um circuito de linha do tipo “loop constante”.

A função Loop do programa de ciclo seria simplesmente:

void loop ()
{
   readLFSsensors (); // Ler sensores, armazenar os valores no Array de sensores e calcular o "erro"
   calculatePID (); 
   motorPIDcontrol ();
}

Mas, para uma operação mais completa e real, é importante acrescentar, pelo menos um par de “comandos básicos de linha”.

Por exemplo, vamos introduzir uma nova variável: "mode", definindo 3 estados para esta variável:

#define STOPPED 0
#define FOLLOWING_LINE 1
#define NO_LINE 2

Se todos os sensores de encontrar uma linha preta, a saída do Array de Sensor seria: 1 1 1 1 1. Nesta condição, podemos definir o modo como “PARADO” e o robô deve realizar um “Full Stop”.

if ((LFSensor [0] == 1) && (LFSensor [1] == 1) && (LFSensor [2] == 1) && (LFSensor [3] == 1) && (LFSensor [4] == 1 )) {mode = STOPPED;}

Outra situação comum com robôs seguidores de linha, é quando o mesmo não encontra “nenhuma linha”, ou a saída do Array de Sensores é: 0 0 0 0 0. Neste caso, podemos programá-lo para girar 180 graus (ou girar em ângulos pequenos até que uma linha é encontrada e a condição normal FOLLOWING_LINE é retomada.

else if ((LFSensor [0] == 0) && (LFSensor [1] == 0) && (LFSensor [2] == 0) && (LFSensor [3] == 0) && (LFSensor [4] == 0)) {mode = NO_LINE;)

A função loop completa seria:

void loop () 
{
   readLFSsensors ();
   switch (mode)
   {
     case STOPPED:
        motorStop();
        break;
     case NO_LINE:
        motorStop ();
        motorTurn (LEFT, 180);
        break;
     case FOLLOWING_LINE:
        calculatePID ();
        motorPIDcontrol ();
        break;
   }
}

O código final incluirá integrar lógica adicional e também algumas variáveis devem ser inicializadas, etc. Durante as explicações , deixei estes detalhes de fora para simplificar a explicação, mas acredito que tudo fique claro dando uma olhada no código final.

Usando o App Android para ajustar os ganhos do controlador PID

No código do Arduino, você poderá encontrar no arquivo “robotDefines.h” as seguintes definições para as constantes “default” serem usadas com o controle PID.

float Kp = 50;
float Ki = 0;
float Kd = 0;

Como explicado anteriormente,  a melhor maneira de definir os ganhos corretos (Constantes Kd, Ki e Kd) é usar a metodologia “Tentativa-e-erro”. O lado ruim disso é que você deve re-compilar o programa a cada vez que você defina uma constante nova. Uma maneira de acelerar o processo é usar o App Android para enviar as constantes na fase de setup do programa.

Picture1

Eu desenvolvi um aplicativo Android exclusivamente para isso:

o MJRoBot Line Follower PID Control

Em resumo o aplicativo possui:

fkhjj4uin699t7j-medium

  • Comandos manuais tradicionais:
    • FW, BW, esquerda, direita e parar onde o aplicativo irá enviar ao módulo BT HC-o6, respectivamente:  ‘f’, ‘b’, ‘l’, ‘r’ e ‘s’.

 

fpjpy8zin699t7l-medium

  •  3sliders , um para cada constantes PID:
    • Kp: “p / XXX”
    • Ki: “i / XXX”
    • Kd: “d / XXX”
      • Em que “XXX” é um número de 0 a 100.

 

 

  • Um botão extra (“Play”) que funciona exatamente como o botão conectado ao pino 9 do Arduino. Você poderá utilizar um ou o outro, não importa.

No final deste tutorial, você encontrará o arquivo .aia que pode ser modificado no MIT AppInventor e o arquivo .apk para ser instalado diretamente no seu dispositivo Android.

  • MJRoBot_Line_Follower_PID_Control.aia
  • MJRoBot_Line_Follower_PID_Control.apk

Alterar o código para o ajuste remoto dos ganhos do PID

Durante a fase de setup do programa, introduziremos um “loop” onde você poderá enviar os parâmetros PID para o robô antes de colocá-lo sobre a linha:

while (digitalRead (buttonPin) && !mode) 
{
   checkBTcmd (); // Verificar se um comando é recebido via controle remoto BT
   manualCmd (); // Executar o comando
   command = "";
}
checkPIDvalues (); // Enviar as constantes PID para o dispositivo Android (apenas para verificação)
mode = STOPPED;

A função de comando manual, será:

void manualCmd () 
{
   switch (command [0])
   {
      case 'g':
        mode = FOLLOWING_LINE;
        break;
      case 's':
        motorStop (); // Desligar ambos motores
        break;
      case 'f':
        motorForward ();
        break;
      case 'r':
        motorTurn (RIGHT, 30);
        motorStop ();
        break;
      case 'l':
        motorTurn (LEFT, 30);
        motorStop ();
        break;
      case 'b':
        motorBackward ();
        break;
      case 'p':
        Kp = command [2];
        break;
      case 'i':
        Ki = command [2];
        break;
      case 'd':
        Kd = command [2];
        break;
    }
}

No vídeo, você poderá ver alguns testes usando o App Android:

Video Robô seguidor de linha

Abaixo o código final para o robô:

Arduino & Android codes

Conclusão

Este é o primeiro tutorial de um projeto mais complexo, explorando a potencialidade de um robô seguidor de linha. No próximo tutorial, desenvolverei um robô com habilidade para resolver um labirinto, ou seja não só encontrar o caminho de saída, mas também qual seria o caminho mais curto e rápido.

Espero que esse trabalho possa contribuir para que outras pessoas possam aprender mais sobre eletrônica, robôs, Arduino, etc.

Um abraço e até o próximo post!
Obrigado

Anúncios

2 Respostas para “Rex”, um robô que nunca perde a linha!

  1. 

    Muito bom Marcelão… não é que ele deu a volta pela “esquerda”!!!

    Curtido por 1 pessoa

Trackbacks e Pingbacks:

  1. Robô explorador de labirintos, utilizando Inteligência Artificial com Arduino – MJRoBot.org - abril 28, 2016

    […] projeto foi desenvolvido a partir de meu último tutorial: “Rex”, um robô que nunca perde a linha! Depois de se conseguir desenvolver um robô com capacidade de seguir linhas, o próximo passo […]

    Curtir

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s