ลงมือทำ: จาก verdict สู่ action บนบอร์ด
โมดูล 1 — เริ่มต้น: รันของจริงแล้วแกะดูข้างใน · สไลด์: slides.md · ภาพรวมโมดูล · หน้าหลักสูตร
เติมสี่ช่องในไฟล์ s03_anatomy_edgeai.py จนแอปเฝ้าโมเดลเดียวบี๊บและขึ้นแบนเนอร์เองเมื่อเจอคลาสเป้าหมายที่มั่นใจพอ แล้ว remix ให้สลับโมเดล เปลี่ยนคลาสเป้าหมาย และเปลี่ยน action โดยไม่แตะ logic ตรวจจับ
เป้าหมาย
หัวข้อที่มีชื่อว่า “เป้าหมาย”เมื่อจบบทเรียนนี้ คุณจะ:
- เติม practice/s03_anatomy_edgeai.py ครบสี่ช่อง (select, result, verdict.text, fire_action) จนทำท่าหรือส่งเสียงตรงคลาสเป้าหมายแล้วได้ยินบี๊บหนึ่งครั้งและเห็นแบนเนอร์ และเมื่อออกจากคลาสนั้นแบนเนอร์กลับเป็น “รอจับ …”
- remix แอปด้วยการเปลี่ยน MODEL_KEYWORD และ TARGET_CLASS เป็นโมเดลอื่นที่ใช้เซนเซอร์ต่างกัน โดยตั้งคลาสเป้าหมายจาก labels ที่อ่านได้จริง แล้วยืนยันว่า action ยิงตามคลาสใหม่
- หากรณีที่คลาสเป้าหมายชนะแต่ conf ต่ำกว่า CONF_FLOOR แล้วอธิบายได้ว่าทำไม action จึงไม่ยิง และธง fired ป้องกันอะไร
ก่อนเริ่ม
หัวข้อที่มีชื่อว่า “ก่อนเริ่ม”ผ่านบทเรียน 1.6 มาแล้ว เข้าใจเงื่อนไข hit (label ตรงเป้าหมายและ conf ≥ CONF_FLOOR) กับธง fired
ถ้าจำสี่คำสั่งจากบทเรียน 1.3 ไม่แม่น เปิดเฉลย s01_first_inference.py ทวนก่อน เพราะช่อง 1–3 เป็นของเดิม
- อุปกรณ์: บอร์ด TESAIoT Dev Kit ที่ลงเฟิร์มแวร์ MicroPython ของ BENTO แล้ว หรือ BENTO Emulator ใน BENTO IDE — ฉบับเต็มใช้ on_result ซึ่งบน Emulator จะเรียก callback ก็ต่อเมื่อโปรแกรมเรียก edge_ai.result() หรือ edge_ai.active() จึงควรลองฉบับเต็มบนบอร์ด
- เรียนมาก่อน: บทเรียน 1.6 — แกะแอป Edge AI: ทะเบียนโมเดล verdict และ action
ทั้งไฟล์อ่านเป็นประโยคเดียว: หาโมเดลจากชื่อ → สั่งรัน → วนอ่านผลขึ้นจอทุกราว 180 ms → พอเจอคลาสเป้าหมายที่มั่นใจพอก็สั่งการ → หยุดตอนออก
ช่อง 1–3 ทวนของเดิมจากบทเรียน 1.3 (select(model['index']) ภายใน try, r = edge_ai.result(), verdict.text(...))
ช่อง 4 คือของใหม่ fire_action(r['conf']) ที่อยู่ในเงื่อนไข if hit and not fired: ครึ่งซ้ายของวงจร (เซนเซอร์ → โมเดล) เกิดบน CM55
อัตโนมัติ ครึ่งขวา (result → จอ → action) คือโค้ด Python ของเราบน CM33 ทั้งหมด
จุด remix มีสองชั้น ชั้นแรกอยู่บนหัวไฟล์สองบรรทัด: MODEL_KEYWORD สลับทั้งโมเดลและเซนเซอร์ (IMU → MIC → RADAR) ส่วน
TARGET_CLASS ต้องสะกดตรงกับชื่อใน labels ของโมเดลนั้นเป๊ะ (ดูได้จากบรรทัดในคอนโซลหรือ edge_ai.models()) เช่น
"Cough" คู่กับ "cough" ชั้นที่สองคือ fire_action() ที่แก้ได้ที่เดียว เปลี่ยนโน้ตของ ui.tone, ใช้ ui.sfx(ui.SFX_UI_SELECT),
บี๊บสองระดับตามความมั่นใจ หรือนับจำนวนครั้ง โดยไม่กระทบสิ่งที่แอปตรวจจับ
ตอนจบ บล็อก finally หยุดเครื่องยนต์เสมอ ถ้าใช้ on_result (อย่างในฉบับเต็ม) ต้องถอน callback ด้วย edge_ai.on_result(None) ก่อน stop()
ความสำเร็จของบทเรียนไม่ใช่แค่เห็นแบนเนอร์ แต่ต้องบอกได้ว่าแอปนี้ต่างจากบทเรียน 1.3 ตรงไหน และ remix ของคุณเปลี่ยนอะไร
ตัวอย่างสมบูรณ์
หัวข้อที่มีชื่อว่า “ตัวอย่างสมบูรณ์”s03_anatomy_edgeai_full.py ยกไปใช้ on_result แยกสีด้วย CONF_FLOOR นับจำนวน action และโชว์ latency
ลองอ่านหลังไฟล์ฝึกทำงานแล้ว สังเกตว่าลูปหลักเหลือแค่รับปุ่ม เพราะงานอ่านผลย้ายไปอยู่ใน callback
| ไฟล์ | ไฟล์นี้สอน |
|---|---|
| examples/s03_anatomy_edgeai_full.py | เฝ้าจับคลาสด้วย on_result แล้วสั่งการ (ฉบับเต็ม) |
ฝึกเติม
หัวข้อที่มีชื่อว่า “ฝึกเติม”เติมตามลำดับ 1) edge_ai.select(model['index']) หลัง find_model 2) r = edge_ai.result() 3) verdict.text(r['label'] or '-')
4) fire_action(r['conf']) ในเงื่อนไข hit ตั้ง MODEL_KEYWORD/TARGET_CLASS ที่อยากลองก่อน (เริ่มที่ Motion กับ shaking ก็ได้)
ถ้าจอค้างที่ --- แปลว่าช่อง 1 ยังว่าง ถ้าคลาสขึ้นถูกแต่ไม่มีเสียงและแบนเนอร์ แปลว่าช่อง 4 ยังว่าง
| ไฟล์ฝึก | เรื่อง |
|---|---|
| practice/s03_anatomy_edgeai.py | แกะแอป Edge AI แล้ว remix: สลับโมเดล + สั่งการเมื่อเจอคลาส (ฉบับฝึกเติมโค้ด) |
เปิดเฉลยหลังจากลองเองแล้วอย่างน้อยหนึ่งรอบ และอ่าน วิธีใช้เฉลย ก่อน
| เฉลย | คู่กับ |
|---|---|
| solution/s03_anatomy_edgeai.py | practice/s03_anatomy_edgeai.py |
เช็กความเข้าใจ
หัวข้อที่มีชื่อว่า “เช็กความเข้าใจ”คำถามชุดเดียวกันอยู่ใน quiz.yaml สำหรับระบบที่ตรวจอัตโนมัติ
-
เติมไฟล์ครบแล้ว หน้าจอขึ้นคลาส shaking ถูกต้อง แต่ไม่มีเสียงบี๊บและไม่มีแบนเนอร์เลย ช่องใดยังว่าง (เลือกหนึ่งข้อ · เป้าหมายข้อ 1)
- ก) ช่อง 1 edge_ai.select(model[‘index’])
- ข) ช่อง 2 r = edge_ai.result()
- ค) ช่อง 4 fire_action(r[‘conf’])
- ง) ไม่มีช่องไหนว่าง เป็นเพราะ Emulator ไม่มีลำโพง
เฉลย
ค — คลาสขึ้นจอแปลว่าช่อง 1–3 ทำงานแล้ว ช่อง 4 คือจุดที่ verdict กลายเป็น action ถ้าว่าง แอปจะ “รายงาน” แต่ไม่ “ลงมือ”
-
อยากเปลี่ยนแอปเป็นตัวจับเสียงไอ ต้องตั้งสองบรรทัดบนหัวไฟล์อย่างไร (เลือกหนึ่งข้อ · เป้าหมายข้อ 2)
- ก) MODEL_KEYWORD = “Cough”, TARGET_CLASS = “cough”
- ข) MODEL_KEYWORD = “cough”, TARGET_CLASS = “Cough Detection”
- ค) MODEL_KEYWORD = “Motion”, TARGET_CLASS = “cough”
- ง) MODEL_KEYWORD = 3, TARGET_CLASS = 1
เฉลย
ก — find_model ค้นคำในชื่อโดยไม่สนตัวพิมพ์ แต่ TARGET_CLASS ต้องตรงกับ labels ของโมเดลเป๊ะ ซึ่งของ Cough Detection คือ unlabelled กับ cough
-
ผลคือ {‘label’: ‘shaking’, ‘conf’: 0.42} และ TARGET_CLASS = ‘shaking’ action จะเป็นอย่างไร (เลือกหนึ่งข้อ · เป้าหมายข้อ 3)
- ก) ยิงทันทีเพราะคลาสตรง
- ข) ไม่ยิง เพราะ hit ต้องการ conf ≥ CONF_FLOOR (0.50) ด้วย และ fired ถูกรีเซ็ตเป็น False
- ค) ยิงสองครั้ง
- ง) โปรแกรมโยน OSError
เฉลย
ข — hit เป็น False เพราะ conf ยังไม่ถึงเกณฑ์ โค้ดจึงเข้า elif not hit ซึ่งรีเซ็ต fired และขึ้นแบนเนอร์ “รอจับ” นี่คือตัวกัน false positive
-
เรียงเหตุการณ์เมื่อผู้ใช้เขย่าบอร์ดสองรอบ (เขย่า หยุด เขย่า) โดยเป้าหมายคือ shaking (เรียงลำดับ · เป้าหมายข้อ 3)
- ก) hit เป็น False จึงรีเซ็ต fired = False
- ข) hit เป็น True และ fired เป็น False จึงยิง action แล้วตั้ง fired = True
- ค) hit เป็น True อีกครั้ง fired เป็น False จึงยิง action รอบที่สอง
- ง) hit ยังเป็น True แต่ fired เป็น True จึงไม่ยิงซ้ำ
เฉลย
ข → ง → ก → ค — edge-trigger ยิงตอนเพิ่งเข้าคลาส ไม่ยิงซ้ำระหว่างที่ยังเขย่าค้าง รีเซ็ตเมื่อออกจากคลาส แล้วพร้อมยิงใหม่เมื่อเข้าคลาสอีกครั้ง
MVP ของชุดบทเรียน 1.6–1.7: remix s03_anatomy_edgeai.py ได้จริง คือสลับโมเดล (เปลี่ยน MODEL_KEYWORD และ TARGET_CLASS) และสั่งการเมื่อเจอคำตัดสินที่เข้าเงื่อนไข
- เติมไฟล์ฝึกครบสี่ช่อง ทำท่าหรือเสียงจนได้ยินบี๊บหนึ่งครั้งและเห็นแบนเนอร์
- remix เป็นโมเดลอื่นอย่างน้อยหนึ่งตัว (เช่น Motion → Cough) แล้วยืนยันว่า action ยิงตามคลาสใหม่
- หาท่าหรือเสียงที่คลาสเป้าหมายชนะแต่ conf ต่ำกว่า CONF_FLOOR แล้วจดลงบันทึกการเรียนว่าทำไม action ไม่ยิง
- อธิบายได้ว่าทำไมต้องเช็กทั้ง label และ conf และทำไมต้องมีธง fired
โมดูลถัดไปเราเปิดเสาที่ 1 (DAQ) เริ่มเก็บข้อมูลเซนเซอร์ของเราเองลงไฟล์ CSV เพื่อเป็นวัตถุดิบฝึกโมเดลในโมดูล 5
บทเรียนถัดไป: บทเรียน 2.1 — สุ่มสัญญาณให้ตรงกับโมเดล: อัตราสุ่ม Nyquist หน้าต่าง และ schema ของ CSV
สะท้อนคิด
หัวข้อที่มีชื่อว่า “สะท้อนคิด”- action ที่คุณเลือกใน remix ควรยิงครั้งเดียวต่อการเจอ หรือควรยิงซ้ำเป็นระยะ ถ้ายิงซ้ำ คุณจะออกแบบธงหรือตัวจับเวลาอย่างไร
- ถ้าต้องเอาแอปนี้ไปใช้กับโมเดลที่คลาสเป้าหมายไม่ได้อยู่ลำดับเดียวกัน โค้ดของคุณต้องแก้ตรงไหนบ้าง
คำถามทบทวน
ลองตอบเองก่อน แล้วค่อยเปิดดูเฉลย
-
เติมไฟล์ครบแล้ว หน้าจอขึ้นคลาส shaking ถูกต้อง แต่ไม่มีเสียงบี๊บและไม่มีแบนเนอร์เลย ช่องใดยังว่าง (เป้าหมายข้อ 1)
- ช่อง 1 edge_ai.select(model['index'])
- ช่อง 2 r = edge_ai.result()
- ช่อง 4 fire_action(r['conf'])
- ไม่มีช่องไหนว่าง เป็นเพราะ Emulator ไม่มีลำโพง
ดูเฉลย
คำตอบ: C. ช่อง 4 fire_action(r['conf'])
คลาสขึ้นจอแปลว่าช่อง 1–3 ทำงานแล้ว ช่อง 4 คือจุดที่ verdict กลายเป็น action ถ้าว่าง แอปจะ "รายงาน" แต่ไม่ "ลงมือ"
-
อยากเปลี่ยนแอปเป็นตัวจับเสียงไอ ต้องตั้งสองบรรทัดบนหัวไฟล์อย่างไร (เป้าหมายข้อ 2)
- MODEL_KEYWORD = "Cough", TARGET_CLASS = "cough"
- MODEL_KEYWORD = "cough", TARGET_CLASS = "Cough Detection"
- MODEL_KEYWORD = "Motion", TARGET_CLASS = "cough"
- MODEL_KEYWORD = 3, TARGET_CLASS = 1
ดูเฉลย
คำตอบ: A. MODEL_KEYWORD = "Cough", TARGET_CLASS = "cough"
find_model ค้นคำในชื่อโดยไม่สนตัวพิมพ์ แต่ TARGET_CLASS ต้องตรงกับ labels ของโมเดลเป๊ะ ซึ่งของ Cough Detection คือ unlabelled กับ cough
-
ผลคือ {'label': 'shaking', 'conf': 0.42} และ TARGET_CLASS = 'shaking' action จะเป็นอย่างไร (เป้าหมายข้อ 3)
- ยิงทันทีเพราะคลาสตรง
- ไม่ยิง เพราะ hit ต้องการ conf ≥ CONF_FLOOR (0.50) ด้วย และ fired ถูกรีเซ็ตเป็น False
- ยิงสองครั้ง
- โปรแกรมโยน OSError
ดูเฉลย
คำตอบ: B. ไม่ยิง เพราะ hit ต้องการ conf ≥ CONF_FLOOR (0.50) ด้วย และ fired ถูกรีเซ็ตเป็น False
hit เป็น False เพราะ conf ยังไม่ถึงเกณฑ์ โค้ดจึงเข้า elif not hit ซึ่งรีเซ็ต fired และขึ้นแบนเนอร์ "รอจับ" นี่คือตัวกัน false positive
-
เรียงเหตุการณ์เมื่อผู้ใช้เขย่าบอร์ดสองรอบ (เขย่า หยุด เขย่า) โดยเป้าหมายคือ shaking (เป้าหมายข้อ 3)
- hit เป็น False จึงรีเซ็ต fired = False
- hit เป็น True และ fired เป็น False จึงยิง action แล้วตั้ง fired = True
- hit เป็น True อีกครั้ง fired เป็น False จึงยิง action รอบที่สอง
- hit ยังเป็น True แต่ fired เป็น True จึงไม่ยิงซ้ำ
ดูเฉลย
ลำดับที่ถูก: B. hit เป็น True และ fired เป็น False จึงยิง action แล้วตั้ง fired = True → D. hit ยังเป็น True แต่ fired เป็น True จึงไม่ยิงซ้ำ → A. hit เป็น False จึงรีเซ็ต fired = False → C. hit เป็น True อีกครั้ง fired เป็น False จึงยิง action รอบที่สอง
edge-trigger ยิงตอนเพิ่งเข้าคลาส ไม่ยิงซ้ำระหว่างที่ยังเขย่าค้าง รีเซ็ตเมื่อออกจากคลาส แล้วพร้อมยิงใหม่เมื่อเข้าคลาสอีกครั้ง
อ้างอิงบทเรียนนี้
ถ้านำบทเรียนนี้ไปสอน ทำสไลด์ หรือทำเอกสารต่อ ให้อ้างอิงด้วยข้อความนี้ ถ้าดัดแปลงเนื้อหา ให้เติม (ดัดแปลง)ต่อท้ายชื่อบทเรียน
"ลงมือทำ: จาก verdict สู่ action บนบอร์ด" จาก TESA Open Knowledge โดยสมาคมสมองกลฝังตัวไทย (Thai Embedded Systems Association: TESA) https://github.com/tesaiot/tesa-qualification-program สัญญาอนุญาต CC BY-NC 4.0
ข้อความอ้างอิงภาษาอังกฤษ: "Hands-on: from verdict to action on the board" 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/m01-onboarding/l07-verdict-action-lab/
TESA Open Knowledge · © 2026 สมาคมสมองกลฝังตัวไทย (TESA) · CC BY-NC 4.0
เนื้อหาเผยแพร่ภายใต้ CC BY-NC 4.0 นำไปใช้ต่อในงานที่ไม่ใช่เพื่อการค้าได้ โปรดอ้างอิงสมาคมสมองกลฝังตัวไทย (TESA) ทุกครั้ง · วิธีอ้างอิง TESA