去年在开发家庭服务机器人时,我遇到一个有趣的需求:让机器人能够为视力障碍用户朗读电子书。传统语音合成方案要么音质生硬,要么需要复杂部署。直到发现百度TTS与ROS2的结合方案,才真正实现了自然流畅的"AI说书人"效果。这个系统核心在于ROS2的分布式架构与百度TTS云端API的无缝对接,通过定制化功能包将文本到语音的转换过程封装成标准的ROS2服务节点。
系统采用典型的服务-客户端模型:
tts_server节点
/tts_text话题接收待合成文本/tts_audio话题输出PCM数据novel_reader节点
关键通信接口定义:
python复制# tts_interfaces/srv/Synthesize.srv
string text
---
uint8[] audio_data
float32 sample_rate
百度语音合成API提供多种音色选择(标准女声、情感男声等),通过SDK核心调用流程如下:
python复制from aip import AipSpeech
client = AipSpeech(APP_ID, API_KEY, SECRET_KEY)
def synthesize(text):
result = client.synthesis(
text,
'zh', 1, {
'vol': 5, # 音量[0-15]
'per': 4, # 音色编号
'spd': 5, # 语速[0-9]
'pit': 5 # 音调[0-9]
})
if not isinstance(result, dict):
return result
else:
raise TTSException(result['err_msg'])
重要提示:百度API有QPS限制(免费版10次/秒),需在节点中加入请求队列管理
code复制tts_ros2/
├── config/
│ ├── baidu_tts.yaml # API凭证配置
│ └── voice_profiles.yaml # 音色参数预设
├── launch/
│ └── novel_reader.launch.py
├── src/
│ ├── tts_server.cpp # 核心服务节点
│ └── novel_reader.py # 客户端逻辑
└── package.xml
cpp复制// 将PCM数据封装为ROS2 Audio消息
auto audio_msg = std::make_shared<audio_msgs::msg::Audio>();
audio_msg->header.stamp = now();
audio_msg->encoding = "LINEAR16";
audio_msg->sample_rate = 16000;
audio_msg->data.assign(pcm_data.begin(), pcm_data.end());
python复制# 通过rclpy参数声明支持运行时调整
self.declare_parameters(
namespace='',
parameters=[
('voice_profile', 'gentle_female'),
('speech_rate', 4),
('volume_gain', 1.2)
]
)
采用LRU缓存最近合成的100条文本:
python复制from cachetools import LRUCache
self._audio_cache = LRUCache(maxsize=100)
def get_cached_audio(text):
key = hash(text)
if key in self._audio_cache:
return self._audio_cache[key]
audio = synthesize(text)
self._audio_cache[key] = audio
return audio
async_send_request实现非阻塞调用cpp复制auto qos = rclcpp::QoS(
rclcpp::KeepLast(10),
rmw_qos_profile_sensor_data
);
可能原因及解决方案:
| 现象 | 排查步骤 | 解决方法 |
|---|---|---|
| 规律性卡顿 | 检查top查看CPU负载 |
限制合成线程数 |
| 随机卡顿 | ros2 topic bw /tts_audio |
调整QoS带宽设置 |
| 首句延迟 | 测量API响应时间 | 启用预加载缓存 |
python复制try:
audio = tts_client.call(text)
except Exception as e:
if "qps limit" in str(e):
self.get_logger().warn("QPS超限,启用降级策略")
return self._fallback_tts(text)
elif "invalid text" in str(e):
self._clean_text(text)
通过修改百度API的lang参数实现:
python复制LANG_MAP = {
'en': {'lang': 'en', 'per': 0},
'jp': {'lang': 'cte', 'per': 3}
}
def set_language(lang):
self._voice_config.update(LANG_MAP.get(lang, {}))
为移动机器人添加语音播报功能:
python复制def pose_callback(msg):
if need_announce(msg):
text = f"当前到达{landmark}附近"
self.tts_pub.publish(String(data=text))
我在实际部署中发现,将合成音频的采样率统一转换为16000Hz后,能显著降低ROS2网络传输负载。对于长文本朗读,建议采用"预加载下一章+当前章流式播放"的方案,实测可减少70%的用户等待时间。