mirror of
https://github.com/sunnypilot/sunnypilot.git
synced 2026-08-24 00:03:45 +08:00
Compare commits
19 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4cb6cbebee | |||
| 75df5c1716 | |||
| 8d1a848545 | |||
| 75b4e61913 | |||
| c38b82fa9c | |||
| 72d2d5381d | |||
| 5ad091ad55 | |||
| 02c072b0a1 | |||
| a697daa669 | |||
| 13c8234c96 | |||
| 126ea769f8 | |||
| a4e8a4b67d | |||
| 70eed928e3 | |||
| 2f454eb107 | |||
| 6f009df11a | |||
| 73485b3b0f | |||
| f43fa96a8d | |||
| 9715151cc9 | |||
| b244f5d0ae |
@@ -132,6 +132,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
|
||||
{"UptimeOnroad", {PERSISTENT, FLOAT, "0.0"}},
|
||||
{"UsbGpuActive", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION | CLEAR_ON_IGNITION_ON, BOOL}},
|
||||
{"UsbGpuLoading", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION | CLEAR_ON_IGNITION_ON, BOOL}},
|
||||
{"UsbGpuLoadProgress", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION | CLEAR_ON_IGNITION_ON, INT, "0"}},
|
||||
{"Version", {PERSISTENT, STRING}},
|
||||
|
||||
// --- sunnypilot params --- //
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
import os
|
||||
from openpilot.common.file_chunker import open_file_chunked, get_existing_chunks
|
||||
from openpilot.common.params import Params
|
||||
|
||||
PARAM = "UsbGpuLoadProgress"
|
||||
|
||||
|
||||
class ProgressReader:
|
||||
def __init__(self, inner, total):
|
||||
self._inner = inner
|
||||
self._total = total
|
||||
self._params = Params()
|
||||
self._read = 0
|
||||
self._pct = -1
|
||||
self._step = max(64 * 1024, total // 100)
|
||||
|
||||
def _bump(self, n):
|
||||
self._read += n
|
||||
if self._total:
|
||||
pct = min(100, self._read * 100 // self._total)
|
||||
if pct != self._pct:
|
||||
self._pct = pct
|
||||
self._params.put(PARAM, pct)
|
||||
|
||||
def read(self, size=-1):
|
||||
data = self._inner.read(size)
|
||||
self._bump(len(data))
|
||||
return data
|
||||
|
||||
def readinto(self, b):
|
||||
view = memoryview(b)
|
||||
done = 0
|
||||
while done < len(view):
|
||||
n = self._inner.readinto(view[done:done + self._step])
|
||||
if not n:
|
||||
break
|
||||
done += n
|
||||
self._bump(n)
|
||||
return done
|
||||
|
||||
|
||||
def open_with_progress(pkl_path):
|
||||
total = sum(os.path.getsize(p) for p in get_existing_chunks(pkl_path))
|
||||
return ProgressReader(open_file_chunked(pkl_path), total)
|
||||
@@ -29,6 +29,7 @@ from openpilot.selfdrive.modeld.parse_model_outputs import Parser
|
||||
from openpilot.selfdrive.modeld.compile_modeld import make_input_queues, WARP_INPUTS, POLICY_INPUTS
|
||||
from openpilot.selfdrive.modeld.fill_model_msg import fill_model_msg, fill_driving_model_data, fill_pose_msg, PublishState
|
||||
from openpilot.common.file_chunker import open_file_chunked
|
||||
from openpilot.selfdrive.modeld.load_progress import open_with_progress
|
||||
from openpilot.selfdrive.modeld.constants import ModelConstants, Plan
|
||||
from openpilot.selfdrive.modeld.helpers import usbgpu_present, usbgpu_compiled, modeld_pkl_path, get_tg_input_devices, load_oob
|
||||
|
||||
@@ -145,7 +146,7 @@ class ModelState(ModelStateBase):
|
||||
ModelStateBase.__init__(self)
|
||||
input_devices = get_tg_input_devices(PROCESS_NAME, usbgpu)
|
||||
self.WARP_DEV, self.QUEUE_DEV = input_devices['WARP_DEV'], input_devices['QUEUE_DEV']
|
||||
jits = load_oob(open_file_chunked(modeld_pkl_path(usbgpu)))
|
||||
jits = load_oob(open_with_progress(modeld_pkl_path(usbgpu)) if usbgpu else open_file_chunked(modeld_pkl_path(usbgpu)))
|
||||
metadata = jits['metadata']
|
||||
self.input_shapes = metadata['input_shapes']
|
||||
self.vision_input_names = [k for k in self.input_shapes if 'img' in k]
|
||||
|
||||
@@ -223,7 +223,7 @@ class HudRenderer(Widget):
|
||||
if icon is not self._egpu_icon:
|
||||
self._egpu_fade_time = rl.get_time()
|
||||
self._egpu_icon = icon
|
||||
alpha = self._egpu_alpha_filter.update(loading or 0 < rl.get_time() - self._egpu_fade_time < SET_SPEED_PERSISTENCE)
|
||||
alpha = self._egpu_alpha_filter.update(True)
|
||||
if alpha < 1e-2:
|
||||
return
|
||||
|
||||
@@ -231,6 +231,18 @@ class HudRenderer(Widget):
|
||||
rect.y + rect.height - 14 - (self._txt_wheel.height + icon.height) / 2)
|
||||
rl.draw_texture_ex(icon, pos, 0.0, 1.0, rl.Color(255, 255, 255, int(255 * opacity * alpha)))
|
||||
|
||||
if loading:
|
||||
pct_text = f"{ui_state.usbgpu_load_progress}%"
|
||||
size = FONT_SIZES.max_speed
|
||||
cell = measure_text_cached(self._font_bold, "0", size)
|
||||
widths = [cell.x if c.isdigit() else measure_text_cached(self._font_bold, c, size).x for c in pct_text]
|
||||
x = pos.x - 8 - sum(widths)
|
||||
y = pos.y + (icon.height - cell.y) / 2
|
||||
for c, w in zip(pct_text, widths):
|
||||
glyph = measure_text_cached(self._font_bold, c, size).x
|
||||
rl.draw_text_ex(self._font_bold, c, rl.Vector2(x + (w - glyph) / 2, y), size, 0, rl.WHITE)
|
||||
x += w
|
||||
|
||||
def _draw_steering_wheel(self, rect: rl.Rectangle) -> None:
|
||||
wheel_txt = self._txt_wheel_critical if self._show_wheel_critical else self._txt_wheel
|
||||
|
||||
|
||||
@@ -86,6 +86,7 @@ class UIState(UIStateSP):
|
||||
self.usbgpu_compiled: bool = usbgpu_compiled()
|
||||
self.usbgpu_active: bool | None = self.params.get("UsbGpuActive")
|
||||
self.usbgpu_loading: bool = self.params.get_bool("UsbGpuLoading")
|
||||
self.usbgpu_load_progress: int = self.params.get("UsbGpuLoadProgress", return_default=True)
|
||||
self.started: bool = False
|
||||
self.ignition: bool = False
|
||||
self.recording_audio: bool = False
|
||||
@@ -164,6 +165,9 @@ class UIState(UIStateSP):
|
||||
# Update started state
|
||||
self.started = self.sm["deviceState"].started and self.ignition
|
||||
|
||||
if self.usbgpu_loading:
|
||||
self.usbgpu_load_progress = self.params.get("UsbGpuLoadProgress", return_default=True)
|
||||
|
||||
# Update body state
|
||||
if self.CP is not None and self.is_body != self.CP.notCar:
|
||||
self.is_body = self.CP.notCar
|
||||
|
||||
@@ -25,6 +25,7 @@ from opendbc.car.car_helpers import get_demo_car_params
|
||||
from tinygrad.tensor import Tensor
|
||||
|
||||
from openpilot.common.file_chunker import open_file_chunked
|
||||
from openpilot.selfdrive.modeld.load_progress import open_with_progress
|
||||
from openpilot.common.swaglog import cloudlog
|
||||
from openpilot.common.params import Params
|
||||
from openpilot.common.filter_simple import FirstOrderFilter
|
||||
@@ -107,7 +108,7 @@ class ModelState(ModelStateBase):
|
||||
|
||||
def _init_combined(self, pkl_path, cam_w, cam_h, bundle):
|
||||
cloudlog.warning(f"loading combined pkl: {pkl_path}")
|
||||
jits = load_oob(open_file_chunked(pkl_path))
|
||||
jits = load_oob(open_with_progress(pkl_path) if self.usbgpu else open_file_chunked(pkl_path))
|
||||
|
||||
self.WARP_DEV = 'QCOM' if COMMA_HARDWARE else 'CPU'
|
||||
self.DEV = 'AMD' if self.usbgpu else self.WARP_DEV
|
||||
|
||||
@@ -1,198 +0,0 @@
|
||||
# Live UI
|
||||
|
||||
View a comma device's live sunnypilot UI over Wi-Fi. Replay, ADB, and USB are
|
||||
not used.
|
||||
|
||||
| Mode | Output | Device bridge |
|
||||
| --- | --- | --- |
|
||||
| Mimic (default) | Local UI rendered from live device data | Required |
|
||||
| Exact (`--exact`) | Actual pixels shown on the device | Not required |
|
||||
|
||||
Mimic mode does not directly require SSH, but the device bridge must be started
|
||||
somehow; the examples use SSH. Exact mode, touch control, and `--stop-exact`
|
||||
require key-based SSH access.
|
||||
|
||||
Choose **mimic** when developing or inspecting the local UI with live device
|
||||
data. Choose **exact** when you need to see or control what is physically shown
|
||||
on the device.
|
||||
|
||||
## Before the first run
|
||||
|
||||
1. Replace `192.168.43.1` in the examples if your device uses a different IP.
|
||||
2. Connect the computer and device to networks that can reach each other.
|
||||
3. Run local commands from the sunnypilot repository root.
|
||||
4. For exact mode, verify non-interactive SSH access:
|
||||
|
||||
```bash
|
||||
ssh -o BatchMode=yes comma@192.168.43.1 true
|
||||
```
|
||||
|
||||
The script automatically switches to the repository's `.venv` when present.
|
||||
|
||||
## Mimic UI
|
||||
|
||||
Mimic mode receives live cereal messages and encoded road-camera frames, then
|
||||
renders a new UI instance on the computer. It resembles the device UI but is
|
||||
not a pixel-for-pixel screen mirror.
|
||||
|
||||
Build the messaging bridge once on the computer:
|
||||
|
||||
```bash
|
||||
scons -u cereal/messaging/bridge
|
||||
```
|
||||
|
||||
Use two terminals. In the first, start the bridge on the device and leave it
|
||||
running:
|
||||
|
||||
```bash
|
||||
ssh comma@192.168.43.1
|
||||
./cereal/messaging/bridge
|
||||
```
|
||||
|
||||
In the second terminal, start the local UI:
|
||||
|
||||
```bash
|
||||
BIG=0 ./openpilot/tools/live/ui.py 192.168.43.1
|
||||
```
|
||||
|
||||
`BIG` is inherited from your environment and is not forced by the tool.
|
||||
|
||||
## Exact device screen
|
||||
|
||||
Exact mode streams the actual device display. It does not need either
|
||||
messaging bridge:
|
||||
|
||||
```bash
|
||||
./openpilot/tools/live/ui.py --exact 192.168.43.1
|
||||
```
|
||||
|
||||
The tool runs a temporary capture helper through SSH using AGNOS's existing
|
||||
DRM broker. It does not install software or modify openpilot source on the
|
||||
device. Exact mode requires a compatible AGNOS DRM writeback connector and
|
||||
captures the post-processed display output when that mode is available.
|
||||
|
||||
Exact mode is experimental. Some Qualcomm/AGNOS combinations can time out
|
||||
during DRM writeback, freezing both the mirrored window and physical device UI.
|
||||
This can happen without `--control`; mouse, keyboard, and shortcut forwarding
|
||||
are not required to trigger the capture failure.
|
||||
The tool stops its verified helper when capture stalls, but the kernel display
|
||||
state may require a full device reboot before exact mode can be tried again.
|
||||
Restarting openpilot alone does not reset the display driver.
|
||||
|
||||
Set the capture rate from 1 to 60 FPS; the default is 15:
|
||||
|
||||
```bash
|
||||
./openpilot/tools/live/ui.py --exact --exact-fps 30 192.168.43.1
|
||||
./openpilot/tools/live/ui.py --exact --exact-fps 60 192.168.43.1
|
||||
```
|
||||
|
||||
Start with 15 or 30 FPS. Higher rates use more device CPU and Wi-Fi bandwidth.
|
||||
|
||||
Use `SCALE` to resize the local window:
|
||||
|
||||
```bash
|
||||
SCALE=2 ./openpilot/tools/live/ui.py --exact 192.168.43.1
|
||||
```
|
||||
|
||||
Only one exact session can run at a time.
|
||||
|
||||
## Touch control
|
||||
|
||||
Touch control is available only with exact mode:
|
||||
|
||||
```bash
|
||||
./openpilot/tools/live/ui.py --exact --control 192.168.43.1
|
||||
```
|
||||
|
||||
Left-clicks and drags are forwarded to the physical touchscreen over SSH.
|
||||
These inputs affect the real device, including settings and confirmation
|
||||
buttons.
|
||||
|
||||
When the device's on-screen keyboard is visible, type with the computer
|
||||
keyboard. Letters, numbers, symbols, Space, Backspace, and Return are converted
|
||||
to taps on the device keyboard.
|
||||
|
||||
Exact control also provides simulator-style shortcuts:
|
||||
|
||||
| Shortcut | Action |
|
||||
| --- | --- |
|
||||
| `↓` | Go back with a swipe-down gesture |
|
||||
| `←` | Swipe right |
|
||||
| `→` | Swipe left |
|
||||
| `Command`+`K` (macOS) or `Ctrl`+`K` (Linux/Windows) | Force keyboard forwarding on, or return to automatic detection |
|
||||
|
||||
Hold an arrow key to repeat its gesture until the key is released.
|
||||
|
||||
Escape has no action and does not close the program; use `Ctrl-C` or the window
|
||||
close button to exit. `Command`+`K`/`Ctrl`+`K` enables manual keyboard forwarding
|
||||
regardless of exact-screen resolution; touch locations scale to the captured
|
||||
screen. Automatic keyboard detection targets the 536x240 mici UI. Turn forced
|
||||
forwarding back off after typing so ordinary keys cannot produce unintended
|
||||
taps on incompatible keyboard layouts.
|
||||
|
||||
## Connection status
|
||||
|
||||
Status appears in the window title and terminal:
|
||||
|
||||
- `connected`: mimic telemetry and camera frames, or exact screen frames, are
|
||||
arriving.
|
||||
- `keyboard`: exact-mode keyboard forwarding is currently active. This appears
|
||||
only while the device keyboard is detected or forwarding is forced on.
|
||||
- `no camera`: mimic telemetry is arriving, but road-camera frames are not.
|
||||
- `disconnected`: expected mimic-mode live data has stopped.
|
||||
|
||||
At the default capture rate, exact mode reports a stopped screen stream in
|
||||
about one second. Lower `--exact-fps` values allow more time between frames.
|
||||
If Qualcomm DRM writeback stalls, exact mode immediately stops its verified
|
||||
device helper and exits to avoid leaving the physical display frozen.
|
||||
|
||||
## Stop and clean up
|
||||
|
||||
Press `Ctrl-C` or close the window. If an exact helper remains orphaned, run:
|
||||
|
||||
```bash
|
||||
./openpilot/tools/live/ui.py --stop-exact 192.168.43.1
|
||||
```
|
||||
|
||||
`--kill-exact` is an alias. Stop exact mode before restarting or rebuilding
|
||||
openpilot to avoid blocking the physical UI during a display transition.
|
||||
|
||||
## Options
|
||||
|
||||
```text
|
||||
--exact Show the actual device screen
|
||||
--control Forward mouse and keyboard input; requires --exact
|
||||
--exact-fps FPS Exact capture rate, 1-60 (default: 15)
|
||||
--stop-exact Stop orphaned exact helpers
|
||||
--kill-exact Alias for --stop-exact
|
||||
--ip ADDRESS Alternative to the positional address
|
||||
```
|
||||
|
||||
See the complete command help:
|
||||
|
||||
```bash
|
||||
./openpilot/tools/live/ui.py --help
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
- **`cereal/messaging/bridge` is not built:** Run:
|
||||
|
||||
```bash
|
||||
scons -u cereal/messaging/bridge
|
||||
```
|
||||
|
||||
- **Mimic is disconnected:** Confirm the device bridge is still running and
|
||||
the IP is reachable.
|
||||
- **Mimic reports no camera:** Confirm the device is publishing
|
||||
`roadEncodeData`. Camera display may also wait briefly for the next keyframe.
|
||||
- **Exact says another session is running:** Close the other exact window. If
|
||||
none is open, run `--stop-exact`.
|
||||
- **Exact cannot connect:** Recheck key-based SSH and the device IP using the
|
||||
command in "Before the first run."
|
||||
- **Exact stalls or the physical UI freezes:** Stop the exact window and run
|
||||
`--stop-exact`. If the device UI remains unstable or exact immediately stalls
|
||||
again, fully reboot the device; restarting openpilot is not sufficient. If
|
||||
the problem returns after reboot, use mimic mode because continuous DRM
|
||||
writeback is not reliable on that device/AGNOS combination. Removing
|
||||
`--control` does not address this capture-layer failure.
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user