stm32 mpu6050 cubemx 卡尔曼滤波法读取角度

2023-10-27


前言

本文简述使用mpu6050读取原始数据后解算出角度。
网上大多都是dmp库来解算,但是这种情况操作起来相对复杂。
更方便的方法是使用卡尔曼滤波法来解算出角度,好处是代码量少且移植相对简单许多,弊端是没有航向角。

文件

这里是引用


一、cubemx配置

只要打开硬件i2c即可。选择为fast模式

在这里插入图片描述

如果想用串口输出的话还可以打开usart。

二、mpu6050文件移植

只需要把我下面两个文件添加到你的项目中即可
要注意!!!使用到了微库所以一定要把keil中的微库勾选!!!

mpu6050.c


#include <math.h>
#include "mpu6050.h"

#define RAD_TO_DEG 57.295779513082320876798154814105

#define WHO_AM_I_REG 0x75
#define PWR_MGMT_1_REG 0x6B
#define SMPLRT_DIV_REG 0x19
#define ACCEL_CONFIG_REG 0x1C
#define ACCEL_XOUT_H_REG 0x3B
#define TEMP_OUT_H_REG 0x41
#define GYRO_CONFIG_REG 0x1B
#define GYRO_XOUT_H_REG 0x43

// Setup MPU6050
#define MPU6050_ADDR 0xD0
const uint16_t i2c_timeout = 100;
const double Accel_Z_corrector = 14418.0;

uint32_t timer;

Kalman_t KalmanX = {
        .Q_angle = 0.001f,
        .Q_bias = 0.003f,
        .R_measure = 0.03f
};

Kalman_t KalmanY = {
        .Q_angle = 0.001f,
        .Q_bias = 0.003f,
        .R_measure = 0.03f,
};

uint8_t MPU6050_Init(I2C_HandleTypeDef *I2Cx) {
    uint8_t check;
    uint8_t Data;

    // check device ID WHO_AM_I

    HAL_I2C_Mem_Read(I2Cx, MPU6050_ADDR, WHO_AM_I_REG, 1, &check, 1, i2c_timeout);

    if (check == 104)  // 0x68 will be returned by the sensor if everything goes well
    {
        // power management register 0X6B we should write all 0's to wake the sensor up
        Data = 0;
        HAL_I2C_Mem_Write(I2Cx, MPU6050_ADDR, PWR_MGMT_1_REG, 1, &Data, 1, i2c_timeout);

        // Set DATA RATE of 1KHz by writing SMPLRT_DIV register
        Data = 0x07;
        HAL_I2C_Mem_Write(I2Cx, MPU6050_ADDR, SMPLRT_DIV_REG, 1, &Data, 1, i2c_timeout);

        // Set accelerometer configuration in ACCEL_CONFIG Register
        // XA_ST=0,YA_ST=0,ZA_ST=0, FS_SEL=0 -> ? 2g
        Data = 0x00;
        HAL_I2C_Mem_Write(I2Cx, MPU6050_ADDR, ACCEL_CONFIG_REG, 1, &Data, 1, i2c_timeout);

        // Set Gyroscopic configuration in GYRO_CONFIG Register
        // XG_ST=0,YG_ST=0,ZG_ST=0, FS_SEL=0 -> ? 250 ?/s
        Data = 0x00;
        HAL_I2C_Mem_Write(I2Cx, MPU6050_ADDR, GYRO_CONFIG_REG, 1, &Data, 1, i2c_timeout);
        return 0;
    }
    return 1;
}


void MPU6050_Read_Accel(I2C_HandleTypeDef *I2Cx, MPU6050_t *DataStruct) {
    uint8_t Rec_Data[6];

    // Read 6 BYTES of data starting from ACCEL_XOUT_H register

    HAL_I2C_Mem_Read(I2Cx, MPU6050_ADDR, ACCEL_XOUT_H_REG, 1, Rec_Data, 6, i2c_timeout);

    DataStruct->Accel_X_RAW = (int16_t) (Rec_Data[0] << 8 | Rec_Data[1]);
    DataStruct->Accel_Y_RAW = (int16_t) (Rec_Data[2] << 8 | Rec_Data[3]);
    DataStruct->Accel_Z_RAW = (int16_t) (Rec_Data[4] << 8 | Rec_Data[5]);

    /*** convert the RAW values into acceleration in 'g'
         we have to divide according to the Full scale value set in FS_SEL
         I have configured FS_SEL = 0. So I am dividing by 16384.0
         for more details check ACCEL_CONFIG Register              ****/

    DataStruct->Ax = DataStruct->Accel_X_RAW / 16384.0;
    DataStruct->Ay = DataStruct->Accel_Y_RAW / 16384.0;
    DataStruct->Az = DataStruct->Accel_Z_RAW / Accel_Z_corrector;
}


void MPU6050_Read_Gyro(I2C_HandleTypeDef *I2Cx, MPU6050_t *DataStruct) {
    uint8_t Rec_Data[6];

    // Read 6 BYTES of data starting from GYRO_XOUT_H register

    HAL_I2C_Mem_Read(I2Cx, MPU6050_ADDR, GYRO_XOUT_H_REG, 1, Rec_Data, 6, i2c_timeout);

    DataStruct->Gyro_X_RAW = (int16_t) (Rec_Data[0] << 8 | Rec_Data[1]);
    DataStruct->Gyro_Y_RAW = (int16_t) (Rec_Data[2] << 8 | Rec_Data[3]);
    DataStruct->Gyro_Z_RAW = (int16_t) (Rec_Data[4] << 8 | Rec_Data[5]);

    /*** convert the RAW values into dps (?/s)
         we have to divide according to the Full scale value set in FS_SEL
         I have configured FS_SEL = 0. So I am dividing by 131.0
         for more details check GYRO_CONFIG Register              ****/

    DataStruct->Gx = DataStruct->Gyro_X_RAW / 131.0;
    DataStruct->Gy = DataStruct->Gyro_Y_RAW / 131.0;
    DataStruct->Gz = DataStruct->Gyro_Z_RAW / 131.0;
}

void MPU6050_Read_Temp(I2C_HandleTypeDef *I2Cx, MPU6050_t *DataStruct) {
    uint8_t Rec_Data[2];
    int16_t temp;

    // Read 2 BYTES of data starting from TEMP_OUT_H_REG register

    HAL_I2C_Mem_Read(I2Cx, MPU6050_ADDR, TEMP_OUT_H_REG, 1, Rec_Data, 2, i2c_timeout);

    temp = (int16_t) (Rec_Data[0] << 8 | Rec_Data[1]);
    DataStruct->Temperature = (float) ((int16_t) temp / (float) 340.0 + (float) 36.53);
}

void MPU6050_Read_All(I2C_HandleTypeDef *I2Cx, MPU6050_t *DataStruct) {
    uint8_t Rec_Data[14];
    int16_t temp;

    // Read 14 BYTES of data starting from ACCEL_XOUT_H register

    HAL_I2C_Mem_Read(I2Cx, MPU6050_ADDR, ACCEL_XOUT_H_REG, 1, Rec_Data, 14, i2c_timeout);

    DataStruct->Accel_X_RAW = (int16_t) (Rec_Data[0] << 8 | Rec_Data[1]);
    DataStruct->Accel_Y_RAW = (int16_t) (Rec_Data[2] << 8 | Rec_Data[3]);
    DataStruct->Accel_Z_RAW = (int16_t) (Rec_Data[4] << 8 | Rec_Data[5]);
    temp = (int16_t) (Rec_Data[6] << 8 | Rec_Data[7]);
    DataStruct->Gyro_X_RAW = (int16_t) (Rec_Data[8] << 8 | Rec_Data[9]);
    DataStruct->Gyro_Y_RAW = (int16_t) (Rec_Data[10] << 8 | Rec_Data[11]);
    DataStruct->Gyro_Z_RAW = (int16_t) (Rec_Data[12] << 8 | Rec_Data[13]);

    DataStruct->Ax = DataStruct->Accel_X_RAW / 16384.0;
    DataStruct->Ay = DataStruct->Accel_Y_RAW / 16384.0;
    DataStruct->Az = DataStruct->Accel_Z_RAW / Accel_Z_corrector;
    DataStruct->Temperature = (float) ((int16_t) temp / (float) 340.0 + (float) 36.53);
    DataStruct->Gx = DataStruct->Gyro_X_RAW / 131.0;
    DataStruct->Gy = DataStruct->Gyro_Y_RAW / 131.0;
    DataStruct->Gz = DataStruct->Gyro_Z_RAW / 131.0;

    // Kalman angle solve
    double dt = (double) (HAL_GetTick() - timer) / 1000;
    timer = HAL_GetTick();
    double roll;
    double roll_sqrt = sqrt(
            DataStruct->Accel_X_RAW * DataStruct->Accel_X_RAW + DataStruct->Accel_Z_RAW * DataStruct->Accel_Z_RAW);
    if (roll_sqrt != 0.0) {
        roll = atan(DataStruct->Accel_Y_RAW / roll_sqrt) * RAD_TO_DEG;
    } else {
        roll = 0.0;
    }
    double pitch = atan2(-DataStruct->Accel_X_RAW, DataStruct->Accel_Z_RAW) * RAD_TO_DEG;
    if ((pitch < -90 && DataStruct->KalmanAngleY > 90) || (pitch > 90 && DataStruct->KalmanAngleY < -90)) {
        KalmanY.angle = pitch;
        DataStruct->KalmanAngleY = pitch;
    } else {
        DataStruct->KalmanAngleY = Kalman_getAngle(&KalmanY, pitch, DataStruct->Gy, dt);
    }
    if (fabs(DataStruct->KalmanAngleY) > 90)
        DataStruct->Gx = -DataStruct->Gx;
    DataStruct->KalmanAngleX = Kalman_getAngle(&KalmanX, roll, DataStruct->Gy, dt);

}

double Kalman_getAngle(Kalman_t *Kalman, double newAngle, double newRate, double dt) {
    double rate = newRate - Kalman->bias;
    Kalman->angle += dt * rate;

    Kalman->P[0][0] += dt * (dt * Kalman->P[1][1] - Kalman->P[0][1] - Kalman->P[1][0] + Kalman->Q_angle);
    Kalman->P[0][1] -= dt * Kalman->P[1][1];
    Kalman->P[1][0] -= dt * Kalman->P[1][1];
    Kalman->P[1][1] += Kalman->Q_bias * dt;

    double S = Kalman->P[0][0] + Kalman->R_measure;
    double K[2];
    K[0] = Kalman->P[0][0] / S;
    K[1] = Kalman->P[1][0] / S;

    double y = newAngle - Kalman->angle;
    Kalman->angle += K[0] * y;
    Kalman->bias += K[1] * y;

    double P00_temp = Kalman->P[0][0];
    double P01_temp = Kalman->P[0][1];

    Kalman->P[0][0] -= K[0] * P00_temp;
    Kalman->P[0][1] -= K[0] * P01_temp;
    Kalman->P[1][0] -= K[1] * P00_temp;
    Kalman->P[1][1] -= K[1] * P01_temp;

    return Kalman->angle;
};


mpu6050.h



#ifndef INC_GY521_H_
#define INC_GY521_H_

#endif /* INC_GY521_H_ */

#include <stdint.h>
#include "i2c.h"

// MPU6050 structure
typedef struct {

    int16_t Accel_X_RAW;
    int16_t Accel_Y_RAW;
    int16_t Accel_Z_RAW;
    double Ax;
    double Ay;
    double Az;

    int16_t Gyro_X_RAW;
    int16_t Gyro_Y_RAW;
    int16_t Gyro_Z_RAW;
    double Gx;
    double Gy;
    double Gz;

    float Temperature;

    double KalmanAngleX;
    double KalmanAngleY;
} MPU6050_t;


// Kalman structure
typedef struct {
    double Q_angle;
    double Q_bias;
    double R_measure;
    double angle;
    double bias;
    double P[2][2];
} Kalman_t;


uint8_t MPU6050_Init(I2C_HandleTypeDef *I2Cx);

void MPU6050_Read_Accel(I2C_HandleTypeDef *I2Cx, MPU6050_t *DataStruct);

void MPU6050_Read_Gyro(I2C_HandleTypeDef *I2Cx, MPU6050_t *DataStruct);

void MPU6050_Read_Temp(I2C_HandleTypeDef *I2Cx, MPU6050_t *DataStruct);

void MPU6050_Read_All(I2C_HandleTypeDef *I2Cx, MPU6050_t *DataStruct);

double Kalman_getAngle(Kalman_t *Kalman, double newAngle, double newRate, double dt);


这样子就移植完成了。

三、主函数

主函数中只要调用两个函数即可。
首先是主循环之前的初始化。

	while (MPU6050_Init(&hi2c2) == 1)
	{
	};

这里要注意的就是初始化的时候一定要是你配置的那个i2c。且需要写这个while否则检测不到mpu6050的时候会导致数据混乱。!!!!!!!!
在主循环中调用:

		MPU6050_Read_All(&hi2c1, &MPU6050);

那么我们看一下结构体里面都有什么。最后两个KalmanAngle就是你要的两个角度。只需要MPU6050.KalmanAngleY即可调用。

typedef struct {

    int16_t Accel_X_RAW;
    int16_t Accel_Y_RAW;
    int16_t Accel_Z_RAW;
    double Ax;
    double Ay;
    double Az;

    int16_t Gyro_X_RAW;
    int16_t Gyro_Y_RAW;
    int16_t Gyro_Z_RAW;
    double Gx;
    double Gy;
    double Gz;

    float Temperature;

    double KalmanAngleX;
    double KalmanAngleY;
} MPU6050_t;
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

stm32 mpu6050 cubemx 卡尔曼滤波法读取角度 的相关文章

  • ubuntu设置pac代理

    前言 由于公司ubuntu没有对代理做有线代理去搜索资料 所以下面得内容是为了记录一下 设置代理pac代理有两种方式 可能还有其他得 1 浏览器设置自动代理配置得URL PAC 2 写入profile配置文件 1 浏览器设置自动代理配置的U
  • YOLOv5-Shufflenetv2

    YOLOv5中修改网络结构的一般步骤 models common py 在common py文件中 加入要修改的模块代码 models yolo py 在yolo py文件内的parse model函数里添加新模块的名称 models ne

随机推荐

  • 【100天精通Python】Day51:Python 数据分析_数据分析入门基础与Anaconda 环境搭建

    目录 1 科学计算和数据分析概述 2 数据收集和准备 2 1 数据收集 2 1 1 文件导入 2 1 2 数据库连接 2 1 3 API请求 2 1 4 网络爬虫 2 2 数据清洗 2 2 1 处理缺失值 2 2 2 去除重复值 2 2 3
  • 浪潮服务器NF5280M5配置管理口BMC的IP web界面登录 ipmi 代外【详细】

    开启服务器以后等待按del或f2 进入bios选择第五项Server Mgmt界面选择BMC Network Configuration 回车 选择BMC IPv4 Network Configuration 回车 注意 只需要配置BMC
  • MySQL——必考面试题 ①

    一 为什么要使用数据库 数据保存在内存 优点 存取速度快 缺点 数据不能永久保存 数据保存在文件 优点 数据永久保存 缺点 速度比内存操作慢 频繁的IO操作 查询数据不方便 数据保存在数据库 数据永久保存 使用SQL语句 查询方便效率高 管
  • unity生成vr效果

    这是一个谷歌的插件 GoogleVRForUnity unitypackage 谷歌插件下载地址 开始制作最简单的 VR 盒子 导入 GoogleVRForUnity unitypackage 将项目的平台设置为 Android 平台 在项
  • web前端DOM

    1 2 1 什么是DOM 文档对象模型 Document Object Model 简称DOM 是 W3C 组织推荐的处理可扩展标记语言 html或者xhtml 的标准编程接口 W3C 已经定义了一系列的 DOM 接口 通过这些 DOM 接
  • 2023.1.30日学习内容(多线程接收,发送文件)

    1 多线程接收文件 1 线程文件 public Socket socket public MyThread Socket socket this socket socket Override public void run try Stri
  • WordGo导出word(list)

    导出word文档 param userResume public String getWord BasUserResume userResume WordGo wordGo new WordGo wordGo add userResume
  • 计算机网络期中测验

    目录 一 单选题 二 填空题 三 判断题 一 单选题 1 单选题 采用全双工通信方式 数据传输的方向为 A 可以在两个方向上传输但不能同时进行 B 只能在一个方向上传输 C 可以在两个方向上同时传输 D 以上均不对 答案 C 解析 三种通信
  • 百度移动统计热力图和事件分析的坑

    埋点是这2年比较火的一项技术 友盟 极光推送 腾讯云 百度移动统计都相继开发了增加埋点的SDK 方便开发者使用 其中最为先进的是百度移动统计的无埋点技术 无埋点技术是不用开发者手动埋点的一项技术 很方便使用 对开发减少了开发量 太赞 集成步
  • jieba如何自行 split 或 join ?

    目录 jieba suggest freq 源码 split 关键运行过程解释 注意 使用此函数也有可能分不开 join 关键运行过程解释 jieba add word del word 源码 参考文献 jieba suggest freq
  • 联想拯救者R720 - i5-7300HQ/1050ti(macOS Big Sur/Windows) 双系统在 OpenCore (6.0.3)/ OCC (2.5.0)引导下的安装过程

    前言 重要 硬件列表 拯救者R720 处理器 型号 i5 7300HQ 架构 kaby lake 显卡 核显 UHD630 独显无效 忽略 主板 系列 100 Series 网卡 型号自选自购 不做陈列 声卡 批次不同 型号不同 不做陈列
  • [Unity好插件之PlayMaker]PlayMaker如何扩展额外创建更多的脚本

    学习目标 如果你正在学习使用PlayMaker的话 那么本篇文章将非常的适用 关于如何连线则是你自己的想法 本篇侧重于扩展适用更多的PlayMaker行为Action 那么什么是PlayMaker行为Action呢 就是这个列表 当我们要给
  • js echarts 固定颜色按顺序组合 或者随机生成颜色

    在使用echarts的时候或者大转盘的时候 数据量总是很多 但是颜色可以随机生成 也可以使用自己固定的颜色 这边我就分享了一下几种按照顺序组成颜色的代码 第一种 通过循环颜色 用一个splice 删一个 如果颜色没有了 再重新给他原来的数组
  • 常用事务代码(转)

    Pfcg 绝色维护 Su53 查看权限对象 st01 跟踪 St22 看dump 以分析错误 eg 找到ABAP程序出错的地方 找出founction 用se37查看找到的founction 找到有关权限检查 authority check
  • scscanner:一款功能强大的大规模状态码扫描工具

    关于scscanner scscanner是一款功能强大的大规模状态码扫描工具 该工具可以帮助广大研究人员从一个URL列表文件中批量读取目标网站的状态码响应信息 除此之外 该工具还可以过滤出指定的状态码 并将结果存储到一个文件中以供后续深入
  • neon常用指令(updating)

    函数参考手册 https developer arm com architectures instruction sets simd isas neon intrinsics 并在左侧选择neon Neon 128bit寄存器 所以可支持并
  • mysql将一张表插入到另一张表

    1 表1和表2的字段完全相同且要把表2所有的数据都插入到表1 可以用这种方法 insert into table1 select from table2 2 但是更多的时候我们只希望导入指定字段 可以用这种方法 insert into ta
  • 冯思远:Apache TVM 与机器学习编译发展

    下午好 欢迎大家今天来参加 2023 Meet TVM 作为 Apache TVM PMC 由我来给大家做关于 TVM 的发展以及 TVM 未来 Unity 框架的分享 Apache TVM Evolution 首先为什么会有 MLC Ma
  • 双指针算法模板及例题

    双指针算法时间复杂度O n 一般双指针算法运用于有序的某一个或两个序列中 从O n2 优化到O n 算法模板 for int i 0 j 0 i lt n i while j lt i check i j j 具体问题的逻辑 常见问题分类
  • stm32 mpu6050 cubemx 卡尔曼滤波法读取角度

    文章目录 前言 一 cubemx配置 二 mpu6050文件移植 mpu6050 c mpu6050 h 三 主函数 前言 本文简述使用mpu6050读取原始数据后解算出角度 网上大多都是dmp库来解算 但是这种情况操作起来相对复杂 更方便