Curso Robótica – #5 – Evitar Obstáculos
Se tudo correr conforme o planeado, então, no momento, todos devem ter um robot complexo à sua frente, que já pode montar.
Durante esta parte do curso, usaremos sensores de obstáculos simples. Graças a eles, o robot poderá navegar em terrenos desconhecidos!
Até agora, não devemos chamar o nosso veículo de robot. Ele não tinha nenhum sensor, então ele não podia reagir ao seu redor. Os robots são caracterizados pelo fato de que, graças aos sensores, eles são autónomos (eles mesmos “tomam” as decisões).
Claro, isso é uma grande simplificação. “Tomar decisões” envolve a execução de ações de acordo com o algoritmo escrito pelo homem. Para decisões reais e inteligência artificial, os robots estão longe!
Nas construções móveis, os sensores de distância/obstáculo aparecem com mais frequência. Esses sensores permitem manter uma distância segura de outros participantes do tráfego e proteger o robot de colidir com obstáculos (por exemplo, paredes).
Deve ser lembrado que às vezes esses sensores também são usados para tarefas completamente reversas. Um exemplo ideal serão robots competindo em competições de Sumo que usam sensores para localizar o oponente e empurrá-lo para fora do tabuleiro.
Escolhendo um sensor de obstáculo
Obstáculos podem ser detectados de várias maneiras. Um exemplo é o sensor óptico. Usando um gerador construído com NE555, controlamos o díodo IR. A luz que ricocheteou nos obstáculos atingiu o nosso receptor e foi interpretada como um obstáculo.
Para o registro:
A vantagem da solução acima é que ela detecta um obstáculo sem contato físico. No entanto, por outro lado, este sensor tem um ângulo de visão relativamente estreito.
Em nosso robot, um sensor mecânico simples que será capaz de monitorar tanto a frente quanto a lateral do veículo terá um melhor desempenho. O que é mais importante, com certeza, descrevi novamente mais adiante neste texto.
Sensor de obstáculo mecânico – princípio de funcionamento
Nos conjuntos de componentes deste curso, há 2 chaves de limite com fios soldados. Eles nos permitirão detectar obstáculos:
Do lado eletrónico, o princípio de operação dessas chaves limite não difere dos botões comuns. Depois de pressionar a alavanca (placa), o sinal dentro do interruptor fecha.
Fios da cor não tem qualquer significado
e podem diferir das imagens.
Nossos terminais possuem 3 pinos (pinos) descritos na carcaça da seguinte forma:
- NO (normally open) – normalmente contato aberto
- NC (normally closed) – contato normalmente fechado
- C (commom) – contato comum
Os fios são soldados em C e em saída NO. Isso significa que, se o botão não for pressionado, não haverá conexão entre nossos leads. O sinal será capaz de fluir de C para NO somente após pressionar a alavanca do botão.
A sua operação é, portanto, idêntica aos
botões habituais tactswitch/microswitch.
Se usássemos par C e NC, o botão funcionaria ao contrário. O sinal poderia fluir de uma saída para a outra apenas quando a alavanca não estivesse pressionada. Tocar no botão faria com que os pinos se abrissem.
Conexão e instalação de sensores de obstáculos
Para facilitar a montagem e o posicionamento dos interruptores, nós os fixaremos usando pequenos elementos de contraplacado. Nós não vamos colocar os parafusos através dos orifícios no alojamento da chave! Os seguintes elementos são necessários para montar o primeiro sensor:
- interruptor de limite,
- elemento feito de madeira compensada,
- 2 parafusos M3 com um comprimento de 20 mm,
- 4 anilhas.
Claro, não há um lugar certo para montar os sensores. Existem várias variantes, cada uma tem suas vantagens e desvantagens. Abaixo estão algumas opções junto com sua breve descrição.
Durante as outras partes do curso, usaremos uma variante (No. 3), no entanto, recomendo que teste por conta própria!
Antes de iniciar a montagem, leia a descrição de todas as variantes,
para não adiar os sensores configurados incorretamente mais tarde!
Variante 1 – “frente” do lado de fora
A primeira possibilidade de montar os sensores é ajustá-los na frente e com as alavancas de limite apontando para fora.
Para montar esses sensores, é mais conveniente começar com a aplicação de elementos de compensado e pré-enroscamento das porcas. As anilhas devem ser adicionadas na cabeça do parafuso e na porca. É só depois de tal pré-anexo que traduzimos o sensor por baixo:
Finalmente, conecte os fios dos terminais. Para isso, usamos dois conectores que foram colocados na placa para os sensores. Informações detalhadas sobre este assunto podem ser encontradas na segunda parte do curso de robótica.
Uma das extremidades é conectada ao pino descrito como GND e a outra a A0/A1. A conexão correta:
Como lembrete, esta opção pressupõe a seguinte disposição dos sensores:
Como pode ser visto, esse espaçamento de sensores faz com que as alavancas respondam bem aos obstáculos no lado esquerdo e direito do robot. Infelizmente, uma perna relativamente estreita da mesa em que o robot iria para frente não seria capturada (nenhuma das alavancas teria sido deprimida).
Variante 2 – “frente” para dentro
A instalação de interruptores de limite com contraplacado, assim como os cabos, é a mesma do primeiro método. No entanto, desta vez, nossas alavancas são direcionadas para dentro:
Desta vez, o centro é muito melhor guardado. No entanto, perdemos o controle dos lados do robô. Nesta configuração, podemos nos conectar com obstáculos que não serão captados pelos sensores. Então é hora da 3ª opção…
Variante 3 – interseção de sensores
Seria melhor se nossos sensores tivessem braços ainda mais longos. No entanto, é difícil encontrar interruptores de limite com alavancas ainda mais longas à venda.
Ao construir robots, muitas vezes improvisamos e lidamos com as coisas que temos à mão. Agora vamos usar fragmentos de braçadeiras e tubos termo-retráteis!
O primeiro passo é cortar o fragmento desnecessário do grampo. O plástico é tão macio que a pulseira pode ser cortada mesmo com uma tesoura.
Durante o próximo passo, será necessário um tubo retrátil que, após o aquecimento, reduz o seu diâmetro (encolhe). Usando a tesoura, corte o tubo de 2 cm e aplique-o na alavanca do sensor.
Inserimos a parte mais grossa da banda no tubo termo-retráteis (aquele com os dentes), a
extremidade mais fina (sem os dentes) a partir de agora será a extremidade estendida do sensor.
Detalhes nas fotos abaixo!
Agora aqueça o tubo (para apertar), é melhor fazê-lo sobre um isqueiro, uma vela ou usando um ferro de solda quente (soprando ar quente). Claro que é necessário ter cuidado para não derreter a banda e não se aleijar – cuidado com queimaduras!
Para os jovens leitores, recomendo usar a ajuda de um adulto!
Esses sensores estendidos não podem ser consertados como descrito na opção 1, porque eles se sobrepõem uns aos outros. No entanto, usando o fato de que as alavancas serem agora muito longas, podemos cruzá-las. Graças a isso, será possível detectar obstáculos nas laterais do robô e, ao mesmo tempo, não perderemos de vista a frente – a situação ideal!
Tem de se lembrar que este arranjo introduz uma pequena “confusão”.
Agora o sensor aparafusado à direita nos informará
sobre os obstáculos que estão no lado esquerdo do robot!
É claro que, devido às alavancas estendidas, não podemos montar interruptores de limite como antes, porque eles tocam nas bandas. A solução é muito simples. Apenas um parafuso de sensor a partir do topo e o outro a partir do fundo:
Durante testes adicionais, sugiro começar com esse arranjo de sensores. No entanto, como mencionei anteriormente – eu encorajo os leitores mais tarde a testar outras formas de correção!
Teste de sensor na prática
É hora de verificar como os nossos sensores funcionam na prática. No início, um programa simples que exibirá informações sobre o status dos sensores via UART. O esboço é muito simples e provavelmente não requer tradução. Nós simplesmente verificamos o status das entradas e exibimos as mensagens nessa base.
#define L_SIDE_SENSOR A0 #define R_SIDE_SENSOR A1 void setup() { Serial.begin(9600); //Start komunikacji przez UART pinMode(L_SIDE_SENSOR, INPUT_PULLUP); //Konfiguracja pinu jako wejście pinMode(R_SIDE_SENSOR, INPUT_PULLUP); //Konfiguracja pinu jako wejście } void loop() { if (digitalRead(L_SIDE_SENSOR) == LOW) { Serial.println("Wykryto przeszkode po lewej stronie!"); } if (digitalRead(R_SIDE_SENSOR) == LOW) { Serial.println("Wykryto przeszkode po prawej stronie!"); } delay(500); //Opoznienie dla zwiekszenia czytelnosci komunikatow }
A tarefa do atraso adicionado é limitar a quantidade de mensagens que são enviadas para o computador quando mantemos o botão pressionado. Na prática, o sistema funciona da seguinte maneira:
O uso de sensores no robot
Há muitas maneiras de evitar obstáculos e, certamente, cada leitor resolverá a sua solução. Abaixo eu apresento as minhas propostas para tal programa. No começo, começo com um esboço que foi feito na última parte do curso. Eu só adicionei informações sobre os sensores e a campainha, o que será útil.
Novas linhas foram destacadas no seguinte código:
#define L_PWM 5 #define L_DIR 4 #define R_PWM 6 #define R_DIR 9 #define PWM_MAX 165 #define L_SIDE_SENSOR A0 #define R_SIDE_SENSOR A1 #define BUZZER 10 void setup() { //Konfiguracja pinow od mostka H pinMode(L_DIR, OUTPUT); pinMode(R_DIR, OUTPUT); pinMode(L_PWM, OUTPUT); pinMode(R_PWM, OUTPUT); //Konfiguracja pinow od czujnikow pinMode(L_SIDE_SENSOR, INPUT_PULLUP); pinMode(R_SIDE_SENSOR, INPUT_PULLUP); //Konfiguracja pozostalych elementow pinMode(BUZZER, OUTPUT); digitalWrite(BUZZER, 0); //Wylaczenie buzzera } void loop() { //Jedz do przodu leftMotor(40); rightMotor(40); } void leftMotor(int V) { if (V > 0) { //Jesli predkosc jest wieksza od 0 (dodatnia) V = map(V, 0, 100, 0, PWM_MAX); digitalWrite(L_DIR, 0); //Kierunek: do przodu analogWrite(L_PWM, V); //Ustawienie predkosci } else { V = abs(V); //Funkcja abs() zwroci wartosc V bez znaku V = map(V, 0, 100, 0, PWM_MAX); digitalWrite(L_DIR, 1); //Kierunek: do tyłu analogWrite(L_PWM, V); //Ustawienie predkosci } } void rightMotor(int V) { if (V > 0) { //Jesli predkosc jest wieksza od 0 (dodatnia) V = map(V, 0, 100, 0, PWM_MAX); digitalWrite(R_DIR, 0); //Kierunek: do przodu analogWrite(R_PWM, V); //Ustawienie predkosci } else { V = abs(V); //Funkcja abs() zwroci wartosc V bez znaku V = map(V, 0, 100, 0, PWM_MAX); digitalWrite(R_DIR, 1); //Kierunek: do tyłu analogWrite(R_PWM, V); //Ustawienie predkosci } } void stopMotors() { analogWrite(L_PWM, 0); //Wylaczenie silnika lewego analogWrite(R_PWM, 0); //Wylaczenie silnika prawego }
O programa acima, depois de iniciar o robot, fará com que ele se mova o tempo todo. No loop principal, deve adicionar uma condição que usa sensores. No começo, sugiro realizar a mesma reação para cada um deles:
#define L_PWM 5 #define L_DIR 4 #define R_PWM 6 #define R_DIR 9 #define PWM_MAX 165 #define L_SIDE_SENSOR A0 #define R_SIDE_SENSOR A1 #define BUZZER 10 void setup() { //Konfiguracja pinow od mostka H pinMode(L_DIR, OUTPUT); pinMode(R_DIR, OUTPUT); pinMode(L_PWM, OUTPUT); pinMode(R_PWM, OUTPUT); //Konfiguracja pinow od czujnikow pinMode(L_SIDE_SENSOR, INPUT_PULLUP); pinMode(R_SIDE_SENSOR, INPUT_PULLUP); //Konfiguracja pozostalych elementow pinMode(BUZZER, OUTPUT); digitalWrite(BUZZER, 0); //Wylaczenie buzzera } void loop() { //Jedz do przodu leftMotor(40); rightMotor(40); if (digitalRead(L_SIDE_SENSOR) == LOW || digitalRead(R_SIDE_SENSOR) == LOW) { //Jesli przeszkoda po dowolnej stronie //Jedz wstecz i wydawaj dzwiek leftMotor(-40); rightMotor(-40); digitalWrite(BUZZER, 1); delay(800); //Obrot w miejscu leftMotor(40); rightMotor(-40); digitalWrite(BUZZER, 0); delay(500); //Koniec warunku wracamy do jazdy prosto } } void leftMotor(int V) { if (V > 0) { //Jesli predkosc jest wieksza od 0 (dodatnia) V = map(V, 0, 100, 0, PWM_MAX); digitalWrite(L_DIR, 0); //Kierunek: do przodu analogWrite(L_PWM, V); //Ustawienie predkosci } else { V = abs(V); //Funkcja abs() zwroci wartosc V bez znaku V = map(V, 0, 100, 0, PWM_MAX); digitalWrite(L_DIR, 1); //Kierunek: do tyłu analogWrite(L_PWM, V); //Ustawienie predkosci } } void rightMotor(int V) { if (V > 0) { //Jesli predkosc jest wieksza od 0 (dodatnia) V = map(V, 0, 100, 0, PWM_MAX); digitalWrite(R_DIR, 0); //Kierunek: do przodu analogWrite(R_PWM, V); //Ustawienie predkosci } else { V = abs(V); //Funkcja abs() zwroci wartosc V bez znaku V = map(V, 0, 100, 0, PWM_MAX); digitalWrite(R_DIR, 1); //Kierunek: do tyłu analogWrite(R_PWM, V); //Ustawienie predkosci } } void stopMotors() { analogWrite(L_PWM, 0); //Wylaczenie silnika lewego analogWrite(R_PWM, 0); //Wylaczenie silnika prawego }
Depois de detectar um obstáculo, o robot recua suavemente, depois vira-se e vai mais longe. Naturalmente, o comando direto pode estar na segunda parte da condição (depois de adicionar mais). Isso não é necessário, no entanto, porque o comando direto é emitido em todos os circuitos de loop sem qualquer atraso. Assim que os sensores detectam um obstáculo, o robot vai para as instruções que estão dentro da condição. Após a manobra de rotação, o robot continuará em linha reta.
A operação deste mecanismo simples na prática é visível no seguinte vídeo:
Saltos de robot delicados são causados por uma rápida mudança da direção em que ele viaja. Para eliminar este problema, basta reduzir um pouco a velocidade do veículo.
O robot ainda não usa informações sobre a posição do obstáculo, então ele sempre gira na mesma direção. Ao ajustar o tempo de deslocamento reverso e o tempo de rotação, pode obter um comportamento diferente do robot. Quanto mais tempo as rotações forem, mais caótico será o passeio – encorajo-vos a testar.
No entanto, não exagere com a condução para trás. Lembre-se que a inversão é rígida, sem sensores.
Distinguindo informações de sensores
Para melhor efeito, vamos agora usar informações sobre sensores individuais. Se o obstáculo estiver à esquerda, viraremos à direita (para contornar o bloqueio). Da mesma forma, se detectarmos algo à direita, viraremos à esquerda.
#define L_PWM 5 #define L_DIR 4 #define R_PWM 6 #define R_DIR 9 #define PWM_MAX 165 #define L_SIDE_SENSOR A0 #define R_SIDE_SENSOR A1 #define BUZZER 10 void setup() { //Konfiguracja pinow od mostka H pinMode(L_DIR, OUTPUT); pinMode(R_DIR, OUTPUT); pinMode(L_PWM, OUTPUT); pinMode(R_PWM, OUTPUT); //Konfiguracja pinow od czujnikow pinMode(L_SIDE_SENSOR, INPUT_PULLUP); pinMode(R_SIDE_SENSOR, INPUT_PULLUP); //Konfiguracja pozostalych elementow pinMode(BUZZER, OUTPUT); digitalWrite(BUZZER, 0); //Wylaczenie buzzera } void loop() { //Jedz do przodu leftMotor(40); rightMotor(40); if (digitalRead(L_SIDE_SENSOR) == LOW) { //Jesli przeszkoda po lewej stronie //Jedz wstecz i wydawaj dzwiek leftMotor(-40); rightMotor(-40); digitalWrite(BUZZER, 1); delay(800); //Obrot w miejscu w prawo leftMotor(40); rightMotor(-40); digitalWrite(BUZZER, 0); delay(140); //Koniec warunku wracamy do jazdy prosto } if (digitalRead(R_SIDE_SENSOR) == LOW) { //Jesli przeszkoda po prawej stronie //Jedz wstecz i wydawaj dzwiek leftMotor(-40); rightMotor(-40); digitalWrite(BUZZER, 1); delay(800); //Obrot w miejscu w lewo leftMotor(-40); rightMotor(40); digitalWrite(BUZZER, 0); delay(140); //Koniec warunku wracamy do jazdy prosto } } void leftMotor(int V) { if (V > 0) { //Jesli predkosc jest wieksza od 0 (dodatnia) V = map(V, 0, 100, 0, PWM_MAX); digitalWrite(L_DIR, 0); //Kierunek: do przodu analogWrite(L_PWM, V); //Ustawienie predkosci } else { V = abs(V); //Funkcja abs() zwroci wartosc V bez znaku V = map(V, 0, 100, 0, PWM_MAX); digitalWrite(L_DIR, 1); //Kierunek: do tyłu analogWrite(L_PWM, V); //Ustawienie predkosci } } void rightMotor(int V) { if (V > 0) { //Jesli predkosc jest wieksza od 0 (dodatnia) V = map(V, 0, 100, 0, PWM_MAX); digitalWrite(R_DIR, 0); //Kierunek: do przodu analogWrite(R_PWM, V); //Ustawienie predkosci } else { V = abs(V); //Funkcja abs() zwroci wartosc V bez znaku V = map(V, 0, 100, 0, PWM_MAX); digitalWrite(R_DIR, 1); //Kierunek: do tyłu analogWrite(R_PWM, V); //Ustawienie predkosci } } void stopMotors() { analogWrite(L_PWM, 0); //Wylaczenie silnika lewego analogWrite(R_PWM, 0); //Wylaczenie silnika prawego }
Rotação aleatória do robot
A maneira mais fácil de começar é a partir de um tempo de rotação aleatória, portanto, na prática, o ângulo em que o robot gira após “saltar” do obstáculo será aleatório. Para alterações visíveis, vale a pena começar com um atraso adicional, por exemplo, 50-200 ms.
O primeiro pensamento é usar a função aleatória da seguinte maneira:
int los = random(50, 201);
De seguida, obteremos valores aleatórios do intervalo assumido, mas na maioria dos casos eles não terão um impacto significativo no comportamento do robot. A rotação de 100 ou 102 ms não será praticamente diferente uma da outra (a diferença de tempo será muito pequena).
Será melhor desenhar valores com maior diferenciação (por exemplo, mínimo a cada 10 ms). Para este propósito, é suficiente desenhar valores do intervalo 5 – 20, e então multiplicar este resultado por 10. Na prática, o desenho 5 nos dará 5 * 10 = 50 ms , e o desenho 6 dará 6 * 10 = 60 ms .
int los = random(5, 21) * 10; //Dodajemy losowo od 50 do 200 ms obrotu
Adicionamos esse valor calculado ao tempo de negociação. Claro, também deve lembrar de inicializar o gerador de números pseudo-aleatórios!
#define L_PWM 5 #define L_DIR 4 #define R_PWM 6 #define R_DIR 9 #define PWM_MAX 165 #define L_SIDE_SENSOR A0 #define R_SIDE_SENSOR A1 #define BUZZER 10 void setup() { //Konfiguracja pinow od mostka H pinMode(L_DIR, OUTPUT); pinMode(R_DIR, OUTPUT); pinMode(L_PWM, OUTPUT); pinMode(R_PWM, OUTPUT); //Konfiguracja pinow od czujnikow pinMode(L_SIDE_SENSOR, INPUT_PULLUP); pinMode(R_SIDE_SENSOR, INPUT_PULLUP); //Konfiguracja pozostalych elementow pinMode(BUZZER, OUTPUT); digitalWrite(BUZZER, 0); //Wylaczenie buzzera randomSeed(analogRead(5)); //Inicjalizacja generatora } void loop() { //Jedz do przodu leftMotor(30); rightMotor(30); int los = random(5, 20) * 10; //Dodajemy losowo od 50 do 200 ms obrotu if (digitalRead(L_SIDE_SENSOR) == LOW) { //Jesli przeszkoda po lewej stronie //Jedz wstecz i wydawaj dzwiek leftMotor(-30); rightMotor(-30); digitalWrite(BUZZER, 1); delay(300); //Jazda po łuku leftMotor(30); rightMotor(-30); digitalWrite(BUZZER, 0); delay(140+los); //Koniec warunku wracamy do jazdy prosto } if (digitalRead(R_SIDE_SENSOR) == LOW) { //Jesli przeszkoda po prawej stronie //Jedz wstecz i wydawaj dzwiek leftMotor(-30); rightMotor(-30); digitalWrite(BUZZER, 1); delay(300); //Jazda po łuku leftMotor(-30); rightMotor(30); digitalWrite(BUZZER, 0); delay(140+los); //Koniec warunku wracamy do jazdy prosto } } void leftMotor(int V) { if (V > 0) { //Jesli predkosc jest wieksza od 0 (dodatnia) V = map(V, 0, 100, 0, PWM_MAX); digitalWrite(L_DIR, 0); //Kierunek: do przodu analogWrite(L_PWM, V); //Ustawienie predkosci } else { V = abs(V); //Funkcja abs() zwroci wartosc V bez znaku V = map(V, 0, 100, 0, PWM_MAX); digitalWrite(L_DIR, 1); //Kierunek: do tyłu analogWrite(L_PWM, V); //Ustawienie predkosci } } void rightMotor(int V) { if (V > 0) { //Jesli predkosc jest wieksza od 0 (dodatnia) V = map(V, 0, 100, 0, PWM_MAX); digitalWrite(R_DIR, 0); //Kierunek: do przodu analogWrite(R_PWM, V); //Ustawienie predkosci } else { V = abs(V); //Funkcja abs() zwroci wartosc V bez znaku V = map(V, 0, 100, 0, PWM_MAX); digitalWrite(R_DIR, 1); //Kierunek: do tyłu analogWrite(R_PWM, V); //Ustawienie predkosci } } void stopMotors() { analogWrite(L_PWM, 0); //Wylaczenie silnika lewego analogWrite(R_PWM, 0); //Wylaczenie silnika prawego }
Um exemplo de várias viagens do meu robot usando o código acima:
http://https://youtu.be/_seBRY20SSU
Encorajo-vos a experimentar tempos de reversão e revoluções. Também vale a pena usar outros movimentos do robot, por exemplo: andar em curva. Mostre os vídeos nos comentários, como o seu robot consegue evitar os obstáculos que colocou no seu caminho!
Nos programas acima, nós nunca verificamos se ambos os sensores detectaram algo no caminho do nosso robot. É claro que pode combinar as duas condições e esperar pela situação em que o robot ficará perfeitamente direto no obstáculo. O Arduino verifica os estados de entrada com bastante frequência (milhões de vezes por segundo), pressionando ambos os sensores ao mesmo tempo, muito raramente.
Na maioria das vezes, qualquer um dos sensores detectará um obstáculo um pouco mais rápido que o outro!
Correções mecânicas
Além de alterar o programa, também vale observar o comportamento dos sensores. Se as nossas alavancas forem muito longas ou muito curtas, isso também afetará negativamente a operação do robot.
Além do comprimento da alavanca, vale a pena verificar os ângulos em que os sensores estão posicionados, por vezes, uma ligeira alteração na fixação pode afetar o funcionamento de toda a estrutura. É claro que os sensores não precisam ser ajustados simetricamente – também pode verificar como o robot conseguirá contornar os obstáculos quando cada um dos sensores for definido de forma diferente.
Sumário
Como eu mencionei na parte anterior do curso – não precisa de se preocupar com o fato de que o robot “não poder” ir perfeitamente para a frente. Depois de adicionar sensores, ele pode reagir ao ambiente e fazer correções na sua rota, dependendo dos obstáculos encontrados.
Na próxima parte do curso, vamos lidar com o uso de sensores ópticos. Vamos construir um robot que vai para a fonte mais forte de luz. Na prática, isso significa que você pode controlar o robot com uma lanterna!