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:
- 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.
microsec = ultrasonic.timing();
nessa linha de programação esta dando este erro
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.
ola pessoal, estou tendo esse erro ao compilsr
slgum pode me ajudar ?
exit status 1
‘unsigned int Ultrasonic::timing()’ is private within this context