Euro NCAP DSM 测试场景全清单:从 D-01 到 U-03 的完整解析

发布时间: 2026-04-15
关键词: Euro NCAP、DSM、测试场景、D-01、F-01、U-01、TB-039


场景编码体系

Euro NCAP TB-039 定义了 DSM 测试场景编码:

编码前缀 场景类型 数量
D- 分心检测(Distraction) 5 个
F- 疲劳检测(Fatigue) 3 个
U- 无响应检测(Unresponsive) 3 个

分心检测场景(D 系列)

D-01:视线偏离道路

参数 规范
触发条件 视线偏离前方道路 > 3 秒
偏离角度 yaw > 15° 或 pitch > 10°
检测时限 ≤ 3 秒
警告类型 一级警告(视觉 + 听觉)
通过标准 检测准确率 ≥ 90%

D-02:手持手机通话

参数 规范
触发条件 驾驶员手持手机至耳边
持续时间 ≥ 3 秒
检测时限 ≤ 3 秒
警告类型 一级警告
通过标准 检测准确率 ≥ 90%

D-03:手持手机打字

参数 规范
触发条件 驾驶员手持手机并操作
持续时间 ≥ 3 秒
检测时限 ≤ 3 秒
警告类型 一级警告
通过标准 检测准确率 ≥ 90%

D-04:操作中控屏幕

参数 规范
触发条件 驾驶员操作中控屏幕
持续时间 ≥ 3 秒
检测时限 ≤ 3 秒
警告类型 一级警告
通过标准 检测准确率 ≥ 90%

D-05:低头捡物

参数 规范
触发条件 驾驶员低头(pitch < -20°)
持续时间 ≥ 3 秒
检测时限 ≤ 3 秒
警告类型 一级警告
通过标准 检测准确率 ≥ 90%

疲劳检测场景(F 系列)

F-01:PERCLOS 超标

参数 规范
触发条件 PERCLOS ≥ 30%,持续 5 秒
PERCLOS 定义 眼睑闭合比例(眼睑遮盖瞳孔 > 80%)
检测时限 累计 5 秒内
警告类型 二级警告(视觉 + 听觉 + 触觉)
通过标准 检测准确率 ≥ 90%

F-02:频繁打哈欠

参数 规范
触发条件 2 分钟内打哈欠 ≥ 3 次
哈欠定义 嘴巴张开 > 50% 持续 > 1 秒
检测时限 2 分钟窗口
警告类型 二级警告
通过标准 检测准确率 ≥ 90%

F-03:眼睛闭合超时

参数 规范
触发条件 眼睛完全闭合 > 2 秒
检测时限 ≤ 2 秒
警告类型 立即警告(高风险)
通过标准 检测准确率 ≥ 95%

无响应检测场景(U 系列)

U-01:无方向盘操作

参数 规范
触发条件 无方向盘扭矩输入 > 10 秒
车速要求 > 60 km/h
检测时限 10-15 秒
警告类型 升级警告 → ADAS 干预
通过标准 检测准确率 ≥ 90%

U-02:无踏板操作

参数 规范
触发条件 无油门/刹车操作 > 10 秒
车速要求 > 60 km/h
检测时限 10-15 秒
警告类型 升级警告 → ADAS 干预
通过标准 检测准确率 ≥ 90%

U-03:对警告无反应

参数 规范
触发条件 一级/二级警告后 10 秒内无响应
响应定义 方向盘输入、踏板输入、或视线恢复
检测时限 10 秒
警告类型 ADAS 干预(车道保持 + 减速)
通过标准 干预触发 ≥ 90%

警告级别定义

一级警告(Level 1)

要素 规范
视觉 仪表盘图标闪烁
听觉 单次蜂鸣(≤ 1 秒)
触发条件 首次检测到分心/轻度疲劳
持续时间 警告后 10 秒内

二级警告(Level 2)

要素 规范
视觉 图标常亮 + 文字提示
听觉 持续蜂鸣(≤ 3 秒)
触觉 方向盘振动(可选)
触发条件 一级警告后无响应 或 严重疲劳

ADAS 干预

要素 规范
车道保持 自动修正方向
减速停车 逐步减速至停止
紧急呼叫 联系紧急服务
触发条件 二级警告后无响应

测试环境要求

车辆配置

要求 规范
测试车辆 生产配置车辆
传感器 DMS 摄像头(位置按 OEM 声明)
软件版本 最新生产版本
座椅位置 中间位置

光照条件

条件 说明
日间 > 1000 lux
夜间 < 10 lux(IR 补光)
逆光 光源在摄像头前方

驾驶员要求

要求 说明
人数 ≥ 6 名测试人员
性别 男女各半
年龄 18-65 岁
肤色 Fitzpatrick I-VI 型
配饰 眼镜、太阳镜、帽子

测试流程

准备阶段

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
┌─────────────────────────────────────────────────────┐
│ Euro NCAP DSM 测试流程 │
├─────────────────────────────────────────────────────┤
│ │
│ 1. Dossier 提交 │
│ ─────────────── │
│ • 系统技术规格 │
│ • 传感器配置 │
│ • 算法描述 │
│ • 内部测试报告 │
│ │
│ 2. Euro NCAP 审核 │
│ ───────────────── │
│ • 技术文档审核 │
│ • 测试场景确认 │
│ • 测试时间预约 │
│ │
│ 3. Spot Testing(现场测试) │
│ ───────────────────── │
│ • Euro NCAP 指定场景 │
│ • 测试人员执行动作 │
│ • 记录检测延迟和警告时机 │
│ │
│ 4. 结果评估 │
│ ──────── │
│ • 检测准确率计算 │
│ • 警告时效验证 │
│ • ADAS 协同验证 │
│ │
│ 5. 评分公布 │
│ ──────── │
│ • Safe Driving 得分 │
│ • DSM 功能得分 │
│ │
└─────────────────────────────────────────────────────┘

代码示例:场景模拟器

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
import numpy as np
from dataclasses import dataclass
from typing import Dict, List, Tuple
from enum import Enum

class ScenarioType(Enum):
"""场景类型"""
D_01 = "D-01" # 视线偏离
D_02 = "D-02" # 手机通话
D_03 = "D-03" # 手机打字
D_04 = "D-04" # 中控操作
D_05 = "D-05" # 低头捡物
F_01 = "F-01" # PERCLOS 超标
F_02 = "F-02" # 频繁打哈欠
F_03 = "F-03" # 眼睛闭合
U_01 = "U-01" # 无方向盘操作
U_02 = "U-02" # 无踏板操作
U_03 = "U-03" # 对警告无反应

@dataclass
class ScenarioConfig:
"""场景配置"""
scenario_type: ScenarioType
trigger_duration: float # 触发持续时间(秒)
detection_deadline: float # 检测时限(秒)
warning_level: int # 警告级别 1-2
pass_accuracy: float # 通过准确率阈值

# Euro NCAP 标准配置
SCENARIO_CONFIGS = {
ScenarioType.D_01: ScenarioConfig(ScenarioType.D_01, 3.0, 3.0, 1, 0.9),
ScenarioType.D_02: ScenarioConfig(ScenarioType.D_02, 3.0, 3.0, 1, 0.9),
ScenarioType.D_03: ScenarioConfig(ScenarioType.D_03, 3.0, 3.0, 1, 0.9),
ScenarioType.D_04: ScenarioConfig(ScenarioType.D_04, 3.0, 3.0, 1, 0.9),
ScenarioType.D_05: ScenarioConfig(ScenarioType.D_05, 3.0, 3.0, 1, 0.9),
ScenarioType.F_01: ScenarioConfig(ScenarioType.F_01, 5.0, 5.0, 2, 0.9),
ScenarioType.F_02: ScenarioConfig(ScenarioType.F_02, 120.0, 120.0, 2, 0.9),
ScenarioType.F_03: ScenarioConfig(ScenarioType.F_03, 2.0, 2.0, 2, 0.95),
ScenarioType.U_01: ScenarioConfig(ScenarioType.U_01, 10.0, 15.0, 2, 0.9),
ScenarioType.U_02: ScenarioConfig(ScenarioType.U_02, 10.0, 15.0, 2, 0.9),
ScenarioType.U_03: ScenarioConfig(ScenarioType.U_03, 10.0, 10.0, 2, 0.9),
}

class DSMSimulator:
"""DMS 场景模拟器"""

def __init__(self):
self.current_scenario = None
self.elapsed_time = 0

def start_scenario(self, scenario_type: ScenarioType):
"""开始场景"""
self.current_scenario = SCENARIO_CONFIGS[scenario_type]
self.elapsed_time = 0

def update(self, dt: float,
dms_detection: bool,
warning_issued: bool) -> Dict:
"""更新场景状态

Args:
dt: 时间步长(秒)
dms_detection: DMS 是否检测到
warning_issued: 是否发出警告

Returns:
{
'elapsed_time': float,
'scenario_complete': bool,
'detection_success': bool,
'timing_success': bool
}
"""
self.elapsed_time += dt

config = self.current_scenario

# 检查是否完成
scenario_complete = self.elapsed_time >= config.trigger_duration + config.detection_deadline

# 检查检测是否成功
detection_success = dms_detection and self.elapsed_time >= config.trigger_duration

# 检查时机是否成功
timing_success = (
warning_issued and
self.elapsed_time >= config.trigger_duration and
self.elapsed_time <= config.trigger_duration + config.detection_deadline
)

return {
'elapsed_time': self.elapsed_time,
'scenario_complete': scenario_complete,
'detection_success': detection_success,
'timing_success': timing_success
}


class TestRunner:
"""测试运行器"""

def __init__(self):
self.simulator = DSMSimulator()
self.results = []

def run_all_scenarios(self, dms_system) -> Dict:
"""运行所有场景

Args:
dms_system: DMS 系统实例

Returns:
{
'total_scenarios': int,
'passed_scenarios': int,
'overall_accuracy': float,
'scenario_results': dict
}
"""
scenario_results = {}

for scenario_type in ScenarioType:
result = self._run_single_scenario(scenario_type, dms_system)
scenario_results[scenario_type.value] = result

# 计算总体结果
total = len(scenario_results)
passed = sum(1 for r in scenario_results.values() if r['passed'])
accuracy = passed / total if total > 0 else 0

return {
'total_scenarios': total,
'passed_scenarios': passed,
'overall_accuracy': accuracy,
'scenario_results': scenario_results
}

def _run_single_scenario(self,
scenario_type: ScenarioType,
dms_system) -> Dict:
"""运行单个场景"""
self.simulator.start_scenario(scenario_type)

# 模拟帧序列
frame_rate = 30
dt = 1.0 / frame_rate

detections = []
warnings = []

while True:
# 生成模拟输入
sim_input = self._generate_simulated_input(scenario_type)

# DMS 检测
detection = dms_system.detect(sim_input)

# 更新场景
result = self.simulator.update(
dt,
detection['detected'],
detection.get('warning_issued', False)
)

detections.append(detection['detected'])

if result['scenario_complete']:
break

# 评估结果
config = SCENARIO_CONFIGS[scenario_type]

# 计算检测准确率
detection_accuracy = sum(detections[-30:]) / 30 if len(detections) >= 30 else 0

# 是否通过
passed = detection_accuracy >= config.pass_accuracy

return {
'scenario': scenario_type.value,
'detection_accuracy': detection_accuracy,
'required_accuracy': config.pass_accuracy,
'passed': passed,
'warning_level': config.warning_level
}

def _generate_simulated_input(self, scenario_type: ScenarioType) -> Dict:
"""生成模拟输入"""
# 根据场景类型生成不同的模拟数据
if scenario_type == ScenarioType.D_01:
return {
'gaze_pitch': -25, # 低头
'gaze_yaw': 0,
'eyes_detected': True
}
elif scenario_type == ScenarioType.F_01:
return {
'eye_closure': 0.9, # 高闭合度
'gaze_pitch': 0,
'gaze_yaw': 0,
'eyes_detected': True
}
else:
return {
'gaze_pitch': 0,
'gaze_yaw': 0,
'eyes_detected': True
}

参考资源