ข้ามไปยังเนื้อหา

ลงมือทำ: ฟิลเตอร์ทำสัญญาณให้สะอาดสด ๆ

โมดูล 4 — วิเคราะห์สัญญาณ · สไลด์: slides.md · ภาพรวมโมดูล · หน้าหลักสูตร

เติมห้าจุดใน s08_filters.py ให้สร้างฟิลเตอร์จากชื่อด้วย keyword argument อ่านขนาดความเร่ง ป้อนเข้า filt.update และวาดกราฟดิบเทียบกรอง แล้ววัดผลด้วย noise down % และเทียบสามฟิลเตอร์กับสัญญาณเดียวกัน

เมื่อจบบทเรียนนี้ คุณจะ:

  1. เติมห้าจุดใน practice/s08_filters.py จนสองกราฟต่างกันชัด (เส้นล่างเรียบกว่าเส้นบน) และเลือก EMA, Median, Kalman1D ใน dropdown ได้ครบโดยไม่เกิด error
  2. จดค่า noise down % ของสามฟิลเตอร์กับสัญญาณเดียวกัน และสร้างสถานการณ์ที่ Median ชนะ EMA ชัดเจนพร้อมอธิบายเหตุผล
  3. อธิบายได้ว่าทำไมบรรทัด y = filt.update(x) บรรทัดเดียวใช้ได้กับทุกฟิลเตอร์ และทำไม noise down % วัดจากการกระตุกระหว่างเฟรม

ผ่านบทเรียน 4.1 มาแล้ว รู้จักนิสัยของ EMA, Median และ Kalman1D เตรียมบันทึกการเรียนไว้จดค่า noise down % ของแต่ละฟิลเตอร์

ทั้งไฟล์อ่านเป็นประโยคเดียว: อ่านค่าดิบ → ป้อนเข้าฟิลเตอร์ → วาดดิบเทียบกรอง → วัดว่าลด 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 สำหรับระบบที่ตรวจอัตโนมัติ

  1. เติมไฟล์แล้วแต่เส้น 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 จริง

  2. เขียน return dsp.Median(5) ในจุดที่ 1 แล้วเลือก Median จะเกิดอะไร (เลือกหนึ่งข้อ · เป้าหมายข้อ 1)

    • ก) ได้หน้าต่าง 5 ค่าตามต้องการ
    • ข) โยน TypeError เพราะ window เป็นพารามิเตอร์แบบ keyword เท่านั้น
    • ค) ได้หน้าต่าง 6 ค่า
    • ง) ฟิลเตอร์กลายเป็น EMA
    เฉลย

    ข — พารามิเตอร์ของฟิลเตอร์ใน dsp เป็น keyword-only ต้องเขียน dsp.Median(window=5) การส่งแบบ positional จะถูกปฏิเสธทันที

  3. เคาะบอร์ดแรงครั้งเดียวให้เกิด spike ผลที่คาดว่าจะเห็นบนเส้น Filtered ของ EMA เทียบกับ Median คืออะไร (เลือกหนึ่งข้อ · เป้าหมายข้อ 2)

    • ก) ทั้งสองตัดทิ้งได้เท่ากัน
    • ข) EMA เกลี่ยเป็นโหนกนุ่มที่ยังเห็น ส่วน Median โหวต spike ตกไปจนแทบไม่เห็น
    • ค) EMA ตัดทิ้งหมด ส่วน Median เกลี่ยเป็นโหนก
    • ง) ทั้งสองทำให้เส้นกลายเป็นศูนย์
    เฉลย

    ข — EMA ถ่วงค่าโดดเข้าไปในผลลัพธ์บางส่วน Median เลือกค่ากลางจึงไม่สนค่าโดดเดี่ยว นี่คือความต่างของ “เกลี่ย” กับ “โหวตตัด”

  4. 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 % สูงที่สุดเสมอเป็นสิ่งที่ดีที่สุดหรือไม่ ลองนึกถึงงานที่ต้องตอบสนองเร็ว

คำถามทบทวน

ลองตอบเองก่อน แล้วค่อยเปิดดูเฉลย

  1. เติมไฟล์แล้วแต่เส้น Filtered เหมือนเส้น Raw ทุกจุดไม่ว่าจะเลือกฟิลเตอร์ใด จุดใดยังว่าง (เป้าหมายข้อ 1)

    1. return dsp.Median(window=MED_WINDOW)
    2. mag = (ax*ax + ay*ay + az*az) ** 0.5
    3. y = filt.update(x)
    4. raw_chart.value(clamp(x))
    ดูเฉลย

    คำตอบ: C. y = filt.update(x)

    placeholder y = x ทำให้ค่าที่วาดบนเส้นล่างคือค่าดิบ สองกราฟต่างกันก็ต่อเมื่อค่าผ่าน filt.update จริง

  2. เขียน return dsp.Median(5) ในจุดที่ 1 แล้วเลือก Median จะเกิดอะไร (เป้าหมายข้อ 1)

    1. ได้หน้าต่าง 5 ค่าตามต้องการ
    2. โยน TypeError เพราะ window เป็นพารามิเตอร์แบบ keyword เท่านั้น
    3. ได้หน้าต่าง 6 ค่า
    4. ฟิลเตอร์กลายเป็น EMA
    ดูเฉลย

    คำตอบ: B. โยน TypeError เพราะ window เป็นพารามิเตอร์แบบ keyword เท่านั้น

    พารามิเตอร์ของฟิลเตอร์ใน dsp เป็น keyword-only ต้องเขียน dsp.Median(window=5) การส่งแบบ positional จะถูกปฏิเสธทันที

  3. เคาะบอร์ดแรงครั้งเดียวให้เกิด spike ผลที่คาดว่าจะเห็นบนเส้น Filtered ของ EMA เทียบกับ Median คืออะไร (เป้าหมายข้อ 2)

    1. ทั้งสองตัดทิ้งได้เท่ากัน
    2. EMA เกลี่ยเป็นโหนกนุ่มที่ยังเห็น ส่วน Median โหวต spike ตกไปจนแทบไม่เห็น
    3. EMA ตัดทิ้งหมด ส่วน Median เกลี่ยเป็นโหนก
    4. ทั้งสองทำให้เส้นกลายเป็นศูนย์
    ดูเฉลย

    คำตอบ: B. EMA เกลี่ยเป็นโหนกนุ่มที่ยังเห็น ส่วน Median โหวต spike ตกไปจนแทบไม่เห็น

    EMA ถ่วงค่าโดดเข้าไปในผลลัพธ์บางส่วน Median เลือกค่ากลางจึงไม่สนค่าโดดเดี่ยว นี่คือความต่างของ "เกลี่ย" กับ "โหวตตัด"

  4. raw_jit = 400 และ filt_jit = 100 ค่า noise down % เป็นเท่าไร (เป้าหมายข้อ 3)

    1. 25%
    2. 75%
    3. 100%
    4. 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 ฉบับเต็ม

TESA Open Knowledge · © 2026 สมาคมสมองกลฝังตัวไทย (TESA) · CC BY-NC 4.0

เนื้อหาเผยแพร่ภายใต้ CC BY-NC 4.0 นำไปใช้ต่อในงานที่ไม่ใช่เพื่อการค้าได้ โปรดอ้างอิงสมาคมสมองกลฝังตัวไทย (TESA) ทุกครั้ง · วิธีอ้างอิง TESA