分析加速度计数据特征并设计滤波器

电器工程 Arduino 微控制器 嵌入式 筛选 信号
2022-01-11 08:27:08

我有大约 32 秒的加速度计数据,基本驾驶场景为 25MPH 正常道路,同时撞到大约 7 个坑洼和崎岖不平的道路。加速度计用双面胶带安装在我汽车的仪表板上。

问题:我有所有来自加速度计的噪声数据,我需要用一种简单的方法来检测是否发生了坑洞事件。下面是时域和 FFT 中的几张数据图。加速度计在 GForce 中测量

基本上我希望我的 arduino 知道一个坑洼已经发生了,而且准确度很高,而不是使用研究生水平的数学和技术。

以 100hz 采样的加速度计在 Z 轴上有一个简单的 50HZ RC 低通滤波器

Here is the CSV data for the 32 seconds of accelerometer readings TIME, GFORCE format:

http://hamiltoncomputer.us/50HZLPFDATA.CSV

更新:这是加速度计 1000HZ 的原始全带宽,以我可以在 Arduino 上获得的最高采样率进行采样。直接下载 CSV 文件:大约 112 秒的数据

http://hamiltoncomputer.us/RAWUNFILTEREDFULLBANDWIDTH500HZ.csv

黑色迹线是未经过滤的原始加速度计数据:蓝色迹线由带阻滤波器根据 FFT、Dominate 2HZ 和 12HZ 中发现的极端频率进行过滤。

http://img213.imageshack.us/img213/194/rollout.png

Pothole 事件在时域中如下所示: 在此处输入图像描述

不确定 FFT 中的 10 到 15HZ 分量是什么,是实际的坑洼,还是车轮对路面的跳动,还是汽车的共振频率?

快速傅里叶变换:

快速傅里叶变换

似乎这是实际的坑洞事件,这是一个 HPF @ 13HZ 坑洞的主要特征似乎得到了增强

http://img69.imageshack.us/img69/8663/hpf13potholefft.png

我希望能够实时检测和计算坑洼

似乎违反直觉的是,悬架的移动速度应该比 10 到 13 赫兹慢很多,我相信这会导致晕车

更新:

根据 AngryEE 的建议,我使用了 1000HZ 加速度计的全带宽和我可以在 arduino 上获得的最大采样率。

快速傅里叶变换:

FFT 未过滤数据全带宽

这是坑洼事件的样本数据及其周围的一些颠簸和道路噪音:

未归档数据坑洞事件

添加了二极管包络检测器电路,输出看起来一样...加速度计始终输出 0 到 3.3 伏而不是负... 在此处输入图像描述

更新:

在许多道路测试中,我的汽车在 Z 轴上的速度从未超过 1.6G,最高可达 45 MPH,我使用 rand() 生成伪随机 Gforce 加速度。

我的想法是,如果我可以查看 1 到 3 秒的数据窗口,我可以计算 Z 轴的位移,但我担心加速度计漂移和积分误差。我在这里甚至不需要 90% 的准确度,> 70% 会很好,但是如果我一次查看 1 到 3 秒的位移,那可以实时进行吗?这样我可以看到位移是否大于 1 英寸、2 英寸、5 英寸。位移越大,凹凸或坑洞越粗糙:

你能检查一下我这样做是否正确,我基本上是在桌面上设置的,使用 rand() 生成从 -1.6 到 1.6 G 的随机加速度,以模拟 50HZ 采样率捕获 3 秒数据

如果像你运行 *nix,我使用 Windows.h 中的 Sleep() 来实现 20mS 延迟,50HZ 采样率

我只是想看看代码对你来说是否合适,我还没有做 cicular 缓冲区,我对如何实现它有点困惑:注释掉的代码来自我正在为它工作的类,但我还没有 100% 理解它。循环缓冲区将允许连续移动数据窗口吗?

#include <cstdlib>
#include <iostream>
#include <iomanip>
#include <ctime> // USED BY RAND
#include <windows.h> // Used for delay


using namespace std;



#define SAMPLE_RATE   0.020 // Sample rate in Milliseconds
#define GRAVITYFT_SEC 32 // Gravity velocity 32 feet/sec
#define INCH_FOOT     12 // 12 inches in foot, from velocity to inch displacement calculation










int main(int argc, char *argv[])
{
    srand((unsigned)time(0)); // SEED RAND() for simulation of Geforce Readings

    // SIMULATING ACCELERATION READINGS INTO A CIRCULAR BUFFER

   // circular_buffer Acceleration; // Create a new Circular buffer for Acceleration

   // cb_init(&Acceleration, 150, 4); // Sampling @ 50HZ, 3 seconds of data = 150, size is float data of 4 bytes

    //Simulate a sample run of Acceleration data using Rand()

    // WE WILL BE SIMULATING "RANDOM" GEFORCE RATINGS using the rand() function constraining to -1.6 to 1.6 GFORCE 
    // These ratings are consistent with our road tests of apparently random vibration and Geforce readings not exceeding about 1.6 G's

    float Gforce[150]; // Random Geforce for 3 second window of data
    float velocity[150]; // Hold velocity information
    float displacement[150]; // Hold Displacement information


    float LO = -1.6; // Low GForce limit recorded from 6 road tests at different speeds
    float HI = 1.6; // High GForce limit recorded from 6 road tests at different speeds

    for(int i = 0; i < 150; i++) // 3 Second iwndow of random acceleration data
    {  
            Gforce[i] = LO + (float)rand()/((float)RAND_MAX/(HI-LO)); // Borrowed from Stackexchange : http://stackoverflow.com/questions/686353/c-random-float
            if( i == 0) // Initial values @ first Acceleration
            {
                velocity[i] = Gforce[i] * SAMPLE_RATE * GRAVITYFT_SEC; // Initial velocity
                displacement[i] = velocity[i] * SAMPLE_RATE * INCH_FOOT; // Initial Displacement
            }
            else
            {
                velocity[i] = velocity[i-1] + (Gforce[i] * SAMPLE_RATE * GRAVITYFT_SEC); // Calculate running velocity into buffer
                displacement[i] = displacement[i-1] +(velocity[i] * SAMPLE_RATE * INCH_FOOT); // Calculate running displacement into buffer
            }
            //cout << endl << Gforce[i]; // Debugging
            //cb_push_back(&Acceleration, &Gforce[i]);                   // Push the GeForce into the circular buffer


            Sleep(SAMPLE_RATE*1000); // 20mS delay simulates 50HZ sampling rate Sleep() expects number in mS already so * 1000

    }
    // PRINT RESULTS
    for (int j = 0; j < 150; j++)
            {
                cout << setprecision (3) << Gforce[j] << "\t\t" << velocity[j] << "\t\t" << displacement[j] << endl;
            }

    // READ THE BUFFER





    //cb_free(&Acceleration); // Pervent Memory leaks

    system("PAUSE");
    return EXIT_SUCCESS;
}

样品运行:

    GFORCE          FT/SEC          Inch Displacement Z axis

-0.882          -0.565          -0.136
0.199           -0.437          -0.24
-1.32           -1.29           -0.549
0.928           -0.691          -0.715
0.6             -0.307          -0.788
1.47            0.635           -0.636
0.849           1.18            -0.353
-0.247          1.02            -0.108
1.29            1.85            0.335
0.298           2.04            0.824
-1.04           1.37            1.15
1.1             2.08            1.65
1.52            3.05            2.38
0.078           3.1             3.12
-0.0125         3.09            3.87
1.24            3.88            4.8
0.845           4.42            5.86
0.25            4.58            6.96
0.0463          4.61            8.06
1.37            5.49            9.38
-0.15           5.39            10.7
0.947           6               12.1
1.18            6.75            13.7
-0.791          6.25            15.2
-1.43           5.33            16.5
-1.58           4.32            17.5
1.52            5.29            18.8
-0.208          5.16            20.1
1.36            6.03            21.5
-0.294          5.84            22.9
1.22            6.62            24.5
1.14            7.35            26.3
1.01            8               28.2
0.284           8.18            30.1
1.18            8.93            32.3
-1.43           8.02            34.2
-0.167          7.91            36.1
1.14            8.64            38.2
-1.4            7.74            40
-1.49           6.79            41.7
-0.926          6.2             43.2
-0.575          5.83            44.6
0.978           6.46            46.1
-0.909          5.87            47.5
1.46            6.81            49.2
0.353           7.04            50.8
-1.12           6.32            52.4
-1.12           5.6             53.7
-0.141          5.51            55
0.463           5.8             56.4
-1.1            5.1             57.6
0.591           5.48            59
0.0912          5.54            60.3
-0.47           5.23            61.5
-0.437          4.96            62.7
0.734           5.42            64
-0.343          5.21            65.3
0.836           5.74            66.7
-1.11           5.03            67.9
-0.771          4.54            69
-0.783          4.04            69.9
-0.501          3.72            70.8
-0.569          3.35            71.6
0.765           3.84            72.5
0.568           4.21            73.5
-1.45           3.28            74.3
0.391           3.53            75.2
0.339           3.75            76.1
0.797           4.26            77.1
1.3             5.09            78.3
0.237           5.24            79.6
1.52            6.21            81.1
0.314           6.41            82.6
0.369           6.65            84.2
-0.598          6.26            85.7
-0.905          5.68            87.1
-0.732          5.22            88.3
-1.47           4.27            89.4
0.828           4.8             90.5
0.261           4.97            91.7
0.0473          5               92.9
1.53            5.98            94.3
1.24            6.77            96
-0.0228         6.76            97.6
-0.0453         6.73            99.2
-1.07           6.04            101
-0.345          5.82            102
0.652           6.24            104
1.37            7.12            105
1.15            7.85            107
0.0238          7.87            109
1.43            8.79            111
1.08            9.48            113
1.53            10.5            116
-0.709          10              118
-0.811          9.48            121
-1.06           8.8             123
-1.22           8.02            125
-1.4            7.13            126
0.129           7.21            128
0.199           7.34            130
-0.182          7.22            132
0.135           7.31            133
0.885           7.87            135
0.678           8.31            137
0.922           8.9             139
-1.54           7.91            141
-1.16           7.16            143
-0.632          6.76            145
1.3             7.59            146
-0.67           7.16            148
0.124           7.24            150
-1.19           6.48            151
-0.728          6.01            153
1.22            6.79            154
-1.33           5.94            156
-0.402          5.69            157
-0.532          5.35            159
1.27            6.16            160
0.323           6.37            162
0.428           6.64            163
0.414           6.91            165
-0.614          6.51            166
1.37            7.39            168
0.449           7.68            170
0.55            8.03            172
1.33            8.88            174
-1.2            8.11            176
-0.641          7.7             178
-1.59           6.69            179
1.02            7.34            181
-0.86           6.79            183
-1.55           5.79            184
-0.515          5.46            186
0.352           5.69            187
0.824           6.22            188
1.14            6.94            190
-1.03           6.29            192
-1.13           5.56            193
0.139           5.65            194
0.293           5.84            196
1.08            6.53            197
-1.23           5.75            199
-1.1            5.04            200
-1.17           4.29            201
-0.8            3.78            202
-0.905          3.2             203
-0.0769         3.15            203
-0.323          2.95            204
-0.0186         2.93            205
Press any key to continue . . .
4个回答

这看起来可以通过相当直接的过滤来解决。这是您的原始数据:

以适合此处的详细程度来查看单个事件中发生的事情太多了。这里只是从 26 秒到 28 秒的数据:

我原本想对此进行低通滤波,但这不起作用,因为那里没有低频信号。高频信号的幅度反而上升。这是叠加到原件上的低通:

请注意,这很好地遵循了信号的“平均值”,而不是在坑洼事件期间。如果我们从原始信号中减去这个平均值,那么在事件期间我们会从这个平均值中得到比其他情况更高的偏移量。换句话说,我们真正想要的是高通滤波器。我们将通过从原始数据中减去低通来做到这一点,因为这就是我们到达这里的方式,但在生产系统中,您可以通过明确的高通滤波来做到这一点。无论如何,这是经过高通滤波的原件:

这现在指出了一种用于检测事件的明显方法。事件期间的信号幅度比其他情况多得多。我们可以通过计算 RMS 并应用一些低通滤波来检测这一点:

回顾整个数据,我们看到:

这清楚地识别了数据中的五个事件,尽管我不知道这是否是该数据应该显示的内容。更仔细地观察这些事件,您会注意到每个事件在峰值前后大约 1 秒都有低谷。这意味着,如果仅仅像现在这样对 RMS 信号进行阈值处理还不够好,则可以做更多的事情。例如,一个简单的算法在 1 秒内寻找一个点相对于最低点的高度,这应该会进一步降低背景噪声。关于同一件事的另一种说法是区分这个信号,寻找 1 秒内的上升。然后,一个坑洞事件将被双峰检测到,这意味着一个高峰值之后是一个低峰值。

另一种看待这个问题的方法是带通 RMS 信号。它已经经过低通滤波,但是由于您正在寻找具有强烈斜率的突发事件,因此去除一些低频也应该可以减少背景噪声。

有很多方法可以从这里优化信号,但希望我已经展示了如何获得至少第一次通过有用的结果。

添加:

我很好奇在峰的任一侧寻找下降的效果如何,所以我试了一下。我使用了从上一个图中的 RMS 开始的非线性滤波器。每个点的值是它比前一秒的最低点和下一秒的最低点高出多少的最小值。结果看起来相当不错:

5 个峰中的最低峰比最高背景噪声高 3 倍以上。这当然是假设这 5 个颠簸代表您想要检测的事件,而其余的则不代表。

为回应评论添加:

我在时域中做了滤波器,所以我不直接知道频率响应。对于低通滤波器,我将输入信号与 COS^2 滤波器内核进行卷积。如果我没记错的话,内核的半径(从中心到边缘的距离)为 100 毫秒。我尝试了这个值,直到情节看起来不错。为了对 RMS 进行低通滤波,我使用了相同的滤波器内核,但这次的半径约为一秒。具体我记不太清了。试验直到你得到好的结果。

非线性滤波器没有检测到双峰。正如我所说,我找到了当前点与前一秒内所有点中的最低点之间的差异,以及当前点与之后一秒内所有点中的最低点之间的差异。然后我取了这两个中的最小值。

我使用的软件是我为此目的而破解的程序。我已经有各种例程来读写 CSV 文件,所以我只需要编写过滤代码,这很简单。其余部分是使用我用于操作和绘制 CSV 文件的预先存在的程序完成的。

边缘检测坑洞可能会带来麻烦。汽车的振动包络线就是答案所在,因为传感器看到的实际振动频率要高得多。我会选择 RMS to DC,它以大约 15Hz 或更高的频率响应,并且低通。

我建议不要寻找频域滤波器或阈值,而是尝试为“典型”坑洞提出一个内核,并与它进行运行相关。它被认为是一种模板匹配技术,并且似乎适用于微控制器平台。

请参阅http://scribblethink.org/Work/nvisionInterface/vi95_lewis.pdf进行快速回顾,也许还有 DOBBS、STEVEN E.、NEIL M. SCHMITT 和 HALUK S. OZEMEK。“在微型计算机上使用实时相关通过模板匹配检测 QRS。” 临床工程杂志 9.3(1984):197-212。

如果你在一个更强大的平台上,我建议你试试小波。

另一种方法是计算信号的移动方差,以查看坑洼是否真的突出。这是一个用于移动方差滤波器的 matlab 函数,N 点宽——巧妙地(如果我自己必须这么说的话)使用卷积进行计算

function y=movingvar(X,N)
% y=movingvar(X,N)
% Calculates N-point moving variance of  Vector X
% Highly recommend that N be odd (no error checking)
% Note: first and last N/2 points will be unreliable.
% Output will be a column vector.


X=X(:);
XSQR=X.*X;
convsig=ones(1,N);
y=(conv(convsig,XSQR)-(conv(convsig,X).^2)/N)/(N-1);

y=y(ceil(N/2):length(X)+floor(N/2));