深入浅出理解Allan方差分析方法

news2025/2/26 17:47:36

一、参考资料

深入浅出理解卡尔曼滤波

二、Allan方差分析方法

1. 引言

传统的误差指标往往是采用均值误差(反映整个误差序列有无宏观偏置)、标准差(反映整个误差序列的波动情况),以及均方根(RMS,可以认为是宏观偏置与波动情况的综合)。如下图所示,它们都是反映误差序列的整体情况的指标,其中含有短时间快速变化和长时间缓变的各个成份,无法细分出不同时间尺度上的误差波动情况
在这里插入图片描述

Allan方差是一个相对而言直观形象、朴实无华的概念,它当初是在实践中针对高精度时钟稳定性分析的需求而提出的。Allan方差适用于分析惯导(和时钟)看重长期稳定性的传感器,Allan方差曲线对中长期时间尺度上的误差特性有很强的表现力。Allan方差相比于普通的方差具有更好的描述长时间误差的优势,因此,Allan方差被用来当作IMU噪声标定的标准方法

2. Allan方差的概念

Allan方差,又称为阿伦方差阿兰方差,Allan方差法是20世纪60年代由美国国家标准局的David Allan提出的基于时域的分析方法。

设计Allan方差分析方法的目的,是为了实现这样一种分析:对惯性传感器误差的关注,主要体现在不确定性和稳定性,具体来说,是不同时间尺度的稳定性。例如,战术武器领域关心的是多少秒到多少分钟时间尺度的稳定性,潜水艇领域关心的是天、轴周、月时间尺度的稳定性。那么有没有什么方法,将不同时间尺度上的不确定性,分门别类的表现出来,Allan教授针对这个问题,提出了Allan方差分析方法。

Allan方差是时频分析惯性导航领域常用的一种误差分析方法,它有效地刻画了待研究的误差时间序列在不同时间尺度上的波动水平(不稳定性),并可根据不同时间尺度上的Allan方差值所构成的曲线的形状特征来辨识其中包含的随机过程模型。Allan方差分析方法对中长期的随机波动具有很强的表现力,它完全可以作为一个通用的时间序列分析工具来推广到其它应用领域。

在我们对惯性导航器件(陀螺和加速度计)进行误差分析时,常采用Allan方差分析方法。这是当初从时频领域(高精度时钟的频率稳定性)借鉴来的一种时间序列分析方法非常适合于对中长期波动(不稳定性)进行定量描述和分析,因此正是我们惯性器件误差分析所需要的。

3. Allan方差计算过程

Allan方差是将误差序列在某个指定的时间尺度上的波动情况进行了精确提取,其具体计算步骤如下:
在这里插入图片描述

Step 1 分块

按窗口长度 τ = ( n − 1 ) t 0 \tau=\left(\left.n-1\right.\right)t_0 τ=(n1)t0 将序列 y 分成 N c N_c Nc 块(clusters),每块包n个数据点,块间无重叠。

Step 2 块平均

分别计算各块内n个数据点的均值,记为: y ‾ k = 1 n ∑ i = ⁡ k k + n − 1 y i \begin{aligned}\overline{y}_k=\frac1n\sum_{i\operatorname{=}k}^{k+n-1}y_i\end{aligned} yk=n1i=kk+n1yi

Step 3 计算Allan方差

相邻块的平均值求差。
σ 2 ( τ ) = 1 2 ( N c − 1 ) ∑ k = 1 N c − 1 ( y ˉ k + 1 − y ˉ k ) 2 \begin{aligned}\sigma^2(\tau)=\frac{1}{2(N_c-1)}\sum_{k=1}^{N_c-1}(\bar{y}_{k+1}-\bar{y}_k)^2\end{aligned} σ2(τ)=2(Nc1)1k=1Nc1(yˉk+1yˉk)2

Step 4 统计RMS值

根据Step 3求得的差值,统计RMS值。

(可选)Step 5 绘制Allan方差曲线

改变块长度,重复1~3,并画出Allan标准差随块长度变化的双对数曲线,称为Allan方差曲线。从图中可知,每个时间块,对应一个Allan方差,将所有的不同块时间和对应的Allan方差连起来,即绘制成Allan标准曲线。那么,Allan方差分析就是分析Allan标准曲线。

4. Allan方差图读取误差系数

Allan方差法可用于5种随机误差的标定:

  • 量化噪声:误差系数为 Q Q Q,Allan方差双对数曲线上斜率为-1的线段延长线与t=1的交点的纵坐标读数为 3 T Q \frac{\sqrt{3}}{T}Q T3 Q
  • 角度随机游走:其误差系数 N N N,Allan方差双对数曲线上斜率为 − 1 / 2 -1/2 1/2的线段延长线与 t = 1 t=1 t=1交点的纵坐标读数即为 N T \frac{N}{\sqrt{T}} T N
  • 零偏不稳定性:其误差系数 B B B,Allan方差双对数曲线上斜率为0的线段延长线与t=1交点的纵坐标读数为 2 ln ⁡ 2 π B \sqrt{\frac{2\ln2}{\pi}}B π2ln2 B ,一般常取底部平坦区的最小值或取 t = 1 0 1 t=10^1 t=101 t = 1 0 2 t=10^2 t=102 处的值;
  • 角速率随机游走:其误差系数 K K K,斜率为1/2的线段延长线与 t = 1 t = 1 t=1交点的纵坐标读数为 K T / 3 \frac{K}{\sqrt{T/3}} T/3 K
  • 角速率斜坡:其误差系数 R R R,斜率为1的线段延长线与 t = 1 t=1 t=1交点的纵坐标读数为 R T 2 \frac{RT}{\sqrt{2}} 2 RT
    在这里插入图片描述

假设各种误差源统计独立,那总的艾伦方差为各种误差源之和,即将量化噪声的平方 σ Q σ_Q σQ、角度随机游走的平方 σ R A W σ_{RAW} σRAW、零偏不稳定性的平方 σ b i a s σ_{bias} σbias、角速率随机游走的平方 σ R R W σ_{RRW} σRRW、角速率斜坡的平方 σ R R σ_{RR} σRR的总和。
在这里插入图片描述

5. Allan方差分析

通过块内求平均,去除短时间的不确定性;通过相邻块间求差,去除长时间的不确定性。

如果我们只对某一时间尺度上的误差(即误差波动)感兴趣的话,那么比这个时间尺度更小的细节变化(短时间快速跳动)和比这个时间尺度更大的宏观变化(长时间缓慢漂移)就都不关心了,希望在我们的误差指标中都被消除掉。而Allan方差是这样做到的:

  1. 通过分块确定所要考察的时间块长度;

  2. 利用块内求平均的办法把短于块长度的那些快速变化成份(细节)都抹掉;

  3. 再利用相邻块求差的办法把长于两块长度的那些缓慢变化成份(宏观)都抹掉;

  4. 最后对差值序列统计其方值(这是处理任何随机样本的标准操作),这样统计出来的就是介于1倍块长度和2倍块长度这样一个很窄的时间尺度范围内的误差波动情况。

6. Allan方差曲线

如果我们对误差序列的各个时间尺度上的成份都感兴趣的话,可以将块长度由短到长,“扫描”一遍,得出一组Allan方差值,然后画个“Allan方差 vs. 块长度”的曲线,这样就能全面反映被研究的误差序列的特性了。具体的,实际上是“Allan方差的开方(Allan Deviation) vs. 块长度”的双对数曲线,以便在更大的范围上有更强的表现力。以下是一张经典的高精度陀螺的Allan方差示意图。
在这里插入图片描述
在这里插入图片描述

从Allan方差曲线上,我们可以根据曲线的形状特征来识别出不同类型的随机误差(即随机过程模型)并提取其参数。例如:斜率为-1/2的直线段代表白噪声。具体可参见IEEE制定的一个关于陀螺测试的技术标准中的附录C内容[1]。这个附录中还给出了一个从Allan方差曲线中分析陀螺的多种随机误差的案例(如上图)。但我想提醒大家,实际的Allan方差曲线往往只能表现出少数两三个主要误差类型,因为其它误差都被这几个主要误差给淹没了。而由于我们在工程上关心的恰恰也就是主要误差源,因此我们并不受此困扰。更多的使用Allan方差分析过程中的注意事项可参考西工大严恭敏老师的博文《Allan方差分析的使用要点》,写得非常精准务实。

7. Allan方差的应用

Allan方差分析方法并不局限于对惯性器件和时钟误差的分析,也可推广到其它误差序列分析;而且它也不局限于是对误差序列进行分析,也可应用于任何时间序列的分析(例如建筑形变、地质演化等)。

8. Allan方差与ROS

下面是读取bag包,并获取Allen方差的图片,会生成对应的Allen方差图。这里主要参考了allan_variance_ros 对应的C++文件。

sudo pip install allantools
#!/usr/bin/env python
import rospy
import sys
import allantools
import rosbag
import numpy as np
import csv
import rospkg
import os
import matplotlib.pyplot as plt  # only for plotting, not required for calculations
import math

def getRandomWalkSegment(tau,sigma):

    m = -0.5 # slope of random walk
    """""""""""""""""""""""""""""""""
    " Find point where slope = -0.5 "
    """""""""""""""""""""""""""""""""
    randomWalk = None
    i = 1
    idx = 1
    mindiff = 999
    logTau = -999
    while (logTau<0):
        logTau = math.log(tau[i],10) 
        logSigma = math.log(sigma[i],10)
        prevLogTau = math.log(tau[i-1],10)
        prevLogSigma = math.log(sigma[i-1],10)
        slope = (logSigma-prevLogSigma)/(logTau-prevLogTau)# 随机漫步的斜率
        diff = abs(slope-m)# 当前斜率与目标斜率的差值
        if (diff<mindiff):
            mindiff = diff# 更新最小差值
            idx = i
        i = i + 1

    """"""""""""""""""""""""""""""
    " Project line to tau = 10^0 "
    """"""""""""""""""""""""""""""
    x1 = math.log(tau[idx],10)# 当前点的横坐标
    y1 = math.log(sigma[idx],10)# 当前点的纵坐标
    x2 = 0
    y2 = m*(x2-x1)+y1

    return (pow(10,x1),pow(10,y1),pow(10,x2),pow(10,y2))

def getBiasInstabilityPoint(tau,sigma):
    i = 1
    while (i<tau.size):
        if (tau[i]>1) and ((sigma[i]-sigma[i-1])>0): # only check for tau > 10^0
            break
        i = i + 1
    return (tau[i],sigma[i])

def main(args):

    rospy.init_node('allan_variance_node')

    t0 = rospy.get_time()

    """"""""""""""
    " Parameters "
    """"""""""""""
    bagfile = rospy.get_param('~bagfile_path','~/data/static.bag')# 输入的bag文件路径
    topic = rospy.get_param('~imu_topic_name','/imu')# 输入的imu topic名称
    axis = rospy.get_param('~axis',0)# 输入的轴,0为x轴,1为y轴,2为z轴
    sampleRate = rospy.get_param('~sample_rate',100)# 输入的采样频率
    isDeltaType = rospy.get_param('~delta_measurement',False)# 是否是delta measurement
    numTau = rospy.get_param('~number_of_lags',1000)# 输入的tau数目
    resultsPath = rospy.get_param('~results_directory_path',None)# 输出的结果路径

    """"""""""""""""""""""""""
    " Results Directory Path "
    """"""""""""""""""""""""""
    if resultsPath is None:
        paths = rospkg.get_ros_paths()
        path = paths[1] # path to workspace's devel
        idx = path.find("ws/")# 找到路径
        workspacePath = path[0:(idx+3)]# 取到workspace的路径
        resultsPath = workspacePath + 'av_results/'# 结果输出路径

        if not os.path.isdir(resultsPath):
            os.mkdir(resultsPath)

    print("\nResults will be save in the following directory: \n\n\t %s\n"%resultsPath)

    """"""""""""""""""
    " Form Tau Array "
    """"""""""""""""""
    taus = [None]*numTau# 初始化tau数组

    cnt = 0;
    for i in np.linspace(-2.0, 5.0, num=numTau): # lags will span from 10^-2 to 10^5, log spaced
        taus[cnt] = pow(10,i)# 将tau数组赋值,维度在10^-2 到 10^5
        cnt = cnt + 1

    """""""""""""""""
    " Parse Bagfile "
    """""""""""""""""
    bag = rosbag.Bag(bagfile)

    N = bag.get_message_count(topic) # 在bag文件中找到该topic的消息数量

    data = np.zeros( (6,N) ) # 初始化数据矩阵,维度为6*N

    if isDeltaType:
        scale = sampleRate
    else:
        scale = 1.0

    cnt = 0
    for topic, msg, t in bag.read_messages(topics=[topic]):# 遍历bag文件中的消息
        data[0,cnt] = msg.linear_acceleration.x * scale
        data[1,cnt] = msg.linear_acceleration.y * scale
        data[2,cnt] = msg.linear_acceleration.z * scale
        data[3,cnt] = msg.angular_velocity.x * scale
        data[4,cnt] = msg.angular_velocity.y * scale
        data[5,cnt] = msg.angular_velocity.z * scale
        cnt = cnt + 1

    bag.close()

    print ("[%0.2f seconds] Bagfile parsed\n"%(rospy.get_time()-t0))

    """"""""""""""""""
    " Allan Variance "
    """"""""""""""""""
    if axis is 0:
        currentAxis = 1 # 循环通过所有轴1-6
    else:
        currentAxis = axis # 只需循环一次,然后中断

    while (currentAxis <= 6):
        # taus_used 对应了是否可以使用taus的数组,adev是allan deviation degree的缩写,为allan偏差;adev_err是allan偏差的误差;adev_norm是allan偏差的标准化;
        (taus_used, adev, adev_err, adev_n) = allantools.oadev(data[currentAxis-1], data_type='freq', rate=float(sampleRate), taus=np.array(taus) )# 计算allan variance

        randomWalkSegment = getRandomWalkSegment(taus_used,adev)# 计算random walk segment
        biasInstabilityPoint = getBiasInstabilityPoint(taus_used,adev)# 计算bias instability point

        randomWalk = randomWalkSegment[3]# 获取random walk segment的纵坐标
        biasInstability = biasInstabilityPoint[1]# 获取bias instability point的纵坐标

        """""""""""""""
        " Save as CSV "
        """""""""""""""
        if (currentAxis==1):
            fname = 'allan_accel_x'
            title = 'Allan Deviation: Accelerometer X'
        elif (currentAxis==2):
            fname = 'allan_accel_y'
            title = 'Allan Deviation: Accelerometer Y'
        elif (currentAxis==3):
            fname = 'allan_accel_z'
            title = 'Allan Deviation: Accelerometer Z'
        elif (currentAxis==4):
            fname = 'allan_gyro_x'
            title = 'Allan Deviation: Gyroscope X'
        elif (currentAxis==5):
            fname = 'allan_gyro_y'
            title = 'Allan Deviation: Gyroscope Y'
        elif (currentAxis==6):
            fname = 'allan_gyro_z'
            title = 'Allan Deviation: Gyroscope Z'

        print ("[%0.2f seconds] Finished calculating allan variance - writing results to %s"%(rospy.get_time()-t0,fname))

        f = open(resultsPath + fname + '.csv', 'wt')

        try:
            writer = csv.writer(f)
            writer.writerow( ('Random Walk', 'Bias Instability') )
            writer.writerow( (randomWalk, biasInstability) )
            writer.writerow( ('Tau', 'AllanDev', 'AllanDevError', 'AllanDevN') )
            for i in range(taus_used.size):
                writer.writerow( (taus_used[i],adev[i],adev_err[i],adev_n[i])  )
        finally:
            f.close()

        """""""""""""""
        " Plot Result "
        """""""""""""""
        plt.figure(figsize=(12,8))
        ax = plt.gca()
        ax.set_yscale('log')
        ax.set_xscale('log')

        plt.plot(taus_used,adev)
        plt.plot([randomWalkSegment[0],randomWalkSegment[2]],
                 [randomWalkSegment[1],randomWalkSegment[3]],'k--')
        plt.plot(1,randomWalk,'rx',markeredgewidth=2.5,markersize=14.0)
        plt.plot(biasInstabilityPoint[0],biasInstabilityPoint[1],'ro')

        plt.grid(True, which="both")
        plt.title(title)
        plt.xlabel('Tau (s)')
        plt.ylabel('ADEV')

        for item in ([ax.title, ax.xaxis.label, ax.yaxis.label] +
                    ax.get_xticklabels() + ax.get_yticklabels()):
            item.set_fontsize(20)

        plt.show(block=False)

        plt.savefig(resultsPath + fname)

        currentAxis = currentAxis + 1 + axis*6 # increment currentAxis also break if axis is not =0

    inp=input("Press Enter key to close figures and end program\n")

if __name__ == '__main__':
  main(sys.argv)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/966483.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于科大讯飞AIGC创作平台,构建数字人虚拟主播

笔者为体验目前数字人虚拟主播创作视频的质量&#xff0c;特意制作了一段测试视频。 基于讯飞智作创建 总体感受&#xff0c;数字人虚拟主播具有成本低、可定制性强等优点&#xff0c;但是也存在缺乏人情味、技术限制和法律问题等缺点。因此&#xff0c;在使用数字人虚拟主播时…

安装Ubuntu系统,将U盘当作启动盘后写保护怎么回复?

下载ChipGenius 插入写保护的U盘&#xff0c;打开ChipGenius.exe后可以扫描到U盘&#xff0c;如下图中的E:盘就是我插入的U盘&#xff08;我的PC上只有C、D两个分区&#xff09;&#xff1b; ChipGenius的作用 下载ChipGenius是为了获取U盘的设备信息&#xff1a;重点是主控…

思维的深度,决定职场的高度

经常有读者问我&#xff0c;自己做事很努力&#xff0c;可是结果却总是不尽如人意&#xff0c;问题究竟出在哪里&#xff1f; 虽然成事的关键因素有很多&#xff0c;但是归根结底其实只有两点&#xff0c;就是做局和破局。也就是&#xff0c;如何识破别人给你做的局&#xff1f…

与AI一起学习Anything:30%的人用ChatGPT编程

学习和工作在LLM时代&#xff0c;就是同一件事&#xff0c;在编程这个场景&#xff0c;我们看到了学习和工作高度重叠的可能。 近期&#xff0c;随着ChatGPT热度下降&#xff0c;一些比较稳定的使用场景开始浮出水面&#xff0c;例如编程&#xff0c;据调查数据显示&#xff0c…

yolov5模型转换

yolov5本身release目录有提供了onnx转换好的模型&#xff0c;想着也自己操作一遍&#xff0c;可是实际操作却遇到了问题&#xff0c;这里做下记录方便后续可能用到 安装onnx&#xff0c;转的时候提示出错ONNX: export failure 0.1s: Unsupported ONNX opset version: 17 修改…

【复盘】记录一次数据库连接超时问题

问题 在下午4点左右&#xff0c;发现系统响应不正常。没有将结果返回给上游系统。 问题排查 1.先查看了机器的CPU、内存是否正常。发现没有问题。 2.接着看系统Error日志&#xff0c;发现大量的数据库连接不成功。进而分析是不是可能和请求量增加有关系。发现果然是。将近…

【React】React学习:从初级到高级(三)

3 状态管理 随着应用不断变大&#xff0c;应该更有意识的去关注应用状态如何组织&#xff0c;以及数据如何在组件之间流动。冗余或重复的状态往往是缺陷的根源。 3.1 用State响应输入 3.1.1 声明式地考虑UI 总体步骤如下&#xff1a; 定位组件中不同的视图状态 确定是什么…

C语言---关键词

C语言关键词如下&#xff1a;

centos7快速修改密码

centos7快速修改密码 小白教程&#xff0c;一看就会&#xff0c;一做就成。 1.命令 #第一种&#xff0c;我经常用这个&#xff0c;这个不行了&#xff0c;会用到第二个echo 用户名:密码 | sudo chpasswd #例如下面 echo root:yegoo123 | chpasswd#第二种echo 密码|passwd --st…

Redis之主从复制解读

目录 基本概述 作用 如何配置主从复制 命令配置&#xff08;Slaveof &#xff09; 配置文件配置 主从复制缺点 主从复制原理 主从复制常见问题解答 命令补充&#xff08;info replication&#xff09; 基本概述 主从复制,是指将一台Redis服务器的数据,复制到其他的R…

MySQL分页查询详解:优化大数据集的LIMIT和OFFSET

最近在工作中&#xff0c;我们遇到了一个需求&#xff0c;甲方要求直接从数据库导出一个业务模块中所有使用中的工单信息。为了实现这一目标&#xff0c;我编写了一条SQL查询语句&#xff0c;并请求DBA协助导出数据。尽管工单数量并不多&#xff0c;只有3000多条&#xff0c;但…

国产5G+卫星通信手机推出,加速追赶美国星链,优势是价格更实惠

星链已成为美国在6G时代实现弯道超车的关键&#xff0c;面对着中国在地面移动通信技术上的优势&#xff0c;美国意图依靠卫星通信技术反超&#xff0c;而近期国产5G卫星通信手机的推出&#xff0c;却意味着中国在民用卫星通信技术上领先一步。 一、中国在地面移动通信技术的优势…

【微服务部署】07-调用链追踪

文章目录 集成SkyWalking实现调用链追踪1. SkyWalking架构图2. 代码集成SkyWalking 集成SkyWalking实现调用链追踪 1. SkyWalking架构图 Receiver是SkyWalking的入口&#xff0c;支持gRPC和HTTP协议。 SkyWalking内部有分析和查询两个部分 存储方面SkyWalking支持Elasticsearc…

mybatis源码学习-3-解析器模块

写在前面,这里会有很多借鉴的内容,有以下三个原因 本博客只是作为本人学习记录并用以分享,并不是专业的技术型博客笔者是位刚刚开始尝试阅读源码的人,对源码的阅读流程乃至整体架构并不熟悉,观看他人博客可以帮助我快速入门如果只是笔者自己观看,难免会有很多弄不懂乃至理解错误…

C++实现蜂群涌现效果(flocking)

Flocking算法0704_元宇宙中的程序员的博客-CSDN博客 每个个体的位置&#xff0c;通过计算与周围个体的速度、角度、位置&#xff0c;去更新位置。

【01背包理论】01背包问题dp[i][j](二维数组) <动态规划板子>

【01背包理论】01背包问题 dp[i][j] 有 n 件物品和一个最多能背重量为 w 的背包。 第 i 件物品的重量是 weight[i]&#xff0c;得到的价值是 value[i] 。 每件物品只有一个&#xff0c;求解将哪些物品装入背包里物品价值总和最大。 题解 动态规划 确定 dp 数组以及下标的含义…

使用Docker安装和部署RabbitMQ

&#x1f680; 1 拉取RabbitMQ Docker镜像 首先&#xff0c;使用Docker命令从Docker Hub拉取RabbitMQ官方镜像。打开终端并运行以下命令&#xff1a; docker pull rabbitmq&#x1f680; 2 创建RabbitMQ容器 一旦镜像下载完成&#xff0c;使用以下命令创建RabbitMQ容器&…

报错合集 ing - net::ERR_ABORTED 500 (Internal Server Error)

报错&#xff1a;net::ERR_ABORTED 500 (Internal Server Error) 根据提示找到对应文件 解决&#xff1a;检查代码&#xff0c;根据高亮颜色判断&#xff0c;发现箭头函数漏了一个>。 报错&#xff1a;Uncaught TypeError: Assignment to constant variable. &#xff08…

【负载均衡】常见的负载均衡策略有哪些?

文章目录 前言负载均衡分类常见负载均衡策略小结 前言 负载均衡策略是实现负载均衡器的关键&#xff0c;而负载均衡器又是分布式系统中不可或缺的重要组件。使用它有助于提高系统的整体性能、可用性、可靠性和安全性&#xff0c;同时支持系统的扩展和故障容忍性。对于处理大量…

新建工程——第一个S32DS工程

之前的"测试开发板"章节 测试开发板——第一个AutoSAR程序,使用了一个 demo 工程,不管是裸机程序还是 AutoSAR 程序,那都是别人已经创建好的工程。本节来介绍如何来创建自己的工程,本节介绍如何创建一个 S32DS 的工程,点亮开发板上的 LED 我们从官方提供的例程…