目录

  • 传感器的方向
  • 源码
    • Madgwick_9.c
    • Madgwick_9.h
  • 使用方法
  • 测试
    • main.c
    • 效果

STC15F2K60S2 22.1184MHz
Keil uVision V5.29.0.0
PK51 Prof.Developers Kit Version:9.60.0.0
上位机:Vofa+ 1.3.10


移植自AHRS —— LOXO,算法作者:SOH Madgwick

传感器的方向

源码

所用MCU为STC15F2K60S2 使用内部RC时钟,22.1184MHz

stdint.h见【51单片机快速入门指南】1:基础知识和工程创建
       软件I2C程序见【51单片机快速入门指南】4: 软件 I2C
       串口部分见【51单片机快速入门指南】3.3:USART 串口通信
       MPU6050驱动程序见【51单片机快速入门指南】4.3: I2C读取MPU6050陀螺仪的原始数据
       HMC5883L/QMC5883L驱动程序见【51单片机快速入门指南】4.4:I2C 读取HMC5883L / QMC5883L 磁力计
       磁力计的椭球拟合校准见【51单片机快速入门指南】4.4.1:python串口接收磁力计数据并进行最小二乘法椭球拟合

beta要按需调整,我这里取1.0

Madgwick_9.c

//=====================================================================================================
//
// Implementation of Madgwick's IMU and AHRS algorithms.
// See: http://www.x-io.co.uk/node/8#open_source_ahrs_and_imu_algorithms
//
// Date         Author          Notes
// 29/09/2011   SOH Madgwick    Initial release
// 02/10/2011   SOH Madgwick    Optimised for reduced CPU load
// 19/02/2012   SOH Madgwick    Magnetometer measurement is normalised
//
//=====================================================================================================//---------------------------------------------------------------------------------------------------
// Header files
#include <math.h>
#include "MPU6050.h"//---------------------------------------------------------------------------------------------------
// Definitions#define beta  1.0f                                        // 2 * proportional gain (Kp)//---------------------------------------------------------------------------------------------------
// Variable definitionsfloat q0 = 1.0f, q1 = 0.0f, q2 = 0.0f, q3 = 0.0f;    // quaternion of sensor frame relative to auxiliary frame
float Pitch = 0.0f, Roll = 0.0f, Yaw = 0.0f;//====================================================================================================
// Functionsfloat sampleFreq = 1;
float GYRO_K = 1;void MPU6050_Madgwick_Init(float loop_ms)
{sampleFreq = 1000. / loop_ms; //sample frequency in Hzswitch((MPU_Read_Byte(MPU_GYRO_CFG_REG) >> 3) & 3){case 0:GYRO_K = 1./131/57.3;break;case 1:GYRO_K = 1./65.5/57.3;break;case 2:GYRO_K = 1./32.8/57.3;break;case 3:GYRO_K = 1./16.4/57.3;break;}
}//---------------------------------------------------------------------------------------------------
// Fast inverse square-root
// See: http://en.wikipedia.org/wiki/Fast_inverse_square_rootfloat invSqrt(float x)
{float halfx = 0.5f * x;float y = x;long i = *(long*)&y;i = 0x5f3759df - (i>>1);y = *(float*)&i;y = y * (1.5f - (halfx * y * y));return y;
}//---------------------------------------------------------------------------------------------------
// AHRS algorithm update
//---------------------------------------------------------------------------------------------------
// IMU algorithm updatevoid MadgwickAHRSupdate_6(float gx, float gy, float gz, float ax, float ay, float az)
{float recipNorm;float s0, s1, s2, s3;float qDot1, qDot2, qDot3, qDot4;float _2q0, _2q1, _2q2, _2q3, _4q0, _4q1, _4q2 ,_8q1, _8q2, q0q0, q1q1, q2q2, q3q3;//将陀螺仪AD值转换为 弧度/sgx = gx * GYRO_K;gy = gy * GYRO_K;gz = gz * GYRO_K;// Rate of change of quaternion from gyroscopeqDot1 = 0.5f * (-q1 * gx - q2 * gy - q3 * gz);qDot2 = 0.5f * (q0 * gx + q2 * gz - q3 * gy);qDot3 = 0.5f * (q0 * gy - q1 * gz + q3 * gx);qDot4 = 0.5f * (q0 * gz + q1 * gy - q2 * gx);// Compute feedback only if accelerometer measurement valid (avoids NaN in accelerometer normalisation)if(!((ax == 0.0f) && (ay == 0.0f) && (az == 0.0f))) {// Normalise accelerometer measurementrecipNorm = invSqrt(ax * ax + ay * ay + az * az);ax *= recipNorm;ay *= recipNorm;az *= recipNorm;   // Auxiliary variables to avoid repeated arithmetic_2q0 = 2.0f * q0;_2q1 = 2.0f * q1;_2q2 = 2.0f * q2;_2q3 = 2.0f * q3;_4q0 = 4.0f * q0;_4q1 = 4.0f * q1;_4q2 = 4.0f * q2;_8q1 = 8.0f * q1;_8q2 = 8.0f * q2;q0q0 = q0 * q0;q1q1 = q1 * q1;q2q2 = q2 * q2;q3q3 = q3 * q3;// Gradient decent algorithm corrective steps0 = _4q0 * q2q2 + _2q2 * ax + _4q0 * q1q1 - _2q1 * ay;s1 = _4q1 * q3q3 - _2q3 * ax + 4.0f * q0q0 * q1 - _2q0 * ay - _4q1 + _8q1 * q1q1 + _8q1 * q2q2 + _4q1 * az;s2 = 4.0f * q0q0 * q2 + _2q0 * ax + _4q2 * q3q3 - _2q3 * ay - _4q2 + _8q2 * q1q1 + _8q2 * q2q2 + _4q2 * az;s3 = 4.0f * q1q1 * q3 - _2q1 * ax + 4.0f * q2q2 * q3 - _2q2 * ay;recipNorm = invSqrt(s0 * s0 + s1 * s1 + s2 * s2 + s3 * s3); // normalise step magnitudes0 *= recipNorm;s1 *= recipNorm;s2 *= recipNorm;s3 *= recipNorm;// Apply feedback stepqDot1 -= beta * s0;qDot2 -= beta * s1;qDot3 -= beta * s2;qDot4 -= beta * s3;}// Integrate rate of change of quaternion to yield quaternionq0 += qDot1 * (1.0f / sampleFreq);q1 += qDot2 * (1.0f / sampleFreq);q2 += qDot3 * (1.0f / sampleFreq);q3 += qDot4 * (1.0f / sampleFreq);// Normalise quaternionrecipNorm = invSqrt(q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3);q0 *= recipNorm;q1 *= recipNorm;q2 *= recipNorm;q3 *= recipNorm;Pitch = asin(-2.0f * (q1*q3 - q0*q2))* 57.3f;Roll = atan2(q0*q1 + q2*q3, 0.5f - q1*q1 - q2*q2) * 57.3f;Yaw = atan2(q1*q2 + q0*q3, 0.5f - q2*q2 - q3*q3)* 57.3f;
}void MadgwickAHRSupdate_9(float gx, float gy, float gz, float ax, float ay, float az, float mx, float my, float mz)
{float recipNorm;float s0, s1, s2, s3;float qDot1, qDot2, qDot3, qDot4;float hx, hy;float _2q0mx, _2q0my, _2q0mz, _2q1mx, _2bx, _2bz, _4bx, _4bz, _2q0, _2q1, _2q2, _2q3, _2q0q2, _2q2q3, q0q0, q0q1, q0q2, q0q3, q1q1, q1q2, q1q3, q2q2, q2q3, q3q3;// Use IMU algorithm if magnetometer measurement invalid (avoids NaN in magnetometer normalisation)if((mx == 0.0f) && (my == 0.0f) && (mz == 0.0f)) {MadgwickAHRSupdate_6(gx, gy, gz, ax, ay, az);return;}//将陀螺仪AD值转换为 弧度/sgx = gx * GYRO_K;gy = gy * GYRO_K;gz = gz * GYRO_K;// Rate of change of quaternion from gyroscopeqDot1 = 0.5f * (-q1 * gx - q2 * gy - q3 * gz);qDot2 = 0.5f * (q0 * gx + q2 * gz - q3 * gy);qDot3 = 0.5f * (q0 * gy - q1 * gz + q3 * gx);qDot4 = 0.5f * (q0 * gz + q1 * gy - q2 * gx);// Compute feedback only if accelerometer measurement valid (avoids NaN in accelerometer normalisation)if(!((ax == 0.0f) && (ay == 0.0f) && (az == 0.0f))) {// Normalise accelerometer measurementrecipNorm = invSqrt(ax * ax + ay * ay + az * az);ax *= recipNorm;ay *= recipNorm;az *= recipNorm;   // Normalise magnetometer measurementrecipNorm = invSqrt(mx * mx + my * my + mz * mz);mx *= recipNorm;my *= recipNorm;mz *= recipNorm;// Auxiliary variables to avoid repeated arithmetic_2q0mx = 2.0f * q0 * mx;_2q0my = 2.0f * q0 * my;_2q0mz = 2.0f * q0 * mz;_2q1mx = 2.0f * q1 * mx;_2q0 = 2.0f * q0;_2q1 = 2.0f * q1;_2q2 = 2.0f * q2;_2q3 = 2.0f * q3;_2q0q2 = 2.0f * q0 * q2;_2q2q3 = 2.0f * q2 * q3;q0q0 = q0 * q0;q0q1 = q0 * q1;q0q2 = q0 * q2;q0q3 = q0 * q3;q1q1 = q1 * q1;q1q2 = q1 * q2;q1q3 = q1 * q3;q2q2 = q2 * q2;q2q3 = q2 * q3;q3q3 = q3 * q3;// Reference direction of Earth's magnetic fieldhx = mx * q0q0 - _2q0my * q3 + _2q0mz * q2 + mx * q1q1 + _2q1 * my * q2 + _2q1 * mz * q3 - mx * q2q2 - mx * q3q3;hy = _2q0mx * q3 + my * q0q0 - _2q0mz * q1 + _2q1mx * q2 - my * q1q1 + my * q2q2 + _2q2 * mz * q3 - my * q3q3;_2bx = sqrt(hx * hx + hy * hy);_2bz = -_2q0mx * q2 + _2q0my * q1 + mz * q0q0 + _2q1mx * q3 - mz * q1q1 + _2q2 * my * q3 - mz * q2q2 + mz * q3q3;_4bx = 2.0f * _2bx;_4bz = 2.0f * _2bz;// Gradient decent algorithm corrective steps0 = -_2q2 * (2.0f * q1q3 - _2q0q2 - ax) + _2q1 * (2.0f * q0q1 + _2q2q3 - ay) - _2bz * q2 * (_2bx * (0.5f - q2q2 - q3q3) + _2bz * (q1q3 - q0q2) - mx) + (-_2bx * q3 + _2bz * q1) * (_2bx * (q1q2 - q0q3) + _2bz * (q0q1 + q2q3) - my) + _2bx * q2 * (_2bx * (q0q2 + q1q3) + _2bz * (0.5f - q1q1 - q2q2) - mz);s1 = _2q3 * (2.0f * q1q3 - _2q0q2 - ax) + _2q0 * (2.0f * q0q1 + _2q2q3 - ay) - 4.0f * q1 * (1 - 2.0f * q1q1 - 2.0f * q2q2 - az) + _2bz * q3 * (_2bx * (0.5f - q2q2 - q3q3) + _2bz * (q1q3 - q0q2) - mx) + (_2bx * q2 + _2bz * q0) * (_2bx * (q1q2 - q0q3) + _2bz * (q0q1 + q2q3) - my) + (_2bx * q3 - _4bz * q1) * (_2bx * (q0q2 + q1q3) + _2bz * (0.5f - q1q1 - q2q2) - mz);s2 = -_2q0 * (2.0f * q1q3 - _2q0q2 - ax) + _2q3 * (2.0f * q0q1 + _2q2q3 - ay) - 4.0f * q2 * (1 - 2.0f * q1q1 - 2.0f * q2q2 - az) + (-_4bx * q2 - _2bz * q0) * (_2bx * (0.5f - q2q2 - q3q3) + _2bz * (q1q3 - q0q2) - mx) + (_2bx * q1 + _2bz * q3) * (_2bx * (q1q2 - q0q3) + _2bz * (q0q1 + q2q3) - my) + (_2bx * q0 - _4bz * q2) * (_2bx * (q0q2 + q1q3) + _2bz * (0.5f - q1q1 - q2q2) - mz);s3 = _2q1 * (2.0f * q1q3 - _2q0q2 - ax) + _2q2 * (2.0f * q0q1 + _2q2q3 - ay) + (-_4bx * q3 + _2bz * q1) * (_2bx * (0.5f - q2q2 - q3q3) + _2bz * (q1q3 - q0q2) - mx) + (-_2bx * q0 + _2bz * q2) * (_2bx * (q1q2 - q0q3) + _2bz * (q0q1 + q2q3) - my) + _2bx * q1 * (_2bx * (q0q2 + q1q3) + _2bz * (0.5f - q1q1 - q2q2) - mz);recipNorm = invSqrt(s0 * s0 + s1 * s1 + s2 * s2 + s3 * s3); // normalise step magnitudes0 *= recipNorm;s1 *= recipNorm;s2 *= recipNorm;s3 *= recipNorm;// Apply feedback stepqDot1 -= beta * s0;qDot2 -= beta * s1;qDot3 -= beta * s2;qDot4 -= beta * s3;}// Integrate rate of change of quaternion to yield quaternionq0 += qDot1 * (1.0f / sampleFreq);q1 += qDot2 * (1.0f / sampleFreq);q2 += qDot3 * (1.0f / sampleFreq);q3 += qDot4 * (1.0f / sampleFreq);// Normalise quaternionrecipNorm = invSqrt(q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3);q0 *= recipNorm;q1 *= recipNorm;q2 *= recipNorm;q3 *= recipNorm;Pitch = asin(-2.0f * (q1*q3 - q0*q2))* 57.3f;Roll = atan2(q0*q1 + q2*q3, 0.5f - q1*q1 - q2*q2) * 57.3f;Yaw = atan2(q1*q2 + q0*q3, 0.5f - q2*q2 - q3*q3)* 57.3f;
}//====================================================================================================
// END OF CODE
//====================================================================================================

Madgwick_9.h

#ifndef Madgwick_9_H_
#define Madgwick_9_H_extern float Pitch, Roll, Yaw;
extern float q0, q1, q2, q3;void MPU6050_Madgwick_Init(float loop_ms);
void MadgwickAHRSupdate_6(float gx, float gy, float gz, float ax, float ay, float az);
void MadgwickAHRSupdate_9(float gx, float gy, float gz, float ax, float ay, float az, float mx, float my, float mz);#endif

使用方法

先调用MPU6050_Madgwick_Init(dt),参数为一次循环的时间,单位为ms
再使用MadgwickAHRSupdate_9姿态融合函数。

测试

陀螺仪、磁力计的原始数据经校准后输入MadgwickAHRSupdate_9函数

main.c

#include <STC15F2K60S2.H>
#include "intrins.h"
#include "stdint.h"
#include "USART.h"
#include "./Software_I2C/Software_I2C.h"
#include "XMC5883L.h"
#include "./MPU6050/MPU6050.h"
#include "./MPU6050/Madgwick_9.h"void Delay1ms()      //@22.1184MHz
{unsigned char i, j;_nop_();_nop_();i = 22;j = 128;do{while (--j);} while (--i);
}void delay_ms(uint32_t ms)
{while(ms --)Delay1ms();
}#define LED_PORT P0void main(void)
{int16_t mag_x, mag_y, mag_z;int16_t aacx,aacy,aacz;        //加速度传感器原始数据int16_t gyrox,gyroy,gyroz;  //陀螺仪原始数据MPU_Init();xmc5883lInit();AUXR &= 0xBF;       //定时器时钟12T模式 1T的51使用12T的定时器程序时需要加入这两句AUXR &= 0xFE;     //串口1选择定时器1为波特率发生器USART_Init(USART_MODE_1, Rx_ENABLE, STC_USART_Priority_Lowest, 22118400, 115200, DOUBLE_BAUD_ENABLE, USART_TIMER_1);MPU6050_Madgwick_Init(10.48);while(1){MPU_Get_Accelerometer(&aacx, &aacy, &aacz); //得到加速度传感器数据MPU_Get_Gyroscope(&gyrox, &gyroy, &gyroz);  //得到陀螺仪数据xmc5883lRead(&mag_x, &mag_y, &mag_z);MadgwickAHRSupdate_9(gyrox+7, gyroy+23, gyroz-1, aacx, aacy, aacz, 1.108270606866881 * (mag_x + 297.2882033958856), 0.9218994400020794 * (mag_y + 3088.0092054124193), 0.9871899380641738 * (mag_z + 782.925290575134));printf("%f, ", Pitch);printf("%f, ", Roll);printf("%f\r\n", Yaw);}
}

效果

【51单片机快速入门指南】4.4.3:Madgwick AHRS 九轴姿态融合获取四元数、欧拉角相关推荐

  1. 【51单片机快速入门指南】4.4.2:Mahony AHRS 九轴姿态融合获取四元数、欧拉角

    目录 传感器的方向 源码 Mahony_9.c Mahony_9.h 使用方法 测试 main.c 效果 STC15F2K60S2 22.1184MHz Keil uVision V5.29.0.0 ...

  2. 【51单片机快速入门指南】4.3.1: MPU6050调用DMP库获取四元数和欧拉角

    目录 相关介绍 DMP库相关 DMP加载步骤: DMP设置数据写入 更新DMP DMP数据包结构 程序实现 DMP.c DMP.h 测试程序 四元数 实验现象 欧拉角的获取 普中51-单核-A2 ST ...

  3. 【51单片机快速入门指南】6.4:DHT11、DHT22单总线温湿度传感器

    目录 硬知识 DHT11 DHT22 通信协议 读取步骤 数据解读 DHT11 DHT22 示例程序 DHT11_22.c DHT11_22.h 测试程序 main.c 实验现象 DHT11 DHT2 ...

  4. 【51单片机快速入门指南】4.6:I2C 与 PCF8563实时时钟日历芯片

    目录 硬知识 概述 特性 功能描述 报警功能模式 定时器模式 CLKOUT输出 复位低电压检测器和时钟监视器 低电压检测器和时钟监视器 寄存器结构 寄存器概述 BCD编码格式寄存器概述 Control ...

  5. 【51单片机快速入门指南】6.3:DS18B20 单总线数字温度计的多路读取

    目录 硬知识 DS18B20介绍 时序 初始化时序 写时序 读时序 命令 ROM 操作命令 ROM 搜索举例 存贮器操作命令 示例程序 DS18B20.c DS18B20.h 测试程序 定时器中断服务 ...

  6. 【51单片机快速入门指南】6.1:LCD1602的八线、四线控制及自定义符号,完美兼容Proteus仿真

    目录 硬知识 显示特性 接口定义 操作时序 写操作时序 读操作时序 寄存器 忙标志位BF 地址计数器(AC) 显示数据寄存器(DDRAM) CGROM CGRAM 指令 清屏指令 光标归位指令 进入模 ...

  7. 【51单片机快速入门指南】5.3:SPI控制晶联讯JLX12864G_08602 LCD屏幕

    目录 示例程序 JLX12864G_08602.c JLX12864G_08602.h JLX12864G_08602_Font.c JLX12864G_08602_Font.h 测试程序 main. ...

  8. 【51单片机快速入门指南】5.1:SPI与DS1302时钟芯片

    目录 硬知识 DS1302 简介 DS1302 使用 控制寄存器 日历/时钟寄存器 DS1302 的读写时序 电路设计 示例程序 DS1302.c DS1302.h 测试程序 main.c 实验现象 ...

  9. 【51单片机快速入门指南】4.5:I2C 与 TCA6416实现双向 IO 扩展

    目录 硬知识 IO 扩展芯片 TCA6416A TAC6416A 的寄存器 IO 输入寄存器 IO 输出寄存器 IO 反相寄存器 IO 方向寄存器 TCA6416A 的操作 TCA6416A 写数据 ...

最新文章

  1. sdr 软件_SDR 软件定义的无线电
  2. 我的世界java版怎么添加光影,《我的世界》中国版光影添加教程 国服怎么添加光影?...
  3. Linux(debian)的网络内核参数优化来提高服务器并发处理能力
  4. @scheduled 每30s 执行一次_全球首发5G神U麒麟820,荣耀30S卡位5G档位最强,售价2399起...
  5. asp.net 拦截html,关于c#:如何在-ASPNET-Core-中实现全局异常拦截
  6. 【LESS系列】简介和使用
  7. ssh连接服务器时特别慢的问题的解决方法
  8. Oracle 中运用rollup和cube实现汇总运算
  9. HTML标签嵌套到底怎样才算是规范?
  10. 【原神】元素反应机制
  11. 2017百度之星程序设计大赛 - 资格赛 1003
  12. 计算机英语ppt答辩,计算机专业毕业论文答辩(英文)详解.ppt
  13. 数据分析之数据质量分析
  14. Riverbed’s Modeler Academic
  15. 301转向代码大合集
  16. Redis【10】-Redis发布订阅
  17. volatile修饰变量java_volatile 关键字(修饰变量)
  18. 冒着被打风险,揭秘软件测试工程师面试套路和暗语
  19. 修改json字符串中某个key对应的value值
  20. SpringBoot项目No qualifying bean of type ‘×××Mapper‘ available:的错误解决

热门文章

  1. ExpandableListView 箭头靠右
  2. Server Develop (三) 多进程实现C/S
  3. mysql变量 exec_MySQL slave_exec_mode 参数说明
  4. 5187. 收集足够苹果的最小花园周长
  5. Mybatis—多表查询
  6. leetcode841. 钥匙和房间(bfs)
  7. 机器学习岗位太少_太多的东西要学习,很少的时间
  8. 测试驱动开发 测试前移_我如何以及为什么认为测试驱动开发值得我花时间
  9. 生存分析简介:Kaplan-Meier估计器
  10. 微信退款通知,退款回调数据解密.SHA256签名AEAD_AES_256_GCM解密