ㅤPara o primeiro trabalho prático foram propostos dois problemas.
ㅤO primeiro consistia na criação de um robô tipo manipulador que fosse capaz de seguir o caminho da Fig. 1, onde a região sombreada é o local para situar-se a base do robô.
ㅤO segundo problema também consistia em criar um robô tipo manipulador, porém, que fosse capaz de escrever UFMG na maior área da seção demarcada, em branco, da Fig. 2, e que sua base estivesse na região demarcada por pontos.
ㅤO grupo decidiu por resolver o segundo problema: criar o manipulador que escrevesse UFMG.
ㅤPara realização da disciplina o grupo decidiu por trabalhar com o Arduino Mega 2560. Para isso, foi emprestado pelo professor uma ADAFRUIT MOTOR SHIELD que faz o controle de mais de um motor simultaneamente, o que é necessário para a realização da tarefa, e uma SHIELD LCD 16X2 COM KEYPAD (tela e teclado simples). Por fim a lista de componentes necessários para a construção do manipulador foi a seguinte:
ㅤPara a realização do trabalho a ideia inicial do grupo era a construção de um manipulador elétrico do tipo PP (2 juntas prismáticas). Assim, nosso robô teria dois graus de liberdade, pois se movimentaria em duas direções independentes.
ㅤInicialmente, a base do robô era constituída por uma peça fixa e uma “perna” com roda fixada no Elo y, mas percebemos que bastava a base fixa, pois o Elo estava bem equilibrado.
ㅤOs Elos são trilhos. O primeiro elo é um trilho é fixo à base, e sobre ele corre um trem com o motor (M1) que o movimenta na direção direita-esquerda. Ligado ao trem está o trilho móvel que é o segundo elo, neste está o Efetuador. O trilho móvel se movimenta na direção frente-trás por meio de um motor (M2) também fixo ao trem.
Para que pudéssemos controlar a movimentação dos elos, nas duas Juntas construímos caixas de redução de 275:1.
ㅤO Efetuador consiste em uma caixa para que a caneta seja simplesmente encaixada e esteja livre. Desse modo podemos remover a caneta e substitui-la por outra, caso a tinta acabe, ou queiramos escrever de outra cor.
ㅤA Workspace do robô é um retângulo maior que o espaço destinado à escrita de UFMG, por causa do tamanho dos trilhos usados. Como não podemos usar nenhum tipo de sensor, definir a posição máxima que o robô pode alcançar não era nossa preocupação inicial.
ㅤO Controlador é constituído por uma caixa para pilhas (unidade de potência) e um Arduino Mega 2560 (unidade de cálculo e programação).
ㅤPara que pudéssemos usar as Shields disponibilizadas usamos as bibliotecas Adafruit_MotorShield.h e LiquidCrystal.h. Detalhes sobre o funcionamento da linguagem e algumas ferramentas utilizadas na programação de um Arduino podem ser encontrados aqui.
ㅤNo código, primeiro iniciamos incluindo as bibliotecas:
#include <Wire.h> #include <Adafruit_MotorShield.h> #include "utility/Adafruit_MS_PWMServoDriver.h" #include <LiquidCrystal.h> #include <time.h> #include <math.h>
ㅤAgora, vamos definir no código as placas (Shields) e os motores para podermos chamá-los durante a execução das tarefas.
LiquidCrystal lcd(8, 9, 4, 5, 6, 7); //Shield do LDC: significa que ela vai usar os pinos 8, 9, 4, 5, 6, e 7 Adafruit_MotorShield AFMS = Adafruit_MotorShield(); //Shield da ponte H (onde os motores são ligados) Adafruit_DCMotor *M1 = AFMS.getMotor(4); //o motor1 se chama M1 e está na saida 4 da shield da ponte H Adafruit_DCMotor *M2 = AFMS.getMotor(2); //o motor2 se chama M2 e está na saida 2 da shield da ponte H
ㅤDefiniremos as variáveis dos botões da Shield LCD e a função que lê os botões.
//agora vamos definir as variáveis dos botões::: int lcd_key = 0; int adc_key_in = 0; #define btnRIGHT 0 #define btnUP 1 #define btnDOWN 2 #define btnLEFT 3 #define btnSELECT 4 #define btnNONE 5 int read_LCD_buttons(){ adc_key_in = analogRead(0); // read the value from the sensor // my buttons when read are centered at these valies: 0, 144, 329, 504, 741 // we add approx 50 to those values and check to see if we are close if (adc_key_in > 1000) return btnNONE; // We make this the 1st option for speed reasons since it will be the most likely result if (adc_key_in < 50) return btnRIGHT; if (adc_key_in < 195) return btnUP; if (adc_key_in < 380) return btnDOWN; if (adc_key_in < 555) return btnLEFT; if (adc_key_in < 790) return btnSELECT; return btnNONE; // when all others fail, return this... }
ㅤNão se esqueça das variáveis auxiliares.
int i=1;
ㅤAntes de começar a escrever UFMG, precisamos mover a caneta para a posição que queremos, assim, vamos definir a função calibrarManual(), ela faz os motores girarem usando os botões do LCD. Uma facilidade de usar o Arduino é a possibilidade de usar “switch case”, o que não é aceito na Protoboard do Lego.
void calibrarManual(){//nessa função ao pressionar os botoes o motor gira para o lado selecionado e ao soltar o motor para. Para sair use SELECT ou RST. while(i==1){ i=1; //testar com setSpeed antes M1->setSpeed(255); // velocidade do motor entre 0 (desligado) e 255 (ligado) M2->setSpeed(255); // velocidade do motor entre 0 (desligado) e 255 (ligado) lcd_key = read_LCD_buttons(); //read_LCD_buttons() é a função que retorna o que é apertado switch(lcd_key){ //lcd_key é a qual botão está sendo apertado: as opões estão nos cases::: case btnRIGHT: { //M1->setSpeed(200); // velocidade do motor entre 0 (desligado) e 255 (ligado) M1->run(FORWARD);//FORWARD (->), BACKWARD(<-) or RELEASE (PARA). fazendo o M1 girar para direita lcd.clear(); //limpa do LCD lcd.setCursor(0, 0); //COLOCA O CURSOR NA PRIMEIRA POSIÇÃO E NA PRIMEIRA LINHA lcd.print("Direita"); //IMPRIME Direita E DEPOIS COLOCA O CURSOR NO PRÓXIMO QUADRADO. break;} case btnLEFT: { //M1->setSpeed(200); // velocidade do motor entre 0 (desligado) e 255 (ligado) M1->run(BACKWARD);//FORWARD (->), BACKWARD(<-) or RELEASE (PARA). lcd.clear();//limpa do LCD lcd.setCursor(0, 0); lcd.print("Esquerda"); break;} case btnUP: { //M2->setSpeed(200); // velocidade do motor entre 0 (desligado) e 255 (ligado) M2->run(FORWARD);//FORWARD (->), BACKWARD(<-) or RELEASE (?). lcd.clear();//limpa do LCD lcd.setCursor(0, 0); lcd.print("Frente"); break;} case btnDOWN: { //M2->setSpeed(200); // velocidade do motor entre 0 (desligado) e 255 (ligado) M2->run(BACKWARD);//FORWARD (->), BACKWARD(<-) or RELEASE (PARA). lcd.clear();//limpa do LCD lcd.setCursor(0, 0); lcd.print("Tras"); break;} case btnNONE: { //Isso significa que quando os botões não estão sendo apertados os motores desligam lcd.clear();//limpa do LCD //M1->setSpeed(0); // velocidade do motor entre 0 (desligado) e 255 (ligado) //M1->setSpeed(0); // velocidade do motor entre 0 (desligado) e 255 (ligado) M1->run(RELEASE); M2->run(RELEASE); break;} case btnSELECT: { i=0; //faz sair da função lcd.clear(); //limpa do LCD lcd.setCursor(0, 0); lcd.print("Sair"); delay(3000); lcd.clear(); lcd_key=btnNONE; //evita que o arduino execute a ação escrever automáticamente. lcd.setCursor(0, 0); lcd.print("UP: CalibrarManual"); lcd.setCursor(0, 1); lcd.print("SELECT: Iniciar"); break;}} } }
ㅤA função que faz o robô escrever UFMG é essa:
void escrever(){//M1 direita esquerda, M2 frente trás :::::FORWARD (->), BACKWARD(<-) or RELEASE (STOP). M1->setSpeed(135); // velocidade do motor entre 0 (desligado) e 255 (ligado)(valor padrao-115,se precisar 112) M2->setSpeed(150); // velocidade do motor entre 0 (desligado) e 255 (ligado) M2->run(BACKWARD); delay(k*6500); M1->run(FORWARD); M2->setSpeed(255); delay(300); M2->setSpeed(200); delay(300); M2->setSpeed(155); delay(300); M2->setSpeed(100); delay(300); M2->setSpeed(75); delay(300); M1->run(RELEASE); M2->run(RELEASE); delay(500); M1->run(FORWARD); M2->run(FORWARD); M2->setSpeed(75); delay(300); M2->setSpeed(100); delay(300); M2->setSpeed(155); delay(300); M2->setSpeed(200); delay(300); M2->setSpeed(255); delay(300); M1->run(RELEASE); M2->setSpeed(145); M2->run(FORWARD); delay(k*6500); M2->run(RELEASE); delay(1000); //SOBE lcd.setCursor(0, 0); lcd.print("U"); //depois de escrever o cursor move para a direita M1->run(FORWARD); delay(k*3120); M1->run(RELEASE);delay(1000); //VAI M1->run(BACKWARD); delay(k*2800); M1->run(RELEASE);delay(1000); //VOLTA M2->run(BACKWARD); delay(k*3500); M2->run(RELEASE);delay(1000); //DESCE M1->run(FORWARD); delay(k*2680); M1->run(RELEASE);delay(1000); //VAI M1->run(BACKWARD); delay(k*2800); M1->run(RELEASE);delay(1000); //VOLTA M2->run(BACKWARD); delay(k*4500); M2->run(RELEASE);delay(1000); //DESCE M2->run(FORWARD); delay(k*8200); M2->run(RELEASE);delay(1000); //SOBE lcd.print("F"); //escreve o F ao lado do U M1->run(FORWARD); delay(k*3120); M1->run(RELEASE);delay(1000); //VAI M2->run(BACKWARD); delay(k*8000); M2->run(RELEASE);delay(1000); //DESCE M2->run(FORWARD); delay(k*8215); M2->run(RELEASE);delay(1000); //SOBE M1->setSpeed(133); // velocidade do motor entre 0 (desligado) e 255 (ligado)(valor padrao-115,se precisar 112) M2->setSpeed(200); // velocidade do motor entre 0 (desligado) e 255 (ligado) M2->run(BACKWARD); M1->run(FORWARD); delay(k*1600); M2->run(RELEASE); M1->run(RELEASE); delay(1000); //nesse caso estamos girando os dois motores ao mesmo tempo (DESCE+VAI) M2->run(FORWARD); M1->run(FORWARD); delay(k*1600); M2->run(RELEASE); M1->run(RELEASE);delay(1000); //nesse caso estamos girando os dois motores ao mesmo tempo (SOBE+VAI) M1->setSpeed(133); // velocidade do motor entre 0 (desligado) e 255 (ligado)(valor padrao-115,se precisar 112) M2->setSpeed(150); // velocidade do motor entre 0 (desligado) e 255 (ligado) M2->run(BACKWARD); delay(k*8000); M2->run(RELEASE);delay(1000); //DESCE M2->run(FORWARD); delay(k*8215); M2->run(RELEASE);delay(1000); //SOBE lcd.print("M"); //escreve M ao lado do F M1->run(FORWARD); delay(k*1772); // primeira parte do G M2->run(BACKWARD); M2->setSpeed(75); delay(300); M2->setSpeed(100); delay(300); M2->setSpeed(155); delay(300); M2->setSpeed(200); delay(300); M2->setSpeed(255); delay(300); M1->run(RELEASE); M2->run(RELEASE); delay(300); M2->run(BACKWARD); delay(1250); M2->run(FORWARD); delay(1250); //segunda parte do G M1->run(BACKWARD); M2->run(FORWARD); M2->setSpeed(255); delay(300); M2->setSpeed(200); delay(300); M2->setSpeed(155); delay(300); M2->setSpeed(100); delay(300); M2->setSpeed(75); delay(300); M1->run(RELEASE); M2->run(RELEASE); delay(300); //terceira parte do G M1->run(BACKWARD); M2->run(BACKWARD); M2->setSpeed(75); delay(300); M2->setSpeed(100); delay(300); M2->setSpeed(155); delay(300); M2->setSpeed(200); delay(300); M2->setSpeed(255); delay(300); M1->run(RELEASE); //quarta parte do G M2->setSpeed(145); delay(5000); //quinta parte do G M1->run(FORWARD); M2->setSpeed(255); delay(300); M2->setSpeed(200); delay(300); M2->setSpeed(155); delay(300); M2->setSpeed(100); delay(300); M2->setSpeed(75); delay(300); M1->run(RELEASE); M2->run(RELEASE); delay(300); // sexta parte do G M2->run(FORWARD); M1->run(FORWARD); M2->setSpeed(75); delay(300); M2->setSpeed(100); delay(300); M2->setSpeed(155); delay(300); M2->setSpeed(200); delay(300); M2->setSpeed(255); delay(300); M1->run(RELEASE); M2->setSpeed(145); M2->run(FORWARD); delay(1500); M2->run(RELEASE); M1->run(FORWARD); delay(k*480); M1->run(RELEASE);delay(1000); //VAI M1->run(BACKWARD); delay(k*1520); M1->run(RELEASE);delay(1000); //VOLTA // fim do G lcd.print("G"); //escreve o G ao lado do M M1->setSpeed(0); // velocidade do motor entre 0 (desligado) e 255 (ligado) M2->setSpeed(0); //ou seja, estamos "desligando" os dois motores }
ㅤComo vocês sabem, pra tudo funcionar precisamos ainda do void setup() e do void loop(). O setup() é tipo o int main, é o que vai ser executado uma única vez e é o que é primeiramente executado quando o Arduino é ligado ou reiniciado. Essa função é obrigatória para que o código compile.
void setup() { lcd.begin(16, 2); //inicializa o LCD AFMS.begin(); //Inicializa os motores lcd.clear(); //limpa a tela lcd.setCursor(0, 0); //coloca o cursor na linha 0 e lcd.print("UP: CalibrarManual"); lcd.setCursor(0, 1); lcd.print("SELECT: Iniciar"); }
ㅤAgora, para finalizar, temos o void loop(), que de fato é um loop, infinito. Essa função é obrigatória, o código não compila sem ele, se você for criar alguma coisa que não necessita de um loop, basta deixá-lo em branco: void loop(){}. No nosso código, o void loop, é o que recebe os comandos que chamam as funções calibrarManual() e escrever(). Ao terminar de escrever o programa volta ao modo 'esperando ação'.
void loop() { lcd_key = read_LCD_buttons(); //lendo os botões if(lcd_key==btnUP){ //se "para cima" calibrarManual();} //chama a função calibrar [void calibrarManual()] if(lcd_key==btnSELECT){ //ao apertar SELECT lcd.clear(); //limpa a tela lcd.setCursor(1, 1); //posiciona o cursor na segunda linha e segunda posição lcd.print("INICIANDO..."); //escreve INICIANDO... escrever();} // e chama a função escrever [void escrever()] }
Inicialmente decidimos escrever as letras apenas com linhas retas, por ser fácil de programar e o movimento simples. No vídeo abaixo podemos observar o robô escrevendo a sigla UFMG utilizando esse método.
Com a palavra UFMG com linhas retas feitas, foi feito o controle para as letras U e G no qual elas apresentassem um formato mais arredondado.Nos três vídeos abaixo vemos 3 vistas diferentes do robô escrevendo a palavra UFMG.
| Vídeo 1: | Vídeo 2: | Vídeo 3: |
|---|---|---|