Issue #263: Default fall_thresh raised from 2.0 to 15.0 rad/s² — normal walking produces accelerations of 2.5-5.0 which triggered constant false "Fall Detected" alerts. Added consecutive-frame requirement (3 frames) and 5-second cooldown debounce to prevent alert storms. Issue #265: Added partitions_4mb.csv and sdkconfig.defaults.4mb for ESP32-S3 boards with 4MB flash (e.g. SuperMini). OTA slots are 1.856MB each, fitting the ~978KB firmware binary with room to spare. Co-Authored-By: claude-flow <ruv@ruv.net>
This commit is contained in:
parent
1d4af7c757
commit
8afd76da20
|
|
@ -244,6 +244,10 @@ static uint32_t s_frame_count;
|
||||||
/** Previous phase velocity for fall detection (acceleration). */
|
/** Previous phase velocity for fall detection (acceleration). */
|
||||||
static float s_prev_phase_velocity;
|
static float s_prev_phase_velocity;
|
||||||
|
|
||||||
|
/** Fall detection debounce state (issue #263). */
|
||||||
|
static uint8_t s_fall_consec_count; /**< Consecutive frames above threshold. */
|
||||||
|
static int64_t s_fall_last_alert_us; /**< Timestamp of last fall alert (debounce). */
|
||||||
|
|
||||||
/** Adaptive calibration state. */
|
/** Adaptive calibration state. */
|
||||||
static bool s_calibrated;
|
static bool s_calibrated;
|
||||||
static float s_calib_sum;
|
static float s_calib_sum;
|
||||||
|
|
@ -689,7 +693,7 @@ static void process_frame(const edge_ring_slot_t *slot)
|
||||||
}
|
}
|
||||||
s_presence_detected = (s_presence_score > threshold);
|
s_presence_detected = (s_presence_score > threshold);
|
||||||
|
|
||||||
/* --- Step 10: Fall detection (phase acceleration) --- */
|
/* --- Step 10: Fall detection (phase acceleration + debounce, issue #263) --- */
|
||||||
if (s_history_len >= 3) {
|
if (s_history_len >= 3) {
|
||||||
uint16_t i0 = (s_history_idx + EDGE_PHASE_HISTORY_LEN - 1) % EDGE_PHASE_HISTORY_LEN;
|
uint16_t i0 = (s_history_idx + EDGE_PHASE_HISTORY_LEN - 1) % EDGE_PHASE_HISTORY_LEN;
|
||||||
uint16_t i1 = (s_history_idx + EDGE_PHASE_HISTORY_LEN - 2) % EDGE_PHASE_HISTORY_LEN;
|
uint16_t i1 = (s_history_idx + EDGE_PHASE_HISTORY_LEN - 2) % EDGE_PHASE_HISTORY_LEN;
|
||||||
|
|
@ -697,10 +701,26 @@ static void process_frame(const edge_ring_slot_t *slot)
|
||||||
float accel = fabsf(velocity - s_prev_phase_velocity);
|
float accel = fabsf(velocity - s_prev_phase_velocity);
|
||||||
s_prev_phase_velocity = velocity;
|
s_prev_phase_velocity = velocity;
|
||||||
|
|
||||||
s_fall_detected = (accel > s_cfg.fall_thresh);
|
if (accel > s_cfg.fall_thresh) {
|
||||||
if (s_fall_detected) {
|
s_fall_consec_count++;
|
||||||
ESP_LOGW(TAG, "Fall detected! accel=%.4f > thresh=%.4f",
|
} else {
|
||||||
accel, s_cfg.fall_thresh);
|
s_fall_consec_count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Require EDGE_FALL_CONSEC_MIN consecutive frames above threshold,
|
||||||
|
* plus a cooldown period to prevent alert storms. */
|
||||||
|
int64_t now_us = esp_timer_get_time();
|
||||||
|
int64_t cooldown_us = (int64_t)EDGE_FALL_COOLDOWN_MS * 1000;
|
||||||
|
if (s_fall_consec_count >= EDGE_FALL_CONSEC_MIN
|
||||||
|
&& (now_us - s_fall_last_alert_us) >= cooldown_us)
|
||||||
|
{
|
||||||
|
s_fall_detected = true;
|
||||||
|
s_fall_last_alert_us = now_us;
|
||||||
|
s_fall_consec_count = 0;
|
||||||
|
ESP_LOGW(TAG, "Fall detected! accel=%.4f > thresh=%.4f (consec=%u)",
|
||||||
|
accel, s_cfg.fall_thresh, EDGE_FALL_CONSEC_MIN);
|
||||||
|
} else if (s_fall_consec_count == 0) {
|
||||||
|
s_fall_detected = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -850,6 +870,8 @@ esp_err_t edge_processing_init(const edge_config_t *cfg)
|
||||||
s_latest_rssi = 0;
|
s_latest_rssi = 0;
|
||||||
s_frame_count = 0;
|
s_frame_count = 0;
|
||||||
s_prev_phase_velocity = 0.0f;
|
s_prev_phase_velocity = 0.0f;
|
||||||
|
s_fall_consec_count = 0;
|
||||||
|
s_fall_last_alert_us = 0;
|
||||||
s_last_vitals_send_us = 0;
|
s_last_vitals_send_us = 0;
|
||||||
s_has_prev_iq = false;
|
s_has_prev_iq = false;
|
||||||
s_prev_iq_len = 0;
|
s_prev_iq_len = 0;
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,10 @@
|
||||||
#define EDGE_CALIB_FRAMES 1200 /**< Frames for adaptive calibration (~60s at 20 Hz). */
|
#define EDGE_CALIB_FRAMES 1200 /**< Frames for adaptive calibration (~60s at 20 Hz). */
|
||||||
#define EDGE_CALIB_SIGMA_MULT 3.0f /**< Threshold = mean + 3*sigma of ambient. */
|
#define EDGE_CALIB_SIGMA_MULT 3.0f /**< Threshold = mean + 3*sigma of ambient. */
|
||||||
|
|
||||||
|
/* ---- Fall detection ---- */
|
||||||
|
#define EDGE_FALL_COOLDOWN_MS 5000 /**< Minimum ms between fall alerts (debounce). */
|
||||||
|
#define EDGE_FALL_CONSEC_MIN 3 /**< Consecutive frames above threshold to trigger. */
|
||||||
|
|
||||||
/* ---- SPSC ring buffer slot ---- */
|
/* ---- SPSC ring buffer slot ---- */
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint8_t iq_data[EDGE_MAX_IQ_BYTES]; /**< Raw I/Q bytes from CSI callback. */
|
uint8_t iq_data[EDGE_MAX_IQ_BYTES]; /**< Raw I/Q bytes from CSI callback. */
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,7 @@ void nvs_config_load(nvs_config_t *cfg)
|
||||||
#ifdef CONFIG_EDGE_FALL_THRESH
|
#ifdef CONFIG_EDGE_FALL_THRESH
|
||||||
cfg->fall_thresh = (float)CONFIG_EDGE_FALL_THRESH / 1000.0f;
|
cfg->fall_thresh = (float)CONFIG_EDGE_FALL_THRESH / 1000.0f;
|
||||||
#else
|
#else
|
||||||
cfg->fall_thresh = 2.0f;
|
cfg->fall_thresh = 15.0f; /* Default raised from 2.0 — see issue #263. */
|
||||||
#endif
|
#endif
|
||||||
cfg->vital_window = 256;
|
cfg->vital_window = 256;
|
||||||
#ifdef CONFIG_EDGE_VITAL_INTERVAL_MS
|
#ifdef CONFIG_EDGE_VITAL_INTERVAL_MS
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
# ESP32-S3 CSI Node — 4MB flash partition table (issue #265)
|
||||||
|
# For boards with 4MB flash (e.g. ESP32-S3 SuperMini 4MB).
|
||||||
|
# Binary is ~978KB so each OTA slot is 1.875MB — plenty of room.
|
||||||
|
#
|
||||||
|
# Usage: copy to partitions_display.csv OR set in sdkconfig:
|
||||||
|
# CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions_4mb.csv"
|
||||||
|
# CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
|
||||||
|
# CONFIG_ESPTOOLPY_FLASHSIZE="4MB"
|
||||||
|
#
|
||||||
|
# Name, Type, SubType, Offset, Size, Flags
|
||||||
|
nvs, data, nvs, 0x9000, 0x6000,
|
||||||
|
otadata, data, ota, 0xF000, 0x2000,
|
||||||
|
phy_init, data, phy, 0x11000, 0x1000,
|
||||||
|
ota_0, app, ota_0, 0x20000, 0x1D0000,
|
||||||
|
ota_1, app, ota_1, 0x1F0000, 0x1D0000,
|
||||||
|
Can't render this file because it contains an unexpected character in line 6 and column 44.
|
|
|
@ -168,7 +168,9 @@ def main():
|
||||||
parser.add_argument("--edge-tier", type=int, choices=[0, 1, 2],
|
parser.add_argument("--edge-tier", type=int, choices=[0, 1, 2],
|
||||||
help="Edge processing tier: 0=off, 1=stats, 2=vitals")
|
help="Edge processing tier: 0=off, 1=stats, 2=vitals")
|
||||||
parser.add_argument("--pres-thresh", type=int, help="Presence detection threshold (default: 50)")
|
parser.add_argument("--pres-thresh", type=int, help="Presence detection threshold (default: 50)")
|
||||||
parser.add_argument("--fall-thresh", type=int, help="Fall detection threshold (default: 500)")
|
parser.add_argument("--fall-thresh", type=int, help="Fall detection threshold in milli-units "
|
||||||
|
"(value/1000 = rad/s²). Default: 15000 → 15.0 rad/s². "
|
||||||
|
"Raise to reduce false positives in high-traffic areas.")
|
||||||
parser.add_argument("--vital-win", type=int, help="Phase history window in frames (default: 300)")
|
parser.add_argument("--vital-win", type=int, help="Phase history window in frames (default: 300)")
|
||||||
parser.add_argument("--vital-int", type=int, help="Vitals packet interval in ms (default: 1000)")
|
parser.add_argument("--vital-int", type=int, help="Vitals packet interval in ms (default: 1000)")
|
||||||
parser.add_argument("--subk-count", type=int, help="Top-K subcarrier count (default: 32)")
|
parser.add_argument("--subk-count", type=int, help="Top-K subcarrier count (default: 32)")
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
# ESP32-S3 CSI Node — 4MB Flash SDK Configuration (issue #265)
|
||||||
|
# For boards with 4MB flash (e.g. ESP32-S3 SuperMini 4MB).
|
||||||
|
#
|
||||||
|
# Build: cp sdkconfig.defaults.4mb sdkconfig.defaults && idf.py set-target esp32s3 && idf.py build
|
||||||
|
# Or: idf.py -D SDKCONFIG_DEFAULTS="sdkconfig.defaults.4mb" set-target esp32s3 && idf.py build
|
||||||
|
|
||||||
|
CONFIG_IDF_TARGET="esp32s3"
|
||||||
|
|
||||||
|
# 4MB flash partition table
|
||||||
|
CONFIG_PARTITION_TABLE_CUSTOM=y
|
||||||
|
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions_4mb.csv"
|
||||||
|
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
|
||||||
|
CONFIG_ESPTOOLPY_FLASHSIZE="4MB"
|
||||||
|
|
||||||
|
# Compiler: optimize for size (critical for 4MB)
|
||||||
|
CONFIG_COMPILER_OPTIMIZATION_SIZE=y
|
||||||
|
|
||||||
|
# CSI support
|
||||||
|
CONFIG_ESP_WIFI_CSI_ENABLED=y
|
||||||
|
|
||||||
|
# Disable display support to save flash (ADR-045 display requires 8MB)
|
||||||
|
# CONFIG_DISPLAY_ENABLE is not set
|
||||||
|
|
||||||
|
# Reduce logging to save flash
|
||||||
|
CONFIG_BOOTLOADER_LOG_LEVEL_WARN=y
|
||||||
|
CONFIG_LOG_DEFAULT_LEVEL_INFO=y
|
||||||
|
|
||||||
|
CONFIG_LWIP_SO_RCVBUF=y
|
||||||
|
CONFIG_ESP_MAIN_TASK_STACK_SIZE=8192
|
||||||
Loading…
Reference in New Issue