Compare commits

...

250 Commits

Author SHA1 Message Date
Jason Wen
057c526e4e Merge branch 'feature/slc' into ui-sla-control-only 2025-09-18 13:32:09 -04:00
Jason Wen
9798e111fc make sure to return the correct type 2025-09-18 13:31:55 -04:00
Jason Wen
1a2d9a6ca3 rename and move around 2025-09-18 12:20:01 -04:00
Jason Wen
ce53fe715a start rename 2025-09-18 11:30:11 -04:00
Jason Wen
02eefa9b7a Merge branch 'feature/slc' into ui-sla-control-only 2025-09-18 10:58:38 -04:00
Jason Wen
48b4febb08 fix 2025-09-18 10:57:47 -04:00
Jason Wen
17429857a5 auto by default for now (reimplement in another PR) 2025-09-18 10:06:05 -04:00
Jason Wen
e317cb4ab2 Merge branch 'feature/slc' into ui-sla-control-only 2025-09-18 06:57:53 -04:00
Jason Wen
f64797f87c overriding state 2025-09-18 06:44:34 -04:00
Jason Wen
15c51ddcb2 more rename 2025-09-18 06:27:12 -04:00
Jason Wen
bdebc861ac rename policy 2025-09-18 00:38:01 -04:00
Jason Wen
2542e86587 params rename 2025-09-18 00:36:36 -04:00
Jason Wen
1c65c7e084 long only 2025-09-18 00:35:50 -04:00
Jason Wen
d6fb5e079e Speed Limit Warning (SLW) in another PR 2025-09-18 00:33:29 -04:00
Jason Wen
e58d810fa4 Merge branch 'feature/slc' into ui-sla-control-only
# Conflicts:
#	selfdrive/ui/sunnypilot/qt/offroad/settings/longitudinal_panel.h
#	sunnypilot/selfdrive/controls/lib/speed_limit_controller/speed_limit_controller.py
#	sunnypilot/selfdrive/controls/lib/speed_limit_controller/speed_limit_resolver.py
#	sunnypilot/selfdrive/controls/lib/speed_limit_controller/state.py
2025-09-18 00:20:55 -04:00
Jason Wen
c895969de1 in another PR 2025-09-18 00:20:00 -04:00
Jason Wen
a2e6dfdb1a Merge remote-tracking branch 'sunnypilot/sunnypilot/master' into feature/slc
# Conflicts:
#	selfdrive/ui/sunnypilot/qt/offroad/settings/longitudinal_panel.cc
#	selfdrive/ui/sunnypilot/qt/offroad/settings/longitudinal_panel.h
2025-09-18 00:09:10 -04:00
Jason Wen
c95cff27e8 Speed Limit Control -> Speed Limit Assist 2025-09-18 00:08:23 -04:00
Jason Wen
4a656e9b80 Merge remote-tracking branch 'sunnypilot/sunnypilot/master' into feature/slc
# Conflicts:
#	cereal/custom.capnp
#	selfdrive/controls/lib/longitudinal_planner.py
#	sunnypilot/selfdrive/controls/lib/longitudinal_planner.py
2025-09-17 23:51:27 -04:00
Jason Wen
d877444264 Merge branch 'master' into feature/slc 2025-09-14 21:34:37 -04:00
Jason Wen
3c0d16ae69 less 2025-09-09 23:41:32 -04:00
Jason Wen
c5d95f0e8f add carstateSP 2025-09-09 23:34:26 -04:00
Jason Wen
8f8561b88d already happens while in enabled 2025-09-09 23:00:43 -04:00
Jason Wen
6b217a0d10 update event 2025-09-09 16:55:14 -04:00
Jason Wen
2ac51c182b fix events 2025-09-09 16:37:23 -04:00
Jason Wen
183896f01a move around 2025-09-09 11:05:55 -04:00
Jason Wen
c928e32c04 fix 2025-09-09 02:53:37 -04:00
Jason Wen
8ae9974988 more lint 2025-09-09 02:38:15 -04:00
Jason Wen
e217604b20 lint 2025-09-09 02:14:07 -04:00
Jason Wen
1fd2a8ca1d no longer needed 2025-09-09 02:08:14 -04:00
Jason Wen
29ff34821c more resolver refactor 2025-09-09 02:04:24 -04:00
Jason Wen
51d2d7d5f5 turn off debug 2025-09-09 01:43:44 -04:00
Jason Wen
bf64fa29f7 refactor speed limit resolver 2025-09-09 01:43:38 -04:00
Jason Wen
20eca71fc5 v_target 2025-09-09 01:04:07 -04:00
Jason Wen
27e112e70c comments 2025-09-08 22:53:03 -04:00
Jason Wen
1365d7925c directly return statuses 2025-09-08 22:52:13 -04:00
Jason Wen
f6b855655a refactor new session check 2025-09-08 22:46:07 -04:00
Jason Wen
44b2e3dff3 refactor preactive timeout check 2025-09-08 22:36:32 -04:00
Jason Wen
6278b9000c one method state machine 2025-09-08 22:27:24 -04:00
Jason Wen
014213d867 typo 2025-09-08 21:09:25 -04:00
DevTekVE
95f75ecf29 Merge branch 'master' into feature/slc 2025-09-08 12:17:37 +02:00
Jason Wen
8b5290b462 no 2025-09-02 22:15:13 -04:00
Jason Wen
0e40024548 finish it up 2025-09-02 22:13:06 -04:00
Jason Wen
711a43082a init better 2025-09-02 22:09:26 -04:00
Jason Wen
87beff9cad use vCruiseCluster for set speed 2025-09-02 21:53:05 -04:00
Jason Wen
e1ac6fef51 more 2025-09-02 11:23:44 -04:00
Jason Wen
fa26dda544 more 2025-09-02 11:14:01 -04:00
Jason Wen
c3e513053c Merge remote-tracking branch 'sunnypilot/sunnypilot/master' into feature/slc 2025-09-02 01:16:59 -04:00
Jason Wen
28544dc803 wrap 2025-09-02 01:16:50 -04:00
Jason Wen
19bb39f09a some more tests 2025-09-02 01:14:37 -04:00
Jason Wen
6acb23ba30 fix 2025-09-02 01:07:27 -04:00
Jason Wen
db9e410bee lint 2025-09-02 01:05:04 -04:00
Jason Wen
0adb5570b9 not really needed yet 2025-09-02 01:05:00 -04:00
Jason Wen
157181bc86 more tests 2025-09-02 01:02:30 -04:00
Jason Wen
b325fb2a9e new tests, fixes controller 2025-09-02 00:54:59 -04:00
Jason Wen
8174b2da51 offset default > off 2025-09-01 23:49:47 -04:00
Jason Wen
7824e73272 rename 2025-09-01 22:29:26 -04:00
Jason Wen
ebc117af1d clean 2025-09-01 22:29:00 -04:00
Jason Wen
800920f9bc no more tempinactive 2025-08-31 23:19:53 -04:00
Jason Wen
dcf0dd5d80 fix tests 2025-08-31 12:12:22 -04:00
Jason Wen
e4ae2a7774 introduce disabled, no longer transitions at inactive 2025-08-31 12:06:01 -04:00
Jason Wen
3fe2ec883f no 2025-08-31 10:11:41 -04:00
Jason Wen
3d065a066c wide open 2025-08-31 10:11:01 -04:00
Jason Wen
25668fcf33 no speed factor engage type yet 2025-08-31 10:02:12 -04:00
Jason Wen
db2edc944d no warning in this PR 2025-08-31 10:01:02 -04:00
Jason Wen
1fc7071584 off 2025-08-31 09:58:22 -04:00
Jason Wen
4eab2b01e4 end session if long_active but slc inactive at any given time 2025-08-31 01:27:19 -04:00
Jason Wen
5d2fc14a24 always reset state 2025-08-31 01:24:13 -04:00
Jason Wen
20ff0b8d7b bump 2025-08-31 01:19:22 -04:00
Jason Wen
d9b11dec9a too limiting 2025-08-31 01:07:43 -04:00
Jason Wen
0f2db833d5 some tests 2025-08-31 01:05:26 -04:00
Jason Wen
0ff8e3be3c track session 2025-08-31 00:33:08 -04:00
Jason Wen
e454ca42c9 use frame instead of time 2025-08-31 00:14:57 -04:00
Jason Wen
27fe7aa83f test init 2025-08-31 00:06:07 -04:00
Jason Wen
791f597bf6 fix params 2025-08-31 00:05:12 -04:00
Jason Wen
2bd87ff6a0 no need to pass sm 2025-08-30 23:48:43 -04:00
Jason Wen
a6ea4e31b4 split speed limit resolver out of slc 2025-08-30 23:44:20 -04:00
Jason Wen
84abd66bba use frames instead 2025-08-30 23:35:04 -04:00
Jason Wen
e6a6476740 some 2025-08-30 22:43:02 -04:00
Jason Wen
22fd56e32e Revert "not used for now"
This reverts commit f0083d6241.
2025-08-30 22:40:49 -04:00
Jason Wen
f0083d6241 not used for now 2025-08-30 22:38:31 -04:00
Jason Wen
68d7a955f7 Merge remote-tracking branch 'sunnypilot/sunnypilot/master' into feature/slc 2025-08-30 22:17:41 -04:00
Jason Wen
e1d5b9019b fix 2025-08-30 22:00:13 -04:00
Jason Wen
3408873018 wrong cruise speed 2025-08-30 21:54:42 -04:00
Jason Wen
f2fe63fa5f use existing 2025-08-29 01:03:21 -04:00
Jason Wen
6bea163998 no need 2025-08-29 00:59:03 -04:00
Jason Wen
6956c6ef05 no 2025-08-29 00:57:24 -04:00
Jason Wen
20feab0eae this 2025-08-28 00:20:46 -04:00
Jason Wen
2aee69d267 rename 2025-08-28 00:15:08 -04:00
Jason Wen
13122c6c1d auto draft 2025-08-26 23:26:15 -04:00
Jason Wen
1081dc4d6c rearrange 2025-08-26 20:46:05 -04:00
Jason Wen
727a4ae8cb unused 2025-08-26 20:44:14 -04:00
Jason Wen
561210d2d2 internalize output 2025-08-26 20:34:17 -04:00
Jason Wen
863a8a43a9 Merge remote-tracking branch 'sunnypilot/sunnypilot/master' into feature/slc 2025-08-26 19:10:26 -04:00
Jason Wen
30d5f4ed52 more fixes 2025-08-26 19:08:12 -04:00
nayan
28eb825013 Revert "mapd: improved system transforms GPS navigation from a basic "am I near a road?" check into an intelligent map matching system that understands movement patterns, GPS reliability, and road network"
This reverts commit ea1e521306.
2025-08-25 17:26:13 -04:00
rav4kumar
ea1e521306 mapd: improved system transforms GPS navigation from a basic "am I near a road?" check into an intelligent map matching system that understands movement patterns, GPS reliability, and road network 2025-08-25 17:21:55 -04:00
nayan
2d937295ed Merge branch 'feature/slc' into ui/slc-ui 2025-08-25 17:14:41 -04:00
nayan
4a0b30ae35 omg sunny.. pls 2025-08-25 16:29:36 -04:00
nayan
b8620fb843 Merge branch 'feature/slc' into ui/slc-ui 2025-08-25 16:25:44 -04:00
nayan
200d6145dc sunny pls 2025-08-25 16:00:49 -04:00
Jason Wen
1f017c411c drop new state machine for now 2025-08-25 13:31:28 -04:00
Jason Wen
ca1a626d7a fix 2025-08-25 08:16:08 -04:00
Jason Wen
9822179d47 no more 2025-08-25 08:01:50 -04:00
Jason Wen
ff0c891b5f params fix 2025-08-24 23:27:57 -04:00
Jason Wen
2e0ace119c fix import 2025-08-24 23:27:57 -04:00
Jason Wen
132a109798 rename 2025-08-24 23:27:57 -04:00
Jason Wen
7b9568d0ab user confirm in another PR 2025-08-24 23:27:57 -04:00
Jason Wen
676f48c9b5 Merge remote-tracking branch 'sunnypilot/sunnypilot/master' into feature/slc
# Conflicts:
#	common/params_keys.h
#	selfdrive/selfdrived/selfdrived.py
2025-08-24 22:25:57 -04:00
Kumar
c768cd1973 Merge branch 'feature/slc' into ui/slc-ui 2025-08-10 09:15:11 -07:00
Jason Wen
24059b22f3 Merge remote-tracking branch 'sunnypilot/sunnypilot/master' into feature/slc
# Conflicts:
#	common/params_keys.h
#	opendbc_repo
#	sunnypilot/selfdrive/controls/lib/longitudinal_planner.py
#	system/manager/manager.py
2025-08-02 23:47:22 -04:00
nayan
7b8b0ab949 Merge remote-tracking branch 'origin/feature/slc' into ui/slc-ui
# Conflicts:
#	opendbc_repo
2025-07-04 14:16:02 -04:00
nayan
b5b86fe75e Merge remote-tracking branch 'origin/master-new' into feature/slc
# Conflicts:
#	opendbc_repo
2025-07-04 14:06:35 -04:00
nayan
8d65bf1232 Merge remote-tracking branch 'origin/master-new' into ui/slc-ui
# Conflicts:
#	opendbc_repo
#	selfdrive/ui/sunnypilot/SConscript
#	selfdrive/ui/sunnypilot/qt/offroad/settings/longitudinal_panel.cc
#	selfdrive/ui/sunnypilot/qt/offroad/settings/longitudinal_panel.h
2025-07-03 18:09:03 -04:00
rav4kumar
a39c7cf11d Merge remote-tracking branch 'origin/feature/slc' into ui/slc-ui 2025-06-08 19:11:46 -07:00
Jason Wen
3f7e1e2d16 use CC.longActive 2025-06-07 20:19:20 -04:00
Jason Wen
3864dc5c88 Merge branch 'feature/slc' into ui/slc-ui
# Conflicts:
#	opendbc_repo
2025-06-07 17:39:47 -04:00
Jason Wen
01f32f2c3d Merge remote-tracking branch 'sunnypilot/sunnypilot/master-new' into feature/slc
# Conflicts:
#	opendbc_repo
2025-06-07 17:39:28 -04:00
Jason Wen
95905de1ff Merge branch 'feature/slc' into ui/slc-ui 2025-06-07 17:13:53 -04:00
Jason Wen
374150618c Merge remote-tracking branch 'sunnypilot/sunnypilot/master-new' into feature/slc 2025-06-07 17:10:49 -04:00
Jason Wen
ccd8d5a592 fix tests 2025-06-07 17:10:01 -04:00
Jason Wen
7294709139 lint 2025-06-07 12:08:59 -04:00
Jason Wen
f683f897bb Merge branch 'feature/slc' into ui/slc-ui 2025-06-07 12:07:54 -04:00
Jason Wen
75cffc4100 quote...? 2025-06-07 12:05:13 -04:00
Jason Wen
01f4d23a50 use gps data directly 2025-06-07 11:45:11 -04:00
Jason Wen
a998bf781e refactor 2025-06-07 11:39:26 -04:00
Jason Wen
71f85ec9d3 Merge remote-tracking branch 'sunnypilot/sunnypilot/master-new' into feature/slc 2025-06-07 05:47:48 -04:00
Jason Wen
02ba1af1b5 fix test 2025-06-07 05:46:56 -04:00
Jason Wen
3bfbc5e629 Merge branch 'feature/slc' into ui/slc-ui 2025-06-07 05:17:29 -04:00
Jason Wen
2615735b87 use it directly 2025-06-07 05:17:14 -04:00
Jason Wen
d0b37d645f Merge branch 'feature/slc' into ui/slc-ui 2025-06-07 04:40:13 -04:00
Jason Wen
678216de2c no need to check alive 2025-06-07 04:39:55 -04:00
Jason Wen
dfb8b0963b extra 2025-06-07 04:18:31 -04:00
Jason Wen
d58a875090 upstream opendbc again 2025-06-07 03:52:34 -04:00
Jason Wen
5d39bd211f Merge branch 'feature/slc' into ui/slc-ui 2025-06-07 03:50:55 -04:00
Jason Wen
55931e080a Merge remote-tracking branch 'sunnypilot/sunnypilot/master-new' into feature/slc
# Conflicts:
#	cereal/custom.capnp
#	common/params_keys.h
#	sunnypilot/mapd/live_map_data/__init__.py
#	sunnypilot/mapd/live_map_data/base_map_data.py
#	sunnypilot/mapd/live_map_data/debug.py
#	sunnypilot/mapd/live_map_data/osm_map_data.py
#	sunnypilot/mapd/live_map_data/standalone.py
#	sunnypilot/mapd/mapd_installer.py
#	sunnypilot/mapd/mapd_manager.py
2025-06-07 03:50:28 -04:00
Jason Wen
de2770ee91 rebump 2025-06-07 03:34:08 -04:00
Jason Wen
168337ab73 Revert "in another pr"
This reverts commit a29bccff12.
2025-06-07 03:33:58 -04:00
Jason Wen
0242fd8ce6 Revert "slc in another pr"
This reverts commit 3a6987e6
2025-06-07 03:33:55 -04:00
Jason Wen
8b594eef91 Merge branch 'feature/slc' into ui/slc-ui 2025-06-06 22:54:16 -04:00
Jason Wen
13e987f2ad Merge branch 'mapd' into feature/slc 2025-06-06 22:53:38 -04:00
Jason Wen
0c3773f5d2 even more type hint 2025-06-06 22:53:00 -04:00
Jason Wen
f2faa1262c type hints 2025-06-06 22:50:58 -04:00
Jason Wen
70ef1c0fd5 slight more cleanup 2025-06-06 22:43:42 -04:00
Jason Wen
6a8f31636a combine pm 2025-06-06 22:29:36 -04:00
Jason Wen
6546100afc use directly 2025-06-06 22:27:58 -04:00
Jason Wen
a05f7255c1 from old implementation 2025-06-06 22:24:01 -04:00
Jason Wen
f3eff3fdbd type hint and slight cleanup 2025-06-06 22:22:32 -04:00
Jason Wen
8dc32ac90f unused 2025-06-06 22:04:11 -04:00
Jason Wen
02da318901 sort 2025-06-06 21:56:54 -04:00
Jason Wen
235ffe374b unused 2025-06-06 21:51:38 -04:00
Jason Wen
601e0e082e set core affinity for all realtime processes 2025-06-06 21:51:19 -04:00
Jason Wen
748a0b6481 use horizontal accuracy 2025-06-06 21:35:52 -04:00
Jason Wen
0539cc7348 use horizontal accuracy 2025-06-06 21:31:44 -04:00
Jason Wen
3a6987e6ba slc in another pr 2025-06-06 18:54:05 -04:00
Jason Wen
89d332105f qlog 2025-06-06 18:49:57 -04:00
Jason Wen
4bbde8071d no longer need data type 2025-06-06 18:49:28 -04:00
Jason Wen
a29bccff12 in another pr 2025-06-06 18:49:18 -04:00
Jason Wen
ce0cdcaa17 back to upstream 2025-06-06 18:47:00 -04:00
Jason Wen
dc2402cf30 Merge remote-tracking branch 'sunnypilot/sunnypilot/master-new' into feature/slc
# Conflicts:
#	common/params_keys.h
#	opendbc_repo
2025-06-06 18:46:00 -04:00
Jason Wen
3ca50ef990 Merge branch 'feature/slc' into ui/slc-ui 2025-06-05 23:39:26 -04:00
Jason Wen
064f7a84df Merge remote-tracking branch 'sunnypilot/sunnypilot/master-new' into feature/slc
# Conflicts:
#	opendbc_repo
2025-06-05 23:39:10 -04:00
nayan8teen
a0a8280ac8 Merge remote-tracking branch 'origin/ui/slc-ui' into ui/slc-ui 2025-06-05 19:01:22 -04:00
nayan8teen
8df7f2c743 update margins 2025-06-05 19:01:03 -04:00
Jason Wen
bf277d68f0 Merge branch 'feature/slc' into ui/slc-ui 2025-06-05 02:45:17 -04:00
Jason Wen
7ca82593f0 type hint all 2025-06-05 02:44:55 -04:00
Jason Wen
dc2e5ef460 type hint all 2025-06-05 02:43:56 -04:00
Jason Wen
0306ba701b don't reset if gas pressed 2025-06-05 02:25:55 -04:00
Jason Wen
5d705d3f6d no need for brake pressed 2025-06-05 02:25:05 -04:00
Jason Wen
845a57a21a rename 2025-06-05 02:24:43 -04:00
Jason Wen
cb3f57110b more mypy stuff 2025-06-05 02:22:45 -04:00
Jason Wen
40c456b56b simpler op enabled check 2025-06-05 02:19:26 -04:00
Jason Wen
810ffc266c cleaner 2025-06-05 02:13:53 -04:00
Jason Wen
5b68983497 Merge branch 'feature/slc' into ui/slc-ui 2025-06-05 02:03:03 -04:00
Jason Wen
36e910d500 rename 2025-06-05 02:00:34 -04:00
Jason Wen
4b5cd3eef2 define types in init 2025-06-05 01:59:53 -04:00
Jason Wen
74a2a01a0f single method 2025-06-05 01:57:25 -04:00
Jason Wen
bbe6c684d8 more 2025-06-05 01:50:15 -04:00
Jason Wen
1929a37b1f handle all params with exceptions 2025-06-05 01:49:28 -04:00
Jason Wen
06eaa56a9d policy param catch exceptions 2025-06-05 01:25:18 -04:00
Jason Wen
b0e0b46c8e Merge remote-tracking branch 'sunnypilot/sunnypilot/master-new' into feature/slc 2025-06-05 01:22:20 -04:00
Jason Wen
75b2eca5df mypy my bt 2025-06-04 23:25:15 -04:00
nayan8teen
4d9b0c77b4 hide slc_offset on none 2025-06-04 23:20:57 -04:00
nayan8teen
9b3eb4127c Merge remote-tracking branch 'origin/ui/slc-ui' into ui/slc-ui
# Conflicts:
#	selfdrive/ui/sunnypilot/qt/offroad/settings/longitudinal/slc/speed_limit_control_subpanel.cc
2025-06-04 23:13:58 -04:00
nayan8teen
cf95828e1c adjust margins & slc_offset size 2025-06-04 23:12:12 -04:00
Jason Wen
ea9df3fe10 bigger btns 2025-06-04 23:11:33 -04:00
Jason Wen
01d84dc1ab bigger btns 2025-06-04 23:03:00 -04:00
Jason Wen
c9d0accf16 unused 2025-06-04 22:56:39 -04:00
Jason Wen
094d5a6fcf more formatting 2025-06-04 22:56:15 -04:00
Jason Wen
b0e979f09e use std::atoi 2025-06-04 22:49:17 -04:00
Jason Wen
736815f736 Merge branch 'feature/slc' into ui/slc-ui 2025-06-04 22:40:41 -04:00
Jason Wen
2bed6bfe06 create directory if does not exist 2025-06-04 22:38:20 -04:00
Jason Wen
858d437f57 more 2025-06-04 22:34:26 -04:00
Jason Wen
73b8892125 formatting 2025-06-04 22:31:17 -04:00
Jason Wen
7d9e70d396 Merge remote-tracking branch 'sunnypilot/sunnypilot/master-new' into feature/slc
# Conflicts:
#	opendbc_repo
#	selfdrive/ui/sunnypilot/qt/offroad/settings/settings.cc
2025-06-04 22:21:19 -04:00
nayan8teen
2cd0e918d3 move src from settings/slc to settings/longitudinal/slc 2025-06-04 20:49:02 -04:00
nayan8teen
ee84438c41 minor layout updates
remove unnecessary imports
call showDescription on showEvent instead of refresh
2025-06-04 20:41:01 -04:00
nayan8teen
c88fe5294d update margins 2025-06-02 21:38:07 -04:00
nayan8teen
c06d7effea Merge remote-tracking branch 'origin/feature/slc' into ui/slc-ui 2025-06-02 21:17:20 -04:00
Jason Wen
5b98cdf750 none for now 2025-06-02 00:19:58 -04:00
Jason Wen
42423e35de Merge branch 'feature/slc' into ui/slc-ui 2025-06-02 00:18:38 -04:00
Jason Wen
be11d07a01 type hints 2025-06-02 00:01:19 -04:00
Jason Wen
fe521a39be fix source 2025-06-01 23:58:17 -04:00
Jason Wen
0aa6953066 fix 2025-06-01 23:52:24 -04:00
Jason Wen
4531cd7ac6 use old controller for now 2025-06-01 23:36:55 -04:00
Jason Wen
ab3a7e82e8 fix 2025-06-01 22:49:17 -04:00
Jason Wen
eaa96655ca Merge remote-tracking branch 'sunnypilot/sunnypilot/master-new' into feature/slc
# Conflicts:
#	cereal/custom.capnp
#	cereal/log.capnp
#	cereal/services.py
#	opendbc_repo
2025-06-01 22:48:51 -04:00
nayan8teen
48947609be remove extra include 2025-06-01 22:35:18 -04:00
nayan8teen
a0463f2c1a subpanel within subpanel 2025-06-01 22:28:41 -04:00
nayan8teen
b5084ab97d slc ui 2025-06-01 15:58:50 -04:00
Jason Wen
e350b1bebc bring back car state speed limit 2025-06-01 14:31:52 -04:00
Jason Wen
5ecbed3125 Merge branch 'cs-sp' into feature/slc
# Conflicts:
#	cereal/custom.capnp
#	cereal/log.capnp
#	cereal/services.py
2025-06-01 14:31:34 -04:00
Jason Wen
17e6781a71 fix tests 2025-06-01 10:52:28 -04:00
Jason Wen
72cbcd0601 Car: CarStateSP 2025-06-01 10:32:46 -04:00
Jason Wen
b6f3e5229f Merge remote-tracking branch 'sunnypilot/sunnypilot/master-new' into feature/slc 2025-05-31 23:38:20 -04:00
Jason Wen
6f7a3e7834 missed event 2025-05-31 23:37:49 -04:00
Jason Wen
3885ee068d no car state speed yet 2025-05-31 23:36:20 -04:00
Jason Wen
3d254f8ed6 need to sub 2025-05-31 23:32:39 -04:00
Jason Wen
471b24f7be no nav for now 2025-05-31 23:28:22 -04:00
Jason Wen
d9a1f6a553 publish events 2025-05-31 02:02:52 -04:00
Jason Wen
8cc9eb1562 service missing 2025-05-31 01:44:07 -04:00
Jason Wen
4a94abe2ea let's use gps chips directly for now 2025-05-31 01:34:09 -04:00
Jason Wen
ca100995b2 fix path 2025-05-30 23:38:21 -04:00
Jason Wen
f5e9a439db fix spinner import 2025-05-30 23:36:43 -04:00
Jason Wen
4c133c601d print them out 2025-05-30 23:21:43 -04:00
Jason Wen
69c68f7dc0 Merge remote-tracking branch 'sunnypilot/sunnypilot/master-new' into feature/slc
# Conflicts:
#	selfdrive/ui/sunnypilot/SConscript
2025-05-30 23:14:40 -04:00
Jason Wen
ac67871a47 ui? ui! 2025-05-30 23:09:28 -04:00
Jason Wen
f7eb39abf4 enable debug print 2025-05-30 23:00:34 -04:00
Jason Wen
ca340510bf init these 2025-05-30 22:59:52 -04:00
Jason Wen
c7d1e9f9cc some type hints 2025-05-30 22:56:27 -04:00
Jason Wen
52da6e398c start refactor controller 2025-05-30 22:51:12 -04:00
Jason Wen
26fc671251 wrong state 2025-05-30 20:18:50 -04:00
Jason Wen
b082daeb79 refactor state machine 2025-05-29 22:32:09 -04:00
Jason Wen
a389fa81c2 fixme-sp 2025-05-29 21:20:47 -04:00
Jason Wen
2d47bdf297 implement in long plan 2025-05-29 21:17:41 -04:00
Jason Wen
5e19d7d1a3 Merge remote-tracking branch 'sunnypilot/sunnypilot/master-new' into feature/slc 2025-05-29 19:29:44 -04:00
Jason Wen
440678f64f Merge remote-tracking branch 'sunnypilot/sunnypilot/master-new' into feature/slc
# Conflicts:
#	cereal/custom.capnp
#	sunnypilot/selfdrive/selfdrived/events.py
2025-05-21 00:40:54 -04:00
Jason Wen
b661fee9b1 Merge remote-tracking branch 'sunnypilot/sunnypilot/master-new' into feature/slc 2025-04-30 23:10:52 -04:00
Jason Wen
09be18cd9b Merge remote-tracking branch 'sunnypilot/sunnypilot/master-new' into feature/slc 2025-04-28 22:25:57 -04:00
Jason Wen
b136d05ea2 lint 2025-04-27 22:30:40 -04:00
Jason Wen
2ff6060fe3 sp events 2025-04-27 22:10:55 -04:00
Jason Wen
4001bb1e46 add to cereal first 2025-04-27 21:37:54 -04:00
Jason Wen
49ed7795b9 more 2025-04-27 21:31:26 -04:00
Jason Wen
27fcc2f42a fix linting 2025-04-27 21:02:45 -04:00
Jason Wen
398ded76d6 bring back cereal 2025-04-27 20:25:37 -04:00
Jason Wen
e0b73d32ad old navd helpers 2025-04-27 20:12:49 -04:00
Jason Wen
b9d0b71bc1 more 2025-04-27 20:09:44 -04:00
Jason Wen
938796d467 move 2025-04-27 20:07:18 -04:00
Jason Wen
c50b187da9 some fixes 2025-04-27 19:59:35 -04:00
Jason Wen
2dd1258670 init 2025-04-27 11:52:15 -04:00
27 changed files with 1346 additions and 21 deletions

View File

@@ -145,6 +145,8 @@ struct LongitudinalPlanSP @0xf35cc4560bbf6ec2 {
dec @0 :DynamicExperimentalControl;
longitudinalPlanSource @1 :LongitudinalPlanSource;
smartCruiseControl @2 :SmartCruiseControl;
speedLimitAssist @3 :SpeedLimitAssist;
events @4 :List(OnroadEventSP.Event);
struct DynamicExperimentalControl {
state @0 :DynamicExperimentalControlState;
@@ -180,9 +182,36 @@ struct LongitudinalPlanSP @0xf35cc4560bbf6ec2 {
}
}
struct SpeedLimitAssist {
state @0 :SpeedLimitAssistState;
enabled @1 :Bool;
active @2 :Bool;
speedLimit @3 :Float32;
speedLimitOffset @4 :Float32;
distToSpeedLimit @5 :Float32;
source @6 :SpeedLimitSource;
}
enum LongitudinalPlanSource {
cruise @0;
sccVision @1;
speedLimitAssist @2;
}
enum SpeedLimitAssistState {
disabled @0;
inactive @1; # No speed limit set or not enabled by parameter.
preActive @2;
pending @3; # Awaiting new speed limit.
adapting @4; # Reducing speed to match new speed limit.
active @5; # Cruising at speed limit.
overriding @6; # System overriding with manual control.
}
enum SpeedLimitSource {
none @0;
car @1;
map @2;
}
}
@@ -225,6 +254,10 @@ struct OnroadEventSP @0xda96579883444c35 {
pedalPressedAlertOnly @16;
laneTurnLeft @17;
laneTurnRight @18;
speedLimitPreActive @19;
speedLimitActive @20;
speedLimitConfirmed @21;
speedLimitChanged @22;
}
}
@@ -316,6 +349,7 @@ struct BackupManagerSP @0xf98d843bfd7004a3 {
}
struct CarStateSP @0xb86e6369214c01c8 {
speedLimit @0 :Float32; # m/s
}
struct LiveMapDataSP @0xf416ec09499d9d19 {

View File

@@ -225,4 +225,14 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"OsmStateTitle", {PERSISTENT, STRING}},
{"OsmWayTest", {PERSISTENT, STRING}},
{"RoadName", {CLEAR_ON_ONROAD_TRANSITION, STRING}},
// Speed Limit Control
{"SpeedLimitAssist", {PERSISTENT | BACKUP, BOOL, "0"}},
{"SpeedLimitPolicy", {PERSISTENT | BACKUP, INT, "3"}},
{"SpeedLimitEngageType", {PERSISTENT | BACKUP, INT, "0"}},
{"SpeedLimitOffsetType", {PERSISTENT | BACKUP, INT, "0"}},
{"SpeedLimitValueOffset", {PERSISTENT | BACKUP, INT, "0"}},
{"SpeedLimitWarningType", {PERSISTENT | BACKUP, INT, "0"}},
{"SpeedLimitWarningOffsetType", {PERSISTENT | BACKUP, INT, "0"}},
{"SpeedLimitWarningValueOffset", {PERSISTENT | BACKUP, INT, "0"}},
};

View File

@@ -146,7 +146,7 @@ class LongitudinalPlanner(LongitudinalPlannerSP):
clipped_accel_coast_interp = np.interp(v_ego, [MIN_ALLOW_THROTTLE_SPEED, MIN_ALLOW_THROTTLE_SPEED*2], [accel_clip[1], clipped_accel_coast])
accel_clip[1] = min(accel_clip[1], clipped_accel_coast_interp)
# Get new v_cruise and a_desired from Smart Cruise Control
# Get new v_cruise and a_desired from Smart Cruise Control and Speed Limit Assist
v_cruise, self.a_desired = LongitudinalPlannerSP.update_targets(self, sm, self.v_desired_filter.x, self.a_desired, v_cruise)
if force_slow_decel:

View File

@@ -1,5 +1,6 @@
#!/usr/bin/env python3
from cereal import car
from openpilot.common.gps import get_gps_location_service
from openpilot.common.params import Params
from openpilot.common.realtime import Priority, config_realtime_process
from openpilot.common.swaglog import cloudlog
@@ -16,10 +17,13 @@ def main():
CP = messaging.log_from_bytes(params.get("CarParams", block=True), car.CarParams)
cloudlog.info("plannerd got CarParams: %s", CP.brand)
gps_location_service = get_gps_location_service(params)
ldw = LaneDepartureWarning()
longitudinal_planner = LongitudinalPlanner(CP)
pm = messaging.PubMaster(['longitudinalPlan', 'driverAssistance', 'longitudinalPlanSP'])
sm = messaging.SubMaster(['carControl', 'carState', 'controlsState', 'liveParameters', 'radarState', 'modelV2', 'selfdriveState'],
sm = messaging.SubMaster(['carControl', 'carState', 'controlsState', 'liveParameters', 'radarState', 'modelV2', 'selfdriveState',
'liveMapDataSP', 'carStateSP', gps_location_service],
poll='modelV2')
while True:

View File

@@ -95,9 +95,8 @@ class SelfdriveD(CruiseHelper):
self.sm = messaging.SubMaster(['deviceState', 'pandaStates', 'peripheralState', 'modelV2', 'liveCalibration',
'carOutput', 'driverMonitoringState', 'longitudinalPlan', 'livePose', 'liveDelay',
'managerState', 'liveParameters', 'radarState', 'liveTorqueParameters',
'controlsState', 'carControl', 'driverAssistance', 'alertDebug', 'userBookmark', 'audioFeedback',
'modelDataV2SP'] + \
self.camera_packets + self.sensor_packets + self.gps_packets,
'controlsState', 'carControl', 'driverAssistance', 'alertDebug', 'userBookmark', 'audioFeedback'] + \
self.camera_packets + self.sensor_packets + self.gps_packets + ['modelDataV2SP', 'longitudinalPlanSP'],
ignore_alive=ignore, ignore_avg_freq=ignore,
ignore_valid=ignore, frequency=int(1/DT_CTRL))
@@ -205,6 +204,7 @@ class SelfdriveD(CruiseHelper):
if not self.CP.notCar:
self.events.add_from_msg(self.sm['driverMonitoringState'].events)
self.events_sp.add_from_msg(self.sm['longitudinalPlanSP'].events)
# Add car events, ignore if CAN isn't valid
if CS.canValid:

View File

@@ -67,6 +67,9 @@ class Plant:
lp = messaging.new_message('liveParameters')
car_control = messaging.new_message('carControl')
model = messaging.new_message('modelV2')
car_state_sp = messaging.new_message('carStateSP')
live_map_data_sp = messaging.new_message('liveMapDataSP')
gps_data = messaging.new_message('gpsLocation')
a_lead = (v_lead - self.v_lead_prev)/self.ts
self.v_lead_prev = v_lead
@@ -133,7 +136,10 @@ class Plant:
'controlsState': control.controlsState,
'selfdriveState': ss.selfdriveState,
'liveParameters': lp.liveParameters,
'modelV2': model.modelV2}
'modelV2': model.modelV2,
'carStateSP': car_state_sp.carStateSP,
'liveMapDataSP': live_map_data_sp.liveMapDataSP,
'gpsLocation': gps_data.gpsLocation}
self.planner.update(sm)
self.acceleration = self.planner.output_a_target
self.speed = self.speed + self.acceleration * self.ts

View File

@@ -54,6 +54,8 @@ lateral_panel_qt_src = [
longitudinal_panel_qt_src = [
"sunnypilot/qt/offroad/settings/longitudinal/custom_acc_increment.cc",
"sunnypilot/qt/offroad/settings/longitudinal/speed_limit/speed_limit_policy.cc",
"sunnypilot/qt/offroad/settings/longitudinal/speed_limit/speed_limit_settings.cc",
]
network_src = [

View File

@@ -0,0 +1,49 @@
/*
* 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
enum class SpeedLimitMode {
OFF,
INFORMATION,
WARNING,
ASSIST,
};
inline const char *SpeedLimitModeTexts[]{
QT_TR_NOOP("Off"),
QT_TR_NOOP("Information"),
QT_TR_NOOP("Warning"),
QT_TR_NOOP("Assist"),
};
enum class SpeedLimitOffsetType {
NONE,
FIXED,
PERCENT,
};
inline const char *SpeedLimitOffsetTypeTexts[]{
QT_TR_NOOP("None"),
QT_TR_NOOP("Fixed"),
QT_TR_NOOP("Percent"),
};
enum class SpeedLimitSourcePolicy {
CAR_ONLY,
MAP_ONLY,
CAR_FIRST,
MAP_FIRST,
COMBINED
};
inline const char *SpeedLimitSourcePolicyTexts[]{
QT_TR_NOOP("Car\nOnly"),
QT_TR_NOOP("Map\nOnly"),
QT_TR_NOOP("Car\nFirst"),
QT_TR_NOOP("Map\nFirst"),
QT_TR_NOOP("Combined\nData")
};

View File

@@ -0,0 +1,53 @@
/*
* 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/speed_limit/speed_limit_policy.h"
SpeedLimitPolicy::SpeedLimitPolicy(QWidget *parent) : QWidget(parent) {
QVBoxLayout *main_layout = new QVBoxLayout(this);
main_layout->setContentsMargins(0, 0, 0, 0);
main_layout->setSpacing(0);
// Back button
PanelBackButton *back = new PanelBackButton(tr("Back"));
connect(back, &QPushButton::clicked, [=]() { emit backPress(); });
main_layout->addWidget(back, 0, Qt::AlignLeft);
main_layout->addSpacing(10);
ListWidgetSP *list = new ListWidgetSP(this, true);
std::vector<QString> speed_limit_policy_texts{
SpeedLimitSourcePolicyTexts[static_cast<int>(SpeedLimitSourcePolicy::CAR_ONLY)],
SpeedLimitSourcePolicyTexts[static_cast<int>(SpeedLimitSourcePolicy::MAP_ONLY)],
SpeedLimitSourcePolicyTexts[static_cast<int>(SpeedLimitSourcePolicy::CAR_FIRST)],
SpeedLimitSourcePolicyTexts[static_cast<int>(SpeedLimitSourcePolicy::MAP_FIRST)],
SpeedLimitSourcePolicyTexts[static_cast<int>(SpeedLimitSourcePolicy::COMBINED)]
};
speed_limit_policy = new ButtonParamControlSP(
"SpeedLimitPolicy",
tr("Speed Limit Source"),
"",
"",
speed_limit_policy_texts,
250);
list->addItem(speed_limit_policy);
connect(speed_limit_policy, &ButtonParamControlSP::buttonClicked, this, &SpeedLimitPolicy::refresh);
refresh();
main_layout->addWidget(list);
};
void SpeedLimitPolicy::refresh() {
SpeedLimitSourcePolicy policy_param = static_cast<SpeedLimitSourcePolicy>(std::atoi(params.get("SpeedLimitPolicy").c_str()));
speed_limit_policy->setDescription(sourceDescription(policy_param));
}
void SpeedLimitPolicy::showEvent(QShowEvent *event) {
refresh();
speed_limit_policy->showDescription();
}

View File

@@ -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.
*/
#pragma once
#include "selfdrive/ui/sunnypilot/ui.h"
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h"
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/longitudinal/speed_limit/helpers.h"
#include "selfdrive/ui/sunnypilot/qt/widgets/controls.h"
class SpeedLimitPolicy : public QWidget {
Q_OBJECT
public:
explicit SpeedLimitPolicy(QWidget *parent = nullptr);
void refresh();
void showEvent(QShowEvent *event) override;
signals:
signals:
void backPress();
private:
Params params;
ButtonParamControlSP *speed_limit_policy;
static QString sourceDescription(SpeedLimitSourcePolicy type = SpeedLimitSourcePolicy::CAR_ONLY) {
QString car_only = tr("⦿ Car Only: Use Speed Limit data only from Car");
QString map_only = tr("⦿ Map Only: Use Speed Limit data only from OpenStreetMaps");
QString car_first = tr("⦿ Car First: Use Speed Limit data from Car if available, else use from OpenStreetMaps");
QString map_first = tr("⦿ Map First: Use Speed Limit data from OpenStreetMaps if available, else use from Car");
QString combined = tr("⦿ Combined: Use combined Speed Limit data from Car & OpenStreetMaps");
if (type == SpeedLimitSourcePolicy::CAR_ONLY) {
car_only = "<font color='white'><b>" + car_only + "</b></font>";
} else if (type == SpeedLimitSourcePolicy::MAP_ONLY) {
map_only = "<font color='white'><b>" + map_only + "</b></font>";
} else if (type == SpeedLimitSourcePolicy::CAR_FIRST) {
car_first = "<font color='white'><b>" + car_first + "</b></font>";
} else if (type == SpeedLimitSourcePolicy::MAP_FIRST) {
map_first = "<font color='white'><b>" + map_first + "</b></font>";
} else {
combined = "<font color='white'><b>" + combined + "</b></font>";
}
return QString("%1<br>%2<br>%3<br>%4<br>%5")
.arg(car_only)
.arg(map_only)
.arg(car_first)
.arg(map_first)
.arg(combined);
}
};

View File

@@ -0,0 +1,138 @@
/*
* 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/speed_limit/speed_limit_settings.h"
SpeedLimitSettings::SpeedLimitSettings(QWidget *parent) : QStackedWidget(parent) {
subPanelFrame = new QFrame();
QVBoxLayout *subPanelLayout = new QVBoxLayout(subPanelFrame);
subPanelLayout->setContentsMargins(0, 0, 0, 0);
subPanelLayout->setSpacing(0);
// Back button
PanelBackButton *back = new PanelBackButton(tr("Back"));
connect(back, &QPushButton::clicked, [=]() { emit backPress(); });
subPanelLayout->addWidget(back, 0, Qt::AlignLeft);
subPanelLayout->addSpacing(20);
ListWidgetSP *list = new ListWidgetSP(this, true);
// Speed Limit Assist
// TODO-SP: OptionControlSP with Speed Limit Information/Warning as a single option
speedLimitAssistToggle = new ParamControlSP(
"SpeedLimitAssist",
tr("Speed Limit Assist (SLA)"),
tr("When you engage ACC, you will be prompted to set the cruising speed to the speed limit of the road adjusted by the Offset and Source Policy specified, or the current driving speed. "
"The maximum cruising speed will always be the MAX set speed."),
"",
this);
list->addItem(speedLimitAssistToggle);
// TODO-SP: Combine Assist/Warning/Information
// std::vector<QString> speed_limit_texts{
// SpeedLimitModeTexts[static_cast<int>(SpeedLimitMode::OFF)],
// SpeedLimitModeTexts[static_cast<int>(SpeedLimitMode::INFORMATION)],
// SpeedLimitModeTexts[static_cast<int>(SpeedLimitMode::WARNING)],
// SpeedLimitModeTexts[static_cast<int>(SpeedLimitMode::ASSIST)],
// };
// speed_limit_mode_settings = new ButtonParamControlSP(
// "SpeedLimitMode",
// tr("Speed Limit Mode"),
// tr("When you engage ACC, you will be prompted to set the cruising speed to the speed limit of the road adjusted by the Offset and Source Policy specified, or the current driving speed. "
// "The maximum cruising speed will always be the MAX set speed."),
// "",
// speed_limit_texts,
// 250);
// list->addItem(speed_limit_mode_settings);
auto *speedLimitBtnFrame = new QFrame(this);
auto *speedLimitBtnFrameLayout = new QGridLayout();
speedLimitBtnFrame->setLayout(speedLimitBtnFrameLayout);
speedLimitBtnFrameLayout->setContentsMargins(0, 40, 0, 40);
speedLimitBtnFrameLayout->setSpacing(0);
speedLimitPolicyScreen = new SpeedLimitPolicy(this);
speedLimitSource = new PushButtonSP(tr("Customize Source"));
connect(speedLimitSource, &QPushButton::clicked, [&]() {
setCurrentWidget(speedLimitPolicyScreen);
speedLimitPolicyScreen->refresh();
});
connect(speedLimitPolicyScreen, &SpeedLimitPolicy::backPress, [&]() {
setCurrentWidget(subPanelFrame);
showEvent(new QShowEvent());
});
speedLimitSource->setFixedWidth(720);
speedLimitBtnFrameLayout->addWidget(speedLimitSource, 0, 0, Qt::AlignLeft);
list->addItem(speedLimitBtnFrame);
QFrame *offsetFrame = new QFrame(this);
QVBoxLayout *offsetLayout = new QVBoxLayout(offsetFrame);
std::vector<QString> speed_limit_offset_texts{
SpeedLimitOffsetTypeTexts[static_cast<int>(SpeedLimitOffsetType::NONE)],
SpeedLimitOffsetTypeTexts[static_cast<int>(SpeedLimitOffsetType::FIXED)],
SpeedLimitOffsetTypeTexts[static_cast<int>(SpeedLimitOffsetType::PERCENT)]
};
speed_limit_offset_settings = new ButtonParamControlSP(
"SpeedLimitOffsetType",
tr("Speed Limit Offset"),
"",
"",
speed_limit_offset_texts,
500);
offsetLayout->addWidget(speed_limit_offset_settings);
speed_limit_offset = new OptionControlSP(
"SpeedLimitValueOffset",
"",
"",
"",
{-30, 30}
);
offsetLayout->addWidget(speed_limit_offset);
list->addItem(offsetFrame);
// connect(speed_limit_mode_settings, &ButtonParamControlSP::buttonClicked, this, &SpeedLimitSettings::refresh);
connect(speed_limit_offset, &OptionControlSP::updateLabels, this, &SpeedLimitSettings::refresh);
connect(speed_limit_offset_settings, &ButtonParamControlSP::showDescriptionEvent, speed_limit_offset, &OptionControlSP::showDescription);
connect(speed_limit_offset_settings, &ButtonParamControlSP::buttonClicked, this, &SpeedLimitSettings::refresh);
refresh();
subPanelLayout->addWidget(list);
addWidget(subPanelFrame);
addWidget(speedLimitPolicyScreen);
setCurrentWidget(subPanelFrame);
};
void SpeedLimitSettings::refresh() {
SpeedLimitOffsetType offset_type_param = static_cast<SpeedLimitOffsetType>(std::atoi(params.get("SpeedLimitOffsetType").c_str()));
QString offsetLabel = QString::fromStdString(params.get("SpeedLimitValueOffset"));
speed_limit_offset->setDescription(offsetDescription(offset_type_param));
if (offset_type_param == SpeedLimitOffsetType::PERCENT) {
offsetLabel += "%";
}
if (offset_type_param == SpeedLimitOffsetType::NONE) {
speed_limit_offset->setVisible(false);
} else {
speed_limit_offset->setVisible(true);
speed_limit_offset->setLabel(offsetLabel);
speed_limit_offset->showDescription();
}
}
void SpeedLimitSettings::showEvent(QShowEvent *event) {
refresh();
speed_limit_offset->showDescription();
}

View File

@@ -0,0 +1,55 @@
/*
* 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/offroad/settings/longitudinal/speed_limit/helpers.h"
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/longitudinal/speed_limit/speed_limit_policy.h"
#include "selfdrive/ui/sunnypilot/qt/widgets/controls.h"
class SpeedLimitSettings : public QStackedWidget {
Q_OBJECT
public:
SpeedLimitSettings(QWidget *parent = nullptr);
void refresh();
void showEvent(QShowEvent *event) override;
signals:
void backPress();
private:
Params params;
QFrame *subPanelFrame;
ParamControlSP *speedLimitAssistToggle;
// ButtonParamControlSP *speed_limit_mode_settings;
PushButtonSP *speedLimitSource;
ButtonParamControlSP *speed_limit_offset_settings;
OptionControlSP *speed_limit_offset;
SpeedLimitPolicy *speedLimitPolicyScreen;
static QString offsetDescription(SpeedLimitOffsetType type = SpeedLimitOffsetType::NONE) {
QString none_str = tr("⦿ None: No Offset");
QString fixed_str = tr("⦿ Fixed: Adds a fixed offset [Speed Limit + Offset]");
QString percent_str = tr("⦿ Percent: Adds a percent offset [Speed Limit + (Offset % Speed Limit)]");
if (type == SpeedLimitOffsetType::FIXED) {
fixed_str = "<font color='white'><b>" + fixed_str + "</b></font>";
} else if (type == SpeedLimitOffsetType::PERCENT) {
percent_str = "<font color='white'><b>" + percent_str + "</b></font>";
} else {
none_str = "<font color='white'><b>" + none_str + "</b></font>";
}
return QString("%1<br>%2<br>%3")
.arg(none_str)
.arg(fixed_str)
.arg(percent_str);
}
};

View File

@@ -8,6 +8,21 @@
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/longitudinal_panel.h"
LongitudinalPanel::LongitudinalPanel(QWidget *parent) : QWidget(parent) {
setStyleSheet(R"(
#back_btn {
font-size: 50px;
margin: 0px;
padding: 15px;
border-width: 0;
border-radius: 30px;
color: #dddddd;
background-color: #393939;
}
#back_btn:pressed {
background-color: #4a4a4a;
}
)");
main_layout = new QStackedLayout(this);
ListWidget *list = new ListWidget(this, false);
@@ -40,7 +55,21 @@ LongitudinalPanel::LongitudinalPanel(QWidget *parent) : QWidget(parent) {
QObject::connect(uiState(), &UIState::offroadTransition, this, &LongitudinalPanel::refresh);
speedLimitSettings = new PushButtonSP(tr("Speed Limit"), 750, this);
connect(speedLimitSettings, &QPushButton::clicked, [&]() {
cruisePanelScroller->setLastScrollPosition();
main_layout->setCurrentWidget(speedLimitScreen);
});
list->addItem(speedLimitSettings);
speedLimitScreen = new SpeedLimitSettings(this);
connect(speedLimitScreen, &SpeedLimitSettings::backPress, [=]() {
cruisePanelScroller->restoreScrollPosition();
main_layout->setCurrentWidget(cruisePanelScreen);
});
main_layout->addWidget(cruisePanelScreen);
main_layout->addWidget(speedLimitScreen);
main_layout->setCurrentWidget(cruisePanelScreen);
refresh(offroad);
}

View File

@@ -8,8 +8,10 @@
#pragma once
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/longitudinal/custom_acc_increment.h"
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/longitudinal/speed_limit/speed_limit_settings.h"
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/settings.h"
#include "selfdrive/ui/sunnypilot/qt/widgets/scrollview.h"
#include "selfdrive/ui/sunnypilot/ui.h"
class LongitudinalPanel : public QWidget {
Q_OBJECT
@@ -32,4 +34,6 @@ private:
CustomAccIncrement *customAccIncrement = nullptr;
ParamControl *SmartCruiseControlVision;
ParamControl *intelligentCruiseButtonManagement = nullptr;
SpeedLimitSettings *speedLimitScreen;
PushButtonSP *speedLimitSettings;
};

View File

@@ -18,6 +18,7 @@ ExpandableToggleRow::ExpandableToggleRow(const QString &param, const QString &ti
collapsibleWidget->setVisible(false);
QVBoxLayout *collapsible_layout = new QVBoxLayout();
collapsibleWidget->setLayout(collapsible_layout);
collapsible_layout->setContentsMargins(0, 20, 0, 20);
list = new ListWidgetSP(this, false);

View File

@@ -37,15 +37,14 @@ def live_map_data_sp_thread():
def live_map_data_sp_thread_debug(gps_location_service):
_sub_master = messaging.SubMaster(['carState', 'livePose', 'liveMapDataSP', 'longitudinalPlanSP', gps_location_service])
_sub_master = messaging.SubMaster(['carState', 'livePose', 'liveMapDataSP', 'longitudinalPlanSP', 'carStateSP', gps_location_service])
_sub_master.update()
v_ego = _sub_master['carState'].vEgo
long_spl = _sub_master['longitudinalPlanSP'].speedLimit
_policy = Policy.car_state_priority
_resolver = SpeedLimitResolver(_policy)
_speed_limit, _distance, _source = _resolver.resolve(v_ego, long_spl, _sub_master)
print(_speed_limit, _distance, _source, " <-> ", long_spl)
_resolver = SpeedLimitResolver()
_resolver.policy = Policy.car_state_priority
_resolver.update(v_ego, _sub_master)
print(_resolver.speed_limit, _resolver.distance, _resolver.source)
def main():

View File

@@ -9,6 +9,9 @@ from cereal import messaging, custom
from opendbc.car import structs
from openpilot.sunnypilot.selfdrive.controls.lib.dec.dec import DynamicExperimentalController
from openpilot.sunnypilot.selfdrive.controls.lib.smart_cruise_control.smart_cruise_control import SmartCruiseControl
from openpilot.sunnypilot.selfdrive.controls.lib.speed_limit_assist.speed_limit_assist import SpeedLimitAssist
from openpilot.sunnypilot.selfdrive.controls.lib.speed_limit_assist.speed_limit_resolver import SpeedLimitResolver
from openpilot.sunnypilot.selfdrive.selfdrived.events import EventsSP
from openpilot.sunnypilot.models.helpers import get_active_bundle
DecState = custom.LongitudinalPlanSP.DynamicExperimentalControl.DynamicExperimentalControlState
@@ -17,8 +20,13 @@ Source = custom.LongitudinalPlanSP.LongitudinalPlanSource
class LongitudinalPlannerSP:
def __init__(self, CP: structs.CarParams, mpc):
self.events_sp = EventsSP()
self.resolver = SpeedLimitResolver()
self.dec = DynamicExperimentalController(CP, mpc)
self.scc = SmartCruiseControl()
self.sla = SpeedLimitAssist(CP)
self.generation = int(model_bundle.generation) if (model_bundle := get_active_bundle()) else None
self.source = Source.cruise
@@ -34,11 +42,22 @@ class LongitudinalPlannerSP:
return self.dec.mode()
def update_targets(self, sm: messaging.SubMaster, v_ego: float, a_ego: float, v_cruise: float) -> tuple[float, float]:
self.scc.update(sm, v_ego, a_ego, v_cruise)
long_enabled = sm['carControl'].enabled
long_override = sm['carControl'].cruiseControl.override
self.events_sp.clear()
self.scc.update(sm, long_enabled, long_override, v_ego, a_ego, v_cruise)
# Speed Limit Assist
self.resolver.update(v_ego, sm)
v_cruise_sla = self.sla.update(long_enabled, long_override, v_ego, a_ego, sm['carState'].vCruiseCluster,
self.resolver.speed_limit, self.resolver.distance, self.resolver.source, self.events_sp)
targets = {
Source.cruise: (v_cruise, a_ego),
Source.sccVision: (self.scc.vision.output_v_target, self.scc.vision.output_a_target)
Source.sccVision: (self.scc.vision.output_v_target, self.scc.vision.output_a_target),
Source.speedLimitAssist: (v_cruise_sla, a_ego),
}
self.source = min(targets, key=lambda k: targets[k][0])
@@ -56,6 +75,7 @@ class LongitudinalPlannerSP:
longitudinalPlanSP = plan_sp_send.longitudinalPlanSP
longitudinalPlanSP.longitudinalPlanSource = self.source
longitudinalPlanSP.events = self.events_sp.to_msg()
# Dynamic Experimental Control
dec = longitudinalPlanSP.dec
@@ -75,4 +95,14 @@ class LongitudinalPlannerSP:
sccVision.enabled = self.scc.vision.is_enabled
sccVision.active = self.scc.vision.is_active
# Speed Limit Assist
speedLimitAssist = longitudinalPlanSP.speedLimitAssist
speedLimitAssist.state = self.sla.state
speedLimitAssist.enabled = self.sla.is_enabled
speedLimitAssist.active = self.sla.is_active
speedLimitAssist.speedLimit = float(self.resolver.speed_limit)
speedLimitAssist.speedLimitOffset = float(self.sla.speed_limit_offset)
speedLimitAssist.distToSpeedLimit = float(self.resolver.distance)
speedLimitAssist.source = self.resolver.source
pm.send('longitudinalPlanSP', plan_sp_send)

View File

@@ -12,8 +12,5 @@ class SmartCruiseControl:
def __init__(self):
self.vision = SmartCruiseControlVision()
def update(self, sm: messaging.SubMaster, v_ego: float, a_ego: float, v_cruise: float) -> None:
long_enabled = sm['carControl'].enabled
long_override = sm['carControl'].cruiseControl.override
def update(self, sm: messaging.SubMaster, long_enabled: bool, long_override: bool, v_ego: float, a_ego: float, v_cruise: float) -> None:
self.vision.update(sm, long_enabled, long_override, v_ego, a_ego, v_cruise)

View File

@@ -102,7 +102,7 @@ class SmartCruiseControlVision:
self.v_target = (_A_LAT_REG_MAX / max_curve) ** 0.5
def _update_state_machine(self) -> tuple[bool, bool]:
# ENABLED, ENTERING, TURNING, LEAVING
# ENABLED, ENTERING, TURNING, LEAVING, OVERRIDING
if self.state != VisionState.disabled:
# longitudinal and feature disable always have priority in a non-disabled state
if not self.long_enabled or not self.enabled:
@@ -163,7 +163,7 @@ class SmartCruiseControlVision:
return enabled, active
def _update_solution(self) -> float:
# DISABLED, ENABLED
# DISABLED, ENABLED, OVERRIDING
if self.state not in ACTIVE_STATES:
# when not overshooting, calculate v_turn as the speed at the prediction horizon when following
# the smooth deceleration.

View File

@@ -0,0 +1,20 @@
from cereal import custom
SpeedLimitAssistState = custom.LongitudinalPlanSP.SpeedLimitAssistState
PARAMS_UPDATE_PERIOD = 3. # secs. Time between parameter updates.
DISABLED_GUARD_PERIOD = 2 # secs.
PRE_ACTIVE_GUARD_PERIOD = 5 # secs. Time to wait after activation before considering temp deactivation signal.
# Constants for Limit controllers.
LIMIT_ADAPT_ACC = -1. # m/s^2 Ideal acceleration for the adapting (braking) phase when approaching speed limits.
LIMIT_MIN_ACC = -1.5 # m/s^2 Maximum deceleration allowed for limit controllers to provide.
LIMIT_MAX_ACC = 1.0 # m/s^2 Maximum acceleration allowed for limit controllers to provide while active.
LIMIT_MIN_SPEED = 8.33 # m/s, Minimum speed limit to provide as solution on limit controllers.
LIMIT_SPEED_OFFSET_TH = -1. # m/s Maximum offset between speed limit and current speed for adapting state.
LIMIT_MAX_MAP_DATA_AGE = 10. # s Maximum time to hold to map data, then consider it invalid inside limits controllers.
# Speed Limit Assist Auto mode constants
REQUIRED_INITIAL_MAX_SET_SPEED = 35.7632 # m/s 80 MPH # TODO-SP: customizable with params
CRUISE_SPEED_TOLERANCE = 0.44704 # m/s ±1 MPH tolerance # TODO-SP: metric vs imperial
FALLBACK_CRUISE_SPEED = 255.0 # m/s fallback when no speed limit available

View File

@@ -0,0 +1,15 @@
from enum import IntEnum
class Policy(IntEnum):
map_data_only = 0
car_state_only = 1
map_data_priority = 2
car_state_priority = 3
combined = 4
class OffsetType(IntEnum):
off = 0
fixed = 1
percentage = 2

View File

@@ -0,0 +1,267 @@
"""
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 numpy as np
from cereal import custom
from openpilot.common.constants import CV
from openpilot.common.params import Params
from openpilot.common.realtime import DT_MDL
from openpilot.selfdrive.car.cruise import V_CRUISE_UNSET
from openpilot.sunnypilot.selfdrive.controls.lib.speed_limit_assist import PARAMS_UPDATE_PERIOD, LIMIT_SPEED_OFFSET_TH, \
SpeedLimitAssistState, PRE_ACTIVE_GUARD_PERIOD, REQUIRED_INITIAL_MAX_SET_SPEED, CRUISE_SPEED_TOLERANCE, DISABLED_GUARD_PERIOD
from openpilot.selfdrive.controls.lib.drive_helpers import CONTROL_N
from openpilot.sunnypilot.selfdrive.controls.lib.speed_limit_assist.common import OffsetType
from openpilot.sunnypilot.selfdrive.selfdrived.events import EventsSP
from openpilot.selfdrive.modeld.constants import ModelConstants
EventNameSP = custom.OnroadEventSP.EventName
SpeedLimitSource = custom.LongitudinalPlanSP.SpeedLimitSource
ACTIVE_STATES = (SpeedLimitAssistState.active, SpeedLimitAssistState.adapting)
ENABLED_STATES = (SpeedLimitAssistState.preActive, SpeedLimitAssistState.pending, SpeedLimitAssistState.overriding, *ACTIVE_STATES)
class SpeedLimitAssist:
_speed_limit: float
_distance: float
_source: custom.LongitudinalPlanSP.SpeedLimitSource
v_ego: float
a_ego: float
v_offset: float
last_valid_speed_limit_final: float
def __init__(self, CP):
self.params = Params()
self.CP = CP
self.frame = -1
self.long_engaged_timer = 0
self.pre_active_timer = 0
self.is_metric = self.params.get_bool("IsMetric")
self.enabled = self.params.get_bool("SpeedLimitAssist")
self.long_enabled = False
self.long_enabled_prev = False
self.long_override = False
self.is_enabled = False
self.is_active = False
self.v_ego = 0.
self.a_ego = 0.
self.v_offset = 0.
self.v_cruise_setpoint = 0.
self.v_cruise_setpoint_prev = 0.
self.initial_max_set = False
self._speed_limit = 0.
self.speed_limit_prev = 0.
self.last_valid_speed_limit_final = 0.
self._distance = 0.
self._source = SpeedLimitSource.none
self.state = SpeedLimitAssistState.disabled
self._state_prev = SpeedLimitAssistState.disabled
self.pcm_cruise_op_long = CP.openpilotLongitudinalControl and CP.pcmCruise
self.offset_type = OffsetType(self.params.get("SpeedLimitOffsetType", return_default=True))
self.offset_value = self.params.get("SpeedLimitValueOffset", return_default=True)
# Solution functions mapped to respective states
self.acceleration_solutions = {
SpeedLimitAssistState.disabled: self.get_current_acceleration_as_target,
SpeedLimitAssistState.inactive: self.get_current_acceleration_as_target,
SpeedLimitAssistState.preActive: self.get_current_acceleration_as_target,
SpeedLimitAssistState.pending: self.get_current_acceleration_as_target,
SpeedLimitAssistState.adapting: self.get_adapting_state_target_acceleration,
SpeedLimitAssistState.active: self.get_active_state_target_acceleration,
}
@property
def speed_limit_final(self) -> float:
return self._speed_limit + self.speed_limit_offset
@property
def speed_limit_changed(self) -> bool:
return bool(self._speed_limit != self.speed_limit_prev)
@property
def speed_limit_offset(self) -> float:
return self.get_offset(self.offset_type, self.offset_value)
@property
def v_cruise_setpoint_changed(self) -> bool:
return bool(self.v_cruise_setpoint != self.v_cruise_setpoint_prev)
def get_v_target_from_control(self) -> float:
if self.is_enabled:
# If we have a current valid speed limit, use it
if self._speed_limit > 0:
self.last_valid_speed_limit_final = self.speed_limit_final
return self.speed_limit_final
# If no current speed limit but we have a last valid one, use that
if self.last_valid_speed_limit_final > 0:
return self.last_valid_speed_limit_final
# Fallback
return V_CRUISE_UNSET
def get_offset(self, offset_type: OffsetType, offset_value: int) -> float:
if offset_type == OffsetType.off:
return 0
elif offset_type == OffsetType.fixed:
return offset_value * (CV.KPH_TO_MS if self.is_metric else CV.MPH_TO_MS)
elif offset_type == OffsetType.percentage:
return offset_value * 0.01 * self._speed_limit
else:
raise NotImplementedError("Offset not supported")
def update_params(self) -> None:
if self.frame % int(PARAMS_UPDATE_PERIOD / DT_MDL) == 0:
self.enabled = self.params.get_bool("SpeedLimitAssist")
self.offset_type = OffsetType(self.params.get("SpeedLimitOffsetType", return_default=True))
self.offset_value = self.params.get("SpeedLimitValueOffset", return_default=True)
self.is_metric = self.params.get_bool("IsMetric")
def initial_max_set_confirmed(self) -> bool:
return bool(abs(self.v_cruise_setpoint - REQUIRED_INITIAL_MAX_SET_SPEED) <= CRUISE_SPEED_TOLERANCE)
def detect_manual_cruise_change(self) -> bool:
# If cruise speed changed and it's not what SLA would set
if self.v_cruise_setpoint_changed:
expected_cruise = self.speed_limit_final
return bool(abs(self.v_cruise_setpoint - expected_cruise) > CRUISE_SPEED_TOLERANCE)
return False
def update_calculations(self, v_cruise_setpoint: float) -> None:
self.v_cruise_setpoint = v_cruise_setpoint if not np.isnan(v_cruise_setpoint) else 0.0
# Update current velocity offset (error)
self.v_offset = self.speed_limit_final - self.v_ego
def get_current_acceleration_as_target(self) -> float:
return self.a_ego
def get_adapting_state_target_acceleration(self) -> float:
if self._distance > 0:
return (self.speed_limit_final ** 2 - self.v_ego ** 2) / (2. * self._distance)
return self.v_offset / float(ModelConstants.T_IDXS[CONTROL_N])
def get_active_state_target_acceleration(self) -> float:
return self.v_offset / float(ModelConstants.T_IDXS[CONTROL_N])
def update_state_machine(self):
self._state_prev = self.state
self.long_engaged_timer = max(0, self.long_engaged_timer - 1)
self.pre_active_timer = max(0, self.pre_active_timer - 1)
# ACTIVE, ADAPTING, PENDING, PRE_ACTIVE, INACTIVE, OVERRIDING
if self.state != SpeedLimitAssistState.disabled:
if not self.long_enabled or not self.enabled:
self.state = SpeedLimitAssistState.disabled
self.initial_max_set = False
elif self.long_override:
self.state = SpeedLimitAssistState.overriding
else:
# ACTIVE
if self.state == SpeedLimitAssistState.active:
if self.detect_manual_cruise_change():
self.state = SpeedLimitAssistState.inactive
elif self._speed_limit > 0 and self.v_offset < LIMIT_SPEED_OFFSET_TH:
self.state = SpeedLimitAssistState.adapting
# ADAPTING
elif self.state == SpeedLimitAssistState.adapting:
if self.detect_manual_cruise_change():
self.state = SpeedLimitAssistState.inactive
elif self.v_offset >= LIMIT_SPEED_OFFSET_TH:
self.state = SpeedLimitAssistState.active
# PENDING
elif self.state == SpeedLimitAssistState.pending:
if self._speed_limit > 0:
if self.v_offset < LIMIT_SPEED_OFFSET_TH:
self.state = SpeedLimitAssistState.adapting
else:
self.state = SpeedLimitAssistState.active
# PRE_ACTIVE
elif self.state == SpeedLimitAssistState.preActive:
if self.initial_max_set_confirmed():
self.initial_max_set = True
if self._speed_limit > 0:
if self.v_offset < LIMIT_SPEED_OFFSET_TH:
self.state = SpeedLimitAssistState.adapting
else:
self.state = SpeedLimitAssistState.active
else:
self.state = SpeedLimitAssistState.pending
elif self.pre_active_timer <= PRE_ACTIVE_GUARD_PERIOD:
# Timeout - session ended
self.state = SpeedLimitAssistState.inactive
# OVERRIDING
elif self.state == SpeedLimitAssistState.overriding:
if not self.long_override:
self.state = SpeedLimitAssistState.preActive
# INACTIVE
elif self.state == SpeedLimitAssistState.inactive:
pass
# DISABLED
elif self.state == SpeedLimitAssistState.disabled:
if self.long_enabled and self.enabled:
if self.long_override:
self.state = SpeedLimitAssistState.overriding
elif not self.long_enabled_prev:
self.pre_active_timer = int(DISABLED_GUARD_PERIOD / DT_MDL)
elif self.pre_active_timer <= 0:
self.state = SpeedLimitAssistState.preActive
self.pre_active_timer = int(PRE_ACTIVE_GUARD_PERIOD / DT_MDL)
self.initial_max_set = False
enabled = self.state in ENABLED_STATES
active = self.state in ACTIVE_STATES
return enabled, active
def update_events(self, events_sp: EventsSP) -> None:
if self.state == SpeedLimitAssistState.preActive and self._state_prev != SpeedLimitAssistState.preActive:
events_sp.add(EventNameSP.speedLimitPreActive)
elif self.is_active:
if self._state_prev not in ACTIVE_STATES:
events_sp.add(EventNameSP.speedLimitActive)
elif self.speed_limit_changed:
events_sp.add(EventNameSP.speedLimitChanged)
def update(self, long_enabled: bool, long_override: bool, v_ego: float, a_ego: float, v_cruise_setpoint: float,
speed_limit: float, distance: float, source: custom.LongitudinalPlanSP.SpeedLimitSource, events_sp: EventsSP) -> float:
self.long_enabled = long_enabled
self.long_override = long_override
self.v_ego = v_ego
self.a_ego = a_ego
self._speed_limit = speed_limit
self._distance = distance
self._source = source
self.update_params()
self.update_calculations(v_cruise_setpoint)
self.is_enabled, self.is_active = self.update_state_machine()
self.update_events(events_sp)
# Update change tracking variables
self.speed_limit_prev = self._speed_limit
self.v_cruise_setpoint_prev = self.v_cruise_setpoint
self.long_enabled_prev = self.long_enabled
self.frame += 1
v_target = self.get_v_target_from_control()
return v_target

View File

@@ -0,0 +1,133 @@
import time
import numpy as np
import cereal.messaging as messaging
from cereal import custom
from openpilot.common.gps import get_gps_location_service
from openpilot.common.params import Params
from openpilot.common.realtime import DT_MDL
from openpilot.sunnypilot.selfdrive.controls.lib.speed_limit_assist import LIMIT_MAX_MAP_DATA_AGE, LIMIT_ADAPT_ACC, PARAMS_UPDATE_PERIOD
from openpilot.sunnypilot.selfdrive.controls.lib.speed_limit_assist.common import Policy
SpeedLimitSource = custom.LongitudinalPlanSP.SpeedLimitSource
ALL_SOURCES = tuple(SpeedLimitSource.schema.enumerants.values())
class SpeedLimitResolver:
_limit_solutions: dict[custom.LongitudinalPlanSP.SpeedLimitSource, float]
_distance_solutions: dict[custom.LongitudinalPlanSP.SpeedLimitSource, float]
_v_ego: float
speed_limit: float
distance: float
source: custom.LongitudinalPlanSP.SpeedLimitSource
def __init__(self):
self.params = Params()
self.frame = -1
self._gps_location_service = get_gps_location_service(self.params)
self._limit_solutions = {} # Store for speed limit solutions from different sources
self._distance_solutions = {} # Store for distance to current speed limit start for different sources
self.policy = self.params.get("SpeedLimitPolicy", return_default=True)
self._policy_to_sources_map = {
Policy.car_state_only: [SpeedLimitSource.car],
Policy.car_state_priority: [SpeedLimitSource.car, SpeedLimitSource.map],
Policy.map_data_priority: [SpeedLimitSource.map, SpeedLimitSource.car],
Policy.map_data_only: [SpeedLimitSource.map],
Policy.combined: [SpeedLimitSource.car, SpeedLimitSource.map],
}
self.source = SpeedLimitSource.none
for source in ALL_SOURCES:
self._reset_limit_sources(source)
def update_params(self):
if self.frame % int(PARAMS_UPDATE_PERIOD / DT_MDL) == 0:
self.policy = Policy(self.params.get("SpeedLimitPolicy", return_default=True))
self.change_policy(self.policy)
def change_policy(self, policy: Policy) -> None:
self.policy = policy
def _reset_limit_sources(self, source: custom.LongitudinalPlanSP.SpeedLimitSource) -> None:
self._limit_solutions[source] = 0.
self._distance_solutions[source] = 0.
def _resolve_limit_sources(self, sm: messaging.SubMaster) -> None:
"""Get limit solutions from each data source"""
self._get_from_car_state(sm)
self._get_from_map_data(sm)
def _get_from_car_state(self, sm: messaging.SubMaster) -> None:
self._reset_limit_sources(SpeedLimitSource.car)
self._limit_solutions[SpeedLimitSource.car] = sm['carStateSP'].speedLimit
self._distance_solutions[SpeedLimitSource.car] = 0.
def _get_from_map_data(self, sm: messaging.SubMaster) -> None:
self._reset_limit_sources(SpeedLimitSource.map)
self._process_map_data(sm)
def _process_map_data(self, sm: messaging.SubMaster) -> None:
gps_data = sm[self._gps_location_service]
map_data = sm['liveMapDataSP']
gps_fix_age = time.monotonic() - gps_data.unixTimestampMillis * 1e-3
if gps_fix_age > LIMIT_MAX_MAP_DATA_AGE:
return
speed_limit = map_data.speedLimit if map_data.speedLimitValid else 0.
next_speed_limit = map_data.speedLimitAhead if map_data.speedLimitAheadValid else 0.
self._calculate_map_data_limits(sm, speed_limit, next_speed_limit)
def _calculate_map_data_limits(self, sm: messaging.SubMaster, speed_limit: float, next_speed_limit: float) -> None:
gps_data = sm[self._gps_location_service]
map_data = sm['liveMapDataSP']
distance_since_fix = self._v_ego * (time.monotonic() - gps_data.unixTimestampMillis * 1e-3)
distance_to_speed_limit_ahead = max(0., map_data.speedLimitAheadDistance - distance_since_fix)
self._limit_solutions[SpeedLimitSource.map] = speed_limit
self._distance_solutions[SpeedLimitSource.map] = 0.
if 0. < next_speed_limit < self._v_ego:
adapt_time = (next_speed_limit - self._v_ego) / LIMIT_ADAPT_ACC
adapt_distance = self._v_ego * adapt_time + 0.5 * LIMIT_ADAPT_ACC * adapt_time ** 2
if distance_to_speed_limit_ahead <= adapt_distance:
self._limit_solutions[SpeedLimitSource.map] = next_speed_limit
self._distance_solutions[SpeedLimitSource.map] = distance_to_speed_limit_ahead
def _consolidate(self) -> tuple[float, float, custom.LongitudinalPlanSP.SpeedLimitSource]:
source = self._get_source_solution_according_to_policy()
speed_limit = self._limit_solutions[source] if source else 0.
distance = self._distance_solutions[source] if source else 0.
return speed_limit, distance, source
def _get_source_solution_according_to_policy(self) -> custom.LongitudinalPlanSP.SpeedLimitSource:
sources_for_policy = self._policy_to_sources_map[self.policy]
if self.policy != Policy.combined:
# They are ordered in the order of preference, so we pick the first that's non zero
for source in sources_for_policy:
return source if self._limit_solutions[source] > 0. else SpeedLimitSource.none
limits = np.array([self._limit_solutions[source] for source in sources_for_policy], dtype=float)
sources = np.array(sources_for_policy, dtype=int)
if len(limits) > 0:
min_idx = np.argmin(limits)
return SpeedLimitSource(int(sources[min_idx]))
return SpeedLimitSource.none
def update(self, v_ego: float, sm: messaging.SubMaster) -> None:
self._v_ego = v_ego
self.update_params()
self._resolve_limit_sources(sm)
self.speed_limit, self.distance, self.source = self._consolidate()
self.frame += 1

View File

@@ -0,0 +1,247 @@
"""
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 pytest
from cereal import custom
from opendbc.car.car_helpers import interfaces
from opendbc.car.toyota.values import CAR as TOYOTA
from openpilot.common.constants import CV
from openpilot.common.params import Params
from openpilot.common.realtime import DT_MDL
from openpilot.selfdrive.car.cruise import V_CRUISE_UNSET
from openpilot.sunnypilot.selfdrive.car import interfaces as sunnypilot_interfaces
from openpilot.sunnypilot.selfdrive.controls.lib.speed_limit_assist.common import OffsetType
from openpilot.sunnypilot.selfdrive.controls.lib.speed_limit_assist import SpeedLimitAssistState, REQUIRED_INITIAL_MAX_SET_SPEED, \
PRE_ACTIVE_GUARD_PERIOD
from openpilot.sunnypilot.selfdrive.controls.lib.speed_limit_assist.speed_limit_assist import SpeedLimitAssist, ACTIVE_STATES
from openpilot.sunnypilot.selfdrive.selfdrived.events import EventsSP
SpeedLimitSource = custom.LongitudinalPlanSP.SpeedLimitSource
ALL_STATES = tuple(SpeedLimitAssistState.schema.enumerants.values())
SPEED_LIMITS = {
'residential': 25 * CV.MPH_TO_MS, # 25 mph
'city': 35 * CV.MPH_TO_MS, # 35 mph
'highway': 65 * CV.MPH_TO_MS, # 65 mph
'freeway': 80 * CV.MPH_TO_MS, # 80 mph
}
class TestSpeedLimitAssist:
def setup_method(self):
self.params = Params()
self.reset_custom_params()
self.events_sp = EventsSP()
CI = self._setup_platform(TOYOTA.TOYOTA_RAV4_TSS2_2022)
self.sla = SpeedLimitAssist(CI.CP)
self.sla.pre_active_timer = int(PRE_ACTIVE_GUARD_PERIOD / DT_MDL)
def teardown_method(self, method):
self.reset_state()
def _setup_platform(self, car_name):
CarInterface = interfaces[car_name]
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(CI, self.params)
return CI
def reset_custom_params(self):
self.params.put_bool("SpeedLimitAssist", True)
self.params.put_bool("IsMetric", False)
self.params.put("SpeedLimitOffsetType", 0)
self.params.put("SpeedLimitValueOffset", 0)
def reset_state(self):
self.sla.state = SpeedLimitAssistState.disabled
self.sla.frame = -1
self.sla.last_op_engaged_frame = 0
self.sla.op_engaged = False
self.sla.op_engaged_prev = False
self.sla.initial_max_set = False
self.sla._speed_limit = 0.
self.sla.speed_limit_prev = 0.
self.sla.last_valid_speed_limit_offsetted = 0.
self.sla._distance = 0.
self.sla._source = SpeedLimitSource.none
self.sla.v_cruise_setpoint = 0.
self.sla.v_cruise_setpoint_prev = 0.
self.events_sp.clear()
def initialize_active_state(self, v_cruise_setpoint):
self.sla.state = SpeedLimitAssistState.active
self.sla.v_cruise_setpoint = v_cruise_setpoint
self.sla.v_cruise_setpoint_prev = v_cruise_setpoint
def test_initial_state(self):
assert self.sla.state == SpeedLimitAssistState.disabled
assert not self.sla.is_enabled
assert not self.sla.is_active
assert V_CRUISE_UNSET == self.sla.get_v_target_from_control()
def test_disabled(self):
self.params.put_bool("SpeedLimitAssist", False)
for _ in range(int(10. / DT_MDL)):
_ = self.sla.update(True, False, SPEED_LIMITS['city'], 0, SPEED_LIMITS['highway'], SPEED_LIMITS['city'], 0, SpeedLimitSource.car, self.events_sp)
assert self.sla.state == SpeedLimitAssistState.disabled
def test_transition_disabled_to_preactive(self):
for _ in range(int(3. / DT_MDL)):
_ = self.sla.update(True, False, SPEED_LIMITS['city'], 0, SPEED_LIMITS['highway'], SPEED_LIMITS['city'], 0, SpeedLimitSource.car, self.events_sp)
assert self.sla.state == SpeedLimitAssistState.preActive
assert self.sla.is_enabled and not self.sla.is_active
def test_preactive_to_active_with_max_speed_confirmation(self):
self.sla.state = SpeedLimitAssistState.preActive
v_cruise_sla = self.sla.update(True, False, SPEED_LIMITS['city'], 0, REQUIRED_INITIAL_MAX_SET_SPEED, SPEED_LIMITS['city'], 0, SpeedLimitSource.car, self.events_sp)
assert self.sla.state == SpeedLimitAssistState.active
assert self.sla.is_enabled and self.sla.is_active, f"enabled: {self.sla.is_enabled}, active: {self.sla.is_active}"
assert v_cruise_sla == SPEED_LIMITS['city']
def test_preactive_timeout_to_inactive(self):
self.sla.state = SpeedLimitAssistState.preActive
_ = self.sla.update(True, False, SPEED_LIMITS['city'], 0, SPEED_LIMITS['highway'], SPEED_LIMITS['city'], 0, SpeedLimitSource.car, self.events_sp)
for _ in range(int(PRE_ACTIVE_GUARD_PERIOD / DT_MDL)):
_ = self.sla.update(True, False, SPEED_LIMITS['city'], 0, SPEED_LIMITS['highway'], SPEED_LIMITS['city'], 0, SpeedLimitSource.car, self.events_sp)
assert self.sla.state == SpeedLimitAssistState.inactive
def test_preactive_to_pending_no_speed_limit(self):
self.sla.state = SpeedLimitAssistState.preActive
_ = self.sla.update(True, False, SPEED_LIMITS['city'], 0, REQUIRED_INITIAL_MAX_SET_SPEED, 0, 0, SpeedLimitSource.none, self.events_sp)
assert self.sla.state == SpeedLimitAssistState.pending
assert self.sla.is_enabled and not self.sla.is_active
def test_pending_to_active_when_speed_limit_available(self):
self.sla.state = SpeedLimitAssistState.pending
_ = self.sla.update(True, False, SPEED_LIMITS['city'], 0, REQUIRED_INITIAL_MAX_SET_SPEED, SPEED_LIMITS['city'], 0, SpeedLimitSource.car, self.events_sp)
assert self.sla.state == SpeedLimitAssistState.active
def test_pending_to_adapting_when_below_speed_limit(self):
self.sla.state = SpeedLimitAssistState.pending
_ = self.sla.update(True, False, SPEED_LIMITS['city'] + 5, 0, REQUIRED_INITIAL_MAX_SET_SPEED, SPEED_LIMITS['city'], 0, SpeedLimitSource.car, self.events_sp)
assert self.sla.state == SpeedLimitAssistState.adapting
assert self.sla.is_enabled and self.sla.is_active
def test_active_to_adapting_transition(self):
self.initialize_active_state(REQUIRED_INITIAL_MAX_SET_SPEED)
_ = self.sla.update(True, False, SPEED_LIMITS['city'] + 2, 0, REQUIRED_INITIAL_MAX_SET_SPEED, SPEED_LIMITS['city'], 0, SpeedLimitSource.car, self.events_sp)
assert self.sla.state == SpeedLimitAssistState.adapting
def test_adapting_to_active_transition(self):
self.sla.state = SpeedLimitAssistState.adapting
self.sla.v_cruise_setpoint_prev = REQUIRED_INITIAL_MAX_SET_SPEED
_ = self.sla.update(True, False, SPEED_LIMITS['city'], 0, REQUIRED_INITIAL_MAX_SET_SPEED, SPEED_LIMITS['city'], 0, SpeedLimitSource.car, self.events_sp)
assert self.sla.state == SpeedLimitAssistState.active
def test_manual_cruise_change_detection(self):
self.sla.state = SpeedLimitAssistState.active
expected_cruise = SPEED_LIMITS['highway']
self.sla.v_cruise_setpoint_prev = expected_cruise
different_cruise = SPEED_LIMITS['highway'] + 5
_ = self.sla.update(True, False, SPEED_LIMITS['city'], 0, different_cruise, SPEED_LIMITS['city'], 0, SpeedLimitSource.car, self.events_sp)
assert self.sla.state == SpeedLimitAssistState.inactive
@pytest.mark.parametrize("offset_type, offset_value, speed_limit, expected_offset", [
(OffsetType.fixed, 5, SPEED_LIMITS['city'], 5 * CV.MPH_TO_MS), # 5 MPH fixed offset
(OffsetType.percentage, 10, SPEED_LIMITS['city'], 0.1 * SPEED_LIMITS['city']), # 10% offset
(OffsetType.off, 0, SPEED_LIMITS['city'], 0), # Off
(OffsetType.fixed, 10, SPEED_LIMITS['highway'], 10 * CV.MPH_TO_MS), # Different speed, fixed offset
(OffsetType.percentage, 5, SPEED_LIMITS['highway'], 0.05 * SPEED_LIMITS['highway']), # Different speed, percentage
])
def test_offset_calculations(self, offset_type, offset_value, speed_limit, expected_offset):
self.sla._speed_limit = speed_limit
actual_offset = self.sla.get_offset(offset_type, offset_value)
assert actual_offset == pytest.approx(expected_offset, rel=0.01)
def test_rapid_speed_limit_changes(self):
self.initialize_active_state(REQUIRED_INITIAL_MAX_SET_SPEED)
speed_limits = [SPEED_LIMITS['city'], SPEED_LIMITS['highway'], SPEED_LIMITS['residential']]
for _, speed_limit in enumerate(speed_limits):
_ = self.sla.update(True, False, speed_limit, 0, REQUIRED_INITIAL_MAX_SET_SPEED, speed_limit, 0, SpeedLimitSource.car, self.events_sp)
assert self.sla.state in ACTIVE_STATES
def test_invalid_speed_limits_handling(self):
self.initialize_active_state(REQUIRED_INITIAL_MAX_SET_SPEED)
self.sla.last_valid_speed_limit_final = SPEED_LIMITS['city']
invalid_limits = [-10, 0, 200 * CV.MPH_TO_MS]
for invalid_limit in invalid_limits:
v_cruise_sla = self.sla.update(True, False, SPEED_LIMITS['city'], 0, REQUIRED_INITIAL_MAX_SET_SPEED, invalid_limit, 0, SpeedLimitSource.car, self.events_sp)
assert isinstance(v_cruise_sla, (int, float))
assert v_cruise_sla == V_CRUISE_UNSET or v_cruise_sla > 0
def test_stale_data_handling(self):
self.initialize_active_state(REQUIRED_INITIAL_MAX_SET_SPEED)
old_speed_limit = SPEED_LIMITS['city']
self.sla.last_valid_speed_limit_final = old_speed_limit
v_cruise_sla = self.sla.update(True, False, SPEED_LIMITS['city'], 0, REQUIRED_INITIAL_MAX_SET_SPEED, 0, 0, SpeedLimitSource.car, self.events_sp)
assert self.sla.state in ACTIVE_STATES
assert v_cruise_sla == old_speed_limit
def test_different_speed_limit_sources(self):
self.initialize_active_state(REQUIRED_INITIAL_MAX_SET_SPEED)
for source in (SpeedLimitSource.car, SpeedLimitSource.map):
v_cruise_sla = self.sla.update(True, False, SPEED_LIMITS['city'], 0, REQUIRED_INITIAL_MAX_SET_SPEED, SPEED_LIMITS['city'], 0, source, self.events_sp)
assert v_cruise_sla != V_CRUISE_UNSET
def test_distance_based_adapting(self):
self.sla.state = SpeedLimitAssistState.adapting
self.sla.v_cruise_setpoint_prev = REQUIRED_INITIAL_MAX_SET_SPEED
distance = 100.0
current_speed = SPEED_LIMITS['highway']
target_speed = SPEED_LIMITS['city']
v_cruise_sla = self.sla.update(True, False, current_speed, 0, REQUIRED_INITIAL_MAX_SET_SPEED, target_speed, distance, SpeedLimitSource.map, self.events_sp)
assert self.sla.state == SpeedLimitAssistState.adapting
assert v_cruise_sla == target_speed # TODO-SP: assert expected accel, need to enable self.acceleration_solutions
def test_long_disengaged_to_disabled(self):
self.initialize_active_state(REQUIRED_INITIAL_MAX_SET_SPEED)
v_cruise_sla = self.sla.update(False, False, SPEED_LIMITS['city'], 0, REQUIRED_INITIAL_MAX_SET_SPEED, SPEED_LIMITS['city'],
0, SpeedLimitSource.car, self.events_sp)
assert self.sla.state == SpeedLimitAssistState.disabled
assert v_cruise_sla == V_CRUISE_UNSET
def test_maintain_states_with_no_changes(self):
"""Test that states are maintained when no significant changes occur"""
test_states = [
SpeedLimitAssistState.preActive,
SpeedLimitAssistState.pending,
SpeedLimitAssistState.active,
SpeedLimitAssistState.adapting
]
for state in test_states:
self.sla.state = state
self.sla.op_engaged = True
if state in [SpeedLimitAssistState.pending, SpeedLimitAssistState.active, SpeedLimitAssistState.adapting]:
self.sla.initial_max_set = True
initial_state = state
_ = self.sla.update(True, False, SPEED_LIMITS['city'], 0, REQUIRED_INITIAL_MAX_SET_SPEED,SPEED_LIMITS['city'], 0, SpeedLimitSource.car, self.events_sp)
assert self.sla.state in ALL_STATES # Sanity check
if initial_state == SpeedLimitAssistState.preActive:
assert self.sla.state in [SpeedLimitAssistState.preActive, SpeedLimitAssistState.active]
elif initial_state in ACTIVE_STATES:
assert self.sla.state in ACTIVE_STATES

View File

@@ -0,0 +1,138 @@
import random
import time
import pytest
from pytest_mock import MockerFixture
from cereal import custom
from openpilot.sunnypilot.selfdrive.controls.lib.speed_limit_assist import LIMIT_MAX_MAP_DATA_AGE
from openpilot.sunnypilot.selfdrive.controls.lib.speed_limit_assist.speed_limit_resolver import SpeedLimitResolver, ALL_SOURCES
from openpilot.sunnypilot.selfdrive.controls.lib.speed_limit_assist.common import Policy
SpeedLimitSource = custom.LongitudinalPlanSP.SpeedLimitSource
def create_mock(properties, mocker: MockerFixture):
mock = mocker.MagicMock()
for _property, value in properties.items():
setattr(mock, _property, value)
return mock
def setup_sm_mock(mocker: MockerFixture):
cruise_speed_limit = random.uniform(0, 120)
live_map_data_limit = random.uniform(0, 120)
car_state = create_mock({
'gasPressed': False,
'brakePressed': False,
'standstill': False,
}, mocker)
car_state_sp = create_mock({
'speedLimit': cruise_speed_limit,
}, mocker)
live_map_data = create_mock({
'speedLimit': live_map_data_limit,
'speedLimitValid': True,
'speedLimitAhead': 0.,
'speedLimitAheadValid': 0.,
'speedLimitAheadDistance': 0.,
}, mocker)
gps_data = create_mock({
'unixTimestampMillis': time.monotonic() * 1e3,
}, mocker)
sm_mock = mocker.MagicMock()
sm_mock.__getitem__.side_effect = lambda key: {
'carState': car_state,
'liveMapDataSP': live_map_data,
'carStateSP': car_state_sp,
'gpsLocation': gps_data,
}[key]
return sm_mock
parametrized_policies = pytest.mark.parametrize(
"policy, sm_key, function_key", [
(Policy.car_state_only, 'carStateSP', SpeedLimitSource.car),
(Policy.car_state_priority, 'carStateSP', SpeedLimitSource.car),
(Policy.map_data_only, 'liveMapDataSP', SpeedLimitSource.map),
(Policy.map_data_priority, 'liveMapDataSP', SpeedLimitSource.map),
],
ids=lambda val: val.name if hasattr(val, 'name') else str(val)
)
@pytest.mark.parametrize("resolver_class", [SpeedLimitResolver])
class TestSpeedLimitResolverValidation:
@pytest.mark.parametrize("policy", list(Policy), ids=lambda policy: policy.name)
def test_initial_state(self, resolver_class, policy):
resolver = resolver_class()
resolver.policy = policy
for source in ALL_SOURCES:
if source in resolver._limit_solutions:
assert resolver._limit_solutions[source] == 0.
assert resolver._distance_solutions[source] == 0.
@parametrized_policies
def test_resolver(self, resolver_class, policy, sm_key, function_key, mocker: MockerFixture):
resolver = resolver_class()
resolver.policy = policy
sm_mock = setup_sm_mock(mocker)
source_speed_limit = sm_mock[sm_key].speedLimit
# Assert the resolver
resolver.update(source_speed_limit, sm_mock)
assert resolver.speed_limit == source_speed_limit
assert resolver.source == ALL_SOURCES[function_key]
def test_resolver_combined(self, resolver_class, mocker: MockerFixture):
resolver = resolver_class()
resolver.policy = Policy.combined
sm_mock = setup_sm_mock(mocker)
socket_to_source = {'carStateSP': SpeedLimitSource.car, 'liveMapDataSP': SpeedLimitSource.map}
minimum_key, minimum_speed_limit = min(
((key, sm_mock[key].speedLimit) for key in
socket_to_source.keys()), key=lambda x: x[1])
# Assert the resolver
resolver.update(minimum_speed_limit, sm_mock)
assert resolver.speed_limit == minimum_speed_limit
assert resolver.source == socket_to_source[minimum_key]
@parametrized_policies
def test_parser(self, resolver_class, policy, sm_key, function_key, mocker: MockerFixture):
resolver = resolver_class()
resolver.policy = policy
sm_mock = setup_sm_mock(mocker)
source_speed_limit = sm_mock[sm_key].speedLimit
# Assert the parsing
resolver.update(source_speed_limit, sm_mock)
assert resolver._limit_solutions[ALL_SOURCES[function_key]] == source_speed_limit
assert resolver._distance_solutions[ALL_SOURCES[function_key]] == 0.
@pytest.mark.parametrize("policy", list(Policy), ids=lambda policy: policy.name)
def test_resolve_interaction_in_update(self, resolver_class, policy, mocker: MockerFixture):
v_ego = 50
resolver = resolver_class()
resolver.policy = policy
sm_mock = setup_sm_mock(mocker)
resolver.update(v_ego, sm_mock)
# After resolution
assert resolver.speed_limit is not None
assert resolver.distance is not None
assert resolver.source is not None
@pytest.mark.parametrize("policy", list(Policy), ids=lambda policy: policy.name)
def test_old_map_data_ignored(self, resolver_class, policy, mocker: MockerFixture):
resolver = resolver_class()
resolver.policy = policy
sm_mock = mocker.MagicMock()
sm_mock['gpsLocation'].unixTimestampMillis = (time.monotonic() - 2 * LIMIT_MAX_MAP_DATA_AGE) * 1e3
resolver._get_from_map_data(sm_mock)
assert resolver._limit_solutions[SpeedLimitSource.map] == 0.
assert resolver._distance_solutions[SpeedLimitSource.map] == 0.

View File

@@ -1,4 +1,6 @@
import cereal.messaging as messaging
from cereal import log, car, custom
from openpilot.common.constants import CV
from openpilot.sunnypilot.selfdrive.selfdrived.events_base import EventsBase, Priority, ET, Alert, \
NoEntryAlert, ImmediateDisableAlert, EngagementAlert, NormalPermanentAlert, AlertCallbackType, wrong_car_mode_alert
@@ -14,6 +16,17 @@ EventNameSP = custom.OnroadEventSP.EventName
EVENT_NAME_SP = {v: k for k, v in EventNameSP.schema.enumerants.items()}
def speed_limit_adjust_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int, personality) -> Alert:
speedLimit = sm['longitudinalPlanSP'].sla.speedLimit
speed = round(speedLimit * (CV.MS_TO_KPH if metric else CV.MS_TO_MPH))
message = f'Adjusting to {speed} {"km/h" if metric else "mph"} speed limit'
return Alert(
message,
"",
AlertStatus.normal, AlertSize.small,
Priority.LOW, VisualAlert.none, AudibleAlert.none, 4.)
class EventsSP(EventsBase):
def __init__(self):
super().__init__()
@@ -148,5 +161,29 @@ EVENTS_SP: dict[int, dict[str, Alert | AlertCallbackType]] = {
"",
AlertStatus.normal, AlertSize.small,
Priority.LOW, VisualAlert.none, AudibleAlert.none, 1.),
}
},
EventNameSP.speedLimitActive: {
ET.WARNING: Alert(
"Automatically adjusting",
"to the posted speed limit",
AlertStatus.normal, AlertSize.small,
Priority.LOW, VisualAlert.none, AudibleAlert.none, 5.),
},
EventNameSP.speedLimitChanged: {
ET.WARNING: Alert(
"Set speed changed",
"",
AlertStatus.normal, AlertSize.small,
Priority.LOW, VisualAlert.none, AudibleAlert.none, 5.),
},
EventNameSP.speedLimitPreActive: {
ET.WARNING: Alert(
"Auto Speed Limit Assist: Activation Required",
"Manually change set speed to 80 MPH to activate",
AlertStatus.normal, AlertSize.mid,
Priority.LOW, VisualAlert.none, AudibleAlert.none, 5.),
},
}