本文开始前我们先简单的介绍一下什么是THD? o& t$ d: B; ?3 i8 h" h6 H7 `
6 q+ K6 z2 y& C1 [* u* _
总谐波失真(Total Harmonic Distortion,简称THD)是衡量信号失真程度的重要指标,特别是在音频、电力电子设备和通信系统等领域。THD表示一个信号中谐波分量(除了基波之外的频率分量)相对于基波的比例。具体来说,THD是所有谐波分量的平方和与基波分量平方的比值的平方根,通常用百分比表示。
8 `% e$ k$ }" g, Z( i- [4 D
9 q0 A, H \" ~. R公式表示为:3 U/ W3 E( ~ E6 w3 j( d
, b6 ^& X$ V% Q2 |9 ~" p: [1 _
]8 W% h- ^; `. k7 Q5 r. N其中的V1是基波幅度,V2,V3……Vn是谐波幅度。
4 v( |. M( W% C% T4 s
3 G4 R, e$ |9 e8 B6 D3 A$ `( i这个公式很好理解,一个信号的谐波幅度平方和占谐波频率的百分比。 Q- v9 X0 V% a2 A) ?# V
7 Z8 M0 ]- U) P, `
那么如何获取基波幅度和谐波幅度,就是我们的FFT需要进行的事情。 N" c/ T1 D/ \5 e0 X$ X
# B( g' t3 d+ _利用FFT我们可以获取信号的频率谱。
: D) n2 p {# p2 e+ P
2 n; I% Z4 S, h' ?: A
% d9 S9 R5 F8 ?
7 z" `( i+ `* H- L ?; M
例如一段信号方波信号经过FFT之后,这些突出的方波信号就是谐波的幅值,而第一段(不包括第一个点)的则是其基波幅度。但是这里不包含第一个点,因为那个点是直流的幅度(两倍)。0 K3 u" X' `3 w, X
, k8 }' m3 D% Y$ I- m& j, [/ E
因此我们只需要进行FFT之后统计谐波的平方和即可获得我们的THD。8 f2 `2 {: ?: [9 V2 F( F' j6 j
9 A) U1 q4 P i+ w! {快速傅里叶变换2 w! |1 ^4 F' s; y$ a
关于FFT我们在之前的文章中有过介绍,我们在Keil中可以利用DSP库来进行快速的FFT(手搓的FFT算法效率通常很低)。我们在之前的文章中介绍过如何使用ADC+DMA的快速采样配合DSP库进行FFT。
1 f+ y! \! y6 R, V! R' i! k; z* P* i$ J% W$ ?
2 L2 z- h8 R0 F( Y0 x1 |5 f4 D/ h% u8 J: i$ @3 M+ J1 N
STM32的DMA采样+FFT时域分析(STM32F407)& t, Q* O$ r1 y0 t* n* s
; ~# Q8 s2 j# H+ g8 L! f4 M# E我们同样的在这篇文章的基础上进行。
, k7 x# B0 l7 S( ?- #define ADCLenth 1024
7 i6 m8 z; q2 A6 d! x3 s3 X) p& K - uint16_t ADCValue[ADCLenth];
4 ^: I/ p6 ?0 v' j4 F - arm_cfft_radix4_instance_f32 scfft;//定义scfft结构体
" D t0 J$ d) g+ ~( F) [ - float FFT_InputBuf[ADCLenth*2]; //FFT输入数组
( x ?0 y- N' a) y5 K# @ - float FFT_OutputBuf[ADCLenth]; //FFT输出数组
复制代码
) B9 y( q7 |7 C! }3 c我们定义一个FFT的结构体变量,以及存放ADC采样结果的数组还有FFT的输入输出数组,这里的输入数组之所以长度翻倍是因为对于FFT的输入来说是一个复数即包含实部和虚部,因此长度是两倍。
& _+ I/ D9 V& _) F4 ~" S3 h) I' t- for(int i=0; i < ADCLenth; i++)9 v& \( O3 P0 X- f; x0 \- Z- h5 C
- {1 ~1 E! k: C9 w1 z3 d/ S
- FFT_InputBuf[2*i]=(ADCValue[i] )*3.3/4096; //实部
; Z/ x" I, H2 c/ {" U$ O3 D: ^ - FFT_InputBuf[2*i+1]=0; //虚部
4 c" ~) Y: x6 Q% t, ~ - }
' j0 V$ B7 [7 t - arm_cfft_radix4_f32(&scfft,FFT_InputBuf);
) Y% \! q& R1 A7 Y h: w, m- |. p - arm_cmplx_mag_f32(FFT_InputBuf,FFT_OutputBuf,ADCLenth); //取模得幅值( ~0 R/ u( A0 i! R% E' Y
-
4 G' F# }) l) A5 @7 Y2 n
' { g5 o! Y4 S0 X2 L- for(int i = 2;i<ADCLenth/2;i++)
% t+ P% H3 X, ^' F - {% q- E7 S$ P4 R8 J
- if(FFT_OutputBuf[i]>maxValue)( w0 m) O. D% F; t
- {7 K4 i3 I5 ]( g
- h9 B `- r3 @4 M( H4 f
- maxValue = FFT_OutputBuf[i];6 F% X' H4 O0 c1 M% D
- max = i;
- o% z# k7 r9 x - . G" M& s& ~& _" m, F
- }# F! }; a, C; S0 v- K0 L/ A
- }
复制代码 ! ?. F+ f! E% u! U" x
一轮ADC采样结束之后,我们将其的实部信号和虚部信号(0)存放FFT的输入数组,之后执行快速傅里叶变换获得FFT的模值,模值存放在FFT_OutputBuf中。& _. p3 P- z, |0 R) X0 R
0 e" s/ W4 @( J! a/ t' j2 z
我们通过一个比较循环来寻找FFT结果的最大值,这里我们忽略前几个元素尤其是索引为0的位置,因为他是直流分量的模值。5 Z; k+ R- ]4 f7 p
: m; g& v( ?& c. Z2 d. I
0 {2 f" D/ F% E1 a/ l4 J' |
" l- y) q* k. J- ]3 U2 @之后我们就需要统计各个谐波的幅度。9 I$ @. B' f- T4 _. T7 M* i* D2 g/ @
- float FindMax(float * fft,int index,int wind)/ J5 ?5 c- A) C B$ O0 w" W
- {
5 T" v" ^: e6 e3 l - int max = 0;0 O$ r+ R0 R) `
- for(int i = index-wind ;i <index+wind;i++)) k( @ M" k( c8 V
- {
: i L0 b+ u+ }5 E8 Q - if(fft[i]>max)
+ {: V. G; t( F1 J - {
1 u# I: Q7 `; \: F4 a9 ^+ D - max =fft[i];
' K" G, k V1 _( F# D - }
. [3 W, n4 l5 J# `; S4 P) X' H - 5 X; p* I5 w. T9 D' u
- }
2 r* v$ A" C+ H# i& \ - return max;
! q/ c9 ~5 u1 l Z - * O) K" m3 P3 G! P2 }
- }
复制代码 $ l3 U: u! `8 N2 V% V4 A7 c
我们定义一个函数来寻找某点附近的最大值,这里之所以要寻找某点附近的最大值在这里说明一下。4 d& y- g8 A+ L" m# A( n
; a; v" ?# J- o* g+ j
FFT的索引和频率有关系,每个索引对应的频率之差为:采样频率/采样长度。我们的采样长度是1024,而采样频率是20kHZ,因此索引差为对应的频率差为19.53HZ,因为根据计算,1000HZ的频率对应的索引为51.2而由于索引只能是整数,因此在频谱上基波的最大值并不会是51.2而是51,因为int类型会抛弃小数。9 ?# R" T8 S* X& Y; ^: `% E& u
! }) H4 x8 [) b6 x+ b. O& i7 `所以我们实际统计的是984.3HZ或者1015.56HZ的频率,这样子我们计算谐波幅度进行翻倍的时候并不会是刚刚好好的对应的2KHZ的频率,而是其最大幅度会发生便宜。
1 g# c/ e, a7 } {
0 t0 |( Q7 W ^/ O9 k) `- R) g因此我们因为是寻找我们认为的谐波索引的附近最大值。8 G2 J" y) m" E. @
- for(int i = max;i<ADCLenth/2;i+=max)
$ ~5 k0 @1 \9 R. E# T# @ - {5 J+ c0 j+ M* d% ]
- maxW = FindMax(FFT_OutputBuf,i,max*0.3);
8 x0 D9 ]" }: W( K! A# O0 L+ x - all =all + maxW*maxW;
% ?: q9 v. Z: u5 d9 s# k - }/ t* e1 L3 ^8 s" o6 m
- all = all - maxValue*maxValue;
0 L/ ~5 m1 ]+ b% ~ -
" M+ J* C& s/ B' I - DHT = sqrtf(all)/maxValue;8 j. M- [) c! J
- printf("DHT:%f%c\r\n",DHT*100,'%');7 w6 T9 F* F* N3 N2 G% L+ W
- all = 0;
; O& U' B* H9 |% s( P& @ }7 R - max = 2;1 Q d1 M# [5 l0 ]1 A/ h2 Z9 C/ R
- maxValue = 0;
复制代码 9 o8 Q% e S; M3 `$ f* ~4 J
这样子就是统计我们的THD的值,之后将其打印,这里计算谐波幅度的时候我把基波的幅度也加了上去,我们将其去除。- F& {2 I; I6 t$ c" M0 n B
/ T. E$ [1 }6 U( X2 q9 I
7 B! a. e! b( Y$ e- A# H& `' c2 o7 i6 w3 l" ?4 @. b! b
可以看到外面的THD(图中打错了)计算外面的方波频率的THD在40%左右。
0 c1 _" @0 O/ Q9 t' ^我们利用MatLab生成一下方波信号之后用FFT来看一下理论结果。5 l+ M5 F" N6 L0 M
% B2 L& | c2 I' o( T' r, ~( [4 t4 o( l
6 d1 f: ]0 x( Y$ t$ |6 A! F4 k$ o" p# k3 A. ]+ K
% A' A+ x5 @: k; ~' u* b, H4 `% @" M/ X8 W
其统计到5次谐波对应的值计算出的THD的值43%,这和我们的计算结果也很接近了。
! e5 u9 J8 s2 k+ ^, U我们同样的看一下手机上的显示内容。
1 }# [' ~$ N5 Y4 z' L$ `! X7 X0 b e. A5 U0 z c
2 m1 D5 l. K6 U4 N, i3 g, L2 t
% F0 I @4 G; o9 v* @总谐波失真为42.7%' [1 V3 m# i; p6 h
; ~: _1 a: a, N4 f( b1 a# z
3 @' `! N* M' ]+ }( ~8 F转载自:电路小白2 o2 V( [' Z, o6 y. ?5 f
如有侵权请联系删除
3 t! A5 A# X9 m) w. ?7 Q. D
6 r6 F5 r: [0 V) ]+ N3 \$ C* A- z& I
! a x& [! Z0 |) k B* x* Z# Y1 c$ k4 U# N, A& I Q
|