3D乘员姿态估计:深度+红外融合的车辆内部感知方案

3D乘员姿态估计:深度+红外融合的车辆内部感知方案

论文来源: Sensors 2024 (doi:10.3390/s24175530)
核心方法: 深度+红外图像的3D姿态估计
精度指标: 所有关节误差<10cm(中位数)


Euro NCAP OOP检测要求

异常姿态乘员(Out-of-Position, OOP) 是指:

  • 蜷缩坐姿
  • 儿童倚靠车门
  • 乘员过于靠近仪表盘

Euro NCAP 2026要求系统能检测OOP并调整气囊展开策略。


论文核心贡献

1. 首个纯深度+IR的姿态估计

传统方法依赖RGB图像,但在车内光照变化下不稳定。本文首次仅使用深度和红外数据。

2. 少样本学习

仅需**<100个手动标注样本**即可训练高精度模型。

3. 精度指标

指标 数值
中位数误差 <10cm
训练数据 <100样本
帧率 15fps

技术架构

graph TD
    A[深度摄像头] --> B[深度图预处理]
    C[红外摄像头] --> D[IR图像增强]
    B --> E[特征提取网络]
    D --> E
    E --> F[3D关键点回归]
    F --> G[姿态分类]
    G --> H{姿态判定}
    H -->|正常| I[继续监测]
    H -->|OOP| J[气囊抑制/调整]

实现代码

3D关键点检测

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
"""
3D乘员姿态估计实现
"""
import numpy as np
import torch
import torch.nn as nn
from typing import Tuple, List

# 乘员关键点定义
OCCUPANT_KEYPOINTS = [
'head_top', # 头顶
'neck', # 颈部
'left_shoulder', # 左肩
'right_shoulder', # 右肩
'left_elbow', # 左肘
'right_elbow', # 右肘
'left_hand', # 左手
'right_hand', # 右手
'spine_mid', # 脊柱中段
'spine_base', # 脊柱底部
'left_hip', # 左髋
'right_hip', # 右髋
'left_knee', # 左膝
'right_knee', # 右膝
'left_foot', # 左脚
'right_foot' # 右脚
]

class OccupantPoseEstimator(nn.Module):
"""3D乘员姿态估计网络"""

def __init__(self, num_keypoints: int = 16):
super().__init__()
self.num_keypoints = num_keypoints

# 特征提取骨干(简化版)
self.backbone = nn.Sequential(
nn.Conv2d(2, 32, 3, padding=1), # 深度+IR输入
nn.ReLU(),
nn.MaxPool2d(2),
nn.Conv2d(32, 64, 3, padding=1),
nn.ReLU(),
nn.MaxPool2d(2),
nn.Conv2d(64, 128, 3, padding=1),
nn.ReLU(),
nn.AdaptiveAvgPool2d((8, 8))
)

# 3D关键点回归头
self.keypoint_head = nn.Sequential(
nn.Flatten(),
nn.Linear(128 * 8 * 8, 512),
nn.ReLU(),
nn.Linear(512, num_keypoints * 3) # x, y, z per keypoint
)

def forward(self, depth: torch.Tensor, ir: torch.Tensor) -> torch.Tensor:
"""
前向传播

Args:
depth: 深度图 (B, 1, H, W)
ir: 红外图 (B, 1, H, W)

Returns:
keypoints_3d: 3D关键点 (B, K, 3)
"""
# 拼接输入
x = torch.cat([depth, ir], dim=1)

# 特征提取
features = self.backbone(x)

# 关键点回归
keypoints_flat = self.keypoint_head(features)
keypoints_3d = keypoints_flat.view(-1, self.num_keypoints, 3)

return keypoints_3d


class OOPClassifier:
"""异常姿态分类器"""

def __init__(self):
# 定义正常姿态边界
self.normal_ranges = {
'head_height': (0.6, 1.2), # 头高度范围(米)
'shoulder_tilt': (-0.1, 0.1), # 肩膀倾斜(弧度)
'hip_angle': (80, 120), # 髋关节角度(度)
'knee_angle': (70, 150) # 膝关节角度(度)
}

def classify(self, keypoints_3d: np.ndarray) -> dict:
"""
分类姿态是否异常

Args:
keypoints_3d: 3D关键点 (K, 3)

Returns:
result: 分类结果
"""
# 1. 计算头高度
head_height = keypoints_3d[0, 1] # 头顶y坐标

# 2. 计算肩膀倾斜
left_shoulder = keypoints_3d[2]
right_shoulder = keypoints_3d[3]
shoulder_tilt = np.arctan2(
left_shoulder[1] - right_shoulder[1],
left_shoulder[0] - right_shoulder[0]
)

# 3. 计算髋关节角度
left_hip = keypoints_3d[10]
left_knee = keypoints_3d[12]
spine_base = keypoints_3d[9]
hip_angle = self._calculate_angle(spine_base, left_hip, left_knee)

# 4. 计算膝关节角度
left_ankle = keypoints_3d[14]
knee_angle = self._calculate_angle(left_hip, left_knee, left_ankle)

# 异常判定
is_oop = (
not self._in_range(head_height, self.normal_ranges['head_height']) or
not self._in_range(abs(shoulder_tilt), self.normal_ranges['shoulder_tilt']) or
not self._in_range(hip_angle, self.normal_ranges['hip_angle']) or
not self._in_range(knee_angle, self.normal_ranges['knee_angle'])
)

# 判断具体OOP类型
oop_type = self._classify_oop_type(
head_height, shoulder_tilt, hip_angle, knee_angle
)

return {
'is_oop': is_oop,
'oop_type': oop_type,
'head_height': head_height,
'shoulder_tilt': shoulder_tilt,
'hip_angle': hip_angle,
'knee_angle': knee_angle
}

def _calculate_angle(self, p1: np.ndarray, p2: np.ndarray,
p3: np.ndarray) -> float:
"""计算三点形成的角度"""
v1 = p1 - p2
v2 = p3 - p2

cos_angle = np.dot(v1, v2) / (np.linalg.norm(v1) * np.linalg.norm(v2) + 1e-6)
angle = np.arccos(np.clip(cos_angle, -1, 1))

return np.degrees(angle)

def _in_range(self, value: float, range_tuple: Tuple) -> bool:
"""判断值是否在范围内"""
return range_tuple[0] <= value <= range_tuple[1]

def _classify_oop_type(self, head_height: float, shoulder_tilt: float,
hip_angle: float, knee_angle: float) -> str:
"""分类OOP类型"""
if head_height < 0.6:
return 'slouched' # 蜷缩
elif abs(shoulder_tilt) > 0.2:
return 'leaning' # 倾斜
elif hip_angle < 80:
return 'forward' # 前倾
elif knee_angle < 70:
return 'legs_bent' # 腿部弯曲
else:
return 'normal'


# 测试代码
if __name__ == "__main__":
# 创建模型
model = OccupantPoseEstimator()
classifier = OOPClassifier()

# 模拟输入
depth = torch.randn(1, 1, 240, 320)
ir = torch.randn(1, 1, 240, 320)

# 推理
with torch.no_grad():
keypoints_3d = model(depth, ir)

# 分类
keypoints_np = keypoints_3d[0].numpy()
result = classifier.classify(keypoints_np)

print(f"OOP检测: {result['is_oop']}")
print(f"OOP类型: {result['oop_type']}")
print(f"头高度: {result['head_height']:.2f}m")

传感器配置

深度摄像头选型

传感器 技术 分辨率 帧率 功耗 价格
Intel RealSense D435 结构光 1280×720 90fps 3W $150
PMD CamBoard pico flexx 飞行时间 352×288 45fps 1W $200
Azure Kinect 飞行时间 1024×1024 30fps 5W $400

红外摄像头选型

传感器 分辨率 波段 帧率 价格
OV2311 1600×1200 850nm 60fps $10
AR0237 1920×1200 940nm 60fps $15

少样本学习策略

半监督域适应

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
class SemiSupervisedDomainAdaptation:
"""半监督域适应训练"""

def __init__(self, labeled_samples: int = 100):
self.labeled_samples = labeled_samples

def train(self, depth_ir_data: np.ndarray,
sparse_labels: np.ndarray):
"""
少样本训练

Args:
depth_ir_data: 深度+IR数据 (N, 2, H, W)
sparse_labels: 稀疏标注 (N, K, 3)
"""
# 1. 使用预训练模型
pretrained = load_pretrained_model()

# 2. 仅在标注样本上微调
for epoch in range(100):
# 随机采样标注数据
idx = np.random.choice(len(sparse_labels),
min(self.labeled_samples, len(sparse_labels)))

batch_data = depth_ir_data[idx]
batch_labels = sparse_labels[idx]

# 计算损失
loss = compute_loss(pretrained, batch_data, batch_labels)

# 反向传播
loss.backward()
optimizer.step()

# 3. 使用未标注数据自监督学习
self._self_supervised_learning(pretrained, depth_ir_data)

Euro NCAP OOP测试场景

场景编号 场景描述 检测要求
OOP-01 蜷缩坐姿(身体前倾) 检测+抑制气囊
OOP-02 儿童倚靠车门 检测+调整气囊
OOP-03 乘员过于靠近仪表盘 检测+抑制气囊
OOP-04 腿部放置在仪表盘上 检测+警告
OOP-05 后向儿童座椅 检测+调整气囊

IMS开发启示

优先级排序

优先级 功能 原因
P0 头部高度检测 最关键的OOP指标
P0 肩膀倾斜检测 倚靠检测核心
P1 髋/膝关节角度 完整姿态估计
P1 气囊抑制逻辑 法规要求
P2 少样本学习 降低标注成本

技术路线

graph LR
    A[深度摄像头] --> B[数据采集]
    C[IR摄像头] --> B
    B --> D[预训练模型]
    D --> E[少量标注]
    E --> F[微调训练]
    F --> G[OOP检测]
    G --> H[气囊控制]

总结

3D乘员姿态估计的核心要点:

  1. 传感器选择:深度+IR融合,不依赖可见光
  2. 精度目标:关节误差<10cm
  3. 少样本学习:<100标注样本即可训练
  4. 应用场景:OOP检测→气囊控制

IMS开发建议:优先采购RealSense D435开发原型,验证OOP检测可行性。


参考论文:

  • Sensors 2024: “Three-Dimensional Posture Estimation of Vehicle Occupants”
  • Euro NCAP 2026 OMS Protocol

3D乘员姿态估计:深度+红外融合的车辆内部感知方案
https://dapalm.com/2026/07/29/2026-07-29-3d-occupant-posture-estimation-depth-ir/
作者
Mars
发布于
2026年7月29日
许可协议