AHRS互补滤波(Mahony)算法及开源代码

2023-05-16

文章目录

    • 一、前言
    • 二、算法流程
    • 三、算法步骤
    • 四、算法难点
    • 五、开源源码
    • 参考文献

欢迎关注个人公众号:导航员学习札记

一、前言

AHRS(Attitude and heading reference system,也就是航姿参考系统。在互补滤波算法中传感器主要采用了IMU(陀螺仪、加速度计)和磁力计。

AHRS的基本思想是,在载体没有平移运动的情况下,通过加速度感知重力分量,可以计算出载体的俯仰和横滚;磁力计可以感知磁北方向,因此可以计算载体的磁北航向;而陀螺仪测量输出载体的旋转角速度,通过积分可以计算得到横滚、俯仰、航向增量,但由于陀螺输出值含有误差,采用积分计算,误差会随着时间累积。陀螺仪动态响应特性良好,但计算姿态时会产生累积误差, 磁力计和加速度计测量姿态没有累积误差,但动态响应较差,那么采用加速度计和磁力计的即时输出值对陀螺进行修正,则可以达到优势互补的效果,提高测量精度和系统的动态性能。

二、算法流程

整体流程如下图,只不过图里面的公式用的四元数做旋转,而不是旋转矩阵。
在这里插入图片描述

三、算法步骤

所用的导航系N(地理系)为北东地,机体系B为前右下。假设加速度计测量值为ax,ay,az,陀螺测量值为gx,gy,gz,磁力计测量值为mx,my,mz。

1. 初始化四元数
由于姿态角和四元数是可以相互计算的,因此可以首先根据已知的姿态,或者根据加速度计和磁力计计算出来的姿态初始化四元数,具体公式如下。
在这里插入图片描述

2. 计算加速度计修正量
由四元数表示的从导航系到机体系的旋转矩阵为:
在这里插入图片描述
由于我用的导航系N是北东地,机体系为前右下,如果加速度计坐标系与机体系一致,载体静止且水平时加速度计测量值应为[0,0,-1],Z方向为负(由于加速度计测的实际是1g的支持力,而非重力)。利用Cnb矩阵将地球矢量转到机体系前右下,得到vx, vy, vz。
在这里插入图片描述
将vx,vy,vz与实际加速度计输出(位于机体系中)求向量积,即为误差修正量:
在这里插入图片描述

3. 计算磁力计修正量
如果不考虑误差,磁力计的测量值经过正确Cbn转到地理系,理论上水平方向上只有北向有值,因此地球磁场的切线在南北方向上。但实际上由于航向不准,造成Cbn有误差,所以转换后的磁力计测量值在北向和东向都有值。

互补滤波算法中,先将磁力计的值用Cbn转到地理系:
在这里插入图片描述
由于在水平方向,无论Cbn在航向上有没有误差,转换后水平方向矢量和应该相等。因此可以得到如果航向正确的情况下,通过Cbn转换后的磁力计为[bx, 0, bz] = [sqrt(hx * hx + hy * hy), 0, hz],再将这个值转到机体系下:
在这里插入图片描述
求向量积:
在这里插入图片描述

4. 修正陀螺仪输出值
根据pi调节,设置对陀螺测量值的修正量:
在这里插入图片描述
对陀螺仪测量值进行修正:
在这里插入图片描述
后面就按照正常四元数更新算法进行计算。

四、算法难点

我感觉难点在于怎么设置kp,ki这两个值,可能需要比较多的PID调节的经验和测试数据。

在参考【1】中提到:“关于这一块,现在研究的比较多就是如何实现自适应调参。固定的参数不能获得所有情况下的最优运动姿态角,可以设计参数可调的自适应算法在不同运动状态下进行调节参数的大小。其参数调节规则为:正常运动状态情况下,Kp和Ki值取为系统初始化值;当运动体具有较大运动加速度或姿态变化剧烈时,应选择较小的Kp值(可取其初始化值的0.1倍),而Ki值应在同一数量级内适当取大一点。具体取值需根据实际应用系统选取。”

严恭敏老师的博客最简单的航姿仪算法C程序(AHRS)中设置的kp,ki值为:

void MahonyInit(float tau)
{
 float beta = 2.146f/tau;
 Kp = 2.0f*beta, Ki = beta*beta;
 q0 = 1.0f, q1 = q2 = q3 = 0.0f;
 qua2cnb();
 exInt = eyInt = ezInt = 0.0f;
 tk = 0.0f;
}

Aceinna开源代码中设置的kp,ki值为:

 def __init__(self):
        '''
        vars
        '''
        # algorithm description
        self.input = ['fs', 'gyro', 'accel']#, 'mag']
        self.output = ['att_quat', 'wb', 'ab']
        self.batch = True
        self.results = None
        self.quat = None
        self.wb = None
        self.ab = None
        # algorithm vars
        # config
        self.innovationLimit = 0.1
        self.kp_acc_high = 1
        self.kp_acc_low = 0.01
        self.ki_acc_high = 0.5
        self.ki_acc_low = 0.001
        # state
        self.ini = 0                                # indicate if attitude is initialized
        self.dt = 1.0                               # sample period, sec
        self.q = np.array([1.0, 0.0, 0.0, 0.0])     # quaternion
        self.err_int = np.array([0.0, 0.0, 0.0])    # integral of error
        self.kp_acc = 1
        self.ki_acc = 0.001
        self.gyro_bias = np.array([0.0, 0.0, 0.0])
        self.tmp = np.array([0.0, 0.0, 0.0])

五、开源源码

下面是x-IMU关于互补滤波的开源代码(具体参见Open-Source-AHRS-With-x-IMU)因为使用的坐标系不同,所以重力加速度分量的正负不太一样:

        public MahonyAHRS(float samplePeriod, float kp, float ki)
        {
            SamplePeriod = samplePeriod;
            Kp = kp;
            Ki = ki;
            Quaternion = new float[] { 1f, 0f, 0f, 0f };
            eInt = new float[] { 0f, 0f, 0f };
        }
        
		public void Update(float gx, float gy, float gz, float ax, float ay, float az, float mx, float my, float mz)
        {
            float q1 = Quaternion[0], q2 = Quaternion[1], q3 = Quaternion[2], q4 = Quaternion[3];   // short name local variable for readability
            float norm;
            float hx, hy, bx, bz;
            float vx, vy, vz, wx, wy, wz;
            float ex, ey, ez;
            float pa, pb, pc;

            // Auxiliary variables to avoid repeated arithmetic
            float q1q1 = q1 * q1;
            float q1q2 = q1 * q2;
            float q1q3 = q1 * q3;
            float q1q4 = q1 * q4;
            float q2q2 = q2 * q2;
            float q2q3 = q2 * q3;
            float q2q4 = q2 * q4;
            float q3q3 = q3 * q3;
            float q3q4 = q3 * q4;
            float q4q4 = q4 * q4;   

            // Normalise accelerometer measurement
            norm = (float)Math.Sqrt(ax * ax + ay * ay + az * az);
            if (norm == 0f) return; // handle NaN
            norm = 1 / norm;        // use reciprocal for division
            ax *= norm;
            ay *= norm;
            az *= norm;

            // Normalise magnetometer measurement
            norm = (float)Math.Sqrt(mx * mx + my * my + mz * mz);
            if (norm == 0f) return; // handle NaN
            norm = 1 / norm;        // use reciprocal for division
            mx *= norm;
            my *= norm;
            mz *= norm;

            // Reference direction of Earth's magnetic field
            hx = 2f * mx * (0.5f - q3q3 - q4q4) + 2f * my * (q2q3 - q1q4) + 2f * mz * (q2q4 + q1q3);
            hy = 2f * mx * (q2q3 + q1q4) + 2f * my * (0.5f - q2q2 - q4q4) + 2f * mz * (q3q4 - q1q2);
            bx = (float)Math.Sqrt((hx * hx) + (hy * hy));
            bz = 2f * mx * (q2q4 - q1q3) + 2f * my * (q3q4 + q1q2) + 2f * mz * (0.5f - q2q2 - q3q3);

            // Estimated direction of gravity and magnetic field
            vx = 2f * (q2q4 - q1q3);
            vy = 2f * (q1q2 + q3q4);
            vz = q1q1 - q2q2 - q3q3 + q4q4;
            wx = 2f * bx * (0.5f - q3q3 - q4q4) + 2f * bz * (q2q4 - q1q3);
            wy = 2f * bx * (q2q3 - q1q4) + 2f * bz * (q1q2 + q3q4);
            wz = 2f * bx * (q1q3 + q2q4) + 2f * bz * (0.5f - q2q2 - q3q3);  

            // Error is cross product between estimated direction and measured direction of gravity
            ex = (ay * vz - az * vy) + (my * wz - mz * wy);
            ey = (az * vx - ax * vz) + (mz * wx - mx * wz);
            ez = (ax * vy - ay * vx) + (mx * wy - my * wx);
            if (Ki > 0f)
            {
                eInt[0] += ex;      // accumulate integral error
                eInt[1] += ey;
                eInt[2] += ez;
            }
            else
            {
                eInt[0] = 0.0f;     // prevent integral wind up
                eInt[1] = 0.0f;
                eInt[2] = 0.0f;
            }

            // Apply feedback terms
            gx = gx + Kp * ex + Ki * eInt[0];
            gy = gy + Kp * ey + Ki * eInt[1];
            gz = gz + Kp * ez + Ki * eInt[2];

            // Integrate rate of change of quaternion
            pa = q2;
            pb = q3;
            pc = q4;
            q1 = q1 + (-q2 * gx - q3 * gy - q4 * gz) * (0.5f * SamplePeriod);
            q2 = pa + (q1 * gx + pb * gz - pc * gy) * (0.5f * SamplePeriod);
            q3 = pb + (q1 * gy - pa * gz + pc * gx) * (0.5f * SamplePeriod);
            q4 = pc + (q1 * gz + pa * gy - pb * gx) * (0.5f * SamplePeriod);

            // Normalise quaternion
            norm = (float)Math.Sqrt(q1 * q1 + q2 * q2 + q3 * q3 + q4 * q4);
            norm = 1.0f / norm;
            Quaternion[0] = q1 * norm;
            Quaternion[1] = q2 * norm;
            Quaternion[2] = q3 * norm;
            Quaternion[3] = q4 * norm;
        }

参考文献

【1】基于AHRS的三类姿态解算算法对比(含代码)-基于手机端惯性传感器的航迹推算算法(下)
【2】Pixhawk-姿态解算-互补滤波
【3】Pixhawk之姿态解算篇(4)_补充篇
【4】最简单的航姿仪算法C程序(AHRS)
【5】Open-Source-AHRS-With-x-IMU
【6】Aceinna gnss_ins-sim
【7】PID控制算法原理(抛弃公式,从本质上真正理解PID控制)

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

AHRS互补滤波(Mahony)算法及开源代码 的相关文章

  • Ubuntu如何切换Python版本

    这几天一直在搞小米官方提供的ESP32 WiFi SDK xff0c 过程中遇到了很多坑 xff0c 其中包括Python版本兼容的问题 xff0c 我的Ubuntu 上安装的Python版本是Python3 xff0c 而脚本的使用的是p
  • C++面试宝典:__FILE__,__func__,__LINE__

    C语言中 xff0c FILE xff0c func xff0c LINE 常用于logout xff0c debug调试 注意 xff1a 其使用不需要定义 xff0c FILE 指示当前文件名 xff0c func 指示当前函数名 xf
  • C++面试宝典:头文件引用的顺序

    头文件引用的顺序 当我们有多个头文件的时候 xff0c 特定情况下要注意引用的顺序 如果要在文件a h中声明一个在文件b h中定义的变量 xff0c 而不引用b h 那么要在a cpp文件中引用b h文件 xff0c 并且要先引用b h x
  • c++部署yolov5模型

    C 43 43 部署yolov5模型 前言一 准备模型二 Fastdeploy准备三 调用总结 前言 不可否认 xff0c yolov5在目标检测方面大杀四方 xff0c 在 SOTA 榜上留下过万众瞩目的成绩 xff0c 但是官网代码给的
  • 【信息技术】【2004】基于计算机视觉的无人机自主避障系统中的目标跟踪研究

    本文为瑞典皇家理工学院 xff08 作者 xff1a Johan Driessen xff09 的硕士论文 xff0c 共68页 这篇硕士论文的目的是研究利用商用货架 COTS 硬件和免费 公开可用的计算机视觉库开发一个足够有效的实时自主避
  • 将寄存器地址设为宏对寄存器的值进行操作

    将寄存器地址设为宏 对寄存器的值进行操作 搞了半天才捋清楚 首先 定义一个指针变量 int p 再定义一个变量 int q 61 1 将指针p指向q xff1a p 61 amp q 通过指针对改变q的值 xff1a p 61 0x10 此
  • STM32单片机的八种IO口模式,你应该了解下

    STM32单片机的八种IO口模式 xff0c 你应该了解下 八种IO口模式 STM32有八种IO口模式 xff0c 分别是 xff1a 模拟输入 浮空输入 上拉输入 下拉输入 开漏输出 推挽输出 复用开漏输出和复用推挽输出 1 模拟输入 G
  • C语言实现文件复制的两种方法

    一 使用fread 函数和fwrite 函数 span class token macro property span class token directive keyword define span CRT SECURE NO WARN
  • C++开发模板化动态数组CArray类

    1 头文件 span class token comment Array h span span class token macro property span class token directive keyword pragma sp
  • C语言网络族函数htonl()、htons()、inet_addr()、inet_ntoa()实现

    1 htonl htons 函数实现 1 htonl 将主机数转换成无符号长整型的网络字节顺序 2 htons 将主机数转换成无符号短整型的网络字节顺序 span class token macro property span class
  • C语言实现Windows下的socket编程

    一 UDP 数据报 协议 UDP User Datagram Protocol的简称 xff0c 中文名是用户数据报协议 xff0c 是OSI Open System Interconnection xff0c 开放式系统互联 参考模型中一
  • 数据结构-用栈实现表达式求值

    参照严蔚敏 lt lt 数据结构 gt gt 第2版算法3 22 当输入 时代表表示式结束 算法实现 xff1a span class token macro property span class token directive keyw
  • 数据结构-利用二叉树求解表达式的值

    参照严蔚敏 lt lt 数据结构 gt gt 第2版算法5 12和5 13 当输入 时代表表示式结束 算法实现 span class token macro property span class token directive keywo
  • 数据结构-基于哈夫曼树的数据压缩算法

    参照严蔚敏教材 lt lt 数据结构 gt gt 第2版 描述 输入一串字符串 xff0c 根据给定的字符串中字符出现的频率建立相应哈夫曼树 xff0c 构造哈夫曼编码表 xff0c 在此基础上可以对待压缩文件进行压缩 xff08 即编码
  • VINS-Mono代码阅读笔记:feature_tracker代码阅读(转载)

    转载 xff1a https blog csdn net moyu123456789 article details 100988989 1 入口main函数 feature tracker结点的入口函数为feature tracker n
  • CMake:C/C++和Fortran混合编译

    C C 43 43 和Fortran混合编译构建 使用CMake构建C C 43 43 和Fortran混合项目 Fortran调用C C 43 43 函数 main F90 program main use iso c binding i
  • stm32 FreeRTOS中如何创建任务

    include 34 config h 34 include 34 global h 34 include 34 stdio h 34 include 34 PC h 34 include 34 FreeRTOS h 34 include
  • 串口HAL库函数

    HAL StatusTypeDef HAL UART Transmit UART HandleTypeDef huart uint8 t pData uint16 t Size uint32 t Timeout 串口发送 xff1b 发送指
  • KEIL 那些编辑技巧与方法

    来源 xff1a 公众号 鱼鹰谈单片机 作者 xff1a 鱼鹰Osprey ID xff1a emOsprey 本篇笔记介绍一些鱼鹰常用的 KEIL 编辑方法与技巧 xff0c 用于加快编辑速度 当然了 xff0c 很多人现在更多的是使用
  • PotPlayer优化与最高画质设置(最强本地播放器)

    一 前言 软件 xff1a PotPlayer 描述 xff1a 被誉为本地视频最好用的播放器 xff01 PotPlayer下载地址参考 xff1a https potplayer org 推荐Potplayer论坛 xff1a http

随机推荐

  • Arduino结构体变量使用

    Arduino结构体变量使用 x1f4dd 示例程序 span class token comment 本文使用arduino nano span span class token comment 声明 B span span class
  • 51单片机自定义串口通讯协议控制流水灯+Proteus仿真

    51单片机自定义串口通讯协议控制流水灯 Proteus仿真 Proteus仿真演示 注意不要使用Proteus 8 Professional 8 13版本串口通信会出错 需要利用虚拟串口工具提前创建2个虚拟串口 Proteus里面AT89C
  • Arduino struct结构体定义和使用方法详解

    Arduino struct结构体定义和使用方法 1 直接使用struct定义 示例 span class token keyword struct span span class token class name People span
  • 锂电池基于DW01组成的过充电、过放、短路保护电路

    锂电池基于DW01组成的过充电 过放 短路保护电路 原理图 该电路主要由锂电池保护专用集成电路 xff24 xff37 xff10 xff11 xff0c 充 放电控制MOSFET xff08 内含两只 xff2e 沟道 xff2d xff
  • STM32F103基于标准库开发串口中断接收数据环形队列例程

    STM32F103基于标准库开发串口中断接收数据环形队列例程 本示例源码来源于野火 STM32库开发实战指南 xff0c 是一个值得学习借鉴的资源 x1f4d1 一个完整的串口数据包通讯协议一般包含 xff1a 帧头 地址信息 数据类型 数
  • 基于STM32CubeIDE HAL库利用基本定时器实现串口接收不定长数据

    基于STM32CubeIDE HAL库利用基本定时器实现串口接收不定长数据 申明 xff1a 本文章仅发表在CSDN网站 xff0c 任何其他网见此内容均为盗链和爬取 xff0c 请多多尊重和支持原创 x1f341 对于文中所提供的相关资源
  • idea重构手法

    idea重构手法 四键齐发 xff1a ctrl 43 alt 43 shift 43 T 修改方法名 xff1a shift 43 F6修改方法参数 xff1a Ctrl 43 F6提取常量 xff1a Ctrl 43 Alt 43 C提
  • Linux下实现http的Get方法

    Linux如何实现http的GET数据方法 下载curl库源码 https curl se download html Linux编译 make拷贝库文件 xff0c 目录 curl 7 83 0 lib libs 下 libcurl so
  • 一篇关于GPS定位写得最详实清晰的文章之一

    一篇关于GPS定位写得最详实清晰的文章之一 介绍篇 过去 xff0c 如果你的女友是个路痴 xff0c 大概会有这样的对话 你在哪儿呢 xff1f 啊 xff1f 我在马路上啊 有什么特征 xff1f 头顶有个月亮 你旁边有什么啊 xff1
  • 基于HAL库STM32串口驱动不定长数据接收

    STM32串口驱动不定长数据接收带环形缓冲区 最新框架代码使用方法源码串口接口文件环形缓冲区接口文件 移植图示 使用涉及4个文件 xff0c UART Port c UART Port h CircularQueue h CircularQ
  • OptiTrack---Motive简单使用导出groundtruth

    文章目录 Motive介绍1 详细介绍 Motive使用1 详细使用2 简单使用导出groundtruth 1 首先安装Motive 2 启动Motive 3 建立body xff0c 进行录制 4 对结果进行保存 Motive介绍 1 详
  • 使用U盘安装Ubuntu20.04

    背景 今天自己鼓捣小电脑 xff0c 卖家发过来的时候已经按要求预装了Ubuntu20 04 xff0c 我想改一下卖家起的用户名 也许是计算机名 xff0c 分不太清 xff0c 结果搞的电脑输入密码却进不了桌面 xff0c 最终决定重装
  • 【ROS基础】rviz打开后如何显示实时2D地图

    1 背景 launch 了一个建图程序 xff0c 并打开了 rviz xff0c rviz 中也 add 了 map xff0c 但是 rviz 中并未出现期望的2D地图 xff0c 让人很是手足无措 2 问题解决 百度了才发现自己使用的
  • RTKlib源码解析:ppp和rtkpost中的周跳检测函数

    文章目录 前言detslp mwdetslp gfdetslp lldetslp dop 欢迎关注个人公众号 xff1a 导航员学习札记 前言 本文解析了RTKlib ppp c中两个周跳检测函数detslp mw和detslp gf xf
  • RTKlib相对定位源码解析:resamb_LAMBDA (整周模糊度求解)

    本文对resamb LAMBDA函数 xff0c 以及其中的ddmat restamb函数进行了解析 由于其中的lambda函数在参考论文中都给出了详细推导和计算步骤 xff0c 因此没有解析 lambda函数参考论文 xff1a 1 P
  • RTKlib PPP代码解析

    文章目录 ppposudstate pppudbias pppcorr measppp res 欢迎关注个人公众号 xff1a 导航员学习札记 我所基于的代码版本是RTKlib 2 4 3的一个拓展版本RTKexplore Demo5 xf
  • Android 动态修改SeekBar滑块和进度条的颜色

    方法一 1 需求 xff1a 需要改变其默认颜色 xff0c 样式 2 滑竿样式 seekbar xml lt xml version 61 34 1 0 34 encoding 61 34 utf 8 34 gt lt layer lis
  • GNSS定位(SPP、RTK、PPP)位置坐标系

    欢迎关注个人公众号 xff1a 导航员学习札记 文章目录 一 前言二 单点定位三 差分定位四 PPP 一 前言 最近研究不同FTP的基站数据 xff0c 发现它们坐标系都不一致 xff0c 因此研究了下GNSS定位结果的坐标系 参考了一些文
  • detrend去趋势函数的Matlab、Python与C实现

    文章目录 趋势分量对频域分析的影响detrend去趋势函数 xff08 Matlab Python xff09 detrend的C语言实现 趋势分量对频域分析的影响 在对信号做频域分析时 xff0c 如果有趋势项的存在 xff0c 会对分析
  • AHRS互补滤波(Mahony)算法及开源代码

    文章目录 一 前言二 算法流程三 算法步骤四 算法难点五 开源源码参考文献 欢迎关注个人公众号 xff1a 导航员学习札记 一 前言 AHRS Attitude and heading reference system xff0c 也就是航