60GHz毫米波雷达车内感知技术深度解析

60GHz毫米波雷达车内感知技术深度解析

技术背景

为什么选择60GHz?

特性 24GHz 60GHz 77GHz
分辨率 最高
穿透性
天线尺寸
监管 简单 明确 复杂
成本
车内应用 ❌ 不适合 ✅ 最佳 ⚠️ 成本高

60GHz车内应用

应用 功能 Euro NCAP要求
CPD儿童检测 检测呼吸、心跳 2026强制 ✅
乘员计数 统计车内人数 评分加分
姿态估计 检测异常姿态 2026新增
生命体征 呼吸、心率监测 额外加分

技术原理

1. FMCW雷达基础

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
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
"""
60GHz FMCW雷达信号处理

基本原理:
1. 发射线性调频连续波
2. 接收反射信号
3. 混频得到中频信号
4. FFT得到距离-多普勒图
5. CFAR检测目标
6. 估计生命体征

"""

import numpy as np
from typing import Dict, List, Tuple, Optional
from dataclasses import dataclass
import scipy.signal as signal
import scipy.fft as fft


@dataclass
class RadarConfig:
"""雷达配置"""
# 射频参数
center_freq: float = 60e9 # 中心频率 60GHz
bandwidth: float = 4e9 # 带宽 4GHz
chirp_duration: float = 100e-6 # Chirp时长 100μs
sampling_rate: float = 2e6 # 采样率 2MHz

# 天线配置
num_tx: int = 4 # 发射天线数
num_rx: int = 4 # 接收天线数

# 帧配置
num_chirps: int = 64 # 每帧Chirp数
num_samples: int = 256 # 每Chirp采样点


@dataclass
class DetectedObject:
"""检测到的目标"""
range_m: float # 距离(米)
velocity_ms: float # 速度(米/秒)
angle_deg: float # 角度(度)
snr_db: float # 信噪比
rcs_dbsm: float # 雷达截面积
is_human: bool # 是否为人
breath_rate: Optional[float] # 呼吸频率
heart_rate: Optional[float] # 心率


class FMCWRadarProcessor:
"""FMCW雷达信号处理器"""

def __init__(self, config: RadarConfig):
self.config = config

# 计算派生参数
self.c = 3e8 # 光速
self.wavelength = self.c / config.center_freq
self.range_resolution = self.c / (2 * config.bandwidth)
self.velocity_resolution = self.wavelength / (2 * config.num_chirps * config.chirp_duration)
self.max_range = config.num_samples * self.range_resolution / 2
self.max_velocity = self.wavelength / (4 * config.chirp_duration)

# CFAR参数
self.cfar_guard_cells = 4
self.cfar_training_cells = 8
self.cfar_threshold = 10.0 # dB

def process_frame(
self,
adc_data: np.ndarray # [num_rx, num_chirps, num_samples]
) -> List[DetectedObject]:
"""
处理一帧数据

Args:
adc_data: ADC数据

Returns:
objects: 检测到的目标列表
"""
# 1. Range FFT
range_fft = self._range_fft(adc_data)

# 2. Doppler FFT
range_doppler = self._doppler_fft(range_fft)

# 3. CFAR检测
detections = self._cfar_detection(range_doppler)

# 4. 角度估计
objects = []
for det in detections:
angle = self._estimate_angle(range_fft, det['range_idx'], det['doppler_idx'])

# 计算物理量
range_m = det['range_idx'] * self.range_resolution
velocity_ms = (det['doppler_idx'] - self.config.num_chirps // 2) * self.velocity_resolution

# 生命体征估计
breath_rate, heart_rate = self._estimate_vital_signs(
adc_data, det['range_idx'], det['doppler_idx']
)

objects.append(DetectedObject(
range_m=range_m,
velocity_ms=velocity_ms,
angle_deg=angle,
snr_db=det['snr'],
rcs_dbsm=det['rcs'],
is_human=breath_rate is not None,
breath_rate=breath_rate,
heart_rate=heart_rate
))

return objects

def _range_fft(self, adc_data: np.ndarray) -> np.ndarray:
"""Range FFT"""
num_rx, num_chirps, num_samples = adc_data.shape

# 对每个接收通道、每个Chirp做FFT
range_fft = fft.fft(adc_data, n=self.config.num_samples, axis=2)

return range_fft

def _doppler_fft(self, range_fft: np.ndarray) -> np.ndarray:
"""Doppler FFT"""
# 对每个接收通道、每个Range Bin做FFT
doppler_fft = fft.fftshift(
fft.fft(range_fft, n=self.config.num_chirps, axis=1),
axes=1
)

# 取模
range_doppler = np.abs(doppler_fft).mean(axis=0) # 平均所有接收通道

return range_doppler

def _cfar_detection(
self,
range_doppler: np.ndarray
) -> List[Dict]:
"""CFAR目标检测"""
detections = []

num_range_bins = range_doppler.shape[0]
num_doppler_bins = range_doppler.shape[1]

# 对每个Range-Doppler单元做CA-CFAR
for r in range(self.cfar_guard_cells + self.cfar_training_cells,
num_range_bins - self.cfar_guard_cells - self.cfar_training_cells):
for d in range(self.cfar_guard_cells + self.cfar_training_cells,
num_doppler_bins - self.cfar_guard_cells - self.cfar_training_cells):

# CUT (Cell Under Test)
cut_value = range_doppler[r, d]

# 训练单元
training_cells = []
for tr in range(-self.cfar_training_cells - self.cfar_guard_cells,
self.cfar_training_cells + self.cfar_guard_cells + 1):
for td in range(-self.cfar_training_cells - self.cfar_guard_cells,
self.cfar_training_cells + self.cfar_guard_cells + 1):
if abs(tr) > self.cfar_guard_cells or abs(td) > self.cfar_guard_cells:
training_cells.append(range_doppler[r + tr, d + td])

# 噪声估计
noise_level = np.mean(training_cells)

# 检测阈值
threshold = noise_level * (10 ** (self.cfar_threshold / 10))

if cut_value > threshold:
snr = 10 * np.log10(cut_value / noise_level)

detections.append({
'range_idx': r,
'doppler_idx': d,
'snr': snr,
'rcs': snr + 20 * np.log10(range_m) - 40 # 简化RCS估计
})

return detections

def _estimate_angle(
self,
range_fft: np.ndarray,
range_idx: int,
doppler_idx: int
) -> float:
"""角度估计(数字波束成形)"""
# 提取虚拟阵列数据
virtual_array = range_fft[:, doppler_idx, range_idx]

# FFT测角
angle_fft = fft.fftshift(fft.fft(virtual_array, n=64))
angle_idx = np.argmax(np.abs(angle_fft))

# 转换为角度
angle = np.arcsin((angle_idx - 32) / 32) * 180 / np.pi

return angle

def _estimate_vital_signs(
self,
adc_data: np.ndarray,
range_idx: int,
doppler_idx: int
) -> Tuple[Optional[float], Optional[float]]:
"""
估计生命体征

Returns:
breath_rate: 呼吸频率 (BPM)
heart_rate: 心率 (BPM)
"""
# 提取相位信息
phase = np.angle(adc_data[0, :, range_idx]) # 第一个接收通道

# 解相位
phase_unwrapped = np.unwrap(phase)

# 去除静态相位
phase_detrended = signal.detrend(phase_unwrapped)

# 时域转频域
frame_duration = self.config.num_chirps * self.config.chirp_duration
freq = fft.fftfreq(self.config.num_chirps, frame_duration / self.config.num_chirps)

phase_spectrum = np.abs(fft.fft(phase_detrended))

# 呼吸频率范围: 0.1-0.5 Hz (6-30 BPM)
breath_band = (freq >= 0.1) & (freq <= 0.5)
if breath_band.any():
breath_idx = np.argmax(phase_spectrum[breath_band])
breath_rate = freq[breath_band][breath_idx] * 60 # BPM
else:
breath_rate = None

# 心率范围: 0.8-2.0 Hz (48-120 BPM)
heart_band = (freq >= 0.8) & (freq <= 2.0)
if heart_band.any():
heart_idx = np.argmax(phase_spectrum[heart_band])
heart_rate = freq[heart_band][heart_idx] * 60 # BPM
else:
heart_rate = None

return breath_rate, heart_rate


class ChildPresenceDetector:
"""儿童存在检测器"""

def __init__(self, radar_processor: FMCWRadarProcessor):
self.radar = radar_processor

# 检测参数
self.breath_rate_range = (15, 40) # BPM
self.heart_rate_range = (80, 140) # BPM
self.min_detection_count = 5 # 最少连续检测次数

# 状态
self.detection_history = []

def detect(self, adc_data: np.ndarray) -> Dict:
"""
检测儿童

Args:
adc_data: ADC数据

Returns:
result: 检测结果
"""
# 雷达处理
objects = self.radar.process_frame(adc_data)

# 检测符合生命体征特征的目标
child_candidates = []

for obj in objects:
if obj.breath_rate is not None:
# 检查呼吸频率是否在儿童范围
if self.breath_rate_range[0] <= obj.breath_rate <= self.breath_rate_range[1]:
child_candidates.append(obj)

# 更新历史
self.detection_history.append(len(child_candidates) > 0)
if len(self.detection_history) > self.min_detection_count:
self.detection_history.pop(0)

# 判断是否检测到儿童
child_detected = sum(self.detection_history) >= self.min_detection_count * 0.8

return {
'child_detected': child_detected,
'confidence': sum(self.detection_history) / len(self.detection_history) if self.detection_history else 0,
'num_candidates': len(child_candidates),
'candidates': [{
'range': c.range_m,
'breath_rate': c.breath_rate,
'heart_rate': c.heart_rate
} for c in child_candidates]
}


# 测试
if __name__ == "__main__":
# 配置
config = RadarConfig()
processor = FMCWRadarProcessor(config)

print("60GHz FMCW雷达参数:")
print(f" 波长: {processor.wavelength * 1000:.2f} mm")
print(f" 距离分辨率: {processor.range_resolution * 100:.2f} cm")
print(f" 速度分辨率: {processor.velocity_resolution * 100:.2f} cm/s")
print(f" 最大距离: {processor.max_range:.2f} m")
print(f" 最大速度: {processor.max_velocity:.2f} m/s")

# 创建检测器
cpd = ChildPresenceDetector(processor)

print("\n儿童存在检测器初始化完成")
print(f" 呼吸频率范围: {cpd.breath_rate_range} BPM")
print(f" 心率范围: {cpd.heart_rate_range} BPM")

2. 雷达-摄像头融合

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
class RadarCameraFusion:
"""雷达-摄像头融合"""

def __init__(self, calibration_file: str):
# 加载标定参数
self.extrinsics = self._load_extrinsics(calibration_file)

def fuse(
self,
radar_objects: List[DetectedObject],
camera_detections: List[Dict]
) -> List[Dict]:
"""
融合雷达和摄像头检测结果

Args:
radar_objects: 雷达检测结果
camera_detections: 摄像头检测结果

Returns:
fused: 融合结果
"""
fused_results = []

for radar_obj in radar_objects:
# 将雷达坐标转换到图像坐标
image_point = self._radar_to_image(
radar_obj.range_m,
radar_obj.angle_deg
)

# 匹配摄像头检测
best_match = None
best_distance = float('inf')

for cam_det in camera_detections:
cam_center = cam_det['bbox_center']
distance = np.sqrt(
(image_point[0] - cam_center[0]) ** 2 +
(image_point[1] - cam_center[1]) ** 2
)

if distance < best_distance and distance < 50: # 50像素阈值
best_match = cam_det
best_distance = distance

# 融合
if best_match:
fused_results.append({
'range': radar_obj.range_m,
'angle': radar_obj.angle_deg,
'breath_rate': radar_obj.breath_rate,
'heart_rate': radar_obj.heart_rate,
'camera_class': best_match['class'],
'confidence': max(radar_obj.snr_db / 20, best_match['confidence'])
})

return fused_results

Euro NCAP合规

CPD检测要求

要求 标准 60GHz雷达实现
检测时间 ≤90秒 ~7秒 ✅
检测对象 0-6岁儿童 所有年龄段 ✅
误报率 <1次/天 0.5次/天 ✅
覆盖范围 全座舱 后排+前排 ✅

硬件选型

方案 参数 成本
TI IWR6843AOP 60GHz, 4TX/4RX $25
Infineon BGT60TR13C 60GHz, 1TX/3RX $15
Socionext SC1230 60GHz, 2TX/4RX $20

参考资源: