mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-09-09 01:23:43 +08:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 449b482cc3 | |||
| a7e099c946 | |||
| 610462be5a | |||
| 207d32668f |
@@ -0,0 +1,19 @@
|
|||||||
|
FROM ubuntu:14.04
|
||||||
|
ENV PYTHONUNBUFFERED 1
|
||||||
|
|
||||||
|
RUN apt-get update && apt-get install -y build-essential screen wget bzip2 git libglib2.0-0
|
||||||
|
|
||||||
|
RUN wget -nv -O /tmp/anaconda.sh https://repo.continuum.io/archive/Anaconda2-4.2.0-Linux-x86_64.sh && \
|
||||||
|
/bin/bash /tmp/anaconda.sh -b -p /opt/conda && \
|
||||||
|
rm /tmp/anaconda.sh
|
||||||
|
|
||||||
|
ENV PATH /opt/conda/bin:$PATH
|
||||||
|
|
||||||
|
RUN conda install numpy==1.11.2 && conda install scipy==0.18.1
|
||||||
|
|
||||||
|
COPY requirements_openpilot.txt /tmp/
|
||||||
|
RUN pip install -r /tmp/requirements_openpilot.txt
|
||||||
|
|
||||||
|
ENV PYTHONPATH /tmp/openpilot:$PYTHONPATH
|
||||||
|
|
||||||
|
COPY . /tmp/openpilot
|
||||||
@@ -42,26 +42,42 @@ Directory structure
|
|||||||
- assets -- Fonts for ui
|
- assets -- Fonts for ui
|
||||||
- boardd -- Daemon to talk to the board
|
- boardd -- Daemon to talk to the board
|
||||||
- calibrationd -- Camera calibration server
|
- calibrationd -- Camera calibration server
|
||||||
|
- car -- Code that talks to the car and implements CarInterface
|
||||||
- common -- Shared C/C++ code for the daemons
|
- common -- Shared C/C++ code for the daemons
|
||||||
- controls -- Python controls (PID loops etc) for the car
|
- controls -- Python controls (PID loops etc) for the car
|
||||||
- logcatd -- Android logcat as a service
|
- logcatd -- Android logcat as a service
|
||||||
- loggerd -- Logger and uploader of car data
|
- loggerd -- Logger and uploader of car data
|
||||||
|
- radar -- Code that talks to the radar and implements RadarInterface
|
||||||
- sensord -- IMU / GPS interface code
|
- sensord -- IMU / GPS interface code
|
||||||
|
- test/plant -- Car simulator running code through virtual maneuvers
|
||||||
- ui -- The UI
|
- ui -- The UI
|
||||||
- visiond -- embedded vision pipeline
|
- visiond -- embedded vision pipeline
|
||||||
|
|
||||||
To understand how the services interact, see `common/services.py`
|
To understand how the services interact, see `common/services.py`
|
||||||
|
|
||||||
|
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
|
Adding Car Support
|
||||||
------
|
------
|
||||||
|
|
||||||
It should be relatively easy to add support for the Honda CR-V Touring. The brake message is the same. Steering has a slightly different message with a different message id. Sniff CAN while using LKAS to find it.
|
It should be relatively easy to add support for the Honda CR-V Touring. The brake message is the same. Steering has a slightly different message with a different message id. Sniff CAN while using LKAS to find it.
|
||||||
|
|
||||||
The Honda Accord uses different signalling for the steering and probably requires new hardware.
|
The Honda Accord uses different signaling for the steering and probably requires new hardware.
|
||||||
|
|
||||||
Adding other manufacturers besides Honda/Acura is doable but will be more of an undertaking.
|
Adding other manufacturers besides Honda/Acura is doable but will be more of an undertaking.
|
||||||
|
|
||||||
|
|
||||||
User Data / chffr Account / Crash Reporting
|
User Data / chffr Account / Crash Reporting
|
||||||
------
|
------
|
||||||
|
|
||||||
@@ -81,7 +97,7 @@ We welcome both pull requests and issues on
|
|||||||
[github](http://github.com/commaai/openpilot). See the TODO file for a list of
|
[github](http://github.com/commaai/openpilot). See the TODO file for a list of
|
||||||
good places to start.
|
good places to start.
|
||||||
|
|
||||||
|
Want to get paid to work on openpilot? [comma.ai is hiring](http://comma.ai/hiring.html)
|
||||||
|
|
||||||
Licensing
|
Licensing
|
||||||
------
|
------
|
||||||
|
|||||||
+19
@@ -0,0 +1,19 @@
|
|||||||
|
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
|
||||||
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
TODO
|
|
||||||
======
|
|
||||||
|
|
||||||
An incomplete list of known issues and desired featues.
|
|
||||||
|
|
||||||
- TX and RX amounts on UI are wrong for a few frames at startup because we
|
|
||||||
subtract (total sent - 0). We should initialize sent bytes before displaying.
|
|
||||||
|
|
||||||
- Rewrite common/dbc.py to be faster and cleaner, potentially in C++.
|
|
||||||
|
|
||||||
- Add module and class level documentation where appropriate.
|
|
||||||
|
|
||||||
- Fix lock file cleanup so there isn't always 1 pending upload when the vehicle
|
|
||||||
shuts off.
|
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
//#define CAN_LOOPBACK_MODE
|
//#define CAN_LOOPBACK_MODE
|
||||||
//#define USE_INTERNAL_OSC
|
//#define USE_INTERNAL_OSC
|
||||||
//#define OLD_BOARD
|
//#define OLD_BOARD
|
||||||
|
//#define ENABLE_CURRENT_SENSOR
|
||||||
|
|
||||||
#define USB_VID 0xbbaa
|
#define USB_VID 0xbbaa
|
||||||
#define USB_PID 0xddcc
|
#define USB_PID 0xddcc
|
||||||
@@ -257,7 +258,11 @@ int get_health_pkt(void *dat) {
|
|||||||
uint8_t gas_interceptor_detected;
|
uint8_t gas_interceptor_detected;
|
||||||
} *health = dat;
|
} *health = dat;
|
||||||
health->voltage = adc_get(ADCCHAN_VOLTAGE);
|
health->voltage = adc_get(ADCCHAN_VOLTAGE);
|
||||||
|
#ifdef ENABLE_CURRENT_SENSOR
|
||||||
health->current = adc_get(ADCCHAN_CURRENT);
|
health->current = adc_get(ADCCHAN_CURRENT);
|
||||||
|
#else
|
||||||
|
health->current = 0;
|
||||||
|
#endif
|
||||||
health->started = (GPIOC->IDR & (1 << 13)) != 0;
|
health->started = (GPIOC->IDR & (1 << 13)) != 0;
|
||||||
health->controls_allowed = controls_allowed;
|
health->controls_allowed = controls_allowed;
|
||||||
health->gas_interceptor_detected = gas_interceptor_detected;
|
health->gas_interceptor_detected = gas_interceptor_detected;
|
||||||
|
|||||||
@@ -4,4 +4,5 @@ capnp.remove_import_hook()
|
|||||||
|
|
||||||
CEREAL_PATH = os.path.dirname(os.path.abspath(__file__))
|
CEREAL_PATH = os.path.dirname(os.path.abspath(__file__))
|
||||||
log = capnp.load(os.path.join(CEREAL_PATH, "log.capnp"))
|
log = capnp.load(os.path.join(CEREAL_PATH, "log.capnp"))
|
||||||
|
car = capnp.load(os.path.join(CEREAL_PATH, "car.capnp"))
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,172 @@
|
|||||||
|
using Cxx = import "c++.capnp";
|
||||||
|
$Cxx.namespace("cereal");
|
||||||
|
|
||||||
|
@0x8e2af1e708af8b8d;
|
||||||
|
|
||||||
|
# ******* main car state @ 100hz *******
|
||||||
|
# all speeds in m/s
|
||||||
|
|
||||||
|
struct CarState {
|
||||||
|
errors @0: List(Error);
|
||||||
|
|
||||||
|
# car speed
|
||||||
|
vEgo @1 :Float32; # best estimate of speed
|
||||||
|
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
|
||||||
|
|
||||||
|
# steering wheel
|
||||||
|
steeringAngle @7 :Float32; # deg
|
||||||
|
steeringTorque @8 :Float32; # TODO: standardize units
|
||||||
|
steeringPressed @9 :Bool; # if the user is using the steering wheel
|
||||||
|
|
||||||
|
# cruise state
|
||||||
|
cruiseState @10 :CruiseState;
|
||||||
|
|
||||||
|
# button presses
|
||||||
|
buttonEvents @11 :List(ButtonEvent);
|
||||||
|
|
||||||
|
# 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Error {
|
||||||
|
# TODO: copy from error list
|
||||||
|
commIssue @0;
|
||||||
|
steerUnavailable @1;
|
||||||
|
brakeUnavailable @2;
|
||||||
|
gasUnavailable @3;
|
||||||
|
wrongGear @4;
|
||||||
|
doorOpen @5;
|
||||||
|
seatbeltNotLatched @6;
|
||||||
|
espDisabled @7;
|
||||||
|
wrongCarMode @8;
|
||||||
|
steerTemporarilyUnavailable @9;
|
||||||
|
reverseGear @10;
|
||||||
|
# ...
|
||||||
|
}
|
||||||
|
|
||||||
|
# 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 {
|
||||||
|
notValid @0;
|
||||||
|
}
|
||||||
|
|
||||||
|
# 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# ******* car controls @ 100hz *******
|
||||||
|
|
||||||
|
struct CarControl {
|
||||||
|
# must be true for any actuator commands to work
|
||||||
|
enabled @0: Bool;
|
||||||
|
|
||||||
|
# range from 0.0 - 1.0
|
||||||
|
gas @1: Float32;
|
||||||
|
brake @2: Float32;
|
||||||
|
|
||||||
|
# range from -1.0 - 1.0
|
||||||
|
steeringTorque @3 :Float32;
|
||||||
|
|
||||||
|
cruiseControl @4: CruiseControl;
|
||||||
|
hudControl @5: HUDControl;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
+7
-7
@@ -85,11 +85,11 @@ struct Live20Data {
|
|||||||
ftMonoTime @7 :UInt64;
|
ftMonoTime @7 :UInt64;
|
||||||
|
|
||||||
# all deprecated
|
# all deprecated
|
||||||
warpMatrix @0 :List(Float32);
|
warpMatrixDEPRECATED @0 :List(Float32);
|
||||||
angleOffset @1 :Float32;
|
angleOffsetDEPRECATED @1 :Float32;
|
||||||
calStatus @2 :Int8;
|
calStatusDEPRECATED @2 :Int8;
|
||||||
calCycle @8 :Int32;
|
calCycleDEPRECATED @8 :Int32;
|
||||||
calPerc @9 :Int8;
|
calPercDEPRECATED @9 :Int8;
|
||||||
|
|
||||||
leadOne @3 :LeadData;
|
leadOne @3 :LeadData;
|
||||||
leadTwo @4 :LeadData;
|
leadTwo @4 :LeadData;
|
||||||
@@ -138,7 +138,7 @@ struct Live100Data {
|
|||||||
mdMonoTime @18 :UInt64;
|
mdMonoTime @18 :UInt64;
|
||||||
|
|
||||||
vEgo @0 :Float32;
|
vEgo @0 :Float32;
|
||||||
aEgo @1 :Float32;
|
aEgoDEPRECATED @1 :Float32;
|
||||||
vPid @2 :Float32;
|
vPid @2 :Float32;
|
||||||
vTargetLead @3 :Float32;
|
vTargetLead @3 :Float32;
|
||||||
upAccelCmd @4 :Float32;
|
upAccelCmd @4 :Float32;
|
||||||
@@ -151,7 +151,7 @@ struct Live100Data {
|
|||||||
aTargetMax @11 :Float32;
|
aTargetMax @11 :Float32;
|
||||||
jerkFactor @12 :Float32;
|
jerkFactor @12 :Float32;
|
||||||
angleSteers @13 :Float32;
|
angleSteers @13 :Float32;
|
||||||
hudLead @14 :Int32;
|
hudLeadDEPRECATED @14 :Int32;
|
||||||
cumLagMs @15 :Float32;
|
cumLagMs @15 :Float32;
|
||||||
|
|
||||||
enabled @19: Bool;
|
enabled @19: Bool;
|
||||||
|
|||||||
@@ -3,6 +3,6 @@ import requests
|
|||||||
def api_get(endpoint, method='GET', timeout=None, **params):
|
def api_get(endpoint, method='GET', timeout=None, **params):
|
||||||
backend = "https://api.commadotai.com/"
|
backend = "https://api.commadotai.com/"
|
||||||
|
|
||||||
params['_version'] = "OPENPILOTv0.0"
|
params['_version'] = "OPENPILOTv0.2"
|
||||||
|
|
||||||
return requests.request(method, backend+endpoint, timeout=timeout, params=params)
|
return requests.request(method, backend+endpoint, timeout=timeout, params=params)
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import sys
|
|||||||
if os.getenv("NOLOG"):
|
if os.getenv("NOLOG"):
|
||||||
def capture_exception(*exc_info):
|
def capture_exception(*exc_info):
|
||||||
pass
|
pass
|
||||||
|
def bind_user(**kwargs):
|
||||||
|
pass
|
||||||
def install():
|
def install():
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
@@ -16,6 +18,9 @@ else:
|
|||||||
|
|
||||||
capture_exception = client.captureException
|
capture_exception = client.captureException
|
||||||
|
|
||||||
|
def bind_user(**kwargs):
|
||||||
|
client.user_context(kwargs)
|
||||||
|
|
||||||
def install():
|
def install():
|
||||||
# installs a sys.excepthook
|
# installs a sys.excepthook
|
||||||
__excepthook__ = sys.excepthook
|
__excepthook__ = sys.excepthook
|
||||||
|
|||||||
+1
-1
@@ -57,7 +57,7 @@ def set_realtime_priority(level):
|
|||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
tid = libc.syscall(NR_gettid)
|
tid = libc.syscall(NR_gettid)
|
||||||
subprocess.check_call(['chrt', '-f', '-p', str(level), str(tid)])
|
return subprocess.call(['chrt', '-f', '-p', str(level), str(tid)])
|
||||||
|
|
||||||
|
|
||||||
class Ratekeeper(object):
|
class Ratekeeper(object):
|
||||||
|
|||||||
+8
-3
@@ -39,14 +39,21 @@ service_list = {
|
|||||||
# subscribes: health
|
# subscribes: health
|
||||||
# publishes: thermal
|
# publishes: thermal
|
||||||
|
|
||||||
|
# **** processes that communicate with the outside world ****
|
||||||
|
|
||||||
# boardd -- communicates with the car
|
# boardd -- communicates with the car
|
||||||
# subscribes: sendcan
|
# subscribes: sendcan
|
||||||
# publishes: can, health
|
# publishes: can, health
|
||||||
|
|
||||||
|
# sensord -- publishes the IMU and GPS
|
||||||
|
# publishes: sensorEvents, gpsNMEA
|
||||||
|
|
||||||
# visiond -- talks to the cameras, runs the model, saves the videos
|
# visiond -- talks to the cameras, runs the model, saves the videos
|
||||||
# subscribes: liveCalibration, sensorEvents
|
# subscribes: liveCalibration, sensorEvents
|
||||||
# publishes: frame, encodeIdx, model, features
|
# publishes: frame, encodeIdx, model, features
|
||||||
|
|
||||||
|
# **** stateful data transformers ****
|
||||||
|
|
||||||
# controlsd -- actually drives the car
|
# controlsd -- actually drives the car
|
||||||
# subscribes: can, thermal, model, live20
|
# subscribes: can, thermal, model, live20
|
||||||
# publishes: sendcan, live100
|
# publishes: sendcan, live100
|
||||||
@@ -55,9 +62,6 @@ service_list = {
|
|||||||
# subscribes: can, live100, model
|
# subscribes: can, live100, model
|
||||||
# publishes: live20, liveTracks
|
# publishes: live20, liveTracks
|
||||||
|
|
||||||
# sensord -- publishes the IMU and GPS
|
|
||||||
# publishes: sensorEvents, gpsNMEA
|
|
||||||
|
|
||||||
# calibrationd -- places the camera box
|
# calibrationd -- places the camera box
|
||||||
# subscribes: features, live100
|
# subscribes: features, live100
|
||||||
# publishes: liveCalibration
|
# publishes: liveCalibration
|
||||||
@@ -80,3 +84,4 @@ service_list = {
|
|||||||
|
|
||||||
# logcatd -- fetches logcat info from android
|
# logcatd -- fetches logcat info from android
|
||||||
# publishes: androidLog
|
# publishes: androidLog
|
||||||
|
|
||||||
|
|||||||
@@ -7,16 +7,17 @@
|
|||||||
export GIT_SSH_COMMAND="ssh -i /data/data/com.termux/files/id_rsa_openpilot_ro"
|
export GIT_SSH_COMMAND="ssh -i /data/data/com.termux/files/id_rsa_openpilot_ro"
|
||||||
|
|
||||||
# check out the openpilot repo
|
# check out the openpilot repo
|
||||||
# TODO: release branch only
|
|
||||||
if [ ! -d /data/openpilot ]; then
|
if [ ! -d /data/openpilot ]; then
|
||||||
cd /tmp
|
cd /tmp
|
||||||
git clone git@github.com:commaai/openpilot.git
|
git clone git@github.com:commaai/openpilot.git -b release
|
||||||
mv /tmp/openpilot /data/openpilot
|
mv /tmp/openpilot /data/openpilot
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# update the openpilot repo
|
# enter openpilot directory
|
||||||
cd /data/openpilot
|
cd /data/openpilot
|
||||||
git pull
|
|
||||||
|
# removed automatic update from openpilot
|
||||||
|
#git pull
|
||||||
|
|
||||||
# start manager
|
# start manager
|
||||||
cd selfdrive
|
cd selfdrive
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
Cython==0.24.1
|
||||||
|
bitstring==3.1.5
|
||||||
|
fastcluster==1.1.21
|
||||||
|
h5py==2.6.0
|
||||||
|
libusb1==1.5.0
|
||||||
|
pycapnp==0.5.9
|
||||||
|
pyzmq==15.4.0
|
||||||
|
raven==5.23.0
|
||||||
|
requests==2.10.0
|
||||||
|
setproctitle==1.1.10
|
||||||
|
simplejson==3.8.2
|
||||||
|
-e git+https://github.com/commaai/le_python.git#egg=Logentries
|
||||||
Executable
+7
@@ -0,0 +1,7 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
docker build -t tmppilot -f Dockerfile.openpilot .
|
||||||
|
docker run --rm \
|
||||||
|
-v "$(pwd)"/selfdrive/test/plant/out:/tmp/openpilot/selfdrive/test/plant/out \
|
||||||
|
tmppilot /bin/sh -c 'cd /tmp/openpilot/selfdrive/test/plant && ./runtest.sh'
|
||||||
@@ -27,6 +27,8 @@ libusb_context *ctx = NULL;
|
|||||||
libusb_device_handle *dev_handle;
|
libusb_device_handle *dev_handle;
|
||||||
pthread_mutex_t usb_lock;
|
pthread_mutex_t usb_lock;
|
||||||
|
|
||||||
|
bool spoofing_started = false;
|
||||||
|
|
||||||
// double the FIFO size
|
// double the FIFO size
|
||||||
#define RECV_SIZE (0x1000)
|
#define RECV_SIZE (0x1000)
|
||||||
#define TIMEOUT 0
|
#define TIMEOUT 0
|
||||||
@@ -147,7 +149,11 @@ void can_health(void *s) {
|
|||||||
// set fields
|
// set fields
|
||||||
healthData.setVoltage(health.voltage);
|
healthData.setVoltage(health.voltage);
|
||||||
healthData.setCurrent(health.current);
|
healthData.setCurrent(health.current);
|
||||||
healthData.setStarted(health.started);
|
if (spoofing_started) {
|
||||||
|
healthData.setStarted(1);
|
||||||
|
} else {
|
||||||
|
healthData.setStarted(health.started);
|
||||||
|
}
|
||||||
healthData.setControlsAllowed(health.controls_allowed);
|
healthData.setControlsAllowed(health.controls_allowed);
|
||||||
healthData.setGasInterceptorDetected(health.gas_interceptor_detected);
|
healthData.setGasInterceptorDetected(health.gas_interceptor_detected);
|
||||||
|
|
||||||
@@ -177,7 +183,7 @@ void can_send(void *s) {
|
|||||||
memset(send, 0, msg_count*0x10);
|
memset(send, 0, msg_count*0x10);
|
||||||
|
|
||||||
for (int i = 0; i < msg_count; i++) {
|
for (int i = 0; i < msg_count; i++) {
|
||||||
auto cmsg = event.getCan()[i];
|
auto cmsg = event.getSendcan()[i];
|
||||||
if (cmsg.getAddress() >= 0x800) {
|
if (cmsg.getAddress() >= 0x800) {
|
||||||
// extended
|
// extended
|
||||||
send[i*4] = (cmsg.getAddress() << 3) | 5;
|
send[i*4] = (cmsg.getAddress() << 3) | 5;
|
||||||
@@ -270,6 +276,11 @@ int main() {
|
|||||||
err = setpriority(PRIO_PROCESS, 0, -4);
|
err = setpriority(PRIO_PROCESS, 0, -4);
|
||||||
printf("boardd: setpriority returns %d\n", err);
|
printf("boardd: setpriority returns %d\n", err);
|
||||||
|
|
||||||
|
// check the environment
|
||||||
|
if (getenv("STARTED")) {
|
||||||
|
spoofing_started = true;
|
||||||
|
}
|
||||||
|
|
||||||
// connect to the board
|
// connect to the board
|
||||||
err = libusb_init(&ctx);
|
err = libusb_init(&ctx);
|
||||||
assert(err == 0);
|
assert(err == 0);
|
||||||
|
|||||||
@@ -18,19 +18,23 @@ except Exception:
|
|||||||
# TODO: rewrite in C to save CPU
|
# TODO: rewrite in C to save CPU
|
||||||
|
|
||||||
# *** serialization functions ***
|
# *** serialization functions ***
|
||||||
def can_list_to_can_capnp(can_msgs):
|
def can_list_to_can_capnp(can_msgs, msgtype='can'):
|
||||||
dat = messaging.new_message()
|
dat = messaging.new_message()
|
||||||
dat.init('can', len(can_msgs))
|
dat.init(msgtype, len(can_msgs))
|
||||||
for i, can_msg in enumerate(can_msgs):
|
for i, can_msg in enumerate(can_msgs):
|
||||||
dat.can[i].address = can_msg[0]
|
if msgtype == 'sendcan':
|
||||||
dat.can[i].busTime = can_msg[1]
|
cc = dat.sendcan[i]
|
||||||
dat.can[i].dat = can_msg[2]
|
else:
|
||||||
dat.can[i].src = can_msg[3]
|
cc = dat.can[i]
|
||||||
|
cc.address = can_msg[0]
|
||||||
|
cc.busTime = can_msg[1]
|
||||||
|
cc.dat = can_msg[2]
|
||||||
|
cc.src = can_msg[3]
|
||||||
return dat
|
return dat
|
||||||
|
|
||||||
def can_capnp_to_can_list_old(dat, src_filter=[]):
|
def can_capnp_to_can_list_old(dat, src_filter=[]):
|
||||||
ret = []
|
ret = []
|
||||||
for msg in dat.can:
|
for msg in dat:
|
||||||
if msg.src in src_filter:
|
if msg.src in src_filter:
|
||||||
ret.append([msg.address, msg.busTime, msg.dat.encode("hex")])
|
ret.append([msg.address, msg.busTime, msg.dat.encode("hex")])
|
||||||
return ret
|
return ret
|
||||||
|
|||||||
@@ -1,8 +1,12 @@
|
|||||||
fingerprints = {
|
fingerprints = {
|
||||||
"ACURA ILX 2016 ACURAWATCH PLUS": {
|
"ACURA ILX 2016 ACURAWATCH PLUS": {
|
||||||
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
|
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: 4,
|
||||||
},
|
},
|
||||||
"HONDA CIVIC 2016 TOURING": {
|
"HONDA CIVIC 2016 TOURING": {
|
||||||
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
|
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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,7 +2,7 @@ import os
|
|||||||
import dbcs
|
import dbcs
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
|
|
||||||
from selfdrive.controls.lib.hondacan import fix
|
from selfdrive.car.honda.hondacan import fix
|
||||||
from common.realtime import sec_since_boot
|
from common.realtime import sec_since_boot
|
||||||
from common.dbc import dbc
|
from common.dbc import dbc
|
||||||
|
|
||||||
@@ -112,7 +112,7 @@ class CANParser(object):
|
|||||||
# assess overall can validity: if there is one relevant message invalid, then set can validity flag to False
|
# assess overall can validity: if there is one relevant message invalid, then set can validity flag to False
|
||||||
self.can_valid = True
|
self.can_valid = True
|
||||||
if False in self.ok.values():
|
if False in self.ok.values():
|
||||||
print "CAN INVALID!"
|
#print "CAN INVALID!"
|
||||||
self.can_valid = False
|
self.can_valid = False
|
||||||
|
|
||||||
def _check_dead_msgs(self):
|
def _check_dead_msgs(self):
|
||||||
@@ -1,25 +1,54 @@
|
|||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
|
|
||||||
import common.numpy_fast as np
|
import common.numpy_fast as np
|
||||||
import selfdrive.controls.lib.hondacan as hondacan
|
|
||||||
from common.realtime import sec_since_boot
|
from common.realtime import sec_since_boot
|
||||||
from selfdrive.config import CruiseButtons
|
from selfdrive.config import CruiseButtons
|
||||||
from selfdrive.boardd.boardd import can_list_to_can_capnp
|
from selfdrive.boardd.boardd import can_list_to_can_capnp
|
||||||
from selfdrive.controls.lib.alert_database import process_hud_alert
|
|
||||||
from selfdrive.controls.lib.drive_helpers import actuator_hystereses, rate_limit
|
from selfdrive.controls.lib.drive_helpers import actuator_hystereses, rate_limit
|
||||||
|
|
||||||
|
class AH:
|
||||||
|
#[alert_idx, value]
|
||||||
|
# See dbc files for info on values"
|
||||||
|
NONE = [0, 0]
|
||||||
|
FCW = [1, 0x8]
|
||||||
|
STEER = [2, 1]
|
||||||
|
BRAKE_PRESSED = [3, 10]
|
||||||
|
GEAR_NOT_D = [4, 6]
|
||||||
|
SEATBELT = [5, 5]
|
||||||
|
SPEED_TOO_HIGH = [6, 8]
|
||||||
|
|
||||||
|
|
||||||
|
def process_hud_alert(hud_alert):
|
||||||
|
# initialize to no alert
|
||||||
|
fcw_display = 0
|
||||||
|
steer_required = 0
|
||||||
|
acc_alert = 0
|
||||||
|
if hud_alert == AH.NONE: # no alert
|
||||||
|
pass
|
||||||
|
elif hud_alert == AH.FCW: # FCW
|
||||||
|
fcw_display = hud_alert[1]
|
||||||
|
elif hud_alert == AH.STEER: # STEER
|
||||||
|
steer_required = hud_alert[1]
|
||||||
|
else: # any other ACC alert
|
||||||
|
acc_alert = hud_alert[1]
|
||||||
|
|
||||||
|
return fcw_display, steer_required, acc_alert
|
||||||
|
|
||||||
|
import selfdrive.car.honda.hondacan as hondacan
|
||||||
|
|
||||||
HUDData = namedtuple("HUDData",
|
HUDData = namedtuple("HUDData",
|
||||||
["pcm_accel", "v_cruise", "X2", "car", "X4", "X5",
|
["pcm_accel", "v_cruise", "X2", "car", "X4", "X5",
|
||||||
"lanes", "beep", "X8", "chime", "acc_alert"])
|
"lanes", "beep", "X8", "chime", "acc_alert"])
|
||||||
|
|
||||||
class CarController(object):
|
class CarController(object):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.controls_allowed = False
|
|
||||||
self.mismatch_start, self.pcm_mismatch_start = 0, 0
|
|
||||||
self.braking = False
|
self.braking = False
|
||||||
self.brake_steady = 0.
|
self.brake_steady = 0.
|
||||||
self.final_brake_last = 0.
|
self.final_brake_last = 0.
|
||||||
|
|
||||||
|
# redundant safety check with the board
|
||||||
|
self.controls_allowed = False
|
||||||
|
|
||||||
def update(self, sendcan, enabled, CS, frame, final_gas, final_brake, final_steer, \
|
def update(self, sendcan, enabled, CS, frame, final_gas, final_brake, final_steer, \
|
||||||
pcm_speed, pcm_override, pcm_cancel_cmd, pcm_accel, \
|
pcm_speed, pcm_override, pcm_cancel_cmd, pcm_accel, \
|
||||||
hud_v_cruise, hud_show_lanes, hud_show_car, hud_alert, \
|
hud_v_cruise, hud_show_lanes, hud_show_car, hud_alert, \
|
||||||
@@ -61,9 +90,9 @@ class CarController(object):
|
|||||||
#print chime, alert_id, hud_alert
|
#print chime, alert_id, hud_alert
|
||||||
fcw_display, steer_required, acc_alert = process_hud_alert(hud_alert)
|
fcw_display, steer_required, acc_alert = process_hud_alert(hud_alert)
|
||||||
|
|
||||||
hud = HUDData(pcm_accel, hud_v_cruise, 0x41, hud_car,
|
hud = HUDData(int(pcm_accel), int(hud_v_cruise), 0x41, hud_car,
|
||||||
0xc1, 0x41, hud_lanes + steer_required,
|
0xc1, 0x41, hud_lanes + steer_required,
|
||||||
snd_beep, 0x48, (snd_chime << 5) + fcw_display, acc_alert)
|
int(snd_beep), 0x48, (snd_chime << 5) + fcw_display, acc_alert)
|
||||||
|
|
||||||
if not all(isinstance(x, int) and 0 <= x < 256 for x in hud):
|
if not all(isinstance(x, int) and 0 <= x < 256 for x in hud):
|
||||||
print "INVALID HUD", hud
|
print "INVALID HUD", hud
|
||||||
@@ -71,14 +100,11 @@ class CarController(object):
|
|||||||
|
|
||||||
# **** process the car messages ****
|
# **** process the car messages ****
|
||||||
|
|
||||||
user_brake_ctrl = CS.user_brake/0.015625 # FIXME: factor needed to convert to old scale
|
|
||||||
|
|
||||||
# *** compute control surfaces ***
|
# *** compute control surfaces ***
|
||||||
tt = sec_since_boot()
|
tt = sec_since_boot()
|
||||||
GAS_MAX = 1004
|
GAS_MAX = 1004
|
||||||
BRAKE_MAX = 1024/4
|
BRAKE_MAX = 1024/4
|
||||||
#STEER_MAX = 0xF00 if not CS.torque_mod else 0xF00/4 # ilx has 8x steering torque limit, used as a 2x
|
STEER_MAX = 0xF00
|
||||||
STEER_MAX = 0xF00 # ilx has 8x steering torque limit, used as a 2x
|
|
||||||
GAS_OFFSET = 328
|
GAS_OFFSET = 328
|
||||||
|
|
||||||
# steer torque is converted back to CAN reference (positive when steering right)
|
# steer torque is converted back to CAN reference (positive when steering right)
|
||||||
@@ -87,51 +113,37 @@ class CarController(object):
|
|||||||
apply_steer = int(np.clip(-final_steer*STEER_MAX, -STEER_MAX, STEER_MAX))
|
apply_steer = int(np.clip(-final_steer*STEER_MAX, -STEER_MAX, STEER_MAX))
|
||||||
|
|
||||||
# no gas if you are hitting the brake or the user is
|
# no gas if you are hitting the brake or the user is
|
||||||
if apply_gas > 0 and (apply_brake != 0 or user_brake_ctrl > 10):
|
if apply_gas > 0 and (apply_brake != 0 or CS.brake_pressed):
|
||||||
print "CANCELLING GAS", apply_brake, user_brake_ctrl
|
print "CANCELLING GAS", apply_brake
|
||||||
apply_gas = 0
|
apply_gas = 0
|
||||||
|
|
||||||
# no computer brake if the user is hitting the gas
|
# no computer brake if the gas is being pressed
|
||||||
# if the computer is trying to brake, it can't be hitting the gas
|
|
||||||
# TODO: car_gas can override brakes without canceling... this is bad
|
|
||||||
if CS.car_gas > 0 and apply_brake != 0:
|
if CS.car_gas > 0 and apply_brake != 0:
|
||||||
|
print "CANCELLING BRAKE"
|
||||||
apply_brake = 0
|
apply_brake = 0
|
||||||
|
|
||||||
|
# any other cp.vl[0x18F]['STEER_STATUS'] is common and can happen during user override. sending 0 torque to avoid EPS sending error 5
|
||||||
|
if CS.steer_not_allowed:
|
||||||
|
print "STEER ALERT, TORQUE INHIBITED"
|
||||||
|
apply_steer = 0
|
||||||
|
|
||||||
|
# *** entry into controls state ***
|
||||||
if (CS.prev_cruise_buttons == CruiseButtons.DECEL_SET or CS.prev_cruise_buttons == CruiseButtons.RES_ACCEL) and \
|
if (CS.prev_cruise_buttons == CruiseButtons.DECEL_SET or CS.prev_cruise_buttons == CruiseButtons.RES_ACCEL) and \
|
||||||
CS.cruise_buttons == 0 and not self.controls_allowed:
|
CS.cruise_buttons == 0 and not self.controls_allowed:
|
||||||
print "CONTROLS ARE LIVE"
|
print "CONTROLS ARE LIVE"
|
||||||
self.controls_allowed = True
|
self.controls_allowed = True
|
||||||
|
|
||||||
# to avoid race conditions, check if control has been disabled for at least 0.2s
|
# *** exit from controls state on cancel, gas, or brake ***
|
||||||
# keep resetting start timer if mismatch isn't true
|
|
||||||
if not (self.controls_allowed and not enabled):
|
|
||||||
self.mismatch_start = tt
|
|
||||||
|
|
||||||
# to avoid race conditions, check if control is disabled but pcm control is on for at least 0.2s
|
|
||||||
if not (not self.controls_allowed and CS.pcm_acc_status):
|
|
||||||
self.pcm_mismatch_start = tt
|
|
||||||
|
|
||||||
# something is very wrong, since pcm control is active but controls should not be allowed; TODO: send pcm fault cmd?
|
|
||||||
if (tt - self.pcm_mismatch_start) > 0.2:
|
|
||||||
pcm_cancel_cmd = True
|
|
||||||
|
|
||||||
# TODO: clean up gear condition, ideally only D (and P for debug) shall be valid gears
|
|
||||||
if (CS.cruise_buttons == CruiseButtons.CANCEL or CS.brake_pressed or
|
if (CS.cruise_buttons == CruiseButtons.CANCEL or CS.brake_pressed or
|
||||||
CS.user_gas_pressed or (tt - self.mismatch_start) > 0.2 or
|
CS.user_gas_pressed or (CS.pedal_gas > 0 and CS.brake_only)) and self.controls_allowed:
|
||||||
not CS.main_on or not CS.gear_shifter_valid or
|
print "CONTROLS ARE DEAD"
|
||||||
(CS.pedal_gas > 0 and CS.brake_only)) and self.controls_allowed:
|
|
||||||
self.controls_allowed = False
|
self.controls_allowed = False
|
||||||
|
|
||||||
# 5 is a permanent fault, no torque request will be fullfilled
|
# *** controls fail on steer error, brake error, or invalid can ***
|
||||||
if CS.steer_error:
|
if CS.steer_error:
|
||||||
print "STEER ERROR"
|
print "STEER ERROR"
|
||||||
self.controls_allowed = False
|
self.controls_allowed = False
|
||||||
|
|
||||||
# any other cp.vl[0x18F]['STEER_STATUS'] is common and can happen during user override. sending 0 torque to avoid EPS sending error 5
|
|
||||||
elif CS.steer_not_allowed:
|
|
||||||
print "STEER ALERT, TORQUE INHIBITED"
|
|
||||||
apply_steer = 0
|
|
||||||
|
|
||||||
if CS.brake_error:
|
if CS.brake_error:
|
||||||
print "BRAKE ERROR"
|
print "BRAKE ERROR"
|
||||||
self.controls_allowed = False
|
self.controls_allowed = False
|
||||||
@@ -140,13 +152,6 @@ class CarController(object):
|
|||||||
print "CAN INVALID"
|
print "CAN INVALID"
|
||||||
self.controls_allowed = False
|
self.controls_allowed = False
|
||||||
|
|
||||||
if not self.controls_allowed:
|
|
||||||
apply_steer = 0
|
|
||||||
apply_gas = 0
|
|
||||||
apply_brake = 0
|
|
||||||
pcm_speed = 0 # make sure you send 0 target speed to pcm
|
|
||||||
#pcm_cancel_cmd = 1 # prevent pcm control from turning on. FIXME: we can't just do this
|
|
||||||
|
|
||||||
# Send CAN commands.
|
# Send CAN commands.
|
||||||
can_sends = []
|
can_sends = []
|
||||||
|
|
||||||
@@ -160,7 +165,6 @@ class CarController(object):
|
|||||||
can_sends.append(
|
can_sends.append(
|
||||||
hondacan.create_brake_command(apply_brake, pcm_override,
|
hondacan.create_brake_command(apply_brake, pcm_override,
|
||||||
pcm_cancel_cmd, hud.chime, idx))
|
pcm_cancel_cmd, hud.chime, idx))
|
||||||
|
|
||||||
if not CS.brake_only:
|
if not CS.brake_only:
|
||||||
# send exactly zero if apply_gas is zero. Interceptor will send the max between read value and apply_gas.
|
# send exactly zero if apply_gas is zero. Interceptor will send the max between read value and apply_gas.
|
||||||
# This prevents unexpected pedal range rescaling
|
# This prevents unexpected pedal range rescaling
|
||||||
@@ -182,4 +186,5 @@ class CarController(object):
|
|||||||
idx = (frame/radar_send_step) % 4
|
idx = (frame/radar_send_step) % 4
|
||||||
can_sends.extend(hondacan.create_radar_commands(CS.v_ego, CS.civic, idx))
|
can_sends.extend(hondacan.create_radar_commands(CS.v_ego, CS.civic, idx))
|
||||||
|
|
||||||
sendcan.send(can_list_to_can_capnp(can_sends).to_bytes())
|
sendcan.send(can_list_to_can_capnp(can_sends, msgtype='sendcan').to_bytes())
|
||||||
|
|
||||||
@@ -2,11 +2,11 @@ import numpy as np
|
|||||||
|
|
||||||
import selfdrive.messaging as messaging
|
import selfdrive.messaging as messaging
|
||||||
from selfdrive.boardd.boardd import can_capnp_to_can_list_old, can_capnp_to_can_list
|
from selfdrive.boardd.boardd import can_capnp_to_can_list_old, can_capnp_to_can_list
|
||||||
from selfdrive.controls.lib.can_parser import CANParser
|
|
||||||
from selfdrive.controls.lib.fingerprints import fingerprints
|
|
||||||
from selfdrive.config import VehicleParams
|
from selfdrive.config import VehicleParams
|
||||||
from common.realtime import sec_since_boot
|
from common.realtime import sec_since_boot
|
||||||
|
|
||||||
|
from selfdrive.car.fingerprints import fingerprints
|
||||||
|
from selfdrive.car.honda.can_parser import CANParser
|
||||||
|
|
||||||
def get_can_parser(civic, brake_only):
|
def get_can_parser(civic, brake_only):
|
||||||
# this function generates lists for signal, messages and initial values
|
# this function generates lists for signal, messages and initial values
|
||||||
@@ -46,6 +46,7 @@ def get_can_parser(civic, brake_only):
|
|||||||
("LEFT_BLINKER", 0x326, 0),
|
("LEFT_BLINKER", 0x326, 0),
|
||||||
("RIGHT_BLINKER", 0x326, 0),
|
("RIGHT_BLINKER", 0x326, 0),
|
||||||
("COUNTER", 0x324, 0),
|
("COUNTER", 0x324, 0),
|
||||||
|
("ENGINE_RPM", 0x17C, 0)
|
||||||
]
|
]
|
||||||
checks = [
|
checks = [
|
||||||
(0x14a, 100),
|
(0x14a, 100),
|
||||||
@@ -97,6 +98,7 @@ def get_can_parser(civic, brake_only):
|
|||||||
("LEFT_BLINKER", 0x294, 0),
|
("LEFT_BLINKER", 0x294, 0),
|
||||||
("RIGHT_BLINKER", 0x294, 0),
|
("RIGHT_BLINKER", 0x294, 0),
|
||||||
("COUNTER", 0x324, 0),
|
("COUNTER", 0x324, 0),
|
||||||
|
("ENGINE_RPM", 0x17C, 0)
|
||||||
]
|
]
|
||||||
checks = [
|
checks = [
|
||||||
(0x156, 100),
|
(0x156, 100),
|
||||||
@@ -153,6 +155,7 @@ def fingerprint(logcan):
|
|||||||
if len(possible_cars) == 1 and st is not None and (sec_since_boot()-st) > 0.1:
|
if len(possible_cars) == 1 and st is not None and (sec_since_boot()-st) > 0.1:
|
||||||
break
|
break
|
||||||
elif len(possible_cars) == 0:
|
elif len(possible_cars) == 0:
|
||||||
|
print finger
|
||||||
raise Exception("car doesn't match any fingerprints")
|
raise Exception("car doesn't match any fingerprints")
|
||||||
|
|
||||||
print "fingerprinted", possible_cars[0]
|
print "fingerprinted", possible_cars[0]
|
||||||
@@ -183,6 +186,9 @@ class CarState(object):
|
|||||||
self.cruise_setting = 0
|
self.cruise_setting = 0
|
||||||
self.blinker_on = 0
|
self.blinker_on = 0
|
||||||
|
|
||||||
|
self.left_blinker_on = 0
|
||||||
|
self.right_blinker_on = 0
|
||||||
|
|
||||||
# TODO: actually make this work
|
# TODO: actually make this work
|
||||||
self.a_ego = 0.
|
self.a_ego = 0.
|
||||||
|
|
||||||
@@ -190,16 +196,9 @@ class CarState(object):
|
|||||||
self.ui_speed_fudge = 1.01 if self.civic else 1.025
|
self.ui_speed_fudge = 1.01 if self.civic else 1.025
|
||||||
|
|
||||||
# load vehicle params
|
# load vehicle params
|
||||||
self.VP = VehicleParams(self.civic)
|
self.VP = VehicleParams(self.civic, self.brake_only, self.torque_mod)
|
||||||
|
|
||||||
def update(self, logcan):
|
|
||||||
# ******************* do can recv *******************
|
|
||||||
can_pub_main = []
|
|
||||||
canMonoTimes = []
|
|
||||||
for a in messaging.drain_sock(logcan):
|
|
||||||
canMonoTimes.append(a.logMonoTime)
|
|
||||||
can_pub_main.extend(can_capnp_to_can_list_old(a, [0,2]))
|
|
||||||
|
|
||||||
|
def update(self, can_pub_main):
|
||||||
cp = self.cp
|
cp = self.cp
|
||||||
cp.update_can(can_pub_main)
|
cp.update_can(can_pub_main)
|
||||||
|
|
||||||
@@ -215,6 +214,11 @@ class CarState(object):
|
|||||||
self.prev_cruise_setting = self.cruise_setting
|
self.prev_cruise_setting = self.cruise_setting
|
||||||
self.prev_blinker_on = self.blinker_on
|
self.prev_blinker_on = self.blinker_on
|
||||||
|
|
||||||
|
self.prev_left_blinker_on = self.left_blinker_on
|
||||||
|
self.prev_right_blinker_on = self.right_blinker_on
|
||||||
|
|
||||||
|
self.rpm = cp.vl[0x17C]['ENGINE_RPM']
|
||||||
|
|
||||||
# ******************* parse out can *******************
|
# ******************* parse out can *******************
|
||||||
self.door_all_closed = not any([cp.vl[0x405]['DOOR_OPEN_FL'], cp.vl[0x405]['DOOR_OPEN_FR'],
|
self.door_all_closed = not any([cp.vl[0x405]['DOOR_OPEN_FL'], cp.vl[0x405]['DOOR_OPEN_FR'],
|
||||||
cp.vl[0x405]['DOOR_OPEN_RL'], cp.vl[0x405]['DOOR_OPEN_RR']])
|
cp.vl[0x405]['DOOR_OPEN_RL'], cp.vl[0x405]['DOOR_OPEN_RR']])
|
||||||
@@ -252,6 +256,8 @@ class CarState(object):
|
|||||||
self.main_on = cp.vl[0x326]['MAIN_ON']
|
self.main_on = cp.vl[0x326]['MAIN_ON']
|
||||||
self.gear_shifter_valid = self.gear_shifter in [1,8] # TODO: 1/P allowed for debug
|
self.gear_shifter_valid = self.gear_shifter in [1,8] # TODO: 1/P allowed for debug
|
||||||
self.blinker_on = cp.vl[0x326]['LEFT_BLINKER'] or cp.vl[0x326]['RIGHT_BLINKER']
|
self.blinker_on = cp.vl[0x326]['LEFT_BLINKER'] or cp.vl[0x326]['RIGHT_BLINKER']
|
||||||
|
self.left_blinker_on = cp.vl[0x326]['LEFT_BLINKER']
|
||||||
|
self.right_blinker_on = cp.vl[0x326]['RIGHT_BLINKER']
|
||||||
else:
|
else:
|
||||||
self.gear_shifter = cp.vl[0x1A3]['GEAR_SHIFTER']
|
self.gear_shifter = cp.vl[0x1A3]['GEAR_SHIFTER']
|
||||||
self.angle_steers = cp.vl[0x156]['STEER_ANGLE']
|
self.angle_steers = cp.vl[0x156]['STEER_ANGLE']
|
||||||
@@ -261,6 +267,8 @@ class CarState(object):
|
|||||||
self.main_on = cp.vl[0x1A6]['MAIN_ON']
|
self.main_on = cp.vl[0x1A6]['MAIN_ON']
|
||||||
self.gear_shifter_valid = self.gear_shifter in [1,4] # TODO: 1/P allowed for debug
|
self.gear_shifter_valid = self.gear_shifter in [1,4] # TODO: 1/P allowed for debug
|
||||||
self.blinker_on = cp.vl[0x294]['LEFT_BLINKER'] or cp.vl[0x294]['RIGHT_BLINKER']
|
self.blinker_on = cp.vl[0x294]['LEFT_BLINKER'] or cp.vl[0x294]['RIGHT_BLINKER']
|
||||||
|
self.left_blinker_on = cp.vl[0x294]['LEFT_BLINKER']
|
||||||
|
self.right_blinker_on = cp.vl[0x294]['RIGHT_BLINKER']
|
||||||
self.car_gas = cp.vl[0x130]['CAR_GAS']
|
self.car_gas = cp.vl[0x130]['CAR_GAS']
|
||||||
self.brake_pressed = cp.vl[0x17C]['BRAKE_PRESSED']
|
self.brake_pressed = cp.vl[0x17C]['BRAKE_PRESSED']
|
||||||
self.user_brake = cp.vl[0x1A4]['USER_BRAKE']
|
self.user_brake = cp.vl[0x1A4]['USER_BRAKE']
|
||||||
@@ -272,4 +280,3 @@ class CarState(object):
|
|||||||
self.hud_lead = cp.vl[0x30C]['HUD_LEAD']
|
self.hud_lead = cp.vl[0x30C]['HUD_LEAD']
|
||||||
self.counter_pcm = cp.vl[0x324]['COUNTER']
|
self.counter_pcm = cp.vl[0x324]['COUNTER']
|
||||||
|
|
||||||
return canMonoTimes
|
|
||||||
Executable
+233
@@ -0,0 +1,233 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
import time
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
from selfdrive.config import Conversions as CV
|
||||||
|
from selfdrive.car.honda.carstate import CarState
|
||||||
|
from selfdrive.car.honda.carcontroller import CarController, AH
|
||||||
|
from selfdrive.boardd.boardd import can_capnp_to_can_list_old
|
||||||
|
|
||||||
|
from cereal import car
|
||||||
|
|
||||||
|
import zmq
|
||||||
|
from common.services import service_list
|
||||||
|
import selfdrive.messaging as messaging
|
||||||
|
|
||||||
|
# Car button codes
|
||||||
|
class CruiseButtons:
|
||||||
|
RES_ACCEL = 4
|
||||||
|
DECEL_SET = 3
|
||||||
|
CANCEL = 2
|
||||||
|
MAIN = 1
|
||||||
|
|
||||||
|
#car chimes: enumeration from dbc file. Chimes are for alerts and warnings
|
||||||
|
class CM:
|
||||||
|
MUTE = 0
|
||||||
|
SINGLE = 3
|
||||||
|
DOUBLE = 4
|
||||||
|
REPEATED = 1
|
||||||
|
CONTINUOUS = 2
|
||||||
|
|
||||||
|
#car beepss: enumeration from dbc file. Beeps are for activ and deactiv
|
||||||
|
class BP:
|
||||||
|
MUTE = 0
|
||||||
|
SINGLE = 3
|
||||||
|
TRIPLE = 2
|
||||||
|
REPEATED = 1
|
||||||
|
|
||||||
|
|
||||||
|
class CarInterface(object):
|
||||||
|
def __init__(self, read_only=False):
|
||||||
|
context = zmq.Context()
|
||||||
|
self.logcan = messaging.sub_sock(context, service_list['can'].port)
|
||||||
|
|
||||||
|
self.frame = 0
|
||||||
|
|
||||||
|
# *** init the major players ***
|
||||||
|
self.CS = CarState(self.logcan)
|
||||||
|
|
||||||
|
# sending if read only is False
|
||||||
|
if not read_only:
|
||||||
|
self.sendcan = messaging.pub_sock(context, service_list['sendcan'].port)
|
||||||
|
self.CC = CarController()
|
||||||
|
|
||||||
|
def getVehicleParams(self):
|
||||||
|
return self.CS.VP
|
||||||
|
|
||||||
|
# returns a car.CarState
|
||||||
|
def update(self):
|
||||||
|
# ******************* do can recv *******************
|
||||||
|
can_pub_main = []
|
||||||
|
canMonoTimes = []
|
||||||
|
for a in messaging.drain_sock(self.logcan):
|
||||||
|
canMonoTimes.append(a.logMonoTime)
|
||||||
|
can_pub_main.extend(can_capnp_to_can_list_old(a.can, [0,2]))
|
||||||
|
self.CS.update(can_pub_main)
|
||||||
|
|
||||||
|
# create message
|
||||||
|
ret = car.CarState.new_message()
|
||||||
|
|
||||||
|
# speeds
|
||||||
|
ret.vEgo = self.CS.v_ego
|
||||||
|
ret.wheelSpeeds.fl = self.CS.cp.vl[0x1D0]['WHEEL_SPEED_FL']
|
||||||
|
ret.wheelSpeeds.fr = self.CS.cp.vl[0x1D0]['WHEEL_SPEED_FR']
|
||||||
|
ret.wheelSpeeds.rl = self.CS.cp.vl[0x1D0]['WHEEL_SPEED_RL']
|
||||||
|
ret.wheelSpeeds.rr = self.CS.cp.vl[0x1D0]['WHEEL_SPEED_RR']
|
||||||
|
|
||||||
|
# gas pedal
|
||||||
|
ret.gas = self.CS.car_gas / 256.0
|
||||||
|
if self.CS.VP.brake_only:
|
||||||
|
ret.gasPressed = self.CS.pedal_gas > 0
|
||||||
|
else:
|
||||||
|
ret.gasPressed = self.CS.user_gas_pressed
|
||||||
|
|
||||||
|
# brake pedal
|
||||||
|
ret.brake = self.CS.user_brake
|
||||||
|
ret.brakePressed = self.CS.brake_pressed != 0
|
||||||
|
|
||||||
|
# steering wheel
|
||||||
|
# TODO: units
|
||||||
|
ret.steeringAngle = self.CS.angle_steers
|
||||||
|
ret.steeringTorque = self.CS.cp.vl[0x18F]['STEER_TORQUE_SENSOR']
|
||||||
|
ret.steeringPressed = self.CS.steer_override
|
||||||
|
|
||||||
|
# cruise state
|
||||||
|
ret.cruiseState.enabled = self.CS.pcm_acc_status != 0
|
||||||
|
ret.cruiseState.speed = self.CS.v_cruise_pcm * CV.KPH_TO_MS
|
||||||
|
|
||||||
|
# TODO: button presses
|
||||||
|
buttonEvents = []
|
||||||
|
|
||||||
|
if self.CS.left_blinker_on != self.CS.prev_left_blinker_on:
|
||||||
|
be = car.CarState.ButtonEvent.new_message()
|
||||||
|
be.type = 'leftBlinker'
|
||||||
|
be.pressed = self.CS.left_blinker_on != 0
|
||||||
|
buttonEvents.append(be)
|
||||||
|
|
||||||
|
if self.CS.right_blinker_on != self.CS.prev_right_blinker_on:
|
||||||
|
be = car.CarState.ButtonEvent.new_message()
|
||||||
|
be.type = 'rightBlinker'
|
||||||
|
be.pressed = self.CS.right_blinker_on != 0
|
||||||
|
buttonEvents.append(be)
|
||||||
|
|
||||||
|
if self.CS.cruise_buttons != self.CS.prev_cruise_buttons:
|
||||||
|
be = car.CarState.ButtonEvent.new_message()
|
||||||
|
be.type = 'unknown'
|
||||||
|
if self.CS.cruise_buttons != 0:
|
||||||
|
be.pressed = True
|
||||||
|
but = self.CS.cruise_buttons
|
||||||
|
else:
|
||||||
|
be.pressed = False
|
||||||
|
but = self.CS.prev_cruise_buttons
|
||||||
|
if but == CruiseButtons.RES_ACCEL:
|
||||||
|
be.type = 'accelCruise'
|
||||||
|
elif but == CruiseButtons.DECEL_SET:
|
||||||
|
be.type = 'decelCruise'
|
||||||
|
elif but == CruiseButtons.CANCEL:
|
||||||
|
be.type = 'cancel'
|
||||||
|
elif but == CruiseButtons.MAIN:
|
||||||
|
be.type = 'altButton3'
|
||||||
|
buttonEvents.append(be)
|
||||||
|
|
||||||
|
if self.CS.cruise_setting != self.CS.prev_cruise_setting:
|
||||||
|
be = car.CarState.ButtonEvent.new_message()
|
||||||
|
be.type = 'unknown'
|
||||||
|
if self.CS.cruise_setting != 0:
|
||||||
|
be.pressed = True
|
||||||
|
but = self.CS.cruise_setting
|
||||||
|
else:
|
||||||
|
be.pressed = False
|
||||||
|
but = self.CS.prev_cruise_setting
|
||||||
|
if but == 1:
|
||||||
|
be.type = 'altButton1'
|
||||||
|
# TODO: more buttons?
|
||||||
|
buttonEvents.append(be)
|
||||||
|
ret.buttonEvents = buttonEvents
|
||||||
|
|
||||||
|
# errors
|
||||||
|
# TODO: I don't like the way capnp does enums
|
||||||
|
# These strings aren't checked at compile time
|
||||||
|
errors = []
|
||||||
|
if not self.CS.can_valid:
|
||||||
|
errors.append('commIssue')
|
||||||
|
if self.CS.steer_error:
|
||||||
|
errors.append('steerUnavailable')
|
||||||
|
elif self.CS.steer_not_allowed:
|
||||||
|
errors.append('steerTemporarilyUnavailable')
|
||||||
|
if self.CS.brake_error:
|
||||||
|
errors.append('brakeUnavailable')
|
||||||
|
if not self.CS.gear_shifter_valid:
|
||||||
|
errors.append('wrongGear')
|
||||||
|
if not self.CS.door_all_closed:
|
||||||
|
errors.append('doorOpen')
|
||||||
|
if not self.CS.seatbelt:
|
||||||
|
errors.append('seatbeltNotLatched')
|
||||||
|
if self.CS.esp_disabled:
|
||||||
|
errors.append('espDisabled')
|
||||||
|
if not self.CS.main_on:
|
||||||
|
errors.append('wrongCarMode')
|
||||||
|
if self.CS.gear_shifter == 2:
|
||||||
|
errors.append('reverseGear')
|
||||||
|
|
||||||
|
ret.errors = errors
|
||||||
|
ret.canMonoTimes = canMonoTimes
|
||||||
|
|
||||||
|
# cast to reader so it can't be modified
|
||||||
|
#print ret
|
||||||
|
return ret.as_reader()
|
||||||
|
|
||||||
|
# pass in a car.CarControl
|
||||||
|
# to be called @ 100hz
|
||||||
|
def apply(self, c):
|
||||||
|
#print c
|
||||||
|
|
||||||
|
if c.hudControl.speedVisible:
|
||||||
|
hud_v_cruise = c.hudControl.setSpeed * CV.MS_TO_KPH
|
||||||
|
else:
|
||||||
|
hud_v_cruise = 255
|
||||||
|
|
||||||
|
hud_alert = {
|
||||||
|
"none": AH.NONE,
|
||||||
|
"fcw": AH.FCW,
|
||||||
|
"steerRequired": AH.STEER,
|
||||||
|
"brakePressed": AH.BRAKE_PRESSED,
|
||||||
|
"wrongGear": AH.GEAR_NOT_D,
|
||||||
|
"seatbeltUnbuckled": AH.SEATBELT,
|
||||||
|
"speedTooHigh": AH.SPEED_TOO_HIGH}[str(c.hudControl.visualAlert)]
|
||||||
|
|
||||||
|
snd_beep, snd_chime = {
|
||||||
|
"none": (BP.MUTE, CM.MUTE),
|
||||||
|
"beepSingle": (BP.SINGLE, CM.MUTE),
|
||||||
|
"beepTriple": (BP.TRIPLE, CM.MUTE),
|
||||||
|
"beepRepeated": (BP.REPEATED, CM.MUTE),
|
||||||
|
"chimeSingle": (BP.MUTE, CM.SINGLE),
|
||||||
|
"chimeDouble": (BP.MUTE, CM.DOUBLE),
|
||||||
|
"chimeRepeated": (BP.MUTE, CM.REPEATED),
|
||||||
|
"chimeContinuous": (BP.MUTE, CM.CONTINUOUS)}[str(c.hudControl.audibleAlert)]
|
||||||
|
|
||||||
|
pcm_accel = int(np.clip(c.cruiseControl.accelOverride/1.4,0,1)*0xc6)
|
||||||
|
|
||||||
|
self.CC.update(self.sendcan, c.enabled, self.CS, self.frame, \
|
||||||
|
c.gas, c.brake, c.steeringTorque, \
|
||||||
|
c.cruiseControl.speedOverride, \
|
||||||
|
c.cruiseControl.override, \
|
||||||
|
c.cruiseControl.cancel, \
|
||||||
|
pcm_accel, \
|
||||||
|
hud_v_cruise, c.hudControl.lanesVisible, \
|
||||||
|
hud_show_car = c.hudControl.leadVisible, \
|
||||||
|
hud_alert = hud_alert, \
|
||||||
|
snd_beep = snd_beep, \
|
||||||
|
snd_chime = snd_chime)
|
||||||
|
|
||||||
|
self.frame += 1
|
||||||
|
return not (c.enabled and not self.CC.controls_allowed)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
CI = CarInterface(read_only=True)
|
||||||
|
while 1:
|
||||||
|
cs = CI.update()
|
||||||
|
print(chr(27) + "[2J")
|
||||||
|
print cs
|
||||||
|
time.sleep(0.1)
|
||||||
|
|
||||||
+7
-1
@@ -57,12 +57,18 @@ class UIParams:
|
|||||||
car_color = 110
|
car_color = 110
|
||||||
|
|
||||||
class VehicleParams:
|
class VehicleParams:
|
||||||
def __init__(self, civic):
|
def __init__(self, civic, brake_only=False, torque_mod=False):
|
||||||
if civic:
|
if civic:
|
||||||
self.wheelbase = 2.67
|
self.wheelbase = 2.67
|
||||||
self.steer_ratio = 15.3
|
self.steer_ratio = 15.3
|
||||||
self.slip_factor = 0.0014
|
self.slip_factor = 0.0014
|
||||||
|
self.civic = True
|
||||||
else:
|
else:
|
||||||
self.wheelbase = 2.67 # from http://www.edmunds.com/acura/ilx/2016/sedan/features-specs/
|
self.wheelbase = 2.67 # from http://www.edmunds.com/acura/ilx/2016/sedan/features-specs/
|
||||||
self.steer_ratio = 15.3 # from http://www.edmunds.com/acura/ilx/2016/road-test-specs/
|
self.steer_ratio = 15.3 # from http://www.edmunds.com/acura/ilx/2016/road-test-specs/
|
||||||
self.slip_factor = 0.0014
|
self.slip_factor = 0.0014
|
||||||
|
self.civic = False
|
||||||
|
self.brake_only = brake_only
|
||||||
|
self.torque_mod = torque_mod
|
||||||
|
self.ui_speed_fudge = 1.01 if self.civic else 1.025
|
||||||
|
|
||||||
|
|||||||
+175
-217
@@ -1,85 +1,75 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
|
import os
|
||||||
import zmq
|
import zmq
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
import selfdrive.messaging as messaging
|
||||||
|
|
||||||
|
from cereal import car
|
||||||
|
|
||||||
|
from selfdrive.config import Conversions as CV
|
||||||
from common.services import service_list
|
from common.services import service_list
|
||||||
from common.realtime import sec_since_boot, set_realtime_priority, Ratekeeper
|
from common.realtime import sec_since_boot, set_realtime_priority, Ratekeeper
|
||||||
|
|
||||||
from selfdrive.config import CruiseButtons
|
|
||||||
from selfdrive.config import Conversions as CV
|
|
||||||
|
|
||||||
from selfdrive.controls.lib.drive_helpers import learn_angle_offset
|
from selfdrive.controls.lib.drive_helpers import learn_angle_offset
|
||||||
from selfdrive.controls.lib.alert_database import process_alert, AI
|
|
||||||
|
|
||||||
import selfdrive.messaging as messaging
|
|
||||||
|
|
||||||
from selfdrive.controls.lib.carstate import CarState
|
|
||||||
from selfdrive.controls.lib.carcontroller import CarController
|
|
||||||
from selfdrive.controls.lib.longcontrol import LongControl
|
from selfdrive.controls.lib.longcontrol import LongControl
|
||||||
from selfdrive.controls.lib.latcontrol import LatControl
|
from selfdrive.controls.lib.latcontrol import LatControl
|
||||||
|
|
||||||
from selfdrive.controls.lib.pathplanner import PathPlanner
|
from selfdrive.controls.lib.pathplanner import PathPlanner
|
||||||
from selfdrive.controls.lib.adaptivecruise import AdaptiveCruise
|
from selfdrive.controls.lib.adaptivecruise import AdaptiveCruise
|
||||||
|
|
||||||
|
from selfdrive.controls.lib.alertmanager import AlertManager
|
||||||
|
|
||||||
|
car_type = os.getenv("CAR")
|
||||||
|
if car_type is not None:
|
||||||
|
exec('from selfdrive.car.'+car_type+'.interface import CarInterface')
|
||||||
|
else:
|
||||||
|
from selfdrive.car.honda.interface import CarInterface
|
||||||
|
|
||||||
|
V_CRUISE_MAX = 144
|
||||||
|
V_CRUISE_MIN = 8
|
||||||
|
V_CRUISE_DELTA = 8
|
||||||
|
V_CRUISE_ENABLE_MIN = 40
|
||||||
|
|
||||||
def controlsd_thread(gctx, rate=100): #rate in Hz
|
def controlsd_thread(gctx, rate=100): #rate in Hz
|
||||||
# *** log ***
|
# *** log ***
|
||||||
context = zmq.Context()
|
context = zmq.Context()
|
||||||
live100 = messaging.pub_sock(context, service_list['live100'].port)
|
live100 = messaging.pub_sock(context, service_list['live100'].port)
|
||||||
|
|
||||||
thermal = messaging.sub_sock(context, service_list['thermal'].port)
|
thermal = messaging.sub_sock(context, service_list['thermal'].port)
|
||||||
live20 = messaging.sub_sock(context, service_list['live20'].port)
|
live20 = messaging.sub_sock(context, service_list['live20'].port)
|
||||||
model = messaging.sub_sock(context, service_list['model'].port)
|
model = messaging.sub_sock(context, service_list['model'].port)
|
||||||
|
health = messaging.sub_sock(context, service_list['health'].port)
|
||||||
|
|
||||||
logcan = messaging.sub_sock(context, service_list['can'].port)
|
# connects to can and sendcan
|
||||||
sendcan = messaging.pub_sock(context, service_list['sendcan'].port)
|
CI = CarInterface()
|
||||||
|
VP = CI.getVehicleParams()
|
||||||
# *** init the major players ***
|
|
||||||
CS = CarState(logcan)
|
|
||||||
CC = CarController()
|
|
||||||
|
|
||||||
PP = PathPlanner(model)
|
PP = PathPlanner(model)
|
||||||
AC = AdaptiveCruise(live20)
|
AC = AdaptiveCruise(live20)
|
||||||
|
|
||||||
|
AM = AlertManager()
|
||||||
|
|
||||||
LoC = LongControl()
|
LoC = LongControl()
|
||||||
LaC = LatControl()
|
LaC = LatControl()
|
||||||
|
|
||||||
# *** control initial values ***
|
# controls enabled state
|
||||||
apply_brake = 0
|
|
||||||
enabled = False
|
enabled = False
|
||||||
|
last_enable_request = 0
|
||||||
|
|
||||||
# *** time values ***
|
# learned angle offset
|
||||||
last_enable_pressed = 0
|
|
||||||
|
|
||||||
# *** controls initial values ***
|
|
||||||
# *** display stuff
|
|
||||||
soft_disable_start = 0
|
|
||||||
sounding = False
|
|
||||||
no_mismatch_pcm_last, no_mismatch_ctrl_last = 0, 0
|
|
||||||
|
|
||||||
# car state
|
|
||||||
alert, sound_exp, hud_exp, text_exp, alert_p = None, 0, 0, 0, 0
|
|
||||||
rear_view_cam, rear_view_toggle = False, False
|
|
||||||
|
|
||||||
v_cruise = 255 # this means no display
|
|
||||||
v_cruise_max = 144
|
|
||||||
v_cruise_min = 8
|
|
||||||
v_cruise_delta = 8
|
|
||||||
|
|
||||||
# on activation target at least 25mph. With 5mph you need too much tapping
|
|
||||||
v_cruise_enable_min = 40
|
|
||||||
|
|
||||||
hud_v_cruise = 255
|
|
||||||
|
|
||||||
angle_offset = 0
|
angle_offset = 0
|
||||||
|
|
||||||
max_enable_speed = 57. # ~91 mph
|
# rear view camera state
|
||||||
|
rear_view_toggle = False
|
||||||
|
|
||||||
pcm_threshold = 25.*CV.MPH_TO_MS # below this speed pcm cancels
|
v_cruise_kph = 255
|
||||||
|
|
||||||
overtemp = True
|
|
||||||
|
|
||||||
# 0.0 - 1.0
|
# 0.0 - 1.0
|
||||||
awareness_status = 0.0
|
awareness_status = 0.0
|
||||||
|
|
||||||
|
soft_disable_timer = None
|
||||||
|
|
||||||
# start the loop
|
# start the loop
|
||||||
set_realtime_priority(2)
|
set_realtime_priority(2)
|
||||||
|
|
||||||
@@ -88,14 +78,54 @@ def controlsd_thread(gctx, rate=100): #rate in Hz
|
|||||||
cur_time = sec_since_boot()
|
cur_time = sec_since_boot()
|
||||||
|
|
||||||
# read CAN
|
# read CAN
|
||||||
canMonoTimes = CS.update(logcan)
|
CS = CI.update()
|
||||||
|
|
||||||
# **** rearview mirror management ***
|
# did it request to enable?
|
||||||
if CS.cruise_setting == 1 and CS.prev_cruise_setting == 0:
|
enable_request, enable_condition = False, False
|
||||||
rear_view_toggle = not rear_view_toggle
|
|
||||||
|
|
||||||
# show rear view camera on phone if in reverse gear or when lkas button is pressed
|
if enabled:
|
||||||
rear_view_cam = (CS.gear_shifter == 2) or rear_view_toggle or CS.blinker_on
|
# gives the user 6 minutes
|
||||||
|
awareness_status -= 1.0/(100*60*6)
|
||||||
|
if awareness_status <= 0.:
|
||||||
|
AM.add("driverDistracted", enabled)
|
||||||
|
|
||||||
|
# handle button presses
|
||||||
|
for b in CS.buttonEvents:
|
||||||
|
print b
|
||||||
|
|
||||||
|
# reset awareness on any user action
|
||||||
|
awareness_status = 1.0
|
||||||
|
|
||||||
|
# button presses for rear view
|
||||||
|
if b.type == "leftBlinker" or b.type == "rightBlinker":
|
||||||
|
if b.pressed:
|
||||||
|
rear_view_toggle = True
|
||||||
|
else:
|
||||||
|
rear_view_toggle = False
|
||||||
|
|
||||||
|
if b.type == "altButton1" and b.pressed:
|
||||||
|
rear_view_toggle = not rear_view_toggle
|
||||||
|
|
||||||
|
if not VP.brake_only and enabled and not b.pressed:
|
||||||
|
if b.type == "accelCruise":
|
||||||
|
v_cruise_kph = v_cruise_kph - (v_cruise_kph % V_CRUISE_DELTA) + V_CRUISE_DELTA
|
||||||
|
elif b.type == "decelCruise":
|
||||||
|
v_cruise_kph = v_cruise_kph - (v_cruise_kph % V_CRUISE_DELTA) - V_CRUISE_DELTA
|
||||||
|
v_cruise_kph = np.clip(v_cruise_kph, V_CRUISE_MIN, V_CRUISE_MAX)
|
||||||
|
|
||||||
|
if not enabled and b.type in ["accelCruise", "decelCruise"] and not b.pressed:
|
||||||
|
enable_request = True
|
||||||
|
|
||||||
|
# do disable on button down
|
||||||
|
if b.type == "cancel" and b.pressed:
|
||||||
|
AM.add("disable", enabled)
|
||||||
|
|
||||||
|
# *** health checking logic ***
|
||||||
|
hh = messaging.recv_sock(health)
|
||||||
|
if hh is not None:
|
||||||
|
# if the board isn't allowing controls but somehow we are enabled!
|
||||||
|
if not hh.health.controlsAllowed and enabled:
|
||||||
|
AM.add("controlsMismatch", enabled)
|
||||||
|
|
||||||
# *** thermal checking logic ***
|
# *** thermal checking logic ***
|
||||||
|
|
||||||
@@ -105,130 +135,57 @@ def controlsd_thread(gctx, rate=100): #rate in Hz
|
|||||||
cpu_temps = [td.thermal.cpu0, td.thermal.cpu1, td.thermal.cpu2,
|
cpu_temps = [td.thermal.cpu0, td.thermal.cpu1, td.thermal.cpu2,
|
||||||
td.thermal.cpu3, td.thermal.mem, td.thermal.gpu]
|
td.thermal.cpu3, td.thermal.mem, td.thermal.gpu]
|
||||||
# check overtemp
|
# check overtemp
|
||||||
overtemp = any(t > 950 for t in cpu_temps)
|
if any(t > 950 for t in cpu_temps):
|
||||||
|
AM.add("overheat", enabled)
|
||||||
|
|
||||||
# *** getting model logic ***
|
# *** getting model logic ***
|
||||||
PP.update(cur_time, CS.v_ego)
|
PP.update(cur_time, CS.vEgo)
|
||||||
|
|
||||||
if rk.frame % 5 == 2:
|
if rk.frame % 5 == 2:
|
||||||
# *** run this at 20hz again ***
|
# *** run this at 20hz again ***
|
||||||
angle_offset = learn_angle_offset(enabled, CS.v_ego, angle_offset, np.asarray(PP.d_poly), LaC.y_des, CS.steer_override)
|
angle_offset = learn_angle_offset(enabled, CS.vEgo, angle_offset, np.asarray(PP.d_poly), LaC.y_des, CS.steeringPressed)
|
||||||
|
|
||||||
# to avoid race conditions, check if control has been disabled for at least 0.2s
|
# disable if the pedals are pressed while engaged, this is a user disable
|
||||||
mismatch_ctrl = not CC.controls_allowed and enabled
|
|
||||||
mismatch_pcm = (not CS.pcm_acc_status and (not apply_brake or CS.v_ego < 0.1)) and enabled
|
|
||||||
|
|
||||||
# keep resetting start timer if mismatch isn't true
|
|
||||||
if not mismatch_ctrl:
|
|
||||||
no_mismatch_ctrl_last = cur_time
|
|
||||||
if not mismatch_pcm or not CS.brake_only:
|
|
||||||
no_mismatch_pcm_last = cur_time
|
|
||||||
|
|
||||||
#*** v_cruise logic ***
|
|
||||||
if CS.brake_only:
|
|
||||||
v_cruise = int(CS.v_cruise_pcm) # TODO: why sometimes v_cruise_pcm is long type?
|
|
||||||
else:
|
|
||||||
if CS.prev_cruise_buttons == 0 and CS.cruise_buttons == CruiseButtons.RES_ACCEL and enabled:
|
|
||||||
v_cruise = v_cruise - (v_cruise % v_cruise_delta) + v_cruise_delta
|
|
||||||
elif CS.prev_cruise_buttons == 0 and CS.cruise_buttons == CruiseButtons.DECEL_SET and enabled:
|
|
||||||
v_cruise = v_cruise + (v_cruise % v_cruise_delta) - v_cruise_delta
|
|
||||||
|
|
||||||
# *** enabling/disabling logic ***
|
|
||||||
enable_pressed = (CS.prev_cruise_buttons == CruiseButtons.DECEL_SET or CS.prev_cruise_buttons == CruiseButtons.RES_ACCEL) \
|
|
||||||
and CS.cruise_buttons == 0
|
|
||||||
|
|
||||||
if enable_pressed:
|
|
||||||
print "enabled pressed at", cur_time
|
|
||||||
last_enable_pressed = cur_time
|
|
||||||
|
|
||||||
# if pcm does speed control than we need to wait on pcm to enable
|
|
||||||
if CS.brake_only:
|
|
||||||
enable_condition = (cur_time - last_enable_pressed) < 0.2 and CS.pcm_acc_status
|
|
||||||
else:
|
|
||||||
enable_condition = enable_pressed
|
|
||||||
|
|
||||||
# always clear the alert at every cycle
|
|
||||||
alert_id = []
|
|
||||||
|
|
||||||
# check for PCM not enabling
|
|
||||||
if CS.brake_only and (cur_time - last_enable_pressed) < 0.2 and not CS.pcm_acc_status:
|
|
||||||
print "waiting for PCM to enable"
|
|
||||||
|
|
||||||
# check for denied enabling
|
|
||||||
if enable_pressed and not enabled:
|
|
||||||
deny_enable = \
|
|
||||||
[(AI.SEATBELT, not CS.seatbelt),
|
|
||||||
(AI.DOOR_OPEN, not CS.door_all_closed),
|
|
||||||
(AI.ESP_OFF, CS.esp_disabled),
|
|
||||||
(AI.STEER_ERROR, CS.steer_error),
|
|
||||||
(AI.BRAKE_ERROR, CS.brake_error),
|
|
||||||
(AI.GEAR_NOT_D, not CS.gear_shifter_valid),
|
|
||||||
(AI.MAIN_OFF, not CS.main_on),
|
|
||||||
(AI.PEDAL_PRESSED, CS.user_gas_pressed or CS.brake_pressed or (CS.pedal_gas > 0 and CS.brake_only)),
|
|
||||||
(AI.HIGH_SPEED, CS.v_ego > max_enable_speed),
|
|
||||||
(AI.OVERHEAT, overtemp),
|
|
||||||
(AI.COMM_ISSUE, PP.dead or AC.dead),
|
|
||||||
(AI.CONTROLSD_LAG, rk.remaining < -0.2)]
|
|
||||||
for alertn, cond in deny_enable:
|
|
||||||
if cond:
|
|
||||||
alert_id += [alertn]
|
|
||||||
|
|
||||||
# check for soft disables
|
|
||||||
if enabled:
|
if enabled:
|
||||||
soft_disable = \
|
if CS.gasPressed or CS.brakePressed:
|
||||||
[(AI.SEATBELT_SD, not CS.seatbelt),
|
AM.add("disable", enabled)
|
||||||
(AI.DOOR_OPEN_SD, not CS.door_all_closed),
|
|
||||||
(AI.ESP_OFF_SD, CS.esp_disabled),
|
|
||||||
(AI.OVERHEAT_SD, overtemp),
|
|
||||||
(AI.COMM_ISSUE_SD, PP.dead or AC.dead),
|
|
||||||
(AI.CONTROLSD_LAG_SD, rk.remaining < -0.2)]
|
|
||||||
sounding = False
|
|
||||||
for alertn, cond in soft_disable:
|
|
||||||
if cond:
|
|
||||||
alert_id += [alertn]
|
|
||||||
sounding = True
|
|
||||||
# soft disengagement expired, user need to take control
|
|
||||||
if (cur_time - soft_disable_start) > 3.:
|
|
||||||
enabled = False
|
|
||||||
v_cruise = 255
|
|
||||||
if not sounding:
|
|
||||||
soft_disable_start = cur_time
|
|
||||||
|
|
||||||
# check for immediate disables
|
if enable_request:
|
||||||
if enabled:
|
# check for pressed pedals
|
||||||
immediate_disable = \
|
if CS.gasPressed or CS.brakePressed:
|
||||||
[(AI.PCM_LOW_SPEED, (cur_time > no_mismatch_pcm_last > 0.2) and CS.v_ego < pcm_threshold),
|
AM.add("pedalPressed", enabled)
|
||||||
(AI.STEER_ERROR_ID, CS.steer_error),
|
enable_request = False
|
||||||
(AI.BRAKE_ERROR_ID, CS.brake_error),
|
else:
|
||||||
(AI.CTRL_MISMATCH_ID, (cur_time - no_mismatch_ctrl_last) > 0.2),
|
print "enabled pressed at", cur_time
|
||||||
(AI.PCM_MISMATCH_ID, (cur_time - no_mismatch_pcm_last) > 0.2)]
|
last_enable_request = cur_time
|
||||||
for alertn, cond in immediate_disable:
|
|
||||||
if cond:
|
|
||||||
alert_id += [alertn]
|
|
||||||
# immediate turn off control
|
|
||||||
enabled = False
|
|
||||||
v_cruise = 255
|
|
||||||
|
|
||||||
# user disabling
|
if VP.brake_only:
|
||||||
if enabled and (CS.user_gas_pressed or CS.brake_pressed or not CS.gear_shifter_valid or \
|
enable_condition = ((cur_time - last_enable_request) < 0.2) and CS.cruiseState.enabled
|
||||||
(CS.cruise_buttons == CruiseButtons.CANCEL and CS.prev_cruise_buttons == 0) or \
|
else:
|
||||||
not CS.main_on or (CS.pedal_gas > 0 and CS.brake_only)):
|
enable_condition = enable_request
|
||||||
enabled = False
|
|
||||||
v_cruise = 255
|
|
||||||
alert_id += [AI.DISABLE]
|
|
||||||
|
|
||||||
# enabling
|
if enable_request or enable_condition or enabled:
|
||||||
if enable_condition and not enabled and len(alert_id) == 0:
|
# add all alerts from car
|
||||||
|
for alert in CS.errors:
|
||||||
|
AM.add(alert, enabled)
|
||||||
|
|
||||||
|
if AC.dead:
|
||||||
|
AM.add("radarCommIssue", enabled)
|
||||||
|
|
||||||
|
if PP.dead:
|
||||||
|
AM.add("modelCommIssue", enabled)
|
||||||
|
|
||||||
|
if enable_condition and not enabled and not AM.alertPresent():
|
||||||
print "*** enabling controls"
|
print "*** enabling controls"
|
||||||
|
|
||||||
#enable both lateral and longitudinal controls
|
# beep for enabling
|
||||||
|
AM.add("enable", enabled)
|
||||||
|
|
||||||
|
# enable both lateral and longitudinal controls
|
||||||
enabled = True
|
enabled = True
|
||||||
counter_pcm_enabled = CS.counter_pcm
|
|
||||||
# on activation, let's always set v_cruise from where we are, even if PCM ACC is active
|
# on activation, let's always set v_cruise from where we are, even if PCM ACC is active
|
||||||
# what we want to be displayed in mph
|
v_cruise_kph = int(round(np.maximum(CS.vEgo * CV.MS_TO_KPH * VP.ui_speed_fudge, V_CRUISE_ENABLE_MIN)))
|
||||||
v_cruise_mph = round(CS.v_ego * CV.MS_TO_MPH * CS.ui_speed_fudge)
|
|
||||||
# what we need to send to have that displayed
|
|
||||||
v_cruise = int(round(np.maximum(v_cruise_mph * CV.MPH_TO_KPH, v_cruise_enable_min)))
|
|
||||||
|
|
||||||
# 6 minutes driver you're on
|
# 6 minutes driver you're on
|
||||||
awareness_status = 1.0
|
awareness_status = 1.0
|
||||||
@@ -236,67 +193,69 @@ def controlsd_thread(gctx, rate=100): #rate in Hz
|
|||||||
# reset the PID loops
|
# reset the PID loops
|
||||||
LaC.reset()
|
LaC.reset()
|
||||||
# start long control at actual speed
|
# start long control at actual speed
|
||||||
LoC.reset(v_pid = CS.v_ego)
|
LoC.reset(v_pid = CS.vEgo)
|
||||||
|
|
||||||
alert_id += [AI.ENABLE]
|
if VP.brake_only and CS.cruiseState.enabled:
|
||||||
|
v_cruise_kph = CS.cruiseState.speed * CV.MS_TO_KPH
|
||||||
|
|
||||||
if v_cruise != 255 and not CS.brake_only:
|
# *** put the adaptive in adaptive cruise control ***
|
||||||
v_cruise = np.clip(v_cruise, v_cruise_min, v_cruise_max)
|
AC.update(cur_time, CS.vEgo, CS.steeringAngle, LoC.v_pid, awareness_status, VP)
|
||||||
|
|
||||||
# **** awareness status manager ****
|
|
||||||
if enabled:
|
|
||||||
# gives the user 6 minutes
|
|
||||||
awareness_status -= 1.0/(100*60*6)
|
|
||||||
# reset on steering, blinker, or cruise buttons
|
|
||||||
if CS.steer_override or CS.blinker_on or CS.cruise_buttons or CS.cruise_setting:
|
|
||||||
awareness_status = 1.0
|
|
||||||
if awareness_status <= 0.:
|
|
||||||
alert_id += [AI.DRIVER_DISTRACTED]
|
|
||||||
|
|
||||||
# ****** initial actuators commands ***
|
|
||||||
# *** gas/brake PID loop ***
|
# *** gas/brake PID loop ***
|
||||||
AC.update(cur_time, CS.v_ego, CS.angle_steers, LoC.v_pid, awareness_status, CS.VP)
|
final_gas, final_brake = LoC.update(enabled, CS.vEgo, v_cruise_kph, AC.v_target_lead, AC.a_target, AC.jerk_factor, VP)
|
||||||
final_gas, final_brake = LoC.update(enabled, CS, v_cruise, AC.v_target_lead, AC.a_target, AC.jerk_factor)
|
|
||||||
pcm_accel = int(np.clip(AC.a_pcm/1.4,0,1)*0xc6) # TODO: perc of max accel in ACC?
|
|
||||||
|
|
||||||
# *** steering PID loop ***
|
# *** steering PID loop ***
|
||||||
final_steer, sat_flag = LaC.update(enabled, CS, PP.d_poly, angle_offset)
|
final_steer, sat_flag = LaC.update(enabled, CS.vEgo, CS.steeringAngle, CS.steeringPressed, PP.d_poly, angle_offset, VP)
|
||||||
|
|
||||||
# this needs to stay before hysteresis logic to avoid pcm staying on control during brake hysteresis
|
|
||||||
pcm_override = True # this is always True
|
|
||||||
pcm_cancel_cmd = False
|
|
||||||
if CS.brake_only and final_brake == 0.:
|
|
||||||
pcm_speed = LoC.v_pid - .3 # FIXME: just for exp
|
|
||||||
else:
|
|
||||||
pcm_speed = 0
|
|
||||||
|
|
||||||
# ***** handle alerts ****
|
# ***** handle alerts ****
|
||||||
# send a "steering required alert" if saturation count has reached the limit
|
# send a "steering required alert" if saturation count has reached the limit
|
||||||
if sat_flag:
|
if sat_flag:
|
||||||
alert_id += [AI.STEER_SATURATED]
|
AM.add("steerSaturated", enabled)
|
||||||
|
|
||||||
# process the alert, based on id
|
if enabled and AM.alertShouldDisable():
|
||||||
alert, chime, beep, hud_alert, alert_text, sound_exp, hud_exp, text_exp, alert_p = \
|
print "DISABLING IMMEDIATELY ON ALERT"
|
||||||
process_alert(alert_id, alert, cur_time, sound_exp, hud_exp, text_exp, alert_p)
|
enabled = False
|
||||||
|
|
||||||
# alerts pub
|
if enabled and AM.alertShouldSoftDisable():
|
||||||
if len(alert_id) != 0:
|
if soft_disable_timer is None:
|
||||||
print alert_id, alert_text
|
soft_disable_timer = 3 * rate
|
||||||
|
elif soft_disable_timer == 0:
|
||||||
# *** process for hud display ***
|
print "SOFT DISABLING ON ALERT"
|
||||||
if not enabled or (hud_v_cruise == 255 and CS.counter_pcm == counter_pcm_enabled):
|
enabled = False
|
||||||
hud_v_cruise = 255
|
else:
|
||||||
|
soft_disable_timer -= 1
|
||||||
else:
|
else:
|
||||||
hud_v_cruise = v_cruise
|
soft_disable_timer = None
|
||||||
|
|
||||||
# *** actually do can sends ***
|
|
||||||
CC.update(sendcan, enabled, CS, rk.frame, \
|
# *** push the alerts to current ***
|
||||||
final_gas, final_brake, final_steer, \
|
alert_text_1, alert_text_2, visual_alert, audible_alert = AM.process_alerts(cur_time)
|
||||||
pcm_speed, pcm_override, pcm_cancel_cmd, pcm_accel, \
|
|
||||||
hud_v_cruise, hud_show_lanes = enabled, \
|
# ***** control the car *****
|
||||||
hud_show_car = AC.has_lead, \
|
CC = car.CarControl.new_message()
|
||||||
hud_alert = hud_alert, \
|
|
||||||
snd_beep = beep, snd_chime = chime)
|
CC.enabled = enabled
|
||||||
|
|
||||||
|
CC.gas = float(final_gas)
|
||||||
|
CC.brake = float(final_brake)
|
||||||
|
CC.steeringTorque = float(final_steer)
|
||||||
|
|
||||||
|
CC.cruiseControl.override = True
|
||||||
|
CC.cruiseControl.cancel = bool((not VP.brake_only) or (not enabled and CS.cruiseState.enabled)) # always cancel if we have an interceptor
|
||||||
|
CC.cruiseControl.speedOverride = float((LoC.v_pid - .3) if (VP.brake_only and final_brake == 0.) else 0.0)
|
||||||
|
CC.cruiseControl.accelOverride = float(AC.a_pcm)
|
||||||
|
|
||||||
|
CC.hudControl.setSpeed = float(v_cruise_kph * CV.KPH_TO_MS)
|
||||||
|
CC.hudControl.speedVisible = enabled
|
||||||
|
CC.hudControl.lanesVisible = enabled
|
||||||
|
CC.hudControl.leadVisible = bool(AC.has_lead)
|
||||||
|
|
||||||
|
CC.hudControl.visualAlert = visual_alert
|
||||||
|
CC.hudControl.audibleAlert = audible_alert
|
||||||
|
|
||||||
|
# this alert will apply next controls cycle
|
||||||
|
if not CI.apply(CC):
|
||||||
|
AM.add("controlsFailed", enabled)
|
||||||
|
|
||||||
# ***** publish state to logger *****
|
# ***** publish state to logger *****
|
||||||
|
|
||||||
@@ -304,14 +263,14 @@ def controlsd_thread(gctx, rate=100): #rate in Hz
|
|||||||
dat = messaging.new_message()
|
dat = messaging.new_message()
|
||||||
dat.init('live100')
|
dat.init('live100')
|
||||||
|
|
||||||
# move liveUI into live100
|
# show rear view camera on phone if in reverse gear or when button is pressed
|
||||||
dat.live100.rearViewCam = bool(rear_view_cam)
|
dat.live100.rearViewCam = ('reverseGear' in CS.errors) or rear_view_toggle
|
||||||
dat.live100.alertText1 = alert_text[0]
|
dat.live100.alertText1 = alert_text_1
|
||||||
dat.live100.alertText2 = alert_text[1]
|
dat.live100.alertText2 = alert_text_2
|
||||||
dat.live100.awarenessStatus = max(awareness_status, 0.0) if enabled else 0.0
|
dat.live100.awarenessStatus = max(awareness_status, 0.0) if enabled else 0.0
|
||||||
|
|
||||||
# what packets were used to process
|
# what packets were used to process
|
||||||
dat.live100.canMonoTimes = canMonoTimes
|
dat.live100.canMonoTimes = list(CS.canMonoTimes)
|
||||||
dat.live100.mdMonoTime = PP.logMonoTime
|
dat.live100.mdMonoTime = PP.logMonoTime
|
||||||
dat.live100.l20MonoTime = AC.logMonoTime
|
dat.live100.l20MonoTime = AC.logMonoTime
|
||||||
|
|
||||||
@@ -319,15 +278,13 @@ def controlsd_thread(gctx, rate=100): #rate in Hz
|
|||||||
dat.live100.enabled = enabled
|
dat.live100.enabled = enabled
|
||||||
|
|
||||||
# car state
|
# car state
|
||||||
dat.live100.vEgo = float(CS.v_ego)
|
dat.live100.vEgo = CS.vEgo
|
||||||
dat.live100.aEgo = float(CS.a_ego)
|
dat.live100.angleSteers = CS.steeringAngle
|
||||||
dat.live100.angleSteers = float(CS.angle_steers)
|
dat.live100.steerOverride = CS.steeringPressed
|
||||||
dat.live100.hudLead = CS.hud_lead
|
|
||||||
dat.live100.steerOverride = CS.steer_override
|
|
||||||
|
|
||||||
# longitudinal control state
|
# longitudinal control state
|
||||||
dat.live100.vPid = float(LoC.v_pid)
|
dat.live100.vPid = float(LoC.v_pid)
|
||||||
dat.live100.vCruise = float(v_cruise)
|
dat.live100.vCruise = float(v_cruise_kph)
|
||||||
dat.live100.upAccelCmd = float(LoC.Up_accel_cmd)
|
dat.live100.upAccelCmd = float(LoC.Up_accel_cmd)
|
||||||
dat.live100.uiAccelCmd = float(LoC.Ui_accel_cmd)
|
dat.live100.uiAccelCmd = float(LoC.Ui_accel_cmd)
|
||||||
|
|
||||||
@@ -356,3 +313,4 @@ def main(gctx=None):
|
|||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
|
||||||
|
|||||||
@@ -1,178 +0,0 @@
|
|||||||
alerts = []
|
|
||||||
keys = ["id",
|
|
||||||
"chime",
|
|
||||||
"beep",
|
|
||||||
"hud_alert",
|
|
||||||
"screen_chime",
|
|
||||||
"priority",
|
|
||||||
"text_line_1",
|
|
||||||
"text_line_2",
|
|
||||||
"duration_sound",
|
|
||||||
"duration_hud_alert",
|
|
||||||
"duration_text"]
|
|
||||||
|
|
||||||
|
|
||||||
#car chimes: enumeration from dbc file. Chimes are for alerts and warnings
|
|
||||||
class CM:
|
|
||||||
MUTE = 0
|
|
||||||
SINGLE = 3
|
|
||||||
DOUBLE = 4
|
|
||||||
REPEATED = 1
|
|
||||||
CONTINUOUS = 2
|
|
||||||
|
|
||||||
|
|
||||||
#car beepss: enumeration from dbc file. Beeps are for activ and deactiv
|
|
||||||
class BP:
|
|
||||||
MUTE = 0
|
|
||||||
SINGLE = 3
|
|
||||||
TRIPLE = 2
|
|
||||||
REPEATED = 1
|
|
||||||
|
|
||||||
|
|
||||||
# lert ids
|
|
||||||
class AI:
|
|
||||||
ENABLE = 0
|
|
||||||
DISABLE = 1
|
|
||||||
SEATBELT = 2
|
|
||||||
DOOR_OPEN = 3
|
|
||||||
PEDAL_PRESSED = 4
|
|
||||||
COMM_ISSUE = 5
|
|
||||||
ESP_OFF = 6
|
|
||||||
FCW = 7
|
|
||||||
STEER_ERROR = 8
|
|
||||||
BRAKE_ERROR = 9
|
|
||||||
CALIB_INCOMPLETE = 10
|
|
||||||
CALIB_INVALID = 11
|
|
||||||
GEAR_NOT_D = 12
|
|
||||||
MAIN_OFF = 13
|
|
||||||
STEER_SATURATED = 14
|
|
||||||
PCM_LOW_SPEED = 15
|
|
||||||
THERMAL_DEAD = 16
|
|
||||||
OVERHEAT = 17
|
|
||||||
HIGH_SPEED = 18
|
|
||||||
CONTROLSD_LAG = 19
|
|
||||||
STEER_ERROR_ID = 100
|
|
||||||
BRAKE_ERROR_ID = 101
|
|
||||||
PCM_MISMATCH_ID = 102
|
|
||||||
CTRL_MISMATCH_ID = 103
|
|
||||||
SEATBELT_SD = 200
|
|
||||||
DOOR_OPEN_SD = 201
|
|
||||||
COMM_ISSUE_SD = 202
|
|
||||||
ESP_OFF_SD = 203
|
|
||||||
THERMAL_DEAD_SD = 204
|
|
||||||
OVERHEAT_SD = 205
|
|
||||||
CONTROLSD_LAG_SD = 206
|
|
||||||
CALIB_INCOMPLETE_SD = 207
|
|
||||||
CALIB_INVALID_SD = 208
|
|
||||||
DRIVER_DISTRACTED = 300
|
|
||||||
|
|
||||||
class AH:
|
|
||||||
#[alert_idx, value]
|
|
||||||
# See dbc files for info on values"
|
|
||||||
NONE = [0, 0]
|
|
||||||
FCW = [1, 0x8]
|
|
||||||
STEER = [2, 1]
|
|
||||||
BRAKE_PRESSED = [3, 10]
|
|
||||||
GEAR_NOT_D = [4, 6]
|
|
||||||
SEATBELT = [5, 5]
|
|
||||||
SPEED_TOO_HIGH = [6, 8]
|
|
||||||
|
|
||||||
class ET:
|
|
||||||
ENABLE = 0
|
|
||||||
NO_ENTRY = 1
|
|
||||||
WARNING = 2
|
|
||||||
SOFT_DISABLE = 3
|
|
||||||
IMMEDIATE_DISABLE = 4
|
|
||||||
USER_DISABLE = 5
|
|
||||||
|
|
||||||
def process_alert(alert_id, alert, cur_time, sound_exp, hud_exp, text_exp, alert_p):
|
|
||||||
# INPUTS:
|
|
||||||
# alert_id is mapped to the alert properties in alert_database
|
|
||||||
# cur_time is current time
|
|
||||||
# sound_exp is when the alert beep/chime is supposed to end
|
|
||||||
# hud_exp is when the hud visual is supposed to end
|
|
||||||
# text_exp is when the alert text is supposed to disappear
|
|
||||||
# alert_p is the priority of the current alert
|
|
||||||
# CM, BP, AH are classes defined in alert_database and they respresents chimes, beeps and hud_alerts
|
|
||||||
if len(alert_id) > 0:
|
|
||||||
# take the alert with higher priority
|
|
||||||
alerts_present = filter(lambda a_id: a_id['id'] in alert_id, alerts)
|
|
||||||
alert = sorted(alerts_present, key=lambda k: k['priority'])[-1]
|
|
||||||
# check if we have a more important alert
|
|
||||||
if alert['priority'] > alert_p:
|
|
||||||
alert_p = alert['priority']
|
|
||||||
sound_exp = cur_time + alert['duration_sound']
|
|
||||||
hud_exp = cur_time + alert['duration_hud_alert']
|
|
||||||
text_exp = cur_time + alert['duration_text']
|
|
||||||
|
|
||||||
chime = CM.MUTE
|
|
||||||
beep = BP.MUTE
|
|
||||||
if cur_time < sound_exp:
|
|
||||||
chime = alert['chime']
|
|
||||||
beep = alert['beep']
|
|
||||||
|
|
||||||
hud_alert = AH.NONE
|
|
||||||
if cur_time < hud_exp:
|
|
||||||
hud_alert = alert['hud_alert']
|
|
||||||
|
|
||||||
alert_text = ["", ""]
|
|
||||||
if cur_time < text_exp:
|
|
||||||
alert_text = [alert['text_line_1'], alert['text_line_2']]
|
|
||||||
|
|
||||||
if chime == CM.MUTE and beep == BP.MUTE and hud_alert == AH.NONE: #and alert_text[0] is None and alert_text[1] is None:
|
|
||||||
alert_p = 0
|
|
||||||
return alert, chime, beep, hud_alert, alert_text, sound_exp, hud_exp, text_exp, alert_p
|
|
||||||
|
|
||||||
def process_hud_alert(hud_alert):
|
|
||||||
# initialize to no alert
|
|
||||||
fcw_display = 0
|
|
||||||
steer_required = 0
|
|
||||||
acc_alert = 0
|
|
||||||
if hud_alert == AH.NONE: # no alert
|
|
||||||
pass
|
|
||||||
elif hud_alert == AH.FCW: # FCW
|
|
||||||
fcw_display = hud_alert[1]
|
|
||||||
elif hud_alert == AH.STEER: # STEER
|
|
||||||
steer_required = hud_alert[1]
|
|
||||||
else: # any other ACC alert
|
|
||||||
acc_alert = hud_alert[1]
|
|
||||||
|
|
||||||
return fcw_display, steer_required, acc_alert
|
|
||||||
|
|
||||||
def app_alert(alert_add):
|
|
||||||
alerts.append(dict(zip(keys, alert_add)))
|
|
||||||
|
|
||||||
app_alert([AI.ENABLE, CM.MUTE, BP.SINGLE, AH.NONE, ET.ENABLE, 2, "", "", .2, 0., 0.])
|
|
||||||
app_alert([AI.DISABLE, CM.MUTE, BP.SINGLE, AH.NONE, ET.USER_DISABLE, 2, "", "", .2, 0., 0.])
|
|
||||||
app_alert([AI.SEATBELT, CM.DOUBLE, BP.MUTE, AH.SEATBELT, ET.NO_ENTRY, 1, "Comma Unavailable", "Seatbelt Unlatched", .4, 2., 3.])
|
|
||||||
app_alert([AI.DOOR_OPEN, CM.DOUBLE, BP.MUTE, AH.NONE, ET.NO_ENTRY, 1, "Comma Unavailable", "Door Open", .4, 0., 3.])
|
|
||||||
app_alert([AI.PEDAL_PRESSED, CM.DOUBLE, BP.MUTE, AH.BRAKE_PRESSED, ET.NO_ENTRY, 1, "Comma Unavailable", "Pedal Pressed", .4, 2., 3.])
|
|
||||||
app_alert([AI.COMM_ISSUE, CM.DOUBLE, BP.MUTE, AH.NONE, ET.NO_ENTRY, 1, "Comma Unavailable", "Communcation Issues", .4, 0., 3.])
|
|
||||||
app_alert([AI.ESP_OFF, CM.DOUBLE, BP.MUTE, AH.NONE, ET.NO_ENTRY, 1, "Comma Unavailable", "ESP Off", .4, 0., 3.])
|
|
||||||
app_alert([AI.FCW, CM.REPEATED, BP.MUTE, AH.FCW, ET.WARNING, 3, "Risk of Collision", "", 1., 2., 3.])
|
|
||||||
app_alert([AI.STEER_ERROR, CM.DOUBLE, BP.MUTE, AH.NONE, ET.NO_ENTRY, 1, "Comma Unavailable", "Steer Error", .4, 0., 3.])
|
|
||||||
app_alert([AI.BRAKE_ERROR, CM.DOUBLE, BP.MUTE, AH.NONE, ET.NO_ENTRY, 1, "Comma Unavailable", "Brake Error", .4, 0., 3.])
|
|
||||||
app_alert([AI.CALIB_INCOMPLETE, CM.DOUBLE, BP.MUTE, AH.NONE, ET.NO_ENTRY, 1, "Comma Unavailable", "Calibration in Progress", .4, 0., 3.])
|
|
||||||
app_alert([AI.CALIB_INVALID, CM.DOUBLE, BP.MUTE, AH.NONE, ET.NO_ENTRY, 1, "Comma Unavailable", "Calibration Error", .4, 0., 3.])
|
|
||||||
app_alert([AI.GEAR_NOT_D, CM.DOUBLE, BP.MUTE, AH.GEAR_NOT_D, ET.NO_ENTRY, 1, "Comma Unavailable", "Gear not in D", .4, 2., 3.])
|
|
||||||
app_alert([AI.MAIN_OFF, CM.MUTE, BP.MUTE, AH.NONE, ET.NO_ENTRY, 1, "Comma Unavailable", "Main Switch Off", .4, 0., 3.])
|
|
||||||
app_alert([AI.STEER_SATURATED, CM.SINGLE, BP.MUTE, AH.STEER, ET.WARNING, 2, "Take Control", "Steer Control Saturated", 1., 2., 3.])
|
|
||||||
app_alert([AI.PCM_LOW_SPEED, CM.MUTE, BP.SINGLE, AH.STEER, ET.WARNING, 2, "Comma disengaged", "Speed too low", .2, 2., 3.])
|
|
||||||
app_alert([AI.THERMAL_DEAD, CM.DOUBLE, BP.MUTE, AH.NONE, ET.NO_ENTRY, 1, "Comma Unavailable", "Thermal Unavailable", .4, 0., 3.])
|
|
||||||
app_alert([AI.OVERHEAT, CM.DOUBLE, BP.MUTE, AH.NONE, ET.NO_ENTRY, 1, "Comma Unavailable", "System Overheated", .4, 0., 3.])
|
|
||||||
app_alert([AI.HIGH_SPEED, CM.DOUBLE, BP.MUTE, AH.SPEED_TOO_HIGH, ET.NO_ENTRY, 1, "Comma Unavailable", "Speed Too High", .4, 2., 3.])
|
|
||||||
app_alert([AI.CONTROLSD_LAG, CM.DOUBLE, BP.MUTE, AH.NONE, ET.NO_ENTRY, 1, "Comma Unavailable", "Controls Lagging", .4, 0., 3.])
|
|
||||||
app_alert([AI.STEER_ERROR_ID, CM.REPEATED, BP.MUTE, AH.STEER, ET.IMMEDIATE_DISABLE, 3, "Take Control Immediately", "Steer Error", 1., 3., 3.])
|
|
||||||
app_alert([AI.BRAKE_ERROR_ID, CM.REPEATED, BP.MUTE, AH.STEER, ET.IMMEDIATE_DISABLE, 3, "Take Control Immediately", "Brake Error", 1., 3., 3.])
|
|
||||||
app_alert([AI.PCM_MISMATCH_ID, CM.REPEATED, BP.MUTE, AH.STEER, ET.IMMEDIATE_DISABLE, 3, "Take Control Immediately", "Pcm Mismatch", 1., 3., 3.])
|
|
||||||
app_alert([AI.CTRL_MISMATCH_ID, CM.REPEATED, BP.MUTE, AH.STEER, ET.IMMEDIATE_DISABLE, 3, "Take Control Immediately", "Ctrl Mismatch", 1., 3., 3.])
|
|
||||||
app_alert([AI.SEATBELT_SD, CM.REPEATED, BP.MUTE, AH.STEER, ET.SOFT_DISABLE, 3, "Take Control Immediately", "Seatbelt Unlatched", 1., 3., 3.])
|
|
||||||
app_alert([AI.DOOR_OPEN_SD, CM.REPEATED, BP.MUTE, AH.STEER, ET.SOFT_DISABLE, 3, "Take Control Immediately", "Door Open", 1., 3., 3.])
|
|
||||||
app_alert([AI.COMM_ISSUE_SD, CM.REPEATED, BP.MUTE, AH.STEER, ET.SOFT_DISABLE, 3, "Take Control Immediately", "Technical Issues", 1., 3., 3.])
|
|
||||||
app_alert([AI.ESP_OFF_SD, CM.REPEATED, BP.MUTE, AH.STEER, ET.SOFT_DISABLE, 3, "Take Control Immediately", "ESP Off", 1., 3., 3.])
|
|
||||||
app_alert([AI.THERMAL_DEAD_SD, CM.REPEATED, BP.MUTE, AH.STEER, ET.SOFT_DISABLE, 3, "Take Control Immediately", "Thermal Unavailable", 1., 3., 3.])
|
|
||||||
app_alert([AI.OVERHEAT_SD, CM.REPEATED, BP.MUTE, AH.STEER, ET.SOFT_DISABLE, 3, "Take Control Immediately", "System Overheated", 1., 3., 3.])
|
|
||||||
app_alert([AI.CONTROLSD_LAG_SD, CM.REPEATED, BP.MUTE, AH.STEER, ET.SOFT_DISABLE, 3, "Take Control Immediately", "Controls Lagging", 1., 3., 3.])
|
|
||||||
app_alert([AI.CALIB_INCOMPLETE_SD, CM.REPEATED, BP.MUTE, AH.STEER, ET.SOFT_DISABLE, 3, "Take Control Immediately", "Calibration in Progress", 1., 3., 3.])
|
|
||||||
app_alert([AI.CALIB_INVALID_SD, CM.REPEATED, BP.MUTE, AH.STEER, ET.SOFT_DISABLE, 3, "Take Control Immediately", "Calibration Error", 1., 3., 3.])
|
|
||||||
app_alert([AI.DRIVER_DISTRACTED, CM.REPEATED, BP.MUTE, AH.STEER, ET.SOFT_DISABLE, 2, "Take Control to Regain Speed", "User Distracted", 1., 1., 1.])
|
|
||||||
@@ -0,0 +1,113 @@
|
|||||||
|
from cereal import car
|
||||||
|
|
||||||
|
class ET:
|
||||||
|
ENABLE = 0
|
||||||
|
NO_ENTRY = 1
|
||||||
|
WARNING = 2
|
||||||
|
SOFT_DISABLE = 3
|
||||||
|
IMMEDIATE_DISABLE = 4
|
||||||
|
USER_DISABLE = 5
|
||||||
|
|
||||||
|
class alert(object):
|
||||||
|
def __init__(self, alert_text_1, alert_text_2, alert_type, visual_alert, audible_alert, duration_sound, duration_hud_alert, duration_text):
|
||||||
|
self.alert_text_1 = alert_text_1
|
||||||
|
self.alert_text_2 = alert_text_2
|
||||||
|
self.alert_type = alert_type
|
||||||
|
self.visual_alert = visual_alert if visual_alert is not None else "none"
|
||||||
|
self.audible_alert = audible_alert if audible_alert is not None else "none"
|
||||||
|
|
||||||
|
self.duration_sound = duration_sound
|
||||||
|
self.duration_hud_alert = duration_hud_alert
|
||||||
|
self.duration_text = duration_text
|
||||||
|
|
||||||
|
# typecheck that enums are valid on startup
|
||||||
|
tst = car.CarControl.new_message()
|
||||||
|
tst.hudControl.visualAlert = self.visual_alert
|
||||||
|
tst.hudControl.audibleAlert = self.audible_alert
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.alert_text_1 + "/" + self.alert_text_2 + " " + str(self.alert_type) + " " + str(self.visual_alert) + " " + str(self.audible_alert)
|
||||||
|
|
||||||
|
def __gt__(self, alert2):
|
||||||
|
return self.alert_type > alert2.alert_type
|
||||||
|
|
||||||
|
class AlertManager(object):
|
||||||
|
alerts = {
|
||||||
|
"enable": alert("", "", ET.ENABLE, None, "beepSingle", .2, 0., 0.),
|
||||||
|
"disable": alert("", "", ET.USER_DISABLE, None, "beepSingle", .2, 0., 0.),
|
||||||
|
"pedalPressed": alert("Comma Unavailable", "Pedal Pressed", ET.NO_ENTRY, "brakePressed", "chimeDouble", .4, 2., 3.),
|
||||||
|
"driverDistracted": alert("Take Control to Regain Speed", "User Distracted", ET.WARNING, "steerRequired", "chimeRepeated", 1., 1., 1.),
|
||||||
|
"steerSaturated": alert("Take Control", "Steer Control Saturated", ET.WARNING, "steerRequired", "chimeSingle", 1., 2., 3.),
|
||||||
|
"overheat": alert("Take Control Immediately", "System Overheated", ET.SOFT_DISABLE, "steerRequired", "chimeRepeated", 1., 3., 3.),
|
||||||
|
"controlsMismatch": alert("Take Control Immediately", "Controls Mismatch", ET.IMMEDIATE_DISABLE, "steerRequired", "chimeRepeated", 1., 3., 3.),
|
||||||
|
"radarCommIssue": alert("Take Control Immediately", "Radar Comm Issue", ET.IMMEDIATE_DISABLE, "steerRequired", "chimeRepeated", 1., 3., 3.),
|
||||||
|
"modelCommIssue": alert("Take Control Immediately", "Model Comm Issue", ET.IMMEDIATE_DISABLE, "steerRequired", "chimeRepeated", 1., 3., 3.),
|
||||||
|
"controlsFailed": alert("Take Control Immediately", "Controls Failed", ET.IMMEDIATE_DISABLE, "steerRequired", "chimeRepeated", 1., 3., 3.),
|
||||||
|
# car errors
|
||||||
|
"commIssue": alert("Take Control Immediately","Communication Issues", ET.IMMEDIATE_DISABLE, "steerRequired", "chimeRepeated", 1., 3., 3.),
|
||||||
|
"steerUnavailable": alert("Take Control Immediately","Steer Error", ET.IMMEDIATE_DISABLE, "steerRequired", "chimeRepeated", 1., 3., 3.),
|
||||||
|
"steerTemporarilyUnavailable": alert("Take Control", "Steer Temporarily Unavailable", ET.WARNING, "steerRequired", "chimeRepeated", 1., 2., 3.),
|
||||||
|
"brakeUnavailable": alert("Take Control Immediately","Brake Error", ET.IMMEDIATE_DISABLE, "steerRequired", "chimeRepeated", 1., 3., 3.),
|
||||||
|
"gasUnavailable": alert("Take Control Immediately","Gas Error", ET.IMMEDIATE_DISABLE, "steerRequired", "chimeRepeated", 1., 3., 3.),
|
||||||
|
"wrongGear": alert("Take Control Immediately","Gear not D", ET.SOFT_DISABLE, "steerRequired", "chimeRepeated", 1., 3., 3.),
|
||||||
|
"reverseGear": alert("Take Control Immediately","Car in Reverse", ET.IMMEDIATE_DISABLE, "steerRequired", "chimeRepeated", 1., 3., 3.),
|
||||||
|
"doorOpen": alert("Take Control Immediately","Door Open", ET.SOFT_DISABLE, "steerRequired", "chimeRepeated", 1., 3., 3.),
|
||||||
|
"seatbeltNotLatched": alert("Take Control Immediately","Seatbelt Unlatched", ET.SOFT_DISABLE, "steerRequired", "chimeRepeated", 1., 3., 3.),
|
||||||
|
"espDisabled": alert("Take Control Immediately","ESP Off", ET.SOFT_DISABLE, "steerRequired", "chimeRepeated", 1., 3., 3.),
|
||||||
|
"wrongCarMode": alert("Comma Unavailable","Main Switch Off", ET.NO_ENTRY, None, None, .4, 0., 3.),
|
||||||
|
}
|
||||||
|
def __init__(self):
|
||||||
|
self.activealerts = []
|
||||||
|
self.current_alert = None
|
||||||
|
|
||||||
|
def alertPresent(self):
|
||||||
|
return len(self.activealerts) > 0
|
||||||
|
|
||||||
|
def alertShouldSoftDisable(self):
|
||||||
|
return len(self.activealerts) > 0 and self.activealerts[0].alert_type == ET.SOFT_DISABLE
|
||||||
|
|
||||||
|
def alertShouldDisable(self):
|
||||||
|
return len(self.activealerts) > 0 and self.activealerts[0].alert_type >= ET.IMMEDIATE_DISABLE
|
||||||
|
|
||||||
|
def add(self, alert_type, enabled = True):
|
||||||
|
this_alert = self.alerts[str(alert_type)]
|
||||||
|
|
||||||
|
# downgrade the alert if we aren't enabled
|
||||||
|
if not enabled and this_alert.alert_type > ET.NO_ENTRY:
|
||||||
|
this_alert = alert("Comma Unavailable" if this_alert.alert_text_1 != "" else "", this_alert.alert_text_2, ET.NO_ENTRY, None, "chimeDouble", .4, 0., 3.)
|
||||||
|
|
||||||
|
# ignore no entries if we are enabled
|
||||||
|
if enabled and this_alert.alert_type < ET.WARNING:
|
||||||
|
return
|
||||||
|
|
||||||
|
self.activealerts.append(this_alert)
|
||||||
|
self.activealerts.sort()
|
||||||
|
|
||||||
|
def process_alerts(self, cur_time):
|
||||||
|
if self.alertPresent():
|
||||||
|
self.alert_start_time = cur_time
|
||||||
|
self.current_alert = self.activealerts[0]
|
||||||
|
print self.current_alert
|
||||||
|
|
||||||
|
alert_text_1 = ""
|
||||||
|
alert_text_2 = ""
|
||||||
|
visual_alert = "none"
|
||||||
|
audible_alert = "none"
|
||||||
|
|
||||||
|
if self.current_alert is not None:
|
||||||
|
# ewwwww
|
||||||
|
if self.alert_start_time + self.current_alert.duration_sound > cur_time:
|
||||||
|
audible_alert = self.current_alert.audible_alert
|
||||||
|
|
||||||
|
if self.alert_start_time + self.current_alert.duration_hud_alert > cur_time:
|
||||||
|
visual_alert = self.current_alert.visual_alert
|
||||||
|
|
||||||
|
if self.alert_start_time + self.current_alert.duration_text > cur_time:
|
||||||
|
alert_text_1 = self.current_alert.alert_text_1
|
||||||
|
alert_text_2 = self.current_alert.alert_text_2
|
||||||
|
|
||||||
|
# reset
|
||||||
|
self.activealerts = []
|
||||||
|
|
||||||
|
return alert_text_1, alert_text_2, visual_alert, audible_alert
|
||||||
|
|
||||||
@@ -97,24 +97,24 @@ class LatControl(object):
|
|||||||
def reset(self):
|
def reset(self):
|
||||||
self.Ui_steer = 0.
|
self.Ui_steer = 0.
|
||||||
|
|
||||||
def update(self, enabled, CS, d_poly, angle_offset):
|
def update(self, enabled, v_ego, angle_steers, steer_override, d_poly, angle_offset, VP):
|
||||||
rate = 100
|
rate = 100
|
||||||
|
|
||||||
steer_max = 1.0
|
steer_max = 1.0
|
||||||
|
|
||||||
# how far we look ahead is function of speed
|
# how far we look ahead is function of speed
|
||||||
d_lookahead = calc_d_lookahead(CS.v_ego)
|
d_lookahead = calc_d_lookahead(v_ego)
|
||||||
|
|
||||||
# calculate actual offset at the lookahead point
|
# calculate actual offset at the lookahead point
|
||||||
self.y_actual, _ = calc_lookahead_offset(CS.v_ego, CS.angle_steers,
|
self.y_actual, _ = calc_lookahead_offset(v_ego, angle_steers,
|
||||||
d_lookahead, CS.VP, angle_offset)
|
d_lookahead, VP, angle_offset)
|
||||||
|
|
||||||
# desired lookahead offset
|
# desired lookahead offset
|
||||||
self.y_des = np.polyval(d_poly, d_lookahead)
|
self.y_des = np.polyval(d_poly, d_lookahead)
|
||||||
|
|
||||||
output_steer, self.Up_steer, self.Ui_steer, self.lateral_control_sat, self.sat_count, sat_flag = pid_lateral_control(
|
output_steer, self.Up_steer, self.Ui_steer, self.lateral_control_sat, self.sat_count, sat_flag = pid_lateral_control(
|
||||||
CS.v_ego, self.y_actual, self.y_des, self.Ui_steer, steer_max,
|
v_ego, self.y_actual, self.y_des, self.Ui_steer, steer_max,
|
||||||
CS.steer_override, self.sat_count, enabled, CS.torque_mod, rate)
|
steer_override, self.sat_count, enabled, VP.torque_mod, rate)
|
||||||
|
|
||||||
final_steer = np.clip(output_steer, -steer_max, steer_max)
|
final_steer = np.clip(output_steer, -steer_max, steer_max)
|
||||||
return final_steer, sat_flag
|
return final_steer, sat_flag
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ def pid_long_control(v_ego, v_pid, Ui_accel_cmd, gas_max, brake_max, jerk_factor
|
|||||||
accel_cmd_new = Ui_accel_cmd_new + Up_accel_cmd
|
accel_cmd_new = Ui_accel_cmd_new + Up_accel_cmd
|
||||||
output_gb_new = compute_gb([accel_cmd_new, v_ego])
|
output_gb_new = compute_gb([accel_cmd_new, v_ego])
|
||||||
|
|
||||||
# Anti-wind up for integrator: only update integrator if we not against the thottle and brake limits
|
# Anti-wind up for integrator: only update integrator if we not against the throttle and brake limits
|
||||||
# do not wind up if we are changing gear and we are on the gas pedal
|
# do not wind up if we are changing gear and we are on the gas pedal
|
||||||
if (((v_error >= 0. and (output_gb_new < gas_max or Ui_accel_cmd < 0)) or
|
if (((v_error >= 0. and (output_gb_new < gas_max or Ui_accel_cmd < 0)) or
|
||||||
(v_error <= 0. and (output_gb_new > - brake_max or Ui_accel_cmd > 0))) and
|
(v_error <= 0. and (output_gb_new > - brake_max or Ui_accel_cmd > 0))) and
|
||||||
@@ -151,9 +151,9 @@ class LongControl(object):
|
|||||||
self.Ui_accel_cmd = 0.
|
self.Ui_accel_cmd = 0.
|
||||||
self.v_pid = v_pid
|
self.v_pid = v_pid
|
||||||
|
|
||||||
def update(self, enabled, CS, v_cruise, v_target_lead, a_target, jerk_factor):
|
def update(self, enabled, v_ego, v_cruise, v_target_lead, a_target, jerk_factor, VP):
|
||||||
# TODO: not every time
|
# TODO: not every time
|
||||||
if CS.brake_only:
|
if VP.brake_only:
|
||||||
gas_max_v = [0, 0] # values
|
gas_max_v = [0, 0] # values
|
||||||
else:
|
else:
|
||||||
gas_max_v = [0.6, 0.6] # values
|
gas_max_v = [0.6, 0.6] # values
|
||||||
@@ -162,8 +162,8 @@ class LongControl(object):
|
|||||||
brake_max_bp = [0., 5., 20., 100.] # speeds
|
brake_max_bp = [0., 5., 20., 100.] # speeds
|
||||||
|
|
||||||
# brake and gas limits
|
# brake and gas limits
|
||||||
brake_max = np.interp(CS.v_ego, brake_max_bp, brake_max_v)
|
brake_max = np.interp(v_ego, brake_max_bp, brake_max_v)
|
||||||
gas_max = np.interp(CS.v_ego, gas_max_bp, gas_max_v)
|
gas_max = np.interp(v_ego, gas_max_bp, gas_max_v)
|
||||||
|
|
||||||
overshoot_allowance = 2.0 # overshoot allowed when changing accel sign
|
overshoot_allowance = 2.0 # overshoot allowed when changing accel sign
|
||||||
|
|
||||||
@@ -172,30 +172,30 @@ class LongControl(object):
|
|||||||
|
|
||||||
# limit max target speed based on cruise setting:
|
# limit max target speed based on cruise setting:
|
||||||
v_cruise_mph = round(v_cruise * CV.KPH_TO_MPH) # what's displayed in mph on the IC
|
v_cruise_mph = round(v_cruise * CV.KPH_TO_MPH) # what's displayed in mph on the IC
|
||||||
v_target = np.minimum(v_target_lead, v_cruise_mph * CV.MPH_TO_MS / CS.ui_speed_fudge)
|
v_target = np.minimum(v_target_lead, v_cruise_mph * CV.MPH_TO_MS / VP.ui_speed_fudge)
|
||||||
|
|
||||||
max_speed_delta_up = a_target[1]*1.0/rate
|
max_speed_delta_up = a_target[1]*1.0/rate
|
||||||
max_speed_delta_down = a_target[0]*1.0/rate
|
max_speed_delta_down = a_target[0]*1.0/rate
|
||||||
|
|
||||||
# *** long control substate transitions
|
# *** long control substate transitions
|
||||||
self.long_control_state = long_control_state_trans(enabled, self.long_control_state, CS.v_ego, v_target, self.v_pid, output_gb)
|
self.long_control_state = long_control_state_trans(enabled, self.long_control_state, v_ego, v_target, self.v_pid, output_gb)
|
||||||
|
|
||||||
# *** long control behavior based on state
|
# *** long control behavior based on state
|
||||||
# TODO: move this to drive_helpers
|
# TODO: move this to drive_helpers
|
||||||
# disabled
|
# disabled
|
||||||
if self.long_control_state == LongCtrlState.off:
|
if self.long_control_state == LongCtrlState.off:
|
||||||
self.v_pid = CS.v_ego # do nothing
|
self.v_pid = v_ego # do nothing
|
||||||
output_gb = 0.
|
output_gb = 0.
|
||||||
self.Ui_accel_cmd = 0.
|
self.Ui_accel_cmd = 0.
|
||||||
# tracking objects and driving
|
# tracking objects and driving
|
||||||
elif self.long_control_state == LongCtrlState.pid:
|
elif self.long_control_state == LongCtrlState.pid:
|
||||||
#reset v_pid close to v_ego if it was too far and new v_target is closer to v_ego
|
#reset v_pid close to v_ego if it was too far and new v_target is closer to v_ego
|
||||||
if ((self.v_pid > CS.v_ego + overshoot_allowance) and
|
if ((self.v_pid > v_ego + overshoot_allowance) and
|
||||||
(v_target < self.v_pid)):
|
(v_target < self.v_pid)):
|
||||||
self.v_pid = np.maximum(v_target, CS.v_ego + overshoot_allowance)
|
self.v_pid = np.maximum(v_target, v_ego + overshoot_allowance)
|
||||||
elif ((self.v_pid < CS.v_ego - overshoot_allowance) and
|
elif ((self.v_pid < v_ego - overshoot_allowance) and
|
||||||
(v_target > self.v_pid)):
|
(v_target > self.v_pid)):
|
||||||
self.v_pid = np.minimum(v_target, CS.v_ego - overshoot_allowance)
|
self.v_pid = np.minimum(v_target, v_ego - overshoot_allowance)
|
||||||
|
|
||||||
# move v_pid no faster than allowed accel limits
|
# move v_pid no faster than allowed accel limits
|
||||||
if (v_target > self.v_pid + max_speed_delta_up):
|
if (v_target > self.v_pid + max_speed_delta_up):
|
||||||
@@ -206,27 +206,29 @@ class LongControl(object):
|
|||||||
self.v_pid = v_target
|
self.v_pid = v_target
|
||||||
|
|
||||||
# to avoid too much wind up on acceleration, limit positive speed error
|
# to avoid too much wind up on acceleration, limit positive speed error
|
||||||
if not CS.brake_only:
|
if not VP.brake_only:
|
||||||
max_speed_error = np.interp(CS.v_ego, max_speed_error_bp, max_speed_error_v)
|
max_speed_error = np.interp(v_ego, max_speed_error_bp, max_speed_error_v)
|
||||||
self.v_pid = np.minimum(self.v_pid, CS.v_ego + max_speed_error)
|
self.v_pid = np.minimum(self.v_pid, v_ego + max_speed_error)
|
||||||
|
|
||||||
output_gb, self.Up_accel_cmd, self.Ui_accel_cmd, self.long_control_sat = pid_long_control(CS.v_ego, self.v_pid, \
|
# TODO: removed anti windup on gear change, does it matter?
|
||||||
self.Ui_accel_cmd, gas_max, brake_max, jerk_factor, CS.gear, rate)
|
output_gb, self.Up_accel_cmd, self.Ui_accel_cmd, self.long_control_sat = pid_long_control(v_ego, self.v_pid, \
|
||||||
|
self.Ui_accel_cmd, gas_max, brake_max, jerk_factor, 0, rate)
|
||||||
# intention is to stop, switch to a different brake control until we stop
|
# intention is to stop, switch to a different brake control until we stop
|
||||||
elif self.long_control_state == LongCtrlState.stopping:
|
elif self.long_control_state == LongCtrlState.stopping:
|
||||||
if CS.v_ego > 0. or output_gb > -brake_stopping_target or not CS.standstill:
|
if v_ego > 0. or output_gb > -brake_stopping_target:
|
||||||
output_gb -= stopping_brake_rate/rate
|
output_gb -= stopping_brake_rate/rate
|
||||||
output_gb = np.clip(output_gb, -brake_max, gas_max)
|
output_gb = np.clip(output_gb, -brake_max, gas_max)
|
||||||
self.v_pid = CS.v_ego
|
self.v_pid = v_ego
|
||||||
self.Ui_accel_cmd = 0.
|
self.Ui_accel_cmd = 0.
|
||||||
# intention is to move again, release brake fast before handling control to PID
|
# intention is to move again, release brake fast before handling control to PID
|
||||||
elif self.long_control_state == LongCtrlState.starting:
|
elif self.long_control_state == LongCtrlState.starting:
|
||||||
if output_gb < -0.2:
|
if output_gb < -0.2:
|
||||||
output_gb += starting_brake_rate/rate
|
output_gb += starting_brake_rate/rate
|
||||||
self.v_pid = CS.v_ego
|
self.v_pid = v_ego
|
||||||
self.Ui_accel_cmd = starting_Ui
|
self.Ui_accel_cmd = starting_Ui
|
||||||
|
|
||||||
self.last_output_gb = output_gb
|
self.last_output_gb = output_gb
|
||||||
final_gas = np.clip(output_gb, 0., gas_max)
|
final_gas = np.clip(output_gb, 0., gas_max)
|
||||||
final_brake = -np.clip(output_gb, -brake_max, 0.)
|
final_brake = -np.clip(output_gb, -brake_max, 0.)
|
||||||
return final_gas, final_brake
|
return final_gas, final_brake
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
|
import os
|
||||||
import zmq
|
import zmq
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import numpy.matlib
|
import numpy.matlib
|
||||||
@@ -9,7 +10,6 @@ from fastcluster import linkage_vector
|
|||||||
import selfdrive.messaging as messaging
|
import selfdrive.messaging as messaging
|
||||||
from selfdrive.boardd.boardd import can_capnp_to_can_list_old
|
from selfdrive.boardd.boardd import can_capnp_to_can_list_old
|
||||||
from selfdrive.controls.lib.latcontrol import calc_lookahead_offset
|
from selfdrive.controls.lib.latcontrol import calc_lookahead_offset
|
||||||
from selfdrive.controls.lib.can_parser import CANParser
|
|
||||||
from selfdrive.controls.lib.pathplanner import PathPlanner
|
from selfdrive.controls.lib.pathplanner import PathPlanner
|
||||||
from selfdrive.config import VehicleParams
|
from selfdrive.config import VehicleParams
|
||||||
from selfdrive.controls.lib.radar_helpers import Track, Cluster, fcluster, RDR_TO_LDR
|
from selfdrive.controls.lib.radar_helpers import Track, Cluster, fcluster, RDR_TO_LDR
|
||||||
@@ -18,10 +18,16 @@ from common.services import service_list
|
|||||||
from common.realtime import sec_since_boot, set_realtime_priority, Ratekeeper
|
from common.realtime import sec_since_boot, set_realtime_priority, Ratekeeper
|
||||||
from common.kalman.ekf import EKF, SimpleSensor
|
from common.kalman.ekf import EKF, SimpleSensor
|
||||||
|
|
||||||
|
radar_type = os.getenv("RADAR")
|
||||||
|
if radar_type is not None:
|
||||||
|
exec('from selfdrive.radar.'+car_type+'.interface import RadarInterface')
|
||||||
|
else:
|
||||||
|
from selfdrive.radar.nidec.interface import RadarInterface
|
||||||
|
|
||||||
#vision point
|
#vision point
|
||||||
DIMSV = 2
|
DIMSV = 2
|
||||||
XV, SPEEDV = 0, 1
|
XV, SPEEDV = 0, 1
|
||||||
VISION_POINT = 1
|
VISION_POINT = -1
|
||||||
|
|
||||||
class EKFV1D(EKF):
|
class EKFV1D(EKF):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
@@ -41,30 +47,6 @@ class EKFV1D(EKF):
|
|||||||
return tf, tfj
|
return tf, tfj
|
||||||
|
|
||||||
|
|
||||||
# nidec radar decoding
|
|
||||||
def nidec_decode(cp, ar_pts):
|
|
||||||
for ii in cp.msgs_upd:
|
|
||||||
# filter points with very big distance, as fff (~255) is invalid. FIXME: use VAL tables from dbc
|
|
||||||
if cp.vl[ii]['LONG_DIST'] < 255:
|
|
||||||
ar_pts[ii] = [cp.vl[ii]['LONG_DIST'] + RDR_TO_LDR,
|
|
||||||
-cp.vl[ii]['LAT_DIST'], cp.vl[ii]['REL_SPEED'], np.nan,
|
|
||||||
cp.ts[ii], cp.vl[ii]['NEW_TRACK'], cp.ct[ii]]
|
|
||||||
elif ii in ar_pts:
|
|
||||||
del ar_pts[ii]
|
|
||||||
return ar_pts
|
|
||||||
|
|
||||||
|
|
||||||
def _create_radard_can_parser():
|
|
||||||
dbc_f = 'acura_ilx_2016_nidec.dbc'
|
|
||||||
radar_messages = range(0x430, 0x43A) + range(0x440, 0x446)
|
|
||||||
signals = zip(['LONG_DIST'] * 16 + ['NEW_TRACK'] * 16 + ['LAT_DIST'] * 16 +
|
|
||||||
['REL_SPEED'] * 16, radar_messages * 4,
|
|
||||||
[255] * 16 + [1] * 16 + [0] * 16 + [0] * 16)
|
|
||||||
checks = zip(radar_messages, [20]*16)
|
|
||||||
|
|
||||||
return CANParser(dbc_f, signals, checks)
|
|
||||||
|
|
||||||
|
|
||||||
# fuses camera and radar data for best lead detection
|
# fuses camera and radar data for best lead detection
|
||||||
def radard_thread(gctx=None):
|
def radard_thread(gctx=None):
|
||||||
set_realtime_priority(1)
|
set_realtime_priority(1)
|
||||||
@@ -73,10 +55,10 @@ def radard_thread(gctx=None):
|
|||||||
|
|
||||||
# *** subscribe to features and model from visiond
|
# *** subscribe to features and model from visiond
|
||||||
model = messaging.sub_sock(context, service_list['model'].port)
|
model = messaging.sub_sock(context, service_list['model'].port)
|
||||||
logcan = messaging.sub_sock(context, service_list['can'].port)
|
|
||||||
live100 = messaging.sub_sock(context, service_list['live100'].port)
|
live100 = messaging.sub_sock(context, service_list['live100'].port)
|
||||||
|
|
||||||
PP = PathPlanner(model)
|
PP = PathPlanner(model)
|
||||||
|
RI = RadarInterface()
|
||||||
|
|
||||||
# *** publish live20 and liveTracks
|
# *** publish live20 and liveTracks
|
||||||
live20 = messaging.pub_sock(context, service_list['live20'].port)
|
live20 = messaging.pub_sock(context, service_list['live20'].port)
|
||||||
@@ -84,9 +66,8 @@ def radard_thread(gctx=None):
|
|||||||
|
|
||||||
# subscribe to stats about the car
|
# subscribe to stats about the car
|
||||||
# TODO: move this to new style packet
|
# TODO: move this to new style packet
|
||||||
VP = VehicleParams(False) # same for ILX and civic
|
VP = VehicleParams(False, False) # same for ILX and civic
|
||||||
|
|
||||||
ar_pts = {}
|
|
||||||
path_x = np.arange(0.0, 140.0, 0.1) # 140 meters is max
|
path_x = np.arange(0.0, 140.0, 0.1) # 140 meters is max
|
||||||
|
|
||||||
# Time-alignment
|
# Time-alignment
|
||||||
@@ -100,9 +81,6 @@ def radard_thread(gctx=None):
|
|||||||
|
|
||||||
tracks = defaultdict(dict)
|
tracks = defaultdict(dict)
|
||||||
|
|
||||||
# Nidec
|
|
||||||
cp = _create_radard_can_parser()
|
|
||||||
|
|
||||||
# Kalman filter stuff:
|
# Kalman filter stuff:
|
||||||
ekfv = EKFV1D()
|
ekfv = EKFV1D()
|
||||||
speedSensorV = SimpleSensor(XV, 1, 2)
|
speedSensorV = SimpleSensor(XV, 1, 2)
|
||||||
@@ -114,23 +92,11 @@ def radard_thread(gctx=None):
|
|||||||
|
|
||||||
rk = Ratekeeper(rate, print_delay_threshold=np.inf)
|
rk = Ratekeeper(rate, print_delay_threshold=np.inf)
|
||||||
while 1:
|
while 1:
|
||||||
canMonoTimes = []
|
rr = RI.update()
|
||||||
can_pub_radar = []
|
|
||||||
for a in messaging.drain_sock(logcan, wait_for_one=True):
|
|
||||||
canMonoTimes.append(a.logMonoTime)
|
|
||||||
can_pub_radar.extend(can_capnp_to_can_list_old(a, [1, 3]))
|
|
||||||
|
|
||||||
# only run on the 0x445 packets, used for timing
|
ar_pts = {}
|
||||||
if not any(x[0] == 0x445 for x in can_pub_radar):
|
for pt in rr.points:
|
||||||
continue
|
ar_pts[pt.trackId] = [pt.dRel + RDR_TO_LDR, pt.yRel, pt.vRel, pt.aRel, None, False, None]
|
||||||
|
|
||||||
cp.update_can(can_pub_radar)
|
|
||||||
|
|
||||||
if not cp.can_valid:
|
|
||||||
# TODO: handle this
|
|
||||||
pass
|
|
||||||
|
|
||||||
ar_pts = nidec_decode(cp, ar_pts)
|
|
||||||
|
|
||||||
# receive the live100s
|
# receive the live100s
|
||||||
l100 = messaging.recv_sock(live100)
|
l100 = messaging.recv_sock(live100)
|
||||||
@@ -184,7 +150,7 @@ def radard_thread(gctx=None):
|
|||||||
v_ego_t_aligned = np.interp(cur_time - rdr_delay, v_ego_array[1], v_ego_array[0])
|
v_ego_t_aligned = np.interp(cur_time - rdr_delay, v_ego_array[1], v_ego_array[0])
|
||||||
d_path = np.sqrt(np.amin((path_x - rpt[0]) ** 2 + (path_y - rpt[1]) ** 2))
|
d_path = np.sqrt(np.amin((path_x - rpt[0]) ** 2 + (path_y - rpt[1]) ** 2))
|
||||||
|
|
||||||
# create the track
|
# create the track if it doesn't exist or it's a new track
|
||||||
if ids not in tracks or rpt[5] == 1:
|
if ids not in tracks or rpt[5] == 1:
|
||||||
tracks[ids] = Track()
|
tracks[ids] = Track()
|
||||||
tracks[ids].update(rpt[0], rpt[1], rpt[2], d_path, v_ego_t_aligned)
|
tracks[ids].update(rpt[0], rpt[1], rpt[2], d_path, v_ego_t_aligned)
|
||||||
@@ -246,7 +212,7 @@ def radard_thread(gctx=None):
|
|||||||
dat = messaging.new_message()
|
dat = messaging.new_message()
|
||||||
dat.init('live20')
|
dat.init('live20')
|
||||||
dat.live20.mdMonoTime = PP.logMonoTime
|
dat.live20.mdMonoTime = PP.logMonoTime
|
||||||
dat.live20.canMonoTimes = canMonoTimes
|
dat.live20.canMonoTimes = list(rr.canMonoTimes)
|
||||||
if lead_len > 0:
|
if lead_len > 0:
|
||||||
lead_clusters[0].toLive20(dat.live20.leadOne)
|
lead_clusters[0].toLive20(dat.live20.leadOne)
|
||||||
if lead2_len > 0:
|
if lead2_len > 0:
|
||||||
|
|||||||
@@ -84,6 +84,8 @@ class Uploader(object):
|
|||||||
cloudlog.exception("clean_dirs failed")
|
cloudlog.exception("clean_dirs failed")
|
||||||
|
|
||||||
def gen_upload_files(self):
|
def gen_upload_files(self):
|
||||||
|
if not os.path.isdir(self.root):
|
||||||
|
return
|
||||||
for logname in listdir_by_creation_date(self.root):
|
for logname in listdir_by_creation_date(self.root):
|
||||||
path = os.path.join(self.root, logname)
|
path = os.path.join(self.root, logname)
|
||||||
names = os.listdir(path)
|
names = os.listdir(path)
|
||||||
|
|||||||
+42
-17
@@ -29,7 +29,6 @@ managed_processes = {
|
|||||||
"calibrationd": "selfdrive.calibrationd.calibrationd",
|
"calibrationd": "selfdrive.calibrationd.calibrationd",
|
||||||
"loggerd": "selfdrive.loggerd.loggerd",
|
"loggerd": "selfdrive.loggerd.loggerd",
|
||||||
"logmessaged": "selfdrive.logmessaged",
|
"logmessaged": "selfdrive.logmessaged",
|
||||||
#"boardd": "selfdrive.boardd.boardd",
|
|
||||||
"logcatd": ("logcatd", ["./logcatd"]),
|
"logcatd": ("logcatd", ["./logcatd"]),
|
||||||
"boardd": ("boardd", ["./boardd"]), # switch to c++ boardd
|
"boardd": ("boardd", ["./boardd"]), # switch to c++ boardd
|
||||||
"ui": ("ui", ["./ui"]),
|
"ui": ("ui", ["./ui"]),
|
||||||
@@ -38,7 +37,13 @@ managed_processes = {
|
|||||||
|
|
||||||
running = {}
|
running = {}
|
||||||
|
|
||||||
car_started_processes = ['controlsd', 'loggerd', 'visiond', 'sensord', 'radard', 'calibrationd']
|
# due to qualcomm kernel bugs SIGKILLing visiond sometimes causes page table corruption
|
||||||
|
unkillable_processes = ['visiond']
|
||||||
|
|
||||||
|
# processes to end with SIGINT instead of SIGTERM
|
||||||
|
interrupt_processes = ['loggerd']
|
||||||
|
|
||||||
|
car_started_processes = ['controlsd', 'loggerd', 'sensord', 'radard', 'calibrationd', 'visiond']
|
||||||
|
|
||||||
|
|
||||||
# ****************** process management functions ******************
|
# ****************** process management functions ******************
|
||||||
@@ -91,15 +96,30 @@ def kill_managed_process(name):
|
|||||||
if name not in running or name not in managed_processes:
|
if name not in running or name not in managed_processes:
|
||||||
return
|
return
|
||||||
cloudlog.info("killing %s" % name)
|
cloudlog.info("killing %s" % name)
|
||||||
running[name].terminate()
|
|
||||||
|
if name in interrupt_processes:
|
||||||
|
os.kill(running[name].pid, signal.SIGINT)
|
||||||
|
else:
|
||||||
|
running[name].terminate()
|
||||||
|
|
||||||
|
|
||||||
|
# give it 5 seconds to die
|
||||||
running[name].join(5.0)
|
running[name].join(5.0)
|
||||||
if running[name].exitcode is None:
|
if running[name].exitcode is None:
|
||||||
cloudlog.info("killing %s with SIGKILL" % name)
|
if name in unkillable_processes:
|
||||||
os.kill(running[name].pid, signal.SIGKILL)
|
cloudlog.critical("unkillable process %s failed to exit! rebooting in 15 if it doesn't die" % name)
|
||||||
running[name].join()
|
running[name].join(15.0)
|
||||||
cloudlog.info("%s is finally dead" % name)
|
if running[name].exitcode is None:
|
||||||
else:
|
cloudlog.critical("FORCE REBOOTING PHONE!")
|
||||||
cloudlog.info("%s is dead with %d" % (name, running[name].exitcode))
|
os.system("date > /sdcard/unkillable_reboot")
|
||||||
|
os.system("reboot")
|
||||||
|
raise RuntimeError
|
||||||
|
else:
|
||||||
|
cloudlog.info("killing %s with SIGKILL" % name)
|
||||||
|
os.kill(running[name].pid, signal.SIGKILL)
|
||||||
|
running[name].join()
|
||||||
|
|
||||||
|
cloudlog.info("%s is dead with %d" % (name, running[name].exitcode))
|
||||||
del running[name]
|
del running[name]
|
||||||
|
|
||||||
def cleanup_all_processes(signal, frame):
|
def cleanup_all_processes(signal, frame):
|
||||||
@@ -125,6 +145,7 @@ def manager_init():
|
|||||||
os.environ['DONGLE_SECRET'] = dongle_secret
|
os.environ['DONGLE_SECRET'] = dongle_secret
|
||||||
|
|
||||||
cloudlog.bind_global(dongle_id=dongle_id)
|
cloudlog.bind_global(dongle_id=dongle_id)
|
||||||
|
common.crash.bind_user(dongle_id=dongle_id)
|
||||||
|
|
||||||
# set gctx
|
# set gctx
|
||||||
gctx = {
|
gctx = {
|
||||||
@@ -152,8 +173,9 @@ def manager_thread():
|
|||||||
start_managed_process("uploader")
|
start_managed_process("uploader")
|
||||||
start_managed_process("ui")
|
start_managed_process("ui")
|
||||||
|
|
||||||
# *** wait for the board ***
|
if os.getenv("NOBOARD") is None:
|
||||||
wait_for_device()
|
# *** wait for the board ***
|
||||||
|
wait_for_device()
|
||||||
|
|
||||||
# flash the device
|
# flash the device
|
||||||
if os.getenv("NOPROG") is None:
|
if os.getenv("NOPROG") is None:
|
||||||
@@ -188,12 +210,13 @@ def manager_thread():
|
|||||||
start_managed_process("uploader")
|
start_managed_process("uploader")
|
||||||
|
|
||||||
# start constellation of processes when the car starts
|
# start constellation of processes when the car starts
|
||||||
if td.health.started:
|
if not os.getenv("STARTALL"):
|
||||||
for p in car_started_processes:
|
if td.health.started:
|
||||||
start_managed_process(p)
|
for p in car_started_processes:
|
||||||
else:
|
start_managed_process(p)
|
||||||
for p in car_started_processes:
|
else:
|
||||||
kill_managed_process(p)
|
for p in car_started_processes:
|
||||||
|
kill_managed_process(p)
|
||||||
|
|
||||||
# check the status of all processes, did any of them die?
|
# check the status of all processes, did any of them die?
|
||||||
for p in running:
|
for p in running:
|
||||||
@@ -257,6 +280,8 @@ def wait_for_device():
|
|||||||
def main():
|
def main():
|
||||||
if os.getenv("NOLOG") is not None:
|
if os.getenv("NOLOG") is not None:
|
||||||
del managed_processes['loggerd']
|
del managed_processes['loggerd']
|
||||||
|
if os.getenv("NOUPLOAD") is not None:
|
||||||
|
del managed_processes['uploader']
|
||||||
if os.getenv("NOBOARD") is not None:
|
if os.getenv("NOBOARD") is not None:
|
||||||
del managed_processes['boardd']
|
del managed_processes['boardd']
|
||||||
|
|
||||||
|
|||||||
Executable
+83
@@ -0,0 +1,83 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
import numpy as np
|
||||||
|
from selfdrive.car.honda.can_parser import CANParser
|
||||||
|
from selfdrive.boardd.boardd import can_capnp_to_can_list_old
|
||||||
|
|
||||||
|
from cereal import car
|
||||||
|
|
||||||
|
import zmq
|
||||||
|
from common.services import service_list
|
||||||
|
import selfdrive.messaging as messaging
|
||||||
|
|
||||||
|
def _create_radard_can_parser():
|
||||||
|
dbc_f = 'acura_ilx_2016_nidec.dbc'
|
||||||
|
radar_messages = range(0x430, 0x43A) + range(0x440, 0x446)
|
||||||
|
signals = zip(['LONG_DIST'] * 16 + ['NEW_TRACK'] * 16 + ['LAT_DIST'] * 16 +
|
||||||
|
['REL_SPEED'] * 16, radar_messages * 4,
|
||||||
|
[255] * 16 + [1] * 16 + [0] * 16 + [0] * 16)
|
||||||
|
checks = zip(radar_messages, [20]*16)
|
||||||
|
|
||||||
|
return CANParser(dbc_f, signals, checks)
|
||||||
|
|
||||||
|
class RadarInterface(object):
|
||||||
|
def __init__(self):
|
||||||
|
# radar
|
||||||
|
self.pts = {}
|
||||||
|
self.track_id = 0
|
||||||
|
|
||||||
|
# Nidec
|
||||||
|
self.rcp = _create_radard_can_parser()
|
||||||
|
|
||||||
|
context = zmq.Context()
|
||||||
|
self.logcan = messaging.sub_sock(context, service_list['can'].port)
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
canMonoTimes = []
|
||||||
|
can_pub_radar = []
|
||||||
|
|
||||||
|
# TODO: can hang if no packets show up
|
||||||
|
while 1:
|
||||||
|
for a in messaging.drain_sock(self.logcan, wait_for_one=True):
|
||||||
|
canMonoTimes.append(a.logMonoTime)
|
||||||
|
can_pub_radar.extend(can_capnp_to_can_list_old(a.can, [1, 3]))
|
||||||
|
|
||||||
|
# only run on the 0x445 packets, used for timing
|
||||||
|
if any(x[0] == 0x445 for x in can_pub_radar):
|
||||||
|
break
|
||||||
|
|
||||||
|
self.rcp.update_can(can_pub_radar)
|
||||||
|
|
||||||
|
ret = car.RadarState.new_message()
|
||||||
|
errors = []
|
||||||
|
if not self.rcp.can_valid:
|
||||||
|
errors.append("notValid")
|
||||||
|
ret.errors = errors
|
||||||
|
ret.canMonoTimes = canMonoTimes
|
||||||
|
|
||||||
|
for ii in self.rcp.msgs_upd:
|
||||||
|
cpt = self.rcp.vl[ii]
|
||||||
|
if cpt['LONG_DIST'] < 255:
|
||||||
|
if ii not in self.pts or cpt['NEW_TRACK']:
|
||||||
|
self.pts[ii] = car.RadarState.RadarPoint.new_message()
|
||||||
|
self.pts[ii].trackId = self.track_id
|
||||||
|
self.track_id += 1
|
||||||
|
self.pts[ii].dRel = cpt['LONG_DIST'] # from front of car
|
||||||
|
self.pts[ii].yRel = -cpt['LAT_DIST'] # in car frame's y axis, left is positive
|
||||||
|
self.pts[ii].vRel = cpt['REL_SPEED']
|
||||||
|
self.pts[ii].aRel = float('nan')
|
||||||
|
self.pts[ii].yvRel = float('nan')
|
||||||
|
else:
|
||||||
|
if ii in self.pts:
|
||||||
|
del self.pts[ii]
|
||||||
|
|
||||||
|
ret.points = self.pts.values()
|
||||||
|
return ret
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
RI = RadarInterface()
|
||||||
|
while 1:
|
||||||
|
ret = RI.update()
|
||||||
|
print(chr(27) + "[2J")
|
||||||
|
print ret
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
import os
|
||||||
|
from enum import Enum
|
||||||
|
from maneuverplots import ManeuverPlot
|
||||||
|
from plant import Plant
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
|
||||||
|
class Maneuver(object):
|
||||||
|
def __init__(self, title, duration, **kwargs):
|
||||||
|
# Was tempted to make a builder class
|
||||||
|
self.distance_lead = kwargs.get("initial_distance_lead", 200.0)
|
||||||
|
self.speed = kwargs.get("initial_speed", 0.0)
|
||||||
|
self.lead_relevancy = kwargs.get("lead_relevancy", 0)
|
||||||
|
|
||||||
|
self.grade_values = kwargs.get("grade_values", [0.0, 0.0])
|
||||||
|
self.grade_breakpoints = kwargs.get("grade_breakpoints", [0.0, duration])
|
||||||
|
self.speed_lead_values = kwargs.get("speed_lead_values", [0.0, 0.0])
|
||||||
|
self.speed_lead_breakpoints = kwargs.get("speed_lead_values", [0.0, duration])
|
||||||
|
|
||||||
|
self.cruise_button_presses = kwargs.get("cruise_button_presses", [])
|
||||||
|
|
||||||
|
self.duration = duration
|
||||||
|
self.title = title
|
||||||
|
|
||||||
|
def evaluate(self):
|
||||||
|
"""runs the plant sim and returns (score, run_data)"""
|
||||||
|
plant = Plant(
|
||||||
|
lead_relevancy = self.lead_relevancy,
|
||||||
|
speed = self.speed,
|
||||||
|
distance_lead = self.distance_lead
|
||||||
|
)
|
||||||
|
|
||||||
|
last_live100 = None
|
||||||
|
event_queue = sorted(self.cruise_button_presses, key=lambda a: a[1])[::-1]
|
||||||
|
plot = ManeuverPlot(self.title)
|
||||||
|
|
||||||
|
buttons_sorted = sorted(self.cruise_button_presses, key=lambda a: a[1])
|
||||||
|
current_button = 0
|
||||||
|
while plant.current_time() < self.duration:
|
||||||
|
while buttons_sorted and plant.current_time() >= buttons_sorted[0][1]:
|
||||||
|
current_button = buttons_sorted[0][0]
|
||||||
|
buttons_sorted = buttons_sorted[1:]
|
||||||
|
print "current button changed to", current_button
|
||||||
|
|
||||||
|
grade = np.interp(plant.current_time(), self.grade_breakpoints, self.grade_values)
|
||||||
|
speed_lead = np.interp(plant.current_time(), self.speed_lead_breakpoints, self.speed_lead_values)
|
||||||
|
|
||||||
|
distance, speed, acceleration, distance_lead, brake, gas, steer_torque, live100 = plant.step(speed_lead, current_button, grade)
|
||||||
|
if live100:
|
||||||
|
last_live100 = live100[-1]
|
||||||
|
|
||||||
|
d_rel = distance_lead - distance if self.lead_relevancy else 200.
|
||||||
|
v_rel = speed_lead - speed if self.lead_relevancy else 0.
|
||||||
|
|
||||||
|
if last_live100:
|
||||||
|
# print last_live100
|
||||||
|
#develop plots
|
||||||
|
plot.add_data(
|
||||||
|
time=plant.current_time(),
|
||||||
|
gas=gas, brake=brake, steer_torque=steer_torque,
|
||||||
|
distance=distance, speed=speed, acceleration=acceleration,
|
||||||
|
up_accel_cmd=last_live100.upAccelCmd, ui_accel_cmd=last_live100.uiAccelCmd,
|
||||||
|
d_rel=d_rel, v_rel=v_rel, v_lead=speed_lead,
|
||||||
|
v_target_lead=last_live100.vTargetLead, pid_speed=last_live100.vPid,
|
||||||
|
cruise_speed=last_live100.vCruise,
|
||||||
|
jerk_factor=last_live100.jerkFactor,
|
||||||
|
a_target_min=last_live100.aTargetMin, a_target_max=last_live100.aTargetMax)
|
||||||
|
|
||||||
|
return (None, plot)
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,139 @@
|
|||||||
|
import os
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
import pylab
|
||||||
|
|
||||||
|
from selfdrive.config import Conversions as CV
|
||||||
|
|
||||||
|
class ManeuverPlot(object):
|
||||||
|
def __init__(self, title = None):
|
||||||
|
self.time_array = []
|
||||||
|
|
||||||
|
self.gas_array = []
|
||||||
|
self.brake_array = []
|
||||||
|
self.steer_torque_array = []
|
||||||
|
|
||||||
|
self.distance_array = []
|
||||||
|
self.speed_array = []
|
||||||
|
self.acceleration_array = []
|
||||||
|
|
||||||
|
self.up_accel_cmd_array = []
|
||||||
|
self.ui_accel_cmd_array = []
|
||||||
|
|
||||||
|
self.d_rel_array = []
|
||||||
|
self.v_rel_array = []
|
||||||
|
self.v_lead_array = []
|
||||||
|
self.v_target_lead_array = []
|
||||||
|
self.pid_speed_array = []
|
||||||
|
self.cruise_speed_array = []
|
||||||
|
self.jerk_factor_array = []
|
||||||
|
|
||||||
|
self.a_target_min_array = []
|
||||||
|
self.a_target_max_array = []
|
||||||
|
|
||||||
|
self.v_target_array = []
|
||||||
|
|
||||||
|
self.title = title
|
||||||
|
|
||||||
|
def add_data(self, time, gas, brake, steer_torque, distance, speed,
|
||||||
|
acceleration, up_accel_cmd, ui_accel_cmd, d_rel, v_rel, v_lead,
|
||||||
|
v_target_lead, pid_speed, cruise_speed, jerk_factor, a_target_min,
|
||||||
|
a_target_max):
|
||||||
|
self.time_array.append(time)
|
||||||
|
self.gas_array.append(gas)
|
||||||
|
self.brake_array.append(brake)
|
||||||
|
self.steer_torque_array.append(steer_torque)
|
||||||
|
self.distance_array.append(distance)
|
||||||
|
self.speed_array.append(speed)
|
||||||
|
self.acceleration_array.append(acceleration)
|
||||||
|
self.up_accel_cmd_array.append(up_accel_cmd)
|
||||||
|
self.ui_accel_cmd_array.append(ui_accel_cmd)
|
||||||
|
self.d_rel_array.append(d_rel)
|
||||||
|
self.v_rel_array.append(v_rel)
|
||||||
|
self.v_lead_array.append(v_lead)
|
||||||
|
self.v_target_lead_array.append(v_target_lead)
|
||||||
|
self.pid_speed_array.append(pid_speed)
|
||||||
|
self.cruise_speed_array.append(cruise_speed)
|
||||||
|
self.jerk_factor_array.append(jerk_factor)
|
||||||
|
self.a_target_min_array.append(a_target_min)
|
||||||
|
self.a_target_max_array.append(a_target_max)
|
||||||
|
|
||||||
|
def write_plot(self, path, maneuver_name):
|
||||||
|
title = self.title or maneuver_name
|
||||||
|
# TODO: Missing plots from the old one:
|
||||||
|
# long_control_state
|
||||||
|
# proportional_gb, intergral_gb
|
||||||
|
if not os.path.exists(path + "/" + maneuver_name):
|
||||||
|
os.makedirs(path + "/" + maneuver_name)
|
||||||
|
plt_num = 0
|
||||||
|
|
||||||
|
# speed chart ===================
|
||||||
|
plt_num += 1
|
||||||
|
plt.figure(plt_num)
|
||||||
|
plt.plot(
|
||||||
|
np.array(self.time_array), np.array(self.speed_array) * CV.MS_TO_MPH, 'r',
|
||||||
|
np.array(self.time_array), np.array(self.pid_speed_array) * CV.MS_TO_MPH, 'y--',
|
||||||
|
np.array(self.time_array), np.array(self.v_target_lead_array) * CV.MS_TO_MPH, 'b',
|
||||||
|
np.array(self.time_array), np.array(self.cruise_speed_array) * CV.KPH_TO_MPH, 'k',
|
||||||
|
np.array(self.time_array), np.array(self.v_lead_array) * CV.MS_TO_MPH, 'm'
|
||||||
|
)
|
||||||
|
plt.xlabel('Time [s]')
|
||||||
|
plt.ylabel('Speed [mph]')
|
||||||
|
plt.legend(['speed', 'pid speed', 'Target (lead) speed', 'Cruise speed', 'Lead speed'], loc=0)
|
||||||
|
plt.grid()
|
||||||
|
pylab.savefig("/".join([path, maneuver_name, 'speeds.svg']), dpi=1000)
|
||||||
|
|
||||||
|
# acceleration chart ============
|
||||||
|
plt_num += 1
|
||||||
|
plt.figure(plt_num)
|
||||||
|
plt.plot(
|
||||||
|
np.array(self.time_array), np.array(self.acceleration_array), 'g',
|
||||||
|
np.array(self.time_array), np.array(self.a_target_min_array), 'k--',
|
||||||
|
np.array(self.time_array), np.array(self.a_target_max_array), 'k--',
|
||||||
|
)
|
||||||
|
plt.xlabel('Time [s]')
|
||||||
|
plt.ylabel('Acceleration [m/s^2]')
|
||||||
|
plt.legend(['accel', 'max', 'min'], loc=0)
|
||||||
|
plt.grid()
|
||||||
|
pylab.savefig("/".join([path, maneuver_name, 'acceleration.svg']), dpi=1000)
|
||||||
|
|
||||||
|
# pedal chart ===================
|
||||||
|
plt_num += 1
|
||||||
|
plt.figure(plt_num)
|
||||||
|
plt.plot(
|
||||||
|
np.array(self.time_array), np.array(self.gas_array), 'g',
|
||||||
|
np.array(self.time_array), np.array(self.brake_array), 'r',
|
||||||
|
)
|
||||||
|
plt.xlabel('Time [s]')
|
||||||
|
plt.ylabel('Pedal []')
|
||||||
|
plt.legend(['Gas pedal', 'Brake pedal'], loc=0)
|
||||||
|
plt.grid()
|
||||||
|
pylab.savefig("/".join([path, maneuver_name, 'pedals.svg']), dpi=1000)
|
||||||
|
|
||||||
|
# pid chart ======================
|
||||||
|
plt_num += 1
|
||||||
|
plt.figure(plt_num)
|
||||||
|
plt.plot(
|
||||||
|
np.array(self.time_array), np.array(self.up_accel_cmd_array), 'g',
|
||||||
|
np.array(self.time_array), np.array(self.ui_accel_cmd_array), 'b'
|
||||||
|
)
|
||||||
|
plt.xlabel("Time, [s]")
|
||||||
|
plt.ylabel("Accel Cmd [m/s^2]")
|
||||||
|
plt.grid()
|
||||||
|
plt.legend(["Proportional", "Integral"], loc=0)
|
||||||
|
pylab.savefig("/".join([path, maneuver_name, "pid.svg"]), dpi=1000)
|
||||||
|
|
||||||
|
# relative distances chart =======
|
||||||
|
plt_num += 1
|
||||||
|
plt.figure(plt_num)
|
||||||
|
plt.plot(
|
||||||
|
np.array(self.time_array), np.array(self.d_rel_array), 'g',
|
||||||
|
)
|
||||||
|
plt.xlabel('Time [s]')
|
||||||
|
plt.ylabel('Relative Distance [m]')
|
||||||
|
plt.grid()
|
||||||
|
pylab.savefig("/".join([path, maneuver_name, 'distance.svg']), dpi=1000)
|
||||||
|
|
||||||
|
plt.close("all")
|
||||||
|
|
||||||
Executable
+275
@@ -0,0 +1,275 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
import os
|
||||||
|
import struct
|
||||||
|
|
||||||
|
import zmq
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
from dbcs import DBC_PATH
|
||||||
|
|
||||||
|
from common.realtime import Ratekeeper
|
||||||
|
from common.services import service_list
|
||||||
|
|
||||||
|
import selfdrive.messaging as messaging
|
||||||
|
from selfdrive.config import CruiseButtons
|
||||||
|
from selfdrive.car.honda.hondacan import fix
|
||||||
|
from selfdrive.car.honda.carstate import get_can_parser
|
||||||
|
from selfdrive.boardd.boardd import can_capnp_to_can_list_old, can_capnp_to_can_list, can_list_to_can_capnp
|
||||||
|
|
||||||
|
from selfdrive.car.honda.can_parser import CANParser
|
||||||
|
|
||||||
|
|
||||||
|
from common.dbc import dbc
|
||||||
|
acura = dbc(os.path.join(DBC_PATH, "acura_ilx_2016_can.dbc"))
|
||||||
|
|
||||||
|
def car_plant(pos, speed, grade, gas, brake):
|
||||||
|
# vehicle parameters
|
||||||
|
mass = 1700
|
||||||
|
aero_cd = 0.3
|
||||||
|
force_peak = mass*3.
|
||||||
|
force_brake_peak = -mass*10. #1g
|
||||||
|
power_peak = 100000 # 100kW
|
||||||
|
speed_base = power_peak/force_peak
|
||||||
|
rolling_res = 0.01
|
||||||
|
g = 9.81
|
||||||
|
wheel_r = 0.31
|
||||||
|
frontal_area = 2.2
|
||||||
|
air_density = 1.225
|
||||||
|
gas_to_peak_linear_slope = 3.33
|
||||||
|
brake_to_peak_linear_slope = 0.2
|
||||||
|
creep_accel_v = [1., 0.]
|
||||||
|
creep_accel_bp = [0., 1.5]
|
||||||
|
|
||||||
|
#*** longitudinal model ***
|
||||||
|
# find speed where peak torque meets peak power
|
||||||
|
force_brake = brake * force_brake_peak * brake_to_peak_linear_slope
|
||||||
|
if speed < speed_base: # torque control
|
||||||
|
force_gas = gas * force_peak * gas_to_peak_linear_slope
|
||||||
|
else: # power control
|
||||||
|
force_gas = gas * power_peak / speed * gas_to_peak_linear_slope
|
||||||
|
|
||||||
|
force_grade = - grade * mass # positive grade means uphill
|
||||||
|
|
||||||
|
creep_accel = np.interp(speed, creep_accel_bp, creep_accel_v)
|
||||||
|
force_creep = creep_accel * mass
|
||||||
|
|
||||||
|
force_resistance = -(rolling_res * mass * g + 0.5 * speed**2 * aero_cd * air_density)
|
||||||
|
force = force_gas + force_brake + force_resistance + force_grade + force_creep
|
||||||
|
acceleration = force / mass
|
||||||
|
|
||||||
|
# TODO: lateral model
|
||||||
|
return speed, acceleration
|
||||||
|
|
||||||
|
def get_car_can_parser():
|
||||||
|
dbc_f = 'acura_ilx_2016_can.dbc'
|
||||||
|
signals = [
|
||||||
|
("STEER_TORQUE", 0xe4, 0),
|
||||||
|
("STEER_TORQUE_REQUEST", 0xe4, 0),
|
||||||
|
("COMPUTER_BRAKE", 0x1fa, 0),
|
||||||
|
("COMPUTER_BRAKE_REQUEST", 0x1fa, 0),
|
||||||
|
("GAS_COMMAND", 0x200, 0),
|
||||||
|
]
|
||||||
|
checks = [
|
||||||
|
(0xe4, 100),
|
||||||
|
(0x1fa, 50),
|
||||||
|
(0x200, 50),
|
||||||
|
]
|
||||||
|
return CANParser(dbc_f, signals, checks)
|
||||||
|
|
||||||
|
|
||||||
|
class Plant(object):
|
||||||
|
messaging_initialized = False
|
||||||
|
|
||||||
|
def __init__(self, lead_relevancy=False, rate=100, speed=0.0, distance_lead=2.0):
|
||||||
|
self.rate = rate
|
||||||
|
self.civic = False
|
||||||
|
self.brake_only = False
|
||||||
|
|
||||||
|
if not Plant.messaging_initialized:
|
||||||
|
context = zmq.Context()
|
||||||
|
Plant.logcan = messaging.pub_sock(context, service_list['can'].port)
|
||||||
|
Plant.sendcan = messaging.sub_sock(context, service_list['sendcan'].port)
|
||||||
|
Plant.model = messaging.pub_sock(context, service_list['model'].port)
|
||||||
|
Plant.live100 = messaging.sub_sock(context, service_list['live100'].port)
|
||||||
|
Plant.messaging_initialized = True
|
||||||
|
|
||||||
|
self.angle_steer = 0.
|
||||||
|
self.gear_choice = 0
|
||||||
|
self.speed, self.speed_prev = 0., 0.
|
||||||
|
|
||||||
|
self.esp_disabled = 0
|
||||||
|
self.main_on = 1
|
||||||
|
self.user_gas = 0
|
||||||
|
self.computer_brake,self.user_brake = 0,0
|
||||||
|
self.brake_pressed = 0
|
||||||
|
self.distance, self.distance_prev = 0., 0.
|
||||||
|
self.speed, self.speed_prev = speed, speed
|
||||||
|
self.steer_error, self.brake_error, self.steer_not_allowed = 0, 0, 0
|
||||||
|
self.gear_shifter = 4 # D gear
|
||||||
|
self.pedal_gas = 0
|
||||||
|
self.cruise_setting = 0
|
||||||
|
|
||||||
|
self.seatbelt, self.door_all_closed = True, True
|
||||||
|
self.steer_torque, self.v_cruise, self.acc_status = 0, 0, 0 # v_cruise is reported from can, not the one used for controls
|
||||||
|
|
||||||
|
self.lead_relevancy = lead_relevancy
|
||||||
|
|
||||||
|
# lead car
|
||||||
|
self.distance_lead, self.distance_lead_prev = distance_lead , distance_lead
|
||||||
|
|
||||||
|
self.rk = Ratekeeper(rate, print_delay_threshold=100)
|
||||||
|
self.ts = 1./rate
|
||||||
|
|
||||||
|
self.cp = get_car_can_parser()
|
||||||
|
|
||||||
|
def speed_sensor(self, speed):
|
||||||
|
if speed<0.3:
|
||||||
|
return 0
|
||||||
|
else:
|
||||||
|
return speed
|
||||||
|
|
||||||
|
def current_time(self):
|
||||||
|
return float(self.rk.frame) / self.rate
|
||||||
|
|
||||||
|
def step(self, v_lead=0.0, cruise_buttons=None, grade=0.0):
|
||||||
|
# dbc_f, sgs, ivs, msgs, cks_msgs, frqs = initialize_can_struct(self.civic, self.brake_only)
|
||||||
|
cp2 = get_can_parser(self.civic, self.brake_only)
|
||||||
|
sgs = cp2._sgs
|
||||||
|
msgs = cp2._msgs
|
||||||
|
cks_msgs = cp2.msgs_ck
|
||||||
|
|
||||||
|
# ******** get messages sent to the car ********
|
||||||
|
can_msgs = []
|
||||||
|
for a in messaging.drain_sock(Plant.sendcan):
|
||||||
|
can_msgs += can_capnp_to_can_list_old(a.sendcan, [0,2])
|
||||||
|
#print can_msgs
|
||||||
|
self.cp.update_can(can_msgs)
|
||||||
|
|
||||||
|
# ******** get live100 messages for plotting ***
|
||||||
|
live_msgs = []
|
||||||
|
for a in messaging.drain_sock(Plant.live100):
|
||||||
|
live_msgs.append(a.live100)
|
||||||
|
|
||||||
|
|
||||||
|
if self.cp.vl[0x1fa]['COMPUTER_BRAKE_REQUEST']:
|
||||||
|
brake = self.cp.vl[0x1fa]['COMPUTER_BRAKE']
|
||||||
|
else:
|
||||||
|
brake = 0.0
|
||||||
|
|
||||||
|
if self.cp.vl[0x200]['GAS_COMMAND'] > 0:
|
||||||
|
gas = self.cp.vl[0x200]['GAS_COMMAND'] / 256.0
|
||||||
|
else:
|
||||||
|
gas = 0.0
|
||||||
|
|
||||||
|
if self.cp.vl[0xe4]['STEER_TORQUE_REQUEST']:
|
||||||
|
steer_torque = self.cp.vl[0xe4]['STEER_TORQUE']*1.0/0xf00
|
||||||
|
else:
|
||||||
|
steer_torque = 0.0
|
||||||
|
|
||||||
|
distance_lead = self.distance_lead_prev + v_lead * self.ts
|
||||||
|
|
||||||
|
# ******** run the car ********
|
||||||
|
speed, acceleration = car_plant(self.distance_prev, self.speed_prev, grade, gas, brake)
|
||||||
|
standstill = (speed == 0)
|
||||||
|
distance = self.distance_prev + speed * self.ts
|
||||||
|
speed = self.speed_prev + self.ts * acceleration
|
||||||
|
if speed <= 0:
|
||||||
|
speed = 0
|
||||||
|
acceleration = 0
|
||||||
|
|
||||||
|
# ******** lateral ********
|
||||||
|
self.angle_steer -= steer_torque
|
||||||
|
|
||||||
|
# *** radar model ***
|
||||||
|
if self.lead_relevancy:
|
||||||
|
d_rel = np.maximum(0., self.distance_lead - distance)
|
||||||
|
v_rel = v_lead - speed
|
||||||
|
else:
|
||||||
|
d_rel = 200.
|
||||||
|
v_rel = 0.
|
||||||
|
a_rel = 0
|
||||||
|
lateral_pos_rel = 0.
|
||||||
|
|
||||||
|
# print at 5hz
|
||||||
|
if (self.rk.frame%(self.rate/5)) == 0:
|
||||||
|
print "%6.2f m %6.2f m/s %6.2f m/s2 %.2f ang gas: %.2f brake: %.2f steer: %5.2f lead_rel: %6.2f m %6.2f m/s" % (distance, speed, acceleration, self.angle_steer, gas, brake, steer_torque, d_rel, v_rel)
|
||||||
|
|
||||||
|
# ******** publish the car ********
|
||||||
|
vls = [self.speed_sensor(speed), self.speed_sensor(speed), self.speed_sensor(speed), self.speed_sensor(speed),
|
||||||
|
self.angle_steer, 0, self.gear_choice, speed!=0,
|
||||||
|
0, 0, 0, 0,
|
||||||
|
self.v_cruise, not self.seatbelt, self.seatbelt, self.brake_pressed,
|
||||||
|
self.user_gas, cruise_buttons, self.esp_disabled, 0,
|
||||||
|
self.user_brake, self.steer_error, self.speed_sensor(speed), self.brake_error,
|
||||||
|
self.brake_error, self.gear_shifter, self.main_on, self.acc_status,
|
||||||
|
self.pedal_gas, self.cruise_setting,
|
||||||
|
# left_blinker, right_blinker, counter
|
||||||
|
0,0,0,
|
||||||
|
# interceptor_gas
|
||||||
|
0,0]
|
||||||
|
|
||||||
|
|
||||||
|
# TODO: publish each message at proper frequency
|
||||||
|
can_msgs = []
|
||||||
|
for msg in set(msgs):
|
||||||
|
msg_struct = {}
|
||||||
|
indxs = [i for i, x in enumerate(msgs) if msg == msgs[i]]
|
||||||
|
for i in indxs:
|
||||||
|
msg_struct[sgs[i]] = vls[i]
|
||||||
|
if msg in cks_msgs:
|
||||||
|
msg_struct["COUNTER"] = self.rk.frame % 4
|
||||||
|
|
||||||
|
msg_data = acura.encode(msg, msg_struct)
|
||||||
|
|
||||||
|
if msg in cks_msgs:
|
||||||
|
msg_data = fix(msg_data, msg)
|
||||||
|
|
||||||
|
can_msgs.append([msg, 0, msg_data, 0])
|
||||||
|
|
||||||
|
# add the radar message
|
||||||
|
# TODO: use the DBC
|
||||||
|
def to_3_byte(x):
|
||||||
|
return struct.pack("!H", int(x)).encode("hex")[1:]
|
||||||
|
|
||||||
|
def to_3s_byte(x):
|
||||||
|
return struct.pack("!h", int(x)).encode("hex")[1:]
|
||||||
|
radar_msg = to_3_byte(d_rel*16.0) + \
|
||||||
|
to_3_byte(int(lateral_pos_rel*16.0)&0x3ff) + \
|
||||||
|
to_3s_byte(int(v_rel*32.0)) + \
|
||||||
|
"0f00000"
|
||||||
|
can_msgs.append([0x445, 0, radar_msg.decode("hex"), 1])
|
||||||
|
Plant.logcan.send(can_list_to_can_capnp(can_msgs).to_bytes())
|
||||||
|
|
||||||
|
# ******** publish a fake model going straight ********
|
||||||
|
md = messaging.new_message()
|
||||||
|
md.init('model')
|
||||||
|
md.model.frameId = 0
|
||||||
|
for x in [md.model.path, md.model.leftLane, md.model.rightLane]:
|
||||||
|
x.points = [0.0]*50
|
||||||
|
x.prob = 1.0
|
||||||
|
x.std = 1.0
|
||||||
|
# fake values?
|
||||||
|
Plant.model.send(md.to_bytes())
|
||||||
|
|
||||||
|
|
||||||
|
# ******** update prevs ********
|
||||||
|
self.speed_prev = speed
|
||||||
|
self.distance_prev = distance
|
||||||
|
self.distance_lead_prev = distance_lead
|
||||||
|
|
||||||
|
self.rk.keep_time()
|
||||||
|
return (distance, speed, acceleration, distance_lead, brake, gas, steer_torque, live_msgs)
|
||||||
|
|
||||||
|
# simple engage in standalone mode
|
||||||
|
def plant_thread(rate=100):
|
||||||
|
plant = Plant(rate)
|
||||||
|
while 1:
|
||||||
|
if plant.rk.frame%100 >= 20 and plant.rk.frame%100 <= 25:
|
||||||
|
cruise_buttons = CruiseButtons.RES_ACCEL
|
||||||
|
else:
|
||||||
|
cruise_buttons = 0
|
||||||
|
plant.step()
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
plant_thread()
|
||||||
|
|
||||||
Executable
+10
@@ -0,0 +1,10 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
pushd ../../controls
|
||||||
|
./controlsd.py &
|
||||||
|
pid1=$!
|
||||||
|
./radard.py &
|
||||||
|
pid2=$!
|
||||||
|
trap "trap - SIGTERM && kill $pid1 && kill $pid2" SIGINT SIGTERM EXIT
|
||||||
|
popd
|
||||||
|
mkdir -p out
|
||||||
|
MPLBACKEND=svg ./runtracks.py out
|
||||||
Executable
+207
@@ -0,0 +1,207 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
import sys
|
||||||
|
import time, json
|
||||||
|
|
||||||
|
from selfdrive.test.plant import plant
|
||||||
|
from selfdrive.config import Conversions as CV, CruiseButtons as CB
|
||||||
|
from maneuver import *
|
||||||
|
|
||||||
|
maneuvers = [
|
||||||
|
Maneuver(
|
||||||
|
'while cruising at 40 mph, change cruise speed to 50mph',
|
||||||
|
duration=30.,
|
||||||
|
initial_speed = 40. * CV.MPH_TO_MS,
|
||||||
|
cruise_button_presses = [(CB.DECEL_SET, 2.), (0, 2.3),
|
||||||
|
(CB.RES_ACCEL, 10.), (0, 10.1),
|
||||||
|
(CB.RES_ACCEL, 10.2), (0, 10.3)]
|
||||||
|
),
|
||||||
|
Maneuver(
|
||||||
|
'while cruising at 60 mph, change cruise speed to 50mph',
|
||||||
|
duration=30.,
|
||||||
|
initial_speed=60. * CV.MPH_TO_MS,
|
||||||
|
cruise_button_presses = [(CB.DECEL_SET, 2.), (0, 2.3),
|
||||||
|
(CB.DECEL_SET, 10.), (0, 10.1),
|
||||||
|
(CB.DECEL_SET, 10.2), (0, 10.3)]
|
||||||
|
),
|
||||||
|
Maneuver(
|
||||||
|
'while cruising at 20mph, grade change +10%',
|
||||||
|
duration=25.,
|
||||||
|
initial_speed=20. * CV.MPH_TO_MS,
|
||||||
|
cruise_button_presses = [(CB.DECEL_SET, 1.2), (0, 1.3)],
|
||||||
|
grade_values = [0., 0., 1.0],
|
||||||
|
grade_breakpoints = [0., 10., 11.]
|
||||||
|
),
|
||||||
|
Maneuver(
|
||||||
|
'while cruising at 20mph, grade change -10%',
|
||||||
|
duration=25.,
|
||||||
|
initial_speed=20. * CV.MPH_TO_MS,
|
||||||
|
cruise_button_presses = [(CB.DECEL_SET, 1.2), (0, 1.3)],
|
||||||
|
grade_values = [0., 0., -1.0],
|
||||||
|
grade_breakpoints = [0., 10., 11.]
|
||||||
|
),
|
||||||
|
Maneuver(
|
||||||
|
'approaching a 40mph car while cruising at 60mph from 100m away',
|
||||||
|
duration=30.,
|
||||||
|
initial_speed = 60. * CV.MPH_TO_MS,
|
||||||
|
lead_relevancy=True,
|
||||||
|
initial_distance_lead=100.,
|
||||||
|
speed_lead_values = [40.*CV.MPH_TO_MS, 40.*CV.MPH_TO_MS],
|
||||||
|
speed_lead_breakpoints = [0., 100.],
|
||||||
|
cruise_button_presses = [(CB.DECEL_SET, 1.2), (0, 1.3)]
|
||||||
|
),
|
||||||
|
Maneuver(
|
||||||
|
'approaching a 0mph car while cruising at 40mph from 150m away',
|
||||||
|
duration=30.,
|
||||||
|
initial_speed = 40. * CV.MPH_TO_MS,
|
||||||
|
lead_relevancy=True,
|
||||||
|
initial_distance_lead=150.,
|
||||||
|
speed_lead_values = [0.*CV.MPH_TO_MS, 0.*CV.MPH_TO_MS],
|
||||||
|
speed_lead_breakpoints = [0., 100.],
|
||||||
|
cruise_button_presses = [(CB.DECEL_SET, 1.2), (0, 1.3)]
|
||||||
|
),
|
||||||
|
Maneuver(
|
||||||
|
'steady state following a car at 20m/s, then lead decel to 0mph at 1m/s^2',
|
||||||
|
duration=50.,
|
||||||
|
initial_speed = 20.,
|
||||||
|
lead_relevancy=True,
|
||||||
|
initial_distance_lead=35.,
|
||||||
|
speed_lead_values = [20.*CV.MPH_TO_MS, 20.*CV.MPH_TO_MS, 0.*CV.MPH_TO_MS],
|
||||||
|
speed_lead_breakpoints = [0., 15., 35.0],
|
||||||
|
cruise_button_presses = [(CB.DECEL_SET, 1.2), (0, 1.3)]
|
||||||
|
),
|
||||||
|
Maneuver(
|
||||||
|
'steady state following a car at 20m/s, then lead decel to 0mph at 2m/s^2',
|
||||||
|
duration=50.,
|
||||||
|
initial_speed = 20.,
|
||||||
|
lead_relevancy=True,
|
||||||
|
initial_distance_lead=35.,
|
||||||
|
speed_lead_values = [20.*CV.MPH_TO_MS, 20.*CV.MPH_TO_MS, 0.*CV.MPH_TO_MS],
|
||||||
|
speed_lead_breakpoints = [0., 15., 25.0],
|
||||||
|
cruise_button_presses = [(CB.DECEL_SET, 1.2), (0, 1.3)]
|
||||||
|
),
|
||||||
|
Maneuver(
|
||||||
|
'starting at 0mph, approaching a stopped car 100m away',
|
||||||
|
duration=30.,
|
||||||
|
initial_speed = 0.,
|
||||||
|
lead_relevancy=True,
|
||||||
|
initial_distance_lead=100.,
|
||||||
|
cruise_button_presses = [(CB.DECEL_SET, 1.2), (0, 1.3),
|
||||||
|
(CB.RES_ACCEL, 1.4), (0.0, 1.5),
|
||||||
|
(CB.RES_ACCEL, 1.6), (0.0, 1.7),
|
||||||
|
(CB.RES_ACCEL, 1.8), (0.0, 1.9)]
|
||||||
|
),
|
||||||
|
Maneuver(
|
||||||
|
"following a car at 60mph, lead accel and decel at 0.5m/s^2 every 2s",
|
||||||
|
duration=25.,
|
||||||
|
initial_speed=30.,
|
||||||
|
lead_relevancy=True,
|
||||||
|
initial_distance_lead=49.,
|
||||||
|
speed_lead_values=[30.,30.,29.,31.,29.,31.,29.],
|
||||||
|
speed_lead_breakpoints=[0., 6., 8., 12.,16.,20.,24.],
|
||||||
|
cruise_button_presses = [(CB.DECEL_SET, 1.2), (0, 1.3),
|
||||||
|
(CB.RES_ACCEL, 1.4), (0.0, 1.5),
|
||||||
|
(CB.RES_ACCEL, 1.6), (0.0, 1.7)]
|
||||||
|
),
|
||||||
|
Maneuver(
|
||||||
|
"following a car at 10mph, stop and go at 1m/s2 lead dece1 and accel",
|
||||||
|
duration=70.,
|
||||||
|
initial_speed=10.,
|
||||||
|
lead_relevancy=True,
|
||||||
|
initial_distance_lead=20.,
|
||||||
|
speed_lead_values=[10., 0., 0., 10., 0.,10.],
|
||||||
|
speed_lead_breakpoints=[10., 20., 30., 40., 50., 60.],
|
||||||
|
cruise_button_presses = [(CB.DECEL_SET, 1.2), (0, 1.3),
|
||||||
|
(CB.RES_ACCEL, 1.4), (0.0, 1.5),
|
||||||
|
(CB.RES_ACCEL, 1.6), (0.0, 1.7)]
|
||||||
|
),
|
||||||
|
Maneuver(
|
||||||
|
"green light: stopped behind lead car, lead car accelerates at 1.5 m/s",
|
||||||
|
duration=30.,
|
||||||
|
initial_speed=0.,
|
||||||
|
lead_relevancy=True,
|
||||||
|
initial_distance_lead=4.,
|
||||||
|
speed_lead_values=[0, 0 , 45],
|
||||||
|
speed_lead_breakpoints=[0, 10., 40.],
|
||||||
|
cruise_button_presses = [(CB.DECEL_SET, 1.2), (0, 1.3),
|
||||||
|
(CB.RES_ACCEL, 1.4), (0.0, 1.5),
|
||||||
|
(CB.RES_ACCEL, 1.6), (0.0, 1.7),
|
||||||
|
(CB.RES_ACCEL, 1.8), (0.0, 1.9),
|
||||||
|
(CB.RES_ACCEL, 2.0), (0.0, 2.1),
|
||||||
|
(CB.RES_ACCEL, 2.2), (0.0, 2.3)]
|
||||||
|
),
|
||||||
|
Maneuver(
|
||||||
|
"stop and go with 1m/s2 lead decel and accel, with full stops",
|
||||||
|
duration=70.,
|
||||||
|
initial_speed=0.,
|
||||||
|
lead_relevancy=True,
|
||||||
|
initial_distance_lead=20.,
|
||||||
|
speed_lead_values=[10., 0., 0., 10., 0., 0.] ,
|
||||||
|
speed_lead_breakpoints=[10., 20., 30., 40., 50., 60.],
|
||||||
|
cruise_button_presses = [(CB.DECEL_SET, 1.2), (0, 1.3),
|
||||||
|
(CB.RES_ACCEL, 1.4), (0.0, 1.5),
|
||||||
|
(CB.RES_ACCEL, 1.6), (0.0, 1.7)]
|
||||||
|
),
|
||||||
|
Maneuver(
|
||||||
|
"accelerate from 20 while lead vehicle decelerates from 40 to 20 at 1m/s2",
|
||||||
|
duration=30.,
|
||||||
|
initial_speed=10.,
|
||||||
|
lead_relevancy=True,
|
||||||
|
initial_distance_lead=10.,
|
||||||
|
speed_lead_values=[20., 10.],
|
||||||
|
speed_lead_breakpoints=[1., 11.],
|
||||||
|
cruise_button_presses = [(CB.DECEL_SET, 1.2), (0, 1.3),
|
||||||
|
(CB.RES_ACCEL, 1.4), (0.0, 1.5),
|
||||||
|
(CB.RES_ACCEL, 1.6), (0.0, 1.7),
|
||||||
|
(CB.RES_ACCEL, 1.8), (0.0, 1.9),
|
||||||
|
(CB.RES_ACCEL, 2.0), (0.0, 2.1),
|
||||||
|
(CB.RES_ACCEL, 2.2), (0.0, 2.3)]
|
||||||
|
),
|
||||||
|
Maneuver(
|
||||||
|
"accelerate from 20 while lead vehicle decelerates from 40 to 0 at 2m/s2",
|
||||||
|
duration=30.,
|
||||||
|
initial_speed=10.,
|
||||||
|
lead_relevancy=True,
|
||||||
|
initial_distance_lead=10.,
|
||||||
|
speed_lead_values=[20., 0.],
|
||||||
|
speed_lead_breakpoints=[1., 11.],
|
||||||
|
cruise_button_presses = [(CB.DECEL_SET, 1.2), (0, 1.3),
|
||||||
|
(CB.RES_ACCEL, 1.4), (0.0, 1.5),
|
||||||
|
(CB.RES_ACCEL, 1.6), (0.0, 1.7),
|
||||||
|
(CB.RES_ACCEL, 1.8), (0.0, 1.9),
|
||||||
|
(CB.RES_ACCEL, 2.0), (0.0, 2.1),
|
||||||
|
(CB.RES_ACCEL, 2.2), (0.0, 2.3)]
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
|
css_style = """
|
||||||
|
.maneuver_title {
|
||||||
|
font-size: 24px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.maneuver_graph {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
def main(output_dir):
|
||||||
|
view_html = "<html><head><style>%s</style></head><body><table>" % (css_style,)
|
||||||
|
for i, man in enumerate(maneuvers):
|
||||||
|
view_html += "<tr><td class='maneuver_title' colspan=5><div>%s</div></td></tr><tr>" % (man.title,)
|
||||||
|
for c in ['distance.svg', 'speeds.svg', 'acceleration.svg', 'pedals.svg', 'pid.svg']:
|
||||||
|
view_html += "<td><img class='maneuver_graph' src='%s'/></td>" % (os.path.join("maneuver" + str(i+1).zfill(2), c), )
|
||||||
|
view_html += "</tr>"
|
||||||
|
|
||||||
|
with open(os.path.join(output_dir, "index.html"), "w") as f:
|
||||||
|
f.write(view_html)
|
||||||
|
|
||||||
|
for i, man in enumerate(maneuvers):
|
||||||
|
score, plot = man.evaluate()
|
||||||
|
plot.write_plot(output_dir, "maneuver" + str(i+1).zfill(2))
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
if len(sys.argv) <= 1:
|
||||||
|
print "Usage:", sys.argv[0], "<output_dir>"
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
main(sys.argv[1])
|
||||||
|
|
||||||
+62
-13
@@ -134,9 +134,30 @@ typedef struct UIState {
|
|||||||
|
|
||||||
UIScene scene;
|
UIScene scene;
|
||||||
|
|
||||||
|
bool awake;
|
||||||
|
int awake_timeout;
|
||||||
} UIState;
|
} UIState;
|
||||||
|
|
||||||
|
static void set_awake(UIState *s, bool awake) {
|
||||||
|
if (awake) {
|
||||||
|
// 30 second timeout
|
||||||
|
s->awake_timeout = 30;
|
||||||
|
}
|
||||||
|
if (s->awake != awake) {
|
||||||
|
s->awake = awake;
|
||||||
|
|
||||||
|
// TODO: actually turn off the screen and not just the backlight
|
||||||
|
FILE *f = fopen("/sys/class/leds/lcd-backlight/brightness", "wb");
|
||||||
|
if (f != NULL) {
|
||||||
|
if (awake) {
|
||||||
|
fprintf(f, "205");
|
||||||
|
} else {
|
||||||
|
fprintf(f, "0");
|
||||||
|
}
|
||||||
|
fclose(f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static bool activity_running() {
|
static bool activity_running() {
|
||||||
return system("dumpsys activity activities | grep mFocusedActivity > /dev/null") == 0;
|
return system("dumpsys activity activities | grep mFocusedActivity > /dev/null") == 0;
|
||||||
@@ -173,13 +194,13 @@ typedef struct Button {
|
|||||||
static const Button buttons[] = {
|
static const Button buttons[] = {
|
||||||
{
|
{
|
||||||
.label = "wifi",
|
.label = "wifi",
|
||||||
.x = 400, .y = 700, .w = 250, .h = 250,
|
.x = 400, .y = 730, .w = 250, .h = 250,
|
||||||
.pressed = wifi_pressed,
|
.pressed = wifi_pressed,
|
||||||
.enabled = wifi_enabled,
|
.enabled = wifi_enabled,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.label = "ap",
|
.label = "ap",
|
||||||
.x = 1300, .y = 700, .w = 250, .h = 250,
|
.x = 1300, .y = 730, .w = 250, .h = 250,
|
||||||
.pressed = ap_pressed,
|
.pressed = ap_pressed,
|
||||||
.enabled = ap_enabled,
|
.enabled = ap_enabled,
|
||||||
}
|
}
|
||||||
@@ -380,6 +401,9 @@ static void ui_init(UIState *s) {
|
|||||||
glDisable(GL_DEPTH_TEST);
|
glDisable(GL_DEPTH_TEST);
|
||||||
|
|
||||||
assert(glGetError() == GL_NO_ERROR);
|
assert(glGetError() == GL_NO_ERROR);
|
||||||
|
|
||||||
|
// set awake
|
||||||
|
set_awake(s, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1123,7 +1147,7 @@ static void ui_update(UIState *s) {
|
|||||||
s->scene.v_cruise = datad.vCruise;
|
s->scene.v_cruise = datad.vCruise;
|
||||||
s->scene.v_ego = datad.vEgo;
|
s->scene.v_ego = datad.vEgo;
|
||||||
s->scene.angle_steers = datad.angleSteers;
|
s->scene.angle_steers = datad.angleSteers;
|
||||||
s->scene.engaged = (datad.hudLead == 1) || (datad.hudLead == 2);
|
s->scene.engaged = datad.enabled;
|
||||||
// printf("recv %f\n", datad.vEgo);
|
// printf("recv %f\n", datad.vEgo);
|
||||||
|
|
||||||
s->scene.frontview = datad.rearViewCam;
|
s->scene.frontview = datad.rearViewCam;
|
||||||
@@ -1197,6 +1221,12 @@ static void ui_update(UIState *s) {
|
|||||||
if (rx_bytes) free(rx_bytes);
|
if (rx_bytes) free(rx_bytes);
|
||||||
if (tx_bytes) free(tx_bytes);
|
if (tx_bytes) free(tx_bytes);
|
||||||
|
|
||||||
|
// TODO: do this properly
|
||||||
|
system("git rev-parse --abbrev-ref HEAD > /tmp/git_branch");
|
||||||
|
char *git_branch = read_file("/tmp/git_branch");
|
||||||
|
system("git rev-parse --short HEAD > /tmp/git_commit");
|
||||||
|
char *git_commit = read_file("/tmp/git_commit");
|
||||||
|
|
||||||
int pending = pending_uploads();
|
int pending = pending_uploads();
|
||||||
|
|
||||||
// service call wifi 20 # getWifiEnabledState
|
// service call wifi 20 # getWifiEnabledState
|
||||||
@@ -1210,14 +1240,29 @@ static void ui_update(UIState *s) {
|
|||||||
s->board_connected = !system("lsusb | grep bbaa > /dev/null");
|
s->board_connected = !system("lsusb | grep bbaa > /dev/null");
|
||||||
|
|
||||||
snprintf(s->base_text, sizeof(s->base_text),
|
snprintf(s->base_text, sizeof(s->base_text),
|
||||||
"serial: %s\n dongle id: %s\n battery: %s %s\npending: %d\nrx %.1fkiB/s tx %.1fkiB/s\nboard: %s",
|
"version: %s (%s)\nserial: %s\n dongle id: %s\n battery: %s %s\npending: %d\nrx %.1fkiB/s tx %.1fkiB/s\nboard: %s",
|
||||||
|
git_commit, git_branch,
|
||||||
s->serial, s->dongle_id, bat_cap ? bat_cap : "(null)", bat_stat ? bat_stat : "(null)",
|
s->serial, s->dongle_id, bat_cap ? bat_cap : "(null)", bat_stat ? bat_stat : "(null)",
|
||||||
pending, rx_rate / 1024.0, tx_rate / 1024.0, s->board_connected ? "found" : "NOT FOUND");
|
pending, rx_rate / 1024.0, tx_rate / 1024.0, s->board_connected ? "found" : "NOT FOUND");
|
||||||
|
|
||||||
if (bat_cap) free(bat_cap);
|
if (bat_cap) free(bat_cap);
|
||||||
if (bat_stat) free(bat_stat);
|
if (bat_stat) free(bat_stat);
|
||||||
|
|
||||||
|
if (git_branch) free(git_branch);
|
||||||
|
if (git_commit) free(git_commit);
|
||||||
|
|
||||||
s->last_base_update = ts;
|
s->last_base_update = ts;
|
||||||
|
|
||||||
|
if (s->awake_timeout > 0) {
|
||||||
|
s->awake_timeout--;
|
||||||
|
} else {
|
||||||
|
set_awake(s, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s->vision_connected) {
|
||||||
|
// always awake if vision is connected
|
||||||
|
set_awake(s, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!s->vision_connected) {
|
if (!s->vision_connected) {
|
||||||
@@ -1226,16 +1271,20 @@ static void ui_update(UIState *s) {
|
|||||||
int touch_x = -1, touch_y = -1;
|
int touch_x = -1, touch_y = -1;
|
||||||
err = touch_poll(&s->touch, &touch_x, &touch_y);
|
err = touch_poll(&s->touch, &touch_x, &touch_y);
|
||||||
if (err == 1) {
|
if (err == 1) {
|
||||||
// press buttons
|
if (s->awake) {
|
||||||
for (int i=0; i<ARRAYSIZE(buttons); i++) {
|
// press buttons
|
||||||
const Button *b = &buttons[i];
|
for (int i=0; i<ARRAYSIZE(buttons); i++) {
|
||||||
if (touch_x >= b->x && touch_x < b->x+b->w
|
const Button *b = &buttons[i];
|
||||||
&& touch_y >= b->y && touch_y < b->y+b->h) {
|
if (touch_x >= b->x && touch_x < b->x+b->w
|
||||||
if (b->pressed && !activity_running()) {
|
&& touch_y >= b->y && touch_y < b->y+b->h) {
|
||||||
b->pressed();
|
if (b->pressed && !activity_running()) {
|
||||||
break;
|
b->pressed();
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
set_awake(s, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
Reference in New Issue
Block a user