Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
[submodule "teleoprtc_repo"]
path = teleoprtc_repo
url = ../../commaai/teleoprtc
branch = fix/datachannel-double-counting
[submodule "tinygrad"]
path = tinygrad_repo
url = https://github.com/tinygrad/tinygrad.git
65 changes: 65 additions & 0 deletions selfdrive/debug/bodyview.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#!/usr/bin/env python3
"""Launch the big UI (comma 3X) simulating a comma body."""
import os
import time
import threading

os.environ["BIG"] = "1"

import pyray as rl
from cereal import car, log, messaging
from openpilot.common.params import Params
from openpilot.system.ui.lib.application import gui_app

# Pin window to monitor 0 after init
_orig_init_window = gui_app.init_window
def _init_window_on_monitor0(*args, **kwargs):
_orig_init_window(*args, **kwargs)
pos = rl.get_monitor_position(1)
rl.set_window_position(int(pos.x), int(pos.y))
gui_app.init_window = _init_window_on_monitor0


def send_messages():
pm = messaging.PubMaster(['deviceState', 'pandaStates', 'carParams', 'carState'])

car_params_msg = messaging.new_message('carParams')
car_params_msg.carParams.brand = "body"
car_params_msg.carParams.notCar = True

device_state_msg = messaging.new_message('deviceState')
device_state_msg.deviceState.started = True

panda_msg = messaging.new_message('pandaStates', 1)
panda_msg.pandaStates[0].ignitionLine = True
panda_msg.pandaStates[0].pandaType = log.PandaState.PandaType.uno

car_state_msg = messaging.new_message('carState')
car_state_msg.carState.charging = True
car_state_msg.carState.fuelGauge = 0.80

while True:
pm.send('carParams', car_params_msg)
pm.send('deviceState', device_state_msg)
pm.send('pandaStates', panda_msg)
pm.send('carState', car_state_msg)
time.sleep(0.01)


def main():
# Set CarParamsPersistent so ui_state.CP.notCar is True on startup
params = Params()
CP = car.CarParams.new_message(notCar=True, brand="body", wheelbase=1, steerRatio=10)
params.put("CarParamsPersistent", CP.to_bytes())

# Start message sender in background
t = threading.Thread(target=send_messages, daemon=True)
t.start()

# Import after env is set so BIG_UI picks it up
from openpilot.selfdrive.ui.ui import main as ui_main
ui_main()


if __name__ == "__main__":
main()
2 changes: 1 addition & 1 deletion selfdrive/debug/uiview.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
msgs = {s: messaging.new_message(s) for s in ['controlsState', 'deviceState', 'carParams']}
msgs['deviceState'].deviceState.started = True
msgs['deviceState'].deviceState.deviceType = HARDWARE.get_device_type()
msgs['carParams'].carParams.openpilotLongitudinalControl = True
# msgs['carParams'].carParams.openpilotLongitudinalControl = True

msgs['pandaStates'] = messaging.new_message('pandaStates', 1)
msgs['pandaStates'].pandaStates[0].ignitionLine = True
Expand Down
Empty file.
149 changes: 149 additions & 0 deletions selfdrive/ui/layouts/body/animations.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
from dataclasses import dataclass
from enum import Enum


class AnimationMode(Enum):
ONCE_FORWARD = 1
ONCE_FORWARD_BACKWARD = 2
REPEAT_FORWARD = 3
REPEAT_FORWARD_BACKWARD = 4


@dataclass
class Animation:
frames: list[list[tuple[int, int]]]
starting_frames: list[list[tuple[int, int]]] | None = None # played once before the main loop
frame_duration: float = 0.15 # seconds each frame is shown
mode: AnimationMode = AnimationMode.REPEAT_FORWARD_BACKWARD
repeat_interval: float = 5.0 # seconds between animation restarts (only for REPEAT modes)
hold_end: float = 0.0 # seconds to hold the last frame before playing backward (only for *_BACKWARD modes)


def _mirror(dots: list[tuple[int, int]]) -> list[tuple[int, int]]:
"""Mirror a component from the left side of the face to the right"""
return [(r, 15 - c) for r, c in dots]


def _mirror_no_flip(dots: list[tuple[int, int]], ref: list[tuple[int, int]] | None = None) -> list[tuple[int, int]]:
"""Move a component to the symmetric position without flipping the shape.
ref: reference component defining the full bounding box (e.g. EYE_OPEN for any eye variant)"""
ref = ref if ref is not None else dots
shift = min(c for _, c in _mirror(ref)) - min(c for _, c in ref)
return [(r, c + shift) for r, c in dots]


def _shift_up(dots: list[tuple[int, int]], n: int = 1) -> list[tuple[int, int]]:
return [(r - n, c) for r, c in dots]


def _shift_down(dots: list[tuple[int, int]], n: int = 1) -> list[tuple[int, int]]:
return [(r + n, c) for r, c in dots]


def _shift_left(dots: list[tuple[int, int]], n: int = 1) -> list[tuple[int, int]]:
return [(r, c - n) for r, c in dots]


def _shift_right(dots: list[tuple[int, int]], n: int = 1) -> list[tuple[int, int]]:
return [(r, c + n) for r, c in dots]


def _make_frame(left_eye: list[tuple[int, int]], right_eye: list[tuple[int, int]],
left_brow: list[tuple[int, int]], right_brow: list[tuple[int, int]],
mouth: list[tuple[int, int]]) -> list[tuple[int, int]]:
return left_eye + left_brow + right_eye + right_brow + mouth


# Eyes (left side)
EYE_OPEN = [
(2, 2), (2, 3),
(3, 1), (3, 2), (3, 3), (3, 4),
(4, 1), (4, 2), (4, 3), (4, 4),
(5, 2), (5, 3)
]
EYE_HALF = [
(4, 1), (4, 2), (4, 3), (4, 4),
(5, 2), (5, 3)
]
EYE_CLOSED = [
(4, 1), (4, 4),
(5, 2), (5, 3),
]
EYE_LEFT_LOOK = [
(2, 2), (2, 3),
(3, 1), (3, 2),
(4, 1), (4, 2),
(5, 2), (5, 3),
]
EYE_RIGHT_LOOK = [
(2, 2), (2, 3),
(3, 3), (3, 4),
(4, 3), (4, 4),
(5, 2), (5, 3),
]

# Eyebrows (left side)
BROW_HIGH = [(1, 0), (0, 1), (0, 2)]
BROW_LOWERED = [(2, 0), (1, 1), (1, 2)]
BROW_STRAIGHT = [(2, 0), (2, 1), (2, 2)]
NO_BROW = []

# Mouths (centered, not mirrored)
MOUTH_SMILE = [(6, 6), (7, 7), (7, 8), (6, 9)]
MOUTH_NORMAL = [(7, 7), (7, 8)]
MOUTH_SAD = [(7, 6), (6, 7), (6, 8), (7, 9)]


# --- Animations ---

NORMAL = Animation(
frames=[
_make_frame(EYE_OPEN, _mirror(EYE_OPEN), BROW_HIGH, _mirror(BROW_HIGH), MOUTH_SMILE),
_make_frame(EYE_HALF, _mirror(EYE_HALF), BROW_HIGH, _mirror(BROW_HIGH), MOUTH_SMILE),
_make_frame(EYE_CLOSED, _mirror(EYE_CLOSED), BROW_LOWERED, _mirror(BROW_LOWERED), MOUTH_SMILE),
],
)

ASLEEP = Animation(
frames=[
_make_frame(EYE_CLOSED, _mirror(EYE_CLOSED), NO_BROW, NO_BROW, MOUTH_NORMAL),
],
# frame_duration=0.25,
)

SLEEPY = Animation(
frames=[
_make_frame(EYE_CLOSED, _mirror(EYE_CLOSED), NO_BROW, _mirror(BROW_STRAIGHT), MOUTH_NORMAL),
_make_frame(EYE_CLOSED, _mirror(EYE_HALF), NO_BROW, _mirror(BROW_LOWERED), MOUTH_NORMAL),
_make_frame(EYE_CLOSED, _mirror(EYE_OPEN), NO_BROW, _mirror(BROW_HIGH), MOUTH_NORMAL)
],
frame_duration=0.25,
mode=AnimationMode.ONCE_FORWARD_BACKWARD,
repeat_interval=10,
hold_end=1.5,
)

INQUISITIVE = Animation(
frames=[
_make_frame(EYE_OPEN, _mirror(EYE_OPEN), BROW_HIGH, _mirror(BROW_HIGH), MOUTH_SMILE),

_make_frame(EYE_LEFT_LOOK, _mirror_no_flip(EYE_LEFT_LOOK, EYE_OPEN), BROW_HIGH, _mirror(BROW_HIGH), MOUTH_SMILE),
_make_frame(_shift_left(EYE_LEFT_LOOK, 1), _shift_left(_mirror_no_flip(EYE_LEFT_LOOK, EYE_OPEN), 1), BROW_HIGH, _mirror(BROW_HIGH), MOUTH_SMILE),
_make_frame(_shift_left(EYE_LEFT_LOOK, 1), _shift_left(_mirror_no_flip(EYE_LEFT_LOOK, EYE_OPEN), 1), BROW_HIGH, _mirror(BROW_HIGH), MOUTH_SMILE),
_make_frame(_shift_left(EYE_LEFT_LOOK, 1), _shift_left(_mirror_no_flip(EYE_LEFT_LOOK, EYE_OPEN), 1), BROW_HIGH, _mirror(BROW_HIGH), MOUTH_SMILE),
_make_frame(EYE_LEFT_LOOK, _mirror_no_flip(EYE_LEFT_LOOK, EYE_OPEN), BROW_HIGH, _mirror(BROW_HIGH), MOUTH_SMILE),

# _make_frame(EYE_OPEN, _mirror(EYE_OPEN), BROW_HIGH, _mirror(BROW_HIGH), MOUTH_SMILE),

_make_frame(EYE_RIGHT_LOOK, _mirror_no_flip(EYE_RIGHT_LOOK, EYE_OPEN), BROW_HIGH, _mirror(BROW_HIGH), MOUTH_SMILE),
_make_frame(_shift_right(EYE_RIGHT_LOOK, 1), _shift_right(_mirror_no_flip(EYE_RIGHT_LOOK, EYE_OPEN), 1), BROW_HIGH, _mirror(BROW_HIGH), MOUTH_SMILE),
_make_frame(_shift_right(EYE_RIGHT_LOOK, 1), _shift_right(_mirror_no_flip(EYE_RIGHT_LOOK, EYE_OPEN), 1), BROW_HIGH, _mirror(BROW_HIGH), MOUTH_SMILE),
_make_frame(_shift_right(EYE_RIGHT_LOOK, 1), _shift_right(_mirror_no_flip(EYE_RIGHT_LOOK, EYE_OPEN), 1), BROW_HIGH, _mirror(BROW_HIGH), MOUTH_SMILE),
_make_frame(EYE_RIGHT_LOOK, _mirror_no_flip(EYE_RIGHT_LOOK, EYE_OPEN), BROW_HIGH, _mirror(BROW_HIGH), MOUTH_SMILE),

_make_frame(EYE_OPEN, _mirror(EYE_OPEN), BROW_HIGH, _mirror(BROW_HIGH), MOUTH_SMILE),
],
mode=AnimationMode.REPEAT_FORWARD,
frame_duration=0.2,
repeat_interval=10
)
Loading
Loading