From 9be167f90f857b4890745423d7722b8b05a41f76 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Sat, 20 Jun 2026 14:02:24 -0700 Subject: [PATCH] move lat/long maneuvers into selfdrive/ --- .../tuning}/.gitignore | 1 + selfdrive/tuning/README.md | 149 ++++++++++++++++++ .../tuning/generate_lateral_report.py | 63 +------- .../tuning/generate_longitudinal_report.py | 65 +------- .../tuning}/joystick_control.py | 0 .../tuning}/joystickd.py | 0 .../tuning}/lateral_maneuversd.py | 2 +- selfdrive/tuning/maneuver_helpers.py | 94 +++++++++++ .../tuning}/maneuversd.py | 0 .../tuning}/mpc_longitudinal_tuning_report.py | 2 +- system/manager/process_config.py | 8 +- tools/README.md | 1 - tools/joystick/README.md | 53 ------- tools/lateral_maneuvers/.gitignore | 1 - tools/lateral_maneuvers/README.md | 42 ----- tools/longitudinal_maneuvers/README.md | 48 ------ .../maneuver_helpers.py | 18 --- 17 files changed, 260 insertions(+), 287 deletions(-) rename {tools/longitudinal_maneuvers => selfdrive/tuning}/.gitignore (56%) create mode 100644 selfdrive/tuning/README.md rename tools/lateral_maneuvers/generate_report.py => selfdrive/tuning/generate_lateral_report.py (81%) rename tools/longitudinal_maneuvers/generate_report.py => selfdrive/tuning/generate_longitudinal_report.py (73%) rename {tools/joystick => selfdrive/tuning}/joystick_control.py (100%) rename {tools/joystick => selfdrive/tuning}/joystickd.py (100%) rename {tools/lateral_maneuvers => selfdrive/tuning}/lateral_maneuversd.py (98%) create mode 100644 selfdrive/tuning/maneuver_helpers.py rename {tools/longitudinal_maneuvers => selfdrive/tuning}/maneuversd.py (100%) rename {tools/longitudinal_maneuvers => selfdrive/tuning}/mpc_longitudinal_tuning_report.py (99%) delete mode 100644 tools/joystick/README.md delete mode 100644 tools/lateral_maneuvers/.gitignore delete mode 100644 tools/lateral_maneuvers/README.md delete mode 100644 tools/longitudinal_maneuvers/README.md delete mode 100644 tools/longitudinal_maneuvers/maneuver_helpers.py diff --git a/tools/longitudinal_maneuvers/.gitignore b/selfdrive/tuning/.gitignore similarity index 56% rename from tools/longitudinal_maneuvers/.gitignore rename to selfdrive/tuning/.gitignore index e819fa62683a97..ff0d957f461be5 100644 --- a/tools/longitudinal_maneuvers/.gitignore +++ b/selfdrive/tuning/.gitignore @@ -1 +1,2 @@ /longitudinal_reports/ +/lateral_reports/ diff --git a/selfdrive/tuning/README.md b/selfdrive/tuning/README.md new file mode 100644 index 00000000000000..507c17c5e84c1a --- /dev/null +++ b/selfdrive/tuning/README.md @@ -0,0 +1,149 @@ +# Tuning Tools + +Tools for testing and tuning openpilot's lateral and longitudinal control, plus a joystick debug mode. + +## Joystick + +**Hardware needed**: device running openpilot, laptop, joystick (optional) + +With joystick_control, you can connect your laptop to your comma device over the network and debug controls using a joystick or keyboard. +joystick_control uses [inputs](https://pypi.org/project/inputs) which supports many common gamepads and joysticks. + +### Usage + +The car must be off, and openpilot must be offroad before starting `joystick_control`. + +### Using a keyboard + +SSH into your comma device and start joystick_control with the following command: + +```shell +selfdrive/tuning/joystick_control.py --keyboard +``` + +The available buttons and axes will print showing their key mappings. In general, the WASD keys control gas and brakes and steering torque in 5% increments. + +### Joystick on your comma three + +Plug the joystick into your comma three aux USB-C port. Then, SSH into the device and start `joystick_control.py`. + +### Joystick on your laptop + +In order to use a joystick over the network, we need to run joystick_control locally from your laptop and have it send `testJoystick` packets over the network to the comma device. + +1. Connect a joystick to your PC. +2. Connect your laptop to your comma device's hotspot and open a new SSH shell. Since joystick_control is being run on your laptop, we need to write a parameter to let controlsd know to start in joystick debug mode: + ```shell + # on your comma device + echo -n "1" > /data/params/d/JoystickDebugMode + ``` +3. Run bridge with your laptop's IP address. This republishes the `testJoystick` packets sent from your laptop so that openpilot can receive them: + ```shell + # on your comma device + cereal/messaging/bridge {LAPTOP_IP} testJoystick + ``` +4. Start joystick_control on your laptop in ZMQ mode. + ```shell + # on your laptop + export ZMQ=1 + selfdrive/tuning/joystick_control.py + ``` + +--- +Now start your car and openpilot should go into joystick mode with an alert on startup! The status of the axes will display on the alert, while button statuses print in the shell. + +Make sure the conditions are met in the panda to allow controls (e.g. cruise control engaged). You can also make a modification to the panda code to always allow controls. + +![](https://github.com/commaai/openpilot/assets/8762862/e640cbca-cb7a-4dcb-abce-b23b036ad8e7) + +## Longitudinal Maneuvers + +Test your vehicle's longitudinal control tuning with this tool. The tool will test the vehicle's ability to follow a few longitudinal maneuvers and includes a tool to generate a report from the route. + +
Sample snapshot of a report.
+ +### Instructions + +1. Check out a development branch such as `master` on your comma device. +2. Locate either a large empty parking lot or road devoid of any car or foot traffic. Flat, straight road is preferred. The full maneuver suite can take 1 mile or more if left running, however it is recommended to disengage openpilot between maneuvers and turn around if there is not enough space. +3. Turn off the vehicle and set this parameter which will signal to openpilot to start the longitudinal maneuver daemon: + + ```sh + echo -n 1 > /data/params/d/LongitudinalManeuverMode + ``` + +4. Turn your vehicle back on. You will see the "Longitudinal Maneuver Mode" alert: + + ![videoframe_6652](https://github.com/user-attachments/assets/e9d4c95a-cd76-4ab7-933e-19937792fa0f) + +5. Ensure the road ahead is clear, as openpilot will not brake for any obstructions in this mode. Once you are ready, press "Set" on your steering wheel to start the tests. The tests will run for about 4 minutes. If you need to pause the tests, press "Cancel" on your steering wheel. You can resume the tests by pressing "Resume" on your steering wheel. + + **Note:** For GM cars, it is recommended to hold down the resume button for all low-speed tests (starting, stopping and creep) to avoid the car entering standstill. + + ![cog-clip-00 01 11 250-00 01 22 250](https://github.com/user-attachments/assets/c312c1cc-76e8-46e1-a05e-bb9dfb58994f) + +6. When the testing is complete, you'll see an alert that says "Maneuvers Finished." Complete the route by pulling over and turning off the vehicle. + + ![fin2](https://github.com/user-attachments/assets/c06960ae-7cfb-44af-beaa-4dc28848e49f) + +7. Visit https://connect.comma.ai and locate the route(s). They will stand out with lots of orange intervals in their timeline. Ensure "All logs" show as "uploaded." + + ![image](https://github.com/user-attachments/assets/cfe4c6d9-752f-4b24-b421-4b90a01933dc) + +8. Gather the route ID and then run the report generator. The file will be exported to the same directory: + + ```sh + $ python selfdrive/tuning/generate_longitudinal_report.py 57048cfce01d9625/0000010e--5b26bc3be7 'pcm accel compensation' + + processing report for LEXUS_ES_TSS2 + plotting maneuver: start from stop, runs: 4 + plotting maneuver: creep: alternate between +1m/s^2 and -1m/s^2, runs: 2 + plotting maneuver: gas step response: +1m/s^2 from 20mph, runs: 2 + + Report written to /home/batman/openpilot/selfdrive/tuning/longitudinal_reports/LEXUS_ES_TSS2_57048cfce01d9625_0000010e--5b26bc3be7.html + ``` + +You can reach out on [Discord](https://discord.comma.ai) if you have any questions about these instructions or the tool itself. + +## Lateral Maneuvers + +> [!WARNING] +> Use caution when using this tool. + +Test your vehicle's lateral control tuning with this tool. The tool will test the vehicle's ability to follow a few lateral maneuvers and includes a tool to generate a report from the route. + +### Instructions + +1. Check out a development branch such as `master` on your comma device. +2. The full maneuver suite runs at 20 and 30 mph. +3. Enable "Lateral Maneuver Mode" in Settings > Developer on the device while offroad. Alternatively, set the parameter manually: + + ```sh + echo -n 1 > /data/params/d/LateralManeuverMode + ``` + +4. Turn your vehicle back on. You will see "Lateral Maneuver Mode". + +5. Ensure the area ahead is clear, as openpilot will command lateral acceleration steps in this mode. Once you are ready, set ACC manually to the target speed shown on screen and let openpilot stabilize lateral. After 1 seconds of steady straight driving, the maneuver will begin automatically. openpilot lateral control stays engaged between maneuvers normally while waiting for the next maneuver's readiness conditions. The maneuver will be aborted and repeated if speed is out of range, steering is touched or openpilot disengages. + +6. When the testing is complete, you'll see an alert that says "Maneuvers Finished." Complete the route by pulling over and turning off the vehicle. + +7. Visit https://connect.comma.ai and locate the route(s). They will stand out with lots of orange intervals in their timeline. Ensure "All logs" show as "uploaded." + + ![image](https://github.com/user-attachments/assets/cfe4c6d9-752f-4b24-b421-4b90a01933dc) + +8. Gather the route ID and then run the report generator. The file will be exported to the same directory: + + ```sh + $ python selfdrive/tuning/generate_lateral_report.py 98395b7c5b27882e/000001cc--5a73bde686 + + processing report for KIA_EV6 + plotting maneuver: step right 20mph, runs: 3 + plotting maneuver: step left 20mph, runs: 3 + plotting maneuver: sine 0.5Hz 20mph, runs: 3 + plotting maneuver: step right 30mph, runs: 3 + + Opening report: /home/batman/openpilot/selfdrive/tuning/lateral_reports/KIA_EV6_98395b7c5b27882e_000001cc--5a73bde686.html + ``` + +You can reach out on [Discord](https://discord.comma.ai) if you have any questions about these instructions or the tool itself. diff --git a/tools/lateral_maneuvers/generate_report.py b/selfdrive/tuning/generate_lateral_report.py similarity index 81% rename from tools/lateral_maneuvers/generate_report.py rename to selfdrive/tuning/generate_lateral_report.py index c2ef7e99ea99d9..d91869087eda88 100755 --- a/tools/lateral_maneuvers/generate_report.py +++ b/selfdrive/tuning/generate_lateral_report.py @@ -4,20 +4,15 @@ import io import math import numpy as np -import os -import webbrowser from collections import defaultdict -from pathlib import Path import matplotlib.pyplot as plt from openpilot.common.utils import tabulate from cereal import car from openpilot.common.filter_simple import FirstOrderFilter from openpilot.selfdrive.controls.lib.latcontrol_torque import LP_FILTER_CUTOFF_HZ -from openpilot.tools.lib.logreader import LogReader -from openpilot.system.hardware.hw import Paths from openpilot.common.constants import CV -from openpilot.tools.longitudinal_maneuvers.generate_report import format_car_params +from openpilot.selfdrive.tuning.maneuver_helpers import init_report_builder, load_maneuver_route, write_report def lat_accel(curvature, v): @@ -25,22 +20,9 @@ def lat_accel(curvature, v): def report(platform, route, _description, CP, ID, maneuvers): - output_path = Path(__file__).resolve().parent / "lateral_reports" - output_fn = output_path / f"{platform}_{route.replace('/', '_')}.html" - output_path.mkdir(exist_ok=True) target_cross_times = defaultdict(list) + builder = init_report_builder("Lateral maneuver report", platform, route, _description, CP, ID) - builder = [ - "\n", - "

Lateral maneuver report

\n", - f"

{platform}

\n", - f"

{route}

\n", - f"

{ID.gitCommit}, {ID.gitBranch}, {ID.gitRemote}

\n", - ] - if _description is not None: - builder.append(f"

Description: {_description}

\n") - builder.append(f"

CarParams

{format_car_params(CP)}
\n") - builder.append('{ summary }') # to be replaced below for description, runs in maneuvers: # filter incomplete runs completed_runs = [msgs for msgs in runs @@ -202,14 +184,7 @@ def report(platform, route, _description, CP, ID, maneuvers): table.append(l) summary.append(tabulate(table, headers=cols, tablefmt='html', numalign='left') + '\n') - sum_idx = builder.index('{ summary }') - builder[sum_idx:sum_idx + 1] = summary - - with open(output_fn, "w") as f: - f.write(''.join(builder)) - - print(f"\nOpening report: {output_fn}\n") - webbrowser.open_new_tab(str(output_fn)) + write_report("lateral_reports", platform, route, builder, summary) if __name__ == '__main__': @@ -219,33 +194,7 @@ def report(platform, route, _description, CP, ID, maneuvers): args = parser.parse_args() - if '/' in args.route or '|' in args.route: - lr = LogReader(args.route, only_union_types=True) - else: - segs = [seg for seg in os.listdir(Paths.log_root()) if args.route in seg] - lr = LogReader([os.path.join(Paths.log_root(), seg, 'rlog.zst') for seg in segs], only_union_types=True) - - CP = lr.first('carParams') - ID = lr.first('initData') - platform = CP.carFingerprint - print('processing report for', platform) - - maneuvers: list[tuple[str, list[list]]] = [] - active_prev = False - description_prev = None - - for msg in lr: - if msg.which() == 'alertDebug': - active = 'Active' in msg.alertDebug.alertText1 or msg.alertDebug.alertText1 == 'Complete' - if active and not active_prev: - if msg.alertDebug.alertText2 == description_prev: - maneuvers[-1][1].append([]) - else: - maneuvers.append((msg.alertDebug.alertText2, [[]])) - description_prev = maneuvers[-1][0] - active_prev = active - - if active_prev: - maneuvers[-1][1][-1].append(msg) - + platform, CP, ID, maneuvers = load_maneuver_route( + args.route, lambda text: 'Active' in text or text == 'Complete', only_union_types=True, + ) report(platform, args.route, args.description, CP, ID, maneuvers) diff --git a/tools/longitudinal_maneuvers/generate_report.py b/selfdrive/tuning/generate_longitudinal_report.py similarity index 73% rename from tools/longitudinal_maneuvers/generate_report.py rename to selfdrive/tuning/generate_longitudinal_report.py index 32bdb5b1c4cb09..fdec7e501d3a4f 100755 --- a/tools/longitudinal_maneuvers/generate_report.py +++ b/selfdrive/tuning/generate_longitudinal_report.py @@ -2,40 +2,18 @@ import argparse import base64 import io -import os import math -import pprint -import webbrowser from collections import defaultdict -from pathlib import Path import matplotlib.pyplot as plt from openpilot.common.utils import tabulate -from openpilot.tools.lib.logreader import LogReader -from openpilot.system.hardware.hw import Paths - - -def format_car_params(CP): - return pprint.pformat({k: v for k, v in CP.to_dict().items() if not k.endswith('DEPRECATED')}, indent=2) +from openpilot.selfdrive.tuning.maneuver_helpers import init_report_builder, load_maneuver_route, write_report def report(platform, route, _description, CP, ID, maneuvers): - output_path = Path(__file__).resolve().parent / "longitudinal_reports" - output_fn = output_path / f"{platform}_{route.replace('/', '_')}.html" - output_path.mkdir(exist_ok=True) target_cross_times = defaultdict(list) + builder = init_report_builder("Longitudinal maneuver report", platform, route, _description, CP, ID) - builder = [ - "\n", - "

Longitudinal maneuver report

\n", - f"

{platform}

\n", - f"

{route}

\n", - f"

{ID.gitCommit}, {ID.gitBranch}, {ID.gitRemote}

\n", - ] - if _description is not None: - builder.append(f"

Description: {_description}

\n") - builder.append(f"

CarParams

{format_car_params(CP)}
\n") - builder.append('{ summary }') # to be replaced below for description, runs in maneuvers: print(f'plotting maneuver: {description}, runs: {len(runs)}') builder.append("
\n") @@ -138,14 +116,7 @@ def report(platform, route, _description, CP, ID, maneuvers): table.append(l) summary.append(tabulate(table, headers=cols, tablefmt='html', numalign='left') + '\n') - sum_idx = builder.index('{ summary }') - builder[sum_idx:sum_idx + 1] = summary - - with open(output_fn, "w") as f: - f.write(''.join(builder)) - - print(f"\nOpening report: {output_fn}\n") - webbrowser.open_new_tab(str(output_fn)) + write_report("longitudinal_reports", platform, route, builder, summary) if __name__ == '__main__': @@ -155,33 +126,5 @@ def report(platform, route, _description, CP, ID, maneuvers): args = parser.parse_args() - if '/' in args.route or '|' in args.route: - lr = LogReader(args.route) - else: - segs = [seg for seg in os.listdir(Paths.log_root()) if args.route in seg] - lr = LogReader([os.path.join(Paths.log_root(), seg, 'rlog.zst') for seg in segs]) - - CP = lr.first('carParams') - ID = lr.first('initData') - platform = CP.carFingerprint - print('processing report for', platform) - - maneuvers: list[tuple[str, list[list]]] = [] - active_prev = False - description_prev = None - - for msg in lr: - if msg.which() == 'alertDebug': - active = 'Maneuver Active' in msg.alertDebug.alertText1 - if active and not active_prev: - if msg.alertDebug.alertText2 == description_prev: - maneuvers[-1][1].append([]) - else: - maneuvers.append((msg.alertDebug.alertText2, [[]])) - description_prev = maneuvers[-1][0] - active_prev = active - - if active_prev: - maneuvers[-1][1][-1].append(msg) - + platform, CP, ID, maneuvers = load_maneuver_route(args.route, lambda text: 'Maneuver Active' in text) report(platform, args.route, args.description, CP, ID, maneuvers) diff --git a/tools/joystick/joystick_control.py b/selfdrive/tuning/joystick_control.py similarity index 100% rename from tools/joystick/joystick_control.py rename to selfdrive/tuning/joystick_control.py diff --git a/tools/joystick/joystickd.py b/selfdrive/tuning/joystickd.py similarity index 100% rename from tools/joystick/joystickd.py rename to selfdrive/tuning/joystickd.py diff --git a/tools/lateral_maneuvers/lateral_maneuversd.py b/selfdrive/tuning/lateral_maneuversd.py similarity index 98% rename from tools/lateral_maneuvers/lateral_maneuversd.py rename to selfdrive/tuning/lateral_maneuversd.py index 1cc2d3560eafff..63db6c05ae3ba0 100755 --- a/tools/lateral_maneuvers/lateral_maneuversd.py +++ b/selfdrive/tuning/lateral_maneuversd.py @@ -8,7 +8,7 @@ from openpilot.common.params import Params from openpilot.common.swaglog import cloudlog from openpilot.selfdrive.controls.lib.drive_helpers import MIN_SPEED -from openpilot.tools.longitudinal_maneuvers.maneuversd import Action, Maneuver as _Maneuver +from openpilot.selfdrive.tuning.maneuversd import Action, Maneuver as _Maneuver # thresholds for starting maneuvers MAX_SPEED_DEV = 0.7 # deviation in m/s diff --git a/selfdrive/tuning/maneuver_helpers.py b/selfdrive/tuning/maneuver_helpers.py new file mode 100644 index 00000000000000..ade4fa68aab534 --- /dev/null +++ b/selfdrive/tuning/maneuver_helpers.py @@ -0,0 +1,94 @@ +import os +import pprint +import webbrowser +from collections.abc import Callable +from enum import IntEnum +from pathlib import Path + +from openpilot.tools.lib.logreader import LogReader +from openpilot.system.hardware.hw import Paths + + +class Axis(IntEnum): + TIME = 0 + EGO_POSITION = 1 + LEAD_DISTANCE= 2 + EGO_V = 3 + LEAD_V = 4 + EGO_A = 5 + D_REL = 6 + +axis_labels = {Axis.TIME: 'Time (s)', + Axis.EGO_POSITION: 'Ego position (m)', + Axis.LEAD_DISTANCE: 'Lead absolute position (m)', + Axis.EGO_V: 'Ego Velocity (m/s)', + Axis.LEAD_V: 'Lead Velocity (m/s)', + Axis.EGO_A: 'Ego acceleration (m/s^2)', + Axis.D_REL: 'Lead distance (m)'} + + +def format_car_params(CP): + return pprint.pformat({k: v for k, v in CP.to_dict().items() if not k.endswith('DEPRECATED')}, indent=2) + + +def load_maneuver_route(route: str, active_fn: Callable, only_union_types: bool = False): + if '/' in route or '|' in route: + lr = LogReader(route, only_union_types=only_union_types) + else: + segs = [seg for seg in os.listdir(Paths.log_root()) if route in seg] + lr = LogReader([os.path.join(Paths.log_root(), seg, 'rlog.zst') for seg in segs], only_union_types=only_union_types) + + CP = lr.first('carParams') + ID = lr.first('initData') + platform = CP.carFingerprint + print('processing report for', platform) + + maneuvers: list[tuple[str, list[list]]] = [] + active_prev = False + description_prev = None + + for msg in lr: + if msg.which() == 'alertDebug': + active = active_fn(msg.alertDebug.alertText1) + if active and not active_prev: + if msg.alertDebug.alertText2 == description_prev: + maneuvers[-1][1].append([]) + else: + maneuvers.append((msg.alertDebug.alertText2, [[]])) + description_prev = maneuvers[-1][0] + active_prev = active + + if active_prev: + maneuvers[-1][1][-1].append(msg) + + return platform, CP, ID, maneuvers + + +def init_report_builder(title: str, platform: str, route: str, _description, CP, ID) -> list[str]: + builder = [ + "\n", + f"

{title}

\n", + f"

{platform}

\n", + f"

{route}

\n", + f"

{ID.gitCommit}, {ID.gitBranch}, {ID.gitRemote}

\n", + ] + if _description is not None: + builder.append(f"

Description: {_description}

\n") + builder.append(f"

CarParams

{format_car_params(CP)}
\n") + builder.append('{ summary }') + return builder + + +def write_report(output_dir: str, platform: str, route: str, builder: list[str], summary: list[str]): + output_path = Path(__file__).resolve().parent / output_dir + output_fn = output_path / f"{platform}_{route.replace('/', '_')}.html" + output_path.mkdir(exist_ok=True) + + sum_idx = builder.index('{ summary }') + builder[sum_idx:sum_idx + 1] = summary + + with open(output_fn, "w") as f: + f.write(''.join(builder)) + + print(f"\nOpening report: {output_fn}\n") + webbrowser.open_new_tab(str(output_fn)) diff --git a/tools/longitudinal_maneuvers/maneuversd.py b/selfdrive/tuning/maneuversd.py similarity index 100% rename from tools/longitudinal_maneuvers/maneuversd.py rename to selfdrive/tuning/maneuversd.py diff --git a/tools/longitudinal_maneuvers/mpc_longitudinal_tuning_report.py b/selfdrive/tuning/mpc_longitudinal_tuning_report.py similarity index 99% rename from tools/longitudinal_maneuvers/mpc_longitudinal_tuning_report.py rename to selfdrive/tuning/mpc_longitudinal_tuning_report.py index ae3fee7355af64..c77d4dddd5c58a 100644 --- a/tools/longitudinal_maneuvers/mpc_longitudinal_tuning_report.py +++ b/selfdrive/tuning/mpc_longitudinal_tuning_report.py @@ -5,7 +5,7 @@ import matplotlib.pyplot as plt from openpilot.common.realtime import DT_MDL from openpilot.selfdrive.controls.tests.test_following_distance import desired_follow_distance -from openpilot.tools.longitudinal_maneuvers.maneuver_helpers import Axis, axis_labels +from openpilot.selfdrive.tuning.maneuver_helpers import Axis, axis_labels from openpilot.selfdrive.test.longitudinal_maneuvers.maneuver import Maneuver diff --git a/system/manager/process_config.py b/system/manager/process_config.py index a442e4f860db7a..6fe31366ba3ebe 100644 --- a/system/manager/process_config.py +++ b/system/manager/process_config.py @@ -96,7 +96,7 @@ def not_(*fns): PythonProcess("calibrationd", "selfdrive.locationd.calibrationd", only_onroad), PythonProcess("torqued", "selfdrive.locationd.torqued", only_onroad), PythonProcess("controlsd", "selfdrive.controls.controlsd", and_(not_joystick, iscar)), - PythonProcess("joystickd", "tools.joystick.joystickd", or_(joystick, notcar)), + PythonProcess("joystickd", "selfdrive.tuning.joystickd", or_(joystick, notcar)), PythonProcess("selfdrived", "selfdrive.selfdrived.selfdrived", only_onroad), PythonProcess("card", "selfdrive.car.card", only_onroad), PythonProcess("deleter", "system.loggerd.deleter", always_run), @@ -108,8 +108,8 @@ def not_(*fns): PythonProcess("ubloxd", "system.ubloxd.ubloxd", ublox, enabled=TICI), PythonProcess("pigeond", "system.ubloxd.pigeond", ublox, enabled=TICI), PythonProcess("plannerd", "selfdrive.controls.plannerd", not_long_maneuver), - PythonProcess("maneuversd", "tools.longitudinal_maneuvers.maneuversd", long_maneuver), - PythonProcess("lateral_maneuversd", "tools.lateral_maneuvers.lateral_maneuversd", lat_maneuver), + PythonProcess("maneuversd", "selfdrive.tuning.maneuversd", long_maneuver), + PythonProcess("lateral_maneuversd", "selfdrive.tuning.lateral_maneuversd", lat_maneuver), PythonProcess("radard", "selfdrive.controls.radard", only_onroad), PythonProcess("hardwared", "system.hardware.hardwared", always_run), PythonProcess("modem", "system.hardware.tici.modem", always_run, enabled=TICI), @@ -122,7 +122,7 @@ def not_(*fns): # debug procs NativeProcess("bridge", "cereal/messaging", ["./bridge"], notcar), PythonProcess("webrtcd", "system.webrtc.webrtcd", or_(and_(livestream, not_(iscar)), notcar)), - PythonProcess("joystick", "tools.joystick.joystick_control", and_(joystick, iscar)), + PythonProcess("joystick", "selfdrive.tuning.joystick_control", and_(joystick, iscar)), ] managed_processes = {p.name: p for p in procs} diff --git a/tools/README.md b/tools/README.md index 1ea42bbe1d129f..abc3321e46e3ca 100644 --- a/tools/README.md +++ b/tools/README.md @@ -48,7 +48,6 @@ Learn about the openpilot ecosystem and tools by playing our [CTF](/tools/CTF.md ``` ├── cabana/ # View and plot CAN messages from drives or in realtime ├── camerastream/ # Cameras stream over the network -├── joystick/ # Control your car with a joystick ├── lib/ # Libraries to support the tools and reading openpilot logs ├── plotjuggler/ # A tool to plot openpilot logs ├── replay/ # Replay drives and mock openpilot services diff --git a/tools/joystick/README.md b/tools/joystick/README.md deleted file mode 100644 index eb67060ca85b06..00000000000000 --- a/tools/joystick/README.md +++ /dev/null @@ -1,53 +0,0 @@ -# Joystick - -**Hardware needed**: device running openpilot, laptop, joystick (optional) - -With joystick_control, you can connect your laptop to your comma device over the network and debug controls using a joystick or keyboard. -joystick_control uses [inputs](https://pypi.org/project/inputs) which supports many common gamepads and joysticks. - -## Usage - -The car must be off, and openpilot must be offroad before starting `joystick_control`. - -### Using a keyboard - -SSH into your comma device and start joystick_control with the following command: - -```shell -tools/joystick/joystick_control.py --keyboard -``` - -The available buttons and axes will print showing their key mappings. In general, the WASD keys control gas and brakes and steering torque in 5% increments. - -### Joystick on your comma three - -Plug the joystick into your comma three aux USB-C port. Then, SSH into the device and start `joystick_control.py`. - -### Joystick on your laptop - -In order to use a joystick over the network, we need to run joystick_control locally from your laptop and have it send `testJoystick` packets over the network to the comma device. - -1. Connect a joystick to your PC. -2. Connect your laptop to your comma device's hotspot and open a new SSH shell. Since joystick_control is being run on your laptop, we need to write a parameter to let controlsd know to start in joystick debug mode: - ```shell - # on your comma device - echo -n "1" > /data/params/d/JoystickDebugMode - ``` -3. Run bridge with your laptop's IP address. This republishes the `testJoystick` packets sent from your laptop so that openpilot can receive them: - ```shell - # on your comma device - cereal/messaging/bridge {LAPTOP_IP} testJoystick - ``` -4. Start joystick_control on your laptop in ZMQ mode. - ```shell - # on your laptop - export ZMQ=1 - tools/joystick/joystick_control.py - ``` - ---- -Now start your car and openpilot should go into joystick mode with an alert on startup! The status of the axes will display on the alert, while button statuses print in the shell. - -Make sure the conditions are met in the panda to allow controls (e.g. cruise control engaged). You can also make a modification to the panda code to always allow controls. - -![](https://github.com/commaai/openpilot/assets/8762862/e640cbca-cb7a-4dcb-abce-b23b036ad8e7) diff --git a/tools/lateral_maneuvers/.gitignore b/tools/lateral_maneuvers/.gitignore deleted file mode 100644 index a0b6efe6b3f9a1..00000000000000 --- a/tools/lateral_maneuvers/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/lateral_reports/ diff --git a/tools/lateral_maneuvers/README.md b/tools/lateral_maneuvers/README.md deleted file mode 100644 index 3a54bc74099bef..00000000000000 --- a/tools/lateral_maneuvers/README.md +++ /dev/null @@ -1,42 +0,0 @@ -# Lateral Maneuvers Testing Tool - -> [!WARNING] -> Use caution when using this tool. - -Test your vehicle's lateral control tuning with this tool. The tool will test the vehicle's ability to follow a few lateral maneuvers and includes a tool to generate a report from the route. - -## Instructions - -1. Check out a development branch such as `master` on your comma device. -2. The full maneuver suite runs at 20 and 30 mph. -3. Enable "Lateral Maneuver Mode" in Settings > Developer on the device while offroad. Alternatively, set the parameter manually: - - ```sh - echo -n 1 > /data/params/d/LateralManeuverMode - ``` - -4. Turn your vehicle back on. You will see "Lateral Maneuver Mode". - -5. Ensure the area ahead is clear, as openpilot will command lateral acceleration steps in this mode. Once you are ready, set ACC manually to the target speed shown on screen and let openpilot stabilize lateral. After 1 seconds of steady straight driving, the maneuver will begin automatically. openpilot lateral control stays engaged between maneuvers normally while waiting for the next maneuver's readiness conditions. The maneuver will be aborted and repeated if speed is out of range, steering is touched or openpilot disengages. - -6. When the testing is complete, you'll see an alert that says "Maneuvers Finished." Complete the route by pulling over and turning off the vehicle. - -7. Visit https://connect.comma.ai and locate the route(s). They will stand out with lots of orange intervals in their timeline. Ensure "All logs" show as "uploaded." - - ![image](https://github.com/user-attachments/assets/cfe4c6d9-752f-4b24-b421-4b90a01933dc) - -8. Gather the route ID and then run the report generator. The file will be exported to the same directory: - - ```sh - $ python tools/lateral_maneuvers/generate_report.py 98395b7c5b27882e/000001cc--5a73bde686 - - processing report for KIA_EV6 - plotting maneuver: step right 20mph, runs: 3 - plotting maneuver: step left 20mph, runs: 3 - plotting maneuver: sine 0.5Hz 20mph, runs: 3 - plotting maneuver: step right 30mph, runs: 3 - - Opening report: /home/batman/openpilot/tools/lateral_maneuvers/lateral_reports/KIA_EV6_98395b7c5b27882e_000001cc--5a73bde686.html - ``` - -You can reach out on [Discord](https://discord.comma.ai) if you have any questions about these instructions or the tool itself. diff --git a/tools/longitudinal_maneuvers/README.md b/tools/longitudinal_maneuvers/README.md deleted file mode 100644 index 643af7fd826e18..00000000000000 --- a/tools/longitudinal_maneuvers/README.md +++ /dev/null @@ -1,48 +0,0 @@ -# Longitudinal Maneuvers Testing Tool - -Test your vehicle's longitudinal control tuning with this tool. The tool will test the vehicle's ability to follow a few longitudinal maneuvers and includes a tool to generate a report from the route. - -
Sample snapshot of a report.
- -## Instructions - -1. Check out a development branch such as `master` on your comma device. -2. Locate either a large empty parking lot or road devoid of any car or foot traffic. Flat, straight road is preferred. The full maneuver suite can take 1 mile or more if left running, however it is recommended to disengage openpilot between maneuvers and turn around if there is not enough space. -3. Turn off the vehicle and set this parameter which will signal to openpilot to start the longitudinal maneuver daemon: - - ```sh - echo -n 1 > /data/params/d/LongitudinalManeuverMode - ``` - -4. Turn your vehicle back on. You will see the "Longitudinal Maneuver Mode" alert: - - ![videoframe_6652](https://github.com/user-attachments/assets/e9d4c95a-cd76-4ab7-933e-19937792fa0f) - -5. Ensure the road ahead is clear, as openpilot will not brake for any obstructions in this mode. Once you are ready, press "Set" on your steering wheel to start the tests. The tests will run for about 4 minutes. If you need to pause the tests, press "Cancel" on your steering wheel. You can resume the tests by pressing "Resume" on your steering wheel. - - **Note:** For GM cars, it is recommended to hold down the resume button for all low-speed tests (starting, stopping and creep) to avoid the car entering standstill. - - ![cog-clip-00 01 11 250-00 01 22 250](https://github.com/user-attachments/assets/c312c1cc-76e8-46e1-a05e-bb9dfb58994f) - -6. When the testing is complete, you'll see an alert that says "Maneuvers Finished." Complete the route by pulling over and turning off the vehicle. - - ![fin2](https://github.com/user-attachments/assets/c06960ae-7cfb-44af-beaa-4dc28848e49d) - -7. Visit https://connect.comma.ai and locate the route(s). They will stand out with lots of orange intervals in their timeline. Ensure "All logs" show as "uploaded." - - ![image](https://github.com/user-attachments/assets/cfe4c6d9-752f-4b24-b421-4b90a01933dc) - -8. Gather the route ID and then run the report generator. The file will be exported to the same directory: - - ```sh - $ python tools/longitudinal_maneuvers/generate_report.py 57048cfce01d9625/0000010e--5b26bc3be7 'pcm accel compensation' - - processing report for LEXUS_ES_TSS2 - plotting maneuver: start from stop, runs: 4 - plotting maneuver: creep: alternate between +1m/s^2 and -1m/s^2, runs: 2 - plotting maneuver: gas step response: +1m/s^2 from 20mph, runs: 2 - - Report written to /home/batman/openpilot/tools/longitudinal_maneuvers/longitudinal_reports/LEXUS_ES_TSS2_57048cfce01d9625_0000010e--5b26bc3be7.html - ``` - -You can reach out on [Discord](https://discord.comma.ai) if you have any questions about these instructions or the tool itself. diff --git a/tools/longitudinal_maneuvers/maneuver_helpers.py b/tools/longitudinal_maneuvers/maneuver_helpers.py deleted file mode 100644 index 9fc65fb9e679b5..00000000000000 --- a/tools/longitudinal_maneuvers/maneuver_helpers.py +++ /dev/null @@ -1,18 +0,0 @@ -from enum import IntEnum - -class Axis(IntEnum): - TIME = 0 - EGO_POSITION = 1 - LEAD_DISTANCE= 2 - EGO_V = 3 - LEAD_V = 4 - EGO_A = 5 - D_REL = 6 - -axis_labels = {Axis.TIME: 'Time (s)', - Axis.EGO_POSITION: 'Ego position (m)', - Axis.LEAD_DISTANCE: 'Lead absolute position (m)', - Axis.EGO_V: 'Ego Velocity (m/s)', - Axis.LEAD_V: 'Lead Velocity (m/s)', - Axis.EGO_A: 'Ego acceleration (m/s^2)', - Axis.D_REL: 'Lead distance (m)'}