openpilot v0.11.1 release
date: 2026-06-04T09:49:56 master commit: c0ab3550eca2e9daf197c46b7e4b24aa9637cf2e
This commit is contained in:
Executable
+42
@@ -0,0 +1,42 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Forward all openpilot service ports
|
||||
while IFS=' ' read -r name port; do
|
||||
adb forward "tcp:${port}" "tcp:${port}" > /dev/null
|
||||
done < <(python3 - <<'PY'
|
||||
from cereal.services import SERVICE_LIST
|
||||
|
||||
FNV_PRIME = 0x100000001b3
|
||||
FNV_OFFSET_BASIS = 0xcbf29ce484222325
|
||||
START_PORT = 8023
|
||||
MAX_PORT = 65535
|
||||
PORT_RANGE = MAX_PORT - START_PORT
|
||||
MASK = 0xffffffffffffffff
|
||||
|
||||
def fnv1a(endpoint: str) -> int:
|
||||
h = FNV_OFFSET_BASIS
|
||||
for b in endpoint.encode():
|
||||
h ^= b
|
||||
h = (h * FNV_PRIME) & MASK
|
||||
return h
|
||||
|
||||
ports = set()
|
||||
for name in SERVICE_LIST.keys():
|
||||
port = START_PORT + fnv1a(name) % PORT_RANGE
|
||||
ports.add((name, port))
|
||||
|
||||
for name, port in sorted(ports):
|
||||
print(f"{name} {port}")
|
||||
PY
|
||||
)
|
||||
|
||||
# Forward SSH port, finding a free local port if 2222 is taken.
|
||||
SSH_PORT=2222
|
||||
while ss -tln | grep -q ":${SSH_PORT} "; do
|
||||
SSH_PORT=$((SSH_PORT + 1))
|
||||
done
|
||||
adb forward tcp:${SSH_PORT} tcp:22
|
||||
|
||||
# SSH!
|
||||
ssh comma@localhost -p ${SSH_PORT} "$@"
|
||||
Executable
+93
@@ -0,0 +1,93 @@
|
||||
#!/usr/bin/env python3
|
||||
import argparse
|
||||
import shlex
|
||||
import subprocess
|
||||
import threading
|
||||
import time
|
||||
|
||||
from watchdog.events import FileSystemEventHandler
|
||||
from watchdog.observers import Observer
|
||||
|
||||
from openpilot.common.basedir import BASEDIR
|
||||
|
||||
|
||||
def build_rsync_cmd(args) -> list[str]:
|
||||
ssh = [
|
||||
"ssh",
|
||||
"-o", "ControlMaster=auto",
|
||||
"-o", f"ControlPath=/tmp/devsync-{args.ip}.ctl",
|
||||
"-o", "ControlPersist=10m",
|
||||
"-o", "StrictHostKeyChecking=accept-new",
|
||||
]
|
||||
if args.identity:
|
||||
ssh += ["-i", args.identity]
|
||||
|
||||
return [
|
||||
"rsync", "-az",
|
||||
"--files-from=-", "--from0",
|
||||
"-e", " ".join(shlex.quote(p) for p in ssh),
|
||||
"--out-format=%n",
|
||||
BASEDIR + "/", f"comma@{args.ip}:{args.remote}/",
|
||||
]
|
||||
|
||||
|
||||
def git_tracked_files() -> bytes:
|
||||
return subprocess.check_output(
|
||||
["git", "-C", BASEDIR, "ls-files", "--recurse-submodules", "-z"]
|
||||
)
|
||||
|
||||
|
||||
class Handler(FileSystemEventHandler):
|
||||
def __init__(self, sync_fn):
|
||||
self.dirty = threading.Event()
|
||||
self.sync_fn = sync_fn
|
||||
|
||||
def on_any_event(self, event):
|
||||
if not event.is_directory:
|
||||
self.dirty.set()
|
||||
|
||||
def run(self):
|
||||
while True:
|
||||
time.sleep(1)
|
||||
if self.dirty.is_set():
|
||||
self.dirty.clear()
|
||||
self.sync_fn()
|
||||
|
||||
|
||||
def main():
|
||||
p = argparse.ArgumentParser()
|
||||
p.add_argument("ip", help="device IP / hostname")
|
||||
p.add_argument("--remote", default="/data/openpilot", help="remote path on device")
|
||||
p.add_argument("-i", "--identity", default=None, help="ssh identity file")
|
||||
args = p.parse_args()
|
||||
|
||||
print(f"[devsync] watching {BASEDIR}")
|
||||
print(f"[devsync] target comma@{args.ip}:{args.remote}")
|
||||
|
||||
def run_sync():
|
||||
file_list = git_tracked_files()
|
||||
cmd = build_rsync_cmd(args)
|
||||
t0 = time.monotonic()
|
||||
r = subprocess.run(cmd, input=file_list, capture_output=True)
|
||||
dt = time.monotonic() - t0
|
||||
if r.returncode:
|
||||
print(f"[devsync] ERR rc={r.returncode} in {dt:.2f}s")
|
||||
return
|
||||
files = [ln for ln in r.stdout.decode().splitlines() if ln.strip()]
|
||||
msg = f"{len(files)} files: {', '.join(files)}" if files else "no changes"
|
||||
print(f"[devsync] {dt:.2f}s · {msg}")
|
||||
|
||||
run_sync()
|
||||
|
||||
handler = Handler(run_sync)
|
||||
obs = Observer()
|
||||
obs.schedule(handler, BASEDIR, recursive=True)
|
||||
obs.start()
|
||||
handler.run()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
main()
|
||||
except KeyboardInterrupt:
|
||||
print("\n[devsync] stopping")
|
||||
Executable
+77
@@ -0,0 +1,77 @@
|
||||
#!/usr/bin/env python3
|
||||
import os
|
||||
import sys
|
||||
import wave
|
||||
import argparse
|
||||
import numpy as np
|
||||
|
||||
from openpilot.tools.lib.logreader import LogReader, ReadMode
|
||||
|
||||
|
||||
def extract_audio(route_or_segment_name, output_file=None, play=False):
|
||||
lr = LogReader(route_or_segment_name, default_mode=ReadMode.AUTO_INTERACTIVE)
|
||||
audio_messages = list(lr.filter("rawAudioData"))
|
||||
if not audio_messages:
|
||||
print("No rawAudioData messages found in logs")
|
||||
return
|
||||
sample_rate = audio_messages[0].sampleRate
|
||||
|
||||
audio_chunks = []
|
||||
total_frames = 0
|
||||
for msg in audio_messages:
|
||||
audio_array = np.frombuffer(msg.data, dtype=np.int16)
|
||||
audio_chunks.append(audio_array)
|
||||
total_frames += len(audio_array)
|
||||
full_audio = np.concatenate(audio_chunks)
|
||||
|
||||
print(f"Found {total_frames} frames from {len(audio_messages)} audio messages at {sample_rate} Hz")
|
||||
|
||||
if output_file:
|
||||
if write_wav_file(output_file, full_audio, sample_rate):
|
||||
print(f"Audio written to {output_file}")
|
||||
else:
|
||||
print("Audio extraction canceled.")
|
||||
if play:
|
||||
play_audio(full_audio, sample_rate)
|
||||
|
||||
|
||||
def write_wav_file(filename, audio_data, sample_rate):
|
||||
if os.path.exists(filename):
|
||||
if input(f"File '{filename}' exists. Overwrite? (y/N): ").lower() not in ['y', 'yes']:
|
||||
return False
|
||||
|
||||
with wave.open(filename, 'wb') as wav_file:
|
||||
wav_file.setnchannels(1) # Mono
|
||||
wav_file.setsampwidth(2) # 16-bit
|
||||
wav_file.setframerate(sample_rate)
|
||||
wav_file.writeframes(audio_data.tobytes())
|
||||
return True
|
||||
|
||||
|
||||
def play_audio(audio_data, sample_rate):
|
||||
try:
|
||||
import sounddevice as sd
|
||||
|
||||
print("Playing audio... Press Ctrl+C to stop")
|
||||
sd.play(audio_data, sample_rate)
|
||||
sd.wait()
|
||||
except KeyboardInterrupt:
|
||||
print("\nPlayback stopped")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description="Extract audio data from openpilot logs")
|
||||
parser.add_argument("-o", "--output", help="Output WAV file path")
|
||||
parser.add_argument("--play", action="store_true", help="Play audio with sounddevice")
|
||||
parser.add_argument("route_or_segment_name", nargs='?', help="The route or segment name")
|
||||
|
||||
if len(sys.argv) == 1:
|
||||
parser.print_help()
|
||||
sys.exit()
|
||||
args = parser.parse_args()
|
||||
|
||||
output_file = args.output
|
||||
if not args.output and not args.play:
|
||||
output_file = "extracted_audio.wav"
|
||||
|
||||
extract_audio(args.route_or_segment_name.strip(), output_file, args.play)
|
||||
Executable
+43
@@ -0,0 +1,43 @@
|
||||
#!/usr/bin/env python3
|
||||
import sys
|
||||
|
||||
if len(sys.argv) < 4:
|
||||
print(f"{sys.argv[0]} <route> <segment> <frame number> [front|wide|driver]")
|
||||
print('example: ./fetch_image_from_route.py "02c45f73a2e5c6e9|2020-06-01--18-03-08" 3 500 driver')
|
||||
exit(0)
|
||||
|
||||
cameras = {
|
||||
"front": "cameras",
|
||||
"wide": "ecameras",
|
||||
"driver": "dcameras"
|
||||
}
|
||||
|
||||
import requests
|
||||
from PIL import Image
|
||||
from openpilot.tools.lib.auth_config import get_token
|
||||
from openpilot.tools.lib.framereader import FrameReader
|
||||
|
||||
jwt = get_token()
|
||||
|
||||
route = sys.argv[1]
|
||||
segment = int(sys.argv[2])
|
||||
frame = int(sys.argv[3])
|
||||
camera = cameras[sys.argv[4]] if len(sys.argv) > 4 and sys.argv[4] in cameras else "cameras"
|
||||
|
||||
url = f'https://api.commadotai.com/v1/route/{route}/files'
|
||||
r = requests.get(url, headers={"Authorization": f"JWT {jwt}"}, timeout=10)
|
||||
assert r.status_code == 200
|
||||
print("got api response")
|
||||
|
||||
segments = r.json()[camera]
|
||||
if segment >= len(segments):
|
||||
raise Exception(f"segment {segment} not found, got {len(segments)} segments")
|
||||
|
||||
fr = FrameReader(segments[segment])
|
||||
if frame >= fr.frame_count:
|
||||
raise Exception("frame {frame} not found, got {fr.frame_count} frames")
|
||||
|
||||
im = Image.fromarray(fr.get(frame))
|
||||
fn = f"uxxx_{route.replace('|', '_')}_{segment}_{frame}.png"
|
||||
im.save(fn)
|
||||
print(f"saved {fn}")
|
||||
Executable
+47
@@ -0,0 +1,47 @@
|
||||
#!/usr/bin/env python3
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
from openpilot.common.basedir import BASEDIR
|
||||
from openpilot.tools.lib.logreader import LogReader
|
||||
|
||||
os.environ['BASEDIR'] = BASEDIR
|
||||
|
||||
|
||||
def get_arg_parser():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Unlogging and save to file",
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
|
||||
|
||||
parser.add_argument("route", type=(lambda x: x.replace("#", "|")), nargs="?",
|
||||
help="The route whose messages will be published.")
|
||||
parser.add_argument("--out_path", nargs='?', default='/data/ubloxRaw.stream',
|
||||
help="Output pickle file path")
|
||||
return parser
|
||||
|
||||
|
||||
def main():
|
||||
args = get_arg_parser().parse_args(sys.argv[1:])
|
||||
|
||||
lr = LogReader(args.route)
|
||||
|
||||
with open(args.out_path, 'wb') as f:
|
||||
try:
|
||||
done = False
|
||||
i = 0
|
||||
while not done:
|
||||
msg = next(lr)
|
||||
if not msg:
|
||||
break
|
||||
smsg = msg.as_builder()
|
||||
typ = smsg.which()
|
||||
if typ == 'ubloxRaw':
|
||||
f.write(smsg.to_bytes())
|
||||
i += 1
|
||||
except StopIteration:
|
||||
print('All done')
|
||||
print(f'Writed {i} msgs')
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Executable
+8
@@ -0,0 +1,8 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
while true; do
|
||||
if ls /dev/serial/by-id/usb-FTDI_FT230X* 2> /dev/null; then
|
||||
sudo screen /dev/serial/by-id/usb-FTDI_FT230X* 115200
|
||||
fi
|
||||
sleep 0.005
|
||||
done
|
||||
Executable
+23
@@ -0,0 +1,23 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import requests
|
||||
from openpilot.common.params import Params
|
||||
import sys
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) < 2:
|
||||
print(f"{sys.argv[0]} <github username>")
|
||||
exit(1)
|
||||
|
||||
username = sys.argv[1]
|
||||
keys = requests.get(f"https://github.com/{username}.keys", timeout=10)
|
||||
|
||||
if keys.status_code == 200:
|
||||
params = Params()
|
||||
params.put_bool("SshEnabled", True, block=True)
|
||||
params.put("GithubSshKeys", keys.text, block=True)
|
||||
params.put("GithubUsername", username, block=True)
|
||||
print("Set up ssh keys successfully")
|
||||
else:
|
||||
print("Error getting public keys from github")
|
||||
Executable
+57
@@ -0,0 +1,57 @@
|
||||
#!/usr/bin/env python3
|
||||
import os
|
||||
import sys
|
||||
import argparse
|
||||
import re
|
||||
|
||||
from openpilot.common.basedir import BASEDIR
|
||||
from openpilot.tools.lib.auth_config import get_token
|
||||
from openpilot.tools.lib.api import CommaApi
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description="A helper for connecting to devices over the comma prime SSH proxy.\
|
||||
Adding your SSH key to your SSH config is recommended for more convenient use; see https://docs.comma.ai/how-to/connect-to-comma/.")
|
||||
parser.add_argument("device", help="device name or dongle id")
|
||||
parser.add_argument("--host", help="ssh jump server host", default="ssh.comma.ai")
|
||||
parser.add_argument("--port", help="ssh jump server port", default=22, type=int)
|
||||
parser.add_argument("--key", help="ssh key", default=os.path.join(BASEDIR, "system/hardware/tici/id_rsa"))
|
||||
parser.add_argument("--debug", help="enable debug output", action="store_true")
|
||||
args = parser.parse_args()
|
||||
|
||||
r = CommaApi(get_token()).get("v1/me/devices")
|
||||
devices = {x['dongle_id']: x['alias'] for x in r}
|
||||
|
||||
if not re.match("[0-9a-zA-Z]{16}", args.device):
|
||||
user_input = args.device.replace(" ", "").lower()
|
||||
matches = { k: v for k, v in devices.items() if isinstance(v, str) and user_input in v.replace(" ", "").lower() }
|
||||
if len(matches) == 1:
|
||||
dongle_id = list(matches.keys())[0]
|
||||
else:
|
||||
print(f"failed to look up dongle id for \"{args.device}\"", file=sys.stderr)
|
||||
if len(matches) > 1:
|
||||
print("found multiple matches:", file=sys.stderr)
|
||||
for k, v in matches.items():
|
||||
print(f" \"{v}\" ({k})", file=sys.stderr)
|
||||
exit(1)
|
||||
else:
|
||||
dongle_id = args.device
|
||||
|
||||
name = dongle_id
|
||||
if dongle_id in devices:
|
||||
name = f"{devices[dongle_id]} ({dongle_id})"
|
||||
print(f"connecting to {name} through {args.host}:{args.port} ...")
|
||||
|
||||
command = [
|
||||
"ssh",
|
||||
"-i", args.key,
|
||||
"-o", f"ProxyCommand=ssh -i {args.key} -W %h:%p -p %p %h@{args.host}",
|
||||
"-p", str(args.port),
|
||||
]
|
||||
if args.debug:
|
||||
command += ["-v"]
|
||||
command += [
|
||||
f"comma@comma-{dongle_id}",
|
||||
]
|
||||
if args.debug:
|
||||
print(" ".join([f"'{c}'" if " " in c else c for c in command]))
|
||||
os.execvp(command[0], command)
|
||||
Reference in New Issue
Block a user