feat(detect): make detection thresholds runtime-tunable via BLE SET / INI

Replace 11 compile-time THR_* #defines with runtime float globals
(g_thr_grass_roi, g_thr_*_collision, g_thr_*_alert) defined in
stdc_post_process.c and exposed via extern in the header.

- ini/host_stream.ini: add [detect] section with integer promille values
- kp_firmware.c: read [detect] section at startup, log initial values
- event_recorder.c: add 11 BLE SET handlers (THR_GRASS_ROI,
  THR_PERSON/CAR/TREE/BUNKER/POND_COL, THR_PERSON/CAR/TREE/BUNKER/POND_ALT)
  each with range validation (1-1000), runtime update, and INI persistence
- app_header_init.c: rename log field mov= to moving= for clarity
This commit is contained in:
miketsai 2026-07-19 22:52:08 +08:00
parent 174200eb03
commit b2321fcdca
6 changed files with 151 additions and 36 deletions

View File

@ -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

View File

@ -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

View File

@ -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,

View File

@ -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;
}

View File

@ -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);

View File

@ -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)