mirror of
https://github.com/sunnypilot/sunnypilot.git
synced 2026-07-25 11:52:20 +08:00
Merge remote-tracking branch 'commaai/openpilot/master' into gv80-2021-uae
# Conflicts: # opendbc_repo
This commit is contained in:
@@ -51,4 +51,4 @@ jobs:
|
||||
- name: Push master-ci
|
||||
run: |
|
||||
unset TARGET_DIR
|
||||
BRANCH=master-ci release/build_devel.sh
|
||||
BRANCH=__nightly release/build_devel.sh
|
||||
|
||||
@@ -5,7 +5,33 @@ on:
|
||||
- cron: "0 14 * * 1" # every Monday at 2am UTC (6am PST)
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
BASE_IMAGE: openpilot-base
|
||||
BUILD: selfdrive/test/docker_build.sh base
|
||||
RUN: docker run --shm-size 2G -v $PWD:/tmp/openpilot -w /tmp/openpilot -e CI=1 -e PYTHONWARNINGS=error -e FILEREADER_CACHE=1 -e PYTHONPATH=/tmp/openpilot -e NUM_JOBS -e JOB_ID -e GITHUB_ACTION -e GITHUB_REF -e GITHUB_HEAD_REF -e GITHUB_SHA -e GITHUB_REPOSITORY -e GITHUB_RUN_ID -v $GITHUB_WORKSPACE/.ci_cache/scons_cache:/tmp/scons_cache -v $GITHUB_WORKSPACE/.ci_cache/comma_download_cache:/tmp/comma_download_cache -v $GITHUB_WORKSPACE/.ci_cache/openpilot_cache:/tmp/openpilot_cache $BASE_IMAGE /bin/bash -c
|
||||
|
||||
jobs:
|
||||
update_translations:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'commaai/openpilot'
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ./.github/workflows/setup-with-retry
|
||||
- name: Update translations
|
||||
run: |
|
||||
${{ env.RUN }} "python3 selfdrive/ui/update_translations.py --vanish"
|
||||
- name: Create Pull Request
|
||||
uses: peter-evans/create-pull-request@9153d834b60caba6d51c9b9510b087acf9f33f83
|
||||
with:
|
||||
author: Vehicle Researcher <user@comma.ai>
|
||||
commit-message: "Update translations"
|
||||
title: "[bot] Update translations"
|
||||
body: "Automatic PR from repo-maintenance -> update_translations"
|
||||
branch: "update-translations"
|
||||
base: "master"
|
||||
delete-branch: true
|
||||
labels: bot
|
||||
|
||||
package_updates:
|
||||
name: package_updates
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
@@ -84,7 +84,7 @@ jobs:
|
||||
run: >-
|
||||
sudo apt-get install -y imagemagick
|
||||
|
||||
scenes="homescreen settings_device settings_software settings_toggles settings_developer offroad_alert update_available prime onroad onroad_disengaged onroad_override onroad_sidebar onroad_wide onroad_wide_sidebar onroad_alert_small onroad_alert_mid onroad_alert_full driver_camera body keyboard keyboard_uppercase"
|
||||
scenes=$(find ${{ github.workspace }}/pr_ui/*.png -type f -printf "%f\n" | cut -d '.' -f 1 | grep -v 'pair_device')
|
||||
A=($scenes)
|
||||
|
||||
DIFF=""
|
||||
|
||||
@@ -36,6 +36,7 @@ a.out
|
||||
*.pyxbldc
|
||||
*.vcd
|
||||
*.qm
|
||||
*_pyx.cpp
|
||||
config.json
|
||||
clcache
|
||||
compile_commands.json
|
||||
|
||||
Vendored
+2
-6
@@ -166,7 +166,7 @@ node {
|
||||
env.GIT_BRANCH = checkout(scm).GIT_BRANCH
|
||||
env.GIT_COMMIT = checkout(scm).GIT_COMMIT
|
||||
|
||||
def excludeBranches = ['master-ci', 'devel', 'devel-staging', 'release3', 'release3-staging',
|
||||
def excludeBranches = ['__nightly', 'devel', 'devel-staging', 'release3', 'release3-staging',
|
||||
'testing-closet*', 'hotfix-*']
|
||||
def excludeRegex = excludeBranches.join('|').replaceAll('\\*', '.*')
|
||||
|
||||
@@ -183,7 +183,7 @@ node {
|
||||
])
|
||||
}
|
||||
|
||||
if (env.BRANCH_NAME == 'master-ci') {
|
||||
if (env.BRANCH_NAME == '__nightly') {
|
||||
parallel (
|
||||
'nightly': {
|
||||
deviceStage("build nightly", "tici-needs-can", [], [
|
||||
@@ -203,8 +203,6 @@ node {
|
||||
// tici tests
|
||||
'onroad tests': {
|
||||
deviceStage("onroad", "tici-needs-can", ["UNSAFE=1"], [
|
||||
// TODO: ideally, this test runs in master-ci, but it takes 5+m to build it
|
||||
//["build master-ci", "cd $SOURCE_DIR/release && TARGET_DIR=$TEST_DIR $SOURCE_DIR/scripts/retry.sh ./build_devel.sh"],
|
||||
step("build openpilot", "cd system/manager && ./build.py"),
|
||||
step("check dirty", "release/check-dirty.sh"),
|
||||
step("onroad tests", "pytest selfdrive/test/test_onroad.py -s", [timeout: 60]),
|
||||
@@ -240,7 +238,6 @@ node {
|
||||
step("test exposure", "pytest system/camerad/test/test_exposure.py"),
|
||||
])
|
||||
},
|
||||
/*
|
||||
'camerad OS04C10': {
|
||||
deviceStage("OS04C10", "tici-os04c10", ["UNSAFE=1"], [
|
||||
step("build", "cd system/manager && ./build.py"),
|
||||
@@ -248,7 +245,6 @@ node {
|
||||
step("test exposure", "pytest system/camerad/test/test_exposure.py"),
|
||||
])
|
||||
},
|
||||
*/
|
||||
'sensord': {
|
||||
deviceStage("LSM + MMC", "tici-lsmc", ["UNSAFE=1"], [
|
||||
step("build", "cd system/manager && ./build.py"),
|
||||
|
||||
+7
-4
@@ -1,4 +1,8 @@
|
||||
Version 0.9.8 (2025-02-27)
|
||||
Version 0.9.9 (2025-04-XX)
|
||||
========================
|
||||
* Tesla Model 3 and Y support thanks to lukasloetkolben!
|
||||
|
||||
Version 0.9.8 (2025-02-28)
|
||||
========================
|
||||
* New driving model
|
||||
* Model now gates applying positive acceleration in Chill mode
|
||||
@@ -8,12 +12,11 @@ Version 0.9.8 (2025-02-27)
|
||||
* More GPU time for bigger driving models
|
||||
* Power draw reduced 0.5W, which means your device runs cooler
|
||||
* Added toggle to enable driver monitoring even when openpilot is not engaged
|
||||
* FIREHOSE mode
|
||||
* Allows you to maximize your training data uploads to improve the models
|
||||
* Localizer rewritten to remove GPS dependency at runtime
|
||||
* Firehose Mode for maximizing your training data uploads
|
||||
* Enable openpilot longitudinal control for Ford Q3 vehicles
|
||||
* New Toyota TSS2 longitudinal tune
|
||||
* Rivian R1S and R1T support thanks to lukasloetkolben!
|
||||
* Tesla Model 3 and Y support thanks to lukasloetkolben!
|
||||
* Ford F-150, F-150 Hybrid, Mach-E, and Ranger support
|
||||
|
||||
Version 0.9.7 (2024-06-13)
|
||||
|
||||
+10
-1
@@ -151,6 +151,10 @@ struct InitData {
|
||||
gitBranch @11 :Text;
|
||||
gitRemote @13 :Text;
|
||||
|
||||
# this is source commit for prebuilt branches
|
||||
gitSrcCommit @23 :Text;
|
||||
gitSrcCommitDate @24 :Text;
|
||||
|
||||
androidProperties @16 :Map(Text, Text);
|
||||
|
||||
pandaInfo @8 :PandaInfo;
|
||||
@@ -2238,6 +2242,11 @@ struct LiveParametersData {
|
||||
roll @14 :Float32;
|
||||
debugFilterState @16 :FilterState;
|
||||
|
||||
angleOffsetValid @17 :Bool = true;
|
||||
angleOffsetAverageValid @18 :Bool = true;
|
||||
steerRatioValid @19 :Bool = true;
|
||||
stiffnessFactorValid @20 :Bool = true;
|
||||
|
||||
yawRateDEPRECATED @7 :Float32;
|
||||
filterStateDEPRECATED @15 :LiveLocationKalman.Measurement;
|
||||
|
||||
@@ -2564,7 +2573,7 @@ struct Event {
|
||||
|
||||
# DO change the name of the field and struct
|
||||
# DON'T change the ID (e.g. @107)
|
||||
# DON'T change which struct it points to
|
||||
# DON'T change which struct it points to
|
||||
customReserved0 @107 :Custom.CustomReserved0;
|
||||
customReserved1 @108 :Custom.CustomReserved1;
|
||||
customReserved2 @109 :Custom.CustomReserved2;
|
||||
|
||||
@@ -96,109 +96,3 @@ cl_program cl_program_from_source(cl_context ctx, cl_device_id device_id, const
|
||||
}
|
||||
return prg;
|
||||
}
|
||||
|
||||
cl_program cl_program_from_binary(cl_context ctx, cl_device_id device_id, const uint8_t* binary, size_t length, const char* args) {
|
||||
cl_program prg = CL_CHECK_ERR(clCreateProgramWithBinary(ctx, 1, &device_id, &length, &binary, NULL, &err));
|
||||
if (int err = clBuildProgram(prg, 1, &device_id, args, NULL, NULL); err != 0) {
|
||||
cl_print_build_errors(prg, device_id);
|
||||
assert(0);
|
||||
}
|
||||
return prg;
|
||||
}
|
||||
|
||||
// Given a cl code and return a string representation
|
||||
#define CL_ERR_TO_STR(err) case err: return #err
|
||||
const char* cl_get_error_string(int err) {
|
||||
switch (err) {
|
||||
CL_ERR_TO_STR(CL_SUCCESS);
|
||||
CL_ERR_TO_STR(CL_DEVICE_NOT_FOUND);
|
||||
CL_ERR_TO_STR(CL_DEVICE_NOT_AVAILABLE);
|
||||
CL_ERR_TO_STR(CL_COMPILER_NOT_AVAILABLE);
|
||||
CL_ERR_TO_STR(CL_MEM_OBJECT_ALLOCATION_FAILURE);
|
||||
CL_ERR_TO_STR(CL_OUT_OF_RESOURCES);
|
||||
CL_ERR_TO_STR(CL_OUT_OF_HOST_MEMORY);
|
||||
CL_ERR_TO_STR(CL_PROFILING_INFO_NOT_AVAILABLE);
|
||||
CL_ERR_TO_STR(CL_MEM_COPY_OVERLAP);
|
||||
CL_ERR_TO_STR(CL_IMAGE_FORMAT_MISMATCH);
|
||||
CL_ERR_TO_STR(CL_IMAGE_FORMAT_NOT_SUPPORTED);
|
||||
CL_ERR_TO_STR(CL_MAP_FAILURE);
|
||||
CL_ERR_TO_STR(CL_MISALIGNED_SUB_BUFFER_OFFSET);
|
||||
CL_ERR_TO_STR(CL_EXEC_STATUS_ERROR_FOR_EVENTS_IN_WAIT_LIST);
|
||||
CL_ERR_TO_STR(CL_COMPILE_PROGRAM_FAILURE);
|
||||
CL_ERR_TO_STR(CL_LINKER_NOT_AVAILABLE);
|
||||
CL_ERR_TO_STR(CL_LINK_PROGRAM_FAILURE);
|
||||
CL_ERR_TO_STR(CL_DEVICE_PARTITION_FAILED);
|
||||
CL_ERR_TO_STR(CL_KERNEL_ARG_INFO_NOT_AVAILABLE);
|
||||
CL_ERR_TO_STR(CL_INVALID_VALUE);
|
||||
CL_ERR_TO_STR(CL_INVALID_DEVICE_TYPE);
|
||||
CL_ERR_TO_STR(CL_INVALID_PLATFORM);
|
||||
CL_ERR_TO_STR(CL_INVALID_DEVICE);
|
||||
CL_ERR_TO_STR(CL_INVALID_CONTEXT);
|
||||
CL_ERR_TO_STR(CL_INVALID_QUEUE_PROPERTIES);
|
||||
CL_ERR_TO_STR(CL_INVALID_COMMAND_QUEUE);
|
||||
CL_ERR_TO_STR(CL_INVALID_HOST_PTR);
|
||||
CL_ERR_TO_STR(CL_INVALID_MEM_OBJECT);
|
||||
CL_ERR_TO_STR(CL_INVALID_IMAGE_FORMAT_DESCRIPTOR);
|
||||
CL_ERR_TO_STR(CL_INVALID_IMAGE_SIZE);
|
||||
CL_ERR_TO_STR(CL_INVALID_SAMPLER);
|
||||
CL_ERR_TO_STR(CL_INVALID_BINARY);
|
||||
CL_ERR_TO_STR(CL_INVALID_BUILD_OPTIONS);
|
||||
CL_ERR_TO_STR(CL_INVALID_PROGRAM);
|
||||
CL_ERR_TO_STR(CL_INVALID_PROGRAM_EXECUTABLE);
|
||||
CL_ERR_TO_STR(CL_INVALID_KERNEL_NAME);
|
||||
CL_ERR_TO_STR(CL_INVALID_KERNEL_DEFINITION);
|
||||
CL_ERR_TO_STR(CL_INVALID_KERNEL);
|
||||
CL_ERR_TO_STR(CL_INVALID_ARG_INDEX);
|
||||
CL_ERR_TO_STR(CL_INVALID_ARG_VALUE);
|
||||
CL_ERR_TO_STR(CL_INVALID_ARG_SIZE);
|
||||
CL_ERR_TO_STR(CL_INVALID_KERNEL_ARGS);
|
||||
CL_ERR_TO_STR(CL_INVALID_WORK_DIMENSION);
|
||||
CL_ERR_TO_STR(CL_INVALID_WORK_GROUP_SIZE);
|
||||
CL_ERR_TO_STR(CL_INVALID_WORK_ITEM_SIZE);
|
||||
CL_ERR_TO_STR(CL_INVALID_GLOBAL_OFFSET);
|
||||
CL_ERR_TO_STR(CL_INVALID_EVENT_WAIT_LIST);
|
||||
CL_ERR_TO_STR(CL_INVALID_EVENT);
|
||||
CL_ERR_TO_STR(CL_INVALID_OPERATION);
|
||||
CL_ERR_TO_STR(CL_INVALID_GL_OBJECT);
|
||||
CL_ERR_TO_STR(CL_INVALID_BUFFER_SIZE);
|
||||
CL_ERR_TO_STR(CL_INVALID_MIP_LEVEL);
|
||||
CL_ERR_TO_STR(CL_INVALID_GLOBAL_WORK_SIZE);
|
||||
CL_ERR_TO_STR(CL_INVALID_PROPERTY);
|
||||
CL_ERR_TO_STR(CL_INVALID_IMAGE_DESCRIPTOR);
|
||||
CL_ERR_TO_STR(CL_INVALID_COMPILER_OPTIONS);
|
||||
CL_ERR_TO_STR(CL_INVALID_LINKER_OPTIONS);
|
||||
CL_ERR_TO_STR(CL_INVALID_DEVICE_PARTITION_COUNT);
|
||||
case -69: return "CL_INVALID_PIPE_SIZE";
|
||||
case -70: return "CL_INVALID_DEVICE_QUEUE";
|
||||
case -71: return "CL_INVALID_SPEC_ID";
|
||||
case -72: return "CL_MAX_SIZE_RESTRICTION_EXCEEDED";
|
||||
case -1002: return "CL_INVALID_D3D10_DEVICE_KHR";
|
||||
case -1003: return "CL_INVALID_D3D10_RESOURCE_KHR";
|
||||
case -1004: return "CL_D3D10_RESOURCE_ALREADY_ACQUIRED_KHR";
|
||||
case -1005: return "CL_D3D10_RESOURCE_NOT_ACQUIRED_KHR";
|
||||
case -1006: return "CL_INVALID_D3D11_DEVICE_KHR";
|
||||
case -1007: return "CL_INVALID_D3D11_RESOURCE_KHR";
|
||||
case -1008: return "CL_D3D11_RESOURCE_ALREADY_ACQUIRED_KHR";
|
||||
case -1009: return "CL_D3D11_RESOURCE_NOT_ACQUIRED_KHR";
|
||||
case -1010: return "CL_INVALID_DX9_MEDIA_ADAPTER_KHR";
|
||||
case -1011: return "CL_INVALID_DX9_MEDIA_SURFACE_KHR";
|
||||
case -1012: return "CL_DX9_MEDIA_SURFACE_ALREADY_ACQUIRED_KHR";
|
||||
case -1013: return "CL_DX9_MEDIA_SURFACE_NOT_ACQUIRED_KHR";
|
||||
case -1093: return "CL_INVALID_EGL_OBJECT_KHR";
|
||||
case -1092: return "CL_EGL_RESOURCE_NOT_ACQUIRED_KHR";
|
||||
case -1001: return "CL_PLATFORM_NOT_FOUND_KHR";
|
||||
case -1057: return "CL_DEVICE_PARTITION_FAILED_EXT";
|
||||
case -1058: return "CL_INVALID_PARTITION_COUNT_EXT";
|
||||
case -1059: return "CL_INVALID_PARTITION_NAME_EXT";
|
||||
case -1094: return "CL_INVALID_ACCELERATOR_INTEL";
|
||||
case -1095: return "CL_INVALID_ACCELERATOR_TYPE_INTEL";
|
||||
case -1096: return "CL_INVALID_ACCELERATOR_DESCRIPTOR_INTEL";
|
||||
case -1097: return "CL_ACCELERATOR_TYPE_NOT_SUPPORTED_INTEL";
|
||||
case -1000: return "CL_INVALID_GL_SHAREGROUP_REFERENCE_KHR";
|
||||
case -1098: return "CL_INVALID_VA_API_MEDIA_ADAPTER_INTEL";
|
||||
case -1099: return "CL_INVALID_VA_API_MEDIA_SURFACE_INTEL";
|
||||
case -1100: return "CL_VA_API_MEDIA_SURFACE_ALREADY_ACQUIRED_INTEL";
|
||||
case -1101: return "CL_VA_API_MEDIA_SURFACE_NOT_ACQUIRED_INTEL";
|
||||
default: return "CL_UNKNOWN_ERROR";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,4 @@ cl_device_id cl_get_device_id(cl_device_type device_type);
|
||||
cl_context cl_create_context(cl_device_id device_id);
|
||||
void cl_release_context(cl_context context);
|
||||
cl_program cl_program_from_source(cl_context ctx, cl_device_id device_id, const std::string& src, const char* args = nullptr);
|
||||
cl_program cl_program_from_binary(cl_context ctx, cl_device_id device_id, const uint8_t* binary, size_t length, const char* args = nullptr);
|
||||
cl_program cl_program_from_file(cl_context ctx, cl_device_id device_id, const char* path, const char* args);
|
||||
const char* cl_get_error_string(int err);
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
def clip(x, lo, hi):
|
||||
return max(lo, min(hi, x))
|
||||
|
||||
def interp(x, xp, fp):
|
||||
N = len(xp)
|
||||
|
||||
def get_interp(xv):
|
||||
hi = 0
|
||||
while hi < N and xv > xp[hi]:
|
||||
hi += 1
|
||||
low = hi - 1
|
||||
return fp[-1] if hi == N and xv > xp[low] else (
|
||||
fp[0] if hi == 0 else
|
||||
(xv - xp[low]) * (fp[hi] - fp[low]) / (xp[hi] - xp[low]) + fp[low])
|
||||
|
||||
return [get_interp(v) for v in x] if hasattr(x, '__iter__') else get_interp(x)
|
||||
|
||||
def mean(x):
|
||||
return sum(x) / len(x)
|
||||
@@ -8,6 +8,7 @@ inline static std::unordered_map<std::string, uint32_t> keys = {
|
||||
{"AdbEnabled", PERSISTENT},
|
||||
{"AlwaysOnDM", PERSISTENT},
|
||||
{"ApiCache_Device", PERSISTENT},
|
||||
{"ApiCache_FirehoseStats", PERSISTENT},
|
||||
{"AssistNowToken", PERSISTENT},
|
||||
{"AthenadPid", PERSISTENT},
|
||||
{"AthenadUploadQueue", PERSISTENT},
|
||||
@@ -36,7 +37,6 @@ inline static std::unordered_map<std::string, uint32_t> keys = {
|
||||
{"ExperimentalLongitudinalEnabled", PERSISTENT | DEVELOPMENT_ONLY},
|
||||
{"ExperimentalMode", PERSISTENT},
|
||||
{"ExperimentalModeConfirmed", PERSISTENT},
|
||||
{"FirehoseMode", CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION},
|
||||
{"FirmwareQueryDone", CLEAR_ON_MANAGER_START | CLEAR_ON_ONROAD_TRANSITION},
|
||||
{"ForcePowerDown", PERSISTENT},
|
||||
{"GitBranch", PERSISTENT},
|
||||
|
||||
@@ -257,7 +257,6 @@ bool ends_with(const std::string& s, const std::string& suffix) {
|
||||
|
||||
std::string strip(const std::string &str) {
|
||||
auto should_trim = [](unsigned char ch) {
|
||||
// trim whitespace or a null character
|
||||
return std::isspace(ch) || ch == '\0';
|
||||
};
|
||||
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
#define COMMA_VERSION "0.9.8"
|
||||
#define COMMA_VERSION "0.9.9"
|
||||
|
||||
+3
-1
@@ -77,8 +77,10 @@ def openpilot_class_fixture():
|
||||
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
def tici_setup_fixture(openpilot_function_fixture):
|
||||
def tici_setup_fixture(request, openpilot_function_fixture):
|
||||
"""Ensure a consistent state for tests on-device. Needs the openpilot function fixture to run first."""
|
||||
if 'skip_tici_setup' in request.keywords:
|
||||
return
|
||||
HARDWARE.initialize_hardware()
|
||||
HARDWARE.set_power_save(False)
|
||||
os.system("pkill -9 -f athena")
|
||||
|
||||
+9
-9
@@ -58,11 +58,11 @@ A supported vehicle is one that just works when you install a comma device. All
|
||||
|Genesis|G90 2017-20|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai C connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Genesis&model=G90 2017-20">Buy Here</a></sub></details>||
|
||||
|Genesis|GV60 (Advanced Trim) 2023[<sup>5</sup>](#footnotes)|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai A connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Genesis&model=GV60 (Advanced Trim) 2023">Buy Here</a></sub></details>||
|
||||
|Genesis|GV60 (Performance Trim) 2022-23[<sup>5</sup>](#footnotes)|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai K connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Genesis&model=GV60 (Performance Trim) 2022-23">Buy Here</a></sub></details>||
|
||||
|Genesis|GV70 (2.5T Trim, without HDA II) 2022-24[<sup>5</sup>](#footnotes)|All|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai L connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Genesis&model=GV70 (2.5T Trim, without HDA II) 2022-24">Buy Here</a></sub></details>||
|
||||
|Genesis|GV70 (3.5T Trim, without HDA II) 2022-23[<sup>5</sup>](#footnotes)|All|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai M connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Genesis&model=GV70 (3.5T Trim, without HDA II) 2022-23">Buy Here</a></sub></details>||
|
||||
|Genesis|GV70 (2.5T Trim, without HDA II) 2022-24[<sup>5</sup>](#footnotes)|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai L connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Genesis&model=GV70 (2.5T Trim, without HDA II) 2022-24">Buy Here</a></sub></details>||
|
||||
|Genesis|GV70 (3.5T Trim, without HDA II) 2022-23[<sup>5</sup>](#footnotes)|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai M connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Genesis&model=GV70 (3.5T Trim, without HDA II) 2022-23">Buy Here</a></sub></details>||
|
||||
|Genesis|GV70 Electrified (Australia Only) 2022[<sup>5</sup>](#footnotes)|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai Q connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Genesis&model=GV70 Electrified (Australia Only) 2022">Buy Here</a></sub></details>||
|
||||
|Genesis|GV70 Electrified (with HDA II) 2023-24[<sup>5</sup>](#footnotes)|Highway Driving Assist II|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai Q connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Genesis&model=GV70 Electrified (with HDA II) 2023-24">Buy Here</a></sub></details>||
|
||||
|Genesis|GV80 2023[<sup>5</sup>](#footnotes)|All|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai M connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Genesis&model=GV80 2023">Buy Here</a></sub></details>||
|
||||
|Genesis|GV80 2023[<sup>5</sup>](#footnotes)|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai M connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Genesis&model=GV80 2023">Buy Here</a></sub></details>||
|
||||
|GMC|Sierra 1500 2020-21|Driver Alert Package II|openpilot available[<sup>1</sup>](#footnotes)|0 mph|6 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 GM connector<br>- 1 comma 3X<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=GMC&model=Sierra 1500 2020-21">Buy Here</a></sub></details>|<a href="https://youtu.be/5HbNoBLzRwE" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Honda|Accord 2018-22|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|3 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Bosch A connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Honda&model=Accord 2018-22">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=mrUwlj3Mi58" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Honda|Accord Hybrid 2018-22|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|3 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Honda Bosch A connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Honda&model=Accord Hybrid 2018-22">Buy Here</a></sub></details>||
|
||||
@@ -132,8 +132,8 @@ A supported vehicle is one that just works when you install a comma device. All
|
||||
|Hyundai|Veloster 2019-20|Smart Cruise Control (SCC)|Stock|5 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai E connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Hyundai&model=Veloster 2019-20">Buy Here</a></sub></details>||
|
||||
|Jeep|Grand Cherokee 2016-18|Adaptive Cruise Control (ACC)|Stock|0 mph|9 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 FCA connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Jeep&model=Grand Cherokee 2016-18">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=eLR9o2JkuRk" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Jeep|Grand Cherokee 2019-21|Adaptive Cruise Control (ACC)|Stock|0 mph|39 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 FCA connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Jeep&model=Grand Cherokee 2019-21">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=jBe4lWnRSu4" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Kia|Carnival 2022-24[<sup>5</sup>](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai A connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Kia&model=Carnival 2022-24">Buy Here</a></sub></details>||
|
||||
|Kia|Carnival (China only) 2023[<sup>5</sup>](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai K connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Kia&model=Carnival (China only) 2023">Buy Here</a></sub></details>||
|
||||
|Kia|Carnival 2022-24[<sup>5</sup>](#footnotes)|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai A connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Kia&model=Carnival 2022-24">Buy Here</a></sub></details>||
|
||||
|Kia|Carnival (China only) 2023[<sup>5</sup>](#footnotes)|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai K connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Kia&model=Carnival (China only) 2023">Buy Here</a></sub></details>||
|
||||
|Kia|Ceed 2019-21|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai E connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Kia&model=Ceed 2019-21">Buy Here</a></sub></details>||
|
||||
|Kia|EV6 (Southeast Asia only) 2022-24[<sup>5</sup>](#footnotes)|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai P connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Kia&model=EV6 (Southeast Asia only) 2022-24">Buy Here</a></sub></details>||
|
||||
|Kia|EV6 (with HDA II) 2022-24[<sup>5</sup>](#footnotes)|Highway Driving Assist II|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai P connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Kia&model=EV6 (with HDA II) 2022-24">Buy Here</a></sub></details>||
|
||||
@@ -162,9 +162,9 @@ A supported vehicle is one that just works when you install a comma device. All
|
||||
|Kia|Seltos 2021|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai A connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Kia&model=Seltos 2021">Buy Here</a></sub></details>||
|
||||
|Kia|Sorento 2018|Advanced Smart Cruise Control & LKAS|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai E connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Kia&model=Sorento 2018">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=Fkh3s6WHJz8" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Kia|Sorento 2019|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai E connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Kia&model=Sorento 2019">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=Fkh3s6WHJz8" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
|Kia|Sorento 2021-23[<sup>5</sup>](#footnotes)|Smart Cruise Control (SCC)|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai K connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Kia&model=Sorento 2021-23">Buy Here</a></sub></details>||
|
||||
|Kia|Sorento Hybrid 2021-23[<sup>5</sup>](#footnotes)|All|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai A connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Kia&model=Sorento Hybrid 2021-23">Buy Here</a></sub></details>||
|
||||
|Kia|Sorento Plug-in Hybrid 2022-23[<sup>5</sup>](#footnotes)|All|Stock|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai A connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Kia&model=Sorento Plug-in Hybrid 2022-23">Buy Here</a></sub></details>||
|
||||
|Kia|Sorento 2021-23[<sup>5</sup>](#footnotes)|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai K connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Kia&model=Sorento 2021-23">Buy Here</a></sub></details>||
|
||||
|Kia|Sorento Hybrid 2021-23[<sup>5</sup>](#footnotes)|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai A connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Kia&model=Sorento Hybrid 2021-23">Buy Here</a></sub></details>||
|
||||
|Kia|Sorento Plug-in Hybrid 2022-23[<sup>5</sup>](#footnotes)|All|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai A connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Kia&model=Sorento Plug-in Hybrid 2022-23">Buy Here</a></sub></details>||
|
||||
|Kia|Sportage 2023-24[<sup>5</sup>](#footnotes)|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai N connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Kia&model=Sportage 2023-24">Buy Here</a></sub></details>||
|
||||
|Kia|Sportage Hybrid 2023[<sup>5</sup>](#footnotes)|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai N connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Kia&model=Sportage Hybrid 2023">Buy Here</a></sub></details>||
|
||||
|Kia|Stinger 2018-20|Smart Cruise Control (SCC)|openpilot available[<sup>1</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 Hyundai C connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Kia&model=Stinger 2018-20">Buy Here</a></sub></details>|<a href="https://www.youtube.com/watch?v=MJ94qoofYw0" target="_blank"><img height="18px" src="assets/icon-youtube.svg"></img></a>|
|
||||
@@ -312,7 +312,7 @@ A supported vehicle is one that just works when you install a comma device. All
|
||||
|Volkswagen|Touran 2016-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,13</sup>](#footnotes)|0 mph|0 mph|[](##)|[](##)|<details><summary>Parts</summary><sub>- 1 USB-C coupler<br>- 1 VW J533 connector<br>- 1 comma 3X<br>- 1 comma power v2<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Volkswagen&model=Touran 2016-23">Buy Here</a></sub></details>||
|
||||
|
||||
### Footnotes
|
||||
<sup>1</sup>openpilot Longitudinal Control (Alpha) is available behind a toggle; the toggle is only available in non-release branches such as `devel` or `master-ci`. <br />
|
||||
<sup>1</sup>openpilot Longitudinal Control (Alpha) is available behind a toggle; the toggle is only available in non-release branches such as `devel` or `nightly-dev`. <br />
|
||||
<sup>2</sup>By default, this car will use the stock Adaptive Cruise Control (ACC) for longitudinal control. If the Driver Support Unit (DSU) is disconnected, openpilot ACC will replace stock ACC. <b><i>NOTE: disconnecting the DSU disables Automatic Emergency Braking (AEB).</i></b> <br />
|
||||
<sup>3</sup>Refers only to the Focus Mk4 (C519) available in Europe/China/Taiwan/Australasia, not the Focus Mk3 (C346) in North and South America/Southeast Asia. <br />
|
||||
<sup>4</sup>2019 Honda Civic 1.6L Diesel Sedan does not have ALC below 12mph. <br />
|
||||
|
||||
@@ -16,7 +16,7 @@ a [learned simulator](https://youtu.be/EqQNZXqzFSI).
|
||||
* Always-on driver monitoring (behind a toggle)
|
||||
* GPS removed from the driving stack
|
||||
* 100KB qlogs
|
||||
* `master-ci` pushed after 1000 hours of hardware-in-the-loop testing
|
||||
* `nightly` pushed after 1000 hours of hardware-in-the-loop testing
|
||||
* Car interface code moved into [opendbc](https://github.com/commaai/opendbc)
|
||||
* openpilot on PC for Linux x86, Linux arm64, and Mac (Apple Silicon)
|
||||
|
||||
|
||||
+2
-6
@@ -1,13 +1,9 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
if [ -z "$BASEDIR" ]; then
|
||||
BASEDIR="/data/openpilot"
|
||||
fi
|
||||
|
||||
source "$BASEDIR/launch_env.sh"
|
||||
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
|
||||
|
||||
source "$DIR/launch_env.sh"
|
||||
|
||||
function agnos_init {
|
||||
# TODO: move this to agnos
|
||||
sudo rm -f /data/etc/NetworkManager/system-connections/*.nmmeta
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ export OPENBLAS_NUM_THREADS=1
|
||||
export VECLIB_MAXIMUM_THREADS=1
|
||||
|
||||
if [ -z "$AGNOS_VERSION" ]; then
|
||||
export AGNOS_VERSION="11.8"
|
||||
export AGNOS_VERSION="11.10"
|
||||
fi
|
||||
|
||||
export STAGING_ROOT="/data/safe_staging"
|
||||
|
||||
+1
-1
Submodule msgq_repo updated: 095f1e2361...ad9020c430
+1
-1
Submodule opendbc_repo updated: da47550d30...e98cbe9f08
+1
-1
Submodule panda updated: 0924df1e8e...1d5b89956b
+2
-1
@@ -143,6 +143,7 @@ asyncio_default_fixture_loop_scope = "function"
|
||||
markers = [
|
||||
"slow: tests that take awhile to run and can be skipped with -m 'not slow'",
|
||||
"tici: tests that are only meant to run on the C3/C3X",
|
||||
"skip_tici_setup: mark test to skip tici setup fixture"
|
||||
]
|
||||
testpaths = [
|
||||
"common",
|
||||
@@ -215,7 +216,7 @@ lint.select = [
|
||||
"E", "F", "W", "PIE", "C4", "ISC", "A", "B",
|
||||
"NPY", # numpy
|
||||
"UP", # pyupgrade
|
||||
"TRY302", "TRY400", "TRY401", # try/excepts
|
||||
"TRY203", "TRY400", "TRY401", # try/excepts
|
||||
"RUF008", "RUF100",
|
||||
"TID251",
|
||||
"PLR1704",
|
||||
|
||||
+10
-6
@@ -19,15 +19,15 @@ cd $TARGET_DIR
|
||||
cp -r $SOURCE_DIR/.git $TARGET_DIR
|
||||
pre-commit uninstall || true
|
||||
|
||||
echo "[-] bringing master-ci and devel in sync T=$SECONDS"
|
||||
echo "[-] bringing __nightly and devel in sync T=$SECONDS"
|
||||
cd $TARGET_DIR
|
||||
|
||||
git fetch --depth 1 origin master-ci
|
||||
git fetch --depth 1 origin __nightly
|
||||
git fetch --depth 1 origin devel
|
||||
|
||||
git checkout -f --track origin/master-ci
|
||||
git reset --hard master-ci
|
||||
git checkout master-ci
|
||||
git checkout -f --track origin/__nightly
|
||||
git reset --hard __nightly
|
||||
git checkout __nightly
|
||||
git reset --hard origin/devel
|
||||
git clean -xdff
|
||||
git lfs uninstall
|
||||
@@ -51,9 +51,13 @@ rm -f panda/board/obj/panda.bin.signed
|
||||
|
||||
# include source commit hash and build date in commit
|
||||
GIT_HASH=$(git --git-dir=$SOURCE_DIR/.git rev-parse HEAD)
|
||||
GIT_COMMIT_DATE=$(git --git-dir=$SOURCE_DIR/.git show --no-patch --format='%ct %ci' HEAD)
|
||||
DATETIME=$(date '+%Y-%m-%dT%H:%M:%S')
|
||||
VERSION=$(cat $SOURCE_DIR/common/version.h | awk -F\" '{print $2}')
|
||||
|
||||
echo -n "$GIT_HASH" > git_src_commit
|
||||
echo -n "$GIT_COMMIT_DATE" > git_src_commit_date
|
||||
|
||||
echo "[-] committing version $VERSION T=$SECONDS"
|
||||
git add -f .
|
||||
git status
|
||||
@@ -81,7 +85,7 @@ fi
|
||||
|
||||
if [ ! -z "$BRANCH" ]; then
|
||||
echo "[-] Pushing to $BRANCH T=$SECONDS"
|
||||
git push -f origin master-ci:$BRANCH
|
||||
git push -f origin __nightly:$BRANCH
|
||||
fi
|
||||
|
||||
echo "[-] done T=$SECONDS, ready at $TARGET_DIR"
|
||||
|
||||
@@ -74,7 +74,8 @@ find . -name '*.pyc' -delete
|
||||
find . -name 'moc_*' -delete
|
||||
find . -name '__pycache__' -delete
|
||||
rm -rf .sconsign.dblite Jenkinsfile release/
|
||||
rm selfdrive/modeld/models/supercombo.onnx
|
||||
rm selfdrive/modeld/models/driving_vision.onnx
|
||||
rm selfdrive/modeld/models/driving_policy.onnx
|
||||
|
||||
find third_party/ -name '*x86*' -exec rm -r {} +
|
||||
find third_party/ -name '*Darwin*' -exec rm -r {} +
|
||||
@@ -91,14 +92,9 @@ git add -f .
|
||||
git commit --amend -m "openpilot v$VERSION"
|
||||
|
||||
# Run tests
|
||||
TEST_FILES="tools/"
|
||||
cd $SOURCE_DIR
|
||||
cp -pR -n --parents $TEST_FILES $BUILD_DIR/
|
||||
cd $BUILD_DIR
|
||||
RELEASE=1 pytest -n0 -s selfdrive/test/test_onroad.py
|
||||
#system/manager/test/test_manager.py
|
||||
pytest selfdrive/car/tests/test_car_interfaces.py
|
||||
rm -rf $TEST_FILES
|
||||
#pytest selfdrive/car/tests/test_car_interfaces.py
|
||||
|
||||
if [ ! -z "$RELEASE_BRANCH" ]; then
|
||||
echo "[-] pushing release T=$SECONDS"
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>openpilot Terms of Service</title>
|
||||
<style type="text/css">
|
||||
p {
|
||||
margin: 45px 0;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<p>The Terms and Conditions below are effective for all users</p>
|
||||
<p>Last Updated on October 18, 2019</p>
|
||||
<p>Please read these Terms of Use (“Terms”) carefully before using openpilot which is open-sourced software developed by Comma.ai, Inc., a corporation organized under the laws of Delaware (“comma,” “us,” “we,” or “our”).</p>
|
||||
<p><strong>Before using and by accessing openpilot, you indicate that you have read, understood, and agree to these Terms.</strong> These Terms apply to all users and others who access or use openpilot. If others use openpilot through your user account or vehicle, you are responsible to ensure that they only use openpilot when it is safe to do so, and in compliance with these Terms and with applicable law. If you disagree with any part of the Terms, you should not access or use openpilot.</p>
|
||||
<h3 id="communications">Communications</h3>
|
||||
<p>You agree that comma may contact you by email or telephone in connection with openpilot or for other business purposes. You may opt out of receiving email messages at any time by contacting us at support@comma.ai.</p>
|
||||
<p>We collect, use, and share information from and about you and your vehicle in connection with openpilot. You consent to comma accessing the systems associated with openpilot, without additional notice or consent, for the purposes of providing openpilot, data collection, software updates, safety and cybersecurity, suspension or removal of your account, and as disclosed in the Privacy Policy (available at https://connect.comma.ai/privacy).</p>
|
||||
<h3 id="safety">Safety</h3>
|
||||
<p>openpilot performs the functions of Adaptive Cruise Control (ACC) and Lane Keeping Assist System (LKAS) designed for use in compatible motor vehicles. While using openpilot, it is your responsibility to obey all laws, traffic rules, and traffic regulations governing your vehicle and its operation. Access to and use of openpilot is at your own risk and responsibility, and openpilot should be accessed and/or used only when you can do so safely.</p>
|
||||
<p>openpilot does not make your vehicle “autonomous” or capable of operation without the active monitoring of a licensed driver. It is designed to assist a licensed driver. A licensed driver must pay attention to the road, remain aware of navigation at all times, and be prepared to take immediate action. Failure to do so can cause damage, injury, or death.</p>
|
||||
<h3 id="supported-locations-and-models">Supported Locations and Models</h3>
|
||||
<p>openpilot is compatible only with particular makes and models of vehicles. For a complete list of currently supported vehicles, visit https://comma.ai. openpilot will not function properly when installed in an incompatible vehicle. openpilot is compatible only within the geographical boundaries of the United States of America.</p>
|
||||
<h3 id="indemnification">Indemnification</h3>
|
||||
<p><strong>To the maximum extent allowable by law, you agree to defend, indemnify and hold harmless comma, and its employees, partners, suppliers, contractors, investors, agents, officers, directors, and affiliates, from and against any and all claims, damages, causes of action, penalties, interest, demands, obligations, losses, liabilities, costs or debt, additional taxes, and expenses (including but not limited to attorneys’ fees), resulting from or arising out of (i) your use and access of, or inability to use or access, openpilot, (ii) your breach of these Terms, (iii) the inaccuracy of any information, representation or warranty made by you, (iv) activities of anyone other than you in connection with openpilot conducted through your comma device or account, (v) any other of your activities under or in connection with these Terms or openpilot.</strong></p>
|
||||
<h3 id="limitation-of-liability">Limitation of Liability</h3>
|
||||
<p>In no event shall comma, nor its directors, employees, partners, agents, suppliers, or affiliates, be liable for any indirect, incidental, special, consequential or punitive damages, including without limitation, loss of profits, data, use, goodwill, or other intangible losses, resulting from (i) your access to or use of or inability to access or use of the Software; or (ii) any conduct or content of any third party on the Software whether based on warranty, contract, tort (including negligence) or any other legal theory, whether or not we have been informed of the possibility of such damage, and even if a remedy set forth herein is found to have failed of its essential purpose.</p>
|
||||
<h3 id="no-warranty-or-obligations-to-maintain-or-service">No Warranty or Obligations to Maintain or Service</h3>
|
||||
<p>comma provides openpilot without representations, conditions, or warranties of any kind. openpilot is provided on an “AS IS” and “AS AVAILABLE” basis, including with all faults and errors as may occur. To the extent permitted by law and unless prohibited by law, comma on behalf of itself and all persons and parties acting by, through, or for comma, explicitly disclaims all warranties or conditions, express, implied, or collateral, including any implied warranties of merchantability, satisfactory quality, and fitness for a particular purpose in respect of openpilot.</p>
|
||||
<p>To the extent permitted by law, comma does not warrant the operation, performance, or availability of openpilot under all conditions. comma is not responsible for any failures caused by server errors, misdirected or redirected transmissions, failed internet connections, interruptions or failures in the transmission of data, any computer virus, or any acts or omissions of third parties that damage the network or impair wireless service.</p>
|
||||
<p>We undertake reasonable measures to preserve and secure information collected through our openpilot. However, no data collection, transmission or storage system is 100% secure, and there is always a risk that your information may be intercepted without our consent. <em>In using openpilot, you acknowledge that comma is not responsible for intercepted information, and you hereby release us from any and all claims arising out of or related to the use of intercepted information in any unauthorized manner.</em></p>
|
||||
<p>By providing openpilot, comma does not transfer or license its intellectual property or grant rights in its brand names, nor does comma make representations with respect to third-party intellectual property rights.</p>
|
||||
<p>We are not obligated to provide any maintenance or support for openpilot, technical or otherwise. If we voluntarily provide any maintenance or support for openpilot, we may stop any such maintenance, support, or services at any time in our sole discretion.</p>
|
||||
<h3 id="modification-of-software">Modification of Software</h3>
|
||||
<p>In no event shall comma, nor its directors, employees, partners, agents, suppliers, or affiliates, be liable if you choose to modify the software.</p>
|
||||
<h3 id="changes">Changes</h3>
|
||||
<p>We reserve the right, at our sole discretion, to modify or replace these Terms at any time. If a revision is material we will provide at least 15 days’ notice prior to any new terms taking effect. What constitutes a material change will be determined at our sole discretion.</p>
|
||||
<p>By continuing to access or use our Software after any revisions become effective, you agree to be bound by the revised terms. If you do not agree to the new terms, you are no longer authorized to use the Software.</p>
|
||||
<h3 id="contact-us">Contact Us</h3>
|
||||
<p>If you have any questions about these Terms, please contact us at support@comma.ai.</p>
|
||||
</body>
|
||||
</html>
|
||||
@@ -12,11 +12,11 @@
|
||||
|
||||
A supported vehicle is one that just works when you install a comma device. All supported cars provide a better experience than any stock system. Supported vehicles reference the US market unless otherwise specified.
|
||||
|
||||
# {{all_car_docs | length}} Supported Cars
|
||||
# {{all_car_docs | selectattr('support_type', 'eq', SupportType.UPSTREAM) | list | length}} Supported Cars
|
||||
|
||||
|{{Column | map(attribute='value') | join('|') | replace(hardware_col_name, wide_hardware_col_name)}}|
|
||||
|---|---|---|{% for _ in range((Column | length) - 3) %}{{':---:|'}}{% endfor +%}
|
||||
{% for car_docs in all_car_docs %}
|
||||
{% for car_docs in all_car_docs | selectattr('support_type', 'eq', SupportType.UPSTREAM) %}
|
||||
|{% for column in Column %}{{car_docs.get_column(column, star_icon, video_icon, footnote_tag)}}|{% endfor %}
|
||||
|
||||
{% endfor %}
|
||||
|
||||
@@ -56,6 +56,9 @@ class VCruiseHelper:
|
||||
if CS.cruiseState.speed == 0:
|
||||
self.v_cruise_kph = V_CRUISE_UNSET
|
||||
self.v_cruise_cluster_kph = V_CRUISE_UNSET
|
||||
elif CS.cruiseState.speed == -1:
|
||||
self.v_cruise_kph = -1
|
||||
self.v_cruise_cluster_kph = -1
|
||||
else:
|
||||
self.v_cruise_kph = V_CRUISE_UNSET
|
||||
self.v_cruise_cluster_kph = V_CRUISE_UNSET
|
||||
|
||||
@@ -80,6 +80,8 @@ class TestCarInterfaces:
|
||||
|
||||
CC = car.CarControl.new_message(**cc_msg)
|
||||
CC.enabled = True
|
||||
CC.latActive = True
|
||||
CC.longActive = True
|
||||
CC = CC.as_reader()
|
||||
for _ in range(10):
|
||||
car_interface.update([])
|
||||
|
||||
@@ -28,6 +28,7 @@ from openpilot.tools.lib.logreader import LogReader, LogsUnavailable, openpilotc
|
||||
from openpilot.tools.lib.route import SegmentName
|
||||
|
||||
SafetyModel = car.CarParams.SafetyModel
|
||||
SteerControlType = structs.CarParams.SteerControlType
|
||||
|
||||
NUM_JOBS = int(os.environ.get("NUM_JOBS", "1"))
|
||||
JOB_ID = int(os.environ.get("JOB_ID", "0"))
|
||||
@@ -182,7 +183,7 @@ class TestCarModelBase(unittest.TestCase):
|
||||
# make sure car params are within a valid range
|
||||
self.assertGreater(self.CP.mass, 1)
|
||||
|
||||
if self.CP.steerControlType != structs.CarParams.SteerControlType.angle:
|
||||
if self.CP.steerControlType != SteerControlType.angle:
|
||||
tuning = self.CP.lateralTuning.which()
|
||||
if tuning == 'pid':
|
||||
self.assertTrue(len(self.CP.lateralTuning.pid.kpV))
|
||||
@@ -265,6 +266,9 @@ class TestCarModelBase(unittest.TestCase):
|
||||
|
||||
def test_panda_safety_tx_cases(self, data=None):
|
||||
"""Asserts we can tx common messages"""
|
||||
if self.CP.dashcamOnly:
|
||||
self.skipTest("no need to check panda safety for dashcamOnly")
|
||||
|
||||
if self.CP.notCar:
|
||||
self.skipTest("Skipping test for notCar")
|
||||
|
||||
@@ -319,6 +323,8 @@ class TestCarModelBase(unittest.TestCase):
|
||||
msg_strategy = st.binary(min_size=size, max_size=size)
|
||||
msgs = data.draw(st.lists(msg_strategy, min_size=20))
|
||||
|
||||
vehicle_speed_seen = self.CP.steerControlType == SteerControlType.angle and not self.CP.notCar
|
||||
|
||||
for dat in msgs:
|
||||
# due to panda updating state selectively, only edges are expected to match
|
||||
# TODO: warm up CarState with real CAN messages to check edge of both sources
|
||||
@@ -327,6 +333,8 @@ class TestCarModelBase(unittest.TestCase):
|
||||
prev_panda_brake = self.safety.get_brake_pressed_prev()
|
||||
prev_panda_regen_braking = self.safety.get_regen_braking_prev()
|
||||
prev_panda_vehicle_moving = self.safety.get_vehicle_moving()
|
||||
prev_panda_vehicle_speed_min = self.safety.get_vehicle_speed_min()
|
||||
prev_panda_vehicle_speed_max = self.safety.get_vehicle_speed_max()
|
||||
prev_panda_cruise_engaged = self.safety.get_cruise_engaged_prev()
|
||||
prev_panda_acc_main_on = self.safety.get_acc_main_on()
|
||||
|
||||
@@ -354,6 +362,16 @@ class TestCarModelBase(unittest.TestCase):
|
||||
if self.safety.get_vehicle_moving() != prev_panda_vehicle_moving:
|
||||
self.assertEqual(not CS.standstill, self.safety.get_vehicle_moving())
|
||||
|
||||
# check vehicle speed if angle control car or available
|
||||
if self.safety.get_vehicle_speed_min() > 0 or self.safety.get_vehicle_speed_max() > 0:
|
||||
vehicle_speed_seen = True
|
||||
|
||||
if vehicle_speed_seen and (self.safety.get_vehicle_speed_min() != prev_panda_vehicle_speed_min or
|
||||
self.safety.get_vehicle_speed_max() != prev_panda_vehicle_speed_max):
|
||||
v_ego_raw = CS.vEgoRaw / self.CP.wheelSpeedFactor
|
||||
self.assertFalse(v_ego_raw > (self.safety.get_vehicle_speed_max() + 1e-3) or
|
||||
v_ego_raw < (self.safety.get_vehicle_speed_min() - 1e-3))
|
||||
|
||||
if not (self.CP.brand == "honda" and not (self.CP.flags & HondaFlags.BOSCH)):
|
||||
if self.safety.get_cruise_engaged_prev() != prev_panda_cruise_engaged:
|
||||
self.assertEqual(CS.cruiseState.enabled, self.safety.get_cruise_engaged_prev())
|
||||
@@ -379,6 +397,7 @@ class TestCarModelBase(unittest.TestCase):
|
||||
controls_allowed_prev = False
|
||||
CS_prev = car.CarState.new_message()
|
||||
checks = defaultdict(int)
|
||||
vehicle_speed_seen = self.CP.steerControlType == SteerControlType.angle and not self.CP.notCar
|
||||
for idx, can in enumerate(self.can_msgs):
|
||||
CS = self.CI.update(can_capnp_to_list((can.as_builder().to_bytes(), ))).as_reader()
|
||||
for msg in filter(lambda m: m.src in range(64), can.can):
|
||||
@@ -399,6 +418,15 @@ class TestCarModelBase(unittest.TestCase):
|
||||
checks['gasPressed'] += CS.gasPressed != self.safety.get_gas_pressed_prev()
|
||||
checks['standstill'] += CS.standstill == self.safety.get_vehicle_moving()
|
||||
|
||||
# check vehicle speed if angle control car or available
|
||||
if self.safety.get_vehicle_speed_min() > 0 or self.safety.get_vehicle_speed_max() > 0:
|
||||
vehicle_speed_seen = True
|
||||
|
||||
if vehicle_speed_seen:
|
||||
v_ego_raw = CS.vEgoRaw / self.CP.wheelSpeedFactor
|
||||
checks['vEgoRaw'] += (v_ego_raw > (self.safety.get_vehicle_speed_max() + 1e-3) or
|
||||
v_ego_raw < (self.safety.get_vehicle_speed_min() - 1e-3))
|
||||
|
||||
# TODO: remove this exception once this mismatch is resolved
|
||||
brake_pressed = CS.brakePressed
|
||||
if CS.brakePressed and not self.safety.get_brake_pressed_prev():
|
||||
|
||||
@@ -10,22 +10,22 @@ from openpilot.common.realtime import config_realtime_process, Priority, Ratekee
|
||||
from openpilot.common.swaglog import cloudlog
|
||||
|
||||
from opendbc.car.car_helpers import get_car_interface
|
||||
from opendbc.car.vehicle_model import VehicleModel
|
||||
from openpilot.selfdrive.controls.lib.drive_helpers import clip_curvature
|
||||
from openpilot.selfdrive.controls.lib.latcontrol import LatControl, MIN_LATERAL_CONTROL_SPEED
|
||||
from openpilot.selfdrive.controls.lib.latcontrol_pid import LatControlPID
|
||||
from openpilot.selfdrive.controls.lib.latcontrol_angle import LatControlAngle, STEER_ANGLE_SATURATION_THRESHOLD
|
||||
from openpilot.selfdrive.controls.lib.latcontrol_torque import LatControlTorque
|
||||
from openpilot.selfdrive.controls.lib.longcontrol import LongControl
|
||||
from openpilot.selfdrive.controls.lib.vehicle_model import VehicleModel
|
||||
from openpilot.selfdrive.locationd.helpers import PoseCalibrator, Pose
|
||||
|
||||
|
||||
State = log.SelfdriveState.OpenpilotState
|
||||
LaneChangeState = log.LaneChangeState
|
||||
LaneChangeDirection = log.LaneChangeDirection
|
||||
|
||||
ACTUATOR_FIELDS = tuple(car.CarControl.Actuators.schema.fields.keys())
|
||||
|
||||
|
||||
class Controls:
|
||||
def __init__(self) -> None:
|
||||
self.params = Params()
|
||||
@@ -40,11 +40,11 @@ class Controls:
|
||||
'driverMonitoringState', 'onroadEvents', 'driverAssistance'], poll='selfdriveState')
|
||||
self.pm = messaging.PubMaster(['carControl', 'controlsState'])
|
||||
|
||||
self.steer_limited = False
|
||||
self.steer_limited_by_controls = False
|
||||
self.desired_curvature = 0.0
|
||||
|
||||
self.pose_calibrator = PoseCalibrator()
|
||||
self.calibrated_pose: Pose|None = None
|
||||
self.calibrated_pose: Pose | None = None
|
||||
|
||||
self.LoC = LongControl(self.CP)
|
||||
self.VM = VehicleModel(self.CP)
|
||||
@@ -109,12 +109,12 @@ class Controls:
|
||||
actuators.accel = float(self.LoC.update(CC.longActive, CS, long_plan.aTarget, long_plan.shouldStop, pid_accel_limits))
|
||||
|
||||
# Steering PID loop and lateral MPC
|
||||
self.desired_curvature = clip_curvature(CS.vEgo, self.desired_curvature, model_v2.action.desiredCurvature)
|
||||
actuators.curvature = float(self.desired_curvature)
|
||||
self.desired_curvature, curvature_limited = clip_curvature(CS.vEgo, self.desired_curvature, model_v2.action.desiredCurvature, lp.roll)
|
||||
actuators.curvature = self.desired_curvature
|
||||
steer, steeringAngleDeg, lac_log = self.LaC.update(CC.latActive, CS, self.VM, lp,
|
||||
self.steer_limited, self.desired_curvature,
|
||||
self.calibrated_pose) # TODO what if not available
|
||||
actuators.steer = float(steer)
|
||||
self.steer_limited_by_controls, self.desired_curvature,
|
||||
self.calibrated_pose, curvature_limited) # TODO what if not available
|
||||
actuators.torque = float(steer)
|
||||
actuators.steeringAngleDeg = float(steeringAngleDeg)
|
||||
# Ensure no NaNs/Infs
|
||||
for p in ACTUATOR_FIELDS:
|
||||
@@ -161,10 +161,10 @@ class Controls:
|
||||
if self.sm['selfdriveState'].active:
|
||||
CO = self.sm['carOutput']
|
||||
if self.CP.steerControlType == car.CarParams.SteerControlType.angle:
|
||||
self.steer_limited = abs(CC.actuators.steeringAngleDeg - CO.actuatorsOutput.steeringAngleDeg) > \
|
||||
STEER_ANGLE_SATURATION_THRESHOLD
|
||||
self.steer_limited_by_controls = abs(CC.actuators.steeringAngleDeg - CO.actuatorsOutput.steeringAngleDeg) > \
|
||||
STEER_ANGLE_SATURATION_THRESHOLD
|
||||
else:
|
||||
self.steer_limited = abs(CC.actuators.steer - CO.actuatorsOutput.steer) > 1e-2
|
||||
self.steer_limited_by_controls = abs(CC.actuators.torque - CO.actuatorsOutput.torque) > 1e-2
|
||||
|
||||
# TODO: both controlsState and carControl valids should be set by
|
||||
# sm.all_checks(), but this creates a circular dependency
|
||||
@@ -180,7 +180,7 @@ class Controls:
|
||||
|
||||
cs.longitudinalPlanMonoTime = self.sm.logMonoTime['longitudinalPlan']
|
||||
cs.lateralPlanMonoTime = self.sm.logMonoTime['modelV2']
|
||||
cs.desiredCurvature = float(self.desired_curvature)
|
||||
cs.desiredCurvature = self.desired_curvature
|
||||
cs.longControlState = self.LoC.long_control_state
|
||||
cs.upAccelCmd = float(self.LoC.pid.p)
|
||||
cs.uiAccelCmd = float(self.LoC.pid.i)
|
||||
@@ -212,6 +212,7 @@ class Controls:
|
||||
self.publish(CC, lac_log)
|
||||
rk.monitor_time()
|
||||
|
||||
|
||||
def main():
|
||||
config_realtime_process(4, Priority.CTRL_HIGH)
|
||||
controls = Controls()
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import numpy as np
|
||||
from cereal import log
|
||||
from opendbc.car.vehicle_model import ACCELERATION_DUE_TO_GRAVITY
|
||||
from openpilot.common.realtime import DT_CTRL
|
||||
|
||||
MIN_SPEED = 1.0
|
||||
@@ -7,20 +8,33 @@ CONTROL_N = 17
|
||||
CAR_ROTATION_RADIUS = 0.0
|
||||
# This is a turn radius smaller than most cars can achieve
|
||||
MAX_CURVATURE = 0.2
|
||||
MAX_VEL_ERR = 5.0 # m/s
|
||||
|
||||
# EU guidelines
|
||||
MAX_LATERAL_JERK = 5.0
|
||||
MAX_VEL_ERR = 5.0
|
||||
MAX_LATERAL_JERK = 5.0 # m/s^3
|
||||
MAX_LATERAL_ACCEL_NO_ROLL = 3.0 # m/s^2
|
||||
|
||||
def clip_curvature(v_ego, prev_curvature, new_curvature):
|
||||
new_curvature = np.clip(new_curvature, -MAX_CURVATURE, MAX_CURVATURE)
|
||||
v_ego = max(MIN_SPEED, v_ego)
|
||||
max_curvature_rate = MAX_LATERAL_JERK / (v_ego**2) # inexact calculation, check https://github.com/commaai/openpilot/pull/24755
|
||||
safe_desired_curvature = np.clip(new_curvature,
|
||||
prev_curvature - max_curvature_rate * DT_CTRL,
|
||||
prev_curvature + max_curvature_rate * DT_CTRL)
|
||||
|
||||
return safe_desired_curvature
|
||||
def clamp(val, min_val, max_val):
|
||||
clamped_val = float(np.clip(val, min_val, max_val))
|
||||
return clamped_val, clamped_val != val
|
||||
|
||||
|
||||
def clip_curvature(v_ego, prev_curvature, new_curvature, roll):
|
||||
# This function respects ISO lateral jerk and acceleration limits + a max curvature
|
||||
v_ego = max(v_ego, MIN_SPEED)
|
||||
max_curvature_rate = MAX_LATERAL_JERK / (v_ego ** 2) # inexact calculation, check https://github.com/commaai/openpilot/pull/24755
|
||||
new_curvature = np.clip(new_curvature,
|
||||
prev_curvature - max_curvature_rate * DT_CTRL,
|
||||
prev_curvature + max_curvature_rate * DT_CTRL)
|
||||
|
||||
roll_compensation = roll * ACCELERATION_DUE_TO_GRAVITY
|
||||
max_lat_accel = MAX_LATERAL_ACCEL_NO_ROLL + roll_compensation
|
||||
min_lat_accel = -MAX_LATERAL_ACCEL_NO_ROLL + roll_compensation
|
||||
new_curvature, limited_accel = clamp(new_curvature, min_lat_accel / v_ego ** 2, max_lat_accel / v_ego ** 2)
|
||||
|
||||
new_curvature, limited_max_curv = clamp(new_curvature, -MAX_CURVATURE, MAX_CURVATURE)
|
||||
return float(new_curvature), limited_accel or limited_max_curv
|
||||
|
||||
|
||||
def get_speed_error(modelV2: log.ModelDataV2, v_ego: float) -> float:
|
||||
|
||||
@@ -17,14 +17,15 @@ class LatControl(ABC):
|
||||
self.steer_max = 1.0
|
||||
|
||||
@abstractmethod
|
||||
def update(self, active, CS, VM, params, steer_limited, desired_curvature, calibrated_pose):
|
||||
def update(self, active, CS, VM, params, steer_limited_by_controls, desired_curvature, calibrated_pose, curvature_limited):
|
||||
pass
|
||||
|
||||
def reset(self):
|
||||
self.sat_count = 0.
|
||||
|
||||
def _check_saturation(self, saturated, CS, steer_limited):
|
||||
if saturated and CS.vEgo > self.sat_check_min_speed and not steer_limited and not CS.steeringPressed:
|
||||
def _check_saturation(self, saturated, CS, steer_limited_by_controls, curvature_limited):
|
||||
# Saturated only if control output is not being limited by car torque/angle rate limits
|
||||
if (saturated or curvature_limited) and CS.vEgo > self.sat_check_min_speed and not steer_limited_by_controls and not CS.steeringPressed:
|
||||
self.sat_count += self.sat_count_rate
|
||||
else:
|
||||
self.sat_count -= self.sat_count_rate
|
||||
|
||||
@@ -11,7 +11,7 @@ class LatControlAngle(LatControl):
|
||||
super().__init__(CP, CI)
|
||||
self.sat_check_min_speed = 5.
|
||||
|
||||
def update(self, active, CS, VM, params, steer_limited, desired_curvature, calibrated_pose):
|
||||
def update(self, active, CS, VM, params, steer_limited_by_controls, desired_curvature, calibrated_pose, curvature_limited):
|
||||
angle_log = log.ControlsState.LateralAngleState.new_message()
|
||||
|
||||
if not active:
|
||||
@@ -23,7 +23,7 @@ class LatControlAngle(LatControl):
|
||||
angle_steers_des += params.angleOffsetDeg
|
||||
|
||||
angle_control_saturated = abs(angle_steers_des - CS.steeringAngleDeg) > STEER_ANGLE_SATURATION_THRESHOLD
|
||||
angle_log.saturated = bool(self._check_saturation(angle_control_saturated, CS, False))
|
||||
angle_log.saturated = bool(self._check_saturation(angle_control_saturated, CS, False, curvature_limited))
|
||||
angle_log.steeringAngleDeg = float(CS.steeringAngleDeg)
|
||||
angle_log.steeringAngleDesiredDeg = angle_steers_des
|
||||
return 0, float(angle_steers_des), angle_log
|
||||
|
||||
@@ -17,7 +17,7 @@ class LatControlPID(LatControl):
|
||||
super().reset()
|
||||
self.pid.reset()
|
||||
|
||||
def update(self, active, CS, VM, params, steer_limited, desired_curvature, calibrated_pose):
|
||||
def update(self, active, CS, VM, params, steer_limited_by_controls, desired_curvature, calibrated_pose, curvature_limited):
|
||||
pid_log = log.ControlsState.LateralPIDState.new_message()
|
||||
pid_log.steeringAngleDeg = float(CS.steeringAngleDeg)
|
||||
pid_log.steeringRateDeg = float(CS.steeringRateDeg)
|
||||
@@ -43,6 +43,6 @@ class LatControlPID(LatControl):
|
||||
pid_log.i = float(self.pid.i)
|
||||
pid_log.f = float(self.pid.f)
|
||||
pid_log.output = float(output_steer)
|
||||
pid_log.saturated = bool(self._check_saturation(self.steer_max - abs(output_steer) < 1e-3, CS, steer_limited))
|
||||
pid_log.saturated = bool(self._check_saturation(self.steer_max - abs(output_steer) < 1e-3, CS, steer_limited_by_controls, curvature_limited))
|
||||
|
||||
return output_steer, angle_steers_des, pid_log
|
||||
|
||||
@@ -3,9 +3,9 @@ import numpy as np
|
||||
|
||||
from cereal import log
|
||||
from opendbc.car.interfaces import LatControlInputs
|
||||
from opendbc.car.vehicle_model import ACCELERATION_DUE_TO_GRAVITY
|
||||
from openpilot.selfdrive.controls.lib.latcontrol import LatControl
|
||||
from openpilot.common.pid import PIDController
|
||||
from openpilot.selfdrive.controls.lib.vehicle_model import ACCELERATION_DUE_TO_GRAVITY
|
||||
|
||||
# At higher speeds (25+mph) we can assume:
|
||||
# Lateral acceleration achieved by a specific car correlates to
|
||||
@@ -37,7 +37,7 @@ class LatControlTorque(LatControl):
|
||||
self.torque_params.latAccelOffset = latAccelOffset
|
||||
self.torque_params.friction = friction
|
||||
|
||||
def update(self, active, CS, VM, params, steer_limited, desired_curvature, calibrated_pose):
|
||||
def update(self, active, CS, VM, params, steer_limited_by_controls, desired_curvature, calibrated_pose, curvature_limited):
|
||||
pid_log = log.ControlsState.LateralTorqueState.new_message()
|
||||
if not active:
|
||||
output_torque = 0.0
|
||||
@@ -73,7 +73,7 @@ class LatControlTorque(LatControl):
|
||||
desired_lateral_accel - actual_lateral_accel, lateral_accel_deadzone, friction_compensation=True,
|
||||
gravity_adjusted=True)
|
||||
|
||||
freeze_integrator = steer_limited or CS.steeringPressed or CS.vEgo < 5
|
||||
freeze_integrator = steer_limited_by_controls or CS.steeringPressed or CS.vEgo < 5
|
||||
output_torque = self.pid.update(pid_log.error,
|
||||
feedforward=ff,
|
||||
speed=CS.vEgo,
|
||||
@@ -87,7 +87,7 @@ class LatControlTorque(LatControl):
|
||||
pid_log.output = float(-output_torque)
|
||||
pid_log.actualLateralAccel = float(actual_lateral_accel)
|
||||
pid_log.desiredLateralAccel = float(desired_lateral_accel)
|
||||
pid_log.saturated = bool(self._check_saturation(self.steer_max - abs(output_torque) < 1e-3, CS, steer_limited))
|
||||
pid_log.saturated = bool(self._check_saturation(self.steer_max - abs(output_torque) < 1e-3, CS, steer_limited_by_controls, curvature_limited))
|
||||
|
||||
# TODO left is positive in this convention
|
||||
return -output_torque, 0.0, pid_log
|
||||
|
||||
@@ -5,10 +5,10 @@ from opendbc.car.car_helpers import interfaces
|
||||
from opendbc.car.honda.values import CAR as HONDA
|
||||
from opendbc.car.toyota.values import CAR as TOYOTA
|
||||
from opendbc.car.nissan.values import CAR as NISSAN
|
||||
from opendbc.car.vehicle_model import VehicleModel
|
||||
from openpilot.selfdrive.controls.lib.latcontrol_pid import LatControlPID
|
||||
from openpilot.selfdrive.controls.lib.latcontrol_torque import LatControlTorque
|
||||
from openpilot.selfdrive.controls.lib.latcontrol_angle import LatControlAngle
|
||||
from openpilot.selfdrive.controls.lib.vehicle_model import VehicleModel
|
||||
from openpilot.selfdrive.locationd.helpers import Pose
|
||||
from openpilot.common.mock.generators import generate_livePose
|
||||
|
||||
@@ -33,7 +33,15 @@ class TestLatControl:
|
||||
lp = generate_livePose()
|
||||
pose = Pose.from_live_pose(lp.livePose)
|
||||
|
||||
# Saturate for curvature limited and controller limited
|
||||
for _ in range(1000):
|
||||
_, _, lac_log = controller.update(True, CS, VM, params, False, 1, pose)
|
||||
|
||||
_, _, lac_log = controller.update(True, CS, VM, params, False, 0, pose, True)
|
||||
assert lac_log.saturated
|
||||
|
||||
for _ in range(1000):
|
||||
_, _, lac_log = controller.update(True, CS, VM, params, False, 0, pose, False)
|
||||
assert not lac_log.saturated
|
||||
|
||||
for _ in range(1000):
|
||||
_, _, lac_log = controller.update(True, CS, VM, params, False, 1, pose, False)
|
||||
assert lac_log.saturated
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
import pytest
|
||||
import math
|
||||
|
||||
import numpy as np
|
||||
|
||||
from opendbc.car.honda.interface import CarInterface
|
||||
from opendbc.car.honda.values import CAR
|
||||
from openpilot.selfdrive.controls.lib.vehicle_model import VehicleModel, dyn_ss_sol, create_dyn_state_matrices
|
||||
|
||||
|
||||
class TestVehicleModel:
|
||||
def setup_method(self):
|
||||
CP = CarInterface.get_non_essential_params(CAR.HONDA_CIVIC)
|
||||
self.VM = VehicleModel(CP)
|
||||
|
||||
def test_round_trip_yaw_rate(self):
|
||||
# TODO: fix VM to work at zero speed
|
||||
for u in np.linspace(1, 30, num=10):
|
||||
for roll in np.linspace(math.radians(-20), math.radians(20), num=11):
|
||||
for sa in np.linspace(math.radians(-20), math.radians(20), num=11):
|
||||
yr = self.VM.yaw_rate(sa, u, roll)
|
||||
new_sa = self.VM.get_steer_from_yaw_rate(yr, u, roll)
|
||||
|
||||
assert sa == pytest.approx(new_sa)
|
||||
|
||||
def test_dyn_ss_sol_against_yaw_rate(self):
|
||||
"""Verify that the yaw_rate helper function matches the results
|
||||
from the state space model."""
|
||||
|
||||
for roll in np.linspace(math.radians(-20), math.radians(20), num=11):
|
||||
for u in np.linspace(1, 30, num=10):
|
||||
for sa in np.linspace(math.radians(-20), math.radians(20), num=11):
|
||||
|
||||
# Compute yaw rate based on state space model
|
||||
_, yr1 = dyn_ss_sol(sa, u, roll, self.VM)
|
||||
|
||||
# Compute yaw rate using direct computations
|
||||
yr2 = self.VM.yaw_rate(sa, u, roll)
|
||||
assert float(yr1[0]) == pytest.approx(yr2)
|
||||
|
||||
def test_syn_ss_sol_simulate(self):
|
||||
"""Verifies that dyn_ss_sol matches a simulation"""
|
||||
|
||||
for roll in np.linspace(math.radians(-20), math.radians(20), num=11):
|
||||
for u in np.linspace(1, 30, num=10):
|
||||
A, B = create_dyn_state_matrices(u, self.VM)
|
||||
|
||||
# Convert to discrete time system
|
||||
dt = 0.01
|
||||
top = np.hstack((A, B))
|
||||
full = np.vstack((top, np.zeros_like(top))) * dt
|
||||
Md = sum([np.linalg.matrix_power(full, k) / math.factorial(k) for k in range(25)])
|
||||
Ad = Md[:A.shape[0], :A.shape[1]]
|
||||
Bd = Md[:A.shape[0], A.shape[1]:]
|
||||
|
||||
for sa in np.linspace(math.radians(-20), math.radians(20), num=11):
|
||||
inp = np.array([[sa], [roll]])
|
||||
|
||||
# Simulate for 1 second
|
||||
x1 = np.zeros((2, 1))
|
||||
for _ in range(100):
|
||||
x1 = Ad @ x1 + Bd @ inp
|
||||
|
||||
# Compute steady state solution directly
|
||||
x2 = dyn_ss_sol(sa, u, roll, self.VM)
|
||||
|
||||
np.testing.assert_almost_equal(x1, x2, decimal=3)
|
||||
@@ -1,230 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Dynamic bicycle model from "The Science of Vehicle Dynamics (2014), M. Guiggiani"
|
||||
|
||||
The state is x = [v, r]^T
|
||||
with v lateral speed [m/s], and r rotational speed [rad/s]
|
||||
|
||||
The input u is the steering angle [rad], and roll [rad]
|
||||
|
||||
The system is defined by
|
||||
x_dot = A*x + B*u
|
||||
|
||||
A depends on longitudinal speed, u [m/s], and vehicle parameters CP
|
||||
"""
|
||||
|
||||
import numpy as np
|
||||
from numpy.linalg import solve
|
||||
|
||||
from cereal import car
|
||||
|
||||
ACCELERATION_DUE_TO_GRAVITY = 9.8
|
||||
|
||||
|
||||
class VehicleModel:
|
||||
def __init__(self, CP: car.CarParams):
|
||||
"""
|
||||
Args:
|
||||
CP: Car Parameters
|
||||
"""
|
||||
# for math readability, convert long names car params into short names
|
||||
self.m: float = CP.mass
|
||||
self.j: float = CP.rotationalInertia
|
||||
self.l: float = CP.wheelbase
|
||||
self.aF: float = CP.centerToFront
|
||||
self.aR: float = CP.wheelbase - CP.centerToFront
|
||||
self.chi: float = CP.steerRatioRear
|
||||
|
||||
self.cF_orig: float = CP.tireStiffnessFront
|
||||
self.cR_orig: float = CP.tireStiffnessRear
|
||||
self.update_params(1.0, CP.steerRatio)
|
||||
|
||||
def update_params(self, stiffness_factor: float, steer_ratio: float) -> None:
|
||||
"""Update the vehicle model with a new stiffness factor and steer ratio"""
|
||||
self.cF: float = stiffness_factor * self.cF_orig
|
||||
self.cR: float = stiffness_factor * self.cR_orig
|
||||
self.sR: float = steer_ratio
|
||||
|
||||
def steady_state_sol(self, sa: float, u: float, roll: float) -> np.ndarray:
|
||||
"""Returns the steady state solution.
|
||||
|
||||
If the speed is too low we can't use the dynamic model (tire slip is undefined),
|
||||
we then have to use the kinematic model
|
||||
|
||||
Args:
|
||||
sa: Steering wheel angle [rad]
|
||||
u: Speed [m/s]
|
||||
roll: Road Roll [rad]
|
||||
|
||||
Returns:
|
||||
2x1 matrix with steady state solution (lateral speed, rotational speed)
|
||||
"""
|
||||
if u > 0.1:
|
||||
return dyn_ss_sol(sa, u, roll, self)
|
||||
else:
|
||||
return kin_ss_sol(sa, u, self)
|
||||
|
||||
def calc_curvature(self, sa: float, u: float, roll: float) -> float:
|
||||
"""Returns the curvature. Multiplied by the speed this will give the yaw rate.
|
||||
|
||||
Args:
|
||||
sa: Steering wheel angle [rad]
|
||||
u: Speed [m/s]
|
||||
roll: Road Roll [rad]
|
||||
|
||||
Returns:
|
||||
Curvature factor [1/m]
|
||||
"""
|
||||
return (self.curvature_factor(u) * sa / self.sR) + self.roll_compensation(roll, u)
|
||||
|
||||
def curvature_factor(self, u: float) -> float:
|
||||
"""Returns the curvature factor.
|
||||
Multiplied by wheel angle (not steering wheel angle) this will give the curvature.
|
||||
|
||||
Args:
|
||||
u: Speed [m/s]
|
||||
|
||||
Returns:
|
||||
Curvature factor [1/m]
|
||||
"""
|
||||
sf = calc_slip_factor(self)
|
||||
return (1. - self.chi) / (1. - sf * u**2) / self.l
|
||||
|
||||
def get_steer_from_curvature(self, curv: float, u: float, roll: float) -> float:
|
||||
"""Calculates the required steering wheel angle for a given curvature
|
||||
|
||||
Args:
|
||||
curv: Desired curvature [1/m]
|
||||
u: Speed [m/s]
|
||||
roll: Road Roll [rad]
|
||||
|
||||
Returns:
|
||||
Steering wheel angle [rad]
|
||||
"""
|
||||
|
||||
return (curv - self.roll_compensation(roll, u)) * self.sR * 1.0 / self.curvature_factor(u)
|
||||
|
||||
def roll_compensation(self, roll: float, u: float) -> float:
|
||||
"""Calculates the roll-compensation to curvature
|
||||
|
||||
Args:
|
||||
roll: Road Roll [rad]
|
||||
u: Speed [m/s]
|
||||
|
||||
Returns:
|
||||
Roll compensation curvature [rad]
|
||||
"""
|
||||
sf = calc_slip_factor(self)
|
||||
|
||||
if abs(sf) < 1e-6:
|
||||
return 0
|
||||
else:
|
||||
return (ACCELERATION_DUE_TO_GRAVITY * roll) / ((1 / sf) - u**2)
|
||||
|
||||
def get_steer_from_yaw_rate(self, yaw_rate: float, u: float, roll: float) -> float:
|
||||
"""Calculates the required steering wheel angle for a given yaw_rate
|
||||
|
||||
Args:
|
||||
yaw_rate: Desired yaw rate [rad/s]
|
||||
u: Speed [m/s]
|
||||
roll: Road Roll [rad]
|
||||
|
||||
Returns:
|
||||
Steering wheel angle [rad]
|
||||
"""
|
||||
curv = yaw_rate / u
|
||||
return self.get_steer_from_curvature(curv, u, roll)
|
||||
|
||||
def yaw_rate(self, sa: float, u: float, roll: float) -> float:
|
||||
"""Calculate yaw rate
|
||||
|
||||
Args:
|
||||
sa: Steering wheel angle [rad]
|
||||
u: Speed [m/s]
|
||||
roll: Road Roll [rad]
|
||||
|
||||
Returns:
|
||||
Yaw rate [rad/s]
|
||||
"""
|
||||
return self.calc_curvature(sa, u, roll) * u
|
||||
|
||||
|
||||
def kin_ss_sol(sa: float, u: float, VM: VehicleModel) -> np.ndarray:
|
||||
"""Calculate the steady state solution at low speeds
|
||||
At low speeds the tire slip is undefined, so a kinematic
|
||||
model is used.
|
||||
|
||||
Args:
|
||||
sa: Steering angle [rad]
|
||||
u: Speed [m/s]
|
||||
VM: Vehicle model
|
||||
|
||||
Returns:
|
||||
2x1 matrix with steady state solution
|
||||
"""
|
||||
K = np.zeros((2, 1))
|
||||
K[0, 0] = VM.aR / VM.sR / VM.l * u
|
||||
K[1, 0] = 1. / VM.sR / VM.l * u
|
||||
return K * sa
|
||||
|
||||
|
||||
def create_dyn_state_matrices(u: float, VM: VehicleModel) -> tuple[np.ndarray, np.ndarray]:
|
||||
"""Returns the A and B matrix for the dynamics system
|
||||
|
||||
Args:
|
||||
u: Vehicle speed [m/s]
|
||||
VM: Vehicle model
|
||||
|
||||
Returns:
|
||||
A tuple with the 2x2 A matrix, and 2x2 B matrix
|
||||
|
||||
Parameters in the vehicle model:
|
||||
cF: Tire stiffness Front [N/rad]
|
||||
cR: Tire stiffness Front [N/rad]
|
||||
aF: Distance from CG to front wheels [m]
|
||||
aR: Distance from CG to rear wheels [m]
|
||||
m: Mass [kg]
|
||||
j: Rotational inertia [kg m^2]
|
||||
sR: Steering ratio [-]
|
||||
chi: Steer ratio rear [-]
|
||||
"""
|
||||
A = np.zeros((2, 2))
|
||||
B = np.zeros((2, 2))
|
||||
A[0, 0] = - (VM.cF + VM.cR) / (VM.m * u)
|
||||
A[0, 1] = - (VM.cF * VM.aF - VM.cR * VM.aR) / (VM.m * u) - u
|
||||
A[1, 0] = - (VM.cF * VM.aF - VM.cR * VM.aR) / (VM.j * u)
|
||||
A[1, 1] = - (VM.cF * VM.aF**2 + VM.cR * VM.aR**2) / (VM.j * u)
|
||||
|
||||
# Steering input
|
||||
B[0, 0] = (VM.cF + VM.chi * VM.cR) / VM.m / VM.sR
|
||||
B[1, 0] = (VM.cF * VM.aF - VM.chi * VM.cR * VM.aR) / VM.j / VM.sR
|
||||
|
||||
# Roll input
|
||||
B[0, 1] = -ACCELERATION_DUE_TO_GRAVITY
|
||||
|
||||
return A, B
|
||||
|
||||
|
||||
def dyn_ss_sol(sa: float, u: float, roll: float, VM: VehicleModel) -> np.ndarray:
|
||||
"""Calculate the steady state solution when x_dot = 0,
|
||||
Ax + Bu = 0 => x = -A^{-1} B u
|
||||
|
||||
Args:
|
||||
sa: Steering angle [rad]
|
||||
u: Speed [m/s]
|
||||
roll: Road Roll [rad]
|
||||
VM: Vehicle model
|
||||
|
||||
Returns:
|
||||
2x1 matrix with steady state solution
|
||||
"""
|
||||
A, B = create_dyn_state_matrices(u, VM)
|
||||
inp = np.array([[sa], [roll]])
|
||||
return -solve(A, B) @ inp # type: ignore
|
||||
|
||||
|
||||
def calc_slip_factor(VM: VehicleModel) -> float:
|
||||
"""The slip factor is a measure of how the curvature changes with speed
|
||||
it's positive for Oversteering vehicle, negative (usual case) otherwise.
|
||||
"""
|
||||
return VM.m * (VM.cF * VM.aF - VM.cR * VM.aR) / (VM.l**2 * VM.cF * VM.cR)
|
||||
@@ -6,7 +6,7 @@ from openpilot.tools.lib.live_logreader import live_logreader
|
||||
from openpilot.tools.lib.logreader import LogReader, ReadMode
|
||||
|
||||
|
||||
def main(route: str | None, addrs: list[int]):
|
||||
def main(route: str | None, addrs: list[int], rxoffset: int | None):
|
||||
"""
|
||||
TODO:
|
||||
- highlight TX vs RX clearly
|
||||
@@ -23,7 +23,7 @@ def main(route: str | None, addrs: list[int]):
|
||||
prev_mono_time = 0
|
||||
|
||||
# include rx addresses
|
||||
addrs = addrs + [uds.get_rx_addr_for_tx_addr(addr) for addr in addrs]
|
||||
addrs = addrs + [uds.get_rx_addr_for_tx_addr(addr, rxoffset) for addr in addrs]
|
||||
|
||||
for msg in lr:
|
||||
if msg.which() == 'can':
|
||||
@@ -44,7 +44,9 @@ if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description='View back and forth ISO-TP communication between various ECUs given an address')
|
||||
parser.add_argument('route', nargs='?', help='Route name, live if not specified')
|
||||
parser.add_argument('--addrs', nargs='*', default=[], help='List of tx address to view (0x7e0 for engine)')
|
||||
parser.add_argument('--rxoffset', default='')
|
||||
args = parser.parse_args()
|
||||
|
||||
addrs = [int(addr, base=16) if addr.startswith('0x') else int(addr) for addr in args.addrs]
|
||||
main(args.route, addrs)
|
||||
rxoffset = int(args.rxoffset, base=16) if args.rxoffset else None
|
||||
main(args.route, addrs, rxoffset)
|
||||
|
||||
Executable
+116
@@ -0,0 +1,116 @@
|
||||
#!/usr/bin/env python3
|
||||
import argparse
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
from typing import NamedTuple
|
||||
from openpilot.tools.lib.logreader import LogReader
|
||||
from openpilot.selfdrive.locationd.models.pose_kf import EARTH_G
|
||||
|
||||
RLOG_MIN_LAT_ACTIVE = 50
|
||||
RLOG_MIN_STEERING_UNPRESSED = 50
|
||||
RLOG_MIN_REQUESTING_MAX = 25 # sample many times after reaching max torque
|
||||
|
||||
QLOG_DECIMATION = 10
|
||||
|
||||
|
||||
class Event(NamedTuple):
|
||||
lateral_accel: float
|
||||
speed: float
|
||||
roll: float
|
||||
timestamp: float # relative to start of route (s)
|
||||
|
||||
|
||||
def find_events(lr: LogReader, qlog: bool = False) -> list[Event]:
|
||||
min_lat_active = RLOG_MIN_LAT_ACTIVE // QLOG_DECIMATION if qlog else RLOG_MIN_LAT_ACTIVE
|
||||
min_steering_unpressed = RLOG_MIN_STEERING_UNPRESSED // QLOG_DECIMATION if qlog else RLOG_MIN_STEERING_UNPRESSED
|
||||
min_requesting_max = RLOG_MIN_REQUESTING_MAX // QLOG_DECIMATION if qlog else RLOG_MIN_REQUESTING_MAX
|
||||
|
||||
events = []
|
||||
|
||||
start_ts = 0
|
||||
|
||||
# state tracking
|
||||
steering_unpressed = 0 # frames
|
||||
requesting_max = 0 # frames
|
||||
lat_active = 0 # frames
|
||||
|
||||
# current state
|
||||
curvature = 0
|
||||
v_ego = 0
|
||||
roll = 0
|
||||
|
||||
for msg in lr:
|
||||
if msg.which() == 'carControl':
|
||||
if start_ts == 0:
|
||||
start_ts = msg.logMonoTime
|
||||
|
||||
lat_active = lat_active + 1 if msg.carControl.latActive else 0
|
||||
|
||||
elif msg.which() == 'carOutput':
|
||||
# if we test with driver torque safety, max torque can be slightly noisy
|
||||
requesting_max = requesting_max + 1 if abs(msg.carOutput.actuatorsOutput.torque) > 0.95 else 0
|
||||
|
||||
elif msg.which() == 'carState':
|
||||
steering_unpressed = steering_unpressed + 1 if not msg.carState.steeringPressed else 0
|
||||
v_ego = msg.carState.vEgo
|
||||
|
||||
elif msg.which() == 'controlsState':
|
||||
curvature = msg.controlsState.curvature
|
||||
|
||||
elif msg.which() == 'liveParameters':
|
||||
roll = msg.liveParameters.roll
|
||||
|
||||
if lat_active > min_lat_active and steering_unpressed > min_steering_unpressed and requesting_max > min_requesting_max:
|
||||
# TODO: record max lat accel at the end of the event, need to use the past lat accel as overriding can happen before we detect it
|
||||
requesting_max = 0
|
||||
|
||||
current_lateral_accel = curvature * v_ego ** 2 - roll * EARTH_G
|
||||
events.append(Event(current_lateral_accel, v_ego, roll, round((msg.logMonoTime - start_ts) * 1e-9, 2)))
|
||||
print(events[-1])
|
||||
|
||||
return events
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(description="Find max lateral acceleration events",
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
|
||||
|
||||
parser.add_argument("route")
|
||||
args = parser.parse_args()
|
||||
|
||||
lr = LogReader(args.route, sort_by_time=True)
|
||||
qlog = args.route.endswith('/q')
|
||||
if qlog:
|
||||
print('WARNING: Treating route as qlog!')
|
||||
|
||||
print('Finding events...')
|
||||
events = find_events(lr, qlog=qlog)
|
||||
|
||||
print()
|
||||
print(f'Found {len(events)} events')
|
||||
|
||||
perc_left_accel = -np.percentile([-ev.lateral_accel for ev in events if ev.lateral_accel < 0], 90)
|
||||
perc_right_accel = np.percentile([ev.lateral_accel for ev in events if ev.lateral_accel > 0], 90)
|
||||
|
||||
CP = lr.first('carParams')
|
||||
|
||||
plt.ion()
|
||||
plt.clf()
|
||||
plt.suptitle(f'{CP.carFingerprint} - Max lateral acceleration events')
|
||||
plt.title(args.route)
|
||||
plt.scatter([ev.speed for ev in events], [ev.lateral_accel for ev in events], label='max lateral accel events')
|
||||
|
||||
plt.plot([0, 35], [3, 3], c='r', label='ISO 11270 - 3 m/s^2')
|
||||
plt.plot([0, 35], [-3, -3], c='r')
|
||||
|
||||
plt.plot([0, 35], [perc_left_accel, perc_left_accel], c='g', linestyle='--', label='90th percentile left lateral accel')
|
||||
plt.plot([0, 35], [perc_right_accel, perc_right_accel], c='#ff7f0e', linestyle='--', label='90th percentile right lateral accel')
|
||||
plt.text(0.4, float(perc_left_accel + 0.4), f'{perc_left_accel:.2f} m/s^2', verticalalignment='center', fontsize=12)
|
||||
plt.text(0.4, float(perc_right_accel - 0.4), f'{perc_right_accel:.2f} m/s^2', verticalalignment='center', fontsize=12)
|
||||
|
||||
plt.xlim(0, 35)
|
||||
plt.ylim(-5, 5)
|
||||
plt.xlabel('speed (m/s)')
|
||||
plt.ylabel('lateral acceleration (m/s^2)')
|
||||
plt.legend()
|
||||
plt.show(block=True)
|
||||
@@ -65,8 +65,7 @@ if __name__ == "__main__":
|
||||
CP = msg.carParams
|
||||
car_fw = [fw for fw in CP.carFw if not fw.logging]
|
||||
if len(car_fw) == 0:
|
||||
print("no fw")
|
||||
break
|
||||
print("WARNING: no fw")
|
||||
|
||||
live_fingerprint = CP.carFingerprint
|
||||
live_fingerprint = MIGRATION.get(live_fingerprint, live_fingerprint)
|
||||
@@ -98,7 +97,7 @@ if __name__ == "__main__":
|
||||
print("New style (exact):", exact_matches)
|
||||
print("New style (fuzzy):", fuzzy_matches)
|
||||
|
||||
padding = max([len(fw.brand or UNKNOWN_BRAND) for fw in car_fw])
|
||||
padding = max([len(fw.brand or UNKNOWN_BRAND) for fw in car_fw] + [0])
|
||||
for version in sorted(car_fw, key=lambda fw: fw.brand):
|
||||
subaddr = None if version.subAddress == 0 else hex(version.subAddress)
|
||||
print(f" Brand: {version.brand or UNKNOWN_BRAND:{padding}}, bus: {version.bus} - " +
|
||||
|
||||
@@ -147,13 +147,13 @@ class LocationEstimator:
|
||||
self.car_speed = abs(msg.vEgo)
|
||||
|
||||
elif which == "liveCalibration":
|
||||
# Note that we use this message during calibration
|
||||
if len(msg.rpyCalib) > 0:
|
||||
calib = np.array(msg.rpyCalib)
|
||||
if calib.min() < -CALIB_RPY_SANITY_CHECK or calib.max() > CALIB_RPY_SANITY_CHECK:
|
||||
return HandleLogResult.INPUT_INVALID
|
||||
|
||||
self.device_from_calib = rot_from_euler(calib)
|
||||
self.calibrated = msg.calStatus == log.LiveCalibrationData.Status.calibrated
|
||||
|
||||
elif which == "cameraOdometry":
|
||||
if not self._validate_timestamp(t):
|
||||
|
||||
@@ -5,7 +5,7 @@ from typing import Any
|
||||
|
||||
import numpy as np
|
||||
|
||||
from openpilot.selfdrive.controls.lib.vehicle_model import ACCELERATION_DUE_TO_GRAVITY
|
||||
from opendbc.car.vehicle_model import ACCELERATION_DUE_TO_GRAVITY
|
||||
from openpilot.selfdrive.locationd.models.constants import ObservationKind
|
||||
from openpilot.common.swaglog import cloudlog
|
||||
|
||||
|
||||
@@ -46,18 +46,25 @@ class ParamsLearner:
|
||||
self.yaw_rate_std = 0.0
|
||||
self.roll = 0.0
|
||||
self.steering_angle = 0.0
|
||||
self.roll_valid = False
|
||||
|
||||
def handle_log(self, t, which, msg):
|
||||
if which == 'livePose':
|
||||
device_pose = Pose.from_live_pose(msg)
|
||||
calibrated_pose = self.calibrator.build_calibrated_pose(device_pose)
|
||||
self.yaw_rate, self.yaw_rate_std = calibrated_pose.angular_velocity.z, calibrated_pose.angular_velocity.z_std
|
||||
|
||||
yaw_rate_valid = msg.angularVelocityDevice.valid and self.calibrator.calib_valid
|
||||
yaw_rate_valid = yaw_rate_valid and 0 < self.yaw_rate_std < 10 # rad/s
|
||||
yaw_rate_valid = yaw_rate_valid and abs(self.yaw_rate) < 1 # rad/s
|
||||
if yaw_rate_valid:
|
||||
self.yaw_rate, self.yaw_rate_std = calibrated_pose.angular_velocity.z, calibrated_pose.angular_velocity.z_std
|
||||
else:
|
||||
# This is done to bound the yaw rate estimate when localizer values are invalid or calibrating
|
||||
self.yaw_rate, self.yaw_rate_std = 0.0, np.radians(1)
|
||||
|
||||
localizer_roll, localizer_roll_std = device_pose.orientation.x, device_pose.orientation.x_std
|
||||
localizer_roll_std = np.radians(1) if np.isnan(localizer_roll_std) else localizer_roll_std
|
||||
self.roll_valid = (localizer_roll_std < ROLL_STD_MAX) and (ROLL_MIN < localizer_roll < ROLL_MAX) and msg.sensorsOK
|
||||
if self.roll_valid:
|
||||
roll_valid = (localizer_roll_std < ROLL_STD_MAX) and (ROLL_MIN < localizer_roll < ROLL_MAX) and msg.sensorsOK
|
||||
if roll_valid:
|
||||
roll = localizer_roll
|
||||
# Experimentally found multiplier of 2 to be best trade-off between stability and accuracy or similar?
|
||||
roll_std = 2 * localizer_roll_std
|
||||
@@ -67,18 +74,12 @@ class ParamsLearner:
|
||||
roll_std = np.radians(10.0)
|
||||
self.roll = np.clip(roll, self.roll - ROLL_MAX_DELTA, self.roll + ROLL_MAX_DELTA)
|
||||
|
||||
yaw_rate_valid = msg.angularVelocityDevice.valid and self.calibrator.calib_valid
|
||||
yaw_rate_valid = yaw_rate_valid and 0 < self.yaw_rate_std < 10 # rad/s
|
||||
yaw_rate_valid = yaw_rate_valid and abs(self.yaw_rate) < 1 # rad/s
|
||||
|
||||
if self.active:
|
||||
if msg.posenetOK:
|
||||
|
||||
if yaw_rate_valid:
|
||||
self.kf.predict_and_observe(t,
|
||||
ObservationKind.ROAD_FRAME_YAW_RATE,
|
||||
np.array([[-self.yaw_rate]]),
|
||||
np.array([np.atleast_2d(self.yaw_rate_std**2)]))
|
||||
self.kf.predict_and_observe(t,
|
||||
ObservationKind.ROAD_FRAME_YAW_RATE,
|
||||
np.array([[-self.yaw_rate]]),
|
||||
np.array([np.atleast_2d(self.yaw_rate_std**2)]))
|
||||
|
||||
self.kf.predict_and_observe(t,
|
||||
ObservationKind.ROAD_ROLL,
|
||||
@@ -224,17 +225,21 @@ def main():
|
||||
liveParameters.posenetValid = True
|
||||
liveParameters.sensorValid = sensors_valid
|
||||
liveParameters.steerRatio = float(x[States.STEER_RATIO].item())
|
||||
liveParameters.steerRatioValid = min_sr <= liveParameters.steerRatio <= max_sr
|
||||
liveParameters.stiffnessFactor = float(x[States.STIFFNESS].item())
|
||||
liveParameters.stiffnessFactorValid = 0.2 <= liveParameters.stiffnessFactor <= 5.0
|
||||
liveParameters.roll = float(roll)
|
||||
liveParameters.angleOffsetAverageDeg = float(angle_offset_average)
|
||||
liveParameters.angleOffsetAverageValid = bool(avg_offset_valid)
|
||||
liveParameters.angleOffsetDeg = float(angle_offset)
|
||||
liveParameters.angleOffsetValid = bool(total_offset_valid)
|
||||
liveParameters.valid = all((
|
||||
avg_offset_valid,
|
||||
total_offset_valid,
|
||||
liveParameters.angleOffsetAverageValid,
|
||||
liveParameters.angleOffsetValid ,
|
||||
roll_valid,
|
||||
roll_std < ROLL_STD_MAX,
|
||||
0.2 <= liveParameters.stiffnessFactor <= 5.0,
|
||||
min_sr <= liveParameters.steerRatio <= max_sr,
|
||||
liveParameters.stiffnessFactorValid,
|
||||
liveParameters.steerRatioValid,
|
||||
))
|
||||
liveParameters.steerRatioStd = float(P[States.STEER_RATIO].item())
|
||||
liveParameters.stiffnessFactorStd = float(P[States.STIFFNESS].item())
|
||||
|
||||
@@ -4,11 +4,11 @@ from collections import deque, defaultdict
|
||||
|
||||
import cereal.messaging as messaging
|
||||
from cereal import car, log
|
||||
from opendbc.car.vehicle_model import ACCELERATION_DUE_TO_GRAVITY
|
||||
from openpilot.common.params import Params
|
||||
from openpilot.common.realtime import config_realtime_process, DT_MDL
|
||||
from openpilot.common.filter_simple import FirstOrderFilter
|
||||
from openpilot.common.swaglog import cloudlog
|
||||
from openpilot.selfdrive.controls.lib.vehicle_model import ACCELERATION_DUE_TO_GRAVITY
|
||||
from openpilot.selfdrive.locationd.helpers import PointBuckets, ParameterEstimator, PoseCalibrator, Pose
|
||||
|
||||
HISTORY = 5 # secs
|
||||
@@ -167,7 +167,7 @@ class TorqueEstimator(ParameterEstimator):
|
||||
self.raw_points["lat_active"].append(msg.latActive)
|
||||
elif which == "carOutput":
|
||||
self.raw_points["carOutput_t"].append(t + self.lag)
|
||||
self.raw_points["steer_torque"].append(-msg.actuatorsOutput.steer)
|
||||
self.raw_points["steer_torque"].append(-msg.actuatorsOutput.torque)
|
||||
elif which == "carState":
|
||||
self.raw_points["carState_t"].append(t + self.lag)
|
||||
# TODO: check if high aEgo affects resulting lateral accel
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
*_pyx.cpp
|
||||
@@ -32,10 +32,11 @@ lenvCython.Program('models/commonmodel_pyx.so', 'models/commonmodel_pyx.pyx', LI
|
||||
tinygrad_files = ["#"+x for x in glob.glob(env.Dir("#tinygrad_repo").relpath + "/**", recursive=True, root_dir=env.Dir("#").abspath) if 'pycache' not in x]
|
||||
|
||||
# Get model metadata
|
||||
fn = File("models/supercombo").abspath
|
||||
script_files = [File(Dir("#selfdrive/modeld").File("get_model_metadata.py").abspath)]
|
||||
cmd = f'python3 {Dir("#selfdrive/modeld").abspath}/get_model_metadata.py {fn}.onnx'
|
||||
lenv.Command(fn + "_metadata.pkl", [fn + ".onnx"] + tinygrad_files + script_files, cmd)
|
||||
for model_name in ['driving_vision', 'driving_policy']:
|
||||
fn = File(f"models/{model_name}").abspath
|
||||
script_files = [File(Dir("#selfdrive/modeld").File("get_model_metadata.py").abspath)]
|
||||
cmd = f'python3 {Dir("#selfdrive/modeld").abspath}/get_model_metadata.py {fn}.onnx'
|
||||
lenv.Command(fn + "_metadata.pkl", [fn + ".onnx"] + tinygrad_files + script_files, cmd)
|
||||
|
||||
# Compile tinygrad model
|
||||
pythonpath_string = 'PYTHONPATH="${PYTHONPATH}:' + env.Dir("#tinygrad_repo").abspath + '"'
|
||||
@@ -46,7 +47,7 @@ elif arch == 'Darwin':
|
||||
else:
|
||||
device_string = 'LLVM=1 LLVMOPT=1 BEAM=0 IMAGE=0'
|
||||
|
||||
for model_name in ['supercombo', 'dmonitoring_model']:
|
||||
for model_name in ['driving_vision', 'driving_policy', 'dmonitoring_model']:
|
||||
fn = File(f"models/{model_name}").abspath
|
||||
cmd = f'{pythonpath_string} {device_string} python3 {Dir("#tinygrad_repo").abspath}/examples/openpilot/compile3.py {fn}.onnx {fn}_tinygrad.pkl'
|
||||
lenv.Command(fn + "_tinygrad.pkl", [fn + ".onnx"] + tinygrad_files, cmd)
|
||||
|
||||
@@ -14,8 +14,14 @@ class ModelConstants:
|
||||
|
||||
# model inputs constants
|
||||
MODEL_FREQ = 20
|
||||
HISTORY_FREQ = 5
|
||||
HISTORY_LEN_SECONDS = 5
|
||||
TEMPORAL_SKIP = MODEL_FREQ // HISTORY_FREQ
|
||||
FULL_HISTORY_BUFFER_LEN = MODEL_FREQ * HISTORY_LEN_SECONDS
|
||||
INPUT_HISTORY_BUFFER_LEN = HISTORY_FREQ * HISTORY_LEN_SECONDS
|
||||
|
||||
FEATURE_LEN = 512
|
||||
FULL_HISTORY_BUFFER_LEN = 99
|
||||
|
||||
DESIRE_LEN = 8
|
||||
TRAFFIC_CONVENTION_LEN = 2
|
||||
LAT_PLANNER_STATE_LEN = 4
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null && pwd)"
|
||||
exec "$DIR/dmonitoringmodeld.py" "$@"
|
||||
@@ -24,8 +24,7 @@ if __name__ == "__main__":
|
||||
assert output_slices is not None, 'output_slices not found in metadata'
|
||||
|
||||
metadata = {
|
||||
'policy_model': get_metadata_value_by_name(model, 'policy_model'),
|
||||
'vision_model': get_metadata_value_by_name(model, 'vision_model'),
|
||||
'model_checkpoint': get_metadata_value_by_name(model, 'model_checkpoint'),
|
||||
'output_slices': pickle.loads(codecs.decode(output_slices.encode(), "base64")),
|
||||
'input_shapes': dict([get_name_and_shape(x) for x in model.graph.input]),
|
||||
'output_shapes': dict([get_name_and_shape(x) for x in model.graph.output])
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null && pwd)"
|
||||
exec "$DIR/modeld.py" "$@"
|
||||
+63
-35
@@ -35,8 +35,10 @@ from openpilot.selfdrive.modeld.models.commonmodel_pyx import DrivingModelFrame,
|
||||
PROCESS_NAME = "selfdrive.modeld.modeld"
|
||||
SEND_RAW_PRED = os.getenv('SEND_RAW_PRED')
|
||||
|
||||
MODEL_PKL_PATH = Path(__file__).parent / 'models/supercombo_tinygrad.pkl'
|
||||
METADATA_PATH = Path(__file__).parent / 'models/supercombo_metadata.pkl'
|
||||
VISION_PKL_PATH = Path(__file__).parent / 'models/driving_vision_tinygrad.pkl'
|
||||
POLICY_PKL_PATH = Path(__file__).parent / 'models/driving_policy_tinygrad.pkl'
|
||||
VISION_METADATA_PATH = Path(__file__).parent / 'models/driving_vision_metadata.pkl'
|
||||
POLICY_METADATA_PATH = Path(__file__).parent / 'models/driving_policy_metadata.pkl'
|
||||
|
||||
class FrameMeta:
|
||||
frame_id: int = 0
|
||||
@@ -54,35 +56,53 @@ class ModelState:
|
||||
prev_desire: np.ndarray # for tracking the rising edge of the pulse
|
||||
|
||||
def __init__(self, context: CLContext):
|
||||
self.frames = {'input_imgs': DrivingModelFrame(context), 'big_input_imgs': DrivingModelFrame(context)}
|
||||
self.frames = {
|
||||
'input_imgs': DrivingModelFrame(context, ModelConstants.TEMPORAL_SKIP),
|
||||
'big_input_imgs': DrivingModelFrame(context, ModelConstants.TEMPORAL_SKIP)
|
||||
}
|
||||
self.prev_desire = np.zeros(ModelConstants.DESIRE_LEN, dtype=np.float32)
|
||||
|
||||
# img buffers are managed in openCL transform code
|
||||
self.full_features_buffer = np.zeros((1, ModelConstants.FULL_HISTORY_BUFFER_LEN, ModelConstants.FEATURE_LEN), dtype=np.float32)
|
||||
self.full_desire = np.zeros((1, ModelConstants.FULL_HISTORY_BUFFER_LEN, ModelConstants.DESIRE_LEN), dtype=np.float32)
|
||||
self.full_prev_desired_curv = np.zeros((1, ModelConstants.FULL_HISTORY_BUFFER_LEN, ModelConstants.PREV_DESIRED_CURV_LEN), dtype=np.float32)
|
||||
self.temporal_idxs = slice(-1-(ModelConstants.TEMPORAL_SKIP*(ModelConstants.INPUT_HISTORY_BUFFER_LEN-1)), None, ModelConstants.TEMPORAL_SKIP)
|
||||
|
||||
# policy inputs
|
||||
self.numpy_inputs = {
|
||||
'desire': np.zeros((1, (ModelConstants.FULL_HISTORY_BUFFER_LEN+1), ModelConstants.DESIRE_LEN), dtype=np.float32),
|
||||
'desire': np.zeros((1, ModelConstants.INPUT_HISTORY_BUFFER_LEN, ModelConstants.DESIRE_LEN), dtype=np.float32),
|
||||
'traffic_convention': np.zeros((1, ModelConstants.TRAFFIC_CONVENTION_LEN), dtype=np.float32),
|
||||
'lateral_control_params': np.zeros((1, ModelConstants.LATERAL_CONTROL_PARAMS_LEN), dtype=np.float32),
|
||||
'prev_desired_curv': np.zeros((1, (ModelConstants.FULL_HISTORY_BUFFER_LEN+1), ModelConstants.PREV_DESIRED_CURV_LEN), dtype=np.float32),
|
||||
'features_buffer': np.zeros((1, ModelConstants.FULL_HISTORY_BUFFER_LEN, ModelConstants.FEATURE_LEN), dtype=np.float32),
|
||||
'prev_desired_curv': np.zeros((1, ModelConstants.INPUT_HISTORY_BUFFER_LEN, ModelConstants.PREV_DESIRED_CURV_LEN), dtype=np.float32),
|
||||
'features_buffer': np.zeros((1, ModelConstants.INPUT_HISTORY_BUFFER_LEN, ModelConstants.FEATURE_LEN), dtype=np.float32),
|
||||
}
|
||||
|
||||
with open(METADATA_PATH, 'rb') as f:
|
||||
model_metadata = pickle.load(f)
|
||||
self.input_shapes = model_metadata['input_shapes']
|
||||
with open(VISION_METADATA_PATH, 'rb') as f:
|
||||
vision_metadata = pickle.load(f)
|
||||
self.vision_input_shapes = vision_metadata['input_shapes']
|
||||
self.vision_output_slices = vision_metadata['output_slices']
|
||||
vision_output_size = vision_metadata['output_shapes']['outputs'][1]
|
||||
|
||||
self.output_slices = model_metadata['output_slices']
|
||||
net_output_size = model_metadata['output_shapes']['outputs'][1]
|
||||
self.output = np.zeros(net_output_size, dtype=np.float32)
|
||||
with open(POLICY_METADATA_PATH, 'rb') as f:
|
||||
policy_metadata = pickle.load(f)
|
||||
self.policy_input_shapes = policy_metadata['input_shapes']
|
||||
self.policy_output_slices = policy_metadata['output_slices']
|
||||
policy_output_size = policy_metadata['output_shapes']['outputs'][1]
|
||||
|
||||
# img buffers are managed in openCL transform code
|
||||
self.vision_inputs: dict[str, Tensor] = {}
|
||||
self.vision_output = np.zeros(vision_output_size, dtype=np.float32)
|
||||
self.policy_inputs = {k: Tensor(v, device='NPY').realize() for k,v in self.numpy_inputs.items()}
|
||||
self.policy_output = np.zeros(policy_output_size, dtype=np.float32)
|
||||
self.parser = Parser()
|
||||
|
||||
self.tensor_inputs = {k: Tensor(v, device='NPY').realize() for k,v in self.numpy_inputs.items()}
|
||||
with open(MODEL_PKL_PATH, "rb") as f:
|
||||
self.model_run = pickle.load(f)
|
||||
with open(VISION_PKL_PATH, "rb") as f:
|
||||
self.vision_run = pickle.load(f)
|
||||
|
||||
def slice_outputs(self, model_outputs: np.ndarray) -> dict[str, np.ndarray]:
|
||||
parsed_model_outputs = {k: model_outputs[np.newaxis, v] for k,v in self.output_slices.items()}
|
||||
if SEND_RAW_PRED:
|
||||
parsed_model_outputs['raw_pred'] = model_outputs.copy()
|
||||
with open(POLICY_PKL_PATH, "rb") as f:
|
||||
self.policy_run = pickle.load(f)
|
||||
|
||||
def slice_outputs(self, model_outputs: np.ndarray, output_slices: dict[str, slice]) -> dict[str, np.ndarray]:
|
||||
parsed_model_outputs = {k: model_outputs[np.newaxis, v] for k,v in output_slices.items()}
|
||||
return parsed_model_outputs
|
||||
|
||||
def run(self, buf: VisionBuf, wbuf: VisionBuf, transform: np.ndarray, transform_wide: np.ndarray,
|
||||
@@ -92,8 +112,9 @@ class ModelState:
|
||||
new_desire = np.where(inputs['desire'] - self.prev_desire > .99, inputs['desire'], 0)
|
||||
self.prev_desire[:] = inputs['desire']
|
||||
|
||||
self.numpy_inputs['desire'][0,:-1] = self.numpy_inputs['desire'][0,1:]
|
||||
self.numpy_inputs['desire'][0,-1] = new_desire
|
||||
self.full_desire[0,:-1] = self.full_desire[0,1:]
|
||||
self.full_desire[0,-1] = new_desire
|
||||
self.numpy_inputs['desire'][:] = self.full_desire.reshape((1,ModelConstants.INPUT_HISTORY_BUFFER_LEN,ModelConstants.TEMPORAL_SKIP,-1)).max(axis=2)
|
||||
|
||||
self.numpy_inputs['traffic_convention'][:] = inputs['traffic_convention']
|
||||
self.numpy_inputs['lateral_control_params'][:] = inputs['lateral_control_params']
|
||||
@@ -103,29 +124,36 @@ class ModelState:
|
||||
if TICI:
|
||||
# The imgs tensors are backed by opencl memory, only need init once
|
||||
for key in imgs_cl:
|
||||
if key not in self.tensor_inputs:
|
||||
self.tensor_inputs[key] = qcom_tensor_from_opencl_address(imgs_cl[key].mem_address, self.input_shapes[key], dtype=dtypes.uint8)
|
||||
if key not in self.vision_inputs:
|
||||
self.vision_inputs[key] = qcom_tensor_from_opencl_address(imgs_cl[key].mem_address, self.vision_input_shapes[key], dtype=dtypes.uint8)
|
||||
else:
|
||||
for key in imgs_cl:
|
||||
self.numpy_inputs[key] = self.frames[key].buffer_from_cl(imgs_cl[key]).reshape(self.input_shapes[key])
|
||||
self.tensor_inputs[key] = Tensor(self.numpy_inputs[key], dtype=dtypes.uint8).realize()
|
||||
|
||||
frame_input = self.frames[key].buffer_from_cl(imgs_cl[key]).reshape(self.vision_input_shapes[key])
|
||||
self.vision_inputs[key] = Tensor(frame_input, dtype=dtypes.uint8).realize()
|
||||
|
||||
if prepare_only:
|
||||
return None
|
||||
|
||||
self.output = self.model_run(**self.tensor_inputs).numpy().flatten()
|
||||
self.vision_output = self.vision_run(**self.vision_inputs).numpy().flatten()
|
||||
vision_outputs_dict = self.parser.parse_vision_outputs(self.slice_outputs(self.vision_output, self.vision_output_slices))
|
||||
|
||||
outputs = self.parser.parse_outputs(self.slice_outputs(self.output))
|
||||
|
||||
self.numpy_inputs['features_buffer'][0,:-1] = self.numpy_inputs['features_buffer'][0,1:]
|
||||
self.numpy_inputs['features_buffer'][0,-1] = outputs['hidden_state'][0, :]
|
||||
self.full_features_buffer[0,:-1] = self.full_features_buffer[0,1:]
|
||||
self.full_features_buffer[0,-1] = vision_outputs_dict['hidden_state'][0, :]
|
||||
self.numpy_inputs['features_buffer'][:] = self.full_features_buffer[0, self.temporal_idxs]
|
||||
|
||||
self.policy_output = self.policy_run(**self.policy_inputs).numpy().flatten()
|
||||
policy_outputs_dict = self.parser.parse_policy_outputs(self.slice_outputs(self.policy_output, self.policy_output_slices))
|
||||
|
||||
# TODO model only uses last value now
|
||||
self.numpy_inputs['prev_desired_curv'][0,:-1] = self.numpy_inputs['prev_desired_curv'][0,1:]
|
||||
self.numpy_inputs['prev_desired_curv'][0,-1,:] = outputs['desired_curvature'][0, :]
|
||||
return outputs
|
||||
self.full_prev_desired_curv[0,:-1] = self.full_prev_desired_curv[0,1:]
|
||||
self.full_prev_desired_curv[0,-1,:] = policy_outputs_dict['desired_curvature'][0, :]
|
||||
self.numpy_inputs['prev_desired_curv'][:] = self.full_prev_desired_curv[0, self.temporal_idxs]
|
||||
|
||||
combined_outputs_dict = {**vision_outputs_dict, **policy_outputs_dict}
|
||||
if SEND_RAW_PRED:
|
||||
combined_outputs_dict['raw_pred'] = np.concatenate([self.vision_output.copy(), self.policy_output.copy()])
|
||||
|
||||
return combined_outputs_dict
|
||||
|
||||
|
||||
def main(demo=False):
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
## Neural networks in openpilot
|
||||
To view the architecture of the ONNX networks, you can use [netron](https://netron.app/)
|
||||
|
||||
## Supercombo
|
||||
### Supercombo input format (Full size: 799906 x float32)
|
||||
## Driving Model (vision model + temporal policy model)
|
||||
### Vision inputs (Full size: 799906 x float32)
|
||||
* **image stream**
|
||||
* Two consecutive images (256 * 512 * 3 in RGB) recorded at 20 Hz : 393216 = 2 * 6 * 128 * 256
|
||||
* Each 256 * 512 image is represented in YUV420 with 6 channels : 6 * 128 * 256
|
||||
@@ -15,16 +15,21 @@ To view the architecture of the ONNX networks, you can use [netron](https://netr
|
||||
* Channels 0,1,2,3 represent the full-res Y channel and are represented in numpy as Y[::2, ::2], Y[::2, 1::2], Y[1::2, ::2], and Y[1::2, 1::2]
|
||||
* Channel 4 represents the half-res U channel
|
||||
* Channel 5 represents the half-res V channel
|
||||
### Policy inputs
|
||||
* **desire**
|
||||
* one-hot encoded buffer to command model to execute certain actions, bit needs to be sent for the past 5 seconds (at 20FPS) : 100 * 8
|
||||
* **traffic convention**
|
||||
* one-hot encoded vector to tell model whether traffic is right-hand or left-hand traffic : 2
|
||||
* **lateral control params**
|
||||
* speed and steering delay for predicting the desired curvature: 2
|
||||
* **previous desired curvatures**
|
||||
* vector of previously predicted desired curvatures: 100 * 1
|
||||
* **feature buffer**
|
||||
* A buffer of intermediate features that gets appended to the current feature to form a 5 seconds temporal context (at 20FPS) : 99 * 512
|
||||
* a buffer of intermediate features including the current feature to form a 5 seconds temporal context (at 20FPS) : 100 * 512
|
||||
|
||||
|
||||
### Supercombo output format (Full size: XXX x float32)
|
||||
Read [here](https://github.com/commaai/openpilot/blob/90af436a121164a51da9fa48d093c29f738adf6a/selfdrive/modeld/models/driving.h#L236) for more.
|
||||
### Driving Model output format (Full size: XXX x float32)
|
||||
Refer to **slice_outputs** and **parse_vision_outputs/parse_policy_outputs** in modeld.
|
||||
|
||||
|
||||
## Driver Monitoring Model
|
||||
|
||||
@@ -5,11 +5,12 @@
|
||||
|
||||
#include "common/clutil.h"
|
||||
|
||||
DrivingModelFrame::DrivingModelFrame(cl_device_id device_id, cl_context context) : ModelFrame(device_id, context) {
|
||||
DrivingModelFrame::DrivingModelFrame(cl_device_id device_id, cl_context context, int _temporal_skip) : ModelFrame(device_id, context) {
|
||||
input_frames = std::make_unique<uint8_t[]>(buf_size);
|
||||
temporal_skip = _temporal_skip;
|
||||
input_frames_cl = CL_CHECK_ERR(clCreateBuffer(context, CL_MEM_READ_WRITE, buf_size, NULL, &err));
|
||||
img_buffer_20hz_cl = CL_CHECK_ERR(clCreateBuffer(context, CL_MEM_READ_WRITE, 2*frame_size_bytes, NULL, &err));
|
||||
region.origin = 1 * frame_size_bytes;
|
||||
img_buffer_20hz_cl = CL_CHECK_ERR(clCreateBuffer(context, CL_MEM_READ_WRITE, (temporal_skip+1)*frame_size_bytes, NULL, &err));
|
||||
region.origin = temporal_skip * frame_size_bytes;
|
||||
region.size = frame_size_bytes;
|
||||
last_img_cl = CL_CHECK_ERR(clCreateSubBuffer(img_buffer_20hz_cl, CL_MEM_READ_WRITE, CL_BUFFER_CREATE_TYPE_REGION, ®ion, &err));
|
||||
|
||||
@@ -20,7 +21,7 @@ DrivingModelFrame::DrivingModelFrame(cl_device_id device_id, cl_context context)
|
||||
cl_mem* DrivingModelFrame::prepare(cl_mem yuv_cl, int frame_width, int frame_height, int frame_stride, int frame_uv_offset, const mat3& projection) {
|
||||
run_transform(yuv_cl, MODEL_WIDTH, MODEL_HEIGHT, frame_width, frame_height, frame_stride, frame_uv_offset, projection);
|
||||
|
||||
for (int i = 0; i < 1; i++) {
|
||||
for (int i = 0; i < temporal_skip; i++) {
|
||||
CL_CHECK(clEnqueueCopyBuffer(q, img_buffer_20hz_cl, img_buffer_20hz_cl, (i+1)*frame_size_bytes, i*frame_size_bytes, frame_size_bytes, 0, nullptr, nullptr));
|
||||
}
|
||||
loadyuv_queue(&loadyuv, q, y_cl, u_cl, v_cl, last_img_cl);
|
||||
|
||||
@@ -64,20 +64,21 @@ protected:
|
||||
|
||||
class DrivingModelFrame : public ModelFrame {
|
||||
public:
|
||||
DrivingModelFrame(cl_device_id device_id, cl_context context);
|
||||
DrivingModelFrame(cl_device_id device_id, cl_context context, int _temporal_skip);
|
||||
~DrivingModelFrame();
|
||||
cl_mem* prepare(cl_mem yuv_cl, int frame_width, int frame_height, int frame_stride, int frame_uv_offset, const mat3& projection);
|
||||
|
||||
const int MODEL_WIDTH = 512;
|
||||
const int MODEL_HEIGHT = 256;
|
||||
const int MODEL_FRAME_SIZE = MODEL_WIDTH * MODEL_HEIGHT * 3 / 2;
|
||||
const int buf_size = MODEL_FRAME_SIZE * 2;
|
||||
const int buf_size = MODEL_FRAME_SIZE * 2; // 2 frames are temporal_skip frames apart
|
||||
const size_t frame_size_bytes = MODEL_FRAME_SIZE * sizeof(uint8_t);
|
||||
|
||||
private:
|
||||
LoadYUVState loadyuv;
|
||||
cl_mem img_buffer_20hz_cl, last_img_cl, input_frames_cl;
|
||||
cl_buffer_region region;
|
||||
int temporal_skip;
|
||||
};
|
||||
|
||||
class MonitoringModelFrame : public ModelFrame {
|
||||
|
||||
@@ -20,7 +20,7 @@ cdef extern from "selfdrive/modeld/models/commonmodel.h":
|
||||
|
||||
cppclass DrivingModelFrame:
|
||||
int buf_size
|
||||
DrivingModelFrame(cl_device_id, cl_context)
|
||||
DrivingModelFrame(cl_device_id, cl_context, int)
|
||||
|
||||
cppclass MonitoringModelFrame:
|
||||
int buf_size
|
||||
|
||||
@@ -59,8 +59,8 @@ cdef class ModelFrame:
|
||||
cdef class DrivingModelFrame(ModelFrame):
|
||||
cdef cppDrivingModelFrame * _frame
|
||||
|
||||
def __cinit__(self, CLContext context):
|
||||
self._frame = new cppDrivingModelFrame(context.device_id, context.context)
|
||||
def __cinit__(self, CLContext context, int temporal_skip):
|
||||
self._frame = new cppDrivingModelFrame(context.device_id, context.context, temporal_skip)
|
||||
self.frame = <cppModelFrame*>(self._frame)
|
||||
self.buf_size = self._frame.buf_size
|
||||
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:98f0121ccb6f850077b04cc91bd33d370fc6cbdc2bd35f1ab55628a15a813f36
|
||||
size 15966721
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:897f80d0388250f99bba69b6a8434560cc0fd83157cbeb0bc134c67fe4e64624
|
||||
size 34882971
|
||||
@@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:d21daa542227ecc5972da45df4e26f018ba113c0461f270e367d57e3ad89221a
|
||||
size 51461700
|
||||
@@ -84,23 +84,32 @@ class Parser:
|
||||
outs[name] = pred_mu_final.reshape(final_shape)
|
||||
outs[name + '_stds'] = pred_std_final.reshape(final_shape)
|
||||
|
||||
def parse_outputs(self, outs: dict[str, np.ndarray]) -> dict[str, np.ndarray]:
|
||||
def parse_vision_outputs(self, outs: dict[str, np.ndarray]) -> dict[str, np.ndarray]:
|
||||
self.parse_mdn('pose', outs, in_N=0, out_N=0, out_shape=(ModelConstants.POSE_WIDTH,))
|
||||
self.parse_mdn('wide_from_device_euler', outs, in_N=0, out_N=0, out_shape=(ModelConstants.WIDE_FROM_DEVICE_WIDTH,))
|
||||
self.parse_mdn('road_transform', outs, in_N=0, out_N=0, out_shape=(ModelConstants.POSE_WIDTH,))
|
||||
self.parse_categorical_crossentropy('desire_pred', outs, out_shape=(ModelConstants.DESIRE_PRED_LEN,ModelConstants.DESIRE_PRED_WIDTH))
|
||||
self.parse_binary_crossentropy('meta', outs)
|
||||
return outs
|
||||
|
||||
def parse_policy_outputs(self, outs: dict[str, np.ndarray]) -> dict[str, np.ndarray]:
|
||||
self.parse_mdn('plan', outs, in_N=ModelConstants.PLAN_MHP_N, out_N=ModelConstants.PLAN_MHP_SELECTION,
|
||||
out_shape=(ModelConstants.IDX_N,ModelConstants.PLAN_WIDTH))
|
||||
self.parse_mdn('lane_lines', outs, in_N=0, out_N=0, out_shape=(ModelConstants.NUM_LANE_LINES,ModelConstants.IDX_N,ModelConstants.LANE_LINES_WIDTH))
|
||||
self.parse_mdn('road_edges', outs, in_N=0, out_N=0, out_shape=(ModelConstants.NUM_ROAD_EDGES,ModelConstants.IDX_N,ModelConstants.LANE_LINES_WIDTH))
|
||||
self.parse_mdn('pose', outs, in_N=0, out_N=0, out_shape=(ModelConstants.POSE_WIDTH,))
|
||||
self.parse_mdn('road_transform', outs, in_N=0, out_N=0, out_shape=(ModelConstants.POSE_WIDTH,))
|
||||
self.parse_mdn('sim_pose', outs, in_N=0, out_N=0, out_shape=(ModelConstants.POSE_WIDTH,))
|
||||
self.parse_mdn('wide_from_device_euler', outs, in_N=0, out_N=0, out_shape=(ModelConstants.WIDE_FROM_DEVICE_WIDTH,))
|
||||
self.parse_mdn('lead', outs, in_N=ModelConstants.LEAD_MHP_N, out_N=ModelConstants.LEAD_MHP_SELECTION,
|
||||
out_shape=(ModelConstants.LEAD_TRAJ_LEN,ModelConstants.LEAD_WIDTH))
|
||||
if 'lat_planner_solution' in outs:
|
||||
self.parse_mdn('lat_planner_solution', outs, in_N=0, out_N=0, out_shape=(ModelConstants.IDX_N,ModelConstants.LAT_PLANNER_SOLUTION_WIDTH))
|
||||
if 'desired_curvature' in outs:
|
||||
self.parse_mdn('desired_curvature', outs, in_N=0, out_N=0, out_shape=(ModelConstants.DESIRED_CURV_WIDTH,))
|
||||
for k in ['lead_prob', 'lane_lines_prob', 'meta']:
|
||||
for k in ['lead_prob', 'lane_lines_prob']:
|
||||
self.parse_binary_crossentropy(k, outs)
|
||||
self.parse_categorical_crossentropy('desire_state', outs, out_shape=(ModelConstants.DESIRE_PRED_WIDTH,))
|
||||
self.parse_categorical_crossentropy('desire_pred', outs, out_shape=(ModelConstants.DESIRE_PRED_LEN,ModelConstants.DESIRE_PRED_WIDTH))
|
||||
return outs
|
||||
|
||||
def parse_outputs(self, outs: dict[str, np.ndarray]) -> dict[str, np.ndarray]:
|
||||
outs = self.parse_vision_outputs(outs)
|
||||
outs = self.parse_policy_outputs(outs)
|
||||
return outs
|
||||
|
||||
@@ -1,101 +0,0 @@
|
||||
// clang++ -O2 repro.cc && ./a.out
|
||||
|
||||
#include <sched.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
|
||||
static inline double millis_since_boot() {
|
||||
struct timespec t;
|
||||
clock_gettime(CLOCK_BOOTTIME, &t);
|
||||
return t.tv_sec * 1000.0 + t.tv_nsec * 1e-6;
|
||||
}
|
||||
|
||||
#define MODEL_WIDTH 320
|
||||
#define MODEL_HEIGHT 640
|
||||
|
||||
// null function still breaks it
|
||||
#define input_lambda(x) x
|
||||
|
||||
// this is copied from models/dmonitoring.cc, and is the code that triggers the issue
|
||||
void inner(uint8_t *resized_buf, float *net_input_buf) {
|
||||
int resized_width = MODEL_WIDTH;
|
||||
int resized_height = MODEL_HEIGHT;
|
||||
|
||||
// one shot conversion, O(n) anyway
|
||||
// yuvframe2tensor, normalize
|
||||
for (int r = 0; r < MODEL_HEIGHT/2; r++) {
|
||||
for (int c = 0; c < MODEL_WIDTH/2; c++) {
|
||||
// Y_ul
|
||||
net_input_buf[(c*MODEL_HEIGHT/2) + r] = input_lambda(resized_buf[(2*r*resized_width) + (2*c)]);
|
||||
// Y_ur
|
||||
net_input_buf[(c*MODEL_HEIGHT/2) + r + (2*(MODEL_WIDTH/2)*(MODEL_HEIGHT/2))] = input_lambda(resized_buf[(2*r*resized_width) + (2*c+1)]);
|
||||
// Y_dl
|
||||
net_input_buf[(c*MODEL_HEIGHT/2) + r + ((MODEL_WIDTH/2)*(MODEL_HEIGHT/2))] = input_lambda(resized_buf[(2*r*resized_width+1) + (2*c)]);
|
||||
// Y_dr
|
||||
net_input_buf[(c*MODEL_HEIGHT/2) + r + (3*(MODEL_WIDTH/2)*(MODEL_HEIGHT/2))] = input_lambda(resized_buf[(2*r*resized_width+1) + (2*c+1)]);
|
||||
// U
|
||||
net_input_buf[(c*MODEL_HEIGHT/2) + r + (4*(MODEL_WIDTH/2)*(MODEL_HEIGHT/2))] = input_lambda(resized_buf[(resized_width*resized_height) + (r*resized_width/2) + c]);
|
||||
// V
|
||||
net_input_buf[(c*MODEL_HEIGHT/2) + r + (5*(MODEL_WIDTH/2)*(MODEL_HEIGHT/2))] = input_lambda(resized_buf[(resized_width*resized_height) + ((resized_width/2)*(resized_height/2)) + (r*resized_width/2) + c]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float trial() {
|
||||
int resized_width = MODEL_WIDTH;
|
||||
int resized_height = MODEL_HEIGHT;
|
||||
|
||||
int yuv_buf_len = (MODEL_WIDTH/2) * (MODEL_HEIGHT/2) * 6; // Y|u|v -> y|y|y|y|u|v
|
||||
|
||||
// allocate the buffers
|
||||
uint8_t *resized_buf = (uint8_t*)malloc(resized_width*resized_height*3/2);
|
||||
float *net_input_buf = (float*)malloc(yuv_buf_len*sizeof(float));
|
||||
printf("allocate -- %p 0x%x -- %p 0x%lx\n", resized_buf, resized_width*resized_height*3/2, net_input_buf, yuv_buf_len*sizeof(float));
|
||||
|
||||
// test for bad buffers
|
||||
static int CNT = 20;
|
||||
float avg = 0.0;
|
||||
for (int i = 0; i < CNT; i++) {
|
||||
double s4 = millis_since_boot();
|
||||
inner(resized_buf, net_input_buf);
|
||||
double s5 = millis_since_boot();
|
||||
avg += s5-s4;
|
||||
}
|
||||
avg /= CNT;
|
||||
|
||||
// once it's bad, it's reliably bad
|
||||
if (avg > 10) {
|
||||
printf("HIT %f\n", avg);
|
||||
printf("BAD\n");
|
||||
|
||||
for (int i = 0; i < 200; i++) {
|
||||
double s4 = millis_since_boot();
|
||||
inner(resized_buf, net_input_buf);
|
||||
double s5 = millis_since_boot();
|
||||
printf("%.2f ", s5-s4);
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
exit(0);
|
||||
}
|
||||
|
||||
// don't free so we get a different buffer each time
|
||||
//free(resized_buf);
|
||||
//free(net_input_buf);
|
||||
|
||||
return avg;
|
||||
}
|
||||
|
||||
int main() {
|
||||
while (true) {
|
||||
float ret = trial();
|
||||
printf("got %f\n", ret);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
clang++ -I /home/batman/one/external/tensorflow/include/ -L /home/batman/one/external/tensorflow/lib -Wl,-rpath=/home/batman/one/external/tensorflow/lib main.cc -ltensorflow
|
||||
@@ -1,69 +0,0 @@
|
||||
#include <cassert>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include "tensorflow/c/c_api.h"
|
||||
|
||||
void* read_file(const char* path, size_t* out_len) {
|
||||
FILE* f = fopen(path, "r");
|
||||
if (!f) {
|
||||
return NULL;
|
||||
}
|
||||
fseek(f, 0, SEEK_END);
|
||||
long f_len = ftell(f);
|
||||
rewind(f);
|
||||
|
||||
char* buf = (char*)calloc(f_len, 1);
|
||||
assert(buf);
|
||||
|
||||
size_t num_read = fread(buf, f_len, 1, f);
|
||||
fclose(f);
|
||||
|
||||
if (num_read != 1) {
|
||||
free(buf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (out_len) {
|
||||
*out_len = f_len;
|
||||
}
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
static void DeallocateBuffer(void* data, size_t) {
|
||||
free(data);
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
TF_Buffer* buf;
|
||||
TF_Graph* graph;
|
||||
TF_Status* status;
|
||||
char *path = argv[1];
|
||||
|
||||
// load model
|
||||
{
|
||||
size_t model_size;
|
||||
char tmp[1024];
|
||||
snprintf(tmp, sizeof(tmp), "%s.pb", path);
|
||||
printf("loading model %s\n", tmp);
|
||||
uint8_t *model_data = (uint8_t *)read_file(tmp, &model_size);
|
||||
buf = TF_NewBuffer();
|
||||
buf->data = model_data;
|
||||
buf->length = model_size;
|
||||
buf->data_deallocator = DeallocateBuffer;
|
||||
printf("loaded model of size %d\n", model_size);
|
||||
}
|
||||
|
||||
// import graph
|
||||
status = TF_NewStatus();
|
||||
graph = TF_NewGraph();
|
||||
TF_ImportGraphDefOptions *opts = TF_NewImportGraphDefOptions();
|
||||
TF_GraphImportGraphDef(graph, buf, opts, status);
|
||||
TF_DeleteImportGraphDefOptions(opts);
|
||||
TF_DeleteBuffer(buf);
|
||||
if (TF_GetCode(status) != TF_OK) {
|
||||
printf("FAIL: %s\n", TF_Message(status));
|
||||
} else {
|
||||
printf("SUCCESS\n");
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
import sys
|
||||
import tensorflow as tf
|
||||
|
||||
with open(sys.argv[1], "rb") as f:
|
||||
graph_def = tf.compat.v1.GraphDef()
|
||||
graph_def.ParseFromString(f.read())
|
||||
#tf.io.write_graph(graph_def, '', sys.argv[1]+".try")
|
||||
@@ -1,39 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# type: ignore
|
||||
|
||||
import os
|
||||
import time
|
||||
import numpy as np
|
||||
|
||||
import cereal.messaging as messaging
|
||||
from openpilot.system.manager.process_config import managed_processes
|
||||
|
||||
|
||||
N = int(os.getenv("N", "5"))
|
||||
TIME = int(os.getenv("TIME", "30"))
|
||||
|
||||
if __name__ == "__main__":
|
||||
sock = messaging.sub_sock('modelV2', conflate=False, timeout=1000)
|
||||
|
||||
execution_times = []
|
||||
|
||||
for _ in range(N):
|
||||
os.environ['LOGPRINT'] = 'debug'
|
||||
managed_processes['modeld'].start()
|
||||
time.sleep(5)
|
||||
|
||||
t = []
|
||||
start = time.monotonic()
|
||||
while time.monotonic() - start < TIME:
|
||||
msgs = messaging.drain_sock(sock, wait_for_one=True)
|
||||
for m in msgs:
|
||||
t.append(m.modelV2.modelExecutionTime)
|
||||
|
||||
execution_times.append(np.array(t[10:]) * 1000)
|
||||
managed_processes['modeld'].stop()
|
||||
|
||||
print("\n\n")
|
||||
print(f"ran modeld {N} times for {TIME}s each")
|
||||
for _, t in enumerate(execution_times):
|
||||
print(f"\tavg: {sum(t)/len(t):0.2f}ms, min: {min(t):0.2f}ms, max: {max(t):0.2f}ms")
|
||||
print("\n\n")
|
||||
@@ -292,6 +292,24 @@ def calibration_invalid_alert(CP: car.CarParams, CS: car.CarState, sm: messaging
|
||||
return NormalPermanentAlert("Calibration Invalid", angles)
|
||||
|
||||
|
||||
def paramsd_invalid_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int, personality) -> Alert:
|
||||
if not sm['liveParameters'].angleOffsetValid:
|
||||
angle_offset_deg = sm['liveParameters'].angleOffsetDeg
|
||||
title = "Steering misalignment detected"
|
||||
text = f"Angle offset too high (Offset: {angle_offset_deg:.1f}°)"
|
||||
elif not sm['liveParameters'].steerRatioValid:
|
||||
steer_ratio = sm['liveParameters'].steerRatio
|
||||
title = "Steer ratio mismatch"
|
||||
text = f"Steering rack geometry may be off (Ratio: {steer_ratio:.1f})"
|
||||
elif not sm['liveParameters'].stiffnessFactorValid:
|
||||
stiffness_factor = sm['liveParameters'].stiffnessFactor
|
||||
title = "Abnormal tire stiffness"
|
||||
text = f"Check tires, pressure, or alignment (Factor: {stiffness_factor:.1f})"
|
||||
else:
|
||||
return NoEntryAlert("paramsd Temporary Error")
|
||||
|
||||
return NoEntryAlert(alert_text_1=title, alert_text_2=text)
|
||||
|
||||
def overheat_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int, personality) -> Alert:
|
||||
cpu = max(sm['deviceState'].cpuTempC, default=0.)
|
||||
gpu = max(sm['deviceState'].gpuTempC, default=0.)
|
||||
@@ -321,7 +339,7 @@ def wrong_car_mode_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubM
|
||||
|
||||
def joystick_alert(CP: car.CarParams, CS: car.CarState, sm: messaging.SubMaster, metric: bool, soft_disable_time: int, personality) -> Alert:
|
||||
gb = sm['carControl'].actuators.accel / 4.
|
||||
steer = sm['carControl'].actuators.steer
|
||||
steer = sm['carControl'].actuators.torque
|
||||
vals = f"Gas: {round(gb * 100.)}%, Steer: {round(steer * 100.)}%"
|
||||
return NormalPermanentAlert("Joystick Mode", vals)
|
||||
|
||||
@@ -604,7 +622,7 @@ EVENTS: dict[int, dict[str, Alert | AlertCallbackType]] = {
|
||||
# This alert is thrown when any of these values exceed a sanity check. This can be caused by
|
||||
# bad alignment or bad sensor data. If this happens consistently consider creating an issue on GitHub
|
||||
EventName.paramsdTemporaryError: {
|
||||
ET.NO_ENTRY: NoEntryAlert("paramsd Temporary Error"),
|
||||
ET.NO_ENTRY: paramsd_invalid_alert,
|
||||
ET.SOFT_DISABLE: soft_disable_alert("paramsd Temporary Error"),
|
||||
},
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ from openpilot.system.version import get_build_metadata
|
||||
REPLAY = "REPLAY" in os.environ
|
||||
SIMULATION = "SIMULATION" in os.environ
|
||||
TESTING_CLOSET = "TESTING_CLOSET" in os.environ
|
||||
IGNORE_PROCESSES = {"loggerd", "encoderd", "statsd"}
|
||||
LONGITUDINAL_PERSONALITY_MAP = {v: k for k, v in log.LongitudinalPersonality.schema.enumerants.items()}
|
||||
|
||||
ThermalStatus = log.DeviceState.ThermalStatus
|
||||
@@ -258,7 +259,7 @@ class SelfdriveD:
|
||||
if not_running != self.not_running_prev:
|
||||
cloudlog.event("process_not_running", not_running=not_running, error=True)
|
||||
self.not_running_prev = not_running
|
||||
if self.sm.recv_frame['managerState'] and not_running:
|
||||
if self.sm.recv_frame['managerState'] and (not_running - IGNORE_PROCESSES):
|
||||
self.events.add(EventName.processNotRunning)
|
||||
else:
|
||||
if not SIMULATION and not self.rk.lagging:
|
||||
@@ -327,11 +328,11 @@ class SelfdriveD:
|
||||
if lac.active and not recent_steer_pressed and not self.CP.notCar:
|
||||
clipped_speed = max(CS.vEgo, MIN_LATERAL_CONTROL_SPEED)
|
||||
actual_lateral_accel = controlstate.curvature * (clipped_speed**2)
|
||||
desired_lateral_accel = controlstate.desiredCurvature * (clipped_speed**2)
|
||||
desired_lateral_accel = self.sm['modelV2'].action.desiredCurvature * (clipped_speed**2)
|
||||
undershooting = abs(desired_lateral_accel) / abs(1e-3 + actual_lateral_accel) > 1.2
|
||||
turning = abs(desired_lateral_accel) > 1.0
|
||||
good_speed = CS.vEgo > 5
|
||||
if undershooting and turning and good_speed and lac.saturated:
|
||||
# TODO: lac.saturated includes speed and other checks, should be pulled out
|
||||
if undershooting and turning and lac.saturated:
|
||||
self.events.add(EventName.steerSaturated)
|
||||
|
||||
# Check for FCW
|
||||
|
||||
@@ -358,6 +358,7 @@ def migrate_cameraStates(msgs):
|
||||
|
||||
new_msg = messaging.new_message(msg.which())
|
||||
new_camera_state = getattr(new_msg, new_msg.which())
|
||||
new_camera_state.sensor = camera_state.sensor
|
||||
new_camera_state.frameId = encode_id
|
||||
new_camera_state.encodeId = encode_id
|
||||
# timestampSof was added later so it might be missing on some old segments
|
||||
|
||||
@@ -19,8 +19,8 @@ from openpilot.tools.lib.framereader import FrameReader, NumpyFrameReader
|
||||
from openpilot.tools.lib.logreader import LogReader, save_log
|
||||
from openpilot.tools.lib.github_utils import GithubUtils
|
||||
|
||||
TEST_ROUTE = "2f4452b03ccb98f0|2022-12-03--13-45-30"
|
||||
SEGMENT = 6
|
||||
TEST_ROUTE = "8494c69d3c710e81|000001d4--2648a9a404"
|
||||
SEGMENT = 4
|
||||
MAX_FRAMES = 100 if PC else 400
|
||||
|
||||
NO_MODEL = "NO_MODEL" in os.environ
|
||||
@@ -66,6 +66,8 @@ def generate_report(proposed, master, tmp, commit):
|
||||
(lambda x: get_idx_if_non_empty(x.action.desiredCurvature), "desiredCurvature"),
|
||||
(lambda x: get_idx_if_non_empty(x.leadsV3[0].x, 0), "leadsV3.x"),
|
||||
(lambda x: get_idx_if_non_empty(x.laneLines[1].y, 0), "laneLines.y"),
|
||||
(lambda x: get_idx_if_non_empty(x.meta.desireState, 3), "desireState.laneChangeLeft"),
|
||||
(lambda x: get_idx_if_non_empty(x.meta.desireState, 4), "desireState.laneChangeRight"),
|
||||
(lambda x: get_idx_if_non_empty(x.meta.disengagePredictions.gasPressProbs, 1), "gasPressProbs")
|
||||
], "modelV2")
|
||||
DriverStateV2_Plots = zl([
|
||||
@@ -143,7 +145,8 @@ def trim_logs_to_max_frames(logs, max_frames, frs_types, include_all_types):
|
||||
|
||||
def model_replay(lr, frs):
|
||||
# modeld is using frame pairs
|
||||
modeld_logs = trim_logs_to_max_frames(lr, MAX_FRAMES, {"roadCameraState", "wideRoadCameraState"}, {"roadEncodeIdx", "wideRoadEncodeIdx", "carParams"})
|
||||
modeld_logs = trim_logs_to_max_frames(lr, MAX_FRAMES, {"roadCameraState", "wideRoadCameraState"},
|
||||
{"roadEncodeIdx", "wideRoadEncodeIdx", "carParams", "carState", "carControl"})
|
||||
dmodeld_logs = trim_logs_to_max_frames(lr, MAX_FRAMES, {"driverCameraState"}, {"driverEncodeIdx", "carParams"})
|
||||
|
||||
if not SEND_EXTRA_INPUTS:
|
||||
|
||||
@@ -568,7 +568,7 @@ CONFIGS = [
|
||||
),
|
||||
ProcessConfig(
|
||||
proc_name="modeld",
|
||||
pubs=["deviceState", "roadCameraState", "wideRoadCameraState", "liveCalibration", "driverMonitoringState", "carState"],
|
||||
pubs=["deviceState", "roadCameraState", "wideRoadCameraState", "liveCalibration", "driverMonitoringState", "carState", "carControl"],
|
||||
subs=["modelV2", "drivingModelData", "cameraOdometry"],
|
||||
ignore=["logMonoTime", "modelV2.frameDropPerc", "modelV2.modelExecutionTime", "drivingModelData.frameDropPerc", "drivingModelData.modelExecutionTime"],
|
||||
should_recv_callback=ModeldCameraSyncRcvCallback(),
|
||||
|
||||
@@ -1 +1 @@
|
||||
fab469a6d651df3e17ac018f41b8d2a806d6146a
|
||||
465979047295dad5da3a552db9227ed776a26a79
|
||||
@@ -12,10 +12,9 @@ from openpilot.selfdrive.test.process_replay.process_replay import CONFIGS, FAKE
|
||||
check_openpilot_enabled, check_most_messages_valid, get_custom_params_from_lr
|
||||
from openpilot.selfdrive.test.process_replay.vision_meta import DRIVER_CAMERA_FRAME_SIZES
|
||||
from openpilot.selfdrive.test.update_ci_routes import upload_route
|
||||
from openpilot.tools.lib.route import Route
|
||||
from openpilot.tools.lib.framereader import FrameReader, BaseFrameReader, FrameType
|
||||
from openpilot.tools.lib.logreader import LogReader, LogIterable, save_log
|
||||
|
||||
from openpilot.tools.lib.openpilotci import get_url
|
||||
|
||||
class DummyFrameReader(BaseFrameReader):
|
||||
def __init__(self, w: int, h: int, frame_count: int, pix_val: int):
|
||||
@@ -55,45 +54,28 @@ def regen_segment(
|
||||
|
||||
|
||||
def setup_data_readers(
|
||||
route: str, sidx: int, use_route_meta: bool,
|
||||
needs_driver_cam: bool = True, needs_road_cam: bool = True, dummy_driver_cam: bool = False
|
||||
route: str, sidx: int, needs_driver_cam: bool = True, needs_road_cam: bool = True, dummy_driver_cam: bool = False
|
||||
) -> tuple[LogReader, dict[str, Any]]:
|
||||
if use_route_meta:
|
||||
r = Route(route)
|
||||
lr = LogReader(r.log_paths()[sidx])
|
||||
frs = {}
|
||||
if needs_road_cam and len(r.camera_paths()) > sidx and r.camera_paths()[sidx] is not None:
|
||||
frs['roadCameraState'] = FrameReader(r.camera_paths()[sidx])
|
||||
if needs_road_cam and len(r.ecamera_paths()) > sidx and r.ecamera_paths()[sidx] is not None:
|
||||
frs['wideRoadCameraState'] = FrameReader(r.ecamera_paths()[sidx])
|
||||
if needs_driver_cam:
|
||||
if dummy_driver_cam:
|
||||
frs['driverCameraState'] = DummyFrameReader.zero_dcamera()
|
||||
elif len(r.dcamera_paths()) > sidx and r.dcamera_paths()[sidx] is not None:
|
||||
device_type = next(str(msg.initData.deviceType) for msg in lr if msg.which() == "initData")
|
||||
assert device_type != "neo", "Driver camera not supported on neo segments. Use dummy dcamera."
|
||||
frs['driverCameraState'] = FrameReader(r.dcamera_paths()[sidx])
|
||||
else:
|
||||
lr = LogReader(f"{route}/{sidx}/r")
|
||||
frs = {}
|
||||
if needs_road_cam:
|
||||
frs['roadCameraState'] = FrameReader(f"cd:/{route.replace('|', '/')}/{sidx}/fcamera.hevc")
|
||||
if next((True for m in lr if m.which() == "wideRoadCameraState"), False):
|
||||
frs['wideRoadCameraState'] = FrameReader(f"cd:/{route.replace('|', '/')}/{sidx}/ecamera.hevc")
|
||||
if needs_driver_cam:
|
||||
if dummy_driver_cam:
|
||||
frs['driverCameraState'] = DummyFrameReader.zero_dcamera()
|
||||
else:
|
||||
device_type = next(str(msg.initData.deviceType) for msg in lr if msg.which() == "initData")
|
||||
assert device_type != "neo", "Driver camera not supported on neo segments. Use dummy dcamera."
|
||||
frs['driverCameraState'] = FrameReader(f"cd:/{route.replace('|', '/')}/{sidx}/dcamera.hevc")
|
||||
lr = LogReader(f"{route}/{sidx}/r")
|
||||
frs = {}
|
||||
if needs_road_cam:
|
||||
frs['roadCameraState'] = FrameReader(get_url(route, str(sidx), "fcamera.hevc"))
|
||||
if next((True for m in lr if m.which() == "wideRoadCameraState"), False):
|
||||
frs['wideRoadCameraState'] = FrameReader(get_url(route, str(sidx), "ecamera.hevc"))
|
||||
if needs_driver_cam:
|
||||
if dummy_driver_cam:
|
||||
frs['driverCameraState'] = DummyFrameReader.zero_dcamera()
|
||||
else:
|
||||
device_type = next(str(msg.initData.deviceType) for msg in lr if msg.which() == "initData")
|
||||
assert device_type != "neo", "Driver camera not supported on neo segments. Use dummy dcamera."
|
||||
frs['driverCameraState'] = FrameReader(get_url(route, str(sidx), "dcamera.hevc"))
|
||||
|
||||
return lr, frs
|
||||
|
||||
|
||||
def regen_and_save(
|
||||
route: str, sidx: int, processes: str | Iterable[str] = "all", outdir: str = FAKEDATA,
|
||||
upload: bool = False, use_route_meta: bool = False, disable_tqdm: bool = False, dummy_driver_cam: bool = False
|
||||
upload: bool = False, disable_tqdm: bool = False, dummy_driver_cam: bool = False
|
||||
) -> str:
|
||||
if not isinstance(processes, str) and not hasattr(processes, "__iter__"):
|
||||
raise ValueError("whitelist_proc must be a string or iterable")
|
||||
@@ -110,7 +92,7 @@ def regen_and_save(
|
||||
replayed_processes = CONFIGS
|
||||
|
||||
all_vision_pubs = {pub for cfg in replayed_processes for pub in cfg.vision_pubs}
|
||||
lr, frs = setup_data_readers(route, sidx, use_route_meta,
|
||||
lr, frs = setup_data_readers(route, sidx,
|
||||
needs_driver_cam="driverCameraState" in all_vision_pubs,
|
||||
needs_road_cam="roadCameraState" in all_vision_pubs or "wideRoadCameraState" in all_vision_pubs,
|
||||
dummy_driver_cam=dummy_driver_cam)
|
||||
|
||||
@@ -17,7 +17,7 @@ def regen_job(segment, upload, disable_tqdm):
|
||||
sn = SegmentName(segment[1])
|
||||
fake_dongle_id = 'regen' + ''.join(random.choice('0123456789ABCDEF') for _ in range(11))
|
||||
try:
|
||||
relr = regen_and_save(sn.route_name.canonical_name, sn.segment_num, upload=upload, use_route_meta=False,
|
||||
relr = regen_and_save(sn.route_name.canonical_name, sn.segment_num, upload=upload,
|
||||
outdir=os.path.join(FAKEDATA, fake_dongle_id), disable_tqdm=disable_tqdm, dummy_driver_cam=True)
|
||||
relr = '|'.join(relr.split('/')[-2:])
|
||||
return f' ("{segment[0]}", "{relr}"), '
|
||||
|
||||
@@ -42,22 +42,22 @@ source_segments = [
|
||||
|
||||
segments = [
|
||||
("BODY", "regenA67A128BCD8|2024-08-30--02-36-22--0"),
|
||||
("HYUNDAI", "regen9CBD921E93E|2024-08-30--02-38-51--0"),
|
||||
("HYUNDAI", "regenCCD47FEBC0C|2025-03-04--03-21-48--0"),
|
||||
("HYUNDAI2", "regen306779F6870|2024-10-03--04-03-23--0"),
|
||||
("TOYOTA", "regen1CA7A48E6F7|2024-08-30--02-45-08--0"),
|
||||
("TOYOTA", "regen4A5115B248D|2025-03-04--03-21-43--0"),
|
||||
("TOYOTA2", "regen6E484EDAB96|2024-08-30--02-47-37--0"),
|
||||
("TOYOTA3", "regen4CE950B0267|2024-08-30--02-51-30--0"),
|
||||
("HONDA", "regenC8F0D6ADC5C|2024-08-30--02-54-01--0"),
|
||||
("HONDA", "regenB8CABEC09CC|2025-03-04--03-32-55--0"),
|
||||
("HONDA2", "regen4B38A7428CD|2024-08-30--02-56-31--0"),
|
||||
("CHRYSLER", "regenF3DBBA9E8DF|2024-08-30--02-59-03--0"),
|
||||
("RAM", "regenDB02684E00A|2024-08-30--03-02-54--0"),
|
||||
("SUBARU", "regenAA1FF48CF1F|2024-08-30--03-06-45--0"),
|
||||
("SUBARU", "regen5E3347D0A0F|2025-03-04--03-23-55--0"),
|
||||
("GM", "regen720F2BA4CF6|2024-08-30--03-09-15--0"),
|
||||
("GM2", "regen9ADBECBCD1C|2024-08-30--03-13-04--0"),
|
||||
("NISSAN", "regen58464878D07|2024-08-30--03-15-31--0"),
|
||||
("VOLKSWAGEN", "regenED976DEB757|2024-08-30--03-18-02--0"),
|
||||
("MAZDA", "regenACF84CCF482|2024-08-30--03-21-55--0"),
|
||||
("FORD", "regen756F8230C21|2024-11-07--00-08-24--0"),
|
||||
("FORD", "regenA75209BD115|2025-03-04--03-23-53--0"),
|
||||
("RIVIAN", "bc095dc92e101734|000000db--ee9fe46e57--1"),
|
||||
]
|
||||
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import math
|
||||
import json
|
||||
import os
|
||||
import pathlib
|
||||
import psutil
|
||||
import pytest
|
||||
import shutil
|
||||
import subprocess
|
||||
@@ -12,7 +10,7 @@ from collections import Counter, defaultdict
|
||||
from pathlib import Path
|
||||
from tabulate import tabulate
|
||||
|
||||
from cereal import car, log
|
||||
from cereal import log
|
||||
import cereal.messaging as messaging
|
||||
from cereal.services import SERVICE_LIST
|
||||
from openpilot.common.basedir import BASEDIR
|
||||
@@ -23,6 +21,7 @@ from openpilot.selfdrive.test.helpers import set_params_enabled, release_only
|
||||
from openpilot.system.hardware import HARDWARE
|
||||
from openpilot.system.hardware.hw import Paths
|
||||
from openpilot.tools.lib.logreader import LogReader
|
||||
from openpilot.tools.lib.log_time_series import msgs_to_time_series
|
||||
|
||||
"""
|
||||
CPU usage budget
|
||||
@@ -112,6 +111,7 @@ def cputime_total(ct):
|
||||
|
||||
|
||||
@pytest.mark.tici
|
||||
@pytest.mark.skip_tici_setup
|
||||
class TestOnroad:
|
||||
|
||||
@classmethod
|
||||
@@ -119,7 +119,8 @@ class TestOnroad:
|
||||
if "DEBUG" in os.environ:
|
||||
segs = filter(lambda x: os.path.exists(os.path.join(x, "rlog.zst")), Path(Paths.log_root()).iterdir())
|
||||
segs = sorted(segs, key=lambda x: x.stat().st_mtime)
|
||||
cls.lr = list(LogReader(os.path.join(segs[-3], "rlog.zst")))
|
||||
cls.lr = list(LogReader(os.path.join(segs[-1], "rlog.zst")))
|
||||
cls.ts = msgs_to_time_series(cls.lr)
|
||||
return
|
||||
|
||||
# setup env
|
||||
@@ -140,44 +141,31 @@ class TestOnroad:
|
||||
proc = subprocess.Popen(["python", manager_path])
|
||||
|
||||
sm = messaging.SubMaster(['carState'])
|
||||
with Timeout(150, "controls didn't start"):
|
||||
while sm.recv_frame['carState'] < 0:
|
||||
with Timeout(30, "controls didn't start"):
|
||||
while not sm.seen['carState']:
|
||||
sm.update(1000)
|
||||
|
||||
route = None
|
||||
cls.segments = []
|
||||
with Timeout(300, "timed out waiting for logs"):
|
||||
while route is None:
|
||||
route = params.get("CurrentRoute", encoding="utf-8")
|
||||
time.sleep(0.01)
|
||||
route = params.get("CurrentRoute", encoding="utf-8")
|
||||
assert route is not None
|
||||
|
||||
# test car params caching
|
||||
params.put("CarParamsCache", car.CarParams().to_bytes())
|
||||
|
||||
while len(cls.segments) < 1:
|
||||
segs = set()
|
||||
if Path(Paths.log_root()).exists():
|
||||
segs = set(Path(Paths.log_root()).glob(f"{route}--*"))
|
||||
cls.segments = sorted(segs, key=lambda s: int(str(s).rsplit('--')[-1]))
|
||||
time.sleep(0.01)
|
||||
segs = list(Path(Paths.log_root()).glob(f"{route}--*"))
|
||||
assert len(segs) == 1
|
||||
|
||||
time.sleep(TEST_DURATION)
|
||||
|
||||
finally:
|
||||
cls.gpu_procs = {psutil.Process(int(f.name)).name() for f in pathlib.Path('/sys/devices/virtual/kgsl/kgsl/proc/').iterdir() if f.is_dir()}
|
||||
|
||||
if proc is not None:
|
||||
proc.terminate()
|
||||
if proc.wait(60) is None:
|
||||
proc.kill()
|
||||
|
||||
cls.lrs = [list(LogReader(os.path.join(str(s), "rlog.zst"))) for s in cls.segments]
|
||||
|
||||
cls.lr = list(LogReader(os.path.join(str(cls.segments[0]), "rlog.zst")))
|
||||
cls.log_path = cls.segments[0]
|
||||
cls.lr = list(LogReader(os.path.join(str(segs[0]), "rlog.zst")))
|
||||
st = time.monotonic()
|
||||
cls.ts = msgs_to_time_series(cls.lr)
|
||||
print("msgs to time series", time.monotonic() - st)
|
||||
log_path = segs[0]
|
||||
|
||||
cls.log_sizes = {}
|
||||
for f in cls.log_path.iterdir():
|
||||
for f in log_path.iterdir():
|
||||
assert f.is_file()
|
||||
cls.log_sizes[f] = f.stat().st_size / 1e6
|
||||
|
||||
@@ -198,7 +186,7 @@ class TestOnroad:
|
||||
assert len(msgs) >= math.floor(SERVICE_LIST[s].frequency*int(TEST_DURATION*0.8))
|
||||
|
||||
def test_manager_starting_time(self):
|
||||
st = self.msgs['managerState'][0].logMonoTime / 1e9
|
||||
st = self.ts['managerState']['t'][0]
|
||||
assert (st - self.manager_st) < 10, f"manager.py took {st - self.manager_st}s to publish the first 'managerState' msg"
|
||||
|
||||
def test_cloudlog_size(self):
|
||||
@@ -226,7 +214,7 @@ class TestOnroad:
|
||||
result += "-------------- UI Draw Timing ------------------\n"
|
||||
result += "------------------------------------------------\n"
|
||||
|
||||
ts = [m.uiDebug.drawTimeMillis for m in self.msgs['uiDebug']]
|
||||
ts = self.ts['uiDebug']['drawTimeMillis']
|
||||
result += f"min {min(ts):.2f}ms\n"
|
||||
result += f"max {max(ts):.2f}ms\n"
|
||||
result += f"std {np.std(ts):.2f}ms\n"
|
||||
@@ -309,9 +297,6 @@ class TestOnroad:
|
||||
assert np.max(np.diff(mems)) <= 4, "Max memory increase too high"
|
||||
assert np.average(np.diff(mems)) <= 1, "Average memory increase too high"
|
||||
|
||||
def test_gpu_usage(self):
|
||||
assert self.gpu_procs == {"weston", "ui", "camerad", "selfdrive.modeld.modeld", "selfdrive.modeld.dmonitoringmodeld"}
|
||||
|
||||
def test_camera_frame_timings(self, subtests):
|
||||
# test timing within a single camera
|
||||
result = "\n"
|
||||
@@ -319,7 +304,7 @@ class TestOnroad:
|
||||
result += "----------------- SOF Timing ------------------\n"
|
||||
result += "------------------------------------------------\n"
|
||||
for name in ['roadCameraState', 'wideRoadCameraState', 'driverCameraState']:
|
||||
ts = [getattr(m, m.which()).timestampSof for m in self.lr if name in m.which()]
|
||||
ts = self.ts[name]['timestampSof']
|
||||
d_ms = np.diff(ts) / 1e6
|
||||
d50 = np.abs(d_ms-50)
|
||||
result += f"{name} sof delta vs 50ms: min {min(d50):.2f}ms\n"
|
||||
@@ -338,35 +323,51 @@ class TestOnroad:
|
||||
# sanity checks within a single cam
|
||||
for cam in cams:
|
||||
with subtests.test(test="frame_skips", camera=cam):
|
||||
cam_log = [getattr(x, x.which()) for x in self.msgs[cam]]
|
||||
assert set(np.diff([x.frameId for x in cam_log])) == {1, }, "Frame ID skips"
|
||||
assert set(np.diff(self.ts[cam]['frameId'])) == {1, }, "Frame ID skips"
|
||||
|
||||
# EOF > SOF
|
||||
eof_sof_diff = np.array([x.timestampEof - x.timestampSof for x in cam_log])
|
||||
eof_sof_diff = self.ts[cam]['timestampEof'] - self.ts[cam]['timestampSof']
|
||||
assert np.all(eof_sof_diff > 0)
|
||||
assert np.all(eof_sof_diff < 50*1e6)
|
||||
|
||||
fid = {c: [getattr(m, m.which()).frameId for m in self.msgs[c]] for c in cams}
|
||||
first_fid = [min(x) for x in fid.values()]
|
||||
first_fid = {c: min(self.ts[c]['frameId']) for c in cams}
|
||||
if cam.endswith('CameraState'):
|
||||
# camerad guarantees that all cams start on frame ID 0
|
||||
# (note loggerd also needs to start up fast enough to catch it)
|
||||
assert set(first_fid) == {0, }, "Cameras don't start on frame ID 0"
|
||||
assert set(first_fid.values()) == {0, }, "Cameras don't start on frame ID 0"
|
||||
else:
|
||||
# encoder guarantees all cams start on the same frame ID
|
||||
assert len(set(first_fid)) == 1, "Cameras don't start on same frame ID"
|
||||
assert len(set(first_fid.values())) == 1, "Cameras don't start on same frame ID"
|
||||
|
||||
# we don't do a full segment rotation, so these might not match exactly
|
||||
last_fid = [max(x) for x in fid.values()]
|
||||
assert max(last_fid) - min(last_fid) < 10
|
||||
last_fid = {c: max(self.ts[c]['frameId']) for c in cams}
|
||||
assert max(last_fid.values()) - min(last_fid.values()) < 10
|
||||
|
||||
start, end = min(first_fid), min(last_fid)
|
||||
all_ts = [[getattr(m, m.which()).timestampSof for m in self.msgs[c]] for c in cams]
|
||||
start, end = min(first_fid.values()), min(last_fid.values())
|
||||
for i in range(end-start):
|
||||
ts = [round(x[i]/1e6, 1) for x in all_ts]
|
||||
diff = max(ts) - min(ts)
|
||||
ts = {c: round(self.ts[c]['timestampSof'][i]/1e6, 1) for c in cams}
|
||||
diff = (max(ts.values()) - min(ts.values()))
|
||||
assert diff < 2, f"Cameras not synced properly: frame_id={start+i}, {diff=:.1f}ms, {ts=}"
|
||||
|
||||
def test_camera_encoder_matches(self, subtests):
|
||||
# sanity check that the frame metadata is consistent with the encoded frames
|
||||
pairs = [('roadCameraState', 'roadEncodeIdx'),
|
||||
('wideRoadCameraState', 'wideRoadEncodeIdx'),
|
||||
('driverCameraState', 'driverEncodeIdx')]
|
||||
for cam, enc in pairs:
|
||||
with subtests.test(camera=cam, encoder=enc):
|
||||
cam_frames = {fid: (sof, eof) for fid, sof, eof in zip(
|
||||
self.ts[cam]['frameId'],
|
||||
self.ts[cam]['timestampSof'],
|
||||
self.ts[cam]['timestampEof'],
|
||||
strict=True,
|
||||
)}
|
||||
for i, fid in enumerate(self.ts[enc]['frameId']):
|
||||
cam_sof, cam_eof = cam_frames[fid]
|
||||
enc_sof, enc_eof = self.ts[enc]['timestampSof'][i], self.ts[enc]['timestampEof'][i]
|
||||
assert enc_sof == cam_sof, f"SOF mismatch: frameId={fid}, enc_sof={enc_sof}, cam_sof={cam_sof}"
|
||||
assert enc_eof == cam_eof, f"EOF mismatch: frameId={fid}, enc_eof={enc_eof}, cam_eof={cam_eof}"
|
||||
|
||||
def test_mpc_execution_timings(self):
|
||||
result = "\n"
|
||||
result += "------------------------------------------------\n"
|
||||
@@ -438,12 +439,7 @@ class TestOnroad:
|
||||
|
||||
@release_only
|
||||
def test_startup(self):
|
||||
startup_alert = None
|
||||
for msg in self.lrs[0]:
|
||||
# can't use onroadEvents because the first msg can be dropped while loggerd is starting up
|
||||
if msg.which() == "selfdriveState":
|
||||
startup_alert = msg.selfdriveState.alertText1
|
||||
break
|
||||
startup_alert = self.ts['selfdriveState']['alertText1'][0]
|
||||
expected = EVENTS[log.OnroadEvent.EventName.startup][ET.PERMANENT].alert_text_1
|
||||
assert startup_alert == expected, "wrong startup alert"
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ qt_libs = [widgets, qt_util] + base_libs
|
||||
qt_src = ["main.cc", "ui.cc", "qt/sidebar.cc", "qt/body.cc",
|
||||
"qt/window.cc", "qt/home.cc", "qt/offroad/settings.cc",
|
||||
"qt/offroad/software_settings.cc", "qt/offroad/developer_panel.cc", "qt/offroad/onboarding.cc",
|
||||
"qt/offroad/driverview.cc", "qt/offroad/experimental_mode.cc",
|
||||
"qt/offroad/driverview.cc", "qt/offroad/experimental_mode.cc", "qt/offroad/firehose.cc",
|
||||
"qt/onroad/onroad_home.cc", "qt/onroad/annotated_camera.cc", "qt/onroad/model.cc",
|
||||
"qt/onroad/buttons.cc", "qt/onroad/alerts.cc", "qt/onroad/driver_monitoring.cc", "qt/onroad/hud.cc"]
|
||||
|
||||
@@ -40,12 +40,9 @@ translation_sources = [f"#selfdrive/ui/translations/{l}.ts" for l in languages.v
|
||||
translation_targets = [src.replace(".ts", ".qm") for src in translation_sources]
|
||||
lrelease_bin = 'third_party/qt5/larch64/bin/lrelease' if arch == 'larch64' else 'lrelease'
|
||||
|
||||
lupdate = qt_env.Command(translation_sources + ["translations/alerts_generated.h"], qt_src + widgets_src, "selfdrive/ui/update_translations.py")
|
||||
lrelease = qt_env.Command(translation_targets, translation_sources, f"{lrelease_bin} $SOURCES")
|
||||
qt_env.Depends(lrelease, lupdate)
|
||||
qt_env.NoClean(translation_sources)
|
||||
qt_env.Precious(translation_sources)
|
||||
qt_env.NoCache(lupdate)
|
||||
|
||||
# create qrc file for compiled translations to include with assets
|
||||
translations_assets_src = "#selfdrive/assets/translations_assets.qrc"
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
#include "selfdrive/ui/qt/offroad/firehose.h"
|
||||
#include "selfdrive/ui/ui.h"
|
||||
#include "selfdrive/ui/qt/offroad/settings.h"
|
||||
|
||||
#include <QLabel>
|
||||
#include <QPushButton>
|
||||
#include <QVBoxLayout>
|
||||
#include <QHBoxLayout>
|
||||
#include <QFrame>
|
||||
#include <QScrollArea>
|
||||
#include <QStackedLayout>
|
||||
#include <QProgressBar>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QTimer>
|
||||
|
||||
FirehosePanel::FirehosePanel(SettingsWindow *parent) : QWidget((QWidget*)parent) {
|
||||
layout = new QVBoxLayout(this);
|
||||
layout->setContentsMargins(40, 40, 40, 40);
|
||||
layout->setSpacing(20);
|
||||
|
||||
// header
|
||||
QLabel *title = new QLabel(tr("🔥 Firehose Mode 🔥"));
|
||||
title->setStyleSheet("font-size: 100px; font-weight: 500; font-family: 'Noto Color Emoji';");
|
||||
layout->addWidget(title, 0, Qt::AlignCenter);
|
||||
|
||||
// Create a container for the content
|
||||
QFrame *content = new QFrame();
|
||||
content->setStyleSheet("background-color: #292929; border-radius: 15px; padding: 20px;");
|
||||
QVBoxLayout *content_layout = new QVBoxLayout(content);
|
||||
content_layout->setSpacing(20);
|
||||
|
||||
// Top description
|
||||
QLabel *description = new QLabel(tr("openpilot learns to drive by watching humans, like you, drive.\n\nFirehose Mode allows you to maximize your training data uploads to improve openpilot's driving models. More data means bigger models, which means better Experimental Mode."));
|
||||
description->setStyleSheet("font-size: 45px; padding-bottom: 20px;");
|
||||
description->setWordWrap(true);
|
||||
content_layout->addWidget(description);
|
||||
|
||||
// Add a separator
|
||||
QFrame *line = new QFrame();
|
||||
line->setFrameShape(QFrame::HLine);
|
||||
line->setFrameShadow(QFrame::Sunken);
|
||||
line->setStyleSheet("background-color: #444444; margin-top: 5px; margin-bottom: 5px;");
|
||||
content_layout->addWidget(line);
|
||||
|
||||
toggle_label = new QLabel(tr("Firehose Mode: ACTIVE"));
|
||||
toggle_label->setStyleSheet("font-size: 60px; font-weight: bold; color: white;");
|
||||
content_layout->addWidget(toggle_label);
|
||||
|
||||
// Add contribution label
|
||||
contribution_label = new QLabel();
|
||||
contribution_label->setStyleSheet("font-size: 52px; margin-top: 10px; margin-bottom: 10px;");
|
||||
contribution_label->setWordWrap(true);
|
||||
contribution_label->hide();
|
||||
content_layout->addWidget(contribution_label);
|
||||
|
||||
// Add a separator before detailed instructions
|
||||
QFrame *line2 = new QFrame();
|
||||
line2->setFrameShape(QFrame::HLine);
|
||||
line2->setFrameShadow(QFrame::Sunken);
|
||||
line2->setStyleSheet("background-color: #444444; margin-top: 10px; margin-bottom: 10px;");
|
||||
content_layout->addWidget(line2);
|
||||
|
||||
// Detailed instructions at the bottom
|
||||
detailed_instructions = new QLabel(tr(
|
||||
"For maximum effectiveness, bring your device inside and connect to a good USB-C adapter and Wi-Fi weekly.<br>"
|
||||
"<br>"
|
||||
"Firehose Mode can also work while you're driving if connected to a hotspot or unlimited SIM card.<br>"
|
||||
"<br><br>"
|
||||
"<b>Frequently Asked Questions</b><br><br>"
|
||||
"<i>Does it matter how or where I drive?</i> Nope, just drive as you normally would.<br><br>"
|
||||
"<i>What's a good USB-C adapter?</i> Any fast phone or laptop charger should be fine.<br><br>"
|
||||
"<i>Does it matter which software I run?</i> Yes, only upstream openpilot (and particular forks) are able to be used for training."
|
||||
));
|
||||
detailed_instructions->setStyleSheet("font-size: 40px; color: #E4E4E4;");
|
||||
detailed_instructions->setWordWrap(true);
|
||||
content_layout->addWidget(detailed_instructions);
|
||||
|
||||
layout->addWidget(content, 1);
|
||||
|
||||
// Set up the API request for firehose stats
|
||||
const QString dongle_id = QString::fromStdString(Params().get("DongleId"));
|
||||
firehose_stats = new RequestRepeater(this, CommaApi::BASE_URL + "/v1/devices/" + dongle_id + "/firehose_stats",
|
||||
"ApiCache_FirehoseStats", 30, true);
|
||||
QObject::connect(firehose_stats, &RequestRepeater::requestDone, [=](const QString &response, bool success) {
|
||||
if (success) {
|
||||
QJsonDocument doc = QJsonDocument::fromJson(response.toUtf8());
|
||||
QJsonObject json = doc.object();
|
||||
int count = json["firehose"].toInt();
|
||||
contribution_label->setText(tr("<b>%1 %2</b> of your driving are in the training dataset so far.").arg(count).arg(count == 1 ? "segment" : "segments"));
|
||||
contribution_label->show();
|
||||
}
|
||||
});
|
||||
|
||||
QObject::connect(uiState(), &UIState::uiUpdate, this, &FirehosePanel::refresh);
|
||||
}
|
||||
|
||||
void FirehosePanel::refresh() {
|
||||
auto deviceState = (*uiState()->sm)["deviceState"].getDeviceState();
|
||||
auto networkType = deviceState.getNetworkType();
|
||||
bool networkMetered = deviceState.getNetworkMetered();
|
||||
|
||||
bool is_active = !networkMetered && (networkType != cereal::DeviceState::NetworkType::NONE);
|
||||
if (is_active) {
|
||||
toggle_label->setText(tr("ACTIVE"));
|
||||
toggle_label->setStyleSheet("font-size: 60px; font-weight: bold; color: #2ecc71;");
|
||||
} else {
|
||||
toggle_label->setText(tr("<span stylesheet='font-size: 60px; font-weight: bold; color: #e74c3c;'>INACTIVE</span>: connect to unmetered network"));
|
||||
toggle_label->setStyleSheet("font-size: 60px;");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include <QWidget>
|
||||
#include <QVBoxLayout>
|
||||
#include <QLabel>
|
||||
#include "selfdrive/ui/qt/request_repeater.h"
|
||||
|
||||
// Forward declarations
|
||||
class SettingsWindow;
|
||||
|
||||
class FirehosePanel : public QWidget {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit FirehosePanel(SettingsWindow *parent);
|
||||
|
||||
private:
|
||||
QVBoxLayout *layout;
|
||||
|
||||
QLabel *detailed_instructions;
|
||||
QLabel *contribution_label;
|
||||
QLabel *toggle_label;
|
||||
|
||||
RequestRepeater *firehose_stats;
|
||||
|
||||
private slots:
|
||||
void refresh();
|
||||
};
|
||||
@@ -4,7 +4,6 @@
|
||||
|
||||
#include <QLabel>
|
||||
#include <QPainter>
|
||||
#include <QScrollBar>
|
||||
#include <QTransform>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
@@ -12,7 +11,6 @@
|
||||
#include "common/params.h"
|
||||
#include "selfdrive/ui/qt/util.h"
|
||||
#include "selfdrive/ui/qt/widgets/input.h"
|
||||
#include "selfdrive/ui/qt/widgets/scrollview.h"
|
||||
|
||||
TrainingGuide::TrainingGuide(QWidget *parent) : QFrame(parent) {
|
||||
setAttribute(Qt::WA_OpaquePaintEvent);
|
||||
@@ -85,29 +83,25 @@ void TrainingGuide::paintEvent(QPaintEvent *event) {
|
||||
}
|
||||
|
||||
void TermsPage::showEvent(QShowEvent *event) {
|
||||
// late init, building QML widget takes 200ms
|
||||
if (layout()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QVBoxLayout *main_layout = new QVBoxLayout(this);
|
||||
main_layout->setContentsMargins(45, 35, 45, 45);
|
||||
main_layout->setSpacing(0);
|
||||
|
||||
QLabel *title = new QLabel(tr("Terms & Conditions"));
|
||||
title->setStyleSheet("font-size: 90px; font-weight: 600;");
|
||||
main_layout->addWidget(title);
|
||||
QVBoxLayout *vlayout = new QVBoxLayout();
|
||||
vlayout->setContentsMargins(165, 165, 165, 0);
|
||||
main_layout->addLayout(vlayout);
|
||||
|
||||
QLabel *text = new QLabel(this);
|
||||
text->setTextFormat(Qt::RichText);
|
||||
text->setWordWrap(true);
|
||||
text->setText(QString::fromStdString(util::read_file("../assets/offroad/tc.html")));
|
||||
text->setStyleSheet("font-size:50px; font-weight: 200; color: #C9C9C9; background-color:#1B1B1B; padding:50px 50px;");
|
||||
ScrollView *scroll = new ScrollView(text, this);
|
||||
QLabel *title = new QLabel(tr("Welcome to openpilot"));
|
||||
title->setStyleSheet("font-size: 90px; font-weight: 500;");
|
||||
vlayout->addWidget(title, 0, Qt::AlignTop | Qt::AlignLeft);
|
||||
|
||||
main_layout->addSpacing(30);
|
||||
main_layout->addWidget(scroll);
|
||||
main_layout->addSpacing(50);
|
||||
vlayout->addSpacing(90);
|
||||
QLabel *desc = new QLabel(tr("You must accept the Terms and Conditions to use openpilot. Read the latest terms at <span style='color: #465BEA;'>https://comma.ai/terms</span> before continuing."));
|
||||
desc->setWordWrap(true);
|
||||
desc->setStyleSheet("font-size: 80px; font-weight: 300;");
|
||||
vlayout->addWidget(desc, 0);
|
||||
|
||||
vlayout->addStretch();
|
||||
|
||||
QHBoxLayout* buttons = new QHBoxLayout;
|
||||
buttons->setMargin(0);
|
||||
@@ -118,8 +112,7 @@ void TermsPage::showEvent(QShowEvent *event) {
|
||||
buttons->addWidget(decline_btn);
|
||||
QObject::connect(decline_btn, &QPushButton::clicked, this, &TermsPage::declinedTerms);
|
||||
|
||||
accept_btn = new QPushButton(tr("Scroll to accept"));
|
||||
accept_btn->setEnabled(false);
|
||||
accept_btn = new QPushButton(tr("Agree"));
|
||||
accept_btn->setStyleSheet(R"(
|
||||
QPushButton {
|
||||
background-color: #465BEA;
|
||||
@@ -127,23 +120,9 @@ void TermsPage::showEvent(QShowEvent *event) {
|
||||
QPushButton:pressed {
|
||||
background-color: #3049F4;
|
||||
}
|
||||
QPushButton:disabled {
|
||||
background-color: #4F4F4F;
|
||||
}
|
||||
)");
|
||||
buttons->addWidget(accept_btn);
|
||||
QObject::connect(accept_btn, &QPushButton::clicked, this, &TermsPage::acceptedTerms);
|
||||
QScrollBar *scroll_bar = scroll->verticalScrollBar();
|
||||
connect(scroll_bar, &QScrollBar::valueChanged, this, [this, scroll_bar](int value) {
|
||||
if (value == scroll_bar->maximum()) {
|
||||
enableAccept();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void TermsPage::enableAccept() {
|
||||
accept_btn->setText(tr("Agree"));
|
||||
accept_btn->setEnabled(true);
|
||||
}
|
||||
|
||||
void DeclinePage::showEvent(QShowEvent *event) {
|
||||
|
||||
@@ -65,9 +65,6 @@ class TermsPage : public QFrame {
|
||||
public:
|
||||
explicit TermsPage(QWidget *parent = 0) : QFrame(parent) {}
|
||||
|
||||
public slots:
|
||||
void enableAccept();
|
||||
|
||||
private:
|
||||
void showEvent(QShowEvent *event) override;
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "selfdrive/ui/qt/widgets/prime.h"
|
||||
#include "selfdrive/ui/qt/widgets/scrollview.h"
|
||||
#include "selfdrive/ui/qt/offroad/developer_panel.h"
|
||||
#include "selfdrive/ui/qt/offroad/firehose.h"
|
||||
|
||||
TogglesPanel::TogglesPanel(SettingsWindow *parent) : ListWidget(parent) {
|
||||
// param, title, desc, icon
|
||||
@@ -36,20 +37,6 @@ TogglesPanel::TogglesPanel(SettingsWindow *parent) : ListWidget(parent) {
|
||||
tr("When enabled, pressing the accelerator pedal will disengage openpilot."),
|
||||
"../assets/offroad/icon_disengage_on_accelerator.svg",
|
||||
},
|
||||
{
|
||||
"FirehoseMode",
|
||||
tr("FIREHOSE Mode"),
|
||||
tr("Enable <b>FIREHOSE Mode</b> to get your driving data in the training set.<br><br>"
|
||||
"Follow these steps to get your device ready:<br>"
|
||||
" 1. Bring your device inside and connect to a good USB-C adapter<br>"
|
||||
" 2. Connect to Wi-Fi<br>"
|
||||
" 3. Enable this toggle<br>"
|
||||
" 4. Leave it connected for at least 30 minutes<br>"
|
||||
"<br>"
|
||||
"This toggle turns off once you restart your device. Repeat once a week for maximum effectiveness."
|
||||
""),
|
||||
"../assets/offroad/icon_warning.png",
|
||||
},
|
||||
{
|
||||
"IsLdwEnabled",
|
||||
tr("Enable Lane Departure Warnings"),
|
||||
@@ -334,11 +321,26 @@ void SettingsWindow::showEvent(QShowEvent *event) {
|
||||
}
|
||||
|
||||
void SettingsWindow::setCurrentPanel(int index, const QString ¶m) {
|
||||
if (!param.isEmpty()) {
|
||||
// Check if param ends with "Panel" to determine if it's a panel name
|
||||
if (param.endsWith("Panel")) {
|
||||
QString panelName = param;
|
||||
panelName.chop(5); // Remove "Panel" suffix
|
||||
|
||||
// Find the panel by name
|
||||
for (int i = 0; i < nav_btns->buttons().size(); i++) {
|
||||
if (nav_btns->buttons()[i]->text() == tr(panelName.toStdString().c_str())) {
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
emit expandToggleDescription(param);
|
||||
}
|
||||
}
|
||||
|
||||
panel_widget->setCurrentIndex(index);
|
||||
nav_btns->buttons()[index]->setChecked(true);
|
||||
if (!param.isEmpty()) {
|
||||
emit expandToggleDescription(param);
|
||||
}
|
||||
}
|
||||
|
||||
SettingsWindow::SettingsWindow(QWidget *parent) : QFrame(parent) {
|
||||
@@ -383,6 +385,7 @@ SettingsWindow::SettingsWindow(QWidget *parent) : QFrame(parent) {
|
||||
{tr("Network"), networking},
|
||||
{tr("Toggles"), toggles},
|
||||
{tr("Software"), new SoftwarePanel(this)},
|
||||
{tr("Firehose"), new FirehosePanel(this)},
|
||||
{tr("Developer"), new DeveloperPanel(this)},
|
||||
};
|
||||
|
||||
|
||||
@@ -98,3 +98,6 @@ private:
|
||||
Params params;
|
||||
ParamWatcher *fs_watch;
|
||||
};
|
||||
|
||||
// Forward declaration
|
||||
class FirehosePanel;
|
||||
|
||||
@@ -54,7 +54,7 @@ SoftwarePanel::SoftwarePanel(QWidget* parent) : ListWidget(parent) {
|
||||
connect(targetBranchBtn, &ButtonControl::clicked, [=]() {
|
||||
auto current = params.get("GitBranch");
|
||||
QStringList branches = QString::fromStdString(params.get("UpdaterAvailableBranches")).split(",");
|
||||
for (QString b : {current.c_str(), "devel-staging", "devel", "nightly", "nightly-dev", "master-ci", "master"}) {
|
||||
for (QString b : {current.c_str(), "devel-staging", "devel", "nightly", "nightly-dev", "master"}) {
|
||||
auto i = branches.indexOf(b);
|
||||
if (i >= 0) {
|
||||
branches.removeAt(i);
|
||||
|
||||
@@ -26,6 +26,7 @@ void HudRenderer::updateState(const UIState &s) {
|
||||
// Handle older routes where vCruiseCluster is not set
|
||||
set_speed = car_state.getVCruiseCluster() == 0.0 ? controls_state.getVCruiseDEPRECATED() : car_state.getVCruiseCluster();
|
||||
is_cruise_set = set_speed > 0 && set_speed != SET_SPEED_NA;
|
||||
is_cruise_available = set_speed != -1;
|
||||
|
||||
if (is_cruise_set && !is_metric) {
|
||||
set_speed *= KM_TO_MILE;
|
||||
@@ -47,7 +48,9 @@ void HudRenderer::draw(QPainter &p, const QRect &surface_rect) {
|
||||
p.fillRect(0, 0, surface_rect.width(), UI_HEADER_HEIGHT, bg);
|
||||
|
||||
|
||||
drawSetSpeed(p, surface_rect);
|
||||
if (is_cruise_available) {
|
||||
drawSetSpeed(p, surface_rect);
|
||||
}
|
||||
drawCurrentSpeed(p, surface_rect);
|
||||
|
||||
p.restore();
|
||||
|
||||
@@ -19,6 +19,7 @@ private:
|
||||
float speed = 0;
|
||||
float set_speed = 0;
|
||||
bool is_cruise_set = false;
|
||||
bool is_cruise_available = true;
|
||||
bool is_metric = false;
|
||||
bool v_ego_cluster_seen = false;
|
||||
int status = STATUS_DISENGAGED;
|
||||
|
||||
@@ -6,80 +6,35 @@
|
||||
#include <QPushButton>
|
||||
|
||||
WiFiPromptWidget::WiFiPromptWidget(QWidget *parent) : QFrame(parent) {
|
||||
stack = new QStackedLayout(this);
|
||||
// Setup Firehose Mode
|
||||
QVBoxLayout *main_layout = new QVBoxLayout(this);
|
||||
main_layout->setContentsMargins(56, 40, 56, 40);
|
||||
main_layout->setSpacing(42);
|
||||
|
||||
QLabel *title = new QLabel(tr("<span style='font-family: \"Noto Color Emoji\";'>🔥</span> Firehose Mode <span style='font-family: Noto Color Emoji;'>🔥</span>"));
|
||||
title->setStyleSheet("font-size: 64px; font-weight: 500;");
|
||||
main_layout->addWidget(title);
|
||||
|
||||
// Setup Wi-Fi
|
||||
QFrame *setup = new QFrame;
|
||||
QVBoxLayout *setup_layout = new QVBoxLayout(setup);
|
||||
setup_layout->setContentsMargins(56, 40, 56, 40);
|
||||
setup_layout->setSpacing(20);
|
||||
{
|
||||
QHBoxLayout *title_layout = new QHBoxLayout;
|
||||
title_layout->setSpacing(32);
|
||||
{
|
||||
QLabel *icon = new QLabel;
|
||||
QPixmap pixmap("../assets/offroad/icon_wifi_strength_full.svg");
|
||||
icon->setPixmap(pixmap.scaledToWidth(80, Qt::SmoothTransformation));
|
||||
title_layout->addWidget(icon);
|
||||
QLabel *desc = new QLabel(tr("Maximize your training data uploads to improve openpilot's driving models."));
|
||||
desc->setStyleSheet("font-size: 40px; font-weight: 400;");
|
||||
desc->setWordWrap(true);
|
||||
main_layout->addWidget(desc);
|
||||
|
||||
QLabel *title = new QLabel(tr("Setup Wi-Fi"));
|
||||
title->setStyleSheet("font-size: 64px; font-weight: 600;");
|
||||
title_layout->addWidget(title);
|
||||
title_layout->addStretch();
|
||||
QPushButton *settings_btn = new QPushButton(tr("Open"));
|
||||
connect(settings_btn, &QPushButton::clicked, [=]() { emit openSettings(1, "FirehosePanel"); });
|
||||
settings_btn->setStyleSheet(R"(
|
||||
QPushButton {
|
||||
font-size: 48px;
|
||||
font-weight: 500;
|
||||
border-radius: 10px;
|
||||
background-color: #465BEA;
|
||||
padding: 32px;
|
||||
}
|
||||
setup_layout->addLayout(title_layout);
|
||||
|
||||
QLabel *desc = new QLabel(tr("Connect to Wi-Fi to upload driving data and help improve openpilot"));
|
||||
desc->setStyleSheet("font-size: 40px; font-weight: 400;");
|
||||
desc->setWordWrap(true);
|
||||
setup_layout->addWidget(desc);
|
||||
|
||||
QPushButton *settings_btn = new QPushButton(tr("Open Settings"));
|
||||
connect(settings_btn, &QPushButton::clicked, [=]() { emit openSettings(1); });
|
||||
settings_btn->setStyleSheet(R"(
|
||||
QPushButton {
|
||||
font-size: 48px;
|
||||
font-weight: 500;
|
||||
border-radius: 10px;
|
||||
background-color: #465BEA;
|
||||
padding: 32px;
|
||||
}
|
||||
QPushButton:pressed {
|
||||
background-color: #3049F4;
|
||||
}
|
||||
)");
|
||||
setup_layout->addWidget(settings_btn);
|
||||
}
|
||||
stack->addWidget(setup);
|
||||
|
||||
// Uploading data
|
||||
QWidget *uploading = new QWidget;
|
||||
QVBoxLayout *uploading_layout = new QVBoxLayout(uploading);
|
||||
uploading_layout->setContentsMargins(64, 56, 64, 56);
|
||||
uploading_layout->setSpacing(36);
|
||||
{
|
||||
QHBoxLayout *title_layout = new QHBoxLayout;
|
||||
{
|
||||
QLabel *title = new QLabel(tr("Ready to upload"));
|
||||
title->setStyleSheet("font-size: 64px; font-weight: 600;");
|
||||
title->setWordWrap(true);
|
||||
title->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
|
||||
title_layout->addWidget(title);
|
||||
title_layout->addStretch();
|
||||
|
||||
QLabel *icon = new QLabel;
|
||||
QPixmap pixmap("../assets/offroad/icon_wifi_uploading.svg");
|
||||
icon->setPixmap(pixmap.scaledToWidth(120, Qt::SmoothTransformation));
|
||||
title_layout->addWidget(icon);
|
||||
QPushButton:pressed {
|
||||
background-color: #3049F4;
|
||||
}
|
||||
uploading_layout->addLayout(title_layout);
|
||||
|
||||
QLabel *desc = new QLabel(tr("Training data will be pulled periodically while your device is on Wi-Fi"));
|
||||
desc->setStyleSheet("font-size: 48px; font-weight: 400;");
|
||||
desc->setWordWrap(true);
|
||||
uploading_layout->addWidget(desc);
|
||||
}
|
||||
stack->addWidget(uploading);
|
||||
)");
|
||||
main_layout->addWidget(settings_btn);
|
||||
|
||||
setStyleSheet(R"(
|
||||
WiFiPromptWidget {
|
||||
@@ -87,17 +42,4 @@ WiFiPromptWidget::WiFiPromptWidget(QWidget *parent) : QFrame(parent) {
|
||||
border-radius: 10px;
|
||||
}
|
||||
)");
|
||||
|
||||
QObject::connect(uiState(), &UIState::uiUpdate, this, &WiFiPromptWidget::updateState);
|
||||
}
|
||||
|
||||
void WiFiPromptWidget::updateState(const UIState &s) {
|
||||
if (!isVisible()) return;
|
||||
|
||||
auto &sm = *(s.sm);
|
||||
|
||||
auto network_type = sm["deviceState"].getDeviceState().getNetworkType();
|
||||
auto uploading = network_type == cereal::DeviceState::NetworkType::WIFI ||
|
||||
network_type == cereal::DeviceState::NetworkType::ETHERNET;
|
||||
stack->setCurrentIndex(uploading ? 1 : 0);
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <QFrame>
|
||||
#include <QStackedLayout>
|
||||
#include <QWidget>
|
||||
|
||||
#include "selfdrive/ui/ui.h"
|
||||
|
||||
class WiFiPromptWidget : public QFrame {
|
||||
Q_OBJECT
|
||||
|
||||
@@ -14,10 +11,4 @@ public:
|
||||
|
||||
signals:
|
||||
void openSettings(int index = 0, const QString ¶m = "");
|
||||
|
||||
public slots:
|
||||
void updateState(const UIState &s);
|
||||
|
||||
protected:
|
||||
QStackedLayout *stack;
|
||||
};
|
||||
|
||||
@@ -69,14 +69,13 @@ class Soundd:
|
||||
for sound in sound_list:
|
||||
filename, play_count, volume = sound_list[sound]
|
||||
|
||||
wavefile = wave.open(BASEDIR + "/selfdrive/assets/sounds/" + filename, 'r')
|
||||
with wave.open(BASEDIR + "/selfdrive/assets/sounds/" + filename, 'r') as wavefile:
|
||||
assert wavefile.getnchannels() == 1
|
||||
assert wavefile.getsampwidth() == 2
|
||||
assert wavefile.getframerate() == SAMPLE_RATE
|
||||
|
||||
assert wavefile.getnchannels() == 1
|
||||
assert wavefile.getsampwidth() == 2
|
||||
assert wavefile.getframerate() == SAMPLE_RATE
|
||||
|
||||
length = wavefile.getnframes()
|
||||
self.loaded_sounds[sound] = np.frombuffer(wavefile.readframes(length), dtype=np.int16).astype(np.float32) / (2**16/2)
|
||||
length = wavefile.getnframes()
|
||||
self.loaded_sounds[sound] = np.frombuffer(wavefile.readframes(length), dtype=np.int16).astype(np.float32) / (2**16/2)
|
||||
|
||||
def get_sound_data(self, frames): # get "frames" worth of data from the current alert sound, looping when required
|
||||
|
||||
|
||||
@@ -2,14 +2,12 @@ import pytest
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import tempfile
|
||||
import xml.etree.ElementTree as ET
|
||||
import string
|
||||
import requests
|
||||
from parameterized import parameterized_class
|
||||
|
||||
from openpilot.selfdrive.ui.update_translations import TRANSLATIONS_DIR, LANGUAGES_FILE, update_translations
|
||||
from openpilot.selfdrive.ui.update_translations import TRANSLATIONS_DIR, LANGUAGES_FILE
|
||||
|
||||
with open(LANGUAGES_FILE) as f:
|
||||
translation_files = json.load(f)
|
||||
@@ -34,16 +32,6 @@ class TestTranslations:
|
||||
assert os.path.exists(os.path.join(TRANSLATIONS_DIR, f"{self.file}.ts")), \
|
||||
f"{self.name} has no XML translation file, run selfdrive/ui/update_translations.py"
|
||||
|
||||
def test_translations_updated(self):
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
shutil.copytree(TRANSLATIONS_DIR, tmpdir, dirs_exist_ok=True)
|
||||
update_translations(translation_files=[self.file], translations_dir=tmpdir)
|
||||
|
||||
cur_translations = self._read_translation_file(TRANSLATIONS_DIR, self.file)
|
||||
new_translations = self._read_translation_file(tmpdir, self.file)
|
||||
assert cur_translations == new_translations, \
|
||||
f"{self.file} ({self.name}) XML translation file out of date. Run selfdrive/ui/update_translations.py to update the translation files"
|
||||
|
||||
@pytest.mark.skip("Only test unfinished translations before going to release")
|
||||
def test_unfinished_translations(self):
|
||||
cur_translations = self._read_translation_file(TRANSLATIONS_DIR, self.file)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from collections import namedtuple
|
||||
#!/usr/bin/env python3
|
||||
import capnp
|
||||
import pathlib
|
||||
import shutil
|
||||
@@ -8,6 +8,7 @@ import pywinctl
|
||||
import pyautogui
|
||||
import pickle
|
||||
import time
|
||||
from collections import namedtuple
|
||||
|
||||
from cereal import car, log
|
||||
from msgq.visionipc import VisionIpcServer, VisionStreamType
|
||||
@@ -42,21 +43,24 @@ def setup_settings_device(click, pm: PubMaster):
|
||||
|
||||
def setup_settings_toggles(click, pm: PubMaster):
|
||||
setup_settings_device(click, pm)
|
||||
click(278, 650)
|
||||
click(278, 600)
|
||||
time.sleep(UI_DELAY)
|
||||
|
||||
def setup_settings_software(click, pm: PubMaster):
|
||||
setup_settings_device(click, pm)
|
||||
click(278, 800)
|
||||
click(278, 720)
|
||||
time.sleep(UI_DELAY)
|
||||
|
||||
def setup_settings_firehose(click, pm: PubMaster):
|
||||
click(1780, 730)
|
||||
|
||||
def setup_settings_developer(click, pm: PubMaster):
|
||||
CP = car.CarParams()
|
||||
CP.experimentalLongitudinalAvailable = True
|
||||
Params().put("CarParamsPersistent", CP.to_bytes())
|
||||
|
||||
setup_settings_device(click, pm)
|
||||
click(278, 960)
|
||||
click(278, 970)
|
||||
time.sleep(UI_DELAY)
|
||||
|
||||
def setup_onroad(click, pm: PubMaster):
|
||||
@@ -191,6 +195,7 @@ CASES = {
|
||||
"settings_device": setup_settings_device,
|
||||
"settings_toggles": setup_settings_toggles,
|
||||
"settings_software": setup_settings_software,
|
||||
"settings_firehose": setup_settings_firehose,
|
||||
"settings_developer": setup_settings_developer,
|
||||
"onroad": setup_onroad,
|
||||
"onroad_disengaged": setup_onroad_disengaged,
|
||||
|
||||
@@ -305,6 +305,39 @@
|
||||
<translation>تشغيل وضع الراحة</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>FirehosePanel</name>
|
||||
<message>
|
||||
<source>🔥 Firehose Mode 🔥</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>openpilot learns to drive by watching humans, like you, drive.
|
||||
|
||||
Firehose Mode allows you to maximize your training data uploads to improve openpilot's driving models. More data means bigger models, which means better Experimental Mode.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Firehose Mode: ACTIVE</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>For maximum effectiveness, bring your device inside and connect to a good USB-C adapter and Wi-Fi weekly.<br><br>Firehose Mode can also work while you're driving if connected to a hotspot or unlimited SIM card.<br><br><br><b>Frequently Asked Questions</b><br><br><i>Does it matter how or where I drive?</i> Nope, just drive as you normally would.<br><br><i>What's a good USB-C adapter?</i> Any fast phone or laptop charger should be fine.<br><br><i>Does it matter which software I run?</i> Yes, only upstream openpilot (and particular forks) are able to be used for training.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><b>%1 %2</b> of your driving are in the training dataset so far.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>ACTIVE</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><span stylesheet='font-size: 60px; font-weight: bold; color: #e74c3c;'>INACTIVE</span>: connect to unmetered network</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>HudRenderer</name>
|
||||
<message>
|
||||
@@ -657,6 +690,10 @@ This may take up to a minute.</source>
|
||||
<source>Developer</source>
|
||||
<translation>المطور</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Firehose</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>Setup</name>
|
||||
@@ -966,22 +1003,22 @@ This may take up to a minute.</source>
|
||||
</context>
|
||||
<context>
|
||||
<name>TermsPage</name>
|
||||
<message>
|
||||
<source>Terms & Conditions</source>
|
||||
<translation>الشروط والأحكام</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Decline</source>
|
||||
<translation>رفض</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Scroll to accept</source>
|
||||
<translation>قم بالتمرير للقبول</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Agree</source>
|
||||
<translation>أوافق</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Welcome to openpilot</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>You must accept the Terms and Conditions to use openpilot. Read the latest terms at <span style='color: #465BEA;'>https://comma.ai/terms</span> before continuing.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>TogglesPanel</name>
|
||||
@@ -1093,14 +1130,6 @@ This may take up to a minute.</source>
|
||||
<source>Enable driver monitoring even when openpilot is not engaged.</source>
|
||||
<translation>تمكين مراقبة السائق حتى عندما لا يكون نظام OpenPilot مُفعّلاً.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>FIREHOSE Mode</source>
|
||||
<translation>الوضع FIREHOSE</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Enable <b>FIREHOSE Mode</b> to get your driving data in the training set.<br><br>Follow these steps to get your device ready:<br> 1. Bring your device inside and connect to a good USB-C adapter<br> 2. Connect to Wi-Fi<br> 3. Enable this toggle<br> 4. Leave it connected for at least 30 minutes<br><br>This toggle turns off once you restart your device. Repeat once a week for maximum effectiveness.</source>
|
||||
<translation>قم بتمكين <b>وضع FIREHOSE</b> للحصول على بيانات القيادة الخاصة بك في مجموعة التدريب.<br><br>اتبع الخطوات التالية لتجهيز جهازك:<br> 1. أحضر جهازك إلى الداخل وقم بتوصيله بمحول USB-C جيد<br> 2. اتصل بشبكة Wi-Fi<br> 3. قم بتمكين هذا التبديل<br> 4. اتركه متصلاً لمدة 30 دقيقة على الأقل<br><br>يتم إيقاف تشغيل هذا التبديل بمجرد إعادة تشغيل جهازك. كرر ذلك مرة واحدة في الأسبوع لتحقيق أقصى قدر من الفعالية.</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>Updater</name>
|
||||
@@ -1140,24 +1169,16 @@ This may take up to a minute.</source>
|
||||
<context>
|
||||
<name>WiFiPromptWidget</name>
|
||||
<message>
|
||||
<source>Setup Wi-Fi</source>
|
||||
<translation>إعداد شبكة الواي فاي</translation>
|
||||
<source>Open</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Connect to Wi-Fi to upload driving data and help improve openpilot</source>
|
||||
<translation>الاتصال بشبكة الواي فاي لتحميل بيانات القيادة والمساهمة في تحسين openpilot</translation>
|
||||
<source>Maximize your training data uploads to improve openpilot's driving models.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Open Settings</source>
|
||||
<translation>فتح الإعدادات</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Ready to upload</source>
|
||||
<translation>جاهز للتحميل</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Training data will be pulled periodically while your device is on Wi-Fi</source>
|
||||
<translation>سيتم سحب بيانات التدريب دورياً عندما يكون جهازك متصل بشبكة واي فاي</translation>
|
||||
<source><span style='font-family: "Noto Color Emoji";'>🔥</span> Firehose Mode <span style='font-family: Noto Color Emoji;'>🔥</span></source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
|
||||
@@ -305,6 +305,39 @@
|
||||
<translation>ENTSPANNTER MODUS AN</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>FirehosePanel</name>
|
||||
<message>
|
||||
<source>🔥 Firehose Mode 🔥</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>openpilot learns to drive by watching humans, like you, drive.
|
||||
|
||||
Firehose Mode allows you to maximize your training data uploads to improve openpilot's driving models. More data means bigger models, which means better Experimental Mode.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Firehose Mode: ACTIVE</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>For maximum effectiveness, bring your device inside and connect to a good USB-C adapter and Wi-Fi weekly.<br><br>Firehose Mode can also work while you're driving if connected to a hotspot or unlimited SIM card.<br><br><br><b>Frequently Asked Questions</b><br><br><i>Does it matter how or where I drive?</i> Nope, just drive as you normally would.<br><br><i>What's a good USB-C adapter?</i> Any fast phone or laptop charger should be fine.<br><br><i>Does it matter which software I run?</i> Yes, only upstream openpilot (and particular forks) are able to be used for training.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><b>%1 %2</b> of your driving are in the training dataset so far.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>ACTIVE</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><span stylesheet='font-size: 60px; font-weight: bold; color: #e74c3c;'>INACTIVE</span>: connect to unmetered network</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>HudRenderer</name>
|
||||
<message>
|
||||
@@ -639,6 +672,10 @@ This may take up to a minute.</source>
|
||||
<source>Developer</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Firehose</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>Setup</name>
|
||||
@@ -950,22 +987,22 @@ This may take up to a minute.</source>
|
||||
</context>
|
||||
<context>
|
||||
<name>TermsPage</name>
|
||||
<message>
|
||||
<source>Terms & Conditions</source>
|
||||
<translation>Benutzungsbedingungen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Decline</source>
|
||||
<translation>Ablehnen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Scroll to accept</source>
|
||||
<translation>Scrolle herunter um zu akzeptieren</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Agree</source>
|
||||
<translation>Zustimmen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Welcome to openpilot</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>You must accept the Terms and Conditions to use openpilot. Read the latest terms at <span style='color: #465BEA;'>https://comma.ai/terms</span> before continuing.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>TogglesPanel</name>
|
||||
@@ -1077,14 +1114,6 @@ This may take up to a minute.</source>
|
||||
<source>Enable driver monitoring even when openpilot is not engaged.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>FIREHOSE Mode</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Enable <b>FIREHOSE Mode</b> to get your driving data in the training set.<br><br>Follow these steps to get your device ready:<br> 1. Bring your device inside and connect to a good USB-C adapter<br> 2. Connect to Wi-Fi<br> 3. Enable this toggle<br> 4. Leave it connected for at least 30 minutes<br><br>This toggle turns off once you restart your device. Repeat once a week for maximum effectiveness.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>Updater</name>
|
||||
@@ -1124,23 +1153,15 @@ This may take up to a minute.</source>
|
||||
<context>
|
||||
<name>WiFiPromptWidget</name>
|
||||
<message>
|
||||
<source>Setup Wi-Fi</source>
|
||||
<source>Open</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Connect to Wi-Fi to upload driving data and help improve openpilot</source>
|
||||
<source>Maximize your training data uploads to improve openpilot's driving models.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Open Settings</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Ready to upload</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Training data will be pulled periodically while your device is on Wi-Fi</source>
|
||||
<source><span style='font-family: "Noto Color Emoji";'>🔥</span> Firehose Mode <span style='font-family: Noto Color Emoji;'>🔥</span></source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
|
||||
@@ -305,6 +305,39 @@
|
||||
<translation>MODO CHILL</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>FirehosePanel</name>
|
||||
<message>
|
||||
<source>🔥 Firehose Mode 🔥</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>openpilot learns to drive by watching humans, like you, drive.
|
||||
|
||||
Firehose Mode allows you to maximize your training data uploads to improve openpilot's driving models. More data means bigger models, which means better Experimental Mode.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Firehose Mode: ACTIVE</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>For maximum effectiveness, bring your device inside and connect to a good USB-C adapter and Wi-Fi weekly.<br><br>Firehose Mode can also work while you're driving if connected to a hotspot or unlimited SIM card.<br><br><br><b>Frequently Asked Questions</b><br><br><i>Does it matter how or where I drive?</i> Nope, just drive as you normally would.<br><br><i>What's a good USB-C adapter?</i> Any fast phone or laptop charger should be fine.<br><br><i>Does it matter which software I run?</i> Yes, only upstream openpilot (and particular forks) are able to be used for training.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><b>%1 %2</b> of your driving are in the training dataset so far.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>ACTIVE</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><span stylesheet='font-size: 60px; font-weight: bold; color: #e74c3c;'>INACTIVE</span>: connect to unmetered network</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>HudRenderer</name>
|
||||
<message>
|
||||
@@ -641,6 +674,10 @@ Esto puede tardar un minuto.</translation>
|
||||
<source>Developer</source>
|
||||
<translation>Desarrollador</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Firehose</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>Setup</name>
|
||||
@@ -950,22 +987,22 @@ Esto puede tardar un minuto.</translation>
|
||||
</context>
|
||||
<context>
|
||||
<name>TermsPage</name>
|
||||
<message>
|
||||
<source>Terms & Conditions</source>
|
||||
<translation>Términos & Condiciones</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Decline</source>
|
||||
<translation>Rechazar</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Scroll to accept</source>
|
||||
<translation>Desliza para aceptar</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Agree</source>
|
||||
<translation>Aceptar</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Welcome to openpilot</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>You must accept the Terms and Conditions to use openpilot. Read the latest terms at <span style='color: #465BEA;'>https://comma.ai/terms</span> before continuing.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>TogglesPanel</name>
|
||||
@@ -1077,14 +1114,6 @@ Esto puede tardar un minuto.</translation>
|
||||
<source>Enable the openpilot longitudinal control (alpha) toggle to allow Experimental mode.</source>
|
||||
<translation>Activar el control longitudinal (fase experimental) para permitir el modo Experimental.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>FIREHOSE Mode</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Enable <b>FIREHOSE Mode</b> to get your driving data in the training set.<br><br>Follow these steps to get your device ready:<br> 1. Bring your device inside and connect to a good USB-C adapter<br> 2. Connect to Wi-Fi<br> 3. Enable this toggle<br> 4. Leave it connected for at least 30 minutes<br><br>This toggle turns off once you restart your device. Repeat once a week for maximum effectiveness.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>Updater</name>
|
||||
@@ -1124,24 +1153,16 @@ Esto puede tardar un minuto.</translation>
|
||||
<context>
|
||||
<name>WiFiPromptWidget</name>
|
||||
<message>
|
||||
<source>Setup Wi-Fi</source>
|
||||
<translation>Configurar Wi-Fi</translation>
|
||||
<source>Open</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Connect to Wi-Fi to upload driving data and help improve openpilot</source>
|
||||
<translation>Conectarse al Wi-Fi para subir los datos de conducción y mejorar openpilot</translation>
|
||||
<source>Maximize your training data uploads to improve openpilot's driving models.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Open Settings</source>
|
||||
<translation>Abrir Configuraciones</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Ready to upload</source>
|
||||
<translation>Listo para subir</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Training data will be pulled periodically while your device is on Wi-Fi</source>
|
||||
<translation>Los datos de entrenamiento se extraerán periódicamente mientras tu dispositivo esté conectado a Wi-Fi</translation>
|
||||
<source><span style='font-family: "Noto Color Emoji";'>🔥</span> Firehose Mode <span style='font-family: Noto Color Emoji;'>🔥</span></source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
|
||||
@@ -305,6 +305,39 @@
|
||||
<translation>MODE DÉTENTE ACTIVÉ</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>FirehosePanel</name>
|
||||
<message>
|
||||
<source>🔥 Firehose Mode 🔥</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>openpilot learns to drive by watching humans, like you, drive.
|
||||
|
||||
Firehose Mode allows you to maximize your training data uploads to improve openpilot's driving models. More data means bigger models, which means better Experimental Mode.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Firehose Mode: ACTIVE</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>For maximum effectiveness, bring your device inside and connect to a good USB-C adapter and Wi-Fi weekly.<br><br>Firehose Mode can also work while you're driving if connected to a hotspot or unlimited SIM card.<br><br><br><b>Frequently Asked Questions</b><br><br><i>Does it matter how or where I drive?</i> Nope, just drive as you normally would.<br><br><i>What's a good USB-C adapter?</i> Any fast phone or laptop charger should be fine.<br><br><i>Does it matter which software I run?</i> Yes, only upstream openpilot (and particular forks) are able to be used for training.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><b>%1 %2</b> of your driving are in the training dataset so far.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>ACTIVE</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><span stylesheet='font-size: 60px; font-weight: bold; color: #e74c3c;'>INACTIVE</span>: connect to unmetered network</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>HudRenderer</name>
|
||||
<message>
|
||||
@@ -641,6 +674,10 @@ Cela peut prendre jusqu'à une minute.</translation>
|
||||
<source>Developer</source>
|
||||
<translation>Dév.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Firehose</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>Setup</name>
|
||||
@@ -950,22 +987,22 @@ Cela peut prendre jusqu'à une minute.</translation>
|
||||
</context>
|
||||
<context>
|
||||
<name>TermsPage</name>
|
||||
<message>
|
||||
<source>Terms & Conditions</source>
|
||||
<translation>Termes & Conditions</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Decline</source>
|
||||
<translation>Refuser</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Scroll to accept</source>
|
||||
<translation>Faire défiler pour accepter</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Agree</source>
|
||||
<translation>Accepter</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Welcome to openpilot</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>You must accept the Terms and Conditions to use openpilot. Read the latest terms at <span style='color: #465BEA;'>https://comma.ai/terms</span> before continuing.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>TogglesPanel</name>
|
||||
@@ -1077,14 +1114,6 @@ Cela peut prendre jusqu'à une minute.</translation>
|
||||
<source>Enable driver monitoring even when openpilot is not engaged.</source>
|
||||
<translation>Activer la surveillance conducteur lorsque openpilot n'est pas actif.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>FIREHOSE Mode</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Enable <b>FIREHOSE Mode</b> to get your driving data in the training set.<br><br>Follow these steps to get your device ready:<br> 1. Bring your device inside and connect to a good USB-C adapter<br> 2. Connect to Wi-Fi<br> 3. Enable this toggle<br> 4. Leave it connected for at least 30 minutes<br><br>This toggle turns off once you restart your device. Repeat once a week for maximum effectiveness.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>Updater</name>
|
||||
@@ -1124,24 +1153,16 @@ Cela peut prendre jusqu'à une minute.</translation>
|
||||
<context>
|
||||
<name>WiFiPromptWidget</name>
|
||||
<message>
|
||||
<source>Setup Wi-Fi</source>
|
||||
<translation>Configurer Wi-Fi</translation>
|
||||
<source>Open</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Connect to Wi-Fi to upload driving data and help improve openpilot</source>
|
||||
<translation>Connectez-vous au Wi-Fi pour publier les données de conduite et aider à améliorer openpilot</translation>
|
||||
<source>Maximize your training data uploads to improve openpilot's driving models.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Open Settings</source>
|
||||
<translation>Ouvrir les paramètres</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Ready to upload</source>
|
||||
<translation>Prêt à uploader</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Training data will be pulled periodically while your device is on Wi-Fi</source>
|
||||
<translation>Les données d'entraînement seront envoyées périodiquement lorsque votre appareil est connecté au réseau Wi-Fi</translation>
|
||||
<source><span style='font-family: "Noto Color Emoji";'>🔥</span> Firehose Mode <span style='font-family: Noto Color Emoji;'>🔥</span></source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
|
||||
@@ -305,6 +305,39 @@
|
||||
<translation>CHILLモード</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>FirehosePanel</name>
|
||||
<message>
|
||||
<source>🔥 Firehose Mode 🔥</source>
|
||||
<translation>🔥 Firehoseモード 🔥</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>openpilot learns to drive by watching humans, like you, drive.
|
||||
|
||||
Firehose Mode allows you to maximize your training data uploads to improve openpilot's driving models. More data means bigger models, which means better Experimental Mode.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Firehose Mode: ACTIVE</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>For maximum effectiveness, bring your device inside and connect to a good USB-C adapter and Wi-Fi weekly.<br><br>Firehose Mode can also work while you're driving if connected to a hotspot or unlimited SIM card.<br><br><br><b>Frequently Asked Questions</b><br><br><i>Does it matter how or where I drive?</i> Nope, just drive as you normally would.<br><br><i>What's a good USB-C adapter?</i> Any fast phone or laptop charger should be fine.<br><br><i>Does it matter which software I run?</i> Yes, only upstream openpilot (and particular forks) are able to be used for training.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><b>%1 %2</b> of your driving are in the training dataset so far.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>ACTIVE</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><span stylesheet='font-size: 60px; font-weight: bold; color: #e74c3c;'>INACTIVE</span>: connect to unmetered network</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>HudRenderer</name>
|
||||
<message>
|
||||
@@ -637,6 +670,10 @@ This may take up to a minute.</source>
|
||||
<source>Developer</source>
|
||||
<translation>開発</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Firehose</source>
|
||||
<translation>データ学習</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>Setup</name>
|
||||
@@ -946,22 +983,22 @@ This may take up to a minute.</source>
|
||||
</context>
|
||||
<context>
|
||||
<name>TermsPage</name>
|
||||
<message>
|
||||
<source>Terms & Conditions</source>
|
||||
<translation>利用規約</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Decline</source>
|
||||
<translation>拒否</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Scroll to accept</source>
|
||||
<translation>スクロールして同意</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Agree</source>
|
||||
<translation>同意</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Welcome to openpilot</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>You must accept the Terms and Conditions to use openpilot. Read the latest terms at <span style='color: #465BEA;'>https://comma.ai/terms</span> before continuing.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>TogglesPanel</name>
|
||||
@@ -1073,14 +1110,6 @@ This may take up to a minute.</source>
|
||||
<source>Enable driver monitoring even when openpilot is not engaged.</source>
|
||||
<translation>openpilotが作動していない場合でも運転者モニタリングを有効にする。</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>FIREHOSE Mode</source>
|
||||
<translation>FIREHOSEモード</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Enable <b>FIREHOSE Mode</b> to get your driving data in the training set.<br><br>Follow these steps to get your device ready:<br> 1. Bring your device inside and connect to a good USB-C adapter<br> 2. Connect to Wi-Fi<br> 3. Enable this toggle<br> 4. Leave it connected for at least 30 minutes<br><br>This toggle turns off once you restart your device. Repeat once a week for maximum effectiveness.</source>
|
||||
<translation><b>FIREHOSEモード</b>を有効にすると、あなたの運転が公式のトレーニングデータに追加されます。<br><br>次の手順でデバイスを準備してください:<br> 1. デバイスを屋内に持ち込み、適切なUSB-Cアダプターに接続する<br> 2. Wi-Fiに接続する<br> 3. このスイッチを有効にする<br> 4. 少なくとも30分間接続したままにする<br><br>このスイッチはデバイスを再起動すると無効になります。効果を最大化するためには毎週実行するのが望ましいです。</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>Updater</name>
|
||||
@@ -1120,24 +1149,16 @@ This may take up to a minute.</source>
|
||||
<context>
|
||||
<name>WiFiPromptWidget</name>
|
||||
<message>
|
||||
<source>Setup Wi-Fi</source>
|
||||
<translation>Wi-Fi設定</translation>
|
||||
<source>Open</source>
|
||||
<translation>開く</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Connect to Wi-Fi to upload driving data and help improve openpilot</source>
|
||||
<translation>走行データをアップロードしてopenpilotの改善に役立てるためにWi-Fi接続してください</translation>
|
||||
<source>Maximize your training data uploads to improve openpilot's driving models.</source>
|
||||
<translation>openpilotの運転モデルを改善するために、大容量の学習データをアップロードして下さい。</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Open Settings</source>
|
||||
<translation>設定を開く</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Ready to upload</source>
|
||||
<translation>アップロード準備完了</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Training data will be pulled periodically while your device is on Wi-Fi</source>
|
||||
<translation>デバイスがWi-Fiに接続中は、トレーニングデータが定期的に送信されます</translation>
|
||||
<source><span style='font-family: "Noto Color Emoji";'>🔥</span> Firehose Mode <span style='font-family: Noto Color Emoji;'>🔥</span></source>
|
||||
<translation><span style='font-family: "Noto Color Emoji";'>🔥</span> Firehoseモード <span style='font-family: Noto Color Emoji;'>🔥</span></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
|
||||
@@ -121,27 +121,27 @@
|
||||
</message>
|
||||
<message>
|
||||
<source>Longitudinal Maneuver Mode</source>
|
||||
<translation>롱컨 기동 모드</translation>
|
||||
<translation>롱컨 제어 모드</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>openpilot Longitudinal Control (Alpha)</source>
|
||||
<translation type="unfinished">openpilot 가감속 제어 (알파)</translation>
|
||||
<translation>openpilot 가감속 제어 (알파)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>WARNING: openpilot longitudinal control is in alpha for this car and will disable Automatic Emergency Braking (AEB).</source>
|
||||
<translation type="unfinished">경고: openpilot 가감속 제어 알파 기능으로 차량의 자동긴급제동(AEB)을 비활성화합니다.</translation>
|
||||
<translation>경고: openpilot 가감속 제어 알파 기능으로 차량의 자동긴급제동(AEB)을 비활성화합니다.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>On this car, openpilot defaults to the car's built-in ACC instead of openpilot's longitudinal control. Enable this to switch to openpilot longitudinal control. Enabling Experimental mode is recommended when enabling openpilot longitudinal control alpha.</source>
|
||||
<translation type="unfinished">이 차량은 openpilot 가감속 제어 대신 기본적으로 차량의 ACC로 가감속을 제어합니다. openpilot의 가감속 제어로 전환하려면 이 기능을 활성화하세요. openpilot 가감속 제어 알파를 활성화하는 경우 실험 모드 활성화를 권장합니다.</translation>
|
||||
<translation>이 차량은 openpilot 가감속 제어 대신 기본적으로 차량의 ACC로 가감속을 제어합니다. openpilot의 가감속 제어로 전환하려면 이 기능을 활성화하세요. openpilot 가감속 제어 알파를 활성화하는 경우 실험 모드 활성화를 권장합니다.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Enable ADB</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>ADB 사용</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>ADB (Android Debug Bridge) allows connecting to your device over USB or over the network. See https://docs.comma.ai/how-to/connect-to-comma for more info.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>ADB (안드로이드 디버그 브릿지) USB 또는 네트워크를 통해 장치에 연결할 수 있습니다. 자세한 내용은 https://docs.comma.ai/how-to/connect-to-comma를 참조하세요.</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@@ -305,6 +305,39 @@
|
||||
<translation>안정 모드 사용</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>FirehosePanel</name>
|
||||
<message>
|
||||
<source>🔥 Firehose Mode 🔥</source>
|
||||
<translation>🔥 파이어호스 모드 🔥</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>openpilot learns to drive by watching humans, like you, drive.
|
||||
|
||||
Firehose Mode allows you to maximize your training data uploads to improve openpilot's driving models. More data means bigger models, which means better Experimental Mode.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Firehose Mode: ACTIVE</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>For maximum effectiveness, bring your device inside and connect to a good USB-C adapter and Wi-Fi weekly.<br><br>Firehose Mode can also work while you're driving if connected to a hotspot or unlimited SIM card.<br><br><br><b>Frequently Asked Questions</b><br><br><i>Does it matter how or where I drive?</i> Nope, just drive as you normally would.<br><br><i>What's a good USB-C adapter?</i> Any fast phone or laptop charger should be fine.<br><br><i>Does it matter which software I run?</i> Yes, only upstream openpilot (and particular forks) are able to be used for training.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><b>%1 %2</b> of your driving are in the training dataset so far.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>ACTIVE</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><span stylesheet='font-size: 60px; font-weight: bold; color: #e74c3c;'>INACTIVE</span>: connect to unmetered network</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>HudRenderer</name>
|
||||
<message>
|
||||
@@ -637,6 +670,10 @@ This may take up to a minute.</source>
|
||||
<source>Developer</source>
|
||||
<translation>개발자</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Firehose</source>
|
||||
<translation>파이어호스</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>Setup</name>
|
||||
@@ -946,22 +983,22 @@ This may take up to a minute.</source>
|
||||
</context>
|
||||
<context>
|
||||
<name>TermsPage</name>
|
||||
<message>
|
||||
<source>Terms & Conditions</source>
|
||||
<translation>이용약관</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Decline</source>
|
||||
<translation>거절</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Scroll to accept</source>
|
||||
<translation>동의하려면 아래로 스크롤하세요</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Agree</source>
|
||||
<translation>동의</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Welcome to openpilot</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>You must accept the Terms and Conditions to use openpilot. Read the latest terms at <span style='color: #465BEA;'>https://comma.ai/terms</span> before continuing.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>TogglesPanel</name>
|
||||
@@ -1073,14 +1110,6 @@ This may take up to a minute.</source>
|
||||
<source>Enable driver monitoring even when openpilot is not engaged.</source>
|
||||
<translation>Openpilot이 활성화되지 않은 경우에도 드라이버 모니터링을 활성화합니다.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>FIREHOSE Mode</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Enable <b>FIREHOSE Mode</b> to get your driving data in the training set.<br><br>Follow these steps to get your device ready:<br> 1. Bring your device inside and connect to a good USB-C adapter<br> 2. Connect to Wi-Fi<br> 3. Enable this toggle<br> 4. Leave it connected for at least 30 minutes<br><br>This toggle turns off once you restart your device. Repeat once a week for maximum effectiveness.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>Updater</name>
|
||||
@@ -1120,24 +1149,16 @@ This may take up to a minute.</source>
|
||||
<context>
|
||||
<name>WiFiPromptWidget</name>
|
||||
<message>
|
||||
<source>Setup Wi-Fi</source>
|
||||
<translation>Wi-Fi 설정</translation>
|
||||
<source>Open</source>
|
||||
<translation>열기</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Connect to Wi-Fi to upload driving data and help improve openpilot</source>
|
||||
<translation>Wi-Fi에 연결하여 주행 데이터를 업로드하고 openpilot 개선에 기여하세요</translation>
|
||||
<source>Maximize your training data uploads to improve openpilot's driving models.</source>
|
||||
<translation>훈련 데이터 업로드를 최대화하여 오픈파일럿의 주행 모델을 개선하세요.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Open Settings</source>
|
||||
<translation>설정 열기</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Ready to upload</source>
|
||||
<translation>업로드 준비 완료</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Training data will be pulled periodically while your device is on Wi-Fi</source>
|
||||
<translation>기기가 Wi-Fi에 연결되어 있는 동안 트레이닝 데이터를 주기적으로 전송합니다</translation>
|
||||
<source><span style='font-family: "Noto Color Emoji";'>🔥</span> Firehose Mode <span style='font-family: Noto Color Emoji;'>🔥</span></source>
|
||||
<translation><span style='font-family: "Noto Color Emoji";'>🔥</span> 파이어호스 모드 <span style='font-family: Noto Color Emoji;'>🔥</span></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
|
||||
@@ -305,6 +305,39 @@
|
||||
<translation>MODO CHILL ON</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>FirehosePanel</name>
|
||||
<message>
|
||||
<source>🔥 Firehose Mode 🔥</source>
|
||||
<translation>🔥 Mode Firehose 🔥</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>openpilot learns to drive by watching humans, like you, drive.
|
||||
|
||||
Firehose Mode allows you to maximize your training data uploads to improve openpilot's driving models. More data means bigger models, which means better Experimental Mode.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Firehose Mode: ACTIVE</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>For maximum effectiveness, bring your device inside and connect to a good USB-C adapter and Wi-Fi weekly.<br><br>Firehose Mode can also work while you're driving if connected to a hotspot or unlimited SIM card.<br><br><br><b>Frequently Asked Questions</b><br><br><i>Does it matter how or where I drive?</i> Nope, just drive as you normally would.<br><br><i>What's a good USB-C adapter?</i> Any fast phone or laptop charger should be fine.<br><br><i>Does it matter which software I run?</i> Yes, only upstream openpilot (and particular forks) are able to be used for training.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><b>%1 %2</b> of your driving are in the training dataset so far.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>ACTIVE</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><span stylesheet='font-size: 60px; font-weight: bold; color: #e74c3c;'>INACTIVE</span>: connect to unmetered network</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>HudRenderer</name>
|
||||
<message>
|
||||
@@ -641,6 +674,10 @@ Isso pode levar até um minuto.</translation>
|
||||
<source>Developer</source>
|
||||
<translation>Desenvdor</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Firehose</source>
|
||||
<translation>Firehose</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>Setup</name>
|
||||
@@ -950,22 +987,22 @@ Isso pode levar até um minuto.</translation>
|
||||
</context>
|
||||
<context>
|
||||
<name>TermsPage</name>
|
||||
<message>
|
||||
<source>Terms & Conditions</source>
|
||||
<translation>Termos & Condições</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Decline</source>
|
||||
<translation>Declinar</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Scroll to accept</source>
|
||||
<translation>Role a tela para aceitar</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Agree</source>
|
||||
<translation>Concordo</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Welcome to openpilot</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>You must accept the Terms and Conditions to use openpilot. Read the latest terms at <span style='color: #465BEA;'>https://comma.ai/terms</span> before continuing.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>TogglesPanel</name>
|
||||
@@ -1077,14 +1114,6 @@ Isso pode levar até um minuto.</translation>
|
||||
<source>Enable driver monitoring even when openpilot is not engaged.</source>
|
||||
<translation>Habilite o monitoramento do motorista mesmo quando o openpilot não estiver acionado.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>FIREHOSE Mode</source>
|
||||
<translation>Modo FIREHOSE</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Enable <b>FIREHOSE Mode</b> to get your driving data in the training set.<br><br>Follow these steps to get your device ready:<br> 1. Bring your device inside and connect to a good USB-C adapter<br> 2. Connect to Wi-Fi<br> 3. Enable this toggle<br> 4. Leave it connected for at least 30 minutes<br><br>This toggle turns off once you restart your device. Repeat once a week for maximum effectiveness.</source>
|
||||
<translation>Habilite o <b>Modo FIREHOSE</b> para obter seus dados de direção no conjunto de treinamento.<br><br>Siga estas etapas para preparar seu dispositivo:<br> 1. Leve seu dispositivo para dentro e conecte-o a um bom adaptador USB-C<br> 2. Conecte-se ao Wi-Fi<br> 3. Habilite este toggle<br> 4. Deixe-o conectado por pelo menos 30 minutos.<br><br>Este botão desativa após reiniciar o dispositivo. Repita uma vez por semana para obter a máxima eficácia.</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>Updater</name>
|
||||
@@ -1124,24 +1153,16 @@ Isso pode levar até um minuto.</translation>
|
||||
<context>
|
||||
<name>WiFiPromptWidget</name>
|
||||
<message>
|
||||
<source>Setup Wi-Fi</source>
|
||||
<translation>Configurar Wi-Fi</translation>
|
||||
<source>Open</source>
|
||||
<translation>Abrir</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Connect to Wi-Fi to upload driving data and help improve openpilot</source>
|
||||
<translation>Conecte se ao Wi-Fi para realizar upload de dados de condução e ajudar a melhorar o openpilot</translation>
|
||||
<source>Maximize your training data uploads to improve openpilot's driving models.</source>
|
||||
<translation>Maximize seus envios de dados de treinamento para melhorar os modelos de direção do openpilot.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Open Settings</source>
|
||||
<translation>Abrir Configurações</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Ready to upload</source>
|
||||
<translation>Pronto para upload</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Training data will be pulled periodically while your device is on Wi-Fi</source>
|
||||
<translation>Os dados de treinamento serão extraídos periodicamente enquanto o dispositivo estiver no Wi-Fi</translation>
|
||||
<source><span style='font-family: "Noto Color Emoji";'>🔥</span> Firehose Mode <span style='font-family: Noto Color Emoji;'>🔥</span></source>
|
||||
<translation><span style='font-family: "Noto Color Emoji";'>🔥</span> Mode Firehose <span style='font-family: Noto Color Emoji;'>🔥</span></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
|
||||
@@ -305,6 +305,39 @@
|
||||
<translation>คุณกำลังใช้โหมดชิล</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>FirehosePanel</name>
|
||||
<message>
|
||||
<source>🔥 Firehose Mode 🔥</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>openpilot learns to drive by watching humans, like you, drive.
|
||||
|
||||
Firehose Mode allows you to maximize your training data uploads to improve openpilot's driving models. More data means bigger models, which means better Experimental Mode.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Firehose Mode: ACTIVE</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>For maximum effectiveness, bring your device inside and connect to a good USB-C adapter and Wi-Fi weekly.<br><br>Firehose Mode can also work while you're driving if connected to a hotspot or unlimited SIM card.<br><br><br><b>Frequently Asked Questions</b><br><br><i>Does it matter how or where I drive?</i> Nope, just drive as you normally would.<br><br><i>What's a good USB-C adapter?</i> Any fast phone or laptop charger should be fine.<br><br><i>Does it matter which software I run?</i> Yes, only upstream openpilot (and particular forks) are able to be used for training.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><b>%1 %2</b> of your driving are in the training dataset so far.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>ACTIVE</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><span stylesheet='font-size: 60px; font-weight: bold; color: #e74c3c;'>INACTIVE</span>: connect to unmetered network</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>HudRenderer</name>
|
||||
<message>
|
||||
@@ -637,6 +670,10 @@ This may take up to a minute.</source>
|
||||
<source>Developer</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Firehose</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>Setup</name>
|
||||
@@ -946,22 +983,22 @@ This may take up to a minute.</source>
|
||||
</context>
|
||||
<context>
|
||||
<name>TermsPage</name>
|
||||
<message>
|
||||
<source>Terms & Conditions</source>
|
||||
<translation>ข้อตกลงและเงื่อนไข</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Decline</source>
|
||||
<translation>ปฏิเสธ</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Scroll to accept</source>
|
||||
<translation>เลื่อนเพื่อตอบรับข้อตกลง</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Agree</source>
|
||||
<translation>ยอมรับ</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Welcome to openpilot</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>You must accept the Terms and Conditions to use openpilot. Read the latest terms at <span style='color: #465BEA;'>https://comma.ai/terms</span> before continuing.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>TogglesPanel</name>
|
||||
@@ -1073,14 +1110,6 @@ This may take up to a minute.</source>
|
||||
<source>Enable driver monitoring even when openpilot is not engaged.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>FIREHOSE Mode</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Enable <b>FIREHOSE Mode</b> to get your driving data in the training set.<br><br>Follow these steps to get your device ready:<br> 1. Bring your device inside and connect to a good USB-C adapter<br> 2. Connect to Wi-Fi<br> 3. Enable this toggle<br> 4. Leave it connected for at least 30 minutes<br><br>This toggle turns off once you restart your device. Repeat once a week for maximum effectiveness.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>Updater</name>
|
||||
@@ -1120,24 +1149,16 @@ This may take up to a minute.</source>
|
||||
<context>
|
||||
<name>WiFiPromptWidget</name>
|
||||
<message>
|
||||
<source>Setup Wi-Fi</source>
|
||||
<translation>ตั้งค่า Wi-Fi</translation>
|
||||
<source>Open</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Connect to Wi-Fi to upload driving data and help improve openpilot</source>
|
||||
<translation>เชื่อมต่อกับ Wi-Fi เพื่ออัปโหลดข้อมูลการขับขี่และช่วยปรับปรุง openpilot</translation>
|
||||
<source>Maximize your training data uploads to improve openpilot's driving models.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Open Settings</source>
|
||||
<translation>เปิดการตั้งค่า</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Ready to upload</source>
|
||||
<translation>พร้อมจะอัปโหลด</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Training data will be pulled periodically while your device is on Wi-Fi</source>
|
||||
<translation>ข้อมูลการฝึกฝนจะถูกดึงเป็นระยะระหว่างที่อุปกรณ์ของคุณเชื่อมต่อกับ Wi-Fi</translation>
|
||||
<source><span style='font-family: "Noto Color Emoji";'>🔥</span> Firehose Mode <span style='font-family: Noto Color Emoji;'>🔥</span></source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
|
||||
@@ -305,6 +305,39 @@
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>FirehosePanel</name>
|
||||
<message>
|
||||
<source>🔥 Firehose Mode 🔥</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>openpilot learns to drive by watching humans, like you, drive.
|
||||
|
||||
Firehose Mode allows you to maximize your training data uploads to improve openpilot's driving models. More data means bigger models, which means better Experimental Mode.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Firehose Mode: ACTIVE</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>For maximum effectiveness, bring your device inside and connect to a good USB-C adapter and Wi-Fi weekly.<br><br>Firehose Mode can also work while you're driving if connected to a hotspot or unlimited SIM card.<br><br><br><b>Frequently Asked Questions</b><br><br><i>Does it matter how or where I drive?</i> Nope, just drive as you normally would.<br><br><i>What's a good USB-C adapter?</i> Any fast phone or laptop charger should be fine.<br><br><i>Does it matter which software I run?</i> Yes, only upstream openpilot (and particular forks) are able to be used for training.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><b>%1 %2</b> of your driving are in the training dataset so far.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>ACTIVE</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source><span stylesheet='font-size: 60px; font-weight: bold; color: #e74c3c;'>INACTIVE</span>: connect to unmetered network</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>HudRenderer</name>
|
||||
<message>
|
||||
@@ -635,6 +668,10 @@ This may take up to a minute.</source>
|
||||
<source>Developer</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Firehose</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>Setup</name>
|
||||
@@ -944,22 +981,22 @@ This may take up to a minute.</source>
|
||||
</context>
|
||||
<context>
|
||||
<name>TermsPage</name>
|
||||
<message>
|
||||
<source>Terms & Conditions</source>
|
||||
<translation>Şartlar ve Koşullar</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Decline</source>
|
||||
<translation>Reddet</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Scroll to accept</source>
|
||||
<translation>Kabul etmek için kaydırın</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Agree</source>
|
||||
<translation>Kabul et</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Welcome to openpilot</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>You must accept the Terms and Conditions to use openpilot. Read the latest terms at <span style='color: #465BEA;'>https://comma.ai/terms</span> before continuing.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>TogglesPanel</name>
|
||||
@@ -1071,14 +1108,6 @@ This may take up to a minute.</source>
|
||||
<source>Enable driver monitoring even when openpilot is not engaged.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>FIREHOSE Mode</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Enable <b>FIREHOSE Mode</b> to get your driving data in the training set.<br><br>Follow these steps to get your device ready:<br> 1. Bring your device inside and connect to a good USB-C adapter<br> 2. Connect to Wi-Fi<br> 3. Enable this toggle<br> 4. Leave it connected for at least 30 minutes<br><br>This toggle turns off once you restart your device. Repeat once a week for maximum effectiveness.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>Updater</name>
|
||||
@@ -1118,23 +1147,15 @@ This may take up to a minute.</source>
|
||||
<context>
|
||||
<name>WiFiPromptWidget</name>
|
||||
<message>
|
||||
<source>Setup Wi-Fi</source>
|
||||
<source>Open</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Connect to Wi-Fi to upload driving data and help improve openpilot</source>
|
||||
<source>Maximize your training data uploads to improve openpilot's driving models.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Open Settings</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Ready to upload</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Training data will be pulled periodically while your device is on Wi-Fi</source>
|
||||
<source><span style='font-family: "Noto Color Emoji";'>🔥</span> Firehose Mode <span style='font-family: Noto Color Emoji;'>🔥</span></source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user