Manufactura industrial
Internet industrial de las cosas | Materiales industriales | Mantenimiento y reparación de equipos | Programación industrial |
home  MfgRobots >> Manufactura industrial >  >> Manufacturing Technology >> Proceso de manufactura

MobBob:Robot Arduino DIY controlado por un teléfono inteligente Android

Componentes y suministros

Arduino Nano R3
× 1
Módulo Bluetooth HC-05
× 1
Micro-servo motor SG90
× 4
portapilas 4xAA
× 1
Pilas AA
× 4

Herramientas y máquinas necesarias

Soldador (genérico)
Impresora 3D (genérica)

Aplicaciones y servicios en línea

Arduino IDE

Acerca de este proyecto

Hice este robot de acuerdo con las instrucciones del sitio web. Una gran idea para la simbiosis entre Arduino y Android.

MobBob es un robot controlado por teléfono inteligente. Aprovechando el poder de su teléfono inteligente, MobBob es un robot que camina y habla con reconocimiento de voz y visión por computadora.

Piezas impresas en 3D:https://www.thingiverse.com/thing:715688

Archivo Android.apk:https://apkpure.com/mobbob/com.cevinius.MobBob

En el código querrás:

- Actualice las variables del pin para que coincidan con su compilación

- Ajustar el centro del servo, los valores mínimo y máximo

- Establezca "FRONT_JOINT_HIPS" en 1 o -1 dependiendo de la forma en que estén montados los servos de cadera. Lo monte con el eje servo en la parte delantera de MobBob. Para esta configuración, establezca este valor en 1.

Código

  • código
código Arduino
 / * * =============================================================* Programa de control MobBob - Versión de Bluetooth en serie del software * por Kevin Chan (también conocido como Cevinius) * ===========================================================* * Este programa permite controlar MobBob mediante comandos en serie. En esta versión del código, los comandos * se reciben a través de un puerto serie de software, con pines definidos en #define cerca de la parte superior. * Esto significa que puede usar cualquier placa compatible con Arduino y conectar una tarjeta bluetooth en los pines establecidos para * el software en serie. (A diferencia de la otra versión de este diseñada para la placa Bluno de DFRobot.) * * Este programa es largo y contiene 2 componentes principales:un programa de animación servo suave y un programa analizador de comandos en serie *. * * Sistema de animación * ================* El programa de animación está diseñado para animar matrices de fotogramas clave de servo sin problemas. El código intenta hacer todo lo posible para que sea fácil de usar. * * El sistema de animación solo pondrá en cola 1 comando. es decir, se puede ejecutar un comando, * y un comando se puede poner en cola. Si envía más comandos, sobrescribirán el comando en cola. * * El sistema de animación esperará por defecto para terminar la animación actual antes de comenzar la siguiente. Esto * significa que si los datos de la animación terminan con el robot en su pose base, las cosas se unirán sin problemas. Para * apoyar esto, el sistema de animación también tiene una función en la que una animación puede tener una "secuencia de finalización" * para volver a poner al robot en la pose base. Esta función se utiliza para las animaciones de caminar hacia adelante / hacia atrás. * Esas animaciones tienen una secuencia final que devuelve al robot a la pose base. * * Cuando finaliza la reproducción de una animación, el sistema de animación generará una cadena de respuesta al puerto serie. * Esto permite a las personas que llaman saber cuándo terminaron de reproducirse las animaciones que solicitaron. Esto es útil * para que los usuarios secuencian animaciones, esperando a que una termine antes de comenzar otra. * * El código de animación tiene muchas variables para permitir que se modifiquen las cosas. P.ej. Frecuencia de actualización, pines arduino, etc. * * El formato de matriz de datos de animación también está diseñado para ser fácil de editar a mano. * * Analizador de comandos * ==============* Este sistema analiza los comandos recibidos en serie y los procesa. Los comandos incluyen uno para configurar directamente * las posiciones de los servos, así como comandos para activar animaciones y caminatas predefinidas. * * Por lo tanto, los usuarios que no quieran preocuparse por los detalles de la caminata pueden usar las caminatas / animaciones predefinidas. * Y los usuarios que quieran un control total sobre los servos (para crear nuevas animaciones sobre la marcha) también pueden hacerlo. * * Como se mencionó anteriormente, estos comandos se pueden usar de forma interactiva desde Arduino Serial Monitor. También pueden * enviarse usando Bluetooth LE (cuando se usa un Bluno). La aplicación del teléfono enviará los comandos a través de Bluetooth LE al * Bluno. * * Comandos generales:* ----------------- * Verificación Listo / OK: * Verificación de estado. La respuesta se devuelve inmediatamente para comprobar si el controlador está funcionando. * * Establecer Servo: * tiempo - tiempo para interpolar a ángulos especificados, 0 saltará inmediatamente a ángulos * leftHip - microsegundos desde el centro. -ve es la cadera hacia adentro, + ve es la cadera hacia afuera * leftFoot - microsecs desde plano. -ve es pie abajo, + ve es pie arriba * rightHip - microsegundos desde el centro. -ve es la cadera hacia adentro, + ve es la cadera hacia afuera * pie derecho - microsegundos desde plano. -ve es pie abajo, + ve es pie arriba * Este comando se usa para tener control total sobre los servos. Puede interpolar el robot desde su * pose actual a la pose especificada durante el tiempo especificado. * * Detener / Reiniciar: * Detiene el robot después de la animación actual. Se puede utilizar para detener animaciones configuradas en bucle * indefinidamente. Esto también se puede usar para poner el robot en su pose base (de pie) * * Detener inmediatamente: * Detiene el robot inmediatamente sin esperar a completar la animación actual. Esto * interrumpe la animación actual del robot. Potencialmente, el robot puede estar en mitad de la animación * y en una pose inestable, así que tenga cuidado al usar esto. * * Comandos de caminata estándar:* ----------------------- * Adelante:, -1 significa continuo, 0 o ningún parámetro es lo mismo que 1 vez. * Hacia atrás:, -1 significa continuo, 0 o ningún parámetro es lo mismo que 1 vez. * Girar a la izquierda:, -1 significa continuo, 0 o ningún parámetro es lo mismo que 1 vez. * Girar a la derecha:, -1 significa continuo, 0 o ningún parámetro es lo mismo que 1 vez. * * Comandos de animación divertidos:* ----------------------- * Shake Head:, -1 significa continuo, 0 o ningún parámetro es lo mismo que 1 vez. * * Rebote:, -1 significa continuo, 0 o ningún parámetro es lo mismo que 1 vez. * * Wobble:, -1 significa continuo, 0 o ningún parámetro es lo mismo que 1 vez. * Wobble Left:, -1 significa continuo, 0 o ningún parámetro es lo mismo que 1 vez. * Wobble Right:, -1 significa continuo, 0 o ningún parámetro es lo mismo que 1 vez. * * Tap Feet:, -1 significa continuo, 0 o ningún parámetro es lo mismo que 1 vez. * Toque el pie izquierdo:, -1 significa continuo, 0 o ningún parámetro es lo mismo que 1 vez. * Toque con el pie derecho:, -1 significa continuo, 0 o ningún parámetro es lo mismo que 1 vez. * * Shake Legs:, -1 significa continuo, 0 o ningún parámetro es lo mismo que 1 vez. * Agitar la pierna izquierda:, -1 significa continuo, 0 o ningún parámetro es lo mismo que 1 vez. * Agitar la pierna derecha:, -1 significa continuo, 0 o ningún parámetro es lo mismo que 1 vez. * * Además, el código enviará una cadena de respuesta a través de Serial cuando los comandos hayan terminado * de ejecutarse. * * Si el comando terminó normalmente, la cadena de respuesta es el código de comando sin * parámetros. P.ej. Cuando haya terminado de avanzar, enviará la respuesta "". * * Si un comando se interrumpió con , es posible que la animación actual se haya detenido a la mitad. * En este caso, el robot podría estar en una extraña pose a mitad de camino, y es posible que no se haya jugado a los Anims de acabado *. Para que el usuario sepa que esto ha sucedido, la cadena de respuesta tendrá el * parámetro -1. Por ejemplo, si una caminata se detuvo a mitad de camino usando , la cadena de respuesta sería *  para indicar que la caminata se detuvo, pero se detuvo a mitad de camino. * (Nota:si usa  para detenerse, esperará a que se complete el ciclo de animación actual * antes de detenerse. Por lo tanto, las animaciones no se detendrán a la mitad en ese caso). * * Porque las respuestas se envían después de un La animación está completa, el remitente del comando puede * buscar las cadenas de respuesta para determinar cuándo el robot está listo para un nuevo comando. * P.ej. Si usa el comando , la cadena de respuesta no se envía hasta que se completen los 3 pasos * (y termine la animación). Entonces, el remitente del comando puede esperar la cadena de respuesta * antes de decirle al robot que haga lo siguiente. * / #include  #include  // -------------------------------- -------------------------------------------------- // Velocidad de comunicación en serie:configure esto para su tarjeta en serie (bluetooth) .//------------------------------- -------------------------------------------------- - // Velocidad de comunicación en serie con la placa bluetooth.// Algunas placas están predeterminadas a 9600. La placa que tengo tiene un valor predeterminado de 115200. # define SERIAL_SPEED 115200 // Configure un puerto serie de software en estos pines.const int rxPin =11; // pin usado para recibir dataconst int txPin =12; // pin utilizado para enviar datosSoftwareSerial softwareSerial (rxPin, txPin); // ---------------------------------- ------------------------------------------------ // Configure los pines Arduino:configúrelos para su robot en particular .//------------------------------------- --------------------------------------------- const int SERVO_LEFT_HIP =5; const int SERVO_LEFT_FOOT =2; const int SERVO_RIGHT_HIP =3; const int SERVO_RIGHT_FOOT =4; // ¡Quiero que este código se pueda utilizar en todos los bípedos de 4 servo! (Como Bob, MobBob) // Me di cuenta de que algunas compilaciones montan los servos de cadera de una manera diferente // a como lo hice con MobBob, así que esta configuración te permite configurar el código // para cualquiera de los estilos de compilación.// 1 para el estilo MobBob caderas orientadas hacia el frente (articulación hacia el frente) // -1 para caderas orientadas hacia atrás estilo Bob (articulación hacia atrás) #define FRONT_JOINT_HIPS 1 // ------------------- -------------------------------------------------- ------------- // Constantes de Servo Max / Min / Center:configúrelas para su robot en particular.//------------------ -------------------------------------------------- -------------- const int LEFT_HIP_CENTRE =1580; const int LEFT_HIP_MIN =LEFT_HIP_CENTRE - 500; const int LEFT_HIP_MAX =LEFT_HIP_CENTRE + 500; const int LEFT_FOOT_CENTRE =1410; const int LEFT_CENT_FOT_MIN; const int LEFT_FOOT_MAX =LEFT_FOOT_CENTRE + 500; const int RIGHT_HIP_CENTRE =1500; const int RIGHT_HIP_MIN =RIGHT_HIP_CENTRE - 500; const int RIGHT_HIP_MAX =RIGHT_HIP_CENTRE + 500; const int RIGHT_HIP_CENTRE + 500; const int RIGHT_HIP_CENTRE 465; const int RIGHT_FOOT_MIN =RIGHT_FOOT_CENTRE - 500; const int RIGHT_FOOT_MAX =RIGHT_FOOT_CENTRE + 500; // ------------------------------ ------------------------------------------------ // Funciones auxiliares para ayudar a calcular los valores de las articulaciones de una manera más fácil de usar .// Aquí puede ajustar los signos si los servos están configurados de una manera diferente. // los servos se configuran de manera diferente .// (Ej. Los servos de cadera originales de Bob están al revés de los de MobBob.) //// (Además, me resulta difícil recordar los signos que debo usar para cada servo, ya que // son diferentes para las caderas izquierda / derecha y para los pies izquierdo / derecho). // ------------------------------------------------ ------------------------------ int LeftHipCentre () {return LEFT_HIP_CENTRE; } int LeftHipIn (int milisegundos) {return LEFT_HIP_CENTRE + (FRONT_JOINT_HIPS * milisegundos); } int LeftHipOut (int milisegundos) {return LEFT_HIP_CENTRE - (FRONT_JOINT_HIPS * milisegundos); } int RightHipCentre () {return RIGHT_HIP_CENTRE; } int RightHipIn (int milisegundos) {return RIGHT_HIP_CENTRE - (FRONT_JOINT_HIPS * milisegundos); } int RightHipOut (int milisegundos) {return RIGHT_HIP_CENTRE + (FRONT_JOINT_HIPS * milisegundos); } int LeftFootFlat () {return LEFT_FOOT_CENTRE; } int LeftFootUp (int milisegundos) {return LEFT_FOOT_CENTRE - milisegundos; } int LeftFootDown (int milisegundos) {return LEFT_FOOT_CENTRE + milisegundos; } int RightFootFlat () {return RIGHT_FOOT_CENTRE; } int RightFootUp (int milisegundos) {return RIGHT_FOOT_CENTRE + milisegundos; } int RightFootDown (int milisegundos) {return RIGHT_FOOT_CENTRE - milisegundos; } // ----------------------------------------------- ----------------------------------- // Datos de animación de fotogramas clave para la marcha estándar y otras animaciones servo // // El formato es {, ​​, , , } // milisegundos:tiempo para interpolar a las posiciones de este fotograma clave. P.ej. 500 significa que se necesitarán 500 ms para ir desde la // posición del robot al comienzo de este cuadro a la posición especificada en este cuadro // leftHipMicros - posición de la cadera izquierda en microsec. De servo. // leftFootMicros - posición de la cadera izquierda en servo microsecs.// rightHipMicros - posición de la cadera izquierda en servo microsecs.// rightFootMicros - posición de la cadera izquierda en servo microsecs.// // Los valores de micro servo, admiten un valor especial de -1. Si se da este valor, // le dice al código de animación que ignore este servo en este fotograma clave. es decir, ese servo // permanecerá en la posición que tenía al comienzo de este fotograma clave. //// Además, el primer elemento en el archivo de datos de animación es especial. Es un elemento de metadatos .// El primer elemento es {, 0, 0, 0, 0}, que nos dice el número de cuadros // en la animación. Entonces, el primer fotograma clave real está en animData [1], y el último fotograma clave // ​​está en animData []. (Donde  es el valor en animData [0] [0].) // ----------------------------- -------------------------------------------------- --- // Constantes para hacer que el acceso a las matrices de fotogramas clave sea más legible por humanos.const int TWEEN_TIME_VALUE =0; const int LEFT_HIP_VALUE =1; const int LEFT_FOOT_VALUE =2; const int RIGHT_HIP_VALUE =3; const int RIGHT_FOOT_VALUE used =4; // Constantes en la animación de caminar caminando data.const int FOOT_DELTA =150; const int HIP_DELTA =FRONT_JOINT_HIPS * 120; // Va a la posición erguida predeterminada. Usado por stopAnim (). Int standStraightAnim [] [5] ={// Metadatos. El primer elemento es el número de fotogramas. {1, 0, 0, 0, 0}, // Pies planos, Pies pares {300, LeftHipCentre (), LeftFootFlat (), RightHipCentre (), RightFootFlat ()}}; // Antes de esto, haga que el robot Pies planos, pies uniformes (es decir, standStraightAnim) .int walkForwardAnim [] [5] ={// Metadatos. El primer elemento es el número de fotogramas. {8, 0, 0, 0, 0}, // Inclinar a la izquierda, pies uniformes {300, LeftHipCentre (), LeftFootUp (FOOT_DELTA), RightHipCentre (), RightFootDown (FOOT_DELTA)}, // Inclinar a la izquierda, pie derecho adelante {300, LeftHipIn (HIP_DELTA), LeftFootUp (FOOT_DELTA), RightHipOut (HIP_DELTA), RightFootDown (FOOT_DELTA)}, // Pies planos, Pie derecho adelante {300, LeftHipIn (HIP_DELTA), LeftFootFlatOELTA (), RightHipOut RightFootFlat ()}, // Inclinar a la derecha, pie derecho adelante {300, LeftHipIn (HIP_DELTA), LeftFootDown (FOOT_DELTA), RightHipOut (HIP_DELTA), RightFootUp (FOOT_DELTA)}, // Inclinar a la derecha, pies uniformes {300, LeftHipCentre (), LeftFootDown (FOOT_DELTA), RightHipCentre (), RightFootUp (FOOT_DELTA)}, // Inclinar a la derecha, pie izquierdo adelante {300, LeftHipOut (HIP_DELTA), LeftFootDown (FOOT_DELTA), RightHipIn (HIP_DELTA), RightHipIn (HIP_DELTA) , // Pies planos, Pie izquierdo adelante {300, LeftHipOut (HIP_DELTA), LeftFootFlat (), RightHipIn (HIP_DELTA), RightFootFlat ()}, // Inclinar a la izquierda, Pie izquierdo adelante {300, LeftHipOut (HIP_DEL TA), LeftFootUp (FOOT_DELTA), RightHipIn (HIP_DELTA), RightFootDown (FOOT_DELTA)}}; // Antes de esto, lleve el robot a Feet Flat, Feet Even (p. Ej. standStraightAnim) .int walkBackwardAnim [] [5] ={// Metadatos. El primer elemento es el número de fotogramas. {8, 0, 0, 0, 0}, // Inclinar hacia la izquierda, pies uniformes {300, LeftHipCentre (), LeftFootUp (FOOT_DELTA), RightHipCentre (), RightFootDown (FOOT_DELTA)}, // Inclinar a la izquierda, pie izquierdo adelante {300, LeftHipOut (HIP_DELTA), LeftFootUp (FOOT_DELTA), RightHipIn (HIP_DELTA), RightFootDown (FOOT_DELTA)}, // Pies planos, Pie izquierdo adelante {300, LeftHipOut (HIP_DELTA), LeftFootDELTA (), Right_HipIn RightFootFlat ()}, // Inclinación hacia la derecha, pie izquierdo hacia adelante {300, LeftHipOut (HIP_DELTA), LeftFootDown (FOOT_DELTA), RightHipIn (HIP_DELTA), RightFootUp (FOOT_DELTA)}, // Inclinación hacia la derecha, pies uniformes {300, LeftHipCentre (), LeftFootDown (FOOT_DELTA), RightHipCentre (), RightFootUp (FOOT_DELTA)}, // Inclinar a la derecha, pie derecho adelante {300, LeftHipIn (HIP_DELTA), LeftFootDown (FOOT_DELTA), RightHipOut (HIP_DELTA), RightHipOut (HIP_DELTA), RightHipOut (HIP_DELTA) , // Pies planos, pie derecho adelante {300, LeftHipIn (HIP_DELTA), LeftFootFlat (), RightHipOut (HIP_DELTA), RightFootFlat ()}, // Inclinar a la izquierda, pie derecho adelante {300, LeftHipIn (HIP_DELTA ), LeftFootUp (FOOT_DELTA), RightHipOut (HIP_DELTA), RightFootDown (FOOT_DELTA)}}; // Finish walk anim lleva al robot desde el final de walkForwardAnim / walkBackwardAnim de vuelta a standStraightAnim.int walkEndAnim [] [5] ={// Metadata . El primer elemento es el número de fotogramas. {2, 0, 0, 0, 0}, // Inclinar hacia la izquierda, Pies pares {300, LeftHipCentre (), LeftFootUp (FOOT_DELTA), RightHipCentre (), RightFootDown (FOOT_DELTA)}, // Pies planos, Pies uniformes { 300, LeftHipCentre (), LeftFootFlat (), RightHipCentre (), RightFootFlat ()}}; // Antes de esto, lleve el robot a Feet Flat, Feet Even (es decir, standStraightAnim) .int turnLeftAnim [] [5] ={/ / Metadatos. El primer elemento es el número de fotogramas. {6, 0, 0, 0, 0}, // Inclinar hacia la izquierda, pies uniformes {300, LeftHipCentre (), LeftFootUp (FOOT_DELTA), RightHipCentre (), RightFootDown (FOOT_DELTA)}, // Inclinar a la izquierda, girar a la izquierda hip, girar la cadera derecha {300, LeftHipIn (HIP_DELTA), LeftFootUp (FOOT_DELTA), RightHipIn (HIP_DELTA), RightFootDown (FOOT_DELTA)}, // Pies planos, girar la cadera izquierda, girar la cadera derecha {300, LeftHipIn (HIP_DELTA), LeftFootIn (), RightHipIn (HIP_DELTA), RightFootFlat ()}, // Inclinar hacia la derecha, Girar la cadera izquierda, Girar la cadera derecha {300, LeftHipIn (HIP_DELTA), LeftFootDown (FOOT_DELTA), RightHipIn (HIP_DELTA), RightFootUp (FOOT_DELTA)} // Inclinar hacia la derecha, Pies pares {300, LeftHipCentre (), LeftFootDown (FOOT_DELTA), RightHipCentre (), RightFootUp (FOOT_DELTA)}, // Pies planos, Pies pares {300, LeftHipCentre (), LeftFootFlat (), RightHipCentre ( ), RightFootFlat ()}}; // Antes de esto, lleve el robot a Feet Flat, Feet Even (es decir, standStraightAnim) .int turnRightAnim [] [5] ={// Metadata. El primer elemento es el número de fotogramas. {6, 0, 0, 0, 0}, // Inclinar a la derecha, Pies iguales {300, LeftHipCentre (), LeftFootDown (FOOT_DELTA), RightHipCentre (), RightFootUp (FOOT_DELTA)}, // Inclinar a la derecha, Girar a la izquierda cadera, girar la cadera derecha {300, LeftHipIn (HIP_DELTA), LeftFootDown (FOOT_DELTA), RightHipIn (HIP_DELTA), RightFootUp (FOOT_DELTA)}, // Pies planos, Girar la cadera izquierda, Girar la cadera derecha {300, LeftHipIn (HIP_DELTA), LeftFootIn (), RightHipIn (HIP_DELTA), RightFootFlat ()}, // Inclinar a la izquierda, Girar la cadera izquierda, Girar la cadera derecha {300, LeftHipIn (HIP_DELTA), LeftFootUp (FOOT_DELTA), RightHipIn (HIP_DELTA), RightFootDown (FOOT_DELTA)} // Inclinar hacia la izquierda, Pies pares {300, LeftHipCentre (), LeftFootUp (FOOT_DELTA), RightHipCentre (), RightFootDown (FOOT_DELTA)}, // Pies planos, Pies pares {300, LeftHipCentre (), LeftFootFlat (), RightHipCentre ( ), RightFootFlat ()}}; // Agitar la cabeza anim. Izquierda derecha rápidamente para emular sacudiendo la cabeza.int shakeHeadAnim [] [5] ={// Metadatos. El primer elemento es el número de fotogramas. {4, 0, 0, 0, 0}, // Pies planos, Girar a la izquierda {150, LeftHipOut (HIP_DELTA), LeftFootFlat (), RightHipIn (HIP_DELTA), RightFootFlat ()}, // Pies planos, Pies uniformes {150 , LeftHipCentre (), LeftFootFlat (), RightHipCentre (), RightFootFlat ()}, // Pies planos, Girar a la derecha {150, LeftHipIn (HIP_DELTA), LeftFootFlat (), RightHipOut (HIP_DELTA), RightFootFlat Feet ()}, // flat, Feet even {150, LeftHipCentre (), LeftFootFlat (), RightHipCentre (), RightFootFlat ()}}; // Bamboleo anim. Inclina hacia la izquierda y hacia la derecha para hacer un divertido wobble.int wobbleAnim [] [5] ={// Metadata. El primer elemento es el número de fotogramas. {4, 0, 0, 0, 0}, // Inclinar a la izquierda, Pies pares {300, LeftHipCentre (), LeftFootUp (FOOT_DELTA), RightHipCentre (), RightFootDown (FOOT_DELTA)}, // Pies planos, Pies uniformes {300 , LeftHipCentre (), LeftFootFlat (), RightHipCentre (), RightFootFlat ()}, // Inclinar a la derecha, Pies uniformes {300, LeftHipCentre (), LeftFootDown (FOOT_DELTA), RightHipCentre (), RightFootUp (FOOT_DELTA)} flat, Feet even {300, LeftHipCentre (), LeftFootFlat (), RightHipCentre (), RightFootFlat ()}}; // Bamboleo a la izquierda anim. Inclinar hacia la izquierda y hacia atrás.int wobbleLeftAnim [] [5] ={// Metadatos. El primer elemento es el número de fotogramas. {2, 0, 0, 0, 0}, // Inclinar a la izquierda, Pies pares {300, LeftHipCentre (), LeftFootUp (FOOT_DELTA), RightHipCentre (), RightFootDown (FOOT_DELTA)}, // Pies planos, Pies uniformes {300 , LeftHipCentre (), LeftFootFlat (), RightHipCentre (), RightFootFlat ()},}; // Oscila el anim a la derecha. Inclinar hacia la derecha y hacia atrás.int wobbleRightAnim [] [5] ={// Metadatos. El primer elemento es el número de fotogramas. {2, 0, 0, 0, 0}, // Inclinar a la derecha, Pies uniformes {300, LeftHipCentre (), LeftFootDown (FOOT_DELTA), RightHipCentre (), RightFootUp (FOOT_DELTA)}, // Pies planos, Pies uniformes {300 , LeftHipCentre (), LeftFootFlat (), RightHipCentre (), RightFootFlat ()}}; // Toque pies anim. Toque ambos pies.int tapFeetAnim [] [5] ={// Metadatos. El primer elemento es el número de fotogramas. {2, 0, 0, 0, 0}, // Levanta ambos pies, Pies iguales {500, LeftHipCentre (), LeftFootUp (FOOT_DELTA), RightHipCentre (), RightFootUp (FOOT_DELTA)}, // Pies planos, Pies iguales { 500, LeftHipCentre (), LeftFootFlat (), RightHipCentre (), RightFootFlat ()},}; // Toque el pie izquierdo anim.int tapLeftFootAnim [] [5] ={// Metadatos. El primer elemento es el número de fotogramas. {2, 0, 0, 0, 0}, // Levantar el pie izquierdo, Pies iguales {500, LeftHipCentre (), LeftFootUp (FOOT_DELTA), RightHipCentre (), RightFootFlat ()}, // Pies planos, Pies iguales {500 , LeftHipCentre (), LeftFootFlat (), RightHipCentre (), RightFootFlat ()},}; // Toque el pie derecho anim.int tapRightFootAnim [] [5] ={// Metadatos. El primer elemento es el número de fotogramas. {2, 0, 0, 0, 0}, // Levantar el pie derecho, Pies iguales {500, LeftHipCentre (), LeftFootFlat (), RightHipCentre (), RightFootUp (FOOT_DELTA)}, // Pies planos, Pies iguales {500 , LeftHipCentre (), LeftFootFlat (), RightHipCentre (), RightFootFlat ()},}; // Rebotar hacia arriba y hacia abajo anim.int bounceAnim [] [5] ={// Metadatos. El primer elemento es el número de fotogramas. {2, 0, 0, 0, 0}, // Levanta ambos pies, Pies pares {500, LeftHipCentre (), LeftFootDown (300), RightHipCentre (), RightFootDown (300)}, // Pies planos, Pies pares { 500, LeftHipCentre (), LeftFootFlat (), RightHipCentre (), RightFootFlat ()},}; // Shake Legs Animation.int shakeLegsAnim [] [5] ={// Metadatos. El primer elemento es el número de fotogramas. {14, 0, 0, 0, 0}, // Inclinar a la izquierda, Pies iguales {300, LeftHipCentre (), LeftFootUp (FOOT_DELTA), RightHipCentre (), RightFootDown (FOOT_DELTA)}, // Inclinar a la izquierda, Cadera derecha hacia adentro { 100, LeftHipCentre (), LeftFootUp (FOOT_DELTA), RightHipIn (HIP_DELTA), RightFootDown (FOOT_DELTA)}, // Inclinar a la izquierda, Pies pares {100, LeftHipCentre (), LeftFootUp (FOOT_DELTA), RightHipCentre} , // Inclinar a la izquierda, cadera derecha hacia afuera {100, LeftHipCentre (), LeftFootUp (FOOT_DELTA), RightHipOut (HIP_DELTA), RightFootDown (FOOT_DELTA)}, // Inclinar a la izquierda, pies uniformes {100, LeftHipCentre (), LeftFootUp (FOOT_DELTA) , RightHipCentre (), RightFootDown (FOOT_DELTA)}, // Inclina hacia la izquierda, la cadera derecha en {100, LeftHipCentre (), LeftFootUp (FOOT_DELTA), RightHipIn (HIP_DELTA), RightFootDown (FOOT_DELTA)}, // Inclina uniformemente a la izquierda, Pies 100, LeftHipCentre (), LeftFootUp (FOOT_DELTA), RightHipCentre (), RightFootDown (FOOT_DELTA)}, // Pies planos, Pies pares {300, LeftHipCentre (), LeftFootFlat (), RightHipCentre (), RightFootFlat ()} Plataforma de inclinación ht, Pies pares {300, LeftHipCentre (), LeftFootDown (FOOT_DELTA), RightHipCentre (), RightFootUp (FOOT_DELTA)}, // Inclinar a la derecha, Cadera izquierda en {100, LeftHipIn (HIP_DELTA), LeftFootDown (FOOT_DELTA), , RightFootUp (FOOT_DELTA)}, // Inclinar a la derecha, Pies uniformes {100, LeftHipCentre (), LeftFootDown (FOOT_DELTA), RightHipCentre (), RightFootUp (FOOT_DELTA)}, // Inclinar a la derecha, Cadera izquierda hacia afuera {100, LeftHipOut (HIP_DELTA ), LeftFootDown (FOOT_DELTA), RightHipCentre (), RightFootUp (FOOT_DELTA)}, // Inclinar a la derecha, Pies pares {100, LeftHipCentre (), LeftFootDown (FOOT_DELTA), RightHipCentre (), RightFootUpTA plano) , Pies pares {300, LeftHipCentre (), LeftFootFlat (), RightHipCentre (), RightFootFlat ()}}; // Agitar la pierna izquierda Animation.int shakeLeftLegAnim [] [5] ={// Metadatos. El primer elemento es el número de fotogramas. {12, 0, 0, 0, 0}, // Inclinar a la derecha, Pies iguales {300, LeftHipCentre (), LeftFootDown (FOOT_DELTA), RightHipCentre (), RightFootUp (FOOT_DELTA)}, // Inclinar a la derecha, Cadera izquierda hacia adentro { 100, LeftHipIn (HIP_DELTA), LeftFootDown (FOOT_DELTA), RightHipCentre (), RightFootUp (FOOT_DELTA)}, // Inclinar a la derecha, Pies iguales {100, LeftHipCentre (), LeftFootDown (FOOT_DELTA), RightTAHipCentre (FOOT_DELTA), RightTAHipCentreDot (FOOT_DELTA) , // Inclinar a la derecha, Cadera izquierda hacia afuera {100, LeftHipOut (HIP_DELTA), LeftFootDown (FOOT_DELTA), RightHipCentre (), RightFootUp (FOOT_DELTA)}, // Inclinar a la derecha, Pies iguales {100, LeftHipCentre (), LeftFootDownTA (FOOT_DELTA) , RightHipCentre (), RightFootUp (FOOT_DELTA)}, // Inclina la cadera derecha, izquierda en {100, LeftHipIn (HIP_DELTA), LeftFootDown (FOOT_DELTA), RightHipCentre (), RightFootUp (FOOT_DELTA)}, // Inclina la derecha, Pies pares { 100, LeftHipCentre (), LeftFootDown (FOOT_DELTA), RightHipCentre (), RightFootUp (FOOT_DELTA)}, // Inclinar hacia la derecha, Cadera izquierda hacia afuera {100, LeftHipOut (HIP_DELTA), LeftFootDown (FOOT_DELTA), Right), RightCentre otUp (FOOT_DELTA)}, // Inclinar a la derecha, Pies iguales {100, LeftHipCentre (), LeftFootDown (FOOT_DELTA), RightHipCentre (), RightFootUp (FOOT_DELTA)}, // Inclinar a la derecha, Cadera izquierda en {100, LeftHipIn (HIP_DELTA) , LeftFootDown (FOOT_DELTA), RightHipCentre (), RightFootUp (FOOT_DELTA)}, // Inclinar a la derecha, Pies pares {100, LeftHipCentre (), LeftFootDown (FOOT_DELTA), RightHipCentre (), RightFootUp (FOOT_DELTA) plano Pies iguales {300, LeftHipCentre (), LeftFootFlat (), RightHipCentre (), RightFootFlat ()}}; // Agitar la pierna derecha Animation.int shakeRightLegAnim [] [5] ={// Metadatos. El primer elemento es el número de fotogramas. {12, 0, 0, 0, 0}, // Inclinar a la izquierda, Pies iguales {300, LeftHipCentre (), LeftFootUp (FOOT_DELTA), RightHipCentre (), RightFootDown (FOOT_DELTA)}, // Inclinar a la izquierda, Cadera derecha hacia adentro { 100, LeftHipCentre (), LeftFootUp (FOOT_DELTA), RightHipIn (HIP_DELTA), RightFootDown (FOOT_DELTA)}, // Inclinar a la izquierda, Pies pares {100, LeftHipCentre (), LeftFootUp (FOOT_DELTA), RightHipCentre} , // Inclinar a la izquierda, cadera derecha hacia afuera {100, LeftHipCentre (), LeftFootUp (FOOT_DELTA), RightHipOut (HIP_DELTA), RightFootDown (FOOT_DELTA)}, // Inclinar a la izquierda, pies uniformes {100, LeftHipCentre (), LeftFootUp (FOOT_DELTA) , RightHipCentre (), RightFootDown (FOOT_DELTA)}, // Inclina hacia la izquierda, la cadera derecha en {100, LeftHipCentre (), LeftFootUp (FOOT_DELTA), RightHipIn (HIP_DELTA), RightFootDown (FOOT_DELTA)}, // Inclina uniformemente a la izquierda, Pies 100, LeftHipCentre (), LeftFootUp (FOOT_DELTA), RightHipCentre (), RightFootDown (FOOT_DELTA)}, // Inclinar a la izquierda, a la derecha de la cadera hacia fuera {100, LeftHipCentre (), LeftFootUp (FOOT_DELTA), RightHipOutown (HIP_DELotTAD) (FOOT_DELTA)}, // Inclinar a la izquierda, Pies iguales {100, LeftHipCentre (), LeftFootUp (FOOT_DELTA), RightHipCentre (), RightFootDown (FOOT_DELTA)}, // Inclinar a la izquierda, cadera derecha en {100, LeftHipCentre (), LeftFootUp (FOOT_DELTA), RightHipIn (HIP_DELTA), RightFootDown (FOOT_DELTA)}, // Inclina a la izquierda, Pies iguales {100, LeftHipCentre (), LeftFootUp (FOOT_DELTA), RightHipCentre (), RightFootDown (FOOT_DELTA), // even {300, LeftHipCentre (), LeftFootFlat (), RightHipCentre (), RightFootFlat ()},}; // ------------------------- -------------------------------------------------- ------- // Datos especiales de animación dinámica para configurar / interpolar posiciones de servo .//---------------------------- -------------------------------------------------- ---- // Estos son 2 datos especiales de animación que usamos para la función SetServos (). Tienen // un solo cuadro. Esos cambiarán los datos en estos datos de animación y los reproducirán para // mover servos.int setServosAnim1 [] [5] ={// Metadatos. El primer elemento es el número de fotogramas. {1, 0, 0, 0, 0}, // Inclinar a la izquierda, Pies pares {0, LeftHipCentre (), LeftFootFlat (), RightHipCentre (), RightFootFlat ()}}; int setServosAnim2 [] [5] ={/ / Metadatos. El primer elemento es el número de fotogramas. {1, 0, 0, 0, 0}, // Inclinar a la izquierda, Pies iguales {0, LeftHipCentre (), LeftFootFlat (), RightHipCentre (), RightFootFlat ()}}; // -------- -------------------------------------------------- ------------------------ // Variables servo // -------------------- -------------------------------------------------- ------------Servo servoLeftHip;Servo servoLeftFoot;Servo servoRightHip;Servo servoRightFoot;//------------------------ -------------------------------------------------- --------// State variables for playing animations.//-------------------------------- -------------------------------------------------- // Milliseconds between animation updates.const int millisBetweenAnimUpdate =20;// Time when we did the last animation update.long timeAtLastAnimUpdate;// Related to currently playing anim.int (*currAnim)[5]; // Current animation we're playing.int (*finishAnim)[5]; // Animation to play when the currAnim finishes or is stopped.long timeAtStartOfFrame; // millis() at last keyframe - frame we're lerping fromint targetFrame; // Frame we are lerping toint animNumLoops; // Number of times to play the animation. -1 means loop forever.char animCompleteStr[3] ="--"; // This is a 2 character string. When the anim is complete, // we print out the status as "<" + animComplereStr + ">".// Related to anim queue. Es decir. Next anim to play.bool animInProgress; // Whether an animation is playingint (*nextAnim)[5]; // This is the next animation to play once the current one is done. // i.e. It's like a queue of size 1! // If curr is non-looping, we play this at the end of the current anim. // If curr is looping, this starts at the end of the current loop, // replacing curr anim. // If nothing is playing, this starts right away. int (*nextFinishAnim)[5]; // This is the finish animation for the queued animation.int nextAnimNumLoops; // Number of times to play the animation. -1 means loop forever.char nextAnimCompleteStr[3] ="--"; // This is a 2 character string. When the anim is complete, // we print out the status as "<" + animComplereStr + ">".bool interruptInProgressAnim; // Whether to change anim immediately, interrupting the current one.// Curr servo positionsint currLeftHip;int currLeftFoot;int currRightHip;int currRightFoot;// Servo positions at start of current keyframeint startLeftHip;int startLeftFoot;int startRightHip;int startRightFoot;//-------------------------------------------------------------------------------// Parser Variables//-------------------------------------------------------------------------------// Constant delimiter tag charsconst char START_CHAR ='<';const char END_CHAR ='>';const char SEP_CHAR =',';// Constants and a variable for the parser state.const int PARSER_WAITING =0; // Waiting for '<' to start parsing.const int PARSER_COMMAND =1; // Reading the command string.const int PARSER_PARAM1 =2; // Reading param 1.const int PARSER_PARAM2 =3; // Reading param 2.const int PARSER_PARAM3 =4; // Reading param 3.const int PARSER_PARAM4 =5; // Reading param 3.const int PARSER_PARAM5 =6; // Reading param 3.const int PARSER_EXECUTE =7; // Finished parsing a command, so execute it.// Current parser state.int currParserState =PARSER_WAITING; // String for storing the command. 2 chars for the command and 1 char for '\0'.// We store the command here as we're parsing.char currCmd[3] ="--";// For tracking which letter we are in the command.int currCmdIndex;// Max command length.const int CMD_LENGTH =2;// Current param values. Store them here after we parse them.int currParam1Val;int currParam2Val;int currParam3Val;int currParam4Val;int currParam5Val;// Variable for tracking which digit we're parsing in a param.// We use this to convert the single digits back into a decimal value.int currParamIndex;// Whether the current param is negative.boolean currParamNegative;// Max parameter length. Stop parsing if it exceeds this.const int MAX_PARAM_LENGTH =6;//===============================================================================// Arduino setup() and loop().//===============================================================================void setup() { // Setup the main serial port softwareSerial.begin(SERIAL_SPEED); // Setup the Servos servoLeftHip.attach( SERVO_LEFT_HIP, LEFT_HIP_MIN, LEFT_HIP_MAX); servoLeftFoot.attach( SERVO_LEFT_FOOT, LEFT_FOOT_MIN, LEFT_FOOT_MAX); servoRightHip.attach( SERVO_RIGHT_HIP, RIGHT_HIP_MIN, RIGHT_HIP_MAX); servoRightFoot.attach(SERVO_RIGHT_FOOT, RIGHT_FOOT_MIN, RIGHT_FOOT_MAX); // Set things up for the parser. setup_Parser(); // Set things up for the animation code. setup_Animation();}void loop() { // Update the parser. loop_Parser(); // Update the animation. loop_Animation();}//===============================================================================// Related to the parser//===============================================================================// Sets up the parser stuff. Called in setup(). Should not be called elsewhere.void setup_Parser(){ // Wait for first command. currParserState =PARSER_WAITING; // Print this response to say we've booted and are ready. softwareSerial.println("");}// Loop() for the parser stuff. Called in loop(). Should not be called elsewhere.void loop_Parser(){ //--------------------------------------------------------- // PARSER // // If there is data, parse it and process it. //--------------------------------------------------------- // Read from pin serial port and write it out on USB port. if (softwareSerial.available()> 0) { char c =softwareSerial.read(); // If we're in WAITING state, look for the START_CHAR. if (currParserState ==PARSER_WAITING) { // If it's the START_CHAR, move out of this state... if (c ==START_CHAR) { // Start parsing the command. currParserState =PARSER_COMMAND; // Reset thing ready for parsing currCmdIndex =0; currCmd[0] ='-'; currCmd[1] ='-'; currParam1Val =0; currParam2Val =0; currParam3Val =0; currParam4Val =0; currParam5Val =0; } // Otherwise, stay in this state. } // In the state to look for the command. else if (currParserState ==PARSER_COMMAND) { // Else if it's a separator, parse parameter 1. But make sure it's not // empty, or else it's a parse error. if (c ==SEP_CHAR) { if (currCmdIndex ==CMD_LENGTH) { currParserState =PARSER_PARAM1; currParamIndex =0; currParamNegative =false; } else { currParserState =PARSER_WAITING; } } // Else if it's the end char, there are no parameters, so we're ready to // process. But make sure it's not empty. Otherwise, it's a parse error. else if (c ==END_CHAR) { if (currCmdIndex ==CMD_LENGTH) { currParserState =PARSER_EXECUTE; } else { currParserState =PARSER_WAITING; } } // If we've got too many letters here, we have a parse error, // so abandon and go back to PARSER_WAITING else if ( (currCmdIndex>=CMD_LENGTH) || (c <'A') || (c> 'Z') ) { currParserState =PARSER_WAITING; } // Store the current character. else { currCmd[currCmdIndex] =c; currCmdIndex++; } } // In the state to parse param 1. else if (currParserState ==PARSER_PARAM1) { // Else if it's a separator, parse parameter 1. if (c ==SEP_CHAR) { if (currParamNegative) { currParam1Val =-1 * currParam1Val; } currParserState =PARSER_PARAM2; currParamIndex =0; currParamNegative =false; } // Else if it's the end char, there are no parameters, so we're ready to // process. else if (c ==END_CHAR) { if (currParamNegative) { currParam1Val =-1 * currParam1Val; } currParserState =PARSER_EXECUTE; } // Check for negative at the start. else if ( (currParamIndex ==0) &&(c =='-') ) { currParamNegative =true; currParamIndex++; } // If it's too long, or the character is not a digit, then it's // a parse error, so abandon and go back to PARSER_WAITING. else if ( (currParamIndex>=MAX_PARAM_LENGTH) || (c <'0') || (c> '9') ) { currParserState =PARSER_WAITING; } // It's a valid character, so process it. else { // Shift existing value across and add new digit at the bottom. int currDigitVal =c - '0'; currParam1Val =(currParam1Val * 10) + currDigitVal; currParamIndex++; } } // In the state to parse param 2. else if (currParserState ==PARSER_PARAM2) { // Else if it's a separator, parse parameter 2. if (c ==SEP_CHAR) { if (currParamNegative) { currParam2Val =-1 * currParam2Val; } currParserState =PARSER_PARAM3; currParamIndex =0; currParamNegative =false; } // Else if it's the end char, there are no parameters, so we're ready to // process. else if (c ==END_CHAR) { if (currParamNegative) { currParam2Val =-1 * currParam2Val; } currParserState =PARSER_EXECUTE; } // Check for negative at the start. else if ( (currParamIndex ==0) &&(c =='-') ) { currParamNegative =true; currParamIndex++; } // If it's too long, or the character is not a digit, then it's // a parse error, so abandon and go back to PARSER_WAITING. else if ( (currParamIndex>=MAX_PARAM_LENGTH) || (c <'0') || (c> '9') ) { currParserState =PARSER_WAITING; } // It's a valid character, so process it. else { // Shift existing value across and add new digit at the bottom. int currDigitVal =c - '0'; currParam2Val =(currParam2Val * 10) + currDigitVal; currParamIndex++; } } // In the state to parse param 3. else if (currParserState ==PARSER_PARAM3) { // Else if it's a separator, parse parameter 2. if (c ==SEP_CHAR) { if (currParamNegative) { currParam3Val =-1 * currParam3Val; } currParserState =PARSER_PARAM4; currParamIndex =0; currParamNegative =false; } // Else if it's the end char, there are no parameters, so we're ready to // process. else if (c ==END_CHAR) { if (currParamNegative) { currParam3Val =-1 * currParam3Val; } currParserState =PARSER_EXECUTE; } // Check for negative at the start. else if ( (currParamIndex ==0) &&(c =='-') ) { currParamNegative =true; currParamIndex++; } // If it's too long, or the character is not a digit, then it's // a parse error, so abandon and go back to PARSER_WAITING. else if ( (currParamIndex>=MAX_PARAM_LENGTH) || (c <'0') || (c> '9') ) { currParserState =PARSER_WAITING; } // It's a valid character, so process it. else { // Shift existing value across and add new digit at the bottom. int currDigitVal =c - '0'; currParam3Val =(currParam3Val * 10) + currDigitVal; currParamIndex++; } } // In the state to parse param 4. else if (currParserState ==PARSER_PARAM4) { // Else if it's a separator, parse parameter 2. if (c ==SEP_CHAR) { if (currParamNegative) { currParam4Val =-1 * currParam4Val; } currParserState =PARSER_PARAM5; currParamIndex =0; currParamNegative =false; } // Else if it's the end char, there are no parameters, so we're ready to // process. else if (c ==END_CHAR) { if (currParamNegative) { currParam4Val =-1 * currParam4Val; } currParserState =PARSER_EXECUTE; } // Check for negative at the start. else if ( (currParamIndex ==0) &&(c =='-') ) { currParamNegative =true; currParamIndex++; } // If it's too long, or the character is not a digit, then it's // a parse error, so abandon and go back to PARSER_WAITING. else if ( (currParamIndex>=MAX_PARAM_LENGTH) || (c <'0') || (c> '9') ) { currParserState =PARSER_WAITING; } // It's a valid character, so process it. else { // Shift existing value across and add new digit at the bottom. int currDigitVal =c - '0'; currParam4Val =(currParam4Val * 10) + currDigitVal; currParamIndex++; } } // In the state to parse param 5. else if (currParserState ==PARSER_PARAM5) { // If it's the end char, there are no parameters, so we're ready to // process. if (c ==END_CHAR) { if (currParamNegative) { currParam5Val =-1 * currParam5Val; } currParserState =PARSER_EXECUTE; } // Check for negative at the start. else if ( (currParamIndex ==0) &&(c =='-') ) { currParamNegative =true; currParamIndex++; } // If it's too long, or the character is not a digit, then it's // a parse error, so abandon and go back to PARSER_WAITING. else if ( (currParamIndex>=MAX_PARAM_LENGTH) || (c <'0') || (c> '9') ) { currParserState =PARSER_WAITING; } // It's a valid character, so process it. else { // Shift existing value across and add new digit at the bottom. int currDigitVal =c - '0'; currParam5Val =(currParam5Val * 10) + currDigitVal; currParamIndex++; } } //--------------------------------------------------------- // PARSER CODE HANDLER (Still part of Parser, but section that // processes completed commands) // // If the most recently read char completes a command, // then process the command, and clear the state to // go back to looking for a new command. // // The parsed items are stored in:// currCmd, currParam1Val, currParam2Val, currParam3Val, // currParam4Val, currParam5Val //--------------------------------------------------------- if (currParserState ==PARSER_EXECUTE) { // Ready/OK Check: if ((currCmd[0] =='O') &&(currCmd[1] =='K')) { softwareSerial.println(""); } // Set Servo: // time - time to tween to specified angles // leftHip - microsecs from centre. -ve is hip in, +ve is hip out // leftFoot - microsecs from flat. -ve is foot down, +ve is foot up // rightHip - microsecs from centre. -ve is hip in, +ve is hip out // rightFoot - microsecs from flat. -ve is foot down, +ve is foot up else if ((currCmd[0] =='S') &&(currCmd[1] =='V')) { int tweenTime =currParam1Val; if (currParam1Val <0) { tweenTime =0; } SetServos(tweenTime, currParam2Val, currParam3Val, currParam4Val, currParam5Val, "SV"); } // Stop/Reset:, Stops current anim. Also can be used to put robot into reset position. else if ((currCmd[0] =='S') &&(currCmd[1] =='T')) { StopAnim("ST"); } // Stop Immediate: else if ((currCmd[0] =='S') &&(currCmd[1] =='I')) { StopAnimImmediate("SI"); } // Forward:, -1 means continuous, 0 or no param is the same as 1 time. else if ((currCmd[0] =='F') &&(currCmd[1] =='W')) { int numTimes =currParam1Val; if (currParam1Val <0) { numTimes =-1; } PlayAnimNumTimes(walkForwardAnim, walkEndAnim, numTimes, "FW"); } // Backward:, -1 means continuous, 0 or no param is the same as 1 time. else if ((currCmd[0] =='B') &&(currCmd[1] =='W')) { int numTimes =currParam1Val; if (currParam1Val <0) { numTimes =-1; } PlayAnimNumTimes(walkBackwardAnim, walkEndAnim, numTimes, "BW"); } // Turn Left:, -1 means continuous, 0 or no param is the same as 1 time. else if ((currCmd[0] =='L') &&(currCmd[1] =='T')) { int numTimes =currParam1Val; if (currParam1Val <0) { numTimes =-1; } PlayAnimNumTimes(turnLeftAnim, NULL, numTimes, "LT"); } // Turn Right:, -1 means continuous, 0 or no param is the same as 1 time. else if ((currCmd[0] =='R') &&(currCmd[1] =='T')) { int numTimes =currParam1Val; if (currParam1Val <0) { numTimes =-1; } PlayAnimNumTimes(turnRightAnim, NULL, numTimes, "RT"); } // Shake Head:, -1 means continuous, 0 or no param is the same as 1 time. else if ((currCmd[0] =='S') &&(currCmd[1] =='X')) { int numTimes =currParam1Val; if (currParam1Val <0) { numTimes =-1; } PlayAnimNumTimes(shakeHeadAnim, NULL, numTimes, "SX"); } // Bounce:, -1 means continuous, 0 or no param is the same as 1 time. else if ((currCmd[0] =='B') &&(currCmd[1] =='X')) { int numTimes =currParam1Val; if (currParam1Val <0) { numTimes =-1; } PlayAnimNumTimes(bounceAnim, NULL, numTimes, "BX"); } // Wobble:, -1 means continuous, 0 or no param is the same as 1 time. else if ((currCmd[0] =='W') &&(currCmd[1] =='X')) { int numTimes =currParam1Val; if (currParam1Val <0) { numTimes =-1; } PlayAnimNumTimes(wobbleAnim, NULL, numTimes, "WX"); } // Wobble Left:, -1 means continuous, 0 or no param is the same as 1 time. else if ((currCmd[0] =='W') &&(currCmd[1] =='Y')) { int numTimes =currParam1Val; if (currParam1Val <0) { numTimes =-1; } PlayAnimNumTimes(wobbleLeftAnim, NULL, numTimes, "WY"); } // Wobble Right:, -1 means continuous, 0 or no param is the same as 1 time. else if ((currCmd[0] =='W') &&(currCmd[1] =='Z')) { int numTimes =currParam1Val; if (currParam1Val <0) { numTimes =-1; } PlayAnimNumTimes(wobbleRightAnim, NULL, numTimes, "WZ"); } // Tap Feet:, -1 means continuous, 0 or no param is the same as 1 time. else if ((currCmd[0] =='T') &&(currCmd[1] =='X')) { int numTimes =currParam1Val; if (currParam1Val <0) { numTimes =-1; } PlayAnimNumTimes(tapFeetAnim, NULL, numTimes, "TX"); } // Tap Left Foot:, -1 means continuous, 0 or no param is the same as 1 time. else if ((currCmd[0] =='T') &&(currCmd[1] =='Y')) { int numTimes =currParam1Val; if (currParam1Val <0) { numTimes =-1; } PlayAnimNumTimes(tapLeftFootAnim, NULL, numTimes, "TY"); } // Tap Right Foot:, -1 means continuous, 0 or no param is the same as 1 time. else if ((currCmd[0] =='T') &&(currCmd[1] =='Z')) { int numTimes =currParam1Val; if (currParam1Val <0) { numTimes =-1; } PlayAnimNumTimes(tapRightFootAnim, NULL, numTimes, "TZ"); } // Shake Legs:, -1 means continuous, 0 or no param is the same as 1 time. else if ((currCmd[0] =='L') &&(currCmd[1] =='X')) { int numTimes =currParam1Val; if (currParam1Val <0) { numTimes =-1; } PlayAnimNumTimes(shakeLegsAnim, NULL, numTimes, "LX"); } // Shake Left Leg:, -1 means continuous, 0 or no param is the same as 1 time. else if ((currCmd[0] =='L') &&(currCmd[1] =='Y')) { int numTimes =currParam1Val; if (currParam1Val <0) { numTimes =-1; } PlayAnimNumTimes(shakeLeftLegAnim, NULL, numTimes, "LY"); } // Shake Right Leg:, -1 means continuous, 0 or no param is the same as 1 time. else if ((currCmd[0] =='L') &&(currCmd[1] =='Z')) { int numTimes =currParam1Val; if (currParam1Val <0) { numTimes =-1; } PlayAnimNumTimes(shakeRightLegAnim, NULL, numTimes, "LZ"); } //-------------------------------------------------- // Clear the state and wait for the next command! // This must be done! //-------------------------------------------------- currParserState =PARSER_WAITING; } }}//===============================================================================// Related to playing servo animations.//===============================================================================// Call this to play the given animation once. Pass in NULL if there is no finishAnim.void PlayAnim(int animToPlay[][5], int finishAnim[][5], const char *completeStr){ // Put this in the queue. PlayAnimNumTimes(animToPlay, finishAnim, 1, completeStr);}// Call this to loop the given animation. Pass in NULL if there is no finishAnim.void LoopAnim(int animToPlay[][5], int finishAnim[][5], const char *completeStr){ // Put this in the queue. PlayAnimNumTimes(animToPlay, finishAnim, -1, completeStr);}// Call this to play the given animation the specified number of times. // -1 number of times will make it loop forever.// Pass in NULL if there is no finishAnim.void PlayAnimNumTimes(int animToPlay[][5], int finishAnim[][5], int numTimes, const char *completeStr){ // Put this in the queue. nextAnim =animToPlay; nextFinishAnim =finishAnim; nextAnimNumLoops =numTimes; // Save the completeStr if (completeStr ==NULL) { nextAnimCompleteStr[0] ='-'; nextAnimCompleteStr[1] ='-'; } else { nextAnimCompleteStr[0] =completeStr[0]; nextAnimCompleteStr[1] =completeStr[1]; }}// Stop after the current animation.void StopAnim(const char *completeStr){ // Put this in the queue. PlayAnimNumTimes(standStraightAnim, NULL, 1, completeStr);}// Stop immediately and lerp robot to zero position, interrupting // any animation that is in progress.void StopAnimImmediate(const char *completeStr){ // Put this in the queue. interruptInProgressAnim =true; PlayAnimNumTimes(standStraightAnim, NULL, 1, completeStr);}// Moves servos to the specified positions. Time 0 will make it immediate. Otherwise,// it'll tween it over a specified time.// For positions, 0 means centered.// For hips, -ve is hip left, +ve is hip right// For feet, -ve is foot down, +ve is foot upvoid SetServos(int tweenTime, int leftHip, int leftFoot, int rightHip, int rightFoot, const char* completeStr){ // Save the completeStr if (completeStr ==NULL) { nextAnimCompleteStr[0] ='-'; nextAnimCompleteStr[1] ='-'; } else { nextAnimCompleteStr[0] =completeStr[0]; nextAnimCompleteStr[1] =completeStr[1]; } // Decide which tween data we use. We don't want to over-write the one that is // in progress. We have and reuse these to keep memory allocation fixed. int (*tweenServoData)[5]; if (currAnim !=setServosAnim1) { tweenServoData =setServosAnim1; } else { tweenServoData =setServosAnim2; } // Set the tween information into the animation data. tweenServoData[1][TWEEN_TIME_VALUE] =tweenTime; tweenServoData[1][LEFT_HIP_VALUE] =LeftHipIn(leftHip); tweenServoData[1][LEFT_FOOT_VALUE] =LeftFootUp(leftFoot); tweenServoData[1][RIGHT_HIP_VALUE] =RightHipIn(rightHip); tweenServoData[1][RIGHT_FOOT_VALUE] =RightFootUp(rightFoot); // Queue this tween to be played next. PlayAnim(tweenServoData, NULL, completeStr);}// Set up variables for animation. This is called in setup(). Should be not called by anywhere else.void setup_Animation(){ // Set the servos to the feet flat, feet even position. currLeftHip =LEFT_HIP_CENTRE; currLeftFoot =LEFT_FOOT_CENTRE; currRightHip =RIGHT_HIP_CENTRE; currRightFoot =RIGHT_FOOT_CENTRE; UpdateServos(); // Set the "start" positions to the current ones. So, when // we pay the next anim, we will tween from the current positions. startLeftHip =currLeftHip; startLeftFoot =currLeftFoot; startRightHip =currRightHip; startRightFoot =currRightFoot; // No animation is playing yet, and nothing in the queue yet. timeAtLastAnimUpdate =millis(); animInProgress =false; interruptInProgressAnim =false; currAnim =NULL; finishAnim =NULL; nextAnim =NULL; nextFinishAnim =NULL;}// Loop function for processing animation. This is called in every loop(). Should be be called by anywhere else.//// NOTE:The way looping animations work is that they basically add themselves back to the queue// when a cycle is done, and if there's nothing already queued up! This way, looping animations// work in a similar way to single-play animations, and fits into the queueing system.void loop_Animation(){ // Get the time at the start of this frame. long currTime =millis(); //-------------------------------------------------------------------------------------- // Decide if we want to perform the animation update. We don't execute this every frame. //-------------------------------------------------------------------------------------- if (timeAtLastAnimUpdate + millisBetweenAnimUpdate> currTime) { // Not yet time to do an anim update, so jump out. regreso; } else { // We reset the timer, and then proceed below to handle the current anim update. timeAtLastAnimUpdate =currTime; } //-------------------------------------------------------------------------------------- // Decide if we need to setup and start a new animation. We do if there's no anim // playing or we've been asked to interrupt the anim. //-------------------------------------------------------------------------------------- if ( (nextAnim !=NULL) &&(!animInProgress || interruptInProgressAnim) ) { // If this was an interrupt, we also set the "start" servo positions // to the current ones. This way, the animation system will tween from the // current positions. if (interruptInProgressAnim) { // This is the place to notify someone of an animation finishing after getting interrupted // Print the command string we just finished. -1 parameter indicates it was interrupted. softwareSerial.print("<"); softwareSerial.print(animCompleteStr); softwareSerial.println(",-1>"); // Set the "start" positions to the current ones. So, when // we pay the next anim, we will tween from the current positions. startLeftHip =currLeftHip; startLeftFoot =currLeftFoot; startRightHip =currRightHip; startRightFoot =currRightFoot; // We've handled any interrupt request, so clear the flag. interruptInProgressAnim =false; } // Store the animation we are now playing. currAnim =nextAnim; finishAnim =nextFinishAnim; animCompleteStr[0] =nextAnimCompleteStr[0]; animCompleteStr[1] =nextAnimCompleteStr[1]; nextAnim =NULL; // Queue is cleared. nextFinishAnim =NULL; nextAnimCompleteStr[0] ='-'; nextAnimCompleteStr[1] ='-'; // Record the number of times to play the animation. animNumLoops =nextAnimNumLoops; // Treat current time as start of frame for the initial lerp to the first frame. timeAtStartOfFrame =currTime; // Set the frame counters. targetFrame =1; // First frame we are lerping to. Index 0 is metadata, so skip. // An animation is now in progress animInProgress =true; } //-------------------------------------------------------------------------------------- // If we are currently playing an animation, then update the animation state and the // servo positions. //-------------------------------------------------------------------------------------- if (animInProgress) { // Determine if we need to switch to the next frame. int timeInCurrFrame =currTime - timeAtStartOfFrame; if (timeInCurrFrame> currAnim[targetFrame][TWEEN_TIME_VALUE]) { // Set the servo positions to the targetFrame's values. // We only set this if the value is> 0. -ve values means that // the current target keyframe did not alter that servos position. if (currAnim[targetFrame][LEFT_HIP_VALUE]>=0) { currLeftHip =currAnim[targetFrame][LEFT_HIP_VALUE]; } if (currAnim[targetFrame][LEFT_FOOT_VALUE]>=0) { currLeftFoot =currAnim[targetFrame][LEFT_FOOT_VALUE]; } if (currAnim[targetFrame][RIGHT_HIP_VALUE]>=0) { currRightHip =currAnim[targetFrame][RIGHT_HIP_VALUE]; } if (currAnim[targetFrame][RIGHT_FOOT_VALUE]>=0) { currRightFoot =currAnim[targetFrame][RIGHT_FOOT_VALUE]; } UpdateServos(); // These current values are now the start of frame values. startLeftHip =currLeftHip; startLeftFoot =currLeftFoot; startRightHip =currRightHip; startRightFoot =currRightFoot; // Now, we try to move to the next frame. // - If there is a next frame, set that as the new target, and proceed. // - If there's no next frame, but it's looping, we re-add this animation // to the queue. // - If there's no next frame, and this is not looping, we stop animating. // (Remember that targetFrame is 1-based since the first element of the animation // data array is metadata) // Increment targetFrame, and reset time in the current frame. targetFrame++; timeAtStartOfFrame =currTime; // If there is no next frame, we stop this current animation. // If it is looping, then we re-queue the current animation if the queue is empty. if (targetFrame> NumOfFrames(currAnim)) { // Stop the current animation. animInProgress =false; // If we're looping forever, and there's no next anim, re-queue the // animation if the queue is empty. if ((animNumLoops <0) &&(nextAnim ==NULL)) { LoopAnim(currAnim, finishAnim, animCompleteStr); } // If we're looping forever, and there is something in the queue, then // finish the animation and proceed. else if ((animNumLoops <0) &&(nextAnim !=NULL)) { if (finishAnim !=NULL) { // Switch to the finish anim. currAnim =finishAnim; finishAnim =NULL; // Record the number of times to play the animation. animNumLoops =1; // Treat current time as start of frame for the initial lerp to the first frame. timeAtStartOfFrame =currTime; // Set the frame counters. targetFrame =1; // First frame we are lerping to. Index 0 is metadata, so skip. // An animation is now in progress animInProgress =true; } else { // We've stopped, so can notify if needed. // Print the command string we just finished. softwareSerial.print("<"); softwareSerial.print(animCompleteStr); softwareSerial.println(">"); } } // If we're looping a limited number of times, and there's no next anim, // re-queue the animation if the queue is empty. else if ((animNumLoops> 1) &&(nextAnim ==NULL)) { PlayAnimNumTimes(currAnim, finishAnim, animNumLoops-1, animCompleteStr); } // In this case, numAnimLoops is 1, this is the last loop through, so // we're done. We play the finishAnim first if needed. else { // If there is a finish animation, switch to that animation. if (finishAnim !=NULL) { // Switch to the finish anim. currAnim =finishAnim; finishAnim =NULL; // Record the number of times to play the animation. animNumLoops =1; // Treat current time as start of frame for the initial lerp to the first frame. timeAtStartOfFrame =currTime; // Set the frame counters. targetFrame =1; // First frame we are lerping to. Index 0 is metadata, so skip. // An animation is now in progress animInProgress =true; } // Otherwise, we're done! We've played the finishAnim if there was one. else { // Print the command string we just finished. softwareSerial.print("<"); softwareSerial.print(animCompleteStr); softwareSerial.println(">"); } } } } // If we're still animating (i.e. the previous check didn't find that // we've finished the current animation), then proceed. if (animInProgress) { // Set the servos per data in the current frame. We only update the servos that have target // microsecond values> 0. This is to support the feature where we leave a servo at its // existing position if an animation data item is -1. float frameTimeFraction =(currTime - timeAtStartOfFrame) / ((float) currAnim[targetFrame][TWEEN_TIME_VALUE]); if (currAnim[targetFrame][LEFT_HIP_VALUE]>=0) { currLeftHip =startLeftHip + ((currAnim[targetFrame][LEFT_HIP_VALUE] - startLeftHip) * frameTimeFraction); } if (currAnim[targetFrame][LEFT_FOOT_VALUE]>=0) { currLeftFoot =startLeftFoot + ((currAnim[targetFrame][LEFT_FOOT_VALUE] - startLeftFoot) * frameTimeFraction); } if (currAnim[targetFrame][RIGHT_HIP_VALUE]>=0) { currRightHip =startRightHip + ((currAnim[targetFrame][RIGHT_HIP_VALUE] - startRightHip) * frameTimeFraction); } if (currAnim[targetFrame][RIGHT_FOOT_VALUE]>=0) { currRightFoot =startRightFoot + ((currAnim[targetFrame][RIGHT_FOOT_VALUE] - startRightFoot) * frameTimeFraction); } UpdateServos(); } }}// Move all the servo to the positions set in the curr... variables.// In the code, we update those variables and then call this to set the servos.void UpdateServos(){ servoLeftHip.writeMicroseconds(currLeftHip); servoLeftFoot.writeMicroseconds(currLeftFoot); servoRightHip.writeMicroseconds(currRightHip); servoRightFoot.writeMicroseconds(currRightFoot);}// Return the number of frames in the given animation data.// Have this helper function to avoid the "magic number" reference of animData[0][0].int NumOfFrames(int animData[][5]){ return animData[0][0];}

Esquemas


Proceso de manufactura

  1. Robot Raspberry Pi controlado por Bluetooth
  2. Visualizador de música DIY LUMAZOID Arduino
  3. Dados digitales Arduino
  4. Juego de ruleta DIY 37 LED
  5. Voltímetro de bricolaje con Arduino y un teléfono inteligente
  6. Robot controlado por voz
  7. Robot de piano controlado por Arduino:PiBot
  8. NeoMatrix Arduino Pong
  9. Brazo de robot Arduino DIY:controlado por gestos con las manos
  10. Robot de 4 ruedas hecho con Arduino controlado usando Dabble
  11. Coche robot controlado por pernos