Shady - Robot Personal Assistant
by InventorKrish in Circuits > Arduino
16 Views, 0 Favorites, 0 Comments
Shady - Robot Personal Assistant

Hey, I’m Krishavardan, and I’m 12 years old. Meet Shady — a cutting-edge, AI-powered robot designed to be your personal assistant. Shady can handle everything from answering time-based questions to providing real-time updates. But what makes Shady stand out? It follows you around. Whether you need the current time, weather updates, or assistance with staying organized, Shady is always by your side. Powered by a combination of advanced AI and smart hardware, this robot is more than just intelligent — it’s responsive, adaptive, and perfectly aligned with your everyday needs.
You can check out the Shady - Personal Assistant Demo and get updates on the robot on my YouTube channel here:
Supplies
For Shady, I used 2x Arduino Uno R3 boards to control the various components and bring the robot to life. The robot's movement is powered by 2x DC motors, which are driven by an L298N Motor Driver. To power the system, I used a 1x 11.1V Lithium Battery along with a Current Regulator to ensure the correct voltage is supplied. The robot features a color tracking camera to enhance its interactive capabilities, and everything is wired together using a breadboard and jumper wires. For physical assembly, I used popsicle sticks, cardboard, glue, and hot glue to create the robot's chassis. Additionally, I used a USB cable for programming, and all of this is controlled and programmed through a laptop.
Creating the 3D Model



The 3D model accurately represents the chassis and overall appearance of the robot.
Creating the Chassis




The chassis is built from popsicle sticks and cardboard, held together using a combination of hot glue and Elmer's glue for added strength and stability.
Creating the Circuit Diagram


The images provided represent an early attempt to visualize the circuit design. The first circuit, which is housed inside the robot, follows this configuration: the receiver is connected to an Arduino Uno R3, which then links to an L298N dual motor driver. This driver is connected to a current regulator, which powers two DC motors. Additional components supporting this setup include an 11.1V battery for power, a breadboard for circuit organization, and jumper wires for all necessary connections. The second circuit, which connects externally to a laptop, is much simpler. It consists of a direct connection from the laptop to an Arduino Uno R3, which is then connected to a transmitter. No additional hardware is used in this secondary circuit.
Creating the Circuit



The image shows the physical circuit setup. Some components might be missing due to last-minute modifications, and part of the wiring is concealed inside the robot, routed through an opening underneath where the wheels are mounted.
AI Features


Shady is an advanced AI-powered personal assistant designed to deliver fast, intelligent, and context-aware interactions. Her core features include real-time updates on the date, year, month, week, day, and time, along with the ability to recognize and respond to specific events. She provides live weather forecasts, delivers news updates, and retrieves detailed information from Wikipedia. In addition to being informative, Shady functions as a productivity companion, offering reminder alerts to keep you on schedule. On the system control side, she’s equipped with commands to shut down, restart, or lock the computer, giving her a powerful edge as both an assistant and a digital overseer.
AI's Code
The source code for the AI integrated into the robot governs its time-sensitive operations, real-time data processing, and decision-making functions.
import speech_recognition as sr
import pyttsx3
import datetime
import wikipedia
import warnings
from bs4 import BeautifulSoup
import webbrowser
import os
import time
import subprocess
import wolframalpha
import json
import requests
import random
# Suppress the GuessedAtParserWarning from wikipedia package by specifying parser explicitly
import wikipedia.wikipedia as wp_wikipedia
def custom_parse(html):
return BeautifulSoup(html, features="html.parser")
wp_wikipedia.BeautifulSoup = custom_parse
warnings.filterwarnings("ignore", category=UserWarning, module='wikipedia')
# Shady Boot Up
print('I am now online and ready to assist you.')
engine = pyttsx3.init()
voices = engine.getProperty('voices')
# Use Microsoft Zira if available
zira_voice_id = None
for voice in voices:
if "Zira" in voice.name:
zira_voice_id = voice.id
break
if zira_voice_id:
engine.setProperty('voice', zira_voice_id)
else:
engine.setProperty('voice', voices[0].id) # fallback
def speak(text):
print(text)
engine.say(text)
engine.runAndWait()
def get_weather(city="Frisco"):
api_key = "8ef61edcf1c576d65d836254e11ea420" # This should be secured in production environments.
base_url = "https://api.openweathermap.org/data/2.5/weather?"
complete_url = base_url + "appid=" + api_key + "&q=" + city
try:
response = requests.get(complete_url)
x = response.json()
if x["cod"] != "404":
y = x["main"]
current_temperature = y["temp"]
current_humidity = y["humidity"]
z = x["weather"]
weather_description = z[0]["description"]
temp_celsius = current_temperature - 273.15
weather_info = (
f"Currently in {city.title()}: {weather_description}, "
f"{temp_celsius:.1f} degrees Celsius, humidity at {current_humidity}%."
)
return weather_info
else:
return f"City {city} could not be found."
except Exception:
return "Unable to retrieve weather information at the moment."
def get_news_flash():
placeholders = [
"Breaking news: Coffee demand is rising globally.",
"Update: Artificial Intelligence continues to evolve rapidly.",
"News: Global technological innovation remains at an all-time high.",
"Report: Proactive learning and development observed worldwide.",
]
return random.choice(placeholders)
def get_events():
events = [
"Meeting up with your friends and improving Shady."
]
if events:
return "Your schedule includes: " + "; ".join(events)
else:
return "You have no upcoming events."
def smart_answer(statement):
try:
result = wikipedia.summary(statement, sentences=2)
return result
except Exception:
return "Information on that topic could not be retrieved."
def take_command():
r = sr.Recognizer()
with sr.Microphone() as source:
print("Listening...")
audio = r.listen(source)
try:
statement = r.recognize_google(audio, language='en-in')
print(f"You said: {statement}\n")
except Exception:
return "none"
return statement.lower()
reminder_set = False
reminder_text = ""
reminder_start_time = None
next_reminder_interval = None
def set_reminder(text):
global reminder_set, reminder_text, reminder_start_time, next_reminder_interval
reminder_text = text
reminder_set = True
reminder_start_time = time.time()
next_reminder_interval = None
speak(f"Reminder set: {reminder_text} I will remind you after one hour to three hours.")
def run_assistant():
speak("I am fully operational and standing by.")
try:
while True:
global reminder_set, reminder_text, reminder_start_time, next_reminder_interval
# Check for reminder timing
if reminder_set:
current_time = time.time()
elapsed_time = current_time - reminder_start_time
# Start reminders only after 5 seconds (for testing)
if elapsed_time >= 5:
if next_reminder_interval is None:
# Set next reminder interval randomly between 5 to 15 seconds (for testing)
next_reminder_interval = random.randint(5, 15)
reminder_start_time = current_time # reset start time for next interval
elif elapsed_time >= next_reminder_interval:
speak(f"Reminder: {reminder_text}")
# After first quick reminder, reset to 1 to 3 hours interval
reminder_start_time = current_time
next_reminder_interval = random.randint(3600, 10800)
# Clear reminder so it does not repeat the same reminder
reminder_set = False
reminder_text = ""
statement = take_command()
if statement == "none" or not statement.strip():
continue
if "goodbye" in statement or "stop" in statement or "shutdown" in statement:
speak("I am shutting down now. Have a productive day.")
print("I am shutting down now. Goodbye.")
break
elif "time" in statement:
str_time = datetime.datetime.now().strftime("%I:%M %p").lstrip("0")
speak(f"The current time is {str_time}.")
elif "date" in statement:
str_date = datetime.datetime.now().strftime("%B %d, %Y")
speak(f"Today's date is {str_date}.")
elif "year" in statement:
year = datetime.datetime.now().year
speak(f"The current year is {year}.")
elif "month" in statement:
month = datetime.datetime.now().strftime("%B")
speak(f"The current month is {month}.")
elif "week" in statement or "weekday" in statement:
week_number = datetime.datetime.now().isocalendar()[1]
speak(f"This is week number {week_number} of the year.")
elif "day" in statement:
day = datetime.datetime.now().strftime("%d").lstrip("0")
speak(f"Today is the {day}th day of the month.")
elif "weather" in statement:
city = "Frisco" # Default city
words = statement.split() # Splitting the statement into words to look for a city name
# Search for a city name in the command
for i, word in enumerate(words):
if word.lower() == "weather" and i + 1 < len(words):
city = words[i + 1] # Set the city name after "weather"
break
# Fetch weather information for the city
weather_info = get_weather(city)
speak(weather_info)
# Extract temperature from the weather_info to give clothing recommendation
temp_celsius = float(weather_info.split()[5]) # Extract the temperature (assuming temp is in 6th position)
# Clothing recommendations based on the temperature
if temp_celsius < 10:
speak("Wear a warm jacket and scarf.")
elif temp_celsius < 20:
speak("A light jacket or sweater should be fine.")
else:
speak("Dress lightly! T-shirt and pants are perfect.")
elif "news" in statement:
news = get_news_flash()
speak(news)
elif "event" in statement or "schedule" in statement:
events = get_events()
speak(events)
elif "search" in statement:
search_query = statement.replace('search', '').strip()
if search_query:
webbrowser.open(f"https://www.google.com/search?q={search_query}")
speak(f"Opening Google search for {search_query}.")
elif "lock" in statement:
os.system('rundll32.exe user32.dll,LockWorkStation')
speak("System is now locked.")
elif "restart" in statement:
os.system('shutdown /r /f')
speak("System is restarting now.")
elif "shut down" in statement:
os.system('shutdown /s /f')
speak("System is shutting down now.")
elif "reminder" in statement:
# Remove the keyword "reminder" from the statement to get the reminder text
reminder_text = statement.replace("reminder", "").strip()
if reminder_text:
set_reminder(reminder_text)
speak("Reminder Set.")
else:
speak("Please specify what you want me to remind you about.")
else:
speak("Did you say something? Would you like me to provide information on it?")
user_response = take_command()
if user_response in ["yes", "yeah", "sure", "go ahead"]:
answer = smart_answer(statement)
if answer == "Information on that topic could not be retrieved.":
speak("Sorry, I couldn't find any information on that topic.")
else:
speak(answer)
elif user_response in ["no", "nope", "not really", "no thanks"]:
speak("Okay, I'll be waiting for your command.")
else:
speak("I didn't understand that. Please respond with 'yes' or 'no'.")
except Exception as e:
print(f"Error: {e}")
speak("An unexpected error occurred. Attempting to recover.")
if __name__ == '__main__':
run_assistant()
Robot's Calculation/movement Code
The provided code controls the robot's movement and calculates its direction and distance based on the circumference of its wheels, which is 1 inch. The motors are connected to an L298N motor driver, which is controlled by an Arduino Uno R3. The Arduino receives commands from a transmitter connected to another Arduino, allowing the robot to move in specific directions (forward, backward, left, or right). The code calculates how many rotations of the wheel are needed to cover a certain distance, ensuring the robot moves the desired amount. The Arduino continuously adjusts the movement by sending commands to the motor driver, while also determining the robot's direction, allowing it to follow the intended path accurately. Source Code:
import cv2
import numpy as np
import serial
import time
# Set up serial communication with the Arduino
arduino = serial.Serial('COM3', 9600) # Update with your Arduino's COM port
time.sleep(2) # Wait for Arduino to initialize
# Camera setup
cap = cv2.VideoCapture(0) # Use your camera (0 for default webcam)
# Color range for detecting the object (example: detecting a red object)
lower_red = np.array([0, 120, 70])
upper_red = np.array([10, 255, 255])
# Wheel specifications
wheel_circumference = 1.0 # Wheel circumference in inches
steps_per_rotation = 360 # Number of steps for one full rotation of the wheel
# Variables for distance and previous position
prev_position = None
prev_distance = 0 # To keep track of total distance traveled
def send_command(command):
""" Send a command to the Arduino over serial communication. """
arduino.write(command.encode())
def detect_object(frame):
""" Detect a colored object in the frame (red in this case). """
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
mask = cv2.inRange(hsv, lower_red, upper_red)
# Find contours of the object
contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
if contours:
# Get the largest contour (assuming the object is the largest one)
largest_contour = max(contours, key=cv2.contourArea)
# Get the center of the object
M = cv2.moments(largest_contour)
if M["m00"] != 0:
cx = int(M["m10"] / M["m00"])
cy = int(M["m01"] / M["m00"])
return (cx, cy)
return None
def calculate_rotations(distance):
""" Calculate the number of steps needed for the motors to travel a certain distance. """
rotations = (distance / wheel_circumference) * steps_per_rotation
return int(rotations)
def calculate_direction(prev_position, current_position):
""" Calculate the direction of movement based on object displacement. """
if prev_position is None or current_position is None:
return "STAY", 0
delta_x = current_position[0] - prev_position[0]
delta_y = current_position[1] - prev_position[1]
# Define thresholds to determine the movement direction
if abs(delta_x) > abs(delta_y):
if delta_x > 0:
return "RIGHT", abs(delta_x)
else:
return "LEFT", abs(delta_x)
else:
if delta_y > 0:
return "FORWARD", abs(delta_y)
else:
return "BACKWARD", abs(delta_y)
return "STAY", 0
def move_robot(direction, distance):
""" Move the robot based on the detected direction and distance. """
rotations = calculate_rotations(distance)
for _ in range(rotations):
send_command(direction)
time.sleep(0.1) # Adjust the delay to match the motor's speed
# Main loop to capture video and detect movement
while True:
ret, frame = cap.read()
if not ret:
break
# Detect object
current_position = detect_object(frame)
if current_position:
# Draw a circle at the object’s position
cv2.circle(frame, current_position, 10, (0, 255, 0), -1)
# Calculate direction and movement
if current_position and prev_position:
direction, displacement = calculate_direction(prev_position, current_position)
if direction != "STAY":
print(f"Direction: {direction}, Displacement: {displacement}")
# Move robot based on direction and displacement
move_robot(direction, displacement)
# Update the previous position
prev_position = current_position
# Show the frame
cv2.imshow("Robot Movement", frame)
# Exit condition (press 'q' to exit)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
# Release resources
cap.release()
cv2.destroyAllWindows()