ARM CMSIS为q15 FFT提供错误输出

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ARM CMSIS为q15 FFT提供错误输出相关的知识,希望对你有一定的参考价值。

我正在编程STM32皮质M4微控制器。我做了一个简单的电路来偏移正弦信号,使其从0到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之间的输出1但这不起作用

如果我插入一个具有最大输出的信号或者如果我只留下DC信号,那么结果基本相同,这是错误的......非常错误。

为了测试这是否有效,我在微控制器中创建了自己的正弦波,并且我也做了同样的事情。这是我的代码:

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),DC偏移是2048.唯一的区别是我采用了相同的创建信号并做了相同的计算,并在python中实现了相同的功能,并比较了结果。

在python中,RMS值为0.02209708691207961在浮点计算中我得到了0.176776692,这是正确的...因为我需要得到这个值,除以128乘以8然后乘以2.这是因为给定的值需要通过N个样本(这种情况下128,但如果使用64个样本FFt然后64等等)来抑制cfft(可能不是正确的术语),这给了我与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但是结果缓冲区显然是错误的。

我最近再次对此进行了测试..我创建了浮动正弦波,然后将其转换为q31并进行了FFT并且结果良好,与浮点输出相比,它被衰减了128倍,但输出是正确的值。我甚至尝试在浮点中创建正弦波,将其转换为q15,然后将其从q15值转换为q31,并且q31 FFT的结果相同。因此我只能假设在q15的FFT实现中存在一个错误,我可能在更换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算法即使在“完美波”中也不起作用

答案

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

我实际上有两个问题。

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

我面临的第二个问题是我没有使用复杂的数组作为FFT的输入(index0 = ADc数据向左移位,索引1 = 0,index2 = ADC数据向左移动,......等等正如@JRSchweitzer在评论中所述。

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

希望这将有助于未来的其他人

以上是关于ARM CMSIS为q15 FFT提供错误输出的主要内容,如果未能解决你的问题,请参考以下文章

HI3861学习笔记——CMSIS-RTOS2接口

CMSIS-DAP硬件调试--ARM核单片机仿真调试工具

ARM官方《CMSIS-RTOS教程》之线程Threads

Keil MDK 5.17 released, ARM - CMSIS 4.5.0( 2015-11)

STM32 固件库与 CMSIS 标准如何理解?

FFT 使用 C++ 定点优化 ARM 设备的性能