【问题标题】:ARM CMSIS giving wrong output for q15 FFTARM CMSIS 为 q15 FFT 提供错误的输出
【发布时间】:2018-02-23 21:48:57
【问题描述】:

我正在编写一个 STM32 cortex M4 微控制器。我制作了一个简单的电路来抵消正弦信号,使其从 o 变化(最大输出)到 3v3,基本上我构建了一个电路来为正弦信号提供 DC 偏移,因为微控制器的 ADC 不支持负电压。

我以大约 10kHz 的频率进行采样,并且我对 DMA 进行了编程,使其在产生中断之前进行 128 次采集,以便我可以处理我的数据。

我想做什么

对我的信号执行 128 点 FFT,然后进行一些其他计算(RMS 值、功率等)。基本上我有两个信号,一个用于电压,一个用于电流,我想使用 FFT 进行所有电气计算。我在 python 中实现了这个,它可以工作。

我做了什么

信号的 FFT 的 RMS 值为

import numpy as np
RMS = np.sqrt(np.sum(signal.real * signal.real + signal.imag * signal.imag))/N

其中 N 是您正在使用的数据点的数量,信号是任何信号...为了忽略 DC 偏移,您必须执行以下操作

RMS = np.sqrt(np.sum(signal[1:].real * signal[1:].real + signal[1:].imag * signal[1:].imag))/N

这是我使用 CMSIS DSP 库的 C 代码

q15_t adcData[256] = {0}; /*dma reads 128 points into this buffer*/

q31_t auxRms = 0;
q31_t auxReal = 0;
q31_t auxImag = 0;

q31_t sqrt = 0;
float result = 0.0F;
int i;

arm_cfft_q15(&arm_cfft_sR_q15_len128, adcData, 0, 1);
for(i = 1; i < N; i++) {
    auxReal = adcData[2*i];
    auxImag = adcData[2*i+1];
    auxReal = auxReal * auxReal;
    auxImag = auxImag * auxImag;
    auxRms += auxImag + auxReal;
}

arm_sqrt_q31(interim_rms, &sqrt);
arm_q31_to_float(&sqrt, &result, 1);

与 python 中的相同,对实数值求平方,对虚数值求平方并将它们相加并累加,但由于这是 q15 数学,我做了 q15 平方根并将其转换为浮点数,这给了我一个介于两者之间的输出-1 和 1 但这不起作用

不管我插入一个最大输出的信号还是只留下直流信号,结果基本一样,这是错误的……非常错误。

为了测试这是否有效,我在微控制器中创建了自己的正弦波,并对其进行了同样的操作。这是我的代码:

float var = 0.0F;
float fbuff[256] = {0};
for(i = 0; i < 128; i++) {
    var = 0.03125 * arm_sin_f32(2 * PI * i / 128);
    fbuff[i] = var;
    arm_float_to_q15(&var, &buff[i], 1);
    buff[i] += 2048;
}

有了这个,我得到一个正弦波,它模仿 ADC 的信号最大值,因为它从 0 到 4095(它是一个 12 位 ADC)并且直流偏移是 2048。唯一的区别是我采用相同的创建信号并进行了相同的计算,并且还在 python 中实现了相同的函数并比较了结果。

在 python 中,RMS 值为 0.02209708691207961 在浮点计算中,我得到了 0.176776692,这是正确的……因为我需要得到这个值,将它除以 128 乘以 8,然后再乘以 2。这是因为需要抑制 cfft 的给定值(可能不是正确的术语)N 个样本(本例为 128,但如果使用 64 个样本 FFt,则为 64,依此类推),这给了我与 python 函数完全相同的值。 在 q15 函数中,我得到了 0.489307404,它大约是预期值的 22 倍,所以......显然是错误的。即使当我将浮点 FFT 的输出与“完美”正弦信号的 q15 FFT 进行比较时,它似乎也是错误的......为了注意到这一点,我检查了浮点 FFT 结果和 q15 FFT 结果以及 python FFt 结果。

我还尝试了什么?

由于上述失败,我尝试了以下方法。我没有进行自己的 RMS 计算,而是使用了 arm_rms_q15 函数,它应该接收 128 个样本 q15 缓冲区并输出 q15 RMS 值,这只会给我一个大的 0(当然是由于饱和指令),我还尝试转换我的整个缓冲区使用 arm_q15_to_float(&src, dst, size) 进入浮点缓冲区并执行 FFT BUT 结果缓冲区显然是错误的。

我最近再次对此进行了测试。我创建了浮点正弦波,然后将其转换为 q31 并进行了 FFT,结果很好,与浮点输出相比,它被抑制了 128 倍但输出是正确的值。我什至尝试在浮点中创建正弦波,将其转换为 q15,然后将其从 q15 值转换为 q31,q31 FFT 的结果是相同的。所以我只能假设 FFT 的 q15 实现中存在一个错误,我可能在更换 PC 时自我介绍过,因为几个月前我能够使用 q15 FFT 并且结果还可以(是的,我知道......我忘了提这么重要的事情,但老实说我只是记得它)。所以我会在星期一再次寻找这个问题,看看我正在使用的“新”CMSIS 库中是否存在错误(我可能在更换 PC 时更改了版本)或者我是否可能更改了配置不小心......但是如果有人知道出了什么问题,请在我去寻找错误之前告诉我......这样肯定会更快。

“完美正弦”的输入数据和 FFT 结果示例,第一个是样本数组,第二个是使用 q15 算法的 FFT 数组

我创建的信号的值是

[ 02048  02098  02148  02198  02248  02297  02345  02393
 02440  02486  02531  02574  02617  02658  02698  02736
 02772  02807  02840  02870  02899  02926  02951  02974
 02994  03012  03028  03041  03052  03061  03067  03071
 03072  03071  03067  03061  03052  03041  03028  03012
 02994  02974  02951  02926  02899  02870  02840  02807
 02772  02736  02698  02658  02617  02574  02531  02486
 02440  02393  02345  02297  02248  02198  02148  02098
 02048  01998  01948  01898  01848  01799  01751  01703
 01656  01610  01565  01522  01479  01438  01398  01360
 01324  01289  01256  01226  01197  01170  01145  01122
 01102  01084  01068  01055  01044  01035  01029  01025
 01024  01025  01029  01035  01044  01055  01068  01084
 01102  01122  01145  01170  01197  01226  01256  01289
 01324  01360  01398  01438  01479  01522  01565  01610
 01656  01703  01751  01799  01848  01898  01948  01998
]

得到的 FFT 是

[  01020  01020  00872 -00424  00252 -00248  00108 -00334
-00002  00000  00116 -00148 -00004 -00004  00092 -00094
 00000  00000  00078 -00066 -00002  00000  00066 -00050
 00000  00000  00058 -00040 -00002 -00002  00052 -00030
 00000  00000  00048 -00026 -00002  00000  00044 -00020
-00002  00000  00040 -00016  00000  00000  00038 -00012
 00000  00000  00036 -00010 -00002 -00002  00034 -00008
 00000  00000  00032 -00006 -00002  00000  00030 -00004
 00000  00000  00030  00000  00000 -00002  00028  00002
-00002  00000  00028  00002  00000  00000  00026  00004
 00000  00000  00026  00006  00000  00000  00024  00006
 00000  00000  00022  00008  00000  00002  00022  00008
 00000  00000  00022  00008 -00002 -00002  00020  00010
-00002  00000  00018  00010  00000  00000  00018  00012
 00000  00000  00018  00012  00000  00000  00016  00014
 00000  00000  00016  00014  00000  00000  00016  00014
 00000  00000  00016  00016  00000  00000  00014  00018
-00002  00000  00014  00018  00000  00000  00012  00018
 00000  00000  00012  00020  00000  00000  00010  00020
 00000  00000  00008  00022  00000 -00002  00008  00020
 00000  00000  00008  00022  00000  00000  00006  00022
-00002  00000  00004  00024  00000  00000  00004  00024
 00000  00000  00004  00026 -00002  00000  00002  00026
 00000  00000  00000  00028 -00002  00000  00000  00030
 00000  00000 -00002  00032  00000 -00002 -00004  00032
-00002  00000 -00006  00036  00000  00000 -00010  00036
 00000  00000 -00012  00040  00000 -00002 -00014  00042
 00000  00000 -00020  00046  00000  00000 -00024  00048
 00000  00000 -00030  00052 -00002  00000 -00038  00060
-00002  00000 -00050  00066  00000  00000 -00066  00078
 00000  00000 -00094  00092 -00002  00000 -00150  00114
 00000  00000 -00342  00102 -00256  00268 -00414  00884]

编辑:

我忘了提到 ADC 正在输入有效数据,我得到了一个很好的正弦波,范围大约在 500 到 3500 之间。但这并不是那么重要,因为可以看到 FFT q15 算法即使在“完美波”中也不起作用

【问题讨论】:

  • 看起来你正在给它一个偏移正弦波作为一个真实的信号而不是一个复杂的正弦波(看起来你可能正在使用一个需要复杂输入的函数)。除了大的正偏移(这会给你带来直流的大尖峰)之外,你还有一个非常慢的频率。所有的能量都将在前几个直方图箱中。快乐的虫子狩猎。 J
  • @J.R.Schweitzer 您好,感谢您的回答。我可以尝试,但它仍然可以工作......我的意思是......当我使用 arm_cfft_f32 时它工作了,这是我正在使用的函数的浮点版本,它给了我正确的输出......所以我没有认为是这样,但我会试一试。如果我得到一个好的结果,那么我会告诉你,但我认为可能会发生一些饱和,它会影响我的输出...... SSATinstructions 似乎从来没有给我想要的结果
  • @J.R.Schweitzer 好的..我给了它一个复杂的输入......它非常适合完美的正弦波,但它失败了我得到的糟糕的正弦波。所以我只是将所有值转换为浮点数,它就像一个魅力......嘈杂的输出,但至少它保持在预期值附近。 CMSIS图书馆可能有错误吗?或者,也许我不能使用 ADC 的直接输出,但是我用“完美”进行的模拟告诉我输入是有效的……所以也许是错误……在回答之前,我会去论坛验证这个错误

标签: c arm signal-processing cortex-m


【解决方案1】:

好的,抱歉,我花了这么长时间才发布我的问题解决方案。

我实际上有两个问题。

其中之一是我使用了一个非常小的输入,它适用于软件生成的正弦波,因为它在最小值之内。最小值是指为了不导致溢出而对输入进行了抑制,在 128 个数据点的情况下,输入在大多数情况下实际上为零,因此输出错误。这意味着 12 位可能不够分辨率,我建议将 ADC 寄存器值向左移动 4 位(乘以 16),然后使用该值进行计算,或者只是左对齐 ADC 寄存器。当然,这会给你一个倒置的正弦波,但你可以只翻转第 15 位,或者只是按原样处理波形并反转结果(如有必要......例如 RMS 值将保持不变,但其他一些计算可以倒置)。

我面临的第二个问题是我没有使用复数数组作为 FFT 的输入(index0 = ADc 数据向左移动,索引 1 = 零,index2 = ADC 数据向左移动,.. . 等等),正如@J.R.Schweitzer 在他的评论中所说的那样。

我的解决方案:使用 q31 FFT 将数据向左移动 19 位和一个复杂的输入缓冲区,如珍贵段落中所述。我没有注意到速度上有任何差异,而 q31 输出给了我更好的结果。

希望这对将来的其他人有所帮助

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-04-13
    • 2012-08-21
    • 2016-12-10
    • 2022-11-25
    • 2015-07-06
    • 2020-11-01
    相关资源
    最近更新 更多