SDK สำหรับ TESAIoT Dev Kit
คู่มืออ้างอิง API และ Tutorial (ModusToolbox)
Loading...
Searching...
No Matches
E4 — การอ่านข้อมูลวินิจฉัยของ Edge AI
variant ที่ใช้ได้
mtb-mpy และ mtb-only

แถวข้อมูลวินิจฉัย (diagnostics) บนหน้าจอมีเหมือนกันทั้งสอง variant ส่วน edge_ai.diag() และ ui._diag() เป็นการเรียกบน REPL ของ MicroPython จึงมีเฉพาะบน mtb-mpy เท่านั้น สิ่งที่เทียบเท่ากันบน mtb-only คือแถวบนหน้าจอ บวกกับการเรียก ipc_ui_platform_diag() จากฝั่ง C

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

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

อ่านพื้นผิวข้อมูลวินิจฉัยทั้งสามแหล่ง — เวิร์ดสัญญาณชีพ (heartbeat) ที่อัดรวมค่าไว้ แถวสถิติบนหน้าจอ และดิกชันนารีจาก edge_ai.diag() — แล้วใช้เงื่อนไขผ่านข้อเดียวที่สำคัญจริง: npu_cycles ต้องเดินหน้าไปเรื่อย ๆ ขณะที่ stale_drops ต้องนิ่งอยู่กับที่ และจะทราบด้วยว่าเหตุใด printf ในโค้ด CM55 จึงไม่พิมพ์อะไรออกมาโดยค่าตั้งต้น และจะเปลี่ยนได้อย่างไร

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

ระนาบเหตุการณ์ — สัญญาณชีพที่อัดรวมค่า ภายใน set ไม่มีการส่งเหตุการณ์เจตนา (intent) รายโมเดล เพราะไบต์เจตนาจะรายงานได้แค่ set ไม่ใช่สมาชิกที่ตรวจจับได้ (ให้อ่านสมาชิกด้วย ai_engine_snapshot_model()) สัญญาณชีพที่ส่งทุก 1 s ขณะที่โมเดลทำงานจึงพาตัวนับที่อัดรวมค่าไว้ในค่า 32 บิตค่าเดียว:

บิต ค่า ความหมาย
[31:28] ai_engine_init_calls() & 0xF 0 = task ไม่เคยไปถึง init() ของโมเดล
[27:20] ai_engine_last_init_rc() & 0xFF 0 = ปกติ; ค่าอื่นคือรหัสความล้มเหลวของโมเดล
[19:16] ai_engine_feeds() & 0xF nibble ล่างของตัวนับข้อมูลที่ป้อนเข้าโมเดล
[15:12] ai_engine_inits() & 0xF จำนวนการโหลดแบบเย็นที่สำเร็จ
[11:0] ai_engine_dq_ok() & 0xFFF จำนวนผลตัดสินที่เผยแพร่แล้ว

การอ่าน nibble ของ feeds ข้ามสัญญาณชีพที่ต่อเนื่องกัน: เป็น 0 ทุกครั้ง = ไม่มีตัวอย่าง IMU ใหม่มาถึงโมเดลเลย (การส่งข้อมูลเซนเซอร์จาก CM33 ไม่เดินหน้า); ไม่เป็น 0 แต่ค้าง = การส่งข้อมูลหยุดไปกลางทาง; เปลี่ยนไปเรื่อย ๆ และ dq_ok ไต่ขึ้น = การป้อนข้อมูลและการอนุมานปกติ หาก result() ยังเป็น None แสดงว่าจุดที่ขาดอยู่ที่เส้นทางอ่านผลลัพธ์; เปลี่ยนไปเรื่อย ๆ แต่ dq_ok == 0 = ตัวอย่างมาถึงแต่ไม่มีผลตัดสิน ซึ่งมีได้สองสาเหตุ คือ window ของโมเดลไม่เคยเต็ม (โมเดล อัตรา หรือรูปทรงข้อมูลไม่ตรง) หรือการอนุมานทุกครั้งเกินขอบเขตเวลารอของ Ethos-U จนผลถูกกันไว้ ให้อ่าน stale_drops เพื่อแยกสองกรณีนี้

ระนาบดึงข้อมูล — MODEL_LINK_Q_DIAG ตอบได้แม้ในขณะที่การอนุมานค้างอยู่ และถูกนำออกมาเป็น edge_ai.diag() คีย์ของมันตรงกับ getter ในคู่มือฟิลด์ด้านล่าง

ระนาบหน้าจอ — แถวสถิติ หน้า Edge AI เรนเดอร์ push/s feed/s flush/s … init N/M rc R stale S อัตราการป้อนข้อมูลเป็นผลต่างที่วัดในช่วงอย่างน้อยหนึ่งวินาที:

static uint32_t last_feeds, last_ms;
if ((now_ms - last_ms) >= 1000u) {
const uint32_t feeds = ai_engine_feeds();
const uint32_t feed_hz = ((feeds - last_feeds) * 1000u) / (now_ms - last_ms);
last_feeds = feeds;
last_ms = now_ms;
}
uint32_t ai_engine_feeds(void)
จำนวนตัวอย่างที่โมเดลรับเข้าไป; ให้อ่านเป็นผลต่างในช่วงอย่างน้อย 1 s

คู่มือฟิลด์

ฟิลด์ Getter อ่านอย่างไร
npu_cycles ai_engine_npu_cycles() เป็น u64 ที่แยกส่งเป็นครึ่งบน/ครึ่งล่างบนสาย การที่ค่าเดินหน้า = NPU กำลังถูกใช้งาน แต่ ไม่ใช่ หลักฐานว่าการอนุมานทำจนจบ
stale_drops ai_engine_stale_drops() ต้องนิ่งอยู่กับที่ขณะที่ npu_cycles เดินหน้า นี่คือเงื่อนไขผ่าน ส่วน ml_state เพียงตัวเดียวเชื่อถือไม่ได้
feeds ai_engine_feeds() มีเพียงฟิลด์ "feed" ในข้อมูลวินิจฉัยเท่านั้นที่รายงานปริมาณข้อมูลที่โมเดลรับเข้าไป ให้อนุมานเป็น Hz จากช่วง ≥1 s
dq_ok / dq_calls ai_engine_dq_ok() / ai_engine_dq_calls() dq_ok ค้างอยู่ขณะที่ dq_calls ไต่ขึ้น = NPU ค้าง ส่วน dq_calls ไม่มีผู้เรียกที่มีอยู่จริงในของที่ส่งมอบ — มีการจับคู่ในตัวอย่างที่เขียนขึ้นเองด้านล่าง
init_calls / init_returns ai_engine_init_calls() / ai_engine_init_returns() ให้อ่านคู่กัน ค่า "2/1" = เข้าสู่ init แล้วไม่เคยออกมา
last_init_rc ai_engine_last_init_rc() 0x7FFFFFFF = ไม่เคยถูกเรียก; 0 = ปกติ
inits ai_engine_inits() เป็น nibble ที่อัดรวมอยู่ในเวิร์ดของสัญญาณชีพ
stack_words / stack_free_words ai_engine_stack_words() / ai_engine_stack_free_words() 0 เวิร์ด = เครื่องยนต์ไม่เคยเริ่มทำงาน ส่วน stack_free_words เป็นการสแกนแบบ O(stack) ให้เรียกราว 1 Hz และห้ามเรียกทุกเฟรม

การจับคู่ dq_calls ในตัวอย่างที่เขียนขึ้นเอง:

static void bento_ex_ai_engine_dq_calls(void)
{
static uint32_t last_calls, last_ok;
if (ai_engine_stack_words() == 0u) {
return; /* engine never started — no data */
}
/* Call this block at >= 1 s intervals (e.g. the same 1 Hz slot as
* the watchdog); the deltas are the signal, not the totals. */
uint32_t calls = ai_engine_dq_calls();
uint32_t ok = ai_engine_dq_ok();
uint32_t d_calls = calls - last_calls;
uint32_t d_ok = ok - last_ok;
last_calls = calls;
last_ok = ok;
if (d_calls > 0u && d_ok == 0u) {
/* NPU stall signature: dequeues attempted, none succeeded over
* the whole interval. Surface it — do not silently keep polling.
* (ml_state alone is untrustworthy for this; the counters are
* the evidence.) */
}
}

ทีละขั้น

ขั้นที่ 1 — อ่านแถวบนหน้าจอ (ทั้งสอง variant)

เลือกโมเดลใดก็ได้บนหน้า Edge AI แล้วรอจนขึ้น Running

สิ่งที่ควรสังเกต แถวสถิติอัปเดตประมาณวินาทีละครั้ง ค่า feed/s ไม่เป็นศูนย์ คู่ init N/M มี N == M ค่า rc 0 และ stale คงค่าเดิมไว้ขณะที่โมเดลกำลังทำงาน

ขั้นที่ 2 — ใช้เงื่อนไขผ่าน (mtb-mpy)

>>> a = edge_ai.diag(); import time; time.sleep(2); b = edge_ai.diag()
>>> b['npu_cycles'] > a['npu_cycles'], b['stale_drops'] == a['stale_drops']
(True, True)

สิ่งที่ควรสังเกต ได้ (True, True) ส่วนผลลัพธ์แบบอื่นใดคือการค้าง หรือการป้อนข้อมูลที่ขาดแคลน ให้ดูคู่มือฟิลด์ประกอบ

ขั้นที่ 3 — อ่านตัวเลขของ GFX task (แยกตาม variant)

mtb-mpy:

>>> ui._diag()

สิ่งที่ควรสังเกต เวิร์ด 10 ตัวที่ ipc_ui_platform_diag() คืนมา: สถานะ IRQ ของ DC, ตัวนับการ flush (flush_start_count ที่เพิ่มขึ้น = เฟรมกำลังไหลอยู่), stack HWM ของ GFX และร้อยละของเวลาว่าง ส่วน ui._diag() อยู่ที่ modui.c:1470

mtb-only: ไม่มี REPL ให้เรียก ipc_ui_platform_diag(out, max_words) จากโค้ด CM55 ที่เขียนเอง โดยต้องมี max_words >= 10 และฟังก์ชันคืนค่ามา 10 เวิร์ด ตัวเข้าถึงตัวนั้น — ไม่ใช่ struct g_tesaiot_display_diag — คือเส้นทางที่รองรับ

ขั้นที่ 4 — ยืนยันว่าไม่มีอะไรบน UART ที่เป็นของ Edge AI

สิ่งที่ควรสังเกต บน mtb-mpy คอนโซลแสดง [MPY] ตอนบูต และไม่มีอะไรต่อการอนุมานแต่ละครั้ง ส่วนบน mtb-only แสดง [HB] t=lus tasks=u ทุก 10 s และไม่มีอย่างอื่น ห้ามรอบรรทัดของ Edge AI เพราะไม่มีอยู่จริง CM55 ไม่ได้เป็นเจ้าของ UART (proj_cm55/main.c:9-10)

กับดัก

Warning
printf บน CM55 เป็น no-op โดยค่าตั้งต้น CM55 ไม่มีคอนโซล UART เป็นของ CM33_NS และ newlib stdio บน CM55 จะไปจับ mutex ที่ไม่เคยถูกเตรียมไว้แล้วหยุดคอร์ proj_cm55/bento_cm55_stdio.c จึงนิยาม printf และ puts เป็น no-op แบบ weak ซึ่งเป็นซอร์สเปิดที่อ่านและแก้ได้ ส่วน libbento_edge_ai.a ไม่ได้นิยามทั้งสองตัว (ไม่มีอยู่ใน lib/edge_ai/api.txt และ overridable.txt ว่าง) ทางเลือกของผู้อ่าน: นิยาม printf ของตนเอง ซึ่งเป็น strong จึงชนะในขั้นลิงก์ หรือส่งข้อมูลวินิจฉัยของ CM55 ผ่าน IPC ไปยังคอนโซลของ CM33_NS รายละเอียดอยู่ที่ส่วนหัวของไฟล์นั้น
ml_state ไม่ใช่สัญญาณบอกสุขภาพ ให้ใช้คู่ cycles/stale แทน
ห้ามเรียก stack_free_words() ทุกเฟรม

กล่อง Variant

mtb-mpy mtb-only
ข้อมูลวินิจฉัยของเครื่องยนต์ edge_ai.diag() บวกแถวบนหน้าจอ แถวบนหน้าจอ และ getter ฝั่ง C จากโค้ด CM55 ที่เขียนเอง
ข้อมูลวินิจฉัยของ GFX ui._diag() เรียก ipc_ui_platform_diag() จากฝั่ง C
คอนโซล มีเพียง [MPY] มีเพียง [HB]