Trabalho Prático 3 - Robótica móvel (navegação e controle)
1. Objetivo:
O Trabalho Prático 3 - Robótica Móvel (navegação e controle) teve como premissa a familiarização com o problema de localização, desenvolvimento de um sistema de odometria básica utilizando sensores do tipo shaft-encoding e implementação de técnicas básicas de navegação e controle. As tarefas a serem desenvolvidas são:
1.1. Localização: Através do uso de um sensor utilizando LDR's em montagem diferencial, o robô deverá ser capaz de alinhar com a fonte de luz mais próxima presente no campo de prova.
1.2. Odometria e controle: Uso de sensores break-beam para a construção de shaft-encoders. Implementeção de um controlador PD (Proporcional e Derivativo) para controlar a velocidade do robô Realização de um caminho pré-determinado através de um menu de opções (reta, quadrado, triângulo), onde o comprimento da reta e do lado das figuras geométricas deverá ser escolhido no menu.
1.3. Navegação (line-following): O robô deverá seguir as linhas marcadas no campo da competição, utilizando no máximo dois sensores ópticos (óptico-reflexivo ou LDR).
2. Mecânica:
Grande parte da composição mecânica do robô foi herdada do trabalho anterior (http://homepages.dcc.ufmg.br/~doug/cursos/doku.php?id=cursos:introrobotica:2017-1:grupo05:tp2), sendo alterado somente algumas partes como: inserção dos sensores óptico-reflexivos para a navegação (line-following), eliminação do sensor de detecção de objetos e inserção do sensor de luz polarizada montado na forma diferencial. As imagens abaixo ilustram a montagem mecânica do robô:
3. Eletrônica embarcada
A eletrônica embarcada foi a mesma utilizada no trabalho prático anterior (http://homepages.dcc.ufmg.br/~doug/cursos/doku.php?id=cursos:introrobotica:2017-1:grupo05:tp2), com a ressalva das alterações necessárias para a inserção de novos sensores: sensores opto-reflexivos e break-beam.
3.1. Localização:
Para resolver a tarefa de alinhar o robô com a fonte de luz mais próxima, foi feita uma montagem utilizando dois sensores LDR e filtros polarizadores. Sua configuração final segue abaixo:
3.1.1. LDR Diferencial
O sensor LDR é um resistor cuja resistência varia de acordo com a intensidade da luz que incide sobre ele. Seu funcionamento típico é: à medida que a intensidade da luz aumenta, a sua resistência diminui. Dessa forma, na escuridão, o LDR possui uma resistência máxima, e na luz muito brilhante, sua resistência é mínima. Já um LDR diferencial nada mais é do que dois sensores LDR ligados em série, com um ponto de medição inserido entre estes sensores.
Para a montagem física dos sensores, os LDRs foram posicionados separados por uma parede de lego. Em frente a cada sensor LDR havia um filtro polarizador.
Diferente do trabalho anterior, agora ambas as resistências (R1 e R2) do circuito são variáveis. Os resistores correspondem aos LDRs e o valor de suas resistências variam de acordo com a intensidade da luz. Ao montar um divisor de tensão, obtém-se a equação correspondente exibida acima:
Através da equação, observa-se que a tensão de saída (V_LDR) é diretamente proporcional à resistência (R2) do sensor LDR. Ou seja, a tensão de saída será maior com um índice de luminosidade incidente menor. O circuito do divisor de corrente segue ao lado:
3.2. Filtro Polarizador
Esse tipo de filtro funciona como uma fenda para uma direção certa. Ou seja, o filtro polarizador deixa passar a parte da onda que oscila num determinado plano. A figura abaixo demonstra o que foi citado anteriormente:
A luminária utilizada no projeto emite uma luz polarizada. Dessa forma, só passa luz pelo filtro caso as ondas provenientes da luz estejam alinhadas a um ângulo de 0, 180 ou 360 graus. Caso a diferença de fase seja de 90 ou 270 graus a passagem de luz é nula. Dessa forma, o filtro tinha a função de controlar a intensidade de luz recebida pelos sensores LDR.
4. Software Embarcado
Assim como nos trabalhos anteriores, todo o software embarcado foi composto em C/C++ utilizando o framework Arduino para uma placa microcontrolada base do tipo Arduino Mega2560. As subseções abaixo descrevem a implementação de cada tarefa sob a perspectiva do software.
4.1 Localização – Identificação da fonte de luz polarizada:
Para identificar a fonte de luz mais pŕoxima, o robô faz um giro de 360 graus (em torno de seu eixo Normal) armazenando os valores de entrada dado pelos LDRs. O menor valor lido após esse movimento de rotação é onde a fonte de luz mais próxima está. A localização (ângulo correspondente medido pelos encoders) foi sendo armazenada à medida que uma entrada menor era recebida. Feito isso, o robô voltava para a posição armazenada (posição da luz mais pŕoxima). O algoritmo correspondente segue abaixo:
// Condicao de parada // Verifica se os motores atingiram o num de voltas // necessaria para o ang requisitado (360) if((leftEncCount>=ligthLeftEncoder)&&(rightEncCount>=ligthRightEncoder)){ getOutLp = 1; // Sai do loop } // Gira o robo 360 graus para a direita leftMotor->run(FORWARD); leftMotor->setSpeed(leftMotorSpeed); rigthMotor->run(BACKWARD); rigthMotor->setSpeed(rigthMotorSpeed); // Faz a leitura dos encoderes leftEncState = digitalRead(leftEncoder); rightEncState = digitalRead(rigthEncoder); // Verifica borda de descida do encoder esquerdo if(leftEncState == HIGH) { if(oldLeftEncState == LOW) { leftEncCount++; } }else { //Verifica borda de subida if(oldLeftEncState == HIGH) { leftEncCount++; } } // Verifica borda de descida do encoder direito if(rightEncState == HIGH) { if(oldRightEncState == LOW) { rightEncCount++; } }else { //Verifica borda de subida if(oldRightEncState == HIGH) { rightEncCount++; } } // Faz a leitura do sensor ldr ldrInputValue = analogRead(ldr); // Verifica se o ponto luminoso eh maior // O ponto luminoso ocorre qnd o ldr faz uma leitura pequena if(ldrInputValue < minLdrValue) { minLdrValue = ldrInputValue; ligthLeftEncoder = leftEncCount; ligthRightEncoder = rightEncCount; } oldRightEncState = rightEncState; oldLeftEncState = leftEncState;
Segue abaixo o vídeo da versão final da localização da luz polarizada:
4.2. Odometria e Controle
Foram utilizados sensores break-beam para construir shaft-encoders. Foi implementado um controlador do tipo PD para controlar a velocidade do robô. Além disso, o robô é capaz de realizar um determinado caminho com uma distância selecionada através do menu (Reta, Quadrado, Triângulo).
Os Sensores break-beam foram usados para medir a variação (rotação) do eixo da roda. Dessa forma era possível obter: quão rápido as rodas estão girando e o número total de rotações. Um disco perfurado foi colocado sobre o eixo, entre o par de emissor-detector. À medida que o eixo girava, os furos no disco cortam o feixe de luz na velocidade do giro. As rotações foram contadas a partir do número de bordas de subida e descida. A montagem do encoder segue abaixo:
A distância agora é controlada pelo valor retornado pelos encoders do motor direito e esquerdo. Primeiramente foi necessário medir quantas bordas de subida e descida os encoderes retornavam para uma volta de 360 graus das rodas. O valor final retornado pelo encoder foi obtido de forma empírica (foram feitas várias medidas para obter o melhor valor correspondente).
Considerando que a roda tem 8cm de diâmetro, a distância translacional do robô correspondia a 25,13cm (d = 2*pi*R = 25,13cm). O valor de bordas retornado pelos encoderes foi 22. Através de uma regra de três simples, foi obtido o ganho (= 0.48) que deveria ser multiplicado pelas distâncias requisitadas (dadas em centímetro).
O mesmo foi feito para descobrir o valor correspondente do número de rotação do encoder para o ângulo em graus. Nesse caso, foi medido quantas bordas os encoders retornavam para um giro de 360 graus do robô (em torno de seu eixo Normal). O algoritmo utilizado para resolver o problema segue abaixo:
// Quantas voltas o encoder deve dar para atingir a distancia necessaria distRightMotor = 0.48*dist; distLeftMotor = 0.48*dist; while (getOut != 1) { // Verifica se os motores atingiram o num de voltas necessaria para a dist requisitada if((leftEncCount>=distLeftMotor)&&(rightEncCount>=distRightMotor)) { getOut = 1; } // Caso seja terminado if(getOut == 1) { leftMotor->run(RELEASE); rigthMotor->run(RELEASE); Serial.println("esq"); Serial.println(leftEncCount); Serial.println("dir"); Serial.println(rightEncCount); break; } // Gira as rodas 360 graus, ou seja, 25,13cm de translacao leftMotor->run(FORWARD); leftMotor->setSpeed(leftMotorSpeed); rigthMotor->run(FORWARD); rigthMotor->setSpeed(rightMotorSpeed); // Faz a leitura dos encoderes leftEncState = digitalRead(leftEncoder); rightEncState = digitalRead(rigthEncoder); // Verifica borda de descida do encoder esquerdo if(leftEncState == HIGH) { if(oldLeftEncState == LOW) { leftEncCount++; } }else { //Verifica borda de subida if(oldLeftEncState == HIGH) { leftEncCount++; } } // Verifica borda de descida do encoder direito if(rightEncState == HIGH) { if(oldRightEncState == LOW) { rightEncCount++; } }else { //Verifica borda de subida if(oldRightEncState == HIGH){ rightEncCount++; } } oldRightEncState = rightEncState; oldLeftEncState = leftEncState; }
Por fim, o controle da velocidade foi obtido de forma empírica. Através de vaŕios testes, foi atribuído um ganho Kp que era multiplicado pelo erro (diferença entre o setpoint e a saída). O ganho ke também foi obtido de forma empírica e multiplicado pela variação do erro (diferença entre o erro anterior e o erro atual). O proporcional e o derivativo são somados e atribuídos à saída. Segue abaixo o vídeo da versão final da odometria e controle:
Vídeos - Odometria e Controle:
| Linha reta | Quadrado | Triângulo |
|---|
4.4. Navegação (line-following):
O algoritmo de navegação implementado foi o de seguidor de linha simples, utilizando dois sensores óptico-reflexivos como fonte de sinal de detecção da linha. Para o controle da velocidade dos motores foi utilizado um controlador do tipo PD (Proporcional e Derivativo). O controlador do tipo PD obtém dos sensores break-beam a velocidade a qual o robô desloca-se e a partir desse valor, juntamente com os valores das constantes proporcional e derivativa, efetua o ajuste na alimentação dos motores através do valor da largura de pulso (PWM) aplicada a cada um deles. Desta forma, é possível o robô realizar trajetória em linha reta mesmo tendo motores com taxas de rotação de eixo diferentes – caso comum encontrado na prática. A correção da trajetória para seguir a linha traçada é dada pelos sensores ópticos, quando um dos sensores detecta o traçado, implica que é necessário o aumento da velocidade do motor contrário para a correção da trajetória (correção esta efetuada através do ajuste da alimentação do motor, via alimentação por modulação do tipo larguar de pulso (PWM)). Abaixo, segue trecho do código fonte que implementa o algoritmo de navegação:
//Navegação (line-following) // ... //Atribui ao Setpoint de cada sensor um valor médio setPointR = (blackSurfaceR - whiteSurfaceR)/2 + whiteSurfaceR; setPointL = (blackSurfaceL - whiteSurfaceL)/2 + whiteSurfaceL; lcd.clear(); lcd.setCursor(0,0); lcd.print("Kp = "); Kp = 1; leftMotor->run(FORWARD); leftMotor->setSpeed(60); rigthMotor->run(FORWARD); rigthMotor->setSpeed(72); while(1){ //Leitura dos sensores rightValue = analogRead(rightSensor); leftValue = analogRead(leftSensor); //Erro = Setpoint - leitura correctionR = Kp * (setPointR - rightValue); correctionL = Kp * (setPointL - leftValue); //Move para frente if(rightValue < setPointR && leftValue < setPointL){ leftMotor->run(FORWARD); leftMotor->setSpeed(60); rigthMotor->run(FORWARD); rigthMotor->setSpeed(72); } //Move para direita else if(rightValue > setPointR && leftValue < setPointL){ leftMotor->run(FORWARD); if(60 + correctionR <= 255){ leftMotor->setSpeed(60 + correctionR); } else{ leftMotor->setSpeed(255); } rigthMotor->run(BACKWARD); rigthMotor->setSpeed(72); } //Move para esquerda else if(rightValue < setPointR && leftValue > setPointL){ rigthMotor->run(FORWARD); if(72 + correctionL <= 255){ rigthMotor->setSpeed(72 + correctionL); } else{ rigthMotor->setSpeed(255); } leftMotor->run(BACKWARD); leftMotor->setSpeed(60); } //Move também para esquerda, caso ambos sobre a linha preta else if(rightValue > setPointR && leftValue > setPointL){ rigthMotor->run(FORWARD); if(72 + correctionL <= 255){ rigthMotor->setSpeed(72 + correctionL); } else{ rigthMotor->setSpeed(255); } leftMotor->run(BACKWARD); leftMotor->setSpeed(60); } }
Abaixo, vídeo exibindo a navegação por line-following do robô:
Conclusão:
O trabalho prático proporcionou melhor entendimento dos conceitos apresentados na disciplina de Introdução à Robótica, através da implementação de um robô móvel capaz de realizar as tarefas de localização, odometria e controle e navegação (line-following).
O problema de localização foi resolvido através do uso de um sensor de luz utilizando sensores do tipo LDR montados em uma configuração diferencial, assim, o robô foi capaz de alinhar-se com a fonte de luz mais próxima presente no campo de prova. A odometria e controle foi realizada utilizando sensores do tipo break-beam para construir os shaft-encoders. Um controlador do tipo PD (Proporcional e Derivativo) foi implementado para controlar a velocidade do motor. A tarefa de navegação (line-following) foi feita utilizando dois sensores óptico-reflexivos posicionados nas extremidades do robô. Assim, pôde-se controlar o sentido de delocamento do robô de forma que o mesmo pudesse seguir o trajeto determinado por uma linha demarcada no campo de prova. Tal qual especificado no escopo do trabalho, as tarefas a serem executadas foram selecionadas através de um menu.
Referências:
- The Art of Lego Design; Martin, Fred G.; March 1995
- Shield do display/teclado: https://www.dfrobot.com/wiki/index.php/Arduino_LCD_KeyPad_Shield_(SKU:_DFR0009)
- Robotics Explorations: A Hands-On Introduction to Engineering. Fred Martin. [RE]





