作者:诺言
项目名称:小端机器人
开源协议:MIT
一个完全免费的公益项目,让AI陪伴每一个家庭
一、项目背景:为什么要做这个项目?
去年春节回家,看到父母拿着手机,想查个天气、问个问题,却因为打字慢、不会用搜索而放弃。那一刻我突然意识到:AI技术发展这么快,但真正能让老人轻松使用的工具却很少。
市面上的语音助手要么需要联网、要么有隐私顾虑、要么功能单一。我想做一个:
-
✅ 完全免费,无广告
-
✅ 离线识别,保护隐私
-
✅ 简单易用,老人小孩都能用
-
✅ 功能丰富,能聊天、能放歌、有记忆
于是,小端机器人诞生了。
二、核心功能展示
1. 语音对话
只需说"小端"唤醒,就能像和朋友聊天一样:
-
"小端,武汉明天有没有雨?"
-
"小端,陪我聊聊天"
-
"小端,什么是人工智能?"
2. 本地音乐播放
支持播放手机里的音乐:
-
"小端,放首歌"
-
"小端,播放告白气球"
-
"小端,换一首"
3. 记忆系统
会记住你说过的话,越聊越懂你:
-
"小端,我昨天说了什么?"
-
"小端,我喜欢什么?"
4. 离线语音识别
使用 Sherpa-ONNX 实现完全离线的语音识别,不联网也能用,保护隐私。
三、技术架构
技术栈选择
| 模块 | 技术方案 | 选择理由 |
|------|---------|---------|
| 开发语言 | Kotlin | 简洁、安全、协程支持好 |
| UI框架 | Jetpack Compose | 声明式UI,开发效率高 |
| 语音识别 | Sherpa-ONNX | 完全离线,支持中英文 |
| 语音合成 | Edge TTS | 免费、音质好、支持多音色 |
| AI对话 | 豆包API | 免费额度高,响应快 |
| 本地存储 | SQLite | 轻量、稳定 |
架构设计
┌─────────────────────────────────────┐
│ MainActivity │
│ (Jetpack Compose UI + 生命周期管理) │
└──────────────┬──────────────────────┘
│
┌───────┴───────┐
│ │
┌──────▼──────┐ ┌─────▼──────┐
│ SherpaSpeech│ │ EdgeTTS │
│ Manager │ │ (TTS合成) │
│ (离线ASR) │ └────────────┘
└──────┬──────┘
│
┌──────▼──────┐ ┌─────────────┐
│ AIManager │ │ MusicPlayer │
│ (豆包对话) │ │ (音乐播放) │
└──────┬──────┘ └─────────────┘
│
┌──────▼──────┐
│MemoryManager│
│ (记忆系统) │
└─────────────┘
四、核心技术实现
1. 离线语音识别(Sherpa-ONNX)
为什么选择 Sherpa-ONNX?
-
✅ 完全离线,不需要联网
-
✅ 支持中英文混合识别
-
✅ 识别准确率高
-
✅ 资源占用低
集成步骤:
class SherpaSpeechManager(private val context: Context) {
private var recognizer: OnlineRecognizer? = null
private var stream: OnlineStream? = null
fun initRecognizer() {
val config = OnlineRecognizerConfig(
featConfig = FeatureConfig(
sampleRate = 16000,
featureDim = 80
),
modelConfig = OnlineModelConfig(
transducer = OnlineTransducerModelConfig(
encoder = "asr-model/encoder-epoch-99-avg-1.onnx",
decoder = "asr-model/decoder-epoch-99-avg-1.onnx",
joiner = "asr-model/joiner-epoch-99-avg-1.onnx"
),
tokens = "asr-model/tokens.txt",
numThreads = 2,
provider = "cpu"
),
enableEndpoint = false,
decodingMethod = "greedy_search"
)
recognizer = OnlineRecognizer(
assetManager = context.assets,
config = config
)
}
fun startListening() {
stream = recognizer?.createStream()
// 开始录音并实时识别
val audioRecord = AudioRecord(...)
audioRecord.startRecording()
// 实时处理音频流
while (isListening) {
val samples = readAudioSamples()
stream?.acceptWaveform(samples, 16000)
while (recognizer?.isReady(stream) == true) {
recognizer?.decode(stream)
}
val result = recognizer?.getResult(stream)?.text
onPartialResult?.invoke(result)
}
}
}
关键优化:
-
唤醒词检测:支持"小端"、"小段"等多种发音
-
音量阈值:播放音乐时提高阈值,避免误唤醒
-
资源管理:及时释放 AudioRecord,避免 -38 错误
2. 高质量语音合成(Edge TTS)
为什么选择 Edge TTS?
-
✅ 完全免费
-
✅ 音质接近真人
-
✅ 支持多种音色
-
✅ 支持语速调节
实现代码:
class EdgeTTS {
fun generate(
text: String,
voice: String = "zh-CN-XiaoxiaoNeural",
outputFile: File,
rate: Float = 1.0f
): Boolean {
val requestId = UUID.randomUUID().toString()
val rateStr = if (rate >= 1.0f) "+${((rate - 1.0f) * 100).toInt()}%"
else "${((rate - 1.0f) * 100).toInt()}%"
val ssml = """
<speak version='1.0' xmlns='http://www.w3.org/2001/10/synthesis' xml:lang='zh-CN'>
<voice name='$voice'>
<prosody rate='$rateStr'>$text</prosody>
</voice>
</speak>
""".trimIndent()
// 通过 WebSocket 连接 Edge TTS 服务
val url = "wss://api.msedgeservices.com/tts/cognitiveservices/websocket/v1?..."
client.newWebSocket(request, object : WebSocketListener() {
override fun onMessage(webSocket: WebSocket, bytes: ByteString) {
// 接收音频数据并保存
outputFile.outputStream().use { it.write(bytes.toByteArray()) }
}
})
return true
}
}
支持的音色:
-
晓晓(温柔)、晓伊(活泼)、晓涵(温暖)
-
晓梦(可爱)、晓墨(知性)、晓秋(温柔)
-
云希(阳光男声)、云扬(新闻男声)
3. AI 对话(豆包 API)
集成豆包 API:
class AIManager(
private val settings: SettingsManager,
private val memory: MemoryManager
) {
fun chat(userMessage: String): String {
// 构建上下文(包含历史对话)
val messages = buildList {
add(Message("system", settings.systemPrompt))
addAll(memory.getRecentContext(10)) // 最近10条对话
add(Message("user", userMessage))
}
// 调用豆包 API
val response = httpClient.post("https://ark.cn-beijing.volces.com/api/v3/chat/completions") {
header("Authorization", "Bearer ${settings.doubaoApiKey}")
setBody(ChatRequest(
model = settings.doubaoModel,
messages = messages,
temperature = settings.temperature,
max_tokens = settings.maxTokens
))
}
return response.body<ChatResponse>().choices[0].message.content
}
}
记忆系统实现:
class MemoryManager(context: Context) {
private val db = SQLiteDatabase.openOrCreateDatabase(...)
fun learnFromConversation(userInput: String, aiResponse: String) {
// 提取关键信息
val keywords = extractKeywords(userInput, aiResponse)
// 存储到数据库
db.execSQL("""
INSERT INTO conversations (user_input, ai_response, keywords, timestamp)
VALUES (?, ?, ?, ?)
""", arrayOf(userInput, aiResponse, keywords, System.currentTimeMillis()))
}
fun getRecentContext(count: Int): List<Message> {
// 获取最近的对话记录
val cursor = db.rawQuery("""
SELECT user_input, ai_response
FROM conversations
ORDER BY timestamp DESC
LIMIT ?
""", arrayOf(count.toString()))
return buildList {
while (cursor.moveToNext()) {
add(Message("user", cursor.getString(0)))
add(Message("assistant", cursor.getString(1)))
}
}.reversed()
}
}
4. 本地音乐播放
实现思路:
-
扫描手机音乐库(过滤短音效)
-
支持歌名模糊匹配
-
中文歌曲优先
class MusicPlayer(private val context: Context) {
private val musicLibrary = mutableMapOf<String, String>()
fun loadMusicLibrary() {
// 只加载 150 秒以上的音乐
val cursor = context.contentResolver.query(
MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
arrayOf(MediaStore.Audio.Media.TITLE, MediaStore.Audio.Media.DATA),
"${MediaStore.Audio.Media.DURATION} >= ?",
arrayOf("150000"),
null
)
cursor?.use {
while (it.moveToNext()) {
val title = it.getString(0).lowercase()
val path = it.getString(1)
musicLibrary[title] = path
}
}
}
fun play(songName: String): String {
// 随机播放时,中文歌曲优先
val matchedSong = if (songName.isEmpty()) {
val chineseSongs = musicLibrary.keys.filter { containsChinese(it) }
if (chineseSongs.isNotEmpty()) chineseSongs.random()
else musicLibrary.keys.random()
} else {
musicLibrary.keys.find { it.contains(songName) }
}
matchedSong?.let {
mediaPlayer = MediaPlayer().apply {
setDataSource(musicLibrary[it])
prepare()
start()
}
return "正在播放《$it》"
}
return "未找到歌曲"
}
}
五、UI 设计(Jetpack Compose)
三色状态设计
@Composable
fun RobotScreen() {
var isAwake by remember { mutableStateOf(false) }
var isScreenBlack by remember { mutableStateOf(false) }
Box(
modifier = Modifier
.fillMaxSize()
.background(
when {
isScreenBlack -> Color.Black // 黑屏省电
isAwake -> Color(0xFFFF69B4) // 粉色唤醒
else -> Color(0xFF2196F3) // 蓝色待机
}
)
) {
// 表情动画
FaceAnimator(
isListening = isAwake,
isSpeaking = isSpeakingState
)
// 对话文本
if (isAwake) {
Column {
Text(recognizedText, color = Color(0xFFFFEB3B)) // 黄色用户输入
Text(responseText, color = Color.White) // 白色AI回复
}
}
}
}
自适应字体大小
val fontSize = when {
text.length <= 10 -> (screenWidth * 0.04f).sp
text.length <= 30 -> (screenWidth * 0.03f).sp
else -> (screenWidth * 0.02f).sp
}
六、性能优化
1. 内存优化
-
TTS 音频文件播放完立即删除
-
及时释放 MediaPlayer 资源
-
使用异步
prepareAsync()避免阻塞
2. 电量优化
-
15 秒无声音自动黑屏
-
播放音乐时降低识别灵敏度
-
后台保持监听(避免重启导致的 -38 错误)
3. 稳定性优化
-
全局异常捕获
-
超时保护(TTS 10秒、AI 30秒)
-
资源冲突检测
七、遇到的坑和解决方案
坑1:AudioRecord -38 错误
问题: 从后台回到前台时,AudioRecord 启动失败,报 -38 错误。
原因: 旧的 AudioRecord 没有完全释放,新的无法创建。
解决:
override fun onPause() {
// 不停止监听,让它继续运行
// speechManager.stop() // ❌ 不要调用
}
override fun onResume() {
// 监听一直在运行,无需重启
}
坑2:TTS 播放时被误唤醒
问题: 小端说话时,自己的声音被识别为唤醒词。
原因: 音量阈值太低。
解决:
// 播放音乐时提高音量阈值
if (isPlayingMusic && volume in 500..1000) {
lastVolumeTime = System.currentTimeMillis()
} else if (!isPlayingMusic && volume > 1000) {
lastVolumeTime = System.currentTimeMillis()
}
坑3:唤醒后 7 秒自动蓝屏
问题: 说"小端"后,回复"我在",然后立即蓝屏。
原因: 唤醒时没有更新 lastResponseTime。
解决:
if (!isAwake && hasWakeWord) {
isAwake = true
speakText("我在")
lastResponseTime = System.currentTimeMillis() // ✅ 更新时间
}
八、项目亮点
1. 完全免费
-
无广告、无内购
-
代码开源(MIT 协议)
-
所有 API 都有免费额度
2. 保护隐私
-
语音识别完全离线
-
对话记录存储在本地
-
不上传任何用户数据
3. 适合老人
-
大字体显示
-
语音交互,不用打字
-
操作简单,一句话搞定
4. 持续学习
-
记忆系统,越聊越懂你
-
支持上下文对话
-
可自定义系统提示词
九、未来规划
短期计划(1-3个月)
-
优化真机稳定性(收集用户日志)
-
添加更多唤醒词
-
支持方言识别
-
优化电量消耗
长期计划(6-12个月)
-
支持多轮对话
-
添加日程提醒功能
-
支持智能家居控制
-
开发 iOS 版本
十、开源地址和下载
项目信息
-
项目名称:小端机器人
-
开源协议:MIT
-
开发语言:Kotlin
-
最低系统:Android 8.0+
下载体验
-
Gitee:[项目地址]
-
APK 下载:[蓝奏云链接]
-
使用文档:[语雀文档]
技术交流
-
QQ 群:[群号]
-
问题反馈:Gitee Issues
-
邮箱:[联系邮箱]
十一、写在最后
这是一个公益项目,不求回报,只希望能帮助更多人。
如果你觉得这个项目有价值,欢迎:
-
⭐ 给项目点个 Star
-
🔄 分享给需要的人
-
💬 提出你的建议
-
🤝 参与代码贡献
让 AI 陪伴每一个家庭,让科技更有温度。
作者:诺言
一个热爱技术、关注公益的程序员
相关文章推荐
标签: #Android #Kotlin #AI #语音助手 #开源项目 #公益 #Sherpa-ONNX #Edge-TTS #豆包API
如果这篇文章对你有帮助,欢迎点赞、收藏、关注! 抖音搜小端机器人,有基本教程
有任何问题欢迎在评论区或者Q群362422425留言交流!
使用指南:xiaoduan: 傻傻的机器人 gitee.com/yiliu66/xia…