Guias · 20 min de leitura

Braço robótico com Arduino: escolha do servo, torque, PCA9685 e Bluetooth

Monte um braço robótico com Arduino passo a passo: estrutura em MDF ou impressa em 3D, cálculo de torque do MG996R, controle por potenciômetro e joystick, gravar/reproduzir, PCA9685 e Bluetooth.

Um braço robótico com Arduino é um robô de mesa cujas articulações, movidas por servos, permitem pegar um objeto, levantá-lo, girar e colocá-lo em outro lugar. Formado por base, ombro, cotovelo, punho e garra, ele ensina projeto mecânico, escolha de motores, gerenciamento de alimentação e programação em um único projeto. Melhor ainda: você pode montar um em casa com um Arduino Uno, alguns servos e peças de MDF ou impressas em 3D, e ter uma pequena cópia de um braço robótico industrial funcionando na sua mesa.

Neste guia, cobrimos braços robóticos de 4 e 6 eixos do começo ao fim: a escolha do material da estrutura, um cálculo de torque para decidir entre SG90, MG90S e MG996R, uma fonte externa de 5 V, controle com potenciômetros e joysticks, gravação e reprodução de movimentos, o driver de servos de 16 canais PCA9685, controle por Bluetooth pelo celular e as soluções para os dois problemas mais comuns: tremedeira e resets. Se você não tem certeza de como um servo funciona, comece por O que é servo motor?.

Servo SG90
Servo SG90
Servo MG90S
Servo MG90S
Servo MG996R
Servo MG996R

Como funciona um braço robótico e quantos eixos você precisa?

Pense no braço robótico como um braço humano. Cada articulação tem um servo motor, e o Arduino diz a cada servo “vá para este ângulo”. Graças ao potenciômetro interno, o servo sabe onde está o eixo e mantém esse ângulo mesmo sob carga. Mudando os ângulos das articulações em conjunto, você leva a garra até o ponto desejado. O número de movimentos independentes que um braço consegue fazer se chama graus de liberdade (DOF), ou simplesmente eixos.

ArticulaçãoMovimentoCargaServo recomendado
Base (cintura)Gira o braço para a esquerda e para a direitaNão levanta nada, mas sustenta o braço inteiroMG996R + rolamento
OmbroLevanta o braço para frente e para trásA articulação mais carregadaMG996R ou um servo da classe de 20 kg·cm
CotoveloDobra o antebraçoMédia a altaMG996R
Punho (inclinação)Ajusta o ângulo da garraBaixaMG90S
Rotação do punhoGira a garra em torno do próprio eixoMuito baixaMG90S ou SG90
GarraPega e solta o objetoForça de apertoSG90 ou MG90S

A maioria dos kits de “braço robótico de 6 eixos” à venda tem 6 servos; como a garra não conta como eixo, tecnicamente são um braço de 5 DOF mais uma garra. Para o seu primeiro braço, um projeto de 4 eixos (base, ombro, cotovelo, garra) é o melhor começo: 4 servos, 4 potenciômetros e um Arduino Uno bastam, a fiação fica simples e problemas de torque são raros. As articulações do punho ajudam quando você precisa manter a garra nivelada ou pegar objetos de ângulos diferentes; ao passar para 5–6 servos, um driver PCA9685 se torna quase indispensável.

Lista de materiais

PeçaBraço de 4 eixosBraço de 6 servosObservação
ControladorArduino Uno ou NanoArduino Uno, Nano ou ESP32As entradas extras A6 e A7 do Nano permitem mais potenciômetros
Servo motores4× SG90 ou MG90S3× MG996R + 3× MG90SConfirme a escolha com o cálculo de torque abaixo
Driver de servosNão é necessárioMódulo PCA9685 de 16 canaisApenas 2 fios de sinal via I2C
Fonte de alimentaçãoFonte de 5 V 3 AFonte de 5–6 V, no mínimo 5 AOs servos nunca são alimentados pelo Arduino
Capacitor470–1000 µF, 16 V1000–2200 µF, 16 VNa linha de alimentação dos servos, atenção à polaridade
Controles4× potenciômetros de 10 kΩ2× módulos joystick ou um HC-05Você também pode usar os dois
Botões2 (gravar, reproduzir)Os botões dos joysticks bastamSem resistores extras com INPUT_PULLUP
MecânicaMDF de 3 mm ou peças impressas em 3DMDF de 4 mm, impressão 3D ou suportes de alumínioRolamento 608, parafusos M3 e porcas autotravantes

Projeto mecânico: MDF ou impressão 3D?

MaterialVantagensDesvantagensIndicado para
MDF de 3–4 mm (corte a laser)Barato, leve, fácil de parafusar e colarSensível à umidade; os furos dos parafusos alargam com o tempoPrimeiros braços no estilo MeArm, projetos escolares
Acrílico de 3 mmÓtima aparência, não é afetado pela umidadeQuebradiço; pode trincar ao apertar os parafusosMontagens para exposição e vitrine
Impressão 3D (PLA / PETG)Peças complexas, encaixes dos servos na medida exataImpressões demoradas; o PLA deforma (fluência) no calor e sob carga constanteSeus próprios projetos; PETG nas peças que suportam carga
Kit de suportes de alumínioMuito rígido, peças prontas para o MG996RPesado e caro, aumenta a carga no ombroBraços de 6 servos feitos para levantar objetos mais pesados
Papelão, palitos de picoléQuase de graça, prototipagem rápidaFrágil; as articulações ganham folgaTestar dimensões, primeiras montagens com crianças
  • Mantenha o braço curto e leve: a carga no ombro cresce na mesma proporção da distância. Fazer o braço com 24 cm em vez de 30 cm reduz o torque do ombro em cerca de um quinto.
  • Coloque os servos pesados perto da base: levar o servo do cotovelo para a altura do ombro e acionar o cotovelo com uma haste de transmissão (articulação paralela) é um truque inteligente usado em braços como o MeArm; o ombro deixa de ter que levantar esse servo.
  • Use um rolamento na base: não pendure o peso do braço inteiro no eixo do servo da base. Um rolamento 608 (furo de 8 mm, 22 mm de diâmetro externo, 7 mm de espessura; o clássico rolamento de skate) ou um rolamento de prato giratório (lazy Susan) suporta a carga, e o servo só faz girar.
  • Apoie as articulações dos dois lados: enquanto o servo segura a articulação pelo lado do braço do servo, coloque um pivô no lado oposto com um parafuso M3 e uma porca autotravante. Assim o eixo do servo não sofre esforço de flexão e as engrenagens duram muito mais.
  • Use braços de servo metálicos: coloque um braço redondo metálico de 25 dentes (25T) em cada MG996R e prenda-o ao eixo com o parafuso central. Braços plásticos escorregam e espanam o estriado nas articulações mais carregadas.
  • Coloque borracha nos dedos da garra: uma tira fina de borracha nas pontas dos dedos permite que a garra segure objetos sem escorregar e com menos força de aperto.

Escolha dos servos: SG90, MG90S ou MG996R?

ModeloTorqueVelocidade (por 60°)PesoEngrenagensCorrente de travamentoFunção no braço robótico
SG901,8 kg·cm (4,8 V)0,10 s (4,8 V)9 gPlásticoCerca de 0,6–0,75 AGarra, punho leve, braço pequeno de 4 eixos
MG90S1,8 kg·cm (4,8 V), 2,2 kg·cm (6 V)0,10 s (4,8 V)13,4 gMetalCerca de 0,7–1 APunho, rotação do punho, garra
MG996R9,4 kg·cm (4,8 V), 11 kg·cm (6 V)0,17 s (4,8 V)55 gMetal2,5 A (6 V)Base, ombro, cotovelo
DS321819 kg·cm (5 V), 21,5 kg·cm (6,8 V)0,16 s (5 V)60 gMetalCerca de 2–3 AOmbro de braços longos ou pesados

O valor de torque da tabela é o torque de travamento (stall torque): a força máxima que o servo consegue entregar antes de o eixo parar, e não um valor em que ele possa trabalhar continuamente. À medida que o servo se aproxima desse limite, a corrente sobe em direção à corrente de travamento, ele esquenta e começa a tremer. A regra prática é que o torque exigido por uma articulação seja no máximo metade do torque de travamento do servo. A tensão também conta: o MG996R entrega 9,4 kg·cm em 4,8 V e 11 kg·cm em 6 V, ou seja, o mesmo servo fica cerca de 17% mais forte só pela tensão de alimentação. Por isso uma fonte de 6 V faz sentido em braços com MG996R, mas 6 V é o limite superior para o SG90 e o MG90S. Para a ligação e os limites do MG996R, veja também o guia do servo motor MG996R.

Servo motor MG996R com engrenagens metálicas
MG996R: engrenagens metálicas, 11 kg·cm de torque em 6 V e eixo de saída com estriado de 25 dentes; a escolha padrão para a base, o ombro e o cotovelo de um braço robótico.

Cálculo de torque: o servo do ombro consegue levantar este braço?

O torque que um servo precisa levantar é a soma da massa de cada peça multiplicada pela sua distância horizontal até o eixo da articulação: Torque (kg·cm) = Σ massa (kg) × distância (cm). O pior caso é com o braço esticado na horizontal, porque todas as distâncias estão no máximo; com o braço apontando para cima, esse braço de alavanca cai quase a zero. Para converter em newton-metros, multiplique por 0,098 (1 kg·cm ≈ 0,098 N·m). Como exemplo, vamos calcular a carga no ombro de um braço de mesa com 12 cm do ombro ao cotovelo, 12 cm do cotovelo ao punho, 6 cm do punho até a ponta da garra e um MG996R no cotovelo, com a garra segurando um objeto de 50 g:

PeçaMassaDistância até o eixo do ombroTorque
Peças do braço superior (MDF e parafusos)40 g6 cm (centro de massa)0,04 × 6 = 0,24 kg·cm
Servo do cotovelo (MG996R)55 g12 cm0,055 × 12 = 0,66 kg·cm
Peças do antebraço30 g18 cm0,03 × 18 = 0,54 kg·cm
Servo do punho + garra (MG90S, SG90 e peças)50 g25 cm0,05 × 25 = 1,25 kg·cm
Objeto segurado50 g30 cm0,05 × 30 = 1,50 kg·cm
Total225 g—4,19 kg·cm (≈ 0,41 N·m)

Parado, este braço precisa de 4,19 kg·cm no ombro. Para cobrir a carga extra de acelerar e frear, e manter o servo bem longe do travamento, multiplique por 2: 8,4 kg·cm. Então o ombro precisa no mínimo de um MG996R (9,4 kg·cm em 4,8 V, 11 kg·cm em 6 V); os 1,8–2,2 kg·cm de um SG90 ou MG90S nem sequer mexem este braço. Fazendo o mesmo para o cotovelo, o antebraço (0,03 × 6), o punho com a garra (0,05 × 13) e o objeto (0,05 × 18) somam 1,73 kg·cm, ou 3,5 kg·cm com a margem de segurança, então o cotovelo também precisa de um MG996R. No punho restam só a garra e o objeto, então a carga fica em torno de 0,4 kg·cm e um MG90S sobra. O mesmo cálculo mostra o objeto mais pesado que o braço consegue carregar: aumente a massa do objeto, recalcule o total e fique abaixo da metade do torque do servo.

Há três formas baratas de reduzir o torque no ombro: encurtar o braço, levar o servo do cotovelo para o ombro e acionar o cotovelo com uma haste de transmissão, e adicionar um contrapeso atrás do ombro ou uma mola que puxe o braço para cima. Por exemplo, um peso de 400 g colocado 5 cm atrás do eixo do ombro cria 0,4 × 5 = 2 kg·cm de contratorque e quase reduz pela metade a carga do braço calculada acima; em compensação, o rolamento da base passa a suportar um pouco mais de peso.

Alimentação: por que os servos não são alimentados pelo Arduino?

A maioria dos problemas de braços robóticos vem da alimentação. Um MG996R consome 0,5–0,9 A em movimento e até 2,5 A travado; até um pequeno SG90 pode passar de 0,6 A no travamento. Quando um Arduino Uno funciona pela USB, a corrente total disponível no pino 5V fica em torno de 500 mA, o limite da USB, e a própria placa usa parte disso. Quando três servos grandes começam a se mover ao mesmo tempo, a tensão despenca, o microcontrolador reinicia e o braço volta de repente para a posição inicial. A solução é alimentar os servos com uma fonte separada de 5–6 V e ligar todos os GNDs juntos; o Arduino pode continuar sendo alimentado pela USB ou pela própria fonte. Para opções de alimentação, veja o guia Como alimentar um Arduino e, para estimar quanto tempo um braço a bateria vai funcionar, experimente a calculadora de autonomia da bateria.

Tipo de braçoCorrente estimadaFonte recomendada
Braço pequeno com 4× SG90 ou MG90S0,5–1 A em movimento, 2–3 A de picoFonte de 5 V 3 A ou um módulo LM2596 (3 A no máximo, cerca de 2 A sem dissipador)
Braço de 6 servos com 3× MG996R + 3× MG90S2–3 A em movimento, 8–10 A se todos travaremFonte chaveada de 5–6 V, no mínimo 5 A (10 A para ter folga)
Braço portátilDepende do braçoLiPo 2S (7,4 V) + um módulo abaixador (UBEC) de 5 A ou mais ajustado para 6 V
Fontes a evitar—Bateria de 9 V (PP3, não entrega a corrente), o pino 5V do Arduino, 4 pilhas alcalinas AA em um braço com MG996R
Nunca alimente servos pelo pino 5V do Arduino e sempre ligue o negativo da fonte externa ao GND do Arduino; sem um GND comum, os servos não conseguem ler o sinal e se movem aleatoriamente. Coloque um capacitor eletrolítico na linha de alimentação dos servos, bem ao lado deles, com pelo menos 100 µF por servo (1000–2200 µF e 16 V ou mais para um braço de 6 servos), com a perna mais longa no positivo; um capacitor eletrolítico montado ao contrário pode estufar e estourar. As trilhas de alimentação da protoboard são feitas para cerca de 1 A, então, em um braço com MG996R, leve os fios da fonte direto a um borne de parafuso ou à entrada V+ do PCA9685.

Ligação do braço de 4 eixos

A tabela abaixo mostra a ligação no Arduino Uno e no Raspberry Pi Pico para o braço de 4 eixos controlado por potenciômetros; o código deste guia usa estes pinos. No Uno, a biblioteca Servo consegue acionar um servo em qualquer pino digital, mas, como ela usa o Timer1, o analogWrite deixa de funcionar nos pinos 9 e 10 enquanto houver servos conectados. Você pode conferir a disposição dos pinos da sua placa na ferramenta Pinagem.

ConexãoArduino UnoRaspberry Pi Pico
Sinal do servo da base (fio laranja)D3GP10
Sinal do servo do ombroD5GP11
Sinal do servo do cotoveloD6GP12
Sinal do servo da garraD9GP13
Cursor dos potenciômetros 1–3 (base, ombro, cotovelo)A0, A1, A2GP26, GP27, GP28
Cursor do potenciômetro 4 (garra)A3Nenhum; a garra usa o botão no GP14
Terminais externos dos potenciômetros5V e GND3V3 e GND
Botões de gravar e reproduzir (outro lado no GND)D7 e D8—
Fio vermelho do servo (+)Positivo da fonte externa de 5–6 VPositivo da fonte externa de 5–6 V
Fio marrom do servo (−)Negativo da fonte externa + GND do ArduinoNegativo da fonte externa + GND do Pico
Potenciômetro rotativo de 10 kΩ
Potenciômetro de 10 kΩ: os terminais externos vão no 5V e no GND, o terminal do meio (cursor) em uma entrada analógica; conforme você gira o eixo, a articulação gira na mesma proporção.

Calibre antes da montagem: centralize os servos

  1. Antes de colocar os braços dos servos, carregue o código abaixo; os quatro servos vão para 90 graus, um depois do outro, e ficam parados ali.
  2. Segure o braço em uma posição neutra (por exemplo, braço superior na vertical, antebraço na horizontal, garra meio aberta), encaixe cada braço do servo no dente do estriado mais próximo dessa posição e prenda-o com o parafuso central.
  3. Mude CENTER_ONLY para false no código, escolha uma articulação com TEST e gire devagar o potenciômetro do A0.
  4. Acompanhe o ângulo no Monitor Serial e anote o menor e o maior ângulo que a articulação alcança sem bater em nada nem esticar os cabos.
  5. Defina o ângulo de garra fechada no ponto em que os dedos apenas se tocam; um servo que continua tentando apertar mais fica travado e esquenta.
  6. Coloque os valores encontrados nos arrays MIN_A e MAX_A dos sketches seguintes.
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}

Há um detalhe pequeno, mas importante, neste código: como servo.write(90) é chamado antes de attach(), o servo recebe o ângulo pedido já no primeiro pulso. Se você inverter a ordem, a biblioteca primeiro envia o pulso padrão de 1500 µs (cerca de 90°); se o seu ângulo inicial não for 90, o braço pula para o meio e depois para o alvo ao ligar. Iniciar os servos um a um, com 300 ms de intervalo, também distribui o pico de corrente na partida.

Controle do braço robótico com potenciômetros

O método de controle mais intuitivo é um potenciômetro por articulação: seja qual for o ângulo do eixo do potenciômetro, a articulação vai para o mesmo ângulo, ou seja, você faz controle de posição. Se você montar os potenciômetros em uma maquete pequena do braço (de papelão ou impressa em 3D, nas mesmas proporções), o braço grande copia um a um os movimentos da maquete enquanto você a move com a mão. As leituras analógicas, porém, oscilam naturalmente alguns pontos, e essa oscilação aparece como tremedeira nos servos; o código tem duas medidas contra isso.

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}

O array smooth é um filtro passa-baixa simples: a cada ciclo ele soma só 20% da nova leitura, então saltos repentinos são suavizados e o braço continua respondendo rápido. Troque 0.2 por 0.1 e o movimento fica mais suave, porém mais atrasado. A segunda medida é uma zona morta (deadband): se o novo ângulo não diferir do último escrito em pelo menos 2 graus, nada é enviado ao servo, o que acaba com a tremedeira enquanto o braço está parado. Como map() converte a faixa do potenciômetro direto para os limites seguros de cada articulação, a articulação nunca bate no batente mecânico, mesmo com o potenciômetro girado até o fim.

Gravar e reproduzir movimentos

O que realmente faz de um braço robótico um robô é repetir sozinho um movimento que você mostrou a ele. O método é simples: você leva o braço até uma posição com os potenciômetros e aperta o botão de gravar, e o Arduino guarda os ângulos das quatro articulações. Depois de gravar algumas posições, aperte reproduzir e o braço as repete em ordem, suavizando as transições entre elas. As posições são gravadas na EEPROM de 1024 bytes do Arduino Uno, então sobrevivem quando a placa é desligada; cada posição ocupa 4 bytes, então uma gravação de 100 posições usa só 401 bytes.

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}
AçãoO que você fazO que o braço faz
Salvar uma posiçãoToque curto no botão de gravar (D7)O LED pisca uma vez e a posição é gravada na EEPROM
Apagar as gravaçõesSegure o botão de gravar por 2 segundosO LED pisca cinco vezes e a contagem de passos é zerada
ReproduzirAperte e solte o botão de reproduzir (D8)O LED acende e as posições são reproduzidas em ordem, com transições suaves
Parar a reproduçãoSegure o botão de reproduzir durante a reproduçãoO braço termina a posição atual e para
Voltar ao modo normalNada; acontece sozinho quando a reprodução terminaO braço volta devagar para onde os potenciômetros apontam e o LED apaga

A função moveTo() é o coração do sketch: ela encontra a articulação com o maior caminho a percorrer, divide o movimento nesse número de passos e avança todas as articulações proporcionalmente a cada passo. Assim, todas as articulações chegam ao alvo ao mesmo tempo e o braço se move com fluidez, em vez de dar trancos como um robô que mexe uma articulação por vez. Com 12 ms por grau, um movimento de 90 graus leva cerca de 1,1 segundo. Você pode diminuir esse valor para uma reprodução mais rápida, mas mesmo sem carga um MG996R gira 60 graus em 0,17 segundo, cerca de 3 ms por grau; abaixo disso os servos não acompanham os comandos e o movimento fica aos trancos.

Cada célula da EEPROM pode ser gravada cerca de 100.000 vezes. Como o código usa EEPROM.update(), ele só grava as células cujo valor mudou, então na prática você nunca vai desgastá-la. Para sequências de movimento mais longas, você pode guardar as gravações em um cartão SD ou usar a EEPROM de 4 KB do Arduino Mega.

Driver de servos PCA9685: 6 servos e um sinal sem tremedeira

Quando você passa para 5–6 servos ou adiciona módulos extras, como o Bluetooth, um módulo PCA9685 facilita muito a vida. Essa placa é um gerador de PWM de 16 canais e 12 bits; ela conversa com o Arduino por apenas dois fios usando I2C e gera os pulsos dos servos com o próprio clock, independentemente do processador. O endereço padrão é 0x40; mudando o endereço com os jumpers de solda A0–A5, você pode encadear até 62 placas, ou 992 servos. Em 50 Hz, o período de 20 ms é dividido em 4096 passos, então um passo dura cerca de 4,9 µs; a faixa de 544–2400 µs cobre cerca de 380 passos, o que dá uma resolução de aproximadamente meio grau.

Pino do PCA9685Arduino UnoESP32Raspberry Pi Pico
GNDGNDGNDGND
VCC (só alimenta o chip)5V3V33V3
SDAA4GPIO 21GP4
SCLA5GPIO 22GP5
Borne V+ (alimentação dos servos)Fonte externa de 5–6 VFonte externa de 5–6 VFonte externa de 5–6 V
OEDeixe desconectadoDeixe desconectadoDeixe desconectado
Conectores de servo 0–5Base, ombro, cotovelo, punho, rotação do punho, garraMesma ordemMesma ordem
O PCA9685 tem duas alimentações separadas: o VCC alimenta só o chip, enquanto os servos são alimentados pela entrada V+ do borne de parafuso. Se o V+ ficar sem alimentação, a placa aparece no I2C, mas os servos não se mexem. No Arduino Uno, os pinos A4 e A5 são usados pelo I2C, então, com um PCA9685 conectado, sobram só A0–A3 como entradas analógicas; se você precisar de mais potenciômetros, use um Arduino Nano com as entradas extras A6 e A7.

Controle do braço robótico com joysticks

Um joystick funciona de um jeito diferente do potenciômetro: ao ser solto, ele volta para o centro por causa da mola. Se você ligar o ângulo diretamente à posição do joystick, a articulação salta para 90 graus no instante em que você o solta. O jeito certo é o controle de velocidade: quanto mais você empurra o joystick, mais rápido a articulação gira, e quando você solta, a articulação fica onde está. No código abaixo, dois joysticks controlam quatro articulações (joystick esquerdo para base e ombro, joystick direito para cotovelo e punho); o botão do joystick esquerdo abre e fecha a garra, e o do direito manda o braço para a posição inicial. Ligue as saídas VRx e VRy do joystick esquerdo em A0 e A1, as saídas VRy e VRx do joystick direito em A2 e A3 e os pinos SW em D2 e D4; os joysticks podem ser alimentados pelo pino 5V do Arduino. Para compilar o código, instale a Adafruit PWM Servo Driver Library pelo Gerenciador de Bibliotecas e aceite a dependência Adafruit BusIO.

Módulo joystick analógico de dois eixos
Módulo joystick: as saídas VRx e VRy leem cerca de 512 em repouso, e o botão SW puxa para o GND quando pressionado. Dois módulos conseguem controlar quatro articulações de um braço robótico.
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 impede que pequenos desvios, como os valores de 490–530 lidos no centro, façam o braço se arrastar sozinho. MAX_SPEED define a velocidade, em graus por segundo, com o joystick empurrado até o fim; 90 graus por segundo é um valor seguro para começar. Se uma articulação se mover no sentido oposto ao do joystick, não troque fios: basta colocar -1 no valor correspondente do array DIR. O código guarda um alvo (target) e uma posição real (current) para cada articulação; como a posição real se aproxima do alvo com limite de velocidade, o braço desliza de volta com suavidade mesmo quando você aperta o botão da posição inicial. A rotação do punho fica fixa em 90 graus neste sketch; você pode controlá-la com o comando R na seção de Bluetooth. Essa técnica de temporização sem bloqueio é explicada em detalhes em millis() vs delay().

Controle por Bluetooth pelo celular (HC-05)

O módulo HC-05 cria uma conexão serial sem fio entre o seu celular e o Arduino. O pino TXD vai no D10 do Arduino, e o pino RXD se liga ao D11 através de um divisor de tensão: D11 → 1 kΩ → RXD → 2 kΩ → GND. O divisor reduz o sinal de 5 V do Arduino para os cerca de 3,3 V que o HC-05 espera. A taxa de dados padrão do módulo é 9600 baud, e o PIN de pareamento costuma ser 1234. O celular envia comandos de texto simples: uma letra escolhe a articulação, o número depois dela dá o ângulo, e uma quebra de linha ou um ponto e vírgula encerra o comando.

Módulo serial Bluetooth HC-05
HC-05: um módulo serial que pareia com o celular via Bluetooth clássico; o VCC recebe 5 V, mas a entrada RXD trabalha com lógica de 3,3 V.
ComandoSignificadoExemplo
B + ânguloBaseB90
S + ânguloOmbroS120
E + ânguloCotoveloE45
W + ânguloPunho (inclinação)W100
R + ânguloRotação do punhoR0
G + ânguloGarraG110 (fechar), G40 (abrir)
HVoltar à posição inicialH
K / P / X / CSalvar posição / reproduzir em loop / parar / apagar posiçõesK
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}

Neste sketch, os servos estão ligados ao PCA9685, e não direto ao Arduino, e isso é proposital. A SoftwareSerial desativa as interrupções por cerca de 1 ms enquanto recebe cada byte; se os servos fossem acionados pelos pinos do Arduino com a biblioteca Servo, os pulsos se alongariam nesse intervalo e o braço daria um tranco a cada comando. Como o PCA9685 gera os pulsos sozinho, o sinal fica firme como uma rocha, não importa o quanto a conexão Bluetooth esteja ocupada. A gravação e a reprodução agora também são feitas pelo celular: leve o braço até uma posição com os controles deslizantes e envie K; depois de algumas posições, P faz o braço repeti-las em loop e X o faz parar. Nesta versão, as posições ficam na RAM e se perdem quando a placa é desligada; se quiser que elas fiquem salvas, adicione o método da EEPROM do sketch anterior.

No celular, basta um app pronto: instale no Android um terminal serial Bluetooth ou um app controlador Bluetooth com controles deslizantes e dê a cada controle um prefixo, como “S”, mais um final de linha. Se quiser criar a sua própria interface com botões e controles deslizantes, o guia app Bluetooth com MIT App Inventor mostra o passo a passo. Como o HC-05 usa Bluetooth clássico, ele não funciona com iPhone; para iPhone, você precisa de uma solução com Bluetooth Low Energy, como o ESP32 BLE.

Braço robótico sem fio com ESP32

Como o ESP32 já tem Bluetooth clássico embutido, você não precisa de um HC-05: com a biblioteca BluetoothSerial, a placa aparece no seu celular como “BlueGrays-RobotKol” e entende os mesmos comandos. Ligue o PCA9685 ao GPIO 21 (SDA) e ao GPIO 22 (SCL) e alimente o VCC dele pelo 3V3; os sinais dos servos passam então a sair em 3,3 V, o que a grande maioria dos servos lê sem problema. A BluetoothSerial só funciona no ESP32 clássico (ESP32-WROOM-32, DevKit); os ESP32-S2, S3 e C3 não têm suporte a Bluetooth clássico.

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}

Braço robótico com Raspberry Pi Pico (MicroPython)

Você não precisa de nenhuma biblioteca extra para acionar servos no Raspberry Pi Pico: ajuste o PWM para 50 Hz e informe a largura do pulso em nanossegundos com duty_ns(). Com 16 canais PWM, o Pico consegue acionar 6 servos diretamente; pares de pinos que compartilham o mesmo slice compartilham a frequência, mas como todo servo usa 50 Hz, isso não é problema. A limitação está no lado analógico: o Pico tem só três pinos de ADC utilizáveis (GP26, GP27, GP28). Por isso, no código abaixo, três articulações são controladas por potenciômetros e a garra abre e fecha com um botão no GP14. Como o Pico trabalha em 3,3 V, ligue os potenciômetros ao 3V3; alimente os servos com uma fonte externa de 5 V, como antes.

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

Indo além: cinemática inversa

Até aqui, definimos o ângulo de cada articulação uma por uma; isso se chama cinemática direta. A cinemática inversa faz o contrário: você informa o ponto (x, y) que a garra deve alcançar e o código calcula os ângulos das articulações necessários. Para um braço de dois elos com comprimento ombro–cotovelo L1 e cotovelo–punho L2, calcule primeiro D = (x² + y² − L1² − L2²) / (2·L1·L2). O ângulo do cotovelo é θ2 = acos(D) e o ângulo do ombro é θ1 = atan2(y, x) + atan2(L2·sin θ2, L1 + L2·cos θ2) (a solução com o cotovelo para cima). Por exemplo, com L1 = L2 = 12 cm e um ponto 15 cm à frente e 5 cm acima do ombro, você obtém D ≈ −0,13, θ2 ≈ 97,6° e θ1 ≈ 67,2°: o braço superior aponta 67 graus acima da horizontal e o antebraço se dobra 97,6 graus para baixo em relação a ele. Se o valor absoluto de D for maior que 1, o ponto está fora do alcance do braço. O ângulo da base é calculado à parte, a partir da posição do alvo vista de cima. Ao converter os resultados em ângulos de servo, leve em conta o sentido de montagem e o ponto central de cada servo.

Tremedeira e resets: erros comuns e soluções

SintomaCausa provávelSolução
Os servos tremem e zumbem com o braço paradoRuído nas leituras dos potenciômetros ou fonte fracaAdicione um filtro de suavização e uma zona morta de 2°, use uma fonte externa e um capacitor de 1000 µF
O Arduino reinicia quando o braço se move, o HC-05 perde a conexãoA corrente de partida dos servos derruba a tensãoAlimente os servos com uma fonte separada de 5–6 V, junte os GNDs e inicie os servos um a um ao ligar
Os servos dão um tranco sempre que chega um comando BluetoothAs interrupções da SoftwareSerial atrapalham os pulsos da biblioteca ServoPasse os servos para um PCA9685 ou use o Bluetooth embutido do ESP32
O braço dá um bote para algum lado ao ligarOs servos não lembram a última posição e se movem em velocidade máxima no primeiro comandoEstacione o braço na posição inicial antes de desligar e chame write() antes de attach()
O ombro não consegue levantar o braço ou vai cedendo devagarTorque insuficienteFaça o cálculo de torque; use um MG996R, um braço mais curto, um contrapeso ou uma mola
Uma articulação trava em uma das pontas e esquentaO servo está forçando contra um batente mecânicoColoque no código os limites MIN_A e MAX_A obtidos na calibração
O servo da garra esquenta ao segurar um objetoA garra continua tentando fechar e o servo fica travadoDefina o ângulo de fechamento no ponto em que a garra apenas encosta no objeto e coloque borracha nos dedos
O PCA9685 está conectado, mas nenhum servo se mexeSem alimentação no borne V+ ou sem GND comumLeve 5–6 V ao V+ e confira o endereço I2C (0x40)
Uma articulação escorrega sob cargaUm braço de servo plástico espanou ou falta o parafuso centralUse um braço de servo metálico e prenda-o ao eixo com o parafuso central
Um jeito rápido de saber se o problema é de alimentação ou de código: rode o sketch de calibração (CENTER_ONLY = false) sem os servos conectados e acompanhe os ângulos no Monitor Serial enquanto gira o potenciômetro. Se os valores parecerem corretos, o problema é a fonte; meça a tensão da linha dos servos com um multímetro enquanto o braço se move e, se ela cair abaixo de 4,5 V, a sua fonte ou a fiação não dá conta. Para todas as causas de reset, veja Por que o meu Arduino fica reiniciando?.

Ideias de projetos com braço robótico

  • Braço separador de cores: leia a cor de cubos com um sensor de cor TCS3200 e deixe o braço colocar cada cor na sua própria caixa.
  • Controle por gestos: com um MPU6050 em uma luva, o braço robótico se inclina para o mesmo lado que o seu punho.
  • Controle remoto sem fio: ligue dois joysticks a outro Arduino e envie os comandos com um nRF24L01; você ganha um controle remoto que dispensa o celular.
  • Controle pelo navegador: crie controles deslizantes em uma página web com um ESP32 e controle o braço de qualquer dispositivo na mesma rede Wi-Fi; um WebSocket no ESP32 reduz a latência ao mínimo.
  • Pegar peças de uma esteira: monte uma pequena esteira transportadora com um motor de passo; quando um TCRT5000 detecta um objeto, o braço o tira da esteira.
  • Projeto escolar e de competição: um braço que grava e reproduz movimentos é uma escolha forte entre as ideias de projetos para feira de ciências, mostrando mecânica, eletrônica e software juntos.

Um braço robótico com Arduino é um projeto que ensina projeto mecânico, cálculo de torque, gerenciamento de alimentação e programação de movimentos suaves de uma vez só, e ele cresce conforme o esforço que você coloca nele. Comece com um braço de 4 servos controlado por potenciômetros, adicione a gravação e a reprodução e depois passe para 6 servos com um PCA9685 e Bluetooth. No app BlueGrays, você encontra projetos como controle de servo com potenciômetro e joystick, controle por Bluetooth pelo celular e uma coreografia com 32 servos, com esquemas do circuito e código passo a passo, para testar antes cada articulação do seu braço separadamente.

Perguntas frequentes

Qual servo motor devo usar em um braço robótico com Arduino?

Para articulações que suportam carga, como base, ombro e cotovelo, o MG996R com engrenagens metálicas e 9–11 kg·cm de torque é uma boa escolha. Para articulações leves, como punho e garra, um MG90S ou SG90 basta. Braços pequenos feitos só com SG90 funcionam se forem curtos e leves, mas só conseguem levantar objetos de algumas dezenas de gramas.

De que fonte de alimentação um braço robótico precisa?

Uma fonte de 5 V 3 A basta para um braço pequeno com 4 servos SG90 ou MG90S. Para um braço de 6 servos com 3 MG996R e 3 servos pequenos, use uma fonte chaveada de 5–6 V de no mínimo 5 A, ou 10 A para ter folga. Não esqueça de ligar o negativo da fonte ao GND do Arduino.

Quantos servos um Arduino consegue controlar?

A biblioteca Servo consegue acionar até 12 servos em um Arduino Uno e 48 em um Mega. O limite real, porém, é a corrente: todos os servos devem ser alimentados por uma fonte externa. Um módulo PCA9685 aciona 16 servos usando apenas dois pinos I2C e pode ser encadeado em até 62 módulos.

Por que os servos do meu braço robótico estão tremendo?

As causas mais comuns são fonte fraca, falta de GND comum e ruído nas leituras dos potenciômetros. Use uma fonte externa com um capacitor de 1000 µF na linha dos servos e adicione um filtro de suavização e uma zona morta no código. Se a SoftwareSerial e a biblioteca Servo forem usadas juntas, passar os servos para um PCA9685 elimina a tremedeira por completo.

Quanto peso um braço robótico com SG90 consegue levantar?

O torque de travamento do SG90 é de cerca de 1,8 kg·cm; para uma operação segura, conte com metade disso, 0,9 kg·cm. Como o próprio peso do braço e dos servos consome parte desse orçamento, um braço com SG90 de cerca de 15 cm de comprimento, na prática, só carrega objetos leves de 20–30 g, como uma tampinha de garrafa ou um pequeno cubo de espuma.

Como controlar um braço robótico pelo celular?

Ligue um módulo Bluetooth HC-05 ao Arduino e use no celular um terminal serial Bluetooth ou um app controlador com controles deslizantes para enviar comandos de texto, como “S120”. Com um ESP32, o Bluetooth embutido dispensa um módulo extra. O HC-05 não funciona com iPhone, então, para iPhone, você precisa de um módulo BLE ou do ESP32 BLE.

Ver todos os projetos
Uso de servo motor com potenciômetro
#36Iniciante
Premium

Uso de servo motor com potenciômetro

Conforme você gira o botão do potenciômetro, o braço do servo motor gira junto: uma extremidade do botão corresponde a zero grau, e a outra, a meia volta. O Arduino lê continuamente a posição do botão

4.6(37)
52
Controle de servos com joystick
#60Intermediário
Premium

Controle de servos com joystick

Controle dois motores com um joystick, como ao girar uma câmera de segurança. Empurre a alavanca para os lados e o servo motor inferior gira lentamente; mova para cima ou para baixo e o servo superior

4.7(42)
58
Uso de servo motor
#35Iniciante
Premium

Uso de servo motor

O braço de um pequeno servo motor gira para três posições em sequência: o início, o meio e o fim do percurso, esperando dois segundos em cada parada. O Arduino informa ao motor em qual ângulo ficar, e

4.7(48)
67
Servo motor com ESP32 (controle por potenciômetro)
#118Intermediário
Premium

Servo motor com ESP32 (controle por potenciômetro)

Neste projeto, controlamos um servo motor SG90 com um potenciômetro. Ajustamos o ângulo do servo mapeando o valor analógico lido do potenciômetro para a faixa de 0 a 180 graus. A biblioteca ESP32Servo gera sinais para o servo nos GPIOs do ESP32 adequados e capazes de operar como saída; este projeto usa o GPIO 18. Esse é um método básico de controle para braços robóticos, estabilizadores de câmera e mecanismos de portas automáticas.

4.3(29)
42
Controle de LED pelo celular via Bluetooth
#34Iniciante
Premium

Controle de LED pelo celular via Bluetooth

Um pequeno ensaio para acender e apagar as luzes do seu quarto pelo celular. O módulo Bluetooth repassa ao Arduino as letras enviadas pelo celular; uma letra acende o LED vermelho, outra acende o verd

4.8(68)
89
Carrinho controlado por Bluetooth com L298N
#80Avançado
Premium

Carrinho controlado por Bluetooth com L298N

Transforme seu celular em um controle remoto e dirija seu próprio carrinho robô. O Arduino recebe os comandos de um aplicativo de controle remoto no celular por meio de um módulo Bluetooth e aciona as

5.0(38)
65
Coreografia em círculo: animações com 32 servos
#102Avançado
Premium

Coreografia em círculo: animações com 32 servos

Um projeto de animação visual com 32 servo motores dispostos em círculo e controlados por um Arduino Mega. Os servos são gerenciados em um único array e conectados aos pinos 22–53 do Mega. Eles executam quatro sequências de movimento: uma vibração caótica em que todos vão para ângulos aleatórios, uma varredura sincronizada em que todos se movem ao mesmo tempo, uma onda que gira suavemente pelo círculo e, por fim, uma agulha de bússola que oscila de um lado para o outro.

4.5(40)
62