Conozca a Walbi, un humanoide a escala 50% con programas Arduino para captura y reproducción de movimiento. Se mueve a mano, graba y reproduce luego los movimientos. El WALink BIped es un robot creado por Pedro y Gil Tavares, de Lisboa, para un proyecto de aprendizaje automático que no se concretó.
Walbi usa un Arduino Nano como «cerebro», servos LX-16A de «músculos», y partes plásticas impresas en 3D como «huesos». Los servos LewanSoul LX-16A son servos ideales para pequeños proyectos robóticos, ya que son livianos, pueden mover cargas de más de 19 kg/cm, y se conectan con un solo cable que va de servo a servo, lo que hace que el cableado del robot sea un juego de niños.
Walbi es un humanoide a escala 50%: sus piernas miden 55 cm de altura desde el talón hasta la cintura, y pesan 1,1 kg. Las partes blancas de su cuerpo fueron impresas en 3D, pero podrían haberse hecho fácilmente con madera resistente y liviana.
La programación de Walbi es muy sencilla. Usted puede descargar los dos programas necesarios para realizar la captura y reproducción de movimientos, y entonces puede hacer que Walbi camine, se arrastre, suba, salte o baile. Solo tiene que mover sus piernas a una postura deseada, registrar esa postura, darle forma a Walbi en otra postura, grabarla y así sucesivamente, y luego, cuando haya grabado la secuencia completa, puede sentarse y ver cómo se desempeña hábilmente siguiendo los movimientos que aprendió.
Qué se necesita
Componentes de hardware (sí, siempre hay que comprar algunas cosas):
- 1 Arduino Nano R3
- 10 LX-16A servo
- 1 Placa de interfaz BusLinker
Aplicaciones de software y/o servicios en línea: Arduino IDE
Herramientas manuales y máquinas de fabricación: Impresora 3D (genérica)
Construyendo a Walbi
Las piezas de Walbi se imprimieron en 3D, con plástico PLA, utilizando una impresora FlashForge Creator Pro. Descargar los archivos STL de Thingiverse, o usar un método alternativo para construir los pies, los “huesos” de las piernas y la cintura, utilizando madera o metal. Los soportes de los servos encajan en estas partes, y unen los servos con ellas.
Como se muestra en el dibujo de abajo, necesitará soportes metálicos de los cuatro tipos diferentes disponibles para adjuntar los servos a las partes impresas, y entre sí.
Conexionado
Para controlar los servos LX-16A se necesita una placa de LewanSoul llamada Bus Linker.
Ésta recibirá comandos desde un puerto serie en el Arduino Nano. Como utilizamos la USART del hardware de Arduino para comunicarnos con la computadora, recurrimos a la biblioteca SoftwareSerial para crear un segundo puerto serie en el Nano, que nos sirve para conectarnos a la placa Bus Linker.
El cableado se minimiza con estos servos serie. Hay un cable que va de cada servo al siguiente (un cable serie provisto con los servos) y los servos se enchufan directamente a la placa de depuración. Su computadora se conecta al puerto USB de Arduino, y Arduino se conecta a la placa de depuración mediante tres cables (TX, RX y GND) conectados a los pines de Arduino que fueron configurados para SoftwareSerial.
Los servos utilizan una velocidad de comunicación serie en baudios de 115200 (que es demasiado alto y falta investigar si se modificar). Esta velocidad en baudios es alta para SoftwareSerial, por lo que tuvimos que implementar funciones de comprobación de errores y reintento. En algunos casos se necesitaba persistencia para obtener una lectura correcta.
Fuerza
Los servos pueden proporcionar 19,5kg.cm a 7,4v. Usamos 6v y la corriente en estado quieto resultó inferior a tres amperios.
Programación
Puedes obtener el código Arduino en el repositorio de Github del proyecto.
Se utilizan dos programas para la captura y reproducción de movimiento, una técnica similar a la que se usa en las películas. Empiezas poniendo al robot en una pose. Como los servos están predeterminados para apagar el motor, se pueden girar los servos a mano. Una vez que se tiene el robot en la posición deseada, se usa el programa Walbi_record para leer y mostrar todos los ángulos de servo. Usted luego alimenta esas lecturas de ángulo en la variable poseAngles en Walbi_play, y usa el programa para reproducir la secuencia de poses grabadas a una velocidad establecida por la variable timeToMove (en milisegundos).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 |
// // Walbi, the walking biped // // a project by The Inner Geek / Pedro Tavares Silva // // Tutorial at https://releasetheinnergeek.wordpress.com/ // // Hardware: - Arduino Nano // - LewanSoul LX16-A serial servos x10 // - LewanSoul Debug Board // // GNU General Public License v2.0 // // This program reads and displays the angle of each servo of the robot // #include <SoftwareSerial.h> SoftwareSerial mySerial(10, 11); // 10 = Arduino soft RX, 11 = Arduino soft TX String message = ""; class CommandFeedback { public: byte servoId; byte command; byte parameter[7]; byte numPar; boolean messageComplete = false; int angle(void) { return (int(this->parameter[1]) * 256 + int(this->parameter[0])); }; }; CommandFeedback response; void setup() { Serial.begin(115200); // hardware serial - USB cable from Arduino to PC running Arduino IDE Serial Monitor while (!Serial) {} Serial.println("\n\nHardware serial ready"); mySerial.begin(115200); // SoftwareSerial - connects Arduino to Debug Board serial pins (RX->TX, TX->RX, GND->GND) Serial.println("PROGRAM VERSION 1.1.5\n\nSoftware serial ready\n\nSend 'R' to read servos"); } // ========== SEND COMMAND ===================================== // ============================================================= void sendReadCommandToServos(byte servoID) { byte command[6]; command [0] = 0x55; command [1] = 0x55; command [2] = servoID; command [3] = 3; command [4] = 28; // SERVO_POS_READ command [5] = (~(servoID + 3 + 28)) & 0xff; mySerial.write(command, sizeof(command)); } void process() { if (sizeof(message)) { switch (message.length()) { case 1: // discard message if first byte is invalid if (message[0] != 85) { message = ""; // Serial.println("Discarder invalid byte"); } else // Serial.println("First byte ok"); break; case 2: // discard message if second byte is invalid if (message[1] != 85) { message = ""; // Serial.println("Discarded two invalid bytes"); } else // Serial.println("Second byte ok"); break; case 3: case 4: case 5: case 6: // not enough data, wait for more data // Serial.println("Waiting for more data"); break; default: // 7 and above // Serial.println("Analyzing..."); if (message.length() == (byte(message[3]) + 3)) { // servo message complete // Serial.println("Got enough data."); if (false) {// will check checksum in next version // checksum = (~(id + length + cmd + [5..len-1])) & 0xff message = ""; Serial.println("Bad checksum. Discarded data."); } else { response.servoId = int(message[2]); response.command = int(message[4]); for (int i = 0; i < (int(message[3]) - 3); i++) response.parameter[i] = message[5 + i]; response.numPar = message[3] - 3; response.messageComplete = true; message = ""; // Serial.println("Good data. Loaded into object."); } } else { // Serial.print("Not enough data. Needed "); // Serial.print(int(message[3]) + 3); // Serial.print(" bytes and got "); // Serial.println(message.length()); } } } } boolean listen(int timeOut) { int wait = 0; char incomming; do { if (mySerial.available()) { incomming = mySerial.read(); if (incomming == char(-1)) Serial.println("ERROR reading SoftwareSerial"); else { message += incomming; // Serial.print("\nmessage is now "); // Serial.print(message.length()); // Serial.println(" chars long\n"); // Serial.print("Going to process string <"); // Serial.print(message); // Serial.println(">"); process(); } } wait++; delay(1); } while ((wait < timeOut) and (!response.messageComplete)); if (wait == timeOut) { Serial.println("ERROR: timed-out"); return (false); } else return (true); } void loop() { byte incomingByte; if (Serial.available() > 0) { // wait for command R incomingByte = Serial.read(); if (char(incomingByte) == 'R') { for (int i = 1; i <= 10; i++) { for (int a = 1; a <= 10; a++) { sendReadCommandToServos(i); //read servo 1 if (listen(20)) break; } if (response.messageComplete) { Serial.print("Servo "); Serial.print(response.servoId); Serial.print(" angle: "); Serial.println(response.angle()); response.messageComplete = false; } } } else { Serial.print("\n\nInvalid command received: "); Serial.print("(" + String(char(incomingByte)) + ")"); } } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 |
// // Walbi, the walking biped // // a project by The Inner Geek / Pedro Tavares Silva // // Tutorial at https://releasetheinnergeek.wordpress.com/ // // Hardware: - Arduino Nano // - LewanSoul LX16-A serial servos x10 // - LewanSoul Debug Board // // GNU General Public License v2.0 // // This program replays a series os poses that can be recorded with Walbi_record.ino and // stored in the poseAngles array. // #include <SoftwareSerial.h> SoftwareSerial mySerial(10, 11); // pin 10 = Arduino soft RX (connects to debug board TX) // pin 11 = Arduino soft TX (debub board RX) // // This code by LewanSoul to control LX16-A serial servos (not provided as library) // #define GET_LOW_BYTE(A) (byte)((A)) #define GET_HIGH_BYTE(A) (byte)((A) >> 8) #define BYTE_TO_HW(A, B) ((((unsigned int)(A)) << 8) | (byte)(B)) #define LOBOT_SERVO_FRAME_HEADER 0x55 #define LOBOT_SERVO_MOVE_TIME_WRITE 1 byte buf[10]; byte LobotCheckSum() { byte i; unsigned int temp = 0; for (i = 2; i < buf[3] + 2; i++) { temp += buf[i]; } temp = ~temp; i = (byte)temp; return i; } void moveServo(byte id, unsigned int position, unsigned int time) { buf[0] = buf[1] = LOBOT_SERVO_FRAME_HEADER; buf[2] = id; buf[3] = 7; buf[4] = 1; //LOBOT_SERVO_MOVE_TIME_WRITE buf[5] = GET_LOW_BYTE(position); buf[6] = GET_HIGH_BYTE(position); buf[7] = GET_LOW_BYTE(time); buf[8] = GET_HIGH_BYTE(time); buf[9] = LobotCheckSum(); mySerial.write(buf, 10); } // // End of code by LewanSoul // void setup() { Serial.begin(115200); // hardware serial - USB cable from Arduino to PC running Arduino IDE Serial Monitor while (!Serial) {} mySerial.begin(115200); // SoftwareSerial - connects Arduino to Debug Board serial pins (RX->TX, TX->RX, GND->GND) Serial.println("Walby play VERSION 1.0.0"); } int timeToMove = 1500; const byte numPoses = 5; // replaying a sequence of 5 poses, using 3 different poses recorded previously unsigned int duration [numPoses] = {timeToMove, // how many miliseconds the robot has to move from pose 0 > 1 timeToMove, // from pose 1 > 2 timeToMove, // ... timeToMove, timeToMove }; boolean runOnce = true; // when the program starts the robot goes immediatly to the first pose byte pose [numPoses] = {0, 1, 2, 1, 0}; // this is the order the poses will be played back unsigned int poseAngles [3] [10] = { {565, 576, 538, 505, 357, 418, 489, 489, 641, 479}, // each pose has the angle value for each of the 10 servos {372, 575, 894, 504, 533, 605, 499, 164, 641, 292}, {294, 577, 1004, 505, 586, 672, 495, 48, 640, 234} }; void loop() { byte incomingByte; if (runOnce) { for (int b = 0; b < 10; b++) moveServo( b + 1, poseAngles [0] [b], 500 - b ); Serial.println("Starting posture"); runOnce = false; } Serial.println("Enter the number of the last pose to replay (first is zero, last is 4)"); do {} while (Serial.available() == 0); incomingByte = Serial.read() - '0'; if ((incomingByte >= 0) and (incomingByte < numPoses)) { Serial.print("\nPlaying movements 0 to "); Serial.println(incomingByte); for (int a = 0; a < incomingByte + 1; a++) { for (int b = 0; b < 10; b++) moveServo(b + 1, poseAngles[ pose [a] ] [b], duration [a] - b ); Serial.print("Pose: "); Serial.println( pose [a] ); delay( duration [a] - 10 ); }; } else Serial.println("\nInvalid command!"); } |
Aquí hay algunos consejos y trucos aprendidos al crear Walbi:
- Los soportes para el LX-16A solo se acoplan al servo en UNA posición, por lo que es muy fácil conectarlos incorrectamente, especialmente a las partes impresas en 3D. Tuvimos que reensamblar a Walbi un par de veces para corregir errores de montaje que eran bastante difíciles de detectar.
- Los servos vienen con identificación ID 1 por defecto. Asigne a cada servo una ID diferente antes de montarlos en el robot, o será imposible comunicarse con varios servos serie conectados con la misma ID.
- El uso de bridas para cables realmente mejora la apariencia.
- Los servos vienen con los tornillos necesarios para conectar el disco de acoplamiento de los servos, y el disco a los soportes. Los soportes vienen con los tornillos necesarios para sujetarlos a los servos. Tendrá que comprar tornillos por separado para sostener las conexiones y para el soporte de las piezas de plástico. Se utilizan tornillos y tuercas DIN912 M2-6 y M2-10.
- Es posible mejorar la tracción pegando almohadillas de silicona en las plantas de los pies del robot.
- Es preferible usar discos de acoplamiento de metal para servo, ya que las de plástico que vienen provistas con los servos se romperán en el caso de que las piernas se golpeen durante las pruebas. Si estas piezas se rompen, el robot se aflojará y la reproducción del movimiento perderá precisión. De otra manera, es esta reproducción es sorprendentemente buena.
Piezas a medida
STL para piezas impresas en 3D (Originalmente impreso en un Flash Forge Creator Pro.)
Código Programas Arduino para control de movimiento y reproducción
En Alienexpress encontré algunas publicaciones que pueden servir de guía para obtener los elementos:
SERVO
JUEGO DE SERVO Y ACCESORIOS
SERVO Y PIEZAS DE MONTAJE
Pingback: Chips de potencia ultra baja ayudan a hacer robots pequeños más capaces | Robots Didácticos
Pingback: Piernas robóticas que se basan en la evolución animal para aprender a caminar | Robots Didácticos