Tabela de conteúdos
Trabalho Final - Robôs Brigadistas
Introdução
O quarto trabalho prático, final, consistiu na integração das soluções já implementadas nos outros TPS. Foram utilizados:
- Um ldr para o start (implementado para esse TP Final)
- Um ldr diferencial (implementado no TP3)
- Dois ópticos reflexivos para seguidor de linha (implementado no TP3)
- Um sensor de cores, baseado em LDR e LEDS (implementado no TP2)
- Estrutura do robô móvel com motores (implementada no TP2 e atualizada em todos os TPs)
Materiais
Robô
- Lego
- Arduino Mega 2560 (com respectivo cabo)
- 2 motores DC
- Alicate de corte
- Alicate de bico
- Fios
- Ferro de solda
- Solda
- Termo Retrátil
- Abraçadeira
- Cola quente
LDR de start
- LDR
- Resistor de 10KΩ
- Fios
- Termo retrátil
- Ferro de Solda
- Solda de estanho
- Alicate de corte
- Abraçadeira
Sensor de cores
- Placa de circuito impresso
- LED de alto brilho vermelho
- LED de alto brilho verde
- LED de alto brilho azul
- LDR
- Diodo
- Resistores de 2KΩ
- Resistor de 10KΩ
- Fios
- Termo retrátil
- Ferro de solda
- Solda de estanho
- Alicate de corte
- Alicate de bico
- Abraçadeira
LDR Diferencial
- Plaquinhas
- Resistências
- Trimpots
- Solução de percloreto de ferro
- LDR
- Filtro polarizador
Ópticos reflexivos
- 2 sensores TCRT5000
- 2 Resistências de 330Ω
- 2 Resistências de 12KΩ
- Fios
- Ferro de Solda
- Termoretratil
- Solda de estanho
- Alicate
Procedimentos
Para esse TP, precisavamos consertar e/ou otimizar problemas anteriores, implementar novas soluções e integrar as já existentes, para isso, fizemos as seguintes tarefas:
- Consertar os problemas dos TPs antigos;
- Um dos problemas do TP3 era o LDR Diferencial, que não funcionou como previsto. A fim de ter uma boa implementação, foi corrigido o problema. - Notamos que o robô estava demasiadamente grande, e pesado. Para otimizar a performance do robô, reduzimos as dimensões e peso do robô, sem prejudicar o seu bom funcionamento, e compactamos alguns componentes, como foi o caso do sensor de cores, que teve seu tamanho reduzido. - Notamos que também poderia ser vantajoso alterar a posição de alguns sensores, e assim fizemos com o optico-reflexivo e o sensor de cores, e foi bastante significativo as mudanças.
- Implementação do LDR para o Start;
- A fim de poder iniciar o circuito como pedido, implementamos um sensor para a luz de start. Esse sensor é bem simples, no qual é constituido de LDR e uma resistencia, no qual, quando a luz de start incide sobre ele, um sinal é enviado ao arduino, demonstrando que iniciou a prova, e partir daí ele toma as devidas decisões.
- Implementar um equipamento para manuseio dos blocos.
- Foi necessário a criação de acrescer ao robô essa nova função, devido a demanda do trabalho, e sendo assim acrescemos um novo braço a ele, para manusear as peças no campo.
- Integração e estratégias
- Fazer a junção das soluções já implementadas anteriormente e necessárias para o atual desafio, e unir os códigos dessas implementações em um código de uma única tarefa, e definir as melhores estratégias para a melhor performance do robô.
- Uma das decisões tomadas, era que não seria necessário o uso de um encoder, pois seriamos orientados pela linhas que estão no chão.
Documentação do código
Foi utilizado várias funções já citadas anteriormente nos outros trabalhos, que são:
Seguidor de linha
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();
}
}
}
Sensor de cores
O código do sensor era composto por 4 funções básicas, nas quais eram: Calibragem(), Reconhecimento_Blocos(), Leitura() e ConvertRGB(int atual, char palavra[4]).
A função Calibragem() é chamada apenas no setup, onde ela calibra o sensor para os valores mínimos e máximos de cada cor que serão lidos pelo arduino, no qual se pede uma superficie branca e outra preta para se ter os valores.
A função Leitura() é a que fica por conta de fazer verificar de fato a leitura da cor, onde, primeiro liga o LED vermelho por 0,1s e lê o valor que está no pino 8 do Arduino, e registra esse valor em uma variável e repete o procedimento para os leds verde e azul. e no fim da leitura dos 3 LEDS, ele espera por 0,3s.
A função ConvertRGB tem como função converter o valor do LDR, que pode variar de 0 a 1023 para cada uma das 3 cores, para valores do padrão da representação numérica RGB, que varia de 0 a 255. A função recebe dois parametros, onde um é a leitura no formato analógico, e uma string com 4 caracteres que se refere a qual cor aquela leitura, na qual pode ser “verm”, “verd”, e “azul” (Que são os valores de cada um dos 3 leds). Após isso, a função retorna o valor da superficie lida no padrão numérico RGB.
A função Reconhecimento_Blocos() tem como função aprender os valores RGB de cada um dos blocos da apresentação, e salva os valores nos vetores blocoVermelho[3], blocoVerde[3], blocoAzul[3] e blocoAmarelo[3], onde a posição [0] dos vetores corresponde a cor vermelha, a [1] corresponde a verde, e a [2] corresponde a azul. Com isso, ele aprende qual é a cor do bloco para posteriormente poder ler e comparar com esses valores.
Abaixo estão os códigos de cada uma das funções:
FUNÇÃO CALIBRAGEM
void Calibragem(){
//CALIBRAGEM BRANCO
int botao;
botao = analogRead (0);
do{
botao = analogRead (0);
lcd.setCursor(0,0);
lcd.print("PAPEL BRANCO:");
lcd.setCursor(0,1);
lcd.print("Aperte Select");
}while(not(botao >= 600 & botao < 800));
lcd.clear();
lcd.setCursor(0,0);
lcd.print("PAPEL BRANCO:");
lcd.setCursor(0,1);
lcd.print("Calibrando.");
digitalWrite(PinVerm, 1);
delay(100);
VermMax = analogRead(sensorPin);
digitalWrite(PinVerm, 0);
lcd.print(".");
digitalWrite(PinVerd, 1);
delay(100);
VerdMax = analogRead(sensorPin);
digitalWrite(PinVerd, 0);
lcd.print(".");
digitalWrite(PinAzul, 1);
delay(100);
AzulMax = analogRead(sensorPin);
digitalWrite(PinAzul, 0);
lcd.clear(); // CALIBRAGEM DE PRETO
do{
botao = analogRead (0);
lcd.setCursor(0,0);
lcd.print("PAPEL PRETO:");
lcd.setCursor(0,1);
lcd.print("Aperte Select");
}while(not(botao >= 600 & botao < 800));
lcd.clear();
lcd.setCursor(0,0);
lcd.print("PAPEL PRETO:");
lcd.setCursor(0,1);
lcd.print("Calibrando.");
digitalWrite(PinVerm, 1);
delay(100);
VermMin = analogRead(sensorPin);
digitalWrite(PinVerm, 0);
lcd.print(".");
digitalWrite(PinVerd, 1);
delay(100);
VerdMin = analogRead(sensorPin);
digitalWrite(PinVerd, 0);
lcd.print(".");
digitalWrite(PinAzul, 1);
delay(100);
AzulMin = analogRead(sensorPin);
digitalWrite(PinAzul, 0);
lcd.clear();
}
FUNÇÃO LEITURA
void Leitura(){
digitalWrite(PinVerm, 1); // Leitura Pino Vermelho
delay(100);
int sensor_val = analogRead(sensorPin);
Red = ConvertRGB(sensor_val,"verm");
digitalWrite(PinVerm, 0);
digitalWrite(PinVerd, 1); // Leitura Pino Verde
delay(100);
sensor_val = analogRead(sensorPin);
Green = ConvertRGB(sensor_val,"verd");
digitalWrite(PinVerd, 0);
digitalWrite(PinAzul, 1); // Leitura Pino Azul
delay(100);
sensor_val = analogRead(sensorPin);
Blue = ConvertRGB(sensor_val,"azul");
digitalWrite(PinAzul, 0);
delay (300);
}
FUNÇÃO DE RECONHECIMENTO DOS BLOCOS
void Reconhecimento_Blocos(){
int botao;
botao = analogRead (0);
lcd.clear(); // Reconhecimento Bloco Vermelho
do{
botao = analogRead (0);
lcd.setCursor(0,0);
lcd.print("BLOCO VERMELHO:");
lcd.setCursor(0,1);
lcd.print("Aperte Select");
}while(not(botao >= 600 & botao < 800));
Leitura();
blocoVermelho[0] = Red;
blocoVermelho[1] = Green;
blocoVermelho[2] = Blue;
lcd.clear(); // Reconhecimento Bloco Verde
do{
botao = analogRead (0);
botao = analogRead (0);
lcd.setCursor(0,0);
lcd.print("BLOCO VERDE:");
lcd.setCursor(0,1);
lcd.print("Aperte Select");
}while(not(botao >= 600 & botao < 800));
Leitura();
blocoVerde[0] = Red;
blocoVerde[1] = Green;
blocoVerde[2] = Blue;
lcd.clear(); // Reconhecimento Bloco Azul
do{
botao = analogRead (0);
lcd.setCursor(0,0);
lcd.print("BLOCO AZUL:");
lcd.setCursor(0,1);
lcd.print("Aperte Select");
}while(not(botao >= 600 & botao < 800));
Leitura();
blocoAzul[0] = Red;
blocoAzul[1] = Green;
blocoAzul[2] = Blue;
lcd.clear(); // Reconhecimento Bloco Amarelo
do{
botao = analogRead (0);
lcd.setCursor(0,0);
lcd.print("BLOCO AMARELO:");
lcd.setCursor(0,1);
lcd.print("Aperte Select");
}while(not(botao >= 600 & botao < 800));
Leitura();
blocoAmarelo[0] = Red;
blocoAmarelo[1] = Green;
blocoAmarelo[2] = Blue;
lcd.clear();
}
FUNÇÃO DE CONVERSÃO PARA O PADRÃO RGB
int ConvertRGB(int atual, char palavra[4]) // Convertendo para o formato de cores RGB{
if (palavra == "verm")
{
float aux = (float(atual) - float(VermMin)) / (float(VermMax) - float(VermMin));
aux = aux*255;
if (aux > 255)
return (255);
else if (aux <0)
return (0);
else
return (aux);
}
else if (palavra == "verd")
{
float aux = (float(atual) - float(VerdMin)) / (float(VerdMax) - float(VerdMin));
aux = aux*255;
if (aux > 255)
return (255);
else if (aux <0)
return (0);
else
return (aux);
}
else if (palavra == "azul")
{
float aux = (float(atual) - float(AzulMin)) / (float(AzulMax) - float(AzulMin));
aux = aux*255;
if (aux > 255)
return (255);
else if (aux <0)
return (0);
else
return (aux);
}
}
A função Loop usa as funções pré-estabelecidas chamando a função Leitura, e verificando as variáveis Red, Green e Blue, que são os valores já convertidos para o padrão RGB, e compara se os valores são próximos (com um grau de sensibilidade) aos registrados nos vetores dos blocos, como descritos acima. Após os testes lógicos, ele imprime na tela a cor do bloco e altera o valor da variável global Cor_Bloco para o valor respectivo de cada bloco, e caso se a cor não for a de nenhum bloco, ele imprime “Sem Bloco”, e variável Cor_Bloco continua 0. Essa variável serve para que no modo Multitarefas, ele chame a função e só teste o valor da variável global, para saber qual será a ação a ser realizada.
LDR Diferencial
Como o LDR diferencial foi construído em paralelo, nosso código consiste em executar uma volta e pegando os valores de máximo e minimo. Uma vez coletados estes e sabendo que o robô pode estar ou no lado cujo filtro é horizontal ou vertical temos as seguinte situação:
a) O robô está no lado com o filtro horizontal. Quando o robô está nesse lado as leituras captadas pelo LDR se encontram na faixa de 200, com exceção de quando esse se encontra alinhado ou quase alinhado quando as leituras atingem seu mínimo próximo de 100.
b) O robô está no lado com o filtro vertical. Quando o robô está nesse lado as leituras do LDR mais uma vez encontram-se em valores próximos de 200 alterando, novamente, quando o robô está quase alinhado que tais valores atingem seu máximo cima de 400.
A partir desses conhecimentos sabemos que se a leitura de máximo for superior a 400 ele se encontra do lado com o filtro vertical, nesse caso devendo permanecer girando até encontrar um valor próximo do máximo. Caso contrário o valor procurado é o de mínimo e o robô deverá permanecer em movimento até um valor da janela de máximo ser atingido.
O código encontra-se abaixo:
void orienta(){///Colocar e o cabeçalho da função também
int ldrDifValor = 0;
int Max = 0 ;
int Min = 1023;
long previousMillis = millis();
long interval = 2500;
motor1->setSpeed(50);
motor1->run(FORWARD);
motor2->setSpeed(50);
motor2->run(BACKWARD);
ldrDifValor = analogRead(ldrDif);
//delay(300);
while(millis() - previousMillis < interval){
ldrDifValor = analogRead(ldrDif);
Max = max(ldrDifValor,Max);
Min = min(ldrDifValor,Min);
}//Peimeira volta
if(Max > 400){
while((analogRead(ldrDif) > Max + 10)||(analogRead(ldrDif) < Max - 10)){
}
motor1->run(RELEASE);
motor2->run(RELEASE);
}
else{
while((analogRead(ldrDif) > Min + 10)||(analogRead(ldrDif) < Min - 10)){
}
motor1->run(RELEASE);
motor2->run(RELEASE);
}
}
Tal código foi construído da seguinte maneira:
- Fazer as leituras com o sensor e garantir seu funcionamento adequado, ou seja, não realizando medidas incoerentes.
- O robô faz as leituras imprimindo-as no LCD enquanto ele dá uma volta
- Girar o robô fazendo as leituras de máximo e mínimo. Ao término da volta imprime os valores de máximo e mínimo
- Função de localizar completa
E os novos códigos que foram implementados para o atual trabalho, que são o da estratégia a ser feita e o do LDR de start:
Sensor de Start
O sensor de Start foi de simples implementação já que seu valor fica próximo de zero na ausência de luz e ultrapassa 400 quando com luz. Seu código consiste em apenas em fazer leituras até que um valor maior que o estipulado seja captado.
void loop() {
ldrValor = analogRead(ldr);
if(ldrValor > 500){
motor1->setSpeed(50);
motor1->run(FORWARD);
motor2->setSpeed(50);
motor2->run(FORWARD);
}
}
Algoritmo da estratégia
A estratégia consistia em fazer um percurso com o seguidor de linha e com isso coletando os blocos e leva-los até a base. Inicialmente pretendia-se fazer com que os blocos pretos fossem evitados
Resultados
Colocação na competição: 7ºLugar (Penúltimo)
Apresentação do grupo:
Problemas encontrados
Durante a execução das tarefas grande parte das tarefas exigiu que melhorássemos as coisas anteriormente produzidas. Tentamos suavizar os movimentos do seguidor de linha e na hora de unirmos as partes de código dos respectivos sensores tivemos dificuldade em fazer com que o robô simultaneamente seguisse linha e rejeitasse blocos pretos.
Pontos a melhorar
Uma possível melhora de se fazer seria na garra do robô, uma vez que ela não mantem os bloquinhos presos ao robô sob nenhuma circunstância. Fazer uma integração entre o seguidor de linha e a função de localização de modo que uma vez que o robô pegasse a linha central e chegasse próximo a linha da base ele parasse de seguir linha e passasse a utilizar das leituras do LDR diferencial para nela adentrar. A garra do robô também ficou bem simples não sendo capaz de carregar o bloco ou esfera até a base ou mante-lo preso.






