Figura - Estrutura Final
Sabe-se que um sensor LDR é um resistor variável que muda sua resistência de acordo com a luminosidade a qual está exposto. A medida que a luminosidade aumenta a resistência também aumenta. Sendo assim, dependendo do circuito projetado pode-se observar uma elevação de tensão a medida que a luminosidade aumenta (caso um resistor de pull-down seja utilizado), ou a tensão medida na porta analógica do microcontrolador pode diminuir com o aumento da luminosidade (caso seja usado um resistor de pull-up).
Figura - Resistor Pull-up e Pull-down
É importante notar que os valores de saída destes dois sensores estão relacionados com a resolução do conversor analógico digital do microcontrolador utilizado, sendo que o sinal de um sensor é o complementar do sinal do outro. Isso significa que se o conversor analógico digital do microcontrolador utilizado é de 10 bits (range de 0 - 1023), então se o sensor ligado da segunda maneira tiver como saída um valor X então o mesmo sensor ligado da primeira maneira descrita, terá como saída 1023 - X.
Já um LDR diferencial nada mais é do que sensores LDR ligados em série, com um ponto de medição inserido entre estes sensores. Seu princípio de funcionamento é mesmo de um divisor de tensão. Dependendo da resistência equivalente do circuito, passará uma corrente que é proporcional a relação da tensão de alimentação pela resistência equivalente. Sendo assim, a queda de tensão no primeiro resistor (primeiro sensor LDR) será basicamente a corrente calculada vezes a resistência do LDR.
Montando o circuito de LDR diferencial obtém-se a saída já proporcional a diferença de resitência entre os dois sensores. Porém, pode-se obter um resultado semelhante com o circuito dos LDR montado separadamente e com um processamento interno ao microcontrolador. Com esta montagem apesar de serem usadas mais portas analógicas, pode-se observar separadamente o efeito de cada sensor, e uma maior mobilidade ao circuito é obtida, podendo posicionar os sensores em lugares arbitrários.
Figura - Variação da resistência de acordo com a luminosidade
Figura - Sensor LDR com filtro polarizador
Os filtros polarizadores são filtros que funcionam como fendas em uma direção, isso é, só deixam passar a parte da onda que oscila num determinado plano. Como mostra a figura abaixo:
Figura - Filtro polarizador na luz
Entretanto, a luminária utilizada no projeto ao redor do campo já possui uma luz polarizada. Dessa forma, só passa luz pelo filtro caso as ondas proveniente da luz estejam alinhadas a um ângulo de 0, 180 ou 360 graus. Caso essa diferença de fase seja de 90 ou 270 graus a passagem de luz é nula.
Figura - Filtro com luz polarizada
Assim, este foi colocado na frente do LDR de forma a controlar a intensidade de luz que este recebia. E ajudar na localização, uma vez que era possível se alinhar com as ondas de luz.d
Com o intuito de medir a velocidade de rotação de uma roda, foi construído o circuito de sensores do tipo break-beam. Estes consistem no conjunto de LED emissor de infravermelho e um receptor (fototransistor) posicionados um em frente ao outro de modo que o feixe de luz IR seja interrompido caso algum objeto passe na frente do feixe, impedindo a detecção.
Os sensores foram posicionados em frente às engrenagens que moviam cada roda. Foi observado que o espaçamento das engrenagens era diferente e por isso optou-se por tampar alguns dos furos de modo que apenas furos igualmente espaçados continuassem expostos. Sendo assim os LEDs foram ligados permanentemente à alimentação e os fototransistors conectados às portas digitais do Arduino. Quando havia detecção do feixe de luz (o feixe passava pelo furo da engrenagem) a entrada do microcontrolador era 1 (verdadeiro), caso contrário a entrada era 0. Com isso, pudemos fazer a contagem de furos em determinado intervalo de tempo e inferir a velocidade que o robô se movia.
Figura - Break-Beam
Uma das tarefas propostas neste trabalho prático foi seguir em linha reta até que um bloco fosse detectado em frente ao robô. De acordo com a cor desse bloco diferentes decisões seriam tomadas. Sendo assim foi fez-se necessária a criação de um código confiável para detecção de cores. Em hardware não houveram muitas modificações no circuito detector de cores. O circuito continuou consistindo em um LDR, resistor pull-up de 10KOhm, um LED rgb, e o resistor apropriado para o LED (neste TP usamos um resistor de 180 Ohm). Já no software, a estratégia de detecção mudou.
Posiciona-se uma superfície branca em frente ao LDR e LED RGB, faz-se 5 leituras de tensão proporcional às cores incididas (vermelho, verde, azul) e a média desses valores é salva em um vetor.
Faz-se o mesmo, porém agora com uma superfície preta. A média dos valores lidos é salva em outro vetor, e assim a calibração está concluída.
A checagem de cor é feita constantemente e consiste em realizar 5 leituras do sensor e fazer a média delas. O valor da cor após a calibragem será então a diferença da média calculada para os valores salvos como sendo branco e preto. Faz-se isso para cada uma das três cores.
Feita a relação dos valores de tensão proporcionais a leitura do LDR, relacionou-se esses valores a uma cor. Nesse passo foi utilizada a distância entre pontos para calcular para qual cor salva a leitura realizada era mais próxima. Estes valores para a cor foram inseridos tomando como base a quantidade de R,G e B presente em cada cor e ajustados empiricamente. Sendo assim, a menor distância medida era a cor do bloco que estava posicionado na frente do LDR e imprimiu-se no display LCD o nome dessa cor.
O buzzer é do que um componente eletrônico que faz o papel de um pequeno alto-falante capaz de emitir som. Esse é normalmente usado em projetos que necessitam de avisos sonoros. Neste caso, foi utilizado para avisar quando o robô detectasse um bloco verde em sua frente.
Figura - Ligação no Arduíno
A montagem anterior do carro, principalmente pelas dimensões, altura e caixa de marchas fez com que os ajustes para essa atividade fossem mínimos. As principais mudanças foram a instalação de um assoalho, uma forma provisória de fixação dos motores e sensores e uma maior preocupação do ponto de vista estético e limpeza do robô.
A mudança no assoalho se deu a necessidade de uma boa fixação e maior segurança dos componentes eletrônicos para essa etapa. Além de estar mais pesados, eles também aumentaram em número e volume e começaram a exigir um espaço para serem instalados que permitia uma certa flexibilidade. A mudança foi parcialmente satisfatória, uma vez que as engrenagens ainda se encontravam muito próximas dos componentes e isso cria um certo risco de acidentes com os fios.
Uma boa fixação dos motores e engrenagens é uma exigência inerente as estratégias de controle. A informação do sensor define o posicionamento do robô que ganha com isso muito mais autonomia, no entanto, pára de funcionar corretamente se receber o sinal errado dos sensores. Portanto, essa é uma preocupação que é de extrema importância e ainda precisa ter mais atenção. Os sensores foram fixados em peças de LEGO com cola quente e então grudados no assoalho pelo próprio encaixe das peças. Isso flexibilizou a escolha do posicionamento dos sensores e garantiu uma repetibilidade em suas medições. No entanto, os sensores que estavam próximos ao motor eram eventualmente deslocados da sua posição e geravam erros no funcionamento do robô. Essa fixação ainda precisa ser mais robusta e confiável.
Uma terceira preocupação para essa etapa foram os fios. A quantidade de sensores se tornou muito superior as das tarefas anteriores e quantidade e tamanho dos fios também aumentou. Portanto, foi montada uma estrutura no centro do carro para encobrir esses fios e fixar melhor os motores. A estrutura não encobria as placa ou o arduino e, consequentemente, o encaixe dos fios também estava exposto. O encaixe por sí só já mostrava e expunha muito os fios e é algo que pode ser melhorado.
Sugestões para a próxima etapa:
Estrutura do código Nesta etapa, reestruturamos as bibliotecas internas do código para utilizar os conceitos de orientação objeto. Sendo assim cada biblioteca criada foi abstraída em uma classe, aumentando a legibilidade e manutenibilidade do código. Nesta etapas nos implementamos quatro bibliotecas:
A biblioteca de menu continua com o funcionamento similar ao do TP anterior e portanto não será detalhada nesta documentação.
Shaft Encoder A biblioteca de ShaftEncoder possui rotinas para medir o giro e velocidade do motor associado. Possui basicamente dois tipos de métodos: O read() que é executado no loop do Arduino, que realiza a leitura do break-beam e atualiza o contador e timer interno, e o getSpeed() que calcula a velocidade angular do motor com base nos valores internos.
Quando o método read() detecta uma borda de subida no sensor break-beam isso indica que um quarto da engrenagem (a engrenagem possui 4 orificios que ativam o break-beam) foi girada. Quando isso acontece, nos salvamos o tempo que o motor levou para girar esse ¼ de roda e adicionamos 1 ao contador de giro. Quando o método getSpeed() é chamado, ele calcula a velocidade angular do motor com base no ultimo tempo salvo. Como a velocidade é atualizada apenas quando o engrenagem completa ¼ de giro, se a engrenagem parar de girar a velocidade permanecerá a mesma, causando problemas no módulo de controle. Desta forma nós adicionamos um limite de tempo, de modo que se o break-beam não for acionado dentro de 800ms, assumimos que ele está parado e retornamos a velocidade 0.
Motor Controller A biblioteca de Motor Controller é responsavel por controlar o motor de modo que ele se mantenha a uma velocidade objetivo desejada. Para isso ele utiliza o módulo de Shaft Encoder para medir a velocidade de giro e uma biblioteca externa de controle PID. Nós inicialmente haviamos implementado nossa propria biblioteca de PID, porém optamos por usar uma implementação externa pois ela era mais robusta e confiável (disponível em: https://github.com/br3ttb/Arduino-PID-Library/).
Os dois principais métodos do módulo são o setGoal() e o control(). O setGoal() é utilizado para definir a velocidade objetivo do robô. O control() é executado dentro do loop principal e é responsável por atualizar a tensão aplicada ao motor com base no controlador PID. O algorítimo da função control() funciona da seguinte forma:
Como o Shaft Encoder precisa esperar o giro de ¼ da engrenagem para atualizar sua leitura de velocidade, o metodo control() apenas atualiza a tensão aplicada no motor a cada 150ms. Desta forma, a tensão não é atualizada com base em uma leitura antiga de velocidade. Outra medida necessária para controlar corretamente o motor foi assumir que a leitura de velocidade do motor é de mesmo sinal que a tensão aplicada no motor, já que nosso Shaft Encoder não é capaz de detectar mudança na direção de giro.
Um caso especial da função control() é quando a velocidade objetivo é de 0. Como o não é necessário controlar nesse caso, quando a velocidade objetivo é 0, a tensão aplicada śerá 0 automaticamente.
Color Detect O modo de deteção de cores foi alterado do ultimo trabalho prático para realizar um detecção mais confiavel.
Adicionamos uma função de calibração. Ela funciona lendo uma amostra de branco e outra de preto de modo a ajustar as leituras subsequentes, onde o branco seria a maior leitura do LDR e preto a menor. Desta forma a influencia da luz ambiente é minimizada.
Outra mudança é no algoritmo de interpretação dos valores lidos. Para detectar a cor, nós interpretamos o três valores lidos como um ponto em um espaço de cores 3D, onde os eixos sistema de coordenadas deste espaço é dado pelos valores de vermelho, verde e azul. Nos utilizamos então pontos determinados empiricamente como os pontos ideias de cores no espaço para as cores a serem detectadas (Vermelho, Azul, Verde e Amarelo). Assim calculamos a distancia ente o ponto de cada cor ideal no espaço e a cor lida, e assumimos que a menor distancia é a da cor lida.
Tarefas Como nos outros trabalhos, utilizamos a função runTask() para programar a logica de cada tarefa a ser executada pelo robô. Para auxiliar a programação das tarefas, foram criadas duas funções auxiliares que calculam o tempo necessário para realizar um movimento dado uma velocidade de giro: distanceToDelay() e angleToDelay().
Medimos que para um uma velocidade angular de uma rotação por segundo da engrenagem o robô se movia aproximadamente a 2.75 cm/s. Usamos essa mediada para calcular então o tempo necessário para o robô uma distancia desejada a uma determinada velocidade.
Para o cálculo do tempo para girar um angulo, a distancia entre as rodas do robô e utilizamos esse valor como o diâmetro do circulo descrito. Usando o diâmetro medido podemos calcular o perímetro do circulo, de modo que para um giro de 360 graus seria necessário percorrer uma distancia igual ao perímetro do circulo, logo, dado um angulo qualquer, calculamos a distancia proporcional a ser percorrida para girar o robô. Com a distancia a ser percorrida podemos usar a função distanceToDelay() para determinar o tempo de giro.
Com essas duas funções, programar os caminhos e a tarefa de tomada de decisão trivial.
Alinhamento Para o alinhar o carrinho em direção a luz polarizada utilizamos a seguinte estratégia:
Vídeo: Reconhecimento de Cores
Vídeo: Realização de Percursos
Vídeo:Alinhamento com a Luz Polarizada