Imagem-BANNER-DESTAQUE-BLOG-DESKTOP- Robótica Construa um robôque desvia de obstaculos com arduino

Construa seu robô que desvia de obstáculos com Arduino 2

14Robôs chamam muito a atenção, afinal são um dos poucos “adereços tecnológicos” que parecem ganhar vida quando em operação. E se o robô fosse capaz de desviar sozinho de obstáculos? Legal, né? É justamente o que você aprenderá a fazer aqui neste post!

Você aprenderá a fazer seu próprio robô autônomo, capaz de desviar sozinho de obstáculos e sair de labirintos.

Material necessário

Para fazer este projeto, você precisará de:

Visão geral do robô que desvia de obstáculos

Este projeto consiste de um robô de duas rodas capaz de, sozinho, desviar de obstáculos localizados a sua frente. Ou seja, o robô move-se e desvia de obstáculos por conta própria.

Os obstáculos são detectados a partir de um sensor de distância ultrassônico, capaz de informar a distância entre o robô e obstáculos à frente do mesmo, detectando quaisquer objetos em um range de 2 centímetros até 4 metros de distância do robô.

Desta forma, quando o robô está muito próximo de um obstáculo (neste projeto, muito próximo significa estar a 10 centímetros de distância do robô), este gira para esquerda ou direita até encontrar um caminho livre. Um caminho livre é um caminho sem obstáculos ou com obstáculos localizados a mais de 10 centímetros de distância. Uma vez encontrado um caminho livre, o robô segue em frente.

Para que lado girar quando localizar um obstáculo?

Em caso de o robô encontrar um obstáculo à frente, localizado a 10 ou menos centímetros de distância, este vai girar buscando um caminho livre, conforme já descrito neste post. Porém, como o robô pode decidir para que sentido deve girar (horário ou anti-horário)?

Neste caso, há duas estratégias possíveis:

  1. Girar sempre em um só sentido (horário ou anti-horário): esta estratégia é a mais simples e intuitiva possível.
    Porém, se o robô se encontra em um labirinto, por exemplo, ele pode demorar mais para desviar dos obstáculos, uma vez que achar o caminho livre girando somente para um dos sentidos (horário ou anti-horário) pode fazê-lo girar bastante até encontrá-lo.
    Ainda, dependendo da situação em que se encontra e dos obstáculos encontrados, nesta estratégia o robô pode achar como caminho livre o caminho de onde acabou de vir, causando assim uma espécie de “regressão”, como se o robô desse um passo atrás na solução do labirinto, por exemplo.
  2. Girar em sentido horário ou anti-horário, de forma alternada: aqui, a cada obstáculo encontrado, o robô alterna o sentido de giro. Por exemplo: se no obstáculo anterior ele girou em sentido horário, no próximo obstáculo girará no sentido anti-horário, e vice-versa. Desta forma, a chance do robô regredir na solução de um labirinto é minimizada e a chance de perder tempo girando para o lado errado também é menor.

Considerando as estratégias acima e consequências das mesmas, o robô deste projeto seguirá a estratégia de girar em sentidos alternados (estratégia 2).

Circuito esquemático

O circuito esquemático do robô pode ser visto na figura 1.

Circuito do robô
Figura 1 – Circuito esquemático do projeto

Importante: pelo fato dos motores exigirem muita corrente elétrica para funcionarem (em comparação a corrente exigida pelo Arduino Nano apenas), não se esqueça de remover o fio que liga +5 V ao Vin do Arduino Nano antes da programação do mesmo. Assim, não há o risco de um ou ambos motores ligarem e utilizarem a alimentação da USB do computador, o que pode levar a sérios danos ao computador.
Após a programação do Arduino Nano, pode-se ligar o fio que liga +5 V ao Vin do Arduino Nano novamente.

Além disso, como estamos alimentando o módulo de ponte H L298N com os 5 V fornecidos pelo powerbank e, ainda, este projeto não possui controle de velocidade de giro dos motores, é fundamental que:

  • Os jumpers ATIVA MA e ATIVA MB estejam presentes (colocar jumpers)
  • O jumper ATIVA 5 V esteja ausente (retirar jumper)

Para referências de quais são estes jumpers e onde se localizam no módulo, veja a figura 2.

Ponte H usada para o projeto
Figura 2 – Jumpers e pinos da Ponte H L298N

Montagem do robô que desvia de obstáculos

Para te ajudar a organizar os componentes e fios, segue abaixo algumas fotos do robô deste post já montado.

Parte da frente do robô que desvia de obstáculos
Figura 3 – Foto frontal do robô
Parte superior do robô que desvia de obstáculos
Figura 4 – Foto superior do robô
Parte superior com destaque nas ligações do robô que desvia de obstáculos
Figura 5 – Foto superior com destaque para as ligações
Robô que desvia de obstáculos montado
Figura 6 – Foto do robô montado

Código-fonte do robô que desvia de obstáculos

Segue abaixo o código-fonte do projeto. Leia atentamente seus comentários para total compreensão do mesmo.

#include <Ultrasonic.h>

/* Definições dos GPIOs para leitura do sensor ultrasonico */
#define GPIO_TRIGGER     12
#define GPIO_ECHO        11

/* Definições de operação do sensor ultrasônico */
#define DISTANCIA_MINIMA_CM                10.0 //cm
#define TEMPO_ENTRE_LEITURAS_DE_DISTANCIA  250  //ms

/* Definições para controle dos dois motores */
#define IN_1      3
#define IN_2      4
#define IN_3      5
#define IN_4      6

/* Definições dos motores a serem controlados */
#define MOTOR_A                      0x00
#define MOTOR_B                      0x01

/* Definições das ações dos motores */
#define ACAO_FREIO                   0x00
#define ACAO_MOVIMENTO_ANTI_HORARIO  0x01
#define ACAO_MOVIMENTO_HORARIO       0x02
#define ACAO_PONTO_MORTO             0x03

/* Definições de sentido de giro (em caso de obstáculo) */
#define SENTIDO_GIRO_ANTI_HORARIO    0x00
#define SENTIDO_GIRO_HORARIO         0x01

/* Definições do desvio de objetos */
#define ESTADO_AGUARDA_OBSTACULO     0x00
#define ESTADO_GIRANDO               0x01

/* Variáveis e objetos globais */
Ultrasonic ultrasonic(GPIO_TRIGGER, GPIO_ECHO);
char ultimo_lado_que_girou = SENTIDO_GIRO_ANTI_HORARIO;
char estado_desvio_obstaculos = ESTADO_AGUARDA_OBSTACULO;

/* Protótipos */
void configura_gpios_controle_motor(void);
void controla_motor(char motor, char acao);
float le_distancia_sensor_ultrasonico(void);
void maquina_estados_desvio_obstaculos(float distancia_obstaculo);

/* Função: configura GPIOs de controle do L298N como output
 * Parâmetros: nenhum
 * Retorno: nenhum
 */
void configura_gpios_controle_motor(void)
{
    pinMode(IN_1, OUTPUT);
    pinMode(IN_2, OUTPUT);
    pinMode(IN_3, OUTPUT);
    pinMode(IN_4, OUTPUT);
}

/* Função: controle um motor (freia, movimento anti-horário, movimento horário
 *         ou ponto morto)
 * Parâmetros: motor a ser controlado e ação desejada
 * Retorno: nenhum
 */
void controla_motor(char motor, char acao)
{
    int gpio_1_motor = 0;
    int gpio_2_motor = 0;

    /* seleciona os GPIOs de acordo com o motor desejado */
    switch(motor)
    {
        case MOTOR_A:
            gpio_1_motor = IN_1;
            gpio_2_motor = IN_2;
            break;
    
        case MOTOR_B:
            gpio_1_motor = IN_3;
            gpio_2_motor = IN_4;
            break;

        default:
            /* Motor inválido. Nada mais deve ser feito nesta função */
            return;            
    }

    /* Controla o motor conforme ação desejada */
    switch(acao)
    {
        case ACAO_FREIO:
            digitalWrite(gpio_1_motor, HIGH);
            digitalWrite(gpio_2_motor, HIGH);
            break;

        case ACAO_MOVIMENTO_ANTI_HORARIO:
            digitalWrite(gpio_1_motor, LOW);
            digitalWrite(gpio_2_motor, HIGH);
            break;

        case ACAO_MOVIMENTO_HORARIO:
            digitalWrite(gpio_1_motor, HIGH);
            digitalWrite(gpio_2_motor, LOW);
            break;

        case ACAO_PONTO_MORTO:
            digitalWrite(gpio_1_motor, LOW);
            digitalWrite(gpio_2_motor, LOW);
            break;

        default:
            /* Ação inválida. Nada mais deve ser feito nesta função */
            return;                                                            
    }    
}

/* Função: faz leitura da distância (em centímetros) de obstáculo a frente do robô
 * Parâmetros: nenhum
 * Retorno: distância (cm)
 */
float le_distancia_sensor_ultrasonico(void)
{
    float distancia_cm = 0.0;
    long microsec = 0;
    
    microsec = ultrasonic.timing();
    distancia_cm = ultrasonic.convert(microsec, Ultrasonic::CM);
    return distancia_cm;
}

/* Função: maquina de estado responsavel por controlar o desvio de obstáculos
 * Parâmetros: distância de obstáculo a frente
 * Retorno: nenhum
 */
void maquina_estados_desvio_obstaculos(float distancia_obstaculo)
{
    switch(estado_desvio_obstaculos)
    {
        case ESTADO_AGUARDA_OBSTACULO:
            if (distancia_obstaculo <= DISTANCIA_MINIMA_CM)
            {
                /* Obstáculo encontrado. O robô deve girar para
                   desviar dele */
                Serial.println("[MOVIMENTO] Obstaculo encontrado!");   
                
                /* Alterna sentido de giro para se livrar de obstáculos
                   (para otimizar o desvio de obstáculos) */
                if (ultimo_lado_que_girou == SENTIDO_GIRO_ANTI_HORARIO)
                    ultimo_lado_que_girou = SENTIDO_GIRO_HORARIO;
                else
                    ultimo_lado_que_girou = SENTIDO_GIRO_ANTI_HORARIO;
                    
                estado_desvio_obstaculos = ESTADO_GIRANDO; 
            }
            else
            {
                Serial.println("[MOVIMENTO] Sem obstaculos a frente");
                
                /* Se não há obstáculos, continua em frente */
                controla_motor(MOTOR_A, ACAO_MOVIMENTO_HORARIO);
                controla_motor(MOTOR_B, ACAO_MOVIMENTO_HORARIO);
            }
            
            break;

        case ESTADO_GIRANDO: 
            if (distancia_obstaculo > DISTANCIA_MINIMA_CM)
            {
                /* Não há mais obstáculo a frente do robô */   
                estado_desvio_obstaculos = ESTADO_AGUARDA_OBSTACULO; 
            }
            else
            {
                if (ultimo_lado_que_girou == SENTIDO_GIRO_ANTI_HORARIO)
                {
                    controla_motor(MOTOR_A, ACAO_MOVIMENTO_ANTI_HORARIO);
                    controla_motor(MOTOR_B, ACAO_MOVIMENTO_HORARIO);
                    Serial.println("[MOVIMENTO] Girando no sentido anti-horario...");
                }
                else
                {
                    controla_motor(MOTOR_A, ACAO_MOVIMENTO_HORARIO);
                    controla_motor(MOTOR_B, ACAO_MOVIMENTO_ANTI_HORARIO);
                    Serial.println("[MOVIMENTO] Girando no sentido horario...");
                }
            }
            
            break;
    }
}

void setup() 
{
    Serial.begin(115200);
    
    /* Configura GPIOs de controle do L298N como output e coloca motor em condição de freio */
    configura_gpios_controle_motor();    
    controla_motor(MOTOR_A, ACAO_FREIO);
    controla_motor(MOTOR_B, ACAO_FREIO);
}

void loop() 
{
    float distancia_a_frente = 0.0;

    distancia_a_frente = le_distancia_sensor_ultrasonico();
    Serial.print("* Distancia lida: ");
    Serial.print(distancia_a_frente);
    Serial.println("cm");

    /* Verifica se há obstáculo a frente */
    maquina_estados_desvio_obstaculos(distancia_a_frente);

    delay(TEMPO_ENTRE_LEITURAS_DE_DISTANCIA);
}

Recapitulando: o robô vai em frente até encontrar um obstáculo, localizado a 10 ou menos centímetros a sua frente. Após isso, gira em sentido horário ou anti-horário (sentidos alternados, conforme estratégia adotada) até que consiga um caminho livre (sem obstáculos ou com obstáculo a mais de 10 centímetros a frente). Os giros em sentidos alternados visam aumenta a eficiência nos desvios de obstáculos.

Gostou deste post sobre como construir seu próprio robô que desvia de obstáculos? Deixe seu comentário logo abaixo.

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

2 Comentários

  1. microsec = ultrasonic.timing();
    nessa linha de programação esta dando este erro

    unsigned int Ultrasonic::timing()’ is private within this context

  2. Outra forma de programação interessante, pois tenho uma versão com uso de servo, na qual realiza a leitura de distânçia.