Rubik's Bot

Introducere

Mie îmi place să rezolv cubul rubik deoarece este o activitate pe care o poți face când vrei să te relaxezi, cel puțin atunci când stii să-l faci, așa că am decis să le dau și roboților șansa de a se relaxa rezolvând cubul rubik.

Descriere Generală

După ce cubul a fost amestecat acesta este scanat (sau introdus manual de user) urmând ca permutarea cubului să fie preluată de partea de algoritmică (realizată extern) pentru a produce o listă de mutări optimale pentru a rezolva cubul. La pasul 2 cubul este pus în partea mecanică a robotului și odată ce userul a apăsat butonul de începere a rezolvării robotul va converti lista de mutări, calculată anterior, în comenzi de motoare stepper rezultând în rezolvarea cubului.

 Schema Bloc

Hardware Design

Lista de componente

  • Cub rubik
  • 6 Motoare stepper
  • 6 Drivere motoare stepper
  • Sursă de 12V 2A(min)
  • Sursă coborâtoare la 5V(buck)
  • Convertor USB to Serial
  • Raspberry Pi / Computer

Schema circuitului

 Schema circuitului

Alimentarea se face de la 12V, tensiune care este trecută printr-un convertor buck ce o aduce la 5V.

Motoarele se alimentează de la 12V iar logică circuitului se alimentează de la 5V.

Pinul de DIR de pe drivere este comun între 2 grupe de motoare, fiind inutilă utilizarea a 6 pini separați pentru direcție având în vedere că pentru a roti o față a unui cub rubik este nevoie doar de un motor în funcțiune la un moment de timp. Montajul se putea realiza și cu un singur pin de direcție dar acest lucru ar fi împiedicat întoarcearea în direcții diferite a doua fețe de pe laturi opuse ale cubului, ceea ce este o mișcare validă.

Câte un pin de STEP este necesar pentru a putea executa rotații doar pe un motor la un moment de timp.

Câte un pin de ENABLE la fiecare driver a fost folosit pentru a dezactiva motoarele care nu sunt folosite în momentul unei mutări, deblocându-le și permițând toleranțe mai mari. La început, până când se primește soluția cubului pe usb, toate motoarele sunt dezactivate pentru a nu consuma curent degeaba, la fel și după soluționarea cubului.

Am mai folosit un adaptor usb pentru interfațarea plăcii pe serială și I/O-ul deja prezent pe placă (butonul și ledul).

Proiectul Fizic

Aici am 3D printat un cadru modelat de jayflatland fiind si inspiratia generala a proiectului. Nu am folosit elementele lui de conectare de stepper-cub ci le am modelat singur pe ale mele in mai multe iteratii.

 Cadri 3D printat Imagine cu cadrul, in pozitia de rezolvare a cubului(stanga) si fara motorul de sus pentru a intorduce cubul(dreapta).

 Cupla stepper-cub Ultimele 3 iteratii ale cuplei stepper cub.

 Electronice Toate electronicele proiectului.

 Prototipare la stepper Cum a aratat procesul de prototipare pentru a face cupla pentru axul stepperelor (mai greu decat pare deoarece trebuie sa iei in considerare schimbarea dimensiunilor printului in functie de temperatura).

 Cadru de lemn Prima iteratie a cadrului care dupa 2 zile de munca a fost abandonata din cauza tolerantelor proaste ale lemnului.

Software Design

Am dorit sa fie un sistem destul de complet asa ca am scris si un program de recunoastere a cubului cu OpenCV, calibrare si scripturi de transmisie/debug.

Proiectul pe microcontroler se foloseste de urmatoarea structura de transmisie a datelor:

<timp ms intre mutari> <delay us pentru intre pasi> <lista de mutari> #

Lista de mutari poate contine intrari separate prin spatiu si/sau , de felul urmator:

  • F/R/U/B/L/D - semnifica mutari in sensul acelor de ceasormic ale fetei corespunzatoare
  • F'/R'/U'/B'/L'/D' - semnifica mutari invers sensul acelor de ceasormic ale fetei corespunzatoare
  • F2/R2/U2/B2/L2/D2 - semnifica mutari de 180 ale fetei corespunzatoare

Un input valid arata asa: 1 700 D' B2 U D2 B2 R B' L D B R D2 B2 L2 D2 B2 D' R2 D2 R2 B2 # semnificand:

  • 1ms intre mutari
  • 700us intre pasi
  • Prima mutare a fetei de jos invers sensului acelor de ceassornic
  • …..
  • # - caracter care semnalizeaza finalul listei de mutari

Am folosit algoritmul lui Herbert Kociemba pentru a rezolva cubul, cu implementarea lui muodov.

Tot codul poate fi gasit pe githubul meu din acesta se remarca urmatoarele functii si macro-uri:

#define digitalWrite(port, pin, val) ((val == 0) ? (port &= ~(1 << pin)) : (port |= (1 << pin)))
#define digitalRead(port, pin) ((port & (1 << pin)))
#define pinMode(port, pin, mode) (digitalWrite(port, pin, mode))

Macro-uri folosite pentru a interfata mai usor I/O placii.

void move_face(int face, int steps, int dir, int us_delay) {
 
  digitalWrite(ENB_PORT, enb_pins[face], 0);
 
  if (face < 3) {
    digitalWrite(DIR_PORT, FRU_DIR, dir);
  } else {
    digitalWrite(DIR_PORT, BLD_DIR, dir);
  }
 
  for (int i = 0; i < steps; i++) {
    digitalWrite(STEP_PORT, step_pins[face], 1);
    _delay_us(us_delay);
    digitalWrite(STEP_PORT, step_pins[face], 0);
    _delay_us(us_delay);
  }
 
  digitalWrite(ENB_PORT, enb_pins[face], 1);
}

Functie care muta o fata a cubului face, steps pasi in directia dir cu delay intre pasi de delay_us.

void execute(char* moves) {
 
  char * move;
  move = strtok(moves," ,.-");
 
  int move_delay = atoi(move);
  move = strtok(NULL, " ,.-");
  int us_delay = atoi(move);
  move = strtok(NULL, " ,.-");
 
  while (move != NULL)
  {
 
    int face = 0;
    int steps = 50;
    int dir = 0;
 
    switch (move[0]) {
    case 'F':
      face = 0;
      break;
    case 'R':
      face = 1;
      break;
    case 'U':
      face = 2;
      break;
    case 'B':
      face = 3;
      break;
    case 'L':
      face = 4;
      break;
    case 'D':
      face = 5;
      break;
    }
 
    if (strlen(move) > 1) {
      if (move[1] == '\'') {
        dir = 1;
      } else if (move[1] == '2') {
        steps = 100;
      }
    }
 
    move_face(face, steps, dir, us_delay);
 
    move = strtok(NULL, " ,.-");
 
    _delay_ms(move_delay);
  }
 
}

Functie care primeste stringul de mutari si apeleaz functia de mutare a unei fete cu parametrii corespunzatori.

void get_solution_and_solve() {
 
  char buf[256];
  char c;
 
  while (1) {
    int i = 0;
    c = USART0_receive();
    while (c != '#') {
      buf[i] = c;
      c = USART0_receive();
      i++;
    }
    buf[i] = 0;
 
    USART0_print(buf);
    USART0_print("\n");
    if (c == '#') {
      USART0_print("Ready!\n");
      while (1) {
        if ((digitalRead(BUTTON_PIN, BUTTON)) == 0) {
          digitalWrite(LED_PORT, LED, 1);
          execute(buf);
          digitalWrite(LED_PORT, LED, 0);
          break;
        }
      }
    }
  }
}

Main workflow function - primeste date pe seriala, cand primeste # intra in asteptarea inpupului de la user(butonul). Cand butonul a fost apasat incepe rezolvarea cubului si aprinde ledul. Cand cubul a fost rezolvat stinge ledul si asteapta o alta secventa de mutari.

Rezultate Obţinute

Am reusi sa rezolv cubul in sub 2 secunde, sunt sigur ca se poate si mai rapid, aplicand curbe de acceleratie la steppere si optimizand mutarile (de ex daca trebuie sa mut fata de sus si fata de jos aceste 2 mutari pot fi comprimate intr-o singura mutare deoarece fetele nu isi intersecteaza drumul de intoarcere). De asemenea algoritmul de detectie/calibrare se poate optimiza(reface) pentru o mai buna interactiune cu userul.

Am pus si un video pentru a exemplifica rapiditatea rezolvarii:

Concluzii

Un proiect frumos mi ar placea sa il continui ceea ce voi si face doar ca pe un Arduino.

Download

Bibliografie/Resurse

pm/prj2019/ostiru/mc_rubiks.txt · Last modified: 2021/04/14 17:07 (external edit)
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0