优化雷达信号处理,添加心率和呼吸率平滑处理和异常值检测
This commit is contained in:
36
src/Radar.h
36
src/Radar.h
@@ -1,36 +0,0 @@
|
||||
#ifndef RADAR_H
|
||||
#define RADAR_H
|
||||
// 在main.cpp顶部添加R60ABD1协议相关定义
|
||||
#define FRAME_HEADER1 0x53 // 帧头字节1
|
||||
#define FRAME_HEADER2 0x59 // 帧头字节2
|
||||
#define FRAME_TAIL1 0x54 // 帧尾字节1
|
||||
#define FRAME_TAIL2 0x43 // 帧尾字节2
|
||||
|
||||
// 控制字定义
|
||||
#define CTRL_PRESENCE 0x80 // 人体存在检测
|
||||
#define CTRL_BREATH 0x81 // 呼吸检测
|
||||
#define CTRL_SLEEP 0x84 // 睡眠监测
|
||||
#define CTRL_HEARTRATE 0x85 // 心率监测
|
||||
|
||||
// 命令字定义
|
||||
#define CMD_REPORT 0x80 // 主动上报
|
||||
#define CMD_QUERY 0x81 // 查询命令
|
||||
#define CMD_SET 0x82 // 设置命令
|
||||
|
||||
// 定义R60ABD1数据结构
|
||||
typedef struct {
|
||||
uint8_t present; // 有人/无人状态 (DP1)
|
||||
uint16_t distance; // 人体距离 (DP3) 单位cm
|
||||
uint8_t heartRate; // 心率 (DP6) 单位BPM
|
||||
uint8_t breathRate; // 呼吸率 (DP8) 单位次/分钟
|
||||
uint8_t heartWave; // 心率波形 (DP7) 数值+128
|
||||
uint8_t breathWave; // 呼吸波形 (DP10) 数值+128
|
||||
uint8_t sleepState; // 睡眠状态 (DP12)
|
||||
uint32_t sleepTime; // 睡眠时长 (DP13) 单位秒
|
||||
uint8_t sleepScore; // 睡眠质量评分 (DP14)
|
||||
uint8_t bedEntry; // 入床/离床状态 (DP11)
|
||||
uint8_t abnormal; // 异常状态 (DP18)
|
||||
} R60ABD1Data;
|
||||
|
||||
|
||||
#endif
|
||||
288
src/io_flash.cpp
Normal file
288
src/io_flash.cpp
Normal file
@@ -0,0 +1,288 @@
|
||||
#include "io_flash.h"
|
||||
#include <esp_task_wdt.h>
|
||||
|
||||
// 全局变量定义
|
||||
Preferences preferences; // 声明preferences变量
|
||||
uint16_t currentDeviceId; // 当前设备ID
|
||||
bool clearConfigRequested = false; // 清除配置请求标志
|
||||
bool forceLedOff = false; // 强制关闭LED标志
|
||||
ConfigClearStatus currentConfigClearStatus = CONFIG_NORMAL; // 当前配置清除状态
|
||||
|
||||
// 常量定义
|
||||
const int SLOW_BLINK_INTERVAL = 1000; // 慢闪间隔(毫秒)
|
||||
const int FAST_BLINK_INTERVAL = 200; // 快闪间隔(毫秒)
|
||||
const int BREATHE_INTERVAL = 40; // 呼吸灯更新间隔(毫秒)
|
||||
const int BREATHE_MIN = 0; // 呼吸灯最小亮度值
|
||||
const int BREATHE_MAX = 155; // 呼吸灯最大亮度值
|
||||
const int BREATHE_STEP = 5; // 呼吸灯亮度步进值
|
||||
|
||||
const unsigned long CLEAR_CONFIG_DURATION = 3000; // 清除配置持续时间(毫秒)
|
||||
|
||||
/**
|
||||
* @brief 检查Boot按钮状态
|
||||
* 在启动时检查Boot按钮是否被按下,如果按下则进入配置清除流程
|
||||
*/
|
||||
void checkBootButton() {
|
||||
Serial.println("🔍 检查Boot按钮状态...");
|
||||
|
||||
pinMode(BOOT_BUTTON_PIN, INPUT_PULLUP);
|
||||
|
||||
delay(10);
|
||||
|
||||
int buttonState = digitalRead(BOOT_BUTTON_PIN);
|
||||
Serial.printf("📊 Boot按钮状态: %s\n", buttonState == LOW ? "按下" : "释放");
|
||||
|
||||
if (buttonState == LOW) {
|
||||
Serial.println("⚠️ 检测到Boot按钮按下,请释放按钮后继续启动");
|
||||
Serial.println("⏰ 等待按钮释放...");
|
||||
|
||||
while (digitalRead(BOOT_BUTTON_PIN) == LOW) {
|
||||
delay(100);
|
||||
}
|
||||
|
||||
Serial.println("✅ Boot按钮已释放,正常启动");
|
||||
} else {
|
||||
Serial.println("✅ Boot按钮未按下,正常启动");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 加载设备ID
|
||||
* 从Flash中读取保存的设备ID
|
||||
*/
|
||||
void loadDeviceId() {
|
||||
currentDeviceId = preferences.getUShort("deviceId", 1001);
|
||||
Serial.printf("从Flash加载设备ID: %u\n", currentDeviceId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 保存设备ID
|
||||
* 将设备ID保存到Flash中
|
||||
*/
|
||||
void saveDeviceId() {
|
||||
preferences.putUShort("deviceId", currentDeviceId);
|
||||
Serial.printf("设备ID已保存到Flash: %u\n", currentDeviceId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 清除存储的配置
|
||||
* 清除Flash中保存的所有配置,包括设备ID
|
||||
*/
|
||||
void clearStoredConfig() {
|
||||
Serial.println("🧹 开始清除存储的配置...");
|
||||
|
||||
uint16_t oldDeviceId = preferences.getUShort("deviceId", 0);
|
||||
|
||||
preferences.remove("deviceId");
|
||||
|
||||
Serial.println("✅ 配置已清除完成");
|
||||
Serial.printf("🗑️ 被清除的设备ID: %u\n", oldDeviceId);
|
||||
|
||||
currentDeviceId = 1001;
|
||||
|
||||
Serial.println("🔄 已清除Flash与内存中的配置");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief 配置清除LED控制任务
|
||||
* 根据配置清除状态控制CONFIG_CLEAR_PIN引脚的LED显示
|
||||
* @param parameter 任务参数(未使用)
|
||||
*/
|
||||
void configClearLedTask(void *parameter) {
|
||||
unsigned long lastConfigBlinkTime = 0; // 上次配置清除LED闪烁时间
|
||||
bool configLedState = false; // 配置清除LED状态
|
||||
int configBreatheValue = 0; // 配置清除呼吸灯当前亮度值
|
||||
bool configBreatheIncreasing = true; // 配置清除呼吸灯是否在增加亮度
|
||||
|
||||
while (1) {
|
||||
switch (currentConfigClearStatus) {
|
||||
case CONFIG_NORMAL:
|
||||
analogWrite(CONFIG_CLEAR_PIN, 0);
|
||||
break;
|
||||
|
||||
case CONFIG_PREPARING:
|
||||
analogWrite(CONFIG_CLEAR_PIN, 255);
|
||||
break;
|
||||
|
||||
case CONFIG_CLEARING:
|
||||
if (millis() - lastConfigBlinkTime >= BREATHE_INTERVAL) {
|
||||
analogWrite(CONFIG_CLEAR_PIN, configBreatheValue);
|
||||
|
||||
if (configBreatheIncreasing) {
|
||||
configBreatheValue += 5;
|
||||
if (configBreatheValue >= BREATHE_MAX) {
|
||||
configBreatheValue = BREATHE_MAX;
|
||||
configBreatheIncreasing = false;
|
||||
}
|
||||
} else {
|
||||
configBreatheValue -= 5;
|
||||
if (configBreatheValue <= BREATHE_MIN) {
|
||||
configBreatheValue = BREATHE_MIN;
|
||||
configBreatheIncreasing = true;
|
||||
}
|
||||
}
|
||||
lastConfigBlinkTime = millis();
|
||||
}
|
||||
break;
|
||||
|
||||
case CONFIG_COMPLETED:
|
||||
if (millis() - lastConfigBlinkTime >= FAST_BLINK_INTERVAL) {
|
||||
configLedState = !configLedState;
|
||||
digitalWrite(CONFIG_CLEAR_PIN, configLedState ? HIGH : LOW);
|
||||
lastConfigBlinkTime = millis();
|
||||
|
||||
static int blinkCount = 0;
|
||||
blinkCount++;
|
||||
|
||||
if (blinkCount >= 6) {
|
||||
blinkCount = 0;
|
||||
currentConfigClearStatus = CONFIG_NORMAL;
|
||||
digitalWrite(CONFIG_CLEAR_PIN, LOW);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
vTaskDelay(10 / portTICK_PERIOD_MS);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief BOOT按钮监控任务
|
||||
* 持续监控BOOT按钮状态,检测长按3秒事件并触发配置清除
|
||||
* @param parameter 任务参数(未使用)
|
||||
*/
|
||||
void bootButtonMonitorTask(void *parameter) {
|
||||
Serial.println("🔍 启动BOOT按钮监控任务...");
|
||||
|
||||
pinMode(BOOT_BUTTON_PIN, INPUT_PULLUP);
|
||||
|
||||
unsigned long buttonPressStartTime = 0;
|
||||
bool buttonPressed = false;
|
||||
|
||||
while (1) {
|
||||
int buttonState = digitalRead(BOOT_BUTTON_PIN);
|
||||
|
||||
if (buttonState == LOW && !buttonPressed) {
|
||||
buttonPressed = true;
|
||||
buttonPressStartTime = millis();
|
||||
Serial.println("⚠️ 检测到BOOT按钮按下,长按3秒将清除配置");
|
||||
|
||||
currentConfigClearStatus = CONFIG_PREPARING;
|
||||
}
|
||||
else if (buttonState == HIGH && buttonPressed) {
|
||||
if (!clearConfigRequested) {
|
||||
currentConfigClearStatus = CONFIG_NORMAL;
|
||||
Serial.println("❌ 按钮释放,取消清除操作");
|
||||
}
|
||||
buttonPressed = false;
|
||||
}
|
||||
|
||||
if (buttonPressed && (millis() - buttonPressStartTime >= CLEAR_CONFIG_DURATION)) {
|
||||
if (!clearConfigRequested) {
|
||||
clearConfigRequested = true;
|
||||
forceLedOff = true;
|
||||
ledcWrite(0, 0);
|
||||
Serial.println("✅ 长按3秒确认,将清除配置");
|
||||
Serial.println("💡 网络LED已强制熄灭");
|
||||
|
||||
clearStoredConfig();
|
||||
|
||||
Serial.println("🔄 配置清除完成,LED将闪烁3次表示完成...");
|
||||
|
||||
analogWrite(CONFIG_CLEAR_PIN, 0);
|
||||
Serial.println("🔄 系统即将重启...");
|
||||
|
||||
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||
ESP.restart();
|
||||
}
|
||||
}
|
||||
|
||||
vTaskDelay(50 / portTICK_PERIOD_MS);
|
||||
|
||||
esp_task_wdt_reset();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief LED控制任务
|
||||
* 控制NETWORK_LED_PIN引脚的LED显示,保持常亮
|
||||
* @param parameter 任务参数(未使用)
|
||||
*/
|
||||
void ledControlTask(void *parameter) {
|
||||
while (1) {
|
||||
if (forceLedOff) {
|
||||
ledcWrite(0, 0);
|
||||
} else {
|
||||
ledcWrite(0, 255); // 保持LED常亮
|
||||
}
|
||||
vTaskDelay(100 / portTICK_PERIOD_MS);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief IO和Flash初始化函数
|
||||
* 初始化IO引脚、Flash存储和LED控制
|
||||
*/
|
||||
void io_flash_init() {
|
||||
// 初始化IO引脚
|
||||
pinMode(BOOT_BUTTON_PIN, INPUT);
|
||||
pinMode(NETWORK_LED_PIN, OUTPUT);
|
||||
pinMode(CONFIG_CLEAR_PIN, OUTPUT);
|
||||
pinMode(GPIO8, OUTPUT);
|
||||
pinMode(GPIO9, OUTPUT);
|
||||
pinMode(Radar_Start, OUTPUT); // 初始化雷达启动引脚
|
||||
|
||||
// 设置初始状态
|
||||
digitalWrite(CONFIG_CLEAR_PIN, LOW);
|
||||
digitalWrite(GPIO8, LOW);
|
||||
digitalWrite(GPIO9, LOW);
|
||||
digitalWrite(NETWORK_LED_PIN, LOW);
|
||||
digitalWrite(CONFIG_CLEAR_PIN, LOW);
|
||||
digitalWrite(Radar_Start, HIGH); // 拉高雷达启动引脚,启动雷达
|
||||
|
||||
// 初始化LED控制
|
||||
ledcSetup(0, 5000, 8);
|
||||
ledcSetup(1, 5000, 8);
|
||||
ledcAttachPin(NETWORK_LED_PIN, 0);
|
||||
ledcAttachPin(CONFIG_CLEAR_PIN, 1);
|
||||
|
||||
// 初始化Flash存储
|
||||
preferences.begin("radar_data", false);
|
||||
|
||||
// 加载设备配置
|
||||
Serial.println("💾 加载设备配置...");
|
||||
loadDeviceId();
|
||||
|
||||
// 初始化任务
|
||||
xTaskCreate(
|
||||
configClearLedTask,
|
||||
"Config Clear LED Task",
|
||||
2048,
|
||||
NULL,
|
||||
1,
|
||||
NULL
|
||||
);
|
||||
|
||||
xTaskCreate(
|
||||
bootButtonMonitorTask,
|
||||
"Boot Button Monitor Task",
|
||||
2048,
|
||||
NULL,
|
||||
1,
|
||||
NULL
|
||||
);
|
||||
|
||||
xTaskCreate(
|
||||
ledControlTask,
|
||||
"LED Control Task",
|
||||
2048,
|
||||
NULL,
|
||||
1,
|
||||
NULL
|
||||
);
|
||||
}
|
||||
42
src/io_flash.h
Normal file
42
src/io_flash.h
Normal file
@@ -0,0 +1,42 @@
|
||||
#ifndef IO_FLASH_H
|
||||
#define IO_FLASH_H
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <Preferences.h>
|
||||
|
||||
// 引脚定义
|
||||
#define BOOT_BUTTON_PIN 0 // Boot按钮引脚
|
||||
#define NETWORK_LED_PIN 35 // 网络状态LED指示灯开发板48引脚,雷达板35引脚
|
||||
#define CONFIG_CLEAR_PIN 4 // 配置清除指示灯
|
||||
#define Radar_Start 36 // 定义雷达启动引脚拉高启动
|
||||
#define GPIO8 8 // 自定义GPIO8
|
||||
#define GPIO9 9 // 自定义GPIO9
|
||||
|
||||
// 配置清除指示灯状态枚举
|
||||
enum ConfigClearStatus {
|
||||
CONFIG_NORMAL, // 正常运行 - LOW
|
||||
CONFIG_PREPARING, // 准备清除 - HIGH
|
||||
CONFIG_CLEARING, // 清除过程中 - 呼吸灯
|
||||
CONFIG_COMPLETED // 清除完成 - 快速闪烁3次
|
||||
};
|
||||
|
||||
// 全局变量声明
|
||||
extern Preferences preferences;
|
||||
extern uint16_t currentDeviceId; // 当前设备ID
|
||||
extern bool clearConfigRequested; // 清除配置请求标志
|
||||
extern bool forceLedOff; // 强制关闭LED标志
|
||||
extern ConfigClearStatus currentConfigClearStatus; // 当前配置清除状态
|
||||
|
||||
// 函数声明
|
||||
void checkBootButton();
|
||||
void loadDeviceId();
|
||||
void saveDeviceId();
|
||||
void clearStoredConfig();
|
||||
void io_flash_init();
|
||||
|
||||
// 任务声明
|
||||
void configClearLedTask(void *parameter);
|
||||
void bootButtonMonitorTask(void *parameter);
|
||||
void ledControlTask(void *parameter);
|
||||
|
||||
#endif // IO_FLASH_H
|
||||
2750
src/main.cpp
2750
src/main.cpp
File diff suppressed because it is too large
Load Diff
200
src/radar_vitals.cpp
Normal file
200
src/radar_vitals.cpp
Normal file
@@ -0,0 +1,200 @@
|
||||
#include "radar_vitals.h"
|
||||
#include <math.h>
|
||||
#include <driver/adc.h>
|
||||
|
||||
/******** 参数 ********/
|
||||
#define ADC_MAX 4095.0f
|
||||
#define VREF 3.3f
|
||||
#define TWO_PI 6.2831853f
|
||||
#define RADAR_LAMBDA 0.0125f // 24GHz 波长(米)
|
||||
|
||||
/******** 内部状态 ********/
|
||||
static float fs = 20.0f;
|
||||
|
||||
// 状态变量
|
||||
static float phase[DATA_LEN];
|
||||
static float unwrap_phase[DATA_LEN];
|
||||
static human_state_t human_state = NO_PERSON;
|
||||
static float resp_bpm = 0;
|
||||
static float heart_bpm = 0;
|
||||
|
||||
// 平滑处理相关变量
|
||||
static float resp_bpm_history[10] = {0};
|
||||
static float heart_bpm_history[10] = {0};
|
||||
static int history_index = 0;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/******** 新的雷达处理函数 ********/
|
||||
void radar_process(float *I, float *Q, int len)
|
||||
{
|
||||
/* 1. 去直流 (DC Removal)
|
||||
* I'(n) = I(n) - mean(I)
|
||||
* Q'(n) = Q(n) - mean(Q)
|
||||
*/
|
||||
float meanI = 0, meanQ = 0;
|
||||
for(int i=0;i<len;i++){ meanI += I[i]; meanQ += Q[i]; }
|
||||
meanI /= len; meanQ /= len;
|
||||
for(int i=0;i<len;i++){ I[i] -= meanI; Q[i] -= meanQ; }
|
||||
|
||||
/* 2. IQ → 相位
|
||||
* φ(n) = atan2(Q, I)
|
||||
*/
|
||||
for(int i=0;i<len;i++){
|
||||
phase[i] = atan2f(Q[i], I[i]);
|
||||
}
|
||||
|
||||
/* 3. 相位展开 (Unwrap)
|
||||
* Δφ > π → Δφ -= 2π
|
||||
*/
|
||||
unwrap_phase[0] = phase[0];
|
||||
for(int i=1;i<len;i++){
|
||||
float d = phase[i] - phase[i-1];
|
||||
if(d > M_PI) d -= 2*M_PI;
|
||||
if(d < -M_PI) d += 2*M_PI;
|
||||
unwrap_phase[i] = unwrap_phase[i-1] + d;
|
||||
}
|
||||
|
||||
/* 4. 空人判断:相位方差
|
||||
* Var(φ) = E[(φ-μ)^2]
|
||||
*/
|
||||
float mean = 0, var = 0;
|
||||
for(int i=0;i<len;i++) mean += unwrap_phase[i];
|
||||
mean /= len;
|
||||
for(int i=0;i<len;i++){
|
||||
float d = unwrap_phase[i] - mean;
|
||||
var += d * d;
|
||||
}
|
||||
var /= len;
|
||||
|
||||
if(var < 1e-4){
|
||||
human_state = NO_PERSON;
|
||||
return;
|
||||
}
|
||||
|
||||
/* 5. 活动检测:相位差分能量
|
||||
* E = mean[(φ(n)-φ(n-1))^2]
|
||||
*/
|
||||
float energy = 0;
|
||||
for(int i=1;i<len;i++){
|
||||
float d = unwrap_phase[i] - unwrap_phase[i-1];
|
||||
energy += d * d;
|
||||
}
|
||||
energy /= len;
|
||||
|
||||
if(energy > 0.05){
|
||||
human_state = MOTION;
|
||||
} else {
|
||||
human_state = STATIC_HUMAN;
|
||||
}
|
||||
|
||||
/* 6. 呼吸 / 心率频率估计
|
||||
* 方法:频段能量最大值搜索(简化 DFT)
|
||||
* Fs_eff = SAMPLE_RATE / decimation
|
||||
*/
|
||||
const float Fs_eff = 200.0f; // 4kHz / 20
|
||||
|
||||
// ---- 呼吸:0.1 ~ 0.5 Hz ----
|
||||
float max_resp_mag = 0; float resp_freq = 0;
|
||||
for(float f=0.1f; f<=0.5f; f+=0.02f){
|
||||
float re=0, im=0;
|
||||
for(int n=0;n<len;n++){
|
||||
float ang = 2*M_PI*f*n/Fs_eff;
|
||||
re += unwrap_phase[n]*cosf(ang);
|
||||
im -= unwrap_phase[n]*sinf(ang);
|
||||
}
|
||||
float mag = re*re + im*im;
|
||||
if(mag > max_resp_mag){ max_resp_mag = mag; resp_freq = f; }
|
||||
}
|
||||
float new_resp_bpm = resp_freq * 60.0f;
|
||||
|
||||
// ---- 心率:0.8 ~ 2.5 Hz ----
|
||||
float max_hr_mag = 0; float hr_freq = 0;
|
||||
for(float f=0.8f; f<=2.5f; f+=0.05f){
|
||||
float re=0, im=0;
|
||||
for(int n=0;n<len;n++){
|
||||
float ang = 2*M_PI*f*n/Fs_eff;
|
||||
re += unwrap_phase[n]*cosf(ang);
|
||||
im -= unwrap_phase[n]*sinf(ang);
|
||||
}
|
||||
float mag = re*re + im*im;
|
||||
if(mag > max_hr_mag){ max_hr_mag = mag; hr_freq = f; }
|
||||
}
|
||||
float new_heart_bpm = hr_freq * 60.0f;
|
||||
|
||||
// 异常值检测和过滤
|
||||
if(new_heart_bpm < 40 || new_heart_bpm > 180){
|
||||
new_heart_bpm = heart_bpm; // 使用上次值
|
||||
}
|
||||
if(new_resp_bpm < 4 || new_resp_bpm > 40){
|
||||
new_resp_bpm = resp_bpm; // 使用上次值
|
||||
}
|
||||
|
||||
// 移动平均滤波
|
||||
heart_bpm_history[history_index] = new_heart_bpm;
|
||||
resp_bpm_history[history_index] = new_resp_bpm;
|
||||
history_index = (history_index + 1) % 10;
|
||||
|
||||
// 计算平均值
|
||||
float sum_hr = 0, sum_resp = 0;
|
||||
for(int i=0; i<10; i++){
|
||||
sum_hr += heart_bpm_history[i];
|
||||
sum_resp += resp_bpm_history[i];
|
||||
}
|
||||
heart_bpm = sum_hr / 10.0f;
|
||||
resp_bpm = sum_resp / 10.0f;
|
||||
|
||||
// 四舍五入到最接近的整数
|
||||
heart_bpm = roundf(heart_bpm);
|
||||
resp_bpm = roundf(resp_bpm);
|
||||
}
|
||||
|
||||
|
||||
/******** 获取人体状态 ********/
|
||||
human_state_t radar_get_state(){ return human_state; }
|
||||
|
||||
/******** 获取呼吸率 ********/
|
||||
float radar_get_resp_bpm(){ return resp_bpm; }
|
||||
|
||||
/******** 获取心率 ********/
|
||||
float radar_get_heart_bpm(){ return heart_bpm; }
|
||||
|
||||
/******** 初始化 ********/
|
||||
void radar_vitals_init(float fs_hz)
|
||||
{
|
||||
fs = fs_hz;
|
||||
human_state = NO_PERSON;
|
||||
resp_bpm = 0;
|
||||
heart_bpm = 0;
|
||||
|
||||
// 初始化历史数据数组
|
||||
for(int i=0; i<10; i++){
|
||||
resp_bpm_history[i] = 0;
|
||||
heart_bpm_history[i] = 0;
|
||||
}
|
||||
history_index = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 雷达初始化函数
|
||||
* 初始化雷达相关的ADC和引脚
|
||||
*/
|
||||
void radar_init() {
|
||||
// 配置雷达I/Q引脚为ADC输入
|
||||
pinMode(PIN_I, INPUT);
|
||||
pinMode(PIN_Q, INPUT);
|
||||
|
||||
// 初始化ADC
|
||||
adc1_config_width(ADC_WIDTH_BIT_12);
|
||||
adc1_config_channel_atten(ADC1_CHANNEL_0, ADC_ATTEN_DB_12); // PIN_I (GPIO1) 对应 ADC1_CHANNEL_0
|
||||
adc1_config_channel_atten(ADC1_CHANNEL_1, ADC_ATTEN_DB_12); // PIN_Q (GPIO2) 对应 ADC1_CHANNEL_1
|
||||
|
||||
Serial.println("🏗️ 初始化雷达管理器...");
|
||||
radar_vitals_init(SAMPLE_RATE);
|
||||
}
|
||||
|
||||
|
||||
51
src/radar_vitals.h
Normal file
51
src/radar_vitals.h
Normal file
@@ -0,0 +1,51 @@
|
||||
#ifndef RADAR_VITALS_H
|
||||
#define RADAR_VITALS_H
|
||||
#include <stdint.h>
|
||||
#include <Arduino.h>
|
||||
|
||||
#define PIN_I 9
|
||||
#define PIN_Q 10
|
||||
|
||||
#define ADC_WIDTH ADC_WIDTH_BIT_12 // 配置ADC宽度为12位
|
||||
#define ADC_ATTEN ADC_ATTEN_DB_12 // 配置ADC衰减为12dB
|
||||
|
||||
|
||||
|
||||
#define SAMPLE_RATE 4000 // 基础采样率(Hz)
|
||||
#define DATA_LEN 256 // 数据长度
|
||||
|
||||
// 人体状态枚举
|
||||
typedef enum {
|
||||
NO_PERSON = 0,
|
||||
STATIC_HUMAN,
|
||||
MOTION
|
||||
} human_state_t;
|
||||
|
||||
/******** 雷达采样 ********/
|
||||
typedef struct {
|
||||
uint16_t i_value; // ADC 原始值
|
||||
uint16_t q_value; // ADC 原始值
|
||||
uint32_t timestamp; // ms
|
||||
} radar_sample_t;
|
||||
|
||||
/******** 输出结果 ********/
|
||||
typedef struct {
|
||||
float heart_bpm;
|
||||
float breath_bpm;
|
||||
uint8_t valid; // 1: 有效
|
||||
human_state_t state; // 人体状态
|
||||
} radar_vitals_t;
|
||||
|
||||
/******** 接口 ********/
|
||||
void radar_vitals_init(float fs_hz);
|
||||
void radar_vitals_process(
|
||||
const radar_sample_t *sample,
|
||||
radar_vitals_t *out
|
||||
);
|
||||
void radar_process(float *I, float *Q, int len);
|
||||
human_state_t radar_get_state();
|
||||
float radar_get_resp_bpm();
|
||||
float radar_get_heart_bpm();
|
||||
void radar_init();
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user