feat:增加跌倒防抖帧
fix:修复开机错误跌倒报警。
This commit is contained in:
+1
-1
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
+1
-1
@@ -42,4 +42,4 @@ lib_deps =
|
||||
|
||||
; 上传配置
|
||||
upload_speed = 921600
|
||||
upload_port = COM20
|
||||
upload_port = COM19
|
||||
|
||||
+27
-22
@@ -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) {
|
||||
|
||||
+20
-2
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user