新增 `DistractionDetectionLevel` 参数以控制驾驶员分心检测的灵敏度等级,并在 `dmonitoringd.py` 和 `helpers.py` 中实现不同等级对应的时间阈值配置。同时更新了相关逻辑以支持动态调整该参数。 fix(toyota): 支持 Toyota Wildlander PHEV 车型接入与控制 增加对 Toyota Wildlander PHEV 的指纹识别、车辆规格定义及接口适配,确保其在 TSS2 平台下的正常运行,并修正部分雷达ACC判断条件。 feat(ui): 优化 Dragonpilot 设置界面选项显示语言一致性 将 Dragonpilot 设置页面中的多个下拉选项文本进行国际化处理,统一使用翻译函数包裹,提升多语言兼容性。 chore(config): 更新 launch 脚本 API 地址并切换 shell 解释器 修改 `launch_openpilot.sh` 使用 `/usr/bin/bash` 作为解释器,并设置自定义 API 与 Athena 服务地址。 refactor(key): 实现 ECU 秘钥提取脚本并写入参数存储 创建 `key.py` 脚本用于通过 UDS 协议从 ECU 提取 SecOC 密钥,并将其保存至系统参数中供后续使用。 docs(vscode): 移除不再使用的终端配置项 清理 `.vscode/settings.json` 文件中过时的 terminal 配置内容。 feat(fonts): 新增中文字体资源文件 添加 `china.ttf` 字体文件以增强 UI 在中文环境下的渲染效果。 build(payload): 添加二进制负载文件 引入新的二进制 payload 文件用于辅助密钥提取流程。
303 lines
12 KiB
Python
303 lines
12 KiB
Python
from cereal import log
|
||
from openpilot.common.params import Params, UnknownKeyName
|
||
from openpilot.system.ui.widgets import Widget
|
||
from openpilot.system.ui.widgets.list_view import multiple_button_item, toggle_item
|
||
from openpilot.system.ui.widgets.scroller import Scroller
|
||
from openpilot.system.ui.widgets.confirm_dialog import ConfirmDialog
|
||
from openpilot.system.ui.lib.application import gui_app
|
||
from openpilot.system.ui.lib.multilang import tr, tr_noop
|
||
from openpilot.system.ui.widgets import DialogResult
|
||
from openpilot.selfdrive.ui.ui_state import ui_state
|
||
import os
|
||
|
||
PERSONALITY_TO_INT = log.LongitudinalPersonality.schema.enumerants
|
||
|
||
# Description constants
|
||
DESCRIPTIONS = {
|
||
"OpenpilotEnabledToggle": tr_noop(
|
||
"Use the openpilot system for adaptive cruise control and lane keep driver assistance. " +
|
||
"Your attention is required at all times to use this feature."
|
||
),
|
||
"DisengageOnAccelerator": tr_noop("When enabled, pressing the accelerator pedal will disengage openpilot."),
|
||
"LongitudinalPersonality": tr_noop(
|
||
"Standard is recommended. In aggressive mode, openpilot will follow lead cars closer and be more aggressive with the gas and brake. " +
|
||
"In relaxed mode openpilot will stay further away from lead cars. On supported cars, you can cycle through these personalities with " +
|
||
"your steering wheel distance button."
|
||
),
|
||
"IsLdwEnabled": tr_noop(
|
||
"Receive alerts to steer back into the lane when your vehicle drifts over a detected lane line " +
|
||
"without a turn signal activated while driving over 31 mph (50 km/h)."
|
||
),
|
||
"AlwaysOnDM": tr_noop("The driver monitoring system can be toggled on/off, but long-term activation is recommended"),
|
||
"DistractionDetectionLevel": tr_noop(
|
||
"Set how sensitive the driver distraction detection should be. " +
|
||
"Strict: Very sensitive, warns on minor distractions. " +
|
||
"Moderate: Balanced between sensitivity and false positives. " +
|
||
"Lenient: Only alerts on clear distractions. " +
|
||
"Off: Disable Driver Distraction Detection and Control."
|
||
),
|
||
'RecordFront': tr_noop("Upload data from the driver facing camera and help improve the driver monitoring algorithm."),
|
||
"IsMetric": tr_noop("Display speed in km/h instead of mph."),
|
||
"RecordAudio": tr_noop("Record and store microphone audio while driving. The audio will be included in the dashcam video in comma connect."),
|
||
"DisableLogging": tr_noop("Disable logging service"),
|
||
"DisableUpdates": tr_noop("Disable update service"),
|
||
}
|
||
|
||
|
||
class TogglesLayout(Widget):
|
||
def __init__(self):
|
||
super().__init__()
|
||
self._params = Params()
|
||
self._is_release = self._params.get_bool("IsReleaseBranch")
|
||
|
||
# param, title, desc, icon, needs_restart
|
||
self._toggle_defs = {
|
||
"OpenpilotEnabledToggle": (
|
||
lambda: tr("Enable openpilot"),
|
||
DESCRIPTIONS["OpenpilotEnabledToggle"],
|
||
"chffr_wheel.png",
|
||
True,
|
||
),
|
||
"ExperimentalMode": (
|
||
lambda: tr("Experimental Mode"),
|
||
"",
|
||
"experimental_white.png",
|
||
False,
|
||
),
|
||
"DisengageOnAccelerator": (
|
||
lambda: tr("Disengage on Accelerator Pedal"),
|
||
DESCRIPTIONS["DisengageOnAccelerator"],
|
||
"disengage_on_accelerator.png",
|
||
False,
|
||
),
|
||
"IsLdwEnabled": (
|
||
lambda: tr("Enable Lane Departure Warnings"),
|
||
DESCRIPTIONS["IsLdwEnabled"],
|
||
"warning.png",
|
||
False,
|
||
),
|
||
"AlwaysOnDM": (
|
||
lambda: tr("Always-On Driver Monitoring"),
|
||
DESCRIPTIONS["AlwaysOnDM"],
|
||
"monitoring.png",
|
||
False,
|
||
),
|
||
"RecordFront": (
|
||
lambda: tr("Record and Upload Driver Camera"),
|
||
DESCRIPTIONS["RecordFront"],
|
||
"monitoring.png",
|
||
True,
|
||
),
|
||
"RecordAudio": (
|
||
lambda: tr("Record and Upload Microphone Audio"),
|
||
DESCRIPTIONS["RecordAudio"],
|
||
"microphone.png",
|
||
True,
|
||
),
|
||
"IsMetric": (
|
||
lambda: tr("Use Metric System"),
|
||
DESCRIPTIONS["IsMetric"],
|
||
"metric.png",
|
||
False,
|
||
),
|
||
"DisableLogging": (
|
||
lambda: tr("Disable Logging"),
|
||
DESCRIPTIONS["DisableLogging"],
|
||
"",
|
||
False,
|
||
),
|
||
"DisableUpdates": (
|
||
lambda: tr("Disable Updates"),
|
||
DESCRIPTIONS["DisableUpdates"],
|
||
"",
|
||
False,
|
||
),
|
||
}
|
||
|
||
self._long_personality_setting = multiple_button_item(
|
||
lambda: tr("Driving Personality"),
|
||
tr(DESCRIPTIONS["LongitudinalPersonality"]),
|
||
buttons=[lambda: tr("Aggressive"), lambda: tr("Standard"), lambda: tr("Relaxed")],
|
||
button_width=255,
|
||
callback=self._set_longitudinal_personality,
|
||
selected_index=self._params.get("LongitudinalPersonality", return_default=True),
|
||
icon="speed_limit.png"
|
||
)
|
||
|
||
self._distraction_detection_level = multiple_button_item(
|
||
lambda: tr("Distraction Detection Level"),
|
||
tr(DESCRIPTIONS["DistractionDetectionLevel"]),
|
||
buttons=[lambda: tr("Strict"), lambda: tr("Moderate"), lambda: tr("Lenient")],
|
||
button_width=255,
|
||
callback=self._set_distraction_detection_level,
|
||
selected_index=self._params.get("DistractionDetectionLevel", return_default=True),
|
||
icon="monitoring.png"
|
||
)
|
||
|
||
self._toggles = {}
|
||
self._locked_toggles = set()
|
||
|
||
# rick - hide them
|
||
if "LITE" in os.environ:
|
||
for key in ['AlwaysOnDM', 'RecordFront', 'RecordAudio']:
|
||
self._toggle_defs.pop(key, None)
|
||
|
||
for param, (title, desc, icon, needs_restart) in self._toggle_defs.items():
|
||
toggle = toggle_item(
|
||
title,
|
||
desc,
|
||
self._params.get_bool(param),
|
||
callback=lambda state, p=param: self._toggle_callback(state, p),
|
||
icon=icon,
|
||
)
|
||
|
||
try:
|
||
locked = self._params.get_bool(param + "Lock")
|
||
except UnknownKeyName:
|
||
locked = False
|
||
toggle.action_item.set_enabled(not locked)
|
||
|
||
# Make description callable for live translation
|
||
additional_desc = ""
|
||
if needs_restart and not locked:
|
||
additional_desc = tr("Changing this setting will restart openpilot if the car is powered on.")
|
||
toggle.set_description(lambda og_desc=toggle.description, add_desc=additional_desc: tr(og_desc) + (" " + tr(add_desc) if add_desc else ""))
|
||
|
||
# track for engaged state updates
|
||
if locked:
|
||
self._locked_toggles.add(param)
|
||
|
||
self._toggles[param] = toggle
|
||
|
||
# insert longitudinal personality after NDOG toggle
|
||
if param == "DisengageOnAccelerator":
|
||
self._toggles["LongitudinalPersonality"] = self._long_personality_setting
|
||
|
||
if param == "AlwaysOnDM":
|
||
# 根据AlwaysOnDM状态动态显示/隐藏分心检测级别
|
||
self._toggles["DistractionDetectionLevel"] = self._distraction_detection_level
|
||
# 初始设置可见性
|
||
self._update_distraction_detection_visibility()
|
||
|
||
self._update_experimental_mode_icon()
|
||
self._scroller = Scroller(list(self._toggles.values()), line_separator=True, spacing=0)
|
||
|
||
ui_state.add_engaged_transition_callback(self._update_toggles)
|
||
|
||
def _update_state(self):
|
||
if ui_state.sm.updated["selfdriveState"]:
|
||
personality = PERSONALITY_TO_INT[ui_state.sm["selfdriveState"].personality]
|
||
if personality != ui_state.personality and ui_state.started:
|
||
self._long_personality_setting.action_item.set_selected_button(personality)
|
||
ui_state.personality = personality
|
||
|
||
def show_event(self):
|
||
self._scroller.show_event()
|
||
self._update_toggles()
|
||
|
||
def _update_toggles(self):
|
||
ui_state.update_params()
|
||
|
||
e2e_description = tr(
|
||
"openpilot defaults to driving in chill mode. Experimental mode enables alpha-level features that aren't ready for chill mode. " +
|
||
"Experimental features are listed below:<br>" +
|
||
"<h4>End-to-End Longitudinal Control</h4><br>" +
|
||
"Let the driving model control the gas and brakes. openpilot will drive as it thinks a human would, including stopping for red lights and stop signs. " +
|
||
"Since the driving model decides the speed to drive, the set speed will only act as an upper bound. This is an alpha quality feature; " +
|
||
"mistakes should be expected.<br>" +
|
||
"<h4>New Driving Visualization</h4><br>" +
|
||
"The driving visualization will transition to the road-facing wide-angle camera at low speeds to better show some turns. " +
|
||
"The Experimental mode logo will also be shown in the top right corner."
|
||
)
|
||
|
||
if ui_state.CP is not None:
|
||
if ui_state.has_longitudinal_control:
|
||
self._toggles["ExperimentalMode"].action_item.set_enabled(True)
|
||
self._toggles["ExperimentalMode"].set_description(e2e_description)
|
||
self._long_personality_setting.action_item.set_enabled(True)
|
||
else:
|
||
# no long for now
|
||
self._toggles["ExperimentalMode"].action_item.set_enabled(False)
|
||
self._toggles["ExperimentalMode"].action_item.set_state(False)
|
||
self._long_personality_setting.action_item.set_enabled(False)
|
||
self._params.remove("ExperimentalMode")
|
||
|
||
unavailable = tr("Experimental mode is currently unavailable on this car since the car's stock ACC is used for longitudinal control.")
|
||
|
||
long_desc = unavailable + " " + tr("openpilot longitudinal control may come in a future update.")
|
||
if ui_state.CP.alphaLongitudinalAvailable:
|
||
if self._is_release:
|
||
long_desc = unavailable + " " + tr("An alpha version of openpilot longitudinal control can be tested, along with " +
|
||
"Experimental mode, on non-release branches.")
|
||
else:
|
||
long_desc = tr("Enable the openpilot longitudinal control (alpha) toggle to allow Experimental mode.")
|
||
|
||
self._toggles["ExperimentalMode"].set_description("<b>" + long_desc + "</b><br><br>" + e2e_description)
|
||
else:
|
||
self._toggles["ExperimentalMode"].set_description(e2e_description)
|
||
|
||
self._update_experimental_mode_icon()
|
||
|
||
# TODO: make a param control list item so we don't need to manage internal state as much here
|
||
# refresh toggles from params to mirror external changes
|
||
for param in self._toggle_defs:
|
||
self._toggles[param].action_item.set_state(self._params.get_bool(param))
|
||
|
||
# these toggles need restart, block while engaged
|
||
for toggle_def in self._toggle_defs:
|
||
if self._toggle_defs[toggle_def][3] and toggle_def not in self._locked_toggles:
|
||
self._toggles[toggle_def].action_item.set_enabled(not ui_state.engaged)
|
||
|
||
def _render(self, rect):
|
||
self._scroller.render(rect)
|
||
|
||
def _update_distraction_detection_visibility(self):
|
||
"""根据AlwaysOnDM状态更新分心检测级别的可见性"""
|
||
always_on_dm_enabled = self._params.get_bool("AlwaysOnDM")
|
||
if "DistractionDetectionLevel" in self._toggles:
|
||
# 设置分心检测级别的可见性
|
||
self._toggles["DistractionDetectionLevel"].set_visible(always_on_dm_enabled)
|
||
|
||
def _update_experimental_mode_icon(self):
|
||
icon = "experimental.png" if self._toggles["ExperimentalMode"].action_item.get_state() else "experimental_white.png"
|
||
self._toggles["ExperimentalMode"].set_icon(icon)
|
||
|
||
def _handle_experimental_mode_toggle(self, state: bool):
|
||
confirmed = self._params.get_bool("ExperimentalModeConfirmed")
|
||
if state and not confirmed:
|
||
def confirm_callback(result: int):
|
||
if result == DialogResult.CONFIRM:
|
||
self._params.put_bool("ExperimentalMode", True)
|
||
self._params.put_bool("ExperimentalModeConfirmed", True)
|
||
else:
|
||
self._toggles["ExperimentalMode"].action_item.set_state(False)
|
||
self._update_experimental_mode_icon()
|
||
|
||
# show confirmation dialog
|
||
content = (f"<h1>{self._toggles['ExperimentalMode'].title}</h1><br>" +
|
||
f"<p>{self._toggles['ExperimentalMode'].description}</p>")
|
||
dlg = ConfirmDialog(content, tr("Enable"), rich=True)
|
||
gui_app.set_modal_overlay(dlg, callback=confirm_callback)
|
||
else:
|
||
self._update_experimental_mode_icon()
|
||
self._params.put_bool("ExperimentalMode", state)
|
||
|
||
def _toggle_callback(self, state: bool, param: str):
|
||
if param == "ExperimentalMode":
|
||
self._handle_experimental_mode_toggle(state)
|
||
return
|
||
|
||
self._params.put_bool(param, state)
|
||
if self._toggle_defs[param][3]:
|
||
self._params.put_bool("OnroadCycleRequested", True)
|
||
|
||
# 如果切换的是AlwaysOnDM,更新分心检测级别的可见性
|
||
if param == "AlwaysOnDM":
|
||
self._update_distraction_detection_visibility()
|
||
|
||
def _set_longitudinal_personality(self, button_index: int):
|
||
self._params.put("LongitudinalPersonality", button_index)
|
||
|
||
def _set_distraction_detection_level(self, button_index: int):
|
||
self._params.put("DistractionDetectionLevel", button_index)
|