Manufaturação industrial
Internet das coisas industrial | Materiais industriais | Manutenção e reparo de equipamentos | Programação industrial |
home  MfgRobots >> Manufaturação industrial >  >> Manufacturing Technology >> Processo de manufatura

ASi (Anansi) Robô Companheiro: Construa um Robô Amigável com Arduino Nano e Display LED

Componentes e Materiais

Arduino Nano R3 1
Micro Servomotor SG90 1
Módulo Display Matriz LED MAX7219 Vermelho 1
Suporte para 4 Pilhas AA Uma bateria lítio-íon 3.7V 200mAh também funciona 1
Parafusos 1
Casca de Molde de Bomba de Banho em Acrílico Transparente 1
Adafruit Trinket - Mini Microcontrolador - Lógica 5V 1

Ferramentas e Máquinas Necessárias

Sobre o Projeto

História

Este projeto originalmente seria um Xpider, do Thingiverse. Então vi o Archimedes, da Alex Glow. Me deixou impressionado. Eu queria um desesperadamente. Então comecei a trabalhar no meu próprio companheiro robótico. Percebi que não sou do tipo "coruja", e queria meu familiar especial. Então lembrei da história africana de Anansi, a aranha, trapaceira e deusa das histórias. Decidi projetar o bot com a ideia de uma história, e assim nasceu o Asi (na verdade é Asi_v4, v1-3 foram protótipos).

Montagem

O design original era um Xpider, mas levei a outro nível editando muitas coisas, jogando muita coisa fora e mexendo. Estas são a maioria das peças que você precisa imprimir.

ASi (Anansi) Robô Companheiro: Construa um Robô Amigável com Arduino Nano e Display LED

A montagem é autoexplicativa, mas aqui estão algumas fotos mesmo assim.

ASi (Anansi) Robô Companheiro: Construa um Robô Amigável com Arduino Nano e Display LED
ASi (Anansi) Robô Companheiro: Construa um Robô Amigável com Arduino Nano e Display LED

Para a eletrônica, você geralmente pode colocá-los na parte superior ou inferior da cabeça (sua escolha), mas o servo deve ser anexado como mostrado. Certifique-se de que a engrenagem está nele, por favor.

ASi (Anansi) Robô Companheiro: Construa um Robô Amigável com Arduino Nano e Display LED

O Asi é controlado por dois microcontroladores, um Trinket e um Arduino Nano. O Trinket controla o movimento do olho, graças ao servo ele gira para frente e para trás em intervalos aleatórios. O Arduino Nano controla o olho. Também é aleatório enquanto ele olha ao redor e tal, geralmente as pessoas assumem que ele está olhando para elas.

ASi (Anansi) Robô Companheiro: Construa um Robô Amigável com Arduino Nano e Display LED
ASi (Anansi) Robô Companheiro: Construa um Robô Amigável com Arduino Nano e Display LED
ASi (Anansi) Robô Companheiro: Construa um Robô Amigável com Arduino Nano e Display LED

Nota: Eu os tenho em duas protoboards pequenas que são ligadas em cascata com 4 pilhas AA. (Você também pode usar bateria lítio-íon 3.7V 200mAh. Com a bateria li-on, tive vezes por algum motivo em que não funcionava, então usaria duas baterias de lítio separadas e as conectaria ao mesmo interruptor liga/desliga para tudo iniciar de uma vez.)

A cabeça é apenas uma esfera de Natal que comprei nas festas. Dei um passo para trás e pintei levemente com tinta spray preta. Então pegue um pouco de cola quente e cole no pescoço do Asi e PRONTO, lá está a cabeça!

ASi (Anansi) Robô Companheiro: Construa um Robô Amigável com Arduino Nano e Display LED
ASi (Anansi) Robô Companheiro: Construa um Robô Amigável com Arduino Nano e Display LED
ASi (Anansi) Robô Companheiro: Construa um Robô Amigável com Arduino Nano e Display LED

Por fim, como eu o uso. Peguei um pouco de arame de armadura e passei pelos furos na parte inferior do Asi, e enrolei na alça do ombro da minha mochila.

ASi (Anansi) Robô Companheiro: Construa um Robô Amigável com Arduino Nano e Display LED

Código

Olho do Asi Arduino

O código para o olho do Asi

#include <LedControl.h> // Sempre incluir a biblioteca LedControl
#include "LedControl.h"
/* Criar objeto LetControl, definir conexões de pinos
   Temos 2 MAX72XX para os olhos. */
#define PIN_EYES_DIN 12
#define PIN_EYES_CS 11
#define PIN_EYES_CLK 10
LedControl lc = LedControl(PIN_EYES_DIN, PIN_EYES_CLK, PIN_EYES_CS, 2);

// rotação
bool rotateMatrix0 = false; // rotacionar matriz 0 em 180 graus
bool rotateMatrix1 = false; // rotacionar matriz 1 em 180 graus

// definir olho sem pupila
byte eyeBall[8] = {
  B00111100,
  B01111110,
  B11111111,
  B11111111,
  B11111111,
  B11111111,
  B01111110,
  B00111100
};
byte eyePupil = B11100111;

// armazena estado atual dos LEDs
byte eyeCurrent[8];
int currentX;
int currentY;
int cntLoop = 0;
int cntEffect = 0;

// posições min e max
#define MIN -2
#define MAX 2

// atrasos
#define DELAY_BLINK 40
// realizar efeito a cada # iterações do loop, 0 para desativar
#define EFFECT_ITERATION 4

/* Arduino setup */
void setup() {
  // MAX72XX está em modo economia de energia na inicialização, precisamos fazer wakeup
  lc.shutdown(0, false);
  lc.shutdown(1, false);
  // definir brilho para baixo
  lc.setIntensity(0, 1);
  lc.setIntensity(1, 1);
  // limpar ambos módulos
  lc.clearDisplay(0);
  lc.clearDisplay(1);
  
  // Teste LED - linha vertical
  byte b = B10000000;
  for (int c = 0; c <= 7; c++) {
    for (int r = 0; r <= 7; r++) {
      setRow(0, r, b);
      setRow(1, r, b);
    }
    b = b >> 1;
    delay(50);
  }
  // módulo cheio
  b = B11111111;
  for (int r = 0; r <= 7; r++) {
    setRow(0, r, b);
    setRow(1, r, b);
  }
  delay(500);
  // limpar ambos módulos
  lc.clearDisplay(0);
  lc.clearDisplay(1);
  delay(500);
  
  // seed aleatório
  randomSeed(analogRead(0));
  // centralizar olhos, piscada maluca
  displayEyes(0, 0);
  delay(2000);
  blinkEyes(true, false);
  blinkEyes(false, true);
  delay(1000);
}

/* Arduino loop */
void loop() {
  // mover para posição aleatória, esperar tempo aleatório
  moveEyes(random(MIN, MAX + 1), random(MIN, MAX + 1), 50);
  delay(random(5, 7) * 500);
  
  // hora de piscar?
  if (random(0, 5) == 0) {
    delay(500);
    blinkEyes();
    delay(500);
  }
  
  // hora de efeito?
  if (EFFECT_ITERATION > 0) {
    cntLoop++;
    if (cntLoop == EFFECT_ITERATION) {
      cntLoop = 0;
      if (cntEffect > 6) cntEffect = 0;
      switch(cntEffect) {
        case 0: // cruzar olhos
          crossEyes();
          delay(1000);
          break;
        case 1: // giro redondo
          roundSpin(2);
          delay(1000);
          break;
        case 2: // giro maluco
          crazySpin(2);
          delay(1000);
          break;
        case 3: // olhos "meth"
          methEyes();
          delay(1000);
          break;
        case 4: // olho preguiçoso
          lazyEye();
          delay(1000);
          break;
        case 5: // piscada maluca
          blinkEyes(true, false);
          blinkEyes(false, true);
          delay(1000);
          break;
        case 6: // brilho
          glowEyes(3);
          delay(1000);
          break;
        default:
          break;
      }
      cntEffect++;
    }
  }
}

/* Este método pisca ambos os olhos */
void blinkEyes() {
  blinkEyes(true, true);
}

/* Este método pisca olhos conforme parâmetros fornecidos */
void blinkEyes(boolean blinkLeft, boolean blinkRight) {
  // piscar?
  if (!blinkLeft && !blinkRight) return;
  // fechar pálpebras
  for (int i = 0; i <= 3; i++) {
    if (blinkLeft) {
      setRow(0, i, 0);
      setRow(0, 7-i, 0);
    }
    if (blinkRight) {
      setRow(1, i, 0);
      setRow(1, 7-i, 0);
    }
    delay(DELAY_BLINK);
  }
  // abrir pálpebras
  for (int i = 3; i >= 0; i--) {
    if (blinkLeft) {
      setRow(0, i, eyeCurrent[i]);
      setRow(0, 7-i, eyeCurrent[7-i]);
    }
    if (blinkRight) {
      setRow(1, i, eyeCurrent[i]);
      setRow(1, 7-i, eyeCurrent[7-i]);
    }
    delay(DELAY_BLINK);
  }
}

/* Este método move olhos para posição central, depois move horizontalmente com wrap nas bordas */
void crazySpin(int times) {
  if (times == 0) return;
  moveEyes(0, 0, 50);
  delay(500);
  byte row = eyePupil;
  for (int t = 0; t < times; t++) {
    // girar do centro para L
    for (int i = 0; i < 5; i++) {
      row = row >> 1;
      row = row | B10000000;
      setRow(0, 3, row);
      setRow(1, 3, row);
      setRow(0, 4, row);
      setRow(1, 4, row);
      delay(50);
      if (t == 0) delay((5-i)*10); // aumentar delay na 1ª rolagem (efeito aceleração)
    }
    // girar de R para centro
    for (int i = 0; i < 5; i++) {
      row = row >> 1;
      if (i >= 2) row = row | B10000000;
      setRow(0, 3, row);
      setRow(1, 3, row);
      setRow(0, 4, row);
      setRow(1, 4, row);
      delay(50);
      if (t == (times-1)) delay((i+1)*10); // aumentar delay na última rolagem (efeito desaceleração)
    }
  }
}

/* Este método cruza os olhos */
void crossEyes() {
  moveEyes(0, 0, 50);
  delay(500);
  byte pupilR = eyePupil;
  byte pupilL = eyePupil;
  // mover pupilas juntas
  for (int i = 0; i < 2; i++) {
    pupilR = pupilR >> 1;
    pupilR = pupilR | B10000000;
    pupilL = pupilL << 1;
    pupilL = pupilL | B1;
    setRow(0, 3, pupilR);
    setRow(1, 3, pupilL);
    setRow(0, 4, pupilR);
    setRow(1, 4, pupilL);
    delay(100);
  }
  delay(2000);
  // mover pupilas de volta ao centro
  for (int i = 0; i < 2; i++) {
    pupilR = pupilR << 1;
    pupilR = pupilR | B1;
    pupilL = pupilL >> 1;
    pupilL = pupilL | B10000000;
    setRow(0, 3, pupilR);
    setRow(1, 3, pupilL);
    setRow(0, 4, pupilR);
    setRow(1, 4, pupilL);
    delay(100);
  }
}

/* Este método exibe olho com pupila deslocada por X, Y do centro. X e Y válidos [MIN,MAX] Ambos módulos LED mostram olhos idênticos */
void displayEyes(int offsetX, int offsetY) {
  // garantir offsets em ranges válidos
  offsetX = getValidValue(offsetX);
  offsetY = getValidValue(offsetY);
  // calcular índices para linhas da pupila (realizar offset Y)
  int row1 = 3 - offsetY;
  int row2 = 4 - offsetY;
  // definir linha da pupila
  byte pupilRow = eyePupil;
  // realizar offset X
  // bit shift e preencher novo bit com 1
  if (offsetX > 0) {
    for (int i = 1; i <= offsetX; i++) {
      pupilRow = pupilRow >> 1;
      pupilRow = pupilRow | B10000000;
    }
  } else if (offsetX < 0) {
    for (int i = -1; i >= offsetX; i--) {
      pupilRow = pupilRow << 1;
      pupilRow = pupilRow | B1;
    }
  }
  // linha da pupila não pode ter 1s onde eyeBall tem 0s
  byte pupilRow1 = pupilRow & eyeBall[row1];
  byte pupilRow2 = pupilRow & eyeBall[row2];
  // exibir na matriz LCD, atualizar eyeCurrent
  for(int r = 0; r < 8; r++) {
    if (r == row1) {
      setRow(0, r, pupilRow1);
      setRow(1, r, pupilRow1);
      eyeCurrent[r] = pupilRow1;
    } else if (r == row2) {
      setRow(0, r, pupilRow2);
      setRow(1, r, pupilRow2);
      eyeCurrent[r] = pupilRow2;
    } else {
      setRow(0, r, eyeBall[r]);
      setRow(1, r, eyeBall[r]);
      eyeCurrent[r] = eyeBall[r];
    }
  }
  // atualizar X e Y atuais
  currentX = offsetX;
  currentY = offsetY;
}

/* Este método corrige valor de coordenada fornecido */
int getValidValue(int value) {
  if (value > MAX) return MAX;
  else if (value < MIN) return MIN;
  else return value;
}

/* Este método pulsa olho (muda brilho LED) */
void glowEyes(int times) {
  for (int t = 0; t < times; t++) {
    for (int i = 2; i <= 8; i++) {
      lc.setIntensity(0, i);
      lc.setIntensity(1, i);
      delay(50);
    }
    delay(250);
    for (int i = 7; i >= 1; i--) {
      lc.setIntensity(0, i);
      lc.setIntensity(1, i);
      delay(25);
    }
    delay(150);
  }
}

/* Este método move olhos para centro, fora e depois volta ao centro */
void methEyes() {
  moveEyes(0, 0, 50);
  delay(500);
  byte pupilR = eyePupil;
  byte pupilL = eyePupil;
  // mover pupilas para fora
  for (int i = 0; i < 2; i++) {
    pupilR = pupilR << 1;
    pupilR = pupilR | B1;
    pupilL = pupilL >> 1;
    pupilL = pupilL | B10000000;
    setRow(0, 3, pupilR);
    setRow(1, 3, pupilL);
    setRow(0, 4, pupilR);
    setRow(1, 4, pupilL);
    delay(100);
  }
  delay(2000);
  // mover pupilas de volta ao centro
  for (int i = 0; i < 2; i++) {
    pupilR = pupilR >> 1;
    pupilR = pupilR | B10000000;
    pupilL = pupilL << 1;
    pupilL = pupilL | B1;
    setRow(0, 3, pupilR);
    setRow(1, 3, pupilL);
    setRow(0, 4, pupilR);
    setRow(1, 4, pupilL);
    delay(100);
  }
}

/* Este método move ambos olhos da posição atual para nova posição */
void moveEyes(int newX, int newY, int stepDelay) {
  // definir posição atual como início
  int startX = currentX;
  int startY = currentY;
  // corrigir novos X Y inválidos
  newX = getValidValue(newX);
  newY = getValidValue(newY);
  // avaliar passos
  int stepsX = abs(currentX - newX);
  int stepsY = abs(currentY - newY);
  // precisa mudar pelo menos uma posição
  if ((stepsX == 0) && (stepsY == 0)) return;
  // avaliar direção, # passos, mudança por passo X Y, realizar movimento
  int dirX = (newX >= currentX) ? 1 : -1;
  int dirY = (newY >= currentY) ? 1 : -1;
  int steps = (stepsX > stepsY) ? stepsX : stepsY;
  int intX, intY;
  float changeX = (float)stepsX / (float)steps;
  float changeY = (float)stepsY / (float)steps;
  for (int i = 1; i <= steps; i++) {
    intX = startX + round(changeX * i * dirX);
    intY = startY + round(changeY * i * dirY);
    displayEyes(intX, intY);
    delay(stepDelay);
  }
}

/* Este método abaixa e levanta apenas pupila direita */
void lazyEye() {
  moveEyes(0, 1, 50);
  delay(500);
  // abaixar pupila esquerda lentamente
  for (int i = 0; i < 3; i++) {
    setRow(1, i+2, eyeBall[i+2]);
    setRow(1, i+3, eyeBall[i+3] & eyePupil);
    setRow(1, i+4, eyeBall[i+4] & eyePupil);
    delay(150);
  }
  delay(1000);
  // levantar pupila esquerda rapidamente
  for (int i = 0; i < 3; i++) {
    setRow(1, 4-i, eyeBall[4-i] & eyePupil);
    setRow(1, 5-i, eyeBall[5-i] & eyePupil);
    setRow(1, 6-i, eyeBall[6-i]);
    delay(25);
  }
}

/* Este método gira pupilas no sentido horário */
void roundSpin(int times) {
  if (times == 0) return;
  moveEyes(2, 0, 50);
  delay(500);
  for (int i = 0; i < times; i++) {
    displayEyes(2, -1); delay(40); if (i==0) delay(40);
    displayEyes(1, -2); delay(40); if (i==0) delay(30);
    displayEyes(0, -2); delay(40); if (i==0) delay(20);
    displayEyes(-1, -2); delay(40); if (i==0) delay(10);
    displayEyes(-2, -1); delay(40);
    displayEyes(-2, 0); delay(40);
    displayEyes(-2, 1); delay(40); if (i==(times-1)) delay(10);
    displayEyes(-1, 2); delay(40); if (i==(times-1)) delay(20);
    displayEyes(0, 2); delay(40); if (i==(times-1)) delay(30);
    displayEyes(1, 2); delay(40); if (i==(times-1)) delay(40);
    displayEyes(2, 1); delay(40); if (i==(times-1)) delay(50);
    displayEyes(2, 0); delay(40);
  }
}

/* Este método define valores para linha da matriz. Realiza rotação 180 se necessário */
void setRow(int addr, int row, byte rowValue) {
  if (((addr == 0) && (rotateMatrix0)) || (addr == 1 && rotateMatrix1)) {
    row = abs(row - 7);
    rowValue = bitswap(rowValue);
  }
  lc.setRow(addr, row, rowValue);
}

/* Inverter bits em byte https://www.nrtm.org/index.php/2013/07/25/reverse-bits-in-a-byte/ */
byte bitswap(byte x) {
  byte result;
  asm("mov __tmp_reg__, %[in] \n\t"
      "lsl __tmp_reg__ \n\t"  // shift out high bit to carry
      "ror %[out] \n\t"       // rotate carry __tmp_reg__to low bit (eventually)
      "lsl __tmp_reg__ \n\t"  // 2
      "ror %[out] \n\t"
      "lsl __tmp_reg__ \n\t"  // 3
      "ror %[out] \n\t"
      "lsl __tmp_reg__ \n\t"  // 4
      "ror %[out] \n\t"
      "lsl __tmp_reg__ \n\t"  // 5
      "ror %[out] \n\t"
      "lsl __tmp_reg__ \n\t"  // 6
      "ror %[out] \n\t"
      "lsl __tmp_reg__ \n\t"  // 7
      "ror %[out] \n\t"
      "lsl __tmp_reg__ \n\t"  // 8
      "ror %[out] \n\t"
      : [out] "=r" (result) : [in] "r" (x));
  return(result);
}

Pescoço do Asi com Trinket Arduino

Isto é o que uso para fazer o pescoço do Asi mover para frente e para trás

// Parâmetros do servo. Pino DEVE ser 1 ou 4 no Trinket. Posição do servo
// é especificada em ticks brutos do timer/contador (1 tick = 0.128 milissegundos).
// Tempo de pulso do servo é tipicamente 1-2 ms, mas pode variar ligeiramente entre
// servos, então pode precisar ajustar estes limites para combinar com sua realidade.
#define SERVO_PIN 4 // Pinos 1 ou 4 suportados no Trinket
#define SERVO_MIN 4 // ~1 ms pulso
#define SERVO_MAX 26 // ~2 ms pulso
Adafruit_TiCoServo servo;

void setup(void) {
  #if (F_CPU == 16000000L) // Trinket 16 MHz requer setting prescale para timing correto.
    // Isto DEVE ser feito ANTES de servo.attach()!
    clock_prescale_set(clock_div_1);
  #endif
  servo.attach(SERVO_PIN);
  pinMode(LED_PIN, OUTPUT);
  digitalWrite(LED_PIN, HIGH);
}

uint32_t lastLookTime = 0; // Tempo da última virada de cabeça

void loop(void) {
  unsigned long t = millis(); // Tempo atual
  // Se mais de 1/2 segundo passou desde última virada de cabeça...
  if((t - lastLookTime) > 500) {
    if(random(10) == 0) { // Há chance 1-em-10...
      // ...de mover cabeça aleatoriamente em nova direção:
      servo.write(random(SERVO_MIN, SERVO_MAX));
      lastLookTime = t; // Salvar tempo da virada para referência futura
    }
  }
  // Não relacionado à verificação de virada de cabeça, se
  if(random(10) == 0) { // há chance 1-em-10...
    // ...de um "piscar de olho":
    digitalWrite(LED_PIN, LOW); // LED apaga
    delay(random(50, 250)); // por momento aleatório curto
    digitalWrite(LED_PIN, HIGH); // depois acende
  }
  delay(100); // Repetir loop() ~10 vezes/segundo
}

Peças Personalizadas e Gabinetes

Peça final da perna
Esqueci de adicionar a engrenagem de virar para o servo, geralmente imprimo e encaixo o acessório do servo no meio e parafuso no servo
O corpo do Asi
A base do Asi
Peça da coxa do Asi
Junta da perna
Parafuso que uso para unir pernas do Asi
Porca que vai com o parafuso
Engrenagem do servo usada para fazer a cabeça do Asi virar, recomendaria usar peça do servo e colocar no meio
Peça do meio do Asi
Suporte do servo para engrenagem do Asi
Placa do pescoço do Asi, onde a esfera acrílica é colada ao pescoço

Esquemáticos

O circuito que usei para o pescoço do Asi

ASi (Anansi) Robô Companheiro: Construa um Robô Amigável com Arduino Nano e Display LED

Estes são os que usei para iniciar o Asi. Você pode ligá-los em cascata para alimentar ambos a partir de pequenas placas de circuito

ASi (Anansi) Robô Companheiro: Construa um Robô Amigável com Arduino Nano e Display LED

Processo de manufatura

  1. Construindo um Robô com Arduino e Raspberry Pi: Guia Passo a Passo
  2. Robô DIY Inspirado no Wall‑E com Caixa de CD e Raspberry Pi
  3. PiCy: Construa Seu Próprio Mini Robô Alimentado por Raspberry Pi
  4. Robô Alarme Rolante: Sistema Móvel Inteligente de Despertar com Raspberry Pi e Google Calendar
  5. Construa um Robô Creeper Motorizado do Minecraft com Arduino
  6. Arquimedes: O Robô Coruja Sensor de Emoções com IA
  7. Construa Robô de Visão Computacional Controlado por Fala no Windows 10 IoT Core
  8. Robô ROS: Navegação Controlada por Terminal no Raspberry Pi
  9. Simple Pi Robot – Construa Rover Autônomo 2-WD com Raspberry Pi
  10. Entendendo Robôs de Bebidas: Tecnologia, Automação e Papel na Indústria de Bebidas