Brazo robótico con Arduino: servos, torque, PCA9685 y Bluetooth
Cómo hacer un brazo robótico con Arduino: estructura de MDF o 3D, torque del MG996R, control con potenciómetro y joystick, grabar, PCA9685 y Bluetooth.
Un brazo robótico con Arduino es un robot de escritorio que, gracias a sus articulaciones movidas por servos, puede agarrar un objeto, levantarlo, girar y dejarlo en otro sitio. Con su base, hombro, codo, muñeca y pinza, te enseña diseño mecánico, elección de motores, gestión de la alimentación y programación en un solo proyecto. Y lo mejor: lo puedes construir en casa con un Arduino Uno, unos cuantos servos y piezas de MDF o impresas en 3D, y tener una pequeña copia de un robot industrial funcionando en tu mesa.
En esta guía vemos de principio a fin los brazos robóticos de 4 y 6 ejes: el material de la estructura, un cálculo de torque para decidir entre SG90, MG90S y MG996R, la alimentación externa de 5 V, el control con potenciómetros y joysticks, grabar y reproducir movimientos, el driver de servos de 16 canales PCA9685, el control por Bluetooth desde el móvil y las soluciones a los dos problemas más habituales: temblores y reinicios. Si no tienes claro cómo funciona un servo, empieza por ¿Qué es un servomotor?.



¿Cómo funciona un brazo robótico y cuántos ejes necesitas?
Piensa en un brazo robótico como en un brazo humano. Cada articulación lleva un servomotor y el Arduino le dice a cada servo "ve a este ángulo". Gracias al potenciómetro interno, el servo sabe dónde está su eje y mantiene el ángulo incluso con carga. Cambiando los ángulos de las articulaciones a la vez, llevas la pinza al punto que quieras. El número de movimientos independientes que puede hacer un brazo se llama grados de libertad (DOF), o simplemente ejes.
| Articulación | Movimiento | Carga | Servo recomendado |
|---|---|---|---|
| Base (cintura) | Gira el brazo a izquierda y derecha | No levanta peso, pero soporta todo el brazo | MG996R + rodamiento |
| Hombro | Levanta el brazo hacia delante y atrás | La articulación más exigida | MG996R o un servo de la clase de 20 kg·cm |
| Codo | Dobla el antebrazo | Media-alta | MG996R |
| Muñeca (arriba-abajo) | Ajusta el ángulo de la pinza | Baja | MG90S |
| Giro de muñeca | Gira la pinza sobre su eje | Muy baja | MG90S o SG90 |
| Pinza (gripper) | Agarra y suelta el objeto | Fuerza de apriete | SG90 o MG90S |
La mayoría de los kits de "brazo robótico de 6 ejes" llevan 6 servos; como la pinza no cuenta como eje, técnicamente son un brazo de 5 grados de libertad más una pinza. Para tu primer brazo, un diseño de 4 ejes (base, hombro, codo, pinza) es el mejor punto de partida: bastan 4 servos, 4 potenciómetros y un Arduino Uno, el cableado es sencillo y casi no hay problemas de torque. Las articulaciones de muñeca sirven para mantener la pinza horizontal o agarrar objetos desde distintos ángulos; al pasar a 5–6 servos, el driver PCA9685 se vuelve casi imprescindible.
Lista de materiales
| Pieza | Brazo de 4 ejes | Brazo de 6 servos | Nota |
|---|---|---|---|
| Placa de control | Arduino Uno o Nano | Arduino Uno, Nano o ESP32 | Las entradas extra A6 y A7 del Nano permiten más potenciómetros |
| Servomotores | 4 × SG90 o MG90S | 3 × MG996R + 3 × MG90S | Comprueba tu elección con el cálculo de torque de abajo |
| Driver de servos | No hace falta | Módulo PCA9685 de 16 canales | Solo 2 cables de señal por I2C |
| Fuente de alimentación | Adaptador de 5 V 3 A | 5–6 V, al menos 5 A | Los servos nunca se alimentan desde el Arduino |
| Condensador | 470–1000 µF, 16 V | 1000–2200 µF, 16 V | En la línea de alimentación de los servos, cuidando la polaridad |
| Control | 4 potenciómetros de 10 kΩ | 2 módulos joystick o un HC-05 | También puedes combinar ambos |
| Pulsadores | 2 (grabar, reproducir) | Bastan los pulsadores de los joysticks | Con INPUT_PULLUP no hacen falta resistencias |
| Mecánica | Piezas de MDF de 3 mm o impresas en 3D | MDF de 4 mm, impresión 3D o soportes de aluminio | Rodamiento 608, tornillos M3 y tuercas autoblocantes |
Estructura mecánica: ¿MDF o impresión 3D?
| Material | Ventajas | Inconvenientes | Ideal para |
|---|---|---|---|
| MDF de 3–4 mm (corte láser) | Barato, ligero, fácil de atornillar y pegar | Le afecta la humedad, los agujeros de los tornillos se agrandan con el tiempo | Primeros brazos tipo MeArm, proyectos escolares |
| Acrílico de 3 mm | Queda muy vistoso, no le afecta la humedad | Es frágil; puede agrietarse al apretar tornillos | Proyectos de exposición |
| Impresión 3D (PLA / PETG) | Piezas complejas, alojamientos de servo a medida | Impresión lenta; el PLA se deforma con calor y carga continua | Diseños propios; PETG para piezas que soportan carga |
| Kit de soportes de aluminio | Muy rígido, piezas listas para el MG996R | Pesado y caro, carga más el hombro | Brazos de 6 servos para objetos más pesados |
| Cartón, palitos de helado | Casi gratis, prototipo rápido | Poco resistente, las articulaciones cogen holgura | Probar medidas, primeros intentos con niños |
- Mantén el brazo corto y ligero: La carga en el hombro crece en proporción directa a la distancia. Hacer el brazo de 24 cm en vez de 30 cm reduce el torque del hombro en torno a una quinta parte.
- Coloca los servos pesados cerca de la base: Bajar el servo del codo a la altura del hombro y mover el codo con una varilla (articulación en paralelogramo) es un truco ingenioso que usan brazos como el MeArm; el hombro ya no tiene que levantar ese servo.
- Usa un rodamiento en la base: No cargues todo el peso del brazo sobre el eje del servo de la base. Un rodamiento 608 (8 mm de diámetro interior, 22 mm exterior y 7 mm de ancho; el típico de monopatín) o un rodamiento de plato giratorio soporta la carga y el servo solo gira.
- Apoya las articulaciones por los dos lados: Mientras el servo sujeta la articulación por el lado del horn, pon un pivote en el lado opuesto con un tornillo M3 y una tuerca autoblocante. Así el eje del servo no soporta flexión y los engranajes duran mucho más.
- Usa horns metálicos: Monta en cada MG996R un horn redondo metálico de 25 dientes (25T) y fíjalo al eje con el tornillo central. Los horns de plástico patinan y se desdentan en las articulaciones con mucha carga.
- Añade goma a los dedos de la pinza: Una tira fina de goma en las puntas permite sujetar objetos sin que resbalen y con menos fuerza de apriete.
Elegir servos: ¿SG90, MG90S o MG996R?
| Modelo | Torque | Velocidad (por 60°) | Peso | Engranajes | Corriente de bloqueo | Uso en el brazo |
|---|---|---|---|---|---|---|
| SG90 | 1,8 kg·cm (4,8 V) | 0,10 s (4,8 V) | 9 g | Plástico | Unos 0,6–0,75 A | Pinza, muñeca ligera, brazo pequeño de 4 ejes |
| MG90S | 1,8 kg·cm (4,8 V), 2,2 kg·cm (6 V) | 0,10 s (4,8 V) | 13,4 g | Metal | Unos 0,7–1 A | Muñeca, giro de muñeca, pinza |
| MG996R | 9,4 kg·cm (4,8 V), 11 kg·cm (6 V) | 0,17 s (4,8 V) | 55 g | Metal | 2,5 A (6 V) | Base, hombro, codo |
| DS3218 | 19 kg·cm (5 V), 21,5 kg·cm (6,8 V) | 0,16 s (5 V) | 60 g | Metal | Unos 2–3 A | Hombro de brazos largos o pesados |
El torque de la tabla es el torque de bloqueo (stall torque): la fuerza máxima que da el servo antes de que su eje se detenga, no un valor para trabajar de forma continua. Cuanto más se acerca un servo a ese límite, más sube su corriente hacia la de bloqueo, se calienta y empieza a temblar. La regla práctica es que el torque que necesita una articulación no supere la mitad del torque de bloqueo del servo. El voltaje también cuenta: el MG996R da 9,4 kg·cm a 4,8 V y 11 kg·cm a 6 V, así que el mismo servo gana un 17 % de fuerza solo con la tensión de alimentación. Por eso conviene alimentar a 6 V los brazos con MG996R, aunque para el SG90 y el MG90S 6 V es el límite superior. Para la conexión y los límites del MG996R consulta también la guía servomotor MG996R.

Cálculo de torque: ¿puede el servo del hombro con este brazo?
El torque que tiene que levantar un servo es la suma de la masa de cada pieza multiplicada por su distancia horizontal al eje de la articulación: Torque (kg·cm) = Σ masa (kg) × distancia (cm). El peor caso es con el brazo totalmente estirado en horizontal, porque todas las distancias son máximas; con el brazo vertical, ese brazo de palanca baja casi a cero. Para pasarlo a newton metro, multiplica por 0,098 (1 kg·cm ≈ 0,098 N·m). Como ejemplo, calculemos la carga en el hombro de un brazo de escritorio con 12 cm del hombro al codo, 12 cm del codo a la muñeca, 6 cm de la muñeca a la punta de la pinza y un MG996R en el codo, sujetando un objeto de 50 g:
| Pieza | Masa | Distancia al eje del hombro | Torque |
|---|---|---|---|
| Piezas del brazo superior (MDF y tornillos) | 40 g | 6 cm (centro de masas) | 0,04 × 6 = 0,24 kg·cm |
| Servo del codo (MG996R) | 55 g | 12 cm | 0,055 × 12 = 0,66 kg·cm |
| Piezas del antebrazo | 30 g | 18 cm | 0,03 × 18 = 0,54 kg·cm |
| Servo de muñeca + pinza (MG90S, SG90 y piezas) | 50 g | 25 cm | 0,05 × 25 = 1,25 kg·cm |
| Objeto sujetado | 50 g | 30 cm | 0,05 × 30 = 1,50 kg·cm |
| Total | 225 g | — | 4,19 kg·cm (≈ 0,41 N·m) |
En reposo, este brazo necesita 4,19 kg·cm en el hombro. Para cubrir la carga extra al acelerar y frenar, y mantener el servo lejos del bloqueo, multiplica por 2: 8,4 kg·cm. Es decir, el hombro necesita como mínimo un MG996R (9,4 kg·cm a 4,8 V, 11 kg·cm a 6 V); los 1,8–2,2 kg·cm de un SG90 o MG90S ni siquiera moverían este brazo. Si haces lo mismo con el codo, el antebrazo (0,03 × 6), la muñeca con la pinza (0,05 × 13) y el objeto (0,05 × 18) suman 1,73 kg·cm, 3,5 kg·cm con el margen de seguridad; el codo también pide un MG996R. En la muñeca solo quedan la pinza y el objeto, así que la carga ronda los 0,4 kg·cm y un MG90S va sobrado. El mismo cálculo te dice cuál es el objeto más pesado que puede llevar el brazo: aumenta la masa, recalcula el total y no pases de la mitad del torque del servo.
Alimentación: ¿por qué los servos no se alimentan desde el Arduino?
La mayoría de los problemas de un brazo robótico vienen de la alimentación. Un MG996R consume 0,5–0,9 A en movimiento y hasta 2,5 A bloqueado; incluso un pequeño SG90 puede superar los 0,6 A cuando se bloquea. Con el Arduino Uno alimentado por USB, la corriente total disponible en el pin 5V ronda los 500 mA, el límite del USB, y parte la consume la propia placa. Cuando tres servos grandes arrancan a la vez, la tensión cae de golpe, el microcontrolador se reinicia y el brazo salta a la posición inicial. La solución es alimentar los servos con una fuente aparte de 5–6 V y unir todas las tierras (GND); el Arduino puede seguir alimentándose por USB o con su propio adaptador. Para ver opciones de alimentación consulta la guía alimentación del Arduino, y para estimar cuánto aguanta un brazo con batería usa la calculadora de duración de batería.
| Tipo de brazo | Corriente estimada | Fuente recomendada |
|---|---|---|
| Brazo pequeño con 4 × SG90 o MG90S | 0,5–1 A en movimiento, picos de 2–3 A | Adaptador de 5 V 3 A o módulo LM2596 (3 A máx., unos 2 A sin disipador) |
| Brazo de 6 servos con 3 × MG996R + 3 × MG90S | 2–3 A en movimiento, 8–10 A si se bloquean todos | Fuente conmutada de 5–6 V y al menos 5 A (10 A para ir holgado) |
| Brazo portátil | Según el brazo | LiPo 2S (7,4 V) + módulo reductor (UBEC) de 5 A o más ajustado a 6 V |
| Fuentes que debes evitar | — | Pila de 9 V (no da corriente), el pin 5V del Arduino, 4 pilas AA alcalinas en un brazo con MG996R |
Conexiones del brazo de 4 ejes
La tabla muestra el cableado del brazo de 4 ejes con potenciómetros para Arduino Uno y Raspberry Pi Pico; los códigos de esta guía usan estos pines. En el Uno, la librería Servo puede mover un servo desde cualquier pin digital, pero como usa el Timer1, analogWrite deja de funcionar en los pines 9 y 10 mientras haya servos conectados. Puedes ver la distribución de pines de tu placa en la herramienta Pinout.
| Conexión | Arduino Uno | Raspberry Pi Pico |
|---|---|---|
| Señal servo de la base (cable naranja) | D3 | GP10 |
| Señal servo del hombro | D5 | GP11 |
| Señal servo del codo | D6 | GP12 |
| Señal servo de la pinza | D9 | GP13 |
| Pata central potenciómetros 1–3 (base, hombro, codo) | A0, A1, A2 | GP26, GP27, GP28 |
| Pata central potenciómetro 4 (pinza) | A3 | No hay; la pinza va con el pulsador de GP14 |
| Patas laterales de los potenciómetros | 5V y GND | 3V3 y GND |
| Pulsadores de grabar y reproducir (el otro lado a GND) | D7 y D8 | — |
| Cable rojo del servo (+) | Positivo externo de 5–6 V | Positivo externo de 5–6 V |
| Cable marrón del servo (−) | Negativo de la fuente externa + GND del Arduino | Negativo de la fuente externa + GND de la Pico |

Calibra antes de montar: centra los servos
- Antes de poner los horns, carga el código de abajo; los cuatro servos van uno tras otro a 90 grados y se quedan ahí.
- Sujeta el brazo en una postura neutra (por ejemplo brazo superior vertical, antebrazo horizontal y pinza medio abierta), coloca cada horn en el diente más cercano a esa postura y fíjalo con el tornillo central.
- Pon CENTER_ONLY a false en el código, elige una articulación con TEST y gira despacio el potenciómetro de A0.
- Mira el ángulo en el Monitor Serie y apunta el ángulo mínimo y máximo que alcanza la articulación sin chocar con nada ni tensar los cables.
- Fija el ángulo cerrado de la pinza en el punto en que los dedos justo se tocan; un servo que sigue intentando apretar se queda bloqueado y se calienta.
- Escribe los valores obtenidos en los arrays MIN_A y MAX_A de los siguientes códigos.
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}Hay un detalle pequeño pero importante en este código: como servo.write(90) se llama antes de attach(), el servo recibe el ángulo deseado desde el primer pulso. Si inviertes el orden, la librería envía primero su pulso por defecto de 1500 µs (unos 90°); si tu ángulo inicial no es 90, al encender el brazo salta primero al centro y luego al destino. Arrancar los servos de uno en uno con 300 ms entre ellos también reparte el pico de corriente del arranque.
Controlar el brazo robótico con potenciómetros
El método de control más intuitivo es un potenciómetro por articulación: en el ángulo en que esté el eje del potenciómetro se coloca también la articulación, es decir, haces control de posición. Si montas los potenciómetros en una maqueta pequeña del brazo (de cartón o impresa en 3D, con las mismas proporciones), el brazo grande copia uno a uno los movimientos que haces con la mano. Las lecturas analógicas, sin embargo, bailan por naturaleza unas cuantas unidades y ese baile se nota como temblor en el servo; el código tiene dos medidas contra eso.
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}El array smooth es un filtro paso bajo sencillo: en cada vuelta solo se suma el 20 % de la nueva lectura, así se suavizan los saltos y el brazo sigue respondiendo rápido. Si pones 0,1 en vez de 0,2, el movimiento es más suave pero con más retraso. La segunda medida es una banda muerta: si el nuevo ángulo no difiere al menos 2 grados del último escrito, no se envía nada al servo y desaparece el temblor con el brazo quieto. Como map() convierte el rango del potenciómetro directamente a los límites seguros de cada articulación, la articulación no llega al tope mecánico aunque gires el potenciómetro hasta el final.
Grabar y reproducir movimientos
Lo que convierte de verdad un brazo en un robot es que repita por sí solo un movimiento que le has enseñado. El método es sencillo: llevas el brazo a una postura con los potenciómetros y pulsas el botón de grabar, y el Arduino guarda los ángulos de las cuatro articulaciones. Tras grabar varias posturas, pulsas reproducir y el brazo las repite en orden, suavizando las transiciones. Las posturas se escriben en la EEPROM de 1024 bytes del Arduino Uno, así que no se borran al apagar; cada postura ocupa 4 bytes, de modo que una grabación de 100 posturas solo usa 401 bytes.
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}| Acción | Qué haces | Qué hace el brazo |
|---|---|---|
| Guardar postura | Pulsación corta del botón de grabar (D7) | El LED parpadea una vez y la postura se escribe en la EEPROM |
| Borrar grabaciones | Mantén pulsado el botón de grabar 2 segundos | El LED parpadea cinco veces y el contador de pasos vuelve a cero |
| Reproducir | Pulsa y suelta el botón de reproducir (D8) | El LED se enciende y las posturas se repiten en orden con transiciones suaves |
| Detener la reproducción | Mantén pulsado el botón de reproducir durante la reproducción | El brazo termina la postura actual y se detiene |
| Volver al modo normal | No hagas nada cuando acabe la reproducción | El brazo vuelve despacio a donde marcan los potenciómetros y el LED se apaga |
La función moveTo() es el corazón del programa: busca la articulación con el recorrido más largo, divide el movimiento en ese número de pasos y en cada paso hace avanzar todas las articulaciones de forma proporcional. Así todas llegan al destino a la vez y el brazo se mueve con fluidez, no como un robot que mueve una articulación cada vez. 12 ms por grado significa que un movimiento de 90 grados dura unos 1,1 segundos. Puedes bajar el valor para reproducir más rápido, pero incluso sin carga un MG996R gira 60 grados en 0,17 segundos, unos 3 ms por grado; por debajo de eso los servos no llegan a seguir las órdenes y el movimiento se vuelve brusco.
Driver de servos PCA9685: 6 servos y una señal sin temblores
Cuando pasas a 5–6 servos o añades módulos como el Bluetooth, un módulo PCA9685 te facilita mucho la vida. Esta placa es un generador PWM de 16 canales y 12 bits de resolución; se comunica con el Arduino por solo dos cables mediante I2C y genera los pulsos de los servos con su propio reloj, sin depender del procesador. Su dirección por defecto es 0x40; cambiando la dirección con los puentes de soldadura A0–A5 puedes encadenar hasta 62 placas, es decir, 992 servos. A 50 Hz, el periodo de 20 ms se divide en 4096 pasos, así que cada paso dura unos 4,9 µs; el rango de 544–2400 µs abarca unos 380 pasos, lo que da una resolución de aproximadamente medio grado.
| Pin del PCA9685 | Arduino Uno | ESP32 | Raspberry Pi Pico |
|---|---|---|---|
| GND | GND | GND | GND |
| VCC (solo alimenta el chip) | 5V | 3V3 | 3V3 |
| SDA | A4 | GPIO 21 | GP4 |
| SCL | A5 | GPIO 22 | GP5 |
| Borne V+ (alimentación de servos) | 5–6 V externos | 5–6 V externos | 5–6 V externos |
| OE | Sin conectar | Sin conectar | Sin conectar |
| Conectores de servo 0–5 | Base, hombro, codo, muñeca, giro de muñeca, pinza | Mismo orden | Mismo orden |
Controlar el brazo robótico con joystick
Un joystick funciona distinto que un potenciómetro: al soltarlo vuelve al centro. Si atas el ángulo directamente a la posición del joystick, la articulación salta a 90 grados en cuanto lo sueltas. Lo correcto es el control de velocidad: cuanto más empujas el joystick, más rápido gira la articulación, y al soltarlo se queda donde está. En el código de abajo dos joysticks mueven cuatro articulaciones (el izquierdo la base y el hombro, el derecho el codo y la muñeca); el pulsador del joystick izquierdo abre y cierra la pinza y el del derecho lleva el brazo a la postura inicial. Conecta VRx y VRy del joystick izquierdo a A0 y A1, VRy y VRx del derecho a A2 y A3 y los pines SW a D2 y D4; puedes alimentar los joysticks desde el pin 5V del Arduino. Para compilar el código, instala Adafruit PWM Servo Driver Library desde el Gestor de Librerías y acepta la dependencia Adafruit BusIO.

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 que las pequeñas desviaciones, como 490–530, que se leen en el centro hagan que el brazo se desplace solo. MAX_SPEED fija la velocidad, en grados por segundo, con el joystick a fondo; 90 grados por segundo es un valor seguro para empezar. Si una articulación va al revés que el joystick, no cambies cables: pon a -1 el valor correspondiente del array DIR. El código guarda para cada articulación un objetivo (target) y una posición real (current); como la posición real se acerca al objetivo con un límite de velocidad, el brazo vuelve con suavidad incluso al pulsar el botón de postura inicial, sin saltos. El giro de muñeca queda fijo en 90 grados en este programa; puedes controlarlo con el comando R de la sección de Bluetooth. Los detalles de esta temporización sin bloqueos están en millis() frente a delay().
Control por Bluetooth desde el móvil (HC-05)
El módulo HC-05 crea un enlace serie inalámbrico entre el móvil y el Arduino. Su pin TXD va al D10 del Arduino y el RXD se conecta a D11 a través de un divisor de tensión: D11 → 1 kΩ → RXD → 2 kΩ → GND. El divisor baja la señal de 5 V del Arduino a los 3,3 V aproximados que espera el HC-05. La velocidad de datos por defecto es de 9600 baudios y el PIN de emparejamiento suele ser 1234. El móvil envía comandos de texto sencillos: una letra elige la articulación, el número que la sigue da el ángulo y un salto de línea o un punto y coma cierra el comando.

| Comando | Significado | Ejemplo |
|---|---|---|
| B + ángulo | Base | B90 |
| S + ángulo | Hombro | S120 |
| E + ángulo | Codo | E45 |
| W + ángulo | Muñeca (arriba-abajo) | W100 |
| R + ángulo | Giro de muñeca | R0 |
| G + ángulo | Pinza | G110 (cerrar), G40 (abrir) |
| H | Volver a la postura inicial | H |
| K / P / X / C | Guardar postura / reproducir en bucle / parar / borrar posturas | K |
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}En este programa los servos van conectados al PCA9685 y no directamente al Arduino, y es a propósito. SoftwareSerial desactiva las interrupciones durante 1 ms aproximadamente mientras recibe cada byte; si los servos se movieran desde pines del Arduino con la librería Servo, sus pulsos se alargarían en ese tiempo y el brazo daría un tirón con cada comando. Como el PCA9685 genera los pulsos por su cuenta, la señal no tiembla por mucho tráfico Bluetooth que haya. Grabar y reproducir ahora también se hace desde el móvil: llevas el brazo a una postura con los deslizadores y envías K; tras varias posturas, con P el brazo las repite en bucle y con X se detiene. En esta versión las posturas se guardan en la RAM y se pierden al apagar la placa; si quieres que se conserven, añade el método de la EEPROM del código anterior.
Brazo robótico inalámbrico con ESP32
Como el ESP32 lleva Bluetooth clásico integrado, no necesitas el HC-05: con la librería BluetoothSerial la placa aparece en el móvil como "BlueGrays-RobotKol" y entiende los mismos comandos. Conecta el PCA9685 a GPIO 21 (SDA) y GPIO 22 (SCL) y alimenta VCC desde 3V3; las señales de los servos salen entonces a 3,3 V, algo que la inmensa mayoría de los servos lee sin problemas. BluetoothSerial solo funciona en el ESP32 clásico (ESP32-WROOM-32, DevKit); los ESP32-S2, S3 y C3 no tienen Bluetooth clásico.
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}Brazo robótico con Raspberry Pi Pico (MicroPython)
Para mover servos con la Raspberry Pi Pico no hace falta ninguna librería extra: ajustas el PWM a 50 Hz y das el ancho de pulso en nanosegundos con duty_ns(). Con 16 canales PWM, la Pico puede mover 6 servos directamente; los pares de pines que comparten el mismo slice comparten frecuencia, pero como todos los servos usan 50 Hz no es un problema. La limitación está en la parte analógica: la Pico solo tiene tres pines ADC utilizables (GP26, GP27, GP28). Por eso, en el código de abajo tres articulaciones se mueven con potenciómetros y la pinza se abre y se cierra con un pulsador en GP14. Como la Pico funciona a 3,3 V, conecta los potenciómetros a 3V3; los servos se siguen alimentando con una fuente externa de 5 V.
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 loopUn paso más: cinemática inversa
Hasta ahora hemos dado el ángulo de cada articulación una a una; eso se llama cinemática directa. La cinemática inversa hace lo contrario: indicas el punto (x, y) al que quieres llevar la pinza y el código calcula los ángulos necesarios. En un brazo de dos segmentos con longitud hombro–codo L1 y codo–muñeca L2, primero se calcula D = (x² + y² − L1² − L2²) / (2·L1·L2). El ángulo del codo es θ2 = acos(D) y el del hombro θ1 = atan2(y, x) + atan2(L2·sen θ2, L1 + L2·cos θ2) (solución con el codo arriba). Por ejemplo, con L1 = L2 = 12 cm y un punto 15 cm por delante y 5 cm por encima del hombro, sale D ≈ −0,13, θ2 ≈ 97,6° y θ1 ≈ 67,2°: el brazo superior apunta 67 grados por encima de la horizontal y el antebrazo se dobla 97,6 grados hacia abajo respecto a él. Si el valor absoluto de D es mayor que 1, el punto está fuera del alcance del brazo. El ángulo de la base se calcula aparte a partir de la posición del objetivo vista desde arriba. Al convertir los resultados en ángulos de servo, ten en cuenta el sentido de montaje y el punto central de cada servo.
Temblores y reinicios: errores frecuentes y soluciones
| Síntoma | Causa probable | Solución |
|---|---|---|
| Los servos tiemblan y zumban con el brazo quieto | Ruido en las lecturas de los potenciómetros o fuente débil | Añade un filtro de suavizado y una banda muerta de 2°, usa fuente externa y un condensador de 1000 µF |
| El Arduino se reinicia al mover el brazo y el HC-05 pierde la conexión | La corriente de arranque de los servos hunde la tensión | Alimenta los servos con una fuente aparte de 5–6 V, une las tierras y arranca los servos uno a uno |
| Los servos dan tirones al llegar un comando Bluetooth | Las interrupciones de SoftwareSerial alteran los pulsos de la librería Servo | Pasa los servos al PCA9685 o usa el Bluetooth integrado del ESP32 |
| Al encender, el brazo salta bruscamente | Los servos no recuerdan su última posición y van a toda velocidad con la primera orden | Deja el brazo en la postura inicial antes de apagar y llama a write() antes de attach() |
| El hombro no levanta el brazo o se cae despacio | Falta torque | Haz el cálculo de torque; usa un MG996R, un brazo más corto, un contrapeso o un muelle |
| Una articulación se atasca en un extremo y se calienta | El servo empuja contra un tope mecánico | Escribe en el código los límites MIN_A y MAX_A de la calibración |
| El servo de la pinza se calienta al sujetar un objeto | La pinza sigue intentando cerrar y el servo se queda bloqueado | Ajusta el ángulo cerrado al punto en que justo toca el objeto y añade goma a los dedos |
| El PCA9685 está conectado pero ningún servo se mueve | Sin tensión en el borne V+ o sin tierra común | Da 5–6 V a V+ y comprueba la dirección I2C (0x40) |
| Una articulación patina con carga | El horn de plástico se ha desdentado o falta el tornillo central | Usa un horn metálico y fíjalo al eje con el tornillo central |
Ideas de proyectos con el brazo robótico
- Brazo clasificador de colores: Lee el color de unos cubos con un sensor de color TCS3200 y que el brazo deje cada color en su caja.
- Control por gestos: Con un MPU6050 en un guante, el brazo robótico se inclina hacia el mismo lado que tu muñeca.
- Mando inalámbrico: Conecta dos joysticks a otro Arduino y envía los comandos con un nRF24L01; tendrás un mando a distancia que no necesita el móvil.
- Control desde el navegador: Crea deslizadores en una página web con un ESP32 y controla el brazo desde cualquier dispositivo de la misma red Wi-Fi; un WebSocket en el ESP32 reduce el retardo al mínimo.
- Recoger de una cinta: Construye una pequeña cinta transportadora con un motor paso a paso; cuando un TCRT5000 detecte un objeto, el brazo lo recoge de la cinta.
- Concurso y proyecto escolar: Un brazo que graba y reproduce movimientos es una gran opción entre las ideas para ferias de ciencias, porque muestra mecánica, electrónica y software a la vez.
Un brazo robótico con Arduino te enseña a la vez diseño mecánico, cálculo de torque, gestión de la alimentación y programación de movimientos suaves, y crece con cada hora que le dedicas. Empieza con un brazo de 4 servos y potenciómetros, añade grabar y reproducir y luego pasa a 6 servos con un PCA9685 y Bluetooth. En la app BlueGrays encontrarás proyectos como el control de servos con potenciómetro y joystick, el control por Bluetooth desde el móvil y una coreografía de 32 servos, con esquemas de circuito y código paso a paso, para que pruebes primero cada articulación de tu brazo por separado.
Preguntas frecuentes
¿Qué servo usar para un brazo robótico con Arduino?
Para las articulaciones que soportan carga, como la base, el hombro y el codo, el MG996R con engranajes metálicos y 9–11 kg·cm de torque es una buena opción. Para las articulaciones ligeras, como la muñeca y la pinza, basta un MG90S o un SG90. Los brazos pequeños hechos solo con SG90 funcionan si son cortos y ligeros, pero solo levantan objetos de unas decenas de gramos.
¿Qué fuente de alimentación necesita un brazo robótico?
Para un brazo pequeño con 4 servos SG90 o MG90S basta un adaptador de 5 V 3 A. Para un brazo de 6 servos con 3 MG996R y 3 servos pequeños, se recomienda una fuente conmutada de 5–6 V de al menos 5 A, o de 10 A para ir holgado. No olvides unir el negativo de la fuente al GND del Arduino.
¿Cuántos servos se pueden conectar a un Arduino?
La librería Servo maneja hasta 12 servos en el Arduino Uno y 48 en el Mega. Pero el límite real es la corriente: todos los servos deben alimentarse con una fuente externa. Un módulo PCA9685 controla 16 servos usando solo dos pines I2C y se pueden encadenar hasta 62 módulos.
¿Por qué tiemblan los servos de mi brazo robótico?
Las causas más habituales son una fuente insuficiente, la falta de tierra común y el ruido en las lecturas de los potenciómetros. Usa una fuente externa con un condensador de 1000 µF en la línea de los servos y aplica en el código un filtro de suavizado y una banda muerta. Si usas SoftwareSerial junto con la librería Servo, pasar los servos a un PCA9685 elimina el temblor por completo.
¿Cuánto peso puede levantar un brazo robótico con SG90?
El torque de bloqueo del SG90 ronda 1,8 kg·cm; para trabajar con seguridad, cuenta con la mitad, unos 0,9 kg·cm. Como el propio peso del brazo y de los servos se come parte de ese margen, un brazo con SG90 de unos 15 cm solo puede llevar en la práctica objetos ligeros de 20–30 g, como un tapón de botella o un cubo pequeño de espuma.
¿Cómo se controla un brazo robótico desde el móvil?
Se conecta un módulo Bluetooth HC-05 al Arduino y, desde una terminal serie Bluetooth o una app de control con deslizadores, el móvil envía comandos de texto como "S120". Con un ESP32 no necesitas módulo extra gracias a su Bluetooth integrado. El HC-05 no funciona con el iPhone, así que para el iPhone necesitas un módulo BLE o el ESP32 BLE.
Proyectos para hacer después de leer esto
Ver todos los proyectos
Funcionamiento del servomotor con potenciómetro
A medida que giras el mando del potenciómetro, el brazo del servomotor gira con él: un extremo del mando equivale a cero grados y el otro a media vuelta. El Arduino lee sin parar la posición del mando

Servocontrol con joystick
Controla dos motores con un joystick, igual que si giraras una cámara de seguridad. Si empujas la palanca a izquierda o derecha, el servomotor de abajo gira despacio; si la empujas arriba o abajo, el

Uso del servomotor
El brazo de un pequeño servomotor gira por turnos a tres posiciones: al principio del todo, justo al centro y al final del todo, esperando dos segundos en cada parada. El Arduino le dice al motor en q

Servomotor ESP32 (control mediante potenciómetro)
En este proyecto controlamos un servomotor SG90 mediante un potenciómetro. Ajustamos el ángulo del servo asignando el valor analógico leído en el potenciómetro al rango de 0 a 180 grados. La biblioteca ESP32Servo genera señales de servo en los GPIO adecuados del ESP32 que admiten salida; en este proyecto se utiliza GPIO 18. Este es un método de control básico para brazos robóticos, cardanes de cámara y mecanismos de puertas automáticas.

Controlar un LED desde el móvil por Bluetooth
Un pequeño ensayo para encender y apagar las luces de tu habitación desde el móvil. El módulo Bluetooth pasa al Arduino las letras que envías desde el teléfono; una letra enciende el LED rojo, otra en

Coche controlado por Bluetooth con L298N
Convierte tu móvil en un mando y conduce tu propio coche robot. El Arduino recibe por un módulo Bluetooth las órdenes de una app de control remoto en el móvil y mueve las ruedas con un controlador de

Coreografía de servos en círculo - Patrones animados con 32 servos
Un proyecto de animación visual que utiliza 32 servomotores dispuestos en círculo en un Arduino Mega. Los servos se gestionan en una única matriz y se conectan a los pines 22-53 de Mega para mostrar cuatro secuencias de movimiento diferentes: una vibración caótica en la que todos los servos se mueven en ángulos aleatorios, un barrido síncrono en el que todos se mueven al mismo tiempo, una onda que gira suavemente alrededor de un círculo y, por último, una aguja de brújula que oscila a izquierda
Más artículos
App BlueGrays
Monta este circuito paso a paso en tu teléfono
Esquema, código y lista de piezas en una sola pantalla. Más de 200 proyectos de Arduino, ESP32 y Pico listos para montar.
Escanea con tu teléfono
Apunta la cámara al código y se abrirá la tienda de apps de tu teléfono.
o ábrela en la tienda

