ลงมือทำ: feature vector จากหน้าต่างเลื่อน
โมดูล 4 — วิเคราะห์สัญญาณ · สไลด์: slides.md · ภาพรวมโมดูล · หน้าหลักสูตร
เติมห้าจุดใน s10_windowing.py ให้เก็บสัญญาณเข้า buffer คำนวณ mean std และพลังงานต่อย่าน แล้วเลื่อนหน้าต่างให้ซ้อน 50% จนได้ feature vector หกตัวที่ขยับตามการเคลื่อนไหวจริง พร้อมเห็นว่ามันคือหน่วยข้อมูลของ dataset ในโมดูลถัดไป
เป้าหมาย
หัวข้อที่มีชื่อว่า “เป้าหมาย”เมื่อจบบทเรียนนี้ คุณจะ:
- เติมห้าจุดใน practice/s10_windowing.py จนแท่ง feature ทั้งหกขยับ และตัวนับหน้าต่างเพิ่มขึ้นทุก HOP จุด
- วัดและจดค่า std ตอนวางนิ่งเทียบกับตอนเขย่า แล้วอธิบายได้ว่าต่างกันเพราะอะไร
- อธิบายได้ว่าลำดับ mean → std สำคัญอย่างไร และทำไมการลืม buf = buf[-WIN:] ทำให้หน่วยความจำรั่ว
ก่อนเริ่ม
หัวข้อที่มีชื่อว่า “ก่อนเริ่ม”ผ่านบทเรียน 4.5 มาแล้ว รู้จัก WIN, HOP และ feature หกตัว เตรียมบันทึกการเรียนไว้จดค่า std ตอนนิ่งกับตอนเขย่า
- อุปกรณ์: บอร์ด TESAIoT Dev Kit ที่ลงเฟิร์มแวร์ MicroPython ของ BENTO แล้ว หรือ BENTO Emulator ใน BENTO IDE
- เรียนมาก่อน: บทเรียน 4.5 — feature และหน้าต่าง: สิ่งที่โมเดลเห็นจริง
ทั้งไฟล์อ่านเป็นประโยคเดียว: เก็บสัญญาณเข้า 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)
- ก) buf.append(az) ทำให้ buffer ไม่เคยครบหน้าต่าง
- ข) band_e.append(…)
- ค) buf = buf[-WIN:]
- ง) std
เฉลย
ก — ถ้าไม่เก็บค่าเข้า buffer เงื่อนไข len(buf) >= WIN ไม่มีวันจริง จึงไม่มีการบีบ feature และไม่มีการนับหน้าต่าง
-
วางบอร์ดนิ่งแล้ว std ต่ำ แต่เขย่าแล้ว std สูงมาก เพราะอะไร (เลือกหนึ่งข้อ · เป้าหมายข้อ 2)
- ก) เขย่าทำให้ค่าเฉลี่ยของ az สูงขึ้นเสมอ
- ข) std วัดว่าค่ากระจายห่างจากค่าเฉลี่ยแค่ไหน เขย่าทำให้ az แกว่งไปมารอบค่าเฉลี่ยมาก
- ค) เซนเซอร์เปลี่ยนหน่วยตอนเขย่า
- ง) หน้าต่างสั้นลงตอนเขย่า
เฉลย
ข — ตอนนิ่งทุกจุดใกล้ค่าเฉลี่ย std จึงเล็ก ตอนเขย่าจุดกระจายห่างค่าเฉลี่ย std จึงใหญ่ เป็นเหตุผลที่ feature สถิติง่าย ๆ แยกท่าทางได้ดี
-
ถ้าเขียนบรรทัด std ไว้ก่อนบรรทัด mean ภายใน features() จะเกิดอะไร (เลือกหนึ่งข้อ · เป้าหมายข้อ 3)
- ก) ได้ค่าเท่าเดิม
- ข) std คำนวณจาก mean = 0.0 ค่าเริ่มต้น จึงผิด (ได้ค่าใกล้ระดับของสัญญาณแทนการแกว่ง)
- ค) โปรแกรมโยน NameError ทุกครั้ง
- ง) band energy ผิดแทน
เฉลย
ข — std ต้องใช้ mean ที่คำนวณแล้ว ถ้า mean ยังเป็น 0.0 ผลรวม (x − 0)² จะสะท้อนขนาดของสัญญาณ ไม่ใช่การกระจายรอบค่าเฉลี่ย
-
ลืมเติม 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)
- buf.append(az) ทำให้ buffer ไม่เคยครบหน้าต่าง
- band_e.append(...)
- buf = buf[-WIN:]
- std
ดูเฉลย
คำตอบ: A. buf.append(az) ทำให้ buffer ไม่เคยครบหน้าต่าง
ถ้าไม่เก็บค่าเข้า buffer เงื่อนไข len(buf) >= WIN ไม่มีวันจริง จึงไม่มีการบีบ feature และไม่มีการนับหน้าต่าง
-
วางบอร์ดนิ่งแล้ว std ต่ำ แต่เขย่าแล้ว std สูงมาก เพราะอะไร (เป้าหมายข้อ 2)
- เขย่าทำให้ค่าเฉลี่ยของ az สูงขึ้นเสมอ
- std วัดว่าค่ากระจายห่างจากค่าเฉลี่ยแค่ไหน เขย่าทำให้ az แกว่งไปมารอบค่าเฉลี่ยมาก
- เซนเซอร์เปลี่ยนหน่วยตอนเขย่า
- หน้าต่างสั้นลงตอนเขย่า
ดูเฉลย
คำตอบ: B. std วัดว่าค่ากระจายห่างจากค่าเฉลี่ยแค่ไหน เขย่าทำให้ az แกว่งไปมารอบค่าเฉลี่ยมาก
ตอนนิ่งทุกจุดใกล้ค่าเฉลี่ย std จึงเล็ก ตอนเขย่าจุดกระจายห่างค่าเฉลี่ย std จึงใหญ่ เป็นเหตุผลที่ feature สถิติง่าย ๆ แยกท่าทางได้ดี
-
ถ้าเขียนบรรทัด std ไว้ก่อนบรรทัด mean ภายใน features() จะเกิดอะไร (เป้าหมายข้อ 3)
- ได้ค่าเท่าเดิม
- std คำนวณจาก mean = 0.0 ค่าเริ่มต้น จึงผิด (ได้ค่าใกล้ระดับของสัญญาณแทนการแกว่ง)
- โปรแกรมโยน NameError ทุกครั้ง
- band energy ผิดแทน
ดูเฉลย
คำตอบ: B. std คำนวณจาก mean = 0.0 ค่าเริ่มต้น จึงผิด (ได้ค่าใกล้ระดับของสัญญาณแทนการแกว่ง)
std ต้องใช้ mean ที่คำนวณแล้ว ถ้า mean ยังเป็น 0.0 ผลรวม (x − 0)² จะสะท้อนขนาดของสัญญาณ ไม่ใช่การกระจายรอบค่าเฉลี่ย
-
ลืมเติม buf = buf[-WIN:] ผลระยะยาวคืออะไร (เป้าหมายข้อ 3)
- หน้าต่างไม่ซ้อนกันแต่ทุกอย่างปกติ
- buffer โตขึ้นเรื่อย ๆ ไม่มีที่สิ้นสุดจนหน่วยความจำหมด
- feature ทุกตัวเป็นศูนย์
- อัตราสุ่มเร็วขึ้น
ดูเฉลย
คำตอบ: 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 Open Knowledge · © 2026 สมาคมสมองกลฝังตัวไทย (TESA) · CC BY-NC 4.0
เนื้อหาเผยแพร่ภายใต้ CC BY-NC 4.0 นำไปใช้ต่อในงานที่ไม่ใช่เพื่อการค้าได้ โปรดอ้างอิงสมาคมสมองกลฝังตัวไทย (TESA) ทุกครั้ง · วิธีอ้างอิง TESA