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

ลงมือทำ: ส่องสแตกจาก MicroPython

โมดูล 7 — ใต้ฝากระโปรงและการต่อเติม · สไลด์: slides.md · ภาพรวมโมดูล · หน้าหลักสูตร

เติมคำสั่งฝั่งอ่านห้าตัวของ edge_ai ใน s18_under_the_hood.py คือ links, model, active, result และ latency แล้วกด Trace ให้ log ไล่สแตกสามชั้น transport, control และ result พร้อมจับคู่แต่ละบรรทัดกับฟังก์ชันหรือฟิลด์ใน header ของ SDK และเทียบ latency ของโมเดล int8 บน NPU กับโมเดล float32 บน CPU

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

  1. เติมห้าจุดใน practice/s18_under_the_hood.py จนกด Trace แล้วได้ log ครบสี่ส่วน transport, registry, control และ result โดยค่า active() ตรงกับโมเดลที่เลือก
  2. จับคู่ log อย่างน้อยสามบรรทัดกับฟังก์ชันหรือฟิลด์ใน ai_engine.h หรือ ipc_model_link_defs.h ของ SDK
  3. บนบอร์ด เทียบ latency() ของโมเดล int8 กับโมเดล float32 และอธิบายความต่างด้วยเส้นทาง NPU กับ CPU

ผ่านบทเรียน 7.1 มาแล้ว รู้จักสามคอร์ ทะเบียนโมเดล IPC สอง plane และ ai_result_t เปิด ai_engine.h กับ ipc_model_link_defs.h ของ SDK ไว้คู่จอเหมือนบทเรียนก่อน

  • อุปกรณ์: บอร์ด TESAIoT Dev Kit ที่ลงเฟิร์มแวร์ MicroPython ของ BENTO แล้ว หรือ BENTO Emulator ใน BENTO IDE — บน Emulator ซ้อมเรียก API ฝั่งอ่านได้ (ได้ (‘ipc’,) และทะเบียนห้าโมเดล) แต่ latency กับผลโมเดลเป็นค่าจำลอง การเทียบ int8 กับ float32 ต้องทำบนบอร์ด
  • เรียนมาก่อน: บทเรียน 7.1 — สแตก Edge AI: tri-core, ai_engine, IPC model link และ TFLite-Micro

ไฟล์นี้ไม่ได้อนุมานอะไรใหม่ มันเป็น เครื่องมือส่องสแตก ที่เรียก API ฝั่งอ่านแล้ว log ทีละชั้น ห้าจุดที่เติมคือ (1) links = edge_ai.links() คืน ('ipc',) บอกว่าคุยกับ CM55 ผ่าน IPC model link (2) desc = edge_ai.model(mi) ดึง descriptor ตัวเดียว (name, sensor, labels) ผ่าน Q_MODEL (3) cur = edge_ai.active() หลัง select(sel) ที่ให้ไว้แล้ว ถ้าปกติ cur ต้องเท่ากับ sel นี่คือการยืนยันด้วยการสังเกตที่เห็นกับตา (4) r = edge_ai.result() ผ่าน Q_RESULT และ (5) ms = edge_ai.latency() คือเวลาอนุมานล่าสุดจาก inference_us ทั้งห้าเป็นการอ่านแบบ pull ทั้งหมด

เมื่อ Trace ทำงาน log จะไล่จาก transport → registry → control → result ให้จับคู่แต่ละบรรทัดกับ header ของ SDK เช่น active() กับ ai_engine_active() และ MODEL_LINK_Q_ACTIVE, select(n) กับ MODEL_LINK_CMD_SELECT_BASE + n, key ของ result() กับฟิลด์ของ ai_result_t ซอร์ส C ฝั่งเฟิร์มแวร์ที่ log เอ่ยถึง (ai_engine.c, deepcraft_task.c) ยังไม่เปิดเผย header จึงเป็นหลักฐานสาธารณะที่ตรวจได้ บนบอร์ด ลอง Trace โมเดล int8 (เช่น Motion) แล้วสลับไปโมเดล float32 (ผู้เขียนระบุว่า Push และ Siren) เทียบ latency() จะเห็นว่าเส้นทาง NPU กับ CPU ต่างกันจริง อย่าสลับโมเดลรัว ๆ เว้นเป็นวินาทีเพื่อไม่ให้ control plane ค้าง

s18_under_the_hood_full.py เพิ่มแผนที่สแตกบนจอ ตารางจับคู่ key ของ result() กับฟิลด์ใน ai_result_t callback on_result (เรียกเมื่อคลาสเปลี่ยนและทวนราววินาทีละครั้ง) และการเทียบ latency ระหว่าง int8 กับ float32

ไฟล์ ไฟล์นี้สอน
examples/s18_under_the_hood_full.py เครื่องส่องสแตก Edge AI ฉบับเต็ม (สำหรับ Researcher)

คอมเมนต์ # เติม อยู่ที่บรรทัด 38 (links), 75 (model), 109 (active), 134 (result) และ 142 (latency) ถ้า log ชั้น transport ว่าง จุดที่ 38 ยังว่าง ถ้า active() ขึ้น −1 ตลอดทั้งที่เลือกโมเดลแล้ว ตรวจจุดที่ 109

ไฟล์ฝึก เรื่อง
practice/s18_under_the_hood.py แกะสแตก Edge AI จากปลาย MicroPython ลงไปถึง NPU (ฉบับฝึกเติมโค้ด)

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

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

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

  1. บน Emulator และบนบอร์ด edge_ai.links() ควรคืนอะไร (เลือกหนึ่งข้อ · เป้าหมายข้อ 1)

    • ก) (‘ipc’,)
    • ข) (‘wifi’,)
    • ค) None
    • ง) จำนวนโมเดล
    เฉลย

    ก — MicroPython คุยกับ ai_engine ผ่าน IPC model link ทางเดียว Emulator คืนค่าเดียวกันเพื่อให้โค้ดย้ายไปบอร์ดได้ แม้ในเบราว์เซอร์จะไม่มี IPC จริง

  2. หลัง select(sel) สำเร็จ ค่า cur = edge_ai.active() ควรเป็นอะไร (เลือกหนึ่งข้อ · เป้าหมายข้อ 1)

    • ก) −1
    • ข) sel เพราะ select รอจนเห็น Q_ACTIVE เท่ากับค่าที่ขอแล้ว
    • ค) sel + 1
    • ง) จำนวนโมเดล
    เฉลย

    ข — select คืนก็ต่อเมื่อยืนยันด้วยการสังเกตแล้ว ถ้าไม่ยืนยันมันโยน OSError แทน

  3. log “select(n)” ควรจับคู่กับสิ่งใดใน ipc_model_link_defs.h (เลือกหนึ่งข้อ · เป้าหมายข้อ 2)

    • ก) MODEL_LINK_Q_RESULT
    • ข) MODEL_LINK_CMD_SELECT_BASE (0x90) บวก n บน control plane
    • ค) MODEL_LINK_STREAM_MIC_PCM
    • ง) BENTO_MODEL_LINK_VERSION
    เฉลย

    ข — SELECT ใส่เลขโมเดลไว้ในไบต์คำสั่งบน OP_CTRL แล้วยืนยันด้วย Q_ACTIVE บน query plane

  4. บนบอร์ด latency ของ Motion (int8) ต่ำกว่า Push (float32) มาก เพราะอะไร (เลือกหนึ่งข้อ · เป้าหมายข้อ 3)

    • ก) Motion มีคลาสน้อยกว่า
    • ข) โมเดล int8 ที่ผ่าน Vela รันบน Ethos-U55 ส่วน float32 รันบน CPU ด้วย float kernel
    • ค) เรดาร์ช้ากว่า IMU เสมอ
    • ง) เพราะ IPC ของเรดาร์ช้ากว่า
    เฉลย

    ข — latency วัดเฉพาะช่วงอนุมาน ความต่างหลักจึงมาจากว่ากราฟรันบน NPU หรือบน CPU

MVP ของชุดบทเรียน 7.1–7.2: อธิบายสแตกได้ แล้วชี้ฟังก์ชันหรือฟิลด์ต้นทางของสามชั้น (transport, control, result) ได้อย่างน้อยชั้นละหนึ่งจุด

  • เติมไฟล์ฝึกครบห้าจุด กด Trace บน Emulator หรือบอร์ดจน log ขึ้นครบ
  • จับคู่ log อย่างน้อยสามบรรทัดกับฟังก์ชันหรือฟิลด์ใน ai_engine.h หรือ ipc_model_link_defs.h แล้วจดลงบันทึกการเรียน
  • บนบอร์ด Trace โมเดล int8 หนึ่งตัวกับ float32 หนึ่งตัว จด latency() ทั้งสองและอธิบายความต่าง
  • อธิบายด้วยคำพูดของคุณว่าทำไม active() อาจยังไม่ใช่โมเดลที่เพิ่งขอถ้าอ่านจากที่อื่นระหว่างสลับ

ชุดบทเรียนถัดไป (บทเรียน 7.3–7.4) เราจะใช้แผนที่นี้เพิ่มโมเดลของเราเองให้โผล่ใน edge_ai.models()

บทเรียนถัดไป: บทเรียน 7.3 — เพิ่มโมเดลของเราเอง: สามการแก้ สัญญาสี่ฟังก์ชัน และ Vela

  • log บรรทัดไหนที่คุณยังชี้ต้นทางไม่ได้ และข้อมูลใดที่ขาดไปจากส่วนที่เปิดเผย
  • ถ้าบอร์ดค้างหลังสลับโมเดลรัว ๆ คุณจะเล่าอาการนี้ให้ทีมเฟิร์มแวร์ฟังอย่างไรให้เขาหาต้นเหตุได้

คำถามทบทวน

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

  1. บน Emulator และบนบอร์ด edge_ai.links() ควรคืนอะไร (เป้าหมายข้อ 1)

    1. ('ipc',)
    2. ('wifi',)
    3. None
    4. จำนวนโมเดล
    ดูเฉลย

    คำตอบ: A. ('ipc',)

    MicroPython คุยกับ ai_engine ผ่าน IPC model link ทางเดียว Emulator คืนค่าเดียวกันเพื่อให้โค้ดย้ายไปบอร์ดได้ แม้ในเบราว์เซอร์จะไม่มี IPC จริง

  2. หลัง select(sel) สำเร็จ ค่า cur = edge_ai.active() ควรเป็นอะไร (เป้าหมายข้อ 1)

    1. −1
    2. sel เพราะ select รอจนเห็น Q_ACTIVE เท่ากับค่าที่ขอแล้ว
    3. sel + 1
    4. จำนวนโมเดล
    ดูเฉลย

    คำตอบ: B. sel เพราะ select รอจนเห็น Q_ACTIVE เท่ากับค่าที่ขอแล้ว

    select คืนก็ต่อเมื่อยืนยันด้วยการสังเกตแล้ว ถ้าไม่ยืนยันมันโยน OSError แทน

  3. log "select(n)" ควรจับคู่กับสิ่งใดใน ipc_model_link_defs.h (เป้าหมายข้อ 2)

    1. MODEL_LINK_Q_RESULT
    2. MODEL_LINK_CMD_SELECT_BASE (0x90) บวก n บน control plane
    3. MODEL_LINK_STREAM_MIC_PCM
    4. BENTO_MODEL_LINK_VERSION
    ดูเฉลย

    คำตอบ: B. MODEL_LINK_CMD_SELECT_BASE (0x90) บวก n บน control plane

    SELECT ใส่เลขโมเดลไว้ในไบต์คำสั่งบน OP_CTRL แล้วยืนยันด้วย Q_ACTIVE บน query plane

  4. บนบอร์ด latency ของ Motion (int8) ต่ำกว่า Push (float32) มาก เพราะอะไร (เป้าหมายข้อ 3)

    1. Motion มีคลาสน้อยกว่า
    2. โมเดล int8 ที่ผ่าน Vela รันบน Ethos-U55 ส่วน float32 รันบน CPU ด้วย float kernel
    3. เรดาร์ช้ากว่า IMU เสมอ
    4. เพราะ IPC ของเรดาร์ช้ากว่า
    ดูเฉลย

    คำตอบ: B. โมเดล int8 ที่ผ่าน Vela รันบน Ethos-U55 ส่วน float32 รันบน CPU ด้วย float kernel

    latency วัดเฉพาะช่วงอนุมาน ความต่างหลักจึงมาจากว่ากราฟรันบน NPU หรือบน CPU

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

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

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

ข้อความอ้างอิงภาษาอังกฤษ: "Hands-on: tracing the stack from MicroPython" 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/m07-under-the-hood/l02-trace-the-stack-lab/

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

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

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