I developed a set of functions ( from before espeak became espeak-ng). Would be interested in a comparative analysis, but understand “a solution” is all that is needed.
There are times that it is desirable to speak while continuing on with the program. Does the Python espeakng package wait until speaking is complete or speak asynchronously?
Thanks for sharing! AFAIK, the espeakng say() function is blocking so the robot can’t “walk and talk” as it were.
A related ROS question - ROS nodes run asynchronously but, unless ROS is multithreaded, don’t node activities sometimes block each other? More specifically, if you had a ROS “speaker” node that gave a speech over speaker, wouldn’t all the other nodes just have to wait until the monolog was over? Conversely, if ROS is multithreaded then the robot really should be able to “walk and talk”.
Multi-processing and multi-threading in ROS vs ROS 2 and Python vs C++ are the deep abyss waiting to consume all available time - human and processors’.
I have had luck putting my multi-process TTS calls in the main of a ROS 2 node, but never in a callback or a function that calls spin().
..
import subprocess
--
def say(phrase,vol=100):
phrase = phrase.replace("I'm","I m")
phrase = phrase.replace("'","")
phrase = phrase.replace('"',' quote ')
phrase = phrase.replace('*',"")
# Carl's voice
# subprocess.check_output(['espeak-ng -s150 -ven-us+f5 -a'+str(vol)+' "%s"' % phrase], stderr=subprocess.STDOUT, shell=True)
# Dave's voice
subprocess.check_output(['espeak-ng -s300 -a'+str(vol)+' "%s"' % phrase], stderr=subprocess.STDOUT, shell=True)
def main(args=None):
rclpy.init(args=args)
subscriber = Subscriber()
publisher = Publisher()
command = Twist()
say("I'm Humble Dave. Starting Ross 2 wanderer",vol=200)
time.sleep(2) # Wait till done talking
while 1: # main loop. The robot goes forward until obstacle, and then turns until its free to advance, repeatedly.
time.sleep(1)
say("Going forward",vol=50) # Walk and talk at the same time
go_forward_until_obstacle(subscriber, publisher, command)
say("Something too close", vol=50)
rotate_until_clear(subscriber, publisher, command)
..
I think I have seen some examples of these in GitHub - Eventually, this is the correct architecture for using any robot resource. The TTS node should have both a listener and a service or action. The listener would provide asynchronous TTS, and the service would provide synchronous TTS. As an action, the TTS node could implement preempting one TTS stream with a more important TTS stream, and manage a queue of speech requests. This node would be complicated since the handler for a topic is a callback, and you don’t put long running things like TTS in callbacks.