Allan 方差计算,无需ROS环境

Allan 方差:时间序列分析中的关键指标

在时间序列分析中,Allan 方差是一种重要的工具,用于评估频率稳定性和噪声特性。无论是在科学研究、工程领域还是金融市场,对时间序列数据的准确性和稳定性有着极高的需求。本文将介绍 Allan 方差的概念、应用和计算方法,以及它在不同领域中的重要性。

什么是 Allan 方差?

Allan 方差是由大卫·艾伦(David Allan)于1966年提出的一种统计方法,用于评估频率稳定性和噪声特性。它在分析频率稳定性、惯性导航系统、惯性测量单元(IMU)、全球导航卫星系统(GNSS)、光学惯性导航系统等领域中广泛应用。

Allan 方差通过分析频率数据的稳定性,揭示了时间序列数据中隐藏的特征和噪声模式。它能够检测到由振荡、漂移、随机噪声等引起的频率变化,因此在精密测量和导航系统中被广泛使用。

Allan 方差的计算方法
Allan 方差的计算步骤如下:

  1. 收集数据:首先,需要收集时间序列数据,通常是频率或相位测量数据,这些数据可以是从传感器、设备或系统中采集得到的。
  2. 计算平均差值:将数据按照不同的时间间隔分组,并计算每个时间间隔内的差值。
  3. 计算方差:对每个时间间隔内的差值进行平方并求平均,得到方差。 重复计算:重复上述步骤,不断增大时间间隔,得到一系列方差值。
  4. 绘制 Allan 方差图:将得到的方差值按时间间隔的对数尺度绘制成图表,即可得到 Allan 方差图。
  5. 通过分析 Allan 方差图,可以识别出频率稳定性的特征,例如白噪声、布朗噪声和随机游走等。
    本文参考https://github.com/ori-drs/allan_variance_ros代码,重构了相关代码,去除ROS环境需求。
    完整代码在我的github仓库https://github.com/dockermyself/allan_variance?tab=readme-ov-file

#include "AllanVarianceComputor.hpp"
#include <iostream>
#include <fstream>

namespace allan_variance
{

    typedef const ImuSensorMsg &ImuSensorMsgConstRef;

    AllanVarianceComputor::AllanVarianceComputor(std::string config_file, std::string output_path)
        : firstMsg_(true), overlap_(0.0)
    {
        YAML::Node node = loadYamlFile(config_file);

        get(node, "imu_rate", imu_rate_);
        std::cout << "imu_rate: " << imu_rate_ << std::endl;
        get(node, "measure_rate", measure_rate_);
        std::cout << "measure_rate: " << measure_rate_ << std::endl;

        imu_skip_ = int(imu_rate_ / measure_rate_);

        imu_output_file_ = output_path + "/" + "allan_variance" + ".csv";
    }

    // write_imu_only assumes batch optimization and that an optimization run had already happened
    void AllanVarianceComputor::run(std::string data_path)
    {
        std::cout << "Processing " << data_path << " ..." << std::endl;

        av_output_ = std::ofstream(imu_output_file_, std::ofstream::out);

        int imu_counter = 0;

        // Loop through data
        time_t start = clock();
        std::ifstream file(data_path);
        std::string line;
        while (std::getline(file, line))
        {
            std::istringstream iss(line);
            std::string token;
            float data[7];
            int i = 0;
            while (std::getline(iss, token, ' '))
            {
                data[i] = std::stof(token);
                i++;
            }
            if (i != 7)
            {
                std::cout << "Invalid IMU message" << std::endl;
                continue;
            }
            ImuSensorMsg msg(data[0], data[1], data[2], data[3], data[4], data[5], data[6]);
            tCurrNanoSeconds_ = msg.timestamp * 1e9;

            imu_counter++;

            // // Subsample IMU measurements
            if (imu_counter % imu_skip_ != 0)
            {
                continue;
            }
            printf("IMU counter: %d\n", imu_counter);

            if (difftime(clock(), start) / CLOCKS_PER_SEC >= 2.0)
            {
                start = clock();
            }

            if (firstMsg_)
            {
                firstMsg_ = false;
                firstTime_ = tCurrNanoSeconds_;
                lastImuTime_ = tCurrNanoSeconds_;
            }
            else if (tCurrNanoSeconds_ < lastImuTime_)
            {
                skipped_imu_++;
                std::cout << "IMU out of order. Current(ns): "
                          << tCurrNanoSeconds_ - firstTime_ << " Last(ns): "
                          << lastImuTime_ - firstTime_ << " (" << skipped_imu_ << " dropped)" << std::endl;
                continue;
            }

            lastImuTime_ = tCurrNanoSeconds_;

            ImuMeasurement input;
            input.t = tCurrNanoSeconds_;
            input.I_a_WI = msg.linear_acceleration;
            input.I_w_WI = msg.angular_velocity;
            imuBuffer_.push_back(input);
        }
        // Compute Allan Variance here
        if (!imuBuffer_.empty())
        {
            printf("Computing Allan Variance, buffer size: %ld\n", imuBuffer_.size());
            allanVariance();
        }
        else
        {
            std::cout << "No IMU messages to process" << std::endl;
        }
    }

    void AllanVarianceComputor::closeOutputs() { av_output_.close(); }

    void AllanVarianceComputor::allanVariance()
    {

        std::mutex mtx;
        bool stop_early = false;
        std::map<int, std::vector<std::vector<double>>> averages_map;

        // Range we will sample from (0.1s to 1000s)
        int period_min = 1;
        int period_max = 10000;

// Overlapping method
#pragma omp parallel for
        for (int period = period_min; period < period_max; period++)
        {

            if (stop_early)
            {
                stop_early = true;
                continue;
            }

            std::vector<std::vector<double>> averages;
            double period_time = period * 0.1; // Sampling periods from 0.1s to 1000s

            int max_bin_size = period_time * measure_rate_;
            int overlap = floor(max_bin_size * overlap_);

            std::vector<double> current_average = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0};

            // Compute Averages
            for (int j = 0; j < ((int)imuBuffer_.size() - max_bin_size); j += (max_bin_size - overlap))
            {
                // get average for current bin
                for (int m = 0; m < max_bin_size; m++)
                {
                    // // Acceleration
                    current_average[0] += imuBuffer_[j + m].I_a_WI[0];
                    current_average[1] += imuBuffer_[j + m].I_a_WI[1];
                    current_average[2] += imuBuffer_[j + m].I_a_WI[2];

                    // Gyro - assumes measurements in radians and convert to degrees
                    current_average[3] += imuBuffer_[j + m].I_w_WI[0] * 180 / M_PI;
                    current_average[4] += imuBuffer_[j + m].I_w_WI[1] * 180 / M_PI;
                    current_average[5] += imuBuffer_[j + m].I_w_WI[2] * 180 / M_PI;
                }

                current_average[0] /= max_bin_size;
                current_average[1] /= max_bin_size;
                current_average[2] /= max_bin_size;
                current_average[3] /= max_bin_size;
                current_average[4] /= max_bin_size;
                current_average[5] /= max_bin_size;

                averages.push_back(current_average);
                current_average = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
            }

            {
                std::lock_guard<std::mutex> lck(mtx);
                int num_averages = averages.size();
                std::cout << "Computed " << num_averages << " averages for period " << period_time
                          << " (" << (10000 - averages_map.size()) << " left)" << std::endl;
                averages_map.insert({period, averages});
            }
        }

        if (stop_early)
        {
            std::cout << "Stop requested, stopping calculation!" << std::endl;
            return;
        }

        std::vector<std::vector<double>> allan_variances;
        for (int period = period_min; period < period_max; period++)
        {

            std::vector<std::vector<double>> averages = averages_map.at(period);
            double period_time = period * 0.1; // Sampling periods from 0.1s to 1000s
            int num_averages = averages.size();
            std::cout << "Computed " << num_averages << " bins for sampling period " << period_time << " out of "
                      << imuBuffer_.size() << " measurements." << std::endl;

            // Compute Allan Variance
            std::vector<double> allan_variance = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
            for (int k = 0; k < num_averages - 1; k++)
            {
                allan_variance[0] += std::pow(averages[k + 1][0] - averages[k][0], 2);
                allan_variance[1] += std::pow(averages[k + 1][1] - averages[k][1], 2);
                allan_variance[2] += std::pow(averages[k + 1][2] - averages[k][2], 2);
                allan_variance[3] += std::pow(averages[k + 1][3] - averages[k][3], 2);
                allan_variance[4] += std::pow(averages[k + 1][4] - averages[k][4], 2);
                allan_variance[5] += std::pow(averages[k + 1][5] - averages[k][5], 2);
            }
            std::vector<double> avar = {
                allan_variance[0] / (2 * (num_averages - 1)), allan_variance[1] / (2 * (num_averages - 1)),
                allan_variance[2] / (2 * (num_averages - 1)), allan_variance[3] / (2 * (num_averages - 1)),
                allan_variance[4] / (2 * (num_averages - 1)), allan_variance[5] / (2 * (num_averages - 1))};

            std::vector<double> allan_deviation = {std::sqrt(avar[0]), std::sqrt(avar[1]), std::sqrt(avar[2]),
                                                   std::sqrt(avar[3]), std::sqrt(avar[4]), std::sqrt(avar[5])};

            writeAllanDeviation(allan_deviation, period_time);

            allan_variances.push_back(avar);
        }
    }

    void AllanVarianceComputor::writeAllanDeviation(std::vector<double> variance, double period)
    {
        aVRecorder_.period = period;
        aVRecorder_.accX = variance[0];
        aVRecorder_.accY = variance[1];
        aVRecorder_.accZ = variance[2];
        aVRecorder_.gyroX = variance[3];
        aVRecorder_.gyroY = variance[4];
        aVRecorder_.gyroZ = variance[5];
        aVRecorder_.writeOnFile(av_output_);
    }

} // namespace allan_variance

运行命令

./allan_variance ../data ../config/hfi.yaml
python3 scripts/analysis.py --data data/allan_variance.csv

加速度allan variance
陀螺角速度allan variance
本文使用的IMU为
HFI-A9

  • 13
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
### 回答1: MATLAB提供了计算Allan方差的函数。Allan方差是一种用于分析频率数据的统计方法,在许多领域,如惯性导航、原子钟和语音处理等中被广泛使用。下面是使用MATLAB计算Allan方差的基本步骤: 1. 准备频率数据:首先,你需要准备好频率数据。这些数据可以是时间序列,表示系统的频率随时间的变化。确保数据的采样率是一致的,以便正确计算Allan方差。 2. 使用MATLAB函数计算Allan方差:一旦你准备好频率数据,可以使用MATLAB中的'rwvar'函数计算Allan方差。该函数的语法如下:allanvar = rwvar(frequencyData),其中frequencyData是包含频率数据的向量。 3. 分析结果:计算完成后,你将得到一个包含Allan方差结果的向量。你可以使用MATLAB中的其他函数和工具来可视化和进一步分析这些结果,以了解系统的特性和性能。 需要注意的是,计算Allan方差需要足够长的数据序列,以保证结果的可靠性。数据序列的长度应该是足够长,以包含系统的典型变化和波动。 ### 回答2: MATLAB是一种用于科学计算和数据分析的强大软件工具。在MATLAB中,可以使用allan方差来测量信号的随机漂移和噪声特性。 为了计算allan方差,我们可以按照以下步骤进行操作: 1. 首先,获取要分析的信号。这可以是任何一个在时间上变化的信号,如加速度、速度或任何其他物理量。假设我们有一个长度为N的信号向量X,其中包含了我们要进行allan方差分析的数据。 2. 在MATLAB中,有一个函数名为'variance',它可以用来计算任何一维信号的方差。我们可以使用这个函数来计算每个时间间隔的方差。 3. 下一步是计算allan方差。使用allan方差公式,我们可以通过将不同时间间隔的方差进行平均来得到allan方差。如果我们假设方差从最小时间间隔开始逐渐增加,那么allan方差可以通过以下公式来计算allan_variance = (1 / (2 * (N - 1) * τ^2)) * sum((X(τ) - X(2τ))^2) 其中N是信号向量的长度,τ是不同时间间隔的值。 4. 最后,我们可以使用MATLAB的绘图功能来可视化allan方差的结果。我们可以将allan方差作为时间间隔τ的函数绘制成图表,以便更好地理解信号的随机漂移和噪声特性。 总结起来,MATLAB可以通过计算方差和使用allan方差公式来分析信号的随机漂移和噪声特性。通过可视化allan方差的结果,我们可以更好地理解信号的特性,并作出相应的决策和调整。 ### 回答3: MATLAB可以通过使用allanvar函数来计算Allan方差Allan方差是用于衡量稳定性和噪声特性的一种方法,在信号处理和控制系统中广泛使用。 要计算Allan方差,首先需要将数据导入MATLAB环境。该数据可以是一个时间序列,其中包含随时间变化的测量值。然后,可以使用allanvar函数来计算Allan方差。 该函数需要一个输入参数,即数据向量。可以在MATLAB命令窗口中使用以下语法调用allanvar函数: allanvar(data) 这里,data是包含要计算Allan方差的数据的向量。函数将返回一个包含计算Allan方差值的结果向量。 可以使用这个结果向量来分析数据的噪声特性和稳定性。Allan方差通常以对数的形式表示,因此通常会对结果应用对数变换以获得更直观的结果。 MATLAB的allanvar函数还有其他可选的输入参数,可以用来指定Allan方差估计的类型和长度。可以通过查看MATLAB文档或使用help命令来了解更多关于该函数的详细信息。 通过使用MATLAB的allanvar函数,可以方便地计算和分析信号的Allan方差,从而更好地了解信号的噪声特性和稳定性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值