ลงมือทำ: ฟิลเตอร์ทำสัญญาณให้สะอาดสด ๆ
โมดูล 4 — วิเคราะห์สัญญาณ · สไลด์: slides.md · ภาพรวมโมดูล · หน้าหลักสูตร
เติมห้าจุดใน s08_filters.py ให้สร้างฟิลเตอร์จากชื่อด้วย keyword argument อ่านขนาดความเร่ง ป้อนเข้า filt.update และวาดกราฟดิบเทียบกรอง แล้ววัดผลด้วย noise down % และเทียบสามฟิลเตอร์กับสัญญาณเดียวกัน
เป้าหมาย
หัวข้อที่มีชื่อว่า “เป้าหมาย”เมื่อจบบทเรียนนี้ คุณจะ:
- เติมห้าจุดใน practice/s08_filters.py จนสองกราฟต่างกันชัด (เส้นล่างเรียบกว่าเส้นบน) และเลือก EMA, Median, Kalman1D ใน dropdown ได้ครบโดยไม่เกิด error
- จดค่า noise down % ของสามฟิลเตอร์กับสัญญาณเดียวกัน และสร้างสถานการณ์ที่ Median ชนะ EMA ชัดเจนพร้อมอธิบายเหตุผล
- อธิบายได้ว่าทำไมบรรทัด y = filt.update(x) บรรทัดเดียวใช้ได้กับทุกฟิลเตอร์ และทำไม noise down % วัดจากการกระตุกระหว่างเฟรม
ก่อนเริ่ม
หัวข้อที่มีชื่อว่า “ก่อนเริ่ม”ผ่านบทเรียน 4.1 มาแล้ว รู้จักนิสัยของ EMA, Median และ Kalman1D เตรียมบันทึกการเรียนไว้จดค่า noise down % ของแต่ละฟิลเตอร์
- อุปกรณ์: บอร์ด TESAIoT Dev Kit ที่ลงเฟิร์มแวร์ MicroPython ของ BENTO แล้ว หรือ BENTO Emulator ใน BENTO IDE — โหมด IMU ครบบน Emulator ส่วนโหมด Radar range บน Emulator เป็นระยะจำลองจากลูกบิด ไม่มี multipath จริง
- เรียนมาก่อน: บทเรียน 4.1 — ฟิลเตอร์ DSP: EMA, Median, Kalman และ radar range profile
ทั้งไฟล์อ่านเป็นประโยคเดียว: อ่านค่าดิบ → ป้อนเข้าฟิลเตอร์ → วาดดิบเทียบกรอง → วัดว่าลด noise ได้กี่เปอร์เซ็นต์ วนทุก 60 ms
ฟิลเตอร์ถูกสร้างใน “ขั้นสร้างครั้งเดียว” ผ่าน make_filter(name) ที่แปลงชื่อเป็นอ็อบเจกต์: EMA ให้ไว้แล้วเป็นตัวอย่าง เราเติม
return dsp.Median(window=MED_WINDOW) และ return dsp.Kalman1D(q=KAL_Q, r=KAL_R) ต้องเป็น keyword เสมอ
ถ้าลืมเติมสองจุดนี้ แอปยังรันได้ด้วย EMA แต่พอเลือก Median หรือ Kalman ฟังก์ชันจะคืน None แล้วพังตอนเรียก update
read_raw() อ่าน sensors.bmi270.acceleration() แล้วยุบเป็นขนาด mag = (ax*ax + ay*ay + az*az) ** 0.5 (นิ่งราว 9.8)
คูณ 10 ให้เห็นชัดบนกราฟ 0..250 เพราะฟิลเตอร์ของบทเรียนนี้เป็นหนึ่งมิติ จึงต้องแปลงเวกเตอร์เป็นสเกลาร์ก่อน โหมด Radar range
อ่าน sensors.radar_range() เป็นเซนติเมตรโดยห่อ try/except OSError ไว้ หัวใจของบทเรียนคือบรรทัดเดียว y = filt.update(x)
ที่ไม่ต้องรู้ว่า filt เป็นฟิลเตอร์ชนิดไหน เพราะทุกตัวใช้ API เดียวกัน ถ้าลืมเติม (y = x) สองกราฟจะเหมือนกันเป๊ะ
ซึ่งเป็นการทดสอบง่าย ๆ ว่าเติมถูกหรือยัง
เราวัดความเรียบจาก การกระตุกระหว่างเฟรม ผลรวมของ |x − x_prev| ของเส้นดิบเทียบเส้นกรอง แล้ว
red = (1 − filt_jit / raw_jit) × 100 เพราะกับเซนเซอร์จริงเราไม่มีเฉลยว่าค่าจริงคือเท่าไร การกระตุกที่ลดลงจึงเป็นตัวชี้ที่ตรงไปตรงมา
ความสำเร็จไม่ใช่ “เส้นดูสวย” แต่ต้องบอกได้ว่าทำไม Median ชนะ EMA ตอนเจอ spike และทำไม α เล็กลงจึงเรียบขึ้นแต่ตามช้าลง
ตัวอย่างสมบูรณ์
หัวข้อที่มีชื่อว่า “ตัวอย่างสมบูรณ์”s08_filters_full.py เพิ่ม slider จูนพารามิเตอร์สดตามชนิดฟิลเตอร์ (α ของ EMA, window ของ Median, r ของ Kalman) แถบ noise down
ที่เปลี่ยนสีตามเกณฑ์ และปุ่ม Freeze หยุดภาพไว้เทียบหนามกับเส้นเรียบ
| ไฟล์ | ไฟล์นี้สอน |
|---|---|
| examples/s08_filters_full.py | ทำสัญญาณให้สะอาดด้วยฟิลเตอร์ DSP (ฉบับเต็ม) |
ฝึกเติม
หัวข้อที่มีชื่อว่า “ฝึกเติม”คอมเมนต์ # เติม: ห้าจุดอยู่ที่บรรทัด 46 (Median), 49 (Kalman1D), 99 (ขนาดความเร่ง), 144 (filt.update) และ 148 (วาดสองกราฟ)
เติมทีละจุด แล้วขยับบอร์ด สลับฟิลเตอร์ครบสามตัว ถ้าสองกราฟไม่ต่างกัน ให้ตรวจจุดที่ 144 ก่อน
| ไฟล์ฝึก | เรื่อง |
|---|---|
| practice/s08_filters.py | ทำสัญญาณให้สะอาดด้วยฟิลเตอร์ DSP (ฉบับฝึกเติมโค้ด) |
เปิดเฉลยหลังจากลองเองแล้วอย่างน้อยหนึ่งรอบ และอ่าน วิธีใช้เฉลย ก่อน
| เฉลย | คู่กับ |
|---|---|
| solution/s08_filters.py | practice/s08_filters.py |
เช็กความเข้าใจ
หัวข้อที่มีชื่อว่า “เช็กความเข้าใจ”คำถามชุดเดียวกันอยู่ใน quiz.yaml สำหรับระบบที่ตรวจอัตโนมัติ
-
เติมไฟล์แล้วแต่เส้น Filtered เหมือนเส้น Raw ทุกจุดไม่ว่าจะเลือกฟิลเตอร์ใด จุดใดยังว่าง (เลือกหนึ่งข้อ · เป้าหมายข้อ 1)
- ก) return dsp.Median(window=MED_WINDOW)
- ข) mag = (axax + ayay + az*az) ** 0.5
- ค) y = filt.update(x)
- ง) raw_chart.value(clamp(x))
เฉลย
ค — placeholder y = x ทำให้ค่าที่วาดบนเส้นล่างคือค่าดิบ สองกราฟต่างกันก็ต่อเมื่อค่าผ่าน filt.update จริง
-
เขียน return dsp.Median(5) ในจุดที่ 1 แล้วเลือก Median จะเกิดอะไร (เลือกหนึ่งข้อ · เป้าหมายข้อ 1)
- ก) ได้หน้าต่าง 5 ค่าตามต้องการ
- ข) โยน TypeError เพราะ window เป็นพารามิเตอร์แบบ keyword เท่านั้น
- ค) ได้หน้าต่าง 6 ค่า
- ง) ฟิลเตอร์กลายเป็น EMA
เฉลย
ข — พารามิเตอร์ของฟิลเตอร์ใน dsp เป็น keyword-only ต้องเขียน dsp.Median(window=5) การส่งแบบ positional จะถูกปฏิเสธทันที
-
เคาะบอร์ดแรงครั้งเดียวให้เกิด spike ผลที่คาดว่าจะเห็นบนเส้น Filtered ของ EMA เทียบกับ Median คืออะไร (เลือกหนึ่งข้อ · เป้าหมายข้อ 2)
- ก) ทั้งสองตัดทิ้งได้เท่ากัน
- ข) EMA เกลี่ยเป็นโหนกนุ่มที่ยังเห็น ส่วน Median โหวต spike ตกไปจนแทบไม่เห็น
- ค) EMA ตัดทิ้งหมด ส่วน Median เกลี่ยเป็นโหนก
- ง) ทั้งสองทำให้เส้นกลายเป็นศูนย์
เฉลย
ข — EMA ถ่วงค่าโดดเข้าไปในผลลัพธ์บางส่วน Median เลือกค่ากลางจึงไม่สนค่าโดดเดี่ยว นี่คือความต่างของ “เกลี่ย” กับ “โหวตตัด”
-
raw_jit = 400 และ filt_jit = 100 ค่า noise down % เป็นเท่าไร (เลือกหนึ่งข้อ · เป้าหมายข้อ 3)
- ก) 25%
- ข) 75%
- ค) 100%
- ง) 400%
เฉลย
ข — red = (1 − 100/400) × 100 = 75% การกระตุกของเส้นกรองเหลือหนึ่งในสี่ของเส้นดิบ
MVP ของชุดบทเรียน 4.1–4.2: ฟิลเตอร์ปรับสัญญาณเซนเซอร์ที่มี noise ให้ดีขึ้นอย่างเห็นได้ เส้น Filtered เรียบกว่า Raw ชัดเจน พร้อมตัวเลข noise down % ยืนยัน
- เติมไฟล์ฝึกครบห้าจุด รันได้บน Emulator หรือบอร์ด
- สลับครบสามฟิลเตอร์กับสัญญาณเดียวกัน แล้วจด noise down % ของแต่ละตัวลงบันทึกการเรียน
- สร้าง spike (เคาะบอร์ดแรงครั้งเดียว หรือใช้ radar บนบอร์ด) แล้วอธิบายว่าทำไม Median ชนะ EMA
- อธิบายได้ว่าเลือกฟิลเตอร์ตัวไหน เพราะอะไร และข้อแลกเปลี่ยนเรียบกับตอบสนองคืออะไร
ชุดบทเรียนถัดไป (บทเรียน 4.3–4.4) เราจะมองสัญญาณเดียวกันในโดเมนความถี่ด้วย FFT
บทเรียนถัดไป: บทเรียน 4.3 — FFT และโดเมนความถี่: bin, Nyquist, DC, leakage และ Hann window
สะท้อนคิด
หัวข้อที่มีชื่อว่า “สะท้อนคิด”- ถ้าต่อ Median แล้วตามด้วย EMA คุณคาดว่าจะได้ผลแบบไหน และต้องแลกกับอะไร
- noise down % สูงที่สุดเสมอเป็นสิ่งที่ดีที่สุดหรือไม่ ลองนึกถึงงานที่ต้องตอบสนองเร็ว
คำถามทบทวน
ลองตอบเองก่อน แล้วค่อยเปิดดูเฉลย
-
เติมไฟล์แล้วแต่เส้น Filtered เหมือนเส้น Raw ทุกจุดไม่ว่าจะเลือกฟิลเตอร์ใด จุดใดยังว่าง (เป้าหมายข้อ 1)
- return dsp.Median(window=MED_WINDOW)
- mag = (ax*ax + ay*ay + az*az) ** 0.5
- y = filt.update(x)
- raw_chart.value(clamp(x))
ดูเฉลย
คำตอบ: C. y = filt.update(x)
placeholder y = x ทำให้ค่าที่วาดบนเส้นล่างคือค่าดิบ สองกราฟต่างกันก็ต่อเมื่อค่าผ่าน filt.update จริง
-
เขียน return dsp.Median(5) ในจุดที่ 1 แล้วเลือก Median จะเกิดอะไร (เป้าหมายข้อ 1)
- ได้หน้าต่าง 5 ค่าตามต้องการ
- โยน TypeError เพราะ window เป็นพารามิเตอร์แบบ keyword เท่านั้น
- ได้หน้าต่าง 6 ค่า
- ฟิลเตอร์กลายเป็น EMA
ดูเฉลย
คำตอบ: B. โยน TypeError เพราะ window เป็นพารามิเตอร์แบบ keyword เท่านั้น
พารามิเตอร์ของฟิลเตอร์ใน dsp เป็น keyword-only ต้องเขียน dsp.Median(window=5) การส่งแบบ positional จะถูกปฏิเสธทันที
-
เคาะบอร์ดแรงครั้งเดียวให้เกิด spike ผลที่คาดว่าจะเห็นบนเส้น Filtered ของ EMA เทียบกับ Median คืออะไร (เป้าหมายข้อ 2)
- ทั้งสองตัดทิ้งได้เท่ากัน
- EMA เกลี่ยเป็นโหนกนุ่มที่ยังเห็น ส่วน Median โหวต spike ตกไปจนแทบไม่เห็น
- EMA ตัดทิ้งหมด ส่วน Median เกลี่ยเป็นโหนก
- ทั้งสองทำให้เส้นกลายเป็นศูนย์
ดูเฉลย
คำตอบ: B. EMA เกลี่ยเป็นโหนกนุ่มที่ยังเห็น ส่วน Median โหวต spike ตกไปจนแทบไม่เห็น
EMA ถ่วงค่าโดดเข้าไปในผลลัพธ์บางส่วน Median เลือกค่ากลางจึงไม่สนค่าโดดเดี่ยว นี่คือความต่างของ "เกลี่ย" กับ "โหวตตัด"
-
raw_jit = 400 และ filt_jit = 100 ค่า noise down % เป็นเท่าไร (เป้าหมายข้อ 3)
- 25%
- 75%
- 100%
- 400%
ดูเฉลย
คำตอบ: B. 75%
red = (1 − 100/400) × 100 = 75% การกระตุกของเส้นกรองเหลือหนึ่งในสี่ของเส้นดิบ
อ้างอิงบทเรียนนี้
ถ้านำบทเรียนนี้ไปสอน ทำสไลด์ หรือทำเอกสารต่อ ให้อ้างอิงด้วยข้อความนี้ ถ้าดัดแปลงเนื้อหา ให้เติม (ดัดแปลง)ต่อท้ายชื่อบทเรียน
"ลงมือทำ: ฟิลเตอร์ทำสัญญาณให้สะอาดสด ๆ" จาก TESA Open Knowledge โดยสมาคมสมองกลฝังตัวไทย (Thai Embedded Systems Association: TESA) https://github.com/tesaiot/tesa-qualification-program สัญญาอนุญาต CC BY-NC 4.0
ข้อความอ้างอิงภาษาอังกฤษ: "Hands-on: cleaning a live signal with a filter" from TESA Open Knowledge by the Thai Embedded Systems Association (TESA), https://github.com/tesaiot/tesa-qualification-program, licensed under CC BY-NC 4.0
ลิงก์บทเรียน: https://tesaiot.github.io/tesa-qualification-program/courses/edge-ai-developer/m04-analysis/l02-filters-lab/
TESA Open Knowledge · © 2026 สมาคมสมองกลฝังตัวไทย (TESA) · CC BY-NC 4.0
เนื้อหาเผยแพร่ภายใต้ CC BY-NC 4.0 นำไปใช้ต่อในงานที่ไม่ใช่เพื่อการค้าได้ โปรดอ้างอิงสมาคมสมองกลฝังตัวไทย (TESA) ทุกครั้ง · วิธีอ้างอิง TESA