diff --git a/include/app_flow/pre_post_proc/stdc_post_process.h b/include/app_flow/pre_post_proc/stdc_post_process.h index 562c119..2df1f95 100644 --- a/include/app_flow/pre_post_proc/stdc_post_process.h +++ b/include/app_flow/pre_post_proc/stdc_post_process.h @@ -34,7 +34,6 @@ #define STDC_WARNING_SECONDS 2.0f #define STDC_MOTION_THRESHOLD 3.0f #define STDC_TREE_GROWTH_THR 0.05f -#define THR_GRASS_ROI 0.75f /* grass ratio in forward ROI */ #define THR_CAR_GLOBAL 0.05f #define THR_PERSON_GLOBAL 0.01f #define THR_GREENERY_GLOBAL 0.20f @@ -48,18 +47,20 @@ #define COL_ROI_TOP 0.25f #define COL_ROI_BOTTOM 0.70f -#define THR_PERSON_COLLISION 0.10f -#define THR_CAR_COLLISION 0.20f -#define THR_TREE_COLLISION 0.20f -#define THR_BUNKER_COLLISION 0.20f -#define THR_POND_COLLISION 0.20f - -/* Alert ROI thresholds (left/right sides) — tuned independently from collision */ -#define THR_PERSON_ALERT 0.10f -#define THR_CAR_ALERT 0.10f -#define THR_TREE_ALERT 0.20f -#define THR_BUNKER_ALERT 0.10f -#define THR_POND_ALERT 0.10f +/* Runtime-tunable detection thresholds (BLE SET / INI [detect] section). + * Defined in stdc_post_process.c; set from kp_firmware.c at startup. + * BLE format: SET THR_GRASS_ROI 750 (integer promille, /1000 = 0.750) */ +extern float g_thr_grass_roi; +extern float g_thr_person_collision; +extern float g_thr_car_collision; +extern float g_thr_tree_collision; +extern float g_thr_bunker_collision; +extern float g_thr_pond_collision; +extern float g_thr_person_alert; +extern float g_thr_car_alert; +extern float g_thr_tree_alert; +extern float g_thr_bunker_alert; +extern float g_thr_pond_alert; /* Forward ROI trapezoid - from Python roi_pts */ #define FWD_ROI_TOP_LEFT 0.45f diff --git a/ini/host_stream.ini b/ini/host_stream.ini index e46adc6..7c91748 100644 --- a/ini/host_stream.ini +++ b/ini/host_stream.ini @@ -115,3 +115,18 @@ can_id = 0x100 # 11-bit standard frame ID for outbound event fram speed_normal = 240 # CAN speed command: normal driving speed_alert = 35 # CAN speed command: slow/alert (person or obstacle detected) speed_stop = 10 # CAN speed command: stop/emergency + +[detect] +# Detection thresholds as integer promille (value × 1000). +# BLE SET example: SET THR_GRASS_ROI 750 → 0.750 +thr_grass_roi = 800 # grass ratio in forward ROI → violation +thr_person_col = 200 # person ratio in center ROI → collision +thr_car_col = 200 # car ratio → collision +thr_tree_col = 200 # tree ratio → collision +thr_bunker_col = 200 # bunker ratio → collision +thr_pond_col = 200 # pond ratio → collision +thr_person_alt = 200 # person ratio in side ROI → alert +thr_car_alt = 200 # car ratio → alert +thr_tree_alt = 200 # tree ratio → alert +thr_bunker_alt = 200 # bunker ratio → alert +thr_pond_alt = 200 # pond ratio → alert diff --git a/src/host_stream/app_header_init.c b/src/host_stream/app_header_init.c index f7563b2..151bde7 100644 --- a/src/host_stream/app_header_init.c +++ b/src/host_stream/app_header_init.c @@ -531,7 +531,7 @@ int app_header_recv_inference(uint32_t buf_addr, bool *bl_run_next_inference) s_last_log = now; log_trim_if_needed(); /* keep /tmp/fw.log under LOG_MAX_BYTES */ if (g_verbose_log) { - printf("[STDC] frame=%u mov=%d diff=%.1f " + printf("[STDC] frame=%u moving=%d diff=%.1f " "bunker=%.1f%% car=%.1f%% grass=%.1f%% greenery=%.1f%% " "person=%.1f%% pond=%.1f%% road=%.1f%% tree=%.1f%%\n", ana->frame_count, ana->is_moving, ana->motion_diff, diff --git a/src/host_stream/event_recorder.c b/src/host_stream/event_recorder.c index 89e7a22..a3dec97 100644 --- a/src/host_stream/event_recorder.c +++ b/src/host_stream/event_recorder.c @@ -44,7 +44,7 @@ #include "bt_uart.h" #include "can_bus.h" #include "buzzer.h" -#include "stdc_post_process.h" /* THR_*_COLLISION constants */ +#include "stdc_post_process.h" /* g_thr_* runtime threshold globals */ /* ── External (from kdp2_host_stream.c) ──────────────────────────────────── */ extern VMF_VSRC_HANDLE_T *g_ptVsrcHandle; @@ -895,6 +895,72 @@ static void handle_set_cmd(const char *cmd) persist_ini_int("verbose_log", v); snprintf(ack, sizeof(ack), "SET_ACK verbose_log=%d", v); + } else if (strcmp(key, "THR_GRASS_ROI") == 0) { + if (val < 1 || val > 1000) { bt_uart_send_json("SET_NACK THR_GRASS_ROI OUT_OF_RANGE"); return; } + g_thr_grass_roi = val / 1000.0f; + persist_ini_int("thr_grass_roi", val); + snprintf(ack, sizeof(ack), "SET_ACK thr_grass_roi=%.3f", g_thr_grass_roi); + + } else if (strcmp(key, "THR_PERSON_COL") == 0) { + if (val < 1 || val > 1000) { bt_uart_send_json("SET_NACK THR_PERSON_COL OUT_OF_RANGE"); return; } + g_thr_person_collision = val / 1000.0f; + persist_ini_int("thr_person_col", val); + snprintf(ack, sizeof(ack), "SET_ACK thr_person_col=%.3f", g_thr_person_collision); + + } else if (strcmp(key, "THR_CAR_COL") == 0) { + if (val < 1 || val > 1000) { bt_uart_send_json("SET_NACK THR_CAR_COL OUT_OF_RANGE"); return; } + g_thr_car_collision = val / 1000.0f; + persist_ini_int("thr_car_col", val); + snprintf(ack, sizeof(ack), "SET_ACK thr_car_col=%.3f", g_thr_car_collision); + + } else if (strcmp(key, "THR_TREE_COL") == 0) { + if (val < 1 || val > 1000) { bt_uart_send_json("SET_NACK THR_TREE_COL OUT_OF_RANGE"); return; } + g_thr_tree_collision = val / 1000.0f; + persist_ini_int("thr_tree_col", val); + snprintf(ack, sizeof(ack), "SET_ACK thr_tree_col=%.3f", g_thr_tree_collision); + + } else if (strcmp(key, "THR_BUNKER_COL") == 0) { + if (val < 1 || val > 1000) { bt_uart_send_json("SET_NACK THR_BUNKER_COL OUT_OF_RANGE"); return; } + g_thr_bunker_collision = val / 1000.0f; + persist_ini_int("thr_bunker_col", val); + snprintf(ack, sizeof(ack), "SET_ACK thr_bunker_col=%.3f", g_thr_bunker_collision); + + } else if (strcmp(key, "THR_POND_COL") == 0) { + if (val < 1 || val > 1000) { bt_uart_send_json("SET_NACK THR_POND_COL OUT_OF_RANGE"); return; } + g_thr_pond_collision = val / 1000.0f; + persist_ini_int("thr_pond_col", val); + snprintf(ack, sizeof(ack), "SET_ACK thr_pond_col=%.3f", g_thr_pond_collision); + + } else if (strcmp(key, "THR_PERSON_ALT") == 0) { + if (val < 1 || val > 1000) { bt_uart_send_json("SET_NACK THR_PERSON_ALT OUT_OF_RANGE"); return; } + g_thr_person_alert = val / 1000.0f; + persist_ini_int("thr_person_alt", val); + snprintf(ack, sizeof(ack), "SET_ACK thr_person_alt=%.3f", g_thr_person_alert); + + } else if (strcmp(key, "THR_CAR_ALT") == 0) { + if (val < 1 || val > 1000) { bt_uart_send_json("SET_NACK THR_CAR_ALT OUT_OF_RANGE"); return; } + g_thr_car_alert = val / 1000.0f; + persist_ini_int("thr_car_alt", val); + snprintf(ack, sizeof(ack), "SET_ACK thr_car_alt=%.3f", g_thr_car_alert); + + } else if (strcmp(key, "THR_TREE_ALT") == 0) { + if (val < 1 || val > 1000) { bt_uart_send_json("SET_NACK THR_TREE_ALT OUT_OF_RANGE"); return; } + g_thr_tree_alert = val / 1000.0f; + persist_ini_int("thr_tree_alt", val); + snprintf(ack, sizeof(ack), "SET_ACK thr_tree_alt=%.3f", g_thr_tree_alert); + + } else if (strcmp(key, "THR_BUNKER_ALT") == 0) { + if (val < 1 || val > 1000) { bt_uart_send_json("SET_NACK THR_BUNKER_ALT OUT_OF_RANGE"); return; } + g_thr_bunker_alert = val / 1000.0f; + persist_ini_int("thr_bunker_alt", val); + snprintf(ack, sizeof(ack), "SET_ACK thr_bunker_alt=%.3f", g_thr_bunker_alert); + + } else if (strcmp(key, "THR_POND_ALT") == 0) { + if (val < 1 || val > 1000) { bt_uart_send_json("SET_NACK THR_POND_ALT OUT_OF_RANGE"); return; } + g_thr_pond_alert = val / 1000.0f; + persist_ini_int("thr_pond_alt", val); + snprintf(ack, sizeof(ack), "SET_ACK thr_pond_alt=%.3f", g_thr_pond_alert); + } else { snprintf(ack, sizeof(ack), "SET_NACK UNKNOWN_KEY %.48s", key); bt_uart_send_json(ack); @@ -1411,11 +1477,11 @@ void event_recorder_update(const stdc_analysis_t *ana) int col_now = ana->collision_risk; if (col_now != g_last_collision_warning) { const char *col_type = NULL; - if (ana->col_person_ratio >= THR_PERSON_COLLISION) col_type = "person"; - else if (ana->col_car_ratio >= THR_CAR_COLLISION) col_type = "vehicle"; - else if (ana->col_tree_ratio >= THR_TREE_COLLISION) col_type = "tree"; - else if (ana->col_pond_ratio >= THR_POND_COLLISION) col_type = "water_hazard"; - else if (ana->col_bunker_ratio >= THR_BUNKER_COLLISION) col_type = "bush"; + if (ana->col_person_ratio >= g_thr_person_collision) col_type = "person"; + else if (ana->col_car_ratio >= g_thr_car_collision) col_type = "vehicle"; + else if (ana->col_tree_ratio >= g_thr_tree_collision) col_type = "tree"; + else if (ana->col_pond_ratio >= g_thr_pond_collision) col_type = "water_hazard"; + else if (ana->col_bunker_ratio >= g_thr_bunker_collision) col_type = "bush"; fire_collision_warning(col_now, col_now ? col_type : NULL); g_last_collision_warning = col_now; } diff --git a/src/host_stream/kp_firmware.c b/src/host_stream/kp_firmware.c index a01045c..d75d2b1 100644 --- a/src/host_stream/kp_firmware.c +++ b/src/host_stream/kp_firmware.c @@ -41,6 +41,7 @@ #include "kdp2_host_stream.h" #include "fec_api.h" #include "event_recorder.h" +#include "stdc_post_process.h" #include "bt_uart.h" #include "can_bus.h" #include "buzzer.h" @@ -325,6 +326,25 @@ int loadConfig(HOST_STREAM_INIT_OPT_T* pHostStreamInit) ev_spd_normal, ev_spd_alert, ev_spd_stop); } + /* --- [detect] section: runtime-tunable detection thresholds --- */ + { + g_thr_grass_roi = iniparser_getint(ini, "detect:thr_grass_roi", 750) / 1000.0f; + g_thr_person_collision = iniparser_getint(ini, "detect:thr_person_col", 100) / 1000.0f; + g_thr_car_collision = iniparser_getint(ini, "detect:thr_car_col", 200) / 1000.0f; + g_thr_tree_collision = iniparser_getint(ini, "detect:thr_tree_col", 200) / 1000.0f; + g_thr_bunker_collision = iniparser_getint(ini, "detect:thr_bunker_col", 200) / 1000.0f; + g_thr_pond_collision = iniparser_getint(ini, "detect:thr_pond_col", 200) / 1000.0f; + g_thr_person_alert = iniparser_getint(ini, "detect:thr_person_alt", 100) / 1000.0f; + g_thr_car_alert = iniparser_getint(ini, "detect:thr_car_alt", 100) / 1000.0f; + g_thr_tree_alert = iniparser_getint(ini, "detect:thr_tree_alt", 200) / 1000.0f; + g_thr_bunker_alert = iniparser_getint(ini, "detect:thr_bunker_alt", 100) / 1000.0f; + g_thr_pond_alert = iniparser_getint(ini, "detect:thr_pond_alt", 100) / 1000.0f; + printf("[FW] detect thresholds: grass=%.3f p_col=%.3f c_col=%.3f t_col=%.3f" + " p_alt=%.3f c_alt=%.3f\n", + g_thr_grass_roi, g_thr_person_collision, g_thr_car_collision, + g_thr_tree_collision, g_thr_person_alert, g_thr_car_alert); + } + /* --- [can] section: MCP2515 CAN bus via J15 SPI connector --- */ { int can_enable = iniparser_getint(ini, "can:enable", 0); diff --git a/src/pre_post_proc/stdc_post_process.c b/src/pre_post_proc/stdc_post_process.c index 4eaad9c..5f565f8 100644 --- a/src/pre_post_proc/stdc_post_process.c +++ b/src/pre_post_proc/stdc_post_process.c @@ -16,6 +16,19 @@ extern unsigned int g_verbose_log; +/* Detection thresholds — runtime tunable via BLE SET / INI [detect] section */ +float g_thr_grass_roi = 0.75f; +float g_thr_person_collision = 0.10f; +float g_thr_car_collision = 0.20f; +float g_thr_tree_collision = 0.20f; +float g_thr_bunker_collision = 0.20f; +float g_thr_pond_collision = 0.20f; +float g_thr_person_alert = 0.10f; +float g_thr_car_alert = 0.10f; +float g_thr_tree_alert = 0.20f; +float g_thr_bunker_alert = 0.10f; +float g_thr_pond_alert = 0.10f; + typedef struct { uint8_t *prev_roi_luma; uint32_t prev_roi_capacity; @@ -252,11 +265,11 @@ int stdc_post_process(int model_id, struct kdp_image_s *image_p) ana->left_type[0] = '\0'; { struct { float ratio; float thr; const char *name; } lc[] = { - { ana->left_person_ratio, THR_PERSON_ALERT, "person" }, - { ana->left_car_ratio, THR_CAR_ALERT, "vehicle" }, - { ana->left_tree_ratio, THR_TREE_ALERT, "tree" }, - { ana->left_pond_ratio, THR_POND_ALERT, "water_hazard" }, - { ana->left_bunker_ratio, THR_BUNKER_ALERT, "bush" }, + { ana->left_person_ratio, g_thr_person_alert, "person" }, + { ana->left_car_ratio, g_thr_car_alert, "vehicle" }, + { ana->left_tree_ratio, g_thr_tree_alert, "tree" }, + { ana->left_pond_ratio, g_thr_pond_alert, "water_hazard" }, + { ana->left_bunker_ratio, g_thr_bunker_alert, "bush" }, }; float best = 0.0f; for (int i = 0; i < 5; i++) { @@ -275,11 +288,11 @@ int stdc_post_process(int model_id, struct kdp_image_s *image_p) ana->right_type[0] = '\0'; { struct { float ratio; float thr; const char *name; } rc[] = { - { ana->right_person_ratio, THR_PERSON_ALERT, "person" }, - { ana->right_car_ratio, THR_CAR_ALERT, "vehicle" }, - { ana->right_tree_ratio, THR_TREE_ALERT, "tree" }, - { ana->right_pond_ratio, THR_POND_ALERT, "water_hazard" }, - { ana->right_bunker_ratio, THR_BUNKER_ALERT, "bush" }, + { ana->right_person_ratio, g_thr_person_alert, "person" }, + { ana->right_car_ratio, g_thr_car_alert, "vehicle" }, + { ana->right_tree_ratio, g_thr_tree_alert, "tree" }, + { ana->right_pond_ratio, g_thr_pond_alert, "water_hazard" }, + { ana->right_bunker_ratio, g_thr_bunker_alert, "bush" }, }; float best = 0.0f; for (int i = 0; i < 5; i++) { @@ -296,7 +309,7 @@ int stdc_post_process(int model_id, struct kdp_image_s *image_p) /* ------------------------------------------------------- * 3. Warning flags * ------------------------------------------------------- */ - ana->on_grass = (ana->grass_roi_ratio > THR_GRASS_ROI) ? 1 : 0; + ana->on_grass = (ana->grass_roi_ratio > g_thr_grass_roi) ? 1 : 0; if (ana->on_grass && ana->is_moving) g_stdc_state.consecutive_grass_frames++; else @@ -313,11 +326,11 @@ int stdc_post_process(int model_id, struct kdp_image_s *image_p) ana->tree_approaching = (ana->tree_roi_growth > STDC_TREE_GROWTH_THR) ? 1 : 0; ana->collision_risk = 0; - if (ana->col_person_ratio >= THR_PERSON_COLLISION) ana->collision_risk = 1; - if (ana->col_car_ratio >= THR_CAR_COLLISION) ana->collision_risk = 1; - if (ana->col_tree_ratio >= THR_TREE_COLLISION) ana->collision_risk = 1; - if (ana->col_bunker_ratio >= THR_BUNKER_COLLISION) ana->collision_risk = 1; - if (ana->col_pond_ratio >= THR_POND_COLLISION) ana->collision_risk = 1; + if (ana->col_person_ratio >= g_thr_person_collision) ana->collision_risk = 1; + if (ana->col_car_ratio >= g_thr_car_collision) ana->collision_risk = 1; + if (ana->col_tree_ratio >= g_thr_tree_collision) ana->collision_risk = 1; + if (ana->col_bunker_ratio >= g_thr_bunker_collision) ana->collision_risk = 1; + if (ana->col_pond_ratio >= g_thr_pond_collision) ana->collision_risk = 1; /* ------------------------------------------------------- * 4. Log summary (throttled to 1Hz, verbose_log only)