Robótica

Kleerhangerbeest – o Robô Andarilho de Cabide de Arame controlado com Arduino

Postado em Atualizado em

Versão fácil para Arduino e 2 servos do Coat Hanger Walking Robot, o Robô Andarilho de Cabide de Arame originalmente desenvolvido por Jerome Demers e Gareth Branwyn.
O nome kleerhangerbeest é holandês e significa bicho-cabide, tendo sido inspirado nos robôs eólicos chamados strandbeest (bicho-de-praia) de Theo Jansen – http://www.strandbeest.com/ .

bug1-small-heinlein

Não parece com os “insetos” de Starship Troopers?

Veja o vídeo de efeito…

O chassis é feito em Meccano/Modelix. O resto é sucata que pode ser encontrada em qualquer lugar.

bug7-small bug2-small

bug3-small bug5-small

bug4-small bug6-small

O código Processing para Arduino está em: https://github.com/awangenh/Minhas-Engenhocas/blob/master/Kleerhangerbeest2.ino

Usei um Freaduino ao invés de um Arduino para evitar ter de usar um protoboard em cima do robô. O Freaduino é excelente para coisas que usam servos e outros periféricos que ligam com um cabo sinal/VCC/GND por causa das saídas prontinhas que ele oferece para isso. O circuito é ultrasimples.:

inseto_bb

O modelo do chassis está aqui embaixo (feito com VirtualMec):

chassis-inseto

Tentei manter o chasssis o mais simples possível. Os passos de montagem são esses abaixo:

chassis-inseto1chassis-inseto2

chassis-inseto3chassis-inseto4

chassis-inseto5chassis-inseto6

A lista de peças (códigos da Meccano e não da Modelix) é essa:

Peça    Qtdidade.
5	2
11	2
11a	2
37a	3
37b	13
90a	2
193	2
811	2

Depois de encaixados os servos, você pode fixá-los no lugar usando cantoneiras em “L” (aquelas pecinhas com formato em “L” com apenas dois furos, um redondo e o outro oblongo – código de peça Meccano = 12) fixadas com parafusos nos dois furos inferiores mais extremos do chassis.

Referências originais do Coat Hanger Walking Robot:
http://www.instructables.com/id/How-to-build-the-one-motor-walker/
http://www.technogumbo.com/projects/single-servo-walking-robot/
https://www.youtube.com/watch?v=bxxmN2sQvjk
https://solarbotics.com/product/abgchw/
https://www.youtube.com/watch?v=L984o5mT9HI (com motor de rotacao continua)

O código Processing para Arduino está em: https://github.com/awangenh/Minhas-Engenhocas/blob/master/Kleerhangerbeest2.ino

Braço-Robô com 3 Graus de Liberdade programado em Scratch S4A utilizando Arduino e Meccano/Modelix

Postado em Atualizado em

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…

IMG_4993 (2)

Aqui o programa no dialeto S4A (Scratch for Arduino) da Linguagem de Programação Visual Scratch originalmente desenvolvida pelo MIT:

AE_swf16713054

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:

medium-6 medium-7 medium-9

medium-8 medium-10

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.

Braço-Robo-3GL_bb

Mecanismo de Caminhada de Theo Jansen em Meccano/Modelix

Postado em Atualizado em

Sempre quis montar um Strandbeest (http://www.strandbeest.com/) de Theo Jansen usando Meccano (ou Modelix).

Jansen13c Jansen13d

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

Postado em Atualizado em

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.

carro-robo

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.

IMG_4985  IMG_4988  IMG_4991 (2)

Esquema de conexões do Arduino:

Carro-Meccano-bak_bb

 

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();
    }
}