Skip to content

Commit

Permalink
yuri's Face
Browse files Browse the repository at this point in the history
  • Loading branch information
Matt White committed Jun 23, 2022
1 parent 4975dbd commit e33e2d6
Show file tree
Hide file tree
Showing 6 changed files with 171 additions and 25 deletions.
1 change: 1 addition & 0 deletions Pipfile
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ tensorflow = {file = "https://github.com/bitsy-ai/tensorflow-arm-bin/releases/do
pip = "*"
adafruit-pca9685 = "*"
adafruit-circuitpython-servokit = "*"
pyttsx3 = "*"

[dev-packages]
ipython = "*"
Expand Down
33 changes: 20 additions & 13 deletions Pipfile.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ sudo apt install swig libpulse-dev libasound2-dev git uidmap pipenv \
make build-essential libssl-dev zlib1g-dev \
libbz2-dev libreadline-dev libsqlite3-dev wget curl llvm \
libncursesw5-dev xz-utils tk-dev libxml2-dev libxmlsec1-dev \
libffi-dev liblzma-dev python3-pyaudio portaudio19-dev
libffi-dev liblzma-dev python3-pyaudio portaudio19-dev \
espeak ffmpeg libespeak1
git clone https://github.com/pyenv/pyenv.git ~/.pyenv
cd ~/.pyenv && src/configure && make -C src
Expand Down
2 changes: 1 addition & 1 deletion yuri.yaml
Original file line number Diff line number Diff line change
@@ -1 +1 @@

speaker_type: pyttsx3
136 changes: 127 additions & 9 deletions yuri/servos.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,148 @@
import board
import busio

from typing import List
from dataclasses import dataclass

from loguru import logger
from adafruit_motor import servo
from adafruit_servokit import ServoKit
from adafruit_pca9685 import PCA9685

from yuri.config import Config


@dataclass
class Servo:
servo: servo.Servo
min_angle: int
max_angle: int

@property
def neutral_angle(self) -> int:
return (self.max_angle + self.min_angle) / 2

def max(self):
self.servo.angle = self.max_angle

def min(self):
self.servo.angle = self.min_angle

def neutral(self):
self.servo.angle = self.neutral_angle

@property
def angle(self):
return self.servo.angle


@dataclass
class Face:
left_eye: Servo
right_eye: Servo
nose: Servo
left_mouth: Servo
right_mouth: Servo

def smile(self):
self.left_eye.max()
self.right_eye.max()

self.left_mouth.max()
self.right_mouth.max()

def frown(self):
self.left_eye.min()
self.right_eye.min()

self.left_mouth.min()
self.right_mouth.min()

@property
def servos(self) -> List[Servo]:
return [
self.left_eye,
self.right_eye,
self.nose,
self.left_mouth,
self.right_mouth,
]

def neutral(self):
for servo in self.servos:
servo.neutral()

def min(self):
for servo in self.servos:
servo.min()

def max(self):
for servo in self.servos:
servo.max()



FAST = 0.3


class Servos:
def __init__(self, config: Config):
self.config = config
self.i2c = busio.I2C(board.SCL, board.SDA)
self.pca = PCA9685(self.i2c)
self.pca.frequency = 50
self.servos = [
servo.Servo(self.pca.channels[idx]) for idx in range(5)
]
self.eye_l, self.eye_r, self.nose, self.mouth_l, self.mouth_r = self.servos
self.face = Face(
left_eye=Servo(
servo=servo.Servo(self.pca.channels[0]),
min_angle=100,
max_angle=30,
),
right_eye=Servo(
servo=servo.Servo(self.pca.channels[1]),
min_angle=50,
max_angle=150,
),
nose=Servo(
servo=servo.Servo(self.pca.channels[2]),
min_angle=100,
max_angle=0,
),
left_mouth=Servo(
servo=servo.Servo(self.pca.channels[3]),
min_angle=60,
max_angle=120,
),
right_mouth=Servo(
servo=servo.Servo(self.pca.channels[4]),
min_angle=120,
max_angle=60,
),
)






def rotate(self):
logger.info("triggering servos")
for servo in self.servos:
servo.angle = 180
time.sleep(1)
servo.angle = 0
time.sleep(1)
breakpoint()
# for servo in self.servos:
# servo.angle = 180
# time.sleep(1)
# servo.angle = 0
# time.sleep(1)
logger.info("done")

def pulse(self, servo: servo.ContinuousServo, seconds=0.001, speed=FAST):
servo.throttle = speed
time.sleep(seconds)
servo.throttle = 0


def smile(self):
self.eye_l.throttle = FAST
self.eye_r.throttle = FAST
time.sleep(.001)
self.eye_l.throttle = 0
self.eye_r.throttle = 0
21 changes: 20 additions & 1 deletion yuri/speaker.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
from abc import abstractmethod, ABCMeta
from io import BytesIO

import pyttsx3

from gtts import gTTS
from loguru import logger
from pydub import AudioSegment
Expand All @@ -23,17 +25,34 @@ def say(self, message: str):
logger.info("say.start", message=message)

mp3_fp = BytesIO()
tts = gTTS(message, lang="en")
tts = gTTS(message, lang="en", tld="ru")
tts.write_to_fp(mp3_fp)
mp3_fp.seek(0)

play(AudioSegment.from_mp3(mp3_fp))
logger.info("say.done", message=message)

class Ttsx3Speaker(Speaker):
def __init__(self, config: Config):
self.config = config
self.engine = pyttsx3.init()
# voices 2, 13, 14, 17
# 61 = Russian
# 62 = Slovak
self.engine.setProperty("voice", self.engine.getProperty("voices")[15].id)
self.engine.setProperty("rate", 160)

def say(self, message: str):
logger.info("say.start", message=message)
self.engine.say(message)
self.engine.runAndWait()
logger.info("say.done", message=message)


class SpeakerFactory:
SPEAKERS = {
"google": GoogleSpeaker,
"pyttsx3": Ttsx3Speaker,
}

@classmethod
Expand Down

0 comments on commit e33e2d6

Please sign in to comment.