Compare commits

...

79 Commits

Author SHA1 Message Date
rav4kumar 0864c045e3 Refactor slope and interpolation logic into a utility module
Moved `compute_symmetric_slopes` and `hermite_interpolate` functions from the dynamic personality controller to a shared utility module. Updated imports and references accordingly to improve code reusability and separation of concerns.
2025-05-12 22:14:24 -07:00
rav4kumar 8f72285c25 ci told me to fix it 2025-05-12 22:14:24 -07:00
rav4kumar 97568623e9 Add unit tests for DynamicPersonalityController methods
This commit introduces pytest-based tests for the DynamicPersonalityController, covering slope computations, Hermite interpolation, and dynamic follow distance calculations. It ensures accuracy across different personalities (relaxed, standard, aggressive) and validates behavior for edge cases like invalid input or speed impact.
2025-05-12 22:14:24 -07:00
rav4kumar 3563852b62 Remove unused dynamic personality parameter handling from stock longitudinal planner; add dynamic personality parameter refresh in sunnypilot longitudinal planner 2025-05-12 22:14:24 -07:00
rav4kumar 38e97ab582 dynamic personality 2025-05-12 22:14:22 -07:00
Nayan 5c9ce2a042 ui: display actual fingerprint name with auto-fingerprint (#908)
* use platform directly

* Revert "use platform directly"

This reverts commit b71c315d30.

* update brand from platform package
2025-05-10 23:35:02 -04:00
Jason Wen 3a4c74b67d Revert "ui: display actual fingerprint name with auto-fingerprint" (#907)
Revert "ui: display actual fingerprint name with auto-fingerprint (#906)"

This reverts commit 921b51f56f.
2025-05-10 21:47:24 -04:00
Nayan 4ceb1ecd19 ui: fix description margins on AbstractControlSP_SELECTOR (#902)
fix margins for description on AbstractControlSP_SELECTOR

Co-authored-by: Jason Wen <haibin.wen3@gmail.com>
2025-05-10 19:10:15 -04:00
Nayan 921b51f56f ui: display actual fingerprint name with auto-fingerprint (#906)
use platform directly

Co-authored-by: Jason Wen <haibin.wen3@gmail.com>
2025-05-10 19:01:37 -04:00
Jason Wen 6c1edca806 MADS: use low velocity threshold for silent wrong gear checks (#901)
* MADS: use low velocity threshold for silent wrong gear checks

* higher
2025-05-10 18:51:37 -04:00
Jason Wen bf6123c4ad Reapply "Hyundai: custom longitudinal tuning" (#894)
* Reapply "Hyundai: custom longitudinal tuning" (#892)

This reverts commit 68c593db5f.

* fix panel behavior

* dynamic description

* try to merge and use the same scrollview

* fix for all to update

* dynamic update and fix description

* minimize changes for all brands

* init

* minimize changes for all brands

* more

* even less diff

* more

* even less diff

* more less

* wow srsly

* more less

* wow srsly
2025-05-10 12:58:08 -04:00
Jason Wen 8d06444bdd ui: consolidate panel updates at a single location (#904)
* init

* minimize changes for all brands

* more

* even less diff

* more less

* wow srsly
2025-05-10 12:46:02 -04:00
Jason Wen 08efc252ec ui: fix supported brand settings list (#905) 2025-05-10 12:34:58 -04:00
DevTekVE 6142a52de7 models: aligning naming on modeld_v2 with upstream naming (#903)
Refactor variable names for clarity in 20Hz model logic.

Renamed `full_features_20Hz` to `full_features_buffer` and `desire_20Hz` to `full_desire` for better readability and consistency. This improves code maintainability and aligns variable names with their intended purpose.
2025-05-10 09:46:54 +02:00
Jason Wen fb934247a2 ui: brand settings for Vehicle panel (#898)
* init

* brand factory

* more

* mazda

* nissan

* rivian

* subaru

* tesla

* toyota

* volkswagen

* do this

* stretch here
2025-05-09 23:09:27 -04:00
Nayan 897a2bcedc ui: update Quiet Mode button behavior (#880)
* fix style handling

* vector them

---------

Co-authored-by: Jason Wen <haibin.wen3@gmail.com>
2025-05-09 22:50:52 -04:00
Jason Wen f4c7b04682 ui: encapsulate always enabled buttons into a list in Device panel (#900)
* fix style handling

* vector them

* another pr

---------

Co-authored-by: nayan8teen <nayan8teen@gmail.com>
2025-05-09 22:40:23 -04:00
Nayan e5ac7d5b57 UI: Support inline layout for ButtonParamControlSP (#888)
* support inline layout for ButtonParamControlSP

* final

---------

Co-authored-by: Jason Wen <haibin.wen3@gmail.com>
2025-05-09 22:19:14 -04:00
Jason Wen e89d65b516 ui: fix screens for UI Preview (#899)
* ui: fix screens for UI Preview

* try a delay
2025-05-09 22:08:32 -04:00
Jason Wen ca23bb90cd ui: Platform Selector updates (#897)
* init

* more

* description

* init them to true

* back to false

* fix

* add description
update colors
fix stretch

* yellow & orange are too similar - use blue

* slight updates

* dynamic description

* split it out

* more

---------

Co-authored-by: nayan8teen <nayan8teen@gmail.com>
2025-05-09 18:29:33 -04:00
Jason Wen 80f21949a3 ui: Adjust stretch factor for ListWidgetSP (#896) 2025-05-09 17:42:31 -04:00
Jason Wen 68c593db5f Revert "Hyundai: custom longitudinal tuning" (#892)
* Revert "Hyundai: custom longitudinal tuning (#658)"

This reverts commit 72f09ec9f5.

* bump

* Revert "bump"

This reverts commit a437f3538d.

* bump
2025-05-09 14:53:56 -04:00
Jason Wen 179da5d007 Update Python packages (#893) 2025-05-09 12:57:58 -04:00
Nayan 6de9526d4d bug: fix braking issue while experimental and using old model (#881)
* Fixes an issue on the long planner since Tomb Raider models, where the models are now meant to output the acceleration target and the "should stop" instead of it being calculated. However, older models (particularly those running on modeld_v2 from SP) do not output this. Leading to a "coasting" situation instead of braking when only e2e is used which is totally wrong.
2025-05-09 11:37:59 +02:00
Nayan f09a3b32d1 UI: Fix Padding on OptionControlSP (#886)
fix padding

Co-authored-by: Jason Wen <haibin.wen3@gmail.com>
2025-05-09 01:35:10 -04:00
Discountchubbs 4a094ef56f Models: Enable support for liveDelay in modeld_v2 (#879)
Enable support for liveDelay in modeld_v2
2025-05-09 01:19:35 -04:00
Discountchubbs 72f09ec9f5 Hyundai: custom longitudinal tuning (#658)
* Maybe I was asleep but somehow it worked.

* Maybe I was asleep but somehow it worked.

* 70/30 split

* 60/35 split

* 67:33 split

* use Sqrt for tiny values to increase factor. Oh, also add a test file.

* use Sqrt for tiny values to increase factor. Oh, also add a test file.

* use Sqrt for tiny values to increase factor. Oh, also add a test file.

* Raise these to restart tests

* Raise these to restart tests

* Update jerk calculation mimicking j_ego math.

* README.md

* Update timestep

* Fix test

* Readability

* Lower time

* Add higher limits for CANFD

* Send these as a floating object. update comments to add TODO msg.

* Conditional flags

* yes its a little weird but heres why:

` "aReqValue": long_state.accel_value if enabled else a_val`

its only to not have merge conflicts with syncs from this file, because if not enabled a_val is already at 0 anyways.

* Make it easier to read

* Chronological order

* Trigger rebuild

* This is an internal test on my end. Disregard, for now

* fw version for my car...

* remove fw version as that was a test

* bump submodules

* refactor that while passing internal tests

* bump safety mutation test

* all should match 50 Hz

* bump safety mutation test

* match stock behavior for accel

* do our own clipping

* apply upper/lower dynamically

* bump test

* bump test

* Higher lower limit

* move desired accel force zero in tuning controller

* bump

* bump

* abs it here

* fix logic

* make sure it resets to 0, enforced in safety

* Test a few things

* Higher jerk for VOACC

* Elantra specific

* raise `off` to stock

* bump opendbc test

* allow jerk calculations at all times

* bump

* bump

* match aReqVale

* we dont need such high accel jerk when above 20m/s. This is cruise control, not a racecar.

* 0.45 default long actuator delay

* align type hint

* add lower jerk multiplier for HYUNDAI_IONIQ

* Change min jerk

* should be float

* Change min jerk

* Change min jerk upper

* Change max jerk upper

* Change max jerk upper

* Change max jerk upper

* step + threshold + first order filter to smooth

* constants

* nah

* multiplier to 1.5

* step updates for parabolic accel/braking

* step updates for parabolic accel/braking

* even less

* test multiplier

* Merge remote-tracking branch 'sunnypilot/opendbc/master-new' into HKG-long-tune

* back to default delay for now

* update test

* update config

* update test

* update config.py

* update test

* Remove stoppingDecelRate **No need for this anymore**

* multiplier to 1.0

* Variable lower dependency

* Variable lower dependency

* Variable lower jerk minimum

* Variable lower jerk minimum
planned_accel -> desired_acel

* Revert some scaling

* .01 here

* .01 here

* try even smoother stopping

* missed a `tuning` here that instead calls itself to inf.

* what is math

* Red Diff

* Red Diff

* bring some back

* bring some back

* stronger when needed

* something simple

* simplify more

* simplify more

* dont use future

* whoops

* why

* reset

* Refactor longitudinal controller for better jerk handling

Revised `calculate_a_value` and jerk processing logic to improve acceleration and jerk handling, particularly under dynamic conditions. Adjusted test cases to cover the new logic.

* Refactor longitudinal controller for better jerk handling

Revised `calculate_a_value` and jerk processing logic to improve acceleration and jerk handling, particularly under dynamic conditions. Adjusted test cases to cover the new logic.

* Refactor longitudinal controller for better jerk handling

Revised `calculate_a_value` and jerk processing logic to improve acceleration and jerk handling, particularly under dynamic conditions. Adjusted test cases to cover the new logic.

* Adjust lower_jerk interpolation for improved accuracy

* Lowered the acceleration error threshold from -0.01 to -0.001

* Adjust jerk interpolation thresholds for Hyundai tuning.

Updated the interpolation range  for smaller acceleration errors.

* Move it up

* -.03 not -.05

* radarUnavailable = higher limit patch

* Commit the real change

* Updated the interpolation values and logic for lower_jerk to refine acceleration behavior.

* Updated the interpolation values and logic for lower_jerk to refine acceleration behavior.

* Updated the interpolation values and logic for lower_jerk to refine acceleration behavior.

* Updated the interpolation values and logic for lower_jerk to refine acceleration behavior.

* THIS. THIS WILL STOP FOR LEADS

* Higher for VOACC

* Higher for VOACC

* Rename `make_jerk` to `calculate_jerk` in test cases.

* Adjust jerk tuning for Hyundai longitudinal control

Updated the interpolation table for lower jerk values to improve deceleration handling. This adjustment refines control behavior during higher deceleration requests.

* Updated the interpolation breakpoint from -0.03 to -0.025

* Adjust longitudinal control jerk tuning for smoother response

Updated interpolation values in the jerk tuning logic to improve acceleration smoothing in Hyundai longitudinal control. These changes aim to provide a more consistent and predictable driving experience, particularly during deceleration scenarios.

* Handle radar unavailability in longitudinal tuning check

* Introduce `toggleDisableMsg` to streamline determining toggle states and descriptions.

* `sunnypilot`

* `Refactor longitudinal control acceleration and jerk handling`

Removed unused `FirstOrderFilter` and simplified acceleration and jerk calculations for improved clarity and maintainability. Adjusted logic to replace redundant return statements, streamline jerk computation, and reduce unnecessary dependencies.

* `Refactor longitudinal control acceleration and jerk handling`

Removed unused `FirstOrderFilter` and simplified acceleration and jerk calculations for improved clarity and maintainability. Adjusted logic to replace redundant return statements, streamline jerk computation, and reduce unnecessary dependencies.

* Simplify test to be straight to the point.

* Adjust upper_speed_factor and integrate ramp_update logic

* Adjust upper_speed_factor and integrate ramp_update logic

* Adjust upper_speed_factor and integrate ramp_update logic

* Refine acceleration and jerk tuning logic.

* greater than 0

* bump

* bring back ramp update for jerk upper

* Update opendbc_repo

* Revert "Update opendbc_repo"

This reverts commit b790387c90.

* bump

* bump

* bump

* using count seems to work better

* Refine jerk tuning logic and expand test coverage

* bump vals

* fix logic

* Adjust jerk tuning parameters for Hyundai longitudinal control

* retry lfs check

* bump submodule

* Fix acceleration blending and enhance test coverage

* Update lower_jerk breakpoints

* bump

* Adjust lower jerk parameters and include accel_cmd condition

* Adjust vars

* "Adjust Hyundai longitudinal jerk limits and tuning logic"

* Adjust jerk limits and tuning logic for better control

* Update Hyundai longitudinal tuning and add dynamic jerk logic

Refactored longitudinal configuration to include "lookahead" parameters and revised "jerk_limits" for improved tuning flexibility. Implemented dynamic jerk adaptation logic (Gen1) for smoother braking and acceleration transitions.

* Adjust Hyundai longitudinal control parameters for tuning

* bump to original branch

* QSize Policy to stretch toggle to screen size

* bump

* completely blocked xD

* Sunnypilot -> openpilot

* Whitespace

* Clean up toggle name

* Still allow ramp while in standstill for EV

* Adjust Kia Niro EV tuning parameters for smoother control

* self.cp.flags

* bump submodule

* bump

* Remove ramp for lower desired jerk

* update README.md

* update README.md

* Adjust lookahead_jerk_lower_v values in Hyundai longitudinal config.

* update tuning

* Update tune one last time to vals from device which are much smoother.

* Accel error

* bump

* fix

* rename flag and slight cleanup

* start ui stuff

* long_state -> tuning

* Prep for clean up

* small changes

* less in main

* no longer

* format

* bump

* init panel

* expose signal for other panels to interact with

* split into its own

* unused

* move around

* actually modify CP_SP

* fix offroad transition

* rename

* back

* don't update unless we're looking at it

* move around

* use min length

* do this

* whoops

* move

* bump

* bump

* this is fine

* bump

---------

Co-authored-by: Jason Wen <haibin.wen3@gmail.com>
Co-authored-by: DevTekVE <devtekve@gmail.com>
Co-authored-by: royjr <royjr96@gmail.com>
2025-05-09 01:06:45 -04:00
Jason Wen ff63d17723 ui: emit panel refresh signal from Platform Selector (#891) 2025-05-09 00:46:18 -04:00
Jason Wen 977179f661 Sync: commaai/openpilot:master into sunnypilot/sunnypilot:master-new (#890) 2025-05-09 00:01:25 -04:00
discountchubbs 21715cdc6d Merge branch 'upstream/openpilot/master' into 0508-sync 2025-05-08 23:43:54 -04:00
Dean Lee dde9c703f3 cabana: optimize get_raw_value() function for CAN signal extraction (#35137)
optimize get_raw_value() for CAN signal extraction
2025-05-08 14:31:00 -07:00
Cameron Clough 4bbbe3d2d1 ui(raylib): revert fps to 60 (#35163)
bump default fps
2025-05-08 22:22:53 +01:00
Jason Wen 037695af4a Car interface: pass into setup interfaces on init (#887)
* Car interface: pass into setup interfaces on init

* more

* should be after
2025-05-08 16:04:59 -04:00
Dean Lee 33849245d8 uploader.py: fix empty string handing in AthenadRecentlyViewedRoutes parameter (#35139)
Fix empty strings in AthenadRecentlyViewedRoutes parameter
2025-05-08 16:31:06 +01:00
Cameron Clough 73ee0c022f Reapply "ui(raylib): create BaseWindow (#35074)" (#35077)
* Reapply "ui(raylib): create BaseWindow (#35074)"

This reverts commit 83b84a5bec.

* correct title

* error msg

* cloudlog
2025-05-08 16:18:01 +01:00
Dean Lee e7f7675458 micd: fix thread safety by adding locking for shared state (#35148)
* fix thread safety by adding locking for shared state

* Update system/micd.py

---------

Co-authored-by: Cameron Clough <cameronjclough@gmail.com>
2025-05-08 16:09:44 +01:00
Dean Lee f123e7ed75 CI: add system/ui to UI labeler (#35157)
add system/ui to UI labeler
2025-05-08 15:19:28 +01:00
Maxime Desroches 52669b6ad2 AGNOS 12.1 (#35154)
12.1
2025-05-07 20:34:08 -07:00
Maxime Desroches 47ed90c6cf Reapply "Mypy: Got passing on macos (#34591)" (#35126) (#35153)
* Mypy: Got passing on macos (#34591)

* Mypy: Got mypy passing on macos

* common/realtime.py refactor

* Mypy: mypy passing on darwin

* Refactor: Removed else: pass statement

* Refactor: Removed unnecessary check

* added xattr to pyproject

* loggerd: switched to xatter module

* loggerd: removed unused module in xattr_cache.py

* UV: update uv.lock

* Update system/athena/athenad.py



* athenad: fixed blank lines

* loggerd: refactor of xattr_cache

* cleanup

---------



* fix getxattr no attribute on macOS

* try fixing missing ENOATTR on Linux

---------

Co-authored-by: Andrei Radulescu <andi.radulescu@gmail.com>
Co-authored-by: BrainLess <116778989+BrainLessPea@users.noreply.github.com>
2025-05-07 19:11:37 -07:00
Maxime Desroches 2451d70408 AGNOS 12 (#35151)
agnos 12
2025-05-07 18:21:53 -07:00
Trey Moen 380f383e2e ci: enable cache by default (#35121)
* enable cache for Mac brew and scons

* bump

* save cache by default, explicitly opt-out

* Delete bump-ci

---------

Co-authored-by: Maxime Desroches <desroches.maxime@gmail.com>
2025-05-07 18:13:53 -07:00
Harald Schäfer 0fb4aafa35 Tomb Raider 7 (#35114)
* Revert "Revert TR (#35110)"

This reverts commit df4f2955dc.

* eb5f884a-10ad-49fd-ae5c-e2818c26e568/400

* 1cc828ab-95e5-4620-aa07-b98918b4268d/400

* 5790a2c1-b487-4bef-a3c3-db1fcd5a756d/400

* raw plan
2025-05-07 18:06:41 -07:00
Maxime Desroches 36ff474bc8 remove numpy.core usage (#35152)
fix
2025-05-07 16:44:20 -07:00
Shane Smiskol 2e0fa3f827 Tesla: allow enabling in tight curves (#35147)
* bump

* revert steer limit timer

* alert for stock lkas

* add enum

* same as ldw

* bump

* draft

* bump

* bump

* rm

* why here?1

* bump to master
2025-05-07 16:26:20 -07:00
Shane Smiskol dcca094ad8 Tesla: forward stock LKAS while disengaged (#35150)
* bump

* update docs

* bump
2025-05-07 15:29:41 -07:00
Robbe Derks 433e7268f5 Log register errors as errors (#35141)
log register errors as errors
2025-05-07 18:12:01 +02:00
Dean Lee 7c16e65347 loggerd: remove redundant Params Construction (#35138)
remove redundant Params Construction
2025-05-07 16:39:05 +01:00
Maxime Desroches aa1b790708 revert agnos 12 2025-05-06 21:38:50 -07:00
Andrei Radulescu bbf37ae5c7 Reapply "Mypy: Got passing on macos (#34591)" (#35126)
* Mypy: Got passing on macos (#34591)

* Mypy: Got mypy passing on macos

* common/realtime.py refactor

* Mypy: mypy passing on darwin

* Refactor: Removed else: pass statement

* Refactor: Removed unnecessary check

* added xattr to pyproject

* loggerd: switched to xatter module

* loggerd: removed unused module in xattr_cache.py

* UV: update uv.lock

* Update system/athena/athenad.py

Co-authored-by: Maxime Desroches <desroches.maxime@gmail.com>

* athenad: fixed blank lines

* loggerd: refactor of xattr_cache

* cleanup

---------

Co-authored-by: Maxime Desroches <desroches.maxime@gmail.com>

* fix getxattr no attribute on macOS

* try fixing missing ENOATTR on Linux

---------

Co-authored-by: BrainLess <116778989+BrainLessPea@users.noreply.github.com>
Co-authored-by: Maxime Desroches <desroches.maxime@gmail.com>
2025-05-06 21:09:59 -07:00
Maxime Desroches b400312042 agnos 12 (#35133)
* new

* all

* prod

* both

* 12

* version

* update
2025-05-06 20:37:09 -07:00
Adeeb Shihadeh e64be675e3 don't need to assert, CI updates this 2025-05-06 14:58:40 -07:00
Maxime Desroches 2393e0d27d update CARS doc 2025-05-06 14:54:24 -07:00
Adeeb Shihadeh 97bad78553 bump opendbc 2025-05-06 14:29:16 -07:00
Adeeb Shihadeh 58bc8e3b43 op: more robust openpilot finding (#35136) 2025-05-06 10:51:15 -07:00
Nayan 662877c6f3 models: Consolidate model helpers & get_model_path filename fix (#884)
consolidate run_helpers.py & helpers.py
bugfix for get_model_path
2025-05-06 19:38:06 +02:00
Dean Lee 9622b6f8bd ui(raylib): add a simple toggle component (#35128)
* simple toggle

* Update system/ui/lib/toggle.py

Co-authored-by: Adeeb Shihadeh <adeebshihadeh@gmail.com>

* cleanup

---------

Co-authored-by: Adeeb Shihadeh <adeebshihadeh@gmail.com>
2025-05-06 10:25:51 -07:00
Adeeb Shihadeh ddb19cc074 op: better adb SSH (#35135)
* op: better adb SSH

* fix
2025-05-06 10:21:23 -07:00
Joey 5c1f28591f remove dead link in SAFETY.md (#35122)
* Update SAFETY.md

remove broken link

* Update SAFETY.md

* Update docs/SAFETY.md

---------

Co-authored-by: Shane Smiskol <shane@smiskol.com>
2025-05-05 17:07:27 -07:00
Maxime Desroches 3c58da5c84 mimic agnos pyproject order 2025-05-05 16:54:14 -07:00
Dean Lee b920e2a998 tools: add --auto flag to replay and cabana for loading routes from auto source (#34863)
* add flag to auto load a route from the most suitable source

* split to functions

* early return

* add --auto to replay

* README

* cleanup

* remove prefix

* parse datetime

* cleanup

* improve help

* do not modify logreader.py

* fix seg_num

* cleanup
2025-05-05 16:48:15 -07:00
Maxime Desroches 5a94d818bb add xattr python package (#35131)
* add xattr

* comment
2025-05-05 15:21:06 -07:00
Dean Lee 71b37cfb94 tools/clip: fix SyntaxError in f-string due to unmatched brackets (#35129)
fix lint issue
2025-05-05 14:33:45 -07:00
commaci-public a8b9350103 [bot] Update Python packages (#35125)
Update Python packages

Co-authored-by: Vehicle Researcher <user@comma.ai>
2025-05-05 12:51:04 -07:00
Dean Lee c33e5b3209 python ui: wifi manager (#34814)
* python wifi manager

* fix ui

* need auth callback

* move to widgets

* confirm forgot

* add drag detection

* improve keyboard & list

* remove duplicate

* typos

* use gui_app render

* refactor

* cleanup

* cleanup

* shutdown

* fix types

* revert

* scroll panel cleanup

* reset is_dragging on mouse release

* Revert "reset is_dragging on mouse release"

This reverts commit ff5e51cf6f00848d93aa3ce0bab16602fea7a319.

* unformat

* cleanup

* update state when connecting

* forgotten callback

* maybe fix? first tap didn't work

* Revert "maybe fix? first tap didn't work"

This reverts commit 739f0e3bd37323d10479b081a20d05c7fdff5495.

* remove set_target_fps

* Revert "remove set_target_fps"

This reverts commit 96f74553ef6fa2ab2a4d1a07880175e7da628c9f.

---------

Co-authored-by: Cameron Clough <cameronjclough@gmail.com>
2025-05-05 16:28:12 +01:00
Jason Wen f66bf4b185 Sync: commaai/openpilot:master into sunnypilot/sunnypilot:master-new (#874) 2025-05-04 13:29:31 -04:00
Jason Wen b6c6a3ad19 Merge branch 'upstream/openpilot/master' into sync-20250503
# Conflicts:
#	opendbc_repo
#	selfdrive/controls/lib/longitudinal_planner.py
#	selfdrive/modeld/fill_model_msg.py
2025-05-04 13:21:27 -04:00
Trey Moen 87fae0c6f2 fix(clip): longer timeout for ffmpeg to finish (#35092) 2025-05-04 10:16:48 -07:00
Trey Moen f704d18a8b feat(clip): title and metadata overlay (#35099)
* wip

* moar

* ensure inter is installed

* line len

* refactor

* dont need this

* no longer than

* show meta for 4s
2025-05-04 10:16:35 -07:00
Dean Lee 8ee99523f4 cleanup .gitignore (#35116) 2025-05-04 10:15:47 -07:00
Dean Lee 8c8b2c4488 replay: fix potential timestamp parsing error in Route::load (#35117)
Fix potential timestamp parsing error in Route::load
2025-05-04 10:15:25 -07:00
DevTekVE d90e41f08f models: refactor model bundle structure (#870)
* Refactor model and artifact structures with version compatibility filtering

- Introduced `Artifact` struct and nested it within the `Model` struct for improved clarity and organization.
- Updated enums, logic, and parsing to align with the new struct definitions.
- Implemented version compatibility filtering for model bundles using the `is_bundle_version_compatible` helper.
- Enhanced artifact download handling by adding checks for missing URIs, better error management, and improved logging.
- Adjusted model fetching to point to the latest endpoint (`v3`).

* Make linter happy

* Make linter happy

* Refactor model data parsing to improve readability.

Replaced kwargs-based data extraction with explicit parameter passing for clarity. This enhances code readability and reduces ambiguities in method calls, making the parsing logic more maintainable and straightforward.

* Refactor error handling in active model bundle retrieval.

Wrapped the logic to fetch the active model bundle in a try-except block to prevent unhandled exceptions. This ensures more robust error handling and avoids potential crashes when retrieving or processing model data.

* Refactor exception handling in get_active_model_bundle

Replace bare except with Exception to improve specificity and clarity. This ensures better debugging practices and aligns with recommended coding standards. Other minor whitespace adjustments were made for improved readability.

* Update model path to use artifact fileName property

Replaced `fileName` with `artifact.fileName` in the custom model path construction. This ensures compatibility with updated drive model structures and avoids potential file resolution issues.
2025-05-04 17:03:22 +02:00
DevTekVE 600647d5e2 models: simplify modeld v2 logic process (#875)
* Refactor model runner methods for improved abstraction.

Moved slicing logic to a private `_slice_outputs` method and decoupled `_run_model` for clearer subclass implementation. Removed redundant `output` attribute in `ModelState` to streamline data handling.

* Add output parsing to model_runner and remove duplicate logic

Integrates an output parser directly into `model_runner` for streamlined inference and parsing. Removes redundant parser initialization from `modeld` to avoid duplication and enhance maintainability.

* Reordering

* linter

* linter
2025-05-03 21:01:17 +02:00
Discountchubbs 44ab494c41 BUG FIX: Fix .thneed model runners (#871)
Add support for thneed library and thneedmodel integration

Co-authored-by: DevTekVE <devtekve@gmail.com>
2025-05-03 08:50:40 +02:00
Shane Smiskol db6832762b Tesla: forward Summon (#35113)
bump
2025-05-02 21:48:55 -07:00
Jason Wen 3f883ad215 ui: add longitudinal settings panel (#852)
* ui: add longitudinal settings panel

* ui preview

* add ui preview

* rename to Cruise
2025-05-03 00:41:04 -04:00
Harald Schäfer df4f2955dc Revert TR (#35110)
* Revert "Tomb raider 2 (#35029)"

This reverts commit 2c162d9b75.

* bugfix

* fix policy

* min control speed
2025-05-02 20:54:42 -07:00
Shane Smiskol a1ec8c6bfe test models: check steering disengage matches for Tesla (#35107)
test models: check steering disengage matches for tesla
2025-05-02 20:23:30 -07:00
Harald Schäfer e1d2360b8c Revert: Tomb Raider 6 (#35105) 2025-05-02 10:17:40 -07:00
Shane Smiskol b58552542d Tesla safety: fix high angle rate fault and enforce steering disengage via safety (#35101) 2025-05-01 22:10:16 -07:00
126 changed files with 3244 additions and 1149 deletions
+1 -1
View File
@@ -16,7 +16,7 @@ simulation:
ui:
- changed-files:
- any-glob-to-all-files: 'selfdrive/ui/**'
- any-glob-to-all-files: '{selfdrive/ui/**,system/ui/**}'
tools:
- changed-files:
+1 -1
View File
@@ -12,7 +12,7 @@ inputs:
required: true
save:
description: 'whether to save the cache'
default: 'false'
default: 'true'
required: false
outputs:
cache-hit:
-2
View File
@@ -47,10 +47,8 @@ selfdrive/pandad/pandad
cereal/services.h
cereal/gen
cereal/messaging/bridge
selfdrive/logcatd/logcatd
selfdrive/mapd/default_speeds_by_region.json
system/proclogd/proclogd
selfdrive/ui/translations/alerts_generated.h
selfdrive/ui/translations/tmp
selfdrive/test/longitudinal_maneuvers/out
selfdrive/car/tests/cars_dump
+1
View File
@@ -4,6 +4,7 @@ Version 0.9.9 (2025-05-15)
* New training architecture supervised by MLSIM
* Steering actuator delay is now learned online
* Tesla Model 3 and Y support thanks to lukasloetkolben!
* Lexus RC 2023 support thanks to nelsonjchen!
* Coming soon
* New Honda models
* Bigger vision model
+21 -14
View File
@@ -39,20 +39,6 @@ struct ModelManagerSP @0xaedffd8f31e7b55d {
sha256 @1 :Text;
}
enum Type {
drive @0;
navigation @1;
metadata @2;
}
struct Model {
fullName @0 :Text;
fileName @1 :Text;
downloadUri @2 :DownloadUri;
downloadProgress @3 :DownloadProgress;
type @4 :Type;
}
enum DownloadStatus {
notDownloading @0;
downloading @1;
@@ -67,6 +53,25 @@ struct ModelManagerSP @0xaedffd8f31e7b55d {
eta @2 :UInt32;
}
struct Artifact {
fileName @0 :Text;
downloadUri @1 :DownloadUri;
downloadProgress @2 :DownloadProgress;
}
struct Model {
type @0 :Type;
artifact @1 :Artifact; # Main artifact
metadata @2 :Artifact; # Metadata artifact
enum Type {
supercombo @0;
navigation @1;
vision @2;
policy @3;
}
}
enum Runner {
snpe @0;
tinygrad @1;
@@ -83,6 +88,8 @@ struct ModelManagerSP @0xaedffd8f31e7b55d {
environment @6 :Text;
runner @7 :Runner;
is20hz @8 :Bool;
ref @9 :Text; # New field
minimumSelectorVersion @10 :UInt32;
}
}
+1 -1
View File
@@ -1 +1 @@
#define DEFAULT_MODEL "Tomb_Raider_6 (Default)"
#define DEFAULT_MODEL "Tomb Raider 7 (Default)"
+2
View File
@@ -128,6 +128,7 @@ inline static std::unordered_map<std::string, uint32_t> keys = {
{"CarParamsSPCache", CLEAR_ON_MANAGER_START},
{"CarParamsSPPersistent", PERSISTENT},
{"CarPlatformBundle", PERSISTENT},
{"DynamicPersonality", PERSISTENT | BACKUP},
{"EnableGithubRunner", PERSISTENT | BACKUP},
{"MaxTimeOffroad", PERSISTENT | BACKUP},
{"ModelRunnerTypeCache", CLEAR_ON_ONROAD_TRANSITION},
@@ -164,6 +165,7 @@ inline static std::unordered_map<std::string, uint32_t> keys = {
{"BackupManager_RestoreVersion", PERSISTENT},
// sunnypilot car specific params
{"HyundaiLongitudinalTuning", PERSISTENT},
{"HyundaiRadarTracks", PERSISTENT},
{"HyundaiRadarTracksConfirmed", PERSISTENT},
{"HyundaiRadarTracksPersistent", PERSISTENT},
+3 -2
View File
@@ -1,6 +1,7 @@
"""Utilities for reading real time clocks and keeping soft real time constraints."""
import gc
import os
import sys
import time
from setproctitle import getproctitle
@@ -28,13 +29,13 @@ class Priority:
def set_core_affinity(cores: list[int]) -> None:
if not PC:
if sys.platform == 'linux' and not PC:
os.sched_setaffinity(0, cores)
def config_realtime_process(cores: int | list[int], priority: int) -> None:
gc.disable()
if not PC:
if sys.platform == 'linux' and not PC:
os.sched_setscheduler(0, os.SCHED_FIFO, os.sched_param(priority))
c = cores if isinstance(cores, list) else [cores, ]
set_core_affinity(c)
+13 -6
View File
@@ -4,12 +4,13 @@
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.
# 304 Supported Cars
# 311 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|
|---|---|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
|Acura|ILX 2016-19|AcuraWatch Plus|openpilot|26 mph|25 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<details><summary>Parts</summary><sub>- 1 Honda Nidec connector<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Acura&model=ILX 2016-19">Buy Here</a></sub></details>||
|Acura|RDX 2016-18|AcuraWatch Plus|openpilot|26 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<details><summary>Parts</summary><sub>- 1 Honda Nidec connector<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Acura&model=RDX 2016-18">Buy Here</a></sub></details>||
|Acura|ILX 2016-18|Technology Plus Package or AcuraWatch Plus|openpilot|26 mph|25 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<details><summary>Parts</summary><sub>- 1 Honda Nidec connector<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Acura&model=ILX 2016-18">Buy Here</a></sub></details>||
|Acura|ILX 2019|All|openpilot|26 mph|25 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<details><summary>Parts</summary><sub>- 1 Honda Nidec connector<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Acura&model=ILX 2019">Buy Here</a></sub></details>||
|Acura|RDX 2016-18|AcuraWatch Plus or Advance Package|openpilot|26 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<details><summary>Parts</summary><sub>- 1 Honda Nidec connector<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Acura&model=RDX 2016-18">Buy Here</a></sub></details>||
|Acura|RDX 2019-21|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Honda Bosch A connector<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Acura&model=RDX 2019-21">Buy Here</a></sub></details>||
|Audi|A3 2014-19|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,16</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Audi&model=A3 2014-19">Buy Here</a></sub></details>||
|Audi|A3 Sportback e-tron 2017-18|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,16</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma 3X<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Audi&model=A3 Sportback e-tron 2017-18">Buy Here</a></sub></details>||
@@ -32,17 +33,22 @@ A supported vehicle is one that just works when you install a comma device. All
|Dodge|Durango 2020-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 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Dodge&model=Durango 2020-21">Buy Here</a></sub></details>||
|Ford|Bronco Sport 2021-24|Co-Pilot360 Assist+|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 angled mount (8 degrees)<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Ford&model=Bronco Sport 2021-24">Buy Here</a></sub></details>||
|Ford|Escape 2020-22|Co-Pilot360 Assist+|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Ford&model=Escape 2020-22">Buy Here</a></sub></details>||
|Ford|Escape 2023-24|Co-Pilot360 Assist+|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 Ford Q4 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Ford&model=Escape 2023-24">Buy Here</a></sub></details>||
|Ford|Escape Hybrid 2020-22|Co-Pilot360 Assist+|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Ford&model=Escape Hybrid 2020-22">Buy Here</a></sub></details>||
|Ford|Escape Hybrid 2023-24|Co-Pilot360 Assist+|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 Ford Q4 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Ford&model=Escape Hybrid 2023-24">Buy Here</a></sub></details>||
|Ford|Escape Plug-in Hybrid 2020-22|Co-Pilot360 Assist+|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Ford&model=Escape Plug-in Hybrid 2020-22">Buy Here</a></sub></details>||
|Ford|Escape Plug-in Hybrid 2023-24|Co-Pilot360 Assist+|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 Ford Q4 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Ford&model=Escape Plug-in Hybrid 2023-24">Buy Here</a></sub></details>||
|Ford|Explorer 2020-24|Co-Pilot360 Assist+|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Ford&model=Explorer 2020-24">Buy Here</a></sub></details>||
|Ford|Explorer Hybrid 2020-24|Co-Pilot360 Assist+|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Ford&model=Explorer Hybrid 2020-24">Buy Here</a></sub></details>||
|Ford|F-150 2021-23|Co-Pilot360 Assist 2.0|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 Ford Q4 connector<br>- 1 USB-C coupler<br>- 1 angled mount (8 degrees)<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Ford&model=F-150 2021-23">Buy Here</a></sub></details>||
|Ford|F-150 Hybrid 2021-23|Co-Pilot360 Assist 2.0|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 Ford Q4 connector<br>- 1 USB-C coupler<br>- 1 angled mount (8 degrees)<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Ford&model=F-150 Hybrid 2021-23">Buy Here</a></sub></details>||
|Ford|Focus 2018[<sup>3</sup>](#footnotes)|Adaptive Cruise Control with Lane Centering|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Ford&model=Focus 2018">Buy Here</a></sub></details>||
|Ford|Focus Hybrid 2018[<sup>3</sup>](#footnotes)|Adaptive Cruise Control with Lane Centering|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Ford&model=Focus Hybrid 2018">Buy Here</a></sub></details>||
|Ford|Kuga 2020-22|Adaptive Cruise Control with Lane Centering|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Ford&model=Kuga 2020-22">Buy Here</a></sub></details>||
|Ford|Kuga Hybrid 2020-22|Adaptive Cruise Control with Lane Centering|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Ford&model=Kuga Hybrid 2020-22">Buy Here</a></sub></details>||
|Ford|Kuga Plug-in Hybrid 2020-22|Adaptive Cruise Control with Lane Centering|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Ford&model=Kuga Plug-in Hybrid 2020-22">Buy Here</a></sub></details>||
|Ford|Kuga 2020-23|Adaptive Cruise Control with Lane Centering|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Ford&model=Kuga 2020-23">Buy Here</a></sub></details>||
|Ford|Kuga Hybrid 2020-23|Adaptive Cruise Control with Lane Centering|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Ford&model=Kuga Hybrid 2020-23">Buy Here</a></sub></details>||
|Ford|Kuga 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 Ford Q4 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Ford&model=Kuga Hybrid 2024">Buy Here</a></sub></details>||
|Ford|Kuga Plug-in Hybrid 2020-23|Adaptive Cruise Control with Lane Centering|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Ford&model=Kuga Plug-in Hybrid 2020-23">Buy Here</a></sub></details>||
|Ford|Kuga 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 Ford Q4 connector<br>- 1 USB-C coupler<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 long OBD-C cable (9.5 ft)<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Ford&model=Kuga Plug-in Hybrid 2024">Buy Here</a></sub></details>||
|Ford|Maverick 2022|LARIAT Luxury|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 angled mount (8 degrees)<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Ford&model=Maverick 2022">Buy Here</a></sub></details>||
|Ford|Maverick 2023-24|Co-Pilot360 Assist|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 angled mount (8 degrees)<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Ford&model=Maverick 2023-24">Buy Here</a></sub></details>||
|Ford|Maverick Hybrid 2022|LARIAT Luxury|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Ford Q3 connector<br>- 1 angled mount (8 degrees)<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Ford&model=Maverick Hybrid 2022">Buy Here</a></sub></details>||
@@ -186,6 +192,7 @@ A supported vehicle is one that just works when you install a comma device. All
|Lexus|NX Hybrid 2018-19|All|openpilot available[<sup>2</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<details><summary>Parts</summary><sub>- 1 Toyota A connector<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Lexus&model=NX Hybrid 2018-19">Buy Here</a></sub></details>||
|Lexus|NX Hybrid 2020-21|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Toyota A connector<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Lexus&model=NX Hybrid 2020-21">Buy Here</a></sub></details>||
|Lexus|RC 2018-20|All|Stock|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<details><summary>Parts</summary><sub>- 1 Toyota A connector<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Lexus&model=RC 2018-20">Buy Here</a></sub></details>||
|Lexus|RC 2023|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Toyota A connector<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Lexus&model=RC 2023">Buy Here</a></sub></details>||
|Lexus|RX 2016|Lexus Safety System+|openpilot available[<sup>2</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<details><summary>Parts</summary><sub>- 1 Toyota A connector<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Lexus&model=RX 2016">Buy Here</a></sub></details>||
|Lexus|RX 2017-19|All|openpilot available[<sup>2</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|<details><summary>Parts</summary><sub>- 1 Toyota A connector<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Lexus&model=RX 2017-19">Buy Here</a></sub></details>||
|Lexus|RX 2020-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 Toyota A connector<br>- 1 comma 3X<br>- 1 comma power v3<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Lexus&model=RX 2020-22">Buy Here</a></sub></details>||
+2 -2
View File
@@ -25,9 +25,9 @@ ensuring two main safety requirements.
by stepping on the brake pedal or by pressing the cancel button.
2. The vehicle must not alter its trajectory too quickly for the driver to safely
react. This means that while the system is engaged, the actuators are constrained
to operate within reasonable limits[^1].
to operate within reasonable limits[^1].
For additional safety implementation details, refer to [panda safety model](https://github.com/commaai/panda#safety-model). For vehicle specific implementation of the safety concept, refer to [panda/board/safety/](https://github.com/commaai/panda/tree/master/board/safety).
For additional safety implementation details, refer to [panda safety model](https://github.com/commaai/panda#safety-model). For vehicle specific implementation of the safety concept, refer to [opendbc/safety/safety](https://github.com/commaai/opendbc/tree/master/opendbc/safety/safety).
**Extra note**: comma.ai strongly discourages the use of openpilot forks with safety code either missing or
not fully meeting the above requirements.
+1 -1
View File
@@ -7,7 +7,7 @@ export OPENBLAS_NUM_THREADS=1
export VECLIB_MAXIMUM_THREADS=1
if [ -z "$AGNOS_VERSION" ]; then
export AGNOS_VERSION="11.13"
export AGNOS_VERSION="12.1"
fi
export STAGING_ROOT="/data/safe_staging"
+1
View File
@@ -47,6 +47,7 @@ dependencies = [
# logging
"pyzmq",
"sentry-sdk",
"xattr", # used in place of 'os.getxattr' for macos compatibility
# athena
"PyJWT",
+1 -1
View File
@@ -191,7 +191,7 @@ class CarSpecificEvents:
events.add(EventName.accFaulted)
if CS.steeringPressed:
events.add(EventName.steerOverride)
if CS.steeringDisengage:
if CS.steeringDisengage and not CS_prev.steeringDisengage:
events.add(EventName.steerDisengage)
if CS.brakePressed and CS.standstill:
events.add(EventName.preEnableStandstill)
+1 -1
View File
@@ -108,7 +108,7 @@ class Car:
fixed_fingerprint = json.loads(self.params.get("CarPlatformBundle", encoding='utf-8') or "{}").get("platform", None)
self.CI = get_car(*self.can_callbacks, obd_callback(self.params), alpha_long_allowed, num_pandas, cached_params, fixed_fingerprint)
sunnypilot_interfaces.setup_interfaces(self.CI.CP, self.CI.CP_SP, self.params)
sunnypilot_interfaces.setup_interfaces(self.CI, self.params)
self.RI = interfaces[self.CI.CP.carFingerprint].RadarInterface(self.CI.CP, self.CI.CP_SP)
self.CP = self.CI.CP
self.CP_SP = self.CI.CP_SP
+1 -1
View File
@@ -45,9 +45,9 @@ class TestCarInterfaces:
alpha_long=args['alpha_long'], docs=False)
car_params_sp = CarInterface.get_params_sp(car_params, car_name, args['fingerprints'], args['car_fw'],
alpha_long=args['alpha_long'], docs=False)
sunnypilot_interfaces.setup_interfaces(car_params, car_params_sp)
car_params = car_params.as_reader()
car_interface = CarInterface(car_params, car_params_sp)
sunnypilot_interfaces.setup_interfaces(car_interface)
assert car_params
assert car_params_sp
assert car_interface
+2 -6
View File
@@ -4,7 +4,7 @@ from openpilot.common.basedir import BASEDIR
from opendbc.car.docs import generate_cars_md, get_all_car_docs
from openpilot.selfdrive.debug.dump_car_docs import dump_car_docs
from openpilot.selfdrive.debug.print_docs_diff import print_car_docs_diff
from openpilot.selfdrive.car.docs import CARS_MD_OUT, CARS_MD_TEMPLATE
from openpilot.selfdrive.car.docs import CARS_MD_TEMPLATE
class TestCarDocs:
@@ -13,11 +13,7 @@ class TestCarDocs:
cls.all_cars = get_all_car_docs()
def test_generator(self):
generated_cars_md = generate_cars_md(self.all_cars, CARS_MD_TEMPLATE)
with open(CARS_MD_OUT) as f:
current_cars_md = f.read()
assert generated_cars_md == current_cars_md, "Run selfdrive/car/docs.py to update the compatibility documentation"
generate_cars_md(self.all_cars, CARS_MD_TEMPLATE)
def test_docs_diff(self):
dump_path = os.path.join(BASEDIR, "selfdrive", "car", "tests", "cars_dump")
+5
View File
@@ -338,6 +338,7 @@ class TestCarModelBase(unittest.TestCase):
prev_panda_gas = self.safety.get_gas_pressed_prev()
prev_panda_brake = self.safety.get_brake_pressed_prev()
prev_panda_regen_braking = self.safety.get_regen_braking_prev()
prev_panda_steering_disengage = self.safety.get_steering_disengage_prev()
prev_panda_vehicle_moving = self.safety.get_vehicle_moving()
prev_panda_vehicle_speed_min = self.safety.get_vehicle_speed_min()
prev_panda_vehicle_speed_max = self.safety.get_vehicle_speed_max()
@@ -365,6 +366,9 @@ class TestCarModelBase(unittest.TestCase):
if self.safety.get_regen_braking_prev() != prev_panda_regen_braking:
self.assertEqual(CS.regenBraking, self.safety.get_regen_braking_prev())
if self.safety.get_steering_disengage_prev() != prev_panda_steering_disengage:
self.assertEqual(CS.steeringDisengage, self.safety.get_steering_disengage_prev())
if self.safety.get_vehicle_moving() != prev_panda_vehicle_moving:
self.assertEqual(not CS.standstill, self.safety.get_vehicle_moving())
@@ -440,6 +444,7 @@ class TestCarModelBase(unittest.TestCase):
brake_pressed = False
checks['brakePressed'] += brake_pressed != self.safety.get_brake_pressed_prev()
checks['regenBraking'] += CS.regenBraking != self.safety.get_regen_braking_prev()
checks['steeringDisengage'] += CS.steeringDisengage != self.safety.get_steering_disengage_prev()
if self.CP.pcmCruise:
# On most pcmCruise cars, openpilot's state is always tied to the PCM's cruise state.
@@ -10,6 +10,9 @@ from openpilot.common.swaglog import cloudlog
from openpilot.selfdrive.modeld.constants import index_function
from openpilot.selfdrive.controls.radard import _LEAD_ACCEL_TAU
from openpilot.sunnypilot.selfdrive.controls.lib.dynamic_personality.dynamic_personality_controller import DynamicPersonalityController
if __name__ == '__main__': # generating code
from openpilot.third_party.acados.acados_template import AcadosModel, AcadosOcp, AcadosOcpSolver
else:
@@ -228,6 +231,7 @@ class LongitudinalMpc:
self.solver = AcadosOcpSolverCython(MODEL_NAME, ACADOS_SOLVER_TYPE, N)
self.reset()
self.source = SOURCES[2]
self.dynamic_personality_controller = DynamicPersonalityController()
def reset(self):
# self.solver = AcadosOcpSolverCython(MODEL_NAME, ACADOS_SOLVER_TYPE, N)
@@ -327,9 +331,9 @@ class LongitudinalMpc:
lead_xv = self.extrapolate_lead(x_lead, v_lead, a_lead, a_lead_tau)
return lead_xv
def update(self, radarstate, v_cruise, x, v, a, j, personality=log.LongitudinalPersonality.standard):
t_follow = get_T_FOLLOW(personality)
def update(self, radarstate, v_cruise, x, v, a, j, personality=log.LongitudinalPersonality.standard, dynamic_personality=False):
v_ego = self.x0[1]
t_follow = self.dynamic_personality_controller.get_dynamic_follow_distance(v_ego, personality) if dynamic_personality else get_T_FOLLOW(personality)
self.status = radarstate.leadOne.status or radarstate.leadTwo.status
lead_xv_0 = self.process_lead(radarstate.leadOne)
@@ -1,7 +1,6 @@
#!/usr/bin/env python3
import math
import numpy as np
import cereal.messaging as messaging
from opendbc.car.interfaces import ACCEL_MIN, ACCEL_MAX
from openpilot.common.conversions import Conversions as CV
@@ -93,8 +92,8 @@ class LongitudinalPlanner(LongitudinalPlannerSP):
return x, v, a, j, throttle_prob
def update(self, sm):
LongitudinalPlannerSP.update(self, sm)
self.mode = 'blended' if sm['selfdriveState'].experimentalMode else 'acc'
LongitudinalPlannerSP.update(self, sm)
if dec_mpc_mode := self.get_mpc_mode():
self.mode = dec_mpc_mode
@@ -149,7 +148,7 @@ class LongitudinalPlanner(LongitudinalPlannerSP):
self.mpc.set_weights(prev_accel_constraint, personality=sm['selfdriveState'].personality)
self.mpc.set_cur_state(self.v_desired_filter.x, self.a_desired)
self.mpc.update(sm['radarState'], v_cruise, x, v, a, j, personality=sm['selfdriveState'].personality)
self.mpc.update(sm['radarState'], v_cruise, x, v, a, j, personality=sm['selfdriveState'].personality, dynamic_personality = self.dynamic_personality)
self.v_desired_trajectory = np.interp(CONTROL_N_T_IDX, T_IDXS_MPC, self.mpc.v_solution)
self.a_desired_trajectory = np.interp(CONTROL_N_T_IDX, T_IDXS_MPC, self.mpc.a_solution)
@@ -178,6 +177,10 @@ class LongitudinalPlanner(LongitudinalPlannerSP):
output_a_target = min(output_a_target_mpc, output_a_target_e2e)
self.output_should_stop = output_should_stop_e2e or output_should_stop_mpc
if not self.is_stock:
# To support non Tomb Raider models
output_a_target, self.output_should_stop = output_a_target_mpc, output_should_stop_mpc
for idx in range(2):
accel_clip[idx] = np.clip(accel_clip[idx], self.prev_accel_clip[idx] - 0.05, self.prev_accel_clip[idx] + 0.05)
self.output_a_target = np.clip(output_a_target, accel_clip[0], accel_clip[1])
@@ -23,7 +23,7 @@ class TestLatControl:
CP = CarInterface.get_non_essential_params(car_name)
CP_SP = CarInterface.get_non_essential_params_sp(CP, car_name)
CI = CarInterface(CP, CP_SP)
sunnypilot_interfaces.setup_interfaces(CP, CP_SP)
sunnypilot_interfaces.setup_interfaces(CI)
CP_SP = convert_to_capnp(CP_SP)
VM = VehicleModel(CP)
+5 -12
View File
@@ -90,18 +90,11 @@ def fill_model_msg(base_msg: capnp._DynamicStructBuilder, extended_msg: capnp._D
fill_xyzt(modelV2.orientationRate, ModelConstants.T_IDXS, *net_output_data['plan'][0,:,Plan.ORIENTATION_RATE].T)
# temporal pose
temporal_pose = modelV2.temporalPose
if 'sim_pose' in net_output_data:
temporal_pose.trans = net_output_data['sim_pose'][0,:ModelConstants.POSE_WIDTH//2].tolist()
temporal_pose.transStd = net_output_data['sim_pose_stds'][0,:ModelConstants.POSE_WIDTH//2].tolist()
temporal_pose.rot = net_output_data['sim_pose'][0,ModelConstants.POSE_WIDTH//2:].tolist()
temporal_pose.rotStd = net_output_data['sim_pose_stds'][0,ModelConstants.POSE_WIDTH//2:].tolist()
else:
temporal_pose.trans = net_output_data['plan'][0,0,Plan.VELOCITY].tolist()
temporal_pose.transStd = net_output_data['plan_stds'][0,0,Plan.VELOCITY].tolist()
temporal_pose.rot = net_output_data['plan'][0,0,Plan.ORIENTATION_RATE].tolist()
temporal_pose.rotStd = net_output_data['plan_stds'][0,0,Plan.ORIENTATION_RATE].tolist()
#temporal_pose = modelV2.temporalPose
#temporal_pose.trans = net_output_data['sim_pose'][0,:ModelConstants.POSE_WIDTH//2].tolist()
#temporal_pose.transStd = net_output_data['sim_pose_stds'][0,:ModelConstants.POSE_WIDTH//2].tolist()
#temporal_pose.rot = net_output_data['sim_pose'][0,ModelConstants.POSE_WIDTH//2:].tolist()
#temporal_pose.rotStd = net_output_data['sim_pose_stds'][0,ModelConstants.POSE_WIDTH//2:].tolist()
# poly path
fill_xyz_poly(driving_model_data.path, ModelConstants.POLY_PATH_DEGREE, *net_output_data['plan'][0,:,Plan.POSITION].T)
+7 -4
View File
@@ -41,7 +41,7 @@ POLICY_PKL_PATH = Path(__file__).parent / 'models/driving_policy_tinygrad.pkl'
VISION_METADATA_PATH = Path(__file__).parent / 'models/driving_vision_metadata.pkl'
POLICY_METADATA_PATH = Path(__file__).parent / 'models/driving_policy_metadata.pkl'
LAT_SMOOTH_SECONDS = 0.3
LAT_SMOOTH_SECONDS = 0.1
LONG_SMOOTH_SECONDS = 0.3
MIN_LAT_CONTROL_SPEED = 0.3
@@ -54,9 +54,12 @@ def get_action_from_model(model_output: dict[str, np.ndarray], prev_action: log.
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)
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:
+1 -1
View File
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:8a478376723ba79e2393ca95e2d3e497571ba6fed113e5f13a36f0e4b4d4a7c5
oid sha256:19e30484236efff72d519938c3e26461dbeb89c11d81fa7ecbff8e0263333c18
size 15588463
+6 -1
View File
@@ -474,7 +474,12 @@ void pandad_run(std::vector<Panda *> &pandas) {
for (auto *panda : pandas) {
std::string log = panda->serial_read();
if (!log.empty()) {
LOGD("%s", log.c_str());
if (log.find("Register 0x") != std::string::npos) {
// Log register divergent faults as errors
LOGE("%s", log.c_str());
} else {
LOGD("%s", log.c_str());
}
}
}
+5 -1
View File
@@ -60,6 +60,9 @@ DeveloperPanel::DeveloperPanel(SettingsWindow *parent) : ListWidget(parent) {
enableGithubRunner = new ParamControl("EnableGithubRunner", tr("Enable GitHub runner service"), tr("Enables or disables the github runner service."), "");
addItem(enableGithubRunner);
dynamicpersonality = new ParamControl("DynamicPersonality", tr("Enable Dynamic Personality"), tr("Adjust follow distance dynamically "), "");
addItem(dynamicpersonality);
// error log button
errorLogBtn = new ButtonControl(tr("Error Log"), tr("VIEW"), tr("View the error log for sunnypilot crashes."));
connect(errorLogBtn, &ButtonControl::clicked, [=]() {
@@ -84,7 +87,7 @@ void DeveloperPanel::updateToggles(bool _offroad) {
* - visible, and
* - during onroad & offroad states
*/
if (btn != experimentalLongitudinalToggle) {
if (btn != experimentalLongitudinalToggle && btn != dynamicpersonality) {
btn->setEnabled(_offroad);
}
}
@@ -122,6 +125,7 @@ void DeveloperPanel::updateToggles(bool _offroad) {
// Handle specific controls visibility for release branches
enableGithubRunner->setVisible(!is_release);
dynamicpersonality->setVisible(!is_release);
errorLogBtn->setVisible(!is_release);
joystickToggle->setVisible(!is_release);
@@ -21,6 +21,7 @@ private:
ParamControl* experimentalLongitudinalToggle;
ParamControl* hyundaiRadarTracksToggle;
ParamControl* enableGithubRunner;
ParamControl* dynamicpersonality;
bool is_release;
bool offroad = false;
+19 -1
View File
@@ -21,6 +21,7 @@ qt_src = [
"sunnypilot/qt/offroad/offroad_home.cc",
"sunnypilot/qt/offroad/settings/device_panel.cc",
"sunnypilot/qt/offroad/settings/lateral_panel.cc",
"sunnypilot/qt/offroad/settings/longitudinal_panel.cc",
"sunnypilot/qt/offroad/settings/max_time_offroad.cc",
"sunnypilot/qt/offroad/settings/settings.cc",
"sunnypilot/qt/offroad/settings/software_panel.cc",
@@ -49,11 +50,28 @@ network_src = [
]
vehicle_panel_qt_src = [
"sunnypilot/qt/offroad/settings/vehicle/brand_settings_factory.cc",
"sunnypilot/qt/offroad/settings/vehicle/brand_settings_interface.cc",
"sunnypilot/qt/offroad/settings/vehicle/platform_selector.cc",
]
brand_settings_qt_src = [
"sunnypilot/qt/offroad/settings/vehicle/chrysler_settings.cc",
"sunnypilot/qt/offroad/settings/vehicle/ford_settings.cc",
"sunnypilot/qt/offroad/settings/vehicle/gm_settings.cc",
"sunnypilot/qt/offroad/settings/vehicle/honda_settings.cc",
"sunnypilot/qt/offroad/settings/vehicle/hyundai_settings.cc",
"sunnypilot/qt/offroad/settings/vehicle/mazda_settings.cc",
"sunnypilot/qt/offroad/settings/vehicle/nissan_settings.cc",
"sunnypilot/qt/offroad/settings/vehicle/rivian_settings.cc",
"sunnypilot/qt/offroad/settings/vehicle/subaru_settings.cc",
"sunnypilot/qt/offroad/settings/vehicle/tesla_settings.cc",
"sunnypilot/qt/offroad/settings/vehicle/toyota_settings.cc",
"sunnypilot/qt/offroad/settings/vehicle/volkswagen_settings.cc",
]
sp_widgets_src = widgets_src + network_src
sp_qt_src = qt_src + lateral_panel_qt_src + vehicle_panel_qt_src
sp_qt_src = qt_src + lateral_panel_qt_src + vehicle_panel_qt_src + brand_settings_qt_src
sp_qt_util = qt_util
Export('sp_widgets_src', 'sp_qt_src', "sp_qt_util")
@@ -112,9 +112,18 @@ DevicePanelSP::DevicePanelSP(SettingsWindowSP *parent) : DevicePanel(parent) {
addItem(power_group_layout);
std::vector always_enabled_btns = {
rebootBtn,
poweroffBtn,
offroadBtn,
buttons["quietModeBtn"],
};
QObject::connect(uiState(), &UIState::offroadTransition, [=](bool offroad) {
for (auto btn : findChildren<PushButtonSP*>()) {
if (btn != rebootBtn && btn != poweroffBtn && btn != offroadBtn) {
bool always_enabled = std::find(always_enabled_btns.begin(), always_enabled_btns.end(), btn) != always_enabled_btns.end();
if (!always_enabled) {
btn->setEnabled(offroad);
}
}
@@ -0,0 +1,11 @@
/**
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
*
* This file is part of sunnypilot and is licensed under the MIT License.
* See the LICENSE.md file in the root directory for more details.
*/
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/longitudinal_panel.h"
LongitudinalPanel::LongitudinalPanel(QWidget *parent) : QWidget(parent) {
}
@@ -0,0 +1,17 @@
/**
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
*
* This file is part of sunnypilot and is licensed under the MIT License.
* See the LICENSE.md file in the root directory for more details.
*/
#pragma once
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h"
class LongitudinalPanel : public QWidget {
Q_OBJECT
public:
explicit LongitudinalPanel(QWidget *parent = nullptr);
};
@@ -16,6 +16,7 @@
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/software_panel.h"
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/sunnylink_panel.h"
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/lateral_panel.h"
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/longitudinal_panel.h"
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/trips_panel.h"
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle_panel.h"
@@ -79,6 +80,7 @@ SettingsWindowSP::SettingsWindowSP(QWidget *parent) : SettingsWindow(parent) {
PanelInfo(" " + tr("Toggles"), toggles, "../../sunnypilot/selfdrive/assets/offroad/icon_toggle.png"),
PanelInfo(" " + tr("Software"), new SoftwarePanelSP(this), "../../sunnypilot/selfdrive/assets/offroad/icon_software.png"),
PanelInfo(" " + tr("Steering"), new LateralPanel(this), "../../sunnypilot/selfdrive/assets/offroad/icon_lateral.png"),
PanelInfo(" " + tr("Cruise"), new LongitudinalPanel(this), "../assets/offroad/icon_speed_limit.png"),
PanelInfo(" " + tr("Trips"), new TripsPanel(this), "../../sunnypilot/selfdrive/assets/offroad/icon_trips.png"),
PanelInfo(" " + tr("Vehicle"), new VehiclePanel(this), "../../sunnypilot/selfdrive/assets/offroad/icon_vehicle.png"),
PanelInfo(" " + tr("Firehose"), new FirehosePanel(this), "../../sunnypilot/selfdrive/assets/offroad/icon_firehose.svg"),
@@ -47,24 +47,24 @@ void SoftwarePanelSP::handleBundleDownloadProgress() {
// Get status for each model type in order
for (const auto &model: models) {
QString typeName;
QString modelName;
QString modelName = QString::fromStdString(bundle.getDisplayName());
switch (model.getType()) {
case cereal::ModelManagerSP::Type::DRIVE:
case cereal::ModelManagerSP::Model::Type::SUPERCOMBO:
typeName = tr("Driving");
modelName = QString::fromStdString(bundle.getDisplayName());
break;
case cereal::ModelManagerSP::Type::NAVIGATION:
case cereal::ModelManagerSP::Model::Type::NAVIGATION:
typeName = tr("Navigation");
modelName = QString::fromStdString(model.getFullName());
break;
case cereal::ModelManagerSP::Type::METADATA:
typeName = tr("Metadata");
modelName = QString::fromStdString(model.getFullName());
case cereal::ModelManagerSP::Model::Type::VISION:
typeName = tr("Vision");
break;
case cereal::ModelManagerSP::Model::Type::POLICY:
typeName = tr("Policy");
break;
}
const auto &progress = model.getDownloadProgress();
const auto &progress = model.getArtifact().getDownloadProgress();
QString line;
if (progress.getStatus() == cereal::ModelManagerSP::DownloadStatus::DOWNLOADING) {
@@ -0,0 +1,62 @@
/**
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
*
* This file is part of sunnypilot and is licensed under the MIT License.
* See the LICENSE.md file in the root directory for more details.
*/
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/brand_settings_factory.h"
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/brands.h"
static const QStringList supportedBrands = {
"chrysler",
"ford",
"gm",
"honda",
"hyundai",
"mazda",
"nissan",
"rivian",
"subaru",
"tesla",
"toyota",
"volkswagen",
};
BrandSettingsInterface* BrandSettingsFactory::createBrandSettings(const QString& brand, QWidget* parent) {
if (brand == "chrysler")
return new ChryslerSettings(parent);
if (brand == "ford")
return new FordSettings(parent);
if (brand == "gm")
return new GMSettings(parent);
if (brand == "honda")
return new HondaSettings(parent);
if (brand == "hyundai")
return new HyundaiSettings(parent);
if (brand == "mazda")
return new MazdaSettings(parent);
if (brand == "nissan")
return new NissanSettings(parent);
if (brand == "rivian")
return new RivianSettings(parent);
if (brand == "subaru")
return new SubaruSettings(parent);
if (brand == "tesla")
return new TeslaSettings(parent);
if (brand == "toyota")
return new ToyotaSettings(parent);
if (brand == "volkswagen")
return new VolkswagenSettings(parent);
// Default empty settings if brand not supported
return nullptr;
}
bool BrandSettingsFactory::isBrandSupported(const QString& brand) {
return supportedBrands.contains(brand);
}
QStringList BrandSettingsFactory::getSupportedBrands() {
return supportedBrands;
}
@@ -0,0 +1,18 @@
/**
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
*
* This file is part of sunnypilot and is licensed under the MIT License.
* See the LICENSE.md file in the root directory for more details.
*/
#pragma once
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/brand_settings_interface.h"
class BrandSettingsFactory {
public:
static BrandSettingsInterface* createBrandSettings(const QString &brand, QWidget *parent = nullptr);
static bool isBrandSupported(const QString& brand);
static QStringList getSupportedBrands();
};
@@ -0,0 +1,21 @@
/**
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
*
* This file is part of sunnypilot and is licensed under the MIT License.
* See the LICENSE.md file in the root directory for more details.
*/
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/brand_settings_interface.h"
BrandSettingsInterface::BrandSettingsInterface(QWidget *parent) : QWidget(parent) {
QVBoxLayout *main_layout = new QVBoxLayout(this);
main_layout->setContentsMargins(0, 0, 0, 0);
list = new ListWidget(this, false);
main_layout->addWidget(list);
}
void BrandSettingsInterface::updatePanel(bool _offroad) {
offroad = _offroad;
updateSettings();
}
@@ -0,0 +1,28 @@
/**
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
*
* This file is part of sunnypilot and is licensed under the MIT License.
* See the LICENSE.md file in the root directory for more details.
*/
#pragma once
#include "selfdrive/ui/sunnypilot/ui.h"
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h"
#include "selfdrive/ui/sunnypilot/qt/widgets/controls.h"
class BrandSettingsInterface : public QWidget {
Q_OBJECT
public:
explicit BrandSettingsInterface(QWidget *parent = nullptr);
virtual ~BrandSettingsInterface() = default;
void updatePanel(bool _offroad);
virtual void updateSettings() = 0;
protected:
ListWidget *list = nullptr;
Params params;
bool offroad = false;
};
@@ -0,0 +1,21 @@
/**
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
*
* This file is part of sunnypilot and is licensed under the MIT License.
* See the LICENSE.md file in the root directory for more details.
*/
#pragma once
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/chrysler_settings.h"
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/ford_settings.h"
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/gm_settings.h"
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/honda_settings.h"
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/hyundai_settings.h"
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/mazda_settings.h"
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/nissan_settings.h"
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/rivian_settings.h"
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/subaru_settings.h"
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/tesla_settings.h"
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/toyota_settings.h"
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/volkswagen_settings.h"
@@ -0,0 +1,14 @@
/**
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
*
* This file is part of sunnypilot and is licensed under the MIT License.
* See the LICENSE.md file in the root directory for more details.
*/
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/chrysler_settings.h"
ChryslerSettings::ChryslerSettings(QWidget *parent) : BrandSettingsInterface(parent) {
}
void ChryslerSettings::updateSettings() {
}
@@ -0,0 +1,26 @@
/**
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
*
* This file is part of sunnypilot and is licensed under the MIT License.
* See the LICENSE.md file in the root directory for more details.
*/
#pragma once
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/brand_settings_interface.h"
#include "selfdrive/ui/qt/util.h"
#include "selfdrive/ui/sunnypilot/ui.h"
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h"
#include "selfdrive/ui/sunnypilot/qt/widgets/controls.h"
class ChryslerSettings : public BrandSettingsInterface {
Q_OBJECT
public:
explicit ChryslerSettings(QWidget *parent = nullptr);
void updateSettings() override;
private:
bool offroad = false;
};
@@ -0,0 +1,14 @@
/**
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
*
* This file is part of sunnypilot and is licensed under the MIT License.
* See the LICENSE.md file in the root directory for more details.
*/
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/ford_settings.h"
FordSettings::FordSettings(QWidget *parent) : BrandSettingsInterface(parent) {
}
void FordSettings::updateSettings() {
}
@@ -0,0 +1,26 @@
/**
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
*
* This file is part of sunnypilot and is licensed under the MIT License.
* See the LICENSE.md file in the root directory for more details.
*/
#pragma once
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/brand_settings_interface.h"
#include "selfdrive/ui/qt/util.h"
#include "selfdrive/ui/sunnypilot/ui.h"
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h"
#include "selfdrive/ui/sunnypilot/qt/widgets/controls.h"
class FordSettings : public BrandSettingsInterface {
Q_OBJECT
public:
explicit FordSettings(QWidget *parent = nullptr);
void updateSettings() override;
private:
bool offroad = false;
};
@@ -0,0 +1,14 @@
/**
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
*
* This file is part of sunnypilot and is licensed under the MIT License.
* See the LICENSE.md file in the root directory for more details.
*/
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/gm_settings.h"
GMSettings::GMSettings(QWidget *parent) : BrandSettingsInterface(parent) {
}
void GMSettings::updateSettings() {
}
@@ -0,0 +1,26 @@
/**
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
*
* This file is part of sunnypilot and is licensed under the MIT License.
* See the LICENSE.md file in the root directory for more details.
*/
#pragma once
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/brand_settings_interface.h"
#include "selfdrive/ui/qt/util.h"
#include "selfdrive/ui/sunnypilot/ui.h"
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h"
#include "selfdrive/ui/sunnypilot/qt/widgets/controls.h"
class GMSettings : public BrandSettingsInterface {
Q_OBJECT
public:
explicit GMSettings(QWidget *parent = nullptr);
void updateSettings() override;
private:
bool offroad = false;
};
@@ -0,0 +1,14 @@
/**
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
*
* This file is part of sunnypilot and is licensed under the MIT License.
* See the LICENSE.md file in the root directory for more details.
*/
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/honda_settings.h"
HondaSettings::HondaSettings(QWidget *parent) : BrandSettingsInterface(parent) {
}
void HondaSettings::updateSettings() {
}
@@ -0,0 +1,26 @@
/**
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
*
* This file is part of sunnypilot and is licensed under the MIT License.
* See the LICENSE.md file in the root directory for more details.
*/
#pragma once
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/brand_settings_interface.h"
#include "selfdrive/ui/qt/util.h"
#include "selfdrive/ui/sunnypilot/ui.h"
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h"
#include "selfdrive/ui/sunnypilot/qt/widgets/controls.h"
class HondaSettings : public BrandSettingsInterface {
Q_OBJECT
public:
explicit HondaSettings(QWidget *parent = nullptr);
void updateSettings() override;
private:
bool offroad = false;
};
@@ -0,0 +1,57 @@
/**
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
*
* This file is part of sunnypilot and is licensed under the MIT License.
* See the LICENSE.md file in the root directory for more details.
*/
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/hyundai_settings.h"
HyundaiSettings::HyundaiSettings(QWidget *parent) : BrandSettingsInterface(parent) {
std::vector<QString> tuning_texts{ tr("Off"), tr("Dynamic"), tr("Predictive") };
longitudinalTuningToggle = new ButtonParamControl(
"HyundaiLongitudinalTuning",
tr("Custom Longitudinal Tuning"),
"",
"",
tuning_texts,
500
);
QObject::connect(longitudinalTuningToggle, &ButtonParamControlSP::buttonToggled, this, &HyundaiSettings::updateSettings);
list->addItem(longitudinalTuningToggle);
longitudinalTuningToggle->showDescription();
}
void HyundaiSettings::updateSettings() {
auto longitudinal_tuning_param = std::atoi(params.get("HyundaiLongitudinalTuning").c_str());
auto cp_bytes = params.get("CarParamsPersistent");
if (!cp_bytes.empty()) {
AlignedBuffer aligned_buf;
capnp::FlatArrayMessageReader cmsg(aligned_buf.align(cp_bytes.data(), cp_bytes.size()));
cereal::CarParams::Reader CP = cmsg.getRoot<cereal::CarParams>();
has_longitudinal_control = hasLongitudinalControl(CP);
} else {
has_longitudinal_control = false;
}
LongitudinalTuningOption longitudinal_tuning_option;
if (longitudinal_tuning_param == int(LongitudinalTuningOption::PREDICTIVE)) {
longitudinal_tuning_option = LongitudinalTuningOption::PREDICTIVE;
} else if (longitudinal_tuning_param == int(LongitudinalTuningOption::DYNAMIC)) {
longitudinal_tuning_option = LongitudinalTuningOption::DYNAMIC;
} else {
longitudinal_tuning_option = LongitudinalTuningOption::OFF;
}
bool longitudinal_tuning_disabled = !offroad || !has_longitudinal_control;
QString longitudinal_tuning_description = longitudinalTuningDescription(longitudinal_tuning_option);
if (longitudinal_tuning_disabled) {
longitudinal_tuning_description = toggleDisableMsg(offroad, has_longitudinal_control);
}
longitudinalTuningToggle->setEnabled(!longitudinal_tuning_disabled);
longitudinalTuningToggle->setDescription(longitudinal_tuning_description);
longitudinalTuningToggle->showDescription();
}
@@ -0,0 +1,65 @@
/**
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
*
* This file is part of sunnypilot and is licensed under the MIT License.
* See the LICENSE.md file in the root directory for more details.
*/
#pragma once
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/brand_settings_interface.h"
#include "selfdrive/ui/qt/util.h"
#include "selfdrive/ui/sunnypilot/ui.h"
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h"
#include "selfdrive/ui/sunnypilot/qt/widgets/controls.h"
enum class LongitudinalTuningOption {
OFF,
DYNAMIC,
PREDICTIVE,
};
class HyundaiSettings : public BrandSettingsInterface {
Q_OBJECT
public:
explicit HyundaiSettings(QWidget *parent = nullptr);
void updateSettings() override;
private:
bool has_longitudinal_control = false;
ButtonParamControl *longitudinalTuningToggle = nullptr;
static QString toggleDisableMsg(bool _offroad, bool _has_longitudinal_control) {
if (!_has_longitudinal_control) {
return tr("This feature can only be used with openpilot longitudinal control enabled.");
}
if (!_offroad) {
return tr("Enable \"Always Offroad\" in Device panel, or turn vehicle off to select an option.");
}
return QString();
}
static QString longitudinalTuningDescription(LongitudinalTuningOption option = LongitudinalTuningOption::OFF) {
QString off_str = tr("Off: Uses default tuning");
QString dynamic_str = tr("Dynamic: Adjusts acceleration limits based on current speed");
QString predictive_str = tr("Predictive: Uses future trajectory data to anticipate needed adjustments");
if (option == LongitudinalTuningOption::PREDICTIVE) {
predictive_str = "<font color='white'><b>" + predictive_str + "</b></font>";
} else if (option == LongitudinalTuningOption::DYNAMIC) {
dynamic_str = "<font color='white'><b>" + dynamic_str + "</b></font>";
} else {
off_str = "<font color='white'><b>" + off_str + "</b></font>";
}
return QString("%1<br><br>%2<br>%3<br>%4<br>")
.arg(tr("Fine-tune your driving experience by adjusting acceleration smoothness with openpilot longitudinal control."))
.arg(off_str)
.arg(dynamic_str)
.arg(predictive_str);
}
};
@@ -0,0 +1,14 @@
/**
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
*
* This file is part of sunnypilot and is licensed under the MIT License.
* See the LICENSE.md file in the root directory for more details.
*/
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/mazda_settings.h"
MazdaSettings::MazdaSettings(QWidget *parent) : BrandSettingsInterface(parent) {
}
void MazdaSettings::updateSettings() {
}
@@ -0,0 +1,26 @@
/**
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
*
* This file is part of sunnypilot and is licensed under the MIT License.
* See the LICENSE.md file in the root directory for more details.
*/
#pragma once
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/brand_settings_interface.h"
#include "selfdrive/ui/qt/util.h"
#include "selfdrive/ui/sunnypilot/ui.h"
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h"
#include "selfdrive/ui/sunnypilot/qt/widgets/controls.h"
class MazdaSettings : public BrandSettingsInterface {
Q_OBJECT
public:
explicit MazdaSettings(QWidget *parent = nullptr);
void updateSettings() override;
private:
bool offroad = false;
};
@@ -0,0 +1,14 @@
/**
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
*
* This file is part of sunnypilot and is licensed under the MIT License.
* See the LICENSE.md file in the root directory for more details.
*/
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/nissan_settings.h"
NissanSettings::NissanSettings(QWidget *parent) : BrandSettingsInterface(parent) {
}
void NissanSettings::updateSettings() {
}
@@ -0,0 +1,26 @@
/**
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
*
* This file is part of sunnypilot and is licensed under the MIT License.
* See the LICENSE.md file in the root directory for more details.
*/
#pragma once
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/brand_settings_interface.h"
#include "selfdrive/ui/qt/util.h"
#include "selfdrive/ui/sunnypilot/ui.h"
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h"
#include "selfdrive/ui/sunnypilot/qt/widgets/controls.h"
class NissanSettings : public BrandSettingsInterface {
Q_OBJECT
public:
explicit NissanSettings(QWidget *parent = nullptr);
void updateSettings() override;
private:
bool offroad = false;
};
@@ -43,36 +43,73 @@ PlatformSelector::PlatformSelector() : ButtonControl(tr("Vehicle"), "", "") {
}
});
main_layout->addStretch(0);
refresh(offroad);
}
void PlatformSelector::refresh(bool _offroad) {
QString name = getPlatformBundle("name").toString();
platform = unrecognized_str;
QString platform_color = YELLOW_PLATFORM;
if (!name.isEmpty()) {
setValue(name);
platform = name;
platform_color = BLUE_PLATFORM;
brand = getPlatformBundle("brand").toString();
setText(tr("REMOVE"));
} else {
setText(tr("SEARCH"));
platform = unrecognized_str;
brand = "";
auto cp_bytes = params.get("CarParamsPersistent");
if (!cp_bytes.empty()) {
AlignedBuffer aligned_buf;
capnp::FlatArrayMessageReader cmsg(aligned_buf.align(cp_bytes.data(), cp_bytes.size()));
cereal::CarParams::Reader CP = cmsg.getRoot<cereal::CarParams>();
setValue(QString::fromStdString(CP.getCarFingerprint().cStr()));
platform = QString::fromStdString(CP.getCarFingerprint().cStr());
for (auto it = platforms.constBegin(); it != platforms.constEnd(); ++it) {
if (it.value()["platform"].toString() == platform) {
brand = it.value()["brand"].toString();
break;
}
}
if (platform == "MOCK") {
platform = unrecognized_str;
} else {
platform_color = GREEN_PLATFORM;
}
}
}
setValue(platform, platform_color);
setEnabled(true);
emit refreshPanel();
offroad = _offroad;
FingerprintStatus cur_status;
if (platform_color == GREEN_PLATFORM) {
cur_status = FingerprintStatus::AUTO_FINGERPRINT;
} else if (platform_color == BLUE_PLATFORM) {
cur_status = FingerprintStatus::MANUAL_FINGERPRINT;
} else {
cur_status = FingerprintStatus::UNRECOGNIZED;
}
setDescription(platformDescription(cur_status));
showDescription();
}
void PlatformSelector::setPlatform(const QString &platform) {
QVariantMap platform_data = platforms[platform];
void PlatformSelector::setPlatform(const QString &_platform) {
QVariantMap platform_data = platforms[_platform];
const QString offroad_msg = offroad ? tr("This setting will take effect immediately.") :
tr("This setting will take effect once the device enters offroad state.");
const QString msg = QString("<b>%1</b><br><br>%2")
.arg(platform, offroad_msg);
.arg(_platform, offroad_msg);
QString content("<body><h2 style=\"text-align: center;\">" + tr("Vehicle Selector") + "</h2><br>"
"<p style=\"text-align: center; margin: 0 128px; font-size: 50px;\">" + msg + "</p></body>");
@@ -80,7 +117,7 @@ void PlatformSelector::setPlatform(const QString &platform) {
if (ConfirmationDialog(content, tr("Confirm"), tr("Cancel"), true, this).exec()) {
QJsonObject json_bundle;
json_bundle["platform"] = platform_data["platform"].toString();
json_bundle["name"] = platform;
json_bundle["name"] = _platform;
json_bundle["make"] = platform_data["make"].toString();
json_bundle["brand"] = platform_data["brand"].toString();
json_bundle["model"] = platform_data["model"].toString();
@@ -9,6 +9,16 @@
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h"
static const QString GREEN_PLATFORM = "#00F100";
static const QString BLUE_PLATFORM = "#0086E9";
static const QString YELLOW_PLATFORM = "#FFD500";
enum class FingerprintStatus {
AUTO_FINGERPRINT,
MANUAL_FINGERPRINT,
UNRECOGNIZED,
};
class PlatformSelector : public ButtonControl {
Q_OBJECT
@@ -16,9 +26,15 @@ public:
PlatformSelector();
QVariant getPlatformBundle(const QString &key);
QString platform;
QString brand;
public slots:
void refresh(bool _offroad);
signals:
void refreshPanel();
private:
void searchPlatforms(const QString &query);
void setPlatform(const QString &platform = "");
@@ -26,4 +42,27 @@ private:
Params params;
bool offroad;
QString unrecognized_str = tr("Unrecognized Vehicle");
static QString platformDescription(FingerprintStatus status = FingerprintStatus::UNRECOGNIZED) {
QString auto_str = "🟢 - " + tr("Fingerprinted automatically");
QString manual_str = "🔵 - " + tr("Manually selected");
QString unrecognized_str = "🟡 - " + tr("Not fingerprinted or manually selected");
if (status == FingerprintStatus::AUTO_FINGERPRINT) {
auto_str = "<font color='white'><b>" + auto_str + "</b></font>";
} else if (status == FingerprintStatus::MANUAL_FINGERPRINT) {
manual_str = "<font color='white'><b>" + manual_str + "</b></font>";
} else {
unrecognized_str = "<font color='white'><b>" + unrecognized_str + "</b></font>";
}
return QString("%1<br>%2<br><br>%3<br>%4<br>%5")
.arg(tr("Select vehicle to force fingerprint manually."))
.arg(tr("Colors represent fingerprint status:"))
.arg(auto_str)
.arg(manual_str)
.arg(unrecognized_str);
}
};
@@ -0,0 +1,14 @@
/**
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
*
* This file is part of sunnypilot and is licensed under the MIT License.
* See the LICENSE.md file in the root directory for more details.
*/
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/rivian_settings.h"
RivianSettings::RivianSettings(QWidget *parent) : BrandSettingsInterface(parent) {
}
void RivianSettings::updateSettings() {
}
@@ -0,0 +1,26 @@
/**
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
*
* This file is part of sunnypilot and is licensed under the MIT License.
* See the LICENSE.md file in the root directory for more details.
*/
#pragma once
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/brand_settings_interface.h"
#include "selfdrive/ui/qt/util.h"
#include "selfdrive/ui/sunnypilot/ui.h"
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h"
#include "selfdrive/ui/sunnypilot/qt/widgets/controls.h"
class RivianSettings : public BrandSettingsInterface {
Q_OBJECT
public:
explicit RivianSettings(QWidget *parent = nullptr);
void updateSettings() override;
private:
bool offroad = false;
};
@@ -0,0 +1,14 @@
/**
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
*
* This file is part of sunnypilot and is licensed under the MIT License.
* See the LICENSE.md file in the root directory for more details.
*/
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/subaru_settings.h"
SubaruSettings::SubaruSettings(QWidget *parent) : BrandSettingsInterface(parent) {
}
void SubaruSettings::updateSettings() {
}
@@ -0,0 +1,26 @@
/**
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
*
* This file is part of sunnypilot and is licensed under the MIT License.
* See the LICENSE.md file in the root directory for more details.
*/
#pragma once
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/brand_settings_interface.h"
#include "selfdrive/ui/qt/util.h"
#include "selfdrive/ui/sunnypilot/ui.h"
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h"
#include "selfdrive/ui/sunnypilot/qt/widgets/controls.h"
class SubaruSettings : public BrandSettingsInterface {
Q_OBJECT
public:
explicit SubaruSettings(QWidget *parent = nullptr);
void updateSettings() override;
private:
bool offroad = false;
};
@@ -0,0 +1,14 @@
/**
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
*
* This file is part of sunnypilot and is licensed under the MIT License.
* See the LICENSE.md file in the root directory for more details.
*/
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/tesla_settings.h"
TeslaSettings::TeslaSettings(QWidget *parent) : BrandSettingsInterface(parent) {
}
void TeslaSettings::updateSettings() {
}
@@ -0,0 +1,26 @@
/**
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
*
* This file is part of sunnypilot and is licensed under the MIT License.
* See the LICENSE.md file in the root directory for more details.
*/
#pragma once
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/brand_settings_interface.h"
#include "selfdrive/ui/qt/util.h"
#include "selfdrive/ui/sunnypilot/ui.h"
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h"
#include "selfdrive/ui/sunnypilot/qt/widgets/controls.h"
class TeslaSettings : public BrandSettingsInterface {
Q_OBJECT
public:
explicit TeslaSettings(QWidget *parent = nullptr);
void updateSettings() override;
private:
bool offroad = false;
};
@@ -0,0 +1,14 @@
/**
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
*
* This file is part of sunnypilot and is licensed under the MIT License.
* See the LICENSE.md file in the root directory for more details.
*/
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/toyota_settings.h"
ToyotaSettings::ToyotaSettings(QWidget *parent) : BrandSettingsInterface(parent) {
}
void ToyotaSettings::updateSettings() {
}
@@ -0,0 +1,26 @@
/**
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
*
* This file is part of sunnypilot and is licensed under the MIT License.
* See the LICENSE.md file in the root directory for more details.
*/
#pragma once
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/brand_settings_interface.h"
#include "selfdrive/ui/qt/util.h"
#include "selfdrive/ui/sunnypilot/ui.h"
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h"
#include "selfdrive/ui/sunnypilot/qt/widgets/controls.h"
class ToyotaSettings : public BrandSettingsInterface {
Q_OBJECT
public:
explicit ToyotaSettings(QWidget *parent = nullptr);
void updateSettings() override;
private:
bool offroad = false;
};
@@ -0,0 +1,14 @@
/**
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
*
* This file is part of sunnypilot and is licensed under the MIT License.
* See the LICENSE.md file in the root directory for more details.
*/
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/volkswagen_settings.h"
VolkswagenSettings::VolkswagenSettings(QWidget *parent) : BrandSettingsInterface(parent) {
}
void VolkswagenSettings::updateSettings() {
}
@@ -0,0 +1,26 @@
/**
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
*
* This file is part of sunnypilot and is licensed under the MIT License.
* See the LICENSE.md file in the root directory for more details.
*/
#pragma once
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/brand_settings_interface.h"
#include "selfdrive/ui/qt/util.h"
#include "selfdrive/ui/sunnypilot/ui.h"
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h"
#include "selfdrive/ui/sunnypilot/qt/widgets/controls.h"
class VolkswagenSettings : public BrandSettingsInterface {
Q_OBJECT
public:
explicit VolkswagenSettings(QWidget *parent = nullptr);
void updateSettings() override;
private:
bool offroad = false;
};
@@ -7,26 +7,32 @@
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle_panel.h"
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/brand_settings_factory.h"
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/brands.h"
#include "selfdrive/ui/sunnypilot/qt/widgets/scrollview.h"
VehiclePanel::VehiclePanel(QWidget *parent) : QFrame(parent) {
main_layout = new QStackedLayout(this);
QVBoxLayout *main_layout = new QVBoxLayout(this);
main_layout->setContentsMargins(50, 20, 50, 20);
ListWidget *list = new ListWidget(this);
vehicleScreen = new QWidget(this);
QVBoxLayout *vlayout = new QVBoxLayout(vehicleScreen);
vlayout->setContentsMargins(50, 20, 50, 20);
platformSelector = new PlatformSelector();
QObject::connect(platformSelector, &PlatformSelector::refreshPanel, this, &VehiclePanel::updateBrandSettings);
list->addItem(platformSelector);
brandSettingsContainer = new QWidget(this);
brandSettingsContainerLayout = new QVBoxLayout(brandSettingsContainer);
brandSettingsContainerLayout->setContentsMargins(0, 0, 0, 0);
brandSettingsContainerLayout->setSpacing(0);
list->addItem(brandSettingsContainer);
ScrollViewSP *scroller = new ScrollViewSP(list, this);
vlayout->addWidget(scroller);
main_layout->addWidget(scroller);
currentBrandSettings = nullptr;
QObject::connect(uiState(), &UIState::offroadTransition, this, &VehiclePanel::updatePanel);
main_layout->addWidget(vehicleScreen);
main_layout->setCurrentWidget(vehicleScreen);
}
void VehiclePanel::showEvent(QShowEvent *event) {
@@ -34,7 +40,28 @@ void VehiclePanel::showEvent(QShowEvent *event) {
}
void VehiclePanel::updatePanel(bool _offroad) {
platformSelector->refresh(_offroad);
offroad = _offroad;
platformSelector->refresh(_offroad);
updateBrandSettings();
}
void VehiclePanel::updateBrandSettings() {
if (!isVisible()) {
return;
}
if (currentBrandSettings) {
brandSettingsContainerLayout->removeWidget(currentBrandSettings);
delete currentBrandSettings;
currentBrandSettings = nullptr;
}
if (BrandSettingsFactory::isBrandSupported(platformSelector->brand)) {
currentBrandSettings = BrandSettingsFactory::createBrandSettings(platformSelector->brand, this);
if (currentBrandSettings) {
currentBrandSettings->setContentsMargins(0, 0, 0, 0);
brandSettingsContainerLayout->addWidget(currentBrandSettings);
currentBrandSettings->updatePanel(offroad);
}
}
}
@@ -9,6 +9,7 @@
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h"
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/brand_settings_interface.h"
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/vehicle/platform_selector.h"
class VehiclePanel : public QFrame {
@@ -22,8 +23,12 @@ public slots:
void updatePanel(bool _offroad);
private:
QStackedLayout* main_layout = nullptr;
QWidget* vehicleScreen = nullptr;
PlatformSelector *platformSelector = nullptr;
bool offroad;
PlatformSelector* platformSelector = nullptr;
BrandSettingsInterface* currentBrandSettings = nullptr;
QWidget* brandSettingsContainer = nullptr;
QVBoxLayout* brandSettingsContainerLayout = nullptr;
bool offroad = false;
private slots:
void updateBrandSettings();
};
@@ -118,7 +118,7 @@ AbstractControlSP_SELECTOR::AbstractControlSP_SELECTOR(const QString &title, con
if (!title.isEmpty()) {
title_label = new QPushButton(title);
title_label->setFixedHeight(120);
title_label->setStyleSheet("font-size: 50px; font-weight: 450; text-align: left; border: none; padding: 20 0 0 0");
title_label->setStyleSheet("font-size: 50px; font-weight: 450; text-align: left; border: none; padding: 0 0 0 0");
main_layout->addWidget(title_label, 1);
connect(title_label, &QPushButton::clicked, [=]() {
@@ -148,7 +148,7 @@ AbstractControlSP_SELECTOR::AbstractControlSP_SELECTOR(const QString &title, con
// description
description = new QLabel(desc);
description->setContentsMargins(0, 20, 40, 20);
description->setContentsMargins(40, 20, 40, 20);
description->setStyleSheet("font-size: 40px; color: grey");
description->setWordWrap(true);
description->setVisible(false);
+40 -24
View File
@@ -219,7 +219,7 @@ class ButtonParamControlSP : public AbstractControlSP_SELECTOR {
public:
ButtonParamControlSP(const QString &param, const QString &title, const QString &desc, const QString &icon,
const std::vector<QString> &button_texts, const int minimum_button_width = 300) : AbstractControlSP_SELECTOR(title, desc, icon), button_texts(button_texts) {
const std::vector<QString> &button_texts, const int minimum_button_width = 300, const bool inline_layout = false) : AbstractControlSP_SELECTOR(title, desc, icon), button_texts(button_texts), is_inline_layout(inline_layout) {
const QString style = R"(
QPushButton {
border-radius: 20px;
@@ -246,6 +246,19 @@ public:
key = param.toStdString();
int value = atoi(params.get(key).c_str());
if (inline_layout) {
button_param_layout->setMargin(0);
button_param_layout->setSpacing(0);
if (!title.isEmpty()) {
main_layout->removeWidget(title_label);
hlayout->addWidget(title_label, 1);
}
if (spacingItem != nullptr && main_layout->indexOf(spacingItem) != -1) {
main_layout->removeItem(spacingItem);
spacingItem = nullptr;
}
}
button_group = new QButtonGroup(this);
button_group->setExclusive(true);
for (int i = 0; i < button_texts.size(); i++) {
@@ -254,12 +267,18 @@ public:
button->setChecked(i == value);
button->setStyleSheet(style);
button->setMinimumWidth(minimum_button_width);
if (i == 0) hlayout->addSpacing(2);
hlayout->addWidget(button);
if (i == 0) button_param_layout->addSpacing(2);
button_param_layout->addWidget(button);
button_group->addButton(button, i);
}
hlayout->setAlignment(Qt::AlignLeft);
button_param_layout->setAlignment(Qt::AlignLeft);
if (is_inline_layout) {
QFrame *container = new QFrame;
container->setLayout(button_param_layout);
container->setStyleSheet("background-color: #393939; border-radius: 20px;");
hlayout->addWidget(container);
}
QObject::connect(button_group, QOverload<int>::of(&QButtonGroup::buttonClicked), [=](int id) {
params.put(key, std::to_string(id));
@@ -314,6 +333,10 @@ public:
protected:
void paintEvent(QPaintEvent *event) override {
if (is_inline_layout) {
return;
}
QPainter p(this);
p.setRenderHint(QPainter::Antialiasing);
@@ -348,6 +371,8 @@ private:
std::vector<QString> button_texts;
bool button_group_enabled = true;
bool is_inline_layout;
QHBoxLayout *button_param_layout = is_inline_layout ? new QHBoxLayout() : hlayout;
};
class ListWidgetSP : public QWidget {
@@ -360,7 +385,7 @@ public:
outer_layout.addLayout(&inner_layout);
inner_layout.setMargin(0);
inner_layout.setSpacing(25); // default spacing is 25
outer_layout.addStretch();
outer_layout.addStretch(1);
}
inline void addItem(QWidget *w) { inner_layout.addWidget(w); }
inline void addItem(QLayout *layout) { inner_layout.addLayout(layout); }
@@ -416,8 +441,8 @@ class OptionControlSP : public AbstractControlSP_SELECTOR {
Q_OBJECT
private:
bool isInlineLayout;
QHBoxLayout *optionSelectorLayout = isInlineLayout ? new QHBoxLayout() : hlayout;
bool is_inline_layout;
QHBoxLayout *optionSelectorLayout = is_inline_layout ? new QHBoxLayout() : hlayout;
struct MinMaxValue {
int min_value;
@@ -438,7 +463,7 @@ private:
public:
OptionControlSP(const QString &param, const QString &title, const QString &desc, const QString &icon,
const MinMaxValue &range, const int per_value_change = 1, const bool inline_layout = false, const QMap<QString, QString> *valMap = nullptr) : AbstractControlSP_SELECTOR(title, desc, icon, nullptr), _title(title), valueMap(valMap), isInlineLayout(inline_layout) {
const MinMaxValue &range, const int per_value_change = 1, const bool inline_layout = false, const QMap<QString, QString> *valMap = nullptr) : AbstractControlSP_SELECTOR(title, desc, icon, nullptr), _title(title), valueMap(valMap), is_inline_layout(inline_layout) {
const QString style = R"(
QPushButton {
border-radius: 20px;
@@ -513,7 +538,7 @@ public:
}
optionSelectorLayout->setAlignment(Qt::AlignLeft);
if (isInlineLayout) {
if (is_inline_layout) {
QFrame *container = new QFrame;
container->setLayout(optionSelectorLayout);
container->setStyleSheet("background-color: #393939; border-radius: 20px;");
@@ -540,7 +565,7 @@ public:
protected:
void paintEvent(QPaintEvent *event) override {
if (isInlineLayout) {
if (is_inline_layout) {
return;
}
@@ -633,16 +658,6 @@ public:
}
}
protected:
// Override mouse release event to handle style updates smoothly
void mouseReleaseEvent(QMouseEvent *event) override {
if (!key.empty()) {
bool next_state = !params.getBool(key);
updateStyle(next_state);
}
QPushButton::mouseReleaseEvent(event);
}
private:
std::string key = "";
Params params;
@@ -651,13 +666,14 @@ private:
QString btn_enabled_off_style = "QPushButton:enabled { background-color: #393939; }";
QString btn_enabled_on_style = "QPushButton:enabled { background-color: #1e79e8; }";
QString btn_pressed_style = "QPushButton:pressed { background-color: #4A4A4A; }";
QString btn_disabled_stype = "QPushButton:disabled { background-color: #121212; color: #5C5C5C; }";
QString btn_off_pressed_style = "QPushButton:pressed { background-color: #4A4A4A; }";
QString btn_on_pressed_style = "QPushButton:pressed { background-color: #1E8FFF; }";
QString btn_disabled_style = "QPushButton:disabled { background-color: #121212; color: #5C5C5C; }";
void updateStyle(bool enabled) {
QString enabled_style = enabled ? btn_enabled_on_style : btn_enabled_off_style;
setStyleSheet(buttonStyle + enabled_style + btn_pressed_style + btn_disabled_stype);
QString pressed_style = enabled ? btn_on_pressed_style : btn_off_pressed_style;
setStyleSheet(buttonStyle + enabled_style + pressed_style + btn_disabled_style);
}
};
+11 -3
View File
@@ -37,7 +37,7 @@ DATA: dict[str, capnp.lib.capnp._DynamicStructBuilder] = dict.fromkeys(
"driverStateV2", "roadCameraState", "wideRoadCameraState", "driverCameraState"], None)
def setup_homescreen(click, pm: PubMaster, scroll=None):
pass
time.sleep(UI_DELAY)
def setup_settings_device(click, pm: PubMaster, scroll=None):
click(100, 100)
@@ -61,6 +61,7 @@ def setup_settings_software(click, pm: PubMaster, scroll=None):
time.sleep(UI_DELAY)
def setup_settings_firehose(click, pm: PubMaster, scroll=None):
setup_settings_device(click, pm)
scroll(-400, 278, 962)
click(278, 862)
@@ -151,7 +152,7 @@ def setup_keyboard_uppercase(click, pm: PubMaster, scroll=None):
def setup_driver_camera(click, pm: PubMaster, scroll=None):
setup_settings_device(click, pm)
click(1720, 620)
click(1720, 825)
DATA['deviceState'].deviceState.started = False
setup_onroad(click, pm)
DATA['deviceState'].deviceState.started = True
@@ -239,11 +240,17 @@ def setup_settings_steering_alc(click, pm: PubMaster, scroll=None):
click(970, 534)
time.sleep(UI_DELAY)
def setup_settings_trips(click, pm: PubMaster, scroll=None):
def setup_settings_driving(click, pm: PubMaster, scroll=None):
setup_settings_device(click, pm)
click(278, 962)
time.sleep(UI_DELAY)
def setup_settings_trips(click, pm: PubMaster, scroll=None):
setup_settings_device(click, pm)
scroll(-400, 278, 962)
click(278, 646)
time.sleep(UI_DELAY)
def setup_settings_vehicle(click, pm: PubMaster, scroll=None):
Params().put("CarPlatformBundle", json.dumps(
{
@@ -291,6 +298,7 @@ CASES.update({
"settings_steering": setup_settings_steering,
"settings_steering_mads": setup_settings_steering_mads,
"settings_steering_alc": setup_settings_steering_alc,
"settings_driving": setup_settings_driving,
"settings_trips": setup_settings_trips,
"settings_vehicle": setup_settings_vehicle,
})
+52
View File
@@ -0,0 +1,52 @@
import numpy as np
def compute_symmetric_slopes(x, y):
"""
Compute symmetric slopes for use in Hermite interpolation.
Args:
x: Array of x coordinates
y: Array of y coordinates
Returns:
Array of computed slopes at each point
"""
n = len(x)
m = np.zeros(n)
for i in range(n):
if i == 0:
m[i] = (y[i+1] - y[i]) / (x[i+1] - x[i])
elif i == n-1:
m[i] = (y[i] - y[i-1]) / (x[i] - x[i-1])
else:
m[i] = ((y[i+1] - y[i]) / (x[i+1] - x[i]) + (y[i] - y[i-1]) / (x[i] - x[i-1])) / 2
return m
def hermite_interpolate(x, xp, yp, slopes):
"""
Perform Hermite interpolation at point x given data points and slopes.
Args:
x: Point at which to interpolate
xp: Array of x coordinates of data points
yp: Array of y coordinates of data points
slopes: Array of slopes at data points
Returns:
Interpolated value at x
"""
x = np.clip(x, xp[0], xp[-1])
idx = np.searchsorted(xp, x) - 1
idx = np.clip(idx, 0, len(slopes) - 2)
x0, x1 = xp[idx], xp[idx+1]
y0, y1 = yp[idx], yp[idx+1]
m0, m1 = slopes[idx], slopes[idx+1]
t = (x - x0) / (x1 - x0)
h00 = 2*t**3 - 3*t**2 + 1
h10 = t**3 - 2*t**2 + t
h01 = -2*t**3 + 3*t**2
h11 = t**3 - t**2
return (h00 * y0) + (h10 * (x1 - x0) * m0) + (h01 * y1) + (h11 * (x1 - x0) * m1)
+1 -1
View File
@@ -72,7 +72,7 @@ class ModularAssistiveDrivingSystem:
if self.events.has(EventName.seatbeltNotLatched):
replace_event(EventName.seatbeltNotLatched, EventNameSP.silentSeatbeltNotLatched)
transition_paused_state()
if self.events.has(EventName.wrongGear) and (CS.standstill or CS.gearShifter == GearShifter.reverse):
if self.events.has(EventName.wrongGear) and (CS.vEgo < 2.5 or CS.gearShifter == GearShifter.reverse):
replace_event(EventName.wrongGear, EventNameSP.silentWrongGear)
transition_paused_state()
if self.events.has(EventName.reverseGear):
+5
View File
@@ -52,3 +52,8 @@ commonmodel_lib = lenv.Library('commonmodel', common_src)
lenvCython.Program('runners/runmodel_pyx.so', 'runners/runmodel_pyx.pyx', LIBS=cython_libs, FRAMEWORKS=frameworks)
lenvCython.Program('runners/snpemodel_pyx.so', 'runners/snpemodel_pyx.pyx', LIBS=[snpemodel_lib, snpe_lib, *cython_libs], FRAMEWORKS=frameworks, RPATH=snpe_rpath)
lenvCython.Program('models/commonmodel_pyx.so', 'models/commonmodel_pyx.pyx', LIBS=[commonmodel_lib, *cython_libs], FRAMEWORKS=frameworks)
if arch == "larch64":
thneed_lib = env.SharedLibrary('thneed', thneed_src, LIBS=[gpucommon, common, 'OpenCL', 'dl'])
thneedmodel_lib = env.Library('thneedmodel', ['runners/thneedmodel.cc'])
lenvCython.Program('runners/thneedmodel_pyx.so', 'runners/thneedmodel_pyx.pyx', LIBS=envCython["LIBS"]+[thneedmodel_lib, thneed_lib, gpucommon, common, 'dl', 'OpenCL'])
+1 -1
View File
@@ -23,7 +23,7 @@ from openpilot.sunnypilot.modeld.parse_model_outputs import Parser
from openpilot.sunnypilot.modeld.fill_model_msg import fill_model_msg, fill_pose_msg, PublishState
from openpilot.sunnypilot.modeld.constants import ModelConstants
from openpilot.sunnypilot.modeld.models.commonmodel_pyx import ModelFrame, CLContext
from openpilot.sunnypilot.modeld.runners.run_helpers import get_model_path, load_metadata, prepare_inputs, load_meta_constants
from openpilot.sunnypilot.models.helpers import get_model_path, load_metadata, prepare_inputs, load_meta_constants
PROCESS_NAME = "selfdrive.modeld.modeld_snpe"
SEND_RAW_PRED = os.getenv('SEND_RAW_PRED')
-92
View File
@@ -1,92 +0,0 @@
"""
Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
This file is part of sunnypilot and is licensed under the MIT License.
See the LICENSE.md file in the root directory for more details.
"""
import os
import pickle
import numpy as np
from cereal import custom
from openpilot.sunnypilot.modeld.constants import Meta, MetaTombRaider, MetaSimPose
from openpilot.sunnypilot.modeld.runners import ModelRunner
from openpilot.sunnypilot.models.helpers import get_active_bundle
from openpilot.system.hardware import PC
from openpilot.system.hardware.hw import Paths
from pathlib import Path
USE_ONNX = os.getenv('USE_ONNX', PC)
CUSTOM_MODEL_PATH = Paths.model_root()
METADATA_PATH = Path(__file__).parent / '../models/supercombo_metadata.pkl'
ModelManager = custom.ModelManagerSP
def get_model_path():
if USE_ONNX:
return {ModelRunner.ONNX: Path(__file__).parent / '../models/supercombo.onnx'}
if bundle := get_active_bundle():
drive_model = next(model for model in bundle.models if model.type == ModelManager.Type.drive)
return {ModelRunner.THNEED: f"{CUSTOM_MODEL_PATH}/{drive_model.fileName}"}
return {ModelRunner.THNEED: Path(__file__).parent / '../models/supercombo.thneed'}
def load_metadata():
metadata_path = METADATA_PATH
if bundle := get_active_bundle():
metadata_model = next(model for model in bundle.models if model.type == ModelManager.Type.metadata)
metadata_path = f"{CUSTOM_MODEL_PATH}/{metadata_model.fileName}"
with open(metadata_path, 'rb') as f:
return pickle.load(f)
def prepare_inputs(model_metadata) -> dict[str, np.ndarray]:
# img buffers are managed in openCL transform code so we don't pass them as inputs
inputs = {
k: np.zeros(v, dtype=np.float32).flatten()
for k, v in model_metadata['input_shapes'].items()
if 'img' not in k
}
return inputs
def load_meta_constants(model_metadata):
"""
Determines and loads the appropriate meta model class based on the metadata provided. The function checks
specific keys and conditions within the provided metadata dictionary to identify the corresponding meta
model class to return.
:param model_metadata: Dictionary containing metadata about the model. It includes
details such as input shapes, output slices, and other configurations for identifying
metadata-dependent meta model classes.
:type model_metadata: dict
:return: The appropriate meta model class (Meta, MetaSimPose, or MetaTombRaider)
based on the conditions and metadata provided.
:rtype: type
"""
meta = Meta # Default Meta
if 'sim_pose' in model_metadata['input_shapes'].keys():
# Meta for models with sim_pose input
meta = MetaSimPose
else:
# Meta for Tomb Raider, it does not include sim_pose input but has the same meta slice as previous models
meta_slice = model_metadata['output_slices']['meta']
meta_tf_slice = slice(5868, 5921, None)
if (
meta_slice.start == meta_tf_slice.start and
meta_slice.stop == meta_tf_slice.stop and
meta_slice.step == meta_tf_slice.step
):
meta = MetaTombRaider
return meta
+15 -7
View File
@@ -8,6 +8,7 @@ from openpilot.sunnypilot.modeld_v2 import MODEL_PATH, MODEL_PKL_PATH, METADATA_
from openpilot.sunnypilot.modeld_v2.models.commonmodel_pyx import DrivingModelFrame, CLMem
from openpilot.sunnypilot.modeld_v2.runners.ort_helpers import make_onnx_cpu_runner, ORT_TYPES_TO_NP_TYPES
from openpilot.sunnypilot.modeld_v2.runners.tinygrad_helpers import qcom_tensor_from_opencl_address
from openpilot.sunnypilot.modeld_v2.parse_model_outputs import Parser
from openpilot.system.hardware import TICI
from openpilot.system.hardware.hw import Paths
@@ -34,8 +35,8 @@ class ModelRunner(ABC):
if bundle := get_active_bundle():
bundle_models = {model.type.raw: model for model in bundle.models}
self._drive_model = bundle_models.get(ModelManager.Type.drive)
self._metadata_model = bundle_models.get(ModelManager.Type.metadata)
self._drive_model = bundle_models.get(ModelManager.Model.Type.supercombo)
self._metadata_model = self._drive_model.metadata
self.is_20hz = bundle.is20hz
# Override the metadata path if a metadata model is found in the active bundle
@@ -48,6 +49,7 @@ class ModelRunner(ABC):
self.input_shapes = self.model_metadata['input_shapes']
self.output_slices = self.model_metadata['output_slices']
self.inputs: dict = {}
self.parser = Parser()
@abstractmethod
def prepare_inputs(self, imgs_cl: dict[str, CLMem], numpy_inputs: dict[str, np.ndarray], frames: dict[str, DrivingModelFrame]) -> dict:
@@ -55,16 +57,22 @@ class ModelRunner(ABC):
raise NotImplementedError
@abstractmethod
def run_model(self):
def _run_model(self):
"""Run model inference with prepared inputs."""
raise NotImplementedError("This method should be implemented in subclasses.")
def slice_outputs(self, model_outputs: np.ndarray) -> dict:
def _slice_outputs(self, model_outputs: np.ndarray) -> dict:
"""Slice model outputs according to metadata configuration."""
parsed_outputs = {k: model_outputs[np.newaxis, v] for k, v in self.output_slices.items()}
if SEND_RAW_PRED:
parsed_outputs['raw_pred'] = model_outputs.copy()
return parsed_outputs
def run_model(self) -> dict[str, np.ndarray]:
"""Run model inference with prepared inputs and parse outputs."""
result: dict[str, np.ndarray] = self.parser.parse_outputs(self._slice_outputs(self._run_model()))
return result
class TinygradRunner(ModelRunner):
"""Tinygrad implementation of model runner for TICI hardware."""
@@ -74,7 +82,7 @@ class TinygradRunner(ModelRunner):
model_pkl_path = MODEL_PKL_PATH
if self._drive_model:
model_pkl_path = f"{CUSTOM_MODEL_PATH}/{self._drive_model.fileName}"
model_pkl_path = f"{CUSTOM_MODEL_PATH}/{self._drive_model.artifact.fileName}"
assert model_pkl_path.endswith('_tinygrad.pkl'), f"Invalid model file: {model_pkl_path} for TinygradRunner"
# Load Tinygrad model
@@ -108,7 +116,7 @@ class TinygradRunner(ModelRunner):
return self.inputs
def run_model(self):
def _run_model(self):
return self.model_run(**self.inputs).numpy().flatten()
@@ -130,5 +138,5 @@ class ONNXRunner(ModelRunner):
self.inputs[key] = frames[key].buffer_from_cl(imgs_cl[key]).reshape(self.input_shapes[key]).astype(dtype=self.input_to_nptype[key])
return self.inputs
def run_model(self):
def _run_model(self):
return self.runner.run(None, self.inputs)[0].flatten()
+14 -21
View File
@@ -18,7 +18,6 @@ from openpilot.common.transformations.camera import DEVICE_CAMERAS
from openpilot.common.transformations.model import get_warp_matrix
from openpilot.system import sentry
from openpilot.selfdrive.controls.lib.desire_helper import DesireHelper
from openpilot.sunnypilot.modeld_v2.parse_model_outputs import Parser
from openpilot.sunnypilot.modeld_v2.fill_model_msg import fill_model_msg, fill_pose_msg, PublishState
from openpilot.sunnypilot.modeld_v2.constants import ModelConstants
from openpilot.sunnypilot.modeld_v2.models.commonmodel_pyx import DrivingModelFrame, CLContext
@@ -27,6 +26,7 @@ from openpilot.sunnypilot.modeld_v2.meta_helper import load_meta_constants
from openpilot.sunnypilot.modeld_v2.model_runner import ONNXRunner, TinygradRunner
PROCESS_NAME = "selfdrive.modeld.modeld"
LAT_SMOOTH_SECONDS = 0.0
class FrameMeta:
@@ -41,7 +41,6 @@ class FrameMeta:
class ModelState:
frames: dict[str, DrivingModelFrame]
inputs: dict[str, np.ndarray]
output: np.ndarray
prev_desire: np.ndarray # for tracking the rising edge of the pulse
def __init__(self, context: CLContext):
@@ -54,8 +53,8 @@ class ModelState:
self.frames = {'input_imgs': DrivingModelFrame(context, buffer_length), 'big_input_imgs': DrivingModelFrame(context, buffer_length)}
self.prev_desire = np.zeros(ModelConstants.DESIRE_LEN, dtype=np.float32)
if self.model_runner.is_20hz:
self.full_features_20Hz = np.zeros((ModelConstants.FULL_HISTORY_BUFFER_LEN, ModelConstants.FEATURE_LEN), dtype=np.float32)
self.desire_20Hz = np.zeros((ModelConstants.FULL_HISTORY_BUFFER_LEN + 1, ModelConstants.DESIRE_LEN), dtype=np.float32)
self.full_features_buffer = np.zeros((ModelConstants.FULL_HISTORY_BUFFER_LEN, ModelConstants.FEATURE_LEN), dtype=np.float32)
self.full_desire = np.zeros((ModelConstants.FULL_HISTORY_BUFFER_LEN + 1, ModelConstants.DESIRE_LEN), dtype=np.float32)
# img buffers are managed in openCL transform code
self.numpy_inputs = {}
@@ -64,15 +63,10 @@ class ModelState:
if key not in self.frames: # Managed by opencl
self.numpy_inputs[key] = np.zeros(shape, dtype=np.float32)
self.parser = Parser()
if self.model_runner.is_20hz:
net_output_size = self.model_runner.model_metadata['output_shapes']['outputs'][1]
self.output = np.zeros(net_output_size, dtype=np.float32)
num_elements = self.numpy_inputs['features_buffer'].shape[1]
step_size = int(-100 / num_elements)
self.full_features_20Hz_idxs = np.arange(step_size, step_size * (num_elements + 1), step_size)[::-1]
self.full_features_buffer_idxs = np.arange(step_size, step_size * (num_elements + 1), step_size)[::-1]
self.desire_reshape_dims = (self.numpy_inputs['desire'].shape[0], self.numpy_inputs['desire'].shape[1], -1, self.numpy_inputs['desire'].shape[2])
def run(self, buf: VisionBuf, wbuf: VisionBuf, transform: np.ndarray, transform_wide: np.ndarray,
@@ -83,9 +77,9 @@ class ModelState:
self.prev_desire[:] = inputs['desire']
if self.model_runner.is_20hz:
self.desire_20Hz[:-1] = self.desire_20Hz[1:]
self.desire_20Hz[-1] = new_desire
self.numpy_inputs['desire'][:] = self.desire_20Hz.reshape(self.desire_reshape_dims).max(axis=2)
self.full_desire[:-1] = self.full_desire[1:]
self.full_desire[-1] = new_desire
self.numpy_inputs['desire'][:] = self.full_desire.reshape(self.desire_reshape_dims).max(axis=2)
else:
length = inputs['desire'].shape[0]
self.numpy_inputs['desire'][0, :-1] = self.numpy_inputs['desire'][0, 1:]
@@ -105,13 +99,12 @@ class ModelState:
return None
# Run model inference
self.output = self.model_runner.run_model()
outputs = self.parser.parse_outputs(self.model_runner.slice_outputs(self.output))
outputs = self.model_runner.run_model()
if self.model_runner.is_20hz:
self.full_features_20Hz[:-1] = self.full_features_20Hz[1:]
self.full_features_20Hz[-1] = outputs['hidden_state'][0, :]
self.numpy_inputs['features_buffer'][:] = self.full_features_20Hz[self.full_features_20Hz_idxs]
self.full_features_buffer[:-1] = self.full_features_buffer[1:]
self.full_features_buffer[-1] = outputs['hidden_state'][0, :]
self.numpy_inputs['features_buffer'][:] = self.full_features_buffer[self.full_features_buffer_idxs]
else:
feature_len = outputs['hidden_state'].shape[1]
self.numpy_inputs['features_buffer'][0, :-1] = self.numpy_inputs['features_buffer'][0, 1:]
@@ -171,7 +164,7 @@ def main(demo=False):
# messaging
pm = PubMaster(["modelV2", "drivingModelData", "cameraOdometry"])
sm = SubMaster(["deviceState", "carState", "roadCameraState", "liveCalibration", "driverMonitoringState", "carControl"])
sm = SubMaster(["deviceState", "carState", "roadCameraState", "liveCalibration", "driverMonitoringState", "carControl", "liveDelay"])
publish_state = PublishState()
params = Params()
@@ -196,8 +189,8 @@ def main(demo=False):
CP = messaging.log_from_bytes(params.get("CarParams", block=True), car.CarParams)
cloudlog.info("modeld got CarParams: %s", CP.brand)
# TODO this needs more thought, use .2s extra for now to estimate other delays
steer_delay = CP.steerActuatorDelay + .2
# Enable lagd support for modeld_v2
steer_delay = sm["liveDelay"].lateralDelay + LAT_SMOOTH_SECONDS
DH = DesireHelper()
+45 -50
View File
@@ -11,6 +11,7 @@ import time
import requests
from openpilot.common.params import Params
from openpilot.common.swaglog import cloudlog
from sunnypilot.models.helpers import is_bundle_version_compatible
from cereal import custom
@@ -19,68 +20,49 @@ class ModelParser:
"""Handles parsing of model data into cereal objects"""
@staticmethod
def _parse_model(full_name: str, file_name: str, uri_data: dict,
model_type: custom.ModelManagerSP.Type) -> custom.ModelManagerSP.Model:
model = custom.ModelManagerSP.Model()
def _parse_download_uri(download_uri_data) -> custom.ModelManagerSP.DownloadUri:
download_uri = custom.ModelManagerSP.DownloadUri()
download_uri.uri = download_uri_data.get("url")
download_uri.sha256 = download_uri_data.get("sha256")
return download_uri
download_uri.uri = uri_data["url"]
download_uri.sha256 = uri_data["sha256"]
@staticmethod
def _parse_artifact(artifact_data) -> custom.ModelManagerSP.Artifact:
artifact = custom.ModelManagerSP.Artifact()
artifact.fileName = artifact_data.get("file_name")
artifact.downloadUri = ModelParser._parse_download_uri(artifact_data.get("download_uri", {}))
return artifact
model.fullName = full_name
model.fileName = file_name
model.downloadUri = download_uri
model.type = model_type
@staticmethod
def _parse_model(model_data) -> custom.ModelManagerSP.Model:
model = custom.ModelManagerSP.Model()
model.type = model_data.get("type")
model.artifact = ModelParser._parse_artifact(model_data.get("artifact", {}))
if metadata := model_data.get("metadata"):
model.metadata = ModelParser._parse_artifact(metadata)
return model
@staticmethod
def _parse_bundle(key: str, value: dict) -> custom.ModelManagerSP.ModelBundle:
def _parse_bundle(bundle) -> custom.ModelManagerSP.ModelBundle:
model_bundle = custom.ModelManagerSP.ModelBundle()
# Parse main driving model
models = [
ModelParser._parse_model(
value["full_name"],
value["file_name"],
value["download_uri"],
custom.ModelManagerSP.Type.drive
)
]
# Parse navigation model if exists
if value.get("download_uri_nav"):
models.append(ModelParser._parse_model(
value["full_name_nav"],
value["file_name_nav"],
value["download_uri_nav"],
custom.ModelManagerSP.Type.navigation
))
# Parse metadata model if exists
if value.get("download_uri_metadata"):
models.append(ModelParser._parse_model(
value["full_name_metadata"],
value["file_name_metadata"],
value["download_uri_metadata"],
custom.ModelManagerSP.Type.metadata
))
model_bundle.index = int(value["index"])
model_bundle.internalName = key
model_bundle.displayName = value["display_name"]
model_bundle.models = models
model_bundle.index = int(bundle["index"])
model_bundle.internalName = bundle["short_name"]
model_bundle.displayName = bundle["display_name"]
model_bundle.models = [ModelParser._parse_model(model) for model in bundle.get("models",[])]
model_bundle.status = 0
model_bundle.generation = int(value["generation"])
model_bundle.environment = value["environment"]
model_bundle.runner = value.get("runner", custom.ModelManagerSP.Runner.snpe)
model_bundle.is20hz = value.get("is_20hz", False)
model_bundle.generation = int(bundle["generation"])
model_bundle.environment = bundle["environment"]
model_bundle.runner = bundle.get("runner", custom.ModelManagerSP.Runner.snpe)
model_bundle.is20hz = bundle.get("is_20hz", False)
model_bundle.minimumSelectorVersion = int(bundle["minimum_selector_version"])
return model_bundle
@staticmethod
def parse_models(json_data: dict) -> list[custom.ModelManagerSP.ModelBundle]:
return [ModelParser._parse_bundle(key, value) for key, value in json_data.items()]
found_bundles = [ModelParser._parse_bundle(bundle) for bundle in json_data.get("bundles", [])]
return [bundle for bundle in found_bundles if is_bundle_version_compatible(bundle.to_dict())]
class ModelCache:
@@ -122,7 +104,7 @@ class ModelCache:
class ModelFetcher:
"""Handles fetching and caching of model data from remote source"""
MODEL_URL = "https://docs.sunnypilot.ai/driving_models_v2.json"
MODEL_URL = "https://docs.sunnypilot.ai/driving_models_v3.json"
def __init__(self, params: Params):
self.params = params
@@ -143,7 +125,7 @@ class ModelFetcher:
cloudlog.exception("Error fetching models")
raise
def get_available_models(self) -> list[custom.ModelManagerSP.ModelBundle]:
def get_available_bundles(self) -> list[custom.ModelManagerSP.ModelBundle]:
"""Gets the list of available models, with smart cache handling"""
cached_data, is_expired = self.model_cache.get()
@@ -160,3 +142,16 @@ class ModelFetcher:
cloudlog.warning("Failed to fetch fresh data. Using expired cache as fallback")
return self.model_parser.parse_models(cached_data)
if __name__ == "__main__":
params = Params()
model_fetcher = ModelFetcher(params)
bundles = model_fetcher.get_available_bundles()
for bundle in bundles:
for model in bundle.models:
# Print model details
print(f"Bundle: {bundle.internalName}, Type: {model.type}, Status: {bundle.status}")
# Print artifact details
print(f"Artifact: {model.artifact.fileName}, Download URI: {model.artifact.downloadUri.uri}")
# Print metadata details
print(f"Metadata: {model.metadata.fileName}, Download URI: {model.metadata.downloadUri.uri}")
+116 -3
View File
@@ -7,8 +7,27 @@ See the LICENSE.md file in the root directory for more details.
import hashlib
import os
import pickle
import numpy as np
import json
from openpilot.common.params import Params
from cereal import custom, messaging
from cereal import custom
from openpilot.sunnypilot.modeld.constants import Meta, MetaTombRaider, MetaSimPose
from openpilot.sunnypilot.modeld.runners import ModelRunner
from openpilot.system.hardware import PC
from openpilot.system.hardware.hw import Paths
from pathlib import Path
CURRENT_SELECTOR_VERSION = 3
REQUIRED_MIN_SELECTOR_VERSION = 2
USE_ONNX = os.getenv('USE_ONNX', PC)
CUSTOM_MODEL_PATH = Paths.model_root()
METADATA_PATH = Path(__file__).parent / '../models/supercombo_metadata.pkl'
ModelManager = custom.ModelManagerSP
async def verify_file(file_path: str, expected_hash: str) -> bool:
@@ -24,13 +43,36 @@ async def verify_file(file_path: str, expected_hash: str) -> bool:
return sha256_hash.hexdigest().lower() == expected_hash.lower()
def is_bundle_version_compatible(bundle: dict) -> bool:
"""
Checks whether the model bundle is compatible with the current selector version constraints.
The bundle specifies a `minimum_selector_version`, which defines the minimum selector version
required to load the model. This function ensures that:
1. The model is not too old: the bundle must require at least `REQUIRED_MIN_SELECTOR_VERSION`.
2. The model is not too new: it must support the current selector version (`CURRENT_SELECTOR_VERSION`).
This allows the selector to enforce both a minimum and maximum range of supported models,
even if a model would otherwise be compatible.
:param bundle: Dictionary containing `minimum_selector_version`, as defined by the model bundle.
:type bundle: Dict
:return: True if the selector version is within the accepted range for the bundle; otherwise False.
:rtype: Bool
"""
return bool(REQUIRED_MIN_SELECTOR_VERSION <= bundle.get("minimumSelectorVersion", 0) <= CURRENT_SELECTOR_VERSION)
def get_active_bundle(params: Params = None) -> custom.ModelManagerSP.ModelBundle:
"""Gets the active model bundle from cache"""
if params is None:
params = Params()
if active_bundle := params.get("ModelManager_ActiveBundle"):
return messaging.log_from_bytes(active_bundle, custom.ModelManagerSP.ModelBundle)
try:
if (active_bundle := json.loads(params.get("ModelManager_ActiveBundle") or "{}")) and is_bundle_version_compatible(active_bundle):
return custom.ModelManagerSP.ModelBundle(**active_bundle)
except Exception:
pass
return None
@@ -71,3 +113,74 @@ def get_active_model_runner(params: Params = None, force_check=False) -> custom.
params.put("ModelRunnerTypeCache", str(int(runner_type)))
return runner_type
def _get_model():
if bundle := get_active_bundle():
drive_model = next(model for model in bundle.models if model.type == ModelManager.Model.Type.supercombo)
return drive_model
return None
def get_model_path():
if USE_ONNX:
return {ModelRunner.ONNX: Path(__file__).parent / '../models/supercombo.onnx'}
if model := _get_model():
return {ModelRunner.THNEED: f"{CUSTOM_MODEL_PATH}/{model.artifact.fileName}"}
return {ModelRunner.THNEED: Path(__file__).parent / '../models/supercombo.thneed'}
def load_metadata():
metadata_path = METADATA_PATH
if model := _get_model():
metadata_path = f"{CUSTOM_MODEL_PATH}/{model.metadata.fileName}"
with open(metadata_path, 'rb') as f:
return pickle.load(f)
def prepare_inputs(model_metadata) -> dict[str, np.ndarray]:
# img buffers are managed in openCL transform code so we don't pass them as inputs
inputs = {
k: np.zeros(v, dtype=np.float32).flatten()
for k, v in model_metadata['input_shapes'].items()
if 'img' not in k
}
return inputs
def load_meta_constants(model_metadata):
"""
Determines and loads the appropriate meta model class based on the metadata provided. The function checks
specific keys and conditions within the provided metadata dictionary to identify the corresponding meta
model class to return.
:param model_metadata: Dictionary containing metadata about the model. It includes
details such as input shapes, output slices, and other configurations for identifying
metadata-dependent meta model classes.
:type model_metadata: dict
:return: The appropriate meta model class (Meta, MetaSimPose, or MetaTombRaider)
based on the conditions and metadata provided.
:rtype: type
"""
meta = Meta # Default Meta
if 'sim_pose' in model_metadata['input_shapes'].keys():
# Meta for models with sim_pose input
meta = MetaSimPose
else:
# Meta for Tomb Raider, it does not include sim_pose input but has the same meta slice as previous models
meta_slice = model_metadata['output_slices']['meta']
meta_tf_slice = slice(5868, 5921, None)
if (
meta_slice.start == meta_tf_slice.start and
meta_slice.stop == meta_tf_slice.stop and
meta_slice.step == meta_tf_slice.step
):
meta = MetaTombRaider
return meta
+27 -15
View File
@@ -8,6 +8,7 @@ See the LICENSE.md file in the root directory for more details.
import asyncio
import os
import time
import json
import aiohttp
from openpilot.common.params import Params
@@ -73,43 +74,54 @@ class ModelManagerSP:
# Clean up start time after download completes
del self._download_start_times[model.fileName]
async def _process_model(self, model, destination_path: str) -> None:
async def _process_artifact(self, artifact, destination_path: str) -> None:
"""Processes a single model download including verification"""
url = model.downloadUri.uri
expected_hash = model.downloadUri.sha256
filename = model.fileName
if not artifact.downloadUri.uri:
return None
url = artifact.downloadUri.uri
expected_hash = artifact.downloadUri.sha256
filename = artifact.fileName
full_path = os.path.join(destination_path, filename)
try:
# Check existing file
if os.path.exists(full_path) and await verify_file(full_path, expected_hash):
model.downloadProgress.status = custom.ModelManagerSP.DownloadStatus.cached
model.downloadProgress.progress = 100
model.downloadProgress.eta = 0
artifact.downloadProgress.status = custom.ModelManagerSP.DownloadStatus.cached
artifact.downloadProgress.progress = 100
artifact.downloadProgress.eta = 0
self._report_status()
return
# Download and verify
await self._download_file(url, full_path, model)
await self._download_file(url, full_path, artifact)
if not await verify_file(full_path, expected_hash):
raise ValueError(f"Hash validation failed for {filename}")
model.downloadProgress.status = custom.ModelManagerSP.DownloadStatus.downloaded
model.downloadProgress.eta = 0
artifact.downloadProgress.status = custom.ModelManagerSP.DownloadStatus.downloaded
artifact.downloadProgress.eta = 0
self._report_status()
except Exception as e:
cloudlog.error(f"Error downloading {filename}: {str(e)}")
if os.path.exists(full_path):
os.remove(full_path)
model.downloadProgress.status = custom.ModelManagerSP.DownloadStatus.failed
model.downloadProgress.eta = 0
artifact.downloadProgress.status = custom.ModelManagerSP.DownloadStatus.failed
artifact.downloadProgress.eta = 0
self.selected_bundle.status = custom.ModelManagerSP.DownloadStatus.failed
self._report_status()
# Clean up start time if it exists
self._download_start_times.pop(model.fileName, None)
self._download_start_times.pop(artifact.fileName, None)
raise
async def _process_model(self, model, destination_path: str) -> None:
"""Processes a single model download including verification"""
model_artifact = model.artifact
metadata_artifact = model.metadata
await self._process_artifact(metadata_artifact, destination_path)
await self._process_artifact(model_artifact, destination_path)
def _report_status(self) -> None:
"""Reports current status through messaging system"""
msg = messaging.new_message('modelManagerSP', valid=True)
@@ -134,7 +146,7 @@ class ModelManagerSP:
await asyncio.gather(*tasks)
self.active_bundle = self.selected_bundle
self.active_bundle.status = custom.ModelManagerSP.DownloadStatus.downloaded
self.params.put("ModelManager_ActiveBundle", self.active_bundle.to_bytes())
self.params.put("ModelManager_ActiveBundle", json.dumps(self.active_bundle.to_dict()))
self.selected_bundle = None
except Exception:
@@ -154,7 +166,7 @@ class ModelManagerSP:
while True:
try:
self.available_models = self.model_fetcher.get_available_models()
self.available_models = self.model_fetcher.get_available_bundles()
self.active_bundle = get_active_bundle(self.params)
if index_to_download := self.params.get("ModelManager_DownloadIndex", block=False, encoding="utf-8"):
+1 -1
View File
@@ -1 +1 @@
82ead1a4ecffbde961382329a6cf71bc97a6b112fbfb1f175a51c28ee2472bec
8ef2dbcae743eb132167074a374f0a834308be31cffd532598bb13c3d7144a57
+25 -5
View File
@@ -11,6 +11,7 @@ from opendbc.car.car_helpers import can_fingerprint
from opendbc.car.interfaces import CarInterfaceBase
from opendbc.car.hyundai.radar_interface import RADAR_START_ADDR
from opendbc.car.hyundai.values import HyundaiFlags, DBC as HYUNDAI_DBC
from opendbc.sunnypilot.car.hyundai.longitudinal.helpers import LongitudinalTuningType
from opendbc.sunnypilot.car.hyundai.values import HyundaiFlagsSP
from openpilot.common.params import Params
from openpilot.common.swaglog import cloudlog
@@ -25,9 +26,24 @@ def log_fingerprint(CP: structs.CarParams) -> None:
else:
sentry.capture_fingerprint(CP.carFingerprint, CP.brand)
def _initialize_custom_longitudinal_tuning(CI: CarInterfaceBase, CP: structs.CarParams, CP_SP: structs.CarParamsSP,
params: Params = None) -> None:
if params is None:
params = Params()
def _initialize_neural_network_lateral_control(CP: structs.CarParams, CP_SP: structs.CarParamsSP, params: Params = None,
enabled: bool = False) -> None:
# Hyundai Custom Longitudinal Tuning
if CP.brand == 'hyundai':
hyundai_longitudinal_tuning = int(params.get("HyundaiLongitudinalTuning", encoding="utf8") or 0)
if hyundai_longitudinal_tuning == LongitudinalTuningType.DYNAMIC:
CP_SP.flags |= HyundaiFlagsSP.LONG_TUNING_DYNAMIC.value
if hyundai_longitudinal_tuning == LongitudinalTuningType.PREDICTIVE:
CP_SP.flags |= HyundaiFlagsSP.LONG_TUNING_PREDICTIVE.value
CP_SP = CI.get_longitudinal_tuning_sp(CP, CP_SP)
def _initialize_neural_network_lateral_control(CI: CarInterfaceBase, CP: structs.CarParams, CP_SP: structs.CarParamsSP,
params: Params = None, enabled: bool = False) -> None:
if params is None:
params = Params()
@@ -40,7 +56,7 @@ def _initialize_neural_network_lateral_control(CP: structs.CarParams, CP_SP: str
enabled = params.get_bool("NeuralNetworkLateralControl")
if enabled:
CarInterfaceBase.configure_torque_tune(CP.carFingerprint, CP.lateralTuning)
CI.configure_torque_tune(CP.carFingerprint, CP.lateralTuning)
CP_SP.neuralNetworkLateralControl.model.path = nnlc_model_path
CP_SP.neuralNetworkLateralControl.model.name = nnlc_model_name
@@ -61,8 +77,12 @@ def _initialize_radar_tracks(CP: structs.CarParams, CP_SP: structs.CarParamsSP,
CP.radarUnavailable = False
def setup_interfaces(CP: structs.CarParams, CP_SP: structs.CarParamsSP, params: Params = None):
_initialize_neural_network_lateral_control(CP, CP_SP, params)
def setup_interfaces(CI: CarInterfaceBase, params: Params = None) -> None:
CP = CI.CP
CP_SP = CI.CP_SP
_initialize_custom_longitudinal_tuning(CI, CP, CP_SP, params)
_initialize_neural_network_lateral_control(CI, CP, CP_SP, params)
_initialize_radar_tracks(CP, CP_SP, params)
@@ -0,0 +1,39 @@
from cereal import log
from openpilot.sunnypilot.common.utils import compute_symmetric_slopes, hermite_interpolate
class DynamicPersonalityController:
"""
Controller for managing dynamic personality-based following distances
that adapt based on vehicle speed and selected driving personality.
"""
def __init__(self):
pass
def get_dynamic_follow_distance(self, v_ego, personality=log.LongitudinalPersonality.standard):
"""
Calculate the dynamic follow distance based on current speed and personality.
Args:
v_ego: Current vehicle speed
personality: Selected longitudinal personality mode
Returns:
float: The calculated follow distance factor
"""
if personality == log.LongitudinalPersonality.relaxed:
x_vel = [0., 2.5, 5., 19.7, 22.2, 40.]
y_dist = [1.25, 1.25, 1.3, 1.3, 1.75, 1.75]
elif personality == log.LongitudinalPersonality.standard:
x_vel = [0., 2.5, 5., 19.7, 22.2, 40.]
y_dist = [1.20, 1.20, 1.275, 1.275, 1.50, 1.50]
elif personality == log.LongitudinalPersonality.aggressive:
x_vel = [0., 2.5, 6., 19.7, 22.2, 40.]
y_dist = [0.92, 0.9, 1.25, 1.25, 1.30, 1.30]
else:
raise NotImplementedError("Dynamic personality not supported")
slopes = compute_symmetric_slopes(x_vel, y_dist)
result = float(hermite_interpolate(v_ego, x_vel, y_dist, slopes))
return result
@@ -0,0 +1,116 @@
import pytest
import numpy as np
from cereal import log
from openpilot.sunnypilot.selfdrive.controls.lib.dynamic_personality.dynamic_personality_controller import DynamicPersonalityController
from openpilot.sunnypilot.common.utils import compute_symmetric_slopes, hermite_interpolate
class TestDynamicPersonalityController:
def setup_method(self):
"""Set up the controller before each test"""
self.controller = DynamicPersonalityController()
def test_compute_symmetric_slopes(self):
"""Test the symmetric slope computation method"""
x = np.array([1.0, 2.0, 3.0, 4.0])
y = np.array([2.0, 3.0, 5.0, 8.0])
# Manual calculation of expected slopes based on the algorithm:
# First slope: direct derivative from first segment
# Middle slopes: average of adjacent segments
# Last slope: direct derivative from last segment
expected_slopes = np.zeros(4)
expected_slopes[0] = (y[1] - y[0]) / (x[1] - x[0]) # 1.0
expected_slopes[1] = ((y[2] - y[1]) / (x[2] - x[1]) + (y[1] - y[0]) / (x[1] - x[0])) / 2 # (2.0 + 1.0) / 2 = 1.5
expected_slopes[2] = ((y[3] - y[2]) / (x[3] - x[2]) + (y[2] - y[1]) / (x[2] - x[1])) / 2 # (3.0 + 2.0) / 2 = 2.5
expected_slopes[3] = (y[3] - y[2]) / (x[3] - x[2]) # 3.0
computed_slopes = compute_symmetric_slopes(x, y)
np.testing.assert_allclose(computed_slopes, expected_slopes, rtol=1e-5)
def test_hermite_interpolate(self):
"""Test hermite interpolation with known values"""
xp = np.array([0.0, 10.0, 20.0])
yp = np.array([5.0, 15.0, 10.0])
slopes = np.array([1.0, 0.0, -1.0])
# Test at data points
assert hermite_interpolate(0.0, xp, yp, slopes) == pytest.approx(5.0)
assert hermite_interpolate(10.0, xp, yp, slopes) == pytest.approx(15.0)
assert hermite_interpolate(20.0, xp, yp, slopes) == pytest.approx(10.0)
# Test interpolation
assert hermite_interpolate(5.0, xp, yp, slopes) == pytest.approx(11.25)
assert hermite_interpolate(15.0, xp, yp, slopes) == pytest.approx(13.75)
def test_hermite_interpolate_clipping(self):
"""Test that hermite interpolation properly clips values outside the range"""
xp = np.array([0.0, 10.0, 20.0])
yp = np.array([5.0, 15.0, 10.0])
slopes = np.array([1.0, 0.0, -1.0])
# Test clipping below minimum
assert hermite_interpolate(-5.0, xp, yp, slopes) == pytest.approx(5.0)
# Test clipping above maximum
assert hermite_interpolate(25.0, xp, yp, slopes) == pytest.approx(10.0)
def test_get_dynamic_follow_distance_relaxed(self):
"""Test follow distance calculation for relaxed personality"""
# Test at specific data points
assert self.controller.get_dynamic_follow_distance(0.0, log.LongitudinalPersonality.relaxed) == pytest.approx(1.25)
assert self.controller.get_dynamic_follow_distance(5.0, log.LongitudinalPersonality.relaxed) == pytest.approx(1.3)
assert self.controller.get_dynamic_follow_distance(40.0, log.LongitudinalPersonality.relaxed) == pytest.approx(1.75)
# Test interpolation
assert self.controller.get_dynamic_follow_distance(20.0, log.LongitudinalPersonality.relaxed) > 1.3
assert self.controller.get_dynamic_follow_distance(20.0, log.LongitudinalPersonality.relaxed) < 1.75
def test_get_dynamic_follow_distance_standard(self):
"""Test follow distance calculation for standard personality"""
# Test at specific data points
assert self.controller.get_dynamic_follow_distance(0.0, log.LongitudinalPersonality.standard) == pytest.approx(1.20)
assert self.controller.get_dynamic_follow_distance(5.0, log.LongitudinalPersonality.standard) == pytest.approx(1.275)
assert self.controller.get_dynamic_follow_distance(40.0, log.LongitudinalPersonality.standard) == pytest.approx(1.50)
# Test interpolation
mid_value = self.controller.get_dynamic_follow_distance(21.0, log.LongitudinalPersonality.standard)
assert mid_value > 1.275
assert mid_value < 1.50
def test_get_dynamic_follow_distance_aggressive(self):
"""Test follow distance calculation for aggressive personality"""
# Test at specific data points
assert self.controller.get_dynamic_follow_distance(0.0, log.LongitudinalPersonality.aggressive) == pytest.approx(0.92)
assert self.controller.get_dynamic_follow_distance(6.0, log.LongitudinalPersonality.aggressive) == pytest.approx(1.25)
assert self.controller.get_dynamic_follow_distance(40.0, log.LongitudinalPersonality.aggressive) == pytest.approx(1.30)
# Test intermediate value
assert self.controller.get_dynamic_follow_distance(4.0, log.LongitudinalPersonality.aggressive) > 0.9
assert self.controller.get_dynamic_follow_distance(4.0, log.LongitudinalPersonality.aggressive) < 1.25
def test_get_dynamic_follow_distance_invalid(self):
"""Test that an invalid personality raises NotImplementedError"""
with pytest.raises(NotImplementedError, match="Dynamic personality not supported"):
self.controller.get_dynamic_follow_distance(10.0, 999) # Using an invalid personality value
def test_get_dynamic_follow_distance_comparison(self):
"""Test that relaxed > standard > aggressive for follow distances"""
speed = 20.0
relaxed = self.controller.get_dynamic_follow_distance(speed, log.LongitudinalPersonality.relaxed)
standard = self.controller.get_dynamic_follow_distance(speed, log.LongitudinalPersonality.standard)
aggressive = self.controller.get_dynamic_follow_distance(speed, log.LongitudinalPersonality.aggressive)
assert relaxed > standard
assert standard > aggressive
def test_speed_impact_on_follow_distance(self):
"""Test that follow distance increases with speed"""
personality = log.LongitudinalPersonality.standard
low_speed = self.controller.get_dynamic_follow_distance(5.0, personality)
high_speed = self.controller.get_dynamic_follow_distance(30.0, personality)
assert high_speed > low_speed
@@ -7,7 +7,9 @@ See the LICENSE.md file in the root directory for more details.
from cereal import messaging, custom
from opendbc.car import structs
from openpilot.sunnypilot.models.helpers import get_active_model_runner
from openpilot.sunnypilot.selfdrive.controls.lib.dec.dec import DynamicExperimentalController
from openpilot.common.params import Params
DecState = custom.LongitudinalPlanSP.DynamicExperimentalControl.DynamicExperimentalControlState
@@ -15,6 +17,18 @@ DecState = custom.LongitudinalPlanSP.DynamicExperimentalControl.DynamicExperimen
class LongitudinalPlannerSP:
def __init__(self, CP: structs.CarParams, mpc):
self.dec = DynamicExperimentalController(CP, mpc)
self.is_stock = get_active_model_runner() == custom.ModelManagerSP.Runner.stock
self.params = Params()
self.param_read_counter = 0
self.dynamic_personality = False
self.read_param()
def read_param(self):
try:
self.dynamic_personality = self.params.get_bool("DynamicPersonality")
except AttributeError:
pass
def get_mpc_mode(self) -> str | None:
if not self.dec.active():
@@ -23,6 +37,9 @@ class LongitudinalPlannerSP:
return self.dec.mode()
def update(self, sm: messaging.SubMaster) -> None:
self.param_read_counter += 1
if self.param_read_counter % 50 == 0:
self.read_param()
self.dec.update(sm)
def publish_longitudinal_plan_sp(self, sm: messaging.SubMaster, pm: messaging.PubMaster) -> None:
@@ -24,7 +24,7 @@ class TestNNLCFingerprintBase:
CP_SP = CarInterface.get_non_essential_params_sp(CP, car_name)
CI = CarInterface(CP, CP_SP)
sunnypilot_interfaces.setup_interfaces(CP, CP_SP, Params())
sunnypilot_interfaces.setup_interfaces(CI, Params())
return CI
@@ -22,7 +22,7 @@ class TestNNTorqueModel:
CP_SP = CarInterface.get_non_essential_params_sp(CP, car_name)
CI = CarInterface(CP, CP_SP)
sunnypilot_interfaces.setup_interfaces(CP, CP_SP, params)
sunnypilot_interfaces.setup_interfaces(CI, params)
CP_SP = convert_to_capnp(CP_SP)
@@ -51,7 +51,7 @@ class TestNeuralNetworkLateralControl:
CP_SP = CarInterface.get_non_essential_params_sp(CP, car_name)
CI = CarInterface(CP, CP_SP)
sunnypilot_interfaces.setup_interfaces(CP, CP_SP, params)
sunnypilot_interfaces.setup_interfaces(CI, params)
CP_SP = convert_to_capnp(CP_SP)
VM = VehicleModel(CP)
+7 -7
View File
@@ -845,15 +845,15 @@ def ws_manage(ws: WebSocket, end_event: threading.Event) -> None:
onroad_prev = onroad
if sock is not None:
if sys.platform == 'darwin': # macOS
sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPALIVE, 7 if onroad else 30)
else:
# While not sending data, onroad, we can expect to time out in 7 + (7 * 2) = 21s
# offroad, we can expect to time out in 30 + (10 * 3) = 60s
# FIXME: TCP_USER_TIMEOUT is effectively 2x for some reason (32s), so it's mostly unused
# While not sending data, onroad, we can expect to time out in 7 + (7 * 2) = 21s
# offroad, we can expect to time out in 30 + (10 * 3) = 60s
# FIXME: TCP_USER_TIMEOUT is effectively 2x for some reason (32s), so it's mostly unused
if sys.platform == 'linux':
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_USER_TIMEOUT, 16000 if onroad else 0)
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, 7 if onroad else 30)
elif sys.platform == 'darwin':
sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPALIVE, 7 if onroad else 30)
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, 7 if onroad else 10)
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPCNT, 2 if onroad else 3)
+12 -12
View File
@@ -56,29 +56,29 @@
},
{
"name": "boot",
"url": "https://commadist.azureedge.net/agnosupdate/boot-9b07cc366919890cc88bdd45c8c7e643bf66557caf9ad6a1373accc6dcacd892.img.xz",
"hash": "9b07cc366919890cc88bdd45c8c7e643bf66557caf9ad6a1373accc6dcacd892",
"hash_raw": "9b07cc366919890cc88bdd45c8c7e643bf66557caf9ad6a1373accc6dcacd892",
"url": "https://commadist.azureedge.net/agnosupdate/boot-3d8e848796924081f5a6b3d745808b1117ae2ec41c03f2d41ee2e75633bd6425.img.xz",
"hash": "3d8e848796924081f5a6b3d745808b1117ae2ec41c03f2d41ee2e75633bd6425",
"hash_raw": "3d8e848796924081f5a6b3d745808b1117ae2ec41c03f2d41ee2e75633bd6425",
"size": 18479104,
"sparse": false,
"full_check": true,
"has_ab": true,
"ondevice_hash": "41d31b862fec1b87879b508c405adb9d7b5c0a3324f7350bd904f451605b06cf"
"ondevice_hash": "2075104847d1c96a06f07e85efb9f48d0e792d75a059047eae7ba4b463ffeadf"
},
{
"name": "system",
"url": "https://commadist.azureedge.net/agnosupdate/system-02a6f40cc305faf703ab8f993a49d720043e4df1c0787d60dcf87eedb9f2ffde.img.xz",
"hash": "c56256a64e6d7e16886e39a4263ffb686ed0f03d3a665c3552f54a39723f8824",
"hash_raw": "02a6f40cc305faf703ab8f993a49d720043e4df1c0787d60dcf87eedb9f2ffde",
"size": 4404019200,
"url": "https://commadist.azureedge.net/agnosupdate/system-b22bd239c6f9527596fd49b98b7d521a563f99b90953ce021ee36567498e99c7.img.xz",
"hash": "cb9bfde1e995b97f728f5d5ad8d7a0f7a01544db5d138ead9b2350f222640939",
"hash_raw": "b22bd239c6f9527596fd49b98b7d521a563f99b90953ce021ee36567498e99c7",
"size": 5368709120,
"sparse": true,
"full_check": false,
"has_ab": true,
"ondevice_hash": "ed2e11f52beb8559223bf9fb989fd4ef5d2ce66eeb11ae0053fff8e41903a533",
"ondevice_hash": "e92a1f34158c60364c8d47b8ebbb6e59edf8d4865cd5edfeb2355d6f54f617fc",
"alt": {
"hash": "02a6f40cc305faf703ab8f993a49d720043e4df1c0787d60dcf87eedb9f2ffde",
"url": "https://commadist.azureedge.net/agnosupdate/system-02a6f40cc305faf703ab8f993a49d720043e4df1c0787d60dcf87eedb9f2ffde.img",
"size": 4404019200
"hash": "b22bd239c6f9527596fd49b98b7d521a563f99b90953ce021ee36567498e99c7",
"url": "https://commadist.azureedge.net/agnosupdate/system-b22bd239c6f9527596fd49b98b7d521a563f99b90953ce021ee36567498e99c7.img",
"size": 5368709120
}
}
]
+25 -25
View File
@@ -339,62 +339,62 @@
},
{
"name": "boot",
"url": "https://commadist.azureedge.net/agnosupdate/boot-9b07cc366919890cc88bdd45c8c7e643bf66557caf9ad6a1373accc6dcacd892.img.xz",
"hash": "9b07cc366919890cc88bdd45c8c7e643bf66557caf9ad6a1373accc6dcacd892",
"hash_raw": "9b07cc366919890cc88bdd45c8c7e643bf66557caf9ad6a1373accc6dcacd892",
"url": "https://commadist.azureedge.net/agnosupdate/boot-3d8e848796924081f5a6b3d745808b1117ae2ec41c03f2d41ee2e75633bd6425.img.xz",
"hash": "3d8e848796924081f5a6b3d745808b1117ae2ec41c03f2d41ee2e75633bd6425",
"hash_raw": "3d8e848796924081f5a6b3d745808b1117ae2ec41c03f2d41ee2e75633bd6425",
"size": 18479104,
"sparse": false,
"full_check": true,
"has_ab": true,
"ondevice_hash": "41d31b862fec1b87879b508c405adb9d7b5c0a3324f7350bd904f451605b06cf"
"ondevice_hash": "2075104847d1c96a06f07e85efb9f48d0e792d75a059047eae7ba4b463ffeadf"
},
{
"name": "system",
"url": "https://commadist.azureedge.net/agnosupdate/system-02a6f40cc305faf703ab8f993a49d720043e4df1c0787d60dcf87eedb9f2ffde.img.xz",
"hash": "c56256a64e6d7e16886e39a4263ffb686ed0f03d3a665c3552f54a39723f8824",
"hash_raw": "02a6f40cc305faf703ab8f993a49d720043e4df1c0787d60dcf87eedb9f2ffde",
"size": 4404019200,
"url": "https://commadist.azureedge.net/agnosupdate/system-b22bd239c6f9527596fd49b98b7d521a563f99b90953ce021ee36567498e99c7.img.xz",
"hash": "cb9bfde1e995b97f728f5d5ad8d7a0f7a01544db5d138ead9b2350f222640939",
"hash_raw": "b22bd239c6f9527596fd49b98b7d521a563f99b90953ce021ee36567498e99c7",
"size": 5368709120,
"sparse": true,
"full_check": false,
"has_ab": true,
"ondevice_hash": "ed2e11f52beb8559223bf9fb989fd4ef5d2ce66eeb11ae0053fff8e41903a533",
"ondevice_hash": "e92a1f34158c60364c8d47b8ebbb6e59edf8d4865cd5edfeb2355d6f54f617fc",
"alt": {
"hash": "02a6f40cc305faf703ab8f993a49d720043e4df1c0787d60dcf87eedb9f2ffde",
"url": "https://commadist.azureedge.net/agnosupdate/system-02a6f40cc305faf703ab8f993a49d720043e4df1c0787d60dcf87eedb9f2ffde.img",
"size": 4404019200
"hash": "b22bd239c6f9527596fd49b98b7d521a563f99b90953ce021ee36567498e99c7",
"url": "https://commadist.azureedge.net/agnosupdate/system-b22bd239c6f9527596fd49b98b7d521a563f99b90953ce021ee36567498e99c7.img",
"size": 5368709120
}
},
{
"name": "userdata_90",
"url": "https://commadist.azureedge.net/agnosupdate/userdata_90-175a5d3353daa5e7b7d9939cb51a2f1d7e6312b4708ad654c351da2f1ef4f108.img.xz",
"hash": "ff01a0ca5a2ea6661f836248043a211cd8d71c3269c139cb574b56855fabc3f4",
"hash_raw": "175a5d3353daa5e7b7d9939cb51a2f1d7e6312b4708ad654c351da2f1ef4f108",
"url": "https://commadist.azureedge.net/agnosupdate/userdata_90-4bb7239f7e82c846e4d2584c0c433f03c582a80950de4094e6c190563d6d84ac.img.xz",
"hash": "b18001a2a87caa070fabf6321f8215ac353d6444564e3f86329b4dccc039ce54",
"hash_raw": "4bb7239f7e82c846e4d2584c0c433f03c582a80950de4094e6c190563d6d84ac",
"size": 96636764160,
"sparse": true,
"full_check": true,
"has_ab": false,
"ondevice_hash": "2f3d69e5015a45a18c3553f2edc5706aacd6d84a4b3d5010a3d76a1a3aa910b0"
"ondevice_hash": "15ce16f2349d5b4d5fec6ad1e36222b1ae744ed10b8930bc9af75bd244dccb3c"
},
{
"name": "userdata_89",
"url": "https://commadist.azureedge.net/agnosupdate/userdata_89-61bdaf82d3036af6e45e86adbaab02918b41debd5b58b6708d7987084d514d1b.img.xz",
"hash": "714970777e02bb53a71640735bdb84b3071ecbc0346b978ce12eb667d75634ec",
"hash_raw": "61bdaf82d3036af6e45e86adbaab02918b41debd5b58b6708d7987084d514d1b",
"url": "https://commadist.azureedge.net/agnosupdate/userdata_89-e36b59bf9ff755b6ca488df2ba1e20da8f7dab6b8843129f3fdcccd7ff2ff7d8.img.xz",
"hash": "12682cf54596ab1bd1c2464c4ca85888e4e06b47af5ff7d0432399e9907e2f64",
"hash_raw": "e36b59bf9ff755b6ca488df2ba1e20da8f7dab6b8843129f3fdcccd7ff2ff7d8",
"size": 95563022336,
"sparse": true,
"full_check": true,
"has_ab": false,
"ondevice_hash": "95e6889a808b8d266660990e67e917cf3b63179f23588565af7f2fa54f70ac76"
"ondevice_hash": "e4df9dea47ff04967d971263d50c17460ef240457e8d814e7c4f409f7493eb8a"
},
{
"name": "userdata_30",
"url": "https://commadist.azureedge.net/agnosupdate/userdata_30-a40553d3fd339cb0107f1cc55fd532820f192a7a9a90d05243ad00fcbf804997.img.xz",
"hash": "33e5ab398620f147b885a9627b2608591bd9e1c9aa481eb705dc86707d706ea2",
"hash_raw": "a40553d3fd339cb0107f1cc55fd532820f192a7a9a90d05243ad00fcbf804997",
"url": "https://commadist.azureedge.net/agnosupdate/userdata_30-fe1d86f5322c675c58b3ae9753a4670abf44a25746bf6ac822aed108bb577282.img.xz",
"hash": "fa471703be0f0647617d183312d5209d23407f1628e4ab0934e6ec54b1a6b263",
"hash_raw": "fe1d86f5322c675c58b3ae9753a4670abf44a25746bf6ac822aed108bb577282",
"size": 32212254720,
"sparse": true,
"full_check": true,
"has_ab": false,
"ondevice_hash": "cd6291dea40968123f7af0b831cbfbbd6e515b676f2e427ae47ff358f6ac148e"
"ondevice_hash": "0b5b2402c9caa1ed7b832818e66580c974251e735bda91f2f226c41499d5616e"
}
]
]
@@ -56,10 +56,10 @@ class TestPowerDraw:
def valid_msg_count(self, proc, msg_counts):
msgs_received = sum(msg_counts[msg] for msg in proc.msgs)
msgs_expected = self.get_expected_messages(proc)
return np.core.numeric.isclose(msgs_expected, msgs_received, rtol=.02, atol=2)
return np.isclose(msgs_expected, msgs_received, rtol=.02, atol=2)
def valid_power_draw(self, proc, used):
return np.core.numeric.isclose(used, proc.power, rtol=proc.rtol, atol=proc.atol)
return np.isclose(used, proc.power, rtol=proc.rtol, atol=proc.atol)
def tabulate_msg_counts(self, msgs_and_power):
msg_counts = defaultdict(int)
+1 -1
View File
@@ -203,7 +203,7 @@ void handle_user_flag(LoggerdState *s) {
// mark route for uploading
Params params;
std::string routes = Params().get("AthenadRecentlyViewedRoutes");
std::string routes = params.get("AthenadRecentlyViewedRoutes");
params.put("AthenadRecentlyViewedRoutes", routes + "," + s->logger.routeName());
prev_segment = s->logger.segment();
+1 -1
View File
@@ -89,7 +89,7 @@ class Uploader:
def list_upload_files(self, metered: bool) -> Iterator[tuple[str, str, str]]:
r = self.params.get("AthenadRecentlyViewedRoutes", encoding="utf8")
requested_routes = [] if r is None else r.split(",")
requested_routes = [] if r is None else [route for route in r.split(",") if route]
for logdir in listdir_by_creation(self.root):
path = os.path.join(self.root, logdir)
+6 -5
View File
@@ -1,16 +1,17 @@
import os
import errno
import xattr
_cached_attributes: dict[tuple, bytes | None] = {}
def getxattr(path: str, attr_name: str) -> bytes | None:
key = (path, attr_name)
if key not in _cached_attributes:
try:
response = os.getxattr(path, attr_name)
response = xattr.getxattr(path, attr_name)
except OSError as e:
# ENODATA means attribute hasn't been set
if e.errno == errno.ENODATA:
# ENODATA (Linux) or ENOATTR (macOS) means attribute hasn't been set
if e.errno == errno.ENODATA or (hasattr(errno, 'ENOATTR') and e.errno == errno.ENOATTR):
response = None
else:
raise
@@ -19,4 +20,4 @@ def getxattr(path: str, attr_name: str) -> bytes | None:
def setxattr(path: str, attr_name: str, attr_value: bytes) -> None:
_cached_attributes.pop((path, attr_name), None)
return os.setxattr(path, attr_name, attr_value)
xattr.setxattr(path, attr_name, attr_value)
+2
View File
@@ -45,6 +45,8 @@ def manager_init() -> None:
("AutoLaneChangeTimer", "0"),
("AutoLaneChangeBsmDelay", "0"),
("DynamicExperimentalControl", "0"),
("DynamicPersonality", "0"),
("HyundaiLongitudinalTuning", "0"),
("Mads", "1"),
("MadsMainCruiseAllowed", "1"),
("MadsPauseLateralOnBrake", "0"),
+20 -13
View File
@@ -1,6 +1,7 @@
#!/usr/bin/env python3
import numpy as np
from functools import cache
import threading
from cereal import messaging
from openpilot.common.realtime import Ratekeeper
@@ -52,12 +53,18 @@ class Mic:
self.sound_pressure_weighted = 0
self.sound_pressure_level_weighted = 0
def update(self):
msg = messaging.new_message('microphone', valid=True)
msg.microphone.soundPressure = float(self.sound_pressure)
msg.microphone.soundPressureWeighted = float(self.sound_pressure_weighted)
self.lock = threading.Lock()
msg.microphone.soundPressureWeightedDb = float(self.sound_pressure_level_weighted)
def update(self):
with self.lock:
sound_pressure = self.sound_pressure
sound_pressure_weighted = self.sound_pressure_weighted
sound_pressure_level_weighted = self.sound_pressure_level_weighted
msg = messaging.new_message('microphone', valid=True)
msg.microphone.soundPressure = float(sound_pressure)
msg.microphone.soundPressureWeighted = float(sound_pressure_weighted)
msg.microphone.soundPressureWeightedDb = float(sound_pressure_level_weighted)
self.pm.send('microphone', msg)
self.rk.keep_time()
@@ -69,17 +76,17 @@ class Mic:
Logged A-weighted equivalents are rough approximations of the human-perceived loudness.
"""
with self.lock:
self.measurements = np.concatenate((self.measurements, indata[:, 0]))
self.measurements = np.concatenate((self.measurements, indata[:, 0]))
while self.measurements.size >= FFT_SAMPLES:
measurements = self.measurements[:FFT_SAMPLES]
while self.measurements.size >= FFT_SAMPLES:
measurements = self.measurements[:FFT_SAMPLES]
self.sound_pressure, _ = calculate_spl(measurements)
measurements_weighted = apply_a_weighting(measurements)
self.sound_pressure_weighted, self.sound_pressure_level_weighted = calculate_spl(measurements_weighted)
self.sound_pressure, _ = calculate_spl(measurements)
measurements_weighted = apply_a_weighting(measurements)
self.sound_pressure_weighted, self.sound_pressure_level_weighted = calculate_spl(measurements_weighted)
self.measurements = self.measurements[FFT_SAMPLES:]
self.measurements = self.measurements[FFT_SAMPLES:]
@retry(attempts=7, delay=3)
def get_stream(self, sd):
+1 -1
View File
@@ -7,7 +7,7 @@ from openpilot.common.basedir import BASEDIR
from openpilot.common.swaglog import cloudlog
from openpilot.system.hardware import HARDWARE
DEFAULT_FPS = 30
DEFAULT_FPS = 60
FPS_LOG_INTERVAL = 5 # Seconds between logging FPS drops
FPS_DROP_THRESHOLD = 0.9 # FPS drop threshold for triggering a warning
FPS_CRITICAL_THRESHOLD = 0.5 # Critical threshold for triggering strict actions
+15 -2
View File
@@ -4,6 +4,7 @@ from enum import IntEnum
MOUSE_WHEEL_SCROLL_SPEED = 30
INERTIA_FRICTION = 0.95 # The rate at which the inertia slows down
MIN_VELOCITY = 0.1 # Minimum velocity before stopping the inertia
DRAG_THRESHOLD = 5 # Pixels of movement to consider it a drag, not a click
class ScrollState(IntEnum):
@@ -16,10 +17,12 @@ class GuiScrollPanel:
def __init__(self, show_vertical_scroll_bar: bool = False):
self._scroll_state: ScrollState = ScrollState.IDLE
self._last_mouse_y: float = 0.0
self._start_mouse_y: float = 0.0 # Track the initial mouse position for drag detection
self._offset = rl.Vector2(0, 0)
self._view = rl.Rectangle(0, 0, 0, 0)
self._show_vertical_scroll_bar: bool = show_vertical_scroll_bar
self._velocity_y = 0.0 # Velocity for inertia
self._is_dragging = False
def handle_scroll(self, bounds: rl.Rectangle, content: rl.Rectangle) -> rl.Vector2:
mouse_pos = rl.get_mouse_position()
@@ -35,20 +38,27 @@ class GuiScrollPanel:
self._scroll_state = ScrollState.DRAGGING_SCROLLBAR
self._last_mouse_y = mouse_pos.y
self._start_mouse_y = mouse_pos.y # Record starting position
self._velocity_y = 0.0 # Reset velocity when drag starts
self._is_dragging = False # Reset dragging flag
if self._scroll_state != ScrollState.IDLE:
if rl.is_mouse_button_down(rl.MouseButton.MOUSE_BUTTON_LEFT):
delta_y = mouse_pos.y - self._last_mouse_y
# Check if movement exceeds the drag threshold
total_drag = abs(mouse_pos.y - self._start_mouse_y)
if total_drag > DRAG_THRESHOLD:
self._is_dragging = True
if self._scroll_state == ScrollState.DRAGGING_CONTENT:
self._offset.y += delta_y
else:
elif self._scroll_state == ScrollState.DRAGGING_SCROLLBAR:
delta_y = -delta_y
self._last_mouse_y = mouse_pos.y
self._velocity_y = delta_y # Update velocity during drag
else:
elif rl.is_mouse_button_released(rl.MouseButton.MOUSE_BUTTON_LEFT):
self._scroll_state = ScrollState.IDLE
# Handle mouse wheel scrolling
@@ -73,3 +83,6 @@ class GuiScrollPanel:
self._offset.y = max(min(self._offset.y, 0), -max_scroll_y)
return self._offset
def is_click_valid(self) -> bool:
return self._scroll_state == ScrollState.IDLE and not self._is_dragging and rl.is_mouse_button_released(rl.MouseButton.MOUSE_BUTTON_LEFT)
+53
View File
@@ -0,0 +1,53 @@
import pyray as rl
ON_COLOR = rl.GREEN
OFF_COLOR = rl.Color(0x39, 0x39, 0x39, 255)
KNOB_COLOR = rl.WHITE
BG_HEIGHT = 60
KNOB_HEIGHT = 80
WIDTH = 160
class Toggle:
def __init__(self, x, y, initial_state=False):
self._state = initial_state
self._rect = rl.Rectangle(x, y, WIDTH, KNOB_HEIGHT)
def handle_input(self):
if rl.is_mouse_button_pressed(rl.MOUSE_LEFT_BUTTON):
mouse_pos = rl.get_mouse_position()
if rl.check_collision_point_rec(mouse_pos, self._rect):
self._state = not self._state
def get_state(self):
return self._state
def render(self):
self._draw_background()
self._draw_knob()
def _draw_background(self):
bg_rect = rl.Rectangle(
self._rect.x + 5,
self._rect.y + (KNOB_HEIGHT - BG_HEIGHT) / 2,
self._rect.width - 10,
BG_HEIGHT,
)
rl.draw_rectangle_rounded(bg_rect, 1.0, 10, ON_COLOR if self._state else OFF_COLOR)
def _draw_knob(self):
knob_radius = KNOB_HEIGHT / 2
knob_x = self._rect.x + knob_radius if not self._state else self._rect.x + self._rect.width - knob_radius
knob_y = self._rect.y + knob_radius
rl.draw_circle(int(knob_x), int(knob_y), knob_radius, KNOB_COLOR)
if __name__ == "__main__":
from openpilot.system.ui.lib.application import gui_app
gui_app.init_window("Text toggle example")
toggle = Toggle(100, 100)
for _ in gui_app.render():
toggle.handle_input()
toggle.render()

Some files were not shown because too many files have changed in this diff Show More