feat: connected with RI properly

ref: N25B-397
This commit is contained in:
JobvAlewijk
2026-01-12 14:23:00 +01:00
parent bb0c1bd383
commit 1932ac959b
4 changed files with 43 additions and 53 deletions

View File

@@ -1,3 +1,4 @@
from .face_rec_agent import FacePerceptionAgent as FacePerceptionAgent
from .transcription_agent.transcription_agent import ( from .transcription_agent.transcription_agent import (
TranscriptionAgent as TranscriptionAgent, TranscriptionAgent as TranscriptionAgent,
) )

View File

@@ -1,74 +1,55 @@
import zmq import asyncio
import zmq.asyncio as azmq
from control_backend.agents import BaseAgent from control_backend.agents import BaseAgent
class FacePerceptionAgent(BaseAgent): class FacePerceptionAgent(BaseAgent):
""" """
Receives and processes face detection / recognition events Receives face presence updates from the RICommunicationAgent
coming from Pepper (via a NAOqi -> ZMQ bridge). via the internal PUB/SUB bus.
""" """
def __init__(self, name: str): def __init__(self, name: str):
super().__init__(name) super().__init__(name)
self._address = "tcp://127.0.0.1:5559" self._last_face_state: bool | None = None
self._socket: azmq.Socket | None = None
async def setup(self): async def setup(self):
self.logger.info("Starting FacePerceptionAgent") self.logger.info("Starting FacePerceptionAgent")
self.add_behavior(self._poll_loop())
ctx = azmq.Context.instance() async def _poll_loop(self):
self._socket = ctx.socket(zmq.SUB) poll_interval = 1.0
self._socket.setsockopt_string(zmq.SUBSCRIBE, "")
self._socket.connect(self._address)
self.add_behavior(self._listen_loop())
async def _listen_loop(self):
while self._running: while self._running:
try: try:
msg = await self._socket.recv_json() # Ask RICommunicationAgent (via main socket)
await self._process_face_data(msg) await self._req_socket.send_json({"endpoint": "face", "data": {}})
except Exception:
self.logger.exception("Error receiving face data")
async def _process_face_data(self, data: dict): response = await asyncio.wait_for(
""" self._req_socket.recv_json(), timeout=poll_interval
Processes NAOqi FaceDetected-derived data.
"""
faces = data.get("faces", [])
new_recognitions = data.get("new_recognitions", [])
if not faces:
self.logger.debug("No faces detected")
return
self.logger.debug("Detected %d face(s)", len(faces))
for face in faces:
face_id = face.get("face_id")
alpha = face.get("alpha")
beta = face.get("beta")
# size_x = face.get("size_x")
# size_y = face.get("size_y")
recognized = face.get("recognized", False)
label = face.get("label")
score = face.get("score", 0.0)
if recognized:
self.logger.info("Recognized %s (score=%.2f, id=%s)", label, score, face_id)
else:
self.logger.debug(
"Unrecognized face id=%s at (α=%.2f, β=%.2f)", face_id, alpha, beta
) )
# Temporal-filtered recognition (important!) face_present = bool(response.get("data", False))
for name in new_recognitions:
self.logger.info("New person recognized: %s", name)
# 🔮 Example belief posting hook if self._last_face_state is None:
# await self.post_belief("person_present", name=name) self._last_face_state = face_present
continue
if face_present != self._last_face_state:
self._last_face_state = face_present
self.logger.info("👀 Face detected" if face_present else "🙈 Face lost")
# TODO: post belief to BDI here
except Exception as e:
self.logger.warning("Face polling failed")
self.logger.warn(e)
await asyncio.sleep(poll_interval)
async def _handle_face_change(self, present: bool):
if present:
self.logger.info("👀 Face detected")
# await self.post_belief("face_present", value=True)
else:
self.logger.info("🙈 No face detected")
# await self.post_belief("face_present", value=False)

View File

@@ -49,6 +49,7 @@ class AgentSettings(BaseModel):
robot_speech_name: str = "robot_speech_agent" robot_speech_name: str = "robot_speech_agent"
robot_gesture_name: str = "robot_gesture_agent" robot_gesture_name: str = "robot_gesture_agent"
user_interrupt_name: str = "user_interrupt_agent" user_interrupt_name: str = "user_interrupt_agent"
face_agent_name: str = "face_detection_agent"
class BehaviourSettings(BaseModel): class BehaviourSettings(BaseModel):

View File

@@ -38,6 +38,7 @@ from control_backend.agents.communication import RICommunicationAgent
# Emotional Agents # Emotional Agents
# LLM Agents # LLM Agents
from control_backend.agents.llm import LLMAgent from control_backend.agents.llm import LLMAgent
from control_backend.agents.perception.face_rec_agent import FacePerceptionAgent
# User Interrupt Agent # User Interrupt Agent
from control_backend.agents.user_interrupt.user_interrupt_agent import UserInterruptAgent from control_backend.agents.user_interrupt.user_interrupt_agent import UserInterruptAgent
@@ -147,6 +148,12 @@ async def lifespan(app: FastAPI):
"name": settings.agent_settings.user_interrupt_name, "name": settings.agent_settings.user_interrupt_name,
}, },
), ),
"FaceDetectionAgent": (
FacePerceptionAgent,
{
"name": settings.agent_settings.face_agent_name,
},
),
} }
agents = [] agents = []