TTS
The /qtrobot/tts/... services let QTrobot speak — plain text or SSML, across multiple engines, with full control over voice, language, rate, and pitch. These examples assume the gateway from Connection is already running.
QTrobot ships with Acapela configured as the default TTS engine.
List engines, languages, and voices
ros2 service call /qtrobot/tts/engines/list qtrobot_interfaces/srv/TtsEnginesList "{}"
ros2 service call /qtrobot/tts/engine/languages/get qtrobot_interfaces/srv/TtsEngineLanguagesGet "{engine: 'acapela'}"
ros2 service call /qtrobot/tts/engine/voices/get qtrobot_interfaces/srv/TtsEngineVoicesGet "{engine: 'acapela'}"
Each robot ships with a set of pre-installed Acapela voices, listed above. If you need a voice that isn't installed, pick one from the Acapela voice repertoire and contact support@luxai.com to have it installed.
Engine configuration and default engine
ros2 service call /qtrobot/tts/engine/configure/get qtrobot_interfaces/srv/TtsEngineConfigureGet "{engine: 'acapela'}"
ros2 service call /qtrobot/tts/engine/configure/set qtrobot_interfaces/srv/TtsEngineConfigureSet \
"{config: '{\"pitch\": 1.0, \"rate\": 0.8}', engine: 'acapela'}"
ros2 service call /qtrobot/tts/default_engine/get qtrobot_interfaces/srv/TtsDefaultEngineGet "{}"
ros2 service call /qtrobot/tts/default_engine/set qtrobot_interfaces/srv/TtsDefaultEngineSet "{engine: 'acapela'}"
config is a JSON-encoded string, since service definitions don't support arbitrary nested dicts.
Say text
#!/usr/bin/env python3
import rclpy
from rclpy.node import Node
from qtrobot_interfaces.srv import TtsEngineSayText
class TtsClient(Node):
def __init__(self):
super().__init__('tts_client')
self.client = self.create_client(TtsEngineSayText, '/qtrobot/tts/engine/say/text')
self.client.wait_for_service(timeout_sec=5.0)
def say(self, text: str, **kwargs):
request = TtsEngineSayText.Request(text=text, **kwargs)
future = self.client.call_async(request)
rclpy.spin_until_future_complete(self, future)
return future.result()
def main():
rclpy.init()
node = TtsClient()
node.say('Hello, This is spoken with the default settings.')
node.say('This is spoken slower at a higher pitch.', engine='acapela', rate=0.85, pitch=1.2)
node.say('This is spoken with the Rosie voice.', engine='acapela', voice='Rosie')
node.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()
ros2 service call /qtrobot/tts/engine/say/text qtrobot_interfaces/srv/TtsEngineSayText \
"{text: 'I will speak with different speed. \\\\rspd=130\\\\ for example now I am speaking faster.', engine: 'acapela'}"
\rspd=130\ above is an Acapela voice tag, not SSML — see SSML below for the distinction.
Cancel speech
ros2 service call /qtrobot/tts/engine/cancel qtrobot_interfaces/srv/TtsEngineCancel "{}"
SSML
Acapela does not support SSML. Instead, it has its own set of special voice tags (like the \rspd=...\ speed tag used above) — see the Acapela voice tag reference for the full list. SSML is supported by other engines, such as Azure below.
ros2 service call /qtrobot/tts/engine/supports/ssml qtrobot_interfaces/srv/TtsEngineSupportsSsml "{engine: 'azure'}"
from qtrobot_interfaces.srv import TtsEngineSaySsml
ssml = (
'<speak version="1.0" xmlns="http://www.w3.org/2001/10/synthesis" xmlns:mstts="https://www.w3.org/2001/mstts" xml:lang="en-US">'
' <voice name="en-US-MultiTalker-Ava-Andrew:DragonHDLatestNeural">'
' <mstts:dialog>'
' <mstts:turn speaker="ava">Hello, Andrew! How is your day going?</mstts:turn>'
' <mstts:turn speaker="andrew">Hey Ava! It has been great, just exploring some new features in QTrobot.</mstts:turn>'
' </mstts:dialog>'
' </voice>'
'</speak>'
)
# Same client/call pattern as the TtsClient node above, targeting /qtrobot/tts/engine/say/ssml
client = node.create_client(TtsEngineSaySsml, '/qtrobot/tts/engine/say/ssml')
future = client.call_async(TtsEngineSaySsml.Request(ssml=ssml, engine='azure'))
rclpy.spin_until_future_complete(node, future)
Azure TTS
Azure Text-to-Speech is a cloud-based engine offering hundreds of neural voices across 140+ languages, full SSML control (pitch, rate, volume, pauses, pronunciation), and even custom voice cloning. It's a good option when you need a voice, language, or SSML feature Acapela doesn't provide.
1. Get an Azure subscription key
Follow Microsoft's Azure AI Speech documentation to create a free Azure subscription and get your speech resource's subscription key and region.
2. Configure the key on QTRP
From QTPC:
ssh qtrp
sudo nano /opt/luxai/qtrobot_service_hub/etc/tts/azure.yaml
Set your region and subscription key:
parameters:
- name: region
type: string
value: "westeurope" # your Azure resource region
- name: subscription_key
type: string
value: "" # your Azure subscription key
3. Enable the Azure engine
sudo nano /opt/luxai/qtrobot_service_hub/etc/tts/tts.yaml
Add azure to the list of enabled engines:
- name: engines
type: list[string]
value: [acapela, azure]
scope: cli
4. Restart the service
sudo systemctl restart qtrobot-service-hub.service
sudo systemctl status qtrobot-service-hub.service
Once restarted, azure will show up in the /qtrobot/tts/engines/list response and is ready to use with engine: 'azure' on the say-text/say-ssml services.
Next steps
Continue with the Face and Emotion tutorial, or see the full tts namespace in the ROS2 API Reference.