E' un progetto ben documentato di jjrobots con software e modelli 3D disponibili da qui.
Mi piace perché:
Pensato come robot a 2 assi per orientare un puntatore laser può montare anche altri payload (webcam, “pistole”, ecc.).
La BOM è abbastanza ridotta:
| descrizione | numero | disponibile | prototipo |
|---|---|---|---|
| pezzi 3D stampati | 11 | manca solo il supporto payload che va creato su misura | |
| stepper nema17 | 2 | sì | da L03 |
| driver stepper | 2 | sì | a4988 da L03? |
| cavi motore | 2 | venduti col motore | come collegarli al driver? |
| puleggia GT2 20 denti | 2 | da kit amazon | |
| cuscinetti 6002RS or 6002ZZ | 2 | 5€ da cesena cuscinetti | |
| fascette 100x5mm | 10 | sì | nessuna |
| cinghia 200mm 200 GT2 | 3 | da acquistare | da kit amazon |
| fascetta avvolgi-cavo | 1 | opzionale | nessuna |
| ventose/piedini (rubber suction pad) | 3 | da acquistare | piedini da L51 o si fa senza |
| arduino zero | 1 | usare ESP32? | arduino uno |
| alimentatore 12V | 1 | sì | da laboratorio |
| viti M3 6mm | 6 | ? | 8mm e 3-4 6mm da lab51 |
| viti M3 10mm | 14 | da magazzino Comandini P09 | |
| viti M3 15mm | 10 | da lab51 | |
| viti M3 45mm | 4 | ? | 40mm da lab51 |
| dadi M3 | 6 | ? | da lab51 |
Ho preso questo kit da amazon con due pulegge 20 denti (foro 5mm), due 60 denti e due cinghie (larghezza 6mm) lunghezza 200mm GT2. I cuscinetti li ho presi da cesena cuscinetti (i più economici 5€).
L'assemblaggio è mostrato passo per passo con delle immagini nel sito del progetto. Per non sbagliare è molto utile scaricare da thingiverse il file .STEP che contiene l'intero assemblaggio. Aprendolo con FreeCAD si possono evidenziare le singole parti e l'insieme. Quando è possibile è sempre meglio usare i file STEP, infatti:
NB: in entrambi i casi non è possibile risalire alle azioni con cui è stata creato il pezzo.
L'assemblaggio pone questi problemi:
Rifaccio il pezzo upper arm motor holder left ma lo traslo in basso di 2,7mm tagliando di fatto la parte che finisce sotto il piatto. Di fatto lo accorcio di quasi 3mm sperando che si adattino sia le viti che i fori dell'altro motor holder.
Il motore è collegato alla breadboard in questo modo:
In generale tutto il cablaggio è estremamente fragile e sarebbe opportuno progettare un PCB con driver, morsetti per l'alimentazione, header per i cavi dei motori. Anche un supporto stampato in PLA per alloggiare il tutto non sarebbe male.
Per il collegamento dei due motori stepper seguire questo tutorial (riportato anche nella pagina stepper). In particolare la piedinatura è questa:
Altri problemi:
// basato sugli esempi della libreria AccelStepper (multiple steppers e proportional) // joystick analogico comanda la velocità ma c'è una posizione limite che non viene superata // il pulsante del joystick abilita/disabilita il driver (LED13 acceso se abilitato) // il pulsante è gestito con un interrupt sul fronte di discesa // il rimbalzo del pulsante è gestito trascurando commutazioni che si ripetono prima di 200ms // i rapporti di riduzione sono: 64:20 per l'altezza , e 80:20 per la rotazione // 200 passi/giro diventano 640 passi/giro per altezza e 800 passi/giro per la rotazione // NB il driver disturba molto l'ingresso analogico -> SERVONO ALIMENTAZIONI SEPARATE // NB la seriale disturba il pilotaggio del motore -> DISATTIVARE LA SERIALE // libreria per gestire i motori stepper con due driver A4988 #include <AccelStepper.h> // istanza con opzione DRIVER, pin 9 STEP e pin 8 DIR per rotazione AccelStepper stepper1(AccelStepper::DRIVER, 9, 8); // istanza con opzione DRIVER, pin 11 STEP e pin 10 DIR per altezza AccelStepper stepper2(AccelStepper::DRIVER, 11, 10); // piedini int pinPulsante = 2; int pinEnable = 7; // segnali analogici: X su A0 e Y su A1 // variabili aggiornate dall'interrupt service routine (ISR) volatile int enable = 1; // di default disabilitato perché attivo basso volatile unsigned long last_interrupt_time = 0; // ultimo interrupt volatile unsigned long interrupt_time = 0; // interrupt corrente // input dal joystick int analogX; // comanda la rotazione (0 = estremo sinistro) int analogY; // comanda l'altezza (0 = estremo superiore) int offset = 512; // posizione di riposo del joystick // controllo motori int velX = 0; // velocità in passi/s per stepper1 int velY = 0; // velocità in passi/s per stepper2 int posX = 120; // posizione limite per stepper1 (+-60° circa) int posY = 70; // posizione limite per stepper2 (+-45° circa) void setup() { // pullup interno per il pulsante joystick pinMode(pinPulsante, INPUT_PULLUP); // pin ENABLE del driver pinMode(pinEnable, OUTPUT); // disabilito driver digitalWrite(pinEnable, enable); // interrupt quando è premuto il pulsante abilitazione attachInterrupt(digitalPinToInterrupt(pinPulsante), toggle, FALLING); // impostazioni driver stepper1.setMaxSpeed(500); // 1 giri/s stepper -> 0.3 giri/s laser stepper2.setMaxSpeed(500); // debug seriale // NB il driver non funziona correttamente se attivata // Serial.begin(9600); } void loop() { // lettura posizione jostick analogX = analogRead(A0); analogY = analogRead(A1); // calcolo velocità velX = ((analogX - offset) / 10) * 8; // dividendo si elimina l'ultima cifra che è incerta velY = ((analogY - offset) / 10) * 8; // moltiplicando si ottiene un valore tra 0 e 204 passi/s // Serial.print(analogX); // Serial.print(" "); // Serial.print(analogY); // Serial.print(" "); // Serial.print(velX); // Serial.print(" "); // Serial.print(velY); // Serial.print(" "); // Serial.print(enable); // Serial.print(" "); // Serial.print(stepper1.speed()); // Serial.println(); // segnalazione dell'abilitazione col LED 13 if (enable) { digitalWrite(13, LOW); } else { digitalWrite(13, HIGH); } // movimento stepper1 if (velX > 0) { stepper1.moveTo(posX); // posizione da raggiungere stepper1.setSpeed(velX); // velocità con cui raggiungerla stepper1.runSpeedToPosition(); // muove il motore fino a destinazione } else if (velX < 0) { stepper1.moveTo(-posX); stepper1.setSpeed(velX); stepper1.runSpeedToPosition(); } else { stepper1.moveTo(posX); // probabilmente bastava mettere un blocco di codice vuoto stepper1.setSpeed(0); stepper1.runSpeedToPosition(); } // movimento stepper2 if (velY > 0) { stepper2.moveTo(posY); stepper2.setSpeed(velY); stepper2.runSpeedToPosition(); } else if (velY < 0) { stepper2.moveTo(-posY); stepper2.setSpeed(velY); stepper2.runSpeedToPosition(); } else { stepper2.moveTo(posY); stepper2.setSpeed(0); stepper2.runSpeedToPosition(); } } // ISR pressione pulsante void toggle() { // serve un debounce o commuta a caso per i rimbalzi // così commuta solo ogni 200 ms interrupt_time = millis(); // leggo il tempo dell'interrupt if (interrupt_time - last_interrupt_time > 200) { // se sono passati 200ms enable = !enable; // commuto enable digitalWrite(pinEnable, enable); // imposto il piedino corrispondente } last_interrupt_time = interrupt_time; // aggiorno il tempo dell'ultimo interrupt }