[pilot智驾系统] 驾驶员监控守护进程(dmonitoringd)
第7章:驾驶员监控守护进程(dmonitoringd)
在上一章中,我们研究了纵向规划器(LongitudinalPlanner),它精妙地规划车辆的加速和制动。
但当sunnypilot
忙于驾驶时,谁来确保我们——人类驾驶员——保持注意力集中并在需要时随时准备接管?
这就是**驾驶员监控守护进程(dmonitoringd)**的职责所在
将dmonitoringd
视为sunnypilot
的警觉副驾驶或安全监督员。它通过车内面向驾驶员的摄像头持续观察我们,评估注意力水平,检测是否分心甚至困倦。如果发现注意力不足,会触发警报以恢复专注,确保当sunnypilot
需要协助时我们随时准备接管。
为什么需要驾驶员监控守护进程?
尽管sunnypilot
是先进的驾驶辅助系统,但并非完全自动驾驶。
驾驶员始终负有最终责任,必须随时准备介入。没有dmonitoringd
,sunnypilot
无法知晓驾驶员是否在看手机、闭眼或面朝别处,这将带来巨大安全隐患。
dmonitoringd
通过主动监控驾驶员
状态解决这个问题。它确保sunnypilot
始终在"驾驶员完全专注"的前提下运行,如果这个前提不成立,则会提醒驾驶员恢复注意力,甚至为安全考虑解除系统。
用例:检测驾驶员分心
假设我们开启sunnypilot
驾驶时低头看手机几秒钟。
dmonitoringd
如何检测并响应?
- 摄像头输入:驾驶员摄像头持续拍摄视频流
- AI分析:将图像输入强大的AI模型,分析头部姿态、眼球运动和面部特征
- 分心检测:AI检测到头部转向或视线偏离道路
- 意识水平:开始降低"注意力"评分
- 警报:如果注意力评分过低,触发声音警报(如"请注意!")和屏幕视觉警告。若仍无响应,最终可能解除
sunnypilot
驾驶员监控守护进程工作原理
dmonitoringd
以后台持续运行的两个主要阶段工作:
- 驾驶员监控模型(
dmonitoringmodeld
):直接处理原始摄像头图像的"大脑",使用AI模型分析面部、眼睛和头部姿态,输出详细的"驾驶员状态"(如头部俯仰/偏航/旋转角度、眨眼概率等) - 驾驶员监控逻辑(
dmonitoringd
):“决策者”,获取模型的原始驾驶员状态,结合其他信息(如车速、sunnypilot
激活状态),通过规则集确定整体awareness
(注意力水平)并决定是否触发警报
流程可视化:
如何与驾驶员监控守护进程交互(其输出)
作为初学者,我们不会直接"调用"dmonitoringd
函数。其他sunnypilot
组件会监听其发布的driverMonitoringState
消息,该消息包含驾驶员是否分心、当前注意力水平及活动警报等关键信息。
以下是其他组件(如UI状态(UIState)或自动驾驶守护进程(selfdrived))读取该信息的示例:
import cereal.messaging as messaging
from cereal import log # 访问DriverMonitoringState和EventName# 创建SubMaster监听消息
sm = messaging.SubMaster(["driverMonitoringState"])# 在UI的高频循环中
while True:sm.update(0) # 检查新消息if sm.updated["driverMonitoringState"]:dm_state = sm["driverMonitoringState"]# 检查驾驶员是否被检测为分心if dm_state.isDistracted:print("警告:驾驶员分心!")# 查看当前注意力水平(1.0为完全专注,0.0为临界)print(f"驾驶员注意力:{dm_state.awarenessStatus:.2f}")# 检查活动警报for event in dm_state.events:if event.enable: # 仅显示启用的事件if event.name == log.OnroadEvent.EventName.promptDriverDistracted:print(" 警报:提示-请注意!")elif event.name == log.OnroadEvent.EventName.driverDistracted:print(" 警报:红色-立即接管控制!")# ...其他逻辑...
这段代码展示了获取当前驾驶员监控状态的简易方式。基于dm_state.isDistracted
、dm_state.awarenessStatus
和events
列表,UI可以显示警告,自动驾驶守护进程(selfdrived)可以决定是否需要调整sunnypilot
行为或解除控制。
底层原理:驾驶员监控的双子系统
dmonitoringd
实际上由两个协同工作的Python程序组成:
1. dmonitoringmodeld.py
:AI视觉
该程序(selfdrive/modeld/dmonitoringmodeld.py
)专用于运行"观察"驾驶员的神经网络(AI模型):
# 摘自selfdrive/modeld/dmonitoringmodeld.py(简化版)
import cereal.messaging as messaging
from msgq.visionipc import VisionIpcClient, VisionStreamType
# ...其他模型加载和处理导入...# 代表驾驶员监控AI模型
class ModelState:def __init__(self, cl_ctx):# 加载实际AI模型(.pkl文件)self.model_run = pickle.load(f)# ...输入缓冲区设置...def run(self, buf: VisionBuf, calib: np.ndarray, transform: np.ndarray) -> tuple[np.ndarray, float]:# 1. 为AI模型准备摄像头图像(buf)input_img_cl = self.frame.prepare(buf, transform.flatten())# 2. 使用图像和校准数据运行AI模型output = self.model_run(**self.tensor_inputs).contiguous().realize().uop.base.buffer.numpy()return output, # ...执行时间...def main():# ...(初始设置)...model = ModelState(cl_context) # 初始化AI模型vipc_client = VisionIpcClient("camerad", VisionStreamType.VISION_STREAM_DRIVER, True, cl_context) # 连接摄像头pm = messaging.PubMaster(["driverStateV2"]) # 模型输出发布者while True:buf = vipc_client.recv() # 从驾驶员摄像头获取新帧if buf is None:continue# 从liveCalibration消息获取校准# calib = ...# 运行AI模型获取原始预测model_output, gpu_execution_time = model.run(buf, calib, model_transform)# 将原始模型输出转换为Cereal消息并发布pm.send("driverStateV2", get_driverstate_packet(model_output, ...))# ...
dmonitoringmodeld.py
持续从驾驶员摄像头接收帧(vipc_client.recv()
),将这些帧与车辆校准数据一起输入ModelState.run()
方法。该方法执行专用AI模型,输出代表驾驶员面部、视线、眨眼概率等原始预测的数字数组(model_output
),最后打包成driverStateV2
消息发布。
2. dmonitoringd.py
:逻辑引擎
该程序(selfdrive/monitoring/dmonitoringd.py
)是核心逻辑,订阅dmonitoringmodeld
的driverStateV2
消息及其他关键消息,使用DriverMonitoring
类处理所有信息并确定最终的驾驶员注意力状态和警报。
# 摘自selfdrive/monitoring/dmonitoringd.py(简化版)
import cereal.messaging as messaging
from openpilot.common.params import Params
from openpilot.selfdrive.monitoring.helpers import DriverMonitoringdef dmonitoringd_thread():params = Params()pm = messaging.PubMaster(['driverMonitoringState']) # 最终DM状态发布者sm = messaging.SubMaster(['driverStateV2', 'liveCalibration', 'carState', 'selfdriveState', 'modelV2','carControl'], poll='driverStateV2') # 订阅者# 初始化包含所有逻辑的DriverMonitoring类DM = DriverMonitoring(rhd_saved=params.get_bool("IsRhdDetected"))while True:sm.update() # 等待新消息,特别是driverStateV2if not sm.updated['driverStateV2']:continue # 仅当有新驾驶员监控模型输出时继续# 使用所有订阅消息运行主逻辑步骤DM.run_step(sm)# 发布最终driverMonitoringState消息dat = DM.get_state_packet(valid=sm.all_checks())pm.send('driverMonitoringState', dat)# ...定期保存RHD偏好...
dmonitoringd_thread
的while True
循环是主心跳,通过sm.updated['driverStateV2']
持续接收处理后的驾驶员数据
将所有相关信息(来自sm
)传递给DriverMonitoring
类的DM.run_step()
方法,这里是所有决策发生的地方。
DriverMonitoring
辅助类(helpers.py
)
DriverMonitoring
类(selfdrive/monitoring/helpers.py
)包含管理驾驶员注意力和警报的所有复杂逻辑,围绕几个关键概念构建:
awareness
:0.0到1.0的值,代表驾驶员专注程度。检测到分心时降低,专注时升高(或触摸方向盘表示参与时)_set_timers(active_monitoring)
:决定awareness
衰减速度。检测到面部且模型置信度高(active_monitoring=True
)时衰减较慢;未检测到面部或模型不确定时awareness
快速衰减,更快触发警报_update_states(driver_state, ...)
:处理原始driverStateV2
数据:face_orientation_from_net
:将原始神经网络输出转换为易懂的驾驶员头部相对于车辆的横滚、俯仰和偏航角度DriverPose
:跟踪驾驶员头部姿态,使用RunningStatFilter
学习"自然"头部姿态偏移以校准DistractedType
:识别特定分心类型,如DISTRACTED_POSE
(头部转向)、DISTRACTED_BLINK
(闭眼或眨眼过多)或DISTRACTED_E2E
(模型直接"未准备"预测)- 更新
self.face_detected
、self.pose.low_std
(模型对姿态检测的置信度)和self.driver_distracted
_update_events(driver_engaged, op_engaged, ...)
:核心注意力逻辑和警报生成:- 驾驶员分心或模型不确定时递减
self.awareness
- 驾驶员专注时递增
self.awareness
(恢复) - 根据
awareness
水平和预定义阈值(threshold_pre
、threshold_prompt
),添加特定OnroadEvent
(如橙色警报promptDriverDistracted
或红色警报driverDistracted
)到self.current_events
- 管理"终止警报"以防止驾驶员严重分心多次后重新激活
- 驾驶员分心或模型不确定时递减
DRIVE_MONITOR_SETTINGS
:定义驾驶员监控的所有关键阈值和时间,如警报触发时间(_AWARENESS_TIME
、DISTRACTED_TIME
)、面部/眼睛检测概率(_FACE_THRESHOLD
、_EYE_THRESHOLD
)和头部姿态限制(_POSE_PITCH_THRESHOLD
、_POSE_YAW_THRESHOLD
)。这些设置经过精心调校以确保安全
# 摘自selfdrive/monitoring/helpers.py(简化版)
from cereal import log
from openpilot.selfdrive.selfdrived.events import Events
from openpilot.common.filter_simple import FirstOrderFilter
from openpilot.common.stat_live import RunningStatFilterclass DriverMonitoring:def __init__(self, rhd_saved=False, settings=None, always_on=False):self.settings = DRIVER_MONITOR_SETTINGS() if settings is None else settingsself.awareness = 1.0 # 初始完全专注self.driver_distracted = Falseself.driver_distraction_filter = FirstOrderFilter(0., self.settings._DISTRACTED_FILTER_TS, self.settings._DT_DMON)self.pose = DriverPose(self.settings._POSE_OFFSET_MAX_COUNT) # 跟踪头部姿态self.current_events = Events() # 存储活动警报# ...其他初始化...def _update_states(self, driver_state, cal_rpy, car_speed, op_engaged):# 确定是否检测到面部,从原始模型输出计算横滚、俯仰、偏航self.face_detected = driver_state.leftDriverData.faceProb > self.settings._FACE_THRESHOLDself.pose.roll, self.pose.pitch, self.pose.yaw = face_orientation_from_net(driver_state.leftDriverData.faceOrientation, driver_state.leftDriverData.facePosition, cal_rpy)# 基于姿态、眨眼或端到端模型输出检测驾驶员是否分心self.distracted_types = self._get_distracted_types()self.driver_distracted = (DistractedType.DISTRACTED_POSE in self.distracted_types) and self.face_detected and self.pose.low_std# 更新注意力过滤器并校准自然头部姿态if self.face_detected and car_speed > self.settings._POSE_CALIB_MIN_SPEED:self.pose.pitch_offseter.push_and_update(self.pose.pitch)self.pose.yaw_offseter.push_and_update(self.pose.yaw)self.pose.calibrated = self.pose.pitch_offseter.filtered_stat.n > self.settings._POSE_OFFSET_MIN_COUNT# ...(更多模型不确定性和计时器设置逻辑)...def _update_events(self, driver_engaged, op_engaged, standstill, wrong_gear, car_speed):self._reset_events() # 清除前一周期的事件# 如果驾驶员专注且注意力未达最大值,恢复注意力driver_attentive = self.driver_distraction_filter.x < 0.37if (driver_attentive and self.face_detected and self.pose.low_std and self.awareness > 0):self.awareness = min(self.awareness + self.settings._RECOVERY_FACTOR_MIN * self.step_change, 1.)# 如果驾驶员分心,降低注意力certainly_distracted = self.driver_distraction_filter.x > 0.63 and self.driver_distracted and self.face_detectedif certainly_distracted:self.awareness = max(self.awareness - self.step_change, -0.1)# 根据注意力水平添加警报alert = Noneif self.awareness <= 0.:alert = EventName.driverDistracted # 红色警报,解除控制elif self.awareness <= self.settings._DISTRACTED_PROMPT_TIME_TILL_TERMINAL / self.settings._DISTRACTED_TIME:alert = EventName.promptDriverDistracted # 橙色警报elif self.awareness <= self.settings._DISTRACTED_PRE_TIME_TILL_TERMINAL / self.settings._DISTRACTED_TIME:alert = EventName.preDriverDistracted # 绿色警报if alert is not None:self.current_events.add(alert)def get_state_packet(self, valid=True):# 构建并返回driverMonitoringState消息dat = messaging.new_message('driverMonitoringState', valid=valid)# ...填充当前注意力、事件、faceDetected等...return datdef run_step(self, sm):# 由dmonitoringd.py调用以运行主逻辑self._update_states(driver_state=sm['driverStateV2'],cal_rpy=sm['liveCalibration'].rpyCalib,car_speed=sm['carState'].vEgo,op_engaged=sm['selfdriveState'].enabled)self._update_events(driver_engaged=sm['carState'].steeringPressed or sm['carState'].gasPressed,op_engaged=sm['selfdriveState'].enabled,standstill=sm['carState'].standstill,wrong_gear=sm['carState'].gearShifter in [car.CarState.GearShifter.reverse, car.CarState.GearShifter.park],car_speed=sm['carState'].vEgo)
DriverMonitoring
类是dmonitoringd
的核心,使用_update_states
方法从driverStateV2
消息初步理解驾驶员物理状态,然后_update_events
结合车辆数据持续调整awareness
水平并生成适当警报,确保驾驶员始终参与并准备接管。
总结
**驾驶员监控守护进程(dmonitoringd)**是sunnypilot
至关重要的安全组件。
通过专用摄像头持续观察驾驶员并运用先进AI,它确保方向盘后的人保持专注并准备介入。它智能评估分心和困倦,提供及时警报,使sunnypilot
成为更安全、更负责的驾驶辅助系统。
接下来,我们将焦点从驾驶员转回车辆,探索车辆接口(CarInterface)——sunnypilot
实际与车辆通信并理解其状态的方式
下一章:车辆接口(CarInterface)