RELATORIO TP1 relatorio_tp_1_walle_final.pdf
RELATORIO TP2 tp2_robotica.docx_-_google_docs.pdf
Membros •Carlos Eduardo •Eugênio •Everton •Mônica
RELATÓRIO TP 3
A divisão da documentação do Trabalho Prático #3 foi feita em três partes: pela do diário de bordo abordando a montagem do robô, sobre a base teórica sendo aplicada e a conclusão.
• Diário de bordo
Nosso grupo se reuniu semanalmente para pensar e montar como seria a parte mecânica e a parte lógica de programação do robô e dos sensores. Visando garantir manobrabilidade ao sistema, o robô foi desenvolvido em triciclo, com duas rodas de tração traseira independentes e um rolon fixado pelo tipo padrão com cola quente na dianteira.
De forma a evitar flutuações abruptas de velocidade e saídas desejáveis de torque, o sistema de transmissão conta com uma cadeia de engrenagens de diferentes diâmetros na saída de cada motor.
Para controlar a velocidade dos dois motores de maneira que um motor acompanhe o outro, os encoders foram fixados junto as engrenagens para girar de maneira equivalente às rodas. Encoders Rotativos foram comprados por conta da falta de equipamentos necessários para a montagem, em Belo Horizonte, do especificado no enunciado do TP3.
Os encoders efetuam a leitura do deslocamento de um ponto arbitrário qualquer na engrenagem que os liga às suas respectivas cadeias de engrenagens, periodicamente. O objetivo é igualar esses deslocamentos enquanto o robô deve estar andando em linha reta. Para tal, compensações na potência do motor mais devagar são efetuadas, ou redução na potência do motor mais rápido, dependendo da situação (uma janela de velocidades aceitáveis foi arbitrariamente determinada).
#include <RotaryEncoder.h>
RotaryEncoder* encoderLeft = new RotaryEncoder(A8, A9);
RotaryEncoder* encoderRight = new RotaryEncoder(A13, A12);
(…)
int delayAndRead(int delayMs, RotaryEncoder* encoder1, RotaryEncoder* encoder2){
int current = 0;
encoder1->tick();
encoder2->tick();
for(current = 0; current < delayMs; current+= 10){
encoder1->tick();
encoder2->tick();
delay(10);
}
return encoder1->getPosition() - encoder2->getPosition();
}
—- Função delayAndRead
A biblioteca RotaryEncoder para Arduino foi utilizada já que ela é compatível com o hardware dos encoders utilizados e foi recomendada, como exemplo, na página do fabricante ( Biblioteca e exemplos em: https://github.com/mathertel/RotaryEncoder ).
As ocorrências da função delay(uint16_t), padrão do Arduino, no nosso código, foram substituídas por uma criada por nós, chamada delayAndRead(int, RotaryEncoder*, RotaryEncoder*), que efetua a função de pausar o fluxo de execução do programa enquanto lê e tira a diferença periodicamente do deslocamento entre cada encoder, retornando-a. A nova função divide o tempo de pausa desejado em instantes menores de 10ms para atualizar a leitura dos encoders, acessados no código através de objetos de uma classe definida por uma biblioteca de terceiros.
Todas as tarefas que envolviam o traço de uma linha reta chamam uma função chamada straightLine(), que leva em conta as leituras das diferenças desses encoders para efetuar o controle da potência de cada motor. A função straightLine() respeita uma janela de velocidade arbitrariamente definida pelo gurpo, escolhendo aumentar ou reduzir a potência de um motor de acordo com a janela e o outro motor. Leituras de deslocamento e operações de controle são feitas a cada 500ms até atingir o tempo de traçado da reta desejado.
void straightLine(int maxTime, int mode){
left->setMode(mode);
right->setMode(mode);
left->setSpeed(leftSpeed);
right->setSpeed(rightSpeed);
int difference = 0;
for(int cur = 0; cur < maxTime; cur += 500){
ledPrint(lcd, 0, 0, (String(encoderLeft->getPosition()) + String(" ") + String(encoderRight->getPosition())).c_str());
difference = delayAndRead(500, encoderLeft, encoderRight);
if(difference > 0){
if(rightSpeed >= BASESPEED && rightSpeed < MAXSPEED){
rightSpeed = rightSpeed + difference;
}
if(rightSpeed >= MAXSPEED){
leftSpeed -= difference;
}
}else{
if(leftSpeed >= BASESPEED && leftSpeed < MAXSPEED){
leftSpeed = leftSpeed + difference;
}
if(leftSpeed >= MAXSPEED){
rightSpeed -= difference;
}
}
if(leftSpeed < BASESPEED) leftSpeed = BASESPEED;
if(leftSpeed > MAXSPEED) leftSpeed = MAXSPEED;
if(rightSpeed < BASESPEED) rightSpeed = BASESPEED;
if(rightSpeed > MAXSPEED) rightSpeed = MAXSPEED;
if(rightSpeed == MAXSPEED && leftSpeed == MAXSPEED){
rightSpeed = BASESPEED;
leftSpeed = BASESPEED;
}
}
}
—- Função de desenhar linha reta e controle
O código do TP3 utilizou o mesmo sistema de menu e semi-modularização descrito na nossa documentação do TP2, com a adição de novas rotinas (como configurar tamanho de uma reta, achar luz e andar seguindo linhas pretas numa superfície branca). Também foi reutilizada nossa classe Motor, que define um motor e operações básicas para serem efetuadas no mesmo.
As rotinas de traço de linha reta, triângulo e quadrado foram reconfiguradas para utilizarem a função straightLine(), além de poderem ter suas retas de tamanho arbitrário configurável por um item no menu.
Trabalhando em paralelo a essa montagem, o grupo fez a montagem dos sensores para que eles fossem colocados corretamente no robô. Os ópticos reflexivos foram mais trabalhosos, pois demandam mais atenção para verificar se ambos os lados estão alinhados com os filtros de suas respectivas lâmpadas.
Usamos novos 4 sensores LDR, 2 para verificar se o robô estava andando em linha reta na parte da frente posicionados para baixo e os outros para fazer o alinhamento com a luz.
O controle de seguir linha e fazer curvas guiando-se por faixas pretas é possibilitado por dois sensores LDR, espaçados por uma distância maior que a linha preta a ser seguida, que detectam o branco ao redor e informam ao robô que é necessário corrigir o movimento quando se percebe que os sensores estão acima da linha. Um threshold obtido empiricamente foi definido como valor máximo detectável pelos sensores para uma luz refletida por superfícies considerada preta, tudo acima seria branco. O robô obteve resultados muito satisfatórios com essa configuração.
boolean isBlack(int value){
return value <= 300;
}
(…)
void initFollowBlackLine(){
left->setMode(FORWARD);
right->setMode(FORWARD);
left->motor->setSpeed(BASESPEED);
right->motor->setSpeed(BASESPEED);
leftSpeed = BASESPEED;
rightSpeed = BASESPEED;
}
void followBlackLine(){
if(reset){
initFollowBlackLine();
reset = false;
}
if(isBlack(analogRead(SENSORLEFT))){
Motor::curveForward(left, right, BASESPEED, 500);
encoderLeft->setPosition(0);
encoderRight->setPosition(0);
}else if(isBlack(analogRead(SENSORRIGHT))){
Motor::curveForward(right, left, BASESPEED, 500);
encoderLeft->setPosition(0);
encoderRight->setPosition(0);
}else{
straightLine(500,FORWARD);
}
}
—- Código do módulo de seguir faixas pretas
Utilizando censores LDR e filtros polarizadores de luz (perpendiculares entre si), o robô faz um giro de 360º para verificar qual a luz mais próxima dos seus sensores, sendo que colocamos um sensor óptico reflexivo na frente e outro atrás. A primeira volta mapeia os valores máximos para 40 seções do círculo. Uma segunda volta é feita para posicionar o robô na seção com o maior brilho detectado.
Durante a leitura para cada seção, são efetuadas 5 leituras no total e o que é armazenado na memória para aquela seção (uma posição num array) é a média entre esses valores. A maior média por bloco representa o bloco desejado, ao qual o robô buscara se reposicionar na segunda volta.
int lightReads[41];
void initFindLight(){
}
void loopAndCompute(int maxPos){
int backReads[5];
int frontReads[5];
right->setMode(FORWARD);
left->setMode(BACKWARD);
right->motor->setSpeed(BASESPEED);
left->motor->setSpeed(BASESPEED);
for(int loopstep = 0; loopstep < maxPos; loopstep++){
for(int now = 0; now < 5; now++){
backReads[now] = analogRead(SENSORBACK);
frontReads[now] = analogRead(SENSORFRONT);
delayAndRead(50, encoderLeft, encoderRight);
}
int accuBack = 0;
int accuFront = 0;
for(int now = 0; now < 5; now++){
accuBack += backReads[now];
accuFront += frontReads[now];
delayAndRead(50, encoderLeft, encoderRight);
}
accuBack = accuBack / 5;
accuFront = accuFront / 5;
lightReads[loopstep] = accuBack > accuFront ? accuBack : accuFront;
}
}
void findLight(){
if(reset){
initFindLight();
reset = false;
}
loopAndCompute(41);
right->setMode(RELEASE);
left->setMode(RELEASE);
right->motor->setSpeed(0);
left->motor->setSpeed(0);
int maximum = -1;
int pivot = 0;
for(int index = 0; index <= 40; index++){
if(lightReads[index] > maximum){
maximum = lightReads[index];
pivot = index;
}
}
loopAndCompute(pivot + 1);
if((pivot - 1 >= 0 && lightReads[pivot - 1] > lightReads[pivot]) ||
(lightReads[pivot] > lightReads[pivot + 1] && pivot + 1 < 41)){
right->setMode(BACKWARD);
left->setMode(FORWARD);
right->motor->setSpeed(BASESPEED);
left->motor->setSpeed(BASESPEED);
delayAndRead(900, encoderLeft, encoderRight);
}
right->setMode(RELEASE);
left->setMode(RELEASE);
right->motor->setSpeed(0);
left->motor->setSpeed(0);
programState = END;
}
—- Código da leitura por luz mais próxima
Após isso, iniciamos o reforço e montagem final de todo o circuito, integrando ele com a estrutura móvel já montada. Foi necessário soldar e depois encapsular com as borrachas termo retráteis para garantir segurança.
• Base teórica
Primeiramente, decidimos colocar a roda esférica fixa no sistema pelo fato da roda castor ter apresentado muita instabilidade na locomoção no trabalho prático 2. Ela permitiu mais estabilidade ao robô por não oferecer tanta resistência contra movimentos laterais, como a roda castor faz dependendo de sua configuração momentânea.
A locomoção do nosso robô é controlada por dois encoders rotatórios conectados às cadeias de engrenagens que transferem torque partindo de cada motor à sua respectiva roda.
Sabe-se os encoders são classificados como proprioceptivos, isto é, medem valores internos ao sistema. Para aplicá-los ao sistema, fizemos eles rotacionarem representando o deslocamento de um ponto arbitrário qualquer da engrenagem que o conecta a seu motor. Nossa estratégia foi tentar igualar, a cada intervalo de leitura.
Os LDR para detectar a luz mais próxima estavam atrás de polarizadores de luz, para filtrar somente a luz de seu respectivo polo (conjunto de duas lâmpadas na mesa), esses polarizados de maneira perpendicular. As ondas de luz, ao passar por uma superfície com esse filtro de polarização, são filtradas de maneira que somente as ondas com a orientação do filtro conseguem passar através.
Um polo da mesa emitindo luz numa orientação determinada não consegue ser detectado pelo LDR por trás do filtro perpendicular aquele do polo. O filtro equivalente filtra algum ruído do ambiente e deixa passar somente as ondas na mesma orientação, incluindo as emitidas pelo polo.
• Conclusão
Conseguimos ver na prática que a escolha de qualquer tipo de montagem envolve escolhas, as quais envolvem benefícios e malefícios. Melhoramos a rotação das peças graças às cadeias de engrenagens melhores posicionadas e montadas.
Os encoders são peças fundamentais para o controle do robô, o qual foi feito de maneira a compensar um motor por outro.
O controle de seguir linha é possibilitado por dois sensores LDR, espaçados por uma distância maior que a linha preta a ser seguida, que detectam o branco ao redor e informam ao robô que é necessário corrigir o movimento quando se percebe que os sensores estão acima da linha
Quanto a luz mais próxima, o robô escaneia o ambiente inteiro através de um giro e depois posiciona-se na sessão com o maior brilho encontrado. Em concomitância a isso, é válido concluir que o menu foi de fácil acesso aos usuários e eles poderiam fazer a escolha de qualquer medição.
Vídeo
https://www.youtube.com/watch?v=BhpN4EMBAJs
O trabalho final de robótica da disciplina consiste em projetar e construir um robô autônomo capaz de se localizar, transportar objetos num circuito e retornar à uma base inicial. A metáfora do enunciado consiste num incêndio, onde os objetos a serem salvos são vítimas, a serem discriminadas pelo grau de emergência de sua situação. O robô seria então, um bombeiro brigadista, socorrendo vítimas e levando-as a um lugar seguro. Neste trabalho final é demonstrado a combinação dos aprendizados de todos os trabalhos passados. O robô final utiliza múltiplos sensores para localização e entrada em suas tomadas de decisões momentâneas, controla sua velocidade, posição e orientação, navega em um ambiente semi estruturado, coleta objetos e reproduz uma estratégia para tal.
As especificações completas do cenário se encontram na página da disciplina. ( http://homepages.dcc.ufmg.br/~doug/cursos/doku.php?id=cursos:introrobotica:2018-1:competicao ). Aqui, explicaremos a nossa estratégia para o problema já definido. Nosso robô não discrimina os blocos por cor, escolha feita tanto por causa da simplificação, por causa da limitação de tempo e também pela limitação do número de portas analógicas disponíveis na placa do Arduino. A estratégia adotada foi seguir o circuito de faixas pretas, após alinhar-se com as luzes polarizadas, até percorrer os dois primeiros quadrantes próximos à base, coletando os blocos em cima da pista.
O robô percorre os dois primeiros quadrantes próximos à base, potencialmente coletando 6 blocos.
O robô é composto de 2 LDRs com filtros polarizadores (para detectar as luzes da base conforme especificação) na parte traseira, 1 LDR frontal (para detectar as mesmas lâmpadas, mas sem discriminá-las, na hora de voltar para a base), 2 LDRs e um LED branco de alta intensidade no circuito responsável por sensoriar a linha preta e mais um LDR embaixo do robô para detectar a luz de largada. As garras são fixas e delimitam uma área onde os blocos ficarão para sem arrastados para a base.

Partes traseira e frontal do robô
Os sensores LDR atrás de filtros polarizados se encontram na parte traseira do robô, assim, quando esses encontram a luz, ele se alinha com ela de tal maneira que a parte frontal já está livre para avançar (o robô fica de frente para o circuito e de costas para as lâmpadas). O robô percorre o primeiro quadrante, vira para a linha que o guia de volta à base, detecta as lâmpadas de luz na base, segue em linha reta, entrega os blocos, faz uma ré, gira, e volta para limpar o segundo quadrante. Quando o robô detecta as lâmpadas de luz polarizadas com o LDR frontal, significa que ele está virado para a base, logo ele segue em linha reta ignorando a linha preta e executa a manobra (ré e giro), voltando para o circuito. Não há controle de diferenças entre os dois motores DC, as retas do robô são guiadas pelas faixas pretas e outras retas curtas satisfatórias foram obtidas e confirmadas com testes no laboratório.
O robô do trabalho prático 3 não foi reutilizado e foi desmontando. Visando garantir manobrabilidade ao sistema, o novo robô foi desenvolvido também em triciclo, com duas rodas de tração traseira independentes e um roll-on fixado com cola quente na dianteira. Há um motor para cada eixo nos eixos traseiros, além de duas rodas (do kit do lego) para aumentar a estabilidade do robô, inteirando 4 rodas usadas no total. Abaixo, uma imagem do robô final:
De forma a evitar flutuações abruptas de velocidade e boas saídas de torque, o sistema de transmissão conta com algumas reduções antes do movimento chegar às rodas, diminuindo a velocidade e aumentando o torque. Os motores utilizados também já possuem uma caixa de transmissão amarela que fazem um trabalho de redução semelhante inicial, mas ainda não adequado às configurações das nossas rodas. Para seguir um caminho fixado no trajeto, o robô possui dois sensores LDR que são posicionados de forma a serem espaçados por uma distância maior que a linha preta a ser seguida, que detectam o branco ao redor e informam ao robô que é necessário corrigir o movimento quando se percebe que os sensores estão acima da linha. Há também um LED branco de alta intensidade do meio dos LDRs, para iluminar a parte de baixo do robô e aumentar a diferença na detecção de uma superfície branca e preta (mesa e fita). Em relação à largada, foi instalado um LDR para o robô detectar a luz de início. O sensor foi colocado na parte inferior.
Parte inferior, LDR de largada atrás do roll-on, LDRs seguidores de linha e LED visíveis
Para a competição o robô também deveria se posicionar com as luzes polarizadas, após a largada, e para esse fim, utilizamos dois LDRs polarizados: um está com um filtro perpendicular ao outro, os dois lado a lado, computa-se a diferença entre a detecção desses num dado momento para achar e alinhar o robô com a luz polarizada. Um LDR frontal também foi instalado para detectar as lâmpadas polarizadas no caminho de volta (já que os LDRs de posicionamento estão atrás). Não é necessário discriminá-las pois as duas juntas são a maior fonte de luz no ambiente, na volta sendo utilizadas para localizar a base. Finalizamos a montagem de todo o circuito, integrando ele com a estrutura móvel já montada. Foi necessário soldar e depois encapsular com as borrachas termo-retráteis para garantir segurança.
O código do projeto final reaproveitou os módulos de motor, seguir circuito de fita preta e detectar luz polarizada com alguns ajustes. O estado inicial do robô é mostrar uma mensagem que pede para que esse seja posicionado na luz de largada e que o usuário pressione qualquer botão (que não seja o reset). Todos os atuadores e LED estão desligados. Para medir os sessenta segundos, a função delay(time) foi substituída por delayAndRegister(time), que executa a primeira e registra o tempo passado por parâmetro de maneira a incrementar um contador global. Quando esse atinge 60, o loop responsável por percorrer o circuito para e um procedimento chamado shutdown() é acionado, que desliga os motores e o LED e retorna o robô ao estado inicial. Uma mudança significativa em relação ao trabalho anterior foi o algoritmo de encontrar as duas luzes polarizadas. Primeiramente, o robô fazia uma volta inteira para sensoriar o ambiente e medir em qual direção estaria a maior diferença entre os dois LDRs atrás de filtros polarizadores, para na segunda semi-volta se posicionar apontando para essa direção. Posteriormente, o robô girava até detectar que a diferença entre os dois LDRs estava aumentando e após atingir um determinado valor, parava imediatamente. Por fim, foi desenvolvido um algoritmo que dita que o robô deve manter-se girando até encontrar uma sequência de aumentos na diferença entre os valores dos LDRs, em relação ao sensoriamento imediatamente anterior, e ultrapassar um valor estimado como média que representa a diferença entre os LDRs quando o robô está apontado para as lâmpadas, para então atingir o estado de ajuste mais preciso. O estado de ajuste mais preciso representa, inicialmente, um momento em que o robô já está aproximadamente alinhado com as lâmpadas, precisando apenas de retoques. O robô inverte o sentido de sua rotação e diminui um pouco a potência de seus motores a cada iteração, diminuindo cada vez mais o arco, até parar ou ler novamente uma sequência de aumentos nas diferenças entre os LEDs.
int findLight(int goodSample, int reductionFactor){
int change = true;
int back, front, now, old;
bool checkin = false;
right->setMode(FORWARD);
left->setMode(BACKWARD);
right->motor->setSpeed(CURVESPEED - reductionFactor);
left->motor->setSpeed(CURVESPEED - reductionFactor);
while(1){
statusPrint();
back = analogRead(SENSORBACK);
front = analogRead(SENSORFRONT);
now = abs(back - front);
if(now > old && !checkin){ // Tendência de aumento
checkin = true;
}else if(now > goodSample && checkin && now < old){ // Ajuste fino e fim
do{
right->setMode(change ? BACKWARD : FORWARD);
left->setMode(change ? FORWARD : BACKWARD);
right->motor->setSpeed(CURVESPEED - reductionFactor);
left->motor->setSpeed(CURVESPEED - reductionFactor);
delayAndRegister(250);
now = abs(back - front);
change = !change;
reductionFactor += 10;
} while(now < old && reductionFactor < CURVESPEED);
break; // FIM LOOP GRANDE E FUNÇÃO
} else if(now < old){ // Quebrou tendência de aumento
checkin = false;
}
old = now;
delayAndRegister(150);
}
left->stop();
right->stop();
return now;
}.
—- Código da função de alinhar com as luzes polarizadas
O código de seguir o circuito de faixas pretas só foi alterado, em relação ao anterior, para que esse parasse de ser executado por um tempo quando o robô detectar as luzes polarizadas na sua frente (significando que ele já percorreu boa parte do circuito e está agora virado para a base, lembrando que ele faz a largada com esse sensor frontal apontando para o circuito, e os LDRs polarizados, localizados na parte de trás, apontando para as luzes). O robô, então, executa o procedimento de andar reto para a base, dar ré e girar de volta ao circuito, aí voltando para a execução do trecho de seguir o circuito de faixas pretas.
O trabalho, a disciplina e a competição foram muito proveitosos para satisfazer nossa curiosidade inicial sobre a área de robótica. Foi possível aprender na prática a importância do planejamento e dos testes para a confecção de um sistema relativamente complexo, que interfaceia o mundo real analógico e o mundo digital. A teoria da disciplina se justifica na prática, à medida que é usada para resolver problemas encontrados durante a confecção e testes dos robôs. A importância do controle bem feito foi comprovada várias vezes ao longo do percurso. Projeto, execução, correções e acabamento podem ser fases executadas várias vezes durante a produção de apenas um robô. Também foi proveitoso para a nossa formação como engenheiros e cientistas confeccionar os trabalhos em equipe, vivenciando várias reuniões, troca de ideias, definição de planos, compromissos, metas, e vários testes.