openpilot v0.5.12 release

old-commit-hash: 3f9059fea8
commatwo_master v0.5.12
Vehicle Researcher 6 years ago
parent d34a9b934c
commit 0932b367bd
  1. 5
      .travis.yml
  2. 15
      CONTRIBUTING.md
  3. 9
      Dockerfile.openpilot
  4. 6
      README.md
  5. 14
      RELEASES.md
  6. 4
      apk/ai.comma.plus.frame.apk
  7. 6
      cereal/Makefile
  8. 143
      cereal/car.capnp
  9. 39
      cereal/log.capnp
  10. 11
      check_code_quality.sh
  11. 10
      common/kalman/Makefile
  12. 29
      common/kalman/simple_kalman.py
  13. 16
      common/kalman/simple_kalman_impl.pxd
  14. 35
      common/kalman/simple_kalman_impl.pyx
  15. 23
      common/kalman/simple_kalman_old.py
  16. 5
      common/kalman/simple_kalman_setup.py
  17. 0
      common/kalman/tests/__init__.py
  18. 116
      common/kalman/tests/test_ekf.py
  19. 85
      common/kalman/tests/test_simple_kalman.py
  20. 28
      common/timeout.py
  21. 11
      common/transformations/camera.py
  22. 13
      common/transformations/model.py
  23. 3
      phonelibs/bzip2/build.txt
  24. 3
      phonelibs/bzip2/bzlib.h
  25. 3
      phonelibs/bzip2/libbz2.a
  26. 3
      phonelibs/install_capnp.sh
  27. 3
      phonelibs/zmq/x64/include/czmq.h
  28. 3
      phonelibs/zmq/x64/include/czmq_library.h
  29. 3
      phonelibs/zmq/x64/include/czmq_prelude.h
  30. 3
      phonelibs/zmq/x64/include/zactor.h
  31. 3
      phonelibs/zmq/x64/include/zarmour.h
  32. 3
      phonelibs/zmq/x64/include/zauth.h
  33. 3
      phonelibs/zmq/x64/include/zbeacon.h
  34. 3
      phonelibs/zmq/x64/include/zcert.h
  35. 3
      phonelibs/zmq/x64/include/zcertstore.h
  36. 3
      phonelibs/zmq/x64/include/zchunk.h
  37. 3
      phonelibs/zmq/x64/include/zclock.h
  38. 3
      phonelibs/zmq/x64/include/zconfig.h
  39. 3
      phonelibs/zmq/x64/include/zdigest.h
  40. 3
      phonelibs/zmq/x64/include/zdir.h
  41. 3
      phonelibs/zmq/x64/include/zdir_patch.h
  42. 3
      phonelibs/zmq/x64/include/zfile.h
  43. 3
      phonelibs/zmq/x64/include/zframe.h
  44. 3
      phonelibs/zmq/x64/include/zgossip.h
  45. 3
      phonelibs/zmq/x64/include/zhash.h
  46. 3
      phonelibs/zmq/x64/include/zhashx.h
  47. 3
      phonelibs/zmq/x64/include/ziflist.h
  48. 3
      phonelibs/zmq/x64/include/zlist.h
  49. 3
      phonelibs/zmq/x64/include/zlistx.h
  50. 3
      phonelibs/zmq/x64/include/zloop.h
  51. 3
      phonelibs/zmq/x64/include/zmonitor.h
  52. 3
      phonelibs/zmq/x64/include/zmq.h
  53. 3
      phonelibs/zmq/x64/include/zmq_utils.h
  54. 3
      phonelibs/zmq/x64/include/zmsg.h
  55. 3
      phonelibs/zmq/x64/include/zpoller.h
  56. 3
      phonelibs/zmq/x64/include/zproc.h
  57. 3
      phonelibs/zmq/x64/include/zproxy.h
  58. 3
      phonelibs/zmq/x64/include/zrex.h
  59. 3
      phonelibs/zmq/x64/include/zsock.h
  60. 3
      phonelibs/zmq/x64/include/zstr.h
  61. 3
      phonelibs/zmq/x64/include/zsys.h
  62. 3
      phonelibs/zmq/x64/include/ztimerset.h
  63. 3
      phonelibs/zmq/x64/include/ztrie.h
  64. 3
      phonelibs/zmq/x64/include/zuuid.h
  65. 3
      phonelibs/zmq/x64/lib/libczmq.a
  66. 3
      phonelibs/zmq/x64/lib/libczmq.la
  67. 1
      phonelibs/zmq/x64/lib/libczmq.so
  68. 1
      phonelibs/zmq/x64/lib/libczmq.so.4
  69. 3
      phonelibs/zmq/x64/lib/libczmq.so.4.0.2
  70. 3
      phonelibs/zmq/x64/lib/libzmq.a
  71. 3
      phonelibs/zmq/x64/lib/libzmq.la
  72. 3
      phonelibs/zmq/x64/lib/libzmq.lai
  73. 1
      phonelibs/zmq/x64/lib/libzmq.so
  74. 1
      phonelibs/zmq/x64/lib/libzmq.so.5
  75. 3
      phonelibs/zmq/x64/lib/libzmq.so.5.1.2
  76. 3
      phonelibs/zmq/x64/lib/pkgconfig/libczmq.pc
  77. 3
      phonelibs/zmq/x64/lib/pkgconfig/libzmq.pc
  78. 6
      run_docker_tests.sh
  79. 15
      selfdrive/athena/athenad.py
  80. 20
      selfdrive/boardd/Makefile
  81. 1
      selfdrive/boardd/boardd.cc
  82. 246
      selfdrive/boardd/boardd.py
  83. 25
      selfdrive/boardd/boardd_api_impl.pyx
  84. 25
      selfdrive/boardd/boardd_setup.py
  85. 36
      selfdrive/boardd/can_list_to_can_capnp.cc
  86. 0
      selfdrive/boardd/tests/__init__.py
  87. 247
      selfdrive/boardd/tests/boardd_old.py
  88. 19
      selfdrive/boardd/tests/fuzzer.py
  89. 76
      selfdrive/boardd/tests/test_boardd_api.py
  90. 51
      selfdrive/boardd/tests/test_boardd_loopback.py
  91. 10
      selfdrive/can/Makefile
  92. 4
      selfdrive/can/packer.cc
  93. 73
      selfdrive/can/packer.py
  94. 111
      selfdrive/can/packer_impl.pyx
  95. 5
      selfdrive/can/packer_setup.py
  96. 3
      selfdrive/can/parser.py
  97. 6
      selfdrive/can/process_dbc.py
  98. 0
      selfdrive/can/tests/__init__.py
  99. 67
      selfdrive/can/tests/packer_old.py
  100. 35
      selfdrive/can/tests/test_packer_chrysler.py
  101. Some files were not shown because too many files have changed in this diff Show More

@ -13,6 +13,11 @@ script:
tmppilot /bin/sh -c 'cd /tmp/openpilot/ && pyflakes $(find . -iname "*.py" | grep -vi "^\./pyextra.*" | grep -vi "^\./panda")' tmppilot /bin/sh -c 'cd /tmp/openpilot/ && pyflakes $(find . -iname "*.py" | grep -vi "^\./pyextra.*" | grep -vi "^\./panda")'
- docker run - docker run
tmppilot /bin/sh -c 'cd /tmp/openpilot/ && pylint $(find . -iname "*.py" | grep -vi "^\./pyextra.*" | grep -vi "^\./panda"); exit $(($? & 3))' tmppilot /bin/sh -c 'cd /tmp/openpilot/ && pylint $(find . -iname "*.py" | grep -vi "^\./pyextra.*" | grep -vi "^\./panda"); exit $(($? & 3))'
- docker run tmppilot /bin/sh -c 'cd /tmp/openpilot/ && make -C cereal && python -m unittest discover common'
- docker run tmppilot /bin/sh -c 'cd /tmp/openpilot/ && make -C cereal && python -m unittest discover selfdrive/can'
- docker run tmppilot /bin/sh -c 'cd /tmp/openpilot/ && make -C cereal && python -m unittest discover selfdrive/boardd'
- docker run tmppilot /bin/sh -c 'cd /tmp/openpilot/ && make -C cereal && python -m unittest discover selfdrive/controls'
- docker run tmppilot /bin/sh -c 'cd /tmp/openpilot/ && python -m unittest discover selfdrive/loggerd'
- docker run - docker run
-v "$(pwd)"/selfdrive/test/tests/plant/out:/tmp/openpilot/selfdrive/test/tests/plant/out -v "$(pwd)"/selfdrive/test/tests/plant/out:/tmp/openpilot/selfdrive/test/tests/plant/out
tmppilot /bin/sh -c 'cd /tmp/openpilot/selfdrive/test/tests/plant && OPTEST=1 ./test_longitudinal.py' tmppilot /bin/sh -c 'cd /tmp/openpilot/selfdrive/test/tests/plant && OPTEST=1 ./test_longitudinal.py'

@ -10,9 +10,22 @@ Most open source development activity is coordinated through our [Discord](https
* Make sure you have a [GitHub account](https://github.com/signup/free) * Make sure you have a [GitHub account](https://github.com/signup/free)
* Fork [our repositories](https://github.com/commaai) on GitHub * Fork [our repositories](https://github.com/commaai) on GitHub
## Testing
### Local Testing
You can test your changes on your machine by running `run_docker_tests.sh`. This will run some automated tests in docker against your code.
### Automated Testing
All PRs are automatically checked by travis. Check out `.travis.yml` for what travis runs. Any new tests sould be added to travis.
### Code Style and Linting
Code is automatically check for style by travis as part of the automated tests. You can also run these yourself by running `check_code_quality.sh`.
## Car Ports (openpilot) ## Car Ports (openpilot)
We've released a [Model Port guide](https://medium.com/@comma_ai/openpilot-port-guide-for-toyota-models-e5467f4b5fe6) for porting to Toyota/Lexus models. We've released a [Model Port guide](https://medium.com/@comma_ai/openpilot-port-guide-for-toyota-models-e5467f4b5fe6) for porting to Toyota/Lexus models.
If you port openpilot to a substantially new car brand, see this more generic [Brand Port guide](https://medium.com/@comma_ai/how-to-write-a-car-port-for-openpilot-7ce0785eda84). You might also be eligible for a bounty. See our bounties at [comma.ai/bounties.html](https://comma.ai/bounties.html) If you port openpilot to a substantially new car brand, see this more generic [Brand Port guide](https://medium.com/@comma_ai/how-to-write-a-car-port-for-openpilot-7ce0785eda84). You might also be eligible for a bounty. See our bounties at [comma.ai/bounties.html](https://comma.ai/bounties.html)

@ -2,17 +2,15 @@ FROM ubuntu:16.04
ENV PYTHONUNBUFFERED 1 ENV PYTHONUNBUFFERED 1
RUN apt-get update && apt-get install -y \ RUN apt-get update && apt-get install -y \
autoconf \
build-essential \ build-essential \
clang \ clang \
vim \
screen \
wget \ wget \
bzip2 \ bzip2 \
git \ git \
libglib2.0-0 \ libglib2.0-0 \
libtool \
python-pip \ python-pip \
capnproto \
libcapnp-dev \
libzmq5-dev \ libzmq5-dev \
libffi-dev \ libffi-dev \
libusb-1.0-0 \ libusb-1.0-0 \
@ -21,6 +19,9 @@ RUN apt-get update && apt-get install -y \
ocl-icd-opencl-dev \ ocl-icd-opencl-dev \
opencl-headers opencl-headers
COPY phonelibs/install_capnp.sh /tmp/install_capnp.sh
RUN /tmp/install_capnp.sh
RUN pip install --upgrade pip==18.0 RUN pip install --upgrade pip==18.0
RUN pip install numpy==1.11.2 scipy==0.18.1 matplotlib==2.1.2 RUN pip install numpy==1.11.2 scipy==0.18.1 matplotlib==2.1.2

@ -92,10 +92,13 @@ Supported Cars
| 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-19 | All | Yes | Yes<sup>2</sup>| 0mph | 0mph | Toyota | | Lexus | RX Hybrid 2016-19 | All | Yes | Yes<sup>2</sup>| 0mph | 0mph | Toyota |
| Subaru | Crosstrek 2018 | EyeSight | Yes | Stock | 0mph | 0mph | Subaru |
| Subaru | Impreza 2019 | EyeSight | Yes | Stock | 0mph | 0mph | Subaru | | Subaru | Impreza 2019 | EyeSight | Yes | Stock | 0mph | 0mph | Subaru |
| Toyota | Avalon 2016 | TSS-P | Yes | Yes<sup>2</sup>| 20mph<sup>1</sup>| 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<sup>1</sup>| 0mph | Toyota | | Toyota | Corolla 2017-18 | All | Yes | Yes<sup>2</sup>| 20mph<sup>1</sup>| 0mph | Toyota |
| Toyota | Corolla Hatchback 2019 | All | Yes | Yes | 0mph | 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 |
@ -103,6 +106,7 @@ Supported Cars
| Toyota | Prius Prime 2017-19 | All | Yes | Yes<sup>2</sup>| 0mph | 0mph | Toyota | | Toyota | Prius Prime 2017-19 | All | Yes | Yes<sup>2</sup>| 0mph | 0mph | Toyota |
| Toyota | Rav4 2016 | TSS-P | Yes | Yes<sup>2</sup>| 20mph<sup>1</sup>| 0mph | Toyota | | Toyota | Rav4 2016 | TSS-P | Yes | Yes<sup>2</sup>| 20mph<sup>1</sup>| 0mph | Toyota |
| Toyota | Rav4 2017-18 | All | Yes | Yes<sup>2</sup>| 20mph<sup>1</sup>| 0mph | Toyota | | Toyota | Rav4 2017-18 | All | Yes | Yes<sup>2</sup>| 20mph<sup>1</sup>| 0mph | Toyota |
| Toyota | Rav4 2019 | All | Yes | Yes | 0mph | 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)***
@ -159,6 +163,7 @@ Directory structure
├── pyextra # Libraries used on EON ├── pyextra # Libraries used on EON
└── selfdrive # Code needed to drive the car └── selfdrive # Code needed to drive the car
├── assets # Fonts and images for UI ├── assets # Fonts and images for UI
├── athena # Allows communication with the app
├── boardd # Daemon to talk to the board ├── boardd # Daemon to talk to the board
├── can # Helpers for parsing CAN messages ├── can # Helpers for parsing CAN messages
├── car # Car specific code to read states and control actuators ├── car # Car specific code to read states and control actuators
@ -169,7 +174,6 @@ Directory structure
├── logcatd # Android logcat as a service ├── logcatd # Android logcat as a service
├── loggerd # Logger and uploader of car data ├── loggerd # Logger and uploader of car data
├── mapd # Fetches map data and computes next global path ├── mapd # Fetches map data and computes next global path
├── orbd # Computes ORB features from frames
├── proclogd # Logs information from proc ├── proclogd # Logs information from proc
├── sensord # IMU / GPS interface code ├── sensord # IMU / GPS interface code
├── test # Car simulator running code through virtual maneuvers ├── test # Car simulator running code through virtual maneuvers

@ -1,3 +1,17 @@
Version 0.5.12 (2019-05-16)
==========================
* Improve lateral control for the Prius and Prius Prime
* Compress logs before writing to disk
* Remove old driving data when storage reaches 90% full
* Fix small offset in following distance
* Various small CPU optimizations
* Improve offroad power consumption: require NEOS Update
* Add default speed limits for Estonia thanks to martinl!
* Subaru Crosstrek support thanks to martinl!
* Toyota Avalon support thanks to njbrown09!
* Toyota Rav4 with TSS 2.0 support thansk to wocsor!
* Toyota Corolla with TSS 2.0 support thansk to wocsor!
Version 0.5.11 (2019-04-17) Version 0.5.11 (2019-04-17)
======================== ========================
* Add support for Subaru * Add support for Subaru

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:2d45eb035af4adeaf31fe96ab36fc310962cc8c375319ac2a5e707b2cdc03097 oid sha256:61fc21ef8cec4ca15be05b1b5fdb9c4d95f3a78be891a36d84bd2dd89441e28e
size 2588994 size 2596571

@ -6,20 +6,20 @@ 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
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)
ifneq (, $(shell which capnpc-java)) ifneq (, $(shell which capnpc-java))
GENS += gen/java/Car.java gen/java/Log.java GENS += gen/java/Car.java gen/java/Log.java
else else
$(warning capnpc-java not found, skipping java build) $(warning capnpc-java not found, skipping java build)
endif endif
endif
endif endif
ifeq ($(UNAME_M),aarch64) ifeq ($(UNAME_M),aarch64)
CAPNPC=PATH=$(PWD)/../phonelibs/capnp-cpp/aarch64/bin/:$$PATH capnpc CAPNPC=PATH=$(PWD)/../phonelibs/capnp-cpp/aarch64/bin/:$$PATH capnpc
else else

@ -73,6 +73,7 @@ struct CarEvent @0x9b1657f34caf3ad3 {
lowBattery @48; lowBattery @48;
invalidGiraffeHonda @49; invalidGiraffeHonda @49;
vehicleModelInvalid @50; vehicleModelInvalid @50;
controlsFailed @51;
} }
} }
@ -281,31 +282,83 @@ struct CarControl {
struct CarParams { struct CarParams {
carName @0 :Text; carName @0 :Text;
radarNameDEPRECATED @1 :Text; carFingerprint @1 :Text;
carFingerprint @2 :Text;
enableGasInterceptor @2 :Bool;
enableSteerDEPRECATED @3 :Bool; enableCruise @3 :Bool;
enableGasInterceptor @4 :Bool; enableCamera @4 :Bool;
enableBrakeDEPRECATED @5 :Bool; enableDsu @5 :Bool; # driving support unit
enableCruise @6 :Bool; enableApgs @6 :Bool; # advanced parking guidance system
enableCamera @26 :Bool;
enableDsu @27 :Bool; # driving support unit minEnableSpeed @7 :Float32;
enableApgs @28 :Bool; # advanced parking guidance system minSteerSpeed @8 :Float32;
safetyModel @9 :Int16;
minEnableSpeed @17 :Float32; safetyParam @10 :Int16;
minSteerSpeed @49 :Float32;
safetyModel @18 :Int16; steerMaxBP @11 :List(Float32);
safetyParam @41 :Int16; steerMaxV @12 :List(Float32);
gasMaxBP @13 :List(Float32);
steerMaxBP @19 :List(Float32); gasMaxV @14 :List(Float32);
steerMaxV @20 :List(Float32); brakeMaxBP @15 :List(Float32);
gasMaxBP @21 :List(Float32); brakeMaxV @16 :List(Float32);
gasMaxV @22 :List(Float32);
brakeMaxBP @23 :List(Float32);
brakeMaxV @24 :List(Float32); # things about the car in the manual
mass @17 :Float32; # [kg] running weight
longPidDeadzoneBP @32 :List(Float32); wheelbase @18 :Float32; # [m] distance from rear to front axle
longPidDeadzoneV @33 :List(Float32); centerToFront @19 :Float32; # [m] GC distance to front axle
steerRatio @20 :Float32; # [] ratio between front wheels and steering wheel angles
steerRatioRear @21 :Float32; # [] rear steering ratio wrt front steering (usually 0)
# things we can derive
rotationalInertia @22 :Float32; # [kg*m2] body rotational inertia
tireStiffnessFront @23 :Float32; # [N/rad] front tire coeff of stiff
tireStiffnessRear @24 :Float32; # [N/rad] rear tire coeff of stiff
longitudinalTuning @25 :LongitudinalPIDTuning;
lateralTuning :union {
pid @26 :LateralPIDTuning;
indi @27 :LateralINDITuning;
}
steerLimitAlert @28 :Bool;
vEgoStopping @29 :Float32; # Speed at which the car goes into stopping state
directAccelControl @30 :Bool; # Does the car have direct accel control or just gas/brake
stoppingControl @31 :Bool; # Does the car allows full control even at lows speeds when stopping
startAccel @32 :Float32; # Required acceleraton to overcome creep braking
steerRateCost @33 :Float32; # Lateral MPC cost on steering rate
steerControlType @34 :SteerControlType;
radarOffCan @35 :Bool; # True when radar objects aren't visible on CAN
steerActuatorDelay @36 :Float32; # Steering wheel actuator delay in seconds
openpilotLongitudinalControl @37 :Bool; # is openpilot doing the longitudinal control?
struct LateralPIDTuning {
kpBP @0 :List(Float32);
kpV @1 :List(Float32);
kiBP @2 :List(Float32);
kiV @3 :List(Float32);
kf @4 :Float32;
}
struct LongitudinalPIDTuning {
kpBP @0 :List(Float32);
kpV @1 :List(Float32);
kiBP @2 :List(Float32);
kiV @3 :List(Float32);
deadzoneBP @4 :List(Float32);
deadzoneV @5 :List(Float32);
}
struct LateralINDITuning {
outerLoopGain @0 :Float32;
innerLoopGain @1 :Float32;
timeConstant @2 :Float32;
actuatorEffectiveness @3 :Float32;
}
enum SafetyModels { enum SafetyModels {
# does NOT match board setting # does NOT match board setting
@ -323,46 +376,6 @@ struct CarParams {
subaru @11; subaru @11;
} }
# things about the car in the manual
mass @7 :Float32; # [kg] running weight
wheelbase @8 :Float32; # [m] distance from rear to front axle
centerToFront @9 :Float32; # [m] GC distance to front axle
steerRatio @10 :Float32; # [] ratio between front wheels and steering wheel angles
steerRatioRear @11 :Float32; # [] rear steering ratio wrt front steering (usually 0)
# things we can derive
rotationalInertia @12 :Float32; # [kg*m2] body rotational inertia
tireStiffnessFront @13 :Float32; # [N/rad] front tire coeff of stiff
tireStiffnessRear @14 :Float32; # [N/rad] rear tire coeff of stiff
# Kp and Ki for the lateral control
steerKpBP @42 :List(Float32);
steerKpV @43 :List(Float32);
steerKiBP @44 :List(Float32);
steerKiV @45 :List(Float32);
steerKpDEPRECATED @15 :Float32;
steerKiDEPRECATED @16 :Float32;
steerKf @25 :Float32;
# Kp and Ki for the longitudinal control
longitudinalKpBP @36 :List(Float32);
longitudinalKpV @37 :List(Float32);
longitudinalKiBP @38 :List(Float32);
longitudinalKiV @39 :List(Float32);
steerLimitAlert @29 :Bool;
vEgoStopping @30 :Float32; # Speed at which the car goes into stopping state
directAccelControl @31 :Bool; # Does the car have direct accel control or just gas/brake
stoppingControl @34 :Bool; # Does the car allows full control even at lows speeds when stopping
startAccel @35 :Float32; # Required acceleraton to overcome creep braking
steerRateCost @40 :Float32; # Lateral MPC cost on steering rate
steerControlType @46 :SteerControlType;
radarOffCan @47 :Bool; # True when radar objects aren't visible on CAN
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;
angle @1; angle @1;

@ -388,9 +388,9 @@ struct Live100Data {
ufAccelCmd @33 :Float32; ufAccelCmd @33 :Float32;
yActualDEPRECATED @6 :Float32; yActualDEPRECATED @6 :Float32;
yDesDEPRECATED @7 :Float32; yDesDEPRECATED @7 :Float32;
upSteer @8 :Float32; upSteerDEPRECATED @8 :Float32;
uiSteer @9 :Float32; uiSteerDEPRECATED @9 :Float32;
ufSteer @34 :Float32; ufSteerDEPRECATED @34 :Float32;
aTargetMinDEPRECATED @10 :Float32; aTargetMinDEPRECATED @10 :Float32;
aTargetMaxDEPRECATED @11 :Float32; aTargetMaxDEPRECATED @11 :Float32;
aTarget @35 :Float32; aTarget @35 :Float32;
@ -428,6 +428,11 @@ struct Live100Data {
vCurvature @46 :Float32; vCurvature @46 :Float32;
decelForTurn @47 :Bool; decelForTurn @47 :Bool;
lateralControlState :union {
indiState @52 :LateralINDIState;
pidState @53 :LateralPIDState;
}
enum ControlState { enum ControlState {
disabled @0; disabled @0;
preEnabled @1; preEnabled @1;
@ -455,6 +460,31 @@ struct Live100Data {
full @3; # full screen full @3; # full screen
} }
struct LateralINDIState {
active @0 :Bool;
steerAngle @1 :Float32;
steerRate @2 :Float32;
steerAccel @3 :Float32;
rateSetPoint @4 :Float32;
accelSetPoint @5 :Float32;
accelError @6 :Float32;
delayedOutput @7 :Float32;
delta @8 :Float32;
output @9 :Float32;
}
struct LateralPIDState {
active @0 :Bool;
steerAngle @1 :Float32;
steerRate @2 :Float32;
angleError @3 :Float32;
p @4 :Float32;
i @5 :Float32;
f @6 :Float32;
output @7 :Float32;
saturated @8 :Bool;
}
} }
struct LiveEventData { struct LiveEventData {
@ -545,7 +575,7 @@ struct LogRotate {
struct Plan { struct Plan {
mdMonoTime @9 :UInt64; mdMonoTime @9 :UInt64;
l20MonoTime @10 :UInt64; l20MonoTime @10 :UInt64;
events @13 :List(Car.CarEvent); eventsDEPRECATED @13 :List(Car.CarEvent);
# lateral, 3rd order polynomial # lateral, 3rd order polynomial
lateralValidDEPRECATED @0 :Bool; lateralValidDEPRECATED @0 :Bool;
@ -583,6 +613,7 @@ struct Plan {
decelForTurn @22 :Bool; decelForTurn @22 :Bool;
mapValid @25 :Bool; mapValid @25 :Bool;
radarValid @28 :Bool; radarValid @28 :Bool;
radarCommIssue @30 :Bool;
processingDelay @29 :Float32; processingDelay @29 :Float32;

@ -0,0 +1,11 @@
#!/bin/bash
pyflakes $(find . -iname "*.py" | grep -vi "^\./pyextra.*" | grep -vi "^\./panda")
RESULT=$?
if [ $RESULT -eq 0 ]; then
pylint $(find . -iname "*.py" | grep -vi "^\./pyextra.*" | grep -vi "^\./panda")
RESULT=$? & 3
fi
[ $RESULT -ne 0 ] && exit 1
exit 0

@ -0,0 +1,10 @@
all: simple_kalman_impl.so
simple_kalman_impl.so: simple_kalman_impl.pyx simple_kalman_impl.pxd simple_kalman_setup.py
python simple_kalman_setup.py build_ext --inplace
rm -rf build
rm simple_kalman_impl.c
.PHONY: clean
clean:
rm -f simple_kalman_impl.so

@ -1,23 +1,10 @@
import numpy as np # pylint: skip-file
import os
import subprocess
kalman_dir = os.path.dirname(os.path.abspath(__file__))
subprocess.check_call(["make", "simple_kalman_impl.so"], cwd=kalman_dir)
class KF1D: from simple_kalman_impl import KF1D as KF1D
# this EKF assumes constant covariance matrix, so calculations are much simpler # Silence pyflakes
# the Kalman gain also needs to be precomputed using the control module assert KF1D
def __init__(self, x0, A, C, K):
self.x = x0
self.A = A
self.C = C
self.K = K
self.A_K = self.A - np.dot(self.K, self.C)
# K matrix needs to be pre-computed as follow:
# import control
# (x, l, K) = control.dare(np.transpose(self.A), np.transpose(self.C), Q, R)
# self.K = np.transpose(K)
def update(self, meas):
self.x = np.dot(self.A_K, self.x) + np.dot(self.K, meas)
return self.x

@ -0,0 +1,16 @@
cdef class KF1D:
cdef public:
double x0_0
double x1_0
double K0_0
double K1_0
double A0_0
double A0_1
double A1_0
double A1_1
double C0_0
double C0_1
double A_K_0
double A_K_1
double A_K_2
double A_K_3

@ -0,0 +1,35 @@
cdef class KF1D:
def __init__(self, x0, A, C, K):
self.x0_0 = x0[0][0]
self.x1_0 = x0[1][0]
self.A0_0 = A[0][0]
self.A0_1 = A[0][1]
self.A1_0 = A[1][0]
self.A1_1 = A[1][1]
self.C0_0 = C[0]
self.C0_1 = C[1]
self.K0_0 = K[0][0]
self.K1_0 = K[1][0]
self.A_K_0 = self.A0_0 - self.K0_0 * self.C0_0
self.A_K_1 = self.A0_1 - self.K0_0 * self.C0_1
self.A_K_2 = self.A1_0 - self.K1_0 * self.C0_0
self.A_K_3 = self.A1_1 - self.K1_0 * self.C0_1
def update(self, meas):
cdef double x0_0 = self.A_K_0 * self.x0_0 + self.A_K_1 * self.x1_0 + self.K0_0 * meas
cdef double x1_0 = self.A_K_2 * self.x0_0 + self.A_K_3 * self.x1_0 + self.K1_0 * meas
self.x0_0 = x0_0
self.x1_0 = x1_0
return [self.x0_0, self.x1_0]
@property
def x(self):
return [[self.x0_0], [self.x1_0]]
@x.setter
def x(self, x):
self.x0_0 = x[0][0]
self.x1_0 = x[1][0]

@ -0,0 +1,23 @@
import numpy as np
class KF1D:
# this EKF assumes constant covariance matrix, so calculations are much simpler
# the Kalman gain also needs to be precomputed using the control module
def __init__(self, x0, A, C, K):
self.x = x0
self.A = A
self.C = C
self.K = K
self.A_K = self.A - np.dot(self.K, self.C)
# K matrix needs to be pre-computed as follow:
# import control
# (x, l, K) = control.dare(np.transpose(self.A), np.transpose(self.C), Q, R)
# self.K = np.transpose(K)
def update(self, meas):
self.x = np.dot(self.A_K, self.x) + np.dot(self.K, meas)
return self.x

@ -0,0 +1,5 @@
from distutils.core import setup, Extension
from Cython.Build import cythonize
setup(name='Simple Kalman Implementation',
ext_modules=cythonize(Extension("simple_kalman_impl", ["simple_kalman_impl.pyx"])))

@ -0,0 +1,116 @@
import numpy as np
import numpy.matlib
import unittest
import timeit
from common.kalman.ekf import EKF, SimpleSensor, FastEKF1D
class TestEKF(EKF):
def __init__(self, var_init, Q):
super(TestEKF, self).__init__(False)
self.identity = numpy.matlib.identity(2)
self.state = numpy.matlib.zeros((2, 1))
self.covar = self.identity * var_init
self.process_noise = numpy.matlib.diag(Q)
def calc_transfer_fun(self, dt):
tf = numpy.matlib.identity(2)
tf[0, 1] = dt
return tf, tf
class EKFTest(unittest.TestCase):
def test_update_scalar(self):
ekf = TestEKF(1e3, [0.1, 1])
dt = 1. / 100
sensor = SimpleSensor(0, 1, 2)
readings = map(sensor.read, np.arange(100, 300))
for reading in readings:
ekf.update_scalar(reading)
ekf.predict(dt)
np.testing.assert_allclose(ekf.state, [[300], [100]], 1e-4)
np.testing.assert_allclose(
ekf.covar,
np.asarray([[0.0563, 0.10278], [0.10278, 0.55779]]),
atol=1e-4)
def test_unbiased(self):
ekf = TestEKF(1e3, [0., 0.])
dt = np.float64(1. / 100)
sensor = SimpleSensor(0, 1, 2)
readings = map(sensor.read, np.arange(1000))
for reading in readings:
ekf.update_scalar(reading)
ekf.predict(dt)
np.testing.assert_allclose(ekf.state, [[1000.], [100.]], 1e-4)
class FastEKF1DTest(unittest.TestCase):
def test_correctness(self):
dt = 1. / 100
reading = SimpleSensor(0, 1, 2).read(100)
ekf = TestEKF(1e3, [0.1, 1])
fast_ekf = FastEKF1D(dt, 1e3, [0.1, 1])
ekf.update_scalar(reading)
fast_ekf.update_scalar(reading)
self.assertAlmostEqual(ekf.state[0] , fast_ekf.state[0])
self.assertAlmostEqual(ekf.state[1] , fast_ekf.state[1])
self.assertAlmostEqual(ekf.covar[0, 0], fast_ekf.covar[0])
self.assertAlmostEqual(ekf.covar[0, 1], fast_ekf.covar[2])
self.assertAlmostEqual(ekf.covar[1, 1], fast_ekf.covar[1])
ekf.predict(dt)
fast_ekf.predict(dt)
self.assertAlmostEqual(ekf.state[0] , fast_ekf.state[0])
self.assertAlmostEqual(ekf.state[1] , fast_ekf.state[1])
self.assertAlmostEqual(ekf.covar[0, 0], fast_ekf.covar[0])
self.assertAlmostEqual(ekf.covar[0, 1], fast_ekf.covar[2])
self.assertAlmostEqual(ekf.covar[1, 1], fast_ekf.covar[1])
def test_speed(self):
setup = """
import numpy as np
from common.kalman.tests.test_ekf import TestEKF
from common.kalman.ekf import SimpleSensor, FastEKF1D
dt = 1. / 100
reading = SimpleSensor(0, 1, 2).read(100)
var_init, Q = 1e3, [0.1, 1]
ekf = TestEKF(var_init, Q)
fast_ekf = FastEKF1D(dt, var_init, Q)
"""
timeit.timeit("""
ekf.update_scalar(reading)
ekf.predict(dt)
""", setup=setup, number=1000)
ekf_speed = timeit.timeit("""
ekf.update_scalar(reading)
ekf.predict(dt)
""", setup=setup, number=20000)
timeit.timeit("""
fast_ekf.update_scalar(reading)
fast_ekf.predict(dt)
""", setup=setup, number=1000)
fast_ekf_speed = timeit.timeit("""
fast_ekf.update_scalar(reading)
fast_ekf.predict(dt)
""", setup=setup, number=20000)
assert fast_ekf_speed < ekf_speed / 4
if __name__ == "__main__":
unittest.main()

@ -0,0 +1,85 @@
import unittest
import random
import timeit
import numpy as np
from common.kalman.simple_kalman import KF1D
from common.kalman.simple_kalman_old import KF1D as KF1D_old
class TestSimpleKalman(unittest.TestCase):
def setUp(self):
dt = 0.01
x0_0 = 0.0
x1_0 = 0.0
A0_0 = 1.0
A0_1 = dt
A1_0 = 0.0
A1_1 = 1.0
C0_0 = 1.0
C0_1 = 0.0
K0_0 = 0.12287673
K1_0 = 0.29666309
self.kf_old = KF1D_old(x0=np.matrix([[x0_0], [x1_0]]),
A=np.matrix([[A0_0, A0_1], [A1_0, A1_1]]),
C=np.matrix([C0_0, C0_1]),
K=np.matrix([[K0_0], [K1_0]]))
self.kf = KF1D(x0=[[x0_0], [x1_0]],
A=[[A0_0, A0_1], [A1_0, A1_1]],
C=[C0_0, C0_1],
K=[[K0_0], [K1_0]])
def test_getter_setter(self):
self.kf.x = [[1.0], [1.0]]
self.assertEqual(self.kf.x, [[1.0], [1.0]])
def update_returns_state(self):
x = self.kf.update(100)
self.assertEqual(x, self.kf.x)
def test_old_equal_new(self):
for _ in range(1000):
v_wheel = random.uniform(0, 200)
x_old = self.kf_old.update(v_wheel)
x = self.kf.update(v_wheel)
# Compare the output x, verify that the error is less than 1e-4
self.assertAlmostEqual(x_old[0], x[0])
self.assertAlmostEqual(x_old[1], x[1])
def test_new_is_faster(self):
setup = """
import numpy as np
from common.kalman.simple_kalman import KF1D
from common.kalman.simple_kalman_old import KF1D as KF1D_old
dt = 0.01
x0_0 = 0.0
x1_0 = 0.0
A0_0 = 1.0
A0_1 = dt
A1_0 = 0.0
A1_1 = 1.0
C0_0 = 1.0
C0_1 = 0.0
K0_0 = 0.12287673
K1_0 = 0.29666309
kf_old = KF1D_old(x0=np.matrix([[x0_0], [x1_0]]),
A=np.matrix([[A0_0, A0_1], [A1_0, A1_1]]),
C=np.matrix([C0_0, C0_1]),
K=np.matrix([[K0_0], [K1_0]]))
kf = KF1D(x0=[[x0_0], [x1_0]],
A=[[A0_0, A0_1], [A1_0, A1_1]],
C=[C0_0, C0_1],
K=[[K0_0], [K1_0]])
"""
kf_speed = timeit.timeit("kf.update(1234)", setup=setup, number=10000)
kf_old_speed = timeit.timeit("kf_old.update(1234)", setup=setup, number=10000)
self.assertTrue(kf_speed < kf_old_speed / 4)

@ -0,0 +1,28 @@
import signal
class TimeoutException(Exception):
pass
class Timeout:
"""
Timeout context manager.
For example this code will raise a TimeoutException:
with Timeout(seconds=5, error_msg="Sleep was too long"):
time.sleep(10)
"""
def __init__(self, seconds, error_msg=None):
if error_msg is None:
error_msg = 'Timed out after {} seconds'.format(seconds)
self.seconds = seconds
self.error_msg = error_msg
def handle_timeout(self, signume, frame):
raise TimeoutException(self.error_msg)
def __enter__(self):
signal.signal(signal.SIGALRM, self.handle_timeout)
signal.alarm(self.seconds)
def __exit__(self, exc_type, exc_val, exc_tb):
signal.alarm(0)

@ -200,3 +200,14 @@ def transform_img(base_img,
augmented_rgb[:cyy] = cv2.warpPerspective(base_img, M, (output_size[0], cyy), borderMode=cv2.BORDER_REPLICATE) augmented_rgb[:cyy] = cv2.warpPerspective(base_img, M, (output_size[0], cyy), borderMode=cv2.BORDER_REPLICATE)
return augmented_rgb return augmented_rgb
def yuv_crop(frame, output_size, center=None):
# output_size in camera coordinates so u,v
# center in array coordinates so row, column
rgb = cv2.cvtColor(frame, cv2.COLOR_YUV2RGB_I420)
if not center:
center = (rgb.shape[0]/2, rgb.shape[1]/2)
rgb_crop = rgb[center[0] - output_size[1]/2: center[0] + output_size[1]/2,
center[1] - output_size[0]/2: center[1] + output_size[0]/2]
return cv2.cvtColor(rgb_crop, cv2.COLOR_RGB2YUV_I420)

@ -1,8 +1,7 @@
import numpy as np import numpy as np
from common.transformations.camera import eon_focal_length, \ from common.transformations.camera import eon_focal_length, \
vp_from_ke, \ vp_from_ke, get_view_frame_from_road_frame, \
get_view_frame_from_road_frame, \
FULL_FRAME_SIZE FULL_FRAME_SIZE
# segnet # segnet
@ -117,6 +116,16 @@ def get_camera_frame_from_model_frame(camera_frame_from_road_frame, height=model
return np.dot(camera_from_model_camera, model_camera_from_model_frame) return np.dot(camera_from_model_camera, model_camera_from_model_frame)
def get_camera_frame_from_medmodel_frame(camera_frame_from_road_frame):
camera_frame_from_ground = camera_frame_from_road_frame[:, (0, 1, 3)]
medmodel_frame_from_ground = medmodel_frame_from_road_frame[:, (0, 1, 3)]
ground_from_medmodel_frame = np.linalg.inv(medmodel_frame_from_ground)
camera_frame_from_medmodel_frame = np.dot(camera_frame_from_ground, ground_from_medmodel_frame)
return camera_frame_from_medmodel_frame
def get_camera_frame_from_bigmodel_frame(camera_frame_from_road_frame): def get_camera_frame_from_bigmodel_frame(camera_frame_from_road_frame):
camera_frame_from_ground = camera_frame_from_road_frame[:, (0, 1, 3)] camera_frame_from_ground = camera_frame_from_road_frame[:, (0, 1, 3)]
bigmodel_frame_from_ground = bigmodel_frame_from_road_frame[:, (0, 1, 3)] bigmodel_frame_from_ground = bigmodel_frame_from_road_frame[:, (0, 1, 3)]

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:555f74644a70161700a8517ff6351ea8c988ca0a550a23550121147963c7cb6c
size 159

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:b360bfe8e0769adaa8ee6ae26324e26c40ccbdb557677a6b8ad506214984d2e8
size 6245

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:526122b6d0ca17de16c39595741ac34f32d2987d9bc4ca645750a73a3d0494b5
size 260714

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:028ce694fe58a051bdc31e915da55fda733acf0ade0ad9da8ffd23deea27f941
size 1707

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:e434c73611bb5576b3a9b1a6a40bce70a5ecbe36ea1a510392a2935196af0569
size 1275

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:7a7c22cb33dcd531a21837dc70103043012f7b29d1f5b1b7aaf430f9034ef6f9
size 5539

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:c6866da03dbd52cfd3eb2b3cd1fcbce6d81756d0d573590065d4a42f212a9356
size 19294

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:f5e5e9e2086688abc765e0c13d6621b7648d0a5fe41446af777f9f192fbaeb29
size 2473

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:f9696bd0aea94f596a6ad7083b72e7fc30c31b0da16d5731225a31d1c2c4ee11
size 3644

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:4a97a24653ef009fb968102d4d4a41fe2bde9efea9d0d9cfffe1abb14b4cf592
size 3611

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:c4212faea9f2a2f7ba343cc02987fd96deffe8e23dee35e2c0a088841abffecb
size 2856

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:450ea0eab5a4fd081d1face3cb4991cbdae8a05721493e5f8a099be208b2515b
size 4283

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:be988dcc7301ebd693f1485cbe60d22d7cecc2d9fb6d986eadfded77678ff74c
size 3567

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:c5391efa3017a3fd4d90b73f661e43f0b0664052520f4f983b38766376521d7b
size 5899

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:7f3f0c545553e4cef2115043f5ba58f5c856889c215fe123ed95d46dae14dfa5
size 2465

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:e9b32a750b627e4a621ab3e9a4cefe64b3a367da091b8757450791e8d9361c2b
size 6591

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:22a41bbe93e99cca59fe8d1617fb27b336cd2c4f2d094b4eea95f98e3470fce3
size 2069

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:4ea8ae429fee870ac8c605fe28adf604d7dd22109360f7c089471ec9f07f754d
size 6129

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:bdf4336095870dba4e5fdb89a7867b26b61a879cf4c0631dab857ebbee79ff9f
size 2422

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:a66c39280771660524a50e359e2adc6f35df35a11d6cf7d922740e2d3b34e04a
size 6250

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:6f3b444f0b237c105a7aaef90ba2e09609e5d06a5a278c6cc5a6b8a1aa6c9752
size 6538

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:132f3c042ffc2a056405a6857a66ec6bfe40fad7edfc3229ef6b833407a0bfe3
size 2964

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:ad6a1d63f4059cd6dd9c18eae4afd6a449c6f1ae4b03a1b697af79708612b9e1
size 8178

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:d163c60813069859c1c8ea94e07f7769f35b406147abc342d3dbb9448cfb8dc9
size 12557

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:3df305f973face711a307384a310bbe3d74afbd06fc263e4fe78e6886244df73
size 2256

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:23ca4ea43fe89ef066c42c0524b21bb2f3857bda386f8df8109f69cb25f13cc4
size 6202

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:382c7fa2cfdcb3efdf1631ba6ba1f97bb03297c102cf6e629c3db8519e946a0e
size 8547

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:32aa6915cea28c1a7b1bd360d0dc7d4981ace423a126c8708718d5058893289d
size 7185

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:8c4004636f31648f98050e637daa231c70fcf4b64da326548b4f50dccba5ceb1
size 1889

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:e00ab4b318526fc2b340ca69cafcd5e5071810f63471a0fe89064ee18e1afafb
size 24177

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:926abbe8a8ab28df17746ca5bf60440b799b391be172c406e0628cdef3788909
size 2316

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:1e08ea0a57a0cede5c2860dd447d5452226b61de22dc778e6e6c602ee940ad20
size 11537

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:1b2af935bab05d478ed103138e5a0b5ddce85a270de2eef73c474155a93f612c
size 3453

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:c18a537b231ec1228cc9d4ca3c8e0d81aad0311bafc2d9f8081d9ad10671c935
size 7801

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:61798dbd369cfefd3a90376818f628107bff9131d95cc8b444a924c28cb06115
size 3889

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:5aa3120214f9fa79436f9b5ed4db050a5974d774dd99bd0e9bba13f70f04be6d
size 2903

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:b1e1908455d9810980fb5c9de6726c33a327ff71976d08f696860cd506087c6c
size 44814

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:ea6f83ec5aa4ae69d95a991f0a9c8912b7f8e1c23db1d73be934c80e086f783a
size 4557

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:0848d4218ef47604f36f365d1ee83727f2a03494c53a58475803b5f36437c5e6
size 16109

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:09649e6b235a6b2af50eb7e7644c38fd5132d16e4b63351affd7adf5009f06ad
size 3551

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:89dc6a1b5e09267704dc07ed986fb82b5057e51a4e7f0bf801cc204dedf9fb4e
size 4475

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:6de67e7d53ebbffefe2c330bded67425df97d8bb5d79717d0227e3b6fb1899f9
size 2790

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:c55f283766b3a7746306f6599c2094630a6d55c51ca7d3e84d0d3fb099bd57a0
size 986208

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:371efa44e737c438c7524139d773d3a2c2481704a5c38c6766fd24f8449ae91c
size 1084

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:bde558fe588d8bd4b8016ad4637a7bc683a6957085b2a3528cd681354504b5ab
size 563416

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:a9ef32858d9dd32d623f448d68901525ac6971a5a790b70d61555b1a8def4eca
size 5319544

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:6a9380180adcdbf42c77910639d622b44f716e905bc26d25b00d46e1f72b4ea6
size 965

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:573977c92c2d4ac52c3df775a216276b1fd050d3abd03906d2b852879c41ce85
size 946

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:89878c51741e047a157fbcb223d57c4ca76441796a53540e86ef9c811c71bc45
size 1335280

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:6f17e5e2b5e61fd0efb58d14572bf9eb11ea017ca5e05088422f79bf4548a402
size 946

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:77a40af70e122f5db9a4d043811921ff7105fc3bf66d445d195b3a9289f16c11
size 220

@ -3,6 +3,12 @@ set -e
docker build -t tmppilot -f Dockerfile.openpilot . docker build -t tmppilot -f Dockerfile.openpilot .
docker run --rm \
tmppilot /bin/sh -c 'cd /tmp/openpilot/selfdrive/test/ && ./test_fingerprints.py'
docker run --rm \
tmppilot /bin/sh -c 'cd /tmp/openpilot/ && pyflakes $(find . -iname "*.py" | grep -vi "^\./pyextra.*" | grep -vi "^\./panda")'
docker run --rm \
tmppilot /bin/sh -c 'cd /tmp/openpilot/ && pylint $(find . -iname "*.py" | grep -vi "^\./pyextra.*" | grep -vi "^\./panda"); exit $(($? & 3))'
docker run --rm \ docker run --rm \
-v "$(pwd)"/selfdrive/test/tests/plant/out:/tmp/openpilot/selfdrive/test/tests/plant/out \ -v "$(pwd)"/selfdrive/test/tests/plant/out:/tmp/openpilot/selfdrive/test/tests/plant/out \
tmppilot /bin/sh -c 'cd /tmp/openpilot/selfdrive/test/tests/plant && OPTEST=1 ./test_longitudinal.py' tmppilot /bin/sh -c 'cd /tmp/openpilot/selfdrive/test/tests/plant && OPTEST=1 ./test_longitudinal.py'

@ -6,9 +6,11 @@ import time
import threading import threading
import traceback import traceback
import zmq import zmq
import requests
import six.moves.queue import six.moves.queue
from jsonrpc import JSONRPCResponseManager, dispatcher from jsonrpc import JSONRPCResponseManager, dispatcher
from websocket import create_connection, WebSocketTimeoutException from websocket import create_connection, WebSocketTimeoutException
from selfdrive.loggerd.config import ROOT
import selfdrive.crash as crash import selfdrive.crash as crash
import selfdrive.messaging as messaging import selfdrive.messaging as messaging
@ -71,6 +73,19 @@ def getMessage(service=None, timeout=1000):
ret = messaging.recv_one(socket) ret = messaging.recv_one(socket)
return ret.to_dict() return ret.to_dict()
@dispatcher.add_method
def listDataDirectory():
files = [os.path.relpath(os.path.join(dp, f), ROOT) for dp, dn, fn in os.walk(ROOT) for f in fn]
return files
@dispatcher.add_method
def uploadFileToUrl(fn, url, headers):
if len(fn) == 0 or fn[0] == '/' or '..' in fn:
return 500
with open(os.path.join(ROOT, fn), "rb") as f:
ret = requests.put(url, data=f, headers=headers, timeout=10)
return ret.status_code
def ws_recv(ws, end_event): def ws_recv(ws, end_event):
while not end_event.is_set(): while not end_event.is_set():
try: try:

@ -46,6 +46,7 @@ all: boardd
include ../common/cereal.mk include ../common/cereal.mk
OBJS = boardd.o \ OBJS = boardd.o \
can_list_to_can_capnp.o \
../common/swaglog.o \ ../common/swaglog.o \
../common/params.o \ ../common/params.o \
../common/util.o \ ../common/util.o \
@ -72,6 +73,15 @@ boardd.o: boardd.cc
-I../../ \ -I../../ \
-c -o '$@' '$<' -c -o '$@' '$<'
boardd_api_impl.so: libcan_list_to_can_capnp.a boardd_api_impl.pyx boardd_setup.py
python boardd_setup.py build_ext --inplace
rm -rf build
rm -f boardd_api_impl.cpp
libcan_list_to_can_capnp.a: can_list_to_can_capnp.o $(CEREAL_OBJS)
ar rcs '$@' $^
%.o: %.c %.o: %.c
@echo "[ CC ] $@" @echo "[ CC ] $@"
$(CC) $(CFLAGS) -MMD \ $(CC) $(CFLAGS) -MMD \
@ -81,8 +91,16 @@ boardd.o: boardd.cc
$(JSON_FLAGS) \ $(JSON_FLAGS) \
-c -o '$@' '$<' -c -o '$@' '$<'
%.o: %.cc
@echo "[ CC ] $@"
$(CXX) $(CXXFLAGS) -MMD \
-Iinclude -I.. -I../.. \
$(CEREAL_CXXFLAGS) \
$(ZMQ_FLAGS) \
-c -o '$@' '$<'
.PHONY: clean .PHONY: clean
clean: clean:
rm -f boardd $(OBJS) $(DEPS) rm -f boardd libcan_list_to_can_capnp.a boardd_api_impl.so $(OBJS) $(DEPS)
-include $(DEPS) -include $(DEPS)

@ -85,6 +85,7 @@ void *safety_setter_thread(void *s) {
// format for board, make copy due to alignment issues, will be freed on out of scope // format for board, make copy due to alignment issues, will be freed on out of scope
auto amsg = kj::heapArray<capnp::word>((value_sz / sizeof(capnp::word)) + 1); auto amsg = kj::heapArray<capnp::word>((value_sz / sizeof(capnp::word)) + 1);
memcpy(amsg.begin(), value, value_sz); memcpy(amsg.begin(), value, value_sz);
free(value);
capnp::FlatArrayMessageReader cmsg(amsg); capnp::FlatArrayMessageReader cmsg(amsg);
cereal::CarParams::Reader car_params = cmsg.getRoot<cereal::CarParams>(); cereal::CarParams::Reader car_params = cmsg.getRoot<cereal::CarParams>();

@ -1,49 +1,13 @@
#!/usr/bin/env python # pylint: skip-file
# This file is not used by OpenPilot. Only boardd.cc is used.
# The python version is slower, but has more options for development.
# TODO: merge the extra functionalities of this file (like MOCK) in boardd.c and
# delete this python version of boardd
import os import os
import struct import subprocess
import zmq
import time
import selfdrive.messaging as messaging # Cython
from common.realtime import Ratekeeper boardd_api_dir = os.path.dirname(os.path.abspath(__file__))
from selfdrive.services import service_list subprocess.check_call(["make", "boardd_api_impl.so"], cwd=boardd_api_dir)
from selfdrive.swaglog import cloudlog from selfdrive.boardd.boardd_api_impl import can_list_to_can_capnp
assert can_list_to_can_capnp
# USB is optional
try:
import usb1
from usb1 import USBErrorIO, USBErrorOverflow #pylint: disable=no-name-in-module
except Exception:
pass
SAFETY_NOOUTPUT = 0
SAFETY_HONDA = 1
SAFETY_TOYOTA = 2
SAFETY_CHRYSLER = 9
SAFETY_TOYOTA_NOLIMITS = 0x1336
SAFETY_ALLOUTPUT = 0x1337
# *** serialization functions ***
def can_list_to_can_capnp(can_msgs, msgtype='can'):
dat = messaging.new_message()
dat.init(msgtype, len(can_msgs))
for i, can_msg in enumerate(can_msgs):
if msgtype == 'sendcan':
cc = dat.sendcan[i]
else:
cc = dat.can[i]
cc.address = can_msg[0]
cc.busTime = can_msg[1]
cc.dat = str(can_msg[2])
cc.src = can_msg[3]
return dat
def can_capnp_to_can_list(can, src_filter=None): def can_capnp_to_can_list(can, src_filter=None):
ret = [] ret = []
@ -51,199 +15,3 @@ def can_capnp_to_can_list(can, src_filter=None):
if src_filter is None or msg.src in src_filter: if src_filter is None or msg.src in src_filter:
ret.append((msg.address, msg.busTime, msg.dat, msg.src)) ret.append((msg.address, msg.busTime, msg.dat, msg.src))
return ret return ret
# *** can driver ***
def can_health():
while 1:
try:
dat = handle.controlRead(usb1.TYPE_VENDOR | usb1.RECIPIENT_DEVICE, 0xd2, 0, 0, 0x10)
break
except (USBErrorIO, USBErrorOverflow):
cloudlog.exception("CAN: BAD HEALTH, RETRYING")
v, i, started = struct.unpack("IIB", dat[0:9])
# TODO: units
return {"voltage": v, "current": i, "started": bool(started)}
def __parse_can_buffer(dat):
ret = []
for j in range(0, len(dat), 0x10):
ddat = dat[j:j+0x10]
f1, f2 = struct.unpack("II", ddat[0:8])
ret.append((f1 >> 21, f2>>16, ddat[8:8+(f2&0xF)], (f2>>4)&0xF))
return ret
def can_send_many(arr):
snds = []
for addr, _, dat, alt in arr:
if addr < 0x800: # only support 11 bit addr
snd = struct.pack("II", ((addr << 21) | 1), len(dat) | (alt << 4)) + dat
snd = snd.ljust(0x10, '\x00')
snds.append(snd)
while 1:
try:
handle.bulkWrite(3, ''.join(snds))
break
except (USBErrorIO, USBErrorOverflow):
cloudlog.exception("CAN: BAD SEND MANY, RETRYING")
def can_recv():
dat = ""
while 1:
try:
dat = handle.bulkRead(1, 0x10*256)
break
except (USBErrorIO, USBErrorOverflow):
cloudlog.exception("CAN: BAD RECV, RETRYING")
return __parse_can_buffer(dat)
def can_init():
global handle, context
handle = None
cloudlog.info("attempting can init")
context = usb1.USBContext()
#context.setDebug(9)
for device in context.getDeviceList(skip_on_error=True):
if device.getVendorID() == 0xbbaa and device.getProductID() == 0xddcc:
handle = device.open()
handle.claimInterface(0)
handle.controlWrite(0x40, 0xdc, SAFETY_ALLOUTPUT, 0, b'')
if handle is None:
cloudlog.warn("CAN NOT FOUND")
exit(-1)
cloudlog.info("got handle")
cloudlog.info("can init done")
def boardd_mock_loop():
context = zmq.Context()
can_init()
handle.controlWrite(0x40, 0xdc, SAFETY_ALLOUTPUT, 0, b'')
logcan = messaging.sub_sock(context, service_list['can'].port)
sendcan = messaging.pub_sock(context, service_list['sendcan'].port)
while 1:
tsc = messaging.drain_sock(logcan, wait_for_one=True)
snds = [can_capnp_to_can_list(x.can) for x in tsc]
snds = [x for x in snds if x[-1] <= 1]
can_send_many(snds)
# recv @ 100hz
can_msgs = can_recv()
print("sent %d got %d" % (len(snds), len(can_msgs)))
m = can_list_to_can_capnp(can_msgs)
sendcan.send(m.to_bytes())
def boardd_test_loop():
can_init()
cnt = 0
while 1:
can_send_many([[0xbb,0,"\xaa\xaa\xaa\xaa",0], [0xaa,0,"\xaa\xaa\xaa\xaa"+struct.pack("!I", cnt),1]])
#can_send_many([[0xaa,0,"\xaa\xaa\xaa\xaa",0]])
#can_send_many([[0xaa,0,"\xaa\xaa\xaa\xaa",1]])
# recv @ 100hz
can_msgs = can_recv()
print("got %d" % (len(can_msgs)))
time.sleep(0.01)
cnt += 1
# *** main loop ***
def boardd_loop(rate=200):
rk = Ratekeeper(rate)
context = zmq.Context()
can_init()
# *** publishes can and health
logcan = messaging.pub_sock(context, service_list['can'].port)
health_sock = messaging.pub_sock(context, service_list['health'].port)
# *** subscribes to can send
sendcan = messaging.sub_sock(context, service_list['sendcan'].port)
# drain sendcan to delete any stale messages from previous runs
messaging.drain_sock(sendcan)
while 1:
# health packet @ 1hz
if (rk.frame%rate) == 0:
health = can_health()
msg = messaging.new_message()
msg.init('health')
# store the health to be logged
msg.health.voltage = health['voltage']
msg.health.current = health['current']
msg.health.started = health['started']
msg.health.controlsAllowed = True
health_sock.send(msg.to_bytes())
# recv @ 100hz
can_msgs = can_recv()
# publish to logger
# TODO: refactor for speed
if len(can_msgs) > 0:
dat = can_list_to_can_capnp(can_msgs)
logcan.send(dat.to_bytes())
# send can if we have a packet
tsc = messaging.recv_sock(sendcan)
if tsc is not None:
can_send_many(can_capnp_to_can_list(tsc.sendcan))
rk.keep_time()
# *** main loop ***
def boardd_proxy_loop(rate=200, address="192.168.2.251"):
rk = Ratekeeper(rate)
context = zmq.Context()
can_init()
# *** subscribes can
logcan = messaging.sub_sock(context, service_list['can'].port, addr=address)
# *** publishes to can send
sendcan = messaging.pub_sock(context, service_list['sendcan'].port)
# drain sendcan to delete any stale messages from previous runs
messaging.drain_sock(sendcan)
while 1:
# recv @ 100hz
can_msgs = can_recv()
#for m in can_msgs:
# print("R: {0} {1}".format(hex(m[0]), str(m[2]).encode("hex")))
# publish to logger
# TODO: refactor for speed
if len(can_msgs) > 0:
dat = can_list_to_can_capnp(can_msgs, "sendcan")
sendcan.send(dat.to_bytes())
# send can if we have a packet
tsc = messaging.recv_sock(logcan)
if tsc is not None:
cl = can_capnp_to_can_list(tsc.can)
#for m in cl:
# print("S: {0} {1}".format(hex(m[0]), str(m[2]).encode("hex")))
can_send_many(cl)
rk.keep_time()
def main(gctx=None):
if os.getenv("MOCK") is not None:
boardd_mock_loop()
elif os.getenv("PROXY") is not None:
boardd_proxy_loop()
elif os.getenv("BOARDTEST") is not None:
boardd_test_loop()
else:
boardd_loop()
if __name__ == "__main__":
main()

@ -0,0 +1,25 @@
# distutils: language = c++
from libcpp.vector cimport vector
from libcpp.string cimport string
from libcpp cimport bool
cdef struct can_frame:
long address
string dat
long busTime
long src
cdef extern void can_list_to_can_capnp_cpp(const vector[can_frame] &can_list, string &out, bool sendCan)
def can_list_to_can_capnp(can_msgs, msgtype='can'):
cdef vector[can_frame] can_list
cdef can_frame f
for can_msg in can_msgs:
f.address = can_msg[0]
f.busTime = can_msg[1]
f.dat = can_msg[2]
f.src = can_msg[3]
can_list.push_back(f)
cdef string out
can_list_to_can_capnp_cpp(can_list, out, msgtype == 'sendcan')
return out

@ -0,0 +1,25 @@
import subprocess
from distutils.core import setup, Extension
from Cython.Build import cythonize
PHONELIBS = '../../phonelibs'
ARCH = subprocess.check_output(["uname", "-m"]).rstrip()
ARCH_DIR = 'x64' if ARCH == "x86_64" else 'aarch64'
setup(name='Boardd API Implementation',
ext_modules=cythonize(
Extension(
"boardd_api_impl",
libraries=[':libcan_list_to_can_capnp.a', ':libcapnp.a', ':libcapnp.a', ':libkj.a'],
library_dirs=[
'./',
PHONELIBS + '/capnp-cpp/' + ARCH_DIR + '/lib/',
PHONELIBS + '/capnp-c/' + ARCH_DIR + '/lib/'
],
sources=['boardd_api_impl.pyx'],
language="c++",
extra_compile_args=["-std=c++11"],
)
)
)

@ -0,0 +1,36 @@
#include <vector>
#include <tuple>
#include <string>
#include "common/timing.h"
#include <capnp/serialize.h>
#include "cereal/gen/cpp/log.capnp.h"
#include "cereal/gen/cpp/car.capnp.h"
typedef struct {
long address;
std::string dat;
long busTime;
long src;
} can_frame;
extern "C" {
void can_list_to_can_capnp_cpp(const std::vector<can_frame> &can_list, std::string &out, bool sendCan) {
capnp::MallocMessageBuilder msg;
cereal::Event::Builder event = msg.initRoot<cereal::Event>();
event.setLogMonoTime(nanos_since_boot());
auto canData = sendCan ? event.initSendcan(can_list.size()) : event.initCan(can_list.size());
int i = 0;
for (auto it = can_list.begin(); it != can_list.end(); it++, i++) {
canData[i].setAddress(it->address);
canData[i].setBusTime(it->busTime);
canData[i].setDat(kj::arrayPtr((uint8_t*)it->dat.data(), it->dat.size()));
canData[i].setSrc(it->src);
}
auto words = capnp::messageToFlatArray(msg);
auto bytes = words.asBytes();
out.append((const char *)bytes.begin(), bytes.size());
}
}

@ -0,0 +1,247 @@
#!/usr/bin/env python
# This file is not used by OpenPilot. Only boardd.cc is used.
# The python version is slower, but has more options for development.
# TODO: merge the extra functionalities of this file (like MOCK) in boardd.c and
# delete this python version of boardd
import os
import struct
import zmq
import time
import selfdrive.messaging as messaging
from common.realtime import Ratekeeper
from selfdrive.services import service_list
from selfdrive.swaglog import cloudlog
from selfdrive.boardd.boardd import can_capnp_to_can_list
# USB is optional
try:
import usb1
from usb1 import USBErrorIO, USBErrorOverflow #pylint: disable=no-name-in-module
except Exception:
pass
SAFETY_NOOUTPUT = 0
SAFETY_HONDA = 1
SAFETY_TOYOTA = 2
SAFETY_CHRYSLER = 9
SAFETY_TOYOTA_NOLIMITS = 0x1336
SAFETY_ALLOUTPUT = 0x1337
# *** serialization functions ***
def can_list_to_can_capnp(can_msgs, msgtype='can'):
dat = messaging.new_message()
dat.init(msgtype, len(can_msgs))
for i, can_msg in enumerate(can_msgs):
if msgtype == 'sendcan':
cc = dat.sendcan[i]
else:
cc = dat.can[i]
cc.address = can_msg[0]
cc.busTime = can_msg[1]
cc.dat = str(can_msg[2])
cc.src = can_msg[3]
return dat
# *** can driver ***
def can_health():
while 1:
try:
dat = handle.controlRead(usb1.TYPE_VENDOR | usb1.RECIPIENT_DEVICE, 0xd2, 0, 0, 0x10)
break
except (USBErrorIO, USBErrorOverflow):
cloudlog.exception("CAN: BAD HEALTH, RETRYING")
v, i, started = struct.unpack("IIB", dat[0:9])
# TODO: units
return {"voltage": v, "current": i, "started": bool(started)}
def __parse_can_buffer(dat):
ret = []
for j in range(0, len(dat), 0x10):
ddat = dat[j:j+0x10]
f1, f2 = struct.unpack("II", ddat[0:8])
ret.append((f1 >> 21, f2>>16, ddat[8:8+(f2&0xF)], (f2>>4)&0xFF))
return ret
def can_send_many(arr):
snds = []
for addr, _, dat, alt in arr:
if addr < 0x800: # only support 11 bit addr
snd = struct.pack("II", ((addr << 21) | 1), len(dat) | (alt << 4)) + dat
snd = snd.ljust(0x10, '\x00')
snds.append(snd)
while 1:
try:
handle.bulkWrite(3, ''.join(snds))
break
except (USBErrorIO, USBErrorOverflow):
cloudlog.exception("CAN: BAD SEND MANY, RETRYING")
def can_recv():
dat = ""
while 1:
try:
dat = handle.bulkRead(1, 0x10*256)
break
except (USBErrorIO, USBErrorOverflow):
cloudlog.exception("CAN: BAD RECV, RETRYING")
return __parse_can_buffer(dat)
def can_init():
global handle, context
handle = None
cloudlog.info("attempting can init")
context = usb1.USBContext()
#context.setDebug(9)
for device in context.getDeviceList(skip_on_error=True):
if device.getVendorID() == 0xbbaa and device.getProductID() == 0xddcc:
handle = device.open()
handle.claimInterface(0)
handle.controlWrite(0x40, 0xdc, SAFETY_ALLOUTPUT, 0, b'')
if handle is None:
cloudlog.warn("CAN NOT FOUND")
exit(-1)
cloudlog.info("got handle")
cloudlog.info("can init done")
def boardd_mock_loop():
context = zmq.Context()
can_init()
handle.controlWrite(0x40, 0xdc, SAFETY_ALLOUTPUT, 0, b'')
logcan = messaging.sub_sock(context, service_list['can'].port)
sendcan = messaging.pub_sock(context, service_list['sendcan'].port)
while 1:
tsc = messaging.drain_sock(logcan, wait_for_one=True)
snds = map(lambda x: can_capnp_to_can_list(x.can), tsc)
snd = []
for s in snds:
snd += s
snd = filter(lambda x: x[-1] <= 1, snd)
can_send_many(snd)
# recv @ 100hz
can_msgs = can_recv()
print("sent %d got %d" % (len(snd), len(can_msgs)))
m = can_list_to_can_capnp(can_msgs, msgtype='sendcan')
sendcan.send(m.to_bytes())
def boardd_test_loop():
can_init()
cnt = 0
while 1:
can_send_many([[0xbb,0,"\xaa\xaa\xaa\xaa",0], [0xaa,0,"\xaa\xaa\xaa\xaa"+struct.pack("!I", cnt),1]])
#can_send_many([[0xaa,0,"\xaa\xaa\xaa\xaa",0]])
#can_send_many([[0xaa,0,"\xaa\xaa\xaa\xaa",1]])
# recv @ 100hz
can_msgs = can_recv()
print("got %d" % (len(can_msgs)))
time.sleep(0.01)
cnt += 1
# *** main loop ***
def boardd_loop(rate=200):
rk = Ratekeeper(rate)
context = zmq.Context()
can_init()
# *** publishes can and health
logcan = messaging.pub_sock(context, service_list['can'].port)
health_sock = messaging.pub_sock(context, service_list['health'].port)
# *** subscribes to can send
sendcan = messaging.sub_sock(context, service_list['sendcan'].port)
# drain sendcan to delete any stale messages from previous runs
messaging.drain_sock(sendcan)
while 1:
# health packet @ 1hz
if (rk.frame%rate) == 0:
health = can_health()
msg = messaging.new_message()
msg.init('health')
# store the health to be logged
msg.health.voltage = health['voltage']
msg.health.current = health['current']
msg.health.started = health['started']
msg.health.controlsAllowed = True
health_sock.send(msg.to_bytes())
# recv @ 100hz
can_msgs = can_recv()
# publish to logger
# TODO: refactor for speed
if len(can_msgs) > 0:
dat = can_list_to_can_capnp(can_msgs).to_bytes()
logcan.send(dat)
# send can if we have a packet
tsc = messaging.recv_sock(sendcan)
if tsc is not None:
can_send_many(can_capnp_to_can_list(tsc.sendcan))
rk.keep_time()
# *** main loop ***
def boardd_proxy_loop(rate=200, address="192.168.2.251"):
rk = Ratekeeper(rate)
context = zmq.Context()
can_init()
# *** subscribes can
logcan = messaging.sub_sock(context, service_list['can'].port, addr=address)
# *** publishes to can send
sendcan = messaging.pub_sock(context, service_list['sendcan'].port)
# drain sendcan to delete any stale messages from previous runs
messaging.drain_sock(sendcan)
while 1:
# recv @ 100hz
can_msgs = can_recv()
#for m in can_msgs:
# print("R: {0} {1}".format(hex(m[0]), str(m[2]).encode("hex")))
# publish to logger
# TODO: refactor for speed
if len(can_msgs) > 0:
dat = can_list_to_can_capnp(can_msgs, "sendcan")
sendcan.send(dat)
# send can if we have a packet
tsc = messaging.recv_sock(logcan)
if tsc is not None:
cl = can_capnp_to_can_list(tsc.can)
#for m in cl:
# print("S: {0} {1}".format(hex(m[0]), str(m[2]).encode("hex")))
can_send_many(cl)
rk.keep_time()
def main(gctx=None):
if os.getenv("MOCK") is not None:
boardd_mock_loop()
elif os.getenv("PROXY") is not None:
boardd_proxy_loop()
elif os.getenv("BOARDTEST") is not None:
boardd_test_loop()
else:
boardd_loop()
if __name__ == "__main__":
main()

@ -0,0 +1,19 @@
#!/usr/bin/env python
import time
import random
from boardd_old import can_init, can_recv, can_send_many, can_health
if __name__ == "__main__":
can_init()
while 1:
c = random.randint(0, 3)
if c == 0:
print can_recv()
elif c == 1:
print can_health()
elif c == 2:
many = [[0x123, 0, "abcdef", 0]] * random.randint(1, 10)
can_send_many(many)
elif c == 3:
time.sleep(random.randint(0, 100) / 1000.0)

@ -0,0 +1,76 @@
import random
import numpy as np
import boardd_old
import selfdrive.boardd.boardd as boardd
from common.realtime import sec_since_boot
from cereal import log
import unittest
def generate_random_can_data_list():
can_list = []
cnt = random.randint(1, 64)
for j in xrange(cnt):
can_data = np.random.bytes(random.randint(1, 8))
can_list.append([random.randint(0, 128), random.randint(0, 128), can_data, random.randint(0, 128)])
return can_list, cnt
class TestBoarddApiMethods(unittest.TestCase):
def test_correctness(self):
for i in xrange(1000):
can_list, _ = generate_random_can_data_list()
# Sendcan
# Old API
m_old = boardd_old.can_list_to_can_capnp(can_list, 'sendcan').to_bytes()
# new API
m = boardd.can_list_to_can_capnp(can_list, 'sendcan')
ev_old = log.Event.from_bytes(m_old)
ev = log.Event.from_bytes(m)
self.assertEqual(ev_old.which(), ev.which())
self.assertEqual(len(ev.sendcan), len(ev_old.sendcan))
for i in xrange(len(ev.sendcan)):
attrs = ['address', 'busTime', 'dat', 'src']
for attr in attrs:
self.assertEqual(getattr(ev.sendcan[i], attr, 'new'), getattr(ev_old.sendcan[i], attr, 'old'))
# Can
m_old = boardd_old.can_list_to_can_capnp(can_list, 'can').to_bytes()
# new API
m = boardd.can_list_to_can_capnp(can_list, 'can')
ev_old = log.Event.from_bytes(m_old)
ev = log.Event.from_bytes(m)
self.assertEqual(ev_old.which(), ev.which())
self.assertEqual(len(ev.can), len(ev_old.can))
for i in xrange(len(ev.can)):
attrs = ['address', 'busTime', 'dat', 'src']
for attr in attrs:
self.assertEqual(getattr(ev.can[i], attr, 'new'), getattr(ev_old.can[i], attr, 'old'))
def test_performance(self):
can_list, cnt = generate_random_can_data_list()
recursions = 1000
n1 = sec_since_boot()
for i in xrange(recursions):
boardd_old.can_list_to_can_capnp(can_list, 'sendcan').to_bytes()
n2 = sec_since_boot()
elapsed_old = n2 - n1
# print('Old API, elapsed time: {} secs'.format(elapsed_old))
n1 = sec_since_boot()
for i in xrange(recursions):
boardd.can_list_to_can_capnp(can_list)
n2 = sec_since_boot()
elapsed_new = n2 - n1
# print('New API, elapsed time: {} secs'.format(elapsed_new))
self.assertTrue(elapsed_new < elapsed_old / 2)
if __name__ == '__main__':
unittest.main()

@ -0,0 +1,51 @@
#!/usr/bin/env python
"""Run boardd with the BOARDD_LOOPBACK envvar before running this test."""
import os
import random
import zmq
import time
from selfdrive.boardd.boardd import can_list_to_can_capnp
from selfdrive.messaging import drain_sock, pub_sock, sub_sock
from selfdrive.services import service_list
def get_test_string():
return b"test"+os.urandom(10)
BUS = 0
def main():
context = zmq.Context()
rcv = sub_sock(context, service_list['can'].port) # port 8006
snd = pub_sock(context, service_list['sendcan'].port) # port 8017
time.sleep(0.3) # wait to bind before send/recv
for i in range(10):
print("Loop %d" % i)
at = random.randint(1024, 2000)
st = get_test_string()[0:8]
snd.send(can_list_to_can_capnp([[at, 0, st, 0]], msgtype='sendcan').to_bytes())
time.sleep(0.1)
res = drain_sock(rcv, True)
assert len(res) == 1
res = res[0].can
assert len(res) == 2
msg0, msg1 = res
assert msg0.dat == st
assert msg1.dat == st
assert msg0.address == at
assert msg1.address == at
assert msg0.src == 0x80 | BUS
assert msg1.src == BUS
print("Success")
if __name__ == "__main__":
main()

@ -23,9 +23,8 @@ ifeq ($(UNAME_S),Darwin)
else ifeq ($(OPTEST),1) else ifeq ($(OPTEST),1)
ZMQ_LIBS = -lzmq ZMQ_LIBS = -lzmq
else ifeq ($(UNAME_M),x86_64) else ifeq ($(UNAME_M),x86_64)
EXTERNAL := ../../external ZMQ_FLAGS = -I$(PHONELIBS)/zmq/x64/include
ZMQ_FLAGS = -I$(EXTERNAL)/zmq/include ZMQ_LIBS = -L$(PHONELIBS)/zmq/x64/lib -l:libzmq.a
ZMQ_LIBS = -L$(EXTERNAL)/zmq/lib -l:libzmq.a
else ifeq ($(UNAME_M),aarch64) else ifeq ($(UNAME_M),aarch64)
ZMQ_FLAGS = -I$(PHONELIBS)/zmq/aarch64/include ZMQ_FLAGS = -I$(PHONELIBS)/zmq/aarch64/include
ZMQ_LIBS = -L$(PHONELIBS)/zmq/aarch64/lib -l:libzmq.a ZMQ_LIBS = -L$(PHONELIBS)/zmq/aarch64/lib -l:libzmq.a
@ -67,6 +66,11 @@ libdbc.so:: $(LIBDBC_OBJS) $(DBC_OBJS)
$(CEREAL_CXXFLAGS) \ $(CEREAL_CXXFLAGS) \
$(CEREAL_LIBS) $(CEREAL_LIBS)
packer_impl.so: packer_impl.pyx packer_setup.py
python packer_setup.py build_ext --inplace
rm -rf build
rm -f packer_impl.cpp
$(OBJDIR)/%.o: %.cc $(OBJDIR)/%.o: %.cc
@echo "[ CXX ] $@" @echo "[ CXX ] $@"
$(CXX) -fPIC -c -o '$@' $^ \ $(CXX) -fPIC -c -o '$@' $^ \

@ -129,4 +129,8 @@ extern "C" {
return cp->pack(address, std::vector<SignalPackValue>(vals, vals+num_vals), counter); return cp->pack(address, std::vector<SignalPackValue>(vals, vals+num_vals), counter);
} }
uint64_t canpack_pack_vector(void* inst, uint32_t address, const std::vector<SignalPackValue> &signals, int counter) {
CANPacker *cp = (CANPacker*)inst;
return cp->pack(address, signals, counter);
}
} }

@ -1,68 +1,9 @@
import six # pylint: skip-file
import struct import os
from selfdrive.can.libdbc_py import libdbc, ffi import subprocess
can_dir = os.path.dirname(os.path.abspath(__file__))
subprocess.check_call(["make", "packer_impl.so"], cwd=can_dir)
class CANPacker(object): from selfdrive.can.packer_impl import CANPacker
def __init__(self, dbc_name): assert CANPacker
self.packer = libdbc.canpack_init(dbc_name)
self.dbc = libdbc.dbc_lookup(dbc_name)
self.sig_names = {}
self.name_to_address_and_size = {}
num_msgs = self.dbc[0].num_msgs
for i in range(num_msgs):
msg = self.dbc[0].msgs[i]
name = ffi.string(msg.name)
address = msg.address
self.name_to_address_and_size[name] = (address, msg.size)
self.name_to_address_and_size[address] = (address, msg.size)
def pack(self, addr, values, counter):
values_thing = []
for name, value in six.iteritems(values):
if name not in self.sig_names:
self.sig_names[name] = ffi.new("char[]", name)
values_thing.append({
'name': self.sig_names[name],
'value': value
})
values_c = ffi.new("SignalPackValue[]", values_thing)
return libdbc.canpack_pack(self.packer, addr, len(values_thing), values_c, counter)
def pack_bytes(self, addr, values, counter=-1):
addr, size = self.name_to_address_and_size[addr]
val = self.pack(addr, values, counter)
r = struct.pack(">Q", val)
return addr, r[:size]
def make_can_msg(self, addr, bus, values, counter=-1):
addr, msg = self.pack_bytes(addr, values, counter)
return [addr, 0, msg, bus]
if __name__ == "__main__":
## little endian test
cp = CANPacker("hyundai_santa_fe_2019_ccan")
s = cp.pack_bytes(0x340, {
"CR_Lkas_StrToqReq": -0.06,
#"CF_Lkas_FcwBasReq": 1,
"CF_Lkas_MsgCount": 7,
"CF_Lkas_HbaSysState": 0,
#"CF_Lkas_Chksum": 3,
})
s = cp.pack_bytes(0x340, {
"CF_Lkas_MsgCount": 1,
})
# big endian test
#cp = CANPacker("honda_civic_touring_2016_can_generated")
#s = cp.pack_bytes(0xe4, {
# "STEER_TORQUE": -2,
#})
print([hex(ord(v)) for v in s[1]])
print(s[1].encode("hex"))

@ -0,0 +1,111 @@
# distutils: language = c++
from libc.stdint cimport uint32_t, uint64_t
from libcpp.vector cimport vector
from libcpp.map cimport map
from libcpp.string cimport string
from libcpp cimport bool
from posix.dlfcn cimport dlopen, dlsym, RTLD_LAZY
import os
import subprocess
cdef struct SignalPackValue:
const char* name
double value
ctypedef enum SignalType:
DEFAULT,
HONDA_CHECKSUM,
HONDA_COUNTER,
TOYOTA_CHECKSUM,
PEDAL_CHECKSUM,
PEDAL_COUNTER
cdef struct Signal:
const char* name
int b1, b2, bo
bool is_signed
double factor, offset
SignalType type
cdef struct Msg:
const char* name
uint32_t address
unsigned int size
size_t num_sigs
const Signal *sigs
cdef struct Val:
const char* name
uint32_t address
const char* def_val
const Signal *sigs
cdef struct DBC:
const char* name
size_t num_msgs
const Msg *msgs
const Val *vals
size_t num_vals
ctypedef void * (*canpack_init_func)(const char* dbc_name)
ctypedef uint64_t (*canpack_pack_vector_func)(void* inst, uint32_t address, const vector[SignalPackValue] &signals, int counter)
ctypedef const DBC * (*dbc_lookup_func)(const char* dbc_name)
cdef class CANPacker(object):
cdef void *packer
cdef const DBC *dbc
cdef map[string, (int, int)] name_to_address_and_size
cdef map[int, int] address_to_size
cdef canpack_init_func canpack_init
cdef canpack_pack_vector_func canpack_pack_vector
cdef dbc_lookup_func dbc_lookup
def __init__(self, dbc_name):
can_dir = os.path.dirname(os.path.abspath(__file__))
libdbc_fn = os.path.join(can_dir, "libdbc.so")
subprocess.check_call(["make"], cwd=can_dir)
cdef void *libdbc = dlopen(libdbc_fn, RTLD_LAZY)
self.canpack_init = <canpack_init_func>dlsym(libdbc, 'canpack_init')
self.canpack_pack_vector = <canpack_pack_vector_func>dlsym(libdbc, 'canpack_pack_vector')
self.dbc_lookup = <dbc_lookup_func>dlsym(libdbc, 'dbc_lookup')
self.packer = self.canpack_init(dbc_name)
self.dbc = self.dbc_lookup(dbc_name)
num_msgs = self.dbc[0].num_msgs
for i in range(num_msgs):
msg = self.dbc[0].msgs[i]
self.name_to_address_and_size[string(msg.name)] = (msg.address, msg.size)
self.address_to_size[msg.address] = msg.size
cdef uint64_t pack(self, addr, values, counter):
cdef vector[SignalPackValue] values_thing
cdef SignalPackValue spv
for name, value in values.iteritems():
spv.name = name
spv.value = value
values_thing.push_back(spv)
return self.canpack_pack_vector(self.packer, addr, values_thing, counter)
cdef inline uint64_t ReverseBytes(self, uint64_t x):
return (((x & 0xff00000000000000ull) >> 56) |
((x & 0x00ff000000000000ull) >> 40) |
((x & 0x0000ff0000000000ull) >> 24) |
((x & 0x000000ff00000000ull) >> 8) |
((x & 0x00000000ff000000ull) << 8) |
((x & 0x0000000000ff0000ull) << 24) |
((x & 0x000000000000ff00ull) << 40) |
((x & 0x00000000000000ffull) << 56))
cpdef make_can_msg(self, name_or_addr, bus, values, counter=-1):
cdef int addr, size
if type(name_or_addr) == int:
addr = name_or_addr
size = self.address_to_size[name_or_addr]
else:
addr, size = self.name_to_address_and_size[name_or_addr]
cdef uint64_t val = self.pack(addr, values, counter)
val = self.ReverseBytes(val)
return [addr, 0, (<char *>&val)[:size], bus]

@ -0,0 +1,5 @@
from distutils.core import setup, Extension
from Cython.Build import cythonize
setup(name='CAN Packer API Implementation',
ext_modules=cythonize(Extension("packer_impl", ["packer_impl.pyx"], language="c++", extra_compile_args=["-std=c++11"])))

@ -1,4 +1,3 @@
import six
import time import time
from collections import defaultdict from collections import defaultdict
import numbers import numbers
@ -59,7 +58,7 @@ class CANParser(object):
{ {
'address': msg_address, 'address': msg_address,
'check_frequency': freq, 'check_frequency': freq,
} for msg_address, freq in six.iteritems(message_options)]) } for msg_address, freq in message_options.items()])
self.can = libdbc.can_init(bus, dbc_name, len(message_options_c), message_options_c, self.can = libdbc.can_init(bus, dbc_name, len(message_options_c), message_options_c,
len(signal_options_c), signal_options_c, sendcan, tcp_addr) len(signal_options_c), signal_options_c, sendcan, tcp_addr)

@ -2,7 +2,7 @@
import os import os
import glob import glob
import sys import sys
import six
import jinja2 import jinja2
from collections import Counter from collections import Counter
@ -39,10 +39,10 @@ def main():
continue #skip output is newer than template and dbc continue #skip output is newer than template and dbc
msgs = [(address, msg_name, msg_size, sorted(msg_sigs, key=lambda s: s.name not in ("COUNTER", "CHECKSUM"))) # process counter and checksums first msgs = [(address, msg_name, msg_size, sorted(msg_sigs, key=lambda s: s.name not in ("COUNTER", "CHECKSUM"))) # process counter and checksums first
for address, ((msg_name, msg_size), msg_sigs) in sorted(six.iteritems(can_dbc.msgs)) if msg_sigs] for address, ((msg_name, msg_size), msg_sigs) in sorted(can_dbc.msgs.items()) if msg_sigs]
def_vals = {a: set(b) for a,b in can_dbc.def_vals.items()} #remove duplicates def_vals = {a: set(b) for a,b in can_dbc.def_vals.items()} #remove duplicates
def_vals = [(address, sig) for address, sig in sorted(six.iteritems(def_vals))] def_vals = [(address, sig) for address, sig in sorted(def_vals.items())]
if can_dbc.name.startswith("honda") or can_dbc.name.startswith("acura"): if can_dbc.name.startswith("honda") or can_dbc.name.startswith("acura"):
checksum_type = "honda" checksum_type = "honda"

@ -0,0 +1,67 @@
import struct
from selfdrive.can.libdbc_py import libdbc, ffi
class CANPacker(object):
def __init__(self, dbc_name):
self.packer = libdbc.canpack_init(dbc_name)
self.dbc = libdbc.dbc_lookup(dbc_name)
self.sig_names = {}
self.name_to_address_and_size = {}
num_msgs = self.dbc[0].num_msgs
for i in range(num_msgs):
msg = self.dbc[0].msgs[i]
name = ffi.string(msg.name)
address = msg.address
self.name_to_address_and_size[name] = (address, msg.size)
self.name_to_address_and_size[address] = (address, msg.size)
def pack(self, addr, values, counter):
values_thing = []
for name, value in values.iteritems():
if name not in self.sig_names:
self.sig_names[name] = ffi.new("char[]", name)
values_thing.append({
'name': self.sig_names[name],
'value': value
})
values_c = ffi.new("SignalPackValue[]", values_thing)
return libdbc.canpack_pack(self.packer, addr, len(values_thing), values_c, counter)
def pack_bytes(self, addr, values, counter=-1):
addr, size = self.name_to_address_and_size[addr]
val = self.pack(addr, values, counter)
r = struct.pack(">Q", val)
return addr, r[:size]
def make_can_msg(self, addr, bus, values, counter=-1):
addr, msg = self.pack_bytes(addr, values, counter)
return [addr, 0, msg, bus]
if __name__ == "__main__":
## little endian test
cp = CANPacker("hyundai_santa_fe_2019_ccan")
s = cp.pack_bytes(0x340, {
"CR_Lkas_StrToqReq": -0.06,
#"CF_Lkas_FcwBasReq": 1,
"CF_Lkas_MsgCount": 7,
"CF_Lkas_HbaSysState": 0,
#"CF_Lkas_Chksum": 3,
})
s = cp.pack_bytes(0x340, {
"CF_Lkas_MsgCount": 1,
})
# big endian test
#cp = CANPacker("honda_civic_touring_2016_can_generated")
#s = cp.pack_bytes(0xe4, {
# "STEER_TORQUE": -2,
#})
print([hex(ord(v)) for v in s[1]])
print(s[1].encode("hex"))

@ -0,0 +1,35 @@
import unittest
import random
from selfdrive.can.tests.packer_old import CANPacker as CANPackerOld
from selfdrive.can.packer import CANPacker
import selfdrive.car.chrysler.chryslercan as chryslercan
class TestPackerMethods(unittest.TestCase):
def setUp(self):
self.chrysler_cp_old = CANPackerOld("chrysler_pacifica_2017_hybrid")
self.chrysler_cp = CANPacker("chrysler_pacifica_2017_hybrid")
def test_correctness(self):
# Test all commands, randomize the params.
for _ in xrange(1000):
gear = ('drive', 'reverse', 'low')[random.randint(0, 3) % 3]
lkas_active = (random.randint(0, 2) % 2 == 0)
hud_alert = random.randint(0, 6)
hud_count = random.randint(0, 65536)
lkas_car_model = random.randint(0, 65536)
m_old = chryslercan.create_lkas_hud(self.chrysler_cp_old, gear, lkas_active, hud_alert, hud_count, lkas_car_model)
m = chryslercan.create_lkas_hud(self.chrysler_cp, gear, lkas_active, hud_alert, hud_count, lkas_car_model)
self.assertEqual(m_old, m)
apply_steer = (random.randint(0, 2) % 2 == 0)
moving_fast = (random.randint(0, 2) % 2 == 0)
frame = random.randint(0, 65536)
m_old = chryslercan.create_lkas_command(self.chrysler_cp_old, apply_steer, moving_fast, frame)
m = chryslercan.create_lkas_command(self.chrysler_cp, apply_steer, moving_fast, frame)
self.assertEqual(m_old, m)
if __name__ == "__main__":
unittest.main()

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

Loading…
Cancel
Save