openpilot v0.5.7 release

pull/819/head^2
Vehicle Researcher 6 years ago
parent 573a6915fc
commit 210db686bb
  1. 84
      README.md
  2. 10
      RELEASES.md
  3. BIN
      apk/ai.comma.plus.offroad.apk
  4. 7
      cereal/Makefile
  5. 1
      cereal/car.capnp
  6. 19
      cereal/log.capnp
  7. 3
      common/params.py
  8. 2
      common/transformations/model.py
  9. BIN
      installer/updater/updater
  10. 1
      phonelibs/openblas/libopenblas.so
  11. BIN
      phonelibs/openblas/libopenblas_armv8p-r0.2.19.so
  12. 3
      requirements_openpilot.txt
  13. 7
      selfdrive/boardd/boardd.py
  14. 1
      selfdrive/car/ford/interface.py
  15. 1
      selfdrive/car/gm/interface.py
  16. 15
      selfdrive/car/honda/carstate.py
  17. 7
      selfdrive/car/honda/interface.py
  18. 1
      selfdrive/car/hyundai/interface.py
  19. 1
      selfdrive/car/mock/interface.py
  20. 1
      selfdrive/car/toyota/interface.py
  21. 2
      selfdrive/common/version.h
  22. 13
      selfdrive/controls/controlsd.py
  23. 12
      selfdrive/controls/lib/longitudinal_mpc/libmpc_py.py
  24. 31
      selfdrive/controls/lib/planner.py
  25. 0
      selfdrive/locationd/calibration_helpers.py
  26. 8
      selfdrive/locationd/calibrationd.py
  27. BIN
      selfdrive/loggerd/loggerd
  28. 12
      selfdrive/manager.py
  29. 0
      selfdrive/mapd/__init__.py
  30. 240
      selfdrive/mapd/mapd.py
  31. 223
      selfdrive/mapd/mapd_helpers.py
  32. BIN
      selfdrive/sensord/gpsd
  33. BIN
      selfdrive/sensord/sensord
  34. 28
      selfdrive/thermald.py
  35. 251
      selfdrive/ui/ui.c
  36. BIN
      selfdrive/visiond/visiond

@ -51,54 +51,54 @@ Also, we have a several thousand people community on [slack](https://slack.comma
Hardware Hardware
------ ------
Right now openpilot supports the [EON Dashcam DevKit](https://comma.ai/shop/products/eon-dashcam-devkit). We'd like to support other platforms as well. At the moment openpilot supports the [EON Dashcam DevKit](https://comma.ai/shop/products/eon-dashcam-devkit). A [panda](https://shop.comma.ai/products/panda-obd-ii-dongle) and a [giraffe](https://comma.ai/shop/products/giraffe/) are recommended tools to interface the EON with the car. We'd like to support other platforms as well.
Install openpilot on a neo device by entering ``https://openpilot.comma.ai`` during NEOS setup. Install openpilot on a neo device by entering ``https://openpilot.comma.ai`` during NEOS setup.
Supported Cars Supported Cars
------ ------
| Make | Model | Supported Package | Lateral | Longitudinal | No Accel Below | No Steer Below | Giraffe | | Make | Model | Supported Package | Lateral | Longitudinal | No Accel Below | No Steer Below | Giraffe |
| ---------------------| ------------------------| ---------------------| --------| ---------------| -----------------| ---------------|-------------------| | ---------------------| -------------------------| ---------------------| --------| ---------------| -----------------| ---------------|-------------------|
| Acura | ILX 2016-17 | AcuraWatch Plus | Yes | Yes | 25mph<sup>1</sup>| 25mph | Nidec | | Acura | ILX 2016-17 | AcuraWatch Plus | Yes | Yes | 25mph<sup>1</sup>| 25mph | Nidec |
| Acura | RDX 2018 | AcuraWatch Plus | Yes | Yes | 25mph<sup>1</sup>| 12mph | Nidec | | Acura | RDX 2018 | AcuraWatch Plus | Yes | Yes | 25mph<sup>1</sup>| 12mph | Nidec |
| Cadillac<sup>3</sup> | ATS 2018 | Adaptive Cruise | Yes | Yes | 0mph | 7mph | Custom<sup>7</sup>| | Chevrolet<sup>3</sup>| Malibu 2017 | Adaptive Cruise | Yes | Yes | 0mph | 7mph | Custom<sup>7</sup>|
| Chevrolet<sup>3</sup>| Malibu 2017 | Adaptive Cruise | Yes | Yes | 0mph | 7mph | Custom<sup>7</sup>| | Chevrolet<sup>3</sup>| Volt 2017-18 | Adaptive Cruise | Yes | Yes | 0mph | 7mph | Custom<sup>7</sup>|
| Chevrolet<sup>3</sup>| Volt 2017-18 | Adaptive Cruise | Yes | Yes | 0mph | 7mph | Custom<sup>7</sup>| | Cadillac<sup>3</sup> | ATS 2018 | Adaptive Cruise | Yes | Yes | 0mph | 7mph | Custom<sup>7</sup>|
| GMC<sup>3</sup>| Acadia Denali 2018 | Adaptive Cruise | Yes | Yes | 0mph | 7mph | Custom<sup>7</sup>| | GMC<sup>3</sup> | Acadia Denali 2018 | Adaptive Cruise | Yes | Yes | 0mph | 7mph | Custom<sup>7</sup>|
| Holden<sup>3</sup> | Astra 2017 | Adaptive Cruise | Yes | Yes | 0mph | 7mph | Custom<sup>7</sup>| | Holden<sup>3</sup> | Astra 2017 | Adaptive Cruise | Yes | Yes | 0mph | 7mph | Custom<sup>7</sup>|
| Honda | Accord 2018 | All | Yes | Stock | 0mph | 3mph | Bosch | | Honda | Accord 2018 | All | Yes | Stock | 0mph | 3mph | Bosch |
| Honda | Civic 2016-18 | Honda Sensing | Yes | Yes | 0mph | 12mph | Nidec | | Honda | Civic Sedan/Coupe 2016-18| Honda Sensing | Yes | Yes | 0mph | 12mph | Nidec |
| Honda | Civic 2017-18 *(Hatch)* | Honda Sensing | Yes | Stock | 0mph | 12mph | Bosch | | Honda | Civic Hatchback 2017-18 | Honda Sensing | Yes | Stock | 0mph | 12mph | Bosch |
| Honda | CR-V 2015-16 | Touring | Yes | Yes | 25mph<sup>1</sup>| 12mph | Nidec | | Honda | CR-V 2015-16 | Touring | Yes | Yes | 25mph<sup>1</sup>| 12mph | Nidec |
| Honda | CR-V 2017-18 | Honda Sensing | Yes | Stock | 0mph | 12mph | Bosch | | Honda | CR-V 2017-18 | Honda Sensing | Yes | Stock | 0mph | 12mph | Bosch |
| Honda | Odyssey 2017-19 | Honda Sensing | Yes | Yes | 25mph<sup>1</sup>| 0mph | Inverted Nidec | | Honda | Odyssey 2017-19 | Honda Sensing | Yes | Yes | 25mph<sup>1</sup>| 0mph | Inverted Nidec |
| Honda | Pilot 2016-18 | Honda Sensing | Yes | Yes | 25mph<sup>1</sup>| 12mph | Nidec | | Honda | Pilot 2016-18 | Honda Sensing | Yes | Yes | 25mph<sup>1</sup>| 12mph | Nidec |
| Honda | Pilot 2019 | All | Yes | Yes | 25mph<sup>1</sup>| 12mph | Inverted Nidec | | Honda | Pilot 2019 | All | Yes | Yes | 25mph<sup>1</sup>| 12mph | Inverted Nidec |
| Honda | Ridgeline 2017-18 | Honda Sensing | Yes | Yes | 25mph<sup>1</sup>| 12mph | Nidec | | Honda | Ridgeline 2017-18 | Honda Sensing | Yes | Yes | 25mph<sup>1</sup>| 12mph | Nidec |
| Hyundai | Santa Fe 2019 | All | Yes | Stock | 0mph | 0mph | Custom<sup>6</sup>| | Hyundai | Santa Fe 2019 | All | Yes | Stock | 0mph | 0mph | Custom<sup>6</sup>|
| Hyundai | Elantra 2017 | SCC + LKAS | Yes | Stock | 19mph | 34mph | Custom<sup>6</sup>| | Hyundai | Elantra 2017 | SCC + LKAS | Yes | Stock | 19mph | 34mph | Custom<sup>6</sup>|
| Hyundai | Genesis 2018 | All | Yes | Stock | 19mph | 34mph | Custom<sup>6</sup>| | Hyundai | Genesis 2018 | All | Yes | Stock | 19mph | 34mph | Custom<sup>6</sup>|
| Kia | Sorento 2018 | All | Yes | Stock | 0mph | 0mph | Custom<sup>6</sup>| | Kia | Sorento 2018 | All | Yes | Stock | 0mph | 0mph | Custom<sup>6</sup>|
| Kia | Stinger 2018 | SCC + LKAS | Yes | Stock | 0mph | 0mph | Custom<sup>6</sup>| | Kia | Stinger 2018 | SCC + LKAS | Yes | Stock | 0mph | 0mph | Custom<sup>6</sup>|
| Lexus | RX Hybrid 2016-18 | All | Yes | Yes<sup>2</sup>| 0mph | 0mph | Toyota | | Lexus | RX Hybrid 2016-18 | All | Yes | Yes<sup>2</sup>| 0mph | 0mph | Toyota |
| Toyota | Camry 2018<sup>4</sup> | All | Yes | Stock | 0mph<sup>5</sup> | 0mph | Toyota | | Toyota | Camry 2018<sup>4</sup> | All | Yes | Stock | 0mph<sup>5</sup> | 0mph | Toyota |
| Toyota | C-HR 2017-18<sup>4</sup>| All | Yes | Stock | 0mph | 0mph | Toyota | | Toyota | C-HR 2017-18<sup>4</sup> | All | Yes | Stock | 0mph | 0mph | Toyota |
| Toyota | Corolla 2017-18 | All | Yes | Yes<sup>2</sup>| 20mph | 0mph | Toyota | | Toyota | Corolla 2017-18 | All | Yes | Yes<sup>2</sup>| 20mph | 0mph | Toyota |
| Toyota | Highlander 2017-18 | All | Yes | Yes<sup>2</sup>| 0mph | 0mph | Toyota | | Toyota | Highlander 2017-18 | All | Yes | Yes<sup>2</sup>| 0mph | 0mph | Toyota |
| Toyota | Highlander Hybrid 2018 | All | Yes | Yes<sup>2</sup>| 0mph | 0mph | Toyota | | Toyota | Highlander Hybrid 2018 | All | Yes | Yes<sup>2</sup>| 0mph | 0mph | Toyota |
| Toyota | Prius 2016 | TSS-P | Yes | Yes<sup>2</sup>| 0mph | 0mph | Toyota | | Toyota | Prius 2016 | TSS-P | Yes | Yes<sup>2</sup>| 0mph | 0mph | Toyota |
| Toyota | Prius 2017-18 | All | Yes | Yes<sup>2</sup>| 0mph | 0mph | Toyota | | Toyota | Prius 2017-18 | All | Yes | Yes<sup>2</sup>| 0mph | 0mph | Toyota |
| Toyota | Prius Prime 2017-18 | All | Yes | Yes<sup>2</sup>| 0mph | 0mph | Toyota | | Toyota | Prius Prime 2017-18 | All | Yes | Yes<sup>2</sup>| 0mph | 0mph | Toyota |
| Toyota | Rav4 2016 | TSS-P | Yes | Yes<sup>2</sup>| 20mph | 0mph | Toyota | | Toyota | Rav4 2016 | TSS-P | Yes | Yes<sup>2</sup>| 20mph | 0mph | Toyota |
| Toyota | Rav4 2017-18 | All | Yes | Yes<sup>2</sup>| 20mph | 0mph | Toyota | | Toyota | Rav4 2017-18 | All | Yes | Yes<sup>2</sup>| 20mph | 0mph | Toyota |
| Toyota | Rav4 Hybrid 2017-18 | All | Yes | Yes<sup>2</sup>| 0mph | 0mph | Toyota | | Toyota | Rav4 Hybrid 2017-18 | All | Yes | Yes<sup>2</sup>| 0mph | 0mph | Toyota |
<sup>1</sup>[Comma Pedal](https://community.comma.ai/wiki/index.php/Comma_Pedal) is used to provide stop-and-go capability to some of the openpilot-supported cars that don't currently support stop-and-go. Here is how to [build a Comma Pedal](https://medium.com/@jfrux/comma-pedal-building-with-macrofab-6328bea791e8). ***NOTE: The Comma Pedal is not officially supported by [comma.ai](https://comma.ai)*** <sup>1</sup>[Comma Pedal](https://community.comma.ai/wiki/index.php/Comma_Pedal) is used to provide stop-and-go capability to some of the openpilot-supported cars that don't currently support stop-and-go. Here is how to [build a Comma Pedal](https://medium.com/@jfrux/comma-pedal-building-with-macrofab-6328bea791e8). ***NOTE: The Comma Pedal is not officially supported by [comma.ai](https://comma.ai)***
<sup>2</sup>When disconnecting the Driver Support Unit (DSU), otherwise longitudinal control is stock ACC. For DSU locations, see [Toyota Wiki page](https://community.comma.ai/wiki/index.php/Toyota) <sup>2</sup>When disconnecting the Driver Support Unit (DSU), otherwise longitudinal control is stock ACC. For DSU locations, see [Toyota Wiki page](https://community.comma.ai/wiki/index.php/Toyota)
<sup>3</sup>[GM installation guide](https://zoneos.com/volt/). <sup>3</sup>[GM installation guide](https://zoneos.com/volt/).
<sup>4</sup>It needs an extra 120Ohm resistor ([pic1](https://i.imgur.com/CmdKtTP.jpg), [pic2](https://i.imgur.com/s2etUo6.jpg)) on bus 3 and giraffe switches set to 01X1 (11X1 for stock LKAS), where X depends on if you have the [comma power](https://comma.ai/shop/products/power/). <sup>4</sup>It needs an extra 120Ohm resistor ([pic1](https://i.imgur.com/CmdKtTP.jpg), [pic2](https://i.imgur.com/s2etUo6.jpg)) on bus 3 and giraffe switches set to 01X1 (11X1 for stock LKAS), where X depends on if you have the [comma power](https://comma.ai/shop/products/power/).
<sup>5</sup>28mph for Camry 4CYL L, 4CYL LE and 4CYL SE which don't have Full-Speed Range Dynamic Radar Cruise Control. <sup>5</sup>28mph for Camry 4CYL L, 4CYL LE and 4CYL SE which don't have Full-Speed Range Dynamic Radar Cruise Control.
<sup>6</sup>Open sourced [Hyundai Giraffe](https://github.com/commaai/neo/tree/master/giraffe/hyundai) is designed ofor the 2019 Sante Fe; pinout may differ for other Hyundais. <br /> <sup>6</sup>Open sourced [Hyundai Giraffe](https://github.com/commaai/neo/tree/master/giraffe/hyundai) is designed ofor the 2019 Sante Fe; pinout may differ for other Hyundais. <br />
<sup>7</sup>Community built Giraffe, find more information here, [GM Giraffe](https://zoneos.com/shop/) <br /> <sup>7</sup>Community built Giraffe, find more information here, [GM Giraffe](https://zoneos.com/shop/) <br />

@ -1,3 +1,13 @@
Version 0.5.7 (2018-12-06)
========================
* Speed limit from OpenStreetMap added to UI
* Highlight speed limit when speed exceeds road speed limit plus a delta
* Option to limit openpilot max speed to road speed limit plus a delta
* Cadillac ATS support thanks to vntarasov!
* GMC Acadia support thanks to CryptoKylan!
* Decrease GPU power consumption
* NEOSv8 autoupdate
Version 0.5.6 (2018-11-16) Version 0.5.6 (2018-11-16)
======================== ========================
* Refresh settings layout and add feature descriptions * Refresh settings layout and add feature descriptions

Binary file not shown.

@ -6,13 +6,16 @@ GENS := gen/cpp/car.capnp.c++ gen/cpp/log.capnp.c++
JS := gen/js/car.capnp.js gen/js/log.capnp.js JS := gen/js/car.capnp.js gen/js/log.capnp.js
UNAME_M ?= $(shell uname -m) UNAME_M ?= $(shell uname -m)
# only generate C++ for docker tests # only generate C++ for docker tests
ifneq ($(OPTEST),1) ifneq ($(OPTEST),1)
GENS += gen/c/car.capnp.c gen/c/log.capnp.c gen/c/include/c++.capnp.h gen/c/include/java.capnp.h GENS += gen/c/car.capnp.c gen/c/log.capnp.c gen/c/include/c++.capnp.h gen/c/include/java.capnp.h
ifeq ($(UNAME_M),x86_64) ifeq ($(UNAME_M),x86_64)
GENS += gen/java/Car.java gen/java/Log.java ifneq (, $(shell which capnpc-java))
GENS += gen/java/Car.java gen/java/Log.java
else
$(warning capnpc-java not found, skipping java build)
endif
endif endif
endif endif

@ -355,6 +355,7 @@ struct CarParams {
radarOffCan @47 :Bool; # True when radar objects aren't visible on CAN radarOffCan @47 :Bool; # True when radar objects aren't visible on CAN
steerActuatorDelay @48 :Float32; # Steering wheel actuator delay in seconds steerActuatorDelay @48 :Float32; # Steering wheel actuator delay in seconds
openpilotLongitudinalControl @50 :Bool; # is openpilot doing the longitudinal control?
enum SteerControlType { enum SteerControlType {
torque @0; torque @0;

@ -276,7 +276,8 @@ struct ThermalData {
startedTs @13 :UInt64; startedTs @13 :UInt64;
thermalStatus @14 :ThermalStatus; thermalStatus @14 :ThermalStatus;
chargerDisabled @17 :Bool; chargingError @17 :Bool;
chargingDisabled @18 :Bool;
enum ThermalStatus { enum ThermalStatus {
green @0; # all processes run green @0; # all processes run
@ -344,6 +345,7 @@ struct LiveCalibrationData {
warpMatrix @0 :List(Float32); warpMatrix @0 :List(Float32);
# camera_frame_from_model_frame # camera_frame_from_model_frame
warpMatrix2 @5 :List(Float32); warpMatrix2 @5 :List(Float32);
warpMatrixBig @6 :List(Float32);
calStatus @1 :Int8; calStatus @1 :Int8;
calCycle @2 :Int32; calCycle @2 :Int32;
calPerc @3 :Int8; calPerc @3 :Int8;
@ -562,6 +564,10 @@ struct Plan {
gpsPlannerActive @19 :Bool; gpsPlannerActive @19 :Bool;
# maps
vCurvature @21 :Float32;
decelForTurn @22 :Bool;
struct GpsTrajectory { struct GpsTrajectory {
x @0 :List(Float32); x @0 :List(Float32);
y @1 :List(Float32); y @1 :List(Float32);
@ -1567,8 +1573,17 @@ struct LiveParametersData {
} }
struct LiveMapData { struct LiveMapData {
valid @0 :Bool; speedLimitValid @0 :Bool;
speedLimit @1 :Float32; speedLimit @1 :Float32;
curvatureValid @2 :Bool;
curvature @3 :Float32;
wayId @4 :UInt64;
roadX @5 :List(Float32);
roadY @6 :List(Float32);
lastGps @7: GpsLocationData;
roadCurvatureX @8 :List(Float32);
roadCurvature @9 :List(Float32);
distToTurn @10 :Float32;
} }

@ -63,6 +63,7 @@ keys = {
"IsUploadVideoOverCellularEnabled": TxType.PERSISTENT, "IsUploadVideoOverCellularEnabled": TxType.PERSISTENT,
"IsDriverMonitoringEnabled": TxType.PERSISTENT, "IsDriverMonitoringEnabled": TxType.PERSISTENT,
"IsGeofenceEnabled": TxType.PERSISTENT, "IsGeofenceEnabled": TxType.PERSISTENT,
"SpeedLimitOffset": TxType.PERSISTENT,
# written: visiond # written: visiond
# read: visiond, controlsd # read: visiond, controlsd
"CalibrationParams": TxType.PERSISTENT, "CalibrationParams": TxType.PERSISTENT,
@ -74,6 +75,8 @@ keys = {
"DoUninstall": TxType.CLEAR_ON_MANAGER_START, "DoUninstall": TxType.CLEAR_ON_MANAGER_START,
"ShouldDoUpdate": TxType.CLEAR_ON_MANAGER_START, "ShouldDoUpdate": TxType.CLEAR_ON_MANAGER_START,
"IsUpdateAvailable": TxType.PERSISTENT, "IsUpdateAvailable": TxType.PERSISTENT,
"LongitudinalControl": TxType.PERSISTENT,
"LimitSetSpeed": TxType.PERSISTENT,
"RecordFront": TxType.PERSISTENT, "RecordFront": TxType.PERSISTENT,
} }

@ -83,7 +83,7 @@ def get_model_height_transform(camera_frame_from_road_frame, height):
# camera_frame_from_model_frame aka 'warp matrix' # camera_frame_from_model_frame aka 'warp matrix'
# was: calibration.h/CalibrationTransform # was: calibration.h/CalibrationTransform
def get_camera_frame_from_model_frame(camera_frame_from_road_frame, height): def get_camera_frame_from_model_frame(camera_frame_from_road_frame, height=model_height):
vp = vp_from_ke(camera_frame_from_road_frame) vp = vp_from_ke(camera_frame_from_road_frame)
model_camera_from_model_frame = np.array([ model_camera_from_model_frame = np.array([

Binary file not shown.

@ -0,0 +1 @@
libopenblas_armv8p-r0.2.19.so

@ -5,7 +5,7 @@ libusb1==1.5.0
pycapnp==0.6.3 pycapnp==0.6.3
pyzmq==15.4.0 pyzmq==15.4.0
raven==5.23.0 raven==5.23.0
requests==2.10.0 requests==2.20.0
setproctitle==1.1.10 setproctitle==1.1.10
simplejson==3.8.2 simplejson==3.8.2
pyyaml==3.12 pyyaml==3.12
@ -16,4 +16,5 @@ filterpy==1.2.4
smbus2==0.2.0 smbus2==0.2.0
pyflakes==1.6.0 pyflakes==1.6.0
-e git+https://github.com/commaai/le_python.git@5eef8f5be5929d33973e1b10e686fa0cdcd6792f#egg=Logentries -e git+https://github.com/commaai/le_python.git@5eef8f5be5929d33973e1b10e686fa0cdcd6792f#egg=Logentries
-e git+https://github.com/commaai/python-overpy.git@f86529af402d4642e1faeb146671c40284007323#egg=overpy
Flask==1.0.2 Flask==1.0.2

@ -71,9 +71,10 @@ def __parse_can_buffer(dat):
def can_send_many(arr): def can_send_many(arr):
snds = [] snds = []
for addr, _, dat, alt in arr: for addr, _, dat, alt in arr:
snd = struct.pack("II", ((addr << 21) | 1), len(dat) | (alt << 4)) + dat if addr < 0x800: # only support 11 bit addr
snd = snd.ljust(0x10, '\x00') snd = struct.pack("II", ((addr << 21) | 1), len(dat) | (alt << 4)) + dat
snds.append(snd) snd = snd.ljust(0x10, '\x00')
snds.append(snd)
while 1: while 1:
try: try:
handle.bulkWrite(3, ''.join(snds)) handle.bulkWrite(3, ''.join(snds))

@ -119,6 +119,7 @@ class CarInterface(object):
ret.brakeMaxV = [1., 0.8] ret.brakeMaxV = [1., 0.8]
ret.enableCamera = not any(x for x in [970, 973, 984] if x in fingerprint) ret.enableCamera = not any(x for x in [970, 973, 984] if x in fingerprint)
ret.openpilotLongitudinalControl = False
cloudlog.warn("ECU Camera Simulated: %r", ret.enableCamera) cloudlog.warn("ECU Camera Simulated: %r", ret.enableCamera)
ret.steerLimitAlert = False ret.steerLimitAlert = False

@ -62,6 +62,7 @@ class CarInterface(object):
# Have to go passive if ASCM is online (ACC-enabled cars), # Have to go passive if ASCM is online (ACC-enabled cars),
# or camera is on powertrain bus (LKA cars without ACC). # or camera is on powertrain bus (LKA cars without ACC).
ret.enableCamera = not any(x for x in STOCK_CONTROL_MSGS[candidate] if x in fingerprint) ret.enableCamera = not any(x for x in STOCK_CONTROL_MSGS[candidate] if x in fingerprint)
ret.openpilotLongitudinalControl = ret.enableCamera
std_cargo = 136 std_cargo = 136

@ -40,8 +40,6 @@ def get_can_signals(CP):
("LEFT_BLINKER", "SCM_FEEDBACK", 0), ("LEFT_BLINKER", "SCM_FEEDBACK", 0),
("RIGHT_BLINKER", "SCM_FEEDBACK", 0), ("RIGHT_BLINKER", "SCM_FEEDBACK", 0),
("GEAR", "GEARBOX", 0), ("GEAR", "GEARBOX", 0),
("BRAKE_ERROR_1", "STANDSTILL", 1),
("BRAKE_ERROR_2", "STANDSTILL", 1),
("SEATBELT_DRIVER_LAMP", "SEATBELT_STATUS", 1), ("SEATBELT_DRIVER_LAMP", "SEATBELT_STATUS", 1),
("SEATBELT_DRIVER_LATCHED", "SEATBELT_STATUS", 0), ("SEATBELT_DRIVER_LATCHED", "SEATBELT_STATUS", 0),
("BRAKE_PRESSED", "POWERTRAIN_DATA", 0), ("BRAKE_PRESSED", "POWERTRAIN_DATA", 0),
@ -63,7 +61,6 @@ def get_can_signals(CP):
("STEERING_SENSORS", 100), ("STEERING_SENSORS", 100),
("SCM_FEEDBACK", 10), ("SCM_FEEDBACK", 10),
("GEARBOX", 100), ("GEARBOX", 100),
("STANDSTILL", 50),
("SEATBELT_STATUS", 10), ("SEATBELT_STATUS", 10),
("CRUISE", 10), ("CRUISE", 10),
("POWERTRAIN_DATA", 100), ("POWERTRAIN_DATA", 100),
@ -84,9 +81,12 @@ def get_can_signals(CP):
checks += [("GAS_PEDAL_2", 100)] checks += [("GAS_PEDAL_2", 100)]
else: else:
# Nidec signals. # Nidec signals.
signals += [("CRUISE_SPEED_PCM", "CRUISE", 0), signals += [("BRAKE_ERROR_1", "STANDSTILL", 1),
("BRAKE_ERROR_2", "STANDSTILL", 1),
("CRUISE_SPEED_PCM", "CRUISE", 0),
("CRUISE_SPEED_OFFSET", "CRUISE_PARAMS", 0)] ("CRUISE_SPEED_OFFSET", "CRUISE_PARAMS", 0)]
checks += [("CRUISE_PARAMS", 50)] checks += [("CRUISE_PARAMS", 50),
("STANDSTILL", 50)]
if CP.carFingerprint in (CAR.ACCORD, CAR.ACCORD_15, CAR.ACCORDH): if CP.carFingerprint in (CAR.ACCORD, CAR.ACCORD_15, CAR.ACCORDH):
signals += [("DRIVERS_DOOR_OPEN", "SCM_FEEDBACK", 1)] signals += [("DRIVERS_DOOR_OPEN", "SCM_FEEDBACK", 1)]
@ -205,7 +205,10 @@ class CarState(object):
self.steer_error = cp.vl["STEER_STATUS"]['STEER_STATUS'] not in [0, 2, 3, 4, 6] self.steer_error = cp.vl["STEER_STATUS"]['STEER_STATUS'] not in [0, 2, 3, 4, 6]
self.steer_not_allowed = cp.vl["STEER_STATUS"]['STEER_STATUS'] != 0 self.steer_not_allowed = cp.vl["STEER_STATUS"]['STEER_STATUS'] != 0
self.steer_warning = cp.vl["STEER_STATUS"]['STEER_STATUS'] not in [0, 3] # 3 is low speed lockout, not worth a warning self.steer_warning = cp.vl["STEER_STATUS"]['STEER_STATUS'] not in [0, 3] # 3 is low speed lockout, not worth a warning
self.brake_error = cp.vl["STANDSTILL"]['BRAKE_ERROR_1'] or cp.vl["STANDSTILL"]['BRAKE_ERROR_2'] if self.CP.radarOffCan:
self.brake_error = 0
else:
self.brake_error = cp.vl["STANDSTILL"]['BRAKE_ERROR_1'] or cp.vl["STANDSTILL"]['BRAKE_ERROR_2']
self.esp_disabled = cp.vl["VSA_STATUS"]['ESP_DISABLED'] self.esp_disabled = cp.vl["VSA_STATUS"]['ESP_DISABLED']
# calc best v_ego estimate, by averaging two opposite corners # calc best v_ego estimate, by averaging two opposite corners

@ -151,10 +151,13 @@ class CarInterface(object):
ret.safetyModel = car.CarParams.SafetyModels.hondaBosch ret.safetyModel = car.CarParams.SafetyModels.hondaBosch
ret.enableCamera = True ret.enableCamera = True
ret.radarOffCan = True ret.radarOffCan = True
ret.openpilotLongitudinalControl = False
else: else:
ret.safetyModel = car.CarParams.SafetyModels.honda ret.safetyModel = car.CarParams.SafetyModels.honda
ret.enableCamera = not any(x for x in CAMERA_MSGS if x in fingerprint) ret.enableCamera = not any(x for x in CAMERA_MSGS if x in fingerprint)
ret.enableGasInterceptor = 0x201 in fingerprint ret.enableGasInterceptor = 0x201 in fingerprint
ret.openpilotLongitudinalControl = ret.enableCamera
cloudlog.warn("ECU Camera Simulated: %r", ret.enableCamera) cloudlog.warn("ECU Camera Simulated: %r", ret.enableCamera)
cloudlog.warn("ECU Gas Interceptor: %r", ret.enableGasInterceptor) cloudlog.warn("ECU Gas Interceptor: %r", ret.enableGasInterceptor)
@ -485,8 +488,8 @@ class CarInterface(object):
ret.buttonEvents = buttonEvents ret.buttonEvents = buttonEvents
# events # events
# TODO: I don't like the way capnp does enums # TODO: event names aren't checked at compile time.
# These strings aren't checked at compile time # Maybe there is a way to use capnp enums directly
events = [] events = []
if not self.CS.can_valid: if not self.CS.can_valid:
self.can_invalid_count += 1 self.can_invalid_count += 1

@ -163,6 +163,7 @@ class CarInterface(object):
ret.longPidDeadzoneV = [0.] ret.longPidDeadzoneV = [0.]
ret.enableCamera = not any(x for x in CAMERA_MSGS if x in fingerprint) ret.enableCamera = not any(x for x in CAMERA_MSGS if x in fingerprint)
ret.openpilotLongitudinalControl = False
ret.steerLimitAlert = False ret.steerLimitAlert = False
ret.stoppingControl = False ret.stoppingControl = False

@ -47,6 +47,7 @@ class CarInterface(object):
ret.carFingerprint = candidate ret.carFingerprint = candidate
ret.safetyModel = car.CarParams.SafetyModels.noOutput ret.safetyModel = car.CarParams.SafetyModels.noOutput
ret.openpilotLongitudinalControl = False
# FIXME: hardcoding honda civic 2016 touring params so they can be used to # FIXME: hardcoding honda civic 2016 touring params so they can be used to
# scale unknown params for other cars # scale unknown params for other cars

@ -186,6 +186,7 @@ class CarInterface(object):
ret.enableCamera = not check_ecu_msgs(fingerprint, ECU.CAM) ret.enableCamera = not check_ecu_msgs(fingerprint, ECU.CAM)
ret.enableDsu = not check_ecu_msgs(fingerprint, ECU.DSU) ret.enableDsu = not check_ecu_msgs(fingerprint, ECU.DSU)
ret.enableApgs = False #not check_ecu_msgs(fingerprint, ECU.APGS) ret.enableApgs = False #not check_ecu_msgs(fingerprint, ECU.APGS)
ret.openpilotLongitudinalControl = ret.enableCamera and ret.enableDsu
cloudlog.warn("ECU Camera Simulated: %r", ret.enableCamera) cloudlog.warn("ECU Camera Simulated: %r", ret.enableCamera)
cloudlog.warn("ECU DSU Simulated: %r", ret.enableDsu) cloudlog.warn("ECU DSU Simulated: %r", ret.enableDsu)
cloudlog.warn("ECU APGS Simulated: %r", ret.enableApgs) cloudlog.warn("ECU APGS Simulated: %r", ret.enableApgs)

@ -1 +1 @@
#define COMMA_VERSION "0.5.6-release" #define COMMA_VERSION "0.5.7-release"

@ -2,13 +2,11 @@
import gc import gc
import zmq import zmq
import json import json
from cereal import car, log from cereal import car, log
from common.numpy_fast import clip from common.numpy_fast import clip
from common.realtime import sec_since_boot, set_realtime_priority, Ratekeeper from common.realtime import sec_since_boot, set_realtime_priority, Ratekeeper
from common.profiler import Profiler from common.profiler import Profiler
from common.params import Params from common.params import Params
import selfdrive.messaging as messaging import selfdrive.messaging as messaging
from selfdrive.config import Conversions as CV from selfdrive.config import Conversions as CV
from selfdrive.services import service_list from selfdrive.services import service_list
@ -25,7 +23,7 @@ from selfdrive.controls.lib.latcontrol import LatControl
from selfdrive.controls.lib.alertmanager import AlertManager from selfdrive.controls.lib.alertmanager import AlertManager
from selfdrive.controls.lib.vehicle_model import VehicleModel from selfdrive.controls.lib.vehicle_model import VehicleModel
from selfdrive.controls.lib.driver_monitor import DriverStatus from selfdrive.controls.lib.driver_monitor import DriverStatus
from selfdrive.locationd.calibration_values import Calibration, Filter from selfdrive.locationd.calibration_helpers import Calibration, Filter
ThermalStatus = log.ThermalData.ThermalStatus ThermalStatus = log.ThermalData.ThermalStatus
State = log.Live100Data.ControlState State = log.Live100Data.ControlState
@ -121,14 +119,14 @@ def data_sample(CI, CC, thermal, calibration, health, driver_monitor, gps_locati
return CS, events, cal_status, cal_perc, overtemp, free_space, low_battery, mismatch_counter return CS, events, cal_status, cal_perc, overtemp, free_space, low_battery, mismatch_counter
def calc_plan(CS, CP, events, PL, LaC, LoC, v_cruise_kph, driver_status, geofence): def calc_plan(CS, CP, VM, events, PL, LaC, LoC, v_cruise_kph, driver_status, geofence):
"""Calculate a longitudinal plan using MPC""" """Calculate a longitudinal plan using MPC"""
# Slow down when based on driver monitoring or geofence # Slow down when based on driver monitoring or geofence
force_decel = driver_status.awareness < 0. or (geofence is not None and not geofence.in_geofence) force_decel = driver_status.awareness < 0. or (geofence is not None and not geofence.in_geofence)
# Update planner # Update planner
plan_packet = PL.update(CS, LaC, LoC, v_cruise_kph, force_decel) plan_packet = PL.update(CS, CP, VM, LaC, LoC, v_cruise_kph, force_decel)
plan = plan_packet.plan plan = plan_packet.plan
plan_ts = plan_packet.logMonoTime plan_ts = plan_packet.logMonoTime
events += list(plan.events) events += list(plan.events)
@ -461,6 +459,7 @@ def controlsd_thread(gctx=None, rate=100, default_bias=0.):
# Write CarParams for radard and boardd safety mode # Write CarParams for radard and boardd safety mode
params.put("CarParams", CP.to_bytes()) params.put("CarParams", CP.to_bytes())
params.put("LongitudinalControl", "1" if CP.openpilotLongitudinalControl else "0")
state = State.disabled state = State.disabled
soft_disable_timer = 0 soft_disable_timer = 0
@ -496,7 +495,7 @@ def controlsd_thread(gctx=None, rate=100, default_bias=0.):
prof.checkpoint("Sample") prof.checkpoint("Sample")
# Define longitudinal plan (MPC) # Define longitudinal plan (MPC)
plan, plan_ts = calc_plan(CS, CP, events, PL, LaC, LoC, v_cruise_kph, driver_status, geofence) plan, plan_ts = calc_plan(CS, CP, VM, events, PL, LaC, LoC, v_cruise_kph, driver_status, geofence)
prof.checkpoint("Plan") prof.checkpoint("Plan")
if not passive: if not passive:
@ -512,7 +511,7 @@ def controlsd_thread(gctx=None, rate=100, default_bias=0.):
# Publish data # Publish data
CC = data_send(PL.perception_state, plan, plan_ts, CS, CI, CP, VM, state, events, actuators, v_cruise_kph, rk, carstate, carcontrol, CC = data_send(PL.perception_state, plan, plan_ts, CS, CI, CP, VM, state, events, actuators, v_cruise_kph, rk, carstate, carcontrol,
live100, livempc, AM, driver_status, LaC, LoC, angle_offset, passive) live100, livempc, AM, driver_status, LaC, LoC, angle_offset, passive)
prof.checkpoint("Sent") prof.checkpoint("Sent")
rk.keep_time() # Run at 100Hz rk.keep_time() # Run at 100Hz

@ -1,11 +1,19 @@
import os import os
import platform
import subprocess import subprocess
from cffi import FFI from cffi import FFI
mpc_dir = os.path.join(os.path.dirname(os.path.abspath(__file__))) mpc_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)))
subprocess.check_call(["make", "-j4"], cwd=mpc_dir)
if platform.machine() == "x86_64":
try:
FFI().dlopen(os.path.join(mpc_dir, "libmpc1.so"))
except OSError:
# libmpc1.so is likely built for aarch64. cleaning...
subprocess.check_call(["make", "clean"], cwd=mpc_dir)
subprocess.check_call(["make", "-j4"], cwd=mpc_dir)
def _get_libmpc(mpc_id): def _get_libmpc(mpc_id):
libmpc_fn = os.path.join(mpc_dir, "libmpc%d.so" % mpc_id) libmpc_fn = os.path.join(mpc_dir, "libmpc%d.so" % mpc_id)
@ -36,9 +44,7 @@ def _get_libmpc(mpc_id):
return (ffi, ffi.dlopen(libmpc_fn)) return (ffi, ffi.dlopen(libmpc_fn))
mpcs = [_get_libmpc(1), _get_libmpc(2)] mpcs = [_get_libmpc(1), _get_libmpc(2)]
def get_libmpc(mpc_id): def get_libmpc(mpc_id):
return mpcs[mpc_id - 1] return mpcs[mpc_id - 1]

@ -6,6 +6,7 @@ import numpy as np
from copy import copy from copy import copy
from cereal import log from cereal import log
from collections import defaultdict from collections import defaultdict
from common.params import Params
from common.realtime import sec_since_boot from common.realtime import sec_since_boot
from common.numpy_fast import interp from common.numpy_fast import interp
import selfdrive.messaging as messaging import selfdrive.messaging as messaging
@ -19,6 +20,10 @@ from selfdrive.controls.lib.speed_smoother import speed_smoother
from selfdrive.controls.lib.longcontrol import LongCtrlState, MIN_CAN_SPEED from selfdrive.controls.lib.longcontrol import LongCtrlState, MIN_CAN_SPEED
from selfdrive.controls.lib.radar_helpers import _LEAD_ACCEL_TAU from selfdrive.controls.lib.radar_helpers import _LEAD_ACCEL_TAU
# Max lateral acceleration, used to caclulate how much to slow down in turns
A_Y_MAX = 2.0 # m/s^2
NO_CURVATURE_SPEED = 200. * CV.MPH_TO_MS
_DT = 0.01 # 100Hz _DT = 0.01 # 100Hz
_DT_MPC = 0.2 # 5Hz _DT_MPC = 0.2 # 5Hz
MAX_SPEED_ERROR = 2.0 MAX_SPEED_ERROR = 2.0
@ -249,8 +254,10 @@ class Planner(object):
context = zmq.Context() context = zmq.Context()
self.CP = CP self.CP = CP
self.poller = zmq.Poller() self.poller = zmq.Poller()
self.live20 = messaging.sub_sock(context, service_list['live20'].port, conflate=True, poller=self.poller) self.live20 = messaging.sub_sock(context, service_list['live20'].port, conflate=True, poller=self.poller)
self.model = messaging.sub_sock(context, service_list['model'].port, conflate=True, poller=self.poller) self.model = messaging.sub_sock(context, service_list['model'].port, conflate=True, poller=self.poller)
self.live_map_data = messaging.sub_sock(context, service_list['liveMapData'].port, conflate=True, poller=self.poller)
if os.environ.get('GPS_PLANNER_ACTIVE', False): if os.environ.get('GPS_PLANNER_ACTIVE', False):
self.gps_planner_plan = messaging.sub_sock(context, service_list['gpsPlannerPlan'].port, conflate=True, poller=self.poller, addr=GPS_PLANNER_ADDR) self.gps_planner_plan = messaging.sub_sock(context, service_list['gpsPlannerPlan'].port, conflate=True, poller=self.poller, addr=GPS_PLANNER_ADDR)
@ -293,8 +300,12 @@ class Planner(object):
self.last_gps_planner_plan = None self.last_gps_planner_plan = None
self.gps_planner_active = False self.gps_planner_active = False
self.last_live_map_data = None
self.perception_state = log.Live20Data.new_message() self.perception_state = log.Live20Data.new_message()
self.params = Params()
self.v_speedlimit = NO_CURVATURE_SPEED
def choose_solution(self, v_cruise_setpoint, enabled): def choose_solution(self, v_cruise_setpoint, enabled):
if enabled: if enabled:
solutions = {'cruise': self.v_cruise} solutions = {'cruise': self.v_cruise}
@ -327,7 +338,7 @@ class Planner(object):
self.v_acc_future = min([self.mpc1.v_mpc_future, self.mpc2.v_mpc_future, v_cruise_setpoint]) self.v_acc_future = min([self.mpc1.v_mpc_future, self.mpc2.v_mpc_future, v_cruise_setpoint])
# this runs whenever we get a packet that can change the plan # this runs whenever we get a packet that can change the plan
def update(self, CS, LaC, LoC, v_cruise_kph, force_slow_decel): def update(self, CS, CP, VM, LaC, LoC, v_cruise_kph, force_slow_decel):
cur_time = sec_since_boot() cur_time = sec_since_boot()
v_cruise_setpoint = v_cruise_kph * CV.KPH_TO_MS v_cruise_setpoint = v_cruise_kph * CV.KPH_TO_MS
@ -342,6 +353,8 @@ class Planner(object):
l20 = messaging.recv_one(socket) l20 = messaging.recv_one(socket)
elif socket is self.gps_planner_plan: elif socket is self.gps_planner_plan:
gps_planner_plan = messaging.recv_one(socket) gps_planner_plan = messaging.recv_one(socket)
elif socket is self.live_map_data:
self.last_live_map_data = messaging.recv_one(socket).liveMapData
if gps_planner_plan is not None: if gps_planner_plan is not None:
self.last_gps_planner_plan = gps_planner_plan self.last_gps_planner_plan = gps_planner_plan
@ -381,9 +394,23 @@ class Planner(object):
enabled = (LoC.long_control_state == LongCtrlState.pid) or (LoC.long_control_state == LongCtrlState.stopping) enabled = (LoC.long_control_state == LongCtrlState.pid) or (LoC.long_control_state == LongCtrlState.stopping)
following = self.lead_1.status and self.lead_1.dRel < 45.0 and self.lead_1.vLeadK > CS.vEgo and self.lead_1.aLeadK > 0.0 following = self.lead_1.status and self.lead_1.dRel < 45.0 and self.lead_1.vLeadK > CS.vEgo and self.lead_1.aLeadK > 0.0
if self.last_live_map_data:
self.v_speedlimit = NO_CURVATURE_SPEED
# Speed limit
if self.last_live_map_data.speedLimitValid:
speed_limit = self.last_live_map_data.speedLimit
set_speed_limit_active = self.params.get("LimitSetSpeed") == "1" and self.params.get("SpeedLimitOffset") is not None
if set_speed_limit_active:
offset = float(self.params.get("SpeedLimitOffset"))
self.v_speedlimit = speed_limit + offset
v_cruise_setpoint = min([v_cruise_setpoint, self.v_speedlimit])
# Calculate speed for normal cruise control # Calculate speed for normal cruise control
if enabled: if enabled:
accel_limits = map(float, calc_cruise_accel_limits(CS.vEgo, following)) accel_limits = map(float, calc_cruise_accel_limits(CS.vEgo, following))
# TODO: make a separate lookup for jerk tuning # TODO: make a separate lookup for jerk tuning
jerk_limits = [min(-0.1, accel_limits[0]), max(0.1, accel_limits[1])] jerk_limits = [min(-0.1, accel_limits[0]), max(0.1, accel_limits[1])]

@ -6,13 +6,13 @@ import copy
import json import json
import numpy as np import numpy as np
import selfdrive.messaging as messaging import selfdrive.messaging as messaging
from selfdrive.locationd.calibration_values import Calibration, Filter from selfdrive.locationd.calibration_helpers import Calibration, Filter
from selfdrive.swaglog import cloudlog from selfdrive.swaglog import cloudlog
from selfdrive.services import service_list from selfdrive.services import service_list
from common.params import Params from common.params import Params
from common.ffi_wrapper import ffi_wrap from common.ffi_wrapper import ffi_wrap
import common.transformations.orientation as orient import common.transformations.orientation as orient
from common.transformations.model import model_height, get_camera_frame_from_model_frame from common.transformations.model import model_height, get_camera_frame_from_model_frame, get_camera_frame_from_bigmodel_frame
from common.transformations.camera import view_frame_from_device_frame, get_view_frame_from_road_frame, \ from common.transformations.camera import view_frame_from_device_frame, get_view_frame_from_road_frame, \
eon_intrinsics, get_calib_from_vp, normalize, denormalize, H, W eon_intrinsics, get_calib_from_vp, normalize, denormalize, H, W
@ -208,13 +208,15 @@ class Calibrator(object):
calib = get_calib_from_vp(self.vp) calib = get_calib_from_vp(self.vp)
extrinsic_matrix = get_view_frame_from_road_frame(0, calib[1], calib[2], model_height) extrinsic_matrix = get_view_frame_from_road_frame(0, calib[1], calib[2], model_height)
ke = eon_intrinsics.dot(extrinsic_matrix) ke = eon_intrinsics.dot(extrinsic_matrix)
warp_matrix = get_camera_frame_from_model_frame(ke, model_height) warp_matrix = get_camera_frame_from_model_frame(ke)
warp_matrix_big = get_camera_frame_from_bigmodel_frame(ke)
cal_send = messaging.new_message() cal_send = messaging.new_message()
cal_send.init('liveCalibration') cal_send.init('liveCalibration')
cal_send.liveCalibration.calStatus = self.cal_status cal_send.liveCalibration.calStatus = self.cal_status
cal_send.liveCalibration.calPerc = min(self.frame_counter * 100 / CALIBRATION_CYCLES_NEEDED, 100) cal_send.liveCalibration.calPerc = min(self.frame_counter * 100 / CALIBRATION_CYCLES_NEEDED, 100)
cal_send.liveCalibration.warpMatrix2 = map(float, warp_matrix.flatten()) cal_send.liveCalibration.warpMatrix2 = map(float, warp_matrix.flatten())
cal_send.liveCalibration.warpMatrixBig = map(float, warp_matrix_big.flatten())
cal_send.liveCalibration.extrinsicMatrix = map(float, extrinsic_matrix.flatten()) cal_send.liveCalibration.extrinsicMatrix = map(float, extrinsic_matrix.flatten())
livecalibration.send(cal_send.to_bytes()) livecalibration.send(cal_send.to_bytes())

Binary file not shown.

@ -42,7 +42,7 @@ def unblock_stdout():
if __name__ == "__main__": if __name__ == "__main__":
if os.path.isfile("/init.qcom.rc") \ if os.path.isfile("/init.qcom.rc") \
and (not os.path.isfile("/VERSION") or int(open("/VERSION").read()) < 6): and (not os.path.isfile("/VERSION") or int(open("/VERSION").read()) < 8):
# update continue.sh before updating NEOS # update continue.sh before updating NEOS
if os.path.isfile(os.path.join(BASEDIR, "scripts", "continue.sh")): if os.path.isfile(os.path.join(BASEDIR, "scripts", "continue.sh")):
@ -88,6 +88,7 @@ managed_processes = {
"controlsd": "selfdrive.controls.controlsd", "controlsd": "selfdrive.controls.controlsd",
"radard": "selfdrive.controls.radard", "radard": "selfdrive.controls.radard",
"ubloxd": "selfdrive.locationd.ubloxd", "ubloxd": "selfdrive.locationd.ubloxd",
"mapd": "selfdrive.mapd.mapd",
"loggerd": ("selfdrive/loggerd", ["./loggerd"]), "loggerd": ("selfdrive/loggerd", ["./loggerd"]),
"logmessaged": "selfdrive.logmessaged", "logmessaged": "selfdrive.logmessaged",
"tombstoned": "selfdrive.tombstoned", "tombstoned": "selfdrive.tombstoned",
@ -135,7 +136,8 @@ car_started_processes = [
'visiond', 'visiond',
'proclogd', 'proclogd',
'ubloxd', 'ubloxd',
'orbd' 'orbd',
'mapd',
] ]
def register_managed_process(name, desc, car_started=False): def register_managed_process(name, desc, car_started=False):
@ -474,6 +476,12 @@ def main():
params.put("IsDriverMonitoringEnabled", "1") params.put("IsDriverMonitoringEnabled", "1")
if params.get("IsGeofenceEnabled") is None: if params.get("IsGeofenceEnabled") is None:
params.put("IsGeofenceEnabled", "-1") params.put("IsGeofenceEnabled", "-1")
if params.get("SpeedLimitOffset") is None:
params.put("SpeedLimitOffset", "0")
if params.get("LongitudinalControl") is None:
params.put("LongitudinalControl", "0")
if params.get("LimitSetSpeed") is None:
params.put("LimitSetSpeed", "0")
# is this chffrplus? # is this chffrplus?
if os.getenv("PASSIVE") is not None: if os.getenv("PASSIVE") is not None:

@ -0,0 +1,240 @@
#!/usr/bin/env python
# Add phonelibs openblas to LD_LIBRARY_PATH if import fails
try:
from scipy import spatial
except ImportError as e:
import os
import sys
from common.basedir import BASEDIR
openblas_path = os.path.join(BASEDIR, "phonelibs/openblas/")
os.environ['LD_LIBRARY_PATH'] += ':' + openblas_path
args = [sys.executable]
args.extend(sys.argv)
os.execv(sys.executable, args)
import time
import zmq
import threading
import numpy as np
import overpy
from collections import defaultdict
from common.transformations.coordinates import geodetic2ecef
from selfdrive.services import service_list
import selfdrive.messaging as messaging
from mapd_helpers import LOOKAHEAD_TIME, MAPS_LOOKAHEAD_DISTANCE, Way, circle_through_points
OVERPASS_API_URL = "https://overpass.kumi.systems/api/interpreter"
OVERPASS_HEADERS = {
'User-Agent': 'NEOS (comma.ai)'
}
last_gps = None
query_lock = threading.Lock()
last_query_result = None
last_query_pos = None
def build_way_query(lat, lon, radius=50):
"""Builds a query to find all highways within a given radius around a point"""
pos = " (around:%f,%f,%f)" % (radius, lat, lon)
q = """(
way
""" + pos + """
[highway];
>;);out;
"""
return q
def query_thread():
global last_query_result, last_query_pos
api = overpy.Overpass(url=OVERPASS_API_URL, headers=OVERPASS_HEADERS, timeout=10.)
while True:
if last_gps is not None:
fix_ok = last_gps.flags & 1
if not fix_ok:
continue
if last_query_pos is not None:
cur_ecef = geodetic2ecef((last_gps.latitude, last_gps.longitude, last_gps.altitude))
prev_ecef = geodetic2ecef((last_query_pos.latitude, last_query_pos.longitude, last_query_pos.altitude))
dist = np.linalg.norm(cur_ecef - prev_ecef)
if dist < 1000:
continue
q = build_way_query(last_gps.latitude, last_gps.longitude, radius=2000)
try:
new_result = api.query(q)
# Build kd-tree
nodes = []
real_nodes = []
node_to_way = defaultdict(list)
for n in new_result.nodes:
nodes.append((float(n.lat), float(n.lon), 0))
real_nodes.append(n)
for way in new_result.ways:
for n in way.nodes:
node_to_way[n.id].append(way)
nodes = np.asarray(nodes)
nodes = geodetic2ecef(nodes)
tree = spatial.cKDTree(nodes)
query_lock.acquire()
last_query_result = new_result, tree, real_nodes, node_to_way
last_query_pos = last_gps
query_lock.release()
except Exception as e:
print e
query_lock.acquire()
last_query_result = None
query_lock.release()
time.sleep(1)
def mapsd_thread():
global last_gps
context = zmq.Context()
gps_sock = messaging.sub_sock(context, service_list['gpsLocation'].port, conflate=True)
gps_external_sock = messaging.sub_sock(context, service_list['gpsLocationExternal'].port, conflate=True)
map_data_sock = messaging.pub_sock(context, service_list['liveMapData'].port)
cur_way = None
curvature_valid = False
curvature = None
upcoming_curvature = 0.
dist_to_turn = 0.
road_points = None
xx = np.arange(0, MAPS_LOOKAHEAD_DISTANCE, 10)
while True:
gps = messaging.recv_one(gps_sock)
gps_ext = messaging.recv_one_or_none(gps_external_sock)
if gps_ext is not None:
gps = gps_ext.gpsLocationExternal
else:
gps = gps.gpsLocation
last_gps = gps
fix_ok = gps.flags & 1
if not fix_ok or last_query_result is None:
cur_way = None
curvature = None
curvature_valid = False
upcoming_curvature = 0.
dist_to_turn = 0.
road_points = None
else:
lat = gps.latitude
lon = gps.longitude
heading = gps.bearing
speed = gps.speed
query_lock.acquire()
cur_way = Way.closest(last_query_result, lat, lon, heading)
if cur_way is not None:
pnts, curvature_valid = cur_way.get_lookahead(last_query_result, lat, lon, heading, MAPS_LOOKAHEAD_DISTANCE)
xs = pnts[:, 0]
ys = pnts[:, 1]
road_points = map(float, xs), map(float, ys)
if speed < 10:
curvature_valid = False
# The curvature is valid when at least MAPS_LOOKAHEAD_DISTANCE of road is found
if curvature_valid:
# Compute the curvature for each point
with np.errstate(divide='ignore'):
circles = [circle_through_points(*p) for p in zip(pnts, pnts[1:], pnts[2:])]
circles = np.asarray(circles)
radii = np.nan_to_num(circles[:, 2])
radii[radii < 10] = np.inf
curvature = 1. / radii
# Index of closest point
closest = np.argmin(np.linalg.norm(pnts, axis=1))
dist_to_closest = pnts[closest, 0] # We can use x distance here since it should be close
# Compute distance along path
dists = list()
dists.append(0)
for p, p_prev in zip(pnts, pnts[1:, :]):
dists.append(dists[-1] + np.linalg.norm(p - p_prev))
dists = np.asarray(dists)
dists = dists - dists[closest] + dist_to_closest
# TODO: Determine left or right turn
curvature = np.nan_to_num(curvature)
curvature_interp = np.interp(xx, dists[1:-1], curvature)
curvature_lookahead = curvature_interp[:int(speed * LOOKAHEAD_TIME / 10)]
# Outlier rejection
new_curvature = np.percentile(curvature_lookahead, 90)
k = 0.9
upcoming_curvature = k * upcoming_curvature + (1 - k) * new_curvature
in_turn_indices = curvature_interp > 0.8 * new_curvature
if np.any(in_turn_indices):
dist_to_turn = np.min(xx[in_turn_indices])
else:
dist_to_turn = 999
query_lock.release()
dat = messaging.new_message()
dat.init('liveMapData')
if last_gps is not None:
dat.liveMapData.lastGps = last_gps
if cur_way is not None:
dat.liveMapData.wayId = cur_way.id
# Seed limit
max_speed = cur_way.max_speed
if max_speed is not None:
dat.liveMapData.speedLimitValid = True
dat.liveMapData.speedLimit = max_speed
# Curvature
dat.liveMapData.curvatureValid = curvature_valid
dat.liveMapData.curvature = float(upcoming_curvature)
dat.liveMapData.distToTurn = float(dist_to_turn)
if road_points is not None:
dat.liveMapData.roadX, dat.liveMapData.roadY = road_points
if curvature is not None:
dat.liveMapData.roadCurvatureX = map(float, xx)
dat.liveMapData.roadCurvature = map(float, curvature_interp)
map_data_sock.send(dat.to_bytes())
def main(gctx=None):
main_thread = threading.Thread(target=mapsd_thread)
main_thread.daemon = True
main_thread.start()
q_thread = threading.Thread(target=query_thread)
q_thread.daemon = True
q_thread.start()
while True:
time.sleep(0.1)
if __name__ == "__main__":
main()

@ -0,0 +1,223 @@
import math
import numpy as np
from datetime import datetime
from selfdrive.config import Conversions as CV
from common.transformations.coordinates import LocalCoord, geodetic2ecef
LOOKAHEAD_TIME = 10.
MAPS_LOOKAHEAD_DISTANCE = 50 * LOOKAHEAD_TIME
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. """
conversion = CV.KPH_TO_MS
if 'mph' in max_speed:
max_speed = max_speed.replace(' mph', '')
conversion = CV.MPH_TO_MS
return float(max_speed) * conversion
class Way:
def __init__(self, way):
self.id = way.id
self.way = way
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):
results, tree, real_nodes, node_to_way = 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)
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)
if closest_way is None or score < best_score:
closest_way = way
best_score = score
return closest_way
def __str__(self):
return "%s %s" % (self.id, self.way.tags)
@property
def max_speed(self):
"""Extracts the (conditional) speed limit from a way"""
if not self.way:
return None
tags = self.way.tags
max_speed = None
if 'maxspeed' in tags:
max_speed = parse_speed_unit(tags['maxspeed'])
if 'maxspeed:conditional' in tags:
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 = parse_speed_unit(max_speed_cond)
return max_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, query_results, lat, lon, heading, backwards=False):
results, tree, real_nodes, node_to_way = 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 and w.tags['highway'] == self.way.tags['highway']]
if len(ways) == 1:
way = Way(ways[0])
except KeyError:
pass
return way
def get_lookahead(self, query_results, 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(query_results, lat, lon, heading)
if not way:
break
return pnts, valid

Binary file not shown.

Binary file not shown.

@ -84,6 +84,7 @@ _FAN_SPEEDS = [0, 16384, 32768, 65535]
# max fan speed only allowed if battery is hot # max fan speed only allowed if battery is hot
_BAT_TEMP_THERSHOLD = 45. _BAT_TEMP_THERSHOLD = 45.
def handle_fan(max_cpu_temp, bat_temp, fan_speed): def handle_fan(max_cpu_temp, bat_temp, fan_speed):
new_speed_h = next(speed for speed, temp_h in zip(_FAN_SPEEDS, _TEMP_THRS_H) if temp_h > max_cpu_temp) new_speed_h = next(speed for speed, temp_h in zip(_FAN_SPEEDS, _TEMP_THRS_H) if temp_h > max_cpu_temp)
new_speed_l = next(speed for speed, temp_l in zip(_FAN_SPEEDS, _TEMP_THRS_L) if temp_l > max_cpu_temp) new_speed_l = next(speed for speed, temp_l in zip(_FAN_SPEEDS, _TEMP_THRS_L) if temp_l > max_cpu_temp)
@ -103,6 +104,23 @@ def handle_fan(max_cpu_temp, bat_temp, fan_speed):
return fan_speed return fan_speed
def check_car_battery_voltage(should_start, health, charging_disabled):
# charging disallowed if:
# - there are health packets from panda, and;
# - 12V battery voltage is too low, and;
# - onroad isn't started
if charging_disabled and (health is None or health.health.voltage > 11500):
charging_disabled = False
os.system('echo "1" > /sys/class/power_supply/battery/charging_enabled')
elif not charging_disabled and health is not None and health.health.voltage < 11000 and not should_start:
charging_disabled = True
os.system('echo "0" > /sys/class/power_supply/battery/charging_enabled')
return charging_disabled
class LocationStarter(object): class LocationStarter(object):
def __init__(self): def __init__(self):
self.last_good_loc = 0 self.last_good_loc = 0
@ -133,6 +151,7 @@ class LocationStarter(object):
cloudlog.event("location_start", location=location.to_dict() if location else None) cloudlog.event("location_start", location=location.to_dict() if location else None)
return location.speed*3.6 > 10 return location.speed*3.6 > 10
def thermald_thread(): def thermald_thread():
setup_eon_fan() setup_eon_fan()
@ -156,6 +175,10 @@ def thermald_thread():
health_sock.RCVTIMEO = 1500 health_sock.RCVTIMEO = 1500
current_filter = FirstOrderFilter(0., CURRENT_TAU, 1.) current_filter = FirstOrderFilter(0., CURRENT_TAU, 1.)
# Make sure charging is enabled
charging_disabled = False
os.system('echo "1" > /sys/class/power_supply/battery/charging_enabled')
params = Params() params = Params()
while 1: while 1:
@ -182,7 +205,6 @@ def thermald_thread():
msg.thermal.usbOnline = bool(int(f.read())) msg.thermal.usbOnline = bool(int(f.read()))
current_filter.update(msg.thermal.batteryCurrent / 1e6) current_filter.update(msg.thermal.batteryCurrent / 1e6)
msg.thermal.chargerDisabled = current_filter.x > 1.0 # if current is ? 1A out, then charger might be off
# TODO: add car battery voltage check # TODO: add car battery voltage check
max_cpu_temp = max(msg.thermal.cpu0, msg.thermal.cpu1, max_cpu_temp = max(msg.thermal.cpu0, msg.thermal.cpu1,
@ -268,6 +290,10 @@ def thermald_thread():
started_seen and (sec_since_boot() - off_ts) > 60: started_seen and (sec_since_boot() - off_ts) > 60:
os.system('LD_LIBRARY_PATH="" svc power shutdown') os.system('LD_LIBRARY_PATH="" svc power shutdown')
charging_disabled = check_car_battery_voltage(should_start, health, charging_disabled)
msg.thermal.chargingDisabled = charging_disabled
msg.thermal.chargingError = current_filter.x > 1.0 # if current is > 1A out, then charger might be off
msg.thermal.started = started_ts is not None msg.thermal.started = started_ts is not None
msg.thermal.startedTs = int(1e9*(started_ts or 0)) msg.thermal.startedTs = int(1e9*(started_ts or 0))

@ -87,6 +87,8 @@ const int alert_sizes[] = {
[ALERTSIZE_FULL] = vwp_h, [ALERTSIZE_FULL] = vwp_h,
}; };
const int SET_SPEED_NA = 255;
// TODO: this is also hardcoded in common/transformations/camera.py // TODO: this is also hardcoded in common/transformations/camera.py
const mat3 intrinsic_matrix = (mat3){{ const mat3 intrinsic_matrix = (mat3){{
910., 0., 582., 910., 0., 582.,
@ -224,9 +226,13 @@ typedef struct UIState {
int awake_timeout; int awake_timeout;
int volume_timeout; int volume_timeout;
int speed_lim_off_timeout;
int is_metric_timeout;
int status; int status;
bool is_metric; bool is_metric;
float speed_lim_off;
bool is_ego_over_limit;
bool passive; bool passive;
char alert_type[64]; char alert_type[64];
char alert_sound[64]; char alert_sound[64];
@ -282,6 +288,26 @@ static void set_do_exit(int sig) {
do_exit = 1; do_exit = 1;
} }
static void read_speed_lim_off(UIState *s) {
char *speed_lim_off = NULL;
read_db_value(NULL, "SpeedLimitOffset", &speed_lim_off, NULL);
s->speed_lim_off = 0.;
if (speed_lim_off) {
s->speed_lim_off = strtod(speed_lim_off, NULL);
free(speed_lim_off);
}
s->speed_lim_off_timeout = 2 * 60; // 2Hz
}
static void read_is_metric(UIState *s) {
char *is_metric;
const int result = read_db_value(NULL, "IsMetric", &is_metric, NULL);
if (result == 0) {
s->is_metric = is_metric[0] == '1';
free(is_metric);
}
s->is_metric_timeout = 2 * 60; // 2Hz
}
static const char frame_vertex_shader[] = static const char frame_vertex_shader[] =
"attribute vec4 aPosition;\n" "attribute vec4 aPosition;\n"
@ -530,12 +556,9 @@ static void ui_init_vision(UIState *s, const VisionStreamBufs back_bufs,
0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0,
}}; }};
char *value; read_speed_lim_off(s);
const int result = read_db_value(NULL, "IsMetric", &value, NULL); read_is_metric(s);
if (result == 0) { s->is_metric_timeout = 60; // offset so values isn't read together with limit offset
s->is_metric = value[0] == '1';
free(value);
}
} }
static void ui_draw_transformed_box(UIState *s, uint32_t color) { static void ui_draw_transformed_box(UIState *s, uint32_t color) {
@ -915,40 +938,80 @@ static void ui_draw_vision_maxspeed(UIState *s) {
const UIScene *scene = &s->scene; const UIScene *scene = &s->scene;
int ui_viz_rx = scene->ui_viz_rx; int ui_viz_rx = scene->ui_viz_rx;
int ui_viz_rw = scene->ui_viz_rw; int ui_viz_rw = scene->ui_viz_rw;
float maxspeed = s->scene.v_cruise;
const int viz_maxspeed_x = (ui_viz_rx + (bdr_s*2));
const int viz_maxspeed_y = (box_y + (bdr_s*1.5));
const int viz_maxspeed_w = 180;
const int viz_maxspeed_h = 202;
char maxspeed_str[32]; char maxspeed_str[32];
bool is_cruise_set = (maxspeed != 0 && maxspeed != 255); float maxspeed = s->scene.v_cruise;
int maxspeed_calc = maxspeed * 0.6225 + 0.5;
float speedlimit = s->scene.speedlimit;
int speedlim_calc = speedlimit * 2.2369363 + 0.5;
int speed_lim_off = s->speed_lim_off * 2.2369363 + 0.5;
if (s->is_metric) {
maxspeed_calc = maxspeed + 0.5;
speedlim_calc = speedlimit * 3.6 + 0.5;
speed_lim_off = s->speed_lim_off * 3.6 + 0.5;
}
bool is_cruise_set = (maxspeed != 0 && maxspeed != SET_SPEED_NA);
bool is_speedlim_valid = s->scene.speedlimit_valid;
bool is_set_over_limit = is_speedlim_valid && s->scene.engaged &&
is_cruise_set && maxspeed_calc > (speedlim_calc + speed_lim_off);
int viz_maxspeed_w = 184;
int viz_maxspeed_h = 202;
int viz_maxspeed_x = (ui_viz_rx + (bdr_s*2));
int viz_maxspeed_y = (box_y + (bdr_s*1.5));
int viz_maxspeed_xo = 180;
viz_maxspeed_w += viz_maxspeed_xo;
viz_maxspeed_x += viz_maxspeed_w - (viz_maxspeed_xo * 2);
// Draw Background
nvgBeginPath(s->vg);
nvgRoundedRect(s->vg, viz_maxspeed_x, viz_maxspeed_y, viz_maxspeed_w, viz_maxspeed_h, 30);
if (is_set_over_limit) {
nvgFillColor(s->vg, nvgRGBA(218, 111, 37, 180));
} else {
nvgFillColor(s->vg, nvgRGBA(0, 0, 0, 100));
}
nvgFill(s->vg);
// Draw Border
nvgBeginPath(s->vg); nvgBeginPath(s->vg);
nvgRoundedRect(s->vg, viz_maxspeed_x, viz_maxspeed_y, viz_maxspeed_w, viz_maxspeed_h, 20); nvgRoundedRect(s->vg, viz_maxspeed_x, viz_maxspeed_y, viz_maxspeed_w, viz_maxspeed_h, 20);
nvgStrokeColor(s->vg, nvgRGBA(255,255,255,80)); if (is_set_over_limit) {
nvgStrokeWidth(s->vg, 6); nvgStrokeColor(s->vg, nvgRGBA(218, 111, 37, 255));
} else if (is_speedlim_valid && !s->is_ego_over_limit) {
nvgStrokeColor(s->vg, nvgRGBA(255, 255, 255, 255));
} else if (is_speedlim_valid && s->is_ego_over_limit) {
nvgStrokeColor(s->vg, nvgRGBA(255, 255, 255, 20));
} else {
nvgStrokeColor(s->vg, nvgRGBA(255, 255, 255, 100));
}
nvgStrokeWidth(s->vg, 10);
nvgStroke(s->vg); nvgStroke(s->vg);
// Draw "MAX" Text
nvgTextAlign(s->vg, NVG_ALIGN_CENTER | NVG_ALIGN_BASELINE); nvgTextAlign(s->vg, NVG_ALIGN_CENTER | NVG_ALIGN_BASELINE);
nvgFontFace(s->vg, "sans-regular"); nvgFontFace(s->vg, "sans-regular");
nvgFontSize(s->vg, 26*2.5); nvgFontSize(s->vg, 26*2.5);
nvgFillColor(s->vg, nvgRGBA(255, 255, 255, 200)); if (is_cruise_set) {
nvgText(s->vg, viz_maxspeed_x+viz_maxspeed_w/2, 148, "MAX", NULL); nvgFillColor(s->vg, nvgRGBA(255, 255, 255, 200));
} else {
nvgFillColor(s->vg, nvgRGBA(255, 255, 255, 100));
}
nvgText(s->vg, viz_maxspeed_x+(viz_maxspeed_xo/2)+(viz_maxspeed_w/2), 148, "MAX", NULL);
nvgFontFace(s->vg, "sans-semibold"); // Draw Speed Text
nvgFontSize(s->vg, 52*2.5); nvgFontFace(s->vg, "sans-bold");
nvgFillColor(s->vg, nvgRGBA(255, 255, 255, 255)); nvgFontSize(s->vg, 48*2.5);
if (is_cruise_set) { if (is_cruise_set) {
if (s->is_metric) { snprintf(maxspeed_str, sizeof(maxspeed_str), "%d", maxspeed_calc);
snprintf(maxspeed_str, sizeof(maxspeed_str), "%d", (int)(maxspeed + 0.5)); nvgFillColor(s->vg, nvgRGBA(255, 255, 255, 255));
} else { nvgText(s->vg, viz_maxspeed_x+(viz_maxspeed_xo/2)+(viz_maxspeed_w/2), 242, maxspeed_str, NULL);
snprintf(maxspeed_str, sizeof(maxspeed_str), "%d", (int)(maxspeed * 0.6225 + 0.5));
}
nvgText(s->vg, viz_maxspeed_x+viz_maxspeed_w/2, 242, maxspeed_str, NULL);
} else { } else {
nvgFontFace(s->vg, "sans-semibold");
nvgFontSize(s->vg, 42*2.5); nvgFontSize(s->vg, 42*2.5);
nvgText(s->vg, viz_maxspeed_x+viz_maxspeed_w/2, 242, "N/A", NULL); nvgFillColor(s->vg, nvgRGBA(255, 255, 255, 100));
nvgText(s->vg, viz_maxspeed_x+(viz_maxspeed_xo/2)+(viz_maxspeed_w/2), 242, "N/A", NULL);
} }
} }
@ -957,69 +1020,84 @@ static void ui_draw_vision_speedlimit(UIState *s) {
int ui_viz_rx = scene->ui_viz_rx; int ui_viz_rx = scene->ui_viz_rx;
int ui_viz_rw = scene->ui_viz_rw; int ui_viz_rw = scene->ui_viz_rw;
if (!s->scene.speedlimit_valid){ char speedlim_str[32];
return;
}
float speedlimit = s->scene.speedlimit; float speedlimit = s->scene.speedlimit;
int speedlim_calc = speedlimit * 2.2369363 + 0.5;
if (s->is_metric) {
speedlim_calc = speedlimit * 3.6 + 0.5;
}
const int viz_maxspeed_w = 180; bool is_speedlim_valid = s->scene.speedlimit_valid;
const int viz_maxspeed_h = 202; float hysteresis_offset = 0.5;
if (s->is_ego_over_limit) {
const int viz_event_w = 220; hysteresis_offset = 0.0;
const int viz_event_x = ((ui_viz_rx + ui_viz_rw) - (viz_event_w + (bdr_s*2))); }
s->is_ego_over_limit = is_speedlim_valid && s->scene.v_ego > (speedlimit + s->speed_lim_off + hysteresis_offset);
const int viz_maxspeed_x = viz_event_x + (viz_event_w-viz_maxspeed_w);
const int viz_maxspeed_y = (footer_y + ((footer_h - viz_maxspeed_h) / 2)) - 20; int viz_speedlim_w = 180;
int viz_speedlim_h = 202;
int viz_speedlim_x = (ui_viz_rx + (bdr_s*2));
int viz_speedlim_y = (box_y + (bdr_s*1.5));
if (!is_speedlim_valid) {
viz_speedlim_w -= 5;
viz_speedlim_h -= 10;
viz_speedlim_x += 9;
viz_speedlim_y += 5;
}
int viz_speedlim_bdr = is_speedlim_valid ? 30 : 15;
char maxspeed_str[32]; // Draw Background
nvgBeginPath(s->vg);
nvgRoundedRect(s->vg, viz_speedlim_x, viz_speedlim_y, viz_speedlim_w, viz_speedlim_h, viz_speedlim_bdr);
if (is_speedlim_valid && s->is_ego_over_limit) {
nvgFillColor(s->vg, nvgRGBA(218, 111, 37, 180));
} else if (is_speedlim_valid) {
nvgFillColor(s->vg, nvgRGBA(255, 255, 255, 255));
} else {
nvgFillColor(s->vg, nvgRGBA(255, 255, 255, 100));
}
nvgFill(s->vg);
if (s->is_metric) { // Draw Border
if (is_speedlim_valid) {
nvgStrokeWidth(s->vg, 10);
nvgStroke(s->vg);
nvgBeginPath(s->vg); nvgBeginPath(s->vg);
nvgCircle(s->vg, viz_maxspeed_x + viz_maxspeed_w / 2, viz_maxspeed_y + viz_maxspeed_h / 2, 127); nvgRoundedRect(s->vg, viz_speedlim_x, viz_speedlim_y, viz_speedlim_w, viz_speedlim_h, 20);
nvgFillColor(s->vg, nvgRGBA(195, 0, 0, 255)); if (s->is_ego_over_limit) {
nvgFill(s->vg); nvgStrokeColor(s->vg, nvgRGBA(218, 111, 37, 255));
} else if (is_speedlim_valid) {
nvgStrokeColor(s->vg, nvgRGBA(255, 255, 255, 255));
}
}
nvgBeginPath(s->vg); // Draw "Speed Limit" Text
nvgCircle(s->vg, viz_maxspeed_x + viz_maxspeed_w / 2, viz_maxspeed_y + viz_maxspeed_h / 2, 100); nvgTextAlign(s->vg, NVG_ALIGN_CENTER | NVG_ALIGN_BASELINE);
nvgFontFace(s->vg, "sans-semibold");
nvgFontSize(s->vg, 50);
nvgFillColor(s->vg, nvgRGBA(0, 0, 0, 255));
if (is_speedlim_valid && s->is_ego_over_limit) {
nvgFillColor(s->vg, nvgRGBA(255, 255, 255, 255)); nvgFillColor(s->vg, nvgRGBA(255, 255, 255, 255));
nvgFill(s->vg); }
nvgText(s->vg, viz_speedlim_x+viz_speedlim_w/2 + (is_speedlim_valid ? 6 : 0), viz_speedlim_y + (is_speedlim_valid ? 50 : 45), "SPEED", NULL);
nvgText(s->vg, viz_speedlim_x+viz_speedlim_w/2 + (is_speedlim_valid ? 6 : 0), viz_speedlim_y + (is_speedlim_valid ? 90 : 85), "LIMIT", NULL);
nvgTextAlign(s->vg, NVG_ALIGN_CENTER | NVG_ALIGN_BASELINE); // Draw Speed Text
nvgFontFace(s->vg, "sans-bold"); nvgFontFace(s->vg, "sans-bold");
nvgFontSize(s->vg, 130); nvgFontSize(s->vg, 48*2.5);
if (s->is_ego_over_limit) {
nvgFillColor(s->vg, nvgRGBA(255, 255, 255, 255));
} else {
nvgFillColor(s->vg, nvgRGBA(0, 0, 0, 255)); nvgFillColor(s->vg, nvgRGBA(0, 0, 0, 255));
}
snprintf(maxspeed_str, sizeof(maxspeed_str), "%d", (int)(speedlimit * 3.6 + 0.5)); if (is_speedlim_valid) {
nvgText(s->vg, viz_maxspeed_x+viz_maxspeed_w/2, viz_maxspeed_y + 135, maxspeed_str, NULL); snprintf(speedlim_str, sizeof(speedlim_str), "%d", speedlim_calc);
nvgText(s->vg, viz_speedlim_x+viz_speedlim_w/2, viz_speedlim_y + (is_speedlim_valid ? 170 : 165), speedlim_str, NULL);
} else { } else {
const int border = 10;
nvgBeginPath(s->vg);
nvgRoundedRect(s->vg, viz_maxspeed_x - border, viz_maxspeed_y - border, viz_maxspeed_w + 2 * border, viz_maxspeed_h + 2 * border, 30);
nvgFillColor(s->vg, nvgRGBA(255, 255, 255, 255));
nvgFill(s->vg);
nvgBeginPath(s->vg);
nvgRoundedRect(s->vg, viz_maxspeed_x, viz_maxspeed_y, viz_maxspeed_w, viz_maxspeed_h, 20);
nvgStrokeColor(s->vg, nvgRGBA(0, 0, 0, 255));
nvgStrokeWidth(s->vg, 8);
nvgStroke(s->vg);
nvgTextAlign(s->vg, NVG_ALIGN_CENTER | NVG_ALIGN_BASELINE);
nvgFontFace(s->vg, "sans-semibold"); nvgFontFace(s->vg, "sans-semibold");
nvgFontSize(s->vg, 50); nvgFontSize(s->vg, 42*2.5);
nvgFillColor(s->vg, nvgRGBA(0, 0, 0, 255)); nvgText(s->vg, viz_speedlim_x+viz_speedlim_w/2, viz_speedlim_y + (is_speedlim_valid ? 170 : 165), "N/A", NULL);
nvgText(s->vg, viz_maxspeed_x+viz_maxspeed_w/2, viz_maxspeed_y + 50, "SPEED", NULL); }
nvgText(s->vg, viz_maxspeed_x+viz_maxspeed_w/2, viz_maxspeed_y + 90, "LIMIT", NULL);
nvgFontFace(s->vg, "sans-bold");
nvgFontSize(s->vg, 120);
nvgFillColor(s->vg, nvgRGBA(0, 0, 0, 255));
snprintf(maxspeed_str, sizeof(maxspeed_str), "%d", (int)(speedlimit * 2.2369363 + 0.5));
nvgText(s->vg, viz_maxspeed_x+viz_maxspeed_w/2, viz_maxspeed_y + 170, maxspeed_str, NULL);
}
} }
static void ui_draw_vision_speed(UIState *s) { static void ui_draw_vision_speed(UIState *s) {
@ -1137,6 +1215,7 @@ static void ui_draw_vision_header(UIState *s) {
nvgFill(s->vg); nvgFill(s->vg);
ui_draw_vision_maxspeed(s); ui_draw_vision_maxspeed(s);
ui_draw_vision_speedlimit(s);
ui_draw_vision_speed(s); ui_draw_vision_speed(s);
ui_draw_vision_wheel(s); ui_draw_vision_wheel(s);
} }
@ -1151,8 +1230,6 @@ static void ui_draw_vision_footer(UIState *s) {
// Driver Monitoring // Driver Monitoring
ui_draw_vision_face(s); ui_draw_vision_face(s);
ui_draw_vision_speedlimit(s);
} }
static void ui_draw_vision_alert(UIState *s, int va_size, int va_color, static void ui_draw_vision_alert(UIState *s, int va_size, int va_color,
@ -1710,7 +1787,7 @@ static void ui_update(UIState *s) {
struct cereal_LiveMapData datad; struct cereal_LiveMapData datad;
cereal_read_LiveMapData(&datad, eventd.liveMapData); cereal_read_LiveMapData(&datad, eventd.liveMapData);
s->scene.speedlimit = datad.speedLimit; s->scene.speedlimit = datad.speedLimit;
s->scene.speedlimit_valid = datad.valid; s->scene.speedlimit_valid = datad.speedLimitValid;
} }
capn_free(&ctx); capn_free(&ctx);
zmq_msg_close(&msg); zmq_msg_close(&msg);
@ -1975,6 +2052,18 @@ int main() {
set_volume(s, volume); set_volume(s, volume);
} }
if (s->speed_lim_off_timeout > 0) {
s->speed_lim_off_timeout--;
} else {
read_speed_lim_off(s);
}
if (s->is_metric_timeout > 0) {
s->is_metric_timeout--;
} else {
read_is_metric(s);
}
pthread_mutex_unlock(&s->lock); pthread_mutex_unlock(&s->lock);
// the bg thread needs to be scheduled, so the main thread needs time without the lock // the bg thread needs to be scheduled, so the main thread needs time without the lock

Binary file not shown.
Loading…
Cancel
Save