Compare commits

..

8 Commits

Author SHA1 Message Date
discountchubbs 8718bff2f8 fix ty 2026-11-12 14:57:35 -08:00
discountchubbs e8b907c0e2 review fixes 2026-11-12 14:53:53 -08:00
James Vecellio-Grant ab935a039c Update helpers.py 2026-11-12 13:12:46 -08:00
James Vecellio-Grant 6223edb4fe Update frame_fusion.py 2026-11-12 13:11:04 -08:00
discountchubbs 39e204246c bind inputs once 2026-11-12 12:17:36 -08:00
discountchubbs 28b56dd234 in-place frames 2026-11-12 12:09:15 -08:00
Jason Wen 61af0a0512 bump opendbc and update CARS.md 2026-11-12 12:47:54 -05:00
github-actions[bot] 066c0e2957 [bot] Update Python packages (#2087)
* Update Python packages

* no

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Jason Wen <haibin.wen3@gmail.com>
2026-11-11 23:10:58 -05:00
21 changed files with 88 additions and 159 deletions
+1 -1
View File
@@ -42,7 +42,7 @@ jobs:
WARP_PKL="openpilot/sunnypilot/modeld_v2/models/driving_warp_${res}_tinygrad.pkl"
echo "Compiling $WARP_PKL on QCOM"
taskset -c 7 env DEV=QCOM IMAGE=1 FLOAT16=1 NOLOCALS=1 JIT_BATCH_SIZE=0 OPENPILOT_HACKS=1 python3 openpilot/sunnypilot/modeld_v2/compile_warp.py \
--frame ${FRAME_PARAM} --warp-to ${MODEL_SIZE} --output "${WARP_PKL}"
--frame ${FRAME_PARAM} --warp-to ${MODEL_SIZE} --split-frames --output "${WARP_PKL}"
BIG_WARP_PKL="openpilot/sunnypilot/modeld_v2/models/big_driving_warp_${res}_tinygrad.pkl"
echo "Compiling $BIG_WARP_PKL on AMD"
-1
View File
@@ -4,7 +4,6 @@
[submodule "opendbc"]
path = opendbc_repo
url = https://github.com/sunnypilot/opendbc.git
branch = toyotabsmhack
[submodule "msgq"]
path = msgq_repo
url = https://github.com/sunnypilot/msgq.git
+4 -1
View File
@@ -4,7 +4,7 @@
A supported vehicle is one that just works when you install a comma device. All supported cars provide a better experience than any stock system. Supported vehicles reference the US market unless otherwise specified.
# 351 Supported Cars
# 354 Supported Cars
|Make|Model|Supported Package|ACC|No ACC accel below|No ALC below|Steering Torque|Resume from stop|<a href="##"><img width=2000></a>Hardware Needed<br>&nbsp;|Video|Setup Video|
|---|---|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
@@ -153,18 +153,21 @@ A supported vehicle is one that just works when you install a comma device. All
|Hyundai|Sonata 2018-19|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai E connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Sonata 2018-19">Buy Here</a></sub></details>|||
|Hyundai|Sonata 2020-23|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai A connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Sonata 2020-23">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=ix63r9kE3Fw" target="_blank"><img height="18px" src="assets/icon-youtube.svg" /></a>||
|Hyundai|Sonata Hybrid 2020-23|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai A connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Sonata Hybrid 2020-23">Buy Here</a></sub></details>|||
|Hyundai|Sonata Hybrid (without HDA II) 2024-26|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai A connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Sonata Hybrid (without HDA II) 2024-26">Buy Here</a></sub></details>|||
|Hyundai|Staria 2023|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai K connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Staria 2023">Buy Here</a></sub></details>|||
|Hyundai|Tucson 2021|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai L connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Tucson 2021">Buy Here</a></sub></details>|||
|Hyundai|Tucson 2022|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai N connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Tucson 2022">Buy Here</a></sub></details>|||
|Hyundai|Tucson 2023-24|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai N connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Tucson 2023-24">Buy Here</a></sub></details>|||
|Hyundai|Tucson Diesel 2019|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai L connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Tucson Diesel 2019">Buy Here</a></sub></details>|||
|Hyundai|Tucson Hybrid 2022-24|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai N connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Tucson Hybrid 2022-24">Buy Here</a></sub></details>|||
|Hyundai|Tucson Hybrid (without HDA II) 2025-26|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai N connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Tucson Hybrid (without HDA II) 2025-26">Buy Here</a></sub></details>|||
|Hyundai|Tucson Plug-in Hybrid 2024|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai N connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Tucson Plug-in Hybrid 2024">Buy Here</a></sub></details>|||
|Hyundai|Veloster 2019-20|Smart Cruise Control (SCC)|Stock|5 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai E connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Hyundai Veloster 2019-20">Buy Here</a></sub></details>|||
|Jeep|Grand Cherokee 2016-18|Adaptive Cruise Control (ACC)|Stock|0 mph|9 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 FCA connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Jeep Grand Cherokee 2016-18">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=eLR9o2JkuRk" target="_blank"><img height="18px" src="assets/icon-youtube.svg" /></a>||
|Jeep|Grand Cherokee 2019-21|Adaptive Cruise Control (ACC)|Stock|0 mph|39 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 FCA connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Jeep Grand Cherokee 2019-21">Buy Here</a></sub></details>|||
|Kia|Carnival 2022-24|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai A connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia Carnival 2022-24">Buy Here</a></sub></details>|||
|Kia|Carnival (China only) 2023|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai K connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia Carnival (China only) 2023">Buy Here</a></sub></details>|||
|Kia|Carnival Hybrid (with HDA II) 2025-26|Highway Driving Assist II|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai Q connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia Carnival Hybrid (with HDA II) 2025-26">Buy Here</a></sub></details>|||
|Kia|Ceed 2019-21|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai E connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia Ceed 2019-21">Buy Here</a></sub></details>|||
|Kia|Ceed Plug-in Hybrid 2021|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai B connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia Ceed Plug-in Hybrid 2021">Buy Here</a></sub></details>|||
|Kia|EV6 (Southeast Asia only) 2022-24|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Hyundai P connector<br>- 1 OBD-C cable (2 ft)<br>- 1 comma four<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br><a href="https://comma.ai/shop/comma-3x?harness=Kia EV6 (Southeast Asia only) 2022-24">Buy Here</a></sub></details>|||
-1
View File
@@ -245,7 +245,6 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"SubaruStopAndGoManualParkingBrake", {PERSISTENT | BACKUP, BOOL, "0"}},
{"TeslaCoopSteering", {PERSISTENT | BACKUP, BOOL, "0"}},
{"TeslaMadsScreenButton", {PERSISTENT | BACKUP, INT, "0"}},
{"ToyotaEnhancedBsm", {PERSISTENT | BACKUP, BOOL, "0"}},
{"ToyotaEnforceStockLongitudinal", {PERSISTENT | BACKUP, BOOL, "0"}},
{"ToyotaStopAndGoHack", {PERSISTENT | BACKUP, BOOL, "0"}},
{"ToyotaTSS2Smooth", {PERSISTENT | BACKUP, BOOL, "0"}},
@@ -11,17 +11,11 @@ from openpilot.system.ui.lib.multilang import tr, tr_noop
from openpilot.system.ui.widgets import DialogResult
from openpilot.system.ui.widgets.confirm_dialog import ConfirmDialog
from openpilot.system.ui.sunnypilot.widgets.list_view import toggle_item_sp
from opendbc.car.toyota.values import CAR, ToyotaFlags
from opendbc.sunnypilot.car.toyota.values import ToyotaFlagsSP
from opendbc.car.toyota.values import ToyotaFlags
ONROAD_ONLY_DESCRIPTION = tr_noop("Start the vehicle to check vehicle compatibility.")
SNG_HACK_UNAVAILABLE = tr_noop("sunnypilot Longitudinal Control must be available and enabled for your vehicle to use this feature.")
BSM_UNAVAILABLE = tr_noop("Enhanced Blind Spot Monitoring requires a TSS2 Toyota/Lexus without SecOC, " +
"or a smartDSU with sunnypilot longitudinal control.")
BSM_STOCK_LONG_UNAVAILABLE = tr_noop("Enhanced Blind Spot Monitoring is not available while Enforce Factory Longitudinal Control is enabled.")
BSM_REQUIRED = tr_noop("This vehicle does not report blind spot status over the standard CAN signals. " +
"Enhanced Blind Spot Monitoring is required for blind spot detection.")
PLATFORM_UNAVAILABLE = tr_noop("This feature is currently not available on this platform.")
DESCRIPTIONS = {
@@ -32,10 +26,6 @@ DESCRIPTIONS = {
'sunnypilot will allow some Toyota/Lexus cars to auto resume during stop and go traffic. ' +
'This feature is only applicable to certain models that are able to use longitudinal control. This is an alpha feature. Use at your own risk.'
),
'enhanced_bsm': tr_noop(
'sunnypilot will poll the blind spot monitor diagnostics directly from the radar. Use this on vehicles whose blind spot monitors ' +
'do not report blind spot status over the standard CAN signals. This is an alpha feature. Use at your own risk.'
),
'tss2_smooth': tr_noop(
'Enable smoother longitudinal control for TSS2 vehicles. ' +
'Uses a custom speed-dependent integral gain curve for smoother traffic driving.'
@@ -63,14 +53,6 @@ class ToyotaSettings(BrandSettings):
enabled=lambda: not ui_state.engaged,
)
self.enhanced_bsm = toggle_item_sp(
lambda: tr("Enhanced Blind Spot Monitoring (Alpha)"),
description=lambda: tr(DESCRIPTIONS["enhanced_bsm"]),
initial_state=ui_state.params.get_bool("ToyotaEnhancedBsm"),
callback=self._on_enable_enhanced_bsm,
enabled=lambda: not ui_state.engaged,
)
self.tss2_smooth = toggle_item_sp(
lambda: tr("TSS2 Smooth Longitudinal (Alpha)"),
description=lambda: tr(DESCRIPTIONS["tss2_smooth"]),
@@ -82,7 +64,6 @@ class ToyotaSettings(BrandSettings):
self.items = [
self.enforce_stock_longitudinal,
self.stop_and_go_hack,
self.enhanced_bsm,
self.tss2_smooth,
]
@@ -149,25 +130,6 @@ class ToyotaSettings(BrandSettings):
ui_state.params.put_bool("ToyotaStopAndGoHack", False)
ui_state.params.put_bool("OnroadCycleRequested", True)
def _on_enable_enhanced_bsm(self, state: bool):
if state:
def confirm_callback(result: int):
if result == DialogResult.CONFIRM:
ui_state.params.put_bool("ToyotaEnhancedBsm", True)
ui_state.params.put_bool("OnroadCycleRequested", True)
else:
self.enhanced_bsm.action_item.set_state(False)
content = (f"<h1>{self.enhanced_bsm.title}</h1><br>" +
f"<p>{self.enhanced_bsm.description}</p>")
dlg = ConfirmDialog(content, tr("Enable"), rich=True, callback=confirm_callback)
gui_app.push_widget(dlg)
else:
ui_state.params.put_bool("ToyotaEnhancedBsm", False)
ui_state.params.put_bool("OnroadCycleRequested", True)
def update_settings(self):
if ui_state.CP is not None:
longitudinal = ui_state.CP.openpilotLongitudinalControl
@@ -188,33 +150,6 @@ class ToyotaSettings(BrandSettings):
if show_desc:
self.stop_and_go_hack.show_description(True)
# Enhanced BSM needs the radar diagnostic address, which is only in the TX allowlist for
# TSS2 cars without SecOC, or for TSS-P cars with a smartDSU. Both need openpilot long.
sdsu_bsm_supported = (ui_state.CP_SP is not None and
bool(ui_state.CP_SP.flags & ToyotaFlagsSP.SMART_DSU))
bsm_supported = (not bool(ui_state.CP.flags & ToyotaFlags.SECOC) and
(bool(ui_state.CP.flags & ToyotaFlags.TSS2) or sdsu_bsm_supported))
bsm_available = bsm_supported and longitudinal and not enforce_stock
self.enhanced_bsm.action_item.set_enabled(bsm_available and not ui_state.engaged)
bsm_desc = tr(DESCRIPTIONS["enhanced_bsm"])
if not bsm_supported:
self.enhanced_bsm.action_item.set_state(False)
bsm_desc = "<b>" + tr(BSM_UNAVAILABLE) + "</b>\n\n" + bsm_desc
elif enforce_stock:
self.enhanced_bsm.action_item.set_state(False)
bsm_desc = "<b>" + tr(BSM_STOCK_LONG_UNAVAILABLE) + "</b>\n\n" + bsm_desc
elif not longitudinal:
self.enhanced_bsm.action_item.set_state(False)
bsm_desc = "<b>" + tr(SNG_HACK_UNAVAILABLE) + "</b>\n\n" + bsm_desc
elif ui_state.CP.carFingerprint == CAR.TOYOTA_PRIUS_TSS2:
bsm_desc = "<b>" + tr(BSM_REQUIRED) + "</b>\n\n" + bsm_desc
if self.enhanced_bsm.description != bsm_desc:
self.enhanced_bsm.set_description(bsm_desc)
if not bsm_available:
self.enhanced_bsm.show_description(True)
tss2 = bool(ui_state.CP.flags & ToyotaFlags.TSS2)
if longitudinal and not enforce_stock and tss2:
self.tss2_smooth.action_item.set_enabled(not ui_state.engaged)
@@ -238,12 +173,6 @@ class ToyotaSettings(BrandSettings):
self.stop_and_go_hack.set_description(new_desc)
self.stop_and_go_hack.show_description(True)
self.enhanced_bsm.action_item.set_enabled(False)
bsm_desc = "<b>" + tr(ONROAD_ONLY_DESCRIPTION) + "</b>\n\n" + tr(DESCRIPTIONS["enhanced_bsm"])
if self.enhanced_bsm.description != bsm_desc:
self.enhanced_bsm.set_description(bsm_desc)
self.enhanced_bsm.show_description(True)
self.tss2_smooth.action_item.set_enabled(False)
new_desc = "<b>" + tr(ONROAD_ONLY_DESCRIPTION) + "</b>\n\n" + tr(DESCRIPTIONS["tss2_smooth"])
if self.tss2_smooth.description != new_desc:
+12 -4
View File
@@ -17,16 +17,25 @@ def main():
parser.add_argument("--frame", required=True, help="width,height,stride,y_height,uv_height,buffer_size")
parser.add_argument("--warp-to", required=True, help="model input WxH")
parser.add_argument("--output", required=True)
parser.add_argument("--split-frames", action="store_true", help="one input per camera (frame, big_frame), bound to the camera buffers by pointer")
args = parser.parse_args()
from examples.openpilot.compile_warp import make_frame_prepare, parse_frame, parse_size
from tinygrad import Device, Tensor, TinyJit
frame = parse_frame(args.frame)
warp = make_frame_prepare(frame, *parse_size(args.warp_to))
run = TinyJit(lambda input_frame, M_inv: Tensor.stack(*(warp(input_frame[i], M_inv[i]) for i in range(2))), prune=True)
rng = np.random.default_rng(42)
inputs = {"input_frame": Tensor(rng.integers(0, 256, (2, frame.size), dtype=np.uint8), device=Device.DEFAULT).realize(),
"M_inv": Tensor((rng.standard_normal((2, 3, 3)) * 8).astype(np.float32), device=Device.DEFAULT).realize()}
frames = rng.integers(0, 256, (2, frame.size), dtype=np.uint8)
m_inv = Tensor((rng.standard_normal((2, 3, 3)) * 8).astype(np.float32), device=Device.DEFAULT).realize()
if args.split_frames:
run = TinyJit(lambda frame, big_frame, M_inv: Tensor.stack(warp(frame, M_inv[0]), warp(big_frame, M_inv[1])), prune=True)
inputs = {"frame": Tensor(frames[0], device=Device.DEFAULT).realize(), "big_frame": Tensor(frames[1], device=Device.DEFAULT).realize(), "M_inv": m_inv}
specs = {"frame": ((frame.size,), "|u1", Device.DEFAULT), "big_frame": ((frame.size,), "|u1", Device.DEFAULT),
"M_inv": ((2, 3, 3), "<f4", Device.DEFAULT)}
else:
run = TinyJit(lambda input_frame, M_inv: Tensor.stack(*(warp(input_frame[i], M_inv[i]) for i in range(2))), prune=True)
inputs = {"input_frame": Tensor(frames, device=Device.DEFAULT).realize(), "M_inv": m_inv}
specs = {"input_frame": ((2, frame.size), "|u1", Device.DEFAULT), "M_inv": ((2, 3, 3), "<f4", Device.DEFAULT)}
with record_kernel_linear() as recorded:
run(**inputs)
run(**inputs)
@@ -34,7 +43,6 @@ def main():
assert len(recorded) == 1, f"expected one captured kernel list, got {len(recorded)}"
part = serialize_jit(run, recorded[0], check)
PlainJit(*part)
specs = {"input_frame": ((2, frame.size), "|u1", Device.DEFAULT), "M_inv": ((2, 3, 3), "<f4", Device.DEFAULT)}
write_bundle(args.output, {"run": part}, {"input_specs": specs, "metadata": {}})
print(f"Saved warp to {args.output} ({len(part[0]['calls'])} kernels)")
@@ -12,6 +12,8 @@ from functools import partial
from openpilot.common.basedir import BASEDIR
from openpilot.sunnypilot.modeld_v2.helpers import _dtype, read_parts
SPLIT_FRAMES = ("big_frame", "frame")
def pack_layout(shapes: dict[str, tuple], frame_size: int) -> tuple[dict[str, int], int]:
offsets, offset = {}, 0
@@ -39,29 +41,30 @@ def frame_part(warp_path: str, parts: dict, extras: dict) -> dict:
(model, model_blob), (warp, warp_blob) = parts["run"], read_parts(warp_path)[0]["run"]
states = sorted(name for name in extras["input_specs"] if f"next_{name}" in extras["metadata"]["output_shapes"])
host = {name: shape for name, (shape, _, _) in extras["input_specs"].items() if name not in states and name != "new_img"}
frame_size = next(s["numel"] for s in warp["inputs"] if s["name"] == "input_frame") // 2
offsets, packed = pack_layout(host, frame_size)
frame_specs = {s["name"]: s for s in warp["inputs"] if s["name"] in SPLIT_FRAMES}
offsets, packed = pack_layout(host, 0 if frame_specs else next(s["numel"] for s in warp["inputs"] if s["name"] == "input_frame") // 2)
shift, upload, u8 = len(model_blob), len(model["buffers"]) + len(warp["buffers"]), repr(_dtype("dtypes.uint8"))
def view(name: str, nbytes: int, dtype: str) -> dict:
return {"buffer": upload, "offset": offsets[name], "nbytes": nbytes, "dtype": dtype}
outputs = sorted(s["slot"] for s in model["inputs"] if s["name"] is None)
names = sorted(["host", *states])
names = sorted(["host", *states, *frame_specs])
slots = {s["slot"]: names.index(s["name"]) for s in model["inputs"] if s["name"] in states} | \
{slot: len(names) + i for i, slot in enumerate(outputs)}
returned = warp["returns"]["tensors"][0]
model_inputs = {s["slot"]: {"input": slots[s["slot"]]} if s["slot"] in slots else
{k: v for k, v in returned.items() if k != "shape"} | {"buffer": returned["buffer"] + len(model["buffers"])} if s["name"] == "new_img" else
view(s["name"], s["numel"] * _dtype(s["dtype"]).itemsize, s["dtype"]) for s in model["inputs"]}
warp_inputs = {s["slot"]: view("frames" if s["name"] == "input_frame" else "tfm", s["numel"] * _dtype(s["dtype"]).itemsize, s["dtype"])
for s in warp["inputs"]}
warp_inputs = {s["slot"]: {"input": names.index(s["name"])} if s["name"] in frame_specs else
view("frames" if s["name"] == "input_frame" else "tfm", s["numel"] * _dtype(s["dtype"]).itemsize, s["dtype"]) for s in warp["inputs"]}
def remap(calls: list, inputs: dict, buffers: int, lib: int) -> list:
return [call | ({"lib": [call["lib"][0] + lib, call["lib"][1] + lib]} if "lib" in call else {}) |
{"args": [dict(inputs[a["input"]]) if "input" in a else a | {"buffer": a["buffer"] + buffers} for a in call["args"]]} for call in calls]
header = {"input_names": names, "returns": {"kind": "NoneType", "tensors": []}, "check": None,
"inputs": [{"slot": names.index("host"), "name": "host", "dtype": u8, "numel": packed, "device": "NPY"}] +
[s | {"slot": slots[s["slot"]]} for s in model["inputs"] if s["slot"] in slots],
[s | {"slot": slots[s["slot"]]} for s in model["inputs"] if s["slot"] in slots] +
[s | {"slot": names.index(name)} for name, s in frame_specs.items()],
"buffers": model["buffers"] + [b | ({"init": [b["init"][0] + shift, b["init"][1] + shift]} if "init" in b else {}) for b in warp["buffers"]] +
[{"nbytes": packed, "device": model["inputs"][0]["device"], "dtype": u8}],
"calls": [{"kind": "copy", "args": [{"buffer": upload, "offset": 0, "nbytes": packed, "dtype": u8}, {"input": names.index("host")}]}] +
+18 -1
View File
@@ -270,15 +270,32 @@ def _program_uop(call: dict, lib: bytes):
UOp(Ops.SOURCE, arg=""), UOp(Ops.BINARY, arg=lib)), arg=info)
def _input_tensors(kwargs: dict) -> list: # in _prepare_jit_inputs' order
from tinygrad import Tensor
tensors = [t for _, t in sorted(kwargs.items()) if t.__class__ is Tensor]
for value in kwargs.values():
tensors += [t for t in (value.values() if isinstance(value, dict) else value if isinstance(value, (tuple, list)) else ())
if t.__class__ is Tensor and not any(t is y for y in tensors)]
return tensors
class _Jit:
def __init__(self, captured, input_names: list, buffers: list):
self.captured, self.input_names, self.buffers = captured, input_names, buffers
def __call__(self, *args, **kwargs):
import tinygrad.engine.jit as jit
from tinygrad.uop.ops import Ops
# same names, no variables: the binding is the tensors' own uops, without _prepare_jit_inputs' graph rewrites
if not args and self.__dict__.get("_names") == sorted(kwargs):
tensors = _input_tensors(kwargs)
if all(t.uop.is_realized for t in tensors):
return self.captured([t.uop.src[0] if t.uop.op is Ops.UNSHARD else t.uop for t in tensors], {})
input_uops, var_vals, names, _ = jit._prepare_jit_inputs(args, kwargs)
if names != self.input_names:
raise ValueError(f"expected inputs {self.input_names}, got {names}")
if not args and not var_vals:
self._names = sorted(kwargs)
return self.captured(input_uops, var_vals)
@@ -311,7 +328,7 @@ class PlainJit(_Jit):
calls.append(_program_uop(call, bytes(blob[slice(*call["lib"])])).call(*arguments))
else:
calls.append(arguments[0].store_call(arguments[1]))
# stand-ins for JIT inputs: copy staging to GPU is decided from the input buffers, the real ones bind per call
# allocate gpu buffers
stand_ins = {slot: UOp.from_buffer(Buffer(_device(spec["device"], device_map), spec["numel"] * _dtype(spec["dtype"]).itemsize).ensure_allocated(),
_dtype(spec["dtype"])) for slot, spec in inputs.items()}
input_uops = tuple(stand_ins.get(slot, next(iter(stand_ins.values()), None)) for slot in range(max(stand_ins, default=-1) + 1))
@@ -11,7 +11,7 @@ import pickle
import numpy as np
from openpilot.common.basedir import BASEDIR
from openpilot.sunnypilot.modeld_v2.frame_fusion import pack_layout
from openpilot.sunnypilot.modeld_v2.frame_fusion import pack_layout, SPLIT_FRAMES
from openpilot.sunnypilot.modeld_v2.helpers import load_model
from openpilot.sunnypilot.modeld_v2.compile_modeld import (POLICY_INPUTS, derive_frame_skip,
make_split_input_queues, make_supercombo_input_queues)
@@ -169,16 +169,18 @@ class NativeTinygradAdapter(BaseModelAdapter):
self.warp_frame_size = self.frame_copy_size
self._init_common()
self.run_model = self.jits['run']
self.split = [name for name in SPLIT_FRAMES if name in self.run_model.input_names]
self.frame_inputs: dict[str, Tensor] = {}
self.input_queues = {name: Tensor(np.zeros(shape, dtype=dtype), device=self.model_device).realize()
for name, (shape, dtype) in self.input_shapes.items() if name in self.state_pairs}
shapes = {name: shape for name, (shape, _) in self.input_shapes.items() if name not in self.state_pairs and name != 'new_img'}
offsets, packed = pack_layout(shapes, self.warp_frame_size)
offsets, packed = pack_layout(shapes, 0 if self.split else self.warp_frame_size)
self.packed_input = np.zeros(packed, dtype=np.uint8)
self.host = Tensor(self.packed_input, device='NPY')
self.numpy_inputs = {name: np.ndarray(shape, dtype=np.float32, buffer=self.packed_input, offset=offsets[name])
for name, shape in ({'tfm': (2, 3, 3)} | shapes).items()}
self.frames = self.packed_input[offsets['frames']:].reshape(2, self.warp_frame_size)
self.frames = self.packed_input[offsets['frames']:].reshape(2, -1)
self.outputs = {name: Tensor(np.zeros(shape, dtype=dtype), device=device).realize() for name, (shape, dtype, device) in self.jits['output_specs'].items()}
for name, next_name in self.state_pairs.items():
@@ -187,17 +189,29 @@ class NativeTinygradAdapter(BaseModelAdapter):
def copy_frames(self, bufs):
for i, key in enumerate(self._vision_input_names):
if key in bufs:
if key not in bufs:
continue
if not self.split:
data = bufs[key].data if hasattr(bufs[key], 'data') else bufs[key]
np.copyto(self.frames[i], np.frombuffer(data, dtype=np.uint8, count=self.warp_frame_size))
elif (name := ('frame', 'big_frame')[i]) in self.split:
if isinstance(bufs[key], np.ndarray): # cached memory: the GPU would read stale lines in place
self.frame_inputs[name] = Tensor(np.ascontiguousarray(bufs[key][:self.warp_frame_size]), device=self.model_device).realize()
continue
ptr = np.frombuffer(bufs[key].data, dtype=np.uint8).ctypes.data # visionipc buffers are coherent
if (key, ptr) not in self._blob_cache:
self._blob_cache[(key, ptr)] = Tensor.from_blob(ptr, (self.warp_frame_size,), dtype='uint8', device=self.model_device)
self.frame_inputs[name] = self._blob_cache[(key, ptr)]
def reset_warmup_buffers(self) -> None:
self.packed_input[:] = 0
self.frame_inputs.clear()
self._blob_cache.clear()
for key in self.state_pairs:
self.input_queues[key].assign(0).realize()
def run(self):
self.run_model(host=self.host, **self.input_queues, output_buffers=self.outputs)
self.run_model(host=self.host, **self.input_queues, **self.frame_inputs, output_buffers=self.outputs)
return self.outputs['outputs']
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:4b22fd6bcb3a3e8a642e6866449dd9c30c12e694ff0741cad9e34e4b00d8c5fa
size 3698799
oid sha256:d776c72125156b5dd3eb59768409401e28c8a52a3a619cd3b9f02c10a156fda6
size 3699072
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:9a17fd6acece4fab23784ef0f3f8bba28a049e9599e5e9e5ee7b36e4b1d24a6f
size 7924847
oid sha256:a3f13ed12e087817a86afd48b2496c66c4f61a1925065b676b2945e81fabaacd
size 7924864
@@ -144,6 +144,21 @@ class TestPlainArtifact(OpenpilotTestCase):
for name in ("outputs", "next_state_feat_q"):
assert np.array_equal(inputs["output_buffers"][name].numpy(), expected["output_buffers"][name].numpy()), name
split_path = os.path.join(self.tmp.name, "warp_split_tinygrad.pkl")
with mock.patch.object(sys, "argv", ["compile_warp", "--frame", FRAME, "--warp-to", f"{MODEL_W}x{MODEL_H}", "--output", split_path, "--split-frames"]):
compile_warp.main()
split = load_model(path, cache=False, extend=functools.partial(frame_part, split_path))
assert split["run"].input_names == ["big_frame", "frame", "host", "state_feat_q"]
offsets, packed = pack_layout(HOST, 0)
host = np.zeros(packed, np.uint8)
for name, shape in ({"tfm": (2, 3, 3)} | HOST).items():
np.ndarray(shape, np.float32, host, offsets[name])[...] = tfm.numpy() if name == "tfm" else inputs[name].numpy()
out = {"outputs": Tensor.zeros(OUT).contiguous().realize(), "next_state_feat_q": Tensor.zeros(STATE).contiguous().realize()}
split["run"](host=Tensor(host, device="NPY"), frame=Tensor(frames.numpy()[0]).realize(), big_frame=Tensor(frames.numpy()[1]).realize(),
state_feat_q=expected["state_feat_q"], output_buffers=out)
for name in ("outputs", "next_state_feat_q"):
assert np.array_equal(out[name].numpy(), expected["output_buffers"][name].numpy()), name
def test_tinygrad_api_the_loader_reads(self):
import tinygrad.engine.jit as jit
from tinygrad.device import Buffer, TinyELF
@@ -126,7 +126,6 @@ def initialize_params(params) -> list[dict[str, Any]]:
keys.extend([
"ToyotaEnforceStockLongitudinal",
"ToyotaStopAndGoHack",
"ToyotaEnhancedBsm",
"ToyotaTSS2Smooth",
])
+3 -15
View File
@@ -13,7 +13,6 @@ from opendbc.car.subaru.values import CAR as SUBARU_CAR, SubaruFlags
from opendbc.car.toyota.values import CAR as TOYOTA_CAR, ToyotaFlags
from opendbc.sunnypilot.car.interfaces import has_bsm
from opendbc.sunnypilot.car.tesla.values import TeslaFlagsSP
from opendbc.sunnypilot.car.toyota.values import ToyotaFlagsSP
from openpilot.common.params import Params
from openpilot.common.swaglog import cloudlog
from openpilot.common.hardware import HARDWARE
@@ -45,7 +44,6 @@ CAPABILITY_FIELDS = (
"device_type",
"subaru_has_sng",
"hyundai_alpha_long_available",
"toyota_enhanced_bsm_available",
"toyota_tss2",
"custom_model_active",
)
@@ -70,7 +68,6 @@ CAPABILITY_LABELS: dict[str, str] = {
"device_type": "Device type",
"subaru_has_sng": "Subaru Stop-and-Go available",
"hyundai_alpha_long_available": "Hyundai Alpha Longitudinal available",
"toyota_enhanced_bsm_available": "Toyota Enhanced BSM available",
"toyota_tss2": "Toyota TSS2 platform",
"custom_model_active": "Non-Default model selected",
}
@@ -92,7 +89,6 @@ CAR_CAPABILITY_FIELDS = (
"subaru_has_sng",
"hyundai_alpha_long_available",
"toyota_tss2",
"toyota_enhanced_bsm_available",
)
# Explicit defaults for non-boolean capability fields
@@ -108,7 +104,7 @@ def _bundle_field(bundle: dict | None, key: str) -> str:
return bundle.get(key, "") if isinstance(bundle, dict) else ""
def _resolve_brand_capabilities(caps: dict, bundle_platform: str, CP, CP_SP=None) -> None:
def _resolve_brand_capabilities(caps: dict, bundle_platform: str, CP) -> None:
"""Set brand-specific capabilities from bundle platform or CarParams fallback.
Bundle (manual car selection) is a pre-fingerprint approximation.
@@ -142,19 +138,11 @@ def _resolve_brand_capabilities(caps: dict, bundle_platform: str, CP, CP_SP=None
elif brand == "toyota":
if bundle_platform:
try:
flags = TOYOTA_CAR[bundle_platform].config.flags
caps["toyota_tss2"] = bool(flags & ToyotaFlags.TSS2)
caps["toyota_enhanced_bsm_available"] = bool(flags & ToyotaFlags.TSS2) and not bool(flags & ToyotaFlags.SECOC)
caps["toyota_tss2"] = bool(TOYOTA_CAR[bundle_platform].config.flags & ToyotaFlags.TSS2)
except KeyError:
cloudlog.exception(f"capabilities: unknown toyota platform {bundle_platform!r}")
elif CP is not None:
caps["toyota_tss2"] = bool(CP.flags & ToyotaFlags.TSS2)
# same gate as _initialize_toyota: openpilot longitudinal, not SecOC, and either TSS2 or a
# smartDSU. The bundle path can't see 0x2FF or longitudinal, so it stays TSS2-only.
sdsu_bsm = CP_SP is not None and bool(CP_SP.flags & ToyotaFlagsSP.SMART_DSU)
caps["toyota_enhanced_bsm_available"] = (bool(CP.openpilotLongitudinalControl) and
not bool(CP.flags & ToyotaFlags.SECOC) and
(bool(CP.flags & ToyotaFlags.TSS2) or sdsu_bsm))
def generate_capabilities(params: Params | None = None) -> dict:
@@ -229,7 +217,7 @@ def generate_capabilities(params: Params | None = None) -> dict:
if CP is not None:
caps["has_bsm"] = has_bsm(CP, CP_SP)
_resolve_brand_capabilities(caps, bundle_platform, CP, CP_SP)
_resolve_brand_capabilities(caps, bundle_platform, CP)
return caps
@@ -156,7 +156,7 @@ The tables below describe the **compiled** `settings_ui.json` schema — what th
**Enablement rules**: Grayed out (disabled) when rules fail. Frontend shows a contextual badge explaining why.
**Capability fields** (referenced in rules): `has_longitudinal_control`, `has_icbm`, `icbm_available`, `torque_allowed`, `brand`, `pcm_cruise`, `alpha_long_available`, `steer_control_type`, `has_bsm`, `is_release`, `is_sp_release`, `is_development`, `tesla_has_vehicle_bus`, `has_stop_and_go`, `stock_longitudinal`, `toyota_enhanced_bsm_available`
**Capability fields** (referenced in rules): `has_longitudinal_control`, `has_icbm`, `icbm_available`, `torque_allowed`, `brand`, `pcm_cruise`, `alpha_long_available`, `steer_control_type`, `has_bsm`, `is_release`, `is_sp_release`, `is_development`, `tesla_has_vehicle_bus`, `has_stop_and_go`, `stock_longitudinal`
---
@@ -2361,28 +2361,6 @@
}
]
},
{
"key": "ToyotaEnhancedBsm",
"widget": "toggle",
"needs_onroad_cycle": true,
"title": "Enhanced Blind Spot Monitoring (Alpha)",
"description": "sunnypilot will poll the blind spot monitor diagnostics directly from the radar. Use this on vehicles whose blind spot monitors do not report blind spot status over the standard CAN signals. This is an alpha feature. Use at your own risk.",
"enablement": [
{
"type": "not_engaged"
},
{
"type": "capability",
"field": "toyota_enhanced_bsm_available",
"equals": true
},
{
"type": "capability",
"field": "stock_longitudinal",
"equals": false
}
]
},
{
"key": "ToyotaTSS2Smooth",
"widget": "toggle",
@@ -101,21 +101,6 @@ sections:
- type: param
key: ToyotaEnforceStockLongitudinal
equals: false
- key: ToyotaEnhancedBsm
widget: toggle
needs_onroad_cycle: true
title: Enhanced Blind Spot Monitoring (Alpha)
description: sunnypilot will poll the blind spot monitor diagnostics directly from the radar. Use this on vehicles whose
blind spot monitors do not report blind spot status over the standard CAN signals. This is an alpha feature. Use at
your own risk.
enablement:
- $ref: '#/macros/not_engaged'
- type: capability
field: toyota_enhanced_bsm_available
equals: true
- type: capability
field: stock_longitudinal
equals: false
- key: ToyotaTSS2Smooth
widget: toggle
needs_onroad_cycle: true
@@ -70,18 +70,12 @@ class TestOpaquePerBrandFlags(OpenpilotTestCase):
def test_hyundai_alpha_long_available_field_present(self):
assert "hyundai_alpha_long_available" in CAPABILITY_FIELDS
def test_toyota_enhanced_bsm_available_field_present(self):
assert "toyota_enhanced_bsm_available" in CAPABILITY_FIELDS
def test_subaru_has_sng_default_false(self, caps):
assert caps["subaru_has_sng"] is False
def test_hyundai_alpha_long_available_default_false(self, caps):
assert caps["hyundai_alpha_long_available"] is False
def test_toyota_enhanced_bsm_available_default_false(self, caps):
assert caps["toyota_enhanced_bsm_available"] is False
def test_toyota_tss2_field_present(self):
assert "toyota_tss2" in CAPABILITY_FIELDS
@@ -207,7 +207,6 @@ class TestNotEngagedReplacement(OpenpilotTestCase):
"AlphaLongitudinalEnabled",
"ToyotaEnforceStockLongitudinal",
"ToyotaStopAndGoHack",
"ToyotaEnhancedBsm",
"ToyotaTSS2Smooth",
], names=["key"])
def test_offroad_only_replaced_with_not_engaged(self, schema, key):
@@ -286,7 +286,6 @@ class TestKnownVehicleSettings(OpenpilotTestCase):
keys = {i["key"] for i in _brand_items(schema["vehicle_settings"].get("toyota"))}
assert "ToyotaEnforceStockLongitudinal" in keys
assert "ToyotaStopAndGoHack" in keys
assert "ToyotaEnhancedBsm" in keys
assert "ToyotaTSS2Smooth" in keys
def test_tesla_has_coop_steering(self, schema):