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

ลงมือทำ: action pipeline ที่กัน false positive

โมดูล 6 — แอป Edge AI · สไลด์: slides.md · ภาพรวมโมดูล · หน้าหลักสูตร

เติมสี่ด่านของท่อใน s16_action_pipeline.py คือ อ่านผล กรอง debounce+cooldown และยิง action จนไฟบนจอไล่ฟ้า เหลือง แดง พร้อมเสียงและ log เมื่อคลาสเป้าหมายค้างนานพอ แล้วจูน NEED_HITS กับ COOLDOWN_MS และพิสูจน์ว่าท่อปฏิเสธสัญญาณปลอมได้จริง

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

  1. เติมสี่จุดใน practice/s16_action_pipeline.py จนการเจอคลาสเป้าหมายต่อเนื่องครบ NEED_HITS ยิงไฟแดง เสียง และ log หนึ่งบรรทัด ส่วนสัญญาณเฟรมเดียวไม่ยิง
  2. จูน NEED_HITS อย่างน้อยสามค่า (เช่น 1, 3, 6) บันทึกจำนวน false positive และ false negative ของแต่ละค่า แล้วเลือกค่าให้งานที่กำหนดพร้อมเหตุผล
  3. ชี้ได้ว่าสัญญาณปลอมที่คุณสร้างถูกด่านใดกันไว้ และอธิบายว่าถ้าลืม last_fire = now จะเกิดอะไร

ผ่านบทเรียน 6.3 มาแล้ว เข้าใจท่อสี่ด่าน EMA และการยิงตอนขอบขึ้น เตรียมบันทึกการเรียนไว้จดตารางจูน NEED_HITS

  • อุปกรณ์: บอร์ด TESAIoT Dev Kit ที่ลงเฟิร์มแวร์ MicroPython ของ BENTO แล้ว หรือ BENTO Emulator ใน BENTO IDE — บน Emulator ตั้ง MODEL_KEYWORD = “Motion” กับ TARGET_CLASS = “shaking” แล้วกดปุ่ม Shake ค้างเพื่อซ้อมท่อ ส่วนเสียงไอจริงต้องใช้บอร์ด
  • เรียนมาก่อน: บทเรียน 6.3 — ท่อสั่งการ: CONF_FLOOR, debounce, cooldown และ on_result

ทั้งไฟล์อ่านเป็นประโยคเดียว: เลือกโมเดล → วนอ่านผล → เฟรมนี้เจอเป้าหมายไหม → ครบ debounce และ cooldown ไหม → ยิง action ไฟล์ฝึกเตรียม widget การ์ดไฟ fire_action() และ set_light() ไว้แล้ว เหลือสี่จุดตามการไหล: (1) r = edge_ai.result() ถ้าว่าง จอไม่ขึ้นคลาสและท่อไม่เดิน (2) hit = (r['label'] == TARGET_CLASS and r['conf'] >= edge_ai.CONF_FLOOR) ถ้าว่าง streak ไม่ขยับ (3) should_fire = ready and cooled โดย ready = streak >= NEED_HITS และ cooled มาจาก ticks_diff ที่เตรียมไว้ และ (4) fire_action(r['conf']); last_fire = now; streak = 0 คือสั่งการ ตั้งนาฬิกาเว้นช่วงใหม่ และล้างตัวนับ ลืม last_fire = now แล้ว cooldown ไม่เริ่มใหม่ ท่อจะยิงรัวเมื่อเหตุการณ์ค้าง

หัวใจเชิงวิศวกรรมคือการจูน ไม่มีค่าที่ถูกตายตัว NEED_HITS สูงเตือนผิดน้อยแต่พลาดเหตุการณ์สั้น CONF_FLOOR สูงเชื่อเฉพาะที่มั่นใจมาก COOLDOWN_MS ยาวไม่รบกวนซ้ำแต่เหตุการณ์จริงสองครั้งติดกันจะเห็นครั้งเดียว วัดผลด้วยการนับ false positive (ยิงทั้งที่ไม่มีเหตุ) เทียบกับ false negative (มีเหตุแต่ไม่ยิง) แล้วถามว่างานนี้พลาดแบบไหนแพงกว่า ความสำเร็จไม่ใช่แค่ไฟติด แต่ต้องแสดงว่าท่อ ปฏิเสธ สัญญาณปลอมได้

s16_action_pipeline_full.py เพิ่ม dsp.EMA smoothing ที่ EMA_ALPHA = 0.35 ไฟสี่สถานะ on_result ที่ log เฉพาะตอนคลาสเปลี่ยน และตัวนับ blocked ที่นับครั้งที่ cooldown กันการยิงซ้ำไว้ ลองไอหรือเขย่าค้างยาวแล้วดู blocked เพิ่ม

ไฟล์ ไฟล์นี้สอน
examples/s16_action_pipeline_full.py action pipeline ครบวง: กรอง + smooth + debounce + action (ฉบับเต็ม)

คอมเมนต์ # เติม: อยู่ที่บรรทัด 120 (อ่านผล), 134 (กรอง), 152 (debounce + cooldown) และ 157 (ยิง action) ถ้าไอแวบเดียวแล้วยิง แปลว่าด่าน 2 หรือ 3 ยังไม่ทำงาน ถ้าเขย่าค้างแล้วยิงรัว ตรวจ last_fire = now ในจุดที่ 157

ไฟล์ฝึก เรื่อง
practice/s16_action_pipeline.py จาก verdict สู่ action จริง: RGB + เสียง + log (ฉบับฝึกเติมโค้ด)

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

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

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

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

    • ก) จุดที่ 2 hit ยังเป็น False
    • ข) จุดที่ 4
    • ค) จุดที่ 3
    • ง) ไม่มีจุดใดผิด
    เฉลย

    ก — streak เพิ่มเมื่อ hit เป็นจริงเท่านั้น ถ้า hit ยังเป็นค่าเริ่มต้น False ท่อจะรีเซ็ตทุกเฟรม

  2. ไฟขึ้นเหลือง 1/3 และ 2/3 ตามการเขย่า แต่ไม่เคยแดง ไม่มีเสียงหรือ log จุดใดยังว่าง (เลือกหนึ่งข้อ · เป้าหมายข้อ 1)

    • ก) จุดที่ 1
    • ข) จุดที่ 2
    • ค) จุดที่ 3 หรือ 4 (should_fire ยังเป็น False หรือยังไม่เรียก fire_action)
    • ง) COOLDOWN_MS เป็นศูนย์
    เฉลย

    ค — streak ขยับแปลว่าด่าน 1 และ 2 ทำงาน ปลายท่อที่ไม่ยิงอยู่ที่เงื่อนไข should_fire หรือการเรียก fire_action

  3. NEED_HITS = 1 ผลการทดสอบน่าจะเป็นแบบใด (เลือกหนึ่งข้อ · เป้าหมายข้อ 2)

    • ก) ไม่เคยยิง
    • ข) ไวที่สุดแต่ false positive มากที่สุด เพราะเฟรมเดียวที่ผ่านด่านกรองก็ยิง
    • ค) พลาดเหตุการณ์จริงบ่อยที่สุด
    • ง) เหมือน NEED_HITS = 6
    เฉลย

    ข — debounce ที่ 1 เฟรมเท่ากับไม่มี debounce ส่วนค่าสูงอย่าง 6 จะพลาดเหตุการณ์สั้นของจริงบ่อยขึ้น

  4. ลืม last_fire = now ในจุดที่ 4 แล้วเขย่าค้างยาว จะเกิดอะไร (เลือกหนึ่งข้อ · เป้าหมายข้อ 3)

    • ก) ยิงครั้งเดียวตามปกติ
    • ข) cooldown ไม่เริ่มนับใหม่ ท่อยิงซ้ำทุกครั้งที่ streak ครบ
    • ค) ไม่ยิงเลย
    • ง) โปรแกรม error
    เฉลย

    ข — cooled ยังเป็นจริงตลอดเพราะ last_fire ไม่ขยับ ด่าน cooldown จึงไม่มีผล

MVP ของชุดบทเรียน 6.3–6.4: action pipeline แบบ debounce ที่คลาสเป้าหมายต่อเนื่องจุดชนวน action จริง ส่วนสัญญาณกระพริบสั้น ๆ ถูกกันไว้

  • เติมไฟล์ฝึกครบสี่จุด ซ้อมบน Emulator ด้วย Motion กับปุ่ม Shake แล้วลองกับเสียงไอจริงบนบอร์ด
  • จูน NEED_HITS = 1, 3, 6 ทำการทดสอบเดียวกันสิบครั้งต่อค่า แล้วจดตาราง false positive กับ false negative ลงบันทึกการเรียน
  • สร้างสัญญาณที่ควรถูกกัน (กด Shake แวบเดียวหรือไอครั้งเดียว) ยืนยันว่าไม่ยิง และบอกว่าด่านใดกันไว้
  • เลือกค่าให้สองงาน (เตือนไฟไหม้ กับ เตือนขโมย) พร้อมเหตุผล

ชุดบทเรียนถัดไป (บทเรียน 6.5–6.6) เราจะรวม verdict กับเซนเซอร์ดิบ (sensor fusion) แล้วส่งเหตุการณ์ออกเน็ตด้วย WiFi และ MQTT

บทเรียนถัดไป: บทเรียน 6.5 — sensor fusion: verdict ของโมเดลกับเซนเซอร์ดิบ

  • ค่าที่คุณเลือกสำหรับเตือนขโมยกับเตือนไฟไหม้ต่างกันอย่างไร และคุณจะอธิบายให้ลูกค้าฟังอย่างไร
  • ถ้า log ของคุณมีแต่ ALERT แต่ไม่มีบันทึกการกันยิง คุณจะรู้ได้อย่างไรว่าเกราะทำงานอยู่

คำถามทบทวน

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

  1. ไฟค้างสีฟ้าตลอดแม้เขย่าค้างนาน streak ไม่ขยับเลย จุดใดยังว่าง (เป้าหมายข้อ 1)

    1. จุดที่ 2 hit ยังเป็น False
    2. จุดที่ 4
    3. จุดที่ 3
    4. ไม่มีจุดใดผิด
    ดูเฉลย

    คำตอบ: A. จุดที่ 2 hit ยังเป็น False

    streak เพิ่มเมื่อ hit เป็นจริงเท่านั้น ถ้า hit ยังเป็นค่าเริ่มต้น False ท่อจะรีเซ็ตทุกเฟรม

  2. ไฟขึ้นเหลือง 1/3 และ 2/3 ตามการเขย่า แต่ไม่เคยแดง ไม่มีเสียงหรือ log จุดใดยังว่าง (เป้าหมายข้อ 1)

    1. จุดที่ 1
    2. จุดที่ 2
    3. จุดที่ 3 หรือ 4 (should_fire ยังเป็น False หรือยังไม่เรียก fire_action)
    4. COOLDOWN_MS เป็นศูนย์
    ดูเฉลย

    คำตอบ: C. จุดที่ 3 หรือ 4 (should_fire ยังเป็น False หรือยังไม่เรียก fire_action)

    streak ขยับแปลว่าด่าน 1 และ 2 ทำงาน ปลายท่อที่ไม่ยิงอยู่ที่เงื่อนไข should_fire หรือการเรียก fire_action

  3. NEED_HITS = 1 ผลการทดสอบน่าจะเป็นแบบใด (เป้าหมายข้อ 2)

    1. ไม่เคยยิง
    2. ไวที่สุดแต่ false positive มากที่สุด เพราะเฟรมเดียวที่ผ่านด่านกรองก็ยิง
    3. พลาดเหตุการณ์จริงบ่อยที่สุด
    4. เหมือน NEED_HITS = 6
    ดูเฉลย

    คำตอบ: B. ไวที่สุดแต่ false positive มากที่สุด เพราะเฟรมเดียวที่ผ่านด่านกรองก็ยิง

    debounce ที่ 1 เฟรมเท่ากับไม่มี debounce ส่วนค่าสูงอย่าง 6 จะพลาดเหตุการณ์สั้นของจริงบ่อยขึ้น

  4. ลืม last_fire = now ในจุดที่ 4 แล้วเขย่าค้างยาว จะเกิดอะไร (เป้าหมายข้อ 3)

    1. ยิงครั้งเดียวตามปกติ
    2. cooldown ไม่เริ่มนับใหม่ ท่อยิงซ้ำทุกครั้งที่ streak ครบ
    3. ไม่ยิงเลย
    4. โปรแกรม error
    ดูเฉลย

    คำตอบ: B. cooldown ไม่เริ่มนับใหม่ ท่อยิงซ้ำทุกครั้งที่ streak ครบ

    cooled ยังเป็นจริงตลอดเพราะ last_fire ไม่ขยับ ด่าน cooldown จึงไม่มีผล

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

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

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

ข้อความอ้างอิงภาษาอังกฤษ: "Hands-on: an action pipeline that resists false positives" 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/m06-apps/l04-action-pipeline-lab/

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

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

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