Mục lục:

EasyFFT: Biến đổi Fourier nhanh (FFT) cho Arduino: 6 bước
EasyFFT: Biến đổi Fourier nhanh (FFT) cho Arduino: 6 bước

Video: EasyFFT: Biến đổi Fourier nhanh (FFT) cho Arduino: 6 bước

Video: EasyFFT: Biến đổi Fourier nhanh (FFT) cho Arduino: 6 bước
Video: EasyFFT: Fast Fourier Transform (FFT) for Arduino 2024, Tháng mười một
Anonim
Image
Image

Đo tần số từ tín hiệu thu được có thể là một nhiệm vụ khó khăn, đặc biệt là trên Arduino vì nó có sức mạnh tính toán thấp hơn. Có các phương pháp có sẵn để bắt tín hiệu vượt vạch 0 trong đó tần số được ghi lại bằng cách kiểm tra xem tín hiệu vượt qua vạch 0 trong thời gian nhất định bao nhiêu lần. Phương pháp như vậy có thể không hoạt động khi tín hiệu là sự kết hợp của nhiều tần số khác nhau.

Điều này bằng cách nào đó rất khó để viết mã nếu bạn không phải là người có nền tảng như vậy. Nhưng là một người mày mò, mã này có thể rất hữu ích cho các dự án khác nhau liên quan đến âm nhạc, phân tích tín hiệu. Động cơ của dự án này là chuẩn bị một mã dễ thực hiện trên Arduino mà không cần hiểu rõ về nó.

Dự án này không giải thích hoạt động của FFT nhưng giải thích ứng dụng của chức năng FFT. Quá trình tương tự cũng được giải thích trong video đính kèm.

Nếu bạn chỉ quan tâm đến ứng dụng của mã và không muốn giải thích về nó. Bạn có thể bỏ qua trực tiếp bước số 3.

Bước 1: Giới thiệu về Biến đổi tần số

Giới thiệu về Biến đổi tần số
Giới thiệu về Biến đổi tần số
Giới thiệu về Biến đổi tần số
Giới thiệu về Biến đổi tần số

Bất kỳ tín hiệu nào cũng có thể được tạo thành từ sự kết hợp của nhiều sóng hình sin khác nhau. Vì vậy, bất kỳ tín hiệu dựa trên thời gian nào cũng có thể được hiển thị dưới dạng kết hợp của nhiều sin khác nhau có biên độ khác nhau.

Tôi đã cố gắng giải thích hoạt động của DFT (biến đổi Fourier rời rạc) trong một trong những hướng dẫn trước đó (https://www.instructables.com/id/Arduino-Frequency…). Các phương pháp này cực kỳ chậm đối với bất kỳ ứng dụng thời gian thực nào. điều này làm cho nó gần như vô dụng.

Trong hình ảnh, một tín hiệu được hiển thị là sự kết hợp của hai tần số f2 và f5. Tín hiệu này được nhân với các sóng sin thử nghiệm có giá trị từ f1 đến f5.

Có thể chỉ ra về mặt toán học rằng - phép nhân của hai tập dữ liệu hài có tần số khác nhau có xu hướng bằng không (số lượng dữ liệu cao hơn có thể dẫn đến kết quả sai). Trong trường hợp của chúng ta, Nếu hai tần số nhân này có cùng tần số (hoặc rất gần) thì tổng của phép nhân là số khác không.

Vì vậy, nếu tín hiệu của chúng ta được nhân với tổng f1 của phép nhân sẽ bằng không (gần bằng 0 đối với ứng dụng thực tế). tương tự là trường hợp f3, f4. Tuy nhiên đối với giá trị, đầu ra f2 và f5 sẽ không bằng 0, nhưng cao hơn đáng kể so với các giá trị còn lại.

Ở đây một tín hiệu được thử nghiệm với 5 tần số, vì vậy tín hiệu cần được nhân với năm tần số. Tính toán dữ dội như vậy cần thời gian cao hơn. Về mặt toán học, người ta chỉ ra rằng với N số mẫu thì cần N * N phép nhân phức.

Bước 2: Chuyển đổi Fourier nhanh

Để làm cho việc tính toán DFT nhanh hơn, thuật toán FFT đã được phát triển bởi James Cooley và John Tukey. Thuật toán này cũng được coi là một trong những thuật toán quan trọng nhất của thế kỷ 20. Nó chia tín hiệu thành một phần lẻ và phần chẵn theo trình tự làm cho một số phép tính bắt buộc thấp hơn. Bằng cách sử dụng nó, tổng số phép nhân phức tạp cần thiết có thể được giảm xuống NlogN. đó là một cải tiến đáng kể.

Bạn có thể tham khảo các tài liệu tham khảo dưới đây mà tôi đã đề cập trong khi viết mã để hiểu chi tiết về toán học đằng sau FFT:

1.

2.

3.

4.

Bước 3: Giải thích mã

1. Hình sin nhanh và Cosine:

Tính toán FFT lấy giá trị của nhiều sin và cosin khác nhau nhiều lần. Chức năng sẵn có của Arduino không đủ nhanh và cần nhiều thời gian để cung cấp giá trị cần thiết. Điều này làm cho mã chậm hơn đáng kể (tăng gấp đôi thời gian cho 64 mẫu). Để chống lại vấn đề này, giá trị sine từ 0 đến 90 độ được lưu trữ dưới dạng bội số của 255. Làm như vậy sẽ loại bỏ nhu cầu sử dụng lưu trữ số dưới dạng float và chúng ta có thể lưu trữ nó dưới dạng byte chiếm 1/4 không gian trên Arduino. Sine_data cần phải dán ở đầu mã để khai báo nó như một biến toàn cục.

Ngoài sine_data, một mảng có tên f_peaks được khai báo là một biến toàn cục. Sau mỗi lần chạy hàm FFT, mảng này sẽ cập nhật. Trong đó f_peaks [0] là tần số chiếm ưu thế nhất và các giá trị khác theo thứ tự giảm dần.

byte sine_data [91] = {0, 4, 9, 13, 18, 22, 27, 31, 35, 40, 44, 49, 53, 57, 62, 66, 70, 75, 79, 83, 87, 91, 96, 100, 104, 108, 112, 116, 120, 124, 127, 131, 135, 139, 143, 146, 150, 153, 157, 160, 164, 167, 171, 174, 177, 180, 183, 186, 189, 192, 195, 198, 201, 204, 206, 209, 211, 214, 216, 219, 221, 223, 225, 227, 229, 231, 233, 235, 236, 238, 240, 241, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 253, 254, 254, 254, 255, 255, 255, 255}; float f_peaks [5];

Vì chúng tôi đã lưu trữ giá trị của sin từ 0 đến 90 độ, bất kỳ giá trị nào của sin hoặc cosine đều có thể được tính toán. Dưới đây hàm vòng đầu tiên của số đến dấu thập phân 0 và trả về giá trị từ dữ liệu được lưu trữ. phương pháp này chỉ cần một phép chia động. Điều này có thể được giảm hơn nữa bằng cách lưu trữ trực tiếp các giá trị sin (không phải là bội số 255). nhưng điều đó tiêu tốn nhiều bộ nhớ trên Arduino.

Sử dụng quy trình trên làm giảm độ chính xác nhưng cải thiện tốc độ. Đối với 64 điểm, nó mang lại lợi thế là 8ms và đối với 128 điểm, nó mang lại lợi thế là 20ms.

Bước 4: Giải thích mã: Chức năng FFT

FFT chỉ có thể được thực hiện cho kích thước mẫu là 2, 4, 8, 16, 32, 64, v.v. nếu giá trị không phải là 2 ^ n, thì nó sẽ lấy giá trị phía dưới của giá trị. Ví dụ, nếu chúng ta chọn kích thước mẫu là 70 thì nó sẽ chỉ xem xét 64 mẫu đầu tiên và bỏ qua phần còn lại.

Luôn luôn khuyến nghị có kích thước mẫu là 2 ^ n. có thể là:

2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, …

Hai float out_r và out_im sẽ chiếm dung lượng bộ nhớ cao. đối với Arduino nano sẽ không hoạt động đối với các mẫu cao hơn 128 (và trong một số trường hợp là 128) do thiếu bộ nhớ khả dụng.

dữ liệu int unsigned [13] = {1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048};

int a, c1, f, o, x; a = N; for (int i = 0; i <12; i ++) // tính các mức {if (data <= a) {o = i;}} int in_ps [data [o] = {}; // đầu vào để giải trình tự float out_r [data [o] = {}; // phần thực của biến đổi float out_im [data [o] = {}; // phần hình ảnh của phép biến đổi

Quy trình tiếp theo như sau:

1. Mã tạo ra một bit đảo ngược thứ tự cho kích thước mẫu đã cho (chi tiết về việc đảo ngược bit trên tài liệu tham khảo: bước 2)

2. Dữ liệu đầu vào được sắp xếp theo thứ tự đã tạo, 3. FFT thực hiện

4. Biên độ của số phức được tính toán, 5. Các đỉnh được phát hiện và sắp xếp theo thứ tự giảm dần

6. kết quả có thể được truy cập từ f_peaks.

[để truy cập dữ liệu khác (ngoài tần số cao nhất), mã phải được sửa đổi, để biến cục bộ có thể được sao chép sang một số biến toàn cục được xác định trước]

Bước 5: Kiểm tra mã

Kiểm tra mã
Kiểm tra mã
Kiểm tra mã
Kiểm tra mã

Một sóng tam giác mẫu được đưa ra làm đầu vào. đối với tần số lấy mẫu sóng này là 10 Hz và tần số của chính sóng là 1,25 Hz.

Như có thể được hiển thị từ đầu ra thô, giá trị phù hợp với FFT được Scilab tính toán. tuy nhiên, những giá trị này không hoàn toàn giống như chúng ta có độ chính xác thấp nhưng sóng sin nhanh hơn.

Ở đầu ra tần số mảng là 1,25 và 3,75. không nhất thiết phải lấy giá trị chính xác mọi lúc. thông thường những con số này được gọi là tần số. vì vậy giá trị đầu ra có thể ở bất kỳ đâu trong các thùng được chỉ định.

Tốc độ, vận tốc:

đối với Arduino nano, nó cần:

16 Điểm: 4ms32 Điểm: 10ms 64 Điểm: 26ms 128 Điểm: 53ms

Bước 6: Kết luận

Mã FFT này có thể được sử dụng trong các ứng dụng thời gian thực. Vì nó mất khoảng 30 mili giây để hoàn thành phép tính. Tuy nhiên, độ phân giải của nó bị giới hạn bởi một số mẫu. Số lượng mẫu được giới hạn bởi bộ nhớ Arduino. Bằng cách sử dụng Arduino Mega hoặc các bảng hiệu suất cao hơn, độ chính xác có thể được cải thiện.

nếu bạn có bất kỳ câu hỏi, đề xuất hoặc sửa chữa, vui lòng bình luận.

Cập nhật (2/5/21)

Cập nhật: // ----------------------------- Chức năng FFT --------------- ------------------------------- // float FFT (int in , int N, float Frequency)

Kiểu dữ liệu của N đã thay đổi thành Số nguyên (Byte hiện có) để hỗ trợ kích thước mẫu> 255. Nếu kích thước mẫu là <= 128, kiểu dữ liệu byte nên được sử dụng.

Đề xuất: