Make the eyes follow detected faces; start the face at power-up
hermes_face now subscribes to /vision/faces and glances toward the best face (a recognised one first, then the biggest), smoothed, holding 2.5 s and drifting back to centre when none is seen. Sleeping ignores gaze. Image x flips on the display: the camera looks the way the robot faces, so a person on the image's right is on the robot's right, the viewer's left. Fix the pan sign carried over from the prototype: positive pan is the robot's left (the MCP contract), which is the viewer's right on the display. robot-face.service starts the face at boot and restarts it on failure, with a private working directory because lgpio drops a notification pipe in its cwd and systemd's default / is not writable. robot-cpu-performance.service holds the CPU at full clock, which halves the face's CPU cost. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AFN8nRRCiDiMN7ChBpHJhs
This commit is contained in:
@@ -0,0 +1,31 @@
|
||||
"""Where the eyes should look, given where faces are in the camera image. No ROS imports."""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import NamedTuple, Optional, Sequence
|
||||
|
||||
|
||||
class Target(NamedTuple):
|
||||
cx: float # face centre, pixels
|
||||
cy: float
|
||||
w: float # face size, pixels
|
||||
h: float
|
||||
score: float = 1.0
|
||||
name: str = '' # who, if recognised
|
||||
|
||||
|
||||
def pick_target(faces: Sequence[Target]) -> Optional[Target]:
|
||||
"""A recognised face wins over an unknown one; otherwise the biggest, most confident face."""
|
||||
if not faces:
|
||||
return None
|
||||
return max(faces, key=lambda f: (bool(f.name), f.w * f.h * f.score))
|
||||
|
||||
|
||||
def gaze_from_pixel(cx: float, cy: float, width: float, height: float) -> tuple[float, float]:
|
||||
"""Image position -> eye direction (+x the viewer's right, +y down), each in [-1, 1].
|
||||
|
||||
The camera looks the way the robot faces, so a person on the right of the image is on the
|
||||
robot's right, which is the viewer's left on the display: x flips, y does not.
|
||||
"""
|
||||
gx = (cx - width / 2.0) / (width / 2.0)
|
||||
gy = (cy - height / 2.0) / (height / 2.0)
|
||||
return -max(-1.0, min(1.0, gx)), max(-1.0, min(1.0, gy))
|
||||
Reference in New Issue
Block a user