姿态解算代码详解

#include <Wire.h>
#include <math.h>

// MPU6050引脚定义
const int MPU6050_ADDR = 0x68; // I2C地址
const int SCL_PIN = 22;         // GPIO22
const int SDA_PIN = 21;         // GPIO21

// 校准参数
float accelBias[3] = {0, 0, 0};
float gyroBias[3] = {0, 0, 0};

// 姿态变量
float roll = 0, pitch = 0, yaw = 0;
unsigned long prevTime = 0;

void setup() {
  Serial.begin(115200);
  Wire.begin(SDA_PIN, SCL_PIN, 400000); // 400kHz I2C
  
  // 初始化MPU6050
  Wire.beginTransmission(MPU6050_ADDR);
  Wire.write(0x6B); // PWR_MGMT_1寄存器
  Wire.write(0);    // 解除休眠状态
  Wire.endTransmission(true);
  
  // 校准传感器(放置传感器水平静止2秒)
  calibrateMPU6050();
  Serial.println("MPU6050 Ready");
}

void loop() {
  // 读取原始数据
  int16_t accelX, accelY, accelZ, gyroX, gyroY, gyroZ;
  readMPU6050(accelX, accelY, accelZ, gyroX, gyroY, gyroZ);
  
  // 转换为实际物理量
  float accelData[3] = {
    (accelX / 16384.0) - accelBias[0], // ±2g范围
    (accelY / 16384.0) - accelBias[1],
    (accelZ / 16384.0) - accelBias[2]
  };
  
  float gyroData[3] = {
    (gyroX / 131.0) - gyroBias[0],     // ±250°/s范围
    (gyroY / 131.0) - gyroBias[1],
    (gyroZ / 131.0) - gyroBias[2]
  };
  
  // 计算时间间隔(单位:秒)
  unsigned long currentTime = micros();
  float deltaTime = (currentTime - prevTime) / 1000000.0;
  prevTime = currentTime;
  
  // 姿态解算(互补滤波)
  calculateAttitude(accelData, gyroData, deltaTime);
  
  // 串口输出
  Serial.print("Roll: "); Serial.print(roll);
  Serial.print("°\tPitch: "); Serial.print(pitch);
  Serial.print("°\tYaw: "); Serial.print(yaw); Serial.println("°");
  
  delay(50); // 控制输出频率
}

// 读取MPU6050原始数据
void readMPU6050(int16_t &ax, int16_t &ay, int16_t &az, int16_t &gx, int16_t &gy, int16_t &gz) {
  Wire.beginTransmission(MPU6050_ADDR);
  Wire.write(0x3B); // 从ACCEL_XOUT_H开始读取
  Wire.endTransmission(false);
  Wire.requestFrom(MPU6050_ADDR, 14, true);
  
  ax = Wire.read() << 8 | Wire.read();
  ay = Wire.read() << 8 | Wire.read();
  az = Wire.read() << 8 | Wire.read();
  Wire.read(); // 跳过温度数据
  Wire.read();
  gx = Wire.read() << 8 | Wire.read();
  gy = Wire.read() << 8 | Wire.read();
  gz = Wire.read() << 8 | Wire.read();
}

// 校准MPU6050
void calibrateMPU6050() {
  Serial.println("Calibrating MPU6050... Keep sensor stationary!");
  delay(2000);
  
  int32_t accelSum[3] = {0}, gyroSum[3] = {0};
  for (int i = 0; i < 200; i++) {
    int16_t ax, ay, az, gx, gy, gz;
    readMPU6050(ax, ay, az, gx, gy, gz);
    accelSum[0] += ax; accelSum[1] += ay; accelSum[2] += az;
    gyroSum[0] += gx; gyroSum[1] += gy; gyroSum[2] += gz;
    delay(10);
  }
  
  accelBias[0] = accelSum[0] / 200 / 16384.0;
  accelBias[1] = accelSum[1] / 200 / 16384.0;
  accelBias[2] = (accelSum[2] / 200 - 16384) / 16384.0; // 减去1g重力
  
  gyroBias[0] = gyroSum[0] / 200 / 131.0;
  gyroBias[1] = gyroSum[1] / 200 / 131.0;
  gyroBias[2] = gyroSum[2] / 200 / 131.0;
  
  Serial.println("Calibration Complete");
}

// 互补滤波姿态解算
void calculateAttitude(float accel[3], float gyro[3], float dt) {
  // 加速度计计算姿态(单位:度)
  float accRoll = atan2(accel[1], accel[2]) * 180 / PI;
  float accPitch = atan2(-accel[0], sqrt(accel[1]*accel[1] + accel[2]*accel[2])) * 180 / PI;
  
  // 互补滤波系数(0.98依赖陀螺仪,0.02依赖加速度计)
  const float alpha = 0.98;
  
  // 融合数据
  roll = alpha * (roll + gyro[0] * dt) + (1 - alpha) * accRoll;
  pitch = alpha * (pitch + gyro[1] * dt) + (1 - alpha) * accPitch;
  yaw += gyro[2] * dt; // 陀螺仪直接积分
}

1. 代码结构解析

1.1 硬件配置部分
const int MPU6050_ADDR = 0x68;  // I2C器件地址
const int SCL_PIN = 22;         // I2C时钟引脚
const int SDA_PIN = 21;         // I2C数据引脚
  • 采用硬件I2C接口,时钟频率设置为400kHz
  • 标准MPU6050地址为0x68(AD0引脚接地时)
1.2 核心变量
float accelBias[3], gyroBias[3];  // 校准偏移量
float roll, pitch, yaw;           // 欧拉角
unsigned long prevTime;           // 时间戳
  • 使用三轴校准参数补偿传感器误差
  • 采用micros()高精度计时实现dt计算
1.3 初始化流程
 
void setup() {
  // 1. 串口初始化
  // 2. I2C总线启动
  // 3. 唤醒MPU6050
  // 4. 自动校准
}
  • 通过PWR_MGMT_1寄存器(0x6B)解除休眠
  • 校准过程需保持设备静止2秒
1.4 主循环逻辑
 
void loop() {
  // 1. 读取原始数据
  // 2. 单位转换和校准补偿
  // 3. 计算时间间隔dt
  // 4. 互补滤波姿态解算
  // 5. 串口输出结果
}
  • 20Hz数据更新频率(delay(50))
  • 同时输出roll/pitch/yaw三轴角度

2. 关键算法说明

2.1 数据读取协议
 
void readMPU6050() {
  // 寄存器0x3B起始的14字节数据:
  // ACCEL_XOUT_H → ACCEL_ZOUT_L → TEMP_OUT_H → GYRO_XOUT_H → GYRO_ZOUT_L
}
  • 采用连续读取模式提高效率
  • 数据格式:大端序16位补码
2.2 校准算法
 
void calibrateMPU6050() {
  // 加速度计:扣除重力影响
  // 陀螺仪:计算零偏平均值
}
  • 加速度计Z轴默认减去1g(16384 LSB)
  • 采集200个样本进行统计分析
2.3 互补滤波实现
 
void calculateAttitude() {
  // 加速度计求瞬时角度
  // 陀螺仪积分得到趋势角度
  // 加权融合(α=0.98)
}
  • 加速度计:atan2三角函数解算
  • 陀螺仪:角度=角速度×时间积分
  • 动态权重抑制各自缺点

3. 代码流程图


4. 关键参数说明

参数计算公式物理意义
加速度计灵敏度16384 LSB/g (±2g)每g重力对应的数字量
陀螺仪灵敏度131 LSB/°/s (±250°/s)每度/秒对应的数字量
互补滤波系数α0.98陀螺仪数据权重占比
校准采样数200次统计平均的样本数量
### 四元数在姿态中的应用 #### 四元数的基础概念 四元数是一种扩展了复数系统的数学结构,用于描述三维空间内的旋转。其一般形式为 \( q = q_0 + q_1i + q_2j + q_3k \),其中 \( q_0, q_1, q_2, q_3 \) 是实数系数[^3]。 #### 归一化处理 为了确保四元数能够正确表示旋转操作,通常需要对其进行归一化处理。对于任意给定的四元数 \( q \),可以通过下面的方式获得单位四元数: \[ q_{normalized} = \frac{q}{|q|}, |q| = \sqrt{q_0^2 + q_1^2 + q_2^2 + q_3^2} \] 这里 \( |q| \) 表示四元数的模或范数[^1]。 #### 通过角速度更新四元数 当利用三轴陀螺仪数据来估计设备的姿态变化时,会涉及到基于角速度矢量 \( \overrightarrow{\omega_n} \) 更新当前姿态对应的四元数。具体来说,就是将新的角速度信息转换成增量四元数 \( q' \),再与现有的姿态四元数相乘以获取最新的状态: \[ q = q' \otimes q \] 此过程中需要注意的是,\( \overrightarrow{\omega_n} \) 需要先被归一化后再参与计[^2]。 #### 实现代码示例 下面是Python实现的一个简单例子,展示了如何根据角速度更新四元数: ```python import numpy as np def normalize_quaternion(q): norm = np.sqrt(np.sum(np.square(q))) return q / norm if norm != 0 else q def update_quaternion_with_gyro(gyro_data, dt, current_q): omega_norm = np.linalg.norm(gyro_data) half_theta = omega_norm * dt / 2.0 sin_half_theta_over_two = np.sin(half_theta) delta_q = [ np.cos(half_theta), gyro_data[0]*sin_half_theta_over_two, gyro_data[1]*sin_half_theta_over_two, gyro_data[2]*sin_half_theta_over_two ] updated_q = quaternion_multiply(delta_q, current_q) return normalize_quaternion(updated_q) def quaternion_multiply(q1, q2): w1, x1, y1, z1 = q1 w2, x2, y2, z2 = q2 product = [-x1*x2 - y1*y2 - z1*z2 + w1*w2, x1*w2 + y1*z2 - z1*y2 + w1*x2, -x1*z2 + y1*w2 + z1*x2 + w1*y2, x1*y2 - y1*x2 + z1*w2 + w1*z2] return product # Example usage: gyro_reading = [0.5, 0.7, 0.9] # Angular velocity from gyroscope (rad/s) time_step = 0.01 # Time step between measurements (s) initial_orientation = [1., 0., 0., 0.] # Initial orientation as a unit quaternion new_orientation = update_quaternion_with_gyro(normalize_quaternion(gyro_reading), time_step, initial_orientation) print(new_orientation) ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值