Tabela de conteúdos
Trabalho Prático 3
Introdução
O trabalho prático consistia na elaboração de um robô que deveria realizar três tarefas :
- Por meio de um sensor LDR diferencial e lentes polarizadas, o robô deverá se alinhar com a fonte de luz mais próxima;
- Utilizando sensores break-beam, deve-se criar encoders para medir a rotação de cada roda. A partir disso, a velocidade do robô deve ser controlada para percorrer caminhos pré-determinados (Reta, Triângulo, Quadrado);
- O robô deve ser capaz de seguir linhas pretas no chão. Para isso, será utilizado sensores de infravermelho reflexivos;
- A escolha de tarefas deve ser feita a partir de um menu, utilizando os botões e o LCD do kit Arduino.
Estrutura física
Para a locomoção, foi utilizada essência a mesma estrutura física do trabalho anterior (TP2). A grande vantagem dessa base é a sua compacticidade e o fato de as duas rodas estarem no meio do corpo do robô, fato que permite uma facilidade maior para realizar curvas e girar em torno de seu eixo.
Sensores
Foram construídos 5 sensores para este trabalho, sendo 2 encoders, 2 infravermelho e 1 LDR diferencial.
Encoders
Os encoders são sensores utilizados para medir a rotação de uma roda. Montamos 2 e utilizamos 1 para cada roda do robô.
Utilizamos chaves ópticas break-beam em conjunto com resistores para construir o circuito abaixo:
Assim, em conjunto com uma rodinha com furos das peças do lego, o sensor é capaz de medir rotação. A construção final ficou como abaixo:
LDR Diferencial
O LDR diferencial é o sensor utilizado para a localização do robô. Ele consiste em um circuito divisor de tensão composto por 2 LDRs, em que o sinal é lido no ponto entre eles.
Na mesa que o robô precisa se localizar, existem 2 lâmpadas nas extremidades com filtros de luz direcionais em que, uma delas deixa a luz passar na direção vertical e a outra deixa a luz passar na direção horizontal. Assim, utilizando esses filtros em cada um dos LDRs (vertical em um e horizontal no outro), o sensor irá medir uma intensidade máxima quando apontado para uma das lâmpadas e mínima quando apontado para a outra. O circuito e a montagem estão abaixo:
Sensores Infravermelho
Esses sensores são utilizados para detectar objetos, mas funcionam muito bem para distinguir cores preto e branco. Assim, foram utilizados para detectar a linha preta sobre a mesa branca. Eles são compostos por um LED emissor de luz infravermelho e um transistor receptor dessa luz. O LED emite a luz que reflete no objeto à sua frente, fazendo com que o o transistor receba esse feixe.
Algoritmo
Controle
Para a realização do controle, utilizou-se como base um PID clássico. A função de controle recebe como base o erro entre a velocidade desejada (setpoint) e a velocidade atual, e retorna um valor de tensão PWM para o motor. É importante ressaltar que cada motor(esquerdo e direito) possui uma malha de controle independente.
double Controlador::LeiDeControle(double erro){ unsigned long now=millis(); double dt = (now - this->TempoAnterior); this->SomatorioErro += erro * dt; double dErr = (erro - this->ErroAnterior); double output = this->Kp*erro + this->Ki*SomatorioErro + this->Kd*dErr; this->TempoAnterior=now; this->ErroAnterior=erro; return(output); }
Localização
A localização feita com o sensor diferencial implica em valores de máximo em mínimo conforme o alinhamento do sensor com a lâmpada de luz polarizada mais próxima. Como não havia necessidade de alinhar com uma lâmpada em especifico e sim com a mais próxima, pode-se transformar o minimo em um maximo subtraindo um limiar do valor medido.
Os dados obtidos ao girar o sensor foram obtidos quando este estava próximo das lâmpadas.
Como pode-se observar, existe muito ruído, implicando na necessidade de implementar um filtro smoothing.
O código resultando é o seguinte:
bool Localizacao::EstaAlinhado() { int sinalAnterior = 0; if (this->readIndex == 0) { sinalAnterior = readings[19]; } else { sinalAnterior = readings[this->readIndex -1]; } this->RetornaSinal(); int val = readings[this->readIndex]; // Buffer utilizado pelo smoothing if( val < sinalAnterior) { return true; } else { return false; } }
Odometria
Para realizar a medição de velocidade do robô, utilizados chaves ópticas como encoders e interrupção para realizar a contagem de voltas. Isso foi feito com uma classe:
class Encoder { int const Pino; unsigned long int Voltas = 0; unsigned long int VoltasDuranteUltimoConsumo = 0; unsigned long int TempoDesdeUltimoConsumo; public: Encoder(const int pino); void IncrementaVoltas() { Voltas++; }; void operator++() { Voltas++; } float RetornaRPM(); void ZeraVoltas() { Voltas = 0; } static float CalculoRPMdeVoltas(int); int RetornaVolta(){return Voltas;} float CalculoVoltas(int v); }; float Encoder::RetornaRPM() { unsigned long int tempoAtual = millis(); unsigned long int deltaTempo = float(tempoAtual - this->TempoDesdeUltimoConsumo); float deltaRotacoes = this->CalculoVoltas(this->Voltas - this->VoltasDuranteUltimoConsumo); this->VoltasDuranteUltimoConsumo = this->Voltas; this->TempoDesdeUltimoConsumo = tempoAtual; return deltaRotacoes*60000.0/88.0/float(deltaTempo); // 60000 88 // 1916.92 }
A implementação da interrupção não pode ser feita utilizando uma função membro de uma classe porque ela usa um argumento, mesmo que nenhum seja declarado, o ponteiro this que aponta para o objeto instanciado do qual o método é chamado. A solução para contornar esse problema é implementar uma função void que chama a função desse membro e realizar a associação da interrupção com o pino no arquivo .ino
void leftMotorInterruptHandler() { encoderMotorEsquerda.IncrementaVoltas(); } void setup(){ attachInterrupt(digitalPinToInterrupt(LEFT_ENCODER_PIN), leftMotorInterruptHandler , RISING ); . . . }
Seguir Linha
void SeguidorDeLinha::Seguir (int esquerda, int direita){ if(esquerda < LIMIAR_LINHA && direita < LIMIAR_LINHA){ //SE OS DOIS ESTIVEREM DENTRO DA LINHA, O ROBO ANDARÁ PARA FRENTE }else if(esquerda < LIMIAR_LINHA && direita > LIMIAR_LINHA){ // SE O LADO ESQUERDO ESTIVER FORA, O ROBO FARÁ UMA CURVA PARA DIREITA }else if (esquerda > LIMIAR_LINHA && direita < LIMIAR_LINHA){ //ANALOGO AO ANTERIOR, }else { //SE OS 2 LADOS ESTIVEREM FORA DA LINHA O ROBO GIRA NO PRÓPRIO EIXO PROCURANDO UMA LINHA } }
Desafios
Os prncipais desafios do tp foram a integração dos algoritmos e a realização do controle.
O controle PID deve ser tunado de forma paramétrica, o que pe inviável de se fazer por tentativa e erro sem o surporte de um sistema automatizado de testes. O controle proporcional usando árvore de decisões (um amontoado de if else) também foi dificil de desenvolver apesar de ter funcionado durante uma etapa do desenvolvimento. O contole por chaveamente com histerese funcionou bem mas num momento muito tardio para que desse tempo de integrar com o restante do código.
A integração foi difícil de realizar em função do alto nível de acoplamento das funções e classes desenvolvidas.
Outro desafio a ser infrentado é a calibração do sensor de localização. Os problemas sencontrados foram
- Sensores tem sensibilidades diferentes à luz
- Pequenas alterações de ângulo implicavam em grande perda do alinhamento com a luz polarizada
- O sensor encontrava o máximo em um ângulo diferente do desejado, de forma persistente








