ARM CMSIS 为 q15 FFT 提供了错误的输出
ARM CMSIS giving wrong output for q15 FFT
我正在为 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 是您正在使用的数据点数,信号是任何信号...为了忽略直流偏移,您必须执行以下操作
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 中实现了相同的功能并比较了结果。
IN python RMS 值为 0.02209708691207961
在float计算中我得到0.176776692,这是正确的...因为我需要得到这个值,将它除以128乘以8然后乘以2。这是因为cfft的给定值需要被抑制(可能不是正确的术语)N 个样本(本例为 128,但如果使用 64 个样本 FFt,则为 64,依此类推),这给了我与 python 函数完全相同的值。
在 q15 函数中,我得到 0.489307404,它比预期值大 22 倍,所以……显然是错误的。即使当我将浮点 FFT 的输出与 "perfect" 正弦信号的 q15 FFT 的输出进行比较时,它似乎也是错误的......为了注意到这一点,我检查了浮点 FFT 结果和 q15 FFT 结果以及 python FFt 结果。
我还尝试了什么?
由于上述方法失败,我尝试了以下方法。我没有进行自己的 RMS 计算,而是使用了 arm_rms_q15 函数,该函数应该接收一个 128 个样本 q15 缓冲区并输出一个 q15 RMS 值,这只会给我一个很大的 fat 0(当然是由于饱和指令),我也尝试过使用 arm_q15_to_float(&src, dst, size) 将我的整个缓冲区转换为浮点缓冲区并执行 FFT BUT 结果缓冲区显然是错误的。
我最近又测试了这个..我创建了浮动正弦波然后将其转换为q31并进行了FFT,结果很好,相比之下它被抑制了128倍到浮点输出,但输出是正确的值。我什至尝试在浮点中创建正弦波,将其转换为 q15,然后将其从 q15 值转换为 q31,而 q31 FFT 的结果是相同的。所以我只能假设在 FFT 的 q15 实现中存在一个错误,我可能在更换 PC 时介绍了自己,因为几个月前我能够使用 q15 FFT 并且结果还可以(是的我知道......我忘了提到这么重要的事情,但老实说我只是记得)。所以我会在星期一再次寻找这个问题,看看我正在使用的 "new" CMSIS 库中是否存在错误(我可能在更换 PC 时更改了版本)或者我是否已经更改不小心配置了...但是如果有人知道哪里出了问题,请在我去寻找错误之前告诉我...这样肯定会更快。
"perfect sinusoidal"的输入数据和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 算法即使在 "perfect wave"
中也不起作用
好的,抱歉我花了这么长时间才 post 我解决了这个问题。
我实际上遇到了 2 个问题。
其中之一是我使用了非常小的输入,它适用于软件生成的正弦波,因为它刚好在最小值内。我所说的最小值是指对输入进行抑制,以免导致溢出,在 128 个数据点的情况下,大部分时间输入几乎为零,因此输出错误。这意味着 12 位可能不够分辨率,我建议将 ADC 寄存器值向左移动 4 位(乘以 16),然后使用该值进行计算,或者只是左对齐 ADC 寄存器。当然,这会给你一个倒置的正弦波,但你可以只翻转第 15 位,或者只是按原样处理波并反转结果(如有必要......例如 RMS 值将保持不变,但其他一些计算可能会反转)。
我面临的第二个问题是我没有使用复数数组作为 FFT 的输入(索引 0 = ADC 数据向左移动,索引 1 = 零,索引 2 = ADC 数据向左移动, ...等等),正如@J.R.Schweitzer在他的评论中所说。
我的解决方案:使用 q31 FFT,将数据向左移动 19 位,并使用珍贵段落中提到的复杂输入缓冲区。我没有注意到速度有任何差异,而 q31 输出给了我更好的结果。
希望这对以后的其他人有所帮助
我正在为 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 是您正在使用的数据点数,信号是任何信号...为了忽略直流偏移,您必须执行以下操作
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 中实现了相同的功能并比较了结果。
IN python RMS 值为 0.02209708691207961 在float计算中我得到0.176776692,这是正确的...因为我需要得到这个值,将它除以128乘以8然后乘以2。这是因为cfft的给定值需要被抑制(可能不是正确的术语)N 个样本(本例为 128,但如果使用 64 个样本 FFt,则为 64,依此类推),这给了我与 python 函数完全相同的值。 在 q15 函数中,我得到 0.489307404,它比预期值大 22 倍,所以……显然是错误的。即使当我将浮点 FFT 的输出与 "perfect" 正弦信号的 q15 FFT 的输出进行比较时,它似乎也是错误的......为了注意到这一点,我检查了浮点 FFT 结果和 q15 FFT 结果以及 python FFt 结果。
我还尝试了什么?
由于上述方法失败,我尝试了以下方法。我没有进行自己的 RMS 计算,而是使用了 arm_rms_q15 函数,该函数应该接收一个 128 个样本 q15 缓冲区并输出一个 q15 RMS 值,这只会给我一个很大的 fat 0(当然是由于饱和指令),我也尝试过使用 arm_q15_to_float(&src, dst, size) 将我的整个缓冲区转换为浮点缓冲区并执行 FFT BUT 结果缓冲区显然是错误的。
我最近又测试了这个..我创建了浮动正弦波然后将其转换为q31并进行了FFT,结果很好,相比之下它被抑制了128倍到浮点输出,但输出是正确的值。我什至尝试在浮点中创建正弦波,将其转换为 q15,然后将其从 q15 值转换为 q31,而 q31 FFT 的结果是相同的。所以我只能假设在 FFT 的 q15 实现中存在一个错误,我可能在更换 PC 时介绍了自己,因为几个月前我能够使用 q15 FFT 并且结果还可以(是的我知道......我忘了提到这么重要的事情,但老实说我只是记得)。所以我会在星期一再次寻找这个问题,看看我正在使用的 "new" CMSIS 库中是否存在错误(我可能在更换 PC 时更改了版本)或者我是否已经更改不小心配置了...但是如果有人知道哪里出了问题,请在我去寻找错误之前告诉我...这样肯定会更快。
"perfect sinusoidal"的输入数据和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 算法即使在 "perfect wave"
中也不起作用好的,抱歉我花了这么长时间才 post 我解决了这个问题。
我实际上遇到了 2 个问题。
其中之一是我使用了非常小的输入,它适用于软件生成的正弦波,因为它刚好在最小值内。我所说的最小值是指对输入进行抑制,以免导致溢出,在 128 个数据点的情况下,大部分时间输入几乎为零,因此输出错误。这意味着 12 位可能不够分辨率,我建议将 ADC 寄存器值向左移动 4 位(乘以 16),然后使用该值进行计算,或者只是左对齐 ADC 寄存器。当然,这会给你一个倒置的正弦波,但你可以只翻转第 15 位,或者只是按原样处理波并反转结果(如有必要......例如 RMS 值将保持不变,但其他一些计算可能会反转)。
我面临的第二个问题是我没有使用复数数组作为 FFT 的输入(索引 0 = ADC 数据向左移动,索引 1 = 零,索引 2 = ADC 数据向左移动, ...等等),正如@J.R.Schweitzer在他的评论中所说。
我的解决方案:使用 q31 FFT,将数据向左移动 19 位,并使用珍贵段落中提到的复杂输入缓冲区。我没有注意到速度有任何差异,而 q31 输出给了我更好的结果。
希望这对以后的其他人有所帮助