回答

收藏

零知开源——ESP8266结合ICM20948实现高精度姿态解算

其他 其他 1198 人阅读 | 0 人回复 | 2025-03-07

零知实验室发布新版ICM20948模块,可以非常方便的应用在零知各个系列开发板或其他类似MCU,它可以作为已经停产的MPU9250的替代品,下面演示它在零知ESP8266上的使用。





一、ICM20948深度解析:九轴传感器的核心技术1.1 什么是IMU?IMUInertial Measurement Unit)即惯性测量单元,是融合加速度计、陀螺仪和磁力计的核心传感器。ICM20948作为新一代九轴IMU,具备以下技术特性:
IMU20948技术特性
参数
规格
技术优势
加速度测量范围
±2g/±4g/±8g/±16g
16ADC0.98mg/LSB@±16g
陀螺仪量程
±250/±500/±1000/±2000 dps
0.0038°/s/LSB@±250dps
磁力计量程
±4900μT
16位分辨率,0.15μT/LSB
数据输出速率
最高1125Hz
支持SPI/I2C双接口
工作电压
1.71V-3.6V
超低功耗模式<5μA
1.2 硬件架构解析芯片内部采用三层堆叠结构:

MEMS传感层:包含三轴加速度计和陀螺仪
ASIC处理层:集成数字运动处理器(DMP)
磁力计层***09916磁力计通过I2C从接口连接


1.3 九轴数据融合原理姿态解算通过传感器融合算法实现:
姿态矩阵=加速度计校准✖陀螺仪积分✖磁力计补偿


典型算法对比:
算法
计算复杂度
精度
适用场景
互补滤波

一般
低速运动
卡尔曼滤波


动态环境
Mahony
中等
较高
嵌入式系统

二、硬件系统搭建2.1 物料清单
组件
型号
主控板
零知ESP8266
九轴传感器
ICM20948
连接线
杜邦线
电源
USB适配器

2.2 电路连接详解零知ESP8266ICM20948九轴加速度传感器的接线图:
零知ESP8266
ICM20948
3.3V
VCC
GND
GND
SCL
SCL
SDA
SDA

三、软件系统开发3.1 校准验证代码
  1. ​// 在setup()中添加的校准验证
  2. if(SerialDebug) {
  3.   Serial.println("Post-Calibration Accel Bias (mg):");
  4.   Serial.print(1000*myIMU.accelBias[0]);
  5.   Serial.print(" ");
  6.   Serial.print(1000*myIMU.accelBias[1]);
  7.   Serial.print(" ");
  8.   Serial.println(1000*myIMU.accelBias[2]);
  9. }
复制代码
将加速度偏置转换为mg单位(1g=1000mg)
X/Y轴偏置应<50mg,Z轴接近0(理想值)
确保校准过程有效,避免硬件安装误差



3.2 动态零位补偿
  1. ​static int calibration_cnt = 0;
  2. if(calibration_cnt < 1000 && abs(myIMU.gx)<0.5 && abs(myIMU.gy)<0.5) {
  3.   myIMU.accelBias[0] += myIMU.ax * 0.001;
  4.   myIMU.accelBias[1] += myIMU.ay * 0.001;
  5.   calibration_cnt++;
  6. }
复制代码
1000次采样持续修正加速度偏置
0.001为学习率系数,控制校准速度
实现动态自适应,消除温度漂移影响


3.3传感器数据预处理 1.加速度计处理
  1. ​myIMU.ax = (float)myIMU.accelCount[0] * myIMU.aRes - myIMU.accelBias[0];
  2. myIMU.ay = (float)myIMU.accelCount[1] * myIMU.aRes - myIMU.accelBias[1];
  3. myIMU.az = (float)myIMU.accelCount[2] * myIMU.aRes - myIMU.accelBias[2];
复制代码
数据处理流程
accelCount:原始ADC值
aRes:分辨率计算(例如±16g量程时为2048 LSB/g)
减去校准偏置消除零位误差
关键参数
量程设置:建议初始化时配置为±8g
分辨率公式:aRes = 16.0 / 32768.0 (16位ADC)


2.陀螺仪处理
  1. ​myIMU.gx = (float)myIMU.gyroCount[0] * myIMU.gRes - myIMU.gyroBias[0];
  2. myIMU.gy = (float)myIMU.gyroCount[1] * myIMU.gRes - myIMU.gyroBias[1];
  3. myIMU.gz = (float)myIMU.gyroCount[2] * myIMU.gRes - myIMU.gyroBias[2];
复制代码
漂移控制
典型偏置值应<1°/s
温度每升高1℃,零偏变化约0.01°/s
改进建议:添加温度补偿函数


3.磁力计数据融合
  1. ​float mx_raw = (float)myIMU.magCount[1] * myIMU.mRes; // X/Y交换
  2. float my_raw = (float)myIMU.magCount[0] * myIMU.mRes;
  3. float mz_raw = -(float)myIMU.magCount[2] * myIMU.mRes; // Z反转

  4. myIMU.mx = (mx_raw - myIMU.magBias[1]) * myIMU.magScale[1];
  5. myIMU.my = (my_raw - myIMU.magBias[0]) * myIMU.magScale[0];
  6. myIMU.mz = (mz_raw - myIMU.magBias[2]) * myIMU.magScale[2];
复制代码
magBias:硬铁干扰补偿
magScale:软铁畸变校正
注意:校准数据需对应新坐标系

3.4姿态解算核心算法 1.Mahony滤波器调用
  1. ​MahonyQuaternionUpdate(
  2.   myIMU.ay,  // 加速度Y→X
  3.   myIMU.ax,   // 加速度X→Y
  4.   -myIMU.az,  // 加速度Z反转
  5.   myIMU.gy * DEG_TO_RAD, // 陀螺Y→X
  6.   myIMU.gx * DEG_TO_RAD, // 陀螺X→Y
  7.   -myIMU.gz * DEG_TO_RAD,// 陀螺Z反转
  8.   myIMU.mx,
  9.   myIMU.my,
  10.   myIMU.mz,
  11.   myIMU.deltat
  12. );
复制代码


2.欧拉角转换
​myIMU.yaw   = atan2(2.0f * (*(getQ()+1) * *(getQ()+2) + *getQ()
                    * *(getQ()+3)), *getQ() * *getQ() + *(getQ()+1)
                    * *(getQ()+1) - *(getQ()+2) * *(getQ()+2) - *(getQ()+3)
                    * *(getQ()+3));
      myIMU.pitch = -asin(2.0f * (*(getQ()+1) * *(getQ()+3) - *getQ()
                    * *(getQ()+2)));
      myIMU.roll  = atan2(2.0f * (*getQ() * *(getQ()+1) + *(getQ()+2)
                    * *(getQ()+3)), *getQ() * *getQ() - *(getQ()+1)
                    * *(getQ()+1) - *(getQ()+2) * *(getQ()+2) + *(getQ()+3)
                    * *(getQ()+3));
      myIMU.pitch *= RAD_TO_DEG;
      myIMU.yaw   *= RAD_TO_DEG;

      // Declination of SparkFun Electronics (40°05'26.6"N 105°11'05.9"W) is
      //         8° 30' E  ± 0° 21' (or 8.5°) on 2016-07-19
      // - http://www.ngdc.noaa.gov/geomag-web/#declination
      myIMU.yaw  -= 8.5;
      myIMU.roll *= RAD_TO_DEG;


3.5数据输出 串口协议设计
  1. ​//打印格式与processing端格式一致
  2.   Serial.print("Or: ");
  3.   Serial.print(myIMU.yaw, 2);
  4.   Serial.print(" ");
  5.   
  6.   Serial.print(myIMU.pitch, 2);
  7.   Serial.print(" ");

  8.   Serial.print(myIMU.roll, 2);
  9.   Serial.println(" ");
复制代码


3.6Processing ***可视化验证
将代码库文件安装包解压到C:\Users\Administrator\Documents\Processing\libraries,然后在Processing中选择开发板对应的串口号,就可以看到我们的***模型根据九轴的姿态进行变化啦:
  1. ​import processing.serial.*;

  2. // 传感器数据
  3. float roll, pitch, yaw;
  4. PVector accelerometer = new PVector();
  5. PVector gyroscope = new PVector();
  6. PVector magneticField = new PVector();

  7. // ***模型
  8. PShape model;
  9. PImage bgImage;

  10. // 串口配置
  11. Serial port;
  12. String[] serialPorts;
  13. int selectedPort = 0;
  14. boolean printSerial = false;

  15. void setup() {
  16.   size(1024, 800, P***);
  17.   frameRate(60);
  18.   
  19.   // 加载资源
  20.   bgImage = loadImage("background.png");
  21.   model = loadShape("biplane.obj"); // 确保使用标准OBJ格式
  22.   model.scale(30);
  23.   
  24.   // 初始化串口
  25.   serialPorts = Serial.list();
  26.   if(serialPorts.length > 0) connectSerial(serialPorts[0]);
  27. }

  28. void draw() {
  29.   background(bgImage);
  30.   
  31.   // ***场景设置
  32.   pushMatrix();
  33.   translate(width/2, height/2, 0);
  34.   lights();
  35.   
  36.   // 应用***
  37.   rotateX(radians(pitch));
  38.   rotateY(radians(yaw));
  39.   rotateZ(radians(roll));
  40.   
  41.   // 绘制模型
  42.   shape(model);
  43.   popMatrix();
  44.   
  45.   // 显示数据
  46.   displaySensorData();
  47. }

  48. void serialEvent(Serial p) {
  49.   try {
  50.     String rawData = p.readStringUntil('\n').trim();
  51.     if(printSerial) println(rawData);
  52.    
  53.     String[] parts = split(rawData, ' ');
  54.     if(parts.length >= 4) {
  55.       switch(parts[0]) {
  56.         case "Or:": // 欧拉角格式:Or: yaw pitch roll
  57.           yaw = float(parts[1]);
  58.           pitch = float(parts[2]);
  59.           roll = float(parts[3]);
  60.           break;
  61.         case "Accel:":
  62.           accelerometer.set(float(parts[1]), float(parts[2]), float(parts[3]));
  63.           break;
  64.         case "Gyro:":
  65.           gyroscope.set(float(parts[1]), float(parts[2]), float(parts[3]));
  66.           break;
  67.         case "Mag:":
  68.           magneticField.set(float(parts[1]), float(parts[2]), float(parts[3]));
  69.           break;
  70.       }
  71.     }
  72.   } catch(Exception e) {
  73.     println("Serial Error: " + e.getMessage());
  74.   }
  75. }

  76. void displaySensorData() {
  77.   fill(0, 255, 0);
  78.   textSize(16);
  79.   textAlign(LEFT, TOP);
  80.   String data = "Accelerometer(g): "
  81.     + nfp(accelerometer.x,1,2) + ", "
  82.     + nfp(accelerometer.y,1,2) + ", "
  83.     + nfp(accelerometer.z,1,2) + "\n"
  84.     + "Gyroscope(deg/s): "
  85.     + nfp(gyroscope.x,1,2) + ", "
  86.     + nfp(gyroscope.y,1,2) + ", "
  87.     + nfp(gyroscope.z,1,2) + "\n"
  88.     + "Orientation: \n"
  89.     + "Yaw: " + nfp(yaw,1,1) + "°\n"
  90.     + "Pitch: " + nfp(pitch,1,1) + "°\n"
  91.     + "Roll: " + nfp(roll,1,1) + "°";
  92.   text(data, 20, 20);
  93. }

  94. void connectSerial(String portName) {
  95.   if(port != null) port.stop();
  96.   try {
  97.     port = new Serial(this, portName, 115200);
  98.     port.bufferUntil('\n');
  99.     println("Connected to: " + portName);
  100.   } catch(Exception e) {
  101.     println("Connection failed: " + e.getMessage());
  102.   }
  103. }

  104. void keyPressed() {
  105.   // 切换串口
  106.   if(key == ' ') {
  107.     selectedPort = (selectedPort + 1) % serialPorts.length;
  108.     connectSerial(serialPorts[selectedPort]);
  109.   }
  110.   // 切换调试输出
  111.   if(key == 'P' || key == 'p') printSerial = !printSerial;
  112.   // 重置视角
  113.   if(key == 'R' || key == 'r') {
  114.     yaw = pitch = roll = 0;
  115.   }
  116. }
复制代码




四、实现结果分析
观察串口打印输出的DMP姿态解算数据如下:



Processing ***可视化验证:
Processing ***可视化验证ICM20948运动姿态数据

五、项目资源汇总5.1 参考资料 ICM20948数据手册
ESP8266技术参考
5.2 源码获取
  1. https://github.com/Leeri1y/ICM20948-ESP8266
复制代码

参考Github仓库


64Windows系统的Processing安装包:
通过网盘分享的文件:processing-4.3.3.7z
链接: https://pan.baidu.com/s/12B4F33M1caRncVjSJiFPTg?pwd=9h5i 提取码: 9h5i


5.3 扩展学习Mahony滤波器数学推导
欢迎各位道友相互讨论,一直在学习的路上!



分享到:
回复

使用道具 举报

您需要登录后才可以回帖 注册/登录

本版积分规则

113 积分
20 主题
+ 关注
热门推荐
关闭

站长推荐上一条 /2 下一条