feat: publish face detection instead of req/res

ref: N25B-395
This commit is contained in:
Twirre Meulenbelt
2026-01-29 17:18:39 +01:00
parent 3bbe97579d
commit 815fc7bcde
2 changed files with 21 additions and 56 deletions

View File

@@ -19,6 +19,8 @@ class AgentSettings(object):
:vartype audio_sender_port: int :vartype audio_sender_port: int
:ivar face_detection_port: Port used for sending face detection events, defaults to 5559. :ivar face_detection_port: Port used for sending face detection events, defaults to 5559.
:vartype face_detection_port: int :vartype face_detection_port: int
:ivar face_detection_interval: Time between face detection events, defaults to 1000 ms.
:vartype face_detection_interval: int
""" """
def __init__( def __init__(
self, self,
@@ -28,6 +30,7 @@ class AgentSettings(object):
video_sender_port=None, video_sender_port=None,
audio_sender_port=None, audio_sender_port=None,
face_detection_port=None, face_detection_port=None,
face_detection_interval=None,
): ):
self.control_backend_host = get_config(control_backend_host, "AGENT__CONTROL_BACKEND_HOST", "localhost") self.control_backend_host = get_config(control_backend_host, "AGENT__CONTROL_BACKEND_HOST", "localhost")
self.actuation_receiver_port = get_config(actuation_receiver_port, "AGENT__ACTUATION_RECEIVER_PORT", 5557, int) self.actuation_receiver_port = get_config(actuation_receiver_port, "AGENT__ACTUATION_RECEIVER_PORT", 5557, int)
@@ -35,6 +38,7 @@ class AgentSettings(object):
self.video_sender_port = get_config(video_sender_port, "AGENT__VIDEO_SENDER_PORT", 5556, int) self.video_sender_port = get_config(video_sender_port, "AGENT__VIDEO_SENDER_PORT", 5556, int)
self.audio_sender_port = get_config(audio_sender_port, "AGENT__AUDIO_SENDER_PORT", 5558, int) self.audio_sender_port = get_config(audio_sender_port, "AGENT__AUDIO_SENDER_PORT", 5558, int)
self.face_detection_port = get_config(face_detection_port, "AGENT__FACE_DETECTION_PORT", 5559, int) self.face_detection_port = get_config(face_detection_port, "AGENT__FACE_DETECTION_PORT", 5559, int)
self.face_detection_interval = get_config(face_detection_interval, "AGENT__FACE_DETECTION_INTERVAL", 1000, int)
class VideoConfig(object): class VideoConfig(object):

View File

@@ -13,61 +13,47 @@ from robot_interface.core.config import settings
class FaceDetectionSender(SocketBase): class FaceDetectionSender(SocketBase):
""" """
Face detection REP endpoint. Face detection endpoint.
- Polls ALMemory["FaceDetected"] Subscribes to and polls ALMemory["FaceDetected"], sends events to CB.
- Maintains a rolling boolean state
- Responds to REQ polling over ZMQ REP
""" """
def __init__(self, zmq_context, port=settings.agent_settings.face_detection_port): def __init__(self, zmq_context, port=settings.agent_settings.face_detection_port):
super(FaceDetectionSender, self).__init__("face") super(FaceDetectionSender, self).__init__("face")
if port: self.create_socket(zmq_context, zmq.PUB, port)
self.create_socket(zmq_context, zmq.REP, port)
self._face_service = None self._face_service = None
self._memory_service = None self._memory_service = None
self.face_detected = False
self._last_seen_face_time = 0
self._face_thread = None self._face_thread = None
self._rep_thread = None
def endpoint_description(self):
return "face"
# ------------------------------------------------------------------
# Face detection logic (runs independently)
# ------------------------------------------------------------------
def start_face_detection(self): def start_face_detection(self):
if not state.qi_session: if not state.qi_session:
logging.warning("No Qi session available. Face detection not started.") logging.warning("No Qi session available. Face detection not started.")
return return
import qi
self._face_service = state.qi_session.service("ALFaceDetection") self._face_service = state.qi_session.service("ALFaceDetection")
self._memory_service = state.qi_session.service("ALMemory") self._memory_service = state.qi_session.service("ALMemory")
self._face_service.setTrackingEnabled(False) self._face_service.setTrackingEnabled(False)
self._face_service.setRecognitionEnabled(False) self._face_service.setRecognitionEnabled(False)
self._face_service.subscribe("FaceDetectionSender", 500, 0.0) self._face_service.subscribe(
"FaceDetectionSender",
settings.agent_settings.face_detection_interval,
0.0,
)
self._face_thread = threading.Thread(target=self._face_loop) self._face_thread = threading.Thread(target=self._face_loop)
self._face_thread.start() self._face_thread.start()
self._rep_thread = threading.Thread(target=self._rep_loop) logging.info("Face detection started.")
self._rep_thread.start()
logging.info("Face detection + REP endpoint started.")
def _face_loop(self): def _face_loop(self):
""" """
Continuously updates `self.face_detected`. Continuously send face detected to the CB, at the interval set in the
``start_face_detection`` method.
""" """
while not state.exit_event.is_set(): while not state.exit_event.is_set():
try: try:
@@ -81,40 +67,11 @@ class FaceDetectionSender(SocketBase):
and len(value[1][0]) > 0 and len(value[1][0]) > 0
) )
now = time.time() self.socket.send(json.dumps({"face_detected": face_present}).encode("utf-8"))
if face_present:
self._last_seen_face_time = now
# Face considered lost after 3 seconds
self.face_detected = (now - self._last_seen_face_time) < 3
except Exception: except Exception:
logging.exception("Error reading FaceDetected") logging.exception("Error reading FaceDetected")
time.sleep(0.1) time.sleep(settings.agent_settings.face_detection_interval / 1000.0)
def _rep_loop(self):
"""
Handles ZMQ REQ/REP polling from the Control Backend.
"""
while not state.exit_event.is_set():
try:
# Receive request (content ignored)
_ = self.socket.recv()
# Respond with current face state
response = {
"endpoint": "face",
"data": self.face_detected,
}
self.socket.send_json(response)
except Exception:
logging.exception("Error in face detection REP loop")
time.sleep(0.1)
def stop_face_detection(self): def stop_face_detection(self):
try: try:
@@ -124,3 +81,7 @@ class FaceDetectionSender(SocketBase):
logging.info("Face detection stopped.") logging.info("Face detection stopped.")
except Exception: except Exception:
logging.warning("Error during face detection cleanup.") logging.warning("Error during face detection cleanup.")
def close(self):
super(FaceDetectionSender, self).close()
self.stop_face_detection()