fix:resident 字段修复(误伤了,重新放开)

feat:增加防抖功能。
This commit is contained in:
2026-08-17 16:23:55 +08:00
parent b960682d73
commit 03129c9ed8
4 changed files with 60 additions and 7 deletions
+2 -2
View File
@@ -19,7 +19,7 @@
// --- 雷达型号选择 --- // --- 雷达型号选择 ---
#define RADAR_TYPE_ABD1 0 // R60ABD1: 心率/呼吸精准, 跌倒一般 #define RADAR_TYPE_ABD1 0 // R60ABD1: 心率/呼吸精准, 跌倒一般
#define RADAR_TYPE_AFD1 1 // R60AFD1: 跌倒/姿态精准, 无心率呼吸 #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 #define RADAR_UART_PORT Serial2
@@ -132,7 +132,7 @@
#define HEART_RATE_MIN 50 #define HEART_RATE_MIN 50
#define HEART_RATE_MAX 120 #define HEART_RATE_MAX 120
#define BREATH_RATE_MIN 10 #define BREATH_RATE_MIN 10
#define BREATH_RATE_MAX 28 #define BREATH_RATE_MAX 26
#define TEMP_LOW 5.0f #define TEMP_LOW 5.0f
#define TEMP_HIGH 45.0f #define TEMP_HIGH 45.0f
#define HUMI_LOW 15.0f #define HUMI_LOW 15.0f
+5
View File
@@ -74,6 +74,7 @@ private:
void _extractSleep(const RadarFrame& frame); void _extractSleep(const RadarFrame& frame);
void _extractWorkStatus(const RadarFrame& frame); void _extractWorkStatus(const RadarFrame& frame);
void _extractDetectRange(const RadarFrame& frame); void _extractDetectRange(const RadarFrame& frame);
void _filterMotion(bool rawMotion); // 运动状态 3 帧去抖
uint8_t _calcChecksum(const uint8_t* data, uint16_t len); uint8_t _calcChecksum(const uint8_t* data, uint16_t len);
void _notifyUpdate(); void _notifyUpdate();
@@ -95,6 +96,10 @@ private:
bool _stablePresence; // 防抖后的稳定有人/无人 bool _stablePresence; // 防抖后的稳定有人/无人
uint8_t _presenceCnt; // 连续一致计数 uint8_t _presenceCnt; // 连续一致计数
bool _lastRawPresence; // 上一次原始值 bool _lastRawPresence; // 上一次原始值
// 运动状态防抖 (体动信号常因小动作抖动, 加 3 帧持续才确认)
uint8_t _motionCnt; // 连续一致计数
bool _lastRawMotion; // 上一次原始值
}; };
#endif // RADAR_MANAGER_H #endif // RADAR_MANAGER_H
+16 -2
View File
@@ -66,12 +66,14 @@ bool g_fallDismissed = false; // 已手动解除, 不重复报警
uint32_t g_vitalLostMs = 0; // 生命体征丢失持续计时 (ms) uint32_t g_vitalLostMs = 0; // 生命体征丢失持续计时 (ms)
bool g_vitalLostActive = false; // 生命体征丢失报警激活 bool g_vitalLostActive = false; // 生命体征丢失报警激活
bool g_vitalLostDismissed = false; // 生命体征丢失解除抑制 bool g_vitalLostDismissed = false; // 生命体征丢失解除抑制
uint32_t g_stillMs = 0; // 持续静默计时 (清醒时即使雷达误判, 持续有体动会重置)
#define ALARM_HYSTERESIS 3 // 连续3次确认才触发 #define ALARM_HYSTERESIS 3 // 连续3次确认才触发
#define FALL_DETECT_MS 10000 // 跌倒持续10秒才触发 (房顶安装, 过滤坐下) #define FALL_DETECT_MS 10000 // 跌倒持续10秒才触发 (房顶安装, 过滤坐下)
#define FALL_CLEAR_MS 18000 // 跌倒消失18秒后才清除 (防误消) #define FALL_CLEAR_MS 18000 // 跌倒消失18秒后才清除 (防误消)
#define VITAL_DETECT_MS 20000 // 心率/呼吸持续异常20秒才触发 #define VITAL_DETECT_MS 20000 // 心率/呼吸持续异常20秒才触发
#define VITAL_CLEAR_MS 15000 // 心率/呼吸持续正常15秒才恢复 #define VITAL_CLEAR_MS 15000 // 心率/呼吸持续正常15秒才恢复
#define VITAL_LOST_MS 60000 // 在床但心率呼吸持续为0达60秒 → 疑似骤停 #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.breathRate = radar.breathRate;
t.body = radar.body; t.body = radar.body;
t.sleepState = radar.sleepState; 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.radarValid = radar.radarValid;
t.vitalValid = radar.vitalValid; t.vitalValid = radar.vitalValid;
+37 -3
View File
@@ -24,6 +24,8 @@ RadarManager::RadarManager()
, _stablePresence(false) , _stablePresence(false)
, _presenceCnt(0) , _presenceCnt(0)
, _lastRawPresence(false) , _lastRawPresence(false)
, _motionCnt(0)
, _lastRawMotion(false)
{ {
memset(&_currentFrame, 0, sizeof(_currentFrame)); memset(&_currentFrame, 0, sizeof(_currentFrame));
memset(&_data, 0, sizeof(_data)); memset(&_data, 0, sizeof(_data));
@@ -241,7 +243,13 @@ void RadarManager::_processCompleteFrame() {
_extractHeartRate(_currentFrame); _extractHeartRate(_currentFrame);
break; break;
case CTRL_FALL: case CTRL_FALL:
#if RADAR_TYPE == RADAR_TYPE_AFD1
_extractFall(_currentFrame); // AFD1 专用跌倒 _extractFall(_currentFrame); // AFD1 专用跌倒
#else
// ABD1 不会发 0x83 帧, 若收到可能是干扰, 直接丢弃
(void)_currentFrame;
#endif
break;
break; break;
case CTRL_SLEEP: case CTRL_SLEEP:
_extractSleep(_currentFrame); _extractSleep(_currentFrame);
@@ -282,7 +290,8 @@ void RadarManager::_extractPresence(const RadarFrame& frame) {
// cmd 0x01 (len=1): 运动状态/体动 (值越大越活跃) // cmd 0x01 (len=1): 运动状态/体动 (值越大越活跃)
else if (frame.command == 0x01 && frame.data_length >= 1) { else if (frame.command == 0x01 && frame.data_length >= 1) {
_data.body = raw; _data.body = raw;
_data.motion = (raw >= 1); bool rawMotion = (raw >= 1);
_filterMotion(rawMotion); // 3 帧防抖
_data.radarValid = true; _data.radarValid = true;
_data.lastUpdate = millis(); _data.lastUpdate = millis();
_notifyUpdate(); _notifyUpdate();
@@ -297,14 +306,16 @@ void RadarManager::_extractPresence(const RadarFrame& frame) {
// cmd 0x04 (len=2): [0]=presence [1]=motion // cmd 0x04 (len=2): [0]=presence [1]=motion
else if (frame.command == 0x04 && frame.data_length >= 2) { else if (frame.command == 0x04 && frame.data_length >= 2) {
rawPresence = (frame.data[0] != 0); rawPresence = (frame.data[0] != 0);
_data.motion = (frame.data[1] == 0); bool rawMotion = (frame.data[1] == 0);
_filterMotion(rawMotion); // 3 帧防抖
_data.radarValid = true; _data.radarValid = true;
_data.lastUpdate = millis(); _data.lastUpdate = millis();
} }
// cmd 0x05 (len=6): 完整包 // cmd 0x05 (len=6): 完整包
else if (frame.command == 0x05 && frame.data_length >= 6) { else if (frame.command == 0x05 && frame.data_length >= 6) {
rawPresence = (frame.data[0] != 0); 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.distance = ((uint16_t)frame.data[2] << 8) | frame.data[3];
_data.body = frame.data[4]; _data.body = frame.data[4];
_data.radarValid = true; _data.radarValid = true;
@@ -386,10 +397,33 @@ void RadarManager::_extractSleep(const RadarFrame& frame) {
} }
if (frame.data_length >= 4) { if (frame.data_length >= 4) {
#if RADAR_TYPE == RADAR_TYPE_AFD1
// AFD1 协议: data[3] 低 2 位 = 跌倒/驻留
_data.fall = (frame.data[3] & 0x01) != 0; _data.fall = (frame.data[3] & 0x01) != 0;
_data.resident = (frame.data[3] & 0x02) != 0; _data.resident = (frame.data[3] & 0x02) != 0;
_data.lastUpdate = millis(); _data.lastUpdate = millis();
_notifyUpdate(); _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;
} }
} }