diff --git a/.gitignore b/.gitignore index 2585c1085..60f937060 100644 --- a/.gitignore +++ b/.gitignore @@ -87,10 +87,19 @@ poetry.toml # rick - these are generated during compilation. selfdrive/camerad/camerad selfdrive/golden/ -selfdrive/legacy_modeld/_dmonitoringmodeld +selfdrive/hybrid_modeld/_dmonitoringmodeld +selfdrive/hybrid_modeld/_modeld +selfdrive/hybrid_modeld/models/supercombo.thneed +selfdrive/hybrid_modeld/models/supercombo_badweights.thneed +selfdrive/hybrid_modeld/thneed/compile +selfdrive/loggerd/bootlog + +# 0813 models selfdrive/legacy_modeld/_modeld selfdrive/legacy_modeld/models/supercombo.thneed +selfdrive/legacy_modeld/_dmonitoringmodeld selfdrive/legacy_modeld/models/supercombo_badweights.thneed selfdrive/legacy_modeld/thneed/compile -selfdrive/loggerd/bootlog +third_party/acados/lib +# lang files *.po~ diff --git a/README.md b/README.md index eb6146b72..de76e713a 100755 --- a/README.md +++ b/README.md @@ -42,6 +42,10 @@ I encourage users to consider purchasing a [comma 3](https://shop.comma.ai) for * Services are not optimized for resource usage, and using all services may result in overheating issues. * Language files can only be generated in a PC due to missing Qt5 tools. +## Configuration + +* Considering performance issues, I have turned off all logging-related services. If anyone needs to use them, they can use `dp_logging` parameter to enable them. +* If you are not a Comma Two device, you can use the `dp_no_fan_ctrl` parameter to disable fan-related detection and control. ======================= diff --git a/RELEASES.md b/RELEASES.md index b2e7be396..482e959d9 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,5 +1,11 @@ Version 0.9.3 (2023-06-XX) ======================== +* New driving model +* New driving personality setting + * Three settings: aggressive, standard, and relaxed + * Standard is recommended and the default + * In aggressive mode lead follow distance is shorter and quicker gas/brake response + * In relaxed mode lead follow distance is longer Version 0.9.2 (2023-05-22) ======================== diff --git a/cereal/README.md b/cereal/README.md index a07953b97..737534e36 100644 --- a/cereal/README.md +++ b/cereal/README.md @@ -1,5 +1,4 @@ -What is cereal? [![cereal tests](https://github.com/commaai/cereal/workflows/tests/badge.svg?event=push)](https://github.com/commaai/cereal/actions) [![codecov](https://codecov.io/gh/commaai/cereal/branch/master/graph/badge.svg)](https://codecov.io/gh/commaai/cereal) ----- +# What is cereal? [![cereal tests](https://github.com/commaai/cereal/workflows/tests/badge.svg?event=push)](https://github.com/commaai/cereal/actions) [![codecov](https://codecov.io/gh/commaai/cereal/branch/master/graph/badge.svg)](https://codecov.io/gh/commaai/cereal) cereal is both a messaging spec for robotics systems as well as generic high performance IPC pub sub messaging with a single publisher and multiple subscribers. @@ -9,29 +8,35 @@ Imagine this use case: * A localization process subscribes to the `sensorEvents` packet to use the IMU also -Messaging Spec ----- +## Messaging Spec -You'll find the message types in [log.capnp](log.capnp). It uses [Cap'n proto](https://capnproto.org/capnp-tool.html) and defines one struct called Event. +You'll find the message types in [log.capnp](log.capnp). It uses [Cap'n proto](https://capnproto.org/capnp-tool.html) and defines one struct called `Event`. -All Events have a `logMonoTime` and a `valid`. Then a big union defines the packet type. +All `Events` have a `logMonoTime` and a `valid`. Then a big union defines the packet type. - -Message definition Best Practices ----- +### Best Practices - **All fields must describe quantities in SI units**, unless otherwise specified in the field name. - - In the context of the message they are in, field names should be completely unambiguous. - - All values should be easy to plot and be human-readable with minimal parsing. +### Maintaining backwards-compatibility +When making changes to the messaging spec you want to maintain backwards-compatability, such that old logs can +be parsed with a new version of cereal. Adding structs and adding members to structs is generally safe, most other +things are not. Read more details [here](https://capnproto.org/language.html). -Pub Sub Backends ----- +### Custom forks -cereal supports two backends, one based on [zmq](https://zeromq.org/) and another called msgq, a custom pub sub based on shared memory that doesn't require the bytes to pass through the kernel. +Forks of [openpilot](https://github.com/commaai/openpilot) might want to add things to the messaging +spec, however this could conflict with future changes made in mainline cereal/openpilot. Rebasing against mainline openpilot +then means breaking backwards-compatibility with all old logs of your fork. So we added reserved events in +[custom.capnp](custom.capnp) that we will leave empty in mainline cereal/openpilot. **If you only modify those, you can ensure your +fork will remain backwards-compatible with all versions of mainline cereal/openpilot and your fork.** + +## Pub Sub Backends + +cereal supports two backends, one based on [zmq](https://zeromq.org/) and another called [msgq](messaging/msgq.cc), a custom pub sub based on shared memory that doesn't require the bytes to pass through the kernel. Example --- diff --git a/cereal/__init__.py b/cereal/__init__.py index 88a181cbc..4e26ffb2e 100644 --- a/cereal/__init__.py +++ b/cereal/__init__.py @@ -7,3 +7,4 @@ capnp.remove_import_hook() log = capnp.load(os.path.join(CEREAL_PATH, "log.capnp")) car = capnp.load(os.path.join(CEREAL_PATH, "car.capnp")) +custom = capnp.load(os.path.join(CEREAL_PATH, "custom.capnp")) diff --git a/cereal/car.capnp b/cereal/car.capnp index 2730888f7..319e3b7b6 100644 --- a/cereal/car.capnp +++ b/cereal/car.capnp @@ -23,7 +23,6 @@ struct CarEvent @0x9b1657f34caf3ad3 { enum EventName @0xbaa8c5d505f727de { canError @0; steerUnavailable @1; - brakeUnavailable @2; wrongGear @4; doorOpen @5; seatbeltNotLatched @6; @@ -140,6 +139,7 @@ struct CarEvent @0x9b1657f34caf3ad3 { startupOneplusDEPRECATED @82; startupFuzzyFingerprintDEPRECATED @97; noTargetDEPRECATED @25; + brakeUnavailableDEPRECATED @2; } } @@ -166,6 +166,8 @@ struct CarState { # gas pedal, 0.0-1.0 gas @3 :Float32; # this is user pedal only gasPressed @4 :Bool; # this is user pedal only + + engineRpm @46 :Float32; # brake pedal, 0.0-1.0 brake @5 :Float32; # this is user pedal only @@ -187,6 +189,7 @@ struct CarState { stockFcw @31 :Bool; espDisabled @32 :Bool; accFaulted @42 :Bool; + carFaultedNonCritical @47 :Bool; # some ECU is faulted, but car remains controllable # cruise state cruiseState @10 :CruiseState; diff --git a/cereal/custom.capnp b/cereal/custom.capnp new file mode 100644 index 000000000..c8b7f2523 --- /dev/null +++ b/cereal/custom.capnp @@ -0,0 +1,59 @@ +using Cxx = import "./include/c++.capnp"; +$Cxx.namespace("cereal"); + +@0xb526ba661d550a59; + +# custom.capnp: a home for empty structs reserved for custom forks +# These structs are guaranteed to remain reserved and empty in mainline +# cereal, so use these if you want custom events in your fork. + +# you can rename the struct, but don't change the identifier +struct LiveMapData @0x81c2f05a394cf4af { + speedLimitValid @0 :Bool; + speedLimit @1 :Float32; + speedLimitAheadValid @2 :Bool; + speedLimitAhead @3 :Float32; + speedLimitAheadDistance @4 :Float32; + turnSpeedLimitValid @5 :Bool; + turnSpeedLimit @6 :Float32; + turnSpeedLimitEndDistance @7 :Float32; + turnSpeedLimitSign @8 :Int16; + turnSpeedLimitsAhead @9 :List(Float32); + turnSpeedLimitsAheadDistances @10 :List(Float32); + turnSpeedLimitsAheadSigns @11 :List(Int16); + lastGpsTimestamp @12 :Int64; # Milliseconds since January 1, 1970. + currentRoadName @13 :Text; + lastGpsLatitude @14 :Float64; + lastGpsLongitude @15 :Float64; + lastGpsSpeed @16 :Float32; + lastGpsBearingDeg @17 :Float32; + lastGpsAccuracy @18 :Float32; + lastGpsBearingAccuracyDeg @19 :Float32; +} + +struct CustomReserved1 @0xaedffd8f31e7b55d { +} + +struct CustomReserved2 @0xf35cc4560bbf6ec2 { +} + +struct CustomReserved3 @0xda96579883444c35 { +} + +struct CustomReserved4 @0x80ae746ee2596b11 { +} + +struct CustomReserved5 @0xa5cd762cd951a455 { +} + +struct CustomReserved6 @0xf98d843bfd7004a3 { +} + +struct CustomReserved7 @0xb86e6369214c01c8 { +} + +struct CustomReserved8 @0xf416ec09499d9d19 { +} + +struct CustomReserved9 @0xa1680744031fdb2d { +} diff --git a/cereal/dp.capnp b/cereal/dp.capnp deleted file mode 100644 index c367f641e..000000000 --- a/cereal/dp.capnp +++ /dev/null @@ -1,11 +0,0 @@ -using Cxx = import "./include/c++.capnp"; -$Cxx.namespace("cereal"); - -@0xbfa7e645486440c7; - -# mapd -struct LiveMapData { - speedLimit @0 :Float32; - speedLimitValid @1 :Bool; - currentRoadName @2 :Text; -} diff --git a/cereal/libcereal_shared.so b/cereal/libcereal_shared.so index 07eea5ff0..0c26b48cf 100755 Binary files a/cereal/libcereal_shared.so and b/cereal/libcereal_shared.so differ diff --git a/cereal/log.capnp b/cereal/log.capnp index 03dc212b3..c44d8ed4d 100644 --- a/cereal/log.capnp +++ b/cereal/log.capnp @@ -3,7 +3,7 @@ $Cxx.namespace("cereal"); using Car = import "car.capnp"; using Legacy = import "legacy.capnp"; -using Dp = import "dp.capnp"; +using Custom = import "custom.capnp"; @0xf3b1f17e25a4285b; @@ -16,6 +16,12 @@ struct Map(Key, Value) { value @1 :Value; } } + +enum LongitudinalPersonality { + aggressive @0; + standard @1; + relaxed @2; + } struct InitData { kernelArgs @0 :List(Text); @@ -603,6 +609,7 @@ struct LiveCalibrationData { rpyCalib @7 :List(Float32); rpyCalibSpread @8 :List(Float32); wideFromDeviceEuler @10 :List(Float32); + height @12 :List(Float32); warpMatrixDEPRECATED @0 :List(Float32); calStatusDEPRECATED @1 :Int8; @@ -971,6 +978,7 @@ struct LongitudinalPlan @0xe00b5b3eba12876c { jerks @34 :List(Float32); solverExecutionTime @35 :Float32; + personality @36 :LongitudinalPersonality; enum LongitudinalPlanSource { cruise @0; @@ -1126,6 +1134,8 @@ struct LiveLocationKalman { excessiveResets @24 :Bool; timeToFirstFix @25 :Float32; + filterState @26 : Measurement; + enum Status { uninitialized @0; uncalibrated @1; @@ -1982,6 +1992,8 @@ struct CameraOdometry { rotStd @3 :List(Float32); # std rad/s in device frame wideFromDeviceEuler @6 :List(Float32); wideFromDeviceEulerStd @7 :List(Float32); + roadTransformTrans @8 :List(Float32); + roadTransformTransStd @9 :List(Float32); } struct Sentinel { @@ -2202,6 +2214,18 @@ struct Event { wideRoadEncodeData @88 :EncodeData; qRoadEncodeData @89 :EncodeData; + # *********** Custom: reserved for forks *********** + liveMapData @107 :Custom.LiveMapData; + customReserved1 @108 :Custom.CustomReserved1; + customReserved2 @109 :Custom.CustomReserved2; + customReserved3 @110 :Custom.CustomReserved3; + customReserved4 @111 :Custom.CustomReserved4; + customReserved5 @112 :Custom.CustomReserved5; + customReserved6 @113 :Custom.CustomReserved6; + customReserved7 @114 :Custom.CustomReserved7; + customReserved8 @115 :Custom.CustomReserved8; + customReserved9 @116 :Custom.CustomReserved9; + # *********** legacy + deprecated *********** model @9 :Legacy.ModelData; # TODO: rename modelV2 and mark this as deprecated liveMpcDEPRECATED @36 :LiveMpcData; @@ -2216,7 +2240,7 @@ struct Event { cellInfoDEPRECATED @28 :List(Legacy.CellInfo); wifiScanDEPRECATED @29 :List(Legacy.WifiScan); uiNavigationEventDEPRECATED @50 :Legacy.UiNavigationEvent; - liveMapData @62 :Dp.LiveMapData; + liveMapDataDEPRECATED @62 :LiveMapDataDEPRECATED; gpsPlannerPointsDEPRECATED @40 :Legacy.GPSPlannerPoints; gpsPlannerPlanDEPRECATED @41 :Legacy.GPSPlannerPlan; applanixRawDEPRECATED @42 :Data; diff --git a/cereal/messaging/__init__.py b/cereal/messaging/__init__.py index c695769e6..bc1167f03 100644 --- a/cereal/messaging/__init__.py +++ b/cereal/messaging/__init__.py @@ -1,5 +1,5 @@ # must be build with scons -from .messaging_pyx import Context, Poller, SubSocket, PubSocket # pylint: disable=no-name-in-module, import-error +from .messaging_pyx import Context, Poller, SubSocket, PubSocket, SocketEventHandle, toggle_fake_events, set_fake_prefix, get_fake_prefix, delete_fake_prefix, wait_for_one_event # pylint: disable=no-name-in-module, import-error from .messaging_pyx import MultiplePublishersError, MessagingError # pylint: disable=no-name-in-module, import-error import os import capnp @@ -12,6 +12,11 @@ from cereal.services import service_list assert MultiplePublishersError assert MessagingError +assert toggle_fake_events +assert set_fake_prefix +assert get_fake_prefix +assert delete_fake_prefix +assert wait_for_one_event NO_TRAVERSAL_LIMIT = 2**64-1 AVG_FREQ_HISTORY = 100 @@ -27,9 +32,20 @@ except ImportError: context = Context() + +def fake_event_handle(endpoint: str, identifier: Optional[str] = None, override: bool = True, enable: bool = False) -> SocketEventHandle: + identifier = identifier or get_fake_prefix() + handle = SocketEventHandle(endpoint, identifier, override) + if override: + handle.enabled = enable + + return handle + + def log_from_bytes(dat: bytes) -> capnp.lib.capnp._DynamicStructReader: return log.Event.from_bytes(dat, traversal_limit_in_words=NO_TRAVERSAL_LIMIT) + def new_message(service: Optional[str] = None, size: Optional[int] = None) -> capnp.lib.capnp._DynamicStructBuilder: dat = log.Event.new_message() dat.logMonoTime = int(sec_since_boot() * 1e9) @@ -41,11 +57,13 @@ def new_message(service: Optional[str] = None, size: Optional[int] = None) -> ca dat.init(service, size) return dat + def pub_sock(endpoint: str) -> PubSocket: sock = PubSocket() sock.connect(context, endpoint) return sock + def sub_sock(endpoint: str, poller: Optional[Poller] = None, addr: str = "127.0.0.1", conflate: bool = False, timeout: Optional[int] = None) -> SubSocket: sock = SubSocket() @@ -75,6 +93,7 @@ def drain_sock_raw(sock: SubSocket, wait_for_one: bool = False) -> List[bytes]: return ret + def drain_sock(sock: SubSocket, wait_for_one: bool = False) -> List[capnp.lib.capnp._DynamicStructReader]: """Receive all message currently available on the queue""" ret: List[capnp.lib.capnp._DynamicStructReader] = [] @@ -114,18 +133,21 @@ def recv_sock(sock: SubSocket, wait: bool = False) -> Optional[capnp.lib.capnp._ return dat + def recv_one(sock: SubSocket) -> Optional[capnp.lib.capnp._DynamicStructReader]: dat = sock.receive() if dat is not None: dat = log_from_bytes(dat) return dat + def recv_one_or_none(sock: SubSocket) -> Optional[capnp.lib.capnp._DynamicStructReader]: dat = sock.receive(non_blocking=True) if dat is not None: dat = log_from_bytes(dat) return dat + def recv_one_retry(sock: SubSocket) -> capnp.lib.capnp._DynamicStructReader: """Keep receiving until we get a message""" while True: @@ -133,6 +155,7 @@ def recv_one_retry(sock: SubSocket) -> capnp.lib.capnp._DynamicStructReader: if dat is not None: return log_from_bytes(dat) + class SubMaster: def __init__(self, services: List[str], poll: Optional[List[str]] = None, ignore_alive: Optional[List[str]] = None, ignore_avg_freq: Optional[List[str]] = None, @@ -247,6 +270,7 @@ class SubMaster: and self.all_freq_ok(service_list=service_list) \ and self.all_valid(service_list=service_list) + class PubMaster: def __init__(self, services: List[str]): self.sock = {} diff --git a/cereal/messaging/event.h b/cereal/messaging/event.h new file mode 100644 index 000000000..c638b6b4e --- /dev/null +++ b/cereal/messaging/event.h @@ -0,0 +1,58 @@ +#pragma once + +#include +#include + +#define CEREAL_EVENTS_PREFIX std::string("cereal_events") + +void event_state_shm_mmap(std::string endpoint, std::string identifier, char **shm_mem, std::string *shm_path); + +enum EventPurpose { + RECV_CALLED, + RECV_READY +}; + +struct EventState { + int fds[2]; + bool enabled; +}; + +class Event { +private: + int event_fd = -1; + + inline void throw_if_invalid() const { + if (!this->is_valid()) { + throw std::runtime_error("Event does not have valid file descriptor."); + } + } +public: + Event(int fd = -1); + + void set() const; + int clear() const; + void wait(int timeout_sec = -1) const; + bool peek() const; + bool is_valid() const; + int fd() const; + + static int wait_for_one(const std::vector& events, int timeout_sec = -1); +}; + +class SocketEventHandle { +private: + std::string shm_path; + EventState* state; +public: + SocketEventHandle(std::string endpoint, std::string identifier = "", bool override = true); + ~SocketEventHandle(); + + bool is_enabled(); + void set_enabled(bool enabled); + Event recv_called(); + Event recv_ready(); + + static void toggle_fake_events(bool enabled); + static void set_fake_prefix(std::string prefix); + static std::string fake_prefix(); +}; diff --git a/cereal/messaging/impl_fake.h b/cereal/messaging/impl_fake.h new file mode 100644 index 000000000..0b3f6eb41 --- /dev/null +++ b/cereal/messaging/impl_fake.h @@ -0,0 +1,67 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "cereal/messaging/messaging.h" +#include "cereal/messaging/event.h" + +template +class FakeSubSocket: public TSubSocket { +private: + Event *recv_called = nullptr; + Event *recv_ready = nullptr; + EventState *state = nullptr; + +public: + FakeSubSocket(): TSubSocket() {} + ~FakeSubSocket() { + delete recv_called; + delete recv_ready; + if (state != nullptr) { + munmap(state, sizeof(EventState)); + } + } + + int connect(Context *context, std::string endpoint, std::string address, bool conflate=false, bool check_endpoint=true) override { + const char* cereal_prefix = std::getenv("CEREAL_FAKE_PREFIX"); + + char* mem; + std::string identifier = cereal_prefix != nullptr ? std::string(cereal_prefix) : ""; + event_state_shm_mmap(endpoint, identifier, &mem, nullptr); + + this->state = (EventState*)mem; + this->recv_called = new Event(state->fds[EventPurpose::RECV_CALLED]); + this->recv_ready = new Event(state->fds[EventPurpose::RECV_READY]); + + return TSubSocket::connect(context, endpoint, address, conflate, check_endpoint); + } + + Message *receive(bool non_blocking=false) override { + if (this->state->enabled) { + this->recv_called->set(); + this->recv_ready->wait(); + this->recv_ready->clear(); + } + + return TSubSocket::receive(non_blocking); + } +}; + +class FakePoller: public Poller { +private: + std::vector sockets; + +public: + void registerSocket(SubSocket *socket) override; + std::vector poll(int timeout) override; + ~FakePoller() {}; +}; diff --git a/cereal/messaging/messaging.pxd b/cereal/messaging/messaging.pxd index 38c6b09e1..97b9a22b0 100644 --- a/cereal/messaging/messaging.pxd +++ b/cereal/messaging/messaging.pxd @@ -6,6 +6,34 @@ from libcpp.vector cimport vector from libcpp cimport bool +cdef extern from "cereal/messaging/impl_fake.h": + cdef cppclass Event: + @staticmethod + int wait_for_one(vector[Event], int) except + + + Event() + Event(int) + void set() + int clear() + void wait(int) except + + bool peek() + int fd() + + cdef cppclass SocketEventHandle: + @staticmethod + void toggle_fake_events(bool) + @staticmethod + void set_fake_prefix(string) + @staticmethod + string fake_prefix() + + SocketEventHandle(string, string, bool) + bool is_enabled() + void set_enabled(bool) + Event recv_called() + Event recv_ready() + + cdef extern from "cereal/messaging/messaging.h": cdef cppclass Context: @staticmethod diff --git a/cereal/messaging/messaging_pyx.so b/cereal/messaging/messaging_pyx.so index 8bc391070..ddb08ce4f 100755 Binary files a/cereal/messaging/messaging_pyx.so and b/cereal/messaging/messaging_pyx.so differ diff --git a/cereal/visionipc/visionipc_pyx.so b/cereal/visionipc/visionipc_pyx.so index 2ea6fc9ec..314a7e4ae 100755 Binary files a/cereal/visionipc/visionipc_pyx.so and b/cereal/visionipc/visionipc_pyx.so differ diff --git a/common/legacy_modeldata.h b/common/hybrid_modeldata.h similarity index 100% rename from common/legacy_modeldata.h rename to common/hybrid_modeldata.h diff --git a/common/params_pyx.so b/common/params_pyx.so index a522165f6..c4c2c6edc 100755 Binary files a/common/params_pyx.so and b/common/params_pyx.so differ diff --git a/common/util.h b/common/util.h index c80dc234f..34721700e 100644 --- a/common/util.h +++ b/common/util.h @@ -70,7 +70,7 @@ std::string string_format(const std::string& format, Args... args) { return std::string(buf.get(), buf.get() + size - 1); } -std::string getenv(const char* key, const char* default_val = ""); +std::string getenv(const char* key, std::string default_val = ""); int getenv(const char* key, int default_val); float getenv(const char* key, float default_val); diff --git a/common/version.h b/common/version.h index c41423539..cb3af68e6 100644 --- a/common/version.h +++ b/common/version.h @@ -1 +1 @@ -#define COMMA_VERSION "2023.05.25" +#define COMMA_VERSION "2023.06.12" diff --git a/docs/CARS.md b/docs/CARS.md index 551bf42ae..373851932 100644 --- a/docs/CARS.md +++ b/docs/CARS.md @@ -6,262 +6,262 @@ A supported vehicle is one that just works when you install a comma three. All s # 252 Supported Cars -|Make|Model|Supported Package|ACC|No ACC accel below|No ALC below|Steering Torque|Resume from stop|Harness Kit
 |Video| +|Make|Model|Supported Package|ACC|No ACC accel below|No ALC below|Steering Torque|Resume from stop|Hardware Needed
 |Video| |---|---|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:| -|Acura|ILX 2016-19|AcuraWatch Plus|openpilot|25 mph|25 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Honda Nidec connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Acura|RDX 2016-18|AcuraWatch Plus|openpilot|25 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Honda Nidec connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Acura|RDX 2019-22|All|openpilot available[1](#footnotes)|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Honda Bosch A connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Audi|A3 2014-19|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 harness box
- 1 long OBD-C cable
|| -|Audi|A3 Sportback e-tron 2017-18|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 harness box
- 1 long OBD-C cable
|| -|Audi|Q2 2018|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 harness box
- 1 long OBD-C cable
|| -|Audi|Q3 2019-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 harness box
- 1 long OBD-C cable
|| -|Audi|RS3 2018|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 harness box
- 1 long OBD-C cable
|| -|Audi|S3 2015-17|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 harness box
- 1 long OBD-C cable
|| -|Buick|LaCrosse 2017-19[3](#footnotes)|Driver Confidence Package 2|openpilot|18 mph|7 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 OBD-II connector
- 1 USB-C coupler
- 1 long OBD-C cable
|| -|Cadillac|Escalade 2017[3](#footnotes)|Driver Assist Package|openpilot|0 mph|7 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 OBD-II connector
- 1 USB-C coupler
- 1 long OBD-C cable
|| -|Cadillac|Escalade ESV 2016[3](#footnotes)|Adaptive Cruise Control (ACC) & LKAS|openpilot|0 mph|7 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 OBD-II connector
- 1 USB-C coupler
- 1 long OBD-C cable
|| -|Chevrolet|Bolt EUV 2022-23|Premier or Premier Redline Trim without Super Cruise Package|openpilot available[1](#footnotes)|3 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 GM connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Chevrolet|Bolt EV 2022-23|2LT Trim with Adaptive Cruise Control Package|openpilot available[1](#footnotes)|3 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 GM connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Chevrolet|Silverado 1500 2020-21|Safety Package II|openpilot available[1](#footnotes)|3 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 GM connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Chevrolet|Trailblazer 2021-22|Adaptive Cruise Control (ACC)|openpilot available[1](#footnotes)|3 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 GM connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Chevrolet|Volt 2017-18[3](#footnotes)|Adaptive Cruise Control (ACC)|openpilot|0 mph|7 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 OBD-II connector
- 1 USB-C coupler
- 1 long OBD-C cable
|| -|Chrysler|Pacifica 2017-18|Adaptive Cruise Control (ACC)|Stock|0 mph|9 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 FCA connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Chrysler|Pacifica 2019-20|Adaptive Cruise Control (ACC)|Stock|0 mph|39 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 FCA connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Chrysler|Pacifica 2021|All|Stock|0 mph|39 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 FCA connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Chrysler|Pacifica Hybrid 2017-18|Adaptive Cruise Control (ACC)|Stock|0 mph|9 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 FCA connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Chrysler|Pacifica Hybrid 2019-23|Adaptive Cruise Control (ACC)|Stock|0 mph|39 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 FCA connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| +|Acura|ILX 2016-19|AcuraWatch Plus|openpilot|25 mph|25 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Honda Nidec connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Acura|RDX 2016-18|AcuraWatch Plus|openpilot|25 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Honda Nidec connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Acura|RDX 2019-22|All|openpilot available[1](#footnotes)|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Honda Bosch A connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Audi|A3 2014-19|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,10](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 comma three
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Audi|A3 Sportback e-tron 2017-18|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,10](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 comma three
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Audi|Q2 2018|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,10](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 comma three
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Audi|Q3 2019-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,10](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 comma three
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Audi|RS3 2018|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,10](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 comma three
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Audi|S3 2015-17|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,10](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 comma three
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Buick|LaCrosse 2017-19[3](#footnotes)|Driver Confidence Package 2|openpilot|18 mph|7 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 OBD-II connector
- 1 comma three
- 2 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Cadillac|Escalade 2017[3](#footnotes)|Driver Assist Package|openpilot|0 mph|7 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 OBD-II connector
- 1 comma three
- 2 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Cadillac|Escalade ESV 2016[3](#footnotes)|Adaptive Cruise Control (ACC) & LKAS|openpilot|0 mph|7 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 OBD-II connector
- 1 comma three
- 2 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Chevrolet|Bolt EUV 2022-23|Premier or Premier Redline Trim without Super Cruise Package|openpilot available[1](#footnotes)|3 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 GM connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Chevrolet|Bolt EV 2022-23|2LT Trim with Adaptive Cruise Control Package|openpilot available[1](#footnotes)|3 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 GM connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Chevrolet|Silverado 1500 2020-21|Safety Package II|openpilot available[1](#footnotes)|3 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 GM connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Chevrolet|Trailblazer 2021-22|Adaptive Cruise Control (ACC)|openpilot available[1](#footnotes)|3 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 GM connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Chevrolet|Volt 2017-18[3](#footnotes)|Adaptive Cruise Control (ACC)|openpilot|0 mph|7 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 OBD-II connector
- 1 comma three
- 2 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Chrysler|Pacifica 2017-18|Adaptive Cruise Control (ACC)|Stock|0 mph|9 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 FCA connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Chrysler|Pacifica 2019-20|Adaptive Cruise Control (ACC)|Stock|0 mph|39 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 FCA connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Chrysler|Pacifica 2021|All|Stock|0 mph|39 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 FCA connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Chrysler|Pacifica Hybrid 2017-18|Adaptive Cruise Control (ACC)|Stock|0 mph|9 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 FCA connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Chrysler|Pacifica Hybrid 2019-23|Adaptive Cruise Control (ACC)|Stock|0 mph|39 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 FCA connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| |comma|body|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|None|| -|Ford|Bronco Sport 2021-22|Co-Pilot360 Assist+|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Ford Q3 connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Ford|Escape 2020-22|Co-Pilot360 Assist+|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Ford Q3 connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Ford|Explorer 2020-22|Co-Pilot360 Assist+|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Ford Q3 connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Ford|Kuga 2020-22|Adaptive Cruise Control with Lane Centering|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Ford Q3 connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Ford|Maverick 2022-23|Co-Pilot360 Assist|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Ford Q3 connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Genesis|G70 2018-19|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai F connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Genesis|G70 2020|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai F connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Genesis|G80 2017|All|Stock|19 mph|37 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai J connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Genesis|G80 2018-19|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai H connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Genesis|G90 2017-18|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai C connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Genesis|GV60 (Advanced Trim) 2023[5](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai A connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Genesis|GV60 (Performance Trim) 2023[5](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai K connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Genesis|GV70 (2.5T Trim) 2022-23[5](#footnotes)|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai L connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Genesis|GV70 (3.5T Trim) 2022-23[5](#footnotes)|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai M connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Genesis|GV80 2023[5](#footnotes)|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai M connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|GMC|Acadia 2018[3](#footnotes)|Adaptive Cruise Control (ACC)|openpilot|0 mph|7 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 OBD-II connector
- 1 USB-C coupler
- 1 long OBD-C cable
|| -|GMC|Sierra 1500 2020-21|Driver Alert Package II|openpilot available[1](#footnotes)|3 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 GM connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Honda|Accord 2018-22|All|openpilot available[1](#footnotes)|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Honda Bosch A connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Honda|Accord Hybrid 2018-22|All|openpilot available[1](#footnotes)|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Honda Bosch A connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Honda|Civic 2016-18|Honda Sensing|openpilot|0 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Honda Nidec connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Honda|Civic 2019-21|All|openpilot available[1](#footnotes)|0 mph|2 mph[4](#footnotes)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Honda Bosch A connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Honda|Civic 2022|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Honda Bosch B connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Honda|Civic Hatchback 2017-21|Honda Sensing|openpilot available[1](#footnotes)|0 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Honda Bosch A connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Honda|Civic Hatchback 2022|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Honda Bosch B connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Honda|CR-V 2015-16|Touring Trim|openpilot|25 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Honda Nidec connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Honda|CR-V 2017-22|Honda Sensing|openpilot available[1](#footnotes)|0 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Honda Bosch A connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Honda|CR-V Hybrid 2017-19|Honda Sensing|openpilot available[1](#footnotes)|0 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Honda Bosch A connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Honda|e 2020|All|openpilot available[1](#footnotes)|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Honda Bosch A connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Honda|Fit 2018-20|Honda Sensing|openpilot|25 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Honda Nidec connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Honda|Freed 2020|Honda Sensing|openpilot|25 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Honda Nidec connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Honda|HR-V 2019-22|Honda Sensing|openpilot|25 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Honda Nidec connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Honda|HR-V 2023|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Honda Bosch B connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Honda|Insight 2019-22|All|openpilot available[1](#footnotes)|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Honda Bosch A connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Honda|Inspire 2018|All|openpilot available[1](#footnotes)|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Honda Bosch A connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Honda|Odyssey 2018-20|Honda Sensing|openpilot|25 mph|0 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Honda Nidec connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Honda|Passport 2019-22|All|openpilot|25 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Honda Nidec connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Honda|Pilot 2016-22|Honda Sensing|openpilot|25 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Honda Nidec connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Honda|Ridgeline 2017-23|Honda Sensing|openpilot|25 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Honda Nidec connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Hyundai|Elantra 2017-19|Smart Cruise Control (SCC)|Stock|19 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai B connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Hyundai|Elantra 2021-23|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai K connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Hyundai|Elantra GT 2017-19|Smart Cruise Control (SCC)|Stock|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai E connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Hyundai|Elantra Hybrid 2021-23|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai K connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Hyundai|Genesis 2015-16|Smart Cruise Control (SCC)|Stock|19 mph|37 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai J connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Hyundai|i30 2017-19|Smart Cruise Control (SCC)|Stock|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai E connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Hyundai|Ioniq 5 (Southeast Asia only) 2022-23[5](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai Q connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Hyundai|Ioniq 5 (with HDA II) 2022-23[5](#footnotes)|Highway Driving Assist II|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai Q connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Hyundai|Ioniq 5 (without HDA II) 2022-23[5](#footnotes)|Highway Driving Assist|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai K connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Hyundai|Ioniq Electric 2019|Smart Cruise Control (SCC)|Stock|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai C connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Hyundai|Ioniq Electric 2020|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai H connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Hyundai|Ioniq Hybrid 2017-19|Smart Cruise Control (SCC)|Stock|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai C connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Hyundai|Ioniq Hybrid 2020-22|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai H connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Hyundai|Ioniq Plug-in Hybrid 2019|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai C connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Hyundai|Ioniq Plug-in Hybrid 2020-22|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai H connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Hyundai|Kona 2020|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai B connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Hyundai|Kona Electric 2018-21|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai G connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Hyundai|Kona Electric 2022|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai O connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Hyundai|Kona Hybrid 2020|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai I connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Hyundai|Palisade 2020-22|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai H connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Hyundai|Santa Cruz 2022-23[5](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai N connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Hyundai|Santa Fe 2019-20|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai D connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Hyundai|Santa Fe 2021-22|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai L connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Hyundai|Santa Fe Hybrid 2022|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai L connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Hyundai|Santa Fe Plug-in Hybrid 2022|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai L connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Hyundai|Sonata 2018-19|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai E connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Hyundai|Sonata 2020-23|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai A connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Hyundai|Sonata Hybrid 2020-22|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai A connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Hyundai|Tucson 2021|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai L connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Hyundai|Tucson 2022[5](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai N connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Hyundai|Tucson 2023[5](#footnotes)|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai N connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Hyundai|Tucson Diesel 2019|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai L connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Hyundai|Tucson Hybrid 2022-23[5](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai N connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Hyundai|Veloster 2019-20|Smart Cruise Control (SCC)|Stock|5 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai E connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Jeep|Grand Cherokee 2016-18|Adaptive Cruise Control (ACC)|Stock|0 mph|9 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 FCA connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Jeep|Grand Cherokee 2019-21|Adaptive Cruise Control (ACC)|Stock|0 mph|39 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 FCA connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Kia|Ceed 2019|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai E connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Kia|EV6 (Southeast Asia only) 2022-23[5](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai P connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Kia|EV6 (with HDA II) 2022-23[5](#footnotes)|Highway Driving Assist II|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai P connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Kia|EV6 (without HDA II) 2022-23[5](#footnotes)|Highway Driving Assist|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai L connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Kia|Forte 2019-21|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai G connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Kia|Forte 2023|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai E connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Kia|K5 2021-22|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai A connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Kia|K5 Hybrid 2020|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai A connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Kia|Niro EV 2019|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai H connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Kia|Niro EV 2020|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai F connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Kia|Niro EV 2021|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai C connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Kia|Niro EV 2022|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai H connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Kia|Niro EV 2023[5](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai A connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Kia|Niro Hybrid 2021-22|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai F connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Kia|Niro Hybrid 2023[5](#footnotes)|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai A connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Kia|Niro Plug-in Hybrid 2018-19|All|openpilot available[1](#footnotes)|10 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai C connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Kia|Niro Plug-in Hybrid 2020|All|openpilot available[1](#footnotes)|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai D connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Kia|Optima 2017|Advanced Smart Cruise Control|Stock|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai B connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Kia|Optima 2019-20|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai G connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Kia|Seltos 2021|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai A connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Kia|Sorento 2018|Advanced Smart Cruise Control|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai C connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Kia|Sorento 2019|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai E connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Kia|Sorento 2021-23[5](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai K connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Kia|Sorento Plug-in Hybrid 2022-23[5](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai A connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Kia|Sportage 2023[5](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai N connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Kia|Sportage Hybrid 2023[5](#footnotes)|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai N connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Kia|Stinger 2018-20|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai C connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Kia|Stinger 2022|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai K connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Kia|Telluride 2020-22|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai H connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Lexus|CT Hybrid 2017-18|Lexus Safety System+|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 Toyota connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Lexus|ES 2017-18|All|openpilot available[2](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 Toyota connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Lexus|ES 2019-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Toyota connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Lexus|ES Hybrid 2017-18|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Toyota connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Lexus|ES Hybrid 2019-23|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Toyota connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Lexus|IS 2017-19|All|Stock|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 Toyota connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Lexus|NX 2018-19|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 Toyota connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Lexus|NX 2020-21|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Toyota connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Lexus|NX Hybrid 2018-19|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 Toyota connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Lexus|NX Hybrid 2020-21|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Toyota connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Lexus|RC 2018-20|All|Stock|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 Toyota connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Lexus|RX 2016|Lexus Safety System+|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 Toyota connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Lexus|RX 2017-19|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 Toyota connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Lexus|RX 2020-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Toyota connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Lexus|RX Hybrid 2016|Lexus Safety System+|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 Toyota connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Lexus|RX Hybrid 2017-19|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 Toyota connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Lexus|RX Hybrid 2020-21|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Toyota connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Lexus|UX Hybrid 2019-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Toyota connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Lincoln|Aviator 2021|Co-Pilot360 Plus|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Ford Q3 connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|MAN|eTGE 2020-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|31 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 harness box
- 1 long OBD-C cable
|| -|MAN|TGE 2017-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|31 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 harness box
- 1 long OBD-C cable
|| -|Mazda|CX-5 2022-23|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Mazda connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Mazda|CX-9 2021-23|All|Stock|0 mph|28 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Mazda connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Nissan|Altima 2019-20|ProPILOT Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 Nissan B connector
- 1 RJ45 cable (7 ft)
- 1 USB-C coupler
- 1 harness box
- 1 long OBD-C cable
|| -|Nissan|Leaf 2018-23|ProPILOT Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 Nissan A connector
- 1 RJ45 cable (7 ft)
- 1 USB-C coupler
- 1 harness box
- 1 long OBD-C cable
|| -|Nissan|Rogue 2018-20|ProPILOT Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 Nissan A connector
- 1 RJ45 cable (7 ft)
- 1 USB-C coupler
- 1 harness box
- 1 long OBD-C cable
|| -|Nissan|X-Trail 2017|ProPILOT Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 Nissan A connector
- 1 RJ45 cable (7 ft)
- 1 USB-C coupler
- 1 harness box
- 1 long OBD-C cable
|| -|Ram|1500 2019-23|Adaptive Cruise Control (ACC)|Stock|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Ram connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|SEAT|Ateca 2018|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 harness box
- 1 long OBD-C cable
|| -|SEAT|Leon 2014-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 harness box
- 1 long OBD-C cable
|| -|Subaru|Ascent 2019-21|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 Subaru A connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Subaru|Crosstrek 2018-19|EyeSight Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 Subaru A connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Subaru|Crosstrek 2020-23|EyeSight Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 Subaru A connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Subaru|Forester 2019-21|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 Subaru A connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Subaru|Impreza 2017-19|EyeSight Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 Subaru A connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Subaru|Impreza 2020-22|EyeSight Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 Subaru A connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Subaru|Legacy 2020-22|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 Subaru B connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Subaru|Outback 2020-22|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 Subaru B connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Subaru|XV 2018-19|EyeSight Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 Subaru A connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Subaru|XV 2020-21|EyeSight Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 Subaru A connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Škoda|Fabia 2022-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 harness box
- 1 long OBD-C cable
[10](#footnotes)|| -|Škoda|Kamiq 2021[7](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 harness box
- 1 long OBD-C cable
[10](#footnotes)|| -|Škoda|Karoq 2019-21|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 harness box
- 1 long OBD-C cable
|| -|Škoda|Kodiaq 2017-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 harness box
- 1 long OBD-C cable
|| -|Škoda|Octavia 2015, 2018-19|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 harness box
- 1 long OBD-C cable
|| -|Škoda|Octavia RS 2016|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 harness box
- 1 long OBD-C cable
|| -|Škoda|Scala 2020|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 harness box
- 1 long OBD-C cable
[10](#footnotes)|| -|Škoda|Superb 2015-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 harness box
- 1 long OBD-C cable
|| -|Toyota|Alphard 2019-20|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Toyota connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Toyota|Alphard Hybrid 2021|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Toyota connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Toyota|Avalon 2016|Toyota Safety Sense P|openpilot available[2](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 Toyota connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Toyota|Avalon 2017-18|All|openpilot available[2](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 Toyota connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Toyota|Avalon 2019-21|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 Toyota connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Toyota|Avalon 2022|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Toyota connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Toyota|Avalon Hybrid 2019-21|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 Toyota connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Toyota|Avalon Hybrid 2022|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Toyota connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Toyota|C-HR 2017-20|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 Toyota connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Toyota|C-HR 2021|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 Toyota connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Toyota|C-HR Hybrid 2017-20|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 Toyota connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Toyota|C-HR Hybrid 2021-22|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 Toyota connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Toyota|Camry 2018-20|All|Stock|0 mph[6](#footnotes)|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 Toyota connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Toyota|Camry 2021-23|All|openpilot|0 mph[6](#footnotes)|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Toyota connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Toyota|Camry Hybrid 2018-20|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 Toyota connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Toyota|Camry Hybrid 2021-23|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Toyota connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Toyota|Corolla 2017-19|All|openpilot available[2](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 Toyota connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Toyota|Corolla 2020-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Toyota connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Toyota|Corolla Cross (Non-US only) 2020-23|All|openpilot|17 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Toyota connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Toyota|Corolla Cross Hybrid (Non-US only) 2020-22|All|openpilot|17 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Toyota connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Toyota|Corolla Hatchback 2019-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Toyota connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Toyota|Corolla Hybrid 2020-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Toyota connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Toyota|Corolla Hybrid (Non-US only) 2020-23|All|openpilot|17 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Toyota connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Toyota|Highlander 2017-19|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Toyota connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Toyota|Highlander 2020-23|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Toyota connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Toyota|Highlander Hybrid 2017-19|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Toyota connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Toyota|Highlander Hybrid 2020-23|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Toyota connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Toyota|Mirai 2021|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Toyota connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Toyota|Prius 2016|Toyota Safety Sense P|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 Toyota connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Toyota|Prius 2017-20|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 Toyota connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Toyota|Prius 2021-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Toyota connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Toyota|Prius Prime 2017-20|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 Toyota connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Toyota|Prius Prime 2021-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Toyota connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Toyota|Prius v 2017|Toyota Safety Sense P|openpilot available[2](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Toyota connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Toyota|RAV4 2016|Toyota Safety Sense P|openpilot available[2](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 Toyota connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Toyota|RAV4 2017-18|All|openpilot available[2](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 Toyota connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Toyota|RAV4 2019-21|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Toyota connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Toyota|RAV4 2022|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 Toyota connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Toyota|RAV4 Hybrid 2016|Toyota Safety Sense P|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Toyota connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Toyota|RAV4 Hybrid 2017-18|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Toyota connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Toyota|RAV4 Hybrid 2019-21|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Toyota connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Toyota|RAV4 Hybrid 2022|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 Toyota connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Toyota|Sienna 2018-20|All|openpilot available[2](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Toyota connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 harness box
|| -|Volkswagen|Arteon 2018-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 harness box
- 1 long OBD-C cable
|| -|Volkswagen|Arteon eHybrid 2020-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 harness box
- 1 long OBD-C cable
|| -|Volkswagen|Arteon R 2020-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 harness box
- 1 long OBD-C cable
|| -|Volkswagen|Atlas 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 harness box
- 1 long OBD-C cable
|| -|Volkswagen|Atlas Cross Sport 2021-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 harness box
- 1 long OBD-C cable
|| -|Volkswagen|California 2021|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|31 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 harness box
- 1 long OBD-C cable
|| -|Volkswagen|Caravelle 2020|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|31 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 harness box
- 1 long OBD-C cable
|| -|Volkswagen|CC 2018-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 harness box
- 1 long OBD-C cable
|| -|Volkswagen|Crafter 2017-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|31 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 harness box
- 1 long OBD-C cable
|| -|Volkswagen|e-Crafter 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|31 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 harness box
- 1 long OBD-C cable
|| -|Volkswagen|e-Golf 2014-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 harness box
- 1 long OBD-C cable
|| -|Volkswagen|Golf 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 harness box
- 1 long OBD-C cable
|| -|Volkswagen|Golf Alltrack 2015-19|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 harness box
- 1 long OBD-C cable
|| -|Volkswagen|Golf GTD 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 harness box
- 1 long OBD-C cable
|| -|Volkswagen|Golf GTE 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 harness box
- 1 long OBD-C cable
|| -|Volkswagen|Golf GTI 2015-21|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 harness box
- 1 long OBD-C cable
|| -|Volkswagen|Golf R 2015-19|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 harness box
- 1 long OBD-C cable
|| -|Volkswagen|Golf SportsVan 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 harness box
- 1 long OBD-C cable
|| -|Volkswagen|Grand California 2019-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|31 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 harness box
- 1 long OBD-C cable
|| -|Volkswagen|Jetta 2018-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 harness box
- 1 long OBD-C cable
|| -|Volkswagen|Jetta GLI 2021-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 harness box
- 1 long OBD-C cable
|| -|Volkswagen|Passat 2015-22[8](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 harness box
- 1 long OBD-C cable
|| -|Volkswagen|Passat Alltrack 2015-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 harness box
- 1 long OBD-C cable
|| -|Volkswagen|Passat GTE 2015-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 harness box
- 1 long OBD-C cable
|| -|Volkswagen|Polo 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 harness box
- 1 long OBD-C cable
[10](#footnotes)|| -|Volkswagen|Polo GTI 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 harness box
- 1 long OBD-C cable
[10](#footnotes)|| -|Volkswagen|T-Cross 2021|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 harness box
- 1 long OBD-C cable
[10](#footnotes)|| -|Volkswagen|T-Roc 2021|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 harness box
- 1 long OBD-C cable
[10](#footnotes)|| -|Volkswagen|Taos 2022|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 harness box
- 1 long OBD-C cable
|| -|Volkswagen|Teramont 2018-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 harness box
- 1 long OBD-C cable
|| -|Volkswagen|Teramont Cross Sport 2021-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 harness box
- 1 long OBD-C cable
|| -|Volkswagen|Teramont X 2021-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 harness box
- 1 long OBD-C cable
|| -|Volkswagen|Tiguan 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 harness box
- 1 long OBD-C cable
|| -|Volkswagen|Tiguan eHybrid 2021-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 harness box
- 1 long OBD-C cable
|| -|Volkswagen|Touran 2016-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,9](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 harness box
- 1 long OBD-C cable
|| +|Ford|Bronco Sport 2021-22|Co-Pilot360 Assist+|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Ford Q3 connector
- 1 RJ45 cable (7 ft)
- 1 angled mount (8 degrees)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Ford|Escape 2020-22|Co-Pilot360 Assist+|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Ford Q3 connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Ford|Explorer 2020-22|Co-Pilot360 Assist+|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Ford Q3 connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Ford|Kuga 2020-22|Adaptive Cruise Control with Lane Centering|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Ford Q3 connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Ford|Maverick 2022-23|Co-Pilot360 Assist|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Ford Q3 connector
- 1 RJ45 cable (7 ft)
- 1 angled mount (8 degrees)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Genesis|G70 2018-19|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai F connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Genesis|G70 2020|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai F connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Genesis|G80 2017|All|Stock|19 mph|37 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai J connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Genesis|G80 2018-19|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai H connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Genesis|G90 2017-18|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai C connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Genesis|GV60 (Advanced Trim) 2023[5](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai A connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Genesis|GV60 (Performance Trim) 2023[5](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai K connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Genesis|GV70 (2.5T Trim) 2022-23[5](#footnotes)|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai L connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Genesis|GV70 (3.5T Trim) 2022-23[5](#footnotes)|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai M connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Genesis|GV80 2023[5](#footnotes)|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai M connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|GMC|Acadia 2018[3](#footnotes)|Adaptive Cruise Control (ACC)|openpilot|0 mph|7 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 OBD-II connector
- 1 comma three
- 2 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|GMC|Sierra 1500 2020-21|Driver Alert Package II|openpilot available[1](#footnotes)|3 mph|6 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 GM connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Honda|Accord 2018-22|All|openpilot available[1](#footnotes)|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Honda Bosch A connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Honda|Accord Hybrid 2018-22|All|openpilot available[1](#footnotes)|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Honda Bosch A connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Honda|Civic 2016-18|Honda Sensing|openpilot|0 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Honda Nidec connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Honda|Civic 2019-21|All|openpilot available[1](#footnotes)|0 mph|2 mph[4](#footnotes)|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Honda Bosch A connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Honda|Civic 2022|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Honda Bosch B connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Honda|Civic Hatchback 2017-21|Honda Sensing|openpilot available[1](#footnotes)|0 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Honda Bosch A connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Honda|Civic Hatchback 2022|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Honda Bosch B connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Honda|CR-V 2015-16|Touring Trim|openpilot|25 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Honda Nidec connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Honda|CR-V 2017-22|Honda Sensing|openpilot available[1](#footnotes)|0 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Honda Bosch A connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Honda|CR-V Hybrid 2017-19|Honda Sensing|openpilot available[1](#footnotes)|0 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Honda Bosch A connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Honda|e 2020|All|openpilot available[1](#footnotes)|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Honda Bosch A connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Honda|Fit 2018-20|Honda Sensing|openpilot|25 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Honda Nidec connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Honda|Freed 2020|Honda Sensing|openpilot|25 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Honda Nidec connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Honda|HR-V 2019-22|Honda Sensing|openpilot|25 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Honda Nidec connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Honda|HR-V 2023|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Honda Bosch B connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Honda|Insight 2019-22|All|openpilot available[1](#footnotes)|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Honda Bosch A connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Honda|Inspire 2018|All|openpilot available[1](#footnotes)|0 mph|3 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Honda Bosch A connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Honda|Odyssey 2018-20|Honda Sensing|openpilot|25 mph|0 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Honda Nidec connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Honda|Passport 2019-23|All|openpilot|25 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Honda Nidec connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Honda|Pilot 2016-22|Honda Sensing|openpilot|25 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Honda Nidec connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Honda|Ridgeline 2017-23|Honda Sensing|openpilot|25 mph|12 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Honda Nidec connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Hyundai|Elantra 2017-19|Smart Cruise Control (SCC)|Stock|19 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai B connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Hyundai|Elantra 2021-23|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai K connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Hyundai|Elantra GT 2017-19|Smart Cruise Control (SCC)|Stock|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai E connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Hyundai|Elantra Hybrid 2021-23|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai K connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Hyundai|Genesis 2015-16|Smart Cruise Control (SCC)|Stock|19 mph|37 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai J connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Hyundai|i30 2017-19|Smart Cruise Control (SCC)|Stock|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai E connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Hyundai|Ioniq 5 (Southeast Asia only) 2022-23[5](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai Q connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Hyundai|Ioniq 5 (with HDA II) 2022-23[5](#footnotes)|Highway Driving Assist II|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai Q connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Hyundai|Ioniq 5 (without HDA II) 2022-23[5](#footnotes)|Highway Driving Assist|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai K connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Hyundai|Ioniq Electric 2019|Smart Cruise Control (SCC)|Stock|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai C connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Hyundai|Ioniq Electric 2020|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai H connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Hyundai|Ioniq Hybrid 2017-19|Smart Cruise Control (SCC)|Stock|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai C connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Hyundai|Ioniq Hybrid 2020-22|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai H connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Hyundai|Ioniq Plug-in Hybrid 2019|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai C connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Hyundai|Ioniq Plug-in Hybrid 2020-22|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai H connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Hyundai|Kona 2020|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai B connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Hyundai|Kona Electric 2018-21|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai G connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Hyundai|Kona Electric 2022|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai O connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Hyundai|Kona Hybrid 2020|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai I connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Hyundai|Palisade 2020-22|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai H connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Hyundai|Santa Cruz 2022-23[5](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai N connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Hyundai|Santa Fe 2019-20|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai D connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Hyundai|Santa Fe 2021-22|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai L connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Hyundai|Santa Fe Hybrid 2022-23|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai L connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Hyundai|Santa Fe Plug-in Hybrid 2022|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai L connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Hyundai|Sonata 2018-19|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai E connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Hyundai|Sonata 2020-23|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai A connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Hyundai|Sonata Hybrid 2020-22|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai A connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Hyundai|Tucson 2021|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai L connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Hyundai|Tucson 2022[5](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai N connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Hyundai|Tucson 2023[5](#footnotes)|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai N connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Hyundai|Tucson Diesel 2019|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai L connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Hyundai|Tucson Hybrid 2022-23[5](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai N connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Hyundai|Veloster 2019-20|Smart Cruise Control (SCC)|Stock|5 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai E connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Jeep|Grand Cherokee 2016-18|Adaptive Cruise Control (ACC)|Stock|0 mph|9 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 FCA connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Jeep|Grand Cherokee 2019-21|Adaptive Cruise Control (ACC)|Stock|0 mph|39 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 FCA connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Kia|Ceed 2019|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai E connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Kia|EV6 (Southeast Asia only) 2022-23[5](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai P connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Kia|EV6 (with HDA II) 2022-23[5](#footnotes)|Highway Driving Assist II|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai P connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Kia|EV6 (without HDA II) 2022-23[5](#footnotes)|Highway Driving Assist|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai L connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Kia|Forte 2019-21|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai G connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Kia|Forte 2023|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai E connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Kia|K5 2021-22|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai A connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Kia|K5 Hybrid 2020|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai A connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Kia|Niro EV 2019|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai H connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Kia|Niro EV 2020|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai F connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Kia|Niro EV 2021|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai C connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Kia|Niro EV 2022|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai H connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Kia|Niro EV 2023[5](#footnotes)|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai A connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Kia|Niro Hybrid 2021-22|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai F connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Kia|Niro Hybrid 2023[5](#footnotes)|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai A connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Kia|Niro Plug-in Hybrid 2018-19|All|openpilot available[1](#footnotes)|10 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai C connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Kia|Niro Plug-in Hybrid 2020|All|openpilot available[1](#footnotes)|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai D connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Kia|Optima 2017|Advanced Smart Cruise Control|Stock|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai B connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Kia|Optima 2019-20|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai G connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Kia|Seltos 2021|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai A connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Kia|Sorento 2018|Advanced Smart Cruise Control|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai C connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Kia|Sorento 2019|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai E connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Kia|Sorento 2021-23[5](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai K connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Kia|Sorento Plug-in Hybrid 2022-23[5](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai A connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Kia|Sportage 2023[5](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai N connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Kia|Sportage Hybrid 2023[5](#footnotes)|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai N connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Kia|Stinger 2018-20|Smart Cruise Control (SCC)|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai C connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Kia|Stinger 2022|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai K connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Kia|Telluride 2020-22|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Hyundai H connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Lexus|CT Hybrid 2017-18|Lexus Safety System+|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 RJ45 cable (7 ft)
- 1 Toyota connector
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Lexus|ES 2017-18|All|openpilot available[2](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 RJ45 cable (7 ft)
- 1 Toyota connector
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Lexus|ES 2019-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 RJ45 cable (7 ft)
- 1 Toyota connector
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Lexus|ES Hybrid 2017-18|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 RJ45 cable (7 ft)
- 1 Toyota connector
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Lexus|ES Hybrid 2019-23|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 RJ45 cable (7 ft)
- 1 Toyota connector
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Lexus|IS 2017-19|All|Stock|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 RJ45 cable (7 ft)
- 1 Toyota connector
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Lexus|NX 2018-19|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 RJ45 cable (7 ft)
- 1 Toyota connector
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Lexus|NX 2020-21|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 RJ45 cable (7 ft)
- 1 Toyota connector
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Lexus|NX Hybrid 2018-19|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 RJ45 cable (7 ft)
- 1 Toyota connector
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Lexus|NX Hybrid 2020-21|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 RJ45 cable (7 ft)
- 1 Toyota connector
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Lexus|RC 2018-20|All|Stock|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 RJ45 cable (7 ft)
- 1 Toyota connector
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Lexus|RX 2016|Lexus Safety System+|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 RJ45 cable (7 ft)
- 1 Toyota connector
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Lexus|RX 2017-19|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 RJ45 cable (7 ft)
- 1 Toyota connector
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Lexus|RX 2020-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 RJ45 cable (7 ft)
- 1 Toyota connector
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Lexus|RX Hybrid 2016|Lexus Safety System+|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 RJ45 cable (7 ft)
- 1 Toyota connector
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Lexus|RX Hybrid 2017-19|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 RJ45 cable (7 ft)
- 1 Toyota connector
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Lexus|RX Hybrid 2020-21|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 RJ45 cable (7 ft)
- 1 Toyota connector
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Lexus|UX Hybrid 2019-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 RJ45 cable (7 ft)
- 1 Toyota connector
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Lincoln|Aviator 2021|Co-Pilot360 Plus|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Ford Q3 connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|MAN|eTGE 2020-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,10](#footnotes)|0 mph|31 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 angled mount (8 degrees)
- 1 comma three
- 1 harness box
- 1 long OBD-C cable
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|MAN|TGE 2017-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,10](#footnotes)|0 mph|31 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 angled mount (8 degrees)
- 1 comma three
- 1 harness box
- 1 long OBD-C cable
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Mazda|CX-5 2022-23|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Mazda connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Mazda|CX-9 2021-23|All|Stock|0 mph|28 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 Mazda connector
- 1 RJ45 cable (7 ft)
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Nissan|Altima 2019-20|ProPILOT Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 Nissan B connector
- 1 RJ45 cable (7 ft)
- 1 USB-C coupler
- 1 comma three
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Nissan|Leaf 2018-23|ProPILOT Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 Nissan A connector
- 1 RJ45 cable (7 ft)
- 1 USB-C coupler
- 1 comma three
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Nissan|Rogue 2018-20|ProPILOT Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 Nissan A connector
- 1 RJ45 cable (7 ft)
- 1 USB-C coupler
- 1 comma three
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Nissan|X-Trail 2017|ProPILOT Assist|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 Nissan A connector
- 1 RJ45 cable (7 ft)
- 1 USB-C coupler
- 1 comma three
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Ram|1500 2019-23|Adaptive Cruise Control (ACC)|Stock|0 mph|32 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 RJ45 cable (7 ft)
- 1 Ram connector
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|SEAT|Ateca 2018|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,10](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 comma three
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|SEAT|Leon 2014-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,10](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 comma three
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Subaru|Ascent 2019-21|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 RJ45 cable (7 ft)
- 1 Subaru A connector
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Subaru|Crosstrek 2018-19|EyeSight Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 RJ45 cable (7 ft)
- 1 Subaru A connector
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Subaru|Crosstrek 2020-23|EyeSight Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 RJ45 cable (7 ft)
- 1 Subaru A connector
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Subaru|Forester 2019-21|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 RJ45 cable (7 ft)
- 1 Subaru A connector
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Subaru|Impreza 2017-19|EyeSight Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 RJ45 cable (7 ft)
- 1 Subaru A connector
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Subaru|Impreza 2020-22|EyeSight Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 RJ45 cable (7 ft)
- 1 Subaru A connector
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Subaru|Legacy 2020-22|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 RJ45 cable (7 ft)
- 1 Subaru B connector
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Subaru|Outback 2020-22|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 RJ45 cable (7 ft)
- 1 Subaru B connector
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Subaru|XV 2018-19|EyeSight Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-empty.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 RJ45 cable (7 ft)
- 1 Subaru A connector
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Subaru|XV 2020-21|EyeSight Driver Assistance|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 RJ45 cable (7 ft)
- 1 Subaru A connector
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Škoda|Fabia 2022-23[9](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,10](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 comma three
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
[11](#footnotes)|| +|Škoda|Kamiq 2021[7,9](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,10](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 comma three
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
[11](#footnotes)|| +|Škoda|Karoq 2019-21[9](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,10](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 comma three
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Škoda|Kodiaq 2017-23[9](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,10](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 comma three
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Škoda|Octavia 2015, 2018-19[9](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,10](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 comma three
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Škoda|Octavia RS 2016[9](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,10](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 comma three
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Škoda|Scala 2020[9](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,10](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 comma three
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
[11](#footnotes)|| +|Škoda|Superb 2015-22[9](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,10](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 comma three
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Toyota|Alphard 2019-20|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 RJ45 cable (7 ft)
- 1 Toyota connector
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Toyota|Alphard Hybrid 2021|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 RJ45 cable (7 ft)
- 1 Toyota connector
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Toyota|Avalon 2016|Toyota Safety Sense P|openpilot available[2](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 RJ45 cable (7 ft)
- 1 Toyota connector
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Toyota|Avalon 2017-18|All|openpilot available[2](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 RJ45 cable (7 ft)
- 1 Toyota connector
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Toyota|Avalon 2019-21|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 RJ45 cable (7 ft)
- 1 Toyota connector
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Toyota|Avalon 2022|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 RJ45 cable (7 ft)
- 1 Toyota connector
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Toyota|Avalon Hybrid 2019-21|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 RJ45 cable (7 ft)
- 1 Toyota connector
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Toyota|Avalon Hybrid 2022|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 RJ45 cable (7 ft)
- 1 Toyota connector
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Toyota|C-HR 2017-20|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 RJ45 cable (7 ft)
- 1 Toyota connector
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Toyota|C-HR 2021|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 RJ45 cable (7 ft)
- 1 Toyota connector
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Toyota|C-HR Hybrid 2017-20|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 RJ45 cable (7 ft)
- 1 Toyota connector
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Toyota|C-HR Hybrid 2021-22|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 RJ45 cable (7 ft)
- 1 Toyota connector
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Toyota|Camry 2018-20|All|Stock|0 mph[6](#footnotes)|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 RJ45 cable (7 ft)
- 1 Toyota connector
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Toyota|Camry 2021-23|All|openpilot|0 mph[6](#footnotes)|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 RJ45 cable (7 ft)
- 1 Toyota connector
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Toyota|Camry Hybrid 2018-20|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 RJ45 cable (7 ft)
- 1 Toyota connector
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Toyota|Camry Hybrid 2021-23|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 RJ45 cable (7 ft)
- 1 Toyota connector
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Toyota|Corolla 2017-19|All|openpilot available[2](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 RJ45 cable (7 ft)
- 1 Toyota connector
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Toyota|Corolla 2020-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 RJ45 cable (7 ft)
- 1 Toyota connector
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Toyota|Corolla Cross (Non-US only) 2020-23|All|openpilot|17 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 RJ45 cable (7 ft)
- 1 Toyota connector
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Toyota|Corolla Cross Hybrid (Non-US only) 2020-22|All|openpilot|17 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 RJ45 cable (7 ft)
- 1 Toyota connector
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Toyota|Corolla Hatchback 2019-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 RJ45 cable (7 ft)
- 1 Toyota connector
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Toyota|Corolla Hybrid 2020-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 RJ45 cable (7 ft)
- 1 Toyota connector
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Toyota|Corolla Hybrid (Non-US only) 2020-23|All|openpilot|17 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 RJ45 cable (7 ft)
- 1 Toyota connector
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Toyota|Highlander 2017-19|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 RJ45 cable (7 ft)
- 1 Toyota connector
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Toyota|Highlander 2020-23|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 RJ45 cable (7 ft)
- 1 Toyota connector
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Toyota|Highlander Hybrid 2017-19|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 RJ45 cable (7 ft)
- 1 Toyota connector
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Toyota|Highlander Hybrid 2020-23|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 RJ45 cable (7 ft)
- 1 Toyota connector
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Toyota|Mirai 2021|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 RJ45 cable (7 ft)
- 1 Toyota connector
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Toyota|Prius 2016|Toyota Safety Sense P|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 RJ45 cable (7 ft)
- 1 Toyota connector
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Toyota|Prius 2017-20|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 RJ45 cable (7 ft)
- 1 Toyota connector
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Toyota|Prius 2021-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 RJ45 cable (7 ft)
- 1 Toyota connector
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Toyota|Prius Prime 2017-20|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 RJ45 cable (7 ft)
- 1 Toyota connector
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Toyota|Prius Prime 2021-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 RJ45 cable (7 ft)
- 1 Toyota connector
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Toyota|Prius v 2017|Toyota Safety Sense P|openpilot available[2](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 RJ45 cable (7 ft)
- 1 Toyota connector
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Toyota|RAV4 2016|Toyota Safety Sense P|openpilot available[2](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 RJ45 cable (7 ft)
- 1 Toyota connector
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Toyota|RAV4 2017-18|All|openpilot available[2](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 RJ45 cable (7 ft)
- 1 Toyota connector
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Toyota|RAV4 2019-21|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 RJ45 cable (7 ft)
- 1 Toyota connector
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Toyota|RAV4 2022|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 RJ45 cable (7 ft)
- 1 Toyota connector
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Toyota|RAV4 Hybrid 2016|Toyota Safety Sense P|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 RJ45 cable (7 ft)
- 1 Toyota connector
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Toyota|RAV4 Hybrid 2017-18|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 RJ45 cable (7 ft)
- 1 Toyota connector
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Toyota|RAV4 Hybrid 2019-21|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 RJ45 cable (7 ft)
- 1 Toyota connector
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Toyota|RAV4 Hybrid 2022|All|Stock|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 RJ45 cable (7 ft)
- 1 Toyota connector
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Toyota|Sienna 2018-20|All|openpilot available[2](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 RJ45 cable (7 ft)
- 1 Toyota connector
- 1 comma power v2
- 1 comma three
- 1 harness box
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Arteon 2018-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,10](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 comma three
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Arteon eHybrid 2020-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,10](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 comma three
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Arteon R 2020-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,10](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 comma three
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Atlas 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,10](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 comma three
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Atlas Cross Sport 2021-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,10](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 comma three
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|California 2021|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,10](#footnotes)|0 mph|31 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 angled mount (8 degrees)
- 1 comma three
- 1 harness box
- 1 long OBD-C cable
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Caravelle 2020|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,10](#footnotes)|0 mph|31 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 angled mount (8 degrees)
- 1 comma three
- 1 harness box
- 1 long OBD-C cable
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|CC 2018-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,10](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 comma three
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Crafter 2017-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,10](#footnotes)|0 mph|31 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 angled mount (8 degrees)
- 1 comma three
- 1 harness box
- 1 long OBD-C cable
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|e-Crafter 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,10](#footnotes)|0 mph|31 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 angled mount (8 degrees)
- 1 comma three
- 1 harness box
- 1 long OBD-C cable
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|e-Golf 2014-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,10](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 comma three
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Golf 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,10](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 comma three
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Golf Alltrack 2015-19|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,10](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 comma three
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Golf GTD 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,10](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 comma three
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Golf GTE 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,10](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 comma three
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Golf GTI 2015-21|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,10](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 comma three
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Golf R 2015-19|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,10](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 comma three
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Golf SportsVan 2015-20|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,10](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 comma three
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Grand California 2019-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,10](#footnotes)|0 mph|31 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 angled mount (8 degrees)
- 1 comma three
- 1 harness box
- 1 long OBD-C cable
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Jetta 2018-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,10](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 comma three
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Jetta GLI 2021-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,10](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 comma three
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Passat 2015-22[8](#footnotes)|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,10](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 comma three
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Passat Alltrack 2015-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,10](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 comma three
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Passat GTE 2015-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,10](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 comma three
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Polo 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,10](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 comma three
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
[11](#footnotes)|| +|Volkswagen|Polo GTI 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,10](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 comma three
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
[11](#footnotes)|| +|Volkswagen|T-Cross 2021|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,10](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 comma three
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
[11](#footnotes)|| +|Volkswagen|T-Roc 2021|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,10](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 comma three
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
[11](#footnotes)|| +|Volkswagen|Taos 2022|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,10](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 comma three
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Teramont 2018-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,10](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 comma three
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Teramont Cross Sport 2021-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,10](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 comma three
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Teramont X 2021-22|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,10](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 comma three
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Tiguan 2018-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,10](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 comma three
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Tiguan eHybrid 2021-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,10](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 comma three
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| +|Volkswagen|Touran 2016-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[1,10](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|
View- 1 J533 connector
- 1 USB-C coupler
- 1 comma three
- 1 harness box
- 1 long OBD-C cable
- 1 mount
- 1 right angle OBD-C cable (1.5 ft)
Buy Here
|| - +### Footnotes 1Experimental openpilot longitudinal control is available behind a toggle; the toggle is only available in non-release branches such as `devel` or `master-ci`.
2By default, this car will use the stock Adaptive Cruise Control (ACC) for longitudinal control. If the Driver Support Unit (DSU) is disconnected, openpilot ACC will replace stock ACC. NOTE: disconnecting the DSU disables Automatic Emergency Braking (AEB).
3Requires a community built ASCM harness. NOTE: disconnecting the ASCM disables Automatic Emergency Braking (AEB).
@@ -270,8 +270,9 @@ A supported vehicle is one that just works when you install a comma three. All s 6openpilot operates above 28mph for Camry 4CYL L, 4CYL LE and 4CYL SE which don't have Full-Speed Range Dynamic Radar Cruise Control.
7Not including the China market Kamiq, which is based on the (currently) unsupported PQ34 platform.
8Refers only to the MQB-based European B8 Passat, not the NMS Passat in the USA/China/Mideast markets.
-9Only available for vehicles using a gateway (J533) harness. At this time, vehicles using a camera harness are limited to using stock ACC.
-10Model-years 2022 and beyond may have a combined CAN gateway and BCM, which is supported by openpilot in software, but doesn't yet have a harness available from the comma store.
+9Some Škoda vehicles are equipped with heated windshields, which are known to block GPS signal needed for some comma three functionality.
+10Only available for vehicles using a gateway (J533) harness. At this time, vehicles using a camera harness are limited to using stock ACC.
+11Model-years 2022 and beyond may have a combined CAN gateway and BCM, which is supported by openpilot in software, but doesn't yet have a harness available from the comma store.
## Community Maintained Cars Although they're not upstream, the community has openpilot running on other makes and models. See the 'Community Supported Models' section of each make [on our wiki](https://wiki.comma.ai/). diff --git a/launch_chffrplus.sh b/launch_chffrplus.sh index 4c2e9aeef..37fe79730 100755 --- a/launch_chffrplus.sh +++ b/launch_chffrplus.sh @@ -11,7 +11,7 @@ DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )" function two_init { # convert to no ir ctrl param if [ -f /data/media/0/no_ir_ctrl ]; then - echo -n 1 > /data/params/d/dp_no_ir_ctrl + echo -n 1 > /data/params/d/dp_device_no_ir_ctrl fi mount -o remount,rw /system @@ -232,11 +232,6 @@ function two_init { } function agnos_init { - # wait longer for weston to come up - if [ -f "$BASEDIR/prebuilt" ]; then - sleep 3 - fi - # TODO: move this to agnos sudo rm -f /data/etc/NetworkManager/system-connections/*.nmmeta diff --git a/opendbc/bmw_e9x_e8x.dbc b/opendbc/bmw_e9x_e8x.dbc index 0bcd876a0..7ec95c0b1 100644 --- a/opendbc/bmw_e9x_e8x.dbc +++ b/opendbc/bmw_e9x_e8x.dbc @@ -42,9 +42,9 @@ BO_ 170 AccPedal: 8 DME SG_ CruisePedalInactive : 55|1@0+ (1,0) [0|1] "" XXX SG_ ThrottlelPressed : 50|1@0+ (1,0) [0|1] "" XXX SG_ AcceleratorPedalPressed : 52|1@0+ (1,0) [0|7] "" XXX - SG_ AcceleratorPedalPercentage : 16|16@1+ (0.01,0) [0|100] "" XXX + SG_ AcceleratorPedalPercentage : 16|16@1+ (0.04,0) [0|100] "" XXX SG_ Counter_170 : 8|4@1+ (1,0) [0|15] "" XXX - SG_ EngineSpeed : 32|16@1- (0.25,0) [0|65535] "U/min" XXX + SG_ EngineSpeed : 32|16@1+ (0.25,0) [0|8000] "rpm" XXX SG_ Checksum_170 : 0|8@1- (1,0) [0|65535] "" XXX BO_ 404 CruiseControl: 4 SZL @@ -98,10 +98,10 @@ BO_ 201 SteeringWheelAngle_DSC: 8 SZL SG_ SteeringPosition : 0|16@1- (0.0428316886,0) [-600|600] "deg" DSC BO_ 206 WheelSpeeds: 8 DSC - SG_ Wheel1 : 0|16@1- (0.0643699,0) [0|255] "kph" XXX - SG_ Wheel2 : 16|16@1- (0.0643699,0) [0|255] "kph" XXX - SG_ Wheel4 : 48|16@1- (0.0643699,0) [0|255] "kph" XXX - SG_ Wheel3 : 32|16@1- (0.0643699,0) [0|255] "kph" XXX + SG_ Wheel_FL : 0|16@1- (0.0625,0) [0|255] "kph" XXX + SG_ Wheel_FR : 16|16@1- (0.0625,0) [0|255] "kph" XXX + SG_ Wheel_RL : 32|16@1- (0.0625,0) [0|255] "kph" XXX + SG_ Wheel_RR : 48|16@1- (0.0625,0) [0|255] "kph" XXX BO_ 884 WheelToleranceAdjustment: 8 DSC @@ -141,6 +141,10 @@ BO_ 182 DynamicCruiseControlTorqueDemand: 8 DSC BO_ 186 TransmissionData: 8 EGS SG_ Counter_186 : 48|4@1+ (1,0) [0|14] "" XXX + SG_ Shifting : 4|1@1+ (1,0) [0|15] "" XXX + SG_ OutputShaftSpeed : 24|16@1- (0.125,0) [0|255] "rpm" XXX + SG_ GearRatio : 8|8@1+ (0.05,0) [0|255] "" XXX + SG_ GearTar : 0|4@1+ (1,-4) [0|255] "" XXX SG_ Checksum_186 : 40|8@1+ (1,0) [0|15] "" XXX BO_ 191 RequestedWheelTorqueDriveTrain: 8 LDM @@ -198,6 +202,7 @@ BO_ 408 GearSelectorSwitch: 8 GWS BO_ 422 DistanceRoute: 8 DSC BO_ 436 InstrumentClusterStatus_KOMBI: 8 CCC + SG_ HandbrakeActive : 41|1@1+ (1,0) [0|3] "" XXX BO_ 464 EngineData: 8 DME SG_ RPM_IDLG_TAR : 56|8@1+ (5,0) [0|1270] "1/min" XXX @@ -206,7 +211,7 @@ BO_ 464 EngineData: 8 DME SG_ AIP_ENG : 24|8@1+ (2,598) [600|1106] "hPa" XXX SG_ ST_SW_WAUP : 22|2@1+ (1,0) [0|0] "" XXX SG_ ST_ENG_RUN : 20|2@1+ (1,0) [0|0] "" XXX - SG_ Counter_464 : 16|4@0+ (1,0) [0|14] "" XXX + SG_ Counter_464 : 16|4@1+ (1,0) [0|14] "" XXX SG_ TEMP_EOI : 8|8@1+ (1,-48) [0|0] "C" XXX SG_ TEMP_ENG : 0|8@1+ (1,-48) [0|0] "C" XXX @@ -221,7 +226,7 @@ BO_ 200 SteeringWheelAngle_slow: 6 SZL BO_ 466 TransmissionDataDisplay: 8 EGS SG_ ShiftLeverMode : 32|2@1+ (1,0) [0|3] "" XXX - SG_ GearRelated_TBD : 12|4@1+ (1,0) [0|15] "" XXX + SG_ GearAct : 12|4@1+ (1,-4) [0|15] "" XXX SG_ Counter_466 : 28|4@1+ (1,0) [0|14] "" XXX SG_ ShiftLeverPosition : 0|4@1+ (1,0) [0|8] "" XXX SG_ xFF : 40|8@1+ (1,0) [0|255] "" XXX @@ -720,6 +725,7 @@ BO_ 843 Seat_back_lock_status_FA: 8 SM_FA BO_ 845 Status_seat_back_lock_BF: 8 SM_BF BO_ 847 Status_contact_handbrake: 8 JBBF + SG_ Handbrake_push : 0|2@1+ (1,0) [0|3] "" XXX BO_ 858 Appointment_Condition_Based_Service: 8 CCC @@ -853,7 +859,6 @@ BO_ 996 Configuration_rear_view_camera_CKM: 8 CCC CM_ SG_ 170 ThrottlelPressed "Active when accelerator pedal pressed or cruise control: drives"; CM_ SG_ 170 AcceleratorPedalPressed "Active only when driver actually presses the pedal"; CM_ SG_ 170 AcceleratorPedalPercentage "ToDo Factor to be adjusted"; - CM_ SG_ 404 plus1mph_request "Appears when +1mph/kph stalk is depressed"; CM_ SG_ 404 minus1mph_request "Appears when -1mph/kph stalk is depressed"; CM_ SG_ 404 Cancel_request_up_stalk "Appears when cancel stalk (up) is depressed"; @@ -871,7 +876,8 @@ CM_ SG_ 169 ALIV_TORQ_2_DME "Counter TORQ_2"; CM_ SG_ 182 TORQ_TAR_DSC "torque target DSC"; CM_ SG_ 403 CruiseControlSetpointSpeed "Speed target - unit depends on locale"; - +CM_ SG_ 186 GearTar "Values corresponds to forward gears. TBD Add enums for park, reverse"; +CM_ SG_ 466 GearAct "TransmissionDataDisplay"; CM_ SG_ 414 DSC_full_off "0x4 enabling, 0xA enabled. TBD"; CM_ SG_ 416 YawRate "Lateral Acceleration"; diff --git a/opendbc/can/libdbc.so b/opendbc/can/libdbc.so index b07356b8f..537738aa8 100755 Binary files a/opendbc/can/libdbc.so and b/opendbc/can/libdbc.so differ diff --git a/opendbc/can/parser_pyx.so b/opendbc/can/parser_pyx.so index b144a295a..20c7731ec 100755 Binary files a/opendbc/can/parser_pyx.so and b/opendbc/can/parser_pyx.so differ diff --git a/opendbc/generator/honda/honda_civic_ex_2022_can.dbc b/opendbc/generator/honda/honda_civic_ex_2022_can.dbc index c0f4cfca1..97aee2642 100644 --- a/opendbc/generator/honda/honda_civic_ex_2022_can.dbc +++ b/opendbc/generator/honda/honda_civic_ex_2022_can.dbc @@ -12,6 +12,12 @@ BO_ 401 GEARBOX: 8 PCM SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" EON SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" EON +BO_ 419 GEARBOX_ALT: 8 PCM + SG_ GEAR : 7|8@0+ (1,0) [0|255] "" EON + SG_ GEAR_SHIFTER : 29|6@0+ (1,0) [0|63] "" EON + SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" EON + SG_ CHECKSUM : 59|4@0+ (1,0) [0|3] "" EON + BO_ 432 STANDSTILL: 7 VSA SG_ WHEELS_MOVING : 12|1@0+ (1,0) [0|1] "" EON SG_ BRAKE_ERROR_1 : 11|1@0+ (1,0) [0|1] "" EON @@ -87,3 +93,4 @@ CM_ SG_ 456 STANDSTILL "set to 1 when camera requests -4.0 m/s^2"; VAL_ 401 GEAR_SHIFTER 32 "L" 16 "S" 8 "D" 4 "N" 2 "R" 1 "P"; VAL_ 401 GEAR 7 "L" 10 "S" 4 "D" 3 "N" 2 "R" 1 "P"; +VAL_ 419 GEAR_SHIFTER 32 "D" 16 "N" 8 "R" 4 "P" 0 "B" ; diff --git a/opendbc/generator/nissan/_nissan_common.dbc b/opendbc/generator/nissan/_nissan_common.dbc new file mode 100644 index 000000000..6300ea942 --- /dev/null +++ b/opendbc/generator/nissan/_nissan_common.dbc @@ -0,0 +1,113 @@ +BO_ 2 STEER_ANGLE_SENSOR: 5 XXX + SG_ STEER_ANGLE_RATE : 16|8@1+ (1,0) [0|255] "" XXX + SG_ SET_ME_X07 : 24|8@1+ (1,0) [0|255] "" XXX + SG_ STEER_ANGLE : 0|16@1- (-0.1,0) [0|65535] "" XXX + SG_ COUNTER : 32|4@1+ (1,0) [0|15] "" XXX + +BO_ 361 LKAS: 8 XXX + SG_ MAX_TORQUE : 39|8@0+ (0.01,0) [0|255] "Nm" XXX + SG_ SET_0x80 : 47|8@0+ (1,0) [0|255] "" XXX + SG_ LKA_ACTIVE : 52|1@0+ (1,0) [0|15] "" XXX + SG_ SET_0x80_2 : 31|8@0+ (1,0) [0|255] "" XXX + SG_ COUNTER : 51|4@0+ (1,0) [0|15] "" XXX + SG_ DESIRED_ANGLE : 7|18@0+ (-0.01,1310) [-1311.43|1310] "" XXX + SG_ CHECKSUM : 63|8@0+ (1,0) [0|255] "" XXX + +BO_ 389 STEER_TORQUE_SENSOR: 8 XXX + SG_ LKAS_ACTIVE : 37|1@0+ (1,0) [0|3] "" XXX + SG_ STEER_TORQUE_LKAS : 47|8@0+ (1,0) [0|255] "" XXX + SG_ STEER_ANGLE : 23|18@0+ (-0.01,1310) [0|262143] "" XXX + SG_ STEER_TORQUE_DRIVER : 7|12@0+ (-0.01,20.47) [0|4095] "Nm" XXX + SG_ COUNTER : 51|4@0+ (1,0) [0|15] "" XXX + SG_ CHECKSUM : 63|8@0+ (1,0) [0|127] "" XXX + +BO_ 645 WHEEL_SPEEDS_REAR: 8 XXX + SG_ WHEEL_SPEED_RR : 7|16@0+ (0.005,0) [0|65535] "KPH" XXX + SG_ WHEEL_SPEED_RL : 23|16@0+ (0.005,0) [0|65535] "KPH" XXX + +BO_ 689 PROPILOT_HUD: 8 XXX + SG_ LARGE_WARNING_FLASHING : 9|1@0+ (1,0) [0|1] "" XXX + SG_ SIDE_RADAR_ERROR_FLASHING1 : 10|1@0+ (1,0) [0|1] "" XXX + SG_ SIDE_RADAR_ERROR_FLASHING2 : 11|1@0+ (1,0) [0|1] "" XXX + SG_ RIGHT_LANE_YELLOW_FLASH : 12|1@0+ (1,0) [0|1] "" XXX + SG_ LEFT_LANE_YELLOW_FLASH : 13|1@0+ (1,0) [0|1] "" XXX + SG_ LEAD_CAR : 14|1@0+ (1,0) [0|1] "" XXX + SG_ LEAD_CAR_ERROR : 15|1@0+ (1,0) [0|1] "" XXX + SG_ FRONT_RADAR_ERROR : 16|1@0+ (1,0) [0|1] "" XXX + SG_ FRONT_RADAR_ERROR_FLASHING : 17|1@0+ (1,0) [0|1] "" XXX + SG_ RIGHT_LANE_GREEN : 24|1@0+ (1,0) [0|1] "" XXX + SG_ LEFT_LANE_GREEN : 25|1@0+ (1,0) [0|1] "" XXX + SG_ SIDE_RADAR_ERROR_FLASHING3 : 27|1@0+ (1,0) [0|1] "" XXX + SG_ LKAS_ERROR_FLASHING : 29|1@0+ (1,0) [0|1] "" XXX + SG_ SAFETY_SHIELD_ACTIVE : 44|1@0+ (1,0) [0|1] "" XXX + SG_ LARGE_STEERING_WHEEL_ICON : 61|2@0+ (1,0) [0|3] "" XXX + SG_ RIGHT_LANE_GREEN_FLASH : 62|1@0+ (1,0) [0|1] "" XXX + SG_ LEFT_LANE_GREEN_FLASH : 63|1@0+ (1,0) [0|1] "" XXX + SG_ FOLLOW_DISTANCE : 3|2@0+ (1,0) [0|3] "" XXX + SG_ AUDIBLE_TONE : 47|3@0+ (1,0) [0|8] "" XXX + SG_ SPEED_SET_ICON : 7|2@0+ (1,0) [0|3] "" XXX + SG_ SMALL_STEERING_WHEEL_ICON : 42|3@0+ (1,0) [0|7] "" XXX + SG_ SET_SPEED : 39|8@0+ (1,0) [0|255] "" XXX + SG_ unknown02 : 1|2@0+ (1,0) [0|3] "" XXX + SG_ unknown05 : 5|2@0+ (1,0) [0|3] "" XXX + SG_ unknown08 : 8|7@0+ (1,0) [0|63] "" XXX + SG_ unknown26 : 26|1@0+ (1,0) [0|1] "" XXX + SG_ unknown28 : 28|1@0+ (1,0) [0|1] "" XXX + SG_ unknown31 : 31|2@0+ (1,0) [0|3] "" XXX + SG_ unknown43 : 43|1@0+ (1,0) [0|1] "" XXX + SG_ unknown55 : 55|8@0+ (1,0) [0|63] "" XXX + SG_ unknown59 : 59|4@0+ (1,0) [0|15] "" XXX + +BO_ 783 CRUISE_STATE: 3 XXX + SG_ CRUISE_ENABLED : 3|1@0+ (1,0) [0|1] "" XXX + +BO_ 1228 PROPILOT_HUD_INFO_MSG: 8 XXX + SG_ NA_HIGH_ACCEL_TEMP : 0|1@0+ (1,0) [0|1] "" XXX + SG_ SIDE_RADAR_NA_HIGH_CABIN_TEMP : 8|1@0+ (1,0) [0|1] "" XXX + SG_ SIDE_RADAR_MALFUNCTION : 11|1@0+ (1,0) [0|1] "" XXX + SG_ LKAS_MALFUNCTION : 12|1@0+ (1,0) [0|1] "" XXX + SG_ FRONT_RADAR_MALFUNCTION : 13|1@0+ (1,0) [0|1] "" XXX + SG_ SIDE_RADAR_NA_CLEAN_REAR_CAMERA : 14|1@0+ (1,0) [0|1] "" XXX + SG_ NA_POOR_ROAD_CONDITIONS : 16|1@0+ (1,0) [0|1] "" XXX + SG_ CURRENTLY_UNAVAILABLE : 17|1@0+ (1,0) [0|1] "" XXX + SG_ SAFETY_SHIELD_OFF : 18|1@0+ (1,0) [0|1] "" XXX + SG_ FRONT_COLLISION_NA_FRONT_RADAR_OBSTRUCTION : 20|1@0+ (1,0) [0|1] "" XXX + SG_ PEDAL_MISSAPPLICATION_SYSTEM_ACTIVATED : 24|1@0+ (1,0) [0|1] "" XXX + SG_ SIDE_IMPACT_NA_RADAR_OBSTRUCTION : 25|1@0+ (1,0) [0|1] "" XXX + SG_ WARNING_DO_NOT_ENTER : 33|1@0+ (1,0) [0|1] "" XXX + SG_ SIDE_IMPACT_SYSTEM_OFF : 34|1@0+ (1,0) [0|1] "" XXX + SG_ SIDE_IMPACT_MALFUNCTION : 35|1@0+ (1,0) [0|1] "" XXX + SG_ FRONT_COLLISION_MALFUNCTION : 36|1@0+ (1,0) [0|1] "" XXX + SG_ SIDE_RADAR_MALFUNCTION2 : 37|1@0+ (1,0) [0|1] "" XXX + SG_ LKAS_MALFUNCTION2 : 38|1@0+ (1,0) [0|1] "" XXX + SG_ FRONT_RADAR_MALFUNCTION2 : 39|1@0+ (1,0) [0|1] "" XXX + SG_ PROPILOT_NA_MSGS : 42|3@0+ (1,0) [0|7] "" XXX + SG_ BOTTOM_MSG : 45|3@0+ (1,0) [0|7] "" XXX + SG_ HANDS_ON_WHEEL_WARNING : 47|1@0+ (1,0) [0|1] "" XXX + SG_ WARNING_STEP_ON_BRAKE_NOW : 51|1@0+ (1,0) [0|1] "" XXX + SG_ PROPILOT_NA_FRONT_CAMERA_OBSTRUCTED : 52|1@0+ (1,0) [0|1] "" XXX + SG_ PROPILOT_NA_HIGH_CABIN_TEMP : 53|1@0+ (1,0) [0|1] "" XXX + SG_ WARNING_PROPILOT_MALFUNCTION : 54|1@0+ (1,0) [0|3] "" XXX + SG_ ACC_UNAVAILABLE_HIGH_CABIN_TEMP : 62|1@0+ (1,0) [0|1] "" XXX + SG_ ACC_NA_FRONT_CAMERA_IMPARED : 63|1@0+ (1,0) [0|1] "" XXX + SG_ unknown07 : 7|7@0+ (1,0) [0|127] "" XXX + SG_ unknown10 : 10|2@0+ (1,0) [0|3] "" XXX + SG_ unknown15 : 15|1@0+ (1,0) [0|1] "" XXX + SG_ unknown23 : 23|3@0+ (1,0) [0|7] "" XXX + SG_ unknown19 : 19|1@0+ (1,0) [0|1] "" XXX + SG_ unknown31 : 31|6@0+ (1,0) [0|63] "" XXX + SG_ unknown32 : 32|1@0+ (1,0) [0|1] "" XXX + SG_ unknown46 : 46|1@0+ (1,0) [0|1] "" XXX + SG_ unknown50 : 50|3@0+ (1,0) [0|7] "" XXX + SG_ unknown55 : 55|1@0+ (1,0) [0|1] "" XXX + SG_ unknown61 : 61|6@0+ (1,0) [0|63] "" XXX + +BO_ 1227 LKAS_SETTINGS: 8 XXX + SG_ LKAS_ENABLED : 51|1@0+ (1,0) [0|1] "" XXX + +VAL_ 1228 PROPILOT_NA_MSGS 0 "NO_MSG" 1 "NA_FRONT_CAMERA_IMPARED" 2 "STEERING_ASSIST_ON_STANDBY" 3 "NA_PARKING_ASSIST_ENABLED" 4 "STEER_ASSIST_CURRENTLY_NA" 5 "NA_BAD_WEATHER" 6 "NA_PARK_BRAKE_ON" 7 "NA_SEATBELT_NOT_FASTENED" ; +VAL_ 1228 BOTTOM_MSG 0 "OK_STEER_ASSIST_SETTINGS" 1 "NO_MSG" 2 "PRESS_SET_TO_SET_SPEED" 3 "PRESS_RES_SET_TO_CHANGE_SPEED" 4 "PRESS_RES_TO_RESTART" 5 "NO_MSG" 6 "CRUISE_NOT_AVAIL" 7 "NO_MSG" ; +VAL_ 689 FOLLOW_DISTANCE 0 "NO_FOLLOW_DISTANCE" 1 "FOLLOW_DISTANCE_1" 2 "FOLLOW_DISTANCE_2" 3 "FOLLOW_DISANCE_3" ; +VAL_ 689 AUDIBLE_TONE 0 "NO_TONE" 1 "CONT" 2 "FAST_BEEP_CONT" 3 "TRIPLE_FAST_BEEP_CONT" 4 "SLOW_BEEP_CONT" 5 "QUAD_SLOW_BEEP_CONT" 6 "SINGLE_BEEP_ONCE" 7 "DOUBLE_BEEP_ONCE" ; +VAL_ 689 SMALL_STEERING_WHEEL_ICON 0 "NO_ICON" 1 "GRAY_ICON" 2 "GRAY_ICON_FLASHING" 3 "GREEN_ICON" 4 "GREEN_ICON_FLASHING" 5 "RED_ICON" 6 "RED_ICON_FLASHING" 7 "YELLOW_ICON" ; +VAL_ 689 LARGE_STEERING_WHEEL_ICON 0 "NO_STEERINGWHEEL" 1 "GRAY_STEERINGWHEEL" 2 "GREEN_STEERINGWHEEL" 3 "GREEN_STEERINGWHEEL_FLASHING" ; diff --git a/opendbc/generator/nissan/nissan_leaf_2018.dbc b/opendbc/generator/nissan/nissan_leaf_2018.dbc new file mode 100644 index 000000000..3376c3784 --- /dev/null +++ b/opendbc/generator/nissan/nissan_leaf_2018.dbc @@ -0,0 +1,62 @@ +CM_ "IMPORT _nissan_common.dbc"; + +BO_ 42 SEATBELT: 8 XXX + SG_ SEATBELT_DRIVER_LATCHED : 27|1@1+ (1,0) [0|3] "" XXX + SG_ SEATBELT_DRIVER_UNLATCHED : 26|1@0+ (1,0) [0|1] "" XXX + SG_ unknown2 : 31|4@0+ (1,0) [0|15] "" XXX + SG_ unknown3 : 24|2@1+ (1,0) [0|3] "" XXX + SG_ unknown1 : 7|24@0+ (1,0) [0|16777215] "" XXX + SG_ unknown4 : 39|16@0+ (1,0) [0|65535] "" XXX + +BO_ 460 BRAKE_PEDAL: 8 XXX + SG_ BRAKE_PEDAL : 7|8@0+ (1,0) [0|256] "" XXX + +BO_ 569 CRUISE_THROTTLE: 8 XXX + SG_ GAS_PEDAL_INVERTED : 15|8@0+ (1,0) [0|255] "" XXX + SG_ GAS_PEDAL : 7|8@0+ (1,0) [0|255] "" XXX + SG_ CRUISE_AVAILABLE : 17|1@0+ (1,0) [0|1] "" XXX + SG_ unsure1 : 23|6@0+ (1,0) [0|63] "" XXX + SG_ unsure2 : 16|1@0+ (1,0) [0|1] "" XXX + SG_ unsure3 : 31|2@0+ (1,0) [0|3] "" XXX + SG_ NO_BUTTON_PRESSED : 29|1@0+ (1,0) [0|1] "" XXX + SG_ RES_BUTTON : 28|1@0+ (1,0) [0|1] "" XXX + SG_ SET_BUTTON : 27|1@0+ (1,0) [0|1] "" XXX + SG_ FOLLOW_DISTANCE_BUTTON : 26|1@0+ (1,0) [0|1] "" XXX + SG_ CANCEL_BUTTON : 25|1@0+ (1,0) [0|1] "" XXX + SG_ PROPILOT_BUTTON : 24|1@0+ (1,0) [0|1] "" XXX + SG_ USER_BRAKE_PRESSED : 37|1@0+ (1,0) [0|1] "" XXX + SG_ COUNTER : 32|2@1+ (1,0) [0|3] "" XXX + SG_ unsure5 : 47|8@0+ (1,0) [0|255] "" XXX + SG_ unsure6 : 55|8@0+ (1,0) [0|255] "" XXX + SG_ unsure7 : 63|8@0+ (1,0) [0|255] "" XXX + +BO_ 640 CANCEL_MSG: 8 XXX + SG_ CANCEL_SEATBELT : 1|1@0+ (1,0) [0|1] "" XXX + SG_ NEW_SIGNAL_1 : 7|6@0+ (1,0) [0|63] "" XXX + SG_ NEW_SIGNAL_2 : 0|1@0+ (1,0) [0|1] "" XXX + SG_ NEW_SIGNAL_3 : 15|56@0+ (1,0) [0|72057594037927940] "" XXX + +BO_ 644 WHEEL_SPEEDS_FRONT: 8 XXX + SG_ WHEEL_SPEED_FR : 7|16@0+ (0.005,0) [0|65535] "KPH" XXX + SG_ WHEEL_SPEED_FL : 23|16@0+ (0.005,0) [0|65535] "KPH" XXX + +BO_ 852 ESP: 8 XXX + SG_ ESP_DISABLED : 38|1@0+ (1,0) [0|1] "" XXX + +BO_ 853 HUD_SETTINGS: 8 XXX + SG_ SPEED_MPH : 37|1@0+ (1,0) [0|1] "" XXX + +BO_ 856 LIGHTS: 8 XXX + SG_ LEFT_BLINKER : 17|1@0+ (1,0) [0|1] "" XXX + SG_ RIGHT_BLINKER : 18|1@0+ (1,0) [0|1] "" XXX + +BO_ 1057 GEARBOX: 3 XXX + SG_ GEAR_SHIFTER : 5|3@0+ (1,0) [0|255] "" XXX + +BO_ 1549 DOORS_LIGHTS: 8 XXX + SG_ DOOR_OPEN_FL : 3|1@0+ (1,0) [0|1] "" XXX + SG_ DOOR_OPEN_FR : 4|1@0+ (1,0) [0|1] "" XXX + SG_ DOOR_OPEN_RL : 5|1@0+ (1,0) [0|1] "" XXX + SG_ DOOR_OPEN_RR : 6|1@0+ (1,0) [0|1] "" XXX + +VAL_ 1057 GEAR_SHIFTER 7 "B" 4 "D" 3 "N" 2 "R" 1 "P" ; diff --git a/opendbc/generator/nissan/nissan_x_trail_2017.dbc b/opendbc/generator/nissan/nissan_x_trail_2017.dbc new file mode 100644 index 000000000..46e6f9c99 --- /dev/null +++ b/opendbc/generator/nissan/nissan_x_trail_2017.dbc @@ -0,0 +1,69 @@ +CM_ "IMPORT _nissan_common.dbc"; + +BO_ 348 GAS_PEDAL: 8 XXX + SG_ GAS_PEDAL_RAW : 26|11@0+ (1,0) [0|2047] "" XXX + SG_ GAS_PEDAL : 47|10@0+ (1,0) [0|1023] "" XXX + +BO_ 438 PRO_PILOT: 8 XXX + SG_ COUNTER : 55|4@0+ (1,0) [0|255] "" XXX + SG_ SET_ME_X03 : 33|2@0+ (1,0) [0|15] "" XXX + SG_ CRUISE_ACTIVATED : 38|1@0+ (1,0) [0|3] "" XXX + SG_ CRUISE_ON : 36|1@0+ (1,0) [0|255] "" XXX + SG_ STEER_STATUS : 51|1@0+ (1,0) [0|3] "" XXX + +BO_ 523 CRUISE_THROTTLE: 6 XXX + SG_ PROPILOT_BUTTON : 8|1@0+ (1,0) [0|1] "" XXX + SG_ CANCEL_BUTTON : 9|1@0+ (1,0) [0|1] "" XXX + SG_ GAS_PEDAL_INVERTED : 37|10@0+ (1,0) [0|1023] "" XXX + SG_ SET_BUTTON : 11|1@0+ (1,0) [0|1] "" XXX + SG_ RES_BUTTON : 12|1@0+ (1,0) [0|1] "" XXX + SG_ FOLLOW_DISTANCE_BUTTON : 10|1@0+ (1,0) [0|1] "" XXX + SG_ NO_BUTTON_PRESSED : 13|1@0+ (1,0) [0|1] "" XXX + SG_ GAS_PEDAL : 31|10@0+ (1,0) [0|255] "" XXX + SG_ USER_BRAKE_PRESSED : 21|1@0+ (1,0) [0|1] "" XXX + SG_ USER_BRAKE_PRESSED_INVERTED : 22|1@0+ (1,0) [0|3] "" XXX + SG_ NEW_SIGNAL_2 : 23|1@0+ (1,0) [0|1] "" XXX + SG_ GAS_PRESSED_INVERTED : 20|1@0+ (1,0) [0|255] "" XXX + SG_ COUNTER : 17|2@0+ (1,0) [0|3] "" XXX + SG_ unsure1 : 7|10@0+ (1,0) [0|1023] "" XXX + SG_ unsure2 : 43|4@0+ (1,0) [0|1] "" XXX + SG_ unsure3 : 19|2@0+ (1,0) [0|3] "" XXX + +BO_ 665 ESP: 8 XXX + SG_ ESP_DISABLED : 24|1@0+ (1,0) [0|1] "" XXX + +BO_ 666 WHEEL_SPEEDS_FRONT: 8 XXX + SG_ WHEEL_SPEED_FR : 7|16@0+ (0.005,0) [0|65535] "KPH" XXX + SG_ WHEEL_SPEED_FL : 23|16@0+ (0.005,0) [0|65535] "KPH" XXX + +BO_ 768 STEER_TORQUE_SENSOR2: 2 XXX + SG_ STEERING_TORQUE : 6|7@0+ (1,0) [0|127] "" XXX + SG_ STEERING_PRESSED : 15|1@0+ (-1,1) [0|7] "" XXX + +BO_ 1055 GEARBOX: 2 XXX + SG_ SPORTS_MODE : 13|1@0+ (1,0) [0|1] "" XXX + SG_ GEAR_SHIFTER : 5|3@0+ (1,0) [0|255] "" XXX + +BO_ 1107 LIGHTS: 8 XXX + SG_ RIGHT_BLINKER : 12|1@0+ (1,0) [0|1] "" XXX + SG_ LEFT_BLINKER : 11|1@0+ (1,0) [0|1] "" XXX + SG_ HEADLIGHTS : 5|1@0+ (1,0) [0|1] "" XXX + +BO_ 1108 DOORS_LIGHTS: 8 XXX + SG_ DOOR_CLOSED_RR : 40|1@0+ (1,0) [0|1] "" XXX + SG_ DOOR_OPEN_RR : 41|1@0+ (1,0) [0|1] "" XXX + SG_ DOOR_CLOSED_RL : 42|1@0+ (1,0) [0|1] "" XXX + SG_ DOOR_OPEN_RL : 43|1@0+ (1,0) [0|1] "" XXX + SG_ DOOR_CLOSED_FL : 44|1@0+ (1,0) [0|1] "" XXX + SG_ DOOR_OPEN_FL : 45|1@0+ (1,0) [0|1] "" XXX + SG_ DOOR_CLOSED_FR : 46|1@0+ (1,0) [0|3] "" XXX + SG_ DOOR_OPEN_FR : 47|1@0+ (1,0) [0|3] "" XXX + SG_ BOOT_OPEN : 55|1@0+ (1,0) [0|1] "" XXX + SG_ BRAKE_LIGHT : 54|1@0+ (1,0) [0|1] "" XXX + SG_ USER_BRAKE_PRESSED : 23|1@0+ (1,0) [0|1] "" XXX + +BO_ 1273 HUD: 7 XXX + SG_ SEATBELT_DRIVER_LATCHED : 25|1@0+ (1,0) [0|1] "" XXX + SG_ SPEED_MPH : 5|1@0+ (1,0) [0|1] "" XXX + +VAL_ 1055 GEAR_SHIFTER 6 "L" 4 "D" 3 "N" 2 "R" 1 "P" ; diff --git a/opendbc/honda_civic_ex_2022_can_generated.dbc b/opendbc/honda_civic_ex_2022_can_generated.dbc index f696d13b4..581e7c66e 100644 --- a/opendbc/honda_civic_ex_2022_can_generated.dbc +++ b/opendbc/honda_civic_ex_2022_can_generated.dbc @@ -438,6 +438,12 @@ BO_ 401 GEARBOX: 8 PCM SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" EON SG_ CHECKSUM : 59|4@0+ (1,0) [0|15] "" EON +BO_ 419 GEARBOX_ALT: 8 PCM + SG_ GEAR : 7|8@0+ (1,0) [0|255] "" EON + SG_ GEAR_SHIFTER : 29|6@0+ (1,0) [0|63] "" EON + SG_ COUNTER : 61|2@0+ (1,0) [0|3] "" EON + SG_ CHECKSUM : 59|4@0+ (1,0) [0|3] "" EON + BO_ 432 STANDSTILL: 7 VSA SG_ WHEELS_MOVING : 12|1@0+ (1,0) [0|1] "" EON SG_ BRAKE_ERROR_1 : 11|1@0+ (1,0) [0|1] "" EON @@ -513,3 +519,4 @@ CM_ SG_ 456 STANDSTILL "set to 1 when camera requests -4.0 m/s^2"; VAL_ 401 GEAR_SHIFTER 32 "L" 16 "S" 8 "D" 4 "N" 2 "R" 1 "P"; VAL_ 401 GEAR 7 "L" 10 "S" 4 "D" 3 "N" 2 "R" 1 "P"; +VAL_ 419 GEAR_SHIFTER 32 "D" 16 "N" 8 "R" 4 "P" 0 "B" ; diff --git a/opendbc/hyundai_canfd.dbc b/opendbc/hyundai_canfd.dbc index f88afe298..2ce413c09 100644 --- a/opendbc/hyundai_canfd.dbc +++ b/opendbc/hyundai_canfd.dbc @@ -76,7 +76,7 @@ BO_ 81 ADRV_0x51: 32 ADRV BO_ 96 ESP_STATUS: 32 XXX SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX - SG_ ESP_DISABLED : 42|3@1+ (1,0) [0|63] "" XXX + SG_ TRACTION_AND_STABILITY_CONTROL : 42|3@1+ (1,0) [0|63] "" XXX SG_ BRAKE_PRESSURE : 128|10@1+ (1,0) [0|65535] "" XXX SG_ BRAKE_PRESSED : 148|1@1+ (1,0) [0|3] "" XXX @@ -94,6 +94,10 @@ BO_ 112 GEAR_ALT_2: 32 XXX BO_ 160 WHEEL_SPEEDS: 24 XXX SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX SG_ COUNTER : 16|8@1+ (1,0) [0|255] "" XXX + SG_ MOVING_FORWARD : 56|1@0+ (1,0) [0|1] "" XXX + SG_ MOVING_BACKWARD : 57|1@0+ (1,0) [0|1] "" XXX + SG_ MOVING_FORWARD2 : 58|1@0+ (1,0) [0|1] "" XXX + SG_ MOVING_BACKWARD2 : 59|1@0+ (1,0) [0|1] "" XXX SG_ WHEEL_SPEED_1 : 64|16@1+ (0.03125,0) [0|65535] "m/s" XXX SG_ WHEEL_SPEED_2 : 80|16@1+ (0.03125,0) [0|65535] "m/s" XXX SG_ WHEEL_SPEED_3 : 96|16@1+ (0.03125,0) [0|65535] "m/s" XXX @@ -301,6 +305,8 @@ BO_ 463 CRUISE_BUTTONS: 8 XXX SG_ NORMAL_CRUISE_MAIN_BTN : 21|1@1+ (1,0) [0|1] "" XXX SG_ COUNTER : 12|4@1+ (1,0) [0|255] "" XXX SG_ CRUISE_BUTTONS : 16|3@1+ (1,0) [0|3] "" XXX + SG_ RIGHT_PADDLE : 25|1@1+ (1,0) [0|1] "" XXX + SG_ LEFT_PADDLE : 27|1@1+ (1,0) [0|1] "" XXX BO_ 474 ADRV_0x1da: 32 ADRV SG_ CHECKSUM : 0|16@1+ (1,0) [0|65535] "" XXX @@ -498,8 +504,12 @@ BO_ 961 BLINKER_STALKS: 8 XXX BO_ 1041 DOORS_SEATBELTS: 8 XXX SG_ CHECKSUM_MAYBE : 7|8@0+ (1,0) [0|65535] "" XXX SG_ COUNTER_ALT : 15|4@0+ (1,0) [0|15] "" XXX - SG_ DRIVER_SEATBELT_LATCHED : 42|1@0+ (1,0) [0|1] "" XXX - SG_ DRIVER_DOOR_OPEN : 24|1@1+ (1,0) [0|1] "" XXX + SG_ DRIVER_DOOR : 24|1@1+ (1,0) [0|1] "" XXX + SG_ PASSENGER_DOOR : 34|1@0+ (1,0) [0|1] "" XXX + SG_ DRIVER_REAR_DOOR : 52|1@0+ (1,0) [0|1] "" XXX + SG_ PASSENGER_REAR_DOOR : 56|1@0+ (1,0) [0|1] "" XXX + SG_ DRIVER_SEATBELT : 42|1@0+ (1,0) [0|1] "" XXX + SG_ PASSENGER_SEATBELT : 36|1@0+ (1,0) [0|1] "" XXX BO_ 1043 BLINKERS: 8 XXX SG_ COUNTER_ALT : 15|4@0+ (1,0) [0|15] "" XXX @@ -551,6 +561,33 @@ BO_ 506 CLUSTER_SPEED_LIMIT: 32 XXX SG_ SECONDARY_LIMIT_2 : 103|8@0+ (1,0) [0|127] "" XXX SG_ SCHOOL_ZONE : 155|1@0+ (1,0) [0|1] "" XXX +BO_ 1144 DRIVE_MODE: 8 XXX + SG_ DRIVE_MODE : 0|16@1+ (1,-61611) [0|61611] "" XXX + SG_ DRIVE_MODE2 : 28|3@1+ (1,0) [1|3] "" XXX + +BO_ 1151 HVAC_TOUCH_BUTTONS: 8 XXX + SG_ AUTO_BUTTON : 8|1@0+ (1,0) [0|1] "" XXX + SG_ SYNC_BUTTON : 12|1@0+ (1,0) [0|1] "" XXX + SG_ FR_DEFROST_BUTTON : 20|1@0+ (1,0) [0|1] "" XXX + SG_ RR_DEFROST_BUTTON : 22|1@0+ (1,0) [0|1] "" XXX + SG_ FAN_SPEED_UP_BUTTON : 24|1@0+ (1,0) [0|1] "" XXX + SG_ FAN_SPEED_DOWN_BUTTON : 26|1@0+ (1,0) [0|1] "" XXX + SG_ AIR_DIRECTION_BUTTON : 28|1@0+ (1,0) [0|1] "" XXX + SG_ AC_BUTTON : 40|1@0+ (1,0) [0|1] "" XXX + SG_ DRIVER_ONLY_BUTTON : 44|1@0+ (1,0) [0|1] "" XXX + SG_ RECIRC_BUTTON : 48|1@0+ (1,0) [0|1] "" XXX + SG_ HEAT_BUTTON : 52|1@0+ (1,0) [0|1] "" XXX + +BO_ 1259 LOCAL_TIME2: 8 XXX + SG_ HOURS : 15|5@0+ (1,0) [0|31] "" XXX + SG_ MINUTES : 21|6@0+ (1,0) [0|63] "" XXX + SG_ SECONDS : 24|6@1+ (1,0) [0|63] "" XXX + SG_ NEW_SIGNAL_3 : 39|1@0+ (1,0) [0|1] "" XXX + +BO_ 1264 LOCAL_TIME: 8 XXX + SG_ HOURS : 12|5@0+ (1,0) [0|31] "" XXX + SG_ MINUTES : 21|6@0+ (1,0) [0|63] "" XXX + SG_ SECONDS : 31|8@0+ (1,0) [0|59] "" XXX CM_ SG_ 96 BRAKE_PRESSURE "User applied brake pedal pressure. Ramps from computer applied pressure on falling edge of cruise. Cruise cancels if !=0"; @@ -570,9 +607,12 @@ VAL_ 69 GEAR 0 "P" 5 "D" 6 "N" 7 "R" ; VAL_ 112 GEAR 0 "P" 5 "D" 6 "N" 7 "R" ; VAL_ 80 LKA_ICON 0 "hidden" 1 "grey" 2 "green" 3 "flashing green" ; VAL_ 80 LKA_MODE 1 "warning only" 2 "assist" 6 "off" ; +VAL_ 96 TRACTION_AND_STABILITY_CONTROL 0 "On" 5 "Limited" 1 "Off"; VAL_ 234 LKA_FAULT 0 "ok" 1 "lka fault" ; VAL_ 298 LKA_ICON 0 "hidden" 1 "grey" 2 "green" 3 "flashing green" ; VAL_ 298 LKA_MODE 1 "warning only" 2 "assist" 6 "off" ; +VAL_ 304 PARK_BUTTON 1 "Pressed" 2 "Not Pressed"; +VAL_ 304 KNOB_POSITION 1 "R" 2 "N (on R side)" 3 "Centered" 4 "N (on D side)" 5 "D"; VAL_ 304 GEAR 1 "P" 2 "R" 3 "N" 4 "D" ; VAL_ 352 AEB_SETTING 1 "off" 2 "warning only" 3 "active assist" ; VAL_ 362 BLINKER_CONTROL 1 "hazards" 2 "hazards button backlight" 3 "left blinkers" 4 "right blinkers"; @@ -580,3 +620,13 @@ VAL_ 373 ACCEnable 0 "SCC ready" 1 "SCC temp fault" 2 "SCC permanent fault" 3 "S VAL_ 416 ACCMode 0 "off" 1 "enabled" 2 "driver_override" 3 "off_maybe_fault" 4 "cancelled" ; VAL_ 426 CRUISE_BUTTONS 0 "none" 1 "res_accel" 2 "set_decel" 3 "gap_distance" 4 "pause_resume" ; VAL_ 463 CRUISE_BUTTONS 0 "none" 1 "res_accel" 2 "set_decel" 3 "gap_distance" 4 "pause_resume" ; +VAL_ 463 RIGHT_PADDLE 0 "Not Pulled" 1 "Pulled"; +VAL_ 463 LEFT_PADDLE 0 "Not Pulled" 1 "Pulled"; +VAL_ 1041 DRIVER_DOOR 0 "Closed" 1 "Opened"; +VAL_ 1041 PASSENGER_DOOR 0 "Closed" 1 "Opened"; +VAL_ 1041 DRIVER_REAR_DOOR 0 "Closed" 1 "Opened"; +VAL_ 1041 PASSENGER_REAR_DOOR 0 "Closed" 1 "Opened"; +VAL_ 1041 DRIVER_SEATBELT 0 "Unlatched" 1 "Latched"; +VAL_ 1041 PASSENGER_SEATBELT 0 "Unlatched" 1 "Latched"; +VAL_ 1144 DRIVE_MODE2 3 "Set Sport" 1 "Set Normal" 2 "Set Eco"; +VAL_ 1240 DISTANCE_UNIT 1 "Miles" 0 "Kilometers"; diff --git a/opendbc/nissan_leaf_2018.dbc b/opendbc/nissan_leaf_2018_generated.dbc similarity index 94% rename from opendbc/nissan_leaf_2018.dbc rename to opendbc/nissan_leaf_2018_generated.dbc index 6483d9cbf..bfd30e483 100644 --- a/opendbc/nissan_leaf_2018.dbc +++ b/opendbc/nissan_leaf_2018_generated.dbc @@ -1,62 +1,20 @@ -VERSION "" - - -NS_ : - NS_DESC_ - CM_ - BA_DEF_ - BA_ - VAL_ - CAT_DEF_ - CAT_ - FILTER - BA_DEF_DEF_ - EV_DATA_ - ENVVAR_DATA_ - SGTYPE_ - SGTYPE_VAL_ - BA_DEF_SGTYPE_ - BA_SGTYPE_ - SIG_TYPE_REF_ - VAL_TABLE_ - SIG_GROUP_ - SIG_VALTYPE_ - SIGTYPE_VALTYPE_ - BO_TX_BU_ - BA_DEF_REL_ - BA_REL_ - BA_DEF_DEF_REL_ - BU_SG_REL_ - BU_EV_REL_ - BU_BO_REL_ - SG_MUL_VAL_ - -BS_: - -BU_: XXX +CM_ "AUTOGENERATED FILE, DO NOT EDIT"; +CM_ "Imported file _nissan_common.dbc starts here"; BO_ 2 STEER_ANGLE_SENSOR: 5 XXX SG_ STEER_ANGLE_RATE : 16|8@1+ (1,0) [0|255] "" XXX SG_ SET_ME_X07 : 24|8@1+ (1,0) [0|255] "" XXX SG_ STEER_ANGLE : 0|16@1- (-0.1,0) [0|65535] "" XXX SG_ COUNTER : 32|4@1+ (1,0) [0|15] "" XXX -BO_ 42 SEATBELT: 8 XXX - SG_ SEATBELT_DRIVER_LATCHED : 27|1@1+ (1,0) [0|3] "" XXX - SG_ SEATBELT_DRIVER_UNLATCHED : 26|1@0+ (1,0) [0|1] "" XXX - SG_ unknown2 : 31|4@0+ (1,0) [0|15] "" XXX - SG_ unknown3 : 24|2@1+ (1,0) [0|3] "" XXX - SG_ unknown1 : 7|24@0+ (1,0) [0|16777215] "" XXX - SG_ unknown4 : 39|16@0+ (1,0) [0|65535] "" XXX - BO_ 361 LKAS: 8 XXX SG_ MAX_TORQUE : 39|8@0+ (0.01,0) [0|255] "Nm" XXX SG_ SET_0x80 : 47|8@0+ (1,0) [0|255] "" XXX SG_ LKA_ACTIVE : 52|1@0+ (1,0) [0|15] "" XXX SG_ SET_0x80_2 : 31|8@0+ (1,0) [0|255] "" XXX SG_ COUNTER : 51|4@0+ (1,0) [0|15] "" XXX - SG_ DESIRED_ANGLE : 7|18@0+ (-0.01,1310) [0|255] "" XXX + SG_ DESIRED_ANGLE : 7|18@0+ (-0.01,1310) [-1311.43|1310] "" XXX SG_ CHECKSUM : 63|8@0+ (1,0) [0|255] "" XXX BO_ 389 STEER_TORQUE_SENSOR: 8 XXX @@ -67,38 +25,6 @@ BO_ 389 STEER_TORQUE_SENSOR: 8 XXX SG_ COUNTER : 51|4@0+ (1,0) [0|15] "" XXX SG_ CHECKSUM : 63|8@0+ (1,0) [0|127] "" XXX -BO_ 460 BRAKE_PEDAL: 8 XXX - SG_ BRAKE_PEDAL : 7|8@0+ (1,0) [0|256] "" XXX - -BO_ 569 CRUISE_THROTTLE: 8 XXX - SG_ GAS_PEDAL_INVERTED : 15|8@0+ (1,0) [0|255] "" XXX - SG_ GAS_PEDAL : 7|8@0+ (1,0) [0|255] "" XXX - SG_ CRUISE_AVAILABLE : 17|1@0+ (1,0) [0|1] "" XXX - SG_ unsure1 : 23|6@0+ (1,0) [0|63] "" XXX - SG_ unsure2 : 16|1@0+ (1,0) [0|1] "" XXX - SG_ unsure3 : 31|2@0+ (1,0) [0|3] "" XXX - SG_ NO_BUTTON_PRESSED : 29|1@0+ (1,0) [0|1] "" XXX - SG_ RES_BUTTON : 28|1@0+ (1,0) [0|1] "" XXX - SG_ SET_BUTTON : 27|1@0+ (1,0) [0|1] "" XXX - SG_ FOLLOW_DISTANCE_BUTTON : 26|1@0+ (1,0) [0|1] "" XXX - SG_ CANCEL_BUTTON : 25|1@0+ (1,0) [0|1] "" XXX - SG_ PROPILOT_BUTTON : 24|1@0+ (1,0) [0|1] "" XXX - SG_ USER_BRAKE_PRESSED : 37|1@0+ (1,0) [0|1] "" XXX - SG_ COUNTER : 32|2@1+ (1,0) [0|3] "" XXX - SG_ unsure5 : 47|8@0+ (1,0) [0|255] "" XXX - SG_ unsure6 : 55|8@0+ (1,0) [0|255] "" XXX - SG_ unsure7 : 63|8@0+ (1,0) [0|255] "" XXX - -BO_ 640 CANCEL_MSG: 8 XXX - SG_ CANCEL_SEATBELT : 1|1@0+ (1,0) [0|1] "" XXX - SG_ NEW_SIGNAL_1 : 7|6@0+ (1,0) [0|63] "" XXX - SG_ NEW_SIGNAL_2 : 0|1@0+ (1,0) [0|1] "" XXX - SG_ NEW_SIGNAL_3 : 15|56@0+ (1,0) [0|72057594037927940] "" XXX - -BO_ 644 WHEEL_SPEEDS_FRONT: 8 XXX - SG_ WHEEL_SPEED_FR : 7|16@0+ (0.005,0) [0|65535] "KPH" XXX - SG_ WHEEL_SPEED_FL : 23|16@0+ (0.005,0) [0|65535] "KPH" XXX - BO_ 645 WHEEL_SPEEDS_REAR: 8 XXX SG_ WHEEL_SPEED_RR : 7|16@0+ (0.005,0) [0|65535] "KPH" XXX SG_ WHEEL_SPEED_RL : 23|16@0+ (0.005,0) [0|65535] "KPH" XXX @@ -139,19 +65,6 @@ BO_ 689 PROPILOT_HUD: 8 XXX BO_ 783 CRUISE_STATE: 3 XXX SG_ CRUISE_ENABLED : 3|1@0+ (1,0) [0|1] "" XXX -BO_ 852 ESP: 8 XXX - SG_ ESP_DISABLED : 38|1@0+ (1,0) [0|1] "" XXX - -BO_ 853 HUD_SETTINGS: 8 XXX - SG_ SPEED_MPH : 37|1@0+ (1,0) [0|1] "" XXX - -BO_ 856 LIGHTS: 8 XXX - SG_ LEFT_BLINKER : 17|1@0+ (1,0) [0|1] "" XXX - SG_ RIGHT_BLINKER : 18|1@0+ (1,0) [0|1] "" XXX - -BO_ 1057 GEARBOX: 3 XXX - SG_ GEAR_SHIFTER : 5|3@0+ (1,0) [0|255] "" XXX - BO_ 1228 PROPILOT_HUD_INFO_MSG: 8 XXX SG_ NA_HIGH_ACCEL_TEMP : 0|1@0+ (1,0) [0|1] "" XXX SG_ SIDE_RADAR_NA_HIGH_CABIN_TEMP : 8|1@0+ (1,0) [0|1] "" XXX @@ -193,19 +106,75 @@ BO_ 1228 PROPILOT_HUD_INFO_MSG: 8 XXX SG_ unknown55 : 55|1@0+ (1,0) [0|1] "" XXX SG_ unknown61 : 61|6@0+ (1,0) [0|63] "" XXX -BO_ 1549 DOORS_LIGHTS: 8 XXX - SG_ DOOR_OPEN_FL : 3|1@0+ (1,0) [0|1] "" XXX - SG_ DOOR_OPEN_FR : 4|1@0+ (1,0) [0|1] "" XXX - SG_ DOOR_OPEN_RL : 5|1@0+ (1,0) [0|1] "" XXX - SG_ DOOR_OPEN_RR : 6|1@0+ (1,0) [0|1] "" XXX - BO_ 1227 LKAS_SETTINGS: 8 XXX SG_ LKAS_ENABLED : 51|1@0+ (1,0) [0|1] "" XXX -VAL_ 1057 GEAR_SHIFTER 7 "B" 4 "D" 3 "N" 2 "R" 1 "P" ; VAL_ 1228 PROPILOT_NA_MSGS 0 "NO_MSG" 1 "NA_FRONT_CAMERA_IMPARED" 2 "STEERING_ASSIST_ON_STANDBY" 3 "NA_PARKING_ASSIST_ENABLED" 4 "STEER_ASSIST_CURRENTLY_NA" 5 "NA_BAD_WEATHER" 6 "NA_PARK_BRAKE_ON" 7 "NA_SEATBELT_NOT_FASTENED" ; VAL_ 1228 BOTTOM_MSG 0 "OK_STEER_ASSIST_SETTINGS" 1 "NO_MSG" 2 "PRESS_SET_TO_SET_SPEED" 3 "PRESS_RES_SET_TO_CHANGE_SPEED" 4 "PRESS_RES_TO_RESTART" 5 "NO_MSG" 6 "CRUISE_NOT_AVAIL" 7 "NO_MSG" ; VAL_ 689 FOLLOW_DISTANCE 0 "NO_FOLLOW_DISTANCE" 1 "FOLLOW_DISTANCE_1" 2 "FOLLOW_DISTANCE_2" 3 "FOLLOW_DISANCE_3" ; VAL_ 689 AUDIBLE_TONE 0 "NO_TONE" 1 "CONT" 2 "FAST_BEEP_CONT" 3 "TRIPLE_FAST_BEEP_CONT" 4 "SLOW_BEEP_CONT" 5 "QUAD_SLOW_BEEP_CONT" 6 "SINGLE_BEEP_ONCE" 7 "DOUBLE_BEEP_ONCE" ; VAL_ 689 SMALL_STEERING_WHEEL_ICON 0 "NO_ICON" 1 "GRAY_ICON" 2 "GRAY_ICON_FLASHING" 3 "GREEN_ICON" 4 "GREEN_ICON_FLASHING" 5 "RED_ICON" 6 "RED_ICON_FLASHING" 7 "YELLOW_ICON" ; VAL_ 689 LARGE_STEERING_WHEEL_ICON 0 "NO_STEERINGWHEEL" 1 "GRAY_STEERINGWHEEL" 2 "GREEN_STEERINGWHEEL" 3 "GREEN_STEERINGWHEEL_FLASHING" ; + +CM_ "nissan_leaf_2018.dbc starts here"; + +BO_ 42 SEATBELT: 8 XXX + SG_ SEATBELT_DRIVER_LATCHED : 27|1@1+ (1,0) [0|3] "" XXX + SG_ SEATBELT_DRIVER_UNLATCHED : 26|1@0+ (1,0) [0|1] "" XXX + SG_ unknown2 : 31|4@0+ (1,0) [0|15] "" XXX + SG_ unknown3 : 24|2@1+ (1,0) [0|3] "" XXX + SG_ unknown1 : 7|24@0+ (1,0) [0|16777215] "" XXX + SG_ unknown4 : 39|16@0+ (1,0) [0|65535] "" XXX + +BO_ 460 BRAKE_PEDAL: 8 XXX + SG_ BRAKE_PEDAL : 7|8@0+ (1,0) [0|256] "" XXX + +BO_ 569 CRUISE_THROTTLE: 8 XXX + SG_ GAS_PEDAL_INVERTED : 15|8@0+ (1,0) [0|255] "" XXX + SG_ GAS_PEDAL : 7|8@0+ (1,0) [0|255] "" XXX + SG_ CRUISE_AVAILABLE : 17|1@0+ (1,0) [0|1] "" XXX + SG_ unsure1 : 23|6@0+ (1,0) [0|63] "" XXX + SG_ unsure2 : 16|1@0+ (1,0) [0|1] "" XXX + SG_ unsure3 : 31|2@0+ (1,0) [0|3] "" XXX + SG_ NO_BUTTON_PRESSED : 29|1@0+ (1,0) [0|1] "" XXX + SG_ RES_BUTTON : 28|1@0+ (1,0) [0|1] "" XXX + SG_ SET_BUTTON : 27|1@0+ (1,0) [0|1] "" XXX + SG_ FOLLOW_DISTANCE_BUTTON : 26|1@0+ (1,0) [0|1] "" XXX + SG_ CANCEL_BUTTON : 25|1@0+ (1,0) [0|1] "" XXX + SG_ PROPILOT_BUTTON : 24|1@0+ (1,0) [0|1] "" XXX + SG_ USER_BRAKE_PRESSED : 37|1@0+ (1,0) [0|1] "" XXX + SG_ COUNTER : 32|2@1+ (1,0) [0|3] "" XXX + SG_ unsure5 : 47|8@0+ (1,0) [0|255] "" XXX + SG_ unsure6 : 55|8@0+ (1,0) [0|255] "" XXX + SG_ unsure7 : 63|8@0+ (1,0) [0|255] "" XXX + +BO_ 640 CANCEL_MSG: 8 XXX + SG_ CANCEL_SEATBELT : 1|1@0+ (1,0) [0|1] "" XXX + SG_ NEW_SIGNAL_1 : 7|6@0+ (1,0) [0|63] "" XXX + SG_ NEW_SIGNAL_2 : 0|1@0+ (1,0) [0|1] "" XXX + SG_ NEW_SIGNAL_3 : 15|56@0+ (1,0) [0|72057594037927940] "" XXX + +BO_ 644 WHEEL_SPEEDS_FRONT: 8 XXX + SG_ WHEEL_SPEED_FR : 7|16@0+ (0.005,0) [0|65535] "KPH" XXX + SG_ WHEEL_SPEED_FL : 23|16@0+ (0.005,0) [0|65535] "KPH" XXX + +BO_ 852 ESP: 8 XXX + SG_ ESP_DISABLED : 38|1@0+ (1,0) [0|1] "" XXX + +BO_ 853 HUD_SETTINGS: 8 XXX + SG_ SPEED_MPH : 37|1@0+ (1,0) [0|1] "" XXX + +BO_ 856 LIGHTS: 8 XXX + SG_ LEFT_BLINKER : 17|1@0+ (1,0) [0|1] "" XXX + SG_ RIGHT_BLINKER : 18|1@0+ (1,0) [0|1] "" XXX + +BO_ 1057 GEARBOX: 3 XXX + SG_ GEAR_SHIFTER : 5|3@0+ (1,0) [0|255] "" XXX + +BO_ 1549 DOORS_LIGHTS: 8 XXX + SG_ DOOR_OPEN_FL : 3|1@0+ (1,0) [0|1] "" XXX + SG_ DOOR_OPEN_FR : 4|1@0+ (1,0) [0|1] "" XXX + SG_ DOOR_OPEN_RL : 5|1@0+ (1,0) [0|1] "" XXX + SG_ DOOR_OPEN_RR : 6|1@0+ (1,0) [0|1] "" XXX + +VAL_ 1057 GEAR_SHIFTER 7 "B" 4 "D" 3 "N" 2 "R" 1 "P" ; diff --git a/opendbc/nissan_x_trail_2017.dbc b/opendbc/nissan_x_trail_2017_generated.dbc similarity index 94% rename from opendbc/nissan_x_trail_2017.dbc rename to opendbc/nissan_x_trail_2017_generated.dbc index ea27445d3..c6a3ef7a8 100644 --- a/opendbc/nissan_x_trail_2017.dbc +++ b/opendbc/nissan_x_trail_2017_generated.dbc @@ -1,58 +1,20 @@ -VERSION "" - - -NS_ : - NS_DESC_ - CM_ - BA_DEF_ - BA_ - VAL_ - CAT_DEF_ - CAT_ - FILTER - BA_DEF_DEF_ - EV_DATA_ - ENVVAR_DATA_ - SGTYPE_ - SGTYPE_VAL_ - BA_DEF_SGTYPE_ - BA_SGTYPE_ - SIG_TYPE_REF_ - VAL_TABLE_ - SIG_GROUP_ - SIG_VALTYPE_ - SIGTYPE_VALTYPE_ - BO_TX_BU_ - BA_DEF_REL_ - BA_REL_ - BA_DEF_DEF_REL_ - BU_SG_REL_ - BU_EV_REL_ - BU_BO_REL_ - SG_MUL_VAL_ - -BS_: - -BU_: XXX +CM_ "AUTOGENERATED FILE, DO NOT EDIT"; +CM_ "Imported file _nissan_common.dbc starts here"; BO_ 2 STEER_ANGLE_SENSOR: 5 XXX SG_ STEER_ANGLE_RATE : 16|8@1+ (1,0) [0|255] "" XXX SG_ SET_ME_X07 : 24|8@1+ (1,0) [0|255] "" XXX SG_ STEER_ANGLE : 0|16@1- (-0.1,0) [0|65535] "" XXX SG_ COUNTER : 32|4@1+ (1,0) [0|15] "" XXX -BO_ 348 GAS_PEDAL: 8 XXX - SG_ GAS_PEDAL_RAW : 26|11@0+ (1,0) [0|2047] "" XXX - SG_ GAS_PEDAL : 47|10@0+ (1,0) [0|1023] "" XXX - BO_ 361 LKAS: 8 XXX SG_ MAX_TORQUE : 39|8@0+ (0.01,0) [0|255] "Nm" XXX SG_ SET_0x80 : 47|8@0+ (1,0) [0|255] "" XXX SG_ LKA_ACTIVE : 52|1@0+ (1,0) [0|15] "" XXX SG_ SET_0x80_2 : 31|8@0+ (1,0) [0|255] "" XXX SG_ COUNTER : 51|4@0+ (1,0) [0|15] "" XXX - SG_ DESIRED_ANGLE : 7|18@0+ (-0.01,1310) [0|255] "" XXX + SG_ DESIRED_ANGLE : 7|18@0+ (-0.01,1310) [-1311.43|1310] "" XXX SG_ CHECKSUM : 63|8@0+ (1,0) [0|255] "" XXX BO_ 389 STEER_TORQUE_SENSOR: 8 XXX @@ -63,42 +25,10 @@ BO_ 389 STEER_TORQUE_SENSOR: 8 XXX SG_ COUNTER : 51|4@0+ (1,0) [0|15] "" XXX SG_ CHECKSUM : 63|8@0+ (1,0) [0|127] "" XXX -BO_ 438 PRO_PILOT: 8 XXX - SG_ COUNTER : 55|4@0+ (1,0) [0|255] "" XXX - SG_ SET_ME_X03 : 33|2@0+ (1,0) [0|15] "" XXX - SG_ CRUISE_ACTIVATED : 38|1@0+ (1,0) [0|3] "" XXX - SG_ CRUISE_ON : 36|1@0+ (1,0) [0|255] "" XXX - SG_ STEER_STATUS : 51|1@0+ (1,0) [0|3] "" XXX - -BO_ 523 CRUISE_THROTTLE: 6 XXX - SG_ PROPILOT_BUTTON : 8|1@0+ (1,0) [0|1] "" XXX - SG_ CANCEL_BUTTON : 9|1@0+ (1,0) [0|1] "" XXX - SG_ GAS_PEDAL_INVERTED : 37|10@0+ (1,0) [0|1023] "" XXX - SG_ SET_BUTTON : 11|1@0+ (1,0) [0|1] "" XXX - SG_ RES_BUTTON : 12|1@0+ (1,0) [0|1] "" XXX - SG_ FOLLOW_DISTANCE_BUTTON : 10|1@0+ (1,0) [0|1] "" XXX - SG_ NO_BUTTON_PRESSED : 13|1@0+ (1,0) [0|1] "" XXX - SG_ GAS_PEDAL : 31|10@0+ (1,0) [0|255] "" XXX - SG_ USER_BRAKE_PRESSED : 21|1@0+ (1,0) [0|1] "" XXX - SG_ USER_BRAKE_PRESSED_INVERTED : 22|1@0+ (1,0) [0|3] "" XXX - SG_ NEW_SIGNAL_2 : 23|1@0+ (1,0) [0|1] "" XXX - SG_ GAS_PRESSED_INVERTED : 20|1@0+ (1,0) [0|255] "" XXX - SG_ COUNTER : 17|2@0+ (1,0) [0|3] "" XXX - SG_ unsure1 : 7|10@0+ (1,0) [0|1023] "" XXX - SG_ unsure2 : 43|4@0+ (1,0) [0|1] "" XXX - SG_ unsure3 : 19|2@0+ (1,0) [0|3] "" XXX - BO_ 645 WHEEL_SPEEDS_REAR: 8 XXX SG_ WHEEL_SPEED_RR : 7|16@0+ (0.005,0) [0|65535] "KPH" XXX SG_ WHEEL_SPEED_RL : 23|16@0+ (0.005,0) [0|65535] "KPH" XXX -BO_ 665 ESP: 8 XXX - SG_ ESP_DISABLED : 24|1@0+ (1,0) [0|1] "" XXX - -BO_ 666 WHEEL_SPEEDS_FRONT: 8 XXX - SG_ WHEEL_SPEED_FR : 7|16@0+ (0.005,0) [0|65535] "KPH" XXX - SG_ WHEEL_SPEED_FL : 23|16@0+ (0.005,0) [0|65535] "KPH" XXX - BO_ 689 PROPILOT_HUD: 8 XXX SG_ LARGE_WARNING_FLASHING : 9|1@0+ (1,0) [0|1] "" XXX SG_ SIDE_RADAR_ERROR_FLASHING1 : 10|1@0+ (1,0) [0|1] "" XXX @@ -132,35 +62,9 @@ BO_ 689 PROPILOT_HUD: 8 XXX SG_ unknown55 : 55|8@0+ (1,0) [0|63] "" XXX SG_ unknown59 : 59|4@0+ (1,0) [0|15] "" XXX -BO_ 768 STEER_TORQUE_SENSOR2: 2 XXX - SG_ STEERING_TORQUE : 6|7@0+ (1,0) [0|127] "" XXX - SG_ STEERING_PRESSED : 15|1@0+ (-1,1) [0|7] "" XXX - BO_ 783 CRUISE_STATE: 3 XXX SG_ CRUISE_ENABLED : 3|1@0+ (1,0) [0|1] "" XXX -BO_ 1055 GEARBOX: 2 XXX - SG_ SPORTS_MODE : 13|1@0+ (1,0) [0|1] "" XXX - SG_ GEAR_SHIFTER : 5|3@0+ (1,0) [0|255] "" XXX - -BO_ 1107 LIGHTS: 8 XXX - SG_ RIGHT_BLINKER : 12|1@0+ (1,0) [0|1] "" XXX - SG_ LEFT_BLINKER : 11|1@0+ (1,0) [0|1] "" XXX - SG_ HEADLIGHTS : 5|1@0+ (1,0) [0|1] "" XXX - -BO_ 1108 DOORS_LIGHTS: 8 XXX - SG_ DOOR_CLOSED_RR : 40|1@0+ (1,0) [0|1] "" XXX - SG_ DOOR_OPEN_RR : 41|1@0+ (1,0) [0|1] "" XXX - SG_ DOOR_CLOSED_RL : 42|1@0+ (1,0) [0|1] "" XXX - SG_ DOOR_OPEN_RL : 43|1@0+ (1,0) [0|1] "" XXX - SG_ DOOR_CLOSED_FL : 44|1@0+ (1,0) [0|1] "" XXX - SG_ DOOR_OPEN_FL : 45|1@0+ (1,0) [0|1] "" XXX - SG_ DOOR_CLOSED_FR : 46|1@0+ (1,0) [0|3] "" XXX - SG_ DOOR_OPEN_FR : 47|1@0+ (1,0) [0|3] "" XXX - SG_ BOOT_OPEN : 55|1@0+ (1,0) [0|1] "" XXX - SG_ BRAKE_LIGHT : 54|1@0+ (1,0) [0|1] "" XXX - SG_ USER_BRAKE_PRESSED : 23|1@0+ (1,0) [0|1] "" XXX - BO_ 1228 PROPILOT_HUD_INFO_MSG: 8 XXX SG_ NA_HIGH_ACCEL_TEMP : 0|1@0+ (1,0) [0|1] "" XXX SG_ SIDE_RADAR_NA_HIGH_CABIN_TEMP : 8|1@0+ (1,0) [0|1] "" XXX @@ -205,14 +109,79 @@ BO_ 1228 PROPILOT_HUD_INFO_MSG: 8 XXX BO_ 1227 LKAS_SETTINGS: 8 XXX SG_ LKAS_ENABLED : 51|1@0+ (1,0) [0|1] "" XXX -BO_ 1273 HUD: 7 XXX - SG_ SEATBELT_DRIVER_LATCHED : 25|1@0+ (1,0) [0|1] "" XXX - SG_ SPEED_MPH : 5|1@0+ (1,0) [0|1] "" XXX - -VAL_ 1055 GEAR_SHIFTER 6 "L" 4 "D" 3 "N" 2 "R" 1 "P" ; VAL_ 1228 PROPILOT_NA_MSGS 0 "NO_MSG" 1 "NA_FRONT_CAMERA_IMPARED" 2 "STEERING_ASSIST_ON_STANDBY" 3 "NA_PARKING_ASSIST_ENABLED" 4 "STEER_ASSIST_CURRENTLY_NA" 5 "NA_BAD_WEATHER" 6 "NA_PARK_BRAKE_ON" 7 "NA_SEATBELT_NOT_FASTENED" ; VAL_ 1228 BOTTOM_MSG 0 "OK_STEER_ASSIST_SETTINGS" 1 "NO_MSG" 2 "PRESS_SET_TO_SET_SPEED" 3 "PRESS_RES_SET_TO_CHANGE_SPEED" 4 "PRESS_RES_TO_RESTART" 5 "NO_MSG" 6 "CRUISE_NOT_AVAIL" 7 "NO_MSG" ; VAL_ 689 FOLLOW_DISTANCE 0 "NO_FOLLOW_DISTANCE" 1 "FOLLOW_DISTANCE_1" 2 "FOLLOW_DISTANCE_2" 3 "FOLLOW_DISANCE_3" ; VAL_ 689 AUDIBLE_TONE 0 "NO_TONE" 1 "CONT" 2 "FAST_BEEP_CONT" 3 "TRIPLE_FAST_BEEP_CONT" 4 "SLOW_BEEP_CONT" 5 "QUAD_SLOW_BEEP_CONT" 6 "SINGLE_BEEP_ONCE" 7 "DOUBLE_BEEP_ONCE" ; VAL_ 689 SMALL_STEERING_WHEEL_ICON 0 "NO_ICON" 1 "GRAY_ICON" 2 "GRAY_ICON_FLASHING" 3 "GREEN_ICON" 4 "GREEN_ICON_FLASHING" 5 "RED_ICON" 6 "RED_ICON_FLASHING" 7 "YELLOW_ICON" ; VAL_ 689 LARGE_STEERING_WHEEL_ICON 0 "NO_STEERINGWHEEL" 1 "GRAY_STEERINGWHEEL" 2 "GREEN_STEERINGWHEEL" 3 "GREEN_STEERINGWHEEL_FLASHING" ; + +CM_ "nissan_x_trail_2017.dbc starts here"; + +BO_ 348 GAS_PEDAL: 8 XXX + SG_ GAS_PEDAL_RAW : 26|11@0+ (1,0) [0|2047] "" XXX + SG_ GAS_PEDAL : 47|10@0+ (1,0) [0|1023] "" XXX + +BO_ 438 PRO_PILOT: 8 XXX + SG_ COUNTER : 55|4@0+ (1,0) [0|255] "" XXX + SG_ SET_ME_X03 : 33|2@0+ (1,0) [0|15] "" XXX + SG_ CRUISE_ACTIVATED : 38|1@0+ (1,0) [0|3] "" XXX + SG_ CRUISE_ON : 36|1@0+ (1,0) [0|255] "" XXX + SG_ STEER_STATUS : 51|1@0+ (1,0) [0|3] "" XXX + +BO_ 523 CRUISE_THROTTLE: 6 XXX + SG_ PROPILOT_BUTTON : 8|1@0+ (1,0) [0|1] "" XXX + SG_ CANCEL_BUTTON : 9|1@0+ (1,0) [0|1] "" XXX + SG_ GAS_PEDAL_INVERTED : 37|10@0+ (1,0) [0|1023] "" XXX + SG_ SET_BUTTON : 11|1@0+ (1,0) [0|1] "" XXX + SG_ RES_BUTTON : 12|1@0+ (1,0) [0|1] "" XXX + SG_ FOLLOW_DISTANCE_BUTTON : 10|1@0+ (1,0) [0|1] "" XXX + SG_ NO_BUTTON_PRESSED : 13|1@0+ (1,0) [0|1] "" XXX + SG_ GAS_PEDAL : 31|10@0+ (1,0) [0|255] "" XXX + SG_ USER_BRAKE_PRESSED : 21|1@0+ (1,0) [0|1] "" XXX + SG_ USER_BRAKE_PRESSED_INVERTED : 22|1@0+ (1,0) [0|3] "" XXX + SG_ NEW_SIGNAL_2 : 23|1@0+ (1,0) [0|1] "" XXX + SG_ GAS_PRESSED_INVERTED : 20|1@0+ (1,0) [0|255] "" XXX + SG_ COUNTER : 17|2@0+ (1,0) [0|3] "" XXX + SG_ unsure1 : 7|10@0+ (1,0) [0|1023] "" XXX + SG_ unsure2 : 43|4@0+ (1,0) [0|1] "" XXX + SG_ unsure3 : 19|2@0+ (1,0) [0|3] "" XXX + +BO_ 665 ESP: 8 XXX + SG_ ESP_DISABLED : 24|1@0+ (1,0) [0|1] "" XXX + +BO_ 666 WHEEL_SPEEDS_FRONT: 8 XXX + SG_ WHEEL_SPEED_FR : 7|16@0+ (0.005,0) [0|65535] "KPH" XXX + SG_ WHEEL_SPEED_FL : 23|16@0+ (0.005,0) [0|65535] "KPH" XXX + +BO_ 768 STEER_TORQUE_SENSOR2: 2 XXX + SG_ STEERING_TORQUE : 6|7@0+ (1,0) [0|127] "" XXX + SG_ STEERING_PRESSED : 15|1@0+ (-1,1) [0|7] "" XXX + +BO_ 1055 GEARBOX: 2 XXX + SG_ SPORTS_MODE : 13|1@0+ (1,0) [0|1] "" XXX + SG_ GEAR_SHIFTER : 5|3@0+ (1,0) [0|255] "" XXX + +BO_ 1107 LIGHTS: 8 XXX + SG_ RIGHT_BLINKER : 12|1@0+ (1,0) [0|1] "" XXX + SG_ LEFT_BLINKER : 11|1@0+ (1,0) [0|1] "" XXX + SG_ HEADLIGHTS : 5|1@0+ (1,0) [0|1] "" XXX + +BO_ 1108 DOORS_LIGHTS: 8 XXX + SG_ DOOR_CLOSED_RR : 40|1@0+ (1,0) [0|1] "" XXX + SG_ DOOR_OPEN_RR : 41|1@0+ (1,0) [0|1] "" XXX + SG_ DOOR_CLOSED_RL : 42|1@0+ (1,0) [0|1] "" XXX + SG_ DOOR_OPEN_RL : 43|1@0+ (1,0) [0|1] "" XXX + SG_ DOOR_CLOSED_FL : 44|1@0+ (1,0) [0|1] "" XXX + SG_ DOOR_OPEN_FL : 45|1@0+ (1,0) [0|1] "" XXX + SG_ DOOR_CLOSED_FR : 46|1@0+ (1,0) [0|3] "" XXX + SG_ DOOR_OPEN_FR : 47|1@0+ (1,0) [0|3] "" XXX + SG_ BOOT_OPEN : 55|1@0+ (1,0) [0|1] "" XXX + SG_ BRAKE_LIGHT : 54|1@0+ (1,0) [0|1] "" XXX + SG_ USER_BRAKE_PRESSED : 23|1@0+ (1,0) [0|1] "" XXX + +BO_ 1273 HUD: 7 XXX + SG_ SEATBELT_DRIVER_LATCHED : 25|1@0+ (1,0) [0|1] "" XXX + SG_ SPEED_MPH : 5|1@0+ (1,0) [0|1] "" XXX + +VAL_ 1055 GEAR_SHIFTER 6 "L" 4 "D" 3 "N" 2 "R" 1 "P" ; diff --git a/opendbc/vw_golf_mk4.dbc b/opendbc/vw_golf_mk4.dbc index eb8f6c959..c562b9e05 100644 --- a/opendbc/vw_golf_mk4.dbc +++ b/opendbc/vw_golf_mk4.dbc @@ -303,7 +303,8 @@ BO_ 1408 Motor_Flexia: 8 XXX SG_ Multiplex_Schalter_Motor_Flexia M : 0|1@1+ (1,0) [0|0] "" XXX BO_ 1416 Motor_7: 8 XXX - SG_ Frei_Motor_7_3 : 40|24@1+ (1,0) [0|0] "" XXX + SG_ Oltemperatur : 56|8@1+ (1,0) [0|0] "" XXX + SG_ Frei_Motor_7_3 : 40|16@1+ (1,0) [0|0] "" XXX SG_ Ladedruck : 32|8@1+ (0.01,0) [0|2.54] "bar" XXX SG_ Vorzeichen_Motordrehzahlgradien : 31|1@1+ (1,0) [0|0] "" XXX SG_ Motordrehzahlgradient : 24|7@1+ (1,0) [0|126] "U/min" XXX @@ -321,7 +322,8 @@ BO_ 1160 Motor_6: 8 XXX SG_ Frei_Motor_6_4 : 58|2@1+ (1,0) [0|0] "" XXX SG_ ltemperaturschutz : 57|1@1+ (1,0) [0|0] "" XXX SG_ GRA_Bremseingriff_Freigabe : 56|1@1+ (1,0) [0|0] "" XXX - SG_ Frei_Motor_6_3 : 40|16@1+ (1,0) [0|0] "" XXX + SG_ Frei_Motor_6_3 : 48|8@1+ (1,0) [0|0] "" XXX + SG_ Ruckmeldung_Momenten : 40|8@1+ (0.39,0) [0|100] "" XXX SG_ GRA_Sollbeschleunigung : 32|8@1+ (0.024,-3.984) [-3.984|2.112] "m/s2" XXX SG_ Hoeheninfo__Motor_6_ : 24|8@1+ (0.00787,0) [0|2] "" XXX SG_ Istmoment_f_r_Getriebe : 16|8@1+ (0.39,0) [0|99] "MDI" XXX @@ -770,25 +772,19 @@ BO_ 1340 Fahrwerk_1: 1 XXX SG_ Ansteuererung_Fahrzeugniveau : 0|4@1+ (1,0) [0|15] "" XXX BO_ 1472 EPB_1: 8 XXX - SG_ CHECKSUM : 56|8@1+ (1,0) [0|255] "" XXX SG_ COUNTER : 0|4@1+ (1,0) [0|15] "" XXX - SG_ EP1__Text : 52|4@1+ (1,0) [0|8] "" Vector__XXX - SG_ EP1_Failure_gelb : 51|1@1+ (1,0) [0|1] "" XXX - SG_ EP1_Failure_BKL : 50|1@1+ (1,0) [0|1] "" XXX - SG_ EP1_Warnton : 49|1@1+ (1,0) [0|1] "" XXX - SG_ EP1_Fkt_Lampe : 48|1@1+ (1,0) [0|1] "" Vector__XXX - SG_ EP1_Freigabe_Ver : 33|1@1+ (1,0) [0|1] "" XXX - SG_ EP1_Failureeintr : 32|1@1+ (1,0) [0|1] "" XXX - SG_ EP1_Neig_winkel : 16|8@1+ (1,-128) [-128|127] "g" XXX - SG_ EP1_Verzoegerung : 24|8@1+ (0.048,-7.968) [-7.968|4.224] "g" XXX - SG_ EP1_Sta_NWS : 15|1@1+ (1,0) [0|1] "" XXX - SG_ EP1_Schalterinfo : 13|2@1+ (1,0) [0|3] "" XXX - SG_ EP1_Spannkraft : 8|5@1+ (1,0) [0|30] "kN" XXX - SG_ EP1_Sta_Schalter : 7|1@1+ (1,0) [0|1] "" XXX + SG_ EP1_Fehler_Sta : 4|2@1+ (1,0) [0|3] "" Vector__XXX SG_ EP1_Sta_EPB : 6|1@1+ (1,0) [0|1] "" XXX - SG_ EP1_Failure_Sta : 4|2@1+ (1,0) [0|3] "" XXX + SG_ EP1_Sta_Schalter : 7|1@1+ (1,0) [0|1] "" XXX + SG_ EP1_Spannkraft : 8|5@1+ (1,0) [0|30] "Unit_KiloNewto" XXX + SG_ EP1_Schalterinfo : 13|2@1+ (1,0) [0|3] "" XXX + SG_ EP1_Sta_NWS : 15|1@1+ (1,0) [0|1] "" XXX + SG_ EP1_Neig_winkel : 16|8@1+ (1,-128) [-128|127] "Unit_PerCentOfForceOfGravi" XXX + SG_ EP1_Verzoegerung : 24|8@1+ (0.048,-7.968) [-7.968|4.224] "Unit_MeterPerSeconSquar" XXX + SG_ EP1_Fehlereintr : 32|1@1+ (1,0) [0|1] "" XXX + SG_ EP1_Freigabe_Ver : 33|1@1+ (1,0) [0|1] "" XXX SG_ EP1_AutoHold_zul : 34|1@1+ (1,0) [0|1] "" XXX - SG_ EP1_AutoHold_active : 35|1@1+ (1,0) [0|1] "" XXX + SG_ EP1_AutoHold_aktiv : 35|1@1+ (1,0) [0|1] "" XXX SG_ EP1_SleepInd : 36|1@1+ (1,0) [0|1] "" XXX SG_ EP1_Status_Kl_15 : 37|1@1+ (1,0) [0|1] "" XXX SG_ EP1_Lampe_AutoP : 38|1@1+ (1,0) [0|1] "" XXX @@ -797,9 +793,15 @@ BO_ 1472 EPB_1: 8 XXX SG_ EP1_Warnton2 : 41|1@1+ (1,0) [0|1] "" XXX SG_ EP1_AnfShLock : 42|1@1+ (1,0) [0|1] "" XXX SG_ EPB_Autoholdlampe : 43|1@1+ (1,0) [0|1] "" XXX + SG_ EP1_QualNeigWi : 44|1@1+ (1,0) [0|1] "" XXX SG_ EP1_KuppModBer : 45|2@1+ (1,0) [0|3] "" XXX SG_ EP1_HydrHalten : 47|1@1+ (1,0) [0|1] "" XXX - SG_ EP1_QualNeigWi : 44|1@1+ (1,0) [0|1] "" XXX + SG_ EP1_Fkt_Lampe : 48|1@1+ (1,0) [0|1] "" Vector__XXX + SG_ EP1_Warnton : 49|1@1+ (1,0) [0|1] "" XXX + SG_ EP1_Fehler_BKL : 50|1@1+ (1,0) [0|1] "" Vector__XXX + SG_ EP1_Fehler_gelb : 51|1@1+ (1,0) [0|1] "" XXX + SG_ EP1__Text : 52|4@1+ (1,0) [0|8] "" Vector__XXX + SG_ CHECKSUM : 56|8@1+ (1,0) [0|255] "" XXX BO_ 1326 Diag_Lenkhilfe: 3 XXX SG_ Werkstattcode__Diag_ : 16|8@1+ (1,0) [0|0] "" XXX @@ -1169,28 +1171,39 @@ BO_ 644 Motor_Bremse: 6 XXX SG_ MOB_Bremsstgr : 16|11@1+ (0.048852,0) [0|100] "Unit_PerCent" Vector__XXX SG_ MOB_Bremsmom : 27|13@1+ (4,0) [0|32760] "Unit_NewtoMeter" Bremse_MK25AESP -BO_ 870 AWV: 5 XXX - SG_ AWV_2_Gurtstraffer : 39|1@1+ (1,0) [0|1] "" Bremsbooster - SG_ AWV_Infoton : 38|1@1+ (1,0) [0|1] "" Gateway_Kbi_VW_A,Bremsbooster - SG_ AWV_2_Warnsymbol : 37|1@1+ (1,0) [0|1] "" Gateway_Kbi_VW_A,Bremsbooster - SG_ AWV_2_Warnton : 36|1@1+ (1,0) [0|1] "" Gateway_Kbi_VW_A,Bremsbooster - SG_ AWV_2_Ruckprofil : 33|3@1+ (1,0) [0|7] "" Bremsbooster - SG_ AWV_2_Freigabe : 32|1@1+ (1,0) [0|1] "" Bremsbooster - SG_ AWV_2_Umfeldwarn : 31|1@1+ (1,0) [0|1] "" Bremsbooster - SG_ AWV_2_SU_Lampe : 30|1@1+ (1,0) [0|1] "" Bremsbooster - SG_ AWV_2_SU_Gong : 29|1@1+ (1,0) [0|1] "" Bremsbooster - SG_ AWV_2_SU_Bremsruck : 28|1@1+ (1,0) [0|1] "" Bremsbooster - SG_ AWV_2_SU_Warnzeit : 26|2@1+ (1,0) [0|3] "" Bremsbooster - SG_ AWV_2_Fehler : 25|1@1+ (1,0) [0|1] "" Gateway_Kbi_VW_A,Bremsbooster - SG_ AWV_2_Status : 24|1@1+ (1,0) [0|1] "" Gateway_Kbi_VW_A,Bremsbooster - SG_ AWV_res_20 : 20|4@1+ (1,0) [0|0] "" Bremsbooster - SG_ AWV_1_Parameter : 18|2@1+ (1,0) [0|3] "" Bremsbooster - SG_ AWV_1_Prefill : 17|1@1+ (1,0) [0|1] "" Bremsbooster - SG_ AWV_1_Freigabe : 16|1@1+ (1,0) [0|1] "" Bremsbooster - SG_ AWV_Text : 12|4@1+ (1,0) [0|15] "" Gateway_Kbi_VW_A,Bremsbooster - SG_ AWV_Zaehler : 8|4@1+ (1,0) [0|15] "" Bremsbooster - SG_ AWV_Checksumme : 0|8@1+ (1,0) [0|255] "" Bremsbooster - +BO_ 870 AWV: 8 XXX + SG_ CHECKSUM : 0|8@1+ (1,0) [0|255] "" Vector__XXX + SG_ COUNTER : 8|4@1+ (1,0) [0|15] "" Vector__XXX + SG_ AWV_Text : 12|4@1+ (1,0) [0|14] "" Vector__XXX + SG_ AWV_1_Freigabe : 16|1@1+ (1,0) [0|1] "" Vector__XXX + SG_ AWV_1_Prefill : 17|1@1+ (1,0) [0|1] "" Vector__XXX + SG_ AWV_1_Parameter : 18|2@1+ (1,0) [0|3] "" Vector__XXX + SG_ AWV_only : 20|4@1+ (1,0) [0|0] "" Vector__XXX + SG_ AWV_CityANB_Auspraegung : 21|1@1+ (1,0) [0|1] "" Vector__XXX + SG_ AWV_Halten : 22|1@1+ (1,0) [0|1] "" Vector__XXX + SG_ ANB_Teilbremsung_Freigabe : 23|1@1+ (1,0) [0|1] "" Vector__XXX + SG_ AWV_2_Status : 24|1@1+ (1,0) [0|1] "" Vector__XXX + SG_ AWV_2_Fehler : 25|1@1+ (1,0) [0|1] "" Vector__XXX + SG_ AWV_2_SU_Warnzeit : 26|2@1+ (1,0) [0|3] "" Vector__XXX + SG_ AWV_2_SU_Bremsruck : 28|1@1+ (1,0) [0|1] "" Vector__XXX + SG_ AWV_2_SU_Gong : 29|1@1+ (1,0) [0|1] "" Vector__XXX + SG_ AWV_2_SU_Lampe : 30|1@1+ (1,0) [0|1] "" Vector__XXX + SG_ AWV_2_Umfeldwarn : 31|1@1+ (1,0) [0|1] "" Vector__XXX + SG_ AWV_2_Freigabe : 32|1@1+ (1,0) [0|1] "" Vector__XXX + SG_ AWV_2_Ruckprofil : 33|3@1+ (1,0) [0|7] "" Vector__XXX + SG_ AWV_2_Warnton : 36|1@1+ (1,0) [0|1] "" Vector__XXX + SG_ AWV_2_Warnsymbol : 37|1@1+ (1,0) [0|1] "" Vector__XXX + SG_ AWV_Infoton : 38|1@1+ (1,0) [0|1] "" Vector__XXX + SG_ AWV_2_Gurtstraffer : 39|1@1+ (1,0) [0|1] "" Vector__XXX + SG_ AWV_Konfiguration_Menueanf : 40|1@1+ (1,0) [0|1] "" Vector__XXX + SG_ AWV_Konfiguration_Vorw_Menueanf : 41|1@1+ (1,0) [0|1] "" Vector__XXX + SG_ AWV_Konfiguration_Status : 42|1@1+ (1,0) [0|1] "" Vector__XXX + SG_ AWV_Konfiguration_Vorw_Status : 43|1@1+ (1,0) [0|1] "" Vector__XXX + SG_ AWV_2_Abstandswarnung : 51|1@1+ (1,0) [0|1] "" Vector__XXX + SG_ ANB_Zielbremsung_Freigabe : 52|1@1+ (1,0) [0|1] "" Vector__XXX + SG_ ANB_CM_Anforderung : 53|1@1+ (1,0) [0|1] "" Vector__XXX + SG_ ANB_Ziel_Teilbrems_Verz_Anf : 54|10@1+ (0.024,-20.016) [0|1023] "Unit_MeterPerSeconSquar" Vector__XXX + BO_ 1470 LDW_Status: 8 XXX SG_ LDW_Lernmodus_rechts : 0|2@1+ (1,0) [0|3] "" XXX SG_ LDW_Lernmodus_links : 2|2@1+ (1,0) [0|3] "" XXX @@ -1473,6 +1486,7 @@ CM_ SG_ 640 mechanisches_Motor_Verlustmomen "Mechanical Torque Loss"; CM_ SG_ 640 Fahrpedalwert_oder_Drosselklapp "Accelerator Pedal or Throttle Position"; CM_ SG_ 640 Motordrehzahl "Engine Speed"; CM_ SG_ 640 Momentenangaben_ungenau "Approximate Torque Values"; +CM_ SG_ 640 inneres_Motor_Moment_ohne_exter "Inner torque without external"; CM_ SG_ 644 MOB_CHECKSUM "Checksum MOB"; CM_ SG_ 644 MOB_COUNTER "Counter MOB"; @@ -1503,6 +1517,9 @@ CM_ SG_ 912 BSK_HD_Hauptraste "Status of trunk lid main detent"; CM_ SG_ 1088 Zaehler_Getriebe_1 "Counter Getriebe_1"; CM_ SG_ 1088 Waehlhebelposition__Getriebe_1_ "Gear Selector Position"; +CM_ SG_ 1088 inneres_Soll_Motormoment "Desired Inner Torque"; +CM_ SG_ 1088 Schaltabsicht "Shift Intent"; +CM_ SG_ 1088 Wandlerverlustmoment "Converter Torque Loss"; CM_ SG_ 1056 Fehlerstatus_Aussentemp__4_1 "ambient temp error"; CM_ SG_ 1056 Fehlerstatus_Oeltemperatur_4_1 "oil temp error"; @@ -1513,15 +1530,30 @@ CM_ SG_ 1056 Kuehlmitteltemp__4_1__Kombi_2_ "kombi coolant temperature"; CM_ SG_ 1096 Zaehler_Waehlhebel_1 "Counter Waehlhebel_1"; -CM_ SG_ 1152 Checksumme_Motor_5 "Checksum Motor_5"; +CM_ SG_ 1152 CHECKSUM "Checksum Motor_5"; +CM_ SG_ 1152 Anlasser_Ausspuren "Starter Disable"; +CM_ SG_ 1152 Anlasser_Freigabe "Starter Release"; +CM_ SG_ 1152 Klimadrucksignal__Motor_5_ "Air conditioning pressure signal"; +CM_ SG_ 1152 Kraftstoffverbrauchssignal "Fuel consumption signal"; +CM_ SG_ 1152 K_hlerluefteransteuerung "Cooling fan control signal"; +CM_ SG_ 1152 Klimakompressor_Leistungsreduzi "Air conditioning compressor power reduction flag"; +CM_ SG_ 1152 Klimakompressor_aus__Motor_5_ "Air conditioning compressor"; +CM_ SG_ 1152 Anlasser_Freigabe "Starter release"; +CM_ SG_ 1152 OBD_2_Lampe "OBD light"; +CM_ SG_ 1152 E_Gas_Lampe "ETB light"; +CM_ SG_ 1152 Ladekontroll_Lampe "Charge light"; +CM_ SG_ 1152 Vorgluehlampe__Motor_5_ "Glow light"; CM_ SG_ 1160 Zaehler_Motor_6 "Counter Motor_6"; CM_ SG_ 1160 Hoeheninfo__Motor_6_ "Altitude Correction"; -CM_ SG_ 1160 Istmoment_f_r_Getriebe "Actual Torque"; -CM_ SG_ 1160 Sollmoment_f_r_Getriebe "Target Torque"; +CM_ SG_ 1160 Istmoment_f_r_Getriebe "Actual torque for gear"; +CM_ SG_ 1160 Sollmoment_f_r_Getriebe "Target torque for gearbox"; CM_ SG_ 1160 Checksumme_Motor_6 "Checksum Motor_6"; +CM_ SG_ 1160 GRA_Sollbeschleunigung "GRA target acceleration"; +CM_ SG_ 1160 Ruckmeldung_Momenten "Feedback torque-integral gear intervention"; CM_ SG_ 1344 Zahler_Getriebe_2 "Counter Getriebe_2"; +CM_ SG_ 1344 Hochschaltlampe "Upshift Flag"; CM_ SG_ 1386 ACA_V_Wunsch "255=unset"; @@ -1536,6 +1568,9 @@ CM_ SG_ 1408 Ansaugsystem "Induction System"; CM_ SG_ 1408 Motorleistung "Maximum engine power"; CM_ SG_ 1416 Ladedruck "Boost Pressure"; +CM_ SG_ 1416 Motordrehzahlgradient "Engine speed gradient"; +CM_ SG_ 1416 Hoeheninfo__Motor_7_ "Altitude correction factor"; +CM_ SG_ 1416 Oltemperatur "Oil temperature"; CM_ SG_ 1470 LDW_Direction "0=right,1=left"; CM_ SG_ 1470 XX_DLCORTLC1 "Might be DLC or TLC"; @@ -1543,6 +1578,34 @@ CM_ SG_ 1470 XX_DLCORTLC2 "Might be DLC or TLC, might have wrong size"; CM_ SG_ 1550 MFA_v_Signal_02 "0=km/h, 1=mph"; +VAL_ 870 AWV_Text 0 "kein_Text" 1 "FrontAssist_aus" 2 "FrontAssist_startet" 3 "FrontAssist_Warnung" 4 "FrontAssist_Sens_reinig" 5 "FrontAssist_Failure" 6 "FrontAssist_Demo" 7 "Vorhalt" 8 "Bremsung_wird_gerade_durchgefuehrt" 9 "Sensor_not_verfuegbar" 10 "Sensor_reinigen" 11 "Service_notwendig_Failure" 12 "Funktion_vom_Fahrer_deactivated" 13 "Funktion_vom_Fahrer_activated" 14 "FrontAssist_zur_Zeit_not_verfuegbar__rev_Failure"; +VAL_ 870 AWV_1_Freigabe 0 "nicht_freigegeben" 1 "freigegeben"; +VAL_ 870 AWV_1_Prefill 0 "keine_Prefill_Anf" 1 "Prefill_Anf"; +VAL_ 870 AWV_1_Parameter 0 "Defaultparametersatz" 1 "Par_leicht_erh_Empf" 2 "Par_erh_Empf" 3 "Par_hoechster_Empf"; +VAL_ 870 AWV_only 0 "ACC_und_AWV_verbaut" 1 "AWV_ohne_ACC_verbaut"; +VAL_ 870 AWV_CityANB_Auspraegung 0 "autom_Bremsung_im_ges_vBereich" 1 "autom_Bremsung_im_def_vBereich"; +VAL_ 870 AWV_Halten 0 "keine_Anforderung" 1 "Anforderung_das_Fzg_im_Stillstand_zu_halten"; +VAL_ 870 ANB_Teilbremsung_Freigabe 0 "Teilbremsung_nicht_freigegeben" 1 "Teilbremsung_freigegeben"; +VAL_ 870 AWV_2_Status 0 "Lampe_aus" 1 "Lampe_ein"; +VAL_ 870 AWV_2_Fehler 0 "Lampe_aus" 1 "Lampe_ein"; +VAL_ 870 AWV_2_SU_Warnzeit 0 "frueh" 1 "normal" 2 "spaet" 3 "adaptiv"; +VAL_ 870 AWV_2_SU_Bremsruck 0 "Bremsruck_deaktiviert" 1 "Bremsruck_aktiviert"; +VAL_ 870 AWV_2_SU_Gong 0 "Gong_deaktiviert" 1 "Gong_aktiviert"; +VAL_ 870 AWV_2_SU_Lampe 0 "Lampe_deaktiviert" 1 "Lampe_aktiviert"; +VAL_ 870 AWV_2_Umfeldwarn 0 "keine_Warnung" 1 "Warnung"; +VAL_ 870 AWV_2_Freigabe 0 "keine_Ruckfreigabe" 1 "Ruckfreigabe"; +VAL_ 870 AWV_2_Ruckprofil 0 "kein_Ruck" 1 "Ruckprofil_1" 2 "Ruckprofil_2" 3 "Ruckprofil_3" 4 "Ruckprofil_4" 5 "Ruckprofil_5" 6 "not_erlaubt" 7 "not_erlaubt"; +VAL_ 870 AWV_2_Warnton 0 "Aus" 1 "Ein"; +VAL_ 870 AWV_2_Warnsymbol 0 "Aus" 1 "Ein"; +VAL_ 870 AWV_Infoton 0 "Aus" 1 "Ein"; +VAL_ 870 AWV_2_Gurtstraffer 0 "Gurt_not_straffen" 1 "Gurt_straffen"; +VAL_ 870 AWV_Konfiguration_Menueanf 0 "Menue_deaktivieren" 1 "Menue_aktivieren"; +VAL_ 870 AWV_Konfiguration_Vorw_Menueanf 0 "Menue_deaktivieren" 1 "Menue_aktivieren"; +VAL_ 870 AWV_Konfiguration_Status 0 "AWV_inaktiv" 1 "AWV_aktiv"; +VAL_ 870 AWV_Konfiguration_Vorw_Status 0 "AWV_Vorwarnung_inaktiv" 1 "AWV_Vorwarnung_aktiv"; +VAL_ 870 AWV_2_Abstandswarnung 0 "kein_Warnhinweis" 1 "Warnhinweis"; +VAL_ 870 ANB_Zielbremsung_Freigabe 0 "Zielbremsung_nicht_freigegeben" 1 "Zielbremsung_freigegeben"; +VAL_ 870 ANB_CM_Anforderung 0 "keine_Anforderung" 1 "Anforderung_aktiv"; VAL_ 872 ACS_Sta_ADR 2 "ADR_passiv" 0 "ADR_nicht_aktiv" 1 "ADR_aktiv" 3 "irrev_Fehler" ; VAL_ 872 ACS_ADR_Schub 1 "Verz_begr_auf_Schub" 0 "Verz_nicht_begr_auf_Schub" ; @@ -1577,3 +1640,30 @@ VAL_ 1386 ACA_ID_StaGRA 0 "keine_Anzeige" ; VAL_ 1386 ACA_Codierung 0 "ACC" 1 "GRA" ; VAL_ 1386 ACA_Tachokranz 0 "nicht_beleuchtet" 1 "beleuchtet" ; VAL_ 1386 ACA_Aend_Zeitluecke 1 "Anzeige_angef" 0 "keine_Anzeige" ; + +VAL_ 1472 EP1_Fehler_Sta 0 "volle_Funktion" 1 "linke_Seite_fehlerhaft" 2 "rechte_Seite_fehlerhaft" 3 "beide_Seiten_fehlerhaft"; +VAL_ 1472 EP1_Sta_EPB 0 "Bremse_geoeffnet" 1 "Bremse_geschlossen"; +VAL_ 1472 EP1_Sta_Schalter 0 "volle_Funktion" 1 "Schalter_ausser_Funktion"; +VAL_ 1472 EP1_Spannkraft 31 "Fehler"; +VAL_ 1472 EP1_Schalterinfo 0 "keine_Fahreranforderung" 1 "Fahreranforderung_oeffnen" 2 "Fahreranforderung_schliessen" 3 "Schalterfehler"; +VAL_ 1472 EP1_Sta_NWS 0 "volle_Funktion" 1 "keine_Funktion"; +VAL_ 1472 EP1_Fehlereintr 0 "kein_Fehlerspeichereintrag" 1 "Fehlerspeichereintrag"; +VAL_ 1472 EP1_Freigabe_Ver 0 "Verzoegerungsanf_nicht_freigegeb" 1 "Verzoegerungsanf_freigegeben"; +VAL_ 1472 EP1_AutoHold_zul 0 "Pers_nicht_zulaessig" 1 "Pers_zulaessig"; +VAL_ 1472 EP1_AutoHold_aktiv 0 "nein" 1 "ja"; +VAL_ 1472 EP1_SleepInd 0 "CAN_wird_benoetigt" 1 "Sleep_bereit"; +VAL_ 1472 EP1_Status_Kl_15 0 "Kl_15_aus" 1 "Kl_15_ein"; +VAL_ 1472 EP1_Lampe_AutoP 0 "Lampe_aus" 1 "Lampe_ein"; +VAL_ 1472 EP1_Bremslicht 0 "Aus" 1 "Ein"; +VAL_ 1472 EP1_Warnton1 0 "Aus" 1 "Ein"; +VAL_ 1472 EP1_Warnton2 0 "Aus" 1 "Ein"; +VAL_ 1472 EP1_AnfShLock 0 "Aus" 1 "Ein"; +VAL_ 1472 EPB_Autoholdlampe 0 "Lampe_aus" 1 "Lampe_ein"; +VAL_ 1472 EP1_QualNeigWi 0 "gueltiger_Wert" 1 "Ersatz_Init_oder_Fehlerwert"; +VAL_ 1472 EP1_KuppModBer 0 "Kuppsensor_aus_Modbereich" 1 "Kupplsensor_im_Modbereich" 2 "Sensorsignal_ungenau" 3 "Sensor_defekt"; +VAL_ 1472 EP1_HydrHalten 0 "Fzg_nicht_hydr_geh" 1 "Fzg_hydr_geh"; +VAL_ 1472 EP1_Fkt_Lampe 0 "Lampe_aus" 1 "Lampe_ein"; +VAL_ 1472 EP1_Warnton 0 "Warnton_aus" 1 "Warnton_an"; +VAL_ 1472 EP1_Fehler_BKL 0 "BKL_aus" 1 "BKL_an"; +VAL_ 1472 EP1_Fehler_gelb 0 "Lampe_aus" 1 "Lampe_ein"; +VAL_ 1472 EP1__Text 0 "kein_Text" 1 "Text_1" 2 "Text_2" 3 "Text_3" 4 "Text_4" 5 "Text_5" 6 "reserviert" 7 "reserviert" 8 "Text_8"; diff --git a/panda/board/obj/bootstub.panda.bin b/panda/board/obj/bootstub.panda.bin index 4502f0f47..c05333850 100755 Binary files a/panda/board/obj/bootstub.panda.bin and b/panda/board/obj/bootstub.panda.bin differ diff --git a/panda/board/obj/panda.bin.signed b/panda/board/obj/panda.bin.signed index 9efd18206..c5ec66eb6 100644 Binary files a/panda/board/obj/panda.bin.signed and b/panda/board/obj/panda.bin.signed differ diff --git a/panda/python/__init__.py b/panda/python/__init__.py index 227f76932..ccef44300 100644 --- a/panda/python/__init__.py +++ b/panda/python/__init__.py @@ -529,13 +529,15 @@ class Panda: return True @staticmethod - def wait_for_dfu(dfu_serial: str, timeout: Optional[int] = None) -> bool: + def wait_for_dfu(dfu_serial: Optional[str], timeout: Optional[int] = None) -> bool: t_start = time.monotonic() - while dfu_serial not in PandaDFU.list(): + dfu_list = PandaDFU.list() + while (dfu_serial is None and len(dfu_list) == 0) or (dfu_serial is not None and dfu_serial not in dfu_list): logging.debug("waiting for DFU...") time.sleep(0.1) if timeout is not None and (time.monotonic() - t_start) > timeout: return False + dfu_list = PandaDFU.list() return True def up_to_date(self) -> bool: @@ -626,12 +628,6 @@ class Panda: # ******************* control ******************* - def enter_bootloader(self): - try: - self._handle.controlWrite(Panda.REQUEST_OUT, 0xd1, 0, 0, b'') - except Exception: - logging.exception("exception while entering bootloader") - def get_version(self): return self._handle.controlRead(Panda.REQUEST_IN, 0xd6, 0, 0, 0x40).decode('utf8') diff --git a/panda/python/usb.py b/panda/python/usb.py index 2e236a999..1487f101b 100644 --- a/panda/python/usb.py +++ b/panda/python/usb.py @@ -77,7 +77,7 @@ class STBootloaderUSBHandle(BaseSTBootloaderHandle): self._status() # Program - bs = self._mcu_type.config.block_size + bs = min(len(dat), self._mcu_type.config.block_size) dat += b"\xFF" * ((bs - len(dat)) % bs) for i in range(0, len(dat) // bs): ldat = dat[i * bs:(i + 1) * bs] diff --git a/rednose/helpers/ekf_sym_pyx.so b/rednose/helpers/ekf_sym_pyx.so index 3b4ab2d52..1d25035bf 100755 Binary files a/rednose/helpers/ekf_sym_pyx.so and b/rednose/helpers/ekf_sym_pyx.so differ diff --git a/selfdrive/assets/locales/events.pot b/selfdrive/assets/locales/events.pot index 8918db1e5..69c551213 100644 --- a/selfdrive/assets/locales/events.pot +++ b/selfdrive/assets/locales/events.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-05-24 11:59+0800\n" +"POT-Creation-Date: 2023-06-12 11:20+0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -465,15 +465,12 @@ msgstr "" #: ../selfdrive/controls/lib/events.py:830 #: ../selfdrive/controls/lib/events.py:832 -#: ../selfdrive/controls/lib/events.py:897 -#: ../selfdrive/controls/lib/events.py:899 -#: ../selfdrive/controls/lib/events.py:956 +#: ../selfdrive/controls/lib/events.py:950 msgid "Cruise Fault: Restart the Car" msgstr "" #: ../selfdrive/controls/lib/events.py:831 -#: ../selfdrive/controls/lib/events.py:898 -#: ../selfdrive/controls/lib/events.py:955 +#: ../selfdrive/controls/lib/events.py:949 msgid "Cruise Fault: Restart the car to engage" msgstr "" @@ -530,73 +527,73 @@ msgstr "" msgid "LKAS Fault: Restart the car to engage" msgstr "" -#: ../selfdrive/controls/lib/events.py:904 +#: ../selfdrive/controls/lib/events.py:898 msgid "" "Reverse\n" "Gear" msgstr "" -#: ../selfdrive/controls/lib/events.py:908 -#: ../selfdrive/controls/lib/events.py:909 +#: ../selfdrive/controls/lib/events.py:902 +#: ../selfdrive/controls/lib/events.py:903 msgid "Reverse Gear" msgstr "" -#: ../selfdrive/controls/lib/events.py:915 +#: ../selfdrive/controls/lib/events.py:909 msgid "Cruise Is Off" msgstr "" -#: ../selfdrive/controls/lib/events.py:922 -#: ../selfdrive/controls/lib/events.py:923 +#: ../selfdrive/controls/lib/events.py:916 +#: ../selfdrive/controls/lib/events.py:917 msgid "Planner Solution Error" msgstr "" -#: ../selfdrive/controls/lib/events.py:931 -#: ../selfdrive/controls/lib/events.py:932 -#: ../selfdrive/controls/lib/events.py:933 +#: ../selfdrive/controls/lib/events.py:925 +#: ../selfdrive/controls/lib/events.py:926 +#: ../selfdrive/controls/lib/events.py:927 msgid "Harness Relay Malfunction" msgstr "" -#: ../selfdrive/controls/lib/events.py:932 +#: ../selfdrive/controls/lib/events.py:926 msgid "Check Hardware" msgstr "" -#: ../selfdrive/controls/lib/events.py:938 +#: ../selfdrive/controls/lib/events.py:932 msgid "openpilot Canceled" msgstr "" -#: ../selfdrive/controls/lib/events.py:939 +#: ../selfdrive/controls/lib/events.py:933 msgid "Speed too low" msgstr "" -#: ../selfdrive/controls/lib/events.py:947 +#: ../selfdrive/controls/lib/events.py:941 msgid "Speed Too High" msgstr "" -#: ../selfdrive/controls/lib/events.py:948 +#: ../selfdrive/controls/lib/events.py:942 msgid "Model uncertain at this speed" msgstr "" -#: ../selfdrive/controls/lib/events.py:951 +#: ../selfdrive/controls/lib/events.py:945 msgid "Slow down to engage" msgstr "" -#: ../selfdrive/controls/lib/events.py:960 +#: ../selfdrive/controls/lib/events.py:954 msgid "LKAS Disabled: Enable LKAS to engage" msgstr "" -#: ../selfdrive/controls/lib/events.py:961 +#: ../selfdrive/controls/lib/events.py:955 msgid "LKAS Disabled" msgstr "" -#: ../selfdrive/controls/lib/events.py:965 +#: ../selfdrive/controls/lib/events.py:959 msgid "Vehicle Sensors Invalid" msgstr "" -#: ../selfdrive/controls/lib/events.py:966 -#: ../selfdrive/controls/lib/events.py:967 +#: ../selfdrive/controls/lib/events.py:960 +#: ../selfdrive/controls/lib/events.py:961 msgid "Vehicle Sensors Calibrating" msgstr "" -#: ../selfdrive/controls/lib/events.py:966 +#: ../selfdrive/controls/lib/events.py:960 msgid "Drive to Calibrate" msgstr "" diff --git a/selfdrive/assets/locales/ja-JP/LC_MESSAGES/events.po b/selfdrive/assets/locales/ja-JP/LC_MESSAGES/events.po index c1e8731ac..819b5a9ea 100644 --- a/selfdrive/assets/locales/ja-JP/LC_MESSAGES/events.po +++ b/selfdrive/assets/locales/ja-JP/LC_MESSAGES/events.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-05-24 11:41+0800\n" +"POT-Creation-Date: 2023-06-08 16:04+0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: nikkurie <@nikkurie>\n" "Language-Team: LANGUAGE \n" @@ -502,15 +502,12 @@ msgstr "ローメモリ:デバイスを再起動" #: ../selfdrive/controls/lib/events.py:830 #: ../selfdrive/controls/lib/events.py:832 -#: ../selfdrive/controls/lib/events.py:897 -#: ../selfdrive/controls/lib/events.py:899 -#: ../selfdrive/controls/lib/events.py:956 +#: ../selfdrive/controls/lib/events.py:950 msgid "Cruise Fault: Restart the Car" msgstr "クルーズ失敗:車を再起動" #: ../selfdrive/controls/lib/events.py:831 -#: ../selfdrive/controls/lib/events.py:898 -#: ../selfdrive/controls/lib/events.py:955 +#: ../selfdrive/controls/lib/events.py:949 msgid "Cruise Fault: Restart the car to engage" msgstr "クルーズ失敗:車を再起動後発進" @@ -569,78 +566,78 @@ msgstr "LKASの故障:車を再起動" msgid "LKAS Fault: Restart the car to engage" msgstr "LKASの故障:車を再起動後発進" -#: ../selfdrive/controls/lib/events.py:904 +#: ../selfdrive/controls/lib/events.py:898 #, fuzzy msgid "" "Reverse\n" "Gear" msgstr "Rに切り替え" -#: ../selfdrive/controls/lib/events.py:908 -#: ../selfdrive/controls/lib/events.py:909 +#: ../selfdrive/controls/lib/events.py:902 +#: ../selfdrive/controls/lib/events.py:903 msgid "Reverse Gear" msgstr "Rに切り替え" -#: ../selfdrive/controls/lib/events.py:915 +#: ../selfdrive/controls/lib/events.py:909 msgid "Cruise Is Off" msgstr "クルーズコントロールオフ" -#: ../selfdrive/controls/lib/events.py:922 -#: ../selfdrive/controls/lib/events.py:923 +#: ../selfdrive/controls/lib/events.py:916 +#: ../selfdrive/controls/lib/events.py:917 msgid "Planner Solution Error" msgstr "Planner Solution エラー" -#: ../selfdrive/controls/lib/events.py:931 -#: ../selfdrive/controls/lib/events.py:932 -#: ../selfdrive/controls/lib/events.py:933 +#: ../selfdrive/controls/lib/events.py:925 +#: ../selfdrive/controls/lib/events.py:926 +#: ../selfdrive/controls/lib/events.py:927 #, fuzzy msgid "Harness Relay Malfunction" msgstr "ハーネスが故障" -#: ../selfdrive/controls/lib/events.py:932 +#: ../selfdrive/controls/lib/events.py:926 #, fuzzy msgid "Check Hardware" msgstr "ハードウェアを確認して" -#: ../selfdrive/controls/lib/events.py:938 +#: ../selfdrive/controls/lib/events.py:932 msgid "openpilot Canceled" msgstr "オープンパイロットはキャンセルされました" -#: ../selfdrive/controls/lib/events.py:939 +#: ../selfdrive/controls/lib/events.py:933 msgid "Speed too low" msgstr "速度が遅すぎる" -#: ../selfdrive/controls/lib/events.py:947 +#: ../selfdrive/controls/lib/events.py:941 msgid "Speed Too High" msgstr "速度が速すぎる" -#: ../selfdrive/controls/lib/events.py:948 +#: ../selfdrive/controls/lib/events.py:942 msgid "Model uncertain at this speed" msgstr "" -#: ../selfdrive/controls/lib/events.py:951 +#: ../selfdrive/controls/lib/events.py:945 msgid "Slow down to engage" msgstr "速度を落として発進" -#: ../selfdrive/controls/lib/events.py:960 +#: ../selfdrive/controls/lib/events.py:954 #, fuzzy msgid "LKAS Disabled: Enable LKAS to engage" msgstr "LKASの故障:車を再起動後発進" -#: ../selfdrive/controls/lib/events.py:961 +#: ../selfdrive/controls/lib/events.py:955 msgid "LKAS Disabled" msgstr "" -#: ../selfdrive/controls/lib/events.py:965 +#: ../selfdrive/controls/lib/events.py:959 msgid "Vehicle Sensors Invalid" msgstr "" -#: ../selfdrive/controls/lib/events.py:966 -#: ../selfdrive/controls/lib/events.py:967 +#: ../selfdrive/controls/lib/events.py:960 +#: ../selfdrive/controls/lib/events.py:961 msgid "Vehicle Sensors Calibrating" msgstr "" -#: ../selfdrive/controls/lib/events.py:966 +#: ../selfdrive/controls/lib/events.py:960 #, fuzzy msgid "Drive to Calibrate" msgstr "ドライバーは注意力散漫" diff --git a/selfdrive/assets/locales/ko-KR/LC_MESSAGES/events.po b/selfdrive/assets/locales/ko-KR/LC_MESSAGES/events.po index 52be0c87d..2f7d26dd6 100644 --- a/selfdrive/assets/locales/ko-KR/LC_MESSAGES/events.po +++ b/selfdrive/assets/locales/ko-KR/LC_MESSAGES/events.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-05-24 11:41+0800\n" +"POT-Creation-Date: 2023-06-08 16:04+0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -502,15 +502,12 @@ msgstr "메모리 부족: 장치를 재시작하세요" #: ../selfdrive/controls/lib/events.py:830 #: ../selfdrive/controls/lib/events.py:832 -#: ../selfdrive/controls/lib/events.py:897 -#: ../selfdrive/controls/lib/events.py:899 -#: ../selfdrive/controls/lib/events.py:956 +#: ../selfdrive/controls/lib/events.py:950 msgid "Cruise Fault: Restart the Car" msgstr "크루즈 오류: 차량을 재시작하세요" #: ../selfdrive/controls/lib/events.py:831 -#: ../selfdrive/controls/lib/events.py:898 -#: ../selfdrive/controls/lib/events.py:955 +#: ../selfdrive/controls/lib/events.py:949 msgid "Cruise Fault: Restart the car to engage" msgstr "크루즈 오류: 시작을 위해 차량을 재시작하세요" @@ -569,78 +566,78 @@ msgstr "LKAS 오류: 차량을 재시작하세요" msgid "LKAS Fault: Restart the car to engage" msgstr "LKAS 오류: 시작을 위해 차량을 재시작하세요" -#: ../selfdrive/controls/lib/events.py:904 +#: ../selfdrive/controls/lib/events.py:898 #, fuzzy msgid "" "Reverse\n" "Gear" msgstr "후진 기어" -#: ../selfdrive/controls/lib/events.py:908 -#: ../selfdrive/controls/lib/events.py:909 +#: ../selfdrive/controls/lib/events.py:902 +#: ../selfdrive/controls/lib/events.py:903 msgid "Reverse Gear" msgstr "후진 기어" -#: ../selfdrive/controls/lib/events.py:915 +#: ../selfdrive/controls/lib/events.py:909 msgid "Cruise Is Off" msgstr "크루즈 꺼짐" -#: ../selfdrive/controls/lib/events.py:922 -#: ../selfdrive/controls/lib/events.py:923 +#: ../selfdrive/controls/lib/events.py:916 +#: ../selfdrive/controls/lib/events.py:917 msgid "Planner Solution Error" msgstr "" -#: ../selfdrive/controls/lib/events.py:931 -#: ../selfdrive/controls/lib/events.py:932 -#: ../selfdrive/controls/lib/events.py:933 +#: ../selfdrive/controls/lib/events.py:925 +#: ../selfdrive/controls/lib/events.py:926 +#: ../selfdrive/controls/lib/events.py:927 #, fuzzy msgid "Harness Relay Malfunction" msgstr "하네스 오작동" -#: ../selfdrive/controls/lib/events.py:932 +#: ../selfdrive/controls/lib/events.py:926 #, fuzzy msgid "Check Hardware" msgstr "장치를 점검하세요" -#: ../selfdrive/controls/lib/events.py:938 +#: ../selfdrive/controls/lib/events.py:932 msgid "openpilot Canceled" msgstr "오픈파일럿 시작불가" -#: ../selfdrive/controls/lib/events.py:939 +#: ../selfdrive/controls/lib/events.py:933 msgid "Speed too low" msgstr "선행차량이 없습니다" -#: ../selfdrive/controls/lib/events.py:947 +#: ../selfdrive/controls/lib/events.py:941 msgid "Speed Too High" msgstr "속도가 너무 높습니다" -#: ../selfdrive/controls/lib/events.py:948 +#: ../selfdrive/controls/lib/events.py:942 msgid "Model uncertain at this speed" msgstr "" -#: ../selfdrive/controls/lib/events.py:951 +#: ../selfdrive/controls/lib/events.py:945 msgid "Slow down to engage" msgstr "시작을 위해 차량의 속도를 낮추세요" -#: ../selfdrive/controls/lib/events.py:960 +#: ../selfdrive/controls/lib/events.py:954 #, fuzzy msgid "LKAS Disabled: Enable LKAS to engage" msgstr "LKAS 오류: 시작을 위해 차량을 재시작하세요" -#: ../selfdrive/controls/lib/events.py:961 +#: ../selfdrive/controls/lib/events.py:955 msgid "LKAS Disabled" msgstr "" -#: ../selfdrive/controls/lib/events.py:965 +#: ../selfdrive/controls/lib/events.py:959 msgid "Vehicle Sensors Invalid" msgstr "" -#: ../selfdrive/controls/lib/events.py:966 -#: ../selfdrive/controls/lib/events.py:967 +#: ../selfdrive/controls/lib/events.py:960 +#: ../selfdrive/controls/lib/events.py:961 msgid "Vehicle Sensors Calibrating" msgstr "" -#: ../selfdrive/controls/lib/events.py:966 +#: ../selfdrive/controls/lib/events.py:960 #, fuzzy msgid "Drive to Calibrate" msgstr "운전자 전방주시 불안" diff --git a/selfdrive/assets/locales/zh-CN/LC_MESSAGES/events.mo b/selfdrive/assets/locales/zh-CN/LC_MESSAGES/events.mo index 257802e08..4edb7321a 100644 Binary files a/selfdrive/assets/locales/zh-CN/LC_MESSAGES/events.mo and b/selfdrive/assets/locales/zh-CN/LC_MESSAGES/events.mo differ diff --git a/selfdrive/assets/locales/zh-CN/LC_MESSAGES/events.po b/selfdrive/assets/locales/zh-CN/LC_MESSAGES/events.po index b236c19e5..029d0b2f3 100644 --- a/selfdrive/assets/locales/zh-CN/LC_MESSAGES/events.po +++ b/selfdrive/assets/locales/zh-CN/LC_MESSAGES/events.po @@ -7,32 +7,32 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-05-24 11:41+0800\n" +"POT-Creation-Date: 2023-06-08 16:04+0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Rick Lan \n" "Language-Team: LANGUAGE \n" -"Language: zh-CN\n" +"Language: zh-TW\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #: ../selfdrive/controls/lib/events.py:152 msgid "openpilot Unavailable" -msgstr "openpilot无法启动" +msgstr "无法使用 dragonpilot" #: ../selfdrive/controls/lib/events.py:161 #: ../selfdrive/controls/lib/events.py:176 msgid "TAKE CONTROL IMMEDIATELY" -msgstr "危险!立刻人工接管车辆!" +msgstr "立即接管控制" #: ../selfdrive/controls/lib/events.py:171 #, fuzzy msgid "openpilot will disengage" -msgstr "openpilot无法启动" +msgstr "dragonpilot 将解除" #: ../selfdrive/controls/lib/events.py:198 msgid "Always keep hands on wheel and eyes on road" -msgstr "将手放在方向盘上并持续注意路面情况" +msgstr "请保持双手握住方向盘并注意道路情况" #: ../selfdrive/controls/lib/events.py:235 msgid "WARNING: This branch is not tested" @@ -41,70 +41,70 @@ msgstr "注意:这个分支未经过测试" #: ../selfdrive/controls/lib/events.py:238 #, fuzzy, python-brace-format msgid "Drive above {speed} to engage" -msgstr "车速请高于 %(speed)d %(unit)s" +msgstr "请以超过 {speed} 的速度行驶以启动" #: ../selfdrive/controls/lib/events.py:243 #, fuzzy, python-format msgid "Steer Unavailable Below %s" -msgstr "转向控制暂时失效,车速低于 %d %s" +msgstr "在 %s 以下无法进行转向" #: ../selfdrive/controls/lib/events.py:250 #, fuzzy msgid "Recalibration" -msgstr "校准无效" +msgstr "重新校准" #: ../selfdrive/controls/lib/events.py:250 #, fuzzy msgid "Calibration" -msgstr "校准无效" +msgstr "校准" #: ../selfdrive/controls/lib/events.py:252 #, fuzzy, python-brace-format msgid "{word} in Progress: {perc}%" -msgstr "正在校准中:%d%%" +msgstr "{word}进行中:{perc}%" #: ../selfdrive/controls/lib/events.py:253 #, fuzzy, python-brace-format msgid "Drive Above {speed}" -msgstr "车速请高于 %(speed)d %(unit)s" +msgstr "行驶超过 {speed}" #: ../selfdrive/controls/lib/events.py:260 msgid "Poor GPS reception" -msgstr "GPS 信号不良" +msgstr "GPS 讯号不良" #: ../selfdrive/controls/lib/events.py:261 msgid "Hardware malfunctioning if sky is visible" -msgstr "" +msgstr "若天空可见,表示硬体出现故障" #: ../selfdrive/controls/lib/events.py:269 #: ../selfdrive/controls/lib/events.py:678 msgid "Out of Storage" -msgstr "存储空间不足" +msgstr "储存空间不足" #: ../selfdrive/controls/lib/events.py:269 #, python-format msgid "%s%% full" -msgstr "" +msgstr "%s%% 已满" #: ../selfdrive/controls/lib/events.py:275 #, python-brace-format msgid "Speed Error: {err:.1f} m/s" -msgstr "" +msgstr "速度误差:{err:.1f} m/s" #: ../selfdrive/controls/lib/events.py:276 #: ../selfdrive/controls/lib/events.py:806 msgid "Posenet Speed Invalid" -msgstr "" +msgstr "Posenet 速度无效" #: ../selfdrive/controls/lib/events.py:282 #: ../selfdrive/controls/lib/events.py:783 msgid "Process Not Running" -msgstr "" +msgstr "行程未执行" #: ../selfdrive/controls/lib/events.py:288 #, fuzzy msgid "Communication Issue Between Processes" -msgstr "进程间出现通讯问题" +msgstr "行程间出现通讯问题" #: ../selfdrive/controls/lib/events.py:294 #: ../selfdrive/controls/lib/events.py:571 @@ -114,7 +114,7 @@ msgstr "摄像头故障" #: ../selfdrive/controls/lib/events.py:301 #, python-brace-format msgid "Remount Device (Pitch: {pitch:.1f}°, Yaw: {yaw:.1f}°)" -msgstr "" +msgstr "重新安装设备(俯仰角: {pitch:.1f}°,偏航角: {yaw:.1f}°)" #: ../selfdrive/controls/lib/events.py:302 msgid "Calibration Invalid" @@ -128,21 +128,21 @@ msgstr "系统过热" #: ../selfdrive/controls/lib/events.py:313 msgid "Low Memory" -msgstr "内存过低" +msgstr "记忆体过低" #: ../selfdrive/controls/lib/events.py:313 #, python-format, python-brace-format msgid "{memory_usage_percent}% used" -msgstr "" +msgstr "已使用 {memory_usage_percent}% 的记忆体" #: ../selfdrive/controls/lib/events.py:318 msgid "High CPU Usage" -msgstr "" +msgstr "CPU 使用量偏高" #: ../selfdrive/controls/lib/events.py:318 #, python-format msgid "%s%% used" -msgstr "" +msgstr "已使用 %s%%" #: ../selfdrive/controls/lib/events.py:322 #: ../selfdrive/controls/lib/events.py:795 @@ -154,35 +154,36 @@ msgstr "操控模型有延迟" #: ../selfdrive/controls/lib/events.py:322 #, python-format, python-brace-format msgid "{frame_drop_perc:.1f}% frames dropped" -msgstr "" +msgstr "已遗失 {frame_drop_perc:.1f}% 帧数" #: ../selfdrive/controls/lib/events.py:326 #, fuzzy msgid "Enable Adaptive Cruise to Engage" -msgstr "启用自适应巡航" +msgstr "启用自适应巡航以启动" #: ../selfdrive/controls/lib/events.py:328 #, fuzzy msgid "Enable Main Switch to Engage" -msgstr "请于设置里启用社群开发功能" +msgstr "启用主开关以启动" #: ../selfdrive/controls/lib/events.py:335 #, python-brace-format msgid "Gas: {gas_percent}%, Steer: {steer_percent}%" -msgstr "" +msgstr "油门:{gas_percent}%,转向:{steer_percent}%" #: ../selfdrive/controls/lib/events.py:339 #: ../selfdrive/controls/lib/events.py:352 msgid "Joystick Mode" -msgstr "手柄控制模式" +msgstr "摇杆控制模式" #: ../selfdrive/controls/lib/events.py:356 +#, fuzzy msgid "System Initializing" -msgstr "" +msgstr "系统初始化中" #: ../selfdrive/controls/lib/events.py:360 msgid "Be ready to take over at any time" -msgstr "请准备好随时人工接管车辆" +msgstr "随时准备接管控制" #: ../selfdrive/controls/lib/events.py:369 #: ../selfdrive/controls/lib/events.py:370 @@ -191,12 +192,12 @@ msgstr "行车记录模式" #: ../selfdrive/controls/lib/events.py:375 msgid "Dashcam mode for unsupported car" -msgstr "行车记录模式 (尚未支持车型)" +msgstr "行车记录模式 (尚未支援车种)" #: ../selfdrive/controls/lib/events.py:379 #: ../selfdrive/controls/lib/events.py:403 msgid "Car Unrecognized" -msgstr "无法辨识车辆型号" +msgstr "无法辨识车款" #: ../selfdrive/controls/lib/events.py:380 msgid "Check comma power connections" @@ -214,7 +215,7 @@ msgstr "原厂 LKAS 已开启" #: ../selfdrive/controls/lib/events.py:391 msgid "Turn off stock LKAS to engage" -msgstr "请先关闭原厂 LKAS 才能启用" +msgstr "需关闭原厂 LKAS 才能启用" #: ../selfdrive/controls/lib/events.py:409 #: ../selfdrive/controls/lib/events.py:418 @@ -248,7 +249,7 @@ msgstr "横向控制暂时失效" #: ../selfdrive/controls/lib/events.py:457 #: ../selfdrive/controls/lib/events.py:465 msgid "Pay Attention" -msgstr "" +msgstr "请注视路况" #: ../selfdrive/controls/lib/events.py:466 #: ../selfdrive/controls/lib/events.py:474 @@ -263,11 +264,11 @@ msgstr "立即解除" #: ../selfdrive/controls/lib/events.py:481 #, fuzzy msgid "Touch Steering Wheel: No Face Detected" -msgstr "请触碰方向盘:未识别到驾驶者面容" +msgstr "请触碰方向盘:未侦测到驾驶面容" #: ../selfdrive/controls/lib/events.py:489 msgid "Touch Steering Wheel" -msgstr "" +msgstr "请触碰方向盘" #: ../selfdrive/controls/lib/events.py:490 #: ../selfdrive/controls/lib/events.py:498 @@ -276,11 +277,11 @@ msgstr "驾驶没有反应" #: ../selfdrive/controls/lib/events.py:505 msgid "TAKE CONTROL" -msgstr "人工接管控制" +msgstr "接管控制" #: ../selfdrive/controls/lib/events.py:506 msgid "Resume Driving Manually" -msgstr "请人工接管恢复驾驶" +msgstr "请自行恢復驾驶" #: ../selfdrive/controls/lib/events.py:513 #, fuzzy @@ -289,16 +290,16 @@ msgstr "请按 RES 继续" #: ../selfdrive/controls/lib/events.py:525 msgid "Steer Left to Start Lane Change Once Safe" -msgstr "确认安全后请往左轻转方向盘换道" +msgstr "确认安全后请往左轻打方向盘换道" #: ../selfdrive/controls/lib/events.py:533 msgid "Steer Right to Start Lane Change Once Safe" -msgstr "确认安全后请往右轻转方向盘换道" +msgstr "确认安全后请往右轻打方向盘换道" #: ../selfdrive/controls/lib/events.py:541 #, fuzzy msgid "Car Detected in Blindspot" -msgstr "盲区监测到车辆" +msgstr "盲点侦测到车辆" #: ../selfdrive/controls/lib/events.py:549 msgid "Changing Lanes" @@ -306,11 +307,11 @@ msgstr "切换车道中" #: ../selfdrive/controls/lib/events.py:557 msgid "Take Control" -msgstr "" +msgstr "请接手控制" #: ../selfdrive/controls/lib/events.py:558 msgid "Turn Exceeds Steering Limit" -msgstr "弯道幅度超过横向操控限制" +msgstr "弯道超过横向操控限制" #: ../selfdrive/controls/lib/events.py:565 msgid "Fan Malfunction" @@ -319,27 +320,27 @@ msgstr "散热风扇故障" #: ../selfdrive/controls/lib/events.py:565 #: ../selfdrive/controls/lib/events.py:583 msgid "Likely Hardware Issue" -msgstr "" +msgstr "硬体可能有问题" #: ../selfdrive/controls/lib/events.py:572 #, fuzzy msgid "Camera Malfunction: Reboot Your Device" -msgstr "系统错误:请重启您的设备" +msgstr "相机故障:请重新启动设备" #: ../selfdrive/controls/lib/events.py:576 #: ../selfdrive/controls/lib/events.py:577 msgid "Camera Frame Rate Low" -msgstr "" +msgstr "相机帧率偏低" #: ../selfdrive/controls/lib/events.py:576 #: ../selfdrive/controls/lib/events.py:700 msgid "Reboot your Device" -msgstr "请重启装置" +msgstr "请重启设备" #: ../selfdrive/controls/lib/events.py:578 #, fuzzy msgid "Camera Frame Rate Low: Reboot Your Device" -msgstr "系统错误:请重启您的设备" +msgstr "相机帧率低:请重新启动设备" #: ../selfdrive/controls/lib/events.py:583 msgid "GPS Malfunction" @@ -348,11 +349,11 @@ msgstr "GPS 故障" #: ../selfdrive/controls/lib/events.py:609 #, fuzzy msgid "Cancel Pressed" -msgstr "启用时侦测到驾驶踩踏油门/刹车" +msgstr "按下取消" #: ../selfdrive/controls/lib/events.py:614 msgid "Brake Hold Active" -msgstr "驻车刹车已启用" +msgstr "驻车煞车已启用" #: ../selfdrive/controls/lib/events.py:619 #, fuzzy @@ -362,17 +363,17 @@ msgstr "电子驻车已启动" #: ../selfdrive/controls/lib/events.py:624 #, fuzzy msgid "Pedal Pressed" -msgstr "启用时侦测到驾驶踩踏油门/刹车" +msgstr "踩下踏板" #: ../selfdrive/controls/lib/events.py:630 #, fuzzy msgid "Release Brake to Engage" -msgstr "电子驻车已启动" +msgstr "放开踏板以启动" #: ../selfdrive/controls/lib/events.py:658 #, fuzzy msgid "Press Set to Engage" -msgstr "电子驻车已启动" +msgstr "按下 SET 以启动" #: ../selfdrive/controls/lib/events.py:663 #, fuzzy @@ -383,31 +384,31 @@ msgstr "巡航模式关闭" #: ../selfdrive/controls/lib/events.py:673 #, fuzzy msgid "Vehicle Steering Time Limit" -msgstr "弯道幅度超过横向操控限制" +msgstr "车辆转向时间限制" #: ../selfdrive/controls/lib/events.py:687 #: ../selfdrive/controls/lib/events.py:691 #: ../selfdrive/controls/lib/events.py:692 msgid "Sensor Data Invalid" -msgstr "" +msgstr "感测器数据无效" #: ../selfdrive/controls/lib/events.py:688 msgid "Ensure device is mounted securely" -msgstr "" +msgstr "请确保设备已安装牢固" #: ../selfdrive/controls/lib/events.py:700 #: ../selfdrive/controls/lib/events.py:701 msgid "Speaker not found" -msgstr "找不到音效装置" +msgstr "找不到音效设备" #: ../selfdrive/controls/lib/events.py:705 msgid "Distraction Level Too High" -msgstr "驾驶分心次数过多" +msgstr "驾驶分心太多次" #: ../selfdrive/controls/lib/events.py:715 #: ../selfdrive/controls/lib/events.py:716 msgid "Gear not D" -msgstr "不在 D 档" +msgstr "不在 D 档位" #: ../selfdrive/controls/lib/events.py:726 #: ../selfdrive/controls/lib/events.py:727 @@ -426,12 +427,12 @@ msgstr "正在校准中" #: ../selfdrive/controls/lib/events.py:738 #, fuzzy msgid "Device Remount Detected: Recalibrating" -msgstr "请将设备放于新的位置并重新校准" +msgstr "侦测到装置重新安装:重新校准中" #: ../selfdrive/controls/lib/events.py:739 #, fuzzy msgid "Remount Detected: Recalibrating" -msgstr "请将设备放于新的位置并重新校准" +msgstr "侦测到重新安装:重新校准中" #: ../selfdrive/controls/lib/events.py:743 #: ../selfdrive/controls/lib/events.py:744 @@ -441,12 +442,12 @@ msgstr "车门开启" #: ../selfdrive/controls/lib/events.py:748 #: ../selfdrive/controls/lib/events.py:749 msgid "Seatbelt Unlatched" -msgstr "未系安全带" +msgstr "安全带未繫" #: ../selfdrive/controls/lib/events.py:753 #: ../selfdrive/controls/lib/events.py:754 msgid "Electronic Stability Control Disabled" -msgstr "" +msgstr "电子稳定控制已停用" #: ../selfdrive/controls/lib/events.py:758 #: ../selfdrive/controls/lib/events.py:759 @@ -455,13 +456,13 @@ msgstr "电量过低" #: ../selfdrive/controls/lib/events.py:767 msgid "Communication Issue between Processes" -msgstr "进程间出现通讯问题" +msgstr "行程间出现通讯问题" #: ../selfdrive/controls/lib/events.py:771 #: ../selfdrive/controls/lib/events.py:772 #, fuzzy msgid "Low Communication Rate between Processes" -msgstr "进程间出现通讯问题" +msgstr "行程间出现通讯问题" #: ../selfdrive/controls/lib/events.py:776 #, fuzzy @@ -471,36 +472,33 @@ msgstr "控制发生错误" #: ../selfdrive/controls/lib/events.py:777 #, fuzzy msgid "Controls Process Lagging: Reboot Your Device" -msgstr "系统错误:请重启您的设备" +msgstr "控制处理延迟:请重新启动装置" #: ../selfdrive/controls/lib/events.py:787 #: ../selfdrive/controls/lib/events.py:788 msgid "Radar Error: Restart the Car" -msgstr "雷达信号错误:请重新启动车辆" +msgstr "雷达讯号错误:请重新发动车辆" #: ../selfdrive/controls/lib/events.py:813 #: ../selfdrive/controls/lib/events.py:814 msgid "Device Fell Off Mount" -msgstr "设备已经掉落" +msgstr "设备掉落侦测" #: ../selfdrive/controls/lib/events.py:818 #: ../selfdrive/controls/lib/events.py:820 msgid "Low Memory: Reboot Your Device" -msgstr "内存不足:请重启您的装置" +msgstr "记忆体不足:请重启您的设备" #: ../selfdrive/controls/lib/events.py:830 #: ../selfdrive/controls/lib/events.py:832 -#: ../selfdrive/controls/lib/events.py:897 -#: ../selfdrive/controls/lib/events.py:899 -#: ../selfdrive/controls/lib/events.py:956 +#: ../selfdrive/controls/lib/events.py:950 msgid "Cruise Fault: Restart the Car" -msgstr "巡航系统错误:请重新启动车辆" +msgstr "巡航系统错误:请重新发动车辆" #: ../selfdrive/controls/lib/events.py:831 -#: ../selfdrive/controls/lib/events.py:898 -#: ../selfdrive/controls/lib/events.py:955 +#: ../selfdrive/controls/lib/events.py:949 msgid "Cruise Fault: Restart the car to engage" -msgstr "巡航系统错误:请重新启动车辆" +msgstr "巡航系统错误:请重新发动车辆" #: ../selfdrive/controls/lib/events.py:836 #: ../selfdrive/controls/lib/events.py:837 @@ -510,135 +508,136 @@ msgstr "控制不匹配" #: ../selfdrive/controls/lib/events.py:841 #, fuzzy msgid "Camera CRC Error - Road" -msgstr "道路摄像头错误" +msgstr "相机 CRC 错误 - 道路" #: ../selfdrive/controls/lib/events.py:847 msgid "Camera CRC Error - Road Fisheye" -msgstr "" +msgstr "相机 CRC 错误 - 道路鱼眼" #: ../selfdrive/controls/lib/events.py:853 msgid "Camera CRC Error - Driver" -msgstr "" +msgstr "相机 CRC 错误 - 驾驶" #: ../selfdrive/controls/lib/events.py:861 #: ../selfdrive/controls/lib/events.py:862 #: ../selfdrive/controls/lib/events.py:863 msgid "USB Error: Reboot Your Device" -msgstr "USB 错误:请重启您的装置" +msgstr "USB 错误:请重启您的设备" #: ../selfdrive/controls/lib/events.py:871 msgid "CAN Error" -msgstr "" +msgstr "CAN 错误" #: ../selfdrive/controls/lib/events.py:873 #: ../selfdrive/controls/lib/events.py:877 msgid "CAN Error: Check Connections" -msgstr "CAN 信号错误:请检查线路" +msgstr "CAN 讯号错误:请检查线路" #: ../selfdrive/controls/lib/events.py:881 msgid "CAN Bus Disconnected" -msgstr "" +msgstr "CAN 通讯已中断" #: ../selfdrive/controls/lib/events.py:883 msgid "CAN Bus Disconnected: Likely Faulty Cable" -msgstr "" +msgstr "CAN 通讯已中断:线组可能出错" #: ../selfdrive/controls/lib/events.py:887 #, fuzzy msgid "CAN Bus Disconnected: Check Connections" -msgstr "CAN 信号错误:请检查线路" +msgstr "CAN 通讯已中断:请检查线路" #: ../selfdrive/controls/lib/events.py:891 #: ../selfdrive/controls/lib/events.py:893 msgid "LKAS Fault: Restart the Car" -msgstr "LKAS 错误:请重新启动车辆" +msgstr "LKAS 错误:请重新发动车辆" #: ../selfdrive/controls/lib/events.py:892 msgid "LKAS Fault: Restart the car to engage" -msgstr "LKAS 错误:请重新启动车辆" +msgstr "LKAS 错误:请重新发动车辆" -#: ../selfdrive/controls/lib/events.py:904 +#: ../selfdrive/controls/lib/events.py:898 msgid "" "Reverse\n" "Gear" msgstr "倒车中" -#: ../selfdrive/controls/lib/events.py:908 -#: ../selfdrive/controls/lib/events.py:909 +#: ../selfdrive/controls/lib/events.py:902 +#: ../selfdrive/controls/lib/events.py:903 msgid "Reverse Gear" msgstr "切换至倒车档" -#: ../selfdrive/controls/lib/events.py:915 +#: ../selfdrive/controls/lib/events.py:909 msgid "Cruise Is Off" -msgstr "巡航系统已关闭" +msgstr "巡航系统关闭" -#: ../selfdrive/controls/lib/events.py:922 -#: ../selfdrive/controls/lib/events.py:923 +#: ../selfdrive/controls/lib/events.py:916 +#: ../selfdrive/controls/lib/events.py:917 msgid "Planner Solution Error" -msgstr "Planner Solution 错误" +msgstr "规划器解决方案错误" -#: ../selfdrive/controls/lib/events.py:931 -#: ../selfdrive/controls/lib/events.py:932 -#: ../selfdrive/controls/lib/events.py:933 +#: ../selfdrive/controls/lib/events.py:925 +#: ../selfdrive/controls/lib/events.py:926 +#: ../selfdrive/controls/lib/events.py:927 #, fuzzy msgid "Harness Relay Malfunction" -msgstr "Harness 故障" +msgstr "配线继电器故障" + +#: ../selfdrive/controls/lib/events.py:926 +msgid "Check Hardware" +msgstr "请检查硬体" #: ../selfdrive/controls/lib/events.py:932 -msgid "Check Hardware" -msgstr "请检查硬件" - -#: ../selfdrive/controls/lib/events.py:938 msgid "openpilot Canceled" -msgstr "openpilot 已取消" +msgstr "dragonpilot 已取消" -#: ../selfdrive/controls/lib/events.py:939 +#: ../selfdrive/controls/lib/events.py:933 msgid "Speed too low" msgstr "车速过慢" -#: ../selfdrive/controls/lib/events.py:947 +#: ../selfdrive/controls/lib/events.py:941 msgid "Speed Too High" msgstr "车速过快" -#: ../selfdrive/controls/lib/events.py:948 +#: ../selfdrive/controls/lib/events.py:942 msgid "Model uncertain at this speed" msgstr "模型在这个速度下无法正确识别" -#: ../selfdrive/controls/lib/events.py:951 +#: ../selfdrive/controls/lib/events.py:945 msgid "Slow down to engage" msgstr "请减速后再启用" -#: ../selfdrive/controls/lib/events.py:960 +#: ../selfdrive/controls/lib/events.py:954 #, fuzzy msgid "LKAS Disabled: Enable LKAS to engage" -msgstr "LKAS 错误:请重新启动车辆" +msgstr "LKAS 错误:请重新发动车辆" -#: ../selfdrive/controls/lib/events.py:961 +#: ../selfdrive/controls/lib/events.py:955 msgid "LKAS Disabled" -msgstr "" +msgstr "LKAS 已关闭" -#: ../selfdrive/controls/lib/events.py:965 +#: ../selfdrive/controls/lib/events.py:959 +#, fuzzy msgid "Vehicle Sensors Invalid" -msgstr "" +msgstr "感测器数据无效" -#: ../selfdrive/controls/lib/events.py:966 -#: ../selfdrive/controls/lib/events.py:967 +#: ../selfdrive/controls/lib/events.py:960 +#: ../selfdrive/controls/lib/events.py:961 msgid "Vehicle Sensors Calibrating" -msgstr "" +msgstr "车辆感应器校准中" -#: ../selfdrive/controls/lib/events.py:966 +#: ../selfdrive/controls/lib/events.py:960 #, fuzzy msgid "Drive to Calibrate" msgstr "驾驶分心" #~ msgid "Adjusting to %(speed)s %(unit)s" -#~ msgstr "巡航速度設為 %(speed)s %(unit)s" +#~ msgstr "巡航速度设为 %(speed)s %(unit)s" #~ msgid "km/h" -#~ msgstr "公里" +#~ msgstr "km/h" #~ msgid "mph" -#~ msgstr "英里" +#~ msgstr "mph" #~ msgid "STEERING REQUIRED: Blinkers ON" #~ msgstr "请接管方向盘:方向灯开启" @@ -650,13 +649,13 @@ msgstr "驾驶分心" #~ msgstr "巡航模式关闭" #~ msgid "Main Switch Off" -#~ msgstr "巡航主开关已关闭" +#~ msgstr "主开关已关闭" #~ msgid "STOPPED" #~ msgstr "已停止" #~ msgid "ESP Off" -#~ msgstr "ESP 已关闭" +#~ msgstr "ESP 关闭" #~ msgid "Cruise Faulted" #~ msgstr "巡航系统错误" @@ -667,8 +666,11 @@ msgstr "驾驶分心" #~ msgid "No Close Lead Car" #~ msgstr "前方没有车辆" +#~ msgid "No Data from Device Sensors" +#~ msgstr "未收到设备传感器数据" + #~ msgid "If sky is visible, contact support" -#~ msgstr "如果您不在地下室/隧道,请联系客服处理" +#~ msgstr "如果您不在地下室/隧道,请联系客服" #~ msgid "Auto Lane Change starts in %.1f secs" #~ msgstr "%.1f 秒后自动换道" @@ -677,7 +679,7 @@ msgstr "驾驶分心" #~ msgstr "请注意其它车辆" #~ msgid "openpilot Not Available" -#~ msgstr "openpilot无法启动" +#~ msgstr "无法使用 openpilot" #~ msgid "openpilot will not brake while gas pressed" #~ msgstr "在您踩着油门的时候 openpilot 将不会刹车" @@ -701,14 +703,11 @@ msgstr "驾驶分心" #~ msgstr "巡航速度设至目前速限" #~ msgid "Out of Storage Space" -#~ msgstr "存储空间不足" +#~ msgstr "储存空间不足" #~ msgid "Speed Too Low" #~ msgstr "车速过慢" -#~ msgid "No Data from Device Sensors" -#~ msgstr "未收到装置传感器数据" - #~ msgid "Model Output Uncertain" #~ msgstr "视觉模型判断不明确" @@ -719,25 +718,25 @@ msgstr "驾驶分心" #~ msgstr "广角道路摄像头错误" #~ msgid "STEERING REQUIRED: Lane Keeping OFF" -#~ msgstr "请接管方向盘:车道保持已关闭" +#~ msgstr "请接管方向盘:车道维持关闭" #~ msgid "DEBUG ALERT" #~ msgstr "除错用警示讯息" #~ msgid "Unsupported Giraffe Configuration" -#~ msgstr "未支持的 Giraffe 设置" +#~ msgstr "未支援的 Giraffe 设置" #~ msgid "Visit comma.ai/tg" #~ msgstr "请查阅 comma.ai/tg" #~ msgid "White Panda Is No Longer Supported" -#~ msgstr "不再支持 White Panda" +#~ msgstr "不再支援 White Panda" #~ msgid "Upgrade to comma two or black panda" #~ msgstr "请升级至 comma two 或是使用 black panda" #~ msgid "White panda is no longer supported" -#~ msgstr "不再支持 White panda" +#~ msgstr "不再支援 White panda" #~ msgid "Community Feature Detected" #~ msgstr "检测到社群开发功能" @@ -746,13 +745,13 @@ msgstr "驾驶分心" #~ msgstr "有碰撞的风险" #~ msgid "Driver Appears Distracted" -#~ msgstr "驾驶者分心" +#~ msgstr "驾驶分心" #~ msgid "Driver Was Unresponsive" -#~ msgstr "驾驶者没有反应" +#~ msgstr "驾驶没有反应" #~ msgid "CHECK DRIVER FACE VISIBILITY" -#~ msgstr "请检查驾驶者面部的可见度" +#~ msgstr "请检查驾驶面部的可见度" #~ msgid "Driver Monitor Model Output Uncertain" #~ msgstr "驾驶监控模型判断不明确" @@ -782,28 +781,28 @@ msgstr "驾驶分心" #~ msgstr "记忆体严重不足" #~ msgid "Gas Fault: Restart the Car" -#~ msgstr "油门错误:请重新启动车辆" +#~ msgstr "油门错误:请重新发动车辆" #~ msgid "Gas Error: Restart the Car" -#~ msgstr "油门错误:请重新启动车辆" +#~ msgstr "油门错误:请重新发动车辆" #~ msgid "Slow down to resume operation" #~ msgstr "请减速后再启用" #~ msgid "Please connect to Internet" -#~ msgstr "请连接网络" +#~ msgstr "请连接网路" #~ msgid "An Update Check Is Required to Engage" #~ msgstr "需检查更新后才能启用" #~ msgid "Please Connect to Internet" -#~ msgstr "请连接网络" +#~ msgstr "请连接网路" #~ msgid "Left ALC will start in 3s" -#~ msgstr "准备自动变道到左侧车道" +#~ msgstr "准备自动切至左车道" #~ msgid "Right ALC will start in 3s" -#~ msgstr "准备自动变道到右侧车道" +#~ msgstr "准备自动切至右车道" #~ msgid "Lead Car Is Moving" #~ msgstr "前方车辆车移动中" @@ -824,4 +823,4 @@ msgstr "驾驶分心" #~ msgstr "时间限制已绕过" #~ msgid "Release wheel when ready" -#~ msgstr "准备好后可松开放向盘" +#~ msgstr "准备好后请松开放向盘" diff --git a/selfdrive/assets/locales/zh-TW/LC_MESSAGES/events.mo b/selfdrive/assets/locales/zh-TW/LC_MESSAGES/events.mo index b211d2b52..01eb064aa 100644 Binary files a/selfdrive/assets/locales/zh-TW/LC_MESSAGES/events.mo and b/selfdrive/assets/locales/zh-TW/LC_MESSAGES/events.mo differ diff --git a/selfdrive/assets/locales/zh-TW/LC_MESSAGES/events.po b/selfdrive/assets/locales/zh-TW/LC_MESSAGES/events.po index 07281857d..d73bd36be 100644 --- a/selfdrive/assets/locales/zh-TW/LC_MESSAGES/events.po +++ b/selfdrive/assets/locales/zh-TW/LC_MESSAGES/events.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-05-24 11:41+0800\n" +"POT-Creation-Date: 2023-06-08 16:04+0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Rick Lan \n" "Language-Team: LANGUAGE \n" @@ -28,7 +28,7 @@ msgstr "立即接管控制" #: ../selfdrive/controls/lib/events.py:171 #, fuzzy msgid "openpilot will disengage" -msgstr "Openpilot 將解除" +msgstr "dragonpilot 將解除" #: ../selfdrive/controls/lib/events.py:198 msgid "Always keep hands on wheel and eyes on road" @@ -491,15 +491,12 @@ msgstr "記憶體不足:請重啟您的設備" #: ../selfdrive/controls/lib/events.py:830 #: ../selfdrive/controls/lib/events.py:832 -#: ../selfdrive/controls/lib/events.py:897 -#: ../selfdrive/controls/lib/events.py:899 -#: ../selfdrive/controls/lib/events.py:956 +#: ../selfdrive/controls/lib/events.py:950 msgid "Cruise Fault: Restart the Car" msgstr "巡航系統錯誤:請重新發動車輛" #: ../selfdrive/controls/lib/events.py:831 -#: ../selfdrive/controls/lib/events.py:898 -#: ../selfdrive/controls/lib/events.py:955 +#: ../selfdrive/controls/lib/events.py:949 msgid "Cruise Fault: Restart the car to engage" msgstr "巡航系統錯誤:請重新發動車輛" @@ -558,77 +555,77 @@ msgstr "LKAS 錯誤:請重新發動車輛" msgid "LKAS Fault: Restart the car to engage" msgstr "LKAS 錯誤:請重新發動車輛" -#: ../selfdrive/controls/lib/events.py:904 +#: ../selfdrive/controls/lib/events.py:898 msgid "" "Reverse\n" "Gear" msgstr "倒車中" -#: ../selfdrive/controls/lib/events.py:908 -#: ../selfdrive/controls/lib/events.py:909 +#: ../selfdrive/controls/lib/events.py:902 +#: ../selfdrive/controls/lib/events.py:903 msgid "Reverse Gear" msgstr "切換至倒車檔" -#: ../selfdrive/controls/lib/events.py:915 +#: ../selfdrive/controls/lib/events.py:909 msgid "Cruise Is Off" msgstr "巡航系統關閉" -#: ../selfdrive/controls/lib/events.py:922 -#: ../selfdrive/controls/lib/events.py:923 +#: ../selfdrive/controls/lib/events.py:916 +#: ../selfdrive/controls/lib/events.py:917 msgid "Planner Solution Error" msgstr "規劃器解決方案錯誤" -#: ../selfdrive/controls/lib/events.py:931 -#: ../selfdrive/controls/lib/events.py:932 -#: ../selfdrive/controls/lib/events.py:933 +#: ../selfdrive/controls/lib/events.py:925 +#: ../selfdrive/controls/lib/events.py:926 +#: ../selfdrive/controls/lib/events.py:927 #, fuzzy msgid "Harness Relay Malfunction" msgstr "配線繼電器故障" -#: ../selfdrive/controls/lib/events.py:932 +#: ../selfdrive/controls/lib/events.py:926 msgid "Check Hardware" msgstr "請檢查硬體" -#: ../selfdrive/controls/lib/events.py:938 +#: ../selfdrive/controls/lib/events.py:932 msgid "openpilot Canceled" -msgstr "openpilot 已取消" +msgstr "dragonpilot 已取消" -#: ../selfdrive/controls/lib/events.py:939 +#: ../selfdrive/controls/lib/events.py:933 msgid "Speed too low" msgstr "車速過慢" -#: ../selfdrive/controls/lib/events.py:947 +#: ../selfdrive/controls/lib/events.py:941 msgid "Speed Too High" msgstr "車速過快" -#: ../selfdrive/controls/lib/events.py:948 +#: ../selfdrive/controls/lib/events.py:942 msgid "Model uncertain at this speed" msgstr "模型在這個速度下無法正確識別" -#: ../selfdrive/controls/lib/events.py:951 +#: ../selfdrive/controls/lib/events.py:945 msgid "Slow down to engage" msgstr "請減速後再啟用" -#: ../selfdrive/controls/lib/events.py:960 +#: ../selfdrive/controls/lib/events.py:954 #, fuzzy msgid "LKAS Disabled: Enable LKAS to engage" msgstr "LKAS 錯誤:請重新發動車輛" -#: ../selfdrive/controls/lib/events.py:961 +#: ../selfdrive/controls/lib/events.py:955 msgid "LKAS Disabled" msgstr "LKAS 已關閉" -#: ../selfdrive/controls/lib/events.py:965 +#: ../selfdrive/controls/lib/events.py:959 #, fuzzy msgid "Vehicle Sensors Invalid" msgstr "感測器數據無效" -#: ../selfdrive/controls/lib/events.py:966 -#: ../selfdrive/controls/lib/events.py:967 +#: ../selfdrive/controls/lib/events.py:960 +#: ../selfdrive/controls/lib/events.py:961 msgid "Vehicle Sensors Calibrating" msgstr "車輛感應器校準中" -#: ../selfdrive/controls/lib/events.py:966 +#: ../selfdrive/controls/lib/events.py:960 #, fuzzy msgid "Drive to Calibrate" msgstr "駕駛分心" diff --git a/selfdrive/assets/offroad/icon_wifi_uploading.svg b/selfdrive/assets/offroad/icon_wifi_uploading.svg new file mode 100644 index 000000000..95cb0e283 --- /dev/null +++ b/selfdrive/assets/offroad/icon_wifi_uploading.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/selfdrive/athena/athenad.py b/selfdrive/athena/athenad.py index 0ac0437cb..e4bf03d8e 100755 --- a/selfdrive/athena/athenad.py +++ b/selfdrive/athena/athenad.py @@ -171,7 +171,7 @@ def jsonrpc_handler(end_event: threading.Event) -> None: try: data = recv_queue.get(timeout=1) if "method" in data: - cloudlog.debug(f"athena.jsonrpc_handler.call_method {data}") + cloudlog.event("athena.jsonrpc_handler.call_method", data=data) response = JSONRPCResponseManager.handle(data, dispatcher) send_queue.put_nowait(response.json) elif "id" in data and ("result" in data or "error" in data): diff --git a/selfdrive/boardd/boardd b/selfdrive/boardd/boardd index 929eba081..bb917cb30 100755 Binary files a/selfdrive/boardd/boardd and b/selfdrive/boardd/boardd differ diff --git a/selfdrive/boardd/boardd_api_impl.so b/selfdrive/boardd/boardd_api_impl.so index 1616fa33c..a63f7887b 100755 Binary files a/selfdrive/boardd/boardd_api_impl.so and b/selfdrive/boardd/boardd_api_impl.so differ diff --git a/selfdrive/boardd/panda.h b/selfdrive/boardd/panda.h index fb859d3a5..795d61a7f 100644 --- a/selfdrive/boardd/panda.h +++ b/selfdrive/boardd/panda.h @@ -57,7 +57,7 @@ public: std::string hw_serial(); // Static functions - static std::vector list(); + static std::vector list(bool usb_only=false); // Panda functionality cereal::PandaState::PandaType get_hw_type(); diff --git a/selfdrive/camerad/camerad b/selfdrive/camerad/camerad index d9ad79da1..a9fc68658 100755 Binary files a/selfdrive/camerad/camerad and b/selfdrive/camerad/camerad differ diff --git a/selfdrive/car/chrysler/values.py b/selfdrive/car/chrysler/values.py index 17ba59f96..bd7c3acc3 100644 --- a/selfdrive/car/chrysler/values.py +++ b/selfdrive/car/chrysler/values.py @@ -5,7 +5,7 @@ from typing import Dict, List, Optional, Union from cereal import car from panda.python import uds from selfdrive.car import dbc_dict -from selfdrive.car.docs_definitions import CarInfo, Harness, HarnessKit +from selfdrive.car.docs_definitions import CarHarness, CarInfo, CarParts from selfdrive.car.fw_query_definitions import FwQueryConfig, Request, p16 Ecu = car.CarParams.Ecu @@ -60,7 +60,7 @@ RAM_CARS = RAM_DT | RAM_HD @dataclass class ChryslerCarInfo(CarInfo): package: str = "Adaptive Cruise Control (ACC)" - harness_kit: HarnessKit = HarnessKit(Harness.fca) + car_parts: CarParts = CarParts.common([CarHarness.fca]) CAR_INFO: Dict[str, Optional[Union[ChryslerCarInfo, List[ChryslerCarInfo]]]] = { @@ -74,10 +74,10 @@ CAR_INFO: Dict[str, Optional[Union[ChryslerCarInfo, List[ChryslerCarInfo]]]] = { ], CAR.JEEP_CHEROKEE: ChryslerCarInfo("Jeep Grand Cherokee 2016-18", video_link="https://www.youtube.com/watch?v=eLR9o2JkuRk"), CAR.JEEP_CHEROKEE_2019: ChryslerCarInfo("Jeep Grand Cherokee 2019-21", video_link="https://www.youtube.com/watch?v=jBe4lWnRSu4"), - CAR.RAM_1500: ChryslerCarInfo("Ram 1500 2019-23", harness_kit=HarnessKit(Harness.ram)), + CAR.RAM_1500: ChryslerCarInfo("Ram 1500 2019-23", car_parts=CarParts.common([CarHarness.ram])), CAR.RAM_HD: [ - ChryslerCarInfo("Ram 2500 2020-22", harness_kit=HarnessKit(Harness.ram)), - ChryslerCarInfo("Ram 3500 2019-22", harness_kit=HarnessKit(Harness.ram)), + ChryslerCarInfo("Ram 2500 2020-22", car_parts=CarParts.common([CarHarness.ram])), + ChryslerCarInfo("Ram 3500 2019-22", car_parts=CarParts.common([CarHarness.ram])), ], } @@ -225,8 +225,8 @@ FW_VERSIONS = { b'68453513AD', b'68453514AD', b'68510283AG', - b'68527375AD', b'68527346AE', + b'68527375AD', ], (Ecu.srs, 0x744, None): [ b'68428609AB', @@ -248,8 +248,8 @@ FW_VERSIONS = { b'68438456AF', b'68535469AB', b'68535470AC', - b'68586307AB', b'68548900AB', + b'68586307AB', ], (Ecu.fwdRadar, 0x753, None): [ b'04672892AB', @@ -275,13 +275,14 @@ FW_VERSIONS = { b'68522583AB', b'68522585AB', b'68552788AA', + b'68552789AA', b'68552790AA', b'68585112AB', - b'68552789AA', ], (Ecu.engine, 0x7e0, None): [ b'05036065AE ', b'05036066AE ', + b'05149846AA ', b'68378701AI ', b'68378758AM ', b'68448163AJ', @@ -289,7 +290,6 @@ FW_VERSIONS = { b'68500630AD', b'68500630AE', b'68539650AD', - b'05149846AA ', ], (Ecu.transmission, 0x7e1, None): [ b'68360078AL', @@ -301,8 +301,8 @@ FW_VERSIONS = { b'68445533AB', b'68484467AC', b'68502994AD', - b'68540431AB', b'68520867AE', + b'68540431AB', ], }, @@ -314,6 +314,7 @@ FW_VERSIONS = { b'68525485AB', b'68525487AB', b'68525498AB', + b'68528791AF', ], (Ecu.srs, 0x744, None): [ b'68399794AC', @@ -329,9 +330,11 @@ FW_VERSIONS = { b'68504022AC', b'68530686AB', b'68530686AC', + b'68544596AC', ], (Ecu.fwdRadar, 0x753, None): [ b'04672895AB', + b'04672934AB', b'56029827AG', b'56029827AH', b'68462657AE', @@ -341,6 +344,7 @@ FW_VERSIONS = { (Ecu.eps, 0x761, None): [ b'68421036AC', b'68507906AB', + b'68534023AC', ], (Ecu.engine, 0x7e0, None): [ b'52370131AF', @@ -349,6 +353,7 @@ FW_VERSIONS = { b'52370931CT', b'52401032AE', b'52421132AF', + b'52421332AF', b'68527616AD ', b'M2370131MB', b'M2421132MB', diff --git a/selfdrive/car/docs_definitions.py b/selfdrive/car/docs_definitions.py index bfb182c5e..23b460147 100644 --- a/selfdrive/car/docs_definitions.py +++ b/selfdrive/car/docs_definitions.py @@ -1,6 +1,5 @@ import re from collections import namedtuple -import copy from dataclasses import dataclass, field from enum import Enum from typing import Dict, List, Optional, Tuple, Union @@ -21,7 +20,7 @@ class Column(Enum): FSR_STEERING = "No ALC below" STEERING_TORQUE = "Steering Torque" AUTO_RESUME = "Resume from stop" - HARNESS = "Harness Kit" + HARDWARE = "Hardware Needed" VIDEO = "Video" @@ -31,60 +30,130 @@ class Star(Enum): EMPTY = "empty" -class Harness(Enum): - nidec = "Honda Nidec" - bosch_a = "Honda Bosch A" - bosch_b = "Honda Bosch B" - toyota = "Toyota" - subaru_a = "Subaru A" - subaru_b = "Subaru B" - fca = "FCA" - ram = "Ram" - vw = "VW" - j533 = "J533" - hyundai_a = "Hyundai A" - hyundai_b = "Hyundai B" - hyundai_c = "Hyundai C" - hyundai_d = "Hyundai D" - hyundai_e = "Hyundai E" - hyundai_f = "Hyundai F" - hyundai_g = "Hyundai G" - hyundai_h = "Hyundai H" - hyundai_i = "Hyundai I" - hyundai_j = "Hyundai J" - hyundai_k = "Hyundai K" - hyundai_l = "Hyundai L" - hyundai_m = "Hyundai M" - hyundai_n = "Hyundai N" - hyundai_o = "Hyundai O" - hyundai_p = "Hyundai P" - hyundai_q = "Hyundai Q" - custom = "Developer" - obd_ii = "OBD-II" - gm = "GM" - nissan_a = "Nissan A" - nissan_b = "Nissan B" - mazda = "Mazda" - ford_q3 = "Ford Q3" - ford_q4 = "Ford Q4" - none = "None" +# A part + its comprised parts +@dataclass +class BasePart: + name: str + parts: List[Enum] = field(default_factory=list) + + def all_parts(self): + # Recursively get all parts + _parts = 'parts' + parts = [] + parts.extend(getattr(self, _parts)) + for part in getattr(self, _parts): + parts.extend(part.value.all_parts()) + + return parts -class HarnessPart(Enum): - harness_box = "harness box" - comma_power_v2 = "comma power v2" - rj45_cable = "RJ45 cable (7 ft)" - long_obdc_cable = "long OBD-C cable" - usbc_coupler = "USB-C coupler" +class EnumBase(Enum): + @property + def type(self): + return PartType(self.__class__) -DEFAULT_HARNESS_PARTS: List[HarnessPart] = [HarnessPart.harness_box, HarnessPart.comma_power_v2, HarnessPart.rj45_cable] +class Mount(EnumBase): + mount = BasePart("mount") + angled_mount_8_degrees = BasePart("angled mount (8 degrees)") + + +class Cable(EnumBase): + rj45_cable_7ft = BasePart("RJ45 cable (7 ft)") + long_obdc_cable = BasePart("long OBD-C cable") + usb_a_2_a_cable = BasePart("USB A-A cable") + usbc_otg_cable = BasePart("USB C OTG cable") + usbc_coupler = BasePart("USB-C coupler") + obd_c_cable_1_5ft = BasePart("OBD-C cable (1.5 ft)") + right_angle_obd_c_cable_1_5ft = BasePart("right angle OBD-C cable (1.5 ft)") + + +class Accessory(EnumBase): + harness_box = BasePart("harness box") + comma_power_v2 = BasePart("comma power v2") @dataclass -class HarnessKit: - connector: Harness = Harness.none - parts: List[HarnessPart] = field(default_factory=lambda: copy.copy(DEFAULT_HARNESS_PARTS)) +class BaseCarHarness(BasePart): + parts: List[Enum] = field(default_factory=lambda: [Accessory.harness_box, Accessory.comma_power_v2, Cable.rj45_cable_7ft]) + has_connector: bool = True # without are hidden on the harness connector page + + +class CarHarness(EnumBase): + nidec = BaseCarHarness("Honda Nidec connector") + bosch_a = BaseCarHarness("Honda Bosch A connector") + bosch_b = BaseCarHarness("Honda Bosch B connector") + toyota = BaseCarHarness("Toyota connector") + subaru_a = BaseCarHarness("Subaru A connector") + subaru_b = BaseCarHarness("Subaru B connector") + fca = BaseCarHarness("FCA connector") + ram = BaseCarHarness("Ram connector") + vw = BaseCarHarness("VW connector") + j533 = BaseCarHarness("J533 connector", parts=[Accessory.harness_box, Cable.long_obdc_cable, Cable.usbc_coupler]) + hyundai_a = BaseCarHarness("Hyundai A connector") + hyundai_b = BaseCarHarness("Hyundai B connector") + hyundai_c = BaseCarHarness("Hyundai C connector") + hyundai_d = BaseCarHarness("Hyundai D connector") + hyundai_e = BaseCarHarness("Hyundai E connector") + hyundai_f = BaseCarHarness("Hyundai F connector") + hyundai_g = BaseCarHarness("Hyundai G connector") + hyundai_h = BaseCarHarness("Hyundai H connector") + hyundai_i = BaseCarHarness("Hyundai I connector") + hyundai_j = BaseCarHarness("Hyundai J connector") + hyundai_k = BaseCarHarness("Hyundai K connector") + hyundai_l = BaseCarHarness("Hyundai L connector") + hyundai_m = BaseCarHarness("Hyundai M connector") + hyundai_n = BaseCarHarness("Hyundai N connector") + hyundai_o = BaseCarHarness("Hyundai O connector") + hyundai_p = BaseCarHarness("Hyundai P connector") + hyundai_q = BaseCarHarness("Hyundai Q connector") + custom = BaseCarHarness("Developer connector") + obd_ii = BaseCarHarness("OBD-II connector", parts=[Cable.long_obdc_cable, Cable.long_obdc_cable], has_connector=False) + gm = BaseCarHarness("GM connector") + nissan_a = BaseCarHarness("Nissan A connector", parts=[Accessory.harness_box, Cable.rj45_cable_7ft, Cable.long_obdc_cable, Cable.usbc_coupler]) + nissan_b = BaseCarHarness("Nissan B connector", parts=[Accessory.harness_box, Cable.rj45_cable_7ft, Cable.long_obdc_cable, Cable.usbc_coupler]) + mazda = BaseCarHarness("Mazda connector") + ford_q3 = BaseCarHarness("Ford Q3 connector") + ford_q4 = BaseCarHarness("Ford Q4 connector") + + +class Device(EnumBase): + three = BasePart("comma three", parts=[Mount.mount, Cable.right_angle_obd_c_cable_1_5ft]) + # variant of comma three with angled mounts + three_angled_mount = BasePart("comma three", parts=[Mount.angled_mount_8_degrees, Cable.right_angle_obd_c_cable_1_5ft]) + red_panda = BasePart("red panda") + + +class Kit(EnumBase): + red_panda_kit = BasePart("CAN FD panda kit", parts=[Device.red_panda, Accessory.harness_box, Cable.usb_a_2_a_cable, Cable.usbc_otg_cable, Cable.obd_c_cable_1_5ft]) + + +class PartType(Enum): + accessory = Accessory + cable = Cable + connector = CarHarness + device = Device + kit = Kit + mount = Mount + + +DEFAULT_CAR_PARTS: List[EnumBase] = [Device.three] + + +@dataclass +class CarParts: + parts: List[EnumBase] = field(default_factory=list) + + @classmethod + def common(cls, add: List[EnumBase] = None, remove: List[EnumBase] = None): + p = [part for part in (add or []) + DEFAULT_CAR_PARTS if part not in (remove or [])] + return cls(p) + + def all_parts(self): + parts = [] + for part in self.parts: + parts.extend(part.value.all_parts()) + return self.parts + parts CarFootnote = namedtuple("CarFootnote", ["text", "column", "docs_only", "shop_footnote"], defaults=(False, False)) @@ -153,9 +222,10 @@ class CarInfo: footnotes: List[Enum] = field(default_factory=list) min_steer_speed: Optional[float] = None min_enable_speed: Optional[float] = None + auto_resume: Optional[bool] = None - # harness connectors + all the parts needed - harness_kit: HarnessKit = HarnessKit() + # all the parts needed for the supported car + car_parts: CarParts = CarParts() def init(self, CP: car.CarParams, all_footnotes: Dict[Enum, int]): self.car_name = CP.carName @@ -184,13 +254,17 @@ class CarInfo: if self.min_enable_speed is None: self.min_enable_speed = CP.minEnableSpeed - # harness column - harness_col = self.harness_kit.connector.value - if self.harness_kit.connector is not Harness.none: + if self.auto_resume is None: + self.auto_resume = CP.autoResumeSng + + # hardware column + hardware_col = "None" + if self.car_parts.parts: model_years = self.model + (' ' + self.years if self.years else '') - harness_connector = f'- 1 {harness_col} connector' - harness_parts = '
'.join([f"- {self.harness_kit.parts.count(part)} {part.value}" for part in sorted(set(self.harness_kit.parts), key=lambda part: part.value)]) - harness_col = f'
View{harness_connector}
{harness_parts}
' + buy_link = f'Buy Here' + car_parts_docs = self.car_parts.all_parts() + parts = '
'.join([f"- {car_parts_docs.count(part)} {part.value.name}" for part in sorted(set(car_parts_docs), key=lambda part: str(part.value.name))]) + hardware_col = f'
View{parts}
{buy_link}
' self.row: Dict[Enum, Union[str, Star]] = { Column.MAKE: self.make, @@ -200,8 +274,8 @@ class CarInfo: Column.FSR_LONGITUDINAL: f"{max(self.min_enable_speed * CV.MS_TO_MPH, 0):.0f} mph", Column.FSR_STEERING: f"{max(self.min_steer_speed * CV.MS_TO_MPH, 0):.0f} mph", Column.STEERING_TORQUE: Star.EMPTY, - Column.AUTO_RESUME: Star.FULL if CP.autoResumeSng else Star.EMPTY, - Column.HARNESS: harness_col, + Column.AUTO_RESUME: Star.FULL if self.auto_resume else Star.EMPTY, + Column.HARDWARE: hardware_col, Column.VIDEO: self.video_link if self.video_link is not None else "", # replaced with an image and link from template in get_column } @@ -232,7 +306,7 @@ class CarInfo: acc = "" if self.min_enable_speed > 0: acc = f" while driving above {self.min_enable_speed * CV.MS_TO_MPH:.0f} mph" - elif CP.autoResumeSng: + elif self.auto_resume: acc = " that automatically resumes from a stop" if self.row[Column.STEERING_TORQUE] != Star.FULL: diff --git a/selfdrive/car/ford/carcontroller.py b/selfdrive/car/ford/carcontroller.py index d9a9ae6bc..5bdafa58b 100644 --- a/selfdrive/car/ford/carcontroller.py +++ b/selfdrive/car/ford/carcontroller.py @@ -70,7 +70,7 @@ class CarController: if self.CP.carFingerprint in CANFD_CARS: # TODO: extended mode mode = 1 if CC.latActive else 0 - counter = self.frame // CarControllerParams.STEER_STEP + counter = (self.frame // CarControllerParams.STEER_STEP) % 0xF can_sends.append(create_lat_ctl2_msg(self.packer, mode, 0., 0., -apply_curvature, 0., counter)) else: can_sends.append(create_lat_ctl_msg(self.packer, CC.latActive, 0., 0., -apply_curvature, 0.)) diff --git a/selfdrive/car/ford/values.py b/selfdrive/car/ford/values.py index 6425179a2..edc40f629 100644 --- a/selfdrive/car/ford/values.py +++ b/selfdrive/car/ford/values.py @@ -4,7 +4,7 @@ from typing import Dict, List, Set, Union from cereal import car from selfdrive.car import AngleRateLimit, dbc_dict -from selfdrive.car.docs_definitions import CarInfo, Harness, HarnessKit +from selfdrive.car.docs_definitions import CarHarness, CarInfo, CarParts, Device from selfdrive.car.fw_query_definitions import FwQueryConfig, Request, StdQueries Ecu = car.CarParams.Ecu @@ -29,8 +29,8 @@ class CarControllerParams: ANGLE_RATE_LIMIT_DOWN = AngleRateLimit(speed_bp=[5, 25], angle_v=[0.000225, 0.00015]) CURVATURE_ERROR = 0.002 # ~6 degrees at 10 m/s, ~10 degrees at 35 m/s - ACCEL_MAX = 2.0 # m/s^s max acceleration - ACCEL_MIN = -3.5 # m/s^s max deceleration + ACCEL_MAX = 2.0 # m/s^2 max acceleration + ACCEL_MIN = -3.5 # m/s^2 max deceleration MIN_GAS = -0.5 INACTIVE_GAS = -5.0 @@ -66,7 +66,11 @@ DBC: Dict[str, Dict[str, str]] = defaultdict(lambda: dbc_dict("ford_lincoln_base @dataclass class FordCarInfo(CarInfo): package: str = "Co-Pilot360 Assist+" - harness_kit: HarnessKit = HarnessKit(Harness.ford_q3) + car_parts: CarParts = CarParts.common([CarHarness.ford_q3]) + + def init_make(self, CP: car.CarParams): + if CP.carFingerprint in (CAR.BRONCO_SPORT_MK1, CAR.MAVERICK_MK1): + self.car_parts = CarParts([Device.three_angled_mount, CarHarness.ford_q3]) CAR_INFO: Dict[str, Union[CarInfo, List[CarInfo]]] = { diff --git a/selfdrive/car/fw_versions.py b/selfdrive/car/fw_versions.py index 1c0d5003e..cd3ef7977 100755 --- a/selfdrive/car/fw_versions.py +++ b/selfdrive/car/fw_versions.py @@ -1,7 +1,8 @@ #!/usr/bin/env python3 from collections import defaultdict -from typing import Any, Dict, List, Set +from typing import Any, DefaultDict, Dict, List, Optional, Set, Tuple from tqdm import tqdm +import capnp import panda.python.uds as uds from cereal import car @@ -27,7 +28,8 @@ def chunks(l, n=128): yield l[i:i + n] -def build_fw_dict(fw_versions, filter_brand=None): +def build_fw_dict(fw_versions: List[capnp.lib.capnp._DynamicStructBuilder], + filter_brand: Optional[str] = None) -> Dict[Tuple[int, Optional[int]], Set[bytes]]: fw_versions_dict = defaultdict(set) for fw in fw_versions: if (filter_brand is None or fw.brand == filter_brand) and not fw.logging: @@ -36,14 +38,14 @@ def build_fw_dict(fw_versions, filter_brand=None): return dict(fw_versions_dict) -def get_brand_addrs(): - brand_addrs = defaultdict(set) +def get_brand_addrs() -> Dict[str, Set[Tuple[int, Optional[int]]]]: + brand_addrs: DefaultDict[str, Set[Tuple[int, Optional[int]]]] = defaultdict(set) for brand, cars in VERSIONS.items(): # Add ecus in database + extra ecus to match against brand_addrs[brand] |= {(addr, sub_addr) for _, addr, sub_addr in FW_QUERY_CONFIGS[brand].extra_ecus} for fw in cars.values(): brand_addrs[brand] |= {(addr, sub_addr) for _, addr, sub_addr in fw.keys()} - return brand_addrs + return dict(brand_addrs) def match_fw_to_car_fuzzy(fw_versions_dict, log=True, exclude=None): @@ -101,8 +103,8 @@ def match_fw_to_car_exact(fw_versions_dict) -> Set[str]: candidates = FW_VERSIONS for candidate, fws in candidates.items(): + config = FW_QUERY_CONFIGS[MODEL_TO_BRAND[candidate]] for ecu, expected_versions in fws.items(): - config = FW_QUERY_CONFIGS[MODEL_TO_BRAND[candidate]] ecu_type = ecu[0] addr = ecu[1:] @@ -214,7 +216,8 @@ def set_obd_multiplexing(params: Params, obd_multiplexing: bool): cloudlog.warning("OBD multiplexing set successfully") -def get_fw_versions_ordered(logcan, sendcan, ecu_rx_addrs, timeout=0.1, num_pandas=1, debug=False, progress=False): +def get_fw_versions_ordered(logcan, sendcan, ecu_rx_addrs, timeout=0.1, num_pandas=1, debug=False, progress=False) -> \ + List[capnp.lib.capnp._DynamicStructBuilder]: """Queries for FW versions ordering brands by likelihood, breaks when exact match is found""" all_car_fw = [] @@ -235,7 +238,8 @@ def get_fw_versions_ordered(logcan, sendcan, ecu_rx_addrs, timeout=0.1, num_pand return all_car_fw -def get_fw_versions(logcan, sendcan, query_brand=None, extra=None, timeout=0.1, num_pandas=1, debug=False, progress=False): +def get_fw_versions(logcan, sendcan, query_brand=None, extra=None, timeout=0.1, num_pandas=1, debug=False, progress=False) -> \ + List[capnp.lib.capnp._DynamicStructBuilder]: versions = VERSIONS.copy() params = Params() @@ -286,11 +290,11 @@ def get_fw_versions(logcan, sendcan, query_brand=None, extra=None, timeout=0.1, set_obd_multiplexing(params, r.obd_multiplexing) try: - addrs = [(a, s) for (b, a, s) in addr_chunk if b in (brand, 'any') and - (len(r.whitelist_ecus) == 0 or ecu_types[(b, a, s)] in r.whitelist_ecus)] + query_addrs = [(a, s) for (b, a, s) in addr_chunk if b in (brand, 'any') and + (len(r.whitelist_ecus) == 0 or ecu_types[(b, a, s)] in r.whitelist_ecus)] - if addrs: - query = IsoTpParallelQuery(sendcan, logcan, r.bus, addrs, r.request, r.response, r.rx_offset, debug=debug) + if query_addrs: + query = IsoTpParallelQuery(sendcan, logcan, r.bus, query_addrs, r.request, r.response, r.rx_offset, debug=debug) for (tx_addr, sub_addr), version in query.get_data(timeout).items(): f = car.CarParams.CarFw.new_message() diff --git a/selfdrive/car/gm/values.py b/selfdrive/car/gm/values.py index 865ba8ed0..b21c303d8 100644 --- a/selfdrive/car/gm/values.py +++ b/selfdrive/car/gm/values.py @@ -5,7 +5,7 @@ from typing import Dict, List, Union from cereal import car from selfdrive.car import dbc_dict -from selfdrive.car.docs_definitions import CarFootnote, CarInfo, Column, Harness, HarnessKit, HarnessPart +from selfdrive.car.docs_definitions import CarFootnote, CarHarness, CarInfo, CarParts, Column Ecu = car.CarParams.Ecu @@ -89,9 +89,9 @@ class GMCarInfo(CarInfo): def init_make(self, CP: car.CarParams): if CP.networkLocation == car.CarParams.NetworkLocation.fwdCamera: - self.harness_kit = HarnessKit(Harness.gm) + self.car_parts = CarParts.common([CarHarness.gm]) else: - self.harness_kit = HarnessKit(Harness.obd_ii, parts=[HarnessPart.long_obdc_cable, HarnessPart.usbc_coupler]) + self.car_parts = CarParts.common([CarHarness.obd_ii]) self.footnotes.append(Footnote.OBD_II) diff --git a/selfdrive/car/honda/carstate.py b/selfdrive/car/honda/carstate.py index bcc239c2d..49a2b0931 100644 --- a/selfdrive/car/honda/carstate.py +++ b/selfdrive/car/honda/carstate.py @@ -68,7 +68,7 @@ def get_can_signals(CP, gearbox_msg, main_on_sig_msg): ("SCM_BUTTONS", 25), ] - if CP.carFingerprint in (CAR.CRV_HYBRID, CAR.CIVIC_BOSCH_DIESEL, CAR.ACURA_RDX_3G, CAR.HONDA_E): + if CP.carFingerprint in (CAR.CRV_HYBRID, CAR.CIVIC_BOSCH_DIESEL, CAR.ACURA_RDX_3G, CAR.HONDA_E, CAR.CRV_HYBRID_BSM): checks.append((gearbox_msg, 50)) else: checks.append((gearbox_msg, 100)) @@ -103,7 +103,7 @@ def get_can_signals(CP, gearbox_msg, main_on_sig_msg): else: checks.append(("CRUISE_PARAMS", 50)) - if CP.carFingerprint in (CAR.ACCORD, CAR.ACCORDH, CAR.CIVIC_BOSCH, CAR.CIVIC_BOSCH_DIESEL, CAR.CRV_HYBRID, CAR.INSIGHT, CAR.ACURA_RDX_3G, CAR.HONDA_E, CAR.CIVIC_2022, CAR.HRV_3G): + if CP.carFingerprint in (CAR.ACCORD, CAR.ACCORDH, CAR.CIVIC_BOSCH, CAR.CIVIC_BOSCH_DIESEL, CAR.CRV_HYBRID, CAR.INSIGHT, CAR.ACURA_RDX_3G, CAR.HONDA_E, CAR.CIVIC_2022, CAR.HRV_3G, CAR.CRV_HYBRID_BSM): signals.append(("DRIVERS_DOOR_OPEN", "SCM_FEEDBACK")) elif CP.carFingerprint in (CAR.ODYSSEY_CHN, CAR.FREED, CAR.HRV): signals.append(("DRIVERS_DOOR_OPEN", "SCM_BUTTONS")) @@ -148,7 +148,6 @@ class CarState(CarStateBase): self.shifter_values = can_define.dv[self.gearbox_msg]["GEAR_SHIFTER"] self.steer_status_values = defaultdict(lambda: "UNKNOWN", can_define.dv["STEER_STATUS"]["STEER_STATUS"]) - self.brake_error = False self.brake_switch_prev = False self.brake_switch_active = False self.cruise_setting = 0 @@ -179,7 +178,7 @@ class CarState(CarStateBase): # panda checks if the signal is non-zero ret.standstill = cp.vl["ENGINE_DATA"]["XMISSION_SPEED"] < 1e-5 # TODO: find a common signal across all cars - if self.CP.carFingerprint in (CAR.ACCORD, CAR.ACCORDH, CAR.CIVIC_BOSCH, CAR.CIVIC_BOSCH_DIESEL, CAR.CRV_HYBRID, CAR.INSIGHT, CAR.ACURA_RDX_3G, CAR.HONDA_E, CAR.CIVIC_2022, CAR.HRV_3G): + if self.CP.carFingerprint in (CAR.ACCORD, CAR.ACCORDH, CAR.CIVIC_BOSCH, CAR.CIVIC_BOSCH_DIESEL, CAR.CRV_HYBRID, CAR.INSIGHT, CAR.ACURA_RDX_3G, CAR.HONDA_E, CAR.CIVIC_2022, CAR.HRV_3G, CAR.CRV_HYBRID_BSM): ret.doorOpen = bool(cp.vl["SCM_FEEDBACK"]["DRIVERS_DOOR_OPEN"]) elif self.CP.carFingerprint in (CAR.ODYSSEY_CHN, CAR.FREED, CAR.HRV): ret.doorOpen = bool(cp.vl["SCM_BUTTONS"]["DRIVERS_DOOR_OPEN"]) @@ -195,9 +194,17 @@ class CarState(CarStateBase): ret.steerFaultTemporary = steer_status not in ("NORMAL", "LOW_SPEED_LOCKOUT", "NO_TORQUE_ALERT_2") if self.CP.carFingerprint in HONDA_BOSCH_RADARLESS: - self.brake_error = cp.vl["CRUISE_FAULT_STATUS"]["CRUISE_FAULT"] - elif self.CP.openpilotLongitudinalControl: - self.brake_error = cp.vl["STANDSTILL"]["BRAKE_ERROR_1"] or cp.vl["STANDSTILL"]["BRAKE_ERROR_2"] + ret.accFaulted = bool(cp.vl["CRUISE_FAULT_STATUS"]["CRUISE_FAULT"]) + else: + # On some cars, these two signals are always 1, this flag is masking a bug in release + # FIXME: find and set the ACC faulted signals on more platforms + if self.CP.openpilotLongitudinalControl: + ret.accFaulted = bool(cp.vl["STANDSTILL"]["BRAKE_ERROR_1"] or cp.vl["STANDSTILL"]["BRAKE_ERROR_2"]) + + # Log non-critical stock ACC/LKAS faults if Nidec (camera) + if self.CP.carFingerprint not in HONDA_BOSCH: + ret.carFaultedNonCritical = bool(cp_cam.vl["ACC_HUD"]["ACC_PROBLEM"] or cp_cam.vl["LKAS_HUD"]["LKAS_PROBLEM"]) + ret.espDisabled = cp.vl["VSA_STATUS"]["ESP_DISABLED"] != 0 ret.wheelSpeeds = self.get_wheel_speeds( @@ -332,12 +339,15 @@ class CarState(CarStateBase): ("AEB_REQ_1", "BRAKE_COMMAND"), ("FCW", "BRAKE_COMMAND"), ("CHIME", "BRAKE_COMMAND"), + ("LKAS_PROBLEM", "LKAS_HUD"), ("FCM_OFF", "ACC_HUD"), ("FCM_OFF_2", "ACC_HUD"), ("FCM_PROBLEM", "ACC_HUD"), + ("ACC_PROBLEM", "ACC_HUD"), ("ICONS", "ACC_HUD")] checks += [ ("ACC_HUD", 10), + ("LKAS_HUD", 10), ("BRAKE_COMMAND", 50), ] diff --git a/selfdrive/car/honda/interface.py b/selfdrive/car/honda/interface.py index 65deab740..c2898efe9 100755 --- a/selfdrive/car/honda/interface.py +++ b/selfdrive/car/honda/interface.py @@ -7,6 +7,7 @@ from selfdrive.car.honda.values import CarControllerParams, CruiseButtons, Honda from selfdrive.car import STD_CARGO_KG, CivicParams, create_button_event, scale_tire_stiffness, get_safety_config from selfdrive.car.interfaces import CarInterfaceBase from selfdrive.car.disable_ecu import disable_ecu +from common.params import Params ButtonType = car.CarState.ButtonEvent.Type @@ -50,7 +51,7 @@ class CarInterface(CarInterfaceBase): ret.pcmCruise = not ret.enableGasInterceptor - if candidate == CAR.CRV_5G: + if candidate in (CAR.CRV_5G, CAR.CRV_HYBRID_BSM): ret.enableBsm = 0x12f8bfa7 in fingerprint[0] # Detect Bosch cars with new HUD msgs @@ -164,7 +165,7 @@ class CarInterface(CarInterfaceBase): tire_stiffness_factor = 0.677 ret.wheelSpeedFactor = 1.025 - elif candidate == CAR.CRV_HYBRID: + elif candidate in (CAR.CRV_HYBRID, CAR.CRV_HYBRID_BSM): ret.mass = 1667. + STD_CARGO_KG # mean of 4 models in kg ret.wheelbase = 2.66 ret.centerToFront = ret.wheelbase * 0.41 @@ -326,9 +327,6 @@ class CarInterface(CarInterfaceBase): # events events = self.create_common_events(ret, pcm_enable=False) - if self.CS.brake_error: - events.add(EventName.brakeUnavailable) - if self.CP.pcmCruise and ret.vEgo < self.CP.minEnableSpeed: events.add(EventName.belowEngageSpeed) diff --git a/selfdrive/car/honda/values.py b/selfdrive/car/honda/values.py index 05c6f2543..7d2a2b42a 100644 --- a/selfdrive/car/honda/values.py +++ b/selfdrive/car/honda/values.py @@ -6,7 +6,7 @@ from cereal import car from common.conversions import Conversions as CV from panda.python import uds from selfdrive.car import dbc_dict -from selfdrive.car.docs_definitions import CarFootnote, CarInfo, Column, Harness, HarnessKit +from selfdrive.car.docs_definitions import CarFootnote, CarHarness, CarInfo, CarParts, Column from selfdrive.car.fw_query_definitions import FwQueryConfig, Request, StdQueries, p16 Ecu = car.CarParams.Ecu @@ -97,6 +97,8 @@ class CAR: INSIGHT = "HONDA INSIGHT 2019" HONDA_E = "HONDA E 2020" + CRV_HYBRID_BSM = "HONDA CR-V HYBRID 2019 w/ BSM" + class Footnote(Enum): CIVIC_DIESEL = CarFootnote( @@ -110,9 +112,9 @@ class HondaCarInfo(CarInfo): def init_make(self, CP: car.CarParams): if CP.carFingerprint in HONDA_BOSCH: - self.harness_kit = HarnessKit(Harness.bosch_b) if CP.carFingerprint in HONDA_BOSCH_RADARLESS else HarnessKit(Harness.bosch_a) + self.car_parts = CarParts.common([CarHarness.bosch_b]) if CP.carFingerprint in HONDA_BOSCH_RADARLESS else CarParts.common([CarHarness.bosch_a]) else: - self.harness_kit = HarnessKit(Harness.nidec) + self.car_parts = CarParts.common([CarHarness.nidec]) CAR_INFO: Dict[str, Optional[Union[HondaCarInfo, List[HondaCarInfo]]]] = { @@ -146,7 +148,7 @@ CAR_INFO: Dict[str, Optional[Union[HondaCarInfo, List[HondaCarInfo]]]] = { CAR.ACURA_RDX_3G: HondaCarInfo("Acura RDX 2019-22", "All", min_steer_speed=3. * CV.MPH_TO_MS), CAR.PILOT: [ HondaCarInfo("Honda Pilot 2016-22", min_steer_speed=12. * CV.MPH_TO_MS), - HondaCarInfo("Honda Passport 2019-22", "All", min_steer_speed=12. * CV.MPH_TO_MS), + HondaCarInfo("Honda Passport 2019-23", "All", min_steer_speed=12. * CV.MPH_TO_MS), ], CAR.RIDGELINE: HondaCarInfo("Honda Ridgeline 2017-23", min_steer_speed=12. * CV.MPH_TO_MS), CAR.INSIGHT: HondaCarInfo("Honda Insight 2019-22", "All", min_steer_speed=3. * CV.MPH_TO_MS), @@ -198,6 +200,7 @@ FW_QUERY_CONFIG = FwQueryConfig( ) FW_VERSIONS = { + CAR.CRV_HYBRID_BSM: {(Ecu.vsa, 0xfff, None): [b'\x00']}, CAR.ACCORD: { (Ecu.programmedFuelInjection, 0x18da10f1, None): [ b'37805-6A0-8720\x00\x00', @@ -1130,6 +1133,7 @@ FW_VERSIONS = { b'37805-RLV-B210\x00\x00', b'37805-RLV-L160\x00\x00', b'37805-RLV-B420\x00\x00', + b'37805-RLV-F120\x00\x00', ], (Ecu.gateway, 0x18daeff1, None): [ b'38897-TG7-A030\x00\x00', @@ -1568,6 +1572,7 @@ DBC = { CAR.CRV_5G: dbc_dict('honda_crv_ex_2017_can_generated', None, body_dbc='honda_crv_ex_2017_body_generated'), CAR.CRV_EU: dbc_dict('honda_crv_executive_2016_can_generated', 'acura_ilx_2016_nidec'), CAR.CRV_HYBRID: dbc_dict('honda_accord_2018_can_generated', None), + CAR.CRV_HYBRID_BSM: dbc_dict('honda_accord_2018_can_generated', None, body_dbc='honda_crv_ex_2017_body_generated'), CAR.FIT: dbc_dict('honda_fit_ex_2018_can_generated', 'acura_ilx_2016_nidec'), CAR.FREED: dbc_dict('honda_fit_ex_2018_can_generated', 'acura_ilx_2016_nidec'), CAR.HRV: dbc_dict('honda_fit_ex_2018_can_generated', 'acura_ilx_2016_nidec'), @@ -1591,6 +1596,6 @@ HONDA_NIDEC_ALT_PCM_ACCEL = {CAR.ODYSSEY} HONDA_NIDEC_ALT_SCM_MESSAGES = {CAR.ACURA_ILX, CAR.ACURA_RDX, CAR.CRV, CAR.CRV_EU, CAR.FIT, CAR.FREED, CAR.HRV, CAR.ODYSSEY_CHN, CAR.PILOT, CAR.RIDGELINE} HONDA_BOSCH = {CAR.ACCORD, CAR.ACCORDH, CAR.CIVIC_BOSCH, CAR.CIVIC_BOSCH_DIESEL, CAR.CRV_5G, - CAR.CRV_HYBRID, CAR.INSIGHT, CAR.ACURA_RDX_3G, CAR.HONDA_E, CAR.CIVIC_2022, CAR.HRV_3G} + CAR.CRV_HYBRID, CAR.INSIGHT, CAR.ACURA_RDX_3G, CAR.HONDA_E, CAR.CIVIC_2022, CAR.HRV_3G, CAR.CRV_HYBRID_BSM} HONDA_BOSCH_ALT_BRAKE_SIGNAL = {CAR.ACCORD, CAR.CRV_5G, CAR.ACURA_RDX_3G, CAR.HRV_3G} HONDA_BOSCH_RADARLESS = {CAR.CIVIC_2022, CAR.HRV_3G} diff --git a/selfdrive/car/hyundai/carstate.py b/selfdrive/car/hyundai/carstate.py index cec2c3e69..9bf2e0d4c 100644 --- a/selfdrive/car/hyundai/carstate.py +++ b/selfdrive/car/hyundai/carstate.py @@ -173,8 +173,8 @@ class CarState(CarStateBase): ret.brakePressed = cp.vl["TCS"]["DriverBraking"] == 1 - ret.doorOpen = cp.vl["DOORS_SEATBELTS"]["DRIVER_DOOR_OPEN"] == 1 - ret.seatbeltUnlatched = cp.vl["DOORS_SEATBELTS"]["DRIVER_SEATBELT_LATCHED"] == 0 + ret.doorOpen = cp.vl["DOORS_SEATBELTS"]["DRIVER_DOOR"] == 1 + ret.seatbeltUnlatched = cp.vl["DOORS_SEATBELTS"]["DRIVER_SEATBELT"] == 0 gear = cp.vl[self.gear_msg_canfd]["GEAR"] ret.gearShifter = self.parse_gear_shifter(self.shifter_values.get(gear)) @@ -457,8 +457,8 @@ class CarState(CarStateBase): ("LEFT_LAMP", "BLINKERS"), ("RIGHT_LAMP", "BLINKERS"), - ("DRIVER_DOOR_OPEN", "DOORS_SEATBELTS"), - ("DRIVER_SEATBELT_LATCHED", "DOORS_SEATBELTS"), + ("DRIVER_DOOR", "DOORS_SEATBELTS"), + ("DRIVER_SEATBELT", "DOORS_SEATBELTS"), ] checks = [ @@ -486,6 +486,8 @@ class CarState(CarStateBase): if not (CP.flags & HyundaiFlags.CANFD_CAMERA_SCC.value) and not CP.openpilotLongitudinalControl: signals += [ + ("COUNTER", "SCC_CONTROL"), + ("CHECKSUM", "SCC_CONTROL"), ("ACCMode", "SCC_CONTROL"), ("VSetDis", "SCC_CONTROL"), ("CRUISE_STANDSTILL", "SCC_CONTROL"), @@ -528,6 +530,7 @@ class CarState(CarStateBase): elif CP.flags & HyundaiFlags.CANFD_CAMERA_SCC: signals += [ ("COUNTER", "SCC_CONTROL"), + ("CHECKSUM", "SCC_CONTROL"), ("NEW_SIGNAL_1", "SCC_CONTROL"), ("MainMode_ACC", "SCC_CONTROL"), ("ACCMode", "SCC_CONTROL"), diff --git a/selfdrive/car/hyundai/hyundaican.py b/selfdrive/car/hyundai/hyundaican.py index 858f3d087..afd112326 100644 --- a/selfdrive/car/hyundai/hyundaican.py +++ b/selfdrive/car/hyundai/hyundaican.py @@ -21,7 +21,7 @@ def create_lkas11(packer, frame, car_fingerprint, apply_steer, steer_req, CAR.IONIQ_EV_2020, CAR.IONIQ_PHEV, CAR.KIA_SELTOS, CAR.ELANTRA_2021, CAR.GENESIS_G70_2020, CAR.ELANTRA_HEV_2021, CAR.SONATA_HYBRID, CAR.KONA_EV, CAR.KONA_HEV, CAR.KONA_EV_2022, CAR.SANTA_FE_2022, CAR.KIA_K5_2021, CAR.IONIQ_HEV_2022, CAR.SANTA_FE_HEV_2022, - CAR.SANTA_FE_PHEV_2022, CAR.KIA_STINGER_2022, CAR.KIA_K5_HEV_2020): + CAR.SANTA_FE_PHEV_2022, CAR.KIA_STINGER_2022, CAR.KIA_K5_HEV_2020, CAR.KIA_CEED): values["CF_Lkas_LdwsActivemode"] = int(left_lane) + (int(right_lane) << 1) values["CF_Lkas_LdwsOpt_USM"] = 2 diff --git a/selfdrive/car/hyundai/values.py b/selfdrive/car/hyundai/values.py index b5d35cc9e..b6e97a38a 100644 --- a/selfdrive/car/hyundai/values.py +++ b/selfdrive/car/hyundai/values.py @@ -6,7 +6,7 @@ from cereal import car from panda.python import uds from common.conversions import Conversions as CV from selfdrive.car import dbc_dict -from selfdrive.car.docs_definitions import CarFootnote, CarInfo, Column, Harness, HarnessKit +from selfdrive.car.docs_definitions import CarFootnote, CarHarness, CarInfo, CarParts, Column from selfdrive.car.fw_query_definitions import FwQueryConfig, Request, p16 Ecu = car.CarParams.Ecu @@ -147,114 +147,114 @@ class HyundaiCarInfo(CarInfo): CAR_INFO: Dict[str, Optional[Union[HyundaiCarInfo, List[HyundaiCarInfo]]]] = { CAR.ELANTRA: [ - HyundaiCarInfo("Hyundai Elantra 2017-19", min_enable_speed=19 * CV.MPH_TO_MS, harness_kit=HarnessKit(Harness.hyundai_b)), - HyundaiCarInfo("Hyundai Elantra GT 2017-19", harness_kit=HarnessKit(Harness.hyundai_e)), - HyundaiCarInfo("Hyundai i30 2017-19", harness_kit=HarnessKit(Harness.hyundai_e)), + HyundaiCarInfo("Hyundai Elantra 2017-19", min_enable_speed=19 * CV.MPH_TO_MS, car_parts=CarParts.common([CarHarness.hyundai_b])), + HyundaiCarInfo("Hyundai Elantra GT 2017-19", car_parts=CarParts.common([CarHarness.hyundai_e])), + HyundaiCarInfo("Hyundai i30 2017-19", car_parts=CarParts.common([CarHarness.hyundai_e])), ], - CAR.ELANTRA_2021: HyundaiCarInfo("Hyundai Elantra 2021-23", video_link="https://youtu.be/_EdYQtV52-c", harness_kit=HarnessKit(Harness.hyundai_k)), - CAR.ELANTRA_HEV_2021: HyundaiCarInfo("Hyundai Elantra Hybrid 2021-23", video_link="https://youtu.be/_EdYQtV52-c", harness_kit=HarnessKit(Harness.hyundai_k)), + CAR.ELANTRA_2021: HyundaiCarInfo("Hyundai Elantra 2021-23", video_link="https://youtu.be/_EdYQtV52-c", car_parts=CarParts.common([CarHarness.hyundai_k])), + CAR.ELANTRA_HEV_2021: HyundaiCarInfo("Hyundai Elantra Hybrid 2021-23", video_link="https://youtu.be/_EdYQtV52-c", car_parts=CarParts.common([CarHarness.hyundai_k])), CAR.HYUNDAI_GENESIS: [ - HyundaiCarInfo("Hyundai Genesis 2015-16", min_enable_speed=19 * CV.MPH_TO_MS, harness_kit=HarnessKit(Harness.hyundai_j)), # TODO: check 2015 packages - HyundaiCarInfo("Genesis G80 2017", "All", min_enable_speed=19 * CV.MPH_TO_MS, harness_kit=HarnessKit(Harness.hyundai_j)), + HyundaiCarInfo("Hyundai Genesis 2015-16", min_enable_speed=19 * CV.MPH_TO_MS, car_parts=CarParts.common([CarHarness.hyundai_j])), # TODO: check 2015 packages + HyundaiCarInfo("Genesis G80 2017", "All", min_enable_speed=19 * CV.MPH_TO_MS, car_parts=CarParts.common([CarHarness.hyundai_j])), ], - CAR.IONIQ: HyundaiCarInfo("Hyundai Ioniq Hybrid 2017-19", harness_kit=HarnessKit(Harness.hyundai_c)), - CAR.IONIQ_HEV_2022: HyundaiCarInfo("Hyundai Ioniq Hybrid 2020-22", harness_kit=HarnessKit(Harness.hyundai_h)), # TODO: confirm 2020-21 harness - CAR.IONIQ_EV_LTD: HyundaiCarInfo("Hyundai Ioniq Electric 2019", harness_kit=HarnessKit(Harness.hyundai_c)), - CAR.IONIQ_EV_2020: HyundaiCarInfo("Hyundai Ioniq Electric 2020", "All", harness_kit=HarnessKit(Harness.hyundai_h)), - CAR.IONIQ_PHEV_2019: HyundaiCarInfo("Hyundai Ioniq Plug-in Hybrid 2019", harness_kit=HarnessKit(Harness.hyundai_c)), - CAR.IONIQ_PHEV: HyundaiCarInfo("Hyundai Ioniq Plug-in Hybrid 2020-22", "All", harness_kit=HarnessKit(Harness.hyundai_h)), - CAR.KONA: HyundaiCarInfo("Hyundai Kona 2020", harness_kit=HarnessKit(Harness.hyundai_b)), - CAR.KONA_EV: HyundaiCarInfo("Hyundai Kona Electric 2018-21", harness_kit=HarnessKit(Harness.hyundai_g)), - CAR.KONA_EV_2022: HyundaiCarInfo("Hyundai Kona Electric 2022", harness_kit=HarnessKit(Harness.hyundai_o)), - CAR.KONA_HEV: HyundaiCarInfo("Hyundai Kona Hybrid 2020", video_link="https://youtu.be/0dwpAHiZgFo", harness_kit=HarnessKit(Harness.hyundai_i)), # TODO: check packages - CAR.SANTA_FE: HyundaiCarInfo("Hyundai Santa Fe 2019-20", "All", harness_kit=HarnessKit(Harness.hyundai_d)), - CAR.SANTA_FE_2022: HyundaiCarInfo("Hyundai Santa Fe 2021-22", "All", video_link="https://youtu.be/VnHzSTygTS4", harness_kit=HarnessKit(Harness.hyundai_l)), - CAR.SANTA_FE_HEV_2022: HyundaiCarInfo("Hyundai Santa Fe Hybrid 2022", "All", harness_kit=HarnessKit(Harness.hyundai_l)), - CAR.SANTA_FE_PHEV_2022: HyundaiCarInfo("Hyundai Santa Fe Plug-in Hybrid 2022", "All", harness_kit=HarnessKit(Harness.hyundai_l)), - CAR.SONATA: HyundaiCarInfo("Hyundai Sonata 2020-23", "All", video_link="https://www.youtube.com/watch?v=ix63r9kE3Fw", harness_kit=HarnessKit(Harness.hyundai_a)), - CAR.SONATA_LF: HyundaiCarInfo("Hyundai Sonata 2018-19", harness_kit=HarnessKit(Harness.hyundai_e)), + CAR.IONIQ: HyundaiCarInfo("Hyundai Ioniq Hybrid 2017-19", car_parts=CarParts.common([CarHarness.hyundai_c])), + CAR.IONIQ_HEV_2022: HyundaiCarInfo("Hyundai Ioniq Hybrid 2020-22", car_parts=CarParts.common([CarHarness.hyundai_h])), # TODO: confirm 2020-21 harness + CAR.IONIQ_EV_LTD: HyundaiCarInfo("Hyundai Ioniq Electric 2019", car_parts=CarParts.common([CarHarness.hyundai_c])), + CAR.IONIQ_EV_2020: HyundaiCarInfo("Hyundai Ioniq Electric 2020", "All", car_parts=CarParts.common([CarHarness.hyundai_h])), + CAR.IONIQ_PHEV_2019: HyundaiCarInfo("Hyundai Ioniq Plug-in Hybrid 2019", car_parts=CarParts.common([CarHarness.hyundai_c])), + CAR.IONIQ_PHEV: HyundaiCarInfo("Hyundai Ioniq Plug-in Hybrid 2020-22", "All", car_parts=CarParts.common([CarHarness.hyundai_h])), + CAR.KONA: HyundaiCarInfo("Hyundai Kona 2020", car_parts=CarParts.common([CarHarness.hyundai_b])), + CAR.KONA_EV: HyundaiCarInfo("Hyundai Kona Electric 2018-21", car_parts=CarParts.common([CarHarness.hyundai_g])), + CAR.KONA_EV_2022: HyundaiCarInfo("Hyundai Kona Electric 2022", car_parts=CarParts.common([CarHarness.hyundai_o])), + CAR.KONA_HEV: HyundaiCarInfo("Hyundai Kona Hybrid 2020", video_link="https://youtu.be/0dwpAHiZgFo", car_parts=CarParts.common([CarHarness.hyundai_i])), # TODO: check packages + CAR.SANTA_FE: HyundaiCarInfo("Hyundai Santa Fe 2019-20", "All", car_parts=CarParts.common([CarHarness.hyundai_d])), + CAR.SANTA_FE_2022: HyundaiCarInfo("Hyundai Santa Fe 2021-22", "All", video_link="https://youtu.be/VnHzSTygTS4", car_parts=CarParts.common([CarHarness.hyundai_l])), + CAR.SANTA_FE_HEV_2022: HyundaiCarInfo("Hyundai Santa Fe Hybrid 2022-23", "All", car_parts=CarParts.common([CarHarness.hyundai_l])), + CAR.SANTA_FE_PHEV_2022: HyundaiCarInfo("Hyundai Santa Fe Plug-in Hybrid 2022", "All", car_parts=CarParts.common([CarHarness.hyundai_l])), + CAR.SONATA: HyundaiCarInfo("Hyundai Sonata 2020-23", "All", video_link="https://www.youtube.com/watch?v=ix63r9kE3Fw", car_parts=CarParts.common([CarHarness.hyundai_a])), + CAR.SONATA_LF: HyundaiCarInfo("Hyundai Sonata 2018-19", car_parts=CarParts.common([CarHarness.hyundai_e])), CAR.TUCSON: [ - HyundaiCarInfo("Hyundai Tucson 2021", min_enable_speed=19 * CV.MPH_TO_MS, harness_kit=HarnessKit(Harness.hyundai_l)), - HyundaiCarInfo("Hyundai Tucson Diesel 2019", harness_kit=HarnessKit(Harness.hyundai_l)), + HyundaiCarInfo("Hyundai Tucson 2021", min_enable_speed=19 * CV.MPH_TO_MS, car_parts=CarParts.common([CarHarness.hyundai_l])), + HyundaiCarInfo("Hyundai Tucson Diesel 2019", car_parts=CarParts.common([CarHarness.hyundai_l])), ], CAR.PALISADE: [ - HyundaiCarInfo("Hyundai Palisade 2020-22", "All", video_link="https://youtu.be/TAnDqjF4fDY?t=456", harness_kit=HarnessKit(Harness.hyundai_h)), - HyundaiCarInfo("Kia Telluride 2020-22", "All", harness_kit=HarnessKit(Harness.hyundai_h)), + HyundaiCarInfo("Hyundai Palisade 2020-22", "All", video_link="https://youtu.be/TAnDqjF4fDY?t=456", car_parts=CarParts.common([CarHarness.hyundai_h])), + HyundaiCarInfo("Kia Telluride 2020-22", "All", car_parts=CarParts.common([CarHarness.hyundai_h])), ], - CAR.VELOSTER: HyundaiCarInfo("Hyundai Veloster 2019-20", min_enable_speed=5. * CV.MPH_TO_MS, harness_kit=HarnessKit(Harness.hyundai_e)), - CAR.SONATA_HYBRID: HyundaiCarInfo("Hyundai Sonata Hybrid 2020-22", "All", harness_kit=HarnessKit(Harness.hyundai_a)), + CAR.VELOSTER: HyundaiCarInfo("Hyundai Veloster 2019-20", min_enable_speed=5. * CV.MPH_TO_MS, car_parts=CarParts.common([CarHarness.hyundai_e])), + CAR.SONATA_HYBRID: HyundaiCarInfo("Hyundai Sonata Hybrid 2020-22", "All", car_parts=CarParts.common([CarHarness.hyundai_a])), CAR.IONIQ_5: [ - HyundaiCarInfo("Hyundai Ioniq 5 (Southeast Asia only) 2022-23", "All", harness_kit=HarnessKit(Harness.hyundai_q)), - HyundaiCarInfo("Hyundai Ioniq 5 (without HDA II) 2022-23", "Highway Driving Assist", harness_kit=HarnessKit(Harness.hyundai_k)), - HyundaiCarInfo("Hyundai Ioniq 5 (with HDA II) 2022-23", "Highway Driving Assist II", harness_kit=HarnessKit(Harness.hyundai_q)), + HyundaiCarInfo("Hyundai Ioniq 5 (Southeast Asia only) 2022-23", "All", car_parts=CarParts.common([CarHarness.hyundai_q])), + HyundaiCarInfo("Hyundai Ioniq 5 (without HDA II) 2022-23", "Highway Driving Assist", car_parts=CarParts.common([CarHarness.hyundai_k])), + HyundaiCarInfo("Hyundai Ioniq 5 (with HDA II) 2022-23", "Highway Driving Assist II", car_parts=CarParts.common([CarHarness.hyundai_q])), ], CAR.TUCSON_4TH_GEN: [ - HyundaiCarInfo("Hyundai Tucson 2022", harness_kit=HarnessKit(Harness.hyundai_n)), - HyundaiCarInfo("Hyundai Tucson 2023", "All", harness_kit=HarnessKit(Harness.hyundai_n)), + HyundaiCarInfo("Hyundai Tucson 2022", car_parts=CarParts.common([CarHarness.hyundai_n])), + HyundaiCarInfo("Hyundai Tucson 2023", "All", car_parts=CarParts.common([CarHarness.hyundai_n])), ], - CAR.TUCSON_HYBRID_4TH_GEN: HyundaiCarInfo("Hyundai Tucson Hybrid 2022-23", "All", harness_kit=HarnessKit(Harness.hyundai_n)), - CAR.SANTA_CRUZ_1ST_GEN: HyundaiCarInfo("Hyundai Santa Cruz 2022-23", harness_kit=HarnessKit(Harness.hyundai_n)), + CAR.TUCSON_HYBRID_4TH_GEN: HyundaiCarInfo("Hyundai Tucson Hybrid 2022-23", "All", car_parts=CarParts.common([CarHarness.hyundai_n])), + CAR.SANTA_CRUZ_1ST_GEN: HyundaiCarInfo("Hyundai Santa Cruz 2022-23", car_parts=CarParts.common([CarHarness.hyundai_n])), # Kia CAR.KIA_FORTE: [ - HyundaiCarInfo("Kia Forte 2019-21", harness_kit=HarnessKit(Harness.hyundai_g)), - HyundaiCarInfo("Kia Forte 2023", harness_kit=HarnessKit(Harness.hyundai_e)), + HyundaiCarInfo("Kia Forte 2019-21", car_parts=CarParts.common([CarHarness.hyundai_g])), + HyundaiCarInfo("Kia Forte 2023", car_parts=CarParts.common([CarHarness.hyundai_e])), ], - CAR.KIA_K5_2021: HyundaiCarInfo("Kia K5 2021-22", harness_kit=HarnessKit(Harness.hyundai_a)), - CAR.KIA_K5_HEV_2020: HyundaiCarInfo("Kia K5 Hybrid 2020", harness_kit=HarnessKit(Harness.hyundai_a)), + CAR.KIA_K5_2021: HyundaiCarInfo("Kia K5 2021-22", car_parts=CarParts.common([CarHarness.hyundai_a])), + CAR.KIA_K5_HEV_2020: HyundaiCarInfo("Kia K5 Hybrid 2020", car_parts=CarParts.common([CarHarness.hyundai_a])), CAR.KIA_NIRO_EV: [ - HyundaiCarInfo("Kia Niro EV 2019", "All", video_link="https://www.youtube.com/watch?v=lT7zcG6ZpGo", harness_kit=HarnessKit(Harness.hyundai_h)), - HyundaiCarInfo("Kia Niro EV 2020", "All", video_link="https://www.youtube.com/watch?v=lT7zcG6ZpGo", harness_kit=HarnessKit(Harness.hyundai_f)), - HyundaiCarInfo("Kia Niro EV 2021", "All", video_link="https://www.youtube.com/watch?v=lT7zcG6ZpGo", harness_kit=HarnessKit(Harness.hyundai_c)), - HyundaiCarInfo("Kia Niro EV 2022", "All", video_link="https://www.youtube.com/watch?v=lT7zcG6ZpGo", harness_kit=HarnessKit(Harness.hyundai_h)), + HyundaiCarInfo("Kia Niro EV 2019", "All", video_link="https://www.youtube.com/watch?v=lT7zcG6ZpGo", car_parts=CarParts.common([CarHarness.hyundai_h])), + HyundaiCarInfo("Kia Niro EV 2020", "All", video_link="https://www.youtube.com/watch?v=lT7zcG6ZpGo", car_parts=CarParts.common([CarHarness.hyundai_f])), + HyundaiCarInfo("Kia Niro EV 2021", "All", video_link="https://www.youtube.com/watch?v=lT7zcG6ZpGo", car_parts=CarParts.common([CarHarness.hyundai_c])), + HyundaiCarInfo("Kia Niro EV 2022", "All", video_link="https://www.youtube.com/watch?v=lT7zcG6ZpGo", car_parts=CarParts.common([CarHarness.hyundai_h])), ], - CAR.KIA_NIRO_EV_2ND_GEN: HyundaiCarInfo("Kia Niro EV 2023", "All", harness_kit=HarnessKit(Harness.hyundai_a)), + CAR.KIA_NIRO_EV_2ND_GEN: HyundaiCarInfo("Kia Niro EV 2023", "All", car_parts=CarParts.common([CarHarness.hyundai_a])), CAR.KIA_NIRO_PHEV: [ - HyundaiCarInfo("Kia Niro Plug-in Hybrid 2018-19", "All", min_enable_speed=10. * CV.MPH_TO_MS, harness_kit=HarnessKit(Harness.hyundai_c)), - HyundaiCarInfo("Kia Niro Plug-in Hybrid 2020", "All", harness_kit=HarnessKit(Harness.hyundai_d)), + HyundaiCarInfo("Kia Niro Plug-in Hybrid 2018-19", "All", min_enable_speed=10. * CV.MPH_TO_MS, car_parts=CarParts.common([CarHarness.hyundai_c])), + HyundaiCarInfo("Kia Niro Plug-in Hybrid 2020", "All", car_parts=CarParts.common([CarHarness.hyundai_d])), ], CAR.KIA_NIRO_HEV_2021: [ - HyundaiCarInfo("Kia Niro Hybrid 2021-22", harness_kit=HarnessKit(Harness.hyundai_f)), # TODO: 2021 could be hyundai_d, verify + HyundaiCarInfo("Kia Niro Hybrid 2021-22", car_parts=CarParts.common([CarHarness.hyundai_f])), # TODO: 2021 could be hyundai_d, verify ], - CAR.KIA_NIRO_HEV_2ND_GEN: HyundaiCarInfo("Kia Niro Hybrid 2023", harness_kit=HarnessKit(Harness.hyundai_a)), - CAR.KIA_OPTIMA_G4: HyundaiCarInfo("Kia Optima 2017", "Advanced Smart Cruise Control", harness_kit=HarnessKit(Harness.hyundai_b)), # TODO: may support 2016, 2018 - CAR.KIA_OPTIMA_G4_FL: HyundaiCarInfo("Kia Optima 2019-20", harness_kit=HarnessKit(Harness.hyundai_g)), + CAR.KIA_NIRO_HEV_2ND_GEN: HyundaiCarInfo("Kia Niro Hybrid 2023", car_parts=CarParts.common([CarHarness.hyundai_a])), + CAR.KIA_OPTIMA_G4: HyundaiCarInfo("Kia Optima 2017", "Advanced Smart Cruise Control", car_parts=CarParts.common([CarHarness.hyundai_b])), # TODO: may support 2016, 2018 + CAR.KIA_OPTIMA_G4_FL: HyundaiCarInfo("Kia Optima 2019-20", car_parts=CarParts.common([CarHarness.hyundai_g])), CAR.KIA_OPTIMA_H: [ HyundaiCarInfo("Kia Optima Hybrid 2017", "Advanced Smart Cruise Control"), # TODO: may support adjacent years HyundaiCarInfo("Kia Optima Hybrid 2019"), ], - CAR.KIA_SELTOS: HyundaiCarInfo("Kia Seltos 2021", harness_kit=HarnessKit(Harness.hyundai_a)), - CAR.KIA_SPORTAGE_5TH_GEN: HyundaiCarInfo("Kia Sportage 2023", harness_kit=HarnessKit(Harness.hyundai_n)), + CAR.KIA_SELTOS: HyundaiCarInfo("Kia Seltos 2021", car_parts=CarParts.common([CarHarness.hyundai_a])), + CAR.KIA_SPORTAGE_5TH_GEN: HyundaiCarInfo("Kia Sportage 2023", car_parts=CarParts.common([CarHarness.hyundai_n])), CAR.KIA_SORENTO: [ - HyundaiCarInfo("Kia Sorento 2018", "Advanced Smart Cruise Control", video_link="https://www.youtube.com/watch?v=Fkh3s6WHJz8", harness_kit=HarnessKit(Harness.hyundai_c)), - HyundaiCarInfo("Kia Sorento 2019", video_link="https://www.youtube.com/watch?v=Fkh3s6WHJz8", harness_kit=HarnessKit(Harness.hyundai_e)), + HyundaiCarInfo("Kia Sorento 2018", "Advanced Smart Cruise Control", video_link="https://www.youtube.com/watch?v=Fkh3s6WHJz8", car_parts=CarParts.common([CarHarness.hyundai_c])), + HyundaiCarInfo("Kia Sorento 2019", video_link="https://www.youtube.com/watch?v=Fkh3s6WHJz8", car_parts=CarParts.common([CarHarness.hyundai_e])), ], - CAR.KIA_SORENTO_4TH_GEN: HyundaiCarInfo("Kia Sorento 2021-23", harness_kit=HarnessKit(Harness.hyundai_k)), - CAR.KIA_SORENTO_PHEV_4TH_GEN: HyundaiCarInfo("Kia Sorento Plug-in Hybrid 2022-23", harness_kit=HarnessKit(Harness.hyundai_a)), - CAR.KIA_SPORTAGE_HYBRID_5TH_GEN: HyundaiCarInfo("Kia Sportage Hybrid 2023", harness_kit=HarnessKit(Harness.hyundai_n)), - CAR.KIA_STINGER: HyundaiCarInfo("Kia Stinger 2018-20", video_link="https://www.youtube.com/watch?v=MJ94qoofYw0", harness_kit=HarnessKit(Harness.hyundai_c)), - CAR.KIA_STINGER_2022: HyundaiCarInfo("Kia Stinger 2022", "All", harness_kit=HarnessKit(Harness.hyundai_k)), - CAR.KIA_CEED: HyundaiCarInfo("Kia Ceed 2019", harness_kit=HarnessKit(Harness.hyundai_e)), + CAR.KIA_SORENTO_4TH_GEN: HyundaiCarInfo("Kia Sorento 2021-23", car_parts=CarParts.common([CarHarness.hyundai_k])), + CAR.KIA_SORENTO_PHEV_4TH_GEN: HyundaiCarInfo("Kia Sorento Plug-in Hybrid 2022-23", car_parts=CarParts.common([CarHarness.hyundai_a])), + CAR.KIA_SPORTAGE_HYBRID_5TH_GEN: HyundaiCarInfo("Kia Sportage Hybrid 2023", car_parts=CarParts.common([CarHarness.hyundai_n])), + CAR.KIA_STINGER: HyundaiCarInfo("Kia Stinger 2018-20", video_link="https://www.youtube.com/watch?v=MJ94qoofYw0", car_parts=CarParts.common([CarHarness.hyundai_c])), + CAR.KIA_STINGER_2022: HyundaiCarInfo("Kia Stinger 2022", "All", car_parts=CarParts.common([CarHarness.hyundai_k])), + CAR.KIA_CEED: HyundaiCarInfo("Kia Ceed 2019", car_parts=CarParts.common([CarHarness.hyundai_e])), CAR.KIA_EV6: [ - HyundaiCarInfo("Kia EV6 (Southeast Asia only) 2022-23", "All", harness_kit=HarnessKit(Harness.hyundai_p)), - HyundaiCarInfo("Kia EV6 (without HDA II) 2022-23", "Highway Driving Assist", harness_kit=HarnessKit(Harness.hyundai_l)), - HyundaiCarInfo("Kia EV6 (with HDA II) 2022-23", "Highway Driving Assist II", harness_kit=HarnessKit(Harness.hyundai_p)) + HyundaiCarInfo("Kia EV6 (Southeast Asia only) 2022-23", "All", car_parts=CarParts.common([CarHarness.hyundai_p])), + HyundaiCarInfo("Kia EV6 (without HDA II) 2022-23", "Highway Driving Assist", car_parts=CarParts.common([CarHarness.hyundai_l])), + HyundaiCarInfo("Kia EV6 (with HDA II) 2022-23", "Highway Driving Assist II", car_parts=CarParts.common([CarHarness.hyundai_p])) ], # Genesis CAR.GENESIS_GV60_EV_1ST_GEN: [ - HyundaiCarInfo("Genesis GV60 (Advanced Trim) 2023", "All", harness_kit=HarnessKit(Harness.hyundai_a)), - HyundaiCarInfo("Genesis GV60 (Performance Trim) 2023", "All", harness_kit=HarnessKit(Harness.hyundai_k)), + HyundaiCarInfo("Genesis GV60 (Advanced Trim) 2023", "All", car_parts=CarParts.common([CarHarness.hyundai_a])), + HyundaiCarInfo("Genesis GV60 (Performance Trim) 2023", "All", car_parts=CarParts.common([CarHarness.hyundai_k])), ], - CAR.GENESIS_G70: HyundaiCarInfo("Genesis G70 2018-19", "All", harness_kit=HarnessKit(Harness.hyundai_f)), - CAR.GENESIS_G70_2020: HyundaiCarInfo("Genesis G70 2020", "All", harness_kit=HarnessKit(Harness.hyundai_f)), + CAR.GENESIS_G70: HyundaiCarInfo("Genesis G70 2018-19", "All", car_parts=CarParts.common([CarHarness.hyundai_f])), + CAR.GENESIS_G70_2020: HyundaiCarInfo("Genesis G70 2020", "All", car_parts=CarParts.common([CarHarness.hyundai_f])), CAR.GENESIS_GV70_1ST_GEN: [ - HyundaiCarInfo("Genesis GV70 (2.5T Trim) 2022-23", "All", harness_kit=HarnessKit(Harness.hyundai_l)), - HyundaiCarInfo("Genesis GV70 (3.5T Trim) 2022-23", "All", harness_kit=HarnessKit(Harness.hyundai_m)), + HyundaiCarInfo("Genesis GV70 (2.5T Trim) 2022-23", "All", car_parts=CarParts.common([CarHarness.hyundai_l])), + HyundaiCarInfo("Genesis GV70 (3.5T Trim) 2022-23", "All", car_parts=CarParts.common([CarHarness.hyundai_m])), ], - CAR.GENESIS_G80: HyundaiCarInfo("Genesis G80 2018-19", "All", harness_kit=HarnessKit(Harness.hyundai_h)), - CAR.GENESIS_G90: HyundaiCarInfo("Genesis G90 2017-18", "All", harness_kit=HarnessKit(Harness.hyundai_c)), - CAR.GENESIS_GV80: HyundaiCarInfo("Genesis GV80 2023", "All", harness_kit=HarnessKit(Harness.hyundai_m)), + CAR.GENESIS_G80: HyundaiCarInfo("Genesis G80 2018-19", "All", car_parts=CarParts.common([CarHarness.hyundai_h])), + CAR.GENESIS_G90: HyundaiCarInfo("Genesis G90 2017-18", "All", car_parts=CarParts.common([CarHarness.hyundai_c])), + CAR.GENESIS_GV80: HyundaiCarInfo("Genesis GV80 2023", "All", car_parts=CarParts.common([CarHarness.hyundai_m])), } class Buttons: @@ -542,14 +542,11 @@ FW_VERSIONS = { }, CAR.SONATA: { (Ecu.fwdRadar, 0x7d0, None): [ - b'\xf1\x00DN8 1.00 99110-L0000 \xaa\xaa\xaa\xaa\xaa\xaa\xaa ', - b'\xf1\x00DN8 1.00 99110-L0000 \xaa\xaa\xaa\xaa\xaa\xaa\xaa\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', b'\xf1\x00DN8_ SCC F-CU- 1.00 1.00 99110-L0000 ', b'\xf1\x00DN8_ SCC F-CUP 1.00 1.00 99110-L0000 ', b'\xf1\x00DN8_ SCC F-CUP 1.00 1.02 99110-L1000 ', b'\xf1\x00DN8_ SCC FHCUP 1.00 1.00 99110-L0000 ', b'\xf1\x00DN8_ SCC FHCUP 1.00 1.01 99110-L1000 ', - b'\xf1\x00DN89110-L0000 \xaa\xaa\xaa\xaa\xaa\xaa\xaa ', ], (Ecu.abs, 0x7d1, None): [ b'\xf1\x00DN ESC \x07 106 \x07\x01 58910-L0100', @@ -851,18 +848,22 @@ FW_VERSIONS = { (Ecu.eps, 0x7d4, None): [ b'\xf1\x00TM MDPS C 1.00 1.02 56310-CLAC0 4TSHC102', b'\xf1\x00TM MDPS R 1.00 1.05 57700-CL000 4TSHP105', + b'\xf1\x00TM MDPS C 1.00 1.02 56310-GA000 4TSHA100', ], (Ecu.fwdCamera, 0x7c4, None): [ b'\xf1\x00TMH MFC AT EUR LHD 1.00 1.06 99211-S1500 220727', b'\xf1\x00TMH MFC AT USA LHD 1.00 1.03 99211-S1500 210224', + b'\xf1\x00TMA MFC AT USA LHD 1.00 1.03 99211-S2500 220414', ], (Ecu.transmission, 0x7e1, None): [ + b'\xf1\x00PSBG2333 E16\x00\x00\x00\x00\x00\x00\x00TTM2H16SA3\xa3\x1b\xe14', b'\xf1\x00PSBG2333 E16\x00\x00\x00\x00\x00\x00\x00TTM2H16UA3I\x94\xac\x8f', b'\xf1\x87959102T250\x00\x00\x00\x00\x00\xf1\x81E14\x00\x00\x00\x00\x00\x00\x00\xf1\x00PSBG2333 E14\x00\x00\x00\x00\x00\x00\x00TTM2H16SA2\x80\xd7l\xb2', ], (Ecu.engine, 0x7e0, None): [ b'\xf1\x87391312MTC1', b'\xf1\x87391312MTE0', + b'\xf1\x87391312MTL0', ], }, CAR.SANTA_FE_PHEV_2022: { @@ -1101,12 +1102,14 @@ FW_VERSIONS = { b'\xf1\x00IK MDPS R 1.00 1.07 57700-G9220 4I2VL107', b'\xf1\x00IK MDPS R 1.00 1.07 57700-G9420 4I4VL107', b'\xf1\x00IK MDPS R 1.00 1.08 57700-G9420 4I4VL108', + b'\xf1\x00IK MDPS R 1.00 1.08 57700-G9200 4I2CL108', ], (Ecu.transmission, 0x7e1, None): [ b'\xf1\x87VCJLP18407832DN3\x88vXfvUVT\x97eFU\x87d7v\x88eVeveFU\x89\x98\x7f\xff\xb2\xb0\xf1\x81E25\x00\x00\x00', b'\x00\x00\x00\x00\xf1\x00bcsh8p54 E25\x00\x00\x00\x00\x00\x00\x00SIK0T33NB4\xecE\xefL', b'\xf1\x87VDKLT18912362DN4wfVfwefeveVUwfvw\x88vWfvUFU\x89\xa9\x8f\xff\x87w\xf1\x81E25\x00\x00\x00\x00\x00\x00\x00\xf1\x00bcsh8p54 E25\x00\x00\x00\x00\x00\x00\x00SIK0T33NB4\xecE\xefL', b'\xf1\x87VDJLC18480772DK9\x88eHfwfff\x87eFUeDEU\x98eFe\x86T5DVyo\xff\x87s\xf1\x81E25\x00\x00\x00\x00\x00\x00\x00\xf1\x00bcsh8p54 E25\x00\x00\x00\x00\x00\x00\x00SIK0T33KB5\x9f\xa5&\x81', + b'\xf1\x00bcsh8p54 E25\x00\x00\x00\x00\x00\x00\x00SIK0T20KB3Wuvz', ], (Ecu.fwdRadar, 0x7d0, None): [ b'\xf1\x00IK__ SCC F-CUP 1.00 1.02 96400-G9100 ', @@ -1120,6 +1123,7 @@ FW_VERSIONS = { (Ecu.engine, 0x7e0, None): [ b'\xf1\x81640J0051\x00\x00\x00\x00\x00\x00\x00\x00', b'\xf1\x81640H0051\x00\x00\x00\x00\x00\x00\x00\x00', + b'\xf1\x81606G2051\x00\x00\x00\x00\x00\x00\x00\x00', ], }, CAR.GENESIS_G80: { diff --git a/selfdrive/car/isotp_parallel_query.py b/selfdrive/car/isotp_parallel_query.py index 965d2e183..8ab9728e5 100644 --- a/selfdrive/car/isotp_parallel_query.py +++ b/selfdrive/car/isotp_parallel_query.py @@ -90,12 +90,14 @@ class IsoTpParallelQuery: for addr in self.functional_addrs: self._create_isotp_msg(addr, None, -1).send(self.request[0]) - # If querying functional addrs, set up physical IsoTpMessages to send consecutive frames + # Send first frame (single or first) to all addresses and receive asynchronously in the loop below. + # If querying functional addrs, only set up physical IsoTpMessages to send consecutive frames for msg in msgs.values(): msg.send(self.request[0], setup_only=len(self.functional_addrs) > 0) results = {} start_time = time.monotonic() + addrs_responded = set() # track addresses that have ever sent a valid iso-tp frame for timeout logging response_timeouts = {tx_addr: start_time + timeout for tx_addr in self.msg_addrs} while True: self.rx() @@ -110,6 +112,7 @@ class IsoTpParallelQuery: # Extend timeout for each consecutive ISO-TP frame to avoid timing out on long responses if rx_in_progress: + addrs_responded.add(tx_addr) response_timeouts[tx_addr] = time.monotonic() + timeout if not dat: @@ -117,7 +120,7 @@ class IsoTpParallelQuery: counter = request_counter[tx_addr] expected_response = self.response[counter] - response_valid = dat[:len(expected_response)] == expected_response + response_valid = dat.startswith(expected_response) if response_valid: if counter + 1 < len(self.request): @@ -140,8 +143,14 @@ class IsoTpParallelQuery: cur_time = time.monotonic() for tx_addr in response_timeouts: if cur_time - response_timeouts[tx_addr] > 0: - if request_counter[tx_addr] > 0 and not request_done[tx_addr]: - cloudlog.error(f"iso-tp query timeout after receiving response: {tx_addr}") + if not request_done[tx_addr]: + if request_counter[tx_addr] > 0: + cloudlog.error(f"iso-tp query timeout after receiving partial response: {tx_addr}") + elif tx_addr in addrs_responded: + cloudlog.error(f"iso-tp query timeout while receiving response: {tx_addr}") + # TODO: handle functional addresses + # else: + # cloudlog.error(f"iso-tp query timeout with no response: {tx_addr}") request_done[tx_addr] = True # Break if all requests are done (finished or timed out) diff --git a/selfdrive/car/mazda/mazdacan.py b/selfdrive/car/mazda/mazdacan.py index 3b29a2562..58a505f91 100644 --- a/selfdrive/car/mazda/mazdacan.py +++ b/selfdrive/car/mazda/mazdacan.py @@ -44,6 +44,7 @@ def create_steering_control(packer, car_fingerprint, frame, apply_steer, lkas): csum = csum % 256 + values = {} if car_fingerprint in GEN1: values = { "LKAS_REQUEST": apply_steer, diff --git a/selfdrive/car/mazda/values.py b/selfdrive/car/mazda/values.py index 79f07a1fd..3fbf34e5f 100644 --- a/selfdrive/car/mazda/values.py +++ b/selfdrive/car/mazda/values.py @@ -3,7 +3,7 @@ from typing import Dict, List, Union from cereal import car from selfdrive.car import dbc_dict -from selfdrive.car.docs_definitions import CarInfo, Harness, HarnessKit +from selfdrive.car.docs_definitions import CarHarness, CarInfo, CarParts from selfdrive.car.fw_query_definitions import FwQueryConfig, Request, StdQueries Ecu = car.CarParams.Ecu @@ -37,7 +37,7 @@ class CAR: @dataclass class MazdaCarInfo(CarInfo): package: str = "All" - harness_kit: HarnessKit = HarnessKit(Harness.mazda) + car_parts: CarParts = CarParts.common([CarHarness.mazda]) CAR_INFO: Dict[str, Union[MazdaCarInfo, List[MazdaCarInfo]]] = { diff --git a/selfdrive/car/nissan/carcontroller.py b/selfdrive/car/nissan/carcontroller.py index 5064e8c45..4e99d2490 100644 --- a/selfdrive/car/nissan/carcontroller.py +++ b/selfdrive/car/nissan/carcontroller.py @@ -63,6 +63,7 @@ class CarController: can_sends.append(nissancan.create_steering_control( self.packer, apply_angle, self.frame, CC.latActive, self.lkas_max_torque)) + # Below are the HUD messages. We copy the stock message and modify if self.CP.carFingerprint != CAR.ALTIMA: if self.frame % 2 == 0: can_sends.append(nissancan.create_lkas_hud_msg( diff --git a/selfdrive/car/nissan/carstate.py b/selfdrive/car/nissan/carstate.py index bbba92dde..7fbc80766 100644 --- a/selfdrive/car/nissan/carstate.py +++ b/selfdrive/car/nissan/carstate.py @@ -171,6 +171,7 @@ class CarState(CarStateBase): ("USER_BRAKE_PRESSED", "CRUISE_THROTTLE"), ("NEW_SIGNAL_2", "CRUISE_THROTTLE"), ("GAS_PRESSED_INVERTED", "CRUISE_THROTTLE"), + ("COUNTER", "CRUISE_THROTTLE"), ("unsure1", "CRUISE_THROTTLE"), ("unsure2", "CRUISE_THROTTLE"), ("unsure3", "CRUISE_THROTTLE"), diff --git a/selfdrive/car/nissan/nissancan.py b/selfdrive/car/nissan/nissancan.py index 01fb3463a..89754775b 100644 --- a/selfdrive/car/nissan/nissancan.py +++ b/selfdrive/car/nissan/nissancan.py @@ -1,4 +1,3 @@ -import copy import crcmod from selfdrive.car.nissan.values import CAR @@ -23,7 +22,23 @@ def create_steering_control(packer, apply_steer, frame, steer_on, lkas_max_torqu def create_acc_cancel_cmd(packer, car_fingerprint, cruise_throttle_msg): - values = copy.copy(cruise_throttle_msg) + values = {s: cruise_throttle_msg[s] for s in [ + "COUNTER", + "PROPILOT_BUTTON", + "CANCEL_BUTTON", + "GAS_PEDAL_INVERTED", + "SET_BUTTON", + "RES_BUTTON", + "FOLLOW_DISTANCE_BUTTON", + "NO_BUTTON_PRESSED", + "GAS_PEDAL", + "USER_BRAKE_PRESSED", + "NEW_SIGNAL_2", + "GAS_PRESSED_INVERTED", + "unsure1", + "unsure2", + "unsure3", + ]} can_bus = 1 if car_fingerprint == CAR.ALTIMA else 2 values["CANCEL_BUTTON"] = 1 @@ -37,7 +52,12 @@ def create_acc_cancel_cmd(packer, car_fingerprint, cruise_throttle_msg): def create_cancel_msg(packer, cancel_msg, cruise_cancel): - values = copy.copy(cancel_msg) + values = {s: cancel_msg[s] for s in [ + "CANCEL_SEATBELT", + "NEW_SIGNAL_1", + "NEW_SIGNAL_2", + "NEW_SIGNAL_3", + ]} if cruise_cancel: values["CANCEL_SEATBELT"] = 1 @@ -46,7 +66,34 @@ def create_cancel_msg(packer, cancel_msg, cruise_cancel): def create_lkas_hud_msg(packer, lkas_hud_msg, enabled, left_line, right_line, left_lane_depart, right_lane_depart): - values = lkas_hud_msg + values = {s: lkas_hud_msg[s] for s in [ + "LARGE_WARNING_FLASHING", + "SIDE_RADAR_ERROR_FLASHING1", + "SIDE_RADAR_ERROR_FLASHING2", + "LEAD_CAR", + "LEAD_CAR_ERROR", + "FRONT_RADAR_ERROR", + "FRONT_RADAR_ERROR_FLASHING", + "SIDE_RADAR_ERROR_FLASHING3", + "LKAS_ERROR_FLASHING", + "SAFETY_SHIELD_ACTIVE", + "RIGHT_LANE_GREEN_FLASH", + "LEFT_LANE_GREEN_FLASH", + "FOLLOW_DISTANCE", + "AUDIBLE_TONE", + "SPEED_SET_ICON", + "SMALL_STEERING_WHEEL_ICON", + "unknown59", + "unknown55", + "unknown26", + "unknown28", + "unknown31", + "SET_SPEED", + "unknown43", + "unknown08", + "unknown05", + "unknown02", + ]} values["RIGHT_LANE_YELLOW_FLASH"] = 1 if right_lane_depart else 0 values["LEFT_LANE_YELLOW_FLASH"] = 1 if left_lane_depart else 0 @@ -59,7 +106,47 @@ def create_lkas_hud_msg(packer, lkas_hud_msg, enabled, left_line, right_line, le def create_lkas_hud_info_msg(packer, lkas_hud_info_msg, steer_hud_alert): - values = lkas_hud_info_msg + values = {s: lkas_hud_info_msg[s] for s in [ + "NA_HIGH_ACCEL_TEMP", + "SIDE_RADAR_NA_HIGH_CABIN_TEMP", + "SIDE_RADAR_MALFUNCTION", + "LKAS_MALFUNCTION", + "FRONT_RADAR_MALFUNCTION", + "SIDE_RADAR_NA_CLEAN_REAR_CAMERA", + "NA_POOR_ROAD_CONDITIONS", + "CURRENTLY_UNAVAILABLE", + "SAFETY_SHIELD_OFF", + "FRONT_COLLISION_NA_FRONT_RADAR_OBSTRUCTION", + "PEDAL_MISSAPPLICATION_SYSTEM_ACTIVATED", + "SIDE_IMPACT_NA_RADAR_OBSTRUCTION", + "WARNING_DO_NOT_ENTER", + "SIDE_IMPACT_SYSTEM_OFF", + "SIDE_IMPACT_MALFUNCTION", + "FRONT_COLLISION_MALFUNCTION", + "SIDE_RADAR_MALFUNCTION2", + "LKAS_MALFUNCTION2", + "FRONT_RADAR_MALFUNCTION2", + "PROPILOT_NA_MSGS", + "BOTTOM_MSG", + "HANDS_ON_WHEEL_WARNING", + "WARNING_STEP_ON_BRAKE_NOW", + "PROPILOT_NA_FRONT_CAMERA_OBSTRUCTED", + "PROPILOT_NA_HIGH_CABIN_TEMP", + "WARNING_PROPILOT_MALFUNCTION", + "ACC_UNAVAILABLE_HIGH_CABIN_TEMP", + "ACC_NA_FRONT_CAMERA_IMPARED", + "unknown07", + "unknown10", + "unknown15", + "unknown23", + "unknown19", + "unknown31", + "unknown32", + "unknown46", + "unknown61", + "unknown55", + "unknown50", + ]} if steer_hud_alert: values["HANDS_ON_WHEEL_WARNING"] = 1 diff --git a/selfdrive/car/nissan/values.py b/selfdrive/car/nissan/values.py index cbe62645d..568c33630 100644 --- a/selfdrive/car/nissan/values.py +++ b/selfdrive/car/nissan/values.py @@ -4,7 +4,7 @@ from typing import Dict, List, Optional, Union from cereal import car from panda.python import uds from selfdrive.car import AngleRateLimit, dbc_dict -from selfdrive.car.docs_definitions import CarInfo, Harness, HarnessKit, HarnessPart +from selfdrive.car.docs_definitions import CarInfo, CarHarness, CarParts from selfdrive.car.fw_query_definitions import FwQueryConfig, Request, StdQueries Ecu = car.CarParams.Ecu @@ -30,13 +30,10 @@ class CAR: ALTIMA = "NISSAN ALTIMA 2020" -NISSAN_HARNESS_PARTS = [HarnessPart.harness_box, HarnessPart.rj45_cable, HarnessPart.long_obdc_cable, HarnessPart.usbc_coupler] - - @dataclass class NissanCarInfo(CarInfo): package: str = "ProPILOT Assist" - harness_kit: HarnessKit = HarnessKit(Harness.nissan_a, parts=NISSAN_HARNESS_PARTS) + car_parts: CarParts = CarParts.common([CarHarness.nissan_a]) CAR_INFO: Dict[str, Optional[Union[NissanCarInfo, List[NissanCarInfo]]]] = { @@ -44,7 +41,7 @@ CAR_INFO: Dict[str, Optional[Union[NissanCarInfo, List[NissanCarInfo]]]] = { CAR.LEAF: NissanCarInfo("Nissan Leaf 2018-23", video_link="https://youtu.be/vaMbtAh_0cY"), CAR.LEAF_IC: None, # same platforms CAR.ROGUE: NissanCarInfo("Nissan Rogue 2018-20"), - CAR.ALTIMA: NissanCarInfo("Nissan Altima 2019-20", harness_kit=HarnessKit(Harness.nissan_b, parts=NISSAN_HARNESS_PARTS)), + CAR.ALTIMA: NissanCarInfo("Nissan Altima 2019-20", car_parts=CarParts.common([CarHarness.nissan_b])), } FINGERPRINTS = { @@ -171,9 +168,9 @@ FW_VERSIONS = { } DBC = { - CAR.XTRAIL: dbc_dict('nissan_x_trail_2017', None), - CAR.LEAF: dbc_dict('nissan_leaf_2018', None), - CAR.LEAF_IC: dbc_dict('nissan_leaf_2018', None), - CAR.ROGUE: dbc_dict('nissan_x_trail_2017', None), - CAR.ALTIMA: dbc_dict('nissan_x_trail_2017', None), + CAR.XTRAIL: dbc_dict('nissan_x_trail_2017_generated', None), + CAR.LEAF: dbc_dict('nissan_leaf_2018_generated', None), + CAR.LEAF_IC: dbc_dict('nissan_leaf_2018_generated', None), + CAR.ROGUE: dbc_dict('nissan_x_trail_2017_generated', None), + CAR.ALTIMA: dbc_dict('nissan_x_trail_2017_generated', None), } diff --git a/selfdrive/car/subaru/carcontroller.py b/selfdrive/car/subaru/carcontroller.py index c4246b380..ae3305fbe 100644 --- a/selfdrive/car/subaru/carcontroller.py +++ b/selfdrive/car/subaru/carcontroller.py @@ -10,10 +10,6 @@ class CarController: self.apply_steer_last = 0 self.frame = 0 - self.es_lkas_state_cnt = -1 - self.es_distance_cnt = -1 - self.es_dashstatus_cnt = -1 - self.infotainmentstatus_cnt = -1 self.cruise_button_prev = 0 self.last_cancel_frame = 0 @@ -29,7 +25,6 @@ class CarController: # *** steering *** if (self.frame % self.p.STEER_STEP) == 0: - apply_steer = int(round(actuators.steer * self.p.STEER_MAX)) # limits due to driver torque @@ -51,7 +46,7 @@ class CarController: # *** alerts and pcm cancel *** if self.CP.carFingerprint in PREGLOBAL_CARS: - if self.es_distance_cnt != CS.es_distance_msg["COUNTER"]: + if self.frame % 5 == 0: # 1 = main, 2 = set shallow, 3 = set deep, 4 = resume shallow, 5 = resume deep # disengage ACC when OP is disengaged if pcm_cancel_cmd: @@ -68,7 +63,6 @@ class CarController: self.cruise_button_prev = cruise_button can_sends.append(subarucan.create_preglobal_es_distance(self.packer, cruise_button, CS.es_distance_msg)) - self.es_distance_cnt = CS.es_distance_msg["COUNTER"] else: if pcm_cancel_cmd and (self.frame - self.last_cancel_frame) > 0.2: @@ -76,19 +70,15 @@ class CarController: can_sends.append(subarucan.create_es_distance(self.packer, CS.es_distance_msg, bus, pcm_cancel_cmd)) self.last_cancel_frame = self.frame - if self.es_dashstatus_cnt != CS.es_dashstatus_msg["COUNTER"]: + if self.frame % 10 == 0: can_sends.append(subarucan.create_es_dashstatus(self.packer, CS.es_dashstatus_msg)) - self.es_dashstatus_cnt = CS.es_dashstatus_msg["COUNTER"] - if self.es_lkas_state_cnt != CS.es_lkas_state_msg["COUNTER"]: can_sends.append(subarucan.create_es_lkas_state(self.packer, CS.es_lkas_state_msg, CC.enabled, hud_control.visualAlert, hud_control.leftLaneVisible, hud_control.rightLaneVisible, hud_control.leftLaneDepart, hud_control.rightLaneDepart)) - self.es_lkas_state_cnt = CS.es_lkas_state_msg["COUNTER"] - - if self.CP.flags & SubaruFlags.SEND_INFOTAINMENT and self.infotainmentstatus_cnt != CS.es_infotainmentstatus_msg["COUNTER"]: - can_sends.append(subarucan.create_infotainmentstatus(self.packer, CS.es_infotainmentstatus_msg, hud_control.visualAlert)) - self.infotainmentstatus_cnt = CS.es_infotainmentstatus_msg["COUNTER"] + + if self.CP.flags & SubaruFlags.SEND_INFOTAINMENT: + can_sends.append(subarucan.create_infotainmentstatus(self.packer, CS.es_infotainmentstatus_msg, hud_control.visualAlert)) new_actuators = actuators.copy() new_actuators.steer = self.apply_steer_last / self.p.STEER_MAX diff --git a/selfdrive/car/subaru/carstate.py b/selfdrive/car/subaru/carstate.py index 9d7b0a65c..8ce31b184 100644 --- a/selfdrive/car/subaru/carstate.py +++ b/selfdrive/car/subaru/carstate.py @@ -110,6 +110,7 @@ class CarState(CarStateBase): def get_global_es_distance_signals(): signals = [ ("COUNTER", "ES_Distance"), + ("CHECKSUM", "ES_Distance"), ("Signal1", "ES_Distance"), ("Cruise_Fault", "ES_Distance"), ("Cruise_Throttle", "ES_Distance"), @@ -251,6 +252,7 @@ class CarState(CarStateBase): else: signals = [ ("COUNTER", "ES_DashStatus"), + ("CHECKSUM", "ES_DashStatus"), ("PCB_Off", "ES_DashStatus"), ("LDW_Off", "ES_DashStatus"), ("Signal1", "ES_DashStatus"), @@ -278,6 +280,7 @@ class CarState(CarStateBase): ("Cruise_State", "ES_DashStatus"), ("COUNTER", "ES_LKAS_State"), + ("CHECKSUM", "ES_LKAS_State"), ("LKAS_Alert_Msg", "ES_LKAS_State"), ("Signal1", "ES_LKAS_State"), ("LKAS_ACTIVE", "ES_LKAS_State"), @@ -305,6 +308,8 @@ class CarState(CarStateBase): if CP.flags & SubaruFlags.SEND_INFOTAINMENT: signals += [ + ("COUNTER", "INFOTAINMENT_STATUS"), + ("CHECKSUM", "INFOTAINMENT_STATUS"), ("LKAS_State_Infotainment", "INFOTAINMENT_STATUS"), ("LKAS_Blue_Lines", "INFOTAINMENT_STATUS"), ("Signal1", "INFOTAINMENT_STATUS"), diff --git a/selfdrive/car/subaru/subarucan.py b/selfdrive/car/subaru/subarucan.py index 3b498d3f7..bc9bf4c0a 100644 --- a/selfdrive/car/subaru/subarucan.py +++ b/selfdrive/car/subaru/subarucan.py @@ -1,4 +1,3 @@ -import copy from cereal import car VisualAlert = car.CarControl.HUDControl.VisualAlert @@ -18,7 +17,28 @@ def create_steering_status(packer): def create_es_distance(packer, es_distance_msg, bus, pcm_cancel_cmd): - values = copy.copy(es_distance_msg) + values = {s: es_distance_msg[s] for s in [ + "CHECKSUM", + "COUNTER", + "Signal1", + "Cruise_Fault", + "Cruise_Throttle", + "Signal2", + "Car_Follow", + "Signal3", + "Cruise_Soft_Disable", + "Signal7", + "Cruise_Brake_Active", + "Distance_Swap", + "Cruise_EPB", + "Signal4", + "Close_Distance", + "Signal5", + "Cruise_Cancel", + "Cruise_Set", + "Cruise_Resume", + "Signal6", + ]} values["COUNTER"] = (values["COUNTER"] + 1) % 0x10 if pcm_cancel_cmd: values["Cruise_Cancel"] = 1 @@ -26,7 +46,24 @@ def create_es_distance(packer, es_distance_msg, bus, pcm_cancel_cmd): def create_es_lkas_state(packer, es_lkas_state_msg, enabled, visual_alert, left_line, right_line, left_lane_depart, right_lane_depart): - values = copy.copy(es_lkas_state_msg) + values = {s: es_lkas_state_msg[s] for s in [ + "CHECKSUM", + "COUNTER", + "LKAS_Alert_Msg", + "Signal1", + "LKAS_ACTIVE", + "LKAS_Dash_State", + "Signal2", + "Backward_Speed_Limit_Menu", + "LKAS_Left_Line_Enable", + "LKAS_Left_Line_Light_Blink", + "LKAS_Right_Line_Enable", + "LKAS_Right_Line_Light_Blink", + "LKAS_Left_Line_Visible", + "LKAS_Right_Line_Visible", + "LKAS_Alert", + "Signal3", + ]} # Filter the stock LKAS "Keep hands on wheel" alert if values["LKAS_Alert_Msg"] == 1: @@ -69,7 +106,35 @@ def create_es_lkas_state(packer, es_lkas_state_msg, enabled, visual_alert, left_ def create_es_dashstatus(packer, dashstatus_msg): - values = copy.copy(dashstatus_msg) + values = {s: dashstatus_msg[s] for s in [ + "CHECKSUM", + "COUNTER", + "PCB_Off", + "LDW_Off", + "Signal1", + "Cruise_State_Msg", + "LKAS_State_Msg", + "Signal2", + "Cruise_Soft_Disable", + "Cruise_Status_Msg", + "Signal3", + "Cruise_Distance", + "Signal4", + "Conventional_Cruise", + "Signal5", + "Cruise_Disengaged", + "Cruise_Activated", + "Signal6", + "Cruise_Set_Speed", + "Cruise_Fault", + "Cruise_On", + "Display_Own_Car", + "Brake_Lights", + "Car_Follow", + "Signal7", + "Far_Distance", + "Cruise_State", + ]} # Filter stock LKAS disabled and Keep hands on steering wheel OFF alerts if values["LKAS_State_Msg"] in (2, 3): @@ -80,18 +145,26 @@ def create_es_dashstatus(packer, dashstatus_msg): def create_infotainmentstatus(packer, infotainmentstatus_msg, visual_alert): # Filter stock LKAS disabled and Keep hands on steering wheel OFF alerts - if infotainmentstatus_msg["LKAS_State_Infotainment"] in (3, 4): - infotainmentstatus_msg["LKAS_State_Infotainment"] = 0 + values = {s: infotainmentstatus_msg[s] for s in [ + "CHECKSUM", + "COUNTER", + "LKAS_State_Infotainment", + "LKAS_Blue_Lines", + "Signal1", + "Signal2", + ]} + if values["LKAS_State_Infotainment"] in (3, 4): + values["LKAS_State_Infotainment"] = 0 # Show Keep hands on wheel alert for openpilot steerRequired alert if visual_alert == VisualAlert.steerRequired: - infotainmentstatus_msg["LKAS_State_Infotainment"] = 3 + values["LKAS_State_Infotainment"] = 3 # Show Obstacle Detected for fcw if visual_alert == VisualAlert.fcw: - infotainmentstatus_msg["LKAS_State_Infotainment"] = 2 + values["LKAS_State_Infotainment"] = 2 - return packer.make_can_msg("INFOTAINMENT_STATUS", 0, infotainmentstatus_msg) + return packer.make_can_msg("INFOTAINMENT_STATUS", 0, values) # *** Subaru Pre-global *** @@ -112,9 +185,27 @@ def create_preglobal_steering_control(packer, apply_steer): def create_preglobal_es_distance(packer, cruise_button, es_distance_msg): - values = copy.copy(es_distance_msg) - values["Cruise_Button"] = cruise_button + values = {s: es_distance_msg[s] for s in [ + "Cruise_Throttle", + "Signal1", + "Car_Follow", + "Signal2", + "Brake_On", + "Distance_Swap", + "Standstill", + "Signal3", + "Close_Distance", + "Signal4", + "Standstill_2", + "Cruise_Fault", + "Signal5", + "COUNTER", + "Signal6", + "Cruise_Button", + "Signal7", + ]} + values["Cruise_Button"] = cruise_button values["Checksum"] = subaru_preglobal_checksum(packer, values, "ES_Distance") return packer.make_can_msg("ES_Distance", 0, values) diff --git a/selfdrive/car/subaru/values.py b/selfdrive/car/subaru/values.py index 1977b43f9..12367d9b5 100644 --- a/selfdrive/car/subaru/values.py +++ b/selfdrive/car/subaru/values.py @@ -5,7 +5,7 @@ from typing import Dict, List, Union from cereal import car from panda.python import uds from selfdrive.car import dbc_dict -from selfdrive.car.docs_definitions import CarInfo, Harness, HarnessKit +from selfdrive.car.docs_definitions import CarHarness, CarInfo, CarParts from selfdrive.car.fw_query_definitions import FwQueryConfig, Request, StdQueries, p16 Ecu = car.CarParams.Ecu @@ -53,13 +53,13 @@ class CAR: @dataclass class SubaruCarInfo(CarInfo): package: str = "EyeSight Driver Assistance" - harness_kit: HarnessKit = HarnessKit(Harness.subaru_a) + car_parts: CarParts = CarParts.common([CarHarness.subaru_a]) CAR_INFO: Dict[str, Union[SubaruCarInfo, List[SubaruCarInfo]]] = { CAR.ASCENT: SubaruCarInfo("Subaru Ascent 2019-21", "All"), - CAR.OUTBACK: SubaruCarInfo("Subaru Outback 2020-22", "All", harness_kit=HarnessKit(Harness.subaru_b)), - CAR.LEGACY: SubaruCarInfo("Subaru Legacy 2020-22", "All", harness_kit=HarnessKit(Harness.subaru_b)), + CAR.OUTBACK: SubaruCarInfo("Subaru Outback 2020-22", "All", car_parts=CarParts.common([CarHarness.subaru_b])), + CAR.LEGACY: SubaruCarInfo("Subaru Legacy 2020-22", "All", car_parts=CarParts.common([CarHarness.subaru_b])), CAR.IMPREZA: [ SubaruCarInfo("Subaru Impreza 2017-19"), SubaruCarInfo("Subaru Crosstrek 2018-19", video_link="https://youtu.be/Agww7oE1k-s?t=26"), diff --git a/selfdrive/car/tesla/carcontroller.py b/selfdrive/car/tesla/carcontroller.py index 1f18c3d0e..aeaaba88e 100644 --- a/selfdrive/car/tesla/carcontroller.py +++ b/selfdrive/car/tesla/carcontroller.py @@ -24,17 +24,18 @@ class CarController: hands_on_fault = CS.steer_warning == "EAC_ERROR_HANDS_ON" and CS.hands_on_level >= 3 lkas_enabled = CC.latActive and not hands_on_fault - if lkas_enabled: - # Angular rate limit based on speed - apply_angle = apply_std_steer_angle_limits(actuators.steeringAngleDeg, self.apply_angle_last, CS.out.vEgo, CarControllerParams) + if self.frame % 2 == 0: + if lkas_enabled: + # Angular rate limit based on speed + apply_angle = apply_std_steer_angle_limits(actuators.steeringAngleDeg, self.apply_angle_last, CS.out.vEgo, CarControllerParams) - # To not fault the EPS - apply_angle = clip(apply_angle, CS.out.steeringAngleDeg - 20, CS.out.steeringAngleDeg + 20) - else: - apply_angle = CS.out.steeringAngleDeg + # To not fault the EPS + apply_angle = clip(apply_angle, CS.out.steeringAngleDeg - 20, CS.out.steeringAngleDeg + 20) + else: + apply_angle = CS.out.steeringAngleDeg - self.apply_angle_last = apply_angle - can_sends.append(self.tesla_can.create_steering_control(apply_angle, lkas_enabled, self.frame)) + self.apply_angle_last = apply_angle + can_sends.append(self.tesla_can.create_steering_control(apply_angle, lkas_enabled, (self.frame // 2) % 16)) # Longitudinal control (in sync with stock message, about 40Hz) if self.CP.openpilotLongitudinalControl: @@ -59,7 +60,7 @@ class CarController: # TODO: HUD control new_actuators = actuators.copy() - new_actuators.steeringAngleDeg = apply_angle + new_actuators.steeringAngleDeg = self.apply_angle_last self.frame += 1 return new_actuators, can_sends diff --git a/selfdrive/car/tesla/teslacan.py b/selfdrive/car/tesla/teslacan.py index e5d904f80..a491c030f 100644 --- a/selfdrive/car/tesla/teslacan.py +++ b/selfdrive/car/tesla/teslacan.py @@ -1,4 +1,3 @@ -import copy import crcmod from common.conversions import Conversions as CV @@ -18,12 +17,12 @@ class TeslaCAN: ret += sum(dat) return ret & 0xFF - def create_steering_control(self, angle, enabled, frame): + def create_steering_control(self, angle, enabled, counter): values = { "DAS_steeringAngleRequest": -angle, "DAS_steeringHapticRequest": 0, "DAS_steeringControlType": 1 if enabled else 0, - "DAS_steeringControlCounter": (frame % 16), + "DAS_steeringControlCounter": counter, } data = self.packer.make_can_msg("DAS_steeringControl", CANBUS.chassis, values)[2] @@ -31,7 +30,40 @@ class TeslaCAN: return self.packer.make_can_msg("DAS_steeringControl", CANBUS.chassis, values) def create_action_request(self, msg_stw_actn_req, cancel, bus, counter): - values = copy.copy(msg_stw_actn_req) + # We copy this whole message when spamming cancel + values = {s: msg_stw_actn_req[s] for s in [ + "SpdCtrlLvr_Stat", + "VSL_Enbl_Rq", + "SpdCtrlLvrStat_Inv", + "DTR_Dist_Rq", + "TurnIndLvr_Stat", + "HiBmLvr_Stat", + "WprWashSw_Psd", + "WprWash_R_Sw_Posn_V2", + "StW_Lvr_Stat", + "StW_Cond_Flt", + "StW_Cond_Psd", + "HrnSw_Psd", + "StW_Sw00_Psd", + "StW_Sw01_Psd", + "StW_Sw02_Psd", + "StW_Sw03_Psd", + "StW_Sw04_Psd", + "StW_Sw05_Psd", + "StW_Sw06_Psd", + "StW_Sw07_Psd", + "StW_Sw08_Psd", + "StW_Sw09_Psd", + "StW_Sw10_Psd", + "StW_Sw11_Psd", + "StW_Sw12_Psd", + "StW_Sw13_Psd", + "StW_Sw14_Psd", + "StW_Sw15_Psd", + "WprSw6Posn", + "MC_STW_ACTN_RQ", + "CRC_STW_ACTN_RQ", + ]} if cancel: values["SpdCtrlLvr_Stat"] = 1 diff --git a/selfdrive/car/tesla/values.py b/selfdrive/car/tesla/values.py index bac025a9c..f79e5f009 100644 --- a/selfdrive/car/tesla/values.py +++ b/selfdrive/car/tesla/values.py @@ -103,8 +103,8 @@ BUTTONS = [ ] class CarControllerParams: - ANGLE_RATE_LIMIT_UP = AngleRateLimit(speed_bp=[0., 5., 15.], angle_v=[5., .8, .15]) - ANGLE_RATE_LIMIT_DOWN = AngleRateLimit(speed_bp=[0., 5., 15.], angle_v=[5., 3.5, 0.4]) + ANGLE_RATE_LIMIT_UP = AngleRateLimit(speed_bp=[0., 5., 15.], angle_v=[10., 1.6, .3]) + ANGLE_RATE_LIMIT_DOWN = AngleRateLimit(speed_bp=[0., 5., 15.], angle_v=[10., 7.0, 0.8]) JERK_LIMIT_MAX = 8 JERK_LIMIT_MIN = -8 ACCEL_TO_SPEED_MULTIPLIER = 3 diff --git a/selfdrive/car/torque_data/substitute.yaml b/selfdrive/car/torque_data/substitute.yaml index 5ea84e559..f2da925fe 100644 --- a/selfdrive/car/torque_data/substitute.yaml +++ b/selfdrive/car/torque_data/substitute.yaml @@ -83,3 +83,6 @@ SUBARU OUTBACK 2015 - 2017: SUBARU IMPREZA LIMITED 2019 SUBARU FORESTER 2017 - 2018: SUBARU IMPREZA LIMITED 2019 SUBARU LEGACY 2015 - 2018: SUBARU IMPREZA LIMITED 2019 SUBARU ASCENT LIMITED 2019: SUBARU FORESTER 2019 + +# custom +HONDA CR-V HYBRID 2019 w/ BSM: HONDA CR-V HYBRID 2019 diff --git a/selfdrive/car/toyota/carcontroller.py b/selfdrive/car/toyota/carcontroller.py index 290fcfa10..b1fa7f9ed 100644 --- a/selfdrive/car/toyota/carcontroller.py +++ b/selfdrive/car/toyota/carcontroller.py @@ -9,6 +9,7 @@ from selfdrive.car.toyota.values import CAR, STATIC_DSU_MSGS, NO_STOP_TIMER_CAR, UNSUPPORTED_DSU_CAR from opendbc.can.packer import CANPacker from common.conversions import Conversions as CV +from common.params import Params VisualAlert = car.CarControl.HUDControl.VisualAlert @@ -40,8 +41,12 @@ class CarController: self.gas = 0 self.accel = 0 - self.dp_auto_lock_gear_prev = GearShifter.park - self.dp_auto_lock_once = False + self.dp_toyota_auto_lock_gear_prev = GearShifter.park + self.dp_toyota_auto_lock_once = False + p = Params() + self.dp_toyota_auto_lock = p.get_bool("dp_toyota_auto_lock") + self.dp_toyota_auto_unlock = p.get_bool("dp_toyota_auto_unlock") + self.dp_toyota_sng = p.get_bool("dp_toyota_sng") def update(self, CC, CS, now_nanos): @@ -97,7 +102,7 @@ class CarController: pcm_cancel_cmd = 1 # on entering standstill, send standstill request - if CS.out.standstill and not self.last_standstill and (self.CP.carFingerprint not in NO_STOP_TIMER_CAR or self.CP.enableGasInterceptor): + if not self.dp_toyota_sng and CS.out.standstill and not self.last_standstill and (self.CP.carFingerprint not in NO_STOP_TIMER_CAR or self.CP.enableGasInterceptor): self.standstill_req = True if CS.pcm_acc_status != 8: # pcm entered standstill or it's disabled @@ -113,13 +118,15 @@ class CarController: # https://github.com/AlexandreSato/animalpilot/blob/personal/doors.py if not CS.out.doorOpen: gear = CS.out.gearShifter - if gear == GearShifter.park and self.dp_auto_lock_gear_prev != gear: - can_sends.append(make_can_msg(0x750, UNLOCK_CMD, 0)) - self.dp_auto_lock_once = False - elif gear == GearShifter.drive and not self.dp_auto_lock_once and CS.out.vEgo >= LOCK_AT_SPEED: - can_sends.append(make_can_msg(0x750, LOCK_CMD, 0)) - self.dp_auto_lock_once = True - self.dp_auto_lock_gear_prev = gear + if gear == GearShifter.park and self.dp_toyota_auto_lock_gear_prev != gear: + if self.dp_toyota_auto_lock: + can_sends.append(make_can_msg(0x750, UNLOCK_CMD, 0)) + self.dp_toyota_auto_lock_once = False + elif gear == GearShifter.drive and not self.dp_toyota_auto_lock_once and CS.out.vEgo >= LOCK_AT_SPEED: + if self.dp_toyota_auto_unlock: + can_sends.append(make_can_msg(0x750, LOCK_CMD, 0)) + self.dp_toyota_auto_lock_once = True + self.dp_toyota_auto_lock_gear_prev = gear # *** control msgs *** # print("steer {0} {1} {2} {3}".format(apply_steer, min_lim, max_lim, CS.steer_torque_motor) diff --git a/selfdrive/car/toyota/interface.py b/selfdrive/car/toyota/interface.py index 4c386d512..bb375b21b 100644 --- a/selfdrive/car/toyota/interface.py +++ b/selfdrive/car/toyota/interface.py @@ -6,6 +6,7 @@ from selfdrive.car.toyota.values import Ecu, CAR, DBC, ToyotaFlags, CarControlle MIN_ACC_SPEED, EPS_SCALE, EV_HYBRID_CAR, UNSUPPORTED_DSU_CAR, NO_STOP_TIMER_CAR, ANGLE_CONTROL_CAR from selfdrive.car import STD_CARGO_KG, scale_tire_stiffness, get_safety_config from selfdrive.car.interfaces import CarInterfaceBase +from common.params import Params EventName = car.CarEvent.EventName @@ -217,6 +218,13 @@ class CarInterface(CarInterfaceBase): ret.openpilotLongitudinalControl = bool(ret.flags & ToyotaFlags.SMART_DSU) or ret.enableDsu or candidate in (TSS2_CAR - RADAR_ACC_CAR) ret.autoResumeSng = ret.openpilotLongitudinalControl and candidate in NO_STOP_TIMER_CAR + # toyota radar acc car should consider radarUnavailable = True + # and when radarUnavailable, openpilot longitudinal control is allowed when smart dsu is installed and experimental_long is on + ret.radarUnavailable = candidate in RADAR_ACC_CAR + if ret.radarUnavailable: + ret.experimentalLongitudinalAvailable = bool(ret.flags & ToyotaFlags.SMART_DSU) + ret.openpilotLongitudinalControl = experimental_long and ret.experimentalLongitudinalAvailable + if not ret.openpilotLongitudinalControl: ret.safetyConfigs[0].safetyParam |= Panda.FLAG_TOYOTA_STOCK_LONGITUDINAL diff --git a/selfdrive/car/toyota/values.py b/selfdrive/car/toyota/values.py index 00552485a..422c7560f 100644 --- a/selfdrive/car/toyota/values.py +++ b/selfdrive/car/toyota/values.py @@ -6,7 +6,7 @@ from typing import Dict, List, Union from cereal import car from common.conversions import Conversions as CV from selfdrive.car import dbc_dict -from selfdrive.car.docs_definitions import CarFootnote, CarInfo, Column, Harness, HarnessKit +from selfdrive.car.docs_definitions import CarFootnote, CarInfo, Column, CarParts, CarHarness from selfdrive.car.fw_query_definitions import FwQueryConfig, Request, StdQueries Ecu = car.CarParams.Ecu @@ -102,7 +102,7 @@ class Footnote(Enum): @dataclass class ToyotaCarInfo(CarInfo): package: str = "All" - harness_kit: HarnessKit = HarnessKit(Harness.toyota) + car_parts: CarParts = CarParts.common([CarHarness.toyota]) CAR_INFO: Dict[str, Union[ToyotaCarInfo, List[ToyotaCarInfo]]] = { @@ -1131,7 +1131,6 @@ FW_VERSIONS = { b'\x01896630EF8000\x00\x00\x00\x00', b'\x02896630E66000\x00\x00\x00\x00897CF4801001\x00\x00\x00\x00', b'\x02896630E66100\x00\x00\x00\x00897CF4801001\x00\x00\x00\x00', - b'\x01896630EA1000\x00\x00\x00\x00897CF4801001\x00\x00\x00\x00', b'\x02896630EB3000\x00\x00\x00\x00897CF4801001\x00\x00\x00\x00', b'\x02896630EB3100\x00\x00\x00\x00897CF4801001\x00\x00\x00\x00', ], diff --git a/selfdrive/car/volkswagen/values.py b/selfdrive/car/volkswagen/values.py index e4a57ec23..1fc21fe5c 100755 --- a/selfdrive/car/volkswagen/values.py +++ b/selfdrive/car/volkswagen/values.py @@ -7,7 +7,8 @@ from cereal import car from panda.python import uds from opendbc.can.can_define import CANDefine from selfdrive.car import dbc_dict -from selfdrive.car.docs_definitions import CarFootnote, CarInfo, Column, Harness, HarnessKit, HarnessPart +from selfdrive.car.docs_definitions import CarFootnote, CarHarness, CarInfo, CarParts, Column, \ + Device from selfdrive.car.fw_query_definitions import FwQueryConfig, Request, p16 Ecu = car.CarParams.Ecu @@ -153,23 +154,32 @@ class Footnote(Enum): PASSAT = CarFootnote( "Refers only to the MQB-based European B8 Passat, not the NMS Passat in the USA/China/Mideast markets.", Column.MODEL) - VW_EXP_LONG = CarFootnote ( + SKODA_HEATED_WINDSHIELD = CarFootnote( + "Some Škoda vehicles are equipped with heated windshields, which are known " + + "to block GPS signal needed for some comma three functionality.", + Column.MODEL) + VW_EXP_LONG = CarFootnote( "Only available for vehicles using a gateway (J533) harness. At this time, vehicles using a camera harness " + "are limited to using stock ACC.", Column.LONGITUDINAL) VW_MQB_A0 = CarFootnote( "Model-years 2022 and beyond may have a combined CAN gateway and BCM, which is supported by openpilot " + "in software, but doesn't yet have a harness available from the comma store.", - Column.HARNESS) + Column.HARDWARE) @dataclass class VWCarInfo(CarInfo): package: str = "Adaptive Cruise Control (ACC) & Lane Assist" - harness_kit: HarnessKit = HarnessKit(Harness.j533, parts=[HarnessPart.harness_box, HarnessPart.long_obdc_cable, HarnessPart.usbc_coupler]) + car_parts: CarParts = CarParts.common([CarHarness.j533]) def init_make(self, CP: car.CarParams): - self.footnotes.insert(0, Footnote.VW_EXP_LONG) + self.footnotes.append(Footnote.VW_EXP_LONG) + if "SKODA" in CP.carFingerprint: + self.footnotes.append(Footnote.SKODA_HEATED_WINDSHIELD) + + if CP.carFingerprint in (CAR.CRAFTER_MK2, CAR.TRANSPORTER_T61): + self.car_parts = CarParts([Device.three_angled_mount, CarHarness.j533]) CAR_INFO: Dict[str, Union[VWCarInfo, List[VWCarInfo]]] = { @@ -195,11 +205,11 @@ CAR_INFO: Dict[str, Union[VWCarInfo, List[VWCarInfo]]] = { ], CAR.GOLF_MK7: [ VWCarInfo("Volkswagen e-Golf 2014-20"), - VWCarInfo("Volkswagen Golf 2015-20"), - VWCarInfo("Volkswagen Golf Alltrack 2015-19"), + VWCarInfo("Volkswagen Golf 2015-20", auto_resume=False), + VWCarInfo("Volkswagen Golf Alltrack 2015-19", auto_resume=False), VWCarInfo("Volkswagen Golf GTD 2015-20"), VWCarInfo("Volkswagen Golf GTE 2015-20"), - VWCarInfo("Volkswagen Golf GTI 2015-21"), + VWCarInfo("Volkswagen Golf GTI 2015-21", auto_resume=False), VWCarInfo("Volkswagen Golf R 2015-19"), VWCarInfo("Volkswagen Golf SportsVan 2015-20"), ], @@ -633,6 +643,7 @@ FW_VERSIONS = { b'\xf1\x870DD300045T \xf1\x891601', b'\xf1\x870DL300011H \xf1\x895201', b'\xf1\x870CW300042H \xf1\x891601', + b'\xf1\x870CW300042H \xf1\x891607', b'\xf1\x870GC300042H \xf1\x891404', b'\xf1\x870D9300018C \xf1\x895297', b'\xf1\x870GC300043 \xf1\x892301', @@ -640,6 +651,7 @@ FW_VERSIONS = { (Ecu.srs, 0x715, None): [ b'\xf1\x873Q0959655AE\xf1\x890195\xf1\x82\r56140056130012416612124111', b'\xf1\x873Q0959655AF\xf1\x890195\xf1\x82\r56140056130012026612120211', + b'\xf1\x873Q0959655AN\xf1\x890305\xf1\x82\r58160058140013036914110311', b'\xf1\x873Q0959655AN\xf1\x890306\xf1\x82\r58160058140013036914110311', b'\xf1\x873Q0959655BA\xf1\x890195\xf1\x82\r56140056130012516612125111', b'\xf1\x873Q0959655BB\xf1\x890195\xf1\x82\r56140056130012026612120211', @@ -656,6 +668,7 @@ FW_VERSIONS = { b'\xf1\x875Q0909143K \xf1\x892033\xf1\x820514B0060703', b'\xf1\x875Q0909143M \xf1\x892041\xf1\x820522B0060803', b'\xf1\x875Q0909143M \xf1\x892041\xf1\x820522B0080803', + b'\xf1\x875Q0909143P \xf1\x892051\xf1\x820526B0060905', b'\xf1\x875Q0909144AB\xf1\x891082\xf1\x82\00521B00606A1', b'\xf1\x875Q0909144S \xf1\x891063\xf1\x82\00516B00501A1', b'\xf1\x875Q0909144T \xf1\x891072\xf1\x82\00521B00703A1', @@ -668,6 +681,7 @@ FW_VERSIONS = { b'\xf1\x873Q0907572B \xf1\x890192', b'\xf1\x873Q0907572C \xf1\x890195', b'\xf1\x873Q0907572C \xf1\x890196', + b'\xf1\x875Q0907572P \xf1\x890682', b'\xf1\x875Q0907572R \xf1\x890771', ], }, @@ -1113,6 +1127,7 @@ FW_VERSIONS = { }, CAR.SKODA_KAROQ_MK1: { (Ecu.engine, 0x7e0, None): [ + b'\xf1\x8705E906018P \xf1\x895472', b'\xf1\x8705E906018P \xf1\x896020', b'\xf1\x8705L906022BS\xf1\x890913', ], @@ -1121,14 +1136,17 @@ FW_VERSIONS = { b'\xf1\x870GC300014L \xf1\x892802', ], (Ecu.srs, 0x715, None): [ + b'\xf1\x873Q0959655BH\xf1\x890703\xf1\x82\x0e1213001211001101131112012100', b'\xf1\x873Q0959655BH\xf1\x890712\xf1\x82\0161213001211001101131122012100', b'\xf1\x873Q0959655DE\xf1\x890731\xf1\x82\x0e1213001211001101131121012J00', ], (Ecu.eps, 0x712, None): [ + b'\xf1\x875Q0910143B \xf1\x892201\xf1\x82\x0563T6090500', b'\xf1\x875Q0910143C \xf1\x892211\xf1\x82\00567T6100500', b'\xf1\x875Q0910143C \xf1\x892211\xf1\x82\x0567T6100700', ], (Ecu.fwdRadar, 0x757, None): [ + b'\xf1\x872Q0907572AB\xf1\x890397', b'\xf1\x872Q0907572M \xf1\x890233', b'\xf1\x872Q0907572T \xf1\x890383', ], diff --git a/selfdrive/controls/controlsd.py b/selfdrive/controls/controlsd.py index ac9496bae..c5fa57713 100755 --- a/selfdrive/controls/controlsd.py +++ b/selfdrive/controls/controlsd.py @@ -20,7 +20,6 @@ from selfdrive.controls.lib.drive_helpers import VCruiseHelper, get_lag_adjusted from selfdrive.controls.lib.latcontrol import LatControl, MIN_LATERAL_CONTROL_SPEED from selfdrive.controls.lib.longcontrol import LongControl from selfdrive.controls.lib.latcontrol_pid import LatControlPID -from selfdrive.controls.lib.latcontrol_indi import LatControlINDI from selfdrive.controls.lib.latcontrol_angle import LatControlAngle, STEER_ANGLE_SATURATION_THRESHOLD from selfdrive.controls.lib.latcontrol_torque import LatControlTorque from selfdrive.controls.lib.events import Events, ET @@ -34,10 +33,11 @@ LANE_DEPARTURE_THRESHOLD = 0.1 REPLAY = "REPLAY" in os.environ SIMULATION = "SIMULATION" in os.environ +TESTING_CLOSET = "TESTING_CLOSET" in os.environ NOSENSOR = "NOSENSOR" in os.environ IGNORE_PROCESSES = {"loggerd", "encoderd", "statsd", "mapd"} -NO_IR_CTRL = Params().get_bool("dp_no_ir_ctrl") +NO_IR_CTRL = Params().get_bool("dp_device_no_ir_ctrl") if NO_IR_CTRL: IGNORE_PROCESSES |= {'driverCameraState', 'driverMonitoringState'} NO_FAN_CTRL = Params().get_bool("dp_no_fan_ctrl") @@ -91,6 +91,8 @@ class Controls: self.dp_no_gps_ctrl = self.params.get_bool("dp_no_gps_ctrl") self.dp_no_fan_ctrl = self.params.get_bool("dp_no_fan_ctrl") self.dp_alka = self.params.get_bool("dp_alka") + self.dp_0813 = self.params.get_bool("dp_0813") + self.dp_device_disable_temp_check = self.params.get_bool("dp_device_disable_temp_check") self.sm = sm if self.sm is None: ignore = ['testJoystick'] @@ -103,7 +105,7 @@ class Controls: self.sm = messaging.SubMaster(['deviceState', 'pandaStates', 'peripheralState', 'modelV2', 'liveCalibration', 'driverMonitoringState', 'longitudinalPlan', 'lateralPlan', 'liveLocationKalman', 'managerState', 'liveParameters', 'radarState', 'liveTorqueParameters', 'testJoystick'] + self.camera_packets, - ignore_alive=ignore, ignore_avg_freq=['radarState', 'longitudinalPlan', 'testJoystick']) + ignore_alive=ignore, ignore_avg_freq=['radarState', 'testJoystick']) if CI is None: # wait for one pandaState and one CAN packet @@ -111,7 +113,7 @@ class Controls: get_one_can(self.can_sock) num_pandas = len(messaging.recv_one_retry(self.sm.sock['pandaStates']).pandaStates) - experimental_long_allowed = self.params.get_bool("ExperimentalLongitudinalEnabled") and not is_release_branch() + experimental_long_allowed = not self.dp_0813 and self.params.get_bool("ExperimentalLongitudinalEnabled") # and not is_release_branch() self.CI, self.CP = get_car(self.can_sock, self.pm.sock['sendcan'], experimental_long_allowed, num_pandas) else: self.CI, self.CP = CI, CI.CP @@ -152,7 +154,7 @@ class Controls: put_nonblocking("CarParamsPersistent", cp_bytes) # cleanup old params - if not self.CP.experimentalLongitudinalAvailable or is_release_branch(): + if not self.CP.experimentalLongitudinalAvailable:# or is_release_branch(): self.params.remove("ExperimentalLongitudinalEnabled") if not self.CP.openpilotLongitudinalControl: self.params.remove("ExperimentalMode") @@ -170,8 +172,6 @@ class Controls: self.LaC = LatControlAngle(self.CP, self.CI) elif self.CP.lateralTuning.which() == 'pid': self.LaC = LatControlPID(self.CP, self.CI) - elif self.CP.lateralTuning.which() == 'indi': - self.LaC = LatControlINDI(self.CP, self.CI) elif self.CP.lateralTuning.which() == 'torque': self.LaC = LatControlTorque(self.CP, self.CI) @@ -197,6 +197,7 @@ class Controls: self.desired_curvature_rate = 0.0 self.experimental_mode = False self.v_cruise_helper = VCruiseHelper(self.CP) + self.recalibrating_seen = False # TODO: no longer necessary, aside from process replay self.sm['liveParameters'].valid = True @@ -276,7 +277,7 @@ class Controls: self.events.add_from_msg(CS.events) # Create events for temperature, disk space, and memory - if self.sm['deviceState'].thermalStatus >= ThermalStatus.red: + if not self.dp_device_disable_temp_check and self.sm['deviceState'].thermalStatus >= ThermalStatus.red: self.events.add(EventName.overheat) if self.sm['deviceState'].freeSpacePercent < 7 and not SIMULATION: # under 7% of space free no enable allowed @@ -305,7 +306,9 @@ class Controls: if cal_status == log.LiveCalibrationData.Status.uncalibrated: self.events.add(EventName.calibrationIncomplete) elif cal_status == log.LiveCalibrationData.Status.recalibrating: - set_offroad_alert("Offroad_Recalibration", True) + if not self.recalibrating_seen: + set_offroad_alert("Offroad_Recalibration", True) + self.recalibrating_seen = True self.events.add(EventName.calibrationRecalibrating) else: self.events.add(EventName.calibrationInvalid) @@ -389,7 +392,7 @@ class Controls: else: self.logged_comm_issue = None - if not self.sm['liveParameters'].valid: + if not self.sm['liveParameters'].valid and not TESTING_CLOSET: self.events.add(EventName.vehicleModelInvalid) if not self.sm['lateralPlan'].mpcSolutionValid: self.events.add(EventName.plannerError) @@ -872,6 +875,8 @@ class Controls: self.is_metric = self.params.get_bool("IsMetric") self.experimental_mode = self.params.get_bool("ExperimentalMode") and self.CP.openpilotLongitudinalControl + if self.CP.radarUnavailable and self.dp_0813: + self.experimental_mode = False # Sample data from sockets and get a carState CS = self.data_sample() diff --git a/selfdrive/controls/lib/alerts_offroad.json b/selfdrive/controls/lib/alerts_offroad.json index 42d2512e5..9226c94d8 100644 --- a/selfdrive/controls/lib/alerts_offroad.json +++ b/selfdrive/controls/lib/alerts_offroad.json @@ -30,7 +30,7 @@ "severity": 0 }, "Offroad_UnofficialHardware": { - "text": "Device failed to register. It will not connect to or upload to comma.ai servers, and receives no support from comma.ai. If this is an official device, contact support@comma.ai.", + "text": "Device failed to register. It will not connect to or upload to comma.ai servers, and receives no support from comma.ai. If this is an official device, visit https://comma.ai/support.", "severity": 1 }, "Offroad_StorageMissing": { diff --git a/selfdrive/controls/lib/drive_helpers.py b/selfdrive/controls/lib/drive_helpers.py index 163c3912c..efd7e1488 100644 --- a/selfdrive/controls/lib/drive_helpers.py +++ b/selfdrive/controls/lib/drive_helpers.py @@ -4,7 +4,7 @@ from cereal import car, log from common.conversions import Conversions as CV from common.numpy_fast import clip, interp from common.realtime import DT_MDL -from selfdrive.legacy_modeld.constants import T_IDXS +from selfdrive.hybrid_modeld.constants import T_IDXS # WARNING: this value was determined based on the model's training distribution, # model predictions above this speed can be unpredictable @@ -37,6 +37,13 @@ CRUISE_INTERVAL_SIGN = { ButtonType.decelCruise: -1, } +# rick - for 0813 +LAT_MPC_N = 16 +class MPC_COST_LAT: + PATH = 1.0 + HEADING = 1.0 + STEER_RATE = 1.0 + class VCruiseHelper: def __init__(self, CP): diff --git a/selfdrive/controls/lib/events.py b/selfdrive/controls/lib/events.py index fd9836865..ec20b7861 100644 --- a/selfdrive/controls/lib/events.py +++ b/selfdrive/controls/lib/events.py @@ -893,12 +893,6 @@ EVENTS: Dict[int, Dict[str, Union[Alert, AlertCallbackType]]] = { ET.NO_ENTRY: NoEntryAlert(_("LKAS Fault: Restart the Car")), }, - EventName.brakeUnavailable: { - ET.IMMEDIATE_DISABLE: ImmediateDisableAlert(_("Cruise Fault: Restart the Car")), - ET.PERMANENT: NormalPermanentAlert(_("Cruise Fault: Restart the car to engage")), - ET.NO_ENTRY: NoEntryAlert(_("Cruise Fault: Restart the Car")), - }, - EventName.reverseGear: { ET.PERMANENT: Alert( _("Reverse\nGear"), diff --git a/selfdrive/controls/lib/latcontrol_indi.py b/selfdrive/controls/lib/latcontrol_indi.py deleted file mode 100644 index dca82c672..000000000 --- a/selfdrive/controls/lib/latcontrol_indi.py +++ /dev/null @@ -1,120 +0,0 @@ -import math -import numpy as np - -from cereal import log -from common.filter_simple import FirstOrderFilter -from common.numpy_fast import clip, interp -from common.realtime import DT_CTRL -from selfdrive.controls.lib.latcontrol import LatControl - - -class LatControlINDI(LatControl): - def __init__(self, CP, CI): - super().__init__(CP, CI) - self.angle_steers_des = 0. - - A = np.array([[1.0, DT_CTRL, 0.0], - [0.0, 1.0, DT_CTRL], - [0.0, 0.0, 1.0]]) - C = np.array([[1.0, 0.0, 0.0], - [0.0, 1.0, 0.0]]) - - # Q = np.matrix([[1e-2, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 10.0]]) - # R = np.matrix([[1e-2, 0.0], [0.0, 1e3]]) - - # (x, l, K) = control.dare(np.transpose(A), np.transpose(C), Q, R) - # K = np.transpose(K) - K = np.array([[7.30262179e-01, 2.07003658e-04], - [7.29394177e+00, 1.39159419e-02], - [1.71022442e+01, 3.38495381e-02]]) - - self.speed = 0. - - self.K = K - self.A_K = A - np.dot(K, C) - self.x = np.array([[0.], [0.], [0.]]) - - self._RC = (CP.lateralTuning.indi.timeConstantBP, CP.lateralTuning.indi.timeConstantV) - self._G = (CP.lateralTuning.indi.actuatorEffectivenessBP, CP.lateralTuning.indi.actuatorEffectivenessV) - self._outer_loop_gain = (CP.lateralTuning.indi.outerLoopGainBP, CP.lateralTuning.indi.outerLoopGainV) - self._inner_loop_gain = (CP.lateralTuning.indi.innerLoopGainBP, CP.lateralTuning.indi.innerLoopGainV) - - self.steer_filter = FirstOrderFilter(0., self.RC, DT_CTRL) - self.reset() - - @property - def RC(self): - return interp(self.speed, self._RC[0], self._RC[1]) - - @property - def G(self): - return interp(self.speed, self._G[0], self._G[1]) - - @property - def outer_loop_gain(self): - return interp(self.speed, self._outer_loop_gain[0], self._outer_loop_gain[1]) - - @property - def inner_loop_gain(self): - return interp(self.speed, self._inner_loop_gain[0], self._inner_loop_gain[1]) - - def reset(self): - super().reset() - self.steer_filter.x = 0. - self.speed = 0. - - def update(self, active, CS, VM, params, last_actuators, steer_limited, desired_curvature, desired_curvature_rate, llk): - self.speed = CS.vEgo - # Update Kalman filter - y = np.array([[math.radians(CS.steeringAngleDeg)], [math.radians(CS.steeringRateDeg)]]) - self.x = np.dot(self.A_K, self.x) + np.dot(self.K, y) - - indi_log = log.ControlsState.LateralINDIState.new_message() - indi_log.steeringAngleDeg = math.degrees(self.x[0]) - indi_log.steeringRateDeg = math.degrees(self.x[1]) - indi_log.steeringAccelDeg = math.degrees(self.x[2]) - - steers_des = VM.get_steer_from_curvature(-desired_curvature, CS.vEgo, params.roll) - steers_des += math.radians(params.angleOffsetDeg) - indi_log.steeringAngleDesiredDeg = math.degrees(steers_des) - - # desired rate is the desired rate of change in the setpoint, not the absolute desired curvature - rate_des = VM.get_steer_from_curvature(-desired_curvature_rate, CS.vEgo, 0) - indi_log.steeringRateDesiredDeg = math.degrees(rate_des) - - if not active: - indi_log.active = False - self.steer_filter.x = 0.0 - output_steer = 0 - else: - # Expected actuator value - self.steer_filter.update_alpha(self.RC) - self.steer_filter.update(last_actuators.steer) - - # Compute acceleration error - rate_sp = self.outer_loop_gain * (steers_des - self.x[0]) + rate_des - accel_sp = self.inner_loop_gain * (rate_sp - self.x[1]) - accel_error = accel_sp - self.x[2] - - # Compute change in actuator - g_inv = 1. / self.G - delta_u = g_inv * accel_error - - # If steering pressed, only allow wind down - if CS.steeringPressed and (delta_u * last_actuators.steer > 0): - delta_u = 0 - - output_steer = self.steer_filter.x + delta_u - - output_steer = clip(output_steer, -self.steer_max, self.steer_max) - - indi_log.active = True - indi_log.rateSetPoint = float(rate_sp) - indi_log.accelSetPoint = float(accel_sp) - indi_log.accelError = float(accel_error) - indi_log.delayedOutput = float(self.steer_filter.x) - indi_log.delta = float(delta_u) - indi_log.output = float(output_steer) - indi_log.saturated = self._check_saturation(self.steer_max - abs(output_steer) < 1e-3, CS, steer_limited) - - return float(output_steer), float(steers_des), indi_log diff --git a/selfdrive/controls/lib/lateral_mpc_lib/acados_ocp_lat.json b/selfdrive/controls/lib/lateral_mpc_lib/acados_ocp_lat.json deleted file mode 100644 index af9c99f25..000000000 --- a/selfdrive/controls/lib/lateral_mpc_lib/acados_ocp_lat.json +++ /dev/null @@ -1,544 +0,0 @@ -{ - "acados_include_path": "/data/openpilot/third_party/acados/include/acados/include", - "acados_lib_path": "/data/openpilot/third_party/acados/include/acados/lib", - "code_export_directory": "/data/openpilot/selfdrive/controls/lib/lateral_mpc_lib/c_generated_code", - "constraints": { - "C": [], - "C_e": [], - "D": [], - "constr_type": "BGH", - "constr_type_e": "BGH", - "idxbu": [], - "idxbx": [ - 2, - 3 - ], - "idxbx_0": [ - 0, - 1, - 2, - 3 - ], - "idxbx_e": [], - "idxbxe_0": [ - 0, - 1, - 2, - 3 - ], - "idxsbu": [], - "idxsbx": [], - "idxsbx_e": [], - "idxsg": [], - "idxsg_e": [], - "idxsh": [], - "idxsh_e": [], - "idxsphi": [], - "idxsphi_e": [], - "lbu": [], - "lbx": [ - -1.5707963267948966, - -0.8726646259971648 - ], - "lbx_0": [ - 0.0, - 0.0, - 0.0, - 0.0 - ], - "lbx_e": [], - "lg": [], - "lg_e": [], - "lh": [], - "lh_e": [], - "lphi": [], - "lphi_e": [], - "lsbu": [], - "lsbx": [], - "lsbx_e": [], - "lsg": [], - "lsg_e": [], - "lsh": [], - "lsh_e": [], - "lsphi": [], - "lsphi_e": [], - "ubu": [], - "ubx": [ - 1.5707963267948966, - 0.8726646259971648 - ], - "ubx_0": [ - 0.0, - 0.0, - 0.0, - 0.0 - ], - "ubx_e": [], - "ug": [], - "ug_e": [], - "uh": [], - "uh_e": [], - "uphi": [], - "uphi_e": [], - "usbu": [], - "usbx": [], - "usbx_e": [], - "usg": [], - "usg_e": [], - "ush": [], - "ush_e": [], - "usphi": [], - "usphi_e": [] - }, - "cost": { - "Vu": [ - [ - 0.0 - ], - [ - 0.0 - ], - [ - 0.0 - ], - [ - 0.0 - ], - [ - 0.0 - ] - ], - "Vu_0": [ - [ - 0.0 - ], - [ - 0.0 - ], - [ - 0.0 - ], - [ - 0.0 - ], - [ - 0.0 - ] - ], - "Vx": [ - [ - 0.0, - 0.0, - 0.0, - 0.0 - ], - [ - 0.0, - 0.0, - 0.0, - 0.0 - ], - [ - 0.0, - 0.0, - 0.0, - 0.0 - ], - [ - 0.0, - 0.0, - 0.0, - 0.0 - ], - [ - 0.0, - 0.0, - 0.0, - 0.0 - ] - ], - "Vx_0": [ - [ - 0.0, - 0.0, - 0.0, - 0.0 - ], - [ - 0.0, - 0.0, - 0.0, - 0.0 - ], - [ - 0.0, - 0.0, - 0.0, - 0.0 - ], - [ - 0.0, - 0.0, - 0.0, - 0.0 - ], - [ - 0.0, - 0.0, - 0.0, - 0.0 - ] - ], - "Vx_e": [ - [ - 0.0, - 0.0, - 0.0, - 0.0 - ], - [ - 0.0, - 0.0, - 0.0, - 0.0 - ], - [ - 0.0, - 0.0, - 0.0, - 0.0 - ] - ], - "Vz": [], - "Vz_0": [], - "W": [ - [ - 0.0, - 0.0, - 0.0, - 0.0, - 0.0 - ], - [ - 0.0, - 0.0, - 0.0, - 0.0, - 0.0 - ], - [ - 0.0, - 0.0, - 0.0, - 0.0, - 0.0 - ], - [ - 0.0, - 0.0, - 0.0, - 0.0, - 0.0 - ], - [ - 0.0, - 0.0, - 0.0, - 0.0, - 0.0 - ] - ], - "W_0": [ - [ - 0.0, - 0.0, - 0.0, - 0.0, - 0.0 - ], - [ - 0.0, - 0.0, - 0.0, - 0.0, - 0.0 - ], - [ - 0.0, - 0.0, - 0.0, - 0.0, - 0.0 - ], - [ - 0.0, - 0.0, - 0.0, - 0.0, - 0.0 - ], - [ - 0.0, - 0.0, - 0.0, - 0.0, - 0.0 - ] - ], - "W_e": [ - [ - 0.0, - 0.0, - 0.0 - ], - [ - 0.0, - 0.0, - 0.0 - ], - [ - 0.0, - 0.0, - 0.0 - ] - ], - "Zl": [], - "Zl_e": [], - "Zu": [], - "Zu_e": [], - "cost_ext_fun_type": "casadi", - "cost_ext_fun_type_0": "casadi", - "cost_ext_fun_type_e": "casadi", - "cost_type": "NONLINEAR_LS", - "cost_type_0": "NONLINEAR_LS", - "cost_type_e": "NONLINEAR_LS", - "yref": [ - 0.0, - 0.0, - 0.0, - 0.0, - 0.0 - ], - "yref_0": [ - 0.0, - 0.0, - 0.0, - 0.0, - 0.0 - ], - "yref_e": [ - 0.0, - 0.0, - 0.0 - ], - "zl": [], - "zl_e": [], - "zu": [], - "zu_e": [] - }, - "cython_include_dirs": "/data/data/com.termux/files/usr/lib/python3.8/site-packages/numpy/core/include", - "dims": { - "N": 16, - "nbu": 0, - "nbx": 2, - "nbx_0": 4, - "nbx_e": 0, - "nbxe_0": 4, - "ng": 0, - "ng_e": 0, - "nh": 0, - "nh_e": 0, - "np": 2, - "nphi": 0, - "nphi_e": 0, - "nr": 0, - "nr_e": 0, - "ns": 0, - "ns_e": 0, - "nsbu": 0, - "nsbx": 0, - "nsbx_e": 0, - "nsg": 0, - "nsg_e": 0, - "nsh": 0, - "nsh_e": 0, - "nsphi": 0, - "nsphi_e": 0, - "nu": 1, - "nx": 4, - "ny": 5, - "ny_0": 5, - "ny_e": 3, - "nz": 0 - }, - "model": { - "dyn_disc_fun": null, - "dyn_disc_fun_jac": null, - "dyn_disc_fun_jac_hess": null, - "dyn_ext_fun_type": "casadi", - "dyn_source_discrete": null, - "gnsf": { - "nontrivial_f_LO": 1, - "purely_linear": 0 - }, - "name": "lat" - }, - "parameter_values": [ - 0.0, - 0.0 - ], - "problem_class": "OCP", - "simulink_opts": { - "inputs": { - "cost_W": 0, - "cost_W_0": 0, - "cost_W_e": 0, - "lbu": 1, - "lbx": 1, - "lbx_0": 1, - "lbx_e": 1, - "lg": 1, - "lh": 1, - "parameter_traj": 1, - "reset_solver": 0, - "u_init": 0, - "ubu": 1, - "ubx": 1, - "ubx_0": 1, - "ubx_e": 1, - "ug": 1, - "uh": 1, - "x_init": 0, - "y_ref": 1, - "y_ref_0": 1, - "y_ref_e": 1 - }, - "outputs": { - "CPU_time": 1, - "CPU_time_lin": 0, - "CPU_time_qp": 0, - "CPU_time_sim": 0, - "KKT_residual": 1, - "solver_status": 1, - "sqp_iter": 1, - "u0": 1, - "utraj": 0, - "x1": 1, - "xtraj": 0 - }, - "samplingtime": "t0" - }, - "solver_options": { - "Tsim": 0.009765625, - "alpha_min": 0.05, - "alpha_reduction": 0.7, - "collocation_type": "GAUSS_LEGENDRE", - "eps_sufficient_descent": 0.0001, - "exact_hess_constr": 1, - "exact_hess_cost": 1, - "exact_hess_dyn": 1, - "ext_cost_num_hess": 0, - "full_step_dual": 0, - "globalization": "FIXED_STEP", - "globalization_use_SOC": 0, - "hessian_approx": "GAUSS_NEWTON", - "hpipm_mode": "BALANCE", - "initialize_t_slacks": 0, - "integrator_type": "ERK", - "levenberg_marquardt": 0.0, - "line_search_use_sufficient_descent": 0, - "model_external_shared_lib_dir": null, - "model_external_shared_lib_name": null, - "nlp_solver_max_iter": 100, - "nlp_solver_step_length": 1.0, - "nlp_solver_tol_comp": 1e-06, - "nlp_solver_tol_eq": 1e-06, - "nlp_solver_tol_ineq": 1e-06, - "nlp_solver_tol_stat": 1e-06, - "nlp_solver_type": "SQP_RTI", - "print_level": 0, - "qp_solver": "PARTIAL_CONDENSING_HPIPM", - "qp_solver_cond_N": 1, - "qp_solver_iter_max": 1, - "qp_solver_tol_comp": null, - "qp_solver_tol_eq": null, - "qp_solver_tol_ineq": null, - "qp_solver_tol_stat": null, - "qp_solver_warm_start": 0, - "regularize_method": null, - "sim_method_jac_reuse": [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0 - ], - "sim_method_newton_iter": 3, - "sim_method_num_stages": [ - 4, - 4, - 4, - 4, - 4, - 4, - 4, - 4, - 4, - 4, - 4, - 4, - 4, - 4, - 4, - 4 - ], - "sim_method_num_steps": [ - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1 - ], - "tf": 2.5, - "time_steps": [ - 0.009765625, - 0.029296875, - 0.048828125, - 0.068359375, - 0.087890625, - 0.107421875, - 0.126953125, - 0.146484375, - 0.166015625, - 0.185546875, - 0.205078125, - 0.224609375, - 0.244140625, - 0.263671875, - 0.283203125, - 0.302734375 - ] - } -} \ No newline at end of file diff --git a/selfdrive/controls/lib/lateral_mpc_lib/c_generated_code/acados_sim_solver_lat.h b/selfdrive/controls/lib/lateral_mpc_lib/c_generated_code/acados_sim_solver_lat.h deleted file mode 100644 index 86b9c84c9..000000000 --- a/selfdrive/controls/lib/lateral_mpc_lib/c_generated_code/acados_sim_solver_lat.h +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright 2019 Gianluca Frison, Dimitris Kouzoupis, Robin Verschueren, - * Andrea Zanelli, Niels van Duijkeren, Jonathan Frey, Tommaso Sartor, - * Branimir Novoselnik, Rien Quirynen, Rezart Qelibari, Dang Doan, - * Jonas Koenemann, Yutao Chen, Tobias Schöls, Jonas Schlagenhauf, Moritz Diehl - * - * This file is part of acados. - * - * The 2-Clause BSD License - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE.; - */ - -#ifndef ACADOS_SIM_lat_H_ -#define ACADOS_SIM_lat_H_ - -#include "acados_c/sim_interface.h" -#include "acados_c/external_function_interface.h" - -#define LAT_NX 4 -#define LAT_NZ 0 -#define LAT_NU 1 -#define LAT_NP 2 - -#ifdef __cplusplus -extern "C" { -#endif - - -// ** capsule for solver data ** -typedef struct sim_solver_capsule -{ - // acados objects - sim_in *acados_sim_in; - sim_out *acados_sim_out; - sim_solver *acados_sim_solver; - sim_opts *acados_sim_opts; - sim_config *acados_sim_config; - void *acados_sim_dims; - - /* external functions */ - // ERK - external_function_param_casadi * sim_forw_vde_casadi; - external_function_param_casadi * sim_expl_ode_fun_casadi; - external_function_param_casadi * sim_expl_ode_hess; - - // IRK - external_function_param_casadi * sim_impl_dae_fun; - external_function_param_casadi * sim_impl_dae_fun_jac_x_xdot_z; - external_function_param_casadi * sim_impl_dae_jac_x_xdot_u_z; - external_function_param_casadi * sim_impl_dae_hess; - - // GNSF - external_function_param_casadi * sim_gnsf_phi_fun; - external_function_param_casadi * sim_gnsf_phi_fun_jac_y; - external_function_param_casadi * sim_gnsf_phi_jac_y_uhat; - external_function_param_casadi * sim_gnsf_f_lo_jac_x1_x1dot_u_z; - external_function_param_casadi * sim_gnsf_get_matrices_fun; - -} sim_solver_capsule; - - -ACADOS_SYMBOL_EXPORT int lat_acados_sim_create(sim_solver_capsule *capsule); -ACADOS_SYMBOL_EXPORT int lat_acados_sim_solve(sim_solver_capsule *capsule); -ACADOS_SYMBOL_EXPORT int lat_acados_sim_free(sim_solver_capsule *capsule); -ACADOS_SYMBOL_EXPORT int lat_acados_sim_update_params(sim_solver_capsule *capsule, double *value, int np); - -ACADOS_SYMBOL_EXPORT sim_config * lat_acados_get_sim_config(sim_solver_capsule *capsule); -ACADOS_SYMBOL_EXPORT sim_in * lat_acados_get_sim_in(sim_solver_capsule *capsule); -ACADOS_SYMBOL_EXPORT sim_out * lat_acados_get_sim_out(sim_solver_capsule *capsule); -ACADOS_SYMBOL_EXPORT void * lat_acados_get_sim_dims(sim_solver_capsule *capsule); -ACADOS_SYMBOL_EXPORT sim_opts * lat_acados_get_sim_opts(sim_solver_capsule *capsule); -ACADOS_SYMBOL_EXPORT sim_solver * lat_acados_get_sim_solver(sim_solver_capsule *capsule); - - -ACADOS_SYMBOL_EXPORT sim_solver_capsule * lat_acados_sim_solver_create_capsule(void); -ACADOS_SYMBOL_EXPORT int lat_acados_sim_solver_free_capsule(sim_solver_capsule *capsule); - -#ifdef __cplusplus -} -#endif - -#endif // ACADOS_SIM_lat_H_ diff --git a/selfdrive/controls/lib/lateral_mpc_lib/c_generated_code/make_sfun_lat.m b/selfdrive/controls/lib/lateral_mpc_lib/c_generated_code/make_sfun_lat.m deleted file mode 100644 index f8371f3e8..000000000 --- a/selfdrive/controls/lib/lateral_mpc_lib/c_generated_code/make_sfun_lat.m +++ /dev/null @@ -1,125 +0,0 @@ -% -% Copyright 2019 Gianluca Frison, Dimitris Kouzoupis, Robin Verschueren, -% Andrea Zanelli, Niels van Duijkeren, Jonathan Frey, Tommaso Sartor, -% Branimir Novoselnik, Rien Quirynen, Rezart Qelibari, Dang Doan, -% Jonas Koenemann, Yutao Chen, Tobias Schöls, Jonas Schlagenhauf, Moritz Diehl -% -% This file is part of acados. -% -% The 2-Clause BSD License -% -% Redistribution and use in source and binary forms, with or without -% modification, are permitted provided that the following conditions are met: -% -% 1. Redistributions of source code must retain the above copyright notice, -% this list of conditions and the following disclaimer. -% -% 2. Redistributions in binary form must reproduce the above copyright notice, -% this list of conditions and the following disclaimer in the documentation -% and/or other materials provided with the distribution. -% -% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -% ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -% LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -% CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -% SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -% INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -% CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -% ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -% POSSIBILITY OF SUCH DAMAGE.; -% - -SOURCES = { ... - 'lat_model/lat_expl_ode_fun.c', ... - 'lat_model/lat_expl_vde_forw.c',... - 'lat_cost/lat_cost_y_0_fun.c',... - 'lat_cost/lat_cost_y_0_fun_jac_ut_xt.c',... - 'lat_cost/lat_cost_y_0_hess.c',... - 'lat_cost/lat_cost_y_fun.c',... - 'lat_cost/lat_cost_y_fun_jac_ut_xt.c',... - 'lat_cost/lat_cost_y_hess.c',... - 'lat_cost/lat_cost_y_e_fun.c',... - 'lat_cost/lat_cost_y_e_fun_jac_ut_xt.c',... - 'lat_cost/lat_cost_y_e_hess.c',... - 'acados_solver_sfunction_lat.c', ... - 'acados_solver_lat.c' - }; - -INC_PATH = '/data/openpilot/third_party/acados/include/acados/include'; - -INCS = {['-I', fullfile(INC_PATH, 'blasfeo', 'include')], ... - ['-I', fullfile(INC_PATH, 'hpipm', 'include')], ... - ['-I', fullfile(INC_PATH, 'acados')], ... - ['-I', fullfile(INC_PATH)]}; - - - -CFLAGS = 'CFLAGS=$CFLAGS'; -LDFLAGS = 'LDFLAGS=$LDFLAGS'; -COMPFLAGS = 'COMPFLAGS=$COMPFLAGS'; -COMPDEFINES = 'COMPDEFINES=$COMPDEFINES'; - - - -LIB_PATH = ['-L', fullfile('/data/openpilot/third_party/acados/include/acados/lib')]; - -LIBS = {'-lacados', '-lhpipm', '-lblasfeo'}; - -% acados linking libraries and flags - - -mex('-v', '-O', CFLAGS, LDFLAGS, COMPFLAGS, COMPDEFINES, INCS{:}, ... - LIB_PATH, LIBS{:}, SOURCES{:}, ... - '-output', 'acados_solver_sfunction_lat' ); - -fprintf( [ '\n\nSuccessfully created sfunction:\nacados_solver_sfunction_lat', '.', ... - eval('mexext')] ); - - -%% print note on usage of s-function -fprintf('\n\nNote: Usage of Sfunction is as follows:\n') -input_note = 'Inputs are:\n'; -i_in = 1; -input_note = strcat(input_note, num2str(i_in), ') lbx_0 - lower bound on x for stage 0,',... - ' size [4]\n '); -i_in = i_in + 1; -input_note = strcat(input_note, num2str(i_in), ') ubx_0 - upper bound on x for stage 0,',... - ' size [4]\n '); -i_in = i_in + 1; -input_note = strcat(input_note, num2str(i_in), ') parameters - concatenated for all shooting nodes 0 to N+1,',... - ' size [34]\n '); -i_in = i_in + 1; -input_note = strcat(input_note, num2str(i_in), ') y_ref_0, size [5]\n '); -i_in = i_in + 1; -input_note = strcat(input_note, num2str(i_in), ') y_ref - concatenated for shooting nodes 1 to N-1,',... - ' size [75]\n '); -i_in = i_in + 1; -input_note = strcat(input_note, num2str(i_in), ') y_ref_e, size [3]\n '); -i_in = i_in + 1; -input_note = strcat(input_note, num2str(i_in), ') lbx for shooting nodes 1 to N-1, size [30]\n '); -i_in = i_in + 1; -input_note = strcat(input_note, num2str(i_in), ') ubx for shooting nodes 1 to N-1, size [30]\n '); -i_in = i_in + 1; - -fprintf(input_note) - -disp(' ') - -output_note = 'Outputs are:\n'; -i_out = 0; -i_out = i_out + 1; -output_note = strcat(output_note, num2str(i_out), ') u0, control input at node 0, size [1]\n '); -i_out = i_out + 1; -output_note = strcat(output_note, num2str(i_out), ') acados solver status (0 = SUCCESS)\n '); -i_out = i_out + 1; -output_note = strcat(output_note, num2str(i_out), ') KKT residual\n '); -i_out = i_out + 1; -output_note = strcat(output_note, num2str(i_out), ') x1, state at node 1\n '); -i_out = i_out + 1; -output_note = strcat(output_note, num2str(i_out), ') CPU time\n '); -i_out = i_out + 1; -output_note = strcat(output_note, num2str(i_out), ') SQP iterations\n '); - -fprintf(output_note) diff --git a/selfdrive/controls/lib/lateral_mpc_lib/lat_mpc.py b/selfdrive/controls/lib/lateral_mpc_lib/lat_mpc.py index 8218fcd70..48be1957b 100755 --- a/selfdrive/controls/lib/lateral_mpc_lib/lat_mpc.py +++ b/selfdrive/controls/lib/lateral_mpc_lib/lat_mpc.py @@ -5,7 +5,7 @@ import numpy as np from casadi import SX, vertcat, sin, cos from common.realtime import sec_since_boot # WARNING: imports outside of constants will not trigger a rebuild -from selfdrive.legacy_modeld.constants import T_IDXS +from selfdrive.hybrid_modeld.constants import T_IDXS if __name__ == '__main__': # generating code from third_party.acados.acados_template import AcadosModel, AcadosOcp, AcadosOcpSolver diff --git a/selfdrive/controls/lib/lateral_planner.py b/selfdrive/controls/lib/lateral_planner.py index 7ada7d6c8..7180ab951 100644 --- a/selfdrive/controls/lib/lateral_planner.py +++ b/selfdrive/controls/lib/lateral_planner.py @@ -129,7 +129,7 @@ class LateralPlanner: self.last_cloudlog_t = t cloudlog.warning("Lateral mpc - nan: True") - if self.lat_mpc.cost > 20000. or mpc_nans: + if self.lat_mpc.cost > 1e6 or mpc_nans: self.solution_invalid_cnt += 1 else: self.solution_invalid_cnt = 0 diff --git a/selfdrive/controls/lib/legacy_lateral_mpc_lib/lat_mpc.py b/selfdrive/controls/lib/legacy_lateral_mpc_lib/lat_mpc.py old mode 100755 new mode 100644 index b58a39bc2..65df64d1c --- a/selfdrive/controls/lib/legacy_lateral_mpc_lib/lat_mpc.py +++ b/selfdrive/controls/lib/legacy_lateral_mpc_lib/lat_mpc.py @@ -5,6 +5,8 @@ import numpy as np from casadi import SX, vertcat, sin, cos from common.realtime import sec_since_boot +# from selfdrive.controls.lib.drive_helpers import LAT_MPC_N as N +N = 16 from selfdrive.legacy_modeld.constants import T_IDXS if __name__ == '__main__': # generating code @@ -19,7 +21,6 @@ X_DIM = 4 P_DIM = 2 MODEL_NAME = 'lat' ACADOS_SOLVER_TYPE = 'SQP_RTI' -N = 16 def gen_lat_model(): model = AcadosModel() @@ -89,7 +90,7 @@ def gen_lat_ocp(): # TODO hacky weights to keep behavior the same ocp.model.cost_y_expr = vertcat(y_ego, ((v_ego +5.0) * psi_ego), - ((v_ego + 5.0) * 4.0 * curv_rate)) + ((v_ego +5.0) * 4 * curv_rate)) ocp.model.cost_y_expr_e = vertcat(y_ego, ((v_ego +5.0) * psi_ego)) @@ -147,7 +148,7 @@ class LateralMpc(): #TODO hacky weights to keep behavior the same self.solver.cost_set(N, 'W', (3/20.)*W[:2,:2]) - def run(self, x0, p, y_pts, heading_pts, curv_rate_pts): + def run(self, x0, p, y_pts, heading_pts): x0_cp = np.copy(x0) p_cp = np.copy(p) self.solver.constraints_set(0, "lbx", x0_cp) @@ -156,7 +157,6 @@ class LateralMpc(): v_ego = p_cp[0] # rotation_radius = p_cp[1] self.yref[:,1] = heading_pts*(v_ego+5.0) - self.yref[:,2] = curv_rate_pts * (v_ego+5.0) * 4.0 for i in range(N): self.solver.cost_set(i, "yref", self.yref[i]) self.solver.set(i, "p", p_cp) @@ -177,4 +177,4 @@ class LateralMpc(): if __name__ == "__main__": ocp = gen_lat_ocp() AcadosOcpSolver.generate(ocp, json_file=JSON_FILE) - # AcadosOcpSolver.build(ocp.code_export_directory, with_cython=True) + # AcadosOcpSolver.build(ocp.code_export_directory, with_cython=True) \ No newline at end of file diff --git a/selfdrive/controls/lib/legacy_lateral_planner.py b/selfdrive/controls/lib/legacy_lateral_planner.py index 3aa3cbf62..1cf26e8c8 100644 --- a/selfdrive/controls/lib/legacy_lateral_planner.py +++ b/selfdrive/controls/lib/legacy_lateral_planner.py @@ -1,47 +1,46 @@ -''' -This is the lateral_planner from 0.8.16 - -reason I keep this as a separate file is that Nuclear Grade model released during 0.8.15 / 0.8.16. -So it could handle better with old planners. - -Note 1: This may not work in newer version. - -''' - import numpy as np from common.realtime import sec_since_boot, DT_MDL from common.numpy_fast import interp from system.swaglog import cloudlog from selfdrive.controls.lib.legacy_lateral_mpc_lib.lat_mpc import LateralMpc -from selfdrive.controls.lib.drive_helpers import CONTROL_N -from selfdrive.controls.lib.legacy_lateral_mpc_lib.lat_mpc import N as LAT_MPC_N +from selfdrive.controls.lib.drive_helpers import CONTROL_N, MPC_COST_LAT, LAT_MPC_N, CAR_ROTATION_RADIUS from selfdrive.controls.lib.lane_planner import LanePlanner, TRAJECTORY_SIZE from selfdrive.controls.lib.desire_helper import DesireHelper import cereal.messaging as messaging from cereal import log +from common.params import Params -class MPC_COST_LAT: - PATH = 1.0 - HEADING = 1.0 - STEER_RATE = 1.0 +STEER_RATE_COST = { + "chrysler": 0.7, + "ford": 1., + "gm": 1., + "honda": 0.5, + "hyundai": 0.5, + "mazda": 1., + "nissan": 0.5, + "subaru": 0.7, + "tesla": 0.5, + "toyota": 1., + "volkswagen": 1., +} class LateralPlanner: def __init__(self, CP): - # rick - Change this to switch laneline / laneless mode - self.use_lanelines = False self.LP = LanePlanner() self.DH = DesireHelper() + self.dp_lat_lane_priority_mode = Params().get_bool("dp_lat_lane_priority_mode") + self.dp_lat_lane_priority_mode_active = False - # Vehicle model parameters used to calculate lateral movement of car - self.factor1 = CP.wheelbase - CP.centerToFront - self.factor2 = (CP.centerToFront * CP.mass) / (CP.wheelbase * CP.tireStiffnessRear) self.last_cloudlog_t = 0 + try: + self.steer_rate_cost = STEER_RATE_COST[CP.carName] + except: + self.steer_rate_cost = 0. self.solution_invalid_cnt = 0 self.path_xyz = np.zeros((TRAJECTORY_SIZE, 3)) self.path_xyz_stds = np.ones((TRAJECTORY_SIZE, 3)) self.plan_yaw = np.zeros((TRAJECTORY_SIZE,)) - self.plan_curv_rate = np.zeros((TRAJECTORY_SIZE,)) self.t_idxs = np.arange(TRAJECTORY_SIZE) self.y_pts = np.zeros(TRAJECTORY_SIZE) @@ -62,7 +61,7 @@ class LateralPlanner: if len(md.position.x) == TRAJECTORY_SIZE and len(md.orientation.x) == TRAJECTORY_SIZE: self.path_xyz = np.column_stack([md.position.x, md.position.y, md.position.z]) self.t_idxs = np.array(md.position.t) - self.plan_yaw = np.array(md.orientation.z) + self.plan_yaw = list(md.orientation.z) if len(md.position.xStd) == TRAJECTORY_SIZE: self.path_xyz_stds = np.column_stack([md.position.xStd, md.position.yStd, md.position.zStd]) @@ -75,35 +74,36 @@ class LateralPlanner: self.LP.lll_prob *= self.DH.lane_change_ll_prob self.LP.rll_prob *= self.DH.lane_change_ll_prob + # dp - check laneline prob when priority is on + use_laneline = False + if self.dp_lat_lane_priority_mode: + self._update_laneless_laneline_mode() + use_laneline = self.dp_lat_lane_priority_mode_active + # Calculate final driving path and set MPC costs - if self.use_lanelines: + if use_laneline: d_path_xyz = self.LP.get_d_path(v_ego, self.t_idxs, self.path_xyz) - self.lat_mpc.set_weights(MPC_COST_LAT.PATH, MPC_COST_LAT.HEADING, MPC_COST_LAT.STEER_RATE) + self.lat_mpc.set_weights(MPC_COST_LAT.PATH, MPC_COST_LAT.HEADING, self.steer_rate_cost) else: d_path_xyz = self.path_xyz + path_cost = np.clip(abs(self.path_xyz[0, 1] / self.path_xyz_stds[0, 1]), 0.5, 1.5) * MPC_COST_LAT.PATH # Heading cost is useful at low speed, otherwise end of plan can be off-heading - heading_cost = interp(v_ego, [5.0, 10.0], [MPC_COST_LAT.HEADING, 0.15]) - self.lat_mpc.set_weights(MPC_COST_LAT.PATH, heading_cost, MPC_COST_LAT.STEER_RATE) + heading_cost = interp(v_ego, [5.0, 10.0], [MPC_COST_LAT.HEADING, 0.0]) + self.lat_mpc.set_weights(path_cost, heading_cost, self.steer_rate_cost) y_pts = np.interp(v_ego * self.t_idxs[:LAT_MPC_N + 1], np.linalg.norm(d_path_xyz, axis=1), d_path_xyz[:, 1]) heading_pts = np.interp(v_ego * self.t_idxs[:LAT_MPC_N + 1], np.linalg.norm(self.path_xyz, axis=1), self.plan_yaw) - curv_rate_pts = np.interp(v_ego * self.t_idxs[:LAT_MPC_N + 1], np.linalg.norm(self.path_xyz, axis=1), self.plan_curv_rate) self.y_pts = y_pts assert len(y_pts) == LAT_MPC_N + 1 assert len(heading_pts) == LAT_MPC_N + 1 - assert len(curv_rate_pts) == LAT_MPC_N + 1 - lateral_factor = max(0, self.factor1 - (self.factor2 * v_ego**2)) - p = np.array([v_ego, lateral_factor]) + # self.x0[4] = v_ego + p = np.array([v_ego, CAR_ROTATION_RADIUS]) self.lat_mpc.run(self.x0, p, y_pts, - heading_pts, - curv_rate_pts) + heading_pts) # init state for next - # mpc.u_sol is the desired curvature rate given x0 curv state. - # with x0[3] = measured_curvature, this would be the actual desired rate. - # instead, interpolate x_sol so that x0[3] is the desired curvature for lat_control. self.x0[3] = interp(DT_MDL, self.t_idxs[:LAT_MPC_N + 1], self.lat_mpc.x_sol[:, 3]) # Check for infeasible MPC solution @@ -127,15 +127,11 @@ class LateralPlanner: plan_send.valid = sm.all_checks(service_list=['carState', 'controlsState', 'modelV2']) lateralPlan = plan_send.lateralPlan - lateralPlan.modelMonoTime = sm.logMonoTime['modelV2'] - # rick - deprecated # lateralPlan.laneWidth = float(self.LP.lane_width) lateralPlan.dPathPoints = self.y_pts.tolist() lateralPlan.psis = self.lat_mpc.x_sol[0:CONTROL_N, 2].tolist() - lateralPlan.curvatures = self.lat_mpc.x_sol[0:CONTROL_N, 3].tolist() lateralPlan.curvatureRates = [float(x) for x in self.lat_mpc.u_sol[0:CONTROL_N - 1]] + [0.0] - # rick - deprecated # lateralPlan.lProb = float(self.LP.lll_prob) # lateralPlan.rProb = float(self.LP.rll_prob) # lateralPlan.dProb = float(self.LP.d_prob) @@ -144,8 +140,16 @@ class LateralPlanner: lateralPlan.solverExecutionTime = self.lat_mpc.solve_time lateralPlan.desire = self.DH.desire - lateralPlan.useLaneLines = False + lateralPlan.useLaneLines = self.dp_lat_lane_priority_mode and self.dp_lat_lane_priority_mode_active lateralPlan.laneChangeState = self.DH.lane_change_state lateralPlan.laneChangeDirection = self.DH.lane_change_direction pm.send('lateralPlan', plan_send) + + + def _update_laneless_laneline_mode(self): + # decide what mode should we use + if (self.LP.lll_prob + self.LP.rll_prob)/2 < 0.3: + self.dp_lat_lane_priority_mode_active = False + if (self.LP.lll_prob + self.LP.rll_prob)/2 > 0.5: + self.dp_lat_lane_priority_mode_active = True \ No newline at end of file diff --git a/selfdrive/controls/lib/legacy_longitudinal_mpc_lib/c_generated_code/acados_solver_long.h b/selfdrive/controls/lib/legacy_longitudinal_mpc_lib/c_generated_code/acados_solver_long.h index 1b07566e2..afc630825 100644 --- a/selfdrive/controls/lib/legacy_longitudinal_mpc_lib/c_generated_code/acados_solver_long.h +++ b/selfdrive/controls/lib/legacy_longitudinal_mpc_lib/c_generated_code/acados_solver_long.h @@ -42,7 +42,7 @@ #define LONG_NX 3 #define LONG_NZ 0 #define LONG_NU 1 -#define LONG_NP 4 +#define LONG_NP 6 #define LONG_NBX 0 #define LONG_NBX0 3 #define LONG_NBU 0 diff --git a/selfdrive/controls/lib/legacy_longitudinal_mpc_lib/c_generated_code/libacados_ocp_solver_long.so b/selfdrive/controls/lib/legacy_longitudinal_mpc_lib/c_generated_code/libacados_ocp_solver_long.so index 17e6e1b37..0d5ddf608 100755 Binary files a/selfdrive/controls/lib/legacy_longitudinal_mpc_lib/c_generated_code/libacados_ocp_solver_long.so and b/selfdrive/controls/lib/legacy_longitudinal_mpc_lib/c_generated_code/libacados_ocp_solver_long.so differ diff --git a/selfdrive/controls/lib/legacy_longitudinal_mpc_lib/long_mpc.py b/selfdrive/controls/lib/legacy_longitudinal_mpc_lib/long_mpc.py index 37e3306bd..f3f6349f5 100644 --- a/selfdrive/controls/lib/legacy_longitudinal_mpc_lib/long_mpc.py +++ b/selfdrive/controls/lib/legacy_longitudinal_mpc_lib/long_mpc.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 import os import numpy as np - +from cereal import log from common.realtime import sec_since_boot from common.numpy_fast import clip, interp from system.swaglog import cloudlog @@ -24,7 +24,7 @@ SOURCES = ['lead0', 'lead1', 'cruise'] X_DIM = 3 U_DIM = 1 -PARAM_DIM = 4 +PARAM_DIM = 6 COST_E_DIM = 5 COST_DIM = COST_E_DIM + 1 CONSTR_DIM = 4 @@ -45,24 +45,44 @@ ACADOS_SOLVER_TYPE = 'SQP_RTI' # much better convergence of the MPC with low iterations N = 12 MAX_T = 10.0 -T_IDXS_LST = [index_function(idx, max_val=MAX_T, max_idx=N) for idx in range(N+1)] +T_IDXS_LST = [index_function(idx, max_val=MAX_T, max_idx=N+1) for idx in range(N+1)] T_IDXS = np.array(T_IDXS_LST) T_DIFFS = np.diff(T_IDXS, prepend=[0.]) MIN_ACCEL = -3.5 -T_FOLLOW = 1.45 +# T_FOLLOW = 1.45 COMFORT_BRAKE = 2.5 STOP_DISTANCE = 6.0 +def get_jerk_factor(personality=log.LongitudinalPersonality.standard): + if personality==log.LongitudinalPersonality.relaxed: + return 1.0 + elif personality==log.LongitudinalPersonality.standard: + return 1.0 + elif personality==log.LongitudinalPersonality.aggressive: + return 0.5 + else: + raise NotImplementedError("Longitudinal personality not supported") + + +def get_T_FOLLOW(personality=log.LongitudinalPersonality.standard): + if personality==log.LongitudinalPersonality.relaxed: + return 1.75 + elif personality==log.LongitudinalPersonality.standard: + return 1.45 + elif personality==log.LongitudinalPersonality.aggressive: + return 1.25 + else: + raise NotImplementedError("Longitudinal personality not supported") + def get_stopped_equivalence_factor(v_lead): return (v_lead**2) / (2 * COMFORT_BRAKE) -def get_safe_obstacle_distance(v_ego): - return (v_ego**2) / (2 * COMFORT_BRAKE) + T_FOLLOW * v_ego + STOP_DISTANCE - -def desired_follow_distance(v_ego, v_lead): - return get_safe_obstacle_distance(v_ego) - get_stopped_equivalence_factor(v_lead) +def get_safe_obstacle_distance(v_ego, t_follow, stop_distance): + return (v_ego**2) / (2 * COMFORT_BRAKE) + t_follow * v_ego + stop_distance +def desired_follow_distance(v_ego, v_lead, t_follow=get_T_FOLLOW(), stop_distance=STOP_DISTANCE): + return get_safe_obstacle_distance(v_ego, t_follow, stop_distance) - get_stopped_equivalence_factor(v_lead) def gen_long_model(): model = AcadosModel() @@ -89,7 +109,9 @@ def gen_long_model(): a_max = SX.sym('a_max') x_obstacle = SX.sym('x_obstacle') prev_a = SX.sym('prev_a') - model.p = vertcat(a_min, a_max, x_obstacle, prev_a) + lead_t_follow = SX.sym('lead_t_follow') + stop_distance = SX.sym('stop_distance') + model.p = vertcat(a_min, a_max, x_obstacle, prev_a, lead_t_follow, stop_distance) # dynamics model f_expl = vertcat(v_ego, a_ego, j_ego) @@ -123,11 +145,13 @@ def gen_long_ocp(): a_min, a_max = ocp.model.p[0], ocp.model.p[1] x_obstacle = ocp.model.p[2] prev_a = ocp.model.p[3] + lead_t_follow = ocp.model.p[4] + stop_distance = ocp.model.p[5] ocp.cost.yref = np.zeros((COST_DIM, )) ocp.cost.yref_e = np.zeros((COST_E_DIM, )) - desired_dist_comfort = get_safe_obstacle_distance(v_ego) + desired_dist_comfort = get_safe_obstacle_distance(v_ego, lead_t_follow, stop_distance) # The main cost in normal operation is how close you are to the "desired" distance # from an obstacle at every timestep. This obstacle can be a lead car @@ -153,7 +177,7 @@ def gen_long_ocp(): x0 = np.zeros(X_DIM) ocp.constraints.x0 = x0 - ocp.parameter_values = np.array([-1.2, 1.2, 0.0, 0.0]) + ocp.parameter_values = np.array([-1.2, 1.2, 0.0, 0.0, get_T_FOLLOW(), STOP_DISTANCE]) # We put all constraint cost weights to 0 and only set them at runtime cost_weights = np.zeros(CONSTR_DIM) @@ -192,14 +216,11 @@ def gen_long_ocp(): class LongitudinalMpc: def __init__(self, e2e=False): self.e2e = e2e - self.solver = AcadosOcpSolverCython(MODEL_NAME, ACADOS_SOLVER_TYPE, N) self.reset() self.source = SOURCES[2] def reset(self): - # self.solver = AcadosOcpSolverCython(MODEL_NAME, ACADOS_SOLVER_TYPE, N) - self.solver.reset() - # self.solver.options_set('print_level', 2) + self.solver = AcadosOcpSolverCython(MODEL_NAME, ACADOS_SOLVER_TYPE, N) self.v_solution = np.zeros(N+1) self.a_solution = np.zeros(N+1) self.prev_a = np.array(self.a_solution) @@ -231,6 +252,8 @@ class LongitudinalMpc: self.params[:,0] = -10. self.params[:,1] = 10. self.params[:,2] = 1e5 + self.params[:,4] = get_T_FOLLOW() + self.params[:,5] = STOP_DISTANCE else: self.set_weights_for_lead_policy(prev_accel_constraint) @@ -251,7 +274,7 @@ class LongitudinalMpc: self.solver.cost_set(i, 'Zl', Zl) def set_weights_for_xva_policy(self): - W = np.asfortranarray(np.diag([0., 0.2, 0.25, 1., 0.0, .1])) + W = np.asfortranarray(np.diag([0., 10., 1., 10., 0.0, 1.])) for i in range(N): self.solver.cost_set(i, 'W', W) # Setting the slice without the copy make the array not contiguous, @@ -306,7 +329,8 @@ class LongitudinalMpc: self.cruise_min_a = min_a self.cruise_max_a = max_a - def update(self, carstate, radarstate, v_cruise): + def update(self, carstate, radarstate, v_cruise, personality=log.LongitudinalPersonality.standard, stop_distance=STOP_DISTANCE): + t_follow = get_T_FOLLOW(personality) v_ego = self.x0[1] self.status = radarstate.leadOne.status or radarstate.leadTwo.status @@ -330,26 +354,23 @@ class LongitudinalMpc: v_cruise_clipped = np.clip(v_cruise * np.ones(N+1), v_lower, v_upper) - cruise_obstacle = np.cumsum(T_DIFFS * v_cruise_clipped) + get_safe_obstacle_distance(v_cruise_clipped) + cruise_obstacle = np.cumsum(T_DIFFS * v_cruise_clipped) + get_safe_obstacle_distance(v_cruise_clipped, t_follow, stop_distance) x_obstacles = np.column_stack([lead_0_obstacle, lead_1_obstacle, cruise_obstacle]) self.source = SOURCES[np.argmin(x_obstacles[0])] self.params[:,2] = np.min(x_obstacles, axis=1) self.params[:,3] = np.copy(self.prev_a) + self.params[:,4] = t_follow + self.params[:,5] = stop_distance self.run() if (np.any(lead_xv_0[:,0] - self.x_sol[:,0] < CRASH_DISTANCE) and - radarstate.leadOne.modelProb > 0.9): + radarstate.leadOne.modelProb > 0.9): self.crash_cnt += 1 else: self.crash_cnt = 0 def update_with_xva(self, x, v, a): - # v, and a are in local frame, but x is wrt the x[0] position - # In >90degree turns, x goes to 0 (and may even be -ve) - # So, we use integral(v) + x[0] to obtain the forward-distance - xforward = ((v[1:] + v[:-1]) / 2) * (T_IDXS[1:] - T_IDXS[:-1]) - x = np.cumsum(np.insert(xforward, 0, x[0])) self.yref[:,1] = x self.yref[:,2] = v self.yref[:,3] = a @@ -360,8 +381,6 @@ class LongitudinalMpc: self.run() def run(self): - # t0 = sec_since_boot() - # reset = 0 for i in range(N+1): self.solver.set(i, 'p', self.params[i]) self.solver.constraints_set(0, "lbx", self.x0) @@ -396,11 +415,9 @@ class LongitudinalMpc: self.last_cloudlog_t = t cloudlog.warning(f"Long mpc reset, solution_status: {self.solution_status}") self.reset() - # reset = 1 - # print(f"long_mpc timings: total internal {self.solve_time:.2e}, external: {(sec_since_boot() - t0):.2e} qp {self.time_qp_solution:.2e}, lin {self.time_linearization:.2e} qp_iter {qp_iter}, reset {reset}") if __name__ == "__main__": ocp = gen_long_ocp() AcadosOcpSolver.generate(ocp, json_file=JSON_FILE) - # AcadosOcpSolver.build(ocp.code_export_directory, with_cython=True) + # AcadosOcpSolver.build(ocp.code_export_directory, with_cython=True) \ No newline at end of file diff --git a/selfdrive/controls/lib/legacy_longitudinal_planner.py b/selfdrive/controls/lib/legacy_longitudinal_planner.py old mode 100755 new mode 100644 index bd0d0a1d0..24e83f775 --- a/selfdrive/controls/lib/legacy_longitudinal_planner.py +++ b/selfdrive/controls/lib/legacy_longitudinal_planner.py @@ -1,25 +1,17 @@ -''' -This is the longitudinal_planner from 0.8.16 - -reason I keep this as a separate file is that Nuclear Grade model released during 0.8.15 / 0.8.16. -So it could handle better with old planners. - -Note 1: This may not work in newer version. - -Note 2: **This planner does not support Experimental Mode** - -''' +#!/usr/bin/env python3 import math import numpy as np from common.numpy_fast import interp +from common.params import Params +from cereal import log import cereal.messaging as messaging -from common.conversions import Conversions as CV from common.filter_simple import FirstOrderFilter from common.realtime import DT_MDL from selfdrive.legacy_modeld.constants import T_IDXS +from common.conversions import Conversions as CV from selfdrive.controls.lib.longcontrol import LongCtrlState -from selfdrive.controls.lib.legacy_longitudinal_mpc_lib.long_mpc import LongitudinalMpc +from selfdrive.controls.lib.legacy_longitudinal_mpc_lib.long_mpc import LongitudinalMpc, STOP_DISTANCE from selfdrive.controls.lib.legacy_longitudinal_mpc_lib.long_mpc import T_IDXS as T_IDXS_MPC from selfdrive.controls.lib.drive_helpers import V_CRUISE_MAX, CONTROL_N from system.swaglog import cloudlog @@ -45,8 +37,6 @@ def limit_accel_in_turns(v_ego, angle_steers, a_target, CP): this should avoid accelerating when losing the target in turns """ - # FIXME: This function to calculate lateral accel is incorrect and should use the VehicleModel - # The lookup table for turns should also be updated if we do this a_total_max = interp(v_ego, _A_TOTAL_MAX_BP, _A_TOTAL_MAX_V) a_y = v_ego ** 2 * angle_steers * CV.DEG_TO_RAD / (CP.steerRatio * CP.wheelbase) a_x_allowed = math.sqrt(max(a_total_max ** 2 - a_y ** 2, 0.)) @@ -68,19 +58,34 @@ class LongitudinalPlanner: self.a_desired_trajectory = np.zeros(CONTROL_N) self.j_desired_trajectory = np.zeros(CONTROL_N) self.solverExecutionTime = 0.0 + self.params = Params() + self.param_read_counter = 0 + self.read_param() + self.personality = log.LongitudinalPersonality.standard + + def read_param(self): + param_value = self.params.get('LongitudinalPersonality') + if param_value is not None: + self.personality = int(param_value) + else: + self.personality = log.LongitudinalPersonality.standard def update(self, sm): + if self.param_read_counter % 50 == 0: + self.read_param() + self.param_read_counter += 1 v_ego = sm['carState'].vEgo v_cruise_kph = sm['controlsState'].vCruise v_cruise_kph = min(v_cruise_kph, V_CRUISE_MAX) v_cruise = v_cruise_kph * CV.KPH_TO_MS - long_control_off = sm['controlsState'].longControlState == LongCtrlState.off + long_control_state = sm['controlsState'].longControlState force_slow_decel = sm['controlsState'].forceDecel # Reset current state when not engaged, or user is controlling the speed - reset_state = long_control_off if self.CP.openpilotLongitudinalControl else not sm['controlsState'].enabled + reset_state = long_control_state == LongCtrlState.off + reset_state = reset_state or sm['carState'].gasPressed # No change cost when user is controlling the speed, or when standstill prev_accel_constraint = not (reset_state or sm['carState'].standstill) @@ -105,7 +110,8 @@ class LongitudinalPlanner: self.mpc.set_weights(prev_accel_constraint) self.mpc.set_accel_limits(accel_limits_turns[0], accel_limits_turns[1]) self.mpc.set_cur_state(self.v_desired_filter.x, self.a_desired) - self.mpc.update(sm['carState'], sm['radarState'], v_cruise) + stop_distance = STOP_DISTANCE if not self.CP.radarUnavailable else interp(sm['carState'].vEgo, [0., 2.78, 5.55, 22.], [3.7, 4., 5, STOP_DISTANCE]) + self.mpc.update(sm['carState'], sm['radarState'], v_cruise, personality=self.personality, stop_distance=stop_distance) self.v_desired_trajectory = np.interp(T_IDXS[:CONTROL_N], T_IDXS_MPC, self.mpc.v_solution) self.a_desired_trajectory = np.interp(T_IDXS[:CONTROL_N], T_IDXS_MPC, self.mpc.a_solution) @@ -139,5 +145,6 @@ class LongitudinalPlanner: longitudinalPlan.fcw = self.fcw longitudinalPlan.solverExecutionTime = self.mpc.solve_time + longitudinalPlan.personality = self.personality pm.send('longitudinalPlan', plan_send) diff --git a/selfdrive/controls/lib/longcontrol.py b/selfdrive/controls/lib/longcontrol.py index 7514efcce..c6a727a83 100644 --- a/selfdrive/controls/lib/longcontrol.py +++ b/selfdrive/controls/lib/longcontrol.py @@ -3,7 +3,7 @@ from common.numpy_fast import clip, interp from common.realtime import DT_CTRL from selfdrive.controls.lib.drive_helpers import CONTROL_N, apply_deadzone from selfdrive.controls.lib.pid import PIDController -from selfdrive.legacy_modeld.constants import T_IDXS +from selfdrive.hybrid_modeld.constants import T_IDXS LongCtrlState = car.CarControl.Actuators.LongControlState diff --git a/selfdrive/controls/lib/longitudinal_mpc_lib/acados_ocp_long.json b/selfdrive/controls/lib/longitudinal_mpc_lib/acados_ocp_long.json deleted file mode 100644 index e361e32cc..000000000 --- a/selfdrive/controls/lib/longitudinal_mpc_lib/acados_ocp_long.json +++ /dev/null @@ -1,628 +0,0 @@ -{ - "acados_include_path": "/data/openpilot/third_party/acados/include/acados/include", - "acados_lib_path": "/data/openpilot/third_party/acados/include/acados/lib", - "code_export_directory": "/data/openpilot/selfdrive/controls/lib/longitudinal_mpc_lib/c_generated_code", - "constraints": { - "C": [], - "C_e": [], - "D": [], - "constr_type": "BGH", - "constr_type_e": "BGH", - "idxbu": [], - "idxbx": [], - "idxbx_0": [ - 0, - 1, - 2 - ], - "idxbx_e": [], - "idxbxe_0": [ - 0, - 1, - 2 - ], - "idxsbu": [], - "idxsbx": [], - "idxsbx_e": [], - "idxsg": [], - "idxsg_e": [], - "idxsh": [ - 0, - 1, - 2, - 3 - ], - "idxsh_e": [], - "idxsphi": [], - "idxsphi_e": [], - "lbu": [], - "lbx": [], - "lbx_0": [ - 0.0, - 0.0, - 0.0 - ], - "lbx_e": [], - "lg": [], - "lg_e": [], - "lh": [ - 0.0, - 0.0, - 0.0, - 0.0 - ], - "lh_e": [], - "lphi": [], - "lphi_e": [], - "lsbu": [], - "lsbx": [], - "lsbx_e": [], - "lsg": [], - "lsg_e": [], - "lsh": [ - 0.0, - 0.0, - 0.0, - 0.0 - ], - "lsh_e": [], - "lsphi": [], - "lsphi_e": [], - "ubu": [], - "ubx": [], - "ubx_0": [ - 0.0, - 0.0, - 0.0 - ], - "ubx_e": [], - "ug": [], - "ug_e": [], - "uh": [ - 10000.0, - 10000.0, - 10000.0, - 10000.0 - ], - "uh_e": [], - "uphi": [], - "uphi_e": [], - "usbu": [], - "usbx": [], - "usbx_e": [], - "usg": [], - "usg_e": [], - "ush": [ - 0.0, - 0.0, - 0.0, - 0.0 - ], - "ush_e": [], - "usphi": [], - "usphi_e": [] - }, - "cost": { - "Vu": [ - [ - 0.0 - ], - [ - 0.0 - ], - [ - 0.0 - ], - [ - 0.0 - ], - [ - 0.0 - ], - [ - 0.0 - ] - ], - "Vu_0": [ - [ - 0.0 - ], - [ - 0.0 - ], - [ - 0.0 - ], - [ - 0.0 - ], - [ - 0.0 - ], - [ - 0.0 - ] - ], - "Vx": [ - [ - 0.0, - 0.0, - 0.0 - ], - [ - 0.0, - 0.0, - 0.0 - ], - [ - 0.0, - 0.0, - 0.0 - ], - [ - 0.0, - 0.0, - 0.0 - ], - [ - 0.0, - 0.0, - 0.0 - ], - [ - 0.0, - 0.0, - 0.0 - ] - ], - "Vx_0": [ - [ - 0.0, - 0.0, - 0.0 - ], - [ - 0.0, - 0.0, - 0.0 - ], - [ - 0.0, - 0.0, - 0.0 - ], - [ - 0.0, - 0.0, - 0.0 - ], - [ - 0.0, - 0.0, - 0.0 - ], - [ - 0.0, - 0.0, - 0.0 - ] - ], - "Vx_e": [ - [ - 0.0, - 0.0, - 0.0 - ], - [ - 0.0, - 0.0, - 0.0 - ], - [ - 0.0, - 0.0, - 0.0 - ], - [ - 0.0, - 0.0, - 0.0 - ], - [ - 0.0, - 0.0, - 0.0 - ] - ], - "Vz": [], - "Vz_0": [], - "W": [ - [ - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0 - ], - [ - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0 - ], - [ - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0 - ], - [ - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0 - ], - [ - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0 - ], - [ - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0 - ] - ], - "W_0": [ - [ - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0 - ], - [ - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0 - ], - [ - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0 - ], - [ - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0 - ], - [ - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0 - ], - [ - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0 - ] - ], - "W_e": [ - [ - 0.0, - 0.0, - 0.0, - 0.0, - 0.0 - ], - [ - 0.0, - 0.0, - 0.0, - 0.0, - 0.0 - ], - [ - 0.0, - 0.0, - 0.0, - 0.0, - 0.0 - ], - [ - 0.0, - 0.0, - 0.0, - 0.0, - 0.0 - ], - [ - 0.0, - 0.0, - 0.0, - 0.0, - 0.0 - ] - ], - "Zl": [ - 0.0, - 0.0, - 0.0, - 0.0 - ], - "Zl_e": [], - "Zu": [ - 0.0, - 0.0, - 0.0, - 0.0 - ], - "Zu_e": [], - "cost_ext_fun_type": "casadi", - "cost_ext_fun_type_0": "casadi", - "cost_ext_fun_type_e": "casadi", - "cost_type": "NONLINEAR_LS", - "cost_type_0": "NONLINEAR_LS", - "cost_type_e": "NONLINEAR_LS", - "yref": [ - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0 - ], - "yref_0": [ - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0 - ], - "yref_e": [ - 0.0, - 0.0, - 0.0, - 0.0, - 0.0 - ], - "zl": [ - 0.0, - 0.0, - 0.0, - 0.0 - ], - "zl_e": [], - "zu": [ - 0.0, - 0.0, - 0.0, - 0.0 - ], - "zu_e": [] - }, - "cython_include_dirs": "/data/data/com.termux/files/usr/lib/python3.8/site-packages/numpy/core/include", - "dims": { - "N": 12, - "nbu": 0, - "nbx": 0, - "nbx_0": 3, - "nbx_e": 0, - "nbxe_0": 3, - "ng": 0, - "ng_e": 0, - "nh": 4, - "nh_e": 0, - "np": 7, - "nphi": 0, - "nphi_e": 0, - "nr": 0, - "nr_e": 0, - "ns": 4, - "ns_e": 0, - "nsbu": 0, - "nsbx": 0, - "nsbx_e": 0, - "nsg": 0, - "nsg_e": 0, - "nsh": 4, - "nsh_e": 0, - "nsphi": 0, - "nsphi_e": 0, - "nu": 1, - "nx": 3, - "ny": 6, - "ny_0": 6, - "ny_e": 5, - "nz": 0 - }, - "model": { - "dyn_disc_fun": null, - "dyn_disc_fun_jac": null, - "dyn_disc_fun_jac_hess": null, - "dyn_ext_fun_type": "casadi", - "dyn_source_discrete": null, - "gnsf": { - "nontrivial_f_LO": 1, - "purely_linear": 0 - }, - "name": "long" - }, - "parameter_values": [ - -1.2, - 1.2, - 0.0, - 0.0, - 1.45, - 0.75, - 5.5 - ], - "problem_class": "OCP", - "simulink_opts": { - "inputs": { - "cost_W": 0, - "cost_W_0": 0, - "cost_W_e": 0, - "lbu": 1, - "lbx": 1, - "lbx_0": 1, - "lbx_e": 1, - "lg": 1, - "lh": 1, - "parameter_traj": 1, - "reset_solver": 0, - "u_init": 0, - "ubu": 1, - "ubx": 1, - "ubx_0": 1, - "ubx_e": 1, - "ug": 1, - "uh": 1, - "x_init": 0, - "y_ref": 1, - "y_ref_0": 1, - "y_ref_e": 1 - }, - "outputs": { - "CPU_time": 1, - "CPU_time_lin": 0, - "CPU_time_qp": 0, - "CPU_time_sim": 0, - "KKT_residual": 1, - "solver_status": 1, - "sqp_iter": 1, - "u0": 1, - "utraj": 0, - "x1": 1, - "xtraj": 0 - }, - "samplingtime": "t0" - }, - "solver_options": { - "Tsim": 0.06944444444444445, - "alpha_min": 0.05, - "alpha_reduction": 0.7, - "collocation_type": "GAUSS_LEGENDRE", - "eps_sufficient_descent": 0.0001, - "exact_hess_constr": 1, - "exact_hess_cost": 1, - "exact_hess_dyn": 1, - "ext_cost_num_hess": 0, - "full_step_dual": 0, - "globalization": "FIXED_STEP", - "globalization_use_SOC": 0, - "hessian_approx": "GAUSS_NEWTON", - "hpipm_mode": "BALANCE", - "initialize_t_slacks": 0, - "integrator_type": "ERK", - "levenberg_marquardt": 0.0, - "line_search_use_sufficient_descent": 0, - "model_external_shared_lib_dir": null, - "model_external_shared_lib_name": null, - "nlp_solver_max_iter": 100, - "nlp_solver_step_length": 1.0, - "nlp_solver_tol_comp": 1e-06, - "nlp_solver_tol_eq": 1e-06, - "nlp_solver_tol_ineq": 1e-06, - "nlp_solver_tol_stat": 1e-06, - "nlp_solver_type": "SQP_RTI", - "print_level": 0, - "qp_solver": "PARTIAL_CONDENSING_HPIPM", - "qp_solver_cond_N": 1, - "qp_solver_iter_max": 10, - "qp_solver_tol_comp": 0.001, - "qp_solver_tol_eq": 0.001, - "qp_solver_tol_ineq": 0.001, - "qp_solver_tol_stat": 0.001, - "qp_solver_warm_start": 0, - "regularize_method": null, - "sim_method_jac_reuse": [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0 - ], - "sim_method_newton_iter": 3, - "sim_method_num_stages": [ - 4, - 4, - 4, - 4, - 4, - 4, - 4, - 4, - 4, - 4, - 4, - 4 - ], - "sim_method_num_steps": [ - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1 - ], - "tf": 10.0, - "time_steps": [ - 0.06944444444444445, - 0.20833333333333334, - 0.3472222222222222, - 0.48611111111111116, - 0.6250000000000002, - 0.7638888888888886, - 0.9027777777777786, - 1.041666666666666, - 1.1805555555555554, - 1.3194444444444455, - 1.4583333333333313, - 1.5972222222222232 - ] - } -} \ No newline at end of file diff --git a/selfdrive/controls/lib/longitudinal_mpc_lib/c_generated_code/acados_sim_solver_long.h b/selfdrive/controls/lib/longitudinal_mpc_lib/c_generated_code/acados_sim_solver_long.h deleted file mode 100644 index c2f7a3771..000000000 --- a/selfdrive/controls/lib/longitudinal_mpc_lib/c_generated_code/acados_sim_solver_long.h +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright 2019 Gianluca Frison, Dimitris Kouzoupis, Robin Verschueren, - * Andrea Zanelli, Niels van Duijkeren, Jonathan Frey, Tommaso Sartor, - * Branimir Novoselnik, Rien Quirynen, Rezart Qelibari, Dang Doan, - * Jonas Koenemann, Yutao Chen, Tobias Schöls, Jonas Schlagenhauf, Moritz Diehl - * - * This file is part of acados. - * - * The 2-Clause BSD License - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE.; - */ - -#ifndef ACADOS_SIM_long_H_ -#define ACADOS_SIM_long_H_ - -#include "acados_c/sim_interface.h" -#include "acados_c/external_function_interface.h" - -#define LONG_NX 3 -#define LONG_NZ 0 -#define LONG_NU 1 -#define LONG_NP 7 - -#ifdef __cplusplus -extern "C" { -#endif - - -// ** capsule for solver data ** -typedef struct sim_solver_capsule -{ - // acados objects - sim_in *acados_sim_in; - sim_out *acados_sim_out; - sim_solver *acados_sim_solver; - sim_opts *acados_sim_opts; - sim_config *acados_sim_config; - void *acados_sim_dims; - - /* external functions */ - // ERK - external_function_param_casadi * sim_forw_vde_casadi; - external_function_param_casadi * sim_expl_ode_fun_casadi; - external_function_param_casadi * sim_expl_ode_hess; - - // IRK - external_function_param_casadi * sim_impl_dae_fun; - external_function_param_casadi * sim_impl_dae_fun_jac_x_xdot_z; - external_function_param_casadi * sim_impl_dae_jac_x_xdot_u_z; - external_function_param_casadi * sim_impl_dae_hess; - - // GNSF - external_function_param_casadi * sim_gnsf_phi_fun; - external_function_param_casadi * sim_gnsf_phi_fun_jac_y; - external_function_param_casadi * sim_gnsf_phi_jac_y_uhat; - external_function_param_casadi * sim_gnsf_f_lo_jac_x1_x1dot_u_z; - external_function_param_casadi * sim_gnsf_get_matrices_fun; - -} sim_solver_capsule; - - -ACADOS_SYMBOL_EXPORT int long_acados_sim_create(sim_solver_capsule *capsule); -ACADOS_SYMBOL_EXPORT int long_acados_sim_solve(sim_solver_capsule *capsule); -ACADOS_SYMBOL_EXPORT int long_acados_sim_free(sim_solver_capsule *capsule); -ACADOS_SYMBOL_EXPORT int long_acados_sim_update_params(sim_solver_capsule *capsule, double *value, int np); - -ACADOS_SYMBOL_EXPORT sim_config * long_acados_get_sim_config(sim_solver_capsule *capsule); -ACADOS_SYMBOL_EXPORT sim_in * long_acados_get_sim_in(sim_solver_capsule *capsule); -ACADOS_SYMBOL_EXPORT sim_out * long_acados_get_sim_out(sim_solver_capsule *capsule); -ACADOS_SYMBOL_EXPORT void * long_acados_get_sim_dims(sim_solver_capsule *capsule); -ACADOS_SYMBOL_EXPORT sim_opts * long_acados_get_sim_opts(sim_solver_capsule *capsule); -ACADOS_SYMBOL_EXPORT sim_solver * long_acados_get_sim_solver(sim_solver_capsule *capsule); - - -ACADOS_SYMBOL_EXPORT sim_solver_capsule * long_acados_sim_solver_create_capsule(void); -ACADOS_SYMBOL_EXPORT int long_acados_sim_solver_free_capsule(sim_solver_capsule *capsule); - -#ifdef __cplusplus -} -#endif - -#endif // ACADOS_SIM_long_H_ diff --git a/selfdrive/controls/lib/longitudinal_mpc_lib/c_generated_code/libacados_ocp_solver_long.so b/selfdrive/controls/lib/longitudinal_mpc_lib/c_generated_code/libacados_ocp_solver_long.so index 70228d48a..b510235a3 100755 Binary files a/selfdrive/controls/lib/longitudinal_mpc_lib/c_generated_code/libacados_ocp_solver_long.so and b/selfdrive/controls/lib/longitudinal_mpc_lib/c_generated_code/libacados_ocp_solver_long.so differ diff --git a/selfdrive/controls/lib/longitudinal_mpc_lib/c_generated_code/make_sfun_long.m b/selfdrive/controls/lib/longitudinal_mpc_lib/c_generated_code/make_sfun_long.m deleted file mode 100644 index 5675b71b7..000000000 --- a/selfdrive/controls/lib/longitudinal_mpc_lib/c_generated_code/make_sfun_long.m +++ /dev/null @@ -1,128 +0,0 @@ -% -% Copyright 2019 Gianluca Frison, Dimitris Kouzoupis, Robin Verschueren, -% Andrea Zanelli, Niels van Duijkeren, Jonathan Frey, Tommaso Sartor, -% Branimir Novoselnik, Rien Quirynen, Rezart Qelibari, Dang Doan, -% Jonas Koenemann, Yutao Chen, Tobias Schöls, Jonas Schlagenhauf, Moritz Diehl -% -% This file is part of acados. -% -% The 2-Clause BSD License -% -% Redistribution and use in source and binary forms, with or without -% modification, are permitted provided that the following conditions are met: -% -% 1. Redistributions of source code must retain the above copyright notice, -% this list of conditions and the following disclaimer. -% -% 2. Redistributions in binary form must reproduce the above copyright notice, -% this list of conditions and the following disclaimer in the documentation -% and/or other materials provided with the distribution. -% -% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -% ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -% LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -% CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -% SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -% INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -% CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -% ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -% POSSIBILITY OF SUCH DAMAGE.; -% - -SOURCES = { ... - 'long_model/long_expl_ode_fun.c', ... - 'long_model/long_expl_vde_forw.c',... - 'long_cost/long_cost_y_0_fun.c',... - 'long_cost/long_cost_y_0_fun_jac_ut_xt.c',... - 'long_cost/long_cost_y_0_hess.c',... - 'long_cost/long_cost_y_fun.c',... - 'long_cost/long_cost_y_fun_jac_ut_xt.c',... - 'long_cost/long_cost_y_hess.c',... - 'long_cost/long_cost_y_e_fun.c',... - 'long_cost/long_cost_y_e_fun_jac_ut_xt.c',... - 'long_cost/long_cost_y_e_hess.c',... - 'long_constraints/long_constr_h_fun.c', ... - 'long_constraints/long_constr_h_fun_jac_uxt_zt_hess.c', ... - 'long_constraints/long_constr_h_fun_jac_uxt_zt.c', ... - 'acados_solver_sfunction_long.c', ... - 'acados_solver_long.c' - }; - -INC_PATH = '/data/openpilot/third_party/acados/include/acados/include'; - -INCS = {['-I', fullfile(INC_PATH, 'blasfeo', 'include')], ... - ['-I', fullfile(INC_PATH, 'hpipm', 'include')], ... - ['-I', fullfile(INC_PATH, 'acados')], ... - ['-I', fullfile(INC_PATH)]}; - - - -CFLAGS = 'CFLAGS=$CFLAGS'; -LDFLAGS = 'LDFLAGS=$LDFLAGS'; -COMPFLAGS = 'COMPFLAGS=$COMPFLAGS'; -COMPDEFINES = 'COMPDEFINES=$COMPDEFINES'; - - - -LIB_PATH = ['-L', fullfile('/data/openpilot/third_party/acados/include/acados/lib')]; - -LIBS = {'-lacados', '-lhpipm', '-lblasfeo'}; - -% acados linking libraries and flags - - -mex('-v', '-O', CFLAGS, LDFLAGS, COMPFLAGS, COMPDEFINES, INCS{:}, ... - LIB_PATH, LIBS{:}, SOURCES{:}, ... - '-output', 'acados_solver_sfunction_long' ); - -fprintf( [ '\n\nSuccessfully created sfunction:\nacados_solver_sfunction_long', '.', ... - eval('mexext')] ); - - -%% print note on usage of s-function -fprintf('\n\nNote: Usage of Sfunction is as follows:\n') -input_note = 'Inputs are:\n'; -i_in = 1; -input_note = strcat(input_note, num2str(i_in), ') lbx_0 - lower bound on x for stage 0,',... - ' size [3]\n '); -i_in = i_in + 1; -input_note = strcat(input_note, num2str(i_in), ') ubx_0 - upper bound on x for stage 0,',... - ' size [3]\n '); -i_in = i_in + 1; -input_note = strcat(input_note, num2str(i_in), ') parameters - concatenated for all shooting nodes 0 to N+1,',... - ' size [91]\n '); -i_in = i_in + 1; -input_note = strcat(input_note, num2str(i_in), ') y_ref_0, size [6]\n '); -i_in = i_in + 1; -input_note = strcat(input_note, num2str(i_in), ') y_ref - concatenated for shooting nodes 1 to N-1,',... - ' size [66]\n '); -i_in = i_in + 1; -input_note = strcat(input_note, num2str(i_in), ') y_ref_e, size [5]\n '); -i_in = i_in + 1; -input_note = strcat(input_note, num2str(i_in), ') lh, size [4]\n '); -i_in = i_in + 1; -input_note = strcat(input_note, num2str(i_in), ') uh, size [4]\n '); -i_in = i_in + 1; - -fprintf(input_note) - -disp(' ') - -output_note = 'Outputs are:\n'; -i_out = 0; -i_out = i_out + 1; -output_note = strcat(output_note, num2str(i_out), ') u0, control input at node 0, size [1]\n '); -i_out = i_out + 1; -output_note = strcat(output_note, num2str(i_out), ') acados solver status (0 = SUCCESS)\n '); -i_out = i_out + 1; -output_note = strcat(output_note, num2str(i_out), ') KKT residual\n '); -i_out = i_out + 1; -output_note = strcat(output_note, num2str(i_out), ') x1, state at node 1\n '); -i_out = i_out + 1; -output_note = strcat(output_note, num2str(i_out), ') CPU time\n '); -i_out = i_out + 1; -output_note = strcat(output_note, num2str(i_out), ') SQP iterations\n '); - -fprintf(output_note) diff --git a/selfdrive/controls/lib/longitudinal_mpc_lib/long_mpc.py b/selfdrive/controls/lib/longitudinal_mpc_lib/long_mpc.py index 7c6cb71c4..7cac8f534 100644 --- a/selfdrive/controls/lib/longitudinal_mpc_lib/long_mpc.py +++ b/selfdrive/controls/lib/longitudinal_mpc_lib/long_mpc.py @@ -1,12 +1,12 @@ #!/usr/bin/env python3 import os import numpy as np - +from cereal import log from common.realtime import sec_since_boot from common.numpy_fast import clip from system.swaglog import cloudlog # WARNING: imports outside of constants will not trigger a rebuild -from selfdrive.legacy_modeld.constants import index_function +from selfdrive.hybrid_modeld.constants import index_function from selfdrive.controls.lib.radar_helpers import _LEAD_ACCEL_TAU if __name__ == '__main__': # generating code @@ -54,17 +54,37 @@ FCW_IDXS = T_IDXS < 5.0 T_DIFFS = np.diff(T_IDXS, prepend=[0.]) MIN_ACCEL = -3.5 MAX_ACCEL = 2.0 -T_FOLLOW = 1.45 COMFORT_BRAKE = 2.5 -STOP_DISTANCE = 5.5 +STOP_DISTANCE = 6.0 + +def get_jerk_factor(personality=log.LongitudinalPersonality.standard): + if personality==log.LongitudinalPersonality.relaxed: + return 1.0 + elif personality==log.LongitudinalPersonality.standard: + return 1.0 + elif personality==log.LongitudinalPersonality.aggressive: + return 0.5 + else: + raise NotImplementedError("Longitudinal personality not supported") + + +def get_T_FOLLOW(personality=log.LongitudinalPersonality.standard): + if personality==log.LongitudinalPersonality.relaxed: + return 1.75 + elif personality==log.LongitudinalPersonality.standard: + return 1.45 + elif personality==log.LongitudinalPersonality.aggressive: + return 1.25 + else: + raise NotImplementedError("Longitudinal personality not supported") def get_stopped_equivalence_factor(v_lead): return (v_lead**2) / (2 * COMFORT_BRAKE) -def get_safe_obstacle_distance(v_ego, t_follow=T_FOLLOW, stop_distance=STOP_DISTANCE): +def get_safe_obstacle_distance(v_ego, t_follow, stop_distance): return (v_ego**2) / (2 * COMFORT_BRAKE) + t_follow * v_ego + stop_distance -def desired_follow_distance(v_ego, v_lead, t_follow=T_FOLLOW, stop_distance=STOP_DISTANCE): +def desired_follow_distance(v_ego, v_lead, t_follow=get_T_FOLLOW(), stop_distance=STOP_DISTANCE): return get_safe_obstacle_distance(v_ego, t_follow, stop_distance) - get_stopped_equivalence_factor(v_lead) @@ -163,7 +183,7 @@ def gen_long_ocp(): x0 = np.zeros(X_DIM) ocp.constraints.x0 = x0 - ocp.parameter_values = np.array([-1.2, 1.2, 0.0, 0.0, T_FOLLOW, LEAD_DANGER_FACTOR, STOP_DISTANCE]) + ocp.parameter_values = np.array([-1.2, 1.2, 0.0, 0.0, get_T_FOLLOW(), LEAD_DANGER_FACTOR, STOP_DISTANCE]) # We put all constraint cost weights to 0 and only set them at runtime cost_weights = np.zeros(CONSTR_DIM) @@ -203,10 +223,9 @@ class LongitudinalMpc: def __init__(self, mode='acc'): self.mode = mode self.solver = AcadosOcpSolverCython(MODEL_NAME, ACADOS_SOLVER_TYPE, N) - self.t_follow = T_FOLLOW - self.stop_distance = STOP_DISTANCE self.reset() self.source = SOURCES[2] + self.t_follow = get_T_FOLLOW() def reset(self): # self.solver = AcadosOcpSolverCython(MODEL_NAME, ACADOS_SOLVER_TYPE, N) @@ -253,10 +272,11 @@ class LongitudinalMpc: for i in range(N): self.solver.cost_set(i, 'Zl', Zl) - def set_weights(self, prev_accel_constraint=True): + def set_weights(self, prev_accel_constraint=True, personality=log.LongitudinalPersonality.standard): + jerk_factor = get_jerk_factor(personality) if self.mode == 'acc': a_change_cost = A_CHANGE_COST if prev_accel_constraint else 0 - cost_weights = [X_EGO_OBSTACLE_COST, X_EGO_COST, V_EGO_COST, A_EGO_COST, a_change_cost, J_EGO_COST] + cost_weights = [X_EGO_OBSTACLE_COST, X_EGO_COST, V_EGO_COST, A_EGO_COST, jerk_factor * a_change_cost, jerk_factor * J_EGO_COST] constraint_cost_weights = [LIMIT_COST, LIMIT_COST, LIMIT_COST, DANGER_ZONE_COST] elif self.mode == 'blended': a_change_cost = 40.0 if prev_accel_constraint else 0 @@ -311,15 +331,14 @@ class LongitudinalMpc: self.cruise_min_a = min_a self.max_a = max_a - def update(self, radarstate, v_cruise, x, v, a, j, prev_accel_constraint, t_follow=T_FOLLOW, stop_distance=STOP_DISTANCE): + def update(self, radarstate, v_cruise, x, v, a, j, personality=log.LongitudinalPersonality.standard, stop_distance=STOP_DISTANCE): + self.t_follow = get_T_FOLLOW(personality) v_ego = self.x0[1] self.status = radarstate.leadOne.status or radarstate.leadTwo.status lead_xv_0 = self.process_lead(radarstate.leadOne) lead_xv_1 = self.process_lead(radarstate.leadTwo) - self.t_follow = t_follow - self.stop_distance = stop_distance # To estimate a safe distance from a moving lead, we calculate how much stopping # distance that lead needs as a minimum. We can add that to the current distance # and then treat that as a stopped car/obstacle at this new distance. @@ -340,7 +359,7 @@ class LongitudinalMpc: v_cruise_clipped = np.clip(v_cruise * np.ones(N+1), v_lower, v_upper) - cruise_obstacle = np.cumsum(T_DIFFS * v_cruise_clipped) + get_safe_obstacle_distance(v_cruise_clipped, self.t_follow, self.stop_distance) + cruise_obstacle = np.cumsum(T_DIFFS * v_cruise_clipped) + get_safe_obstacle_distance(v_cruise_clipped, self.t_follow, stop_distance) x_obstacles = np.column_stack([lead_0_obstacle, lead_1_obstacle, cruise_obstacle]) self.source = SOURCES[np.argmin(x_obstacles[0])] @@ -375,7 +394,7 @@ class LongitudinalMpc: self.params[:,2] = np.min(x_obstacles, axis=1) self.params[:,3] = np.copy(self.prev_a) self.params[:,4] = self.t_follow - self.params[:,6] = self.stop_distance + self.params[:,6] = stop_distance self.run() if (np.any(lead_xv_0[FCW_IDXS,0] - self.x_sol[FCW_IDXS,0] < CRASH_DISTANCE) and @@ -387,9 +406,9 @@ class LongitudinalMpc: # Check if it got within lead comfort range # TODO This should be done cleaner if self.mode == 'blended': - if any((lead_0_obstacle - get_safe_obstacle_distance(self.x_sol[:,1], self.t_follow, self.stop_distance))- self.x_sol[:,0] < 0.0): + if any((lead_0_obstacle - get_safe_obstacle_distance(self.x_sol[:,1], self.t_follow, stop_distance))- self.x_sol[:,0] < 0.0): self.source = 'lead0' - if any((lead_1_obstacle - get_safe_obstacle_distance(self.x_sol[:,1], self.t_follow, self.stop_distance))- self.x_sol[:,0] < 0.0) and \ + if any((lead_1_obstacle - get_safe_obstacle_distance(self.x_sol[:,1], self.t_follow, stop_distance))- self.x_sol[:,0] < 0.0) and \ (lead_1_obstacle[0] - lead_0_obstacle[0]): self.source = 'lead1' diff --git a/selfdrive/controls/lib/longitudinal_planner.py b/selfdrive/controls/lib/longitudinal_planner.py index 46de878af..6365b4fa1 100755 --- a/selfdrive/controls/lib/longitudinal_planner.py +++ b/selfdrive/controls/lib/longitudinal_planner.py @@ -2,14 +2,16 @@ import math import numpy as np from common.numpy_fast import clip, interp +from common.params import Params +from cereal import log import cereal.messaging as messaging from common.conversions import Conversions as CV from common.filter_simple import FirstOrderFilter from common.realtime import DT_MDL -from selfdrive.legacy_modeld.constants import T_IDXS +from selfdrive.hybrid_modeld.constants import T_IDXS from selfdrive.controls.lib.longcontrol import LongCtrlState -from selfdrive.controls.lib.longitudinal_mpc_lib.long_mpc import LongitudinalMpc, MIN_ACCEL, MAX_ACCEL, T_FOLLOW, STOP_DISTANCE +from selfdrive.controls.lib.longitudinal_mpc_lib.long_mpc import LongitudinalMpc, MIN_ACCEL, MAX_ACCEL, STOP_DISTANCE from selfdrive.controls.lib.longitudinal_mpc_lib.long_mpc import T_IDXS as T_IDXS_MPC from selfdrive.controls.lib.drive_helpers import V_CRUISE_MAX, CONTROL_N, get_speed_error from system.swaglog import cloudlog @@ -70,7 +72,6 @@ class LongitudinalPlanner: self.dp_e2e_standstill_last = False self.dp_e2e_swap_count = 0 self.dp_e2e_stop_count = 0 - self.dp_e2e_tf = T_FOLLOW self.dp_e2e_tf_count = 0 self.CP = CP @@ -85,6 +86,17 @@ class LongitudinalPlanner: self.a_desired_trajectory = np.zeros(CONTROL_N) self.j_desired_trajectory = np.zeros(CONTROL_N) self.solverExecutionTime = 0.0 + self.params = Params() + self.param_read_counter = 0 + self.read_param() + self.personality = log.LongitudinalPersonality.standard + + def read_param(self): + param_value = self.params.get('LongitudinalPersonality') + if param_value is not None: + self.personality = int(param_value) + else: + self.personality = log.LongitudinalPersonality.standard def _set_dp_e2e_mode(self, mode, force=False): reset_state = False @@ -149,7 +161,7 @@ class LongitudinalPlanner: # if sm['dragonConf'].dpE2EConditionalVoacc and self.dp_e2e_has_lead: if self.CP.radarUnavailable and self.dp_e2e_has_lead: # drive above conditional speed and lead is too close - if lead_dist <= v_ego_kph * self.dp_e2e_tf * interp(v_ego_kph, [50., 60., 80., 85, 90.], [1.25, 1.20, 1.10, 1.05, 1.]) / 3.6: + if lead_dist <= v_ego_kph * self.mpc.t_follow * interp(v_ego_kph, [50., 60., 80., 85, 90.], [1.25, 1.20, 1.10, 1.05, 1.]) / 3.6: self.dp_e2e_tf_count += 1 else: self.dp_e2e_tf_count = 0 @@ -188,6 +200,10 @@ class LongitudinalPlanner: return x, v, a, j def update(self, sm): + if self.param_read_counter % 50 == 0: + self.read_param() + self.param_read_counter += 1 + # self.mpc.mode = 'blended' if sm['controlsState'].experimentalMode else 'acc' dp_reset_state = self.conditional_e2e(sm) v_ego = sm['carState'].vEgo @@ -235,13 +251,13 @@ class LongitudinalPlanner: accel_limits_turns[0] = min(accel_limits_turns[0], self.a_desired + 0.05) accel_limits_turns[1] = max(accel_limits_turns[1], self.a_desired - 0.05) - self.mpc.set_weights(prev_accel_constraint) + self.mpc.set_weights(prev_accel_constraint, personality=self.personality) self.mpc.set_accel_limits(accel_limits_turns[0], accel_limits_turns[1]) self.mpc.set_cur_state(self.v_desired_filter.x, self.a_desired) x, v, a, j = self.parse_model(sm['modelV2'], self.v_model_error) # dynamic stopping distance ONLY when on radarUnavailable vehicles (e.g. Toyota C-HR, VW) stop_distance = STOP_DISTANCE if not self.CP.radarUnavailable else interp(sm['carState'].vEgo, [0., 2.78, 5.55, 22.], [3.7, 4., 5, STOP_DISTANCE]) - self.mpc.update(sm['radarState'], v_cruise, x, v, a, j, prev_accel_constraint, T_FOLLOW, stop_distance) + self.mpc.update(sm['radarState'], v_cruise, x, v, a, j, personality=self.personality, stop_distance=stop_distance) self.v_desired_trajectory_full = np.interp(T_IDXS, T_IDXS_MPC, self.mpc.v_solution) self.a_desired_trajectory_full = np.interp(T_IDXS, T_IDXS_MPC, self.mpc.a_solution) @@ -279,5 +295,6 @@ class LongitudinalPlanner: longitudinalPlan.longitudinalValid = self.mpc.mode == 'acc' longitudinalPlan.solverExecutionTime = self.mpc.solve_time + longitudinalPlan.personality = self.personality pm.send('longitudinalPlan', plan_send) diff --git a/selfdrive/controls/lib/radar_helpers.py b/selfdrive/controls/lib/radar_helpers.py index 6da66b431..4184340dc 100644 --- a/selfdrive/controls/lib/radar_helpers.py +++ b/selfdrive/controls/lib/radar_helpers.py @@ -1,4 +1,4 @@ -from common.numpy_fast import mean, clip +from common.numpy_fast import mean from common.kalman.simple_kalman import KF1D @@ -131,15 +131,14 @@ class Cluster(): } def get_RadarState_from_vision(self, lead_msg, v_ego, model_v_ego): - v_ego_error_factor = clip(v_ego / (model_v_ego + 1e-3), 0.7, 1.3) - lead_v_pred = lead_msg.v[0] * v_ego_error_factor + lead_v_rel_pred = lead_msg.v[0] - model_v_ego return { "dRel": float(lead_msg.x[0] - RADAR_TO_CAMERA), "yRel": float(-lead_msg.y[0]), - "vRel": float(lead_v_pred - v_ego), - "vLead": float(lead_v_pred), - "vLeadK": float(lead_v_pred), - "aLeadK": float(lead_msg.a[0]), + "vRel": float(lead_v_rel_pred), + "vLead": float(v_ego + lead_v_rel_pred), + "vLeadK": float(v_ego + lead_v_rel_pred), + "aLeadK": 0.0, "aLeadTau": 0.3, "fcw": False, "modelProb": float(lead_msg.prob), diff --git a/selfdrive/controls/plannerd.py b/selfdrive/controls/plannerd.py index f85ec85ba..789527e37 100755 --- a/selfdrive/controls/plannerd.py +++ b/selfdrive/controls/plannerd.py @@ -4,15 +4,17 @@ from cereal import car from common.params import Params from common.realtime import Priority, config_realtime_process from system.swaglog import cloudlog -from selfdrive.legacy_modeld.constants import T_IDXS -from selfdrive.controls.lib.longitudinal_planner import LongitudinalPlanner -from selfdrive.controls.lib.lateral_planner import LateralPlanner -# rick - old planners from 0.8.16 -# from selfdrive.controls.lib.legacy_longitudinal_planner import LongitudinalPlanner -# from selfdrive.controls.lib.legacy_lateral_planner import LateralPlanner +from selfdrive.hybrid_modeld.constants import T_IDXS import cereal.messaging as messaging from system.hardware import TICI +if Params().get_bool("dp_0813"): + from selfdrive.controls.lib.legacy_longitudinal_planner import LongitudinalPlanner + from selfdrive.controls.lib.legacy_lateral_planner import LateralPlanner +else: + from selfdrive.controls.lib.longitudinal_planner import LongitudinalPlanner + from selfdrive.controls.lib.lateral_planner import LateralPlanner + def cumtrapz(x, t): return np.concatenate([[0], np.cumsum(((x[0:-1] + x[1:])/2) * np.diff(t))]) diff --git a/selfdrive/dragonpilot/mapd.py b/selfdrive/dragonpilot/mapd.py deleted file mode 100644 index d88ff637b..000000000 --- a/selfdrive/dragonpilot/mapd.py +++ /dev/null @@ -1,231 +0,0 @@ -#!/usr/bin/env python3 -#pylint: skip-file -# The MIT License -# -# Copyright (c) 2019-, Rick Lan, dragonpilot community, and a number of other of contributors. -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. - -import cereal.messaging as messaging -from common.realtime import Ratekeeper, set_core_affinity, set_realtime_priority, sec_since_boot -from common.params import Params -import json -from cereal import log -import overpy -import os -import subprocess - -OSM_QUERY = ["/data/media/0/osm/bin/osm3s_query", "--db-dir=/data/media/0/osm/db/"] - -OSM_ONLINE_QUERY_THRESHOLD = 5 # secs -OSM_LOCAL_QUERY_THRESHOLD = 3 # times - -class OSM(): - def __init__(self, last_gps_pos): - self._api = overpy.Overpass() - - self._local_osm_query_fail_count = 0 - self._last_gps_pos = last_gps_pos - self._fetch_time_prev = 0. - self._way_tags = [] - - self.way_id = 0 - self.road_name = None - self.speed_limit = 0 - - if os.path.isdir("/data/media/0/osm/bin/") and os.path.isdir("/data/media/0/osm/db/"): - self.local_osm_enabled = True - print("Local OSM installed") - else: - self.local_osm_enabled = False - print("Local OSM enabled = %s" % self.local_osm_enabled) - - def process_res(self, res): - if len(res) > 0: - self._way_tags = res[0].tags - self.way_id = res[0].id - - - def fetch_tags_around_location(self, latitude, longitude): - if len(self._way_tags) > 0 and len(self._last_gps_pos) > 0 and self._last_gps_pos["latitude"] == latitude and self._last_gps_pos["longitude"] == longitude: - return self._way_tags - q = f""" - way(around:10, {latitude}, {longitude}) - [highway] - [highway~"^(motorway|trunk|primary|secondary|tertiary|unclassified|residential)$"]; - ( - way._["name"]; - way._["maxspeed"]; - (._;>;); - ); - out tags; - """ - if self.local_osm_enabled: - try: - completion = subprocess.run(OSM_QUERY + [f"--request={q}"], check=True, capture_output=True) - res = self._api.parse_xml(completion.stdout).ways - self.process_res(res) - except Exception as e: - self._local_osm_query_fail_count += 1 - pass - - # use remote OSM when local osm is not enabled or failed too many times - if not self.local_osm_enabled or self._local_osm_query_fail_count >= OSM_LOCAL_QUERY_THRESHOLD: - fetch_time = sec_since_boot() - if fetch_time - self._fetch_time_prev < OSM_ONLINE_QUERY_THRESHOLD: - return - try: - res = self._api.query(q).ways - self.process_res(res) - self._local_osm_query_fail_count = 0 - except Exception as e: - print(f'Exception while querying OSM:\n{e}') - self._fetch_time_prev = fetch_time - - self._process_tags() - - def _process_tags(self): - if len(self._way_tags) > 0: - for tag in self._way_tags: - if tag.startswith("name"): - self.road_name = self._way_tags[tag] - elif tag.startswith("maxspeed"): - self.speed_limit = self._way_tags[tag] - -class MapD(): - def __init__(self, position_service): - last_gps_params = Params().get('LastGPSPosition') - self.last_gps_pos = json.loads(last_gps_params) if last_gps_params is not None else [] - self.osm = OSM(self.last_gps_pos) - - self.position_service = position_service - self._pause_query = False - self._longitude = None - self._latitude = None - - self._way_id_prev = 0 - self._road_name_prev = None - self._same_road_count = 0 - - def apply_last_gps_pos(self): - # use last gps position before first fixed - if self.last_gps_pos is not None and len(self.last_gps_pos) > 0: - self._latitude = self.last_gps_pos["latitude"] - self._longitude = self.last_gps_pos["longitude"] - # print("MAPD: Use GPS pos: %s, %s (LastGPSPosition)" % (self._latitude, self._longitude)) - - def update_car_state(self, sm): - sock = 'carState' - if not sm.updated[sock] or not sm.valid[sock]: - return - - car_state = sm[sock] - self._pause_query = car_state.vEgo <= 2.78 # 10km/h - - def update_position(self, sm): - if self.position_service == "liveLocationKalman": - self.update_locationd(sm) - else: - self.update_gps(sm) - - def update_locationd(self, sm): - sock = 'liveLocationKalman' - if not sm.updated[sock] or not sm.valid[sock]: - return - - location = sm[sock] - location_valid = (location.status == log.LiveLocationKalman.Status.valid) and location.positionGeodetic.valid - - if not location_valid: - return - - self._latitude = location.positionGeodetic.value[0] - self._longitude = location.positionGeodetic.value[1] - # print("MAPD: Use GPS pos: %s, %s (locationd)" % (self._latitude, self._longitude)) - - def update_gps(self, sm): - sock = 'gpsLocationExternal' - if not sm.updated[sock] or not sm.valid[sock]: - return - - log = sm[sock] - self.last_gps = log - - # ignore the message if the fix is invalid - if log.flags % 2 == 0: - return - - self._latitude = log.latitude - self._longitude = log.longitude - # print("MAPD: Use GPS pos to: %s, %s (gpsLocationExternal)" % (self._latitude, self._longitude)) - - def fetch_osm_data(self): - if self._pause_query: - return - self.osm.fetch_tags_around_location(self._latitude, self._longitude) - - def publish(self, pm): - if self._pause_query: - return - map_data_msg = messaging.new_message('liveMapData') - map_data_msg.liveMapData.currentRoadName = "" if self.osm.road_name is None else self.osm.road_name - - if (self.osm.road_name is not None and self.osm.road_name == self._road_name_prev) or self.osm.way_id == self._way_id_prev: - self._same_road_count += 1 - else: - self._same_road_count = 0 - self._road_name_prev = self.osm.road_name - self._way_id_prev = self.osm.way_id - map_data_msg.liveMapData.speedLimitValid = self._same_road_count > 3 and self.osm.speed_limit > 0 - - map_data_msg.liveMapData.speedLimit = self.osm.speed_limit - - pm.send('liveMapData', map_data_msg) - # print(f'MAPD *****: Publish: \n{map_data_msg}\n********') - -# provides live map data information -def mapd_thread(sm=None, pm=None): - use_locationd = False - position_service = "liveLocationKalman" if use_locationd else "gpsLocationExternal" - - set_core_affinity([1,]) - set_realtime_priority(1) - mapd = MapD(position_service) - rk = Ratekeeper(0.5, print_delay_threshold=None) # Keeps rate at 0.5 hz - - if sm is None: - sm = messaging.SubMaster(["carState", position_service]) - if pm is None: - pm = messaging.PubMaster(['liveMapData']) - - mapd.apply_last_gps_pos() - while True: - sm.update() - mapd.update_car_state(sm) - mapd.update_position(sm) - mapd.fetch_osm_data() - mapd.publish(pm) - rk.keep_time() - -def main(sm=None, pm=None): - mapd_thread(sm, pm) - - -if __name__ == "__main__": - main() diff --git a/selfdrive/hybrid_modeld/__init__.py b/selfdrive/hybrid_modeld/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/selfdrive/legacy_modeld/_dmonitoringmodeld b/selfdrive/hybrid_modeld/_dmonitoringmodeld similarity index 54% rename from selfdrive/legacy_modeld/_dmonitoringmodeld rename to selfdrive/hybrid_modeld/_dmonitoringmodeld index 721c67575..91a0164be 100755 Binary files a/selfdrive/legacy_modeld/_dmonitoringmodeld and b/selfdrive/hybrid_modeld/_dmonitoringmodeld differ diff --git a/selfdrive/hybrid_modeld/_modeld b/selfdrive/hybrid_modeld/_modeld new file mode 100755 index 000000000..fe2a9b023 Binary files /dev/null and b/selfdrive/hybrid_modeld/_modeld differ diff --git a/selfdrive/hybrid_modeld/constants.py b/selfdrive/hybrid_modeld/constants.py new file mode 100644 index 000000000..125864b98 --- /dev/null +++ b/selfdrive/hybrid_modeld/constants.py @@ -0,0 +1,7 @@ +IDX_N = 33 + +def index_function(idx, max_val=192, max_idx=32): + return (max_val) * ((idx/max_idx)**2) + + +T_IDXS = [index_function(idx, max_val=10.0) for idx in range(IDX_N)] diff --git a/selfdrive/legacy_modeld/dmonitoringmodeld b/selfdrive/hybrid_modeld/dmonitoringmodeld similarity index 100% rename from selfdrive/legacy_modeld/dmonitoringmodeld rename to selfdrive/hybrid_modeld/dmonitoringmodeld diff --git a/selfdrive/hybrid_modeld/modeld b/selfdrive/hybrid_modeld/modeld new file mode 100755 index 000000000..a5e740589 --- /dev/null +++ b/selfdrive/hybrid_modeld/modeld @@ -0,0 +1,11 @@ +#!/bin/sh + +DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null && pwd)" +cd $DIR + +if [ -f /EON ]; then + export LD_LIBRARY_PATH="/data/pythonpath/third_party/snpe/aarch64/:$LD_LIBRARY_PATH" +else + export LD_LIBRARY_PATH="$DIR/../../third_party/snpe/x86_64-linux-clang:$DIR/../../openpilot/third_party/snpe/x86_64:$LD_LIBRARY_PATH" +fi +exec ./_modeld \ No newline at end of file diff --git a/selfdrive/legacy_modeld/models/README.md b/selfdrive/hybrid_modeld/models/README.md similarity index 98% rename from selfdrive/legacy_modeld/models/README.md rename to selfdrive/hybrid_modeld/models/README.md index 7522cecec..bf90cd5d3 100644 --- a/selfdrive/legacy_modeld/models/README.md +++ b/selfdrive/hybrid_modeld/models/README.md @@ -97,7 +97,7 @@ To view the architecture of the ONNX networks, you can use [netron](https://netr * normalized, ranging from -1.0 to 1.0 ### output format -* 39 x float32 outputs ([parsing example](https://github.com/commaai/openpilot/blob/master/selfdrive/legacy_modeld/models/dmonitoring.cc#L165)) +* 39 x float32 outputs ([parsing example](https://github.com/commaai/openpilot/blob/master/selfdrive/hybrid_modeld/models/dmonitoring.cc#L165)) * face pose: 12 = 6 + 6 * face orientation [pitch, yaw, roll] in camera frame: 3 * face position [dx, dy] relative to image center: 2 diff --git a/selfdrive/hybrid_modeld/models/commonmodel.h b/selfdrive/hybrid_modeld/models/commonmodel.h new file mode 100644 index 000000000..b4501a2f9 --- /dev/null +++ b/selfdrive/hybrid_modeld/models/commonmodel.h @@ -0,0 +1,48 @@ +#pragma once + +#include +#include + +#include + +#define CL_USE_DEPRECATED_OPENCL_1_2_APIS +#ifdef __APPLE__ +#include +#else +#include +#endif + +#include "common/mat.h" +#include "cereal/messaging/messaging.h" +#include "selfdrive/hybrid_modeld/transforms/loadyuv.h" +#include "selfdrive/hybrid_modeld/transforms/transform.h" + +const bool send_raw_pred = getenv("SEND_RAW_PRED") != NULL; + +void softmax(const float* input, float* output, size_t len); +float softplus(float input); +float sigmoid(float input); + +template +constexpr const kj::ArrayPtr to_kj_array_ptr(const std::array &arr) { + return kj::ArrayPtr(arr.data(), arr.size()); +} + +class ModelFrame { +public: + ModelFrame(cl_device_id device_id, cl_context context); + ~ModelFrame(); + float* prepare(cl_mem yuv_cl, int width, int height, const mat3& transform, cl_mem *output); + + const int MODEL_WIDTH = 512; + const int MODEL_HEIGHT = 256; + const int MODEL_FRAME_SIZE = MODEL_WIDTH * MODEL_HEIGHT * 3 / 2; + const int buf_size = MODEL_FRAME_SIZE * 2; + +private: + Transform transform; + LoadYUVState loadyuv; + cl_command_queue q; + cl_mem y_cl, u_cl, v_cl, net_input_cl; + std::unique_ptr input_frames; +}; diff --git a/selfdrive/legacy_modeld/models/dmonitoring.h b/selfdrive/hybrid_modeld/models/dmonitoring.h similarity index 92% rename from selfdrive/legacy_modeld/models/dmonitoring.h rename to selfdrive/hybrid_modeld/models/dmonitoring.h index 722cc6b89..f5ca0434c 100644 --- a/selfdrive/legacy_modeld/models/dmonitoring.h +++ b/selfdrive/hybrid_modeld/models/dmonitoring.h @@ -4,8 +4,8 @@ #include "cereal/messaging/messaging.h" #include "common/util.h" -#include "selfdrive/legacy_modeld/models/commonmodel.h" -#include "selfdrive/legacy_modeld/runners/run.h" +#include "selfdrive/hybrid_modeld/models/commonmodel.h" +#include "selfdrive/hybrid_modeld/runners/run.h" #define OUTPUT_SIZE 39 diff --git a/selfdrive/legacy_modeld/models/dmonitoring_model.current b/selfdrive/hybrid_modeld/models/dmonitoring_model.current similarity index 100% rename from selfdrive/legacy_modeld/models/dmonitoring_model.current rename to selfdrive/hybrid_modeld/models/dmonitoring_model.current diff --git a/selfdrive/hybrid_modeld/models/dmonitoring_model.onnx b/selfdrive/hybrid_modeld/models/dmonitoring_model.onnx new file mode 100644 index 000000000..bd986e2f0 Binary files /dev/null and b/selfdrive/hybrid_modeld/models/dmonitoring_model.onnx differ diff --git a/selfdrive/legacy_modeld/models/dmonitoring_model_q.dlc b/selfdrive/hybrid_modeld/models/dmonitoring_model_q.dlc similarity index 100% rename from selfdrive/legacy_modeld/models/dmonitoring_model_q.dlc rename to selfdrive/hybrid_modeld/models/dmonitoring_model_q.dlc diff --git a/selfdrive/hybrid_modeld/models/driving.h b/selfdrive/hybrid_modeld/models/driving.h new file mode 100644 index 000000000..68eda2abd --- /dev/null +++ b/selfdrive/hybrid_modeld/models/driving.h @@ -0,0 +1,285 @@ +#pragma once + +// gate this here +#define TEMPORAL +#define DESIRE +#define TRAFFIC_CONVENTION + +#include +#include + +#include "cereal/messaging/messaging.h" +#include "cereal/visionipc/visionipc_client.h" +#include "common/mat.h" +#include "common/hybrid_modeldata.h" +#include "common/util.h" +#include "selfdrive/hybrid_modeld/models/commonmodel.h" +#include "selfdrive/hybrid_modeld/models/nav.h" +#include "selfdrive/hybrid_modeld/runners/run.h" + +constexpr int DESIRE_LEN = 8; +constexpr int DESIRE_PRED_LEN = 4; +constexpr int TRAFFIC_CONVENTION_LEN = 2; +constexpr int DRIVING_STYLE_LEN = 12; +constexpr int MODEL_FREQ = 20; + +constexpr int DISENGAGE_LEN = 5; +constexpr int BLINKER_LEN = 6; +constexpr int META_STRIDE = 7; + +constexpr int PLAN_MHP_N = 5; +constexpr int STOP_LINE_MHP_N = 3; + +constexpr int LEAD_MHP_N = 2; +constexpr int LEAD_TRAJ_LEN = 6; +constexpr int LEAD_PRED_DIM = 4; +constexpr int LEAD_MHP_SELECTION = 3; + +struct ModelOutputXYZ { + float x; + float y; + float z; +}; +static_assert(sizeof(ModelOutputXYZ) == sizeof(float)*3); + +struct ModelOutputYZ { + float y; + float z; +}; +static_assert(sizeof(ModelOutputYZ) == sizeof(float)*2); + +struct ModelOutputPlanElement { + ModelOutputXYZ position; + ModelOutputXYZ velocity; + ModelOutputXYZ acceleration; + ModelOutputXYZ rotation; + ModelOutputXYZ rotation_rate; +}; +static_assert(sizeof(ModelOutputPlanElement) == sizeof(ModelOutputXYZ)*5); + +struct ModelOutputPlanPrediction { + std::array mean; + std::array std; + float prob; +}; +static_assert(sizeof(ModelOutputPlanPrediction) == (sizeof(ModelOutputPlanElement)*TRAJECTORY_SIZE*2) + sizeof(float)); + +struct ModelOutputPlans { + std::array prediction; + + constexpr const ModelOutputPlanPrediction &get_best_prediction() const { + int max_idx = 0; + for (int i = 1; i < prediction.size(); i++) { + if (prediction[i].prob > prediction[max_idx].prob) { + max_idx = i; + } + } + return prediction[max_idx]; + } +}; +static_assert(sizeof(ModelOutputPlans) == sizeof(ModelOutputPlanPrediction)*PLAN_MHP_N); + +struct ModelOutputLinesXY { + std::array left_far; + std::array left_near; + std::array right_near; + std::array right_far; +}; +static_assert(sizeof(ModelOutputLinesXY) == sizeof(ModelOutputYZ)*TRAJECTORY_SIZE*4); + +struct ModelOutputLineProbVal { + float val_deprecated; + float val; +}; +static_assert(sizeof(ModelOutputLineProbVal) == sizeof(float)*2); + +struct ModelOutputLinesProb { + ModelOutputLineProbVal left_far; + ModelOutputLineProbVal left_near; + ModelOutputLineProbVal right_near; + ModelOutputLineProbVal right_far; +}; +static_assert(sizeof(ModelOutputLinesProb) == sizeof(ModelOutputLineProbVal)*4); + +struct ModelOutputLaneLines { + ModelOutputLinesXY mean; + ModelOutputLinesXY std; + ModelOutputLinesProb prob; +}; +static_assert(sizeof(ModelOutputLaneLines) == (sizeof(ModelOutputLinesXY)*2) + sizeof(ModelOutputLinesProb)); + +struct ModelOutputEdgessXY { + std::array left; + std::array right; +}; +static_assert(sizeof(ModelOutputEdgessXY) == sizeof(ModelOutputYZ)*TRAJECTORY_SIZE*2); + +struct ModelOutputRoadEdges { + ModelOutputEdgessXY mean; + ModelOutputEdgessXY std; +}; +static_assert(sizeof(ModelOutputRoadEdges) == (sizeof(ModelOutputEdgessXY)*2)); + +struct ModelOutputLeadElement { + float x; + float y; + float velocity; + float acceleration; +}; +static_assert(sizeof(ModelOutputLeadElement) == sizeof(float)*4); + +struct ModelOutputLeadPrediction { + std::array mean; + std::array std; + std::array prob; +}; +static_assert(sizeof(ModelOutputLeadPrediction) == (sizeof(ModelOutputLeadElement)*LEAD_TRAJ_LEN*2) + (sizeof(float)*LEAD_MHP_SELECTION)); + +struct ModelOutputLeads { + std::array prediction; + std::array prob; + + constexpr const ModelOutputLeadPrediction &get_best_prediction(int t_idx) const { + int max_idx = 0; + for (int i = 1; i < prediction.size(); i++) { + if (prediction[i].prob[t_idx] > prediction[max_idx].prob[t_idx]) { + max_idx = i; + } + } + return prediction[max_idx]; + } +}; +static_assert(sizeof(ModelOutputLeads) == (sizeof(ModelOutputLeadPrediction)*LEAD_MHP_N) + (sizeof(float)*LEAD_MHP_SELECTION)); + +struct ModelOutputStopLineElement { + ModelOutputXYZ position; + ModelOutputXYZ rotation; + float speed; + float time; +}; +static_assert(sizeof(ModelOutputStopLineElement) == (sizeof(ModelOutputXYZ)*2 + sizeof(float)*2)); + +struct ModelOutputStopLinePrediction { + ModelOutputStopLineElement mean; + ModelOutputStopLineElement std; + float prob; +}; +static_assert(sizeof(ModelOutputStopLinePrediction) == (sizeof(ModelOutputStopLineElement)*2 + sizeof(float))); + +struct ModelOutputStopLines { + std::array prediction; + float prob; + + constexpr const ModelOutputStopLinePrediction &get_best_prediction(int t_idx) const { + int max_idx = 0; + for (int i = 1; i < prediction.size(); i++) { + if (prediction[i].prob > prediction[max_idx].prob) { + max_idx = i; + } + } + return prediction[max_idx]; + } +}; +static_assert(sizeof(ModelOutputStopLines) == (sizeof(ModelOutputStopLinePrediction)*STOP_LINE_MHP_N) + sizeof(float)); + +struct ModelOutputPose { + ModelOutputXYZ velocity_mean; + ModelOutputXYZ rotation_mean; + ModelOutputXYZ velocity_std; + ModelOutputXYZ rotation_std; +}; +static_assert(sizeof(ModelOutputPose) == sizeof(ModelOutputXYZ)*4); + +struct ModelOutputDisengageProb { + float gas_disengage; + float brake_disengage; + float steer_override; + float brake_3ms2; + float brake_4ms2; + float brake_5ms2; + float gas_pressed; +}; +static_assert(sizeof(ModelOutputDisengageProb) == sizeof(float)*7); + +struct ModelOutputBlinkerProb { + float left; + float right; +}; +static_assert(sizeof(ModelOutputBlinkerProb) == sizeof(float)*2); + +struct ModelOutputDesireProb { + union { + struct { + float none; + float turn_left; + float turn_right; + float lane_change_left; + float lane_change_right; + float keep_left; + float keep_right; + float null; + }; + struct { + std::array array; + }; + }; +}; +static_assert(sizeof(ModelOutputDesireProb) == sizeof(float)*DESIRE_LEN); + +struct ModelOutputMeta { + ModelOutputDesireProb desire_state_prob; + float engaged_prob; + std::array disengage_prob; + std::array blinker_prob; + std::array desire_pred_prob; +}; +static_assert(sizeof(ModelOutputMeta) == sizeof(ModelOutputDesireProb) + sizeof(float) + (sizeof(ModelOutputDisengageProb)*DISENGAGE_LEN) + (sizeof(ModelOutputBlinkerProb)*BLINKER_LEN) + (sizeof(ModelOutputDesireProb)*DESIRE_PRED_LEN)); + +struct ModelOutput { + const ModelOutputPlans plans; + const ModelOutputLaneLines lane_lines; + const ModelOutputRoadEdges road_edges; + const ModelOutputLeads leads; + const ModelOutputStopLines stop_lines; + const ModelOutputMeta meta; + const ModelOutputPose pose; +}; + +constexpr int OUTPUT_SIZE = sizeof(ModelOutput) / sizeof(float); +#ifdef TEMPORAL + constexpr int TEMPORAL_SIZE = 512; +#else + constexpr int TEMPORAL_SIZE = 0; +#endif +constexpr int NET_OUTPUT_SIZE = OUTPUT_SIZE + TEMPORAL_SIZE; + +// TODO: convert remaining arrays to std::array and update model runners +struct ModelState { + ModelFrame *frame = nullptr; + ModelFrame *wide_frame = nullptr; + std::array output = {}; + std::unique_ptr m; +#ifdef DESIRE + float prev_desire[DESIRE_LEN] = {}; + float pulse_desire[DESIRE_LEN] = {}; +#endif +#ifdef TRAFFIC_CONVENTION + float traffic_convention[TRAFFIC_CONVENTION_LEN] = {}; +#endif +#ifdef DRIVING_STYLE + float driving_style[DRIVING_STYLE_LEN] = {}; +#endif +#ifdef NAV + float nav_features[NAV_FEATURE_LEN] = {}; +#endif +}; + +void model_init(ModelState* s, cl_device_id device_id, cl_context context); +ModelOutput *model_eval_frame(ModelState* s, VisionBuf* buf, VisionBuf* buf_wide, + const mat3 &transform, const mat3 &transform_wide, float *desire_in, bool is_rhd, float *driving_style, float *nav_features, bool prepare_only); +void model_free(ModelState* s); +void model_publish(PubMaster &pm, uint32_t vipc_frame_id, uint32_t vipc_frame_id_extra, uint32_t frame_id, float frame_drop, + const ModelOutput &net_outputs, uint64_t timestamp_eof, + float model_execution_time, kj::ArrayPtr raw_pred, const bool valid); +void posenet_publish(PubMaster &pm, uint32_t vipc_frame_id, uint32_t vipc_dropped_frames, + const ModelOutput &net_outputs, uint64_t timestamp_eof, const bool valid); diff --git a/selfdrive/legacy_modeld/models/nav.h b/selfdrive/hybrid_modeld/models/nav.h similarity index 92% rename from selfdrive/legacy_modeld/models/nav.h rename to selfdrive/hybrid_modeld/models/nav.h index 207c8035a..b666381a1 100644 --- a/selfdrive/legacy_modeld/models/nav.h +++ b/selfdrive/hybrid_modeld/models/nav.h @@ -3,9 +3,9 @@ #include "cereal/messaging/messaging.h" #include "cereal/visionipc/visionipc_client.h" #include "common/util.h" -#include "common/legacy_modeldata.h" -#include "selfdrive/legacy_modeld/models/commonmodel.h" -#include "selfdrive/legacy_modeld/runners/run.h" +#include "common/hybrid_modeldata.h" +#include "selfdrive/hybrid_modeld/models/commonmodel.h" +#include "selfdrive/hybrid_modeld/runners/run.h" constexpr int NAV_INPUT_SIZE = 256*256; constexpr int NAV_FEATURE_LEN = 64; diff --git a/selfdrive/hybrid_modeld/models/supercombo.dlc b/selfdrive/hybrid_modeld/models/supercombo.dlc new file mode 100644 index 000000000..2e336a2d4 Binary files /dev/null and b/selfdrive/hybrid_modeld/models/supercombo.dlc differ diff --git a/selfdrive/hybrid_modeld/models/supercombo.onnx b/selfdrive/hybrid_modeld/models/supercombo.onnx new file mode 100644 index 000000000..749ac1287 Binary files /dev/null and b/selfdrive/hybrid_modeld/models/supercombo.onnx differ diff --git a/selfdrive/hybrid_modeld/models/supercombo.thneed b/selfdrive/hybrid_modeld/models/supercombo.thneed new file mode 100644 index 000000000..3b6ae846e Binary files /dev/null and b/selfdrive/hybrid_modeld/models/supercombo.thneed differ diff --git a/selfdrive/hybrid_modeld/models/supercombo_badweights.thneed b/selfdrive/hybrid_modeld/models/supercombo_badweights.thneed new file mode 100644 index 000000000..c644e5b82 Binary files /dev/null and b/selfdrive/hybrid_modeld/models/supercombo_badweights.thneed differ diff --git a/selfdrive/hybrid_modeld/runners/onnx_runner.py b/selfdrive/hybrid_modeld/runners/onnx_runner.py new file mode 100755 index 000000000..2a6238c1d --- /dev/null +++ b/selfdrive/hybrid_modeld/runners/onnx_runner.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python3 + +import os +import sys +import numpy as np + +os.environ["OMP_NUM_THREADS"] = "4" +os.environ["OMP_WAIT_POLICY"] = "PASSIVE" + +import onnxruntime as ort # pylint: disable=import-error + +def read(sz): + dd = [] + gt = 0 + while gt < sz * 4: + st = os.read(0, sz * 4 - gt) + assert(len(st) > 0) + dd.append(st) + gt += len(st) + return np.frombuffer(b''.join(dd), dtype=np.float32) + +def write(d): + os.write(1, d.tobytes()) + +def run_loop(m): + ishapes = [[1]+ii.shape[1:] for ii in m.get_inputs()] + keys = [x.name for x in m.get_inputs()] + + # run once to initialize CUDA provider + if "CUDAExecutionProvider" in m.get_providers(): + m.run(None, dict(zip(keys, [np.zeros(shp, dtype=np.float32) for shp in ishapes]))) + + print("ready to run onnx model", keys, ishapes, file=sys.stderr) + while 1: + inputs = [] + for shp in ishapes: + ts = np.product(shp) + #print("reshaping %s with offset %d" % (str(shp), offset), file=sys.stderr) + inputs.append(read(ts).reshape(shp)) + ret = m.run(None, dict(zip(keys, inputs))) + #print(ret, file=sys.stderr) + for r in ret: + write(r) + + +if __name__ == "__main__": + print("Onnx available providers: ", ort.get_available_providers(), file=sys.stderr) + options = ort.SessionOptions() + options.graph_optimization_level = ort.GraphOptimizationLevel.ORT_DISABLE_ALL + if 'OpenVINOExecutionProvider' in ort.get_available_providers() and 'ONNXCPU' not in os.environ: + provider = 'OpenVINOExecutionProvider' + elif 'CUDAExecutionProvider' in ort.get_available_providers() and 'ONNXCPU' not in os.environ: + options.intra_op_num_threads = 2 + provider = 'CUDAExecutionProvider' + else: + options.intra_op_num_threads = 2 + options.inter_op_num_threads = 8 + options.execution_mode = ort.ExecutionMode.ORT_SEQUENTIAL + options.graph_optimization_level = ort.GraphOptimizationLevel.ORT_ENABLE_ALL + provider = 'CPUExecutionProvider' + + print("Onnx selected provider: ", [provider], file=sys.stderr) + ort_session = ort.InferenceSession(sys.argv[1], options, providers=[provider]) + print("Onnx using ", ort_session.get_providers(), file=sys.stderr) + run_loop(ort_session) diff --git a/selfdrive/hybrid_modeld/runners/onnxmodel.h b/selfdrive/hybrid_modeld/runners/onnxmodel.h new file mode 100644 index 000000000..dd29093ce --- /dev/null +++ b/selfdrive/hybrid_modeld/runners/onnxmodel.h @@ -0,0 +1,50 @@ +#pragma once + +#include + +#include "selfdrive/hybrid_modeld/runners/runmodel.h" + +class ONNXModel : public RunModel { +public: + ONNXModel(const char *path, float *output, size_t output_size, int runtime, bool use_extra = false); + ~ONNXModel(); + void addRecurrent(float *state, int state_size); + void addDesire(float *state, int state_size); + void addNavFeatures(float *state, int state_size); + void addDrivingStyle(float *state, int state_size); + void addTrafficConvention(float *state, int state_size); + void addCalib(float *state, int state_size); + void addImage(float *image_buf, int buf_size); + void addExtra(float *image_buf, int buf_size); + void execute(); +private: + int proc_pid; + + float *output; + size_t output_size; + + float *rnn_input_buf = NULL; + int rnn_state_size; + float *desire_input_buf = NULL; + int desire_state_size; + float *nav_features_input_buf = NULL; + int nav_features_size; + float *driving_style_input_buf = NULL; + int driving_style_size; + float *traffic_convention_input_buf = NULL; + int traffic_convention_size; + float *calib_input_buf = NULL; + int calib_size; + float *image_input_buf = NULL; + int image_buf_size; + float *extra_input_buf = NULL; + int extra_buf_size; + bool use_extra; + + // pipe to communicate to keras subprocess + void pread(float *buf, int size); + void pwrite(float *buf, int size); + int pipein[2]; + int pipeout[2]; +}; + diff --git a/selfdrive/hybrid_modeld/runners/run.h b/selfdrive/hybrid_modeld/runners/run.h new file mode 100644 index 000000000..c64f300fe --- /dev/null +++ b/selfdrive/hybrid_modeld/runners/run.h @@ -0,0 +1,10 @@ +#pragma once + +#include "runmodel.h" +#include "snpemodel.h" + +#if defined(USE_THNEED) +#include "thneedmodel.h" +#elif defined(USE_ONNX_MODEL) +#include "onnxmodel.h" +#endif diff --git a/selfdrive/hybrid_modeld/runners/runmodel.h b/selfdrive/hybrid_modeld/runners/runmodel.h new file mode 100644 index 000000000..487a2a1fe --- /dev/null +++ b/selfdrive/hybrid_modeld/runners/runmodel.h @@ -0,0 +1,17 @@ +#pragma once +class RunModel { +public: + virtual ~RunModel() {} + virtual void addRecurrent(float *state, int state_size) {} + virtual void addDesire(float *state, int state_size) {} + virtual void addNavFeatures(float *state, int state_size) {} + virtual void addDrivingStyle(float *state, int state_size) {} + virtual void addTrafficConvention(float *state, int state_size) {} + virtual void addCalib(float *state, int state_size) {} + virtual void addImage(float *image_buf, int buf_size) {} + virtual void addExtra(float *image_buf, int buf_size) {} + virtual void execute() {} + virtual void* getInputBuf() { return nullptr; } + virtual void* getExtraBuf() { return nullptr; } +}; + diff --git a/selfdrive/hybrid_modeld/runners/snpemodel.h b/selfdrive/hybrid_modeld/runners/snpemodel.h new file mode 100644 index 000000000..6db727c76 --- /dev/null +++ b/selfdrive/hybrid_modeld/runners/snpemodel.h @@ -0,0 +1,83 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "runmodel.h" + +#define USE_CPU_RUNTIME 0 +#define USE_GPU_RUNTIME 1 +#define USE_DSP_RUNTIME 2 + +#ifdef USE_THNEED +#include "selfdrive/hybrid_modeld/thneed/thneed.h" +#endif + +class SNPEModel : public RunModel { +public: + SNPEModel(const char *path, float *loutput, size_t loutput_size, int runtime, bool luse_extra = false); + void addRecurrent(float *state, int state_size); + void addTrafficConvention(float *state, int state_size); + void addCalib(float *state, int state_size); + void addDesire(float *state, int state_size); + void addDrivingStyle(float *state, int state_size); + void addNavFeatures(float *state, int state_size); + void addImage(float *image_buf, int buf_size); + void addExtra(float *image_buf, int buf_size); + void execute(); + +#ifdef USE_THNEED + Thneed *thneed = NULL; +#endif + +private: + std::string model_data; + +#if defined(QCOM) || defined(QCOM2) + zdl::DlSystem::Runtime_t Runtime; +#endif + + // snpe model stuff + std::unique_ptr snpe; + + // snpe input stuff + zdl::DlSystem::UserBufferMap inputMap; + std::unique_ptr inputBuffer; + float *input; + size_t input_size; + + // snpe output stuff + zdl::DlSystem::UserBufferMap outputMap; + std::unique_ptr outputBuffer; + float *output; + size_t output_size; + + // extra input stuff + std::unique_ptr extraBuffer; + float *extra; + size_t extra_size; + bool use_extra; + + // recurrent and desire + std::unique_ptr addExtra(float *state, int state_size, int idx); + float *recurrent; + size_t recurrent_size; + std::unique_ptr recurrentBuffer; + float *trafficConvention; + std::unique_ptr trafficConventionBuffer; + float *desire; + std::unique_ptr desireBuffer; + float *navFeatures; + std::unique_ptr navFeaturesBuffer; + float *drivingStyle; + std::unique_ptr drivingStyleBuffer; + float *calib; + std::unique_ptr calibBuffer; +}; diff --git a/selfdrive/hybrid_modeld/runners/thneedmodel.h b/selfdrive/hybrid_modeld/runners/thneedmodel.h new file mode 100644 index 000000000..03ae8b9df --- /dev/null +++ b/selfdrive/hybrid_modeld/runners/thneedmodel.h @@ -0,0 +1,35 @@ +#pragma once + +#include "selfdrive/hybrid_modeld/runners/runmodel.h" +#include "selfdrive/hybrid_modeld/thneed/thneed.h" + +class ThneedModel : public RunModel { +public: + ThneedModel(const char *path, float *loutput, size_t loutput_size, int runtime, bool luse_extra = false); + void addRecurrent(float *state, int state_size); + void addTrafficConvention(float *state, int state_size); + void addDesire(float *state, int state_size); + void addNavFeatures(float *state, int state_size); + void addDrivingStyle(float *state, int state_size); + void addImage(float *image_buf, int buf_size); + void addExtra(float *image_buf, int buf_size); + void execute(); + void* getInputBuf(); + void* getExtraBuf(); +private: + Thneed *thneed = NULL; + bool recorded; + bool use_extra; + + float *input; + float *extra; + float *output; + + // recurrent and desire + float *recurrent; + float *trafficConvention; + float *drivingStyle; + float *desire; + float *navFeatures; +}; + diff --git a/selfdrive/hybrid_modeld/tests/__init__.py b/selfdrive/hybrid_modeld/tests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/selfdrive/hybrid_modeld/tests/snpe_benchmark/.gitignore b/selfdrive/hybrid_modeld/tests/snpe_benchmark/.gitignore new file mode 100644 index 000000000..d83a1b2ff --- /dev/null +++ b/selfdrive/hybrid_modeld/tests/snpe_benchmark/.gitignore @@ -0,0 +1 @@ +benchmark diff --git a/selfdrive/hybrid_modeld/tests/snpe_benchmark/benchmark.sh b/selfdrive/hybrid_modeld/tests/snpe_benchmark/benchmark.sh new file mode 100755 index 000000000..a9d3f7978 --- /dev/null +++ b/selfdrive/hybrid_modeld/tests/snpe_benchmark/benchmark.sh @@ -0,0 +1,4 @@ +#!/bin/sh -e +clang++ -I /data/openpilot/third_party/snpe/include/ -L/data/pythonpath/third_party/snpe/aarch64 -lSNPE benchmark.cc -o benchmark +export LD_LIBRARY_PATH="/data/pythonpath/third_party/snpe/aarch64/:$HOME/openpilot/third_party/snpe/x86_64/:$LD_LIBRARY_PATH" +exec ./benchmark $1 diff --git a/selfdrive/hybrid_modeld/tests/test_modeld.py b/selfdrive/hybrid_modeld/tests/test_modeld.py new file mode 100755 index 000000000..f09aec578 --- /dev/null +++ b/selfdrive/hybrid_modeld/tests/test_modeld.py @@ -0,0 +1,107 @@ +#!/usr/bin/env python3 +import time +import unittest +import numpy as np +import random + +import cereal.messaging as messaging +from cereal.visionipc.visionipc_pyx import VisionIpcServer, VisionStreamType # pylint: disable=no-name-in-module, import-error +from common.transformations.camera import tici_f_frame_size +from common.realtime import DT_MDL +from selfdrive.manager.process_config import managed_processes + + +VIPC_STREAM = {"roadCameraState": VisionStreamType.VISION_STREAM_ROAD, "driverCameraState": VisionStreamType.VISION_STREAM_DRIVER, + "wideRoadCameraState": VisionStreamType.VISION_STREAM_WIDE_ROAD} + +IMG = np.zeros(int(tici_f_frame_size[0]*tici_f_frame_size[1]*(3/2)), dtype=np.uint8) +IMG_BYTES = IMG.flatten().tobytes() + +class TestModeld(unittest.TestCase): + + def setUp(self): + self.vipc_server = VisionIpcServer("camerad") + self.vipc_server.create_buffers(VisionStreamType.VISION_STREAM_ROAD, 40, False, *tici_f_frame_size) + self.vipc_server.create_buffers(VisionStreamType.VISION_STREAM_DRIVER, 40, False, *tici_f_frame_size) + self.vipc_server.create_buffers(VisionStreamType.VISION_STREAM_WIDE_ROAD, 40, False, *tici_f_frame_size) + self.vipc_server.start_listener() + + self.sm = messaging.SubMaster(['modelV2', 'cameraOdometry']) + self.pm = messaging.PubMaster(['roadCameraState', 'wideRoadCameraState', 'liveCalibration', 'lateralPlan']) + + managed_processes['modeld'].start() + time.sleep(0.2) + self.sm.update(1000) + + def tearDown(self): + managed_processes['modeld'].stop() + del self.vipc_server + + def _send_frames(self, frame_id, cams=None): + if cams is None: + cams = ('roadCameraState', 'wideRoadCameraState') + + cs = None + for cam in cams: + msg = messaging.new_message(cam) + cs = getattr(msg, cam) + cs.frameId = frame_id + cs.timestampSof = int((frame_id * DT_MDL) * 1e9) + cs.timestampEof = int(cs.timestampSof + (DT_MDL * 1e9)) + + self.pm.send(msg.which(), msg) + self.vipc_server.send(VIPC_STREAM[msg.which()], IMG_BYTES, cs.frameId, + cs.timestampSof, cs.timestampEof) + return cs + + def _wait(self): + self.sm.update(5000) + if self.sm['modelV2'].frameId != self.sm['cameraOdometry'].frameId: + self.sm.update(1000) + + def test_modeld(self): + for n in range(1, 500): + cs = self._send_frames(n) + self._wait() + + mdl = self.sm['modelV2'] + self.assertEqual(mdl.frameId, n) + self.assertEqual(mdl.frameIdExtra, n) + self.assertEqual(mdl.timestampEof, cs.timestampEof) + self.assertEqual(mdl.frameAge, 0) + self.assertEqual(mdl.frameDropPerc, 0) + + odo = self.sm['cameraOdometry'] + self.assertEqual(odo.frameId, n) + self.assertEqual(odo.timestampEof, cs.timestampEof) + + def test_dropped_frames(self): + """ + modeld should only run on consecutive road frames + """ + frame_id = -1 + road_frames = list() + for n in range(1, 50): + if (random.random() < 0.1) and n > 3: + cams = random.choice([(), ('wideRoadCameraState', )]) + self._send_frames(n, cams) + else: + self._send_frames(n) + road_frames.append(n) + self._wait() + + if len(road_frames) < 3 or road_frames[-1] - road_frames[-2] == 1: + frame_id = road_frames[-1] + + mdl = self.sm['modelV2'] + odo = self.sm['cameraOdometry'] + self.assertEqual(mdl.frameId, frame_id) + self.assertEqual(mdl.frameIdExtra, frame_id) + self.assertEqual(odo.frameId, frame_id) + if n != frame_id: + self.assertFalse(self.sm.updated['modelV2']) + self.assertFalse(self.sm.updated['cameraOdometry']) + + +if __name__ == "__main__": + unittest.main() diff --git a/selfdrive/hybrid_modeld/tests/tf_test/build.sh b/selfdrive/hybrid_modeld/tests/tf_test/build.sh new file mode 100755 index 000000000..4e92ca069 --- /dev/null +++ b/selfdrive/hybrid_modeld/tests/tf_test/build.sh @@ -0,0 +1,2 @@ +#!/bin/bash +clang++ -I /home/batman/one/external/tensorflow/include/ -L /home/batman/one/external/tensorflow/lib -Wl,-rpath=/home/batman/one/external/tensorflow/lib main.cc -ltensorflow diff --git a/selfdrive/hybrid_modeld/tests/tf_test/pb_loader.py b/selfdrive/hybrid_modeld/tests/tf_test/pb_loader.py new file mode 100755 index 000000000..78fd33aef --- /dev/null +++ b/selfdrive/hybrid_modeld/tests/tf_test/pb_loader.py @@ -0,0 +1,8 @@ +#!/usr/bin/env python3 +import sys +import tensorflow as tf # pylint: disable=import-error + +with open(sys.argv[1], "rb") as f: + graph_def = tf.compat.v1.GraphDef() + graph_def.ParseFromString(f.read()) + #tf.io.write_graph(graph_def, '', sys.argv[1]+".try") diff --git a/selfdrive/hybrid_modeld/tests/timing/benchmark.py b/selfdrive/hybrid_modeld/tests/timing/benchmark.py new file mode 100755 index 000000000..3c7ce5b70 --- /dev/null +++ b/selfdrive/hybrid_modeld/tests/timing/benchmark.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python3 +# type: ignore +# pylint: skip-file + +import os +import time +import numpy as np + +import cereal.messaging as messaging +from selfdrive.manager.process_config import managed_processes + + +N = int(os.getenv("N", "5")) +TIME = int(os.getenv("TIME", "30")) + +if __name__ == "__main__": + sock = messaging.sub_sock('modelV2', conflate=False, timeout=1000) + + execution_times = [] + + for _ in range(N): + os.environ['LOGPRINT'] = 'debug' + managed_processes['modeld'].start() + time.sleep(5) + + t = [] + start = time.monotonic() + while time.monotonic() - start < TIME: + msgs = messaging.drain_sock(sock, wait_for_one=True) + for m in msgs: + t.append(m.modelV2.modelExecutionTime) + + execution_times.append(np.array(t[10:]) * 1000) + managed_processes['modeld'].stop() + + print("\n\n") + print(f"ran modeld {N} times for {TIME}s each") + for n, t in enumerate(execution_times): + print(f"\tavg: {sum(t)/len(t):0.2f}ms, min: {min(t):0.2f}ms, max: {max(t):0.2f}ms") + print("\n\n") diff --git a/selfdrive/hybrid_modeld/thneed/README b/selfdrive/hybrid_modeld/thneed/README new file mode 100644 index 000000000..f3bc66d8f --- /dev/null +++ b/selfdrive/hybrid_modeld/thneed/README @@ -0,0 +1,8 @@ +thneed is an SNPE accelerator. I know SNPE is already an accelerator, but sometimes things need to go even faster.. + +It runs on the local device, and caches a single model run. Then it replays it, but fast. + +thneed slices through abstraction layers like a fish. + +You need a thneed. + diff --git a/selfdrive/hybrid_modeld/thneed/__init__.py b/selfdrive/hybrid_modeld/thneed/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/selfdrive/hybrid_modeld/thneed/compile b/selfdrive/hybrid_modeld/thneed/compile new file mode 100755 index 000000000..4e83e950a Binary files /dev/null and b/selfdrive/hybrid_modeld/thneed/compile differ diff --git a/selfdrive/hybrid_modeld/thneed/kernels/convolution_.cl b/selfdrive/hybrid_modeld/thneed/kernels/convolution_.cl new file mode 100644 index 000000000..1b9d74b83 --- /dev/null +++ b/selfdrive/hybrid_modeld/thneed/kernels/convolution_.cl @@ -0,0 +1,272 @@ + read_only image2d_t input, +#ifndef DEPTHWISE + short startPackedInputChannel, + short numPackedInputChannelsForGroup, short totalNumPackedInputChannels, + // typo required for API compatibility + short packedOuputChannelOffset, short totalNumPackedOutputChannels, +#else + short totalNumPackedChannels, +#endif + read_only image2d_t weights, __constant float *biases, + short filterSizeX, short filterSizeY, + write_only image2d_t output, + short paddingX, short paddingY, short strideX, short strideY, +#ifdef SUPPORT_DILATION + short dilationX, short dilationY, +#endif + short neuron, float a, float b, float min_clamp, float max_clamp, +#ifndef DEPTHWISE + // note: these are not supported + __constant float *parameters, __constant float *batchNormBiases, +#endif + short numOutputColumns +#ifdef SUPPORT_ACCUMULATION + , short doAccumulate, read_only image2d_t accumulator +#endif + ) { + +#ifndef NUM_OUTPUTS + #define NUM_OUTPUTS 4 +#endif + + // init + const sampler_t smp = CLK_NORMALIZED_COORDS_FALSE | CLK_ADDRESS_CLAMP | CLK_FILTER_NEAREST; + short packedOutputChannel = get_global_id(0); + short startOutputColumn = mul24((short)get_global_id(1), NUM_OUTPUTS); + short outputRow = get_global_id(2); + +#ifdef DEPTHWISE + short totalNumPackedInputChannels = totalNumPackedChannels; + short totalNumPackedOutputChannels = totalNumPackedChannels; + short startPackedInputChannel = packedOutputChannel; +#endif + + short startX = mad24(mad24(startOutputColumn, strideX, -paddingX), totalNumPackedInputChannels, startPackedInputChannel); + short strideWithChannels = mul24(strideX, totalNumPackedInputChannels); + + float4 outputValues[NUM_OUTPUTS]; + for (short i = 0; i < NUM_OUTPUTS; ++i) { + outputValues[i] = (float4)(0, 0, 0, 0); + } + + int2 inputLocation; + inputLocation.y = mad24(outputRow, strideY, -paddingY); + + int2 weightLocation; + weightLocation.x = 0; + weightLocation.y = packedOutputChannel; + +#ifdef DEPTHWISE + +#ifdef SUPPORT_DILATION + + // depthwise convolution + for (short rfRow = 0; rfRow < filterSizeY; ++rfRow) { + for (short rfColumn = 0; rfColumn < filterSizeX; ++rfColumn) { + short dilatedStepX = mul24(totalNumPackedChannels, dilationX); + inputLocation.x = mad24(rfColumn, dilatedStepX, startX); + float4 inputValues[4]; + for (short i = 0; i < 4; ++i) { + inputValues[i] = read_imagef(input, smp, inputLocation); + inputLocation.x += strideWithChannels; + } + float4 weightValues = read_imagef(weights, smp, weightLocation); + ++weightLocation.x; + outputValues[0] += inputValues[0] * weightValues; + outputValues[1] += inputValues[1] * weightValues; + outputValues[2] += inputValues[2] * weightValues; + outputValues[3] += inputValues[3] * weightValues; + } + inputLocation.y += dilationY; + } + +#else + + // depthwise unstrided convolution + for (short rfRow = 0; rfRow < filterSizeY; ++rfRow) { + float4 inputValues[4]; + inputLocation.x = startX; + for (short i = 1; i < 4; ++i) { + inputValues[i] = read_imagef(input, smp, inputLocation); + inputLocation.x += totalNumPackedOutputChannels; + } + for (short rfColumn = 0; rfColumn < filterSizeX; ++rfColumn) { + inputValues[0] = inputValues[1]; + inputValues[1] = inputValues[2]; + inputValues[2] = inputValues[3]; + inputValues[3] = read_imagef(input, smp, inputLocation); + inputLocation.x += totalNumPackedChannels; + float4 weightValues = read_imagef(weights, smp, weightLocation); + ++weightLocation.x; + outputValues[0] += inputValues[0] * weightValues; + outputValues[1] += inputValues[1] * weightValues; + outputValues[2] += inputValues[2] * weightValues; + outputValues[3] += inputValues[3] * weightValues; + } + ++inputLocation.y; + } + +#endif + +#elif defined(ONLY_1X1_CONV) + + // 1x1 convolution + short endPackedInputChannel = startPackedInputChannel + numPackedInputChannelsForGroup; + for (short packedInputChannel = startPackedInputChannel; packedInputChannel < endPackedInputChannel; ++packedInputChannel) { + float4 weightValues[4]; + for (short outChIdx = 0; outChIdx < 4; ++outChIdx) { + weightValues[outChIdx] = read_imagef(weights, smp, weightLocation); + ++weightLocation.x; + } + + inputLocation.x = startX + packedInputChannel; + float4 inputValues[NUM_OUTPUTS]; + for (short i = 0; i < NUM_OUTPUTS; ++i) { + inputValues[i] = read_imagef(input, smp, inputLocation); + inputLocation.x += strideWithChannels; + } + + for (short i = 0; i < NUM_OUTPUTS; ++i) { + float4 curOutputValues = outputValues[i]; + curOutputValues.x += inputValues[i].x * weightValues[0].x; + curOutputValues.x += inputValues[i].y * weightValues[0].y; + curOutputValues.x += inputValues[i].z * weightValues[0].z; + curOutputValues.x += inputValues[i].w * weightValues[0].w; + curOutputValues.y += inputValues[i].x * weightValues[1].x; + curOutputValues.y += inputValues[i].y * weightValues[1].y; + curOutputValues.y += inputValues[i].z * weightValues[1].z; + curOutputValues.y += inputValues[i].w * weightValues[1].w; + curOutputValues.z += inputValues[i].x * weightValues[2].x; + curOutputValues.z += inputValues[i].y * weightValues[2].y; + curOutputValues.z += inputValues[i].z * weightValues[2].z; + curOutputValues.z += inputValues[i].w * weightValues[2].w; + curOutputValues.w += inputValues[i].x * weightValues[3].x; + curOutputValues.w += inputValues[i].y * weightValues[3].y; + curOutputValues.w += inputValues[i].z * weightValues[3].z; + curOutputValues.w += inputValues[i].w * weightValues[3].w; + outputValues[i] = curOutputValues; + } + } + packedOutputChannel += packedOuputChannelOffset; + +#else + + // normal convolution + for (short rfRow = 0; rfRow < filterSizeY; ++rfRow) { + for (short packedInputChannel = 0; packedInputChannel < numPackedInputChannelsForGroup; ++packedInputChannel) { + short startXForChannel = startX + packedInputChannel; + for (short rfColumn = 0; rfColumn < filterSizeX; ++rfColumn) { + + float4 weightValues[4]; + for (short outChIdx = 0; outChIdx < 4; ++outChIdx) { + weightValues[outChIdx] = read_imagef(weights, smp, weightLocation); + ++weightLocation.x; + } + +#ifdef SUPPORT_DILATION + short dilatedStepX = mul24(totalNumPackedInputChannels, dilationX); + inputLocation.x = mad24(rfColumn, dilatedStepX, startXForChannel); +#else + inputLocation.x = mad24(rfColumn, totalNumPackedInputChannels, startXForChannel); +#endif + float4 inputValues[NUM_OUTPUTS]; + for (short i = 0; i < NUM_OUTPUTS; ++i) { + inputValues[i] = read_imagef(input, smp, inputLocation); + inputLocation.x += strideWithChannels; + } + for (short i = 0; i < NUM_OUTPUTS; ++i) { + float4 curOutputValues = outputValues[i]; + curOutputValues.x += inputValues[i].x * weightValues[0].x; + curOutputValues.x += inputValues[i].y * weightValues[0].y; + curOutputValues.x += inputValues[i].z * weightValues[0].z; + curOutputValues.x += inputValues[i].w * weightValues[0].w; + curOutputValues.y += inputValues[i].x * weightValues[1].x; + curOutputValues.y += inputValues[i].y * weightValues[1].y; + curOutputValues.y += inputValues[i].z * weightValues[1].z; + curOutputValues.y += inputValues[i].w * weightValues[1].w; + curOutputValues.z += inputValues[i].x * weightValues[2].x; + curOutputValues.z += inputValues[i].y * weightValues[2].y; + curOutputValues.z += inputValues[i].z * weightValues[2].z; + curOutputValues.z += inputValues[i].w * weightValues[2].w; + curOutputValues.w += inputValues[i].x * weightValues[3].x; + curOutputValues.w += inputValues[i].y * weightValues[3].y; + curOutputValues.w += inputValues[i].z * weightValues[3].z; + curOutputValues.w += inputValues[i].w * weightValues[3].w; + outputValues[i] = curOutputValues; + } + } + } +#ifdef SUPPORT_DILATION + inputLocation.y += dilationY; +#else + ++inputLocation.y; +#endif + } + packedOutputChannel += packedOuputChannelOffset; +#endif + + // bias + short outputChannel = mul24(packedOutputChannel, 4); + float4 biasValues = vload4(0, biases + outputChannel); + for (short i = 0; i < NUM_OUTPUTS; ++i) { + outputValues[i] += biasValues; + } + +#ifdef SUPPORT_ACCUMULATION + // accumulate + if (doAccumulate) { + int2 outputLocation; + short outputColumn = startOutputColumn; + outputLocation.y = outputRow; + for (short i = 0; i < NUM_OUTPUTS; ++i) { + outputLocation.x = mad24(outputColumn, totalNumPackedOutputChannels, packedOutputChannel); + if (outputColumn < numOutputColumns) { + outputValues[i] += read_imagef(accumulator, smp, outputLocation); + } + ++outputColumn; + } + } +#endif + + // activation + switch (neuron) { + case 1: + for (short i = 0; i < NUM_OUTPUTS; ++i) { + outputValues[i] = max(outputValues[i], 0.0f); + } + break; + case 2: + for (short i = 0; i < NUM_OUTPUTS; ++i) { + outputValues[i] = a * tanh(b * outputValues[i]); + } + break; + case 3: + for (short i = 0; i < NUM_OUTPUTS; ++i) { + outputValues[i] = native_recip(1.0f + native_exp(-a * outputValues[i] + b)); + } + break; + case 4: + for (short i = 0; i < NUM_OUTPUTS; ++i) { + outputValues[i] = max(outputValues[i], min_clamp); + outputValues[i] = min(outputValues[i], max_clamp); + } + break; + case 5: + for (short i = 0; i < NUM_OUTPUTS; ++i) { + outputValues[i] = max(outputValues[i], 0.0f) + a * (native_exp(min(outputValues[i], 0.0f)) - 1.0f); + } + break; + } + + // output + int2 outputLocation; + short outputColumn = startOutputColumn; + outputLocation.y = outputRow; + for (short i = 0; i < NUM_OUTPUTS; ++i) { + outputLocation.x = mad24(outputColumn, totalNumPackedOutputChannels, packedOutputChannel); + if (outputColumn < numOutputColumns) { + write_imagef(output, outputLocation, outputValues[i]); + } + ++outputColumn; + } +} diff --git a/selfdrive/hybrid_modeld/thneed/kernels/convolution_horizontal_reduced_reads.cl b/selfdrive/hybrid_modeld/thneed/kernels/convolution_horizontal_reduced_reads.cl new file mode 100644 index 000000000..fcea88ce9 --- /dev/null +++ b/selfdrive/hybrid_modeld/thneed/kernels/convolution_horizontal_reduced_reads.cl @@ -0,0 +1,3 @@ +#define SUPPORT_DILATION + +__kernel void convolution_horizontal_reduced_reads( diff --git a/selfdrive/hybrid_modeld/thneed/kernels/convolution_horizontal_reduced_reads_1x1.cl b/selfdrive/hybrid_modeld/thneed/kernels/convolution_horizontal_reduced_reads_1x1.cl new file mode 100644 index 000000000..0d15d8058 --- /dev/null +++ b/selfdrive/hybrid_modeld/thneed/kernels/convolution_horizontal_reduced_reads_1x1.cl @@ -0,0 +1,4 @@ +#define ONLY_1X1_CONV +#define SUPPORT_ACCUMULATION + +__kernel void convolution_horizontal_reduced_reads_1x1( diff --git a/selfdrive/hybrid_modeld/thneed/kernels/convolution_horizontal_reduced_reads_5_outputs.cl b/selfdrive/hybrid_modeld/thneed/kernels/convolution_horizontal_reduced_reads_5_outputs.cl new file mode 100644 index 000000000..69421fc2a --- /dev/null +++ b/selfdrive/hybrid_modeld/thneed/kernels/convolution_horizontal_reduced_reads_5_outputs.cl @@ -0,0 +1,3 @@ +#define NUM_OUTPUTS 5 + +__kernel void convolution_horizontal_reduced_reads_5_outputs( diff --git a/selfdrive/hybrid_modeld/thneed/kernels/convolution_horizontal_reduced_reads_depthwise.cl b/selfdrive/hybrid_modeld/thneed/kernels/convolution_horizontal_reduced_reads_depthwise.cl new file mode 100644 index 000000000..50e39941d --- /dev/null +++ b/selfdrive/hybrid_modeld/thneed/kernels/convolution_horizontal_reduced_reads_depthwise.cl @@ -0,0 +1,4 @@ +#define DEPTHWISE +#define SUPPORT_DILATION + +__kernel void convolution_horizontal_reduced_reads_depthwise( diff --git a/selfdrive/hybrid_modeld/thneed/kernels/convolution_horizontal_reduced_reads_depthwise_stride_1.cl b/selfdrive/hybrid_modeld/thneed/kernels/convolution_horizontal_reduced_reads_depthwise_stride_1.cl new file mode 100644 index 000000000..b347cb6c7 --- /dev/null +++ b/selfdrive/hybrid_modeld/thneed/kernels/convolution_horizontal_reduced_reads_depthwise_stride_1.cl @@ -0,0 +1,3 @@ +#define DEPTHWISE + +__kernel void convolution_horizontal_reduced_reads_depthwise_stride_1( diff --git a/selfdrive/hybrid_modeld/thneed/lib.py b/selfdrive/hybrid_modeld/thneed/lib.py new file mode 100644 index 000000000..dccdfb10a --- /dev/null +++ b/selfdrive/hybrid_modeld/thneed/lib.py @@ -0,0 +1,31 @@ +import struct, json + +def load_thneed(fn): + with open(fn, "rb") as f: + json_len = struct.unpack("I", f.read(4))[0] + jdat = json.loads(f.read(json_len).decode('latin_1')) + weights = f.read() + ptr = 0 + for o in jdat['objects']: + if o['needs_load']: + nptr = ptr + o['size'] + o['data'] = weights[ptr:nptr] + ptr = nptr + for o in jdat['binaries']: + nptr = ptr + o['length'] + o['data'] = weights[ptr:nptr] + ptr = nptr + return jdat + +def save_thneed(jdat, fn): + new_weights = [] + for o in jdat['objects'] + jdat['binaries']: + if 'data' in o: + new_weights.append(o['data']) + del o['data'] + new_weights = b''.join(new_weights) + with open(fn, "wb") as f: + j = json.dumps(jdat, ensure_ascii=False).encode('latin_1') + f.write(struct.pack("I", len(j))) + f.write(j) + f.write(new_weights) diff --git a/selfdrive/hybrid_modeld/thneed/thneed.h b/selfdrive/hybrid_modeld/thneed/thneed.h new file mode 100644 index 000000000..ba50f6479 --- /dev/null +++ b/selfdrive/hybrid_modeld/thneed/thneed.h @@ -0,0 +1,132 @@ +#pragma once + +#ifndef __user +#define __user __attribute__(()) +#endif + +#include +#include +#include +#include +#include + +#include + +#include "msm_kgsl.h" + +using namespace std; + +namespace json11 { + class Json; +} +class Thneed; + +class GPUMalloc { + public: + GPUMalloc(int size, int fd); + ~GPUMalloc(); + void *alloc(int size); + private: + uint64_t base; + int remaining; +}; + +class CLQueuedKernel { + public: + CLQueuedKernel(Thneed *lthneed) { thneed = lthneed; } + CLQueuedKernel(Thneed *lthneed, + cl_kernel _kernel, + cl_uint _work_dim, + const size_t *_global_work_size, + const size_t *_local_work_size); + cl_int exec(); + uint64_t benchmark(); + void debug_print(bool verbose); + int get_arg_num(const char *search_arg_name); + cl_program program; + string name; + cl_uint num_args; + vector arg_names; + vector arg_types; + vector args; + vector args_size; + cl_kernel kernel = NULL; + json11::Json to_json() const; + + cl_uint work_dim; + size_t global_work_size[3] = {0}; + size_t local_work_size[3] = {0}; + private: + Thneed *thneed; +}; + +class CachedIoctl { + public: + virtual void exec() {} +}; + +class CachedSync: public CachedIoctl { + public: + CachedSync(Thneed *lthneed, string ldata) { thneed = lthneed; data = ldata; } + void exec(); + private: + Thneed *thneed; + string data; +}; + +class CachedCommand: public CachedIoctl { + public: + CachedCommand(Thneed *lthneed, struct kgsl_gpu_command *cmd); + void exec(); + private: + void disassemble(int cmd_index); + struct kgsl_gpu_command cache; + unique_ptr cmds; + unique_ptr objs; + Thneed *thneed; + vector > kq; +}; + +class Thneed { + public: + Thneed(bool do_clinit=false); + void stop(); + void execute(float **finputs, float *foutput, bool slow=false); + void wait(); + int optimize(); + + vector input_clmem; + vector inputs; + vector input_sizes; + cl_mem output = NULL; + + cl_context context = NULL; + cl_command_queue command_queue; + cl_device_id device_id; + int context_id; + + // protected? + bool record; + int debug; + int timestamp; + unique_ptr ram; + vector > cmds; + int fd; + + // all CL kernels + void find_inputs_outputs(); + void copy_inputs(float **finputs); + void copy_output(float *foutput); + cl_int clexec(); + vector > kq; + + // pending CL kernels + vector > ckq; + + // loading and saving + void load(const char *filename); + void save(const char *filename, bool save_binaries=false); + private: + void clinit(); +}; + diff --git a/selfdrive/hybrid_modeld/thneed/weights_fixup.py b/selfdrive/hybrid_modeld/thneed/weights_fixup.py new file mode 100755 index 000000000..ea2536402 --- /dev/null +++ b/selfdrive/hybrid_modeld/thneed/weights_fixup.py @@ -0,0 +1,145 @@ +#!/usr/bin/env python3 +import os +import struct +import zipfile +import numpy as np +from tqdm import tqdm + +from common.basedir import BASEDIR +from selfdrive.hybrid_modeld.thneed.lib import load_thneed, save_thneed + +# this is junk code, but it doesn't have deps +def load_dlc_weights(fn): + archive = zipfile.ZipFile(fn, 'r') + dlc_params = archive.read("model.params") + + def extract(rdat): + idx = rdat.find(b"\x00\x00\x00\x09\x04\x00\x00\x00") + rdat = rdat[idx+8:] + ll = struct.unpack("I", rdat[0:4])[0] + buf = np.frombuffer(rdat[4:4+ll*4], dtype=np.float32) + rdat = rdat[4+ll*4:] + dims = struct.unpack("I", rdat[0:4])[0] + buf = buf.reshape(struct.unpack("I"*dims, rdat[4:4+dims*4])) + if len(buf.shape) == 4: + buf = np.transpose(buf, (3,2,0,1)) + return buf + + def parse(tdat): + ll = struct.unpack("I", tdat[0:4])[0] + 4 + return (None, [extract(tdat[0:]), extract(tdat[ll:])]) + + ptr = 0x20 + def r4(): + nonlocal ptr + ret = struct.unpack("I", dlc_params[ptr:ptr+4])[0] + ptr += 4 + return ret + ranges = [] + cnt = r4() + for _ in range(cnt): + o = r4() + ptr + # the header is 0xC + plen, is_4, is_2 = struct.unpack("III", dlc_params[o:o+0xC]) + assert is_4 == 4 and is_2 == 2 + ranges.append((o+0xC, o+plen+0xC)) + ranges = sorted(ranges, reverse=True) + + return [parse(dlc_params[s:e]) for s,e in ranges] + +# this won't run on device without onnx +def load_onnx_weights(fn): + import onnx + from onnx import numpy_helper + + model = onnx.load(fn) + graph = model.graph # pylint: disable=maybe-no-member + init = {x.name:x for x in graph.initializer} + + onnx_layers = [] + for node in graph.node: + #print(node.name, node.op_type, node.input, node.output) + vals = [] + for inp in node.input: + if inp in init: + vals.append(numpy_helper.to_array(init[inp])) + if len(vals) > 0: + onnx_layers.append((node.name, vals)) + return onnx_layers + +def weights_fixup(target, source_thneed, dlc): + #onnx_layers = load_onnx_weights(os.path.join(BASEDIR, "models/supercombo.onnx")) + onnx_layers = load_dlc_weights(dlc) + jdat = load_thneed(source_thneed) + + bufs = {} + for o in jdat['objects']: + bufs[o['id']] = o + + thneed_layers = [] + for k in jdat['kernels']: + #print(k['name']) + vals = [] + for a in k['args']: + if a in bufs: + o = bufs[a] + if o['needs_load'] or ('buffer_id' in o and bufs[o['buffer_id']]['needs_load']): + #print(" ", o['arg_type']) + vals.append(o) + if len(vals) > 0: + thneed_layers.append((k['name'], vals)) + + assert len(thneed_layers) == len(onnx_layers) + + # fix up weights + for tl, ol in tqdm(zip(thneed_layers, onnx_layers), total=len(thneed_layers)): + #print(tl[0], ol[0]) + assert len(tl[1]) == len(ol[1]) + for o, onnx_weight in zip(tl[1], ol[1]): + if o['arg_type'] == "image2d_t": + obuf = bufs[o['buffer_id']] + saved_weights = np.frombuffer(obuf['data'], dtype=np.float16).reshape(o['height'], o['row_pitch']//2) + + if len(onnx_weight.shape) == 4: + # convolution + oc,ic,ch,cw = onnx_weight.shape + + if 'depthwise' in tl[0]: + assert ic == 1 + weights = np.transpose(onnx_weight.reshape(oc//4,4,ch,cw), (0,2,3,1)).reshape(o['height'], o['width']*4) + else: + weights = np.transpose(onnx_weight.reshape(oc//4,4,ic//4,4,ch,cw), (0,4,2,5,1,3)).reshape(o['height'], o['width']*4) + else: + # fc_Wtx + weights = onnx_weight + + new_weights = np.zeros((o['height'], o['row_pitch']//2), dtype=np.float32) + new_weights[:, :weights.shape[1]] = weights + + # weights shouldn't be too far off + err = np.mean((saved_weights.astype(np.float32) - new_weights)**2) + assert err < 1e-3 + rerr = np.mean(np.abs((saved_weights.astype(np.float32) - new_weights)/(new_weights+1e-12))) + assert rerr < 0.5 + + # fix should improve things + fixed_err = np.mean((new_weights.astype(np.float16).astype(np.float32) - new_weights)**2) + assert (err/fixed_err) >= 1 + + #print(" ", o['size'], onnx_weight.shape, o['row_pitch'], o['width'], o['height'], "err %.2fx better" % (err/fixed_err)) + + obuf['data'] = new_weights.astype(np.float16).tobytes() + + elif o['arg_type'] == "float*": + # unconverted floats are correct + new_weights = np.zeros(o['size']//4, dtype=np.float32) + new_weights[:onnx_weight.shape[0]] = onnx_weight + assert new_weights.tobytes() == o['data'] + #print(" ", o['size'], onnx_weight.shape) + + save_thneed(jdat, target) + +if __name__ == "__main__": + weights_fixup(os.path.join(BASEDIR, "models/supercombo_fixed.thneed"), + os.path.join(BASEDIR, "models/supercombo.thneed"), + os.path.join(BASEDIR, "models/supercombo.dlc")) diff --git a/selfdrive/hybrid_modeld/transforms/loadyuv.cl b/selfdrive/hybrid_modeld/transforms/loadyuv.cl new file mode 100644 index 000000000..7dd3d973a --- /dev/null +++ b/selfdrive/hybrid_modeld/transforms/loadyuv.cl @@ -0,0 +1,47 @@ +#define UV_SIZE ((TRANSFORMED_WIDTH/2)*(TRANSFORMED_HEIGHT/2)) + +__kernel void loadys(__global uchar8 const * const Y, + __global float * out, + int out_offset) +{ + const int gid = get_global_id(0); + const int ois = gid * 8; + const int oy = ois / TRANSFORMED_WIDTH; + const int ox = ois % TRANSFORMED_WIDTH; + + const uchar8 ys = Y[gid]; + const float8 ysf = convert_float8(ys); + + // 02 + // 13 + + __global float* outy0; + __global float* outy1; + if ((oy & 1) == 0) { + outy0 = out + out_offset; //y0 + outy1 = out + out_offset + UV_SIZE*2; //y2 + } else { + outy0 = out + out_offset + UV_SIZE; //y1 + outy1 = out + out_offset + UV_SIZE*3; //y3 + } + + vstore4(ysf.s0246, 0, outy0 + (oy/2) * (TRANSFORMED_WIDTH/2) + ox/2); + vstore4(ysf.s1357, 0, outy1 + (oy/2) * (TRANSFORMED_WIDTH/2) + ox/2); +} + +__kernel void loaduv(__global uchar8 const * const in, + __global float8 * out, + int out_offset) +{ + const int gid = get_global_id(0); + const uchar8 inv = in[gid]; + const float8 outv = convert_float8(inv); + out[gid + out_offset / 8] = outv; +} + +__kernel void copy(__global float8 * inout, + int in_offset) +{ + const int gid = get_global_id(0); + inout[gid] = inout[gid + in_offset / 8]; +} diff --git a/selfdrive/hybrid_modeld/transforms/loadyuv.h b/selfdrive/hybrid_modeld/transforms/loadyuv.h new file mode 100644 index 000000000..7d27ef5d4 --- /dev/null +++ b/selfdrive/hybrid_modeld/transforms/loadyuv.h @@ -0,0 +1,16 @@ +#pragma once + +#include "common/clutil.h" + +typedef struct { + int width, height; + cl_kernel loadys_krnl, loaduv_krnl, copy_krnl; +} LoadYUVState; + +void loadyuv_init(LoadYUVState* s, cl_context ctx, cl_device_id device_id, int width, int height); + +void loadyuv_destroy(LoadYUVState* s); + +void loadyuv_queue(LoadYUVState* s, cl_command_queue q, + cl_mem y_cl, cl_mem u_cl, cl_mem v_cl, + cl_mem out_cl, bool do_shift = false); diff --git a/selfdrive/hybrid_modeld/transforms/transform.cl b/selfdrive/hybrid_modeld/transforms/transform.cl new file mode 100644 index 000000000..8ad186935 --- /dev/null +++ b/selfdrive/hybrid_modeld/transforms/transform.cl @@ -0,0 +1,54 @@ +#define INTER_BITS 5 +#define INTER_TAB_SIZE (1 << INTER_BITS) +#define INTER_SCALE 1.f / INTER_TAB_SIZE + +#define INTER_REMAP_COEF_BITS 15 +#define INTER_REMAP_COEF_SCALE (1 << INTER_REMAP_COEF_BITS) + +__kernel void warpPerspective(__global const uchar * src, + int src_step, int src_offset, int src_rows, int src_cols, + __global uchar * dst, + int dst_step, int dst_offset, int dst_rows, int dst_cols, + __constant float * M) +{ + int dx = get_global_id(0); + int dy = get_global_id(1); + + if (dx < dst_cols && dy < dst_rows) + { + float X0 = M[0] * dx + M[1] * dy + M[2]; + float Y0 = M[3] * dx + M[4] * dy + M[5]; + float W = M[6] * dx + M[7] * dy + M[8]; + W = W != 0.0f ? INTER_TAB_SIZE / W : 0.0f; + int X = rint(X0 * W), Y = rint(Y0 * W); + + short sx = convert_short_sat(X >> INTER_BITS); + short sy = convert_short_sat(Y >> INTER_BITS); + short ay = (short)(Y & (INTER_TAB_SIZE - 1)); + short ax = (short)(X & (INTER_TAB_SIZE - 1)); + + int v0 = (sx >= 0 && sx < src_cols && sy >= 0 && sy < src_rows) ? + convert_int(src[mad24(sy, src_step, src_offset + sx)]) : 0; + int v1 = (sx+1 >= 0 && sx+1 < src_cols && sy >= 0 && sy < src_rows) ? + convert_int(src[mad24(sy, src_step, src_offset + (sx+1))]) : 0; + int v2 = (sx >= 0 && sx < src_cols && sy+1 >= 0 && sy+1 < src_rows) ? + convert_int(src[mad24(sy+1, src_step, src_offset + sx)]) : 0; + int v3 = (sx+1 >= 0 && sx+1 < src_cols && sy+1 >= 0 && sy+1 < src_rows) ? + convert_int(src[mad24(sy+1, src_step, src_offset + (sx+1))]) : 0; + + float taby = 1.f/INTER_TAB_SIZE*ay; + float tabx = 1.f/INTER_TAB_SIZE*ax; + + int dst_index = mad24(dy, dst_step, dst_offset + dx); + + int itab0 = convert_short_sat_rte( (1.0f-taby)*(1.0f-tabx) * INTER_REMAP_COEF_SCALE ); + int itab1 = convert_short_sat_rte( (1.0f-taby)*tabx * INTER_REMAP_COEF_SCALE ); + int itab2 = convert_short_sat_rte( taby*(1.0f-tabx) * INTER_REMAP_COEF_SCALE ); + int itab3 = convert_short_sat_rte( taby*tabx * INTER_REMAP_COEF_SCALE ); + + int val = v0 * itab0 + v1 * itab1 + v2 * itab2 + v3 * itab3; + + uchar pix = convert_uchar_sat((val + (1 << (INTER_REMAP_COEF_BITS-1))) >> INTER_REMAP_COEF_BITS); + dst[dst_index] = pix; + } +} diff --git a/selfdrive/hybrid_modeld/transforms/transform.h b/selfdrive/hybrid_modeld/transforms/transform.h new file mode 100644 index 000000000..a1629a517 --- /dev/null +++ b/selfdrive/hybrid_modeld/transforms/transform.h @@ -0,0 +1,25 @@ +#pragma once + +#define CL_USE_DEPRECATED_OPENCL_1_2_APIS +#ifdef __APPLE__ +#include +#else +#include +#endif + +#include "common/mat.h" + +typedef struct { + cl_kernel krnl; + cl_mem m_y_cl, m_uv_cl; +} Transform; + +void transform_init(Transform* s, cl_context ctx, cl_device_id device_id); + +void transform_destroy(Transform* transform); + +void transform_queue(Transform* s, cl_command_queue q, + cl_mem yuv, int in_width, int in_height, + cl_mem out_y, cl_mem out_u, cl_mem out_v, + int out_width, int out_height, + const mat3& projection); diff --git a/selfdrive/legacy_modeld/_modeld b/selfdrive/legacy_modeld/_modeld index e03a3b685..ea5062df0 100755 Binary files a/selfdrive/legacy_modeld/_modeld and b/selfdrive/legacy_modeld/_modeld differ diff --git a/selfdrive/legacy_modeld/models/driving.h b/selfdrive/legacy_modeld/models/driving.h index f53698f5a..7cffa0e00 100644 --- a/selfdrive/legacy_modeld/models/driving.h +++ b/selfdrive/legacy_modeld/models/driving.h @@ -11,16 +11,14 @@ #include "cereal/messaging/messaging.h" #include "cereal/visionipc/visionipc_client.h" #include "common/mat.h" -#include "common/legacy_modeldata.h" +#include "common/hybrid_modeldata.h" #include "common/util.h" #include "selfdrive/legacy_modeld/models/commonmodel.h" -#include "selfdrive/legacy_modeld/models/nav.h" #include "selfdrive/legacy_modeld/runners/run.h" constexpr int DESIRE_LEN = 8; constexpr int DESIRE_PRED_LEN = 4; constexpr int TRAFFIC_CONVENTION_LEN = 2; -constexpr int DRIVING_STYLE_LEN = 12; constexpr int MODEL_FREQ = 20; constexpr int DISENGAGE_LEN = 5; @@ -28,7 +26,6 @@ constexpr int BLINKER_LEN = 6; constexpr int META_STRIDE = 7; constexpr int PLAN_MHP_N = 5; -constexpr int STOP_LINE_MHP_N = 3; constexpr int LEAD_MHP_N = 2; constexpr int LEAD_TRAJ_LEN = 6; @@ -151,37 +148,6 @@ struct ModelOutputLeads { }; static_assert(sizeof(ModelOutputLeads) == (sizeof(ModelOutputLeadPrediction)*LEAD_MHP_N) + (sizeof(float)*LEAD_MHP_SELECTION)); -struct ModelOutputStopLineElement { - ModelOutputXYZ position; - ModelOutputXYZ rotation; - float speed; - float time; -}; -static_assert(sizeof(ModelOutputStopLineElement) == (sizeof(ModelOutputXYZ)*2 + sizeof(float)*2)); - -struct ModelOutputStopLinePrediction { - ModelOutputStopLineElement mean; - ModelOutputStopLineElement std; - float prob; -}; -static_assert(sizeof(ModelOutputStopLinePrediction) == (sizeof(ModelOutputStopLineElement)*2 + sizeof(float))); - -struct ModelOutputStopLines { - std::array prediction; - float prob; - - constexpr const ModelOutputStopLinePrediction &get_best_prediction(int t_idx) const { - int max_idx = 0; - for (int i = 1; i < prediction.size(); i++) { - if (prediction[i].prob > prediction[max_idx].prob) { - max_idx = i; - } - } - return prediction[max_idx]; - } -}; -static_assert(sizeof(ModelOutputStopLines) == (sizeof(ModelOutputStopLinePrediction)*STOP_LINE_MHP_N) + sizeof(float)); - struct ModelOutputPose { ModelOutputXYZ velocity_mean; ModelOutputXYZ rotation_mean; @@ -240,7 +206,6 @@ struct ModelOutput { const ModelOutputLaneLines lane_lines; const ModelOutputRoadEdges road_edges; const ModelOutputLeads leads; - const ModelOutputStopLines stop_lines; const ModelOutputMeta meta; const ModelOutputPose pose; }; @@ -255,8 +220,7 @@ constexpr int NET_OUTPUT_SIZE = OUTPUT_SIZE + TEMPORAL_SIZE; // TODO: convert remaining arrays to std::array and update model runners struct ModelState { - ModelFrame *frame = nullptr; - ModelFrame *wide_frame = nullptr; + ModelFrame *frame; std::array output = {}; std::unique_ptr m; #ifdef DESIRE @@ -266,17 +230,11 @@ struct ModelState { #ifdef TRAFFIC_CONVENTION float traffic_convention[TRAFFIC_CONVENTION_LEN] = {}; #endif -#ifdef DRIVING_STYLE - float driving_style[DRIVING_STYLE_LEN] = {}; -#endif -#ifdef NAV - float nav_features[NAV_FEATURE_LEN] = {}; -#endif }; void model_init(ModelState* s, cl_device_id device_id, cl_context context); -ModelOutput *model_eval_frame(ModelState* s, VisionBuf* buf, VisionBuf* buf_wide, - const mat3 &transform, const mat3 &transform_wide, float *desire_in, bool is_rhd, float *driving_style, float *nav_features, bool prepare_only); +ModelOutput *model_eval_frame(ModelState* s, VisionBuf* buf, + const mat3 &transform, float *desire_in, bool is_rhd, bool prepare_only); void model_free(ModelState* s); void model_publish(PubMaster &pm, uint32_t vipc_frame_id, uint32_t vipc_frame_id_extra, uint32_t frame_id, float frame_drop, const ModelOutput &net_outputs, uint64_t timestamp_eof, diff --git a/selfdrive/legacy_modeld/models/supercombo.thneed b/selfdrive/legacy_modeld/models/supercombo.thneed index a66c126d3..2b02a7eab 100644 Binary files a/selfdrive/legacy_modeld/models/supercombo.thneed and b/selfdrive/legacy_modeld/models/supercombo.thneed differ diff --git a/selfdrive/locationd/calibrationd.py b/selfdrive/locationd/calibrationd.py index dc4ffbb1f..c9d9340a2 100755 --- a/selfdrive/locationd/calibrationd.py +++ b/selfdrive/locationd/calibrationd.py @@ -24,6 +24,8 @@ MIN_SPEED_FILTER = 15 * CV.MPH_TO_MS MAX_VEL_ANGLE_STD = np.radians(0.25) MAX_YAW_RATE_FILTER = np.radians(2) # per second +MAX_HEIGHT_STD = np.exp(-3.5) + # This is at model frequency, blocks needed for efficiency SMOOTH_CYCLES = 10 BLOCK_SIZE = 100 @@ -32,9 +34,10 @@ INPUTS_WANTED = 50 # We want a little bit more than we need for stability MAX_ALLOWED_SPREAD = np.radians(2) RPY_INIT = np.array([0.0,0.0,0.0]) WIDE_FROM_DEVICE_EULER_INIT = np.array([0.0, 0.0, 0.0]) +HEIGHT_INIT = np.array([1.22]) -# These values are needed to accommodate biggest modelframe -PITCH_LIMITS = np.array([-0.09074112085129739, 0.14907572052989657]) +# These values are needed to accommodate the model frame in the narrow cam of the C3 +PITCH_LIMITS = np.array([-0.09074112085129739, 0.17]) YAW_LIMITS = np.array([-0.06912048084718224, 0.06912048084718235]) DEBUG = os.getenv("DEBUG") is not None @@ -50,6 +53,8 @@ def sanity_clip(rpy: np.ndarray) -> np.ndarray: np.clip(rpy[1], PITCH_LIMITS[0] - .005, PITCH_LIMITS[1] + .005), np.clip(rpy[2], YAW_LIMITS[0] - .005, YAW_LIMITS[1] + .005)]) +def moving_avg_with_linear_decay(prev_mean: np.ndarray, new_val: np.ndarray, idx: int, block_size: float) -> np.ndarray: + return (idx*prev_mean + (block_size - idx) * new_val) / block_size class Calibrator: def __init__(self, param_put: bool = False): @@ -62,6 +67,7 @@ class Calibrator: calibration_params = params.get("CalibrationParams") rpy_init = RPY_INIT wide_from_device_euler = WIDE_FROM_DEVICE_EULER_INIT + height = HEIGHT_INIT valid_blocks = 0 self.cal_status = log.LiveCalibrationData.Status.uncalibrated @@ -71,21 +77,28 @@ class Calibrator: rpy_init = np.array(msg.liveCalibration.rpyCalib) valid_blocks = msg.liveCalibration.validBlocks wide_from_device_euler = np.array(msg.liveCalibration.wideFromDeviceEuler) + height = np.array(msg.liveCalibration.height) except Exception: cloudlog.exception("Error reading cached CalibrationParams") - self.reset(rpy_init, valid_blocks, wide_from_device_euler) + self.reset(rpy_init, valid_blocks, wide_from_device_euler, height) self.update_status() def reset(self, rpy_init: np.ndarray = RPY_INIT, valid_blocks: int = 0, wide_from_device_euler_init: np.ndarray = WIDE_FROM_DEVICE_EULER_INIT, + height_init: np.ndarray = HEIGHT_INIT, smooth_from: Optional[np.ndarray] = None) -> None: if not np.isfinite(rpy_init).all(): self.rpy = RPY_INIT.copy() else: self.rpy = rpy_init.copy() + if not np.isfinite(height_init).all() or len(height_init) != 1: + self.height = HEIGHT_INIT.copy() + else: + self.height = height_init.copy() + if not np.isfinite(wide_from_device_euler_init).all() or len(wide_from_device_euler_init) != 3: self.wide_from_device_euler = WIDE_FROM_DEVICE_EULER_INIT.copy() else: @@ -98,6 +111,7 @@ class Calibrator: self.rpys = np.tile(self.rpy, (INPUTS_WANTED, 1)) self.wide_from_device_eulers = np.tile(self.wide_from_device_euler, (INPUTS_WANTED, 1)) + self.heights = np.tile(self.height, (INPUTS_WANTED, 1)) self.idx = 0 self.block_idx = 0 @@ -120,6 +134,7 @@ class Calibrator: valid_idxs = self.get_valid_idxs() if valid_idxs: self.wide_from_device_euler = np.mean(self.wide_from_device_eulers[valid_idxs], axis=0) + self.height = np.mean(self.heights[valid_idxs], axis=0) rpys = self.rpys[valid_idxs] self.rpy = np.mean(rpys, axis=0) max_rpy_calib = np.array(np.max(rpys, axis=0)) @@ -140,6 +155,7 @@ class Calibrator: # If spread is too high, assume mounting was changed and reset to last block. # Make the transition smooth. Abrupt transitions are not good for feedback loop through supercombo model. + # TODO: add height spread check with smooth transition too if max(self.calib_spread) > MAX_ALLOWED_SPREAD and self.cal_status == log.LiveCalibrationData.Status.calibrated: self.reset(self.rpys[self.block_idx - 1], valid_blocks=1, smooth_from=self.rpy) self.cal_status = log.LiveCalibrationData.Status.recalibrating @@ -160,13 +176,21 @@ class Calibrator: def handle_cam_odom(self, trans: List[float], rot: List[float], wide_from_device_euler: List[float], - trans_std: List[float]) -> Optional[np.ndarray]: + trans_std: List[float], + road_transform_trans: List[float], + road_transform_trans_std: List[float]) -> Optional[np.ndarray]: self.old_rpy_weight = max(0.0, self.old_rpy_weight - 1/SMOOTH_CYCLES) straight_and_fast = ((self.v_ego > MIN_SPEED_FILTER) and (trans[0] > MIN_SPEED_FILTER) and (abs(rot[2]) < MAX_YAW_RATE_FILTER)) angle_std_threshold = MAX_VEL_ANGLE_STD - certain_if_calib = ((np.arctan2(trans_std[1], trans[0]) < angle_std_threshold) or - (self.valid_blocks < INPUTS_NEEDED)) + height_std_threshold = MAX_HEIGHT_STD + rpy_certain = np.arctan2(trans_std[1], trans[0]) < angle_std_threshold + if len(road_transform_trans_std) == 3: + height_certain = road_transform_trans_std[2] < height_std_threshold + else: + height_certain = True + + certain_if_calib = (rpy_certain and height_certain) or (self.valid_blocks < INPUTS_NEEDED) if not (straight_and_fast and certain_if_calib): return None @@ -180,10 +204,16 @@ class Calibrator: new_wide_from_device_euler = np.array(wide_from_device_euler) else: new_wide_from_device_euler = WIDE_FROM_DEVICE_EULER_INIT - self.rpys[self.block_idx] = (self.idx*self.rpys[self.block_idx] + - (BLOCK_SIZE - self.idx) * new_rpy) / float(BLOCK_SIZE) - self.wide_from_device_eulers[self.block_idx] = (self.idx*self.wide_from_device_eulers[self.block_idx] + - (BLOCK_SIZE - self.idx) * new_wide_from_device_euler) / float(BLOCK_SIZE) + + if (len(road_transform_trans) == 3): + new_height = np.array([road_transform_trans[2]]) + else: + new_height = HEIGHT_INIT + + self.rpys[self.block_idx] = moving_avg_with_linear_decay(self.rpys[self.block_idx], new_rpy, self.idx, float(BLOCK_SIZE)) + self.wide_from_device_eulers[self.block_idx] = moving_avg_with_linear_decay(self.wide_from_device_eulers[self.block_idx], new_wide_from_device_euler, self.idx, float(BLOCK_SIZE)) + self.heights[self.block_idx] = moving_avg_with_linear_decay(self.heights[self.block_idx], new_height, self.idx, float(BLOCK_SIZE)) + self.idx = (self.idx + 1) % BLOCK_SIZE if self.idx == 0: self.block_idx += 1 @@ -206,6 +236,7 @@ class Calibrator: liveCalibration.rpyCalib = smooth_rpy.tolist() liveCalibration.rpyCalibSpread = self.calib_spread.tolist() liveCalibration.wideFromDeviceEuler = self.wide_from_device_euler.tolist() + liveCalibration.height = self.height.tolist() if self.not_car: liveCalibration.validBlocks = INPUTS_NEEDED @@ -243,7 +274,9 @@ def calibrationd_thread(sm: Optional[messaging.SubMaster] = None, pm: Optional[m new_rpy = calibrator.handle_cam_odom(sm['cameraOdometry'].trans, sm['cameraOdometry'].rot, sm['cameraOdometry'].wideFromDeviceEuler, - sm['cameraOdometry'].transStd) + sm['cameraOdometry'].transStd, + sm['cameraOdometry'].roadTransformTrans, + sm['cameraOdometry'].roadTransformTransStd) if DEBUG and new_rpy is not None: print('got new rpy', new_rpy) diff --git a/selfdrive/locationd/locationd b/selfdrive/locationd/locationd index 96a51ac51..5cd556688 100755 Binary files a/selfdrive/locationd/locationd and b/selfdrive/locationd/locationd differ diff --git a/selfdrive/locationd/models/generated/car.h b/selfdrive/locationd/models/generated/car.h index 703d79316..860f0bf09 100644 --- a/selfdrive/locationd/models/generated/car.h +++ b/selfdrive/locationd/models/generated/car.h @@ -9,27 +9,27 @@ void car_update_27(double *in_x, double *in_P, double *in_z, double *in_R, doubl void car_update_29(double *in_x, double *in_P, double *in_z, double *in_R, double *in_ea); void car_update_28(double *in_x, double *in_P, double *in_z, double *in_R, double *in_ea); void car_update_31(double *in_x, double *in_P, double *in_z, double *in_R, double *in_ea); -void car_err_fun(double *nom_x, double *delta_x, double *out_1673219004331321340); -void car_inv_err_fun(double *nom_x, double *true_x, double *out_7685300362947227633); -void car_H_mod_fun(double *state, double *out_7906035710387220062); -void car_f_fun(double *state, double dt, double *out_4520596976096901018); -void car_F_fun(double *state, double dt, double *out_3330787842923361570); -void car_h_25(double *state, double *unused, double *out_2485841073551241221); -void car_H_25(double *state, double *unused, double *out_5566715376639757475); -void car_h_24(double *state, double *unused, double *out_9107689886717443190); -void car_H_24(double *state, double *unused, double *out_3651963511000598916); -void car_h_30(double *state, double *unused, double *out_2210647011266735332); -void car_H_30(double *state, double *unused, double *out_1039019046512149277); -void car_h_26(double *state, double *unused, double *out_3131220224619295231); -void car_H_26(double *state, double *unused, double *out_1825212057765701251); -void car_h_27(double *state, double *unused, double *out_8884882590965090367); -void car_H_27(double *state, double *unused, double *out_3262613117696092494); -void car_h_29(double *state, double *unused, double *out_5247433062711068017); -void car_H_29(double *state, double *unused, double *out_1549250390826541461); -void car_h_28(double *state, double *unused, double *out_88573126699093227); -void car_H_28(double *state, double *unused, double *out_3533148626242989113); -void car_h_31(double *state, double *unused, double *out_6010819607712498771); -void car_H_31(double *state, double *unused, double *out_1199003955532349775); +void car_err_fun(double *nom_x, double *delta_x, double *out_36399703031354541); +void car_inv_err_fun(double *nom_x, double *true_x, double *out_1481941880078478546); +void car_H_mod_fun(double *state, double *out_782698275337075542); +void car_f_fun(double *state, double dt, double *out_1178114905851405491); +void car_F_fun(double *state, double dt, double *out_6287201840608329987); +void car_h_25(double *state, double *unused, double *out_6173327891092993483); +void car_H_25(double *state, double *unused, double *out_7500800954454960250); +void car_h_24(double *state, double *unused, double *out_8736520822803851319); +void car_H_24(double *state, double *unused, double *out_3503268526265875046); +void car_h_30(double *state, double *unused, double *out_6448521953377499372); +void car_H_30(double *state, double *unused, double *out_7630139901598200320); +void car_h_26(double *state, double *unused, double *out_346002517644304982); +void car_H_26(double *state, double *unused, double *out_7204439800380535142); +void car_h_27(double *state, double *unused, double *out_7271742914955712488); +void car_H_27(double *state, double *unused, double *out_8641840860310926385); +void car_h_29(double *state, double *unused, double *out_3411735901933166687); +void car_H_29(double *state, double *unused, double *out_6928478133441375352); +void car_h_28(double *state, double *unused, double *out_1446094004912979838); +void car_H_28(double *state, double *unused, double *out_1846079116371844778); +void car_h_31(double *state, double *unused, double *out_7623959283721876650); +void car_H_31(double *state, double *unused, double *out_7470154992577999822); void car_predict(double *in_x, double *in_P, double *in_Q, double dt); void car_set_mass(double x); void car_set_rotational_inertia(double x); diff --git a/selfdrive/locationd/models/generated/gnss.h b/selfdrive/locationd/models/generated/gnss.h index 56fe6ccc5..8374e602b 100644 --- a/selfdrive/locationd/models/generated/gnss.h +++ b/selfdrive/locationd/models/generated/gnss.h @@ -5,18 +5,18 @@ void gnss_update_6(double *in_x, double *in_P, double *in_z, double *in_R, doubl void gnss_update_20(double *in_x, double *in_P, double *in_z, double *in_R, double *in_ea); void gnss_update_7(double *in_x, double *in_P, double *in_z, double *in_R, double *in_ea); void gnss_update_21(double *in_x, double *in_P, double *in_z, double *in_R, double *in_ea); -void gnss_err_fun(double *nom_x, double *delta_x, double *out_8821442694437018176); -void gnss_inv_err_fun(double *nom_x, double *true_x, double *out_2669404392972591154); -void gnss_H_mod_fun(double *state, double *out_3628383119241527248); -void gnss_f_fun(double *state, double dt, double *out_2911894030962606432); -void gnss_F_fun(double *state, double dt, double *out_8657587500259786555); -void gnss_h_6(double *state, double *sat_pos, double *out_1940478305202382078); -void gnss_H_6(double *state, double *sat_pos, double *out_5861605845652759501); -void gnss_h_20(double *state, double *sat_pos, double *out_3198189178613923928); -void gnss_H_20(double *state, double *sat_pos, double *out_3468662543713286228); -void gnss_h_7(double *state, double *sat_pos_vel, double *out_2083846631906470419); -void gnss_H_7(double *state, double *sat_pos_vel, double *out_227112697527214299); -void gnss_h_21(double *state, double *sat_pos_vel, double *out_2083846631906470419); -void gnss_H_21(double *state, double *sat_pos_vel, double *out_227112697527214299); +void gnss_err_fun(double *nom_x, double *delta_x, double *out_920563397091966765); +void gnss_inv_err_fun(double *nom_x, double *true_x, double *out_9207898715518151756); +void gnss_H_mod_fun(double *state, double *out_8858641397583587342); +void gnss_f_fun(double *state, double dt, double *out_8883459878223034240); +void gnss_F_fun(double *state, double dt, double *out_5523223948218415506); +void gnss_h_6(double *state, double *sat_pos, double *out_580488090984610716); +void gnss_H_6(double *state, double *sat_pos, double *out_930024451989407159); +void gnss_h_20(double *state, double *sat_pos, double *out_5239371180373600875); +void gnss_H_20(double *state, double *sat_pos, double *out_4343366647947653302); +void gnss_h_7(double *state, double *sat_pos_vel, double *out_1146845787665601201); +void gnss_H_7(double *state, double *sat_pos_vel, double *out_913804198191897829); +void gnss_h_21(double *state, double *sat_pos_vel, double *out_1146845787665601201); +void gnss_H_21(double *state, double *sat_pos_vel, double *out_913804198191897829); void gnss_predict(double *in_x, double *in_P, double *in_Q, double dt); } \ No newline at end of file diff --git a/selfdrive/locationd/models/generated/libkf.so b/selfdrive/locationd/models/generated/libkf.so index 29c820dbe..6ea431533 100755 Binary files a/selfdrive/locationd/models/generated/libkf.so and b/selfdrive/locationd/models/generated/libkf.so differ diff --git a/selfdrive/locationd/models/generated/live.h b/selfdrive/locationd/models/generated/live.h index 8e4e64f47..c214a3fb4 100644 --- a/selfdrive/locationd/models/generated/live.h +++ b/selfdrive/locationd/models/generated/live.h @@ -10,29 +10,29 @@ void live_update_32(double *in_x, double *in_P, double *in_z, double *in_R, doub void live_update_13(double *in_x, double *in_P, double *in_z, double *in_R, double *in_ea); void live_update_14(double *in_x, double *in_P, double *in_z, double *in_R, double *in_ea); void live_update_33(double *in_x, double *in_P, double *in_z, double *in_R, double *in_ea); -void live_H(double *in_vec, double *out_7160508158065063740); -void live_err_fun(double *nom_x, double *delta_x, double *out_9119248387773333404); -void live_inv_err_fun(double *nom_x, double *true_x, double *out_6697903320192654180); -void live_H_mod_fun(double *state, double *out_277792456163775303); -void live_f_fun(double *state, double dt, double *out_4978833357093498698); -void live_F_fun(double *state, double dt, double *out_6849203160499840969); -void live_h_4(double *state, double *unused, double *out_416461024327247384); -void live_H_4(double *state, double *unused, double *out_3921598954962045660); -void live_h_9(double *state, double *unused, double *out_8227592735492858479); -void live_H_9(double *state, double *unused, double *out_3365619980302401810); -void live_h_10(double *state, double *unused, double *out_4296550159527316506); -void live_H_10(double *state, double *unused, double *out_4607961823255245423); -void live_h_12(double *state, double *unused, double *out_7070759218890925604); -void live_H_12(double *state, double *unused, double *out_1097857453069916135); -void live_h_35(double *state, double *unused, double *out_5090403797667048641); -void live_H_35(double *state, double *unused, double *out_554936897589438284); -void live_h_32(double *state, double *unused, double *out_6688851263345634384); -void live_H_32(double *state, double *unused, double *out_6154347310456520517); -void live_h_13(double *state, double *unused, double *out_4001286955811606869); -void live_H_13(double *state, double *unused, double *out_9160093207497218410); -void live_h_14(double *state, double *unused, double *out_8227592735492858479); -void live_H_14(double *state, double *unused, double *out_3365619980302401810); -void live_h_33(double *state, double *unused, double *out_3464094948502741824); -void live_H_33(double *state, double *unused, double *out_2595620107049419320); +void live_H(double *in_vec, double *out_4281580954174424078); +void live_err_fun(double *nom_x, double *delta_x, double *out_6020033059433352461); +void live_inv_err_fun(double *nom_x, double *true_x, double *out_8784462575491582337); +void live_H_mod_fun(double *state, double *out_3196248901070636896); +void live_f_fun(double *state, double dt, double *out_1307995963717417138); +void live_F_fun(double *state, double dt, double *out_2913954870345271558); +void live_h_4(double *state, double *unused, double *out_5227201962942267756); +void live_H_4(double *state, double *unused, double *out_8934556766272716891); +void live_h_9(double *state, double *unused, double *out_8242329221781602876); +void live_H_9(double *state, double *unused, double *out_6045695213992637549); +void live_h_10(double *state, double *unused, double *out_4525709092164084266); +void live_H_10(double *state, double *unused, double *out_6290261927440969782); +void live_h_12(double *state, double *unused, double *out_1533628872533736742); +void live_H_12(double *state, double *unused, double *out_8313457741225123224); +void live_h_35(double *state, double *unused, double *out_3219567362710053882); +void live_H_35(double *state, double *unused, double *out_5567894708900109515); +void live_h_32(double *state, double *unused, double *out_7936133118583631274); +void live_H_32(double *state, double *unused, double *out_7279438951942359868); +void live_h_13(double *state, double *unused, double *out_2282936705415825262); +void live_H_13(double *state, double *unused, double *out_2592371241030708137); +void live_h_14(double *state, double *unused, double *out_8242329221781602876); +void live_H_14(double *state, double *unused, double *out_6045695213992637549); +void live_h_33(double *state, double *unused, double *out_5915136300903460843); +void live_H_33(double *state, double *unused, double *out_2417337704261251911); void live_predict(double *in_x, double *in_P, double *in_Q, double dt); } \ No newline at end of file diff --git a/selfdrive/loggerd/bootlog b/selfdrive/loggerd/bootlog index bd4fc9cb7..d543d2565 100755 Binary files a/selfdrive/loggerd/bootlog and b/selfdrive/loggerd/bootlog differ diff --git a/selfdrive/loggerd/loggerd b/selfdrive/loggerd/loggerd index c62332667..93aeac4d4 100755 Binary files a/selfdrive/loggerd/loggerd and b/selfdrive/loggerd/loggerd differ diff --git a/selfdrive/manager/manager.py b/selfdrive/manager/manager.py index 85a9806b9..345e0fb11 100755 --- a/selfdrive/manager/manager.py +++ b/selfdrive/manager/manager.py @@ -7,6 +7,7 @@ import sys import traceback from typing import List, Tuple, Union +from cereal import log import cereal.messaging as messaging import selfdrive.sentry as sentry from common.basedir import BASEDIR @@ -34,6 +35,8 @@ def manager_init() -> None: params = Params() params.clear_all(ParamKeyType.CLEAR_ON_MANAGER_START) + params.clear_all(ParamKeyType.CLEAR_ON_ONROAD_TRANSITION) + params.clear_all(ParamKeyType.CLEAR_ON_OFFROAD_TRANSITION) default_params: List[Tuple[str, Union[str, bytes]]] = [ ("CompletedTrainingVersion", "0"), @@ -42,11 +45,23 @@ def manager_init() -> None: ("HasAcceptedTerms", "0"), ("LanguageSetting", "main_en"), ("OpenpilotEnabledToggle", "1"), + ("LongitudinalPersonality", str(log.LongitudinalPersonality.standard)), + ("DisableUpdates", "1"), ("dp_no_gps_ctrl", "0"), ("dp_no_fan_ctrl", "0"), + ("dp_logging", "0"), ("dp_alka", "1"), ("dp_mapd", "1"), ("dp_lat_lane_priority_mode", "0"), + ("dp_0813", "0"), + ("dp_device_auto_shutdown", "0"), + ("dp_device_auto_shutdown_in", "30"), + ("dp_toyota_sng", "0"), + ("dp_toyota_auto_lock", "0"), + ("dp_toyota_auto_unlock", "0"), + ("dp_device_display_off_mode", "0"), + ("dp_device_audible_alert_mode", "0"), + ("dp_device_disable_temp_check", "0"), ] if not PC: default_params.append(("LastUpdateTime", datetime.datetime.utcnow().isoformat().encode('utf8'))) @@ -138,6 +153,9 @@ def manager_thread() -> None: ignore += ["manage_athenad", "uploader"] if os.getenv("NOBOARD") is not None: ignore.append("pandad") + + if not params.get_bool("dp_logging"): + ignore += ["logcatd", "proclogd", "loggerd"] ignore += [x for x in os.getenv("BLOCK", "").split(",") if len(x) > 0] if not params.get_bool("dp_mapd") or params.get_bool("dp_no_gps_ctrl"): diff --git a/selfdrive/manager/process_config.py b/selfdrive/manager/process_config.py index e4075b990..ddc72fb12 100644 --- a/selfdrive/manager/process_config.py +++ b/selfdrive/manager/process_config.py @@ -6,7 +6,6 @@ from system.hardware import PC, TICI, EON from selfdrive.manager.process import PythonProcess, NativeProcess, DaemonProcess NO_IR_CTRL = os.path.isfile('/data/media/0/no_ir_ctrl') -log_on = os.path.isfile('/data/media/0/log_on') WEBCAM = os.getenv("USE_WEBCAM") is not None @@ -33,21 +32,24 @@ def ublox(started, params, CP: car.CarParams) -> bool: def qcomgps(started, params, CP: car.CarParams) -> bool: return started and not ublox_available() + +use_old_model = Params().get_bool("dp_0813") + procs = [ # due to qualcomm kernel bugs SIGKILLing camerad sometimes causes page table corruption NativeProcess("camerad", "selfdrive/camerad", ["./camerad"], unkillable=True, callback=driverview), NativeProcess("clocksd", "system/clocksd", ["./clocksd"]), - NativeProcess("logcatd", "system/logcatd", ["./logcatd"], enabled=log_on), - NativeProcess("proclogd", "system/proclogd", ["./proclogd"], enabled=log_on), - PythonProcess("logmessaged", "system.logmessaged", offroad=True, enabled=log_on), + NativeProcess("logcatd", "system/logcatd", ["./logcatd"]), + NativeProcess("proclogd", "system/proclogd", ["./proclogd"]), + PythonProcess("logmessaged", "system.logmessaged", offroad=True), # PythonProcess("micd", "system.micd"), # PythonProcess("timezoned", "system.timezoned", enabled=not PC, offroad=True), DaemonProcess("manage_athenad", "selfdrive.athena.manage_athenad", "AthenadPid"), - NativeProcess("dmonitoringmodeld", "selfdrive/legacy_modeld", ["./dmonitoringmodeld"], enabled=(not PC or WEBCAM) and not NO_IR_CTRL, callback=driverview), + NativeProcess("dmonitoringmodeld", "selfdrive/hybrid_modeld", ["./dmonitoringmodeld"], enabled=(not PC or WEBCAM) and not NO_IR_CTRL, callback=driverview), # NativeProcess("encoderd", "system/loggerd", ["./encoderd"]), - NativeProcess("loggerd", "selfdrive/loggerd", ["./loggerd"], onroad=False, callback=logging, enabled=log_on), - NativeProcess("modeld", "selfdrive/legacy_modeld", ["./modeld"]), + NativeProcess("loggerd", "selfdrive/loggerd", ["./loggerd"], onroad=False, callback=logging), + NativeProcess("modeld", "selfdrive/hybrid_modeld" if not use_old_model else "selfdrive/legacy_modeld", ["./modeld"]), # NativeProcess("mapsd", "selfdrive/navd", ["./map_renderer"], enabled=False), # NativeProcess("navmodeld", "selfdrive/modeld", ["./navmodeld"], enabled=False), NativeProcess("sensord", "system/sensord", ["./sensord"], enabled=not PC, offroad=True), @@ -58,7 +60,7 @@ procs = [ PythonProcess("calibrationd", "selfdrive.locationd.calibrationd"), PythonProcess("torqued", "selfdrive.locationd.torqued"), PythonProcess("controlsd", "selfdrive.controls.controlsd"), - PythonProcess("deleter", "selfdrive.loggerd.deleter", offroad=True, enabled=log_on), + PythonProcess("deleter", "selfdrive.loggerd.deleter", offroad=True), PythonProcess("dmonitoringd", "selfdrive.legacy_monitoring.dmonitoringd", enabled=(not PC or WEBCAM) and not NO_IR_CTRL, callback=driverview), # PythonProcess("laikad", "selfdrive.locationd.laikad"), # PythonProcess("rawgpsd", "system.sensord.rawgps.rawgpsd", enabled=TICI, onroad=False, callback=qcomgps), @@ -85,7 +87,7 @@ procs = [ PythonProcess("androidd", "system.hardware.eon.androidd", enabled=EON, offroad=True), # mapd - PythonProcess("mapd", "selfdrive.dragonpilot.mapd"), + PythonProcess("mapd", "selfdrive.mapd.mapd"), # gpxd PythonProcess("gpxd", "selfdrive.dragonpilot.gpxd"), PythonProcess("gpx_uploader", "selfdrive.dragonpilot.gpx_uploader", offroad=True), diff --git a/selfdrive/mapd/__init__.py b/selfdrive/mapd/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/selfdrive/mapd/config.py b/selfdrive/mapd/config.py new file mode 100644 index 000000000..815644f74 --- /dev/null +++ b/selfdrive/mapd/config.py @@ -0,0 +1,7 @@ +# Map query config + +QUERY_RADIUS = 3000 # mts. Radius to use on OSM data queries. +MIN_DISTANCE_FOR_NEW_QUERY = 1000 # mts. Minimum distance to query area edge before issuing a new query. +FULL_STOP_MAX_SPEED = 0.1 # m/s Max speed for considering car is stopped. +LOOK_AHEAD_HORIZON_TIME = 15. # s. Time horizon for look ahead of turn speed sections to provide on liveMapData msg. +LANE_WIDTH = 2.7 # Lane width estimate. Used for detecting departures from way. diff --git a/selfdrive/mapd/default_speeds.json b/selfdrive/mapd/default_speeds.json new file mode 100644 index 000000000..7126c27fc --- /dev/null +++ b/selfdrive/mapd/default_speeds.json @@ -0,0 +1,106 @@ +{ + "_comment": "These speeds are from https://wiki.openstreetmap.org/wiki/Speed_limits Special cases have been stripped", + "AR:urban": "40", + "AR:urban:primary": "60", + "AR:urban:secondary": "60", + "AR:rural": "110", + "AT:urban": "50", + "AT:rural": "100", + "AT:trunk": "100", + "AT:motorway": "130", + "BE:urban": "50", + "BE-VLG:rural": "70", + "BE-WAL:rural": "90", + "BE:trunk": "120", + "BE:motorway": "120", + "CH:urban[1]": "50", + "CH:rural": "80", + "CH:trunk": "100", + "CH:motorway": "120", + "CZ:pedestrian_zone": "20", + "CZ:living_street": "20", + "CZ:urban": "50", + "CZ:urban_trunk": "80", + "CZ:urban_motorway": "80", + "CZ:rural": "90", + "CZ:trunk": "110", + "CZ:motorway": "130", + "DK:urban": "50", + "DK:rural": "80", + "DK:motorway": "130", + "DE:living_street": "7", + "DE:residential": "30", + "DE:urban": "50", + "DE:rural": "100", + "DE:trunk": "none", + "DE:motorway": "none", + "FI:urban": "50", + "FI:rural": "80", + "FI:trunk": "100", + "FI:motorway": "120", + "FR:urban": "50", + "FR:rural": "80", + "FR:trunk": "110", + "FR:motorway": "130", + "GR:urban": "50", + "GR:rural": "90", + "GR:trunk": "110", + "GR:motorway": "130", + "HU:urban": "50", + "HU:rural": "90", + "HU:trunk": "110", + "HU:motorway": "130", + "IT:urban": "50", + "IT:rural": "90", + "IT:trunk": "110", + "IT:motorway": "130", + "JP:national": "60", + "JP:motorway": "100", + "LT:living_street": "20", + "LT:urban": "50", + "LT:rural": "90", + "LT:trunk": "120", + "LT:motorway": "130", + "PL:living_street": "20", + "PL:urban": "50", + "PL:rural": "90", + "PL:trunk": "100", + "PL:motorway": "140", + "RO:urban": "50", + "RO:rural": "90", + "RO:trunk": "100", + "RO:motorway": "130", + "RU:living_street": "20", + "RU:urban": "60", + "RU:rural": "90", + "RU:motorway": "110", + "SK:urban": "50", + "SK:rural": "90", + "SK:trunk": "90", + "SK:motorway": "90", + "SI:urban": "50", + "SI:rural": "90", + "SI:trunk": "110", + "SI:motorway": "130", + "ES:living_street": "20", + "ES:urban": "50", + "ES:rural": "50", + "ES:trunk": "90", + "ES:motorway": "120", + "SE:urban": "50", + "SE:rural": "70", + "SE:trunk": "90", + "SE:motorway": "110", + "GB:nsl_restricted": "30 mph", + "GB:nsl_single": "60 mph", + "GB:nsl_dual": "70 mph", + "GB:motorway": "70 mph", + "UA:urban": "50", + "UA:rural": "90", + "UA:trunk": "110", + "UA:motorway": "130", + "UZ:living_street": "30", + "UZ:urban": "70", + "UZ:rural": "100", + "UZ:motorway": "110" +} diff --git a/selfdrive/mapd/default_speeds_generator.py b/selfdrive/mapd/default_speeds_generator.py new file mode 100644 index 000000000..888c9e1df --- /dev/null +++ b/selfdrive/mapd/default_speeds_generator.py @@ -0,0 +1,240 @@ +#!/usr/bin/env python +import json + +DEFAULT_OUTPUT_FILENAME = "default_speeds_by_region.json" + +def main(filename = DEFAULT_OUTPUT_FILENAME): + countries = [] + + """ + -------------------------------------------------- + US - United State of America + -------------------------------------------------- + """ + US = Country("US") # First step, create the country using the ISO 3166 two letter code + countries.append(US) # Second step, add the country to countries list + + """ Default rules """ + # Third step, add some default rules for the country + # Speed limit rules are based on OpenStreetMaps (OSM) tags. + # The dictionary {...} defines the tag_name: value + # if a road in OSM has a tag with the name tag_name and this value, the speed limit listed below will be applied. + # The text at the end is the speed limit (use no unit for km/h) + # Rules apply in the order in which they are written for each country + # Rules for specific regions (states) take priority over country rules + # If you modify existing country rules, you must update all existing states without that rule to use the old rule + US.add_rule({"highway": "motorway"}, "65 mph") # On US roads with the tag highway and value motorway, the speed limit will default to 65 mph + US.add_rule({"highway": "trunk"}, "55 mph") + US.add_rule({"highway": "primary"}, "55 mph") + US.add_rule({"highway": "secondary"}, "45 mph") + US.add_rule({"highway": "tertiary"}, "35 mph") + US.add_rule({"highway": "unclassified"}, "55 mph") + US.add_rule({"highway": "residential"}, "25 mph") + US.add_rule({"highway": "service"}, "25 mph") + US.add_rule({"highway": "motorway_link"}, "55 mph") + US.add_rule({"highway": "trunk_link"}, "55 mph") + US.add_rule({"highway": "primary_link"}, "55 mph") + US.add_rule({"highway": "secondary_link"}, "45 mph") + US.add_rule({"highway": "tertiary_link"}, "35 mph") + US.add_rule({"highway": "living_street"}, "15 mph") + + """ States """ + new_york = US.add_region("New York") # Fourth step, add a state/region to country + new_york.add_rule({"highway": "primary"}, "45 mph") # Fifth step , add rules to the state. See the text above for how to write rules + new_york.add_rule({"highway": "secondary"}, "55 mph") + new_york.add_rule({"highway": "tertiary"}, "55 mph") + new_york.add_rule({"highway": "residential"}, "30 mph") + new_york.add_rule({"highway": "primary_link"}, "45 mph") + new_york.add_rule({"highway": "secondary_link"}, "55 mph") + new_york.add_rule({"highway": "tertiary_link"}, "55 mph") + # All if not written by the state, the rules will default to the country rules + + #california = US.add_region("California") + # California uses only the default US rules + + michigan = US.add_region("Michigan") + michigan.add_rule({"highway": "motorway"}, "70 mph") + + oregon = US.add_region("Oregon") + oregon.add_rule({"highway": "motorway"}, "55 mph") + oregon.add_rule({"highway": "secondary"}, "35 mph") + oregon.add_rule({"highway": "tertiary"}, "30 mph") + oregon.add_rule({"highway": "service"}, "15 mph") + oregon.add_rule({"highway": "secondary_link"}, "35 mph") + oregon.add_rule({"highway": "tertiary_link"}, "30 mph") + + south_dakota = US.add_region("South Dakota") + south_dakota.add_rule({"highway": "motorway"}, "80 mph") + south_dakota.add_rule({"highway": "trunk"}, "70 mph") + south_dakota.add_rule({"highway": "primary"}, "65 mph") + south_dakota.add_rule({"highway": "trunk_link"}, "70 mph") + south_dakota.add_rule({"highway": "primary_link"}, "65 mph") + + wisconsin = US.add_region("Wisconsin") + wisconsin.add_rule({"highway": "trunk"}, "65 mph") + wisconsin.add_rule({"highway": "tertiary"}, "45 mph") + wisconsin.add_rule({"highway": "unclassified"}, "35 mph") + wisconsin.add_rule({"highway": "trunk_link"}, "65 mph") + wisconsin.add_rule({"highway": "tertiary_link"}, "45 mph") + + """ + -------------------------------------------------- + AU - Australia + -------------------------------------------------- + """ + AU = Country("AU") + countries.append(AU) + + """ Default rules """ + AU.add_rule({"highway": "motorway"}, "100") + AU.add_rule({"highway": "trunk"}, "80") + AU.add_rule({"highway": "primary"}, "80") + AU.add_rule({"highway": "secondary"}, "50") + AU.add_rule({"highway": "tertiary"}, "50") + AU.add_rule({"highway": "unclassified"}, "80") + AU.add_rule({"highway": "residential"}, "50") + AU.add_rule({"highway": "service"}, "40") + AU.add_rule({"highway": "motorway_link"}, "90") + AU.add_rule({"highway": "trunk_link"}, "80") + AU.add_rule({"highway": "primary_link"}, "80") + AU.add_rule({"highway": "secondary_link"}, "50") + AU.add_rule({"highway": "tertiary_link"}, "50") + AU.add_rule({"highway": "living_street"}, "30") + + """ + -------------------------------------------------- + CA - Canada + -------------------------------------------------- + """ + CA = Country("CA") + countries.append(CA) + + """ Default rules """ + CA.add_rule({"highway": "motorway"}, "100") + CA.add_rule({"highway": "trunk"}, "80") + CA.add_rule({"highway": "primary"}, "80") + CA.add_rule({"highway": "secondary"}, "50") + CA.add_rule({"highway": "tertiary"}, "50") + CA.add_rule({"highway": "unclassified"}, "80") + CA.add_rule({"highway": "residential"}, "40") + CA.add_rule({"highway": "service"}, "40") + CA.add_rule({"highway": "motorway_link"}, "90") + CA.add_rule({"highway": "trunk_link"}, "80") + CA.add_rule({"highway": "primary_link"}, "80") + CA.add_rule({"highway": "secondary_link"}, "50") + CA.add_rule({"highway": "tertiary_link"}, "50") + CA.add_rule({"highway": "living_street"}, "20") + + + """ + -------------------------------------------------- + DE - Germany + -------------------------------------------------- + """ + DE = Country("DE") + countries.append(DE) + + """ Default rules """ + DE.add_rule({"highway": "motorway"}, "none") + DE.add_rule({"highway": "living_street"}, "10") + DE.add_rule({"highway": "residential"}, "30") + DE.add_rule({"zone:traffic": "DE:rural"}, "100") + DE.add_rule({"zone:traffic": "DE:urban"}, "50") + DE.add_rule({"zone:maxspeed": "DE:30"}, "30") + DE.add_rule({"zone:maxspeed": "DE:urban"}, "50") + DE.add_rule({"zone:maxspeed": "DE:rural"}, "100") + DE.add_rule({"zone:maxspeed": "DE:motorway"}, "none") + DE.add_rule({"bicycle_road": "yes"}, "30") + + + """ + -------------------------------------------------- + EE - Estonia + -------------------------------------------------- + """ + EE = Country("EE") + countries.append(EE) + + """ Default rules """ + EE.add_rule({"highway": "motorway"}, "90") + EE.add_rule({"highway": "trunk"}, "90") + EE.add_rule({"highway": "primary"}, "90") + EE.add_rule({"highway": "secondary"}, "50") + EE.add_rule({"highway": "tertiary"}, "50") + EE.add_rule({"highway": "unclassified"}, "90") + EE.add_rule({"highway": "residential"}, "40") + EE.add_rule({"highway": "service"}, "40") + EE.add_rule({"highway": "motorway_link"}, "90") + EE.add_rule({"highway": "trunk_link"}, "70") + EE.add_rule({"highway": "primary_link"}, "70") + EE.add_rule({"highway": "secondary_link"}, "50") + EE.add_rule({"highway": "tertiary_link"}, "50") + EE.add_rule({"highway": "living_street"}, "20") + + + """ --- DO NOT MODIFY CODE BELOW THIS LINE --- """ + """ --- ADD YOUR COUNTRY OR STATE ABOVE --- """ + + # Final step + write_json(countries, filename) + +def write_json(countries, filename = DEFAULT_OUTPUT_FILENAME): + out_dict = {} + for country in countries: + out_dict.update(country.jsonify()) + json_string = json.dumps(out_dict, indent=2) + with open(filename, "wb") as f: + f.write(json_string) + + +class Region(object): + ALLOWABLE_TAG_KEYS = ["highway", "zone:traffic", "bicycle_road", "zone:maxspeed"] + ALLOWABLE_HIGHWAY_TYPES = ["motorway", "trunk", "primary", "secondary", "tertiary", "unclassified", "residential", "service", "motorway_link", "trunk_link", "primary_link", "secondary_link", "tertiary_link", "living_street"] + def __init__(self, name): + self.name = name + self.rules = [] + + def add_rule(self, tag_conditions, speed): + new_rule = {} + if not isinstance(tag_conditions, dict): + raise TypeError("Rule tag conditions must be dictionary") + if not all(tag_key in self.ALLOWABLE_TAG_KEYS for tag_key in tag_conditions): + raise ValueError("Rule tag keys must be in allowable tag kesy") # If this is by mistake, please update ALLOWABLE_TAG_KEYS + if 'highway' in tag_conditions: + if not tag_conditions['highway'] in self.ALLOWABLE_HIGHWAY_TYPES: + raise ValueError("Invalid Highway type {}".format(tag_conditions["highway"])) + new_rule['tags'] = tag_conditions + try: + new_rule['speed'] = str(speed) + except ValueError: + raise ValueError("Rule speed must be string") + self.rules.append(new_rule) + + def jsonify(self): + ret_dict = {} + ret_dict[self.name] = self.rules + return ret_dict + +class Country(Region): + ALLOWABLE_COUNTRY_CODES = ["AF","AX","AL","DZ","AS","AD","AO","AI","AQ","AG","AR","AM","AW","AU","AT","AZ","BS","BH","BD","BB","BY","BE","BZ","BJ","BM","BT","BO","BQ","BA","BW","BV","BR","IO","BN","BG","BF","BI","KH","CM","CA","CV","KY","CF","TD","CL","CN","CX","CC","CO","KM","CG","CD","CK","CR","CI","HR","CU","CW","CY","CZ","DK","DJ","DM","DO","EC","EG","SV","GQ","ER","EE","ET","FK","FO","FJ","FI","FR","GF","PF","TF","GA","GM","GE","DE","GH","GI","GR","GL","GD","GP","GU","GT","GG","GN","GW","GY","HT","HM","VA","HN","HK","HU","IS","IN","ID","IR","IQ","IE","IM","IL","IT","JM","JP","JE","JO","KZ","KE","KI","KP","KR","KW","KG","LA","LV","LB","LS","LR","LY","LI","LT","LU","MO","MK","MG","MW","MY","MV","ML","MT","MH","MQ","MR","MU","YT","MX","FM","MD","MC","MN","ME","MS","MA","MZ","MM","NA","NR","NP","NL","NC","NZ","NI","NE","NG","NU","NF","MP","NO","OM","PK","PW","PS","PA","PG","PY","PE","PH","PN","PL","PT","PR","QA","RE","RO","RU","RW","BL","SH","KN","LC","MF","PM","VC","WS","SM","ST","SA","SN","RS","SC","SL","SG","SX","SK","SI","SB","SO","ZA","GS","SS","ES","LK","SD","SR","SJ","SZ","SE","CH","SY","TW","TJ","TZ","TH","TL","TG","TK","TO","TT","TN","TR","TM","TC","TV","UG","UA","AE","GB","US","UM","UY","UZ","VU","VE","VN","VG","VI","WF","EH","YE","ZM","ZW"] + def __init__(self, ISO_3166_alpha_2): + Region.__init__(self, ISO_3166_alpha_2) + if ISO_3166_alpha_2 not in self.ALLOWABLE_COUNTRY_CODES: + raise ValueError("Not valid IOS 3166 country code") + self.regions = {} + + def add_region(self, name): + self.regions[name] = Region(name) + return self.regions[name] + + def jsonify(self): + ret_dict = {} + ret_dict[self.name] = {} + for r_name, region in self.regions.items(): + ret_dict[self.name].update(region.jsonify()) + ret_dict[self.name]['Default'] = self.rules + return ret_dict + + +if __name__ == '__main__': + main() diff --git a/selfdrive/mapd/lib/NodesData.py b/selfdrive/mapd/lib/NodesData.py new file mode 100644 index 000000000..2f30607df --- /dev/null +++ b/selfdrive/mapd/lib/NodesData.py @@ -0,0 +1,393 @@ +import numpy as np +from enum import Enum +from selfdrive.mapd.lib.geo import DIRECTION, R, vectors + +# from scipy.interpolate import splev, splprep +from opspline import splev, splprep # pylint: disable=E0401 + + +_TURN_CURVATURE_THRESHOLD = 0.002 # 1/mts. A curvature over this value will generate a speed limit section. +_MAX_LAT_ACC = 2.3 # Maximum lateral acceleration in turns. +_SPLINE_EVAL_STEP = 5 # mts for spline evaluation for curvature calculation +_MIN_SPEED_SECTION_LENGTH = 100. # mts. Sections below this value will not be split in smaller sections. +_MAX_CURV_DEVIATION_FOR_SPLIT = 2. # Split a speed section if the max curvature deviates from mean by this factor. +_MAX_CURV_SPLIT_ARC_ANGLE = 90. # degrees. Arc section to split into new speed section around max curvature. +_MIN_NODE_DISTANCE = 50. # mts. Minimum distance between nodes for spline evaluation. Data is enhanced if not met. +_ADDED_NODES_DIST = 15. # mts. Distance between added nodes when data is enhanced for spline evaluation. +_DIVERTION_SEARCH_RANGE = [-200., 50.] # mt. Range of distance to current location for divertion search. + + +def nodes_raw_data_array_for_wr(wr, drop_last=False): + """Provides an array of raw node data (id, lat, lon, speed_limit) for all nodes in way relation + """ + sl = wr.speed_limit + data = np.array([(n.id, n.lat, n.lon, sl) for n in wr.way.nodes], dtype=float) + + # reverse the order if way direction is backwards + if wr.direction == DIRECTION.BACKWARD: + data = np.flip(data, axis=0) + + # drop last if requested + return data[:-1] if drop_last else data + + +def node_calculations(points): + """Provides node calculations based on an array of (lat, lon) points in radians. + points is a (N x 1) array where N >= 3 + """ + if len(points) < 3: + raise(IndexError) + + # Get the vector representation of node points in cartesian plane. + # (N-1, 2) array. Not including (0., 0.) + v = vectors(points) * R + + # Calculate the vector magnitudes (or distance) + # (N-1, 1) array. No distance for v[-1] + d = np.linalg.norm(v, axis=1) + + # Calculate the bearing (from true north clockwise) for every node. + # (N-1, 1) array. No bearing for v[-1] + b = np.arctan2(v[:, 0], v[:, 1]) + + # Add origin to vector space. (i.e first node in list) + v = np.concatenate(([[0., 0.]], v)) + + # Provide distance to previous node and distance to next node + dp = np.concatenate(([0.], d)) + dn = np.concatenate((d, [0.])) + + # Provide cumulative distance on route + dr = np.cumsum(dp, axis=0) + + # Bearing of last node should keep bearing from previous. + b = np.concatenate((b, [b[-1]])) + + return v, dp, dn, dr, b + + +def spline_curvature_calculations(vect, dist_prev): + """Provides an array of curvatures and its distances by applying a spline interpolation + to the path described by the nodes data. + """ + # We need to artificially enhance the data before applying spline interpolation to avoid getting + # inexistent curvature values close to irregularities on the road when the resolution of nodes data + # approaching the irregularity is low. + + # - Find indexes where dist_prev is greater than threshold + too_far_idxs = np.nonzero(dist_prev >= _MIN_NODE_DISTANCE)[0] + + # - Traversing in reverse order, enhance data by adding points at the found indexes. + for idx in too_far_idxs[::-1]: + dp = dist_prev[idx] # distance of vector that needs to be replaced by higher resolution vectors. + n = int(np.ceil(dp / _ADDED_NODES_DIST)) # number of vectors that need to be added. + new_v = vect[idx, :] / n # new relative vector to insert. + vect = np.delete(vect, idx, axis=0) # remove the relative vector to be replaced by the insertion of new vectors. + vect = np.insert(vect, [idx] * n, [new_v] * n, axis=0) # insert n new relative vectors + + # Data is now enhanced, we can proceed with curvature evaluation. + # - Create cumulative arrays for distance traveled and vector (x, y) + ds = np.cumsum(dist_prev, axis=0) + vs = np.cumsum(vect, axis=0) + + # - spline interpolation + tck, u = splprep([vs[:, 0], vs[:, 1]]) # pylint: disable=unbalanced-tuple-unpacking + + # - evaluate every _SPLINE_EVAL_STEP mts. + n = max(int(ds[-1] / _SPLINE_EVAL_STEP), len(u)) + unew = np.arange(0, n + 1) / n + + # - get derivatives + d1 = splev(unew, tck, der=1) + d2 = splev(unew, tck, der=2) + + # - calculate curvatures + num = d1[0] * d2[1] - d1[1] * d2[0] + den = (d1[0]**2 + d1[1]**2)**(1.5) + curv = num / den + curv_ds = unew * ds[-1] + + return curv, curv_ds + + +def speed_section(curv_sec): + """Map curvature section data into turn speed sections data. + Returns: [section start distance, section end distance, speed limit based on max curvature, sing of curvature] + """ + max_curv_idx = np.argmax(curv_sec[:, 0]) + start = np.amin(curv_sec[:, 2]) + end = np.amax(curv_sec[:, 2]) + + return np.array([start, end, np.sqrt(_MAX_LAT_ACC / curv_sec[max_curv_idx, 0]), curv_sec[max_curv_idx, 1]]) + + +def split_speed_section_by_sign(curv_sec): + """Will split the given curvature section in subsections if there is a change of sign on the curvature value + in the section. + """ + # Find the indexes where the curvatures change signs (if any). + c_idx = np.nonzero(np.diff(curv_sec[:, 1]))[0] + 1 + + # Split section base on change of sign. + return np.split(curv_sec, c_idx) + + +def split_speed_section_by_curv_degree(curv_sec): + """Will split the given curvature section in subsections as to isolate peaks of turn with substantially + higher curvature values. This will aid on preventing having very long turn sections with low speed limit + that is only really necessary for a small region of the section. + """ + # Only consider spliting a section if long enough. + length = curv_sec[-1, 2] - curv_sec[0, 2] + if length <= _MIN_SPEED_SECTION_LENGTH: + return [curv_sec] + + # Only split if max curvature deviates substantially from mean curvature. + max_curv_idx = np.argmax(curv_sec[:, 0]) + max_curv = curv_sec[max_curv_idx, 0] + mean_curv = np.mean(curv_sec[:, 0]) + if max_curv / mean_curv <= _MAX_CURV_DEVIATION_FOR_SPLIT: + return [curv_sec] + + # Calcualate where to split as to isolate a curve section around the max curvature peak. + arc_side = (np.radians(_MAX_CURV_SPLIT_ARC_ANGLE) / max_curv) / 2. + arc_side_idx_lenght = int(np.ceil(arc_side / _SPLINE_EVAL_STEP)) + split_idxs = [max_curv_idx - arc_side_idx_lenght, max_curv_idx + arc_side_idx_lenght] + split_idxs = list(filter(lambda idx: idx > 0 and idx < len(curv_sec) - 1, split_idxs)) + + # If the arc section to split extendes outside the section, then no need to split. + if len(split_idxs) == 0: + return [curv_sec] + + # Create the splits and split the resulting sections recursevly. + splits = [split_speed_section_by_curv_degree(cs) for cs in np.split(curv_sec, split_idxs)] + + # Flatten the results and return the new list of curvature sections. + curv_secs = [cs for split in splits for cs in split] + return curv_secs + + +def speed_limits_for_curvatures_data(curv, dist): + """Provides the calculations for the speed limits from the curvatures array and distances, + by providing distances to curvature sections and correspoinding speed limit values as well as + curvature direction/sign. + """ + # Prepare a data array for processing with absolute curvature values, curvature sign and distances. + curv_abs = np.abs(curv) + data = np.column_stack((curv_abs, np.sign(curv), dist)) + + # Find where curvatures overshoot turn curvature threshold and define as section + is_section = curv_abs >= _TURN_CURVATURE_THRESHOLD + + # Find the indexes where the sections start and end. i.e. change indexes. + c_idx = np.nonzero(np.diff(is_section))[0] + 1 + + # Create independent arrays for each split section base on change indexes. + splits = np.array(np.split(data, c_idx), dtype=object) + + # Filter the splits to keep only the curvature section arrays by getting the odd or even split arrays depending + # on whether the first split is a curvature split or not. + curv_sec_idxs = np.arange(0 if is_section[0] else 1, len(splits), 2, dtype=int) + curv_secs = splits[curv_sec_idxs] + + # Further split the curv sections by sign change + sub_secs = [split_speed_section_by_sign(cs) for cs in curv_secs] + curv_secs = [cs for sub_sec in sub_secs for cs in sub_sec] + + # Further split the curv sections by degree of curvature + sub_secs = [split_speed_section_by_curv_degree(cs) for cs in curv_secs] + curv_secs = [cs for sub_sec in sub_secs for cs in sub_sec] + + # Return an array where each row represents a turn speed limit section. + # [start, end, speed_limit, curvature_sign] + return np.array([speed_section(cs) for cs in curv_secs]) + +def is_wr_a_valid_divertion_from_node(wr, node_id, wr_ids): + """ + Evaluates if the way relation `wr` is a valid divertion from node with id `node_id`. + A valid divertion is a way relation with an edge node with the given `node_id` that is not already included + in the list of way relations in the route (`wr_ids`) and that can be travaled in the direction as if starting + from node with id `node_id` + """ + if wr.id in wr_ids: + return False + wr.update_direction_from_starting_node(node_id) + return not wr.is_prohibited + + +class SpeedLimitSection(): + """And object representing a speed limited road section ahead. + provides the start and end distance and the speed limit value + """ + def __init__(self, start, end, value): + self.start = start + self.end = end + self.value = value + + def __repr__(self): + return f'from: {self.start}, to: {self.end}, limit: {self.value}' + + +class TurnSpeedLimitSection(SpeedLimitSection): + def __init__(self, start, end, value, sign): + super().__init__(start, end, value) + self.curv_sign = sign + + def __repr__(self): + return f'{super().__repr__()}, sign: {self.curv_sign}' + + +class NodeDataIdx(Enum): + """Column index for data elements on NodesData underlying data store. + """ + node_id = 0 + lat = 1 + lon = 2 + speed_limit = 3 + x = 4 # x value of cartesian vector representing the section between last node and this node. + y = 5 # y value of cartesian vector representing the section between last node and this node. + dist_prev = 6 # distance to previous node. + dist_next = 7 # distance to next node + dist_route = 8 # cumulative distance on route + bearing = 9 # bearing of the vector departing from this node. + + +class NodesData: + """Container for the list of node data from a ordered list of way relations to be used in a Route + """ + def __init__(self, way_relations, wr_index): + self._nodes_data = np.array([]) + self._divertions = [[]] + self._curvature_speed_sections_data = np.array([]) + + way_count = len(way_relations) + if way_count == 0: + return + + # We want all the nodes from the last way section + nodes_data = nodes_raw_data_array_for_wr(way_relations[-1]) + + # For the ways before the last in the route we want all the nodes but the last, as that one is the first on + # the next section. Collect them, append last way node data and concatenate the numpy arrays. + if way_count > 1: + wrs_data = tuple([nodes_raw_data_array_for_wr(wr, drop_last=True) for wr in way_relations[:-1]]) + wrs_data += (nodes_data,) + nodes_data = np.concatenate(wrs_data) + + # Get a subarray with lat, lon to compute the remaining node values. + lat_lon_array = nodes_data[:, [1, 2]] + points = np.radians(lat_lon_array) + # Ensure we have more than 3 points, if not calculations are not possible. + if len(points) <= 3: + return + vect, dist_prev, dist_next, dist_route, bearing = node_calculations(points) + + # append calculations to nodes_data + # nodes_data structure: [id, lat, lon, speed_limit, x, y, dist_prev, dist_next, dist_route, bearing] + self._nodes_data = np.column_stack((nodes_data, vect, dist_prev, dist_next, dist_route, bearing)) + + # Build route divertion options data from the wr_index. + wr_ids = [wr.id for wr in way_relations] + self._divertions = [[wr for wr in wr_index.way_relations_with_edge_node_id(node_id) + if is_wr_a_valid_divertion_from_node(wr, node_id, wr_ids)] + for node_id in nodes_data[:, 0]] + + # Store calculcations for curvature sections speed limits. We need more than 3 points to be able to process. + # _curvature_speed_sections_data structure: [dist_start, dist_stop, speed_limits, curv_sign] + if len(vect) > 3: + curv, curv_ds = spline_curvature_calculations(vect, dist_prev) + self._curvature_speed_sections_data = speed_limits_for_curvatures_data(curv, curv_ds) + + @property + def count(self): + return len(self._nodes_data) + + def get(self, node_data_idx): + """Returns the array containing all the elements of a specific NodeDataIdx type. + """ + if len(self._nodes_data) == 0 or node_data_idx.value >= self._nodes_data.shape[1]: + return np.array([]) + + return self._nodes_data[:, node_data_idx.value] + + def speed_limits_ahead(self, ahead_idx, distance_to_node_ahead): + """Returns and array of SpeedLimitSection objects for the actual route ahead of current location + """ + if len(self._nodes_data) == 0 or ahead_idx is None: + return [] + + # Find the cumulative distances where speed limit changes. Build Speed limit sections for those. + dist = np.concatenate(([distance_to_node_ahead], self.get(NodeDataIdx.dist_next)[ahead_idx:])) + dist = np.cumsum(dist, axis=0) + sl = self.get(NodeDataIdx.speed_limit)[ahead_idx - 1:] + sl_next = np.concatenate((sl[1:], [0.])) + + # Create a boolean mask where speed limit changes and filter values + sl_change = sl != sl_next + distances = dist[sl_change] + speed_limits = sl[sl_change] + + # Create speed limits sections combining all continious nodes that have same speed limit value. + start = 0. + limits_ahead = [] + for idx, end in enumerate(distances): + limits_ahead.append(SpeedLimitSection(start, end, speed_limits[idx])) + start = end + + return limits_ahead + + def distance_to_end(self, ahead_idx, distance_to_node_ahead): + if len(self._nodes_data) == 0 or ahead_idx is None: + return None + + return np.sum(np.concatenate(([distance_to_node_ahead], self.get(NodeDataIdx.dist_next)[ahead_idx:]))) + + def curvatures_speed_limit_sections_ahead(self, ahead_idx, distance_to_node_ahead): + """Returns and array of TurnSpeedLimitSection objects for the actual route ahead of current location for + speed limit sections due to curvatures in the road. + """ + if len(self._curvature_speed_sections_data) == 0 or ahead_idx is None: + return [] + + # Find the current distance traveled so far on the route. + dist_curr = self.get(NodeDataIdx.dist_route)[ahead_idx] - distance_to_node_ahead + + # Filter the sections to get only those where the stop distance is ahead of current. + sec_filter = self._curvature_speed_sections_data[:, 1] > dist_curr + data = self._curvature_speed_sections_data[sec_filter] + + # Offset distances to current distance. + data[:, [0, 1]] -= dist_curr + + # Create speed limits sections + limits_ahead = [TurnSpeedLimitSection(max(0., d[0]), d[1], d[2], d[3]) for d in data] + + return limits_ahead + + def possible_divertions(self, ahead_idx, distance_to_node_ahead): + """ Returns and array with the way relations the route could possible divert to by finding + the alternative way divertions on the nodes in the vicinity of the current location. + """ + if len(self._nodes_data) == 0 or ahead_idx is None: + return [] + + dist_route = self.get(NodeDataIdx.dist_route) + rel_dist = dist_route - dist_route[ahead_idx] + distance_to_node_ahead + valid_idxs = np.nonzero(np.logical_and(rel_dist >= _DIVERTION_SEARCH_RANGE[0], + rel_dist <= _DIVERTION_SEARCH_RANGE[1]))[0] + valid_divertions = [self._divertions[i] for i in valid_idxs] + + return [wr for wrs in valid_divertions for wr in wrs] # flatten. + + def distance_to_node(self, node_id, ahead_idx, distance_to_node_ahead): + """ + Provides the distance to a specific node in the route identified by `node_id` in reference to the node ahead + (`ahead_idx`) and the distance from current location to the node ahead (`distance_to_node_ahead`). + """ + node_ids = self.get(NodeDataIdx.node_id) + node_idxs = np.nonzero(node_ids == node_id)[0] + if len(self._nodes_data) == 0 or ahead_idx is None or len(node_idxs) == 0: + return None + + return self.get(NodeDataIdx.dist_route)[node_idxs[0]] - self.get(NodeDataIdx.dist_route)[ahead_idx] + \ + distance_to_node_ahead diff --git a/selfdrive/mapd/lib/Route.py b/selfdrive/mapd/lib/Route.py new file mode 100644 index 000000000..7aa502bb2 --- /dev/null +++ b/selfdrive/mapd/lib/Route.py @@ -0,0 +1,340 @@ +from selfdrive.mapd.lib.NodesData import NodesData, NodeDataIdx +from selfdrive.mapd.config import QUERY_RADIUS +from selfdrive.mapd.lib.geo import ref_vectors, R, distance_to_points +from itertools import compress +import numpy as np + + +_ACCEPTABLE_BEARING_DELTA_COSINE = -0.7 # Continuation paths with a bearing of 180 +/- 45 degrees. +_MAX_ALLOWED_BEARING_DELTA_COSINE_AT_EDGE = -0.3420 # bearing delta at route edge must be 180 +/- 70 degrees. +_MAP_DATA_EDGE_DISTANCE = 50 # mts. Consider edge of map data from this distance to edge of query radius. + + +class Route(): + """A set of consecutive way relations forming a default driving route. + """ + def __init__(self, current, wr_index, way_collection_id, query_center): + """Create a Route object from a given `wr_index` (Way relation index) + + Args: + current (WayRelation): The Way Relation that is currently located. It must be active. + wr_index (WayRelationIndex): The indexes of WayRelations by node id. + way_collection_id (UUID): The id of the Way Collection that created this Route. + query_center (Numpy Array): lat, lon] numpy array in radians indicating the center of the data query. + """ + self.way_collection_id = way_collection_id + self._ordered_way_relations = [] + self._nodes_data = None + self._reset() + + # An active current way is needed to be able to build a route + if not current.active: + return + + # Build the route by finding iteratavely the best matching ways continuing after the end of the + # current (last_wr) way. Use the index to find the continuation posibilities on each iteration. + last_wr = current + ordered_way_ids = [] + split_wrs = [] + while True: + # - Append current element to the route list of ordered way relations. + self._ordered_way_relations.append(last_wr) + ordered_way_ids.append(last_wr.id) + + # - Get the id of the node at the end of the way and then fetch the way relations that share the end node id. + last_node_id = last_wr.last_node.id + way_relations = wr_index.way_relations_with_edge_node_id(last_node_id) + + # - Add split way relations when necessary and remove parent way relations. + split_wrs_to_add = [wr for wr in split_wrs if last_node_id in wr.edge_nodes_ids] + way_relations.extend(split_wrs_to_add) + parent_ids = [wr.parent_wr_id for wr in split_wrs_to_add] + way_relations = [wr for wr in way_relations if wr.id not in parent_ids] + + # - If no more way_relations than last_wr, we have to check if we join another wr on an internal node, and + # if we do, we replace such way relation with the split of it and continue. + if len(way_relations) == 1: + way_relations = wr_index.way_relations_with_node_id(last_node_id) + # If no more way_relations than last_wr or its parent, we got to the end. + if len(way_relations) == 1: + break + + # If last_wr is a split, replace its parent with last_wr + way_relations = [last_wr if wr is last_wr.parent else wr for wr in way_relations] + + # If we join a wr on an internal node, then we artificially split the wr in two and pass both wrs as + # candidates to the wr selection code below. + wr_to_split = [wr for wr in way_relations if wr is not last_wr][0] + next_split_way_id = -len(split_wrs) - 1 # Keep split wrs ids unique on Route + new_wrs = wr_to_split.split(last_node_id, [next_split_way_id, next_split_way_id - 1]) + # If it could not be splited, we are done. + if len(new_wrs) != 2: + break + + # Replace the original way relation for the splitted version on way_relations and track splited wrs. + split_wrs.extend(new_wrs) + way_relations.remove(wr_to_split) + way_relations.extend(new_wrs) + + # - Get the coordinates for the edge node and build the array of coordinates for the nodes before the edge node + # on each of the common way relations, then get the vectors in cartesian plane for the end sections of each way. + ref_point = last_wr.last_node_coordinates + points = np.array([wr.node_before_edge_coordinates(last_node_id) for wr in way_relations]) + v = ref_vectors(ref_point, points) * R + + # - Calculate the bearing (from true north clockwise) for every end section of each way. + b = np.arctan2(v[:, 0], v[:, 1]) + + # - Find index of las_wr section and calculate deltas of bearings to the other sections. + last_wr_idx = way_relations.index(last_wr) + b_ref = b[last_wr_idx] + delta = b - b_ref + + # - Update the direction of the possible route continuation ways as starting from last_node_id. + # Make sure to exclude any ways already included in the ordered list as to not modify direction when there + # are looping roads (like roundabouts). A way will never be included twice in a route anyway. + for wr in way_relations: + if wr.id not in ordered_way_ids: + wr.update_direction_from_starting_node(last_node_id) + + # - Filter the possible route continuation way relations: + # - exclude any way already added to the ordered list. + # - exclude all way relations that are prohibited due to traffic direction. + mask = [wr.id not in ordered_way_ids and not wr.is_prohibited for wr in way_relations] + way_relations = list(compress(way_relations, mask)) + delta = delta[mask] + + # if no options left, we got to the end. + if len(way_relations) == 0: + break + + # - The cosine of the bearing delta will aid us in choosing the way that continues. The cosine is + # minimum (-1) for a perfect straight continuation as delta would be pi or -pi. + cos_delta = np.cos(delta) + + def pick_best_idx(cos_delta): + """Selects the best index on `cos_delta` array for a way that continues the route. + In principle we want to choose the way that continues as straight as possible. + Bue we need to make sure that if there are 2 or more ways continuing relatively straight, then we + need to disambiguate, either by matching the `ref` or `name` value of the continuing way with the + last way selected. + This can prevent cases where the chosen route could be for instance an exit ramp of a way due to the fact + that the ramp has a better match on bearing to previous way. We choose to stay on the road with the same `ref` + or `name` value if available. + If there is no ambiguity or there are no `name` or `ref` values to disambiguate, then we pick the one with + the straightest following direction. + """ + # Find the indexes of the cosine of the deltas that are considered straight enough to continue. + idxs = np.nonzero(cos_delta < _ACCEPTABLE_BEARING_DELTA_COSINE)[0] + + # If no amiguity or no way to break it, just return the straightest line. + if len(idxs) <= 1 or (last_wr.ref is None and last_wr.name is None): + # The section with the best continuation is the one with a bearing delta closest to pi. This is equivalent + # to taking the one with the smallest cosine of the bearing delta, as cosine is minimum (-1) on both pi + # and -pi. + return np.argmin(cos_delta) + + wrs = [way_relations[idx] for idx in idxs] + + # If we find a continuation way with the same reference we just choose it. + refs = list(map(lambda wr: wr.ref, wrs)) + if last_wr.ref is not None: + idx = next((idx for idx, ref in enumerate(refs) if ref == last_wr.ref), None) + if idx is not None: + return idxs[idx] + + # If we find a continuation way with the same name we just choose it. + names = list(map(lambda wr: wr.name, wrs)) + if last_wr.name is not None: + idx = next((idx for idx, name in enumerate(names) if name == last_wr.name), None) + if idx is not None: + return idxs[idx] + + # We did not manage to deambiguate, choose straightest path. + return np.argmin(cos_delta) + + # Get the index of the continuation way. + best_idx = pick_best_idx(cos_delta) + + # - Make sure to not select as route continuation a way that turns too much if we are close to the border of + # map data queried. This is to avoid building a route that takes a sharp turn just because we do not have the + # data for the way that actually continues straight. + if cos_delta[best_idx] > _MAX_ALLOWED_BEARING_DELTA_COSINE_AT_EDGE: + dist_to_center = distance_to_points(query_center, np.array([ref_point]))[0] + if dist_to_center > QUERY_RADIUS - _MAP_DATA_EDGE_DISTANCE: + break + + # - Select next way. + last_wr = way_relations[best_idx] + + # Build the node data from the ordered list of way relations + self._nodes_data = NodesData(self._ordered_way_relations, wr_index) + + # Locate where we are in the route node list. + self._locate() + + def __repr__(self): + count = self._nodes_data.count if self._nodes_data is not None else None + return f'Route: {self.way_collection_id}, idx ahead: {self._ahead_idx} of {count}' + + def _reset(self): + self._limits_ahead = None + self._cuvature_limits_ahead = None + self._curvatures_ahead = None + self._ahead_idx = None + self._distance_to_node_ahead = None + + @property + def located(self): + return self._ahead_idx is not None + + def _locate(self): + """Will resolve the index in the nodes_data list for the node ahead of the current location. + It updates as well the distance from the current location to the node ahead. + """ + current = self.current_wr + if current is None: + return + + node_ahead_id = current.node_ahead.id + self._distance_to_node_ahead = current.distance_to_node_ahead + start_idx = self._ahead_idx if self._ahead_idx is not None else 1 + self._ahead_idx = None + + ids = self._nodes_data.get(NodeDataIdx.node_id) + for idx in range(start_idx, len(ids)): + if ids[idx] == node_ahead_id: + self._ahead_idx = idx + break + + @property + def current_wr(self): + return self._ordered_way_relations[0] if len(self._ordered_way_relations) else None + + def update(self, location_rad, bearing_rad, location_stdev): + """Will update the route structure based on the given `location_rad` and `bearing_rad` assuming progress on the + route on the original direction. If direction has changed or active point on the route can not be found, the route + will become invalid. + """ + if len(self._ordered_way_relations) == 0 or location_rad is None or bearing_rad is None: + return + + # Skip if no update on location or bearing. + if np.array_equal(self.current_wr.location_rad, location_rad) and self.current_wr.bearing_rad == bearing_rad: + return + + # Transverse the way relations on the actual order until we find an active one. From there, rebuild the route + # with the way relations remaining ahead. + for idx, wr in enumerate(self._ordered_way_relations): + active_direction = wr.direction + wr.update(location_rad, bearing_rad, location_stdev) + + if not wr.active: + continue + + if wr.direction != active_direction: + # Driving direction on the route has changed. stop. + break + + # We have now the current wr. Repopulate from here till the end and locate + self._ordered_way_relations = self._ordered_way_relations[idx:] + self._reset() + self._locate() + + # If the active way is diverting, check whether there are posibilities to divert from the route in the + # vecinity of the current location. If there are possibilities, then stop here to loose the route as we are + # most likely driving away. If there are no possibilites, then stick to the route as the diversion is probably + # just a matter of GPS accuracy. (It can happen after driving under a bridge) + if wr.diverting and len(self._nodes_data.possible_divertions(self._ahead_idx, self._distance_to_node_ahead)) > 0: + break + + # The current location in route is valid, return. + return + + # if we got here, there is no new active way relation or driving direction has changed. Reset. + self._reset() + + @property + def speed_limits_ahead(self): + """Returns and array of SpeedLimitSection objects for the actual route ahead of current location + """ + if self._limits_ahead is not None: + return self._limits_ahead + + if self._nodes_data is None or self._ahead_idx is None: + return [] + + self._limits_ahead = self._nodes_data.speed_limits_ahead(self._ahead_idx, self._distance_to_node_ahead) + return self._limits_ahead + + @property + def curvature_speed_limits_ahead(self): + """Returns and array of TurnSpeedLimitSection objects for the actual route ahead of current location due + to curvatures + """ + if self._cuvature_limits_ahead is not None: + return self._cuvature_limits_ahead + + if self._nodes_data is None or self._ahead_idx is None: + return [] + + self._cuvature_limits_ahead = self._nodes_data. \ + curvatures_speed_limit_sections_ahead(self._ahead_idx, self._distance_to_node_ahead) + + return self._cuvature_limits_ahead + + @property + def current_speed_limit(self): + if not self.located: + return None + + limits_ahead = self.speed_limits_ahead + if len(limits_ahead) == 0 or limits_ahead[0].start != 0: + return None + + return limits_ahead[0].value + + @property + def current_curvature_speed_limit_section(self): + if not self.located: + return None + + limits_ahead = self.curvature_speed_limits_ahead + if len(limits_ahead) == 0 or limits_ahead[0].start != 0: + return None + + return limits_ahead[0] + + @property + def next_speed_limit_section(self): + if not self.located: + return None + + limits_ahead = self.speed_limits_ahead + if len(limits_ahead) == 0: + return None + + # Find the first section that does not start in 0. i.e. the next section + for section in limits_ahead: + if section.start > 0: + return section + + return None + + def next_curvature_speed_limit_sections(self, horizon_mts): + if not self.located: + return [] + + # Provide the curvature speed sections that start ahead (> 0) and up to horizon + return list(filter(lambda la: la.start > 0 and la.start <= horizon_mts, self.curvature_speed_limits_ahead)) + + @property + def distance_to_end(self): + if not self.located: + return None + + return self._nodes_data.distance_to_end(self._ahead_idx, self._distance_to_node_ahead) + + @property + def current_road_name(self): + return self.current_wr.road_name if self.located else None diff --git a/selfdrive/mapd/lib/WayCollection.py b/selfdrive/mapd/lib/WayCollection.py new file mode 100644 index 000000000..56ad0b60d --- /dev/null +++ b/selfdrive/mapd/lib/WayCollection.py @@ -0,0 +1,85 @@ +from selfdrive.mapd.lib.WayRelation import WayRelation +from selfdrive.mapd.lib.WayRelationIndex import WayRelationIndex +from selfdrive.mapd.lib.Route import Route +from selfdrive.mapd.config import LANE_WIDTH +import uuid + + +_ACCEPTABLE_BEARING_DELTA_IND = 0.7071067811865475 # sin(pi/4) | 45 degrees acceptable bearing delta + + +class WayCollection(): + """A collection of WayRelations to use for maps data analysis. + """ + def __init__(self, ways, query_center): + """Creates a WayCollection with a set of OSM way objects. + + Args: + ways (Array): Collection of Way objects fetched from OSM in a radius around `query_center` + query_center (Numpy Array): [lat, lon] numpy array in radians indicating the center of the data query. + """ + self.id = uuid.uuid4() + self.way_relations = [WayRelation(way) for way in ways] + self.query_center = query_center + + self.wr_index = WayRelationIndex(self.way_relations) + + def get_route(self, location_rad, bearing_rad, location_stdev): + """Provides the best route found in the way collection based on current location and bearing. + """ + if location_rad is None or bearing_rad is None or location_stdev is None: + return None + + # Update all way relations in collection to the provided location and bearing. + for wr in self.way_relations: + wr.update(location_rad, bearing_rad, location_stdev) + + # Get the way relations where a match was found. i.e. those now marked as active as long as the direction of + # travel is valid. + valid_way_relations = [wr for wr in self.way_relations if wr.active and not wr.is_prohibited] + + # If no active, then we could not find a current way to build a route. + if len(valid_way_relations) == 0: + return None + + # If only one valid, then pick it as current. + if len(valid_way_relations) == 1: + current = valid_way_relations[0] + + # If more than one is valid, filter out any valid way relation where the bearing delta indicator is too high. + else: + wr_acceptable_bearing = list(filter(lambda wr: wr.active_bearing_delta <= _ACCEPTABLE_BEARING_DELTA_IND, + valid_way_relations)) + + # If delta bearing indicator is too high for all, then use as current the one that has the shorter one. + if len(wr_acceptable_bearing) == 0: + valid_way_relations.sort(key=lambda wr: wr.active_bearing_delta) + current = valid_way_relations[0] + + # If only one with acceptable bearing, use it. + elif len(wr_acceptable_bearing) == 1: + current = wr_acceptable_bearing[0] + + else: + # If more than one with acceptable bearing, filter the ones with distance to way lower than 2 standard + # deviation from GPS accuracy (95%) + half the road width estimate. + wr_accurate_distance = [wr for wr in wr_acceptable_bearing + if wr.distance_to_way <= 2. * location_stdev + wr.lanes * LANE_WIDTH / 2.] + + # If none with accurate distance to way, then select the closest to the way + if len(wr_accurate_distance) == 0: + wr_acceptable_bearing.sort(key=lambda wr: wr.distance_to_way) + current = wr_acceptable_bearing[0] + + # If only one with distance under accuracy, select this one. + elif len(wr_accurate_distance) == 1: + current = wr_accurate_distance[0] + + # If more than one with distance under accuracy. Then select the one with lowest highway rank. + # i.e. prefere motorways over other roads and so on. This is to prevent selecting a small paralel + # road to a main road when the accuracy is poor. + else: + wr_accurate_distance.sort(key=lambda wr: wr.highway_rank) + current = wr_accurate_distance[0] + + return Route(current, self.wr_index, self.id, self.query_center) diff --git a/selfdrive/mapd/lib/WayRelation.py b/selfdrive/mapd/lib/WayRelation.py new file mode 100644 index 000000000..7c796eb40 --- /dev/null +++ b/selfdrive/mapd/lib/WayRelation.py @@ -0,0 +1,422 @@ +from selfdrive.mapd.lib.geo import DIRECTION, R, vectors, bearing_to_points, distance_to_points +from selfdrive.mapd.lib.osm import create_way +from common.conversions import Conversions as CV +from selfdrive.mapd.config import LANE_WIDTH +from common.basedir import BASEDIR +from datetime import datetime as dt +import numpy as np +import re +import json + + +_WAY_BBOX_PADING = 80. / R # 80 mts of pading to bounding box. (expressed in radians) + + +with open(BASEDIR + "/selfdrive/mapd/lib/default_speeds.json", "rb") as f: + _COUNTRY_LIMITS = json.loads(f.read()) + + +_WD = { + 'Mo': 0, + 'Tu': 1, + 'We': 2, + 'Th': 3, + 'Fr': 4, + 'Sa': 5, + 'Su': 6 +} + +_HIGHWAY_RANK = { + 'motorway': 0, + 'motorway_link': 1, + 'trunk': 10, + 'trunk_link': 11, + 'primary': 20, + 'primary_link': 21, + 'secondary': 30, + 'secondary_link': 31, + 'tertiary': 40, + 'tertiary_link': 41, + 'unclassified': 50, + 'residential': 60, + 'living_street': 61 +} + + +def is_osm_time_condition_active(condition_string): + """ + Will indicate if a time condition for a restriction as described + @ https://wiki.openstreetmap.org/wiki/Conditional_restrictions + is active for the current date and time of day. + """ + now = dt.now().astimezone() + today = now.date() + week_days = [] + + # Look for days of week matched and validate if today matches criteria. + dr = re.findall(r'(Mo|Tu|We|Th|Fr|Sa|Su[-,\s]*?)', condition_string) + + if len(dr) == 1: + week_days = [_WD[dr[0]]] + # If two or more matches condider it a range of days between 1st and 2nd element. + elif len(dr) > 1: + week_days = list(range(_WD[dr[0]], _WD[dr[1]] + 1)) + + # If valid week days list is not empy and today day is not in the list, then the time-date range is not active. + if len(week_days) > 0 and now.weekday() not in week_days: + return False + + # Look for time ranges on the day. No time range, means all day + tr = re.findall(r'([0-9]{1,2}:[0-9]{2})\s*?-\s*?([0-9]{1,2}:[0-9]{2})', condition_string) + + # if no time range but there were week days set, consider it active during the whole day + if len(tr) == 0: + return len(dr) > 0 + + # Search among time ranges matched, one where now time belongs too. If found range is active. + for times_tup in tr: + times = list(map(lambda tt: dt. + combine(today, dt.strptime(tt, '%H:%M').time().replace(tzinfo=now.tzinfo)), times_tup)) + if now >= times[0] and now <= times[1]: + return True + + return False + + +def speed_limit_value_for_limit_string(limit_string): + # Look for matches of speed by default in kph, or in mph when explicitly noted. + v = re.match(r'^\s*([0-9]{1,3})\s*?(mph)?\s*$', limit_string) + if v is None: + return None + conv = CV.MPH_TO_MS if v[2] is not None and v[2] == "mph" else CV.KPH_TO_MS + return conv * float(v[1]) + + +def speed_limit_for_osm_tag_limit_string(limit_string): + # https://wiki.openstreetmap.org/wiki/Key:maxspeed + if limit_string is None: + # When limit is set to 0. is considered not existing. + return 0. + + # Attempt to parse limit as simple numeric value considering units. + limit = speed_limit_value_for_limit_string(limit_string) + if limit is not None: + return limit + + # Look for matches of speed with country implicit values. + v = re.match(r'^\s*([A-Z]{2}):([a-z_]+):?([0-9]{1,3})?(\s+)?(mph)?\s*', limit_string) + if v is None: + return 0. + + if v[2] == "zone" and v[3] is not None: + conv = CV.MPH_TO_MS if v[5] is not None and v[5] == "mph" else CV.KPH_TO_MS + limit = conv * float(v[3]) + elif f'{v[1]}:{v[2]}' in _COUNTRY_LIMITS: + limit = speed_limit_value_for_limit_string(_COUNTRY_LIMITS[f'{v[1]}:{v[2]}']) + + return limit if limit is not None else 0. + + +def conditional_speed_limit_for_osm_tag_limit_string(limit_string): + if limit_string is None: + # When limit is set to 0. is considered not existing. + return 0. + + # Look for matches of the ` @ ()` format + v = re.match(r'^(.*)@\s*\((.*)\).*$', limit_string) + if v is None: + return 0. # No valid format match + + value = speed_limit_for_osm_tag_limit_string(v[1]) + if value == 0.: + return 0. # Invalid speed limit value + + # Look for date-time conditions separated by semicolon + v = re.findall(r'(?:;|^)([^;]*)', v[2]) + for datetime_condition in v: + if is_osm_time_condition_active(datetime_condition): + return value + + # If we get here, no current date-time conditon is active. + return 0. + + +class WayRelation(): + """A class that represent the relationship of an OSM way and a given `location` and `bearing` of a driving vehicle. + """ + def __init__(self, way, parent=None): + self.way = way + self.parent = parent + self.parent_wr_id = parent.id if parent is not None else None # For WRs created as splits of other WRs + self.reset_location_variables() + self.direction = DIRECTION.NONE + self._speed_limit = None + self._one_way = way.tags.get("oneway") + self.name = way.tags.get('name') + self.ref = way.tags.get('ref') + self.highway_type = way.tags.get("highway") + self.highway_rank = _HIGHWAY_RANK.get(self.highway_type, 1000) + try: + self.lanes = int(way.tags.get('lanes')) + except Exception: + self.lanes = 2 + + # Create numpy arrays with nodes data to support calculations. + self._nodes_np = np.radians(np.array([[nd.lat, nd.lon] for nd in way.nodes], dtype=float)) + self._nodes_ids = np.array([nd.id for nd in way .nodes], dtype=int) + + # Get the vectors representation of the segments betwheen consecutive nodes. (N-1, 2) + v = vectors(self._nodes_np) * R + + # Calculate the vector magnitudes (or distance) between nodes. (N-1) + self._way_distances = np.linalg.norm(v, axis=1) + + # Calculate the bearing (from true north clockwise) for every section of the way (vectors between nodes). (N-1) + self._way_bearings = np.arctan2(v[:, 0], v[:, 1]) + + # Define bounding box to ease the process of locating a node in a way. + # [[min_lat, min_lon], [max_lat, max_lon]] + self.bbox = np.row_stack((np.amin(self._nodes_np, 0) - _WAY_BBOX_PADING, + np.amax(self._nodes_np, 0) + _WAY_BBOX_PADING)) + + # Get the edge nodes ids. + self.edge_nodes_ids = [way.nodes[0].id, way.nodes[-1].id] + + def __repr__(self): + return f'(id: {self.id}, between {self.behind_idx} and {self.ahead_idx}, {self.direction}, active: {self.active})' + + def __eq__(self, other): + if isinstance(other, WayRelation): + return self.id == other.id + return False + + def reset_location_variables(self): + self.distance_to_node_ahead = 0. + self.location_rad = None + self.bearing_rad = None + self.active = False + self.diverting = False + self.ahead_idx = None + self.behind_idx = None + self._active_bearing_delta = None + self._distance_to_way = None + + @property + def id(self): + return self.way.id + + @property + def road_name(self): + if self.name is not None: + return self.name + return self.ref + + def update(self, location_rad, bearing_rad, location_stdev): + """Will update and validate the associated way with a given `location_rad` and `bearing_rad`. + Specifically it will find the nodes behind and ahead of the current location and bearing. + If no proper fit to the way geometry, the way relation is marked as invalid. + """ + self.reset_location_variables() + + # Ignore if location not in way bounding box + if not self.is_location_in_bbox(location_rad): + return + + # - Get the distance and bearings from location to all nodes. (N) + bearings = bearing_to_points(location_rad, self._nodes_np) + distances = distance_to_points(location_rad, self._nodes_np) + + # - Get absolute bearing delta to current driving bearing. (N) + delta = np.abs(bearing_rad - bearings) + + # - Nodes are ahead if the cosine of the delta is positive (N) + is_ahead = np.cos(delta) >= 0. + + # - Possible locations on the way are those where adjacent nodes change from ahead to behind or viceversa. + possible_idxs = np.nonzero(np.diff(is_ahead))[0] + + # - when no possible locations found, then the location is not in this way. + if len(possible_idxs) == 0: + return + + # - Find then angle formed between the vectors from the current location to consecutive nodes. This is the + # value of the difference in the bearings of the vectors. + teta = np.diff(bearings) + + # - When two consecutive nodes will be ahead and behind, they will form a triangle with the current location. + # We find the closest distance to the way by solving the area of the triangle and finding the height (h). + # We must use the abolute value of the sin of the angle in the formula, which is equivalent to ensure we + # are considering the smallest of the two angles formed between the two vectors. + # https://www.mathsisfun.com/algebra/trig-area-triangle-without-right-angle.html + h = distances[:-1] * distances[1:] * np.abs(np.sin(teta)) / self._way_distances + + # - Calculate the delta between driving bearing and way bearings. (N-1) + bw_delta = self._way_bearings - bearing_rad + + # - The absolut value of the sin of `bw_delta` indicates how close the bearings match independent of direction. + # We will use this value along the distance to the way to aid on way selection. (N-1) + abs_sin_bw_delta = np.abs(np.sin(bw_delta)) + + # - Get the delta to way bearing indicators and the distance to the way for the possible locations. + abs_sin_bw_delta_possible = abs_sin_bw_delta[possible_idxs] + h_possible = h[possible_idxs] + + # - Get the index where the distance to the way is minimum. That is the chosen location. + min_h_possible_idx = np.argmin(h_possible) + min_delta_idx = possible_idxs[min_h_possible_idx] + + # - If the distance to the way is over 4 standard deviations of the gps accuracy + half the maximum road width + # estimate, then we are way too far to stick to this way (i.e. we are not on this way anymore) + half_road_width_estimate = self.lanes * LANE_WIDTH / 2. + if h_possible[min_h_possible_idx] > 4. * location_stdev + half_road_width_estimate: + return + + # - If the distance to the road is greater than 2 standard deviations of the gps accuracy + half the maximum road + # width estimate then we are most likely diverting from this route. + diverting = h_possible[min_h_possible_idx] > 2. * location_stdev + half_road_width_estimate + + # Populate location variables with result + if is_ahead[min_delta_idx]: + self.direction = DIRECTION.BACKWARD + self.ahead_idx = min_delta_idx + self.behind_idx = min_delta_idx + 1 + else: + self.direction = DIRECTION.FORWARD + self.ahead_idx = min_delta_idx + 1 + self.behind_idx = min_delta_idx + + self._distance_to_way = h[min_delta_idx] + self._active_bearing_delta = abs_sin_bw_delta_possible[min_h_possible_idx] + # TODO: The distance to node ahead currently represent the distance from the GPS fix location. + # It would be perhaps more accurate to use the distance on the projection over the direct line between + # the two nodes. + self.distance_to_node_ahead = distances[self.ahead_idx] + self.active = True + self.diverting = diverting + self.location_rad = location_rad + self.bearing_rad = bearing_rad + self._speed_limit = None + + def update_direction_from_starting_node(self, start_node_id): + self._speed_limit = None + if self.edge_nodes_ids[0] == start_node_id: + self.direction = DIRECTION.FORWARD + elif self.edge_nodes_ids[-1] == start_node_id: + self.direction = DIRECTION.BACKWARD + else: + self.direction = DIRECTION.NONE + + def is_location_in_bbox(self, location_rad): + """Indicates if a given location is contained in the bounding box surrounding the way. + self.bbox = [[min_lat, min_lon], [max_lat, max_lon]] + """ + is_g = np.greater_equal(location_rad, self.bbox[0, :]) + is_l = np.less_equal(location_rad, self.bbox[1, :]) + + return np.all(np.concatenate((is_g, is_l))) + + @property + def speed_limit(self): + if self._speed_limit is not None: + return self._speed_limit + + # Get string from corresponding tag, consider conditional limits first. + limit_string = self.way.tags.get("maxspeed:conditional") + if limit_string is None: + if self.direction == DIRECTION.FORWARD: + limit_string = self.way.tags.get("maxspeed:forward:conditional") + elif self.direction == DIRECTION.BACKWARD: + limit_string = self.way.tags.get("maxspeed:backward:conditional") + + limit = conditional_speed_limit_for_osm_tag_limit_string(limit_string) + + # When no conditional limit set, attempt to get from regular speed limit tags. + if limit == 0.: + limit_string = self.way.tags.get("maxspeed") + if limit_string is None: + if self.direction == DIRECTION.FORWARD: + limit_string = self.way.tags.get("maxspeed:forward") + elif self.direction == DIRECTION.BACKWARD: + limit_string = self.way.tags.get("maxspeed:backward") + + limit = speed_limit_for_osm_tag_limit_string(limit_string) + + self._speed_limit = limit + return self._speed_limit + + @property + def active_bearing_delta(self): + """Returns the sine of the delta between the current location bearing and the exact + bearing of the portion of way we are currentluy located at. + """ + return self._active_bearing_delta + + @property + def is_one_way(self): + return self._one_way in ['yes'] or self.highway_type in ["motorway"] + + @property + def is_prohibited(self): + # Direction must be defined to asses this property. Default to `True` if not. + if self.direction == DIRECTION.NONE: + return True + return self.is_one_way and self.direction == DIRECTION.BACKWARD + + @property + def distance_to_way(self): + """Returns the perpendicular (i.e. minimum) distance between current location and the way + """ + return self._distance_to_way + + @property + def node_ahead(self): + return self.way.nodes[self.ahead_idx] if self.ahead_idx is not None else None + + @property + def last_node(self): + """Returns the last node on the way considering the traveling direction + """ + if self.direction == DIRECTION.FORWARD: + return self.way.nodes[-1] + if self.direction == DIRECTION.BACKWARD: + return self.way.nodes[0] + return None + + @property + def last_node_coordinates(self): + """Returns the coordinates for the last node on the way considering the traveling direction. (in radians) + """ + if self.direction == DIRECTION.FORWARD: + return self._nodes_np[-1] + if self.direction == DIRECTION.BACKWARD: + return self._nodes_np[0] + return None + + def node_before_edge_coordinates(self, node_id): + """Returns the coordinates of the node before the edge node identifeid with `node_id`. (in radians) + """ + if self.edge_nodes_ids[0] == node_id: + return self._nodes_np[1] + + if self.edge_nodes_ids[-1] == node_id: + return self._nodes_np[-2] + + return np.array([0., 0.]) + + def split(self, node_id, way_ids=None): + """ Returns and array with the way relations resulting from spliting the current way relation at node_id + """ + idxs = np.nonzero(self._nodes_ids == node_id)[0] + if len(idxs) == 0: + return [] + + idx = idxs[0] + if idx == 0 or idx == len(self._nodes_ids) - 1: + return [self] + + if not isinstance(way_ids, list): + way_ids = [-1, -2] # Default id values. + + ways = [create_way(way_ids[0], node_ids=self._nodes_ids[:idx + 1], from_way=self.way), + create_way(way_ids[1], node_ids=self._nodes_ids[idx:], from_way=self.way)] + return [WayRelation(way, parent=self) for way in ways] diff --git a/selfdrive/mapd/lib/WayRelationIndex.py b/selfdrive/mapd/lib/WayRelationIndex.py new file mode 100644 index 000000000..e941dcd5b --- /dev/null +++ b/selfdrive/mapd/lib/WayRelationIndex.py @@ -0,0 +1,34 @@ + + +class WayRelationIndex(): + """ + A class containing an index of WayRelations by node ids of internal nodes and edge nodes. + """ + def __init__(self, way_relations): + self._edge_nodes_index_dict = {} + self._full_nodes_index_dict = {} + + for wr in way_relations: + self.add(wr) + + def add(self, way_relation): + for node in way_relation.way.nodes: + node_id = node.id + self._full_nodes_index_dict[node_id] = self._full_nodes_index_dict.get(node_id, []) + [way_relation] + if node_id in way_relation.edge_nodes_ids: + self._edge_nodes_index_dict[node_id] = self._edge_nodes_index_dict.get(node_id, []) + [way_relation] + + def remove(self, way_relation): + for node in way_relation.way.nodes: + node_id = node.id + self._full_nodes_index_dict[node_id] = [wr for wr in self._full_nodes_index_dict.get(node_id, []) + if wr is not way_relation] + if node_id in way_relation.edge_nodes_ids: + self._edge_nodes_index_dict[node_id] = [wr for wr in self._edge_nodes_index_dict.get(node_id, []) + if wr is not way_relation] + + def way_relations_with_edge_node_id(self, node_id): + return self._edge_nodes_index_dict.get(node_id, []) + + def way_relations_with_node_id(self, node_id): + return self._full_nodes_index_dict.get(node_id, []) diff --git a/selfdrive/mapd/lib/default_speeds.json b/selfdrive/mapd/lib/default_speeds.json new file mode 100644 index 000000000..a8db60880 --- /dev/null +++ b/selfdrive/mapd/lib/default_speeds.json @@ -0,0 +1,111 @@ +{ + "_comment": "These speeds are from https://wiki.openstreetmap.org/wiki/Speed_limits Special cases have been stripped", + "AR:urban": "40", + "AR:urban:primary": "60", + "AR:urban:secondary": "60", + "AR:rural": "110", + "AT:urban": "50", + "AT:rural": "100", + "AT:trunk": "100", + "AT:motorway": "130", + "BE:urban": "50", + "BE-VLG:rural": "70", + "BE-WAL:rural": "90", + "BE:trunk": "120", + "BE:motorway": "120", + "CH:urban[1]": "50", + "CH:rural": "80", + "CH:trunk": "100", + "CH:motorway": "120", + "CZ:pedestrian_zone": "20", + "CZ:living_street": "20", + "CZ:urban": "50", + "CZ:urban_trunk": "80", + "CZ:urban_motorway": "80", + "CZ:rural": "90", + "CZ:trunk": "110", + "CZ:motorway": "130", + "DK:urban": "50", + "DK:rural": "80", + "DK:motorway": "130", + "DE:living_street": "7", + "DE:residential": "30", + "DE:urban": "50", + "DE:rural": "100", + "DE:trunk": "none", + "DE:motorway": "none", + "FI:urban": "50", + "FI:rural": "80", + "FI:trunk": "100", + "FI:motorway": "120", + "FR:urban": "50", + "FR:rural": "80", + "FR:trunk": "110", + "FR:motorway": "130", + "GR:urban": "50", + "GR:rural": "90", + "GR:trunk": "110", + "GR:motorway": "130", + "HU:urban": "50", + "HU:rural": "90", + "HU:trunk": "110", + "HU:motorway": "130", + "IT:urban": "50", + "IT:rural": "90", + "IT:trunk": "110", + "IT:motorway": "130", + "JP:national": "60", + "JP:motorway": "100", + "LT:living_street": "20", + "LT:urban": "50", + "LT:rural": "90", + "LT:trunk": "120", + "LT:motorway": "130", + "PL:living_street": "20", + "PL:urban": "50", + "PL:rural": "90", + "PL:trunk": "100", + "PL:motorway": "140", + "RO:urban": "50", + "RO:rural": "90", + "RO:trunk": "100", + "RO:motorway": "130", + "RU:living_street": "20", + "RU:urban": "60", + "RU:rural": "90", + "RU:motorway": "110", + "SK:urban": "50", + "SK:rural": "90", + "SK:trunk": "90", + "SK:motorway": "90", + "SI:urban": "50", + "SI:rural": "90", + "SI:trunk": "110", + "SI:motorway": "130", + "ES:living_street": "20", + "ES:urban": "50", + "ES:rural": "50", + "ES:trunk": "90", + "ES:motorway": "120", + "SE:urban": "50", + "SE:rural": "70", + "SE:trunk": "90", + "SE:motorway": "110", + "GB:nsl_restricted": "30 mph", + "GB:nsl_single": "60 mph", + "GB:nsl_dual": "70 mph", + "GB:motorway": "70 mph", + "UA:urban": "50", + "UA:rural": "90", + "UA:trunk": "110", + "UA:motorway": "130", + "UZ:living_street": "30", + "UZ:urban": "70", + "UZ:rural": "100", + "UZ:motorway": "110", + "ZA:trunk": "120", + "ZA:residential": "60", + "ZA:rural": "100", + "ZA:urban": "60", + "ZA:motorway": "120" +} diff --git a/selfdrive/mapd/lib/geo.py b/selfdrive/mapd/lib/geo.py new file mode 100644 index 000000000..8f6232225 --- /dev/null +++ b/selfdrive/mapd/lib/geo.py @@ -0,0 +1,66 @@ +from enum import Enum +import numpy as np + + +R = 6373000.0 # approximate radius of earth in mts + + +def vectors(points): + """Provides a array of vectors on cartesian space (x, y). + Each vector represents the path from a point in `points` to the next. + `points` must by a (N, 2) array of [lat, lon] pairs in radians. + """ + latA = points[:-1, 0] + latB = points[1:, 0] + delta = np.diff(points, axis=0) + dlon = delta[:, 1] + + x = np.sin(dlon) * np.cos(latB) + y = np.cos(latA) * np.sin(latB) - (np.sin(latA) * np.cos(latB) * np.cos(dlon)) + + return np.column_stack((x, y)) + + +def ref_vectors(ref, points): + """Provides a array of vectors on cartesian space (x, y). + Each vector represents the path from ref to a point in `points`. + `points` must by a (N, 2) array of [lat, lon] pairs in radians. + """ + latA = ref[0] + latB = points[:, 0] + delta = points - ref + dlon = delta[:, 1] + + x = np.sin(dlon) * np.cos(latB) + y = np.cos(latA) * np.sin(latB) - (np.sin(latA) * np.cos(latB) * np.cos(dlon)) + + return np.column_stack((x, y)) + + +def bearing_to_points(point, points): + """Calculate the bearings (angle from true north clockwise) of the vectors between `point` and each + one of the entries in `points`. Both `point` and `points` elements are 2 element arrays containing a latitud, + longitude pair in radians. + """ + delta = points - point + x = np.sin(delta[:, 1]) * np.cos(points[:, 0]) + y = np.cos(point[0]) * np.sin(points[:, 0]) - (np.sin(point[0]) * np.cos(points[:, 0]) * np.cos(delta[:, 1])) + return np.arctan2(x, y) + + +def distance_to_points(point, points): + """Calculate the distance of the vectors between `point` and each one of the entries in `points`. + Both `point` and `points` elements are 2 element arrays containing a latitud, longitude pair in radians. + """ + delta = points - point + a = np.sin(delta[:, 0] / 2)**2 + np.cos(point[0]) * np.cos(points[:, 0]) * np.sin(delta[:, 1] / 2)**2 + c = 2 * np.arctan2(np.sqrt(a), np.sqrt(1 - a)) + return c * R + + +class DIRECTION(Enum): + NONE = 0 + AHEAD = 1 + BEHIND = 2 + FORWARD = 3 + BACKWARD = 4 diff --git a/selfdrive/mapd/lib/osm.py b/selfdrive/mapd/lib/osm.py new file mode 100644 index 000000000..2fd8f4da4 --- /dev/null +++ b/selfdrive/mapd/lib/osm.py @@ -0,0 +1,80 @@ +import overpy +import subprocess +import numpy as np +from selfdrive.mapd.lib.geo import R +import os +from selfdrive.mapd.config import QUERY_RADIUS + +OSM_QUERY = ["/data/media/0/osm/bin/osm3s_query", "--db-dir=/data/media/0/osm/db/"] + +def create_way(way_id, node_ids, from_way): + """ + Creates and OSM Way with the given `way_id` and list of `node_ids`, copying attributes and tags from `from_way` + """ + return overpy.Way(way_id, node_ids=node_ids, attributes={}, result=from_way._result, + tags=from_way.tags) + + +class OSM(): + def __init__(self, last_gps_pos): + self.api = overpy.Overpass() + # self.api = overpy.Overpass(url='http://3.65.170.21/api/interpreter') + + self.local_osm_query_fail_count = 0 + self.ways = [] + + self.last_gps_pos = last_gps_pos + + if os.path.isdir("/data/media/0/osm/bin/") and os.path.isdir("/data/media/0/osm/db/"): + self.local_osm_enabled = True + print("Local OSM installed") + try: + print("Testing local OSM...") + if len(last_gps_pos) > 0: + ways = self.fetch_road_ways_around_location(last_gps_pos['latitude'], last_gps_pos['longitude'], QUERY_RADIUS) + self.local_osm_enabled = len(ways) > 0 + if self.local_osm_enabled: + self.ways = ways + + except Exception as e: + self.local_osm_enabled = False + print(f"Local OSM test failed:\n{e}") + else: + self.local_osm_enabled = False + print("Local OSM enabled = %s" % self.local_osm_enabled) + + def fetch_road_ways_around_location(self, lat, lon, radius): + # when lat/lon is same as last_gps_pos lat, lon, we can just use cached way data + # this only happened when we are using lastGPSPosition params + if len(self.ways) > 0 and len(self.last_gps_pos) > 0 and self.last_gps_pos["latitude"] == lat and self.last_gps_pos["longitude"] == lon: + return self.ways + # Calculate the bounding box coordinates for the bbox containing the circle around location. + bbox_angle = np.degrees(radius / R) + # fetch all ways and nodes on this ways in bbox + bbox_str = f'{str(lat - bbox_angle)},{str(lon - bbox_angle)},{str(lat + bbox_angle)},{str(lon + bbox_angle)}' + q = """ + way(""" + bbox_str + """) + [highway] + [highway!~"^(footway|path|corridor|bridleway|steps|cycleway|construction|bus_guideway|escape|service|track)$"]; + (._;>;); + out; + """ + if self.local_osm_enabled: + try: + completion = subprocess.run(OSM_QUERY + [f"--request={q}"], check=True, capture_output=True) + self.ways = self.api.parse_xml(completion.stdout).ways + + except Exception as e: + self.local_osm_query_fail_count += 1 + print(f'Exception while querying local OSM:\n{e}') + pass + + # use remote OSM when local osm is not enabled or failed too many times + if not self.local_osm_enabled or self.local_osm_query_fail_count >= 5: + try: + self.ways = self.api.query(q).ways + self.local_osm_query_fail_count = 0 + except Exception as e: + print(f'Exception while querying OSM:\n{e}') + + return self.ways diff --git a/selfdrive/mapd/mapd.py b/selfdrive/mapd/mapd.py new file mode 100644 index 000000000..5e0be0e34 --- /dev/null +++ b/selfdrive/mapd/mapd.py @@ -0,0 +1,325 @@ +#!/usr/bin/env python3 +import threading +from traceback import print_exception +import numpy as np +# from time import strftime, gmtime +import cereal.messaging as messaging +from common.realtime import Ratekeeper, set_core_affinity, set_realtime_priority +from selfdrive.mapd.lib.osm import OSM +from selfdrive.mapd.lib.geo import distance_to_points +from selfdrive.mapd.lib.WayCollection import WayCollection +from selfdrive.mapd.config import QUERY_RADIUS, MIN_DISTANCE_FOR_NEW_QUERY, FULL_STOP_MAX_SPEED, LOOK_AHEAD_HORIZON_TIME +from system.swaglog import cloudlog +from common.params import Params +import json +from cereal import log +import math + + +_DEBUG = False +_CLOUDLOG_DEBUG = True + + +def _debug(msg, log_to_cloud=True): + if _CLOUDLOG_DEBUG and log_to_cloud: + cloudlog.debug(msg) + if _DEBUG: + print(msg) + + +def excepthook(args): + _debug(f'MapD: Threading exception:\n{args}') + print_exception(args.exc_type, args.exc_value, args.exc_traceback) + + +threading.excepthook = excepthook + + +class MapD(): + def __init__(self, position_service): + last_gps_params = Params().get('LastGPSPosition') + self.last_gps_pos = json.loads(last_gps_params) if last_gps_params is not None else [] + + self.osm = OSM(self.last_gps_pos) + self.way_collection = None + self.route = None + self.last_gps_fix_timestamp = 0 + self.last_gps = None + self.location_deg = None # The current location in degrees. + self.location_rad = None # The current location in radians as a Numpy array. + self.bearing_rad = None + self.location_stdev = None # The current location accuracy in mts. 1 standard devitation. + self.gps_speed = 0. + self.last_fetch_location = None + self.last_route_update_fix_timestamp = 0 + self.last_publish_fix_timestamp = 0 + # self._op_enabled = False + self._disengaging = False + self._query_thread = None + self._lock = threading.RLock() + self.position_service = position_service + + # def udpate_state(self, sm): + # sock = 'controlsState' + # if not sm.updated[sock] or not sm.valid[sock]: + # return + # + # controls_state = sm[sock] + # self._disengaging = not controls_state.enabled and self._op_enabled + # self._op_enabled = controls_state.enabled + + def apply_last_gps_pos(self): + # use last gps position before first fixed + if self.last_gps_pos is not None and len(self.last_gps_pos) > 0: + self.location_rad = np.radians(np.array([self.last_gps_pos["latitude"], self.last_gps_pos["longitude"]], dtype=float)) + self.location_deg = (self.last_gps_pos["latitude"], self.last_gps_pos["longitude"]) + self.bearing_rad = np.radians(0., dtype=float) + self.location_stdev = 5. + + def update_position(self, sm): + if self.position_service == "liveLocationKalman": + self.update_locationd(sm) + else: + self.update_gps(sm) + + def update_car_state(self, sm): + sock = 'carState' + if not sm.updated[sock] or not sm.valid[sock]: + return + car_state = sm[sock] + self.gps_speed = car_state.vEgo + self._disengaging = abs(car_state.steeringRateDeg) > 60 + + def update_locationd(self, sm): + sock = 'liveLocationKalman' + if not sm.updated[sock] or not sm.valid[sock]: + return + + location = sm[sock] + location_valid = (location.status == log.LiveLocationKalman.Status.valid) and location.positionGeodetic.valid + + if not location_valid: + return + + lat = location.positionGeodetic.value[0] + lon = location.positionGeodetic.value[1] + self.last_gps_fix_timestamp = location.unixTimestampMillis + self.location_rad = np.radians(np.array([lat, lon], dtype=float)) + self.location_deg = (lat, lon) + self.bearing_rad = np.radians(math.degrees(location.calibratedOrientationNED.value[2]), dtype=float) + # use actual car speed + # self.gps_speed = log.speed + self.location_stdev = 1.25 + + def update_gps(self, sm): + sock = 'gpsLocationExternal' + if not sm.updated[sock] or not sm.valid[sock]: + return + + log = sm[sock] + self.last_gps = log + + # ignore the message if the fix is invalid + if log.flags % 2 == 0: + return + + self.last_gps_fix_timestamp = log.unixTimestampMillis # Unix TS. Milliseconds since January 1, 1970. + self.location_rad = np.radians(np.array([log.latitude, log.longitude], dtype=float)) + self.location_deg = (log.latitude, log.longitude) + self.bearing_rad = np.radians(log.bearingDeg, dtype=float) + # use actual car speed + # self.gps_speed = log.speed + self.location_stdev = log.accuracy # log accuracies are presumably 1 standard deviation. + + # _debug('Mapd: ********* Got GPS fix' + # + f'Pos: {self.location_deg} +/- {self.location_stdev * 2.} mts.\n' + # + f'Bearing: {log.bearingDeg} +/- {log.bearingAccuracyDeg * 2.} deg.\n' + # + f'timestamp: {strftime("%d-%m-%y %H:%M:%S", gmtime(self.last_gps_fix_timestamp * 1e-3))}' + # + '*******', log_to_cloud=False) + + def _query_osm_not_blocking(self): + def query(osm, location_deg, location_rad, radius): + _debug(f'Mapd: Start query for OSM map data at {location_deg}') + lat, lon = location_deg + ways = osm.fetch_road_ways_around_location(lat, lon, radius) + _debug(f'Mapd: Query to OSM finished with {len(ways)} ways') + + # Only issue an update if we received some ways. Otherwise it is most likely a conectivity issue. + # Will retry on next loop. + if len(ways) > 0: + new_way_collection = WayCollection(ways, location_rad) + + # Use the lock to update the way_collection as it might be being used to update the route. + _debug('Mapd: Locking to write results from osm.', log_to_cloud=False) + with self._lock: + self.way_collection = new_way_collection + self.last_fetch_location = location_rad + _debug(f'Mapd: Updated map data @ {location_deg} - got {len(ways)} ways') + + _debug('Mapd: Releasing Lock to write results from osm', log_to_cloud=False) + + # Ignore if we have a query thread already running. + if self._query_thread is not None and self._query_thread.is_alive(): + return + + self._query_thread = threading.Thread(target=query, args=(self.osm, self.location_deg, self.location_rad, + QUERY_RADIUS)) + self._query_thread.start() + + def updated_osm_data(self): + if self.route is not None: + distance_to_end = self.route.distance_to_end + if distance_to_end is not None and distance_to_end >= MIN_DISTANCE_FOR_NEW_QUERY: + # do not query as long as we have a route with enough distance ahead. + return + + if self.location_rad is None: + return + + if self.last_fetch_location is not None: + distance_since_last = distance_to_points(self.last_fetch_location, np.array([self.location_rad]))[0] + if distance_since_last < QUERY_RADIUS - MIN_DISTANCE_FOR_NEW_QUERY: + # do not query if are still not close to the border of previous query area + return + + self._query_osm_not_blocking() + + def update_route(self): + def update_proc(): + # Ensure we clear the route on op disengage, this way we can correct possible incorrect map data due + # to wrongly locating or picking up the wrong route. + if self._disengaging: + self.route = None + _debug('Mapd *****: Clearing Route as system is disengaging. ********') + + if self.way_collection is None or self.location_rad is None or self.bearing_rad is None: + _debug('Mapd *****: Can not update route. Missing WayCollection, location or bearing ********') + return + + if self.route is not None and self.last_route_update_fix_timestamp == self.last_gps_fix_timestamp: + _debug('Mapd *****: Skipping route update. No new fix since last update ********') + return + + self.last_route_update_fix_timestamp = self.last_gps_fix_timestamp + + # Create the route if not existent or if it was generated by an older way collection + if self.route is None or self.route.way_collection_id != self.way_collection.id: + self.route = self.way_collection.get_route(self.location_rad, self.bearing_rad, self.location_stdev) + _debug(f'Mapd *****: Route created: \n{self.route}\n********') + return + + # Do not attempt to update the route if the car is going close to a full stop, as the bearing can start + # jumping and creating unnecesary loosing of the route. Since the route update timestamp has been updated + # a new liveMapData message will be published with the current values (which is desirable) + if self.gps_speed < FULL_STOP_MAX_SPEED: + _debug('Mapd *****: Route Not updated as car has Stopped ********') + return + + self.route.update(self.location_rad, self.bearing_rad, self.location_stdev) + if self.route.located: + _debug(f'Mapd *****: Route updated: \n{self.route}\n********') + return + + # if an old route did not mange to locate, attempt to regenerate form way collection. + self.route = self.way_collection.get_route(self.location_rad, self.bearing_rad, self.location_stdev) + _debug(f'Mapd *****: Failed to update location in route. Regenerated with route: \n{self.route}\n********') + + # We use the lock when updating the route, as it reads `way_collection` which can ben updated by + # a new query result from the _query_thread. + _debug('Mapd: Locking to update route.', log_to_cloud=False) + with self._lock: + update_proc() + + _debug('Mapd: Releasing Lock to update route', log_to_cloud=False) + + def publish(self, pm): #, sm): + # Ensure we have a route currently located + if self.route is None or not self.route.located: + _debug('Mapd: Skipping liveMapData message as there is no route or is not located.') + return + + # Ensure we have a route update since last publish + if self.last_publish_fix_timestamp == self.last_route_update_fix_timestamp: + _debug('Mapd: Skipping liveMapData since there is no new gps fix.') + return + + self.last_publish_fix_timestamp = self.last_route_update_fix_timestamp + + speed_limit = self.route.current_speed_limit + next_speed_limit_section = self.route.next_speed_limit_section + # turn_speed_limit_section = self.route.current_curvature_speed_limit_section + # horizon_mts = self.gps_speed * LOOK_AHEAD_HORIZON_TIME + # next_turn_speed_limit_sections = self.route.next_curvature_speed_limit_sections(horizon_mts) + current_road_name = self.route.current_road_name + + map_data_msg = messaging.new_message('liveMapData') + # map_data_msg.valid = sm.all_alive(service_list=[self.position_service]) and \ + # sm.all_valid(service_list=[self.position_service]) + + # map_data_msg.liveMapData.lastGpsTimestamp = self.last_gps.unixTimestampMillis + # map_data_msg.liveMapData.lastGpsLatitude = float(self.last_gps.latitude) + # map_data_msg.liveMapData.lastGpsLongitude = float(self.last_gps.longitude) + # map_data_msg.liveMapData.lastGpsSpeed = float(self.last_gps.speed) + # map_data_msg.liveMapData.lastGpsBearingDeg = float(self.last_gps.bearingDeg) + # map_data_msg.liveMapData.lastGpsAccuracy = float(self.last_gps.accuracy) + # map_data_msg.liveMapData.lastGpsBearingAccuracyDeg = float(self.last_gps.bearingAccuracyDeg) + + map_data_msg.liveMapData.speedLimitValid = bool(speed_limit is not None) + map_data_msg.liveMapData.speedLimit = float(speed_limit if speed_limit is not None else 0.0) + map_data_msg.liveMapData.speedLimitAheadValid = bool(next_speed_limit_section is not None) + map_data_msg.liveMapData.speedLimitAhead = float(next_speed_limit_section.value + if next_speed_limit_section is not None else 0.0) + map_data_msg.liveMapData.speedLimitAheadDistance = float(next_speed_limit_section.start + if next_speed_limit_section is not None else 0.0) + + # map_data_msg.liveMapData.turnSpeedLimitValid = bool(turn_speed_limit_section is not None) + # map_data_msg.liveMapData.turnSpeedLimit = float(turn_speed_limit_section.value + # if turn_speed_limit_section is not None else 0.0) + # map_data_msg.liveMapData.turnSpeedLimitSign = int(turn_speed_limit_section.curv_sign + # if turn_speed_limit_section is not None else 0) + # map_data_msg.liveMapData.turnSpeedLimitEndDistance = float(turn_speed_limit_section.end + # if turn_speed_limit_section is not None else 0.0) + # map_data_msg.liveMapData.turnSpeedLimitsAhead = [float(s.value) for s in next_turn_speed_limit_sections] + # map_data_msg.liveMapData.turnSpeedLimitsAheadDistances = [float(s.start) for s in next_turn_speed_limit_sections] + # map_data_msg.liveMapData.turnSpeedLimitsAheadSigns = [float(s.curv_sign) for s in next_turn_speed_limit_sections] + + map_data_msg.liveMapData.currentRoadName = str(current_road_name if current_road_name is not None else "") + + pm.send('liveMapData', map_data_msg) + _debug(f'Mapd *****: Publish: \n{map_data_msg}\n********', log_to_cloud=False) + + +# provides live map data information +def mapd_thread(sm=None, pm=None): + use_locationd = False + position_service = "liveLocationKalman" if use_locationd else "gpsLocationExternal" + + set_core_affinity([1,]) + set_realtime_priority(1) + mapd = MapD(position_service) + rk = Ratekeeper(1., print_delay_threshold=None) # Keeps rate at 1 hz + + # *** setup messaging + if sm is None: + sm = messaging.SubMaster(["carState", position_service]) + if pm is None: + pm = messaging.PubMaster(['liveMapData']) + + mapd.apply_last_gps_pos() + while True: + sm.update() + # mapd.udpate_state(sm) + mapd.update_car_state(sm) + mapd.update_position(sm) + mapd.updated_osm_data() + mapd.update_route() + mapd.publish(pm) #, sm) + rk.keep_time() + + +def main(sm=None, pm=None): + mapd_thread(sm, pm) + + +if __name__ == "__main__": + main() diff --git a/selfdrive/mapd/mapd_helpers.py b/selfdrive/mapd/mapd_helpers.py new file mode 100644 index 000000000..58a33ee41 --- /dev/null +++ b/selfdrive/mapd/mapd_helpers.py @@ -0,0 +1,364 @@ +import math +import json +import numpy as np +from datetime import datetime +from common.basedir import BASEDIR +from selfdrive.config import Conversions as CV +from common.transformations.coordinates import LocalCoord, geodetic2ecef + +LOOKAHEAD_TIME = 10. +MAPS_LOOKAHEAD_DISTANCE = 50 * LOOKAHEAD_TIME + +DEFAULT_SPEEDS_JSON_FILE = BASEDIR + "/selfdrive/mapd/default_speeds.json" +DEFAULT_SPEEDS = {} +with open(DEFAULT_SPEEDS_JSON_FILE, "rb") as f: + DEFAULT_SPEEDS = json.loads(f.read()) + +DEFAULT_SPEEDS_BY_REGION_JSON_FILE = BASEDIR + "/selfdrive/mapd/default_speeds_by_region.json" +DEFAULT_SPEEDS_BY_REGION = {} +with open(DEFAULT_SPEEDS_BY_REGION_JSON_FILE, "rb") as f: + DEFAULT_SPEEDS_BY_REGION = json.loads(f.read()) + +def circle_through_points(p1, p2, p3): + """Fits a circle through three points + Formulas from: http://www.ambrsoft.com/trigocalc/circle3d.htm""" + x1, y1, _ = p1 + x2, y2, _ = p2 + x3, y3, _ = p3 + + A = x1 * (y2 - y3) - y1 * (x2 - x3) + x2 * y3 - x3 * y2 + B = (x1**2 + y1**2) * (y3 - y2) + (x2**2 + y2**2) * (y1 - y3) + (x3**2 + y3**2) * (y2 - y1) + C = (x1**2 + y1**2) * (x2 - x3) + (x2**2 + y2**2) * (x3 - x1) + (x3**2 + y3**2) * (x1 - x2) + D = (x1**2 + y1**2) * (x3 * y2 - x2 * y3) + (x2**2 + y2**2) * (x1 * y3 - x3 * y1) + (x3**2 + y3**2) * (x2 * y1 - x1 * y2) + + return (-B / (2 * A), - C / (2 * A), np.sqrt((B**2 + C**2 - 4 * A * D) / (4 * A**2))) + +def parse_speed_unit(max_speed): + """Converts a maxspeed string to m/s based on the unit present in the input. + OpenStreetMap defaults to kph if no unit is present. """ + + if not max_speed: + return None + + conversion = CV.KPH_TO_MS + if 'mph' in max_speed: + max_speed = max_speed.replace(' mph', '') + conversion = CV.MPH_TO_MS + try: + return float(max_speed) * conversion + except ValueError: + return None + +def parse_speed_tags(tags): + """Parses tags on a way to find the maxspeed string""" + max_speed = None + + if 'maxspeed' in tags: + max_speed = tags['maxspeed'] + + if 'maxspeed:conditional' in tags: + try: + max_speed_cond, cond = tags['maxspeed:conditional'].split(' @ ') + cond = cond[1:-1] + + start, end = cond.split('-') + now = datetime.now() # TODO: Get time and timezone from gps fix so this will work correctly on replays + start = datetime.strptime(start, "%H:%M").replace(year=now.year, month=now.month, day=now.day) + end = datetime.strptime(end, "%H:%M").replace(year=now.year, month=now.month, day=now.day) + + if start <= now <= end: + max_speed = max_speed_cond + except ValueError: + pass + + if not max_speed and 'source:maxspeed' in tags: + max_speed = DEFAULT_SPEEDS.get(tags['source:maxspeed'], None) + if not max_speed and 'maxspeed:type' in tags: + max_speed = DEFAULT_SPEEDS.get(tags['maxspeed:type'], None) + + max_speed = parse_speed_unit(max_speed) + return max_speed + +def geocode_maxspeed(tags, location_info): + max_speed = None + try: + geocode_country = location_info.get('country', '') + geocode_region = location_info.get('region', '') + + country_rules = DEFAULT_SPEEDS_BY_REGION.get(geocode_country, {}) + country_defaults = country_rules.get('Default', []) + for rule in country_defaults: + rule_valid = all( + tag_name in tags + and tags[tag_name] == value + for tag_name, value in rule['tags'].items() + ) + if rule_valid: + max_speed = rule['speed'] + break #stop searching country + + region_rules = country_rules.get(geocode_region, []) + for rule in region_rules: + rule_valid = all( + tag_name in tags + and tags[tag_name] == value + for tag_name, value in rule['tags'].items() + ) + if rule_valid: + max_speed = rule['speed'] + break #stop searching region + except KeyError: + pass + max_speed = parse_speed_unit(max_speed) + return max_speed + +class Way: + def __init__(self, way, query_results): + self.id = way.id + self.way = way + self.query_results = query_results + + points = list() + + for node in self.way.get_nodes(resolve_missing=False): + points.append((float(node.lat), float(node.lon), 0.)) + + self.points = np.asarray(points) + + @classmethod + def closest(cls, query_results, lat, lon, heading, prev_way=None): + results, tree, real_nodes, node_to_way, location_info = query_results + + cur_pos = geodetic2ecef((lat, lon, 0)) + nodes = tree.query_ball_point(cur_pos, 500) + + # If no nodes within 500m, choose closest one + if not nodes: + nodes = [tree.query(cur_pos)[1]] + + ways = [] + for n in nodes: + real_node = real_nodes[n] + ways += node_to_way[real_node.id] + ways = set(ways) + + closest_way = None + best_score = None + for way in ways: + way = Way(way, query_results) + points = way.points_in_car_frame(lat, lon, heading) + + on_way = way.on_way(lat, lon, heading, points) + if not on_way: + continue + + # Create mask of points in front and behind + x = points[:, 0] + y = points[:, 1] + angles = np.arctan2(y, x) + front = np.logical_and((-np.pi / 2) < angles, + angles < (np.pi / 2)) + behind = np.logical_not(front) + + dists = np.linalg.norm(points, axis=1) + + # Get closest point behind the car + dists_behind = np.copy(dists) + dists_behind[front] = np.NaN + closest_behind = points[np.nanargmin(dists_behind)] + + # Get closest point in front of the car + dists_front = np.copy(dists) + dists_front[behind] = np.NaN + closest_front = points[np.nanargmin(dists_front)] + + # fit line: y = a*x + b + x1, y1, _ = closest_behind + x2, y2, _ = closest_front + a = (y2 - y1) / max((x2 - x1), 1e-5) + b = y1 - a * x1 + + # With a factor of 60 a 20m offset causes the same error as a 20 degree heading error + # (A 20 degree heading offset results in an a of about 1/3) + score = abs(a) * 60. + abs(b) + + # Prefer same type of road + if prev_way is not None: + if way.way.tags.get('highway', '') == prev_way.way.tags.get('highway', ''): + score *= 0.5 + + if closest_way is None or score < best_score: + closest_way = way + best_score = score + + # Normal score is < 5 + if best_score > 50: + return None + + return closest_way + + def __str__(self): + return "%s %s" % (self.id, self.way.tags) + + def max_speed(self): + """Extracts the (conditional) speed limit from a way""" + if not self.way: + return None + + max_speed = parse_speed_tags(self.way.tags) + if not max_speed: + location_info = self.query_results[4] + max_speed = geocode_maxspeed(self.way.tags, location_info) + + return max_speed + + def max_speed_ahead(self, current_speed_limit, lat, lon, heading, lookahead): + """Look ahead for a max speed""" + if not self.way: + return None + + speed_ahead = None + speed_ahead_dist = None + lookahead_ways = 5 + way = self + for i in range(lookahead_ways): + way_pts = way.points_in_car_frame(lat, lon, heading) + + # Check current lookahead distance + max_dist = np.linalg.norm(way_pts[-1, :]) + + if max_dist > 2 * lookahead: + break + + if 'maxspeed' in way.way.tags: + spd = parse_speed_tags(way.way.tags) + if not spd: + location_info = self.query_results[4] + spd = geocode_maxspeed(way.way.tags, location_info) + if spd < current_speed_limit: + speed_ahead = spd + min_dist = np.linalg.norm(way_pts[1, :]) + speed_ahead_dist = min_dist + break + # Find next way + way = way.next_way() + if not way: + break + + return speed_ahead, speed_ahead_dist + + def advisory_max_speed(self): + if not self.way: + return None + + tags = self.way.tags + adv_speed = None + + if 'maxspeed:advisory' in tags: + adv_speed = tags['maxspeed:advisory'] + adv_speed = parse_speed_unit(adv_speed) + return adv_speed + + def on_way(self, lat, lon, heading, points=None): + if points is None: + points = self.points_in_car_frame(lat, lon, heading) + x = points[:, 0] + return np.min(x) < 0. and np.max(x) > 0. + + def closest_point(self, lat, lon, heading, points=None): + if points is None: + points = self.points_in_car_frame(lat, lon, heading) + i = np.argmin(np.linalg.norm(points, axis=1)) + return points[i] + + def distance_to_closest_node(self, lat, lon, heading, points=None): + if points is None: + points = self.points_in_car_frame(lat, lon, heading) + return np.min(np.linalg.norm(points, axis=1)) + + def points_in_car_frame(self, lat, lon, heading): + lc = LocalCoord.from_geodetic([lat, lon, 0.]) + + # Build rotation matrix + heading = math.radians(-heading + 90) + c, s = np.cos(heading), np.sin(heading) + rot = np.array([[c, s, 0.], [-s, c, 0.], [0., 0., 1.]]) + + # Convert to local coordinates + points_carframe = lc.geodetic2ned(self.points).T + + # Rotate with heading of car + points_carframe = np.dot(rot, points_carframe[(1, 0, 2), :]).T + + return points_carframe + + def next_way(self, backwards=False): + results, tree, real_nodes, node_to_way, location_info = self.query_results + + if backwards: + node = self.way.nodes[0] + else: + node = self.way.nodes[-1] + + ways = node_to_way[node.id] + + way = None + try: + # Simple heuristic to find next way + ways = [w for w in ways if w.id != self.id] + ways = [w for w in ways if w.nodes[0] == node] + + # Filter on highway tag + acceptable_tags = list() + cur_tag = self.way.tags['highway'] + acceptable_tags.append(cur_tag) + if cur_tag == 'motorway_link': + acceptable_tags.append('motorway') + acceptable_tags.append('trunk') + acceptable_tags.append('primary') + ways = [w for w in ways if w.tags['highway'] in acceptable_tags] + + # Filter on number of lanes + cur_num_lanes = int(self.way.tags['lanes']) + if len(ways) > 1: + ways_same_lanes = [w for w in ways if int(w.tags['lanes']) == cur_num_lanes] + if len(ways_same_lanes) == 1: + ways = ways_same_lanes + if len(ways) > 1: + ways = [w for w in ways if int(w.tags['lanes']) > cur_num_lanes] + if len(ways) == 1: + way = Way(ways[0], self.query_results) + + except (KeyError, ValueError): + pass + + return way + + def get_lookahead(self, lat, lon, heading, lookahead): + pnts = None + way = self + valid = False + + for i in range(5): + # Get new points and append to list + new_pnts = way.points_in_car_frame(lat, lon, heading) + + if pnts is None: + pnts = new_pnts + else: + pnts = np.vstack([pnts, new_pnts]) + + # Check current lookahead distance + max_dist = np.linalg.norm(pnts[-1, :]) + if max_dist > lookahead: + valid = True + + if max_dist > 2 * lookahead: + break + + # Find next way + way = way.next_way() + if not way: + break + + return pnts, valid diff --git a/selfdrive/mapd/test/__init__.py b/selfdrive/mapd/test/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/selfdrive/mapd/test/mock_data.py b/selfdrive/mapd/test/mock_data.py new file mode 100644 index 000000000..b7da229bd --- /dev/null +++ b/selfdrive/mapd/test/mock_data.py @@ -0,0 +1,266 @@ +from selfdrive.mapd.lib.WayCollection import WayCollection +from selfdrive.mapd.lib.geo import vectors, R +from selfdrive.mapd.lib.NodesData import _MIN_NODE_DISTANCE, _ADDED_NODES_DIST, _SPLINE_EVAL_STEP, \ + _MIN_SPEED_SECTION_LENGTH, nodes_raw_data_array_for_wr, node_calculations, is_wr_a_valid_divertion_from_node, \ + spline_curvature_calculations, speed_limits_for_curvatures_data +from scipy.interpolate import splev, splprep +import numpy as np +import overpy + + +class MockNodesData(): + def __init__(self, way_coords): + self.degrees = np.array(way_coords) + self.radians = np.radians(self.degrees) + + # ***************** + # Expected code implementation nodes_data + self.v = vectors(self.radians) * R + self.d = np.linalg.norm(self.v, axis=1) + self.b = np.arctan2(self.v[:, 0], self.v[:, 1]) + self.v = np.concatenate(([[0., 0.]], self.v)) + self.dp = np.concatenate(([0.], self.d)) + self.dn = np.concatenate((self.d, [0.])) + self.dr = np.cumsum(self.dp, axis=0) + self.b = np.concatenate((self.b, [self.b[-1]])) + + # Expected code implementation spline_curvature_calculations + vect = self.v + dist_prev = self.dp + too_far_idxs = np.nonzero(self.dp >= _MIN_NODE_DISTANCE)[0] + for idx in too_far_idxs[::-1]: + dp = dist_prev[idx] # distance of vector that needs to be replaced by higher resolution vectors. + n = int(np.ceil(dp / _ADDED_NODES_DIST)) # number of vectors that need to be added. + new_v = vect[idx, :] / n # new relative vector to insert. + vect = np.delete(vect, idx, axis=0) # remove the relative vector to be replaced by the insertion of new vectors. + vect = np.insert(vect, [idx] * n, [new_v] * n, axis=0) # insert n new relative vectors + ds = np.cumsum(dist_prev, axis=0) + vs = np.cumsum(vect, axis=0) + tck, u = splprep([vs[:, 0], vs[:, 1]]) # pylint: disable=W0632 + n = max(int(ds[-1] / _SPLINE_EVAL_STEP), len(u)) + unew = np.arange(0, n + 1) / n + d1 = splev(unew, tck, der=1) + d2 = splev(unew, tck, der=2) + num = d1[0] * d2[1] - d1[1] * d2[0] + den = (d1[0]**2 + d1[1]**2)**(1.5) + self.curv = num / den + self.curv_ds = unew * ds[-1] + # ***************** + + +class MockCurveSection(): + def __init__(self, func, di=0., df=1000., step=10.): + self.di = di + self.df = df + self.n = (df - di) // step + self.u = np.arange(0, self.n + 1) / self.n + self.curv_ds = self.u * (df - di) + di + self.curv = func(self.u) + self.curv_abs = np.abs(self.curv) + self.curv_sec = np.column_stack((self.curv_abs, np.sign(self.curv), self.curv_ds)) + + +class MockOSMQueryResponse(): + def __init__(self, xml_path, query_center): + self.api = overpy.Overpass() + self.query_center = np.radians(np.array(query_center)) + + with open(xml_path, 'r') as f: + overpass_xml = f.read() + self.ways = self.api.parse_xml(overpass_xml).ways + + self.wayCollection = WayCollection(self.ways, self.query_center) + +class MockRouteData(): + def __init__(self, way_ids, way_collection, first_node_id): # way)ids must be in order forming a route. + self.wrs = [next(wr for wr in way_collection.way_relations if wr.id == way_id) for way_id in way_ids] + self.way_collection = way_collection + self.first_node_id = first_node_id + + def reset(self): + way_relations = self.wrs + wr_index = self.way_collection.wr_index + + # Nodes Data processing expects way relations to be updated with direction before running. + for idx, wr in enumerate(way_relations): + if idx == 0: + wr.update_direction_from_starting_node(self.first_node_id) + else: + wr.update_direction_from_starting_node(way_relations[idx - 1].last_node.id) + + # ***** Expected calculations + self._nodes_data = np.array([]) + self._divertions = [[]] + self._curvature_speed_sections_data = np.array([]) + way_count = len(way_relations) + if way_count == 0: + return + # We want all the nodes from the last way section + nodes_data = nodes_raw_data_array_for_wr(way_relations[-1]) + # For the ways before the last in the route we want all the nodes but the last, as that one is the first on + # the next section. Collect them, append last way node data and concatenate the numpy arrays. + if way_count > 1: + wrs_data = tuple([nodes_raw_data_array_for_wr(wr, drop_last=True) for wr in way_relations[:-1]]) + wrs_data += (nodes_data,) + nodes_data = np.concatenate(wrs_data) + # Get a subarray with lat, lon to compute the remaining node values. + lat_lon_array = nodes_data[:, [1, 2]] + points = np.radians(lat_lon_array) + # Ensure we have more than 3 points, if not calculations are not possible. + if len(points) <= 3: + return + vect, dist_prev, dist_next, dist_route, bearing = node_calculations(points) + # append calculations to nodes_data + # nodes_data structure: [id, lat, lon, speed_limit, x, y, dist_prev, dist_next, dist_route, bearing] + self._nodes_data = np.column_stack((nodes_data, vect, dist_prev, dist_next, dist_route, bearing)) + # Build route divertion options data from the wr_index. + wr_ids = [wr.id for wr in way_relations] + self._divertions = [[wr for wr in wr_index.way_relations_with_edge_node_id(node_id) + if is_wr_a_valid_divertion_from_node(wr, node_id, wr_ids)] + for node_id in nodes_data[:, 0]] + # Store calculcations for curvature sections speed limits. We need more than 3 points to be able to process. + # _curvature_speed_sections_data structure: [dist_start, dist_stop, speed_limits, curv_sign] + if len(vect) > 3: + self._curv, self._curv_ds = spline_curvature_calculations(vect, dist_prev) + self._curvature_speed_sections_data = speed_limits_for_curvatures_data(self._curv, self._curv_ds) + # ***** + + +# Test data in degrees from this road: +# https://www.google.de/maps/@52.209263,13.8723137,13z +_WAY_NODES_COORDS_01 = [ + [52.1933703, 13.8723799], + [52.1939477, 13.8711273], + [52.1942004, 13.8705818], + [52.1945408, 13.8698496], + [52.1948447, 13.8691873], + [52.1950772, 13.8685726], + [52.1951168, 13.8684641], + [52.1956681, 13.8670323], + [52.1958716, 13.8664936], + [52.1964366, 13.8649875], + [52.1969283, 13.8636040], + [52.1970203, 13.8634430], + [52.1975486, 13.8626307], + [52.1976354, 13.8624971], + [52.1977827, 13.8621795], + [52.1978564, 13.8619220], + [52.1981843, 13.8604497], + [52.1982614, 13.8602140], + [52.1983351, 13.8600595], + [52.1992768, 13.8579824], + [52.1995107, 13.8574321], + [52.1995948, 13.8572604], + [52.1996818, 13.8571155], + [52.1998000, 13.8570029], + [52.2000659, 13.8568236], + [52.2003868, 13.8566005], + [52.2007182, 13.8564460], + [52.2008760, 13.8564117], + [52.2009865, 13.8564117], + [52.2011390, 13.8564202], + [52.2012267, 13.8564496], + [52.2012544, 13.8564577], + [52.2013179, 13.8564803], + [52.2020491, 13.8571756], + [52.2026014, 13.8576991], + [52.2027592, 13.8578879], + [52.2027960, 13.8579309], + [52.2028960, 13.8580939], + [52.2030170, 13.8583343], + [52.2036587, 13.8597076], + [52.2052946, 13.8633039], + [52.2064332, 13.8658435], + [52.2067856, 13.8666332], + [52.2068961, 13.8668477], + [52.2070777, 13.8670890], + [52.2073723, 13.8674409], + [52.2077457, 13.8679387], + [52.2083874, 13.8687455], + [52.2093341, 13.8699214], + [52.2099652, 13.8707540], + [52.2102282, 13.8712089], + [52.2104228, 13.8715694], + [52.2106122, 13.8718955], + [52.2107619, 13.8721756], + [52.2108695, 13.8723771], + [52.2110747, 13.8727610], + [52.2111514, 13.8729047], + [52.2114010, 13.8733718], + [52.2114694, 13.8735006], + [52.2115430, 13.8736636], + [52.2116086, 13.8737571], + [52.2116770, 13.8738172], + [52.2117611, 13.8738515], + [52.2118664, 13.8738566], + [52.2119322, 13.8738439], + [52.2121058, 13.8737924], + [52.2122583, 13.8737495], + [52.2123265, 13.8737260], + [52.2124213, 13.8736894], + [52.2127466, 13.8734888], + [52.2128263, 13.8734491], + [52.2131313, 13.8733117], + [52.2133943, 13.8731830], + [52.2136625, 13.8731057], + [52.2139465, 13.8730456], + [52.2143619, 13.8730113], + [52.2148773, 13.8729942], + [52.2152275, 13.8730325], + [52.2153110, 13.8730398], + [52.2157442, 13.8730848], + [52.2158833, 13.8731036]] + + +mockNodesData01 = MockNodesData(_WAY_NODES_COORDS_01) + +# OSM Query around B96 south of Berlin +mockOSMResponse01 = MockOSMQueryResponse('selfdrive/mapd/test/mock_osm_response_01.xml', + [52.31400353586984, 13.447158941786366]) + +# OSM Query on curvy town area south of Germany. +mockOSMResponse02 = MockOSMQueryResponse('selfdrive/mapd/test/mock_osm_response_02.xml', + [48.16573269276522, 9.81418473659117]) + +mockWayCollection01 = WayCollection(mockOSMResponse01.ways, mockOSMResponse01.query_center) +mockWayCollection02 = WayCollection(mockOSMResponse02.ways, mockOSMResponse02.query_center) + +# Normal curvy Way. way id: 179532213 with 35 Nodes. +mockOSMWay_01_01_LongCurvy = next(way for way in mockOSMResponse01.ways if way.id == 179532213) + +# Looped way. way id: 29233907 +mockOSMWay_01_02_Loop = next(way for way in mockOSMResponse01.ways if way.id == 29233907) + +# Complex curvy road through town with intersections. way id:178450395 +mockOSMWay_02_01_CurvyTownWithIntersections = next(way for way in mockOSMResponse02.ways if way.id == 178450395) + +# Valid divertion for way 02_01 at node: 34785115. way id: 27955186 +mockOSMWay_02_02_Divertion_34785115 = next(way for way in mockOSMResponse02.ways if way.id == 27955186) + +# 3 node way. way id: 807781992 +mockOSMWay_02_03_Short_3_node_way = next(way for way in mockOSMResponse02.ways if way.id == 807781992) + +# data composing route 01 in way collection 02 +mockRouteData_02_01 = MockRouteData([60890967, 737120246, 601406617, 60890971, 178450395], mockWayCollection02, + first_node_id=201962346) + +# data composing route 02 in way collection 02. Single WR +mockRouteData_02_02_single_wr = MockRouteData([178450395], mockWayCollection02, first_node_id=762086638) + +# data composing route 03 in way collection 02. Multiple speed limits +mockRouteData_02_03 = MockRouteData([158799549, 798805532, 28707704, 158797898, 602249535, 602249536, 825823509, + 178449088, 916462523, 158796386], mockWayCollection02, + first_node_id=252601829) + +# 1000mt section with one full sin cycle as curv values. +mockCurveSectionSin = MockCurveSection(lambda x: np.sin(x * 2 * np.pi)) + +# 200mt section with changing curvature rate. +mockCurveSteepCurvChange = MockCurveSection(lambda x: 0.05 * x**3 - 0.007 * x**2 + 0.001 * x, df=200) + +# _MIN_SPEED_SECTION_LENGTH section with changing curvature rate. +mockCurveSteepCurvChangeShort = MockCurveSection( + lambda x: 0.05 * x**3 - 0.007 * x**2 + 0.001 * x, df=_MIN_SPEED_SECTION_LENGTH) + +# 200mt section with smooth changing curvature rate. no deviation over 2. +mockCurveSmoothCurveChange = MockCurveSection(lambda x: 0.0002 * x**3 - 0.001 * x**2 + 0.6 * x, df=200) diff --git a/selfdrive/mapd/test/mock_osm_response_01.xml b/selfdrive/mapd/test/mock_osm_response_01.xml new file mode 100644 index 000000000..d1e2a219c --- /dev/null +++ b/selfdrive/mapd/test/mock_osm_response_01.xml @@ -0,0 +1,9908 @@ + + + +The data included in this document is from www.openstreetmap.org. The data is made available under ODbL. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/selfdrive/mapd/test/mock_osm_response_02.xml b/selfdrive/mapd/test/mock_osm_response_02.xml new file mode 100644 index 000000000..bbcf72f3e --- /dev/null +++ b/selfdrive/mapd/test/mock_osm_response_02.xml @@ -0,0 +1,11529 @@ + + + +The data included in this document is from www.openstreetmap.org. The data is made available under ODbL. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/selfdrive/mapd/test/test_NodesData.py b/selfdrive/mapd/test/test_NodesData.py new file mode 100644 index 000000000..915f575ed --- /dev/null +++ b/selfdrive/mapd/test/test_NodesData.py @@ -0,0 +1,354 @@ +import unittest +import numpy as np +from selfdrive.mapd.lib.geo import DIRECTION +from common.conversions import Conversions as CV +from selfdrive.mapd.lib.WayRelation import WayRelation +from selfdrive.mapd.lib.NodesData import nodes_raw_data_array_for_wr, node_calculations, \ + spline_curvature_calculations, split_speed_section_by_sign, split_speed_section_by_curv_degree, speed_section, \ + speed_limits_for_curvatures_data, is_wr_a_valid_divertion_from_node, SpeedLimitSection, TurnSpeedLimitSection, \ + NodesData, NodeDataIdx +from selfdrive.mapd.test.mock_data import mockOSMWay_01_01_LongCurvy, mockNodesData01, mockCurveSectionSin, \ + mockCurveSteepCurvChange, mockCurveSteepCurvChangeShort, mockCurveSmoothCurveChange, \ + mockOSMWay_02_01_CurvyTownWithIntersections, mockOSMWay_02_02_Divertion_34785115, mockOSMWay_02_03_Short_3_node_way, \ + mockRouteData_02_01, mockRouteData_02_02_single_wr, mockRouteData_02_03 +from numpy.testing import assert_array_almost_equal + + +class TestNodesDataFileFunctions(unittest.TestCase): + def test_nodes_raw_data_array_for_wr(self): + wr = WayRelation(mockOSMWay_01_01_LongCurvy) + data_e = np.array([(n.id, n.lat, n.lon, wr.speed_limit) for n in wr.way.nodes], dtype=float) + data = nodes_raw_data_array_for_wr(wr) + + assert_array_almost_equal(data, data_e) + + def test_nodes_raw_data_array_for_wr_flips_when_backwards(self): + wr = WayRelation(mockOSMWay_01_01_LongCurvy) + wr.direction = DIRECTION.BACKWARD + + data_e = np.array([(n.id, n.lat, n.lon, wr.speed_limit) for n in wr.way.nodes], dtype=float) + data_e = np.flip(data_e, axis=0) + + data = nodes_raw_data_array_for_wr(wr) + + assert_array_almost_equal(data, data_e) + + def test_nodes_raw_data_array_for_wr_drops_last(self): + wr = WayRelation(mockOSMWay_01_01_LongCurvy) + data_e = np.array([(n.id, n.lat, n.lon, wr.speed_limit) for n in wr.way.nodes], dtype=float)[:-1] + data = nodes_raw_data_array_for_wr(wr, drop_last=True) + + assert_array_almost_equal(data, data_e) + + def test_node_calculations(self): + points = mockNodesData01.radians + + v, dp, dn, dr, b = node_calculations(points) + + assert_array_almost_equal(v, mockNodesData01.v) + assert_array_almost_equal(dp, mockNodesData01.dp) + assert_array_almost_equal(dn, mockNodesData01.dn) + assert_array_almost_equal(dr, mockNodesData01.dr) + assert_array_almost_equal(b, mockNodesData01.b) + + def test_node_calculations_index_error(self): + points = mockNodesData01.radians[:2] + + with self.assertRaises(IndexError): + node_calculations(points) + + def test_spline_curvature_calculations(self): + vect = mockNodesData01.v + dist_prev = mockNodesData01.dp + + curv, curv_ds = spline_curvature_calculations(vect, dist_prev) + + assert_array_almost_equal(curv, mockNodesData01.curv) + assert_array_almost_equal(curv_ds, mockNodesData01.curv_ds) + + def test_spline_curvature_calculations_with_route_data(self): + mockRouteData_02_01.reset() + nodes_data = mockRouteData_02_01._nodes_data + vect = np.column_stack((nodes_data[:, 4], nodes_data[:, 5])) + dist_prev = nodes_data[:, 6] + + curv, curv_ds = spline_curvature_calculations(vect, dist_prev) + + assert_array_almost_equal(curv, mockRouteData_02_01._curv) + assert_array_almost_equal(curv_ds, mockRouteData_02_01._curv_ds) + + def test_split_speed_section_by_sign(self): + curv_sec = mockCurveSectionSin.curv_sec + new_secs = split_speed_section_by_sign(curv_sec) + + # 3 sections with matching initial and final distance + self.assertEqual(len(new_secs), 3) + self.assertEqual(new_secs[0][0][2], mockCurveSectionSin.di) + self.assertEqual(new_secs[2][-1][2], mockCurveSectionSin.df) + + # All new sections has same sign internally + for sec in new_secs: + self.assertEqual(np.average(sec, axis=0)[1], sec[0][1]) + + # Sections change sign + for idx in range(2): + self.assertNotEqual(new_secs[idx][0][1], new_secs[idx + 1][0][1]) + + # total items consistency + lenghts = [len(sec) for sec in new_secs] + self.assertEqual(len(curv_sec), sum(lenghts)) + + def test_split_speed_section_by_curv_degree(self): + curv_sec = mockCurveSteepCurvChange.curv_sec + new_secs = split_speed_section_by_curv_degree(curv_sec) + + # 3 sections with matching initial and final distance + self.assertEqual(len(new_secs), 3) + self.assertEqual(new_secs[0][0][2], mockCurveSteepCurvChange.di) + self.assertEqual(new_secs[2][-1][2], mockCurveSteepCurvChange.df) + + # Sections split at the right points + split_dist = [sec[-1][2] for sec in new_secs] + self.assertListEqual(split_dist, [50., 150., 200.]) + + def test_split_speed_section_by_curv_degree_does_nothing_if_short(self): + curv_sec = mockCurveSteepCurvChangeShort.curv_sec + new_secs = split_speed_section_by_curv_degree(curv_sec) + + self.assertEqual(len(new_secs), 1) + assert_array_almost_equal(curv_sec, new_secs[0]) + + def test_split_speed_section_by_curv_degree_does_nothing_if_no_substantial_change(self): + curv_sec = mockCurveSmoothCurveChange.curv_sec + new_secs = split_speed_section_by_curv_degree(curv_sec) + + self.assertEqual(len(new_secs), 1) + assert_array_almost_equal(curv_sec, new_secs[0]) + + def test_speed_section(self): + curv_sec = mockCurveSectionSin.curv_sec + + speed_secs = speed_section(curv_sec) + expected = np.array([0., 1000., 1.51657509, 1.]) + + assert_array_almost_equal(speed_secs, expected) + + def test_speed_limits_for_curvatures_data(self): + curv = mockCurveSectionSin.curv + curv_ds = mockCurveSectionSin.curv_ds + + expected = np.array([ + [10., 490., 1.51657509, 1.], + [510., 990., 1.51657509, -1.]]) + limits = speed_limits_for_curvatures_data(curv, curv_ds) + + assert_array_almost_equal(limits, expected) + + def test_is_wr_a_valid_divertion_from_node(self): + wr = WayRelation(mockOSMWay_02_01_CurvyTownWithIntersections) + mockOSMWay_02_02_Divertion_34785115.tags['oneway'] = 'yes' + wr_div = WayRelation(mockOSMWay_02_02_Divertion_34785115) + + # False if id already in route + wr_ids = [wr.id, wr_div.id] + self.assertFalse(is_wr_a_valid_divertion_from_node(wr_div, 34785115, wr_ids)) + + # True if id not in route, node_id is edge and not prohibited + wr_ids = [wr.id, 11111, 22222] + self.assertTrue(is_wr_a_valid_divertion_from_node(wr_div, 34785115, wr_ids)) + + # False if id not in route, node_id is edge but prohibited (wrong direction from node 319503453) + self.assertFalse(is_wr_a_valid_divertion_from_node(wr_div, 319503453, wr_ids)) + + # False if id not in route, node_id is not edge + self.assertFalse(is_wr_a_valid_divertion_from_node(wr_div, 44444, wr_ids)) + + +class TestSpeedLimitSection(unittest.TestCase): + def test_speed_limit_section_init(self): + section = SpeedLimitSection(10., 20., 50.) + + self.assertEqual(section.start, 10.) + self.assertEqual(section.end, 20.) + self.assertEqual(section.value, 50.) + + +class TestTurnSpeedLimitSection(unittest.TestCase): + def test_turn_speed_limit_section_init(self): + section = TurnSpeedLimitSection(10., 20., 50., -1.) + + self.assertEqual(section.start, 10.) + self.assertEqual(section.end, 20.) + self.assertEqual(section.value, 50.) + self.assertEqual(section.curv_sign, -1.) + + +class TestNodesData(unittest.TestCase): + def test_init_with_empty_list(self): + nd = NodesData([], {}) + + self.assertEqual(len(nd._nodes_data), 0) + num_diverstions = sum([len(d) for d in nd._divertions]) + self.assertEqual(num_diverstions, 0) + self.assertEqual(len(nd._curvature_speed_sections_data), 0) + + def test_init_with_single_wr_includes_all_wr_nodes(self): + mockRouteData_02_02_single_wr.reset() + way_relations = mockRouteData_02_02_single_wr.wrs + wr_index = mockRouteData_02_02_single_wr.way_collection.wr_index + + nd = NodesData(way_relations, wr_index) + + assert_array_almost_equal(nd._nodes_data, mockRouteData_02_02_single_wr._nodes_data) + assert_array_almost_equal(nd._curvature_speed_sections_data, + mockRouteData_02_02_single_wr._curvature_speed_sections_data) + self.assertListEqual(nd._divertions, mockRouteData_02_02_single_wr._divertions) + self.assertEqual(len(nd._nodes_data), len(way_relations[0].way.nodes)) + self.assertEqual(len(nd._curvature_speed_sections_data), 6) + num_diverstions = sum([len(d) for d in nd._divertions]) + self.assertEqual(num_diverstions, 6) + + def test_init_with_less_than_4_nodes(self): + wr_t = WayRelation(mockOSMWay_02_03_Short_3_node_way) + + nd = NodesData([wr_t], {}) + + self.assertEqual(len(nd._nodes_data), 0) + num_diverstions = sum([len(d) for d in nd._divertions]) + self.assertEqual(num_diverstions, 0) + self.assertEqual(len(nd._curvature_speed_sections_data), 0) + + def test_init_with_multiple_wr(self): + mockRouteData_02_01.reset() + way_relations = mockRouteData_02_01.wrs + wr_index = mockRouteData_02_01.way_collection.wr_index + + nd = NodesData(way_relations, wr_index) + + assert_array_almost_equal(nd._nodes_data, mockRouteData_02_01._nodes_data) + assert_array_almost_equal(nd._curvature_speed_sections_data, mockRouteData_02_01._curvature_speed_sections_data) + self.assertListEqual(nd._divertions, mockRouteData_02_01._divertions) + self.assertEqual(len(nd._curvature_speed_sections_data), 9) + num_diverstions = sum([len(d) for d in nd._divertions]) + self.assertEqual(num_diverstions, 14) + + def test_count(self): + mockRouteData_02_01.reset() + way_relations = mockRouteData_02_01.wrs + wr_index = mockRouteData_02_01.way_collection.wr_index + num_n = sum([len(wr.way.nodes) for wr in way_relations]) - len(way_relations) + 1 + + nd = NodesData(way_relations, wr_index) + + self.assertEqual(nd.count, num_n) + + def test_get_on_empty(self): + wr_t = WayRelation(mockOSMWay_02_03_Short_3_node_way) + + nd = NodesData([wr_t], {}) + assert_array_almost_equal(nd.get(NodeDataIdx.node_id), np.array([])) + + def test_get_values(self): + mockRouteData_02_01.reset() + way_relations = mockRouteData_02_01.wrs + wr_index = mockRouteData_02_01.way_collection.wr_index + + nd = NodesData(way_relations, wr_index) + + assert_array_almost_equal(nd.get(NodeDataIdx.node_id), mockRouteData_02_01._nodes_data[:, 0]) + assert_array_almost_equal(nd.get(NodeDataIdx.lat), mockRouteData_02_01._nodes_data[:, 1]) + assert_array_almost_equal(nd.get(NodeDataIdx.lon), mockRouteData_02_01._nodes_data[:, 2]) + assert_array_almost_equal(nd.get(NodeDataIdx.speed_limit), mockRouteData_02_01._nodes_data[:, 3]) + assert_array_almost_equal(nd.get(NodeDataIdx.x), mockRouteData_02_01._nodes_data[:, 4]) + assert_array_almost_equal(nd.get(NodeDataIdx.y), mockRouteData_02_01._nodes_data[:, 5]) + assert_array_almost_equal(nd.get(NodeDataIdx.dist_prev), mockRouteData_02_01._nodes_data[:, 6]) + assert_array_almost_equal(nd.get(NodeDataIdx.dist_next), mockRouteData_02_01._nodes_data[:, 7]) + assert_array_almost_equal(nd.get(NodeDataIdx.dist_route), mockRouteData_02_01._nodes_data[:, 8]) + assert_array_almost_equal(nd.get(NodeDataIdx.bearing), mockRouteData_02_01._nodes_data[:, 9]) + + def test_speed_limits_ahead_from_empty(self): + wr_t = WayRelation(mockOSMWay_02_03_Short_3_node_way) + + nd = NodesData([wr_t], {}) + self.assertEqual(len(nd.speed_limits_ahead(1, 10.)), 0) + + def test_speed_limits_ahead(self): + mockRouteData_02_03.reset() + way_relations = mockRouteData_02_03.wrs + wr_index = mockRouteData_02_03.way_collection.wr_index + + nd = NodesData(way_relations, wr_index) + + # empty when ahead_idx is none. + self.assertEqual(len(nd.speed_limits_ahead(None, 10.)), 0) + + # All limist from 0 + all_limits = nd.speed_limits_ahead(1, nd.get(NodeDataIdx.dist_next)[0]) + self.assertEqual(len(all_limits), 4) # 4 limits on this mock road. + self.assertListEqual([sl.value for sl in all_limits], [v * CV.KPH_TO_MS for v in [50, 100, 50, 100]]) + for idx, sl in enumerate(all_limits): + self.assertTrue(sl.end > sl.start) + self.assertTrue(sl.value > 0.) + if idx == 0: + self.assertEqual(sl.start, 0.) + else: + self.assertEqual(sl.start, all_limits[idx - 1].end) + self.assertNotEqual(sl.value, all_limits[idx - 1].value) + + def test_distance_to_end_from_empty(self): + wr_t = WayRelation(mockOSMWay_02_03_Short_3_node_way) + + nd = NodesData([wr_t], {}) + self.assertIsNone(nd.distance_to_end(1, 10.)) + + def test_distance_to_end(self): + mockRouteData_02_03.reset() + way_relations = mockRouteData_02_03.wrs + wr_index = mockRouteData_02_03.way_collection.wr_index + + nd = NodesData(way_relations, wr_index) + + # none when ahead_idx is none. + self.assertIsNone(nd.distance_to_end(None, 10.)) + + # From the begining + expected = np.sum(nd.get(NodeDataIdx.dist_next)) + self.assertAlmostEqual(nd.distance_to_end(1, nd.get(NodeDataIdx.dist_next)[0]), expected) + self.assertAlmostEqual(nd.get(NodeDataIdx.dist_route)[-1], expected) + + # From the node next to last + expected = nd.get(NodeDataIdx.dist_next)[-2] + self.assertAlmostEqual(nd.distance_to_end(nd.count - 2, 0.), expected) + + def test_distance_to_node(self): + mockRouteData_02_03.reset() + way_relations = mockRouteData_02_03.wrs + wr_index = mockRouteData_02_03.way_collection.wr_index + + nd = NodesData(way_relations, wr_index) + dist_to_node_ahead = 10. + node_id = 1887995486 # Some node id in the middle of the way. idx 50 + node_idx = np.nonzero(nd.get(NodeDataIdx.node_id) == node_id)[0][0] + + # none when ahead_idx is none. + self.assertIsNone(nd.distance_to_node(node_id, None, dist_to_node_ahead)) + + # From the begining + expected = nd.get(NodeDataIdx.dist_route)[node_idx] + self.assertAlmostEqual(nd.distance_to_node(node_id, 1, nd.get(NodeDataIdx.dist_next)[0]), expected) + + # From the end + expected = -np.sum(nd.get(NodeDataIdx.dist_next)[node_idx:]) + self.assertAlmostEqual(nd.distance_to_node(node_id, len(nd.get(NodeDataIdx.node_id)) - 1, 0.), expected) + + # From some node behind including dist to node ahead + ahead_idx = node_idx - 10 + expected = np.sum(nd.get(NodeDataIdx.dist_next)[ahead_idx:node_idx]) + dist_to_node_ahead + self.assertAlmostEqual(nd.distance_to_node(node_id, ahead_idx, dist_to_node_ahead), expected) + + # From some node ahead including dist to node ahead + ahead_idx = node_idx + 10 + expected = -np.sum(nd.get(NodeDataIdx.dist_next)[node_idx:ahead_idx]) + dist_to_node_ahead + self.assertAlmostEqual(nd.distance_to_node(node_id, ahead_idx, dist_to_node_ahead), expected) + +# TODO: Missing tests for curvatures_speed_limit_sections_ahead and possible_divertions diff --git a/selfdrive/mapd/test/test_WayRelation.py b/selfdrive/mapd/test/test_WayRelation.py new file mode 100644 index 000000000..9719c86f0 --- /dev/null +++ b/selfdrive/mapd/test/test_WayRelation.py @@ -0,0 +1,651 @@ +import copy +import unittest +import numpy as np +from unittest import mock +from numpy.testing import assert_array_almost_equal +from datetime import datetime as dt, timezone, timedelta +from common.conversions import Conversions as CV +from selfdrive.mapd.lib.WayRelation import WayRelation, is_osm_time_condition_active, \ + conditional_speed_limit_for_osm_tag_limit_string, speed_limit_for_osm_tag_limit_string +from selfdrive.mapd.config import LANE_WIDTH +from selfdrive.mapd.lib.geo import DIRECTION, R, vectors +from selfdrive.mapd.test.mock_data import mockOSMWay_01_01_LongCurvy, mockOSMWay_01_02_Loop, \ + mockOSMWay_02_01_CurvyTownWithIntersections + + +class TestWayRelationFileFunctions(unittest.TestCase): + def test_speed_limit_for_osm_tag_limit_string(self): + values = [ + None, # Invalid + "1000", # Invalid + "60 kph", # Invalid + "100", + "30 mph", + "DE:zone:40", + "DE:zone:50 mph", + "AR:urban", + "CZ:pedestrian_zone", + "DK:urban", + "DK:rural", + "DK:motorway", + "DE:living_street", + "DE:residential", + "DE:urban", + "DE:rural", + "DE:trunk", # No limit + "DE:motorway", # No limit + "GB:nsl_restricted", + "GB:nsl_single", + "GB:nsl_dual", + "GB:motorway", + "GB:invalid", # Invalid + ] + + expected = [ + 0., + 0., + 0., + 100. * CV.KPH_TO_MS, + 30. * CV.MPH_TO_MS, + 40. * CV.KPH_TO_MS, + 50. * CV.MPH_TO_MS, + 40. * CV.KPH_TO_MS, + 20. * CV.KPH_TO_MS, + 50. * CV.KPH_TO_MS, + 80. * CV.KPH_TO_MS, + 130. * CV.KPH_TO_MS, + 7. * CV.KPH_TO_MS, + 30. * CV.KPH_TO_MS, + 50. * CV.KPH_TO_MS, + 100. * CV.KPH_TO_MS, + 0., + 0., + 30. * CV.MPH_TO_MS, + 60. * CV.MPH_TO_MS, + 70. * CV.MPH_TO_MS, + 70. * CV.MPH_TO_MS, + 0., + ] + + result = [speed_limit_for_osm_tag_limit_string(sls) for sls in values] + + self.assertEqual(result, expected) + + @mock.patch('selfdrive.mapd.lib.WayRelation.dt') + def test_is_osm_time_condition_active(self, mock_dt): + tz = timezone(timedelta(hours=1), 'berlin') + wed_10_10_am = dt(2021, 9, 1, 10, 10, 0) + mock_dt.now.return_value = wed_10_10_am + mock_dt.tzinfo = tz + mock_dt.combine = dt.combine + mock_dt.strptime = dt.strptime + + values = [ + "WE", # Invalid + "We", + "Mo", + "Fr", + "Tu-Th", + "10:00", # Invalid + "10:00-10:30", + "We 10:00-10:30", + "SU 10:00-10:30", # Valid, SU string not considered a day string. + "Sa 10:00-10:30", + "Tu-Th 10:00-10:30", + ] + + expected = [ + False, # Invalid + True, + False, + False, + True, + False, # Invalid + True, + True, + True, + False, + True, + ] + + result = [is_osm_time_condition_active(cs) for cs in values] + + self.assertEqual(result, expected) + + @mock.patch('selfdrive.mapd.lib.WayRelation.dt') + def test_conditional_speed_limit_for_osm_tag_limit_string(self, mock_dt): + tz = timezone(timedelta(hours=1), 'berlin') + wed_10_10_am = dt(2021, 9, 1, 10, 10, 0) + mock_dt.now.return_value = wed_10_10_am + mock_dt.tzinfo = tz + mock_dt.combine = dt.combine + mock_dt.strptime = dt.strptime + + values = [ + None, # Invalid + "Hola", # Invalid + "100 @ (WE)", # Invalid + "x @ (We)", # Invalid + "100 @ (We)", + "100 @ (Mo)", + "100 @ (Fr)", + "100 @ (Tu-Th)", + "100 @ (10:00)", # Invalid + "100 @ (10:00-10:30)", + "100 @ (We 10:00-10:30)", + "100 @ (SU 10:00-10:30)", # Valid, SU string not considered a day string. + "100 @ (Sa 10:00-10:30)", + "100 @ (Tu-Th 10:00-10:30)", + "100 @ (Mo-Th;Su)", + "100 @ (Mo Th;Fr-Sa)", + "100 @ (Fr-Su;Mo-Tu)", + "100 @ (10:00-10:30;15:00-16:00)", + "100 @ (We;Mo-Tu)", + "100 @ (We 10:00-10:30;Th 15:00-16:00)", + "100 @ (Tu 10:00-10:30;Th 15:00-16:00)", + ] + + _100 = 100. * CV.KPH_TO_MS + + expected = [ + 0., # Invalid + 0., # Invalid + 0., # Invalid + 0., # Invalid + _100, + 0., + 0., + _100, + 0., # Invalid + _100, + _100, + _100, + 0., + _100, + _100, + _100, + 0., + _100, + _100, + _100, + 0. + ] + + result = [conditional_speed_limit_for_osm_tag_limit_string(ls) for ls in values] + + self.assertEqual(result, expected) + + +class TestWayRelation(unittest.TestCase): + def test_way_relation_init(self): + wayRelation = WayRelation(mockOSMWay_01_01_LongCurvy) + + nodes_np_expected = np.radians(np.array([[nd.lat, nd.lon] for nd in wayRelation.way.nodes], dtype=float)) + v = vectors(wayRelation._nodes_np) + way_distances_expected = np.linalg.norm(v * R, axis=1) + way_bearings_expected = np.arctan2(v[:, 0], v[:, 1]) + bbox_expected = np.array([ + [0.91321784, 0.2346417], + [0.91344672, 0.23475751]]) + + self.assertEqual(wayRelation.way.id, 179532213) + self.assertIsNone(wayRelation.parent_wr_id) + self.assertEqual(wayRelation.direction, DIRECTION.NONE) + self.assertEqual(wayRelation._speed_limit, None) + self.assertEqual(wayRelation._one_way, 'yes') + self.assertEqual(wayRelation.name, None) + self.assertEqual(wayRelation.ref, 'B 96') + self.assertEqual(wayRelation.highway_type, 'trunk') + self.assertEqual(wayRelation.highway_rank, 10) + self.assertEqual(wayRelation.lanes, 2) + assert_array_almost_equal(wayRelation._nodes_np, nodes_np_expected) + assert_array_almost_equal(wayRelation._way_distances, way_distances_expected) + assert_array_almost_equal(wayRelation._way_bearings, way_bearings_expected) + assert_array_almost_equal(wayRelation.bbox, bbox_expected) + self.assertEqual(wayRelation.edge_nodes_ids, [wayRelation.way.nodes[0].id, wayRelation.way.nodes[-1].id]) + + def test_way_relation_init_with_parent(self): + wayRelation = WayRelation(mockOSMWay_01_01_LongCurvy, parent=WayRelation(mockOSMWay_01_02_Loop)) + + self.assertEqual(wayRelation.way.id, 179532213) + self.assertEqual(wayRelation.parent_wr_id, 29233907) + + def test_way_relation_equality(self): + wayRelation1 = WayRelation(mockOSMWay_01_01_LongCurvy) + wayRelation2 = copy.copy(wayRelation1) + wayRelation3 = copy.deepcopy(wayRelation1) + wayRelation3.way.id = 123 + + self.assertEqual(wayRelation1, wayRelation2) + self.assertNotEqual(wayRelation1, wayRelation3) + + def test_way_relation_reset_location_variables(self): + wayRelation = WayRelation(mockOSMWay_01_01_LongCurvy) + self.make_wayRelation_location_dirty(wayRelation) + + wayRelation.reset_location_variables() + + self.assert_wayRelation_variables_reset(wayRelation) + + def test_way_relation_id(self): + wayRelation = WayRelation(mockOSMWay_01_01_LongCurvy) + + self.assertEqual(wayRelation.id, 179532213) + + def test_way_relation_road_name(self): + # road name when no tag for name or ref + wayRelation = WayRelation(mockOSMWay_01_02_Loop) + self.assertIsNone(wayRelation.road_name) + # road name based on ref tag + wayRelation = WayRelation(mockOSMWay_01_01_LongCurvy) + self.assertEqual(wayRelation.road_name, "B 96") + # road name based on name tag + wayRelation = WayRelation(mockOSMWay_02_01_CurvyTownWithIntersections) + self.assertEqual(wayRelation.road_name, "Hauptstraße") + + def test_way_relation_update_resets_on_update(self): + wayRelation = WayRelation(mockOSMWay_01_01_LongCurvy) + self.make_wayRelation_location_dirty(wayRelation) + location_rad = np.array([0., 0.]) # Location outside bbox + + wayRelation.update(location_rad, 0., 10.) + + self.assertFalse(wayRelation.is_location_in_bbox(location_rad)) + self.assert_wayRelation_variables_reset(wayRelation) + + def test_way_relation_update_only_resets_if_no_possible_found(self): + wayRelation = WayRelation(mockOSMWay_01_01_LongCurvy) + location_rad = wayRelation.bbox[0] # Location inside bbox but outside actual way (due to padding) + + wayRelation.update(location_rad, 0., 10.) + + self.assertTrue(wayRelation.is_location_in_bbox(location_rad)) + self.assert_wayRelation_variables_reset(wayRelation) + + def test_way_relation_updates_in_the_correct_direction_with_correct_property_values(self): + wayRelation = WayRelation(mockOSMWay_01_01_LongCurvy) + location_rad = np.radians(np.array([52.32855593146639, 13.445320150125069])) + bearing_rad = 0. + + wayRelation.update(location_rad, bearing_rad, 10.) + + self.assertTrue(wayRelation.is_location_in_bbox(location_rad)) + self.assertEqual(wayRelation.direction, DIRECTION.FORWARD) + self.assertEqual(wayRelation.ahead_idx, 17) + self.assertEqual(wayRelation.behind_idx, 16) + self.assertAlmostEqual(wayRelation._distance_to_way, 3.43290781621360) + self.assertAlmostEqual(wayRelation._active_bearing_delta, 0.320717420388962) + self.assertAlmostEqual(wayRelation.distance_to_node_ahead, 25.4998961709014) + self.assertTrue(wayRelation.active) + self.assertFalse(wayRelation.diverting) + assert_array_almost_equal(wayRelation.location_rad, location_rad) + self.assertEqual(wayRelation.bearing_rad, bearing_rad) + self.assertIsNone(wayRelation._speed_limit) + + bearing_rad = 180. + + wayRelation.update(location_rad, bearing_rad, 10.) + + self.assertTrue(wayRelation.is_location_in_bbox(location_rad)) + self.assertEqual(wayRelation.direction, DIRECTION.BACKWARD) + self.assertEqual(wayRelation.ahead_idx, 16) + self.assertEqual(wayRelation.behind_idx, 17) + self.assertAlmostEqual(wayRelation._distance_to_way, 3.43290781621360) + self.assertAlmostEqual(wayRelation._active_bearing_delta, 0.9507682562504284) + self.assertAlmostEqual(wayRelation.distance_to_node_ahead, 11.11623371145368) + self.assertTrue(wayRelation.active) + self.assertFalse(wayRelation.diverting) + assert_array_almost_equal(wayRelation.location_rad, location_rad) + self.assertEqual(wayRelation.bearing_rad, bearing_rad) + self.assertIsNone(wayRelation._speed_limit) + + def test_way_relation_updates_with_location_closest_to_way_when_multiple_possible(self): + wayRelation = WayRelation(mockOSMWay_01_02_Loop) + location_rad = np.radians(np.array([52.313303275461564, 13.437729236325788])) + bearing_rad = np.radians(10.) + + wayRelation.update(location_rad, bearing_rad, 10.) + + self.assertTrue(wayRelation.is_location_in_bbox(location_rad)) + self.assertEqual(wayRelation.direction, DIRECTION.BACKWARD) + self.assertEqual(wayRelation.ahead_idx, 26) + self.assertEqual(wayRelation.behind_idx, 27) + self.assertAlmostEqual(wayRelation._distance_to_way, 10.151775235257011) + self.assertAlmostEqual(wayRelation._active_bearing_delta, 0.06371131069242782) + self.assertAlmostEqual(wayRelation.distance_to_node_ahead, 10.174073707120915) + self.assertTrue(wayRelation.active) + self.assertFalse(wayRelation.diverting) + assert_array_almost_equal(wayRelation.location_rad, location_rad) + self.assertEqual(wayRelation.bearing_rad, bearing_rad) + self.assertIsNone(wayRelation._speed_limit) + + def test_way_relation_updates_will_become_inactive_if_too_far_from_way(self): + wayRelation = WayRelation(mockOSMWay_01_01_LongCurvy) + # Location is 24.9 mts away from the way. There are 2 Lanes in this way. + location_rad = np.radians(np.array([52.328634560607746, 13.445609877522788])) + location_stdev = 5.5 # threshold is 4 * location_stdev + LANE_WIDTH + distance_threshold = 4. * location_stdev + wayRelation.lanes * LANE_WIDTH / 2. + + wayRelation.update(location_rad, 0., location_stdev) + self.assertTrue(wayRelation.active) + self.assertLess(wayRelation._distance_to_way, distance_threshold) + + location_stdev = 5. + + wayRelation.update(location_rad, 0., location_stdev) + self.assertFalse(wayRelation.active) + + def test_way_relation_updates_will_update_diverting_correctly(self): + wayRelation = WayRelation(mockOSMWay_01_01_LongCurvy) + # Location is 24.9 mts away from the way. There are 2 Lanes in this way. + location_rad = np.radians(np.array([52.328634560607746, 13.445609877522788])) + location_stdev = 11. + distance_threshold = 2. * location_stdev + wayRelation.lanes * LANE_WIDTH / 2. + + wayRelation.update(location_rad, 0., location_stdev) + + self.assertLess(wayRelation._distance_to_way, distance_threshold) + self.assertFalse(wayRelation.diverting) + + location_stdev = 10. + distance_threshold = 2. * location_stdev + wayRelation.lanes * LANE_WIDTH / 2. + + wayRelation.update(location_rad, 0., location_stdev) + + self.assertGreater(wayRelation._distance_to_way, distance_threshold) + self.assertTrue(wayRelation.diverting) + + def test_way_relation_update_direction_from_starting_node_resets_speed_limit(self): + wayRelation = WayRelation(mockOSMWay_01_01_LongCurvy) + wayRelation._speed_limit = 10. + + wayRelation.update_direction_from_starting_node(wayRelation.way.nodes[0].id) + + self.assertIsNone(wayRelation._speed_limit) + + def test_way_relation_update_direction_from_starting_node_updates_correctly(self): + wayRelation = WayRelation(mockOSMWay_01_01_LongCurvy) + wayRelation.update_direction_from_starting_node(wayRelation.way.nodes[0].id) + self.assertEqual(wayRelation.direction, DIRECTION.FORWARD) + + wayRelation.update_direction_from_starting_node(wayRelation.way.nodes[-1].id) + self.assertEqual(wayRelation.direction, DIRECTION.BACKWARD) + + wayRelation.update_direction_from_starting_node(0) + self.assertEqual(wayRelation.direction, DIRECTION.NONE) + + def test_way_relation_is_location_in_bbox(self): + wayRelation = WayRelation(mockOSMWay_01_02_Loop) + bbox = wayRelation.bbox + + loc_avg = np.average(bbox, axis=0) + loc_min = np.min(bbox, axis=0) + loc_max = np.max(bbox, axis=0) + + locations = [ + loc_avg, + loc_min, + loc_max, + [loc_avg[0], loc_min[1]], + [loc_avg[0], loc_max[1]], + [loc_min[0], loc_avg[1]], + [loc_max[0], loc_avg[1]], + loc_min - 0.1, + loc_max + 0.1, + [loc_avg[0], loc_min[1] - 0.1], + [loc_avg[0], loc_max[1] + 0.1], + [loc_min[0] - 0.1, loc_avg[1]], + [loc_max[0] + 0.1, loc_avg[1]], + ] + + is_in = [wayRelation.is_location_in_bbox(loc) for loc in locations] + + self.assertEqual(is_in, [True, True, True, True, True, True, True, False, False, False, False, False, False]) + + def test_way_relation_speed_limit_when_set(self): + wayRelation = WayRelation(mockOSMWay_01_02_Loop) + wayRelation._speed_limit = 10. + + self.assertEqual(wayRelation.speed_limit, 10.) + + @mock.patch('selfdrive.mapd.lib.WayRelation.dt') + def test_way_relation_speed_limit_conditional(self, mock_dt): + tz = timezone(timedelta(hours=1), 'berlin') + wed_10_10_am = dt(2021, 9, 1, 10, 10, 0) + mock_dt.now.return_value = wed_10_10_am + mock_dt.tzinfo = tz + mock_dt.combine = dt.combine + mock_dt.strptime = dt.strptime + + # Reset all tags before teting + mockOSMWay_01_02_Loop.tags = {} + wayRelation = WayRelation(mockOSMWay_01_02_Loop) + + # No Value + self.assertEqual(wayRelation.speed_limit, 0.) + + # Value on both directions + wayRelation._speed_limit = None + wayRelation.way.tags["maxspeed:conditional"] = "100 @ (We 10:00-10:30)" + self.assertEqual(wayRelation.speed_limit, 100. * CV.KPH_TO_MS) + + # Value on forward + wayRelation.way.tags.pop("maxspeed:conditional") + wayRelation._speed_limit = None + wayRelation.direction = DIRECTION.FORWARD + self.assertEqual(wayRelation.speed_limit, 0.) + + wayRelation._speed_limit = None + wayRelation.way.tags["maxspeed:forward:conditional"] = "100 @ (We 10:00-10:30)" + self.assertEqual(wayRelation.speed_limit, 100. * CV.KPH_TO_MS) + + # Value on backward + wayRelation._speed_limit = None + wayRelation.direction = DIRECTION.BACKWARD + self.assertEqual(wayRelation.speed_limit, 0.) + + wayRelation._speed_limit = None + wayRelation.way.tags["maxspeed:backward:conditional"] = "100 @ (We 10:00-10:30)" + self.assertEqual(wayRelation.speed_limit, 100. * CV.KPH_TO_MS) + + def test_way_relation_speed_limit_maxspeed(self): + # Reset all tags before teting + mockOSMWay_01_02_Loop.tags = {} + wayRelation = WayRelation(mockOSMWay_01_02_Loop) + + # No Value + self.assertEqual(wayRelation.speed_limit, 0.) + + # Value on both directions + wayRelation._speed_limit = None + wayRelation.way.tags["maxspeed"] = "100" + self.assertEqual(wayRelation.speed_limit, 100. * CV.KPH_TO_MS) + + # Value on forward + wayRelation.way.tags.pop("maxspeed") + wayRelation._speed_limit = None + wayRelation.direction = DIRECTION.FORWARD + self.assertEqual(wayRelation.speed_limit, 0.) + + wayRelation._speed_limit = None + wayRelation.way.tags["maxspeed:forward"] = "100" + self.assertEqual(wayRelation.speed_limit, 100. * CV.KPH_TO_MS) + + # Value on backward + wayRelation._speed_limit = None + wayRelation.direction = DIRECTION.BACKWARD + self.assertEqual(wayRelation.speed_limit, 0.) + + wayRelation._speed_limit = None + wayRelation.way.tags["maxspeed:backward"] = "100" + self.assertEqual(wayRelation.speed_limit, 100. * CV.KPH_TO_MS) + + def test_way_relation_active_bearing_delta_reflects_internal_value(self): + wayRelation = WayRelation(mockOSMWay_01_02_Loop) + wayRelation._active_bearing_delta = 10. + self.assertEqual(wayRelation.active_bearing_delta, 10.) + + def test_way_relation_is_one_way(self): + # Setup initial tags + mockOSMWay_01_02_Loop.tags = { + 'oneway': 'yes', + 'highway': 'unclassified' + } + wayRelation = WayRelation(mockOSMWay_01_02_Loop) + + # oneway = yes + self.assertTrue(wayRelation.is_one_way) + + # oneway non existing + wayRelation._one_way = None + self.assertFalse(wayRelation.is_one_way) + + # highway = motorway + wayRelation.highway_type = 'motorway' + self.assertTrue(wayRelation.is_one_way) + + def test_way_relation_is_prohibited(self): + # Setup initial tags + mockOSMWay_01_02_Loop.tags = { + 'oneway': 'yes' + } + wayRelation = WayRelation(mockOSMWay_01_02_Loop) + + # Direction undefined + wayRelation.direction = DIRECTION.NONE + self.assertTrue(wayRelation.is_prohibited) + + # oneway = yes + wayRelation.direction = DIRECTION.BACKWARD + self.assertTrue(wayRelation.is_prohibited) + + wayRelation.direction = DIRECTION.FORWARD + self.assertFalse(wayRelation.is_prohibited) + + # oneway non existing + wayRelation._one_way = None + self.assertFalse(wayRelation.is_one_way) + + wayRelation.direction = DIRECTION.BACKWARD + self.assertFalse(wayRelation.is_prohibited) + + def test_way_relation_distance_to_way_reflects_internal_value(self): + wayRelation = WayRelation(mockOSMWay_01_02_Loop) + wayRelation._distance_to_way = 10. + self.assertEqual(wayRelation.distance_to_way, 10.) + + def test_way_relation_node_ahead(self): + wayRelation = WayRelation(mockOSMWay_01_01_LongCurvy) + # ahead_ids is None on init + self.assertIsNone(wayRelation.node_ahead) + + wayRelation.ahead_idx = 15 + self.assertEqual(wayRelation.node_ahead, wayRelation.way.nodes[15]) + + def test_way_relation_last_node(self): + wayRelation = WayRelation(mockOSMWay_01_01_LongCurvy) + # direction is NONE on init + self.assertIsNone(wayRelation.last_node) + + # forward + wayRelation.direction = DIRECTION.FORWARD + self.assertEqual(wayRelation.last_node, wayRelation.way.nodes[-1]) + + # backward + wayRelation.direction = DIRECTION.BACKWARD + self.assertEqual(wayRelation.last_node, wayRelation.way.nodes[0]) + + def test_way_relation_last_node_coordinates(self): + wayRelation = WayRelation(mockOSMWay_01_01_LongCurvy) + # direction is NONE on init + self.assertIsNone(wayRelation.last_node_coordinates) + + # forward + wayRelation.direction = DIRECTION.FORWARD + coords = np.radians(np.array([wayRelation.way.nodes[-1].lat, wayRelation.way.nodes[-1].lon], dtype=float)) + assert_array_almost_equal(wayRelation.last_node_coordinates, coords) + + # backward + wayRelation.direction = DIRECTION.BACKWARD + coords = np.radians(np.array([wayRelation.way.nodes[0].lat, wayRelation.way.nodes[0].lon], dtype=float)) + assert_array_almost_equal(wayRelation.last_node_coordinates, coords) + + def test_way_relation_node_before_edge_coordinates(self): + wayRelation = WayRelation(mockOSMWay_01_01_LongCurvy) + + coords = wayRelation.node_before_edge_coordinates(0) + assert_array_almost_equal(coords, np.array([0., 0.])) + + coords = wayRelation.node_before_edge_coordinates(wayRelation.way.nodes[0].id) + coords_e = np.radians(np.array([wayRelation.way.nodes[1].lat, wayRelation.way.nodes[1].lon], dtype=float)) + assert_array_almost_equal(coords, coords_e) + + coords = wayRelation.node_before_edge_coordinates(wayRelation.way.nodes[-1].id) + coords_e = np.radians(np.array([wayRelation.way.nodes[-2].lat, wayRelation.way.nodes[-2].lon], dtype=float)) + assert_array_almost_equal(coords, coords_e) + + def test_way_relation_split_no_matching_node(self): + wayRelation = WayRelation(mockOSMWay_01_01_LongCurvy) + + wrs = wayRelation.split(0) + self.assertEqual(len(wrs), 0) + + def test_way_relation_split_use_correct_ids(self): + wayRelation = WayRelation(mockOSMWay_01_01_LongCurvy) + + wrs = wayRelation.split(wayRelation._nodes_ids[5], [-100, -200]) + self.assertEqual(wrs[0].id, -100) + self.assertEqual(wrs[1].id, -200) + + def test_way_relation_split_on_edge_node(self): + wayRelation = WayRelation(mockOSMWay_01_01_LongCurvy) + edge_node_ids = wayRelation.edge_nodes_ids + + for edge_node_id in edge_node_ids: + wrs = wayRelation.split(edge_node_id) + self.assertEqual(len(wrs), 1) + self.assertEqual(wrs[0], wayRelation) + self.assertEqual(wrs[0].way.tags, wayRelation.way.tags) + + def test_way_relation_split_on_internal_node(self): + wayRelation = WayRelation(mockOSMWay_01_01_LongCurvy) + way_ids = [-10, -20] + + for idx, node_id in enumerate(wayRelation._nodes_ids): + if idx == 0 or idx == len(wayRelation._nodes_ids) - 1: + continue + wrs = wayRelation.split(node_id, way_ids) + self.assertEqual(len(wrs), 2) + assert_array_almost_equal(wrs[0]._nodes_ids, wayRelation._nodes_ids[:idx + 1]) + assert_array_almost_equal(wrs[1]._nodes_ids, wayRelation._nodes_ids[idx:]) + self.assertIn(node_id, wrs[0].edge_nodes_ids) + self.assertIn(node_id, wrs[1].edge_nodes_ids) + self.assertEqual(wrs[0].way.tags, wayRelation.way.tags) + self.assertEqual(wrs[1].way.tags, wayRelation.way.tags) + self.assertEqual(way_ids, [wr.id for wr in wrs]) + + # Helpers + def make_wayRelation_location_dirty(self, wayRelation): + wayRelation.distance_to_node_ahead = 10. + wayRelation.location_rad = 0.8 + wayRelation.bearing_rad = 2. + wayRelation.active = True + wayRelation.diverting = True + wayRelation.ahead_idx = 5 + wayRelation.behind_idx = 4 + wayRelation._active_bearing_delta = 3. + wayRelation._distance_to_way = 20. + + def assert_wayRelation_variables_reset(self, wayRelation): + self.assertEqual(wayRelation.distance_to_node_ahead, 0.) + self.assertIsNone(wayRelation.location_rad) + self.assertIsNone(wayRelation.bearing_rad) + self.assertFalse(wayRelation.active) + self.assertFalse(wayRelation.diverting) + self.assertIsNone(wayRelation.ahead_idx) + self.assertIsNone(wayRelation.behind_idx) + self.assertIsNone(wayRelation._active_bearing_delta) + self.assertIsNone(wayRelation._distance_to_way) + + def wayRelation_mid_point_rad(self, wayRelation): + return np.average(wayRelation.bbox, axis=0) diff --git a/selfdrive/mapd/test/test_WayRelationIndex.py b/selfdrive/mapd/test/test_WayRelationIndex.py new file mode 100644 index 000000000..faa70c141 --- /dev/null +++ b/selfdrive/mapd/test/test_WayRelationIndex.py @@ -0,0 +1,74 @@ +import unittest +from selfdrive.mapd.lib.WayRelationIndex import WayRelationIndex +from selfdrive.mapd.test.mock_data import mockWayCollection01 + + +class TestWayRelationIndex(unittest.TestCase): + def test_init_and_add(self): + wrs = mockWayCollection01.way_relations + wr_index = WayRelationIndex(wrs) + + # exptected init logic, including add logic. + edge_nodes_index_dict = {} + full_nodes_index_dict = {} + for wr in wrs: + for node in wr.way.nodes: + node_id = node.id + full_nodes_index_dict[node_id] = full_nodes_index_dict.get(node_id, []) + [wr] + if node_id in wr.edge_nodes_ids: + edge_nodes_index_dict[node_id] = edge_nodes_index_dict.get(node_id, []) + [wr] + + # assert logic delivers same result + self.assertDictEqual(edge_nodes_index_dict, wr_index._edge_nodes_index_dict) + self.assertDictEqual(full_nodes_index_dict, wr_index._full_nodes_index_dict) + self.assertEqual(len(wr_index._edge_nodes_index_dict), 586) + self.assertEqual(len(wr_index._full_nodes_index_dict), 2342) + + def test_remove(self): + wrs = mockWayCollection01.way_relations + wr_index = WayRelationIndex(wrs) + + wr_to_remove = wrs[0] + affected_full_node_ids = [nd.id for nd in wr_to_remove.way.nodes] + affected_edge_node_ids = wr_to_remove.edge_nodes_ids + + initial_full_lists = [wr_index._full_nodes_index_dict[ndid] for ndid in affected_full_node_ids] + initial_edge_lists = [wr_index._edge_nodes_index_dict[ndid] for ndid in affected_edge_node_ids] + + expected_final_full_lists = [[wr for wr in li if wr is not wr_to_remove] for li in initial_full_lists] + expected_final_edge_lists = [[wr for wr in li if wr is not wr_to_remove] for li in initial_edge_lists] + + wr_index.remove(wr_to_remove) + + final_full_lists = [wr_index._full_nodes_index_dict[ndid] for ndid in affected_full_node_ids] + final_edge_lists = [wr_index._edge_nodes_index_dict[ndid] for ndid in affected_edge_node_ids] + + for idx, li in enumerate(final_full_lists): + self.assertListEqual(li, expected_final_full_lists[idx]) + + for idx, li in enumerate(final_edge_lists): + self.assertListEqual(li, expected_final_edge_lists[idx]) + + def test_way_relations_with_edge_node_id(self): + wr_index = WayRelationIndex([]) + ref_dict = { + 0: ["fake_wr1", "fake_wr2"], + 1: ["fake_wr3"], + 3: ["fake_wr4", "fake_wr5", "fake_wr6"], + } + wr_index._edge_nodes_index_dict = ref_dict + + for key, li in ref_dict.items(): + self.assertListEqual(li, wr_index.way_relations_with_edge_node_id(key)) + + def test_way_relations_with_node_id(self): + wr_index = WayRelationIndex([]) + ref_dict = { + 0: ["fake_wr1", "fake_wr2"], + 1: ["fake_wr3"], + 3: ["fake_wr4", "fake_wr5", "fake_wr6"], + } + wr_index._full_nodes_index_dict = ref_dict + + for key, li in ref_dict.items(): + self.assertListEqual(li, wr_index.way_relations_with_node_id(key)) diff --git a/selfdrive/mapd/test/test_geo.py b/selfdrive/mapd/test/test_geo.py new file mode 100644 index 000000000..a18fe717b --- /dev/null +++ b/selfdrive/mapd/test/test_geo.py @@ -0,0 +1,234 @@ +import unittest +from selfdrive.mapd.lib.geo import vectors, ref_vectors, bearing_to_points, distance_to_points +import numpy as np +from numpy.testing import assert_array_almost_equal +from selfdrive.mapd.test.mock_data import mockNodesData01 + + +class TestMapsdGeoLibrary(unittest.TestCase): + def test_vectors(self): + points = mockNodesData01.radians + expected = np.array([ + [-1.34011951e-05, 1.00776468e-05], + [-5.83610920e-06, 4.41046897e-06], + [-7.83348567e-06, 5.94114032e-06], + [-7.08560788e-06, 5.30408795e-06], + [-6.57632550e-06, 4.05791838e-06], + [-1.16077872e-06, 6.91151252e-07], + [-1.53178098e-05, 9.62215139e-06], + [-5.76314175e-06, 3.55176643e-06], + [-1.61124141e-05, 9.86127759e-06], + [-1.48006628e-05, 8.58192512e-06], + [-1.72237209e-06, 1.60570482e-06], + [-8.68985228e-06, 9.22062311e-06], + [-1.42922812e-06, 1.51494711e-06], + [-3.39761486e-06, 2.57087743e-06], + [-2.75467373e-06, 1.28631255e-06], + [-1.57501989e-05, 5.72309451e-06], + [-2.52143954e-06, 1.34565295e-06], + [-1.65278643e-06, 1.28630942e-06], + [-2.22196114e-05, 1.64360838e-05], + [-5.88675934e-06, 4.08234746e-06], + [-1.83673390e-06, 1.46782408e-06], + [-1.55004206e-06, 1.51843800e-06], + [-1.20451533e-06, 2.06298011e-06], + [-1.91801338e-06, 4.64083285e-06], + [-2.38653483e-06, 5.60076524e-06], + [-1.65269781e-06, 5.78402290e-06], + [-3.66908309e-07, 2.75412965e-06], + [0.00000000e+00, 1.92858882e-06], + [9.09242615e-08, 2.66162711e-06], + [3.14490354e-07, 1.53065382e-06], + [8.66452477e-08, 4.83456208e-07], + [2.41750593e-07, 1.10828411e-06], + [7.43745228e-06, 1.27618831e-05], + [5.59968054e-06, 9.63947367e-06], + [2.01951467e-06, 2.75413219e-06], + [4.59952643e-07, 6.42281301e-07], + [1.74353749e-06, 1.74533121e-06], + [2.57144338e-06, 2.11185266e-06], + [1.46893187e-05, 1.11999169e-05], + [3.84659229e-05, 2.85527952e-05], + [2.71627936e-05, 1.98727946e-05], + [8.44632540e-06, 6.15058628e-06], + [2.29420323e-06, 1.92859222e-06], + [2.58083439e-06, 3.16952222e-06], + [3.76373643e-06, 5.14174911e-06], + [5.32416098e-06, 6.51707770e-06], + [8.62890928e-06, 1.11998258e-05], + [1.25762497e-05, 1.65231340e-05], + [8.90452991e-06, 1.10148240e-05], + [4.86505726e-06, 4.59023120e-06], + [3.85545276e-06, 3.39642031e-06], + [3.48753893e-06, 3.30566145e-06], + [2.99557303e-06, 2.61276368e-06], + [2.15496788e-06, 1.87797727e-06], + [4.10564937e-06, 3.58142649e-06], + [1.53680853e-06, 1.33866906e-06], + [4.99540175e-06, 4.35635790e-06], + [1.37744970e-06, 1.19380643e-06], + [1.74319821e-06, 1.28456429e-06], + [9.99931238e-07, 1.14493663e-06], + [6.42735560e-07, 1.19380547e-06], + [3.66818436e-07, 1.46782199e-06], + [5.45413874e-08, 1.83783170e-06], + [-1.35818548e-07, 1.14842666e-06], + [-5.50758101e-07, 3.02989178e-06], + [-4.58785270e-07, 2.66162724e-06], + [-2.51315555e-07, 1.19031459e-06], + [-3.91409773e-07, 1.65457223e-06], + [-2.14525206e-06, 5.67755902e-06], + [-4.24558096e-07, 1.39102753e-06], + [-1.46936730e-06, 5.32325561e-06], + [-1.37632061e-06, 4.59021715e-06], + [-8.26642899e-07, 4.68097349e-06], + [-6.42702724e-07, 4.95673534e-06], + [-3.66796960e-07, 7.25009780e-06], + [-1.82861669e-07, 8.99542699e-06], + [4.09564134e-07, 6.11214315e-06], + [7.80629912e-08, 1.45734993e-06], + [4.81205526e-07, 7.56076647e-06], + [2.01036346e-07, 2.42775302e-06]]) + + v = vectors(points) + assert_array_almost_equal(v, expected) + + def test_ref_vectors(self): + points = mockNodesData01.radians + expected = np.array([ + [1.59924145e-04, -1.07153714e-04], + [1.46520873e-04, -9.70788297e-05], + [1.40683931e-04, -9.26694631e-05], + [1.32849368e-04, -8.67297434e-05], + [1.25762852e-04, -8.14268689e-05], + [1.19185869e-04, -7.73700167e-05], + [1.18024984e-04, -7.66790438e-05], + [1.02705711e-04, -6.70592230e-05], + [9.69420991e-05, -6.35082196e-05], + [8.08284530e-05, -5.36489556e-05], + [6.60268961e-05, -4.50685727e-05], + [6.43043874e-05, -4.34630144e-05], + [5.56137708e-05, -3.42431117e-05], + [5.41844341e-05, -3.27282671e-05], + [5.07866397e-05, -3.01576270e-05], + [4.80318817e-05, -2.88714948e-05], + [3.22813286e-05, -2.31493755e-05], + [2.97598330e-05, -2.18038275e-05], + [2.81069973e-05, -2.05175815e-05], + [5.88679032e-06, -4.08230278e-06], + [0.00000000e+00, 0.00000000e+00], + [-1.83673390e-06, 1.46782408e-06], + [-3.38677236e-06, 2.98626574e-06], + [-4.59127869e-06, 5.04925111e-06], + [-6.50926460e-06, 9.69009532e-06], + [-8.89575243e-06, 1.52908806e-05], + [-1.05483839e-05, 2.10749224e-05], + [-1.09152548e-05, 2.38290571e-05], + [-1.09152276e-05, 2.57576459e-05], + [-1.08242659e-05, 2.84192717e-05], + [-1.05097542e-05, 2.99499212e-05], + [-1.04231024e-05, 3.04333762e-05], + [-1.01813369e-05, 3.15416571e-05], + [-2.74371711e-06, 4.43034426e-05], + [2.85599752e-06, 5.39428964e-05], + [4.87550206e-06, 5.66970360e-05], + [5.33545066e-06, 5.73393202e-05], + [7.07897615e-06, 5.90846634e-05], + [9.65040026e-06, 6.11965396e-05], + [2.43395796e-05, 7.23966392e-05], + [6.28046063e-05, 1.00950641e-04], + [8.99657904e-05, 1.20825635e-04], + [9.84114021e-05, 1.26977201e-04], + [1.00705361e-04, 1.28906084e-04], + [1.03285783e-04, 1.32075942e-04], + [1.07048835e-04, 1.37218192e-04], + [1.12372096e-04, 1.43736004e-04], + [1.20999382e-04, 1.54937080e-04], + [1.33573053e-04, 1.71462176e-04], + [1.42475686e-04, 1.82478533e-04], + [1.47339899e-04, 1.87069658e-04], + [1.51194707e-04, 1.90466811e-04], + [1.54681601e-04, 1.93773152e-04], + [1.57676653e-04, 1.96386513e-04], + [1.59831239e-04, 1.98264929e-04], + [1.63936150e-04, 2.01847201e-04], + [1.65472675e-04, 2.03186195e-04], + [1.70467147e-04, 2.07543619e-04], + [1.71844334e-04, 2.08737728e-04], + [1.73587247e-04, 2.10022678e-04], + [1.74586922e-04, 2.11167839e-04], + [1.75229389e-04, 2.12361789e-04], + [1.75595876e-04, 2.13829694e-04], + [1.75650001e-04, 2.15667538e-04], + [1.75513922e-04, 2.16815933e-04], + [1.74962478e-04, 2.19845700e-04], + [1.74503092e-04, 2.22507224e-04], + [1.74251509e-04, 2.23697482e-04], + [1.73859727e-04, 2.25351966e-04], + [1.71713202e-04, 2.31029044e-04], + [1.71288336e-04, 2.32419977e-04], + [1.69817793e-04, 2.37742908e-04], + [1.68440467e-04, 2.42332824e-04], + [1.67612807e-04, 2.47013617e-04], + [1.66969033e-04, 2.51970213e-04], + [1.66600674e-04, 2.59220232e-04], + [1.66415880e-04, 2.68215619e-04], + [1.66824132e-04, 2.74327850e-04], + [1.66901881e-04, 2.75785216e-04], + [1.67381459e-04, 2.83346086e-04], + [1.67581971e-04, 2.85773882e-04]]) + + v = ref_vectors(points[20], points) + assert_array_almost_equal(v, expected) + + def test_bearing_to_points(self): + points = mockNodesData01.radians + expected = np.array([ + 2.16112265, 2.15595027, 2.15326799, 2.14916735, 2.14538642, + 2.14657678, 2.14694997, 2.1492257, 2.1507589, 2.15676899, + 2.16973441, 2.1651606, 2.12270237, 2.11416356, 2.10665211, + 2.11201708, 2.19291574, 2.2031069, 2.20136186, 2.17712517, + 0., -0.8965745, -0.84815954, -0.73792895, -0.59150953, + -0.5269061, -0.46406215, -0.42954043, -0.4008254, -0.36391371, + -0.33748609, -0.32996807, -0.31223189, -0.06185112, 0.05289544, + 0.08578116, 0.0927833, 0.11924233, 0.15640718, 0.32432622, + 0.55653415, 0.64003094, 0.6593301, 0.66319086, 0.66367982, + 0.66251077, 0.66354137, 0.66302176, 0.66181884, 0.66291139, + 0.66714676, 0.67095594, 0.67367984, 0.6765003, 0.67847961, + 0.68212344, 0.68345356, 0.68762778, 0.68876073, 0.69070183, + 0.69085143, 0.68988665, 0.68753177, 0.68348884, 0.68051081, + 0.67220053, 0.66506824, 0.66177969, 0.65712162, 0.63916951, + 0.6351146, 0.62025347, 0.60741567, 0.59618923, 0.58521935, + 0.57122582, 0.55532475, 0.54636839, 0.54422542, 0.53357655, + 0.53037033]) + + v = bearing_to_points(points[20], points) + assert_array_almost_equal(v, expected) + + def test_distance_to_points(self): + points = mockNodesData01.radians + expected = np.array([ + 1226.82569068, 1120.13820773, 1073.61121415, 1011.10016574, + 954.81557436, 905.58045038, 896.97734399, 781.7102819, + 738.58271117, 618.26145463, 509.47052142, 494.6403804, + 416.22483123, 403.42108699, 376.42615499, 357.15106681, + 253.15957483, 235.11572972, 221.77439728, 45.65465979, + 0., 14.98414, 28.77606056, 43.49299446, + 74.39463425, 112.74005248, 150.19482607, 167.03665191, + 178.28443483, 193.80834084, 202.28154097, 205.01173833, + 211.22777104, 282.88676739, 344.25957352, 362.66370657, + 367.00206795, 379.23951996, 394.82505328, 486.76073331, + 757.70254732, 960.03439155, 1023.81434529, 1042.49401713, + 1068.53770096, 1109.12696535, 1162.74555108, 1252.847351, + 1385.17179405, 1475.42502599, 1517.57849916, 1549.79838056, + 1580.12405964, 1605.05483058, 1622.98937809, 1657.19268821, + 1669.99157205, 1711.63883132, 1723.09133393, 1736.47655688, + 1746.16073119, 1754.63481838, 1763.34186103, 1772.62691273, + 1777.76189094, 1790.62024447, 1802.11488235, 1807.1040605, + 1813.90756815, 1834.49265566, 1840.00708445, 1861.96087374, + 1880.81678093, 1902.42091191, 1926.37194131, 1963.78301115, + 2011.62679077, 2046.18028824, 2054.37811294, 2097.30347724, + 2111.28586072]) + + v = distance_to_points(points[20], points) + assert_array_almost_equal(v, expected) diff --git a/selfdrive/thermald/power_monitoring.py b/selfdrive/thermald/power_monitoring.py index fd7539ffd..1814be7b1 100644 --- a/selfdrive/thermald/power_monitoring.py +++ b/selfdrive/thermald/power_monitoring.py @@ -40,9 +40,9 @@ class PowerMonitoring: self.car_voltage_instant_mV = 12e3 # Last value of peripheralState voltage self.integration_lock = threading.Lock() self.is_oneplus = os.path.isfile('/ONEPLUS') - self.dp_auto_shutdown = True - self.dp_auto_shutdown_in = 300 - self.dp_auto_shutdown_voltage_prev = 0 + self.dp_device_auto_shutdown = self.params.get_bool("dp_device_auto_shutdown") + self.dp_device_auto_shutdown_in = int(self.params.get("dp_device_auto_shutdown_in")) + self.dp_device_auto_shutdown_voltage_prev = 0 car_battery_capacity_uWh = self.params.get("CarBatteryCapacity") if car_battery_capacity_uWh is None: @@ -186,23 +186,6 @@ class PowerMonitoring: should_shutdown &= started_seen or (now > MIN_ON_TIME_S) return should_shutdown - # See if we need to disable charging - def legacy_should_disable_charging(self, ignition: bool, in_car: bool, offroad_timestamp: Optional[float]) -> bool: - if offroad_timestamp is None: - return False - - now = sec_since_boot() - - disable_charging = False - disable_charging |= (now - offroad_timestamp) > MAX_TIME_OFFROAD_S - disable_charging |= (self.car_voltage_mV < (VBATT_PAUSE_CHARGING * 1e3)) and (self.car_voltage_instant_mV > (VBATT_INSTANT_PAUSE_CHARGING * 1e3)) - disable_charging |= (self.car_battery_capacity_uWh <= 0) - disable_charging &= not ignition - disable_charging &= (not self.params.get_bool("DisablePowerDown")) - disable_charging &= in_car - disable_charging |= self.params.get_bool("ForcePowerDown") - return disable_charging - def legacy_should_shutdown(self, peripheralState, ignition, in_car, offroad_timestamp, started_seen): if offroad_timestamp is None: return False @@ -211,18 +194,18 @@ class PowerMonitoring: panda_charging = (peripheralState.usbPowerMode != log.PeripheralState.UsbPowerMode.client) # BATT_PERC_OFF = 3 if self.is_oneplus else 10 - if started_seen and self.dp_auto_shutdown and (now - offroad_timestamp) > self.dp_auto_shutdown_in: + if started_seen and self.dp_device_auto_shutdown and (now - offroad_timestamp) > self.dp_device_auto_shutdown_in: self.params.put_bool("ForcePowerDown", True) if not panda_charging: return True # rick - if voltage is not updating, assuming the panda is disconnected (e.g. white panda or black w/o comma power) - if peripheralState.voltage == self.dp_auto_shutdown_voltage_prev: + if peripheralState.voltage == self.dp_device_auto_shutdown_voltage_prev: return True - self.dp_auto_shutdown_voltage_prev = peripheralState.voltage + self.dp_device_auto_shutdown_voltage_prev = peripheralState.voltage should_shutdown = False # Wait until we have shut down charging before powering down - should_shutdown |= (not panda_charging and self.legacy_should_disable_charging(ignition, in_car, offroad_timestamp)) + # should_shutdown |= (not panda_charging and self.legacy_should_disable_charging(ignition, in_car, offroad_timestamp)) # should_shutdown |= ((HARDWARE.get_battery_capacity() < BATT_PERC_OFF) and (not HARDWARE.get_battery_charging()) and ((now - offroad_timestamp) > 60)) should_shutdown &= started_seen or (now > MIN_ON_TIME_S) return should_shutdown diff --git a/selfdrive/thermald/thermald.py b/selfdrive/thermald/thermald.py index 71e1ba2d3..2839651e4 100755 --- a/selfdrive/thermald/thermald.py +++ b/selfdrive/thermald/thermald.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -import datetime +# import datetime import os import queue import threading @@ -191,6 +191,8 @@ def thermald_thread(end_event, hw_queue): fan_controller = None + dp_device_disable_temp_check = params.get_bool("dp_device_disable_temp_check") + while not end_event.is_set(): sm.update(PANDA_STATES_TIMEOUT) @@ -296,8 +298,9 @@ def thermald_thread(end_event, hw_queue): startup_conditions["not_taking_snapshot"] = not params.get_bool("IsTakingSnapshot") # if any CPU gets above 107 or the battery gets above 63, kill all processes # controls will warn with CPU above 95 or battery above 60 - onroad_conditions["device_temp_good"] = thermal_status < ThermalStatus.danger - set_offroad_alert_if_changed("Offroad_TemperatureTooHigh", (not onroad_conditions["device_temp_good"])) + if not dp_device_disable_temp_check: + onroad_conditions["device_temp_good"] = thermal_status < ThermalStatus.danger + set_offroad_alert_if_changed("Offroad_TemperatureTooHigh", (not onroad_conditions["device_temp_good"])) # TODO: this should move to TICI.initialize_hardware, but we currently can't import params there if TICI: @@ -366,10 +369,10 @@ def thermald_thread(end_event, hw_queue): if not TICI: # Check if we need to disable charging (handled by boardd) - msg.deviceState.chargingDisabled = power_monitor.legacy_should_disable_charging(onroad_conditions["ignition"], in_car, off_ts) + msg.deviceState.chargingDisabled = power_monitor.legacy_should_shutdown(peripheralState, onroad_conditions["ignition"], in_car, off_ts, started_seen) # Check if we need to shut down - if power_monitor.legacy_should_shutdown(peripheralState, onroad_conditions["ignition"], in_car, off_ts, started_seen): + if msg.deviceState.chargingDisabled: cloudlog.warning(f"shutting device down, offroad since {off_ts}") params.put_bool("DoShutdown", True) else: diff --git a/selfdrive/ui/_ui b/selfdrive/ui/_ui index 5c7b6583d..35c78ef81 100755 Binary files a/selfdrive/ui/_ui and b/selfdrive/ui/_ui differ diff --git a/selfdrive/ui/qt/libpython_helpers.so b/selfdrive/ui/qt/libpython_helpers.so index 7725d6783..02dcf6c58 100755 Binary files a/selfdrive/ui/qt/libpython_helpers.so and b/selfdrive/ui/qt/libpython_helpers.so differ diff --git a/selfdrive/ui/qt/spinner b/selfdrive/ui/qt/spinner index 8e7061187..f813db1d2 100755 Binary files a/selfdrive/ui/qt/spinner and b/selfdrive/ui/qt/spinner differ diff --git a/selfdrive/ui/qt/text b/selfdrive/ui/qt/text index 1cd75eb92..39eb41a62 100755 Binary files a/selfdrive/ui/qt/text and b/selfdrive/ui/qt/text differ diff --git a/selfdrive/ui/soundd/_soundd b/selfdrive/ui/soundd/_soundd index 0f58ba01e..73bfa66df 100755 Binary files a/selfdrive/ui/soundd/_soundd and b/selfdrive/ui/soundd/_soundd differ diff --git a/selfdrive/ui/translations/main_de.qm b/selfdrive/ui/translations/main_de.qm index 199f0a40d..d8ad177a4 100644 Binary files a/selfdrive/ui/translations/main_de.qm and b/selfdrive/ui/translations/main_de.qm differ diff --git a/selfdrive/ui/translations/main_de.ts b/selfdrive/ui/translations/main_de.ts index 46a2ed414..17ced2e4c 100644 --- a/selfdrive/ui/translations/main_de.ts +++ b/selfdrive/ui/translations/main_de.ts @@ -127,6 +127,183 @@ Abbrechen + + DPCtrlPanel + + When enabled, openpilot lateral Control will be always on when ACC MAIN is ON. +Reboot required. + + + + Enable MapD + + + + When enabled, openpilot will display current road name and speed limit on the screen. +Reboot required. + + + + Enable Lane Priority Mode + + + + When enabled, openpilot will use lane lines for lateral control, fallback to laneless mode automatically when lane lines probabilities are low. +Reboot required. + + + + Enable Auto Shutdown + + + + When enabled, openpilot will shutdown the device automatically. +Reboot required. + + + + Auto Shutdown In + + + + mins + + + + Enable Stop and Go (SnG) Hack + + + + When enabled, openpilot will stop sending standstill signal when the car is fully stopped. +ONLY WORK ON SOME VEHICLES. +Reboot Required. + + + + Enable Door Auto Locking + + + + When enabled, openpilot will attempt to lock the doors when drive above 10 km/h (6.2 mph). +Reboot Required. + + + + Enable Door Auto Unlocking + + + + When enabled, openpilot will attempt to unlock the doors when shift to gear P. +Reboot Required. + + + + When enabled, openpilot will use the good old 0.8.13.1 driving model. +For safety reason, vision only openpilot longitudinal will be disabled. +Reboot required. + + + + Use 0.8.13.1 Driving Model + + + + Ctrl - Lateral + + + + Enable ALKA + + + + Ctrl - Longitudinal + + + + Device + Gerät + + + Ctrl - Overall + + + + Toyota / Lexus + + + + Disable Temp Check + + + + When enabled, openpilot will disable device temperature check. +**NOTED** An overheated device may result in random shutdowns or lag. +Reboot required. + + + + Disable IR + + + + When enabled, openpilot will disable IR completely. +Reboot required. + + + + Standard + + + + On-Road + + + + MAIN + + + + OP + + + + Off + + + + Display Mode + + + + On-Road - When driving, the display will be off (excl. warning). +MAIN - When ACC MAIN is on, the display will be off (excl. warning). +OP - When OP is enabled, the display will be off (excl. warning). +Off - the display will be off completely (incl. warning). +Reboot required. + + + + Warning + + + + Audible Alert Mode + + + + Warning - Only emits sound when there is a warning. +Off - Does not emit any sound at all. + + + + Adjust your shutdown waiting period. + + + + Immediately + + + DeclinePage @@ -551,14 +728,6 @@ location set comma prime comma prime - - CONNECT.COMMA.AI - CONNECT.COMMA.AI - - - COMMA POINTS - COMMA PUNKTE - QObject @@ -908,6 +1077,26 @@ This may take up to a minute. Uninstall Deinstallieren + + failed to check for update + + + + up to date, last checked %1 + + + + DOWNLOAD + + + + update available + + + + never + + SshControl @@ -1086,6 +1275,26 @@ This may take up to a minute. On this car, openpilot defaults to the car's built-in ACC instead of openpilot's longitudinal control. Enable this to switch to openpilot longitudinal control. Enabling Experimental mode is recommended when enabling openpilot longitudinal control alpha. + + Aggressive + + + + Standard + + + + Relaxed + + + + Driving Personality + + + + Standard is recommended. In aggressive mode, openpilot will follow lead cars closer and be more aggressive with the gas and brake. + + Enable Right-Hand Drive @@ -1094,33 +1303,6 @@ This may take up to a minute. Allow openpilot to obey left-hand traffic conventions and perform driver monitoring on right driver seat. - - Enable ALKA - - - - When enabled, openpilot lateral Control will be always on when ACC MAIN is ON. -Reboot required. - - - - Enable MapD - - - - When enabled, openpilot will display current road name and speed limit on the screen. -Reboot required. - - - - Enable Lane Priority Mode - - - - When enabled, openpilot will use lane lines for lateral control, fallback to laneless mode automatically when lane lines probabilities are low. -Reboot required. - - Updater @@ -1157,6 +1339,29 @@ Reboot required. Aktualisierung fehlgeschlagen + + WiFiPromptWidget + + Setup Wi-Fi + + + + Connect to Wi-Fi to upload driving data and help improve openpilot + + + + Open Settings + + + + Uploading training data + + + + Your data is used to train driving models and help improve openpilot + + + WifiUI diff --git a/selfdrive/ui/translations/main_en.qm b/selfdrive/ui/translations/main_en.qm index dab9f0ee1..7951d29ae 100644 Binary files a/selfdrive/ui/translations/main_en.qm and b/selfdrive/ui/translations/main_en.qm differ diff --git a/selfdrive/ui/translations/main_ja.qm b/selfdrive/ui/translations/main_ja.qm index 1a29f41f3..c8d72233d 100644 Binary files a/selfdrive/ui/translations/main_ja.qm and b/selfdrive/ui/translations/main_ja.qm differ diff --git a/selfdrive/ui/translations/main_ja.ts b/selfdrive/ui/translations/main_ja.ts index 49723ce98..4b76f56b1 100644 --- a/selfdrive/ui/translations/main_ja.ts +++ b/selfdrive/ui/translations/main_ja.ts @@ -127,6 +127,183 @@ キャンセル + + DPCtrlPanel + + When enabled, openpilot lateral Control will be always on when ACC MAIN is ON. +Reboot required. + + + + Enable MapD + + + + When enabled, openpilot will display current road name and speed limit on the screen. +Reboot required. + + + + Enable Lane Priority Mode + + + + When enabled, openpilot will use lane lines for lateral control, fallback to laneless mode automatically when lane lines probabilities are low. +Reboot required. + + + + Enable Auto Shutdown + + + + When enabled, openpilot will shutdown the device automatically. +Reboot required. + + + + Auto Shutdown In + + + + mins + + + + Enable Stop and Go (SnG) Hack + + + + When enabled, openpilot will stop sending standstill signal when the car is fully stopped. +ONLY WORK ON SOME VEHICLES. +Reboot Required. + + + + Enable Door Auto Locking + + + + When enabled, openpilot will attempt to lock the doors when drive above 10 km/h (6.2 mph). +Reboot Required. + + + + Enable Door Auto Unlocking + + + + When enabled, openpilot will attempt to unlock the doors when shift to gear P. +Reboot Required. + + + + When enabled, openpilot will use the good old 0.8.13.1 driving model. +For safety reason, vision only openpilot longitudinal will be disabled. +Reboot required. + + + + Use 0.8.13.1 Driving Model + + + + Ctrl - Lateral + + + + Enable ALKA + + + + Ctrl - Longitudinal + + + + Device + デバイス + + + Ctrl - Overall + + + + Toyota / Lexus + + + + Disable Temp Check + + + + When enabled, openpilot will disable device temperature check. +**NOTED** An overheated device may result in random shutdowns or lag. +Reboot required. + + + + Disable IR + + + + When enabled, openpilot will disable IR completely. +Reboot required. + + + + Standard + + + + On-Road + + + + MAIN + + + + OP + + + + Off + + + + Display Mode + + + + On-Road - When driving, the display will be off (excl. warning). +MAIN - When ACC MAIN is on, the display will be off (excl. warning). +OP - When OP is enabled, the display will be off (excl. warning). +Off - the display will be off completely (incl. warning). +Reboot required. + + + + Warning + + + + Audible Alert Mode + + + + Warning - Only emits sound when there is a warning. +Off - Does not emit any sound at all. + + + + Adjust your shutdown waiting period. + + + + Immediately + + + DeclinePage @@ -552,14 +729,6 @@ location set comma prime comma prime - - CONNECT.COMMA.AI - CONNECT.COMMA.AI - - - COMMA POINTS - COMMA POINTS - QObject @@ -904,6 +1073,26 @@ This may take up to a minute. Uninstall アンインストール + + failed to check for update + + + + up to date, last checked %1 + + + + DOWNLOAD + + + + update available + + + + never + + SshControl @@ -1080,6 +1269,26 @@ This may take up to a minute. On this car, openpilot defaults to the car's built-in ACC instead of openpilot's longitudinal control. Enable this to switch to openpilot longitudinal control. Enabling Experimental mode is recommended when enabling openpilot longitudinal control alpha. + + Aggressive + + + + Standard + + + + Relaxed + + + + Driving Personality + + + + Standard is recommended. In aggressive mode, openpilot will follow lead cars closer and be more aggressive with the gas and brake. + + Enable Right-Hand Drive @@ -1088,33 +1297,6 @@ This may take up to a minute. Allow openpilot to obey left-hand traffic conventions and perform driver monitoring on right driver seat. - - Enable ALKA - - - - When enabled, openpilot lateral Control will be always on when ACC MAIN is ON. -Reboot required. - - - - Enable MapD - - - - When enabled, openpilot will display current road name and speed limit on the screen. -Reboot required. - - - - Enable Lane Priority Mode - - - - When enabled, openpilot will use lane lines for lateral control, fallback to laneless mode automatically when lane lines probabilities are low. -Reboot required. - - Updater @@ -1151,6 +1333,29 @@ Reboot required. 更新失敗 + + WiFiPromptWidget + + Setup Wi-Fi + + + + Connect to Wi-Fi to upload driving data and help improve openpilot + + + + Open Settings + + + + Uploading training data + + + + Your data is used to train driving models and help improve openpilot + + + WifiUI diff --git a/selfdrive/ui/translations/main_ko.qm b/selfdrive/ui/translations/main_ko.qm index 02697e10a..bd1d2ff24 100644 Binary files a/selfdrive/ui/translations/main_ko.qm and b/selfdrive/ui/translations/main_ko.qm differ diff --git a/selfdrive/ui/translations/main_ko.ts b/selfdrive/ui/translations/main_ko.ts index a126888cc..1ec3b81fa 100644 --- a/selfdrive/ui/translations/main_ko.ts +++ b/selfdrive/ui/translations/main_ko.ts @@ -127,6 +127,183 @@ 취소 + + DPCtrlPanel + + When enabled, openpilot lateral Control will be always on when ACC MAIN is ON. +Reboot required. + + + + Enable MapD + + + + When enabled, openpilot will display current road name and speed limit on the screen. +Reboot required. + + + + Enable Lane Priority Mode + + + + When enabled, openpilot will use lane lines for lateral control, fallback to laneless mode automatically when lane lines probabilities are low. +Reboot required. + + + + Enable Auto Shutdown + + + + When enabled, openpilot will shutdown the device automatically. +Reboot required. + + + + Auto Shutdown In + + + + mins + + + + Enable Stop and Go (SnG) Hack + + + + When enabled, openpilot will stop sending standstill signal when the car is fully stopped. +ONLY WORK ON SOME VEHICLES. +Reboot Required. + + + + Enable Door Auto Locking + + + + When enabled, openpilot will attempt to lock the doors when drive above 10 km/h (6.2 mph). +Reboot Required. + + + + Enable Door Auto Unlocking + + + + When enabled, openpilot will attempt to unlock the doors when shift to gear P. +Reboot Required. + + + + When enabled, openpilot will use the good old 0.8.13.1 driving model. +For safety reason, vision only openpilot longitudinal will be disabled. +Reboot required. + + + + Use 0.8.13.1 Driving Model + + + + Ctrl - Lateral + + + + Enable ALKA + + + + Ctrl - Longitudinal + + + + Device + 장치 + + + Ctrl - Overall + + + + Toyota / Lexus + + + + Disable Temp Check + + + + When enabled, openpilot will disable device temperature check. +**NOTED** An overheated device may result in random shutdowns or lag. +Reboot required. + + + + Disable IR + + + + When enabled, openpilot will disable IR completely. +Reboot required. + + + + Standard + + + + On-Road + + + + MAIN + + + + OP + + + + Off + + + + Display Mode + + + + On-Road - When driving, the display will be off (excl. warning). +MAIN - When ACC MAIN is on, the display will be off (excl. warning). +OP - When OP is enabled, the display will be off (excl. warning). +Off - the display will be off completely (incl. warning). +Reboot required. + + + + Warning + + + + Audible Alert Mode + + + + Warning - Only emits sound when there is a warning. +Off - Does not emit any sound at all. + + + + Adjust your shutdown waiting period. + + + + Immediately + + + DeclinePage @@ -552,14 +729,6 @@ location set comma prime comma prime - - CONNECT.COMMA.AI - CONNECT.COMMA.AI - - - COMMA POINTS - COMMA POINTS - QObject @@ -905,6 +1074,26 @@ This may take up to a minute. Uninstall 제거 + + failed to check for update + 업데이트 확인 실패 + + + up to date, last checked %1 + 최신 상태, 마지막으로 확인 %1 + + + DOWNLOAD + 다운로드 + + + update available + 업데이트 가능 + + + never + 업데이트 안함 + SshControl @@ -1081,6 +1270,26 @@ This may take up to a minute. On this car, openpilot defaults to the car's built-in ACC instead of openpilot's longitudinal control. Enable this to switch to openpilot longitudinal control. Enabling Experimental mode is recommended when enabling openpilot longitudinal control alpha. 이 차량은 openpilot 롱컨트롤 대신 차량의 내장 ACC로 기본 설정됩니다. openpilot 롱컨트롤으로 전환하려면 이 기능을 활성화하세요. openpilot 롱컨트롤 알파를 활성화하는경우 실험적 모드 활성화를 권장합니다. + + Aggressive + + + + Standard + + + + Relaxed + + + + Driving Personality + + + + Standard is recommended. In aggressive mode, openpilot will follow lead cars closer and be more aggressive with the gas and brake. + + Enable Right-Hand Drive @@ -1089,33 +1298,6 @@ This may take up to a minute. Allow openpilot to obey left-hand traffic conventions and perform driver monitoring on right driver seat. - - Enable ALKA - - - - When enabled, openpilot lateral Control will be always on when ACC MAIN is ON. -Reboot required. - - - - Enable MapD - - - - When enabled, openpilot will display current road name and speed limit on the screen. -Reboot required. - - - - Enable Lane Priority Mode - - - - When enabled, openpilot will use lane lines for lateral control, fallback to laneless mode automatically when lane lines probabilities are low. -Reboot required. - - Updater @@ -1152,6 +1334,29 @@ Reboot required. 업데이트 실패 + + WiFiPromptWidget + + Setup Wi-Fi + Wi-Fi 설정 + + + Connect to Wi-Fi to upload driving data and help improve openpilot + Wi-Fi에 연결하여 주행 데이터를 업로드하고 openpilot 개선에 참여하세요. + + + Open Settings + 설정 열기 + + + Uploading training data + 트레이닝 데이터 업로드 + + + Your data is used to train driving models and help improve openpilot + 귀하의 데이터는 운전 모델을 교육하고 openpilot을 개선하는 데 사용됩니다. + + WifiUI diff --git a/selfdrive/ui/translations/main_pt-BR.qm b/selfdrive/ui/translations/main_pt-BR.qm index 99700e0cc..dfd183ff6 100644 Binary files a/selfdrive/ui/translations/main_pt-BR.qm and b/selfdrive/ui/translations/main_pt-BR.qm differ diff --git a/selfdrive/ui/translations/main_pt-BR.ts b/selfdrive/ui/translations/main_pt-BR.ts index 7f7e92acf..1375b6184 100644 --- a/selfdrive/ui/translations/main_pt-BR.ts +++ b/selfdrive/ui/translations/main_pt-BR.ts @@ -127,6 +127,183 @@ Cancelar + + DPCtrlPanel + + When enabled, openpilot lateral Control will be always on when ACC MAIN is ON. +Reboot required. + + + + Enable MapD + + + + When enabled, openpilot will display current road name and speed limit on the screen. +Reboot required. + + + + Enable Lane Priority Mode + + + + When enabled, openpilot will use lane lines for lateral control, fallback to laneless mode automatically when lane lines probabilities are low. +Reboot required. + + + + Enable Auto Shutdown + + + + When enabled, openpilot will shutdown the device automatically. +Reboot required. + + + + Auto Shutdown In + + + + mins + + + + Enable Stop and Go (SnG) Hack + + + + When enabled, openpilot will stop sending standstill signal when the car is fully stopped. +ONLY WORK ON SOME VEHICLES. +Reboot Required. + + + + Enable Door Auto Locking + + + + When enabled, openpilot will attempt to lock the doors when drive above 10 km/h (6.2 mph). +Reboot Required. + + + + Enable Door Auto Unlocking + + + + When enabled, openpilot will attempt to unlock the doors when shift to gear P. +Reboot Required. + + + + When enabled, openpilot will use the good old 0.8.13.1 driving model. +For safety reason, vision only openpilot longitudinal will be disabled. +Reboot required. + + + + Use 0.8.13.1 Driving Model + + + + Ctrl - Lateral + + + + Enable ALKA + + + + Ctrl - Longitudinal + + + + Device + Dispositivo + + + Ctrl - Overall + + + + Toyota / Lexus + + + + Disable Temp Check + + + + When enabled, openpilot will disable device temperature check. +**NOTED** An overheated device may result in random shutdowns or lag. +Reboot required. + + + + Disable IR + + + + When enabled, openpilot will disable IR completely. +Reboot required. + + + + Standard + + + + On-Road + + + + MAIN + + + + OP + + + + Off + + + + Display Mode + + + + On-Road - When driving, the display will be off (excl. warning). +MAIN - When ACC MAIN is on, the display will be off (excl. warning). +OP - When OP is enabled, the display will be off (excl. warning). +Off - the display will be off completely (incl. warning). +Reboot required. + + + + Warning + + + + Audible Alert Mode + + + + Warning - Only emits sound when there is a warning. +Off - Does not emit any sound at all. + + + + Adjust your shutdown waiting period. + + + + Immediately + + + DeclinePage @@ -319,11 +496,11 @@ ExperimentalModeButton EXPERIMENTAL MODE ON - MODO EXPERIMENTAL ATIVADO + MODO EXPERIMENTAL ON CHILL MODE ON - MODO CHILL ATIVADO + MODO CHILL ON @@ -553,14 +730,6 @@ trabalho definido comma prime comma prime - - CONNECT.COMMA.AI - CONNECT.COMMA.AI - - - COMMA POINTS - PONTOS COMMA - QObject @@ -909,6 +1078,26 @@ Isso pode levar até um minuto. Uninstall Desinstalar + + failed to check for update + + + + up to date, last checked %1 + + + + DOWNLOAD + + + + update available + + + + never + + SshControl @@ -1085,6 +1274,26 @@ Isso pode levar até um minuto. On this car, openpilot defaults to the car's built-in ACC instead of openpilot's longitudinal control. Enable this to switch to openpilot longitudinal control. Enabling Experimental mode is recommended when enabling openpilot longitudinal control alpha. Neste carro, o openpilot tem como padrão o ACC embutido do carro em vez do controle longitudinal do openpilot. Habilite isso para alternar para o controle longitudinal openpilot. Recomenda-se ativar o modo Experimental ao ativar o alfa de controle longitudinal openpilot. + + Aggressive + + + + Standard + + + + Relaxed + + + + Driving Personality + + + + Standard is recommended. In aggressive mode, openpilot will follow lead cars closer and be more aggressive with the gas and brake. + + Enable Right-Hand Drive @@ -1093,33 +1302,6 @@ Isso pode levar até um minuto. Allow openpilot to obey left-hand traffic conventions and perform driver monitoring on right driver seat. - - Enable ALKA - - - - When enabled, openpilot lateral Control will be always on when ACC MAIN is ON. -Reboot required. - - - - Enable MapD - - - - When enabled, openpilot will display current road name and speed limit on the screen. -Reboot required. - - - - Enable Lane Priority Mode - - - - When enabled, openpilot will use lane lines for lateral control, fallback to laneless mode automatically when lane lines probabilities are low. -Reboot required. - - Updater @@ -1156,6 +1338,30 @@ Reboot required. Falha na atualização + + WiFiPromptWidget + + Setup Wi-Fi + Configurar +Wi-Fi + + + Connect to Wi-Fi to upload driving data and help improve openpilot + Conecte se ao Wi-Fi para upload de dados de condução e ajudar a melhorar o openpilot + + + Open Settings + Abrir Configurações + + + Uploading training data + Subindo dados para treinamento + + + Your data is used to train driving models and help improve openpilot + Seus dados são utilizados para treinar modelos de direção e ajudar a melhorar o openpilot + + WifiUI diff --git a/selfdrive/ui/translations/main_zh-CHS.qm b/selfdrive/ui/translations/main_zh-CHS.qm index 1b3d04096..a07968254 100644 Binary files a/selfdrive/ui/translations/main_zh-CHS.qm and b/selfdrive/ui/translations/main_zh-CHS.qm differ diff --git a/selfdrive/ui/translations/main_zh-CHS.ts b/selfdrive/ui/translations/main_zh-CHS.ts index 06bf7a3bc..232068834 100644 --- a/selfdrive/ui/translations/main_zh-CHS.ts +++ b/selfdrive/ui/translations/main_zh-CHS.ts @@ -1,6 +1,6 @@ - + AbstractAlert @@ -20,15 +20,15 @@ AdvancedNetworking Back - 返回 + 回上页 Enable Tethering - 启用WiFi热点 + 启用网路分享 Tethering Password - WiFi热点密码 + 网路分享密码 EDIT @@ -36,35 +36,58 @@ Enter new tethering password - 输入新的WiFi热点密码 + 输入新的网路分享密码 IP Address - IP地址 + IP 地址 Enable Roaming - 启用数据漫游 + 启用漫游 APN Setting - APN设置 + APN 设置 Enter APN - 输入APN + 输入 APN leave blank for automatic configuration - 留空以自动配置 + 留空白将自动配置 Cellular Metered - 按流量计费的手机移动网络 + 行动网路 Prevent large data uploads when on a metered connection - 当使用按流量计费的连接时,避免上传大流量数据 + 防止使用行动网路上传大量的数据 + + + + Alert + + openpilot Unavailable + 无法使用 dragonpilot + + + Waiting for controls to start + 等待控制服务开始 + + + TAKE CONTROL IMMEDIATELY + 立即接管控制 + + + Controls Unresponsive + 控制服务无回应 + + + Reboot Device + 重新启动设备 @@ -79,86 +102,313 @@ MAX - 最高定速 + 最高 SPEED - SPEED + 速度 LIMIT - LIMIT + 速限 C2NetworkPanel Wi-Fi Settings - + Wi-Fi 设定 OPEN - + 开启 Tethering Settings - + 热点设定 IP Address - IP地址 + IP 地址 CarSelectionPanel [AUTO SELECT] - + [自动选择] ConfirmationDialog Ok - 好的 + 确定 Cancel 取消 + + DPCtrlPanel + + When enabled, openpilot lateral Control will be always on when ACC MAIN is ON. +Reboot required. + 当启用时,当 ACC MAIN 为 ON,dragonpilot 的横向控制功能将一直保持开启。 +需要重新启动。 + + + Enable MapD + 启用 MapD 服务 + + + When enabled, openpilot will display current road name and speed limit on the screen. +Reboot required. + 当启用时,dragonpilot 将在萤幕上显示当前道路名称和速限。 +需要重新启动。 + + + Enable Lane Priority Mode + 启用车道线优先模式 + + + When enabled, openpilot will use lane lines for lateral control, fallback to laneless mode automatically when lane lines probabilities are low. +Reboot required. + 当启用时,dragonpilot 将使用车道线进行横向控制,在车道线概率较低时自动切换至无车道线模式。 +需要重新启动。 + + + Enable Auto Shutdown + 启用自动关机 + + + When enabled, openpilot will shutdown the device automatically. + Reboot required. + 启用后,dragonpilot 将会自动关闭设备。 + 需要重新启动。 + + + Auto Shutdown In + 自动关机倒数 + + + Adjust your shutdown waiting period. +0 = shutdown immediately. + 调整关机等待时间。 +0 = 立即关机。 + + + mins + 分钟 + + + Enable Stop and Go (SnG) Hack + 启用停止行走(SnG)修改 + + + When enabled, openpilot will stop sending standstill signal when the car is fully stopped. +ONLY WORK ON SOME VEHICLES. +Reboot Required. + 启用后,当车辆完全停止时,dragonpilot 将停止发送停止信号。 +仅适用于部分车辆。 +需要重新启动。 + + + Enable Door Auto Locking + 启用自动门锁定 + + + When enabled, openpilot will attempt to lock the doors when driving above 10 km/h (6.2 mph). +Reboot Required. + 启用后,当速度超过 10 km/h(6.2 mph)时,dragonpilot 将尝试锁定车门。 +需要重新启动。 + + + Enable Door Auto Unlocking + 启用自动解锁车门 + + + When enabled, openpilot will attempt to unlock the doors when shifting to gear P. +Reboot Required. + 启用后,当换档至 P 档时,dragonpilot 将尝试解锁车门。 +需要重新启动。 + + + When enabled, openpilot will use the good old 0.8.13.1 driving model. +For safety reason, vision-only openpilot longitudinal will be disabled. +Reboot required. + 启用后,dragonpilot 将使用旧版的0.8.13.1驾驶模型。 +出于安全考虑,仅基于视觉的 dragonpilot 纵向控制将被禁用。 +需要重新启动。 + + + Use 0.8.13.1 Driving Model + 使用 0.8.13.1 驾驶模型 + + + Ctrl - Lateral + 控制 - 横向 + + + Enable ALKA + 启用全时置中 + + + Ctrl - Longitudinal + 控制 - 纵向 + + + Device + 设备 + + + Ctrl - Overall + 控制 - 整体 + + + Toyota / Lexus + 丰田/雷克萨斯 + + + When enabled, openpilot will use the good old 0.8.13.1 driving model. +For safety reason, vision only openpilot longitudinal will be disabled. +Reboot required. + 启用后,dragonpilot 将使用优良的0.8.13.1驾驶模型。 +出于安全考虑,仅基于视觉的 dragonpilot 纵向控制将被禁用。 +需要重新启动。 + + + When enabled, openpilot will shutdown the device automatically. +Reboot required. + 启用后,dragonpilot 将自动关机。 +需要重新启动。 + + + When enabled, openpilot will attempt to lock the doors when drive above 10 km/h (6.2 mph). +Reboot Required. + 启用后,当速度超过 10 km/h(6.2 mph)时,dragonpilot 将尝试锁定车门。 +需要重新启动。 + + + When enabled, openpilot will attempt to unlock the doors when shift to gear P. +Reboot Required. + 启用后,当换到 P 档时,dragonpilot 将尝试解锁车门。 +需要重新启动。 + + + Disable Temp Check + 停用温度检查 + + + When enabled, openpilot will disable device temperature check. +**NOTED** An overheated device may result in random shutdowns or lag. +Reboot required. + 启用时,dragonpilot 将停用设备温度检查。 +**请注意** 过热的设备可能导致随机关机或卡顿。 +需要重新启动。 + + + Disable IR + 停用红外线 + + + When enabled, openpilot will disable IR completely. +Reboot required. + 启用时,dragonpilot 将完全停用红外线。 +需要重新启动。 + + + Standard + 标准 + + + On-Road + On-Road + + + MAIN + MAIN + + + OP + OP + + + Off + 关闭 + + + Display Mode + 显示模式 + + + On-Road - When driving, the display will be off (excl. warning). +MAIN - When ACC MAIN is on, the display will be off (excl. warning). +OP - When OP is enabled, the display will be off (excl. warning). +Off - the display will be off completely (incl. warning). +Reboot required. + On-Road - 在行驶时,显示将关闭(不包括警示)。 +MAIN - 当 ACC 主模式开启时,显示将关闭(不包括警示)。 +OP - 当 OP 功能启用时,显示将关闭(不包括警示)。 +关闭 - 显示将完全关闭(包括警示)。 +需要重新启动。 + + + Warning + 警示 + + + Audible Alert Mode + 提示声模式 + + + Warning - Only emits sound when there is a warning. +Off - Does not emit any sound at all. + 警示 - 只有在有警示时才发出提示声。 +关闭 - 完全不发出任何提示声。 + + + Adjust your shutdown waiting period. + 调整您的关机等待时间。 + + + Immediately + 马上关机 + + DeclinePage You must accept the Terms and Conditions in order to use openpilot. - 您必须接受条款和条件以使用openpilot。 + 您必须先接受条款和条件才能使用 dragonpilot。 Back - 返回 + 回上页 Decline, uninstall %1 - 拒绝并卸载%1 + 拒绝并解除安装 %1 DevicePanel Dongle ID - 设备ID(Dongle ID) + Dongle ID N/A - N/A + 无法使用 Serial - 序列号 + 序号 Driver Camera - 驾驶员摄像头 + 驾驶员监控镜头 PREVIEW @@ -166,11 +416,11 @@ Preview the driver facing camera to ensure that driver monitoring has good visibility. (vehicle must be off) - 打开并预览驾驶员摄像头,以确保驾驶员监控具有良好视野。(仅熄火时可用) + 预览驾驶员监控镜头画面,以确保其具有良好视野。(仅在熄火时可用) Reset Calibration - 重置设备校准 + 重置校准 RESET @@ -178,39 +428,39 @@ Are you sure you want to reset calibration? - 您确定要重置设备校准吗? + 您确定要重置校准吗? Review Training Guide - 新手指南 + 观看使用教学 REVIEW - 查看 + 观看 Review the rules, features, and limitations of openpilot - 查看openpilot的使用规则,以及其功能和限制。 + 观看 dragonpilot 的使用规则、功能和限制 Are you sure you want to review the training guide? - 您确定要查看新手指南吗? + 您确定要观看使用教学吗? Regulatory - 监管信息 + 法规/监管 VIEW - 查看 + 观看 Change Language - 切换语言 + 更改语言 CHANGE - 切换 + 更改 Select a language @@ -218,7 +468,7 @@ Reboot - 重启 + 重新启动 Power Off @@ -226,27 +476,27 @@ openpilot requires the device to be mounted within 4° left or right and within 5° up or 8° down. openpilot is continuously calibrating, resetting is rarely required. - openpilot要求设备安装的偏航角在左4°和右4°之间,俯仰角在上5°和下8°之间。一般来说,openpilot会持续更新校准,很少需要重置。 + dragonpilot 需要将设备固定在左右偏差 4° 以内,朝上偏差 5° 以内或朝下偏差 8° 以内。镜头在后台会持续自动校准,很少有需要重置的情况。 Your device is pointed %1° %2 and %3° %4. - 您的设备校准为%1° %2、%3° %4。 + 你的设备目前朝%2 %1° 以及朝%4 %3° 。 down - 朝下 + up - 朝上 + left - 朝左 + right - 朝右 + Are you sure you want to reboot? @@ -254,38 +504,46 @@ Disengage to Reboot - 取消openpilot以重新启动 + 请先取消控车才能重新启动 Are you sure you want to power off? - 您确定要关机吗? + 您确定您要关机吗? Disengage to Power Off - 取消openpilot以关机 + 请先取消控车才能关机 Reset - 重置 + 重设 Review - 预览 + 回顾 + + + 除错控制台 + 除错控制台 + + + 显示 tmux 输出时发生错误。 + 显示 tmux 输出时发生错误。 Debug Console - + 除错控制台 Error displaying tmux output. - + 显示 tmux 输出时发生错误。 DriveStats Drives - 旅程数 + 旅程 Hours @@ -293,11 +551,11 @@ ALL TIME - 全部 + 总共 PAST WEEK - 过去一周 + 上周 KM @@ -312,18 +570,18 @@ DriverViewScene camera starting - 正在启动相机 + 开启相机中 ExperimentalModeButton EXPERIMENTAL MODE ON - 试验模式运行 + 实验模式 ON CHILL MODE ON - 轻松模式运行 + 轻松模式 ON @@ -335,7 +593,7 @@ Need at least %n character(s)! - 至少需要 %n 个字符! + 需要至少 %n 个字元! @@ -343,14 +601,14 @@ Installer Installing... - 正在安装…… + 安装中… MapETA eta - 埃塔 + 抵达 min @@ -396,7 +654,7 @@ CLEAR - 清空 + 清除 Recent Destinations @@ -404,27 +662,29 @@ Try the Navigation Beta - 试用导航测试版 + 试用导航功能 Get turn-by-turn directions displayed and more with a comma prime subscription. Sign up now: https://connect.comma.ai - 订阅comma prime以获取导航。 -立即注册:https://connect.comma.ai + 成为 comma 高级会员来使用导航功能 +立即註册:https://connect.comma.ai No home location set - 家:未设定 + 未设定 +住家位置 No work location set - 工作:未设定 + 未设定 +工作位置 no recent destinations - 无最近目的地 + 没有最近的导航记录 @@ -453,7 +713,7 @@ location set Networking Advanced - 高级 + 进阶 Enter password @@ -461,7 +721,7 @@ location set for "%1" - 网络名称:"%1" + 给 "%1" Wrong password @@ -476,30 +736,30 @@ location set ALERTS - 警报 + 提醒 ALERT - 警报 + 提醒 PairingPopup Pair your device to your comma account - 将您的设备与comma账号配对 + 将设备与您的 comma 帐号配对 Go to https://connect.comma.ai on your phone - 在手机上访问 https://connect.comma.ai + 用手机连至 https://connect.comma.ai Click "add new device" and scan the QR code on the right - 点击“添加新设备”,扫描右侧二维码 + 点选 "add new device" 后扫描右边的二维码 Bookmark connect.comma.ai to your home screen to use it like an app - 将 connect.comma.ai 收藏到您的主屏幕,以便像应用程序一样使用它 + 将 connect.comma.ai 加入您的主屏幕,以便像手机 App 一样使用它 @@ -517,15 +777,15 @@ location set PrimeAdWidget Upgrade Now - 现在升级 + 马上升级 Become a comma prime member at connect.comma.ai - 打开connect.comma.ai以注册comma prime会员 + 成为 connect.comma.ai 的高级会员 PRIME FEATURES: - comma prime特权: + 高级会员特点: Remote access @@ -533,7 +793,7 @@ location set 1 year of storage - 1年数据存储 + 一年的云端行车记录 Developer perks @@ -548,34 +808,26 @@ location set comma prime - comma prime - - - CONNECT.COMMA.AI - CONNECT.COMMA.AI - - - COMMA POINTS - COMMA POINTS点数 + comma 高级会员 QObject Reboot - 重启 + 重新启动 Exit - 退出 + 离开 dashcam - 行车记录仪 + 行车记录器 openpilot - openpilot + dragonpilot %n minute(s) ago @@ -600,15 +852,15 @@ location set Reset Reset failed. Reboot to try again. - 重置失败。 重新启动以重试。 + 重置失败。请重新启动后再试。 Are you sure you want to reset your device? - 您确定要重置您的设备吗? + 您确定要重置你的设备吗? System Reset - 恢复出厂设置 + 系统重置 Cancel @@ -616,7 +868,7 @@ location set Reboot - 重启 + 重新启动 Confirm @@ -624,16 +876,16 @@ location set Unable to mount data partition. Partition may be corrupted. Press confirm to erase and reset your device. - + 无法挂载资料分割区 分割区可能已经毁损 请确认是否要删除并重新设定 Press confirm to erase all content and settings. Press cancel to resume boot. - + 按下确认以删除所有内容及设定 按下取消来继续开机 Resetting device... This may take up to a minute. - + 设备重置中 此过程可能需要几分钟 @@ -648,7 +900,7 @@ This may take up to a minute. Network - 网络 + 网路 Toggles @@ -656,7 +908,7 @@ This may take up to a minute. Software - 软件 + 软体 Navigation @@ -664,22 +916,22 @@ This may take up to a minute. Vehicle Model: - + 车辆型号: [AUTO SELECT] - + [自动选择] Setup WARNING: Low Voltage - 警告:低电压 + 警告:电压过低 Power your device in a car with a harness or proceed at your own risk. - 请使用car harness线束为您的设备供电,或自行承担风险。 + 请使用车上 harness 提供的电源,若继续的话您需要自担风险。 Power off @@ -691,27 +943,27 @@ This may take up to a minute. Getting Started - 开始设置 + 入门 Before we get on the road, let’s finish installation and cover some details. - 开始旅程之前,让我们完成安装并介绍一些细节。 + 在我们上路之前,让我们完成安装并介绍一些细节。 Connect to Wi-Fi - 连接到WiFi + 连接到无线网络 Back - 返回 + 回上页 Continue without Wi-Fi - 不连接WiFi并继续 + 在没有 Wi-Fi 的情况下继续 Waiting for internet - 等待网络连接 + 连接至网路中 Enter URL @@ -719,11 +971,11 @@ This may take up to a minute. for Custom Software - 以下载自定义软件 + 定制的软体 Downloading... - 正在下载…… + 下载中… Download Failed @@ -731,23 +983,23 @@ This may take up to a minute. Ensure the entered URL is valid, and the device’s internet connection is good. - 请确保互联网连接良好且输入的URL有效。 + 请确定您输入的是有效的安装网址,并且确定设备的网路连线状态良好。 Reboot device - 重启设备 + 重新启动 Start over - 重来 + 重新开始 No custom software found at this URL. - + 无法在此URL找到定制的软体 Something went wrong. Reboot the device. - + 发生了一些错误 请重新启动您的设备 @@ -758,7 +1010,7 @@ This may take up to a minute. Pair your device with comma connect (connect.comma.ai) and claim your comma prime offer. - 将您的设备与comma connect (connect.comma.ai)配对并领取您的comma prime优惠。 + 将您的设备与 comma connect (connect.comma.ai) 配对并领取您的 comma 高级会员优惠。 Pair device @@ -769,31 +1021,31 @@ This may take up to a minute. Sidebar CONNECT - CONNECT + 云端服务 OFFLINE - 离线 + 已离线 ONLINE - 在线 + 已连线 ERROR - 连接出错 + 错误 TEMP - 设备温度 + 温度 HIGH - 过热 + 偏高 GOOD - 良好 + 正常 OK @@ -801,15 +1053,15 @@ This may take up to a minute. VEHICLE - 车辆连接 + 车辆通讯 NO - + 未连线 PANDA - PANDA + 车辆通讯 GPS @@ -817,7 +1069,7 @@ This may take up to a minute. SEARCH - 搜索中 + 车辆通讯 -- @@ -825,34 +1077,34 @@ This may take up to a minute. Wi-Fi - Wi-Fi + ETH - 以太网 + 2G - 2G + 3G - 3G + LTE - LTE + 5G - 5G + SoftwarePanel Updates are only downloaded while the car is off. - 车辆熄火时才能下载升级文件。 + 系统更新只会在熄火时下载。 Current Version @@ -876,62 +1128,82 @@ This may take up to a minute. SELECT - 选择 + 选取 Select a branch - 选择分支 + 选取一个分支 UNINSTALL - 卸载 + 解除安装 Uninstall %1 - 卸载 %1 + 解除安装 %1 Are you sure you want to uninstall? - 您确定要卸载吗? + 您确定您要解除安装吗? CHECK - 查看 + 检查 Uninstall - 卸载 + 解除安装 + + + failed to check for update + 检查更新失败 + + + up to date, last checked %1 + 已是最新版本,上次检查时间:%1 + + + DOWNLOAD + 下载 + + + update available + 有可用的更新 + + + never + 从未更新 SshControl SSH Keys - SSH密钥 + SSH 密钥 Warning: This grants SSH access to all public keys in your GitHub settings. Never enter a GitHub username other than your own. A comma employee will NEVER ask you to add their GitHub username. - 警告:这将授予SSH访问权限给您GitHub设置中的所有公钥。切勿输入您自己以外的GitHub用户名。comma员工永远不会要求您添加他们的GitHub用户名。 + 警告:这将授权给 GitHub 帐号中所有公钥 SSH 访问权限。切勿输入非您自己的 GitHub 用户名。comma 员工「永远不会」要求您添加他们的 GitHub 用户名。 ADD - 添加 + 新增 Enter your GitHub username - 输入您的GitHub用户名 + 请输入您 GitHub 的用户名 LOADING - 正在加载 + 载入中 REMOVE - 删除 + 移除 Username '%1' has no keys on GitHub - 用户名“%1”在GitHub上没有密钥 + GitHub 用户 '%1' 没有设定任何密钥 Request timed out @@ -939,14 +1211,14 @@ This may take up to a minute. Username '%1' doesn't exist on GitHub - GitHub上不存在用户名“%1” + GitHub 用户 '%1' 不存在 SshToggle Enable SSH - 启用SSH + 启用 SSH 服务 @@ -961,22 +1233,22 @@ This may take up to a minute. Scroll to accept - 滑动以接受 + 滑动至页尾接受条款 Agree - 同意 + 接受 TogglesPanel Enable openpilot - 启用openpilot + 启用 dragonpilot Use the openpilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. Changing this setting takes effect when the car is powered off. - 使用openpilot进行自适应巡航和车道保持辅助。使用此功能时您必须时刻保持注意力。该设置的更改在熄火时生效。 + 使用 dragonpilot 的主动式巡航和车道保持功能,开启后您需要持续集中注意力,设定变更在重新启动车辆后生效。 Enable Lane Departure Warnings @@ -984,7 +1256,7 @@ This may take up to a minute. Receive alerts to steer back into the lane when your vehicle drifts over a detected lane line without a turn signal activated while driving over 31 mph (50 km/h). - 车速超过31mph(50km/h)时,若检测到车辆越过车道线且未打转向灯,系统将发出警告以提醒您返回车道。 + 车速在时速 50 公里 (31 英里) 以上且未打方向灯的情况下,如果侦测到车辆驶出目前车道线时,发出车道偏离警告。 Use Metric System @@ -992,141 +1264,182 @@ This may take up to a minute. Display speed in km/h instead of mph. - 显示车速时,以km/h代替mph。 + 启用后,速度单位显示将从 mp/h 改为 km/h。 Record and Upload Driver Camera - 录制并上传驾驶员摄像头 + 记录并上传驾驶监控影像 Upload data from the driver facing camera and help improve the driver monitoring algorithm. - 上传驾驶员摄像头的数据,帮助改进驾驶员监控算法。 + 上传驾驶监控的录像来协助我们提升驾驶监控的准确率。 Disengage on Accelerator Pedal - 踩油门时取消控制 + 油门取消控车 When enabled, pressing the accelerator pedal will disengage openpilot. - 启用后,踩下油门踏板将取消openpilot。 + 启用后,踩踏油门将会取消 dragonpilot 控制。 Show ETA in 24h Format - 以24小时格式显示预计到达时间 + 预计到达时间单位改用 24 小时制 Use 24h format instead of am/pm - 使用24小时制代替am/pm + 使用 24 小时制。(预设值为 12 小时制) Show Map on Left Side of UI - 在介面左侧显示地图 + 将地图显示在画面的左侧 Show map on left side when in split screen view. - 在分屏模式中,将地图置于屏幕左侧。 + 进入分割画面后,地图将会显示在画面的左侧。 Experimental Mode - 测试模式 + 实验模式 openpilot defaults to driving in <b>chill mode</b>. Experimental mode enables <b>alpha-level features</b> that aren't ready for chill mode. Experimental features are listed below: - openpilot 默认 <b>轻松模式</b>驾驶车辆。试验模式启用一些轻松模式之外的 <b>试验性功能</b>。试验性功能包括: + dragonpilot 预设以 <b>轻松模式</b> 驾驶。 实验模式启用了尚未准备好进入轻松模式的 <b>alpha 级功能</b>。实验功能如下: 🌮 End-to-End Longitudinal Control 🌮 - 🌮 端到端(End-to-End) 纵向控制 🌮 + 🌮端到端纵向控制🌮 Let the driving model control the gas and brakes. openpilot will drive as it thinks a human would, including stopping for red lights and stop signs. Since the driving model decides the speed to drive, the set speed will only act as an upper bound. This is an alpha quality feature; mistakes should be expected. - 允许驾驶模型控制加速和制动,openpilot将模仿人类驾驶车辆,包括在红灯和停车让行标识前停车。鉴于驾驶模型确定行驶车速,所设定的车速仅作为上限。此功能尚处于早期测试状态,有可能会出现操作错误。 + 让驾驶模型来控制油门及煞车。dragonpilot 将会模拟人类的驾驶行为,包含在看见红灯及停止标示时停车。由于车速将由驾驶模型决定,因此您设定的时速将成为速度上限。本功能仍在早期实验阶段,请预期模型有犯错的可能性。 New Driving Visualization - 新驾驶视角 + 新的驾驶视觉介面 The driving visualization will transition to the road-facing wide-angle camera at low speeds to better show some turns. The Experimental mode logo will also be shown in the top right corner. - 当低速行驶时,驾驶视角将切换到前向广角摄像头,便于更完整地显示转向路径。右上角将显示试验模式图标。 + 低速行驶时,将会切换成路侧广角镜头,以完整显示转弯路径,右上角将出现实验模式图案。 Experimental mode is currently unavailable on this car since the car's stock ACC is used for longitudinal control. - 由于此车辆使用自带的ACC纵向控制,当前无法使用试验模式。 + 因车辆使用内建ACC系统,无法在本车辆上启动实验模式。 openpilot longitudinal control may come in a future update. - + 未来可能会推出 dragonpilot 纵向控制 An experimental version of openpilot longitudinal control can be tested, along with Experimental mode, on non-release branches. - + 在非发行分支中 可找到包含实验模式的 dragonpilot 纵向控制测试版本 Enable experimental longitudinal control to allow Experimental mode. - 启用试验性的纵向控制,以便允许使用试验模式。 + 启用实验性纵向控制以使用实验模式。 openpilot Longitudinal Control (Alpha) - + dragonpilot 纵向控制(Alpha 版) WARNING: openpilot longitudinal control is in alpha for this car and will disable Automatic Emergency Braking (AEB). - + 警告: dragonpilot 纵向控制对于此车辆处于测试阶段(Alpha 版),并将停用自动紧急制动(AEB)。 On this car, openpilot defaults to the car's built-in ACC instead of openpilot's longitudinal control. Enable this to switch to openpilot longitudinal control. Enabling Experimental mode is recommended when enabling openpilot longitudinal control alpha. - + 在这辆车上,dragonpilot 默认使用车辆内置的 ACC 而不是 dragonpilot 的纵向控制。启用此选项以切换至 dragonpilot 的纵向控制。建议在启用 dragonpilot 纵向控制 Alpha 版时启用实验模式。 + + + Aggressive + 积极 + + + Standard + 标准 + + + Relaxed + 舒适 + + + Driving Personality + 驾驶风格 + + + Standard is recommended. In aggressive mode, openpilot will follow lead cars closer and be more aggressive with the gas and brake. + 推荐使用标准模式。在积极模式下,dragonpilot 将更紧密地跟随前车,并更积极的控制油门和刹车。 Enable Right-Hand Drive - + 启用右驾模式 Allow openpilot to obey left-hand traffic conventions and perform driver monitoring on right driver seat. - + 允许 dragonpilot 遵守左侧交通规则并在右侧驾驶座上进行驾驶者监控。 Enable ALKA - + 启用全时置中 + + + When enabled, openpilot lateral Control will be always on when ACC MAIN is ON. + Reboot required. + 启用后,当 ACC 系统开关开启时,dragonpilot 的横向控制将始终保持开启状态。 + 需要重新启动。 + + + Enable MapD + 启用 MapD + + + When enabled, openpilot will display current road name and speed limit on the screen. + Reboot required. + 启用后,dragonpilot 将在屏幕上显示当前道路名称和速度限制。 + 需要重新启动。 + + + Enable Lane Priority Mode + 启用车道优先模式 + + + When enabled, openpilot will use lane lines for lateral control, fallback to laneless mode automatically when lane lines probabilities are low. + Reboot required. + 启用后,dragonpilot 将使用车道线进行横向控制,当车道线概率较低时,将自动切换至无车道模式。 + 需要重新启动。 When enabled, openpilot lateral Control will be always on when ACC MAIN is ON. Reboot required. - - - - Enable MapD - + 当启用时,当 ACC MAIN 为 ON,dragonpilot 的横向控制功能将一直保持开启。 +需要重新启动。 When enabled, openpilot will display current road name and speed limit on the screen. Reboot required. - - - - Enable Lane Priority Mode - + 当启用时,dragonpilot 将在萤幕上显示当前道路名称和速限。 +需要重新启动。 When enabled, openpilot will use lane lines for lateral control, fallback to laneless mode automatically when lane lines probabilities are low. Reboot required. - + 当启用时,dragonpilot 将使用车道线进行横向控制,在车道线概率较低时自动切换至无车道线模式。 +需要重新启动。 Updater Update Required - 需要更新 + 系统更新 An operating system update is required. Connect your device to Wi-Fi for the fastest update experience. The download size is approximately 1GB. - 操作系统需要更新。请将您的设备连接到WiFi以获取更快的更新体验。下载大小约为1GB。 + 设备的操作系统需要更新。请将您的设备连接到 Wi-Fi 以获得最快的更新体验。下载大小约为 1GB。 Connect to Wi-Fi - 连接到WiFi + 连接到无线网络 Install @@ -1134,42 +1447,65 @@ Reboot required. Back - 返回 + 回上页 Loading... - 正在加载…… + 载入中… Reboot - 重启 + 重新启动 Update failed 更新失败 + + WiFiPromptWidget + + Setup Wi-Fi + 设置 Wi-Fi + + + Connect to Wi-Fi to upload driving data and help improve openpilot + 连接到 Wi-Fi 上传驾驶数据,帮助改进 dragonpilot + + + Open Settings + 打开设置 + + + Uploading training data + 正在上传训练数据 + + + Your data is used to train driving models and help improve openpilot + 您的数据用于训练驾驶模型并帮助改进 dragonpilot + + WifiUI Scanning for networks... - 正在扫描网络…… + 扫描无线网路中... CONNECTING... - 正在连接…… + 连线中... FORGET - 忽略 + 清除 Forget Wi-Fi Network "%1"? - 忽略WiFi网络 "%1"? + 清除 Wi-Fi 网路 "%1"? Forget - 忽略 + 清除 diff --git a/selfdrive/ui/translations/main_zh-CHT.qm b/selfdrive/ui/translations/main_zh-CHT.qm index db957eec2..f908b7b83 100644 Binary files a/selfdrive/ui/translations/main_zh-CHT.qm and b/selfdrive/ui/translations/main_zh-CHT.qm differ diff --git a/selfdrive/ui/translations/main_zh-CHT.ts b/selfdrive/ui/translations/main_zh-CHT.ts index 25bc91320..cd163de06 100644 --- a/selfdrive/ui/translations/main_zh-CHT.ts +++ b/selfdrive/ui/translations/main_zh-CHT.ts @@ -67,6 +67,29 @@ 防止使用行動網路上傳大量的數據 + + Alert + + openpilot Unavailable + 無法使用 dragonpilot + + + Waiting for controls to start + 等待控制服務開始 + + + TAKE CONTROL IMMEDIATELY + 立即接管控制 + + + Controls Unresponsive + 控制服務無回應 + + + Reboot Device + 重新啟動設備 + + AnnotatedCameraWidget @@ -94,7 +117,7 @@ C2NetworkPanel Wi-Fi Settings - 無線網路設置 + Wi-Fi 設定 OPEN @@ -102,7 +125,7 @@ Tethering Settings - 熱點設置 + 熱點設定 IP Address @@ -127,11 +150,238 @@ 取消 + + DPCtrlPanel + + When enabled, openpilot lateral Control will be always on when ACC MAIN is ON. +Reboot required. + 當啟用時,當 ACC MAIN 為 ON,dragonpilot 的橫向控制功能將一直保持開啟。 +需要重新啟動。 + + + Enable MapD + 啟用 MapD 服務 + + + When enabled, openpilot will display current road name and speed limit on the screen. +Reboot required. + 當啟用時,dragonpilot 將在螢幕上顯示當前道路名稱和速限。 +需要重新啟動。 + + + Enable Lane Priority Mode + 啟用車道線優先模式 + + + When enabled, openpilot will use lane lines for lateral control, fallback to laneless mode automatically when lane lines probabilities are low. +Reboot required. + 當啟用時,dragonpilot 將使用車道線進行橫向控制,在車道線概率較低時自動切換至無車道線模式。 +需要重新啟動。 + + + Enable Auto Shutdown + 啟用自動關機 + + + When enabled, openpilot will shutdown the device automatically. + Reboot required. + 啟用後,dragonpilot 將會自動關閉設備。 + 需要重新啟動。 + + + Auto Shutdown In + 自動關機倒數 + + + Adjust your shutdown waiting period. +0 = shutdown immediately. + 調整關機等待時間。 +0 = 立即關機。 + + + mins + 分鐘 + + + Enable Stop and Go (SnG) Hack + 啟用停止行走(SnG)修改 + + + When enabled, openpilot will stop sending standstill signal when the car is fully stopped. +ONLY WORK ON SOME VEHICLES. +Reboot Required. + 啟用後,當車輛完全停止時,dragonpilot 將停止發送停止信號。 +僅適用於部分車輛。 +需要重新啟動。 + + + Enable Door Auto Locking + 啟用自動門鎖定 + + + When enabled, openpilot will attempt to lock the doors when driving above 10 km/h (6.2 mph). +Reboot Required. + 啟用後,當速度超過 10 km/h(6.2 mph)時,dragonpilot 將嘗試鎖定車門。 +需要重新啟動。 + + + Enable Door Auto Unlocking + 啟用自動解鎖車門 + + + When enabled, openpilot will attempt to unlock the doors when shifting to gear P. +Reboot Required. + 啟用後,當換檔至 P 檔時,dragonpilot 將嘗試解鎖車門。 +需要重新啟動。 + + + When enabled, openpilot will use the good old 0.8.13.1 driving model. +For safety reason, vision-only openpilot longitudinal will be disabled. +Reboot required. + 啟用後,dragonpilot 將使用舊版的0.8.13.1駕駛模型。 +出於安全考慮,僅基於視覺的 dragonpilot 縱向控制將被禁用。 +需要重新啟動。 + + + Use 0.8.13.1 Driving Model + 使用 0.8.13.1 駕駛模型 + + + Ctrl - Lateral + 控制 - 橫向 + + + Enable ALKA + 啟用全時置中 + + + Ctrl - Longitudinal + 控制 - 縱向 + + + Device + 設備 + + + Ctrl - Overall + 控制 - 整體 + + + Toyota / Lexus + 豐田/雷克薩斯 + + + When enabled, openpilot will use the good old 0.8.13.1 driving model. +For safety reason, vision only openpilot longitudinal will be disabled. +Reboot required. + 啟用後,dragonpilot 將使用優良的0.8.13.1駕駛模型。 +出於安全考慮,僅基於視覺的 dragonpilot 縱向控制將被禁用。 +需要重新啟動。 + + + When enabled, openpilot will shutdown the device automatically. +Reboot required. + 啟用後,dragonpilot 將自動關機。 +需要重新啟動。 + + + When enabled, openpilot will attempt to lock the doors when drive above 10 km/h (6.2 mph). +Reboot Required. + 啟用後,當速度超過 10 km/h(6.2 mph)時,dragonpilot 將嘗試鎖定車門。 +需要重新啟動。 + + + When enabled, openpilot will attempt to unlock the doors when shift to gear P. +Reboot Required. + 啟用後,當換到 P 檔時,dragonpilot 將嘗試解鎖車門。 +需要重新啟動。 + + + Disable Temp Check + 停用溫度檢查 + + + When enabled, openpilot will disable device temperature check. +**NOTED** An overheated device may result in random shutdowns or lag. +Reboot required. + 啟用時,dragonpilot 將停用設備溫度檢查。 +**請注意** 過熱的設備可能導致隨機關機或卡頓。 +需要重新啟動。 + + + Disable IR + 停用紅外線 + + + When enabled, openpilot will disable IR completely. +Reboot required. + 啟用時,dragonpilot 將完全停用紅外線。 +需要重新啟動。 + + + Standard + 標準 + + + On-Road + On-Road + + + MAIN + MAIN + + + OP + OP + + + Off + 關閉 + + + Display Mode + 顯示模式 + + + On-Road - When driving, the display will be off (excl. warning). +MAIN - When ACC MAIN is on, the display will be off (excl. warning). +OP - When OP is enabled, the display will be off (excl. warning). +Off - the display will be off completely (incl. warning). +Reboot required. + On-Road - 在行駛時,顯示將關閉(不包括警示)。 +MAIN - 當 ACC 主模式開啟時,顯示將關閉(不包括警示)。 +OP - 當 OP 功能啟用時,顯示將關閉(不包括警示)。 +關閉 - 顯示將完全關閉(包括警示)。 +需要重新啟動。 + + + Warning + 警示 + + + Audible Alert Mode + 提示聲模式 + + + Warning - Only emits sound when there is a warning. +Off - Does not emit any sound at all. + 警示 - 只有在有警示時才發出提示聲。 +關閉 - 完全不發出任何提示聲。 + + + Adjust your shutdown waiting period. + 調整您的關機等待時間。 + + + Immediately + 馬上關機 + + DeclinePage You must accept the Terms and Conditions in order to use openpilot. - 您必須先接受條款和條件才能使用 openpilot。 + 您必須先接受條款和條件才能使用 dragonpilot。 Back @@ -190,7 +440,7 @@ Review the rules, features, and limitations of openpilot - 觀看 openpilot 的使用規則、功能和限制 + 觀看 dragonpilot 的使用規則、功能和限制 Are you sure you want to review the training guide? @@ -226,7 +476,7 @@ openpilot requires the device to be mounted within 4° left or right and within 5° up or 8° down. openpilot is continuously calibrating, resetting is rarely required. - openpilot 需要將設備固定在左右偏差 4° 以內,朝上偏差 5° 以内或朝下偏差 8° 以内。鏡頭在後台會持續自動校準,很少有需要重置的情况。 + dragonpilot 需要將設備固定在左右偏差 4° 以內,朝上偏差 5° 以内或朝下偏差 8° 以内。鏡頭在後台會持續自動校準,很少有需要重置的情况。 Your device is pointed %1° %2 and %3° %4. @@ -272,9 +522,17 @@ Review 回顧 + + 除錯控制台 + 除錯控制台 + + + 顯示 tmux 輸出時發生錯誤。 + 顯示 tmux 輸出時發生錯誤。 + Debug Console - 除錯視窗 + 除錯控制台 Error displaying tmux output. @@ -552,14 +810,6 @@ location set comma prime comma 高級會員 - - CONNECT.COMMA.AI - CONNECT.COMMA.AI - - - COMMA POINTS - COMMA 積分 - QObject @@ -577,7 +827,7 @@ location set openpilot - openpilot + dragonpilot %n minute(s) ago @@ -666,7 +916,7 @@ This may take up to a minute. Vehicle Model: - 車型: + 車輛型號: [AUTO SELECT] @@ -904,6 +1154,26 @@ This may take up to a minute. Uninstall 解除安裝 + + failed to check for update + 檢查更新失敗 + + + up to date, last checked %1 + 已是最新版本,上次檢查時間:%1 + + + DOWNLOAD + 下載 + + + update available + 有可用的更新 + + + never + 從未更新 + SshControl @@ -974,11 +1244,11 @@ This may take up to a minute. TogglesPanel Enable openpilot - 啟用 openpilot + 啟用 dragonpilot Use the openpilot system for adaptive cruise control and lane keep driver assistance. Your attention is required at all times to use this feature. Changing this setting takes effect when the car is powered off. - 使用 openpilot 的主動式巡航和車道保持功能,開啟後您需要持續集中注意力,設定變更在重新啟動車輛後生效。 + 使用 dragonpilot 的主動式巡航和車道保持功能,開啟後您需要持續集中注意力,設定變更在重新啟動車輛後生效。 Enable Lane Departure Warnings @@ -1010,7 +1280,7 @@ This may take up to a minute. When enabled, pressing the accelerator pedal will disengage openpilot. - 啟用後,踩踏油門將會取消 openpilot 控制。 + 啟用後,踩踏油門將會取消 dragonpilot 控制。 Show ETA in 24h Format @@ -1034,7 +1304,7 @@ This may take up to a minute. openpilot defaults to driving in <b>chill mode</b>. Experimental mode enables <b>alpha-level features</b> that aren't ready for chill mode. Experimental features are listed below: - openpilot 預設以 <b>輕鬆模式</b> 駕駛。 實驗模式啟用了尚未準備好進入輕鬆模式的 <b>alpha 級功能</b>。實驗功能如下: + dragonpilot 預設以 <b>輕鬆模式</b> 駕駛。 實驗模式啟用了尚未準備好進入輕鬆模式的 <b>alpha 級功能</b>。實驗功能如下: 🌮 End-to-End Longitudinal Control 🌮 @@ -1042,7 +1312,7 @@ This may take up to a minute. Let the driving model control the gas and brakes. openpilot will drive as it thinks a human would, including stopping for red lights and stop signs. Since the driving model decides the speed to drive, the set speed will only act as an upper bound. This is an alpha quality feature; mistakes should be expected. - 讓駕駛模型來控制油門及煞車。openpilot將會模擬人類的駕駛行為,包含在看見紅燈及停止標示時停車。由於車速將由駕駛模型決定,因此您設定的時速將成為速度上限。本功能仍在早期實驗階段,請預期模型有犯錯的可能性。 + 讓駕駛模型來控制油門及煞車。dragonpilot 將會模擬人類的駕駛行為,包含在看見紅燈及停止標示時停車。由於車速將由駕駛模型決定,因此您設定的時速將成為速度上限。本功能仍在早期實驗階段,請預期模型有犯錯的可能性。 New Driving Visualization @@ -1058,11 +1328,11 @@ This may take up to a minute. openpilot longitudinal control may come in a future update. - 未來可能會推出openpilot縱向控制 + 未來可能會推出 dragonpilot 縱向控制 An experimental version of openpilot longitudinal control can be tested, along with Experimental mode, on non-release branches. - 在非發行分支中 可找到包含實驗模式的openpilot縱向控制測試版本 + 在非發行分支中 可找到包含實驗模式的 dragonpilot 縱向控制測試版本 Enable experimental longitudinal control to allow Experimental mode. @@ -1070,15 +1340,35 @@ This may take up to a minute. openpilot Longitudinal Control (Alpha) - openpilot 縱向控制(Alpha 版) + dragonpilot 縱向控制(Alpha 版) WARNING: openpilot longitudinal control is in alpha for this car and will disable Automatic Emergency Braking (AEB). - 警告:此車輛的 openpilot 縱向控制功能尚處於測試階段(Alpha 版),使用該功能將會停用自動緊急制動系統(AEB)。 + 警告: dragonpilot 縱向控制對於此車輛處於測試階段(Alpha 版),並將停用自動緊急制動(AEB)。 On this car, openpilot defaults to the car's built-in ACC instead of openpilot's longitudinal control. Enable this to switch to openpilot longitudinal control. Enabling Experimental mode is recommended when enabling openpilot longitudinal control alpha. - 在這輛車上,openpilot 默認使用車輛內建的自適應巡航系統(ACC),而不是 openpilot 的縱向控制功能。啟用此功能可切換至 openpilot 的縱向控制模式。建議在啟用 openpilo t縱向控制 alpha 版時啟用實驗模式。 + 在這輛車上,dragonpilot 默認使用車輛內置的 ACC 而不是 dragonpilot 的縱向控制。啟用此選項以切換至 dragonpilot 的縱向控制。建議在啟用 dragonpilot 縱向控制 Alpha 版時啟用實驗模式。 + + + Aggressive + 積極 + + + Standard + 標準 + + + Relaxed + 舒適 + + + Driving Personality + 駕駛風格 + + + Standard is recommended. In aggressive mode, openpilot will follow lead cars closer and be more aggressive with the gas and brake. + 推薦使用標準模式。在積極模式下,dragonpilot 將更緊密地跟隨前車,並更積極的控制油門和剎車。 Enable Right-Hand Drive @@ -1086,36 +1376,54 @@ This may take up to a minute. Allow openpilot to obey left-hand traffic conventions and perform driver monitoring on right driver seat. - 允許 openpilot 遵守左側交通規則並在右側駕駛座進行駕駛者監控。 + 允許 dragonpilot 遵守左側交通規則並在右側駕駛座上進行駕駛者監控。 Enable ALKA - 啟用全時置中 + 啟用全時置中 + + + When enabled, openpilot lateral Control will be always on when ACC MAIN is ON. + Reboot required. + 啟用後,當 ACC 系統開關開啟時,dragonpilot 的橫向控制將始終保持開啟狀態。 + 需要重新啟動。 + + + Enable MapD + 啟用 MapD + + + When enabled, openpilot will display current road name and speed limit on the screen. + Reboot required. + 啟用後,dragonpilot 將在屏幕上顯示當前道路名稱和速度限制。 + 需要重新啟動。 + + + Enable Lane Priority Mode + 啟用車道優先模式 + + + When enabled, openpilot will use lane lines for lateral control, fallback to laneless mode automatically when lane lines probabilities are low. + Reboot required. + 啟用後,dragonpilot 將使用車道線進行橫向控制,當車道線概率較低時,將自動切換至無車道模式。 + 需要重新啟動。 When enabled, openpilot lateral Control will be always on when ACC MAIN is ON. Reboot required. - 啟用後,openpilot 的橫向控制將始終在 ACC MAIN 開啟時開啟。 + 當啟用時,當 ACC MAIN 為 ON,dragonpilot 的橫向控制功能將一直保持開啟。 需要重新啟動。 - - Enable MapD - 啟用地圖服務 - When enabled, openpilot will display current road name and speed limit on the screen. Reboot required. - 啟用後,openpilot 將在屏幕上顯示當前道路名稱和速限。 + 當啟用時,dragonpilot 將在螢幕上顯示當前道路名稱和速限。 需要重新啟動。 - - Enable Lane Priority Mode - 啟用車道優先模式 - When enabled, openpilot will use lane lines for lateral control, fallback to laneless mode automatically when lane lines probabilities are low. Reboot required. - 啟用後,openpilot 將使用車道線進行橫向控制,在車道線的概率低時自動切換至無車道線模式。 + 當啟用時,dragonpilot 將使用車道線進行橫向控制,在車道線概率較低時自動切換至無車道線模式。 需要重新啟動。 @@ -1154,6 +1462,29 @@ Reboot required. 更新失敗 + + WiFiPromptWidget + + Setup Wi-Fi + 設置 Wi-Fi + + + Connect to Wi-Fi to upload driving data and help improve openpilot + 連接到 Wi-Fi 上傳駕駛數據,幫助改進 dragonpilot + + + Open Settings + 打開設置 + + + Uploading training data + 正在上傳訓練數據 + + + Your data is used to train driving models and help improve openpilot + 您的數據用於訓練駕駛模型並幫助改進 dragonpilot + + WifiUI diff --git a/system/clocksd/clocksd b/system/clocksd/clocksd index 610fefc32..efa479b18 100755 Binary files a/system/clocksd/clocksd and b/system/clocksd/clocksd differ diff --git a/system/hardware/hw.h b/system/hardware/hw.h index 31f1631a9..9a0db3d12 100644 --- a/system/hardware/hw.h +++ b/system/hardware/hw.h @@ -22,7 +22,7 @@ inline std::string log_root() { return Hardware::PC() ? util::getenv("HOME") + "/.comma/media/0/realdata" : "/data/media/0/realdata"; } inline std::string params() { - return Hardware::PC() ? util::getenv("HOME") + "/.comma/params" : "/data/params"; + return Hardware::PC() ? util::getenv("PARAMS_ROOT", util::getenv("HOME") + "/.comma/params") : "/data/params"; } inline std::string rsa_file() { return Hardware::PC() ? util::getenv("HOME") + "/.comma/persist/comma/id_rsa" : "/persist/comma/id_rsa"; diff --git a/system/hardware/tici/hardware.h b/system/hardware/tici/hardware.h index fba67d96c..580dc83ee 100644 --- a/system/hardware/tici/hardware.h +++ b/system/hardware/tici/hardware.h @@ -77,6 +77,7 @@ public: static std::map get_init_logs() { std::map ret = { {"/BUILD", util::read_file("/BUILD")}, + {"lsblk", util::check_output("lsblk -o NAME,SIZE,STATE,VENDOR,MODEL,REV,SERIAL")}, }; std::string bs = util::check_output("abctl --boot_slot"); diff --git a/system/logcatd/logcatd b/system/logcatd/logcatd index 1cca5876d..af9d6070a 100755 Binary files a/system/logcatd/logcatd and b/system/logcatd/logcatd differ diff --git a/system/proclogd/proclogd b/system/proclogd/proclogd index 8cef066f6..9ba9643fd 100755 Binary files a/system/proclogd/proclogd and b/system/proclogd/proclogd differ diff --git a/system/sensord/_sensord b/system/sensord/_sensord index 51feb3643..c72b7a6b4 100755 Binary files a/system/sensord/_sensord and b/system/sensord/_sensord differ diff --git a/system/ubloxd/ubloxd b/system/ubloxd/ubloxd index 345f0ea2e..3a21f4e16 100755 Binary files a/system/ubloxd/ubloxd and b/system/ubloxd/ubloxd differ diff --git a/third_party/acados/acados_template/__pycache__/__init__.cpython-38.pyc b/third_party/acados/acados_template/__pycache__/__init__.cpython-38.pyc deleted file mode 100644 index b07ec67b9..000000000 Binary files a/third_party/acados/acados_template/__pycache__/__init__.cpython-38.pyc and /dev/null differ diff --git a/third_party/acados/acados_template/__pycache__/acados_model.cpython-38.pyc b/third_party/acados/acados_template/__pycache__/acados_model.cpython-38.pyc deleted file mode 100644 index cfe31fdac..000000000 Binary files a/third_party/acados/acados_template/__pycache__/acados_model.cpython-38.pyc and /dev/null differ diff --git a/third_party/acados/acados_template/__pycache__/acados_ocp.cpython-38.pyc b/third_party/acados/acados_template/__pycache__/acados_ocp.cpython-38.pyc deleted file mode 100644 index 0a9df576d..000000000 Binary files a/third_party/acados/acados_template/__pycache__/acados_ocp.cpython-38.pyc and /dev/null differ diff --git a/third_party/acados/acados_template/__pycache__/acados_ocp_solver.cpython-38.pyc b/third_party/acados/acados_template/__pycache__/acados_ocp_solver.cpython-38.pyc deleted file mode 100644 index 4b3413130..000000000 Binary files a/third_party/acados/acados_template/__pycache__/acados_ocp_solver.cpython-38.pyc and /dev/null differ diff --git a/third_party/acados/acados_template/__pycache__/acados_sim.cpython-38.pyc b/third_party/acados/acados_template/__pycache__/acados_sim.cpython-38.pyc deleted file mode 100644 index 5c00ec50f..000000000 Binary files a/third_party/acados/acados_template/__pycache__/acados_sim.cpython-38.pyc and /dev/null differ diff --git a/third_party/acados/acados_template/__pycache__/acados_sim_solver.cpython-38.pyc b/third_party/acados/acados_template/__pycache__/acados_sim_solver.cpython-38.pyc deleted file mode 100644 index e844653f3..000000000 Binary files a/third_party/acados/acados_template/__pycache__/acados_sim_solver.cpython-38.pyc and /dev/null differ diff --git a/third_party/acados/acados_template/__pycache__/builders.cpython-38.pyc b/third_party/acados/acados_template/__pycache__/builders.cpython-38.pyc deleted file mode 100644 index 4b1554387..000000000 Binary files a/third_party/acados/acados_template/__pycache__/builders.cpython-38.pyc and /dev/null differ diff --git a/third_party/acados/acados_template/__pycache__/generate_c_code_constraint.cpython-38.pyc b/third_party/acados/acados_template/__pycache__/generate_c_code_constraint.cpython-38.pyc deleted file mode 100644 index f918b89c3..000000000 Binary files a/third_party/acados/acados_template/__pycache__/generate_c_code_constraint.cpython-38.pyc and /dev/null differ diff --git a/third_party/acados/acados_template/__pycache__/generate_c_code_discrete_dynamics.cpython-38.pyc b/third_party/acados/acados_template/__pycache__/generate_c_code_discrete_dynamics.cpython-38.pyc deleted file mode 100644 index d5ae40baf..000000000 Binary files a/third_party/acados/acados_template/__pycache__/generate_c_code_discrete_dynamics.cpython-38.pyc and /dev/null differ diff --git a/third_party/acados/acados_template/__pycache__/generate_c_code_explicit_ode.cpython-38.pyc b/third_party/acados/acados_template/__pycache__/generate_c_code_explicit_ode.cpython-38.pyc deleted file mode 100644 index e61815923..000000000 Binary files a/third_party/acados/acados_template/__pycache__/generate_c_code_explicit_ode.cpython-38.pyc and /dev/null differ diff --git a/third_party/acados/acados_template/__pycache__/generate_c_code_external_cost.cpython-38.pyc b/third_party/acados/acados_template/__pycache__/generate_c_code_external_cost.cpython-38.pyc deleted file mode 100644 index 0a7be844b..000000000 Binary files a/third_party/acados/acados_template/__pycache__/generate_c_code_external_cost.cpython-38.pyc and /dev/null differ diff --git a/third_party/acados/acados_template/__pycache__/generate_c_code_gnsf.cpython-38.pyc b/third_party/acados/acados_template/__pycache__/generate_c_code_gnsf.cpython-38.pyc deleted file mode 100644 index 15913c701..000000000 Binary files a/third_party/acados/acados_template/__pycache__/generate_c_code_gnsf.cpython-38.pyc and /dev/null differ diff --git a/third_party/acados/acados_template/__pycache__/generate_c_code_implicit_ode.cpython-38.pyc b/third_party/acados/acados_template/__pycache__/generate_c_code_implicit_ode.cpython-38.pyc deleted file mode 100644 index 39be65e9f..000000000 Binary files a/third_party/acados/acados_template/__pycache__/generate_c_code_implicit_ode.cpython-38.pyc and /dev/null differ diff --git a/third_party/acados/acados_template/__pycache__/generate_c_code_nls_cost.cpython-38.pyc b/third_party/acados/acados_template/__pycache__/generate_c_code_nls_cost.cpython-38.pyc deleted file mode 100644 index 63dbbbdab..000000000 Binary files a/third_party/acados/acados_template/__pycache__/generate_c_code_nls_cost.cpython-38.pyc and /dev/null differ diff --git a/third_party/acados/acados_template/__pycache__/utils.cpython-38.pyc b/third_party/acados/acados_template/__pycache__/utils.cpython-38.pyc deleted file mode 100644 index 18c8ae4b8..000000000 Binary files a/third_party/acados/acados_template/__pycache__/utils.cpython-38.pyc and /dev/null differ diff --git a/third_party/cluster/__pycache__/__init__.cpython-38.pyc b/third_party/cluster/__pycache__/__init__.cpython-38.pyc deleted file mode 100644 index 99cd778d7..000000000 Binary files a/third_party/cluster/__pycache__/__init__.cpython-38.pyc and /dev/null differ diff --git a/third_party/cluster/__pycache__/fastcluster_py.cpython-38.pyc b/third_party/cluster/__pycache__/fastcluster_py.cpython-38.pyc deleted file mode 100644 index 3c084a3bc..000000000 Binary files a/third_party/cluster/__pycache__/fastcluster_py.cpython-38.pyc and /dev/null differ diff --git a/third_party/cluster/fastcluster.os b/third_party/cluster/fastcluster.os deleted file mode 100644 index cb0dd99a3..000000000 Binary files a/third_party/cluster/fastcluster.os and /dev/null differ diff --git a/tools/replay/replay.h b/tools/replay/replay.h index 2bb426361..b7704132c 100644 --- a/tools/replay/replay.h +++ b/tools/replay/replay.h @@ -22,6 +22,7 @@ enum REPLAY_FLAGS { REPLAY_FLAG_NO_HW_DECODER = 0x0100, REPLAY_FLAG_FULL_SPEED = 0x0200, REPLAY_FLAG_NO_VIPC = 0x0400, + REPLAY_FLAG_ALL_SERVICES = 0x0800, }; enum class FindFlag { @@ -40,7 +41,7 @@ class Replay : public QObject { Q_OBJECT public: - Replay(QString route, QStringList allow, QStringList block, SubMaster *sm = nullptr, + Replay(QString route, QStringList allow, QStringList block, QStringList base_blacklist, SubMaster *sm = nullptr, uint32_t flags = REPLAY_FLAG_NONE, QString data_dir = "", QObject *parent = 0); ~Replay(); bool load(); @@ -67,7 +68,7 @@ public: inline QDateTime currentDateTime() const { return route_->datetime().addSecs(currentSeconds()); } inline uint64_t routeStartTime() const { return route_start_ts_; } inline int toSeconds(uint64_t mono_time) const { return (mono_time - route_start_ts_) / 1e9; } - inline int totalSeconds() const { return segments_.size() * 60; } + inline int totalSeconds() const { return (!segments_.empty()) ? (segments_.rbegin()->first + 1) * 60 : 0; } inline void setSpeed(float speed) { speed_ = speed; } inline float getSpeed() const { return speed_; } inline const std::vector *events() const { return events_.get(); }