Mês: abril 2014
Braço-Robô com 3 Graus de Liberdade programado em Scratch S4A utilizando Arduino e Meccano/Modelix
Um braço-robô com 3 graus de liberdade foi uma coisa que sempre me fascinou, inclusive pela sua suposta complexidade. Pensei e resolvi construir um que pudesse ser programado por qualquer criança usando a linguagem de programação visual para crianças Scratch. Aqui está o resultado.
Usei um Arduino Uno R3 com Firmata modificado para S4A e dois servos TowerPro SG90 para o manipulador e a 3ª junta e dois servos TowerPro 5010, mais fortes, para a 1ª e 2ª juntas. Os contrapesos são luvas de latão 3/4″ com rosca. Antes que eu esqueça: Tem um dirty hack no S4A: mudei o código Smalltalk e o código do Firmata para supoprtar 4 servos ao invés de dois…
Estou pensando em transportar para Snap! Scratch usando o servidor de comunicação s2a_fm de Alan Yorinks ao invés do S4A (Scratch for Arduino), que é bem limitado e só roda em Windows. Mas isso será outro post…
Aqui o programa no dialeto S4A (Scratch for Arduino) da Linguagem de Programação Visual Scratch originalmente desenvolvida pelo MIT:
Fiz um vídeo para mostrar algumas coisas:
O braço-robô foi construído utilizando-se peças de vários kits-clone de Meccano, incluindo-se aí peças originais Meccano vintage inglesas da década de 1940-50, como também peças do clone brasileiro Modelix. Abaixo algumas imagens:
Para quem quiser refazer, aqui o esquema, detalhando quais servos em cada saída digital. Lembre-se, o S4AFirmware.ino original só suporta servos nos pinos 11 e 12. Tem que modificar.
Microcontrolled Action Shooting Bike Helmet (Versão 0.1 Pré-Alfa)
Peguei a ideia de Jake Spurlock daqui http://makezine.com/projects/make-38-cameras-and-av/gopro-swivel-camera-mount/ e automatizei o processo de varredura para a captura.
Neste post a versão pré-alfa da Engenhoca. Vamos melhorar….
Aqui o detalhe do mecanismo de movimentação e da eletrônica de automação. Nesta versão o mecanismo de suporte é fixado diretamente ao eixo de nylon do servomotor. Para passear na Beira-Mar num domingo de manhã, como no filminho abaixo, é adequado. Se usar para MTB vai quebrar…
Veja aqui um vídeo…
Aqui o código Processing em um Sketch para o Arduino. É uma pequena variação do exemplo-padrão Sweep.ino.
SweepCamera.ino – Código em Processing para Arduino |
---|
#include Servo myservo; // create servo object to control a servo // a maximum of eight servo objects can be created int pos = 0; // variable to store the servo position void setup() { myservo.attach(9); // attaches the servo on pin 9 to the servo object } void loop() { myservo.write(pos); // tell servo to go to position in variable 'pos' delay(500); // added - stop at sideview before start sweeping for(pos = 0; pos < 180; pos += 1) // goes from 0 degrees to 180 degrees { // in steps of 1 degree myservo.write(pos); // tell servo to go to position in variable 'pos' delay(20); // waits 15ms for the servo to reach the position // changed to 20ms } delay(500); // added - stop at sideview before start sweeping for(pos = 180; pos>=1; pos-=1) // goes from 180 degrees to 0 degrees { myservo.write(pos); // tell servo to go to position in variable 'pos' delay(20); // waits 15ms for the servo to reach the position // changed to 20ms } } |
Mecanismo de Caminhada de Theo Jansen em Meccano/Modelix
Sempre quis montar um Strandbeest (http://www.strandbeest.com/) de Theo Jansen usando Meccano (ou Modelix).
Aqui há uma planilha que eu preparei contendo as relações de tamanho que você precisa de acordo com vários modelos e cálculos: planilha theo jansen.
A tabela abaixo dá um resumo (observe que há duas sugestões de tamanhos para as peças Meccano (ou Modelix)). A tabela converte duas interpretações em distâncias entre furos para peças Meccano/Modelix (1/2 polegada entre centros de furos). Para entendê-la veja a planilha detalhada no link acima. Nessa planilha o código de cores da modelagem em VirtualMec das imagens acima é mantido e fica mais fácil você identificar cada peça.
—
Orig. Hansen Sketch Label | Part Name | Ghassaei Diagram | Size Ratio (4volt) | Size (20) | Amanda Ghassaei | 1/2 Inches – Mecc. hole distances | #Holes in Meccano Part | 1/2 Inches – Mecc. hole distances | #Holes in Meccano Part |
j | Upper drive bar | (1, 2) | 100 | 20,00 | 50,00 | 14,00 | 15 | 13,00 | 14 |
k | Lower drive bar | (1, 5) | 124 | 24,80 | 61,90 | 17,36 | 18 | 16,12 | 17 |
c | Inside connector bar | (3, 5) | 79 | 15,80 | 39,30 | 11,06 | 12 | 10,27 | 11 |
f | Outside connector bar | (4, 6) | 79 | 15,80 | 39,40 | 11,06 | 12 | 10,27 | 11 |
b | Inside soulder | (2, 3) | 83 | 16,60 | 41,50 | 11,62 | 13 | 10,79 | 12 |
e | Outside shoulder | (2, 4) | 112 | 22,40 | 55,80 | 15,68 | 17 | 14,56 | 16 |
d | Bottom shoulder | (3, 4) | 80 | 16,00 | 40,10 | 11,20 | 12 | 10,40 | 11 |
g | Top of foot | (5, 6) | 73 | 14,60 | 36,70 | 10,22 | 11 | 9,49 | 10 |
i | Inside of foot | (5, 7) | 98 | 19,60 | 49,00 | 13,72 | 15 | 12,74 | 14 |
h | Outside of foot | (6, 7) | 131 | 26,20 | 65,70 | 18,34 | 19 | 17,03 | 18 |
m | Drive pin offset | (0, 1) | 30 | 6,00 | 15,00 | 4,20 | 5 | 3,90 | 5 |
a | Drive to shoulder | x | 76 | 15,20 | 38,00 | 10,64 | 12 | 9,88 | 11 |
l | Shoulder down offset | y | 16 | 3,20 | 7,80 | 2,24 | 3 | 2,08 | 3 |
Carro-Robô que desvia de Obstáculos feito com Arduino e Meccano/Modelix
Este carro encontra seu caminho usando varredura por ultra-som (sonar). O transdutor de ultra-som montado sobre um servo emite pings para a frente, como um sonar de submarino. Sempre que o carro encontra um obstáculo a 20 cm, o ultra-som faz uma varredura para a esquerda e para a direita através do acionamento do servo que suporta o ultra-som: o caminho mais desimpedido é escolhido. O segundo servo (direção!) é então acionado e o carro faz uma curva nesta direção por alguns segundos e, então, continua reto.
Um goniômetro na roda (veja parte inferior direita da imagem acima) faz as vezes de conta-giros e monitora a velocidade do carro. Se o carro estiver em uma subida ou um tereno difícil que o lentifique (grama!), a ponte-H que controla o motor aumenta a corrente para o motor de forma a que ele ande mais rápido e tenha mais força. Quando a velocidade retorna ao normal, a corrente “de cruzeiro” é restituída. Se o carro chegar perto demais de um obstáculo, ele dá marcha a ré por alguns segundos antes de escolher um novo caminho: assim ele nunca fica entalado em um canto.
Esquema de conexões do Arduino:
Código em Processing para Arduino |
---|
#include <Servo.h> Servo us_servo; // create servo object to control the ultrasound servo Servo steering_servo; // create servo object to control the steering wheel servo int pos = 0; // variable to store the servo position int inputPin=4; // connect digital I/O 4 to the ECHO/Rx Pin int outputPin=5; // connect digital I/O 5 to the TRIG/TX Pin int greenLed=3; int redLed=7; // H-bridge pins int speedPin = 11; int directionPin = 12; //int relayPin=11; int us_echo, us_echo_dir, us_echo_esq; int min_distance = 45; // Distancia de echo considerado obstáculo int max_distance = 200; // Eco a partir do qual distancia é considerada infinita int tempo_curva = 2000; // Tempo durante o qual direção fica virada em curva /* ------ Variaveis e constantes do Conta-Giros ------- */ int dtPin = 2; // Se mudar pino, TEM que mudar a interrupção abaixo int interrupcaoContaGiros = 0; // TEM que ser 0 se dtPin = 2.... unsigned long ultimaMudanca = 0; // Tempo absoluto na ultima leitura do contagiros long intervalo = 0; const int altaVelocidade = 100; // Intervalo max. entre leituras para a velocidade ser considerada alta const int baixaVelocidade = 600; // Intervalo max. entre leituras para a velocidade ser considerada baixa int velocidade; /* -------------------------------------------------------*/ /*============================================== Analog motor control functions (employs H-bridge L298N) ==============================================*/ void praFrente() { Serial.print("Full speed forward\n"); digitalWrite(directionPin, HIGH); // Set direction as forward analogWrite(speedPin, 0); // Set the speed as full } void devagarPraFrente() { //Serial.print("Half speed forward\n"); digitalWrite(directionPin, HIGH); // Set direction as forward analogWrite(speedPin, 100); // Set the speed as half } void praTras() { Serial.print("Full speed backwards\n"); digitalWrite(directionPin, LOW ); // Set direction as reverse analogWrite(speedPin, 255); // Set the speed as full speed } void devagarPraTras() { Serial.print("Half speed backwards\n"); digitalWrite(directionPin, LOW ); // Set direction as reverse analogWrite(speedPin, 160); // Set the speed as half speed } void parar() { Serial.print("Stop\n"); analogWrite(speedPin, 0); // Set the speed to stop } /*============================================== Ultrassound pinging functions ==============================================*/ int ping() { int firstEcho; int secondEcho; int thirdEcho; int distance; int distance_cm; // first echo digitalWrite(outputPin, LOW); // send low pulse for 2μs delayMicroseconds(2); digitalWrite(outputPin, HIGH); // send high pulse for 10μs delayMicroseconds(15); digitalWrite(outputPin, LOW); // back to low pulse distance = pulseIn(inputPin, HIGH); // read echo value firstEcho= distance/29/2; // in cm // Serial.print(firstEcho); delay(50); // second echo digitalWrite(outputPin, LOW); // send low pulse for 2μs delayMicroseconds(2); digitalWrite(outputPin, HIGH); // send high pulse for 10μs delayMicroseconds(15); digitalWrite(outputPin, LOW); // back to low pulse distance = pulseIn(inputPin, HIGH); // read echo value secondEcho= distance/29/2; // in cm /* Serial.print(" "); Serial.print(secondEcho); */ delay(50); // third echo digitalWrite(outputPin, LOW); // send low pulse for 2μs delayMicroseconds(2); digitalWrite(outputPin, HIGH); // send high pulse for 10μs delayMicroseconds(15); digitalWrite(outputPin, LOW); // back to low pulse distance = pulseIn(inputPin, HIGH); // read echo value thirdEcho= distance/29/2; // in cm /* Serial.print(" "); Serial.println(thirdEcho); */ delay(50); if ((firstEcho < min_distance) && (secondEcho < min_distance) && (thirdEcho < min_distance)) { return((firstEcho + secondEcho + thirdEcho) / 3); } else { return(max_distance); } } void us_direita() { us_servo.write(45); delay (500); } void us_esquerda() { us_servo.write(135); delay (500); } // ATTENTION: Steering servo is mounted inverted in relation to the US servo void steering_direita() { steering_servo.write(135); delay (500); } void steering_esquerda() { steering_servo.write(45); delay (500); } /* ================================================ */ void reageObstaculo() /* Dá marcha a ré (por via das dúvidas) Troca para luz vermelha. Avalia distancias direita e esquerda. Escolhe caminho mais livre e faz curva. ================================================ */ { // Obstaculo! // Muda cores dos LEDs de indicação de modo digitalWrite(redLed, HIGH); digitalWrite(greenLed, LOW); // Para motor parar(); // Dar a re um pouco... devagarPraTras(); delay(1000); parar(); // Executa varredura sonar... // Move servo para direita us_direita(); // Pinga eco direito us_echo_dir = ping(); // Move servo para esquerda us_esquerda(); // Pinga eco esquerdo us_echo_esq = ping(); // Reseta posicao servo US us_servo.write(90); delay (1000); if (us_echo_esq > us_echo_dir) { steering_esquerda(); } else { steering_direita(); } // Faz a curva!!! // Liga motor (se já estiver ligado, continua ligado...) // digitalWrite(relayPin, HIGH); devagarPraFrente(); delay(tempo_curva); steering_servo.write(90); // steer straight ahead delay (1000); } /* ============================================== Rotinas de propriocepção do veículo Desenvolvidas para monitorar se o veículo está andando ou se parou/encalhou... Desenvolvidas para um goniômetro Keyes KY-40, que possui 5 saídas: GND, +, SW (chaveia ao apertar eixo), DT e CLK. Em nosso código conectamos 3: -/+ e DT a dtPin. Basta para avaliar velocidade quando não se deseja saber sentido. Necessita do artificio de programação feio de usar uma variável conta-giros global... ==============================================*/ /* ============================================== */ int avaliaVelocidade() /* Retorna 0, 1 ou 2, dependendo se: 0 -> Parou 1 -> Está lento (devemos acelerar - achou grama, tapete peludo ou inclinação?) 2 -> Velocidade nominal atingida.... ==============================================*/ { intervalo = millis() - ultimaMudanca; Serial.print(intervalo); if (intervalo > altaVelocidade) { if (intervalo > baixaVelocidade) { Serial.println(" -> parou..."); return(0); } else { Serial.println(" -> lento..."); return(1); } } else { Serial.println(" -> rapido..."); return(2); } } /* ============================================== */ void reageRotacao() /* Função chamada por interrupção sempre que o goniômetro apresenta uma leitura de rotação. ============================================== */ { ultimaMudanca = millis(); } /* ============================================== */ void inicializaContaGiros() /* ============================================== */ { ultimaMudanca = millis(); pinMode(dtPin, INPUT); attachInterrupt(interrupcaoContaGiros, reageRotacao, CHANGE); // encoder pin na interrupção 0 (dtPin = pin 2) } /*============================================== Funções Auxiliares ==============================================*/ void blinkRed() { digitalWrite(redLed, HIGH); delay(160); digitalWrite(redLed, LOW); delay(160); digitalWrite(redLed, HIGH); delay(160); digitalWrite(redLed, LOW); delay(160); digitalWrite(redLed, HIGH); delay(160); digitalWrite(redLed, LOW); } void blinkGreen() { digitalWrite(greenLed, HIGH); delay(160); digitalWrite(greenLed, LOW); delay(160); digitalWrite(greenLed, HIGH); delay(160); digitalWrite(greenLed, LOW); delay(160); digitalWrite(greenLed, HIGH); delay(160); digitalWrite(greenLed, LOW); } /*============================================== Main Setup ==============================================*/ void setup() { Serial.begin(9600); pinMode(inputPin, INPUT); pinMode(outputPin, OUTPUT); pinMode(greenLed, OUTPUT); pinMode(redLed, OUTPUT); // H-bridge pins pinMode(directionPin, OUTPUT); // Set Pin directionPin as output, this is the motor direction control //pinMode(relayPin, OUTPUT); // Servos initialization us_servo.attach(10); // attaches the servo on pin 10 to the servo object that does US scans steering_servo.attach(9); // attaches the servo on pin 9 to the steering wheel servo object // Inicialização da propriocepção inicializaContaGiros(); blinkGreen(); Serial.println("Partindo..."); } /*============================================== Main Loop ==============================================*/ void loop() { us_echo = ping(); if (us_echo == max_distance) { // Freie Fahrt! digitalWrite(redLed, LOW); digitalWrite(greenLed, HIGH); // Liga motor (se já estiver ligado, continua ligado...) // digitalWrite(relayPin, HIGH); devagarPraFrente(); } else { // Obstaculo! reageObstaculo(); } velocidade = avaliaVelocidade(); if (velocidade == 1) { //acelera praFrente(); blinkGreen(); //blinkGreen(); devagarPraFrente(); } if (velocidade == 0) { blinkRed(); //marcha a ré reageObstaculo(); } } |