[pilot智驾系统] 模型守护进程(modeld)
第5章:模型守护进程(modeld)
在上一章中,我们看到控制守护进程(controlsd)如何将高层命令转化为精确的转向和踏板输入。
但这些高层命令,特别是期望路径和未来速度,究竟从何而来?sunnypilot
如何"看到"世界并预测接下来会发生什么?
这就是**模型守护进程(modeld)**的职责所在!
将modeld
视为sunnypilot
的**视觉先知**。
它就像一个训练有素的专家,通过车辆摄像头持续观察道路。它不仅能看到当前的情况,还能以闪电般的速度处理这些图像,理解驾驶环境,最重要的是预测未来。
modeld
使用先进的人工智能(特别是神经网络)来预测:
- 车道线位置(以及它们未来的位置)。
- 其他车辆和障碍物的位置(以及它们的移动方向)。
- 车辆未来几秒的路径和速度。
- 潜在的驾驶员行为,比如是否可能尝试接管控制。
这种对车辆周围环境的深入理解和预测,对sunnypilot
做出安全智能的自动驾驶决策至关重要。
为什么需要模型守护进程?
没有modeld
,sunnypilot
将如同盲人,无法预测未来。它不知道道路如何弯曲,交通速度如何变化,或者前方车辆是否在减速。这将无法规划出平稳、安全和高效的驾驶路径。
modeld
为sunnypilot
提供了"眼睛"和"水晶球"。它将原始摄像头像素转化为对驾驶场景的丰富结构化理解,并精确预测未来事件,使sunnypilot
能够有效规划行动。
用例:预测车辆未来路径
想象我们正接近一个平缓的弯道。sunnypilot
需要知道精确的转向角度和速度以平稳通过弯道。
modeld
如何帮助?它通过摄像头持续观察前方道路,识别车道的曲率,并预测车辆未来10秒应遵循的精确路径。它还观察前方车辆的速度并预测其未来移动,帮助sunnypilot
决定最佳加速度。
模型守护进程的工作原理
modeld
在持续循环中运行,尽可能快地处理新的摄像头帧。以下是简化流程:
- 捕获图像:从车辆的主摄像头和广角摄像头接收实时视频帧。
- 输入AI模型:这些图像被输入高度优化的神经网络(AI模型)。这些模型经过数百万英里真实驾驶数据的广泛训练。
- 处理与预测:模型分析像素以识别道路上的一切:车道线、道路边缘、其他车辆(称为"前导车")以及前方道路的整体几何形状。基于此,它们预测车辆的最佳未来路径、速度和加速度。
- 打包输出:
modeld
将所有原始预测组织成易于理解的消息。 - 发布消息:最后,它发布这些消息,主要是
modelV2
、drivingModelData
和cameraOdometry
,供其他sunnypilot
组件使用。
让我们可视化这一流程:
如何使用模型守护进程(与其输出交互)
作为初学者,我们不会直接"使用"modeld
调用其函数。
相反,我们会读取它发布的详细消息,特别是modelV2
消息。此消息包含关于驾驶环境的所有预测。
以下是sunnypilot
其他部分(如控制守护进程(controlsd)或纵向规划器(LongitudinalPlanner))读取这些信息的方式:
import cereal.messaging as messaging
from cereal import log# 创建SubMaster以监听消息
sm = messaging.SubMaster(["modelV2"])# 在运行非常频繁的循环中
while True:sm.update(0) # 检查新消息if sm.updated["modelV2"]:model_output = sm["modelV2"]# 获取转向的期望曲率(来自'action'字段)desired_curvature = model_output.action.desiredCurvatureprint(f"Modeld预测期望曲率: {desired_curvature:.4f} (弧度/米)")# 获取油门/刹车的期望加速度(来自'action'字段)desired_accel = model_output.action.desiredAccelerationprint(f"Modeld预测期望加速度: {desired_accel:.2f} m/s^2")# 检查模型是否认为我们应该停止if model_output.action.shouldStop:print("Modeld提示: 该停车了!")# ... 其他逻辑 ...
这段代码展示了控制守护进程(controlsd)如何从modeld
的modelV2
消息中获取转向的desiredCurvature
,以及纵向规划器(LongitudinalPlanner)如何获取desiredAcceleration
。
这是驱动sunnypilot
路径和速度控制的核心数据。
底层原理:modeld
核心循环
modeld
在selfdrive/modeld/modeld.py
中实现并持续运行。
其主while True
循环负责管理摄像头输入、运行AI模型和发布结果。
让我们看看主循环的简化版本:
# 摘自selfdrive/modeld/modeld.py(简化版)
import cereal.messaging as messaging
from openpilot.selfdrive.modeld.fill_model_msg import fill_model_msg, fill_pose_msg, PublishState
from openpilot.selfdrive.modeld.constants import ModelConstants, Plan
from openpilot.selfdrive.modeld.models.commonmodel_pyx import DrivingModelFrame, CLContext
from openpilot.selfdrive.modeld.parse_model_outputs import Parser
# ... (其他导入) ...class ModelState: # 简化表示# 保存我们的AI模型和缓冲区def run(self, bufs, transforms, inputs, prepare_only):# ... 运行视觉和策略模型的逻辑 ...# 返回从模型解析的输出passdef main():# ... (初始设置) ...model = ModelState(cl_context) # 初始化AI模型状态pm = messaging.PubMaster(["modelV2", "drivingModelData", "cameraOdometry"]) # 发布者sm = messaging.SubMaster(["deviceState", "carState", "roadCameraState", "liveCalibration", "driverMonitoringState", "carControl", "liveDelay"]) # 订阅者publish_state = PublishState() # 帮助管理一些输出数据while True:# 1. 接收最新的摄像头帧(bufs)# 这部分等待来自摄像头守护进程的新视频帧。# (从vipc_client_main和vipc_client_extra接收帧的代码很复杂,此处简化)buf_main, buf_extra = None, None # 假设这些已填充新帧# 2. 更新其他输入(车辆状态、驾驶员意图、校准)sm.update(0) # 从其他守护进程获取最新消息v_ego = max(sm["carState"].vEgo, 0.) # 当前车速desire = DH.desire # 驾驶员意图(如左/右变道)# ... 其他输入如交通规则、校准变换 ...# 3. 为AI模型准备输入vec_desire = np.zeros(ModelConstants.DESIRE_LEN, dtype=np.float32)if desire >= 0 and desire < ModelConstants.DESIRE_LEN:vec_desire[desire] = 1 # 将驾驶员意图转换为向量inputs = {'desire': vec_desire,'traffic_convention': traffic_convention, # 如右侧通行}bufs = {name: buf_extra if 'big' in name else buf_main for name in model.vision_input_names}transforms = {name: model_transform_extra if 'big' in name else model_transform_main for name in model.vision_input_names}# 4. 运行AI模型获取预测model_output = model.run(bufs, transforms, inputs, prepare_only=False)# model_execution_time = ... (测量耗时) ...if model_output is not None:# 5. 从模型输出中提取期望动作(曲率、加速度)action = get_action_from_model(model_output, prev_action, lat_delay + DT_MDL, long_delay + DT_MDL, v_ego)prev_action = action# 6. 填充并发布消息modelv2_send = messaging.new_message('modelV2')drivingdata_send = messaging.new_message('drivingModelData')posenet_send = messaging.new_message('cameraOdometry')fill_model_msg(drivingdata_send, modelv2_send, model_output, action, publish_state,meta_main.frame_id, meta_extra.frame_id, frame_id, frame_drop_ratio,meta_main.timestamp_eof, model_execution_time, live_calib_seen)fill_pose_msg(posenet_send, model_output, meta_main.frame_id, vipc_dropped_frames, meta_main.timestamp_eof, live_calib_seen)pm.send('modelV2', modelv2_send)pm.send('drivingModelData', drivingdata_send)pm.send('cameraOdometry', posenet_send)# ...
这个简化的main
函数展示了modeld
的持续循环:获取输入、运行AI模型(model.run
)、提取"动作"(期望路径/速度)并发布结果。
两个核心AI模型(视觉与策略)
在model.run
内部,modeld
使用两种主要类型的神经网络协同工作:
- 视觉模型:该模型接收原始摄像头图像并从中提取丰富特征。它直接"看到"道路及其上的一切。
- 输入:原始摄像头图像(像素)。
- 输出:
- 车道线与道路边缘:车道线和道路边界的精确位置和概率。
- 前导车:前方车辆的位置、速度和概率。
- 隐藏状态(特征):对道路场景的浓缩"理解",随后输入到下一个模型。
- 策略模型:该模型接收视觉模型的"隐藏状态"(特征),以及一些历史数据和驾驶员意图,以预测未来。
- 输入:视觉模型的隐藏状态、历史特征、驾驶员意图(如尝试变道)。
- 输出:
- 未来路径:车辆未来几秒的预测路径(位置、速度、加速度)。这是主要的"计划"。
- 驾驶员意图:各种驾驶员动作的概率(如踩油门、踩刹车、转向覆盖)。
- 激活预测:由于驾驶员动作或系统限制导致
sunnypilot
退出的概率。
通过拆分任务,sunnypilot
可以高效处理视觉信息,并利用这种理解对车辆未来行为和环境做出复杂预测。
处理模型输出(parse_model_outputs.py
)
这些AI模型的原始输出只是数字数组。selfdrive/modeld/parse_model_outputs.py
中的Parser
类负责将这些数字解释为有意义的预测,如概率和坐标。
它将模型的"猜测"转化为结构化数据。
填充消息(fill_model_msg.py
)
一旦Parser
将原始数字转化为可理解的预测(如车道线随时间变化的X、Y、Z坐标),selfdrive/modeld/fill_model_msg.py
中的函数将这些处理后的数据精心组织到发布的modelV2
、drivingModelData
和cameraOdometry
消息中。
这是预测路径、车道线、前导车和其他信息被组织成其他守护进程期望格式的地方。
推导动作(get_action_from_model
)
关键步骤是从model_output
中提取action
(期望曲率和加速度)。
这在modeld.py
中的get_action_from_model
函数中完成:
# 摘自selfdrive/modeld/modeld.py(简化版)
from openpilot.selfdrive.controls.lib.drive_helpers import get_accel_from_plan, smooth_value, get_curvature_from_plandef get_action_from_model(model_output: dict[str, np.ndarray], prev_action: log.ModelDataV2.Action,lat_action_t: float, long_action_t: float, v_ego: float) -> log.ModelDataV2.Action:plan = model_output['plan'][0] # 获取主要预测路径# 从预测路径计算期望加速度desired_accel, should_stop = get_accel_from_plan(plan[:,Plan.VELOCITY][:,0], # 预测速度plan[:,Plan.ACCELERATION][:,0], # 预测加速度ModelConstants.T_IDXS, # 时间索引action_t=long_action_t) # 加速度的目标时间desired_accel = smooth_value(desired_accel, prev_action.desiredAcceleration, LONG_SMOOTH_SECONDS)# 从预测路径(方向)计算期望曲率desired_curvature = get_curvature_from_plan(plan[:,Plan.T_FROM_CURRENT_EULER][:,2], # 预测偏航角plan[:,Plan.ORIENTATION_RATE][:,2], # 预测偏航率ModelConstants.T_IDXS, # 时间索引v_ego, # 当前速度lat_action_t) # 曲率的目标时间# 对曲率应用平滑处理以实现更平稳的转向if v_ego > MIN_LAT_CONTROL_SPEED:desired_curvature = smooth_value(desired_curvature, prev_action.desiredCurvature, LAT_SMOOTH_SECONDS)else:desired_curvature = prev_action.desiredCurvature # 如果速度太低,保持之前的曲率return log.ModelDataV2.Action(desiredCurvature=float(desired_curvature),desiredAcceleration=float(desired_accel),shouldStop=bool(should_stop))
这个函数非常重要
它从AI模型的详细plan
(预测的未来路径、速度和加速度随时间变化)中提取出单一的desiredCurvature
(用于转向)和desiredAcceleration
(用于油门/刹车)。
它还对这些值应用"平滑"处理,确保sunnypilot
对车辆的命令不会突兀,而是平稳舒适。这两个值desiredCurvature
和desiredAcceleration
是控制守护进程(controlsd)和纵向规划器(LongitudinalPlanner)直接使用的关键输出。
总结
**模型守护进程(modeld)**是sunnypilot
的眼睛和预测大脑。
通过使用复杂的AI模型持续处理摄像头图像,它理解当前的驾驶环境,并准确预测车辆的未来路径、速度和周围障碍物。
然后,它将这些洞察组织成清晰的消息,供其他sunnypilot
组件(如控制守护进程(controlsd)和纵向规划器(LongitudinalPlanner))使用,以安全智能地驾驶车辆。
这是sunnypilot
能够"看到"和"思考"前方道路的基础。
接下来,我们将看到modeld
对未来速度的预测如何被**纵向规划器(LongitudinalPlanner)**用于做出加速和刹车的智能决策。
下一章:纵向规划器(LongitudinalPlanner)