Calterah UWB CPD 开发套件:2T4R UWB SoC 与端到端工具链深度解析

本文深度解析 Calterah(加特兰)最新发布的 UWB CPD 雷达开发套件,基于 Dubhe CAL1106AQ 芯片——业界首款 2T4R UWB SoC,从硬件参考设计到 ML 模型部署的全链路技术拆解,并给出对 IMS 座舱感知系统的开发启示。

1. 行业背景:UWB CPD 量产挑战

1.1 Euro NCAP CPD 要求

Euro NCAP 要求 CPD 系统在车内有儿童时触发告警,响应时间 <13 秒。当前主流方案对比:

方案 原理 优势 劣势
摄像头 视觉识别 可识别类型 被遮挡时失效
重量传感器 座椅压力 简单成熟 无法区分物体/儿童
超声波 声波反射 低成本 风噪误报
UWB 雷达 生命体征检测 穿透遮挡、无隐私 芯片+算法开发复杂

1.2 UWB CPD 的核心优势

UWB 雷达”感知生命”——不”看”形状(摄像头)、不”感”重量(压力传感器),而是检测婴儿胸腔的微小呼吸运动:

特性 UWB 雷达 摄像头 超声波
穿透遮挡 ✅ 穿透毛毯/座椅 ❌ 视线遮挡 ❌ 声波被阻
光照条件 ✅ 全光照 ❌ 需光/IR ✅ 不受影响
隐私 ✅ 无图像 ❌ 录制视频 ✅ 无图像
误报率 ✅ 低(区分生命/物体) ⚠️ 中(混淆玩偶) ⚠️ 高(风/虫触发)
检测类型 生命体征(呼吸) 视觉识别 运动/体积变化

1.3 UWB CPD 工作原理

1
2
3
4
5
6
7
8
9
10
UWB 传感器 → 发射超短脉冲 → 反射回波 → 分析 Channel Impulse Response (CIR)
↓
静态物体:恒定时延
呼吸儿童:微小 ToF 变化(mm级)
↓
匹配生命体征频率(20-40次/分钟婴儿)
↓
触发"Life Detected"告警
↓
TCU → 云端 → 手机APP通知

2. Calterah CAL1106AQ 芯片

2.1 核心规格

参数 CAL1106AQ
天线配置 2T4R(2 发射 + 4 接收)
行业地位 业界首款高性能 2T4R UWB SoC
检测能力 CPD / 乘员分类 / 生命体征 / 手势识别
角度测量 增强(相比 1T1R)
封装 车规级

2.2 2T4R vs 传统方案

配置 角度分辨率 覆盖范围 成本 适用
1T1R 无角度 单点 最低 基础 CPD
1T3R 有限角度 中等 中 标准 CPD
2T4R 高精度角度 广覆盖 中高 CPD+乘员分类+生命体征
4T4R 最高 最广 最高 高端应用

3. 开发套件组成

3.1 五大组件

3.2 工具链全流程

阶段 工具 输入 输出
数据采集 标定化采集工具 车内 UWB 信号 CIR 数据集
信号处理 分层参数引擎 CIR 原始数据 特征向量
机器学习 ML 训练框架 特征+标签 训练好的模型
评估 评估工具 模型+测试集 性能报告
部署 SDK 代码生成 训练好的模型 部署代码

4. 端到端开发流程

4.1 数据采集

要素 要求
标注方法 标定化标注方法论
正负样本比 科学比例
场景覆盖 多车型/多座位/多遮挡
数据格式 CIR 时序数据

4.2 信号处理

Calterah 的分层参数设计是核心创新:

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
import numpy as np
from typing import Tuple

class UWBRadarProcessor:
"""
Calterah UWB 雷达信号处理器(概念实现)

基于分层参数设计:
- 默认参数:适配大部分车型/环境
- 微调参数:少量参数针对特定车型
"""

def __init__(self, config: str = 'default'):
"""
Args:
config: 'default' 或 'custom_vehicle'
"""
# 默认参数(适用大部分车型)
self.default_params = {
'range_resolution': 0.015, # 1.5cm 距离分辨率
'velocity_resolution': 0.05, # 5cm/s 速度分辨率
'detection_range': 2.0, # 2m 检测范围
'clutter_removal': 'mean_subtract',
'static_threshold': 0.3,
'breathing_freq_range': (0.3, 0.8), # 18-48 次/分钟
'heart_rate_range': (1.0, 2.0), # 60-120 次/分钟
}

# 微调参数(仅少量需要按车型调整)
self.tuning_params = {
'antenna_delay': 0, # 天线延迟(按安装位置)
'clutter_map': None, # 杂波图(按车内环境)
}

def process_cir(self, cir_data: np.ndarray) -> Dict:
"""
处理 Channel Impulse Response 数据

Args:
cir_data: CIR 数据, shape=(N_frames, N_rx, N_samples)

Returns:
特征字典
"""
# 1. 杂波去除
clean_data = self._remove_clutter(cir_data)

# 2. 距离-多普勒图
range_doppler = self._compute_range_doppler(clean_data)

# 3. 提取生命体征
breathing = self._extract_breathing(clean_data)
heart_rate = self._extract_heart_rate(clean_data)

# 4. 特征向量
features = {
'range_profile': self._range_profile(clean_data),
'doppler_profile': range_doppler,
'breathing_rate': breathing,
'breathing_amplitude': np.max(np.abs(breathing)),
'heart_rate': heart_rate,
'motion_level': np.var(clean_data, axis=0).mean(),
'static_energy': np.mean(np.abs(clean_data)),
}

return features

def _remove_clutter(self, data: np.ndarray) -> np.ndarray:
"""均值减法去杂波"""
return data - np.mean(data, axis=0, keepdims=True)

def _compute_range_doppler(self, data: np.ndarray) -> np.ndarray:
"""计算距离-多普勒图"""
# 沿时间轴 FFT 得到多普勒
doppler = np.fft.fft(data, axis=0)
# 沿距离轴 FFT
range_doppler = np.fft.fft(doppler, axis=-1)
return np.abs(range_doppler)

def _extract_breathing(self, data: np.ndarray) -> np.ndarray:
"""提取呼吸信号(带通滤波 0.3-0.8 Hz)"""
from scipy.signal import butter, filtfilt

fs = 20 # 假设 20fps
low, high = self.default_params['breathing_freq_range']
b, a = butter(4, [low/(fs/2), high/(fs/2)], btype='band')

# 对每个接收通道的每个距离门滤波
breathing = np.zeros_like(data)
for rx in range(data.shape[1]):
for rng in range(data.shape[2]):
breathing[:, rx, rng] = filtfilt(b, a, data[:, rx, rng])

return breathing

def _extract_heart_rate(self, data: np.ndarray) -> np.ndarray:
"""提取心率信号(带通滤波 1.0-2.0 Hz)"""
from scipy.signal import butter, filtfilt

fs = 20
low, high = self.default_params['heart_rate_range']
b, a = butter(4, [low/(fs/2), high/(fs/2)], btype='band')

hr = np.zeros_like(data)
for rx in range(data.shape[1]):
for rng in range(data.shape[2]):
hr[:, rx, rng] = filtfilt(b, a, data[:, rx, rng])

return hr

def _range_profile(self, data: np.ndarray) -> np.ndarray:
"""距离剖面"""
return np.abs(np.fft.fft(data, axis=-1)).mean(axis=0)


# 测试
if __name__ == "__main__":
np.random.seed(42)

processor = UWBRadarProcessor()

# 模拟 CIR 数据(100帧, 4接收通道, 64采样点)
# 静态杂波 + 模拟呼吸信号
t = np.arange(100) / 20.0 # 5秒数据
breathing_signal = 0.1 * np.sin(2 * np.pi * 0.4 * t) # 0.4Hz = 24次/分

cir = np.random.randn(100, 4, 64) * 0.01 # 噪声
cir += 0.5 # 静态杂波
cir[:, :, 20] += breathing_signal.reshape(-1, 1, 1) # 距离门20处有呼吸

features = processor.process_cir(cir)

print("=== UWB 雷达特征 ===")
print(f"静态能量: {features['static_energy']:.4f}")
print(f"运动水平: {features['motion_level']:.6f}")
print(f"呼吸幅度: {features['breathing_amplitude']:.4f}")
print(f"距离剖面峰值位置: 门{np.argmax(features['range_profile'][0])}")

4.3 机器学习训练

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
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split
import numpy as np

class CPDClassifier:
"""
Calterah 开发套件中的 ML 模型(概念实现)

区分场景:
- empty: 空座
- adult: 成人
- child: 儿童
- object: 静态物体
"""

def __init__(self):
self.model = RandomForestClassifier(
n_estimators=100,
max_depth=10,
random_state=42,
)
self.feature_names = [
'static_energy', 'motion_level',
'breathing_rate', 'breathing_amplitude',
'heart_rate', 'range_peak', 'doppler_peak',
]

def extract_features(self, processor: UWBRadarProcessor,
cir_data: np.ndarray) -> np.ndarray:
"""从 CIR 数据提取特征向量"""
features = processor.process_cir(cir_data)

return np.array([[
features['static_energy'],
features['motion_level'],
features.get('breathing_rate', 0),
features['breathing_amplitude'],
features.get('heart_rate', 0),
np.max(features['range_profile']),
np.max(features['doppler_profile']),
]])

def train(self, X: np.ndarray, y: np.ndarray) -> Dict:
"""训练模型"""
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42
)
self.model.fit(X_train, y_train)

train_acc = self.model.score(X_train, y_train)
test_acc = self.model.score(X_test, y_test)

return {
'train_accuracy': train_acc,
'test_accuracy': test_acc,
'feature_importance': dict(zip(
self.feature_names,
self.model.feature_importances_
)),
}

def predict(self, X: np.ndarray) -> np.ndarray:
"""预测"""
return self.model.predict(X)

def predict_proba(self, X: np.ndarray) -> np.ndarray:
"""概率预测"""
return self.model.predict_proba(X)


# 测试
if __name__ == "__main__":
np.random.seed(42)

clf = CPDClassifier()
processor = UWBRadarProcessor()

# 生成模拟训练数据
n_samples = 200
X = []
y = []
labels = ['empty', 'adult', 'child', 'object']

for i, label in enumerate(labels):
for _ in range(n_samples // 4):
cir = np.random.randn(100, 4, 64) * 0.01 + 0.5

if label == 'adult':
cir[:, :, 20] += 0.3 * np.sin(2*np.pi*0.25*np.arange(100)/20).reshape(-1,1,1)
cir[:, :, 20] += 0.1 * np.sin(2*np.pi*1.2*np.arange(100)/20).reshape(-1,1,1)
elif label == 'child':
cir[:, :, 15] += 0.1 * np.sin(2*np.pi*0.4*np.arange(100)/20).reshape(-1,1,1)
cir[:, :, 15] += 0.05 * np.sin(2*np.pi*1.5*np.arange(100)/20).reshape(-1,1,1)
elif label == 'object':
pass # 静态

features = clf.extract_features(processor, cir)
X.append(features[0])
y.append(label)

X = np.array(X)
y = np.array(y)

result = clf.train(X, y)
print("=== 训练结果 ===")
print(f"训练准确率: {result['train_accuracy']:.2%}")
print(f"测试准确率: {result['test_accuracy']:.2%}")
print(f"\n特征重要性:")
for name, imp in sorted(result['feature_importance'].items(),
key=lambda x: -x[1]):
print(f" {name}: {imp:.4f}")

4.4 部署

步骤 操作 自动化程度
模型训练完成 工具链自动生成 SDK 代码 全自动
代码替换 替换生成的代码到项目 手动替换
重新编译 编译软件 手动
车型适配 微调少量参数 数天内完成

5. 四大核心优势

优势 说明 价值
标准化 完整 SOP 覆盖全流程 降低开发门槛
可视化 中间结果+运行日志 高效评估/排错
可扩展 多车型/场景/天线配置 一次开发多处使用
可部署 自动化工作流 快速可靠部署

6. 对 IMS 的开发启示

6.1 UWB CPD 在 IMS 中的定位

功能模块 UWB 角色 与视觉/雷达的关系
CPD 儿童检测 主传感器 替代/补充摄像头
乘员分类 辅助 补充视觉分类
生命体征监测 主传感器 新功能(驾驶员健康)
手势识别 辅助 补充视觉手势

6.2 UWB + 摄像头融合方案

6.3 开发优先级建议

建议 优先级 依据
采购 Calterah 开发套件评估 🔴 高 直接可用,缩短开发周期
UWB CPD 原型验证 🔴 高 Euro NCAP 2026 强制要求
UWB+视觉融合算法开发 🟡 中 提升检测准确率
生命体征监测功能开发 🟡 中 差异化功能
车型适配自动化工具 🟢 低 量产阶段需要

6.4 芯片选型对比

芯片 Calterah CAL1106AQ TI IWR6843AOP NXP NCJ29D6
频段 UWB (6-8 GHz) 60GHz mmWave UWB (6-8 GHz)
天线 2T4R 3T4R (AOP) 1T1R
CPD ✅ 主功能 ✅ ✅
生命体征 ✅ ✅ ⚠️ 有限
手势识别 ✅ ❌ ❌
工具链 完整端到端 需自建 基础 SDK

7. 总结

Calterah UWB CPD 开发套件代表了 UWB 座舱感知从”芯片”到”量产方案”的关键一步。其核心价值不在于芯片本身,而在于:

  1. 端到端工具链:从数据采集到部署的全流程标准化
  2. 分层参数设计:大部分参数用默认值,仅少量微调
  3. 车型适配加速:数天内完成单车适配(vs 传统数月)
  4. 2T4R 高精度:业界首款 2T4R UWB SoC,角度测量更准

对于 IMS 开发,Calterah 提供了一条从”零”到”量产”的 UWB CPD 快速通道。结合 DMS/OMS 摄像头,UWB 雷达可以补齐视觉方案在遮挡场景和隐私场景下的短板,同时增加生命体征监测这一差异化功能。


参考来源:

  • Calterah 官方:UWB CPD Radar Development Suite (2026-08)
  • RF Wireless World: UWB Radar for Child Presence Detection
  • Calterah CAL1106AQ 产品文档