1. 为什么选择Ekho TTS在嵌入式Linux平台?
在开发嵌入式Linux设备的语音交互功能时,离线语音合成(TTS)是个硬需求。我做过不少智能硬件项目,发现很多场景下设备根本连不上网,比如工业控制设备、户外智能终端,或者对隐私要求极高的医疗设备。这时候在线语音合成API就完全派不上用场了。
市面上开源的TTS引擎不少,但真正适合嵌入式环境的却不多。Ekho(余音)这个项目我用了五六年,它的优势很明显:首先是纯离线运行,不依赖网络;其次是跨平台支持好,从x86到ARM架构都能跑;最重要的是它对中文支持很友好,普通话、粤语甚至古代汉语都能处理。记得去年给一个博物馆做导览设备,就用Ekho实现了文言文朗读,现场效果出乎意料的好。
不过Ekho的机械音确实比较明显,不像商业方案那么自然。但经过参数调优和后期处理,完全可以达到实用水平。我经手的一个电梯语音提示项目,就是用Ekho做的,用户反馈根本听不出是合成语音。
2. 搭建嵌入式Linux开发环境
2.1 硬件准备要点
做嵌入式开发首先得搞定交叉编译环境。我习惯用树莓派CM4模块做验证,它的Cortex-A72核心性能足够跑TTS,价格也便宜。实际项目中要根据需求选择硬件,比如需要低功耗就选全志H616,要高性能可以考虑瑞芯微RK3588。
最近在做一个智能门锁项目,用的是全志R328芯片,内存只有128MB。这种资源受限的环境特别考验优化能力,后面我会详细讲怎么裁剪Ekho的内存占用。
2.2 系统环境配置
Ubuntu 20.04是我最推荐的开发环境,稳定性好,软件包齐全。先装好基础工具链:
sudo apt update sudo apt install -y gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf如果是Yocto或Buildroot定制系统,记得把这些依赖包加进镜像:
- alsa-lib(音频输出)
- libsndfile(WAV文件处理)
- pulseaudio(音频服务)
我遇到过最坑的问题是声卡驱动不兼容。有一次客户用的是特殊型号的音频芯片,Ekho死活不出声。后来发现是ALSA配置问题,解决方法是在/etc/asound.conf里加上:
pcm.!default { type plug slave.pcm "hw:0,0" }3. Ekho的编译与移植实战
3.1 源码获取与交叉编译
Ekho的最新源码在官方git仓库:
git clone https://gitlab.com/ekho/ekho.git交叉编译的关键是配置好--host参数:
./configure --host=arm-linux-gnueabihf \ --prefix=/usr/ekho \ --with-audio=alsa make -j$(nproc)这里有个坑要注意:新版Ekho依赖espeak-ng而不是老旧的espeak。如果编译报错提示"espeak_ng.h not found",需要先安装:
sudo apt install espeak-ng libespeak-ng-dev3.2 依赖库的精简策略
嵌入式系统存储空间紧张,可以用这些方法瘦身:
- 删除不需要的语音包(比如只用普通话就删掉粤语资源)
- 静态链接关键库减少动态依赖
- 使用strip命令裁剪调试符号:
arm-linux-gnueabihf-strip ekho实测下来,完整安装需要25MB空间,经过优化可以压缩到8MB左右。对于Flash只有16MB的设备,还可以把语音数据放到TF卡上,通过--data-path参数指定路径。
4. 系统集成与性能优化
4.1 开机自启动方案
在systemd系统里创建服务文件/etc/systemd/system/ekho-tts.service:
[Unit] Description=Ekho TTS Service After=network.target sound.target [Service] ExecStart=/usr/bin/ekho --daemon Restart=always [Install] WantedBy=multi-user.target如果是老式的init.d系统,可以在/etc/rc.local添加:
/usr/bin/ekho --daemon &4.2 语音质量调优技巧
Ekho默认参数生成的语音比较生硬,我总结了几条优化经验:
- 调整语速(-s参数):
ekho -s 50 "语速降低更清晰"- 添加韵律标记(用//标注重音):
ekho "这句话//重点词要强调"- 后期音频处理(用sox工具):
ekho "需要优化的文本" -o raw.wav sox raw.wav final.wav tempo 0.9 pitch 50 norm给农业设备做语音提示时,发现加入0.5秒淡入淡出能显著提升听感:
sox input.wav output.wav fade t 0.5 0 0.55. 实际应用中的问题排查
5.1 常见音频问题解决
如果Ekho没声音,按这个顺序检查:
- 确认声卡设备节点存在:
ls /dev/snd/- 测试ALSA基础功能:
aplay -l speaker-test -t wav -c 2- 检查PulseAudio服务状态:
pactl list sinks遇到最奇葩的问题是音频输出有杂音,最后发现是电源干扰。解决方法是在播放前设置合适的缓冲参数:
export EKHO_AUDIO_BUFFER=10245.2 内存泄漏排查案例
在长期运行的设备上,发现Ekho会缓慢增加内存占用。用valgrind检测发现是语音缓存没清理:
valgrind --leak-check=full ekho "测试文本"解决方法是在调用libekho的代码中定期执行:
ekho_clean_cache();或者直接使用--restrict-cache参数限制缓存大小:
ekho --restrict-cache=50 "文本"6. 进阶应用开发
6.1 Python集成方案
通过CFFI封装Ekho的C接口,实测延迟能控制在200ms内:
import cffi ffi = cffi.FFI() ffi.cdef(""" void ekho_speak(const char* text); """) ekho = ffi.dlopen("/usr/lib/libekho.so") ekho.ekho_speak("Python调用成功")6.2 语音播报队列实现
在需要连续播报的场景,我设计了个简单的队列系统:
#include <pthread.h> #include <queue> std::queue<std::string> msg_queue; pthread_mutex_t lock; void* tts_thread(void*) { while(1) { if(!msg_queue.empty()) { pthread_mutex_lock(&lock); std::string text = msg_queue.front(); msg_queue.pop(); pthread_mutex_unlock(&lock); ekho_speak(text.c_str()); } usleep(100000); } }这个方案在智能快递柜项目上运行很稳定,能同时处理触摸屏操作和语音提示。
7. 替代方案对比
虽然Ekho很优秀,但有些场景可能需要其他方案。我做过一个对比测试:
| 方案 | 内存占用 | 语音质量 | 多语言支持 | 授权方式 |
|---|---|---|---|---|
| Ekho | 15MB | 中 | 优秀 | GPL |
| Festival | 50MB | 较差 | 一般 | BSD |
| Pico TTS | 5MB | 较好 | 有限 | Apache |
| 讯飞离线SDK | 30MB | 优秀 | 优秀 | 商业授权 |
如果是教育类产品,推荐用Ekho+Pico TTS混合方案:中文用Ekho,英文用Pico,这样能在保证质量的同时控制资源占用。