-
Notifications
You must be signed in to change notification settings - Fork 87.3k
Description
import tkinter as tk
import threading
import time
import random
import speech_recognition as sr
from tkinter import ttk
----- Settings -----
OWNER_NAME = "owner"
CONFIRM_TIMEOUT = 5
Enhanced commands with multiple language support
ACTION_MAP = {
"wave": {
"action": "Wave Hand",
"keywords": {
"en": ["wave", "hello", "hi", "greet"],
"ar": ["اهتز", "مرحباً", "لوح", "سلم"]
}
},
"forward": {
"action": "Move Forward",
"keywords": {
"en": ["forward", "front", "ahead", "advance", "go"],
"ar": ["امام", "تقديم", "انطلق", "اسرع"]
}
},
"backward": {
"action": "Move Backward",
"keywords": {
"en": ["backward", "back", "behind", "retreat"],
"ar": ["خلف", "تراجع", "ارجع", "الى وراء"]
}
},
"left": {
"action": "Turn Left",
"keywords": {
"en": ["left", "turn left", "go left"],
"ar": ["يسار", "اتجه يسار", "شمال", "يسارا"]
}
},
"right": {
"action": "Turn Right",
"keywords": {
"en": ["right", "turn right", "go right"],
"ar": ["يمين", "اتجه يمين", "يمينا"]
}
},
"reset": {
"action": "Reset Position",
"keywords": {
"en": ["reset", "home", "center", "stop"],
"ar": ["استعادة", "مركز", "اعد", "توقف"]
}
}
}
ACTIONS = list(ACTION_MAP.keys())
pending = {}
_next_id = 0
lock = threading.Lock()
current_language = "en" # Default language
----- Control Functions -----
def next_id():
global _next_id
with lock:
_next_id += 1
return _next_id
def find_matching_command(text, lang="en"):
"""Find matching command in text with language support"""
text = text.lower().strip()
for cmd_id, cmd_info in ACTION_MAP.items():
# Check keywords in the specified language
if lang in cmd_info["keywords"]:
for keyword in cmd_info["keywords"][lang]:
if keyword.lower() in text:
return cmd_id, cmd_info["action"]
return None, None
def receive_command(from_name, text, confidence=0.8):
"""Receive command with multi-language support"""
# Try current language first
cmd_id, action = find_matching_command(text, current_language)
# If not found, try other languages
if not action:
for lang in ["en", "ar"]:
if lang != current_language:
cmd_id, action = find_matching_command(text, lang)
if action:
break
if not action:
app.log_event(f"[WARN] Cannot recognize command: {text}")
return
if from_name == OWNER_NAME and confidence >= 0.55:
app.log_event(f"[AUTH] Owner verified → Executing {action}")
execute_action(action)
return
cid = next_id()
pending[cid] = {"from": from_name, "action": action, "ts": time.time()}
app.update_pending()
app.log_event(f"[PENDING-{cid}] {from_name}: {action}. Awaiting confirmation")
def wait_timeout(cid):
start = time.time()
while time.time() - start < CONFIRM_TIMEOUT:
time.sleep(0.2)
if cid not in pending:
return
if cid in pending:
info = pending.pop(cid)
app.update_pending()
app.log_event(f"[TIMEOUT] Command {cid} ignored (from {info['from']})")
def confirm_cmd(cid):
if cid in pending:
info = pending.pop(cid)
app.update_pending()
app.log_event(f"[CONFIRMED] Approved {cid} → Executing {info['action']}")
execute_action(info['action'])
else:
app.log_event(f"[CONFIRM] No pending command with id {cid}")
def execute_action(action):
app.log_event(f"[EXECUTE] {action}")
app.perform_action(action)
time.sleep(0.5)
app.log_event(f"[DONE] {action}")
def switch_language(lang):
"""Switch voice recognition language"""
global current_language
current_language = lang
app.log_event(f"[LANGUAGE] Switched to {lang.upper()} voice recognition")
app.update_language_display()
----- Enhanced Voice Recognition -----
def listen_microphone():
r = sr.Recognizer()
try:
with sr.Microphone() as source:
r.adjust_for_ambient_noise(source, duration=1)
app.log_event("[INFO] Ready to listen... Adjusted for ambient noise")
except:
app.log_event("[WARN] Cannot access microphone")
return
while True:
try:
app.log_event(f"[LISTENING] Listening for {current_language.upper()} commands...")
with sr.Microphone() as source:
r.pause_threshold = 1.0
r.energy_threshold = 300
audio = r.listen(source, timeout=5, phrase_time_limit=3)
# Try recognition with current language
if current_language == "ar":
text = r.recognize_google(audio, language="ar-AR")
else:
text = r.recognize_google(audio, language="en-US")
app.log_event(f"[VOICE] Recognized: '{text}'")
if text.strip():
receive_command(OWNER_NAME, text, confidence=0.8)
except sr.WaitTimeoutError:
continue
except sr.UnknownValueError:
app.log_event("[ERROR] Could not understand audio")
except sr.RequestError as e:
app.log_event(f"[ERROR] Recognition service error: {e}")
except Exception as e:
app.log_event(f"[ERROR] {e}")
time.sleep(0.5)
----- Event Simulation -----
def simulate_events():
while True:
time.sleep(random.uniform(5, 8))
who = random.choice(["Alice", "Bob", "Charlie"])
phrase = random.choice(ACTIONS)
conf = 0.55
app.log_event(f"[EVENT] {who} → '{phrase}' (conf={conf:.2f})")
receive_command(who, phrase, conf)
----- Enhanced GUI -----
class RobotApp:
def init(self, master):
self.master = master
master.title("AI Robot Simulator - Multi Language")
master.geometry("850x650")
# Font settings
self.title_font = ("Arial", 16, "bold")
self.normal_font = ("Arial", 11)
self.small_font = ("Arial", 10)
# Title Frame
title_frame = tk.Frame(master, bg="#2C3E50")
title_frame.pack(fill="x", padx=10, pady=5)
tk.Label(title_frame, text="AI Robot Simulator - Voice Controlled",
font=self.title_font, fg="white", bg="#2C3E50").pack(pady=10)
# Language Selection Frame
lang_frame = tk.Frame(master)
lang_frame.pack(fill="x", padx=10, pady=5)
tk.Label(lang_frame, text="Voice Language:", font=self.normal_font).pack(side="left")
self.lang_var = tk.StringVar(value="en")
tk.Radiobutton(lang_frame, text="English", variable=self.lang_var,
value="en", command=lambda: switch_language("en"),
font=self.small_font).pack(side="left", padx=10)
tk.Radiobutton(lang_frame, text="Arabic", variable=self.lang_var,
value="ar", command=lambda: switch_language("ar"),
font=self.small_font).pack(side="left", padx=10)
# Commands Frame
commands_frame = tk.Frame(master, relief="solid", bd=1, padx=10, pady=10)
commands_frame.pack(fill="x", padx=10, pady=5)
tk.Label(commands_frame, text="Supported Voice Commands:",
font=self.normal_font, justify="left").pack(anchor="w")
self.commands_display = tk.Text(commands_frame, height=6, wrap="word", font=("Arial", 9))
self.commands_display.pack(fill="x", pady=5)
# Robot Canvas
self.canvas = tk.Canvas(master, width=500, height=300, bg="white",
highlightthickness=1, highlightbackground="#CCCCCC")
self.canvas.pack(pady=10)
# Manual Control Buttons
button_frame = tk.Frame(master)
button_frame.pack(pady=10)
# Row 1
button_frame1 = tk.Frame(button_frame)
button_frame1.pack()
buttons1 = [
("Move Forward", "forward"),
("Move Backward", "backward"),
("Reset Position", "reset")
]
for text, action in buttons1:
btn = tk.Button(button_frame1, text=text,
command=lambda a=action: receive_command(OWNER_NAME, a, 1.0),
font=self.small_font, width=15, height=2,
bg="#27AE60", fg="white")
btn.pack(side="left", padx=5)
# Row 2
button_frame2 = tk.Frame(button_frame)
button_frame2.pack(pady=5)
buttons2 = [
("Turn Left", "left"),
("Turn Right", "right"),
("Wave Hand", "wave")
]
for text, action in buttons2:
btn = tk.Button(button_frame2, text=text,
command=lambda a=action: receive_command(OWNER_NAME, a, 1.0),
font=self.small_font, width=15, height=2,
bg="#2980B9", fg="white")
btn.pack(side="left", padx=5)
# Event Log Frame
log_frame = tk.Frame(master)
log_frame.pack(fill="both", expand=True, padx=10, pady=5)
tk.Label(log_frame, text="Event Log:",
font=self.normal_font, justify="left").pack(anchor="w")
# Text area with scrollbar
text_frame = tk.Frame(log_frame)
text_frame.pack(fill="both", expand=True)
self.log = tk.Text(text_frame, height=8, wrap="word", font=("Consolas", 9))
scrollbar = tk.Scrollbar(text_frame, command=self.log.yview)
self.log.config(yscrollcommand=scrollbar.set)
self.log.pack(side="left", fill="both", expand=True)
scrollbar.pack(side="right", fill="y")
# Pending Commands Frame
pending_frame = tk.Frame(master)
pending_frame.pack(fill="x", padx=10, pady=5)
tk.Label(pending_frame, text="Pending Commands:",
font=self.normal_font, justify="left").pack(anchor="w")
self.pending_box = tk.Listbox(pending_frame, height=3, font=self.small_font)
self.pending_box.pack(fill="x")
# Confirm button
confirm_btn = tk.Button(pending_frame, text="Confirm Last Command",
command=self.confirm_last,
font=self.small_font,
bg="#E67E22", fg="white", width=20)
confirm_btn.pack(pady=5)
self.draw_robot()
self.update_sensors()
self.update_language_display()
def confirm_last(self):
if pending:
last_cid = list(pending.keys())[-1]
confirm_cmd(last_cid)
def log_event(self, text):
timestamp = time.strftime("%H:%M:%S")
formatted_text = f"[{timestamp}] {text}"
self.log.insert("end", formatted_text + "\n")
self.log.see("end")
self.master.update()
def update_pending(self):
self.pending_box.delete(0, "end")
for cid, info in pending.items():
display_text = f"{cid}: {info['from']} → {info['action']}"
self.pending_box.insert("end", display_text)
def update_language_display(self):
"""Update the commands display based on current language"""
self.commands_display.delete(1.0, "end")
lang_name = "English" if current_language == "en" else "Arabic"
self.commands_display.insert("end", f"Current Language: {lang_name}\n\n")
for cmd_id, cmd_info in ACTION_MAP.items():
if current_language in cmd_info["keywords"]:
keywords = ", ".join(cmd_info["keywords"][current_language])
self.commands_display.insert("end", f"• {cmd_info['action']}: {keywords}\n")
self.commands_display.config(state="disabled")
def draw_robot(self):
self.canvas.delete("all")
# Robot body
self.canvas.create_rectangle(220, 150, 280, 250, fill="#B0BEC5", outline="#37474F", width=2)
# Head
self.canvas.create_oval(220, 100, 280, 150, fill="#FFEB3B", outline="#37474F", width=2)
# Eyes
self.canvas.create_oval(235, 115, 245, 125, fill="black")
self.canvas.create_oval(255, 115, 265, 125, fill="black")
# Arms
self.left_arm = self.canvas.create_line(220, 160, 180, 200, width=6, fill="#5D4037")
self.right_arm = self.canvas.create_line(280, 160, 320, 200, width=6, fill="#5D4037")
# Wheels
self.left_wheel = self.canvas.create_oval(210, 250, 230, 270, fill="#212121")
self.right_wheel = self.canvas.create_oval(270, 250, 290, 270, fill="#212121")
# Sensors
self.temp_sensor = self.canvas.create_rectangle(20, 20, 90, 40, fill="#F44336")
self.humidity_sensor = self.canvas.create_rectangle(100, 20, 170, 40, fill="#2196F3")
# Sensor labels
self.canvas.create_text(55, 30, text="Temp", fill="white", font=("Arial", 9, "bold"))
self.canvas.create_text(135, 30, text="Humidity", fill="white", font=("Arial", 9, "bold"))
def perform_action(self, action):
if action == "Wave Hand":
for i in range(3):
self.canvas.coords(self.right_arm, 280, 160, 340, 140)
self.master.update()
time.sleep(0.3)
self.canvas.coords(self.right_arm, 280, 160, 320, 200)
self.master.update()
time.sleep(0.3)
elif action == "Move Forward":
self.canvas.move("all", 0, -20)
self.master.update()
elif action == "Move Backward":
self.canvas.move("all", 0, 20)
self.master.update()
elif action == "Turn Left":
self.canvas.move(self.left_wheel, -15, 0)
self.canvas.move(self.right_wheel, -5, 0)
self.master.update()
elif action == "Turn Right":
self.canvas.move(self.left_wheel, 5, 0)
self.canvas.move(self.right_wheel, 15, 0)
self.master.update()
elif action == "Reset Position":
self.canvas.delete("all")
self.draw_robot()
self.master.update()
def update_sensors(self):
temp = random.randint(20, 35)
humidity = random.randint(30, 80)
# Update sensor colors
temp_color = f"#ff{max(0, 255-temp*7):02x}00"
hum_color = f"#0000{min(255, humidity*3):02x}"
self.canvas.itemconfig(self.temp_sensor, fill=temp_color)
self.canvas.itemconfig(self.humidity_sensor, fill=hum_color)
# Update sensor readings
self.canvas.delete("sensor_readings")
self.canvas.create_text(55, 30, text=f"{temp}°C", fill="white",
font=("Arial", 8, "bold"), tags="sensor_readings")
self.canvas.create_text(135, 30, text=f"{humidity}%", fill="white",
font=("Arial", 8, "bold"), tags="sensor_readings")
self.master.after(3000, self.update_sensors)
----- Application Startup -----
if name == "main":
root = tk.Tk()
app = RobotApp(root)
# Start background threads
threading.Thread(target=simulate_events, daemon=True).start()
threading.Thread(target=listen_microphone, daemon=True).start()
app.log_event("✅ Application ready")
app.log_event("🎤 Voice commands activated")
app.log_event("🔊 You can speak commands in English or Arabic")
app.log_event("🌐 Use the radio buttons to switch voice recognition language")
root.mainloop()