[pilot智驾系统] docs | 用户界面状态(UIState)
sunnypilot是一款开源自动驾驶系统,通过集成车辆数据和摄像头信息实现智能驾驶辅助。该系统包含用户界面状态(UIState)、自动驾驶守护进程(selfdrived)、控制守护进程(controlsd)等核心模块,采用集中式架构管理驾驶状态。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, UIStatus
def 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.DISENGAGED
return "💙 蓝色(未激活)"
# 假设每次屏幕需要更新时都会调用此函数:
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._instance
def _initialize(self):
# 这是"监听器",接收来自sunnypilot其他部分的消息
self.sm = messaging.SubMaster(
[
"deviceState", # 告诉我们设备是否已"启动"
"selfdriveState", # 告诉我们openpilot是否启用/激活
"pandaStates", # 告诉我们车辆点火状态等
# ... 许多其他消息 ...
]
)
self.status: UIStatus = UIStatus.DISENGAGED
self.started: bool = False
self.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.PandaType
class 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.OpenpilotState
class 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管理持久设置和配置数据的另一个基本概念。
火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。
更多推荐

所有评论(0)