diff --git a/Autonomous/marcus_autonomous.py b/Autonomous/marcus_autonomous.py index 3b6a6fd..6ad6c24 100644 --- a/Autonomous/marcus_autonomous.py +++ b/Autonomous/marcus_autonomous.py @@ -61,6 +61,25 @@ import base64 from datetime import datetime from pathlib import Path +# Persist autonomous-mode events to logs/autonomous.log so an unattended +# explore/patrol run leaves an audit trail. Terminal output is preserved +# (the existing " [Auto] ..." indented lines still print) but every +# event is also written to the dedicated log for post-mortem review. +try: + from Core.logger import log as _core_log +except Exception: + _core_log = None + + +def _alog(msg: str, level: str = "info") -> None: + """Print and persist to logs/autonomous.log.""" + print(f" [Auto] {msg}") + if _core_log is not None: + try: + _core_log(f"[Auto] {msg}", level, "autonomous") + except Exception: + pass + # ══════════════════════════════════════════════════════════════════════════════ # CONFIGURATION @@ -134,7 +153,7 @@ class AutonomousMode: """Start autonomous exploration.""" with self._lock: if self._enabled: - print(" [Auto] Already running — use 'auto off' to stop first") + _alog("Already running — use 'auto off' to stop first") return self._enabled = True @@ -153,19 +172,19 @@ class AutonomousMode: ) self._thread.start() - print(f"\n [Auto] Exploration started") - print(f" [Auto] Map folder: {self._map_dir}") - print(f" [Auto] Type 'auto off' to stop\n") + _alog(f"Exploration started") + _alog(f"Map folder: {self._map_dir}") + _alog("Type 'auto off' to stop") def disable(self): """Stop autonomous exploration and save results.""" with self._lock: if not self._enabled: - print(" [Auto] Not running") + _alog("Not running") return self._enabled = False - print("\n [Auto] Stopping exploration...") + _alog("Stopping exploration...") # Wait for thread to finish if self._thread and self._thread.is_alive(): @@ -183,29 +202,29 @@ class AutonomousMode: obs = len(self._observations) if not running: - print(" [Auto] Status: IDLE") + _alog("Status: IDLE") if self._map_dir: - print(f" [Auto] Last map: {self._map_dir}") + _alog(f"Last map: {self._map_dir}") return elapsed = time.time() - (self._start_time or time.time()) mins = int(elapsed // 60) secs = int(elapsed % 60) - print(f" [Auto] Status: EXPLORING") - print(f" [Auto] Duration: {mins}m {secs}s") - print(f" [Auto] Steps: {step} | Observations: {obs}") + _alog(f"Status: EXPLORING") + _alog(f"Duration: {mins}m {secs}s") + _alog(f"Steps: {step} | Observations: {obs}") if self._area_counts: areas = ", ".join(f"{k}:{v}" for k, v in sorted(self._area_counts.items())) - print(f" [Auto] Areas seen: {areas}") + _alog(f"Areas seen: {areas}") if self._all_objects: - print(f" [Auto] Objects found: {', '.join(sorted(self._all_objects))}") + _alog(f"Objects found: {', '.join(sorted(self._all_objects))}") pos = self._odom_pos() if pos: - print(f" [Auto] Position: x={pos['x']:.2f} y={pos['y']:.2f} heading={pos['heading']:.1f}°") + _alog(f"Position: x={pos['x']:.2f} y={pos['y']:.2f} heading={pos['heading']:.1f}°") def is_enabled(self) -> bool: with self._lock: @@ -215,7 +234,7 @@ class AutonomousMode: """Save current state to disk without stopping.""" self._save_observations() self._save_path() - print(f" [Auto] Snapshot saved to {self._map_dir}") + _alog(f"Snapshot saved to {self._map_dir}") # ── EXPLORATION LOOP ──────────────────────────────────────────────────────── @@ -240,7 +259,7 @@ class AutonomousMode: if self._yolo_sees("person"): closest = self._yolo_closest("person") if closest and closest.distance_estimate == "very close": - print(f" [Auto] Person very close — pausing 2s") + _alog(f"Person very close — pausing 2s") self._gradual_stop() time.sleep(2.0) continue @@ -268,7 +287,7 @@ class AutonomousMode: # ── Movement decision ───────────────────────────────────────────── if consecutive_blocks >= 3: # Stuck — turn more aggressively - print(f" [Auto] Stuck — turning {self._last_turn} 180°") + _alog(f"Stuck — turning {self._last_turn} 180°") self._turn(self._last_turn, TURN_DURATION * 2) consecutive_blocks = 0 continue @@ -281,14 +300,14 @@ class AutonomousMode: # Alternate left/right turns to explore both directions turn_dir = "left" if self._last_turn == "right" else "right" self._last_turn = turn_dir - print(f" [Auto] Obstacle — turning {turn_dir}") + _alog(f"Obstacle — turning {turn_dir}") self._turn(turn_dir, TURN_DURATION) else: consecutive_blocks = 0 # ── Max observations check ──────────────────────────────────────── if len(self._observations) >= MAX_OBSERVATIONS: - print(f" [Auto] Max observations ({MAX_OBSERVATIONS}) reached — stopping") + _alog(f"Max observations ({MAX_OBSERVATIONS}) reached — stopping") self._enabled = False break @@ -375,9 +394,9 @@ class AutonomousMode: } self._observations.append(obs) - print(f" [Auto] Step {self._step} | {area_type} | {observation[:60]}") + _alog(f"Step {self._step} | {area_type} | {observation[:60]}") if objects: - print(f" [Auto] Objects: {', '.join(objects)}") + _alog(f"Objects: {', '.join(objects)}") # Save frame if interesting if interesting and SAVE_FRAMES: @@ -396,7 +415,7 @@ class AutonomousMode: self._save_path() except Exception as e: - print(f" [Auto] LLaVA assess error: {e}") + _alog(f"LLaVA assess error: {e}") # ── FILE I/O ──────────────────────────────────────────────────────────────── @@ -429,7 +448,7 @@ class AutonomousMode: json.dump(self._observations, f, indent=2, ensure_ascii=False) tmp.replace(path) except Exception as e: - print(f" [Auto] Save observations error: {e}") + _alog(f"Save observations error: {e}") def _save_path(self): if not self._map_dir or not self._path: @@ -441,7 +460,7 @@ class AutonomousMode: json.dump(self._path, f, indent=2) tmp.replace(path) except Exception as e: - print(f" [Auto] Save path error: {e}") + _alog(f"Save path error: {e}") def _save_frame(self, img_b64: str, step: int): """Save a camera frame as JPEG.""" @@ -452,7 +471,7 @@ class AutonomousMode: with open(frame_path, "wb") as f: f.write(__import__("base64").b64decode(img_b64)) except Exception as e: - print(f" [Auto] Save frame error: {e}") + _alog(f"Save frame error: {e}") def _generate_summary(self) -> str: """Generate a text summary of the exploration session.""" @@ -500,7 +519,7 @@ class AutonomousMode: with open(self._map_dir / "summary.txt", "w", encoding="utf-8") as f: f.write(summary) except Exception as e: - print(f" [Auto] Save summary error: {e}") + _alog(f"Save summary error: {e}") def _print_summary(self): """Print exploration summary to terminal.""" @@ -508,15 +527,15 @@ class AutonomousMode: mins = int(elapsed // 60) secs = int(elapsed % 60) - print(f"\n [Auto] Exploration complete") - print(f" [Auto] Duration: {mins}m {secs}s | Steps: {self._step}") - print(f" [Auto] Observations: {len(self._observations)}") + _alog(f"Exploration complete") + _alog(f"Duration: {mins}m {secs}s | Steps: {self._step}") + _alog(f"Observations: {len(self._observations)}") if self._area_counts: - print(f" [Auto] Areas: {dict(sorted(self._area_counts.items()))}") + _alog(f"Areas: {dict(sorted(self._area_counts.items()))}") if self._all_objects: - print(f" [Auto] Objects: {', '.join(sorted(self._all_objects))}") + _alog(f"Objects: {', '.join(sorted(self._all_objects))}") if self._map_dir: - print(f" [Auto] Saved to: {self._map_dir}\n") + _alog(f"Saved to: {self._map_dir}\n") diff --git a/Brain/marcus_brain.py b/Brain/marcus_brain.py index 60fa09b..d0d09aa 100644 --- a/Brain/marcus_brain.py +++ b/Brain/marcus_brain.py @@ -186,10 +186,12 @@ _voice_module = None def _init_voice(): """ - Initialize the voice subsystem: G1 built-in mic + Whisper STT + G1 - built-in TtsMaker for replies. Every transcribed command flows through - process_command(), and the resulting `speak` string is sent to the G1 - speaker. + Initialize the voice subsystem: G1 built-in mic + Gemini Live S2S + (subprocess in gemini_sdk env, runs Voice/gemini_runner.py). Every + transcribed user command flows through process_command(); the brain + decides motion side-effects, but the verbal reply itself comes from + Gemini's voice through the G1 speaker (NOT TtsMaker — TtsMaker stays + for non-voice subsystem callers but isn't invoked from this callback). """ global _audio_api, _voice_module try: @@ -201,6 +203,14 @@ def _init_voice(): # Heuristic filter for unusable Gemini transcripts. Gemini emits # `` literally when audio is non-speech and `.` for empty # bursts. These shouldn't pollute the terminal or trigger motion. + # Bilingual aware: short Arabic motion commands like "اجلس" / + # "قف" / "تعال" are LEGIT — only reject short non-ASCII-non-Arabic + # snippets (Korean / Thai / etc — echo/distortion garbage). + def _is_supported_lang_char(c: str) -> bool: + # ASCII letters/digits/punct OR Arabic block (U+0600..U+06FF) + # OR whitespace. + return c.isascii() or "؀" <= c <= "ۿ" or c.isspace() + def _is_garbage_transcript(t: str) -> bool: stripped = t.strip().strip(".!?,").strip() if not stripped: @@ -211,10 +221,12 @@ def _init_voice(): # Bare single character (often "." → ".") or all punctuation. if all(not c.isalnum() for c in stripped): return True - # Short non-ASCII fragments (e.g. Korean / Thai / Arabic - # snippets that come from echo or distortion). - if len(stripped) <= 6 and not all(ord(c) < 128 for c in stripped): - return True + # Reject only when text is BOTH short AND uses chars from + # neither English nor Arabic — those are echo/distortion + # mistranscriptions (Korean/Thai/etc fragments). + if len(stripped) <= 6: + if not all(_is_supported_lang_char(c) for c in stripped): + return True return False def _on_command(text, lang): diff --git a/Config/config_Voice.json b/Config/config_Voice.json index 3e3c4f6..922e83d 100644 --- a/Config/config_Voice.json +++ b/Config/config_Voice.json @@ -19,7 +19,10 @@ "gemini_chunk_size": 512, "gemini_send_sample_rate": 16000, "gemini_receive_sample_rate": 24000, - "gemini_record_enabled": true, + + "_gemini_record_comment": "Per-turn WAV recorder — saves _user.wav (mic) + _robot.wav (Gemini's voice) + appends one entry to index.json under Data/Voice/Recordings/gemini_turns/. OFF by default in production: 11 MB grew over a single ~2 hour test session, will fill the disk over a deployment. Flip to true ONLY when debugging mic-quality / transcript / echo issues. gemini_record_keep_count caps the on-disk file count when recording is on; oldest turn pair gets pruned when a new one lands.", + "gemini_record_enabled": false, + "gemini_record_keep_count": 50, "_gemini_camera_comment": "Stream camera frames to Gemini Live so vision answers ('what do you see') are correct rather than hallucinated. Marcus parent grabs JPEG frames via API.camera_api.get_frame() at gemini_frame_interval_sec cadence and pipes them to the runner over stdin. Frame_max_age_sec drops stale frames. Set gemini_send_frames=false to disable (saves API tokens but breaks vision questions).", "gemini_send_frames": true, @@ -35,9 +38,9 @@ "gemini_begin_stream_pause_sec": 0.15, "gemini_wait_finish_margin_sec": 0.3, - "_gemini_system_prompt_comment": "Persona for Gemini Live's spoken reply. Gemini owns the voice in this architecture, so make this prompt match the experience you want users to hear. The robot's body is controlled by Marcus's brain via a side channel — Gemini doesn't need to invoke motions itself, just acknowledge them naturally. Override by pointing gemini_system_prompt_file at a text file (relative paths resolve from PROJECT_ROOT).", + "_gemini_system_prompt_comment": "Persona for Gemini Live. **Gemini is the SOLE wake-word gatekeeper** — Marcus does NOT check for 'Sanad'/'سند' in Python. The dispatcher just listens to whatever Gemini speaks; if Gemini says a motion-confirmation phrase ('Turning right', 'أستدير يميناً'), Marcus dispatches motion. Therefore the persona below MUST keep Gemini disciplined: never use a motion-confirmation phrase unless the user actually said the wake word. The strict rules are spelled out inside the prompt itself. Override by pointing gemini_system_prompt_file at a text file.", "gemini_system_prompt_file": "", - "gemini_system_prompt": "You are Sanad (سند), a friendly humanoid robot assistant made by YS Lootah Technology in Dubai. Your body is a Unitree G1 humanoid. You can see the user through your camera and talk to them in real time. You speak both English and Arabic naturally — match the user's language in your reply. Reply briefly, usually one or two sentences. When the user asks 'what do you see' / 'ماذا ترى' or describes the scene, look at the camera frames you're receiving and answer accurately based on what's actually there; do not invent details. CRITICAL ACTION RULE — physical motion only happens when the user addresses you by name 'Sanad' (English) or 'سند' (Arabic) AND gives an action. Examples: 'Sanad, turn right' → say 'Turning right.' 'سند، استدر يميناً' → say 'أستدير يميناً.' Plain conversation or vision queries WITHOUT 'Sanad' / 'سند' are fine but DO NOT trigger any motion confirmation — just chat or describe. NEVER say 'Turning' / 'Moving' / 'Sitting' / 'أستدير' / 'أتحرك' unless the user actually said 'Sanad' / 'سند' first. When you do say a motion confirmation, use the same language the user used. Motion verbs supported (English / Arabic): turn left/right (استدر يميناً/يساراً), turn around (استدر للخلف), move forward/back (تحرك للأمام/للخلف), sit down (اجلس), stand up (قف), wave hello (لوّح), raise/lower arm (ارفع/اخفض يدك), come here (تعال), follow me (اتبعني), stay here (ابق هنا), go home (اذهب للبيت), stop (توقف), patrol (طوف), look around (انظر حولك).", + "gemini_system_prompt": "You are Sanad (سند), a friendly humanoid robot made by YS Lootah Technology in Dubai. Your body is a Unitree G1 humanoid. You see the user through your camera and talk to them in real time. You speak BOTH English and Arabic fluently — always match the user's language in your reply. Reply briefly: usually one or two sentences. When the user asks what you see / 'ماذا ترى' / 'شو شايف' or otherwise asks about the scene, look at the camera frames you are receiving and answer ONLY with what is actually there; never invent details.\n\nCRITICAL ACTION GATE — YOU ARE THE ONLY GATEKEEPER. An external system listens to YOUR spoken reply for motion-confirmation phrases ('Turning right', 'Sitting down', 'أستدير يميناً', 'أجلس', etc.) and uses those phrases to physically move the robot. Your verbal acknowledgement IS the trigger. So:\n\nRULE 1 — Use motion-confirmation phrases ONLY when the user clearly addressed you BY NAME ('Sanad' in English OR 'سند' / 'يا سند' in Arabic) AND requested an action. The wake word and the action must both be present in the SAME user utterance.\n\nRULE 2 — If the user requests a motion WITHOUT saying your name (e.g. 'turn right', 'استدر يميناً', 'sit down'), DO NOT say any motion-confirmation phrase. Reply naturally explaining you need to be addressed by name first — e.g. 'Please call me Sanad first if you'd like me to move.' / 'لازم تنادي عليّ باسمي سند قبل لما أتحرك.'\n\nRULE 3 — If the user says only your name ('Sanad' / 'سند' alone) with no action, reply 'Yes?' / 'نعم؟'. Do NOT say any motion phrase.\n\nRULE 4 — Plain chat, vision queries, hypotheticals ('I might turn around if I get lost'), descriptive references ('the person turned right and walked away'): NEVER use a motion verb in first person present continuous. Rephrase so you do not say 'Turning ...', 'Moving ...', 'Sitting ...', 'Standing ...', 'أستدير ...', 'أتحرك ...', 'أجلس ...', 'أقف ...'.\n\nRULE 5 — When you DO confirm a real motion request, reply in the SAME language the user used and emit ONE short confirmation phrase per requested motion, separated by punctuation, in the order the user listed them. Single motion: 'Turning right.' / 'أستدير يميناً.' Compound (multiple motions in one utterance): 'Turning right, then moving forward.' / 'أستدير يميناً، ثم أتحرك للأمام.' / 'سأقف، ثم ألوّح.' Each motion verb must come from the supported list below. Do NOT collapse multiple requested motions into one verb, do NOT add motions the user did not ask for, and do NOT pad with filler — only the chained motion phrases plus optional commas/'then'/'ثم'/'وبعدين'.\n\nRULE 6 — PARAMETRIC MOTIONS. When the user includes a NUMBER ('turn 360', 'turn left 90 degrees', 'walk 5 steps', 'walk 2 meters back', 'استدر 360 درجة', 'امشِ خمس خطوات', 'تحرك مترين'), KEEP the number in your confirmation phrase using these EXACT shapes — the external dispatcher reads the number from your reply and passes it to the robot:\n • 'Turning degrees.' / 'Turning left degrees.' / 'Turning right degrees.'\n • 'Walking steps.' / 'Walking forward steps.' / 'Walking back steps.'\n • 'Walking meters.' / 'Walking forward meters.' / 'Walking backward meters.'\n • 'أستدير درجة.' / 'أستدير يميناً درجة.' / 'أستدير يساراً درجة.'\n • 'أمشي خطوات.' / 'أمشي للأمام متر.' / 'أمشي للخلف متر.'\nUse digit characters ('5', '90', '360'), not spelled words ('five', 'ninety', 'خمسة'). Convert spelled-out numbers and Arabic words ('خمس'/'خمسة'/'عشرة') to digits before speaking. If the user says 'turn around' WITHOUT a number, treat it as the fixed 'turn around' motion — do NOT invent 180. If the user gives multiple parametric motions ('turn right 90 then walk 3 steps'), chain them per Rule 5 with the numbers preserved: 'Turning right 90 degrees, then walking 3 steps.'\n\nMotion verbs supported (English / Arabic): turn left/right (استدر يميناً/يساراً), turn around (استدر للخلف), move forward/back (تحرك للأمام/للخلف), sit down (اجلس), stand up (قف), wave hello (لوّح), raise/lower arm (ارفع/اخفض يدك), come here (تعال), follow me (اتبعني), stay here (ابق هنا), go home (اذهب للبيت), stop (توقف), patrol (طوف), look around (انظر حولك).", "_gemini_vad_comment": "Gemini server-side VAD tuning. start_sensitivity/end_sensitivity accept 'START_SENSITIVITY_HIGH|LOW' and 'END_SENSITIVITY_HIGH|LOW'. HIGH start = eagerly treats any speech-like sound as turn start, LOW = more conservative. LOW end = longer patience before ending a turn, HIGH = cuts turn sooner. prefix_padding_ms preserves audio from just before speech is detected. silence_duration_ms is how long of quiet ends a turn.", "gemini_vad_start_sensitivity": "START_SENSITIVITY_HIGH", @@ -51,7 +54,8 @@ "gemini_max_consecutive_errors": 10, "gemini_no_messages_timeout_sec": 30, - "mic_gain": 1.0, + "_mic_gain_comment": "Software gain applied to every mic chunk before it reaches Gemini Live. G1 far-field mic is quiet at default — voices below 1m can land below Gemini's server-side VAD start threshold and never trigger a turn (you'll see 'alive (no speech ...)' repeating in voice.log). 1.0 = unchanged. 2.0–3.0 = comfortable for a 1–2 m talking distance. Above 4.0 starts clipping loud syllables. Tune by reading the e= value in 'alive (no speech ...)' lines: aim for e>500 when you speak normally; >1500 if you want barge-in to fire mid-Gemini-reply.", + "mic_gain": 2.5, "_dispatch_comment": "Motion command dispatch side-channel. Marcus listens to Gemini's input_transcription; if the text contains a wake-word variant AND the remainder fuzzy-matches a canonical phrase in command_vocab at >= command_vocab_cutoff, Marcus fires on_command() in parallel to Gemini's verbal reply. Dedup on the canonical form within command_cooldown_sec prevents streaming partials from double-firing.", "command_vocab_cutoff": 0.72, diff --git a/Config/instruction.json b/Config/instruction.json index 82ae8dc..426551f 100644 --- a/Config/instruction.json +++ b/Config/instruction.json @@ -1,5 +1,5 @@ { - "_description": "Bilingual voice command instructions — single source of truth for the voice dispatch tables. Loaded by Voice/marcus_voice.py at module level. Adding a new motion command, a new accent variant, a new Arabic phrasing, or fixing a misheard wake-word transcription is a JSON-only edit; no Python change required.", + "_description": "Bilingual voice command instructions — single source of truth for the voice dispatch tables. Loaded by Voice/marcus_voice.py at module level. Adding a new motion command, a new accent variant, a new Arabic phrasing, or fixing a misheard wake-word transcription is a JSON-only edit; no Python change required. Arabic phrase tables cover MSA (Fusha) plus Gulf/Khaleeji, Egyptian (Masri), Levantine (Shami), Iraqi, and Maghrebi dialects.", "_format": "wake_words = whole-word substrings the dispatch gate looks for in the user's transcript. Any match (English or Arabic) opens motion for the current turn. actions = per-motion phrase tables. Each action has a `canonical` string (what marcus_brain receives), `user_phrases` (what the user might SAY when asking for the motion — used for fuzzy-match + Arabic-to-English translation after wake-word strip), and `bot_phrases` (what Gemini might SPEAK when acknowledging — used by the bot-side dispatcher to fire motion off Gemini's own confirmation). All matching is substring-based; English entries are matched case-insensitively, Arabic entries match as-is. Keep the canonical string consistent with Brain/command_parser.py vocabulary.", @@ -16,8 +16,9 @@ "sa nad", "san ad", "san odd", "san add" ], "arabic": [ - "سند", "سنّاد", "ساند", "سنود", "سنَد", "سنّد", "سَند", - "يا سند", "يا سنّاد", "يا ساند", "يا سَند" + "سند", "سنّاد", "ساند", "سنود", "سنَد", "سنّد", "سَند", "سنّود", "ساندي", "سنادي", + "يا سند", "يا سنّاد", "يا ساند", "يا سَند", "يا سنود", "ياسند", "ياسنود", + "السند", "السنّاد" ] }, @@ -26,11 +27,21 @@ "canonical": "turn right", "user_phrases": { "english": ["turn right", "rotate right", "spin right", "go right", "face right", "right"], - "arabic": ["استدر يميناً", "استدر يمينا", "ادر يميناً", "ادر يمينا", "لف يمين", "لف يميناً", "يمين"] + "arabic": [ + "استدر يميناً", "استدر يمينا", "ادر يميناً", "ادر يمينا", "لف يمين", "لف يميناً", "يمين", + "دور يمين", "دور لليمين", "دور على اليمين", "دور ع اليمين", + "خش يمين", "خش ع اليمين", "حوّد يمين", "حود يمين", "حوّد لليمين", + "دير يمين", "دير لليمين", "دير على ليمن", "دير ع اليمين", + "لف لليمين", "لف على اليمين", "لف عاليمين", "لف ع اليمين", + "روح يمين", "اتجه يمين", "اتجه لليمين", "تجه يمين" + ] }, "bot_phrases": { "english": ["turning right"], - "arabic": ["أستدير يميناً", "أستدير يمينا", "استدير يميناً", "استدير يمينا", "ألف يميناً", "ألف يمينا"] + "arabic": [ + "أستدير يميناً", "أستدير يمينا", "استدير يميناً", "استدير يمينا", "ألف يميناً", "ألف يمينا", + "بلف يمين", "بدور يمين", "أدور يمين", "أحوّد يمين", "أخش يمين" + ] } }, @@ -38,11 +49,21 @@ "canonical": "turn left", "user_phrases": { "english": ["turn left", "rotate left", "spin left", "go left", "face left", "left"], - "arabic": ["استدر يساراً", "استدر يسارا", "ادر يساراً", "ادر يسارا", "لف يسار", "لف يساراً", "يسار", "شمال"] + "arabic": [ + "استدر يساراً", "استدر يسارا", "ادر يساراً", "ادر يسارا", "لف يسار", "لف يساراً", "يسار", "شمال", + "دور يسار", "دور شمال", "دور لليسار", "دور على اليسار", "دور ع الشمال", + "خش يسار", "خش شمال", "خش ع الشمال", "حوّد شمال", "حود شمال", "حوّد يسار", "حوّد لليسار", + "دير يسار", "دير شمال", "دير لليسار", "دير على ليسار", "دير ع الشمال", + "لف لليسار", "لف على اليسار", "لف عاشمال", "لف ع الشمال", + "روح يسار", "روح شمال", "اتجه يسار", "اتجه شمال", "اتجه لليسار", "تجه شمال" + ] }, "bot_phrases": { "english": ["turning left"], - "arabic": ["أستدير يساراً", "أستدير يسارا", "استدير يساراً", "استدير يسارا", "ألف يساراً", "ألف يسارا"] + "arabic": [ + "أستدير يساراً", "أستدير يسارا", "استدير يساراً", "استدير يسارا", "ألف يساراً", "ألف يسارا", + "بلف شمال", "بدور شمال", "أدور شمال", "أحوّد شمال", "أخش شمال" + ] } }, @@ -50,11 +71,19 @@ "canonical": "turn around", "user_phrases": { "english": ["turn around", "turn back", "spin around", "about face", "face the other way"], - "arabic": ["استدر للخلف", "استدر إلى الوراء", "اتجه للخلف", "ادر للخلف", "ارجع وجهك"] + "arabic": [ + "استدر للخلف", "استدر إلى الوراء", "اتجه للخلف", "ادر للخلف", "ارجع وجهك", + "لف ورا", "لف للورا", "لف للخلف", "دور ورا", "دور للورا", "دور للخلف", + "اتلف", "اتلف ورا", "دير وراك", "دير للورا", "استدر ورا", + "اقلب وجهك", "اقلب", "اعكس اتجاهك", "غيّر اتجاهك" + ] }, "bot_phrases": { "english": ["turning around"], - "arabic": ["أستدير للخلف", "أستدير إلى الوراء", "استدير للخلف"] + "arabic": [ + "أستدير للخلف", "أستدير إلى الوراء", "استدير للخلف", + "بلف ورا", "بدور للورا", "أدور للخلف" + ] } }, @@ -62,11 +91,22 @@ "canonical": "move forward", "user_phrases": { "english": ["move forward", "go forward", "walk forward", "step forward", "forward", "keep going", "walk ahead", "move ahead"], - "arabic": ["تحرك للأمام", "تحرك إلى الأمام", "اذهب للأمام", "امش للأمام", "تقدم", "للأمام", "أمام"] + "arabic": [ + "تحرك للأمام", "تحرك إلى الأمام", "اذهب للأمام", "امش للأمام", "تقدم", "للأمام", "أمام", + "روح قدام", "روح لقدام", "روح كدام", + "امشي قدام", "امشي لقدام", "امش قدام", + "اتقدم", "تقدم لقدام", "يلا قدام", "يالله قدام", + "فوت", "فوت لقدام", "فوت قدام", + "سير لقدام", "سير قدام", "اسير قدام", + "خش قدام", "تعال قدام", "للقدام", "قدام" + ] }, "bot_phrases": { "english": ["moving forward", "walking forward", "stepping forward", "going forward", "going ahead"], - "arabic": ["أتحرك للأمام", "أتحرك إلى الأمام", "أتقدم", "أمشي للأمام", "أذهب للأمام"] + "arabic": [ + "أتحرك للأمام", "أتحرك إلى الأمام", "أتقدم", "أمشي للأمام", "أذهب للأمام", + "بمشي قدام", "براح قدام", "أروح قدام", "بتقدم", "بفوت لقدام" + ] } }, @@ -74,11 +114,21 @@ "canonical": "move backward", "user_phrases": { "english": ["move back", "move backward", "go back", "go backward", "walk back", "walk backward", "step back", "backward", "back", "reverse"], - "arabic": ["تحرك للخلف", "تحرك إلى الخلف", "اذهب للخلف", "امش للخلف", "ارجع", "ارجع للخلف", "للخلف", "خلف"] + "arabic": [ + "تحرك للخلف", "تحرك إلى الخلف", "اذهب للخلف", "امش للخلف", "ارجع", "ارجع للخلف", "للخلف", "خلف", + "روح ورا", "روح للورا", "ارجع ورا", "ارجع للورا", + "امشي ورا", "امشي للورا", "امش ورا", + "تأخر", "تراجع", "ورا", "للورا", + "رجع للخلف", "ارجع لورا", "رجع لورا", + "سير للورا", "سير ورا" + ] }, "bot_phrases": { "english": ["moving backward", "moving back", "walking backward", "walking back", "stepping back", "going back"], - "arabic": ["أتحرك للخلف", "أتحرك إلى الخلف", "أرجع", "أمشي للخلف", "أعود للخلف"] + "arabic": [ + "أتحرك للخلف", "أتحرك إلى الخلف", "أرجع", "أمشي للخلف", "أعود للخلف", + "بمشي ورا", "براح ورا", "أرجع ورا", "بتراجع" + ] } }, @@ -86,11 +136,20 @@ "canonical": "move right", "user_phrases": { "english": ["step right", "move right", "slide right", "strafe right", "sidestep right"], - "arabic": ["تحرك يميناً", "تحرك يمينا", "خطوة يمين", "اتجه يميناً"] + "arabic": [ + "تحرك يميناً", "تحرك يمينا", "خطوة يمين", "اتجه يميناً", + "زحلق يمين", "زحلق لليمين", "اتزحلق يمين", + "خطوة لليمين", "خطوة ع اليمين", "خطوة على اليمين", + "حرك يمين", "حرّك يمين", "اخطو يمين", "اخطو لليمين", + "زح يمين", "زح لليمين" + ] }, "bot_phrases": { "english": ["moving right", "stepping right", "sliding right"], - "arabic": ["أتحرك يميناً", "أتحرك يمينا", "أخطو يميناً"] + "arabic": [ + "أتحرك يميناً", "أتحرك يمينا", "أخطو يميناً", + "بزحلق يمين", "أزحلق يمين", "بخطو يمين" + ] } }, @@ -98,11 +157,20 @@ "canonical": "move left", "user_phrases": { "english": ["step left", "move left", "slide left", "strafe left", "sidestep left"], - "arabic": ["تحرك يساراً", "تحرك يسارا", "خطوة يسار", "اتجه يساراً"] + "arabic": [ + "تحرك يساراً", "تحرك يسارا", "خطوة يسار", "اتجه يساراً", + "زحلق يسار", "زحلق شمال", "زحلق لليسار", "اتزحلق شمال", + "خطوة شمال", "خطوة لليسار", "خطوة ع الشمال", "خطوة على اليسار", + "حرك يسار", "حرك شمال", "حرّك شمال", "اخطو يسار", "اخطو شمال", "اخطو لليسار", + "زح يسار", "زح شمال" + ] }, "bot_phrases": { "english": ["moving left", "stepping left", "sliding left"], - "arabic": ["أتحرك يساراً", "أتحرك يسارا", "أخطو يساراً"] + "arabic": [ + "أتحرك يساراً", "أتحرك يسارا", "أخطو يساراً", + "بزحلق شمال", "أزحلق شمال", "بخطو شمال" + ] } }, @@ -110,11 +178,21 @@ "canonical": "stop", "user_phrases": { "english": ["stop", "halt", "wait", "pause", "freeze", "hold", "stop moving", "stand still", "don't move"], - "arabic": ["توقف", "قف مكانك", "اوقف", "انتظر", "اثبت", "لا تتحرك"] + "arabic": [ + "توقف", "قف مكانك", "اوقف", "انتظر", "اثبت", "لا تتحرك", + "وقّف", "وقف", "وگف", "بطّل", "بطل حركة", "بطل", + "خلاص", "كفاية", "يكفي", + "استنى", "استنى شوية", "اصبر", "صبر", "اصبر شوية", + "تو", "ثبات", "اثبت مكانك", "ما تتحرك", "ما تمشي", + "هدّي", "هدّى", "روّق" + ] }, "bot_phrases": { "english": ["stopping", "halting", "holding"], - "arabic": ["أتوقف", "توقفت", "أنتظر"] + "arabic": [ + "أتوقف", "توقفت", "أنتظر", + "بوقّف", "وقّفت", "بستنى", "أصبر", "بطّلت" + ] } }, @@ -122,11 +200,19 @@ "canonical": "sit down", "user_phrases": { "english": ["sit down", "sit", "take a seat", "have a seat"], - "arabic": ["اجلس", "ارتح", "اقعد"] + "arabic": [ + "اجلس", "ارتح", "اقعد", + "اقعد على الأرض", "اقعد ع الأرض", "اجلس على الأرض", + "اگعد", "گلس", "تفضل اقعد", "ارتاح", + "خد راحتك واقعد", "اقعد يا سند" + ] }, "bot_phrases": { "english": ["sitting down", "sitting"], - "arabic": ["أجلس", "أقعد", "جلست"] + "arabic": [ + "أجلس", "أقعد", "جلست", + "بقعد", "قعدت", "بجلس", "أرتاح" + ] } }, @@ -134,11 +220,19 @@ "canonical": "stand up", "user_phrases": { "english": ["stand up", "stand", "get up", "rise"], - "arabic": ["قف", "انهض", "ارفع نفسك"] + "arabic": [ + "قف", "انهض", "ارفع نفسك", + "قوم", "گوم", "نوض", "نض", + "قوم على رجلك", "قوم على رجليك", "انهض من مكانك", + "اوقف على رجلك" + ] }, "bot_phrases": { "english": ["standing up", "getting up", "rising"], - "arabic": ["أقف", "أنهض", "وقفت"] + "arabic": [ + "أقف", "أنهض", "وقفت", + "بقوم", "قمت", "نضت", "بنهض" + ] } }, @@ -146,11 +240,21 @@ "canonical": "wave hello", "user_phrases": { "english": ["wave hello", "wave", "say hi", "greet", "wave to me", "wave at me"], - "arabic": ["لوّح", "لوح", "لوّح بيدك", "حيّ", "سلّم"] + "arabic": [ + "لوّح", "لوح", "لوّح بيدك", "حيّ", "سلّم", + "لوّح لي", "لوّحلي", "لوّح بيدك لي", + "سلّم بيدك", "سلّم علي", "حيّيني", "حيي", + "هز يدك", "هز إيدك", "هزّ ايدك", + "قول هاي", "قول مرحبا", "قول السلام", + "حييني", "سلم علي" + ] }, "bot_phrases": { "english": ["waving hello", "waving", "saying hi", "greeting"], - "arabic": ["ألوّح", "ألوح", "ألوّح بيدي", "أحيّ", "أسلّم"] + "arabic": [ + "ألوّح", "ألوح", "ألوّح بيدي", "أحيّ", "أسلّم", + "بلوّح", "بسلّم", "بهز إيدي", "أهز يدي" + ] } }, @@ -158,11 +262,21 @@ "canonical": "raise arm", "user_phrases": { "english": ["raise arm", "raise your arm", "lift your arm", "arm up", "hand up"], - "arabic": ["ارفع يدك", "ارفع ذراعك", "اليد للأعلى"] + "arabic": [ + "ارفع يدك", "ارفع ذراعك", "اليد للأعلى", + "ارفع إيدك", "ارفع ايدك", "ارفع يديك", + "طلّع يدك", "طلع يدك", "طلّع إيدك", "طلع إيدك", + "علّي يدك", "علّي إيدك", "علي يدك", + "يدك فوق", "إيدك فوق", "اليد فوق", "حط يدك فوق", + "ارفع اليد", "ارفع ايديك" + ] }, "bot_phrases": { "english": ["raising arm", "raising my arm", "lifting my arm", "arm up"], - "arabic": ["أرفع يدي", "أرفع ذراعي"] + "arabic": [ + "أرفع يدي", "أرفع ذراعي", + "برفع إيدي", "بطلّع إيدي", "بعلّي إيدي", "رفعت يدي" + ] } }, @@ -170,11 +284,21 @@ "canonical": "lower arm", "user_phrases": { "english": ["lower arm", "lower your arm", "drop your arm", "arm down", "hand down", "rest your arm"], - "arabic": ["اخفض يدك", "اخفض ذراعك", "اليد للأسفل", "نزل يدك"] + "arabic": [ + "اخفض يدك", "اخفض ذراعك", "اليد للأسفل", "نزل يدك", + "نزّل يدك", "نزّل إيدك", "نزل إيدك", "نزّل ايدك", + "حط يدك", "حط إيدك", "حط ايدك", "خلي يدك تحت", + "طيح يدك", "طيّح يدك", "طيح إيدك", + "يدك تحت", "إيدك تحت", "اليد تحت", + "ارح يدك", "ريّح يدك", "ريّح إيدك" + ] }, "bot_phrases": { "english": ["lowering arm", "lowering my arm", "dropping my arm", "arm down"], - "arabic": ["أخفض يدي", "أخفض ذراعي", "أنزل يدي"] + "arabic": [ + "أخفض يدي", "أخفض ذراعي", "أنزل يدي", + "بنزّل إيدي", "نزّلت إيدي", "بريّح إيدي" + ] } }, @@ -182,11 +306,20 @@ "canonical": "point", "user_phrases": { "english": ["point", "point at it", "point to it", "point there"], - "arabic": ["اشر", "أشِر", "اشر إلى", "اشر هناك"] + "arabic": [ + "اشر", "أشِر", "اشر إلى", "اشر هناك", + "أشّر", "اشّر", "أشر بأصبعك", "أشر بصبعك", "اشر بصبعك", + "شاور", "شاورلي", "أشاور", "شاور هناك", + "ورّيني", "وريني", "ورجيني", + "بصبعك", "حدد", "حدد بصبعك" + ] }, "bot_phrases": { "english": ["pointing"], - "arabic": ["أشير"] + "arabic": [ + "أشير", + "بشاور", "بأشر", "بشير", "أشّرت" + ] } }, @@ -194,11 +327,23 @@ "canonical": "come here", "user_phrases": { "english": ["come here", "come to me", "come closer", "approach", "get closer", "come over here", "come"], - "arabic": ["تعال", "تعال هنا", "تعال إليّ", "اقترب", "تقرب"] + "arabic": [ + "تعال", "تعال هنا", "تعال إليّ", "اقترب", "تقرب", + "تعالى", "تعالى هنا", "تعالى لي", "تعالى عندي", + "تعا", "تعا لهون", "تعا عندي", + "أجي", "أرواح", "ارواحلي", + "قرّب", "قرب", "قرّب لي", "قرّب مني", "قرب مني", + "گرّب", "گرب لي", + "يلا تعال", "يا الله تعال", "تعال جنبي", "خش جنبي", + "تعال يا سند", "تعال بسرعة", "هلم" + ] }, "bot_phrases": { "english": ["coming over", "coming to you", "approaching"], - "arabic": ["آتي إليك", "أقترب", "أتقرّب"] + "arabic": [ + "آتي إليك", "أقترب", "أتقرّب", + "بجي", "جاي", "جاي لك", "بقرّب", "أقرّب منك" + ] } }, @@ -206,11 +351,23 @@ "canonical": "follow me", "user_phrases": { "english": ["follow me", "come with me", "walk with me"], - "arabic": ["اتبعني", "تعال معي", "امش معي"] + "arabic": [ + "اتبعني", "تعال معي", "امش معي", + "تعال ورايا", "تعال ورائي", "تعا ورايي", + "تعال ويايا", "تعال وياي", + "خش ورايا", "خليك ورايا", + "يلا معي", "يلا معاي", "يا الله معي", + "روح ويايا", "امشي معاي", "امشي معي", + "خليك معي", "ضل معي", "ضل ورايا", + "تعال خلفي", "خش خلفي", "اتبعني يا سند" + ] }, "bot_phrases": { "english": ["following you", "following", "coming with you"], - "arabic": ["أتبعك", "آتي معك", "أمشي معك"] + "arabic": [ + "أتبعك", "آتي معك", "أمشي معك", + "بتبعك", "جاي ورايك", "بمشي معاك", "ماشي معاك" + ] } }, @@ -218,11 +375,22 @@ "canonical": "stay here", "user_phrases": { "english": ["stay here", "stay", "wait here", "hold position", "don't follow me"], - "arabic": ["ابق هنا", "اثبت هنا", "انتظر هنا", "لا تتبعني"] + "arabic": [ + "ابق هنا", "اثبت هنا", "انتظر هنا", "لا تتبعني", + "خليك هنا", "خليك هون", "خلك هنا", "خل هنا", + "فضل هنا", "ضل هنا", "ضل هون", "ابقى هنا", + "اصبر هنا", "استنى هنا", "تو هنا", + "ثبات هنا", "اثبت بمكانك", "ابق بمكانك", + "ما تتبعني", "ما تجي ورايا", "ما تمشي معي", + "خليك بمحلك", "ضل بمحلك" + ] }, "bot_phrases": { "english": ["staying here", "staying", "waiting here"], - "arabic": ["أبقى هنا", "أنتظر هنا", "أثبت هنا"] + "arabic": [ + "أبقى هنا", "أنتظر هنا", "أثبت هنا", + "باقي هنا", "بضل هنا", "بستنى هنا", "بقعد هنا" + ] } }, @@ -230,11 +398,21 @@ "canonical": "go home", "user_phrases": { "english": ["go home", "return home", "head home", "go back home"], - "arabic": ["اذهب للبيت", "اذهب إلى البيت", "ارجع للبيت", "عُد للبيت"] + "arabic": [ + "اذهب للبيت", "اذهب إلى البيت", "ارجع للبيت", "عُد للبيت", + "روح البيت", "روح للبيت", "روح ع البيت", "روح عالبيت", "روح بيت", + "ارجع البيت", "ارجاع البيت", "ارجع عالبيت", "ارجع ع البيت", + "خش البيت", "اطلع البيت", + "ارجع للقاعدة", "روح للقاعدة", "اتجه للبيت", "اتجه للقاعدة", + "عد للبيت", "عود للبيت" + ] }, "bot_phrases": { "english": ["going home", "heading home", "returning home"], - "arabic": ["أعود للبيت", "أذهب للبيت", "أتجه للبيت"] + "arabic": [ + "أعود للبيت", "أذهب للبيت", "أتجه للبيت", + "براح البيت", "راجع البيت", "بروح للبيت", "أرجع للبيت" + ] } }, @@ -242,11 +420,20 @@ "canonical": "patrol", "user_phrases": { "english": ["patrol", "start patrol", "begin patrol", "patrol the area", "walk the route"], - "arabic": ["طوف", "ابدأ الدورية", "ابدأ التطواف", "افحص المكان"] + "arabic": [ + "طوف", "ابدأ الدورية", "ابدأ التطواف", + "دور بالمكان", "دور حواليك", "دور حوالينا", "لف بالمكان", "لف في المكان", + "تجول", "ابدأ التجوال", "تجول بالمكان", + "حراسة", "ابدأ الحراسة", "احرس المكان", + "طوف بالمكان", "تطوّف", "روح طوف" + ] }, "bot_phrases": { "english": ["patrolling", "starting patrol", "beginning patrol"], - "arabic": ["أطوف", "أبدأ الدورية", "أبدأ التطواف"] + "arabic": [ + "أطوف", "أبدأ الدورية", "أبدأ التطواف", + "بطوف", "بدور بالمكان", "ببدأ الدورية", "أتجول" + ] } }, @@ -254,11 +441,21 @@ "canonical": "look around", "user_phrases": { "english": ["look around", "scan the room", "scan around", "survey the area", "have a look around"], - "arabic": ["انظر حولك", "تفحص المكان", "افحص المكان", "تطلع حولك"] + "arabic": [ + "انظر حولك", "تفحص المكان", "افحص المكان", "تطلع حولك", + "شوف حولك", "شوف حواليك", "شوف الغرفة", "شوف اللي حواليك", + "بص حواليك", "بص حولك", "بص في الغرفة", + "طلّ حواليك", "طلّ حولك", "تطلّع حواليك", "تطلّع حولك", + "افحص الغرفة", "افحص الموقع", "افحص اللي حولك", + "اسكان المكان", "امسح المكان", "امسح بنظرك" + ] }, "bot_phrases": { "english": ["looking around", "scanning around", "surveying the area"], - "arabic": ["أنظر حولي", "أتفحص المكان", "أتطلع حولي"] + "arabic": [ + "أنظر حولي", "أتفحص المكان", "أتطلع حولي", + "بشوف حواليّ", "بطلّ حواليّ", "بفحص المكان", "بمسح المكان" + ] } }, @@ -266,12 +463,96 @@ "canonical": "what do you see", "user_phrases": { "english": ["what do you see", "what can you see", "describe this", "describe what you see", "tell me what you see"], - "arabic": ["ماذا ترى", "ماذا تشاهد", "صف ما تراه", "أخبرني ماذا ترى"] + "arabic": [ + "ماذا ترى", "ماذا تشاهد", "صف ما تراه", "أخبرني ماذا ترى", + "شو شايف", "شو بتشوف", "شو عم تشوف", "شو تشوف", + "شنو تشوف", "شنو شايف", "شنو هذا", + "إيش تشوف", "ايش تشوف", "ايش شايف", "إيش شايف", + "وش تشوف", "وش شايف", "وش هذا", + "شايف إيه", "بتشوف إيه", "إيه اللي قدامك", "إيه اللي شايفه", "إيه ده", + "اشنو كاتشوف", "اشنو كاتشوف قدامك", + "صف لي اللي قدامك", "قول لي شو شايف", "وصف لي اللي قدامك", "وصفلي اللي قدامك", + "احكي لي اللي شايفه", "احكيلي شو شايف", "قولي شو شايف" + ] }, "bot_phrases": { "english": [], "arabic": [] } } - } + }, + + "_parametric_description": "Motion confirmations from Gemini that carry a NUMBER (turn 90 degrees, walk 5 steps, walk 2 meters). Each entry is regex-matched against Gemini's spoken reply; the captured groups are substituted into command_template via $1, $2 placeholders, and the resulting command_template string is dispatched to the brain — Brain/command_parser.py already parses 'turn left 90 degrees' / 'walk forward 5 steps' / 'walk 2 meters' natively. Order matters: more-specific patterns (with direction) MUST come before less-specific (no direction) so they claim spans first.", + "parametric_actions": [ + { + "_": "turn left/right N degrees — 'Turning right 90 degrees.'", + "regex": "(?:turn(?:ing)?|spin(?:ning)?|rotat(?:e|ing))\\s+(left|right)\\s+(\\d+)\\s*deg(?:ree(?:s)?)?", + "command_template": "turn $1 $2 degrees" + }, + { + "_": "turn N degrees — 'Turning 360 degrees.'", + "regex": "(?:turn(?:ing)?|spin(?:ning)?|rotat(?:e|ing))\\s+(\\d+)\\s*deg(?:ree(?:s)?)?", + "command_template": "turn $1 degrees" + }, + { + "_": "walk forward/back N steps — 'Walking forward 5 steps.'", + "regex": "(?:walk(?:ing)?|step(?:ping)?|mov(?:e|ing))\\s+(forward|back(?:ward)?)\\s+(\\d+)\\s+steps?", + "command_template": "walk $1 $2 steps" + }, + { + "_": "walk N steps forward/back — 'Walking 5 steps forward.'", + "regex": "(?:walk(?:ing)?|step(?:ping)?|tak(?:e|ing))\\s+(\\d+)\\s+steps?\\s+(forward|back(?:ward)?)", + "command_template": "walk $2 $1 steps" + }, + { + "_": "walk N steps (default forward) — 'Taking 5 steps.'", + "regex": "(?:walk(?:ing)?|step(?:ping)?|tak(?:e|ing))\\s+(\\d+)\\s+steps?", + "command_template": "walk $1 steps" + }, + { + "_": "walk forward/back N meters — 'Walking forward 2 meters.'", + "regex": "(?:walk(?:ing)?|mov(?:e|ing))\\s+(forward|back(?:ward)?)\\s+(\\d+(?:\\.\\d+)?)\\s*m(?:eter(?:s)?)?\\b", + "command_template": "walk $1 $2 meters" + }, + { + "_": "walk N meters forward/back — 'Walking 2 meters forward.'", + "regex": "(?:walk(?:ing)?|mov(?:e|ing))\\s+(\\d+(?:\\.\\d+)?)\\s*m(?:eter(?:s)?)?\\s+(forward|back(?:ward)?)", + "command_template": "walk $2 $1 meters" + }, + { + "_": "walk N meters (default forward) — 'Walking 2 meters.'", + "regex": "(?:walk(?:ing)?|mov(?:e|ing))\\s+(\\d+(?:\\.\\d+)?)\\s*m(?:eter(?:s)?)?\\b", + "command_template": "walk $1 meters" + }, + { + "_": "Arabic: 'أستدير يميناً 90 درجة' / 'أستدير يمينا 90 درجة'", + "regex": "أستدير\\s+يمين[اًا]?ً?\\s+(\\d+)\\s*درجة", + "command_template": "turn right $1 degrees" + }, + { + "_": "Arabic: 'أستدير يساراً 90 درجة'", + "regex": "أستدير\\s+يسار[اًا]?ً?\\s+(\\d+)\\s*درجة", + "command_template": "turn left $1 degrees" + }, + { + "_": "Arabic: 'أستدير 360 درجة' (no direction)", + "regex": "أستدير\\s+(\\d+)\\s*درجة", + "command_template": "turn $1 degrees" + }, + { + "_": "Arabic: 'أمشي/أتحرك 5 خطوات'", + "regex": "(?:أمشي|أتحرك)\\s+(\\d+)\\s*خطو(?:ات|ة|تين)?", + "command_template": "walk $1 steps" + }, + { + "_": "Arabic: 'أتحرك للأمام مترين' / 'أمشي للأمام 2 متر'", + "regex": "(?:أمشي|أتحرك)\\s+للأمام\\s+(\\d+(?:\\.\\d+)?)\\s*متر", + "command_template": "walk forward $1 meters" + }, + { + "_": "Arabic: 'أتحرك للخلف مترين'", + "regex": "(?:أمشي|أتحرك)\\s+للخلف\\s+(\\d+(?:\\.\\d+)?)\\s*متر", + "command_template": "walk backward $1 meters" + } + ] } diff --git a/Data/Brain/Sessions/session_001_2026-04-27/commands.json b/Data/Brain/Sessions/session_001_2026-04-27/commands.json index ca8071d..0637a08 100644 --- a/Data/Brain/Sessions/session_001_2026-04-27/commands.json +++ b/Data/Brain/Sessions/session_001_2026-04-27/commands.json @@ -1,128 +1 @@ -[ - { - "time": "08:59:54", - "cmd": "turn right", - "response": "local command", - "duration_s": 0.0 - }, - { - "time": "09:00:04", - "cmd": "turn left", - "response": "local command", - "duration_s": 0.0 - }, - { - "time": "09:00:48", - "cmd": "turn left", - "response": "local command", - "duration_s": 0.0 - }, - { - "time": "09:02:00", - "cmd": "turn right", - "response": "local command", - "duration_s": 0.0 - }, - { - "time": "09:02:07", - "cmd": "turn right", - "response": "local command", - "duration_s": 0.0 - }, - { - "time": "09:02:26", - "cmd": "turn right", - "response": "local command", - "duration_s": 0.0 - }, - { - "time": "09:03:37", - "cmd": "turn left", - "response": "local command", - "duration_s": 0.0 - }, - { - "time": "09:03:53", - "cmd": "move forward", - "response": "local command", - "duration_s": 0.0 - }, - { - "time": "09:04:00", - "cmd": "move backward", - "response": "local command", - "duration_s": 0.0 - }, - { - "time": "09:04:38", - "cmd": "turn left", - "response": "local command", - "duration_s": 0.0 - }, - { - "time": "09:04:47", - "cmd": "turn right", - "response": "local command", - "duration_s": 0.0 - }, - { - "time": "09:05:09", - "cmd": "turn right", - "response": "local command", - "duration_s": 0.0 - }, - { - "time": "09:05:18", - "cmd": "turn left", - "response": "local command", - "duration_s": 0.0 - }, - { - "time": "09:05:21", - "cmd": "turn right", - "response": "local command", - "duration_s": 0.0 - }, - { - "time": "09:05:24", - "cmd": "move forward", - "response": "local command", - "duration_s": 0.0 - }, - { - "time": "09:05:31", - "cmd": "move backward", - "response": "local command", - "duration_s": 0.0 - }, - { - "time": "09:05:38", - "cmd": "turn left", - "response": "local command", - "duration_s": 0.0 - }, - { - "time": "09:06:40", - "cmd": "move forward", - "response": "local command", - "duration_s": 0.0 - }, - { - "time": "09:07:48", - "cmd": "move backward", - "response": "local command", - "duration_s": 0.0 - }, - { - "time": "09:07:57", - "cmd": "move backward", - "response": "local command", - "duration_s": 0.0 - }, - { - "time": "09:09:04", - "cmd": "turn right", - "response": "local command", - "duration_s": 0.0 - } -] \ No newline at end of file +[] \ No newline at end of file diff --git a/Data/Voice/Recordings/gemini_turns/20260427_085937_robot.wav b/Data/Voice/Recordings/gemini_turns/20260427_085937_robot.wav deleted file mode 100644 index a1f47d0..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_085937_robot.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_085937_user.wav b/Data/Voice/Recordings/gemini_turns/20260427_085937_user.wav deleted file mode 100644 index 659f25c..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_085937_user.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_085952_robot.wav b/Data/Voice/Recordings/gemini_turns/20260427_085952_robot.wav deleted file mode 100644 index d53c9b5..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_085952_robot.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_085952_user.wav b/Data/Voice/Recordings/gemini_turns/20260427_085952_user.wav deleted file mode 100644 index b34d041..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_085952_user.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090002_user.wav b/Data/Voice/Recordings/gemini_turns/20260427_090002_user.wav deleted file mode 100644 index 4b227f2..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090002_user.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090003_robot.wav b/Data/Voice/Recordings/gemini_turns/20260427_090003_robot.wav deleted file mode 100644 index 9e9b537..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090003_robot.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090003_user.wav b/Data/Voice/Recordings/gemini_turns/20260427_090003_user.wav deleted file mode 100644 index 9ce9bf4..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090003_user.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090020_robot.wav b/Data/Voice/Recordings/gemini_turns/20260427_090020_robot.wav deleted file mode 100644 index 969b3c2..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090020_robot.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090020_user.wav b/Data/Voice/Recordings/gemini_turns/20260427_090020_user.wav deleted file mode 100644 index 0bd8bbb..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090020_user.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090042_robot.wav b/Data/Voice/Recordings/gemini_turns/20260427_090042_robot.wav deleted file mode 100644 index cd54382..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090042_robot.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090042_user.wav b/Data/Voice/Recordings/gemini_turns/20260427_090042_user.wav deleted file mode 100644 index 2734d62..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090042_user.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090047_user.wav b/Data/Voice/Recordings/gemini_turns/20260427_090047_user.wav deleted file mode 100644 index 261aa2a..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090047_user.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090157_robot.wav b/Data/Voice/Recordings/gemini_turns/20260427_090157_robot.wav deleted file mode 100644 index 7772c7d..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090157_robot.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090157_user.wav b/Data/Voice/Recordings/gemini_turns/20260427_090157_user.wav deleted file mode 100644 index 7b3fda3..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090157_user.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090206_robot.wav b/Data/Voice/Recordings/gemini_turns/20260427_090206_robot.wav deleted file mode 100644 index 935dbd6..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090206_robot.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090206_user.wav b/Data/Voice/Recordings/gemini_turns/20260427_090206_user.wav deleted file mode 100644 index 5c8c670..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090206_user.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090224_robot.wav b/Data/Voice/Recordings/gemini_turns/20260427_090224_robot.wav deleted file mode 100644 index bdcc420..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090224_robot.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090224_user.wav b/Data/Voice/Recordings/gemini_turns/20260427_090224_user.wav deleted file mode 100644 index 12a034d..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090224_user.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090336_robot.wav b/Data/Voice/Recordings/gemini_turns/20260427_090336_robot.wav deleted file mode 100644 index 5ba5242..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090336_robot.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090336_user.wav b/Data/Voice/Recordings/gemini_turns/20260427_090336_user.wav deleted file mode 100644 index 2b3668c..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090336_user.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090346_robot.wav b/Data/Voice/Recordings/gemini_turns/20260427_090346_robot.wav deleted file mode 100644 index 7ea6656..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090346_robot.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090346_user.wav b/Data/Voice/Recordings/gemini_turns/20260427_090346_user.wav deleted file mode 100644 index bddc513..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090346_user.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090352_robot.wav b/Data/Voice/Recordings/gemini_turns/20260427_090352_robot.wav deleted file mode 100644 index 03f5045..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090352_robot.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090352_user.wav b/Data/Voice/Recordings/gemini_turns/20260427_090352_user.wav deleted file mode 100644 index e13e5fe..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090352_user.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090359_robot.wav b/Data/Voice/Recordings/gemini_turns/20260427_090359_robot.wav deleted file mode 100644 index b0a4606..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090359_robot.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090359_user.wav b/Data/Voice/Recordings/gemini_turns/20260427_090359_user.wav deleted file mode 100644 index 8c6bba6..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090359_user.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090412_robot.wav b/Data/Voice/Recordings/gemini_turns/20260427_090412_robot.wav deleted file mode 100644 index 01c89c0..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090412_robot.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090412_user.wav b/Data/Voice/Recordings/gemini_turns/20260427_090412_user.wav deleted file mode 100644 index 94a02b5..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090412_user.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090419_robot.wav b/Data/Voice/Recordings/gemini_turns/20260427_090419_robot.wav deleted file mode 100644 index 605f668..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090419_robot.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090419_user.wav b/Data/Voice/Recordings/gemini_turns/20260427_090419_user.wav deleted file mode 100644 index b55084e..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090419_user.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090437_robot.wav b/Data/Voice/Recordings/gemini_turns/20260427_090437_robot.wav deleted file mode 100644 index ad32348..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090437_robot.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090437_user.wav b/Data/Voice/Recordings/gemini_turns/20260427_090437_user.wav deleted file mode 100644 index a01e24d..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090437_user.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090446_robot.wav b/Data/Voice/Recordings/gemini_turns/20260427_090446_robot.wav deleted file mode 100644 index a8c8cc7..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090446_robot.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090446_user.wav b/Data/Voice/Recordings/gemini_turns/20260427_090446_user.wav deleted file mode 100644 index 66a6106..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090446_user.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090459_robot.wav b/Data/Voice/Recordings/gemini_turns/20260427_090459_robot.wav deleted file mode 100644 index 34d6da2..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090459_robot.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090459_user.wav b/Data/Voice/Recordings/gemini_turns/20260427_090459_user.wav deleted file mode 100644 index 9d2f975..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090459_user.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090508_robot.wav b/Data/Voice/Recordings/gemini_turns/20260427_090508_robot.wav deleted file mode 100644 index cadd4b6..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090508_robot.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090508_user.wav b/Data/Voice/Recordings/gemini_turns/20260427_090508_user.wav deleted file mode 100644 index afca174..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090508_user.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090516_robot.wav b/Data/Voice/Recordings/gemini_turns/20260427_090516_robot.wav deleted file mode 100644 index 1a29a76..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090516_robot.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090516_user.wav b/Data/Voice/Recordings/gemini_turns/20260427_090516_user.wav deleted file mode 100644 index 296406a..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090516_user.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090519_robot.wav b/Data/Voice/Recordings/gemini_turns/20260427_090519_robot.wav deleted file mode 100644 index c260de6..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090519_robot.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090519_user.wav b/Data/Voice/Recordings/gemini_turns/20260427_090519_user.wav deleted file mode 100644 index 32f6217..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090519_user.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090522_user.wav b/Data/Voice/Recordings/gemini_turns/20260427_090522_user.wav deleted file mode 100644 index 649e583..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090522_user.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090523_robot.wav b/Data/Voice/Recordings/gemini_turns/20260427_090523_robot.wav deleted file mode 100644 index d688a9f..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090523_robot.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090523_user.wav b/Data/Voice/Recordings/gemini_turns/20260427_090523_user.wav deleted file mode 100644 index bafd787..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090523_user.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090530_robot.wav b/Data/Voice/Recordings/gemini_turns/20260427_090530_robot.wav deleted file mode 100644 index e672fd3..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090530_robot.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090530_user.wav b/Data/Voice/Recordings/gemini_turns/20260427_090530_user.wav deleted file mode 100644 index 0e7503a..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090530_user.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090537_robot.wav b/Data/Voice/Recordings/gemini_turns/20260427_090537_robot.wav deleted file mode 100644 index 60db1ce..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090537_robot.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090537_user.wav b/Data/Voice/Recordings/gemini_turns/20260427_090537_user.wav deleted file mode 100644 index 1a7bef3..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090537_user.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090552_robot.wav b/Data/Voice/Recordings/gemini_turns/20260427_090552_robot.wav deleted file mode 100644 index 2e03034..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090552_robot.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090552_user.wav b/Data/Voice/Recordings/gemini_turns/20260427_090552_user.wav deleted file mode 100644 index b77ee6e..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090552_user.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090601_user.wav b/Data/Voice/Recordings/gemini_turns/20260427_090601_user.wav deleted file mode 100644 index ad0929a..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090601_user.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090607_user.wav b/Data/Voice/Recordings/gemini_turns/20260427_090607_user.wav deleted file mode 100644 index 72266d0..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090607_user.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090610_robot.wav b/Data/Voice/Recordings/gemini_turns/20260427_090610_robot.wav deleted file mode 100644 index 8a1c24e..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090610_robot.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090610_user.wav b/Data/Voice/Recordings/gemini_turns/20260427_090610_user.wav deleted file mode 100644 index 59c9b75..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090610_user.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090616_robot.wav b/Data/Voice/Recordings/gemini_turns/20260427_090616_robot.wav deleted file mode 100644 index 1cea3fe..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090616_robot.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090616_user.wav b/Data/Voice/Recordings/gemini_turns/20260427_090616_user.wav deleted file mode 100644 index 58b05ea..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090616_user.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090632_robot.wav b/Data/Voice/Recordings/gemini_turns/20260427_090632_robot.wav deleted file mode 100644 index 689251a..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090632_robot.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090632_user.wav b/Data/Voice/Recordings/gemini_turns/20260427_090632_user.wav deleted file mode 100644 index 09a4623..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090632_user.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090640_user.wav b/Data/Voice/Recordings/gemini_turns/20260427_090640_user.wav deleted file mode 100644 index 16b341e..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090640_user.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090656_user.wav b/Data/Voice/Recordings/gemini_turns/20260427_090656_user.wav deleted file mode 100644 index 44cfc09..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090656_user.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090702_user.wav b/Data/Voice/Recordings/gemini_turns/20260427_090702_user.wav deleted file mode 100644 index efefafb..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090702_user.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090722_robot.wav b/Data/Voice/Recordings/gemini_turns/20260427_090722_robot.wav deleted file mode 100644 index d1787f6..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090722_robot.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090722_user.wav b/Data/Voice/Recordings/gemini_turns/20260427_090722_user.wav deleted file mode 100644 index aad5110..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090722_user.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090728_robot.wav b/Data/Voice/Recordings/gemini_turns/20260427_090728_robot.wav deleted file mode 100644 index 1bee4f3..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090728_robot.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090728_user.wav b/Data/Voice/Recordings/gemini_turns/20260427_090728_user.wav deleted file mode 100644 index c7b4a8b..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090728_user.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090729_robot.wav b/Data/Voice/Recordings/gemini_turns/20260427_090729_robot.wav deleted file mode 100644 index 10e8cd4..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090729_robot.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090729_user.wav b/Data/Voice/Recordings/gemini_turns/20260427_090729_user.wav deleted file mode 100644 index fe18495..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090729_user.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090732_user.wav b/Data/Voice/Recordings/gemini_turns/20260427_090732_user.wav deleted file mode 100644 index e613c61..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090732_user.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090733_robot.wav b/Data/Voice/Recordings/gemini_turns/20260427_090733_robot.wav deleted file mode 100644 index 2a9591b..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090733_robot.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090733_user.wav b/Data/Voice/Recordings/gemini_turns/20260427_090733_user.wav deleted file mode 100644 index 37cc213..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090733_user.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090748_robot.wav b/Data/Voice/Recordings/gemini_turns/20260427_090748_robot.wav deleted file mode 100644 index cfc975e..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090748_robot.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090748_user.wav b/Data/Voice/Recordings/gemini_turns/20260427_090748_user.wav deleted file mode 100644 index b617d9d..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090748_user.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090757_user.wav b/Data/Voice/Recordings/gemini_turns/20260427_090757_user.wav deleted file mode 100644 index 3614d58..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090757_user.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090805_robot.wav b/Data/Voice/Recordings/gemini_turns/20260427_090805_robot.wav deleted file mode 100644 index fed7e1e..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090805_robot.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090805_user.wav b/Data/Voice/Recordings/gemini_turns/20260427_090805_user.wav deleted file mode 100644 index 795ed8c..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090805_user.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090811_user.wav b/Data/Voice/Recordings/gemini_turns/20260427_090811_user.wav deleted file mode 100644 index bb23875..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090811_user.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090812_user.wav b/Data/Voice/Recordings/gemini_turns/20260427_090812_user.wav deleted file mode 100644 index fe72071..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090812_user.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090830_user.wav b/Data/Voice/Recordings/gemini_turns/20260427_090830_user.wav deleted file mode 100644 index 1be1150..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090830_user.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090835_robot.wav b/Data/Voice/Recordings/gemini_turns/20260427_090835_robot.wav deleted file mode 100644 index 6323133..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090835_robot.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090835_user.wav b/Data/Voice/Recordings/gemini_turns/20260427_090835_user.wav deleted file mode 100644 index 7c0f5da..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090835_user.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090842_user.wav b/Data/Voice/Recordings/gemini_turns/20260427_090842_user.wav deleted file mode 100644 index 67dd89d..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090842_user.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090843_user.wav b/Data/Voice/Recordings/gemini_turns/20260427_090843_user.wav deleted file mode 100644 index dc2e543..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090843_user.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090847_robot.wav b/Data/Voice/Recordings/gemini_turns/20260427_090847_robot.wav deleted file mode 100644 index 4603336..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090847_robot.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090847_user.wav b/Data/Voice/Recordings/gemini_turns/20260427_090847_user.wav deleted file mode 100644 index 9eba478..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090847_user.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090856_robot.wav b/Data/Voice/Recordings/gemini_turns/20260427_090856_robot.wav deleted file mode 100644 index da4f210..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090856_robot.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090856_user.wav b/Data/Voice/Recordings/gemini_turns/20260427_090856_user.wav deleted file mode 100644 index 4b59ae6..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090856_user.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090904_user.wav b/Data/Voice/Recordings/gemini_turns/20260427_090904_user.wav deleted file mode 100644 index e7e9b5f..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090904_user.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090910_robot.wav b/Data/Voice/Recordings/gemini_turns/20260427_090910_robot.wav deleted file mode 100644 index 81259e2..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090910_robot.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090910_user.wav b/Data/Voice/Recordings/gemini_turns/20260427_090910_user.wav deleted file mode 100644 index c9a54fa..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090910_user.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090915_robot.wav b/Data/Voice/Recordings/gemini_turns/20260427_090915_robot.wav deleted file mode 100644 index 5f574fd..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090915_robot.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090915_user.wav b/Data/Voice/Recordings/gemini_turns/20260427_090915_user.wav deleted file mode 100644 index 472c188..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090915_user.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090919_robot.wav b/Data/Voice/Recordings/gemini_turns/20260427_090919_robot.wav deleted file mode 100644 index 6980519..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090919_robot.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090919_user.wav b/Data/Voice/Recordings/gemini_turns/20260427_090919_user.wav deleted file mode 100644 index 9161c7a..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090919_user.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090927_user.wav b/Data/Voice/Recordings/gemini_turns/20260427_090927_user.wav deleted file mode 100644 index f47cafd..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090927_user.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090928_robot.wav b/Data/Voice/Recordings/gemini_turns/20260427_090928_robot.wav deleted file mode 100644 index 40efa2e..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090928_robot.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090928_user.wav b/Data/Voice/Recordings/gemini_turns/20260427_090928_user.wav deleted file mode 100644 index e97660e..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090928_user.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090940_robot.wav b/Data/Voice/Recordings/gemini_turns/20260427_090940_robot.wav deleted file mode 100644 index 3b8cf67..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090940_robot.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090940_user.wav b/Data/Voice/Recordings/gemini_turns/20260427_090940_user.wav deleted file mode 100644 index 68f002c..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090940_user.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090941_user.wav b/Data/Voice/Recordings/gemini_turns/20260427_090941_user.wav deleted file mode 100644 index c637c3b..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090941_user.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090946_robot.wav b/Data/Voice/Recordings/gemini_turns/20260427_090946_robot.wav deleted file mode 100644 index 638f67a..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090946_robot.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090946_user.wav b/Data/Voice/Recordings/gemini_turns/20260427_090946_user.wav deleted file mode 100644 index d333e9f..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090946_user.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090957_robot.wav b/Data/Voice/Recordings/gemini_turns/20260427_090957_robot.wav deleted file mode 100644 index 71b6700..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090957_robot.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090957_user.wav b/Data/Voice/Recordings/gemini_turns/20260427_090957_user.wav deleted file mode 100644 index f055312..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090957_user.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_090958_user.wav b/Data/Voice/Recordings/gemini_turns/20260427_090958_user.wav deleted file mode 100644 index b145284..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_090958_user.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_091001_robot.wav b/Data/Voice/Recordings/gemini_turns/20260427_091001_robot.wav deleted file mode 100644 index f0cf406..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_091001_robot.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_091001_user.wav b/Data/Voice/Recordings/gemini_turns/20260427_091001_user.wav deleted file mode 100644 index 575cbff..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_091001_user.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_091016_robot.wav b/Data/Voice/Recordings/gemini_turns/20260427_091016_robot.wav deleted file mode 100644 index fe959ce..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_091016_robot.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_091016_user.wav b/Data/Voice/Recordings/gemini_turns/20260427_091016_user.wav deleted file mode 100644 index cd1ccf6..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_091016_user.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_091017_robot.wav b/Data/Voice/Recordings/gemini_turns/20260427_091017_robot.wav deleted file mode 100644 index 03e5c81..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_091017_robot.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_091017_user.wav b/Data/Voice/Recordings/gemini_turns/20260427_091017_user.wav deleted file mode 100644 index 1438878..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_091017_user.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_091018_user.wav b/Data/Voice/Recordings/gemini_turns/20260427_091018_user.wav deleted file mode 100644 index 13fb5bb..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_091018_user.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_091022_user.wav b/Data/Voice/Recordings/gemini_turns/20260427_091022_user.wav deleted file mode 100644 index 2cd6813..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_091022_user.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_091031_user.wav b/Data/Voice/Recordings/gemini_turns/20260427_091031_user.wav deleted file mode 100644 index 936cd19..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_091031_user.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_091033_user.wav b/Data/Voice/Recordings/gemini_turns/20260427_091033_user.wav deleted file mode 100644 index f33b72a..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_091033_user.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_091036_user.wav b/Data/Voice/Recordings/gemini_turns/20260427_091036_user.wav deleted file mode 100644 index 44915d4..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_091036_user.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_091043_user.wav b/Data/Voice/Recordings/gemini_turns/20260427_091043_user.wav deleted file mode 100644 index c419b12..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_091043_user.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_091049_user.wav b/Data/Voice/Recordings/gemini_turns/20260427_091049_user.wav deleted file mode 100644 index 4acd2f7..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_091049_user.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_091051_user.wav b/Data/Voice/Recordings/gemini_turns/20260427_091051_user.wav deleted file mode 100644 index 9775d6f..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_091051_user.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_091054_robot.wav b/Data/Voice/Recordings/gemini_turns/20260427_091054_robot.wav deleted file mode 100644 index 5554731..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_091054_robot.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_091054_user.wav b/Data/Voice/Recordings/gemini_turns/20260427_091054_user.wav deleted file mode 100644 index 78a3160..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_091054_user.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_091058_robot.wav b/Data/Voice/Recordings/gemini_turns/20260427_091058_robot.wav deleted file mode 100644 index 699e000..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_091058_robot.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_091058_user.wav b/Data/Voice/Recordings/gemini_turns/20260427_091058_user.wav deleted file mode 100644 index a6e6af5..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_091058_user.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_091059_robot.wav b/Data/Voice/Recordings/gemini_turns/20260427_091059_robot.wav deleted file mode 100644 index 5c73dfd..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_091059_robot.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_091059_user.wav b/Data/Voice/Recordings/gemini_turns/20260427_091059_user.wav deleted file mode 100644 index e728f7d..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_091059_user.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_091100_robot.wav b/Data/Voice/Recordings/gemini_turns/20260427_091100_robot.wav deleted file mode 100644 index 495d94f..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_091100_robot.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_091100_user.wav b/Data/Voice/Recordings/gemini_turns/20260427_091100_user.wav deleted file mode 100644 index 6040355..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_091100_user.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_091104_user.wav b/Data/Voice/Recordings/gemini_turns/20260427_091104_user.wav deleted file mode 100644 index 4db9f0d..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_091104_user.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_091114_robot.wav b/Data/Voice/Recordings/gemini_turns/20260427_091114_robot.wav deleted file mode 100644 index 81c1465..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_091114_robot.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_091114_user.wav b/Data/Voice/Recordings/gemini_turns/20260427_091114_user.wav deleted file mode 100644 index 7129090..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_091114_user.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_091121_robot.wav b/Data/Voice/Recordings/gemini_turns/20260427_091121_robot.wav deleted file mode 100644 index ef811a3..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_091121_robot.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_091121_user.wav b/Data/Voice/Recordings/gemini_turns/20260427_091121_user.wav deleted file mode 100644 index f5d13ad..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_091121_user.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_091122_robot.wav b/Data/Voice/Recordings/gemini_turns/20260427_091122_robot.wav deleted file mode 100644 index c956d26..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_091122_robot.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_091122_user.wav b/Data/Voice/Recordings/gemini_turns/20260427_091122_user.wav deleted file mode 100644 index 41d9620..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_091122_user.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/20260427_091123_user.wav b/Data/Voice/Recordings/gemini_turns/20260427_091123_user.wav deleted file mode 100644 index 0f91db9..0000000 Binary files a/Data/Voice/Recordings/gemini_turns/20260427_091123_user.wav and /dev/null differ diff --git a/Data/Voice/Recordings/gemini_turns/index.json b/Data/Voice/Recordings/gemini_turns/index.json deleted file mode 100644 index 5c52b54..0000000 --- a/Data/Voice/Recordings/gemini_turns/index.json +++ /dev/null @@ -1,887 +0,0 @@ -{ - "records": [ - { - "timestamp": "20260427_085937", - "started_at": 1777251577.9944012, - "user_text": "", - "robot_text": "Turning right.", - "user_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_085937_user.wav", - "user_duration_sec": 1.056, - "robot_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_085937_robot.wav", - "robot_duration_sec": 0.88 - }, - { - "timestamp": "20260427_085952", - "started_at": 1777251592.5528038, - "user_text": "", - "robot_text": "", - "user_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_085952_user.wav", - "user_duration_sec": 0.128 - }, - { - "timestamp": "20260427_085952", - "started_at": 1777251592.7480435, - "user_text": "", - "robot_text": "Turning left.", - "user_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_085952_user.wav", - "user_duration_sec": 2.112, - "robot_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_085952_robot.wav", - "robot_duration_sec": 0.96 - }, - { - "timestamp": "20260427_090002", - "started_at": 1777251602.7470634, - "user_text": "", - "robot_text": "", - "user_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090002_user.wav", - "user_duration_sec": 0.032 - }, - { - "timestamp": "20260427_090003", - "started_at": 1777251603.1536026, - "user_text": "What do you think?", - "robot_text": "I see a black and grey backpack resting on a black container. To the left of that, there's a cardboard box with a blue package on top, all against a frosted glass wall.", - "user_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090003_user.wav", - "user_duration_sec": 1.216, - "robot_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090003_robot.wav", - "robot_duration_sec": 9.88 - }, - { - "timestamp": "20260427_090020", - "started_at": 1777251620.2809963, - "user_text": "", - "robot_text": "Sanad, turn left.", - "user_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090020_user.wav", - "user_duration_sec": 2.656, - "robot_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090020_robot.wav", - "robot_duration_sec": 1.48 - }, - { - "timestamp": "20260427_090042", - "started_at": 1777251642.4523458, - "user_text": "Non.", - "robot_text": "Turning left.", - "user_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090042_user.wav", - "user_duration_sec": 0.896, - "robot_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090042_robot.wav", - "robot_duration_sec": 1.0 - }, - { - "timestamp": "20260427_090047", - "started_at": 1777251647.9008353, - "user_text": "", - "robot_text": "Turning right.", - "user_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090047_user.wav", - "user_duration_sec": 7.232 - }, - { - "timestamp": "20260427_090157", - "started_at": 1777251717.7036119, - "user_text": "อ", - "robot_text": "Turning right.", - "user_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090157_user.wav", - "user_duration_sec": 1.44, - "robot_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090157_robot.wav", - "robot_duration_sec": 0.88 - }, - { - "timestamp": "20260427_090206", - "started_at": 1777251726.217288, - "user_text": ".", - "robot_text": "Turning right.", - "user_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090206_user.wav", - "user_duration_sec": 2.176, - "robot_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090206_robot.wav", - "robot_duration_sec": 0.84 - }, - { - "timestamp": "20260427_090224", - "started_at": 1777251744.4927664, - "user_text": "", - "robot_text": "", - "user_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090224_user.wav", - "user_duration_sec": 0.128 - }, - { - "timestamp": "20260427_090224", - "started_at": 1777251744.9639776, - "user_text": "แล้ว", - "robot_text": "Turning left.", - "user_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090224_user.wav", - "user_duration_sec": 12.48, - "robot_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090224_robot.wav", - "robot_duration_sec": 0.92 - }, - { - "timestamp": "20260427_090336", - "started_at": 1777251816.1296601, - "user_text": "Come", - "robot_text": "Okay. What's next?", - "user_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090336_user.wav", - "user_duration_sec": 1.248, - "robot_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090336_robot.wav", - "robot_duration_sec": 1.32 - }, - { - "timestamp": "20260427_090346", - "started_at": 1777251826.7168398, - "user_text": ".", - "robot_text": "Moving forward.", - "user_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090346_user.wav", - "user_duration_sec": 0.992, - "robot_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090346_robot.wav", - "robot_duration_sec": 1.04 - }, - { - "timestamp": "20260427_090352", - "started_at": 1777251832.4164839, - "user_text": "Do.", - "robot_text": "Moving backward.", - "user_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090352_user.wav", - "user_duration_sec": 0.928, - "robot_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090352_robot.wav", - "robot_duration_sec": 1.0 - }, - { - "timestamp": "20260427_090359", - "started_at": 1777251839.9720776, - "user_text": "What do you see?", - "robot_text": "I see a blurred silhouette of a person through the frosted glass wall.", - "user_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090359_user.wav", - "user_duration_sec": 1.056, - "robot_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090359_robot.wav", - "robot_duration_sec": 4.28 - }, - { - "timestamp": "20260427_090412", - "started_at": 1777251852.6062336, - "user_text": "What did he doing?", - "robot_text": "The silhouette looks like they are standing still.", - "user_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090412_user.wav", - "user_duration_sec": 0.864, - "robot_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090412_robot.wav", - "robot_duration_sec": 3.08 - }, - { - "timestamp": "20260427_090419", - "started_at": 1777251859.8165183, - "user_text": "", - "robot_text": "Turning left.", - "user_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090419_user.wav", - "user_duration_sec": 1.888, - "robot_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090419_robot.wav", - "robot_duration_sec": 1.0 - }, - { - "timestamp": "20260427_090437", - "started_at": 1777251877.9682913, - "user_text": "", - "robot_text": "Turning right.", - "user_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090437_user.wav", - "user_duration_sec": 1.12, - "robot_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090437_robot.wav", - "robot_duration_sec": 0.84 - }, - { - "timestamp": "20260427_090446", - "started_at": 1777251886.566751, - "user_text": "", - "robot_text": "I see a person wearing a maroon t-shirt and jeans, holding a phone.", - "user_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090446_user.wav", - "user_duration_sec": 1.408, - "robot_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090446_robot.wav", - "robot_duration_sec": 4.44 - }, - { - "timestamp": "20260427_090459", - "started_at": 1777251899.2506564, - "user_text": "", - "robot_text": "Turning right.", - "user_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090459_user.wav", - "user_duration_sec": 1.536, - "robot_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090459_robot.wav", - "robot_duration_sec": 0.84 - }, - { - "timestamp": "20260427_090508", - "started_at": 1777251908.4730532, - "user_text": "", - "robot_text": "", - "user_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090508_user.wav", - "user_duration_sec": 0.032 - }, - { - "timestamp": "20260427_090508", - "started_at": 1777251908.5165405, - "user_text": "", - "robot_text": "Turning left.", - "user_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090508_user.wav", - "user_duration_sec": 2.304, - "robot_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090508_robot.wav", - "robot_duration_sec": 0.96 - }, - { - "timestamp": "20260427_090516", - "started_at": 1777251916.4680145, - "user_text": "", - "robot_text": "", - "user_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090516_user.wav", - "user_duration_sec": 0.16 - }, - { - "timestamp": "20260427_090516", - "started_at": 1777251916.6805983, - "user_text": "", - "robot_text": "Turning right.", - "user_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090516_user.wav", - "user_duration_sec": 1.056, - "robot_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090516_robot.wav", - "robot_duration_sec": 0.52 - }, - { - "timestamp": "20260427_090519", - "started_at": 1777251919.4270263, - "user_text": "Nou, een groot. ", - "robot_text": "Moving forward.", - "user_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090519_user.wav", - "user_duration_sec": 1.312, - "robot_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090519_robot.wav", - "robot_duration_sec": 1.04 - }, - { - "timestamp": "20260427_090522", - "started_at": 1777251922.3460593, - "user_text": "", - "robot_text": "", - "user_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090522_user.wav", - "user_duration_sec": 0.512 - }, - { - "timestamp": "20260427_090523", - "started_at": 1777251923.1038663, - "user_text": ". Go. Go. Go.", - "robot_text": "Moving backward.", - "user_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090523_user.wav", - "user_duration_sec": 1.696, - "robot_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090523_robot.wav", - "robot_duration_sec": 1.08 - }, - { - "timestamp": "20260427_090530", - "started_at": 1777251930.3607652, - "user_text": "The n left one step.", - "robot_text": "Turning left", - "user_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090530_user.wav", - "user_duration_sec": 1.44, - "robot_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090530_robot.wav", - "robot_duration_sec": 0.96 - }, - { - "timestamp": "20260427_090537", - "started_at": 1777251937.6848032, - "user_text": "what do you say now?", - "robot_text": "I see a person standing near a glass door with a metal handle. The person is holding a cup and a phone.", - "user_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090537_user.wav", - "user_duration_sec": 1.408, - "robot_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090537_robot.wav", - "robot_duration_sec": 6.64 - }, - { - "timestamp": "20260427_090552", - "started_at": 1777251952.6296594, - "user_text": "Reach the person.", - "robot_text": "Moving towards the person.", - "user_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090552_user.wav", - "user_duration_sec": 1.024, - "robot_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090552_robot.wav", - "robot_duration_sec": 1.4 - }, - { - "timestamp": "20260427_090601", - "started_at": 1777251961.8537927, - "user_text": ".", - "robot_text": "", - "user_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090601_user.wav", - "user_duration_sec": 1.984 - }, - { - "timestamp": "20260427_090607", - "started_at": 1777251967.7927585, - "user_text": "o la transferencia.", - "robot_text": "", - "user_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090607_user.wav", - "user_duration_sec": 0.512 - }, - { - "timestamp": "20260427_090610", - "started_at": 1777251970.1781626, - "user_text": "", - "robot_text": "Okay, I'm here now.", - "user_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090610_user.wav", - "user_duration_sec": 0.736, - "robot_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090610_robot.wav", - "robot_duration_sec": 1.4 - }, - { - "timestamp": "20260427_090616", - "started_at": 1777251976.7410944, - "user_text": "Can you see the person in front of you?", - "robot_text": "Yes, I can clearly see them.", - "user_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090616_user.wav", - "user_duration_sec": 5.376, - "robot_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090616_robot.wav", - "robot_duration_sec": 2.12 - }, - { - "timestamp": "20260427_090632", - "started_at": 1777251992.7623394, - "user_text": ".", - "robot_text": "Moving forward.", - "user_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090632_user.wav", - "user_duration_sec": 1.92, - "robot_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090632_robot.wav", - "robot_duration_sec": 1.08 - }, - { - "timestamp": "20260427_090640", - "started_at": 1777252000.1078405, - "user_text": "", - "robot_text": "", - "user_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090640_user.wav", - "user_duration_sec": 9.504 - }, - { - "timestamp": "20260427_090656", - "started_at": 1777252016.8462038, - "user_text": ".", - "robot_text": "", - "user_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090656_user.wav", - "user_duration_sec": 3.36 - }, - { - "timestamp": "20260427_090702", - "started_at": 1777252022.7362535, - "user_text": "", - "robot_text": "Understood. How", - "user_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090702_user.wav", - "user_duration_sec": 13.664 - }, - { - "timestamp": "20260427_090722", - "started_at": 1777252042.0906878, - "user_text": "", - "robot_text": "It seems you've come", - "user_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090722_user.wav", - "user_duration_sec": 3.648, - "robot_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090722_robot.wav", - "robot_duration_sec": 1.36 - }, - { - "timestamp": "20260427_090728", - "started_at": 1777252048.2146206, - "user_text": "", - "robot_text": "quite close, I can", - "user_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090728_user.wav", - "user_duration_sec": 0.032, - "robot_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090728_robot.wav", - "robot_duration_sec": 0.92 - }, - { - "timestamp": "20260427_090728", - "started_at": 1777252048.6483164, - "user_text": "", - "robot_text": "only see the front", - "user_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090728_user.wav", - "user_duration_sec": 0.032, - "robot_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090728_robot.wav", - "robot_duration_sec": 1.36 - }, - { - "timestamp": "20260427_090729", - "started_at": 1777252049.0833862, - "user_text": "", - "robot_text": "of a black shirt now.", - "user_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090729_user.wav", - "user_duration_sec": 0.032, - "robot_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090729_robot.wav", - "robot_duration_sec": 1.56 - }, - { - "timestamp": "20260427_090732", - "started_at": 1777252052.333645, - "user_text": "", - "robot_text": "", - "user_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090732_user.wav", - "user_duration_sec": 0.544 - }, - { - "timestamp": "20260427_090733", - "started_at": 1777252053.1307166, - "user_text": "Go backwards two steps.", - "robot_text": "Moving backward two steps.", - "user_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090733_user.wav", - "user_duration_sec": 1.856, - "robot_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090733_robot.wav", - "robot_duration_sec": 1.76 - }, - { - "timestamp": "20260427_090748", - "started_at": 1777252068.5899441, - "user_text": ".", - "robot_text": "Moving backward three steps.", - "user_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090748_user.wav", - "user_duration_sec": 2.112, - "robot_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090748_robot.wav", - "robot_duration_sec": 1.88 - }, - { - "timestamp": "20260427_090757", - "started_at": 1777252077.7019086, - "user_text": "", - "robot_text": "", - "user_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090757_user.wav", - "user_duration_sec": 1.984 - }, - { - "timestamp": "20260427_090805", - "started_at": 1777252085.0313835, - "user_text": "All right . The person that we see is his name is.", - "robot_text": "It's nice to meet you, Omar!", - "user_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090805_user.wav", - "user_duration_sec": 2.016, - "robot_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090805_robot.wav", - "robot_duration_sec": 1.68 - }, - { - "timestamp": "20260427_090811", - "started_at": 1777252091.4707015, - "user_text": "", - "robot_text": "", - "user_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090811_user.wav", - "user_duration_sec": 0.576 - }, - { - "timestamp": "20260427_090812", - "started_at": 1777252092.8878224, - "user_text": ". . ", - "robot_text": "", - "user_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090812_user.wav", - "user_duration_sec": 8.352 - }, - { - "timestamp": "20260427_090830", - "started_at": 1777252110.536339, - "user_text": "Yeah.", - "robot_text": "", - "user_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090830_user.wav", - "user_duration_sec": 2.368 - }, - { - "timestamp": "20260427_090835", - "started_at": 1777252115.0498679, - "user_text": "46 degree.", - "robot_text": "I'm sorry, I couldn't quite hear you. The audio was a bit clipped. Could you please repeat what you said?", - "user_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090835_user.wav", - "user_duration_sec": 1.184, - "robot_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090835_robot.wav", - "robot_duration_sec": 7.4 - }, - { - "timestamp": "20260427_090842", - "started_at": 1777252122.5312247, - "user_text": "", - "robot_text": "", - "user_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090842_user.wav", - "user_duration_sec": 0.992 - }, - { - "timestamp": "20260427_090843", - "started_at": 1777252123.9837608, - "user_text": "", - "robot_text": "", - "user_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090843_user.wav", - "user_duration_sec": 1.6 - }, - { - "timestamp": "20260427_090847", - "started_at": 1777252127.0211418, - "user_text": "pul rotations", - "robot_text": "I'm still having trouble understanding with the distorted audio. Could you please repeat that?", - "user_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090847_user.wav", - "user_duration_sec": 0.352, - "robot_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090847_robot.wav", - "robot_duration_sec": 5.6 - }, - { - "timestamp": "20260427_090856", - "started_at": 1777252136.6518312, - "user_text": "180", - "robot_text": "Turning right 180 degrees.", - "user_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090856_user.wav", - "user_duration_sec": 1.568, - "robot_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090856_robot.wav", - "robot_duration_sec": 1.88 - }, - { - "timestamp": "20260427_090904", - "started_at": 1777252144.8639987, - "user_text": ".", - "robot_text": "", - "user_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090904_user.wav", - "user_duration_sec": 2.784 - }, - { - "timestamp": "20260427_090910", - "started_at": 1777252150.2576299, - "user_text": "Keep sending once.", - "robot_text": "I've completed the 180-degree turn.", - "user_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090910_user.wav", - "user_duration_sec": 0.256, - "robot_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090910_robot.wav", - "robot_duration_sec": 1.96 - }, - { - "timestamp": "20260427_090915", - "started_at": 1777252155.5633724, - "user_text": "Make filter.", - "robot_text": "I can't do a \"full turn.\"", - "user_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090915_user.wav", - "user_duration_sec": 1.312, - "robot_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090915_robot.wav", - "robot_duration_sec": 1.32 - }, - { - "timestamp": "20260427_090919", - "started_at": 1777252159.8562431, - "user_text": "", - "robot_text": "I'm sorry, but I'm still having trouble understanding due to the audio. Could you please say that again?", - "user_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090919_user.wav", - "user_duration_sec": 0.704, - "robot_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090919_robot.wav", - "robot_duration_sec": 5.72 - }, - { - "timestamp": "20260427_090927", - "started_at": 1777252167.57357, - "user_text": "", - "robot_text": "", - "user_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090927_user.wav", - "user_duration_sec": 0.896 - }, - { - "timestamp": "20260427_090928", - "started_at": 1777252168.82067, - "user_text": "", - "robot_text": "The audio is still distorted, I'm not catching", - "user_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090928_user.wav", - "user_duration_sec": 6.464, - "robot_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090928_robot.wav", - "robot_duration_sec": 3.68 - }, - { - "timestamp": "20260427_090940", - "started_at": 1777252180.1934843, - "user_text": "", - "robot_text": "what you're saying. Could you please speak", - "user_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090940_user.wav", - "user_duration_sec": 0.032, - "robot_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090940_robot.wav", - "robot_duration_sec": 2.0 - }, - { - "timestamp": "20260427_090940", - "started_at": 1777252180.7317903, - "user_text": "", - "robot_text": "clearly?", - "user_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090940_user.wav", - "user_duration_sec": 0.032, - "robot_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090940_robot.wav", - "robot_duration_sec": 0.76 - }, - { - "timestamp": "20260427_090941", - "started_at": 1777252181.180568, - "user_text": "", - "robot_text": "", - "user_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090941_user.wav", - "user_duration_sec": 3.36 - }, - { - "timestamp": "20260427_090946", - "started_at": 1777252186.123283, - "user_text": "", - "robot_text": "The sound is still very distorted,", - "user_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090946_user.wav", - "user_duration_sec": 8.192, - "robot_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090946_robot.wav", - "robot_duration_sec": 1.96 - }, - { - "timestamp": "20260427_090957", - "started_at": 1777252197.1439726, - "user_text": "", - "robot_text": "I'm afraid. Could", - "user_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090957_user.wav", - "user_duration_sec": 0.032, - "robot_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090957_robot.wav", - "robot_duration_sec": 1.48 - }, - { - "timestamp": "20260427_090957", - "started_at": 1777252197.572026, - "user_text": "", - "robot_text": "you try again?", - "user_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090957_user.wav", - "user_duration_sec": 0.032, - "robot_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090957_robot.wav", - "robot_duration_sec": 1.52 - }, - { - "timestamp": "20260427_090958", - "started_at": 1777252198.012275, - "user_text": "", - "robot_text": "", - "user_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_090958_user.wav", - "user_duration_sec": 2.016 - }, - { - "timestamp": "20260427_091001", - "started_at": 1777252201.9657176, - "user_text": ".", - "robot_text": "I apologize, but the audio", - "user_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_091001_user.wav", - "user_duration_sec": 4.864, - "robot_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_091001_robot.wav", - "robot_duration_sec": 1.92 - }, - { - "timestamp": "20260427_091016", - "started_at": 1777252216.6062548, - "user_text": "", - "robot_text": "is still very unclear.", - "user_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_091016_user.wav", - "user_duration_sec": 0.032, - "robot_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_091016_robot.wav", - "robot_duration_sec": 1.76 - }, - { - "timestamp": "20260427_091017", - "started_at": 1777252217.0914323, - "user_text": "", - "robot_text": "Could you please say", - "user_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_091017_user.wav", - "user_duration_sec": 0.032, - "robot_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_091017_robot.wav", - "robot_duration_sec": 1.08 - }, - { - "timestamp": "20260427_091017", - "started_at": 1777252217.528023, - "user_text": "", - "robot_text": "the command again?", - "user_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_091017_user.wav", - "user_duration_sec": 0.032, - "robot_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_091017_robot.wav", - "robot_duration_sec": 0.92 - }, - { - "timestamp": "20260427_091017", - "started_at": 1777252217.977242, - "user_text": "", - "robot_text": "", - "user_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_091017_user.wav", - "user_duration_sec": 0.032, - "robot_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_091017_robot.wav", - "robot_duration_sec": 0.48 - }, - { - "timestamp": "20260427_091018", - "started_at": 1777252218.4572434, - "user_text": "", - "robot_text": "", - "user_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_091018_user.wav", - "user_duration_sec": 2.176 - }, - { - "timestamp": "20260427_091022", - "started_at": 1777252222.3050365, - "user_text": "", - "robot_text": "", - "user_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_091022_user.wav", - "user_duration_sec": 3.936 - }, - { - "timestamp": "20260427_091031", - "started_at": 1777252231.3111856, - "user_text": "قصدي. ", - "robot_text": "", - "user_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_091031_user.wav", - "user_duration_sec": 0.448 - }, - { - "timestamp": "20260427_091033", - "started_at": 1777252233.5816064, - "user_text": "", - "robot_text": "", - "user_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_091033_user.wav", - "user_duration_sec": 1.376 - }, - { - "timestamp": "20260427_091036", - "started_at": 1777252236.9071608, - "user_text": "", - "robot_text": "", - "user_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_091036_user.wav", - "user_duration_sec": 3.744 - }, - { - "timestamp": "20260427_091043", - "started_at": 1777252243.6780462, - "user_text": ".", - "robot_text": "I'm sorry,", - "user_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_091043_user.wav", - "user_duration_sec": 2.976 - }, - { - "timestamp": "20260427_091049", - "started_at": 1777252249.3750374, - "user_text": "남쪽 라인 하나 둘 셋", - "robot_text": "", - "user_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_091049_user.wav", - "user_duration_sec": 0.864 - }, - { - "timestamp": "20260427_091051", - "started_at": 1777252251.6280284, - "user_text": "", - "robot_text": "", - "user_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_091051_user.wav", - "user_duration_sec": 1.312 - }, - { - "timestamp": "20260427_091054", - "started_at": 1777252254.4071884, - "user_text": "", - "robot_text": "It seems there's still some distortion.", - "user_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_091054_user.wav", - "user_duration_sec": 1.568, - "robot_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_091054_robot.wav", - "robot_duration_sec": 1.68 - }, - { - "timestamp": "20260427_091058", - "started_at": 1777252258.6175046, - "user_text": "", - "robot_text": "Could", - "user_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_091058_user.wav", - "user_duration_sec": 0.032, - "robot_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_091058_robot.wav", - "robot_duration_sec": 0.88 - }, - { - "timestamp": "20260427_091059", - "started_at": 1777252259.0527377, - "user_text": "", - "robot_text": "you please", - "user_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_091059_user.wav", - "user_duration_sec": 0.032, - "robot_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_091059_robot.wav", - "robot_duration_sec": 0.84 - }, - { - "timestamp": "20260427_091059", - "started_at": 1777252259.5382385, - "user_text": "", - "robot_text": "try again,", - "user_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_091059_user.wav", - "user_duration_sec": 0.032, - "robot_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_091059_robot.wav", - "robot_duration_sec": 0.8 - }, - { - "timestamp": "20260427_091059", - "started_at": 1777252259.9547665, - "user_text": "", - "robot_text": "maybe with a clearer", - "user_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_091059_user.wav", - "user_duration_sec": 0.032, - "robot_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_091059_robot.wav", - "robot_duration_sec": 1.08 - }, - { - "timestamp": "20260427_091100", - "started_at": 1777252260.424161, - "user_text": "", - "robot_text": "voice?", - "user_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_091100_user.wav", - "user_duration_sec": 0.032, - "robot_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_091100_robot.wav", - "robot_duration_sec": 0.92 - }, - { - "timestamp": "20260427_091100", - "started_at": 1777252260.8556893, - "user_text": "", - "robot_text": "", - "user_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_091100_user.wav", - "user_duration_sec": 1.92 - }, - { - "timestamp": "20260427_091104", - "started_at": 1777252264.4879806, - "user_text": ".", - "robot_text": "", - "user_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_091104_user.wav", - "user_duration_sec": 5.888 - }, - { - "timestamp": "20260427_091114", - "started_at": 1777252274.7428982, - "user_text": ".", - "robot_text": "The audio is still cutting", - "user_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_091114_user.wav", - "user_duration_sec": 4.16, - "robot_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_091114_robot.wav", - "robot_duration_sec": 1.84 - }, - { - "timestamp": "20260427_091121", - "started_at": 1777252281.7678783, - "user_text": "", - "robot_text": "out. Could you please", - "user_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_091121_user.wav", - "user_duration_sec": 0.032, - "robot_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_091121_robot.wav", - "robot_duration_sec": 1.44 - }, - { - "timestamp": "20260427_091122", - "started_at": 1777252282.195701, - "user_text": "", - "robot_text": "say what you'd like me to do?", - "user_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_091122_user.wav", - "user_duration_sec": 0.032, - "robot_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_091122_robot.wav", - "robot_duration_sec": 1.52 - }, - { - "timestamp": "20260427_091122", - "started_at": 1777252282.6409268, - "user_text": "", - "robot_text": "", - "user_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_091122_user.wav", - "user_duration_sec": 0.032, - "robot_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_091122_robot.wav", - "robot_duration_sec": 0.28 - }, - { - "timestamp": "20260427_091123", - "started_at": 1777252283.0551784, - "user_text": "", - "robot_text": "", - "user_wav": "/home/unitree/Marcus/Data/Voice/Recordings/gemini_turns/20260427_091123_user.wav", - "user_duration_sec": 2.24 - } - ], - "total_records": 95 -} \ No newline at end of file diff --git a/Data/Voice/Recordings/unk_1776999824.wav b/Data/Voice/Recordings/unk_1776999824.wav deleted file mode 100644 index b2eeef9..0000000 Binary files a/Data/Voice/Recordings/unk_1776999824.wav and /dev/null differ diff --git a/Data/Voice/Recordings/unk_1777000093.wav b/Data/Voice/Recordings/unk_1777000093.wav deleted file mode 100644 index 41ec751..0000000 Binary files a/Data/Voice/Recordings/unk_1777000093.wav and /dev/null differ diff --git a/Navigation/goal_nav.py b/Navigation/goal_nav.py index 0952a90..3c57bf1 100644 --- a/Navigation/goal_nav.py +++ b/Navigation/goal_nav.py @@ -12,6 +12,28 @@ from API.llava_api import call_llava, ask_goal, OLLAMA_MODEL from API.memory_api import log_detection from Core.config_loader import load_config +# Persist navigation events to logs/navigation.log so a tour-guide / patrol +# session leaves a post-mortem audit trail (every goal, every step, every +# YOLO-vs-LLaVA arbitration). Terminal output is unchanged — _nlog prints +# the same indented " [GoalNav] ..." line callers expect AND mirrors it +# to the log. Logger failures are swallowed so logging glitches can never +# crash the navigation loop. +try: + from Core.logger import log as _core_log +except Exception: + _core_log = None + + +def _nlog(msg: str, level: str = "info") -> None: + """Print and persist to logs/navigation.log.""" + print(f" [GoalNav] {msg}") + if _core_log is not None: + try: + _core_log(f"[GoalNav] {msg}", level, "navigation") + except Exception: + pass + + _cfg = load_config("Navigation") GOAL_ALIASES = _cfg["goal_aliases"] @@ -93,10 +115,10 @@ def navigate_to_goal(goal: str, max_steps: int = 0): condition = _extract_extra_condition(goal, yolo_target) if yolo_target else None if yolo_target: - print(f" [GoalNav] YOLO target: '{yolo_target}'" + _nlog(f"YOLO target: '{yolo_target}'" f"{f' condition: {condition}' if condition else ''}") else: - print(f" [GoalNav] No YOLO shortcut — relying on LLaVA") + _nlog("No YOLO shortcut — relying on LLaVA") # --- continuous rotation thread (with LiDAR obstacle pause) --- rotating = [True] @@ -134,16 +156,16 @@ def navigate_to_goal(goal: str, max_steps: int = 0): did_work = True if condition: if not _verify_condition(yolo_target, condition, img_b64): - print(f" [GoalNav] YOLO sees {yolo_target} but condition " + _nlog(f"YOLO sees {yolo_target} but condition " f"'{condition}' not met — continuing") # fall through to the sleep-skip path else: - print(f" [GoalNav] YOLO confirmed '{yolo_target}' at step {step}") + _nlog(f"YOLO confirmed '{yolo_target}' at step {step}") log_detection(yolo_target, position="goal", distance="close") reached = True break else: - print(f" [GoalNav] YOLO confirmed '{yolo_target}' at step {step}") + _nlog(f"YOLO confirmed '{yolo_target}' at step {step}") log_detection(yolo_target, position="goal", distance="close") reached = True break @@ -155,12 +177,12 @@ def navigate_to_goal(goal: str, max_steps: int = 0): did_work = True d = ask_goal(goal, img_b64) if d.get("reached"): - print(f" [GoalNav] LLaVA says goal reached at step {step}") + _nlog(f"LLaVA says goal reached at step {step}") reached = True break speak = d.get("speak", "") if speak: - print(f" [GoalNav] LLaVA: {speak}") + _nlog(f"LLaVA: {speak}") # Only pay the scan interval when nothing happened this step. # If YOLO hit or LLaVA fired, they already took 50–1000 ms. @@ -173,8 +195,8 @@ def navigate_to_goal(goal: str, max_steps: int = 0): gradual_stop() if reached: - print(f" [GoalNav] Arrived: '{goal}'") + _nlog(f"Arrived: '{goal}'") else: - print(f" [GoalNav] Could not reach '{goal}' within {max_steps} steps") + _nlog(f"Could not reach '{goal}' within {max_steps} steps", "warn") return reached diff --git a/Navigation/patrol.py b/Navigation/patrol.py index f647d8a..2f8c98c 100644 --- a/Navigation/patrol.py +++ b/Navigation/patrol.py @@ -11,6 +11,24 @@ from API.memory_api import mem, log_detection from Brain.executor import move_step from Core.config_loader import load_config +# Persist patrol events to logs/navigation.log so a long unattended patrol +# session leaves a usable post-mortem audit (start/finish, every step's +# observation, every PPE violation alert, every proximity pause). +try: + from Core.logger import log as _core_log +except Exception: + _core_log = None + + +def _plog(msg: str, level: str = "info") -> None: + """Print and persist to logs/navigation.log.""" + print(f" [Patrol] {msg}") + if _core_log is not None: + try: + _core_log(f"[Patrol] {msg}", level, "navigation") + except Exception: + pass + _cfg = load_config("Patrol") DEFAULT_DURATION = _cfg["default_duration_minutes"] @@ -41,7 +59,7 @@ def patrol(duration_minutes: float = 0.0, alert_callback=None): end_time = time.time() + duration_minutes * 60.0 step = 0 - print(f" [Patrol] Starting {duration_minutes:.1f}-minute patrol") + _plog(f"Starting {duration_minutes:.1f}-minute patrol") try: while time.time() < end_time: @@ -52,7 +70,7 @@ def patrol(duration_minutes: float = 0.0, alert_callback=None): if violations: for v in violations: alert_text = f"PPE violation: {v}" - print(f" [Patrol] {alert_text}") + _plog(alert_text, "warn") log_detection(v, position="patrol", distance="") if alert_callback: alert_callback(alert_text) @@ -71,11 +89,11 @@ def patrol(duration_minutes: float = 0.0, alert_callback=None): duration = float(d.get("duration", 1.0)) if observation: - print(f" [Patrol] step {step}: {observation}") + _plog(f"step {step}: {observation}") if alert: alert_text = f"Alert: {alert}" - print(f" [Patrol] {alert_text}") + _plog(alert_text, "warn") if alert_callback: alert_callback(alert_text) @@ -93,7 +111,7 @@ def patrol(duration_minutes: float = 0.0, alert_callback=None): if yolo_person_too_close(threshold=PROXIMITY_THRESH) or lidar_blocked: reason = "LiDAR obstacle" if lidar_blocked else "Person too close" - print(f" [Patrol] {reason} — pausing {PROXIMITY_PAUSE}s") + _plog(f"{reason} — pausing {PROXIMITY_PAUSE}s", "warn") gradual_stop() time.sleep(PROXIMITY_PAUSE) continue @@ -106,9 +124,9 @@ def patrol(duration_minutes: float = 0.0, alert_callback=None): time.sleep(0.5) except KeyboardInterrupt: - print(" [Patrol] Interrupted") + _plog("Interrupted", "warn") finally: gradual_stop() - print(f" [Patrol] Finished after {step} steps") + _plog(f"Finished after {step} steps") return step diff --git a/Vision/marcus_yolo.py b/Vision/marcus_yolo.py index d646454..c7fe595 100644 --- a/Vision/marcus_yolo.py +++ b/Vision/marcus_yolo.py @@ -34,6 +34,27 @@ except Exception as _e: print(f" [YOLO] config_Vision.json not loaded ({_e}) — using defaults") _cfg = {} +# Logger — every YOLO event also persists to logs/vision.log so post-mortem +# audits can reconstruct what the eyes saw, when models loaded/failed, and +# when inference errors fired. Helper keeps the terminal output unchanged +# (still prints " [YOLO] ..." with the existing indent) but ALSO writes +# a clean "[YOLO] ..." line to vision.log via Core.logger. +try: + from Core.logger import log as _core_log +except Exception: + _core_log = None + + +def _vlog(msg: str, level: str = "info") -> None: + """Print to terminal AND append to logs/vision.log. Logger failures + are swallowed so any logging glitch can't crash the inference loop.""" + print(f" [YOLO] {msg}") + if _core_log is not None: + try: + _core_log(f"[YOLO] {msg}", level, "vision") + except Exception: + pass + YOLO_MODEL_PATH = os.path.join(_PROJECT_ROOT, _cfg.get("yolo_model_path", "Models/yolov8m.pt")) YOLO_CONFIDENCE = float(_cfg.get("yolo_confidence", 0.45)) YOLO_IOU = float(_cfg.get("yolo_iou", 0.45)) @@ -365,7 +386,7 @@ def _inference_loop(model, is_ppe: bool, raw_frame_ref, frame_lock, verbose=False )[0] except Exception as e: - print(f" [YOLO] Inference error: {e}") + _vlog(f"Inference error: {e}", "error") time.sleep(0.2) continue @@ -445,14 +466,14 @@ def start_yolo(raw_frame_ref=None, frame_lock=None): try: from ultralytics import YOLO except ImportError: - print(" [YOLO] ultralytics not installed — pip install ultralytics") + _vlog("ultralytics not installed — pip install ultralytics", "error") return False - print(f" [YOLO] Loading model: {YOLO_MODEL_PATH}") + _vlog(f"Loading model: {YOLO_MODEL_PATH}") try: model = YOLO(YOLO_MODEL_PATH) except Exception as e: - print(f" [YOLO] Failed to load model: {e}") + _vlog(f"Failed to load model: {e}", "error") return False names = set(model.names.values()) @@ -467,7 +488,7 @@ def start_yolo(raw_frame_ref=None, frame_lock=None): try: model.to(device) except Exception as e: - print(f" [YOLO] Could not move model to {device} ({e}) — continuing") + _vlog(f"Could not move model to {device} ({e}) — continuing", "warn") gpu_info = "" if device != "cpu": @@ -477,7 +498,7 @@ def start_yolo(raw_frame_ref=None, frame_lock=None): except Exception: pass - print(f" [YOLO] Model loaded ✅ | device: {device}{gpu_info}" + _vlog(f"Model loaded ✅ | device: {device}{gpu_info}" f"{' | FP16' if use_half else ''} | " f"{'PPE model' if is_ppe else f'{len(TRACKED_CLASSES & names)} tracked classes'}") diff --git a/Voice/_probe_multicast.py b/Voice/_probe_multicast.py new file mode 100644 index 0000000..59bc4ee --- /dev/null +++ b/Voice/_probe_multicast.py @@ -0,0 +1,123 @@ +#!/usr/bin/env python3 +"""Probe — does the G1 audio multicast actually deliver packets? + +Run on the Jetson: + /home/unitree/miniconda3/envs/gemini_sdk/bin/python ~/Marcus/Voice/_probe_multicast.py + +This is a STRIPPED-DOWN version of Voice/audio_io.py::BuiltinMic.start() +plus a 5-second packet counter. Tells us in plain numbers whether the G1 +audio service is publishing on 239.168.123.161:5555 — independent of any +Marcus state. Prints: + + interfaces with 192.168.123.x: [list] + chosen local_ip: x.x.x.x + joined multicast 239.168.123.161:5555 ✓ + packets received in 5s: N + total bytes received: M + first packet bytes (hex preview): ... + +If packets=0 → G1 audio service isn't publishing OR Jetson can't reach +the multicast group. Either way Marcus's voice can never work until +THIS probe shows packets > 0. +""" +import socket +import struct +import subprocess +import sys +import time + + +GROUP = "239.168.123.161" +PORT = 5555 + + +def find_g1_ips() -> list: + """Return all local IPs in 192.168.123.0/24 (the G1 internal subnet).""" + out = subprocess.run( + ["ip", "-4", "-o", "addr"], capture_output=True, text=True, + ).stdout + found = [] + for line in out.splitlines(): + for tok in line.split(): + if tok.startswith("192.168.123."): + found.append(tok.split("/")[0]) + return found + + +def main() -> int: + ips = find_g1_ips() + print(f" interfaces with 192.168.123.x: {ips or '(none — Jetson NOT on G1 subnet)'}") + if not ips: + print(" ✗ Cannot proceed — no 192.168.123.x interface on this host.") + print(" The G1 audio multicast only delivers to that subnet.") + print(" Check `ip addr show` and reconnect to the G1's network.") + return 1 + + local_ip = ips[0] + print(f" chosen local_ip: {local_ip}") + + sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + try: + sock.bind(("", PORT)) + except Exception as e: + print(f" ✗ bind on UDP port {PORT} failed: {e}") + return 2 + + mreq = struct.pack( + "4s4s", + socket.inet_aton(GROUP), + socket.inet_aton(local_ip), + ) + try: + sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq) + except Exception as e: + print(f" ✗ multicast join failed: {e}") + return 3 + sock.settimeout(0.5) + print(f" joined multicast {GROUP}:{PORT} ✓") + print(f" listening for 5 seconds...") + + deadline = time.time() + 5.0 + pkt_count = 0 + byte_count = 0 + first_pkt = None + while time.time() < deadline: + try: + data, addr = sock.recvfrom(4096) + except socket.timeout: + continue + except Exception as e: + print(f" ✗ recvfrom error: {e}") + break + pkt_count += 1 + byte_count += len(data) + if first_pkt is None: + first_pkt = data + + sock.close() + + print(f" packets received in 5s: {pkt_count}") + print(f" total bytes received: {byte_count}") + if first_pkt is not None: + preview = first_pkt[:24].hex(" ") + print(f" first packet bytes (hex preview): {preview} ({len(first_pkt)} bytes total)") + + if pkt_count == 0: + print() + print(" ✗ ZERO packets received. Diagnosis:") + print(" - G1's audio service is not publishing, OR") + print(" - This Jetson is on the right subnet but a firewall or") + print(" switch is blocking multicast (rare on G1's setup).") + print(" Fix: restart the G1 audio service or reboot the robot.") + return 4 + + print() + print(f" ✓ Multicast IS delivering ({pkt_count} packets / {byte_count} bytes in 5s).") + print(f" Expected for 16 kHz mono int16: ~{int(5*16000*2/byte_count*pkt_count)} packets/s if continuous") + print(f" If audio is non-zero too → the issue is somewhere in the runner; not the network.") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/Voice/builtin_mic.py b/Voice/builtin_mic.py index 7b6c868..cb8b965 100644 --- a/Voice/builtin_mic.py +++ b/Voice/builtin_mic.py @@ -16,8 +16,17 @@ Do not add new code here — extend Voice/audio_io.py instead. from __future__ import annotations +import os +import sys import time +# When run directly (`python3 Voice/builtin_mic.py`) the parent +# directory isn't on sys.path, so the `Voice.audio_io` import below +# would fail. Add it before the import resolves. +_PROJECT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +if _PROJECT_DIR not in sys.path: + sys.path.insert(0, _PROJECT_DIR) + from Voice.audio_io import BuiltinMic as _BaseBuiltinMic diff --git a/Voice/gemini_runner.py b/Voice/gemini_runner.py index a408a75..a15e260 100644 --- a/Voice/gemini_runner.py +++ b/Voice/gemini_runner.py @@ -191,7 +191,8 @@ _AI_GRACE = float(_STT.get("gemini_ai_speak_grace_sec", 0.15)) _FRAME_INTERVAL = float(_STT.get("gemini_frame_interval_sec", 0.5)) _FRAME_MAX_AGE = float(_STT.get("gemini_frame_max_age_sec", 1.5)) -_REC_ENABLED = bool(_STT.get("gemini_record_enabled", True)) +_REC_ENABLED = bool(_STT.get("gemini_record_enabled", False)) +_REC_KEEP = int(_STT.get("gemini_record_keep_count", 50)) _DATA_DIR = os.path.join( _PROJECT_ROOT, _VCFG.get("audio", {}).get("data_dir", "Data/Voice/Recordings"), @@ -381,7 +382,11 @@ async def _send_mic_loop(session, types_mod, mic, speaker, recorder, sess: _Sess if energy > 250: last_activity = now elif now - last_activity > 10: - log("info", f"alive (no speech {now - last_activity:.0f}s)") + # Show actual energy + a short rolling sample of recent peaks so + # "no speech" diagnostics reveal whether the mic is silent (e≈0, + # mic broken / multicast not delivering) vs picking up faint + # ambient (e≈50-200, mic OK but user too far / mic_gain too low). + log("info", f"alive (no speech {now - last_activity:.0f}s, e={energy})") last_activity = now try: @@ -572,6 +577,7 @@ async def main_async() -> int: out_dir=_DATA_DIR, user_rate=SEND_SAMPLE_RATE, robot_rate=RECEIVE_SAMPLE_RATE, + keep_count=_REC_KEEP, ) session_num = 0 diff --git a/Voice/marcus_voice.py b/Voice/marcus_voice.py index 8e10a68..af27468 100644 --- a/Voice/marcus_voice.py +++ b/Voice/marcus_voice.py @@ -7,28 +7,25 @@ Pipeline: G1 mic UDP ──► BuiltinMic (Voice/audio_io.py) │ ▼ - GeminiBrain (Voice/gemini/script.py) - │ audio out (24 kHz) + GeminiBrain (Voice/gemini_script.py — subprocess in gemini_sdk env) + │ audio out (24 kHz, Gemini's voice through G1 speaker) + │ output_transcription (chunks of Gemini's reply text) ▼ - BuiltinSpeaker (Voice/audio_io.py) ──► G1 speaker - │ user transcript (on_command) - ▼ - _dispatch_gemini_command - - require wake word "Sanad" - - fuzzy-match command_vocab - - dedup within command_cooldown_sec + _dispatch_gemini_bot (Marcus side-channel) + - match Gemini's spoken phrase against + Config/instruction.json::actions[*].bot_phrases + - dedup canonical command within command_cooldown_sec │ ▼ - on_command(text, "en") ──► Marcus brain + on_command(canonical, "en") ──► Marcus brain → motion -Gemini owns both STT and TTS — it hears the user and replies with its own -voice. Marcus's on_command hook fires alongside Gemini's verbal reply so -motion commands (\"Sanad, turn right\") still move the robot body while -the conversation flows naturally. - -Wake word is enforced at dispatch only — Gemini chats normally on all -speech; the robot moves only when \"Sanad\" + a recognised action phrase -appears in the transcript. +GEMINI IS THE WAKE-WORD GATEKEEPER. The persona prompt in +Config/config_Voice.json::gemini_system_prompt instructs Gemini to ONLY +speak motion-confirmation phrases ('Turning right', 'أستدير يميناً') +when the user said the wake word ('Sanad' or 'سند') AND requested an +action. If Gemini says a motion phrase, Marcus trusts that decision and +fires the matching robot motion. Marcus does NOT inspect user transcripts +for wake words anymore — single gatekeeper, no double-checking. """ from __future__ import annotations @@ -158,6 +155,48 @@ def _build_arabic_motion_map(data: dict) -> dict: return out +def _build_parametric_actions(data: dict) -> list: + """Compile parametric_actions from instruction.json into a list of + (compiled_regex, command_template) tuples. Every regex carries + re.IGNORECASE so 'Turning' / 'turning' / 'TURNING' all match. Arabic + is unaffected by IGNORECASE. + + Bad entries (missing regex/template, regex compile errors) are + skipped with a warning — the rest of the table still loads. + """ + out = [] + for entry in (data.get("parametric_actions", []) or []): + if not isinstance(entry, dict): + continue + rx = entry.get("regex") + tpl = entry.get("command_template") + if not rx or not tpl: + continue + try: + compiled = re.compile(rx, re.IGNORECASE) + except re.error as e: + try: + log.warning("parametric_actions skipped (bad regex %r): %s", rx, e) + except Exception: + pass + continue + out.append((compiled, tpl)) + return out + + +def _format_param_template(template: str, groups: tuple) -> str: + """Substitute $1, $2, … in a parametric command template with the + matching regex groups. None groups (optional captures that didn't + fire) are treated as empty strings; surrounding whitespace is + collapsed so 'turn 90 degrees' becomes 'turn 90 degrees'.""" + out = template + for i, g in enumerate(groups, 1): + token = "$" + str(i) + out = out.replace(token, "" if g is None else str(g)) + out = re.sub(r"\s+", " ", out).strip() + return out + + def _build_bot_motion_patterns(data: dict) -> list: """List of (needle, canonical) the bot dispatcher matches Gemini's spoken reply against. English needles are lowercased so the dispatcher @@ -189,6 +228,7 @@ WAKE_WORDS: set = _build_wake_words(_INSTRUCTIONS) COMMAND_VOCAB: list = _build_command_vocab(_INSTRUCTIONS) _ARABIC_MOTION_TO_CANONICAL: dict = _build_arabic_motion_map(_INSTRUCTIONS) _BOT_MOTION_PATTERNS: list = _build_bot_motion_patterns(_INSTRUCTIONS) +_PARAMETRIC_ACTIONS: list = _build_parametric_actions(_INSTRUCTIONS) # Garbage patterns + min length stay in config_Voice.json (they're # noise filtering, not voice instructions). @@ -308,36 +348,43 @@ class VoiceModule: # instruction tables). global WAKE_WORDS, COMMAND_VOCAB, GARBAGE_PATTERNS, \ _MIN_TRANSCRIPTION_LENGTH, _ARABIC_MOTION_TO_CANONICAL, \ - _BOT_MOTION_PATTERNS, _INSTRUCTIONS + _BOT_MOTION_PATTERNS, _PARAMETRIC_ACTIONS, _INSTRUCTIONS _INSTRUCTIONS = _load_instructions() WAKE_WORDS = _build_wake_words(_INSTRUCTIONS) COMMAND_VOCAB = _build_command_vocab(_INSTRUCTIONS) _ARABIC_MOTION_TO_CANONICAL = _build_arabic_motion_map(_INSTRUCTIONS) _BOT_MOTION_PATTERNS = _build_bot_motion_patterns(_INSTRUCTIONS) + _PARAMETRIC_ACTIONS = _build_parametric_actions(_INSTRUCTIONS) GARBAGE_PATTERNS = {p.lower() for p in self._stt.get("garbage_patterns", [])} _MIN_TRANSCRIPTION_LENGTH = int(self._stt.get("min_transcription_length", 3)) self._vocab_cutoff = float(self._stt.get("command_vocab_cutoff", 0.72)) log.info( "instruction.json loaded: %d wake_words, %d command_vocab, " - "%d arabic→canonical, %d bot patterns; " + "%d arabic→canonical, %d bot patterns, %d parametric; " "+ %d garbage_patterns from config_Voice.json", len(WAKE_WORDS), len(COMMAND_VOCAB), len(_ARABIC_MOTION_TO_CANONICAL), len(_BOT_MOTION_PATTERNS), - len(GARBAGE_PATTERNS), + len(_PARAMETRIC_ACTIONS), len(GARBAGE_PATTERNS), ) - # Dispatch dedup state: Gemini's input_transcription can fire - # multiple times per turn (streaming partials). Track the last - # canonical command + timestamp so we don't move twice. + # Dispatch dedup state. Gemini's output_transcription arrives in + # many small chunks per turn — and one chunk may contain MULTIPLE + # motion phrases for compound commands ("Turning right, then + # moving forward."). Two layers of dedup: + # 1. _fired_canons_this_turn — set of canonicals already fired + # since the current Gemini turn started; cleared on turn_end. + # Same motion never fires twice within one reply. + # 2. _last_gemini_canon / _last_gemini_dispatch_at — cross-chunk + # cooldown safety net for cases where a turn_end is delayed + # and the next turn's phrase arrives before the reset. + self._fired_canons_this_turn: set = set() self._last_gemini_canon = "" self._last_gemini_dispatch_at = 0.0 - - # Wake-word gate state. Motion (whether triggered from the user's - # transcript OR from Gemini's verbal confirmation) only fires - # within a turn whose user transcript contained "Sanad" / "سند" - # at least once. Reset on turn_end. This is the safety rule: - # actions ALWAYS require the wake word, in either language. - self._wake_active_for_turn = False + # Wake-word gate is enforced INSIDE Gemini via the persona prompt + # (see config_Voice.json::gemini_system_prompt). Marcus does NOT + # check for "Sanad"/"سند" in Python anymore — if Gemini speaks + # a motion-confirmation phrase, that's its own decision and we + # trust it. Cleaner, single gatekeeper, no double-checking. # Gemini brain reference for flush_mic() — populated by # _voice_loop_gemini after spawning the runner subprocess. @@ -434,7 +481,10 @@ class VoiceModule: system_prompt=system_prompt, api_key=api_key, on_transcript=self._on_gemini_transcript, - on_command=self._dispatch_gemini_command, + # No on_command — Marcus does NOT inspect user transcripts + # for wake words anymore. Gemini's persona prompt enforces + # the wake-word gate; if Gemini speaks a motion phrase, the + # bot-side dispatcher below picks it up and fires motion. on_bot_text=self._on_gemini_say_chunk, on_turn_end=self._on_gemini_turn_end, ) @@ -523,11 +573,12 @@ class VoiceModule: self._flush_gemini_say_locked() def _on_gemini_turn_end(self) -> None: - """Flush any pending Gemini output chunks at turn boundary, - and close the wake-word motion gate for the next turn.""" + """Flush any pending Gemini output chunks at turn boundary, and + reset the per-turn motion dedup set so the next user→Gemini + exchange starts fresh.""" with self._gemini_say_lock: self._flush_gemini_say_locked() - self._wake_active_for_turn = False + self._fired_canons_this_turn.clear() def _flush_gemini_say_locked(self) -> None: """Caller MUST hold self._gemini_say_lock. Prints one [Sanad] said: line.""" @@ -545,73 +596,6 @@ class VoiceModule: except Exception: pass - def _dispatch_gemini_command(self, text: str, lang: str = "en") -> None: - """ - Fire self._on_command for any transcript prefixed with the wake - word "Sanad". Marcus's brain is the authoritative decision maker - in the STT-only architecture — it handles motion AND Q&A AND - vision queries AND replies via TtsMaker. - - The vocab-match gate has been dropped: if the user says - "Sanad, what's the weather" the transcript still reaches the - brain, which either answers via its VLM or declines. This keeps - all Gemini-heard queries routed through one place (Marcus) and - removes the audio collision that full S2S had. - - Examples: - "Sanad, turn right" → strip → "turn right" → brain → motion - "Sanad, what do you see"→ strip → "what do you see" → brain → VLM - "Sanad" → bare wake → skip (no payload) - "turn right" → no wake word → skip (conversation gate) - - Dedup: Gemini emits streaming partials; same normalized command - within command_cooldown_sec fires only once. - """ - if not text or not _has_wake_word(text): - return - - # Wake word found — open the motion gate for this turn so the - # bot-side dispatcher (Gemini's spoken confirmation) is also - # allowed to fire. Reset on turn_end. - self._wake_active_for_turn = True - - stripped = _strip_wake_word(text) - if not stripped or len(stripped.strip()) < _MIN_TRANSCRIPTION_LENGTH: - return - - # Bilingual support: translate Arabic motion phrases to their - # English canonical equivalent BEFORE fuzzy-matching. Marcus's - # command_parser is English-only. - stripped = _translate_arabic_motion(stripped) - - low = stripped.lower().strip().rstrip(".!?,") - vocab_exact = {c.lower() for c in COMMAND_VOCAB} - if low in GARBAGE_PATTERNS and low not in vocab_exact: - return - - # Fuzzy-normalize (maps "turn right up" → "turn right") if the - # transcript is close to a vocab entry — but unlike before, we - # forward everything that passed the wake-word gate, not just - # vocab hits. Marcus's command_parser + VLM handles the rest. - command = self._normalize_command(stripped) - canon = command.lower().strip().rstrip(".!?,") - - now = time.time() - cooldown = float(self._stt.get("command_cooldown_sec", 1.5)) - if (canon == self._last_gemini_canon - and now - self._last_gemini_dispatch_at < cooldown): - return - self._last_gemini_canon = canon - self._last_gemini_dispatch_at = now - - log.info("dispatch (gemini): %s", command[:120]) - _log_transcript("CMD", command) - if self._on_command: - try: - self._on_command(command, lang or "en") - except Exception as e: - log.error("on_command error: %s", e, exc_info=True) - def flush_mic(self) -> None: """ Tell the Gemini runner subprocess to drop its mic buffer. Called @@ -642,53 +626,122 @@ class VoiceModule: def _BOT_MOTION_PATTERNS(self): return _BOT_MOTION_PATTERNS + # _PARAMETRIC_ACTIONS holds the regex/template tuples for motion + # confirmations that carry a number ('Turning 360 degrees.', + # 'Walking 5 steps.'). Built from instruction.json::parametric_actions. + @property + def _PARAMETRIC_ACTIONS(self): + return _PARAMETRIC_ACTIONS + def _dispatch_gemini_bot(self, text: str) -> None: """ - Dispatch motion when Gemini's spoken reply contains a known motion- - confirmation pattern (English or Arabic). Strict gate: only fires - if the current turn's user transcript already passed the wake-word - check (`_wake_active_for_turn`) — so motion ALWAYS requires "Sanad" - / "سند" somewhere in the user's request. Dedups against the - user-transcript path so the same command can't fire twice. + Dispatch motion when Gemini's spoken reply contains motion- + confirmation patterns (English or Arabic). The wake-word gate + lives INSIDE Gemini (see config_Voice.json::gemini_system_prompt) — + if Gemini speaks a motion phrase, that is its own decision after + evaluating whether the user said "Sanad"/"سند". Marcus trusts it + and dispatches the motion(s). + + COMPOUND-COMMAND HANDLING: a single chunk may contain multiple + motion phrases — e.g. "Turning right, then moving forward." We + scan for ALL non-overlapping motion patterns in the chunk and + fire each unique canonical in spoken order. Three guards: + 1. Longest-needle-first sort + span-claiming on the pattern + list rejects overlaps so 'moving forward' wins over the + shorter prefix 'moving' inside the same span. + 2. _fired_canons_this_turn ensures each canonical fires AT + MOST ONCE per Gemini turn (compound replies often repeat + the same verb across chunks); cleared on turn_end. + 3. command_cooldown_sec is a final safety net for runaway + same-canon dispatch across a turn_end gap. """ if not text: return - # SAFETY GATE: motion only when wake word was heard this turn. - if not self._wake_active_for_turn: + + # .lower() preserves character-by-character indexing for both + # ASCII and Arabic, so positions in `low` correspond to positions + # in `text` for ordering. Arabic patterns are stored as-is and + # unaffected by lowercasing. + low = text.lower() + if not low.strip(): return - low = text.strip().rstrip(".!?,").lower() - # Note: we keep both English (lowercased) and Arabic patterns; - # Arabic is unaffected by .lower() since it has no case. - if not low: - return + # Walk every pattern, collect all (start_index, command_string, + # label) matches that don't overlap an already-claimed span. + # Two pattern families: + # - PARAMETRIC (regex with capture groups, e.g. 'turning 90 + # degrees') — scanned FIRST because they're more specific + # than the bare-canonical match 'turn right'. The dispatched + # string is the formatted command_template ('turn left 90 + # degrees'), which Brain/command_parser.py parses natively. + # - FIXED CANONICALS (substring needles from bot_phrases) + # — scanned second; pre-sorted longest-first so multi-word + # phrases claim spans before shorter prefixes. + # Per-turn dedup (_fired_canons_this_turn) is keyed by the + # dispatched command string, so the same parametric command + # ('turn 90 degrees') only fires once per Gemini reply. + matches = [] # type: list[tuple[int, str, str]] + claimed_spans = [] # type: list[tuple[int, int]] + + for compiled, template in self._PARAMETRIC_ACTIONS: + for m in compiled.finditer(low): + j, end = m.start(), m.end() + if any(not (end <= s or j >= e) + for (s, e) in claimed_spans): + continue + cmd_text = _format_param_template(template, m.groups()) + if not cmd_text: + continue + matches.append((j, cmd_text, "param")) + claimed_spans.append((j, end)) - canon = None for needle, cmd in self._BOT_MOTION_PATTERNS: - # Match-anywhere so leading filler ("OK, " / "حسناً، ") doesn't - # block the recognition. English needles are lowercase; Arabic - # needles match as-is. - if needle in low or needle in text: - canon = cmd - break - if canon is None: + if not needle: + continue + i = 0 + while True: + j = low.find(needle, i) + if j == -1: + break + end = j + len(needle) + if any(not (end <= s or j >= e) + for (s, e) in claimed_spans): + i = j + 1 + continue + matches.append((j, cmd, "canon")) + claimed_spans.append((j, end)) + i = end + + if not matches: return + # Spoken order — sort by position so we fire turn_right BEFORE + # move_forward when Gemini said "Turning right, then moving + # forward." + matches.sort(key=lambda m: m[0]) + now = time.time() cooldown = float(self._stt.get("command_cooldown_sec", 1.5)) - if (canon == self._last_gemini_canon - and now - self._last_gemini_dispatch_at < cooldown): - return - self._last_gemini_canon = canon - self._last_gemini_dispatch_at = now + for _, cmd, label in matches: + if cmd in self._fired_canons_this_turn: + continue + if (cmd == self._last_gemini_canon + and now - self._last_gemini_dispatch_at < cooldown): + continue + self._fired_canons_this_turn.add(cmd) + self._last_gemini_canon = cmd + self._last_gemini_dispatch_at = now - log.info("dispatch (gemini-bot): %s (heard: %r)", canon, text[:80]) - _log_transcript("CMD-BOT", canon) - if self._on_command: - try: - self._on_command(canon, "en") - except Exception as e: - log.error("on_command error: %s", e, exc_info=True) + log.info( + "dispatch (gemini-bot, %s): %s (heard: %r)", + label, cmd, text[:80], + ) + _log_transcript("CMD-BOT", cmd) + if self._on_command: + try: + self._on_command(cmd, "en") + except Exception as e: + log.error("on_command error: %s", e, exc_info=True) # ─── start / stop ───────────────────────────────────── diff --git a/Voice/turn_recorder.py b/Voice/turn_recorder.py index 2c2fc46..86a345e 100644 --- a/Voice/turn_recorder.py +++ b/Voice/turn_recorder.py @@ -39,11 +39,16 @@ class TurnRecorder: out_dir: str = "", user_rate: int = 16000, robot_rate: int = 24000, + keep_count: int = 50, ): self.enabled = bool(enabled) and bool(out_dir) self.out_dir = out_dir self.user_rate = int(user_rate) self.robot_rate = int(robot_rate) + # Cap the number of turn-pairs on disk. 0/negative = unlimited. + # finish_turn() prunes the oldest WAVs after every save so the + # folder can never balloon past keep_count*2 files in the wild. + self.keep_count = int(keep_count) if self.enabled: os.makedirs(self.out_dir, exist_ok=True) self._lock = threading.Lock() @@ -119,6 +124,7 @@ class TurnRecorder: len(robot_data) / (self.robot_rate * 2), 3, ) self._append_index(entry) + self._rotate() log.info( "recorded turn → %s (user %.1fs, robot %.1fs)", stamp, @@ -129,6 +135,30 @@ class TurnRecorder: log.warning("recording save failed: %s", exc) return entry + def _rotate(self) -> None: + """Prune oldest WAV files when count exceeds keep_count*2 (one + user + one robot per turn). index.json is never touched.""" + if self.keep_count <= 0: + return + try: + wavs = [ + f for f in os.listdir(self.out_dir) + if f.endswith(".wav") + ] + if len(wavs) <= self.keep_count * 2: + return + # Sort oldest-first by mtime; remove the head of the list. + wavs_paths = [os.path.join(self.out_dir, f) for f in wavs] + wavs_paths.sort(key=lambda p: os.path.getmtime(p)) + to_remove = wavs_paths[: len(wavs_paths) - self.keep_count * 2] + for p in to_remove: + try: + os.remove(p) + except Exception: + pass + except Exception as exc: + log.warning("recording rotate failed: %s", exc) + @staticmethod def _save_wav(path: str, pcm: bytes, rate: int) -> None: with wave.open(path, "wb") as wf: