Robô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:
- Um Kit Chassi 2WD Robô para Arduino
- Uma Placa Nano V3.0 + Cabo USB para Arduino
- Um Driver Motor Ponte H L298n
- Um Protoboard 400 Pontos
- Um Sensor de Distância Ultrassônico HC-SR04
- Um Suporte Sensor Ultrassônico HC-SR04
- Um kit de Jumpers Macho-Fêmea x40 Unidades
- Um Kit Jumpers Macho-Macho x65 Unidades
- Como fonte de alimentação, este projeto usa um powerbank (carregador portátil de celulares) sendo portanto uma “bateria recarregável” para seu robô. Se você não conhece o que é um powerbank, veja aqui fotos de alguns deles.
Dica: quanto maior a carga (quanto mais mAh) do powerbank, melhor, pois ele funcionará por mais tempo antes de precisar de uma recarga. Neste post, utilizei um de 10.000mAh, que é uma carga considerável.
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:
- 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. - 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.
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.
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.
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.
Esse projeto serve para arduíno uno
Olá Valdinei.
Funciona, porém, será necessário efetuar algumas mudanças tanto no código quanto no esquema elétrico.
Att.
Vitor Mattos.
Suporte Técnico MakerHero.
ola pessoal, estou tendo esse erro ao compilsr
slgum pode me ajudar ?
exit status 1
‘unsigned int Ultrasonic::timing()’ is private within this context
Outra forma de programação interessante, pois tenho uma versão com uso de servo, na qual realiza a leitura de distânçia.
microsec = ultrasonic.timing();
nessa linha de programação esta dando este erro
unsigned int Ultrasonic::timing()’ is private within this context