Aprendendo a trabalhar com Sensores
O terceiro trabalho prático consistiu na construção de sensores. Eles foram:
Em caráter especial o professor permitiu o uso de um encoder industrial para fazer a parte de odometria do trabalho, uma vez que não estavam sendo encontrados os materiais necessários para confeccioná-los. Além disso foi pedido a implantação de um controle PID.
Robô
LDR Diferencial
Ópticos reflexivos
Encoder
A montagem do robô foi dividida em 4 etapas:
- Um dos problemas do TP2 era o menu, que era sempre necessário reiniciar o arduíno para usar outra função, tendo isso em mente, a nova implementação visa não ser necessário ter que reiniciar, e se precisar, não precisar que calibre ou demais contra-tempos.
Na etapa de confecção do Controle PID primeiramente realizou-se um estudo detalhado sobre os motores através dos enconders que possibilitou a determinação de uma zona de tensão que seria vantajosa para a movimentação do robô e a caracterização de cada um dos dois motores que possibilitou determinar a diferença entre eles auxiliando a determinação das ações de controle. Apos isso inicio-se a confecção do controlador PID que determinava as atuações do robô para que esse se movesse com regularidade em 3 percursos diferentes : Reta,Triângulo e Quadrado com o valor de lado de tamanho determinado pelo usuário através de um seletor no display.
O código do seguidor de linha seguiu a seguinte lógica:
O código embora aparentemente bom demonstrou não ter tanta autonomia para o robô.
O LDR diferencial foi construído em cima de uma plaquinha que de cobre que depois foi corroída com uma solução de percloreto de ferro fazendo assim um circuito impresso. Os LDR's foram colocados dentro de um suporte para led de modo a entrar focar mais a entrada de luz, evitando interferências externas. Para refinar as leituras foram colocados dois trimpots e os ldrs colocados com um, a resistência na horizontal e outro na vertical, de modo que os filtros polarizadores direcionassem a luz para a resistência na posição correspondente. Sobre o código, a ideia a principio era fazer uma função recursiva que refinasse o alinhamento do sensor com a luz. Uma vez que essa ideia foi mal sucedida, a ideia passou a ser algo como: enquanto os valores não se encontram dentro das faixas da luz polarizadas,gire.
A função para localizar, dada a entrada do LDR e uma variável pra receber a leitura do sensor, utiliza, como supracitado de parâmetros de comparação previamente estabelecidos e segue a lógica de enquanto não forem encontrados os parâmetros o robô fica rotacionando.
void orienta(int ldrDifValor,int ldrDif){
while(ldrDifValor<(valor_comp_V+30) && ldrDifValor>(valor_comp_H+30)){
motor1->run(BACKWARD);
motor1->setSpeed(80);
motor2->run(FORWARD);
motor2->setSpeed(80);
delay(1000);
motor1->run(RELEASE);
motor2->run(RELEASE);
ldrDifValor = analogRead(ldrDif);
lcd.print(ldrDifValor);
delay(1000);
lcd.clear();
if(ldrDifValor<100){
lcd.print("Erro");
break;
}
}
O código do Menu é composto por 3 funções, onde elas são:
A função Menu() é feita para ser chamada apenas uma vez no loop(), onde internamente ela tem um while(1) para não necessitar de voltar no Loop, para uma implementação mais fácil. A função Menu() tem as opções para serem selecionadas: Localizacao, Caminhos, Seguidor Linha, e calibração; quando é selecionado Localizacao e Seguidor Linha, é chamado a função que faz essa determinada ação, e quando chamado Caminhos, se chama a função Caminhos(), que também faz parte do Menu. O código do Menu() é:
void Menu(){
lcd.clear();
delay(500);
int x=4, ant=3;
int botao;
while(1){
botao = analogRead(0);
if (x<= 0 || x>= 8) x=4;
if (x!=ant){
switch(x%4){
case 0:
lcd.setCursor(0,0);lcd.print(" Localizacao ");
lcd.setCursor(0,1);lcd.print("<- ->");
break;
case 1:
lcd.setCursor(0,0);lcd.print(" Caminhos ");
lcd.setCursor(0,1);lcd.print("<- ->");
break;
case 2:
lcd.setCursor(0,0);lcd.print(" Seguidor Linha ");
lcd.setCursor(0,1);lcd.print("<- ->");
break;
case 3:
lcd.setCursor(0,0);lcd.print(" Calibracao ");
lcd.setCursor(0,1);lcd.print("<- ->");
break;
}
}
ant=x;
if(botao >= 400 & botao < 600 ) // Reconfigurar os valores para o valor de esquerda
x--;
else if(botao >= 0 & botao < 100) // Reconfigurar os valores para o valor de direita
x++;
else if((botao >= 600 & botao < 800)||(botao >= 200 & botao < 300)) {// Foi apertado select ou down
if ((x%4+1) == 1) {// Foi selecionado a primeira função do robô: Localização
/***********************************************************/
//COLOQUE AQUI A FUNÇÃO QUE CHAMA A EXECUÇÃO DA LOCALIZAÇÃO
/***********************************************************/
lcd.print("Entrou");
ldrDifValor = analogRead(ldrDif);
lcd.print(ldrDifValor);
orienta(ldrDifValor,ldrDif);
motor1->run(RELEASE);
motor2->run(RELEASE);
lcd.print("Parou");
lcd.clear();
}
else if ((x%4+1) == 2)
Caminhos(); //Foi selecionado a segunda função do robô: de fazer caminhos diversos
else if ((x%4+1) == 3){ //Foi selecionado a terceira função do robô: Seguidor de linha.
/********************************************************************/
//COLOQUE AQUI A FUNÇÃO QUE CHAMA A EXECUÇÃO DO MODO SEGUIDOR DE LINHA
/********************************************************************/
lcd.clear();
while(1){
leitura();
}
}
else if ((x%4+1) == 4){ //Foi selecionado a quarta função do robô: Calibrar os sensores.
/*********************************************/
//COLOQUE AQUI A FUNÇÃO QUE CHAMA A CALIBRAÇÃO
/*********************************************/
lcd.clear();lcd.setCursor(0,0);lcd.print("Ainda nao feito");} // APÓS COLOCAR A FUNÇÃO, APAGUE ISSO
}
delay(250);
}
}
A função Caminhos() é complementar ao menu, onde tem internamente um while(1), para a execução ficar somente na função. A função Caminhos tem na sua interface a opção de escolher a opção: Reta, Triângulo e Quadrado, e se, em qualquer momento, for apertado para cima, chama de volta a função Menu(). Se selecionado qualquer uma das opções, uma variável recebe o valor que corresponde ao caminho a ser executado, e chama a função Comprimentos(int a), passando como parâmetro o valor que corresponde ao caminho a ser seguido. O código da função caminhos é:
void Caminhos(){
lcd.clear();
delay(500);
int i;
int x=3, ant=2;
int botao;
while(1){
botao = analogRead(0);
if (x<= 0 || x>= 6) x=3;
if (x!=ant){
switch(x%3){
case 0:
lcd.setCursor(0,0);lcd.print(" Reta ");
lcd.setCursor(0,1);lcd.print("<- ->");
i=0;
break;
case 1:
lcd.setCursor(0,0);lcd.print(" Triangulo ");
lcd.setCursor(0,1);lcd.print("<- ->");
i=1;
break;
case 2:
lcd.setCursor(0,0);lcd.print(" Quadrado ");
lcd.setCursor(0,1);lcd.print("<- ->");
i=2;
break;
}
}
ant=x;
if(botao >= 400 & botao < 600 ) x--; // Reconfigurar os valores para o valor de esquerda
else if(botao >= 0 & botao < 50) x++;// Reconfigurar os valores para o valor de direita
else if(botao >= 50 & botao < 150) Menu(); // Foi apertado up
else if((botao >= 600 & botao < 800)||(botao >= 200 & botao < 300)) Comprimentos((i+1)); // Foi apertado
select ou down
delay(250);
}
}
A função Comprimentos(int a), também é complementar ao menu, e tem em sua interface selecionar o comprimento do lado da figura geométrica selecionada na função Caminhos(), anteriormente. Foi pensado em criar com uma interface amigável, onde foi feito uma miniatura do lado impresso na tela do arduíno, seguido do comprometimento mostrado em número. O código da função Comprimentos é:
void Comprimentos(int a){
lcd.clear();
delay(500);
int x=30, y=30, z=30;
int botao;
while(1){
botao=analogRead(0);
if (a == 1){ // Comprimento da reta
lcd.setCursor(0,0);
lcd.print("Comprimento Reta");
lcd.setCursor(0,1);
lcd.print("v__________| ");
lcd.print(x);
lcd.print(" ");
if(x <= 0) x = 0;
if(x>=179) x=179;
lcd.setCursor((int(x/15)),1);
lcd.print("v");
if (botao >= 400 & botao < 600 ) x--; // Reconfigurar os valores para o valor de esquerda
else if (botao >= 0 & botao < 50 ) x++; // Reconfigurar os valores para o valor de direita
else if(botao >= 50 & botao < 150) Caminhos(); // Foi apertado up
else if((botao >= 600 & botao < 800)||(botao >= 200 & botao < 300)) {// Foi apertado select ou down
/*******************************************************************************************/
//COLOQUE AQUI A CHAMADA DA FUNÇÃO DA RETA! USE O PARAMETRO 'x' QUE É O COMPRIMENTOS DA LINHA.
/*******************************************************************************************/
MoverControladoReta(x);
ResetarPID();
// while(not((botao >= 400 & botao < 600 )||(botao >= 0 & botao < 50 )))botao=analogRead(0); // Pode
ser excluido apos incluir a função, só serve para travar.
delay(100);
}
}
else if (a==2){ // Comprimento dos lados do triangulo
lcd.setCursor(0,0);lcd.print("Lado Triangulo");
lcd.setCursor(0,1);lcd.print("v__________| ");lcd.print(x);lcd.print(" ");
if(x <= 0) x = 0;
if(x>=179) x=179;
lcd.setCursor((int(x/15)),1);lcd.print("v");
if (botao >= 400 & botao < 600 ) x--; // Reconfigurar os valores para o valor de esquerda
else if (botao >= 0 & botao < 50 ) x++; // Reconfigurar os valores para o valor de direita
else if(botao >= 50 & botao < 150) Caminhos(); // Foi apertado up
else if((botao >= 600 & botao < 800)||(botao >= 200 & botao < 300)){ // Foi apertado select ou down
/***********************************************************************************************************/
//COLOQUE AQUI A CHAMADA DA FUNÇÃO DO QUADRADO! USE O PARAMETRO 'x' QUE É O COMPRIMENTOS DO LADO DO
QUADRADO.
/***********************************************************************************************************/
MoverControladoReta(x);
ResetarPID();
MoverControladoAngulo(90);
ResetarPID();
MoverControladoReta(x);
ResetarPID();
MoverControladoAngulo(140);
ResetarPID();
MoverControladoReta(x+((40*x)/100));
ResetarPID();
//while(not((botao >= 400 & botao < 600 )||(botao >= 0 & botao < 50 )))botao=analogRead(0);;} // Pode ser
excluido apos incluir a função, só serve para travar.
delay(100);
}
O código do Seguidor de Linha é representado abaixo:
void pretobranco(){
Serial.print("Leitura2");
lcd.print("ESQUERDA");
Serial.println();
//MoverControladoAngulo(90);
motor1->run(FORWARD);
motor2->run(FORWARD);
motor1->setSpeed(35);
motor2->setSpeed(60);
}
void brancopreto(){
Serial.print("Leitura3");
lcd.print("Direita");
Serial.println();
//MoverControladoAngulo(-90);
motor1->run(FORWARD);
motor2->run(FORWARD);
motor1->setSpeed(60);
motor2->setSpeed(35);
}
void brancobranco(){
Serial.print("Leitura1");
lcd.print("RETO");
Serial.println();
motor1->run(FORWARD);
motor2->run(FORWARD);
motor1->setSpeed(60);
motor2->setSpeed(60);
}
void leitura(){
while(1){
Serial.print(digitalRead(S2));
Serial.print(digitalRead(S1));
if(digitalRead(S1) == 0 && digitalRead(S2) == 0){ //VIRA PRA ESQUERDA 90º
int i=0;
Serial.print("Leitura0");
lcd.print("Direita");
Serial.println();
motor1->run(RELEASE);
motor2->run(RELEASE);
delay(800);
Serial.print("Andou pra frente");
while(i != 200){
motor1->run(FORWARD); //Vai um pouco pra frente (vai estar lendo só branco)
motor2->run(FORWARD);
motor1->setSpeed(56.4);
motor2->setSpeed(60);
if(digitalRead(S1) == 1 && digitalRead(S2) == 1){
i++;
}
}
i=0;
motor1->run(RELEASE);
motor2->run(RELEASE);
delay(800);
Serial.print("fez angulo");
MoverControladoAngulo(90);
Serial.print("andou pra tras");
motor1->run(BACKWARD); //Volta um pouco pra trás;
motor2->run(BACKWARD);
motor1->setSpeed(60);
motor2->setSpeed(60);
delay(1300);
Serial.print("proximo");
motor1->run(RELEASE);
motor2->run(RELEASE);
delay(800);
ResetarPID();
}
//vai linha reta
if(digitalRead(S1) == 1 && digitalRead(S2) == 1){ // SEGUE RETO
brancobranco();
}
//vai para esquerda
if(digitalRead(S1) == 1 && digitalRead(S2) == 0){ //VIRA PRA ESQUERDA
pretobranco();
}
//vai para direita
if(digitalRead(S1) == 0 && digitalRead(S2) == 1){ //VIRA PRA DIREITA
brancopreto();
}
}
}
LDR Diferencial(25%) O LDR diferencial durante os testes com a sala vazia foi bem sucedido em sua orientação com as luzes polarizadas. Entretanto o uso de um dispositivo para a calibração e o estabelecimento de parâmetros, foi questionado, uma vez que com base nesses valores a decisão do robô fica pre-estabelecida tirando sua autonomia e a análise das leituras in loco e menos sensíveis a incertezas do próprio circuito, pessoas e flutuações de luminosidade. A sugestão de alteração envolve a construção de um elaborado algoritmo de derivação. Que a partir das variações das leituras tente fazer com que a leitura se aproxime ao maior ou menor valores detectados pelo sensor. Ou seja, seriam necessárias duas voltas para que o robô fosse capaz de cumprir sua missão. Outra ideia seria fazer as leituras enquanto elas forem menores que o máximo, por exemplo, e uma vez que as leituras antes com um valor maior que o anterior passarem a ser menores, sabemos que passamos do ponto de alinhamento, sendo necessária assim um retorno do robô e garantido seu alinhamento
Um dos principais pontos a melhorar é a implementação efetiva da característica característica do projeto, pois durante a realização do TP3 ainda existia grande influencia dos projetistas na tomada de decisão do robô fazendo a eficiência do mesmo ser reduzida. Outro ponto a se melhorar é as proporções do robô que causaram diversas variações na apresentação do projeto, além da massa o robô possui uma largura e um comprimento muito próximos do limite suportado pelo ambiente de teste.