From 78458361fa399537aa0cecc22af329b694c330de Mon Sep 17 00:00:00 2001 From: yangzhijia <547486824@qq.com> Date: Tue, 18 Aug 2026 14:51:57 +0800 Subject: [PATCH] =?UTF-8?q?feat=EF=BC=9A=E5=A2=9E=E5=8A=A0=E8=B7=8C?= =?UTF-8?q?=E5=80=92=E9=98=B2=E6=8A=96=E5=B8=A7=20fix=EF=BC=9A=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E5=BC=80=E6=9C=BA=E9=94=99=E8=AF=AF=E8=B7=8C=E5=80=92?= =?UTF-8?q?=E6=8A=A5=E8=AD=A6=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/config.h | 2 +- include/radar_manager.h | 5 +++++ platformio.ini | 2 +- src/main.cpp | 49 +++++++++++++++++++++++------------------ src/radar_manager.cpp | 22 ++++++++++++++++-- 5 files changed, 54 insertions(+), 26 deletions(-) diff --git a/include/config.h b/include/config.h index 83ceafe..019dae5 100644 --- a/include/config.h +++ b/include/config.h @@ -19,7 +19,7 @@ // --- 雷达型号选择 --- #define RADAR_TYPE_ABD1 0 // R60ABD1: 心率/呼吸精准, 跌倒一般 #define RADAR_TYPE_AFD1 1 // R60AFD1: 跌倒/姿态精准, 无心率呼吸 -#define RADAR_TYPE RADAR_TYPE_ABD1 // AFD1 (跌倒/姿态精准, 卫生间用) +#define RADAR_TYPE RADAR_TYPE_AFD1 // AFD1 (跌倒/姿态精准, 卫生间用) // --- 毫米波雷达 --- #define RADAR_UART_PORT Serial2 diff --git a/include/radar_manager.h b/include/radar_manager.h index e98d872..78e6e4a 100644 --- a/include/radar_manager.h +++ b/include/radar_manager.h @@ -75,6 +75,7 @@ private: void _extractWorkStatus(const RadarFrame& frame); void _extractDetectRange(const RadarFrame& frame); void _filterMotion(bool rawMotion); // 运动状态 3 帧去抖 + void _filterFall(bool rawFall); // 跌倒状态 2 帧去抖 (防 AFD1 自检期噪声) uint8_t _calcChecksum(const uint8_t* data, uint16_t len); void _notifyUpdate(); @@ -100,6 +101,10 @@ private: // 运动状态防抖 (体动信号常因小动作抖动, 加 3 帧持续才确认) uint8_t _motionCnt; // 连续一致计数 bool _lastRawMotion; // 上一次原始值 + + // 跌倒状态防抖 (AFD1 自检期 fall 位可能为随机值, 2 帧持续才确认) + uint8_t _fallCnt; // 连续一致计数 + bool _lastRawFall; // 上一次原始值 }; #endif // RADAR_MANAGER_H diff --git a/platformio.ini b/platformio.ini index a55295d..c90236f 100644 --- a/platformio.ini +++ b/platformio.ini @@ -42,4 +42,4 @@ lib_deps = ; 上传配置 upload_speed = 921600 -upload_port = COM20 +upload_port = COM19 diff --git a/src/main.cpp b/src/main.cpp index da6339e..d8a3fe6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -108,8 +108,8 @@ void syncTime() { void handleAlarmCommand(const AlarmCommand& cmd) { if (strcmp(cmd.type, "alarm_restore") == 0) { - // 告警恢复 — 远程指令与手动按钮同等优先级 - g_alarm.executeRestore(cmd); + // 告警恢复 — 远程指令与手动按钮同等优先级 (镜像 onAlarmDismiss 逻辑) + g_alarm.clearAllLocal(); // 清除全部本地告警状态 (远程恢复=手动解除) g_localFallActive = false; @@ -131,17 +131,18 @@ void handleAlarmCommand(const AlarmCommand& cmd) { g_breathNormalMs = 0; g_fallDetectMs = 0; g_fallClearMs = 0; - g_fallDismissed = false; g_vitalLostMs = 0; g_vitalLostActive = false; - // 解除抑制标志也复位 (远程恢复后重新武装) - g_waterDismissed = false; - g_heartDismissed = false; - g_breathDismissed = false; - g_tempDismissed = false; - g_humiDismissed = false; - g_vitalLostDismissed = false; + // 抑制标志置 true — 与按钮一致 (原 BUG: 置 false 导致报警立即重新触发) + // 等报警源信号真正恢复正常后才由 checkLocalAlarms() 重新武装 + g_fallDismissed = true; + g_waterDismissed = true; + g_heartDismissed = true; + g_breathDismissed = true; + g_tempDismissed = true; + g_humiDismissed = true; + g_vitalLostDismissed = true; } else { // 告警触发 g_alarm.executeAlarm(cmd); @@ -271,21 +272,25 @@ TelemetryData buildTelemetry(const RadarRawData& radar) { t.heartRate = radar.heartRate; t.breathRate = radar.breathRate; t.body = radar.body; - t.sleepState = radar.sleepState; - // 清醒时不报睡眠评分: - // - radar.resident=false 人离床 → 必然清醒 - // - radar.sleepState==0 雷达说清醒 → 不报 - // - 持续静默 < 5 分钟 → 可能是清醒坐着被误判, 也不报 - // 这道闸门防止雷达误判深睡时上报误导评分 + + // ─── 睡眠状态判定: 设备端二次校验 ─── + // 雷达常把"清醒静坐"误判为深睡 (只看体动/呼吸/心率不看眼睛) + // 设备端用"持续静默"过滤: 5 分钟内必须无任何体动才算真睡 + // - 持续微动 (手掌点击/键盘敲击/呼吸起伏) → 必然判清醒 + // - 完全无体动持续 5 分钟 → 才是真睡 if (radar.body > 0) { - g_stillMs = 0; // 有体动, 重置静默计时 (人在清醒活动) + g_stillMs = 0; // 任何体动立即重置, 表明此刻清醒 } else if (g_stillMs == 0) { g_stillMs = millis(); } - bool sleepConfirmed = (radar.resident && radar.sleepState != 0 && - radar.vitalValid && - millis() - g_stillMs >= SLEEP_QUIET_MS); - t.sleepScore = sleepConfirmed ? radar.sleepScore : 0; + bool realSleep = (radar.resident && radar.vitalValid && + millis() - g_stillMs >= SLEEP_QUIET_MS); + + // sleepState 二次校验: 雷达说睡 + 静默达标 → 上报; 否则强制清醒 + t.sleepState = realSleep ? radar.sleepState : 0; + // 评分与 sleepState 保持一致 (同步显示) + t.sleepScore = realSleep ? radar.sleepScore : 0; + t.radarValid = radar.radarValid; t.vitalValid = radar.vitalValid; @@ -330,7 +335,7 @@ void checkLocalAlarms(const RadarRawData& radar) { // ─── ★★★★★ 人身安全: 跌倒 (仅浴室/走廊/客厅) ─── bool fallNow = enFall && radar.fall && humanPresent; // 当前是否处于跌倒状态 - if (fallNow && !g_fallDismissed && millis() > 30000) { + if (fallNow && !g_fallDismissed && millis() > 30000) { // AFD1 自检约15-30s, 30s 跳过 + 2 帧去抖兜底 // 跌倒中且未解除 → 累积时长, 满 FALL_DETECT_MS 触发 if (!g_localFallActive) { if (g_fallDetectMs == 0) { diff --git a/src/radar_manager.cpp b/src/radar_manager.cpp index e1f663f..15a5b25 100644 --- a/src/radar_manager.cpp +++ b/src/radar_manager.cpp @@ -26,6 +26,8 @@ RadarManager::RadarManager() , _lastRawPresence(false) , _motionCnt(0) , _lastRawMotion(false) + , _fallCnt(0) + , _lastRawFall(false) { memset(&_currentFrame, 0, sizeof(_currentFrame)); memset(&_data, 0, sizeof(_data)); @@ -371,13 +373,28 @@ void RadarManager::_extractHeartRate(const RadarFrame& frame) { // AFD1 专用: 跌倒检测 (控制字 0x83) void RadarManager::_extractFall(const RadarFrame& frame) { if (frame.data_length >= 1) { - _data.fall = (frame.data[0] != 0); + bool rawFall = (frame.data[0] != 0); + _filterFall(rawFall); // 2 帧去抖, 防 AFD1 自检期误报 _data.radarValid = true; _data.lastUpdate = millis(); _notifyUpdate(); } } +// 跌倒状态防抖: 连续 2 帧一致才更新 _data.fall +// AFD1 自检期 fall 位可能是随机值, 单帧误报会被过滤 +void RadarManager::_filterFall(bool rawFall) { + if (rawFall == _lastRawFall) { + if (_fallCnt < 2) _fallCnt++; + } else { + _fallCnt = 0; + _lastRawFall = rawFall; + } + if (_fallCnt >= 2 && rawFall != _data.fall) { + _data.fall = rawFall; + } +} + void RadarManager::_extractSleep(const RadarFrame& frame) { // 从睡眠数据提取: cmd=0x00/0x01 为入床离床状态 if (frame.data_length >= 1) { @@ -399,7 +416,8 @@ void RadarManager::_extractSleep(const RadarFrame& frame) { if (frame.data_length >= 4) { #if RADAR_TYPE == RADAR_TYPE_AFD1 // AFD1 协议: data[3] 低 2 位 = 跌倒/驻留 - _data.fall = (frame.data[3] & 0x01) != 0; + bool rawFallFromSleep = (frame.data[3] & 0x01) != 0; + _filterFall(rawFallFromSleep); // 走防抖, 0x83 和 0x84 帧任一报 fall=true 都计数 _data.resident = (frame.data[3] & 0x02) != 0; _data.lastUpdate = millis(); _notifyUpdate();