From 03129c9ed8b47f226d2417a95bfb6085f28cf50e Mon Sep 17 00:00:00 2001 From: yangzhijia <547486824@qq.com> Date: Mon, 17 Aug 2026 16:23:55 +0800 Subject: [PATCH] =?UTF-8?q?fix=EF=BC=9Aresident=20=E5=AD=97=E6=AE=B5?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D=EF=BC=88=E8=AF=AF=E4=BC=A4=E4=BA=86=EF=BC=8C?= =?UTF-8?q?=E9=87=8D=E6=96=B0=E6=94=BE=E5=BC=80=EF=BC=89=20feat=EF=BC=9A?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E9=98=B2=E6=8A=96=E5=8A=9F=E8=83=BD=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/config.h | 4 ++-- include/radar_manager.h | 5 +++++ src/main.cpp | 18 ++++++++++++++++-- src/radar_manager.cpp | 40 +++++++++++++++++++++++++++++++++++++--- 4 files changed, 60 insertions(+), 7 deletions(-) diff --git a/include/config.h b/include/config.h index 8c3c340..83ceafe 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_AFD1 // AFD1 (跌倒/姿态精准, 卫生间用) +#define RADAR_TYPE RADAR_TYPE_ABD1 // AFD1 (跌倒/姿态精准, 卫生间用) // --- 毫米波雷达 --- #define RADAR_UART_PORT Serial2 @@ -132,7 +132,7 @@ #define HEART_RATE_MIN 50 #define HEART_RATE_MAX 120 #define BREATH_RATE_MIN 10 -#define BREATH_RATE_MAX 28 +#define BREATH_RATE_MAX 26 #define TEMP_LOW 5.0f #define TEMP_HIGH 45.0f #define HUMI_LOW 15.0f diff --git a/include/radar_manager.h b/include/radar_manager.h index adbdb08..e98d872 100644 --- a/include/radar_manager.h +++ b/include/radar_manager.h @@ -74,6 +74,7 @@ private: void _extractSleep(const RadarFrame& frame); void _extractWorkStatus(const RadarFrame& frame); void _extractDetectRange(const RadarFrame& frame); + void _filterMotion(bool rawMotion); // 运动状态 3 帧去抖 uint8_t _calcChecksum(const uint8_t* data, uint16_t len); void _notifyUpdate(); @@ -95,6 +96,10 @@ private: bool _stablePresence; // 防抖后的稳定有人/无人 uint8_t _presenceCnt; // 连续一致计数 bool _lastRawPresence; // 上一次原始值 + + // 运动状态防抖 (体动信号常因小动作抖动, 加 3 帧持续才确认) + uint8_t _motionCnt; // 连续一致计数 + bool _lastRawMotion; // 上一次原始值 }; #endif // RADAR_MANAGER_H diff --git a/src/main.cpp b/src/main.cpp index 998c24a..da6339e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -66,12 +66,14 @@ bool g_fallDismissed = false; // 已手动解除, 不重复报警 uint32_t g_vitalLostMs = 0; // 生命体征丢失持续计时 (ms) bool g_vitalLostActive = false; // 生命体征丢失报警激活 bool g_vitalLostDismissed = false; // 生命体征丢失解除抑制 +uint32_t g_stillMs = 0; // 持续静默计时 (清醒时即使雷达误判, 持续有体动会重置) #define ALARM_HYSTERESIS 3 // 连续3次确认才触发 #define FALL_DETECT_MS 10000 // 跌倒持续10秒才触发 (房顶安装, 过滤坐下) #define FALL_CLEAR_MS 18000 // 跌倒消失18秒后才清除 (防误消) #define VITAL_DETECT_MS 20000 // 心率/呼吸持续异常20秒才触发 #define VITAL_CLEAR_MS 15000 // 心率/呼吸持续正常15秒才恢复 #define VITAL_LOST_MS 60000 // 在床但心率呼吸持续为0达60秒 → 疑似骤停 +#define SLEEP_QUIET_MS 300000 // 需持续5分钟静默才上报睡眠评分 (清醒坐着会被雷达误判, 靠这个过滤) // ============================================================ // 时间同步 @@ -270,8 +272,20 @@ TelemetryData buildTelemetry(const RadarRawData& radar) { t.breathRate = radar.breathRate; t.body = radar.body; t.sleepState = radar.sleepState; - // 清醒时不报睡眠评分 (雷达此时的值是残留/无意义), 避免误导 - t.sleepScore = (radar.resident && radar.sleepState != 0) ? radar.sleepScore : 0; + // 清醒时不报睡眠评分: + // - radar.resident=false 人离床 → 必然清醒 + // - radar.sleepState==0 雷达说清醒 → 不报 + // - 持续静默 < 5 分钟 → 可能是清醒坐着被误判, 也不报 + // 这道闸门防止雷达误判深睡时上报误导评分 + if (radar.body > 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; t.radarValid = radar.radarValid; t.vitalValid = radar.vitalValid; diff --git a/src/radar_manager.cpp b/src/radar_manager.cpp index 1884d23..e1f663f 100644 --- a/src/radar_manager.cpp +++ b/src/radar_manager.cpp @@ -24,6 +24,8 @@ RadarManager::RadarManager() , _stablePresence(false) , _presenceCnt(0) , _lastRawPresence(false) + , _motionCnt(0) + , _lastRawMotion(false) { memset(&_currentFrame, 0, sizeof(_currentFrame)); memset(&_data, 0, sizeof(_data)); @@ -241,7 +243,13 @@ void RadarManager::_processCompleteFrame() { _extractHeartRate(_currentFrame); break; case CTRL_FALL: +#if RADAR_TYPE == RADAR_TYPE_AFD1 _extractFall(_currentFrame); // AFD1 专用跌倒 +#else + // ABD1 不会发 0x83 帧, 若收到可能是干扰, 直接丢弃 + (void)_currentFrame; +#endif + break; break; case CTRL_SLEEP: _extractSleep(_currentFrame); @@ -282,7 +290,8 @@ void RadarManager::_extractPresence(const RadarFrame& frame) { // cmd 0x01 (len=1): 运动状态/体动 (值越大越活跃) else if (frame.command == 0x01 && frame.data_length >= 1) { _data.body = raw; - _data.motion = (raw >= 1); + bool rawMotion = (raw >= 1); + _filterMotion(rawMotion); // 3 帧防抖 _data.radarValid = true; _data.lastUpdate = millis(); _notifyUpdate(); @@ -297,14 +306,16 @@ void RadarManager::_extractPresence(const RadarFrame& frame) { // cmd 0x04 (len=2): [0]=presence [1]=motion else if (frame.command == 0x04 && frame.data_length >= 2) { rawPresence = (frame.data[0] != 0); - _data.motion = (frame.data[1] == 0); + bool rawMotion = (frame.data[1] == 0); + _filterMotion(rawMotion); // 3 帧防抖 _data.radarValid = true; _data.lastUpdate = millis(); } // cmd 0x05 (len=6): 完整包 else if (frame.command == 0x05 && frame.data_length >= 6) { rawPresence = (frame.data[0] != 0); - _data.motion = (frame.data[1] == 0); + bool rawMotion = (frame.data[1] == 0); + _filterMotion(rawMotion); // 3 帧防抖 _data.distance = ((uint16_t)frame.data[2] << 8) | frame.data[3]; _data.body = frame.data[4]; _data.radarValid = true; @@ -386,10 +397,33 @@ 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; _data.resident = (frame.data[3] & 0x02) != 0; _data.lastUpdate = millis(); _notifyUpdate(); +#else + // ABD1 协议: data[3] bit0 是睡眠状态标志 (不是跌倒), bit1 才是驻留 + // 之前误把 bit0 当 fall 触发误报, 现只取 resident + _data.resident = (frame.data[3] & 0x02) != 0; + _data.lastUpdate = millis(); + _notifyUpdate(); +#endif + } +} + +// 运动状态防抖: 连续 3 帧一致才更新 _data.motion +// 雷达体动信号容易因小幅抖动/呼吸胸腔起伏/电磁干扰在 0/1 之间跳变 +void RadarManager::_filterMotion(bool rawMotion) { + if (rawMotion == _lastRawMotion) { + if (_motionCnt < 3) _motionCnt++; + } else { + _motionCnt = 0; + _lastRawMotion = rawMotion; + } + if (_motionCnt >= 3 && rawMotion != _data.motion) { + _data.motion = rawMotion; } }