Cuaterniones sin esfuerzo: Cómo usar el coprocesador de IA del LILYGO T-BHI260AP para controlar objetos 3D.

En esta práctica aprenderás a leer la orientación en el espacio de un objeto usando el sensor IMU con inteligencia artificial T-BHI260AP de LILYGO, conectado por I2C a una tarjeta STM32 Black Pill. En lugar de procesar manualmente los datos del acelerómetro y el giroscopio, dejarás que el propio sensor entregue la orientación ya calculada en forma de cuaterniones. Para comprobar que todo funciona, llevarás esos datos a la computadora y moverás un cubo 3D en tiempo real que imita exactamente cómo giras el sensor en tu mano.

Introducción

Cada vez más dispositivos —relojes inteligentes, drones, robots y wearables— necesitan saber cómo se mueven en el espacio. El reto es lograrlo sin agotar la batería ni saturar al microcontrolador con miles de cálculos por segundo. El T-BHI260AP resuelve esto procesando el movimiento dentro de sí mismo. A continuación se explican los conceptos clave.

¿Qué es el T-BHI260AP?

Es una pequeña placa de conexión (breakout) de LILYGO construida alrededor del chip Bosch BHI260AP: un IMU de 6 ejes (acelerómetro y giroscopio) que integra, en un solo encapsulado, un microcontrolador de 32 bits y algoritmos de autoaprendizaje por IA. Expone los pines de comunicación para conectarlo directamente al microcontrolador que prefieras (Arduino, ESP32 o STM32).

¿Qué es la “IA en el borde” (edge AI)?

Significa que el procesamiento de los datos ocurre dentro del propio sensor, no en la nube ni en tu microcontrolador principal. El BHI260AP actúa como coprocesador: recibe los datos crudos del IMU, los procesa (fusión de sensores, orientación 3D, conteo de pasos, detección de gestos) y entrega resultados ya interpretados. Así se libera al microcontrolador y se reduce drásticamente el consumo de energía.

Materiales

Diagrama de Conexión

Con todos tus materiales listos, te dejo el siguiente diagrama donde unirás todo para darle vida a tu T-BHI260AP :

Requerimientos Previos

Antes de programar la tarjeta necesitas preparar tu computadora con tres cosas: el controlador (driver) para que la STM32 sea reconocida por USB, el soporte de placas STM32 dentro del Arduino IDE y la librería del sensor. Sigue estos pasos en orden.

Instala el driver de la STM32 (STM32CubeProgrammer)

El método de carga que usaremos es por DFU (USB), y el controlador necesario para que Windows reconozca la tarjeta en este modo viene incluido con STM32CubeProgrammer. Descárgalo desde la página oficial de STMicroelectronics e instálalo con las opciones por defecto. Con esto, tu sistema operativo podrá comunicarse con la Black Pill sin instalar drivers por separado.

Agrega el soporte de placas STM32 al Arduino IDE

Abre el Arduino IDE y entra a Archivo › Preferencias (File › Preferences). En el campo “Gestor de URLs Adicionales de Tarjetas” pega la siguiente dirección y acepta:

https://github.com/stm32duino/BoardManagerFiles/raw/main/package_stmicroelectronics_index.json.

Después ve a Herramientas › Placa › Gestor de Tarjetas (Tools › Board › Boards Manager), busca “STM32 MCU based boards” (de STMicroelectronics) e instálalo. Esta es la extensión conocida como STM32duino, que permite programar las tarjetas STM32 desde el entorno de Arduino.

Instala la librería del sensor (SensorLib)

La librería que maneja el BHI260AP es SensorLib, de lewisxhe. Descárgala como archivo .ZIP desde su repositorio:

https://github.com/lewisxhe/SensorLib.

En GitHub, presiona el botón verde Code › Download ZIP. Luego, en el Arduino IDE, ve a Programa › Incluir Librería › Añadir biblioteca .ZIP (Sketch › Include Library › Add .ZIP Library) y selecciona el archivo que descargaste. Esta librería incluye el soporte BHY2 necesario para leer los cuaterniones del sensor.

Configura la placa en el Arduino IDE

Con la tarjeta conectada por USB, entra al menú Herramientas (Tools) y ajusta los parámetros como se muestra en la imagen. La configuración utilizada en esta práctica es:

  • Board: Generic STM32F4 series.
  • Board part number: Generic Black Pill F411CE.
  • Upload method: STM32CubeProgrammer (DFU).
  • USB support: CDC (generic ‘Serial’ supersede U(S)ART).
  • U(S)ART support: Enabled (generic ‘Serial’).
  • USB speed: Low/Full Speed.
  • Port: selecciona el puerto COM que aparezca al conectar la tarjeta .

Tip: Si la carga falla con el método DFU, ponla en modo bootloader manteniendo presionado el botón BOOT0, luego conecta el USB C, espera 2 s y finalmente suelta el botón BOOT0.

Código

Lectura de cuaterniones en la STM32 (Arduino / STM32duino):

Sube el siguiente código a tu placa de desarrollo Black Pill

 
#define BOSCH_APP30_SHUTTLE_BHI260_FW
#include <BoschFirmware.h>

SensorBHI260AP bhy;

void parse_cuaternion(uint8_t sensor_id, const uint8_t *data,
                      uint32_t size, uint64_t *timestamp, void *user_data)
{
    struct bhy2_data_quaternion q;
    bhy2_parse_quaternion(data, &q);
    Serial.print(q.w / 16384.0f, 4); Serial.print(",");
    Serial.print(q.x / 16384.0f, 4); Serial.print(",");
    Serial.print(q.y / 16384.0f, 4); Serial.print(",");
    Serial.println(q.z / 16384.0f, 4);
}

void setup()
{
    Serial.begin(115200);
    uint32_t t0 = millis();
    while (!Serial && (millis() - t0 < 3000)) { delay(10); }

    Wire.setSDA(BHI260_SDA);
    Wire.setSCL(BHI260_SCL);
    Wire.begin();

    bhy.setPins(BHI260_RST);
    bhy.setFirmware(bosch_firmware_image, bosch_firmware_size, bosch_firmware_type, false);
    bhy.setBootFromFlash(false);

    if (!bhy.begin(Wire, BHI260AP_SLAVE_ADDRESS_L)) {
        while (1) { delay(1000); }
    }

    bhy.configure(BoschSensorID::GAME_ROTATION_VECTOR, 100.0, 0);
    bhy.onResultEvent(BoschSensorID::GAME_ROTATION_VECTOR, parse_cuaternion);
}

void loop()
{
    bhy.update();
    delay(50);
}

Este código imprime en cada lectura los cuatro valores del cuaternión separados por comas (w, x, y, z) por el puerto serie a 115200 baudios.

Visualización del cubo 3D (Processing):

Cierra el monitor serial del Arduino IDE antes de ejecutar Processing (un mismo puerto COM no puede estar abierto en dos programas). Cambia COM10 por el puerto de tu equipo y presiona Run.

 
import processing.serial.*;
Serial port = null;
String buffer = "";
int recibidas = 0;
String estado = "abriendo puerto en 1s...";
float qw=1, qx=0, qy=0, qz=0;
float[][] V={{-1,-1,-1},{1,-1,-1},{1,1,-1},{-1,1,-1},{-1,-1,1},{1,-1,1},{1,1,1},{-1,1,1}};
int[][] F={{4,5,6,7},{0,1,2,3},{1,2,6,5},{0,3,7,4},{3,2,6,7},{0,1,5,4}};
color[] C;
void setup(){
  size(700,700);
  surface.setVisible(true);
  C=new color[]{color(220,60,60),color(60,120,220),color(60,200,90),
                color(230,180,40),color(200,90,200),color(60,200,200)};
}
void draw(){
  background(25);
  if(port==null && frameCount==60){
    try{ port=new Serial(this,"COM10",115200); estado="COM10 ABIERTO"; }
    catch(Exception e){ estado="ERROR: "+e.getMessage(); }
  }
  if(port!=null){
    while(port.available()>0){
      char c=(char)port.read();
      if(c=='\n'){ procesar(trim(buffer)); buffer=""; }
      else if(c!='\r') buffer+=c;
    }
  }
  float n=sqrt(qw*qw+qx*qx+qy*qy+qz*qz); if(n<1e-6) n=1;
  float w=qw/n,x=qx/n,y=qy/n,z=qz/n;
  float r00=1-2*(y*y+z*z),r01=2*(x*y-w*z),  r02=2*(x*z+w*y);
  float r10=2*(x*y+w*z),  r11=1-2*(x*x+z*z),r12=2*(y*z-w*x);
  float r20=2*(x*z-w*y),  r21=2*(y*z+w*x),  r22=1-2*(x*x+y*y);
  float S=150, cx=width/2, cy=height/2;
  float[] px=new float[8], py=new float[8], pz=new float[8];
  for(int i=0;i<8;i++){
    float vx=V[i][0],vy=V[i][1],vz=V[i][2];
    px[i]=cx+(r00*vx+r01*vy+r02*vz)*S;
    py[i]=cy-(r10*vx+r11*vy+r12*vz)*S;
    pz[i]=    r20*vx+r21*vy+r22*vz;
  }
  int[] ord={0,1,2,3,4,5};
  float[] fz=new float[6];
  for(int i=0;i<6;i++) fz[i]=pz[F[i][0]]+pz[F[i][1]]+pz[F[i][2]]+pz[F[i][3]];
  for(int a=0;a<6;a++) for(int b=a+1;b<6;b++)
    if(fz[ord[b]]<fz[ord[a]]){ int t=ord[a]; ord[a]=ord[b]; ord[b]=t; }
  stroke(0); strokeWeight(1);
  for(int k=0;k<6;k++){
    int f=ord[k];
    fill(C[f]);
    beginShape();
    for(int j=0;j<4;j++) vertex(px[F[f][j]], py[F[f][j]]);
    endShape(CLOSE);
  }
  fill(255); textAlign(LEFT,TOP); textSize(14);
  text(estado,10,10);
  text("Recibidas: "+recibidas,10,30);
  text("q = "+nf(qw,1,3)+", "+nf(qx,1,3)+", "+nf(qy,1,3)+", "+nf(qz,1,3),10,50);
}
void procesar(String s){
  if(s.length()==0) return;
  String[] v=split(s,',');
  if(v.length>=4){
    float w=float(v[0]),x=float(v[1]),y=float(v[2]),z=float(v[3]);
    if(!Float.isNaN(w)&&!Float.isNaN(x)&&!Float.isNaN(y)&&!Float.isNaN(z)){
      qw=w; qx=x; qy=y; qz=z; recibidas++;
    }
  }
}

Conclusiones

El T-BHI260AP es una excelente puerta de entrada al edge AI y al procesamiento de movimiento: delega todo el cálculo complejo al chip Bosch y te entrega la orientación lista para usar. Al visualizarla con Processing compruebas, de forma inmediata y visual, que la comunicación funciona y que el sensor responde con precisión. A partir de aquí puedes dar el siguiente paso —por ejemplo, controlar el ángulo de un servomotor según la inclinación detectada— uniendo el mundo del movimiento con el de la robótica.

@unitelectronics

Lleva tus proyectos al siguiente nivel con el nuevo LILYGO T-BHI260AP. Este sensor no solo mide el movimiento; tiene un coprocesador de Inteligencia Artificial integrado que hace toda la fusión de sensores por ti. ¡IA en el sensor, no en tu MCU! 🧠✨ Disponible en uelectronics.com #LILYGO #arduino #esp32 #electronica #maker

♬ sonido original – UNIT Electronics

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *