Euro NCAP 2026 安全带误用检测技术与算法实现

Euro NCAP 2026 安全带误用检测技术与算法实现

Euro NCAP 安全带检测要求演进

从二元检测到行为分析

Euro NCAP 2026 对安全带检测的要求发生根本性变化:

版本 检测内容 评估方式
2025 及之前 安全带是否系上 二元检测(系/未系)
2026 安全带如何系 路由分析(正确/误用)

三种误用类型

Euro NCAP 2026 明确要求检测以下三种安全带误用:

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
正确佩戴:
┌─────────────────────────────────┐
│ ┌─────┐ │
│ │ 肩带 │ ← 跨越肩部 │
│ └──┬──┘ │
│ │ │
│ │ │
│ ┌──┴──┐ │
│ │ 腰带 │ ← 贴合髋骨 │
│ └─────┘ │
└─────────────────────────────────┘

误用类型 1:仅扣扣子
┌─────────────────────────────────┐
│ ┌─────┐ │
│ │ │ │
│ │ ═══ │ ← 安全带未穿过 │
│ │ │ │
│ └─────┘ │
│ 扣子已扣 │
└─────────────────────────────────┘

误用类型 2:安全带在背后
┌─────────────────────────────────┐
│ ┌─────┐ │
│ │ ═══ │ ← 安全带在背后 │
│ └─────┘ │
│ │
│ (背面视图) │
│ ═══ │
└─────────────────────────────────┘

误用类型 3:仅系腰带
┌─────────────────────────────────┐
│ ┌─────┐ │
│ │ │ │
│ │ │ ← 肩带未使用 │
│ └─────┘ │
│ ┌─────┐ │
│ │ 腰带 │ │
│ └─────┘ │
└─────────────────────────────────┘

检测技术路线

方案对比

方案 传感器 优势 劣势
视觉检测 RGB 摄像头 直观、可解释性强 受光照/遮挡影响
深度检测 ToF/结构光 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
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
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
import numpy as np
import cv2
from typing import List, Tuple, Optional
from dataclasses import dataclass
from enum import Enum

class BeltStatus(Enum):
"""安全带状态枚举"""
UNBUCKLED = "unbuckled" # 未系
CORRECT = "correct" # 正确佩戴
BUCKLE_ONLY = "buckle_only" # 仅扣扣子
BEHIND_BACK = "behind_back" # 安全带在背后
LAP_ONLY = "lap_only" # 仅系腰带


@dataclass
class Keypoint:
"""关键点"""
name: str
x: float
y: float
confidence: float = 1.0


class SeatbeltMisuseDetector:
"""
安全带误用检测器

基于 RGB 摄像头检测安全带路由
"""

def __init__(self):
# 关键点定义
self.belt_keypoints = [
'anchor_shoulder', # 肩部锚点
'anchor_hip', # 髋部锚点
'buckle', # 扣子
'shoulder_point', # 安全带在肩部的点
'chest_point', # 安全带在胸部的点
'lap_point_left', # 腰带左端
'lap_point_right', # 腰带右端
]

# 人体关键点
self.body_keypoints = [
'left_shoulder',
'right_shoulder',
'neck',
'chest',
'left_hip',
'right_hip'
]

# 检测参数
self.min_belt_width = 20 # 安全带最小宽度(像素)
self.max_belt_width = 80 # 安全带最大宽度
self.belt_color_lower = np.array([0, 0, 100]) # HSV 下界
self.belt_color_upper = np.array([180, 100, 255]) # HSV 上界

def detect(self,
image: np.ndarray,
body_keypoints: List[Keypoint]) -> Tuple[BeltStatus, dict]:
"""
检测安全带状态

Args:
image: 输入图像
body_keypoints: 人体关键点列表

Returns:
status: 安全带状态
info: 检测信息
"""
# 1. 检测安全带线段
belt_segments = self._detect_belt_segments(image)

# 2. 获取人体关键点
body_dict = {kp.name: kp for kp in body_keypoints}

# 3. 分析安全带路由
status, info = self._analyze_belt_routing(belt_segments, body_dict)

return status, info

def _detect_belt_segments(self, image: np.ndarray) -> List[dict]:
"""
检测安全带线段

使用颜色分割 + 霍夫变换检测安全带
"""
# 转换到 HSV 空间
hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)

# 颜色分割(假设安全带为深色)
mask = cv2.inRange(hsv, self.belt_color_lower, self.belt_color_upper)

# 形态学处理
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))
mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel)

# 边缘检测
edges = cv2.Canny(mask, 50, 150)

# 霍夫线变换
lines = cv2.HoughLinesP(
edges,
rho=1,
theta=np.pi/180,
threshold=50,
minLineLength=50,
maxLineGap=10
)

# 转换为线段列表
segments = []
if lines is not None:
for line in lines:
x1, y1, x2, y2 = line[0]
segments.append({
'start': (x1, y1),
'end': (x2, y2),
'length': np.sqrt((x2-x1)**2 + (y2-y1)**2),
'angle': np.arctan2(y2-y1, x2-x1)
})

return segments

def _analyze_belt_routing(self,
belt_segments: List[dict],
body_keypoints: dict) -> Tuple[BeltStatus, dict]:
"""
分析安全带路由
"""
info = {
'segments_count': len(belt_segments),
'shoulder_belt_detected': False,
'lap_belt_detected': False,
'buckle_detected': False
}

if not belt_segments:
return BeltStatus.UNBUCKLED, info

# 获取人体区域
if 'right_shoulder' not in body_keypoints or 'left_hip' not in body_keypoints:
return BeltStatus.UNBUCKLED, info

right_shoulder = body_keypoints['right_shoulder']
left_ship = body_keypoints['left_hip']
neck = body_keypoints.get('neck', Keypoint('neck',
(right_shoulder.x + body_keypoints['left_shoulder'].x) / 2,
right_shoulder.y - 20))

# 1. 检测肩带(从肩部到髋部对角线)
shoulder_belt = self._find_diagonal_belt(
belt_segments,
(right_shoulder.x, right_shoulder.y),
(left_hip.x, left_hip.y)
)
info['shoulder_belt_detected'] = shoulder_belt is not None

# 2. 检测腰带(横向线段)
lap_belt = self._find_horizontal_belt(
belt_segments,
(body_keypoints['left_hip'].x, body_keypoints['left_hip'].y),
(body_keypoints['right_hip'].x, body_keypoints['right_hip'].y)
)
info['lap_belt_detected'] = lap_belt is not None

# 3. 判断状态
if info['shoulder_belt_detected'] and info['lap_belt_detected']:
# 检测是否在背后(通过安全带相对于身体的位置判断)
if self._check_behind_back(shoulder_belt, body_keypoints):
return BeltStatus.BEHIND_BACK, info
else:
return BeltStatus.CORRECT, info

elif info['lap_belt_detected'] and not info['shoulder_belt_detected']:
return BeltStatus.LAP_ONLY, info

elif not info['shoulder_belt_detected'] and not info['lap_belt_detected']:
# 可能仅扣了扣子
# 需要进一步检测扣子区域
if self._check_buckle_only(belt_segments, body_keypoints):
return BeltStatus.BUCKLE_ONLY, info
else:
return BeltStatus.UNBUCKLED, info

return BeltStatus.UNBUCKLED, info

def _find_diagonal_belt(self,
segments: List[dict],
start_point: Tuple[float, float],
end_point: Tuple[float, float]) -> Optional[dict]:
"""查找对角线安全带(肩带)"""
expected_angle = np.arctan2(
end_point[1] - start_point[1],
end_point[0] - start_point[0]
)

for seg in segments:
# 检查角度是否接近
angle_diff = abs(seg['angle'] - expected_angle)
if angle_diff < np.pi / 6: # 30度容差
# 检查是否在预期区域内
# 简化:假设匹配
return seg

return None

def _find_horizontal_belt(self,
segments: List[dict],
left_point: Tuple[float, float],
right_point: Tuple[float, float]) -> Optional[dict]:
"""查找横向安全带(腰带)"""
for seg in segments:
# 横向线段角度接近 0 或 π
if abs(seg['angle']) < np.pi / 6 or abs(abs(seg['angle']) - np.pi) < np.pi / 6:
# 检查是否在腰带区域
y_min = min(left_point[1], right_point[1]) - 30
y_max = max(left_point[1], right_point[1]) + 30

if y_min <= seg['start'][1] <= y_max:
return seg

return None

def _check_behind_back(self,
shoulder_belt: dict,
body_keypoints: dict) -> bool:
"""
检测安全带是否在背后

原理:如果在背后,安全带相对于身体表面的可见性会降低
"""
# 简化实现:通过安全带与身体轮廓的关系判断
# 实际需要更复杂的 3D 推理
return False

def _check_buckle_only(self,
segments: List[dict],
body_keypoints: dict) -> bool:
"""
检测仅扣扣子

原理:扣子区域有线段,但无肩带/腰带
"""
# 简化实现
return False


# 深度学习模型版本
class SeatbeltMisuseCNN:
"""
基于 CNN 的安全带误用检测

使用端到端深度学习模型
"""

def __init__(self, model_path: str = None):
# 加载预训练模型
# 实际应使用 MobileNet/EfficientNet 等轻量级模型
self.model = self._build_model()

if model_path:
self.model.load_weights(model_path)

def _build_model(self):
"""构建模型"""
import tensorflow as tf
from tensorflow.keras import layers, models

# 输入:图像 + 人体关键点热力图
image_input = layers.Input(shape=(224, 224, 3), name='image')
heatmap_input = layers.Input(shape=(224, 224, 17), name='heatmap')

# 图像分支
x1 = layers.Conv2D(32, 3, strides=2, padding='same')(image_input)
x1 = layers.BatchNormalization()(x1)
x1 = layers.Activation('relu')(x1)

x1 = layers.Conv2D(64, 3, strides=2, padding='same')(x1)
x1 = layers.BatchNormalization()(x1)
x1 = layers.Activation('relu')(x1)

x1 = layers.Conv2D(128, 3, strides=2, padding='same')(x1)
x1 = layers.BatchNormalization()(x1)
x1 = layers.Activation('relu')(x1)

# 热力图分支
x2 = layers.Conv2D(32, 3, strides=2, padding='same')(heatmap_input)
x2 = layers.BatchNormalization()(x2)
x2 = layers.Activation('relu')(x2)

x2 = layers.Conv2D(64, 3, strides=2, padding='same')(x2)
x2 = layers.BatchNormalization()(x2)
x2 = layers.Activation('relu')(x2)

# 融合
x = layers.Concatenate()([x1, x2])

x = layers.GlobalAveragePooling2D()(x)
x = layers.Dense(128, activation='relu')(x)
x = layers.Dropout(0.5)(x)

# 输出:5 类(未系、正确、仅扣子、背后、仅腰带)
output = layers.Dense(5, activation='softmax', name='status')(x)

model = models.Model(
inputs=[image_input, heatmap_input],
outputs=output
)

return model

def predict(self,
image: np.ndarray,
keypoints: np.ndarray) -> Tuple[BeltStatus, float]:
"""
预测安全带状态

Args:
image: 输入图像
keypoints: 人体关键点热力图

Returns:
status: 安全带状态
confidence: 置信度
"""
# 预处理
image = cv2.resize(image, (224, 224))
image = image.astype(np.float32) / 255.0

heatmap = cv2.resize(keypoints, (224, 224))

# 推理
preds = self.model.predict([
image[np.newaxis, ...],
heatmap[np.newaxis, ...]
])

# 后处理
class_idx = np.argmax(preds[0])
confidence = preds[0][class_idx]

status_map = {
0: BeltStatus.UNBUCKLED,
1: BeltStatus.CORRECT,
2: BeltStatus.BUCKLE_ONLY,
3: BeltStatus.BEHIND_BACK,
4: BeltStatus.LAP_ONLY
}

return status_map[class_idx], confidence

检测难点与解决方案

难点 1:遮挡

1
2
3
4
5
问题:手臂、衣物遮挡安全带
解决方案:
├─ 多视角摄像头
├─ 时序推理(等待手臂移开)
└─ 推断模型(基于身体姿态推断安全带位置)

难点 2:光照

1
2
3
4
5
问题:强光/逆光影响安全带检测
解决方案:
├─ 红外摄像头
├─ 主动补光
└─ HDR 成像

难点 3:安全带颜色多样性

1
2
3
4
5
问题:不同车型安全带颜色不同
解决方案:
├─ 形状特征(线段检测)优于颜色特征
├─ 数据增强(多种颜色训练)
└─ 自适应阈值

Euro NCAP 测试场景

场景 测试内容 通过条件
SB-01 正确佩戴 正确识别为 CORRECT
SB-02 仅扣扣子 正确识别为 BUCKLE_ONLY
SB-03 安全带在背后 正确识别为 BEHIND_BACK
SB-04 仅系腰带 正确识别为 LAP_ONLY
SB-05 不同体型 AF05-AM95 全覆盖
SB-06 遮挡场景 手臂遮挡时正确识别

IMS 开发启示

1. 摄像头布局

1
2
3
4
5
6
7
8
9
10
11
12
推荐布局:
┌─────────────────────────────────┐
│ │
[A柱摄像头] [A柱摄像头]
│ ↘ ↙ │
│ ┌──────────────┐ │
│ │ 驾驶员座椅 │ │
│ └──────────────┘ │
│ ↑ │
[仪表台摄像头]
│ │
└─────────────────────────────────┘

2. 算法模块化

模块 输入 输出 技术方案
安全带检测 图像 线段列表 颜色分割 + 霍夫变换
人体姿态 图像 关键点 HRNet/MediaPipe
路由分析 线段 + 关键点 状态 规则 + ML 混合
状态确认 多帧结果 最终状态 时序投票

3. 性能指标

指标 目标值
检测准确率 ≥95%
误报率 ≤5%
检测时延 ≤2秒
体型覆盖 AF05-AM95

参考来源:


Euro NCAP 2026 安全带误用检测技术与算法实现
https://dapalm.com/2026/06/13/2026-06-13-Euro-NCAP-2026-Seatbelt-Misuse-Detection/
作者
Mars
发布于
2026年6月13日
许可协议