SDK สำหรับ TESAIoT Dev Kit
คู่มืออ้างอิง API และ Tutorial (MTB & µPython)
Loading...
Searching...
No Matches
E2 — Parallel set และการวนถามผลลัพธ์
variant ที่ใช้ได้
mtb-mpy และ mtb-only

ตัวอย่าง MicroPython ใช้ได้บน mtb-mpy เท่านั้น ส่วนการยุบรวมผลฝั่ง C และสิ่งที่สังเกตได้บนหน้าจอใช้ได้ทั้งสอง variant ข้อความตัดตอนฝั่ง C ทั้งหมดคัดมาจากซอร์สที่อยู่ใน archive (ดูหมายเหตุใน E1 — Select, confirm, start: REQUESTED เทียบกับ ACTIVE)

Note
เครดิต: โมเดล Edge AI เป็นของ Infineon ไม่ใช่ของ TESAIoT โมเดล motion, audio และ radar ที่เฟิร์มแวร์นี้ส่งมอบ (proj_cm55/modules/ai_models/model_*.c) เป็นผลลัพธ์ที่ export จาก DEEPCRAFT™ Studio และมีลิขสิทธิ์ของ Imagimob AB ซึ่งเป็นบริษัทในเครือ Infineon Technologies ส่วนโมเดล Siren, Cough และ Factory Alarm เป็น DEEPCRAFT™ Ready Model ของผู้สร้างรายเดียวกัน เผยแพร่โดย Infineon อยู่ภายใต้ Imagimob AI Model Evaluation License Agreement และไม่ได้แจกจ่ายต่อในแพ็กเกจนี้ TESAIoT ไม่ได้ฝึกและไม่ได้เป็นเจ้าของโมเดลใดเลย สิ่งที่เป็นของ TESAIoT คือ engine ที่ครอบอยู่ ซอร์สของโมเดลที่ส่งมอบมาระบุเพียง All Rights Reserved โดยไม่มีการให้สิทธิ์ใด ๆ เขียนไว้ในไฟล์ ที่นี่จึงเป็นการให้เครดิต ไม่ใช่การส่งต่อสิทธิ์ การใช้งานที่นี่เป็นไปเพื่อการวิจัยและการอบรม ไม่ใช่การใช้เชิงพาณิชย์ ผู้ที่ต้องการสิทธิ์การใช้งานต้องขอจาก Infineon และ Imagimob โดยตรง ผู้ที่ต้องการฝึกโมเดลของตนเองเริ่มได้ที่ https://www.infineon.com/design-resources/embedded-software/deepcraft-edge-ai-solutions/deepcraft-studio รายละเอียดฉบับเต็มและการอ้างอิงข้อสัญญาอยู่ที่ THIRD_PARTY_NOTICES.md §2.2, §2.4 และ §4.3

เป้าหมายของหัวข้อนี้

สั่งให้หลายโมเดลทำงานพร้อมกันในฐานะ set อ่านผลตัดสินจาก set โดยไม่ทำให้การตรวจจับหายไป และรู้จัก settle gate ที่กันผลตัดสินไว้ระหว่างที่หน้าต่างข้อมูล (window) ของเสียงกำลังเติมอยู่ ประโยคที่สำคัญที่สุดเพียงประโยคเดียว: ai_engine_snapshot() ใช้ไม่ได้ภายใน parallel set (ชุดโมเดลที่ป้อนข้อมูลขนานกัน)

ลำดับการทำงานจริงของเฟิร์มแวร์

ค่าคงที่ของ set (ai_engine.h:97-113): MIC = 255, ROOM = 254, INTRUDER = 253, ALL = 252 และ AI_PARALLEL_FIRST = ALL ส่วนชื่อเรียกเดิม 13/14/15 คลี่ไปเป็น 253/254/255 (บท E1) archive รุ่นที่เก่ากว่าการมี ALL จะปฏิเสธค่า 252 บนฮาร์ดแวร์ (ai_engine.h:109-113) การที่เครื่องยนต์จะยอมรับดัชนีของ set ใด ๆ ต้องมี EDGE_AI_HAS_MIC (ai_engine.c:1850-1859)

การเข้าสู่ set จะสร้างสมาชิกภาพ (membership) ขึ้นใหม่ทุกครั้งที่เข้า (set_resolve(), ai_engine.c:1166-1189): สมาชิกภาพที่กำหนดไว้อย่างชัดแจ้งด้วย set_define ชนะขาด มิฉะนั้นจึงใช้รายการจับคู่ชื่อที่คอมไพล์มา (set_wants(), ai_engine.c:1092-1127) ไม่มีการแคชไว้เลย โมเดลที่ init() ล้มเหลวไปครั้งหนึ่งจึงไม่ถูกตัดออกไปตลอดรอบการบูต ลำดับความสำคัญที่ค่าชัดแจ้งเป็นฝ่ายชนะอยู่ที่ ai_engine.c:1146-1149

สมาชิกภาพที่กำหนดเองedge_ai.set_define(set, [i, j, ...]) (modedgeai.c:867-921) ทำตามลำดับ: คลี่ดัชนีเดิม (:876), SET_BEGIN (:886), SET_ADD หนึ่งครั้งต่อหนึ่งสมาชิกโดยเว้นจังหวะ 5 ms (:895-902), SET_COMMIT (:904), รอตั้งตัว 40 ms (:907) แล้วอ่านกลับด้วย Q_SET (:910) — การอ่านกลับนี้เป็นสิ่งเดียวที่จับได้ว่ามี ADD ตัวใดหล่นหาย ฝั่ง CM55 นำคำสั่งสร้างทั้งสามไปใช้เป็นชุดเดียวกันทั้งชุด:

ที่มา
ยกมาจาก deepcraft_task.c:727-749 (คอมไพล์รวมอยู่ใน archive สำเร็จรูป ไม่ได้ส่งมอบมาเป็นซอร์ส)

การอ่านกลับซึ่งทำหน้าที่เป็นการตอบรับเพียงอย่างเดียวที่มี:

ที่มา
ยกมาจาก deepcraft_task.c:334-355 (คอมไพล์รวมอยู่ใน archive สำเร็จรูป ไม่ได้ส่งมอบมาเป็นซอร์ส)

การยุบรวมผลภายใน set — เลือกสมาชิกที่เป็นผลบวกซึ่งมีค่าความเชื่อมั่นสูงสุด ผ่าน ai_engine_set_members() + ai_engine_snapshot_model() + ai_result_top_positive():

ที่มา
ยกมาจาก deepcraft_task.c:433-470 (คอมไพล์รวมอยู่ใน archive สำเร็จรูป ไม่ได้ส่งมอบมาเป็นซอร์ส)

คะแนนถูกแปลงเป็นช่วง 0..100 ที่ deepcraft_task.c:478-483 ส่วน class_count == 0 หมายความว่า "ยังไม่มีผลตัดสิน" และฝั่ง CM33 จะคืนค่า None (:487)

ผลข้างเคียงเรื่องอัตราเซนเซอร์ dc_desired_sensor_rate() (deepcraft_task.c:259-269) จะไล่ดูสมาชิกใน set แล้วยกอัตราของ accelerometer ขึ้นเป็น 50 Hz หากมีสมาชิก ตัวใดตัวหนึ่ง เป็น AI_SENSOR_IMU — บรรทัด :250-254 บันทึกไว้ว่าสมาชิก Motion ของ Intruder Watch เคยถูกป้อนข้อมูลที่ 10 Hz และแถวของมันไม่เคยขยับเลย ส่วน ai_engine_model() คืนค่า NULL สำหรับดัชนีเทียมของ set ดังนั้นให้แยกกิ่งด้วย idx >= AI_PARALLEL_FIRST ก่อนเป็นอันดับแรก:

ที่มา
ยกมาจาก deepcraft_task.c:239-275 (คอมไพล์รวมอยู่ใน archive สำเร็จรูป ไม่ได้ส่งมอบมาเป็นซอร์ส)

ทีละขั้น

ขั้นที่ 1 — ดูรายชื่อ set ที่หน้าจอมีให้ (ทั้งสอง variant)

บนหน้าจอ ให้เปิดดรอปดาวน์เลือกโมเดลบนหน้า Edge AI แถวของ set จะถูกเพิ่มเข้ามาก็ต่อเมื่อ set นั้นมีสมาชิกอย่างน้อย 2 ตัวและมีชื่อ:

ที่มา
ยกมาจาก page_edge_ai.c:1033-1047 (คอมไพล์รวมอยู่ใน archive สำเร็จรูป ไม่ได้ส่งมอบมาเป็นซอร์ส)

สิ่งที่ควรสังเกต แถวของ set (Sound Watch, Room Presence, Intruder Watch, All Models) ปรากฏต่อจากโมเดลเดี่ยวที่คอมไพล์มา ส่วน set ที่มีสมาชิกซึ่งคลี่ออกมาได้น้อยกว่า 2 ตัวจะไม่ปรากฏเลย ซึ่งเป็นข้อเท็จจริงเรื่องเนื้อหาของ build ไม่ใช่ความผิดพลาด

ขั้นที่ 2 — เลือก set หนึ่งชุดแล้วดู settle gate

>>> edge_ai.select(255) # MIC set; 15 is the legacy alias
True
>>> edge_ai.result()
>>> edge_ai.result()

สิ่งที่ควรสังเกต ในช่วงไม่กี่วินาทีแรก result() คืนค่า None — เพราะ class_count == 0 — ระหว่างที่หน้าต่างข้อมูลของเสียงกำลังเติม บนหน้าจอ ป้ายผลตัดสินจะอ่านว่า filling windows lu%% โดยตัวเลขร้อยละไต่ขึ้น (page_edge_ai.c:698-699) และ badge แสดง CHIP_LOADING gate คือ ai_engine_mic_settling() / ai_engine_mic_settle_pct():

ที่มา
ยกมาจาก page_edge_ai.c:685-708 (คอมไพล์รวมอยู่ใน archive สำเร็จรูป ไม่ได้ส่งมอบมาเป็นซอร์ส)

ให้สังเกตกฎที่บันทึกไว้ที่ page_edge_ai.c:702-705: บรรทัดสถิติต้องอัปเดตต่อไป ระหว่าง ช่วงตั้งตัวด้วย มิฉะนั้นการที่ NPU ค้างอยู่ภายในหน้าต่างข้อมูลจะมองไม่เห็น ส่วน mic_settle_pct() มีความหมายเฉพาะเมื่ออยู่ภายใน set เท่านั้น กรณีอื่นมันคืนค่า 100

ขั้นที่ 3 — วนถามผลลัพธ์

>>> r = edge_ai.result()
>>> r

สิ่งที่ควรสังเกต เมื่อตั้งตัวเสร็จแล้ว จะได้ผลลัพธ์ที่ฟิลด์ต่าง ๆ มาจากสมาชิกที่เป็นผลบวกซึ่งมีค่าความเชื่อมั่นสูงสุด แต่ละแถวของสมาชิกบนหน้าจอมีตัวจับเวลาความเงียบของตัวเอง ซึ่งอนุมานจากความเก่าของ seq (page_edge_ai.c:728-739):

ที่มา
ยกมาจาก page_edge_ai.c:717-723 (คอมไพล์รวมอยู่ใน archive สำเร็จรูป ไม่ได้ส่งมอบมาเป็นซอร์ส)

ai_engine_snapshot_model() ที่คืนค่า false หมายความว่า "ไม่เคยเผยแพร่ผลเลย" ซึ่งไม่เหมือนกับ "เงียบ" และดัชนีที่ส่งเข้าไปต้องเป็นดัชนีจริงใน registry ห้ามเป็นดัชนีเทียมของ set เด็ดขาด

ขั้นที่ 4 — กำหนดสมาชิกภาพเอง

>>> edge_ai.set_define(253, [0, 2])
>>> edge_ai.select(253)
True

สิ่งที่ควรสังเกต set_define คืนค่าหลังจากที่การอ่านกลับด้วย Q_SET ยืนยันสมาชิกครบทุกตัวแล้ว ADD ที่หล่นหายจะปรากฏเป็นรายการที่สั้นลง และคำนิยามทั้งชุดจะถูกปฏิเสธหากมีดัชนีใดไม่ถูกต้อง (ได้หมดหรือไม่ได้เลย ai_engine.c:1060-1064) สมาชิกภาพรับได้ 1..8 รายการ และ set_define(252, ...) ล้มเหลวเสมอ เพราะ AI_PARALLEL_ALL ไม่มี slot (set_slot(), ai_engine.c:1040-1048)

ขั้นที่ 5 — อ่าน active() เพียงครั้งเดียวต่อหนึ่งรอบเรนเดอร์ (ฝั่ง C)

ที่มา
ยกมาจาก page_edge_ai.c:565-575 (คอมไพล์รวมอยู่ใน archive สำเร็จรูป ไม่ได้ส่งมอบมาเป็นซอร์ส)

สิ่งที่ควรสังเกต ไม่มีอะไรให้เห็น เพราะนี่คือวินัยที่กันไม่ให้ป้ายเลย์เอาต์กับจำนวนแถวไม่ตรงกัน ในตอนที่ deepcraft task สั่ง start หรือ stop เครื่องยนต์จาก task อื่นกลางรอบเรนเดอร์

กับดัก

Warning
อันตราย (hazard) จากการใช้ snapshot ภายใน set ai_engine_snapshot() เป็นแบบผู้เขียนรายหลังทับรายก่อนบน slot เดียว ภายใน set นั้น "a cough detected at 0.94 is overwritten by three 'unlabelled' … The detection is not late, it is gone" (ai_engine.c:1876-1885) ให้ใช้ ai_engine_snapshot_model() ทีละสมาชิกแล้วยุบรวมผลเอง กิ่งที่ตัดสินว่าจะใช้ตัวใดใน 2 ตัวนี้เคยเป็น == AI_PARALLEL_MIC จนถึง 2026-08-09 Room Presence และ Intruder Watch จึงตกลงไปอยู่ใน slot แบบที่กิ่งนั้นมีไว้เพื่อหลีกเลี่ยงพอดี (deepcraft_task.c:448-450) — การทดสอบที่ถูกต้องคือช่วง [AI_PARALLEL_FIRST, AI_PARALLEL_MIC]
set_define เรียกได้ใน task context เท่านั้น มันครอบการเผยแพร่ด้วย taskENTER_CRITICAL (ai_engine.c:1059-1075) ห้ามเรียกจาก callback ของ pipe
ai_engine_model_count() นำหน้าจำนวนแถว ai_engine_model(i) ยังคืนค่า NULL ได้เมื่อเจอช่องว่างของตัวคลี่ ให้ continue ห้ามยกเลิกทั้งลูป:
ที่มา
ยกมาจาก page_edge_ai.c:1019-1032 (คอมไพล์รวมอยู่ใน archive สำเร็จรูป ไม่ได้ส่งมอบมาเป็นซอร์ส)

กล่อง Variant

mtb-mpy mtb-only
เลือก set edge_ai.select(252..255) แถวในดรอปดาวน์ แล้วกด Load
กำหนดสมาชิกภาพ edge_ai.set_define() ทำได้จากโค้ด CM55 ที่เขียนเองเท่านั้น โดยเรียก ai_engine_set_define() จาก task แล้วอ่านกลับด้วย ai_engine_set_members_defined()
สิ่งที่สังเกตได้ระหว่างตั้งตัว result() เป็น None; หน้าจอแสดง filling windows lu%% หน้าจอแสดง filling windows lu%%
ผลตัดสิน dict จาก edge_ai.result() ป้ายผลตัดสิน บวกตัวจับเวลาความเงียบรายแถว