บทเรียน 1.7 — ลงมือทำ: จาก verdict สู่ action บนบอร์ด

แกะแอป Edge AI · จาก verdict สู่ action

โมดูล 1 — เริ่มต้น: รันของจริงแล้วแกะดูข้างใน

ต่อจากบทเรียน 1.6 — แกะแอป Edge AI: ทะเบียนโมเดล verdict และ action

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

ไล่หนึ่งรอบเต็ม — sensor → model → UI → action

รวมทุกจังหวะเป็นภาพเดียว: จากเซนเซอร์บนบอร์ด จนถึงเสียงบี๊บที่คุณได้ยิน — นี่คือวงจรที่แอปชุดบทเรียนนี้เดินทุก ~180 ms

เซนเซอร์ IMU/RADAR/MIC โมเดล (M55) NPU อนุมาน result() verdict dict จอ + เงื่อนไข label + conf action บี๊บ + แบนเนอร์ CM55 + NPU โค้ด Python บน CM33 วนรอบใหม่ทุก ~180 ms

ครึ่งซ้าย (เซนเซอร์ → โมเดล) เกิดบน CM55 อัตโนมัติ เราไม่แตะ · ครึ่งขวา (result → จอ → action) คือโค้ด Python ของเราบน CM33 — ชุดบทเรียนนี้เราคุมครึ่งขวาทั้งหมด

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

โครงของไฟล์ s03_anatomy_edgeai.py

ทั้งไฟล์อ่านเป็นประโยคเดียว: "หาโมเดลจากชื่อ → สั่งรัน → วนอ่านผลขึ้นจอ → พอเจอคลาสเป้าหมายมั่นใจพอ ก็สั่งการ → หยุดตอนออก"

find_model :37 (ให้ไว้แล้ว) select ช่อง 1 อ่านผล + จอ ช่อง 2+3 hit? ออก→stop fire_action ช่อง 4 · ยิงครั้งเดียว no yes → สั่งการ แถวล่าง = จังหวะ 3 (action) ที่ต่อจากการวาดจอ

ตัวเลขช่อง (1–4) ชี้จุดที่คุณต้องเติมในไฟล์ฝึก — เหมือนบทเรียน 1.1–1.3 แต่ช่องที่ 4 เปลี่ยนจาก stop() มาเป็น fire_action() ซึ่งคือหัวใจใหม่ของชุดบทเรียนนี้

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

ไล่โค้ด (1) — find_model + select

ช่องเติมที่ 1: หาโมเดลจากชื่อ (ให้ไว้แล้ว) แล้วสั่งให้ CM55 เริ่มรัน:

model = find_model(MODEL_KEYWORD)   # ให้ไว้แล้ว: คืน dict ทั้งก้อน
labels = model['labels']

try:
    # เติม: สั่งให้ CM55 รันโมเดลนี้ ด้วย edge_ai.select(model['index'])
    pass
    lcd.console('<span class=ok> เริ่มอนุมาน…</span>')
except OSError as e:
    lcd.console('<span class=error> โหลดไม่สำเร็จ: %s</span>' % e)
  • แทน pass ด้วย edge_ai.select(model['index']) — เอา index จาก dict ไปสั่ง
  • ต้องอยู่ใน try เพราะ select() โยน OSError ได้ (ข้ามคอร์อาจพลาด — เหมือนบทเรียน 1.1–1.3)
  • ถ้าลืมเติม: จอค้างที่ --- ตลอด เพราะไม่มีโมเดลรัน result() คืน None

MODEL_KEYWORD อยู่บนหัวไฟล์ — remix ได้ทันที เปลี่ยนเป็น "Cough" แอปก็กลายเป็นตัวจับเสียงไอ โดยไม่แตะ logic ข้างล่างเลย

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

ไล่โค้ด (2) — อ่านผล + verdict.text

ช่องเติมที่ 2 และ 3: จังหวะ 2 เดิมจากบทเรียน 1.1–1.3 อ่าน verdict แล้วเอาคลาสที่ชนะขึ้นจอ:

# เติม: อ่านผลอนุมานล่าสุดมาเก็บใน r  ->  r = edge_ai.result()
r = None
pass
if r and r['seq'] != last_seq:          # มีผลใหม่จริงไหม (seq เปลี่ยน)
    last_seq = r['seq']
    # เติม: แสดงคลาสที่ชนะ ด้วย verdict.text(r['label'] or '-')
    pass
    conf.text("conf: %.0f %%" % (r['conf'] * 100))
    top = r['top']
    for i, (lb, br) in enumerate(rows):
        if i < len(r['scores']):
            br.value(int(r['scores'][i] * 100))
            br.color(GREEN if i == top else DIM)
  • ช่อง 2: r = edge_ai.result() · ช่อง 3: verdict.text(r['label'] or '-')
  • เช็ก seq ก่อนเสมอ — วาดจอเฉพาะตอนมีผลใหม่ (เหตุผลเดียวกับบทเรียน 1.1–1.3)

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

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

ไล่โค้ด (3) — เงื่อนไข hit + fire_action

ช่องเติมที่ 4: จังหวะ 3 (ของใหม่) — พอวาดจอเสร็จ เช็กว่าเข้าเงื่อนไขไหม ถ้าใช่ก็สั่งการ:

    hit = (r['label'] == TARGET_CLASS
           and r['conf'] >= edge_ai.CONF_FLOOR)   # label ตรง + มั่นใจพอ
    if hit and not fired:
        # เติม: สั่งการเมื่อจับ TARGET ได้ครั้งแรก -> fire_action(r['conf'])
        pass
        fired = True
    elif not hit:
        fired = False                              # ออกจากคลาสแล้ว รีเซ็ต
        banner.text("รอจับ %s ..." % TARGET_CLASS)
        banner.color(RED)
  • แทน pass ด้วย fire_action(r['conf']) — ส่ง conf เข้าไปให้ action โชว์
  • and not fired = ยิงครั้งเดียวต่อการเจอ (edge-trigger) ไม่บี๊บรัว
  • ถ้าลืมเติม: จอโชว์คลาสถูก แต่ไม่มีเสียง ไม่มีแบนเนอร์ — แอปยัง "ไม่ลงมือ"

นี่คือบรรทัดที่ทำให้แอปชุดบทเรียนนี้ต่างจากบทเรียน 1.1–1.3 — verdict กลายเป็น action ตรงจุดนี้เป๊ะ เติมเสร็จเมื่อไร แอปของคุณ "ทำงาน" ไม่ใช่แค่ "รายงาน"

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

ไล่โค้ด (4) — หยุดให้เรียบร้อย

ปุ่ม back และการเก็บกวาดตอนออก (ให้ไว้แล้ว — เหมือนบทเรียน 1.1–1.3):

finally:
    edge_ai.stop()      # ให้ไว้แล้ว: ออกยังไงก็หยุดเครื่องยนต์เสมอ
    lcd.console('<span class=ok> จบการทำงาน</span>')
  • บล็อก finally ให้ไว้แล้ว: ไม่ว่าจะออกด้วยปุ่ม back หรือ error โมเดลจะถูกหยุดเสมอ
  • ในฉบับเต็มที่ใช้ on_result ต้องถอน callback ด้วย: edge_ai.on_result(None) ก่อน stop()

นิสัย embedded เดิม: ออกจากงานยังไง ทิ้งเครื่องไว้ให้เรียบร้อยแบบนั้น — หยุดโมเดล ถอน callback ที่ต่อไว้ ไม่ปล่อยอะไรค้างหลังโปรแกรมจบ

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

remix (1) — สลับโมเดล + เปลี่ยนคลาสเป้าหมาย

หัวใจของ MVP วันนี้คือ remix — และจุด remix แรกอยู่บนหัวไฟล์แค่สองบรรทัด:

MODEL_KEYWORD = "Motion"     # → "Baby Cry" / "Cough" / "Siren" / "Push"
TARGET_CLASS  = "shaking"    # → คลาสของโมเดลใหม่ (ดูจาก labels ในคอนโซล)
  • เปลี่ยน MODEL_KEYWORD = สลับทั้งโมเดลและเซนเซอร์ (IMU → MIC → RADAR) โดยไม่แตะ logic
  • เปลี่ยน TARGET_CLASS = เปลี่ยนว่า "จับอะไรแล้วสั่งการ" — ต้องเป็นชื่อจาก labels ของโมเดลนั้นเป๊ะ
  • ตัวอย่าง remix เสียงไอ: MODEL_KEYWORD = "Cough", TARGET_CLASS = "cough"

อยากรู้ว่าโมเดลใหม่มีคลาสอะไร? รันแล้วดูบรรทัด โมเดล: ... คลาส: ... ในคอนโซล หรือถาม REPL: edge_ai.models() — ถามฮาร์ดแวร์ก่อนเสมอ

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

remix (2) — เปลี่ยน action

จุด remix ที่สองคือ การกระทำ — แก้แค่ใน fire_action() ที่เดียว logic ตรวจจับไม่ต้องแตะ:

  • เปลี่ยนเสียง — ui.tone(60, ...) (โดกลาง) แทน 72 หรือใช้ ui.sfx(ui.SFX_UI_SELECT) เล่นเสียงสำเร็จรูป
  • action สองระดับ — ถ้า conf สูงมากบี๊บสองครั้ง ถ้าพอผ่านเกณฑ์บี๊บครั้งเดียว
  • นับเหตุการณ์ — เก็บตัวนับว่ายิง action ไปกี่ครั้ง แล้วโชว์บนจอ (ฉบับเต็มทำแบบนี้)
  • แจ้งเตือนจริง — (ต่อยอด โมดูล 6 (Apps)) ส่ง MQTT / เขียนไฟล์ log เมื่อเจอ

เพราะเราแยก "ตัดสินใจ" ออกจาก "ลงมือ" การ remix action จึงปลอดภัย — คุณเปลี่ยนสิ่งที่แอป ทำ ได้ โดยไม่กระทบสิ่งที่แอป ตรวจจับ

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

ลงมือทำ — เติมช่องว่างทั้ง 4 จุด

เปิด s03_anatomy_edgeai.py ในไฟล์มี pass วางไว้ 4 จุด ตรงที่ต้องเติมคำสั่งจริง:

# จุด เติมด้วย ถ้าลืม
1 หลัง find_model edge_ai.select(model['index']) จอค้าง --- ไม่มีผล
2 ในลูป r = edge_ai.result() จอไม่ขึ้นคลาสเลย
3 มีผลใหม่ verdict.text(r['label'] or '-') แถบขยับ แต่ตัวใหญ่ไม่เปลี่ยน
4 เข้าเงื่อนไข hit fire_action(r['conf']) เจอคลาสถูก แต่ไม่บี๊บ ไม่มีแบนเนอร์

ขั้นตอน:

  1. ตั้ง MODEL_KEYWORD + TARGET_CLASS ที่อยากลอง (เริ่มจาก Motion/shaking ก็ได้)
  2. ไล่หา # เติม: ทีละจุด แล้วแทน pass ด้วยคำสั่งตามคำใบ้
  3. กด Run (Emulator) หรือ Program to Device (บอร์ด) ทำท่า/ส่งเสียงจนแบนเนอร์ขึ้น + ได้ยินบี๊บ

ช่อง 1–3 คือของเดิมจากบทเรียน 1.1–1.3 (แค่ทวน) ช่อง 4 คือของใหม่ — เติมครบเมื่อไร คุณได้แอป Edge AI ที่ "ลงมือทำ" ตัวแรกของตัวเอง

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

แหล่งเรียนรู้เพิ่มเติม — softmax และความมั่นใจ

อยากเข้าใจ softmax/argmax ให้ลึกกว่าในบทเรียน ลองดูสื่อคุณภาพเหล่านี้ (ลิงก์ไปต้นทาง ไม่ได้ฝังวิดีโอไว้ในสไลด์):

วิดีโอ

เอกสาร / ภาพอ้างอิง

วิดีโอ/ภาพภายนอกเป็นของเจ้าของต้นฉบับ ใช้เพื่อการศึกษา อ้างอิงลิงก์ต้นทาง

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

ชัยชนะที่เห็นได้ + MVP ของชุดบทเรียนนี้

ชัยชนะที่เห็นได้ของชุดบทเรียนนี้ · remix แอป Edge AI ให้จับคลาสเป้าหมายได้เอง แล้ว "บี๊บ + ขึ้นแบนเนอร์" สั่งการจริง

MVP ของบทเรียน 1.6–1.7 (เกณฑ์ผ่านของชุดบทเรียน): คุณ remix s03_anatomy_edgeai.py ได้จริง — สลับโมเดล (เปลี่ยน MODEL_KEYWORD/TARGET_CLASS) + สั่งการเมื่อเจอ verdict ที่เข้าเงื่อนไข

  • ทำบน Emulator หรือ บอร์ดจริง ก็ได้ (โค้ดชุดเดียวกัน)
  • อธิบายได้ว่าทำไม action เช็กทั้ง label และ conf >= CONF_FLOOR และทำไมต้องมี fired

"สั่งการได้" ไม่ใช่แค่ "เห็นแบนเนอร์" — คุณต้องบอกได้ว่าแอปนี้ต่างจากบทเรียน 1.1–1.3 ตรงไหน (จังหวะ action) และ remix ของคุณเปลี่ยนอะไรไปจากต้นฉบับ

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

บันไดช่วยเหลือ — ใบ้ → เริ่มจากโครง → เฉลย → ฉบับเต็ม

ถ้าติด ให้ไต่บันไดนี้ทีละขั้น อย่าเพิ่งกระโดดไปดูเฉลย เพราะของจะเข้าหัวตอนที่คุณพยายามเองก่อน:

  • ใบ้ — คำใบ้อยู่ในคอมเมนต์ # เติม: ทั้ง 4 จุดในไฟล์ฝึก + ตารางหน้าที่แล้ว บอกว่าแต่ละช่องเติมอะไร
  • เริ่มจากโครง — s03_anatomy_edgeai.py มีโครงครบทั้งไฟล์แล้ว เหลือแค่ 4 บรรทัดให้เติม
  • เฉลย — s03_anatomy_edgeai.py เติมครบพร้อมคอมเมนต์อธิบายทุกช่อง (อ่านให้เข้าใจ ปิดไฟล์ แล้วพิมพ์เอง)
  • ฉบับเต็ม — s03_anatomy_edgeai_full.py ยกไปใช้ on_result + แยกสี CONF_FLOOR + นับจำนวน action + latency

ลองเขียนเองให้สุดก่อนนะ ถ้าติดจริงๆ ค่อยเปิดเฉลยดูทีละช่อง แล้วกลับมาพิมพ์เอง — เดี๋ยวเราค่อย ๆ แกะไปด้วยกัน

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

เชื่อมโยงรากฐาน — วันนี้เราแตะอะไรไปบ้าง

การ remix แอป Edge AI ตัวเดียวซ่อนแนวคิดที่จะใช้ไปตลอด โมดูล 6 (Apps):

ฝั่ง Edge AI / ระบบ

  • Model registry — เลือกโมเดลจากชื่อด้วย find_model() ไม่ hard-code index (ทนต่อการเปลี่ยนเฟิร์มแวร์)
  • Verdict → decision — action ตั้งอยู่บน label + conf >= CONF_FLOOR ไม่ใช่ป้ายคลาสเดียวๆ
  • Verdict → action — จาก inference สู่ application: การกระทำจริงเมื่อเข้าเงื่อนไข
  • Event-driven — on_result(cb) เฟิร์มแวร์เรียก cb ให้ตอนคลาสเปลี่ยน (แทน poll เอง)

ฝั่ง MicroPython / โครงโปรแกรม

  • แยกตัดสินใจจากลงมือ — เงื่อนไข hit แยกจาก fire_action() remix ง่าย
  • edge-trigger — ธง fired ยิง action ครั้งเดียวต่อการเจอ ไม่รัว
  • เก็บกวาดตอนจบ — finally: stop() (+ on_result(None) ในฉบับเต็ม)

ทั้งหมดยังยืนบนคำสั่งเดิมของ edge_ai — ชุดบทเรียนนี้ไม่ได้เพิ่มของยาก แต่สอน "ประกอบ" คำสั่งเดิมให้กลายเป็นแอปที่ลงมือทำได้

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

ใช้จริงที่ไหน — verdict → action ในโลกจริง

pattern "จับคลาสเป้าหมาย แล้วสั่งการ" ที่เราทำวันนี้ คือแก่นของสินค้า Edge AI จริงทุกตัว:

Motion (IMU) — นาฬิกาตรวจการล้ม verdict: "fall" เกินเกณฑ์ → action: โทรฉุกเฉิน ต้องยิงครั้งเดียว ไม่โทรรัวทุกเฟรม = edge-trigger + CONF_FLOOR ของเราวันนี้ เสียง (MIC) — เครื่องเฝ้าเด็ก verdict: "baby_cry" → action: แจ้งมือถือพ่อแม่ เสียงไม่ออกจากเครื่อง → ความเป็นส่วนตัว = remix "Baby Cry" ของเราวันนี้ เรดาร์ (RADAR) — สวิตช์ไร้สัมผัส verdict: "Push" → action: เปิดไฟ/ประตู ทำงานในที่มืด ไม่ใช้กล้อง = remix "Push" ของเราวันนี้ ร่วมกัน — verdict + action ทุกสินค้า = จับคลาส + ตัดสินด้วย conf + ลงมือ ต่างกันแค่ "action" ปลายทาง คือ โมดูล 6 (Apps, โมดูล 6) ของเรา

สังเกตว่าทุกสินค้าใช้ pattern เดียวกับแอปชุดบทเรียนนี้เป๊ะ — ต่างกันแค่ action ปลายทาง เราแค่กำลังเริ่มจากปลายทางเพื่อย้อนไปสร้างเองได้

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

งานทำเอง + สรุปบทเรียน

งานทำเอง (ท้ายบทเรียน):

  1. เติม s03_anatomy_edgeai.py ให้ครบทั้ง 4 ช่อง รันได้จริง (Emulator หรือบอร์ด)
  2. remix: เปลี่ยน MODEL_KEYWORD + TARGET_CLASS เป็นโมเดลอื่น (เช่น Motion → Cough) ยืนยัน action ยิงตามคลาสใหม่
  3. หาท่า/เสียงที่ทำให้คลาสเป้าหมาย "ชนะแต่ conf ต่ำกว่า CONF_FLOOR" แล้วอธิบายว่าทำไม action ถึงไม่ยิง

ใบ้ข้อ 3 — เงื่อนไข hit เช็กทั้ง label และ conf ถ้าคลาสถูกแต่มั่นใจไม่ถึงเกณฑ์ hit เป็น False action จึงไม่ยิง (นี่คือตัวกัน false positive)

วันนี้เราได้: แกะกายวิภาคแอป Edge AI เป็น 3 จังหวะ (select→result→action) · เลือกโมเดลจากชื่อ · เพิ่ม action บน verdict ด้วยเงื่อนไข label+conf · รู้จัก on_result · remix เป็นแอปของตัวเอง

ชุดบทเรียนถัดไป (บทเรียน 2.1–2.2) เราปิดกล่อง Onboarding แล้วเปิด Pillar 1 — DAQ: เริ่มเก็บข้อมูลเซนเซอร์ของเราเองลงไฟล์ CSV เพื่อเป็นวัตถุดิบฝึกโมเดลในบล็อกถัดๆ ไป เจอกันครับ

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