Update 2026-04-27 13:48:17

This commit is contained in:
kassam 2026-04-27 13:48:18 +04:00
parent 2129cadb11
commit c6c847e3ab
151 changed files with 847 additions and 1263 deletions

View File

@ -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")

View File

@ -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
# `<noise>` 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,9 +221,11 @@ 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):
# 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

View File

@ -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 <ts>_user.wav (mic) + <ts>_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 <N> degrees.' / 'Turning left <N> degrees.' / 'Turning right <N> degrees.'\n • 'Walking <N> steps.' / 'Walking forward <N> steps.' / 'Walking back <N> steps.'\n • 'Walking <N> meters.' / 'Walking forward <N> meters.' / 'Walking backward <N> meters.'\n • 'أستدير <N> درجة.' / 'أستدير يميناً <N> درجة.' / 'أستدير يساراً <N> درجة.'\n • 'أمشي <N> خطوات.' / 'أمشي للأمام <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.03.0 = comfortable for a 12 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,

View File

@ -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"
}
]
}

View File

@ -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
}
]
[]

Some files were not shown because too many files have changed in this diff Show More