En la entrada de hoy vamos a hablar sobre como conectar una IMU de 9 grados de libertad, basada en el BMX055(Link en Aliexpress), a nuestro ESP32 a través de I2C. Para ellos necesitamos:
- Conectar el ESP32 y la IMU entre ellas, y a la alimentación.
- Configurar el i2c del ESP32
- Leer los valores de la IMU.
- Conexión con la IMU
Conexión con la IMU
El BMX055 se puede controlar a través de SPI o I2C , en nuestro caso vamos a usarlo con i2c. Mirando el datasheet del BMX055 (Enlace) nos indica que el protocolo de comunicación se seleciona cambiando el voltaje en el pin ‘PS’ (Protocol Select). Si el voltaje de PS es igual a Vss entonces se selecciona I2C mientras que el SPI se selecciona con el pin PS a tierra.

Conexión IMU para I2C.
El BMX055 se compone de 3 bloques, uno para el magnetómetro, otro para el acelerómetro y el último para el giroscopio. Cada uno de ellos funciona como si estuviesen separados, con una dirección I2C diferente. Como suele ser general, la dirección de cada dispositivo se puede modificar mediante el voltaje de alguno de los pines expuestos. En este caso los pines que se utilizar para cambiar la dirección I2C son: SDO1, SDO2 y CSB3. Nosotros vamos a usar los valores por defecto, que son los que se cargan con los 3 pines a tierra. Con esta configuración, las direcciones que se carga son:
- Acelerómetro: 0x18
- Giroscopio: 0x68
- Magnetómetro: 0x10
- Direcciones I2C del BMX055

Selección de direcciones
Por último, la hoja de datos indica que el CSB1 y CSB2 tienen que quedar al aire cuando se use el i2c como protocolo de comunicaciones. Así que ya solo quedaría conectar los pines propios del i2c a los puertos correspondientes del ESP32. En este caso, hemos elegido los pines D19 (SCL) y D18(SDA) .
Con esto la IMU quedaría alimentada y configurada para empezar a ser leída por el ESP32 a través del i2c.
Leyendo el I2C desde el ESP32
Para leer los datos que genera nuestra IMU necesitamos una manera de leer datos por el I2C, para hacerlo más sencillo, vamos a usar una librería que podemos encontrar en Github. Para usarlo, solo necesitamos inicializar los controladores del I2C que nos vienen ya instanciados. Nosotros vamos a usar el puerto ‘0’, para lo cual vamos a iniciarlo tal que así:
i2c0.begin(GPIO_NUM_19, GPIO_NUM_18, (gpio_pullup_t)0x1, (gpio_pullup_t)0x1, 400000)
Una vez tenemos el I2C configurado solo tenemos que ir leyendo los datos de los diferentes registros que aparecen en el datasheet.
uint8_t temperature = 0; i2c0->readByte(ACCEL_ADDRESS, 0x08, &temperature); float temp = 23 + (temperature *0.5);
El resto de datos, tienen una cantidad de configuraciones infinita. Sin embargo, hace falta una pequeña configuración inicial del acelerómetro y del giroscopio para empezar a leer los datos. Lo primero que hacemos es configurar el filtro del acelerómetro y la resolución del giroscopio.
i2c0->writeByte(ACCEL_ADDRESS, 0x13, 0x0); i2c0->writeByte(ACCEL_ADDRESS, 0x0F, 0x03);
Una vez configurado podemos leer los datos del acelerómetro con la resolución por defecto (0.98 mg/LSB). Con este pequeño código obtenemos el valor del acelerómetro en metros por segundo.
uint8_t accel_x_lsb = 0; uint8_t accel_y_lsb = 0; uint8_t accel_z_lsb = 0; uint8_t accel_x_msb = 0; uint8_t accel_y_msb = 0; uint8_t accel_z_msb = 0; i2c0->readByte(ACCEL_ADDRESS, 0x02, &accel_x_lsb); i2c0->readByte(ACCEL_ADDRESS, 0x04, &accel_y_lsb); i2c0->readByte(ACCEL_ADDRESS, 0x06, &accel_z_lsb); i2c0->readByte(ACCEL_ADDRESS, 0x03, &accel_x_msb); i2c0->readByte(ACCEL_ADDRESS, 0x05, &accel_y_msb); i2c0->readByte(ACCEL_ADDRESS, 0x07, &accel_z_msb); int16_t accel_x_ = (accel_x_msb << 4) | (accel_x_lsb >> 4); int16_t accel_y_ = (accel_y_msb << 4) | (accel_y_lsb >> 4); int16_t accel_z_ = (accel_z_msb << 4) | (accel_z_lsb >> 4); if (accel_x_ > 2047) { accel_x_ -= 4096; } if (accel_y_ > 2047) { accel_y_ -= 4096; } if (accel_z_ > 2047) { accel_z_ -= 4096; } a_x = accel_x_ * 0.98 / 1000.0; a_y = accel_y_ * 0.98 / 1000.0; a_z = accel_z_ * 0.98 / 1000.0;
Para el caso del giroscopio es similar, pero leyendo otros registros del I2C:
uint8_t gyro_x_lsb = 0; uint8_t gyro_y_lsb = 0; uint8_t gyro_z_lsb = 0; uint8_t gyro_x_msb = 0; uint8_t gyro_y_msb = 0; uint8_t gyro_z_msb = 0; i2c0->readByte(GYRO_ADDRESS, 0x02, &gyro_x_lsb); i2c0->readByte(GYRO_ADDRESS, 0x04, &gyro_y_lsb); i2c0->readByte(GYRO_ADDRESS, 0x06, &gyro_z_lsb); i2c0->readByte(GYRO_ADDRESS, 0x03, &gyro_x_msb); i2c0->readByte(GYRO_ADDRESS, 0x05, &gyro_y_msb); i2c0->readByte(GYRO_ADDRESS, 0x07, &gyro_z_msb); int16_t gyro_x_ = (gyro_x_msb << 8) | gyro_x_lsb; int16_t gyro_y_ = (gyro_y_msb << 8) | gyro_y_lsb; int16_t gyro_z_ = (gyro_z_msb << 8) | gyro_z_lsb; float gyro_x = (gyro_x_ * 2000) / 32767 ; // - gyro_x_offset float gyro_y = (gyro_y_ * 2000) / 32767 ; // - gyro_y_offset float gyro_z = (gyro_z_ * 2000) / 32767 ; // - gyro_z_offset w_x = gyro_x * 3.14159265358979323846 / 180.0; w_y = gyro_y * 3.14159265358979323846 / 180.0; w_z = gyro_z * 3.14159265358979323846 / 180.0;
Y con esto ya tenemos todo, tenemos los datos de aceleración y giro en unidades internacionales (m/s y rad/s). El magnetómetro es diferente y es un poco más complicado así que hablaremos de él en otro momento.
Extra: todos estos datos nos sirven para obtener la posición de nuestra IMU en Pitch, Roll y Yaw. Para ello hay que usar algoritmos que nos ayuden en el cálculo. En nuestro caso hemos usado el algoritmo de Madgwick (pdf)
Una vez que tenemos los datos de la IMU, puede ser que no tengamos la orientación de la IMU correcta. Podemos, con unas operaciones matemáticas, sacar la orientación actual de la IMU y recalcular los datos para obtener las aceleraciones X,Y,Z en función de la orientación original. Para ello podéis verlo en el siguiente enlace
0 comentarios