[pilot智驾系统] docs | 用户界面状态(UIState)
链接:https://docs.sunnypilot.ai/
docs:sunnypilot
sunnypilot
是一个开源自动驾驶系统,能够增强汽车的各项功能。
它作为副驾驶,通过解析
车辆数据和摄像头画面来预测
路况和驾驶员行为。
系统随后生成
精确的转向和速度指令,将其转换为汽车能理解的操作指令,同时持续监控
驾驶员注意力以确保行车安全与专注度。
所有运行数据和设置都通过*用户友好界面*进行持续管理和展示
。
可视化
章节列表
- 用户界面状态(UIState)
- 参数系统
- 自动驾驶守护进程(selfdrived)
- 控制守护进程(controlsd)
- 模型守护进程(modeld)
- 纵向规划器(LongitudinalPlanner)
- 驾驶员监控守护进程(dmonitoringd)
- 车辆接口(CarInterface)
- 横向控制(LatControl)
- Panda守护进程(pandad)
第1章:用户界面状态(UIState)
想象我们正在使用sunnypilot
自动驾驶系统开车。看着屏幕,上面显示着大量信息:当前车速、sunnypilot
是否处于激活驾驶状态(engaged)、各类警告提示、或是系统仍在初始化准备中。
所有这些信息都在实时变化。屏幕如何知道在任何特定时刻该显示什么内容?
这就是**用户界面状态(UIState)**发挥作用的地方!
将UI State视为一块共享的数字白板。sunnypilot
系统中所有收集信息
的模块(如传感器、车辆数据、驾驶逻辑)都会将它们最重要的发现写在这块白板上。
然后,负责在屏幕上绘制内容的部分(用户界面,即UI)只需查看这块白板就能知道需要显示什么内容。
它是UI所需展示所有信息的唯一"真相来源",确保我们始终能看到关于sunnypilot
当前状态的最新信息。
为什么需要UI State?
如果没有统一的UI State,屏幕上的每个单独元素(速度显示、警告信息、驾驶模式指示器)都需要独立获取车辆状态。这就像一个大团队中的每个人都试图在不同时间从不同人那里获取更新——混乱且低效
UI State通过创建一个集中存放所有关键显示信息的地方来解决这个问题。
这使得任何UI元素都能轻松获取正确渲染自身所需的数据。
用例:显示sunnypilot
的驾驶状态
举个常见例子:了解sunnypilot
当前是已激活(actively helping you drive)还是未激活(not actively controlling the car)。UI需要清晰地显示这一点,比如通过改变屏幕背景颜色。
UI如何知道是否处于激活状态?它只需查看UI State!
如何使用UI State
UIState
对象始终可供UI的不同部分读取。
在Python中,我们可以导入ui_state
并访问其属性。
假设我们想知道sunnypilot
是否已激活,以决定背景颜色。
# 摘自selfdrive/ui/layouts/main.py(简化版)
from openpilot.selfdrive.ui.ui_state import ui_state, UIStatusdef get_screen_background_color():"""检查UI State以确定适当的屏幕背景颜色"""current_status = ui_state.status # 从UI State读取当前状态if current_status == UIStatus.ENGAGED:return "💚 绿色(已激活)"elif current_status == UIStatus.OVERRIDE:return "💛 黄色(覆盖中)"else: # UIStatus.DISENGAGEDreturn "💙 蓝色(未激活)"# 假设每次屏幕需要更新时都会调用此函数:
print(f"屏幕背景应为:{get_screen_background_color()}")# 如果sunnypilot变为激活状态,ui_state.status会改变,
# 此函数将返回"💚 绿色(已激活)"
这段简短代码展示了UI元素(如背景)如何简单地向ui_state
询问当前status
。根据答案,它可以向驾驶员显示正确的颜色。这是UI保持更新的非常简洁直接的方式。
底层原理:UI State如何更新
那么这块神奇的ui_state
白板是如何获取信息的?
UIState
对象持续监听来自sunnypilot
其他部分的"消息"。这些消息就像传递的小纸条,包含关于车辆、驾驶系统等的最新信息。当UIState
收到新消息时,它会更新自己的内部变量以反映最新情况。
以下是这个过程的简化序列:
- 其他守护进程发送更新:
sunnypilot
中的各种后台程序(称为"守护进程")持续监控车辆速度、是否踩油门、或sunnypilot
的驾驶逻辑(Selfdrive守护进程(selfdrived))是否活跃等信息。它们将这些信息作为消息发送出去。 - UIState监听:
UIState
有一个特殊的"监听器"(称为SubMaster
)等待这些消息。 - UIState处理:当
UIState
收到新消息时,它会读取信息并更新自己的属性,如started
、ignition
、status
和engaged
。 - UI元素读取:屏幕上的元素(如背景、速度显示、警告)然后从
UIState
读取这些更新的属性以正确绘制自己。
代码解析
在sunnypilot
中,UIState
主要在Python(用于sunnypilot
UI框架)和C++(用于较旧的openpilot
UI框架)中实现。为简单起见,我们将重点介绍Python版本,因为它清晰地展示了这个概念。
UIState
类被设计为"单例"(singleton),这意味着任何时候都只有一个实例在运行。
确保每个人都在查看同一块白板。
# 摘自selfdrive/ui/ui_state.py(简化版)
import cereal.messaging as messaging
from enum import Enum # 用于定义状态类型class UIStatus(Enum):DISENGAGED = "disengaged"ENGAGED = "engaged"OVERRIDE = "override"class UIState:_instance: 'UIState | None' = None # 确保只有一个实例的内部变量def __new__(cls):if cls._instance is None:cls._instance = super().__new__(cls)cls._instance._initialize() # 仅调用一次初始化器return cls._instancedef _initialize(self):# 这是"监听器",接收来自sunnypilot其他部分的消息self.sm = messaging.SubMaster(["deviceState", # 告诉我们设备是否已"启动""selfdriveState", # 告诉我们openpilot是否启用/激活"pandaStates", # 告诉我们车辆点火状态等# ... 许多其他消息 ...])self.status: UIStatus = UIStatus.DISENGAGEDself.started: bool = Falseself.ignition: bool = False# ... 其他UI相关数据 ...# 全局'ui_state'对象,每个人都可以导入和使用:
# ui_state = UIState() # 这行代码实际创建单例实例
_initialize
方法设置了SubMaster
(self.sm
),这是监听来自其他守护进程消息的关键组件。它定义了UIState
感兴趣的所有不同类型消息。它还初始化了重要变量如status
、started
和ignition
。
接下来,update
方法会被非常频繁地调用(每秒多次)以保持UIState
最新。
# 摘自selfdrive/ui/ui_state.py(简化版)
class UIState:# ... (之前的代码) ...def update(self) -> None:# 1. 检查来自所有来源的新消息self.sm.update(0)# 2. 根据这些消息更新我们的内部状态变量self._update_state()# 3. 确定整体UI状态self._update_status()# 4. 更新设备特定内容如屏幕亮度device.update()
update
方法是UIState
的核心。它首先调用self.sm.update(0)
读取任何新消息。然后,它调用两个重要的辅助方法:_update_state
和_update_status
。
以下是_update_state
的简化工作原理:
# 摘自selfdrive/ui/ui_state.py(简化版)
import cereal.messaging as messaging
from cereal import log # 用于log.PandaState.PandaTypeclass UIState:# ... (之前的代码) ...def _update_state(self) -> None:# 是否有新的'pandaStates'消息到达?if self.sm.updated["pandaStates"]:panda_states = self.sm["pandaStates"]if len(panda_states) > 0:# 检查任何连接的panda是否有点火信号self.ignition = any(state.ignitionLine or state.ignitionCan for state in panda_states)# 是否有新的'deviceState'消息到达?if self.sm.updated["deviceState"]:# 根据设备状态更新我们的'started'变量self.started = self.sm["deviceState"].started and self.ignition
在_update_state
中,UIState
检查特定消息是否已更新。
例如,它检查pandaStates
看车辆是否点火,检查deviceState
看整个sunnypilot
系统是否被视为"已启动"。它结合这些来设置self.started
标志。
最后,_update_status
确定可见的UI状态(已激活、未激活、覆盖中):
# 摘自selfdrive/ui/ui_state.py(简化版)
from cereal import log # 用于log.SelfdriveState.OpenpilotStateclass UIState:# ... (之前的代码) ...def _update_status(self) -> None:# 如果有新的'selfdriveState'消息到达,更新UI状态if self.started and self.sm.updated["selfdriveState"]:ss = self.sm["selfdriveState"] # 获取selfdrive状态数据state = ss.state # 获取特定状态值(如preEnabled)if state in (log.SelfdriveState.OpenpilotState.preEnabled, log.SelfdriveState.OpenpilotState.overriding):self.status = UIStatus.OVERRIDE # 驾驶员正在覆盖else:# 如果selfdrive启用,我们就是已激活,否则未激活self.status = UIStatus.ENGAGED if ss.enabled else UIStatus.DISENGAGED# ... (更多关于在途/离途转换的逻辑) ...
_update_status
方法对我们的例子至关重要。
它读取selfdriveState
消息。如果sunnypilot
是"enabled"(意味着它正在主动驾驶),self.status
属性被设为UIStatus.ENGAGED
。
如果驾驶员正在覆盖,则是UIStatus.OVERRIDE
。这就是前面背景颜色例子获取信息的方式!
虽然sunnypilot
中的主要UI逻辑使用Python,但基础项目openpilot
也有C++实现的UIState
(selfdrive/ui/ui.cc
和selfdrive/ui/ui.h
),其目的相同。两个版本都充当UI数据的中心枢纽。
总结
**用户界面状态(UIState)**就像是sunnypilot
显示系统的中枢神经系统。
它充当一块持续更新的白板,为所有UI元素提供必要信息,向驾驶员准确展示sunnypilot
正在做什么。
通过集中这些数据,sunnypilot
确保用户界面始终一致、响应迅速且易于理解。(实现了ui和date的解耦,通过中端实现)
下一章,我们将了解**参数系统**,这是帮助sunnypilot
管理持久设置和配置数据的另一个基本概念。
下一章:参数系统