HeadTracker GNU

Tema en 'R/C ELECTRÓNICA' iniciado por merss, 23 Sep 2009.

  1. merss Miembro Activo

    merss
    Registrado:
    26 Dic 2006
    Mensajes:
    1.317
    Me Gusta recibidos:
    0
    Este es un proyecto que estado mirando para desarrollarlo y después compartirlo con el foro. Pero me estoy dando cuenta de que no dispongo del tiempo suficiente para llevarlo a cabo. Se trata de realizar un HeadTraker de código abierto con el archiconocido Arduino y un Wii Nunchuck. Todo esto porque vi un post en DIYDrones en el que hacian precisamente esto mismo, pero con la salvedad de que el Ardu se conecta directamente a los servos. En nuestro caso seria que el Ardu leyese un Serial-PPM e inyectase los pulsos del Nunchuck en el tren. De esta forma conseguiriamos un Head tracker funcional, barato y que encima podríamos adaptar a la radio que quisiésemos sin depender de si fuentes externas hacen el trabajo que cobran o no.

    Una ventaja del proyecto es que el código de adquisición de datos del Nunchuck ya esta hecho, lo único que habría que hacer es una parte del código que leyese un tren de pulsos desde la emisora y generar otro con dos canales cambiados, pertenecientes al Pan y al Tilt.

    El codigo es el siguiente:

    Insertar CODE, HTML o PHP:
    #include <Wire.h>
    #include <string.h>
    #include <stdio.h>
    
    uint8_t outbuf[6];
    
    int cnt = 0;
    int ledPin = 13;
    
    int servoPin = 7;
    int servoPin2 = 6;
    
    int pulseWidth = 0;
    int pulseWidth2 = 0;
    
    long lastPulse = 0;
    long lastPulse2 = 0;
    
    int z_button = 0;
    int c_button = 0;
    
    int refreshTime = 20;
    
    int minPulse = 1000;
    int minPulse2 = 500;
    
    int dtime=10;
    
    #define pwbuffsize 10
    long pwbuff[pwbuffsize];
    long pwbuffpos = 0;
    long pwbuff2[pwbuffsize];
    long pwbuffpos2 = 0;
    
    void setup()
    {
        beginSerial (19200);
        Wire.begin ();
        nunchuck_init ();
        pinMode(servoPin, OUTPUT);
        pinMode(servoPin2, OUTPUT);
    
        pulseWidth = minPulse;
        pulseWidth2 = minPulse2;
        Serial.print ("Finished setup\n");
    }
    
    void nunchuck_init()
    {
        Wire.beginTransmission (0x52);
        Wire.send (0x40);
        Wire.send (0x00);  
        Wire.endTransmission ();
    }
    
    void send_zero()
    {
        Wire.beginTransmission (0x52);
        Wire.send (0x00);
        Wire.endTransmission ();
    }
    
    int t = 0;
    
    void loop()
    {
        t++;
        long last = millis();
    
        if( t == 1) {
    
            t = 0;
    
            Wire.requestFrom (0x52, 6);
    
            while (Wire.available ()) {
                outbuf[cnt] = nunchuk_decode_byte (Wire.receive ());
                digitalWrite (ledPin, HIGH);
                cnt++;
            }
    
            if (cnt >= 5) {
    
                //            printNunchuckData();
    
                int z_button = 0;
                int c_button = 0;
    
                if ((outbuf[5] >> 0) & 1) 
                    z_button = 1;
                if ((outbuf[5] >> 1) & 1)
                    c_button = 1;
    
                switch (c_button) {
                case 1:
                    switch (z_button) {
                    case 0:
                        break;
                    case 1:
                        muovi();
                        break;
                    }
                    break;
                case 0:
                    switch (z_button) {
                    case 0:
                        delay(10000);
                        break;
                    case 1:
                        delay(3000);
                        break;
                    }
                    break;
                }
            }
    
            cnt = 0;
            send_zero();
    
        } // if(t==)
    
        updateServo();
    
        delay(dtime);
    }
    
    
    void updateServo() {
    
        if (millis() - lastPulse >= refreshTime) {
    
            digitalWrite(servoPin, HIGH);
            delayMicroseconds(pulseWidth);
            digitalWrite(servoPin, LOW);
    
            digitalWrite(servoPin2, HIGH);
            delayMicroseconds(pulseWidth2);
            digitalWrite(servoPin2, LOW);
    
            lastPulse = millis();
        }
    }
    
    int i=0;
    void printNunchuckData()
    {
        int joy_x_axis = outbuf[0];
        int joy_y_axis = outbuf[1];
        int accel_x_axis = outbuf[2]; // * 2 * 2; 
        int accel_y_axis = outbuf[3]; // * 2 * 2;
        int accel_z_axis = outbuf[4]; // * 2 * 2;
    
        int z_button = 0;
        int c_button = 0;
    
        if ((outbuf[5] >> 0) & 1) 
            z_button = 1;
        if ((outbuf[5] >> 1) & 1)
            c_button = 1;
        if ((outbuf[5] >> 2) & 1) 
            accel_x_axis += 2;
        if ((outbuf[5] >> 3) & 1)
            accel_x_axis += 1;
    
        if ((outbuf[5] >> 4) & 1)
            accel_y_axis += 2;
        if ((outbuf[5] >> 5) & 1)
            accel_y_axis += 1;
    
        if ((outbuf[5] >> 6) & 1)
            accel_z_axis += 2;
        if ((outbuf[5] >> 7) & 1)
            accel_z_axis += 1;
    
        Serial.print (i,DEC);
        Serial.print ("\t");
    
        Serial.print ("X: ");
        Serial.print (joy_x_axis, DEC);
        Serial.print ("\t");
    
        Serial.print ("Y: ");
        Serial.print (joy_y_axis, DEC);
        Serial.print ("\t");
    
        Serial.print ("AccX: ");
        Serial.print (accel_x_axis, DEC);
        Serial.print ("\t");
    
        Serial.print ("AccY: ");
        Serial.print (accel_y_axis, DEC);
        Serial.print ("\t");
    
        Serial.print ("AccZ: ");
        Serial.print (accel_z_axis, DEC);
        Serial.print ("\t");
    
        Serial.print (z_button, DEC);
        Serial.print (" ");
        Serial.print (c_button, DEC);
        Serial.print ("\r\n");
        i++;
    }
    
    char nunchuk_decode_byte (char x)
    {
        x = (x ^ 0x17) + 0x17;
        return x;
    }
    
    void muovi (){
        float tilt = (700 - outbuf[3]*2*2);
        float tilt2 = outbuf[2]*2*2;
    
        tilt = (tilt);
        pulseWidth = (tilt * 5) + minPulse;
    
        tilt2 = (tilt2-288);
        pulseWidth2 = (tilt2 * 5) + minPulse2;
    
        pwbuff[pwbuffpos] = pulseWidth;
        pwbuff2[pwbuffpos2] = pulseWidth2;
        
        if( ++pwbuffpos == pwbuffsize ) pwbuffpos = 0;
        if( ++pwbuffpos2 == pwbuffsize ) pwbuffpos2 = 0;
    
    
        pulseWidth=0;
        pulseWidth2=0;
    
        for( int p=0; p<pwbuffsize; p++ ){
            pulseWidth += pwbuff[p];
            pulseWidth2 += pwbuff2[p];
        }
    
        pulseWidth /= pwbuffsize;
        pulseWidth2 /= pwbuffsize;
    
    }
    
    Esta sacado de este link: http://diydrones.com/profiles/blog/show?id=705844:BlogPost:31713&page=3#comments

    Podéis ver el vídeo del funcionamiento:



    No es perfecto, pero parece que centra bastante bien, ademas de tener una precisión más que aceptable.

    La idea seria desmontar el Nunchuck y sacarle la PCB interior, que al parecer es bastante pequeña (todo el envoltorio de plástico no nos interesa para nada) y conectarlo a un Arduino nano, que apenas mide 2x3cm, con lo que quedaría un conjunto bastante compacto.

    Os dejo un link donde se ve desmontado: http://www.elotrolado.net/hilo_fotos-de-nunchuk-por-dentro-56k-no_653028

    Además de todo esto, el precio final seria bastante reducido. Un Arduino nano cuesta al rededor de unos 18€ comprado en Sparkfun, y el Nunchuck cuesta alrededor de 20€ en cualquier tienda de videoconsola, aunque se puede ahorrar un poco más comprando una copia en china: http://www.dealextreme.com/details.dx/sku.24529 Cuesta algo mas de 6€ puesto en casa. Aunque dicen que la placa es de menor calidad, la función que desarrolla es la misma (esto es lo que he leído por Internet, ahora no recuerdo donde). Por tanto el precio del conjunto rondaría los 25€, casi regalado para un Headtracker.

    Algunos links más con información:

    http://todbot.com/blog/2007/10/25/boarduino-wii-nunchuck-servo/

    http://www.windmeadow.com/node/42

    http://diydrones.com/profiles/blogs/705844:BlogPost:39393

    http://diydrones.com/profiles/blogs/pan-and-tilt-camera-with-basic


    Creo que la idea es bastante buena. El problema es que se necesita gente que sepa desarrollar la parte de lectura y escritura de trenes de pulsos.

    Saludos
     
  2. luico Moderator

    luico
    Registrado:
    9 Mar 2007
    Mensajes:
    3.210
    Me Gusta recibidos:
    0
  3. merss Miembro Activo

    merss
    Registrado:
    26 Dic 2006
    Mensajes:
    1.317
    Me Gusta recibidos:
    0
    Pues no sabia ni que lo habías puesto luico...
     
  4. merss Miembro Activo

    merss
    Registrado:
    26 Dic 2006
    Mensajes:
    1.317
    Me Gusta recibidos:
    0
    Bueno... pues nada... parece que nadie se anima. :blink:

    Yo he empezado hacerlo, en el poco tiempo que tengo y con mis pocos conocimientos de programación. Si consigo algo algo (que lo dudo) lo postearé. Aunque voy ha hacerlo para mi emisora y condiciones especiales. Hacerlo estandard para utilizarlo en todas las emisoras esta fuera de mi alcance.

    Saludos
     
  5. cacer Miembro Activo

    cacer
    Registrado:
    3 Ene 2008
    Mensajes:
    1.136
    Me Gusta recibidos:
    0
    Hace ya tiempo, yo diria un año, controle un pequeño robot con el Arduino y el Nunchaku.

    Este mando presenta sus datos en I2C, protocolo bastante sencillo de usar, pero el nunchaku, no es tan preciso como pudier parecer. Al menos, me dio muchos problemas para afinar los movimientos.

    Si saco tiempo os ayudo en esta tarea, pero creo que es mejor usar un ADXL3xx directamente.

    Como tengo las dos cosas y quiero un headtraker, pero no alos presios que se estan vendiendo, buscaremos ese hueco de algun sitio.

    Dejarme prmero que termine mi setup :ansioso:
     
  6. merss Miembro Activo

    merss
    Registrado:
    26 Dic 2006
    Mensajes:
    1.317
    Me Gusta recibidos:
    0
    Cacer, gracias por el ofrecimiento.

    Uniendo varios códigos y librerías que he ido encontrado por la red, y uniéndolas... he conseguido la adquisición de datos, es decir, guardar en un array todos los pulsos de la lectura del sppm y del movimiento del nunchaku.

    El problema que la librería que lee el sppm solo lee los 8 primeros canales, por lo que como utilizo receptores de 10 canales para fpv, pero mi emisora es de 9, he puesto los canales del pan y el tilt en el 9 y el 10, así le gano un canal.

    Lo de utilizar el nunchaku, es porque el código ya esta hecho, (que supongo que del ADXL3xx también habrá algo), y porque es bastante barato.

    Saludos
     
  7. Pumuky Gurú FPV

    Pumuky
    Registrado:
    16 Sep 2008
    Mensajes:
    4.092
    Me Gusta recibidos:
    0
    Bueno alomejor digo una locura pero supongo que cualquier idea puede tener beneficios.


    Si lo mas complicado es acondicionar el tren de pulsos para una emisora de 9, 10 o mas canales, tambien se podria adaptar la salida del arduino a una salida analogica y inyectarla directamente en un par de los potenciometros de las emisoras, osea sustituir 2 potenciometros por 2 salidas del arduino y dejar asi que la emisora siga gestionando su propio tren de pulsos, engañando esos 2 canales que provienen del arduino y no del propio potenciometro.

    Tal vez el mayor problema sea abrir la emisora y ponerle la toma adecuada para conectar, pero llegados a este punto por lo que veo no seria mucho problema para la mayoria de vosotros no?.

    Si dije una solemne estupidez pido disculpas.
     
  8. merss Miembro Activo

    merss
    Registrado:
    26 Dic 2006
    Mensajes:
    1.317
    Me Gusta recibidos:
    0
    Pumuky, eso ya lo había pensado, no es mala idea, pero la verdad es que no me gustaría nada tocar mi emisora. Además quiero hacer una cosa bastante interesante... si me sale... es ganarle un canal extra a la emisora, es decir, que en mi caso emita 10 canales, siendo los dos últimos del pan y el tilt, aunque mi emisora emita 9.
    Estoy dándole vueltas a la adquisición del sppm, porque hay una librería que lo hace, pero entonces no puedo gastar otra (son incompatibles) para emitir el pulso. Al final creo que voy a implementar la adquisición y envío de la trama por mi cuenta... ya os pediré ayuda.

    Ya os iré informando...

    Saludos
     
    #8 merss, 27 Sep 2009
    Última edición: 27 Sep 2009
  9. merss Miembro Activo

    merss
    Registrado:
    26 Dic 2006
    Mensajes:
    1.317
    Me Gusta recibidos:
    0
    He estado mirando de usar el wii motion plus en vez de el nunchuk, la verdad es que es mucho más estable que el nunchuk, pero el problema es que solo nos da la velocidad instantánea del objeto, a priori se puede sacar el espacio recorrido... pero es demasiado complicado para mi nivel... así que de momento descartado.

    Os dejo un vídeo para que veáis el funcionamiento:



    Existe un código para obtener la posición del objeto utilizando conjuntamente el nunchuk y el wii motion plus... ya que con los acelerometros de uno y los giróscopos del otro tenemos una IMU de 6 gdl. De hecho el código tiene hasta los filtros kalman implementados, el problema es que si sumas lo que cuesta todo casi sale más a cuenta comprar un HT hecho...

    A una cosa que le he estado dando vueltas es a usar dos giroscopos con bloqueo de cola. Los hay muy baratos en china. Lo que no se es si seria realmente efectivo... Que os parece?

    Saludos
     
  10. cacer Miembro Activo

    cacer
    Registrado:
    3 Ene 2008
    Mensajes:
    1.136
    Me Gusta recibidos:
    0
    Pumuky,

    Siempre puedes sacar una pletina por detras de la emisora con los dos potenciometros y la electronica del Arduino o es posible que un simple attiny y no tacar el interior de esta.

    Creo que no es ninguna tonteria, yo como soy pobre, solo tengo una emisora de 7 canales y quiero sacarle el pan an til con un joystick o el Head a voluntad.

    No se si me explico.

    Asi tienes los dos canales adicionales de quita y pon jejeje:rolleyes2:
     
  11. cacer Miembro Activo

    cacer
    Registrado:
    3 Ene 2008
    Mensajes:
    1.136
    Me Gusta recibidos:
    0
    He encontrado este codigo simple que lee el tren de pulsos, inicialmente indica Jordi que de la salida de un receptor, pero es lo mismo leerlo de la salida trainer. El link de donde lo he sacado, es este

    He jugado con el y funciona bastante bien. Todo lo mas que he visto, es que inicialmente leia 4 canales y simplemente indicandole el numero que quieres te lo lee sin problemas.

    Mañana intentare hacer la parte de escritura y lectura de un potenciometro, para añadir canales.

    Insertar CODE, HTML o PHP:
    #define channumber 6 //Indica el numero de canales a leer
    int value[channumber];
    
    void setup()
    {
    Serial.begin(57600); //Velocidad de sincronizacion serie
    pinMode(3, INPUT); //Define el PIN 3 como entrada
    }
    
    
    void loop()
    {
    while(pulseIn(3, LOW) < 5000){} //Wait for the beginning of the frame
    for(int x=0; x<=channumber-1; x++)//Loop to store all the channel position
    {
    value[x]=pulseIn(3, LOW);
    }
    for(int x=0; x<=channumber-1; x++)//Loop to print and clear all the channel readings
    {
    Serial.print(value[x]); //Print the value
    Serial.print(" ");
    value[x]=0; //Clear the value afeter is printed
    }
    Serial.println(""); //Start a new line
    
     
  12. merss Miembro Activo

    merss
    Registrado:
    26 Dic 2006
    Mensajes:
    1.317
    Me Gusta recibidos:
    0
    Muchas gracias cacer! esto va a simplificar todo. Voy a rehacer el código para utilizar este sistema.

    Saludos
     
  13. merss Miembro Activo

    merss
    Registrado:
    26 Dic 2006
    Mensajes:
    1.317
    Me Gusta recibidos:
    0
    Bueno... creo que ya tengo acabado el código... :blink: seguro que fallan dos mil cosas, pero bueno... De momento, esta hecho para adquirir una trama sppm y volver a enviar inyectando los pulsos correspondientes.

    Ahora bien, para mi emisora esto no vale, o más bien, para como quiero utilizarlo no vale, ya que mi intención es interceptar la señal entre la emisora y el módulo, pero no había caído que uso sppm (vaya error de novato..), y no estoy dispuesto a cambiar a ppm, ya que me aporta muchísima mas seguridad la primera opción.

    He decidido que para hacer pruebas voy a dejar de utilizar la parte del código que se encarga de adquirir el pulso, y voy a inyectarlo a través del puerto trainer, el problema es que solo se puede utilizar los 4 primeros canales, con lo que quiero eliminar el stick izquierdo, y utilizar esos canales para el pan y el tilt (actualmente lo gasto así), y dejar el timón de dirección al Ardu (mezclado con alerones) y el motor en una deslizadera lateral.

    No descarto el comentario de pumuky. Simular potenciometros... aunque no me gustaría tocar la emisora.

    Voy a pedir los componentes para montarlo todo.
     
  14. Nosepo Miembro Activo

    Nosepo
    Registrado:
    12 Jul 2009
    Mensajes:
    1.621
    Me Gusta recibidos:
    0
    Animo mers, hay que tener ilusion para que salgan las cosas, lastima que no tenga ni idea de como ayudaros.
     

Compartir esta página