Go Down

Topic: Conectar 3 MPU-6050 a un arduino uno, y leer datos al mismo tiempo (Read 200 times) previous topic - next topic

Hola amigos buenas tardes, espero estén teniendo éxito en sus proyectos!!!
Actualmente me encuentro trabajando con un brazo de 6 GDL y para controlar los 6 servos uso 3 MPU-6050, el problema es que no encuentro la manera de leer los datos de los 3 al mismo tiempo, lo eh echo con 2 utilizando para uno la direccion por defecto 0x68 y para el otro cambiandola a 0x69, pero para el tercero no se como hacerlo U_u , espero puedan ayudarme..

surbyte

bueno si hablas de direcciones es porque estas comunicándote por i2c.
Considera explicar detalladamente el contexto de tu proyecto para que sea fácil la ayuda. No estamos en tu cabeza.
Lo que veo es que la Linea A0 (única disponible) te permite dos niveles 0 y Vlogic por lo tanto tus direcciones parecieran estar limitadas a dos via i2c.

Por otra parte lo que no esta limitado es que lo leas via SPI, según el manual del MPU6050 solo debes usa los pines SPI y un CS por cada MPU. Me parece una alternativa válida.

Bueno, buscando se encuentra: Cito
Quote
Multiple sensors

The pin "AD0" selects between I2C address 0x68 and 0x69. That makes it possible to have two of these sensors in a project. Most breakout boards have a pullup or pulldown resistor to make AD0 default low or high. Connect AD0 to GND or 3.3V for the other I2C address.

When more MPU-6050 sensors are needed in a project, the I2C-bus can be extended with multiplexers. However, someone in the forum mentioned a nice trick:

Trick
Using more than two MPU-6050 sensors can be achieved by connecting each of the AD0 pins to a seperate output of the Arduino. If the number of pins is a problem, then a shift register or a port expander can be used.

The output of a 5V Arduino can not be used. In that case a voltage divider or level shifter for 3.3 volts on each of the outputs is needed. The 5V output pins can also be converted in 3.3V open collector outputs by using transistors or an open-collector driver. Pullup resistors to 3.3V should be added for a high level of 3.3V.
Suppose all AD0 lines are default high (3.3V), so every MPU-6050 is I2C address 0x69. That I2C address is however never used ! The Arduino makes one of the AD0 lines low, and uses that sensor at I2C address 0x68. After that is finished, the Arduino selects another AD0 line, and can use that sensor.
So every sensor is used at I2C address 0x68 (one by one) and 0x69 is never used.

This should make it possible to have many MPU-6050 sensors in a project. Even more than 10 sensor should be possible.
Note that requesting data from many MPU-6050 sensors is slow, because the I2C-bus is slow. A sensor with SPI interface is faster.

At this moment (15 July 2014) it is not known if this trick works for the MPU-6050.
Ahi tienes el modo de resolverlo.


Asi es estoy usando I2C, ya que el modelo de MPU-6050 con el que cuento solo me permite usar este protocolo, hay otro que permite usar ambos I2C y SPI pero no es el que tengo, y por ello me estoy viendo un tanto limitado con las direcciones.

surbyte

Entonces comienza indiando que modelo exacto de MPU esas usando, coloca la hoja de datos de TU MODELO y podremos sugerirte algo.
Resumiendo: A0 te cambia las direcciónes posibles 0x68 o 0x69.
Entonces usas pines del arduino como Chip select conectados a A0 de cada MPU, o sea 3 pines.
En condiciones de uso, bajas el pin a LOW con lo cual trabajas en la dirección 0x68 y los demas siempre en 0x69 (por lo que no van a responder). Al dejar de usarlo levantas el nivel en ese pin y seleccionas el que desees.
Un truco simple que te permite leer los 3.

EL acelerometro que estoy usando es un GY-521 MPU-6050 http://www.invensense.com/mems/gyro/documents/PS-MPU-6000A-00v3.4.pdf
La cuestión de leer los datos al mismo tiempo es que el brazo robot tiene que responder en tiempo real a los movimientos que realize el mio, este es el código que eh usado para leer los datos de 2 MPU, es algo sencillo.

Code: [Select]
#include <Wire.h>

//Direccion I2C de la IMU
#define MPU 0x68
#define MPU2 0x69
//Ratios de conversion
#define A_R 16384.0
#define G_R 131.0

//Conversion de radianes a grados 180/PI
#define RAD_A_DEG = 57.295779

//MPU-6050 da los valores en enteros de 16 bits
//Valores sin refinar
int16_t AcX, AcY, AcZ, GyX, GyY, GyZ;
int16_t AcX2, AcY2, AcZ2, GyX2, GyY2, GyZ2;
//Angulos
float Acc[2];
float Gy[2];
float Angle[2];
float Acc2[2];
float Gy2[2];
float Angle2[2];

void setup()
{
Wire.begin();
Wire.beginTransmission(MPU);
Wire.write(0x6B);
Wire.write(0);
Wire.endTransmission(true);
Serial.begin(9600);Wire.begin();

Wire.begin();
Wire.beginTransmission(MPU2);
Wire.write(0x6B);
Wire.write(0);
Wire.endTransmission(true);
Serial.begin(9600);

}
void loop()
{
  //Leer los valores del Acelerometro de la IMU
  Wire.beginTransmission(MPU);
  Wire.write(0x3B); //Pedir el registro 0x3B - corresponde al AcX
  Wire.endTransmission(false);
  Wire.requestFrom(MPU,6,true); //A partir del 0x3B, se piden 6 registros
  AcX=Wire.read()<<8|Wire.read(); //Cada valor ocupa 2 registros
  AcY=Wire.read()<<8|Wire.read();
  AcZ=Wire.read()<<8|Wire.read();
  
  //Se calculan los angulos Y, X respectivamente. (primer IMU)
  Acc[1] = atan(-1*(AcX/A_R)/sqrt(pow((AcY/A_R),2) + pow((AcZ/A_R),2)))*RAD_TO_DEG;
  Acc[0] = atan((AcY/A_R)/sqrt(pow((AcX/A_R),2) + pow((AcZ/A_R),2)))*RAD_TO_DEG;


  Wire.beginTransmission(MPU2);
  Wire.write(0x3B); //Pedir el registro 0x3B - corresponde al AcX
  Wire.endTransmission(false);
  Wire.requestFrom(MPU2,6,true); //A partir del 0x3B, se piden 6 registros
  AcX2=Wire.read()<<8|Wire.read(); //Cada valor ocupa 2 registros
  AcY2=Wire.read()<<8|Wire.read();
  AcZ2=Wire.read()<<8|Wire.read();
  
  //Se calculan los angulos Y, X respectivamente.(segundo IMU)
  Acc2[1] = atan(-1*(AcX2/A_R)/sqrt(pow((AcY2/A_R),2) + pow((AcZ2/A_R),2)))*RAD_TO_DEG;
  Acc2[0] = atan((AcY2/A_R)/sqrt(pow((AcX2/A_R),2) + pow((AcZ2/A_R),2)))*RAD_TO_DEG;

  //Leer los valores del Giroscopio
  Wire.beginTransmission(MPU);
  Wire.write(0x43);
  Wire.endTransmission(false);
  Wire.requestFrom(MPU,4,true); //A diferencia del Acelerometro, solo se piden 4 registros
  GyX=Wire.read()<<8|Wire.read();
  GyY=Wire.read()<<8|Wire.read();
  
  //Calculo del angulo del Giroscopio
  Gy[0] = GyX/G_R;
  Gy[1] = GyY/G_R;

  //Aplicar el Filtro Complementario
  Angle[0] = 0.98 *(Angle[0]+Gy[0]*0.010) + 0.02*Acc[0];
  Angle[1] = 0.98 *(Angle[1]+Gy[1]*0.010) + 0.02*Acc[1];

  //Mostrar los valores por consola
  Serial.print("Angle X: "); Serial.print(Angle[0]); Serial.print("\n");
  Serial.print("Angle Y: "); Serial.print(Angle[1]); Serial.print("\n------------\n");

  delay(10); //Nuestra dt sera, pues, 0.010, que es el intervalo de tiempo en cada medida
  
  Wire.beginTransmission(MPU2);
  Wire.write(0x43);
  Wire.endTransmission(false);
  Wire.requestFrom(MPU2,4,true); //A diferencia del Acelerometro, solo se piden 4 registros
  GyX2=Wire.read()<<8|Wire.read();
  GyY2=Wire.read()<<8|Wire.read();



  //Calculo del angulo del Giroscopio
  Gy2[0] = GyX2/G_R;
  Gy2[1] = GyY2/G_R;

  //Aplicar el Filtro Complementario
  Angle2[0] = 0.98 *(Angle2[0]+Gy2[0]*0.010) + 0.02*Acc2[0];
  Angle2[1] = 0.98 *(Angle2[1]+Gy2[1]*0.010) + 0.02*Acc2[1];

  //Mostrar los valores por consola
  Serial.print("Angle X2: "); Serial.print(Angle2[0]); Serial.print("\n");
  Serial.print("Angle Y2: "); Serial.print(Angle2[1]); Serial.print("\n------------\n");

  delay(10); //Nuestra dt sera, pues, 0.010, que es el intervalo de tiempo en cada medida
}

surbyte

Sigues sin entenderme? Por lo visto si.

Defino constantes

Code: [Select]
const byte MPUpinA0[3] = {2,3,4};
// resto de definición de variables

void setup()
  // resto de las configuraciones del setup

 for (int i=2; i<5; i++) {
    pinMode(MPUpinA0[i], OUTPUT);
 }

 for (int i=2; i<5; i++) {
    seleccionMPU(i);
    Wire.begin();
    Wire.beginTransmission(MPU);
    Wire.write(0x6B);
    Wire.write(0);
    Wire.endTransmission(true);
}
  // resto de las configuraciones del setup
}
en el loop()


void seleccionoMPU(int b){

 for (int i=2; i<5; i++) {
    if (b != i)
       digitalWrite(MPUpinA0[i], HIGH); // deshabilito MPU correspondiente o sea lo paso a 0x69
    else
       digitalWrite(MPUpinA0[i], LOW);  // habilito MPU(b)
}


Es solo una idea, tal vez debas trabajarla un poco.
Luego para leer un MPU pones seleccionoMPU(X)

Acabo de darme cuenta que seleccionoMPU lo hice pensando en los pines que elegí, asi que tal vez sería mas cómodo adaptarlo para que cuando pongas digamos seleccionoMPU(1) ese 1 se corresponda con el primer elemento o pin que elige el MPU.
Espero ser claro.

Quote
La cuestión de leer los datos al mismo tiempo es que el brazo robot tiene que responder en tiempo real a los movimientos que realize el mio
No existe respueta en RT. Leeras datos de cada uno a la velocidad que permita el i2c y en forma secuencial. Eso demorará mseg, pero debiera ser suficiente para tu sistema.

Gracias por la idea me quedo claro la forma en que trabajara el código, trabajare en ello. Y si tienes razón no es posible una respuesta en RT, así que dependeré de la velocidad que permita I2C.

surbyte

Lo que te sugerí hay que trabajarlo, no se si funcionará. Supongo que si los inicializas a cada uno y luego vas haibiltano el que esta en el loop y deshabilitando los demás podras con la tarea.
En breve tendré que hacer algo parecido asi que me sirvió el tema.

Go Up
 


Please enter a valid email to subscribe

Confirm your email address

We need to confirm your email address.
To complete the subscription, please click the link in the email we just sent you.

Thank you for subscribing!

Arduino
via Egeo 16
Torino, 10131
Italy