บทเรียน 4.1 — ฟิลเตอร์ DSP: EMA, Median, Kalman และ radar range profile

ทำสัญญาณให้สะอาดสด ๆ ตรงหน้า

โมดูล 4 — วิเคราะห์สัญญาณ

เปิดกล่อง Analysis (เสาที่ 3 ของวงจรข้อมูล)

คาถาประจำบทเรียน: "สัญญาณดิบมีหนามเสมอ — ฟิลเตอร์คือมือที่ปัดหนามทิ้ง เหลือแต่ของจริงให้โมเดลเห็น"

dsp.EMA · dsp.Median · dsp.Kalman1D บนบอร์ด BENTO (PSoC Edge · Cortex-M55 + Ethos-U55 NPU)

TESA Open Knowledge · © 2026 สมาคมสมองกลฝังตัวไทย (TESA) · ดัดแปลงจาก Edge AI Developer (รศ.วิรุฬห์ ศรีบริรักษ์, BUU) · CC BY-NC 4.0

เปิดบทเรียนด้วยของจริงก่อน

เหมือนทุกบทเรียน เราเริ่มแบบ กลับด้าน — รันของที่ทำงานได้ก่อน แล้วค่อยแกะว่ามันสะอาดขึ้นได้ยังไง

รันของจริงก่อน Raw vs Filtered แกะดูข้างใน ฟิลเตอร์ทำงานยังไง เติม/จูนเอง EMA/Median/Kalman อยากสร้างต่อ FFT · feature (4.3–4.6)

วิธี PRIMM (Predict–Run–Investigate–Modify–Make) อันเดิม: ครั้งนี้เราจะเปิดแอปที่โชว์เส้นดิบเทียบเส้นที่กรองแล้วสองกราฟซ้อนกัน แล้วมือคุณเขย่าบอร์ด ตาคุณเห็นเส้นล่างเรียบกว่าเส้นบนทันที

ยังไม่ต้องเข้าใจสมการฟิลเตอร์ทุกตัวในนาทีแรก ขอแค่เห็นว่า "เส้นบนกระตุก เส้นล่างนิ่ง" แล้วเริ่มสงสัยว่ามันทำได้ยังไง เท่านั้นพอ

TESA Open Knowledge · © 2026 สมาคมสมองกลฝังตัวไทย (TESA) · ดัดแปลงจาก Edge AI Developer (รศ.วิรุฬห์ ศรีบริรักษ์, BUU) · CC BY-NC 4.0

เป้าหมายของชุดบทเรียนนี้

จบชุดบทเรียนนี้เราจะเดินครบ แล้วปิดท้ายด้วยการทำสัญญาณจริงให้สะอาดสด ๆ:

  1. ทำไมสัญญาณดิบต้องกรอง — สัญญาณรบกวน (noise) มาจากไหน และมันหลอกโมเดลยังไง
  2. ฟิลเตอร์สามตระกูล ที่ใช้บ่อยสุดใน Edge AI: dsp.EMA (ถัวเฉลี่ยถ่วงน้ำหนัก) · dsp.Median (ตัดหนามโดด) · dsp.Kalman1D (ชั่งน้ำหนัก predict/measure)
  3. API ร่วม ของทุกฟิลเตอร์: .update(x) / .value() / .reset() — สร้างครั้งเดียว ป้อนทีละค่า
  4. radar range profile — ตัวอย่างของจริงที่ฝั่ง C ทำ HPF→FFT→dB→peak ให้แล้ว เหลือ median กันหนามฝั่งเรา
  5. ลงมือ: เปิดแอป s08_filters.py เขย่าบอร์ด ดูเส้นดิบเทียบเส้นกรอง + เปอร์เซ็นต์ลดสัญญาณรบกวน

ปลายทางของวันนี้: เลือกฟิลเตอร์ใน dropdown ขยับบอร์ด แล้วเห็นเส้นล่างสะอาดกว่าเส้นบนอย่างชัดเจน พร้อมตัวเลข "noise down %"

วันนี้เราเน้น "กรองเป็น + อธิบายข้อแลกเปลี่ยนได้" ส่วน FFT กับ feature extraction เก็บไว้เป็น บทเรียน 4.3–4.6 ต่อจากนี้

TESA Open Knowledge · © 2026 สมาคมสมองกลฝังตัวไทย (TESA) · ดัดแปลงจาก Edge AI Developer (รศ.วิรุฬห์ ศรีบริรักษ์, BUU) · CC BY-NC 4.0

ชุดบทเรียนนี้อยู่ตรงไหนของวงจร

เราเข้าเสาที่ 3 แล้ว — Analysis ต่อจาก Processing (โมดูล 3) ที่เราแปลงสัญญาณดิบเป็นปริมาณที่มีความหมาย (tilt, พลังงาน, dBFS)

1 · DAQ โมดูล 2 2 · Processing โมดูล 3 (มาแล้ว) 3 · Analysis 4.1–4.2 (วันนี้) · 4.3–4.4 · 4.5–4.6 4 · Training โมดูล 5 5 · Apps โมดูล 6 Analysis = ทำสัญญาณให้ "อ่านง่าย" ก่อนส่งให้โมเดล — เริ่มที่การกรองสัญญาณรบกวน

Processing แปลง "หน่วย" (accel → tilt), Analysis จัดการ "รูปร่างของสัญญาณตามเวลา/ความถี่" ฟิลเตอร์วันนี้คือก้าวแรก ก่อนจะไป FFT (บทเรียน 4.3–4.4) และ feature ที่โมเดลกินจริง (บทเรียน 4.5–4.6)

TESA Open Knowledge · © 2026 สมาคมสมองกลฝังตัวไทย (TESA) · ดัดแปลงจาก Edge AI Developer (รศ.วิรุฬห์ ศรีบริรักษ์, BUU) · CC BY-NC 4.0

ทบทวนเร็ว — จาก บทเรียน 3.1–3.2 มาถึงตรงนี้

บทเรียน 3.1–3.2 เราเอาสัญญาณดิบมาแปลงเป็นปริมาณที่มีความหมาย เช่น dsp.tilt(ax,ay,az) คืน (roll,pitch) แล้วโชว์เป็นเกจ

  • แต่ตอนโชว์เกจจริง คุณคงเห็นว่ามัน สั่น ตลอด ทั้งที่วางบอร์ดนิ่ง ๆ — นั่นแหละสัญญาณรบกวน
  • บทเรียน 3.1–3.2 ตอบคำถาม "ค่านี้แปลว่าอะไร" (ฟิสิกส์) · บทเรียน 4.1–4.2 ตอบ "ค่านี้เชื่อได้แค่ไหน และจะทำให้นิ่งได้ยังไง" (สัญญาณ)
  • เครื่องมือเดียวกัน คือโมดูล dsp — บทเรียน 3.1–3.2 ใช้ฟังก์ชันฟิสิกส์ (tilt/altitude) วันนี้เราใช้คลาสฟิลเตอร์ (EMA/Median/Kalman1D)

ถ้าบทเรียน 3.1–3.2 คุณเคยรำคาญว่า "ทำไมเกจกระพริบจัง" — ชุดบทเรียนนี้แหละคือคำตอบ เราจะจับความกระพริบนั้นมากดให้นิ่ง

TESA Open Knowledge · © 2026 สมาคมสมองกลฝังตัวไทย (TESA) · ดัดแปลงจาก Edge AI Developer (รศ.วิรุฬห์ ศรีบริรักษ์, BUU) · CC BY-NC 4.0

ปัญหา: สัญญาณดิบมีหนามเสมอ

ไม่มีเซนเซอร์ตัวไหนในโลกให้ค่าเรียบ ๆ ทุกอย่างมี หนาม (noise) ปนมาด้วย บางทีก็หนามเล็ก ๆ กระจาย บางทีก็ค่าโดดสุดขั้ว (outlier)

สัญญาณจริง (ของที่เราอยากได้) = เส้นประ · สัญญาณดิบ = เส้นส้มมีหนาม ถ้าเอาสัญญาณดิบไปตัดสินใจตรง ๆ (threshold/โมเดล) หนามพวกนี้จะทำให้ "เจอผี" — เดี๋ยวข้าม เดี๋ยวไม่ข้าม
  • หนามเล็กกระจาย มาจากความร้อน วงจร ADC การสั่นสะเทือน — เรียกว่า noise
  • ค่าโดดสุดขั้ว (spike) มาจากการสะท้อนหลายทาง (multipath ของเรดาร์) หรือชนสัญญาณชั่วขณะ — เรียกว่า outlier
  • ฟิลเตอร์ต่างชนิดเก่งกันคนละแบบ: บางตัวเก่งหนามเล็ก บางตัวเก่ง spike (เดี๋ยวเราจะเจอ)

นี่คือเหตุผลที่ Analysis มาก่อน Training เสมอ — ถ้าป้อนสัญญาณสกปรกให้โมเดล มันจะเรียน "หนาม" ไปด้วย แล้วพังตอนใช้จริง

TESA Open Knowledge · © 2026 สมาคมสมองกลฝังตัวไทย (TESA) · ดัดแปลงจาก Edge AI Developer (รศ.วิรุฬห์ ศรีบริรักษ์, BUU) · CC BY-NC 4.0

รันของจริงก่อน — เห็นแอปทำงานเลย

เปิด s08_filters_full.py (หรือ s08_filters.py ที่เติมครบแล้ว) จะเห็นสองกราฟซ้อนกัน:

  • กราฟบน (Raw) — สัญญาณดิบจาก IMU (ขนาดความเร่งสามแกน) มีหนามชัดตอนขยับ
  • กราฟล่าง (Filtered) — ค่าเดียวกันที่ผ่านฟิลเตอร์ เรียบขึ้นเห็น ๆ
  • แถบ noise down % — บอกว่าฟิลเตอร์ลด "การกระตุกระหว่างเฟรม" ได้กี่เปอร์เซ็นต์

ลองเลย: เขย่าบอร์ดเบา ๆ → เส้นบนเต้นแรง เส้นล่างตามช้ากว่าและเรียบกว่า แล้วสลับฟิลเตอร์ใน dropdown ดูว่าเส้นล่างเปลี่ยนนิสัยยังไง

อย่าเพิ่งอ่านโค้ด รันก่อน เล่นก่อน ให้ตาเห็นความต่างของสามฟิลเตอร์ด้วยมือตัวเอง แล้วค่อยกลับมาถามว่า "มันต่างกันเพราะอะไร"

TESA Open Knowledge · © 2026 สมาคมสมองกลฝังตัวไทย (TESA) · ดัดแปลงจาก Edge AI Developer (รศ.วิรุฬห์ ศรีบริรักษ์, BUU) · CC BY-NC 4.0

สัญญาณรบกวนมาจากไหน

ก่อนจะกรอง เราต้องรู้ว่ากำลังสู้กับอะไร noise มีหลายหน้า และแต่ละหน้าเรียกฟิลเตอร์คนละตัว

ชนิด หน้าตา มาจาก ใครจัดการเก่ง
white noise หนามเล็กกระจายรอบค่าจริง ความร้อน · วงจร · ADC EMA / SMA (ถัวเฉลี่ย)
spike / outlier ค่าโดดสุดขั้วเป็นครั้งคราว multipath · ชนสัญญาณ · บิตพลิก Median (โหวตค่ากลาง)
drift ค่าค่อย ๆ เลื่อนช้า ๆ อุณหภูมิ · แบตหมด HPF (ตัดของช้า)
สั่นเร็วปนช้า จริงบ้างรบกวนบ้าง ทุกอย่างรวมกัน Kalman (ชั่งน้ำหนัก)
  • ไม่มีฟิลเตอร์ "ตัวเทพ" ที่ชนะทุกแบบ — เลือกให้ตรงกับหน้าของ noise ที่เจอ
  • วันนี้เราโฟกัส 3 ตัว: EMA (หนามเล็ก) · Median (spike) · Kalman1D (ผสม)

การถามว่า "สัญญาณของฉันสกปรกแบบไหน" คือทักษะวิศวกรที่แท้จริง — ก่อนหยิบเครื่องมือ ให้มองศัตรูก่อน

TESA Open Knowledge · © 2026 สมาคมสมองกลฝังตัวไทย (TESA) · ดัดแปลงจาก Edge AI Developer (รศ.วิรุฬห์ ศรีบริรักษ์, BUU) · CC BY-NC 4.0

แนวคิดหลัก — ฟิลเตอร์คือความจำ

หัวใจของฟิลเตอร์ตามเวลา (temporal filter): มันไม่ได้มองแค่ค่าปัจจุบันค่าเดียว แต่ จำอดีต แล้วเอามาถ่วงกับค่าใหม่

ค่าใหม่ x (มีหนาม) ฟิลเตอร์ จำอดีต + ถ่วงกับค่าใหม่ state ภายใน (.value()) ค่าที่กรองแล้ว y (เรียบขึ้น) ป้อนทีละค่าในลูป: y = filt.update(x)
  • ทุกฟิลเตอร์ในโมดูล dsp ทำงานแบบ streaming: ป้อนทีละค่าด้วย .update(x) มันเก็บ state ไว้ในตัวแล้วคืนค่าที่กรองแล้ว
  • ต่างจากการโหลดทั้งอาร์เรย์มากรอง — บนไมโครคอนโทรลเลอร์เราไม่มี RAM เก็บทั้งสัญญาณ จึงกรองสด ทีละตัวอย่าง

"ความเรียบ" มาจากการที่ฟิลเตอร์ไม่ยอมกระโดดตามค่าใหม่ทันที มันถ่วงด้วยอดีต — ยิ่งถ่วงหนัก ยิ่งเรียบ แต่ก็ยิ่งตอบสนองช้า นี่คือข้อแลกเปลี่ยนหลักของทั้งชุดบทเรียน

TESA Open Knowledge · © 2026 สมาคมสมองกลฝังตัวไทย (TESA) · ดัดแปลงจาก Edge AI Developer (รศ.วิรุฬห์ ศรีบริรักษ์, BUU) · CC BY-NC 4.0

โมดูล dsp — ฟิลเตอร์สำเร็จให้แล้ว

โมดูล dsp (ฝั่ง C บน CM33) มีคลาสฟิลเตอร์พร้อมใช้หลายตัว ทุกตัวหน้าตา API เหมือนกันเป๊ะ วันนี้เราโฟกัสสามตัว:

คลาส สร้างด้วย เก่งเรื่อง ความจำ
dsp.EMA EMA(alpha=0.15) หนามเล็กกระจาย ค่าเดียว (ถูกสุด)
dsp.SMA SMA(window=10) หนามเล็ก + ค่าเฉลี่ยตรง buffer N ค่า
dsp.Median Median(window=5) spike / outlier โดด ๆ buffer N ค่า
dsp.LPF LPF(cutoff=5, fs=100) ตัดของเร็ว (ความถี่สูง) ค่าเดียว
dsp.HPF HPF(cutoff=0.5, fs=100) ตัด drift (ความถี่ต่ำ) ค่าเดียว
dsp.Kalman1D Kalman1D(q=0.02, r=0.6) ผสม จริง+รบกวน ค่าเดียว + ความมั่นใจ
  • สำคัญ: พารามิเตอร์ทุกตัวเป็น keyword argument (alpha=, window=, q=, r=) ส่งแบบ positional เช่น dsp.EMA(0.15) จะโยน TypeError ทันที
  • วันนี้เราเล่นสามตัวหลัก: EMA / Median / Kalman1D (อีกสามตัวเป็นการบ้านต่อยอด)

ฝั่ง C เขียนฟิลเตอร์ให้เสร็จ เรากลายเป็นคนเลือกและจูน — นี่คือ 70% ที่ได้ฟรี เราลงมือกับ 30% ที่เหลือคือ "เลือกให้ถูก + ตั้งค่าให้พอดี"

TESA Open Knowledge · © 2026 สมาคมสมองกลฝังตัวไทย (TESA) · ดัดแปลงจาก Edge AI Developer (รศ.วิรุฬห์ ศรีบริรักษ์, BUU) · CC BY-NC 4.0

API ร่วมของทุกฟิลเตอร์ — สามเมท็อด

จำสามเมท็อดนี้ตัวเดียว ใช้ได้กับฟิลเตอร์ทุกตัวในโมดูล dsp:

import dsp
f = dsp.EMA(alpha=0.15)      # 1) สร้างครั้งเดียว (นอกลูป!)
while True:
    x = read_sensor()        # ค่าดิบหนึ่งตัว
    y = f.update(x)          # 2) ป้อนเข้า -> คืนค่าที่กรองแล้ว
    last = f.value()        #    ดูค่าล่าสุดโดยไม่ป้อนใหม่
    f.reset()               # 3) ล้าง state (เช่นตอนสลับแหล่งสัญญาณ)
  • .update(x) — หัวใจ: ป้อนตัวอย่างใหม่หนึ่งค่า คืนค่าที่กรองแล้ว (เรียกทุกวนรอบ)
  • .value() — คืนค่าล่าสุดที่กรองไว้ ไม่เปลี่ยน state (เอาไว้อ่านซ้ำ)
  • .reset() — ล้างความจำกลับเป็นศูนย์ (เริ่มนับใหม่)

สร้างครั้งเดียวนอกลูป สำคัญมาก เพราะ state (ความจำ) อยู่ในอ็อบเจกต์ ถ้าสร้างใหม่ทุกวนรอบ = ลบความจำทุกครั้ง ฟิลเตอร์จะไม่มีวันเรียบ (นี่คือบั๊กยอดฮิต)

TESA Open Knowledge · © 2026 สมาคมสมองกลฝังตัวไทย (TESA) · ดัดแปลงจาก Edge AI Developer (รศ.วิรุฬห์ ศรีบริรักษ์, BUU) · CC BY-NC 4.0

EMA — ถัวเฉลี่ยแบบถ่วงน้ำหนักอดีต

EMA (Exponential Moving Average) ฟิลเตอร์ที่ถูกและใช้บ่อยสุด สูตรมีแค่บรรทัดเดียว:

yt=α xt+(1−α) yt−1y_t = \alpha\, x_t + (1-\alpha)\, y_{t-1}

  • ค่าใหม่ xtx_t มีน้ำหนัก α\alpha · ค่าที่กรองไว้เดิม yt−1y_{t-1} มีน้ำหนัก 1−α1-\alpha
  • α\alpha เล็ก (เช่น 0.05) = เชื่ออดีตมาก → เรียบมาก แต่ตอบสนองช้า (ลากตามช้า)
  • α\alpha ใหญ่ (เช่น 0.5) = เชื่อค่าใหม่มาก → ไวมาก แต่เรียบน้อย (หนามยังโผล่)
α = 0.5 (ไว) α = 0.15 (สมดุล) α = 0.05 (เรียบ/ช้า) α เล็กลง → เส้นเรียบขึ้น แต่ตามของจริงช้าลง (ข้อแลกเปลี่ยน)

คำถามวิศวกร: "ฉันยอมช้าแค่ไหนเพื่อแลกความเรียบ?" — งานตรวจการล้มต้องไว เลือก α ใหญ่หน่อย · งานวัดอุณหภูมิห้องยอมช้าได้ เลือก α เล็ก ในไฟล์เราตั้ง EMA_ALPHA = 0.15 เป็นจุดตั้งต้นที่สมดุล

TESA Open Knowledge · © 2026 สมาคมสมองกลฝังตัวไทย (TESA) · ดัดแปลงจาก Edge AI Developer (รศ.วิรุฬห์ ศรีบริรักษ์, BUU) · CC BY-NC 4.0

Median — โหวตเอาค่ากลาง ตัดหนามโดด

Median filter เก่งคนละเรื่องกับ EMA มันไม่ถัวเฉลี่ย แต่ เรียงค่าล่าสุด N ตัวแล้วเอาค่ากลาง — ค่าโดดสุดขั้วจึงไม่มีสิทธิ์ชนะ

หน้าต่าง 5 ค่าล่าสุด (มี spike ปน): 98 101 240 99 100 → เรียง → 98 99 100 101 240 ค่ากลาง = 100 (spike 240 ถูกดันไปขอบ ไม่โดนเลือก) ถ้าใช้ mean จะได้ (98+101+240+99+100)/5 = 127.6 — โดน spike ลากเสียหมด
  • สร้างด้วย dsp.Median(window=5) — เฟิร์มแวร์บังคับ window เป็นเลขคี่ (3..15) เพื่อให้มีค่ากลางชัด
  • ข้อดี: spike เดียวโดดแค่ไหนก็ไม่กระเทือนผล · ข้อเสีย: กินความจำ N ค่า และตอบสนองขอบคมช้ากว่านิด

นี่คือฟิลเตอร์ที่ตัวอย่างเรดาร์ (05_radar_distance.py) ใช้จริง เพราะระยะจากเรดาร์ชอบกระโดดเพราะ multipath — median กันได้ตรงจุด เดี๋ยวเราจะเห็นมันในหน้า radar range profile

TESA Open Knowledge · © 2026 สมาคมสมองกลฝังตัวไทย (TESA) · ดัดแปลงจาก Edge AI Developer (รศ.วิรุฬห์ ศรีบริรักษ์, BUU) · CC BY-NC 4.0

Kalman1D — ชั่งน้ำหนักระหว่าง "ที่คาด" กับ "ที่วัด"

Kalman filter ฉลาดกว่าเพื่อน มันเก็บทั้งค่าประมาณ xx และ ความไม่มั่นใจ pp แล้วทุกก้าวจะชั่งน้ำหนักว่าจะเชื่อการวัดใหม่แค่ไหน

predict p += q (ยิ่งไม่แน่ใจขึ้น) gain k = p / (p + r) เชื่อการวัดแค่ไหน update x += k(z - x)
  • dsp.Kalman1D(q=0.02, r=0.6) — สองปุ่มปรับ:
    • q (process noise) ต่ำ = เชื่อว่าสัญญาณจริง "นิ่ง" → เรียบมาก · สูง = ยอมให้ค่าจริงขยับเร็ว
    • r (measurement noise) สูง = "ไม่ค่อยเชื่อเซนเซอร์" (มองว่า noisy) → กรองหนัก · ต่ำ = เชื่อการวัด
  • ต่างจาก EMA ตรงที่ Kalman ปรับความหนักของการกรองเองตามความมั่นใจ ไม่ได้ใช้ค่าคงที่ตัวเดียว

เริ่มจูนง่าย ๆ: ล็อก q ไว้เล็ก ๆ แล้วเล่นกับ r อย่างเดียว — r สูงขึ้น = เรียบขึ้นแต่ตามช้าลง (คุ้น ๆ ไหม มันคือข้อแลกเปลี่ยนเดิม แค่คนละหน้าตา)

TESA Open Knowledge · © 2026 สมาคมสมองกลฝังตัวไทย (TESA) · ดัดแปลงจาก Edge AI Developer (รศ.วิรุฬห์ ศรีบริรักษ์, BUU) · CC BY-NC 4.0

ภาพเคลื่อนไหว — EMA ทำสัญญาณ noisy ให้เรียบ

ภาพเคลื่อนไหว: เส้นสัญญาณดิบที่มีสัญญาณรบกวนเทียบกับเส้น EMA ที่เรียบกว่า พร้อมสูตร y[t] = α·x[t] + (1−α)·y[t−1]

เส้นเขียว (เรียบ) ค่อย ๆ วาดทับเส้นแดง (ดิบ) — เห็นแนวโน้มจริงโดยไม่ถูกหนามหลอก

TESA Open Knowledge · © 2026 สมาคมสมองกลฝังตัวไทย (TESA) · ดัดแปลงจาก Edge AI Developer (รศ.วิรุฬห์ ศรีบริรักษ์, BUU) · CC BY-NC 4.0

คณิตของ EMA — อ่านทีละตัวอักษร

สูตร EMA มีบรรทัดเดียว ท่องได้เลย แล้วเราจะแกะทีละสัญลักษณ์ว่ามันแปลว่าอะไร:

y[n]=α x[n]+(1−α) y[n−1]y[n] = \alpha\, x[n] + (1-\alpha)\, y[n-1]

  • y[n]y[n] — ค่าที่กรองแล้ว รอบนี้ (ค่าที่เราจะเอาไปวาด/ใช้ตัดสินใจ)
  • x[n]x[n] — ค่าดิบจากเซนเซอร์ รอบนี้ (ยังมีหนาม)
  • y[n−1]y[n-1] — ค่าที่กรองแล้ว รอบก่อน = ความจำของฟิลเตอร์ (state ที่เก็บในอ็อบเจกต์)
  • α\alpha — น้ำหนักที่เราให้กับ "ของใหม่" (0<α≤10 < \alpha \le 1) ส่วน 1−α1-\alpha คือน้ำหนักที่ให้กับ "ของเก่า"

อ่านเป็นประโยคเดียว: ค่าใหม่หนึ่งส่วน α\alpha ผสมกับความทรงจำเดิมอีก 1−α1-\alpha ส่วน

ของใหม่ x[n] · น้ำหนัก α α ใหญ่ = ไว · α เล็ก = เรียบ ความจำเดิม y[n-1] · น้ำหนัก (1-α) ยิ่งถ่วงหนัก ยิ่งลากตามช้า +

ทำไมสำคัญกับชุดบทเรียนนี้: ค่า EMA_ALPHA = 0.15 ในไฟล์คือการเลือกว่า "เชื่อของใหม่ 15% เชื่อความจำเดิม 85%" นั่นแหละคือปุ่มเดียวที่ตัดสินว่าเส้นล่างจะเรียบแค่ไหน ตามช้าแค่ไหน — เข้าใจสูตรนี้ตัวเดียว จูน EMA เป็นทันที

TESA Open Knowledge · © 2026 สมาคมสมองกลฝังตัวไทย (TESA) · ดัดแปลงจาก Edge AI Developer (รศ.วิรุฬห์ ศรีบริรักษ์, BUU) · CC BY-NC 4.0

คณิตของ Kalman — gain คือ "จะเชื่อการวัดแค่ไหน"

หัวใจของ Kalman1D คือค่าเดียวชื่อ Kalman gain KK ที่คำนวณใหม่ทุกก้าว:

K=PP+Rx←x+K (z−x)K = \frac{P}{P + R} \qquad\quad x \leftarrow x + K\,(z - x)

  • PP — ความ ไม่มั่นใจ ในค่าประมาณของเราตอนนี้ (มาจากปุ่ม qq: ยิ่ง qq สูง PP ยิ่งโต)
  • RR — ความ ไม่น่าเชื่อถือของเซนเซอร์ (ปุ่ม rr: เซนเซอร์ยิ่ง noisy เราตั้ง RR ยิ่งสูง)
  • zz — ค่าที่ วัดได้จริง รอบนี้ · xx — ค่าประมาณที่เราเชื่อ
  • KK — อยู่ระหว่าง 00 ถึง 11 เสมอ: ใกล้ 11 = เชื่อการวัดเต็มที่ · ใกล้ 00 = เชื่อค่าประมาณเดิม แทบไม่ขยับ

ลองแทนค่าให้เห็นภาพ:

  • ถ้า RR ใหญ่กว่า PP มาก (เซนเซอร์ห่วย) → KK เล็ก → xx ขยับนิดเดียว = กรองหนัก เรียบมาก
  • ถ้า PP ใหญ่กว่า RR มาก (เราไม่มั่นใจตัวเอง) → KK ใกล้ 11 → xx กระโดดตามการวัด = ไวมาก

ทำไมสำคัญกับชุดบทเรียนนี้: EMA ใช้ α\alpha คงที่ ทั้งชีวิต แต่ Kalman คำนวณ KK ใหม่ทุกก้าวตามความมั่นใจ — นี่คือเหตุผลที่มันฉลาดกว่า แต่ก็มีสองปุ่ม (q, r) ให้จูน ไม่ใช่ปุ่มเดียวเหมือน EMA เทียบสองสูตรนี้ข้าง ๆ กัน คุณจะเห็นว่า Kalman ก็คือ EMA ที่ปรับ α\alpha ของตัวเองได้

TESA Open Knowledge · © 2026 สมาคมสมองกลฝังตัวไทย (TESA) · ดัดแปลงจาก Edge AI Developer (รศ.วิรุฬห์ ศรีบริรักษ์, BUU) · CC BY-NC 4.0

เทียบสามฟิลเตอร์ — เลือกตัวไหนดี

ไม่มีตัวชนะขาด เลือกให้ตรงกับหน้าของ noise และงบประมาณ (CPU/RAM) ที่มี

EMA Median Kalman1D
เก่งเรื่อง หนามเล็กกระจาย spike / outlier ผสม จริง+รบกวน
ราคา (CPU/RAM) ถูกสุด (ค่าเดียว) กลาง (buffer N) กลาง (คณิตนิดหน่อย)
ปุ่มปรับ alpha window q, r
จุดอ่อน spike ยังเล็ดลอด ตอบขอบคมช้า ต้องจูน 2 ค่า
เหมาะกับ เกจอุณหภูมิ/แสง ระยะเรดาร์/GPS IMU fusion ละเอียด
  • เจอ spike → Median ก่อนเลย (EMA จะเกลี่ย spike ให้เนียนแต่ยังเห็นโหนก)
  • เจอ หนามเล็ก เน้นถูก → EMA
  • อยากเรียบ + ตามทัน + ยอมจูน → Kalman1D

ในงานจริงเรามัก ต่อฟิลเตอร์กัน เช่น Median กัน spike ก่อน แล้ว EMA เกลี่ยให้เนียนต่อ — วันนี้เราลองทีละตัวก่อน ให้เข้าใจนิสัยของแต่ละตัวชัด ๆ

TESA Open Knowledge · © 2026 สมาคมสมองกลฝังตัวไทย (TESA) · ดัดแปลงจาก Edge AI Developer (รศ.วิรุฬห์ ศรีบริรักษ์, BUU) · CC BY-NC 4.0

radar range profile — ของจริงที่ฝั่ง C ทำให้แล้ว

ตัวอย่างอ้างอิงของชุดบทเรียนนี้คือ 05_radar_distance.py — ไม้วัดระยะด้วยเรดาร์ 60 GHz ที่โชว์ว่าการกรองสำคัญแค่ไหนกับสัญญาณจริง

raw ADCantenna HPFตัด DC/drift FFT→ ระยะ (4.3–4.4) dB + peakหา bin แรง Median (เรา)กัน multipath ระยะบนจอ
  • sensors.radar_range() คืน dict: {'distance_m','peak_db','resolution_m','target','seq'} — ฝั่ง C ทำ HPF→FFT→dB→peak ให้หมดแล้ว
  • ความละเอียด ~0.33 m/bin (ดูจาก resolution_m) — บอกโซนได้ แต่ระยะชอบ กระโดด เพราะการสะท้อนหลายทาง (multipath)
  • ฟิลเตอร์ที่เหมาะคือ Median เพราะการกระโดดคือ spike แท้ ๆ — ในแอปเราจึงเลือก Median ได้เมื่อสลับแหล่งเป็น Radar

sensors.radar_range() ให้ระยะจริงบน บอร์ดจริง เท่านั้น (บน Emulator เป็นระยะจำลองที่หมุนด้วยลูกบิด) และบนบอร์ดจะโยน OSError ถ้า radar DSP ยังไม่ทำงาน — โค้ดเราจึงห่อ try/except OSError ไว้ คงเส้นนิ่งแทนที่จะพัง (บทเรียนเดียวกับ select() ใน บทเรียน 1.1–1.3)

TESA Open Knowledge · © 2026 สมาคมสมองกลฝังตัวไทย (TESA) · ดัดแปลงจาก Edge AI Developer (รศ.วิรุฬห์ ศรีบริรักษ์, BUU) · CC BY-NC 4.0