Guide · 19 min di lettura

Braccio robotico Arduino: servo, calcolo della coppia e Bluetooth

Come costruire un braccio robotico Arduino: struttura in MDF o stampa 3D, coppia del MG996R, controllo con potenziometro e joystick, PCA9685 e Bluetooth.

Un braccio robotico Arduino è un robot da scrivania che, grazie alle articolazioni mosse da servomotori, può afferrare un oggetto, sollevarlo, ruotare e posarlo altrove. Con base, spalla, gomito, polso e pinza, ti insegna in un solo progetto progettazione meccanica, scelta dei motori, gestione dell'alimentazione e programmazione. E il bello è che puoi costruirlo a casa con un Arduino Uno, qualche servo e parti in MDF o stampate in 3D, facendo lavorare sulla tua scrivania una piccola copia di un robot industriale.

In questa guida vediamo dall'inizio alla fine i bracci robotici a 4 e 6 assi: il materiale della struttura, il calcolo della coppia per scegliere tra SG90, MG90S e MG996R, l'alimentazione esterna a 5 V, il controllo con potenziometri e joystick, la registrazione e riproduzione dei movimenti, il driver per servo a 16 canali PCA9685, il controllo Bluetooth dallo smartphone e le soluzioni ai due problemi più frequenti: tremolii e reset. Se non sai ancora come funziona un servo, parti da Cos'è un servomotore?.

SG90
SG90
MG90S
MG90S
MG996R
MG996R

Come funziona un braccio robotico e quanti assi servono?

Immagina un braccio robotico come un braccio umano. In ogni articolazione c'è un servomotore e Arduino dice a ogni servo "vai a questo angolo". Grazie al potenziometro interno, il servo sa dove si trova il suo albero e mantiene l'angolo anche sotto carico. Cambiando insieme gli angoli delle articolazioni porti la pinza nel punto che vuoi. Il numero di movimenti indipendenti che un braccio può compiere si chiama gradi di libertà (DOF), o più semplicemente assi.

ArticolazioneMovimentoCaricoServo consigliato
Base (rotazione)Ruota il braccio a destra e a sinistraNon solleva nulla ma sostiene tutto il braccioMG996R + cuscinetto
SpallaSolleva il braccio in avanti e indietroL'articolazione più sollecitataMG996R o servo della classe 20 kg·cm
GomitoPiega l'avambraccioMedio-altoMG996R
Polso (su e giù)Regola l'angolo della pinzaBassoMG90S
Rotazione del polsoRuota la pinza sul proprio asseMolto bassoMG90S o SG90
Pinza (gripper)Afferra e rilascia l'oggettoForza di serraggioSG90 o MG90S

La maggior parte dei kit di "braccio robotico a 6 assi" ha 6 servo; poiché la pinza non conta come asse, tecnicamente si tratta di un braccio a 5 gradi di libertà più una pinza. Per il tuo primo braccio, un progetto a 4 assi (base, spalla, gomito, pinza) è il punto di partenza migliore: bastano 4 servo, 4 potenziometri e un Arduino Uno, il cablaggio resta semplice e i problemi di coppia sono rari. Le articolazioni del polso servono per tenere la pinza orizzontale o afferrare oggetti da angolazioni diverse; quando passi a 5–6 servo, il driver PCA9685 diventa quasi indispensabile.

Lista dei materiali

ComponenteBraccio a 4 assiBraccio a 6 servoNota
Scheda di controlloArduino Uno o NanoArduino Uno, Nano o ESP32Gli ingressi extra A6 e A7 del Nano permettono più potenziometri
Servomotori4 × SG90 o MG90S3 × MG996R + 3 × MG90SVerifica la scelta con il calcolo della coppia qui sotto
Driver per servoNon serveModulo PCA9685 a 16 canaliSolo 2 fili di segnale in I2C
Alimentatore5 V 3 A5–6 V, almeno 5 AI servo non si alimentano mai da Arduino
Condensatore470–1000 µF, 16 V1000–2200 µF, 16 VSulla linea di alimentazione dei servo, rispettando la polarità
Comandi4 potenziometri da 10 kΩ2 moduli joystick o un HC-05Puoi anche usarli insieme
Pulsanti2 (registra, riproduci)Bastano i pulsanti dei joystickCon INPUT_PULLUP niente resistenze
MeccanicaParti in MDF da 3 mm o stampate in 3DMDF da 4 mm, stampa 3D o staffe in alluminioCuscinetto 608, viti M3 e dadi autobloccanti

Struttura meccanica: MDF o stampa 3D?

MaterialePregiDifettiIdeale per
MDF da 3–4 mm (taglio laser)Economico, leggero, facile da avvitare e incollareTeme l'umidità, i fori delle viti si allargano col tempoPrimi bracci stile MeArm, progetti scolastici
Acrilico da 3 mmMolto bello, non teme l'umiditàFragile; può incrinarsi stringendo le vitiProgetti da esposizione
Stampa 3D (PLA / PETG)Parti complesse, sedi dei servo su misuraStampa lunga; il PLA si deforma col calore e sotto carico costanteProgetti personali; PETG per le parti portanti
Kit di staffe in alluminioMolto rigido, parti pronte per il MG996RPesante e costoso, carica di più la spallaBracci a 6 servo per oggetti più pesanti
Cartone, bastoncini da gelatoQuasi gratis, prototipo rapidoPoco robusto, le articolazioni prendono giocoProvare le misure, primi tentativi con i bambini
  • Tieni il braccio corto e leggero: Il carico sulla spalla cresce in proporzione diretta alla distanza. Un braccio da 24 cm invece di 30 cm riduce la coppia della spalla di circa un quinto.
  • Metti i servo pesanti vicino alla base: Spostare il servo del gomito all'altezza della spalla e azionare il gomito con un'asta di spinta (parallelogramma) è un trucco furbo usato da bracci come il MeArm; la spalla non deve più sollevare quel servo.
  • Usa un cuscinetto nella base: Non caricare tutto il peso del braccio sull'albero del servo della base. Un cuscinetto 608 (8 mm interno, 22 mm esterno, 7 mm di spessore; il classico da skateboard) o un cuscinetto per piatto girevole regge il carico e il servo si limita a ruotare.
  • Sostieni le articolazioni da entrambi i lati: Mentre il servo tiene l'articolazione dal lato della squadretta, aggiungi un perno sul lato opposto con una vite M3 e un dado autobloccante. Così l'albero del servo non subisce flessione e gli ingranaggi durano molto di più.
  • Usa squadrette metalliche: Monta su ogni MG996R una squadretta tonda metallica a 25 denti (25T) e fissala all'albero con la vite centrale. Le squadrette di plastica slittano e si spanano sulle articolazioni molto caricate.
  • Aggiungi gomma alle dita della pinza: Una sottile striscia di gomma sulle punte permette di tenere gli oggetti senza farli scivolare e con meno forza di serraggio.

Scegliere i servo: SG90, MG90S o MG996R?

ModelloCoppiaVelocità (per 60°)PesoIngranaggiCorrente di stalloRuolo nel braccio
SG901,8 kg·cm (4,8 V)0,10 s (4,8 V)9 gPlasticaCirca 0,6–0,75 APinza, polso leggero, piccolo braccio a 4 assi
MG90S1,8 kg·cm (4,8 V), 2,2 kg·cm (6 V)0,10 s (4,8 V)13,4 gMetalloCirca 0,7–1 APolso, rotazione del polso, pinza
MG996R9,4 kg·cm (4,8 V), 11 kg·cm (6 V)0,17 s (4,8 V)55 gMetallo2,5 A (6 V)Base, spalla, gomito
DS321819 kg·cm (5 V), 21,5 kg·cm (6,8 V)0,16 s (5 V)60 gMetalloCirca 2–3 ASpalla di bracci lunghi o pesanti

La coppia indicata in tabella è la coppia di stallo (stall torque): la forza massima che il servo fornisce prima che il suo albero si fermi, non un valore di lavoro continuo. Più un servo si avvicina a quel limite, più la sua corrente sale verso quella di stallo, si scalda e comincia a tremare. La regola pratica è che la coppia richiesta da un'articolazione non superi la metà della coppia di stallo del servo. Conta anche la tensione: il MG996R dà 9,4 kg·cm a 4,8 V e 11 kg·cm a 6 V, quindi lo stesso servo diventa circa il 17% più forte solo grazie alla tensione. Per questo con i bracci a MG996R conviene alimentare a 6 V, ma per SG90 e MG90S 6 V è il limite massimo. Per collegamenti e limiti del MG996R leggi anche la guida servomotore MG996R.

Servomotore MG996R con ingranaggi metallici
MG996R: ingranaggi metallici, coppia di 11 kg·cm a 6 V e albero di uscita a 25 denti; la scelta standard per base, spalla e gomito di un braccio robotico.

Calcolo della coppia: il servo della spalla regge questo braccio?

La coppia che un servo deve vincere è la somma della massa di ogni parte moltiplicata per la sua distanza orizzontale dall'asse dell'articolazione: Coppia (kg·cm) = Σ massa (kg) × distanza (cm). Il caso peggiore è con il braccio completamente disteso in orizzontale, perché tutte le distanze sono massime; con il braccio verticale il braccio di leva scende quasi a zero. Per passare ai newton metro moltiplica per 0,098 (1 kg·cm ≈ 0,098 N·m). Come esempio calcoliamo il carico sulla spalla di un braccio da scrivania con 12 cm dalla spalla al gomito, 12 cm dal gomito al polso, 6 cm dal polso alla punta della pinza e un MG996R nel gomito, con la pinza che tiene un oggetto da 50 g:

ParteMassaDistanza dall'asse della spallaCoppia
Parti del braccio superiore (MDF e viti)40 g6 cm (baricentro)0,04 × 6 = 0,24 kg·cm
Servo del gomito (MG996R)55 g12 cm0,055 × 12 = 0,66 kg·cm
Parti dell'avambraccio30 g18 cm0,03 × 18 = 0,54 kg·cm
Servo del polso + pinza (MG90S, SG90 e parti)50 g25 cm0,05 × 25 = 1,25 kg·cm
Oggetto afferrato50 g30 cm0,05 × 30 = 1,50 kg·cm
Totale225 g—4,19 kg·cm (≈ 0,41 N·m)

Da fermo, questo braccio richiede 4,19 kg·cm alla spalla. Per coprire il carico extra di accelerazioni e frenate e tenere il servo lontano dallo stallo, moltiplica per 2: 8,4 kg·cm. Quindi la spalla ha bisogno almeno di un MG996R (9,4 kg·cm a 4,8 V, 11 kg·cm a 6 V); gli 1,8–2,2 kg·cm di un SG90 o MG90S non smuoverebbero nemmeno questo braccio. Rifacendo il calcolo per il gomito, avambraccio (0,03 × 6), polso con pinza (0,05 × 13) e oggetto (0,05 × 18) danno 1,73 kg·cm, cioè 3,5 kg·cm con il margine di sicurezza: anche il gomito vuole un MG996R. Sul polso restano solo pinza e oggetto, il carico è di circa 0,4 kg·cm e un MG90S basta e avanza. Lo stesso calcolo ti dice anche qual è l'oggetto più pesante che il braccio può portare: aumenta la massa, ricalcola il totale e resta sotto la metà della coppia del servo.

Ci sono tre modi economici per ridurre la coppia della spalla: accorciare il braccio, spostare il servo del gomito all'altezza della spalla azionando il gomito con un'asta, e aggiungere un contrappeso dietro la spalla o una molla che tiri il braccio verso l'alto. Per esempio, un peso da 400 g posto 5 cm dietro l'asse della spalla genera una controcoppia di 0,4 × 5 = 2 kg·cm e quasi dimezza il carico del braccio dell'esempio; in cambio il cuscinetto della base sostiene un po' più di peso.

Alimentazione: perché i servo non si alimentano da Arduino?

La maggior parte dei problemi di un braccio robotico nasce dall'alimentazione. Un MG996R assorbe 0,5–0,9 A in movimento e fino a 2,5 A in stallo; persino un piccolo SG90 può superare 0,6 A quando si blocca. Con Arduino Uno alimentato via USB, la corrente totale disponibile sul pin 5V è di circa 500 mA, il limite dell'USB, e una parte la consuma la scheda stessa. Quando tre servo grandi partono insieme, la tensione crolla, il microcontrollore si resetta e il braccio scatta nella posizione iniziale. La soluzione è alimentare i servo con una sorgente separata da 5–6 V e collegare insieme tutte le masse (GND); Arduino può continuare a essere alimentato via USB o dal suo alimentatore. Per le opzioni di alimentazione consulta la guida alimentazione di Arduino, e per stimare quanto dura un braccio a batteria usa il calcolatore di durata della batteria.

Tipo di braccioCorrente stimataAlimentazione consigliata
Braccio piccolo con 4 × SG90 o MG90S0,5–1 A in movimento, picchi di 2–3 AAlimentatore 5 V 3 A o modulo LM2596 (max 3 A, circa 2 A senza dissipatore)
Braccio a 6 servo: 3 × MG996R + 3 × MG90S2–3 A in movimento, 8–10 A se vanno tutti in stalloAlimentatore switching 5–6 V da almeno 5 A (10 A per stare tranquillo)
Braccio portatileDipende dal braccioLiPo 2S (7,4 V) + modulo step-down (UBEC) da 5 A o più regolato a 6 V
Sorgenti da evitare—Batteria da 9 V (non regge la corrente), il pin 5V di Arduino, 4 pile AA alcaline su un braccio con MG996R
Non alimentare mai i servo dal pin 5V di Arduino e collega sempre il negativo dell'alimentatore esterno al GND di Arduino; senza una massa comune i servo non capiscono il segnale e si muovono a caso. Metti un condensatore elettrolitico sulla linea dei servo, subito accanto a loro, calcolando almeno 100 µF per servo (1000–2200 µF, almeno 16 V per un braccio a 6 servo), con il piedino lungo sul positivo; un elettrolitico montato al contrario può gonfiarsi ed esplodere. Le linee di alimentazione della breadboard sono pensate per circa 1 A; su un braccio con MG996R porta i cavi di alimentazione direttamente a un morsetto o all'ingresso V+ del PCA9685.

Collegamenti del braccio a 4 assi

La tabella qui sotto riporta il cablaggio del braccio a 4 assi con potenziometri per Arduino Uno e Raspberry Pi Pico; gli sketch di questa guida usano questi pin. Su Uno la libreria Servo può pilotare un servo da qualsiasi pin digitale, ma poiché usa il Timer1, analogWrite smette di funzionare sui pin 9 e 10 finché ci sono servo collegati. Puoi vedere la piedinatura della tua scheda nello strumento Pinout.

CollegamentoArduino UnoRaspberry Pi Pico
Segnale servo della base (filo arancione)D3GP10
Segnale servo della spallaD5GP11
Segnale servo del gomitoD6GP12
Segnale servo della pinzaD9GP13
Cursore potenziometri 1–3 (base, spalla, gomito)A0, A1, A2GP26, GP27, GP28
Cursore potenziometro 4 (pinza)A3Nessuno; la pinza usa il pulsante su GP14
Piedini esterni dei potenziometri5V e GND3V3 e GND
Pulsanti registra e riproduci (altro lato a GND)D7 e D8—
Filo rosso del servo (+)Positivo esterno 5–6 VPositivo esterno 5–6 V
Filo marrone del servo (−)Negativo dell'alimentatore esterno + GND di ArduinoNegativo dell'alimentatore esterno + GND del Pico
Potenziometro rotativo da 10 kΩ
Potenziometro da 10 kΩ: i piedini esterni vanno a 5V e GND, quello centrale (cursore) a un ingresso analogico; girando l'albero, l'articolazione ruota nella stessa proporzione.

Calibra prima del montaggio: centra i servo

  1. Prima di montare le squadrette carica il codice qui sotto; tutti e quattro i servo vanno uno dopo l'altro a 90 gradi e restano lì.
  2. Tieni il braccio in una posa neutra (per esempio braccio superiore verticale, avambraccio orizzontale e pinza semiaperta), monta ogni squadretta sul dente più vicino a quella posa e fissala con la vite centrale.
  3. Nel codice imposta CENTER_ONLY a false, scegli un'articolazione con TEST e gira lentamente il potenziometro su A0.
  4. Osserva l'angolo nel Monitor seriale e annota l'angolo minimo e massimo che l'articolazione raggiunge senza urtare nulla né tirare i cavi.
  5. Fissa l'angolo chiuso della pinza nel punto in cui le dita si toccano appena; un servo che continua a voler stringere resta in stallo e si scalda.
  6. Scrivi i valori trovati negli array MIN_A e MAX_A degli sketch successivi.
C++ (Arduino IDE)
1#include <Servo.h>
2
3const byte SERVO_PIN[4] = {3, 5, 6, 9};   // taban, omuz, dirsek, kıskaç / base, shoulder, elbow, gripper
4const byte TEST = 1;                       // denenecek servo (0–3) / servo under test (0–3)
5const bool CENTER_ONLY = true;             // true: hepsi 90°'de bekler / true: all wait at 90°
6
7Servo servo[4];
8
9void setup() {
10  Serial.begin(9600);
11  for (byte i = 0; i < 4; i++) {
12    servo[i].write(90);                    // önce açıyı ver / set the angle first
13    servo[i].attach(SERVO_PIN[i]);         // sonra bağla: zıplamaz / then attach: no jump
14    delay(300);                            // akım darbelerini ayır / spread the current surges
15  }
16}
17
18void loop() {
19  if (CENTER_ONLY) return;                 // korna takarken burada kal / stay here while fitting horns
20  int angle = map(analogRead(A0), 0, 1023, 0, 180);   // A0'daki pot / pot on A0
21  servo[TEST].write(angle);
22  Serial.print(F("Servo "));
23  Serial.print(TEST);
24  Serial.print(F(" aci / angle: "));
25  Serial.println(angle);                   // sınırları buradan oku / read the limits here
26  delay(50);
27}

C'è un dettaglio piccolo ma importante in questo codice: poiché servo.write(90) viene chiamato prima di attach(), il servo riceve l'angolo voluto già dal primo impulso. Se inverti l'ordine, la libreria invia prima il suo impulso predefinito di 1500 µs (circa 90°); se il tuo angolo iniziale non è 90, all'accensione il braccio scatta prima al centro e poi verso l'obiettivo. Avviare i servo uno alla volta a 300 ms di distanza distribuisce anche il picco di corrente all'accensione.

Controllare il braccio robotico con i potenziometri

Il metodo di controllo più intuitivo è un potenziometro per articolazione: l'articolazione segue l'angolo dell'albero del potenziometro, quindi fai un controllo di posizione. Se monti i potenziometri in un piccolo modellino del braccio (in cartone o stampato in 3D, con le stesse proporzioni), il braccio grande copia uno a uno i movimenti che fai con la mano. Le letture analogiche però oscillano per natura di qualche unità, e quell'oscillazione si trasforma in tremolio del servo; il codice prevede due contromisure.

C++ (Arduino IDE)
1#include <Servo.h>
2
3const byte SERVO_PIN[4] = {3, 5, 6, 9};        // taban, omuz, dirsek, kıskaç / base, shoulder, elbow, gripper
4const byte POT_PIN[4]   = {A0, A1, A2, A3};    // her eklem için bir pot / one pot per joint
5const byte MIN_A[4] = {0, 15, 20, 40};         // kalibrasyonda bulduğun alt sınırlar / lower limits from calibration
6const byte MAX_A[4] = {180, 165, 160, 110};    // üst sınırlar / upper limits
7
8Servo servo[4];
9float smooth[4];                               // süzülmüş pot değeri / filtered pot value
10int lastAngle[4];                              // son yazılan açı / last written angle
11
12void setup() {
13  for (byte i = 0; i < 4; i++) {
14    smooth[i] = analogRead(POT_PIN[i]);
15    lastAngle[i] = map((int)smooth[i], 0, 1023, MIN_A[i], MAX_A[i]);
16    servo[i].write(lastAngle[i]);              // önce açı, sonra attach / angle first, then attach
17    servo[i].attach(SERVO_PIN[i]);
18    delay(300);                                // servolar sırayla uyansın / wake the servos one by one
19  }
20}
21
22void loop() {
23  for (byte i = 0; i < 4; i++) {
24    smooth[i] += (analogRead(POT_PIN[i]) - smooth[i]) * 0.2;   // yumuşatma filtresi / low-pass filter
25    int angle = map((int)smooth[i], 0, 1023, MIN_A[i], MAX_A[i]);
26    if (abs(angle - lastAngle[i]) >= 2) {      // ölü bant: küçük gürültüyü yok say / deadband: ignore small noise
27      servo[i].write(angle);
28      lastAngle[i] = angle;
29    }
30  }
31  delay(15);
32}

L'array smooth è un semplice filtro passa-basso: a ogni ciclo si aggiunge solo il 20% della nuova lettura, così i salti si ammorbidiscono e il braccio risponde comunque in fretta. Con 0,1 al posto di 0,2 il movimento diventa più morbido ma più lento a reagire. La seconda contromisura è una banda morta: finché il nuovo angolo non differisce di almeno 2 gradi dall'ultimo scritto, al servo non arriva nulla e il tremolio a braccio fermo sparisce. Poiché map() converte l'escursione del potenziometro direttamente nei limiti sicuri di ogni articolazione, l'articolazione non arriva al finecorsa meccanico nemmeno con il potenziometro girato fino in fondo.

Registrare e riprodurre i movimenti

Quello che rende davvero robot un braccio è che ripeta da solo un movimento che gli hai mostrato. Il metodo è semplice: porti il braccio in una posa con i potenziometri e premi il pulsante di registrazione, e Arduino memorizza gli angoli delle quattro articolazioni. Dopo qualche posa premi riproduci e il braccio le ripete in ordine, ammorbidendo i passaggi tra l'una e l'altra. Le pose vengono scritte nella EEPROM da 1024 byte di Arduino Uno, quindi non si cancellano spegnendo la scheda; ogni posa occupa 4 byte, perciò una registrazione di 100 pose usa solo 401 byte.

C++ (Arduino IDE)
1#include <Servo.h>
2#include <EEPROM.h>
3
4const byte SERVO_PIN[4] = {3, 5, 6, 9};        // taban, omuz, dirsek, kıskaç / base, shoulder, elbow, gripper
5const byte POT_PIN[4]   = {A0, A1, A2, A3};
6const byte MIN_A[4] = {0, 15, 20, 40};         // güvenli sınırlar / safe limits
7const byte MAX_A[4] = {180, 165, 160, 110};
8const byte BTN_REC = 7, BTN_PLAY = 8, LED = 13; // butonların diğer ucu GND'de / buttons go to GND
9const byte MAX_STEPS = 100;                    // 100 adım x 4 bayt = 400 bayt / 100 steps x 4 bytes
10
11Servo servo[4];
12float smooth[4];                               // süzülmüş pot değeri / filtered pot value
13byte pos[4];                                   // servoların şu anki açısı / current servo angles
14byte stepCount;                                // kayıtlı adım sayısı / number of saved steps
15
16bool pressed(byte pin) {                       // basılıyken true / true while pressed
17  return digitalRead(pin) == LOW;
18}
19
20byte potAngle(byte i) {
21  smooth[i] += (analogRead(POT_PIN[i]) - smooth[i]) * 0.2;   // yumuşatma / smoothing
22  return map((int)smooth[i], 0, 1023, MIN_A[i], MAX_A[i]);
23}
24
25void blink(byte n) {
26  for (byte k = 0; k < n; k++) {
27    digitalWrite(LED, HIGH); delay(80);
28    digitalWrite(LED, LOW);  delay(80);
29  }
30}
31
32// Tüm eklemleri aynı anda hedefe götürür / moves all joints to the target together
33void moveTo(const byte target[4], byte msPerDegree) {
34  byte start[4];
35  int steps = 0;
36  for (byte i = 0; i < 4; i++) {
37    start[i] = pos[i];
38    int d = abs((int)target[i] - (int)start[i]);
39    if (d > steps) steps = d;                  // en uzun hareket / longest move
40  }
41  for (int s = 1; s <= steps; s++) {
42    for (byte i = 0; i < 4; i++) {
43      pos[i] = start[i] + (long)((int)target[i] - (int)start[i]) * s / steps;   // ara açı / in-between angle
44      servo[i].write(pos[i]);
45    }
46    delay(msPerDegree);
47  }
48}
49
50void saveStep() {
51  if (stepCount >= MAX_STEPS) { blink(3); return; }           // hafıza dolu / memory full
52  for (byte i = 0; i < 4; i++) EEPROM.update(1 + stepCount * 4 + i, pos[i]);
53  stepCount++;
54  EEPROM.update(0, stepCount);                 // 0. bayt adım sayısını tutar / byte 0 holds the step count
55  blink(1);
56}
57
58void playBack() {
59  byte target[4];
60  for (byte s = 0; s < stepCount; s++) {
61    for (byte i = 0; i < 4; i++) target[i] = EEPROM.read(1 + s * 4 + i);
62    moveTo(target, 12);                        // derece başına 12 ms / 12 ms per degree
63    delay(400);                                // her pozda kısa bekleme / short pause at each pose
64    if (pressed(BTN_PLAY)) break;              // oynatırken basınca durur / press again to stop
65  }
66}
67
68void syncToPots() {                            // potların konumuna yumuşakça dön / ease back to the pots
69  byte target[4];
70  for (byte i = 0; i < 4; i++) {
71    smooth[i] = analogRead(POT_PIN[i]);
72    target[i] = map((int)smooth[i], 0, 1023, MIN_A[i], MAX_A[i]);
73  }
74  moveTo(target, 12);
75}
76
77void setup() {
78  pinMode(BTN_REC, INPUT_PULLUP);
79  pinMode(BTN_PLAY, INPUT_PULLUP);
80  pinMode(LED, OUTPUT);
81  stepCount = EEPROM.read(0);
82  if (stepCount > MAX_STEPS) stepCount = 0;    // boş EEPROM 255 okur / a blank EEPROM reads 255
83  for (byte i = 0; i < 4; i++) {
84    smooth[i] = analogRead(POT_PIN[i]);
85    pos[i] = potAngle(i);
86    servo[i].write(pos[i]);                    // önce açı, sonra attach / angle first, then attach
87    servo[i].attach(SERVO_PIN[i]);
88    delay(300);
89  }
90}
91
92void loop() {
93  // Kayıt: kısa bas = pozu kaydet, 2 sn basılı tut = hepsini sil / Record: short press = save pose, hold 2 s = erase all
94  if (pressed(BTN_REC)) {
95    unsigned long t0 = millis();
96    while (pressed(BTN_REC) && millis() - t0 < 2000) {}
97    if (millis() - t0 >= 2000) {
98      stepCount = 0;
99      EEPROM.update(0, 0);
100      blink(5);
101    } else {
102      saveStep();
103    }
104    while (pressed(BTN_REC)) {}                // bırakılmasını bekle / wait for release
105    delay(50);                                 // buton arkı / debounce
106  }
107
108  // Oynat: kaydedilen pozları sırayla tekrarla / play: repeat the saved poses in order
109  if (pressed(BTN_PLAY) && stepCount > 0) {
110    while (pressed(BTN_PLAY)) {}
111    delay(50);
112    digitalWrite(LED, HIGH);
113    playBack();
114    while (pressed(BTN_PLAY)) {}
115    syncToPots();
116    digitalWrite(LED, LOW);
117  }
118
119  // Normal mod: kolu potlarla sür / normal mode: drive the arm with the pots
120  for (byte i = 0; i < 4; i++) {
121    byte a = potAngle(i);
122    if (abs((int)a - (int)pos[i]) >= 2) {
123      pos[i] = a;
124      servo[i].write(a);
125    }
126  }
127  delay(15);
128}
AzioneCosa faiCosa fa il braccio
Salvare una posaPressione breve del pulsante di registrazione (D7)Il LED lampeggia una volta, la posa viene scritta nella EEPROM
Cancellare le registrazioniTieni premuto il pulsante di registrazione per 2 secondiIl LED lampeggia cinque volte, il contatore dei passi si azzera
RiprodurrePremi e rilascia il pulsante di riproduzione (D8)Il LED si accende, le pose si ripetono in ordine con passaggi morbidi
Fermare la riproduzioneTieni premuto il pulsante di riproduzione durante la riproduzioneIl braccio completa la posa in corso e si ferma
Tornare alla modalità normaleNon fare nulla a fine riproduzioneIl braccio torna lentamente dove indicano i potenziometri, il LED si spegne

La funzione moveTo() è il cuore dello sketch: trova l'articolazione con il percorso più lungo, divide il movimento in altrettanti passi e a ogni passo fa avanzare tutte le articolazioni in proporzione. Così arrivano tutte a destinazione nello stesso momento e il braccio si muove in modo fluido, non come un robot che muove un'articolazione alla volta. 12 ms per grado significano che un movimento di 90 gradi dura circa 1,1 secondi. Puoi abbassare il valore per una riproduzione più veloce, ma anche a vuoto un MG996R ruota di 60 gradi in 0,17 secondi, cioè circa 3 ms per grado; sotto questo valore i servo non riescono a seguire i comandi e il movimento diventa a scatti.

Ogni cella della EEPROM sopporta circa 100.000 scritture. Poiché il codice usa EEPROM.update(), scrive solo le celle il cui valore è cambiato, quindi in pratica non la consumerai mai. Per sequenze più lunghe puoi salvare le registrazioni su una scheda SD o usare la EEPROM da 4 KB di Arduino Mega.

Driver per servo PCA9685: 6 servo e un segnale senza tremolii

Quando passi a 5–6 servo o aggiungi moduli come il Bluetooth, un modulo PCA9685 ti semplifica parecchio la vita. Questa scheda è un generatore PWM a 16 canali con risoluzione di 12 bit; comunica con Arduino su soli due fili tramite I2C e genera gli impulsi dei servo con il proprio clock, indipendentemente dal processore. L'indirizzo predefinito è 0x40; cambiando l'indirizzo con i ponticelli a saldare A0–A5 puoi collegare in catena fino a 62 schede, cioè 992 servo. A 50 Hz il periodo di 20 ms è diviso in 4096 passi, quindi un passo vale circa 4,9 µs; l'intervallo 544–2400 µs copre circa 380 passi, il che dà una risoluzione di circa mezzo grado.

Pin del PCA9685Arduino UnoESP32Raspberry Pi Pico
GNDGNDGNDGND
VCC (solo alimentazione del chip)5V3V33V3
SDAA4GPIO 21GP4
SCLA5GPIO 22GP5
Morsetto V+ (alimentazione servo)5–6 V esterni5–6 V esterni5–6 V esterni
OELascialo scollegatoLascialo scollegatoLascialo scollegato
Connettori servo 0–5Base, spalla, gomito, polso, rotazione del polso, pinzaStesso ordineStesso ordine
Il PCA9685 ha due alimentazioni separate: VCC alimenta solo il chip, mentre i servo prendono corrente dall'ingresso V+ sul morsetto. Se V+ resta senza tensione, la scheda compare sul bus I2C ma i servo non si muovono. Su Arduino Uno, A4 e A5 servono per l'I2C, quindi con il PCA9685 collegato restano come ingressi analogici solo A0–A3; se ti servono più potenziometri, usa un Arduino Nano con gli ingressi extra A6 e A7.

Controllare il braccio robotico con i joystick

Un joystick funziona in modo diverso da un potenziometro: quando lo lasci torna al centro. Se leghi l'angolo direttamente alla posizione del joystick, l'articolazione scatta a 90 gradi appena lo rilasci. Il metodo giusto è il controllo di velocità: più spingi il joystick, più veloce ruota l'articolazione, e quando lo lasci resta dov'è. Nel codice qui sotto due joystick muovono quattro articolazioni (quello sinistro base e spalla, quello destro gomito e polso); il pulsante del joystick sinistro apre e chiude la pinza, quello del destro riporta il braccio nella posa iniziale. Collega le uscite VRx e VRy del joystick sinistro ad A0 e A1, VRy e VRx del destro ad A2 e A3 e i pin SW a D2 e D4; i joystick si possono alimentare dal pin 5V di Arduino. Per compilare il codice installa Adafruit PWM Servo Driver Library dal Gestore librerie e accetta la dipendenza Adafruit BusIO.

Modulo joystick analogico a due assi
Modulo joystick: le uscite VRx e VRy leggono circa 512 a riposo e il pulsante SW porta il segnale a GND quando lo premi. Con due moduli muovi quattro articolazioni di un braccio robotico.
C++ (Arduino IDE)
1#include <Wire.h>
2#include <Adafruit_PWMServoDriver.h>
3
4Adafruit_PWMServoDriver pca = Adafruit_PWMServoDriver(0x40);   // varsayılan adres / default address
5
6// Kanal: 0 taban, 1 omuz, 2 dirsek, 3 bilek, 4 bilek dönüşü, 5 kıskaç / Channel: 0 base, 1 shoulder, 2 elbow, 3 wrist, 4 wrist roll, 5 gripper
7const byte N = 6;
8const float MIN_A[N] = {0, 15, 20, 10, 0, 40};        // güvenli sınırlar / safe limits
9const float MAX_A[N] = {180, 165, 160, 170, 180, 110};
10const float HOME[N]  = {90, 90, 90, 90, 90, 40};      // başlangıç pozu, kıskaç açık / home pose, gripper open
11const byte JOY[4] = {A0, A1, A2, A3};                  // J1 X, J1 Y, J2 Y, J2 X → kanal / channel 0, 1, 2, 3
12const int8_t DIR[4] = {1, -1, 1, 1};                   // ters giden ekseni -1 yap / set -1 to invert an axis
13const byte BTN_GRIP = 2, BTN_HOME = 4;                 // joystick SW pinleri / joystick SW pins
14const float MAX_SPEED = 90;                            // en yüksek hız, derece/sn / top speed, degrees/s
15const float DT = 0.02;                                 // 20 ms döngü / 20 ms loop
16const int DEADZONE = 60;                               // ortadaki ölü bölge / dead zone around center
17
18float target[N], current[N];
19bool gripClosed = false;
20
21void writeServo(byte ch, float a) {
22  // Servo.h ile aynı 544–2400 µs aralığı / same 544–2400 µs range as Servo.h
23  pca.writeMicroseconds(ch, (uint16_t)(544 + a * (2400 - 544) / 180.0));
24}
25
26bool newPress(byte pin, byte k) {                      // her basışta bir kez true / true once per press
27  static bool prev[2] = {HIGH, HIGH};
28  bool now = digitalRead(pin);
29  bool edge = (prev[k] == HIGH && now == LOW);
30  prev[k] = now;
31  return edge;
32}
33
34void setup() {
35  pinMode(BTN_GRIP, INPUT_PULLUP);
36  pinMode(BTN_HOME, INPUT_PULLUP);
37  pca.begin();
38  pca.setOscillatorFrequency(27000000);                // çoğu modülde ~27 MHz / ~27 MHz on most boards
39  pca.setPWMFreq(50);                                  // servolar için 50 Hz / 50 Hz for servos
40  Wire.setClock(400000);                               // hızlı I2C / fast I2C
41  for (byte i = 0; i < N; i++) {
42    target[i] = current[i] = HOME[i];
43    writeServo(i, HOME[i]);
44    delay(300);                                        // servolar sırayla uyansın / wake servos one by one
45  }
46}
47
48void loop() {
49  static unsigned long last = 0;
50  if (millis() - last < 20) return;                    // 50 Hz güncelleme / 50 Hz update
51  last = millis();
52
53  // Joystick hızı belirler, konumu değil / the joystick sets speed, not position
54  for (byte i = 0; i < 4; i++) {
55    int v = analogRead(JOY[i]) - 512;
56    if (abs(v) > DEADZONE) {
57      target[i] += DIR[i] * (v / 512.0) * MAX_SPEED * DT;
58      target[i] = constrain(target[i], MIN_A[i], MAX_A[i]);
59    }
60  }
61  if (newPress(BTN_GRIP, 0)) {                         // kıskaç aç/kapa / toggle gripper
62    gripClosed = !gripClosed;
63    target[5] = gripClosed ? MAX_A[5] : MIN_A[5];
64  }
65  if (newPress(BTN_HOME, 1)) {                         // başlangıç pozuna dön / go to home pose
66    for (byte i = 0; i < N; i++) target[i] = HOME[i];
67    gripClosed = false;
68  }
69
70  // Her eklem hedefe hız sınırıyla gider / each joint follows its target with a speed limit
71  for (byte i = 0; i < N; i++) {
72    float d = constrain(target[i] - current[i], -MAX_SPEED * DT, MAX_SPEED * DT);
73    if (d != 0) {
74      current[i] += d;
75      writeServo(i, current[i]);
76    }
77  }
78}

DEADZONE evita che piccoli scostamenti come 490–530, letti con il joystick al centro, facciano scivolare il braccio da solo. MAX_SPEED stabilisce la velocità, in gradi al secondo, con il joystick spinto a fondo; 90 gradi al secondo è un valore sicuro per iniziare. Se un'articolazione va al contrario rispetto al joystick, non invertire i fili: imposta a -1 il valore corrispondente nell'array DIR. Il codice tiene per ogni articolazione un obiettivo (target) e una posizione reale (current); poiché la posizione reale raggiunge l'obiettivo con un limite di velocità, il braccio torna in modo fluido, senza scatti, anche quando premi il pulsante della posa iniziale. La rotazione del polso resta fissa a 90 gradi in questo sketch; puoi comandarla con il comando R della sezione Bluetooth. I dettagli di questa temporizzazione non bloccante sono spiegati in millis() invece di delay().

Controllo Bluetooth dallo smartphone (HC-05)

Il modulo HC-05 crea un collegamento seriale senza fili tra lo smartphone e Arduino. Il suo pin TXD va al D10 di Arduino, mentre il pin RXD si collega a D11 tramite un partitore di tensione: D11 → 1 kΩ → RXD → 2 kΩ → GND. Il partitore abbassa il segnale a 5 V di Arduino ai circa 3,3 V che si aspetta l'HC-05. La velocità dati predefinita è 9600 baud e il PIN di associazione di solito è 1234. Lo smartphone invia semplici comandi di testo: una lettera sceglie l'articolazione, il numero che segue indica l'angolo e un a capo o un punto e virgola chiude il comando.

Modulo seriale Bluetooth HC-05
HC-05: modulo seriale che si associa allo smartphone via Bluetooth classico; VCC accetta 5 V, ma l'ingresso RXD lavora con livelli a 3,3 V.
ComandoSignificatoEsempio
B + angoloBaseB90
S + angoloSpallaS120
E + angoloGomitoE45
W + angoloPolso (su e giù)W100
R + angoloRotazione del polsoR0
G + angoloPinzaG110 (chiudi), G40 (apri)
HTorna alla posa inizialeH
K / P / X / CSalva posa / riproduci in loop / ferma / cancella le poseK
C++ (Arduino IDE)
1#include <Wire.h>
2#include <SoftwareSerial.h>
3#include <Adafruit_PWMServoDriver.h>
4
5SoftwareSerial bt(10, 11);                 // D10 ← HC-05 TXD, D11 → bölücü / divider → HC-05 RXD
6Adafruit_PWMServoDriver pca = Adafruit_PWMServoDriver(0x40);
7
8const byte N = 6;
9const char NAME[N] = {'B', 'S', 'E', 'W', 'R', 'G'};   // taban, omuz, dirsek, bilek, dönüş, kıskaç / base, shoulder, elbow, wrist, roll, gripper
10const byte MIN_A[N] = {0, 15, 20, 10, 0, 40};          // güvenli sınırlar / safe limits
11const byte MAX_A[N] = {180, 165, 160, 170, 180, 110};
12const byte HOME[N]  = {90, 90, 90, 90, 90, 40};
13const byte MAX_POSES = 30;
14const float SPEED = 1.5;                   // 20 ms'de en fazla 1,5° (75°/sn) / max 1.5° per 20 ms (75°/s)
15
16float current[N];
17byte target[N];
18byte poses[MAX_POSES][N];                  // kaydedilen pozlar (RAM) / recorded poses (RAM)
19byte poseCount = 0, playIndex = 0;
20bool playing = false;
21char buf[12];
22byte len = 0;
23
24void writeServo(byte ch, float a) {
25  pca.writeMicroseconds(ch, (uint16_t)(544 + a * (2400 - 544) / 180.0));   // 544–2400 µs
26}
27
28void handle(char *cmd) {                   // ör. / e.g. "S120", "H", "K", "P"
29  char c = toupper(cmd[0]);
30  if (c == 'H') {                          // başlangıç pozu / home pose
31    for (byte i = 0; i < N; i++) target[i] = HOME[i];
32    playing = false;
33  } else if (c == 'K') {                   // pozu kaydet / save pose
34    if (poseCount < MAX_POSES) {
35      for (byte i = 0; i < N; i++) poses[poseCount][i] = target[i];
36      poseCount++;
37    }
38    bt.print(F("Poz / pose: "));
39    bt.println(poseCount);
40  } else if (c == 'P') {                   // döngüyle oynat / play in a loop
41    if (poseCount > 0) { playing = true; playIndex = 0; }
42  } else if (c == 'X') {                   // durdur / stop
43    playing = false;
44  } else if (c == 'C') {                   // kayıtları sil / clear poses
45    poseCount = 0;
46    playing = false;
47  } else {
48    for (byte i = 0; i < N; i++) {
49      if (c == NAME[i]) {
50        target[i] = constrain(atoi(cmd + 1), MIN_A[i], MAX_A[i]);
51        playing = false;                   // elle komut oynatmayı keser / a manual command stops playback
52      }
53    }
54  }
55}
56
57bool arrived() {
58  for (byte i = 0; i < N; i++)
59    if (fabs(target[i] - current[i]) > 0.5) return false;
60  return true;
61}
62
63void setup() {
64  bt.begin(9600);                          // HC-05 varsayılan hızı / HC-05 default baud
65  pca.begin();
66  pca.setOscillatorFrequency(27000000);
67  pca.setPWMFreq(50);
68  for (byte i = 0; i < N; i++) {
69    target[i] = HOME[i];
70    current[i] = HOME[i];
71    writeServo(i, HOME[i]);
72    delay(300);                            // servolar sırayla uyansın / wake servos one by one
73  }
74}
75
76void loop() {
77  // Komutları satır satır topla / collect commands line by line
78  while (bt.available()) {
79    char ch = bt.read();
80    if (ch == '\n' || ch == '\r' || ch == ';') {
81      if (len > 0) { buf[len] = '\0'; handle(buf); len = 0; }
82    } else if (len < sizeof(buf) - 1) {
83      buf[len++] = ch;
84    }
85  }
86
87  static unsigned long last = 0, arrivedAt = 0;
88  if (millis() - last < 20) return;        // 50 Hz güncelleme / 50 Hz update
89  last = millis();
90
91  if (playing) {
92    if (!arrived()) arrivedAt = millis();  // hareket sürüyor / still moving
93    else if (millis() - arrivedAt > 300) { // her pozda 300 ms bekle / hold each pose 300 ms
94      for (byte i = 0; i < N; i++) target[i] = poses[playIndex][i];
95      playIndex = (playIndex + 1) % poseCount;   // sona gelince başa dön / wrap around
96      arrivedAt = millis();
97    }
98  }
99
100  for (byte i = 0; i < N; i++) {
101    float d = constrain(target[i] - current[i], -SPEED, SPEED);   // hız sınırı / speed limit
102    if (d != 0) {
103      current[i] += d;
104      writeServo(i, current[i]);
105    }
106  }
107}

In questo sketch i servo sono collegati al PCA9685 e non direttamente ad Arduino, ed è una scelta voluta. SoftwareSerial disattiva gli interrupt per circa 1 ms mentre riceve ogni byte; se i servo fossero pilotati dai pin di Arduino con la libreria Servo, in quel momento i loro impulsi si allungherebbero e il braccio avrebbe uno scatto a ogni comando. Poiché il PCA9685 genera da solo gli impulsi, il segnale resta stabile per quanto intenso sia il traffico Bluetooth. Ora anche registrazione e riproduzione si fanno dallo smartphone: porti il braccio in una posa con i cursori e invii K; dopo qualche posa, con P il braccio le ripete in loop e con X si ferma. In questa versione le pose restano nella RAM e si perdono quando spegni la scheda; se vuoi conservarle, aggiungi il metodo EEPROM dello sketch precedente.

Sullo smartphone basta un'app già pronta: installa su Android un terminale seriale Bluetooth o un'app di controllo Bluetooth con cursori e assegna a ogni cursore un prefisso come "S" e un a capo. Se vuoi creare la tua interfaccia con pulsanti e cursori, la guida app Bluetooth con MIT App Inventor ti accompagna passo dopo passo. Poiché l'HC-05 usa il Bluetooth classico, non funziona con l'iPhone; per l'iPhone ti serve una soluzione Bluetooth Low Energy come l'ESP32 BLE.

Braccio robotico wireless con ESP32

Poiché l'ESP32 ha il Bluetooth classico integrato, l'HC-05 non serve: con la libreria BluetoothSerial la scheda compare sullo smartphone come "BlueGrays-RobotKol" e capisce gli stessi comandi. Collega il PCA9685 a GPIO 21 (SDA) e GPIO 22 (SCL) e alimenta VCC dal 3V3; i segnali dei servo escono così a 3,3 V, che la stragrande maggioranza dei servo legge senza problemi. BluetoothSerial funziona solo sull'ESP32 classico (ESP32-WROOM-32, DevKit); ESP32-S2, S3 e C3 non supportano il Bluetooth classico.

C++ (Arduino IDE)
1#include <Wire.h>
2#include <BluetoothSerial.h>
3#include <Adafruit_PWMServoDriver.h>
4
5BluetoothSerial bt;                        // klasik Bluetooth SPP / classic Bluetooth SPP
6Adafruit_PWMServoDriver pca = Adafruit_PWMServoDriver(0x40);
7
8const int N = 6;
9const char NAME[N] = {'B', 'S', 'E', 'W', 'R', 'G'};   // taban, omuz, dirsek, bilek, dönüş, kıskaç / base, shoulder, elbow, wrist, roll, gripper
10const int MIN_A[N] = {0, 15, 20, 10, 0, 40};
11const int MAX_A[N] = {180, 165, 160, 170, 180, 110};
12const int HOME[N]  = {90, 90, 90, 90, 90, 40};
13float current[N], target[N];
14String line;
15
16void writeServo(int ch, float a) {
17  pca.writeMicroseconds(ch, (uint16_t)(544 + a * (2400 - 544) / 180.0));   // 544–2400 µs
18}
19
20void handle(String cmd) {                  // ör. / e.g. "E45", "H"
21  cmd.trim();
22  if (cmd.length() == 0) return;
23  char c = toupper(cmd[0]);
24  if (c == 'H') {
25    for (int i = 0; i < N; i++) target[i] = HOME[i];
26    return;
27  }
28  int value = cmd.substring(1).toInt();
29  for (int i = 0; i < N; i++)
30    if (c == NAME[i]) target[i] = constrain(value, MIN_A[i], MAX_A[i]);
31}
32
33void setup() {
34  Serial.begin(115200);
35  bt.begin("BlueGrays-RobotKol");          // telefonda görünen ad / name shown on the phone
36  Wire.begin(21, 22);                      // SDA = GPIO 21, SCL = GPIO 22
37  pca.begin();
38  pca.setOscillatorFrequency(27000000);
39  pca.setPWMFreq(50);
40  for (int i = 0; i < N; i++) {
41    current[i] = target[i] = HOME[i];
42    writeServo(i, HOME[i]);
43    delay(300);
44  }
45  Serial.println("Bluetooth hazir / ready");
46}
47
48void loop() {
49  while (bt.available()) {
50    char ch = bt.read();
51    if (ch == '\n' || ch == ';') { handle(line); line = ""; }
52    else if (line.length() < 12) line += ch;
53  }
54
55  static unsigned long last = 0;
56  if (millis() - last < 20) return;        // 50 Hz güncelleme / 50 Hz update
57  last = millis();
58  for (int i = 0; i < N; i++) {
59    float d = constrain(target[i] - current[i], -1.5f, 1.5f);   // yumuşak hareket / smooth motion
60    if (d != 0) {
61      current[i] += d;
62      writeServo(i, current[i]);
63    }
64  }
65}

Braccio robotico con Raspberry Pi Pico (MicroPython)

Per pilotare servo con il Raspberry Pi Pico non serve alcuna libreria aggiuntiva: imposti il PWM a 50 Hz e indichi la larghezza dell'impulso in nanosecondi con duty_ns(). Con 16 canali PWM il Pico può pilotare 6 servo direttamente; le coppie di pin che condividono lo stesso slice hanno la stessa frequenza, ma dato che tutti i servo usano 50 Hz non è un problema. Il limite è sul lato analogico: il Pico ha solo tre pin ADC utilizzabili (GP26, GP27, GP28). Per questo nel codice qui sotto tre articolazioni sono comandate dai potenziometri e la pinza si apre e si chiude con un pulsante su GP14. Visto che il Pico lavora a 3,3 V, collega i potenziometri a 3V3; i servo restano alimentati da una sorgente esterna a 5 V.

MicroPython
1from machine import Pin, PWM, ADC
2import time
3
4# Servo pinleri: taban, omuz, dirsek, kıskaç / servo pins: base, shoulder, elbow, gripper
5servos = [PWM(Pin(p)) for p in (10, 11, 12, 13)]
6for s in servos:
7    s.freq(50)                                  # 50 Hz servo sinyali / 50 Hz servo signal
8
9pots = [ADC(26), ADC(27), ADC(28)]              # Pico'da 3 ADC pini var / the Pico has 3 ADC pins
10button = Pin(14, Pin.IN, Pin.PULL_UP)           # kıskaç butonu GND'ye / gripper button to GND
11
12MIN_A = (0, 15, 20, 40)                         # güvenli sınırlar / safe limits
13MAX_A = (180, 165, 160, 110)
14
15def write(i, angle):
16    angle = max(MIN_A[i], min(MAX_A[i], angle))
17    servos[i].duty_ns(544000 + int(angle * 10311))   # 544–2400 µs aralığı / 544–2400 µs range
18
19smooth = [p.read_u16() for p in pots]
20grip_closed = False
21last_btn = 1
22write(3, MIN_A[3])                              # kıskaç açık başlar / gripper starts open
23
24while True:
25    for i in range(3):
26        smooth[i] += (pots[i].read_u16() - smooth[i]) * 0.2   # yumuşatma / smoothing
27        write(i, MIN_A[i] + (MAX_A[i] - MIN_A[i]) * smooth[i] / 65535)
28    b = button.value()
29    if b == 0 and last_btn == 1:                # yeni basış / new press
30        grip_closed = not grip_closed
31        write(3, MAX_A[3] if grip_closed else MIN_A[3])
32    last_btn = b
33    time.sleep_ms(20)                           # 50 Hz döngü / 50 Hz loop

Un passo avanti: la cinematica inversa

Finora abbiamo dato l'angolo di ogni articolazione uno per uno; si chiama cinematica diretta. La cinematica inversa fa il contrario: indichi il punto (x, y) che vuoi far raggiungere alla pinza e il codice calcola gli angoli necessari. Per un braccio a due segmenti con lunghezza spalla–gomito L1 e gomito–polso L2 si calcola prima D = (x² + y² − L1² − L2²) / (2·L1·L2). L'angolo del gomito è θ2 = acos(D) e quello della spalla θ1 = atan2(y, x) + atan2(L2·sin θ2, L1 + L2·cos θ2) (soluzione con gomito in alto). Per esempio, con L1 = L2 = 12 cm e un punto 15 cm davanti e 5 cm sopra la spalla si ottiene D ≈ −0,13, θ2 ≈ 97,6° e θ1 ≈ 67,2°: il braccio superiore punta 67 gradi sopra l'orizzontale e l'avambraccio si piega di 97,6 gradi verso il basso rispetto a esso. Se il valore assoluto di D supera 1, il punto è fuori dalla portata del braccio. L'angolo della base si calcola a parte, dalla posizione dell'obiettivo vista dall'alto. Quando converti i risultati in angoli dei servo, considera il verso di montaggio e il punto centrale di ciascun servo.

Tremolii e reset: errori comuni e soluzioni

SintomoCausa probabileSoluzione
I servo tremano e ronzano a braccio fermoRumore nelle letture dei potenziometri o alimentazione deboleAggiungi un filtro di livellamento e una banda morta di 2°, usa un alimentatore esterno e un condensatore da 1000 µF
Arduino si resetta quando il braccio si muove, l'HC-05 perde la connessioneLa corrente di spunto dei servo fa crollare la tensioneAlimenta i servo con una sorgente separata da 5–6 V, unisci le masse, avvia i servo uno alla volta
I servo hanno uno scatto a ogni comando BluetoothGli interrupt di SoftwareSerial disturbano gli impulsi della libreria ServoSposta i servo su un PCA9685 o usa il Bluetooth integrato dell'ESP32
All'accensione il braccio scatta bruscamenteI servo non ricordano l'ultima posizione e al primo comando vanno a piena velocitàRiporta il braccio nella posa iniziale prima di spegnere, chiama write() prima di attach()
La spalla non solleva il braccio o si abbassa lentamenteCoppia insufficienteFai il calcolo della coppia; usa un MG996R, un braccio più corto, un contrappeso o una molla
Un'articolazione si blocca a fine corsa e si scaldaIl servo spinge contro un finecorsa meccanicoInserisci nel codice i limiti MIN_A e MAX_A trovati in calibrazione
Il servo della pinza si scalda mentre tiene un oggettoLa pinza continua a voler chiudere e il servo resta in stalloImposta l'angolo chiuso nel punto in cui tocca appena l'oggetto e aggiungi gomma alle dita
Il PCA9685 è collegato ma nessun servo si muoveNessuna tensione sul morsetto V+ o massa non comunePorta 5–6 V su V+ e controlla l'indirizzo I2C (0x40)
Un'articolazione slitta sotto caricoLa squadretta di plastica si è spanata o manca la vite centraleUsa una squadretta metallica e fissala all'albero con la vite centrale
Un modo rapido per capire se il problema è l'alimentazione o il codice: esegui lo sketch di calibrazione (CENTER_ONLY = false) senza servo collegati e osserva gli angoli nel Monitor seriale mentre giri il potenziometro. Se i valori sono corretti, il problema è l'alimentazione; misura con un multimetro la tensione sulla linea dei servo mentre il braccio si muove e, se scende sotto 4,5 V, alimentatore o cavi non sono adeguati. Tutte le cause dei reset sono spiegate in Perché il mio Arduino si resetta?.

Idee di progetto con il braccio robotico

  • Braccio che smista i colori: Leggi il colore di alcuni cubetti con un sensore di colore TCS3200 e fai posare al braccio ogni colore nella sua scatola.
  • Controllo con i gesti: Con un MPU6050 su un guanto, il braccio robotico si inclina come il tuo polso.
  • Telecomando wireless: Collega due joystick a un secondo Arduino e invia i comandi con un nRF24L01; ottieni un telecomando che non ha bisogno dello smartphone.
  • Controllo dal browser: Crea dei cursori su una pagina web con un ESP32 e comanda il braccio da qualsiasi dispositivo della stessa rete Wi-Fi; un WebSocket su ESP32 riduce al minimo la latenza.
  • Prelievo dal nastro: Costruisci un piccolo nastro trasportatore con un motore passo-passo; quando un TCRT5000 vede un oggetto, il braccio lo preleva dal nastro.
  • Gara e progetto scolastico: Un braccio che registra e riproduce i movimenti è un'ottima scelta tra le idee per progetti da concorso, perché mostra insieme meccanica, elettronica e software.

Un braccio robotico Arduino ti insegna nello stesso momento progettazione meccanica, calcolo della coppia, gestione dell'alimentazione e programmazione di movimenti fluidi, e cresce con ogni ora che gli dedichi. Inizia con un braccio a 4 servo comandato da potenziometri, aggiungi registrazione e riproduzione, poi passa a 6 servo con PCA9685 e Bluetooth. Nell'app BlueGrays trovi progetti come il controllo dei servo con potenziometro e joystick, il controllo Bluetooth dallo smartphone e una coreografia con 32 servo, completi di schemi elettrici e codice passo passo, così puoi provare prima ogni articolazione del tuo braccio da sola.

Domande frequenti

Quale servo usare per un braccio robotico Arduino?

Per le articolazioni che reggono il carico, come base, spalla e gomito, il MG996R con ingranaggi metallici e 9–11 kg·cm di coppia è un'ottima scelta. Per le articolazioni leggere, come polso e pinza, bastano un MG90S o un SG90. I piccoli bracci fatti solo con SG90 funzionano se restano corti e leggeri, ma sollevano solo oggetti di qualche decina di grammi.

Che alimentatore serve per un braccio robotico?

Per un piccolo braccio con 4 servo SG90 o MG90S basta un alimentatore da 5 V 3 A. Per un braccio a 6 servo con 3 MG996R e 3 servo piccoli serve un alimentatore switching da 5–6 V e almeno 5 A, meglio 10 A per avere margine. Non dimenticare di collegare il negativo dell'alimentatore al GND di Arduino.

Quanti servo si possono collegare ad Arduino?

La libreria Servo gestisce fino a 12 servo su Arduino Uno e 48 sul Mega. Il vero limite però è la corrente: tutti i servo vanno alimentati da una sorgente esterna. Un modulo PCA9685 pilota 16 servo usando solo due pin I2C e si può collegare in catena fino a 62 moduli.

Perché i servo del mio braccio robotico tremano?

Le cause più comuni sono un'alimentazione insufficiente, la mancanza di una massa comune e il rumore nelle letture dei potenziometri. Usa un alimentatore esterno con un condensatore da 1000 µF sulla linea dei servo e applica nel codice un filtro di livellamento e una banda morta. Se usi SoftwareSerial insieme alla libreria Servo, spostare i servo su un PCA9685 elimina del tutto il tremolio.

Quanto peso può sollevare un braccio robotico con SG90?

La coppia di stallo del SG90 è di circa 1,8 kg·cm; per un uso sicuro considera la metà, cioè 0,9 kg·cm. Dato che il peso del braccio e dei servo consuma già parte di questo margine, in pratica un braccio con SG90 lungo circa 15 cm porta solo oggetti leggeri da 20–30 g, come un tappo di bottiglia o un piccolo cubetto di spugna.

Come si controlla un braccio robotico dallo smartphone?

Si collega ad Arduino un modulo Bluetooth HC-05 e lo smartphone invia comandi di testo come "S120" da un terminale seriale Bluetooth o da un'app di controllo con cursori. Con un ESP32 non serve un modulo aggiuntivo grazie al Bluetooth integrato. L'HC-05 non funziona con l'iPhone, quindi per l'iPhone serve un modulo BLE o l'ESP32 BLE.

Vedi tutti i progetti
Funzionamento del servomotore con potenziometro
#36Principiante
Premium

Funzionamento del servomotore con potenziometro

Mentre giri la manopola del potenziometro, il braccio del servomotore ruota insieme a lei: un'estremità della manopola corrisponde a zero gradi, l'altra a mezzo giro. L'Arduino legge di continuo la po

4.6(37)
52
Servocomando con joystick
#60Intermedio
Premium

Servocomando con joystick

Comanda due motori con un joystick, proprio come se girassi una telecamera di sicurezza. Spingendo la levetta a destra o a sinistra il servomotore in basso ruota piano, spingendola su o giù quello in

4.7(42)
58
Utilizzo del servomotore
#35Principiante
Premium

Utilizzo del servomotore

Il braccio di un piccolo servomotore ruota a turno in tre posizioni: tutto all'inizio, esattamente a metà e tutto alla fine, aspettando due secondi a ogni fermata. L'Arduino dice al motore a quale ang

4.7(48)
67
Servomotore ESP32 (controllo tramite potenziometro)
#118Intermedio
Premium

Servomotore ESP32 (controllo tramite potenziometro)

In questo progetto controlliamo un servomotore SG90 tramite un potenziometro. Regoliamo l'angolo del servomotore mappando il valore analogico letto dal potenziometro su un intervallo compreso tra 0 e 180 gradi. La libreria ESP32Servo genera segnali per servomotori sui GPIO adatti dell'ESP32 che possono funzionare come uscite; in questo progetto si utilizza GPIO 18. Si tratta di un metodo di controllo fondamentale per bracci robotici, gimbal per telecamere e meccanismi di porte automatiche.

4.3(29)
42
Controllare un LED dal telefono tramite Bluetooth
#34Principiante
Premium

Controllare un LED dal telefono tramite Bluetooth

Una piccola prova per accendere e spegnere le luci della tua stanza dal telefono. Il modulo Bluetooth passa all'Arduino le lettere che invii dal telefono; una lettera accende il LED rosso, un'altra qu

4.8(68)
89
Auto controllata via Bluetooth con L298N
#80Avanzato
Premium

Auto controllata via Bluetooth con L298N

Trasforma il telefono in un telecomando e guida la tua auto robot. L'Arduino riceve tramite un modulo Bluetooth i comandi di un'app di controllo remoto sul telefono e fa girare le ruote con un driver

5.0(38)
65
Coreografia di servo in cerchio - Animazioni con 32 servomotori
#102Avanzato
Premium

Coreografia di servo in cerchio - Animazioni con 32 servomotori

Un progetto di animazione visiva che utilizza 32 servomotori disposti in cerchio su un Arduino Mega. I servo sono gestiti in un unico array e collegati ai pin 22-53 del Mega per visualizzare quattro diverse sequenze di movimento: una vibrazione caotica in cui tutti i servo si muovono ad angoli casuali, uno sweep sincrono in cui si muovono tutti allo stesso tempo, un'onda che ruota dolcemente intorno a un cerchio e infine un ago di bussola che oscilla a destra e a sinistra. L'effetto onda vortico

4.5(40)
62