Merge remote-tracking branch 'upstream/master' into toyota-steer-faults

pull/24067/head
Shane Smiskol 3 years ago
commit d17c13087b
  1. 2
      .github/workflows/prebuilt.yaml
  2. 1
      Pipfile
  3. 24
      Pipfile.lock
  4. 1
      selfdrive/common/params.cc
  5. 2
      selfdrive/controls/controlsd.py
  6. 7
      selfdrive/manager/manager.py
  7. 22
      selfdrive/manager/process.py
  8. 24
      selfdrive/manager/process_config.py
  9. 147
      selfdrive/ui/qt/body.cc
  10. 21
      selfdrive/ui/qt/body.h
  11. 2
      selfdrive/ui/qt/home.cc
  12. 14
      selfdrive/ui/qt/maps/map.cc
  13. 12
      selfdrive/ui/qt/onroad.cc
  14. 2
      selfdrive/ui/qt/widgets/cameraview.cc
  15. 22
      selfdrive/ui/tests/body.py
  16. 2
      selfdrive/ui/ui.cc
  17. 11
      tools/joystick/README.md
  18. 54
      tools/latencylogger/latency_logger.py

@ -3,6 +3,8 @@ on:
schedule: schedule:
- cron: '0 * * * *' - cron: '0 * * * *'
workflow_dispatch:
env: env:
BASE_IMAGE: openpilot-base BASE_IMAGE: openpilot-base
DOCKER_REGISTRY: ghcr.io/commaai DOCKER_REGISTRY: ghcr.io/commaai

@ -4,6 +4,7 @@ url = "https://pypi.org/simple"
verify_ssl = true verify_ssl = true
[dev-packages] [dev-packages]
azure-storage-blob = "~=2.1"
control = "*" control = "*"
coverage = "*" coverage = "*"
dictdiffer = "*" dictdiffer = "*"

24
Pipfile.lock generated

@ -1,7 +1,7 @@
{ {
"_meta": { "_meta": {
"hash": { "hash": {
"sha256": "41e285b10b9b5a353b3eb9c202886e52d22403c369784877aaa35e099aa203a8" "sha256": "918c8cba5c6a0242dc0f6ea74246176a1c54f0a9395feddf35af2189cc813378"
}, },
"pipfile-spec": 6, "pipfile-spec": 6,
"requires": { "requires": {
@ -1111,6 +1111,28 @@
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==21.4.0" "version": "==21.4.0"
}, },
"azure-common": {
"hashes": [
"sha256:4ac0cd3214e36b6a1b6a442686722a5d8cc449603aa833f3f0f40bda836704a3",
"sha256:5c12d3dcf4ec20599ca6b0d3e09e86e146353d443e7fcc050c9a19c1f9df20ad"
],
"version": "==1.1.28"
},
"azure-storage-blob": {
"hashes": [
"sha256:a8e91a51d4f62d11127c7fd8ba0077385c5b11022f0269f8a2a71b9fc36bef31",
"sha256:b90323aad60f207f9f90a0c4cf94c10acc313c20b39403398dfba51f25f7b454"
],
"index": "pypi",
"version": "==2.1.0"
},
"azure-storage-common": {
"hashes": [
"sha256:b01a491a18839b9d05a4fe3421458a0ddb5ab9443c14e487f40d16f9a1dc2fbe",
"sha256:ccedef5c67227bc4d6670ffd37cec18fb529a1b7c3a5e53e4096eb0cf23dc73f"
],
"version": "==2.1.0"
},
"babel": { "babel": {
"hashes": [ "hashes": [
"sha256:ab49e12b91d937cd11f0b67cb259a57ab4ad2b59ac7a3b41d6c06c0ac5b0def9", "sha256:ab49e12b91d937cd11f0b67cb259a57ab4ad2b59ac7a3b41d6c06c0ac5b0def9",

@ -94,6 +94,7 @@ std::unordered_map<std::string, uint32_t> keys = {
{"CompletedTrainingVersion", PERSISTENT}, {"CompletedTrainingVersion", PERSISTENT},
{"ControlsReady", CLEAR_ON_MANAGER_START | CLEAR_ON_IGNITION_ON}, {"ControlsReady", CLEAR_ON_MANAGER_START | CLEAR_ON_IGNITION_ON},
{"CurrentRoute", CLEAR_ON_MANAGER_START | CLEAR_ON_IGNITION_ON}, {"CurrentRoute", CLEAR_ON_MANAGER_START | CLEAR_ON_IGNITION_ON},
{"DisableLogging", CLEAR_ON_MANAGER_START | CLEAR_ON_IGNITION_ON},
{"DisablePowerDown", PERSISTENT}, {"DisablePowerDown", PERSISTENT},
{"DisableRadar_Allow", PERSISTENT}, {"DisableRadar_Allow", PERSISTENT},
{"DisableRadar", PERSISTENT}, // WARNING: THIS DISABLES AEB {"DisableRadar", PERSISTENT}, // WARNING: THIS DISABLES AEB

@ -343,7 +343,7 @@ class Controls:
# Check for mismatch between openpilot and car's PCM # Check for mismatch between openpilot and car's PCM
cruise_mismatch = CS.cruiseState.enabled and (not self.enabled or not self.CP.pcmCruise) cruise_mismatch = CS.cruiseState.enabled and (not self.enabled or not self.CP.pcmCruise)
self.cruise_mismatch_counter = self.cruise_mismatch_counter + 1 if cruise_mismatch else 0 self.cruise_mismatch_counter = self.cruise_mismatch_counter + 1 if cruise_mismatch else 0
if self.cruise_mismatch_counter > int(3. / DT_CTRL): if self.cruise_mismatch_counter > int(6. / DT_CTRL):
self.events.add(EventName.cruiseMismatch) self.events.add(EventName.cruiseMismatch)
# Check for FCW # Check for FCW

@ -128,17 +128,16 @@ def manager_thread() -> None:
ignore.append("pandad") ignore.append("pandad")
ignore += [x for x in os.getenv("BLOCK", "").split(",") if len(x) > 0] ignore += [x for x in os.getenv("BLOCK", "").split(",") if len(x) > 0]
ensure_running(managed_processes.values(), started=False, not_run=ignore)
sm = messaging.SubMaster(['deviceState', 'carParams'], poll=['deviceState']) sm = messaging.SubMaster(['deviceState', 'carParams'], poll=['deviceState'])
pm = messaging.PubMaster(['managerState']) pm = messaging.PubMaster(['managerState'])
ensure_running(managed_processes.values(), False, params=params, CP=sm['carParams'], not_run=ignore)
while True: while True:
sm.update() sm.update()
started = sm['deviceState'].started started = sm['deviceState'].started
driverview = params.get_bool("IsDriverViewEnabled") ensure_running(managed_processes.values(), started, params=params, CP=sm['carParams'], not_run=ignore)
ensure_running(managed_processes.values(), started=started, driverview=driverview, notcar=sm['carParams'].notCar, not_run=ignore)
running = ' '.join("%s%s\u001b[0m" % ("\u001b[32m" if p.proc.is_alive() else "\u001b[31m", p.name) running = ' '.join("%s%s\u001b[0m" % ("\u001b[32m" if p.proc.is_alive() else "\u001b[31m", p.name)
for p in managed_processes.values() if p.proc) for p in managed_processes.values() if p.proc)

@ -4,7 +4,7 @@ import signal
import struct import struct
import time import time
import subprocess import subprocess
from typing import Optional, List, ValuesView from typing import Optional, Callable, List, ValuesView
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from multiprocessing import Process from multiprocessing import Process
@ -12,6 +12,7 @@ from setproctitle import setproctitle # pylint: disable=no-name-in-module
import cereal.messaging as messaging import cereal.messaging as messaging
import selfdrive.sentry as sentry import selfdrive.sentry as sentry
from cereal import car
from common.basedir import BASEDIR from common.basedir import BASEDIR
from common.params import Params from common.params import Params
from common.realtime import sec_since_boot from common.realtime import sec_since_boot
@ -71,8 +72,7 @@ class ManagerProcess(ABC):
sigkill = False sigkill = False
onroad = True onroad = True
offroad = False offroad = False
driverview = False callback: Optional[Callable[[bool, Params, car.CarParams], bool]] = None
notcar = False
proc: Optional[Process] = None proc: Optional[Process] = None
enabled = True enabled = True
name = "" name = ""
@ -184,15 +184,14 @@ class ManagerProcess(ABC):
class NativeProcess(ManagerProcess): class NativeProcess(ManagerProcess):
def __init__(self, name, cwd, cmdline, enabled=True, onroad=True, offroad=False, driverview=False, notcar=False, unkillable=False, sigkill=False, watchdog_max_dt=None): def __init__(self, name, cwd, cmdline, enabled=True, onroad=True, offroad=False, callback=None, unkillable=False, sigkill=False, watchdog_max_dt=None):
self.name = name self.name = name
self.cwd = cwd self.cwd = cwd
self.cmdline = cmdline self.cmdline = cmdline
self.enabled = enabled self.enabled = enabled
self.onroad = onroad self.onroad = onroad
self.offroad = offroad self.offroad = offroad
self.driverview = driverview self.callback = callback
self.notcar = notcar
self.unkillable = unkillable self.unkillable = unkillable
self.sigkill = sigkill self.sigkill = sigkill
self.watchdog_max_dt = watchdog_max_dt self.watchdog_max_dt = watchdog_max_dt
@ -217,14 +216,13 @@ class NativeProcess(ManagerProcess):
class PythonProcess(ManagerProcess): class PythonProcess(ManagerProcess):
def __init__(self, name, module, enabled=True, onroad=True, offroad=False, driverview=False, notcar=False, unkillable=False, sigkill=False, watchdog_max_dt=None): def __init__(self, name, module, enabled=True, onroad=True, offroad=False, callback=None, unkillable=False, sigkill=False, watchdog_max_dt=None):
self.name = name self.name = name
self.module = module self.module = module
self.enabled = enabled self.enabled = enabled
self.onroad = onroad self.onroad = onroad
self.offroad = offroad self.offroad = offroad
self.driverview = driverview self.callback = callback
self.notcar = notcar
self.unkillable = unkillable self.unkillable = unkillable
self.sigkill = sigkill self.sigkill = sigkill
self.watchdog_max_dt = watchdog_max_dt self.watchdog_max_dt = watchdog_max_dt
@ -291,7 +289,7 @@ class DaemonProcess(ManagerProcess):
pass pass
def ensure_running(procs: ValuesView[ManagerProcess], started: bool, driverview: bool=False, notcar: bool=False, def ensure_running(procs: ValuesView[ManagerProcess], started: bool, params=None, CP: car.CarParams=None,
not_run: Optional[List[str]]=None) -> None: not_run: Optional[List[str]]=None) -> None:
if not_run is None: if not_run is None:
not_run = [] not_run = []
@ -301,9 +299,9 @@ def ensure_running(procs: ValuesView[ManagerProcess], started: bool, driverview:
run = any(( run = any((
p.offroad and not started, p.offroad and not started,
p.onroad and started, p.onroad and started,
p.driverview and driverview,
p.notcar and notcar,
)) ))
if p.callback is not None and None not in (params, CP):
run = run or p.callback(started, params, CP)
# Conditions that block a process from starting # Conditions that block a process from starting
run = run and not any(( run = run and not any((

@ -1,18 +1,30 @@
import os import os
from cereal import car
from common.params import Params
from selfdrive.hardware import TICI, PC from selfdrive.hardware import TICI, PC
from selfdrive.manager.process import PythonProcess, NativeProcess, DaemonProcess from selfdrive.manager.process import PythonProcess, NativeProcess, DaemonProcess
WEBCAM = os.getenv("USE_WEBCAM") is not None WEBCAM = os.getenv("USE_WEBCAM") is not None
def driverview(started: bool, params: Params, CP: car.CarParams) -> bool:
return params.get_bool("IsDriverViewEnabled")
def notcar(started: bool, params: Params, CP: car.CarParams) -> bool:
return CP.notCar
def logging(started, params, CP: car.CarParams) -> bool:
run = (not CP.notCar) or not params.get_bool("DisableLogging")
return started and run
procs = [ procs = [
DaemonProcess("manage_athenad", "selfdrive.athena.manage_athenad", "AthenadPid"), DaemonProcess("manage_athenad", "selfdrive.athena.manage_athenad", "AthenadPid"),
# due to qualcomm kernel bugs SIGKILLing camerad sometimes causes page table corruption # due to qualcomm kernel bugs SIGKILLing camerad sometimes causes page table corruption
NativeProcess("camerad", "selfdrive/camerad", ["./camerad"], unkillable=True, driverview=True), NativeProcess("camerad", "selfdrive/camerad", ["./camerad"], unkillable=True, callback=driverview),
NativeProcess("clocksd", "selfdrive/clocksd", ["./clocksd"]), NativeProcess("clocksd", "selfdrive/clocksd", ["./clocksd"]),
NativeProcess("dmonitoringmodeld", "selfdrive/modeld", ["./dmonitoringmodeld"], enabled=(not PC or WEBCAM), driverview=True), NativeProcess("dmonitoringmodeld", "selfdrive/modeld", ["./dmonitoringmodeld"], enabled=(not PC or WEBCAM), callback=driverview),
NativeProcess("logcatd", "selfdrive/logcatd", ["./logcatd"]), NativeProcess("logcatd", "selfdrive/logcatd", ["./logcatd"]),
NativeProcess("loggerd", "selfdrive/loggerd", ["./loggerd"]), NativeProcess("loggerd", "selfdrive/loggerd", ["./loggerd"], onroad=False, callback=logging),
NativeProcess("modeld", "selfdrive/modeld", ["./modeld"]), NativeProcess("modeld", "selfdrive/modeld", ["./modeld"]),
NativeProcess("navd", "selfdrive/ui/navd", ["./navd"], offroad=True), NativeProcess("navd", "selfdrive/ui/navd", ["./navd"], offroad=True),
NativeProcess("proclogd", "selfdrive/proclogd", ["./proclogd"]), NativeProcess("proclogd", "selfdrive/proclogd", ["./proclogd"]),
@ -25,7 +37,7 @@ procs = [
PythonProcess("calibrationd", "selfdrive.locationd.calibrationd"), PythonProcess("calibrationd", "selfdrive.locationd.calibrationd"),
PythonProcess("controlsd", "selfdrive.controls.controlsd"), PythonProcess("controlsd", "selfdrive.controls.controlsd"),
PythonProcess("deleter", "selfdrive.loggerd.deleter", offroad=True), PythonProcess("deleter", "selfdrive.loggerd.deleter", offroad=True),
PythonProcess("dmonitoringd", "selfdrive.monitoring.dmonitoringd", enabled=(not PC or WEBCAM), driverview=True), PythonProcess("dmonitoringd", "selfdrive.monitoring.dmonitoringd", enabled=(not PC or WEBCAM), callback=driverview),
PythonProcess("logmessaged", "selfdrive.logmessaged", offroad=True), PythonProcess("logmessaged", "selfdrive.logmessaged", offroad=True),
PythonProcess("pandad", "selfdrive.boardd.pandad", offroad=True), PythonProcess("pandad", "selfdrive.boardd.pandad", offroad=True),
PythonProcess("paramsd", "selfdrive.locationd.paramsd"), PythonProcess("paramsd", "selfdrive.locationd.paramsd"),
@ -38,8 +50,8 @@ procs = [
PythonProcess("uploader", "selfdrive.loggerd.uploader", offroad=True), PythonProcess("uploader", "selfdrive.loggerd.uploader", offroad=True),
PythonProcess("statsd", "selfdrive.statsd", offroad=True), PythonProcess("statsd", "selfdrive.statsd", offroad=True),
NativeProcess("bridge", "cereal/messaging", ["./bridge"], onroad=False, notcar=True), NativeProcess("bridge", "cereal/messaging", ["./bridge"], onroad=False, callback=notcar),
PythonProcess("webjoystick", "tools.joystick.web", onroad=False, notcar=True), PythonProcess("webjoystick", "tools.joystick.web", onroad=False, callback=notcar),
# Experimental # Experimental
PythonProcess("rawgpsd", "selfdrive.sensord.rawgps.rawgpsd", enabled=os.path.isfile("/persist/comma/use-quectel-rawgps")), PythonProcess("rawgpsd", "selfdrive.sensord.rawgps.rawgpsd", enabled=os.path.isfile("/persist/comma/use-quectel-rawgps")),

@ -1,48 +1,132 @@
#include "selfdrive/ui/qt/body.h" #include "selfdrive/ui/qt/body.h"
#include <cmath> #include <cmath>
#include <algorithm>
#include <QPainter> #include <QPainter>
#include <QStackedLayout>
BodyWindow::BodyWindow(QWidget *parent) : QLabel(parent) { #include "selfdrive/common/params.h"
#include "selfdrive/common/timing.h"
RecordButton::RecordButton(QWidget *parent) : QPushButton(parent) {
setCheckable(true);
setChecked(false);
setFixedSize(148, 148);
QObject::connect(this, &QPushButton::toggled, [=]() {
setEnabled(false);
});
}
void RecordButton::paintEvent(QPaintEvent *event) {
QPainter p(this);
p.setRenderHint(QPainter::Antialiasing);
QPoint center(width() / 2, height() / 2);
QColor bg(isChecked() ? "#FFFFFF" : "#737373");
QColor accent(isChecked() ? "#FF0000" : "#FFFFFF");
if (!isEnabled()) {
bg = QColor("#404040");
accent = QColor("#FFFFFF");
}
if (isDown()) {
accent.setAlphaF(0.7);
}
p.setPen(Qt::NoPen);
p.setBrush(bg);
p.drawEllipse(center, 74, 74);
p.setPen(QPen(accent, 6));
p.setBrush(Qt::NoBrush);
p.drawEllipse(center, 42, 42);
p.setPen(Qt::NoPen);
p.setBrush(accent);
p.drawEllipse(center, 22, 22);
}
BodyWindow::BodyWindow(QWidget *parent) : fuel_filter(1.0, 5., 1. / UI_FREQ), QWidget(parent) {
QStackedLayout *layout = new QStackedLayout(this);
layout->setStackingMode(QStackedLayout::StackAll);
QWidget *w = new QWidget;
QVBoxLayout *vlayout = new QVBoxLayout(w);
vlayout->setMargin(45);
layout->addWidget(w);
// face
face = new QLabel();
face->setAlignment(Qt::AlignCenter);
layout->addWidget(face);
awake = new QMovie("../assets/body/awake.gif"); awake = new QMovie("../assets/body/awake.gif");
awake->setCacheMode(QMovie::CacheAll); awake->setCacheMode(QMovie::CacheAll);
sleep = new QMovie("../assets/body/sleep.gif"); sleep = new QMovie("../assets/body/sleep.gif");
sleep->setCacheMode(QMovie::CacheAll); sleep->setCacheMode(QMovie::CacheAll);
QPalette p(Qt::black); // record button
setPalette(p); btn = new RecordButton(this);
setAutoFillBackground(true); vlayout->addWidget(btn, 0, Qt::AlignBottom | Qt::AlignRight);
QObject::connect(btn, &QPushButton::clicked, [=](bool checked) {
setAlignment(Qt::AlignCenter); btn->setEnabled(false);
Params().putBool("DisableLogging", !checked);
setAttribute(Qt::WA_TransparentForMouseEvents, true); last_button = nanos_since_boot();
});
w->raise();
QObject::connect(uiState(), &UIState::uiUpdate, this, &BodyWindow::updateState); QObject::connect(uiState(), &UIState::uiUpdate, this, &BodyWindow::updateState);
} }
void BodyWindow::paintEvent(QPaintEvent *event) { void BodyWindow::paintEvent(QPaintEvent *event) {
QLabel::paintEvent(event);
QPainter p(this); QPainter p(this);
p.setRenderHint(QPainter::Antialiasing); p.setRenderHint(QPainter::Antialiasing);
p.fillRect(rect(), QColor(0, 0, 0));
// battery outline + detail
p.translate(width() - 136, 16);
const QColor gray = QColor("#737373");
p.setBrush(Qt::NoBrush);
p.setPen(QPen(gray, 4, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
p.drawRoundedRect(2, 2, 78, 36, 8, 8);
p.setPen(Qt::NoPen); p.setPen(Qt::NoPen);
p.setBrush(gray);
p.drawRoundedRect(84, 12, 6, 16, 4, 4);
p.drawRect(84, 12, 3, 16);
// draw battery level // battery level
const int offset = 90; double fuel = std::clamp(fuel_filter.x(), 0.2f, 1.0f);
const int radius = 60 / 2; const int m = 5; // manual margin since we can't do an inner border
const int levels = 5; p.setPen(Qt::NoPen);
const float interval = 1. / levels; p.setBrush(fuel > 0.25 ? QColor("#32D74B") : QColor("#FF453A"));
for (int i = 0; i < levels; i++) { p.drawRoundedRect(2 + m, 2 + m, (78 - 2*m)*fuel, 36 - 2*m, 4, 4);
float level = 1.0 - (i+1)*interval;
float perc = (fuel >= level) ? 1.0 : 0.35; // charging status
if (charging) {
p.setBrush(QColor(255, 255, 255, 255 * perc)); p.setPen(Qt::NoPen);
QPoint pt(width() - (i*offset + offset / 2), offset / 2); p.setBrush(Qt::white);
p.drawEllipse(pt, radius, radius); const QPolygonF charger({
QPointF(12.31, 0),
QPointF(12.31, 16.92),
QPointF(18.46, 16.92),
QPointF(6.15, 40),
QPointF(6.15, 23.08),
QPointF(0, 23.08),
});
p.drawPolygon(charger.translated(98, 0));
} }
} }
void BodyWindow::offroadTransition(bool offroad) {
btn->setChecked(true);
btn->setEnabled(true);
fuel_filter.reset(1.0);
}
void BodyWindow::updateState(const UIState &s) { void BodyWindow::updateState(const UIState &s) {
if (!isVisible()) { if (!isVisible()) {
@ -52,14 +136,25 @@ void BodyWindow::updateState(const UIState &s) {
const SubMaster &sm = *(s.sm); const SubMaster &sm = *(s.sm);
auto cs = sm["carState"].getCarState(); auto cs = sm["carState"].getCarState();
fuel = cs.getFuelGauge(); charging = cs.getCharging();
fuel_filter.update(cs.getFuelGauge());
// TODO: use carState.standstill when that's fixed // TODO: use carState.standstill when that's fixed
const bool standstill = std::abs(cs.getVEgo()) < 0.01; const bool standstill = std::abs(cs.getVEgo()) < 0.01;
QMovie *m = standstill ? sleep : awake; QMovie *m = standstill ? sleep : awake;
if (m != movie()) { if (m != face->movie()) {
setMovie(m); face->setMovie(m);
movie()->start(); face->movie()->start();
}
// update record button state
if (sm.updated("managerState") && (sm.rcv_time("managerState") - last_button)*1e-9 > 0.5) {
for (auto proc : sm["managerState"].getManagerState().getProcesses()) {
if (proc.getName() == "loggerd") {
btn->setEnabled(true);
btn->setChecked(proc.getRunning());
}
}
} }
update(); update();

@ -2,20 +2,37 @@
#include <QMovie> #include <QMovie>
#include <QLabel> #include <QLabel>
#include <QPushButton>
#include "selfdrive/common/util.h"
#include "selfdrive/ui/ui.h" #include "selfdrive/ui/ui.h"
class BodyWindow : public QLabel { class RecordButton : public QPushButton {
Q_OBJECT
public:
RecordButton(QWidget* parent = 0);
private:
void paintEvent(QPaintEvent*) override;
};
class BodyWindow : public QWidget {
Q_OBJECT Q_OBJECT
public: public:
BodyWindow(QWidget* parent = 0); BodyWindow(QWidget* parent = 0);
private: private:
float fuel = 1.0; bool charging = false;
uint64_t last_button = 0;
FirstOrderFilter fuel_filter;
QLabel *face;
QMovie *awake, *sleep; QMovie *awake, *sleep;
RecordButton *btn;
void paintEvent(QPaintEvent*) override; void paintEvent(QPaintEvent*) override;
private slots: private slots:
void updateState(const UIState &s); void updateState(const UIState &s);
void offroadTransition(bool onroad);
}; };

@ -32,7 +32,6 @@ HomeWindow::HomeWindow(QWidget* parent) : QWidget(parent) {
body = new BodyWindow(this); body = new BodyWindow(this);
slayout->addWidget(body); slayout->addWidget(body);
body->setEnabled(false);
driver_view = new DriverViewWindow(this); driver_view = new DriverViewWindow(this);
connect(driver_view, &DriverViewWindow::done, [=] { connect(driver_view, &DriverViewWindow::done, [=] {
@ -59,6 +58,7 @@ void HomeWindow::updateState(const UIState &s) {
} }
void HomeWindow::offroadTransition(bool offroad) { void HomeWindow::offroadTransition(bool offroad) {
body->setEnabled(false);
sidebar->setVisible(offroad); sidebar->setVisible(offroad);
if (offroad) { if (offroad) {
slayout->setCurrentWidget(home); slayout->setCurrentWidget(home);

@ -117,17 +117,15 @@ void MapWindow::timerUpdate() {
auto location = (*sm)["liveLocationKalman"].getLiveLocationKalman(); auto location = (*sm)["liveLocationKalman"].getLiveLocationKalman();
auto pos = location.getPositionGeodetic(); auto pos = location.getPositionGeodetic();
auto orientation = location.getCalibratedOrientationNED(); auto orientation = location.getCalibratedOrientationNED();
auto velocity = location.getVelocityCalibrated();
localizer_valid = (location.getStatus() == cereal::LiveLocationKalman::Status::VALID) && pos.getValid(); localizer_valid = (location.getStatus() == cereal::LiveLocationKalman::Status::VALID) &&
pos.getValid() && orientation.getValid() && velocity.getValid();
if (localizer_valid) { if (localizer_valid) {
float velocity = location.getVelocityCalibrated().getValue()[0]; last_position = QMapbox::Coordinate(pos.getValue()[0], pos.getValue()[1]);
float bearing = RAD2DEG(orientation.getValue()[2]); last_bearing = RAD2DEG(orientation.getValue()[2]);
auto coordinate = QMapbox::Coordinate(pos.getValue()[0], pos.getValue()[1]); velocity_filter.update(velocity.getValue()[0]);
last_position = coordinate;
last_bearing = bearing;
velocity_filter.update(velocity);
} }
} }

@ -294,6 +294,8 @@ void NvgWindow::updateFrameMat(int w, int h) {
} }
void NvgWindow::drawLaneLines(QPainter &painter, const UIState *s) { void NvgWindow::drawLaneLines(QPainter &painter, const UIState *s) {
painter.save();
const UIScene &scene = s->scene; const UIScene &scene = s->scene;
// lanelines // lanelines
for (int i = 0; i < std::size(scene.lane_line_vertices); ++i) { for (int i = 0; i < std::size(scene.lane_line_vertices); ++i) {
@ -329,9 +331,13 @@ void NvgWindow::drawLaneLines(QPainter &painter, const UIState *s) {
} }
painter.setBrush(bg); painter.setBrush(bg);
painter.drawPolygon(scene.track_vertices.v, scene.track_vertices.cnt); painter.drawPolygon(scene.track_vertices.v, scene.track_vertices.cnt);
painter.restore();
} }
void NvgWindow::drawLead(QPainter &painter, const cereal::ModelDataV2::LeadDataV3::Reader &lead_data, const QPointF &vd) { void NvgWindow::drawLead(QPainter &painter, const cereal::ModelDataV2::LeadDataV3::Reader &lead_data, const QPointF &vd) {
painter.save();
const float speedBuff = 10.; const float speedBuff = 10.;
const float leadBuff = 40.; const float leadBuff = 40.;
const float d_rel = lead_data.getX()[0]; const float d_rel = lead_data.getX()[0];
@ -361,6 +367,8 @@ void NvgWindow::drawLead(QPainter &painter, const cereal::ModelDataV2::LeadDataV
QPointF chevron[] = {{x + (sz * 1.25), y + sz}, {x, y}, {x - (sz * 1.25), y + sz}}; QPointF chevron[] = {{x + (sz * 1.25), y + sz}, {x, y}, {x - (sz * 1.25), y + sz}};
painter.setBrush(redColor(fillAlpha)); painter.setBrush(redColor(fillAlpha));
painter.drawPolygon(chevron, std::size(chevron)); painter.drawPolygon(chevron, std::size(chevron));
painter.restore();
} }
void NvgWindow::paintGL() { void NvgWindow::paintGL() {
@ -370,8 +378,6 @@ void NvgWindow::paintGL() {
painter.setRenderHint(QPainter::Antialiasing); painter.setRenderHint(QPainter::Antialiasing);
painter.setPen(Qt::NoPen); painter.setPen(Qt::NoPen);
drawHud(painter);
UIState *s = uiState(); UIState *s = uiState();
if (s->worldObjectsVisible()) { if (s->worldObjectsVisible()) {
@ -388,6 +394,8 @@ void NvgWindow::paintGL() {
} }
} }
drawHud(painter);
double cur_draw_t = millis_since_boot(); double cur_draw_t = millis_since_boot();
double dt = cur_draw_t - prev_draw_t; double dt = cur_draw_t - prev_draw_t;
double fps = fps_filter.update(1. / dt * 1000); double fps = fps_filter.update(1. / dt * 1000);

@ -216,6 +216,7 @@ void CameraViewWidget::paintGL() {
if (latest_frame == nullptr) return; if (latest_frame == nullptr) return;
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glViewport(0, 0, width(), height()); glViewport(0, 0, width(), height());
glBindVertexArray(frame_vao); glBindVertexArray(frame_vao);
@ -238,6 +239,7 @@ void CameraViewWidget::paintGL() {
glBindVertexArray(0); glBindVertexArray(0);
glBindTexture(GL_TEXTURE_2D, 0); glBindTexture(GL_TEXTURE_2D, 0);
glActiveTexture(GL_TEXTURE0); glActiveTexture(GL_TEXTURE0);
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
} }
void CameraViewWidget::vipcConnected(VisionIpcClient *vipc_client) { void CameraViewWidget::vipcConnected(VisionIpcClient *vipc_client) {

@ -0,0 +1,22 @@
#!/usr/bin/env python3
import time
import cereal.messaging as messaging
if __name__ == "__main__":
while True:
pm = messaging.PubMaster(['carParams', 'carState'])
batt = 1.
while True:
msg = messaging.new_message('carParams')
msg.carParams.carName = "COMMA BODY"
msg.carParams.notCar = True
pm.send('carParams', msg)
for b in range(100, 0, -1):
msg = messaging.new_message('carState')
msg.carState.charging = True
msg.carState.fuelGauge = b / 100.
pm.send('carState', msg)
time.sleep(0.1)
time.sleep(1)

@ -233,7 +233,7 @@ UIState::UIState(QObject *parent) : QObject(parent) {
sm = std::make_unique<SubMaster, const std::initializer_list<const char *>>({ sm = std::make_unique<SubMaster, const std::initializer_list<const char *>>({
"modelV2", "controlsState", "liveCalibration", "radarState", "deviceState", "roadCameraState", "modelV2", "controlsState", "liveCalibration", "radarState", "deviceState", "roadCameraState",
"pandaStates", "carParams", "driverMonitoringState", "sensorEvents", "carState", "liveLocationKalman", "pandaStates", "carParams", "driverMonitoringState", "sensorEvents", "carState", "liveLocationKalman",
"wideRoadCameraState", "wideRoadCameraState", "managerState",
}); });
Params params; Params params;

@ -45,6 +45,17 @@ In order to use a joystick over the network, we need to run joystickd locally fr
tools/joystick/joystickd.py tools/joystick/joystickd.py
``` ```
### Web joystick on your mobile device
A browser-based virtual joystick designed for touch screens. Starts automatically when installed on comma body (non-car robotics platform).
For cars, start the web joystick service manually via SSH before starting the car.
```shell
tools/joystick/web.py
```
After starting the car/body, open the web joystick app at this URL: `http://[comma three IP address]:5000`
--- ---
Now start your car and openpilot should go into joystick mode with an alert on startup! The status of the axes will display on the alert, while button statuses print in the shell. Now start your car and openpilot should go into joystick mode with an alert on startup! The status of the axes will display on the alert, while button statuses print in the shell.

@ -7,7 +7,7 @@ import sys
from collections import defaultdict from collections import defaultdict
from tools.lib.logreader import logreader_from_route_or_segment from tools.lib.logreader import logreader_from_route_or_segment
DEMO_ROUTE = "9f583b1d93915c31|2022-04-26--18-49-49" DEMO_ROUTE = "9f583b1d93915c31|2022-04-26--18-49-49"
SERVICES = ['camerad', 'modeld', 'plannerd', 'controlsd', 'boardd'] SERVICES = ['camerad', 'modeld', 'plannerd', 'controlsd', 'boardd']
@ -25,7 +25,7 @@ MSGQ_TO_SERVICE = {
SERVICE_TO_DURATIONS = { SERVICE_TO_DURATIONS = {
'camerad': ['processingTime'], 'camerad': ['processingTime'],
'modeld': ['modelExecutionTime', 'gpuExecutionTime'], 'modeld': ['modelExecutionTime', 'gpuExecutionTime'],
'plannerd': ["solverExecutionTime"], 'plannerd': ['solverExecutionTime'],
} }
def read_logs(lr): def read_logs(lr):
@ -38,7 +38,7 @@ def read_logs(lr):
if msg.which() == 'sendcan': if msg.which() == 'sendcan':
latest_sendcan_monotime = msg.logMonoTime latest_sendcan_monotime = msg.logMonoTime
continue continue
if msg.which() in MSGQ_TO_SERVICE: if msg.which() in MSGQ_TO_SERVICE:
service = MSGQ_TO_SERVICE[msg.which()] service = MSGQ_TO_SERVICE[msg.which()]
msg_obj = getattr(msg, msg.which()) msg_obj = getattr(msg, msg.which())
@ -83,8 +83,10 @@ def read_logs(lr):
if msg_obj.frameIdExtra != frame_id: if msg_obj.frameIdExtra != frame_id:
frame_mismatches.append(frame_id) frame_mismatches.append(frame_id)
assert frame_id_fails < 20, "Too many frameId fetch fails" if frame_id_fails > 20:
assert len(frame_mismatches) < 20, "Too many frame mismatches" print("Warning, many frameId fetch fails", frame_id_fails)
if len(frame_mismatches) > 20:
print("Warning, many frame mismatches", len(frame_mismatches))
return (data, frame_mismatches) return (data, frame_mismatches)
def find_frame_id(time, service, start_times, end_times): def find_frame_id(time, service, start_times, end_times):
@ -98,15 +100,15 @@ def find_t0(start_times, frame_id=-1):
m = max(start_times.keys()) m = max(start_times.keys())
while frame_id <= m: while frame_id <= m:
for service in SERVICES: for service in SERVICES:
if service in start_times[frame_id]: if start_times[frame_id][service]:
return start_times[frame_id][service] return start_times[frame_id][service]
frame_id += 1 frame_id += 1
raise Exception('No start time has been set') raise Exception('No start time has been set')
## ASSUMES THAT AT LEAST ONE CLOUDLOG IS MADE IN CONTROLSD
def insert_cloudlogs(lr, timestamps, start_times, end_times): def insert_cloudlogs(lr, timestamps, start_times, end_times):
t0 = find_t0(start_times) # at least one cloudlog must be made in controlsd
t0 = find_t0(start_times)
failed_inserts = 0 failed_inserts = 0
latest_controls_frameid = 0 latest_controls_frameid = 0
for msg in lr: for msg in lr:
@ -117,7 +119,7 @@ def insert_cloudlogs(lr, timestamps, start_times, end_times):
service = jmsg['ctx']['daemon'] service = jmsg['ctx']['daemon']
event = jmsg['msg']['timestamp']['event'] event = jmsg['msg']['timestamp']['event']
if time < t0: if time < t0:
# Filter out controlsd messages which arrive before the camera loop # Filter out controlsd messages which arrive before the camera loop
continue continue
if "frame_id" in jmsg['msg']['timestamp']: if "frame_id" in jmsg['msg']['timestamp']:
@ -138,40 +140,41 @@ def insert_cloudlogs(lr, timestamps, start_times, end_times):
timestamps[frame_id][service].append((event, time)) timestamps[frame_id][service].append((event, time))
else: else:
failed_inserts += 1 failed_inserts += 1
assert latest_controls_frameid > 0, "No timestamp in controlsd"
assert failed_inserts < len(timestamps), "Too many failed cloudlog inserts" if latest_controls_frameid == 0:
print("Warning: failed to bind boardd logs to a frame ID. Add a timestamp cloudlog in controlsd.")
elif failed_inserts > len(timestamps):
print(f"Warning: failed to bind {failed_inserts} cloudlog timestamps to a frame ID")
def print_timestamps(timestamps, durations, start_times, relative): def print_timestamps(timestamps, durations, start_times, relative):
t0 = find_t0(start_times) t0 = find_t0(start_times)
for frame_id in timestamps.keys(): for frame_id in timestamps.keys():
print('='*80) print('='*80)
print("Frame ID:", frame_id) print("Frame ID:", frame_id)
if relative: if relative:
t0 = find_t0(start_times, frame_id) t0 = find_t0(start_times, frame_id)
for service in SERVICES: for service in SERVICES:
print(" "+service) print(" "+service)
events = timestamps[frame_id][service] events = timestamps[frame_id][service]
for event, time in sorted(events, key = lambda x: x[1]): for event, time in sorted(events, key = lambda x: x[1]):
print(" "+'%-53s%-53s' %(event, str((time-t0)/1e6))) print(" "+'%-53s%-53s' %(event, str((time-t0)/1e6)))
for event, time in durations[frame_id][service]: for event, time in durations[frame_id][service]:
print(" "+'%-53s%-53s' %(event, str(time*1000))) print(" "+'%-53s%-53s' %(event, str(time*1000)))
def graph_timestamps(timestamps, start_times, end_times, relative): def graph_timestamps(timestamps, start_times, end_times, relative):
t0 = find_t0(start_times) t0 = find_t0(start_times)
y0 = min(start_times.keys())
fig, ax = plt.subplots() fig, ax = plt.subplots()
ax.set_xlim(0, 150 if relative else 750) ax.set_xlim(0, 150 if relative else 750)
ax.set_ylim(y0, y0+15) ax.set_ylim(0, 15)
ax.set_xlabel('milliseconds') ax.set_xlabel('milliseconds')
ax.set_ylabel('Frame ID')
colors = ['blue', 'green', 'red', 'yellow', 'purple'] colors = ['blue', 'green', 'red', 'yellow', 'purple']
assert len(colors) == len(SERVICES), 'Each service needs a color' assert len(colors) == len(SERVICES), 'Each service needs a color'
points = {"x": [], "y": [], "labels": []} points = {"x": [], "y": [], "labels": []}
for frame_id, services in timestamps.items(): for i, (frame_id, services) in enumerate(timestamps.items()):
if relative: if relative:
t0 = find_t0(start_times, frame_id) t0 = find_t0(start_times, frame_id)
service_bars = [] service_bars = []
for service, events in services.items(): for service, events in services.items():
if start_times[frame_id][service] and end_times[frame_id][service]: if start_times[frame_id][service] and end_times[frame_id][service]:
@ -180,9 +183,9 @@ def graph_timestamps(timestamps, start_times, end_times, relative):
service_bars.append(((start-t0)/1e6,(end-start)/1e6)) service_bars.append(((start-t0)/1e6,(end-start)/1e6))
for event in events: for event in events:
points['x'].append((event[1]-t0)/1e6) points['x'].append((event[1]-t0)/1e6)
points['y'].append(frame_id) points['y'].append(i)
points['labels'].append(event[0]) points['labels'].append(event[0])
ax.broken_barh(service_bars, (frame_id-0.45, 0.9), facecolors=(colors), alpha=0.5) ax.broken_barh(service_bars, (i-0.45, 0.9), facecolors=(colors), alpha=0.5)
scatter = ax.scatter(points['x'], points['y'], marker='d', edgecolor='black') scatter = ax.scatter(points['x'], points['y'], marker='d', edgecolor='black')
tooltip = mpld3.plugins.PointLabelTooltip(scatter, labels=points['labels']) tooltip = mpld3.plugins.PointLabelTooltip(scatter, labels=points['labels'])
@ -212,6 +215,7 @@ if __name__ == "__main__":
r = DEMO_ROUTE if args.demo else args.route_or_segment_name.strip() r = DEMO_ROUTE if args.demo else args.route_or_segment_name.strip()
lr = logreader_from_route_or_segment(r, sort_by_time=True) lr = logreader_from_route_or_segment(r, sort_by_time=True)
data, _ = get_timestamps(lr) data, _ = get_timestamps(lr)
print_timestamps(data['timestamp'], data['duration'], data['start'], args.relative) print_timestamps(data['timestamp'], data['duration'], data['start'], args.relative)
if args.plot: if args.plot:

Loading…
Cancel
Save