ไอเดียคร่าวๆ
ไอเดียของโปรเจคนี้คือการเอา DFT ไปรันบนไมโครคอนโทรลเลอร์ AVR 8 บิต ด้วยอัลกอริทึมแบบง่ายสุดๆ แล้วให้มันแสดงผลการแปลงฟูเรียร์ของสัญญาณเสียงแบบเรียลไทม์ได้เลย
ก่อนจะเริ่มลงมือกัน มาดูกันก่อนว่าน้องต้องรู้อะไรบ้าง:
- DFT - สมการและการตีความทางคณิตศาสตร์
- ไมโครคอนโทรลเลอร์ตระกูล AVR: พื้นฐานและการเขียนโปรแกรมด้วย C
- อิเล็กทรอนิกส์สำหรับงานเสียง
ถ้าพร้อมแล้วกับหัวข้อพวกนั้น... มาจัดการกันเลย!
อย่างที่รู้กัน 'เสียง' เกิดจากการรบกวนทางกายภาพในตัวกลาง สิ่งนี้สร้างขึ้นด้วยลำโพงที่แปลงสัญญาณไฟฟ้ารูปไซน์ให้เป็นการกระจัด ดังนั้นคลื่นเสียงทั้งหมดก็มาจากสัญญาณไซน์นั่นแหละ และคลื่นใดๆ ก็สามารถแสดงเป็นผลรวมแบบถ่วงน้ำหนักของคลื่นไซน์ที่มีความถี่เชิงมุมเป็นพหุคูณของความถี่ฐานได้ สมการด้านล่างคือรูปแบบทางคณิตศาสตร์ของคลื่นใดๆ โดยที่น้ำหนัก (weights) ก็คือแอมพลิจูดของความถี่นั้นๆ เอง อัลกอริทึม DFT จะทำหน้าที่หา weights พวกนี้สำหรับความถี่ต่างๆ เมื่อเราให้สัญญาณที่ไม่รู้จักเข้าไป
อัลกอริทึม DFT
ทีนี้ DFT สามารถคำนวณได้จากสมการนี้:
สมการด้านบนนี่เอาไปเขียนใน C ได้ตรงๆ เลย แต่ก่อนจะเข้าสู่โค้ด มาดูวงจรกันก่อน:
ลงมือต่อวงจร
สำหรับวงจรนี้ เราใช้ ATmega328P กับ LCD JHD162A นะ LCD ต่อเข้ากับ PORT-D ของ MCU และตั้งค่าให้ทำงานในโหมดบัสข้อมูล 4 บิต ส่วน LM386 นั้นเป็นแอมป์เสียงโดยเฉพาะ หน้าที่ของมันคือขยายสัญญาณเสียงซึ่งปกติจะมีแอมพลิจูดสูงสุดแค่ประมาณ 0.7V เพื่อผลลัพธ์ที่แม่นยำ เราต้องปรับค่าโพเทนชิออมิเตอร์ 10K ให้เหมาะสม รวมถึงจัดวางตัวเก็บประจุให้ดีเพื่อลดสัญญาณรบกวนให้เหลือน้อยที่สุด
การลงมือทำจริง: ตารางไซน์-โคไซน์ และการแสดงผลแบบฮิสโตแกรม
โปรเจคนี้เผยให้เห็นเลเยอร์ต่างๆ ของการทำงานแบบง่ายๆ ตั้งแต่รับสัญญาณไปจนถึงแสดงผล:
- เลเยอร์ตรวจจับ: ADC ใน ATMega328p
- เลเยอร์แปลงสัญญาณ: ระบบใช้พินดิจิทัลความเร็วสูงเพื่อรับสถานะบิตแบบเร่งด่วน เพื่อประสานงานงานสำคัญๆ ด้านการรับรู้
- เลเยอร์อินเทอร์เฟซสเปกตรัม: LCD 16x2 แบบคัสตอมแคร็กเตอร์
- เลเยอร์อินเทอร์เฟซเสียง: แอมป์ LM386
- ลอจิกการประมวลผล: โค้ด C ใช้กลยุทธ์ "ตารางค้นหา" (หรือ DFT-dispatch): มันจะตีความค่าสัญญาณเสียงที่สุ่มมา แล้วจับคู่กับบินความถี่ต่างๆ เพื่อให้การวิเคราะห์สเปกตรัมภาพที่ปลอดภัยและเป็นจังหวะ โดยไม่ต้องพึ่งฟังก์ชันใน
math.hที่คำนวณช้า
หลังจากอัพโหลดโค้ดเสร็จ พอเริ่มต้น ตัวคอนโทรลเลอร์จะเซ็ตพิน I/O จากนั้นตั้งค่า ADC โหลดไดรเวอร์ LCD แล้วก็รันแอนิเมชั่นตอนบูต...
ส่วนโค้ด DFT จะเริ่มรันตั้งแต่บรรทัดที่ 7 นับจาก 'int main()'
ในที่นี้เราใช้ DFT 32 จุด เพื่อให้สามารถจำแนกสัญญาณเสียงเข้าเป็น 16 ความถี่ได้ สำหรับ DFT 32 จุด นี้ MCU ต้องคำนวณค่า ไซน์และโคไซน์ทั้งหมด 512 ค่า ถ้าใช้ฟังก์ชันจากไลบรารี math.h การคำนวณจะใช้เวลานานมาก ดังนั้นเราจึงสร้างตารางค้นหา (lookup table) เก็บไว้ในแฟลชของ MCU แทน ทำให้การคำนวณเร็วขึ้นมากและแสดงผลแบบเรียลไทม์ได้สำเร็จ สู้งานนะน้อง!
พอโปรแกรมเคาน์เตอร์วิ่งเข้ามาในส่วนแอปพลิเคชัน (ลูปอนันต์) อันดับแรกมันจะทำการสุ่มตัวอย่างอินพุต แล้วส่งต่อไปให้ DFT ผลลัพธ์จะแสดงออกมาในรูปแบบกราฟแท่ง (ฮิสโตแกรม) บน LCD ซึ่งเราเรียกมันว่า 'บิน' (bins) นั่นแหละ
ที่นี่เราใช้ Character LCD ธรรมดา เลยต้องมานั่งกำหนดรูปแท่งกราฟเองด้วยการเขียนโค้ดเพิ่ม LCD รุ่นนี้มันให้เราสร้าง 8 ตัวอักษรกำหนดเอง (Custom Character) ได้ โดยแต่ละตัวใช้พื้นที่ 8 ไบต์ ในซอร์สโค้ดพี่เขียนฟังก์ชันสำหรับสร้างตัวอักษรพวกนี้ไว้ให้แล้ว จัดไปวัยรุ่น
ส่วนสุ่มตัวอย่างเสียง (Audio Sampling)
มาเข้าเรื่องการสุ่มตัวอย่างเสียงกันดีกว่า พอ MCU เริ่มทำงาน มันจะเข้าสู่โหมด 0 (mode0) โดยอัตโนมัติ ซึ่งมีความถี่สุ่มตัวอย่าง (Sampling Frequency) อยู่ที่ 17.920 Ksps เราก็เลยได้ช่วงความถี่ที่วิเคราะห์ได้ประมาณ 560 - 8960 Hz ตามเกณฑ์ของ Nyquist ไงล่ะ อัตราการสุ่มตัวอย่างนี่กำหนดด้วยค่า Prescaler ของ ADC ซึ่งเซ็ตไว้ในรีจิสเตอร์ ADCSRA ของ MCU
ปุ่ม 'SW' เนี่ย เอาไว้ใช้สลับช่วงความถี่นั่นเอง คือเริ่มต้นจะอยู่ที่ 560 - 8960 Hz (mode0) พอกดปุ่มมันจะเปลี่ยนเป็น 280 - 4480 Hz (mode1, อัตราสุ่มตัวอย่าง = 8960 Hz) พอกดอีกทีก็กลับมาที่โหมดเริ่มต้น สรุปคือปุ่มนี้มันไปสลับค่าในรีจิสเตอร์ ADCSRA นั่นเอง (อยากรู้ลึกกว่านี้ ไปเปิด datasheet ของ MCU ดูนะน้อง ห้ามช็อตนะตัวนี้)
ผลลัพธ์จาก DFT จะออกมาในช่วง 0 - 32 (ประมาณนี่แหละ) (ต้องปรับ Trimmer Pot ให้เหมาะสม หรือไม่ก็เพิ่มโค้ดสำหรับหารค่าผลลัพธ์เพื่อปรับสเกล) พี่เลยเอามาหาร 2 เพื่อปรับสเกลก่อนส่งไปแสดงผลบน LCD (ดูในโค้ดได้)
สำคัญมาก: ถ้าอยากให้การวิเคราะห์สเปกตรัมแม่นยำ อย่าลืมทำ ADC Offset Calibration (ค่ากลางมักจะอยู่ที่ 511 บิต) ในโค้ดด้วย! และต้องมั่นใจว่าตั้ง อัตราการสุ่มตัวอย่าง (Sampling Rate) (เช่น 17.92 Ksps) ให้เหมาะสมกับช่วงความถี่เสียงที่ต้องการวิเคราะห์ทุกครั้ง!
รูป DFT เมื่อให้อินพุตและโหมดต่างกัน:
ทีนี้คำถามก็คือ 'เจ้าเครื่องนี้มันแม่นยำแค่ไหน?' เนื่องจากพี่ใช้วิธี Lookup Table เพื่อประหยัดพื้นที่ใน Flash พี่เลยเก็บค่าจำนวนเต็มของ sine กับ cosine โดยคูณมันขึ้นมา 1000 เท่า แล้วค่อยหารผลลัพธ์สุดท้ายลง 1000 เท่า มันเลยไม่ใช่เครื่องมือที่แม่นยำสุดๆ แต่เรื่องความแม่นยำเนี่ย เราต้องหาจุดสมดุลระหว่างความแม่นยำกับความเร็วให้ดี พี่เลือกแสดงผลแบบเรียลไทม์ เลยยอมลดความแม่นยำลงนิดหน่อย แต่มันก็ยังมีความแม่นยำและความเร็วที่ใช้การได้ดีเลยล่ะ เมื่อเทียบกับอุปกรณ์ที่ใช้ฟังก์ชันจากไลบรารี math.h
สาธิตการทำงาน
นี่คือวิดีโอสาธิต DFT Audio Analyser กำลังวิเคราะห์เพลง 'flute' โดย Tony Igy
แนวทางการพัฒนาต่อ
- เพิ่ม OLED Identity Dashboard: ต่อจอ OLED ขนาดเล็กเพื่อแสดงข้อมูลเช่น "ความถี่สูงสุด (Peak Frequency)" หรือเมตริกอื่นๆ
- เพิ่ม Multi-sensor Sync Synchronization: ต่อโมดูลบลูทูธเฉพาะทาง เพื่อทำการวิเคราะห์ "Wireless Spectrogram" แบบไร้สายด้วยความแม่นยำสูงขึ้น
- เพิ่ม Cloud Interface Registration Support: สร้างเว็บแดชบอร์ดเฉพาะทางบนสมาร์ทโฟนผ่าน WiFi/BT เพื่อติดตามและบันทึกประวัติการวิเคราะห์เสียงได้อย่างแม่นยำ
- เพิ่ม Advanced Profile Customization Support: บูรณาการอัลกอริทึมเฉพาะทาง เพื่อให้สามารถเปลี่ยนค่า Trigger อัตโนมัติตามอินพุตจากผู้ใช้หรือลักษณะของสัญญาณได้
DFT Audio Analyser เป็นโปรเจกต์ที่เพอร์เฟกต์สำหรับสายวิทย์ที่กำลังมองหาเครื่องมือสำหรับวิเคราะห์สัญญาณแบบอินเทอร์แอคทีฟและน่าสนใจ! สู้งานนะน้อง
สำหรับโปรเจกต์ Arduino และ AVR อื่นๆ