fix:resident 字段修复(误伤了,重新放开)
feat:增加防抖功能。
This commit is contained in:
+2
-2
@@ -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
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user