mirror of
https://github.com/dragonpilot/dragonpilot.git
synced 2026-06-12 05:44:14 +08:00
Compare commits
122 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
28c0797d30 | ||
|
|
a701aa7292 | ||
|
|
433f934783 | ||
|
|
db67cffb4d | ||
|
|
0dc6778548 | ||
|
|
c86b52698b | ||
|
|
e5e5aa7ded | ||
|
|
4474b9b371 | ||
|
|
26e966852c | ||
|
|
75ac92b90f | ||
|
|
96f8e5158e | ||
|
|
b68f3f7781 | ||
|
|
36bef17224 | ||
|
|
715771bcff | ||
|
|
8e264baaa2 | ||
|
|
8ff147de6d | ||
|
|
71057c586f | ||
|
|
fac22f4038 | ||
|
|
40094779d7 | ||
|
|
2552aa45c3 | ||
|
|
c2972a80f9 | ||
|
|
791a440709 | ||
|
|
13145dd9a7 | ||
|
|
e9c477dcf2 | ||
|
|
8eebd19884 | ||
|
|
a54b35a4d6 | ||
|
|
8a6f7a5e78 | ||
|
|
6752959d4a | ||
|
|
da52d065a4 | ||
|
|
b731b7cf12 | ||
|
|
5f014635e1 | ||
|
|
ef3d8314d3 | ||
|
|
7ef3fd567f | ||
|
|
b773e27ad1 | ||
|
|
a77c0a1098 | ||
|
|
c7b5fb9116 | ||
|
|
57b9ddf20e | ||
|
|
b8085e2c42 | ||
|
|
c251b312d8 | ||
|
|
14fb17e22f | ||
|
|
a34c87ab46 | ||
|
|
1ad9cc8c67 | ||
|
|
5627d0d7fd | ||
|
|
2cfdbefde8 | ||
|
|
7dabcdace8 | ||
|
|
187a70f760 | ||
|
|
48303589e9 | ||
|
|
8385b27cad | ||
|
|
abd75aedd7 | ||
|
|
daf54ad54d | ||
|
|
94fe677f91 | ||
|
|
3de85098e5 | ||
|
|
5524dc8773 | ||
|
|
19dd5f3e32 | ||
|
|
99cb610b12 | ||
|
|
9d3963559a | ||
|
|
1b8c44b506 | ||
|
|
6f46f988d9 | ||
|
|
68485aa4e4 | ||
|
|
1581fdc198 | ||
|
|
317ae0fb37 | ||
|
|
5bf4196aed | ||
|
|
38aa03e0f7 | ||
|
|
9a9dc3ab23 | ||
|
|
e4aa959e2c | ||
|
|
2aa9a56f40 | ||
|
|
721ed4ec0e | ||
|
|
70be4ceab1 | ||
|
|
5cf91d0496 | ||
|
|
6fee0bdb2d | ||
|
|
e40c161125 | ||
|
|
97eb55cc55 | ||
|
|
65134be0d1 | ||
|
|
dbf71a23aa | ||
|
|
7fec3db1d6 | ||
|
|
26b573c1d0 | ||
|
|
5ec1e7307e | ||
|
|
8bc36b7f21 | ||
|
|
3b909eb693 | ||
|
|
57e39c4472 | ||
|
|
ff7672339c | ||
|
|
6e824a2c22 | ||
|
|
32fa49e093 | ||
|
|
2250eac58f | ||
|
|
97be6b3a0e | ||
|
|
942655c947 | ||
|
|
615db3f7fd | ||
|
|
0bb75c5389 | ||
|
|
7b5ee81d2d | ||
|
|
7fe46f1e1d | ||
|
|
50c0d1c9da | ||
|
|
6dbf544d06 | ||
|
|
41e3a0f699 | ||
|
|
c5d8aec28b | ||
|
|
4653a9aef0 | ||
|
|
ab3492bb90 | ||
|
|
ed7cbb3866 | ||
|
|
a30626cfe3 | ||
|
|
d2c087b3e2 | ||
|
|
0a747f991d | ||
|
|
f5a1e86d85 | ||
|
|
693bcb0f83 | ||
|
|
0d0daed86e | ||
|
|
95a349abcc | ||
|
|
c6ba5dc539 | ||
|
|
6c3afeec0f | ||
|
|
29c58b4588 | ||
|
|
ecc565aa3f | ||
|
|
db61810f98 | ||
|
|
48f203ad5b | ||
|
|
6ab4ac2dfb | ||
|
|
9cb3c7b6e6 | ||
|
|
adaa4ed350 | ||
|
|
a64b9aa9b8 | ||
|
|
0138eca61d | ||
|
|
139a40de29 | ||
|
|
17d9becd3c | ||
|
|
449b482cc3 | ||
|
|
a7e099c946 | ||
|
|
610462be5a | ||
|
|
207d32668f | ||
|
|
e94a30bec0 |
28
.gitignore
vendored
28
.gitignore
vendored
@@ -1,2 +1,30 @@
|
||||
.DS_Store
|
||||
.tags
|
||||
.ipynb_checkpoints
|
||||
.idea
|
||||
model2.png
|
||||
|
||||
*.DSYM
|
||||
*.d
|
||||
*.pyc
|
||||
*.pyo
|
||||
.*.swp
|
||||
.*.swo
|
||||
.*.un~
|
||||
*.o
|
||||
*.so
|
||||
*.a
|
||||
*.clb
|
||||
*.class
|
||||
*.pyxbldc
|
||||
*.vcd
|
||||
config.json
|
||||
clcache
|
||||
|
||||
board/obj/
|
||||
selfdrive/boardd/boardd
|
||||
selfdrive/logcatd/logcatd
|
||||
selfdrive/proclogd/proclogd
|
||||
selfdrive/ui/ui
|
||||
/src/
|
||||
|
||||
|
||||
12
.travis.yml
Normal file
12
.travis.yml
Normal file
@@ -0,0 +1,12 @@
|
||||
sudo: required
|
||||
|
||||
services:
|
||||
- docker
|
||||
|
||||
install:
|
||||
- docker build -t tmppilot -f Dockerfile.openpilot .
|
||||
|
||||
script:
|
||||
- docker run --rm
|
||||
-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'
|
||||
19
Dockerfile.openpilot
Normal file
19
Dockerfile.openpilot
Normal file
@@ -0,0 +1,19 @@
|
||||
FROM ubuntu:16.04
|
||||
ENV PYTHONUNBUFFERED 1
|
||||
|
||||
RUN apt-get update && apt-get install -y build-essential clang vim screen wget bzip2 git libglib2.0-0 python-pip capnproto libcapnp-dev libzmq5-dev libffi-dev libusb-1.0-0
|
||||
RUN pip install numpy==1.11.2 scipy==0.18.1 matplotlib
|
||||
|
||||
COPY requirements_openpilot.txt /tmp/
|
||||
RUN pip install -r /tmp/requirements_openpilot.txt
|
||||
|
||||
ENV PYTHONPATH /tmp/openpilot:$PYTHONPATH
|
||||
|
||||
COPY ./common /tmp/openpilot/common
|
||||
COPY ./cereal /tmp/openpilot/cereal
|
||||
COPY ./opendbc /tmp/openpilot/opendbc
|
||||
COPY ./selfdrive /tmp/openpilot/selfdrive
|
||||
COPY ./phonelibs /tmp/openpilot/phonelibs
|
||||
COPY ./pyextra /tmp/openpilot/pyextra
|
||||
|
||||
RUN mkdir -p /tmp/openpilot/selfdrive/test/out
|
||||
7
LICENSE.openpilot
Normal file
7
LICENSE.openpilot
Normal file
@@ -0,0 +1,7 @@
|
||||
Copyright (c) 2016, Comma.ai, Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
9
Makefile
Normal file
9
Makefile
Normal file
@@ -0,0 +1,9 @@
|
||||
|
||||
code_dir := $(shell pwd)
|
||||
|
||||
# TODO: Add a global build system
|
||||
|
||||
.PHONY: all
|
||||
all:
|
||||
cd selfdrive && PYTHONPATH=$(code_dir) PREPAREONLY=1 ./manager.py
|
||||
|
||||
176
README.md
176
README.md
@@ -1,53 +1,153 @@
|
||||
opendbc
|
||||
Welcome to openpilot
|
||||
======
|
||||
|
||||
The project to democratize access to the decoder ring of your car.
|
||||
[openpilot](http://github.com/commaai/openpilot) is an open source driving agent.
|
||||
|
||||
Currently it performs the functions of Adaptive Cruise Control (ACC) and Lane Keeping Assist System (LKAS) for Hondas, Acuras and Toyotas. It's about on par with Tesla Autopilot at launch, and better than [all other manufacturers](http://www.thedrive.com/tech/5707/the-war-for-autonomous-driving-part-iii-us-vs-germany-vs-japan).
|
||||
|
||||
The openpilot codebase has been written to be concise and enable rapid prototyping. We look forward to your contributions - improving real vehicle automation has never been easier.
|
||||
|
||||
### DBC file basics
|
||||
Here are [some](https://www.youtube.com/watch?v=9OwTJFuDI7g) [videos](https://www.youtube.com/watch?v=64Wvt5pYQmE) [of](https://www.youtube.com/watch?v=6IW7Nejsr3A) [it](https://www.youtube.com/watch?v=-VN1YcC83nA) [running](https://www.youtube.com/watch?v=EQJZvVeihZk). And a really cool [tutorial](https://www.youtube.com/watch?v=PwOnsT2UW5o).
|
||||
|
||||
A DBC file encodes, in a humanly readable way, the information needed to understand a vehicle's CAN bus traffic. A vehicle might have multiple CAN buses and every CAN bus is represented by its own dbc file.
|
||||
Wondering what's the DBC file format? [Here](http://www.socialledge.com/sjsu/index.php?title=DBC_Format) a good overview.
|
||||
Hardware
|
||||
------
|
||||
|
||||
### How to start reverse engineering cars
|
||||
Right now openpilot supports the [NEO research platform](http://github.com/commaai/neo) and the [EON Dashcam DevKit](https://shop.comma.ai/products/eon-dashcam-devkit). We'd like to support other platforms as well.
|
||||
|
||||
[opendbc](https://github.com/commaai/opendbc) is integrated with [cabana](https://community.comma.ai/cabana/).
|
||||
Install openpilot on a neo device by entering ``https://openpilot.comma.ai`` during NEOS setup.
|
||||
|
||||
Use [panda](https://github.com/commaai/panda) to connect your car to a computer.
|
||||
Supported Cars
|
||||
------
|
||||
|
||||
### DBC file preprocessor
|
||||
- Acura ILX 2016 with AcuraWatch Plus
|
||||
- Due to use of the cruise control for gas, it can only be enabled above 25 mph
|
||||
|
||||
DBC files for different models of the same brand have a lot of overlap. Therefore, we wrote a preprocessor to create DBC files from a brand DBC file and a model specific DBC file. The source DBC files can be found in the generator folder. After changing one of the files run the generator.py script to regenerate the output files. These output files will be placed in the root of the opendbc repository and are suffixed by _generated.
|
||||
- Honda Civic 2016-2017 with Honda Sensing
|
||||
- Due to limitations in steering firmware, steering is disabled below 12 mph
|
||||
- Note that the hatchback model is not supported
|
||||
|
||||
### Good practices for contributing to opendbc
|
||||
- Honda CR-V Touring 2015-2016
|
||||
- Can only be enabled above 25 mph
|
||||
|
||||
- Comments: the best way to store comments is to add them directly to the DBC files. For example:
|
||||
```
|
||||
CM_ SG_ 490 LONG_ACCEL "wheel speed derivative, noisy and zero snapping";
|
||||
```
|
||||
is a comment that refers to signal `LONG_ACCEL` in message `490`. Using comments is highly recommended, especially for doubts and uncertainties. [cabana](https://community.comma.ai/cabana/) can easily display/add/edit comments to signals and messages.
|
||||
- Honda Odyssey 2018 with Honda Sensing (alpha!)
|
||||
- Can only be enabled above 25 mph
|
||||
|
||||
- Units: when applicable, it's recommended to convert signals into physical units, by using a proper signal factor. Using a SI unit is preferred, unless a non-SI unit rounds the signal factor much better.
|
||||
For example:
|
||||
```
|
||||
SG_ VEHICLE_SPEED : 7|15@0+ (0.00278,0) [0|70] "m/s" PCM
|
||||
```
|
||||
is better than:
|
||||
```
|
||||
SG_ VEHICLE_SPEED : 7|15@0+ (0.00620,0) [0|115] "mph" PCM
|
||||
```
|
||||
However, the cleanest option is really:
|
||||
```
|
||||
SG_ VEHICLE_SPEED : 7|15@0+ (0.01,0) [0|250] "kph" PCM
|
||||
```
|
||||
- Acura RDX 2018 with AcuraWatch Plus (alpha!)
|
||||
- Can only be enabled above 25 mph
|
||||
|
||||
- Signal's size: always use the smallest amount of bits possible. For example, let's say I'm reverse engineering the gas pedal position and I've determined that it's in a 3 bytes message. For 0% pedal position I read a message value of `0x00 0x00 0x00`, while for 100% of pedal position I read `0x64 0x00 0x00`: clearly, the gas pedal position is within the first byte of the message and I might be tempted to define the signal `GAS_POS` as:
|
||||
```
|
||||
SG_ GAS_POS : 7|8@0+ (1,0) [0|100] "%" PCM
|
||||
```
|
||||
However, I can't be sure that the very first bit of the message is referred to the pedal position: I haven't seen it changing! Therefore, a safer way of defining the signal is:
|
||||
```
|
||||
SG_ GAS_POS : 6|7@0+ (1,0) [0|100] "%" PCM
|
||||
```
|
||||
which leaves the first bit unallocated. This prevents from very erroneous reading of the gas pedal position, in case the first bit is indeed used for something else.
|
||||
- Toyota RAV-4 2016+ non-hybrid with TSS-P
|
||||
- By default it uses stock Toyota ACC for longitudinal control
|
||||
- openpilot longitudinal control available after unplugging the [Driving Support ECU](https://community.comma.ai/wiki/index.php/Toyota#Rav4_.28for_openpilot.29) and can be enabled above 20 mph
|
||||
|
||||
- Toyota Prius 2017 (alpha!)
|
||||
- By default it uses stock Toyota ACC for longitudinal control
|
||||
- openpilot longitudinal control available after unplugging the [Driving Support ECU](https://community.comma.ai/wiki/index.php/Toyota#Prius_.28for_openpilot.29)
|
||||
- Lateral control needs improvements
|
||||
|
||||
- Toyota RAV-4 2017 hybrid (alpha!)
|
||||
- By default it uses stock Toyota ACC for longitudinal control
|
||||
- openpilot longitudinal control available after unplugging the [Driving Support ECU](https://community.comma.ai/wiki/index.php/Toyota#Rav4_.28for_openpilot.29) and can do stop and go
|
||||
|
||||
- Toyota Corolla 2017 (alpha!)
|
||||
- By default it uses stock Toyota ACC for longitudinal control
|
||||
- openpilot longitudinal control available after unplugging the [Driving Support ECU](https://community.comma.ai/wiki/index.php/Toyota#Corolla_.28for_openpilot.29) and can be enabled above 20 mph
|
||||
|
||||
- Lexus RX 2017 hybrid (alpha!)
|
||||
- By default it uses stock Lexus ACC for longitudinal control
|
||||
- openpilot longitudinal control available after unplugging the [Driving Support ECU](https://community.comma.ai/wiki/index.php/Toyota#Lexus_RX_hybrid)
|
||||
|
||||
In Progress Cars
|
||||
------
|
||||
- All TSS-P Toyota with Steering Assist.
|
||||
- 'Full Speed Range Dynamic Radar Cruise Control' is required to enable stop-and-go. Only the Prius, Camry and C-HR have this option.
|
||||
- Even though the Tundra, Sequoia and the Land Cruiser have TSS-P, they don't have Steering Assist and are not supported.
|
||||
- All LSS-P Lexus with Steering Assist or Lane Keep Assist.
|
||||
- 'All-Speed Range Dynamic Radar Cruise Control' is required to enable stop-and-go. Only the GS, GSH, GS, F, RX, RXH, LX, NX, NXH, LC, LCH, LS, LSH have this option.
|
||||
- Even though the LX have TSS-P, it does not have Steering Assist and is not supported.
|
||||
|
||||
Community WIP Cars
|
||||
------
|
||||
|
||||
- [Chevy Volt 2016-2018 Premier with Driver Confidence II](https://github.com/commaai/openpilot/pull/104)
|
||||
|
||||
- [Classic Tesla Model S (pre-AP)](https://github.com/commaai/openpilot/pull/145)
|
||||
|
||||
- [Honda Pilot 2017 with Honda Sensing](https://github.com/commaai/openpilot/pull/161)
|
||||
|
||||
Directory structure
|
||||
------
|
||||
|
||||
- cereal -- The messaging spec used for all logs on the phone
|
||||
- common -- Library like functionality we've developed here
|
||||
- opendbc -- Files showing how to interpret data from cars
|
||||
- panda -- Code used to communicate on CAN and LIN
|
||||
- phonelibs -- Libraries used on the phone
|
||||
- selfdrive -- Code needed to drive the car
|
||||
- assets -- Fonts for ui
|
||||
- boardd -- Daemon to talk to the board
|
||||
- car -- Code that talks to the car and implements CarInterface
|
||||
- common -- Shared C/C++ code for the daemons
|
||||
- controls -- Python controls (PID loops etc) for the car
|
||||
- debug -- Tools to help you debug and do car ports
|
||||
- logcatd -- Android logcat as a service
|
||||
- loggerd -- Logger and uploader of car data
|
||||
- proclogd -- Logs information from proc
|
||||
- sensord -- IMU / GPS interface code
|
||||
- test/plant -- Car simulator running code through virtual maneuvers
|
||||
- ui -- The UI
|
||||
- visiond -- embedded vision pipeline
|
||||
|
||||
To understand how the services interact, see `selfdrive/service_list.yaml`
|
||||
|
||||
Testing on PC
|
||||
------
|
||||
|
||||
There is rudimentary infrastructure to run a basic simulation and generate a report of openpilot's behavior in different scenarios.
|
||||
|
||||
```bash
|
||||
# Requires working docker
|
||||
./run_docker_tests.sh
|
||||
```
|
||||
|
||||
The results are written to `selfdrive/test/plant/out/index.html`
|
||||
|
||||
More extensive testing infrastructure and simulation environments are coming soon.
|
||||
|
||||
Adding Car Support
|
||||
------
|
||||
|
||||
comma.ai offers [bounties](http://comma.ai/bounties.html) for adding additional car support.
|
||||
|
||||
CR-V Touring support came in through this program. Chevy Volt is close. Accord is close as well.
|
||||
|
||||
User Data / chffr Account / Crash Reporting
|
||||
------
|
||||
|
||||
By default openpilot creates an account and includes a client for chffr, our dashcam app. We use your data to train better models and improve openpilot for everyone.
|
||||
|
||||
It's open source software, so you are free to disable it if you wish.
|
||||
|
||||
It logs the road facing camera, CAN, GPS, IMU, magnetometer, thermal sensors, crashes, and operating system logs.
|
||||
It does not log the user facing camera or the microphone.
|
||||
|
||||
By using it, you agree to [our privacy policy](https://beta.comma.ai/privacy.html). You understand that use of this software or its related services will generate certain types of user data, which may be logged and stored at the sole discretion of comma.ai. By accepting this agreement, you grant an irrevocable, perpetual, worldwide right to comma.ai for the use of this data.
|
||||
|
||||
Contributing
|
||||
------
|
||||
|
||||
We welcome both pull requests and issues on
|
||||
[github](http://github.com/commaai/openpilot). See the TODO file for a list of
|
||||
good places to start.
|
||||
|
||||
Want to get paid to work on openpilot? [comma.ai is hiring](http://comma.ai/positions.html)
|
||||
|
||||
Licensing
|
||||
------
|
||||
|
||||
openpilot is released under the MIT license.
|
||||
|
||||
Any user of this software shall indemnify and hold harmless Comma.ai, Inc. and its directors, officers, employees, agents, stockholders, affiliates, subcontractors and customers from and against all allegations, claims, actions, suits, demands, damages, liabilities, obligations, losses, settlements, judgments, costs and expenses (including without limitation attorneys’ fees and costs) which arise out of, relate to or result from any use of this software by user.
|
||||
|
||||
**THIS IS ALPHA QUALITY SOFTWARE FOR RESEARCH PURPOSES ONLY. THIS IS NOT A PRODUCT.
|
||||
YOU ARE RESPONSIBLE FOR COMPLYING WITH LOCAL LAWS AND REGULATIONS.
|
||||
NO WARRANTY EXPRESSED OR IMPLIED.**
|
||||
|
||||
177
RELEASES.md
Normal file
177
RELEASES.md
Normal file
@@ -0,0 +1,177 @@
|
||||
Version 0.4.2 (2018-02-05)
|
||||
==========================
|
||||
* Add alpha support for 2017 Lexus RX Hybrid
|
||||
* Add alpha support for 2018 ACURA RDX
|
||||
* Updated fingerprint to include Toyota Rav4 SE and Prius Prime
|
||||
* Bugfixes for Acura ILX and Honda Odyssey
|
||||
|
||||
Version 0.4.1 (2018-01-30)
|
||||
==========================
|
||||
* Add alpha support for 2017 Toyota Corolla
|
||||
* Add alpha support for 2018 Honda Odyssey with Honda Sensing
|
||||
* Add alpha support for Grey Panda
|
||||
* Refactored car abstraction layer to make car ports easier
|
||||
* Increased steering torque limit on Honda CR-V by 30%
|
||||
|
||||
Version 0.4.0.2 (2018-01-18)
|
||||
==========================
|
||||
* Add focus adjustment slider
|
||||
* Minor bugfixes
|
||||
|
||||
Version 0.4.0.1 (2017-12-21)
|
||||
==========================
|
||||
* New UI to match chffrplus
|
||||
* Improved lateral control tuning to fix oscillations on Civic
|
||||
* Add alpha support for 2017 Toyota Rav4 Hybrid
|
||||
* Reduced CPU usage
|
||||
* Removed unnecessary utilization of fan at max speed
|
||||
* Minor bug fixes
|
||||
|
||||
Version 0.3.9 (2017-11-21)
|
||||
==========================
|
||||
* Add alpha support for 2017 Toyota Prius
|
||||
* Improved longitudinal control using model predictive control
|
||||
* Enable Forward Collision Warning
|
||||
* Acura ILX now maintains openpilot engaged at standstill when brakes are applied
|
||||
|
||||
Version 0.3.8.2 (2017-10-30)
|
||||
==========================
|
||||
* Add alpha support for 2017 Toyota RAV4
|
||||
* Smoother lateral control
|
||||
* Stay silent if stock system is connected through giraffe
|
||||
* Minor bug fixes
|
||||
|
||||
Version 0.3.7 (2017-09-30)
|
||||
==========================
|
||||
* Improved lateral control using model predictive control
|
||||
* Improved lane centering
|
||||
* Improved GPS
|
||||
* Reduced tendency of path deviation near right side exits
|
||||
* Enable engagement while the accelerator pedal is pressed
|
||||
* Enable engagement while the brake pedal is pressed, when stationary and with lead vehicle within 5m
|
||||
* Disable engagement when park brake or brake hold are active
|
||||
* Fixed sporadic longitudinal pulsing in Civic
|
||||
* Cleanups to vehicle interface
|
||||
|
||||
Version 0.3.6.1 (2017-08-15)
|
||||
============================
|
||||
* Mitigate low speed steering oscillations on some vehicles
|
||||
* Include board steering check for CR-V
|
||||
|
||||
Version 0.3.6 (2017-08-08)
|
||||
==========================
|
||||
* Fix alpha CR-V support
|
||||
* Improved GPS
|
||||
* Fix display of target speed not always matching HUD
|
||||
* Increased acceleration after stop
|
||||
* Mitigated some vehicles driving too close to the right line
|
||||
|
||||
Version 0.3.5 (2017-07-30)
|
||||
==========================
|
||||
* Fix bug where new devices would not begin calibration
|
||||
* Minor robustness improvements
|
||||
|
||||
Version 0.3.4 (2017-07-28)
|
||||
==========================
|
||||
* Improved model trained on more data
|
||||
* Much improved controls tuning
|
||||
* Performance improvements
|
||||
* Bugfixes and improvements to calibration
|
||||
* Driving log can play back video
|
||||
* Acura only: system now stays engaged below 25mph as long as brakes are applied
|
||||
|
||||
Version 0.3.3 (2017-06-28)
|
||||
===========================
|
||||
* Improved model trained on more data
|
||||
* Alpha CR-V support thanks to energee and johnnwvs!
|
||||
* Using the opendbc project for DBC files
|
||||
* Minor performance improvements
|
||||
* UI update thanks to pjlao307
|
||||
* Power off button
|
||||
* 6% more torque on the Civic
|
||||
|
||||
Version 0.3.2 (2017-05-22)
|
||||
===========================
|
||||
* Minor stability bugfixes
|
||||
* Added metrics and rear view mirror disable to settings
|
||||
* Update model with more crowdsourced data
|
||||
|
||||
Version 0.3.1 (2017-05-17)
|
||||
===========================
|
||||
* visiond stability bugfix
|
||||
* Add logging for angle and flashing
|
||||
|
||||
Version 0.3.0 (2017-05-12)
|
||||
===========================
|
||||
* Add CarParams struct to improve the abstraction layer
|
||||
* Refactor visiond IPC to support multiple clients
|
||||
* Add raw GPS and beginning support for navigation
|
||||
* Improve model in visiond using crowdsourced data
|
||||
* Add improved system logging to diagnose instability
|
||||
* Rewrite baseui in React Native
|
||||
* Moved calibration to the cloud
|
||||
|
||||
Version 0.2.9 (2017-03-01)
|
||||
===========================
|
||||
* Retain compatibility with NEOS v1
|
||||
|
||||
Version 0.2.8 (2017-02-27)
|
||||
===========================
|
||||
* Fix bug where frames were being dropped in minute 71
|
||||
|
||||
Version 0.2.7 (2017-02-08)
|
||||
===========================
|
||||
* Better performance and pictures at night
|
||||
* Fix ptr alignment issue in boardd
|
||||
* Fix brake error light, fix crash if too cold
|
||||
|
||||
Version 0.2.6 (2017-01-31)
|
||||
===========================
|
||||
* Fix bug in visiond model execution
|
||||
|
||||
Version 0.2.5 (2017-01-30)
|
||||
===========================
|
||||
* Fix race condition in manager
|
||||
|
||||
Version 0.2.4 (2017-01-27)
|
||||
===========================
|
||||
* OnePlus 3T support
|
||||
* Enable installation as NEOS app
|
||||
* Various minor bugfixes
|
||||
|
||||
Version 0.2.3 (2017-01-11)
|
||||
===========================
|
||||
* Reduce space usage by 80%
|
||||
* Add better logging
|
||||
* Add Travis CI
|
||||
|
||||
Version 0.2.2 (2017-01-10)
|
||||
===========================
|
||||
* Board triggers started signal on CAN messages
|
||||
* Improved autoexposure
|
||||
* Handle out of space, improve upload status
|
||||
|
||||
Version 0.2.1 (2016-12-14)
|
||||
===========================
|
||||
* Performance improvements, removal of more numpy
|
||||
* Fix boardd process priority
|
||||
* Make counter timer reset on use of steering wheel
|
||||
|
||||
Version 0.2 (2016-12-12)
|
||||
=========================
|
||||
* Car/Radar abstraction layers have shipped, see cereal/car.capnp
|
||||
* controlsd has been refactored
|
||||
* Shipped plant model and testing maneuvers
|
||||
* visiond exits more gracefully now
|
||||
* Hardware encoder in visiond should always init
|
||||
* ui now turns off the screen after 30 seconds
|
||||
* Switch to openpilot release branch for future releases
|
||||
* Added preliminary Docker container to run tests on PC
|
||||
|
||||
Version 0.1 (2016-11-29)
|
||||
=========================
|
||||
* Initial release of openpilot
|
||||
* Adaptive cruise control is working
|
||||
* Lane keep assist is working
|
||||
* Support for Acura ILX 2016 with AcuraWatch Plus
|
||||
* Support for Honda Civic 2016 Touring Edition
|
||||
73
SAFETY.md
Normal file
73
SAFETY.md
Normal file
@@ -0,0 +1,73 @@
|
||||
openpilot Safety
|
||||
======
|
||||
|
||||
openpilot is an Adaptive Cruise Control (ACC) and Lane Keeping Assist (LKA) system.
|
||||
Like other ACC and LKA systems, openpilot requires the driver to be alert and to
|
||||
pay attention at all times. We repeat, **driver alertness is necessary, but not
|
||||
sufficient, for openpilot to be used safely**.
|
||||
|
||||
Even with an attentive driver, we must make further efforts for the system to be
|
||||
safe. We have designed openpilot with two other safety considerations.
|
||||
|
||||
1. The driver must always be capable to immediately retake manual control of the vehicle,
|
||||
by stepping on either pedal or by pressing the cancel button.
|
||||
2. The vehicle must not alter its trajectory too quickly for the driver to safely
|
||||
react. This means that while the system is engaged, the actuators are constrained
|
||||
to operate within reasonable limits.
|
||||
|
||||
Following are details of the car specific safety implementations:
|
||||
|
||||
Honda/Acura
|
||||
------
|
||||
|
||||
- While the system is engaged, gas, brake and steer limits are subject to the same limits used by
|
||||
the stock system.
|
||||
|
||||
- Without an interceptor, the gas is controlled by the Powertrain Control Module (PCM).
|
||||
The PCM limits acceleration to what is reasonable for a cruise control system. With an
|
||||
interceptor, the gas is clipped to 60%.
|
||||
|
||||
- The brake is controlled by the 0x1FA CAN message. This message allows full
|
||||
braking, although the board and the software clip it to 1/4th of the max.
|
||||
This is around .3g of braking.
|
||||
|
||||
- Steering is controlled by the 0xE4 CAN message. The Electronic Power Steering (EPS)
|
||||
controller in the car limits the torque to a very small amount, so regardless of the
|
||||
message, the controller cannot jerk the wheel.
|
||||
|
||||
- Brake and gas pedal pressed signals are contained in the 0x17C CAN message. A rising edge of
|
||||
either signal triggers a disengagement, which is enforced by the board and in software. The
|
||||
green led on the board signifies if the board is allowing control messages.
|
||||
|
||||
- Honda CAN uses both a counter and a checksum to ensure integrity and prevent
|
||||
replay of the same message.
|
||||
|
||||
Toyota/Lexus
|
||||
------
|
||||
|
||||
- While the system is engaged, gas, brake and steer limits are subject to the same limits used by
|
||||
the stock system.
|
||||
|
||||
- With the stock Driving Support Unit (DSU) enabled, the acceleration is controlled
|
||||
by the stock system and is subject to the stock adaptive cruise control limits. Without the
|
||||
stock DSU connected, the acceleration command is controlled by the 0x343 CAN message and its
|
||||
value is limited by the board and the software to between .3g of deceleration and .15g of
|
||||
acceleration. The acceleration command is ignored by the Engine Control Module (ECM) while the
|
||||
cruise control system is disengaged.
|
||||
|
||||
- Steering torque is controlled through the 0x2E4 CAN message and it's limited by the board and in
|
||||
software to a value of -1500 and 1500. In addition, the vehicle EPS unit will not respond to
|
||||
commands outside these limits. A steering torque rate limit is enforced by the board and in
|
||||
software so that the commanded steering torque must rise from 0 to max value no faster than
|
||||
1.5s. Commanded steering torque is limited by the board and in software to be no more than 350
|
||||
units above the actual EPS generated motor torque to ensure limited differences between
|
||||
commanded and actual torques.
|
||||
|
||||
- Brake and gas pedal pressed signals are contained in the 0x224 and 0x1D2 CAN messages,
|
||||
respectively. A rising edge of either signal triggers a disengagement, which is enforced by the
|
||||
board and in software. Additionally, the cruise control system disengages on the rising edge of
|
||||
the brake pedal pressed signal.
|
||||
|
||||
- The cruise control system state is contained in the 0x1D2 message. No control messages are
|
||||
allowed if the cruise control system is not active. This is enforced by the software and the
|
||||
board. The green led on the board signifies if the board is allowing control messages.
|
||||
BIN
apk/ai.comma.plus.black.apk
Normal file
BIN
apk/ai.comma.plus.black.apk
Normal file
Binary file not shown.
BIN
apk/ai.comma.plus.frame.apk
Normal file
BIN
apk/ai.comma.plus.frame.apk
Normal file
Binary file not shown.
BIN
apk/ai.comma.plus.offroad.apk
Normal file
BIN
apk/ai.comma.plus.offroad.apk
Normal file
Binary file not shown.
2
apk/external/.gitignore
vendored
Normal file
2
apk/external/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
src/*
|
||||
out/*
|
||||
BIN
apk/external/com.spotify.music.apkpatch
vendored
Normal file
BIN
apk/external/com.spotify.music.apkpatch
vendored
Normal file
Binary file not shown.
BIN
apk/external/com.waze.apkpatch
vendored
Normal file
BIN
apk/external/com.waze.apkpatch
vendored
Normal file
Binary file not shown.
0
apk/external/out/.gitkeep
vendored
Normal file
0
apk/external/out/.gitkeep
vendored
Normal file
122
apk/external/patcher.py
vendored
Executable file
122
apk/external/patcher.py
vendored
Executable file
@@ -0,0 +1,122 @@
|
||||
#!/usr/bin/env python2.7
|
||||
|
||||
import os
|
||||
import sys
|
||||
import glob
|
||||
import shutil
|
||||
import urllib2
|
||||
import hashlib
|
||||
import subprocess
|
||||
|
||||
|
||||
EXTERNAL_PATH = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
if os.path.exists("/init.qcom.rc"):
|
||||
# android
|
||||
APKPATCH = os.path.join(EXTERNAL_PATH, 'tools/apkpatch_android')
|
||||
SIGNAPK = os.path.join(EXTERNAL_PATH, 'tools/signapk_android')
|
||||
else:
|
||||
APKPATCH = os.path.join(EXTERNAL_PATH, 'tools/apkpatch')
|
||||
SIGNAPK = os.path.join(EXTERNAL_PATH, 'tools/signapk')
|
||||
|
||||
APKS = {
|
||||
'com.waze': {
|
||||
'src': 'https://apkcache.s3.amazonaws.com/com.waze_1021278.apk',
|
||||
'src_sha256': 'f00957e93e2389f9e30502ac54994b98ac769314b0963c263d4e8baa625ab0c2',
|
||||
'patch': 'com.waze.apkpatch',
|
||||
'out_sha256': '9ec8b0ea3c78c666342865b1bfb66e368a3f5c911df2ad12835206ec8b19f444'
|
||||
},
|
||||
'com.spotify.music': {
|
||||
'src': 'https://apkcache.s3.amazonaws.com/com.spotify.music_24382006.apk',
|
||||
'src_sha256': '0610fea68ee7ba5f8e4e0732ad429d729dd6cbb8bc21222c4c99db6cb09fbff4',
|
||||
'patch': 'com.spotify.music.apkpatch',
|
||||
'out_sha256': '5a3d6f478c7e40403a98ccc8906d7e0ae12b06543b41f5df52149dd09c647c11'
|
||||
},
|
||||
}
|
||||
|
||||
def sha256_path(path):
|
||||
with open(path, 'rb') as f:
|
||||
return hashlib.sha256(f.read()).hexdigest()
|
||||
|
||||
def remove(path):
|
||||
try:
|
||||
os.remove(path)
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
def process(download, patch):
|
||||
# clean up any junk apks
|
||||
for out_apk in glob.glob(os.path.join(EXTERNAL_PATH, 'out/*.apk')):
|
||||
app = os.path.basename(out_apk)[:-4]
|
||||
if app not in APKS:
|
||||
print "remove junk", out_apk
|
||||
remove(out_apk)
|
||||
|
||||
complete = True
|
||||
for k,v in APKS.iteritems():
|
||||
apk_path = os.path.join(EXTERNAL_PATH, 'out', k+'.apk')
|
||||
print "checking", apk_path
|
||||
if os.path.exists(apk_path) and sha256_path(apk_path) == v['out_sha256']:
|
||||
# nothing to do
|
||||
continue
|
||||
|
||||
complete = False
|
||||
|
||||
remove(apk_path)
|
||||
|
||||
src_path = os.path.join(EXTERNAL_PATH, 'src', v['src_sha256'])
|
||||
if not os.path.exists(src_path) or sha256_path(src_path) != v['src_sha256']:
|
||||
if not download:
|
||||
continue
|
||||
|
||||
print "downloading", v['src'], "to", src_path
|
||||
# download it
|
||||
resp = urllib2.urlopen(v['src'])
|
||||
data = resp.read()
|
||||
with open(src_path, 'wb') as src_f:
|
||||
src_f.write(data)
|
||||
|
||||
if sha256_path(src_path) != v['src_sha256']:
|
||||
print "download was corrupted..."
|
||||
continue
|
||||
|
||||
if not patch:
|
||||
continue
|
||||
|
||||
# ignoring lots of TOCTTOU here...
|
||||
|
||||
apk_temp = "/tmp/"+k+".patched"
|
||||
remove(apk_temp)
|
||||
apk_temp2 = "/tmp/"+k+".signed"
|
||||
remove(apk_temp2)
|
||||
|
||||
try:
|
||||
print "patching", v['patch']
|
||||
subprocess.check_call([APKPATCH, 'apply', src_path, apk_temp, os.path.join(EXTERNAL_PATH, v['patch'])])
|
||||
print "signing", apk_temp
|
||||
subprocess.check_call([SIGNAPK,
|
||||
os.path.join(EXTERNAL_PATH, 'tools/certificate.pem'), os.path.join(EXTERNAL_PATH, 'tools/key.pk8'),
|
||||
apk_temp, apk_temp2])
|
||||
|
||||
out_sha256 = sha256_path(apk_temp2) if os.path.exists(apk_temp2) else None
|
||||
|
||||
if out_sha256 == v['out_sha256']:
|
||||
print "done", apk_path
|
||||
shutil.move(apk_temp2, apk_path)
|
||||
else:
|
||||
print "patch was corrupted", apk_temp2, out_sha256
|
||||
finally:
|
||||
remove(apk_temp)
|
||||
remove(apk_temp2)
|
||||
|
||||
return complete
|
||||
|
||||
if __name__ == "__main__":
|
||||
ret = True
|
||||
if len(sys.argv) == 2 and sys.argv[1] == "download":
|
||||
ret = process(True, False)
|
||||
elif len(sys.argv) == 2 and sys.argv[1] == "patch":
|
||||
ret = process(False, True)
|
||||
else:
|
||||
ret = process(True, True)
|
||||
sys.exit(0 if ret else 1)
|
||||
0
apk/external/src/.gitkeep
vendored
Normal file
0
apk/external/src/.gitkeep
vendored
Normal file
BIN
apk/external/tools/ApkPatch.android.jar
vendored
Normal file
BIN
apk/external/tools/ApkPatch.android.jar
vendored
Normal file
Binary file not shown.
7
apk/external/tools/apkpatch_android
vendored
Executable file
7
apk/external/tools/apkpatch_android
vendored
Executable file
@@ -0,0 +1,7 @@
|
||||
#!/system/bin/sh
|
||||
|
||||
DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
|
||||
export LD_LIBRARY_PATH=/system/lib64
|
||||
export CLASSPATH="$DIR"/ApkPatch.android.jar
|
||||
exec app_process "$DIR" ApkPatch "$@"
|
||||
17
apk/external/tools/certificate.pem
vendored
Executable file
17
apk/external/tools/certificate.pem
vendored
Executable file
@@ -0,0 +1,17 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICtTCCAh4CCQDm79UqF+Dc5zANBgkqhkiG9w0BAQUFADCBnjELMAkGA1UEBhMC
|
||||
SUQxEzARBgNVBAgTCkphd2EgQmFyYXQxEDAOBgNVBAcTB0JhbmR1bmcxEjAQBgNV
|
||||
BAoTCUxvbmRhdGlnYTETMBEGA1UECxMKQW5kcm9pZERldjEaMBgGA1UEAxMRTG9y
|
||||
ZW5zaXVzIFcuIEwuIFQxIzAhBgkqhkiG9w0BCQEWFGxvcmVuekBsb25kYXRpZ2Eu
|
||||
bmV0MB4XDTEwMDUwNTA5MjEzOFoXDTEzMDEyODA5MjEzOFowgZ4xCzAJBgNVBAYT
|
||||
AklEMRMwEQYDVQQIEwpKYXdhIEJhcmF0MRAwDgYDVQQHEwdCYW5kdW5nMRIwEAYD
|
||||
VQQKEwlMb25kYXRpZ2ExEzARBgNVBAsTCkFuZHJvaWREZXYxGjAYBgNVBAMTEUxv
|
||||
cmVuc2l1cyBXLiBMLiBUMSMwIQYJKoZIhvcNAQkBFhRsb3JlbnpAbG9uZGF0aWdh
|
||||
Lm5ldDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAy2oWtbdVXMHGiS6cA3qi
|
||||
3VfZt5Vz9jTlux+TEcGx5h18ZKwclyo+z2B0L/p5bYdnrTdFEiD7IxvX+h3lu0JV
|
||||
B9rdXZdyrzXNOw5YFrsn2k7hKvB8KEBaga1gZEwodlc6N14H3FbZdZkIA9V716Pu
|
||||
e5CWBZ2VqU03lUJmKnpH8c8CAwEAATANBgkqhkiG9w0BAQUFAAOBgQBpNgXh8dw9
|
||||
uMjZxzLUXovV5ptHd61jAcZlQlffqPsz6/2QNfIShVdGH9jkm0IudfKkbvvOKive
|
||||
a77t9c4sDh2Sat2L/rx6BfTuS1+y9wFr1Ee8Rrr7wGHhRkx2qqGrXGVWqXn8aE3E
|
||||
P6e7BTPF0ibS+tG8cdDPEisqGFxw36nTNQ==
|
||||
-----END CERTIFICATE-----
|
||||
BIN
apk/external/tools/key.pk8
vendored
Executable file
BIN
apk/external/tools/key.pk8
vendored
Executable file
Binary file not shown.
BIN
apk/external/tools/signapk.android.jar
vendored
Normal file
BIN
apk/external/tools/signapk.android.jar
vendored
Normal file
Binary file not shown.
7
apk/external/tools/signapk_android
vendored
Executable file
7
apk/external/tools/signapk_android
vendored
Executable file
@@ -0,0 +1,7 @@
|
||||
#!/system/bin/sh
|
||||
|
||||
DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
|
||||
export LD_LIBRARY_PATH=/system/lib64
|
||||
export CLASSPATH="$DIR"/signapk.android.jar
|
||||
exec app_process "$DIR" com.android.signapk.SignApk "$@"
|
||||
1
cereal/.gitignore
vendored
Normal file
1
cereal/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
gen
|
||||
52
cereal/Makefile
Normal file
52
cereal/Makefile
Normal file
@@ -0,0 +1,52 @@
|
||||
PWD := $(shell pwd)
|
||||
|
||||
SRCS := log.capnp car.capnp
|
||||
|
||||
GENS := gen/cpp/car.capnp.c++ gen/cpp/log.capnp.c++
|
||||
|
||||
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/c++.capnp.h gen/c/java.capnp.h
|
||||
|
||||
# Dont build java on the phone...
|
||||
ifeq ($(UNAME_M),x86_64)
|
||||
GENS += gen/java/Car.java gen/java/Log.java
|
||||
endif
|
||||
|
||||
endif
|
||||
|
||||
ifeq ($(UNAME_M),aarch64)
|
||||
CAPNPC=PATH=$(PWD)/../phonelibs/capnp-cpp/aarch64/bin/:$$PATH capnpc
|
||||
else
|
||||
CAPNPC=capnpc
|
||||
endif
|
||||
|
||||
.PHONY: all
|
||||
all: $(GENS)
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -rf gen
|
||||
|
||||
gen/c/%.capnp.c: %.capnp
|
||||
@echo "[ CAPNPC C ] $@"
|
||||
mkdir -p gen/c/
|
||||
$(CAPNPC) '$<' -o c:gen/c/
|
||||
|
||||
gen/cpp/%.capnp.c++: %.capnp
|
||||
@echo "[ CAPNPC C++ ] $@"
|
||||
mkdir -p gen/cpp/
|
||||
$(CAPNPC) '$<' -o c++:gen/cpp/
|
||||
|
||||
gen/java/Car.java gen/java/Log.java: $(SRCS)
|
||||
@echo "[ CAPNPC java ] $@"
|
||||
mkdir -p gen/java/
|
||||
$(CAPNPC) $^ -o java:gen/java
|
||||
|
||||
# c-capnproto needs some empty headers
|
||||
gen/c/c++.capnp.h gen/c/java.capnp.h:
|
||||
mkdir -p gen/c/
|
||||
touch '$@'
|
||||
|
||||
20
cereal/__init__.py
Normal file
20
cereal/__init__.py
Normal file
@@ -0,0 +1,20 @@
|
||||
import os
|
||||
import capnp
|
||||
|
||||
CEREAL_PATH = os.path.dirname(os.path.abspath(__file__))
|
||||
capnp.remove_import_hook()
|
||||
|
||||
if os.getenv("NEWCAPNP"):
|
||||
import tempfile
|
||||
import pyximport
|
||||
|
||||
importers = pyximport.install(build_dir=os.path.join(tempfile.gettempdir(), ".pyxbld"))
|
||||
try:
|
||||
import cereal.gen.cython.log_capnp_cython as log
|
||||
import cereal.gen.cython.car_capnp_cython as car
|
||||
finally:
|
||||
pyximport.uninstall(*importers)
|
||||
del importers
|
||||
else:
|
||||
log = capnp.load(os.path.join(CEREAL_PATH, "log.capnp"))
|
||||
car = capnp.load(os.path.join(CEREAL_PATH, "car.capnp"))
|
||||
26
cereal/c++.capnp
Normal file
26
cereal/c++.capnp
Normal file
@@ -0,0 +1,26 @@
|
||||
# Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
|
||||
# Licensed under the MIT License:
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
|
||||
@0xbdf87d7bb8304e81;
|
||||
$namespace("capnp::annotations");
|
||||
|
||||
annotation namespace(file): Text;
|
||||
annotation name(field, enumerant, struct, enum, interface, method, param, group, union): Text;
|
||||
321
cereal/car.capnp
Normal file
321
cereal/car.capnp
Normal file
@@ -0,0 +1,321 @@
|
||||
using Cxx = import "c++.capnp";
|
||||
$Cxx.namespace("cereal");
|
||||
|
||||
using Java = import "java.capnp";
|
||||
$Java.package("ai.comma.openpilot.cereal");
|
||||
$Java.outerClassname("Car");
|
||||
|
||||
@0x8e2af1e708af8b8d;
|
||||
|
||||
# ******* events causing controls state machine transition *******
|
||||
|
||||
struct CarEvent @0x9b1657f34caf3ad3 {
|
||||
name @0 :EventName;
|
||||
enable @1 :Bool;
|
||||
noEntry @2 :Bool;
|
||||
warning @3 :Bool;
|
||||
userDisable @4 :Bool;
|
||||
softDisable @5 :Bool;
|
||||
immediateDisable @6 :Bool;
|
||||
preEnable @7 :Bool;
|
||||
permanent @8 :Bool;
|
||||
|
||||
enum EventName @0xbaa8c5d505f727de {
|
||||
# TODO: copy from error list
|
||||
commIssue @0;
|
||||
steerUnavailable @1;
|
||||
brakeUnavailable @2;
|
||||
gasUnavailable @3;
|
||||
wrongGear @4;
|
||||
doorOpen @5;
|
||||
seatbeltNotLatched @6;
|
||||
espDisabled @7;
|
||||
wrongCarMode @8;
|
||||
steerTempUnavailable @9;
|
||||
reverseGear @10;
|
||||
buttonCancel @11;
|
||||
buttonEnable @12;
|
||||
pedalPressed @13;
|
||||
cruiseDisabled @14;
|
||||
radarCommIssue @15;
|
||||
dataNeeded @16;
|
||||
speedTooLow @17;
|
||||
outOfSpace @18;
|
||||
overheat @19;
|
||||
calibrationInProgress @20;
|
||||
calibrationInvalid @21;
|
||||
controlsMismatch @22;
|
||||
pcmEnable @23;
|
||||
pcmDisable @24;
|
||||
noTarget @25;
|
||||
radarFault @26;
|
||||
modelCommIssue @27;
|
||||
brakeHold @28;
|
||||
parkBrake @29;
|
||||
manualRestart @30;
|
||||
lowSpeedLockout @31;
|
||||
}
|
||||
}
|
||||
|
||||
# ******* main car state @ 100hz *******
|
||||
# all speeds in m/s
|
||||
|
||||
struct CarState {
|
||||
errorsDEPRECATED @0 :List(CarEvent.EventName);
|
||||
events @13 :List(CarEvent);
|
||||
|
||||
# car speed
|
||||
vEgo @1 :Float32; # best estimate of speed
|
||||
aEgo @16 :Float32; # best estimate of acceleration
|
||||
vEgoRaw @17 :Float32; # unfiltered speed from CAN sensors
|
||||
yawRate @22 :Float32; # best estimate of yaw rate
|
||||
standstill @18 :Bool;
|
||||
wheelSpeeds @2 :WheelSpeeds;
|
||||
|
||||
# gas pedal, 0.0-1.0
|
||||
gas @3 :Float32; # this is user + computer
|
||||
gasPressed @4 :Bool; # this is user pedal only
|
||||
|
||||
# brake pedal, 0.0-1.0
|
||||
brake @5 :Float32; # this is user pedal only
|
||||
brakePressed @6 :Bool; # this is user pedal only
|
||||
brakeLights @19 :Bool;
|
||||
|
||||
# steering wheel
|
||||
steeringAngle @7 :Float32; # deg
|
||||
steeringRate @15 :Float32; # deg/s
|
||||
steeringTorque @8 :Float32; # TODO: standardize units
|
||||
steeringPressed @9 :Bool; # if the user is using the steering wheel
|
||||
|
||||
# cruise state
|
||||
cruiseState @10 :CruiseState;
|
||||
|
||||
# gear
|
||||
gearShifter @14 :GearShifter;
|
||||
|
||||
# button presses
|
||||
buttonEvents @11 :List(ButtonEvent);
|
||||
leftBlinker @20 :Bool;
|
||||
rightBlinker @21 :Bool;
|
||||
genericToggle @23 :Bool;
|
||||
|
||||
# lock info
|
||||
doorOpen @24 :Bool;
|
||||
seatbeltUnlatched @25 :Bool;
|
||||
|
||||
# which packets this state came from
|
||||
canMonoTimes @12: List(UInt64);
|
||||
|
||||
struct WheelSpeeds {
|
||||
# optional wheel speeds
|
||||
fl @0 :Float32;
|
||||
fr @1 :Float32;
|
||||
rl @2 :Float32;
|
||||
rr @3 :Float32;
|
||||
}
|
||||
|
||||
struct CruiseState {
|
||||
enabled @0 :Bool;
|
||||
speed @1 :Float32;
|
||||
available @2 :Bool;
|
||||
speedOffset @3 :Float32;
|
||||
standstill @4 :Bool;
|
||||
}
|
||||
|
||||
enum GearShifter {
|
||||
unknown @0;
|
||||
park @1;
|
||||
drive @2;
|
||||
neutral @3;
|
||||
reverse @4;
|
||||
sport @5;
|
||||
low @6;
|
||||
brake @7;
|
||||
}
|
||||
|
||||
|
||||
# send on change
|
||||
struct ButtonEvent {
|
||||
pressed @0 :Bool;
|
||||
type @1 :Type;
|
||||
|
||||
enum Type {
|
||||
unknown @0;
|
||||
leftBlinker @1;
|
||||
rightBlinker @2;
|
||||
accelCruise @3;
|
||||
decelCruise @4;
|
||||
cancel @5;
|
||||
altButton1 @6;
|
||||
altButton2 @7;
|
||||
altButton3 @8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# ******* radar state @ 20hz *******
|
||||
|
||||
struct RadarState {
|
||||
errors @0 :List(Error);
|
||||
points @1 :List(RadarPoint);
|
||||
|
||||
# which packets this state came from
|
||||
canMonoTimes @2 :List(UInt64);
|
||||
|
||||
enum Error {
|
||||
commIssue @0;
|
||||
fault @1;
|
||||
}
|
||||
|
||||
# similar to LiveTracks
|
||||
# is one timestamp valid for all? I think so
|
||||
struct RadarPoint {
|
||||
trackId @0 :UInt64; # no trackId reuse
|
||||
|
||||
# these 3 are the minimum required
|
||||
dRel @1 :Float32; # m from the front bumper of the car
|
||||
yRel @2 :Float32; # m
|
||||
vRel @3 :Float32; # m/s
|
||||
|
||||
# these are optional and valid if they are not NaN
|
||||
aRel @4 :Float32; # m/s^2
|
||||
yvRel @5 :Float32; # m/s
|
||||
|
||||
# some radars flag measurements VS estimates
|
||||
measured @6 :Bool;
|
||||
}
|
||||
}
|
||||
|
||||
# ******* car controls @ 100hz *******
|
||||
|
||||
struct CarControl {
|
||||
# must be true for any actuator commands to work
|
||||
enabled @0 :Bool;
|
||||
|
||||
gasDEPRECATED @1 :Float32;
|
||||
brakeDEPRECATED @2 :Float32;
|
||||
steeringTorqueDEPRECATED @3 :Float32;
|
||||
|
||||
actuators @6 :Actuators;
|
||||
|
||||
cruiseControl @4 :CruiseControl;
|
||||
hudControl @5 :HUDControl;
|
||||
|
||||
struct Actuators {
|
||||
# range from 0.0 - 1.0
|
||||
gas @0: Float32;
|
||||
brake @1: Float32;
|
||||
# range from -1.0 - 1.0
|
||||
steer @2: Float32;
|
||||
}
|
||||
|
||||
struct CruiseControl {
|
||||
cancel @0: Bool;
|
||||
override @1: Bool;
|
||||
speedOverride @2: Float32;
|
||||
accelOverride @3: Float32;
|
||||
}
|
||||
|
||||
struct HUDControl {
|
||||
speedVisible @0: Bool;
|
||||
setSpeed @1: Float32;
|
||||
lanesVisible @2: Bool;
|
||||
leadVisible @3: Bool;
|
||||
visualAlert @4: VisualAlert;
|
||||
audibleAlert @5: AudibleAlert;
|
||||
|
||||
enum VisualAlert {
|
||||
# these are the choices from the Honda
|
||||
# map as good as you can for your car
|
||||
none @0;
|
||||
fcw @1;
|
||||
steerRequired @2;
|
||||
brakePressed @3;
|
||||
wrongGear @4;
|
||||
seatbeltUnbuckled @5;
|
||||
speedTooHigh @6;
|
||||
}
|
||||
|
||||
enum AudibleAlert {
|
||||
# these are the choices from the Honda
|
||||
# map as good as you can for your car
|
||||
none @0;
|
||||
beepSingle @1;
|
||||
beepTriple @2;
|
||||
beepRepeated @3;
|
||||
chimeSingle @4;
|
||||
chimeDouble @5;
|
||||
chimeRepeated @6;
|
||||
chimeContinuous @7;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# ****** car param ******
|
||||
|
||||
struct CarParams {
|
||||
carName @0 :Text;
|
||||
radarNameDEPRECATED @1 :Text;
|
||||
carFingerprint @2 :Text;
|
||||
|
||||
enableSteer @3 :Bool;
|
||||
enableGas @4 :Bool;
|
||||
enableBrake @5 :Bool;
|
||||
enableCruise @6 :Bool;
|
||||
enableCamera @26 :Bool;
|
||||
enableDsu @27 :Bool; # driving support unit
|
||||
enableApgs @28 :Bool; # advanced parking guidance system
|
||||
|
||||
minEnableSpeed @17 :Float32;
|
||||
safetyModel @18 :Int16;
|
||||
safetyParam @41 :Int16;
|
||||
|
||||
steerMaxBP @19 :List(Float32);
|
||||
steerMaxV @20 :List(Float32);
|
||||
gasMaxBP @21 :List(Float32);
|
||||
gasMaxV @22 :List(Float32);
|
||||
brakeMaxBP @23 :List(Float32);
|
||||
brakeMaxV @24 :List(Float32);
|
||||
|
||||
longPidDeadzoneBP @32 :List(Float32);
|
||||
longPidDeadzoneV @33 :List(Float32);
|
||||
|
||||
enum SafetyModels {
|
||||
# does NOT match board setting
|
||||
noOutput @0;
|
||||
honda @1;
|
||||
toyota @2;
|
||||
elm327 @3;
|
||||
}
|
||||
|
||||
# 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
|
||||
steerKp @15 :Float32;
|
||||
steerKi @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
|
||||
}
|
||||
28
cereal/java.capnp
Normal file
28
cereal/java.capnp
Normal file
@@ -0,0 +1,28 @@
|
||||
# Copyright (c) 2013-2015 Sandstorm Development Group, Inc. and contributors
|
||||
# Licensed under the MIT License:
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
|
||||
@0xc5f1af96651f70ea;
|
||||
|
||||
annotation package @0x9ee4c8f803b3b596 (file) : Text;
|
||||
# Name of the package, such as "org.example.foo", in which the generated code will reside.
|
||||
|
||||
annotation outerClassname @0x9b066bb4881f7cd3 (file) : Text;
|
||||
# Name of the outer class that will wrap the generated code.
|
||||
1433
cereal/log.capnp
Normal file
1433
cereal/log.capnp
Normal file
File diff suppressed because it is too large
Load Diff
0
common/__init__.py
Normal file
0
common/__init__.py
Normal file
15
common/api/__init__.py
Normal file
15
common/api/__init__.py
Normal file
@@ -0,0 +1,15 @@
|
||||
import requests
|
||||
|
||||
from selfdrive.version import version
|
||||
|
||||
def api_get(endpoint, method='GET', timeout=None, access_token=None, **params):
|
||||
backend = "https://api.commadotai.com/"
|
||||
|
||||
headers = {}
|
||||
if access_token is not None:
|
||||
headers['Authorization'] = "JWT "+access_token
|
||||
|
||||
headers['User-Agent'] = "openpilot-" + version
|
||||
|
||||
return requests.request(method, backend+endpoint, timeout=timeout, headers = headers, params=params)
|
||||
|
||||
4
common/basedir.py
Normal file
4
common/basedir.py
Normal file
@@ -0,0 +1,4 @@
|
||||
import os
|
||||
BASEDIR = os.path.abspath(os.path.join(os.path.dirname(os.path.realpath(__file__)), "../"))
|
||||
|
||||
|
||||
220
common/dbc.py
Executable file
220
common/dbc.py
Executable file
@@ -0,0 +1,220 @@
|
||||
import re
|
||||
import os
|
||||
import struct
|
||||
import bitstring
|
||||
import sys
|
||||
import numbers
|
||||
from collections import namedtuple
|
||||
|
||||
def int_or_float(s):
|
||||
# return number, trying to maintain int format
|
||||
try:
|
||||
return int(s)
|
||||
except ValueError:
|
||||
return float(s)
|
||||
|
||||
DBCSignal = namedtuple(
|
||||
"DBCSignal", ["name", "start_bit", "size", "is_little_endian", "is_signed",
|
||||
"factor", "offset", "tmin", "tmax", "units"])
|
||||
|
||||
class dbc(object):
|
||||
def __init__(self, fn):
|
||||
self.name, _ = os.path.splitext(os.path.basename(fn))
|
||||
with open(fn) as f:
|
||||
self.txt = f.read().split("\n")
|
||||
self._warned_addresses = set()
|
||||
|
||||
# regexps from https://github.com/ebroecker/canmatrix/blob/master/canmatrix/importdbc.py
|
||||
bo_regexp = re.compile("^BO\_ (\w+) (\w+) *: (\w+) (\w+)")
|
||||
sg_regexp = re.compile("^SG\_ (\w+) : (\d+)\|(\d+)@(\d+)([\+|\-]) \(([0-9.+\-eE]+),([0-9.+\-eE]+)\) \[([0-9.+\-eE]+)\|([0-9.+\-eE]+)\] \"(.*)\" (.*)")
|
||||
sgm_regexp = re.compile("^SG\_ (\w+) (\w+) *: (\d+)\|(\d+)@(\d+)([\+|\-]) \(([0-9.+\-eE]+),([0-9.+\-eE]+)\) \[([0-9.+\-eE]+)\|([0-9.+\-eE]+)\] \"(.*)\" (.*)")
|
||||
|
||||
# A dictionary which maps message ids to tuples ((name, size), signals).
|
||||
# name is the ASCII name of the message.
|
||||
# size is the size of the message in bytes.
|
||||
# signals is a list signals contained in the message.
|
||||
# signals is a list of DBCSignal in order of increasing start_bit.
|
||||
self.msgs = {}
|
||||
|
||||
# lookup to bit reverse each byte
|
||||
self.bits_index = [(i & ~0b111) + ((-i-1) & 0b111) for i in xrange(64)]
|
||||
|
||||
for l in self.txt:
|
||||
l = l.strip()
|
||||
|
||||
if l.startswith("BO_ "):
|
||||
# new group
|
||||
dat = bo_regexp.match(l)
|
||||
|
||||
if dat is None:
|
||||
print "bad BO", l
|
||||
name = dat.group(2)
|
||||
size = int(dat.group(3))
|
||||
ids = int(dat.group(1), 0) # could be hex
|
||||
if ids in self.msgs:
|
||||
sys.exit("Duplicate address detected %d %s" % (ids, self.name))
|
||||
|
||||
self.msgs[ids] = ((name, size), [])
|
||||
|
||||
if l.startswith("SG_ "):
|
||||
# new signal
|
||||
dat = sg_regexp.match(l)
|
||||
go = 0
|
||||
if dat is None:
|
||||
dat = sgm_regexp.match(l)
|
||||
go = 1
|
||||
if dat is None:
|
||||
print "bad SG", l
|
||||
|
||||
sgname = dat.group(1)
|
||||
start_bit = int(dat.group(go+2))
|
||||
signal_size = int(dat.group(go+3))
|
||||
is_little_endian = int(dat.group(go+4))==1
|
||||
is_signed = dat.group(go+5)=='-'
|
||||
factor = int_or_float(dat.group(go+6))
|
||||
offset = int_or_float(dat.group(go+7))
|
||||
tmin = int_or_float(dat.group(go+8))
|
||||
tmax = int_or_float(dat.group(go+9))
|
||||
units = dat.group(go+10)
|
||||
|
||||
self.msgs[ids][1].append(
|
||||
DBCSignal(sgname, start_bit, signal_size, is_little_endian,
|
||||
is_signed, factor, offset, tmin, tmax, units))
|
||||
|
||||
for msg in self.msgs.viewvalues():
|
||||
msg[1].sort(key=lambda x: x.start_bit)
|
||||
|
||||
self.msg_name_to_address = {}
|
||||
for address, m in self.msgs.items():
|
||||
name = m[0][0]
|
||||
self.msg_name_to_address[name] = address
|
||||
|
||||
def lookup_msg_id(self, msg_id):
|
||||
if not isinstance(msg_id, numbers.Number):
|
||||
msg_id = self.msg_name_to_address[msg_id]
|
||||
return msg_id
|
||||
|
||||
def encode(self, msg_id, dd):
|
||||
"""Encode a CAN message using the dbc.
|
||||
|
||||
Inputs:
|
||||
msg_id: The message ID.
|
||||
dd: A dictionary mapping signal name to signal data.
|
||||
"""
|
||||
msg_id = self.lookup_msg_id(msg_id)
|
||||
|
||||
# TODO: Stop using bitstring, which is super slow.
|
||||
msg_def = self.msgs[msg_id]
|
||||
size = msg_def[0][1]
|
||||
|
||||
bsf = bitstring.Bits(hex="00"*size)
|
||||
for s in msg_def[1]:
|
||||
ival = dd.get(s.name)
|
||||
if ival is not None:
|
||||
ival = (ival / s.factor) - s.offset
|
||||
ival = int(round(ival))
|
||||
|
||||
# should pack this
|
||||
if s.is_little_endian:
|
||||
ss = s.start_bit
|
||||
else:
|
||||
ss = self.bits_index[s.start_bit]
|
||||
|
||||
|
||||
if s.is_signed:
|
||||
tbs = bitstring.Bits(int=ival, length=s.size)
|
||||
else:
|
||||
tbs = bitstring.Bits(uint=ival, length=s.size)
|
||||
|
||||
lpad = bitstring.Bits(bin="0b"+"0"*ss)
|
||||
rpad = bitstring.Bits(bin="0b"+"0"*(8*size-(ss+s.size)))
|
||||
tbs = lpad+tbs+rpad
|
||||
|
||||
bsf |= tbs
|
||||
return bsf.tobytes()
|
||||
|
||||
def decode(self, x, arr=None, debug=False):
|
||||
"""Decode a CAN message using the dbc.
|
||||
|
||||
Inputs:
|
||||
x: A collection with elements (address, time, data), where address is
|
||||
the CAN address, time is the bus time, and data is the CAN data as a
|
||||
hex string.
|
||||
arr: Optional list of signals which should be decoded and returned.
|
||||
debug: True to print debugging statements.
|
||||
|
||||
Returns:
|
||||
A tuple (name, data), where name is the name of the CAN message and data
|
||||
is the decoded result. If arr is None, data is a dict of properties.
|
||||
Otherwise data is a list of the same length as arr.
|
||||
|
||||
Returns (None, None) if the message could not be decoded.
|
||||
"""
|
||||
|
||||
if arr is None:
|
||||
out = {}
|
||||
else:
|
||||
out = [None]*len(arr)
|
||||
|
||||
msg = self.msgs.get(x[0])
|
||||
if msg is None:
|
||||
if x[0] not in self._warned_addresses:
|
||||
#print("WARNING: Unknown message address {}".format(x[0]))
|
||||
self._warned_addresses.add(x[0])
|
||||
return None, None
|
||||
|
||||
name = msg[0][0]
|
||||
if debug:
|
||||
print name
|
||||
|
||||
blen = 8*len(x[2])
|
||||
|
||||
st = x[2].rjust(8, '\x00')
|
||||
le, be = None, None
|
||||
|
||||
for s in msg[1]:
|
||||
if arr is not None and s[0] not in arr:
|
||||
continue
|
||||
|
||||
# big or little endian?
|
||||
# see http://vi-firmware.openxcplatform.com/en/master/config/bit-numbering.html
|
||||
if s[3] is False:
|
||||
ss = self.bits_index[s[1]]
|
||||
if be is None:
|
||||
be = struct.unpack(">Q", st)[0]
|
||||
x2_int = be
|
||||
data_bit_pos = (blen - (ss + s[2]))
|
||||
else:
|
||||
if le is None:
|
||||
le = struct.unpack("<Q", st)[0]
|
||||
x2_int = le
|
||||
ss = s[1]
|
||||
data_bit_pos = ss
|
||||
|
||||
if data_bit_pos < 0:
|
||||
continue
|
||||
ival = (x2_int >> data_bit_pos) & ((1 << (s[2])) - 1)
|
||||
|
||||
if s[4] and (ival & (1<<(s[2]-1))): # signed
|
||||
ival -= (1<<s[2])
|
||||
|
||||
# control the offset
|
||||
ival = (ival * s[5]) + s[6]
|
||||
#if debug:
|
||||
# print "%40s %2d %2d %7.2f %s" % (s[0], s[1], s[2], ival, s[-1])
|
||||
|
||||
if arr is None:
|
||||
out[s[0]] = ival
|
||||
else:
|
||||
out[arr.index(s[0])] = ival
|
||||
return name, out
|
||||
|
||||
def get_signals(self, msg):
|
||||
msg = self.lookup_msg_id(msg)
|
||||
return [sgs.name for sgs in self.msgs[msg][1]]
|
||||
|
||||
if __name__ == "__main__":
|
||||
from opendbc import DBC_PATH
|
||||
|
||||
dbc_test = dbc(os.path.join(DBC_PATH, sys.argv[1]))
|
||||
print dbc_test.get_signals(0xe4)
|
||||
104
common/fingerprints.py
Normal file
104
common/fingerprints.py
Normal file
@@ -0,0 +1,104 @@
|
||||
class HONDA:
|
||||
CIVIC = "HONDA CIVIC 2016 TOURING"
|
||||
ACURA_ILX = "ACURA ILX 2016 ACURAWATCH PLUS"
|
||||
CRV = "HONDA CR-V 2016 TOURING"
|
||||
ODYSSEY = "HONDA ODYSSEY 2018 EX-L"
|
||||
ACURA_RDX = "ACURA RDX 2018 ACURAWATCH PLUS"
|
||||
|
||||
|
||||
class TOYOTA:
|
||||
PRIUS = "TOYOTA PRIUS 2017"
|
||||
RAV4H = "TOYOTA RAV4 2017 HYBRID"
|
||||
RAV4 = "TOYOTA RAV4 2017"
|
||||
COROLLA = "TOYOTA COROLLA 2017"
|
||||
LEXUS_RXH = "LEXUS RX HYBRID 2017"
|
||||
|
||||
|
||||
_FINGERPRINTS = {
|
||||
HONDA.ACURA_ILX: {
|
||||
1024L: 5, 513L: 5, 1027L: 5, 1029L: 8, 929L: 4, 1057L: 5, 777L: 8, 1034L: 5, 1036L: 8, 398L: 3, 399L: 7, 145L: 8, 660L: 8, 985L: 3, 923L: 2, 542L: 7, 773L: 7, 800L: 8, 432L: 7, 419L: 8, 420L: 8, 1030L: 5, 422L: 8, 808L: 8, 428L: 8, 304L: 8, 819L: 7, 821L: 5, 57L: 3, 316L: 8, 545L: 4, 464L: 8, 1108L: 8, 597L: 8, 342L: 6, 983L: 8, 344L: 8, 804L: 8, 1039L: 8, 476L: 4, 892L: 8, 490L: 8, 1064L: 7, 882L: 2, 884L: 7, 887L: 8, 888L: 8, 380L: 8, 1365L: 5,
|
||||
# sent messages
|
||||
0xe4: 5, 0x1fa: 8, 0x200: 3, 0x30c: 8, 0x33d: 5,
|
||||
},
|
||||
HONDA.ACURA_RDX: {
|
||||
57L: 3, 145L: 8, 229L: 4, 308L: 5, 316L: 8, 342L: 6, 344L: 8, 380L: 8, 392L: 6, 398L: 3, 399L: 6, 404L: 4, 420L: 8, 422L: 8, 426L: 8, 432L: 7, 464L: 8, 474L: 5, 476L: 4, 487L: 4, 490L: 8, 506L: 8, 542L: 7, 545L: 4, 597L: 8, 660L: 8, 773L: 7, 777L: 8, 780L: 8, 800L: 8, 804L: 8, 808L: 8, 819L: 7, 821L: 5, 829L: 5, 882L: 2, 884L: 7, 887L: 8, 888L: 8, 892L: 8, 923L: 2, 929L: 4, 963L: 8, 965L: 8, 966L: 8, 967L: 8, 983L: 8, 985L: 3, 1024L: 5, 1027L: 5, 1029L: 8, 1033L: 5, 1034L: 5, 1036L: 8, 1039L: 8, 1057L: 5, 1064L: 7, 1108L: 8, 1365L: 5, 1424L: 5, 1729L: 1
|
||||
},
|
||||
HONDA.CIVIC: {
|
||||
1024L: 5, 513L: 5, 1027L: 5, 1029L: 8, 777L: 8, 1036L: 8, 1039L: 8, 1424L: 5, 401L: 8, 148L: 8, 662L: 4, 985L: 3, 795L: 8, 773L: 7, 800L: 8, 545L: 6, 420L: 8, 806L: 8, 808L: 8, 1322L: 5, 427L: 3, 428L: 8, 304L: 8, 432L: 7, 57L: 3, 450L: 8, 929L: 8, 330L: 8, 1302L: 8, 464L: 8, 1361L: 5, 1108L: 8, 597L: 8, 470L: 2, 344L: 8, 804L: 8, 399L: 7, 476L: 7, 1633L: 8, 487L: 4, 892L: 8, 490L: 8, 493L: 5, 884L: 8, 891L: 8, 380L: 8, 1365L: 5,
|
||||
# sent messages
|
||||
0xe4: 5, 0x1fa: 8, 0x200: 3, 0x30c: 8, 0x33d: 5, 0x35e: 8, 0x39f: 8,
|
||||
},
|
||||
HONDA.CRV: {
|
||||
57L: 3, 145L: 8, 316L: 8, 340L: 8, 342L: 6, 344L: 8, 380L: 8, 398L: 3, 399L: 6, 401L: 8, 420L: 8, 422L: 8, 426L: 8, 432L: 7, 464L: 8, 474L: 5, 476L: 4, 487L: 4, 490L: 8, 493L: 3, 507L: 1, 542L: 7, 545L: 4, 597L: 8, 660L: 8, 661L: 4, 773L: 7, 777L: 8, 800L: 8, 804L: 8, 808L: 8, 882L: 2, 884L: 7, 888L: 8, 891L: 8, 892L: 8, 923L: 2, 929L: 8, 983L: 8, 985L: 3, 1024L: 5, 1027L: 5, 1029L: 8, 1033L: 5, 1036L: 8, 1039L: 8, 1057L: 5, 1064L: 7, 1108L: 8, 1125L: 8, 1296L: 8, 1365L: 5, 1424L: 5, 1600L: 5, 1601L: 8,
|
||||
# sent messages
|
||||
0x194: 4, 0x1fa: 8, 0x30c: 8, 0x33d: 5,
|
||||
},
|
||||
HONDA.ODYSSEY: {
|
||||
57L: 3, 148L: 8, 228L: 5, 229L: 4, 316L: 8, 342L: 6, 344L: 8, 380L: 8, 399L: 7, 411L: 5, 419L: 8, 420L: 8, 427L: 3, 432L: 7, 450L: 8, 463L: 8, 464L: 8, 476L: 4, 490L: 8, 506L: 8, 542L: 7, 545L: 6, 597L: 8, 662L: 4, 773L: 7, 777L: 8, 780L: 8, 795L: 8, 800L: 8, 804L: 8, 806L: 8, 808L: 8, 817L: 4, 819L: 7, 821L: 5, 825L: 4, 829L: 5, 837L: 5, 856L: 7, 862L: 8, 871L: 8, 881L: 8, 882L: 4, 884L: 8, 891L: 8, 892L: 8, 905L: 8, 923L: 2, 927L: 8, 929L: 8, 963L: 8, 965L: 8, 966L: 8, 967L: 8, 983L: 8, 985L: 3, 1029L: 8, 1036L: 8, 1052L: 8, 1064L: 7, 1088L: 8, 1089L: 8, 1092L: 1, 1108L: 8, 1110L: 8, 1125L: 8, 1296L: 8, 1302L: 8, 1600L: 5, 1601L: 8, 1612L: 5, 1613L: 5, 1614L: 5, 1615L: 8, 1616L: 5, 1619L: 5, 1623L: 5, 1668L: 5
|
||||
},
|
||||
TOYOTA.RAV4: {
|
||||
36L: 8, 37L: 8, 170L: 8, 180L: 8, 186L: 4, 426L: 6, 452L: 8, 464L: 8, 466L: 8, 467L: 8, 547L: 8, 548L: 8, 552L: 4, 562L: 4, 608L: 8, 610L: 5, 643L: 7, 705L: 8, 725L: 2, 740L: 5, 800L: 8, 835L: 8, 836L: 8, 849L: 4, 869L: 7, 870L: 7, 871L: 2, 896L: 8, 897L: 8, 900L: 6, 902L: 6, 905L: 8, 911L: 8, 916L: 3, 918L: 7, 921L: 8, 933L: 8, 944L: 8, 945L: 8, 951L: 8, 955L: 4, 956L: 8, 979L: 2, 998L: 5, 999L: 7, 1000L: 8, 1001L: 8, 1005L: 2, 1008L: 2, 1014L: 8, 1017L: 8, 1041L: 8, 1042L: 8, 1043L: 8, 1044L: 8, 1056L: 8, 1059L: 1, 1114L: 8, 1161L: 8, 1162L: 8, 1163L: 8, 1176L: 8, 1177L: 8, 1178L: 8, 1179L: 8, 1180L: 8, 1181L: 8, 1190L: 8, 1191L: 8, 1192L: 8, 1196L: 8, 1227L: 8, 1228L: 8, 1235L: 8, 1237L: 8, 1263L: 8, 1264L: 8, 1279L: 8, 1408L: 8, 1409L: 8, 1410L: 8, 1552L: 8, 1553L: 8, 1554L: 8, 1555L: 8, 1556L: 8, 1557L: 8, 1561L: 8, 1562L: 8, 1568L: 8, 1569L: 8, 1570L: 8, 1571L: 8, 1572L: 8, 1584L: 8, 1589L: 8, 1592L: 8, 1593L: 8, 1595L: 8, 1596L: 8, 1597L: 8, 1600L: 8, 1656L: 8, 1664L: 8, 1728L: 8, 1745L: 8, 1779L: 8, 1904L: 8, 1912L: 8, 1990L: 8, 1998L: 8
|
||||
},
|
||||
TOYOTA.RAV4H: {
|
||||
36L: 8, 37L: 8, 170L: 8, 180L: 8, 186L: 4, 426L: 6, 452L: 8, 464L: 8, 466L: 8, 467L: 8, 547L: 8, 548L: 8, 552L: 4, 562L: 4, 608L: 8, 610L: 5, 643L: 7, 705L: 8, 725L: 2, 740L: 5, 800L: 8, 835L: 8, 836L: 8, 849L: 4, 869L: 7, 870L: 7, 871L: 2, 896L: 8, 897L: 8, 900L: 6, 902L: 6, 905L: 8, 911L: 8, 916L: 3, 918L: 7, 921L: 8, 933L: 8, 944L: 8, 945L: 8, 951L: 8, 955L: 8, 956L: 8, 979L: 2, 998L: 5, 999L: 7, 1000L: 8, 1001L: 8, 1008L: 2, 1014L: 8, 1017L: 8, 1041L: 8, 1042L: 8, 1043L: 8, 1044L: 8, 1056L: 8, 1059L: 1, 1114L: 8, 1161L: 8, 1162L: 8, 1163L: 8, 1176L: 8, 1177L: 8, 1178L: 8, 1179L: 8, 1180L: 8, 1181L: 8, 1190L: 8, 1191L: 8, 1192L: 8, 1196L: 8, 1227L: 8, 1228L: 8, 1235L: 8, 1237L: 8, 1263L: 8, 1279L: 8, 1408L: 8, 1409L: 8, 1410L: 8, 1552L: 8, 1553L: 8, 1554L: 8, 1555L: 8, 1556L: 8, 1557L: 8, 1561L: 8, 1562L: 8, 1568L: 8, 1569L: 8, 1570L: 8, 1571L: 8, 1572L: 8, 1584L: 8, 1589L: 8, 1592L: 8, 1593L: 8, 1595L: 8, 1596L: 8, 1597L: 8, 1600L: 8, 1656L: 8, 1664L: 8, 1728L: 8, 1745L: 8, 1779L: 8, 1904L: 8, 1912L: 8, 1990L: 8, 1998L: 8, 581L: 5, 296: 8, 560L: 7, 713L: 8, 550L: 8, 608L: 8, 37L: 8, 36L: 8, 950L: 8, 1198L: 8, 1197L: 8, 1199L: 8, 1212L: 8, 953L: 3, 1264L: 8, 1184L: 8, 1005L: 2, 1185L: 8, 1232L: 8, 1186L: 8
|
||||
},
|
||||
TOYOTA.PRIUS: [{
|
||||
36L: 8, 37L: 8, 166L: 8, 170L: 8, 180L: 8, 295L: 8, 296L: 8, 426L: 6, 452L: 8, 466L: 8, 467L: 8, 550L: 8, 552L: 4, 560L: 7, 562L: 6, 581L: 5, 608L: 8, 610L: 8, 614L: 8, 643L: 7, 658L: 8, 713L: 8, 740L: 5, 742L: 8, 743L: 8, 800L: 8, 810L: 2, 814L: 8, 829L: 2, 830L: 7, 835L: 8, 836L: 8, 863L: 8, 869L: 7, 870L: 7, 871L: 2, 898L: 8, 900L: 6, 902L: 6, 905L: 8, 918L: 8, 921L: 8, 933L: 8, 944L: 8, 945L: 8, 950L: 8, 951L: 8, 953L: 8, 955L: 8, 956L: 8, 971L: 7, 975L: 5, 993L: 8, 998L: 5, 999L: 7, 1000L: 8, 1001L: 8, 1014L: 8, 1017L: 8, 1020L: 8, 1041L: 8, 1042L: 8, 1044L: 8, 1056L: 8, 1057L: 8, 1059L: 1, 1071L: 8, 1077L: 8, 1082L: 8, 1083L: 8, 1084L: 8, 1085L: 8, 1086L: 8, 1114L: 8, 1132L: 8, 1161L: 8, 1162L: 8, 1163L: 8, 1175L: 8, 1227L: 8, 1228L: 8, 1235L: 8, 1237L: 8, 1279L: 8, 1552L: 8, 1553L: 8, 1556L: 8, 1557L: 8, 1568L: 8, 1570L: 8, 1571L: 8, 1572L: 8, 1595L: 8, 1777L: 8, 1779L: 8, 1904L: 8, 1912L: 8, 1990L: 8, 1998L: 8
|
||||
},
|
||||
# Prius Prime
|
||||
{
|
||||
36L: 8, 37L: 8, 166L: 8, 170L: 8, 180L: 8, 295L: 8, 296L: 8, 426L: 6, 452L: 8, 466L: 8, 467L: 8, 550L: 8, 552L: 4, 560L: 7, 562L: 6, 581L: 5, 608L: 8, 610L: 8, 614L: 8, 643L: 7, 658L: 8, 713L: 8, 740L: 5, 742L: 8, 743L: 8, 800L: 8, 810L: 2, 814L: 8, 824L: 2, 829L: 2, 830L: 7, 835L: 8, 836L: 8, 863L: 8, 869L: 7, 870L: 7, 871L: 2,898L: 8, 900L: 6, 902L: 6, 905L: 8, 913L: 8, 918L: 8, 921L: 8, 933L: 8, 944L: 8, 945L: 8, 950L: 8, 951L: 8, 953L: 8, 955L: 8, 956L: 8, 971L: 7, 974L: 8, 975L: 5, 993L: 8, 998L: 5, 999L: 7, 1000L: 8, 1001L: 8, 1014L: 8, 1017L: 8, 1020L: 8, 1041L: 8, 1042L: 8, 1044L: 8, 1056L: 8, 1057L: 8, 1059L: 1, 1071L: 8, 1076L: 8, 1077L: 8, 1082L: 8, 1083L: 8, 1084L: 8, 1085L: 8, 1086L: 8, 1114L: 8, 1132L: 8, 1161L: 8, 1162L: 8, 1163L: 8, 1164L: 8, 1165L: 8, 1166L: 8, 1167L: 8, 1175L: 8, 1227L: 8, 1228L: 8, 1235L: 8, 1237L: 8, 1279L: 8, 1552L: 8, 1553L: 8, 1556L: 8, 1557L: 8, 1568L: 8, 1570L: 8, 1571L: 8, 1572L: 8, 1595L: 8, 1777L: 8, 1779L: 8, 1904L: 8, 1912L: 8, 1990L: 8, 1998L: 8
|
||||
},
|
||||
# Taiwanese Prius Prime
|
||||
{
|
||||
36L: 8, 37L: 8, 166L: 8, 170L: 8, 180L: 8, 295L: 8, 296L: 8, 426L: 6, 452L: 8, 466L: 8, 467L: 8, 550L: 8, 552L: 4, 560L: 7, 562L: 6, 581L: 5, 608L: 8, 610L: 8, 614L: 8, 643L: 7, 658L: 8, 713L: 8, 740L: 5, 742L: 8, 743L: 8, 800L: 8, 810L: 2, 814L: 8, 824L: 2, 829L: 2, 830L: 7, 835L: 8, 836L: 8, 845L: 5, 863L: 8, 869L: 7, 870L: 7, 871L: 2,898L: 8, 900L: 6, 902L: 6, 905L: 8, 913L: 8, 918L: 8, 921L: 8, 933L: 8, 944L: 8, 945L: 8, 950L: 8, 951L: 8, 953L: 8, 955L: 8, 956L: 8, 971L: 7, 974L: 8, 975L: 5, 993L: 8, 998L: 5, 999L: 7, 1000L: 8, 1001L: 8, 1005L: 2, 1014L: 8, 1017L: 8, 1020L: 8, 1041L: 8, 1042L: 8, 1044L: 8, 1056L: 8, 1057L: 8, 1059L: 1, 1071L: 8, 1076L: 8, 1077L: 8, 1082L: 8, 1083L: 8, 1084L: 8, 1085L: 8, 1086L: 8, 1114L: 8, 1132L: 8, 1161L: 8, 1162L: 8, 1163L: 8, 1164L: 8, 1165L: 8, 1166L: 8, 1167L: 8, 1175L: 8, 1227L: 8, 1228L: 8, 1235L: 8, 1237L: 8, 1264L: 8, 1279L: 8, 1552L: 8, 1553L: 8, 1556L: 8, 1557L: 8, 1568L: 8, 1570L: 8, 1571L: 8, 1572L: 8, 1595L: 8, 1777L: 8, 1779L: 8, 1904L: 8, 1912L: 8, 1990L: 8, 1998L: 8
|
||||
}
|
||||
],
|
||||
TOYOTA.COROLLA: {
|
||||
36: 8, 37: 8, 170: 8, 180: 8, 186: 4, 426: 6, 452: 8, 464: 8, 466: 8, 467: 8, 547: 8, 548: 8, 552: 4, 608: 8, 610: 5, 643: 7, 705: 8, 740: 5, 800: 8, 835: 8, 836: 8, 849: 4, 869: 7, 870: 7, 871: 2, 896: 8, 897: 8, 900: 6, 902: 6, 905: 8, 911: 8, 916: 2, 921: 8, 933: 8, 944: 8, 945: 8, 951: 8, 955: 4, 956: 8, 979: 2, 992: 8, 998: 5, 999: 7, 1000: 8, 1001: 8, 1017: 8, 1041: 8, 1042: 8, 1043: 8, 1044: 8, 1056: 8, 1059: 1, 1114: 8, 1161: 8, 1162: 8, 1163: 8, 1196: 8, 1227: 8, 1235: 8, 1279: 8, 1552: 8, 1553: 8, 1556: 8, 1557: 8, 1561: 8, 1562: 8, 1568: 8, 1569: 8, 1570: 8, 1571: 8, 1572: 8, 1584: 8, 1589: 8, 1592: 8, 1596: 8, 1597: 8, 1600: 8, 1664: 8, 1728: 8, 1779: 8, 1904: 8, 1912: 8, 1990: 8, 1998: 8
|
||||
},
|
||||
TOYOTA.LEXUS_RXH: {
|
||||
36: 8, 37: 8, 166: 8, 170: 8, 180: 8, 295: 8, 296: 8, 426: 6, 452: 8, 466: 8, 467: 8, 550: 8, 552: 4, 560: 7, 562: 6, 581: 5, 608: 8, 610: 5, 643: 7, 658: 8, 713: 8, 740: 5, 742: 8, 743: 8, 800: 8, 810: 2, 812: 3, 814: 8, 830: 7, 835: 8, 836: 8, 845: 5, 863: 8, 869: 7, 870: 7, 871: 2, 898: 8, 900: 6, 902: 6, 905: 8, 913: 8, 918: 8, 921: 8, 933: 8, 944: 8, 945: 8, 950: 8, 951: 8, 953: 8, 955: 8, 956: 8, 971: 7, 975: 6, 993: 8, 998: 5, 999: 7, 1000: 8, 1001: 8, 1005: 2, 1014: 8, 1017: 8, 1020: 8, 1041: 8, 1042: 8, 1044: 8, 1056: 8, 1059: 1, 1063: 8, 1071: 8, 1077: 8, 1082: 8, 1114: 8, 1161: 8, 1162: 8, 1163: 8, 1164: 8, 1165: 8, 1166: 8, 1167: 8, 1227: 8, 1228: 8, 1235: 8, 1237: 8, 1264: 8, 1279: 8, 1552: 8, 1553: 8, 1556: 8, 1557: 8, 1568: 8, 1570: 8, 1571: 8, 1572: 8, 1575: 8, 1595: 8, 1777: 8, 1779: 8, 1808: 8, 1810: 8, 1816: 8, 1818: 8, 1840: 8, 1848: 8, 1904: 8, 1912: 8, 1940: 8, 1941: 8, 1948: 8, 1949: 8, 1952: 8, 1956: 8, 1960: 8, 1964: 8, 1986: 8, 1990: 8, 1994: 8, 1998: 8, 2004: 8, 2012: 8
|
||||
},
|
||||
}
|
||||
|
||||
# support additional internal only fingerprints
|
||||
try:
|
||||
from common.fingerprints_internal import add_additional_fingerprints
|
||||
add_additional_fingerprints(_FINGERPRINTS)
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
|
||||
def is_valid_for_fingerprint(msg, car_fingerprint):
|
||||
adr = msg.address
|
||||
return msg.src != 0 or (adr in car_fingerprint and car_fingerprint[adr] == len(msg.dat))
|
||||
|
||||
|
||||
def eliminate_incompatible_cars(msg, candidate_cars):
|
||||
"""Removes cars that could not have sent msg.
|
||||
|
||||
Inputs:
|
||||
msg: A cereal/log CanData message from the car.
|
||||
candidate_cars: A list of cars to consider.
|
||||
|
||||
Returns:
|
||||
A list containing the subset of candidate_cars that could have sent msg.
|
||||
"""
|
||||
compatible_cars = []
|
||||
for car_name in candidate_cars:
|
||||
car_fingerprints = _FINGERPRINTS[car_name]
|
||||
if not isinstance(car_fingerprints, list):
|
||||
car_fingerprints = [car_fingerprints]
|
||||
|
||||
for fingerprint in car_fingerprints:
|
||||
if is_valid_for_fingerprint(msg, fingerprint):
|
||||
compatible_cars.append(car_name)
|
||||
break
|
||||
|
||||
return compatible_cars
|
||||
|
||||
|
||||
def all_known_cars():
|
||||
"""Returns a list of all known car strings."""
|
||||
return _FINGERPRINTS.keys()
|
||||
0
common/kalman/__init__.py
Normal file
0
common/kalman/__init__.py
Normal file
251
common/kalman/ekf.py
Normal file
251
common/kalman/ekf.py
Normal file
@@ -0,0 +1,251 @@
|
||||
import abc
|
||||
import numpy as np
|
||||
# The EKF class contains the framework for an Extended Kalman Filter, but must be subclassed to use.
|
||||
# A subclass must implement:
|
||||
# 1) calc_transfer_fun(); see bottom of file for more info.
|
||||
# 2) __init__() to initialize self.state, self.covar, and self.process_noise appropriately
|
||||
|
||||
# Alternatively, the existing implementations of EKF can be used (e.g. EKF2D)
|
||||
|
||||
# Sensor classes are optionally used to pass measurement information into the EKF, to keep
|
||||
# sensor parameters and processing methods for a each sensor together.
|
||||
# Sensor classes have a read() method which takes raw sensor data and returns
|
||||
# a SensorReading object, which can be passed to the EKF update() method.
|
||||
|
||||
# For usage, see run_ekf1d.py in selfdrive/new for a simple example.
|
||||
# ekf.predict(dt) should be called between update cycles with the time since it was last called.
|
||||
# Ideally, predict(dt) should be called at a relatively constant rate.
|
||||
# update() should be called once per sensor, and can be called multiple times between predict steps.
|
||||
# Access and set the state of the filter directly with ekf.state and ekf.covar.
|
||||
|
||||
|
||||
class SensorReading:
|
||||
# Given a perfect model and no noise, data = obs_model * state
|
||||
def __init__(self, data, covar, obs_model):
|
||||
self.data = data
|
||||
self.obs_model = obs_model
|
||||
self.covar = covar
|
||||
|
||||
def __repr__(self):
|
||||
return "SensorReading(data={}, covar={}, obs_model={})".format(
|
||||
repr(self.data), repr(self.covar), repr(self.obs_model))
|
||||
|
||||
|
||||
# A generic sensor class that does no pre-processing of data
|
||||
class SimpleSensor:
|
||||
# obs_model can be
|
||||
# a full observation model matrix, or
|
||||
# an integer or tuple of indices into ekf.state, indicating which variables are being directly observed
|
||||
# covar can be
|
||||
# a full covariance matrix
|
||||
# a float or tuple of individual covars for each component of the sensor reading
|
||||
# dims is the number of states in the EKF
|
||||
def __init__(self, obs_model, covar, dims):
|
||||
# Allow for integer covar/obs_model
|
||||
if not hasattr(obs_model, "__len__"):
|
||||
obs_model = (obs_model, )
|
||||
if not hasattr(covar, "__len__"):
|
||||
covar = (covar, )
|
||||
|
||||
# Full observation model passed
|
||||
if dims in np.array(obs_model).shape:
|
||||
self.obs_model = np.asmatrix(obs_model)
|
||||
self.covar = np.asmatrix(covar)
|
||||
# Indices of unit observations passed
|
||||
else:
|
||||
self.obs_model = np.matlib.zeros((len(obs_model), dims))
|
||||
self.obs_model[:, list(obs_model)] = np.identity(len(obs_model))
|
||||
if np.asarray(covar).ndim == 2:
|
||||
self.covar = np.asmatrix(covar)
|
||||
elif len(covar) == len(obs_model):
|
||||
self.covar = np.matlib.diag(covar)
|
||||
else:
|
||||
self.covar = np.matlib.identity(len(obs_model)) * covar
|
||||
|
||||
def read(self, data, covar=None):
|
||||
if covar:
|
||||
self.covar = covar
|
||||
return SensorReading(data, self.covar, self.obs_model)
|
||||
|
||||
|
||||
class EKF:
|
||||
__metaclass__ = abc.ABCMeta
|
||||
|
||||
def __init__(self, debug=False):
|
||||
self.DEBUG = debug
|
||||
|
||||
|
||||
def __str__(self):
|
||||
return "EKF(state={}, covar={})".format(self.state, self.covar)
|
||||
|
||||
# Measurement update
|
||||
# Reading should be a SensorReading object with data, covar, and obs_model attributes
|
||||
def update(self, reading):
|
||||
# Potential improvements:
|
||||
# deal with negative covars
|
||||
# add noise to really low covars to ensure stability
|
||||
# use mahalanobis distance to reject outliers
|
||||
# wrap angles after state updates and innovation
|
||||
|
||||
# y = z - H*x
|
||||
innovation = reading.data - reading.obs_model * self.state
|
||||
|
||||
if self.DEBUG:
|
||||
print "reading:\n",reading.data
|
||||
print "innovation:\n",innovation
|
||||
|
||||
# S = H*P*H' + R
|
||||
innovation_covar = reading.obs_model * self.covar * reading.obs_model.T + reading.covar
|
||||
|
||||
# K = P*H'*S^-1
|
||||
kalman_gain = self.covar * reading.obs_model.T * np.linalg.inv(
|
||||
innovation_covar)
|
||||
|
||||
if self.DEBUG:
|
||||
print "gain:\n", kalman_gain
|
||||
print "innovation_covar:\n", innovation_covar
|
||||
print "innovation: ", innovation
|
||||
print "test: ", self.covar * reading.obs_model.T * (
|
||||
reading.obs_model * self.covar * reading.obs_model.T + reading.covar *
|
||||
0).I
|
||||
|
||||
# x = x + K*y
|
||||
self.state += kalman_gain*innovation
|
||||
|
||||
# print "covar", np.diag(self.covar)
|
||||
#self.state[(roll_vel, yaw_vel, pitch_vel),:] = reading.data
|
||||
|
||||
# Standard form: P = (I - K*H)*P
|
||||
# self.covar = (self.identity - kalman_gain*reading.obs_model) * self.covar
|
||||
|
||||
# Use the Joseph form for numerical stability: P = (I-K*H)*P*(I - K*H)' + K*R*K'
|
||||
aux_mtrx = (self.identity - kalman_gain * reading.obs_model)
|
||||
self.covar = aux_mtrx * self.covar * aux_mtrx.T + kalman_gain * reading.covar * kalman_gain.T
|
||||
|
||||
if self.DEBUG:
|
||||
print "After update"
|
||||
print "state\n", self.state
|
||||
print "covar:\n",self.covar
|
||||
|
||||
def update_scalar(self, reading):
|
||||
# like update but knowing that measurement is a scalar
|
||||
# this avoids matrix inversions and speeds up (surprisingly) drived.py a lot
|
||||
|
||||
# innovation = reading.data - np.matmul(reading.obs_model, self.state)
|
||||
# innovation_covar = np.matmul(np.matmul(reading.obs_model, self.covar), reading.obs_model.T) + reading.covar
|
||||
# kalman_gain = np.matmul(self.covar, reading.obs_model.T)/innovation_covar
|
||||
# self.state += np.matmul(kalman_gain, innovation)
|
||||
# aux_mtrx = self.identity - np.matmul(kalman_gain, reading.obs_model)
|
||||
# self.covar = np.matmul(aux_mtrx, np.matmul(self.covar, aux_mtrx.T)) + np.matmul(kalman_gain, np.matmul(reading.covar, kalman_gain.T))
|
||||
|
||||
# written without np.matmul
|
||||
es = np.einsum
|
||||
ABC_T = "ij,jk,lk->il"
|
||||
AB_T = "ij,kj->ik"
|
||||
AB = "ij,jk->ik"
|
||||
innovation = reading.data - es(AB, reading.obs_model, self.state)
|
||||
innovation_covar = es(ABC_T, reading.obs_model, self.covar,
|
||||
reading.obs_model) + reading.covar
|
||||
kalman_gain = es(AB_T, self.covar, reading.obs_model) / innovation_covar
|
||||
|
||||
self.state += es(AB, kalman_gain, innovation)
|
||||
aux_mtrx = self.identity - es(AB, kalman_gain, reading.obs_model)
|
||||
self.covar = es(ABC_T, aux_mtrx, self.covar, aux_mtrx) + \
|
||||
es(ABC_T, kalman_gain, reading.covar, kalman_gain)
|
||||
|
||||
# Prediction update
|
||||
def predict(self, dt):
|
||||
es = np.einsum
|
||||
ABC_T = "ij,jk,lk->il"
|
||||
AB = "ij,jk->ik"
|
||||
|
||||
# State update
|
||||
transfer_fun, transfer_fun_jacobian = self.calc_transfer_fun(dt)
|
||||
|
||||
# self.state = np.matmul(transfer_fun, self.state)
|
||||
# self.covar = np.matmul(np.matmul(transfer_fun_jacobian, self.covar), transfer_fun_jacobian.T) + self.process_noise * dt
|
||||
|
||||
# x = f(x, u), written in the form x = A(x, u)*x
|
||||
self.state = es(AB, transfer_fun, self.state)
|
||||
|
||||
# P = J*P*J' + Q
|
||||
self.covar = es(ABC_T, transfer_fun_jacobian, self.covar,
|
||||
transfer_fun_jacobian) + self.process_noise * dt #!dt
|
||||
|
||||
#! Clip covariance to avoid explosions
|
||||
self.covar = np.clip(self.covar,-1e10,1e10)
|
||||
|
||||
@abc.abstractmethod
|
||||
def calc_transfer_fun(self, dt):
|
||||
"""Return a tuple with the transfer function and transfer function jacobian
|
||||
The transfer function and jacobian should both be a numpy matrix of size DIMSxDIMS
|
||||
|
||||
The transfer function matrix A should satisfy the state-update equation
|
||||
x_(k+1) = A * x_k
|
||||
|
||||
The jacobian J is the direct jacobian A*x_k. For linear systems J=A.
|
||||
|
||||
Current implementations calculate A and J as functions of state. Control input
|
||||
can be added trivially by adding a control parameter to predict() and calc_tranfer_update(),
|
||||
and using it during calculation of A and J
|
||||
"""
|
||||
|
||||
|
||||
class FastEKF1D(EKF):
|
||||
"""Fast version of EKF for 1D problems with scalar readings."""
|
||||
|
||||
def __init__(self, dt, var_init, Q):
|
||||
super(FastEKF1D, self).__init__(False)
|
||||
self.state = [0, 0]
|
||||
self.covar = [var_init, var_init, 0]
|
||||
|
||||
# Process Noise
|
||||
self.dtQ0 = dt * Q[0]
|
||||
self.dtQ1 = dt * Q[1]
|
||||
|
||||
def update(self, reading):
|
||||
raise NotImplementedError
|
||||
|
||||
def update_scalar(self, reading):
|
||||
# TODO(mgraczyk): Delete this for speed.
|
||||
# assert np.all(reading.obs_model == [1, 0])
|
||||
|
||||
rcov = reading.covar[0, 0]
|
||||
|
||||
x = self.state
|
||||
S = self.covar
|
||||
|
||||
innovation = reading.data - x[0]
|
||||
innovation_covar = S[0] + rcov
|
||||
|
||||
k0 = S[0] / innovation_covar
|
||||
k1 = S[2] / innovation_covar
|
||||
|
||||
x[0] += k0 * innovation
|
||||
x[1] += k1 * innovation
|
||||
|
||||
mk = 1 - k0
|
||||
S[1] += k1 * (k1 * (S[0] + rcov) - 2 * S[2])
|
||||
S[2] = mk * (S[2] - k1 * S[0]) + rcov * k0 * k1
|
||||
S[0] = mk * mk * S[0] + rcov * k0 * k0
|
||||
|
||||
def predict(self, dt):
|
||||
# State update
|
||||
x = self.state
|
||||
|
||||
x[0] += dt * x[1]
|
||||
|
||||
# P = J*P*J' + Q
|
||||
S = self.covar
|
||||
S[0] += dt * (2 * S[2] + dt * S[1]) + self.dtQ0
|
||||
S[2] += dt * S[1]
|
||||
S[1] += self.dtQ1
|
||||
|
||||
# Clip covariance to avoid explosions
|
||||
S = max(-1e10, min(S, 1e10))
|
||||
|
||||
def calc_transfer_fun(self, dt):
|
||||
tf = np.identity(2)
|
||||
tf[0, 1] = dt
|
||||
tfj = tf
|
||||
return tf, tfj
|
||||
23
common/kalman/simple_kalman.py
Normal file
23
common/kalman/simple_kalman.py
Normal file
@@ -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
|
||||
142
common/logging_extra.py
Normal file
142
common/logging_extra.py
Normal file
@@ -0,0 +1,142 @@
|
||||
import os
|
||||
import sys
|
||||
import copy
|
||||
import json
|
||||
import socket
|
||||
import logging
|
||||
from threading import local
|
||||
from collections import OrderedDict
|
||||
from contextlib import contextmanager
|
||||
|
||||
def json_handler(obj):
|
||||
# if isinstance(obj, (datetime.date, datetime.time)):
|
||||
# return obj.isoformat()
|
||||
return repr(obj)
|
||||
|
||||
def json_robust_dumps(obj):
|
||||
return json.dumps(obj, default=json_handler)
|
||||
|
||||
class NiceOrderedDict(OrderedDict):
|
||||
def __str__(self):
|
||||
return '{'+', '.join("%r: %r" % p for p in self.iteritems())+'}'
|
||||
|
||||
class SwagFormatter(logging.Formatter):
|
||||
def __init__(self, swaglogger):
|
||||
logging.Formatter.__init__(self, None, '%a %b %d %H:%M:%S %Z %Y')
|
||||
|
||||
self.swaglogger = swaglogger
|
||||
self.host = socket.gethostname()
|
||||
|
||||
def format_dict(self, record):
|
||||
record_dict = NiceOrderedDict()
|
||||
|
||||
if isinstance(record.msg, dict):
|
||||
record_dict['msg'] = record.msg
|
||||
else:
|
||||
try:
|
||||
record_dict['msg'] = record.getMessage()
|
||||
except (ValueError, TypeError):
|
||||
record_dict['msg'] = [record.msg]+record.args
|
||||
|
||||
record_dict['ctx'] = self.swaglogger.get_ctx()
|
||||
|
||||
if record.exc_info:
|
||||
record_dict['exc_info'] = self.formatException(record.exc_info)
|
||||
|
||||
record_dict['level'] = record.levelname
|
||||
record_dict['levelnum'] = record.levelno
|
||||
record_dict['name'] = record.name
|
||||
record_dict['filename'] = record.filename
|
||||
record_dict['lineno'] = record.lineno
|
||||
record_dict['pathname'] = record.pathname
|
||||
record_dict['module'] = record.module
|
||||
record_dict['funcName'] = record.funcName
|
||||
record_dict['host'] = self.host
|
||||
record_dict['process'] = record.process
|
||||
record_dict['thread'] = record.thread
|
||||
record_dict['threadName'] = record.threadName
|
||||
record_dict['created'] = record.created
|
||||
|
||||
return record_dict
|
||||
|
||||
def format(self, record):
|
||||
return json_robust_dumps(self.format_dict(record))
|
||||
|
||||
_tmpfunc = lambda: 0
|
||||
_srcfile = os.path.normcase(_tmpfunc.__code__.co_filename)
|
||||
|
||||
class SwagLogger(logging.Logger):
|
||||
def __init__(self):
|
||||
logging.Logger.__init__(self, "swaglog")
|
||||
|
||||
self.global_ctx = {}
|
||||
|
||||
self.log_local = local()
|
||||
self.log_local.ctx = {}
|
||||
|
||||
def findCaller(self):
|
||||
"""
|
||||
Find the stack frame of the caller so that we can note the source
|
||||
file name, line number and function name.
|
||||
"""
|
||||
# f = currentframe()
|
||||
f = sys._getframe(3)
|
||||
#On some versions of IronPython, currentframe() returns None if
|
||||
#IronPython isn't run with -X:Frames.
|
||||
if f is not None:
|
||||
f = f.f_back
|
||||
rv = "(unknown file)", 0, "(unknown function)"
|
||||
while hasattr(f, "f_code"):
|
||||
co = f.f_code
|
||||
filename = os.path.normcase(co.co_filename)
|
||||
if filename in (logging._srcfile, _srcfile):
|
||||
f = f.f_back
|
||||
continue
|
||||
rv = (co.co_filename, f.f_lineno, co.co_name)
|
||||
break
|
||||
return rv
|
||||
|
||||
def local_ctx(self):
|
||||
try:
|
||||
return self.log_local.ctx
|
||||
except AttributeError:
|
||||
self.log_local.ctx = {}
|
||||
return self.log_local.ctx
|
||||
|
||||
def get_ctx(self):
|
||||
return dict(self.local_ctx(), **self.global_ctx)
|
||||
|
||||
@contextmanager
|
||||
def ctx(self, **kwargs):
|
||||
old_ctx = self.local_ctx()
|
||||
self.log_local.ctx = copy.copy(old_ctx) or {}
|
||||
self.log_local.ctx.update(kwargs)
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
self.log_local.ctx = old_ctx
|
||||
|
||||
def bind(self, **kwargs):
|
||||
self.local_ctx().update(kwargs)
|
||||
|
||||
def bind_global(self, **kwargs):
|
||||
self.global_ctx.update(kwargs)
|
||||
|
||||
def event(self, event_name, *args, **kwargs):
|
||||
evt = NiceOrderedDict()
|
||||
evt['event'] = event_name
|
||||
if args:
|
||||
evt['args'] = args
|
||||
evt.update(kwargs)
|
||||
self.info(evt)
|
||||
|
||||
if __name__ == "__main__":
|
||||
log = SwagLogger()
|
||||
|
||||
log.info("asdasd %s", "a")
|
||||
log.info({'wut': 1})
|
||||
|
||||
with log.ctx():
|
||||
log.bind(user="some user")
|
||||
log.info("in req")
|
||||
log.event("do_req")
|
||||
29
common/numpy_fast.py
Normal file
29
common/numpy_fast.py
Normal file
@@ -0,0 +1,29 @@
|
||||
def int_rnd(x):
|
||||
return int(round(x))
|
||||
|
||||
|
||||
def clip(x, lo, hi):
|
||||
return max(lo, min(hi, x))
|
||||
|
||||
|
||||
def interp(x, xp, fp):
|
||||
N = len(xp)
|
||||
if not hasattr(x, '__iter__'):
|
||||
hi = 0
|
||||
while hi < N and x > xp[hi]:
|
||||
hi += 1
|
||||
low = hi - 1
|
||||
return fp[-1] if hi == N and x > xp[low] else (
|
||||
fp[0] if hi == 0 else
|
||||
(x - xp[low]) * (fp[hi] - fp[low]) / (xp[hi] - xp[low]) + fp[low])
|
||||
|
||||
result = []
|
||||
for v in x:
|
||||
hi = 0
|
||||
while hi < N and v > xp[hi]:
|
||||
hi += 1
|
||||
low = hi - 1
|
||||
result.append(fp[-1] if hi == N and v > xp[low] else (fp[
|
||||
0] if hi == 0 else (v - xp[low]) * (fp[hi] - fp[low]) / (xp[hi] - xp[low]
|
||||
) + fp[low]))
|
||||
return result
|
||||
330
common/params.py
Executable file
330
common/params.py
Executable file
@@ -0,0 +1,330 @@
|
||||
#!/usr/bin/env python
|
||||
"""ROS has a parameter server, we have files.
|
||||
|
||||
The parameter store is a persistent key value store, implemented as a directory with a writer lock.
|
||||
On Android, we store params under params_dir = /data/params. The writer lock is a file
|
||||
"<params_dir>/.lock" taken using flock(), and data is stored in a directory symlinked to by
|
||||
"<params_dir>/d".
|
||||
|
||||
Each key, value pair is stored as a file with named <key> with contents <value>, located in
|
||||
<params_dir>/d/<key>
|
||||
|
||||
Readers of a single key can just open("<params_dir>/d/<key>") and read the file contents.
|
||||
Readers who want a consistent snapshot of multiple keys should take the lock.
|
||||
|
||||
Writers should take the lock before modifying anything. Writers should also leave the DB in a
|
||||
consistent state after a crash. The implementation below does this by copying all params to a temp
|
||||
directory <params_dir>/<tmp>, then atomically symlinking <params_dir>/<d> to <params_dir>/<tmp>
|
||||
before deleting the old <params_dir>/<d> directory.
|
||||
|
||||
Writers that only modify a single key can simply take the lock, then swap the corresponding value
|
||||
file in place without messing with <params_dir>/d.
|
||||
"""
|
||||
import time
|
||||
import os
|
||||
import errno
|
||||
import sys
|
||||
import shutil
|
||||
import fcntl
|
||||
import tempfile
|
||||
from enum import Enum
|
||||
|
||||
def mkdirs_exists_ok(path):
|
||||
try:
|
||||
os.makedirs(path)
|
||||
except OSError:
|
||||
if not os.path.isdir(path):
|
||||
raise
|
||||
|
||||
class TxType(Enum):
|
||||
PERSISTANT = 1
|
||||
CLEAR_ON_MANAGER_START = 2
|
||||
CLEAR_ON_CAR_START = 3
|
||||
|
||||
class UnknownKeyName(Exception):
|
||||
pass
|
||||
|
||||
keys = {
|
||||
# written: manager
|
||||
# read: loggerd, uploaderd, baseui
|
||||
"DongleId": TxType.PERSISTANT,
|
||||
"AccessToken": TxType.PERSISTANT,
|
||||
"Version": TxType.PERSISTANT,
|
||||
"GitCommit": TxType.PERSISTANT,
|
||||
"GitBranch": TxType.PERSISTANT,
|
||||
"GitRemote": TxType.PERSISTANT,
|
||||
# written: baseui
|
||||
# read: ui, controls
|
||||
"IsMetric": TxType.PERSISTANT,
|
||||
"IsRearViewMirror": TxType.PERSISTANT,
|
||||
"IsFcwEnabled": TxType.PERSISTANT,
|
||||
"HasAcceptedTerms": TxType.PERSISTANT,
|
||||
"IsUploadVideoOverCellularEnabled": TxType.PERSISTANT,
|
||||
# written: visiond
|
||||
# read: visiond, controlsd
|
||||
"CalibrationParams": TxType.PERSISTANT,
|
||||
# written: visiond
|
||||
# read: visiond, ui
|
||||
"CloudCalibration": TxType.PERSISTANT,
|
||||
# written: controlsd
|
||||
# read: radard
|
||||
"CarParams": TxType.CLEAR_ON_CAR_START,
|
||||
|
||||
"Passive": TxType.PERSISTANT,
|
||||
"DoUninstall": TxType.CLEAR_ON_MANAGER_START,
|
||||
}
|
||||
|
||||
def fsync_dir(path):
|
||||
fd = os.open(path, os.O_RDONLY)
|
||||
try:
|
||||
os.fsync(fd)
|
||||
finally:
|
||||
os.close(fd)
|
||||
|
||||
|
||||
class FileLock(object):
|
||||
def __init__(self, path, create):
|
||||
self._path = path
|
||||
self._create = create
|
||||
self._fd = None
|
||||
|
||||
def acquire(self):
|
||||
self._fd = os.open(self._path, os.O_CREAT if self._create else 0)
|
||||
fcntl.flock(self._fd, fcntl.LOCK_EX)
|
||||
|
||||
def release(self):
|
||||
if self._fd is not None:
|
||||
os.close(self._fd)
|
||||
self._fd = None
|
||||
|
||||
|
||||
class DBAccessor(object):
|
||||
def __init__(self, path):
|
||||
self._path = path
|
||||
self._vals = None
|
||||
|
||||
def keys(self):
|
||||
self._check_entered()
|
||||
return self._vals.keys()
|
||||
|
||||
def get(self, key):
|
||||
self._check_entered()
|
||||
try:
|
||||
return self._vals[key]
|
||||
except KeyError:
|
||||
return None
|
||||
|
||||
def _get_lock(self, create):
|
||||
lock = FileLock(os.path.join(self._path, ".lock"), create)
|
||||
lock.acquire()
|
||||
return lock
|
||||
|
||||
def _read_values_locked(self):
|
||||
"""Callers should hold a lock while calling this method."""
|
||||
vals = {}
|
||||
try:
|
||||
data_path = self._data_path()
|
||||
keys = os.listdir(data_path)
|
||||
for key in keys:
|
||||
with open(os.path.join(data_path, key), "rb") as f:
|
||||
vals[key] = f.read()
|
||||
except (OSError, IOError) as e:
|
||||
# Either the DB hasn't been created yet, or somebody wrote a bug and left the DB in an
|
||||
# inconsistent state. Either way, return empty.
|
||||
if e.errno == errno.ENOENT:
|
||||
return {}
|
||||
|
||||
return vals
|
||||
|
||||
def _data_path(self):
|
||||
return os.path.join(self._path, "d")
|
||||
|
||||
def _check_entered(self):
|
||||
if self._vals is None:
|
||||
raise Exception("Must call __enter__ before using DB")
|
||||
|
||||
|
||||
class DBReader(DBAccessor):
|
||||
def __enter__(self):
|
||||
try:
|
||||
lock = self._get_lock(False)
|
||||
except OSError as e:
|
||||
# Do not create lock if it does not exist.
|
||||
if e.errno == errno.ENOENT:
|
||||
self._vals = {}
|
||||
return self
|
||||
|
||||
try:
|
||||
# Read everything.
|
||||
self._vals = self._read_values_locked()
|
||||
return self
|
||||
finally:
|
||||
lock.release()
|
||||
|
||||
def __exit__(self, type, value, traceback): pass
|
||||
|
||||
|
||||
class DBWriter(DBAccessor):
|
||||
def __init__(self, path):
|
||||
super(DBWriter, self).__init__(path)
|
||||
self._lock = None
|
||||
self._prev_umask = None
|
||||
|
||||
def put(self, key, value):
|
||||
self._vals[key] = value
|
||||
|
||||
def delete(self, key):
|
||||
self._vals.pop(key, None)
|
||||
|
||||
def __enter__(self):
|
||||
mkdirs_exists_ok(self._path)
|
||||
|
||||
# Make sure we can write and that permissions are correct.
|
||||
self._prev_umask = os.umask(0)
|
||||
|
||||
try:
|
||||
os.chmod(self._path, 0o777)
|
||||
self._lock = self._get_lock(True)
|
||||
self._vals = self._read_values_locked()
|
||||
except:
|
||||
os.umask(self._prev_umask)
|
||||
self._prev_umask = None
|
||||
raise
|
||||
|
||||
return self
|
||||
|
||||
def __exit__(self, type, value, traceback):
|
||||
self._check_entered()
|
||||
|
||||
try:
|
||||
# data_path refers to the externally used path to the params. It is a symlink.
|
||||
# old_data_path is the path currently pointed to by data_path.
|
||||
# tempdir_path is a path where the new params will go, which the new data path will point to.
|
||||
# new_data_path is a temporary symlink that will atomically overwrite data_path.
|
||||
#
|
||||
# The current situation is:
|
||||
# data_path -> old_data_path
|
||||
# We're going to write params data to tempdir_path
|
||||
# tempdir_path -> params data
|
||||
# Then point new_data_path to tempdir_path
|
||||
# new_data_path -> tempdir_path
|
||||
# Then atomically overwrite data_path with new_data_path
|
||||
# data_path -> tempdir_path
|
||||
old_data_path = None
|
||||
new_data_path = None
|
||||
tempdir_path = tempfile.mkdtemp(prefix=".tmp", dir=self._path)
|
||||
|
||||
try:
|
||||
# Write back all keys.
|
||||
os.chmod(tempdir_path, 0o777)
|
||||
for k, v in self._vals.items():
|
||||
with open(os.path.join(tempdir_path, k), "wb") as f:
|
||||
f.write(v)
|
||||
f.flush()
|
||||
os.fsync(f.fileno())
|
||||
fsync_dir(tempdir_path)
|
||||
|
||||
data_path = self._data_path()
|
||||
try:
|
||||
old_data_path = os.path.join(self._path, os.readlink(data_path))
|
||||
except (OSError, IOError):
|
||||
# NOTE(mgraczyk): If other DB implementations have bugs, this could cause
|
||||
# copies to be left behind, but we still want to overwrite.
|
||||
pass
|
||||
|
||||
new_data_path = "{}.link".format(tempdir_path)
|
||||
os.symlink(os.path.basename(tempdir_path), new_data_path)
|
||||
os.rename(new_data_path, data_path)
|
||||
fsync_dir(self._path)
|
||||
finally:
|
||||
# If the rename worked, we can delete the old data. Otherwise delete the new one.
|
||||
success = new_data_path is not None and os.path.exists(data_path) and (
|
||||
os.readlink(data_path) == os.path.basename(tempdir_path))
|
||||
|
||||
if success:
|
||||
if old_data_path is not None:
|
||||
shutil.rmtree(old_data_path)
|
||||
else:
|
||||
shutil.rmtree(tempdir_path)
|
||||
|
||||
# Regardless of what happened above, there should be no link at new_data_path.
|
||||
if new_data_path is not None and os.path.islink(new_data_path):
|
||||
os.remove(new_data_path)
|
||||
finally:
|
||||
os.umask(self._prev_umask)
|
||||
self._prev_umask = None
|
||||
|
||||
# Always release the lock.
|
||||
self._lock.release()
|
||||
self._lock = None
|
||||
|
||||
|
||||
|
||||
class JSDB(object):
|
||||
def __init__(self, fn):
|
||||
self._fn = fn
|
||||
|
||||
def begin(self, write=False):
|
||||
if write:
|
||||
return DBWriter(self._fn)
|
||||
else:
|
||||
return DBReader(self._fn)
|
||||
|
||||
class Params(object):
|
||||
def __init__(self, db='/data/params'):
|
||||
self.env = JSDB(db)
|
||||
|
||||
def _clear_keys_with_type(self, tx_type):
|
||||
with self.env.begin(write=True) as txn:
|
||||
for key in keys:
|
||||
if keys[key] == tx_type:
|
||||
txn.delete(key)
|
||||
|
||||
def manager_start(self):
|
||||
self._clear_keys_with_type(TxType.CLEAR_ON_MANAGER_START)
|
||||
|
||||
def car_start(self):
|
||||
self._clear_keys_with_type(TxType.CLEAR_ON_CAR_START)
|
||||
|
||||
def delete(self, key):
|
||||
with self.env.begin(write=True) as txn:
|
||||
txn.delete(key)
|
||||
|
||||
def get(self, key, block=False):
|
||||
if key not in keys:
|
||||
raise UnknownKeyName(key)
|
||||
|
||||
while 1:
|
||||
with self.env.begin() as txn:
|
||||
ret = txn.get(key)
|
||||
if not block or ret is not None:
|
||||
break
|
||||
# is polling really the best we can do?
|
||||
time.sleep(0.05)
|
||||
return ret
|
||||
|
||||
def put(self, key, dat):
|
||||
if key not in keys:
|
||||
raise UnknownKeyName(key)
|
||||
|
||||
with self.env.begin(write=True) as txn:
|
||||
txn.put(key, dat)
|
||||
print "set", key
|
||||
|
||||
if __name__ == "__main__":
|
||||
params = Params()
|
||||
if len(sys.argv) > 2:
|
||||
params.put(sys.argv[1], sys.argv[2])
|
||||
else:
|
||||
for k in keys:
|
||||
pp = params.get(k)
|
||||
if pp is None:
|
||||
print k, "is None"
|
||||
elif all(ord(c) < 128 and ord(c) >= 32 for c in pp):
|
||||
print k, pp
|
||||
else:
|
||||
print k, pp.encode("hex")
|
||||
|
||||
# Test multiprocess:
|
||||
# seq 0 100000 | xargs -P20 -I{} python common/params.py DongleId {} && sleep 0.05
|
||||
# while python common/params.py DongleId; do sleep 0.05; done
|
||||
47
common/profiler.py
Normal file
47
common/profiler.py
Normal file
@@ -0,0 +1,47 @@
|
||||
import time
|
||||
|
||||
class Profiler(object):
|
||||
def __init__(self, enabled=False):
|
||||
self.enabled = enabled
|
||||
self.cp = {}
|
||||
self.cp_ignored = []
|
||||
self.iter = 0
|
||||
self.start_time = time.clock()
|
||||
self.last_time = self.start_time
|
||||
self.tot = 0.
|
||||
|
||||
def reset(self, enabled=False):
|
||||
self.enabled = enabled
|
||||
self.cp = {}
|
||||
self.cp_ignored = []
|
||||
self.iter = 0
|
||||
self.start_time = time.clock()
|
||||
self.last_time = self.start_time
|
||||
|
||||
def checkpoint(self, name, ignore=False):
|
||||
# ignore flag needed when benchmarking threads with ratekeeper
|
||||
if not self.enabled:
|
||||
return
|
||||
tt = time.clock()
|
||||
if name not in self.cp:
|
||||
self.cp[name] = 0.
|
||||
if ignore:
|
||||
self.cp_ignored.append(name)
|
||||
self.cp[name] += tt - self.last_time
|
||||
if not ignore:
|
||||
self.tot += tt - self.last_time
|
||||
self.last_time = tt
|
||||
|
||||
def display(self):
|
||||
if not self.enabled:
|
||||
return
|
||||
self.iter += 1
|
||||
print "******* Profiling *******"
|
||||
for n in self.cp:
|
||||
ms = self.cp[n]
|
||||
if n in self.cp_ignored:
|
||||
print "%30s: %7.2f perc: %1.0f" % (n, ms*1000.0, ms/self.tot*100), " IGNORED"
|
||||
else:
|
||||
print "%30s: %7.2f perc: %1.0f" % (n, ms*1000.0, ms/self.tot*100)
|
||||
print "Iter clock: %2.6f TOTAL: %2.2f" % (self.tot/self.iter, self.tot)
|
||||
|
||||
110
common/realtime.py
Normal file
110
common/realtime.py
Normal file
@@ -0,0 +1,110 @@
|
||||
"""Utilities for reading real time clocks and keeping soft real time constraints."""
|
||||
import os
|
||||
import time
|
||||
import platform
|
||||
import threading
|
||||
import subprocess
|
||||
import multiprocessing
|
||||
|
||||
from cffi import FFI
|
||||
ffi = FFI()
|
||||
ffi.cdef("""
|
||||
|
||||
typedef int clockid_t;
|
||||
struct timespec {
|
||||
long tv_sec; /* Seconds. */
|
||||
long tv_nsec; /* Nanoseconds. */
|
||||
};
|
||||
int clock_gettime (clockid_t clk_id, struct timespec *tp);
|
||||
|
||||
long syscall(long number, ...);
|
||||
|
||||
"""
|
||||
)
|
||||
libc = ffi.dlopen(None)
|
||||
|
||||
|
||||
# see <linux/time.h>
|
||||
CLOCK_MONOTONIC_RAW = 4
|
||||
CLOCK_BOOTTIME = 7
|
||||
|
||||
if platform.system() != 'Darwin' and hasattr(libc, 'clock_gettime'):
|
||||
c_clock_gettime = libc.clock_gettime
|
||||
|
||||
tlocal = threading.local()
|
||||
def clock_gettime(clk_id):
|
||||
if not hasattr(tlocal, 'ts'):
|
||||
tlocal.ts = ffi.new('struct timespec *')
|
||||
|
||||
ts = tlocal.ts
|
||||
|
||||
r = c_clock_gettime(clk_id, ts)
|
||||
if r != 0:
|
||||
raise OSError("clock_gettime")
|
||||
return ts.tv_sec + ts.tv_nsec * 1e-9
|
||||
else:
|
||||
# hack. only for OS X < 10.12
|
||||
def clock_gettime(clk_id):
|
||||
return time.time()
|
||||
|
||||
def monotonic_time():
|
||||
return clock_gettime(CLOCK_MONOTONIC_RAW)
|
||||
|
||||
def sec_since_boot():
|
||||
return clock_gettime(CLOCK_BOOTTIME)
|
||||
|
||||
|
||||
def set_realtime_priority(level):
|
||||
if os.getuid() != 0:
|
||||
print("not setting priority, not root")
|
||||
return
|
||||
if platform.machine() == "x86_64":
|
||||
NR_gettid = 186
|
||||
elif platform.machine() == "aarch64":
|
||||
NR_gettid = 178
|
||||
else:
|
||||
raise NotImplementedError
|
||||
|
||||
tid = libc.syscall(NR_gettid)
|
||||
return subprocess.call(['chrt', '-f', '-p', str(level), str(tid)])
|
||||
|
||||
|
||||
class Ratekeeper(object):
|
||||
def __init__(self, rate, print_delay_threshold=0.):
|
||||
"""Rate in Hz for ratekeeping. print_delay_threshold must be nonnegative."""
|
||||
self._interval = 1. / rate
|
||||
self._next_frame_time = sec_since_boot() + self._interval
|
||||
self._print_delay_threshold = print_delay_threshold
|
||||
self._frame = 0
|
||||
self._remaining = 0
|
||||
self._process_name = multiprocessing.current_process().name
|
||||
|
||||
@property
|
||||
def frame(self):
|
||||
return self._frame
|
||||
|
||||
@property
|
||||
def remaining(self):
|
||||
return self._remaining
|
||||
|
||||
# Maintain loop rate by calling this at the end of each loop
|
||||
def keep_time(self):
|
||||
lagged = self.monitor_time()
|
||||
if self._remaining > 0:
|
||||
time.sleep(self._remaining)
|
||||
return lagged
|
||||
|
||||
# this only monitor the cumulative lag, but does not enforce a rate
|
||||
def monitor_time(self):
|
||||
lagged = False
|
||||
remaining = self._next_frame_time - sec_since_boot()
|
||||
self._next_frame_time += self._interval
|
||||
if remaining < -self._print_delay_threshold:
|
||||
print("%s lagging by %.2f ms" % (self._process_name, -remaining * 1000))
|
||||
lagged = True
|
||||
self._frame += 1
|
||||
self._remaining = remaining
|
||||
return lagged
|
||||
|
||||
if __name__ == "__main__":
|
||||
print sec_since_boot()
|
||||
9
common/testing.py
Normal file
9
common/testing.py
Normal file
@@ -0,0 +1,9 @@
|
||||
import os
|
||||
from nose.tools import nottest
|
||||
|
||||
def phone_only(x):
|
||||
if os.path.isfile("/init.qcom.rc"):
|
||||
return x
|
||||
else:
|
||||
return nottest(x)
|
||||
|
||||
39
launch_openpilot.sh
Executable file
39
launch_openpilot.sh
Executable file
@@ -0,0 +1,39 @@
|
||||
#!/usr/bin/bash
|
||||
|
||||
function launch {
|
||||
# apply update
|
||||
if [ "$(git rev-parse HEAD)" != "$(git rev-parse @{u})" ]; then
|
||||
git reset --hard @{u} &&
|
||||
git clean -xdf &&
|
||||
exec "${BASH_SOURCE[0]}"
|
||||
fi
|
||||
|
||||
# no cpu rationing for now
|
||||
echo 0-3 > /dev/cpuset/background/cpus
|
||||
echo 0-3 > /dev/cpuset/system-background/cpus
|
||||
echo 0-3 > /dev/cpuset/foreground/boost/cpus
|
||||
echo 0-3 > /dev/cpuset/foreground/cpus
|
||||
echo 0-3 > /dev/cpuset/android/cpus
|
||||
|
||||
# wait for network
|
||||
(cd selfdrive/ui/spinner && exec ./spinner 'waiting for network...') & spin_pid=$!
|
||||
until ping -W 1 -c 1 8.8.8.8; do sleep 1; done
|
||||
kill $spin_pid
|
||||
|
||||
# check if NEOS update is required
|
||||
while [ "$(cat /VERSION)" -lt 4 ] && [ ! -e /data/media/0/noupdate ]; do
|
||||
curl -o /tmp/updater https://neos.comma.ai/updater && chmod +x /tmp/updater && /tmp/updater
|
||||
sleep 10
|
||||
done
|
||||
|
||||
export PYTHONPATH="$PWD"
|
||||
|
||||
# start manager
|
||||
cd selfdrive
|
||||
./manager.py
|
||||
|
||||
# if broken, keep on screen error
|
||||
while true; do sleep 1; done
|
||||
}
|
||||
|
||||
launch
|
||||
2
opendbc/.gitignore
vendored
Normal file
2
opendbc/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
*.pyc
|
||||
.*.swp
|
||||
53
opendbc/README.md
Normal file
53
opendbc/README.md
Normal file
@@ -0,0 +1,53 @@
|
||||
opendbc
|
||||
======
|
||||
|
||||
The project to democratize access to the decoder ring of your car.
|
||||
|
||||
|
||||
|
||||
### DBC file basics
|
||||
|
||||
A DBC file encodes, in a humanly readable way, the information needed to understand a vehicle's CAN bus traffic. A vehicle might have multiple CAN buses and every CAN bus is represented by its own dbc file.
|
||||
Wondering what's the DBC file format? [Here](http://www.socialledge.com/sjsu/index.php?title=DBC_Format) a good overview.
|
||||
|
||||
### How to start reverse engineering cars
|
||||
|
||||
[opendbc](https://github.com/commaai/opendbc) is integrated with [cabana](https://community.comma.ai/cabana/).
|
||||
|
||||
Use [panda](https://github.com/commaai/panda) to connect your car to a computer.
|
||||
|
||||
### DBC file preprocessor
|
||||
|
||||
DBC files for different models of the same brand have a lot of overlap. Therefore, we wrote a preprocessor to create DBC files from a brand DBC file and a model specific DBC file. The source DBC files can be found in the generator folder. After changing one of the files run the generator.py script to regenerate the output files. These output files will be placed in the root of the opendbc repository and are suffixed by _generated.
|
||||
|
||||
### Good practices for contributing to opendbc
|
||||
|
||||
- Comments: the best way to store comments is to add them directly to the DBC files. For example:
|
||||
```
|
||||
CM_ SG_ 490 LONG_ACCEL "wheel speed derivative, noisy and zero snapping";
|
||||
```
|
||||
is a comment that refers to signal `LONG_ACCEL` in message `490`. Using comments is highly recommended, especially for doubts and uncertainties. [cabana](https://community.comma.ai/cabana/) can easily display/add/edit comments to signals and messages.
|
||||
|
||||
- Units: when applicable, it's recommended to convert signals into physical units, by using a proper signal factor. Using a SI unit is preferred, unless a non-SI unit rounds the signal factor much better.
|
||||
For example:
|
||||
```
|
||||
SG_ VEHICLE_SPEED : 7|15@0+ (0.00278,0) [0|70] "m/s" PCM
|
||||
```
|
||||
is better than:
|
||||
```
|
||||
SG_ VEHICLE_SPEED : 7|15@0+ (0.00620,0) [0|115] "mph" PCM
|
||||
```
|
||||
However, the cleanest option is really:
|
||||
```
|
||||
SG_ VEHICLE_SPEED : 7|15@0+ (0.01,0) [0|250] "kph" PCM
|
||||
```
|
||||
|
||||
- Signal's size: always use the smallest amount of bits possible. For example, let's say I'm reverse engineering the gas pedal position and I've determined that it's in a 3 bytes message. For 0% pedal position I read a message value of `0x00 0x00 0x00`, while for 100% of pedal position I read `0x64 0x00 0x00`: clearly, the gas pedal position is within the first byte of the message and I might be tempted to define the signal `GAS_POS` as:
|
||||
```
|
||||
SG_ GAS_POS : 7|8@0+ (1,0) [0|100] "%" PCM
|
||||
```
|
||||
However, I can't be sure that the very first bit of the message is referred to the pedal position: I haven't seen it changing! Therefore, a safer way of defining the signal is:
|
||||
```
|
||||
SG_ GAS_POS : 6|7@0+ (1,0) [0|100] "%" PCM
|
||||
```
|
||||
which leaves the first bit unallocated. This prevents from very erroneous reading of the gas pedal position, in case the first bit is indeed used for something else.
|
||||
12
panda/.gitignore
vendored
Normal file
12
panda/.gitignore
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
*.pyc
|
||||
.*.swp
|
||||
.*.swo
|
||||
*.o
|
||||
a.out
|
||||
*~
|
||||
.#*
|
||||
dist/
|
||||
pandacan.egg-info/
|
||||
board/obj/
|
||||
examples/output.csv
|
||||
.DS_Store
|
||||
20
panda/.travis.yml
Normal file
20
panda/.travis.yml
Normal file
@@ -0,0 +1,20 @@
|
||||
language: python
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- build/commaai/panda/boardesp/esp-open-sdk/crosstool-NG
|
||||
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- gcc-arm-none-eabi
|
||||
- libnewlib-arm-none-eabi
|
||||
- gperf
|
||||
- texinfo
|
||||
- help2man
|
||||
|
||||
script:
|
||||
- python setup.py install
|
||||
- pushd board && make bin && popd
|
||||
- pushd boardesp && git clone --recursive https://github.com/pfalcon/esp-open-sdk.git && pushd esp-open-sdk && git checkout 03f5e898a059451ec5f3de30e7feff30455f7cec && LD_LIBRARY_PATH="" make STANDALONE=y && popd && popd
|
||||
- pushd boardesp && make user1.bin && popd
|
||||
7
panda/LICENSE
Normal file
7
panda/LICENSE
Normal file
@@ -0,0 +1,7 @@
|
||||
Copyright (c) 2016, Comma.ai, Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
89
panda/README.md
Normal file
89
panda/README.md
Normal file
@@ -0,0 +1,89 @@
|
||||
Welcome to panda
|
||||
======
|
||||
|
||||
[panda](http://github.com/commaai/panda) is the nicest universal car interface ever.
|
||||
|
||||
<a href="https://panda.comma.ai"><img src="https://github.com/commaai/panda/blob/master/panda.png">
|
||||
|
||||
<img src="https://github.com/commaai/panda/blob/master/buy.png"></a>
|
||||
|
||||
It supports 3x CAN, 2x LIN, and 1x GMLAN. It also charges a phone. On the computer side, it has both USB and Wi-Fi.
|
||||
|
||||
It uses an [STM32F413](http://www.st.com/en/microcontrollers/stm32f413-423.html?querycriteria=productId=LN2004) for low level stuff and an [ESP8266](https://en.wikipedia.org/wiki/ESP8266) for Wi-Fi. They are connected over high speed SPI, so the panda is actually capable of dumping the full contents of the busses over Wi-Fi, unlike every other dongle on amazon. ELM327 is weak, panda is strong.
|
||||
|
||||
It is 2nd gen hardware, reusing code and parts from the [NEO](https://github.com/commaai/neo) interface board.
|
||||
|
||||
[](https://travis-ci.org/commaai/panda)
|
||||
|
||||
Usage
|
||||
------
|
||||
|
||||
To install the library:
|
||||
```
|
||||
# pip install pandacan
|
||||
```
|
||||
|
||||
See [this class](https://github.com/commaai/panda/blob/master/python/__init__.py#L80) for how to interact with the panda.
|
||||
|
||||
For example, to receive CAN messages:
|
||||
```
|
||||
>>> from panda import Panda
|
||||
>>> panda = Panda()
|
||||
>>> panda.can_recv()
|
||||
```
|
||||
And to send one on bus 0:
|
||||
```
|
||||
>>> panda.can_send(0x1aa, "message", 0)
|
||||
```
|
||||
More examples coming soon
|
||||
|
||||
Software interface support
|
||||
------
|
||||
|
||||
As a universal car interface, it should support every reasonable software interface.
|
||||
|
||||
- User space ([done](https://github.com/commaai/panda/tree/master/python))
|
||||
- socketcan in kernel ([alpha](https://github.com/commaai/panda/tree/master/drivers/linux))
|
||||
- ELM327 ([done](https://github.com/commaai/panda/blob/master/boardesp/elm327.c))
|
||||
- Windows J2534 ([alpha](https://github.com/commaai/panda/tree/master/drivers/windows))
|
||||
|
||||
Directory structure
|
||||
------
|
||||
|
||||
- board -- Code that runs on the STM32
|
||||
- boardesp -- Code that runs on the ESP8266
|
||||
- drivers -- Drivers (not needed for use with python)
|
||||
- python -- Python userspace library for interfacing with the panda
|
||||
- tests -- Tests and helper programs for panda
|
||||
|
||||
Programming (over USB)
|
||||
------
|
||||
|
||||
[Programming the Board (STM32)](board/README.md)
|
||||
|
||||
[Programming the ESP](boardesp/README.md)
|
||||
|
||||
|
||||
Debugging
|
||||
------
|
||||
|
||||
To print out the serial console from the STM32, run tests/debug_console.py
|
||||
|
||||
To print out the serial console from the ESP8266, run PORT=1 tests/debug_console.py
|
||||
|
||||
Safety Model
|
||||
------
|
||||
|
||||
When a panda powers up, by default it's in "SAFETY_NOOUTPUT" mode. While in no output mode, the buses are also forced to be silent. In order to send messages, you have to select a safety mode. Currently, setting safety modes is only supported over USB.
|
||||
|
||||
Safety modes can also optionally support "controls_allowed", which allows or blocks a subset of messages based on a piece of state in the board.
|
||||
|
||||
Hardware
|
||||
------
|
||||
|
||||
Check out the hardware [guide](https://github.com/commaai/panda/blob/master/docs/guide.pdf)
|
||||
|
||||
Licensing
|
||||
------
|
||||
|
||||
panda software is released under the MIT license unless otherwise specified.
|
||||
31
panda/TODO
Normal file
31
panda/TODO
Normal file
@@ -0,0 +1,31 @@
|
||||
** Projects **
|
||||
|
||||
== ELM327 Emulator ==
|
||||
|
||||
Write an elm327 emulator in boardesp/elm327.c and make it work with Torque
|
||||
|
||||
You'll find a start at this in the "elm327" branch.
|
||||
|
||||
== socketcan Kernel Driver ==
|
||||
|
||||
Write a kernel driver version of lib/panda.py that exposes the Panda on socketcan and makes it work with those tools.
|
||||
|
||||
You may want to switch to interrupt endpoint first. Should LIN be exposed as a serial interface?
|
||||
|
||||
== Windows J2534 DLL ==
|
||||
|
||||
Write a Windows DLL that exposes the J2534 API.
|
||||
|
||||
Will make the Panda work with car diagnostic software.
|
||||
|
||||
|
||||
** Refactors **
|
||||
|
||||
== USB Interrupt Endpoint ==
|
||||
|
||||
Switch USB to use an interrupt endpoint instead of a bulk endpoint for can recv
|
||||
|
||||
== WebSocket Support ==
|
||||
|
||||
Add CAN streaming over WebSocket to the ELM code in addition to the UDP pipe.
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user