SDK สำหรับ TESAIoT Dev Kit
คู่มืออ้างอิง API และ Tutorial (MTB & µPython)
Loading...
Searching...
No Matches
ai_engine.h
Go to the documentation of this file.
1/*******************************************************************************
2 * File Name : ai_engine.h
3 *
4 * Description : BENTO Edge AI engine — runs ONE DEEPCRAFT model at a time
5 * on CM55 (TFLite-Micro + Ethos-U55) and publishes a result
6 * snapshot for the UI and the MicroPython model link.
7 *
8 * WHY ONE AT A TIME (design ruling, three independent walls):
9 * 1. one NPU — Ethos-U55 inferences serialize anyway;
10 * 2. one sensor pipeline per model family (IMU / radar /
11 * mic) with different sample rates and windows;
12 * 3. CM55 CPU budget beside LVGL (GFX task, priority 6).
13 * Models are all COMPILED IN; activating one is a registry
14 * switch, not a reload — no flash or weight copy.
15 *
16 * Every inference is timed with the DWT cycle counter and
17 * published in the snapshot, so a model that outgrows its
18 * cadence is visible on-screen rather than silent.
19 *
20 * Target : PSoC Edge E84, CM55
21 * //! [doc-drift-fix] — docs/template_local_deltas.list
22 *******************************************************************************/
23
24#ifndef AI_ENGINE_H
25#define AI_ENGINE_H
26
27#include <stdint.h>
28#include <stdbool.h>
29#include <stddef.h> /* NULL — ai_result_top_positive() below is inline here */
30
31#ifdef __cplusplus
32extern "C" {
33#endif
34
35#define AI_MAX_CLASSES (8u)
36#define AI_MAX_LABEL_LEN (16u)
37
44
46typedef struct {
47 const char *name;
48 const char *description;
50 uint8_t class_count;
51 /* Registry contract: index 0 is the negative class -- the one that means
52 * "nothing is happening". It is "unlabelled" in the audio and radar models,
53 * "idle" in motion, "normal" in fall. Everything from 1 up is a positive
54 * detection.
55 *
56 * The watch rows depend on this: a row's confidence is the maximum over
57 * classes 1..class_count-1 (see row_score_pct in page_edge_ai.c), so a
58 * model whose classes were all positive would render as if the loudest one
59 * were always firing. A new model that cannot honour this needs that code
60 * changed, not a quiet exception here. */
62 uint32_t flash_bytes;
63 uint16_t period_ms;
64
65 /* DEEPCRAFT-generated entry points (symbol-prefixed per model so several
66 * models can be linked into one image — the generated sources all declare
67 * IMAI_* otherwise). */
68 int (*init)(void);
69 int (*enqueue)(const float *in);
70 int (*dequeue)(float *out);
71 void (*finalize)(void);
73
75typedef struct {
76 uint8_t model_index;
77 uint8_t class_count;
78 uint8_t top_class;
79 uint8_t running;
81 uint32_t inference_us;
83 uint32_t inferences;
84 uint32_t seq;
86
99#define AI_PARALLEL_MIC (255)
100
105#define AI_PARALLEL_ROOM (254)
106#define AI_PARALLEL_INTRUDER (253)
107
109#define AI_PARALLEL_ALL (252)
110
115#define AI_PARALLEL_FIRST AI_PARALLEL_ALL
116
117/* These numbers are also spelled in the wire header, because MicroPython has to
118 * resolve a legacy 13/14/15 to the same value before it can confirm a select.
119 * When the two drifted, every Sound Watch select reported "not confirmed" on a
120 * board that had in fact switched. Checked here rather than trusted, and only
121 * when the wire header is in the translation unit, so this header still
122 * compiles on its own. */
123#if defined(MODEL_LINK_SET_MIC)
124_Static_assert(AI_PARALLEL_ALL == MODEL_LINK_SET_ALL, "set index drift");
125_Static_assert(AI_PARALLEL_INTRUDER == MODEL_LINK_SET_INTRUDER, "set index drift");
126_Static_assert(AI_PARALLEL_ROOM == MODEL_LINK_SET_ROOM, "set index drift");
127_Static_assert(AI_PARALLEL_MIC == MODEL_LINK_SET_MIC, "set index drift");
128#endif
129
131uint32_t ai_engine_set_models(uint32_t set_index, uint8_t *idx, uint32_t max);
132
134const char *ai_engine_set_name(uint32_t set_index);
135
138int ai_engine_set_define(uint32_t set_index, const uint8_t *members, uint32_t n);
139
141uint32_t ai_engine_set_members_defined(uint32_t set_index, uint8_t *out, uint32_t max);
142
147void ai_engine_unload(uint32_t idx);
150
152
154const ai_model_desc_t *ai_engine_model(uint32_t index);
155
191
193uint32_t ai_engine_dyn_count(void);
195
198bool ai_engine_init(void);
199
202bool ai_engine_start(uint32_t index);
203
207
210uint32_t ai_engine_feeds(void);
211uint32_t ai_engine_dq_ok(void);
212uint32_t ai_engine_dq_calls(void);
213
217void ai_engine_set_sensor_rate(uint32_t interval_ms);
218
221uint32_t ai_engine_inits(void);
222
227uint32_t ai_engine_init_calls(void);
228
232
236uint64_t ai_engine_npu_cycles(void);
237
252
257
259void ai_engine_stop(void);
260
263
268
271
276bool ai_engine_snapshot_model(uint32_t index, ai_result_t *out);
277
282uint32_t ai_engine_set_members(uint8_t *idx, uint32_t max);
283
291static inline int ai_result_top_positive(const ai_result_t *r, uint8_t *which)
292{
293 if (which != NULL) { *which = 1u; }
294 if ((r == NULL) || (r->class_count < 2u)) { return 0; }
295 float best = r->scores[1];
296 uint8_t arg = 1u;
297 /* Bounded by class_count, not the array size: publish() writes only that
298 * many floats and the slots above were never set. */
299 for (uint8_t c = 2u; (c < r->class_count) && (c < AI_MAX_CLASSES); c++) {
300 if (r->scores[c] > best) { best = r->scores[c]; arg = c; }
301 }
302 if (which != NULL) { *which = arg; }
303 int pct = (int)(best * 100.0f + 0.5f);
304 if (pct < 0) { pct = 0; }
305 if (pct > 100) { pct = 100; }
306 return pct;
307}
308
312
315
316#ifdef __cplusplus
317}
318#endif
319
320#endif /* AI_ENGINE_H */
#define AI_MAX_CLASSES
Definition ai_engine.h:35
uint32_t ai_engine_dyn_capacity(void)
#define AI_PARALLEL_ROOM
Definition ai_engine.h:105
#define AI_PARALLEL_INTRUDER
Definition ai_engine.h:106
#define AI_PARALLEL_MIC
Definition ai_engine.h:99
int ai_engine_register(const ai_model_desc_t *desc)
#define AI_PARALLEL_ALL
Definition ai_engine.h:109
ai_sensor_t
Definition ai_engine.h:39
@ AI_SENSOR_IMU
Definition ai_engine.h:40
@ AI_SENSOR_RADAR
Definition ai_engine.h:41
@ AI_SENSOR_MIC
Definition ai_engine.h:42
uint32_t ai_engine_dyn_count(void)
uint32_t ai_engine_init_returns(void)
จำนวนครั้งที่ init() ของโมเดล RETURNED ออกมา; ให้อ่านเป็นคู่เสมอ
int32_t ai_engine_last_init_rc(void)
รหัสที่คืนจาก init() ของโมเดลครั้งล่าสุด: 0x7FFFFFFF = ไม่เคยถูกเรียก; 0 = ปกติ
uint32_t ai_engine_feeds(void)
จำนวนตัวอย่างที่โมเดลรับเข้าไป; ให้อ่านเป็นผลต่างในช่วงอย่างน้อย 1 s
uint32_t ai_engine_inits(void)
จำนวนการเริ่มต้นโมเดลที่สำเร็จ; เป็น nibble ที่อัดไว้ในเวิร์ดของสัญญาณชีพ
uint32_t ai_engine_dq_ok(void)
จำนวนการดึงคิวที่สำเร็จ; ค้างนิ่งขณะที่ dq_calls ไต่ขึ้น = NPU หยุดนิ่ง
uint32_t ai_engine_init_calls(void)
จำนวนครั้งที่เข้าไปเรียก init() ของโมเดล; ให้อ่านคู่กับ ai_engine_init_returns()
uint64_t ai_engine_npu_cycles(void)
จำนวน cycle ของ NPU ที่สะสมมาถึงตอนนี้; ไม่ใช่หลักฐานว่ามีการอนุมานใดเสร็จสิ้น
uint32_t ai_engine_dq_calls(void)
จำนวนครั้งที่พยายามดึงคิว; ให้จับคู่กับ ai_engine_dq_ok() เพื่อตรวจการหยุดนิ่ง
uint32_t ai_engine_stale_drops(void)
ผลตัดสินที่ถูกทิ้งเพราะเลยขอบเวลารอของ Ethos-U; ต้องนิ่งอยู่กับที่ขณะที่ npu_cycles เดินหน้า
int ai_engine_active(void)
โมเดลที่ LOADED แล้ว (s_current) หรือ -1; ตามหลัง requested อยู่หนึ่งช่วง cold-init
uint32_t ai_engine_stack_free_words(void)
ค่าต่ำสุดตลอดกาลของจำนวนเวิร์ดใน stack ที่ยังไม่ถูกใช้; เป็นการสแกนแบบ O(stack) — อ่านได้ไม่เกินประมา...
int ai_engine_requested(void)
โมเดลที่ REQUESTED ไว้ (s_active) หรือ -1; เป็นตัวกันที่ถูกต้องสำหรับ fallback
uint32_t ai_engine_stack_words(void)
ขนาด stack ที่ inference task ได้รับ หน่วยเป็นเวิร์ด; 0 = engine ไม่เคยเริ่มทำงาน — เป็น gate สากล
void ai_engine_stop(void)
หยุดโมเดลที่ active อยู่ (idempotent); ต้องจับคู่กับการลดอัตราเซนเซอร์ลง
bool ai_engine_start(uint32_t index)
เปิดใช้งานโมเดล (หรือ set) ด้วยดัชนี แล้วเริ่มการอนุมาน; ต้องตรวจค่าที่คืนมา
bool ai_engine_init(void)
สร้าง inference task (idempotent); ต้องเรียกก่อน ai_engine_start()
bool ai_engine_snapshot(ai_result_t *out)
คัดลอกผลลัพธ์ล่าสุดที่เผยแพร่ไว้; เป็นแบบ last-writer-wins — ใช้ไม่ได้ภายใน set
uint32_t ai_engine_mic_settle_pct(void)
ความคืบหน้าของการ settle 0..100; มีความหมายเฉพาะภายใน set เท่านั้น (นอกนั้นได้ 100)
bool ai_engine_mic_settling(void)
เป็น true ตราบใดที่ parallel set ยังเติมหน้าต่างข้อมูลไม่เต็ม; ผลตัดสินจะถูกระงับไว้
uint32_t ai_engine_model_count(void)
มีโมเดลลงทะเบียนไว้กี่ตัว; ค่านับนำหน้าแถวจริง
const ai_model_desc_t * ai_engine_model(uint32_t index)
รายการใน registry คืน NULL หากอยู่นอกช่วง — และคืน NULL สำหรับดัชนีเทียมของ set
bool ai_engine_snapshot_model(uint32_t index, ai_result_t *out)
ผลตัดสินล่าสุดของโมเดลหนึ่งตัว — เป็นตัวอ่านที่ต้องใช้ภายใน parallel set
void ai_engine_set_sensor_rate(uint32_t interval_ms)
ตั้งช่วงเวลาการป้อนข้อมูลของเซนเซอร์วัดความเร่ง; เป็นผู้ส่งรายเดียวที่ถูกต้องบน s_rate_msg
void ai_engine_resume_sensor(void)
resume จังหวะค่าเริ่มต้นแบบยืนเดี่ยว; ห้ามเรียกติดกันกับการตั้งอัตรา
uint32_t ai_engine_set_members(uint8_t *idx, uint32_t max)
ดัชนีใน registry ของสมาชิกใน set ที่ ACTIVE อยู่; ต้องตรวจเซนเซอร์ของสมาชิกแต่ละราย
uint32_t ai_engine_set_members_defined(uint32_t set_index, uint8_t *out, uint32_t max)
สมาชิกภาพแบบชัดแจ้งหากมีการนิยามไว้ มิฉะนั้นได้ 0 — เป็น ack เดียวของ set_define
uint32_t ai_engine_set_models(uint32_t set_index, uint8_t *idx, uint32_t max)
สมาชิกของ set ใด ๆ ตามลำดับใน registry; สมาชิกภาพที่กำหนดชัดแจ้งชนะขาด
int ai_engine_set_define(uint32_t set_index, const uint8_t *members, uint32_t n)
กำหนดสมาชิกภาพแบบชัดแจ้งให้ set ณ เวลาทำงาน; ต้องอยู่ใน task context และเป็นแบบทั้งหมดหรือไม่เอาเลย
const char * ai_engine_set_name(uint32_t set_index)
ชื่อที่มนุษย์อ่านได้ของ set; คืน NULL สำหรับดัชนีที่ไม่ใช่ set
void ai_engine_unload(uint32_t idx)
คำขอ unload แบบอะซิงโครนัส — จะถูกปฏิเสธ เว้นแต่ engine ว่างอยู่
uint32_t ai_engine_unload_done(void)
ค่านับสะสมของ unload ที่ทำสำเร็จ; ให้อ่านเป็นคู่กับ refused
uint32_t ai_engine_unload_refused(void)
ค่านับสะสมของ unload ที่ถูกปฏิเสธ; ให้อ่านคู่กับ ai_engine_unload_done()
Definition ai_engine.h:46
uint8_t class_count
Definition ai_engine.h:50
const char * name
Definition ai_engine.h:47
const char * class_labels[AI_MAX_CLASSES]
Definition ai_engine.h:61
uint16_t period_ms
Definition ai_engine.h:63
const char * description
Definition ai_engine.h:48
int(*) dequeue(float *out)
Definition ai_engine.h:70
ai_sensor_t sensor
Definition ai_engine.h:49
uint32_t flash_bytes
Definition ai_engine.h:62
void(*) finalize(void)
Definition ai_engine.h:71
int(*) init(void)
Definition ai_engine.h:68
int(*) enqueue(const float *in)
Definition ai_engine.h:69
Definition ai_engine.h:75
uint32_t inference_us
Definition ai_engine.h:81
uint32_t seq
Definition ai_engine.h:84
uint8_t running
Definition ai_engine.h:79
uint8_t class_count
Definition ai_engine.h:77
uint8_t model_index
Definition ai_engine.h:76
uint8_t top_class
Definition ai_engine.h:78
float scores[AI_MAX_CLASSES]
Definition ai_engine.h:80
uint32_t inference_us_max
Definition ai_engine.h:82
uint32_t inferences
Definition ai_engine.h:83