车内乘员年龄性别分类:OMS 多任务 CNN 实现

发布时间: 2026-04-14
关键词: Occupant Classification、Age Detection、Gender Detection、OMS、Multi-task CNN


功能需求

Euro NCAP 2026 要求 OMS 系统能够识别乘员类型,以实现安全气囊自适应部署:

乘员类型 安全策略
成人 正常气囊部署
儿童 降低气囊部署力度或禁用
婴儿座椅 禁用前排气囊

多任务 CNN 架构

架构设计

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
┌─────────────────────────────────────────────────────┐
│ 多任务乘员分类 CNN │
├─────────────────────────────────────────────────────┤
│ │
│ 输入图像 │
│ ──────── │
│ │ │
│ ▼ │
│ ┌─────────────────┐ │
│ │ 共享骨干网络 │ EfficientNet-Lite / MobileNet│
│ │ (Feature Extractor)│ │
│ └────────┬────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────┐ │
│ │ 共享特征层 │ │
│ └────────┬────────┘ │
│ │ │
│ ┌──────┼──────────────┐ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌────┐ ┌────┐ ┌────┐ │
│ │年龄│ │性别│ │类型│ │
│ │分类│ │分类│ │分类│ │
│ └──┬─┘ └──┬─┘ └──┬─┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ 年龄组 男/女 成人/儿童/婴儿座椅 │
│ │
└─────────────────────────────────────────────────────┘

代码实现

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
import torch
import torch.nn as nn
import torch.nn.functional as F
from torchvision.models import efficientnet_v2_s

class OccupantClassifier(nn.Module):
"""乘员分类器(多任务)"""

def __init__(self,
backbone: str = 'efficientnet_v2_s',
num_age_groups: int = 8,
pretrained: bool = True):
super().__init__()

# 骨干网络
if backbone == 'efficientnet_v2_s':
self.backbone = efficientnet_v2_s(pretrained=pretrained)
feature_dim = self.backbone.classifier[1].in_features
self.backbone.classifier = nn.Identity()

# 共享特征层
self.shared_features = nn.Sequential(
nn.Linear(feature_dim, 512),
nn.ReLU(),
nn.Dropout(0.3),
nn.Linear(512, 256),
nn.ReLU(),
)

# 年龄分类头
# 年龄组: 0-9, 10-19, 20-29, 30-39, 40-49, 50-59, 60-69, 70+
self.age_head = nn.Sequential(
nn.Linear(256, 128),
nn.ReLU(),
nn.Dropout(0.2),
nn.Linear(128, num_age_groups)
)

# 性别分类头
self.gender_head = nn.Sequential(
nn.Linear(256, 64),
nn.ReLU(),
nn.Dropout(0.2),
nn.Linear(64, 2) # male, female
)

# 乘员类型分类头
self.type_head = nn.Sequential(
nn.Linear(256, 64),
nn.ReLU(),
nn.Dropout(0.2),
nn.Linear(64, 3) # adult, child, infant_seat
)

# 儿童座椅检测头(辅助任务)
self.child_seat_head = nn.Sequential(
nn.Linear(256, 64),
nn.ReLU(),
nn.Linear(64, 2) # with_child_seat, without
)

def forward(self, x: torch.Tensor) -> dict:
"""
Args:
x: (B, C, H, W) 输入图像(乘员区域)

Returns:
{
'age_logits': (B, num_age_groups),
'gender_logits': (B, 2),
'type_logits': (B, 3),
'child_seat_logits': (B, 2)
}
"""
# 特征提取
features = self.backbone(x)

# 共享特征
shared = self.shared_features(features)

# 多任务输出
return {
'age_logits': self.age_head(shared),
'gender_logits': self.gender_head(shared),
'type_logits': self.type_head(shared),
'child_seat_logits': self.child_seat_head(shared)
}


class OccupantClassifierLoss(nn.Module):
"""多任务损失函数"""

def __init__(self,
age_weight: float = 1.0,
gender_weight: float = 1.0,
type_weight: float = 2.0, # 类型权重更高
child_seat_weight: float = 1.5):
super().__init__()

self.age_weight = age_weight
self.gender_weight = gender_weight
self.type_weight = type_weight
self.child_seat_weight = child_seat_weight

self.ce_loss = nn.CrossEntropyLoss()

def forward(self,
predictions: dict,
targets: dict) -> dict:
"""
Args:
predictions: 模型输出
targets: {
'age': (B,),
'gender': (B,),
'type': (B,),
'child_seat': (B,)
}

Returns:
{
'total_loss': tensor,
'age_loss': tensor,
'gender_loss': tensor,
'type_loss': tensor,
'child_seat_loss': tensor
}
"""
# 年龄损失
age_loss = self.ce_loss(predictions['age_logits'], targets['age'])

# 性别损失
gender_loss = self.ce_loss(predictions['gender_logits'], targets['gender'])

# 类型损失
type_loss = self.ce_loss(predictions['type_logits'], targets['type'])

# 儿童座椅损失
child_seat_loss = self.ce_loss(predictions['child_seat_logits'], targets['child_seat'])

# 总损失
total_loss = (
self.age_weight * age_loss +
self.gender_weight * gender_loss +
self.type_weight * type_loss +
self.child_seat_weight * child_seat_loss
)

return {
'total_loss': total_loss,
'age_loss': age_loss,
'gender_loss': gender_loss,
'type_loss': type_loss,
'child_seat_loss': child_seat_loss
}

年龄分组与安全策略

年龄到乘员类型映射

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
class OccupantTypeMapper:
"""乘员类型映射器"""

# 年龄组定义
AGE_GROUPS = {
0: (0, 9), # 儿童
1: (10, 17), # 青少年
2: (18, 29), # 青年
3: (30, 39), # 中年
4: (40, 49), # 中年
5: (50, 59), # 中老年
6: (60, 69), # 老年
7: (70, 120), # 老年
}

# 乘员类型
OCCUPANT_TYPES = {
0: 'ADULT', # 成人
1: 'CHILD', # 儿童
2: 'INFANT_SEAT', # 婴儿座椅
}

def get_occupant_type(self,
age_group: int,
child_seat_detected: bool,
height_estimate: float = None) -> str:
"""确定乘员类型

Args:
age_group: 年龄组索引
child_seat_detected: 是否检测到儿童座椅
height_estimate: 身高估计(可选)

Returns:
'ADULT' | 'CHILD' | 'INFANT_SEAT'
"""
# 如果检测到婴儿座椅
if child_seat_detected:
return 'INFANT_SEAT'

# 根据年龄组判断
if age_group == 0: # 0-9 岁
return 'CHILD'
elif age_group == 1: # 10-17 岁
# 青少年根据身高判断
if height_estimate and height_estimate < 1.5:
return 'CHILD'
return 'ADULT'
else:
return 'ADULT'

def get_airbag_strategy(self, occupant_type: str) -> dict:
"""获取安全气囊策略

Returns:
{
'enabled': bool,
'deployment_force': float, # 0.0 - 1.0
'warning': str
}
"""
if occupant_type == 'ADULT':
return {
'enabled': True,
'deployment_force': 1.0,
'warning': None
}
elif occupant_type == 'CHILD':
return {
'enabled': True,
'deployment_force': 0.6, # 降低部署力度
'warning': 'CHILD_DETECTED'
}
else: # INFANT_SEAT
return {
'enabled': False,
'deployment_force': 0.0,
'warning': 'INFANT_SEAT_DETECTED_AIRBAG_DISABLED'
}

Valeo IMS 方案

系统能力

Valeo 的车内监控系统能够检测:

参数 检测能力
年龄 年龄组分类
性别 男/女分类
服装 服装厚度检测(影响安全带贴合)
位置 座椅位置、姿态

技术路线

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
┌─────────────────────────────────────────────────────┐
│ Valeo IMS 架构 │
├─────────────────────────────────────────────────────┤
│ │
│ 摄像头输入 │
│ ────────── │
│ │ │
│ ▼ │
│ ┌─────────────────┐ │
│ │ 人体检测 │ YOLO / EfficientDet │
│ └────────┬────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────┐ │
│ │ 人脸检测 │ 人脸关键点 │
│ └────────┬────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────┐ │
│ │ 多任务 CNN │ │
│ │ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ │ │
│ │ │年龄 │ │性别 │ │服装 │ │姿态 │ │ │
│ │ └──┬──┘ └──┬──┘ └──┬──┘ └──┬──┘ │ │
│ │ └───────┴───────┴───────┘ │ │
│ └─────────────────┬───────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────┐ │
│ │ 安全系统输出 │ │
│ │ • 气囊部署策略 │ │
│ │ • 安全带预紧力 │ │
│ │ • 警告提示 │ │
│ └─────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────┘

儿童座椅检测

ISOFIX 检测

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
class ChildSeatDetector:
"""儿童座椅检测器"""

def __init__(self):
# 儿童座椅特征
self.child_seat_features = [
'five_point_harness', # 五点式安全带
'side_wings', # 侧翼
'headrest', # 头枕
'isofix_anchors', # ISOFIX 锚点
]

def detect(self, image: np.ndarray, seat_roi: np.ndarray) -> dict:
"""检测儿童座椅

Args:
image: 全车图像
seat_roi: 座椅区域

Returns:
{
'detected': bool,
'type': 'rear_facing' | 'forward_facing' | 'booster',
'confidence': float,
'isofix_detected': bool
}
"""
# 1. 检测 ISOFIX 锚点(视觉特征)
isofix_detected = self._detect_isofix(seat_roi)

# 2. 检测儿童座椅形状
seat_shape = self._detect_seat_shape(seat_roi)

# 3. 检测五点式安全带
harness_detected = self._detect_harness(seat_roi)

# 4. 综合判断
if harness_detected or seat_shape['confidence'] > 0.8:
return {
'detected': True,
'type': seat_shape['type'],
'confidence': seat_shape['confidence'],
'isofix_detected': isofix_detected
}

return {
'detected': False,
'type': None,
'confidence': 0.0,
'isofix_detected': isofix_detected
}

def _detect_isofix(self, roi: np.ndarray) -> bool:
"""检测 ISOFIX 锚点"""
# ISOFIX 锚点通常有特定的形状和位置
# 简化:使用模板匹配
# 实际需要训练专门的检测器
return False

def _detect_seat_shape(self, roi: np.ndarray) -> dict:
"""检测儿童座椅形状"""
# 使用分割或关键点检测
# 返回座椅类型和置信度
return {
'type': 'rear_facing',
'confidence': 0.9
}

def _detect_harness(self, roi: np.ndarray) -> bool:
"""检测五点式安全带"""
# 检测安全带纹理和形状
return False

Euro NCAP 儿童安全要求

儿童乘员保护得分

功能 得分
ISOFIX 锚点 每个位置 1-2 分
i-Size 标识 额外加分
气囊禁用开关 2 分
集成儿童座椅 额外加分
儿童座椅检测(自动禁用气囊) 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
┌─────────────────────────────────────────────────────┐
│ 自动气囊禁用逻辑 │
├─────────────────────────────────────────────────────┤
│ │
│ 1. 检测阶段 │
│ ────────── │
│ OMS 摄像头 ──► 儿童座椅检测 ──► 乘员分类 │
│ │
│ 2. 决策阶段 │
│ ────────── │
│ ┌─────────────────────────────────────┐ │
│ │ if child_seat_detected: │ │
│ │ airbag = DISABLED │ │
│ │ warning_light = ON │ │
│ │ elif occupant_age < 12: │ │
│ │ airbag = REDUCED_FORCE │ │
│ │ else: │ │
│ │ airbag = NORMAL │ │
│ └─────────────────────────────────────┘ │
│ │
│ 3. 用户反馈 │
│ ────────── │
│ ┌─────────────────┐ │
│ │ PASSENGER AIRBAG│ │
│ │ ○ OFF │ ← 指示灯 │
│ └─────────────────┘ │
│ │
└─────────────────────────────────────────────────────┘

性能评估

准确率指标

任务 目标准确率 说明
年龄组分类 > 80% 8 个年龄组
性别分类 > 90% 男/女
乘员类型 > 95% 成人/儿童/婴儿座椅
儿童座椅检测 > 95% 关键安全功能

延迟要求

场景 延迟要求
气囊部署决策 < 50 ms
警告灯点亮 < 100 ms

参考资源