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.
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.
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).
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.
Imagine cu cadrul, in pozitia de rezolvare a cubului(stanga) si fara motorul de sus pentru a intorduce cubul(dreapta).
Ultimele 3 iteratii ale cuplei stepper cub.
Toate electronicele proiectului.
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).
Prima iteratie a cadrului care dupa 2 zile de munca a fost abandonata din cauza tolerantelor proaste ale lemnului.
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 corespunzatoareF'/R'/U'/B'/L'/D
' - semnifica mutari invers sensul acelor de ceasormic ale fetei corespunzatoareF2/R2/U2/B2/L2/D2
- semnifica mutari de 180 ale fetei corespunzatoareUn 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:
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.
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:
Un proiect frumos mi ar placea sa il continui ceea ce voi si face doar ca pe un Arduino.