1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
| """ NXP NCJ29D6 UWB CPD Detection Algorithm Pipeline 基于 NXP 白皮书与 IEEE 802.15.4z 标准 """
import numpy as np from scipy import signal as sig from typing import Tuple
class UWBCPD_Detector: """ UWB 儿童存在检测器 核心原理: 1. 发射 UWB 脉冲(~2ns 宽度),接收反射信号 2. 对反射信号做距离-多普勒分析 3. 提取微动信号(呼吸频率 0.1-0.5 Hz, 心跳 0.8-2.0 Hz) 4. 与环境基线对比,判定是否有人/动物 参考:NXP White Paper "UWB Radar for Child Presence Detection" """ def __init__(self, config: dict): self.fc = config.get('center_freq', 7.0e9) self.bw = config.get('bandwidth', 500e6) self.pulse_width = 1 / self.bw self.sample_rate = config.get('sample_rate', 1e9) self.range_resolution = 3e8 / (2 * self.bw) self.breath_range = (0.1, 0.5) self.heart_range = (0.8, 2.0) self.detection_threshold = config.get('threshold', 0.35) self.history_frames = config.get('history', 300) def generate_uwb_pulse(self, n_samples: int = 256) -> np.ndarray: """生成 UWB 发射脉冲""" t = np.arange(n_samples) / self.sample_rate pulse = np.zeros(n_samples) sigma = self.pulse_width / (2 * np.sqrt(2 * np.log(2))) center = n_samples // 2 x = (t - t[center]) / sigma pulse = x * (4 * x**4 - 12 * x**2 + 3) * np.exp(-x**2) return pulse / np.max(np.abs(pulse)) def range_profile(self, rx_signal: np.ndarray, tx_pulse: np.ndarray) -> np.ndarray: """ 距离剖面估计(匹配滤波) Args: rx_signal: 接收信号, shape=(N,) tx_pulse: 发射脉冲, shape=(M,) Returns: range_profile: 距离剖面, shape=(N-M+1,) """ range_prof = np.correlate(rx_signal, tx_pulse, mode='full') range_prof = range_prof[len(tx_pulse)-1:] analytic = sig.hilbert(range_prof) envelope = np.abs(analytic) return envelope def extract_micro_motion(self, range_profiles: np.ndarray, target_range_bin: int) -> np.ndarray: """ 从距离剖面序列中提取微动信号 Args: range_profiles: 距离剖面序列, shape=(T, N_range) target_range_bin: 目标距离单元索引 Returns: micro_motion: 微动信号时序, shape=(T,) """ phase_series = np.angle(range_profiles[:, target_range_bin]) phase_diff = np.diff(phase_series) phase_diff = np.mod(phase_diff + np.pi, 2 * np.pi) - np.pi nyquist = 0.5 * 10 bp_breath = sig.butter(2, [self.breath_range[0]/nyquist, self.breath_range[1]/nyquist], btype='band') breath_signal = sig.filtfilt(*bp_breath, phase_diff) return breath_signal def detect_presence(self, micro_motion: np.ndarray, baseline_noise: float) -> Tuple[bool, float]: """ 判定座舱内是否存在生命体 Args: micro_motion: 微动信号时序 baseline_noise: 环境基线噪声水平 Returns: is_present: 是否检测到存在 confidence: 置信度 0-1 """ freqs = np.fft.rfftfreq(len(micro_motion), d=0.1) spectrum = np.abs(np.fft.rfft(micro_motion)) breath_mask = (freqs >= self.breath_range[0]) & (freqs <= self.breath_range[1]) breath_power = np.sum(spectrum[breath_mask]) heart_mask = (freqs >= self.heart_range[0]) & (freqs <= self.heart_range[1]) heart_power = np.sum(spectrum[heart_mask]) total_power = breath_power + 0.5 * heart_power snr = total_power / (baseline_noise + 1e-10) is_present = snr > self.detection_threshold confidence = min(1.0, snr / (self.detection_threshold * 2)) return is_present, confidence
if __name__ == "__main__": detector = UWBCPD_Detector({'threshold': 0.35, 'sample_rate': 1e9}) tx = detector.generate_uwb_pulse(256) print(f"UWB Pulse: {len(tx)} samples, peak={np.max(tx):.3f}") print(f"Range Resolution: {detector.range_resolution*100:.1f} cm") t = np.arange(300) * 0.1 breath = 0.5 * np.sin(2 * np.pi * 0.3 * t) heart = 0.2 * np.sin(2 * np.pi * 1.2 * t) noise = 0.1 * np.random.randn(300) micro_motion = breath + heart + noise is_present, conf = detector.detect_presence(micro_motion, baseline_noise=0.01) print(f"Presence Detected: {is_present}") print(f"Confidence: {conf:.2%}") empty_motion = 0.05 * np.random.randn(300) is_present2, conf2 = detector.detect_presence(empty_motion, baseline_noise=0.01) print(f"Empty Cabin Detected: {is_present2} (should be False)") print(f"Empty Confidence: {conf2:.2%}")
|