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

ลงมือทำ: feature vector จากหน้าต่างเลื่อน

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

เติมห้าจุดใน s10_windowing.py ให้เก็บสัญญาณเข้า buffer คำนวณ mean std และพลังงานต่อย่าน แล้วเลื่อนหน้าต่างให้ซ้อน 50% จนได้ feature vector หกตัวที่ขยับตามการเคลื่อนไหวจริง พร้อมเห็นว่ามันคือหน่วยข้อมูลของ dataset ในโมดูลถัดไป

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

  1. เติมห้าจุดใน practice/s10_windowing.py จนแท่ง feature ทั้งหกขยับ และตัวนับหน้าต่างเพิ่มขึ้นทุก HOP จุด
  2. วัดและจดค่า std ตอนวางนิ่งเทียบกับตอนเขย่า แล้วอธิบายได้ว่าต่างกันเพราะอะไร
  3. อธิบายได้ว่าลำดับ mean → std สำคัญอย่างไร และทำไมการลืม buf = buf[-WIN:] ทำให้หน่วยความจำรั่ว

ผ่านบทเรียน 4.5 มาแล้ว รู้จัก WIN, HOP และ feature หกตัว เตรียมบันทึกการเรียนไว้จดค่า std ตอนนิ่งกับตอนเขย่า

ทั้งไฟล์อ่านเป็นประโยคเดียว: เก็บสัญญาณเข้า buffer → พอครบหน้าต่างก็บีบเป็น feature → โชว์เป็นแท่ง → เลื่อนหน้าต่าง → วนต่อ ห้าจุดที่เติมคือ (1) buf.append(az) ในลูปที่อ่าน sensors.bmi270.motion() ทีละ HOP จุดโดยหน่วง 20 ms (50 Hz) (2) mean = sum(win) / n (3) std = math.sqrt(sum((x - mean) ** 2 for x in win) / n) ต้องมาหลัง mean เสมอเพราะใช้ค่า mean (4) band_e.append(sum((x - m) ** 2 for x in s) / len(s)) ในลูปสี่ย่าน และ (5) buf = buf[-WIN:] หลังบีบ feature เพื่อเก็บแค่ WIN จุดท้าย หน้าต่างถัดไปจึงซ้อน 50% ถ้าลืมจุดสุดท้าย buffer จะโตไม่หยุดจนหน่วยความจำหมด

การแสดงผลให้ไว้แล้ว แท่งถูกสเกลแบบหยาบ (mean และ std คูณ 2 ส่วน band คูณ 0.02) เพียงเพื่อให้ตามองเห็น lcd.console พิมพ์ค่าจริงของ feature vector ควบคู่ ตอนป้อนโมเดลจริงเราจะ normalize อย่างเป็นระบบในโมดูล 5 แต่หลักเดียวกัน คือทำให้ทุก feature อยู่สเกลใกล้กัน ทุกอย่างเป็น Python ล้วนบน CM33 ไม่ต้องพึ่ง NPU

ความสำเร็จของชุดบทเรียนนี้คือบอกได้ว่าทำไมวางนิ่งแล้ว std ต่ำ และ feature vector นี้จะกลายเป็น dataset ได้อย่างไร ในโมดูลถัดไปเราจะทำแบบเดียวกันแต่ติด label แล้วเก็บลง CSV เพื่อเอาไปฝึกโมเดลใน TensorFlow

s10_windowing_full.py เพิ่มการเน้นย่านที่แรงที่สุด ตัดสิน “นิ่ง” หรือ “ขยับ” จาก std ด้วยเส้นแบ่ง STD_MOVE (หน่วย m/s² ปรับได้ตามบอร์ด) และวัดอัตราหน้าต่างต่อวินาที เป็นตัวอย่างของการจำแนกด้วยกฎบน feature ที่เราสร้างเอง

ไฟล์ ไฟล์นี้สอน
examples/s10_windowing_full.py feature front-end แบบเห็นภาพ: windowing + feature vector (ฉบับเต็ม)

คอมเมนต์ # เติม: อยู่ที่บรรทัด 37 (mean), 39 (std), 48 (band energy), 78 (buf.append) และ 91 (buf = buf[-WIN:]) แทน pass หรือค่า 0.0 ด้วยคำสั่งตามคำใบ้ แล้ววางนิ่งสลับเขย่า ถ้าแท่งไม่ขยับ ตรวจการเยื้องบรรทัดและลำดับ mean → std

ไฟล์ฝึก เรื่อง
practice/s10_windowing.py โมเดล “เห็น” อะไร: windowing + feature vector (ฉบับฝึกเติมโค้ด)

เปิดเฉลยหลังจากลองเองแล้วอย่างน้อยหนึ่งรอบ และอ่าน วิธีใช้เฉลย ก่อน

เฉลย คู่กับ
solution/s10_windowing.py practice/s10_windowing.py

คำถามชุดเดียวกันอยู่ใน quiz.yaml สำหรับระบบที่ตรวจอัตโนมัติ

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

    • ก) buf.append(az) ทำให้ buffer ไม่เคยครบหน้าต่าง
    • ข) band_e.append(…)
    • ค) buf = buf[-WIN:]
    • ง) std
    เฉลย

    ก — ถ้าไม่เก็บค่าเข้า buffer เงื่อนไข len(buf) >= WIN ไม่มีวันจริง จึงไม่มีการบีบ feature และไม่มีการนับหน้าต่าง

  2. วางบอร์ดนิ่งแล้ว std ต่ำ แต่เขย่าแล้ว std สูงมาก เพราะอะไร (เลือกหนึ่งข้อ · เป้าหมายข้อ 2)

    • ก) เขย่าทำให้ค่าเฉลี่ยของ az สูงขึ้นเสมอ
    • ข) std วัดว่าค่ากระจายห่างจากค่าเฉลี่ยแค่ไหน เขย่าทำให้ az แกว่งไปมารอบค่าเฉลี่ยมาก
    • ค) เซนเซอร์เปลี่ยนหน่วยตอนเขย่า
    • ง) หน้าต่างสั้นลงตอนเขย่า
    เฉลย

    ข — ตอนนิ่งทุกจุดใกล้ค่าเฉลี่ย std จึงเล็ก ตอนเขย่าจุดกระจายห่างค่าเฉลี่ย std จึงใหญ่ เป็นเหตุผลที่ feature สถิติง่าย ๆ แยกท่าทางได้ดี

  3. ถ้าเขียนบรรทัด std ไว้ก่อนบรรทัด mean ภายใน features() จะเกิดอะไร (เลือกหนึ่งข้อ · เป้าหมายข้อ 3)

    • ก) ได้ค่าเท่าเดิม
    • ข) std คำนวณจาก mean = 0.0 ค่าเริ่มต้น จึงผิด (ได้ค่าใกล้ระดับของสัญญาณแทนการแกว่ง)
    • ค) โปรแกรมโยน NameError ทุกครั้ง
    • ง) band energy ผิดแทน
    เฉลย

    ข — std ต้องใช้ mean ที่คำนวณแล้ว ถ้า mean ยังเป็น 0.0 ผลรวม (x − 0)² จะสะท้อนขนาดของสัญญาณ ไม่ใช่การกระจายรอบค่าเฉลี่ย

  4. ลืมเติม buf = buf[-WIN:] ผลระยะยาวคืออะไร (เลือกหนึ่งข้อ · เป้าหมายข้อ 3)

    • ก) หน้าต่างไม่ซ้อนกันแต่ทุกอย่างปกติ
    • ข) buffer โตขึ้นเรื่อย ๆ ไม่มีที่สิ้นสุดจนหน่วยความจำหมด
    • ค) feature ทุกตัวเป็นศูนย์
    • ง) อัตราสุ่มเร็วขึ้น
    เฉลย

    ข — features() ยังใช้ buf[-WIN:] จึงดูเหมือนทำงานปกติในช่วงแรก แต่ list ถูกต่อท้ายไม่หยุด เป็น memory leak ที่ทำให้บอร์ดค้างในที่สุด

MVP ของชุดบทเรียน 4.5–4.6: สร้าง feature vector จากสัญญาณดิบด้วยมือเอง ตัดหน้าต่าง (window + hop) แล้วบีบเป็น mean, std และ band ที่เปลี่ยนตามการเคลื่อนไหว

  • เติมไฟล์ฝึกครบห้าจุด รันได้บน Emulator หรือบอร์ด
  • วางนิ่งกับเขย่า จดค่า std ทั้งสองกรณีลงบันทึกการเรียน ต่างกันกี่เท่า และเพราะอะไร
  • เปลี่ยน HOP เป็นค่าเดียวกับ WIN แล้วสังเกตว่าอัตราหน้าต่างเปลี่ยนอย่างไร เสี่ยงพลาดอะไร
  • อธิบายได้ว่า window กับ hop คืออะไร ทำไมต้องซ้อน และ std บอกอะไร

โมดูลถัดไป (Training) เราจะเก็บข้อมูลแบบนี้พร้อม label เป็น dataset จริง แบ่ง train, val, test แล้วฝึกโมเดลของเราเอง

บทเรียนถัดไป: บทเรียน 5.1 — วิศวกรรมชุดข้อมูล: สมดุลคลาส หน้าต่าง และการแบ่ง train/val/test

  • feature หกตัวนี้พอสำหรับแยก idle, circle และ shaking หรือไม่ ถ้าไม่พอ คุณจะเพิ่ม feature อะไร
  • ถ้า band ของคุณแบ่งตามความถี่แทนเวลา ผลที่ได้จะใกล้เคียงอะไรที่โมเดลเสียงใช้

คำถามทบทวน

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

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

    1. buf.append(az) ทำให้ buffer ไม่เคยครบหน้าต่าง
    2. band_e.append(...)
    3. buf = buf[-WIN:]
    4. std
    ดูเฉลย

    คำตอบ: A. buf.append(az) ทำให้ buffer ไม่เคยครบหน้าต่าง

    ถ้าไม่เก็บค่าเข้า buffer เงื่อนไข len(buf) >= WIN ไม่มีวันจริง จึงไม่มีการบีบ feature และไม่มีการนับหน้าต่าง

  2. วางบอร์ดนิ่งแล้ว std ต่ำ แต่เขย่าแล้ว std สูงมาก เพราะอะไร (เป้าหมายข้อ 2)

    1. เขย่าทำให้ค่าเฉลี่ยของ az สูงขึ้นเสมอ
    2. std วัดว่าค่ากระจายห่างจากค่าเฉลี่ยแค่ไหน เขย่าทำให้ az แกว่งไปมารอบค่าเฉลี่ยมาก
    3. เซนเซอร์เปลี่ยนหน่วยตอนเขย่า
    4. หน้าต่างสั้นลงตอนเขย่า
    ดูเฉลย

    คำตอบ: B. std วัดว่าค่ากระจายห่างจากค่าเฉลี่ยแค่ไหน เขย่าทำให้ az แกว่งไปมารอบค่าเฉลี่ยมาก

    ตอนนิ่งทุกจุดใกล้ค่าเฉลี่ย std จึงเล็ก ตอนเขย่าจุดกระจายห่างค่าเฉลี่ย std จึงใหญ่ เป็นเหตุผลที่ feature สถิติง่าย ๆ แยกท่าทางได้ดี

  3. ถ้าเขียนบรรทัด std ไว้ก่อนบรรทัด mean ภายใน features() จะเกิดอะไร (เป้าหมายข้อ 3)

    1. ได้ค่าเท่าเดิม
    2. std คำนวณจาก mean = 0.0 ค่าเริ่มต้น จึงผิด (ได้ค่าใกล้ระดับของสัญญาณแทนการแกว่ง)
    3. โปรแกรมโยน NameError ทุกครั้ง
    4. band energy ผิดแทน
    ดูเฉลย

    คำตอบ: B. std คำนวณจาก mean = 0.0 ค่าเริ่มต้น จึงผิด (ได้ค่าใกล้ระดับของสัญญาณแทนการแกว่ง)

    std ต้องใช้ mean ที่คำนวณแล้ว ถ้า mean ยังเป็น 0.0 ผลรวม (x − 0)² จะสะท้อนขนาดของสัญญาณ ไม่ใช่การกระจายรอบค่าเฉลี่ย

  4. ลืมเติม buf = buf[-WIN:] ผลระยะยาวคืออะไร (เป้าหมายข้อ 3)

    1. หน้าต่างไม่ซ้อนกันแต่ทุกอย่างปกติ
    2. buffer โตขึ้นเรื่อย ๆ ไม่มีที่สิ้นสุดจนหน่วยความจำหมด
    3. feature ทุกตัวเป็นศูนย์
    4. อัตราสุ่มเร็วขึ้น
    ดูเฉลย

    คำตอบ: B. buffer โตขึ้นเรื่อย ๆ ไม่มีที่สิ้นสุดจนหน่วยความจำหมด

    features() ยังใช้ buf[-WIN:] จึงดูเหมือนทำงานปกติในช่วงแรก แต่ list ถูกต่อท้ายไม่หยุด เป็น memory leak ที่ทำให้บอร์ดค้างในที่สุด

อ้างอิงบทเรียนนี้

ถ้านำบทเรียนนี้ไปสอน ทำสไลด์ หรือทำเอกสารต่อ ให้อ้างอิงด้วยข้อความนี้ ถ้าดัดแปลงเนื้อหา ให้เติม (ดัดแปลง)ต่อท้ายชื่อบทเรียน

"ลงมือทำ: feature vector จากหน้าต่างเลื่อน" จาก TESA Open Knowledge โดยสมาคมสมองกลฝังตัวไทย (Thai Embedded Systems Association: TESA) https://github.com/tesaiot/tesa-qualification-program สัญญาอนุญาต CC BY-NC 4.0

ข้อความอ้างอิงภาษาอังกฤษ: "Hands-on: a feature vector from a sliding window" 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/l06-windowing-lab/

วิธีอ้างอิง TESA ฉบับเต็ม

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

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